1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21
22 /*
23 * Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
24 * Copyright 2023 Oxide Computer Company
25 */
26
27 #include <limits.h>
28 #include <strings.h>
29 #include <string.h>
30 #include <unistd.h>
31 #include <stdio.h>
32 #include <alloca.h>
33 #include <devid.h>
34 #include <sys/stat.h>
35 #include <libnvpair.h>
36 #include <fm/topo_mod.h>
37 #include <fm/fmd_fmri.h>
38 #include <sys/fm/protocol.h>
39
40 #include <topo_method.h>
41 #include <topo_subr.h>
42 #include <dev.h>
43
44 static int dev_enum(topo_mod_t *, tnode_t *, const char *, topo_instance_t,
45 topo_instance_t, void *, void *);
46 static void dev_release(topo_mod_t *, tnode_t *);
47 static int dev_fmri_nvl2str(topo_mod_t *, tnode_t *, topo_version_t,
48 nvlist_t *, nvlist_t **);
49 static int dev_fmri_str2nvl(topo_mod_t *, tnode_t *, topo_version_t,
50 nvlist_t *, nvlist_t **);
51 static int dev_fmri_create_meth(topo_mod_t *, tnode_t *, topo_version_t,
52 nvlist_t *, nvlist_t **);
53 static int dev_fmri_present(topo_mod_t *, tnode_t *, topo_version_t,
54 nvlist_t *, nvlist_t **);
55 static int dev_fmri_replaced(topo_mod_t *, tnode_t *, topo_version_t,
56 nvlist_t *, nvlist_t **);
57 static int dev_fmri_unusable(topo_mod_t *, tnode_t *, topo_version_t,
58 nvlist_t *, nvlist_t **);
59 static int dev_fmri_service_state(topo_mod_t *, tnode_t *, topo_version_t,
60 nvlist_t *, nvlist_t **);
61
62 static const topo_method_t dev_methods[] = {
63 { TOPO_METH_NVL2STR, TOPO_METH_NVL2STR_DESC, TOPO_METH_NVL2STR_VERSION,
64 TOPO_STABILITY_INTERNAL, dev_fmri_nvl2str },
65 { TOPO_METH_STR2NVL, TOPO_METH_STR2NVL_DESC, TOPO_METH_STR2NVL_VERSION,
66 TOPO_STABILITY_INTERNAL, dev_fmri_str2nvl },
67 { TOPO_METH_FMRI, TOPO_METH_FMRI_DESC, TOPO_METH_FMRI_VERSION,
68 TOPO_STABILITY_INTERNAL, dev_fmri_create_meth },
69 { TOPO_METH_PRESENT, TOPO_METH_PRESENT_DESC, TOPO_METH_PRESENT_VERSION,
70 TOPO_STABILITY_INTERNAL, dev_fmri_present },
71 { TOPO_METH_REPLACED, TOPO_METH_REPLACED_DESC,
72 TOPO_METH_REPLACED_VERSION, TOPO_STABILITY_INTERNAL,
73 dev_fmri_replaced },
74 { TOPO_METH_UNUSABLE, TOPO_METH_UNUSABLE_DESC,
75 TOPO_METH_UNUSABLE_VERSION, TOPO_STABILITY_INTERNAL,
76 dev_fmri_unusable },
77 { TOPO_METH_SERVICE_STATE, TOPO_METH_SERVICE_STATE_DESC,
78 TOPO_METH_SERVICE_STATE_VERSION, TOPO_STABILITY_INTERNAL,
79 dev_fmri_service_state },
80 { NULL }
81 };
82
83 static const topo_modops_t dev_ops =
84 { dev_enum, dev_release };
85 static const topo_modinfo_t dev_info =
86 { "dev", FM_FMRI_SCHEME_DEV, DEV_VERSION, &dev_ops };
87
88 int
dev_init(topo_mod_t * mod,topo_version_t version)89 dev_init(topo_mod_t *mod, topo_version_t version)
90 {
91 if (getenv("TOPOHCDEBUG"))
92 topo_mod_setdebug(mod);
93 topo_mod_dprintf(mod, "initializing dev builtin\n");
94
95 if (version != DEV_VERSION)
96 return (topo_mod_seterrno(mod, EMOD_VER_NEW));
97
98 if (topo_mod_register(mod, &dev_info, TOPO_VERSION) != 0) {
99 topo_mod_dprintf(mod, "failed to register dev_info: "
100 "%s\n", topo_mod_errmsg(mod));
101 return (-1);
102 }
103
104 return (0);
105 }
106
107 void
dev_fini(topo_mod_t * mod)108 dev_fini(topo_mod_t *mod)
109 {
110 topo_mod_unregister(mod);
111 }
112
113 static int
dev_enum(topo_mod_t * mod,tnode_t * pnode,const char * name,topo_instance_t min,topo_instance_t max,void * notused1,void * notused2)114 dev_enum(topo_mod_t *mod, tnode_t *pnode, const char *name,
115 topo_instance_t min, topo_instance_t max, void *notused1, void *notused2)
116 {
117 /*
118 * Methods are registered, but there is no enumeration. Should
119 * enumeration be added be sure to cater for global vs non-global
120 * zones.
121 */
122 (void) topo_method_register(mod, pnode, dev_methods);
123 return (0);
124 }
125
126 static void
dev_release(topo_mod_t * mod,tnode_t * node)127 dev_release(topo_mod_t *mod, tnode_t *node)
128 {
129 topo_method_unregister_all(mod, node);
130 }
131
132 static size_t
fmri_nvl2str(nvlist_t * nvl,char * buf,size_t buflen)133 fmri_nvl2str(nvlist_t *nvl, char *buf, size_t buflen)
134 {
135 char *devid = NULL, *tpl0id = NULL;
136 char *devpath = NULL;
137 size_t size = 0;
138 uint8_t version;
139 int err;
140
141 if (nvlist_lookup_uint8(nvl, FM_VERSION, &version) != 0 ||
142 version > FM_DEV_SCHEME_VERSION)
143 return (0);
144
145 /* Get devid, if present */
146 err = nvlist_lookup_string(nvl, FM_FMRI_DEV_ID, &devid);
147 if (err != 0 && err != ENOENT)
148 return (0);
149
150 /* Get target-port-l0id, if present */
151 err = nvlist_lookup_string(nvl, FM_FMRI_DEV_TGTPTLUN0, &tpl0id);
152 if (err != 0 && err != ENOENT)
153 return (0);
154
155 /* There must be a device path present */
156 err = nvlist_lookup_string(nvl, FM_FMRI_DEV_PATH, &devpath);
157 if (err != 0 || devpath == NULL)
158 return (0);
159
160 /*
161 * dev:///
162 *
163 * The dev scheme does not render fmri authority information
164 * in the string form of an fmri. It is meaningless to
165 * transmit a dev scheme fmri outside of the immediate fault
166 * manager.
167 */
168 if (!topo_fmristr_build(&size, buf, buflen,
169 FM_FMRI_SCHEME_DEV, NULL, ":///")) {
170 return (0);
171 }
172
173 /* device-id part, topo_fmristr_build does nothing if devid is NULL */
174 if (!topo_fmristr_build(&size, buf, buflen,
175 devid, ":" FM_FMRI_DEV_ID "=", NULL)) {
176 return (0);
177 }
178
179 /* target-port-l0id part */
180 if (!topo_fmristr_build(&size, buf, buflen,
181 tpl0id, ":" FM_FMRI_DEV_TGTPTLUN0 "=", NULL)) {
182 return (0);
183 }
184
185 /*
186 * device-path part; the devpath should always start with a /
187 * so you'd think we don't need to add a further / prefix here;
188 * however past implementation has always added the / if
189 * there is a devid component so we continue to do that
190 * so strings match exactly as before. So we can have:
191 *
192 * dev:////pci@0,0/...
193 * dev:///<devid-and-tpl0>//pci@0,0/...
194 *
195 * where <devid-and-tpl0> =
196 * [:devid=<devid>][:target-port-l0id=<tpl0>]
197 */
198 if (!topo_fmristr_build(&size, buf, buflen,
199 devpath, devid || tpl0id ? "/" : NULL, NULL)) {
200 return (0);
201 }
202
203 return (size);
204 }
205
206 static int
dev_fmri_nvl2str(topo_mod_t * mod,tnode_t * node,topo_version_t version,nvlist_t * nvl,nvlist_t ** out)207 dev_fmri_nvl2str(topo_mod_t *mod, tnode_t *node, topo_version_t version,
208 nvlist_t *nvl, nvlist_t **out)
209 {
210 size_t len;
211 char *name = NULL;
212 nvlist_t *fmristr;
213
214 if (version > TOPO_METH_NVL2STR_VERSION)
215 return (topo_mod_seterrno(mod, EMOD_VER_NEW));
216
217 if ((len = fmri_nvl2str(nvl, NULL, 0)) == 0 ||
218 (name = topo_mod_alloc(mod, len + 1)) == NULL ||
219 fmri_nvl2str(nvl, name, len + 1) == 0) {
220 if (name != NULL)
221 topo_mod_free(mod, name, len + 1);
222 return (topo_mod_seterrno(mod, EMOD_FMRI_NVL));
223 }
224
225 if (topo_mod_nvalloc(mod, &fmristr, NV_UNIQUE_NAME) != 0)
226 return (topo_mod_seterrno(mod, EMOD_FMRI_NVL));
227 if (nvlist_add_string(fmristr, "fmri-string", name) != 0) {
228 topo_mod_free(mod, name, len + 1);
229 nvlist_free(fmristr);
230 return (topo_mod_seterrno(mod, EMOD_FMRI_NVL));
231 }
232 topo_mod_free(mod, name, len + 1);
233 *out = fmristr;
234
235 return (0);
236 }
237
238 static int
dev_fmri_str2nvl(topo_mod_t * mod,tnode_t * node,topo_version_t version,nvlist_t * in,nvlist_t ** out)239 dev_fmri_str2nvl(topo_mod_t *mod, tnode_t *node, topo_version_t version,
240 nvlist_t *in, nvlist_t **out)
241 {
242 char *cur, *devid = NULL, *tpl0id = NULL;
243 char *str, *strcp;
244 nvlist_t *fmri;
245 char *devpath;
246 size_t len;
247 int err;
248
249 if (version > TOPO_METH_STR2NVL_VERSION)
250 return (topo_mod_seterrno(mod, EMOD_VER_NEW));
251
252 if (nvlist_lookup_string(in, "fmri-string", &str) != 0)
253 return (topo_mod_seterrno(mod, EMOD_FMRI_NVL));
254
255 len = strlen(str);
256
257 /*
258 * We're expecting a string version of a dev scheme FMRI, and
259 * no fmri authority information.
260 *
261 * The shortest legal string would be "dev:////" (len 8) for a string
262 * with no FMRI auth info, no devid or target-port-l0id and
263 * an empty devpath string.
264 */
265 if (len < 8 || strncmp(str, "dev:///", 7) != 0)
266 return (topo_mod_seterrno(mod, EMOD_FMRI_MALFORM));
267
268 strcp = alloca(len + 1);
269 (void) memcpy(strcp, str, len);
270 strcp[len] = '\0';
271 cur = strcp + 7; /* already parsed "dev:///" */
272
273 /*
274 * If the first character after the "/" that terminates the (empty)
275 * fmri authority is a colon then we have devid and/or target-port-l0id
276 * info. They could be in either order.
277 *
278 * If not a colon then it must be the / that begins the devpath.
279 */
280 if (*cur == ':') {
281 char *eos, *part[2];
282 int i;
283 /*
284 * Look ahead to the "/" that starts the devpath. If not
285 * found or if straight after the : then we're busted.
286 */
287 eos = devpath = strchr(cur, '/');
288 if (devpath == NULL || devpath == cur + 1)
289 return (topo_mod_seterrno(mod, EMOD_FMRI_MALFORM));
290
291 part[0] = ++cur;
292
293 /*
294 * Replace the initial "/" of the devpath with a NUL
295 * to terminate the string before it. We'll undo this
296 * before rendering devpath.
297 */
298 *eos = '\0';
299
300 /*
301 * We should now have a NUL-terminated string matching
302 * foo=<pat1>[:bar=<pat2>] (we stepped over the initial :)
303 * Look for a second colon; if found there must be space
304 * after it for the additional component, but no more colons.
305 */
306 if ((part[1] = strchr(cur, ':')) != NULL) {
307 if (part[1] + 1 == eos ||
308 strchr(part[1] + 1, ':') != NULL)
309 return (topo_mod_seterrno(mod,
310 EMOD_FMRI_MALFORM));
311 *part[1] = '\0'; /* terminate part[0] */
312 part[1]++;
313 }
314
315 for (i = 0; i < 2; i++) {
316 char *eq;
317
318 if (!part[i])
319 continue;
320
321 if ((eq = strchr(part[i], '=')) == NULL ||
322 *(eq + 1) == '\0')
323 return (topo_mod_seterrno(mod,
324 EMOD_FMRI_MALFORM));
325
326 *eq = '\0';
327 if (strcmp(part[i], FM_FMRI_DEV_ID) == 0)
328 devid = eq + 1;
329 else if (strcmp(part[i], FM_FMRI_DEV_TGTPTLUN0) == 0)
330 tpl0id = eq + 1;
331 else
332 return (topo_mod_seterrno(mod,
333 EMOD_FMRI_MALFORM));
334 }
335
336 if (devid == NULL && tpl0id == NULL)
337 return (topo_mod_seterrno(mod, EMOD_FMRI_MALFORM));
338
339 cur = devpath; /* initial slash is NULled */
340 } else if (*cur != '/') {
341 /* the device-path should start with a slash */
342 return (topo_mod_seterrno(mod, EMOD_FMRI_MALFORM));
343 } else {
344 devpath = cur;
345 }
346
347 if (topo_mod_nvalloc(mod, &fmri, NV_UNIQUE_NAME) != 0)
348 return (topo_mod_seterrno(mod, EMOD_FMRI_NVL));
349
350 err = nvlist_add_uint8(fmri, FM_VERSION, FM_DEV_SCHEME_VERSION);
351 err |= nvlist_add_string(fmri, FM_FMRI_SCHEME, FM_FMRI_SCHEME_DEV);
352
353 if (devid != NULL)
354 err |= nvlist_add_string(fmri, FM_FMRI_DEV_ID, devid);
355
356 if (tpl0id != NULL)
357 err |= nvlist_add_string(fmri, FM_FMRI_DEV_TGTPTLUN0, tpl0id);
358
359 if (devid != NULL || tpl0id != NULL)
360 *devpath = '/'; /* we NULed this earlier; put it back */
361
362 /* step over repeated initial / in the devpath */
363 while (*(devpath + 1) == '/')
364 devpath++;
365
366 err |= nvlist_add_string(fmri, FM_FMRI_DEV_PATH, devpath);
367
368 if (err != 0) {
369 nvlist_free(fmri);
370 return (topo_mod_seterrno(mod, EMOD_FMRI_NVL));
371 }
372
373 *out = fmri;
374
375 return (0);
376 }
377
378 static int
dev_fmri_present(topo_mod_t * mod,tnode_t * node,topo_version_t version,nvlist_t * in,nvlist_t ** out)379 dev_fmri_present(topo_mod_t *mod, tnode_t *node, topo_version_t version,
380 nvlist_t *in, nvlist_t **out)
381 {
382 uint8_t fmversion;
383 char *devpath = NULL;
384 uint32_t present;
385 char *devid = NULL, *path;
386 ddi_devid_t id;
387 ddi_devid_t matchid;
388 di_node_t dnode;
389 struct stat sb;
390 int len;
391
392 if (version > TOPO_METH_PRESENT_VERSION)
393 return (topo_mod_seterrno(mod, EMOD_VER_NEW));
394
395 if (nvlist_lookup_uint8(in, FM_VERSION, &fmversion) != 0 ||
396 fmversion > FM_DEV_SCHEME_VERSION ||
397 nvlist_lookup_string(in, FM_FMRI_DEV_PATH, &devpath) != 0)
398 return (topo_mod_seterrno(mod, EMOD_FMRI_MALFORM));
399
400 (void) nvlist_lookup_string(in, FM_FMRI_DEV_ID, &devid);
401
402 if (devpath == NULL || strlen(devpath) == 0)
403 return (topo_mod_seterrno(mod, EMOD_FMRI_MALFORM));
404
405 /*
406 * stat() the device node in devfs. This will tell us if the device is
407 * present or not. Don't stat the minor, just the whole device.
408 * If the device is present and there is a devid, it must also match.
409 * so di_init that one node. No need for DINFOFORCE.
410 */
411 len = strlen(devpath) + strlen("/devices") + 1;
412 path = topo_mod_alloc(mod, len);
413 (void) snprintf(path, len, "/devices%s", devpath);
414 if (devid == NULL) {
415 if (stat(path, &sb) != -1)
416 present = 1;
417 else if ((dnode = di_init("/", DINFOCACHE)) == DI_NODE_NIL)
418 present = 0;
419 else {
420 if (di_lookup_node(dnode, devpath) == DI_NODE_NIL)
421 present = 0;
422 else
423 present = 1;
424 di_fini(dnode);
425 }
426 } else {
427 if (stat(path, &sb) == -1)
428 present = 0;
429 else if ((dnode = di_init(devpath, DINFOCPYONE)) == DI_NODE_NIL)
430 present = 0;
431 else {
432 if ((id = di_devid(dnode)) == NULL ||
433 devid_str_decode(devid, &matchid, NULL) != 0)
434 present = 0;
435 else {
436 if (devid_compare(id, matchid) != 0)
437 present = 0;
438 else
439 present = 1;
440 devid_free(matchid);
441 }
442 di_fini(dnode);
443 }
444 }
445 topo_mod_free(mod, path, len);
446
447 if (topo_mod_nvalloc(mod, out, NV_UNIQUE_NAME) != 0)
448 return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
449 if (nvlist_add_uint32(*out, TOPO_METH_PRESENT_RET, present) != 0) {
450 nvlist_free(*out);
451 return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
452 }
453
454 return (0);
455 }
456
457 static int
dev_fmri_replaced(topo_mod_t * mod,tnode_t * node,topo_version_t version,nvlist_t * in,nvlist_t ** out)458 dev_fmri_replaced(topo_mod_t *mod, tnode_t *node, topo_version_t version,
459 nvlist_t *in, nvlist_t **out)
460 {
461 uint8_t fmversion;
462 char *devpath = NULL;
463 uint32_t rval;
464 char *devid = NULL, *path;
465 ddi_devid_t id;
466 ddi_devid_t matchid;
467 di_node_t dnode;
468 struct stat sb;
469 int len;
470
471 if (version > TOPO_METH_REPLACED_VERSION)
472 return (topo_mod_seterrno(mod, EMOD_VER_NEW));
473
474 if (nvlist_lookup_uint8(in, FM_VERSION, &fmversion) != 0 ||
475 fmversion > FM_DEV_SCHEME_VERSION ||
476 nvlist_lookup_string(in, FM_FMRI_DEV_PATH, &devpath) != 0)
477 return (topo_mod_seterrno(mod, EMOD_FMRI_MALFORM));
478
479 (void) nvlist_lookup_string(in, FM_FMRI_DEV_ID, &devid);
480
481 if (devpath == NULL || strlen(devpath) == 0)
482 return (topo_mod_seterrno(mod, EMOD_FMRI_MALFORM));
483
484 /*
485 * stat() the device node in devfs. This will tell us if the device is
486 * present or not. Don't stat the minor, just the whole device.
487 * If the device is present and there is a devid, it must also match.
488 * so di_init that one node. No need for DINFOFORCE.
489 */
490 len = strlen(devpath) + strlen("/devices") + 1;
491 path = topo_mod_alloc(mod, len);
492 (void) snprintf(path, len, "/devices%s", devpath);
493 if (devid == NULL) {
494 if (stat(path, &sb) != -1)
495 rval = FMD_OBJ_STATE_UNKNOWN;
496 else if ((dnode = di_init("/", DINFOCACHE)) == DI_NODE_NIL)
497 rval = FMD_OBJ_STATE_UNKNOWN;
498 else {
499 if (di_lookup_node(dnode, devpath) == DI_NODE_NIL)
500 rval = FMD_OBJ_STATE_UNKNOWN;
501 else
502 rval = FMD_OBJ_STATE_UNKNOWN;
503 di_fini(dnode);
504 }
505 } else {
506 if (stat(path, &sb) == -1)
507 rval = FMD_OBJ_STATE_UNKNOWN;
508 else if ((dnode = di_init(devpath, DINFOCPYONE)) == DI_NODE_NIL)
509 rval = FMD_OBJ_STATE_UNKNOWN;
510 else {
511 if ((id = di_devid(dnode)) == NULL ||
512 devid_str_decode(devid, &matchid, NULL) != 0)
513 rval = FMD_OBJ_STATE_UNKNOWN;
514 else {
515 if (devid_compare(id, matchid) != 0)
516 rval = FMD_OBJ_STATE_REPLACED;
517 else
518 rval = FMD_OBJ_STATE_STILL_PRESENT;
519 devid_free(matchid);
520 }
521 di_fini(dnode);
522 }
523 }
524 topo_mod_free(mod, path, len);
525
526 if (topo_mod_nvalloc(mod, out, NV_UNIQUE_NAME) != 0)
527 return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
528 if (nvlist_add_uint32(*out, TOPO_METH_REPLACED_RET, rval) != 0) {
529 nvlist_free(*out);
530 return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
531 }
532
533 return (0);
534 }
535
536 static int
dev_fmri_unusable(topo_mod_t * mod,tnode_t * node,topo_version_t version,nvlist_t * in,nvlist_t ** out)537 dev_fmri_unusable(topo_mod_t *mod, tnode_t *node, topo_version_t version,
538 nvlist_t *in, nvlist_t **out)
539 {
540 di_node_t dnode;
541 uint8_t fmversion;
542 char *devpath = NULL;
543 uint32_t unusable;
544 uint_t state;
545
546 if (version > TOPO_METH_UNUSABLE_VERSION)
547 return (topo_mod_seterrno(mod, EMOD_VER_NEW));
548
549 if (nvlist_lookup_uint8(in, FM_VERSION, &fmversion) != 0 ||
550 fmversion > FM_DEV_SCHEME_VERSION ||
551 nvlist_lookup_string(in, FM_FMRI_DEV_PATH, &devpath) != 0)
552 return (topo_mod_seterrno(mod, EMOD_FMRI_MALFORM));
553
554 if (devpath == NULL)
555 return (topo_mod_seterrno(mod, EMOD_FMRI_MALFORM));
556
557 if ((dnode = di_init(devpath, DINFOCPYONE)) == DI_NODE_NIL) {
558 if (errno != ENXIO)
559 return (topo_mod_seterrno(mod, EMOD_UKNOWN_ENUM));
560 unusable = 1;
561 } else {
562 uint_t retired = di_retired(dnode);
563 state = di_state(dnode);
564 if (retired || (state & (DI_DEVICE_OFFLINE | DI_DEVICE_DOWN |
565 DI_BUS_QUIESCED | DI_BUS_DOWN)))
566 unusable = 1;
567 else
568 unusable = 0;
569 di_fini(dnode);
570 }
571
572 if (topo_mod_nvalloc(mod, out, NV_UNIQUE_NAME) != 0)
573 return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
574 if (nvlist_add_uint32(*out, TOPO_METH_UNUSABLE_RET, unusable) != 0) {
575 nvlist_free(*out);
576 return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
577 }
578
579 return (0);
580 }
581
582 static int
dev_fmri_service_state(topo_mod_t * mod,tnode_t * node,topo_version_t version,nvlist_t * in,nvlist_t ** out)583 dev_fmri_service_state(topo_mod_t *mod, tnode_t *node, topo_version_t version,
584 nvlist_t *in, nvlist_t **out)
585 {
586 di_node_t dnode;
587 uint8_t fmversion;
588 char *devpath = NULL;
589 uint32_t service_state;
590 uint_t state;
591
592 if (version > TOPO_METH_SERVICE_STATE_VERSION)
593 return (topo_mod_seterrno(mod, EMOD_VER_NEW));
594
595 if (nvlist_lookup_uint8(in, FM_VERSION, &fmversion) != 0 ||
596 fmversion > FM_DEV_SCHEME_VERSION ||
597 nvlist_lookup_string(in, FM_FMRI_DEV_PATH, &devpath) != 0)
598 return (topo_mod_seterrno(mod, EMOD_FMRI_MALFORM));
599
600 if (devpath == NULL)
601 return (topo_mod_seterrno(mod, EMOD_FMRI_MALFORM));
602
603 if ((dnode = di_init(devpath, DINFOCPYONE)) == DI_NODE_NIL) {
604 if (errno != ENXIO)
605 return (topo_mod_seterrno(mod, EMOD_UKNOWN_ENUM));
606 service_state = FMD_SERVICE_STATE_UNUSABLE;
607 } else {
608 uint_t retired = di_retired(dnode);
609 state = di_state(dnode);
610 if (retired || (state & (DI_DEVICE_OFFLINE | DI_DEVICE_DOWN |
611 DI_BUS_QUIESCED | DI_BUS_DOWN)))
612 service_state = FMD_SERVICE_STATE_UNUSABLE;
613 else if (state & DI_DEVICE_DEGRADED)
614 service_state = FMD_SERVICE_STATE_DEGRADED;
615 else
616 service_state = FMD_SERVICE_STATE_OK;
617 di_fini(dnode);
618 }
619
620 if (topo_mod_nvalloc(mod, out, NV_UNIQUE_NAME) != 0)
621 return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
622 if (nvlist_add_uint32(*out, TOPO_METH_SERVICE_STATE_RET,
623 service_state) != 0) {
624 nvlist_free(*out);
625 return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
626 }
627
628 return (0);
629 }
630
631 static nvlist_t *
dev_fmri_create(topo_mod_t * mp,const char * id,const char * path)632 dev_fmri_create(topo_mod_t *mp, const char *id, const char *path)
633 {
634 nvlist_t *out = NULL;
635 int e;
636
637 if (topo_mod_nvalloc(mp, &out, NV_UNIQUE_NAME) != 0) {
638 (void) topo_mod_seterrno(mp, EMOD_FMRI_NVL);
639 return (NULL);
640 }
641 e = nvlist_add_string(out, FM_FMRI_SCHEME, FM_FMRI_SCHEME_DEV);
642 e |= nvlist_add_uint8(out, FM_VERSION, FM_DEV_SCHEME_VERSION);
643 e |= nvlist_add_string(out, FM_FMRI_DEV_PATH, path);
644
645 if (id != NULL)
646 e |= nvlist_add_string(out, FM_FMRI_DEV_ID, id);
647
648 if (e == 0)
649 return (out);
650
651 topo_mod_dprintf(mp, "construction of dev nvl failed");
652 (void) topo_mod_seterrno(mp, EMOD_FMRI_NVL);
653 nvlist_free(out);
654 return (NULL);
655 }
656
657 static int
dev_fmri_create_meth(topo_mod_t * mp,tnode_t * node,topo_version_t version,nvlist_t * in,nvlist_t ** out)658 dev_fmri_create_meth(topo_mod_t *mp, tnode_t *node, topo_version_t version,
659 nvlist_t *in, nvlist_t **out)
660 {
661 nvlist_t *args = NULL;
662 char *path, *id = NULL;
663
664 if (version > TOPO_METH_FMRI_VERSION)
665 return (topo_mod_seterrno(mp, EMOD_VER_NEW));
666
667 if (nvlist_lookup_nvlist(in, TOPO_METH_FMRI_ARG_NVL, &args) != 0 ||
668 nvlist_lookup_string(args, FM_FMRI_DEV_PATH, &path) != 0) {
669 topo_mod_dprintf(mp, "no path string in method argument\n");
670 return (topo_mod_seterrno(mp, EMOD_METHOD_INVAL));
671 }
672
673 (void) nvlist_lookup_string(args, FM_FMRI_DEV_ID, &id);
674
675 if ((*out = dev_fmri_create(mp, id, path)) == NULL)
676 return (-1);
677 return (0);
678 }
679