138465Smsmith/*- 238465Smsmith * Copyright (c) 1998 Michael Smith <msmith@freebsd.org> 338465Smsmith * All rights reserved. 438465Smsmith * 538465Smsmith * Redistribution and use in source and binary forms, with or without 638465Smsmith * modification, are permitted provided that the following conditions 738465Smsmith * are met: 838465Smsmith * 1. Redistributions of source code must retain the above copyright 938465Smsmith * notice, this list of conditions and the following disclaimer. 1038465Smsmith * 2. Redistributions in binary form must reproduce the above copyright 1138465Smsmith * notice, this list of conditions and the following disclaimer in the 1238465Smsmith * documentation and/or other materials provided with the distribution. 1338465Smsmith * 1438465Smsmith * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1538465Smsmith * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1638465Smsmith * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1738465Smsmith * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 1838465Smsmith * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 1938465Smsmith * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2038465Smsmith * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2138465Smsmith * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2238465Smsmith * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2338465Smsmith * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2438465Smsmith * SUCH DAMAGE. 2538465Smsmith */ 2638465Smsmith 27119483Sobrien#include <sys/cdefs.h> 28119483Sobrien__FBSDID("$FreeBSD: stable/11/stand/common/module.c 329010 2018-02-08 02:44:21Z kevans $"); 29119483Sobrien 3038465Smsmith/* 3159854Sbp * file/module function dispatcher, support, etc. 3238465Smsmith */ 3338465Smsmith 3438465Smsmith#include <stand.h> 3538465Smsmith#include <string.h> 3640141Speter#include <sys/param.h> 3740141Speter#include <sys/linker.h> 3883321Speter#include <sys/module.h> 3983321Speter#include <sys/queue.h> 40269614Smarcel#include <sys/stdint.h> 4138465Smsmith 4238465Smsmith#include "bootstrap.h" 4338465Smsmith 4483321Speter#define MDIR_REMOVED 0x0001 4583321Speter#define MDIR_NOHINTS 0x0002 4683321Speter 4783321Speterstruct moduledir { 4883321Speter char *d_path; /* path of modules directory */ 4983321Speter u_char *d_hints; /* content of linker.hints file */ 5083321Speter int d_hintsz; /* size of hints data */ 5183321Speter int d_flags; 5283321Speter STAILQ_ENTRY(moduledir) d_link; 5383321Speter}; 5483321Speter 5559854Sbpstatic int file_load(char *filename, vm_offset_t dest, struct preloaded_file **result); 5686469Siedowsestatic int file_load_dependencies(struct preloaded_file *base_mod); 5783321Speterstatic char * file_search(const char *name, char **extlist); 5883321Speterstatic struct kernel_module * file_findmodule(struct preloaded_file *fp, char *modname, struct mod_depend *verinfo); 5983321Speterstatic int file_havepath(const char *name); 6083321Speterstatic char *mod_searchmodule(char *name, struct mod_depend *verinfo); 6159854Sbpstatic void file_insert_tail(struct preloaded_file *mp); 6259854Sbpstruct file_metadata* metadata_next(struct file_metadata *base_mp, int type); 6383321Speterstatic void moduledir_readhints(struct moduledir *mdp); 6483321Speterstatic void moduledir_rebuild(void); 6538764Smsmith 6639178Smsmith/* load address should be tweaked by first module loaded (kernel) */ 6738465Smsmithstatic vm_offset_t loadaddr = 0; 6838465Smsmith 69271054Sian#if defined(LOADER_FDT_SUPPORT) 70271054Sianstatic const char *default_searchpath = 71271054Sian "/boot/kernel;/boot/modules;/boot/dtb"; 72271054Sian#else 73111852Srustatic const char *default_searchpath ="/boot/kernel;/boot/modules"; 74271054Sian#endif 7539178Smsmith 7683321Speterstatic STAILQ_HEAD(, moduledir) moduledir_list = STAILQ_HEAD_INITIALIZER(moduledir_list); 7783321Speter 7859854Sbpstruct preloaded_file *preloaded_files = NULL; 7938465Smsmith 8083321Speterstatic char *kld_ext_list[] = { 8183321Speter ".ko", 8283321Speter "", 83172445Sobrien ".debug", 8483321Speter NULL 8583321Speter}; 8683321Speter 8783321Speter 8838764Smsmith/* 8938764Smsmith * load an object, either a disk file or code module. 9038764Smsmith * 9138764Smsmith * To load a file, the syntax is: 9238764Smsmith * 9338764Smsmith * load -t <type> <path> 9438764Smsmith * 9538764Smsmith * code modules are loaded as: 9638764Smsmith * 9738764Smsmith * load <path> <options> 9838764Smsmith */ 9938764Smsmith 10038465SmsmithCOMMAND_SET(load, "load", "load a kernel or module", command_load); 10138465Smsmith 10238465Smsmithstatic int 10338465Smsmithcommand_load(int argc, char *argv[]) 10438465Smsmith{ 105286234Strasz struct preloaded_file *fp; 10638764Smsmith char *typestr; 10783321Speter int dofile, dokld, ch, error; 108299499Spfg 10983321Speter dokld = dofile = 0; 11038764Smsmith optind = 1; 11142512Smsmith optreset = 1; 11238764Smsmith typestr = NULL; 11353993Sdcs if (argc == 1) { 11453993Sdcs command_errmsg = "no filename specified"; 115293835Ssmh return (CMD_CRIT); 11653993Sdcs } 11783321Speter while ((ch = getopt(argc, argv, "kt:")) != -1) { 11838764Smsmith switch(ch) { 11983321Speter case 'k': 12083321Speter dokld = 1; 12183321Speter break; 12238764Smsmith case 't': 12338764Smsmith typestr = optarg; 12438764Smsmith dofile = 1; 12538764Smsmith break; 12638764Smsmith case '?': 12738764Smsmith default: 12838764Smsmith /* getopt has already reported an error */ 129293835Ssmh return (CMD_OK); 13038764Smsmith } 13138764Smsmith } 13238764Smsmith argv += (optind - 1); 13338764Smsmith argc -= (optind - 1); 13438764Smsmith 13538764Smsmith /* 13638764Smsmith * Request to load a raw file? 13738764Smsmith */ 13838764Smsmith if (dofile) { 13977971Sjesper if ((argc != 2) || (typestr == NULL) || (*typestr == 0)) { 14038764Smsmith command_errmsg = "invalid load type"; 141293835Ssmh return (CMD_CRIT); 14238764Smsmith } 143286234Strasz 144286234Strasz fp = file_findfile(argv[1], typestr); 145286234Strasz if (fp) { 146329010Skevans snprintf(command_errbuf, sizeof(command_errbuf), 147329010Skevans "warning: file '%s' already loaded", argv[1]); 148293835Ssmh return (CMD_WARN); 149286234Strasz } 150286234Strasz 151293835Ssmh if (file_loadraw(argv[1], typestr, 1) != NULL) 152293835Ssmh return (CMD_OK); 153293835Ssmh 154293835Ssmh /* Failing to load mfs_root is never going to end well! */ 155293835Ssmh if (strcmp("mfs_root", typestr) == 0) 156293835Ssmh return (CMD_FATAL); 157293835Ssmh 158293835Ssmh return (CMD_ERROR); 15938764Smsmith } 16038764Smsmith /* 16183321Speter * Do we have explicit KLD load ? 16283321Speter */ 16383321Speter if (dokld || file_havepath(argv[1])) { 16483321Speter error = mod_loadkld(argv[1], argc - 2, argv + 2); 165293835Ssmh if (error == EEXIST) { 166329010Skevans snprintf(command_errbuf, sizeof(command_errbuf), 167329010Skevans "warning: KLD '%s' already loaded", argv[1]); 168293835Ssmh return (CMD_WARN); 169293835Ssmh } 170293835Ssmh 171293835Ssmh return (error == 0 ? CMD_OK : CMD_CRIT); 17283321Speter } 17383321Speter /* 17438764Smsmith * Looks like a request for a module. 17538764Smsmith */ 17683321Speter error = mod_load(argv[1], NULL, argc - 2, argv + 2); 177293835Ssmh if (error == EEXIST) { 178329010Skevans snprintf(command_errbuf, sizeof(command_errbuf), 179329010Skevans "warning: module '%s' already loaded", argv[1]); 180293835Ssmh return (CMD_WARN); 181293835Ssmh } 182293835Ssmh 183293835Ssmh return (error == 0 ? CMD_OK : CMD_CRIT); 18438465Smsmith} 18538465Smsmith 186321232Sngie#ifdef LOADER_GELI_SUPPORT 187188666SthompsaCOMMAND_SET(load_geli, "load_geli", "load a geli key", command_load_geli); 188188666Sthompsa 189188666Sthompsastatic int 190188666Sthompsacommand_load_geli(int argc, char *argv[]) 191188666Sthompsa{ 192188666Sthompsa char typestr[80]; 193188666Sthompsa char *cp; 194188666Sthompsa int ch, num; 195188666Sthompsa 196188666Sthompsa if (argc < 3) { 197188666Sthompsa command_errmsg = "usage is [-n key#] <prov> <file>"; 198188666Sthompsa return(CMD_ERROR); 199188666Sthompsa } 200188666Sthompsa 201188666Sthompsa num = 0; 202188666Sthompsa optind = 1; 203188666Sthompsa optreset = 1; 204188666Sthompsa while ((ch = getopt(argc, argv, "n:")) != -1) { 205188666Sthompsa switch(ch) { 206188666Sthompsa case 'n': 207188666Sthompsa num = strtol(optarg, &cp, 0); 208188666Sthompsa if (cp == optarg) { 209329010Skevans snprintf(command_errbuf, sizeof(command_errbuf), 210329010Skevans "bad key index '%s'", optarg); 211188666Sthompsa return(CMD_ERROR); 212188666Sthompsa } 213188666Sthompsa break; 214188666Sthompsa case '?': 215188666Sthompsa default: 216188666Sthompsa /* getopt has already reported an error */ 217188666Sthompsa return(CMD_OK); 218188666Sthompsa } 219188666Sthompsa } 220188666Sthompsa argv += (optind - 1); 221188666Sthompsa argc -= (optind - 1); 222188666Sthompsa sprintf(typestr, "%s:geli_keyfile%d", argv[1], num); 223277215Sroyger return (file_loadraw(argv[2], typestr, 1) ? CMD_OK : CMD_ERROR); 224188666Sthompsa} 225321232Sngie#endif 226188666Sthompsa 227269618Smarcelvoid 228269618Smarcelunload(void) 229269618Smarcel{ 230269618Smarcel struct preloaded_file *fp; 23138712Smsmith 23259854Sbp while (preloaded_files != NULL) { 23359854Sbp fp = preloaded_files; 23459854Sbp preloaded_files = preloaded_files->f_next; 23559854Sbp file_discard(fp); 23638712Smsmith } 23738712Smsmith loadaddr = 0; 23865613Sdcs unsetenv("kernelname"); 239269618Smarcel} 240269618Smarcel 241269618SmarcelCOMMAND_SET(unload, "unload", "unload all modules", command_unload); 242269618Smarcel 243269618Smarcelstatic int 244269618Smarcelcommand_unload(int argc, char *argv[]) 245269618Smarcel{ 246269618Smarcel unload(); 24738712Smsmith return(CMD_OK); 24838712Smsmith} 24938712Smsmith 25038465SmsmithCOMMAND_SET(lsmod, "lsmod", "list loaded modules", command_lsmod); 25138465Smsmith 25238465Smsmithstatic int 25338465Smsmithcommand_lsmod(int argc, char *argv[]) 25438465Smsmith{ 25559854Sbp struct preloaded_file *fp; 25659854Sbp struct kernel_module *mp; 25759854Sbp struct file_metadata *md; 25838465Smsmith char lbuf[80]; 259328889Skevans int ch, verbose, ret = 0; 26038764Smsmith 26138764Smsmith verbose = 0; 26238764Smsmith optind = 1; 26342512Smsmith optreset = 1; 26438764Smsmith while ((ch = getopt(argc, argv, "v")) != -1) { 26538764Smsmith switch(ch) { 26638764Smsmith case 'v': 26738764Smsmith verbose = 1; 26838764Smsmith break; 26938764Smsmith case '?': 27038764Smsmith default: 27138764Smsmith /* getopt has already reported an error */ 27238764Smsmith return(CMD_OK); 27338764Smsmith } 27438764Smsmith } 27538764Smsmith 27638465Smsmith pager_open(); 27759854Sbp for (fp = preloaded_files; fp; fp = fp->f_next) { 278328889Skevans snprintf(lbuf, sizeof(lbuf), " %p: ", (void *) fp->f_addr); 27938465Smsmith pager_output(lbuf); 280269616Smarcel pager_output(fp->f_name); 281328889Skevans snprintf(lbuf, sizeof(lbuf), " (%s, 0x%lx)\n", fp->f_type, 282328889Skevans (long)fp->f_size); 283328889Skevans if (pager_output(lbuf)) 284328889Skevans break; 28559854Sbp if (fp->f_args != NULL) { 28638465Smsmith pager_output(" args: "); 28759854Sbp pager_output(fp->f_args); 288300117Simp if (pager_output("\n")) 289300117Simp break; 29038465Smsmith } 29159854Sbp if (fp->f_modules) { 29259854Sbp pager_output(" modules: "); 29359854Sbp for (mp = fp->f_modules; mp; mp = mp->m_next) { 294328889Skevans snprintf(lbuf, sizeof(lbuf), "%s.%d ", mp->m_name, 295328889Skevans mp->m_version); 29659854Sbp pager_output(lbuf); 29759854Sbp } 298300117Simp if (pager_output("\n")) 299300117Simp break; 300300117Simp } 30159854Sbp if (verbose) { 30238764Smsmith /* XXX could add some formatting smarts here to display some better */ 30359854Sbp for (md = fp->f_metadata; md != NULL; md = md->md_next) { 304328889Skevans snprintf(lbuf, sizeof(lbuf), " 0x%04x, 0x%lx\n", 305328889Skevans md->md_type, (long) md->md_size); 306300117Simp if (pager_output(lbuf)) 307300117Simp break; 30838764Smsmith } 30959854Sbp } 310328889Skevans if (ret) 311328889Skevans break; 31238465Smsmith } 31338465Smsmith pager_close(); 31438465Smsmith return(CMD_OK); 31538465Smsmith} 31638465Smsmith 31738764Smsmith/* 31859854Sbp * File level interface, functions file_* 31938764Smsmith */ 32038465Smsmithint 32159854Sbpfile_load(char *filename, vm_offset_t dest, struct preloaded_file **result) 32238465Smsmith{ 323241069Sae static int last_file_format = 0; 32459854Sbp struct preloaded_file *fp; 32559854Sbp int error; 32659854Sbp int i; 32738764Smsmith 328220311Smarcel if (archsw.arch_loadaddr != NULL) 329220311Smarcel dest = archsw.arch_loadaddr(LOAD_RAW, filename, dest); 330220311Smarcel 33159854Sbp error = EFTYPE; 332241069Sae for (i = last_file_format, fp = NULL; 333241069Sae file_formats[i] && fp == NULL; i++) { 334220332Smarcel error = (file_formats[i]->l_load)(filename, dest, &fp); 33559854Sbp if (error == 0) { 336241069Sae fp->f_loader = last_file_format = i; /* remember the loader */ 33759854Sbp *result = fp; 33859854Sbp break; 339241069Sae } else if (last_file_format == i && i != 0) { 340241070Sae /* Restart from the beginning */ 341249719Sae i = -1; 342249719Sae last_file_format = 0; 343241070Sae fp = NULL; 344241070Sae continue; 34559854Sbp } 34659854Sbp if (error == EFTYPE) 34759854Sbp continue; /* Unknown to this handler? */ 34859854Sbp if (error) { 349329010Skevans snprintf(command_errbuf, sizeof(command_errbuf), 350329010Skevans "can't load file '%s': %s", filename, strerror(error)); 35159854Sbp break; 35259854Sbp } 35359854Sbp } 35459854Sbp return (error); 35559854Sbp} 35638465Smsmith 35759854Sbpstatic int 358207854Simpfile_load_dependencies(struct preloaded_file *base_file) 359207854Simp{ 36059854Sbp struct file_metadata *md; 36159854Sbp struct preloaded_file *fp; 36283321Speter struct mod_depend *verinfo; 36383321Speter struct kernel_module *mp; 36459854Sbp char *dmodname; 36559854Sbp int error; 36659854Sbp 36759854Sbp md = file_findmetadata(base_file, MODINFOMD_DEPLIST); 36859854Sbp if (md == NULL) 36959854Sbp return (0); 37059854Sbp error = 0; 37159854Sbp do { 37283321Speter verinfo = (struct mod_depend*)md->md_data; 37383321Speter dmodname = (char *)(verinfo + 1); 37483321Speter if (file_findmodule(NULL, dmodname, verinfo) == NULL) { 37559854Sbp printf("loading required module '%s'\n", dmodname); 37683321Speter error = mod_load(dmodname, verinfo, 0, NULL); 37759854Sbp if (error) 37859854Sbp break; 37983321Speter /* 38083321Speter * If module loaded via kld name which isn't listed 38183321Speter * in the linker.hints file, we should check if it have 38283321Speter * required version. 38383321Speter */ 38483321Speter mp = file_findmodule(NULL, dmodname, verinfo); 38583321Speter if (mp == NULL) { 386329010Skevans snprintf(command_errbuf, sizeof(command_errbuf), 387329010Skevans "module '%s' exists but with wrong version", dmodname); 38883321Speter error = ENOENT; 38983321Speter break; 39083321Speter } 39159854Sbp } 39259854Sbp md = metadata_next(md, MODINFOMD_DEPLIST); 39359854Sbp } while (md); 39457468Sbp if (!error) 39557468Sbp return (0); 39657468Sbp /* Load failed; discard everything */ 39759854Sbp while (base_file != NULL) { 39859854Sbp fp = base_file; 39959854Sbp base_file = base_file->f_next; 40059854Sbp file_discard(fp); 40138764Smsmith } 40257468Sbp return (error); 40338764Smsmith} 404246953Skientzle 40538764Smsmith/* 406294058Ssmh * We've been asked to load (fname) as (type), so just suck it in, 40738764Smsmith * no arguments or anything. 40838764Smsmith */ 409262345Sianstruct preloaded_file * 410294058Ssmhfile_loadraw(const char *fname, char *type, int insert) 41138764Smsmith{ 41259854Sbp struct preloaded_file *fp; 413294058Ssmh char *name; 41438764Smsmith int fd, got; 41538764Smsmith vm_offset_t laddr; 41638764Smsmith 41738764Smsmith /* We can't load first */ 41859854Sbp if ((file_findfile(NULL, NULL)) == NULL) { 41938764Smsmith command_errmsg = "can't load file before kernel"; 420262345Sian return(NULL); 42138764Smsmith } 42238764Smsmith 42339178Smsmith /* locate the file on the load path */ 424294058Ssmh name = file_search(fname, NULL); 425294058Ssmh if (name == NULL) { 426329010Skevans snprintf(command_errbuf, sizeof(command_errbuf), 427329010Skevans "can't find '%s'", fname); 428262345Sian return(NULL); 42939178Smsmith } 430220311Smarcel 43138764Smsmith if ((fd = open(name, O_RDONLY)) < 0) { 432329010Skevans snprintf(command_errbuf, sizeof(command_errbuf), 433329010Skevans "can't open '%s': %s", name, strerror(errno)); 43444570Sdcs free(name); 435262345Sian return(NULL); 43638764Smsmith } 43738764Smsmith 438220311Smarcel if (archsw.arch_loadaddr != NULL) 439220311Smarcel loadaddr = archsw.arch_loadaddr(LOAD_RAW, name, loadaddr); 440201340Snyan 441269614Smarcel printf("%s ", name); 442269614Smarcel 44338764Smsmith laddr = loadaddr; 44438764Smsmith for (;;) { 44538764Smsmith /* read in 4k chunks; size is not really important */ 44638764Smsmith got = archsw.arch_readin(fd, laddr, 4096); 44738764Smsmith if (got == 0) /* end of file */ 44838764Smsmith break; 44938764Smsmith if (got < 0) { /* error */ 450329010Skevans snprintf(command_errbuf, sizeof(command_errbuf), 451329010Skevans "error reading '%s': %s", name, strerror(errno)); 45244210Sdcs free(name); 45357269Smsmith close(fd); 454262345Sian return(NULL); 45538764Smsmith } 45638764Smsmith laddr += got; 45738764Smsmith } 458269614Smarcel 459269614Smarcel printf("size=%#jx\n", (uintmax_t)(laddr - loadaddr)); 460269614Smarcel 46138764Smsmith /* Looks OK so far; create & populate control structure */ 46259854Sbp fp = file_alloc(); 463240342Savg fp->f_name = strdup(name); 46459854Sbp fp->f_type = strdup(type); 46559854Sbp fp->f_args = NULL; 46659854Sbp fp->f_metadata = NULL; 46759854Sbp fp->f_loader = -1; 46859854Sbp fp->f_addr = loadaddr; 46959854Sbp fp->f_size = laddr - loadaddr; 47038764Smsmith 47138764Smsmith /* recognise space consumption */ 47238764Smsmith loadaddr = laddr; 47338764Smsmith 47459854Sbp /* Add to the list of loaded files */ 475277215Sroyger if (insert != 0) 476277215Sroyger file_insert_tail(fp); 47757269Smsmith close(fd); 478262345Sian return(fp); 47938764Smsmith} 48038764Smsmith 48138764Smsmith/* 48259854Sbp * Load the module (name), pass it (argc),(argv), add container file 48359854Sbp * to the list of loaded files. 48459854Sbp * If module is already loaded just assign new argc/argv. 48538764Smsmith */ 48659854Sbpint 48783321Spetermod_load(char *modname, struct mod_depend *verinfo, int argc, char *argv[]) 48838764Smsmith{ 48959854Sbp struct kernel_module *mp; 49059854Sbp int err; 49159854Sbp char *filename; 49238764Smsmith 49383321Speter if (file_havepath(modname)) { 49483321Speter printf("Warning: mod_load() called instead of mod_loadkld() for module '%s'\n", modname); 49583321Speter return (mod_loadkld(modname, argc, argv)); 49683321Speter } 49757468Sbp /* see if module is already loaded */ 49883321Speter mp = file_findmodule(NULL, modname, verinfo); 49957468Sbp if (mp) { 50059854Sbp#ifdef moduleargs 50159854Sbp if (mp->m_args) 50259854Sbp free(mp->m_args); 50359854Sbp mp->m_args = unargv(argc, argv); 50459854Sbp#endif 505329010Skevans snprintf(command_errbuf, sizeof(command_errbuf), 506329010Skevans "warning: module '%s' already loaded", mp->m_name); 50759854Sbp return (0); 50857468Sbp } 50959854Sbp /* locate file with the module on the search path */ 51083321Speter filename = mod_searchmodule(modname, verinfo); 51159854Sbp if (filename == NULL) { 512329010Skevans snprintf(command_errbuf, sizeof(command_errbuf), 513329010Skevans "can't find '%s'", modname); 51459854Sbp return (ENOENT); 51559854Sbp } 51683321Speter err = mod_loadkld(filename, argc, argv); 51783321Speter return (err); 51883321Speter} 51983321Speter 52083321Speter/* 52183321Speter * Load specified KLD. If path is omitted, then try to locate it via 52283321Speter * search path. 52383321Speter */ 52483321Speterint 52583321Spetermod_loadkld(const char *kldname, int argc, char *argv[]) 52683321Speter{ 52783321Speter struct preloaded_file *fp, *last_file; 52883321Speter int err; 52983321Speter char *filename; 53083321Speter 53183321Speter /* 53283321Speter * Get fully qualified KLD name 53383321Speter */ 53483321Speter filename = file_search(kldname, kld_ext_list); 53583321Speter if (filename == NULL) { 536329010Skevans snprintf(command_errbuf, sizeof(command_errbuf), 537329010Skevans "can't find '%s'", kldname); 53883321Speter return (ENOENT); 53983321Speter } 540299499Spfg /* 54183321Speter * Check if KLD already loaded 54283321Speter */ 54383321Speter fp = file_findfile(filename, NULL); 54483321Speter if (fp) { 545329010Skevans snprintf(command_errbuf, sizeof(command_errbuf), 546329010Skevans "warning: KLD '%s' already loaded", filename); 54783321Speter free(filename); 54883321Speter return (0); 54983321Speter } 550299499Spfg for (last_file = preloaded_files; 55159854Sbp last_file != NULL && last_file->f_next != NULL; 55259854Sbp last_file = last_file->f_next) 55359854Sbp ; 55457468Sbp 55559854Sbp do { 55659854Sbp err = file_load(filename, loadaddr, &fp); 55759854Sbp if (err) 55838764Smsmith break; 55959854Sbp fp->f_args = unargv(argc, argv); 56059854Sbp loadaddr = fp->f_addr + fp->f_size; 56159854Sbp file_insert_tail(fp); /* Add to the list of loaded files */ 56286469Siedowse if (file_load_dependencies(fp) != 0) { 56359854Sbp err = ENOENT; 56459854Sbp last_file->f_next = NULL; 56559854Sbp loadaddr = last_file->f_addr + last_file->f_size; 56659854Sbp fp = NULL; 56759854Sbp break; 56859854Sbp } 56959854Sbp } while(0); 570329010Skevans if (err == EFTYPE) { 571329010Skevans snprintf(command_errbuf, sizeof(command_errbuf), 572329010Skevans "don't know how to load module '%s'", filename); 573329010Skevans } 57459854Sbp if (err && fp) 57559854Sbp file_discard(fp); 57659854Sbp free(filename); 57757468Sbp return (err); 57838764Smsmith} 57938764Smsmith 58059854Sbp/* 58159854Sbp * Find a file matching (name) and (type). 58259854Sbp * NULL may be passed as a wildcard to either. 58359854Sbp */ 58459854Sbpstruct preloaded_file * 585277215Sroygerfile_findfile(const char *name, const char *type) 58638764Smsmith{ 58759854Sbp struct preloaded_file *fp; 58838764Smsmith 58959854Sbp for (fp = preloaded_files; fp != NULL; fp = fp->f_next) { 59059854Sbp if (((name == NULL) || !strcmp(name, fp->f_name)) && 59159854Sbp ((type == NULL) || !strcmp(type, fp->f_type))) 59259854Sbp break; 59359854Sbp } 59459854Sbp return (fp); 59538465Smsmith} 59638465Smsmith 59738764Smsmith/* 59859854Sbp * Find a module matching (name) inside of given file. 59959854Sbp * NULL may be passed as a wildcard. 60038764Smsmith */ 60159854Sbpstruct kernel_module * 60283321Speterfile_findmodule(struct preloaded_file *fp, char *modname, 60383321Speter struct mod_depend *verinfo) 60438465Smsmith{ 60583321Speter struct kernel_module *mp, *best; 60683321Speter int bestver, mver; 60759854Sbp 60859854Sbp if (fp == NULL) { 60959854Sbp for (fp = preloaded_files; fp; fp = fp->f_next) { 61083321Speter mp = file_findmodule(fp, modname, verinfo); 611299499Spfg if (mp) 61283321Speter return (mp); 61359854Sbp } 61459854Sbp return (NULL); 61538465Smsmith } 61683321Speter best = NULL; 61783321Speter bestver = 0; 61859854Sbp for (mp = fp->f_modules; mp; mp = mp->m_next) { 61983321Speter if (strcmp(modname, mp->m_name) == 0) { 62083321Speter if (verinfo == NULL) 62183321Speter return (mp); 62283321Speter mver = mp->m_version; 62383321Speter if (mver == verinfo->md_ver_preferred) 62483321Speter return (mp); 625299499Spfg if (mver >= verinfo->md_ver_minimum && 62683321Speter mver <= verinfo->md_ver_maximum && 62783321Speter mver > bestver) { 62883321Speter best = mp; 62983321Speter bestver = mver; 63083321Speter } 63183321Speter } 63259854Sbp } 63383321Speter return (best); 63438465Smsmith} 63538764Smsmith/* 63638764Smsmith * Make a copy of (size) bytes of data from (p), and associate them as 63738764Smsmith * metadata of (type) to the module (mp). 63838764Smsmith */ 63938712Smsmithvoid 64059854Sbpfile_addmetadata(struct preloaded_file *fp, int type, size_t size, void *p) 64138712Smsmith{ 64259854Sbp struct file_metadata *md; 64338712Smsmith 64464187Sjhb md = malloc(sizeof(struct file_metadata) - sizeof(md->md_data) + size); 64538712Smsmith md->md_size = size; 64638712Smsmith md->md_type = type; 64738712Smsmith bcopy(p, md->md_data, size); 64859854Sbp md->md_next = fp->f_metadata; 64959854Sbp fp->f_metadata = md; 65038712Smsmith} 65138712Smsmith 65238764Smsmith/* 65359854Sbp * Find a metadata object of (type) associated with the file (fp) 65438764Smsmith */ 65559854Sbpstruct file_metadata * 65659854Sbpfile_findmetadata(struct preloaded_file *fp, int type) 65738712Smsmith{ 65859854Sbp struct file_metadata *md; 65938712Smsmith 66059854Sbp for (md = fp->f_metadata; md != NULL; md = md->md_next) 66138712Smsmith if (md->md_type == type) 66238712Smsmith break; 66338712Smsmith return(md); 66438712Smsmith} 66538764Smsmith 666318019Sroyger/* 667318019Sroyger * Remove all metadata from the file. 668318019Sroyger */ 669318019Sroygervoid 670318019Sroygerfile_removemetadata(struct preloaded_file *fp) 671318019Sroyger{ 672318019Sroyger struct file_metadata *md, *next; 673318019Sroyger 674318019Sroyger for (md = fp->f_metadata; md != NULL; md = next) 675318019Sroyger { 676318019Sroyger next = md->md_next; 677318019Sroyger free(md); 678318019Sroyger } 679318019Sroyger fp->f_metadata = NULL; 680318019Sroyger} 681318019Sroyger 68259854Sbpstruct file_metadata * 68359854Sbpmetadata_next(struct file_metadata *md, int type) 68457468Sbp{ 68557468Sbp if (md == NULL) 68657468Sbp return (NULL); 68757468Sbp while((md = md->md_next) != NULL) 68857468Sbp if (md->md_type == type) 68957468Sbp break; 69057468Sbp return (md); 69157468Sbp} 69257468Sbp 69383321Speterstatic char *emptyextlist[] = { "", NULL }; 69483321Speter 69538764Smsmith/* 69683321Speter * Check if the given file is in place and return full path to it. 69783321Speter */ 69883321Speterstatic char * 69983321Speterfile_lookup(const char *path, const char *name, int namelen, char **extlist) 70083321Speter{ 70183321Speter struct stat st; 70283321Speter char *result, *cp, **cpp; 70383321Speter int pathlen, extlen, len; 70483321Speter 70583321Speter pathlen = strlen(path); 70683321Speter extlen = 0; 70783321Speter if (extlist == NULL) 70883321Speter extlist = emptyextlist; 70983321Speter for (cpp = extlist; *cpp; cpp++) { 71083321Speter len = strlen(*cpp); 71183321Speter if (len > extlen) 71283321Speter extlen = len; 71383321Speter } 71483321Speter result = malloc(pathlen + namelen + extlen + 2); 71583321Speter if (result == NULL) 71683321Speter return (NULL); 71783321Speter bcopy(path, result, pathlen); 71883321Speter if (pathlen > 0 && result[pathlen - 1] != '/') 71983321Speter result[pathlen++] = '/'; 72083321Speter cp = result + pathlen; 72183321Speter bcopy(name, cp, namelen); 72283321Speter cp += namelen; 72383321Speter for (cpp = extlist; *cpp; cpp++) { 72483321Speter strcpy(cp, *cpp); 72583321Speter if (stat(result, &st) == 0 && S_ISREG(st.st_mode)) 72683321Speter return result; 72783321Speter } 72883321Speter free(result); 72983321Speter return NULL; 73083321Speter} 73183321Speter 73283321Speter/* 73383321Speter * Check if file name have any qualifiers 73483321Speter */ 73583321Speterstatic int 73683321Speterfile_havepath(const char *name) 73783321Speter{ 73883321Speter const char *cp; 73983321Speter 74083321Speter archsw.arch_getdev(NULL, name, &cp); 74183321Speter return (cp != name || strchr(name, '/') != NULL); 74283321Speter} 74383321Speter 74483321Speter/* 74539178Smsmith * Attempt to find the file (name) on the module searchpath. 74638764Smsmith * If (name) is qualified in any way, we simply check it and 74738764Smsmith * return it or NULL. If it is not qualified, then we attempt 74838764Smsmith * to construct a path using entries in the environment variable 74938764Smsmith * module_path. 75038764Smsmith * 75138764Smsmith * The path we return a pointer to need never be freed, as we manage 75238764Smsmith * it internally. 75338764Smsmith */ 75438764Smsmithstatic char * 75583321Speterfile_search(const char *name, char **extlist) 75638764Smsmith{ 75783321Speter struct moduledir *mdp; 75883321Speter struct stat sb; 75944570Sdcs char *result; 76083321Speter int namelen; 76138764Smsmith 76238764Smsmith /* Don't look for nothing */ 76344210Sdcs if (name == NULL) 76483321Speter return(NULL); 76538764Smsmith 76644210Sdcs if (*name == 0) 76744210Sdcs return(strdup(name)); 76844210Sdcs 76983321Speter if (file_havepath(name)) { 77038764Smsmith /* Qualified, so just see if it exists */ 77138764Smsmith if (stat(name, &sb) == 0) 77244210Sdcs return(strdup(name)); 77338764Smsmith return(NULL); 77438764Smsmith } 77583321Speter moduledir_rebuild(); 77644570Sdcs result = NULL; 77783321Speter namelen = strlen(name); 77883321Speter STAILQ_FOREACH(mdp, &moduledir_list, d_link) { 77983321Speter result = file_lookup(mdp->d_path, name, namelen, extlist); 78083321Speter if (result) 78138764Smsmith break; 78238764Smsmith } 78338764Smsmith return(result); 78438764Smsmith} 78538764Smsmith 78683321Speter#define INT_ALIGN(base, ptr) ptr = \ 787298433Spfg (base) + roundup2((ptr) - (base), sizeof(int)) 78883321Speter 78983321Speterstatic char * 79083321Spetermod_search_hints(struct moduledir *mdp, const char *modname, 79183321Speter struct mod_depend *verinfo) 79283321Speter{ 79383321Speter u_char *cp, *recptr, *bufend, *best; 79483321Speter char *result; 79583321Speter int *intp, bestver, blen, clen, found, ival, modnamelen, reclen; 79683321Speter 79783321Speter moduledir_readhints(mdp); 79883321Speter modnamelen = strlen(modname); 79983321Speter found = 0; 80083321Speter result = NULL; 80183321Speter bestver = 0; 80283321Speter if (mdp->d_hints == NULL) 80383321Speter goto bad; 80483321Speter recptr = mdp->d_hints; 80583321Speter bufend = recptr + mdp->d_hintsz; 80683321Speter clen = blen = 0; 80783321Speter best = cp = NULL; 80883321Speter while (recptr < bufend && !found) { 80983321Speter intp = (int*)recptr; 81083321Speter reclen = *intp++; 81183321Speter ival = *intp++; 812298230Sallanjude cp = (u_char*)intp; 81383321Speter switch (ival) { 81483321Speter case MDT_VERSION: 81583321Speter clen = *cp++; 81683321Speter if (clen != modnamelen || bcmp(cp, modname, clen) != 0) 81783321Speter break; 81883321Speter cp += clen; 81983321Speter INT_ALIGN(mdp->d_hints, cp); 82083321Speter ival = *(int*)cp; 82183321Speter cp += sizeof(int); 82283321Speter clen = *cp++; 82383321Speter if (verinfo == NULL || ival == verinfo->md_ver_preferred) { 82483321Speter found = 1; 82583321Speter break; 82683321Speter } 827299499Spfg if (ival >= verinfo->md_ver_minimum && 82883321Speter ival <= verinfo->md_ver_maximum && 82983321Speter ival > bestver) { 83083321Speter bestver = ival; 83183321Speter best = cp; 83283321Speter blen = clen; 83383321Speter } 83483321Speter break; 83583321Speter default: 83683321Speter break; 83783321Speter } 83883321Speter recptr += reclen + sizeof(int); 83983321Speter } 84083321Speter /* 84183321Speter * Finally check if KLD is in the place 84283321Speter */ 84383321Speter if (found) 844298230Sallanjude result = file_lookup(mdp->d_path, (const char *)cp, clen, NULL); 84583321Speter else if (best) 846298230Sallanjude result = file_lookup(mdp->d_path, (const char *)best, blen, NULL); 84783321Speterbad: 84883321Speter /* 84983321Speter * If nothing found or hints is absent - fallback to the old way 85083321Speter * by using "kldname[.ko]" as module name. 85183321Speter */ 85283321Speter if (!found && !bestver && result == NULL) 85383321Speter result = file_lookup(mdp->d_path, modname, modnamelen, kld_ext_list); 85483321Speter return result; 85583321Speter} 85683321Speter 85738764Smsmith/* 85839178Smsmith * Attempt to locate the file containing the module (name) 85939178Smsmith */ 86039178Smsmithstatic char * 86183321Spetermod_searchmodule(char *name, struct mod_depend *verinfo) 86239178Smsmith{ 86383321Speter struct moduledir *mdp; 86483321Speter char *result; 86539178Smsmith 86683321Speter moduledir_rebuild(); 86783321Speter /* 86883321Speter * Now we ready to lookup module in the given directories 86983321Speter */ 87083321Speter result = NULL; 87183321Speter STAILQ_FOREACH(mdp, &moduledir_list, d_link) { 87283321Speter result = mod_search_hints(mdp, name, verinfo); 87383321Speter if (result) 87483321Speter break; 87583321Speter } 87683321Speter 87739178Smsmith return(result); 87839178Smsmith} 87939178Smsmith 88059854Sbpint 88183321Speterfile_addmodule(struct preloaded_file *fp, char *modname, int version, 88259854Sbp struct kernel_module **newmp) 88359854Sbp{ 88459854Sbp struct kernel_module *mp; 88583321Speter struct mod_depend mdepend; 88639178Smsmith 88783321Speter bzero(&mdepend, sizeof(mdepend)); 88883321Speter mdepend.md_ver_preferred = version; 88983321Speter mp = file_findmodule(fp, modname, &mdepend); 89059854Sbp if (mp) 89159854Sbp return (EEXIST); 89259854Sbp mp = malloc(sizeof(struct kernel_module)); 89359854Sbp if (mp == NULL) 89459854Sbp return (ENOMEM); 89559854Sbp bzero(mp, sizeof(struct kernel_module)); 89659854Sbp mp->m_name = strdup(modname); 89783321Speter mp->m_version = version; 89859854Sbp mp->m_fp = fp; 89959854Sbp mp->m_next = fp->f_modules; 90059854Sbp fp->f_modules = mp; 90159854Sbp if (newmp) 90259854Sbp *newmp = mp; 90359854Sbp return (0); 90459854Sbp} 90559854Sbp 90639178Smsmith/* 90759854Sbp * Throw a file away 90838764Smsmith */ 90938764Smsmithvoid 91059854Sbpfile_discard(struct preloaded_file *fp) 91138764Smsmith{ 91259854Sbp struct file_metadata *md, *md1; 91359854Sbp struct kernel_module *mp, *mp1; 91459854Sbp if (fp == NULL) 91559854Sbp return; 91659854Sbp md = fp->f_metadata; 91759854Sbp while (md) { 91859854Sbp md1 = md; 91959854Sbp md = md->md_next; 92059854Sbp free(md1); 92159854Sbp } 92259854Sbp mp = fp->f_modules; 92359854Sbp while (mp) { 92459854Sbp if (mp->m_name) 92539178Smsmith free(mp->m_name); 92659854Sbp mp1 = mp; 92759854Sbp mp = mp->m_next; 92859854Sbp free(mp1); 929299499Spfg } 93059854Sbp if (fp->f_name != NULL) 93159854Sbp free(fp->f_name); 93259854Sbp if (fp->f_type != NULL) 93359854Sbp free(fp->f_type); 93459854Sbp if (fp->f_args != NULL) 93559854Sbp free(fp->f_args); 93659854Sbp free(fp); 93738764Smsmith} 93838764Smsmith 93938764Smsmith/* 94059854Sbp * Allocate a new file; must be used instead of malloc() 94139178Smsmith * to ensure safe initialisation. 94239178Smsmith */ 94359854Sbpstruct preloaded_file * 94459854Sbpfile_alloc(void) 94539178Smsmith{ 94659854Sbp struct preloaded_file *fp; 947299499Spfg 94859854Sbp if ((fp = malloc(sizeof(struct preloaded_file))) != NULL) { 94959854Sbp bzero(fp, sizeof(struct preloaded_file)); 95039178Smsmith } 95159854Sbp return (fp); 95239178Smsmith} 95339178Smsmith 95439178Smsmith/* 95538764Smsmith * Add a module to the chain 95638764Smsmith */ 95738764Smsmithstatic void 95859854Sbpfile_insert_tail(struct preloaded_file *fp) 95938764Smsmith{ 96059854Sbp struct preloaded_file *cm; 96138764Smsmith 96259854Sbp /* Append to list of loaded file */ 96359854Sbp fp->f_next = NULL; 96459854Sbp if (preloaded_files == NULL) { 96559854Sbp preloaded_files = fp; 96638764Smsmith } else { 96759854Sbp for (cm = preloaded_files; cm->f_next != NULL; cm = cm->f_next) 96838764Smsmith ; 96959854Sbp cm->f_next = fp; 97038764Smsmith } 97138764Smsmith} 97259854Sbp 97383321Speterstatic char * 97483321Spetermoduledir_fullpath(struct moduledir *mdp, const char *fname) 97583321Speter{ 97683321Speter char *cp; 97783321Speter 97883321Speter cp = malloc(strlen(mdp->d_path) + strlen(fname) + 2); 97983321Speter if (cp == NULL) 98083321Speter return NULL; 98183321Speter strcpy(cp, mdp->d_path); 98283321Speter strcat(cp, "/"); 98383321Speter strcat(cp, fname); 98483321Speter return (cp); 98583321Speter} 98683321Speter 98783321Speter/* 98883321Speter * Read linker.hints file into memory performing some sanity checks. 98983321Speter */ 99083321Speterstatic void 99183321Spetermoduledir_readhints(struct moduledir *mdp) 99283321Speter{ 99383321Speter struct stat st; 99483321Speter char *path; 99583321Speter int fd, size, version; 99683321Speter 99783321Speter if (mdp->d_hints != NULL || (mdp->d_flags & MDIR_NOHINTS)) 99883321Speter return; 99983321Speter path = moduledir_fullpath(mdp, "linker.hints"); 1000154257Smarius if (stat(path, &st) != 0 || 1001154257Smarius st.st_size < (ssize_t)(sizeof(version) + sizeof(int)) || 1002275261Simp st.st_size > LINKER_HINTS_MAX || (fd = open(path, O_RDONLY)) < 0) { 100383321Speter free(path); 100483321Speter mdp->d_flags |= MDIR_NOHINTS; 100583321Speter return; 100683321Speter } 100783321Speter free(path); 100883321Speter size = read(fd, &version, sizeof(version)); 100983321Speter if (size != sizeof(version) || version != LINKER_HINTS_VERSION) 101083321Speter goto bad; 101183321Speter size = st.st_size - size; 101283321Speter mdp->d_hints = malloc(size); 101383321Speter if (mdp->d_hints == NULL) 101483321Speter goto bad; 101583321Speter if (read(fd, mdp->d_hints, size) != size) 101683321Speter goto bad; 101783321Speter mdp->d_hintsz = size; 101883321Speter close(fd); 101983321Speter return; 102083321Speterbad: 102183321Speter close(fd); 102283321Speter if (mdp->d_hints) { 102383321Speter free(mdp->d_hints); 102483321Speter mdp->d_hints = NULL; 102583321Speter } 102683321Speter mdp->d_flags |= MDIR_NOHINTS; 102783321Speter return; 102883321Speter} 102983321Speter 103083321Speter/* 103183321Speter * Extract directories from the ';' separated list, remove duplicates. 103283321Speter */ 103383321Speterstatic void 103483321Spetermoduledir_rebuild(void) 103583321Speter{ 103683321Speter struct moduledir *mdp, *mtmp; 103783321Speter const char *path, *cp, *ep; 1038293724Ssmh size_t cplen; 103983321Speter 104083321Speter path = getenv("module_path"); 104183321Speter if (path == NULL) 104283321Speter path = default_searchpath; 104383321Speter /* 104483321Speter * Rebuild list of module directories if it changed 104583321Speter */ 104683321Speter STAILQ_FOREACH(mdp, &moduledir_list, d_link) 104783321Speter mdp->d_flags |= MDIR_REMOVED; 104883321Speter 104983321Speter for (ep = path; *ep != 0; ep++) { 105083321Speter cp = ep; 105183321Speter for (; *ep != 0 && *ep != ';'; ep++) 105283321Speter ; 105383321Speter /* 105483321Speter * Ignore trailing slashes 105583321Speter */ 105683321Speter for (cplen = ep - cp; cplen > 1 && cp[cplen - 1] == '/'; cplen--) 105783321Speter ; 105883321Speter STAILQ_FOREACH(mdp, &moduledir_list, d_link) { 105983321Speter if (strlen(mdp->d_path) != cplen || bcmp(cp, mdp->d_path, cplen) != 0) 106083321Speter continue; 106183321Speter mdp->d_flags &= ~MDIR_REMOVED; 106283321Speter break; 106383321Speter } 106483321Speter if (mdp == NULL) { 106583321Speter mdp = malloc(sizeof(*mdp) + cplen + 1); 106683321Speter if (mdp == NULL) 106783321Speter return; 106883321Speter mdp->d_path = (char*)(mdp + 1); 106983321Speter bcopy(cp, mdp->d_path, cplen); 107083321Speter mdp->d_path[cplen] = 0; 107183321Speter mdp->d_hints = NULL; 107283321Speter mdp->d_flags = 0; 107383321Speter STAILQ_INSERT_TAIL(&moduledir_list, mdp, d_link); 107483321Speter } 107594419Speter if (*ep == 0) 107694419Speter break; 107783321Speter } 107883321Speter /* 107983321Speter * Delete unused directories if any 108083321Speter */ 108183321Speter mdp = STAILQ_FIRST(&moduledir_list); 108283321Speter while (mdp) { 108383321Speter if ((mdp->d_flags & MDIR_REMOVED) == 0) { 108483321Speter mdp = STAILQ_NEXT(mdp, d_link); 108583321Speter } else { 108683321Speter if (mdp->d_hints) 108783321Speter free(mdp->d_hints); 108883321Speter mtmp = mdp; 108983321Speter mdp = STAILQ_NEXT(mdp, d_link); 109083321Speter STAILQ_REMOVE(&moduledir_list, mtmp, moduledir, d_link); 109183321Speter free(mtmp); 109283321Speter } 109383321Speter } 109483321Speter return; 109583321Speter} 1096