174a31ce6Stimh /*
274a31ce6Stimh * CDDL HEADER START
374a31ce6Stimh *
474a31ce6Stimh * The contents of this file are subject to the terms of the
574a31ce6Stimh * Common Development and Distribution License (the "License").
674a31ce6Stimh * You may not use this file except in compliance with the License.
774a31ce6Stimh *
874a31ce6Stimh * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
974a31ce6Stimh * or http://www.opensolaris.org/os/licensing.
1074a31ce6Stimh * See the License for the specific language governing permissions
1174a31ce6Stimh * and limitations under the License.
1274a31ce6Stimh *
1374a31ce6Stimh * When distributing Covered Code, include this CDDL HEADER in each
1474a31ce6Stimh * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
1574a31ce6Stimh * If applicable, add the following below this CDDL HEADER, with the
1674a31ce6Stimh * fields enclosed by brackets "[]" replaced with your own identifying
1774a31ce6Stimh * information: Portions Copyright [yyyy] [name of copyright owner]
1874a31ce6Stimh *
1974a31ce6Stimh * CDDL HEADER END
2074a31ce6Stimh */
2174a31ce6Stimh
2274a31ce6Stimh /*
23392e836bSGavin Maltby * Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
2474a31ce6Stimh */
2574a31ce6Stimh
2674a31ce6Stimh #include <limits.h>
2774a31ce6Stimh #include <strings.h>
2874a31ce6Stimh #include <string.h>
2974a31ce6Stimh #include <unistd.h>
3074a31ce6Stimh #include <stdio.h>
3174a31ce6Stimh #include <alloca.h>
3224db4641Seschrock #include <devid.h>
33faf8f993Sstephh #include <sys/stat.h>
3474a31ce6Stimh #include <libnvpair.h>
3574a31ce6Stimh #include <fm/topo_mod.h>
3625c6ff4bSstephh #include <fm/fmd_fmri.h>
3774a31ce6Stimh #include <sys/fm/protocol.h>
3874a31ce6Stimh
390eb822a1Scindi #include <topo_method.h>
4074a31ce6Stimh #include <topo_subr.h>
410eb822a1Scindi #include <dev.h>
4274a31ce6Stimh
4374a31ce6Stimh static int dev_enum(topo_mod_t *, tnode_t *, const char *, topo_instance_t,
440eb822a1Scindi topo_instance_t, void *, void *);
4574a31ce6Stimh static void dev_release(topo_mod_t *, tnode_t *);
4674a31ce6Stimh static int dev_fmri_nvl2str(topo_mod_t *, tnode_t *, topo_version_t,
4774a31ce6Stimh nvlist_t *, nvlist_t **);
4874a31ce6Stimh static int dev_fmri_str2nvl(topo_mod_t *, tnode_t *, topo_version_t,
4974a31ce6Stimh nvlist_t *, nvlist_t **);
5074a31ce6Stimh static int dev_fmri_create_meth(topo_mod_t *, tnode_t *, topo_version_t,
5174a31ce6Stimh nvlist_t *, nvlist_t **);
5224db4641Seschrock static int dev_fmri_present(topo_mod_t *, tnode_t *, topo_version_t,
5324db4641Seschrock nvlist_t *, nvlist_t **);
5425c6ff4bSstephh static int dev_fmri_replaced(topo_mod_t *, tnode_t *, topo_version_t,
5525c6ff4bSstephh nvlist_t *, nvlist_t **);
5624db4641Seschrock static int dev_fmri_unusable(topo_mod_t *, tnode_t *, topo_version_t,
5724db4641Seschrock nvlist_t *, nvlist_t **);
5825c6ff4bSstephh static int dev_fmri_service_state(topo_mod_t *, tnode_t *, topo_version_t,
5925c6ff4bSstephh nvlist_t *, nvlist_t **);
6074a31ce6Stimh
6174a31ce6Stimh static const topo_method_t dev_methods[] = {
6274a31ce6Stimh { TOPO_METH_NVL2STR, TOPO_METH_NVL2STR_DESC, TOPO_METH_NVL2STR_VERSION,
6374a31ce6Stimh TOPO_STABILITY_INTERNAL, dev_fmri_nvl2str },
6474a31ce6Stimh { TOPO_METH_STR2NVL, TOPO_METH_STR2NVL_DESC, TOPO_METH_STR2NVL_VERSION,
6574a31ce6Stimh TOPO_STABILITY_INTERNAL, dev_fmri_str2nvl },
6674a31ce6Stimh { TOPO_METH_FMRI, TOPO_METH_FMRI_DESC, TOPO_METH_FMRI_VERSION,
6774a31ce6Stimh TOPO_STABILITY_INTERNAL, dev_fmri_create_meth },
6824db4641Seschrock { TOPO_METH_PRESENT, TOPO_METH_PRESENT_DESC, TOPO_METH_PRESENT_VERSION,
6924db4641Seschrock TOPO_STABILITY_INTERNAL, dev_fmri_present },
7025c6ff4bSstephh { TOPO_METH_REPLACED, TOPO_METH_REPLACED_DESC,
7125c6ff4bSstephh TOPO_METH_REPLACED_VERSION, TOPO_STABILITY_INTERNAL,
7225c6ff4bSstephh dev_fmri_replaced },
7324db4641Seschrock { TOPO_METH_UNUSABLE, TOPO_METH_UNUSABLE_DESC,
7424db4641Seschrock TOPO_METH_UNUSABLE_VERSION, TOPO_STABILITY_INTERNAL,
7524db4641Seschrock dev_fmri_unusable },
7625c6ff4bSstephh { TOPO_METH_SERVICE_STATE, TOPO_METH_SERVICE_STATE_DESC,
7725c6ff4bSstephh TOPO_METH_SERVICE_STATE_VERSION, TOPO_STABILITY_INTERNAL,
7825c6ff4bSstephh dev_fmri_service_state },
7974a31ce6Stimh { NULL }
8074a31ce6Stimh };
8174a31ce6Stimh
820eb822a1Scindi static const topo_modops_t dev_ops =
830eb822a1Scindi { dev_enum, dev_release };
8474a31ce6Stimh static const topo_modinfo_t dev_info =
850eb822a1Scindi { "dev", FM_FMRI_SCHEME_DEV, DEV_VERSION, &dev_ops };
8674a31ce6Stimh
870eb822a1Scindi int
dev_init(topo_mod_t * mod,topo_version_t version)880eb822a1Scindi dev_init(topo_mod_t *mod, topo_version_t version)
8974a31ce6Stimh {
900eb822a1Scindi if (getenv("TOPOHCDEBUG"))
910eb822a1Scindi topo_mod_setdebug(mod);
9274a31ce6Stimh topo_mod_dprintf(mod, "initializing dev builtin\n");
9374a31ce6Stimh
940eb822a1Scindi if (version != DEV_VERSION)
950eb822a1Scindi return (topo_mod_seterrno(mod, EMOD_VER_NEW));
960eb822a1Scindi
970eb822a1Scindi if (topo_mod_register(mod, &dev_info, TOPO_VERSION) != 0) {
9874a31ce6Stimh topo_mod_dprintf(mod, "failed to register dev_info: "
9974a31ce6Stimh "%s\n", topo_mod_errmsg(mod));
1000eb822a1Scindi return (-1);
10174a31ce6Stimh }
1020eb822a1Scindi
1030eb822a1Scindi return (0);
10474a31ce6Stimh }
10574a31ce6Stimh
10674a31ce6Stimh void
dev_fini(topo_mod_t * mod)10774a31ce6Stimh dev_fini(topo_mod_t *mod)
10874a31ce6Stimh {
10974a31ce6Stimh topo_mod_unregister(mod);
11074a31ce6Stimh }
11174a31ce6Stimh
11274a31ce6Stimh /*ARGSUSED*/
11374a31ce6Stimh 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)11474a31ce6Stimh dev_enum(topo_mod_t *mod, tnode_t *pnode, const char *name,
1150eb822a1Scindi topo_instance_t min, topo_instance_t max, void *notused1, void *notused2)
11674a31ce6Stimh {
117*f6e214c7SGavin Maltby /*
118*f6e214c7SGavin Maltby * Methods are registered, but there is no enumeration. Should
119*f6e214c7SGavin Maltby * enumeration be added be sure to cater for global vs non-global
120*f6e214c7SGavin Maltby * zones.
121*f6e214c7SGavin Maltby */
12274a31ce6Stimh (void) topo_method_register(mod, pnode, dev_methods);
12374a31ce6Stimh return (0);
12474a31ce6Stimh }
12574a31ce6Stimh
12674a31ce6Stimh static void
dev_release(topo_mod_t * mod,tnode_t * node)12774a31ce6Stimh dev_release(topo_mod_t *mod, tnode_t *node)
12874a31ce6Stimh {
12974a31ce6Stimh topo_method_unregister_all(mod, node);
13074a31ce6Stimh }
13174a31ce6Stimh
13274a31ce6Stimh static ssize_t
fmri_nvl2str(nvlist_t * nvl,char * buf,size_t buflen)13374a31ce6Stimh fmri_nvl2str(nvlist_t *nvl, char *buf, size_t buflen)
13474a31ce6Stimh {
135392e836bSGavin Maltby char *devid = NULL, *tpl0id = NULL;
13674a31ce6Stimh char *devpath = NULL;
137392e836bSGavin Maltby ssize_t size = 0;
138392e836bSGavin Maltby uint8_t version;
13974a31ce6Stimh int err;
14074a31ce6Stimh
14174a31ce6Stimh if (nvlist_lookup_uint8(nvl, FM_VERSION, &version) != 0 ||
14274a31ce6Stimh version > FM_DEV_SCHEME_VERSION)
14374a31ce6Stimh return (-1);
14474a31ce6Stimh
145392e836bSGavin Maltby /* Get devid, if present */
146392e836bSGavin Maltby err = nvlist_lookup_string(nvl, FM_FMRI_DEV_ID, &devid);
14774a31ce6Stimh if (err != 0 && err != ENOENT)
14874a31ce6Stimh return (-1);
14974a31ce6Stimh
150392e836bSGavin Maltby /* Get target-port-l0id, if present */
151392e836bSGavin Maltby err = nvlist_lookup_string(nvl, FM_FMRI_DEV_TGTPTLUN0, &tpl0id);
15274a31ce6Stimh if (err != 0 && err != ENOENT)
15374a31ce6Stimh return (-1);
15474a31ce6Stimh
15574a31ce6Stimh /* There must be a device path present */
15674a31ce6Stimh err = nvlist_lookup_string(nvl, FM_FMRI_DEV_PATH, &devpath);
15774a31ce6Stimh if (err != 0 || devpath == NULL)
15874a31ce6Stimh return (-1);
15974a31ce6Stimh
160392e836bSGavin Maltby /*
161392e836bSGavin Maltby * dev:///
162392e836bSGavin Maltby *
163392e836bSGavin Maltby * The dev scheme does not render fmri authority information
164392e836bSGavin Maltby * in the string form of an fmri. It is meaningless to
165392e836bSGavin Maltby * transmit a dev scheme fmri outside of the immediate fault
166392e836bSGavin Maltby * manager.
167392e836bSGavin Maltby */
16874a31ce6Stimh topo_fmristr_build(&size,
169392e836bSGavin Maltby buf, buflen, FM_FMRI_SCHEME_DEV, NULL, ":///");
17074a31ce6Stimh
17174a31ce6Stimh /* device-id part, topo_fmristr_build does nothing if devid is NULL */
17274a31ce6Stimh topo_fmristr_build(&size,
173392e836bSGavin Maltby buf, buflen, devid, ":" FM_FMRI_DEV_ID "=", NULL);
17474a31ce6Stimh
175392e836bSGavin Maltby /* target-port-l0id part */
176392e836bSGavin Maltby topo_fmristr_build(&size,
177392e836bSGavin Maltby buf, buflen, tpl0id, ":" FM_FMRI_DEV_TGTPTLUN0 "=", NULL);
178392e836bSGavin Maltby
179392e836bSGavin Maltby /*
180392e836bSGavin Maltby * device-path part; the devpath should always start with a /
181392e836bSGavin Maltby * so you'd think we don't need to add a further / prefix here;
182392e836bSGavin Maltby * however past implementation has always added the / if
183392e836bSGavin Maltby * there is a devid component so we continue to do that
184392e836bSGavin Maltby * so strings match exactly as before. So we can have:
185392e836bSGavin Maltby *
186392e836bSGavin Maltby * dev:////pci@0,0/...
187392e836bSGavin Maltby * dev:///<devid-and-tpl0>//pci@0,0/...
188392e836bSGavin Maltby *
189392e836bSGavin Maltby * where <devid-and-tpl0> =
190392e836bSGavin Maltby * [:devid=<devid>][:target-port-l0id=<tpl0>]
191392e836bSGavin Maltby */
192392e836bSGavin Maltby topo_fmristr_build(&size, buf, buflen, devpath,
193392e836bSGavin Maltby devid || tpl0id ? "/" : NULL, NULL);
19474a31ce6Stimh
19574a31ce6Stimh return (size);
19674a31ce6Stimh }
19774a31ce6Stimh
19874a31ce6Stimh /*ARGSUSED*/
19974a31ce6Stimh static int
dev_fmri_nvl2str(topo_mod_t * mod,tnode_t * node,topo_version_t version,nvlist_t * nvl,nvlist_t ** out)20074a31ce6Stimh dev_fmri_nvl2str(topo_mod_t *mod, tnode_t *node, topo_version_t version,
20174a31ce6Stimh nvlist_t *nvl, nvlist_t **out)
20274a31ce6Stimh {
20374a31ce6Stimh ssize_t len;
20474a31ce6Stimh char *name = NULL;
20574a31ce6Stimh nvlist_t *fmristr;
20674a31ce6Stimh
20774a31ce6Stimh if (version > TOPO_METH_NVL2STR_VERSION)
20874a31ce6Stimh return (topo_mod_seterrno(mod, EMOD_VER_NEW));
20974a31ce6Stimh
21074a31ce6Stimh if ((len = fmri_nvl2str(nvl, NULL, 0)) == 0 ||
21174a31ce6Stimh (name = topo_mod_alloc(mod, len + 1)) == NULL ||
21274a31ce6Stimh fmri_nvl2str(nvl, name, len + 1) == 0) {
21374a31ce6Stimh if (name != NULL)
21474a31ce6Stimh topo_mod_free(mod, name, len + 1);
21574a31ce6Stimh return (topo_mod_seterrno(mod, EMOD_FMRI_NVL));
21674a31ce6Stimh }
21774a31ce6Stimh
21874a31ce6Stimh if (topo_mod_nvalloc(mod, &fmristr, NV_UNIQUE_NAME) != 0)
21974a31ce6Stimh return (topo_mod_seterrno(mod, EMOD_FMRI_NVL));
22074a31ce6Stimh if (nvlist_add_string(fmristr, "fmri-string", name) != 0) {
22174a31ce6Stimh topo_mod_free(mod, name, len + 1);
22274a31ce6Stimh nvlist_free(fmristr);
22374a31ce6Stimh return (topo_mod_seterrno(mod, EMOD_FMRI_NVL));
22474a31ce6Stimh }
22574a31ce6Stimh topo_mod_free(mod, name, len + 1);
22674a31ce6Stimh *out = fmristr;
22774a31ce6Stimh
22874a31ce6Stimh return (0);
22974a31ce6Stimh }
23074a31ce6Stimh
23174a31ce6Stimh /*ARGSUSED*/
23274a31ce6Stimh static int
dev_fmri_str2nvl(topo_mod_t * mod,tnode_t * node,topo_version_t version,nvlist_t * in,nvlist_t ** out)23374a31ce6Stimh dev_fmri_str2nvl(topo_mod_t *mod, tnode_t *node, topo_version_t version,
23474a31ce6Stimh nvlist_t *in, nvlist_t **out)
23574a31ce6Stimh {
236392e836bSGavin Maltby char *cur, *devid = NULL, *tpl0id = NULL;
237392e836bSGavin Maltby char *str, *strcp;
23874a31ce6Stimh nvlist_t *fmri;
23974a31ce6Stimh char *devpath;
240392e836bSGavin Maltby size_t len;
24174a31ce6Stimh int err;
24274a31ce6Stimh
24374a31ce6Stimh if (version > TOPO_METH_STR2NVL_VERSION)
24474a31ce6Stimh return (topo_mod_seterrno(mod, EMOD_VER_NEW));
24574a31ce6Stimh
24674a31ce6Stimh if (nvlist_lookup_string(in, "fmri-string", &str) != 0)
24774a31ce6Stimh return (topo_mod_seterrno(mod, EMOD_FMRI_NVL));
24874a31ce6Stimh
249392e836bSGavin Maltby len = strlen(str);
250392e836bSGavin Maltby
251392e836bSGavin Maltby /*
252392e836bSGavin Maltby * We're expecting a string version of a dev scheme FMRI, and
253392e836bSGavin Maltby * no fmri authority information.
254392e836bSGavin Maltby *
255392e836bSGavin Maltby * The shortest legal string would be "dev:////" (len 8) for a string
256392e836bSGavin Maltby * with no FMRI auth info, no devid or target-port-l0id and
257392e836bSGavin Maltby * an empty devpath string.
258392e836bSGavin Maltby */
259392e836bSGavin Maltby if (len < 8 || strncmp(str, "dev:///", 7) != 0)
26074a31ce6Stimh return (topo_mod_seterrno(mod, EMOD_FMRI_MALFORM));
26174a31ce6Stimh
262392e836bSGavin Maltby strcp = alloca(len + 1);
263392e836bSGavin Maltby (void) memcpy(strcp, str, len);
264392e836bSGavin Maltby strcp[len] = '\0';
265392e836bSGavin Maltby cur = strcp + 7; /* already parsed "dev:///" */
266392e836bSGavin Maltby
267392e836bSGavin Maltby /*
268392e836bSGavin Maltby * If the first character after the "/" that terminates the (empty)
269392e836bSGavin Maltby * fmri authority is a colon then we have devid and/or target-port-l0id
270392e836bSGavin Maltby * info. They could be in either order.
271392e836bSGavin Maltby *
272392e836bSGavin Maltby * If not a colon then it must be the / that begins the devpath.
273392e836bSGavin Maltby */
274392e836bSGavin Maltby if (*cur == ':') {
275392e836bSGavin Maltby char *eos, *part[2];
276392e836bSGavin Maltby int i;
277392e836bSGavin Maltby /*
278392e836bSGavin Maltby * Look ahead to the "/" that starts the devpath. If not
279392e836bSGavin Maltby * found or if straight after the : then we're busted.
280392e836bSGavin Maltby */
281392e836bSGavin Maltby eos = devpath = strchr(cur, '/');
282392e836bSGavin Maltby if (devpath == NULL || devpath == cur + 1)
283392e836bSGavin Maltby return (topo_mod_seterrno(mod, EMOD_FMRI_MALFORM));
284392e836bSGavin Maltby
285392e836bSGavin Maltby part[0] = ++cur;
286392e836bSGavin Maltby
287392e836bSGavin Maltby /*
288392e836bSGavin Maltby * Replace the initial "/" of the devpath with a NUL
289392e836bSGavin Maltby * to terminate the string before it. We'll undo this
290392e836bSGavin Maltby * before rendering devpath.
291392e836bSGavin Maltby */
292392e836bSGavin Maltby *eos = '\0';
293392e836bSGavin Maltby
294392e836bSGavin Maltby /*
295392e836bSGavin Maltby * We should now have a NUL-terminated string matching
296392e836bSGavin Maltby * foo=<pat1>[:bar=<pat2>] (we stepped over the initial :)
297392e836bSGavin Maltby * Look for a second colon; if found there must be space
298392e836bSGavin Maltby * after it for the additional component, but no more colons.
299392e836bSGavin Maltby */
300392e836bSGavin Maltby if ((part[1] = strchr(cur, ':')) != NULL) {
301392e836bSGavin Maltby if (part[1] + 1 == eos ||
302392e836bSGavin Maltby strchr(part[1] + 1, ':') != NULL)
303392e836bSGavin Maltby return (topo_mod_seterrno(mod,
304392e836bSGavin Maltby EMOD_FMRI_MALFORM));
305392e836bSGavin Maltby *part[1] = '\0'; /* terminate part[0] */
306392e836bSGavin Maltby part[1]++;
307392e836bSGavin Maltby }
30874a31ce6Stimh
309392e836bSGavin Maltby for (i = 0; i < 2; i++) {
310392e836bSGavin Maltby char *eq;
311392e836bSGavin Maltby
312392e836bSGavin Maltby if (!part[i])
313392e836bSGavin Maltby continue;
314392e836bSGavin Maltby
315392e836bSGavin Maltby if ((eq = strchr(part[i], '=')) == NULL ||
316392e836bSGavin Maltby *(eq + 1) == '\0')
317392e836bSGavin Maltby return (topo_mod_seterrno(mod,
318392e836bSGavin Maltby EMOD_FMRI_MALFORM));
319392e836bSGavin Maltby
320392e836bSGavin Maltby *eq = '\0';
321392e836bSGavin Maltby if (strcmp(part[i], FM_FMRI_DEV_ID) == 0)
322392e836bSGavin Maltby devid = eq + 1;
323392e836bSGavin Maltby else if (strcmp(part[i], FM_FMRI_DEV_TGTPTLUN0) == 0)
324392e836bSGavin Maltby tpl0id = eq + 1;
325392e836bSGavin Maltby else
326392e836bSGavin Maltby return (topo_mod_seterrno(mod,
327392e836bSGavin Maltby EMOD_FMRI_MALFORM));
328392e836bSGavin Maltby }
329392e836bSGavin Maltby
330392e836bSGavin Maltby if (devid == NULL && tpl0id == NULL)
33174a31ce6Stimh return (topo_mod_seterrno(mod, EMOD_FMRI_MALFORM));
33274a31ce6Stimh
333392e836bSGavin Maltby cur = devpath; /* initial slash is NULled */
334392e836bSGavin Maltby } else if (*cur != '/') {
335392e836bSGavin Maltby /* the device-path should start with a slash */
33674a31ce6Stimh return (topo_mod_seterrno(mod, EMOD_FMRI_MALFORM));
337392e836bSGavin Maltby } else {
338392e836bSGavin Maltby devpath = cur;
339392e836bSGavin Maltby }
34074a31ce6Stimh
34174a31ce6Stimh if (topo_mod_nvalloc(mod, &fmri, NV_UNIQUE_NAME) != 0)
34274a31ce6Stimh return (topo_mod_seterrno(mod, EMOD_FMRI_NVL));
34374a31ce6Stimh
34474a31ce6Stimh err = nvlist_add_uint8(fmri, FM_VERSION, FM_DEV_SCHEME_VERSION);
34574a31ce6Stimh err |= nvlist_add_string(fmri, FM_FMRI_SCHEME, FM_FMRI_SCHEME_DEV);
346392e836bSGavin Maltby
34774a31ce6Stimh if (devid != NULL)
34874a31ce6Stimh err |= nvlist_add_string(fmri, FM_FMRI_DEV_ID, devid);
34974a31ce6Stimh
350392e836bSGavin Maltby if (tpl0id != NULL)
351392e836bSGavin Maltby err |= nvlist_add_string(fmri, FM_FMRI_DEV_TGTPTLUN0, tpl0id);
352392e836bSGavin Maltby
353392e836bSGavin Maltby if (devid != NULL || tpl0id != NULL)
354392e836bSGavin Maltby *devpath = '/'; /* we NULed this earlier; put it back */
355392e836bSGavin Maltby
356392e836bSGavin Maltby /* step over repeated initial / in the devpath */
357392e836bSGavin Maltby while (*(devpath + 1) == '/')
358392e836bSGavin Maltby devpath++;
359392e836bSGavin Maltby
360392e836bSGavin Maltby err |= nvlist_add_string(fmri, FM_FMRI_DEV_PATH, devpath);
361392e836bSGavin Maltby
36274a31ce6Stimh if (err != 0) {
36374a31ce6Stimh nvlist_free(fmri);
36474a31ce6Stimh return (topo_mod_seterrno(mod, EMOD_FMRI_NVL));
36574a31ce6Stimh }
366392e836bSGavin Maltby
36774a31ce6Stimh *out = fmri;
36874a31ce6Stimh
36974a31ce6Stimh return (0);
37074a31ce6Stimh }
37174a31ce6Stimh
37224db4641Seschrock /*ARGSUSED*/
37324db4641Seschrock static int
dev_fmri_present(topo_mod_t * mod,tnode_t * node,topo_version_t version,nvlist_t * in,nvlist_t ** out)37424db4641Seschrock dev_fmri_present(topo_mod_t *mod, tnode_t *node, topo_version_t version,
37524db4641Seschrock nvlist_t *in, nvlist_t **out)
37624db4641Seschrock {
37724db4641Seschrock uint8_t fmversion;
37824db4641Seschrock char *devpath = NULL;
37924db4641Seschrock uint32_t present;
380faf8f993Sstephh char *devid = NULL, *path;
381faf8f993Sstephh ddi_devid_t id;
382faf8f993Sstephh ddi_devid_t matchid;
383faf8f993Sstephh di_node_t dnode;
384faf8f993Sstephh struct stat sb;
385faf8f993Sstephh int len;
38624db4641Seschrock
38724db4641Seschrock if (version > TOPO_METH_PRESENT_VERSION)
38824db4641Seschrock return (topo_mod_seterrno(mod, EMOD_VER_NEW));
38924db4641Seschrock
39024db4641Seschrock if (nvlist_lookup_uint8(in, FM_VERSION, &fmversion) != 0 ||
39124db4641Seschrock fmversion > FM_DEV_SCHEME_VERSION ||
39224db4641Seschrock nvlist_lookup_string(in, FM_FMRI_DEV_PATH, &devpath) != 0)
39324db4641Seschrock return (topo_mod_seterrno(mod, EMOD_FMRI_MALFORM));
39424db4641Seschrock
39524db4641Seschrock (void) nvlist_lookup_string(in, FM_FMRI_DEV_ID, &devid);
39624db4641Seschrock
397faf8f993Sstephh if (devpath == NULL || strlen(devpath) == 0)
39824db4641Seschrock return (topo_mod_seterrno(mod, EMOD_FMRI_MALFORM));
39924db4641Seschrock
40024db4641Seschrock /*
401faf8f993Sstephh * stat() the device node in devfs. This will tell us if the device is
402faf8f993Sstephh * present or not. Don't stat the minor, just the whole device.
403faf8f993Sstephh * If the device is present and there is a devid, it must also match.
404faf8f993Sstephh * so di_init that one node. No need for DINFOFORCE.
40524db4641Seschrock */
40625c6ff4bSstephh len = strlen(devpath) + strlen("/devices") + 1;
407faf8f993Sstephh path = topo_mod_alloc(mod, len);
408faf8f993Sstephh (void) snprintf(path, len, "/devices%s", devpath);
409faf8f993Sstephh if (devid == NULL) {
410faf8f993Sstephh if (stat(path, &sb) != -1)
411faf8f993Sstephh present = 1;
412faf8f993Sstephh else if ((dnode = di_init("/", DINFOCACHE)) == DI_NODE_NIL)
413faf8f993Sstephh present = 0;
414faf8f993Sstephh else {
415faf8f993Sstephh if (di_lookup_node(dnode, devpath) == DI_NODE_NIL)
416faf8f993Sstephh present = 0;
417faf8f993Sstephh else
418faf8f993Sstephh present = 1;
419faf8f993Sstephh di_fini(dnode);
420faf8f993Sstephh }
42124db4641Seschrock } else {
422faf8f993Sstephh if (stat(path, &sb) == -1)
423faf8f993Sstephh present = 0;
424faf8f993Sstephh else if ((dnode = di_init(devpath, DINFOCPYONE)) == DI_NODE_NIL)
425faf8f993Sstephh present = 0;
426faf8f993Sstephh else {
427faf8f993Sstephh if ((id = di_devid(dnode)) == NULL ||
428faf8f993Sstephh devid_str_decode(devid, &matchid, NULL) != 0)
429faf8f993Sstephh present = 0;
430faf8f993Sstephh else {
431faf8f993Sstephh if (devid_compare(id, matchid) != 0)
432faf8f993Sstephh present = 0;
433faf8f993Sstephh else
434faf8f993Sstephh present = 1;
435faf8f993Sstephh devid_free(matchid);
436faf8f993Sstephh }
437faf8f993Sstephh di_fini(dnode);
438faf8f993Sstephh }
43924db4641Seschrock }
440faf8f993Sstephh topo_mod_free(mod, path, len);
44124db4641Seschrock
44224db4641Seschrock if (topo_mod_nvalloc(mod, out, NV_UNIQUE_NAME) != 0)
44324db4641Seschrock return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
44424db4641Seschrock if (nvlist_add_uint32(*out, TOPO_METH_PRESENT_RET, present) != 0) {
44524db4641Seschrock nvlist_free(*out);
44624db4641Seschrock return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
44724db4641Seschrock }
44824db4641Seschrock
44924db4641Seschrock return (0);
45024db4641Seschrock }
45124db4641Seschrock
45225c6ff4bSstephh /*ARGSUSED*/
45325c6ff4bSstephh static int
dev_fmri_replaced(topo_mod_t * mod,tnode_t * node,topo_version_t version,nvlist_t * in,nvlist_t ** out)45425c6ff4bSstephh dev_fmri_replaced(topo_mod_t *mod, tnode_t *node, topo_version_t version,
45525c6ff4bSstephh nvlist_t *in, nvlist_t **out)
45625c6ff4bSstephh {
45725c6ff4bSstephh uint8_t fmversion;
45825c6ff4bSstephh char *devpath = NULL;
45925c6ff4bSstephh uint32_t rval;
46025c6ff4bSstephh char *devid = NULL, *path;
46125c6ff4bSstephh ddi_devid_t id;
46225c6ff4bSstephh ddi_devid_t matchid;
46325c6ff4bSstephh di_node_t dnode;
46425c6ff4bSstephh struct stat sb;
46525c6ff4bSstephh int len;
46625c6ff4bSstephh
46725c6ff4bSstephh if (version > TOPO_METH_REPLACED_VERSION)
46825c6ff4bSstephh return (topo_mod_seterrno(mod, EMOD_VER_NEW));
46925c6ff4bSstephh
47025c6ff4bSstephh if (nvlist_lookup_uint8(in, FM_VERSION, &fmversion) != 0 ||
47125c6ff4bSstephh fmversion > FM_DEV_SCHEME_VERSION ||
47225c6ff4bSstephh nvlist_lookup_string(in, FM_FMRI_DEV_PATH, &devpath) != 0)
47325c6ff4bSstephh return (topo_mod_seterrno(mod, EMOD_FMRI_MALFORM));
47425c6ff4bSstephh
47525c6ff4bSstephh (void) nvlist_lookup_string(in, FM_FMRI_DEV_ID, &devid);
47625c6ff4bSstephh
47725c6ff4bSstephh if (devpath == NULL || strlen(devpath) == 0)
47825c6ff4bSstephh return (topo_mod_seterrno(mod, EMOD_FMRI_MALFORM));
47925c6ff4bSstephh
48025c6ff4bSstephh /*
48125c6ff4bSstephh * stat() the device node in devfs. This will tell us if the device is
48225c6ff4bSstephh * present or not. Don't stat the minor, just the whole device.
48325c6ff4bSstephh * If the device is present and there is a devid, it must also match.
48425c6ff4bSstephh * so di_init that one node. No need for DINFOFORCE.
48525c6ff4bSstephh */
48625c6ff4bSstephh len = strlen(devpath) + strlen("/devices") + 1;
48725c6ff4bSstephh path = topo_mod_alloc(mod, len);
48825c6ff4bSstephh (void) snprintf(path, len, "/devices%s", devpath);
48925c6ff4bSstephh if (devid == NULL) {
49025c6ff4bSstephh if (stat(path, &sb) != -1)
49125c6ff4bSstephh rval = FMD_OBJ_STATE_UNKNOWN;
49225c6ff4bSstephh else if ((dnode = di_init("/", DINFOCACHE)) == DI_NODE_NIL)
493e58a33b6SStephen Hanson rval = FMD_OBJ_STATE_UNKNOWN;
49425c6ff4bSstephh else {
49525c6ff4bSstephh if (di_lookup_node(dnode, devpath) == DI_NODE_NIL)
496e58a33b6SStephen Hanson rval = FMD_OBJ_STATE_UNKNOWN;
49725c6ff4bSstephh else
49825c6ff4bSstephh rval = FMD_OBJ_STATE_UNKNOWN;
49925c6ff4bSstephh di_fini(dnode);
50025c6ff4bSstephh }
50125c6ff4bSstephh } else {
50225c6ff4bSstephh if (stat(path, &sb) == -1)
503e58a33b6SStephen Hanson rval = FMD_OBJ_STATE_UNKNOWN;
50425c6ff4bSstephh else if ((dnode = di_init(devpath, DINFOCPYONE)) == DI_NODE_NIL)
505e58a33b6SStephen Hanson rval = FMD_OBJ_STATE_UNKNOWN;
50625c6ff4bSstephh else {
50725c6ff4bSstephh if ((id = di_devid(dnode)) == NULL ||
50825c6ff4bSstephh devid_str_decode(devid, &matchid, NULL) != 0)
50925c6ff4bSstephh rval = FMD_OBJ_STATE_UNKNOWN;
51025c6ff4bSstephh else {
51125c6ff4bSstephh if (devid_compare(id, matchid) != 0)
51225c6ff4bSstephh rval = FMD_OBJ_STATE_REPLACED;
51325c6ff4bSstephh else
51425c6ff4bSstephh rval = FMD_OBJ_STATE_STILL_PRESENT;
51525c6ff4bSstephh devid_free(matchid);
51625c6ff4bSstephh }
51725c6ff4bSstephh di_fini(dnode);
51825c6ff4bSstephh }
51925c6ff4bSstephh }
52025c6ff4bSstephh topo_mod_free(mod, path, len);
52125c6ff4bSstephh
52225c6ff4bSstephh if (topo_mod_nvalloc(mod, out, NV_UNIQUE_NAME) != 0)
52325c6ff4bSstephh return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
52425c6ff4bSstephh if (nvlist_add_uint32(*out, TOPO_METH_REPLACED_RET, rval) != 0) {
52525c6ff4bSstephh nvlist_free(*out);
52625c6ff4bSstephh return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
52725c6ff4bSstephh }
52825c6ff4bSstephh
52925c6ff4bSstephh return (0);
53025c6ff4bSstephh }
53125c6ff4bSstephh
53224db4641Seschrock /*ARGSUSED*/
53324db4641Seschrock static int
dev_fmri_unusable(topo_mod_t * mod,tnode_t * node,topo_version_t version,nvlist_t * in,nvlist_t ** out)53424db4641Seschrock dev_fmri_unusable(topo_mod_t *mod, tnode_t *node, topo_version_t version,
53524db4641Seschrock nvlist_t *in, nvlist_t **out)
53624db4641Seschrock {
53724db4641Seschrock di_node_t dnode;
53824db4641Seschrock uint8_t fmversion;
53924db4641Seschrock char *devpath = NULL;
54024db4641Seschrock uint32_t unusable;
54124db4641Seschrock uint_t state;
54224db4641Seschrock
54325c6ff4bSstephh if (version > TOPO_METH_UNUSABLE_VERSION)
54424db4641Seschrock return (topo_mod_seterrno(mod, EMOD_VER_NEW));
54524db4641Seschrock
54624db4641Seschrock if (nvlist_lookup_uint8(in, FM_VERSION, &fmversion) != 0 ||
54724db4641Seschrock fmversion > FM_DEV_SCHEME_VERSION ||
54824db4641Seschrock nvlist_lookup_string(in, FM_FMRI_DEV_PATH, &devpath) != 0)
54924db4641Seschrock return (topo_mod_seterrno(mod, EMOD_FMRI_MALFORM));
55024db4641Seschrock
55124db4641Seschrock if (devpath == NULL)
55224db4641Seschrock return (topo_mod_seterrno(mod, EMOD_FMRI_MALFORM));
55324db4641Seschrock
55424db4641Seschrock if ((dnode = di_init(devpath, DINFOCPYONE)) == DI_NODE_NIL) {
55524db4641Seschrock if (errno != ENXIO)
55624db4641Seschrock return (topo_mod_seterrno(mod, EMOD_UKNOWN_ENUM));
55724db4641Seschrock unusable = 1;
55824db4641Seschrock } else {
55925e8c5aaSvikram uint_t retired = di_retired(dnode);
56024db4641Seschrock state = di_state(dnode);
56125e8c5aaSvikram if (retired || (state & (DI_DEVICE_OFFLINE | DI_DEVICE_DOWN |
56225e8c5aaSvikram DI_BUS_QUIESCED | DI_BUS_DOWN)))
56324db4641Seschrock unusable = 1;
56424db4641Seschrock else
56524db4641Seschrock unusable = 0;
56624db4641Seschrock di_fini(dnode);
56724db4641Seschrock }
56824db4641Seschrock
56924db4641Seschrock if (topo_mod_nvalloc(mod, out, NV_UNIQUE_NAME) != 0)
57024db4641Seschrock return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
5713e2676e0Svikram if (nvlist_add_uint32(*out, TOPO_METH_UNUSABLE_RET, unusable) != 0) {
57224db4641Seschrock nvlist_free(*out);
57324db4641Seschrock return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
57424db4641Seschrock }
57524db4641Seschrock
57624db4641Seschrock return (0);
57724db4641Seschrock }
57824db4641Seschrock
57925c6ff4bSstephh /*ARGSUSED*/
58025c6ff4bSstephh static int
dev_fmri_service_state(topo_mod_t * mod,tnode_t * node,topo_version_t version,nvlist_t * in,nvlist_t ** out)58125c6ff4bSstephh dev_fmri_service_state(topo_mod_t *mod, tnode_t *node, topo_version_t version,
58225c6ff4bSstephh nvlist_t *in, nvlist_t **out)
58325c6ff4bSstephh {
58425c6ff4bSstephh di_node_t dnode;
58525c6ff4bSstephh uint8_t fmversion;
58625c6ff4bSstephh char *devpath = NULL;
58725c6ff4bSstephh uint32_t service_state;
58825c6ff4bSstephh uint_t state;
58925c6ff4bSstephh
59025c6ff4bSstephh if (version > TOPO_METH_SERVICE_STATE_VERSION)
59125c6ff4bSstephh return (topo_mod_seterrno(mod, EMOD_VER_NEW));
59225c6ff4bSstephh
59325c6ff4bSstephh if (nvlist_lookup_uint8(in, FM_VERSION, &fmversion) != 0 ||
59425c6ff4bSstephh fmversion > FM_DEV_SCHEME_VERSION ||
59525c6ff4bSstephh nvlist_lookup_string(in, FM_FMRI_DEV_PATH, &devpath) != 0)
59625c6ff4bSstephh return (topo_mod_seterrno(mod, EMOD_FMRI_MALFORM));
59725c6ff4bSstephh
59825c6ff4bSstephh if (devpath == NULL)
59925c6ff4bSstephh return (topo_mod_seterrno(mod, EMOD_FMRI_MALFORM));
60025c6ff4bSstephh
60125c6ff4bSstephh if ((dnode = di_init(devpath, DINFOCPYONE)) == DI_NODE_NIL) {
60225c6ff4bSstephh if (errno != ENXIO)
60325c6ff4bSstephh return (topo_mod_seterrno(mod, EMOD_UKNOWN_ENUM));
60425c6ff4bSstephh service_state = FMD_SERVICE_STATE_UNUSABLE;
60525c6ff4bSstephh } else {
60625c6ff4bSstephh uint_t retired = di_retired(dnode);
60725c6ff4bSstephh state = di_state(dnode);
60825c6ff4bSstephh if (retired || (state & (DI_DEVICE_OFFLINE | DI_DEVICE_DOWN |
60925c6ff4bSstephh DI_BUS_QUIESCED | DI_BUS_DOWN)))
61025c6ff4bSstephh service_state = FMD_SERVICE_STATE_UNUSABLE;
61125c6ff4bSstephh else if (state & DI_DEVICE_DEGRADED)
61225c6ff4bSstephh service_state = FMD_SERVICE_STATE_DEGRADED;
61325c6ff4bSstephh else
61425c6ff4bSstephh service_state = FMD_SERVICE_STATE_OK;
61525c6ff4bSstephh di_fini(dnode);
61625c6ff4bSstephh }
61725c6ff4bSstephh
61825c6ff4bSstephh if (topo_mod_nvalloc(mod, out, NV_UNIQUE_NAME) != 0)
61925c6ff4bSstephh return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
62025c6ff4bSstephh if (nvlist_add_uint32(*out, TOPO_METH_SERVICE_STATE_RET,
62125c6ff4bSstephh service_state) != 0) {
62225c6ff4bSstephh nvlist_free(*out);
62325c6ff4bSstephh return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
62425c6ff4bSstephh }
62525c6ff4bSstephh
62625c6ff4bSstephh return (0);
62725c6ff4bSstephh }
62825c6ff4bSstephh
62974a31ce6Stimh static nvlist_t *
dev_fmri_create(topo_mod_t * mp,const char * id,const char * path)63074a31ce6Stimh dev_fmri_create(topo_mod_t *mp, const char *id, const char *path)
63174a31ce6Stimh {
63274a31ce6Stimh nvlist_t *out = NULL;
63374a31ce6Stimh int e;
63474a31ce6Stimh
63574a31ce6Stimh if (topo_mod_nvalloc(mp, &out, NV_UNIQUE_NAME) != 0) {
63674a31ce6Stimh (void) topo_mod_seterrno(mp, EMOD_FMRI_NVL);
63774a31ce6Stimh return (NULL);
63874a31ce6Stimh }
63974a31ce6Stimh e = nvlist_add_string(out, FM_FMRI_SCHEME, FM_FMRI_SCHEME_DEV);
64074a31ce6Stimh e |= nvlist_add_uint8(out, FM_VERSION, FM_DEV_SCHEME_VERSION);
64174a31ce6Stimh e |= nvlist_add_string(out, FM_FMRI_DEV_PATH, path);
64274a31ce6Stimh
64374a31ce6Stimh if (id != NULL)
64474a31ce6Stimh e |= nvlist_add_string(out, FM_FMRI_DEV_ID, id);
64574a31ce6Stimh
64674a31ce6Stimh if (e == 0)
64774a31ce6Stimh return (out);
64874a31ce6Stimh
64974a31ce6Stimh topo_mod_dprintf(mp, "construction of dev nvl failed");
65074a31ce6Stimh (void) topo_mod_seterrno(mp, EMOD_FMRI_NVL);
65174a31ce6Stimh nvlist_free(out);
65274a31ce6Stimh return (NULL);
65374a31ce6Stimh }
65474a31ce6Stimh
65574a31ce6Stimh /*ARGSUSED*/
65674a31ce6Stimh static int
dev_fmri_create_meth(topo_mod_t * mp,tnode_t * node,topo_version_t version,nvlist_t * in,nvlist_t ** out)65774a31ce6Stimh dev_fmri_create_meth(topo_mod_t *mp, tnode_t *node, topo_version_t version,
65874a31ce6Stimh nvlist_t *in, nvlist_t **out)
65974a31ce6Stimh {
66074a31ce6Stimh nvlist_t *args = NULL;
66174a31ce6Stimh char *path, *id = NULL;
66274a31ce6Stimh
66374a31ce6Stimh if (version > TOPO_METH_FMRI_VERSION)
66474a31ce6Stimh return (topo_mod_seterrno(mp, EMOD_VER_NEW));
66574a31ce6Stimh
66674a31ce6Stimh if (nvlist_lookup_nvlist(in, TOPO_METH_FMRI_ARG_NVL, &args) != 0 ||
66774a31ce6Stimh nvlist_lookup_string(args, FM_FMRI_DEV_PATH, &path) != 0) {
66874a31ce6Stimh topo_mod_dprintf(mp, "no path string in method argument\n");
66974a31ce6Stimh return (topo_mod_seterrno(mp, EMOD_METHOD_INVAL));
67074a31ce6Stimh }
67174a31ce6Stimh
67274a31ce6Stimh (void) nvlist_lookup_string(args, FM_FMRI_DEV_ID, &id);
67374a31ce6Stimh
67474a31ce6Stimh if ((*out = dev_fmri_create(mp, id, path)) == NULL)
67574a31ce6Stimh return (-1);
67674a31ce6Stimh return (0);
67774a31ce6Stimh }
678