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