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$"); 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> 4038465Smsmith 4138465Smsmith#include "bootstrap.h" 4238465Smsmith 4383321Speter#define MDIR_REMOVED 0x0001 4483321Speter#define MDIR_NOHINTS 0x0002 4583321Speter 4683321Speterstruct moduledir { 4783321Speter char *d_path; /* path of modules directory */ 4883321Speter u_char *d_hints; /* content of linker.hints file */ 4983321Speter int d_hintsz; /* size of hints data */ 5083321Speter int d_flags; 5183321Speter STAILQ_ENTRY(moduledir) d_link; 5283321Speter}; 5383321Speter 5459854Sbpstatic int file_load(char *filename, vm_offset_t dest, struct preloaded_file **result); 5586469Siedowsestatic int file_load_dependencies(struct preloaded_file *base_mod); 5683321Speterstatic char * file_search(const char *name, char **extlist); 5783321Speterstatic struct kernel_module * file_findmodule(struct preloaded_file *fp, char *modname, struct mod_depend *verinfo); 5883321Speterstatic int file_havepath(const char *name); 5983321Speterstatic char *mod_searchmodule(char *name, struct mod_depend *verinfo); 6059854Sbpstatic void file_insert_tail(struct preloaded_file *mp); 6159854Sbpstruct file_metadata* metadata_next(struct file_metadata *base_mp, int type); 6283321Speterstatic void moduledir_readhints(struct moduledir *mdp); 6383321Speterstatic void moduledir_rebuild(void); 6438764Smsmith 6539178Smsmith/* load address should be tweaked by first module loaded (kernel) */ 6638465Smsmithstatic vm_offset_t loadaddr = 0; 6738465Smsmith 68273658Sian#if defined(LOADER_FDT_SUPPORT) 69273658Sianstatic const char *default_searchpath = 70273658Sian "/boot/kernel;/boot/modules;/boot/dtb"; 71273658Sian#else 72111852Srustatic const char *default_searchpath ="/boot/kernel;/boot/modules"; 73273658Sian#endif 7439178Smsmith 7583321Speterstatic STAILQ_HEAD(, moduledir) moduledir_list = STAILQ_HEAD_INITIALIZER(moduledir_list); 7683321Speter 7759854Sbpstruct preloaded_file *preloaded_files = NULL; 7838465Smsmith 7983321Speterstatic char *kld_ext_list[] = { 8083321Speter ".ko", 8183321Speter "", 82172445Sobrien ".debug", 8383321Speter NULL 8483321Speter}; 8583321Speter 8683321Speter 8738764Smsmith/* 8838764Smsmith * load an object, either a disk file or code module. 8938764Smsmith * 9038764Smsmith * To load a file, the syntax is: 9138764Smsmith * 9238764Smsmith * load -t <type> <path> 9338764Smsmith * 9438764Smsmith * code modules are loaded as: 9538764Smsmith * 9638764Smsmith * load <path> <options> 9738764Smsmith */ 9838764Smsmith 9938465SmsmithCOMMAND_SET(load, "load", "load a kernel or module", command_load); 10038465Smsmith 10138465Smsmithstatic int 10238465Smsmithcommand_load(int argc, char *argv[]) 10338465Smsmith{ 104294984Ssmh struct preloaded_file *fp; 10538764Smsmith char *typestr; 10683321Speter int dofile, dokld, ch, error; 10738764Smsmith 10883321Speter dokld = dofile = 0; 10938764Smsmith optind = 1; 11042512Smsmith optreset = 1; 11138764Smsmith typestr = NULL; 11253993Sdcs if (argc == 1) { 11353993Sdcs command_errmsg = "no filename specified"; 114294985Ssmh return (CMD_CRIT); 11553993Sdcs } 11683321Speter while ((ch = getopt(argc, argv, "kt:")) != -1) { 11738764Smsmith switch(ch) { 11883321Speter case 'k': 11983321Speter dokld = 1; 12083321Speter break; 12138764Smsmith case 't': 12238764Smsmith typestr = optarg; 12338764Smsmith dofile = 1; 12438764Smsmith break; 12538764Smsmith case '?': 12638764Smsmith default: 12738764Smsmith /* getopt has already reported an error */ 128294985Ssmh return (CMD_OK); 12938764Smsmith } 13038764Smsmith } 13138764Smsmith argv += (optind - 1); 13238764Smsmith argc -= (optind - 1); 13338764Smsmith 13438764Smsmith /* 13538764Smsmith * Request to load a raw file? 13638764Smsmith */ 13738764Smsmith if (dofile) { 13877971Sjesper if ((argc != 2) || (typestr == NULL) || (*typestr == 0)) { 13938764Smsmith command_errmsg = "invalid load type"; 140294985Ssmh return (CMD_CRIT); 14138764Smsmith } 142294984Ssmh 143294984Ssmh fp = file_findfile(argv[1], typestr); 144294984Ssmh if (fp) { 145294984Ssmh sprintf(command_errbuf, "warning: file '%s' already loaded", argv[1]); 146294985Ssmh return (CMD_WARN); 147294984Ssmh } 148294984Ssmh 149294985Ssmh if (file_loadraw(argv[1], typestr, 1) != NULL) 150294985Ssmh return (CMD_OK); 151294985Ssmh 152294985Ssmh /* Failing to load mfs_root is never going to end well! */ 153294985Ssmh if (strcmp("mfs_root", typestr) == 0) 154294985Ssmh return (CMD_FATAL); 155294985Ssmh 156294985Ssmh return (CMD_ERROR); 15738764Smsmith } 15838764Smsmith /* 15983321Speter * Do we have explicit KLD load ? 16083321Speter */ 16183321Speter if (dokld || file_havepath(argv[1])) { 16283321Speter error = mod_loadkld(argv[1], argc - 2, argv + 2); 163294985Ssmh if (error == EEXIST) { 16483321Speter sprintf(command_errbuf, "warning: KLD '%s' already loaded", argv[1]); 165294985Ssmh return (CMD_WARN); 166294985Ssmh } 167294985Ssmh 168294985Ssmh return (error == 0 ? CMD_OK : CMD_CRIT); 16983321Speter } 17083321Speter /* 17138764Smsmith * Looks like a request for a module. 17238764Smsmith */ 17383321Speter error = mod_load(argv[1], NULL, argc - 2, argv + 2); 174294985Ssmh if (error == EEXIST) { 17557468Sbp sprintf(command_errbuf, "warning: module '%s' already loaded", argv[1]); 176294985Ssmh return (CMD_WARN); 177294985Ssmh } 178294985Ssmh 179294985Ssmh return (error == 0 ? CMD_OK : CMD_CRIT); 18038465Smsmith} 18138465Smsmith 182188666SthompsaCOMMAND_SET(load_geli, "load_geli", "load a geli key", command_load_geli); 183188666Sthompsa 184188666Sthompsastatic int 185188666Sthompsacommand_load_geli(int argc, char *argv[]) 186188666Sthompsa{ 187188666Sthompsa char typestr[80]; 188188666Sthompsa char *cp; 189188666Sthompsa int ch, num; 190188666Sthompsa 191188666Sthompsa if (argc < 3) { 192188666Sthompsa command_errmsg = "usage is [-n key#] <prov> <file>"; 193188666Sthompsa return(CMD_ERROR); 194188666Sthompsa } 195188666Sthompsa 196188666Sthompsa num = 0; 197188666Sthompsa optind = 1; 198188666Sthompsa optreset = 1; 199188666Sthompsa while ((ch = getopt(argc, argv, "n:")) != -1) { 200188666Sthompsa switch(ch) { 201188666Sthompsa case 'n': 202188666Sthompsa num = strtol(optarg, &cp, 0); 203188666Sthompsa if (cp == optarg) { 204188666Sthompsa sprintf(command_errbuf, "bad key index '%s'", optarg); 205188666Sthompsa return(CMD_ERROR); 206188666Sthompsa } 207188666Sthompsa break; 208188666Sthompsa case '?': 209188666Sthompsa default: 210188666Sthompsa /* getopt has already reported an error */ 211188666Sthompsa return(CMD_OK); 212188666Sthompsa } 213188666Sthompsa } 214188666Sthompsa argv += (optind - 1); 215188666Sthompsa argc -= (optind - 1); 216188666Sthompsa sprintf(typestr, "%s:geli_keyfile%d", argv[1], num); 217294417Sroyger return (file_loadraw(argv[2], typestr, 1) ? CMD_OK : CMD_ERROR); 218188666Sthompsa} 219188666Sthompsa 22038712SmsmithCOMMAND_SET(unload, "unload", "unload all modules", command_unload); 22138712Smsmith 22238712Smsmithstatic int 22338712Smsmithcommand_unload(int argc, char *argv[]) 22438712Smsmith{ 22559854Sbp struct preloaded_file *fp; 22638712Smsmith 22759854Sbp while (preloaded_files != NULL) { 22859854Sbp fp = preloaded_files; 22959854Sbp preloaded_files = preloaded_files->f_next; 23059854Sbp file_discard(fp); 23138712Smsmith } 23238712Smsmith loadaddr = 0; 23365613Sdcs unsetenv("kernelname"); 23438712Smsmith return(CMD_OK); 23538712Smsmith} 23638712Smsmith 23738465SmsmithCOMMAND_SET(lsmod, "lsmod", "list loaded modules", command_lsmod); 23838465Smsmith 23938465Smsmithstatic int 24038465Smsmithcommand_lsmod(int argc, char *argv[]) 24138465Smsmith{ 24259854Sbp struct preloaded_file *fp; 24359854Sbp struct kernel_module *mp; 24459854Sbp struct file_metadata *md; 24538465Smsmith char lbuf[80]; 24638764Smsmith int ch, verbose; 24738764Smsmith 24838764Smsmith verbose = 0; 24938764Smsmith optind = 1; 25042512Smsmith optreset = 1; 25138764Smsmith while ((ch = getopt(argc, argv, "v")) != -1) { 25238764Smsmith switch(ch) { 25338764Smsmith case 'v': 25438764Smsmith verbose = 1; 25538764Smsmith break; 25638764Smsmith case '?': 25738764Smsmith default: 25838764Smsmith /* getopt has already reported an error */ 25938764Smsmith return(CMD_OK); 26038764Smsmith } 26138764Smsmith } 26238764Smsmith 26338465Smsmith pager_open(); 26459854Sbp for (fp = preloaded_files; fp; fp = fp->f_next) { 26539673Sdfr sprintf(lbuf, " %p: %s (%s, 0x%lx)\n", 26659854Sbp (void *) fp->f_addr, fp->f_name, fp->f_type, (long) fp->f_size); 26738465Smsmith pager_output(lbuf); 26859854Sbp if (fp->f_args != NULL) { 26938465Smsmith pager_output(" args: "); 27059854Sbp pager_output(fp->f_args); 27138465Smsmith pager_output("\n"); 27238465Smsmith } 27359854Sbp if (fp->f_modules) { 27459854Sbp pager_output(" modules: "); 27559854Sbp for (mp = fp->f_modules; mp; mp = mp->m_next) { 27683321Speter sprintf(lbuf, "%s.%d ", mp->m_name, mp->m_version); 27759854Sbp pager_output(lbuf); 27859854Sbp } 27959854Sbp pager_output("\n"); 28059854Sbp } 28159854Sbp if (verbose) { 28238764Smsmith /* XXX could add some formatting smarts here to display some better */ 28359854Sbp for (md = fp->f_metadata; md != NULL; md = md->md_next) { 28439673Sdfr sprintf(lbuf, " 0x%04x, 0x%lx\n", md->md_type, (long) md->md_size); 28538764Smsmith pager_output(lbuf); 28638764Smsmith } 28759854Sbp } 28838465Smsmith } 28938465Smsmith pager_close(); 29038465Smsmith return(CMD_OK); 29138465Smsmith} 29238465Smsmith 29338764Smsmith/* 29459854Sbp * File level interface, functions file_* 29538764Smsmith */ 29638465Smsmithint 29759854Sbpfile_load(char *filename, vm_offset_t dest, struct preloaded_file **result) 29838465Smsmith{ 299241069Sae static int last_file_format = 0; 30059854Sbp struct preloaded_file *fp; 30159854Sbp int error; 30259854Sbp int i; 30338764Smsmith 304220311Smarcel if (archsw.arch_loadaddr != NULL) 305220311Smarcel dest = archsw.arch_loadaddr(LOAD_RAW, filename, dest); 306220311Smarcel 30759854Sbp error = EFTYPE; 308241069Sae for (i = last_file_format, fp = NULL; 309241069Sae file_formats[i] && fp == NULL; i++) { 310220332Smarcel error = (file_formats[i]->l_load)(filename, dest, &fp); 31159854Sbp if (error == 0) { 312241069Sae fp->f_loader = last_file_format = i; /* remember the loader */ 31359854Sbp *result = fp; 31459854Sbp break; 315241069Sae } else if (last_file_format == i && i != 0) { 316241070Sae /* Restart from the beginning */ 317249719Sae i = -1; 318249719Sae last_file_format = 0; 319241070Sae fp = NULL; 320241070Sae continue; 32159854Sbp } 32259854Sbp if (error == EFTYPE) 32359854Sbp continue; /* Unknown to this handler? */ 32459854Sbp if (error) { 32559854Sbp sprintf(command_errbuf, "can't load file '%s': %s", 32659854Sbp filename, strerror(error)); 32759854Sbp break; 32859854Sbp } 32959854Sbp } 33059854Sbp return (error); 33159854Sbp} 33238465Smsmith 33359854Sbpstatic int 334207854Simpfile_load_dependencies(struct preloaded_file *base_file) 335207854Simp{ 33659854Sbp struct file_metadata *md; 33759854Sbp struct preloaded_file *fp; 33883321Speter struct mod_depend *verinfo; 33983321Speter struct kernel_module *mp; 34059854Sbp char *dmodname; 34159854Sbp int error; 34259854Sbp 34359854Sbp md = file_findmetadata(base_file, MODINFOMD_DEPLIST); 34459854Sbp if (md == NULL) 34559854Sbp return (0); 34659854Sbp error = 0; 34759854Sbp do { 34883321Speter verinfo = (struct mod_depend*)md->md_data; 34983321Speter dmodname = (char *)(verinfo + 1); 35083321Speter if (file_findmodule(NULL, dmodname, verinfo) == NULL) { 35159854Sbp printf("loading required module '%s'\n", dmodname); 35283321Speter error = mod_load(dmodname, verinfo, 0, NULL); 35359854Sbp if (error) 35459854Sbp break; 35583321Speter /* 35683321Speter * If module loaded via kld name which isn't listed 35783321Speter * in the linker.hints file, we should check if it have 35883321Speter * required version. 35983321Speter */ 36083321Speter mp = file_findmodule(NULL, dmodname, verinfo); 36183321Speter if (mp == NULL) { 36283321Speter sprintf(command_errbuf, "module '%s' exists but with wrong version", 36383321Speter dmodname); 36483321Speter error = ENOENT; 36583321Speter break; 36683321Speter } 36759854Sbp } 36859854Sbp md = metadata_next(md, MODINFOMD_DEPLIST); 36959854Sbp } while (md); 37057468Sbp if (!error) 37157468Sbp return (0); 37257468Sbp /* Load failed; discard everything */ 37359854Sbp while (base_file != NULL) { 37459854Sbp fp = base_file; 37559854Sbp base_file = base_file->f_next; 37659854Sbp file_discard(fp); 37738764Smsmith } 37857468Sbp return (error); 37938764Smsmith} 380246953Skientzle 38138764Smsmith/* 382294981Ssmh * We've been asked to load (fname) as (type), so just suck it in, 38338764Smsmith * no arguments or anything. 38438764Smsmith */ 385265068Sianstruct preloaded_file * 386294981Ssmhfile_loadraw(const char *fname, char *type, int insert) 38738764Smsmith{ 38859854Sbp struct preloaded_file *fp; 389294981Ssmh char *name; 39038764Smsmith int fd, got; 39138764Smsmith vm_offset_t laddr; 39238764Smsmith 39338764Smsmith /* We can't load first */ 39459854Sbp if ((file_findfile(NULL, NULL)) == NULL) { 39538764Smsmith command_errmsg = "can't load file before kernel"; 396265068Sian return(NULL); 39738764Smsmith } 39838764Smsmith 39939178Smsmith /* locate the file on the load path */ 400294981Ssmh name = file_search(fname, NULL); 401294981Ssmh if (name == NULL) { 402294981Ssmh sprintf(command_errbuf, "can't find '%s'", fname); 403265068Sian return(NULL); 40439178Smsmith } 405220311Smarcel 40638764Smsmith if ((fd = open(name, O_RDONLY)) < 0) { 40738764Smsmith sprintf(command_errbuf, "can't open '%s': %s", name, strerror(errno)); 40844570Sdcs free(name); 409265068Sian return(NULL); 41038764Smsmith } 41138764Smsmith 412220311Smarcel if (archsw.arch_loadaddr != NULL) 413220311Smarcel loadaddr = archsw.arch_loadaddr(LOAD_RAW, name, loadaddr); 414201340Snyan 41538764Smsmith laddr = loadaddr; 41638764Smsmith for (;;) { 41738764Smsmith /* read in 4k chunks; size is not really important */ 41838764Smsmith got = archsw.arch_readin(fd, laddr, 4096); 41938764Smsmith if (got == 0) /* end of file */ 42038764Smsmith break; 42138764Smsmith if (got < 0) { /* error */ 42238764Smsmith sprintf(command_errbuf, "error reading '%s': %s", name, strerror(errno)); 42344210Sdcs free(name); 42457269Smsmith close(fd); 425265068Sian return(NULL); 42638764Smsmith } 42738764Smsmith laddr += got; 42838764Smsmith } 42938764Smsmith 43038764Smsmith /* Looks OK so far; create & populate control structure */ 43159854Sbp fp = file_alloc(); 432240342Savg fp->f_name = strdup(name); 43359854Sbp fp->f_type = strdup(type); 43459854Sbp fp->f_args = NULL; 43559854Sbp fp->f_metadata = NULL; 43659854Sbp fp->f_loader = -1; 43759854Sbp fp->f_addr = loadaddr; 43859854Sbp fp->f_size = laddr - loadaddr; 43938764Smsmith 44038764Smsmith /* recognise space consumption */ 44138764Smsmith loadaddr = laddr; 44238764Smsmith 44359854Sbp /* Add to the list of loaded files */ 444294417Sroyger if (insert != 0) 445294417Sroyger file_insert_tail(fp); 44657269Smsmith close(fd); 447265068Sian return(fp); 44838764Smsmith} 44938764Smsmith 45038764Smsmith/* 45159854Sbp * Load the module (name), pass it (argc),(argv), add container file 45259854Sbp * to the list of loaded files. 45359854Sbp * If module is already loaded just assign new argc/argv. 45438764Smsmith */ 45559854Sbpint 45683321Spetermod_load(char *modname, struct mod_depend *verinfo, int argc, char *argv[]) 45738764Smsmith{ 45859854Sbp struct kernel_module *mp; 45959854Sbp int err; 46059854Sbp char *filename; 46138764Smsmith 46283321Speter if (file_havepath(modname)) { 46383321Speter printf("Warning: mod_load() called instead of mod_loadkld() for module '%s'\n", modname); 46483321Speter return (mod_loadkld(modname, argc, argv)); 46583321Speter } 46657468Sbp /* see if module is already loaded */ 46783321Speter mp = file_findmodule(NULL, modname, verinfo); 46857468Sbp if (mp) { 46959854Sbp#ifdef moduleargs 47059854Sbp if (mp->m_args) 47159854Sbp free(mp->m_args); 47259854Sbp mp->m_args = unargv(argc, argv); 47359854Sbp#endif 47459854Sbp sprintf(command_errbuf, "warning: module '%s' already loaded", mp->m_name); 47559854Sbp return (0); 47657468Sbp } 47759854Sbp /* locate file with the module on the search path */ 47883321Speter filename = mod_searchmodule(modname, verinfo); 47959854Sbp if (filename == NULL) { 48059854Sbp sprintf(command_errbuf, "can't find '%s'", modname); 48159854Sbp return (ENOENT); 48259854Sbp } 48383321Speter err = mod_loadkld(filename, argc, argv); 48483321Speter return (err); 48583321Speter} 48683321Speter 48783321Speter/* 48883321Speter * Load specified KLD. If path is omitted, then try to locate it via 48983321Speter * search path. 49083321Speter */ 49183321Speterint 49283321Spetermod_loadkld(const char *kldname, int argc, char *argv[]) 49383321Speter{ 49483321Speter struct preloaded_file *fp, *last_file; 49583321Speter int err; 49683321Speter char *filename; 49783321Speter 49883321Speter /* 49983321Speter * Get fully qualified KLD name 50083321Speter */ 50183321Speter filename = file_search(kldname, kld_ext_list); 50283321Speter if (filename == NULL) { 50383321Speter sprintf(command_errbuf, "can't find '%s'", kldname); 50483321Speter return (ENOENT); 50583321Speter } 50683321Speter /* 50783321Speter * Check if KLD already loaded 50883321Speter */ 50983321Speter fp = file_findfile(filename, NULL); 51083321Speter if (fp) { 51183321Speter sprintf(command_errbuf, "warning: KLD '%s' already loaded", filename); 51283321Speter free(filename); 51383321Speter return (0); 51483321Speter } 51559854Sbp for (last_file = preloaded_files; 51659854Sbp last_file != NULL && last_file->f_next != NULL; 51759854Sbp last_file = last_file->f_next) 51859854Sbp ; 51957468Sbp 52059854Sbp do { 52159854Sbp err = file_load(filename, loadaddr, &fp); 52259854Sbp if (err) 52338764Smsmith break; 52459854Sbp fp->f_args = unargv(argc, argv); 52559854Sbp loadaddr = fp->f_addr + fp->f_size; 52659854Sbp file_insert_tail(fp); /* Add to the list of loaded files */ 52786469Siedowse if (file_load_dependencies(fp) != 0) { 52859854Sbp err = ENOENT; 52959854Sbp last_file->f_next = NULL; 53059854Sbp loadaddr = last_file->f_addr + last_file->f_size; 53159854Sbp fp = NULL; 53259854Sbp break; 53359854Sbp } 53459854Sbp } while(0); 53538764Smsmith if (err == EFTYPE) 53659854Sbp sprintf(command_errbuf, "don't know how to load module '%s'", filename); 53759854Sbp if (err && fp) 53859854Sbp file_discard(fp); 53959854Sbp free(filename); 54057468Sbp return (err); 54138764Smsmith} 54238764Smsmith 54359854Sbp/* 54459854Sbp * Find a file matching (name) and (type). 54559854Sbp * NULL may be passed as a wildcard to either. 54659854Sbp */ 54759854Sbpstruct preloaded_file * 548294417Sroygerfile_findfile(const char *name, const char *type) 54938764Smsmith{ 55059854Sbp struct preloaded_file *fp; 55138764Smsmith 55259854Sbp for (fp = preloaded_files; fp != NULL; fp = fp->f_next) { 55359854Sbp if (((name == NULL) || !strcmp(name, fp->f_name)) && 55459854Sbp ((type == NULL) || !strcmp(type, fp->f_type))) 55559854Sbp break; 55659854Sbp } 55759854Sbp return (fp); 55838465Smsmith} 55938465Smsmith 56038764Smsmith/* 56159854Sbp * Find a module matching (name) inside of given file. 56259854Sbp * NULL may be passed as a wildcard. 56338764Smsmith */ 56459854Sbpstruct kernel_module * 56583321Speterfile_findmodule(struct preloaded_file *fp, char *modname, 56683321Speter struct mod_depend *verinfo) 56738465Smsmith{ 56883321Speter struct kernel_module *mp, *best; 56983321Speter int bestver, mver; 57059854Sbp 57159854Sbp if (fp == NULL) { 57259854Sbp for (fp = preloaded_files; fp; fp = fp->f_next) { 57383321Speter mp = file_findmodule(fp, modname, verinfo); 57483321Speter if (mp) 57583321Speter return (mp); 57659854Sbp } 57759854Sbp return (NULL); 57838465Smsmith } 57983321Speter best = NULL; 58083321Speter bestver = 0; 58159854Sbp for (mp = fp->f_modules; mp; mp = mp->m_next) { 58283321Speter if (strcmp(modname, mp->m_name) == 0) { 58383321Speter if (verinfo == NULL) 58483321Speter return (mp); 58583321Speter mver = mp->m_version; 58683321Speter if (mver == verinfo->md_ver_preferred) 58783321Speter return (mp); 58883321Speter if (mver >= verinfo->md_ver_minimum && 58983321Speter mver <= verinfo->md_ver_maximum && 59083321Speter mver > bestver) { 59183321Speter best = mp; 59283321Speter bestver = mver; 59383321Speter } 59483321Speter } 59559854Sbp } 59683321Speter return (best); 59738465Smsmith} 59838764Smsmith/* 59938764Smsmith * Make a copy of (size) bytes of data from (p), and associate them as 60038764Smsmith * metadata of (type) to the module (mp). 60138764Smsmith */ 60238712Smsmithvoid 60359854Sbpfile_addmetadata(struct preloaded_file *fp, int type, size_t size, void *p) 60438712Smsmith{ 60559854Sbp struct file_metadata *md; 60638712Smsmith 60764187Sjhb md = malloc(sizeof(struct file_metadata) - sizeof(md->md_data) + size); 60838712Smsmith md->md_size = size; 60938712Smsmith md->md_type = type; 61038712Smsmith bcopy(p, md->md_data, size); 61159854Sbp md->md_next = fp->f_metadata; 61259854Sbp fp->f_metadata = md; 61338712Smsmith} 61438712Smsmith 61538764Smsmith/* 61659854Sbp * Find a metadata object of (type) associated with the file (fp) 61738764Smsmith */ 61859854Sbpstruct file_metadata * 61959854Sbpfile_findmetadata(struct preloaded_file *fp, int type) 62038712Smsmith{ 62159854Sbp struct file_metadata *md; 62238712Smsmith 62359854Sbp for (md = fp->f_metadata; md != NULL; md = md->md_next) 62438712Smsmith if (md->md_type == type) 62538712Smsmith break; 62638712Smsmith return(md); 62738712Smsmith} 62838764Smsmith 62959854Sbpstruct file_metadata * 63059854Sbpmetadata_next(struct file_metadata *md, int type) 63157468Sbp{ 63257468Sbp if (md == NULL) 63357468Sbp return (NULL); 63457468Sbp while((md = md->md_next) != NULL) 63557468Sbp if (md->md_type == type) 63657468Sbp break; 63757468Sbp return (md); 63857468Sbp} 63957468Sbp 64083321Speterstatic char *emptyextlist[] = { "", NULL }; 64183321Speter 64238764Smsmith/* 64383321Speter * Check if the given file is in place and return full path to it. 64483321Speter */ 64583321Speterstatic char * 64683321Speterfile_lookup(const char *path, const char *name, int namelen, char **extlist) 64783321Speter{ 64883321Speter struct stat st; 64983321Speter char *result, *cp, **cpp; 65083321Speter int pathlen, extlen, len; 65183321Speter 65283321Speter pathlen = strlen(path); 65383321Speter extlen = 0; 65483321Speter if (extlist == NULL) 65583321Speter extlist = emptyextlist; 65683321Speter for (cpp = extlist; *cpp; cpp++) { 65783321Speter len = strlen(*cpp); 65883321Speter if (len > extlen) 65983321Speter extlen = len; 66083321Speter } 66183321Speter result = malloc(pathlen + namelen + extlen + 2); 66283321Speter if (result == NULL) 66383321Speter return (NULL); 66483321Speter bcopy(path, result, pathlen); 66583321Speter if (pathlen > 0 && result[pathlen - 1] != '/') 66683321Speter result[pathlen++] = '/'; 66783321Speter cp = result + pathlen; 66883321Speter bcopy(name, cp, namelen); 66983321Speter cp += namelen; 67083321Speter for (cpp = extlist; *cpp; cpp++) { 67183321Speter strcpy(cp, *cpp); 67283321Speter if (stat(result, &st) == 0 && S_ISREG(st.st_mode)) 67383321Speter return result; 67483321Speter } 67583321Speter free(result); 67683321Speter return NULL; 67783321Speter} 67883321Speter 67983321Speter/* 68083321Speter * Check if file name have any qualifiers 68183321Speter */ 68283321Speterstatic int 68383321Speterfile_havepath(const char *name) 68483321Speter{ 68583321Speter const char *cp; 68683321Speter 68783321Speter archsw.arch_getdev(NULL, name, &cp); 68883321Speter return (cp != name || strchr(name, '/') != NULL); 68983321Speter} 69083321Speter 69183321Speter/* 69239178Smsmith * Attempt to find the file (name) on the module searchpath. 69338764Smsmith * If (name) is qualified in any way, we simply check it and 69438764Smsmith * return it or NULL. If it is not qualified, then we attempt 69538764Smsmith * to construct a path using entries in the environment variable 69638764Smsmith * module_path. 69738764Smsmith * 69838764Smsmith * The path we return a pointer to need never be freed, as we manage 69938764Smsmith * it internally. 70038764Smsmith */ 70138764Smsmithstatic char * 70283321Speterfile_search(const char *name, char **extlist) 70338764Smsmith{ 70483321Speter struct moduledir *mdp; 70583321Speter struct stat sb; 70644570Sdcs char *result; 70783321Speter int namelen; 70838764Smsmith 70938764Smsmith /* Don't look for nothing */ 71044210Sdcs if (name == NULL) 71183321Speter return(NULL); 71238764Smsmith 71344210Sdcs if (*name == 0) 71444210Sdcs return(strdup(name)); 71544210Sdcs 71683321Speter if (file_havepath(name)) { 71738764Smsmith /* Qualified, so just see if it exists */ 71838764Smsmith if (stat(name, &sb) == 0) 71944210Sdcs return(strdup(name)); 72038764Smsmith return(NULL); 72138764Smsmith } 72283321Speter moduledir_rebuild(); 72344570Sdcs result = NULL; 72483321Speter namelen = strlen(name); 72583321Speter STAILQ_FOREACH(mdp, &moduledir_list, d_link) { 72683321Speter result = file_lookup(mdp->d_path, name, namelen, extlist); 72783321Speter if (result) 72838764Smsmith break; 72938764Smsmith } 73038764Smsmith return(result); 73138764Smsmith} 73238764Smsmith 73383321Speter#define INT_ALIGN(base, ptr) ptr = \ 73483321Speter (base) + (((ptr) - (base) + sizeof(int) - 1) & ~(sizeof(int) - 1)) 73583321Speter 73683321Speterstatic char * 73783321Spetermod_search_hints(struct moduledir *mdp, const char *modname, 73883321Speter struct mod_depend *verinfo) 73983321Speter{ 74083321Speter u_char *cp, *recptr, *bufend, *best; 74183321Speter char *result; 74283321Speter int *intp, bestver, blen, clen, found, ival, modnamelen, reclen; 74383321Speter 74483321Speter moduledir_readhints(mdp); 74583321Speter modnamelen = strlen(modname); 74683321Speter found = 0; 74783321Speter result = NULL; 74883321Speter bestver = 0; 74983321Speter if (mdp->d_hints == NULL) 75083321Speter goto bad; 75183321Speter recptr = mdp->d_hints; 75283321Speter bufend = recptr + mdp->d_hintsz; 75383321Speter clen = blen = 0; 75483321Speter best = cp = NULL; 75583321Speter while (recptr < bufend && !found) { 75683321Speter intp = (int*)recptr; 75783321Speter reclen = *intp++; 75883321Speter ival = *intp++; 75983321Speter cp = (char*)intp; 76083321Speter switch (ival) { 76183321Speter case MDT_VERSION: 76283321Speter clen = *cp++; 76383321Speter if (clen != modnamelen || bcmp(cp, modname, clen) != 0) 76483321Speter break; 76583321Speter cp += clen; 76683321Speter INT_ALIGN(mdp->d_hints, cp); 76783321Speter ival = *(int*)cp; 76883321Speter cp += sizeof(int); 76983321Speter clen = *cp++; 77083321Speter if (verinfo == NULL || ival == verinfo->md_ver_preferred) { 77183321Speter found = 1; 77283321Speter break; 77383321Speter } 77483321Speter if (ival >= verinfo->md_ver_minimum && 77583321Speter ival <= verinfo->md_ver_maximum && 77683321Speter ival > bestver) { 77783321Speter bestver = ival; 77883321Speter best = cp; 77983321Speter blen = clen; 78083321Speter } 78183321Speter break; 78283321Speter default: 78383321Speter break; 78483321Speter } 78583321Speter recptr += reclen + sizeof(int); 78683321Speter } 78783321Speter /* 78883321Speter * Finally check if KLD is in the place 78983321Speter */ 79083321Speter if (found) 79183321Speter result = file_lookup(mdp->d_path, cp, clen, NULL); 79283321Speter else if (best) 79383321Speter result = file_lookup(mdp->d_path, best, blen, NULL); 79483321Speterbad: 79583321Speter /* 79683321Speter * If nothing found or hints is absent - fallback to the old way 79783321Speter * by using "kldname[.ko]" as module name. 79883321Speter */ 79983321Speter if (!found && !bestver && result == NULL) 80083321Speter result = file_lookup(mdp->d_path, modname, modnamelen, kld_ext_list); 80183321Speter return result; 80283321Speter} 80383321Speter 80438764Smsmith/* 80539178Smsmith * Attempt to locate the file containing the module (name) 80639178Smsmith */ 80739178Smsmithstatic char * 80883321Spetermod_searchmodule(char *name, struct mod_depend *verinfo) 80939178Smsmith{ 81083321Speter struct moduledir *mdp; 81183321Speter char *result; 81239178Smsmith 81383321Speter moduledir_rebuild(); 81483321Speter /* 81583321Speter * Now we ready to lookup module in the given directories 81683321Speter */ 81783321Speter result = NULL; 81883321Speter STAILQ_FOREACH(mdp, &moduledir_list, d_link) { 81983321Speter result = mod_search_hints(mdp, name, verinfo); 82083321Speter if (result) 82183321Speter break; 82283321Speter } 82383321Speter 82439178Smsmith return(result); 82539178Smsmith} 82639178Smsmith 82759854Sbpint 82883321Speterfile_addmodule(struct preloaded_file *fp, char *modname, int version, 82959854Sbp struct kernel_module **newmp) 83059854Sbp{ 83159854Sbp struct kernel_module *mp; 83283321Speter struct mod_depend mdepend; 83339178Smsmith 83483321Speter bzero(&mdepend, sizeof(mdepend)); 83583321Speter mdepend.md_ver_preferred = version; 83683321Speter mp = file_findmodule(fp, modname, &mdepend); 83759854Sbp if (mp) 83859854Sbp return (EEXIST); 83959854Sbp mp = malloc(sizeof(struct kernel_module)); 84059854Sbp if (mp == NULL) 84159854Sbp return (ENOMEM); 84259854Sbp bzero(mp, sizeof(struct kernel_module)); 84359854Sbp mp->m_name = strdup(modname); 84483321Speter mp->m_version = version; 84559854Sbp mp->m_fp = fp; 84659854Sbp mp->m_next = fp->f_modules; 84759854Sbp fp->f_modules = mp; 84859854Sbp if (newmp) 84959854Sbp *newmp = mp; 85059854Sbp return (0); 85159854Sbp} 85259854Sbp 85339178Smsmith/* 85459854Sbp * Throw a file away 85538764Smsmith */ 85638764Smsmithvoid 85759854Sbpfile_discard(struct preloaded_file *fp) 85838764Smsmith{ 85959854Sbp struct file_metadata *md, *md1; 86059854Sbp struct kernel_module *mp, *mp1; 86159854Sbp if (fp == NULL) 86259854Sbp return; 86359854Sbp md = fp->f_metadata; 86459854Sbp while (md) { 86559854Sbp md1 = md; 86659854Sbp md = md->md_next; 86759854Sbp free(md1); 86859854Sbp } 86959854Sbp mp = fp->f_modules; 87059854Sbp while (mp) { 87159854Sbp if (mp->m_name) 87239178Smsmith free(mp->m_name); 87359854Sbp mp1 = mp; 87459854Sbp mp = mp->m_next; 87559854Sbp free(mp1); 87659854Sbp } 87759854Sbp if (fp->f_name != NULL) 87859854Sbp free(fp->f_name); 87959854Sbp if (fp->f_type != NULL) 88059854Sbp free(fp->f_type); 88159854Sbp if (fp->f_args != NULL) 88259854Sbp free(fp->f_args); 88359854Sbp free(fp); 88438764Smsmith} 88538764Smsmith 88638764Smsmith/* 88759854Sbp * Allocate a new file; must be used instead of malloc() 88839178Smsmith * to ensure safe initialisation. 88939178Smsmith */ 89059854Sbpstruct preloaded_file * 89159854Sbpfile_alloc(void) 89239178Smsmith{ 89359854Sbp struct preloaded_file *fp; 89439178Smsmith 89559854Sbp if ((fp = malloc(sizeof(struct preloaded_file))) != NULL) { 89659854Sbp bzero(fp, sizeof(struct preloaded_file)); 89739178Smsmith } 89859854Sbp return (fp); 89939178Smsmith} 90039178Smsmith 90139178Smsmith/* 90238764Smsmith * Add a module to the chain 90338764Smsmith */ 90438764Smsmithstatic void 90559854Sbpfile_insert_tail(struct preloaded_file *fp) 90638764Smsmith{ 90759854Sbp struct preloaded_file *cm; 90838764Smsmith 90959854Sbp /* Append to list of loaded file */ 91059854Sbp fp->f_next = NULL; 91159854Sbp if (preloaded_files == NULL) { 91259854Sbp preloaded_files = fp; 91338764Smsmith } else { 91459854Sbp for (cm = preloaded_files; cm->f_next != NULL; cm = cm->f_next) 91538764Smsmith ; 91659854Sbp cm->f_next = fp; 91738764Smsmith } 91838764Smsmith} 91959854Sbp 92083321Speterstatic char * 92183321Spetermoduledir_fullpath(struct moduledir *mdp, const char *fname) 92283321Speter{ 92383321Speter char *cp; 92483321Speter 92583321Speter cp = malloc(strlen(mdp->d_path) + strlen(fname) + 2); 92683321Speter if (cp == NULL) 92783321Speter return NULL; 92883321Speter strcpy(cp, mdp->d_path); 92983321Speter strcat(cp, "/"); 93083321Speter strcat(cp, fname); 93183321Speter return (cp); 93283321Speter} 93383321Speter 93483321Speter/* 93583321Speter * Read linker.hints file into memory performing some sanity checks. 93683321Speter */ 93783321Speterstatic void 93883321Spetermoduledir_readhints(struct moduledir *mdp) 93983321Speter{ 94083321Speter struct stat st; 94183321Speter char *path; 94283321Speter int fd, size, version; 94383321Speter 94483321Speter if (mdp->d_hints != NULL || (mdp->d_flags & MDIR_NOHINTS)) 94583321Speter return; 94683321Speter path = moduledir_fullpath(mdp, "linker.hints"); 947154257Smarius if (stat(path, &st) != 0 || 948154257Smarius st.st_size < (ssize_t)(sizeof(version) + sizeof(int)) || 94983321Speter st.st_size > 100 * 1024 || (fd = open(path, O_RDONLY)) < 0) { 95083321Speter free(path); 95183321Speter mdp->d_flags |= MDIR_NOHINTS; 95283321Speter return; 95383321Speter } 95483321Speter free(path); 95583321Speter size = read(fd, &version, sizeof(version)); 95683321Speter if (size != sizeof(version) || version != LINKER_HINTS_VERSION) 95783321Speter goto bad; 95883321Speter size = st.st_size - size; 95983321Speter mdp->d_hints = malloc(size); 96083321Speter if (mdp->d_hints == NULL) 96183321Speter goto bad; 96283321Speter if (read(fd, mdp->d_hints, size) != size) 96383321Speter goto bad; 96483321Speter mdp->d_hintsz = size; 96583321Speter close(fd); 96683321Speter return; 96783321Speterbad: 96883321Speter close(fd); 96983321Speter if (mdp->d_hints) { 97083321Speter free(mdp->d_hints); 97183321Speter mdp->d_hints = NULL; 97283321Speter } 97383321Speter mdp->d_flags |= MDIR_NOHINTS; 97483321Speter return; 97583321Speter} 97683321Speter 97783321Speter/* 97883321Speter * Extract directories from the ';' separated list, remove duplicates. 97983321Speter */ 98083321Speterstatic void 98183321Spetermoduledir_rebuild(void) 98283321Speter{ 98383321Speter struct moduledir *mdp, *mtmp; 98483321Speter const char *path, *cp, *ep; 985294981Ssmh size_t cplen; 98683321Speter 98783321Speter path = getenv("module_path"); 98883321Speter if (path == NULL) 98983321Speter path = default_searchpath; 99083321Speter /* 99183321Speter * Rebuild list of module directories if it changed 99283321Speter */ 99383321Speter STAILQ_FOREACH(mdp, &moduledir_list, d_link) 99483321Speter mdp->d_flags |= MDIR_REMOVED; 99583321Speter 99683321Speter for (ep = path; *ep != 0; ep++) { 99783321Speter cp = ep; 99883321Speter for (; *ep != 0 && *ep != ';'; ep++) 99983321Speter ; 100083321Speter /* 100183321Speter * Ignore trailing slashes 100283321Speter */ 100383321Speter for (cplen = ep - cp; cplen > 1 && cp[cplen - 1] == '/'; cplen--) 100483321Speter ; 100583321Speter STAILQ_FOREACH(mdp, &moduledir_list, d_link) { 100683321Speter if (strlen(mdp->d_path) != cplen || bcmp(cp, mdp->d_path, cplen) != 0) 100783321Speter continue; 100883321Speter mdp->d_flags &= ~MDIR_REMOVED; 100983321Speter break; 101083321Speter } 101183321Speter if (mdp == NULL) { 101283321Speter mdp = malloc(sizeof(*mdp) + cplen + 1); 101383321Speter if (mdp == NULL) 101483321Speter return; 101583321Speter mdp->d_path = (char*)(mdp + 1); 101683321Speter bcopy(cp, mdp->d_path, cplen); 101783321Speter mdp->d_path[cplen] = 0; 101883321Speter mdp->d_hints = NULL; 101983321Speter mdp->d_flags = 0; 102083321Speter STAILQ_INSERT_TAIL(&moduledir_list, mdp, d_link); 102183321Speter } 102294419Speter if (*ep == 0) 102394419Speter break; 102483321Speter } 102583321Speter /* 102683321Speter * Delete unused directories if any 102783321Speter */ 102883321Speter mdp = STAILQ_FIRST(&moduledir_list); 102983321Speter while (mdp) { 103083321Speter if ((mdp->d_flags & MDIR_REMOVED) == 0) { 103183321Speter mdp = STAILQ_NEXT(mdp, d_link); 103283321Speter } else { 103383321Speter if (mdp->d_hints) 103483321Speter free(mdp->d_hints); 103583321Speter mtmp = mdp; 103683321Speter mdp = STAILQ_NEXT(mdp, d_link); 103783321Speter STAILQ_REMOVE(&moduledir_list, mtmp, moduledir, d_link); 103883321Speter free(mtmp); 103983321Speter } 104083321Speter } 104183321Speter return; 104283321Speter} 1043