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