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 2006 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_mod.h>
43 #include <topo_subr.h>
44 #include <topo_alloc.h>
45 #include <topo_parse.h>
46 #include <topo_error.h>
47 
48 static tf_rdata_t *topo_xml_walk(topo_mod_t *,
49     tf_info_t *, xmlNodePtr, tnode_t *);
50 
51 int
52 xmlattr_to_stab(topo_mod_t *mp, xmlNodePtr n, const char *stabname,
53     topo_stability_t *rs)
54 {
55 	xmlChar *str;
56 	int rv = 0;
57 
58 	if (n == NULL) {
59 		/* If there is no Stability defined, we default to private */
60 		*rs = TOPO_STABILITY_PRIVATE;
61 		return (0);
62 	}
63 	if ((str = xmlGetProp(n, (xmlChar *)stabname)) == NULL) {
64 		topo_dprintf(mp->tm_hdl, TOPO_DBG_XML,
65 		    "attribute to stability:\n");
66 		return (topo_mod_seterrno(mp, ETOPO_PRSR_NOATTR));
67 	}
68 
69 	if (xmlStrcmp(str, (xmlChar *)Internal) == 0) {
70 		*rs = TOPO_STABILITY_INTERNAL;
71 	} else if (xmlStrcmp(str, (xmlChar *)Private) == 0) {
72 		*rs = TOPO_STABILITY_PRIVATE;
73 	} else if (xmlStrcmp(str, (xmlChar *)Obsolete) == 0) {
74 		*rs = TOPO_STABILITY_OBSOLETE;
75 	} else if (xmlStrcmp(str, (xmlChar *)External) == 0) {
76 		*rs = TOPO_STABILITY_EXTERNAL;
77 	} else if (xmlStrcmp(str, (xmlChar *)Unstable) == 0) {
78 		*rs = TOPO_STABILITY_UNSTABLE;
79 	} else if (xmlStrcmp(str, (xmlChar *)Evolving) == 0) {
80 		*rs = TOPO_STABILITY_EVOLVING;
81 	} else if (xmlStrcmp(str, (xmlChar *)Stable) == 0) {
82 		*rs = TOPO_STABILITY_STABLE;
83 	} else if (xmlStrcmp(str, (xmlChar *)Standard) == 0) {
84 		*rs = TOPO_STABILITY_STANDARD;
85 	} else {
86 		xmlFree(str);
87 		return (topo_mod_seterrno(mp, ETOPO_PRSR_BADSTAB));
88 	}
89 	xmlFree(str);
90 	return (rv);
91 }
92 
93 int
94 xmlattr_to_int(topo_mod_t *mp,
95     xmlNodePtr n, const char *propname, uint64_t *value)
96 {
97 	xmlChar *str;
98 	xmlChar *estr;
99 
100 	topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "attribute to int\n");
101 	if ((str = xmlGetProp(n, (xmlChar *)propname)) == NULL)
102 		return (topo_mod_seterrno(mp, ETOPO_PRSR_NOATTR));
103 	*value = strtoull((char *)str, (char **)&estr, 10);
104 	if (estr == str) {
105 		/* no conversion was done */
106 		xmlFree(str);
107 		return (topo_mod_seterrno(mp, ETOPO_PRSR_BADNUM));
108 	}
109 	xmlFree(str);
110 	return (0);
111 }
112 
113 static int
114 xmlattr_to_fmri(topo_mod_t *mp,
115     xmlNodePtr xn, const char *propname, nvlist_t **rnvl)
116 {
117 	xmlChar *str;
118 
119 	topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "attribute to int\n");
120 	if ((str = xmlGetProp(xn, (xmlChar *)propname)) == NULL)
121 		return (topo_mod_seterrno(mp, ETOPO_PRSR_NOATTR));
122 	if (topo_mod_str2nvl(mp, (const char *)str, rnvl) < 0)
123 		return (-1);
124 	xmlFree(str);
125 	return (0);
126 }
127 
128 static topo_type_t
129 xmlattr_to_type(topo_mod_t *mp, xmlNodePtr xn)
130 {
131 	topo_type_t rv;
132 	xmlChar *str;
133 	if ((str = xmlGetProp(xn, (xmlChar *)Type)) == NULL) {
134 		topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR, "Property missing type");
135 		(void) topo_mod_seterrno(mp, ETOPO_PRSR_NOATTR);
136 		return (TOPO_TYPE_INVALID);
137 	}
138 	if (xmlStrcmp(str, (xmlChar *)Int32) == 0) {
139 		rv = TOPO_TYPE_INT32;
140 	} else if (xmlStrcmp(str, (xmlChar *)UInt32) == 0) {
141 		rv = TOPO_TYPE_UINT32;
142 	} else if (xmlStrcmp(str, (xmlChar *)Int64) == 0) {
143 		rv = TOPO_TYPE_INT64;
144 	} else if (xmlStrcmp(str, (xmlChar *)UInt64) == 0) {
145 		rv = TOPO_TYPE_UINT64;
146 	} else if (xmlStrcmp(str, (xmlChar *)FMRI) == 0) {
147 		rv = TOPO_TYPE_FMRI;
148 	} else if (xmlStrcmp(str, (xmlChar *)String) == 0) {
149 		rv = TOPO_TYPE_STRING;
150 	} else {
151 		xmlFree(str);
152 		topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
153 		    "Unrecognized type attribute.\n");
154 		(void) topo_mod_seterrno(mp, ETOPO_PRSR_BADTYPE);
155 		return (TOPO_TYPE_INVALID);
156 	}
157 	xmlFree(str);
158 	return (rv);
159 }
160 
161 static int
162 xmlprop_xlate(topo_mod_t *mp, xmlNodePtr xn, nvlist_t *nvl)
163 {
164 	topo_type_t ptype;
165 	xmlChar *str;
166 	nvlist_t *fmri;
167 	uint64_t ui;
168 	int e;
169 
170 	if ((str = xmlGetProp(xn, (xmlChar *)Immutable)) != NULL) {
171 		if (xmlStrcmp(str, (xmlChar *)False) == 0)
172 			(void) nvlist_add_boolean_value(nvl, INV_IMMUTE,
173 			    B_FALSE);
174 		else
175 			(void) nvlist_add_boolean_value(nvl, INV_IMMUTE,
176 			    B_TRUE);
177 		xmlFree(str);
178 	} else {
179 		(void) nvlist_add_boolean_value(nvl, INV_IMMUTE, B_TRUE);
180 	}
181 
182 	if ((ptype = xmlattr_to_type(mp, xn)) == TOPO_TYPE_INVALID)
183 		return (-1);
184 	e = nvlist_add_int32(nvl, INV_PVALTYPE, ptype);
185 	if (e != 0)
186 		return (-1);
187 	switch (ptype) {
188 	case TOPO_TYPE_INT32:
189 		if (xmlattr_to_int(mp, xn, Value, &ui) < 0)
190 			return (-1);
191 		e = nvlist_add_int32(nvl, INV_PVAL, (int32_t)ui);
192 		break;
193 	case TOPO_TYPE_UINT32:
194 		if (xmlattr_to_int(mp, xn, Value, &ui) < 0)
195 			return (-1);
196 		e = nvlist_add_uint32(nvl, INV_PVAL, (uint32_t)ui);
197 		break;
198 	case TOPO_TYPE_INT64:
199 		if (xmlattr_to_int(mp, xn, Value, &ui) < 0)
200 			return (-1);
201 		e = nvlist_add_int64(nvl, INV_PVAL, (int64_t)ui);
202 		break;
203 	case TOPO_TYPE_UINT64:
204 		if (xmlattr_to_int(mp, xn, Value, &ui) < 0)
205 			return (-1);
206 		e = nvlist_add_uint64(nvl, INV_PVAL, ui);
207 		break;
208 	case TOPO_TYPE_FMRI:
209 		if (xmlattr_to_fmri(mp, xn, Value, &fmri) < 0)
210 			return (-1);
211 		e = nvlist_add_nvlist(nvl, INV_PVAL, fmri);
212 		nvlist_free(fmri);
213 		break;
214 	case TOPO_TYPE_STRING:
215 		if ((str = xmlGetProp(xn, (xmlChar *)Value)) == NULL)
216 			return (-1);
217 		e = nvlist_add_string(nvl, INV_PVAL, (char *)str);
218 		xmlFree(str);
219 		break;
220 	default:
221 		topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
222 		    "Unrecognized type attribute.\n");
223 		return (topo_mod_seterrno(mp, ETOPO_PRSR_BADTYPE));
224 	}
225 	if (e != 0) {
226 		topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
227 		    "Nvlist construction failed.\n");
228 		return (topo_mod_seterrno(mp, ETOPO_NOMEM));
229 	}
230 	return (0);
231 }
232 
233 static int
234 dependent_create(topo_mod_t *mp,
235     tf_info_t *xinfo, tf_pad_t *pad, xmlNodePtr dxn, tnode_t *ptn)
236 {
237 	tf_rdata_t *rp, *pp, *np;
238 	xmlChar *grptype;
239 	int sibs = 0;
240 
241 	topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "dependent create\n");
242 	if ((grptype = xmlGetProp(dxn, (xmlChar *)Grouping)) == NULL) {
243 		topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
244 		    "Dependents missing grouping attribute");
245 		return (topo_mod_seterrno(mp, ETOPO_PRSR_NOATTR));
246 	}
247 
248 	pp = NULL;
249 	if (xmlStrcmp(grptype, (xmlChar *)Siblings) == 0) {
250 		rp = pad->tpad_sibs;
251 		sibs++;
252 	} else if (xmlStrcmp(grptype, (xmlChar *)Children) == 0) {
253 		rp = pad->tpad_child;
254 	} else {
255 		topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
256 		    "Dependents have bogus grouping attribute");
257 		xmlFree(grptype);
258 		return (topo_mod_seterrno(mp, ETOPO_PRSR_BADGRP));
259 	}
260 	xmlFree(grptype);
261 	/* Add processed dependents to the tail of the list */
262 	while (rp != NULL) {
263 		pp = rp;
264 		rp = rp->rd_next;
265 	}
266 	if ((np = topo_xml_walk(mp, xinfo, dxn, ptn)) == NULL) {
267 		topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
268 		    "error within dependent .xml topology: "
269 		    "%s\n", topo_strerror(topo_mod_errno(mp)));
270 		return (-1);
271 	}
272 	if (pp != NULL)
273 		pp->rd_next = np;
274 	else if (sibs == 1)
275 		pad->tpad_sibs = np;
276 	else
277 		pad->tpad_child = np;
278 	return (0);
279 }
280 
281 static int
282 dependents_create(topo_mod_t *mp,
283     tf_info_t *xinfo, tf_pad_t *pad, xmlNodePtr pxn, tnode_t *ptn)
284 {
285 	xmlNodePtr cn;
286 
287 	topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "dependents create\n");
288 	for (cn = pxn->xmlChildrenNode; cn != NULL; cn = cn->next) {
289 		if (xmlStrcmp(cn->name, (xmlChar *)Dependents) == 0) {
290 			if (dependent_create(mp, xinfo, pad, cn, ptn) < 0)
291 				return (-1);
292 		}
293 	}
294 	return (0);
295 }
296 
297 static int
298 prop_create(topo_mod_t *mp,
299     nvlist_t *pfmri, tnode_t *ptn, const char *gnm, const char *pnm,
300     topo_type_t ptype, int flag)
301 {
302 	nvlist_t *fmri;
303 	uint32_t ui32;
304 	uint64_t ui64;
305 	int32_t i32;
306 	int64_t i64;
307 	char *str;
308 	int err, e;
309 
310 	topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "prop create\n");
311 	switch (ptype) {
312 	case TOPO_TYPE_INT32:
313 		e = nvlist_lookup_int32(pfmri, INV_PVAL, &i32);
314 		break;
315 	case TOPO_TYPE_UINT32:
316 		e = nvlist_lookup_uint32(pfmri, INV_PVAL, &ui32);
317 		break;
318 	case TOPO_TYPE_INT64:
319 		e = nvlist_lookup_int64(pfmri, INV_PVAL, &i64);
320 		break;
321 	case TOPO_TYPE_UINT64:
322 		e = nvlist_lookup_uint64(pfmri, INV_PVAL, &ui64);
323 		break;
324 	case TOPO_TYPE_FMRI:
325 		e = nvlist_lookup_nvlist(pfmri, INV_PVAL, &fmri);
326 		break;
327 	case TOPO_TYPE_STRING:
328 		e = nvlist_lookup_string(pfmri, INV_PVAL, &str);
329 		break;
330 	default:
331 		e = ETOPO_PRSR_BADTYPE;
332 	}
333 	if (e != 0) {
334 		topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
335 		    "prop value lookup failed.\n");
336 		return (topo_mod_seterrno(mp, e));
337 	}
338 	switch (ptype) {
339 	case TOPO_TYPE_INT32:
340 		e = topo_prop_set_int32(ptn, gnm, pnm, flag, i32, &err);
341 		break;
342 	case TOPO_TYPE_UINT32:
343 		e = topo_prop_set_uint32(ptn, gnm, pnm, flag, ui32, &err);
344 		break;
345 	case TOPO_TYPE_INT64:
346 		e = topo_prop_set_int64(ptn, gnm, pnm, flag, i64, &err);
347 		break;
348 	case TOPO_TYPE_UINT64:
349 		e = topo_prop_set_uint64(ptn, gnm, pnm, flag, ui64, &err);
350 		break;
351 	case TOPO_TYPE_FMRI:
352 		e = topo_prop_set_fmri(ptn, gnm, pnm, flag, fmri, &err);
353 		break;
354 	case TOPO_TYPE_STRING:
355 		e = topo_prop_set_string(ptn, gnm, pnm, flag, str, &err);
356 		break;
357 	}
358 	if (e != 0 && err != ETOPO_PROP_DEFD) {
359 
360 		/*
361 		 * Some properties may have already been set
362 		 * in topo_node_bind() or topo_prop_inherit if we are
363 		 * enumerating from a static .xml file
364 		 */
365 		topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR, "prop set "
366 		    "failed %s/%s:%s\n", gnm, pnm, topo_strerror(err));
367 		return (topo_mod_seterrno(mp, err));
368 	}
369 	return (0);
370 }
371 
372 static int
373 props_create(topo_mod_t *mp,
374     tnode_t *ptn, const char *gnm, nvlist_t **props, int nprops)
375 {
376 	topo_type_t ptype;
377 	boolean_t pim;
378 	char *pnm;
379 	int32_t i32;
380 	int flag;
381 	int pn;
382 	int e;
383 
384 	topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "props create\n");
385 	for (pn = 0; pn < nprops; pn++) {
386 		e = nvlist_lookup_string(props[pn], INV_PNAME, &pnm);
387 		if (e != 0) {
388 			topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
389 			    "props create lookup (%s) failure: %s",
390 			    INV_PNAME, topo_strerror(e));
391 			return (topo_mod_seterrno(mp, ETOPO_PRSR_NVPROP));
392 		}
393 		e = nvlist_lookup_boolean_value(props[pn], INV_IMMUTE, &pim);
394 		if (e != 0) {
395 			topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
396 			    "props create lookup (%s) failure: %s",
397 			    INV_IMMUTE, topo_strerror(e));
398 			return (topo_mod_seterrno(mp, ETOPO_PRSR_NVPROP));
399 		}
400 		flag = (pim == B_TRUE) ?
401 		    TOPO_PROP_IMMUTABLE : TOPO_PROP_MUTABLE;
402 
403 		e = nvlist_lookup_int32(props[pn], INV_PVALTYPE, &i32);
404 		if (e != 0) {
405 			topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
406 			    "props create lookup (%s) failure: %s",
407 			    INV_PVALTYPE, topo_strerror(e));
408 			return (topo_mod_seterrno(mp, ETOPO_PRSR_NVPROP));
409 		}
410 		ptype = (topo_type_t)i32;
411 		if (prop_create(mp, props[pn], ptn, gnm, pnm, ptype, flag) < 0)
412 			return (-1);
413 	}
414 	return (0);
415 }
416 
417 static int
418 pgroups_create(topo_mod_t *mp, tf_pad_t *pad, tnode_t *ptn)
419 {
420 	topo_pgroup_info_t pgi;
421 	nvlist_t **props;
422 	char *gnm;
423 	char *nmstab, *dstab;
424 	uint32_t rnprops, nprops;
425 	uint32_t gv;
426 	int pg;
427 	int e;
428 
429 	topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "pgroups create\n");
430 	for (pg = 0; pg < pad->tpad_pgcnt; pg++) {
431 		e = nvlist_lookup_string(pad->tpad_pgs[pg],
432 		    INV_PGRP_NAME, &gnm);
433 		if (e != 0) {
434 			topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
435 			    "pad lookup (%s) failed.\n",
436 			    INV_PGRP_NAME);
437 			return (topo_mod_seterrno(mp, ETOPO_PRSR_NVPROP));
438 		}
439 		e = nvlist_lookup_string(pad->tpad_pgs[pg],
440 		    INV_PGRP_NMSTAB, &nmstab);
441 		if (e != 0) {
442 			if (e != ENOENT) {
443 				topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
444 				    "pad lookup (%s) "
445 				    "failed.\n", INV_PGRP_NMSTAB);
446 				return (topo_mod_seterrno(mp,
447 				    ETOPO_PRSR_NVPROP));
448 			} else {
449 				nmstab = TOPO_STABSTR_PRIVATE;
450 			}
451 		}
452 		e = nvlist_lookup_string(pad->tpad_pgs[pg],
453 		    INV_PGRP_DSTAB, &dstab);
454 		if (e != 0) {
455 			if (e != ENOENT) {
456 				topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
457 				    "pad lookup (%s) failed.\n",
458 				    INV_PGRP_DSTAB);
459 				return (topo_mod_seterrno(mp,
460 				    ETOPO_PRSR_NVPROP));
461 			} else {
462 				dstab = TOPO_STABSTR_PRIVATE;
463 			}
464 		}
465 		e = nvlist_lookup_uint32(pad->tpad_pgs[pg],
466 		    INV_PGRP_VER, &gv);
467 		if (e != 0) {
468 			topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
469 			    "pad lookup (%s) failed.\n",
470 			    INV_PGRP_VER);
471 			return (topo_mod_seterrno(mp, ETOPO_PRSR_NVPROP));
472 		}
473 		pgi.tpi_name = gnm;
474 		pgi.tpi_namestab = topo_name2stability(nmstab);
475 		pgi.tpi_datastab = topo_name2stability(dstab);
476 		pgi.tpi_version = gv;
477 		if (topo_pgroup_create(ptn, &pgi, &e) != 0) {
478 			if (e != ETOPO_PROP_DEFD) {
479 				topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
480 				    "pgroups create failure: %s\n",
481 				    topo_strerror(e));
482 				return (-1);
483 			}
484 		}
485 		e = nvlist_lookup_uint32(pad->tpad_pgs[pg],
486 		    INV_PGRP_NPROP, &rnprops);
487 		e |= nvlist_lookup_nvlist_array(pad->tpad_pgs[pg],
488 		    INV_PGRP_ALLPROPS, &props, &nprops);
489 		if (rnprops != nprops) {
490 			topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
491 			    "recorded number of props %d does not "
492 			    "match number of props recorded %d.\n",
493 			    rnprops, nprops);
494 		}
495 		if (props_create(mp, ptn, gnm, props, nprops) < 0)
496 			return (-1);
497 	}
498 	return (0);
499 }
500 
501 static nvlist_t *
502 pval_record(topo_mod_t *mp, xmlNodePtr xn)
503 {
504 	nvlist_t *pnvl = NULL;
505 	xmlChar *pname;
506 
507 	topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "pval record\n");
508 	if ((pname = xmlGetProp(xn, (xmlChar *)Name)) == NULL) {
509 		topo_dprintf(mp->tm_hdl, TOPO_DBG_XML,
510 		    "propval lacks a name\n");
511 		(void) topo_mod_seterrno(mp, ETOPO_PRSR_NOATTR);
512 		return (NULL);
513 	}
514 	if (topo_mod_nvalloc(mp, &pnvl, NV_UNIQUE_NAME) < 0) {
515 		xmlFree(pname);
516 		return (NULL);
517 	}
518 	if (nvlist_add_string(pnvl, INV_PNAME, (char *)pname) < 0) {
519 		xmlFree(pname);
520 		nvlist_free(pnvl);
521 		return (NULL);
522 	}
523 	xmlFree(pname);
524 	/* FMXXX stability of the property name */
525 
526 	if (xmlprop_xlate(mp, xn, pnvl) < 0) {
527 		nvlist_free(pnvl);
528 		return (NULL);
529 	}
530 	return (pnvl);
531 }
532 
533 static int
534 pgroup_record(topo_mod_t *mp, xmlNodePtr pxn, tf_pad_t *rpad, int pi)
535 {
536 	topo_stability_t nmstab, dstab;
537 	uint64_t ver;
538 	xmlNodePtr cn;
539 	xmlChar *name;
540 	nvlist_t **apl = NULL;
541 	nvlist_t *pgnvl = NULL;
542 	int pcnt = 0;
543 	int ai = 0;
544 	int e;
545 
546 	topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "pgroup record\n");
547 	if ((name = xmlGetProp(pxn, (xmlChar *)Name)) == NULL) {
548 		topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
549 		    "propgroup lacks a name\n");
550 		return (topo_mod_seterrno(mp, ETOPO_PRSR_NOATTR));
551 	}
552 	if (xmlattr_to_int(mp, pxn, Version, &ver) < 0) {
553 		topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
554 		    "propgroup lacks a version\n");
555 		return (topo_mod_seterrno(mp, ETOPO_PRSR_NOATTR));
556 	}
557 	if (xmlattr_to_stab(mp, pxn, Namestab, &nmstab) < 0) {
558 		topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
559 		    "propgroup lacks name-stability\n");
560 		return (topo_mod_seterrno(mp, ETOPO_PRSR_NOATTR));
561 	}
562 	if (xmlattr_to_stab(mp, pxn, Datastab, &dstab) < 0) {
563 		topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
564 		    "propgroup lacks data-stability\n");
565 		return (topo_mod_seterrno(mp, ETOPO_PRSR_NOATTR));
566 	}
567 
568 	topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "pgroup %s\n", (char *)name);
569 	for (cn = pxn->xmlChildrenNode; cn != NULL; cn = cn->next) {
570 		if (xmlStrcmp(cn->name, (xmlChar *)Propval) == 0)
571 			pcnt++;
572 	}
573 
574 	if (topo_mod_nvalloc(mp, &pgnvl, NV_UNIQUE_NAME) < 0) {
575 		xmlFree(name);
576 		return (-1);
577 	}
578 
579 	e = nvlist_add_string(pgnvl, INV_PGRP_NAME, (char *)name);
580 	e |= nvlist_add_uint32(pgnvl, INV_PGRP_NMSTAB, nmstab);
581 	e |= nvlist_add_uint32(pgnvl, INV_PGRP_DSTAB, dstab);
582 	e |= nvlist_add_uint32(pgnvl, INV_PGRP_VER, ver);
583 	e |= nvlist_add_uint32(pgnvl, INV_PGRP_NPROP, pcnt);
584 	if (e != 0 ||
585 	    (apl = topo_mod_zalloc(mp, pcnt * sizeof (nvlist_t *))) == NULL) {
586 		xmlFree(name);
587 		nvlist_free(pgnvl);
588 		return (-1);
589 	}
590 	for (cn = pxn->xmlChildrenNode; cn != NULL; cn = cn->next) {
591 		if (xmlStrcmp(cn->name, (xmlChar *)Propval) == 0) {
592 			if (ai < pcnt) {
593 				if ((apl[ai] = pval_record(mp, cn)) == NULL)
594 					break;
595 			}
596 			ai++;
597 		}
598 	}
599 	xmlFree(name);
600 	e |= (ai != pcnt);
601 	e |= nvlist_add_nvlist_array(pgnvl, INV_PGRP_ALLPROPS, apl, pcnt);
602 	for (ai = 0; ai < pcnt; ai++)
603 		if (apl[ai] != NULL)
604 			nvlist_free(apl[ai]);
605 	topo_mod_free(mp, apl, pcnt * sizeof (nvlist_t *));
606 	if (e != 0) {
607 		nvlist_free(pgnvl);
608 		return (-1);
609 	}
610 	rpad->tpad_pgs[pi] = pgnvl;
611 	return (0);
612 }
613 
614 static int
615 pgroups_record(topo_mod_t *mp, xmlNodePtr pxn, tf_pad_t *rpad)
616 {
617 	xmlNodePtr cn;
618 	int pi = 0;
619 
620 	topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "pgroups record\n");
621 	for (cn = pxn->xmlChildrenNode; cn != NULL; cn = cn->next) {
622 		if (xmlStrcmp(cn->name, (xmlChar *)Propgrp) == 0) {
623 			if (pgroup_record(mp, cn, rpad, pi++) < 0)
624 				return (-1);
625 		}
626 	}
627 	return (0);
628 }
629 
630 /*
631  * Process the property group and dependents xmlNode children of
632  * parent xmlNode pxn.
633  */
634 static int
635 pad_process(topo_mod_t *mp,
636     tf_info_t *xinfo, xmlNodePtr pxn, tnode_t *ptn, tf_pad_t **rpad)
637 {
638 	xmlNodePtr cn;
639 	tf_pad_t *new = *rpad;
640 	int pgcnt = 0;
641 	int dcnt = 0;
642 
643 	topo_dprintf(mp->tm_hdl, TOPO_DBG_XML,
644 	    "pad process beneath %s\n", topo_node_name(ptn));
645 	if (new == NULL) {
646 		for (cn = pxn->xmlChildrenNode; cn != NULL; cn = cn->next) {
647 			if (xmlStrcmp(cn->name, (xmlChar *)Dependents) == 0)
648 				dcnt++;
649 			else if (xmlStrcmp(cn->name, (xmlChar *)Propgrp) == 0)
650 				pgcnt++;
651 		}
652 		if ((new = tf_pad_new(mp, pgcnt, dcnt)) == NULL)
653 			return (-1);
654 		if (dcnt == 0 && pgcnt == 0) {
655 			*rpad = new;
656 			return (0);
657 		}
658 
659 		if (pgcnt > 0) {
660 			new->tpad_pgs =
661 			    topo_mod_zalloc(mp, pgcnt * sizeof (nvlist_t *));
662 			if (new->tpad_pgs == NULL) {
663 				tf_pad_free(mp, new);
664 				return (NULL);
665 			}
666 			if (pgroups_record(mp, pxn, new) < 0) {
667 				tf_pad_free(mp, new);
668 				return (NULL);
669 			}
670 		}
671 		*rpad = new;
672 	}
673 
674 	if (new->tpad_dcnt > 0)
675 		if (dependents_create(mp, xinfo, new, pxn, ptn) < 0)
676 			return (-1);
677 
678 	if (new->tpad_pgcnt > 0)
679 		if (pgroups_create(mp, new, ptn) < 0)
680 			return (-1);
681 	return (0);
682 }
683 
684 static int
685 node_process(topo_mod_t *mp, xmlNodePtr nn, tf_rdata_t *rd)
686 {
687 	xmlChar *str;
688 	topo_instance_t inst;
689 	tf_idata_t *newi;
690 	tnode_t *ntn;
691 	uint64_t ui;
692 	int rv = -1;
693 	int s = 0;
694 
695 	topo_dprintf(mp->tm_hdl, TOPO_DBG_XML,
696 	    "node process %s\n", rd->rd_name);
697 
698 	if (xmlattr_to_int(mp, nn, Instance, &ui) < 0)
699 		goto nodedone;
700 	inst = (topo_instance_t)ui;
701 
702 	if ((str = xmlGetProp(nn, (xmlChar *)Static)) != NULL) {
703 		if (xmlStrcmp(str, (xmlChar *)True) == 0)
704 			s = 1;
705 		xmlFree(str);
706 	}
707 
708 	if (s == 0) {
709 		if (topo_mod_enumerate(rd->rd_mod, rd->rd_pn,
710 		    rd->rd_finfo->tf_scheme, rd->rd_name, inst, inst,
711 		    s == 1 ? &s : NULL) < 0)
712 			goto nodedone;
713 	}
714 	ntn = topo_node_lookup(rd->rd_pn, rd->rd_name, inst);
715 	if (ntn == NULL) {
716 
717 		/*
718 		 * If this is a static node declaration, we can
719 		 * ignore the lookup failure and continue
720 		 * processing.  Otherwise, something
721 		 * went wrong during enumeration
722 		 */
723 		if (s == 1)
724 			rv = 0;
725 		goto nodedone;
726 	}
727 
728 	if ((newi = tf_idata_new(mp, inst, ntn)) == NULL) {
729 		topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
730 		    "tf_idata_new failed.\n");
731 		goto nodedone;
732 	}
733 	if (tf_idata_insert(&rd->rd_instances, newi) < 0) {
734 		topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
735 		    "tf_idata_insert failed.\n");
736 		goto nodedone;
737 	}
738 	if (pad_process(mp, rd->rd_finfo, nn, ntn, &newi->ti_pad) < 0)
739 		goto nodedone;
740 	rv = 0;
741 nodedone:
742 	topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "done with node %s.\n",
743 	    rd->rd_name);
744 	return (rv);
745 }
746 
747 static tf_edata_t *
748 enum_attributes_process(topo_mod_t *mp, xmlNodePtr en)
749 {
750 	tf_edata_t *einfo;
751 	uint64_t ui;
752 
753 	topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "enum attributes process\n");
754 	if ((einfo = topo_mod_zalloc(mp, sizeof (tf_edata_t))) == NULL) {
755 		(void) topo_mod_seterrno(mp, ETOPO_NOMEM);
756 		return (NULL);
757 	}
758 	einfo->te_name = (char *)xmlGetProp(en, (xmlChar *)Name);
759 	if (einfo->te_name == NULL) {
760 		topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
761 		    "Enumerator name attribute missing.\n");
762 		(void) topo_mod_seterrno(mp, ETOPO_PRSR_NOATTR);
763 		goto enodedone;
764 	}
765 
766 	/*
767 	 * Check for recursive enumeration
768 	 */
769 	if (strcmp(einfo->te_name, mp->tm_name) == 0) {
770 		topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
771 		    "Recursive enumeration detected for %s\n",
772 		    einfo->te_name);
773 		(void) topo_mod_seterrno(mp, ETOPO_ENUM_RECURS);
774 		goto enodedone;
775 	}
776 	if (xmlattr_to_int(mp, en, Version, &ui) < 0)
777 		goto enodedone;
778 	einfo->te_vers = (int)ui;
779 
780 	return (einfo);
781 
782 enodedone:
783 	if (einfo->te_name != NULL)
784 		xmlFree(einfo->te_name);
785 	return (NULL);
786 }
787 
788 static int
789 enum_run(topo_mod_t *mp, tf_rdata_t *rd)
790 {
791 	topo_hdl_t *thp = mp->tm_hdl;
792 	int e = -1;
793 
794 	/*
795 	 * Check if the enumerator module is already loaded.
796 	 * Module loading is single-threaded at this point so there's
797 	 * no need to worry about the module going away or bumping the
798 	 * ref count.
799 	 */
800 	if ((rd->rd_mod = topo_mod_lookup(thp, rd->rd_einfo->te_name,
801 	    0)) == NULL) {
802 		if ((rd->rd_mod = topo_mod_load(mp, rd->rd_einfo->te_name,
803 		    rd->rd_einfo->te_vers)) == NULL) {
804 			topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
805 			    "mod_load of %s failed: %s.\n",
806 			    rd->rd_einfo->te_name,
807 			    topo_strerror(topo_mod_errno(mp)));
808 			(void) topo_hdl_seterrno(thp, topo_mod_errno(mp));
809 			return (e);
810 		}
811 	}
812 	/*
813 	 * We're live, so let's enumerate.
814 	 */
815 	topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "enumerate request. (%s)\n",
816 	    rd->rd_einfo->te_name);
817 	e = topo_mod_enumerate(rd->rd_mod, rd->rd_pn, rd->rd_einfo->te_name,
818 	    rd->rd_name, rd->rd_min, rd->rd_max, NULL);
819 	topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "back from enumeration. %d\n",
820 	    e);
821 	if (e != 0) {
822 		topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
823 		    "Enumeration failed (%s)\n",
824 		    topo_strerror(topo_mod_errno(mp)));
825 		(void) topo_hdl_seterrno(thp, EMOD_PARTIAL_ENUM);
826 		return (topo_mod_seterrno(mp, EMOD_PARTIAL_ENUM));
827 	}
828 	return (e);
829 }
830 
831 int
832 topo_xml_range_process(topo_mod_t *mp, xmlNodePtr rn, tf_rdata_t *rd)
833 {
834 	/*
835 	 * The range may have several children xmlNodes, that may
836 	 * represent the enumeration method, property groups,
837 	 * dependents or nodes.
838 	 */
839 	xmlNodePtr cn;
840 	tnode_t *ct;
841 	int e;
842 
843 	topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "process %s range beneath %s\n",
844 	    rd->rd_name, topo_node_name(rd->rd_pn));
845 	e = topo_node_range_create(mp,
846 	    rd->rd_pn, rd->rd_name, rd->rd_min, rd->rd_max);
847 	if (e != 0 && topo_mod_errno(mp) != ETOPO_NODE_DUP) {
848 		topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
849 		    "Range create failed due to %s.\n",
850 		    topo_strerror(topo_mod_errno(mp)));
851 		return (-1);
852 	}
853 	for (cn = rn->xmlChildrenNode; cn != NULL; cn = cn->next)
854 		if (xmlStrcmp(cn->name, (xmlChar *)Enum_meth) == 0)
855 			break;
856 
857 	if (cn != NULL) {
858 		if ((rd->rd_einfo = enum_attributes_process(mp, cn)) == NULL)
859 			return (-1);
860 		if (enum_run(mp, rd) < 0) {
861 			/*
862 			 * Note the failure but continue on
863 			 */
864 			topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
865 			    "Enumeration failed.\n");
866 		}
867 	}
868 
869 	/* Now look for nodes, i.e., hard instances */
870 	for (cn = rn->xmlChildrenNode; cn != NULL; cn = cn->next) {
871 		if (xmlStrcmp(cn->name, (xmlChar *)Node) == 0)
872 			if (node_process(mp, cn, rd) < 0) {
873 				topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
874 				    "node processing failed: %s.\n",
875 				    topo_strerror(topo_mod_errno(mp)));
876 				return (topo_mod_seterrno(mp,
877 				    EMOD_PARTIAL_ENUM));
878 			}
879 	}
880 
881 	/* Property groups and Dependencies */
882 	ct = topo_child_first(rd->rd_pn);
883 	while (ct != NULL) {
884 		/* Only care about instances within the range */
885 		if (strcmp(topo_node_name(ct), rd->rd_name) != 0) {
886 			ct = topo_child_next(rd->rd_pn, ct);
887 			continue;
888 		}
889 		if (pad_process(mp, rd->rd_finfo, rn, ct, &rd->rd_pad) < 0)
890 			return (-1);
891 		ct = topo_child_next(rd->rd_pn, ct);
892 	}
893 	topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "end range process %s\n",
894 	    rd->rd_name);
895 	return (0);
896 }
897 
898 static tf_rdata_t *
899 topo_xml_walk(topo_mod_t *mp,
900     tf_info_t *xinfo, xmlNodePtr croot, tnode_t *troot)
901 {
902 	xmlNodePtr curr;
903 	tf_rdata_t *rr, *pr, *rdp;
904 
905 	/*
906 	 * What we're interested in are children xmlNodes of croot tagged
907 	 * as 'ranges', these define topology nodes may exist, and need
908 	 * to be verified.
909 	 */
910 	topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "topo_xml_walk\n");
911 	rr = pr = NULL;
912 	for (curr = croot->xmlChildrenNode; curr != NULL; curr = curr->next) {
913 		if (curr->name == NULL) {
914 			topo_dprintf(mp->tm_hdl, TOPO_DBG_XML,
915 			    "Ignoring nameless xmlnode\n");
916 			continue;
917 		}
918 		if (xmlStrcmp(curr->name, (xmlChar *)Range) != 0) {
919 			topo_dprintf(mp->tm_hdl, TOPO_DBG_XML,
920 			    "Ignoring non-range %s.\n", curr->name);
921 			continue;
922 		}
923 		if ((rdp = tf_rdata_new(mp, xinfo, curr, troot)) == NULL) {
924 			tf_rdata_free(mp, rr);
925 			return (NULL);
926 		}
927 		if (pr == NULL) {
928 			rr = pr = rdp;
929 		} else {
930 			pr->rd_next = rdp;
931 			pr = rdp;
932 		}
933 		rr->rd_cnt++;
934 	}
935 	return (rr);
936 }
937 
938 /*
939  *  Convert parsed xml topology description into topology nodes
940  */
941 int
942 topo_xml_enum(topo_mod_t *tmp, tf_info_t *xinfo, tnode_t *troot)
943 {
944 	xmlNodePtr xroot;
945 
946 	if ((xroot = xmlDocGetRootElement(xinfo->tf_xdoc)) == NULL) {
947 		topo_dprintf(tmp->tm_hdl, TOPO_DBG_ERR,
948 		    "Couldn't get root xmlNode.\n");
949 		return (-1);
950 	}
951 	if ((xinfo->tf_rd = topo_xml_walk(tmp, xinfo, xroot, troot)) == NULL) {
952 		topo_dprintf(tmp->tm_hdl, TOPO_DBG_ERR,
953 		    "error within .xml topology: %s\n",
954 		    topo_strerror(topo_mod_errno(tmp)));
955 		return (-1);
956 	}
957 	return (0);
958 }
959 
960 /*
961  * Load an XML tree from filename and read it into a DOM parse tree.
962  */
963 static tf_info_t *
964 txml_file_parse(topo_mod_t *tmp,
965     int fd, const char *filenm, const char *escheme)
966 {
967 	xmlValidCtxtPtr vcp;
968 	xmlNodePtr cursor;
969 	xmlDocPtr document;
970 	xmlDtdPtr dtd = NULL;
971 	xmlChar *scheme = NULL;
972 	char *dtdpath = NULL;
973 	int readflags = 0;
974 	tf_info_t *r;
975 	int e, validate = 0;
976 
977 	/*
978 	 * Since topologies can XInclude other topologies, and libxml2
979 	 * doesn't do DTD-based validation with XInclude, by default
980 	 * we don't validate topology files.  One can force
981 	 * validation, though, by creating a TOPOXML_VALIDATE
982 	 * environment variable and creating a TOPO_DTD environment
983 	 * variable with the path to the DTD against which to validate.
984 	 */
985 	if (getenv("TOPOXML_VALIDATE") != NULL) {
986 		dtdpath = getenv("TOPO_DTD");
987 		if (dtdpath != NULL)
988 			xmlLoadExtDtdDefaultValue = 0;
989 		validate = 1;
990 	}
991 
992 	/*
993 	 * Splat warnings and errors related to parsing the topology
994 	 * file if the TOPOXML_PERROR environment variable exists.
995 	 */
996 	if (getenv("TOPOXML_PERROR") == NULL)
997 		readflags = XML_PARSE_NOERROR | XML_PARSE_NOWARNING;
998 
999 	if ((document = xmlReadFd(fd, filenm, NULL, readflags)) == NULL) {
1000 		topo_dprintf(tmp->tm_hdl, TOPO_DBG_ERR,
1001 		    "couldn't parse document.\n");
1002 		return (NULL);
1003 	}
1004 
1005 	/*
1006 	 * Verify that this is a document type we understand.
1007 	 */
1008 	if ((dtd = xmlGetIntSubset(document)) == NULL) {
1009 		topo_dprintf(tmp->tm_hdl, TOPO_DBG_ERR,
1010 		    "document has no DTD.\n");
1011 		return (NULL);
1012 	}
1013 
1014 	if (strcmp((const char *)dtd->SystemID, TOPO_DTD_PATH) != 0) {
1015 		topo_dprintf(tmp->tm_hdl, TOPO_DBG_ERR,
1016 		    "document DTD unknown; bad topology file\n");
1017 		return (NULL);
1018 	}
1019 
1020 	if ((cursor = xmlDocGetRootElement(document)) == NULL) {
1021 		topo_dprintf(tmp->tm_hdl, TOPO_DBG_ERR, "document is empty.\n");
1022 		xmlFreeDoc(document);
1023 		return (NULL);
1024 	}
1025 
1026 	/*
1027 	 * Make sure we're looking at a topology description in the
1028 	 * expected scheme.
1029 	 */
1030 	if (xmlStrcmp(cursor->name, (xmlChar *)Topology) != 0) {
1031 		topo_dprintf(tmp->tm_hdl, TOPO_DBG_ERR,
1032 		    "document is not a topology description.\n");
1033 		xmlFreeDoc(document);
1034 		return (NULL);
1035 	}
1036 	if ((scheme = xmlGetProp(cursor, (xmlChar *)Scheme)) == NULL) {
1037 		topo_dprintf(tmp->tm_hdl, TOPO_DBG_ERR,
1038 		    "topology lacks a scheme.\n");
1039 		(void) topo_mod_seterrno(tmp, ETOPO_PRSR_NOATTR);
1040 		xmlFreeDoc(document);
1041 		return (NULL);
1042 	}
1043 	if (xmlStrcmp(scheme, (xmlChar *)escheme) != 0) {
1044 		topo_dprintf(tmp->tm_hdl, TOPO_DBG_ERR,
1045 		    "topology in unrecognized scheme, %s, expecting %s\n",
1046 		    scheme, escheme);
1047 		(void) topo_mod_seterrno(tmp, ETOPO_PRSR_BADSCH);
1048 		xmlFree(scheme);
1049 		xmlFreeDoc(document);
1050 		return (NULL);
1051 	}
1052 
1053 	if (dtdpath != NULL) {
1054 		dtd = xmlParseDTD(NULL, (xmlChar *)dtdpath);
1055 		if (dtd == NULL) {
1056 			topo_dprintf(tmp->tm_hdl, TOPO_DBG_ERR,
1057 			    "Could not parse DTD \"%s\".\n",
1058 			    dtdpath);
1059 			return (NULL);
1060 		}
1061 
1062 		if (document->extSubset != NULL)
1063 			xmlFreeDtd(document->extSubset);
1064 
1065 		document->extSubset = dtd;
1066 	}
1067 
1068 	if (xmlXIncludeProcessFlags(document, XML_PARSE_XINCLUDE) == -1) {;
1069 		topo_dprintf(tmp->tm_hdl, TOPO_DBG_ERR,
1070 		    "couldn't handle XInclude statements in document\n");
1071 		return (NULL);
1072 	}
1073 
1074 	if (validate) {
1075 		if ((vcp = xmlNewValidCtxt()) == NULL) {
1076 			xmlFree(scheme);
1077 			scheme = NULL;
1078 			return (NULL);
1079 		}
1080 		vcp->warning = xmlParserValidityWarning;
1081 		vcp->error = xmlParserValidityError;
1082 
1083 		e = xmlValidateDocument(vcp, document);
1084 
1085 		xmlFreeValidCtxt(vcp);
1086 
1087 		if (e == 0)
1088 			topo_dprintf(tmp->tm_hdl, TOPO_DBG_ERR,
1089 			    "Document is not valid.\n");
1090 	}
1091 
1092 	if ((r = tf_info_new(tmp, document, scheme)) == NULL)
1093 		return (NULL);
1094 
1095 	xmlFree(scheme);
1096 	scheme = NULL;
1097 	return (r);
1098 }
1099 
1100 static int
1101 txml_file_open(topo_mod_t *mp, const char *filename)
1102 {
1103 	int rfd;
1104 	if ((rfd = open(filename, O_RDONLY)) < 0)
1105 		return (topo_mod_seterrno(mp, ETOPO_PRSR_NOENT));
1106 	return (rfd);
1107 }
1108 
1109 tf_info_t *
1110 topo_xml_read(topo_mod_t *tmp, const char *path, const char *escheme)
1111 {
1112 	int fd;
1113 	tf_info_t *tip;
1114 
1115 	if ((fd = txml_file_open(tmp, path)) < 0) {
1116 		return (NULL);
1117 	}
1118 	tip = txml_file_parse(tmp, fd, path, escheme);
1119 	(void) close(fd);
1120 	return (tip);
1121 }
1122