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