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