xref: /titanic_44/usr/src/lib/fm/topo/libtopo/common/hc.c (revision 48a4016cae8aa2b8b3d8b258eb22e0c781912bed)
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 	if (nf != NULL)
1043 		nvlist_free(nf);
1044 	for (i = 0; i < npairs; i++)
1045 		nvlist_free(pa[i]);
1046 	topo_mod_free(mod, pa, npairs * sizeof (nvlist_t *));
1047 
1048 	topo_mod_strfree(mod, serial);
1049 	topo_mod_strfree(mod, part);
1050 	topo_mod_strfree(mod, rev);
1051 	nvlist_free(auth);
1052 	nvlist_free(nf);
1053 	return (topo_mod_seterrno(mod, EMOD_FMRI_MALFORM));
1054 }
1055 
1056 static nvlist_t *
1057 hc_list_create(topo_mod_t *mod, const char *name, char *inst)
1058 {
1059 	int err;
1060 	nvlist_t *hc;
1061 
1062 	if (topo_mod_nvalloc(mod, &hc, NV_UNIQUE_NAME) != 0)
1063 		return (NULL);
1064 
1065 	err = nvlist_add_string(hc, FM_FMRI_HC_NAME, name);
1066 	err |= nvlist_add_string(hc, FM_FMRI_HC_ID, inst);
1067 	if (err != 0) {
1068 		nvlist_free(hc);
1069 		return (NULL);
1070 	}
1071 
1072 	return (hc);
1073 }
1074 
1075 static nvlist_t *
1076 hc_create_seterror(topo_mod_t *mod, nvlist_t **hcl, int n, nvlist_t *fmri,
1077     int err)
1078 {
1079 	int i;
1080 
1081 	if (hcl != NULL) {
1082 		for (i = 0; i < n + 1; ++i)
1083 			nvlist_free(hcl[i]);
1084 
1085 		topo_mod_free(mod, hcl, sizeof (nvlist_t *) * (n + 1));
1086 	}
1087 
1088 	nvlist_free(fmri);
1089 
1090 	(void) topo_mod_seterrno(mod, err);
1091 
1092 	topo_mod_dprintf(mod, "unable to create hc FMRI: %s\n",
1093 	    topo_mod_errmsg(mod));
1094 
1095 	return (NULL);
1096 }
1097 
1098 static int
1099 hc_name_canonical(topo_mod_t *mod, const char *name)
1100 {
1101 	int i;
1102 
1103 	if (getenv("NOHCCHECK") != NULL)
1104 		return (1);
1105 
1106 	/*
1107 	 * Only enumerate elements with correct canonical names
1108 	 */
1109 	for (i = 0; i < hc_ncanon; i++) {
1110 		if (strcmp(name, hc_canon[i].hcc_name) == 0)
1111 			break;
1112 	}
1113 	if (i >= hc_ncanon) {
1114 		topo_mod_dprintf(mod, "non-canonical name %s\n",
1115 		    name);
1116 		return (0);
1117 	} else {
1118 		return (1);
1119 	}
1120 }
1121 
1122 static nvlist_t *
1123 hc_fmri_create(topo_mod_t *mod, nvlist_t *pfmri, int version, const char *name,
1124     topo_instance_t inst, const nvlist_t *auth, const char *part,
1125     const char *rev, const char *serial)
1126 {
1127 	int i;
1128 	char str[21]; /* sizeof (UINT64_MAX) + '\0' */
1129 	uint_t pelems = 0;
1130 	nvlist_t **phcl = NULL;
1131 	nvlist_t **hcl = NULL;
1132 	nvlist_t *fmri = NULL;
1133 
1134 	if (version > FM_HC_SCHEME_VERSION)
1135 		return (hc_create_seterror(mod,
1136 		    hcl, pelems, fmri, EMOD_VER_OLD));
1137 	else if (version < FM_HC_SCHEME_VERSION)
1138 		return (hc_create_seterror(mod,
1139 		    hcl, pelems, fmri, EMOD_VER_NEW));
1140 
1141 	/*
1142 	 * Check that the requested name is in our canonical list
1143 	 */
1144 	if (hc_name_canonical(mod, name) == 0)
1145 		return (hc_create_seterror(mod,
1146 		    hcl, pelems, fmri, EMOD_NONCANON));
1147 	/*
1148 	 * Copy the parent's HC_LIST
1149 	 */
1150 	if (pfmri != NULL) {
1151 		if (nvlist_lookup_nvlist_array(pfmri, FM_FMRI_HC_LIST,
1152 		    &phcl, &pelems) != 0)
1153 			return (hc_create_seterror(mod,
1154 			    hcl, pelems, fmri, EMOD_FMRI_MALFORM));
1155 	}
1156 
1157 	hcl = topo_mod_zalloc(mod, sizeof (nvlist_t *) * (pelems + 1));
1158 	if (hcl == NULL)
1159 		return (hc_create_seterror(mod,  hcl, pelems, fmri,
1160 		    EMOD_NOMEM));
1161 
1162 	for (i = 0; i < pelems; ++i)
1163 		if (topo_mod_nvdup(mod, phcl[i], &hcl[i]) != 0)
1164 			return (hc_create_seterror(mod,
1165 			    hcl, pelems, fmri, EMOD_FMRI_NVL));
1166 
1167 	(void) snprintf(str, sizeof (str), "%d", inst);
1168 	if ((hcl[i] = hc_list_create(mod, name, str)) == NULL)
1169 		return (hc_create_seterror(mod,
1170 		    hcl, pelems, fmri, EMOD_FMRI_NVL));
1171 
1172 	if ((fmri = hc_base_fmri_create(mod, auth, part, rev, serial)) == NULL)
1173 		return (hc_create_seterror(mod,
1174 		    hcl, pelems, fmri, EMOD_FMRI_NVL));
1175 
1176 	if (nvlist_add_nvlist_array(fmri, FM_FMRI_HC_LIST, hcl, pelems + 1)
1177 	    != 0)
1178 		return (hc_create_seterror(mod,
1179 		    hcl, pelems, fmri, EMOD_FMRI_NVL));
1180 
1181 	if (hcl != NULL) {
1182 		for (i = 0; i < pelems + 1; ++i) {
1183 			if (hcl[i] != NULL)
1184 				nvlist_free(hcl[i]);
1185 		}
1186 		topo_mod_free(mod, hcl, sizeof (nvlist_t *) * (pelems + 1));
1187 	}
1188 
1189 	return (fmri);
1190 }
1191 
1192 /*ARGSUSED*/
1193 static int
1194 hc_fmri_create_meth(topo_mod_t *mod, tnode_t *node, topo_version_t version,
1195     nvlist_t *in, nvlist_t **out)
1196 {
1197 	int ret;
1198 	nvlist_t *args, *pfmri = NULL;
1199 	nvlist_t *auth;
1200 	uint32_t inst;
1201 	char *name, *serial, *rev, *part;
1202 
1203 	if (version > TOPO_METH_FMRI_VERSION)
1204 		return (topo_mod_seterrno(mod, EMOD_VER_NEW));
1205 
1206 	/* First the must-have fields */
1207 	if (nvlist_lookup_string(in, TOPO_METH_FMRI_ARG_NAME, &name) != 0)
1208 		return (topo_mod_seterrno(mod, EMOD_METHOD_INVAL));
1209 	if (nvlist_lookup_uint32(in, TOPO_METH_FMRI_ARG_INST, &inst) != 0)
1210 		return (topo_mod_seterrno(mod, EMOD_METHOD_INVAL));
1211 
1212 	/*
1213 	 * args is optional
1214 	 */
1215 	pfmri = NULL;
1216 	auth = NULL;
1217 	serial = rev = part = NULL;
1218 	if ((ret = nvlist_lookup_nvlist(in, TOPO_METH_FMRI_ARG_NVL, &args))
1219 	    != 0) {
1220 		if (ret != ENOENT)
1221 			return (topo_mod_seterrno(mod, EMOD_METHOD_INVAL));
1222 	} else {
1223 
1224 		/* And then optional arguments */
1225 		(void) nvlist_lookup_nvlist(args, TOPO_METH_FMRI_ARG_PARENT,
1226 		    &pfmri);
1227 		(void) nvlist_lookup_nvlist(args, TOPO_METH_FMRI_ARG_AUTH,
1228 		    &auth);
1229 		(void) nvlist_lookup_string(args, TOPO_METH_FMRI_ARG_PART,
1230 		    &part);
1231 		(void) nvlist_lookup_string(args, TOPO_METH_FMRI_ARG_REV, &rev);
1232 		(void) nvlist_lookup_string(args, TOPO_METH_FMRI_ARG_SER,
1233 		    &serial);
1234 	}
1235 
1236 	*out = hc_fmri_create(mod, pfmri, version, name, inst, auth, part,
1237 	    rev, serial);
1238 	if (*out == NULL)
1239 		return (-1);
1240 	return (0);
1241 }
1242 
1243 struct hc_walk {
1244 	topo_mod_walk_cb_t hcw_cb;
1245 	void *hcw_priv;
1246 	topo_walk_t *hcw_wp;
1247 	nvlist_t **hcw_list;
1248 	nvlist_t *hcw_fmri;
1249 	nvlist_t *hcw_fac;
1250 	uint_t hcw_index;
1251 	uint_t hcw_end;
1252 };
1253 
1254 /*
1255  * Returns true if the given node is beneath the specified FMRI.  This uses
1256  * the TOPO_METH_CONTAINS method, because some enumerators (such as external
1257  * enclosures) may want to do a comparison based on chassis WWN instead of the
1258  * instance ID.  If this comparison function fails or is not supported, then we
1259  * fall back to a direct name/instance comparison.
1260  */
1261 static int
1262 hc_match(topo_mod_t *mod, tnode_t *node, nvlist_t *fmri, const char *name,
1263     topo_instance_t inst, boolean_t *result)
1264 {
1265 	nvlist_t *rsrc;
1266 	nvlist_t *arg, *nvl;
1267 	uint32_t match = 0;
1268 	int err;
1269 
1270 	if (topo_node_resource(node, &rsrc, &err) != 0)
1271 		return (-1);
1272 
1273 	if (topo_mod_nvalloc(mod, &arg, NV_UNIQUE_NAME) != 0 ||
1274 	    nvlist_add_nvlist(arg, TOPO_METH_FMRI_ARG_FMRI,
1275 	    rsrc) != 0 ||
1276 	    nvlist_add_nvlist(arg, TOPO_METH_FMRI_ARG_SUBFMRI,
1277 	    fmri) != 0) {
1278 		nvlist_free(rsrc);
1279 		(void) topo_mod_seterrno(mod, EMOD_NOMEM);
1280 		return (-1);
1281 	}
1282 
1283 	nvlist_free(rsrc);
1284 
1285 	if (topo_method_invoke(node, TOPO_METH_CONTAINS,
1286 	    TOPO_METH_CONTAINS_VERSION, arg, &nvl, &err) != 0) {
1287 		nvlist_free(arg);
1288 		if (err == ETOPO_METHOD_NOTSUP) {
1289 			match = (strcmp(name,
1290 			    topo_node_name(node)) == 0 &&
1291 			    inst == topo_node_instance(node));
1292 		} else {
1293 			return (-1);
1294 		}
1295 	} else {
1296 		nvlist_free(arg);
1297 		if (nvlist_lookup_uint32(nvl, TOPO_METH_CONTAINS_RET,
1298 		    &match) != 0) {
1299 			nvlist_free(nvl);
1300 			(void) topo_mod_seterrno(mod, EMOD_NVL_INVAL);
1301 			return (-1);
1302 		}
1303 		nvlist_free(nvl);
1304 	}
1305 
1306 	*result = (match != 0);
1307 	return (0);
1308 }
1309 
1310 /*
1311  * Ideally, we should just be able to call topo_walk_bysibling().  But that
1312  * code assumes that the name/instance pair will match, so we need to
1313  * explicitly iterate over children of the parent looking for a matching value.
1314  */
1315 static int
1316 hc_walk_sibling(topo_mod_t *mod, tnode_t *node, struct hc_walk *hwp,
1317     const char *name, topo_instance_t inst)
1318 {
1319 	tnode_t *pnp = topo_node_parent(node);
1320 	topo_walk_t *wp = hwp->hcw_wp;
1321 	tnode_t *np;
1322 	boolean_t matched;
1323 	int status;
1324 
1325 	for (np = topo_child_first(pnp); np != NULL;
1326 	    np = topo_child_next(pnp, np)) {
1327 		topo_node_hold(np);
1328 		if (hc_match(mod, np, hwp->hcw_fmri, name, inst,
1329 		    &matched) == 0 && matched) {
1330 			wp->tw_node = np;
1331 			if (wp->tw_mod != NULL)
1332 				status = wp->tw_cb(mod, np, hwp);
1333 			else
1334 				status = wp->tw_cb(wp->tw_thp, np, hwp);
1335 			topo_node_rele(np);
1336 			wp->tw_node = node;
1337 			return (status);
1338 		}
1339 
1340 		topo_node_rele(np);
1341 	}
1342 
1343 	return (TOPO_WALK_TERMINATE);
1344 }
1345 
1346 /*
1347  * Generic walker for the hc-scheme topo tree.  This function uses the
1348  * hierachical nature of the hc-scheme to efficiently step through
1349  * the topo hc tree.  Node lookups are done by topo_walk_byid() and
1350  * topo_walk_bysibling()  at each component level to avoid unnecessary
1351  * traversal of the tree.  hc_walker() never returns TOPO_WALK_NEXT, so
1352  * whether TOPO_WALK_CHILD or TOPO_WALK_SIBLING is specified by
1353  * topo_walk_step() doesn't affect the traversal.
1354  */
1355 static int
1356 hc_walker(topo_mod_t *mod, tnode_t *node, void *pdata)
1357 {
1358 	int i, err;
1359 	struct hc_walk *hwp = (struct hc_walk *)pdata;
1360 	char *name, *id;
1361 	char *fname, *ftype;
1362 	topo_instance_t inst;
1363 	boolean_t match;
1364 
1365 	i = hwp->hcw_index;
1366 	if (i > hwp->hcw_end) {
1367 		if (hwp->hcw_fac != NULL) {
1368 			if ((err = hwp->hcw_cb(mod, node, hwp->hcw_priv))
1369 			    != 0) {
1370 				(void) topo_mod_seterrno(mod, err);
1371 				topo_mod_dprintf(mod, "hc_walker: callback "
1372 				    "failed: %s\n ", topo_mod_errmsg(mod));
1373 				return (TOPO_WALK_ERR);
1374 			}
1375 			topo_mod_dprintf(mod, "hc_walker: callback "
1376 			    "complete: terminate walk\n");
1377 			return (TOPO_WALK_TERMINATE);
1378 		} else {
1379 			topo_mod_dprintf(mod, "hc_walker: node not found\n");
1380 			return (TOPO_WALK_TERMINATE);
1381 		}
1382 	}
1383 
1384 	err = nvlist_lookup_string(hwp->hcw_list[i], FM_FMRI_HC_NAME, &name);
1385 	err |= nvlist_lookup_string(hwp->hcw_list[i], FM_FMRI_HC_ID, &id);
1386 
1387 	if (err != 0) {
1388 		(void) topo_mod_seterrno(mod, EMOD_NVL_INVAL);
1389 		return (TOPO_WALK_ERR);
1390 	}
1391 
1392 	inst = atoi(id);
1393 
1394 	/*
1395 	 * Check to see if our node matches the requested FMRI.  If it doesn't
1396 	 * (because the enumerator determines matching based on something other
1397 	 * than name/instance, or because we're at the first level below the
1398 	 * root), then iterate over siblings to find the matching node.
1399 	 */
1400 	if (hc_match(mod, node, hwp->hcw_fmri, name, inst, &match) != 0)
1401 		return (TOPO_WALK_ERR);
1402 
1403 	if (!match)
1404 		return (hc_walk_sibling(mod, node, hwp, name, inst));
1405 
1406 	topo_mod_dprintf(mod, "hc_walker: walking node:%s=%d for hc:"
1407 	    "%s=%d at %d, end at %d \n", topo_node_name(node),
1408 	    topo_node_instance(node), name, inst, i, hwp->hcw_end);
1409 
1410 	if (i == hwp->hcw_end) {
1411 
1412 		/*
1413 		 * We are at the end of the hc-list.  Now, check for
1414 		 * a facility leaf and walk one more time.
1415 		 */
1416 		if (hwp->hcw_fac != NULL) {
1417 			err = nvlist_lookup_string(hwp->hcw_fac,
1418 			    FM_FMRI_FACILITY_NAME, &fname);
1419 			err |= nvlist_lookup_string(hwp->hcw_fac,
1420 			    FM_FMRI_FACILITY_TYPE, &ftype);
1421 			if (err != 0) {
1422 				(void) topo_mod_seterrno(mod, EMOD_NVL_INVAL);
1423 				return (TOPO_WALK_ERR);
1424 			}
1425 			hwp->hcw_index++;
1426 			topo_mod_dprintf(mod, "hc_walker: walk to facility "
1427 			    "node:%s=%s\n", fname, ftype);
1428 			return (topo_walk_byid(hwp->hcw_wp, fname, 0));
1429 		}
1430 
1431 		/*
1432 		 * Otherwise, this is the node we're looking for.
1433 		 */
1434 		if ((err = hwp->hcw_cb(mod, node, hwp->hcw_priv)) != 0) {
1435 			(void) topo_mod_seterrno(mod, err);
1436 			topo_mod_dprintf(mod, "hc_walker: callback "
1437 			    "failed: %s\n ", topo_mod_errmsg(mod));
1438 			return (TOPO_WALK_ERR);
1439 		} else {
1440 			topo_mod_dprintf(mod, "hc_walker: callback "
1441 			    "complete: terminate walk\n");
1442 			return (TOPO_WALK_TERMINATE);
1443 		}
1444 	}
1445 
1446 	/*
1447 	 * Move on to the next component in the hc-list
1448 	 */
1449 	hwp->hcw_index = ++i;
1450 	err = nvlist_lookup_string(hwp->hcw_list[i], FM_FMRI_HC_NAME, &name);
1451 	err |= nvlist_lookup_string(hwp->hcw_list[i], FM_FMRI_HC_ID, &id);
1452 	if (err != 0) {
1453 		(void) topo_mod_seterrno(mod, err);
1454 		return (TOPO_WALK_ERR);
1455 	}
1456 	inst = atoi(id);
1457 
1458 	return (topo_walk_byid(hwp->hcw_wp, name, inst));
1459 
1460 }
1461 
1462 static struct hc_walk *
1463 hc_walk_init(topo_mod_t *mod, tnode_t *node, nvlist_t *rsrc,
1464     topo_mod_walk_cb_t cb, void *pdata)
1465 {
1466 	int err, ret;
1467 	uint_t sz;
1468 	struct hc_walk *hwp;
1469 	topo_walk_t *wp;
1470 
1471 	if ((hwp = topo_mod_alloc(mod, sizeof (struct hc_walk))) == NULL) {
1472 		(void) topo_mod_seterrno(mod, EMOD_NOMEM);
1473 		return (NULL);
1474 	}
1475 
1476 	if (nvlist_lookup_nvlist_array(rsrc, FM_FMRI_HC_LIST, &hwp->hcw_list,
1477 	    &sz) != 0) {
1478 		topo_mod_dprintf(mod, "hc_walk_init: failed to lookup %s "
1479 		    "nvlist\n", FM_FMRI_HC_LIST);
1480 		topo_mod_free(mod, hwp, sizeof (struct hc_walk));
1481 		(void) topo_mod_seterrno(mod, EMOD_METHOD_INVAL);
1482 		return (NULL);
1483 	}
1484 	if ((ret = nvlist_lookup_nvlist(rsrc, FM_FMRI_FACILITY, &hwp->hcw_fac))
1485 	    != 0) {
1486 		if (ret != ENOENT) {
1487 			topo_mod_dprintf(mod, "hc_walk_init: unexpected error "
1488 			    "looking up %s nvlist", FM_FMRI_FACILITY);
1489 			topo_mod_free(mod, hwp, sizeof (struct hc_walk));
1490 			(void) topo_mod_seterrno(mod, EMOD_METHOD_INVAL);
1491 			return (NULL);
1492 		} else {
1493 			hwp->hcw_fac = NULL;
1494 		}
1495 	}
1496 
1497 	hwp->hcw_fmri = rsrc;
1498 	hwp->hcw_end = sz - 1;
1499 	hwp->hcw_index = 0;
1500 	hwp->hcw_priv = pdata;
1501 	hwp->hcw_cb = cb;
1502 	if ((wp = topo_mod_walk_init(mod, node, hc_walker, (void *)hwp, &err))
1503 	    == NULL) {
1504 		topo_mod_dprintf(mod, "hc_walk_init: topo_mod_walk_init failed "
1505 		    "(%s)\n", topo_strerror(err));
1506 		topo_mod_free(mod, hwp, sizeof (struct hc_walk));
1507 		(void) topo_mod_seterrno(mod, err);
1508 		return (NULL);
1509 	}
1510 
1511 	hwp->hcw_wp = wp;
1512 
1513 	return (hwp);
1514 }
1515 
1516 struct prop_lookup {
1517 	const char *pl_pgroup;
1518 	const char *pl_pname;
1519 	int pl_flag;
1520 	nvlist_t *pl_args;
1521 	nvlist_t *pl_rsrc;
1522 	nvlist_t *pl_prop;
1523 };
1524 
1525 /*ARGSUSED*/
1526 static int
1527 hc_prop_get(topo_mod_t *mod, tnode_t *node, void *pdata)
1528 {
1529 	int err = 0;
1530 
1531 	struct prop_lookup *plp = (struct prop_lookup *)pdata;
1532 
1533 	(void) topo_prop_getprop(node, plp->pl_pgroup, plp->pl_pname,
1534 	    plp->pl_args, &plp->pl_prop, &err);
1535 
1536 	return (err);
1537 }
1538 
1539 static int
1540 hc_fmri_prop_get(topo_mod_t *mod, tnode_t *node, topo_version_t version,
1541     nvlist_t *in, nvlist_t **out)
1542 {
1543 	int err;
1544 	struct hc_walk *hwp;
1545 	struct prop_lookup *plp;
1546 
1547 	if (version > TOPO_METH_PROP_GET_VERSION)
1548 		return (topo_mod_seterrno(mod, ETOPO_METHOD_VERNEW));
1549 
1550 	if ((plp = topo_mod_alloc(mod, sizeof (struct prop_lookup))) == NULL)
1551 		return (topo_mod_seterrno(mod, EMOD_NOMEM));
1552 
1553 	err = nvlist_lookup_string(in, TOPO_PROP_GROUP,
1554 	    (char **)&plp->pl_pgroup);
1555 	err |= nvlist_lookup_string(in, TOPO_PROP_VAL_NAME,
1556 	    (char **)&plp->pl_pname);
1557 	err |= nvlist_lookup_nvlist(in, TOPO_PROP_RESOURCE, &plp->pl_rsrc);
1558 	if (err != 0) {
1559 		topo_mod_free(mod, plp, sizeof (struct prop_lookup));
1560 		return (topo_mod_seterrno(mod, EMOD_METHOD_INVAL));
1561 	}
1562 
1563 	/*
1564 	 * Private args to prop method are optional
1565 	 */
1566 	if ((err = nvlist_lookup_nvlist(in, TOPO_PROP_PARGS, &plp->pl_args))
1567 	    != 0) {
1568 		if (err != ENOENT) {
1569 			topo_mod_free(mod, plp, sizeof (struct prop_lookup));
1570 			return (topo_mod_seterrno(mod, EMOD_METHOD_INVAL));
1571 		} else {
1572 			plp->pl_args = NULL;
1573 		}
1574 	}
1575 
1576 	plp->pl_prop = NULL;
1577 	if ((hwp = hc_walk_init(mod, node, plp->pl_rsrc, hc_prop_get,
1578 	    (void *)plp)) != NULL) {
1579 		if (topo_walk_step(hwp->hcw_wp, TOPO_WALK_CHILD) ==
1580 		    TOPO_WALK_ERR)
1581 			err = -1;
1582 		else
1583 			err = 0;
1584 		topo_walk_fini(hwp->hcw_wp);
1585 		topo_mod_free(mod, hwp, sizeof (struct hc_walk));
1586 	} else {
1587 		err = -1;
1588 	}
1589 
1590 	if (plp->pl_prop != NULL)
1591 		*out = plp->pl_prop;
1592 
1593 	topo_mod_free(mod, plp, sizeof (struct prop_lookup));
1594 
1595 	return (err);
1596 }
1597 
1598 /*ARGSUSED*/
1599 static int
1600 hc_pgrp_get(topo_mod_t *mod, tnode_t *node, void *pdata)
1601 {
1602 	int err = 0;
1603 
1604 	struct prop_lookup *plp = (struct prop_lookup *)pdata;
1605 
1606 	(void) topo_prop_getpgrp(node, plp->pl_pgroup, &plp->pl_prop, &err);
1607 
1608 	return (err);
1609 }
1610 
1611 static int
1612 hc_fmri_pgrp_get(topo_mod_t *mod, tnode_t *node, topo_version_t version,
1613     nvlist_t *in, nvlist_t **out)
1614 {
1615 	int err;
1616 	struct hc_walk *hwp;
1617 	struct prop_lookup *plp;
1618 
1619 	if (version > TOPO_METH_PGRP_GET_VERSION)
1620 		return (topo_mod_seterrno(mod, ETOPO_METHOD_VERNEW));
1621 
1622 	if ((plp = topo_mod_alloc(mod, sizeof (struct prop_lookup))) == NULL)
1623 		return (topo_mod_seterrno(mod, EMOD_NOMEM));
1624 
1625 	err = nvlist_lookup_string(in, TOPO_PROP_GROUP,
1626 	    (char **)&plp->pl_pgroup);
1627 	err |= nvlist_lookup_nvlist(in, TOPO_PROP_RESOURCE, &plp->pl_rsrc);
1628 	if (err != 0) {
1629 		topo_mod_free(mod, plp, sizeof (struct prop_lookup));
1630 		return (topo_mod_seterrno(mod, EMOD_METHOD_INVAL));
1631 	}
1632 
1633 	plp->pl_prop = NULL;
1634 	if ((hwp = hc_walk_init(mod, node, plp->pl_rsrc, hc_pgrp_get,
1635 	    (void *)plp)) != NULL) {
1636 		if (topo_walk_step(hwp->hcw_wp, TOPO_WALK_CHILD) ==
1637 		    TOPO_WALK_ERR)
1638 			err = -1;
1639 		else
1640 			err = 0;
1641 		topo_walk_fini(hwp->hcw_wp);
1642 		topo_mod_free(mod, hwp, sizeof (struct hc_walk));
1643 	} else {
1644 		err = -1;
1645 	}
1646 
1647 	if (plp->pl_prop != NULL)
1648 		*out = plp->pl_prop;
1649 
1650 	topo_mod_free(mod, plp, sizeof (struct prop_lookup));
1651 
1652 	return (err);
1653 }
1654 
1655 /*ARGSUSED*/
1656 static int
1657 hc_prop_setprop(topo_mod_t *mod, tnode_t *node, void *pdata)
1658 {
1659 	int err = 0;
1660 
1661 	struct prop_lookup *plp = (struct prop_lookup *)pdata;
1662 
1663 	(void) topo_prop_setprop(node, plp->pl_pgroup, plp->pl_prop,
1664 	    plp->pl_flag, plp->pl_args, &err);
1665 
1666 	return (err);
1667 }
1668 
1669 /*ARGSUSED*/
1670 static int
1671 hc_fmri_prop_set(topo_mod_t *mod, tnode_t *node, topo_version_t version,
1672     nvlist_t *in, nvlist_t **out)
1673 {
1674 	int err;
1675 	struct hc_walk *hwp;
1676 	struct prop_lookup *plp;
1677 
1678 	if (version > TOPO_METH_PROP_SET_VERSION)
1679 		return (topo_mod_seterrno(mod, ETOPO_METHOD_VERNEW));
1680 
1681 	if ((plp = topo_mod_alloc(mod, sizeof (struct prop_lookup))) == NULL)
1682 		return (topo_mod_seterrno(mod, EMOD_NOMEM));
1683 
1684 	err = nvlist_lookup_string(in, TOPO_PROP_GROUP,
1685 	    (char **)&plp->pl_pgroup);
1686 	err |= nvlist_lookup_nvlist(in, TOPO_PROP_RESOURCE, &plp->pl_rsrc);
1687 	err |= nvlist_lookup_nvlist(in, TOPO_PROP_VAL, &plp->pl_prop);
1688 	err |= nvlist_lookup_int32(in, TOPO_PROP_FLAG, &plp->pl_flag);
1689 	if (err != 0) {
1690 		topo_mod_free(mod, plp, sizeof (struct prop_lookup));
1691 		return (topo_mod_seterrno(mod, EMOD_METHOD_INVAL));
1692 	}
1693 
1694 	/*
1695 	 * Private args to prop method are optional
1696 	 */
1697 	if ((err = nvlist_lookup_nvlist(in, TOPO_PROP_PARGS, &plp->pl_args))
1698 	    != 0) {
1699 		if (err != ENOENT)
1700 			return (topo_mod_seterrno(mod, EMOD_METHOD_INVAL));
1701 		else
1702 			plp->pl_args = NULL;
1703 	}
1704 
1705 	if ((hwp = hc_walk_init(mod, node, plp->pl_rsrc, hc_prop_setprop,
1706 	    (void *)plp)) != NULL) {
1707 		if (topo_walk_step(hwp->hcw_wp, TOPO_WALK_CHILD) ==
1708 		    TOPO_WALK_ERR)
1709 			err = -1;
1710 		else
1711 			err = 0;
1712 		topo_walk_fini(hwp->hcw_wp);
1713 		topo_mod_free(mod, hwp, sizeof (struct hc_walk));
1714 	} else {
1715 		err = -1;
1716 	}
1717 
1718 	topo_mod_free(mod, plp, sizeof (struct prop_lookup));
1719 
1720 	return (err);
1721 }
1722 
1723 struct hc_args {
1724 	nvlist_t *ha_fmri;
1725 	nvlist_t *ha_nvl;
1726 	char *ha_method_name;
1727 	topo_version_t ha_method_ver;
1728 };
1729 
1730 static int
1731 hc_auth_changed(nvlist_t *nva, nvlist_t *nvb, const char *propname)
1732 {
1733 	char *stra, *strb;
1734 
1735 	if (nvlist_lookup_string(nva, propname, &stra) != 0 ||
1736 	    nvlist_lookup_string(nvb, propname, &strb) != 0)
1737 		return (FMD_OBJ_STATE_UNKNOWN);
1738 
1739 	if (strcmp(stra, strb) != 0)
1740 		return (FMD_OBJ_STATE_REPLACED);
1741 	else
1742 		return (FMD_OBJ_STATE_STILL_PRESENT);
1743 }
1744 
1745 static int
1746 hc_is_present(topo_mod_t *mod, tnode_t *node, void *pdata)
1747 {
1748 	int err;
1749 	struct hc_args *hap = (struct hc_args *)pdata;
1750 	nvlist_t *rsrc;
1751 	boolean_t present;
1752 
1753 	/*
1754 	 * check with the enumerator that created this FMRI
1755 	 * (topo node)
1756 	 */
1757 	if (topo_method_invoke(node, TOPO_METH_PRESENT,
1758 	    TOPO_METH_PRESENT_VERSION, hap->ha_fmri, &hap->ha_nvl,
1759 	    &err) < 0) {
1760 
1761 		/*
1762 		 * If the method exists but failed for some other reason,
1763 		 * propagate the error as making any decision over presence is
1764 		 * impossible.
1765 		 */
1766 		if (err != ETOPO_METHOD_NOTSUP)
1767 			return (err);
1768 
1769 		/*
1770 		 * Check the authority information.  If the part id or serial
1771 		 * number doesn't match, then it isn't the same FMRI.
1772 		 * Otherwise, assume presence.
1773 		 */
1774 		if (topo_node_resource(node, &rsrc, &err) != 0)
1775 			return (err);
1776 
1777 		present = B_TRUE;
1778 		if (hc_auth_changed(hap->ha_fmri, rsrc,
1779 		    FM_FMRI_HC_SERIAL_ID) == FMD_OBJ_STATE_REPLACED ||
1780 		    hc_auth_changed(hap->ha_fmri, rsrc,
1781 		    FM_FMRI_HC_PART) == FMD_OBJ_STATE_REPLACED) {
1782 			present = B_FALSE;
1783 		}
1784 		nvlist_free(rsrc);
1785 
1786 		if (topo_mod_nvalloc(mod, &hap->ha_nvl, NV_UNIQUE_NAME) != 0)
1787 			return (EMOD_NOMEM);
1788 
1789 		if (nvlist_add_uint32(hap->ha_nvl,
1790 		    TOPO_METH_PRESENT_RET, present) != 0) {
1791 			nvlist_free(hap->ha_nvl);
1792 			hap->ha_nvl = NULL;
1793 			return (EMOD_NOMEM);
1794 		}
1795 	}
1796 
1797 	return (0);
1798 }
1799 
1800 static int
1801 hc_fmri_present(topo_mod_t *mod, tnode_t *node, topo_version_t version,
1802     nvlist_t *in, nvlist_t **out)
1803 {
1804 	int err;
1805 	struct hc_walk *hwp;
1806 	struct hc_args *hap;
1807 
1808 	if (version > TOPO_METH_PRESENT_VERSION)
1809 		return (topo_mod_seterrno(mod, ETOPO_METHOD_VERNEW));
1810 
1811 	if ((hap = topo_mod_alloc(mod, sizeof (struct hc_args))) == NULL)
1812 		return (topo_mod_seterrno(mod, EMOD_NOMEM));
1813 
1814 	hap->ha_fmri = in;
1815 	hap->ha_nvl = NULL;
1816 	if ((hwp = hc_walk_init(mod, node, hap->ha_fmri, hc_is_present,
1817 	    (void *)hap)) != NULL) {
1818 		if (topo_walk_step(hwp->hcw_wp, TOPO_WALK_CHILD) ==
1819 		    TOPO_WALK_ERR)
1820 			err = -1;
1821 		else
1822 			err = 0;
1823 		topo_walk_fini(hwp->hcw_wp);
1824 		topo_mod_free(mod, hwp, sizeof (struct hc_walk));
1825 	} else {
1826 		err = -1;
1827 	}
1828 
1829 	if (hap->ha_nvl != NULL)
1830 		*out = hap->ha_nvl;
1831 
1832 	topo_mod_free(mod, hap, sizeof (struct hc_args));
1833 
1834 	return (err);
1835 }
1836 
1837 static int
1838 hc_is_replaced(topo_mod_t *mod, tnode_t *node, void *pdata)
1839 {
1840 	int err;
1841 	struct hc_args *hap = (struct hc_args *)pdata;
1842 	uint32_t present = 0;
1843 	nvlist_t *rsrc;
1844 	uint32_t rval = FMD_OBJ_STATE_UNKNOWN;
1845 
1846 	/*
1847 	 * check with the enumerator that created this FMRI
1848 	 * (topo node)
1849 	 */
1850 	if (topo_method_invoke(node, TOPO_METH_REPLACED,
1851 	    TOPO_METH_REPLACED_VERSION, hap->ha_fmri, &hap->ha_nvl,
1852 	    &err) < 0) {
1853 		/*
1854 		 * If the method exists but failed for some other
1855 		 * reason, propagate the error as making any decision
1856 		 * over presence is impossible.
1857 		 */
1858 		if (err != ETOPO_METHOD_NOTSUP)
1859 			return (err);
1860 
1861 		/*
1862 		 * Enumerator didn't provide "replaced" method -
1863 		 * try "present" method
1864 		 */
1865 		if (topo_method_invoke(node, TOPO_METH_PRESENT,
1866 		    TOPO_METH_PRESENT_VERSION, hap->ha_fmri, &hap->ha_nvl,
1867 		    &err) < 0) {
1868 			/*
1869 			 * If the method exists but failed for some other
1870 			 * reason, propagate the error as making any decision
1871 			 * over presence is impossible.
1872 			 */
1873 			if (err != ETOPO_METHOD_NOTSUP)
1874 				return (err);
1875 
1876 			/*
1877 			 * Enumerator didn't provide "present" method either -
1878 			 * so check the authority information.  If the part id
1879 			 * or serial number doesn't match, then it isn't the
1880 			 * same FMRI. Otherwise, if we have a serial number and
1881 			 * it hasn't changed, then assume it is the same FMRI.
1882 			 */
1883 			if (topo_node_resource(node, &rsrc, &err) != 0)
1884 				return (err);
1885 			rval = hc_auth_changed(hap->ha_fmri, rsrc,
1886 			    FM_FMRI_HC_PART);
1887 			if (rval != FMD_OBJ_STATE_REPLACED)
1888 				rval = hc_auth_changed(hap->ha_fmri, rsrc,
1889 				    FM_FMRI_HC_SERIAL_ID);
1890 			nvlist_free(rsrc);
1891 			if (topo_mod_nvalloc(mod, &hap->ha_nvl,
1892 			    NV_UNIQUE_NAME) != 0)
1893 				return (EMOD_NOMEM);
1894 			if (nvlist_add_uint32(hap->ha_nvl,
1895 			    TOPO_METH_REPLACED_RET, rval) != 0) {
1896 				nvlist_free(hap->ha_nvl);
1897 				hap->ha_nvl = NULL;
1898 				return (ETOPO_PROP_NVL);
1899 			}
1900 		} else {
1901 			(void) nvlist_lookup_uint32(hap->ha_nvl,
1902 			    TOPO_METH_PRESENT_RET, &present);
1903 			(void) nvlist_remove(hap->ha_nvl,
1904 			    TOPO_METH_PRESENT_RET, DATA_TYPE_UINT32);
1905 			if (nvlist_add_uint32(hap->ha_nvl,
1906 			    TOPO_METH_REPLACED_RET,
1907 			    present ? FMD_OBJ_STATE_UNKNOWN :
1908 			    FMD_OBJ_STATE_NOT_PRESENT) != 0) {
1909 				nvlist_free(hap->ha_nvl);
1910 				hap->ha_nvl = NULL;
1911 				return (ETOPO_PROP_NVL);
1912 			}
1913 		}
1914 	}
1915 	return (0);
1916 }
1917 
1918 static int
1919 hc_fmri_replaced(topo_mod_t *mod, tnode_t *node, topo_version_t version,
1920     nvlist_t *in, nvlist_t **out)
1921 {
1922 	int err;
1923 	struct hc_walk *hwp;
1924 	struct hc_args *hap;
1925 
1926 	if (version > TOPO_METH_REPLACED_VERSION)
1927 		return (topo_mod_seterrno(mod, ETOPO_METHOD_VERNEW));
1928 
1929 	if ((hap = topo_mod_alloc(mod, sizeof (struct hc_args))) == NULL)
1930 		return (topo_mod_seterrno(mod, EMOD_NOMEM));
1931 
1932 	hap->ha_fmri = in;
1933 	hap->ha_nvl = NULL;
1934 	if ((hwp = hc_walk_init(mod, node, hap->ha_fmri, hc_is_replaced,
1935 	    (void *)hap)) != NULL) {
1936 		if (topo_walk_step(hwp->hcw_wp, TOPO_WALK_CHILD) ==
1937 		    TOPO_WALK_ERR)
1938 			err = -1;
1939 		else
1940 			err = 0;
1941 		topo_walk_fini(hwp->hcw_wp);
1942 		topo_mod_free(mod, hwp, sizeof (struct hc_walk));
1943 	} else {
1944 		err = -1;
1945 	}
1946 
1947 	if (hap->ha_nvl != NULL)
1948 		*out = hap->ha_nvl;
1949 
1950 	topo_mod_free(mod, hap, sizeof (struct hc_args));
1951 
1952 	return (err);
1953 }
1954 
1955 static int
1956 hc_unusable(topo_mod_t *mod, tnode_t *node, void *pdata)
1957 {
1958 	int err;
1959 	struct hc_args *hap = (struct hc_args *)pdata;
1960 
1961 	/*
1962 	 * check with the enumerator that created this FMRI
1963 	 * (topo node)
1964 	 */
1965 	if (topo_method_invoke(node, TOPO_METH_UNUSABLE,
1966 	    TOPO_METH_UNUSABLE_VERSION, hap->ha_fmri, &hap->ha_nvl,
1967 	    &err) < 0) {
1968 
1969 		/*
1970 		 * Err on the side of caution and return usable
1971 		 */
1972 		if (topo_mod_nvalloc(mod, &hap->ha_nvl, NV_UNIQUE_NAME) == 0)
1973 			if (nvlist_add_uint32(hap->ha_nvl,
1974 			    TOPO_METH_UNUSABLE_RET, 0) == 0)
1975 				return (0);
1976 
1977 		return (ETOPO_PROP_NVL);
1978 	}
1979 
1980 	return (0);
1981 }
1982 
1983 static int
1984 hc_fmri_unusable(topo_mod_t *mod, tnode_t *node, topo_version_t version,
1985     nvlist_t *in, nvlist_t **out)
1986 {
1987 	int err;
1988 	struct hc_walk *hwp;
1989 	struct hc_args *hap;
1990 
1991 	if (version > TOPO_METH_UNUSABLE_VERSION)
1992 		return (topo_mod_seterrno(mod, ETOPO_METHOD_VERNEW));
1993 
1994 	if ((hap = topo_mod_alloc(mod, sizeof (struct hc_args))) == NULL)
1995 		return (topo_mod_seterrno(mod, EMOD_NOMEM));
1996 
1997 	hap->ha_fmri = in;
1998 	hap->ha_nvl = NULL;
1999 	if ((hwp = hc_walk_init(mod, node, hap->ha_fmri, hc_unusable,
2000 	    (void *)hap)) != NULL) {
2001 		if (topo_walk_step(hwp->hcw_wp, TOPO_WALK_CHILD) ==
2002 		    TOPO_WALK_ERR)
2003 			err = -1;
2004 		else
2005 			err = 0;
2006 		topo_walk_fini(hwp->hcw_wp);
2007 		topo_mod_free(mod, hwp, sizeof (struct hc_walk));
2008 	} else {
2009 		err = -1;
2010 	}
2011 
2012 	if (hap->ha_nvl != NULL)
2013 		*out = hap->ha_nvl;
2014 
2015 	topo_mod_free(mod, hap, sizeof (struct hc_args));
2016 
2017 	return (err);
2018 }
2019 
2020 struct fac_lookup {
2021 	const char *fl_fac_type;
2022 	uint32_t fl_fac_subtype;
2023 #ifdef _LP64
2024 	uint64_t fl_callback;
2025 	uint64_t fl_callback_args;
2026 #else
2027 	uint32_t fl_callback;
2028 	uint32_t fl_callback_args;
2029 #endif
2030 	nvlist_t *fl_rsrc;
2031 	nvlist_t *fl_fac_rsrc;
2032 };
2033 
2034 static int
2035 hc_fac_get(topo_mod_t *mod, tnode_t *node, void *pdata)
2036 {
2037 	struct fac_lookup *flp = (struct fac_lookup *)pdata;
2038 	topo_walk_cb_t cb = (topo_walk_cb_t)flp->fl_callback;
2039 	topo_faclist_t faclist, *tmp;
2040 	int err, ret = 0;
2041 
2042 	/*
2043 	 * Lookup the specified facility node.  Return with an error if we can't
2044 	 * find it.
2045 	 */
2046 	if (topo_node_facility(mod->tm_hdl, node, flp->fl_fac_type,
2047 	    flp->fl_fac_subtype, &faclist, &err) != 0) {
2048 		topo_mod_dprintf(mod, "hc_fac_get: topo_node_facility "
2049 		    "failed\n");
2050 		return (TOPO_WALK_ERR);
2051 	}
2052 
2053 	/*
2054 	 * Invoke user's callback for each facility node in the topo list,
2055 	 * passing in a pointer to the facility node
2056 	 */
2057 	for (tmp = topo_list_next(&faclist.tf_list); tmp != NULL;
2058 	    tmp = topo_list_next(tmp)) {
2059 
2060 		if ((err = cb(mod->tm_hdl, tmp->tf_node,
2061 		    (void *)flp->fl_callback_args)) != 0) {
2062 			(void) topo_mod_seterrno(mod, err);
2063 			topo_mod_dprintf(mod, "hc_fac_get: callback failed: "
2064 			    "%s\n ", topo_mod_errmsg(mod));
2065 			ret = TOPO_WALK_ERR;
2066 			break;
2067 		}
2068 	}
2069 
2070 	while ((tmp = topo_list_next(&faclist.tf_list)) != NULL) {
2071 		topo_list_delete(&faclist.tf_list, tmp);
2072 		topo_mod_free(mod, tmp, sizeof (topo_faclist_t));
2073 	}
2074 	return (ret);
2075 }
2076 
2077 static int
2078 hc_fmri_facility(topo_mod_t *mod, tnode_t *node, topo_version_t version,
2079     nvlist_t *in, nvlist_t **out)
2080 {
2081 	int err = 0;
2082 	struct hc_walk *hwp;
2083 	struct fac_lookup *flp;
2084 
2085 	if (version > TOPO_METH_FACILITY_VERSION)
2086 		return (topo_mod_seterrno(mod, ETOPO_METHOD_VERNEW));
2087 
2088 	if ((flp = topo_mod_alloc(mod, sizeof (struct fac_lookup))) == NULL)
2089 		return (topo_mod_seterrno(mod, EMOD_NOMEM));
2090 
2091 	/*
2092 	 * lookup arguments: hw resource, facility type, facility subtype,
2093 	 *  callback and callback args
2094 	 */
2095 	err = nvlist_lookup_nvlist(in, TOPO_PROP_RESOURCE, &flp->fl_rsrc);
2096 	err |= nvlist_lookup_string(in, FM_FMRI_FACILITY_TYPE,
2097 	    (char **)&flp->fl_fac_type);
2098 	err |= nvlist_lookup_uint32(in, "type", &flp->fl_fac_subtype);
2099 #ifdef _LP64
2100 	err |= nvlist_lookup_uint64(in, "callback", &flp->fl_callback);
2101 	err |= nvlist_lookup_uint64(in, "callback-args",
2102 	    &flp->fl_callback_args);
2103 #else
2104 	err |= nvlist_lookup_uint32(in, "callback", &flp->fl_callback);
2105 	err |= nvlist_lookup_uint32(in, "callback-args",
2106 	    &flp->fl_callback_args);
2107 #endif
2108 	if (err != 0) {
2109 		topo_mod_dprintf(mod, "hc_fmri_facility: failed to construct "
2110 		    "walker arg nvlist\n");
2111 		topo_mod_free(mod, flp, sizeof (struct fac_lookup));
2112 		return (topo_mod_seterrno(mod, EMOD_METHOD_INVAL));
2113 	}
2114 
2115 	flp->fl_fac_rsrc = NULL;
2116 	if ((hwp = hc_walk_init(mod, node, flp->fl_rsrc, hc_fac_get,
2117 	    (void *)flp)) != NULL) {
2118 		if (topo_walk_step(hwp->hcw_wp, TOPO_WALK_CHILD) ==
2119 		    TOPO_WALK_ERR)
2120 			err = -1;
2121 		else
2122 			err = 0;
2123 		topo_walk_fini(hwp->hcw_wp);
2124 		topo_mod_free(mod, hwp, sizeof (struct hc_walk));
2125 	} else {
2126 		topo_mod_dprintf(mod, "hc_fmri_facility: failed to initialize "
2127 		    "hc walker\n");
2128 		err = -1;
2129 	}
2130 
2131 	if (flp->fl_fac_rsrc != NULL)
2132 		*out = flp->fl_fac_rsrc;
2133 
2134 	topo_mod_free(mod, flp, sizeof (struct fac_lookup));
2135 
2136 	return (err);
2137 }
2138 
2139 /* ARGSUSED */
2140 static int
2141 hc_expand(topo_mod_t *mod, tnode_t *node, void *pdata)
2142 {
2143 	int err;
2144 	nvlist_t *nvl;
2145 	const char **namep;
2146 	struct hc_args *hap = (struct hc_args *)pdata;
2147 	const char *names[] = {
2148 		FM_FMRI_HC_SERIAL_ID,
2149 		FM_FMRI_HC_PART,
2150 		FM_FMRI_HC_REVISION,
2151 		NULL
2152 	};
2153 
2154 	if (topo_node_resource(node, &nvl, &err) != 0)
2155 		return (ETOPO_METHOD_FAIL);
2156 
2157 	for (namep = names; *namep != NULL; namep++) {
2158 		char *in_val, *node_val;
2159 
2160 		if (nvlist_lookup_string(nvl, *namep, &node_val) != 0)
2161 			continue;
2162 
2163 		if (nvlist_lookup_string(hap->ha_fmri, *namep, &in_val) == 0) {
2164 			if (strcmp(in_val, node_val) == 0)
2165 				continue;
2166 			(void) nvlist_remove(hap->ha_fmri, *namep,
2167 			    DATA_TYPE_STRING);
2168 		}
2169 
2170 		if (nvlist_add_string(hap->ha_fmri, *namep, node_val) != 0) {
2171 			nvlist_free(nvl);
2172 			return (ETOPO_PROP_NVL);
2173 		}
2174 	}
2175 	nvlist_free(nvl);
2176 
2177 	return (0);
2178 }
2179 
2180 /* ARGSUSED */
2181 static int
2182 hc_fmri_expand(topo_mod_t *mod, tnode_t *node, topo_version_t version,
2183     nvlist_t *in, nvlist_t **out)
2184 {
2185 	int err;
2186 	struct hc_walk *hwp;
2187 	struct hc_args *hap;
2188 
2189 	if (version > TOPO_METH_EXPAND_VERSION)
2190 		return (topo_mod_seterrno(mod, ETOPO_METHOD_VERNEW));
2191 
2192 	if ((hap = topo_mod_alloc(mod, sizeof (struct hc_args))) == NULL)
2193 		return (topo_mod_seterrno(mod, EMOD_NOMEM));
2194 
2195 	hap->ha_fmri = in;
2196 	hap->ha_nvl = NULL;
2197 	if ((hwp = hc_walk_init(mod, node, hap->ha_fmri, hc_expand,
2198 	    (void *)hap)) != NULL) {
2199 		if (topo_walk_step(hwp->hcw_wp, TOPO_WALK_CHILD) ==
2200 		    TOPO_WALK_ERR)
2201 			err = -1;
2202 		else
2203 			err = 0;
2204 		topo_walk_fini(hwp->hcw_wp);
2205 	} else {
2206 		err = -1;
2207 	}
2208 
2209 	topo_mod_free(mod, hwp, sizeof (struct hc_walk));
2210 
2211 	/* expand method should not return out nvlist */
2212 	assert(hap->ha_nvl == NULL);
2213 
2214 	topo_mod_free(mod, hap, sizeof (struct hc_args));
2215 
2216 	return (err);
2217 }
2218 
2219 static int
2220 hc_retire_subr(topo_mod_t *mod, tnode_t *node, void *pdata)
2221 {
2222 	int err, rc;
2223 	struct hc_args *hap = (struct hc_args *)pdata;
2224 
2225 	topo_mod_dprintf(mod, "hc_retire_subr: invoking method %s\n",
2226 	    hap->ha_method_name);
2227 	/*
2228 	 * check with the enumerator that created this FMRI
2229 	 * (topo node)
2230 	 */
2231 	rc = topo_method_invoke(node, hap->ha_method_name,
2232 	    hap->ha_method_ver, hap->ha_fmri, &hap->ha_nvl, &err);
2233 
2234 	topo_mod_dprintf(mod, "hc_retire_subr: invoking method %s "
2235 	    "returned %d\n", hap->ha_method_name, rc);
2236 
2237 	return (rc < 0 ? err : 0);
2238 }
2239 
2240 static int
2241 hc_fmri_retire_subr(topo_mod_t *mod, tnode_t *node, char *method_name,
2242     topo_version_t builtin_version, topo_version_t version, nvlist_t *in,
2243     nvlist_t **out)
2244 {
2245 	int err;
2246 	struct hc_walk *hwp;
2247 	struct hc_args *hap;
2248 
2249 	if (version > builtin_version)
2250 		return (topo_mod_seterrno(mod, ETOPO_METHOD_VERNEW));
2251 
2252 	if ((hap = topo_mod_alloc(mod, sizeof (struct hc_args))) == NULL)
2253 		return (topo_mod_seterrno(mod, EMOD_NOMEM));
2254 
2255 	hap->ha_fmri = in;
2256 	hap->ha_nvl = NULL;
2257 	hap->ha_method_name = method_name;
2258 	hap->ha_method_ver = version;
2259 	if ((hwp = hc_walk_init(mod, node, hap->ha_fmri, hc_retire_subr,
2260 	    (void *)hap)) != NULL) {
2261 		if (topo_walk_step(hwp->hcw_wp, TOPO_WALK_CHILD) ==
2262 		    TOPO_WALK_ERR)
2263 			err = -1;
2264 		else
2265 			err = 0;
2266 		topo_walk_fini(hwp->hcw_wp);
2267 	} else {
2268 		err = -1;
2269 	}
2270 
2271 	topo_mod_free(mod, hwp, sizeof (struct hc_walk));
2272 
2273 	if (hap->ha_nvl != NULL)
2274 		*out = hap->ha_nvl;
2275 
2276 	topo_mod_free(mod, hap, sizeof (struct hc_args));
2277 
2278 	return (err);
2279 }
2280 
2281 static int
2282 hc_fmri_retire(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_RETIRE,
2286 	    TOPO_METH_RETIRE_VERSION, version, in, out));
2287 }
2288 
2289 static int
2290 hc_fmri_unretire(topo_mod_t *mod, tnode_t *node, topo_version_t version,
2291     nvlist_t *in, nvlist_t **out)
2292 {
2293 	return (hc_fmri_retire_subr(mod, node, TOPO_METH_UNRETIRE,
2294 	    TOPO_METH_UNRETIRE_VERSION, version, in, out));
2295 }
2296 
2297 static int
2298 hc_fmri_service_state(topo_mod_t *mod, tnode_t *node, topo_version_t version,
2299     nvlist_t *in, nvlist_t **out)
2300 {
2301 	return (hc_fmri_retire_subr(mod, node, TOPO_METH_SERVICE_STATE,
2302 	    TOPO_METH_SERVICE_STATE_VERSION, version, in, out));
2303 }
2304