xref: /titanic_51/usr/src/lib/fm/topo/libtopo/common/hc.c (revision 441d80aa4f613b6298fc8bd3151f4be02dbf84fc)
1 /*
2  *
3  * CDDL HEADER START
4  *
5  * The contents of this file are subject to the terms of the
6  * Common Development and Distribution License (the "License").
7  * You may not use this file except in compliance with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 
23 /*
24  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
25  * Use is subject to license terms.
26  */
27 
28 #pragma ident	"%Z%%M%	%I%	%E% SMI"
29 
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <string.h>
33 #include <errno.h>
34 #include <ctype.h>
35 #include <alloca.h>
36 #include <limits.h>
37 #include <fm/topo_mod.h>
38 #include <sys/param.h>
39 #include <sys/systeminfo.h>
40 #include <sys/fm/protocol.h>
41 #include <topo_parse.h>
42 
43 #include <hc_canon.h>
44 
45 #define	HC			"hc"
46 #define	HC_VERSION		TOPO_VERSION
47 
48 static int hc_enum(topo_mod_t *, tnode_t *, const char *, topo_instance_t,
49     topo_instance_t, void *);
50 static void hc_release(topo_mod_t *, tnode_t *);
51 static int hc_contains(topo_mod_t *, tnode_t *, topo_version_t, nvlist_t *,
52     nvlist_t **);
53 static int hc_present(topo_mod_t *, tnode_t *, topo_version_t, nvlist_t *,
54     nvlist_t **);
55 static int hc_unusable(topo_mod_t *, tnode_t *, topo_version_t, nvlist_t *,
56     nvlist_t **);
57 static int hc_fmri_nvl2str(topo_mod_t *, tnode_t *, topo_version_t,
58     nvlist_t *, nvlist_t **);
59 static int hc_fmri_str2nvl(topo_mod_t *, tnode_t *, topo_version_t,
60     nvlist_t *, nvlist_t **);
61 static int hc_compare(topo_mod_t *, tnode_t *, topo_version_t, nvlist_t *,
62     nvlist_t **);
63 static int hc_fmri_create_meth(topo_mod_t *, tnode_t *, topo_version_t,
64     nvlist_t *, nvlist_t **);
65 
66 static nvlist_t *hc_fmri_create(topo_mod_t *, nvlist_t *, int, const char *,
67     topo_instance_t inst, const nvlist_t *, const char *, const char *,
68     const char *);
69 
70 const topo_method_t hc_methods[] = {
71 	{ "hc_contains", "Hardware Component Contains", HC_VERSION,
72 	    TOPO_STABILITY_INTERNAL, hc_contains },
73 	{ "hc_present", "Hardware Component Present", HC_VERSION,
74 	    TOPO_STABILITY_INTERNAL, hc_present },
75 	{ "hc_unusable", "Hardware Component Unusable", HC_VERSION,
76 	    TOPO_STABILITY_INTERNAL, hc_unusable },
77 	{ TOPO_METH_NVL2STR, TOPO_METH_NVL2STR_DESC, TOPO_METH_NVL2STR_VERSION,
78 	    TOPO_STABILITY_INTERNAL, hc_fmri_nvl2str },
79 	{ TOPO_METH_STR2NVL, TOPO_METH_STR2NVL_DESC, TOPO_METH_STR2NVL_VERSION,
80 	    TOPO_STABILITY_INTERNAL, hc_fmri_str2nvl },
81 	{ TOPO_METH_COMPARE, TOPO_METH_COMPARE_DESC, TOPO_METH_COMPARE_VERSION,
82 	    TOPO_STABILITY_INTERNAL, hc_compare },
83 	{ TOPO_METH_FMRI, TOPO_METH_FMRI_DESC, TOPO_METH_FMRI_VERSION,
84 	    TOPO_STABILITY_INTERNAL, hc_fmri_create_meth },
85 	{ NULL }
86 };
87 
88 const topo_modinfo_t hc_info =
89 	{ HC, HC_VERSION, hc_enum, hc_release };
90 
91 void
92 hc_init(topo_mod_t *mp)
93 {
94 	/*
95 	 * Turn on module debugging output
96 	 */
97 	topo_mod_setdebug(mp, TOPO_DBG_ALL);
98 	topo_mod_dprintf(mp, "initializing hc builtin\n");
99 
100 	if (topo_mod_register(mp, &hc_info, NULL) != 0) {
101 		topo_mod_dprintf(mp, "failed to register hc: "
102 		    "%s\n", topo_mod_errmsg(mp));
103 	}
104 }
105 
106 void
107 hc_fini(topo_mod_t *mp)
108 {
109 	topo_mod_unregister(mp);
110 }
111 
112 /*ARGSUSED*/
113 int
114 hc_enum(topo_mod_t *mp, tnode_t *pnode, const char *name, topo_instance_t min,
115     topo_instance_t max, void *notused)
116 {
117 	nvlist_t *pfmri = NULL;
118 	nvlist_t *nvl;
119 	int err;
120 	/*
121 	 * Register root node methods
122 	 */
123 	if (strcmp(name, HC) == 0) {
124 		(void) topo_method_register(mp, pnode, hc_methods);
125 		return (0);
126 	}
127 	if (min != max) {
128 		topo_mod_dprintf(mp,
129 		    "Request to enumerate %s component with an "
130 		    "ambiguous instance number, min (%d) != max (%d).\n",
131 		    HC, min, max);
132 		return (topo_mod_seterrno(mp, EINVAL));
133 	}
134 
135 	(void) topo_node_resource(pnode, &pfmri, &err);
136 	nvl = hc_fmri_create(mp, pfmri, FM_HC_SCHEME_VERSION, name, min,
137 	    NULL, NULL, NULL, NULL);
138 	nvlist_free(pfmri);	/* callee ignores NULLs */
139 	if (nvl == NULL)
140 		return (-1);
141 
142 	if (topo_node_bind(mp, pnode, name, min, nvl, NULL) == NULL) {
143 		topo_mod_dprintf(mp, "topo_node_bind failed: %s\n",
144 		    topo_strerror(topo_mod_errno(mp)));
145 		nvlist_free(nvl);
146 		return (-1);
147 	}
148 	nvlist_free(nvl);
149 	return (0);
150 }
151 
152 /*ARGSUSED*/
153 static void
154 hc_release(topo_mod_t *mp, tnode_t *node)
155 {
156 	topo_method_unregister_all(mp, node);
157 }
158 
159 /*ARGSUSED*/
160 static int
161 hc_contains(topo_mod_t *mp, tnode_t *node, topo_version_t version,
162     nvlist_t *in, nvlist_t **out)
163 {
164 	return (topo_mod_seterrno(mp, EMOD_METHOD_NOTSUP));
165 }
166 
167 /*ARGSUSED*/
168 static int
169 hc_present(topo_mod_t *mp, tnode_t *node, topo_version_t version,
170     nvlist_t *in, nvlist_t **out)
171 {
172 	return (topo_mod_seterrno(mp, EMOD_METHOD_NOTSUP));
173 }
174 
175 /*ARGSUSED*/
176 static int
177 hc_unusable(topo_mod_t *mp, tnode_t *node, topo_version_t version,
178     nvlist_t *in, nvlist_t **out)
179 {
180 	return (topo_mod_seterrno(mp, EMOD_METHOD_NOTSUP));
181 }
182 
183 /*ARGSUSED*/
184 static int
185 hc_compare(topo_mod_t *mp, tnode_t *node, topo_version_t version,
186     nvlist_t *in, nvlist_t **out)
187 {
188 	uint8_t v1, v2;
189 	nvlist_t *nv1, *nv2;
190 	nvlist_t **hcp1, **hcp2;
191 	int err, i;
192 	uint_t nhcp1, nhcp2;
193 
194 	if (version > TOPO_METH_COMPARE_VERSION)
195 		return (topo_mod_seterrno(mp, EMOD_VER_NEW));
196 
197 	if (nvlist_lookup_nvlist(in, "nv1", &nv1) != 0 ||
198 	    nvlist_lookup_nvlist(in, "nv2", &nv2) != 0)
199 		return (topo_mod_seterrno(mp, EMOD_METHOD_INVAL));
200 
201 	if (nvlist_lookup_uint8(nv1, FM_VERSION, &v1) != 0 ||
202 	    nvlist_lookup_uint8(nv2, FM_VERSION, &v2) != 0 ||
203 	    v1 > FM_HC_SCHEME_VERSION || v2 > FM_HC_SCHEME_VERSION)
204 		return (topo_mod_seterrno(mp, EMOD_FMRI_VERSION));
205 
206 	err = nvlist_lookup_nvlist_array(nv1, FM_FMRI_HC_LIST, &hcp1, &nhcp1);
207 	err |= nvlist_lookup_nvlist_array(nv2, FM_FMRI_HC_LIST, &hcp2, &nhcp2);
208 	if (err != 0)
209 		return (topo_mod_seterrno(mp, EMOD_FMRI_NVL));
210 
211 	if (nhcp1 != nhcp2)
212 		return (0);
213 
214 	for (i = 0; i < nhcp1; i++) {
215 		char *nm1 = NULL;
216 		char *nm2 = NULL;
217 		char *id1 = NULL;
218 		char *id2 = NULL;
219 
220 		(void) nvlist_lookup_string(hcp1[i], FM_FMRI_HC_NAME, &nm1);
221 		(void) nvlist_lookup_string(hcp2[i], FM_FMRI_HC_NAME, &nm2);
222 		(void) nvlist_lookup_string(hcp1[i], FM_FMRI_HC_ID, &id1);
223 		(void) nvlist_lookup_string(hcp2[i], FM_FMRI_HC_ID, &id2);
224 		if (nm1 == NULL || nm2 == NULL || id1 == NULL || id2 == NULL)
225 			return (topo_mod_seterrno(mp, EMOD_FMRI_NVL));
226 
227 		if (strcmp(nm1, nm2) == 0 && strcmp(id1, id2) == 0)
228 			continue;
229 
230 		return (0);
231 	}
232 
233 	return (1);
234 }
235 
236 /*
237  * buf_append -- Append str to buf (if it's non-NULL).  Place prepend
238  * in buf in front of str and append behind it (if they're non-NULL).
239  * Continue to update size even if we run out of space to actually
240  * stuff characters in the buffer.
241  */
242 static void
243 buf_append(ssize_t *sz, char *buf, size_t buflen, char *str,
244     char *prepend, char *append)
245 {
246 	ssize_t left;
247 
248 	if (str == NULL)
249 		return;
250 
251 	if (buflen == 0 || (left = buflen - *sz) < 0)
252 		left = 0;
253 
254 	if (buf != NULL && left != 0)
255 		buf += *sz;
256 
257 	if (prepend == NULL && append == NULL)
258 		*sz += snprintf(buf, left, "%s", str);
259 	else if (append == NULL)
260 		*sz += snprintf(buf, left, "%s%s", prepend, str);
261 	else if (prepend == NULL)
262 		*sz += snprintf(buf, left, "%s%s", str, append);
263 	else
264 		*sz += snprintf(buf, left, "%s%s%s", prepend, str, append);
265 }
266 
267 static ssize_t
268 fmri_nvl2str(nvlist_t *nvl, char *buf, size_t buflen)
269 {
270 	nvlist_t **hcprs = NULL;
271 	nvlist_t *anvl = NULL;
272 	uint8_t version;
273 	ssize_t size = 0;
274 	uint_t hcnprs;
275 	char *achas = NULL;
276 	char *adom = NULL;
277 	char *aprod = NULL;
278 	char *asrvr = NULL;
279 	char *ahost = NULL;
280 	char *serial = NULL;
281 	char *part = NULL;
282 	char *root = NULL;
283 	char *rev = NULL;
284 	int more_auth = 0;
285 	int err, i;
286 
287 	if (nvlist_lookup_uint8(nvl, FM_VERSION, &version) != 0 ||
288 	    version > FM_HC_SCHEME_VERSION)
289 		return (-1);
290 
291 	/* Get authority, if present */
292 	err = nvlist_lookup_nvlist(nvl, FM_FMRI_AUTHORITY, &anvl);
293 	if (err != 0 && err != ENOENT)
294 		return (-1);
295 
296 	if ((err = nvlist_lookup_string(nvl, FM_FMRI_HC_ROOT, &root)) != 0)
297 		return (-1);
298 
299 	err = nvlist_lookup_nvlist_array(nvl, FM_FMRI_HC_LIST, &hcprs, &hcnprs);
300 	if (err != 0 || hcprs == NULL)
301 		return (-1);
302 
303 	if (anvl != NULL) {
304 		(void) nvlist_lookup_string(anvl,
305 		    FM_FMRI_AUTH_PRODUCT, &aprod);
306 		(void) nvlist_lookup_string(anvl,
307 		    FM_FMRI_AUTH_CHASSIS, &achas);
308 		(void) nvlist_lookup_string(anvl,
309 		    FM_FMRI_AUTH_DOMAIN, &adom);
310 		(void) nvlist_lookup_string(anvl,
311 		    FM_FMRI_AUTH_SERVER, &asrvr);
312 		(void) nvlist_lookup_string(anvl,
313 		    FM_FMRI_AUTH_HOST, &ahost);
314 		if (aprod != NULL)
315 			more_auth++;
316 		if (achas != NULL)
317 			more_auth++;
318 		if (adom != NULL)
319 			more_auth++;
320 		if (asrvr != NULL)
321 			more_auth++;
322 		if (ahost != NULL)
323 			more_auth++;
324 	}
325 
326 	(void) nvlist_lookup_string(nvl, FM_FMRI_HC_SERIAL_ID, &serial);
327 	(void) nvlist_lookup_string(nvl, FM_FMRI_HC_PART, &part);
328 	(void) nvlist_lookup_string(nvl, FM_FMRI_HC_REVISION, &rev);
329 
330 	/* hc:// */
331 	buf_append(&size, buf, buflen, FM_FMRI_SCHEME_HC, NULL, "://");
332 
333 	/* authority, if any */
334 	if (aprod != NULL)
335 		buf_append(&size, buf, buflen, aprod, FM_FMRI_AUTH_PRODUCT "=",
336 		    --more_auth > 0 ? "," : NULL);
337 	if (achas != NULL)
338 		buf_append(&size, buf, buflen, achas, FM_FMRI_AUTH_CHASSIS "=",
339 		    --more_auth > 0 ? "," : NULL);
340 	if (adom != NULL)
341 		buf_append(&size, buf, buflen, adom, FM_FMRI_AUTH_DOMAIN "=",
342 		    --more_auth > 0 ? "," : NULL);
343 	if (asrvr != NULL)
344 		buf_append(&size, buf, buflen, asrvr, FM_FMRI_AUTH_SERVER "=",
345 		    --more_auth > 0 ? "," : NULL);
346 	if (ahost != NULL)
347 		buf_append(&size, buf, buflen, ahost, FM_FMRI_AUTH_HOST "=",
348 		    NULL);
349 
350 	/* separating slash */
351 	if (serial != NULL || part != NULL || rev != NULL)
352 		buf_append(&size, buf, buflen, "/", NULL, NULL);
353 
354 	/* hardware-id part */
355 	buf_append(&size, buf, buflen, serial, ":" FM_FMRI_HC_SERIAL_ID "=",
356 	    NULL);
357 	buf_append(&size, buf, buflen, part, ":" FM_FMRI_HC_PART "=", NULL);
358 	buf_append(&size, buf, buflen, rev, ":" FM_FMRI_HC_REVISION "=", NULL);
359 
360 	/* separating slash */
361 	buf_append(&size, buf, buflen, "/", NULL, NULL);
362 
363 	/* hc-root */
364 	buf_append(&size, buf, buflen, root, NULL, NULL);
365 
366 	/* all the pairs */
367 	for (i = 0; i < hcnprs; i++) {
368 		char *nm = NULL;
369 		char *id = NULL;
370 
371 		if (i > 0)
372 			buf_append(&size, buf, buflen, "/", NULL, NULL);
373 		(void) nvlist_lookup_string(hcprs[i], FM_FMRI_HC_NAME, &nm);
374 		(void) nvlist_lookup_string(hcprs[i], FM_FMRI_HC_ID, &id);
375 		if (nm == NULL || id == NULL)
376 			return (0);
377 		buf_append(&size, buf, buflen, nm, NULL, "=");
378 		buf_append(&size, buf, buflen, id, NULL, NULL);
379 	}
380 
381 	return (size);
382 }
383 
384 /*ARGSUSED*/
385 static int
386 hc_fmri_nvl2str(topo_mod_t *mod, tnode_t *node, topo_version_t version,
387     nvlist_t *nvl, nvlist_t **out)
388 {
389 	ssize_t len;
390 	char *name = NULL;
391 	nvlist_t *fmristr;
392 
393 	if (version > TOPO_METH_NVL2STR_VERSION)
394 		return (topo_mod_seterrno(mod, EMOD_VER_NEW));
395 
396 	if ((len = fmri_nvl2str(nvl, NULL, 0)) == 0 ||
397 	    (name = topo_mod_alloc(mod, len + 1)) == NULL ||
398 	    fmri_nvl2str(nvl, name, len + 1) == 0) {
399 		if (name != NULL)
400 			topo_mod_free(mod, name, len + 1);
401 		return (topo_mod_seterrno(mod, EMOD_FMRI_NVL));
402 	}
403 
404 	if (topo_mod_nvalloc(mod, &fmristr, NV_UNIQUE_NAME) != 0)
405 		return (topo_mod_seterrno(mod, EMOD_FMRI_NVL));
406 	if (nvlist_add_string(fmristr, "fmri-string", name) != 0) {
407 		topo_mod_free(mod, name, len + 1);
408 		nvlist_free(fmristr);
409 		return (topo_mod_seterrno(mod, EMOD_FMRI_NVL));
410 	}
411 	topo_mod_free(mod, name, len + 1);
412 	*out = fmristr;
413 
414 	return (0);
415 }
416 
417 static nvlist_t *
418 hc_base_fmri_create(topo_mod_t *mod, const nvlist_t *auth, const char *part,
419     const char *rev, const char *serial)
420 {
421 	nvlist_t *fmri;
422 	int err = 0;
423 
424 	/*
425 	 * Create base HC nvlist
426 	 */
427 	if (topo_mod_nvalloc(mod, &fmri, NV_UNIQUE_NAME) != 0)
428 		return (NULL);
429 
430 	err = nvlist_add_uint8(fmri, FM_VERSION, FM_HC_SCHEME_VERSION);
431 	err |= nvlist_add_string(fmri, FM_FMRI_SCHEME, FM_FMRI_SCHEME_HC);
432 	err |= nvlist_add_string(fmri, FM_FMRI_HC_ROOT, "");
433 	if (err != 0) {
434 		nvlist_free(fmri);
435 		return (NULL);
436 	}
437 
438 	/*
439 	 * Add optional payload members
440 	 */
441 	if (serial != NULL)
442 		(void) nvlist_add_string(fmri, FM_FMRI_HC_SERIAL_ID, serial);
443 	if (part != NULL)
444 		(void) nvlist_add_string(fmri, FM_FMRI_HC_PART, part);
445 	if (rev != NULL)
446 		(void) nvlist_add_string(fmri, FM_FMRI_HC_REVISION, rev);
447 	if (auth != NULL)
448 		(void) nvlist_add_nvlist(fmri, FM_FMRI_AUTHORITY,
449 		    (nvlist_t *)auth);
450 
451 	return (fmri);
452 }
453 
454 static nvlist_t **
455 make_hc_pairs(topo_mod_t *mod, char *fromstr, int *num)
456 {
457 	nvlist_t **pa;
458 	char *starti, *startn, *endi, *endi2;
459 	char *ne, *ns;
460 	char *cname;
461 	char *find;
462 	char *cid;
463 	int nslashes = 0;
464 	int npairs = 0;
465 	int i, e;
466 
467 	/*
468 	 * Count equal signs and slashes to determine how many
469 	 * hc-pairs will be present in the final FMRI.  There should
470 	 * be at least as many slashes as equal signs.  There can be
471 	 * more, though if the string after an = includes them.
472 	 */
473 	find = fromstr;
474 	while ((ne = strchr(find, '=')) != NULL) {
475 		find = ne + 1;
476 		npairs++;
477 	}
478 
479 	find = fromstr;
480 	while ((ns = strchr(find, '/')) != NULL) {
481 		find = ns + 1;
482 		nslashes++;
483 	}
484 
485 	/*
486 	 * Do we appear to have a well-formed string version of the FMRI?
487 	 */
488 	if (nslashes < npairs || npairs == 0)
489 		return (NULL);
490 
491 	*num = npairs;
492 
493 	find = fromstr;
494 
495 	pa = topo_mod_alloc(mod, npairs * sizeof (nvlist_t *));
496 	/*
497 	 * We go through a pretty complicated procedure to find the
498 	 * name and id for each pair.  That's because, unfortunately,
499 	 * we have some ids that can have slashes within them.  So
500 	 * we can't just search for the next slash after the equal sign
501 	 * and decide that starts a new pair.  Instead we have to find
502 	 * an equal sign for the next pair and work our way back to the
503 	 * slash from there.
504 	 */
505 	for (i = 0; i < npairs; i++) {
506 		pa[i] = NULL;
507 		startn = strchr(find, '/');
508 		if (startn == NULL)
509 			break;
510 		startn++;
511 		starti = strchr(find, '=');
512 		if (starti == NULL)
513 			break;
514 		*starti = '\0';
515 		cname = topo_mod_strdup(mod, startn);
516 		*starti++ = '=';
517 		endi = strchr(starti, '=');
518 		if (endi != NULL) {
519 			*endi = '\0';
520 			endi2 = strrchr(starti, '/');
521 			if (endi2 == NULL)
522 				break;
523 			*endi = '=';
524 			*endi2 = '\0';
525 			cid = topo_mod_strdup(mod, starti);
526 			*endi2 = '/';
527 			find = endi2;
528 		} else {
529 			cid = topo_mod_strdup(mod, starti);
530 			find = starti + strlen(starti);
531 		}
532 		if ((e = topo_mod_nvalloc(mod, &pa[i], NV_UNIQUE_NAME)) != 0) {
533 			topo_mod_strfree(mod, cname);
534 			topo_mod_strfree(mod, cid);
535 			break;
536 		}
537 
538 		e = nvlist_add_string(pa[i], FM_FMRI_HC_NAME, cname);
539 		e |= nvlist_add_string(pa[i], FM_FMRI_HC_ID, cid);
540 
541 		topo_mod_strfree(mod, cname);
542 		topo_mod_strfree(mod, cid);
543 
544 		if (e != 0) {
545 			break;
546 		}
547 	}
548 	if (i < npairs) {
549 		while (i >= 0)
550 			if (pa[i--] != NULL)
551 				nvlist_free(pa[i + 1]);
552 		topo_mod_free(mod, pa, npairs * sizeof (nvlist_t *));
553 		return (NULL);
554 	}
555 
556 	return (pa);
557 }
558 
559 /*ARGSUSED*/
560 static int
561 hc_fmri_str2nvl(topo_mod_t *mod, tnode_t *node, topo_version_t version,
562     nvlist_t *in, nvlist_t **out)
563 {
564 	nvlist_t **pa = NULL;
565 	nvlist_t *nf = NULL;
566 	char *str, *copy;
567 	int npairs;
568 	int i, e;
569 
570 	if (version > TOPO_METH_STR2NVL_VERSION)
571 		return (topo_mod_seterrno(mod, EMOD_VER_NEW));
572 
573 	if (nvlist_lookup_string(in, "fmri-string", &str) != 0)
574 		return (topo_mod_seterrno(mod, EMOD_METHOD_INVAL));
575 
576 	/* We're expecting a string version of an hc scheme FMRI */
577 	if (strncmp(str, "hc:///", 6) != 0)
578 		return (topo_mod_seterrno(mod, EMOD_FMRI_MALFORM));
579 
580 	copy = topo_mod_strdup(mod, str + 5);
581 	if ((pa = make_hc_pairs(mod, copy, &npairs)) == NULL) {
582 		topo_mod_strfree(mod, copy);
583 		return (topo_mod_seterrno(mod, EMOD_FMRI_MALFORM));
584 	}
585 	topo_mod_strfree(mod, copy);
586 
587 	if ((nf = hc_base_fmri_create(mod, NULL, NULL, NULL, NULL)) == NULL)
588 		goto hcfmbail;
589 	if ((e = nvlist_add_uint32(nf, FM_FMRI_HC_LIST_SZ, npairs)) == 0)
590 		e = nvlist_add_nvlist_array(nf, FM_FMRI_HC_LIST, pa, npairs);
591 	if (e != 0) {
592 		topo_mod_dprintf(mod, "construction of new hc nvl failed");
593 		goto hcfmbail;
594 	}
595 	for (i = 0; i < npairs; i++)
596 		nvlist_free(pa[i]);
597 	topo_mod_free(mod, pa, npairs * sizeof (nvlist_t *));
598 	*out = nf;
599 
600 	return (0);
601 
602 hcfmbail:
603 	if (nf != NULL)
604 		nvlist_free(nf);
605 	for (i = 0; i < npairs; i++)
606 		nvlist_free(pa[i]);
607 	topo_mod_free(mod, pa, npairs * sizeof (nvlist_t *));
608 	return (topo_mod_seterrno(mod, EMOD_FMRI_MALFORM));
609 }
610 
611 static nvlist_t *
612 hc_list_create(topo_mod_t *mod, const char *name, char *inst)
613 {
614 	int err;
615 	nvlist_t *hc;
616 
617 	if (topo_mod_nvalloc(mod, &hc, NV_UNIQUE_NAME) != 0)
618 		return (NULL);
619 
620 	err = nvlist_add_string(hc, FM_FMRI_HC_NAME, name);
621 	err |= nvlist_add_string(hc, FM_FMRI_HC_ID, inst);
622 	if (err != 0) {
623 		nvlist_free(hc);
624 		return (NULL);
625 	}
626 
627 	return (hc);
628 }
629 
630 static nvlist_t *
631 hc_create_seterror(topo_mod_t *mod, nvlist_t **hcl, int n, nvlist_t *fmri,
632     int err)
633 {
634 	int i;
635 
636 	if (hcl != NULL) {
637 		for (i = 0; i < n + 1; ++i)
638 			nvlist_free(hcl[i]);
639 
640 		topo_mod_free(mod, hcl, sizeof (nvlist_t *) * (n + 1));
641 	}
642 
643 	nvlist_free(fmri);
644 
645 	(void) topo_mod_seterrno(mod, err);
646 
647 	topo_mod_dprintf(mod, "unable to create hc FMRI: %s\n",
648 	    topo_mod_errmsg(mod));
649 
650 	return (NULL);
651 }
652 
653 static int
654 hc_name_canonical(const char *name)
655 {
656 	int i;
657 	/*
658 	 * Only enumerate elements with correct canonical names
659 	 */
660 	for (i = 0; i < Hc_ncanon; i++) {
661 		if (strcmp(name, Hc_canon[i]) == 0)
662 			break;
663 	}
664 	if (i >= Hc_ncanon)
665 		return (0);
666 	else
667 		return (1);
668 }
669 
670 static nvlist_t *
671 hc_fmri_create(topo_mod_t *mod, nvlist_t *pfmri, int version, const char *name,
672     topo_instance_t inst, const nvlist_t *auth, const char *part,
673     const char *rev, const char *serial)
674 {
675 	int i;
676 	char str[21]; /* sizeof (UINT64_MAX) + '\0' */
677 	uint_t pelems = 0;
678 	nvlist_t **phcl = NULL;
679 	nvlist_t **hcl = NULL;
680 	nvlist_t *fmri = NULL;
681 
682 	if (version > FM_HC_SCHEME_VERSION)
683 		return (hc_create_seterror(mod,
684 		    hcl, pelems, fmri, EMOD_VER_OLD));
685 	else if (version < FM_HC_SCHEME_VERSION)
686 		return (hc_create_seterror(mod,
687 		    hcl, pelems, fmri, EMOD_VER_NEW));
688 
689 	/*
690 	 * Check that the requested name is in our canonical list
691 	 */
692 	if (hc_name_canonical(name) == 0)
693 		return (hc_create_seterror(mod,
694 		    hcl, pelems, fmri, EMOD_NONCANON));
695 	/*
696 	 * Copy the parent's HC_LIST
697 	 */
698 	if (pfmri != NULL) {
699 		if (nvlist_lookup_nvlist_array(pfmri, FM_FMRI_HC_LIST,
700 		    &phcl, &pelems) != 0)
701 			return (hc_create_seterror(mod,
702 			    hcl, pelems, fmri, EMOD_FMRI_MALFORM));
703 	}
704 
705 	hcl = topo_mod_zalloc(mod, sizeof (nvlist_t *) * (pelems + 1));
706 	if (hcl == NULL)
707 		return (hc_create_seterror(mod,  hcl, pelems, fmri,
708 		    EMOD_NOMEM));
709 
710 	for (i = 0; i < pelems; ++i)
711 		if (topo_mod_nvdup(mod, phcl[i], &hcl[i]) != 0)
712 			return (hc_create_seterror(mod,
713 			    hcl, pelems, fmri, EMOD_FMRI_NVL));
714 
715 	(void) snprintf(str, sizeof (str), "%d", inst);
716 	if ((hcl[i] = hc_list_create(mod, name, str)) == NULL)
717 		return (hc_create_seterror(mod,
718 		    hcl, pelems, fmri, EMOD_FMRI_NVL));
719 
720 	if ((fmri = hc_base_fmri_create(mod, auth, part, rev, serial)) == NULL)
721 		return (hc_create_seterror(mod,
722 		    hcl, pelems, fmri, EMOD_FMRI_NVL));
723 
724 	if (nvlist_add_nvlist_array(fmri, FM_FMRI_HC_LIST, hcl, pelems + 1)
725 	    != 0)
726 		return (hc_create_seterror(mod,
727 		    hcl, pelems, fmri, EMOD_FMRI_NVL));
728 
729 	if (hcl != NULL) {
730 		for (i = 0; i < pelems + 1; ++i) {
731 			if (hcl[i] != NULL)
732 				nvlist_free(hcl[i]);
733 		}
734 		topo_mod_free(mod, hcl, sizeof (nvlist_t *) * (pelems + 1));
735 	}
736 
737 	return (fmri);
738 }
739 
740 /*ARGSUSED*/
741 static int
742 hc_fmri_create_meth(topo_mod_t *mp, tnode_t *node, topo_version_t version,
743     nvlist_t *in, nvlist_t **out)
744 {
745 	nvlist_t *args, *pfmri;
746 	nvlist_t *auth;
747 	uint32_t inst;
748 	char *name, *serial, *rev, *part;
749 
750 	if (version > TOPO_METH_FMRI_VERSION)
751 		return (topo_mod_seterrno(mp, EMOD_VER_NEW));
752 
753 	/* First the must-have fields */
754 	if (nvlist_lookup_string(in, TOPO_METH_FMRI_ARG_NAME, &name) != 0)
755 		return (topo_mod_seterrno(mp, EMOD_METHOD_INVAL));
756 	if (nvlist_lookup_uint32(in, TOPO_METH_FMRI_ARG_INST, &inst) != 0)
757 		return (topo_mod_seterrno(mp, EMOD_METHOD_INVAL));
758 	if (nvlist_lookup_nvlist(in, TOPO_METH_FMRI_ARG_NVL, &args) != 0)
759 		return (topo_mod_seterrno(mp, EMOD_METHOD_INVAL));
760 	if (nvlist_lookup_nvlist(args, TOPO_METH_FMRI_ARG_PARENT, &pfmri) != 0)
761 		return (topo_mod_seterrno(mp, EMOD_METHOD_INVAL));
762 
763 	/* And then optional arguments */
764 	auth = NULL;
765 	serial = rev = part = NULL;
766 	(void) nvlist_lookup_nvlist(args, TOPO_METH_FMRI_ARG_AUTH, &auth);
767 	(void) nvlist_lookup_string(args, TOPO_METH_FMRI_ARG_PART, &part);
768 	(void) nvlist_lookup_string(args, TOPO_METH_FMRI_ARG_REV, &rev);
769 	(void) nvlist_lookup_string(args, TOPO_METH_FMRI_ARG_SER, &serial);
770 
771 	*out = hc_fmri_create(mp,
772 	    pfmri, version, name, inst, auth, part, rev, serial);
773 	if (*out == NULL)
774 		return (-1);
775 	return (0);
776 }
777