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