xref: /titanic_51/usr/src/lib/fm/topo/libtopo/common/hc.c (revision ff3124eff995e6cd8ebd8c6543648e0670920034)
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 2008 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 <fm/topo_hc.h>
39 #include <sys/param.h>
40 #include <sys/systeminfo.h>
41 #include <sys/fm/protocol.h>
42 #include <sys/stat.h>
43 #include <sys/systeminfo.h>
44 #include <sys/utsname.h>
45 
46 #include <topo_method.h>
47 #include <topo_module.h>
48 #include <topo_subr.h>
49 #include <topo_prop.h>
50 #include <topo_tree.h>
51 #include <hc.h>
52 
53 static int hc_enum(topo_mod_t *, tnode_t *, const char *, topo_instance_t,
54     topo_instance_t, void *, void *);
55 static void hc_release(topo_mod_t *, tnode_t *);
56 static int hc_fmri_nvl2str(topo_mod_t *, tnode_t *, topo_version_t,
57     nvlist_t *, nvlist_t **);
58 static int hc_fmri_str2nvl(topo_mod_t *, tnode_t *, topo_version_t,
59     nvlist_t *, nvlist_t **);
60 static int hc_compare(topo_mod_t *, tnode_t *, topo_version_t, nvlist_t *,
61     nvlist_t **);
62 static int hc_fmri_present(topo_mod_t *, tnode_t *, topo_version_t, nvlist_t *,
63     nvlist_t **);
64 static int hc_fmri_unusable(topo_mod_t *, tnode_t *, topo_version_t, nvlist_t *,
65     nvlist_t **);
66 static int hc_fmri_create_meth(topo_mod_t *, tnode_t *, topo_version_t,
67     nvlist_t *, nvlist_t **);
68 static int hc_fmri_prop_get(topo_mod_t *, tnode_t *, topo_version_t,
69     nvlist_t *, nvlist_t **);
70 static int hc_fmri_prop_set(topo_mod_t *, tnode_t *, topo_version_t,
71     nvlist_t *, nvlist_t **);
72 static int hc_fmri_pgrp_get(topo_mod_t *, tnode_t *, topo_version_t,
73     nvlist_t *, nvlist_t **);
74 static int hc_fmri_facility(topo_mod_t *, tnode_t *, topo_version_t,
75     nvlist_t *, nvlist_t **);
76 
77 static nvlist_t *hc_fmri_create(topo_mod_t *, nvlist_t *, int, const char *,
78     topo_instance_t inst, const nvlist_t *, const char *, const char *,
79     const char *);
80 
81 const topo_method_t hc_methods[] = {
82 	{ TOPO_METH_NVL2STR, TOPO_METH_NVL2STR_DESC, TOPO_METH_NVL2STR_VERSION,
83 	    TOPO_STABILITY_INTERNAL, hc_fmri_nvl2str },
84 	{ TOPO_METH_STR2NVL, TOPO_METH_STR2NVL_DESC, TOPO_METH_STR2NVL_VERSION,
85 	    TOPO_STABILITY_INTERNAL, hc_fmri_str2nvl },
86 	{ TOPO_METH_COMPARE, TOPO_METH_COMPARE_DESC, TOPO_METH_COMPARE_VERSION,
87 	    TOPO_STABILITY_INTERNAL, hc_compare },
88 	{ TOPO_METH_PRESENT, TOPO_METH_PRESENT_DESC, TOPO_METH_PRESENT_VERSION,
89 	    TOPO_STABILITY_INTERNAL, hc_fmri_present },
90 	{ TOPO_METH_UNUSABLE, TOPO_METH_UNUSABLE_DESC,
91 	    TOPO_METH_UNUSABLE_VERSION, TOPO_STABILITY_INTERNAL,
92 	    hc_fmri_unusable },
93 	{ TOPO_METH_FMRI, TOPO_METH_FMRI_DESC, TOPO_METH_FMRI_VERSION,
94 	    TOPO_STABILITY_INTERNAL, hc_fmri_create_meth },
95 	{ TOPO_METH_PROP_GET, TOPO_METH_PROP_GET_DESC,
96 	    TOPO_METH_PROP_GET_VERSION, TOPO_STABILITY_INTERNAL,
97 	    hc_fmri_prop_get },
98 	{ TOPO_METH_PROP_SET, TOPO_METH_PROP_SET_DESC,
99 	    TOPO_METH_PROP_SET_VERSION, TOPO_STABILITY_INTERNAL,
100 	    hc_fmri_prop_set },
101 	{ TOPO_METH_PGRP_GET, TOPO_METH_PGRP_GET_DESC,
102 	    TOPO_METH_PGRP_GET_VERSION, TOPO_STABILITY_INTERNAL,
103 	    hc_fmri_pgrp_get },
104 	{ TOPO_METH_FACILITY, TOPO_METH_FACILITY_DESC,
105 	    TOPO_METH_FACILITY_VERSION, TOPO_STABILITY_INTERNAL,
106 	    hc_fmri_facility },
107 	{ NULL }
108 };
109 
110 static const topo_modops_t hc_ops =
111 	{ hc_enum, hc_release };
112 static const topo_modinfo_t hc_info =
113 	{ HC, FM_FMRI_SCHEME_HC, HC_VERSION, &hc_ops };
114 
115 static const hcc_t hc_canon[] = {
116 	{ BANK, TOPO_STABILITY_PRIVATE },
117 	{ BAY, TOPO_STABILITY_PRIVATE },
118 	{ BLADE, TOPO_STABILITY_PRIVATE },
119 	{ BRANCH, TOPO_STABILITY_PRIVATE },
120 	{ CMP, TOPO_STABILITY_PRIVATE },
121 	{ CENTERPLANE, TOPO_STABILITY_PRIVATE },
122 	{ CHASSIS, TOPO_STABILITY_PRIVATE },
123 	{ CHIP, TOPO_STABILITY_PRIVATE },
124 	{ CHIP_SELECT, TOPO_STABILITY_PRIVATE },
125 	{ CORE, TOPO_STABILITY_PRIVATE },
126 	{ CONTROLLER, TOPO_STABILITY_PRIVATE },
127 	{ CPU, TOPO_STABILITY_PRIVATE },
128 	{ CPUBOARD, TOPO_STABILITY_PRIVATE },
129 	{ DIMM, TOPO_STABILITY_PRIVATE },
130 	{ DISK, TOPO_STABILITY_PRIVATE },
131 	{ DRAM, TOPO_STABILITY_PRIVATE },
132 	{ DRAMCHANNEL, TOPO_STABILITY_PRIVATE },
133 	{ FAN, TOPO_STABILITY_PRIVATE },
134 	{ FANMODULE, TOPO_STABILITY_PRIVATE },
135 	{ HOSTBRIDGE, TOPO_STABILITY_PRIVATE },
136 	{ INTERCONNECT, TOPO_STABILITY_PRIVATE },
137 	{ IOBOARD, TOPO_STABILITY_PRIVATE },
138 	{ MEMBOARD, TOPO_STABILITY_PRIVATE },
139 	{ MEMORYBUFFER, TOPO_STABILITY_PRIVATE },
140 	{ MEMORYCONTROL, TOPO_STABILITY_PRIVATE },
141 	{ MICROCORE, TOPO_STABILITY_PRIVATE },
142 	{ MOTHERBOARD, TOPO_STABILITY_PRIVATE },
143 	{ NIU, TOPO_STABILITY_PRIVATE },
144 	{ NIUFN, TOPO_STABILITY_PRIVATE },
145 	{ PCI_BUS, TOPO_STABILITY_PRIVATE },
146 	{ PCI_DEVICE, TOPO_STABILITY_PRIVATE },
147 	{ PCI_FUNCTION, TOPO_STABILITY_PRIVATE },
148 	{ PCIEX_BUS, TOPO_STABILITY_PRIVATE },
149 	{ PCIEX_DEVICE, TOPO_STABILITY_PRIVATE },
150 	{ PCIEX_FUNCTION, TOPO_STABILITY_PRIVATE },
151 	{ PCIEX_ROOT, TOPO_STABILITY_PRIVATE },
152 	{ PCIEX_SWUP, TOPO_STABILITY_PRIVATE },
153 	{ PCIEX_SWDWN, TOPO_STABILITY_PRIVATE },
154 	{ POWERMODULE, TOPO_STABILITY_PRIVATE },
155 	{ PSU, TOPO_STABILITY_PRIVATE },
156 	{ RANK, TOPO_STABILITY_PRIVATE },
157 	{ RISER, TOPO_STABILITY_PRIVATE },
158 	{ SHELF, TOPO_STABILITY_PRIVATE },
159 	{ SES_ENCLOSURE, TOPO_STABILITY_PRIVATE },
160 	{ SYSTEMBOARD, TOPO_STABILITY_PRIVATE },
161 	{ XAUI, TOPO_STABILITY_PRIVATE },
162 	{ XFP, TOPO_STABILITY_PRIVATE }
163 };
164 
165 static int hc_ncanon = sizeof (hc_canon) / sizeof (hcc_t);
166 
167 int
168 hc_init(topo_mod_t *mod, topo_version_t version)
169 {
170 	/*
171 	 * Turn on module debugging output
172 	 */
173 	if (getenv("TOPOHCDEBUG"))
174 		topo_mod_setdebug(mod);
175 
176 	topo_mod_dprintf(mod, "initializing hc builtin\n");
177 
178 	if (version != HC_VERSION)
179 		return (topo_mod_seterrno(mod, EMOD_VER_NEW));
180 
181 	if (topo_mod_register(mod, &hc_info, TOPO_VERSION) != 0) {
182 		topo_mod_dprintf(mod, "failed to register hc: "
183 		    "%s\n", topo_mod_errmsg(mod));
184 		return (-1); /* mod errno already set */
185 	}
186 
187 	return (0);
188 }
189 
190 void
191 hc_fini(topo_mod_t *mod)
192 {
193 	topo_mod_unregister(mod);
194 }
195 
196 
197 static const topo_pgroup_info_t sys_pgroup = {
198 	TOPO_PGROUP_SYSTEM,
199 	TOPO_STABILITY_PRIVATE,
200 	TOPO_STABILITY_PRIVATE,
201 	1
202 };
203 
204 static const topo_pgroup_info_t auth_pgroup = {
205 	FM_FMRI_AUTHORITY,
206 	TOPO_STABILITY_PRIVATE,
207 	TOPO_STABILITY_PRIVATE,
208 	1
209 };
210 
211 static void
212 hc_prop_set(tnode_t *node, nvlist_t *auth)
213 {
214 	int err;
215 	char isa[MAXNAMELEN];
216 	struct utsname uts;
217 	char *prod, *csn, *server;
218 
219 	if (auth == NULL)
220 		return;
221 
222 	if (topo_pgroup_create(node, &auth_pgroup, &err) != 0) {
223 		if (err != ETOPO_PROP_DEFD)
224 			return;
225 	}
226 
227 	/*
228 	 * Inherit if we can, it saves memory
229 	 */
230 	if ((topo_prop_inherit(node, FM_FMRI_AUTHORITY, FM_FMRI_AUTH_PRODUCT,
231 	    &err) != 0) && (err != ETOPO_PROP_DEFD)) {
232 		if (nvlist_lookup_string(auth, FM_FMRI_AUTH_PRODUCT, &prod)
233 		    == 0)
234 			(void) topo_prop_set_string(node, FM_FMRI_AUTHORITY,
235 			    FM_FMRI_AUTH_PRODUCT, TOPO_PROP_IMMUTABLE, prod,
236 			    &err);
237 	}
238 	if ((topo_prop_inherit(node, FM_FMRI_AUTHORITY, FM_FMRI_AUTH_CHASSIS,
239 	    &err) != 0) && (err != ETOPO_PROP_DEFD)) {
240 		if (nvlist_lookup_string(auth, FM_FMRI_AUTH_CHASSIS, &csn) == 0)
241 			(void) topo_prop_set_string(node, FM_FMRI_AUTHORITY,
242 			    FM_FMRI_AUTH_CHASSIS, TOPO_PROP_IMMUTABLE, csn,
243 			    &err);
244 	}
245 	if ((topo_prop_inherit(node, FM_FMRI_AUTHORITY, FM_FMRI_AUTH_SERVER,
246 	    &err) != 0) && (err != ETOPO_PROP_DEFD)) {
247 		if (nvlist_lookup_string(auth, FM_FMRI_AUTH_SERVER, &server)
248 		    == 0)
249 			(void) topo_prop_set_string(node, FM_FMRI_AUTHORITY,
250 			    FM_FMRI_AUTH_SERVER, TOPO_PROP_IMMUTABLE, server,
251 			    &err);
252 	}
253 
254 	if (topo_pgroup_create(node, &sys_pgroup, &err) != 0)
255 		return;
256 
257 	isa[0] = '\0';
258 	(void) sysinfo(SI_ARCHITECTURE, isa, sizeof (isa));
259 	(void) uname(&uts);
260 	(void) topo_prop_set_string(node, TOPO_PGROUP_SYSTEM, TOPO_PROP_ISA,
261 	    TOPO_PROP_IMMUTABLE, isa, &err);
262 	(void) topo_prop_set_string(node, TOPO_PGROUP_SYSTEM, TOPO_PROP_MACHINE,
263 	    TOPO_PROP_IMMUTABLE, uts.machine, &err);
264 }
265 
266 /*ARGSUSED*/
267 int
268 hc_enum(topo_mod_t *mod, tnode_t *pnode, const char *name, topo_instance_t min,
269     topo_instance_t max, void *notused1, void *notused2)
270 {
271 	nvlist_t *pfmri = NULL;
272 	nvlist_t *nvl;
273 	nvlist_t *auth;
274 	tnode_t *node;
275 	int err;
276 	/*
277 	 * Register root node methods
278 	 */
279 	if (strcmp(name, HC) == 0) {
280 		(void) topo_method_register(mod, pnode, hc_methods);
281 		return (0);
282 	}
283 	if (min != max) {
284 		topo_mod_dprintf(mod,
285 		    "Request to enumerate %s component with an "
286 		    "ambiguous instance number, min (%d) != max (%d).\n",
287 		    HC, min, max);
288 		return (topo_mod_seterrno(mod, EINVAL));
289 	}
290 
291 	(void) topo_node_resource(pnode, &pfmri, &err);
292 	auth = topo_mod_auth(mod, pnode);
293 	nvl = hc_fmri_create(mod, pfmri, FM_HC_SCHEME_VERSION, name, min,
294 	    auth, NULL, NULL, NULL);
295 	nvlist_free(pfmri);	/* callee ignores NULLs */
296 	if (nvl == NULL) {
297 		nvlist_free(auth);
298 		return (-1);
299 	}
300 
301 	if ((node = topo_node_bind(mod, pnode, name, min, nvl)) == NULL) {
302 		topo_mod_dprintf(mod, "topo_node_bind failed: %s\n",
303 		    topo_strerror(topo_mod_errno(mod)));
304 		nvlist_free(auth);
305 		nvlist_free(nvl);
306 		return (-1);
307 	}
308 
309 	/*
310 	 * Set FRU for the motherboard node
311 	 */
312 	if (strcmp(name, MOTHERBOARD) == 0)
313 		(void) topo_node_fru_set(node, nvl, 0, &err);
314 
315 	hc_prop_set(node, auth);
316 	nvlist_free(nvl);
317 	nvlist_free(auth);
318 
319 	return (0);
320 }
321 
322 /*ARGSUSED*/
323 static void
324 hc_release(topo_mod_t *mp, tnode_t *node)
325 {
326 	topo_method_unregister_all(mp, node);
327 }
328 
329 static int
330 fmri_compare(topo_mod_t *mod, nvlist_t *nv1, nvlist_t *nv2)
331 {
332 	uint8_t v1, v2;
333 	nvlist_t **hcp1, **hcp2;
334 	nvlist_t *f1 = NULL, *f2 = NULL;
335 	int err, i;
336 	uint_t nhcp1, nhcp2;
337 	char *f1str, *f2str;
338 
339 	if (nvlist_lookup_uint8(nv1, FM_VERSION, &v1) != 0 ||
340 	    nvlist_lookup_uint8(nv2, FM_VERSION, &v2) != 0 ||
341 	    v1 > FM_HC_SCHEME_VERSION || v2 > FM_HC_SCHEME_VERSION)
342 		return (topo_mod_seterrno(mod, EMOD_FMRI_VERSION));
343 
344 	err = nvlist_lookup_nvlist_array(nv1, FM_FMRI_HC_LIST, &hcp1, &nhcp1);
345 	err |= nvlist_lookup_nvlist_array(nv2, FM_FMRI_HC_LIST, &hcp2, &nhcp2);
346 	if (err != 0)
347 		return (topo_mod_seterrno(mod, EMOD_FMRI_NVL));
348 
349 	if (nhcp1 != nhcp2)
350 		return (0);
351 
352 	for (i = 0; i < nhcp1; i++) {
353 		char *nm1 = NULL;
354 		char *nm2 = NULL;
355 		char *id1 = NULL;
356 		char *id2 = NULL;
357 
358 		(void) nvlist_lookup_string(hcp1[i], FM_FMRI_HC_NAME, &nm1);
359 		(void) nvlist_lookup_string(hcp2[i], FM_FMRI_HC_NAME, &nm2);
360 		(void) nvlist_lookup_string(hcp1[i], FM_FMRI_HC_ID, &id1);
361 		(void) nvlist_lookup_string(hcp2[i], FM_FMRI_HC_ID, &id2);
362 		if (nm1 == NULL || nm2 == NULL || id1 == NULL || id2 == NULL)
363 			return (topo_mod_seterrno(mod, EMOD_FMRI_NVL));
364 
365 		if (strcmp(nm1, nm2) == 0 && strcmp(id1, id2) == 0)
366 			continue;
367 
368 		return (0);
369 	}
370 
371 	/*
372 	 * Finally, check if the FMRI's represent a facility node.  If so, then
373 	 * verify that the facilty type ("sensor"|"indicator") and facility
374 	 * name match.
375 	 */
376 	(void) nvlist_lookup_nvlist(nv1, FM_FMRI_FACILITY, &f1);
377 	(void) nvlist_lookup_nvlist(nv2, FM_FMRI_FACILITY, &f2);
378 
379 	if (f1 == NULL && f2 == NULL)
380 		return (1);
381 	else if (f1 == NULL || f2 == NULL)
382 		return (0);
383 
384 	if (nvlist_lookup_string(f1, FM_FMRI_FACILITY_NAME, &f1str) == 0 &&
385 	    nvlist_lookup_string(f2, FM_FMRI_FACILITY_NAME, &f2str) == 0 &&
386 	    strcmp(f1str, f2str) == 0 &&
387 	    nvlist_lookup_string(f1, FM_FMRI_FACILITY_TYPE, &f1str) == 0 &&
388 	    nvlist_lookup_string(f2, FM_FMRI_FACILITY_TYPE, &f2str) == 0 &&
389 	    strcmp(f1str, f2str) == 0) {
390 		return (1);
391 	}
392 	return (0);
393 }
394 
395 /*ARGSUSED*/
396 static int
397 hc_compare(topo_mod_t *mod, tnode_t *node, topo_version_t version,
398     nvlist_t *in, nvlist_t **out)
399 {
400 	int ret;
401 	uint32_t compare;
402 	nvlist_t *nv1, *nv2;
403 
404 	if (version > TOPO_METH_COMPARE_VERSION)
405 		return (topo_mod_seterrno(mod, EMOD_VER_NEW));
406 
407 	if (nvlist_lookup_nvlist(in, TOPO_METH_FMRI_ARG_NV1, &nv1) != 0 ||
408 	    nvlist_lookup_nvlist(in, TOPO_METH_FMRI_ARG_NV2, &nv2) != 0)
409 		return (topo_mod_seterrno(mod, EMOD_METHOD_INVAL));
410 
411 	ret = fmri_compare(mod, nv1, nv2);
412 	if (ret < 0)
413 		return (-1);
414 
415 	compare = ret;
416 	if (topo_mod_nvalloc(mod, out, NV_UNIQUE_NAME) == 0) {
417 		if (nvlist_add_uint32(*out, TOPO_METH_COMPARE_RET,
418 		    compare) == 0)
419 			return (0);
420 		else
421 			nvlist_free(*out);
422 	}
423 
424 	return (-1);
425 }
426 
427 static ssize_t
428 fmri_nvl2str(nvlist_t *nvl, char *buf, size_t buflen)
429 {
430 	nvlist_t **hcprs = NULL;
431 	nvlist_t *anvl = NULL;
432 	nvlist_t *fnvl;
433 	uint8_t version;
434 	ssize_t size = 0;
435 	uint_t hcnprs;
436 	char *achas = NULL;
437 	char *adom = NULL;
438 	char *aprod = NULL;
439 	char *asrvr = NULL;
440 	char *ahost = NULL;
441 	char *serial = NULL;
442 	char *part = NULL;
443 	char *root = NULL;
444 	char *rev = NULL;
445 	char *fname = NULL, *ftype = NULL;
446 	int more_auth = 0;
447 	int err, i;
448 
449 	if (nvlist_lookup_uint8(nvl, FM_VERSION, &version) != 0 ||
450 	    version > FM_HC_SCHEME_VERSION)
451 		return (-1);
452 
453 	/* Get authority, if present */
454 	err = nvlist_lookup_nvlist(nvl, FM_FMRI_AUTHORITY, &anvl);
455 	if (err != 0 && err != ENOENT)
456 		return (-1);
457 
458 	if ((err = nvlist_lookup_string(nvl, FM_FMRI_HC_ROOT, &root)) != 0)
459 		return (-1);
460 
461 	err = nvlist_lookup_nvlist_array(nvl, FM_FMRI_HC_LIST, &hcprs, &hcnprs);
462 	if (err != 0 || hcprs == NULL)
463 		return (-1);
464 
465 	if (anvl != NULL) {
466 		(void) nvlist_lookup_string(anvl,
467 		    FM_FMRI_AUTH_PRODUCT, &aprod);
468 		(void) nvlist_lookup_string(anvl,
469 		    FM_FMRI_AUTH_CHASSIS, &achas);
470 		(void) nvlist_lookup_string(anvl,
471 		    FM_FMRI_AUTH_DOMAIN, &adom);
472 		(void) nvlist_lookup_string(anvl,
473 		    FM_FMRI_AUTH_SERVER, &asrvr);
474 		(void) nvlist_lookup_string(anvl,
475 		    FM_FMRI_AUTH_HOST, &ahost);
476 		if (aprod != NULL)
477 			more_auth++;
478 		if (achas != NULL)
479 			more_auth++;
480 		if (adom != NULL)
481 			more_auth++;
482 		if (asrvr != NULL)
483 			more_auth++;
484 		if (ahost != NULL)
485 			more_auth++;
486 	}
487 
488 	(void) nvlist_lookup_string(nvl, FM_FMRI_HC_SERIAL_ID, &serial);
489 	(void) nvlist_lookup_string(nvl, FM_FMRI_HC_PART, &part);
490 	(void) nvlist_lookup_string(nvl, FM_FMRI_HC_REVISION, &rev);
491 
492 	/* hc:// */
493 	topo_fmristr_build(&size, buf, buflen, FM_FMRI_SCHEME_HC, NULL, "://");
494 
495 	/* authority, if any */
496 	if (aprod != NULL)
497 		topo_fmristr_build(&size,
498 		    buf, buflen, aprod, ":" FM_FMRI_AUTH_PRODUCT "=", NULL);
499 	if (achas != NULL)
500 		topo_fmristr_build(&size,
501 		    buf, buflen, achas, ":" FM_FMRI_AUTH_CHASSIS "=", NULL);
502 	if (adom != NULL)
503 		topo_fmristr_build(&size,
504 		    buf, buflen, adom, ":" FM_FMRI_AUTH_DOMAIN "=", NULL);
505 	if (asrvr != NULL)
506 		topo_fmristr_build(&size,
507 		    buf, buflen, asrvr, ":" FM_FMRI_AUTH_SERVER "=", NULL);
508 	if (ahost != NULL)
509 		topo_fmristr_build(&size,
510 		    buf, buflen, ahost, ":" FM_FMRI_AUTH_HOST "=", NULL);
511 
512 	/* hardware-id part */
513 	topo_fmristr_build(&size,
514 	    buf, buflen, serial, ":" FM_FMRI_HC_SERIAL_ID "=", NULL);
515 	topo_fmristr_build(&size,
516 	    buf, buflen, part, ":" FM_FMRI_HC_PART "=", NULL);
517 	topo_fmristr_build(&size,
518 	    buf, buflen, rev, ":" FM_FMRI_HC_REVISION "=", NULL);
519 
520 	/* separating slash */
521 	topo_fmristr_build(&size, buf, buflen, "/", NULL, NULL);
522 
523 	/* hc-root */
524 	topo_fmristr_build(&size, buf, buflen, root, NULL, NULL);
525 
526 	/* all the pairs */
527 	for (i = 0; i < hcnprs; i++) {
528 		char *nm = NULL;
529 		char *id = NULL;
530 
531 		if (i > 0)
532 			topo_fmristr_build(&size,
533 			    buf, buflen, "/", NULL, NULL);
534 		(void) nvlist_lookup_string(hcprs[i], FM_FMRI_HC_NAME, &nm);
535 		(void) nvlist_lookup_string(hcprs[i], FM_FMRI_HC_ID, &id);
536 		if (nm == NULL || id == NULL)
537 			return (0);
538 		topo_fmristr_build(&size, buf, buflen, nm, NULL, "=");
539 		topo_fmristr_build(&size, buf, buflen, id, NULL, NULL);
540 	}
541 
542 	/*
543 	 * If the nvlist represents a facility node, then we append the
544 	 * facility type and name to the end of the string representation using
545 	 * the format below:
546 	 *
547 	 * ?<ftype>=<fname>
548 	 */
549 	if (nvlist_lookup_nvlist(nvl, FM_FMRI_FACILITY, &fnvl) == 0) {
550 		if (nvlist_lookup_string(fnvl, FM_FMRI_FACILITY_NAME,
551 		    &fname) != 0 || nvlist_lookup_string(fnvl,
552 		    FM_FMRI_FACILITY_TYPE, &ftype) != 0)
553 			return (0);
554 		topo_fmristr_build(&size, buf, buflen, "?", NULL, NULL);
555 		topo_fmristr_build(&size, buf, buflen, "=", ftype, fname);
556 	}
557 
558 	return (size);
559 }
560 
561 /*ARGSUSED*/
562 static int
563 hc_fmri_nvl2str(topo_mod_t *mod, tnode_t *node, topo_version_t version,
564     nvlist_t *nvl, nvlist_t **out)
565 {
566 	ssize_t len;
567 	char *name = NULL;
568 	nvlist_t *fmristr;
569 
570 	if (version > TOPO_METH_NVL2STR_VERSION)
571 		return (topo_mod_seterrno(mod, EMOD_VER_NEW));
572 
573 	if ((len = fmri_nvl2str(nvl, NULL, 0)) == 0 ||
574 	    (name = topo_mod_alloc(mod, len + 1)) == NULL ||
575 	    fmri_nvl2str(nvl, name, len + 1) == 0) {
576 		if (name != NULL)
577 			topo_mod_free(mod, name, len + 1);
578 		return (topo_mod_seterrno(mod, EMOD_FMRI_NVL));
579 	}
580 
581 	if (topo_mod_nvalloc(mod, &fmristr, NV_UNIQUE_NAME) != 0) {
582 		topo_mod_free(mod, name, len + 1);
583 		return (topo_mod_seterrno(mod, EMOD_FMRI_NVL));
584 	}
585 	if (nvlist_add_string(fmristr, "fmri-string", name) != 0) {
586 		topo_mod_free(mod, name, len + 1);
587 		nvlist_free(fmristr);
588 		return (topo_mod_seterrno(mod, EMOD_FMRI_NVL));
589 	}
590 	topo_mod_free(mod, name, len + 1);
591 	*out = fmristr;
592 
593 	return (0);
594 }
595 
596 static nvlist_t *
597 hc_base_fmri_create(topo_mod_t *mod, const nvlist_t *auth, const char *part,
598     const char *rev, const char *serial)
599 {
600 	nvlist_t *fmri;
601 	int err = 0;
602 
603 	/*
604 	 * Create base HC nvlist
605 	 */
606 	if (topo_mod_nvalloc(mod, &fmri, NV_UNIQUE_NAME) != 0)
607 		return (NULL);
608 
609 	err = nvlist_add_uint8(fmri, FM_VERSION, FM_HC_SCHEME_VERSION);
610 	err |= nvlist_add_string(fmri, FM_FMRI_SCHEME, FM_FMRI_SCHEME_HC);
611 	err |= nvlist_add_string(fmri, FM_FMRI_HC_ROOT, "");
612 	if (err != 0) {
613 		nvlist_free(fmri);
614 		return (NULL);
615 	}
616 
617 	/*
618 	 * Add optional payload members
619 	 */
620 	if (serial != NULL)
621 		(void) nvlist_add_string(fmri, FM_FMRI_HC_SERIAL_ID, serial);
622 	if (part != NULL)
623 		(void) nvlist_add_string(fmri, FM_FMRI_HC_PART, part);
624 	if (rev != NULL)
625 		(void) nvlist_add_string(fmri, FM_FMRI_HC_REVISION, rev);
626 	if (auth != NULL)
627 		(void) nvlist_add_nvlist(fmri, FM_FMRI_AUTHORITY,
628 		    (nvlist_t *)auth);
629 
630 	return (fmri);
631 }
632 
633 static nvlist_t **
634 make_hc_pairs(topo_mod_t *mod, char *fmri, int *num)
635 {
636 	nvlist_t **pa;
637 	char *hc, *fromstr;
638 	char *starti, *startn, *endi, *endi2;
639 	char *ne, *ns;
640 	char *cname = NULL;
641 	char *find;
642 	char *cid = NULL;
643 	int nslashes = 0;
644 	int npairs = 0;
645 	int i, hclen;
646 
647 	if ((hc = topo_mod_strdup(mod, fmri + 5)) == NULL)
648 		return (NULL);
649 
650 	hclen = strlen(hc) + 1;
651 
652 	/*
653 	 * Count equal signs and slashes to determine how many
654 	 * hc-pairs will be present in the final FMRI.  There should
655 	 * be at least as many slashes as equal signs.  There can be
656 	 * more, though if the string after an = includes them.
657 	 */
658 	if ((fromstr = strchr(hc, '/')) == NULL)
659 		return (NULL);
660 
661 	find = fromstr;
662 	while ((ne = strchr(find, '=')) != NULL) {
663 		find = ne + 1;
664 		npairs++;
665 	}
666 
667 	find = fromstr;
668 	while ((ns = strchr(find, '/')) != NULL) {
669 		find = ns + 1;
670 		nslashes++;
671 	}
672 
673 	/*
674 	 * Do we appear to have a well-formed string version of the FMRI?
675 	 */
676 	if (nslashes < npairs || npairs == 0) {
677 		topo_mod_free(mod, hc, hclen);
678 		return (NULL);
679 	}
680 
681 	*num = npairs;
682 
683 	find = fromstr;
684 
685 	if ((pa = topo_mod_zalloc(mod, npairs * sizeof (nvlist_t *))) == NULL) {
686 		topo_mod_free(mod, hc, hclen);
687 		return (NULL);
688 	}
689 
690 	/*
691 	 * We go through a pretty complicated procedure to find the
692 	 * name and id for each pair.  That's because, unfortunately,
693 	 * we have some ids that can have slashes within them.  So
694 	 * we can't just search for the next slash after the equal sign
695 	 * and decide that starts a new pair.  Instead we have to find
696 	 * an equal sign for the next pair and work our way back to the
697 	 * slash from there.
698 	 */
699 	for (i = 0; i < npairs; i++) {
700 		startn = strchr(find, '/');
701 		if (startn == NULL)
702 			break;
703 		startn++;
704 		starti = strchr(find, '=');
705 		if (starti == NULL)
706 			break;
707 		*starti = '\0';
708 		if ((cname = topo_mod_strdup(mod, startn)) == NULL)
709 			break;
710 		*starti++ = '=';
711 		endi = strchr(starti, '=');
712 		if (endi != NULL) {
713 			*endi = '\0';
714 			endi2 = strrchr(starti, '/');
715 			if (endi2 == NULL)
716 				break;
717 			*endi = '=';
718 			*endi2 = '\0';
719 			if ((cid = topo_mod_strdup(mod, starti)) == NULL)
720 				break;
721 			*endi2 = '/';
722 			find = endi2;
723 		} else {
724 			if ((cid = topo_mod_strdup(mod, starti)) == NULL)
725 				break;
726 			find = starti + strlen(starti);
727 		}
728 		if (topo_mod_nvalloc(mod, &pa[i], NV_UNIQUE_NAME) < 0)
729 			break;
730 
731 		if (nvlist_add_string(pa[i], FM_FMRI_HC_NAME, cname) ||
732 		    nvlist_add_string(pa[i], FM_FMRI_HC_ID, cid))
733 			break;
734 
735 		topo_mod_strfree(mod, cname);
736 		topo_mod_strfree(mod, cid);
737 		cname = NULL;
738 		cid = NULL;
739 	}
740 
741 	topo_mod_strfree(mod, cname);
742 	topo_mod_strfree(mod, cid);
743 
744 	if (i < npairs) {
745 		for (i = 0; i < npairs; i++)
746 			nvlist_free(pa[i]);
747 		topo_mod_free(mod, pa, npairs * sizeof (nvlist_t *));
748 		topo_mod_free(mod, hc, hclen);
749 		return (NULL);
750 	}
751 
752 	topo_mod_free(mod, hc, hclen);
753 
754 	return (pa);
755 }
756 
757 int
758 make_hc_auth(topo_mod_t *mod, char *fmri, char **serial, char **part,
759 char **rev, nvlist_t **auth)
760 {
761 	char *starti, *startn, *endi, *copy;
762 	char *aname = NULL, *aid = NULL, *fs;
763 	nvlist_t *na = NULL;
764 	size_t len;
765 
766 	if ((copy = topo_mod_strdup(mod, fmri + 5)) == NULL)
767 		return (-1);
768 
769 	len = strlen(copy);
770 
771 	/*
772 	 * Make sure there are a valid authority members
773 	 */
774 	startn = strchr(copy, ':');
775 	fs = strchr(copy, '/');
776 
777 	if (startn == NULL || fs == NULL) {
778 		topo_mod_strfree(mod, copy);
779 		return (0);
780 	}
781 
782 	/*
783 	 * The first colon we encounter must occur before the
784 	 * first slash
785 	 */
786 	if (startn > fs)
787 		goto hcabail;
788 
789 	do {
790 		if (++startn >= copy + len)
791 			break;
792 
793 		if ((starti = strchr(startn, '=')) == NULL)
794 			goto hcabail;
795 
796 		*starti = '\0';
797 		if (++starti > copy + len)
798 			goto hcabail;
799 
800 		if ((aname = topo_mod_strdup(mod, startn)) == NULL)
801 			goto hcabail;
802 
803 		startn = endi = strchr(starti, ':');
804 		if (endi == NULL)
805 			if ((endi = strchr(starti, '/')) == NULL)
806 				break;
807 
808 		*endi = '\0';
809 		if ((aid = topo_mod_strdup(mod, starti)) == NULL)
810 			goto hcabail;
811 
812 		/*
813 		 * Return possible serial, part and revision
814 		 */
815 		if (strcmp(aname, FM_FMRI_HC_SERIAL_ID) == 0) {
816 			*serial = topo_mod_strdup(mod, aid);
817 		} else if (strcmp(aname, FM_FMRI_HC_PART) == 0) {
818 			*part = topo_mod_strdup(mod, aid);
819 		} else if (strcmp(aname, FM_FMRI_HC_REVISION) == 0) {
820 			*rev = topo_mod_strdup(mod, aid);
821 		} else {
822 			if (na == NULL) {
823 				if (topo_mod_nvalloc(mod, &na,
824 				    NV_UNIQUE_NAME) == 0) {
825 					(void) nvlist_add_string(na, aname,
826 					    aid);
827 				}
828 			} else {
829 				(void) nvlist_add_string(na, aname, aid);
830 			}
831 		}
832 		topo_mod_strfree(mod, aname);
833 		topo_mod_strfree(mod, aid);
834 		aname = aid = NULL;
835 
836 	} while (startn != NULL);
837 
838 	*auth = na;
839 
840 	topo_mod_free(mod, copy, len + 1);
841 	return (0);
842 
843 hcabail:
844 	topo_mod_free(mod, copy, len + 1);
845 	topo_mod_strfree(mod, aname);
846 	topo_mod_strfree(mod, aid);
847 	nvlist_free(na);
848 	return (-1);
849 }
850 
851 
852 /*
853  * This function creates an nvlist to represent the facility portion of an
854  * hc-scheme node, given a string representation of the fmri.  This is called by
855  * hc_fmri_str2nvl.  If the string does not contain a facility component
856  * (e.g. ?<ftype>=<fname>) then it bails early and returns 0.
857  *
858  * On failure it returns -1 and sets the topo mod errno
859  */
860 int
861 make_facility(topo_mod_t *mod, char *str, nvlist_t **nvl)
862 {
863 	char *fac, *copy, *fname, *ftype;
864 	nvlist_t *nf = NULL;
865 	size_t len;
866 
867 	if ((fac = strchr(str, '?')) == NULL)
868 		return (0);
869 
870 	++fac;
871 	if ((copy = topo_mod_strdup(mod, fac)) == NULL)
872 		return (topo_mod_seterrno(mod, EMOD_NOMEM));
873 
874 	fac = copy;
875 	len = strlen(fac);
876 
877 	if ((fname = strchr(fac, '=')) == NULL) {
878 		topo_mod_free(mod, copy, len + 1);
879 		return (topo_mod_seterrno(mod, EMOD_FMRI_MALFORM));
880 	}
881 
882 	fname[0] = '\0';
883 	++fname;
884 	ftype = fac;
885 
886 	if (topo_mod_nvalloc(mod, &nf, NV_UNIQUE_NAME) != 0) {
887 		topo_mod_free(mod, copy, len + 1);
888 		return (topo_mod_seterrno(mod, EMOD_NOMEM));
889 	}
890 
891 	if (nvlist_add_string(nf, FM_FMRI_FACILITY_NAME, fname) != 0 ||
892 	    nvlist_add_string(nf, FM_FMRI_FACILITY_TYPE, ftype) != 0) {
893 		topo_mod_free(mod, copy, len + 1);
894 		return (topo_mod_seterrno(mod, EMOD_FMRI_NVL));
895 	}
896 
897 	topo_mod_free(mod, copy, len + 1);
898 
899 	*nvl = nf;
900 
901 	return (0);
902 }
903 
904 /*ARGSUSED*/
905 static int
906 hc_fmri_str2nvl(topo_mod_t *mod, tnode_t *node, topo_version_t version,
907     nvlist_t *in, nvlist_t **out)
908 {
909 	nvlist_t **pa = NULL;
910 	nvlist_t *nf = NULL;
911 	nvlist_t *auth = NULL;
912 	nvlist_t *fac = NULL;
913 	char *str;
914 	char *serial = NULL, *part = NULL, *rev = NULL;
915 	int npairs;
916 	int i, e;
917 
918 	if (version > TOPO_METH_STR2NVL_VERSION)
919 		return (topo_mod_seterrno(mod, EMOD_VER_NEW));
920 
921 	if (nvlist_lookup_string(in, "fmri-string", &str) != 0)
922 		return (topo_mod_seterrno(mod, EMOD_METHOD_INVAL));
923 
924 	/* We're expecting a string version of an hc scheme FMRI */
925 	if (strncmp(str, "hc://", 5) != 0)
926 		return (topo_mod_seterrno(mod, EMOD_FMRI_MALFORM));
927 
928 	if ((pa = make_hc_pairs(mod, str, &npairs)) == NULL)
929 		return (topo_mod_seterrno(mod, EMOD_FMRI_MALFORM));
930 
931 	if (make_hc_auth(mod, str, &serial, &part, &rev, &auth) < 0)
932 		goto hcfmbail;
933 
934 	if ((nf = hc_base_fmri_create(mod, auth, part, rev, serial)) == NULL)
935 		goto hcfmbail;
936 	if ((e = nvlist_add_uint32(nf, FM_FMRI_HC_LIST_SZ, npairs)) == 0)
937 		e = nvlist_add_nvlist_array(nf, FM_FMRI_HC_LIST, pa, npairs);
938 	if (e != 0) {
939 		topo_mod_dprintf(mod, "construction of new hc nvl failed");
940 		goto hcfmbail;
941 	}
942 
943 	/*
944 	 * Clean-up
945 	 */
946 	for (i = 0; i < npairs; i++)
947 		nvlist_free(pa[i]);
948 	topo_mod_free(mod, pa, npairs * sizeof (nvlist_t *));
949 	topo_mod_strfree(mod, serial);
950 	topo_mod_strfree(mod, part);
951 	topo_mod_strfree(mod, rev);
952 	nvlist_free(auth);
953 
954 	if (make_facility(mod, str, &fac) == -1)
955 		goto hcfmbail;
956 
957 	if (fac != NULL) {
958 		if (nvlist_add_nvlist(nf, FM_FMRI_FACILITY, fac) != 0)
959 			goto hcfmbail;
960 	}
961 
962 	*out = nf;
963 
964 	return (0);
965 
966 hcfmbail:
967 	if (nf != NULL)
968 		nvlist_free(nf);
969 	for (i = 0; i < npairs; i++)
970 		nvlist_free(pa[i]);
971 	topo_mod_free(mod, pa, npairs * sizeof (nvlist_t *));
972 
973 	topo_mod_strfree(mod, serial);
974 	topo_mod_strfree(mod, part);
975 	topo_mod_strfree(mod, rev);
976 	nvlist_free(auth);
977 	nvlist_free(nf);
978 	return (topo_mod_seterrno(mod, EMOD_FMRI_MALFORM));
979 }
980 
981 static nvlist_t *
982 hc_list_create(topo_mod_t *mod, const char *name, char *inst)
983 {
984 	int err;
985 	nvlist_t *hc;
986 
987 	if (topo_mod_nvalloc(mod, &hc, NV_UNIQUE_NAME) != 0)
988 		return (NULL);
989 
990 	err = nvlist_add_string(hc, FM_FMRI_HC_NAME, name);
991 	err |= nvlist_add_string(hc, FM_FMRI_HC_ID, inst);
992 	if (err != 0) {
993 		nvlist_free(hc);
994 		return (NULL);
995 	}
996 
997 	return (hc);
998 }
999 
1000 static nvlist_t *
1001 hc_create_seterror(topo_mod_t *mod, nvlist_t **hcl, int n, nvlist_t *fmri,
1002     int err)
1003 {
1004 	int i;
1005 
1006 	if (hcl != NULL) {
1007 		for (i = 0; i < n + 1; ++i)
1008 			nvlist_free(hcl[i]);
1009 
1010 		topo_mod_free(mod, hcl, sizeof (nvlist_t *) * (n + 1));
1011 	}
1012 
1013 	nvlist_free(fmri);
1014 
1015 	(void) topo_mod_seterrno(mod, err);
1016 
1017 	topo_mod_dprintf(mod, "unable to create hc FMRI: %s\n",
1018 	    topo_mod_errmsg(mod));
1019 
1020 	return (NULL);
1021 }
1022 
1023 static int
1024 hc_name_canonical(topo_mod_t *mod, const char *name)
1025 {
1026 	int i;
1027 
1028 	if (getenv("NOHCCHECK") != NULL)
1029 		return (1);
1030 
1031 	/*
1032 	 * Only enumerate elements with correct canonical names
1033 	 */
1034 	for (i = 0; i < hc_ncanon; i++) {
1035 		if (strcmp(name, hc_canon[i].hcc_name) == 0)
1036 			break;
1037 	}
1038 	if (i >= hc_ncanon) {
1039 		topo_mod_dprintf(mod, "non-canonical name %s\n",
1040 		    name);
1041 		return (0);
1042 	} else {
1043 		return (1);
1044 	}
1045 }
1046 
1047 static nvlist_t *
1048 hc_fmri_create(topo_mod_t *mod, nvlist_t *pfmri, int version, const char *name,
1049     topo_instance_t inst, const nvlist_t *auth, const char *part,
1050     const char *rev, const char *serial)
1051 {
1052 	int i;
1053 	char str[21]; /* sizeof (UINT64_MAX) + '\0' */
1054 	uint_t pelems = 0;
1055 	nvlist_t **phcl = NULL;
1056 	nvlist_t **hcl = NULL;
1057 	nvlist_t *fmri = NULL;
1058 
1059 	if (version > FM_HC_SCHEME_VERSION)
1060 		return (hc_create_seterror(mod,
1061 		    hcl, pelems, fmri, EMOD_VER_OLD));
1062 	else if (version < FM_HC_SCHEME_VERSION)
1063 		return (hc_create_seterror(mod,
1064 		    hcl, pelems, fmri, EMOD_VER_NEW));
1065 
1066 	/*
1067 	 * Check that the requested name is in our canonical list
1068 	 */
1069 	if (hc_name_canonical(mod, name) == 0)
1070 		return (hc_create_seterror(mod,
1071 		    hcl, pelems, fmri, EMOD_NONCANON));
1072 	/*
1073 	 * Copy the parent's HC_LIST
1074 	 */
1075 	if (pfmri != NULL) {
1076 		if (nvlist_lookup_nvlist_array(pfmri, FM_FMRI_HC_LIST,
1077 		    &phcl, &pelems) != 0)
1078 			return (hc_create_seterror(mod,
1079 			    hcl, pelems, fmri, EMOD_FMRI_MALFORM));
1080 	}
1081 
1082 	hcl = topo_mod_zalloc(mod, sizeof (nvlist_t *) * (pelems + 1));
1083 	if (hcl == NULL)
1084 		return (hc_create_seterror(mod,  hcl, pelems, fmri,
1085 		    EMOD_NOMEM));
1086 
1087 	for (i = 0; i < pelems; ++i)
1088 		if (topo_mod_nvdup(mod, phcl[i], &hcl[i]) != 0)
1089 			return (hc_create_seterror(mod,
1090 			    hcl, pelems, fmri, EMOD_FMRI_NVL));
1091 
1092 	(void) snprintf(str, sizeof (str), "%d", inst);
1093 	if ((hcl[i] = hc_list_create(mod, name, str)) == NULL)
1094 		return (hc_create_seterror(mod,
1095 		    hcl, pelems, fmri, EMOD_FMRI_NVL));
1096 
1097 	if ((fmri = hc_base_fmri_create(mod, auth, part, rev, serial)) == NULL)
1098 		return (hc_create_seterror(mod,
1099 		    hcl, pelems, fmri, EMOD_FMRI_NVL));
1100 
1101 	if (nvlist_add_nvlist_array(fmri, FM_FMRI_HC_LIST, hcl, pelems + 1)
1102 	    != 0)
1103 		return (hc_create_seterror(mod,
1104 		    hcl, pelems, fmri, EMOD_FMRI_NVL));
1105 
1106 	if (hcl != NULL) {
1107 		for (i = 0; i < pelems + 1; ++i) {
1108 			if (hcl[i] != NULL)
1109 				nvlist_free(hcl[i]);
1110 		}
1111 		topo_mod_free(mod, hcl, sizeof (nvlist_t *) * (pelems + 1));
1112 	}
1113 
1114 	return (fmri);
1115 }
1116 
1117 /*ARGSUSED*/
1118 static int
1119 hc_fmri_create_meth(topo_mod_t *mod, tnode_t *node, topo_version_t version,
1120     nvlist_t *in, nvlist_t **out)
1121 {
1122 	int ret;
1123 	nvlist_t *args, *pfmri = NULL;
1124 	nvlist_t *auth;
1125 	uint32_t inst;
1126 	char *name, *serial, *rev, *part;
1127 
1128 	if (version > TOPO_METH_FMRI_VERSION)
1129 		return (topo_mod_seterrno(mod, EMOD_VER_NEW));
1130 
1131 	/* First the must-have fields */
1132 	if (nvlist_lookup_string(in, TOPO_METH_FMRI_ARG_NAME, &name) != 0)
1133 		return (topo_mod_seterrno(mod, EMOD_METHOD_INVAL));
1134 	if (nvlist_lookup_uint32(in, TOPO_METH_FMRI_ARG_INST, &inst) != 0)
1135 		return (topo_mod_seterrno(mod, EMOD_METHOD_INVAL));
1136 
1137 	/*
1138 	 * args is optional
1139 	 */
1140 	pfmri = NULL;
1141 	auth = NULL;
1142 	serial = rev = part = NULL;
1143 	if ((ret = nvlist_lookup_nvlist(in, TOPO_METH_FMRI_ARG_NVL, &args))
1144 	    != 0) {
1145 		if (ret != ENOENT)
1146 			return (topo_mod_seterrno(mod, EMOD_METHOD_INVAL));
1147 	} else {
1148 
1149 		/* And then optional arguments */
1150 		(void) nvlist_lookup_nvlist(args, TOPO_METH_FMRI_ARG_PARENT,
1151 		    &pfmri);
1152 		(void) nvlist_lookup_nvlist(args, TOPO_METH_FMRI_ARG_AUTH,
1153 		    &auth);
1154 		(void) nvlist_lookup_string(args, TOPO_METH_FMRI_ARG_PART,
1155 		    &part);
1156 		(void) nvlist_lookup_string(args, TOPO_METH_FMRI_ARG_REV, &rev);
1157 		(void) nvlist_lookup_string(args, TOPO_METH_FMRI_ARG_SER,
1158 		    &serial);
1159 	}
1160 
1161 	*out = hc_fmri_create(mod, pfmri, version, name, inst, auth, part,
1162 	    rev, serial);
1163 	if (*out == NULL)
1164 		return (-1);
1165 	return (0);
1166 }
1167 
1168 struct hc_walk {
1169 	topo_mod_walk_cb_t hcw_cb;
1170 	void *hcw_priv;
1171 	topo_walk_t *hcw_wp;
1172 	nvlist_t **hcw_list;
1173 	nvlist_t *hcw_fmri;
1174 	nvlist_t *hcw_fac;
1175 	uint_t hcw_index;
1176 	uint_t hcw_end;
1177 };
1178 
1179 /*
1180  * Returns true if the given node is beneath the specified FMRI.  This uses
1181  * the TOPO_METH_CONTAINS method, because some enumerators (such as external
1182  * enclosures) may want to do a comparison based on chassis WWN instead of the
1183  * instance ID.  If this comparison function fails or is not supported, then we
1184  * fall back to a direct name/instance comparison.
1185  */
1186 static int
1187 hc_match(topo_mod_t *mod, tnode_t *node, nvlist_t *fmri, const char *name,
1188     topo_instance_t inst, boolean_t *result)
1189 {
1190 	nvlist_t *rsrc;
1191 	nvlist_t *arg, *nvl;
1192 	uint32_t match = 0;
1193 	int err;
1194 
1195 	if (topo_node_resource(node, &rsrc, &err) != 0)
1196 		return (-1);
1197 
1198 	if (topo_mod_nvalloc(mod, &arg, NV_UNIQUE_NAME) != 0 ||
1199 	    nvlist_add_nvlist(arg, TOPO_METH_FMRI_ARG_FMRI,
1200 	    rsrc) != 0 ||
1201 	    nvlist_add_nvlist(arg, TOPO_METH_FMRI_ARG_SUBFMRI,
1202 	    fmri) != 0) {
1203 		nvlist_free(rsrc);
1204 		(void) topo_mod_seterrno(mod, EMOD_NOMEM);
1205 		return (-1);
1206 	}
1207 
1208 	nvlist_free(rsrc);
1209 
1210 	if (topo_method_invoke(node, TOPO_METH_CONTAINS,
1211 	    TOPO_METH_CONTAINS_VERSION, arg, &nvl, &err) != 0) {
1212 		nvlist_free(arg);
1213 		if (err == ETOPO_METHOD_NOTSUP) {
1214 			match = (strcmp(name,
1215 			    topo_node_name(node)) == 0 &&
1216 			    inst == topo_node_instance(node));
1217 		} else {
1218 			return (-1);
1219 		}
1220 	} else {
1221 		nvlist_free(arg);
1222 		if (nvlist_lookup_uint32(nvl, TOPO_METH_CONTAINS_RET,
1223 		    &match) != 0) {
1224 			nvlist_free(nvl);
1225 			(void) topo_mod_seterrno(mod, EMOD_NVL_INVAL);
1226 			return (-1);
1227 		}
1228 		nvlist_free(nvl);
1229 	}
1230 
1231 	*result = (match != 0);
1232 	return (0);
1233 }
1234 
1235 /*
1236  * Ideally, we should just be able to call topo_walk_bysibling().  But that
1237  * code assumes that the name/instance pair will match, so we need to
1238  * explicitly iterate over children of the parent looking for a matching value.
1239  */
1240 static int
1241 hc_walk_sibling(topo_mod_t *mod, tnode_t *node, struct hc_walk *hwp,
1242     const char *name, topo_instance_t inst)
1243 {
1244 	tnode_t *pnp = topo_node_parent(node);
1245 	topo_walk_t *wp = hwp->hcw_wp;
1246 	tnode_t *np;
1247 	boolean_t matched;
1248 	int status;
1249 
1250 	for (np = topo_child_first(pnp); np != NULL;
1251 	    np = topo_child_next(pnp, np)) {
1252 		topo_node_hold(np);
1253 		if (hc_match(mod, np, hwp->hcw_fmri, name, inst,
1254 		    &matched) == 0 && matched) {
1255 			wp->tw_node = np;
1256 			if (wp->tw_mod != NULL)
1257 				status = wp->tw_cb(mod, np, hwp);
1258 			else
1259 				status = wp->tw_cb(wp->tw_thp, np, hwp);
1260 			topo_node_rele(np);
1261 			wp->tw_node = node;
1262 			return (status);
1263 		}
1264 
1265 		topo_node_rele(np);
1266 	}
1267 
1268 	return (TOPO_WALK_TERMINATE);
1269 }
1270 
1271 /*
1272  * Generic walker for the hc-scheme topo tree.  This function uses the
1273  * hierachical nature of the hc-scheme to efficiently step through
1274  * the topo hc tree.  Node lookups are done by topo_walk_byid() and
1275  * topo_walk_bysibling()  at each component level to avoid unnecessary
1276  * traversal of the tree.  hc_walker() never returns TOPO_WALK_NEXT, so
1277  * whether TOPO_WALK_CHILD or TOPO_WALK_SIBLING is specified by
1278  * topo_walk_step() doesn't affect the traversal.
1279  */
1280 static int
1281 hc_walker(topo_mod_t *mod, tnode_t *node, void *pdata)
1282 {
1283 	int i, err;
1284 	struct hc_walk *hwp = (struct hc_walk *)pdata;
1285 	char *name, *id;
1286 	char *fname, *ftype;
1287 	topo_instance_t inst;
1288 	boolean_t match;
1289 
1290 	i = hwp->hcw_index;
1291 	if (i > hwp->hcw_end) {
1292 		if (hwp->hcw_fac != NULL) {
1293 			if ((err = hwp->hcw_cb(mod, node, hwp->hcw_priv))
1294 			    != 0) {
1295 				(void) topo_mod_seterrno(mod, err);
1296 				topo_mod_dprintf(mod, "hc_walker: callback "
1297 				    "failed: %s\n ", topo_mod_errmsg(mod));
1298 				return (TOPO_WALK_ERR);
1299 			}
1300 			topo_mod_dprintf(mod, "hc_walker: callback "
1301 			    "complete: terminate walk\n");
1302 			return (TOPO_WALK_TERMINATE);
1303 		} else {
1304 			topo_mod_dprintf(mod, "hc_walker: node not found\n");
1305 			return (TOPO_WALK_TERMINATE);
1306 		}
1307 	}
1308 
1309 	err = nvlist_lookup_string(hwp->hcw_list[i], FM_FMRI_HC_NAME, &name);
1310 	err |= nvlist_lookup_string(hwp->hcw_list[i], FM_FMRI_HC_ID, &id);
1311 
1312 	if (err != 0) {
1313 		(void) topo_mod_seterrno(mod, EMOD_NVL_INVAL);
1314 		return (TOPO_WALK_ERR);
1315 	}
1316 
1317 	inst = atoi(id);
1318 
1319 	/*
1320 	 * Check to see if our node matches the requested FMRI.  If it doesn't
1321 	 * (because the enumerator determines matching based on something other
1322 	 * than name/instance, or because we're at the first level below the
1323 	 * root), then iterate over siblings to find the matching node.
1324 	 */
1325 	if (hc_match(mod, node, hwp->hcw_fmri, name, inst, &match) != 0)
1326 		return (TOPO_WALK_ERR);
1327 
1328 	if (!match)
1329 		return (hc_walk_sibling(mod, node, hwp, name, inst));
1330 
1331 	topo_mod_dprintf(mod, "hc_walker: walking node:%s=%d for hc:"
1332 	    "%s=%d at %d, end at %d \n", topo_node_name(node),
1333 	    topo_node_instance(node), name, inst, i, hwp->hcw_end);
1334 
1335 	if (i == hwp->hcw_end) {
1336 
1337 		/*
1338 		 * We are at the end of the hc-list.  Now, check for
1339 		 * a facility leaf and walk one more time.
1340 		 */
1341 		if (hwp->hcw_fac != NULL) {
1342 			err = nvlist_lookup_string(hwp->hcw_fac,
1343 			    FM_FMRI_FACILITY_NAME, &fname);
1344 			err |= nvlist_lookup_string(hwp->hcw_fac,
1345 			    FM_FMRI_FACILITY_TYPE, &ftype);
1346 			if (err != 0) {
1347 				(void) topo_mod_seterrno(mod, EMOD_NVL_INVAL);
1348 				return (TOPO_WALK_ERR);
1349 			}
1350 			hwp->hcw_index++;
1351 			topo_mod_dprintf(mod, "hc_walker: walk to facility "
1352 			    "node:%s=%s\n", fname, ftype);
1353 			return (topo_walk_byid(hwp->hcw_wp, fname, 0));
1354 		}
1355 
1356 		/*
1357 		 * Otherwise, this is the node we're looking for.
1358 		 */
1359 		if ((err = hwp->hcw_cb(mod, node, hwp->hcw_priv)) != 0) {
1360 			(void) topo_mod_seterrno(mod, err);
1361 			topo_mod_dprintf(mod, "hc_walker: callback "
1362 			    "failed: %s\n ", topo_mod_errmsg(mod));
1363 			return (TOPO_WALK_ERR);
1364 		} else {
1365 			topo_mod_dprintf(mod, "hc_walker: callback "
1366 			    "complete: terminate walk\n");
1367 			return (TOPO_WALK_TERMINATE);
1368 		}
1369 	}
1370 
1371 	/*
1372 	 * Move on to the next component in the hc-list
1373 	 */
1374 	hwp->hcw_index = ++i;
1375 	err = nvlist_lookup_string(hwp->hcw_list[i], FM_FMRI_HC_NAME, &name);
1376 	err |= nvlist_lookup_string(hwp->hcw_list[i], FM_FMRI_HC_ID, &id);
1377 	if (err != 0) {
1378 		(void) topo_mod_seterrno(mod, err);
1379 		return (TOPO_WALK_ERR);
1380 	}
1381 	inst = atoi(id);
1382 
1383 	return (topo_walk_byid(hwp->hcw_wp, name, inst));
1384 
1385 }
1386 
1387 static struct hc_walk *
1388 hc_walk_init(topo_mod_t *mod, tnode_t *node, nvlist_t *rsrc,
1389     topo_mod_walk_cb_t cb, void *pdata)
1390 {
1391 	int err, ret;
1392 	uint_t sz;
1393 	struct hc_walk *hwp;
1394 	topo_walk_t *wp;
1395 
1396 	if ((hwp = topo_mod_alloc(mod, sizeof (struct hc_walk))) == NULL) {
1397 		(void) topo_mod_seterrno(mod, EMOD_NOMEM);
1398 		return (NULL);
1399 	}
1400 
1401 	if (nvlist_lookup_nvlist_array(rsrc, FM_FMRI_HC_LIST, &hwp->hcw_list,
1402 	    &sz) != 0) {
1403 		topo_mod_dprintf(mod, "hc_walk_init: failed to lookup %s "
1404 		    "nvlist\n", FM_FMRI_HC_LIST);
1405 		topo_mod_free(mod, hwp, sizeof (struct hc_walk));
1406 		(void) topo_mod_seterrno(mod, EMOD_METHOD_INVAL);
1407 		return (NULL);
1408 	}
1409 	if ((ret = nvlist_lookup_nvlist(rsrc, FM_FMRI_FACILITY, &hwp->hcw_fac))
1410 	    != 0) {
1411 		if (ret != ENOENT) {
1412 			topo_mod_dprintf(mod, "hc_walk_init: unexpected error "
1413 			    "looking up %s nvlist", FM_FMRI_FACILITY);
1414 			topo_mod_free(mod, hwp, sizeof (struct hc_walk));
1415 			(void) topo_mod_seterrno(mod, EMOD_METHOD_INVAL);
1416 			return (NULL);
1417 		} else {
1418 			hwp->hcw_fac = NULL;
1419 		}
1420 	}
1421 
1422 	hwp->hcw_fmri = rsrc;
1423 	hwp->hcw_end = sz - 1;
1424 	hwp->hcw_index = 0;
1425 	hwp->hcw_priv = pdata;
1426 	hwp->hcw_cb = cb;
1427 	if ((wp = topo_mod_walk_init(mod, node, hc_walker, (void *)hwp, &err))
1428 	    == NULL) {
1429 		topo_mod_dprintf(mod, "hc_walk_init: topo_mod_walk_init failed "
1430 		    "(%s)\n", topo_strerror(err));
1431 		topo_mod_free(mod, hwp, sizeof (struct hc_walk));
1432 		(void) topo_mod_seterrno(mod, err);
1433 		return (NULL);
1434 	}
1435 
1436 	hwp->hcw_wp = wp;
1437 
1438 	return (hwp);
1439 }
1440 
1441 struct prop_lookup {
1442 	const char *pl_pgroup;
1443 	const char *pl_pname;
1444 	int pl_flag;
1445 	nvlist_t *pl_args;
1446 	nvlist_t *pl_rsrc;
1447 	nvlist_t *pl_prop;
1448 };
1449 
1450 /*ARGSUSED*/
1451 static int
1452 hc_prop_get(topo_mod_t *mod, tnode_t *node, void *pdata)
1453 {
1454 	int err = 0;
1455 
1456 	struct prop_lookup *plp = (struct prop_lookup *)pdata;
1457 
1458 	(void) topo_prop_getprop(node, plp->pl_pgroup, plp->pl_pname,
1459 	    plp->pl_args, &plp->pl_prop, &err);
1460 
1461 	return (err);
1462 }
1463 
1464 static int
1465 hc_fmri_prop_get(topo_mod_t *mod, tnode_t *node, topo_version_t version,
1466     nvlist_t *in, nvlist_t **out)
1467 {
1468 	int err;
1469 	struct hc_walk *hwp;
1470 	struct prop_lookup *plp;
1471 
1472 	if (version > TOPO_METH_PROP_GET_VERSION)
1473 		return (topo_mod_seterrno(mod, ETOPO_METHOD_VERNEW));
1474 
1475 	if ((plp = topo_mod_alloc(mod, sizeof (struct prop_lookup))) == NULL)
1476 		return (topo_mod_seterrno(mod, EMOD_NOMEM));
1477 
1478 	err = nvlist_lookup_string(in, TOPO_PROP_GROUP,
1479 	    (char **)&plp->pl_pgroup);
1480 	err |= nvlist_lookup_string(in, TOPO_PROP_VAL_NAME,
1481 	    (char **)&plp->pl_pname);
1482 	err |= nvlist_lookup_nvlist(in, TOPO_PROP_RESOURCE, &plp->pl_rsrc);
1483 	if (err != 0) {
1484 		topo_mod_free(mod, plp, sizeof (struct prop_lookup));
1485 		return (topo_mod_seterrno(mod, EMOD_METHOD_INVAL));
1486 	}
1487 
1488 	/*
1489 	 * Private args to prop method are optional
1490 	 */
1491 	if ((err = nvlist_lookup_nvlist(in, TOPO_PROP_PARGS, &plp->pl_args))
1492 	    != 0) {
1493 		if (err != ENOENT) {
1494 			topo_mod_free(mod, plp, sizeof (struct prop_lookup));
1495 			return (topo_mod_seterrno(mod, EMOD_METHOD_INVAL));
1496 		} else {
1497 			plp->pl_args = NULL;
1498 		}
1499 	}
1500 
1501 	plp->pl_prop = NULL;
1502 	if ((hwp = hc_walk_init(mod, node, plp->pl_rsrc, hc_prop_get,
1503 	    (void *)plp)) != NULL) {
1504 		if (topo_walk_step(hwp->hcw_wp, TOPO_WALK_CHILD) ==
1505 		    TOPO_WALK_ERR)
1506 			err = -1;
1507 		else
1508 			err = 0;
1509 		topo_walk_fini(hwp->hcw_wp);
1510 		topo_mod_free(mod, hwp, sizeof (struct hc_walk));
1511 	} else {
1512 		err = -1;
1513 	}
1514 
1515 	if (plp->pl_prop != NULL)
1516 		*out = plp->pl_prop;
1517 
1518 	topo_mod_free(mod, plp, sizeof (struct prop_lookup));
1519 
1520 	return (err);
1521 }
1522 
1523 /*ARGSUSED*/
1524 static int
1525 hc_pgrp_get(topo_mod_t *mod, tnode_t *node, void *pdata)
1526 {
1527 	int err = 0;
1528 
1529 	struct prop_lookup *plp = (struct prop_lookup *)pdata;
1530 
1531 	(void) topo_prop_getpgrp(node, plp->pl_pgroup, &plp->pl_prop, &err);
1532 
1533 	return (err);
1534 }
1535 
1536 static int
1537 hc_fmri_pgrp_get(topo_mod_t *mod, tnode_t *node, topo_version_t version,
1538     nvlist_t *in, nvlist_t **out)
1539 {
1540 	int err;
1541 	struct hc_walk *hwp;
1542 	struct prop_lookup *plp;
1543 
1544 	if (version > TOPO_METH_PGRP_GET_VERSION)
1545 		return (topo_mod_seterrno(mod, ETOPO_METHOD_VERNEW));
1546 
1547 	if ((plp = topo_mod_alloc(mod, sizeof (struct prop_lookup))) == NULL)
1548 		return (topo_mod_seterrno(mod, EMOD_NOMEM));
1549 
1550 	err = nvlist_lookup_string(in, TOPO_PROP_GROUP,
1551 	    (char **)&plp->pl_pgroup);
1552 	err |= nvlist_lookup_nvlist(in, TOPO_PROP_RESOURCE, &plp->pl_rsrc);
1553 	if (err != 0) {
1554 		topo_mod_free(mod, plp, sizeof (struct prop_lookup));
1555 		return (topo_mod_seterrno(mod, EMOD_METHOD_INVAL));
1556 	}
1557 
1558 	plp->pl_prop = NULL;
1559 	if ((hwp = hc_walk_init(mod, node, plp->pl_rsrc, hc_pgrp_get,
1560 	    (void *)plp)) != NULL) {
1561 		if (topo_walk_step(hwp->hcw_wp, TOPO_WALK_CHILD) ==
1562 		    TOPO_WALK_ERR)
1563 			err = -1;
1564 		else
1565 			err = 0;
1566 		topo_walk_fini(hwp->hcw_wp);
1567 		topo_mod_free(mod, hwp, sizeof (struct hc_walk));
1568 	} else {
1569 		err = -1;
1570 	}
1571 
1572 	if (plp->pl_prop != NULL)
1573 		*out = plp->pl_prop;
1574 
1575 	topo_mod_free(mod, plp, sizeof (struct prop_lookup));
1576 
1577 	return (err);
1578 }
1579 
1580 /*ARGSUSED*/
1581 static int
1582 hc_prop_setprop(topo_mod_t *mod, tnode_t *node, void *pdata)
1583 {
1584 	int err = 0;
1585 
1586 	struct prop_lookup *plp = (struct prop_lookup *)pdata;
1587 
1588 	(void) topo_prop_setprop(node, plp->pl_pgroup, plp->pl_prop,
1589 	    plp->pl_flag, plp->pl_args, &err);
1590 
1591 	return (err);
1592 }
1593 
1594 /*ARGSUSED*/
1595 static int
1596 hc_fmri_prop_set(topo_mod_t *mod, tnode_t *node, topo_version_t version,
1597     nvlist_t *in, nvlist_t **out)
1598 {
1599 	int err;
1600 	struct hc_walk *hwp;
1601 	struct prop_lookup *plp;
1602 
1603 	if (version > TOPO_METH_PROP_SET_VERSION)
1604 		return (topo_mod_seterrno(mod, ETOPO_METHOD_VERNEW));
1605 
1606 	if ((plp = topo_mod_alloc(mod, sizeof (struct prop_lookup))) == NULL)
1607 		return (topo_mod_seterrno(mod, EMOD_NOMEM));
1608 
1609 	err = nvlist_lookup_string(in, TOPO_PROP_GROUP,
1610 	    (char **)&plp->pl_pgroup);
1611 	err |= nvlist_lookup_nvlist(in, TOPO_PROP_RESOURCE, &plp->pl_rsrc);
1612 	err |= nvlist_lookup_nvlist(in, TOPO_PROP_VAL, &plp->pl_prop);
1613 	err |= nvlist_lookup_int32(in, TOPO_PROP_FLAG, &plp->pl_flag);
1614 	if (err != 0) {
1615 		topo_mod_free(mod, plp, sizeof (struct prop_lookup));
1616 		return (topo_mod_seterrno(mod, EMOD_METHOD_INVAL));
1617 	}
1618 
1619 	/*
1620 	 * Private args to prop method are optional
1621 	 */
1622 	if ((err = nvlist_lookup_nvlist(in, TOPO_PROP_PARGS, &plp->pl_args))
1623 	    != 0) {
1624 		if (err != ENOENT)
1625 			return (topo_mod_seterrno(mod, EMOD_METHOD_INVAL));
1626 		else
1627 			plp->pl_args = NULL;
1628 	}
1629 
1630 	if ((hwp = hc_walk_init(mod, node, plp->pl_rsrc, hc_prop_setprop,
1631 	    (void *)plp)) != NULL) {
1632 		if (topo_walk_step(hwp->hcw_wp, TOPO_WALK_CHILD) ==
1633 		    TOPO_WALK_ERR)
1634 			err = -1;
1635 		else
1636 			err = 0;
1637 		topo_walk_fini(hwp->hcw_wp);
1638 		topo_mod_free(mod, hwp, sizeof (struct hc_walk));
1639 	} else {
1640 		err = -1;
1641 	}
1642 
1643 	topo_mod_free(mod, plp, sizeof (struct prop_lookup));
1644 
1645 	return (err);
1646 }
1647 
1648 struct hc_args {
1649 	nvlist_t *ha_fmri;
1650 	nvlist_t *ha_nvl;
1651 };
1652 
1653 static int
1654 hc_is_present(topo_mod_t *mod, tnode_t *node, void *pdata)
1655 {
1656 	int err;
1657 	struct hc_args *hap = (struct hc_args *)pdata;
1658 
1659 	/*
1660 	 * check with the enumerator that created this FMRI
1661 	 * (topo node)
1662 	 */
1663 	if (topo_method_invoke(node, TOPO_METH_PRESENT,
1664 	    TOPO_METH_PRESENT_VERSION, hap->ha_fmri, &hap->ha_nvl,
1665 	    &err) < 0) {
1666 
1667 		/*
1668 		 * Err on the side of caution and return present
1669 		 */
1670 		if (topo_mod_nvalloc(mod, &hap->ha_nvl, NV_UNIQUE_NAME) == 0)
1671 			if (nvlist_add_uint32(hap->ha_nvl,
1672 			    TOPO_METH_PRESENT_RET, 1) == 0)
1673 				return (0);
1674 
1675 		return (ETOPO_PROP_NVL);
1676 	}
1677 
1678 	return (0);
1679 }
1680 
1681 static int
1682 hc_fmri_present(topo_mod_t *mod, tnode_t *node, topo_version_t version,
1683     nvlist_t *in, nvlist_t **out)
1684 {
1685 	int err;
1686 	struct hc_walk *hwp;
1687 	struct hc_args *hap;
1688 
1689 	if (version > TOPO_METH_PRESENT_VERSION)
1690 		return (topo_mod_seterrno(mod, ETOPO_METHOD_VERNEW));
1691 
1692 	if ((hap = topo_mod_alloc(mod, sizeof (struct hc_args))) == NULL)
1693 		return (topo_mod_seterrno(mod, EMOD_NOMEM));
1694 
1695 	hap->ha_fmri = in;
1696 	hap->ha_nvl = NULL;
1697 	if ((hwp = hc_walk_init(mod, node, hap->ha_fmri, hc_is_present,
1698 	    (void *)hap)) != NULL) {
1699 		if (topo_walk_step(hwp->hcw_wp, TOPO_WALK_CHILD) ==
1700 		    TOPO_WALK_ERR)
1701 			err = -1;
1702 		else
1703 			err = 0;
1704 		topo_walk_fini(hwp->hcw_wp);
1705 		topo_mod_free(mod, hwp, sizeof (struct hc_walk));
1706 	} else {
1707 		err = -1;
1708 	}
1709 
1710 	if (hap->ha_nvl != NULL)
1711 		*out = hap->ha_nvl;
1712 
1713 	topo_mod_free(mod, hap, sizeof (struct hc_args));
1714 
1715 	return (err);
1716 }
1717 
1718 static int
1719 hc_unusable(topo_mod_t *mod, tnode_t *node, void *pdata)
1720 {
1721 	int err;
1722 	struct hc_args *hap = (struct hc_args *)pdata;
1723 
1724 	/*
1725 	 * check with the enumerator that created this FMRI
1726 	 * (topo node)
1727 	 */
1728 	if (topo_method_invoke(node, TOPO_METH_UNUSABLE,
1729 	    TOPO_METH_UNUSABLE_VERSION, hap->ha_fmri, &hap->ha_nvl,
1730 	    &err) < 0) {
1731 
1732 		/*
1733 		 * Err on the side of caution and return usable
1734 		 */
1735 		if (topo_mod_nvalloc(mod, &hap->ha_nvl, NV_UNIQUE_NAME) == 0)
1736 			if (nvlist_add_uint32(hap->ha_nvl,
1737 			    TOPO_METH_UNUSABLE_RET, 0) == 0)
1738 				return (0);
1739 
1740 		return (ETOPO_PROP_NVL);
1741 	}
1742 
1743 	return (err);
1744 }
1745 
1746 static int
1747 hc_fmri_unusable(topo_mod_t *mod, tnode_t *node, topo_version_t version,
1748     nvlist_t *in, nvlist_t **out)
1749 {
1750 	int err;
1751 	struct hc_walk *hwp;
1752 	struct hc_args *hap;
1753 
1754 	if (version > TOPO_METH_UNUSABLE_VERSION)
1755 		return (topo_mod_seterrno(mod, ETOPO_METHOD_VERNEW));
1756 
1757 	if ((hap = topo_mod_alloc(mod, sizeof (struct hc_args))) == NULL)
1758 		return (topo_mod_seterrno(mod, EMOD_NOMEM));
1759 
1760 	hap->ha_fmri = in;
1761 	hap->ha_nvl = NULL;
1762 	if ((hwp = hc_walk_init(mod, node, hap->ha_fmri, hc_unusable,
1763 	    (void *)hap)) != NULL) {
1764 		if (topo_walk_step(hwp->hcw_wp, TOPO_WALK_CHILD) ==
1765 		    TOPO_WALK_ERR)
1766 			err = -1;
1767 		else
1768 			err = 0;
1769 		topo_walk_fini(hwp->hcw_wp);
1770 		topo_mod_free(mod, hwp, sizeof (struct hc_walk));
1771 	} else {
1772 		err = -1;
1773 	}
1774 
1775 	if (hap->ha_nvl != NULL)
1776 		*out = hap->ha_nvl;
1777 
1778 	topo_mod_free(mod, hap, sizeof (struct hc_args));
1779 
1780 	return (err);
1781 }
1782 
1783 struct fac_lookup {
1784 	const char *fl_fac_type;
1785 	uint32_t fl_fac_subtype;
1786 #ifdef _LP64
1787 	uint64_t fl_callback;
1788 	uint64_t fl_callback_args;
1789 #else
1790 	uint32_t fl_callback;
1791 	uint32_t fl_callback_args;
1792 #endif
1793 	nvlist_t *fl_rsrc;
1794 	nvlist_t *fl_fac_rsrc;
1795 };
1796 
1797 static int
1798 hc_fac_get(topo_mod_t *mod, tnode_t *node, void *pdata)
1799 {
1800 	struct fac_lookup *flp = (struct fac_lookup *)pdata;
1801 	topo_walk_cb_t cb = (topo_walk_cb_t)flp->fl_callback;
1802 	topo_faclist_t faclist, *tmp;
1803 	int err, ret = 0;
1804 
1805 	/*
1806 	 * Lookup the specified facility node.  Return with an error if we can't
1807 	 * find it.
1808 	 */
1809 	if (topo_node_facility(mod->tm_hdl, node, flp->fl_fac_type,
1810 	    flp->fl_fac_subtype, &faclist, &err) != 0) {
1811 		topo_mod_dprintf(mod, "hc_fac_get: topo_node_facility "
1812 		    "failed\n");
1813 		return (TOPO_WALK_ERR);
1814 	}
1815 
1816 	/*
1817 	 * Invoke user's callback for each facility node in the topo list,
1818 	 * passing in a pointer to the facility node
1819 	 */
1820 	for (tmp = topo_list_next(&faclist.tf_list); tmp != NULL;
1821 	    tmp = topo_list_next(tmp)) {
1822 
1823 		if ((err = cb(mod->tm_hdl, tmp->tf_node,
1824 		    (void *)flp->fl_callback_args)) != 0) {
1825 			(void) topo_mod_seterrno(mod, err);
1826 			topo_mod_dprintf(mod, "hc_fac_get: callback failed: "
1827 			    "%s\n ", topo_mod_errmsg(mod));
1828 			ret = TOPO_WALK_ERR;
1829 			break;
1830 		}
1831 	}
1832 
1833 	while ((tmp = topo_list_next(&faclist.tf_list)) != NULL) {
1834 		topo_list_delete(&faclist.tf_list, tmp);
1835 		topo_mod_free(mod, tmp, sizeof (topo_faclist_t));
1836 	}
1837 	return (ret);
1838 }
1839 
1840 static int
1841 hc_fmri_facility(topo_mod_t *mod, tnode_t *node, topo_version_t version,
1842     nvlist_t *in, nvlist_t **out)
1843 {
1844 	int err = 0;
1845 	struct hc_walk *hwp;
1846 	struct fac_lookup *flp;
1847 
1848 	if (version > TOPO_METH_FACILITY_VERSION)
1849 		return (topo_mod_seterrno(mod, ETOPO_METHOD_VERNEW));
1850 
1851 	if ((flp = topo_mod_alloc(mod, sizeof (struct fac_lookup))) == NULL)
1852 		return (topo_mod_seterrno(mod, EMOD_NOMEM));
1853 
1854 	/*
1855 	 * lookup arguments: hw resource, facility type, facility subtype,
1856 	 *  callback and callback args
1857 	 */
1858 	err = nvlist_lookup_nvlist(in, TOPO_PROP_RESOURCE, &flp->fl_rsrc);
1859 	err |= nvlist_lookup_string(in, FM_FMRI_FACILITY_TYPE,
1860 	    (char **)&flp->fl_fac_type);
1861 	err |= nvlist_lookup_uint32(in, "type", &flp->fl_fac_subtype);
1862 #ifdef _LP64
1863 	err |= nvlist_lookup_uint64(in, "callback", &flp->fl_callback);
1864 	err |= nvlist_lookup_uint64(in, "callback-args",
1865 	    &flp->fl_callback_args);
1866 #else
1867 	err |= nvlist_lookup_uint32(in, "callback", &flp->fl_callback);
1868 	err |= nvlist_lookup_uint32(in, "callback-args",
1869 	    &flp->fl_callback_args);
1870 #endif
1871 	if (err != 0) {
1872 		topo_mod_dprintf(mod, "hc_fmri_facility: failed to construct "
1873 		    "walker arg nvlist\n");
1874 		topo_mod_free(mod, flp, sizeof (struct fac_lookup));
1875 		return (topo_mod_seterrno(mod, EMOD_METHOD_INVAL));
1876 	}
1877 
1878 	flp->fl_fac_rsrc = NULL;
1879 	if ((hwp = hc_walk_init(mod, node, flp->fl_rsrc, hc_fac_get,
1880 	    (void *)flp)) != NULL) {
1881 		if (topo_walk_step(hwp->hcw_wp, TOPO_WALK_CHILD) ==
1882 		    TOPO_WALK_ERR)
1883 			err = -1;
1884 		else
1885 			err = 0;
1886 		topo_walk_fini(hwp->hcw_wp);
1887 		topo_mod_free(mod, hwp, sizeof (struct hc_walk));
1888 	} else {
1889 		topo_mod_dprintf(mod, "hc_fmri_facility: failed to initialize "
1890 		    "hc walker\n");
1891 		err = -1;
1892 	}
1893 
1894 	if (flp->fl_fac_rsrc != NULL)
1895 		*out = flp->fl_fac_rsrc;
1896 
1897 	topo_mod_free(mod, flp, sizeof (struct fac_lookup));
1898 
1899 	return (err);
1900 }
1901