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