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 2008 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 #include <libxml/parser.h>
30 #include <libxml/xinclude.h>
31 #include <sys/fm/protocol.h>
32 #include <assert.h>
33 #include <string.h>
34 #include <strings.h>
35 #include <ctype.h>
36 #include <errno.h>
37 #include <limits.h>
38 #include <fm/libtopo.h>
39 #include <unistd.h>
40 #include <sys/stat.h>
41 #include <fcntl.h>
42 #include <topo_file.h>
43 #include <topo_mod.h>
44 #include <topo_subr.h>
45 #include <topo_alloc.h>
46 #include <topo_parse.h>
47 #include <topo_error.h>
48 
49 static tf_rdata_t *topo_xml_walk(topo_mod_t *, tf_info_t *, xmlNodePtr,
50     tnode_t *);
51 static tf_edata_t *enum_attributes_process(topo_mod_t *, xmlNodePtr);
52 static int enum_run(topo_mod_t *, tf_rdata_t *);
53 static int decorate_nodes(topo_mod_t *, tf_rdata_t *, xmlNodePtr, tnode_t *,
54     tf_pad_t **);
55 
56 
57 int
58 xmlattr_to_stab(topo_mod_t *mp, xmlNodePtr n, const char *stabname,
59     topo_stability_t *rs)
60 {
61 	xmlChar *str;
62 	int rv = 0;
63 
64 	if (n == NULL) {
65 		/* If there is no Stability defined, we default to private */
66 		*rs = TOPO_STABILITY_PRIVATE;
67 		return (0);
68 	}
69 	if ((str = xmlGetProp(n, (xmlChar *)stabname)) == NULL) {
70 		topo_dprintf(mp->tm_hdl, TOPO_DBG_XML,
71 		    "attribute to stability:\n");
72 		return (topo_mod_seterrno(mp, ETOPO_PRSR_NOATTR));
73 	}
74 
75 	if (xmlStrcmp(str, (xmlChar *)Internal) == 0) {
76 		*rs = TOPO_STABILITY_INTERNAL;
77 	} else if (xmlStrcmp(str, (xmlChar *)Private) == 0) {
78 		*rs = TOPO_STABILITY_PRIVATE;
79 	} else if (xmlStrcmp(str, (xmlChar *)Obsolete) == 0) {
80 		*rs = TOPO_STABILITY_OBSOLETE;
81 	} else if (xmlStrcmp(str, (xmlChar *)External) == 0) {
82 		*rs = TOPO_STABILITY_EXTERNAL;
83 	} else if (xmlStrcmp(str, (xmlChar *)Unstable) == 0) {
84 		*rs = TOPO_STABILITY_UNSTABLE;
85 	} else if (xmlStrcmp(str, (xmlChar *)Evolving) == 0) {
86 		*rs = TOPO_STABILITY_EVOLVING;
87 	} else if (xmlStrcmp(str, (xmlChar *)Stable) == 0) {
88 		*rs = TOPO_STABILITY_STABLE;
89 	} else if (xmlStrcmp(str, (xmlChar *)Standard) == 0) {
90 		*rs = TOPO_STABILITY_STANDARD;
91 	} else {
92 		xmlFree(str);
93 		return (topo_mod_seterrno(mp, ETOPO_PRSR_BADSTAB));
94 	}
95 	xmlFree(str);
96 	return (rv);
97 }
98 
99 int
100 xmlattr_to_int(topo_mod_t *mp,
101     xmlNodePtr n, const char *propname, uint64_t *value)
102 {
103 	xmlChar *str;
104 	xmlChar *estr;
105 
106 	topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "attribute to int\n");
107 	if ((str = xmlGetProp(n, (xmlChar *)propname)) == NULL)
108 		return (topo_mod_seterrno(mp, ETOPO_PRSR_NOATTR));
109 	*value = strtoull((char *)str, (char **)&estr, 10);
110 	if (estr == str) {
111 		/* no conversion was done */
112 		xmlFree(str);
113 		return (topo_mod_seterrno(mp, ETOPO_PRSR_BADNUM));
114 	}
115 	xmlFree(str);
116 	return (0);
117 }
118 
119 static int
120 xmlattr_to_fmri(topo_mod_t *mp,
121     xmlNodePtr xn, const char *propname, nvlist_t **rnvl)
122 {
123 	xmlChar *str;
124 
125 	topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "attribute to int\n");
126 	if ((str = xmlGetProp(xn, (xmlChar *)propname)) == NULL)
127 		return (topo_mod_seterrno(mp, ETOPO_PRSR_NOATTR));
128 	if (topo_mod_str2nvl(mp, (const char *)str, rnvl) < 0) {
129 		xmlFree(str);
130 		return (-1);
131 	}
132 	xmlFree(str);
133 	return (0);
134 }
135 
136 static topo_type_t
137 xmlattr_to_type(topo_mod_t *mp, xmlNodePtr xn, xmlChar *attr)
138 {
139 	topo_type_t rv;
140 	xmlChar *str;
141 	if ((str = xmlGetProp(xn, (xmlChar *)attr)) == NULL) {
142 		topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR, "%s attribute missing",
143 		    attr);
144 		(void) topo_mod_seterrno(mp, ETOPO_PRSR_NOATTR);
145 		return (TOPO_TYPE_INVALID);
146 	}
147 	if (xmlStrcmp(str, (xmlChar *)Int32) == 0) {
148 		rv = TOPO_TYPE_INT32;
149 	} else if (xmlStrcmp(str, (xmlChar *)UInt32) == 0) {
150 		rv = TOPO_TYPE_UINT32;
151 	} else if (xmlStrcmp(str, (xmlChar *)Int64) == 0) {
152 		rv = TOPO_TYPE_INT64;
153 	} else if (xmlStrcmp(str, (xmlChar *)UInt64) == 0) {
154 		rv = TOPO_TYPE_UINT64;
155 	} else if (xmlStrcmp(str, (xmlChar *)FMRI) == 0) {
156 		rv = TOPO_TYPE_FMRI;
157 	} else if (xmlStrcmp(str, (xmlChar *)String) == 0) {
158 		rv = TOPO_TYPE_STRING;
159 	} else {
160 		xmlFree(str);
161 		topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
162 		    "Unrecognized type attribute value.\n");
163 		(void) topo_mod_seterrno(mp, ETOPO_PRSR_BADTYPE);
164 		return (TOPO_TYPE_INVALID);
165 	}
166 	xmlFree(str);
167 	return (rv);
168 }
169 
170 static int
171 xlate_common(topo_mod_t *mp, xmlNodePtr xn, topo_type_t ptype, nvlist_t *nvl,
172 const char *name)
173 {
174 	int rv;
175 	uint64_t ui;
176 	nvlist_t *fmri;
177 	xmlChar *str;
178 
179 	topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "xlate_common\n");
180 	switch (ptype) {
181 	case TOPO_TYPE_INT32:
182 		if (xmlattr_to_int(mp, xn, Value, &ui) < 0)
183 			return (-1);
184 		rv = nvlist_add_int32(nvl, name, (int32_t)ui);
185 		break;
186 	case TOPO_TYPE_UINT32:
187 		if (xmlattr_to_int(mp, xn, Value, &ui) < 0)
188 			return (-1);
189 		rv = nvlist_add_uint32(nvl, name, (uint32_t)ui);
190 		break;
191 	case TOPO_TYPE_INT64:
192 		if (xmlattr_to_int(mp, xn, Value, &ui) < 0)
193 			return (-1);
194 		rv = nvlist_add_int64(nvl, name, (int64_t)ui);
195 		break;
196 	case TOPO_TYPE_UINT64:
197 		if (xmlattr_to_int(mp, xn, Value, &ui) < 0)
198 			return (-1);
199 		rv = nvlist_add_uint64(nvl, name, ui);
200 		break;
201 	case TOPO_TYPE_FMRI:
202 		if (xmlattr_to_fmri(mp, xn, Value, &fmri) < 0)
203 			return (-1);
204 		rv = nvlist_add_nvlist(nvl, name, fmri);
205 		nvlist_free(fmri);
206 		break;
207 	case TOPO_TYPE_STRING:
208 		if ((str = xmlGetProp(xn, (xmlChar *)Value)) == NULL)
209 			return (-1);
210 		rv = nvlist_add_string(nvl, name, (char *)str);
211 		xmlFree(str);
212 		break;
213 	default:
214 		topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
215 		    "Unrecognized type attribute.\n");
216 		return (topo_mod_seterrno(mp, ETOPO_PRSR_BADTYPE));
217 	}
218 	if (rv != 0) {
219 		topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
220 		    "Nvlist construction failed.\n");
221 		return (topo_mod_seterrno(mp, ETOPO_NOMEM));
222 	} else
223 		return (0);
224 }
225 
226 static int
227 xmlprop_xlate(topo_mod_t *mp, xmlNodePtr xn, nvlist_t *nvl)
228 {
229 	topo_type_t ptype;
230 	xmlChar *str;
231 
232 	topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "xmlprop_xlate\n");
233 	if ((str = xmlGetProp(xn, (xmlChar *)Immutable)) != NULL) {
234 		if (xmlStrcmp(str, (xmlChar *)False) == 0)
235 			(void) nvlist_add_boolean_value(nvl, INV_IMMUTE,
236 			    B_FALSE);
237 		else
238 			(void) nvlist_add_boolean_value(nvl, INV_IMMUTE,
239 			    B_TRUE);
240 		xmlFree(str);
241 	} else {
242 		(void) nvlist_add_boolean_value(nvl, INV_IMMUTE, B_TRUE);
243 	}
244 
245 	if ((ptype = xmlattr_to_type(mp, xn, (xmlChar *)Type))
246 	    == TOPO_TYPE_INVALID)
247 		return (-1);
248 
249 	if (nvlist_add_int32(nvl, INV_PVALTYPE, ptype) != 0)
250 		return (-1);
251 
252 	return (xlate_common(mp, xn, ptype, nvl, INV_PVAL));
253 }
254 
255 static int
256 dependent_create(topo_mod_t *mp,
257     tf_info_t *xinfo, tf_pad_t *pad, xmlNodePtr dxn, tnode_t *ptn)
258 {
259 	tf_rdata_t *rp, *pp, *np;
260 	xmlChar *grptype;
261 	int sibs = 0;
262 
263 	topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "dependent_create\n");
264 	if ((grptype = xmlGetProp(dxn, (xmlChar *)Grouping)) == NULL) {
265 		topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
266 		    "Dependents missing grouping attribute");
267 		return (topo_mod_seterrno(mp, ETOPO_PRSR_NOATTR));
268 	}
269 
270 	pp = NULL;
271 	if (xmlStrcmp(grptype, (xmlChar *)Siblings) == 0) {
272 		rp = pad->tpad_sibs;
273 		sibs++;
274 	} else if (xmlStrcmp(grptype, (xmlChar *)Children) == 0) {
275 		rp = pad->tpad_child;
276 	} else {
277 		topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
278 		    "Dependents have bogus grouping attribute");
279 		xmlFree(grptype);
280 		return (topo_mod_seterrno(mp, ETOPO_PRSR_BADGRP));
281 	}
282 	xmlFree(grptype);
283 	/* Add processed dependents to the tail of the list */
284 	while (rp != NULL) {
285 		pp = rp;
286 		rp = rp->rd_next;
287 	}
288 	if ((np = topo_xml_walk(mp, xinfo, dxn, ptn)) == NULL) {
289 		topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
290 		    "error within dependent .xml topology: "
291 		    "%s\n", topo_strerror(topo_mod_errno(mp)));
292 		return (-1);
293 	}
294 	if (pp != NULL)
295 		pp->rd_next = np;
296 	else if (sibs == 1)
297 		pad->tpad_sibs = np;
298 	else
299 		pad->tpad_child = np;
300 	return (0);
301 }
302 
303 static int
304 dependents_create(topo_mod_t *mp,
305     tf_info_t *xinfo, tf_pad_t *pad, xmlNodePtr pxn, tnode_t *ptn)
306 {
307 	xmlNodePtr cn;
308 
309 	topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "dependents_create\n");
310 	for (cn = pxn->xmlChildrenNode; cn != NULL; cn = cn->next) {
311 		if (xmlStrcmp(cn->name, (xmlChar *)Dependents) == 0) {
312 			if (dependent_create(mp, xinfo, pad, cn, ptn) < 0)
313 				return (-1);
314 		}
315 	}
316 	return (0);
317 }
318 
319 static int
320 prop_create(topo_mod_t *mp,
321     nvlist_t *pfmri, tnode_t *ptn, const char *gnm, const char *pnm,
322     topo_type_t ptype, int flag)
323 {
324 	nvlist_t *fmri;
325 	uint32_t ui32;
326 	uint64_t ui64;
327 	int32_t i32;
328 	int64_t i64;
329 	char *str;
330 	int err, e;
331 
332 	topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "prop_create(gnm = %s, "
333 	    "pnm = %s)\n", gnm, pnm);
334 	switch (ptype) {
335 	case TOPO_TYPE_INT32:
336 		e = nvlist_lookup_int32(pfmri, INV_PVAL, &i32);
337 		break;
338 	case TOPO_TYPE_UINT32:
339 		e = nvlist_lookup_uint32(pfmri, INV_PVAL, &ui32);
340 		break;
341 	case TOPO_TYPE_INT64:
342 		e = nvlist_lookup_int64(pfmri, INV_PVAL, &i64);
343 		break;
344 	case TOPO_TYPE_UINT64:
345 		e = nvlist_lookup_uint64(pfmri, INV_PVAL, &ui64);
346 		break;
347 	case TOPO_TYPE_FMRI:
348 		e = nvlist_lookup_nvlist(pfmri, INV_PVAL, &fmri);
349 		break;
350 	case TOPO_TYPE_STRING:
351 		e = nvlist_lookup_string(pfmri, INV_PVAL, &str);
352 		break;
353 	default:
354 		e = ETOPO_PRSR_BADTYPE;
355 	}
356 	if (e != 0) {
357 		topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
358 		    "prop_create: prop value lookup failed.\n");
359 		return (topo_mod_seterrno(mp, e));
360 	}
361 	switch (ptype) {
362 	case TOPO_TYPE_INT32:
363 		e = topo_prop_set_int32(ptn, gnm, pnm, flag, i32, &err);
364 		break;
365 	case TOPO_TYPE_UINT32:
366 		e = topo_prop_set_uint32(ptn, gnm, pnm, flag, ui32, &err);
367 		break;
368 	case TOPO_TYPE_INT64:
369 		e = topo_prop_set_int64(ptn, gnm, pnm, flag, i64, &err);
370 		break;
371 	case TOPO_TYPE_UINT64:
372 		e = topo_prop_set_uint64(ptn, gnm, pnm, flag, ui64, &err);
373 		break;
374 	case TOPO_TYPE_FMRI:
375 		e = topo_prop_set_fmri(ptn, gnm, pnm, flag, fmri, &err);
376 		break;
377 	case TOPO_TYPE_STRING:
378 		e = topo_prop_set_string(ptn, gnm, pnm, flag, str, &err);
379 		break;
380 	}
381 	if (e != 0 && err != ETOPO_PROP_DEFD) {
382 
383 		/*
384 		 * Some properties may have already been set
385 		 * in topo_node_bind() or topo_prop_inherit if we are
386 		 * enumerating from a static .xml file
387 		 */
388 		topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR, "prop set "
389 		    "failed %s/%s:%s\n", gnm, pnm, topo_strerror(err));
390 		return (topo_mod_seterrno(mp, err));
391 	}
392 	return (0);
393 }
394 
395 static int
396 props_create(topo_mod_t *mp,
397     tnode_t *ptn, const char *gnm, nvlist_t **props, int nprops)
398 {
399 	topo_type_t ptype;
400 	boolean_t pim;
401 	char *pnm;
402 	int32_t i32;
403 	int flag;
404 	int pn;
405 	int e;
406 
407 	topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "props_create(gnm = %s)\n", gnm);
408 	for (pn = 0; pn < nprops; pn++) {
409 		e = nvlist_lookup_string(props[pn], INV_PNAME, &pnm);
410 		if (e != 0) {
411 			topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
412 			    "props create lookup (%s) failure: %s",
413 			    INV_PNAME, strerror(e));
414 			return (topo_mod_seterrno(mp, ETOPO_PRSR_NVPROP));
415 		}
416 		e = nvlist_lookup_boolean_value(props[pn], INV_IMMUTE, &pim);
417 		if (e != 0) {
418 			topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
419 			    "props create lookup (%s) failure: %s",
420 			    INV_IMMUTE, strerror(e));
421 			return (topo_mod_seterrno(mp, ETOPO_PRSR_NVPROP));
422 		}
423 		flag = (pim == B_TRUE) ?
424 		    TOPO_PROP_IMMUTABLE : TOPO_PROP_MUTABLE;
425 
426 		e = nvlist_lookup_int32(props[pn], INV_PVALTYPE, &i32);
427 		if (e != 0) {
428 			topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
429 			    "props create lookup (%s) failure: %s",
430 			    INV_PVALTYPE, strerror(e));
431 			return (topo_mod_seterrno(mp, ETOPO_PRSR_NVPROP));
432 		}
433 		ptype = (topo_type_t)i32;
434 		if (prop_create(mp, props[pn], ptn, gnm, pnm, ptype, flag) < 0)
435 			return (-1);
436 	}
437 	return (0);
438 }
439 
440 static int
441 pgroups_create(topo_mod_t *mp, tf_pad_t *pad, tnode_t *ptn)
442 {
443 	topo_pgroup_info_t pgi;
444 	nvlist_t **props;
445 	char *gnm;
446 	char *nmstab, *dstab;
447 	uint32_t rnprops, nprops;
448 	uint32_t gv;
449 	int pg;
450 	int e;
451 
452 	topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "pgroups_create\n");
453 	for (pg = 0; pg < pad->tpad_pgcnt; pg++) {
454 		e = nvlist_lookup_string(pad->tpad_pgs[pg],
455 		    INV_PGRP_NAME, &gnm);
456 		if (e != 0) {
457 			topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
458 			    "pad lookup (%s) failed (%s).\n",
459 			    INV_PGRP_NAME, strerror(errno));
460 			return (topo_mod_seterrno(mp, ETOPO_PRSR_NVPROP));
461 		}
462 		e = nvlist_lookup_string(pad->tpad_pgs[pg],
463 		    INV_PGRP_NMSTAB, &nmstab);
464 		if (e != 0) {
465 			if (e != ENOENT) {
466 				topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
467 				    "pad lookup (%s) "
468 				    "failed.\n", INV_PGRP_NMSTAB);
469 				return (topo_mod_seterrno(mp,
470 				    ETOPO_PRSR_NVPROP));
471 			} else {
472 				nmstab = TOPO_STABSTR_PRIVATE;
473 			}
474 		}
475 		e = nvlist_lookup_string(pad->tpad_pgs[pg],
476 		    INV_PGRP_DSTAB, &dstab);
477 		if (e != 0) {
478 			if (e != ENOENT) {
479 				topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
480 				    "pad lookup (%s) failed.\n",
481 				    INV_PGRP_DSTAB);
482 				return (topo_mod_seterrno(mp,
483 				    ETOPO_PRSR_NVPROP));
484 			} else {
485 				dstab = TOPO_STABSTR_PRIVATE;
486 			}
487 		}
488 		e = nvlist_lookup_uint32(pad->tpad_pgs[pg],
489 		    INV_PGRP_VER, &gv);
490 		if (e != 0) {
491 			topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
492 			    "pad lookup (%s) failed.\n",
493 			    INV_PGRP_VER);
494 			return (topo_mod_seterrno(mp, ETOPO_PRSR_NVPROP));
495 		}
496 		pgi.tpi_name = gnm;
497 		pgi.tpi_namestab = topo_name2stability(nmstab);
498 		pgi.tpi_datastab = topo_name2stability(dstab);
499 		pgi.tpi_version = gv;
500 		if (topo_pgroup_create(ptn, &pgi, &e) != 0) {
501 			if (e != ETOPO_PROP_DEFD) {
502 				topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
503 				    "pgroups create failure: %s\n",
504 				    topo_strerror(e));
505 				return (-1);
506 			}
507 		}
508 		e = nvlist_lookup_uint32(pad->tpad_pgs[pg],
509 		    INV_PGRP_NPROP, &rnprops);
510 		/*
511 		 * The number of properties could be zero if the property
512 		 * group only contains propmethod declarations
513 		 */
514 		if (rnprops > 0) {
515 			e |= nvlist_lookup_nvlist_array(pad->tpad_pgs[pg],
516 			    INV_PGRP_ALLPROPS, &props, &nprops);
517 			if (rnprops != nprops) {
518 				topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
519 				    "recorded number of props %d does not "
520 				    "match number of props recorded %d.\n",
521 				    rnprops, nprops);
522 			}
523 			if (props_create(mp, ptn, gnm, props, nprops) < 0)
524 				return (-1);
525 		}
526 	}
527 	return (0);
528 }
529 
530 static nvlist_t *
531 pval_record(topo_mod_t *mp, xmlNodePtr xn)
532 {
533 	nvlist_t *pnvl = NULL;
534 	xmlChar *pname;
535 
536 	topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "pval_record\n");
537 	if ((pname = xmlGetProp(xn, (xmlChar *)Name)) == NULL) {
538 		topo_dprintf(mp->tm_hdl, TOPO_DBG_XML,
539 		    "propval lacks a name\n");
540 		(void) topo_mod_seterrno(mp, ETOPO_PRSR_NOATTR);
541 		return (NULL);
542 	}
543 	if (topo_mod_nvalloc(mp, &pnvl, NV_UNIQUE_NAME) < 0) {
544 		xmlFree(pname);
545 		return (NULL);
546 	}
547 	if (nvlist_add_string(pnvl, INV_PNAME, (char *)pname) < 0) {
548 		xmlFree(pname);
549 		nvlist_free(pnvl);
550 		return (NULL);
551 	}
552 	xmlFree(pname);
553 	/* FMXXX stability of the property name */
554 
555 	if (xmlprop_xlate(mp, xn, pnvl) < 0) {
556 		nvlist_free(pnvl);
557 		return (NULL);
558 	}
559 	return (pnvl);
560 }
561 
562 
563 struct propmeth_data {
564 	const char *pg_name;
565 	const char *prop_name;
566 	topo_type_t prop_type;
567 	const char *meth_name;
568 	topo_version_t meth_ver;
569 	nvlist_t *arg_nvl;
570 };
571 
572 static int
573 register_method(topo_mod_t *mp, tnode_t *ptn, struct propmeth_data *meth)
574 {
575 	int err;
576 
577 	if (topo_prop_method_version_register(ptn, meth->pg_name,
578 	    meth->prop_name, meth->prop_type, meth->meth_name, meth->meth_ver,
579 	    meth->arg_nvl, &err) != 0) {
580 
581 		topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR, "failed to register "
582 		    "propmethod %s for property \"%s\" in propgrp %s on node "
583 		    "%s=%d (%s)\n",
584 		    meth->meth_name, meth->prop_name, meth->pg_name,
585 		    topo_node_name(ptn), topo_node_instance(ptn),
586 		    topo_strerror(err));
587 		return (-1);
588 	}
589 	topo_dprintf(mp->tm_hdl, TOPO_DBG_XML,
590 	    "registered method %s on %s=%d\n",
591 	    meth->meth_name, topo_node_name(ptn), topo_node_instance(ptn));
592 
593 	return (0);
594 }
595 
596 static int
597 pmeth_record(topo_mod_t *mp, const char *pg_name, xmlNodePtr xn, tnode_t *tn,
598     const char *rname, const char *ppgrp_name)
599 {
600 	nvlist_t *arg_nvl = NULL;
601 	xmlNodePtr cn;
602 	xmlChar *meth_name = NULL, *prop_name = NULL;
603 	xmlChar *arg_name = NULL;
604 	uint64_t meth_ver;
605 	topo_type_t prop_type;
606 	struct propmeth_data meth;
607 	int ret = 0;
608 	topo_type_t ptype;
609 	tnode_t *tmp;
610 
611 	topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "pmeth_record\n");
612 
613 	/*
614 	 * Get propmethod attribute values
615 	 */
616 	if ((meth_name = xmlGetProp(xn, (xmlChar *)Name)) == NULL) {
617 		topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
618 		    "propmethod element lacks a name attribute\n");
619 		return (topo_mod_seterrno(mp, ETOPO_PRSR_NOATTR));
620 	}
621 	if (xmlattr_to_int(mp, xn, Version, &meth_ver) < 0) {
622 		topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
623 		    "propmethod element lacks version attribute\n");
624 		ret = topo_mod_seterrno(mp, ETOPO_PRSR_NOATTR);
625 		goto pmr_done;
626 	}
627 	if ((prop_name = xmlGetProp(xn, (xmlChar *)Propname)) == NULL) {
628 		topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
629 		    "propmethod element lacks propname attribute\n");
630 		ret = topo_mod_seterrno(mp, ETOPO_PRSR_NOATTR);
631 		goto pmr_done;
632 	}
633 	if ((prop_type = xmlattr_to_type(mp, xn, (xmlChar *)Proptype))
634 	    == TOPO_TYPE_INVALID) {
635 		topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
636 		    "error decoding proptype attribute\n");
637 		ret = topo_mod_seterrno(mp, ETOPO_PRSR_NOATTR);
638 		goto pmr_done;
639 	}
640 
641 	/*
642 	 * Allocate method argument nvlist
643 	 */
644 	if (topo_mod_nvalloc(mp, &arg_nvl, NV_UNIQUE_NAME) < 0) {
645 		ret = topo_mod_seterrno(mp, ETOPO_NOMEM);
646 		goto pmr_done;
647 	}
648 
649 	/*
650 	 * Iterate through the argval nodes and build the argval nvlist
651 	 */
652 	for (cn = xn->xmlChildrenNode; cn != NULL; cn = cn->next) {
653 		if (xmlStrcmp(cn->name, (xmlChar *)Argval) == 0) {
654 			topo_dprintf(mp->tm_hdl, TOPO_DBG_XML,
655 			    "found argval element\n");
656 			if ((arg_name = xmlGetProp(cn, (xmlChar *)Name))
657 			    == NULL) {
658 				topo_dprintf(mp->tm_hdl, TOPO_DBG_XML,
659 				    "argval element lacks a name attribute\n");
660 				ret = topo_mod_seterrno(mp, ETOPO_PRSR_NOATTR);
661 				goto pmr_done;
662 			}
663 			if ((ptype = xmlattr_to_type(mp, cn, (xmlChar *)Type))
664 			    == TOPO_TYPE_INVALID) {
665 				ret = topo_mod_seterrno(mp, ETOPO_PRSR_BADTYPE);
666 				xmlFree(arg_name);
667 				break;
668 			}
669 			if (xlate_common(mp, cn, ptype, arg_nvl,
670 			    (const char *)arg_name) != 0) {
671 				ret = topo_mod_seterrno(mp, ETOPO_PRSR_BADTYPE);
672 				xmlFree(arg_name);
673 				break;
674 			}
675 		}
676 		if (arg_name) {
677 			xmlFree(arg_name);
678 			arg_name = NULL;
679 		}
680 	}
681 
682 	if (ret != 0)
683 		goto pmr_done;
684 
685 	/*
686 	 * Register the prop method for all of the nodes in our range
687 	 */
688 	meth.pg_name = (const char *)pg_name;
689 	meth.prop_name = (const char *)prop_name;
690 	meth.prop_type = prop_type;
691 	meth.meth_name = (const char *)meth_name;
692 	meth.meth_ver = meth_ver;
693 	meth.arg_nvl = arg_nvl;
694 
695 	/*
696 	 * If the propgroup element is under a range element, we'll apply
697 	 * the method to all of the topo nodes at this level with the same
698 	 * range name.
699 	 *
700 	 * Otherwise, if the propgroup element is under a node element
701 	 * then we'll simply register the method for this node.
702 	 */
703 	if (strcmp(ppgrp_name, Range) == 0) {
704 		for (tmp = tn; tmp != NULL; tmp = topo_child_next(NULL, tmp)) {
705 			if (strcmp(rname, topo_node_name(tmp)) == 0)
706 				if (register_method(mp, tmp, &meth) != 0) {
707 					ret = topo_mod_seterrno(mp,
708 					    ETOPO_PRSR_REGMETH);
709 					goto pmr_done;
710 				}
711 		}
712 	} else {
713 		if (register_method(mp, tn, &meth) != 0) {
714 			ret = topo_mod_seterrno(mp, ETOPO_PRSR_REGMETH);
715 			goto pmr_done;
716 		}
717 	}
718 
719 pmr_done:
720 	if (meth_name)
721 		xmlFree(meth_name);
722 	if (prop_name)
723 		xmlFree(prop_name);
724 	if (arg_nvl)
725 		nvlist_free(arg_nvl);
726 	return (ret);
727 }
728 
729 
730 static int
731 pgroup_record(topo_mod_t *mp, xmlNodePtr pxn, tnode_t *tn, const char *rname,
732     tf_pad_t *rpad, int pi, const char *ppgrp_name)
733 {
734 	topo_stability_t nmstab, dstab;
735 	uint64_t ver;
736 	xmlNodePtr cn;
737 	xmlChar *name;
738 	nvlist_t **apl = NULL;
739 	nvlist_t *pgnvl = NULL;
740 	int pcnt = 0;
741 	int ai = 0;
742 	int e;
743 
744 	topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "pgroup_record\n");
745 	if ((name = xmlGetProp(pxn, (xmlChar *)Name)) == NULL) {
746 		topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
747 		    "propgroup lacks a name\n");
748 		return (topo_mod_seterrno(mp, ETOPO_PRSR_NOATTR));
749 	}
750 	if (xmlattr_to_int(mp, pxn, Version, &ver) < 0) {
751 		topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
752 		    "propgroup lacks a version\n");
753 		return (topo_mod_seterrno(mp, ETOPO_PRSR_NOATTR));
754 	}
755 	if (xmlattr_to_stab(mp, pxn, Namestab, &nmstab) < 0) {
756 		topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
757 		    "propgroup lacks name-stability\n");
758 		return (topo_mod_seterrno(mp, ETOPO_PRSR_NOATTR));
759 	}
760 	if (xmlattr_to_stab(mp, pxn, Datastab, &dstab) < 0) {
761 		topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
762 		    "propgroup lacks data-stability\n");
763 		return (topo_mod_seterrno(mp, ETOPO_PRSR_NOATTR));
764 	}
765 
766 	topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "pgroup %s\n", (char *)name);
767 	for (cn = pxn->xmlChildrenNode; cn != NULL; cn = cn->next) {
768 		if (xmlStrcmp(cn->name, (xmlChar *)Propval) == 0)
769 			pcnt++;
770 	}
771 
772 	if (topo_mod_nvalloc(mp, &pgnvl, NV_UNIQUE_NAME) < 0) {
773 		xmlFree(name);
774 		topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
775 		    "failed to allocate propgroup nvlist\n");
776 		return (topo_mod_seterrno(mp, ETOPO_NOMEM));
777 	}
778 
779 	e = nvlist_add_string(pgnvl, INV_PGRP_NAME, (char *)name);
780 	e |= nvlist_add_uint32(pgnvl, INV_PGRP_NMSTAB, nmstab);
781 	e |= nvlist_add_uint32(pgnvl, INV_PGRP_DSTAB, dstab);
782 	e |= nvlist_add_uint32(pgnvl, INV_PGRP_VER, ver);
783 	e |= nvlist_add_uint32(pgnvl, INV_PGRP_NPROP, pcnt);
784 	if (pcnt > 0)
785 		if (e != 0 ||
786 		    (apl = topo_mod_zalloc(mp, pcnt * sizeof (nvlist_t *)))
787 		    == NULL) {
788 			xmlFree(name);
789 			nvlist_free(pgnvl);
790 			topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
791 			    "failed to allocate nvlist array for properties"
792 			    "(e=%d)\n", e);
793 			return (topo_mod_seterrno(mp, ETOPO_NOMEM));
794 		}
795 	for (cn = pxn->xmlChildrenNode; cn != NULL; cn = cn->next) {
796 		if (xmlStrcmp(cn->name, (xmlChar *)Propval) == 0) {
797 			if (ai < pcnt) {
798 				if ((apl[ai] = pval_record(mp, cn)) == NULL)
799 					break;
800 			}
801 			ai++;
802 		} else if (xmlStrcmp(cn->name, (xmlChar *)Prop_meth) == 0) {
803 			if (pmeth_record(mp, (const char *)name, cn, tn, rname,
804 			    ppgrp_name) < 0)
805 				break;
806 		}
807 	}
808 	xmlFree(name);
809 	if (pcnt > 0) {
810 		e |= (ai != pcnt);
811 		e |= nvlist_add_nvlist_array(pgnvl, INV_PGRP_ALLPROPS, apl,
812 		    pcnt);
813 		for (ai = 0; ai < pcnt; ai++)
814 			if (apl[ai] != NULL)
815 				nvlist_free(apl[ai]);
816 		topo_mod_free(mp, apl, pcnt * sizeof (nvlist_t *));
817 		if (e != 0) {
818 			nvlist_free(pgnvl);
819 			return (-1);
820 		}
821 	}
822 	rpad->tpad_pgs[pi] = pgnvl;
823 	return (0);
824 }
825 
826 static int
827 pgroups_record(topo_mod_t *mp, xmlNodePtr pxn, tnode_t *tn, const char *rname,
828     tf_pad_t *rpad, const char *ppgrp)
829 {
830 	xmlNodePtr cn;
831 	int pi = 0;
832 
833 	topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "pgroups_record: pxn->name=%s\n",
834 	    pxn->name);
835 	for (cn = pxn->xmlChildrenNode; cn != NULL; cn = cn->next) {
836 		if (xmlStrcmp(cn->name, (xmlChar *)Propgrp) == 0) {
837 			if (pgroup_record(mp, cn, tn, rname, rpad, pi++, ppgrp)
838 			    < 0)
839 				return (-1);
840 		}
841 	}
842 	return (0);
843 }
844 
845 /*
846  * psn:	pointer to a "set" XML node
847  * key: string to search the set for
848  *
849  * returns: 1, if the set contains key
850  *          0, otherwise
851  */
852 static int
853 set_contains(topo_mod_t *mp, char *key, char *set)
854 {
855 	char *prod;
856 	int rv = 0;
857 
858 	topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "set_contains(key = %s, "
859 	    "setlist = %s)\n", key, set);
860 
861 	prod = strtok((char *)set, "|");
862 	if (prod && (strcmp(key, prod) == 0))
863 		return (1);
864 
865 	while ((prod = strtok(NULL, "|")))
866 		if (strcmp(key, prod) == 0)
867 			return (1);
868 
869 	return (rv);
870 }
871 
872 
873 /*
874  * Process the property group and dependents xmlNode children of
875  * parent xmlNode pxn.
876  */
877 static int
878 pad_process(topo_mod_t *mp, tf_rdata_t *rd, xmlNodePtr pxn, tnode_t *ptn,
879     tf_pad_t **rpad)
880 {
881 	xmlNodePtr cn, gcn, psn, ecn;
882 	xmlNodePtr def_set = NULL;
883 	tf_pad_t *new = *rpad;
884 	tf_rdata_t tmp_rd;
885 	int pgcnt = 0;
886 	int dcnt = 0;
887 	int ecnt = 0;
888 	int joined_set = 0;
889 	xmlChar *set;
890 	char *key;
891 
892 	topo_dprintf(mp->tm_hdl, TOPO_DBG_XML,
893 	    "pad_process beneath %s\n", topo_node_name(ptn));
894 	if (new == NULL) {
895 		for (cn = pxn->xmlChildrenNode; cn != NULL; cn = cn->next) {
896 			topo_dprintf(mp->tm_hdl, TOPO_DBG_XML,
897 			    "cn->name is %s \n", (char *)cn->name);
898 			/*
899 			 * We're iterating through the XML children looking for
900 			 * four types of elements:
901 			 *   1) dependents elements
902 			 *   2) unconstrained pgroup elements
903 			 *   3) pgroup elements constrained by set elements
904 			 *   4) enum-method elements for the case that we want
905 			 *	to post-process a statically defined node
906 			 */
907 			if (xmlStrcmp(cn->name, (xmlChar *)Dependents) == 0)
908 				dcnt++;
909 			else if (xmlStrcmp(cn->name, (xmlChar *)Propgrp) == 0)
910 				pgcnt++;
911 			else if (xmlStrcmp(cn->name, (xmlChar *)Enum_meth)
912 			    == 0) {
913 				ecn = cn;
914 				ecnt++;
915 			} else if (xmlStrcmp(cn->name, (xmlChar *)Set) == 0) {
916 				if (joined_set)
917 					continue;
918 				set = xmlGetProp(cn, (xmlChar *)Setlist);
919 
920 				if (mp->tm_hdl->th_product)
921 					key = mp->tm_hdl->th_product;
922 				else
923 					key = mp->tm_hdl->th_platform;
924 
925 				/*
926 				 * If it's the default set then we'll store
927 				 * a pointer to it so that if none of the other
928 				 * sets apply to our product we can fall
929 				 * back to this one.
930 				 */
931 				if (strcmp((char *)set, "default") == 0)
932 					def_set = cn;
933 				else if (set_contains(mp, key, (char *)set)) {
934 					psn = cn;
935 					joined_set = 1;
936 					for (gcn = cn->xmlChildrenNode;
937 					    gcn != NULL; gcn = gcn->next) {
938 						if (xmlStrcmp(gcn->name,
939 						    (xmlChar *)Propgrp) == 0)
940 							pgcnt++;
941 					}
942 				}
943 				xmlFree(set);
944 			}
945 		}
946 		/*
947 		 * If we haven't found a set that contains our product AND
948 		 * a default set exists, then we'll process it.
949 		 */
950 		if (!joined_set && def_set) {
951 			topo_dprintf(mp->tm_hdl, TOPO_DBG_XML,
952 			    "Falling back to default set\n");
953 			joined_set = 1;
954 			psn = def_set;
955 			for (gcn = psn->xmlChildrenNode; gcn != NULL;
956 			    gcn = gcn->next) {
957 				if (xmlStrcmp(gcn->name, (xmlChar *)Propgrp)
958 				    == 0)
959 					pgcnt++;
960 			}
961 		}
962 		topo_dprintf(mp->tm_hdl, TOPO_DBG_XML,
963 		    "pad_process: dcnt=%d, pgcnt=%d, ecnt=%d, joined_set=%d\n",
964 		    dcnt, pgcnt, ecnt, joined_set);
965 		/*
966 		 * If an enum-method element was found, AND we're a child of a
967 		 * node element, then we invoke the enumerator so that it can do
968 		 * post-processing of the node.
969 		 */
970 		if (ecnt && (strcmp((const char *)pxn->name, Node) == 0)) {
971 			if ((tmp_rd.rd_einfo = enum_attributes_process(mp, ecn))
972 			    == NULL)
973 				return (-1);
974 			tmp_rd.rd_mod = mp;
975 			tmp_rd.rd_name = rd->rd_name;
976 			tmp_rd.rd_min = rd->rd_min;
977 			tmp_rd.rd_max = rd->rd_max;
978 			tmp_rd.rd_pn = ptn;
979 			if (enum_run(mp, &tmp_rd) < 0) {
980 				/*
981 				 * Note the failure but continue on
982 				 */
983 				topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
984 				    "pad_process: enumeration failed.\n");
985 			}
986 			topo_mod_free(mp, tmp_rd.rd_einfo, sizeof (tf_edata_t));
987 		}
988 		/*
989 		 * Here we allocate an element in an intermediate data structure
990 		 * which keeps track property groups and dependents of the range
991 		 * currently being processed.
992 		 *
993 		 * This structure is referenced in pgroups_record() to create
994 		 * the actual property groups in the topo tree
995 		 */
996 		if ((new = tf_pad_new(mp, pgcnt, dcnt)) == NULL)
997 			return (-1);
998 		if (dcnt == 0 && pgcnt == 0) {
999 			*rpad = new;
1000 			return (0);
1001 		}
1002 
1003 		if (pgcnt > 0) {
1004 			new->tpad_pgs =
1005 			    topo_mod_zalloc(mp, pgcnt * sizeof (nvlist_t *));
1006 			if (new->tpad_pgs == NULL) {
1007 				tf_pad_free(mp, new);
1008 				return (-1);
1009 			}
1010 
1011 			if (joined_set) {
1012 				/*
1013 				 * If the property groups are contained within a
1014 				 * set then they will be one level lower in
1015 				 * the XML tree.
1016 				 */
1017 				if (pgroups_record(mp, psn, ptn, rd->rd_name,
1018 				    new, (const char *)pxn->name) < 0) {
1019 					tf_pad_free(mp, new);
1020 					return (-1);
1021 				}
1022 			} else {
1023 				if (pgroups_record(mp, pxn, ptn, rd->rd_name,
1024 				    new, (const char *)pxn->name) < 0) {
1025 					tf_pad_free(mp, new);
1026 					return (-1);
1027 				}
1028 			}
1029 		}
1030 		*rpad = new;
1031 	}
1032 
1033 	if (new->tpad_dcnt > 0)
1034 		if (dependents_create(mp, rd->rd_finfo, new, pxn, ptn) < 0)
1035 			return (-1);
1036 
1037 	if (new->tpad_pgcnt > 0)
1038 		if (pgroups_create(mp, new, ptn) < 0)
1039 			return (-1);
1040 
1041 	return (0);
1042 }
1043 
1044 
1045 static int
1046 node_process(topo_mod_t *mp, xmlNodePtr nn, tf_rdata_t *rd)
1047 {
1048 	xmlChar *str;
1049 	topo_instance_t inst;
1050 	tf_idata_t *newi;
1051 	tnode_t *ntn;
1052 	uint64_t ui;
1053 	int rv = -1;
1054 	int s = 0;
1055 
1056 	topo_dprintf(mp->tm_hdl, TOPO_DBG_XML,
1057 	    "node_process %s\n", rd->rd_name);
1058 
1059 	if (xmlattr_to_int(mp, nn, Instance, &ui) < 0)
1060 		goto nodedone;
1061 	inst = (topo_instance_t)ui;
1062 
1063 	if ((str = xmlGetProp(nn, (xmlChar *)Static)) != NULL) {
1064 		if (xmlStrcmp(str, (xmlChar *)True) == 0)
1065 			s = 1;
1066 		xmlFree(str);
1067 	}
1068 
1069 	if (s == 0) {
1070 		if (topo_mod_enumerate(rd->rd_mod, rd->rd_pn,
1071 		    rd->rd_finfo->tf_scheme, rd->rd_name, inst, inst,
1072 		    s == 1 ? &s : NULL) < 0)
1073 			goto nodedone;
1074 	}
1075 	ntn = topo_node_lookup(rd->rd_pn, rd->rd_name, inst);
1076 
1077 	if (ntn == NULL) {
1078 
1079 		/*
1080 		 * If this is a static node declaration, we can
1081 		 * ignore the lookup failure and continue
1082 		 * processing.  Otherwise, something
1083 		 * went wrong during enumeration
1084 		 */
1085 		if (s == 1)
1086 			rv = 0;
1087 		goto nodedone;
1088 	}
1089 	if ((newi = tf_idata_new(mp, inst, ntn)) == NULL) {
1090 		topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
1091 		    "node_process: tf_idata_new failed.\n");
1092 		goto nodedone;
1093 	}
1094 	if (tf_idata_insert(&rd->rd_instances, newi) < 0) {
1095 		topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
1096 		    "node_process: tf_idata_insert failed.\n");
1097 		goto nodedone;
1098 	}
1099 	if (pad_process(mp, rd, nn, ntn, &newi->ti_pad) < 0)
1100 		goto nodedone;
1101 	rv = 0;
1102 nodedone:
1103 	topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "done with node %s.\n",
1104 	    rd->rd_name);
1105 	return (rv);
1106 }
1107 
1108 static tf_edata_t *
1109 enum_attributes_process(topo_mod_t *mp, xmlNodePtr en)
1110 {
1111 	tf_edata_t *einfo;
1112 	uint64_t ui;
1113 
1114 	topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "enum_attributes_process\n");
1115 	if ((einfo = topo_mod_zalloc(mp, sizeof (tf_edata_t))) == NULL) {
1116 		(void) topo_mod_seterrno(mp, ETOPO_NOMEM);
1117 		return (NULL);
1118 	}
1119 	einfo->te_name = (char *)xmlGetProp(en, (xmlChar *)Name);
1120 	if (einfo->te_name == NULL) {
1121 		topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
1122 		    "Enumerator name attribute missing.\n");
1123 		(void) topo_mod_seterrno(mp, ETOPO_PRSR_NOATTR);
1124 		goto enodedone;
1125 	}
1126 
1127 	/*
1128 	 * Check for recursive enumeration
1129 	 */
1130 	if (strcmp(einfo->te_name, mp->tm_name) == 0) {
1131 		topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
1132 		    "Recursive enumeration detected for %s\n",
1133 		    einfo->te_name);
1134 		(void) topo_mod_seterrno(mp, ETOPO_ENUM_RECURS);
1135 		goto enodedone;
1136 	}
1137 	if (xmlattr_to_int(mp, en, Version, &ui) < 0)
1138 		goto enodedone;
1139 	einfo->te_vers = (int)ui;
1140 
1141 	return (einfo);
1142 
1143 enodedone:
1144 	if (einfo->te_name != NULL)
1145 		xmlFree(einfo->te_name);
1146 	topo_mod_free(mp, einfo, sizeof (tf_edata_t));
1147 	return (NULL);
1148 }
1149 
1150 static int
1151 enum_run(topo_mod_t *mp, tf_rdata_t *rd)
1152 {
1153 	topo_hdl_t *thp = mp->tm_hdl;
1154 	int e = -1;
1155 
1156 	topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "enum_run\n");
1157 	/*
1158 	 * Check if the enumerator module is already loaded.
1159 	 * Module loading is single-threaded at this point so there's
1160 	 * no need to worry about the module going away or bumping the
1161 	 * ref count.
1162 	 */
1163 	if ((rd->rd_mod = topo_mod_lookup(thp, rd->rd_einfo->te_name,
1164 	    0)) == NULL) {
1165 		if ((rd->rd_mod = topo_mod_load(mp, rd->rd_einfo->te_name,
1166 		    rd->rd_einfo->te_vers)) == NULL) {
1167 			topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
1168 			    "enum_run: mod_load of %s failed: %s.\n",
1169 			    rd->rd_einfo->te_name,
1170 			    topo_strerror(topo_mod_errno(mp)));
1171 			(void) topo_hdl_seterrno(thp, topo_mod_errno(mp));
1172 			return (e);
1173 		}
1174 	}
1175 	/*
1176 	 * We're live, so let's enumerate.
1177 	 */
1178 	topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "enumerate request. (%s)\n",
1179 	    rd->rd_einfo->te_name);
1180 	e = topo_mod_enumerate(rd->rd_mod, rd->rd_pn, rd->rd_einfo->te_name,
1181 	    rd->rd_name, rd->rd_min, rd->rd_max, NULL);
1182 	topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "back from enumeration. %d\n",
1183 	    e);
1184 	if (e != 0) {
1185 		topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
1186 		    "Enumeration failed (%s)\n",
1187 		    topo_strerror(topo_mod_errno(mp)));
1188 		(void) topo_hdl_seterrno(thp, EMOD_PARTIAL_ENUM);
1189 		return (topo_mod_seterrno(mp, EMOD_PARTIAL_ENUM));
1190 	}
1191 	return (e);
1192 }
1193 
1194 
1195 int
1196 decorate_nodes(topo_mod_t *mp, tf_rdata_t *rd, xmlNodePtr pxn, tnode_t *ptn,
1197     tf_pad_t **rpad)
1198 {
1199 	tnode_t *ctn;
1200 
1201 	ctn = topo_child_first(ptn);
1202 	while (ctn != NULL) {
1203 		/* Only care about instances within the range */
1204 		if (strcmp(topo_node_name(ctn), rd->rd_name) != 0) {
1205 			ctn = topo_child_next(ptn, ctn);
1206 			continue;
1207 		}
1208 		if (pad_process(mp, rd, pxn, ctn, rpad) < 0)
1209 			return (-1);
1210 		if (decorate_nodes(mp, rd, pxn, ctn, rpad) < 0)
1211 			return (-1);
1212 		ctn = topo_child_next(ptn, ctn);
1213 	}
1214 	return (0);
1215 }
1216 
1217 int
1218 topo_xml_range_process(topo_mod_t *mp, xmlNodePtr rn, tf_rdata_t *rd)
1219 {
1220 	/*
1221 	 * The range may have several children xmlNodes, that may
1222 	 * represent the enumeration method, property groups,
1223 	 * dependents, nodes or services.
1224 	 */
1225 	xmlNodePtr cn, enum_node = NULL, pmap_node = NULL;
1226 	xmlChar *pmap_name;
1227 	tnode_t *ct;
1228 	int e, ccnt = 0;
1229 
1230 	topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "topo_xml_range_process\n"
1231 	    "process %s range beneath %s\n", rd->rd_name,
1232 	    topo_node_name(rd->rd_pn));
1233 
1234 	e = topo_node_range_create(mp,
1235 	    rd->rd_pn, rd->rd_name, rd->rd_min, rd->rd_max);
1236 	if (e != 0 && topo_mod_errno(mp) != ETOPO_NODE_DUP) {
1237 		topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
1238 		    "Range create failed due to %s.\n",
1239 		    topo_strerror(topo_mod_errno(mp)));
1240 		return (-1);
1241 	}
1242 
1243 	/*
1244 	 * Before we process any of the other child xmlNodes, we iterate through
1245 	 * the children and looking for either enum-method or propmap elements.
1246 	 */
1247 	for (cn = rn->xmlChildrenNode; cn != NULL; cn = cn->next)
1248 		if (xmlStrcmp(cn->name, (xmlChar *)Enum_meth) == 0)
1249 			enum_node = cn;
1250 		else if (xmlStrcmp(cn->name, (xmlChar *)Propmap) == 0)
1251 			pmap_node = cn;
1252 
1253 	/*
1254 	 * If we found an enum-method element, process it first
1255 	 */
1256 	if (enum_node != NULL) {
1257 		if ((rd->rd_einfo = enum_attributes_process(mp, enum_node))
1258 		    == NULL)
1259 			return (-1);
1260 		if (enum_run(mp, rd) < 0) {
1261 			/*
1262 			 * Note the failure but continue on
1263 			 */
1264 			topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
1265 			    "Enumeration failed.\n");
1266 		}
1267 	}
1268 
1269 	/*
1270 	 * Next, check if a propmap element was found and if so, load it in
1271 	 * and parse it.
1272 	 */
1273 	if (pmap_node != NULL) {
1274 		topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "found a propmap "
1275 		    "element\n");
1276 		if ((pmap_name = xmlGetProp(pmap_node, (xmlChar *)Name))
1277 		    == NULL) {
1278 			topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
1279 			    "propmap element missing name attribute.\n");
1280 		} else {
1281 			if (topo_file_load(mp, rd->rd_pn,
1282 			    (const char *)pmap_name,
1283 			    rd->rd_finfo->tf_scheme, 1) < 0) {
1284 
1285 				topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
1286 				    "topo_xml_range_process: topo_file_load"
1287 				    "failed: %s.\n",
1288 				    topo_strerror(topo_mod_errno(mp)));
1289 			}
1290 			xmlFree(pmap_name);
1291 		}
1292 	}
1293 
1294 	/* Now look for nodes, i.e., hard instances */
1295 	for (cn = rn->xmlChildrenNode; cn != NULL; cn = cn->next) {
1296 		if (xmlStrcmp(cn->name, (xmlChar *)Node) == 0) {
1297 			if (node_process(mp, cn, rd) < 0) {
1298 				topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
1299 				    "node processing failed: %s.\n",
1300 				    topo_strerror(topo_mod_errno(mp)));
1301 				return (topo_mod_seterrno(mp,
1302 				    EMOD_PARTIAL_ENUM));
1303 			}
1304 			ccnt++;
1305 		}
1306 	}
1307 
1308 	/*
1309 	 * Finally, process the property groups and dependents
1310 	 *
1311 	 * If the TF_PROPMAP flag is set for the XML file we're currently
1312 	 * processing, then this XML file was loaded via propmap.  In that case
1313 	 * we call a special routine to recursively apply the propgroup settings
1314 	 * to all of nodes in this range
1315 	 */
1316 	if (rd->rd_finfo->tf_flags & TF_PROPMAP)
1317 		(void) decorate_nodes(mp, rd, rn, rd->rd_pn, &rd->rd_pad);
1318 	else {
1319 		ct = topo_child_first(rd->rd_pn);
1320 		while (ct != NULL) {
1321 			/* Only care about instances within the range */
1322 			if (strcmp(topo_node_name(ct), rd->rd_name) != 0) {
1323 				ct = topo_child_next(rd->rd_pn, ct);
1324 				continue;
1325 			}
1326 			if (pad_process(mp, rd, rn, ct, &rd->rd_pad)
1327 			    < 0)
1328 				return (-1);
1329 			ct = topo_child_next(rd->rd_pn, ct);
1330 			ccnt++;
1331 		}
1332 		if (ccnt == 0) {
1333 			topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "no nodes "
1334 			    "processed for range %s\n", rd->rd_name);
1335 			topo_node_range_destroy(rd->rd_pn, rd->rd_name);
1336 			return (-1);
1337 		}
1338 	}
1339 
1340 	topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "topo_xml_range_process: end "
1341 	    "range process %s\n", rd->rd_name);
1342 
1343 	return (0);
1344 }
1345 
1346 static tf_rdata_t *
1347 topo_xml_walk(topo_mod_t *mp,
1348     tf_info_t *xinfo, xmlNodePtr croot, tnode_t *troot)
1349 {
1350 	xmlNodePtr curr, def_set = NULL;
1351 	tf_rdata_t *rr, *pr, *rdp;
1352 	xmlChar *set;
1353 	char *key;
1354 	int joined_set = 0;
1355 
1356 	topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "topo_xml_walk\n");
1357 	rr = pr = NULL;
1358 	/*
1359 	 * First iterate through all the XML nodes at this level to look for
1360 	 * set nodes.
1361 	 */
1362 	for (curr = croot->xmlChildrenNode; curr != NULL; curr = curr->next) {
1363 		if (curr->name == NULL) {
1364 			topo_dprintf(mp->tm_hdl, TOPO_DBG_XML,
1365 			    "topo_xml_walk: Ignoring nameless xmlnode\n");
1366 			continue;
1367 		}
1368 		if (xmlStrcmp(curr->name, (xmlChar *)Set) == 0) {
1369 			if (joined_set)
1370 				continue;
1371 
1372 			set = xmlGetProp(curr, (xmlChar *)Setlist);
1373 
1374 			if (mp->tm_hdl->th_product)
1375 				key = mp->tm_hdl->th_product;
1376 			else
1377 				key = mp->tm_hdl->th_platform;
1378 
1379 			/*
1380 			 * If it's the default set then we'll store
1381 			 * a pointer to it so that if none of the other
1382 			 * sets apply to our product we can fall
1383 			 * back to this one.
1384 			 */
1385 			if (strcmp((char *)set, "default") == 0)
1386 				def_set = curr;
1387 			else if (set_contains(mp, key, (char *)set)) {
1388 				joined_set = 1;
1389 				if ((rdp = topo_xml_walk(mp, xinfo, curr,
1390 				    troot)) == NULL) {
1391 					topo_dprintf(mp->tm_hdl, TOPO_DBG_XML,
1392 					    "topo_xml_walk: failed1\n");
1393 				}
1394 				if (pr == NULL) {
1395 					rr = pr = rdp;
1396 				} else {
1397 					pr->rd_next = rdp;
1398 					pr = rdp;
1399 				}
1400 				rr->rd_cnt++;
1401 			}
1402 			xmlFree(set);
1403 		}
1404 	}
1405 	/*
1406 	 * If we haven't found a set that contains our product AND a default set
1407 	 * exists, then we'll process it.
1408 	 */
1409 	if (!joined_set && def_set)
1410 		if (topo_xml_walk(mp, xinfo, def_set, troot) == NULL) {
1411 			topo_dprintf(mp->tm_hdl, TOPO_DBG_XML,
1412 			    "topo_xml_walk: failed2\n");
1413 		}
1414 
1415 	/*
1416 	 * Now we're interested in children xmlNodes of croot tagged
1417 	 * as 'ranges'.  These define what topology nodes may exist, and need
1418 	 * to be verified.
1419 	 */
1420 	for (curr = croot->xmlChildrenNode; curr != NULL; curr = curr->next) {
1421 		if (curr->name == NULL) {
1422 			topo_dprintf(mp->tm_hdl, TOPO_DBG_XML,
1423 			    "topo_xml_walk: Ignoring nameless xmlnode\n");
1424 			continue;
1425 		}
1426 		if (xmlStrcmp(curr->name, (xmlChar *)Range) != 0) {
1427 			topo_dprintf(mp->tm_hdl, TOPO_DBG_XML,
1428 			    "topo_xml_walk: Ignoring non-range %s.\n",
1429 			    curr->name);
1430 			continue;
1431 		}
1432 		if ((rdp = tf_rdata_new(mp, xinfo, curr, troot)) == NULL) {
1433 			/*
1434 			 * Range processing error, continue walk
1435 			 */
1436 			continue;
1437 		}
1438 		if (pr == NULL) {
1439 			rr = pr = rdp;
1440 		} else {
1441 			pr->rd_next = rdp;
1442 			pr = rdp;
1443 		}
1444 		rr->rd_cnt++;
1445 	}
1446 
1447 	return (rr);
1448 }
1449 
1450 /*
1451  *  Convert parsed xml topology description into topology nodes
1452  */
1453 int
1454 topo_xml_enum(topo_mod_t *tmp, tf_info_t *xinfo, tnode_t *troot)
1455 {
1456 	xmlNodePtr xroot;
1457 
1458 	topo_dprintf(tmp->tm_hdl, TOPO_DBG_XML, "topo_xml_enum\n");
1459 
1460 	if ((xroot = xmlDocGetRootElement(xinfo->tf_xdoc)) == NULL) {
1461 		topo_dprintf(tmp->tm_hdl, TOPO_DBG_ERR,
1462 		    "Couldn't get root xmlNode.\n");
1463 		return (-1);
1464 	}
1465 	if ((xinfo->tf_rd = topo_xml_walk(tmp, xinfo, xroot, troot)) == NULL) {
1466 		topo_dprintf(tmp->tm_hdl, TOPO_DBG_ERR,
1467 		    "error within .xml topology: %s\n",
1468 		    topo_strerror(topo_mod_errno(tmp)));
1469 		return (-1);
1470 	}
1471 	return (0);
1472 }
1473 
1474 /*
1475  * Load an XML tree from filename and read it into a DOM parse tree.
1476  */
1477 static tf_info_t *
1478 txml_file_parse(topo_mod_t *tmp,
1479     int fd, const char *filenm, const char *escheme)
1480 {
1481 	xmlValidCtxtPtr vcp;
1482 	xmlNodePtr cursor;
1483 	xmlDocPtr document;
1484 	xmlDtdPtr dtd = NULL;
1485 	xmlChar *scheme = NULL;
1486 	char *dtdpath = NULL;
1487 	int readflags = 0;
1488 	tf_info_t *r;
1489 	int e, validate = 0;
1490 
1491 	topo_dprintf(tmp->tm_hdl, TOPO_DBG_XML,
1492 	    "txml_file_parse(filenm=%s, escheme=%s)\n", filenm, escheme);
1493 
1494 	/*
1495 	 * Since topologies can XInclude other topologies, and libxml2
1496 	 * doesn't do DTD-based validation with XInclude, by default
1497 	 * we don't validate topology files.  One can force
1498 	 * validation, though, by creating a TOPOXML_VALIDATE
1499 	 * environment variable and creating a TOPO_DTD environment
1500 	 * variable with the path to the DTD against which to validate.
1501 	 */
1502 	if (getenv("TOPOXML_VALIDATE") != NULL) {
1503 		dtdpath = getenv("TOPO_DTD");
1504 		if (dtdpath != NULL)
1505 			xmlLoadExtDtdDefaultValue = 0;
1506 		validate = 1;
1507 	}
1508 
1509 	/*
1510 	 * Splat warnings and errors related to parsing the topology
1511 	 * file if the TOPOXML_PERROR environment variable exists.
1512 	 */
1513 	if (getenv("TOPOXML_PERROR") == NULL)
1514 		readflags = XML_PARSE_NOERROR | XML_PARSE_NOWARNING;
1515 
1516 	if ((document = xmlReadFd(fd, filenm, NULL, readflags)) == NULL) {
1517 		topo_dprintf(tmp->tm_hdl, TOPO_DBG_ERR,
1518 		    "txml_file_parse: couldn't parse document.\n");
1519 		return (NULL);
1520 	}
1521 
1522 	/*
1523 	 * Verify that this is a document type we understand.
1524 	 */
1525 	if ((dtd = xmlGetIntSubset(document)) == NULL) {
1526 		topo_dprintf(tmp->tm_hdl, TOPO_DBG_ERR,
1527 		    "document has no DTD.\n");
1528 		xmlFreeDoc(document);
1529 		return (NULL);
1530 	}
1531 
1532 	if (strcmp((const char *)dtd->SystemID, TOPO_DTD_PATH) != 0) {
1533 		topo_dprintf(tmp->tm_hdl, TOPO_DBG_ERR,
1534 		    "document DTD unknown; bad topology file\n");
1535 		xmlFreeDoc(document);
1536 		return (NULL);
1537 	}
1538 
1539 	if ((cursor = xmlDocGetRootElement(document)) == NULL) {
1540 		topo_dprintf(tmp->tm_hdl, TOPO_DBG_ERR, "document is empty.\n");
1541 		xmlFreeDoc(document);
1542 		return (NULL);
1543 	}
1544 
1545 	/*
1546 	 * Make sure we're looking at a topology description in the
1547 	 * expected scheme.
1548 	 */
1549 	if (xmlStrcmp(cursor->name, (xmlChar *)Topology) != 0) {
1550 		topo_dprintf(tmp->tm_hdl, TOPO_DBG_ERR,
1551 		    "document is not a topology description.\n");
1552 		xmlFreeDoc(document);
1553 		return (NULL);
1554 	}
1555 	if ((scheme = xmlGetProp(cursor, (xmlChar *)Scheme)) == NULL) {
1556 		topo_dprintf(tmp->tm_hdl, TOPO_DBG_ERR,
1557 		    "topology lacks a scheme.\n");
1558 		(void) topo_mod_seterrno(tmp, ETOPO_PRSR_NOATTR);
1559 		xmlFreeDoc(document);
1560 		return (NULL);
1561 	}
1562 	if (xmlStrcmp(scheme, (xmlChar *)escheme) != 0) {
1563 		topo_dprintf(tmp->tm_hdl, TOPO_DBG_ERR,
1564 		    "topology in unrecognized scheme, %s, expecting %s\n",
1565 		    scheme, escheme);
1566 		(void) topo_mod_seterrno(tmp, ETOPO_PRSR_BADSCH);
1567 		xmlFree(scheme);
1568 		xmlFreeDoc(document);
1569 		return (NULL);
1570 	}
1571 
1572 	if (dtdpath != NULL) {
1573 		dtd = xmlParseDTD(NULL, (xmlChar *)dtdpath);
1574 		if (dtd == NULL) {
1575 			topo_dprintf(tmp->tm_hdl, TOPO_DBG_ERR,
1576 			    "Could not parse DTD \"%s\".\n",
1577 			    dtdpath);
1578 			xmlFree(scheme);
1579 			xmlFreeDoc(document);
1580 			return (NULL);
1581 		}
1582 
1583 		if (document->extSubset != NULL)
1584 			xmlFreeDtd(document->extSubset);
1585 
1586 		document->extSubset = dtd;
1587 	}
1588 
1589 	if (xmlXIncludeProcessFlags(document, XML_PARSE_XINCLUDE) == -1) {
1590 		xmlFree(scheme);
1591 		xmlFreeDoc(document);
1592 		topo_dprintf(tmp->tm_hdl, TOPO_DBG_ERR,
1593 		    "couldn't handle XInclude statements in document\n");
1594 		return (NULL);
1595 	}
1596 
1597 	if (validate) {
1598 		if ((vcp = xmlNewValidCtxt()) == NULL) {
1599 			xmlFree(scheme);
1600 			xmlFreeDoc(document);
1601 			return (NULL);
1602 		}
1603 		vcp->warning = xmlParserValidityWarning;
1604 		vcp->error = xmlParserValidityError;
1605 
1606 		e = xmlValidateDocument(vcp, document);
1607 
1608 		xmlFreeValidCtxt(vcp);
1609 
1610 		if (e == 0)
1611 			topo_dprintf(tmp->tm_hdl, TOPO_DBG_ERR,
1612 			    "Document is not valid.\n");
1613 	}
1614 
1615 	if ((r = tf_info_new(tmp, document, scheme)) == NULL) {
1616 		xmlFree(scheme);
1617 		xmlFreeDoc(document);
1618 		return (NULL);
1619 	}
1620 
1621 	xmlFree(scheme);
1622 	scheme = NULL;
1623 	return (r);
1624 }
1625 
1626 tf_info_t *
1627 topo_xml_read(topo_mod_t *tmp, const char *path, const char *escheme)
1628 {
1629 	int fd;
1630 	tf_info_t *tip;
1631 
1632 	if ((fd = open(path, O_RDONLY)) < 0) {
1633 		topo_dprintf(tmp->tm_hdl, TOPO_DBG_ERR,
1634 		    "failed to open %s for reading\n", path);
1635 		return (NULL);
1636 	}
1637 	tip = txml_file_parse(tmp, fd, path, escheme);
1638 	(void) close(fd);
1639 	return (tip);
1640 }
1641