1 /*
   2  * CDDL HEADER START
   3  *
   4  * The contents of this file are subject to the terms of the
   5  * Common Development and Distribution License (the "License").
   6  * You may not use this file except in compliance with the License.
   7  *
   8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
   9  * or http://www.opensolaris.org/os/licensing.
  10  * See the License for the specific language governing permissions
  11  * and limitations under the License.
  12  *
  13  * When distributing Covered Code, include this CDDL HEADER in each
  14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  15  * If applicable, add the following below this CDDL HEADER, with the
  16  * fields enclosed by brackets "[]" replaced with your own identifying
  17  * information: Portions Copyright [yyyy] [name of copyright owner]
  18  *
  19  * CDDL HEADER END
  20  */
  21 
  22 /*
  23  * Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
  24  * Copyright (c) 2013, Joyent, Inc. All rights reserved.
  25  * Copyright (c) 2018, Western Digital Technologies, Inc. All rights reserved.
  26  */
  27 
  28 #include <libxml/parser.h>
  29 #include <libxml/xinclude.h>
  30 #include <sys/fm/protocol.h>
  31 #include <assert.h>
  32 #include <string.h>
  33 #include <strings.h>
  34 #include <ctype.h>
  35 #include <errno.h>
  36 #include <limits.h>
  37 #include <fm/libtopo.h>
  38 #include <unistd.h>
  39 #include <sys/stat.h>
  40 #include <fcntl.h>
  41 #include <topo_file.h>
  42 #include <topo_mod.h>
  43 #include <topo_subr.h>
  44 #include <topo_alloc.h>
  45 #include <topo_parse.h>
  46 #include <topo_error.h>
  47 
  48 static tf_rdata_t *topo_xml_walk(topo_mod_t *, tf_info_t *, xmlNodePtr,
  49     tnode_t *);
  50 static tf_edata_t *enum_attributes_process(topo_mod_t *, xmlNodePtr);
  51 static int enum_run(topo_mod_t *, tf_rdata_t *);
  52 static int fac_enum_run(topo_mod_t *, tnode_t *, const char *);
  53 static int fac_process(topo_mod_t *, xmlNodePtr, tf_rdata_t *, tnode_t *);
  54 static int fac_enum_process(topo_mod_t *, xmlNodePtr, tnode_t *);
  55 static int decorate_nodes(topo_mod_t *, tf_rdata_t *, xmlNodePtr, tnode_t *,
  56     tf_pad_t **);
  57 
  58 
  59 static void
  60 strarr_free(topo_mod_t *mod, char **arr, uint_t nelems)
  61 {
  62         int i;
  63 
  64         for (i = 0; i < nelems; i++)
  65                 topo_mod_strfree(mod, arr[i]);
  66         topo_mod_free(mod, arr, (nelems * sizeof (char *)));
  67 }
  68 
  69 int
  70 xmlattr_to_stab(topo_mod_t *mp, xmlNodePtr n, const char *stabname,
  71     topo_stability_t *rs)
  72 {
  73         xmlChar *str;
  74         int rv = 0;
  75 
  76         if (n == NULL) {
  77                 /* If there is no Stability defined, we default to private */
  78                 *rs = TOPO_STABILITY_PRIVATE;
  79                 return (0);
  80         }
  81         if ((str = xmlGetProp(n, (xmlChar *)stabname)) == NULL) {
  82                 topo_dprintf(mp->tm_hdl, TOPO_DBG_XML,
  83                     "attribute to stability:\n");
  84                 return (topo_mod_seterrno(mp, ETOPO_PRSR_NOATTR));
  85         }
  86 
  87         if (xmlStrcmp(str, (xmlChar *)Internal) == 0) {
  88                 *rs = TOPO_STABILITY_INTERNAL;
  89         } else if (xmlStrcmp(str, (xmlChar *)Private) == 0) {
  90                 *rs = TOPO_STABILITY_PRIVATE;
  91         } else if (xmlStrcmp(str, (xmlChar *)Obsolete) == 0) {
  92                 *rs = TOPO_STABILITY_OBSOLETE;
  93         } else if (xmlStrcmp(str, (xmlChar *)External) == 0) {
  94                 *rs = TOPO_STABILITY_EXTERNAL;
  95         } else if (xmlStrcmp(str, (xmlChar *)Unstable) == 0) {
  96                 *rs = TOPO_STABILITY_UNSTABLE;
  97         } else if (xmlStrcmp(str, (xmlChar *)Evolving) == 0) {
  98                 *rs = TOPO_STABILITY_EVOLVING;
  99         } else if (xmlStrcmp(str, (xmlChar *)Stable) == 0) {
 100                 *rs = TOPO_STABILITY_STABLE;
 101         } else if (xmlStrcmp(str, (xmlChar *)Standard) == 0) {
 102                 *rs = TOPO_STABILITY_STANDARD;
 103         } else {
 104                 xmlFree(str);
 105                 return (topo_mod_seterrno(mp, ETOPO_PRSR_BADSTAB));
 106         }
 107         xmlFree(str);
 108         return (rv);
 109 }
 110 
 111 int
 112 xmlattr_to_int(topo_mod_t *mp,
 113     xmlNodePtr n, const char *propname, uint64_t *value)
 114 {
 115         xmlChar *str;
 116         xmlChar *estr;
 117 
 118         topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "xmlattr_to_int(propname=%s)\n",
 119             propname);
 120         if ((str = xmlGetProp(n, (xmlChar *)propname)) == NULL)
 121                 return (topo_mod_seterrno(mp, ETOPO_PRSR_NOATTR));
 122         *value = strtoull((char *)str, (char **)&estr, 10);
 123         if (estr == str) {
 124                 /* no conversion was done */
 125                 xmlFree(str);
 126                 return (topo_mod_seterrno(mp, ETOPO_PRSR_BADNUM));
 127         }
 128         xmlFree(str);
 129         return (0);
 130 }
 131 
 132 static int
 133 xmlattr_to_fmri(topo_mod_t *mp,
 134     xmlNodePtr xn, const char *propname, nvlist_t **rnvl)
 135 {
 136         xmlChar *str;
 137 
 138         topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "xmlattr_to_fmri(propname=%s)\n",
 139             propname);
 140         if ((str = xmlGetProp(xn, (xmlChar *)propname)) == NULL)
 141                 return (topo_mod_seterrno(mp, ETOPO_PRSR_NOATTR));
 142         if (topo_mod_str2nvl(mp, (const char *)str, rnvl) < 0) {
 143                 xmlFree(str);
 144                 return (-1);
 145         }
 146         xmlFree(str);
 147         return (0);
 148 }
 149 
 150 static topo_type_t
 151 xmlattr_to_type(topo_mod_t *mp, xmlNodePtr xn, xmlChar *attr)
 152 {
 153         topo_type_t rv;
 154         xmlChar *str;
 155         if ((str = xmlGetProp(xn, (xmlChar *)attr)) == NULL) {
 156                 topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR, "%s attribute missing",
 157                     attr);
 158                 (void) topo_mod_seterrno(mp, ETOPO_PRSR_NOATTR);
 159                 return (TOPO_TYPE_INVALID);
 160         }
 161         if (xmlStrcmp(str, (xmlChar *)Int32) == 0) {
 162                 rv = TOPO_TYPE_INT32;
 163         } else if (xmlStrcmp(str, (xmlChar *)UInt32) == 0) {
 164                 rv = TOPO_TYPE_UINT32;
 165         } else if (xmlStrcmp(str, (xmlChar *)Int64) == 0) {
 166                 rv = TOPO_TYPE_INT64;
 167         } else if (xmlStrcmp(str, (xmlChar *)UInt64) == 0) {
 168                 rv = TOPO_TYPE_UINT64;
 169         } else if (xmlStrcmp(str, (xmlChar *)FMRI) == 0) {
 170                 rv = TOPO_TYPE_FMRI;
 171         } else if (xmlStrcmp(str, (xmlChar *)String) == 0) {
 172                 rv = TOPO_TYPE_STRING;
 173         } else if (xmlStrcmp(str, (xmlChar *)Int32_Arr) == 0) {
 174                 rv = TOPO_TYPE_INT32_ARRAY;
 175         } else if (xmlStrcmp(str, (xmlChar *)UInt32_Arr) == 0) {
 176                 rv = TOPO_TYPE_UINT32_ARRAY;
 177         } else if (xmlStrcmp(str, (xmlChar *)Int64_Arr) == 0) {
 178                 rv = TOPO_TYPE_INT64_ARRAY;
 179         } else if (xmlStrcmp(str, (xmlChar *)UInt64_Arr) == 0) {
 180                 rv = TOPO_TYPE_UINT64_ARRAY;
 181         } else if (xmlStrcmp(str, (xmlChar *)String_Arr) == 0) {
 182                 rv = TOPO_TYPE_STRING_ARRAY;
 183         } else if (xmlStrcmp(str, (xmlChar *)FMRI_Arr) == 0) {
 184                 rv = TOPO_TYPE_FMRI_ARRAY;
 185         } else {
 186                 topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
 187                     "Unrecognized type attribute value '%s'.\n", str);
 188                 (void) topo_mod_seterrno(mp, ETOPO_PRSR_BADTYPE);
 189                 xmlFree(str);
 190                 return (TOPO_TYPE_INVALID);
 191         }
 192         xmlFree(str);
 193         return (rv);
 194 }
 195 
 196 static int
 197 xlate_common(topo_mod_t *mp, xmlNodePtr xn, topo_type_t ptype, nvlist_t *nvl,
 198     const char *name)
 199 {
 200         int rv;
 201         uint64_t ui;
 202         uint_t i = 0, nelems = 0;
 203         nvlist_t *fmri;
 204         xmlChar *str;
 205         char **strarrbuf;
 206         void *arrbuf;
 207         nvlist_t **nvlarrbuf;
 208         xmlNodePtr cn;
 209 
 210         topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "xlate_common(name=%s)\n", name);
 211         switch (ptype) {
 212         case TOPO_TYPE_INT32:
 213                 if (xmlattr_to_int(mp, xn, Value, &ui) < 0)
 214                         return (-1);
 215                 rv = nvlist_add_int32(nvl, name, (int32_t)ui);
 216                 break;
 217         case TOPO_TYPE_UINT32:
 218                 if (xmlattr_to_int(mp, xn, Value, &ui) < 0)
 219                         return (-1);
 220                 rv = nvlist_add_uint32(nvl, name, (uint32_t)ui);
 221                 break;
 222         case TOPO_TYPE_INT64:
 223                 if (xmlattr_to_int(mp, xn, Value, &ui) < 0)
 224                         return (-1);
 225                 rv = nvlist_add_int64(nvl, name, (int64_t)ui);
 226                 break;
 227         case TOPO_TYPE_UINT64:
 228                 if (xmlattr_to_int(mp, xn, Value, &ui) < 0)
 229                         return (-1);
 230                 rv = nvlist_add_uint64(nvl, name, ui);
 231                 break;
 232         case TOPO_TYPE_FMRI:
 233                 if (xmlattr_to_fmri(mp, xn, Value, &fmri) < 0)
 234                         return (-1);
 235                 rv = nvlist_add_nvlist(nvl, name, fmri);
 236                 nvlist_free(fmri);
 237                 break;
 238         case TOPO_TYPE_STRING:
 239                 if ((str = xmlGetProp(xn, (xmlChar *)Value)) == NULL)
 240                         return (-1);
 241                 rv = nvlist_add_string(nvl, name, (char *)str);
 242                 xmlFree(str);
 243                 break;
 244         case TOPO_TYPE_INT32_ARRAY:
 245         case TOPO_TYPE_UINT32_ARRAY:
 246         case TOPO_TYPE_INT64_ARRAY:
 247         case TOPO_TYPE_UINT64_ARRAY:
 248         case TOPO_TYPE_STRING_ARRAY:
 249         case TOPO_TYPE_FMRI_ARRAY:
 250                 for (cn = xn->xmlChildrenNode; cn != NULL; cn = cn->next)
 251                         if ((xmlStrcmp(cn->name, (xmlChar *)Propitem) == 0) ||
 252                             (xmlStrcmp(cn->name, (xmlChar *)Argitem) == 0))
 253                                 nelems++;
 254 
 255                 if (nelems < 1) {
 256                         topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR, "No <propitem> "
 257                             "or <argitem> elements found for array val");
 258                         return (-1);
 259                 }
 260                 break;
 261         default:
 262                 topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
 263                     "Unrecognized type attribute (ptype = %d)\n", ptype);
 264                 return (topo_mod_seterrno(mp, ETOPO_PRSR_BADTYPE));
 265         }
 266 
 267         switch (ptype) {
 268         case TOPO_TYPE_INT32_ARRAY:
 269                 if ((arrbuf = topo_mod_alloc(mp, (nelems * sizeof (int32_t))))
 270                     == NULL)
 271                         return (topo_mod_seterrno(mp, ETOPO_NOMEM));
 272                 for (cn = xn->xmlChildrenNode; cn != NULL; cn = cn->next) {
 273                         if ((xmlStrcmp(cn->name, (xmlChar *)Propitem) == 0) ||
 274                             (xmlStrcmp(cn->name, (xmlChar *)Argitem) == 0)) {
 275 
 276                                 if ((str = xmlGetProp(cn, (xmlChar *)Value))
 277                                     == NULL)
 278                                         return (-1);
 279 
 280                                 ((int32_t *)arrbuf)[i++]
 281                                     = atoi((const char *)str);
 282                                 xmlFree(str);
 283                         }
 284                 }
 285 
 286                 rv = nvlist_add_int32_array(nvl, name, (int32_t *)arrbuf,
 287                     nelems);
 288                 topo_mod_free(mp, arrbuf, (nelems * sizeof (int32_t)));
 289                 break;
 290         case TOPO_TYPE_UINT32_ARRAY:
 291                 if ((arrbuf = topo_mod_alloc(mp, (nelems * sizeof (uint32_t))))
 292                     == NULL)
 293                         return (topo_mod_seterrno(mp, ETOPO_NOMEM));
 294                 for (cn = xn->xmlChildrenNode; cn != NULL; cn = cn->next) {
 295                         if ((xmlStrcmp(cn->name, (xmlChar *)Propitem) == 0) ||
 296                             (xmlStrcmp(cn->name, (xmlChar *)Argitem) == 0)) {
 297 
 298                                 if ((str = xmlGetProp(cn, (xmlChar *)Value))
 299                                     == NULL)
 300                                         return (-1);
 301 
 302                                 ((uint32_t *)arrbuf)[i++]
 303                                     = atoi((const char *)str);
 304                                 xmlFree(str);
 305                         }
 306                 }
 307 
 308                 rv = nvlist_add_uint32_array(nvl, name, (uint32_t *)arrbuf,
 309                     nelems);
 310                 topo_mod_free(mp, arrbuf, (nelems * sizeof (uint32_t)));
 311                 break;
 312         case TOPO_TYPE_INT64_ARRAY:
 313                 if ((arrbuf = topo_mod_alloc(mp, (nelems * sizeof (int64_t))))
 314                     == NULL)
 315                         return (topo_mod_seterrno(mp, ETOPO_NOMEM));
 316                 for (cn = xn->xmlChildrenNode; cn != NULL; cn = cn->next) {
 317                         if ((xmlStrcmp(cn->name, (xmlChar *)Propitem) == 0) ||
 318                             (xmlStrcmp(cn->name, (xmlChar *)Argitem) == 0)) {
 319 
 320                                 if ((str = xmlGetProp(cn, (xmlChar *)Value))
 321                                     == NULL)
 322                                         return (-1);
 323 
 324                                 ((int64_t *)arrbuf)[i++]
 325                                     = atol((const char *)str);
 326                                 xmlFree(str);
 327                         }
 328                 }
 329 
 330                 rv = nvlist_add_int64_array(nvl, name, (int64_t *)arrbuf,
 331                     nelems);
 332                 topo_mod_free(mp, arrbuf, (nelems * sizeof (int64_t)));
 333                 break;
 334         case TOPO_TYPE_UINT64_ARRAY:
 335                 if ((arrbuf = topo_mod_alloc(mp, (nelems * sizeof (uint64_t))))
 336                     == NULL)
 337                         return (topo_mod_seterrno(mp, ETOPO_NOMEM));
 338                 for (cn = xn->xmlChildrenNode; cn != NULL; cn = cn->next) {
 339                         if ((xmlStrcmp(cn->name, (xmlChar *)Propitem) == 0) ||
 340                             (xmlStrcmp(cn->name, (xmlChar *)Argitem) == 0)) {
 341 
 342                                 if ((str = xmlGetProp(cn, (xmlChar *)Value))
 343                                     == NULL)
 344                                         return (-1);
 345 
 346                                 ((uint64_t *)arrbuf)[i++]
 347                                     = atol((const char *)str);
 348                                 xmlFree(str);
 349                         }
 350                 }
 351 
 352                 rv = nvlist_add_uint64_array(nvl, name, arrbuf,
 353                     nelems);
 354                 topo_mod_free(mp, arrbuf, (nelems * sizeof (uint64_t)));
 355                 break;
 356         case TOPO_TYPE_STRING_ARRAY:
 357                 if ((strarrbuf = topo_mod_alloc(mp, (nelems * sizeof (char *))))
 358                     == NULL)
 359                         return (topo_mod_seterrno(mp, ETOPO_NOMEM));
 360                 for (cn = xn->xmlChildrenNode; cn != NULL; cn = cn->next) {
 361                         if ((xmlStrcmp(cn->name, (xmlChar *)Propitem) == 0) ||
 362                             (xmlStrcmp(cn->name, (xmlChar *)Argitem) == 0)) {
 363 
 364                                 if ((str = xmlGetProp(cn, (xmlChar *)Value))
 365                                     == NULL)
 366                                         return (-1);
 367 
 368                                 strarrbuf[i++] =
 369                                     topo_mod_strdup(mp, (const char *)str);
 370                                 xmlFree(str);
 371                         }
 372                 }
 373 
 374                 rv = nvlist_add_string_array(nvl, name, strarrbuf, nelems);
 375                 strarr_free(mp, strarrbuf, nelems);
 376                 break;
 377         case TOPO_TYPE_FMRI_ARRAY:
 378                 if ((nvlarrbuf = topo_mod_alloc(mp, (nelems *
 379                     sizeof (nvlist_t *)))) == NULL)
 380                         return (topo_mod_seterrno(mp, ETOPO_NOMEM));
 381                 for (cn = xn->xmlChildrenNode; cn != NULL; cn = cn->next) {
 382                         if ((xmlStrcmp(cn->name, (xmlChar *)Propitem) == 0) ||
 383                             (xmlStrcmp(cn->name, (xmlChar *)Argitem) == 0)) {
 384 
 385                                 if ((str = xmlGetProp(cn, (xmlChar *)Value))
 386                                     == NULL)
 387                                         return (-1);
 388 
 389                                 if (topo_mod_str2nvl(mp, (const char *)str,
 390                                     &(nvlarrbuf[i++])) < 0) {
 391                                         xmlFree(str);
 392                                         return (-1);
 393                                 }
 394                                 xmlFree(str);
 395                         }
 396                 }
 397 
 398                 rv = nvlist_add_nvlist_array(nvl, name, nvlarrbuf,
 399                     nelems);
 400                 topo_mod_free(mp, nvlarrbuf, (nelems * sizeof (nvlist_t *)));
 401                 break;
 402         }
 403 
 404         if (rv != 0) {
 405                 topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
 406                     "Nvlist construction failed.\n");
 407                 return (topo_mod_seterrno(mp, ETOPO_NOMEM));
 408         } else
 409                 return (0);
 410 }
 411 
 412 static int
 413 xmlprop_xlate(topo_mod_t *mp, xmlNodePtr xn, nvlist_t *nvl)
 414 {
 415         topo_type_t ptype;
 416         xmlChar *str;
 417 
 418         topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "xmlprop_xlate\n");
 419         if ((str = xmlGetProp(xn, (xmlChar *)Immutable)) != NULL) {
 420                 if (xmlStrcmp(str, (xmlChar *)False) == 0)
 421                         (void) nvlist_add_boolean_value(nvl, INV_IMMUTE,
 422                             B_FALSE);
 423                 else
 424                         (void) nvlist_add_boolean_value(nvl, INV_IMMUTE,
 425                             B_TRUE);
 426                 xmlFree(str);
 427         } else {
 428                 (void) nvlist_add_boolean_value(nvl, INV_IMMUTE, B_TRUE);
 429         }
 430 
 431         if ((ptype = xmlattr_to_type(mp, xn, (xmlChar *)Type))
 432             == TOPO_TYPE_INVALID)
 433                 return (-1);
 434 
 435         if (nvlist_add_int32(nvl, INV_PVALTYPE, ptype) != 0)
 436                 return (-1);
 437 
 438         return (xlate_common(mp, xn, ptype, nvl, INV_PVAL));
 439 }
 440 
 441 static int
 442 dependent_create(topo_mod_t *mp,
 443     tf_info_t *xinfo, tf_pad_t *pad, xmlNodePtr dxn, tnode_t *ptn)
 444 {
 445         tf_rdata_t *rp, *pp, *np;
 446         xmlChar *grptype;
 447         int sibs = 0;
 448 
 449         topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "dependent_create\n");
 450         if ((grptype = xmlGetProp(dxn, (xmlChar *)Grouping)) == NULL) {
 451                 topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
 452                     "Dependents missing grouping attribute");
 453                 return (topo_mod_seterrno(mp, ETOPO_PRSR_NOATTR));
 454         }
 455 
 456         pp = NULL;
 457         if (xmlStrcmp(grptype, (xmlChar *)Siblings) == 0) {
 458                 rp = pad->tpad_sibs;
 459                 sibs++;
 460         } else if (xmlStrcmp(grptype, (xmlChar *)Children) == 0) {
 461                 rp = pad->tpad_child;
 462         } else {
 463                 topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
 464                     "Dependents have bogus grouping attribute");
 465                 xmlFree(grptype);
 466                 return (topo_mod_seterrno(mp, ETOPO_PRSR_BADGRP));
 467         }
 468         xmlFree(grptype);
 469         /* Add processed dependents to the tail of the list */
 470         while (rp != NULL) {
 471                 pp = rp;
 472                 rp = rp->rd_next;
 473         }
 474         if ((np = topo_xml_walk(mp, xinfo, dxn, ptn)) == NULL) {
 475                 topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
 476                     "error within dependent .xml topology: "
 477                     "%s\n", topo_strerror(topo_mod_errno(mp)));
 478                 return (-1);
 479         }
 480         if (pp != NULL)
 481                 pp->rd_next = np;
 482         else if (sibs == 1)
 483                 pad->tpad_sibs = np;
 484         else
 485                 pad->tpad_child = np;
 486         return (0);
 487 }
 488 
 489 static int
 490 dependents_create(topo_mod_t *mp,
 491     tf_info_t *xinfo, tf_pad_t *pad, xmlNodePtr pxn, tnode_t *ptn)
 492 {
 493         xmlNodePtr cn;
 494 
 495         topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "dependents_create\n");
 496         for (cn = pxn->xmlChildrenNode; cn != NULL; cn = cn->next) {
 497                 if (xmlStrcmp(cn->name, (xmlChar *)Dependents) == 0) {
 498                         if (dependent_create(mp, xinfo, pad, cn, ptn) < 0)
 499                                 return (-1);
 500                 }
 501         }
 502         return (0);
 503 }
 504 
 505 static int
 506 prop_create(topo_mod_t *mp,
 507     nvlist_t *pfmri, tnode_t *ptn, const char *gnm, const char *pnm,
 508     topo_type_t ptype, int flag)
 509 {
 510         nvlist_t *fmri, **fmriarr;
 511         uint32_t ui32, *ui32arr;
 512         uint64_t ui64, *ui64arr;
 513         int32_t i32, *i32arr;
 514         int64_t i64, *i64arr;
 515         uint_t nelem;
 516         char *str, **strarr;
 517         int err, e;
 518 
 519         topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "prop_create(pgrp = %s, "
 520             "prop = %s)\n", gnm, pnm);
 521         switch (ptype) {
 522         case TOPO_TYPE_INT32:
 523                 e = nvlist_lookup_int32(pfmri, INV_PVAL, &i32);
 524                 break;
 525         case TOPO_TYPE_UINT32:
 526                 e = nvlist_lookup_uint32(pfmri, INV_PVAL, &ui32);
 527                 break;
 528         case TOPO_TYPE_INT64:
 529                 e = nvlist_lookup_int64(pfmri, INV_PVAL, &i64);
 530                 break;
 531         case TOPO_TYPE_UINT64:
 532                 e = nvlist_lookup_uint64(pfmri, INV_PVAL, &ui64);
 533                 break;
 534         case TOPO_TYPE_FMRI:
 535                 e = nvlist_lookup_nvlist(pfmri, INV_PVAL, &fmri);
 536                 break;
 537         case TOPO_TYPE_STRING:
 538                 e = nvlist_lookup_string(pfmri, INV_PVAL, &str);
 539                 break;
 540         case TOPO_TYPE_INT32_ARRAY:
 541                 e = nvlist_lookup_int32_array(pfmri, INV_PVAL, &i32arr, &nelem);
 542                 break;
 543         case TOPO_TYPE_UINT32_ARRAY:
 544                 e = nvlist_lookup_uint32_array(pfmri, INV_PVAL, &ui32arr,
 545                     &nelem);
 546                 break;
 547         case TOPO_TYPE_INT64_ARRAY:
 548                 e = nvlist_lookup_int64_array(pfmri, INV_PVAL, &i64arr,
 549                     &nelem);
 550                 break;
 551         case TOPO_TYPE_UINT64_ARRAY:
 552                 e = nvlist_lookup_uint64_array(pfmri, INV_PVAL, &ui64arr,
 553                     &nelem);
 554                 break;
 555         case TOPO_TYPE_STRING_ARRAY:
 556                 e = nvlist_lookup_string_array(pfmri, INV_PVAL, &strarr,
 557                     &nelem);
 558                 break;
 559         case TOPO_TYPE_FMRI_ARRAY:
 560                 e = nvlist_lookup_nvlist_array(pfmri, INV_PVAL, &fmriarr,
 561                     &nelem);
 562                 break;
 563         default:
 564                 e = ETOPO_PRSR_BADTYPE;
 565         }
 566         if (e != 0) {
 567                 topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
 568                     "prop_create: prop value lookup failed.\n");
 569                 return (topo_mod_seterrno(mp, e));
 570         }
 571         switch (ptype) {
 572         case TOPO_TYPE_INT32:
 573                 e = topo_prop_set_int32(ptn, gnm, pnm, flag, i32, &err);
 574                 break;
 575         case TOPO_TYPE_UINT32:
 576                 e = topo_prop_set_uint32(ptn, gnm, pnm, flag, ui32, &err);
 577                 break;
 578         case TOPO_TYPE_INT64:
 579                 e = topo_prop_set_int64(ptn, gnm, pnm, flag, i64, &err);
 580                 break;
 581         case TOPO_TYPE_UINT64:
 582                 e = topo_prop_set_uint64(ptn, gnm, pnm, flag, ui64, &err);
 583                 break;
 584         case TOPO_TYPE_FMRI:
 585                 e = topo_prop_set_fmri(ptn, gnm, pnm, flag, fmri, &err);
 586                 break;
 587         case TOPO_TYPE_STRING:
 588                 e = topo_prop_set_string(ptn, gnm, pnm, flag, str, &err);
 589                 break;
 590         case TOPO_TYPE_INT32_ARRAY:
 591                 e = topo_prop_set_int32_array(ptn, gnm, pnm, flag, i32arr,
 592                     nelem, &err);
 593                 break;
 594         case TOPO_TYPE_UINT32_ARRAY:
 595                 e = topo_prop_set_uint32_array(ptn, gnm, pnm, flag, ui32arr,
 596                     nelem, &err);
 597                 break;
 598         case TOPO_TYPE_INT64_ARRAY:
 599                 e = topo_prop_set_int64_array(ptn, gnm, pnm, flag, i64arr,
 600                     nelem, &err);
 601                 break;
 602         case TOPO_TYPE_UINT64_ARRAY:
 603                 e = topo_prop_set_uint64_array(ptn, gnm, pnm, flag, ui64arr,
 604                     nelem, &err);
 605                 break;
 606         case TOPO_TYPE_STRING_ARRAY:
 607                 e = topo_prop_set_string_array(ptn, gnm, pnm, flag,
 608                     (const char **)strarr, nelem, &err);
 609                 break;
 610         case TOPO_TYPE_FMRI_ARRAY:
 611                 e = topo_prop_set_fmri_array(ptn, gnm, pnm, flag,
 612                     (const nvlist_t **)fmriarr, nelem, &err);
 613                 break;
 614         }
 615         if (e != 0 && err != ETOPO_PROP_DEFD) {
 616 
 617                 /*
 618                  * Some properties may have already been set
 619                  * in topo_node_bind() or topo_prop_inherit if we are
 620                  * enumerating from a static .xml file
 621                  */
 622                 topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR, "prop set "
 623                     "failed %s/%s:%s\n", gnm, pnm, topo_strerror(err));
 624                 return (topo_mod_seterrno(mp, err));
 625         }
 626         return (0);
 627 }
 628 
 629 static int
 630 props_create(topo_mod_t *mp,
 631     tnode_t *ptn, const char *gnm, nvlist_t **props, int nprops)
 632 {
 633         topo_type_t ptype;
 634         boolean_t pim;
 635         char *pnm;
 636         int32_t i32;
 637         int flag;
 638         int pn;
 639         int e;
 640 
 641         topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "props_create(pgrp = %s)\n",
 642             gnm);
 643         for (pn = 0; pn < nprops; pn++) {
 644                 e = nvlist_lookup_string(props[pn], INV_PNAME, &pnm);
 645                 if (e != 0) {
 646                         topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
 647                             "props create lookup (%s) failure: %s",
 648                             INV_PNAME, strerror(e));
 649                         return (topo_mod_seterrno(mp, ETOPO_PRSR_NVPROP));
 650                 }
 651                 e = nvlist_lookup_boolean_value(props[pn], INV_IMMUTE, &pim);
 652                 if (e != 0) {
 653                         topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
 654                             "props create lookup (%s) failure: %s",
 655                             INV_IMMUTE, strerror(e));
 656                         return (topo_mod_seterrno(mp, ETOPO_PRSR_NVPROP));
 657                 }
 658                 flag = (pim == B_TRUE) ?
 659                     TOPO_PROP_IMMUTABLE : TOPO_PROP_MUTABLE;
 660 
 661                 e = nvlist_lookup_int32(props[pn], INV_PVALTYPE, &i32);
 662                 if (e != 0) {
 663                         topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
 664                             "props create lookup (%s) failure: %s",
 665                             INV_PVALTYPE, strerror(e));
 666                         return (topo_mod_seterrno(mp, ETOPO_PRSR_NVPROP));
 667                 }
 668                 ptype = (topo_type_t)i32;
 669                 if (prop_create(mp, props[pn], ptn, gnm, pnm, ptype, flag) < 0)
 670                         return (-1);
 671         }
 672         return (0);
 673 }
 674 
 675 static int
 676 pgroups_create(topo_mod_t *mp, tf_pad_t *pad, tnode_t *ptn)
 677 {
 678         topo_pgroup_info_t pgi;
 679         nvlist_t **props;
 680         char *gnm;
 681         char *nmstab, *dstab;
 682         uint32_t rnprops, nprops;
 683         uint32_t gv;
 684         int pg;
 685         int e;
 686 
 687         topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "pgroups_create: %s=%d\n",
 688             topo_node_name(ptn), topo_node_instance(ptn));
 689         for (pg = 0; pg < pad->tpad_pgcnt; pg++) {
 690                 e = nvlist_lookup_string(pad->tpad_pgs[pg],
 691                     INV_PGRP_NAME, &gnm);
 692                 if (e != 0) {
 693                         topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
 694                             "pad lookup (%s) failed (%s).\n",
 695                             INV_PGRP_NAME, strerror(errno));
 696                         return (topo_mod_seterrno(mp, ETOPO_PRSR_NVPROP));
 697                 }
 698                 e = nvlist_lookup_string(pad->tpad_pgs[pg],
 699                     INV_PGRP_NMSTAB, &nmstab);
 700                 if (e != 0) {
 701                         if (e != ENOENT) {
 702                                 topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
 703                                     "pad lookup (%s) "
 704                                     "failed.\n", INV_PGRP_NMSTAB);
 705                                 return (topo_mod_seterrno(mp,
 706                                     ETOPO_PRSR_NVPROP));
 707                         } else {
 708                                 nmstab = TOPO_STABSTR_PRIVATE;
 709                         }
 710                 }
 711                 e = nvlist_lookup_string(pad->tpad_pgs[pg],
 712                     INV_PGRP_DSTAB, &dstab);
 713                 if (e != 0) {
 714                         if (e != ENOENT) {
 715                                 topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
 716                                     "pad lookup (%s) failed.\n",
 717                                     INV_PGRP_DSTAB);
 718                                 return (topo_mod_seterrno(mp,
 719                                     ETOPO_PRSR_NVPROP));
 720                         } else {
 721                                 dstab = TOPO_STABSTR_PRIVATE;
 722                         }
 723                 }
 724                 e = nvlist_lookup_uint32(pad->tpad_pgs[pg],
 725                     INV_PGRP_VER, &gv);
 726                 if (e != 0) {
 727                         topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
 728                             "pad lookup (%s) failed.\n",
 729                             INV_PGRP_VER);
 730                         return (topo_mod_seterrno(mp, ETOPO_PRSR_NVPROP));
 731                 }
 732                 pgi.tpi_name = gnm;
 733                 pgi.tpi_namestab = topo_name2stability(nmstab);
 734                 pgi.tpi_datastab = topo_name2stability(dstab);
 735                 pgi.tpi_version = gv;
 736                 if (topo_pgroup_create(ptn, &pgi, &e) != 0) {
 737                         if (e != ETOPO_PROP_DEFD) {
 738                                 topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
 739                                     "pgroups create failure: %s\n",
 740                                     topo_strerror(e));
 741                                 return (-1);
 742                         }
 743                 }
 744                 e = nvlist_lookup_uint32(pad->tpad_pgs[pg],
 745                     INV_PGRP_NPROP, &rnprops);
 746                 /*
 747                  * The number of properties could be zero if the property
 748                  * group only contains propmethod declarations
 749                  */
 750                 if (rnprops > 0) {
 751                         e |= nvlist_lookup_nvlist_array(pad->tpad_pgs[pg],
 752                             INV_PGRP_ALLPROPS, &props, &nprops);
 753                         if (rnprops != nprops) {
 754                                 topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
 755                                     "recorded number of props %d does not "
 756                                     "match number of props recorded %d.\n",
 757                                     rnprops, nprops);
 758                         }
 759                         if (props_create(mp, ptn, gnm, props, nprops) < 0)
 760                                 return (-1);
 761                 }
 762         }
 763         return (0);
 764 }
 765 
 766 static nvlist_t *
 767 pval_record(topo_mod_t *mp, xmlNodePtr xn)
 768 {
 769         nvlist_t *pnvl = NULL;
 770         xmlChar *pname;
 771 
 772         topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "pval_record\n");
 773         if ((pname = xmlGetProp(xn, (xmlChar *)Name)) == NULL) {
 774                 topo_dprintf(mp->tm_hdl, TOPO_DBG_XML,
 775                     "propval lacks a name\n");
 776                 (void) topo_mod_seterrno(mp, ETOPO_PRSR_NOATTR);
 777                 return (NULL);
 778         }
 779         if (topo_mod_nvalloc(mp, &pnvl, NV_UNIQUE_NAME) < 0) {
 780                 xmlFree(pname);
 781                 return (NULL);
 782         }
 783         if (nvlist_add_string(pnvl, INV_PNAME, (char *)pname) < 0) {
 784                 xmlFree(pname);
 785                 nvlist_free(pnvl);
 786                 return (NULL);
 787         }
 788         xmlFree(pname);
 789         /* FMXXX stability of the property name */
 790 
 791         if (xmlprop_xlate(mp, xn, pnvl) < 0) {
 792                 nvlist_free(pnvl);
 793                 return (NULL);
 794         }
 795         return (pnvl);
 796 }
 797 
 798 
 799 struct propmeth_data {
 800         const char *pg_name;
 801         const char *prop_name;
 802         topo_type_t prop_type;
 803         const char *meth_name;
 804         topo_version_t meth_ver;
 805         nvlist_t *arg_nvl;
 806 };
 807 
 808 static int
 809 register_method(topo_mod_t *mp, tnode_t *ptn, struct propmeth_data *meth)
 810 {
 811         int err;
 812 
 813         if (topo_prop_method_version_register(ptn, meth->pg_name,
 814             meth->prop_name, meth->prop_type, meth->meth_name, meth->meth_ver,
 815             meth->arg_nvl, &err) != 0) {
 816 
 817                 topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR, "failed to register "
 818                     "propmethod %s for property \"%s\" in propgrp %s on node "
 819                     "%s=%d (%s)\n",
 820                     meth->meth_name, meth->prop_name, meth->pg_name,
 821                     topo_node_name(ptn), topo_node_instance(ptn),
 822                     topo_strerror(err));
 823                 return (-1);
 824         }
 825         topo_dprintf(mp->tm_hdl, TOPO_DBG_XML,
 826             "registered method %s on %s=%d\n",
 827             meth->meth_name, topo_node_name(ptn), topo_node_instance(ptn));
 828 
 829         return (0);
 830 }
 831 
 832 static int
 833 pmeth_record(topo_mod_t *mp, const char *pg_name, xmlNodePtr xn, tnode_t *tn,
 834     const char *rname, const char *ppgrp_name)
 835 {
 836         nvlist_t *arg_nvl = NULL;
 837         xmlNodePtr cn;
 838         xmlChar *meth_name = NULL, *prop_name = NULL;
 839         xmlChar *arg_name = NULL;
 840         uint64_t meth_ver, is_mutable = 0, is_nonvolatile = 0;
 841         topo_type_t prop_type;
 842         struct propmeth_data meth;
 843         int ret = 0, err;
 844         topo_type_t ptype;
 845         tnode_t *tmp;
 846 
 847         topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "pmeth_record: %s=%d "
 848             "(pgrp=%s)\n", topo_node_name(tn), topo_node_instance(tn), pg_name);
 849 
 850         /*
 851          * Get propmethod attribute values
 852          */
 853         if ((meth_name = xmlGetProp(xn, (xmlChar *)Name)) == NULL) {
 854                 topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
 855                     "propmethod element lacks a name attribute\n");
 856                 return (topo_mod_seterrno(mp, ETOPO_PRSR_NOATTR));
 857         }
 858         if (xmlattr_to_int(mp, xn, Version, &meth_ver) < 0) {
 859                 topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
 860                     "propmethod element lacks version attribute\n");
 861                 ret = topo_mod_seterrno(mp, ETOPO_PRSR_NOATTR);
 862                 goto pmr_done;
 863         }
 864         /*
 865          * The "mutable" and "nonvoltile" attributes are optional.  If not
 866          * specified we default to false (0)
 867          */
 868         (void) xmlattr_to_int(mp, xn, Mutable, &is_mutable);
 869         (void) xmlattr_to_int(mp, xn, Nonvolatile, &is_nonvolatile);
 870 
 871         if ((prop_name = xmlGetProp(xn, (xmlChar *)Propname)) == NULL) {
 872                 topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
 873                     "propmethod element lacks propname attribute\n");
 874                 ret = topo_mod_seterrno(mp, ETOPO_PRSR_NOATTR);
 875                 goto pmr_done;
 876         }
 877         if ((prop_type = xmlattr_to_type(mp, xn, (xmlChar *)Proptype))
 878             == TOPO_TYPE_INVALID) {
 879                 topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
 880                     "error decoding proptype attribute\n");
 881                 ret = topo_mod_seterrno(mp, ETOPO_PRSR_NOATTR);
 882                 goto pmr_done;
 883         }
 884 
 885         /*
 886          * Allocate method argument nvlist
 887          */
 888         if (topo_mod_nvalloc(mp, &arg_nvl, NV_UNIQUE_NAME) < 0) {
 889                 ret = topo_mod_seterrno(mp, ETOPO_NOMEM);
 890                 goto pmr_done;
 891         }
 892 
 893         /*
 894          * Iterate through the argval nodes and build the argval nvlist
 895          */
 896         for (cn = xn->xmlChildrenNode; cn != NULL; cn = cn->next) {
 897                 if (xmlStrcmp(cn->name, (xmlChar *)Argval) == 0) {
 898                         topo_dprintf(mp->tm_hdl, TOPO_DBG_XML,
 899                             "found argval element\n");
 900                         if ((arg_name = xmlGetProp(cn, (xmlChar *)Name))
 901                             == NULL) {
 902                                 topo_dprintf(mp->tm_hdl, TOPO_DBG_XML,
 903                                     "argval element lacks a name attribute\n");
 904                                 ret = topo_mod_seterrno(mp, ETOPO_PRSR_NOATTR);
 905                                 goto pmr_done;
 906                         }
 907                         if ((ptype = xmlattr_to_type(mp, cn, (xmlChar *)Type))
 908                             == TOPO_TYPE_INVALID) {
 909                                 ret = topo_mod_seterrno(mp, ETOPO_PRSR_BADTYPE);
 910                                 xmlFree(arg_name);
 911                                 break;
 912                         }
 913                         if (xlate_common(mp, cn, ptype, arg_nvl,
 914                             (const char *)arg_name) != 0) {
 915                                 ret = topo_mod_seterrno(mp, ETOPO_PRSR_BADTYPE);
 916                                 xmlFree(arg_name);
 917                                 break;
 918                         }
 919                 }
 920                 if (arg_name) {
 921                         xmlFree(arg_name);
 922                         arg_name = NULL;
 923                 }
 924         }
 925 
 926         if (ret != 0)
 927                 goto pmr_done;
 928 
 929         /*
 930          * Register the prop method for all of the nodes in our range
 931          */
 932         meth.pg_name = (const char *)pg_name;
 933         meth.prop_name = (const char *)prop_name;
 934         meth.prop_type = prop_type;
 935         meth.meth_name = (const char *)meth_name;
 936         meth.meth_ver = meth_ver;
 937         meth.arg_nvl = arg_nvl;
 938 
 939         /*
 940          * If the propgroup element is under a range element, we'll apply
 941          * the method to all of the topo nodes at this level with the same
 942          * range name.
 943          *
 944          * Otherwise, if the propgroup element is under a node element
 945          * then we'll simply register the method for this node.
 946          */
 947         if (strcmp(ppgrp_name, Range) == 0) {
 948                 for (tmp = tn; tmp != NULL; tmp = topo_child_next(NULL, tmp)) {
 949                         if (strcmp(rname, topo_node_name(tmp)) == 0) {
 950                                 if (register_method(mp, tmp, &meth) != 0) {
 951                                         ret = topo_mod_seterrno(mp,
 952                                             ETOPO_PRSR_REGMETH);
 953                                         goto pmr_done;
 954                                 }
 955                                 if (is_mutable) {
 956                                         if (topo_prop_setmutable(tmp,
 957                                             meth.pg_name, meth.prop_name, &err)
 958                                             != 0) {
 959                                                 ret = topo_mod_seterrno(mp,
 960                                                     ETOPO_PRSR_REGMETH);
 961                                                 goto pmr_done;
 962                                         }
 963                                 }
 964                                 if (is_nonvolatile) {
 965                                         if (topo_prop_setnonvolatile(tmp,
 966                                             meth.pg_name, meth.prop_name, &err)
 967                                             != 0) {
 968                                                 ret = topo_mod_seterrno(mp,
 969                                                     ETOPO_PRSR_REGMETH);
 970                                                 goto pmr_done;
 971                                         }
 972                                 }
 973                         }
 974                 }
 975         } else {
 976                 if (register_method(mp, tn, &meth) != 0) {
 977                         ret = topo_mod_seterrno(mp, ETOPO_PRSR_REGMETH);
 978                         goto pmr_done;
 979                 }
 980                 if (is_mutable) {
 981                         if (topo_prop_setmutable(tn, meth.pg_name,
 982                             meth.prop_name, &err) != 0) {
 983                                 ret = topo_mod_seterrno(mp,
 984                                     ETOPO_PRSR_REGMETH);
 985                                 goto pmr_done;
 986                         }
 987                 }
 988                 if (is_nonvolatile) {
 989                         if (topo_prop_setnonvolatile(tn, meth.pg_name,
 990                             meth.prop_name, &err) != 0) {
 991                                 ret = topo_mod_seterrno(mp,
 992                                     ETOPO_PRSR_REGMETH);
 993                                 goto pmr_done;
 994                         }
 995                 }
 996         }
 997 
 998 pmr_done:
 999         if (meth_name)
