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