xref: /onnv-gate/usr/src/lib/fm/topo/libtopo/common/dev.c (revision 12967:ab9ae749152f)
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