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