xref: /titanic_41/usr/src/lib/fm/topo/libtopo/common/hc.c (revision 40db2e2b777b79f3dd0d6d9629593a07f86b9c0a)
1 /*
2  *
3  * CDDL HEADER START
4  *
5  * The contents of this file are subject to the terms of the
6  * Common Development and Distribution License (the "License").
7  * You may not use this file except in compliance with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 
23 /*
24  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
25  * Use is subject to license terms.
26  */
27 
28 #pragma ident	"%Z%%M%	%I%	%E% SMI"
29 
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <string.h>
33 #include <errno.h>
34 #include <ctype.h>
35 #include <alloca.h>
36 #include <limits.h>
37 #include <fm/topo_mod.h>
38 #include <fm/topo_hc.h>
39 #include <sys/param.h>
40 #include <sys/systeminfo.h>
41 #include <sys/fm/protocol.h>
42 #include <sys/stat.h>
43 #include <sys/systeminfo.h>
44 #include <sys/utsname.h>
45 
46 #include <topo_method.h>
47 #include <topo_subr.h>
48 #include <topo_prop.h>
49 #include <hc.h>
50 
51 static int hc_enum(topo_mod_t *, tnode_t *, const char *, topo_instance_t,
52     topo_instance_t, void *, void *);
53 static void hc_release(topo_mod_t *, tnode_t *);
54 static int hc_fmri_nvl2str(topo_mod_t *, tnode_t *, topo_version_t,
55     nvlist_t *, nvlist_t **);
56 static int hc_fmri_str2nvl(topo_mod_t *, tnode_t *, topo_version_t,
57     nvlist_t *, nvlist_t **);
58 static int hc_compare(topo_mod_t *, tnode_t *, topo_version_t, nvlist_t *,
59     nvlist_t **);
60 static int hc_fmri_present(topo_mod_t *, tnode_t *, topo_version_t, nvlist_t *,
61     nvlist_t **);
62 static int hc_fmri_unusable(topo_mod_t *, tnode_t *, topo_version_t, nvlist_t *,
63     nvlist_t **);
64 static int hc_fmri_create_meth(topo_mod_t *, tnode_t *, topo_version_t,
65     nvlist_t *, nvlist_t **);
66 static int hc_fmri_prop_get(topo_mod_t *, tnode_t *, topo_version_t,
67     nvlist_t *, nvlist_t **);
68 static int hc_fmri_prop_set(topo_mod_t *, tnode_t *, topo_version_t,
69     nvlist_t *, nvlist_t **);
70 static int hc_fmri_pgrp_get(topo_mod_t *, tnode_t *, topo_version_t,
71     nvlist_t *, nvlist_t **);
72 
73 static nvlist_t *hc_fmri_create(topo_mod_t *, nvlist_t *, int, const char *,
74     topo_instance_t inst, const nvlist_t *, const char *, const char *,
75     const char *);
76 
77 const topo_method_t hc_methods[] = {
78 	{ TOPO_METH_NVL2STR, TOPO_METH_NVL2STR_DESC, TOPO_METH_NVL2STR_VERSION,
79 	    TOPO_STABILITY_INTERNAL, hc_fmri_nvl2str },
80 	{ TOPO_METH_STR2NVL, TOPO_METH_STR2NVL_DESC, TOPO_METH_STR2NVL_VERSION,
81 	    TOPO_STABILITY_INTERNAL, hc_fmri_str2nvl },
82 	{ TOPO_METH_COMPARE, TOPO_METH_COMPARE_DESC, TOPO_METH_COMPARE_VERSION,
83 	    TOPO_STABILITY_INTERNAL, hc_compare },
84 	{ TOPO_METH_PRESENT, TOPO_METH_PRESENT_DESC, TOPO_METH_PRESENT_VERSION,
85 	    TOPO_STABILITY_INTERNAL, hc_fmri_present },
86 	{ TOPO_METH_UNUSABLE, TOPO_METH_UNUSABLE_DESC,
87 	    TOPO_METH_UNUSABLE_VERSION, TOPO_STABILITY_INTERNAL,
88 	    hc_fmri_unusable },
89 	{ TOPO_METH_FMRI, TOPO_METH_FMRI_DESC, TOPO_METH_FMRI_VERSION,
90 	    TOPO_STABILITY_INTERNAL, hc_fmri_create_meth },
91 	{ TOPO_METH_PROP_GET, TOPO_METH_PROP_GET_DESC,
92 	    TOPO_METH_PROP_GET_VERSION, TOPO_STABILITY_INTERNAL,
93 	    hc_fmri_prop_get },
94 	{ TOPO_METH_PROP_SET, TOPO_METH_PROP_SET_DESC,
95 	    TOPO_METH_PROP_SET_VERSION, TOPO_STABILITY_INTERNAL,
96 	    hc_fmri_prop_set },
97 	{ TOPO_METH_PGRP_GET, TOPO_METH_PGRP_GET_DESC,
98 	    TOPO_METH_PGRP_GET_VERSION, TOPO_STABILITY_INTERNAL,
99 	    hc_fmri_pgrp_get },
100 	{ NULL }
101 };
102 
103 static const topo_modops_t hc_ops =
104 	{ hc_enum, hc_release };
105 static const topo_modinfo_t hc_info =
106 	{ HC, FM_FMRI_SCHEME_HC, HC_VERSION, &hc_ops };
107 
108 static const hcc_t hc_canon[] = {
109 	{ BAY, TOPO_STABILITY_PRIVATE },
110 	{ BRANCH, TOPO_STABILITY_PRIVATE },
111 	{ CMP, TOPO_STABILITY_PRIVATE },
112 	{ CENTERPLANE, TOPO_STABILITY_PRIVATE },
113 	{ CHASSIS, TOPO_STABILITY_PRIVATE },
114 	{ CHIP, TOPO_STABILITY_PRIVATE },
115 	{ CHIP_SELECT, TOPO_STABILITY_PRIVATE },
116 	{ CPU, TOPO_STABILITY_PRIVATE },
117 	{ CPUBOARD, TOPO_STABILITY_PRIVATE },
118 	{ DIMM, TOPO_STABILITY_PRIVATE },
119 	{ DISK, TOPO_STABILITY_PRIVATE },
120 	{ DRAMCHANNEL, TOPO_STABILITY_PRIVATE },
121 	{ FAN, TOPO_STABILITY_PRIVATE },
122 	{ FANMODULE, TOPO_STABILITY_PRIVATE },
123 	{ HOSTBRIDGE, TOPO_STABILITY_PRIVATE },
124 	{ INTERCONNECT, TOPO_STABILITY_PRIVATE },
125 	{ IOBOARD, TOPO_STABILITY_PRIVATE },
126 	{ MEMBOARD, TOPO_STABILITY_PRIVATE },
127 	{ MEMORYCONTROL, TOPO_STABILITY_PRIVATE },
128 	{ MOTHERBOARD, TOPO_STABILITY_PRIVATE },
129 	{ NIU, TOPO_STABILITY_PRIVATE },
130 	{ NIUFN, TOPO_STABILITY_PRIVATE },
131 	{ PCI_BUS, TOPO_STABILITY_PRIVATE },
132 	{ PCI_DEVICE, TOPO_STABILITY_PRIVATE },
133 	{ PCI_FUNCTION, TOPO_STABILITY_PRIVATE },
134 	{ PCIEX_BUS, TOPO_STABILITY_PRIVATE },
135 	{ PCIEX_DEVICE, TOPO_STABILITY_PRIVATE },
136 	{ PCIEX_FUNCTION, TOPO_STABILITY_PRIVATE },
137 	{ PCIEX_ROOT, TOPO_STABILITY_PRIVATE },
138 	{ PCIEX_SWUP, TOPO_STABILITY_PRIVATE },
139 	{ PCIEX_SWDWN, TOPO_STABILITY_PRIVATE },
140 	{ POWERMODULE, TOPO_STABILITY_PRIVATE },
141 	{ PSU, TOPO_STABILITY_PRIVATE },
142 	{ RANK, TOPO_STABILITY_PRIVATE },
143 	{ SYSTEMBOARD, TOPO_STABILITY_PRIVATE },
144 	{ XAUI, TOPO_STABILITY_PRIVATE },
145 	{ XFP, TOPO_STABILITY_PRIVATE }
146 };
147 
148 static int hc_ncanon = sizeof (hc_canon) / sizeof (hcc_t);
149 
150 int
151 hc_init(topo_mod_t *mod, topo_version_t version)
152 {
153 	/*
154 	 * Turn on module debugging output
155 	 */
156 	if (getenv("TOPOHCDEBUG"))
157 		topo_mod_setdebug(mod);
158 
159 	topo_mod_dprintf(mod, "initializing hc builtin\n");
160 
161 	if (version != HC_VERSION)
162 		return (topo_mod_seterrno(mod, EMOD_VER_NEW));
163 
164 	if (topo_mod_register(mod, &hc_info, TOPO_VERSION) != 0) {
165 		topo_mod_dprintf(mod, "failed to register hc: "
166 		    "%s\n", topo_mod_errmsg(mod));
167 		return (-1); /* mod errno already set */
168 	}
169 
170 	return (0);
171 }
172 
173 void
174 hc_fini(topo_mod_t *mod)
175 {
176 	topo_mod_unregister(mod);
177 }
178 
179 
180 static const topo_pgroup_info_t sys_pgroup = {
181 	TOPO_PGROUP_SYSTEM,
182 	TOPO_STABILITY_PRIVATE,
183 	TOPO_STABILITY_PRIVATE,
184 	1
185 };
186 
187 static const topo_pgroup_info_t auth_pgroup = {
188 	FM_FMRI_AUTHORITY,
189 	TOPO_STABILITY_PRIVATE,
190 	TOPO_STABILITY_PRIVATE,
191 	1
192 };
193 
194 static void
195 hc_prop_set(tnode_t *node, nvlist_t *auth)
196 {
197 	int err;
198 	char isa[MAXNAMELEN];
199 	struct utsname uts;
200 	char *prod, *csn, *server;
201 
202 	if (auth == NULL)
203 		return;
204 
205 	if (topo_pgroup_create(node, &auth_pgroup, &err) != 0) {
206 		if (err != ETOPO_PROP_DEFD)
207 			return;
208 	}
209 
210 	/*
211 	 * Inherit if we can, it saves memory
212 	 */
213 	if ((topo_prop_inherit(node, FM_FMRI_AUTHORITY, FM_FMRI_AUTH_PRODUCT,
214 	    &err) != 0) && (err != ETOPO_PROP_DEFD)) {
215 		if (nvlist_lookup_string(auth, FM_FMRI_AUTH_PRODUCT, &prod)
216 		    == 0)
217 			(void) topo_prop_set_string(node, FM_FMRI_AUTHORITY,
218 			    FM_FMRI_AUTH_PRODUCT, TOPO_PROP_IMMUTABLE, prod,
219 			    &err);
220 	}
221 	if ((topo_prop_inherit(node, FM_FMRI_AUTHORITY, FM_FMRI_AUTH_CHASSIS,
222 	    &err) != 0) && (err != ETOPO_PROP_DEFD)) {
223 		if (nvlist_lookup_string(auth, FM_FMRI_AUTH_CHASSIS, &csn) == 0)
224 			(void) topo_prop_set_string(node, FM_FMRI_AUTHORITY,
225 			    FM_FMRI_AUTH_CHASSIS, TOPO_PROP_IMMUTABLE, csn,
226 			    &err);
227 	}
228 	if ((topo_prop_inherit(node, FM_FMRI_AUTHORITY, FM_FMRI_AUTH_SERVER,
229 	    &err) != 0) && (err != ETOPO_PROP_DEFD)) {
230 		if (nvlist_lookup_string(auth, FM_FMRI_AUTH_SERVER, &server)
231 		    == 0)
232 			(void) topo_prop_set_string(node, FM_FMRI_AUTHORITY,
233 			    FM_FMRI_AUTH_SERVER, TOPO_PROP_IMMUTABLE, server,
234 			    &err);
235 	}
236 
237 	if (topo_pgroup_create(node, &sys_pgroup, &err) != 0)
238 		return;
239 
240 	isa[0] = '\0';
241 	(void) sysinfo(SI_ARCHITECTURE, isa, sizeof (isa));
242 	(void) uname(&uts);
243 	(void) topo_prop_set_string(node, TOPO_PGROUP_SYSTEM, TOPO_PROP_ISA,
244 	    TOPO_PROP_IMMUTABLE, isa, &err);
245 	(void) topo_prop_set_string(node, TOPO_PGROUP_SYSTEM, TOPO_PROP_MACHINE,
246 	    TOPO_PROP_IMMUTABLE, uts.machine, &err);
247 }
248 
249 /*ARGSUSED*/
250 int
251 hc_enum(topo_mod_t *mod, tnode_t *pnode, const char *name, topo_instance_t min,
252     topo_instance_t max, void *notused1, void *notused2)
253 {
254 	nvlist_t *pfmri = NULL;
255 	nvlist_t *nvl;
256 	nvlist_t *auth;
257 	tnode_t *node;
258 	int err;
259 	/*
260 	 * Register root node methods
261 	 */
262 	if (strcmp(name, HC) == 0) {
263 		(void) topo_method_register(mod, pnode, hc_methods);
264 		return (0);
265 	}
266 	if (min != max) {
267 		topo_mod_dprintf(mod,
268 		    "Request to enumerate %s component with an "
269 		    "ambiguous instance number, min (%d) != max (%d).\n",
270 		    HC, min, max);
271 		return (topo_mod_seterrno(mod, EINVAL));
272 	}
273 
274 	(void) topo_node_resource(pnode, &pfmri, &err);
275 	auth = topo_mod_auth(mod, pnode);
276 	nvl = hc_fmri_create(mod, pfmri, FM_HC_SCHEME_VERSION, name, min,
277 	    auth, NULL, NULL, NULL);
278 	nvlist_free(pfmri);	/* callee ignores NULLs */
279 	if (nvl == NULL) {
280 		nvlist_free(auth);
281 		return (-1);
282 	}
283 
284 	if ((node = topo_node_bind(mod, pnode, name, min, nvl)) == NULL) {
285 		topo_mod_dprintf(mod, "topo_node_bind failed: %s\n",
286 		    topo_strerror(topo_mod_errno(mod)));
287 		nvlist_free(auth);
288 		nvlist_free(nvl);
289 		return (-1);
290 	}
291 
292 	/*
293 	 * Set FRU for the motherboard node
294 	 */
295 	if (strcmp(name, MOTHERBOARD) == 0)
296 		(void) topo_node_fru_set(node, nvl, 0, &err);
297 
298 	hc_prop_set(node, auth);
299 	nvlist_free(nvl);
300 	nvlist_free(auth);
301 
302 	return (0);
303 }
304 
305 /*ARGSUSED*/
306 static void
307 hc_release(topo_mod_t *mp, tnode_t *node)
308 {
309 	topo_method_unregister_all(mp, node);
310 }
311 
312 static int
313 fmri_compare(topo_mod_t *mod, nvlist_t *nv1, nvlist_t *nv2)
314 {
315 	uint8_t v1, v2;
316 	nvlist_t **hcp1, **hcp2;
317 	int err, i;
318 	uint_t nhcp1, nhcp2;
319 
320 	if (nvlist_lookup_uint8(nv1, FM_VERSION, &v1) != 0 ||
321 	    nvlist_lookup_uint8(nv2, FM_VERSION, &v2) != 0 ||
322 	    v1 > FM_HC_SCHEME_VERSION || v2 > FM_HC_SCHEME_VERSION)
323 		return (topo_mod_seterrno(mod, EMOD_FMRI_VERSION));
324 
325 	err = nvlist_lookup_nvlist_array(nv1, FM_FMRI_HC_LIST, &hcp1, &nhcp1);
326 	err |= nvlist_lookup_nvlist_array(nv2, FM_FMRI_HC_LIST, &hcp2, &nhcp2);
327 	if (err != 0)
328 		return (topo_mod_seterrno(mod, EMOD_FMRI_NVL));
329 
330 	if (nhcp1 != nhcp2)
331 		return (0);
332 
333 	for (i = 0; i < nhcp1; i++) {
334 		char *nm1 = NULL;
335 		char *nm2 = NULL;
336 		char *id1 = NULL;
337 		char *id2 = NULL;
338 
339 		(void) nvlist_lookup_string(hcp1[i], FM_FMRI_HC_NAME, &nm1);
340 		(void) nvlist_lookup_string(hcp2[i], FM_FMRI_HC_NAME, &nm2);
341 		(void) nvlist_lookup_string(hcp1[i], FM_FMRI_HC_ID, &id1);
342 		(void) nvlist_lookup_string(hcp2[i], FM_FMRI_HC_ID, &id2);
343 		if (nm1 == NULL || nm2 == NULL || id1 == NULL || id2 == NULL)
344 			return (topo_mod_seterrno(mod, EMOD_FMRI_NVL));
345 
346 		if (strcmp(nm1, nm2) == 0 && strcmp(id1, id2) == 0)
347 			continue;
348 
349 		return (0);
350 	}
351 
352 	return (1);
353 }
354 
355 /*ARGSUSED*/
356 static int
357 hc_compare(topo_mod_t *mod, tnode_t *node, topo_version_t version,
358     nvlist_t *in, nvlist_t **out)
359 {
360 	int ret;
361 	uint32_t compare;
362 	nvlist_t *nv1, *nv2;
363 
364 	if (version > TOPO_METH_COMPARE_VERSION)
365 		return (topo_mod_seterrno(mod, EMOD_VER_NEW));
366 
367 	if (nvlist_lookup_nvlist(in, TOPO_METH_FMRI_ARG_NV1, &nv1) != 0 ||
368 	    nvlist_lookup_nvlist(in, TOPO_METH_FMRI_ARG_NV2, &nv2) != 0)
369 		return (topo_mod_seterrno(mod, EMOD_METHOD_INVAL));
370 
371 	ret = fmri_compare(mod, nv1, nv2);
372 	if (ret < 0)
373 		return (-1);
374 
375 	compare = ret;
376 	if (topo_mod_nvalloc(mod, out, NV_UNIQUE_NAME) == 0) {
377 		if (nvlist_add_uint32(*out, TOPO_METH_COMPARE_RET,
378 		    compare) == 0)
379 			return (0);
380 		else
381 			nvlist_free(*out);
382 	}
383 
384 	return (-1);
385 }
386 
387 static ssize_t
388 fmri_nvl2str(nvlist_t *nvl, char *buf, size_t buflen)
389 {
390 	nvlist_t **hcprs = NULL;
391 	nvlist_t *anvl = NULL;
392 	uint8_t version;
393 	ssize_t size = 0;
394 	uint_t hcnprs;
395 	char *achas = NULL;
396 	char *adom = NULL;
397 	char *aprod = NULL;
398 	char *asrvr = NULL;
399 	char *ahost = NULL;
400 	char *serial = NULL;
401 	char *part = NULL;
402 	char *root = NULL;
403 	char *rev = NULL;
404 	int more_auth = 0;
405 	int err, i;
406 
407 	if (nvlist_lookup_uint8(nvl, FM_VERSION, &version) != 0 ||
408 	    version > FM_HC_SCHEME_VERSION)
409 		return (-1);
410 
411 	/* Get authority, if present */
412 	err = nvlist_lookup_nvlist(nvl, FM_FMRI_AUTHORITY, &anvl);
413 	if (err != 0 && err != ENOENT)
414 		return (-1);
415 
416 	if ((err = nvlist_lookup_string(nvl, FM_FMRI_HC_ROOT, &root)) != 0)
417 		return (-1);
418 
419 	err = nvlist_lookup_nvlist_array(nvl, FM_FMRI_HC_LIST, &hcprs, &hcnprs);
420 	if (err != 0 || hcprs == NULL)
421 		return (-1);
422 
423 	if (anvl != NULL) {
424 		(void) nvlist_lookup_string(anvl,
425 		    FM_FMRI_AUTH_PRODUCT, &aprod);
426 		(void) nvlist_lookup_string(anvl,
427 		    FM_FMRI_AUTH_CHASSIS, &achas);
428 		(void) nvlist_lookup_string(anvl,
429 		    FM_FMRI_AUTH_DOMAIN, &adom);
430 		(void) nvlist_lookup_string(anvl,
431 		    FM_FMRI_AUTH_SERVER, &asrvr);
432 		(void) nvlist_lookup_string(anvl,
433 		    FM_FMRI_AUTH_HOST, &ahost);
434 		if (aprod != NULL)
435 			more_auth++;
436 		if (achas != NULL)
437 			more_auth++;
438 		if (adom != NULL)
439 			more_auth++;
440 		if (asrvr != NULL)
441 			more_auth++;
442 		if (ahost != NULL)
443 			more_auth++;
444 	}
445 
446 	(void) nvlist_lookup_string(nvl, FM_FMRI_HC_SERIAL_ID, &serial);
447 	(void) nvlist_lookup_string(nvl, FM_FMRI_HC_PART, &part);
448 	(void) nvlist_lookup_string(nvl, FM_FMRI_HC_REVISION, &rev);
449 
450 	/* hc:// */
451 	topo_fmristr_build(&size, buf, buflen, FM_FMRI_SCHEME_HC, NULL, "://");
452 
453 	/* authority, if any */
454 	if (aprod != NULL)
455 		topo_fmristr_build(&size,
456 		    buf, buflen, aprod, ":" FM_FMRI_AUTH_PRODUCT "=", NULL);
457 	if (achas != NULL)
458 		topo_fmristr_build(&size,
459 		    buf, buflen, achas, ":" FM_FMRI_AUTH_CHASSIS "=", NULL);
460 	if (adom != NULL)
461 		topo_fmristr_build(&size,
462 		    buf, buflen, adom, ":" FM_FMRI_AUTH_DOMAIN "=", NULL);
463 	if (asrvr != NULL)
464 		topo_fmristr_build(&size,
465 		    buf, buflen, asrvr, ":" FM_FMRI_AUTH_SERVER "=", NULL);
466 	if (ahost != NULL)
467 		topo_fmristr_build(&size,
468 		    buf, buflen, ahost, ":" FM_FMRI_AUTH_HOST "=", NULL);
469 
470 	/* hardware-id part */
471 	topo_fmristr_build(&size,
472 	    buf, buflen, serial, ":" FM_FMRI_HC_SERIAL_ID "=", NULL);
473 	topo_fmristr_build(&size,
474 	    buf, buflen, part, ":" FM_FMRI_HC_PART "=", NULL);
475 	topo_fmristr_build(&size,
476 	    buf, buflen, rev, ":" FM_FMRI_HC_REVISION "=", NULL);
477 
478 	/* separating slash */
479 	topo_fmristr_build(&size, buf, buflen, "/", NULL, NULL);
480 
481 	/* hc-root */
482 	topo_fmristr_build(&size, buf, buflen, root, NULL, NULL);
483 
484 	/* all the pairs */
485 	for (i = 0; i < hcnprs; i++) {
486 		char *nm = NULL;
487 		char *id = NULL;
488 
489 		if (i > 0)
490 			topo_fmristr_build(&size,
491 			    buf, buflen, "/", NULL, NULL);
492 		(void) nvlist_lookup_string(hcprs[i], FM_FMRI_HC_NAME, &nm);
493 		(void) nvlist_lookup_string(hcprs[i], FM_FMRI_HC_ID, &id);
494 		if (nm == NULL || id == NULL)
495 			return (0);
496 		topo_fmristr_build(&size, buf, buflen, nm, NULL, "=");
497 		topo_fmristr_build(&size, buf, buflen, id, NULL, NULL);
498 	}
499 
500 	return (size);
501 }
502 
503 /*ARGSUSED*/
504 static int
505 hc_fmri_nvl2str(topo_mod_t *mod, tnode_t *node, topo_version_t version,
506     nvlist_t *nvl, nvlist_t **out)
507 {
508 	ssize_t len;
509 	char *name = NULL;
510 	nvlist_t *fmristr;
511 
512 	if (version > TOPO_METH_NVL2STR_VERSION)
513 		return (topo_mod_seterrno(mod, EMOD_VER_NEW));
514 
515 	if ((len = fmri_nvl2str(nvl, NULL, 0)) == 0 ||
516 	    (name = topo_mod_alloc(mod, len + 1)) == NULL ||
517 	    fmri_nvl2str(nvl, name, len + 1) == 0) {
518 		if (name != NULL)
519 			topo_mod_free(mod, name, len + 1);
520 		return (topo_mod_seterrno(mod, EMOD_FMRI_NVL));
521 	}
522 
523 	if (topo_mod_nvalloc(mod, &fmristr, NV_UNIQUE_NAME) != 0) {
524 		topo_mod_free(mod, name, len + 1);
525 		return (topo_mod_seterrno(mod, EMOD_FMRI_NVL));
526 	}
527 	if (nvlist_add_string(fmristr, "fmri-string", name) != 0) {
528 		topo_mod_free(mod, name, len + 1);
529 		nvlist_free(fmristr);
530 		return (topo_mod_seterrno(mod, EMOD_FMRI_NVL));
531 	}
532 	topo_mod_free(mod, name, len + 1);
533 	*out = fmristr;
534 
535 	return (0);
536 }
537 
538 static nvlist_t *
539 hc_base_fmri_create(topo_mod_t *mod, const nvlist_t *auth, const char *part,
540     const char *rev, const char *serial)
541 {
542 	nvlist_t *fmri;
543 	int err = 0;
544 
545 	/*
546 	 * Create base HC nvlist
547 	 */
548 	if (topo_mod_nvalloc(mod, &fmri, NV_UNIQUE_NAME) != 0)
549 		return (NULL);
550 
551 	err = nvlist_add_uint8(fmri, FM_VERSION, FM_HC_SCHEME_VERSION);
552 	err |= nvlist_add_string(fmri, FM_FMRI_SCHEME, FM_FMRI_SCHEME_HC);
553 	err |= nvlist_add_string(fmri, FM_FMRI_HC_ROOT, "");
554 	if (err != 0) {
555 		nvlist_free(fmri);
556 		return (NULL);
557 	}
558 
559 	/*
560 	 * Add optional payload members
561 	 */
562 	if (serial != NULL)
563 		(void) nvlist_add_string(fmri, FM_FMRI_HC_SERIAL_ID, serial);
564 	if (part != NULL)
565 		(void) nvlist_add_string(fmri, FM_FMRI_HC_PART, part);
566 	if (rev != NULL)
567 		(void) nvlist_add_string(fmri, FM_FMRI_HC_REVISION, rev);
568 	if (auth != NULL)
569 		(void) nvlist_add_nvlist(fmri, FM_FMRI_AUTHORITY,
570 		    (nvlist_t *)auth);
571 
572 	return (fmri);
573 }
574 
575 static nvlist_t **
576 make_hc_pairs(topo_mod_t *mod, char *fmri, int *num)
577 {
578 	nvlist_t **pa;
579 	char *hc, *fromstr;
580 	char *starti, *startn, *endi, *endi2;
581 	char *ne, *ns;
582 	char *cname = NULL;
583 	char *find;
584 	char *cid = NULL;
585 	int nslashes = 0;
586 	int npairs = 0;
587 	int i, hclen;
588 
589 	if ((hc = topo_mod_strdup(mod, fmri + 5)) == NULL)
590 		return (NULL);
591 
592 	hclen = strlen(hc) + 1;
593 
594 	/*
595 	 * Count equal signs and slashes to determine how many
596 	 * hc-pairs will be present in the final FMRI.  There should
597 	 * be at least as many slashes as equal signs.  There can be
598 	 * more, though if the string after an = includes them.
599 	 */
600 	if ((fromstr = strchr(hc, '/')) == NULL)
601 		return (NULL);
602 
603 	find = fromstr;
604 	while ((ne = strchr(find, '=')) != NULL) {
605 		find = ne + 1;
606 		npairs++;
607 	}
608 
609 	find = fromstr;
610 	while ((ns = strchr(find, '/')) != NULL) {
611 		find = ns + 1;
612 		nslashes++;
613 	}
614 
615 	/*
616 	 * Do we appear to have a well-formed string version of the FMRI?
617 	 */
618 	if (nslashes < npairs || npairs == 0) {
619 		topo_mod_free(mod, hc, hclen);
620 		return (NULL);
621 	}
622 
623 	*num = npairs;
624 
625 	find = fromstr;
626 
627 	if ((pa = topo_mod_zalloc(mod, npairs * sizeof (nvlist_t *))) == NULL) {
628 		topo_mod_free(mod, hc, hclen);
629 		return (NULL);
630 	}
631 
632 	/*
633 	 * We go through a pretty complicated procedure to find the
634 	 * name and id for each pair.  That's because, unfortunately,
635 	 * we have some ids that can have slashes within them.  So
636 	 * we can't just search for the next slash after the equal sign
637 	 * and decide that starts a new pair.  Instead we have to find
638 	 * an equal sign for the next pair and work our way back to the
639 	 * slash from there.
640 	 */
641 	for (i = 0; i < npairs; i++) {
642 		startn = strchr(find, '/');
643 		if (startn == NULL)
644 			break;
645 		startn++;
646 		starti = strchr(find, '=');
647 		if (starti == NULL)
648 			break;
649 		*starti = '\0';
650 		if ((cname = topo_mod_strdup(mod, startn)) == NULL)
651 			break;
652 		*starti++ = '=';
653 		endi = strchr(starti, '=');
654 		if (endi != NULL) {
655 			*endi = '\0';
656 			endi2 = strrchr(starti, '/');
657 			if (endi2 == NULL)
658 				break;
659 			*endi = '=';
660 			*endi2 = '\0';
661 			if ((cid = topo_mod_strdup(mod, starti)) == NULL)
662 				break;
663 			*endi2 = '/';
664 			find = endi2;
665 		} else {
666 			if ((cid = topo_mod_strdup(mod, starti)) == NULL)
667 				break;
668 			find = starti + strlen(starti);
669 		}
670 		if (topo_mod_nvalloc(mod, &pa[i], NV_UNIQUE_NAME) < 0)
671 			break;
672 
673 		if (nvlist_add_string(pa[i], FM_FMRI_HC_NAME, cname) ||
674 		    nvlist_add_string(pa[i], FM_FMRI_HC_ID, cid))
675 			break;
676 
677 		topo_mod_strfree(mod, cname);
678 		topo_mod_strfree(mod, cid);
679 		cname = NULL;
680 		cid = NULL;
681 	}
682 
683 	topo_mod_strfree(mod, cname);
684 	topo_mod_strfree(mod, cid);
685 
686 	if (i < npairs) {
687 		for (i = 0; i < npairs; i++)
688 			nvlist_free(pa[i]);
689 		topo_mod_free(mod, pa, npairs * sizeof (nvlist_t *));
690 		topo_mod_free(mod, hc, hclen);
691 		return (NULL);
692 	}
693 
694 	topo_mod_free(mod, hc, hclen);
695 
696 	return (pa);
697 }
698 
699 void
700 make_hc_auth(topo_mod_t *mod, char *fmri, char **serial, char **part,
701 char **rev, nvlist_t **auth)
702 {
703 	char *starti, *startn, *endi, *copy;
704 	char *aname, *aid, *fs;
705 	nvlist_t *na = NULL;
706 	size_t len;
707 
708 	if ((copy = topo_mod_strdup(mod, fmri + 5)) == NULL)
709 		return;
710 
711 	len = strlen(copy);
712 
713 	/*
714 	 * Make sure there are a valid authority members
715 	 */
716 	startn = strchr(copy, ':');
717 	fs = strchr(copy, '/');
718 
719 	if (startn == NULL || fs == NULL) {
720 		topo_mod_strfree(mod, copy);
721 		return;
722 	}
723 
724 	/*
725 	 * The first colon we encounter must occur before the
726 	 * first slash
727 	 */
728 	if (startn > fs)
729 		return;
730 
731 	do {
732 		if (++startn >= copy + len)
733 			break;
734 
735 		if ((starti = strchr(startn, '=')) == NULL)
736 			break;
737 
738 		*starti = '\0';
739 		if (++starti > copy + len)
740 			break;
741 
742 		if ((aname = topo_mod_strdup(mod, startn)) == NULL)
743 			break;
744 
745 		startn = endi = strchr(starti, ':');
746 		if (endi == NULL)
747 			if ((endi = strchr(starti, '/')) == NULL)
748 				break;
749 
750 		*endi = '\0';
751 		if ((aid = topo_mod_strdup(mod, starti)) == NULL) {
752 			topo_mod_strfree(mod, aname);
753 			break;
754 		}
755 
756 		/*
757 		 * Return possible serial, part and revision
758 		 */
759 		if (strcmp(aname, FM_FMRI_HC_SERIAL_ID) == 0) {
760 			*serial = topo_mod_strdup(mod, aid);
761 		} else if (strcmp(aname, FM_FMRI_HC_PART) == 0) {
762 			*part = topo_mod_strdup(mod, aid);
763 		} else if (strcmp(aname, FM_FMRI_HC_REVISION) == 0) {
764 			*rev = topo_mod_strdup(mod, aid);
765 		} else {
766 			if (na == NULL) {
767 				if (topo_mod_nvalloc(mod, &na,
768 				    NV_UNIQUE_NAME) == 0) {
769 					(void) nvlist_add_string(na, aname,
770 					    aid);
771 				}
772 			} else {
773 				(void) nvlist_add_string(na, aname, aid);
774 			}
775 		}
776 		topo_mod_strfree(mod, aname);
777 		topo_mod_strfree(mod, aid);
778 
779 	} while (startn != NULL);
780 
781 	*auth = na;
782 
783 	topo_mod_free(mod, copy, len + 1);
784 }
785 
786 /*ARGSUSED*/
787 static int
788 hc_fmri_str2nvl(topo_mod_t *mod, tnode_t *node, topo_version_t version,
789     nvlist_t *in, nvlist_t **out)
790 {
791 	nvlist_t **pa = NULL;
792 	nvlist_t *nf = NULL;
793 	nvlist_t *auth = NULL;
794 	char *str;
795 	char *serial = NULL, *part = NULL, *rev = NULL;
796 	int npairs;
797 	int i, e;
798 
799 	if (version > TOPO_METH_STR2NVL_VERSION)
800 		return (topo_mod_seterrno(mod, EMOD_VER_NEW));
801 
802 	if (nvlist_lookup_string(in, "fmri-string", &str) != 0)
803 		return (topo_mod_seterrno(mod, EMOD_METHOD_INVAL));
804 
805 	/* We're expecting a string version of an hc scheme FMRI */
806 	if (strncmp(str, "hc://", 5) != 0)
807 		return (topo_mod_seterrno(mod, EMOD_FMRI_MALFORM));
808 
809 	if ((pa = make_hc_pairs(mod, str, &npairs)) == NULL)
810 		return (topo_mod_seterrno(mod, EMOD_FMRI_MALFORM));
811 
812 	make_hc_auth(mod, str, &serial, &part, &rev, &auth);
813 	if ((nf = hc_base_fmri_create(mod, auth, part, rev, serial)) == NULL)
814 		goto hcfmbail;
815 	if ((e = nvlist_add_uint32(nf, FM_FMRI_HC_LIST_SZ, npairs)) == 0)
816 		e = nvlist_add_nvlist_array(nf, FM_FMRI_HC_LIST, pa, npairs);
817 	if (e != 0) {
818 		topo_mod_dprintf(mod, "construction of new hc nvl failed");
819 		goto hcfmbail;
820 	}
821 	for (i = 0; i < npairs; i++)
822 		nvlist_free(pa[i]);
823 	topo_mod_free(mod, pa, npairs * sizeof (nvlist_t *));
824 	if (serial != NULL)
825 		topo_mod_strfree(mod, serial);
826 	if (part != NULL)
827 		topo_mod_strfree(mod, part);
828 	if (rev != NULL)
829 		topo_mod_strfree(mod, rev);
830 	nvlist_free(auth);
831 
832 	*out = nf;
833 
834 	return (0);
835 
836 hcfmbail:
837 	if (nf != NULL)
838 		nvlist_free(nf);
839 	for (i = 0; i < npairs; i++)
840 		nvlist_free(pa[i]);
841 	topo_mod_free(mod, pa, npairs * sizeof (nvlist_t *));
842 	if (serial != NULL)
843 		topo_mod_strfree(mod, serial);
844 	if (part != NULL)
845 		topo_mod_strfree(mod, part);
846 	if (rev != NULL)
847 		topo_mod_strfree(mod, rev);
848 	nvlist_free(auth);
849 	return (topo_mod_seterrno(mod, EMOD_FMRI_MALFORM));
850 }
851 
852 static nvlist_t *
853 hc_list_create(topo_mod_t *mod, const char *name, char *inst)
854 {
855 	int err;
856 	nvlist_t *hc;
857 
858 	if (topo_mod_nvalloc(mod, &hc, NV_UNIQUE_NAME) != 0)
859 		return (NULL);
860 
861 	err = nvlist_add_string(hc, FM_FMRI_HC_NAME, name);
862 	err |= nvlist_add_string(hc, FM_FMRI_HC_ID, inst);
863 	if (err != 0) {
864 		nvlist_free(hc);
865 		return (NULL);
866 	}
867 
868 	return (hc);
869 }
870 
871 static nvlist_t *
872 hc_create_seterror(topo_mod_t *mod, nvlist_t **hcl, int n, nvlist_t *fmri,
873     int err)
874 {
875 	int i;
876 
877 	if (hcl != NULL) {
878 		for (i = 0; i < n + 1; ++i)
879 			nvlist_free(hcl[i]);
880 
881 		topo_mod_free(mod, hcl, sizeof (nvlist_t *) * (n + 1));
882 	}
883 
884 	nvlist_free(fmri);
885 
886 	(void) topo_mod_seterrno(mod, err);
887 
888 	topo_mod_dprintf(mod, "unable to create hc FMRI: %s\n",
889 	    topo_mod_errmsg(mod));
890 
891 	return (NULL);
892 }
893 
894 static int
895 hc_name_canonical(topo_mod_t *mod, const char *name)
896 {
897 	int i;
898 
899 	if (getenv("NOHCCHECK") != NULL)
900 		return (1);
901 
902 	/*
903 	 * Only enumerate elements with correct canonical names
904 	 */
905 	for (i = 0; i < hc_ncanon; i++) {
906 		if (strcmp(name, hc_canon[i].hcc_name) == 0)
907 			break;
908 	}
909 	if (i >= hc_ncanon) {
910 		topo_mod_dprintf(mod, "non-canonical name %s\n",
911 		    name);
912 		return (0);
913 	} else {
914 		return (1);
915 	}
916 }
917 
918 static nvlist_t *
919 hc_fmri_create(topo_mod_t *mod, nvlist_t *pfmri, int version, const char *name,
920     topo_instance_t inst, const nvlist_t *auth, const char *part,
921     const char *rev, const char *serial)
922 {
923 	int i;
924 	char str[21]; /* sizeof (UINT64_MAX) + '\0' */
925 	uint_t pelems = 0;
926 	nvlist_t **phcl = NULL;
927 	nvlist_t **hcl = NULL;
928 	nvlist_t *fmri = NULL;
929 
930 	if (version > FM_HC_SCHEME_VERSION)
931 		return (hc_create_seterror(mod,
932 		    hcl, pelems, fmri, EMOD_VER_OLD));
933 	else if (version < FM_HC_SCHEME_VERSION)
934 		return (hc_create_seterror(mod,
935 		    hcl, pelems, fmri, EMOD_VER_NEW));
936 
937 	/*
938 	 * Check that the requested name is in our canonical list
939 	 */
940 	if (hc_name_canonical(mod, name) == 0)
941 		return (hc_create_seterror(mod,
942 		    hcl, pelems, fmri, EMOD_NONCANON));
943 	/*
944 	 * Copy the parent's HC_LIST
945 	 */
946 	if (pfmri != NULL) {
947 		if (nvlist_lookup_nvlist_array(pfmri, FM_FMRI_HC_LIST,
948 		    &phcl, &pelems) != 0)
949 			return (hc_create_seterror(mod,
950 			    hcl, pelems, fmri, EMOD_FMRI_MALFORM));
951 	}
952 
953 	hcl = topo_mod_zalloc(mod, sizeof (nvlist_t *) * (pelems + 1));
954 	if (hcl == NULL)
955 		return (hc_create_seterror(mod,  hcl, pelems, fmri,
956 		    EMOD_NOMEM));
957 
958 	for (i = 0; i < pelems; ++i)
959 		if (topo_mod_nvdup(mod, phcl[i], &hcl[i]) != 0)
960 			return (hc_create_seterror(mod,
961 			    hcl, pelems, fmri, EMOD_FMRI_NVL));
962 
963 	(void) snprintf(str, sizeof (str), "%d", inst);
964 	if ((hcl[i] = hc_list_create(mod, name, str)) == NULL)
965 		return (hc_create_seterror(mod,
966 		    hcl, pelems, fmri, EMOD_FMRI_NVL));
967 
968 	if ((fmri = hc_base_fmri_create(mod, auth, part, rev, serial)) == NULL)
969 		return (hc_create_seterror(mod,
970 		    hcl, pelems, fmri, EMOD_FMRI_NVL));
971 
972 	if (nvlist_add_nvlist_array(fmri, FM_FMRI_HC_LIST, hcl, pelems + 1)
973 	    != 0)
974 		return (hc_create_seterror(mod,
975 		    hcl, pelems, fmri, EMOD_FMRI_NVL));
976 
977 	if (hcl != NULL) {
978 		for (i = 0; i < pelems + 1; ++i) {
979 			if (hcl[i] != NULL)
980 				nvlist_free(hcl[i]);
981 		}
982 		topo_mod_free(mod, hcl, sizeof (nvlist_t *) * (pelems + 1));
983 	}
984 
985 	return (fmri);
986 }
987 
988 /*ARGSUSED*/
989 static int
990 hc_fmri_create_meth(topo_mod_t *mod, tnode_t *node, topo_version_t version,
991     nvlist_t *in, nvlist_t **out)
992 {
993 	int ret;
994 	nvlist_t *args, *pfmri = NULL;
995 	nvlist_t *auth;
996 	uint32_t inst;
997 	char *name, *serial, *rev, *part;
998 
999 	if (version > TOPO_METH_FMRI_VERSION)
1000 		return (topo_mod_seterrno(mod, EMOD_VER_NEW));
1001 
1002 	/* First the must-have fields */
1003 	if (nvlist_lookup_string(in, TOPO_METH_FMRI_ARG_NAME, &name) != 0)
1004 		return (topo_mod_seterrno(mod, EMOD_METHOD_INVAL));
1005 	if (nvlist_lookup_uint32(in, TOPO_METH_FMRI_ARG_INST, &inst) != 0)
1006 		return (topo_mod_seterrno(mod, EMOD_METHOD_INVAL));
1007 
1008 	/*
1009 	 * args is optional
1010 	 */
1011 	pfmri = NULL;
1012 	auth = NULL;
1013 	serial = rev = part = NULL;
1014 	if ((ret = nvlist_lookup_nvlist(in, TOPO_METH_FMRI_ARG_NVL, &args))
1015 	    != 0) {
1016 		if (ret != ENOENT)
1017 			return (topo_mod_seterrno(mod, EMOD_METHOD_INVAL));
1018 	} else {
1019 
1020 		/* And then optional arguments */
1021 		(void) nvlist_lookup_nvlist(args, TOPO_METH_FMRI_ARG_PARENT,
1022 		    &pfmri);
1023 		(void) nvlist_lookup_nvlist(args, TOPO_METH_FMRI_ARG_AUTH,
1024 		    &auth);
1025 		(void) nvlist_lookup_string(args, TOPO_METH_FMRI_ARG_PART,
1026 		    &part);
1027 		(void) nvlist_lookup_string(args, TOPO_METH_FMRI_ARG_REV, &rev);
1028 		(void) nvlist_lookup_string(args, TOPO_METH_FMRI_ARG_SER,
1029 		    &serial);
1030 	}
1031 
1032 	*out = hc_fmri_create(mod, pfmri, version, name, inst, auth, part,
1033 	    rev, serial);
1034 	if (*out == NULL)
1035 		return (-1);
1036 	return (0);
1037 }
1038 
1039 struct hc_walk {
1040 	topo_mod_walk_cb_t hcw_cb;
1041 	void *hcw_priv;
1042 	topo_walk_t *hcw_wp;
1043 	nvlist_t **hcw_list;
1044 	uint_t hcw_index;
1045 	uint_t hcw_end;
1046 };
1047 
1048 /*
1049  * Generic walker for the hc-scheme topo tree.  This function uses the
1050  * hierachical nature of the hc-scheme to step through efficiently through
1051  * the topo hc tree.  Node lookups are done by topo_walk_byid() and
1052  * topo_walk_bysibling()  at each component level to avoid unnecessary
1053  * traversal of the tree.  hc_walker() never returns TOPO_WALK_NEXT, so
1054  * whether TOPO_WALK_CHILD or TOPO_WALK_SIBLING is specified by
1055  * topo_walk_step() doesn't affect the traversal.
1056  */
1057 static int
1058 hc_walker(topo_mod_t *mod, tnode_t *node, void *pdata)
1059 {
1060 	int i, err;
1061 	struct hc_walk *hwp = (struct hc_walk *)pdata;
1062 	char *name, *id;
1063 	topo_instance_t inst;
1064 
1065 	i = hwp->hcw_index;
1066 	if (i > hwp->hcw_end) {
1067 		(void) topo_mod_seterrno(mod, ETOPO_PROP_NOENT);
1068 		return (TOPO_WALK_TERMINATE);
1069 	}
1070 
1071 	err = nvlist_lookup_string(hwp->hcw_list[i], FM_FMRI_HC_NAME, &name);
1072 	err |= nvlist_lookup_string(hwp->hcw_list[i], FM_FMRI_HC_ID, &id);
1073 
1074 	if (err != 0) {
1075 		(void) topo_mod_seterrno(mod, EMOD_NVL_INVAL);
1076 		return (TOPO_WALK_ERR);
1077 	}
1078 
1079 	inst = atoi(id);
1080 
1081 	/*
1082 	 * Special case for the root node.  We need to walk by siblings
1083 	 * until we find a matching node for cases where there may be multiple
1084 	 * nodes just below the hc root.
1085 	 */
1086 	if (i == 0) {
1087 		if (strcmp(name, topo_node_name(node)) != 0 ||
1088 		    inst != topo_node_instance(node)) {
1089 			return (topo_walk_bysibling(hwp->hcw_wp, name, inst));
1090 		}
1091 	}
1092 
1093 	topo_mod_dprintf(mod, "hc_walker: walking node:%s=%d for hc:"
1094 	    "%s=%d at %d, end at %d \n", topo_node_name(node),
1095 	    topo_node_instance(node), name, inst, i, hwp->hcw_end);
1096 	if (i == hwp->hcw_end) {
1097 		/*
1098 		 * We are at the end of the hc-list.  Verify that
1099 		 * the last node contains the name/instance we are looking for.
1100 		 */
1101 		if (strcmp(topo_node_name(node), name) == 0 &&
1102 		    inst == topo_node_instance(node)) {
1103 			if ((err = hwp->hcw_cb(mod, node, hwp->hcw_priv))
1104 			    != 0) {
1105 				(void) topo_mod_seterrno(mod, err);
1106 				topo_mod_dprintf(mod, "hc_walker: callback "
1107 				    "failed: %s\n ", topo_mod_errmsg(mod));
1108 				return (TOPO_WALK_ERR);
1109 			}
1110 			topo_mod_dprintf(mod, "hc_walker: callback "
1111 			    "complete: terminate walk\n");
1112 			return (TOPO_WALK_TERMINATE);
1113 		} else {
1114 			topo_mod_dprintf(mod, "hc_walker: %s=%d\n "
1115 			    "not found\n", name, inst);
1116 			return (TOPO_WALK_TERMINATE);
1117 		}
1118 	}
1119 
1120 	hwp->hcw_index = ++i;
1121 	err = nvlist_lookup_string(hwp->hcw_list[i], FM_FMRI_HC_NAME, &name);
1122 	err |= nvlist_lookup_string(hwp->hcw_list[i], FM_FMRI_HC_ID, &id);
1123 	if (err != 0) {
1124 		(void) topo_mod_seterrno(mod, err);
1125 		return (TOPO_WALK_ERR);
1126 	}
1127 	inst = atoi(id);
1128 
1129 	topo_mod_dprintf(mod, "hc_walker: walk byid of %s=%d \n", name,
1130 	    inst);
1131 	return (topo_walk_byid(hwp->hcw_wp, name, inst));
1132 
1133 }
1134 
1135 static struct hc_walk *
1136 hc_walk_init(topo_mod_t *mod, tnode_t *node, nvlist_t *rsrc,
1137     topo_mod_walk_cb_t cb, void *pdata)
1138 {
1139 	int err;
1140 	uint_t sz;
1141 	struct hc_walk *hwp;
1142 	topo_walk_t *wp;
1143 
1144 	if ((hwp = topo_mod_alloc(mod, sizeof (struct hc_walk))) == NULL) {
1145 		(void) topo_mod_seterrno(mod, EMOD_NOMEM);
1146 		return (NULL);
1147 	}
1148 
1149 	if (nvlist_lookup_nvlist_array(rsrc, FM_FMRI_HC_LIST, &hwp->hcw_list,
1150 	    &sz) != 0) {
1151 		topo_mod_free(mod, hwp, sizeof (struct hc_walk));
1152 		(void) topo_mod_seterrno(mod, EMOD_METHOD_INVAL);
1153 		return (NULL);
1154 	}
1155 
1156 	hwp->hcw_end = sz - 1;
1157 	hwp->hcw_index = 0;
1158 	hwp->hcw_priv = pdata;
1159 	hwp->hcw_cb = cb;
1160 	if ((wp = topo_mod_walk_init(mod, node, hc_walker, (void *)hwp, &err))
1161 	    == NULL) {
1162 		topo_mod_free(mod, hwp, sizeof (struct hc_walk));
1163 		(void) topo_mod_seterrno(mod, err);
1164 		return (NULL);
1165 	}
1166 
1167 	hwp->hcw_wp = wp;
1168 
1169 	return (hwp);
1170 }
1171 
1172 struct prop_lookup {
1173 	const char *pl_pgroup;
1174 	const char *pl_pname;
1175 	int pl_flag;
1176 	nvlist_t *pl_args;
1177 	nvlist_t *pl_rsrc;
1178 	nvlist_t *pl_prop;
1179 };
1180 
1181 /*ARGSUSED*/
1182 static int
1183 hc_prop_get(topo_mod_t *mod, tnode_t *node, void *pdata)
1184 {
1185 	int err = 0;
1186 
1187 	struct prop_lookup *plp = (struct prop_lookup *)pdata;
1188 
1189 	(void) topo_prop_getprop(node, plp->pl_pgroup, plp->pl_pname,
1190 	    plp->pl_args, &plp->pl_prop, &err);
1191 
1192 	return (err);
1193 }
1194 
1195 static int
1196 hc_fmri_prop_get(topo_mod_t *mod, tnode_t *node, topo_version_t version,
1197     nvlist_t *in, nvlist_t **out)
1198 {
1199 	int err;
1200 	struct hc_walk *hwp;
1201 	struct prop_lookup *plp;
1202 
1203 	if (version > TOPO_METH_PROP_GET_VERSION)
1204 		return (topo_mod_seterrno(mod, ETOPO_METHOD_VERNEW));
1205 
1206 	if ((plp = topo_mod_alloc(mod, sizeof (struct prop_lookup))) == NULL)
1207 		return (topo_mod_seterrno(mod, EMOD_NOMEM));
1208 
1209 	err = nvlist_lookup_string(in, TOPO_PROP_GROUP,
1210 	    (char **)&plp->pl_pgroup);
1211 	err |= nvlist_lookup_string(in, TOPO_PROP_VAL_NAME,
1212 	    (char **)&plp->pl_pname);
1213 	err |= nvlist_lookup_nvlist(in, TOPO_PROP_RESOURCE, &plp->pl_rsrc);
1214 	if (err != 0) {
1215 		topo_mod_free(mod, plp, sizeof (struct prop_lookup));
1216 		return (topo_mod_seterrno(mod, EMOD_METHOD_INVAL));
1217 	}
1218 
1219 	/*
1220 	 * Private args to prop method are optional
1221 	 */
1222 	if ((err = nvlist_lookup_nvlist(in, TOPO_PROP_PARGS, &plp->pl_args))
1223 	    != 0) {
1224 		if (err != ENOENT) {
1225 			topo_mod_free(mod, plp, sizeof (struct prop_lookup));
1226 			return (topo_mod_seterrno(mod, EMOD_METHOD_INVAL));
1227 		} else {
1228 			plp->pl_args = NULL;
1229 		}
1230 	}
1231 
1232 	plp->pl_prop = NULL;
1233 	if ((hwp = hc_walk_init(mod, node, plp->pl_rsrc, hc_prop_get,
1234 	    (void *)plp)) != NULL) {
1235 		if (topo_walk_step(hwp->hcw_wp, TOPO_WALK_CHILD) ==
1236 		    TOPO_WALK_ERR)
1237 			err = -1;
1238 		else
1239 			err = 0;
1240 		topo_walk_fini(hwp->hcw_wp);
1241 		topo_mod_free(mod, hwp, sizeof (struct hc_walk));
1242 	} else {
1243 		err = -1;
1244 	}
1245 
1246 	if (plp->pl_prop != NULL)
1247 		*out = plp->pl_prop;
1248 
1249 	topo_mod_free(mod, plp, sizeof (struct prop_lookup));
1250 
1251 	return (err);
1252 }
1253 
1254 /*ARGSUSED*/
1255 static int
1256 hc_pgrp_get(topo_mod_t *mod, tnode_t *node, void *pdata)
1257 {
1258 	int err = 0;
1259 
1260 	struct prop_lookup *plp = (struct prop_lookup *)pdata;
1261 
1262 	(void) topo_prop_getpgrp(node, plp->pl_pgroup, &plp->pl_prop, &err);
1263 
1264 	return (err);
1265 }
1266 
1267 static int
1268 hc_fmri_pgrp_get(topo_mod_t *mod, tnode_t *node, topo_version_t version,
1269     nvlist_t *in, nvlist_t **out)
1270 {
1271 	int err;
1272 	struct hc_walk *hwp;
1273 	struct prop_lookup *plp;
1274 
1275 	if (version > TOPO_METH_PGRP_GET_VERSION)
1276 		return (topo_mod_seterrno(mod, ETOPO_METHOD_VERNEW));
1277 
1278 	if ((plp = topo_mod_alloc(mod, sizeof (struct prop_lookup))) == NULL)
1279 		return (topo_mod_seterrno(mod, EMOD_NOMEM));
1280 
1281 	err = nvlist_lookup_string(in, TOPO_PROP_GROUP,
1282 	    (char **)&plp->pl_pgroup);
1283 	err |= nvlist_lookup_nvlist(in, TOPO_PROP_RESOURCE, &plp->pl_rsrc);
1284 	if (err != 0) {
1285 		topo_mod_free(mod, plp, sizeof (struct prop_lookup));
1286 		return (topo_mod_seterrno(mod, EMOD_METHOD_INVAL));
1287 	}
1288 
1289 	plp->pl_prop = NULL;
1290 	if ((hwp = hc_walk_init(mod, node, plp->pl_rsrc, hc_pgrp_get,
1291 	    (void *)plp)) != NULL) {
1292 		if (topo_walk_step(hwp->hcw_wp, TOPO_WALK_CHILD) ==
1293 		    TOPO_WALK_ERR)
1294 			err = -1;
1295 		else
1296 			err = 0;
1297 		topo_walk_fini(hwp->hcw_wp);
1298 		topo_mod_free(mod, hwp, sizeof (struct hc_walk));
1299 	} else {
1300 		err = -1;
1301 	}
1302 
1303 	if (plp->pl_prop != NULL)
1304 		*out = plp->pl_prop;
1305 
1306 	topo_mod_free(mod, plp, sizeof (struct prop_lookup));
1307 
1308 	return (err);
1309 }
1310 
1311 /*ARGSUSED*/
1312 static int
1313 hc_prop_setprop(topo_mod_t *mod, tnode_t *node, void *pdata)
1314 {
1315 	int err = 0;
1316 
1317 	struct prop_lookup *plp = (struct prop_lookup *)pdata;
1318 
1319 	(void) topo_prop_setprop(node, plp->pl_pgroup, plp->pl_prop,
1320 	    plp->pl_flag, plp->pl_args, &err);
1321 
1322 	return (err);
1323 }
1324 
1325 /*ARGSUSED*/
1326 static int
1327 hc_fmri_prop_set(topo_mod_t *mod, tnode_t *node, topo_version_t version,
1328     nvlist_t *in, nvlist_t **out)
1329 {
1330 	int err;
1331 	struct hc_walk *hwp;
1332 	struct prop_lookup *plp;
1333 
1334 	if (version > TOPO_METH_PROP_SET_VERSION)
1335 		return (topo_mod_seterrno(mod, ETOPO_METHOD_VERNEW));
1336 
1337 	if ((plp = topo_mod_alloc(mod, sizeof (struct prop_lookup))) == NULL)
1338 		return (topo_mod_seterrno(mod, EMOD_NOMEM));
1339 
1340 	err = nvlist_lookup_string(in, TOPO_PROP_GROUP,
1341 	    (char **)&plp->pl_pgroup);
1342 	err |= nvlist_lookup_nvlist(in, TOPO_PROP_RESOURCE, &plp->pl_rsrc);
1343 	err |= nvlist_lookup_nvlist(in, TOPO_PROP_VAL, &plp->pl_prop);
1344 	err |= nvlist_lookup_int32(in, TOPO_PROP_FLAG, &plp->pl_flag);
1345 	if (err != 0) {
1346 		topo_mod_free(mod, plp, sizeof (struct prop_lookup));
1347 		return (topo_mod_seterrno(mod, EMOD_METHOD_INVAL));
1348 	}
1349 
1350 	/*
1351 	 * Private args to prop method are optional
1352 	 */
1353 	if ((err = nvlist_lookup_nvlist(in, TOPO_PROP_PARGS, &plp->pl_args))
1354 	    != 0) {
1355 		if (err != ENOENT)
1356 			return (topo_mod_seterrno(mod, EMOD_METHOD_INVAL));
1357 		else
1358 			plp->pl_args = NULL;
1359 	}
1360 
1361 	if ((hwp = hc_walk_init(mod, node, plp->pl_rsrc, hc_prop_setprop,
1362 	    (void *)plp)) != NULL) {
1363 		if (topo_walk_step(hwp->hcw_wp, TOPO_WALK_CHILD) ==
1364 		    TOPO_WALK_ERR)
1365 			err = -1;
1366 		else
1367 			err = 0;
1368 		topo_walk_fini(hwp->hcw_wp);
1369 		topo_mod_free(mod, hwp, sizeof (struct hc_walk));
1370 	} else {
1371 		err = -1;
1372 	}
1373 
1374 	topo_mod_free(mod, plp, sizeof (struct prop_lookup));
1375 
1376 	return (err);
1377 }
1378 
1379 struct hc_args {
1380 	nvlist_t *ha_fmri;
1381 	nvlist_t *ha_nvl;
1382 };
1383 
1384 static int
1385 hc_is_present(topo_mod_t *mod, tnode_t *node, void *pdata)
1386 {
1387 	int err;
1388 	struct hc_args *hap = (struct hc_args *)pdata;
1389 
1390 	/*
1391 	 * check with the enumerator that created this FMRI
1392 	 * (topo node)
1393 	 */
1394 	if (topo_method_invoke(node, TOPO_METH_PRESENT,
1395 	    TOPO_METH_PRESENT_VERSION, hap->ha_fmri, &hap->ha_nvl,
1396 	    &err) < 0) {
1397 
1398 		/*
1399 		 * Err on the side of caution and return present
1400 		 */
1401 		if (topo_mod_nvalloc(mod, &hap->ha_nvl, NV_UNIQUE_NAME) == 0)
1402 			if (nvlist_add_uint32(hap->ha_nvl,
1403 			    TOPO_METH_PRESENT_RET, 1) == 0)
1404 				return (0);
1405 
1406 		return (ETOPO_PROP_NVL);
1407 	}
1408 
1409 	return (0);
1410 }
1411 
1412 static int
1413 hc_fmri_present(topo_mod_t *mod, tnode_t *node, topo_version_t version,
1414     nvlist_t *in, nvlist_t **out)
1415 {
1416 	int err;
1417 	struct hc_walk *hwp;
1418 	struct hc_args *hap;
1419 
1420 	if (version > TOPO_METH_PRESENT_VERSION)
1421 		return (topo_mod_seterrno(mod, ETOPO_METHOD_VERNEW));
1422 
1423 	if ((hap = topo_mod_alloc(mod, sizeof (struct hc_args))) == NULL)
1424 		return (topo_mod_seterrno(mod, EMOD_NOMEM));
1425 
1426 	hap->ha_fmri = in;
1427 	hap->ha_nvl = NULL;
1428 	if ((hwp = hc_walk_init(mod, node, hap->ha_fmri, hc_is_present,
1429 	    (void *)hap)) != NULL) {
1430 		if (topo_walk_step(hwp->hcw_wp, TOPO_WALK_CHILD) ==
1431 		    TOPO_WALK_ERR)
1432 			err = -1;
1433 		else
1434 			err = 0;
1435 		topo_walk_fini(hwp->hcw_wp);
1436 		topo_mod_free(mod, hwp, sizeof (struct hc_walk));
1437 	} else {
1438 		err = -1;
1439 	}
1440 
1441 	if (hap->ha_nvl != NULL)
1442 		*out = hap->ha_nvl;
1443 
1444 	topo_mod_free(mod, hap, sizeof (struct hc_args));
1445 
1446 	return (err);
1447 }
1448 
1449 static int
1450 hc_unusable(topo_mod_t *mod, tnode_t *node, void *pdata)
1451 {
1452 	int err;
1453 	struct hc_args *hap = (struct hc_args *)pdata;
1454 
1455 	/*
1456 	 * check with the enumerator that created this FMRI
1457 	 * (topo node)
1458 	 */
1459 	if (topo_method_invoke(node, TOPO_METH_UNUSABLE,
1460 	    TOPO_METH_UNUSABLE_VERSION, hap->ha_fmri, &hap->ha_nvl,
1461 	    &err) < 0) {
1462 
1463 		/*
1464 		 * Err on the side of caution and return usable
1465 		 */
1466 		if (topo_mod_nvalloc(mod, &hap->ha_nvl, NV_UNIQUE_NAME) == 0)
1467 			if (nvlist_add_uint32(hap->ha_nvl,
1468 			    TOPO_METH_UNUSABLE_RET, 0) == 0)
1469 				return (0);
1470 
1471 		return (ETOPO_PROP_NVL);
1472 	}
1473 
1474 	return (err);
1475 }
1476 
1477 static int
1478 hc_fmri_unusable(topo_mod_t *mod, tnode_t *node, topo_version_t version,
1479     nvlist_t *in, nvlist_t **out)
1480 {
1481 	int err;
1482 	struct hc_walk *hwp;
1483 	struct hc_args *hap;
1484 
1485 	if (version > TOPO_METH_UNUSABLE_VERSION)
1486 		return (topo_mod_seterrno(mod, ETOPO_METHOD_VERNEW));
1487 
1488 	if ((hap = topo_mod_alloc(mod, sizeof (struct hc_args))) == NULL)
1489 		return (topo_mod_seterrno(mod, EMOD_NOMEM));
1490 
1491 	hap->ha_fmri = in;
1492 	hap->ha_nvl = NULL;
1493 	if ((hwp = hc_walk_init(mod, node, hap->ha_fmri, hc_unusable,
1494 	    (void *)hap)) != NULL) {
1495 		if (topo_walk_step(hwp->hcw_wp, TOPO_WALK_CHILD) ==
1496 		    TOPO_WALK_ERR)
1497 			err = -1;
1498 		else
1499 			err = 0;
1500 		topo_walk_fini(hwp->hcw_wp);
1501 		topo_mod_free(mod, hwp, sizeof (struct hc_walk));
1502 	} else {
1503 		err = -1;
1504 	}
1505 
1506 	if (hap->ha_nvl != NULL)
1507 		*out = hap->ha_nvl;
1508 
1509 	topo_mod_free(mod, hap, sizeof (struct hc_args));
1510 
1511 	return (err);
1512 }
1513