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