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 	}
706 
707 	if (topo_mod_enumerate(rd->rd_mod, rd->rd_pn, rd->rd_finfo->tf_scheme,
708 	    rd->rd_name, inst, inst, s == 1 ? &s : NULL) < 0)
709 		goto nodedone;
710 	ntn = topo_node_lookup(rd->rd_pn, rd->rd_name, inst);
711 	if (ntn == NULL)
712 		goto nodedone;
713 
714 	if ((newi = tf_idata_new(mp, inst, ntn)) == NULL) {
715 		topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
716 		    "tf_idata_new failed.\n");
717 		goto nodedone;
718 	}
719 	if (tf_idata_insert(&rd->rd_instances, newi) < 0) {
720 		topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
721 		    "tf_idata_insert failed.\n");
722 		goto nodedone;
723 	}
724 	if (pad_process(mp, rd->rd_finfo, nn, ntn, &newi->ti_pad) < 0)
725 		goto nodedone;
726 	rv = 0;
727 nodedone:
728 	topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "done with node %s.\n",
729 	    rd->rd_name);
730 	return (rv);
731 }
732 
733 static tf_edata_t *
734 enum_attributes_process(topo_mod_t *mp, xmlNodePtr en)
735 {
736 	tf_edata_t *einfo;
737 	uint64_t ui;
738 
739 	topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "enum attributes process\n");
740 	if ((einfo = topo_mod_zalloc(mp, sizeof (tf_edata_t))) == NULL) {
741 		(void) topo_mod_seterrno(mp, ETOPO_NOMEM);
742 		return (NULL);
743 	}
744 	einfo->te_name = (char *)xmlGetProp(en, (xmlChar *)Name);
745 	if (einfo->te_name == NULL) {
746 		topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
747 		    "Enumerator name attribute missing.\n");
748 		(void) topo_mod_seterrno(mp, ETOPO_PRSR_NOATTR);
749 		goto enodedone;
750 	}
751 	if (xmlattr_to_int(mp, en, Version, &ui) < 0)
752 		goto enodedone;
753 	einfo->te_vers = (int)ui;
754 	/*
755 	 * FMXXX must deal with name-stability and apply-methods (which are
756 	 * child xmlNodes)
757 	 */
758 	return (einfo);
759 
760 enodedone:
761 	if (einfo->te_name != NULL)
762 		xmlFree(einfo->te_name);
763 	return (NULL);
764 }
765 
766 static int
767 enum_run(topo_mod_t *mp, tf_rdata_t *rd)
768 {
769 	topo_hdl_t *thp = mp->tm_hdl;
770 	int e = -1;
771 
772 	/*
773 	 * Check if the enumerator module is already loaded.
774 	 * Module loading is single-threaded at this point so there's
775 	 * no need to worry about the module going away or bumping the
776 	 * ref count.
777 	 */
778 	if ((rd->rd_mod = topo_mod_lookup(thp, rd->rd_einfo->te_name,
779 	    0)) == NULL) {
780 		if ((rd->rd_mod = topo_mod_load(mp, rd->rd_einfo->te_name,
781 		    rd->rd_einfo->te_vers)) == NULL) {
782 			topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
783 			    "mod_load of %s failed: %s.\n",
784 			    rd->rd_einfo->te_name,
785 			    topo_strerror(topo_mod_errno(mp)));
786 			(void) topo_hdl_seterrno(thp, topo_mod_errno(mp));
787 			return (e);
788 		}
789 	}
790 	/*
791 	 * We're live, so let's enumerate.
792 	 */
793 	topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "enumerate request. (%s)\n",
794 	    rd->rd_einfo->te_name);
795 	e = topo_mod_enumerate(rd->rd_mod, rd->rd_pn, rd->rd_einfo->te_name,
796 	    rd->rd_name, rd->rd_min, rd->rd_max, NULL);
797 	topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "back from enumeration. %d\n",
798 	    e);
799 	if (e != 0) {
800 		topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
801 		    "Enumeration failed (%s)\n",
802 		    topo_strerror(topo_mod_errno(mp)));
803 		(void) topo_hdl_seterrno(thp, EMOD_PARTIAL_ENUM);
804 		return (topo_mod_seterrno(mp, EMOD_PARTIAL_ENUM));
805 	}
806 	return (e);
807 }
808 
809 int
810 topo_xml_range_process(topo_mod_t *mp, xmlNodePtr rn, tf_rdata_t *rd)
811 {
812 	/*
813 	 * The range may have several children xmlNodes, that may
814 	 * represent the enumeration method, property groups,
815 	 * dependents or nodes.
816 	 */
817 	xmlNodePtr cn;
818 	tnode_t *ct;
819 	int e;
820 
821 	topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "process %s range beneath %s\n",
822 	    rd->rd_name, topo_node_name(rd->rd_pn));
823 	e = topo_node_range_create(mp,
824 	    rd->rd_pn, rd->rd_name, rd->rd_min, rd->rd_max);
825 	if (e != 0) {
826 		topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
827 		    "Range create failed due to %s.\n",
828 		    topo_strerror(topo_mod_errno(mp)));
829 		return (-1);
830 	}
831 	for (cn = rn->xmlChildrenNode; cn != NULL; cn = cn->next)
832 		if (xmlStrcmp(cn->name, (xmlChar *)Enum_meth) == 0)
833 			break;
834 
835 	if (cn != NULL) {
836 		if ((rd->rd_einfo = enum_attributes_process(mp, cn)) == NULL)
837 			return (-1);
838 		if (enum_run(mp, rd) < 0) {
839 			/*
840 			 * Note the failure but continue on
841 			 */
842 			topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
843 			    "Enumeration failed.\n");
844 		}
845 	}
846 
847 	/* Now look for nodes, i.e., hard instances */
848 	for (cn = rn->xmlChildrenNode; cn != NULL; cn = cn->next) {
849 		if (xmlStrcmp(cn->name, (xmlChar *)Node) == 0)
850 			if (node_process(mp, cn, rd) < 0) {
851 				topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
852 				    "node processing failed: %s.\n",
853 				    topo_strerror(topo_mod_errno(mp)));
854 				return (topo_mod_seterrno(mp,
855 				    EMOD_PARTIAL_ENUM));
856 			}
857 	}
858 
859 	/* Property groups and Dependencies */
860 	ct = topo_child_first(rd->rd_pn);
861 	while (ct != NULL) {
862 		/* Only care about instances within the range */
863 		if (strcmp(topo_node_name(ct), rd->rd_name) != 0) {
864 			ct = topo_child_next(rd->rd_pn, ct);
865 			continue;
866 		}
867 		if (pad_process(mp, rd->rd_finfo, rn, ct, &rd->rd_pad) < 0)
868 			return (-1);
869 		ct = topo_child_next(rd->rd_pn, ct);
870 	}
871 	topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "end range process %s\n",
872 	    rd->rd_name);
873 	return (0);
874 }
875 
876 static tf_rdata_t *
877 topo_xml_walk(topo_mod_t *mp,
878     tf_info_t *xinfo, xmlNodePtr croot, tnode_t *troot)
879 {
880 	xmlNodePtr curr;
881 	tf_rdata_t *rr, *pr, *rdp;
882 
883 	/*
884 	 * What we're interested in are children xmlNodes of croot tagged
885 	 * as 'ranges', these define topology nodes may exist, and need
886 	 * to be verified.
887 	 */
888 	topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "topo_xml_walk\n");
889 	rr = pr = NULL;
890 	for (curr = croot->xmlChildrenNode; curr != NULL; curr = curr->next) {
891 		if (curr->name == NULL) {
892 			topo_dprintf(mp->tm_hdl, TOPO_DBG_XML,
893 			    "Ignoring nameless xmlnode\n");
894 			continue;
895 		}
896 		if (xmlStrcmp(curr->name, (xmlChar *)Range) != 0) {
897 			topo_dprintf(mp->tm_hdl, TOPO_DBG_XML,
898 			    "Ignoring non-range %s.\n", curr->name);
899 			continue;
900 		}
901 		if ((rdp = tf_rdata_new(mp, xinfo, curr, troot)) == NULL) {
902 			tf_rdata_free(mp, rr);
903 			return (NULL);
904 		}
905 		if (pr == NULL) {
906 			rr = pr = rdp;
907 		} else {
908 			pr->rd_next = rdp;
909 			pr = rdp;
910 		}
911 		rr->rd_cnt++;
912 	}
913 	return (rr);
914 }
915 
916 /*
917  *  Convert parsed xml topology description into topology nodes
918  */
919 int
920 topo_xml_enum(topo_mod_t *tmp, tf_info_t *xinfo, tnode_t *troot)
921 {
922 	xmlNodePtr xroot;
923 
924 	if ((xroot = xmlDocGetRootElement(xinfo->tf_xdoc)) == NULL) {
925 		topo_dprintf(tmp->tm_hdl, TOPO_DBG_ERR,
926 		    "Couldn't get root xmlNode.\n");
927 		return (-1);
928 	}
929 	if ((xinfo->tf_rd = topo_xml_walk(tmp, xinfo, xroot, troot)) == NULL) {
930 		topo_dprintf(tmp->tm_hdl, TOPO_DBG_ERR,
931 		    "error within .xml topology: %s\n",
932 		    topo_strerror(topo_mod_errno(tmp)));
933 		return (-1);
934 	}
935 	return (0);
936 }
937 
938 /*
939  * Load an XML tree from filename and read it into a DOM parse tree.
940  */
941 static tf_info_t *
942 txml_file_parse(topo_mod_t *tmp,
943     int fd, const char *filenm, const char *escheme)
944 {
945 	xmlValidCtxtPtr vcp;
946 	xmlNodePtr cursor;
947 	xmlDocPtr document;
948 	xmlDtdPtr dtd = NULL;
949 	xmlChar *scheme = NULL;
950 	char *dtdpath = NULL;
951 	int readflags = 0;
952 	tf_info_t *r;
953 	int e, validate = 0;
954 
955 	/*
956 	 * Since topologies can XInclude other topologies, and libxml2
957 	 * doesn't do DTD-based validation with XInclude, by default
958 	 * we don't validate topology files.  One can force
959 	 * validation, though, by creating a TOPOXML_VALIDATE
960 	 * environment variable and creating a TOPO_DTD environment
961 	 * variable with the path to the DTD against which to validate.
962 	 */
963 	if (getenv("TOPOXML_VALIDATE") != NULL) {
964 		dtdpath = getenv("TOPO_DTD");
965 		if (dtdpath != NULL)
966 			xmlLoadExtDtdDefaultValue = 0;
967 		validate = 1;
968 	}
969 
970 	/*
971 	 * Splat warnings and errors related to parsing the topology
972 	 * file if the TOPOXML_PERROR environment variable exists.
973 	 */
974 	if (getenv("TOPOXML_PERROR") == NULL)
975 		readflags = XML_PARSE_NOERROR | XML_PARSE_NOWARNING;
976 
977 	if ((document = xmlReadFd(fd, filenm, NULL, readflags)) == NULL) {
978 		topo_dprintf(tmp->tm_hdl, TOPO_DBG_ERR,
979 		    "couldn't parse document.\n");
980 		return (NULL);
981 	}
982 
983 	/*
984 	 * Verify that this is a document type we understand.
985 	 */
986 	if ((dtd = xmlGetIntSubset(document)) == NULL) {
987 		topo_dprintf(tmp->tm_hdl, TOPO_DBG_ERR,
988 		    "document has no DTD.\n");
989 		return (NULL);
990 	}
991 
992 	if (strcmp((const char *)dtd->SystemID, TOPO_DTD_PATH) != 0) {
993 		topo_dprintf(tmp->tm_hdl, TOPO_DBG_ERR,
994 		    "document DTD unknown; bad topology file\n");
995 		return (NULL);
996 	}
997 
998 	if ((cursor = xmlDocGetRootElement(document)) == NULL) {
999 		topo_dprintf(tmp->tm_hdl, TOPO_DBG_ERR, "document is empty.\n");
1000 		xmlFreeDoc(document);
1001 		return (NULL);
1002 	}
1003 
1004 	/*
1005 	 * Make sure we're looking at a topology description in the
1006 	 * expected scheme.
1007 	 */
1008 	if (xmlStrcmp(cursor->name, (xmlChar *)Topology) != 0) {
1009 		topo_dprintf(tmp->tm_hdl, TOPO_DBG_ERR,
1010 		    "document is not a topology description.\n");
1011 		xmlFreeDoc(document);
1012 		return (NULL);
1013 	}
1014 	if ((scheme = xmlGetProp(cursor, (xmlChar *)Scheme)) == NULL) {
1015 		topo_dprintf(tmp->tm_hdl, TOPO_DBG_ERR,
1016 		    "topology lacks a scheme.\n");
1017 		(void) topo_mod_seterrno(tmp, ETOPO_PRSR_NOATTR);
1018 		xmlFreeDoc(document);
1019 		return (NULL);
1020 	}
1021 	if (xmlStrcmp(scheme, (xmlChar *)escheme) != 0) {
1022 		topo_dprintf(tmp->tm_hdl, TOPO_DBG_ERR,
1023 		    "topology in unrecognized scheme, %s, expecting %s\n",
1024 		    scheme, escheme);
1025 		(void) topo_mod_seterrno(tmp, ETOPO_PRSR_BADSCH);
1026 		xmlFree(scheme);
1027 		xmlFreeDoc(document);
1028 		return (NULL);
1029 	}
1030 
1031 	if (dtdpath != NULL) {
1032 		dtd = xmlParseDTD(NULL, (xmlChar *)dtdpath);
1033 		if (dtd == NULL) {
1034 			topo_dprintf(tmp->tm_hdl, TOPO_DBG_ERR,
1035 			    "Could not parse DTD \"%s\".\n",
1036 			    dtdpath);
1037 			return (NULL);
1038 		}
1039 
1040 		if (document->extSubset != NULL)
1041 			xmlFreeDtd(document->extSubset);
1042 
1043 		document->extSubset = dtd;
1044 	}
1045 
1046 	if (xmlXIncludeProcessFlags(document, XML_PARSE_XINCLUDE) == -1) {;
1047 		topo_dprintf(tmp->tm_hdl, TOPO_DBG_ERR,
1048 		    "couldn't handle XInclude statements in document\n");
1049 		return (NULL);
1050 	}
1051 
1052 	if (validate) {
1053 		if ((vcp = xmlNewValidCtxt()) == NULL) {
1054 			xmlFree(scheme);
1055 			scheme = NULL;
1056 			return (NULL);
1057 		}
1058 		vcp->warning = xmlParserValidityWarning;
1059 		vcp->error = xmlParserValidityError;
1060 
1061 		e = xmlValidateDocument(vcp, document);
1062 
1063 		xmlFreeValidCtxt(vcp);
1064 
1065 		if (e == 0)
1066 			topo_dprintf(tmp->tm_hdl, TOPO_DBG_ERR,
1067 			    "Document is not valid.\n");
1068 	}
1069 
1070 	if ((r = tf_info_new(tmp, document, scheme)) == NULL)
1071 		return (NULL);
1072 
1073 	xmlFree(scheme);
1074 	scheme = NULL;
1075 	return (r);
1076 }
1077 
1078 static int
1079 txml_file_open(topo_mod_t *mp, const char *filename)
1080 {
1081 	int rfd;
1082 	if ((rfd = open(filename, O_RDONLY)) < 0)
1083 		return (topo_mod_seterrno(mp, ETOPO_PRSR_NOENT));
1084 	return (rfd);
1085 }
1086 
1087 tf_info_t *
1088 topo_xml_read(topo_mod_t *tmp, const char *path, const char *escheme)
1089 {
1090 	int fd;
1091 	tf_info_t *tip;
1092 
1093 	if ((fd = txml_file_open(tmp, path)) < 0) {
1094 		return (NULL);
1095 	}
1096 	tip = txml_file_parse(tmp, fd, path, escheme);
1097 	(void) close(fd);
1098 	return (tip);
1099 }
1100