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