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