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