1000                 xmlFree(meth_name);
1001         if (prop_name)
1002                 xmlFree(prop_name);
1003         nvlist_free(arg_nvl);
1004         return (ret);
1005 }
1006 
1007 
1008 static int
1009 pgroup_record(topo_mod_t *mp, xmlNodePtr pxn, tnode_t *tn, const char *rname,
1010     tf_pad_t *rpad, int pi, const char *ppgrp_name)
1011 {
1012         topo_stability_t nmstab, dstab;
1013         uint64_t ver;
1014         xmlNodePtr cn;
1015         xmlChar *name;
1016         nvlist_t **apl = NULL;
1017         nvlist_t *pgnvl = NULL;
1018         int pcnt = 0;
1019         int ai = 0;
1020         int e;
1021 
1022         topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "pgroup_record\n");
1023         if ((name = xmlGetProp(pxn, (xmlChar *)Name)) == NULL) {
1024                 topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
1025                     "propgroup lacks a name\n");
1026                 return (topo_mod_seterrno(mp, ETOPO_PRSR_NOATTR));
1027         }
1028         if (xmlattr_to_int(mp, pxn, Version, &ver) < 0) {
1029                 topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
1030                     "propgroup lacks a version\n");
1031                 return (topo_mod_seterrno(mp, ETOPO_PRSR_NOATTR));
1032         }
1033         if (xmlattr_to_stab(mp, pxn, Namestab, &nmstab) < 0) {
1034                 topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
1035                     "propgroup lacks name-stability\n");
1036                 return (topo_mod_seterrno(mp, ETOPO_PRSR_NOATTR));
1037         }
1038         if (xmlattr_to_stab(mp, pxn, Datastab, &dstab) < 0) {
1039                 topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
1040                     "propgroup lacks data-stability\n");
1041                 return (topo_mod_seterrno(mp, ETOPO_PRSR_NOATTR));
1042         }
1043 
1044         topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "pgroup %s\n", (char *)name);
1045         for (cn = pxn->xmlChildrenNode; cn != NULL; cn = cn->next) {
1046                 if (xmlStrcmp(cn->name, (xmlChar *)Propval) == 0)
1047                         pcnt++;
1048         }
1049 
1050         if (topo_mod_nvalloc(mp, &pgnvl, NV_UNIQUE_NAME) < 0) {
1051                 xmlFree(name);
1052                 topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
1053                     "failed to allocate propgroup nvlist\n");
1054                 return (topo_mod_seterrno(mp, ETOPO_NOMEM));
1055         }
1056 
1057         e = nvlist_add_string(pgnvl, INV_PGRP_NAME, (char *)name);
1058         e |= nvlist_add_uint32(pgnvl, INV_PGRP_NMSTAB, nmstab);
1059         e |= nvlist_add_uint32(pgnvl, INV_PGRP_DSTAB, dstab);
1060         e |= nvlist_add_uint32(pgnvl, INV_PGRP_VER, ver);
1061         e |= nvlist_add_uint32(pgnvl, INV_PGRP_NPROP, pcnt);
1062         if (pcnt > 0)
1063                 if (e != 0 ||
1064                     (apl = topo_mod_zalloc(mp, pcnt * sizeof (nvlist_t *)))
1065                     == NULL) {
1066                         xmlFree(name);
1067                         nvlist_free(pgnvl);
1068                         topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
1069                             "failed to allocate nvlist array for properties"
1070                             "(e=%d)\n", e);
1071                         return (topo_mod_seterrno(mp, ETOPO_NOMEM));
1072                 }
1073         for (cn = pxn->xmlChildrenNode; cn != NULL; cn = cn->next) {
1074                 if (xmlStrcmp(cn->name, (xmlChar *)Propval) == 0) {
1075                         if (ai < pcnt) {
1076                                 if ((apl[ai] = pval_record(mp, cn)) == NULL)
1077                                         break;
1078                         }
1079                         ai++;
1080                 } else if (xmlStrcmp(cn->name, (xmlChar *)Prop_meth) == 0) {
1081                         if (pmeth_record(mp, (const char *)name, cn, tn, rname,
1082                             ppgrp_name) < 0)
1083                                 break;
1084                 }
1085         }
1086         xmlFree(name);
1087         if (pcnt > 0) {
1088                 e |= (ai != pcnt);
1089                 e |= nvlist_add_nvlist_array(pgnvl, INV_PGRP_ALLPROPS, apl,
1090                     pcnt);
1091                 for (ai = 0; ai < pcnt; ai++)
1092                         nvlist_free(apl[ai]);
1093                 topo_mod_free(mp, apl, pcnt * sizeof (nvlist_t *));
1094                 if (e != 0) {
1095                         nvlist_free(pgnvl);
1096                         return (-1);
1097                 }
1098         }
1099         rpad->tpad_pgs[pi] = pgnvl;
1100         return (0);
1101 }
1102 
1103 static int
1104 pgroups_record(topo_mod_t *mp, xmlNodePtr pxn, tnode_t *tn, const char *rname,
1105     tf_pad_t *rpad, const char *ppgrp)
1106 {
1107         xmlNodePtr cn;
1108         int pi = 0;
1109 
1110         topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "pgroups_record: pxn->name=%s\n",
1111             pxn->name);
1112         for (cn = pxn->xmlChildrenNode; cn != NULL; cn = cn->next) {
1113                 if (xmlStrcmp(cn->name, (xmlChar *)Propgrp) == 0) {
1114                         if (pgroup_record(mp, cn, tn, rname, rpad, pi++, ppgrp)
1115                             < 0)
1116                                 return (-1);
1117                 }
1118         }
1119         return (0);
1120 }
1121 
1122 /*
1123  * psn: pointer to a "set" XML node
1124  * key: string to search the set for
1125  *
1126  * returns: 1, if the set contains key
1127  *          0, otherwise
1128  */
1129 static int
1130 set_contains(topo_mod_t *mp, char *key, char *set)
1131 {
1132         char *prod;
1133         int rv = 0;
1134 
1135         topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "set_contains(key = %s, "
1136             "setlist = %s)\n", key, set);
1137 
1138         prod = strtok((char *)set, "|");
1139         if (prod && (strcmp(key, prod) == 0))
1140                 return (1);
1141 
1142         while ((prod = strtok(NULL, "|")))
1143                 if (strcmp(key, prod) == 0)
1144                         return (1);
1145 
1146         return (rv);
1147 }
1148 
1149 
1150 /*
1151  * Process the property group and dependents xmlNode children of
1152  * parent xmlNode pxn.
1153  */
1154 static int
1155 pad_process(topo_mod_t *mp, tf_rdata_t *rd, xmlNodePtr pxn, tnode_t *ptn,
1156     tf_pad_t **rpad)
1157 {
1158         xmlNodePtr cn, gcn, psn, ecn, target;
1159         xmlNodePtr def_set = NULL;
1160         tnode_t *ct;
1161         tf_pad_t *new = *rpad;
1162         tf_rdata_t tmp_rd;
1163         int pgcnt = 0;
1164         int dcnt = 0;
1165         int ecnt = 0;
1166         int joined_set = 0, inst;
1167         xmlChar *set;
1168         char *key;
1169 
1170         topo_dprintf(mp->tm_hdl, TOPO_DBG_XML,
1171             "pad_process beneath %s=%d\n", topo_node_name(ptn),
1172             topo_node_instance(ptn));
1173         if (new == NULL) {
1174                 for (cn = pxn->xmlChildrenNode; cn != NULL; cn = cn->next) {
1175                         topo_dprintf(mp->tm_hdl, TOPO_DBG_XML,
1176                             "cn->name is %s \n", (char *)cn->name);
1177                         /*
1178                          * We're iterating through the XML children looking for
1179                          * four types of elements:
1180                          *   1) dependents elements
1181                          *   2) unconstrained pgroup elements
1182                          *   3) pgroup elements constrained by set elements
1183                          *   4) enum-method elements for the case that we want
1184                          *      to post-process a statically defined node
1185                          */
1186                         if (xmlStrcmp(cn->name, (xmlChar *)Dependents) == 0)
1187                                 dcnt++;
1188                         else if (xmlStrcmp(cn->name, (xmlChar *)Propgrp) == 0)
1189                                 pgcnt++;
1190                         else if (xmlStrcmp(cn->name, (xmlChar *)Enum_meth)
1191                             == 0) {
1192                                 ecn = cn;
1193                                 ecnt++;
1194                         } else if (xmlStrcmp(cn->name, (xmlChar *)Set) == 0) {
1195                                 if (joined_set)
1196                                         continue;
1197                                 set = xmlGetProp(cn, (xmlChar *)Setlist);
1198 
1199                                 if (mp->tm_hdl->th_product)
1200                                         key = mp->tm_hdl->th_product;
1201                                 else
1202                                         key = mp->tm_hdl->th_platform;
1203 
1204                                 /*
1205                                  * If it's the default set then we'll store
1206                                  * a pointer to it so that if none of the other
1207                                  * sets apply to our product we can fall
1208                                  * back to this one.
1209                                  */
1210                                 if (strcmp((char *)set, "default") == 0)
1211                                         def_set = cn;
1212                                 else if (set_contains(mp, key, (char *)set)) {
1213                                         psn = cn;
1214                                         joined_set = 1;
1215                                         for (gcn = cn->xmlChildrenNode;
1216                                             gcn != NULL; gcn = gcn->next) {
1217                                                 if (xmlStrcmp(gcn->name,
1218                                                     (xmlChar *)Propgrp) == 0)
1219                                                         pgcnt++;
1220                                         }
1221                                 }
1222                                 xmlFree(set);
1223                         }
1224                 }
1225                 /*
1226                  * If we haven't found a set that contains our product AND
1227                  * a default set exists, then we'll process it.
1228                  */
1229                 if (!joined_set && def_set) {
1230                         topo_dprintf(mp->tm_hdl, TOPO_DBG_XML,
1231                             "Falling back to default set\n");
1232                         joined_set = 1;
1233                         psn = def_set;
1234                         for (gcn = psn->xmlChildrenNode; gcn != NULL;
1235                             gcn = gcn->next) {
1236                                 if (xmlStrcmp(gcn->name, (xmlChar *)Propgrp)
1237                                     == 0)
1238                                         pgcnt++;
1239                         }
1240                 }
1241                 topo_dprintf(mp->tm_hdl, TOPO_DBG_XML,
1242                     "pad_process: dcnt=%d, pgcnt=%d, ecnt=%d, joined_set=%d\n",
1243                     dcnt, pgcnt, ecnt, joined_set);
1244                 /*
1245                  * If an enum-method element was found, AND we're a child of a
1246                  * node element, then we invoke the enumerator so that it can do
1247                  * post-processing of the node.
1248                  */
1249                 if (ecnt && (strcmp((const char *)pxn->name, Node) == 0)) {
1250                         if ((tmp_rd.rd_einfo = enum_attributes_process(mp, ecn))
1251                             == NULL)
1252                                 return (-1);
1253                         tmp_rd.rd_mod = mp;
1254                         tmp_rd.rd_name = rd->rd_name;
1255                         tmp_rd.rd_min = rd->rd_min;
1256                         tmp_rd.rd_max = rd->rd_max;
1257                         tmp_rd.rd_pn = ptn;
1258                         if (enum_run(mp, &tmp_rd) < 0) {
1259                                 /*
1260                                  * Note the failure but continue on
1261                                  */
1262                                 topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
1263                                     "pad_process: enumeration failed.\n");
1264                         }
1265                         tf_edata_free(mp, tmp_rd.rd_einfo);
1266                 }
1267                 /*
1268                  * Here we allocate an element in an intermediate data structure
1269                  * which keeps track property groups and dependents of the range
1270                  * currently being processed.
1271                  *
1272                  * This structure is referenced in pgroups_record() to create
1273                  * the actual property groups in the topo tree
1274                  */
1275                 if ((new = tf_pad_new(mp, pgcnt, dcnt)) == NULL)
1276                         return (-1);
1277 
1278                 if (pgcnt > 0) {
1279                         new->tpad_pgs =
1280                             topo_mod_zalloc(mp, pgcnt * sizeof (nvlist_t *));
1281                         if (new->tpad_pgs == NULL) {
1282                                 tf_pad_free(mp, new);
1283                                 return (-1);
1284                         }
1285                 }
1286                 /*
1287                  * If the property groups are contained within a set
1288                  * then they will be one level lower in the XML tree.
1289                  */
1290                 if (joined_set)
1291                         target = psn;
1292                 else
1293                         target = pxn;
1294 
1295                 /*
1296                  * If there is no "node" element under the "range"
1297                  * element, then we need to attach the facility node to
1298                  * each node in this range.
1299                  *
1300                  * Otherwise we only attach it to the current node
1301                  */
1302                 if (xmlStrcmp(target->name, (xmlChar *)Range) == 0 ||
1303                     xmlStrcmp(target->name, (xmlChar *)Set) == 0) {
1304                         for (ct = topo_child_first(rd->rd_pn);
1305                             ct != NULL;
1306                             ct = topo_child_next(rd->rd_pn, ct)) {
1307 
1308                                 if (strcmp(topo_node_name(ct),
1309                                     rd->rd_name) != 0)
1310                                         continue;
1311 
1312                                 inst = topo_node_instance(ct);
1313                                 if (inst < rd->rd_min || inst > rd->rd_max)
1314                                         continue;
1315 
1316                                 if (fac_enum_process(mp, target, ct) < 0)
1317                                         return (-1);
1318 
1319                                 if (fac_process(mp, target, rd, ct) < 0)
1320                                         return (-1);
1321                         }
1322                 } else {
1323                         if (fac_enum_process(mp, target, ptn) < 0)
1324                                 return (-1);
1325                         if (fac_process(mp, target, rd, ptn) < 0)
1326                                 return (-1);
1327                 }
1328                 if (pgcnt > 0 && pgroups_record(mp, target, ptn, rd->rd_name,
1329                     new, (const char *)pxn->name) < 0) {
1330                         tf_pad_free(mp, new);
1331                         return (-1);
1332                 }
1333                 *rpad = new;
1334         }
1335 
1336         if (new->tpad_dcnt > 0)
1337                 if (dependents_create(mp, rd->rd_finfo, new, pxn, ptn) < 0)
1338                         return (-1);
1339 
1340         if (new->tpad_pgcnt > 0)
1341                 if (pgroups_create(mp, new, ptn) < 0)
1342                         return (-1);
1343 
1344         return (0);
1345 }
1346 
1347 
1348 static int
1349 fac_enum_process(topo_mod_t *mp, xmlNodePtr pn, tnode_t *ptn)
1350 {
1351         xmlNodePtr cn;
1352         xmlChar *fprov = NULL;
1353         int rv = 0;
1354 
1355         topo_dprintf(mp->tm_hdl, TOPO_DBG_XML,
1356             "fac_enum_process() called for %s=%d\n", topo_node_name(ptn),
1357             topo_node_instance(ptn));
1358 
1359         for (cn = pn->xmlChildrenNode; cn != NULL; cn = cn->next) {
1360 
1361                 if (xmlStrcmp(cn->name, (xmlChar *)"fac-enum") != 0)
1362                         continue;
1363 
1364                 if ((fprov = xmlGetProp(cn, (xmlChar *)Provider)) == NULL)
1365                         goto fenumdone;
1366                 /*
1367                  * Invoke enum entry point in facility provider which will
1368                  * cause the facility enumeration node method to be
1369                  * registered.
1370                  */
1371                 if (fac_enum_run(mp, ptn, (const char *)fprov) != 0) {
1372                         topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
1373                             "fac_enum_process: enum entry point failed!\n");
1374                         goto fenumdone;
1375                 }
1376                 xmlFree(fprov);
1377         }
1378         return (0);
1379 fenumdone:
1380         topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "fac-enum processing failed\n");
1381 
1382         if (fprov != NULL)
1383                 xmlFree(fprov);
1384 
1385         return (rv);
1386 }
1387 
1388 
1389 static int
1390 fac_process(topo_mod_t *mp, xmlNodePtr pn, tf_rdata_t *rd, tnode_t *ptn)
1391 {
1392         xmlNodePtr cn;
1393         xmlChar *fname = NULL, *ftype = NULL, *provider = NULL;
1394         tnode_t *ntn = NULL;
1395         tf_idata_t *newi;
1396         int err;
1397         topo_pgroup_info_t pgi;
1398 
1399         topo_dprintf(mp->tm_hdl, TOPO_DBG_XML,
1400             "fac_process() called for %s=%d\n", topo_node_name(ptn),
1401             topo_node_instance(ptn));
1402 
1403         for (cn = pn->xmlChildrenNode; cn != NULL; cn = cn->next) {
1404 
1405                 if (xmlStrcmp(cn->name, (xmlChar *)Facility) != 0)
1406                         continue;
1407 
1408                 if ((fname = xmlGetProp(cn, (xmlChar *)Name)) == NULL)
1409                         goto facdone;
1410 
1411                 topo_dprintf(mp->tm_hdl, TOPO_DBG_XML,
1412                     "processing facility node '%s'\n", fname);
1413 
1414                 if ((ftype = xmlGetProp(cn, (xmlChar *)Type)) == NULL)
1415                         goto facdone;
1416 
1417                 if ((provider = xmlGetProp(cn, (xmlChar *)Provider)) == NULL)
1418                         goto facdone;
1419 
1420                 if (xmlStrcmp(ftype, (xmlChar *)Sensor) != 0 &&
1421                     xmlStrcmp(ftype, (xmlChar *)Indicator) != 0)
1422                         goto facdone;
1423 
1424                 if ((ntn = topo_node_facbind(mp, ptn, (char *)fname,
1425                     (char *)ftype)) == NULL)
1426                         goto facdone;
1427 
1428                 pgi.tpi_name = TOPO_PGROUP_FACILITY;
1429                 pgi.tpi_namestab = TOPO_STABILITY_PRIVATE;
1430                 pgi.tpi_datastab = TOPO_STABILITY_PRIVATE;
1431                 pgi.tpi_version = 1;
1432                 if (topo_pgroup_create(ntn, &pgi, &err) != 0) {
1433                         if (err != ETOPO_PROP_DEFD) {
1434                                 topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
1435                                     "pgroups create failure: %s\n",
1436                                     topo_strerror(err));
1437                                 return (-1);
1438                         }
1439                 }
1440                 /*
1441                  * Invoke enum entry point in the facility provider module,
1442                  * which will cause the provider methods to be registered on
1443                  * this node
1444                  */
1445                 if (fac_enum_run(mp, ntn, (const char *)provider) != 0) {
1446                         topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR, "fac_process: "
1447                             "enum entry point failed for provider %s!\n",
1448                             provider);
1449                         goto facdone;
1450                 }
1451 
1452                 if ((newi = tf_idata_new(mp, 0, ntn)) == NULL)
1453                         goto facdone;
1454 
1455                 if (tf_idata_insert(&rd->rd_instances, newi) < 0)
1456                         goto facdone;
1457 
1458                 if (pad_process(mp, rd, cn, ntn, &newi->ti_pad) < 0)
1459                         goto facdone;
1460 
1461                 topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "done with "
1462                     "facility %s=%s.\n", ftype, fname);
1463 
1464                 xmlFree(ftype);
1465                 xmlFree(fname);
1466                 xmlFree(provider);
1467         }
1468 
1469         return (0);
1470 
1471 facdone:
1472         topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR, "facility processing failed\n");
1473 
1474         if (ftype != NULL)
1475                 xmlFree(ftype);
1476         if (fname != NULL)
1477                 xmlFree(fname);
1478         if (provider != NULL)
1479                 xmlFree(provider);
1480         if (ntn != NULL)
1481                 topo_node_unbind(ntn);
1482 
1483         return (0);
1484 }
1485 
1486 static int
1487 node_process(topo_mod_t *mp, xmlNodePtr nn, tf_rdata_t *rd)
1488 {
1489         xmlChar *str;
1490         topo_instance_t inst;
1491         tf_idata_t *newi;
1492         tnode_t *ntn;
1493         uint64_t ui;
1494         int rv = -1;
1495         int s = 0;
1496 
1497         topo_dprintf(mp->tm_hdl, TOPO_DBG_XML,
1498             "node_process %s\n", rd->rd_name);
1499 
1500         if (xmlattr_to_int(mp, nn, Instance, &ui) < 0)
1501                 goto nodedone;
1502         inst = (topo_instance_t)ui;
1503 
1504         if ((str = xmlGetProp(nn, (xmlChar *)Static)) != NULL) {
1505                 if (xmlStrcmp(str, (xmlChar *)True) == 0)
1506                         s = 1;
1507                 xmlFree(str);
1508         }
1509 
1510         if (s == 0) {
1511                 if (topo_mod_enumerate(rd->rd_mod, rd->rd_pn,
1512                     rd->rd_finfo->tf_scheme, rd->rd_name, inst, inst,
1513                     s == 1 ? &s : NULL) < 0)
1514                         goto nodedone;
1515         }
1516         ntn = topo_node_lookup(rd->rd_pn, rd->rd_name, inst);
1517 
1518         if (ntn == NULL) {
1519 
1520                 /*
1521                  * If this is a static node declaration, we can
1522                  * ignore the lookup failure and continue
1523                  * processing.  Otherwise, something
1524                  * went wrong during enumeration
1525                  */
1526                 if (s == 1)
1527                         rv = 0;
1528                 goto nodedone;
1529         }
1530         if ((newi = tf_idata_new(mp, inst, ntn)) == NULL) {
1531                 topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
1532                     "node_process: tf_idata_new failed.\n");
1533                 goto nodedone;
1534         }
1535         if (tf_idata_insert(&rd->rd_instances, newi) < 0) {
1536                 topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
1537                     "node_process: tf_idata_insert failed.\n");
1538                 goto nodedone;
1539         }
1540         if (pad_process(mp, rd, nn, ntn, &newi->ti_pad) < 0)
1541                 goto nodedone;
1542         if (fac_process(mp, nn, rd, ntn) < 0)
1543                 goto nodedone;
1544         rv = 0;
1545 nodedone:
1546         topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "done with node %s.\n",
1547             rd->rd_name);
1548         return (rv);
1549 }
1550 
1551 static tf_edata_t *
1552 enum_attributes_process(topo_mod_t *mp, xmlNodePtr en)
1553 {
1554         tf_edata_t *einfo;
1555         uint64_t ui;
1556 
1557         topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "enum_attributes_process\n");
1558         if ((einfo = topo_mod_zalloc(mp, sizeof (tf_edata_t))) == NULL) {
1559                 (void) topo_mod_seterrno(mp, ETOPO_NOMEM);
1560                 return (NULL);
1561         }
1562         einfo->te_name = (char *)xmlGetProp(en, (xmlChar *)Name);
1563         if (einfo->te_name == NULL) {
1564                 topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
1565                     "Enumerator name attribute missing.\n");
1566                 (void) topo_mod_seterrno(mp, ETOPO_PRSR_NOATTR);
1567                 goto enodedone;
1568         }
1569 
1570         /*
1571          * Check for recursive enumeration
1572          */
1573         if (strcmp(einfo->te_name, mp->tm_name) == 0) {
1574                 topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
1575                     "Recursive enumeration detected for %s\n",
1576                     einfo->te_name);
1577                 (void) topo_mod_seterrno(mp, ETOPO_ENUM_RECURS);
1578                 goto enodedone;
1579         }
1580         if (xmlattr_to_int(mp, en, Version, &ui) < 0)
1581                 goto enodedone;
1582         einfo->te_vers = (int)ui;
1583 
1584         return (einfo);
1585 
1586 enodedone:
1587         if (einfo->te_name != NULL)
1588                 xmlFree(einfo->te_name);
1589         topo_mod_free(mp, einfo, sizeof (tf_edata_t));
1590         return (NULL);
1591 }
1592 
1593 static int
1594 enum_run(topo_mod_t *mp, tf_rdata_t *rd)
1595 {
1596         topo_hdl_t *thp = mp->tm_hdl;
1597         int e = -1;
1598 
1599         topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "enum_run\n");
1600         /*
1601          * Check if the enumerator module is already loaded.
1602          * Module loading is single-threaded at this point so there's
1603          * no need to worry about the module going away or bumping the
1604          * ref count.
1605          */
1606         if ((rd->rd_mod = topo_mod_lookup(thp, rd->rd_einfo->te_name,
1607             0)) == NULL) {
1608                 if ((rd->rd_mod = topo_mod_load(mp, rd->rd_einfo->te_name,
1609                     rd->rd_einfo->te_vers)) == NULL) {
1610                         topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
1611                             "enum_run: mod_load of %s failed: %s.\n",
1612                             rd->rd_einfo->te_name,
1613                             topo_strerror(topo_mod_errno(mp)));
1614                         (void) topo_hdl_seterrno(thp, topo_mod_errno(mp));
1615                         return (e);
1616                 }
1617         }
1618         /*
1619          * We're live, so let's enumerate.
1620          */
1621         topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "enumerate request. (%s)\n",
1622             rd->rd_einfo->te_name);
1623         e = topo_mod_enumerate(rd->rd_mod, rd->rd_pn, rd->rd_einfo->te_name,
1624             rd->rd_name, rd->rd_min, rd->rd_max, NULL);
1625         topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "back from enumeration. %d\n",
1626             e);
1627         if (e != 0) {
1628                 topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
1629                     "Enumeration failed (%s)\n",
1630                     topo_strerror(topo_mod_errno(mp)));
1631                 (void) topo_hdl_seterrno(thp, EMOD_PARTIAL_ENUM);
1632                 return (topo_mod_seterrno(mp, EMOD_PARTIAL_ENUM));
1633         }
1634         return (e);
1635 }
1636 
1637 static int
1638 fac_enum_run(topo_mod_t *mp, tnode_t *node, const char *name)
1639 {
1640         topo_hdl_t *thp = mp->tm_hdl;
1641         topo_mod_t *fmod;
1642         int e = -1;
1643 
1644         topo_dprintf(thp, TOPO_DBG_XML, "fac_enum_run\n");
1645         /*
1646          * Check if the enumerator module is already loaded.
1647          * Module loading is single-threaded at this point so there's
1648          * no need to worry about the module going away or bumping the
1649          * ref count.
1650          */
1651         if ((fmod = topo_mod_lookup(thp, name, 0)) == NULL) {
1652                 if ((fmod = topo_mod_load(mp, name, TOPO_VERSION)) == NULL) {
1653                         topo_dprintf(thp, TOPO_DBG_ERR,
1654                             "fac_enum_run: mod_load of %s failed: %s.\n",
1655                             name, topo_strerror(topo_mod_errno(mp)));
1656                         (void) topo_hdl_seterrno(thp, topo_mod_errno(mp));
1657                         return (e);
1658                 }
1659         }
1660         /*
1661          * We're live, so let's enumerate.
1662          */
1663         topo_dprintf(thp, TOPO_DBG_XML, "fac enumerate request. (%s)\n", name);
1664         e = topo_mod_enumerate(fmod, node, name, name, 0, 0, NULL);
1665         topo_dprintf(thp, TOPO_DBG_XML, "back from enumeration. %d\n", e);
1666         if (e != 0) {
1667                 topo_dprintf(thp, TOPO_DBG_ERR,
1668                     "Facility provider enumeration failed (%s)\n",
1669                     topo_strerror(topo_mod_errno(mp)));
1670                 (void) topo_hdl_seterrno(thp, EMOD_PARTIAL_ENUM);
1671                 return (topo_mod_seterrno(mp, EMOD_PARTIAL_ENUM));
1672         }
1673         return (e);
1674 }
1675 
1676 int
1677 decorate_nodes(topo_mod_t *mp, tf_rdata_t *rd, xmlNodePtr pxn, tnode_t *ptn,
1678     tf_pad_t **rpad)
1679 {
1680         tnode_t *ctn;
1681 
1682         ctn = topo_child_first(ptn);
1683         while (ctn != NULL) {
1684                 /* Only care about instances within the range */
1685                 if (strcmp(topo_node_name(ctn), rd->rd_name) != 0) {
1686                         ctn = topo_child_next(ptn, ctn);
1687                         continue;
1688                 }
1689                 if (pad_process(mp, rd, pxn, ctn, rpad) < 0)
1690                         return (-1);
1691                 if (decorate_nodes(mp, rd, pxn, ctn, rpad) < 0)
1692                         return (-1);
1693                 ctn = topo_child_next(ptn, ctn);
1694         }
1695         return (0);
1696 }
1697 
1698 int
1699 topo_xml_range_process(topo_mod_t *mp, xmlNodePtr rn, tf_rdata_t *rd)
1700 {
1701         /*
1702          * The range may have several children xmlNodes, that may
1703          * represent the enumeration method, property groups,
1704          * dependents, nodes or services.
1705          */
1706         xmlNodePtr cn, enum_node = NULL, pmap_node = NULL;
1707         xmlChar *pmap_name;
1708         tnode_t *ct;
1709         int e, ccnt = 0;
1710 
1711         topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "topo_xml_range_process\n"
1712             "process %s range beneath %s\n", rd->rd_name,
1713             topo_node_name(rd->rd_pn));
1714 
1715         e = topo_node_range_create(mp,
1716             rd->rd_pn, rd->rd_name, rd->rd_min, rd->rd_max);
1717         if (e != 0 && topo_mod_errno(mp) != EMOD_NODE_DUP) {
1718                 topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
1719                     "Range create failed due to %s.\n",
1720                     topo_strerror(topo_mod_errno(mp)));
1721                 return (-1);
1722         }
1723 
1724         /*
1725          * Before we process any of the other child xmlNodes, we iterate through
1726          * the children and looking for either enum-method or propmap elements.
1727          */
1728         for (cn = rn->xmlChildrenNode; cn != NULL; cn = cn->next)
1729                 if (xmlStrcmp(cn->name, (xmlChar *)Enum_meth) == 0)
1730                         enum_node = cn;
1731                 else if (xmlStrcmp(cn->name, (xmlChar *)Propmap) == 0)
1732                         pmap_node = cn;
1733 
1734         /*
1735          * If we found an enum-method element, process it first
1736          */
1737         if (enum_node != NULL) {
1738                 if ((rd->rd_einfo = enum_attributes_process(mp, enum_node))
1739                     == NULL)
1740                         return (-1);
1741                 if (enum_run(mp, rd) < 0) {
1742                         /*
1743                          * Note the failure but continue on
1744                          */
1745                         topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
1746                             "Enumeration failed.\n");
1747                 }
1748         }
1749 
1750         /*
1751          * Next, check if a propmap element was found and if so, load it in
1752          * and parse it.
1753          */
1754         if (pmap_node != NULL) {
1755                 topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "found a propmap "
1756                     "element\n");
1757                 if ((pmap_name = xmlGetProp(pmap_node, (xmlChar *)Name))
1758                     == NULL) {
1759                         topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
1760                             "propmap element missing name attribute.\n");
1761                 } else {
1762                         if (topo_file_load(mp, rd->rd_pn,
1763                             (const char *)pmap_name,
1764                             rd->rd_finfo->tf_scheme, 1) < 0) {
1765 
1766                                 topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
1767                                     "topo_xml_range_process: topo_file_load"
1768                                     "failed: %s.\n",
1769                                     topo_strerror(topo_mod_errno(mp)));
1770                         }
1771                         xmlFree(pmap_name);
1772                 }
1773         }
1774 
1775         /* Now look for nodes, i.e., hard instances */
1776         for (cn = rn->xmlChildrenNode; cn != NULL; cn = cn->next) {
1777                 if (xmlStrcmp(cn->name, (xmlChar *)Node) == 0) {
1778                         if (node_process(mp, cn, rd) < 0) {
1779                                 topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
1780                                     "node processing failed: %s.\n",
1781                                     topo_strerror(topo_mod_errno(mp)));
1782                                 return (topo_mod_seterrno(mp,
1783                                     EMOD_PARTIAL_ENUM));
1784                         }
1785                         ccnt++;
1786                 }
1787         }
1788 
1789         /*
1790          * Finally, process the property groups and dependents
1791          *
1792          * If the TF_PROPMAP flag is set for the XML file we're currently
1793          * processing, then this XML file was loaded via propmap.  In that case
1794          * we call a special routine to recursively apply the propgroup settings
1795          * to all of nodes in this range
1796          */
1797         if (rd->rd_finfo->tf_flags & TF_PROPMAP)
1798                 (void) decorate_nodes(mp, rd, rn, rd->rd_pn, &rd->rd_pad);
1799         else {
1800                 ct = topo_child_first(rd->rd_pn);
1801                 while (ct != NULL) {
1802                         /* Only care about instances within the range */
1803                         if (strcmp(topo_node_name(ct), rd->rd_name) != 0) {
1804                                 ct = topo_child_next(rd->rd_pn, ct);
1805                                 continue;
1806                         }
1807                         if (pad_process(mp, rd, rn, ct, &rd->rd_pad)
1808                             < 0)
1809                                 return (-1);
1810 
1811                         if (fac_process(mp, rn, rd, ct) < 0)
1812                                 return (-1);
1813 
1814                         ct = topo_child_next(rd->rd_pn, ct);
1815                         ccnt++;
1816                 }
1817         }
1818 
1819         topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "topo_xml_range_process: end "
1820             "range process %s\n", rd->rd_name);
1821 
1822         return (0);
1823 }
1824 
1825 static tf_rdata_t *
1826 topo_xml_walk(topo_mod_t *mp,
1827     tf_info_t *xinfo, xmlNodePtr croot, tnode_t *troot)
1828 {
1829         xmlNodePtr curr, def_set = NULL;
1830         tf_rdata_t *rr, *pr, *rdp;
1831         xmlChar *set;
1832         char *key;
1833         int joined_set = 0;
1834 
1835         topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "topo_xml_walk\n");
1836         rr = pr = NULL;
1837         /*
1838          * First iterate through all the XML nodes at this level to look for
1839          * set nodes.
1840          */
1841         for (curr = croot->xmlChildrenNode; curr != NULL; curr = curr->next) {
1842                 if (curr->name == NULL) {
1843                         topo_dprintf(mp->tm_hdl, TOPO_DBG_XML,
1844                             "topo_xml_walk: Ignoring nameless xmlnode\n");
1845                         continue;
1846                 }
1847                 if (xmlStrcmp(curr->name, (xmlChar *)Set) == 0) {
1848                         if (joined_set)
1849                                 continue;
1850 
1851                         set = xmlGetProp(curr, (xmlChar *)Setlist);
1852 
1853                         if (mp->tm_hdl->th_product)
1854                                 key = mp->tm_hdl->th_product;
1855                         else
1856                                 key = mp->tm_hdl->th_platform;
1857 
1858                         /*
1859                          * If it's the default set then we'll store
1860                          * a pointer to it so that if none of the other
1861                          * sets apply to our product we can fall
1862                          * back to this one.
1863                          */
1864                         if (strcmp((char *)set, "default") == 0)
1865                                 def_set = curr;
1866                         else if (set_contains(mp, key, (char *)set)) {
1867                                 joined_set = 1;
1868                                 if ((rdp = topo_xml_walk(mp, xinfo, curr,
1869                                     troot)) == NULL) {
1870                                         topo_dprintf(mp->tm_hdl, TOPO_DBG_XML,
1871                                             "topo_xml_walk: failed1\n");
1872                                 } else {
1873                                         if (pr == NULL) {
1874                                                 rr = pr = rdp;
1875                                         } else {
1876                                                 pr->rd_next = rdp;
1877                                                 pr = rdp;
1878                                         }
1879                                         rr->rd_cnt++;
1880                                 }
1881                         }
1882                         xmlFree(set);
1883                 }
1884         }
1885         /*
1886          * If we haven't found a set that contains our product AND a default set
1887          * exists, then we'll process it.
1888          */
1889         if (!joined_set && def_set) {
1890                 if ((rdp = topo_xml_walk(mp, xinfo, def_set, troot)) == NULL) {
1891                         topo_dprintf(mp->tm_hdl, TOPO_DBG_XML,
1892                             "topo_xml_walk: failed2\n");
1893                 }
1894                 if (pr == NULL) {
1895                         rr = pr = rdp;
1896                 } else {
1897                         pr->rd_next = rdp;
1898                         pr = rdp;
1899                 }
1900                 rr->rd_cnt++;
1901         }
1902         /*
1903          * Now we're interested in children xmlNodes of croot tagged
1904          * as 'ranges'.  These define what topology nodes may exist, and need
1905          * to be verified.
1906          */
1907         for (curr = croot->xmlChildrenNode; curr != NULL; curr = curr->next) {
1908                 if (curr->name == NULL) {
1909                         topo_dprintf(mp->tm_hdl, TOPO_DBG_XML,
1910                             "topo_xml_walk: Ignoring nameless xmlnode\n");
1911                         continue;
1912                 }
1913                 if (xmlStrcmp(curr->name, (xmlChar *)Range) != 0)
1914                         continue;
1915                 if ((rdp = tf_rdata_new(mp, xinfo, curr, troot)) == NULL) {
1916                         /*
1917                          * Range processing error, continue walk
1918                          */
1919                         continue;
1920                 }
1921                 if (pr == NULL) {
1922                         rr = pr = rdp;
1923                 } else {
1924                         pr->rd_next = rdp;
1925                         pr = rdp;
1926                 }
1927                 rr->rd_cnt++;
1928         }
1929 
1930         return (rr);
1931 }
1932 
1933 /*
1934  *  Convert parsed xml topology description into topology nodes
1935  */
1936 int
1937 topo_xml_enum(topo_mod_t *tmp, tf_info_t *xinfo, tnode_t *troot)
1938 {
1939         xmlNodePtr xroot;
1940 
1941         topo_dprintf(tmp->tm_hdl, TOPO_DBG_XML, "topo_xml_enum\n");
1942 
1943         if ((xroot = xmlDocGetRootElement(xinfo->tf_xdoc)) == NULL) {
1944                 topo_dprintf(tmp->tm_hdl, TOPO_DBG_ERR,
1945                     "Couldn't get root xmlNode.\n");
1946                 return (-1);
1947         }
1948         if ((xinfo->tf_rd = topo_xml_walk(tmp, xinfo, xroot, troot)) == NULL) {
1949                 topo_dprintf(tmp->tm_hdl, TOPO_DBG_ERR,
1950                     "error within .xml topology: %s\n",
1951                     topo_strerror(topo_mod_errno(tmp)));
1952                 return (-1);
1953         }
1954         return (0);
1955 }
1956 
1957 /*
1958  * Load an XML tree from filename and read it into a DOM parse tree.
1959  */
1960 static tf_info_t *
1961 txml_file_parse(topo_mod_t *tmp,
1962     int fd, const char *filenm, const char *escheme)
1963 {
1964         xmlValidCtxtPtr vcp;
1965         xmlNodePtr cursor;
1966         xmlDocPtr document;
1967         xmlDtdPtr dtd = NULL;
1968         xmlChar *scheme = NULL;
1969         char *dtdpath = NULL;
1970         int readflags = 0;
1971         tf_info_t *r;
1972         int e, validate = 0;
1973 
1974         topo_dprintf(tmp->tm_hdl, TOPO_DBG_XML,
1975             "txml_file_parse(filenm=%s, escheme=%s)\n", filenm, escheme);
1976 
1977         /*
1978          * Since topologies can XInclude other topologies, and libxml2
1979          * doesn't do DTD-based validation with XInclude, by default
1980          * we don't validate topology files.  One can force
1981          * validation, though, by creating a TOPOXML_VALIDATE
1982          * environment variable and creating a TOPO_DTD environment
1983          * variable with the path to the DTD against which to validate.
1984          */
1985         if (getenv("TOPOXML_VALIDATE") != NULL) {
1986                 dtdpath = getenv("TOPO_DTD");
1987                 if (dtdpath != NULL)
1988                         xmlLoadExtDtdDefaultValue = 0;
1989                 validate = 1;
1990         }
1991 
1992         /*
1993          * Splat warnings and errors related to parsing the topology
1994          * file if the TOPOXML_PERROR environment variable exists.
1995          */
1996         if (getenv("TOPOXML_PERROR") == NULL)
1997                 readflags = XML_PARSE_NOERROR | XML_PARSE_NOWARNING;
1998 
1999         if ((document = xmlReadFd(fd, filenm, NULL, readflags)) == NULL) {
2000                 topo_dprintf(tmp->tm_hdl, TOPO_DBG_ERR,
2001                     "txml_file_parse: couldn't parse document.\n");
2002                 return (NULL);
2003         }
2004 
2005         /*
2006          * Verify that this is a document type we understand.
2007          */
2008         if ((dtd = xmlGetIntSubset(document)) == NULL) {
2009                 topo_dprintf(tmp->tm_hdl, TOPO_DBG_ERR,
2010                     "document has no DTD.\n");
2011                 xmlFreeDoc(document);
2012                 return (NULL);
2013         }
2014 
2015         if (strcmp((const char *)dtd->SystemID, TOPO_DTD_PATH) != 0) {
2016                 topo_dprintf(tmp->tm_hdl, TOPO_DBG_ERR,
2017                     "document DTD unknown; bad topology file\n");
2018                 xmlFreeDoc(document);
2019                 return (NULL);
2020         }
2021 
2022         if ((cursor = xmlDocGetRootElement(document)) == NULL) {
2023                 topo_dprintf(tmp->tm_hdl, TOPO_DBG_ERR, "document is empty.\n");
2024                 xmlFreeDoc(document);
2025                 return (NULL);
2026         }
2027 
2028         /*
2029          * Make sure we're looking at a topology description in the
2030          * expected scheme.
2031          */
2032         if (xmlStrcmp(cursor->name, (xmlChar *)Topology) != 0) {
2033                 topo_dprintf(tmp->tm_hdl, TOPO_DBG_ERR,
2034                     "document is not a topology description.\n");
2035                 xmlFreeDoc(document);
2036                 return (NULL);
2037         }
2038         if ((scheme = xmlGetProp(cursor, (xmlChar *)Scheme)) == NULL) {
2039                 topo_dprintf(tmp->tm_hdl, TOPO_DBG_ERR,
2040                     "topology lacks a scheme.\n");
2041                 (void) topo_mod_seterrno(tmp, ETOPO_PRSR_NOATTR);
2042                 xmlFreeDoc(document);
2043                 return (NULL);
2044         }
2045         if (xmlStrcmp(scheme, (xmlChar *)escheme) != 0) {
2046                 topo_dprintf(tmp->tm_hdl, TOPO_DBG_ERR,
2047                     "topology in unrecognized scheme, %s, expecting %s\n",
2048                     scheme, escheme);
2049                 (void) topo_mod_seterrno(tmp, ETOPO_PRSR_BADSCH);
2050                 xmlFree(scheme);
2051                 xmlFreeDoc(document);
2052                 return (NULL);
2053         }
2054 
2055         if (dtdpath != NULL) {
2056                 dtd = xmlParseDTD(NULL, (xmlChar *)dtdpath);
2057                 if (dtd == NULL) {
2058                         topo_dprintf(tmp->tm_hdl, TOPO_DBG_ERR,
2059                             "Could not parse DTD \"%s\".\n",
2060                             dtdpath);
2061                         xmlFree(scheme);
2062                         xmlFreeDoc(document);
2063                         return (NULL);
2064                 }
2065 
2066                 if (document->extSubset != NULL)
2067                         xmlFreeDtd(document->extSubset);
2068 
2069                 document->extSubset = dtd;
2070         }
2071 
2072         if (xmlXIncludeProcessFlags(document, XML_PARSE_XINCLUDE) == -1) {
2073                 xmlFree(scheme);
2074                 xmlFreeDoc(document);
2075                 topo_dprintf(tmp->tm_hdl, TOPO_DBG_ERR,
2076                     "couldn't handle XInclude statements in document\n");
2077                 return (NULL);
2078         }
2079 
2080         if (validate) {
2081                 if ((vcp = xmlNewValidCtxt()) == NULL) {
2082                         xmlFree(scheme);
2083                         xmlFreeDoc(document);
2084                         return (NULL);
2085                 }
2086                 vcp->warning = xmlParserValidityWarning;
2087                 vcp->error = xmlParserValidityError;
2088 
2089                 e = xmlValidateDocument(vcp, document);
2090 
2091                 xmlFreeValidCtxt(vcp);
2092 
2093                 if (e == 0)
2094                         topo_dprintf(tmp->tm_hdl, TOPO_DBG_ERR,
2095                             "Document is not valid.\n");
2096         }
2097 
2098         if ((r = tf_info_new(tmp, document, scheme)) == NULL) {
2099                 xmlFree(scheme);
2100                 xmlFreeDoc(document);
2101                 return (NULL);
2102         }
2103 
2104         xmlFree(scheme);
2105         scheme = NULL;
2106         return (r);
2107 }
2108 
2109 tf_info_t *
2110 topo_xml_read(topo_mod_t *tmp, const char *path, const char *escheme)
2111 {
2112         int fd;
2113         tf_info_t *tip;
2114 
2115         if ((fd = open(path, O_RDONLY)) < 0) {
2116                 topo_dprintf(tmp->tm_hdl, TOPO_DBG_ERR,
2117                     "failed to open %s for reading\n", path);
2118                 return (NULL);
2119         }
2120         tip = txml_file_parse(tmp, fd, path, escheme);
2121         (void) close(fd);
2122         return (tip);
2123 }