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