xref: /onnv-gate/usr/src/lib/fm/topo/libtopo/common/mod.c (revision 12967:ab9ae749152f)
11414Scindi /*
21414Scindi  * CDDL HEADER START
31414Scindi  *
41414Scindi  * The contents of this file are subject to the terms of the
51414Scindi  * Common Development and Distribution License (the "License").
61414Scindi  * You may not use this file except in compliance with the License.
71414Scindi  *
81414Scindi  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
91414Scindi  * or http://www.opensolaris.org/os/licensing.
101414Scindi  * See the License for the specific language governing permissions
111414Scindi  * and limitations under the License.
121414Scindi  *
131414Scindi  * When distributing Covered Code, include this CDDL HEADER in each
141414Scindi  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
151414Scindi  * If applicable, add the following below this CDDL HEADER, with the
161414Scindi  * fields enclosed by brackets "[]" replaced with your own identifying
171414Scindi  * information: Portions Copyright [yyyy] [name of copyright owner]
181414Scindi  *
191414Scindi  * CDDL HEADER END
201414Scindi  */
211414Scindi 
221414Scindi /*
23*12967Sgavin.maltby@oracle.com  * Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
241414Scindi  */
251414Scindi 
261414Scindi #include <limits.h>
271414Scindi #include <strings.h>
281414Scindi #include <unistd.h>
291414Scindi #include <libnvpair.h>
301414Scindi #include <fm/topo_mod.h>
311414Scindi #include <sys/fm/protocol.h>
321414Scindi 
331414Scindi #include <fcntl.h>
341414Scindi #include <sys/types.h>
351414Scindi #include <sys/stat.h>
361414Scindi #include <sys/objfs.h>
371414Scindi #include <sys/modctl.h>
381414Scindi #include <libelf.h>
391414Scindi #include <gelf.h>
401414Scindi 
413062Scindi #include <topo_method.h>
423323Scindi #include <topo_subr.h>
433062Scindi #include <mod.h>
441414Scindi 
451414Scindi static int mod_enum(topo_mod_t *, tnode_t *, const char *, topo_instance_t,
463062Scindi     topo_instance_t, void *, void *);
471414Scindi static void mod_release(topo_mod_t *, tnode_t *);
481414Scindi static int mod_fmri_create_meth(topo_mod_t *, tnode_t *, topo_version_t,
491414Scindi     nvlist_t *, nvlist_t **);
503323Scindi static int mod_fmri_nvl2str(topo_mod_t *, tnode_t *, topo_version_t,
513323Scindi     nvlist_t *, nvlist_t **);
521414Scindi 
531414Scindi static const topo_method_t mod_methods[] = {
541414Scindi 	{ TOPO_METH_FMRI, TOPO_METH_FMRI_DESC, TOPO_METH_FMRI_VERSION,
551414Scindi 	    TOPO_STABILITY_INTERNAL, mod_fmri_create_meth },
563323Scindi 	{ TOPO_METH_NVL2STR, TOPO_METH_NVL2STR_DESC, TOPO_METH_NVL2STR_VERSION,
573323Scindi 	    TOPO_STABILITY_INTERNAL, mod_fmri_nvl2str },
581414Scindi 	{ NULL }
591414Scindi };
601414Scindi 
613062Scindi static const topo_modops_t mod_modops =
623062Scindi 	{ mod_enum, mod_release };
631414Scindi static const topo_modinfo_t mod_info =
643062Scindi 	{ "mod", FM_FMRI_SCHEME_MOD, MOD_VERSION, &mod_modops };
651414Scindi 
663062Scindi int
mod_init(topo_mod_t * mod,topo_version_t version)673062Scindi mod_init(topo_mod_t *mod, topo_version_t version)
681414Scindi {
693062Scindi 	if (getenv("TOPOMODDEBUG"))
703062Scindi 		topo_mod_setdebug(mod);
711414Scindi 	topo_mod_dprintf(mod, "initializing mod builtin\n");
721414Scindi 
733062Scindi 	if (version != MOD_VERSION)
743062Scindi 		return (topo_mod_seterrno(mod, EMOD_VER_NEW));
753062Scindi 
763062Scindi 	if (topo_mod_register(mod, &mod_info, TOPO_VERSION) != 0) {
771414Scindi 		topo_mod_dprintf(mod, "failed to register mod_info: "
781414Scindi 		    "%s\n", topo_mod_errmsg(mod));
793062Scindi 		return (-1); /* mod errno already set */
801414Scindi 	}
813062Scindi 
823062Scindi 	return (0);
831414Scindi }
841414Scindi 
851414Scindi void
mod_fini(topo_mod_t * mod)861414Scindi mod_fini(topo_mod_t *mod)
871414Scindi {
881414Scindi 	topo_mod_unregister(mod);
891414Scindi }
901414Scindi 
911414Scindi /*ARGSUSED*/
921414Scindi static int
mod_enum(topo_mod_t * mod,tnode_t * pnode,const char * name,topo_instance_t min,topo_instance_t max,void * notused1,void * notused2)931414Scindi mod_enum(topo_mod_t *mod, tnode_t *pnode, const char *name,
943062Scindi     topo_instance_t min, topo_instance_t max, void *notused1, void *notused2)
951414Scindi {
96*12967Sgavin.maltby@oracle.com 	/*
97*12967Sgavin.maltby@oracle.com 	 * Methods are registered, but there is no enumeration.  Should
98*12967Sgavin.maltby@oracle.com 	 * enumeration be added be sure to cater for global vs non-global
99*12967Sgavin.maltby@oracle.com 	 * zones.
100*12967Sgavin.maltby@oracle.com 	 */
1011414Scindi 	(void) topo_method_register(mod, pnode, mod_methods);
1021414Scindi 	return (0);
1031414Scindi }
1041414Scindi 
1051414Scindi static void
mod_release(topo_mod_t * mod,tnode_t * node)1061414Scindi mod_release(topo_mod_t *mod, tnode_t *node)
1071414Scindi {
1081414Scindi 	topo_method_unregister_all(mod, node);
1091414Scindi }
1101414Scindi 
1113323Scindi static int
mod_binary_path_get(topo_mod_t * mp,const char * objpath)1123323Scindi mod_binary_path_get(topo_mod_t *mp, const char *objpath)
1131414Scindi {
1141414Scindi 	Elf *elf = NULL;
1151414Scindi 	Elf_Scn *scn = NULL;
1161414Scindi 	GElf_Ehdr ehdr;
1171414Scindi 	GElf_Shdr shdr;
1181414Scindi 	int fd;
1191414Scindi 
1201414Scindi 	if ((fd = open(objpath, O_RDONLY)) < 0) {
1213323Scindi 		topo_mod_dprintf(mp, "unable to open %s\n", objpath);
1223323Scindi 		return (-1);
1231414Scindi 	}
1243323Scindi 
1251414Scindi 	if (elf_version(EV_CURRENT) == EV_NONE) {
1261414Scindi 		topo_mod_dprintf(mp, "Elf version out of whack\n");
1271414Scindi 		goto mbpg_bail;
1281414Scindi 	}
1291414Scindi 	if ((elf = elf_begin(fd, ELF_C_READ, NULL)) == NULL) {
1301414Scindi 		topo_mod_dprintf(mp, "elf_begin failed\n");
1311414Scindi 		goto mbpg_bail;
1321414Scindi 	}
1331414Scindi 	if ((gelf_getehdr(elf, &ehdr)) == NULL) {
1341414Scindi 		topo_mod_dprintf(mp, "gelf_getehdr failed\n");
1351414Scindi 		goto mbpg_bail;
1361414Scindi 	}
1371414Scindi 	scn = elf_getscn(elf, 0);	/* "seek" to start of sections */
1381414Scindi 	while ((scn = elf_nextscn(elf, scn)) != NULL) {
1391414Scindi 		const char *sh_name;
1401414Scindi 		if (gelf_getshdr(scn, &shdr) == NULL) {
1411414Scindi 			topo_mod_dprintf(mp, "gelf_getshdr failed\n");
1421414Scindi 			goto mbpg_bail;
1431414Scindi 		}
1441414Scindi 		if (shdr.sh_type != SHT_PROGBITS)
1451414Scindi 			continue;
1461414Scindi 		sh_name = elf_strptr(elf,
1471414Scindi 		    ehdr.e_shstrndx, (size_t)shdr.sh_name);
1481414Scindi 		if (strcmp(sh_name, ".filename") != 0)
1491414Scindi 			continue;
1503323Scindi 		if (elf_getdata(scn, NULL) == NULL) {
1511414Scindi 			topo_mod_dprintf(mp, "no filename data");
1521414Scindi 			break;
1531414Scindi 		}
1541414Scindi 		break;
1551414Scindi 	}
15611050SRobert.Johnston@Sun.COM 	(void) elf_end(elf);
1571414Scindi 	(void) close(fd);
1583323Scindi 	return (0);
1591414Scindi 
1601414Scindi mbpg_bail:
1611414Scindi 	if (elf != NULL)
16211050SRobert.Johnston@Sun.COM 		(void) elf_end(elf);
1631414Scindi 	if (fd >= 0)
1641414Scindi 		(void) close(fd);
1651414Scindi 	(void) topo_mod_seterrno(mp, EMOD_METHOD_INVAL);
1663323Scindi 	return (-1);
1671414Scindi }
1681414Scindi 
1691414Scindi static int
mod_nvl_data(topo_mod_t * mp,nvlist_t * out,const char * path)1701414Scindi mod_nvl_data(topo_mod_t *mp, nvlist_t *out, const char *path)
1711414Scindi {
1721414Scindi 	struct modinfo mi;
1731414Scindi 	struct stat64 s;
1741414Scindi 	int id, e;
1751414Scindi 
1761414Scindi 	if (stat64(path, &s) < 0) {
1771414Scindi 		topo_mod_dprintf(mp,
1781414Scindi 		    "No system object file for driver %s", path);
1791414Scindi 		return (topo_mod_seterrno(mp, EMOD_METHOD_INVAL));
1801414Scindi 	}
1811414Scindi 
1821414Scindi 	id = OBJFS_MODID(s.st_ino);
1831414Scindi 	mi.mi_id = mi.mi_nextid = id;
1841414Scindi 	mi.mi_info = MI_INFO_ONE | MI_INFO_NOBASE;
1851414Scindi 	if (modctl(MODINFO, id, &mi) < 0) {
1861414Scindi 		return (topo_mod_seterrno(mp, EMOD_METHOD_INVAL));
1871414Scindi 	}
1881414Scindi 	mi.mi_name[MODMAXNAMELEN - 1] = '\0';
1891414Scindi 	mi.mi_msinfo[0].msi_linkinfo[MODMAXNAMELEN - 1] = '\0';
1901414Scindi 	e = nvlist_add_string(out, FM_FMRI_SCHEME, FM_FMRI_SCHEME_MOD);
1911414Scindi 	e |= nvlist_add_uint8(out, FM_VERSION, FM_MOD_SCHEME_VERSION);
1921414Scindi 	e |= nvlist_add_int32(out, FM_FMRI_MOD_ID, id);
1931414Scindi 	e |= nvlist_add_string(out, FM_FMRI_MOD_NAME, mi.mi_name);
1941414Scindi 	e |= nvlist_add_string(out,
1951414Scindi 	    FM_FMRI_MOD_DESC, mi.mi_msinfo[0].msi_linkinfo);
1961414Scindi 	if (e != 0)
1971414Scindi 		return (topo_mod_seterrno(mp, EMOD_FMRI_NVL));
1981414Scindi 
1991414Scindi 	return (0);
2001414Scindi }
2011414Scindi 
2021414Scindi static nvlist_t *
mod_fmri_create(topo_mod_t * mp,const char * driver)2031414Scindi mod_fmri_create(topo_mod_t *mp, const char *driver)
2041414Scindi {
2051414Scindi 	nvlist_t *out = NULL;
2061414Scindi 	char objpath[PATH_MAX];
2071414Scindi 
2083062Scindi 	if (topo_mod_nvalloc(mp, &out, NV_UNIQUE_NAME) != 0) {
2091414Scindi 		(void) topo_mod_seterrno(mp, EMOD_FMRI_NVL);
2101414Scindi 		goto mfc_bail;
2111414Scindi 	}
2121414Scindi 
2131414Scindi 	(void) snprintf(objpath, PATH_MAX, "%s/%s/object", OBJFS_ROOT, driver);
2141414Scindi 
2153323Scindi 	/*
2163323Scindi 	 * Validate the module object ELF header if possible
2173323Scindi 	 */
2183323Scindi 	if (mod_binary_path_get(mp, objpath) < 0)
2191414Scindi 		goto mfc_bail;
2201414Scindi 
2213323Scindi 	if (mod_nvl_data(mp, out, objpath) < 0) {
2223323Scindi 		topo_mod_dprintf(mp, "failed to get modinfo for %s", driver);
2231414Scindi 		goto mfc_bail;
2241414Scindi 	}
2251414Scindi 
2261414Scindi 	return (out);
2271414Scindi 
2281414Scindi mfc_bail:
2291414Scindi 	nvlist_free(out);
2301414Scindi 	return (NULL);
2311414Scindi }
2321414Scindi 
2331414Scindi /*ARGSUSED*/
2341414Scindi static int
mod_fmri_create_meth(topo_mod_t * mp,tnode_t * node,topo_version_t version,nvlist_t * in,nvlist_t ** out)2351414Scindi mod_fmri_create_meth(topo_mod_t *mp, tnode_t *node, topo_version_t version,
2361414Scindi     nvlist_t *in, nvlist_t **out)
2371414Scindi {
2381414Scindi 	nvlist_t *args;
2391414Scindi 	nvlist_t *modnvl;
2401414Scindi 	char *driver;
2411414Scindi 
2421414Scindi 	if (version > TOPO_METH_FMRI_VERSION)
2431414Scindi 		return (topo_mod_seterrno(mp, EMOD_VER_NEW));
2441414Scindi 
2451414Scindi 	if (nvlist_lookup_nvlist(in, TOPO_METH_FMRI_ARG_NVL, &args) != 0 ||
2461414Scindi 	    nvlist_lookup_string(args, "DRIVER", &driver) != 0) {
2471414Scindi 		topo_mod_dprintf(mp, "no DRIVER string in method argument\n");
2481414Scindi 		return (topo_mod_seterrno(mp, EMOD_METHOD_INVAL));
2491414Scindi 	}
2501414Scindi 
2511414Scindi 	modnvl = mod_fmri_create(mp, driver);
2521414Scindi 	if (modnvl == NULL) {
2531414Scindi 		*out = NULL;
2541414Scindi 		topo_mod_dprintf(mp, "failed to create contained mod FMRI\n");
2553323Scindi 		return (-1);
2561414Scindi 	}
2571414Scindi 	*out = modnvl;
2581414Scindi 	return (0);
2591414Scindi }
2603323Scindi 
2613323Scindi #define	MAXINTSTR	11
2623323Scindi 
2633323Scindi static ssize_t
fmri_nvl2str(nvlist_t * nvl,char * buf,size_t buflen)2643323Scindi fmri_nvl2str(nvlist_t *nvl, char *buf, size_t buflen)
2653323Scindi {
2663323Scindi 	nvlist_t *anvl = NULL;
26710462SSean.Ye@Sun.COM 	nvpair_t *apair;
2683323Scindi 	uint8_t version;
2693323Scindi 	ssize_t size = 0;
2703323Scindi 	int32_t modid;
27110462SSean.Ye@Sun.COM 	char *modname = NULL, *aname, *aval;
2723323Scindi 	char numbuf[MAXINTSTR];
2733323Scindi 	int err;
2743323Scindi 
2753323Scindi 	if (nvlist_lookup_uint8(nvl, FM_VERSION, &version) != 0 ||
2763323Scindi 	    version > FM_MOD_SCHEME_VERSION)
2773323Scindi 		return (-1);
2783323Scindi 
2793323Scindi 	/* Get authority, if present */
2803323Scindi 	err = nvlist_lookup_nvlist(nvl, FM_FMRI_AUTHORITY, &anvl);
2813323Scindi 	if (err != 0 && err != ENOENT)
2823323Scindi 		return (-1);
2833323Scindi 
2843323Scindi 	/*
2853323Scindi 	 *  For brevity, we only include the module name and id
2863323Scindi 	 *  present in the FMRI in our output string.  The FMRI
2873323Scindi 	 *  also has data on the package containing the module.
2883323Scindi 	 */
2893323Scindi 
2903323Scindi 	/* There must be a module name */
2913323Scindi 	err = nvlist_lookup_string(nvl, FM_FMRI_MOD_NAME, &modname);
2923323Scindi 	if (err != 0 || modname == NULL)
2933323Scindi 		return (-1);
2943323Scindi 
2953323Scindi 	/* There must be a module id */
2963323Scindi 	err = nvlist_lookup_int32(nvl, FM_FMRI_MOD_ID, &modid);
2973323Scindi 	if (err != 0)
2983323Scindi 		return (-1);
2993323Scindi 
3003323Scindi 	/* mod:// */
3013323Scindi 	topo_fmristr_build(&size, buf, buflen, FM_FMRI_SCHEME_MOD, NULL, "://");
3023323Scindi 
3033323Scindi 	/* authority, if any */
30410462SSean.Ye@Sun.COM 	if (anvl != NULL) {
30510462SSean.Ye@Sun.COM 		for (apair = nvlist_next_nvpair(anvl, NULL);
30610462SSean.Ye@Sun.COM 		    apair != NULL; apair = nvlist_next_nvpair(anvl, apair)) {
30710462SSean.Ye@Sun.COM 			if (nvpair_type(apair) != DATA_TYPE_STRING ||
30810462SSean.Ye@Sun.COM 			    nvpair_value_string(apair, &aval) != 0)
30910462SSean.Ye@Sun.COM 				continue;
31010462SSean.Ye@Sun.COM 			aname = nvpair_name(apair);
31110462SSean.Ye@Sun.COM 			topo_fmristr_build(&size, buf, buflen, ":", NULL, NULL);
31210462SSean.Ye@Sun.COM 			topo_fmristr_build(&size, buf, buflen, "=",
31310462SSean.Ye@Sun.COM 			    aname, aval);
31410462SSean.Ye@Sun.COM 		}
31510462SSean.Ye@Sun.COM 	}
3163323Scindi 
3173323Scindi 	/* module parts */
3183323Scindi 	topo_fmristr_build(&size, buf, buflen, modname,
3193323Scindi 	    "/" FM_FMRI_MOD_NAME "=", "/");
3203323Scindi 
3213323Scindi 	(void) snprintf(numbuf, MAXINTSTR, "%d", modid);
3223323Scindi 	topo_fmristr_build(&size, buf, buflen, numbuf, FM_FMRI_MOD_ID "=",
3233323Scindi 	    NULL);
3243323Scindi 
3253323Scindi 	return (size);
3263323Scindi }
3273323Scindi 
3283323Scindi /*ARGSUSED*/
3293323Scindi static int
mod_fmri_nvl2str(topo_mod_t * mod,tnode_t * node,topo_version_t version,nvlist_t * nvl,nvlist_t ** out)3303323Scindi mod_fmri_nvl2str(topo_mod_t *mod, tnode_t *node, topo_version_t version,
3313323Scindi     nvlist_t *nvl, nvlist_t **out)
3323323Scindi {
3333323Scindi 	ssize_t len;
3343323Scindi 	char *name = NULL;
3353323Scindi 	nvlist_t *fmristr;
3363323Scindi 
3373323Scindi 	if (version > TOPO_METH_NVL2STR_VERSION)
3383323Scindi 		return (topo_mod_seterrno(mod, EMOD_VER_NEW));
3393323Scindi 
3403323Scindi 	if ((len = fmri_nvl2str(nvl, NULL, 0)) == 0 ||
3413323Scindi 	    (name = topo_mod_alloc(mod, len + 1)) == NULL ||
3423323Scindi 	    fmri_nvl2str(nvl, name, len + 1) == 0) {
3433323Scindi 		if (name != NULL)
3443323Scindi 			topo_mod_free(mod, name, len + 1);
3453323Scindi 		return (topo_mod_seterrno(mod, EMOD_FMRI_NVL));
3463323Scindi 	}
3473323Scindi 
3483323Scindi 	if (topo_mod_nvalloc(mod, &fmristr, NV_UNIQUE_NAME) != 0)
3493323Scindi 		return (topo_mod_seterrno(mod, EMOD_FMRI_NVL));
3503323Scindi 	if (nvlist_add_string(fmristr, "fmri-string", name) != 0) {
3513323Scindi 		topo_mod_free(mod, name, len + 1);
3523323Scindi 		nvlist_free(fmristr);
3533323Scindi 		return (topo_mod_seterrno(mod, EMOD_FMRI_NVL));
3543323Scindi 	}
3553323Scindi 	topo_mod_free(mod, name, len + 1);
3563323Scindi 	*out = fmristr;
3573323Scindi 
3583323Scindi 	return (0);
3593323Scindi }
360