1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 22 /* 23 * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #include <limits.h> 28 #include <strings.h> 29 #include <unistd.h> 30 #include <libnvpair.h> 31 #include <fm/topo_mod.h> 32 #include <sys/fm/protocol.h> 33 34 #include <fcntl.h> 35 #include <sys/types.h> 36 #include <sys/stat.h> 37 #include <sys/objfs.h> 38 #include <sys/modctl.h> 39 #include <libelf.h> 40 #include <gelf.h> 41 42 #include <topo_method.h> 43 #include <topo_subr.h> 44 #include <mod.h> 45 46 static int mod_enum(topo_mod_t *, tnode_t *, const char *, topo_instance_t, 47 topo_instance_t, void *, void *); 48 static void mod_release(topo_mod_t *, tnode_t *); 49 static int mod_fmri_create_meth(topo_mod_t *, tnode_t *, topo_version_t, 50 nvlist_t *, nvlist_t **); 51 static int mod_fmri_nvl2str(topo_mod_t *, tnode_t *, topo_version_t, 52 nvlist_t *, nvlist_t **); 53 54 static const topo_method_t mod_methods[] = { 55 { TOPO_METH_FMRI, TOPO_METH_FMRI_DESC, TOPO_METH_FMRI_VERSION, 56 TOPO_STABILITY_INTERNAL, mod_fmri_create_meth }, 57 { TOPO_METH_NVL2STR, TOPO_METH_NVL2STR_DESC, TOPO_METH_NVL2STR_VERSION, 58 TOPO_STABILITY_INTERNAL, mod_fmri_nvl2str }, 59 { NULL } 60 }; 61 62 static const topo_modops_t mod_modops = 63 { mod_enum, mod_release }; 64 static const topo_modinfo_t mod_info = 65 { "mod", FM_FMRI_SCHEME_MOD, MOD_VERSION, &mod_modops }; 66 67 int 68 mod_init(topo_mod_t *mod, topo_version_t version) 69 { 70 if (getenv("TOPOMODDEBUG")) 71 topo_mod_setdebug(mod); 72 topo_mod_dprintf(mod, "initializing mod builtin\n"); 73 74 if (version != MOD_VERSION) 75 return (topo_mod_seterrno(mod, EMOD_VER_NEW)); 76 77 if (topo_mod_register(mod, &mod_info, TOPO_VERSION) != 0) { 78 topo_mod_dprintf(mod, "failed to register mod_info: " 79 "%s\n", topo_mod_errmsg(mod)); 80 return (-1); /* mod errno already set */ 81 } 82 83 return (0); 84 } 85 86 void 87 mod_fini(topo_mod_t *mod) 88 { 89 topo_mod_unregister(mod); 90 } 91 92 /*ARGSUSED*/ 93 static int 94 mod_enum(topo_mod_t *mod, tnode_t *pnode, const char *name, 95 topo_instance_t min, topo_instance_t max, void *notused1, void *notused2) 96 { 97 (void) topo_method_register(mod, pnode, mod_methods); 98 return (0); 99 } 100 101 static void 102 mod_release(topo_mod_t *mod, tnode_t *node) 103 { 104 topo_method_unregister_all(mod, node); 105 } 106 107 static int 108 mod_binary_path_get(topo_mod_t *mp, const char *objpath) 109 { 110 Elf *elf = NULL; 111 Elf_Scn *scn = NULL; 112 GElf_Ehdr ehdr; 113 GElf_Shdr shdr; 114 int fd; 115 116 if ((fd = open(objpath, O_RDONLY)) < 0) { 117 topo_mod_dprintf(mp, "unable to open %s\n", objpath); 118 return (-1); 119 } 120 121 if (elf_version(EV_CURRENT) == EV_NONE) { 122 topo_mod_dprintf(mp, "Elf version out of whack\n"); 123 goto mbpg_bail; 124 } 125 if ((elf = elf_begin(fd, ELF_C_READ, NULL)) == NULL) { 126 topo_mod_dprintf(mp, "elf_begin failed\n"); 127 goto mbpg_bail; 128 } 129 if ((gelf_getehdr(elf, &ehdr)) == NULL) { 130 topo_mod_dprintf(mp, "gelf_getehdr failed\n"); 131 goto mbpg_bail; 132 } 133 scn = elf_getscn(elf, 0); /* "seek" to start of sections */ 134 while ((scn = elf_nextscn(elf, scn)) != NULL) { 135 const char *sh_name; 136 if (gelf_getshdr(scn, &shdr) == NULL) { 137 topo_mod_dprintf(mp, "gelf_getshdr failed\n"); 138 goto mbpg_bail; 139 } 140 if (shdr.sh_type != SHT_PROGBITS) 141 continue; 142 sh_name = elf_strptr(elf, 143 ehdr.e_shstrndx, (size_t)shdr.sh_name); 144 if (strcmp(sh_name, ".filename") != 0) 145 continue; 146 if (elf_getdata(scn, NULL) == NULL) { 147 topo_mod_dprintf(mp, "no filename data"); 148 break; 149 } 150 break; 151 } 152 elf_end(elf); 153 (void) close(fd); 154 return (0); 155 156 mbpg_bail: 157 if (elf != NULL) 158 elf_end(elf); 159 if (fd >= 0) 160 (void) close(fd); 161 (void) topo_mod_seterrno(mp, EMOD_METHOD_INVAL); 162 return (-1); 163 } 164 165 static int 166 mod_nvl_data(topo_mod_t *mp, nvlist_t *out, const char *path) 167 { 168 struct modinfo mi; 169 struct stat64 s; 170 int id, e; 171 172 if (stat64(path, &s) < 0) { 173 topo_mod_dprintf(mp, 174 "No system object file for driver %s", path); 175 return (topo_mod_seterrno(mp, EMOD_METHOD_INVAL)); 176 } 177 178 id = OBJFS_MODID(s.st_ino); 179 mi.mi_id = mi.mi_nextid = id; 180 mi.mi_info = MI_INFO_ONE | MI_INFO_NOBASE; 181 if (modctl(MODINFO, id, &mi) < 0) { 182 return (topo_mod_seterrno(mp, EMOD_METHOD_INVAL)); 183 } 184 mi.mi_name[MODMAXNAMELEN - 1] = '\0'; 185 mi.mi_msinfo[0].msi_linkinfo[MODMAXNAMELEN - 1] = '\0'; 186 e = nvlist_add_string(out, FM_FMRI_SCHEME, FM_FMRI_SCHEME_MOD); 187 e |= nvlist_add_uint8(out, FM_VERSION, FM_MOD_SCHEME_VERSION); 188 e |= nvlist_add_int32(out, FM_FMRI_MOD_ID, id); 189 e |= nvlist_add_string(out, FM_FMRI_MOD_NAME, mi.mi_name); 190 e |= nvlist_add_string(out, 191 FM_FMRI_MOD_DESC, mi.mi_msinfo[0].msi_linkinfo); 192 if (e != 0) 193 return (topo_mod_seterrno(mp, EMOD_FMRI_NVL)); 194 195 return (0); 196 } 197 198 static nvlist_t * 199 mod_fmri_create(topo_mod_t *mp, const char *driver) 200 { 201 nvlist_t *out = NULL; 202 char objpath[PATH_MAX]; 203 204 if (topo_mod_nvalloc(mp, &out, NV_UNIQUE_NAME) != 0) { 205 (void) topo_mod_seterrno(mp, EMOD_FMRI_NVL); 206 goto mfc_bail; 207 } 208 209 (void) snprintf(objpath, PATH_MAX, "%s/%s/object", OBJFS_ROOT, driver); 210 211 /* 212 * Validate the module object ELF header if possible 213 */ 214 if (mod_binary_path_get(mp, objpath) < 0) 215 goto mfc_bail; 216 217 if (mod_nvl_data(mp, out, objpath) < 0) { 218 topo_mod_dprintf(mp, "failed to get modinfo for %s", driver); 219 goto mfc_bail; 220 } 221 222 return (out); 223 224 mfc_bail: 225 nvlist_free(out); 226 return (NULL); 227 } 228 229 /*ARGSUSED*/ 230 static int 231 mod_fmri_create_meth(topo_mod_t *mp, tnode_t *node, topo_version_t version, 232 nvlist_t *in, nvlist_t **out) 233 { 234 nvlist_t *args; 235 nvlist_t *modnvl; 236 char *driver; 237 238 if (version > TOPO_METH_FMRI_VERSION) 239 return (topo_mod_seterrno(mp, EMOD_VER_NEW)); 240 241 if (nvlist_lookup_nvlist(in, TOPO_METH_FMRI_ARG_NVL, &args) != 0 || 242 nvlist_lookup_string(args, "DRIVER", &driver) != 0) { 243 topo_mod_dprintf(mp, "no DRIVER string in method argument\n"); 244 return (topo_mod_seterrno(mp, EMOD_METHOD_INVAL)); 245 } 246 247 modnvl = mod_fmri_create(mp, driver); 248 if (modnvl == NULL) { 249 *out = NULL; 250 topo_mod_dprintf(mp, "failed to create contained mod FMRI\n"); 251 return (-1); 252 } 253 *out = modnvl; 254 return (0); 255 } 256 257 #define MAXINTSTR 11 258 259 static ssize_t 260 fmri_nvl2str(nvlist_t *nvl, char *buf, size_t buflen) 261 { 262 nvlist_t *anvl = NULL; 263 nvpair_t *apair; 264 uint8_t version; 265 ssize_t size = 0; 266 int32_t modid; 267 char *modname = NULL, *aname, *aval; 268 char numbuf[MAXINTSTR]; 269 int err; 270 271 if (nvlist_lookup_uint8(nvl, FM_VERSION, &version) != 0 || 272 version > FM_MOD_SCHEME_VERSION) 273 return (-1); 274 275 /* Get authority, if present */ 276 err = nvlist_lookup_nvlist(nvl, FM_FMRI_AUTHORITY, &anvl); 277 if (err != 0 && err != ENOENT) 278 return (-1); 279 280 /* 281 * For brevity, we only include the module name and id 282 * present in the FMRI in our output string. The FMRI 283 * also has data on the package containing the module. 284 */ 285 286 /* There must be a module name */ 287 err = nvlist_lookup_string(nvl, FM_FMRI_MOD_NAME, &modname); 288 if (err != 0 || modname == NULL) 289 return (-1); 290 291 /* There must be a module id */ 292 err = nvlist_lookup_int32(nvl, FM_FMRI_MOD_ID, &modid); 293 if (err != 0) 294 return (-1); 295 296 /* mod:// */ 297 topo_fmristr_build(&size, buf, buflen, FM_FMRI_SCHEME_MOD, NULL, "://"); 298 299 /* authority, if any */ 300 if (anvl != NULL) { 301 for (apair = nvlist_next_nvpair(anvl, NULL); 302 apair != NULL; apair = nvlist_next_nvpair(anvl, apair)) { 303 if (nvpair_type(apair) != DATA_TYPE_STRING || 304 nvpair_value_string(apair, &aval) != 0) 305 continue; 306 aname = nvpair_name(apair); 307 topo_fmristr_build(&size, buf, buflen, ":", NULL, NULL); 308 topo_fmristr_build(&size, buf, buflen, "=", 309 aname, aval); 310 } 311 } 312 313 /* module parts */ 314 topo_fmristr_build(&size, buf, buflen, modname, 315 "/" FM_FMRI_MOD_NAME "=", "/"); 316 317 (void) snprintf(numbuf, MAXINTSTR, "%d", modid); 318 topo_fmristr_build(&size, buf, buflen, numbuf, FM_FMRI_MOD_ID "=", 319 NULL); 320 321 return (size); 322 } 323 324 /*ARGSUSED*/ 325 static int 326 mod_fmri_nvl2str(topo_mod_t *mod, tnode_t *node, topo_version_t version, 327 nvlist_t *nvl, nvlist_t **out) 328 { 329 ssize_t len; 330 char *name = NULL; 331 nvlist_t *fmristr; 332 333 if (version > TOPO_METH_NVL2STR_VERSION) 334 return (topo_mod_seterrno(mod, EMOD_VER_NEW)); 335 336 if ((len = fmri_nvl2str(nvl, NULL, 0)) == 0 || 337 (name = topo_mod_alloc(mod, len + 1)) == NULL || 338 fmri_nvl2str(nvl, name, len + 1) == 0) { 339 if (name != NULL) 340 topo_mod_free(mod, name, len + 1); 341 return (topo_mod_seterrno(mod, EMOD_FMRI_NVL)); 342 } 343 344 if (topo_mod_nvalloc(mod, &fmristr, NV_UNIQUE_NAME) != 0) 345 return (topo_mod_seterrno(mod, EMOD_FMRI_NVL)); 346 if (nvlist_add_string(fmristr, "fmri-string", name) != 0) { 347 topo_mod_free(mod, name, len + 1); 348 nvlist_free(fmristr); 349 return (topo_mod_seterrno(mod, EMOD_FMRI_NVL)); 350 } 351 topo_mod_free(mod, name, len + 1); 352 *out = fmristr; 353 354 return (0); 355 } 356