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