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