1*1414Scindi /* 2*1414Scindi * CDDL HEADER START 3*1414Scindi * 4*1414Scindi * The contents of this file are subject to the terms of the 5*1414Scindi * Common Development and Distribution License (the "License"). 6*1414Scindi * You may not use this file except in compliance with the License. 7*1414Scindi * 8*1414Scindi * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9*1414Scindi * or http://www.opensolaris.org/os/licensing. 10*1414Scindi * See the License for the specific language governing permissions 11*1414Scindi * and limitations under the License. 12*1414Scindi * 13*1414Scindi * When distributing Covered Code, include this CDDL HEADER in each 14*1414Scindi * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15*1414Scindi * If applicable, add the following below this CDDL HEADER, with the 16*1414Scindi * fields enclosed by brackets "[]" replaced with your own identifying 17*1414Scindi * information: Portions Copyright [yyyy] [name of copyright owner] 18*1414Scindi * 19*1414Scindi * CDDL HEADER END 20*1414Scindi */ 21*1414Scindi 22*1414Scindi /* 23*1414Scindi * Copyright 2006 Sun Microsystems, Inc. All rights reserved. 24*1414Scindi * Use is subject to license terms. 25*1414Scindi */ 26*1414Scindi 27*1414Scindi #pragma ident "%Z%%M% %I% %E% SMI" 28*1414Scindi 29*1414Scindi #include <limits.h> 30*1414Scindi #include <strings.h> 31*1414Scindi #include <unistd.h> 32*1414Scindi #include <libnvpair.h> 33*1414Scindi #include <fm/topo_mod.h> 34*1414Scindi #include <sys/fm/protocol.h> 35*1414Scindi 36*1414Scindi #include <fcntl.h> 37*1414Scindi #include <sys/types.h> 38*1414Scindi #include <sys/stat.h> 39*1414Scindi #include <sys/objfs.h> 40*1414Scindi #include <sys/modctl.h> 41*1414Scindi #include <libelf.h> 42*1414Scindi #include <gelf.h> 43*1414Scindi 44*1414Scindi #include <topo_error.h> 45*1414Scindi 46*1414Scindi static int mod_enum(topo_mod_t *, tnode_t *, const char *, topo_instance_t, 47*1414Scindi topo_instance_t, void *); 48*1414Scindi static void mod_release(topo_mod_t *, tnode_t *); 49*1414Scindi static int mod_fmri_create_meth(topo_mod_t *, tnode_t *, topo_version_t, 50*1414Scindi nvlist_t *, nvlist_t **); 51*1414Scindi 52*1414Scindi #define MOD_VERSION TOPO_VERSION 53*1414Scindi 54*1414Scindi static const topo_method_t mod_methods[] = { 55*1414Scindi { TOPO_METH_FMRI, TOPO_METH_FMRI_DESC, TOPO_METH_FMRI_VERSION, 56*1414Scindi TOPO_STABILITY_INTERNAL, mod_fmri_create_meth }, 57*1414Scindi { NULL } 58*1414Scindi }; 59*1414Scindi 60*1414Scindi static const topo_modinfo_t mod_info = 61*1414Scindi { "mod", MOD_VERSION, mod_enum, mod_release }; 62*1414Scindi 63*1414Scindi void 64*1414Scindi mod_init(topo_mod_t *mod) 65*1414Scindi { 66*1414Scindi topo_mod_setdebug(mod, TOPO_DBG_ALL); 67*1414Scindi topo_mod_dprintf(mod, "initializing mod builtin\n"); 68*1414Scindi 69*1414Scindi if (topo_mod_register(mod, &mod_info, NULL) != 0) { 70*1414Scindi topo_mod_dprintf(mod, "failed to register mod_info: " 71*1414Scindi "%s\n", topo_mod_errmsg(mod)); 72*1414Scindi return; 73*1414Scindi } 74*1414Scindi } 75*1414Scindi 76*1414Scindi void 77*1414Scindi mod_fini(topo_mod_t *mod) 78*1414Scindi { 79*1414Scindi topo_mod_unregister(mod); 80*1414Scindi } 81*1414Scindi 82*1414Scindi /*ARGSUSED*/ 83*1414Scindi static int 84*1414Scindi mod_enum(topo_mod_t *mod, tnode_t *pnode, const char *name, 85*1414Scindi topo_instance_t min, topo_instance_t max, void *arg) 86*1414Scindi { 87*1414Scindi (void) topo_method_register(mod, pnode, mod_methods); 88*1414Scindi return (0); 89*1414Scindi } 90*1414Scindi 91*1414Scindi static void 92*1414Scindi mod_release(topo_mod_t *mod, tnode_t *node) 93*1414Scindi { 94*1414Scindi topo_method_unregister_all(mod, node); 95*1414Scindi } 96*1414Scindi 97*1414Scindi static char * 98*1414Scindi mod_binary_path_get(topo_mod_t *mp, char *objpath) 99*1414Scindi { 100*1414Scindi static char Pathbuf[PATH_MAX]; 101*1414Scindi Elf *elf = NULL; 102*1414Scindi Elf_Scn *scn = NULL; 103*1414Scindi Elf_Data *edata; 104*1414Scindi GElf_Ehdr ehdr; 105*1414Scindi GElf_Shdr shdr; 106*1414Scindi int fd; 107*1414Scindi 108*1414Scindi if ((fd = open(objpath, O_RDONLY)) < 0) { 109*1414Scindi topo_mod_dprintf(mp, "failed to open %s", objpath); 110*1414Scindi goto mbpg_bail; 111*1414Scindi } 112*1414Scindi if (elf_version(EV_CURRENT) == EV_NONE) { 113*1414Scindi topo_mod_dprintf(mp, "Elf version out of whack\n"); 114*1414Scindi goto mbpg_bail; 115*1414Scindi } 116*1414Scindi if ((elf = elf_begin(fd, ELF_C_READ, NULL)) == NULL) { 117*1414Scindi topo_mod_dprintf(mp, "elf_begin failed\n"); 118*1414Scindi goto mbpg_bail; 119*1414Scindi } 120*1414Scindi if ((gelf_getehdr(elf, &ehdr)) == NULL) { 121*1414Scindi topo_mod_dprintf(mp, "gelf_getehdr failed\n"); 122*1414Scindi goto mbpg_bail; 123*1414Scindi } 124*1414Scindi scn = elf_getscn(elf, 0); /* "seek" to start of sections */ 125*1414Scindi while ((scn = elf_nextscn(elf, scn)) != NULL) { 126*1414Scindi const char *sh_name; 127*1414Scindi if (gelf_getshdr(scn, &shdr) == NULL) { 128*1414Scindi topo_mod_dprintf(mp, "gelf_getshdr failed\n"); 129*1414Scindi goto mbpg_bail; 130*1414Scindi } 131*1414Scindi if (shdr.sh_type != SHT_PROGBITS) 132*1414Scindi continue; 133*1414Scindi sh_name = elf_strptr(elf, 134*1414Scindi ehdr.e_shstrndx, (size_t)shdr.sh_name); 135*1414Scindi if (strcmp(sh_name, ".filename") != 0) 136*1414Scindi continue; 137*1414Scindi if ((edata = elf_getdata(scn, NULL)) == NULL) { 138*1414Scindi topo_mod_dprintf(mp, "no filename data"); 139*1414Scindi break; 140*1414Scindi } 141*1414Scindi (void) strlcpy(Pathbuf, edata->d_buf, PATH_MAX); 142*1414Scindi break; 143*1414Scindi } 144*1414Scindi elf_end(elf); 145*1414Scindi (void) close(fd); 146*1414Scindi return (Pathbuf); 147*1414Scindi 148*1414Scindi mbpg_bail: 149*1414Scindi if (elf != NULL) 150*1414Scindi elf_end(elf); 151*1414Scindi if (fd >= 0) 152*1414Scindi (void) close(fd); 153*1414Scindi (void) topo_mod_seterrno(mp, EMOD_METHOD_INVAL); 154*1414Scindi return (NULL); 155*1414Scindi } 156*1414Scindi 157*1414Scindi static int 158*1414Scindi mod_nvl_data(topo_mod_t *mp, nvlist_t *out, const char *path) 159*1414Scindi { 160*1414Scindi struct modinfo mi; 161*1414Scindi struct stat64 s; 162*1414Scindi int id, e; 163*1414Scindi 164*1414Scindi if (stat64(path, &s) < 0) { 165*1414Scindi topo_mod_dprintf(mp, 166*1414Scindi "No system object file for driver %s", path); 167*1414Scindi return (topo_mod_seterrno(mp, EMOD_METHOD_INVAL)); 168*1414Scindi } 169*1414Scindi 170*1414Scindi id = OBJFS_MODID(s.st_ino); 171*1414Scindi mi.mi_id = mi.mi_nextid = id; 172*1414Scindi mi.mi_info = MI_INFO_ONE | MI_INFO_NOBASE; 173*1414Scindi if (modctl(MODINFO, id, &mi) < 0) { 174*1414Scindi topo_mod_dprintf(mp, "failed to get modinfo for %s", path); 175*1414Scindi return (topo_mod_seterrno(mp, EMOD_METHOD_INVAL)); 176*1414Scindi } 177*1414Scindi mi.mi_name[MODMAXNAMELEN - 1] = '\0'; 178*1414Scindi mi.mi_msinfo[0].msi_linkinfo[MODMAXNAMELEN - 1] = '\0'; 179*1414Scindi e = nvlist_add_string(out, FM_FMRI_SCHEME, FM_FMRI_SCHEME_MOD); 180*1414Scindi e |= nvlist_add_uint8(out, FM_VERSION, FM_MOD_SCHEME_VERSION); 181*1414Scindi e |= nvlist_add_int32(out, FM_FMRI_MOD_ID, id); 182*1414Scindi e |= nvlist_add_string(out, FM_FMRI_MOD_NAME, mi.mi_name); 183*1414Scindi e |= nvlist_add_string(out, 184*1414Scindi FM_FMRI_MOD_DESC, mi.mi_msinfo[0].msi_linkinfo); 185*1414Scindi if (e != 0) 186*1414Scindi return (topo_mod_seterrno(mp, EMOD_FMRI_NVL)); 187*1414Scindi 188*1414Scindi return (0); 189*1414Scindi } 190*1414Scindi 191*1414Scindi static nvlist_t * 192*1414Scindi mod_fmri_create(topo_mod_t *mp, const char *driver) 193*1414Scindi { 194*1414Scindi topo_hdl_t *thp; 195*1414Scindi nvlist_t *arg = NULL; 196*1414Scindi nvlist_t *out = NULL; 197*1414Scindi nvlist_t *pkg = NULL; 198*1414Scindi char objpath[PATH_MAX]; 199*1414Scindi char *path = NULL; 200*1414Scindi int err; 201*1414Scindi 202*1414Scindi if (topo_mod_nvalloc(mp, &arg, NV_UNIQUE_NAME) != 0 || 203*1414Scindi topo_mod_nvalloc(mp, &out, NV_UNIQUE_NAME) != 0) { 204*1414Scindi (void) topo_mod_seterrno(mp, EMOD_FMRI_NVL); 205*1414Scindi goto mfc_bail; 206*1414Scindi } 207*1414Scindi 208*1414Scindi (void) snprintf(objpath, PATH_MAX, "%s/%s/object", OBJFS_ROOT, driver); 209*1414Scindi 210*1414Scindi if ((path = mod_binary_path_get(mp, objpath)) == NULL) 211*1414Scindi goto mfc_bail; 212*1414Scindi if (nvlist_add_string(arg, "path", path) != 0) { 213*1414Scindi (void) topo_mod_seterrno(mp, EMOD_FMRI_NVL); 214*1414Scindi goto mfc_bail; 215*1414Scindi } 216*1414Scindi 217*1414Scindi if (mod_nvl_data(mp, out, objpath) < 0) 218*1414Scindi goto mfc_bail; 219*1414Scindi 220*1414Scindi thp = topo_mod_handle(mp); 221*1414Scindi pkg = topo_fmri_create(thp, 222*1414Scindi FM_FMRI_SCHEME_PKG, FM_FMRI_SCHEME_PKG, 0, arg, &err); 223*1414Scindi if (pkg == NULL) { 224*1414Scindi (void) topo_mod_seterrno(mp, err); 225*1414Scindi goto mfc_bail; 226*1414Scindi } 227*1414Scindi nvlist_free(arg); 228*1414Scindi arg = NULL; 229*1414Scindi 230*1414Scindi if (nvlist_add_nvlist(out, FM_FMRI_MOD_PKG, pkg) != 0) { 231*1414Scindi (void) topo_mod_seterrno(mp, EMOD_FMRI_NVL); 232*1414Scindi goto mfc_bail; 233*1414Scindi } 234*1414Scindi nvlist_free(pkg); 235*1414Scindi 236*1414Scindi return (out); 237*1414Scindi 238*1414Scindi mfc_bail: 239*1414Scindi nvlist_free(pkg); 240*1414Scindi nvlist_free(out); 241*1414Scindi nvlist_free(arg); 242*1414Scindi return (NULL); 243*1414Scindi } 244*1414Scindi 245*1414Scindi /*ARGSUSED*/ 246*1414Scindi static int 247*1414Scindi mod_fmri_create_meth(topo_mod_t *mp, tnode_t *node, topo_version_t version, 248*1414Scindi nvlist_t *in, nvlist_t **out) 249*1414Scindi { 250*1414Scindi nvlist_t *args; 251*1414Scindi nvlist_t *modnvl; 252*1414Scindi char *driver; 253*1414Scindi 254*1414Scindi if (version > TOPO_METH_FMRI_VERSION) 255*1414Scindi return (topo_mod_seterrno(mp, EMOD_VER_NEW)); 256*1414Scindi 257*1414Scindi if (nvlist_lookup_nvlist(in, TOPO_METH_FMRI_ARG_NVL, &args) != 0 || 258*1414Scindi nvlist_lookup_string(args, "DRIVER", &driver) != 0) { 259*1414Scindi topo_mod_dprintf(mp, "no DRIVER string in method argument\n"); 260*1414Scindi return (topo_mod_seterrno(mp, EMOD_METHOD_INVAL)); 261*1414Scindi } 262*1414Scindi 263*1414Scindi modnvl = mod_fmri_create(mp, driver); 264*1414Scindi if (modnvl == NULL) { 265*1414Scindi *out = NULL; 266*1414Scindi topo_mod_dprintf(mp, "failed to create contained mod FMRI\n"); 267*1414Scindi return (topo_mod_seterrno(mp, EMOD_FMRI_NVL)); 268*1414Scindi } 269*1414Scindi *out = modnvl; 270*1414Scindi return (0); 271*1414Scindi } 272