17aec1d6eScindi /* 27aec1d6eScindi * CDDL HEADER START 37aec1d6eScindi * 47aec1d6eScindi * The contents of this file are subject to the terms of the 57aec1d6eScindi * Common Development and Distribution License (the "License"). 67aec1d6eScindi * You may not use this file except in compliance with the License. 77aec1d6eScindi * 87aec1d6eScindi * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 97aec1d6eScindi * or http://www.opensolaris.org/os/licensing. 107aec1d6eScindi * See the License for the specific language governing permissions 117aec1d6eScindi * and limitations under the License. 127aec1d6eScindi * 137aec1d6eScindi * When distributing Covered Code, include this CDDL HEADER in each 147aec1d6eScindi * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 157aec1d6eScindi * If applicable, add the following below this CDDL HEADER, with the 167aec1d6eScindi * fields enclosed by brackets "[]" replaced with your own identifying 177aec1d6eScindi * information: Portions Copyright [yyyy] [name of copyright owner] 187aec1d6eScindi * 197aec1d6eScindi * CDDL HEADER END 207aec1d6eScindi */ 217aec1d6eScindi 227aec1d6eScindi /* 23*f6e214c7SGavin Maltby * Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved. 247aec1d6eScindi */ 257aec1d6eScindi 267aec1d6eScindi #include <limits.h> 277aec1d6eScindi #include <strings.h> 287aec1d6eScindi #include <unistd.h> 297aec1d6eScindi #include <libnvpair.h> 307aec1d6eScindi #include <fm/topo_mod.h> 317aec1d6eScindi #include <sys/fm/protocol.h> 327aec1d6eScindi 337aec1d6eScindi #include <fcntl.h> 347aec1d6eScindi #include <sys/types.h> 357aec1d6eScindi #include <sys/stat.h> 367aec1d6eScindi #include <sys/objfs.h> 377aec1d6eScindi #include <sys/modctl.h> 387aec1d6eScindi #include <libelf.h> 397aec1d6eScindi #include <gelf.h> 407aec1d6eScindi 410eb822a1Scindi #include <topo_method.h> 429dd0f810Scindi #include <topo_subr.h> 430eb822a1Scindi #include <mod.h> 447aec1d6eScindi 457aec1d6eScindi static int mod_enum(topo_mod_t *, tnode_t *, const char *, topo_instance_t, 460eb822a1Scindi topo_instance_t, void *, void *); 477aec1d6eScindi static void mod_release(topo_mod_t *, tnode_t *); 487aec1d6eScindi static int mod_fmri_create_meth(topo_mod_t *, tnode_t *, topo_version_t, 497aec1d6eScindi nvlist_t *, nvlist_t **); 509dd0f810Scindi static int mod_fmri_nvl2str(topo_mod_t *, tnode_t *, topo_version_t, 519dd0f810Scindi nvlist_t *, nvlist_t **); 527aec1d6eScindi 537aec1d6eScindi static const topo_method_t mod_methods[] = { 547aec1d6eScindi { TOPO_METH_FMRI, TOPO_METH_FMRI_DESC, TOPO_METH_FMRI_VERSION, 557aec1d6eScindi TOPO_STABILITY_INTERNAL, mod_fmri_create_meth }, 569dd0f810Scindi { TOPO_METH_NVL2STR, TOPO_METH_NVL2STR_DESC, TOPO_METH_NVL2STR_VERSION, 579dd0f810Scindi TOPO_STABILITY_INTERNAL, mod_fmri_nvl2str }, 587aec1d6eScindi { NULL } 597aec1d6eScindi }; 607aec1d6eScindi 610eb822a1Scindi static const topo_modops_t mod_modops = 620eb822a1Scindi { mod_enum, mod_release }; 637aec1d6eScindi static const topo_modinfo_t mod_info = 640eb822a1Scindi { "mod", FM_FMRI_SCHEME_MOD, MOD_VERSION, &mod_modops }; 657aec1d6eScindi 660eb822a1Scindi int 670eb822a1Scindi mod_init(topo_mod_t *mod, topo_version_t version) 687aec1d6eScindi { 690eb822a1Scindi if (getenv("TOPOMODDEBUG")) 700eb822a1Scindi topo_mod_setdebug(mod); 717aec1d6eScindi topo_mod_dprintf(mod, "initializing mod builtin\n"); 727aec1d6eScindi 730eb822a1Scindi if (version != MOD_VERSION) 740eb822a1Scindi return (topo_mod_seterrno(mod, EMOD_VER_NEW)); 750eb822a1Scindi 760eb822a1Scindi if (topo_mod_register(mod, &mod_info, TOPO_VERSION) != 0) { 777aec1d6eScindi topo_mod_dprintf(mod, "failed to register mod_info: " 787aec1d6eScindi "%s\n", topo_mod_errmsg(mod)); 790eb822a1Scindi return (-1); /* mod errno already set */ 807aec1d6eScindi } 810eb822a1Scindi 820eb822a1Scindi return (0); 837aec1d6eScindi } 847aec1d6eScindi 857aec1d6eScindi void 867aec1d6eScindi mod_fini(topo_mod_t *mod) 877aec1d6eScindi { 887aec1d6eScindi topo_mod_unregister(mod); 897aec1d6eScindi } 907aec1d6eScindi 917aec1d6eScindi /*ARGSUSED*/ 927aec1d6eScindi static int 937aec1d6eScindi mod_enum(topo_mod_t *mod, tnode_t *pnode, const char *name, 940eb822a1Scindi topo_instance_t min, topo_instance_t max, void *notused1, void *notused2) 957aec1d6eScindi { 96*f6e214c7SGavin Maltby /* 97*f6e214c7SGavin Maltby * Methods are registered, but there is no enumeration. Should 98*f6e214c7SGavin Maltby * enumeration be added be sure to cater for global vs non-global 99*f6e214c7SGavin Maltby * zones. 100*f6e214c7SGavin Maltby */ 1017aec1d6eScindi (void) topo_method_register(mod, pnode, mod_methods); 1027aec1d6eScindi return (0); 1037aec1d6eScindi } 1047aec1d6eScindi 1057aec1d6eScindi static void 1067aec1d6eScindi mod_release(topo_mod_t *mod, tnode_t *node) 1077aec1d6eScindi { 1087aec1d6eScindi topo_method_unregister_all(mod, node); 1097aec1d6eScindi } 1107aec1d6eScindi 1119dd0f810Scindi static int 1129dd0f810Scindi mod_binary_path_get(topo_mod_t *mp, const char *objpath) 1137aec1d6eScindi { 1147aec1d6eScindi Elf *elf = NULL; 1157aec1d6eScindi Elf_Scn *scn = NULL; 1167aec1d6eScindi GElf_Ehdr ehdr; 1177aec1d6eScindi GElf_Shdr shdr; 1187aec1d6eScindi int fd; 1197aec1d6eScindi 1207aec1d6eScindi if ((fd = open(objpath, O_RDONLY)) < 0) { 1219dd0f810Scindi topo_mod_dprintf(mp, "unable to open %s\n", objpath); 1229dd0f810Scindi return (-1); 1237aec1d6eScindi } 1249dd0f810Scindi 1257aec1d6eScindi if (elf_version(EV_CURRENT) == EV_NONE) { 1267aec1d6eScindi topo_mod_dprintf(mp, "Elf version out of whack\n"); 1277aec1d6eScindi goto mbpg_bail; 1287aec1d6eScindi } 1297aec1d6eScindi if ((elf = elf_begin(fd, ELF_C_READ, NULL)) == NULL) { 1307aec1d6eScindi topo_mod_dprintf(mp, "elf_begin failed\n"); 1317aec1d6eScindi goto mbpg_bail; 1327aec1d6eScindi } 1337aec1d6eScindi if ((gelf_getehdr(elf, &ehdr)) == NULL) { 1347aec1d6eScindi topo_mod_dprintf(mp, "gelf_getehdr failed\n"); 1357aec1d6eScindi goto mbpg_bail; 1367aec1d6eScindi } 1377aec1d6eScindi scn = elf_getscn(elf, 0); /* "seek" to start of sections */ 1387aec1d6eScindi while ((scn = elf_nextscn(elf, scn)) != NULL) { 1397aec1d6eScindi const char *sh_name; 1407aec1d6eScindi if (gelf_getshdr(scn, &shdr) == NULL) { 1417aec1d6eScindi topo_mod_dprintf(mp, "gelf_getshdr failed\n"); 1427aec1d6eScindi goto mbpg_bail; 1437aec1d6eScindi } 1447aec1d6eScindi if (shdr.sh_type != SHT_PROGBITS) 1457aec1d6eScindi continue; 1467aec1d6eScindi sh_name = elf_strptr(elf, 1477aec1d6eScindi ehdr.e_shstrndx, (size_t)shdr.sh_name); 1487aec1d6eScindi if (strcmp(sh_name, ".filename") != 0) 1497aec1d6eScindi continue; 1509dd0f810Scindi if (elf_getdata(scn, NULL) == NULL) { 1517aec1d6eScindi topo_mod_dprintf(mp, "no filename data"); 1527aec1d6eScindi break; 1537aec1d6eScindi } 1547aec1d6eScindi break; 1557aec1d6eScindi } 156888e0559SRobert Johnston (void) elf_end(elf); 1577aec1d6eScindi (void) close(fd); 1589dd0f810Scindi return (0); 1597aec1d6eScindi 1607aec1d6eScindi mbpg_bail: 1617aec1d6eScindi if (elf != NULL) 162888e0559SRobert Johnston (void) elf_end(elf); 1637aec1d6eScindi if (fd >= 0) 1647aec1d6eScindi (void) close(fd); 1657aec1d6eScindi (void) topo_mod_seterrno(mp, EMOD_METHOD_INVAL); 1669dd0f810Scindi return (-1); 1677aec1d6eScindi } 1687aec1d6eScindi 1697aec1d6eScindi static int 1707aec1d6eScindi mod_nvl_data(topo_mod_t *mp, nvlist_t *out, const char *path) 1717aec1d6eScindi { 1727aec1d6eScindi struct modinfo mi; 1737aec1d6eScindi struct stat64 s; 1747aec1d6eScindi int id, e; 1757aec1d6eScindi 1767aec1d6eScindi if (stat64(path, &s) < 0) { 1777aec1d6eScindi topo_mod_dprintf(mp, 1787aec1d6eScindi "No system object file for driver %s", path); 1797aec1d6eScindi return (topo_mod_seterrno(mp, EMOD_METHOD_INVAL)); 1807aec1d6eScindi } 1817aec1d6eScindi 1827aec1d6eScindi id = OBJFS_MODID(s.st_ino); 1837aec1d6eScindi mi.mi_id = mi.mi_nextid = id; 1847aec1d6eScindi mi.mi_info = MI_INFO_ONE | MI_INFO_NOBASE; 1857aec1d6eScindi if (modctl(MODINFO, id, &mi) < 0) { 1867aec1d6eScindi return (topo_mod_seterrno(mp, EMOD_METHOD_INVAL)); 1877aec1d6eScindi } 1887aec1d6eScindi mi.mi_name[MODMAXNAMELEN - 1] = '\0'; 1897aec1d6eScindi mi.mi_msinfo[0].msi_linkinfo[MODMAXNAMELEN - 1] = '\0'; 1907aec1d6eScindi e = nvlist_add_string(out, FM_FMRI_SCHEME, FM_FMRI_SCHEME_MOD); 1917aec1d6eScindi e |= nvlist_add_uint8(out, FM_VERSION, FM_MOD_SCHEME_VERSION); 1927aec1d6eScindi e |= nvlist_add_int32(out, FM_FMRI_MOD_ID, id); 1937aec1d6eScindi e |= nvlist_add_string(out, FM_FMRI_MOD_NAME, mi.mi_name); 1947aec1d6eScindi e |= nvlist_add_string(out, 1957aec1d6eScindi FM_FMRI_MOD_DESC, mi.mi_msinfo[0].msi_linkinfo); 1967aec1d6eScindi if (e != 0) 1977aec1d6eScindi return (topo_mod_seterrno(mp, EMOD_FMRI_NVL)); 1987aec1d6eScindi 1997aec1d6eScindi return (0); 2007aec1d6eScindi } 2017aec1d6eScindi 2027aec1d6eScindi static nvlist_t * 2037aec1d6eScindi mod_fmri_create(topo_mod_t *mp, const char *driver) 2047aec1d6eScindi { 2057aec1d6eScindi nvlist_t *out = NULL; 2067aec1d6eScindi char objpath[PATH_MAX]; 2077aec1d6eScindi 2080eb822a1Scindi if (topo_mod_nvalloc(mp, &out, NV_UNIQUE_NAME) != 0) { 2097aec1d6eScindi (void) topo_mod_seterrno(mp, EMOD_FMRI_NVL); 2107aec1d6eScindi goto mfc_bail; 2117aec1d6eScindi } 2127aec1d6eScindi 2137aec1d6eScindi (void) snprintf(objpath, PATH_MAX, "%s/%s/object", OBJFS_ROOT, driver); 2147aec1d6eScindi 2159dd0f810Scindi /* 2169dd0f810Scindi * Validate the module object ELF header if possible 2179dd0f810Scindi */ 2189dd0f810Scindi if (mod_binary_path_get(mp, objpath) < 0) 2197aec1d6eScindi goto mfc_bail; 2207aec1d6eScindi 2219dd0f810Scindi if (mod_nvl_data(mp, out, objpath) < 0) { 2229dd0f810Scindi topo_mod_dprintf(mp, "failed to get modinfo for %s", driver); 2237aec1d6eScindi goto mfc_bail; 2247aec1d6eScindi } 2257aec1d6eScindi 2267aec1d6eScindi return (out); 2277aec1d6eScindi 2287aec1d6eScindi mfc_bail: 2297aec1d6eScindi nvlist_free(out); 2307aec1d6eScindi return (NULL); 2317aec1d6eScindi } 2327aec1d6eScindi 2337aec1d6eScindi /*ARGSUSED*/ 2347aec1d6eScindi static int 2357aec1d6eScindi mod_fmri_create_meth(topo_mod_t *mp, tnode_t *node, topo_version_t version, 2367aec1d6eScindi nvlist_t *in, nvlist_t **out) 2377aec1d6eScindi { 2387aec1d6eScindi nvlist_t *args; 2397aec1d6eScindi nvlist_t *modnvl; 2407aec1d6eScindi char *driver; 2417aec1d6eScindi 2427aec1d6eScindi if (version > TOPO_METH_FMRI_VERSION) 2437aec1d6eScindi return (topo_mod_seterrno(mp, EMOD_VER_NEW)); 2447aec1d6eScindi 2457aec1d6eScindi if (nvlist_lookup_nvlist(in, TOPO_METH_FMRI_ARG_NVL, &args) != 0 || 2467aec1d6eScindi nvlist_lookup_string(args, "DRIVER", &driver) != 0) { 2477aec1d6eScindi topo_mod_dprintf(mp, "no DRIVER string in method argument\n"); 2487aec1d6eScindi return (topo_mod_seterrno(mp, EMOD_METHOD_INVAL)); 2497aec1d6eScindi } 2507aec1d6eScindi 2517aec1d6eScindi modnvl = mod_fmri_create(mp, driver); 2527aec1d6eScindi if (modnvl == NULL) { 2537aec1d6eScindi *out = NULL; 2547aec1d6eScindi topo_mod_dprintf(mp, "failed to create contained mod FMRI\n"); 2559dd0f810Scindi return (-1); 2567aec1d6eScindi } 2577aec1d6eScindi *out = modnvl; 2587aec1d6eScindi return (0); 2597aec1d6eScindi } 2609dd0f810Scindi 2619dd0f810Scindi #define MAXINTSTR 11 2629dd0f810Scindi 2639dd0f810Scindi static ssize_t 2649dd0f810Scindi fmri_nvl2str(nvlist_t *nvl, char *buf, size_t buflen) 2659dd0f810Scindi { 2669dd0f810Scindi nvlist_t *anvl = NULL; 2679c94f155SCheng Sean Ye nvpair_t *apair; 2689dd0f810Scindi uint8_t version; 2699dd0f810Scindi ssize_t size = 0; 2709dd0f810Scindi int32_t modid; 2719c94f155SCheng Sean Ye char *modname = NULL, *aname, *aval; 2729dd0f810Scindi char numbuf[MAXINTSTR]; 2739dd0f810Scindi int err; 2749dd0f810Scindi 2759dd0f810Scindi if (nvlist_lookup_uint8(nvl, FM_VERSION, &version) != 0 || 2769dd0f810Scindi version > FM_MOD_SCHEME_VERSION) 2779dd0f810Scindi return (-1); 2789dd0f810Scindi 2799dd0f810Scindi /* Get authority, if present */ 2809dd0f810Scindi err = nvlist_lookup_nvlist(nvl, FM_FMRI_AUTHORITY, &anvl); 2819dd0f810Scindi if (err != 0 && err != ENOENT) 2829dd0f810Scindi return (-1); 2839dd0f810Scindi 2849dd0f810Scindi /* 2859dd0f810Scindi * For brevity, we only include the module name and id 2869dd0f810Scindi * present in the FMRI in our output string. The FMRI 2879dd0f810Scindi * also has data on the package containing the module. 2889dd0f810Scindi */ 2899dd0f810Scindi 2909dd0f810Scindi /* There must be a module name */ 2919dd0f810Scindi err = nvlist_lookup_string(nvl, FM_FMRI_MOD_NAME, &modname); 2929dd0f810Scindi if (err != 0 || modname == NULL) 2939dd0f810Scindi return (-1); 2949dd0f810Scindi 2959dd0f810Scindi /* There must be a module id */ 2969dd0f810Scindi err = nvlist_lookup_int32(nvl, FM_FMRI_MOD_ID, &modid); 2979dd0f810Scindi if (err != 0) 2989dd0f810Scindi return (-1); 2999dd0f810Scindi 3009dd0f810Scindi /* mod:// */ 3019dd0f810Scindi topo_fmristr_build(&size, buf, buflen, FM_FMRI_SCHEME_MOD, NULL, "://"); 3029dd0f810Scindi 3039dd0f810Scindi /* authority, if any */ 3049c94f155SCheng Sean Ye if (anvl != NULL) { 3059c94f155SCheng Sean Ye for (apair = nvlist_next_nvpair(anvl, NULL); 3069c94f155SCheng Sean Ye apair != NULL; apair = nvlist_next_nvpair(anvl, apair)) { 3079c94f155SCheng Sean Ye if (nvpair_type(apair) != DATA_TYPE_STRING || 3089c94f155SCheng Sean Ye nvpair_value_string(apair, &aval) != 0) 3099c94f155SCheng Sean Ye continue; 3109c94f155SCheng Sean Ye aname = nvpair_name(apair); 3119c94f155SCheng Sean Ye topo_fmristr_build(&size, buf, buflen, ":", NULL, NULL); 3129c94f155SCheng Sean Ye topo_fmristr_build(&size, buf, buflen, "=", 3139c94f155SCheng Sean Ye aname, aval); 3149c94f155SCheng Sean Ye } 3159c94f155SCheng Sean Ye } 3169dd0f810Scindi 3179dd0f810Scindi /* module parts */ 3189dd0f810Scindi topo_fmristr_build(&size, buf, buflen, modname, 3199dd0f810Scindi "/" FM_FMRI_MOD_NAME "=", "/"); 3209dd0f810Scindi 3219dd0f810Scindi (void) snprintf(numbuf, MAXINTSTR, "%d", modid); 3229dd0f810Scindi topo_fmristr_build(&size, buf, buflen, numbuf, FM_FMRI_MOD_ID "=", 3239dd0f810Scindi NULL); 3249dd0f810Scindi 3259dd0f810Scindi return (size); 3269dd0f810Scindi } 3279dd0f810Scindi 3289dd0f810Scindi /*ARGSUSED*/ 3299dd0f810Scindi static int 3309dd0f810Scindi mod_fmri_nvl2str(topo_mod_t *mod, tnode_t *node, topo_version_t version, 3319dd0f810Scindi nvlist_t *nvl, nvlist_t **out) 3329dd0f810Scindi { 3339dd0f810Scindi ssize_t len; 3349dd0f810Scindi char *name = NULL; 3359dd0f810Scindi nvlist_t *fmristr; 3369dd0f810Scindi 3379dd0f810Scindi if (version > TOPO_METH_NVL2STR_VERSION) 3389dd0f810Scindi return (topo_mod_seterrno(mod, EMOD_VER_NEW)); 3399dd0f810Scindi 3409dd0f810Scindi if ((len = fmri_nvl2str(nvl, NULL, 0)) == 0 || 3419dd0f810Scindi (name = topo_mod_alloc(mod, len + 1)) == NULL || 3429dd0f810Scindi fmri_nvl2str(nvl, name, len + 1) == 0) { 3439dd0f810Scindi if (name != NULL) 3449dd0f810Scindi topo_mod_free(mod, name, len + 1); 3459dd0f810Scindi return (topo_mod_seterrno(mod, EMOD_FMRI_NVL)); 3469dd0f810Scindi } 3479dd0f810Scindi 3489dd0f810Scindi if (topo_mod_nvalloc(mod, &fmristr, NV_UNIQUE_NAME) != 0) 3499dd0f810Scindi return (topo_mod_seterrno(mod, EMOD_FMRI_NVL)); 3509dd0f810Scindi if (nvlist_add_string(fmristr, "fmri-string", name) != 0) { 3519dd0f810Scindi topo_mod_free(mod, name, len + 1); 3529dd0f810Scindi nvlist_free(fmristr); 3539dd0f810Scindi return (topo_mod_seterrno(mod, EMOD_FMRI_NVL)); 3549dd0f810Scindi } 3559dd0f810Scindi topo_mod_free(mod, name, len + 1); 3569dd0f810Scindi *out = fmristr; 3579dd0f810Scindi 3589dd0f810Scindi return (0); 3599dd0f810Scindi } 360