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