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.
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.
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]
23 * Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
34 #include <libnvpair.h>
35 #include <fm/topo_mod.h>
36 #include <fm/fmd_fmri.h>
37 #include <sys/fm/protocol.h>
39 #include <topo_method.h>
40 #include <topo_subr.h>
43 static int dev_enum(topo_mod_t
*, tnode_t
*, const char *, topo_instance_t
,
44 topo_instance_t
, void *, void *);
45 static void dev_release(topo_mod_t
*, tnode_t
*);
46 static int dev_fmri_nvl2str(topo_mod_t
*, tnode_t
*, topo_version_t
,
47 nvlist_t
*, nvlist_t
**);
48 static int dev_fmri_str2nvl(topo_mod_t
*, tnode_t
*, topo_version_t
,
49 nvlist_t
*, nvlist_t
**);
50 static int dev_fmri_create_meth(topo_mod_t
*, tnode_t
*, topo_version_t
,
51 nvlist_t
*, nvlist_t
**);
52 static int dev_fmri_present(topo_mod_t
*, tnode_t
*, topo_version_t
,
53 nvlist_t
*, nvlist_t
**);
54 static int dev_fmri_replaced(topo_mod_t
*, tnode_t
*, topo_version_t
,
55 nvlist_t
*, nvlist_t
**);
56 static int dev_fmri_unusable(topo_mod_t
*, tnode_t
*, topo_version_t
,
57 nvlist_t
*, nvlist_t
**);
58 static int dev_fmri_service_state(topo_mod_t
*, tnode_t
*, topo_version_t
,
59 nvlist_t
*, nvlist_t
**);
61 static const topo_method_t dev_methods
[] = {
62 { TOPO_METH_NVL2STR
, TOPO_METH_NVL2STR_DESC
, TOPO_METH_NVL2STR_VERSION
,
63 TOPO_STABILITY_INTERNAL
, dev_fmri_nvl2str
},
64 { TOPO_METH_STR2NVL
, TOPO_METH_STR2NVL_DESC
, TOPO_METH_STR2NVL_VERSION
,
65 TOPO_STABILITY_INTERNAL
, dev_fmri_str2nvl
},
66 { TOPO_METH_FMRI
, TOPO_METH_FMRI_DESC
, TOPO_METH_FMRI_VERSION
,
67 TOPO_STABILITY_INTERNAL
, dev_fmri_create_meth
},
68 { TOPO_METH_PRESENT
, TOPO_METH_PRESENT_DESC
, TOPO_METH_PRESENT_VERSION
,
69 TOPO_STABILITY_INTERNAL
, dev_fmri_present
},
70 { TOPO_METH_REPLACED
, TOPO_METH_REPLACED_DESC
,
71 TOPO_METH_REPLACED_VERSION
, TOPO_STABILITY_INTERNAL
,
73 { TOPO_METH_UNUSABLE
, TOPO_METH_UNUSABLE_DESC
,
74 TOPO_METH_UNUSABLE_VERSION
, TOPO_STABILITY_INTERNAL
,
76 { TOPO_METH_SERVICE_STATE
, TOPO_METH_SERVICE_STATE_DESC
,
77 TOPO_METH_SERVICE_STATE_VERSION
, TOPO_STABILITY_INTERNAL
,
78 dev_fmri_service_state
},
82 static const topo_modops_t dev_ops
=
83 { dev_enum
, dev_release
};
84 static const topo_modinfo_t dev_info
=
85 { "dev", FM_FMRI_SCHEME_DEV
, DEV_VERSION
, &dev_ops
};
88 dev_init(topo_mod_t
*mod
, topo_version_t version
)
90 if (getenv("TOPOHCDEBUG"))
91 topo_mod_setdebug(mod
);
92 topo_mod_dprintf(mod
, "initializing dev builtin\n");
94 if (version
!= DEV_VERSION
)
95 return (topo_mod_seterrno(mod
, EMOD_VER_NEW
));
97 if (topo_mod_register(mod
, &dev_info
, TOPO_VERSION
) != 0) {
98 topo_mod_dprintf(mod
, "failed to register dev_info: "
99 "%s\n", topo_mod_errmsg(mod
));
107 dev_fini(topo_mod_t
*mod
)
109 topo_mod_unregister(mod
);
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
)
118 * Methods are registered, but there is no enumeration. Should
119 * enumeration be added be sure to cater for global vs non-global
122 (void) topo_method_register(mod
, pnode
, dev_methods
);
127 dev_release(topo_mod_t
*mod
, tnode_t
*node
)
129 topo_method_unregister_all(mod
, node
);
133 fmri_nvl2str(nvlist_t
*nvl
, char *buf
, size_t buflen
)
135 char *devid
= NULL
, *tpl0id
= NULL
;
136 char *devpath
= NULL
;
141 if (nvlist_lookup_uint8(nvl
, FM_VERSION
, &version
) != 0 ||
142 version
> FM_DEV_SCHEME_VERSION
)
145 /* Get devid, if present */
146 err
= nvlist_lookup_string(nvl
, FM_FMRI_DEV_ID
, &devid
);
147 if (err
!= 0 && err
!= ENOENT
)
150 /* Get target-port-l0id, if present */
151 err
= nvlist_lookup_string(nvl
, FM_FMRI_DEV_TGTPTLUN0
, &tpl0id
);
152 if (err
!= 0 && err
!= ENOENT
)
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
)
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
168 topo_fmristr_build(&size
,
169 buf
, buflen
, FM_FMRI_SCHEME_DEV
, NULL
, ":///");
171 /* device-id part, topo_fmristr_build does nothing if devid is NULL */
172 topo_fmristr_build(&size
,
173 buf
, buflen
, devid
, ":" FM_FMRI_DEV_ID
"=", NULL
);
175 /* target-port-l0id part */
176 topo_fmristr_build(&size
,
177 buf
, buflen
, tpl0id
, ":" FM_FMRI_DEV_TGTPTLUN0
"=", NULL
);
180 * device-path part; the devpath should always start with a /
181 * so you'd think we don't need to add a further / prefix here;
182 * however past implementation has always added the / if
183 * there is a devid component so we continue to do that
184 * so strings match exactly as before. So we can have:
186 * dev:////pci@0,0/...
187 * dev:///<devid-and-tpl0>//pci@0,0/...
189 * where <devid-and-tpl0> =
190 * [:devid=<devid>][:target-port-l0id=<tpl0>]
192 topo_fmristr_build(&size
, buf
, buflen
, devpath
,
193 devid
|| tpl0id
? "/" : NULL
, NULL
);
200 dev_fmri_nvl2str(topo_mod_t
*mod
, tnode_t
*node
, topo_version_t version
,
201 nvlist_t
*nvl
, nvlist_t
**out
)
207 if (version
> TOPO_METH_NVL2STR_VERSION
)
208 return (topo_mod_seterrno(mod
, EMOD_VER_NEW
));
210 if ((len
= fmri_nvl2str(nvl
, NULL
, 0)) == 0 ||
211 (name
= topo_mod_alloc(mod
, len
+ 1)) == NULL
||
212 fmri_nvl2str(nvl
, name
, len
+ 1) == 0) {
214 topo_mod_free(mod
, name
, len
+ 1);
215 return (topo_mod_seterrno(mod
, EMOD_FMRI_NVL
));
218 if (topo_mod_nvalloc(mod
, &fmristr
, NV_UNIQUE_NAME
) != 0)
219 return (topo_mod_seterrno(mod
, EMOD_FMRI_NVL
));
220 if (nvlist_add_string(fmristr
, "fmri-string", name
) != 0) {
221 topo_mod_free(mod
, name
, len
+ 1);
222 nvlist_free(fmristr
);
223 return (topo_mod_seterrno(mod
, EMOD_FMRI_NVL
));
225 topo_mod_free(mod
, name
, len
+ 1);
233 dev_fmri_str2nvl(topo_mod_t
*mod
, tnode_t
*node
, topo_version_t version
,
234 nvlist_t
*in
, nvlist_t
**out
)
236 char *cur
, *devid
= NULL
, *tpl0id
= NULL
;
243 if (version
> TOPO_METH_STR2NVL_VERSION
)
244 return (topo_mod_seterrno(mod
, EMOD_VER_NEW
));
246 if (nvlist_lookup_string(in
, "fmri-string", &str
) != 0)
247 return (topo_mod_seterrno(mod
, EMOD_FMRI_NVL
));
252 * We're expecting a string version of a dev scheme FMRI, and
253 * no fmri authority information.
255 * The shortest legal string would be "dev:////" (len 8) for a string
256 * with no FMRI auth info, no devid or target-port-l0id and
257 * an empty devpath string.
259 if (len
< 8 || strncmp(str
, "dev:///", 7) != 0)
260 return (topo_mod_seterrno(mod
, EMOD_FMRI_MALFORM
));
262 strcp
= alloca(len
+ 1);
263 (void) memcpy(strcp
, str
, len
);
265 cur
= strcp
+ 7; /* already parsed "dev:///" */
268 * If the first character after the "/" that terminates the (empty)
269 * fmri authority is a colon then we have devid and/or target-port-l0id
270 * info. They could be in either order.
272 * If not a colon then it must be the / that begins the devpath.
278 * Look ahead to the "/" that starts the devpath. If not
279 * found or if straight after the : then we're busted.
281 eos
= devpath
= strchr(cur
, '/');
282 if (devpath
== NULL
|| devpath
== cur
+ 1)
283 return (topo_mod_seterrno(mod
, EMOD_FMRI_MALFORM
));
288 * Replace the initial "/" of the devpath with a NUL
289 * to terminate the string before it. We'll undo this
290 * before rendering devpath.
295 * We should now have a NUL-terminated string matching
296 * foo=<pat1>[:bar=<pat2>] (we stepped over the initial :)
297 * Look for a second colon; if found there must be space
298 * after it for the additional component, but no more colons.
300 if ((part
[1] = strchr(cur
, ':')) != NULL
) {
301 if (part
[1] + 1 == eos
||
302 strchr(part
[1] + 1, ':') != NULL
)
303 return (topo_mod_seterrno(mod
,
305 *part
[1] = '\0'; /* terminate part[0] */
309 for (i
= 0; i
< 2; i
++) {
315 if ((eq
= strchr(part
[i
], '=')) == NULL
||
317 return (topo_mod_seterrno(mod
,
321 if (strcmp(part
[i
], FM_FMRI_DEV_ID
) == 0)
323 else if (strcmp(part
[i
], FM_FMRI_DEV_TGTPTLUN0
) == 0)
326 return (topo_mod_seterrno(mod
,
330 if (devid
== NULL
&& tpl0id
== NULL
)
331 return (topo_mod_seterrno(mod
, EMOD_FMRI_MALFORM
));
333 cur
= devpath
; /* initial slash is NULled */
334 } else if (*cur
!= '/') {
335 /* the device-path should start with a slash */
336 return (topo_mod_seterrno(mod
, EMOD_FMRI_MALFORM
));
341 if (topo_mod_nvalloc(mod
, &fmri
, NV_UNIQUE_NAME
) != 0)
342 return (topo_mod_seterrno(mod
, EMOD_FMRI_NVL
));
344 err
= nvlist_add_uint8(fmri
, FM_VERSION
, FM_DEV_SCHEME_VERSION
);
345 err
|= nvlist_add_string(fmri
, FM_FMRI_SCHEME
, FM_FMRI_SCHEME_DEV
);
348 err
|= nvlist_add_string(fmri
, FM_FMRI_DEV_ID
, devid
);
351 err
|= nvlist_add_string(fmri
, FM_FMRI_DEV_TGTPTLUN0
, tpl0id
);
353 if (devid
!= NULL
|| tpl0id
!= NULL
)
354 *devpath
= '/'; /* we NULed this earlier; put it back */
356 /* step over repeated initial / in the devpath */
357 while (*(devpath
+ 1) == '/')
360 err
|= nvlist_add_string(fmri
, FM_FMRI_DEV_PATH
, devpath
);
364 return (topo_mod_seterrno(mod
, EMOD_FMRI_NVL
));
374 dev_fmri_present(topo_mod_t
*mod
, tnode_t
*node
, topo_version_t version
,
375 nvlist_t
*in
, nvlist_t
**out
)
378 char *devpath
= NULL
;
380 char *devid
= NULL
, *path
;
387 if (version
> TOPO_METH_PRESENT_VERSION
)
388 return (topo_mod_seterrno(mod
, EMOD_VER_NEW
));
390 if (nvlist_lookup_uint8(in
, FM_VERSION
, &fmversion
) != 0 ||
391 fmversion
> FM_DEV_SCHEME_VERSION
||
392 nvlist_lookup_string(in
, FM_FMRI_DEV_PATH
, &devpath
) != 0)
393 return (topo_mod_seterrno(mod
, EMOD_FMRI_MALFORM
));
395 (void) nvlist_lookup_string(in
, FM_FMRI_DEV_ID
, &devid
);
397 if (devpath
== NULL
|| strlen(devpath
) == 0)
398 return (topo_mod_seterrno(mod
, EMOD_FMRI_MALFORM
));
401 * stat() the device node in devfs. This will tell us if the device is
402 * present or not. Don't stat the minor, just the whole device.
403 * If the device is present and there is a devid, it must also match.
404 * so di_init that one node. No need for DINFOFORCE.
406 len
= strlen(devpath
) + strlen("/devices") + 1;
407 path
= topo_mod_alloc(mod
, len
);
408 (void) snprintf(path
, len
, "/devices%s", devpath
);
410 if (stat(path
, &sb
) != -1)
412 else if ((dnode
= di_init("/", DINFOCACHE
)) == DI_NODE_NIL
)
415 if (di_lookup_node(dnode
, devpath
) == DI_NODE_NIL
)
422 if (stat(path
, &sb
) == -1)
424 else if ((dnode
= di_init(devpath
, DINFOCPYONE
)) == DI_NODE_NIL
)
427 if ((id
= di_devid(dnode
)) == NULL
||
428 devid_str_decode(devid
, &matchid
, NULL
) != 0)
431 if (devid_compare(id
, matchid
) != 0)
440 topo_mod_free(mod
, path
, len
);
442 if (topo_mod_nvalloc(mod
, out
, NV_UNIQUE_NAME
) != 0)
443 return (topo_mod_seterrno(mod
, EMOD_NVL_INVAL
));
444 if (nvlist_add_uint32(*out
, TOPO_METH_PRESENT_RET
, present
) != 0) {
446 return (topo_mod_seterrno(mod
, EMOD_NVL_INVAL
));
454 dev_fmri_replaced(topo_mod_t
*mod
, tnode_t
*node
, topo_version_t version
,
455 nvlist_t
*in
, nvlist_t
**out
)
458 char *devpath
= NULL
;
460 char *devid
= NULL
, *path
;
467 if (version
> TOPO_METH_REPLACED_VERSION
)
468 return (topo_mod_seterrno(mod
, EMOD_VER_NEW
));
470 if (nvlist_lookup_uint8(in
, FM_VERSION
, &fmversion
) != 0 ||
471 fmversion
> FM_DEV_SCHEME_VERSION
||
472 nvlist_lookup_string(in
, FM_FMRI_DEV_PATH
, &devpath
) != 0)
473 return (topo_mod_seterrno(mod
, EMOD_FMRI_MALFORM
));
475 (void) nvlist_lookup_string(in
, FM_FMRI_DEV_ID
, &devid
);
477 if (devpath
== NULL
|| strlen(devpath
) == 0)
478 return (topo_mod_seterrno(mod
, EMOD_FMRI_MALFORM
));
481 * stat() the device node in devfs. This will tell us if the device is
482 * present or not. Don't stat the minor, just the whole device.
483 * If the device is present and there is a devid, it must also match.
484 * so di_init that one node. No need for DINFOFORCE.
486 len
= strlen(devpath
) + strlen("/devices") + 1;
487 path
= topo_mod_alloc(mod
, len
);
488 (void) snprintf(path
, len
, "/devices%s", devpath
);
490 if (stat(path
, &sb
) != -1)
491 rval
= FMD_OBJ_STATE_UNKNOWN
;
492 else if ((dnode
= di_init("/", DINFOCACHE
)) == DI_NODE_NIL
)
493 rval
= FMD_OBJ_STATE_UNKNOWN
;
495 if (di_lookup_node(dnode
, devpath
) == DI_NODE_NIL
)
496 rval
= FMD_OBJ_STATE_UNKNOWN
;
498 rval
= FMD_OBJ_STATE_UNKNOWN
;
502 if (stat(path
, &sb
) == -1)
503 rval
= FMD_OBJ_STATE_UNKNOWN
;
504 else if ((dnode
= di_init(devpath
, DINFOCPYONE
)) == DI_NODE_NIL
)
505 rval
= FMD_OBJ_STATE_UNKNOWN
;
507 if ((id
= di_devid(dnode
)) == NULL
||
508 devid_str_decode(devid
, &matchid
, NULL
) != 0)
509 rval
= FMD_OBJ_STATE_UNKNOWN
;
511 if (devid_compare(id
, matchid
) != 0)
512 rval
= FMD_OBJ_STATE_REPLACED
;
514 rval
= FMD_OBJ_STATE_STILL_PRESENT
;
520 topo_mod_free(mod
, path
, len
);
522 if (topo_mod_nvalloc(mod
, out
, NV_UNIQUE_NAME
) != 0)
523 return (topo_mod_seterrno(mod
, EMOD_NVL_INVAL
));
524 if (nvlist_add_uint32(*out
, TOPO_METH_REPLACED_RET
, rval
) != 0) {
526 return (topo_mod_seterrno(mod
, EMOD_NVL_INVAL
));
534 dev_fmri_unusable(topo_mod_t
*mod
, tnode_t
*node
, topo_version_t version
,
535 nvlist_t
*in
, nvlist_t
**out
)
539 char *devpath
= NULL
;
543 if (version
> TOPO_METH_UNUSABLE_VERSION
)
544 return (topo_mod_seterrno(mod
, EMOD_VER_NEW
));
546 if (nvlist_lookup_uint8(in
, FM_VERSION
, &fmversion
) != 0 ||
547 fmversion
> FM_DEV_SCHEME_VERSION
||
548 nvlist_lookup_string(in
, FM_FMRI_DEV_PATH
, &devpath
) != 0)
549 return (topo_mod_seterrno(mod
, EMOD_FMRI_MALFORM
));
552 return (topo_mod_seterrno(mod
, EMOD_FMRI_MALFORM
));
554 if ((dnode
= di_init(devpath
, DINFOCPYONE
)) == DI_NODE_NIL
) {
556 return (topo_mod_seterrno(mod
, EMOD_UKNOWN_ENUM
));
559 uint_t retired
= di_retired(dnode
);
560 state
= di_state(dnode
);
561 if (retired
|| (state
& (DI_DEVICE_OFFLINE
| DI_DEVICE_DOWN
|
562 DI_BUS_QUIESCED
| DI_BUS_DOWN
)))
569 if (topo_mod_nvalloc(mod
, out
, NV_UNIQUE_NAME
) != 0)
570 return (topo_mod_seterrno(mod
, EMOD_NVL_INVAL
));
571 if (nvlist_add_uint32(*out
, TOPO_METH_UNUSABLE_RET
, unusable
) != 0) {
573 return (topo_mod_seterrno(mod
, EMOD_NVL_INVAL
));
581 dev_fmri_service_state(topo_mod_t
*mod
, tnode_t
*node
, topo_version_t version
,
582 nvlist_t
*in
, nvlist_t
**out
)
586 char *devpath
= NULL
;
587 uint32_t service_state
;
590 if (version
> TOPO_METH_SERVICE_STATE_VERSION
)
591 return (topo_mod_seterrno(mod
, EMOD_VER_NEW
));
593 if (nvlist_lookup_uint8(in
, FM_VERSION
, &fmversion
) != 0 ||
594 fmversion
> FM_DEV_SCHEME_VERSION
||
595 nvlist_lookup_string(in
, FM_FMRI_DEV_PATH
, &devpath
) != 0)
596 return (topo_mod_seterrno(mod
, EMOD_FMRI_MALFORM
));
599 return (topo_mod_seterrno(mod
, EMOD_FMRI_MALFORM
));
601 if ((dnode
= di_init(devpath
, DINFOCPYONE
)) == DI_NODE_NIL
) {
603 return (topo_mod_seterrno(mod
, EMOD_UKNOWN_ENUM
));
604 service_state
= FMD_SERVICE_STATE_UNUSABLE
;
606 uint_t retired
= di_retired(dnode
);
607 state
= di_state(dnode
);
608 if (retired
|| (state
& (DI_DEVICE_OFFLINE
| DI_DEVICE_DOWN
|
609 DI_BUS_QUIESCED
| DI_BUS_DOWN
)))
610 service_state
= FMD_SERVICE_STATE_UNUSABLE
;
611 else if (state
& DI_DEVICE_DEGRADED
)
612 service_state
= FMD_SERVICE_STATE_DEGRADED
;
614 service_state
= FMD_SERVICE_STATE_OK
;
618 if (topo_mod_nvalloc(mod
, out
, NV_UNIQUE_NAME
) != 0)
619 return (topo_mod_seterrno(mod
, EMOD_NVL_INVAL
));
620 if (nvlist_add_uint32(*out
, TOPO_METH_SERVICE_STATE_RET
,
621 service_state
) != 0) {
623 return (topo_mod_seterrno(mod
, EMOD_NVL_INVAL
));
630 dev_fmri_create(topo_mod_t
*mp
, const char *id
, const char *path
)
632 nvlist_t
*out
= NULL
;
635 if (topo_mod_nvalloc(mp
, &out
, NV_UNIQUE_NAME
) != 0) {
636 (void) topo_mod_seterrno(mp
, EMOD_FMRI_NVL
);
639 e
= nvlist_add_string(out
, FM_FMRI_SCHEME
, FM_FMRI_SCHEME_DEV
);
640 e
|= nvlist_add_uint8(out
, FM_VERSION
, FM_DEV_SCHEME_VERSION
);
641 e
|= nvlist_add_string(out
, FM_FMRI_DEV_PATH
, path
);
644 e
|= nvlist_add_string(out
, FM_FMRI_DEV_ID
, id
);
649 topo_mod_dprintf(mp
, "construction of dev nvl failed");
650 (void) topo_mod_seterrno(mp
, EMOD_FMRI_NVL
);
657 dev_fmri_create_meth(topo_mod_t
*mp
, tnode_t
*node
, topo_version_t version
,
658 nvlist_t
*in
, nvlist_t
**out
)
660 nvlist_t
*args
= NULL
;
661 char *path
, *id
= NULL
;
663 if (version
> TOPO_METH_FMRI_VERSION
)
664 return (topo_mod_seterrno(mp
, EMOD_VER_NEW
));
666 if (nvlist_lookup_nvlist(in
, TOPO_METH_FMRI_ARG_NVL
, &args
) != 0 ||
667 nvlist_lookup_string(args
, FM_FMRI_DEV_PATH
, &path
) != 0) {
668 topo_mod_dprintf(mp
, "no path string in method argument\n");
669 return (topo_mod_seterrno(mp
, EMOD_METHOD_INVAL
));
672 (void) nvlist_lookup_string(args
, FM_FMRI_DEV_ID
, &id
);
674 if ((*out
= dev_fmri_create(mp
, id
, path
)) == NULL
)