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