11541Stimh /*
21541Stimh * CDDL HEADER START
31541Stimh *
41541Stimh * The contents of this file are subject to the terms of the
51541Stimh * Common Development and Distribution License (the "License").
61541Stimh * You may not use this file except in compliance with the License.
71541Stimh *
81541Stimh * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
91541Stimh * or http://www.opensolaris.org/os/licensing.
101541Stimh * See the License for the specific language governing permissions
111541Stimh * and limitations under the License.
121541Stimh *
131541Stimh * When distributing Covered Code, include this CDDL HEADER in each
141541Stimh * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
151541Stimh * If applicable, add the following below this CDDL HEADER, with the
161541Stimh * fields enclosed by brackets "[]" replaced with your own identifying
171541Stimh * information: Portions Copyright [yyyy] [name of copyright owner]
181541Stimh *
191541Stimh * CDDL HEADER END
201541Stimh */
211541Stimh
221541Stimh /*
2312213SGavin.Maltby@Sun.COM * Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
241541Stimh */
251541Stimh
261541Stimh #include <limits.h>
271541Stimh #include <strings.h>
281541Stimh #include <string.h>
291541Stimh #include <unistd.h>
301541Stimh #include <stdio.h>
311541Stimh #include <alloca.h>
324198Seschrock #include <devid.h>
336135Sstephh #include <sys/stat.h>
341541Stimh #include <libnvpair.h>
351541Stimh #include <fm/topo_mod.h>
367275Sstephh #include <fm/fmd_fmri.h>
371541Stimh #include <sys/fm/protocol.h>
381541Stimh
393062Scindi #include <topo_method.h>
401541Stimh #include <topo_subr.h>
413062Scindi #include <dev.h>
421541Stimh
431541Stimh static int dev_enum(topo_mod_t *, tnode_t *, const char *, topo_instance_t,
443062Scindi topo_instance_t, void *, void *);
451541Stimh static void dev_release(topo_mod_t *, tnode_t *);
461541Stimh static int dev_fmri_nvl2str(topo_mod_t *, tnode_t *, topo_version_t,
471541Stimh nvlist_t *, nvlist_t **);
481541Stimh static int dev_fmri_str2nvl(topo_mod_t *, tnode_t *, topo_version_t,
491541Stimh nvlist_t *, nvlist_t **);
501541Stimh static int dev_fmri_create_meth(topo_mod_t *, tnode_t *, topo_version_t,
511541Stimh nvlist_t *, nvlist_t **);
524198Seschrock static int dev_fmri_present(topo_mod_t *, tnode_t *, topo_version_t,
534198Seschrock nvlist_t *, nvlist_t **);
547275Sstephh static int dev_fmri_replaced(topo_mod_t *, tnode_t *, topo_version_t,
557275Sstephh nvlist_t *, nvlist_t **);
564198Seschrock static int dev_fmri_unusable(topo_mod_t *, tnode_t *, topo_version_t,
574198Seschrock nvlist_t *, nvlist_t **);
587275Sstephh static int dev_fmri_service_state(topo_mod_t *, tnode_t *, topo_version_t,
597275Sstephh nvlist_t *, nvlist_t **);
601541Stimh
611541Stimh static const topo_method_t dev_methods[] = {
621541Stimh { TOPO_METH_NVL2STR, TOPO_METH_NVL2STR_DESC, TOPO_METH_NVL2STR_VERSION,
631541Stimh TOPO_STABILITY_INTERNAL, dev_fmri_nvl2str },
641541Stimh { TOPO_METH_STR2NVL, TOPO_METH_STR2NVL_DESC, TOPO_METH_STR2NVL_VERSION,
651541Stimh TOPO_STABILITY_INTERNAL, dev_fmri_str2nvl },
661541Stimh { TOPO_METH_FMRI, TOPO_METH_FMRI_DESC, TOPO_METH_FMRI_VERSION,
671541Stimh TOPO_STABILITY_INTERNAL, dev_fmri_create_meth },
684198Seschrock { TOPO_METH_PRESENT, TOPO_METH_PRESENT_DESC, TOPO_METH_PRESENT_VERSION,
694198Seschrock TOPO_STABILITY_INTERNAL, dev_fmri_present },
707275Sstephh { TOPO_METH_REPLACED, TOPO_METH_REPLACED_DESC,
717275Sstephh TOPO_METH_REPLACED_VERSION, TOPO_STABILITY_INTERNAL,
727275Sstephh dev_fmri_replaced },
734198Seschrock { TOPO_METH_UNUSABLE, TOPO_METH_UNUSABLE_DESC,
744198Seschrock TOPO_METH_UNUSABLE_VERSION, TOPO_STABILITY_INTERNAL,
754198Seschrock dev_fmri_unusable },
767275Sstephh { TOPO_METH_SERVICE_STATE, TOPO_METH_SERVICE_STATE_DESC,
777275Sstephh TOPO_METH_SERVICE_STATE_VERSION, TOPO_STABILITY_INTERNAL,
787275Sstephh dev_fmri_service_state },
791541Stimh { NULL }
801541Stimh };
811541Stimh
823062Scindi static const topo_modops_t dev_ops =
833062Scindi { dev_enum, dev_release };
841541Stimh static const topo_modinfo_t dev_info =
853062Scindi { "dev", FM_FMRI_SCHEME_DEV, DEV_VERSION, &dev_ops };
861541Stimh
873062Scindi int
dev_init(topo_mod_t * mod,topo_version_t version)883062Scindi dev_init(topo_mod_t *mod, topo_version_t version)
891541Stimh {
903062Scindi if (getenv("TOPOHCDEBUG"))
913062Scindi topo_mod_setdebug(mod);
921541Stimh topo_mod_dprintf(mod, "initializing dev builtin\n");
931541Stimh
943062Scindi if (version != DEV_VERSION)
953062Scindi return (topo_mod_seterrno(mod, EMOD_VER_NEW));
963062Scindi
973062Scindi if (topo_mod_register(mod, &dev_info, TOPO_VERSION) != 0) {
981541Stimh topo_mod_dprintf(mod, "failed to register dev_info: "
991541Stimh "%s\n", topo_mod_errmsg(mod));
1003062Scindi return (-1);
1011541Stimh }
1023062Scindi
1033062Scindi return (0);
1041541Stimh }
1051541Stimh
1061541Stimh void
dev_fini(topo_mod_t * mod)1071541Stimh dev_fini(topo_mod_t *mod)
1081541Stimh {
1091541Stimh topo_mod_unregister(mod);
1101541Stimh }
1111541Stimh
1121541Stimh /*ARGSUSED*/
1131541Stimh 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)1141541Stimh dev_enum(topo_mod_t *mod, tnode_t *pnode, const char *name,
1153062Scindi topo_instance_t min, topo_instance_t max, void *notused1, void *notused2)
1161541Stimh {
117*12967Sgavin.maltby@oracle.com /*
118*12967Sgavin.maltby@oracle.com * Methods are registered, but there is no enumeration. Should
119*12967Sgavin.maltby@oracle.com * enumeration be added be sure to cater for global vs non-global
120*12967Sgavin.maltby@oracle.com * zones.
121*12967Sgavin.maltby@oracle.com */
1221541Stimh (void) topo_method_register(mod, pnode, dev_methods);
1231541Stimh return (0);
1241541Stimh }
1251541Stimh
1261541Stimh static void
dev_release(topo_mod_t * mod,tnode_t * node)1271541Stimh dev_release(topo_mod_t *mod, tnode_t *node)
1281541Stimh {
1291541Stimh topo_method_unregister_all(mod, node);
1301541Stimh }
1311541Stimh
1321541Stimh static ssize_t
fmri_nvl2str(nvlist_t * nvl,char * buf,size_t buflen)1331541Stimh fmri_nvl2str(nvlist_t *nvl, char *buf, size_t buflen)
1341541Stimh {
13512213SGavin.Maltby@Sun.COM char *devid = NULL, *tpl0id = NULL;
13612213SGavin.Maltby@Sun.COM char *devpath = NULL;
1371541Stimh ssize_t size = 0;
13812213SGavin.Maltby@Sun.COM uint8_t version;
1391541Stimh int err;
1401541Stimh
1411541Stimh if (nvlist_lookup_uint8(nvl, FM_VERSION, &version) != 0 ||
1421541Stimh version > FM_DEV_SCHEME_VERSION)
1431541Stimh return (-1);
1441541Stimh
14512213SGavin.Maltby@Sun.COM /* Get devid, if present */
14612213SGavin.Maltby@Sun.COM err = nvlist_lookup_string(nvl, FM_FMRI_DEV_ID, &devid);
1471541Stimh if (err != 0 && err != ENOENT)
1481541Stimh return (-1);
1491541Stimh
15012213SGavin.Maltby@Sun.COM /* Get target-port-l0id, if present */
15112213SGavin.Maltby@Sun.COM err = nvlist_lookup_string(nvl, FM_FMRI_DEV_TGTPTLUN0, &tpl0id);
1521541Stimh if (err != 0 && err != ENOENT)
1531541Stimh return (-1);
1541541Stimh
1551541Stimh /* There must be a device path present */
1561541Stimh err = nvlist_lookup_string(nvl, FM_FMRI_DEV_PATH, &devpath);
1571541Stimh if (err != 0 || devpath == NULL)
1581541Stimh return (-1);
1591541Stimh
16012213SGavin.Maltby@Sun.COM /*
16112213SGavin.Maltby@Sun.COM * dev:///
16212213SGavin.Maltby@Sun.COM *
16312213SGavin.Maltby@Sun.COM * The dev scheme does not render fmri authority information
16412213SGavin.Maltby@Sun.COM * in the string form of an fmri. It is meaningless to
16512213SGavin.Maltby@Sun.COM * transmit a dev scheme fmri outside of the immediate fault
16612213SGavin.Maltby@Sun.COM * manager.
16712213SGavin.Maltby@Sun.COM */
1681541Stimh topo_fmristr_build(&size,
16912213SGavin.Maltby@Sun.COM buf, buflen, FM_FMRI_SCHEME_DEV, NULL, ":///");
1701541Stimh
1711541Stimh /* device-id part, topo_fmristr_build does nothing if devid is NULL */
1721541Stimh topo_fmristr_build(&size,
17312213SGavin.Maltby@Sun.COM buf, buflen, devid, ":" FM_FMRI_DEV_ID "=", NULL);
17412213SGavin.Maltby@Sun.COM
17512213SGavin.Maltby@Sun.COM /* target-port-l0id part */
17612213SGavin.Maltby@Sun.COM topo_fmristr_build(&size,
17712213SGavin.Maltby@Sun.COM buf, buflen, tpl0id, ":" FM_FMRI_DEV_TGTPTLUN0 "=", NULL);
1781541Stimh
17912213SGavin.Maltby@Sun.COM /*
18012213SGavin.Maltby@Sun.COM * device-path part; the devpath should always start with a /
18112213SGavin.Maltby@Sun.COM * so you'd think we don't need to add a further / prefix here;
18212213SGavin.Maltby@Sun.COM * however past implementation has always added the / if
18312213SGavin.Maltby@Sun.COM * there is a devid component so we continue to do that
18412213SGavin.Maltby@Sun.COM * so strings match exactly as before. So we can have:
18512213SGavin.Maltby@Sun.COM *
18612213SGavin.Maltby@Sun.COM * dev:////pci@0,0/...
18712213SGavin.Maltby@Sun.COM * dev:///<devid-and-tpl0>//pci@0,0/...
18812213SGavin.Maltby@Sun.COM *
18912213SGavin.Maltby@Sun.COM * where <devid-and-tpl0> =
19012213SGavin.Maltby@Sun.COM * [:devid=<devid>][:target-port-l0id=<tpl0>]
19112213SGavin.Maltby@Sun.COM */
19212213SGavin.Maltby@Sun.COM topo_fmristr_build(&size, buf, buflen, devpath,
19312213SGavin.Maltby@Sun.COM devid || tpl0id ? "/" : NULL, NULL);
1941541Stimh
1951541Stimh return (size);
1961541Stimh }
1971541Stimh
1981541Stimh /*ARGSUSED*/
1991541Stimh static int
dev_fmri_nvl2str(topo_mod_t * mod,tnode_t * node,topo_version_t version,nvlist_t * nvl,nvlist_t ** out)2001541Stimh dev_fmri_nvl2str(topo_mod_t *mod, tnode_t *node, topo_version_t version,
2011541Stimh nvlist_t *nvl, nvlist_t **out)
2021541Stimh {
2031541Stimh ssize_t len;
2041541Stimh char *name = NULL;
2051541Stimh nvlist_t *fmristr;
2061541Stimh
2071541Stimh if (version > TOPO_METH_NVL2STR_VERSION)
2081541Stimh return (topo_mod_seterrno(mod, EMOD_VER_NEW));
2091541Stimh
2101541Stimh if ((len = fmri_nvl2str(nvl, NULL, 0)) == 0 ||
2111541Stimh (name = topo_mod_alloc(mod, len + 1)) == NULL ||
2121541Stimh fmri_nvl2str(nvl, name, len + 1) == 0) {
2131541Stimh if (name != NULL)
2141541Stimh topo_mod_free(mod, name, len + 1);
2151541Stimh return (topo_mod_seterrno(mod, EMOD_FMRI_NVL));
2161541Stimh }
2171541Stimh
2181541Stimh if (topo_mod_nvalloc(mod, &fmristr, NV_UNIQUE_NAME) != 0)
2191541Stimh return (topo_mod_seterrno(mod, EMOD_FMRI_NVL));
2201541Stimh if (nvlist_add_string(fmristr, "fmri-string", name) != 0) {
2211541Stimh topo_mod_free(mod, name, len + 1);
2221541Stimh nvlist_free(fmristr);
2231541Stimh return (topo_mod_seterrno(mod, EMOD_FMRI_NVL));
2241541Stimh }
2251541Stimh topo_mod_free(mod, name, len + 1);
2261541Stimh *out = fmristr;
2271541Stimh
2281541Stimh return (0);
2291541Stimh }
2301541Stimh
2311541Stimh /*ARGSUSED*/
2321541Stimh static int
dev_fmri_str2nvl(topo_mod_t * mod,tnode_t * node,topo_version_t version,nvlist_t * in,nvlist_t ** out)2331541Stimh dev_fmri_str2nvl(topo_mod_t *mod, tnode_t *node, topo_version_t version,
2341541Stimh nvlist_t *in, nvlist_t **out)
2351541Stimh {
23612213SGavin.Maltby@Sun.COM char *cur, *devid = NULL, *tpl0id = NULL;
23712213SGavin.Maltby@Sun.COM char *str, *strcp;
2381541Stimh nvlist_t *fmri;
2391541Stimh char *devpath;
24012213SGavin.Maltby@Sun.COM size_t len;
2411541Stimh int err;
2421541Stimh
2431541Stimh if (version > TOPO_METH_STR2NVL_VERSION)
2441541Stimh return (topo_mod_seterrno(mod, EMOD_VER_NEW));
2451541Stimh
2461541Stimh if (nvlist_lookup_string(in, "fmri-string", &str) != 0)
2471541Stimh return (topo_mod_seterrno(mod, EMOD_FMRI_NVL));
2481541Stimh
24912213SGavin.Maltby@Sun.COM len = strlen(str);
25012213SGavin.Maltby@Sun.COM
25112213SGavin.Maltby@Sun.COM /*
25212213SGavin.Maltby@Sun.COM * We're expecting a string version of a dev scheme FMRI, and
25312213SGavin.Maltby@Sun.COM * no fmri authority information.
25412213SGavin.Maltby@Sun.COM *
25512213SGavin.Maltby@Sun.COM * The shortest legal string would be "dev:////" (len 8) for a string
25612213SGavin.Maltby@Sun.COM * with no FMRI auth info, no devid or target-port-l0id and
25712213SGavin.Maltby@Sun.COM * an empty devpath string.
25812213SGavin.Maltby@Sun.COM */
25912213SGavin.Maltby@Sun.COM if (len < 8 || strncmp(str, "dev:///", 7) != 0)
2601541Stimh return (topo_mod_seterrno(mod, EMOD_FMRI_MALFORM));
2611541Stimh
26212213SGavin.Maltby@Sun.COM strcp = alloca(len + 1);
26312213SGavin.Maltby@Sun.COM (void) memcpy(strcp, str, len);
26412213SGavin.Maltby@Sun.COM strcp[len] = '\0';
26512213SGavin.Maltby@Sun.COM cur = strcp + 7; /* already parsed "dev:///" */
2661541Stimh
26712213SGavin.Maltby@Sun.COM /*
26812213SGavin.Maltby@Sun.COM * If the first character after the "/" that terminates the (empty)
26912213SGavin.Maltby@Sun.COM * fmri authority is a colon then we have devid and/or target-port-l0id
27012213SGavin.Maltby@Sun.COM * info. They could be in either order.
27112213SGavin.Maltby@Sun.COM *
27212213SGavin.Maltby@Sun.COM * If not a colon then it must be the / that begins the devpath.
27312213SGavin.Maltby@Sun.COM */
27412213SGavin.Maltby@Sun.COM if (*cur == ':') {
27512213SGavin.Maltby@Sun.COM char *eos, *part[2];
27612213SGavin.Maltby@Sun.COM int i;
27712213SGavin.Maltby@Sun.COM /*
27812213SGavin.Maltby@Sun.COM * Look ahead to the "/" that starts the devpath. If not
27912213SGavin.Maltby@Sun.COM * found or if straight after the : then we're busted.
28012213SGavin.Maltby@Sun.COM */
28112213SGavin.Maltby@Sun.COM eos = devpath = strchr(cur, '/');
28212213SGavin.Maltby@Sun.COM if (devpath == NULL || devpath == cur + 1)
2831541Stimh return (topo_mod_seterrno(mod, EMOD_FMRI_MALFORM));
28412213SGavin.Maltby@Sun.COM
28512213SGavin.Maltby@Sun.COM part[0] = ++cur;
28612213SGavin.Maltby@Sun.COM
28712213SGavin.Maltby@Sun.COM /*
28812213SGavin.Maltby@Sun.COM * Replace the initial "/" of the devpath with a NUL
28912213SGavin.Maltby@Sun.COM * to terminate the string before it. We'll undo this
29012213SGavin.Maltby@Sun.COM * before rendering devpath.
29112213SGavin.Maltby@Sun.COM */
29212213SGavin.Maltby@Sun.COM *eos = '\0';
29312213SGavin.Maltby@Sun.COM
29412213SGavin.Maltby@Sun.COM /*
29512213SGavin.Maltby@Sun.COM * We should now have a NUL-terminated string matching
29612213SGavin.Maltby@Sun.COM * foo=<pat1>[:bar=<pat2>] (we stepped over the initial :)
29712213SGavin.Maltby@Sun.COM * Look for a second colon; if found there must be space
29812213SGavin.Maltby@Sun.COM * after it for the additional component, but no more colons.
29912213SGavin.Maltby@Sun.COM */
30012213SGavin.Maltby@Sun.COM if ((part[1] = strchr(cur, ':')) != NULL) {
30112213SGavin.Maltby@Sun.COM if (part[1] + 1 == eos ||
30212213SGavin.Maltby@Sun.COM strchr(part[1] + 1, ':') != NULL)
30312213SGavin.Maltby@Sun.COM return (topo_mod_seterrno(mod,
30412213SGavin.Maltby@Sun.COM EMOD_FMRI_MALFORM));
30512213SGavin.Maltby@Sun.COM *part[1] = '\0'; /* terminate part[0] */
30612213SGavin.Maltby@Sun.COM part[1]++;
30712213SGavin.Maltby@Sun.COM }
30812213SGavin.Maltby@Sun.COM
30912213SGavin.Maltby@Sun.COM for (i = 0; i < 2; i++) {
31012213SGavin.Maltby@Sun.COM char *eq;
31112213SGavin.Maltby@Sun.COM
31212213SGavin.Maltby@Sun.COM if (!part[i])
31312213SGavin.Maltby@Sun.COM continue;
31412213SGavin.Maltby@Sun.COM
31512213SGavin.Maltby@Sun.COM if ((eq = strchr(part[i], '=')) == NULL ||
31612213SGavin.Maltby@Sun.COM *(eq + 1) == '\0')
31712213SGavin.Maltby@Sun.COM return (topo_mod_seterrno(mod,
31812213SGavin.Maltby@Sun.COM EMOD_FMRI_MALFORM));
31912213SGavin.Maltby@Sun.COM
32012213SGavin.Maltby@Sun.COM *eq = '\0';
32112213SGavin.Maltby@Sun.COM if (strcmp(part[i], FM_FMRI_DEV_ID) == 0)
32212213SGavin.Maltby@Sun.COM devid = eq + 1;
32312213SGavin.Maltby@Sun.COM else if (strcmp(part[i], FM_FMRI_DEV_TGTPTLUN0) == 0)
32412213SGavin.Maltby@Sun.COM tpl0id = eq + 1;
32512213SGavin.Maltby@Sun.COM else
32612213SGavin.Maltby@Sun.COM return (topo_mod_seterrno(mod,
32712213SGavin.Maltby@Sun.COM EMOD_FMRI_MALFORM));
32812213SGavin.Maltby@Sun.COM }
32912213SGavin.Maltby@Sun.COM
33012213SGavin.Maltby@Sun.COM if (devid == NULL && tpl0id == NULL)
33112213SGavin.Maltby@Sun.COM return (topo_mod_seterrno(mod, EMOD_FMRI_MALFORM));
33212213SGavin.Maltby@Sun.COM
33312213SGavin.Maltby@Sun.COM cur = devpath; /* initial slash is NULled */
33412213SGavin.Maltby@Sun.COM } else if (*cur != '/') {
33512213SGavin.Maltby@Sun.COM /* the device-path should start with a slash */
33612213SGavin.Maltby@Sun.COM return (topo_mod_seterrno(mod, EMOD_FMRI_MALFORM));
33712213SGavin.Maltby@Sun.COM } else {
33812213SGavin.Maltby@Sun.COM devpath = cur;
3391541Stimh }
3401541Stimh
3411541Stimh if (topo_mod_nvalloc(mod, &fmri, NV_UNIQUE_NAME) != 0)
3421541Stimh return (topo_mod_seterrno(mod, EMOD_FMRI_NVL));
3431541Stimh
3441541Stimh err = nvlist_add_uint8(fmri, FM_VERSION, FM_DEV_SCHEME_VERSION);
3451541Stimh err |= nvlist_add_string(fmri, FM_FMRI_SCHEME, FM_FMRI_SCHEME_DEV);
34612213SGavin.Maltby@Sun.COM
3471541Stimh if (devid != NULL)
3481541Stimh err |= nvlist_add_string(fmri, FM_FMRI_DEV_ID, devid);
3491541Stimh
35012213SGavin.Maltby@Sun.COM if (tpl0id != NULL)
35112213SGavin.Maltby@Sun.COM err |= nvlist_add_string(fmri, FM_FMRI_DEV_TGTPTLUN0, tpl0id);
35212213SGavin.Maltby@Sun.COM
35312213SGavin.Maltby@Sun.COM if (devid != NULL || tpl0id != NULL)
35412213SGavin.Maltby@Sun.COM *devpath = '/'; /* we NULed this earlier; put it back */
35512213SGavin.Maltby@Sun.COM
35612213SGavin.Maltby@Sun.COM /* step over repeated initial / in the devpath */
35712213SGavin.Maltby@Sun.COM while (*(devpath + 1) == '/')
35812213SGavin.Maltby@Sun.COM devpath++;
35912213SGavin.Maltby@Sun.COM
36012213SGavin.Maltby@Sun.COM err |= nvlist_add_string(fmri, FM_FMRI_DEV_PATH, devpath);
36112213SGavin.Maltby@Sun.COM
3621541Stimh if (err != 0) {
3631541Stimh nvlist_free(fmri);
3641541Stimh return (topo_mod_seterrno(mod, EMOD_FMRI_NVL));
3651541Stimh }
36612213SGavin.Maltby@Sun.COM
3671541Stimh *out = fmri;
3681541Stimh
3691541Stimh return (0);
3701541Stimh }
3711541Stimh
3724198Seschrock /*ARGSUSED*/
3734198Seschrock static int
dev_fmri_present(topo_mod_t * mod,tnode_t * node,topo_version_t version,nvlist_t * in,nvlist_t ** out)3744198Seschrock dev_fmri_present(topo_mod_t *mod, tnode_t *node, topo_version_t version,
3754198Seschrock nvlist_t *in, nvlist_t **out)
3764198Seschrock {
3774198Seschrock uint8_t fmversion;
3784198Seschrock char *devpath = NULL;
3794198Seschrock uint32_t present;
3806135Sstephh char *devid = NULL, *path;
3816135Sstephh ddi_devid_t id;
3826135Sstephh ddi_devid_t matchid;
3836135Sstephh di_node_t dnode;
3846135Sstephh struct stat sb;
3856135Sstephh int len;
3864198Seschrock
3874198Seschrock if (version > TOPO_METH_PRESENT_VERSION)
3884198Seschrock return (topo_mod_seterrno(mod, EMOD_VER_NEW));
3894198Seschrock
3904198Seschrock if (nvlist_lookup_uint8(in, FM_VERSION, &fmversion) != 0 ||
3914198Seschrock fmversion > FM_DEV_SCHEME_VERSION ||
3924198Seschrock nvlist_lookup_string(in, FM_FMRI_DEV_PATH, &devpath) != 0)
3934198Seschrock return (topo_mod_seterrno(mod, EMOD_FMRI_MALFORM));
3944198Seschrock
3954198Seschrock (void) nvlist_lookup_string(in, FM_FMRI_DEV_ID, &devid);
3964198Seschrock
3976135Sstephh if (devpath == NULL || strlen(devpath) == 0)
3984198Seschrock return (topo_mod_seterrno(mod, EMOD_FMRI_MALFORM));
3994198Seschrock
4004198Seschrock /*
4016135Sstephh * stat() the device node in devfs. This will tell us if the device is
4026135Sstephh * present or not. Don't stat the minor, just the whole device.
4036135Sstephh * If the device is present and there is a devid, it must also match.
4046135Sstephh * so di_init that one node. No need for DINFOFORCE.
4054198Seschrock */
4067275Sstephh len = strlen(devpath) + strlen("/devices") + 1;
4076135Sstephh path = topo_mod_alloc(mod, len);
4086135Sstephh (void) snprintf(path, len, "/devices%s", devpath);
4096135Sstephh if (devid == NULL) {
4106135Sstephh if (stat(path, &sb) != -1)
4116135Sstephh present = 1;
4126135Sstephh else if ((dnode = di_init("/", DINFOCACHE)) == DI_NODE_NIL)
4136135Sstephh present = 0;
4146135Sstephh else {
4156135Sstephh if (di_lookup_node(dnode, devpath) == DI_NODE_NIL)
4166135Sstephh present = 0;
4176135Sstephh else
4186135Sstephh present = 1;
4196135Sstephh di_fini(dnode);
4206135Sstephh }
4214198Seschrock } else {
4226135Sstephh if (stat(path, &sb) == -1)
4236135Sstephh present = 0;
4246135Sstephh else if ((dnode = di_init(devpath, DINFOCPYONE)) == DI_NODE_NIL)
4256135Sstephh present = 0;
4266135Sstephh else {
4276135Sstephh if ((id = di_devid(dnode)) == NULL ||
4286135Sstephh devid_str_decode(devid, &matchid, NULL) != 0)
4296135Sstephh present = 0;
4306135Sstephh else {
4316135Sstephh if (devid_compare(id, matchid) != 0)
4326135Sstephh present = 0;
4336135Sstephh else
4346135Sstephh present = 1;
4356135Sstephh devid_free(matchid);
4366135Sstephh }
4376135Sstephh di_fini(dnode);
4386135Sstephh }
4394198Seschrock }
4406135Sstephh topo_mod_free(mod, path, len);
4414198Seschrock
4424198Seschrock if (topo_mod_nvalloc(mod, out, NV_UNIQUE_NAME) != 0)
4434198Seschrock return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
4444198Seschrock if (nvlist_add_uint32(*out, TOPO_METH_PRESENT_RET, present) != 0) {
4454198Seschrock nvlist_free(*out);
4464198Seschrock return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
4474198Seschrock }
4484198Seschrock
4494198Seschrock return (0);
4504198Seschrock }
4514198Seschrock
4524198Seschrock /*ARGSUSED*/
4534198Seschrock static int
dev_fmri_replaced(topo_mod_t * mod,tnode_t * node,topo_version_t version,nvlist_t * in,nvlist_t ** out)4547275Sstephh dev_fmri_replaced(topo_mod_t *mod, tnode_t *node, topo_version_t version,
4557275Sstephh nvlist_t *in, nvlist_t **out)
4567275Sstephh {
4577275Sstephh uint8_t fmversion;
4587275Sstephh char *devpath = NULL;
4597275Sstephh uint32_t rval;
4607275Sstephh char *devid = NULL, *path;
4617275Sstephh ddi_devid_t id;
4627275Sstephh ddi_devid_t matchid;
4637275Sstephh di_node_t dnode;
4647275Sstephh struct stat sb;
4657275Sstephh int len;
4667275Sstephh
4677275Sstephh if (version > TOPO_METH_REPLACED_VERSION)
4687275Sstephh return (topo_mod_seterrno(mod, EMOD_VER_NEW));
4697275Sstephh
4707275Sstephh if (nvlist_lookup_uint8(in, FM_VERSION, &fmversion) != 0 ||
4717275Sstephh fmversion > FM_DEV_SCHEME_VERSION ||
4727275Sstephh nvlist_lookup_string(in, FM_FMRI_DEV_PATH, &devpath) != 0)
4737275Sstephh return (topo_mod_seterrno(mod, EMOD_FMRI_MALFORM));
4747275Sstephh
4757275Sstephh (void) nvlist_lookup_string(in, FM_FMRI_DEV_ID, &devid);
4767275Sstephh
4777275Sstephh if (devpath == NULL || strlen(devpath) == 0)
4787275Sstephh return (topo_mod_seterrno(mod, EMOD_FMRI_MALFORM));
4797275Sstephh
4807275Sstephh /*
4817275Sstephh * stat() the device node in devfs. This will tell us if the device is
4827275Sstephh * present or not. Don't stat the minor, just the whole device.
4837275Sstephh * If the device is present and there is a devid, it must also match.
4847275Sstephh * so di_init that one node. No need for DINFOFORCE.
4857275Sstephh */
4867275Sstephh len = strlen(devpath) + strlen("/devices") + 1;
4877275Sstephh path = topo_mod_alloc(mod, len);
4887275Sstephh (void) snprintf(path, len, "/devices%s", devpath);
4897275Sstephh if (devid == NULL) {
4907275Sstephh if (stat(path, &sb) != -1)
4917275Sstephh rval = FMD_OBJ_STATE_UNKNOWN;
4927275Sstephh else if ((dnode = di_init("/", DINFOCACHE)) == DI_NODE_NIL)
49312618SStephen.Hanson@Sun.COM rval = FMD_OBJ_STATE_UNKNOWN;
4947275Sstephh else {
4957275Sstephh if (di_lookup_node(dnode, devpath) == DI_NODE_NIL)
49612618SStephen.Hanson@Sun.COM rval = FMD_OBJ_STATE_UNKNOWN;
4977275Sstephh else
4987275Sstephh rval = FMD_OBJ_STATE_UNKNOWN;
4997275Sstephh di_fini(dnode);
5007275Sstephh }
5017275Sstephh } else {
5027275Sstephh if (stat(path, &sb) == -1)
50312618SStephen.Hanson@Sun.COM rval = FMD_OBJ_STATE_UNKNOWN;
5047275Sstephh else if ((dnode = di_init(devpath, DINFOCPYONE)) == DI_NODE_NIL)
50512618SStephen.Hanson@Sun.COM rval = FMD_OBJ_STATE_UNKNOWN;
5067275Sstephh else {
5077275Sstephh if ((id = di_devid(dnode)) == NULL ||
5087275Sstephh devid_str_decode(devid, &matchid, NULL) != 0)
5097275Sstephh rval = FMD_OBJ_STATE_UNKNOWN;
5107275Sstephh else {
5117275Sstephh if (devid_compare(id, matchid) != 0)
5127275Sstephh rval = FMD_OBJ_STATE_REPLACED;
5137275Sstephh else
5147275Sstephh rval = FMD_OBJ_STATE_STILL_PRESENT;
5157275Sstephh devid_free(matchid);
5167275Sstephh }
5177275Sstephh di_fini(dnode);
5187275Sstephh }
5197275Sstephh }
5207275Sstephh topo_mod_free(mod, path, len);
5217275Sstephh
5227275Sstephh if (topo_mod_nvalloc(mod, out, NV_UNIQUE_NAME) != 0)
5237275Sstephh return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
5247275Sstephh if (nvlist_add_uint32(*out, TOPO_METH_REPLACED_RET, rval) != 0) {
5257275Sstephh nvlist_free(*out);
5267275Sstephh return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
5277275Sstephh }
5287275Sstephh
5297275Sstephh return (0);
5307275Sstephh }
5317275Sstephh
5327275Sstephh /*ARGSUSED*/
5337275Sstephh static int
dev_fmri_unusable(topo_mod_t * mod,tnode_t * node,topo_version_t version,nvlist_t * in,nvlist_t ** out)5344198Seschrock dev_fmri_unusable(topo_mod_t *mod, tnode_t *node, topo_version_t version,
5354198Seschrock nvlist_t *in, nvlist_t **out)
5364198Seschrock {
5374198Seschrock di_node_t dnode;
5384198Seschrock uint8_t fmversion;
5394198Seschrock char *devpath = NULL;
5404198Seschrock uint32_t unusable;
5414198Seschrock uint_t state;
5424198Seschrock
5437275Sstephh if (version > TOPO_METH_UNUSABLE_VERSION)
5444198Seschrock return (topo_mod_seterrno(mod, EMOD_VER_NEW));
5454198Seschrock
5464198Seschrock if (nvlist_lookup_uint8(in, FM_VERSION, &fmversion) != 0 ||
5474198Seschrock fmversion > FM_DEV_SCHEME_VERSION ||
5484198Seschrock nvlist_lookup_string(in, FM_FMRI_DEV_PATH, &devpath) != 0)
5494198Seschrock return (topo_mod_seterrno(mod, EMOD_FMRI_MALFORM));
5504198Seschrock
5514198Seschrock if (devpath == NULL)
5524198Seschrock return (topo_mod_seterrno(mod, EMOD_FMRI_MALFORM));
5534198Seschrock
5544198Seschrock if ((dnode = di_init(devpath, DINFOCPYONE)) == DI_NODE_NIL) {
5554198Seschrock if (errno != ENXIO)
5564198Seschrock return (topo_mod_seterrno(mod, EMOD_UKNOWN_ENUM));
5574198Seschrock unusable = 1;
5584198Seschrock } else {
5594845Svikram uint_t retired = di_retired(dnode);
5604198Seschrock state = di_state(dnode);
5614845Svikram if (retired || (state & (DI_DEVICE_OFFLINE | DI_DEVICE_DOWN |
5624845Svikram DI_BUS_QUIESCED | DI_BUS_DOWN)))
5634198Seschrock unusable = 1;
5644198Seschrock else
5654198Seschrock unusable = 0;
5664198Seschrock di_fini(dnode);
5674198Seschrock }
5684198Seschrock
5694198Seschrock if (topo_mod_nvalloc(mod, out, NV_UNIQUE_NAME) != 0)
5704198Seschrock return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
5714444Svikram if (nvlist_add_uint32(*out, TOPO_METH_UNUSABLE_RET, unusable) != 0) {
5724198Seschrock nvlist_free(*out);
5734198Seschrock return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
5744198Seschrock }
5754198Seschrock
5764198Seschrock return (0);
5774198Seschrock }
5784198Seschrock
5797275Sstephh /*ARGSUSED*/
5807275Sstephh static int
dev_fmri_service_state(topo_mod_t * mod,tnode_t * node,topo_version_t version,nvlist_t * in,nvlist_t ** out)5817275Sstephh dev_fmri_service_state(topo_mod_t *mod, tnode_t *node, topo_version_t version,
5827275Sstephh nvlist_t *in, nvlist_t **out)
5837275Sstephh {
5847275Sstephh di_node_t dnode;
5857275Sstephh uint8_t fmversion;
5867275Sstephh char *devpath = NULL;
5877275Sstephh uint32_t service_state;
5887275Sstephh uint_t state;
5897275Sstephh
5907275Sstephh if (version > TOPO_METH_SERVICE_STATE_VERSION)
5917275Sstephh return (topo_mod_seterrno(mod, EMOD_VER_NEW));
5927275Sstephh
5937275Sstephh if (nvlist_lookup_uint8(in, FM_VERSION, &fmversion) != 0 ||
5947275Sstephh fmversion > FM_DEV_SCHEME_VERSION ||
5957275Sstephh nvlist_lookup_string(in, FM_FMRI_DEV_PATH, &devpath) != 0)
5967275Sstephh return (topo_mod_seterrno(mod, EMOD_FMRI_MALFORM));
5977275Sstephh
5987275Sstephh if (devpath == NULL)
5997275Sstephh return (topo_mod_seterrno(mod, EMOD_FMRI_MALFORM));
6007275Sstephh
6017275Sstephh if ((dnode = di_init(devpath, DINFOCPYONE)) == DI_NODE_NIL) {
6027275Sstephh if (errno != ENXIO)
6037275Sstephh return (topo_mod_seterrno(mod, EMOD_UKNOWN_ENUM));
6047275Sstephh service_state = FMD_SERVICE_STATE_UNUSABLE;
6057275Sstephh } else {
6067275Sstephh uint_t retired = di_retired(dnode);
6077275Sstephh state = di_state(dnode);
6087275Sstephh if (retired || (state & (DI_DEVICE_OFFLINE | DI_DEVICE_DOWN |
6097275Sstephh DI_BUS_QUIESCED | DI_BUS_DOWN)))
6107275Sstephh service_state = FMD_SERVICE_STATE_UNUSABLE;
6117275Sstephh else if (state & DI_DEVICE_DEGRADED)
6127275Sstephh service_state = FMD_SERVICE_STATE_DEGRADED;
6137275Sstephh else
6147275Sstephh service_state = FMD_SERVICE_STATE_OK;
6157275Sstephh di_fini(dnode);
6167275Sstephh }
6177275Sstephh
6187275Sstephh if (topo_mod_nvalloc(mod, out, NV_UNIQUE_NAME) != 0)
6197275Sstephh return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
6207275Sstephh if (nvlist_add_uint32(*out, TOPO_METH_SERVICE_STATE_RET,
6217275Sstephh service_state) != 0) {
6227275Sstephh nvlist_free(*out);
6237275Sstephh return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
6247275Sstephh }
6257275Sstephh
6267275Sstephh return (0);
6277275Sstephh }
6287275Sstephh
6291541Stimh static nvlist_t *
dev_fmri_create(topo_mod_t * mp,const char * id,const char * path)6301541Stimh dev_fmri_create(topo_mod_t *mp, const char *id, const char *path)
6311541Stimh {
6321541Stimh nvlist_t *out = NULL;
6331541Stimh int e;
6341541Stimh
6351541Stimh if (topo_mod_nvalloc(mp, &out, NV_UNIQUE_NAME) != 0) {
6361541Stimh (void) topo_mod_seterrno(mp, EMOD_FMRI_NVL);
6371541Stimh return (NULL);
6381541Stimh }
6391541Stimh e = nvlist_add_string(out, FM_FMRI_SCHEME, FM_FMRI_SCHEME_DEV);
6401541Stimh e |= nvlist_add_uint8(out, FM_VERSION, FM_DEV_SCHEME_VERSION);
6411541Stimh e |= nvlist_add_string(out, FM_FMRI_DEV_PATH, path);
6421541Stimh
6431541Stimh if (id != NULL)
6441541Stimh e |= nvlist_add_string(out, FM_FMRI_DEV_ID, id);
6451541Stimh
6461541Stimh if (e == 0)
6471541Stimh return (out);
6481541Stimh
6491541Stimh topo_mod_dprintf(mp, "construction of dev nvl failed");
6501541Stimh (void) topo_mod_seterrno(mp, EMOD_FMRI_NVL);
6511541Stimh nvlist_free(out);
6521541Stimh return (NULL);
6531541Stimh }
6541541Stimh
6551541Stimh /*ARGSUSED*/
6561541Stimh static int
dev_fmri_create_meth(topo_mod_t * mp,tnode_t * node,topo_version_t version,nvlist_t * in,nvlist_t ** out)6571541Stimh dev_fmri_create_meth(topo_mod_t *mp, tnode_t *node, topo_version_t version,
6581541Stimh nvlist_t *in, nvlist_t **out)
6591541Stimh {
6601541Stimh nvlist_t *args = NULL;
6611541Stimh char *path, *id = NULL;
6621541Stimh
6631541Stimh if (version > TOPO_METH_FMRI_VERSION)
6641541Stimh return (topo_mod_seterrno(mp, EMOD_VER_NEW));
6651541Stimh
6661541Stimh if (nvlist_lookup_nvlist(in, TOPO_METH_FMRI_ARG_NVL, &args) != 0 ||
6671541Stimh nvlist_lookup_string(args, FM_FMRI_DEV_PATH, &path) != 0) {
6681541Stimh topo_mod_dprintf(mp, "no path string in method argument\n");
6691541Stimh return (topo_mod_seterrno(mp, EMOD_METHOD_INVAL));
6701541Stimh }
6711541Stimh
6721541Stimh (void) nvlist_lookup_string(args, FM_FMRI_DEV_ID, &id);
6731541Stimh
6741541Stimh if ((*out = dev_fmri_create(mp, id, path)) == NULL)
6751541Stimh return (-1);
6761541Stimh return (0);
6771541Stimh }
678