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