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