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