module.c revision 273658
118334Speter/*- 290075Sobrien * Copyright (c) 1998 Michael Smith <msmith@freebsd.org> 3169689Skan * All rights reserved. 4169689Skan * 518334Speter * Redistribution and use in source and binary forms, with or without 690075Sobrien * modification, are permitted provided that the following conditions 718334Speter * are met: 890075Sobrien * 1. Redistributions of source code must retain the above copyright 990075Sobrien * notice, this list of conditions and the following disclaimer. 1090075Sobrien * 2. Redistributions in binary form must reproduce the above copyright 1190075Sobrien * notice, this list of conditions and the following disclaimer in the 1218334Speter * documentation and/or other materials provided with the distribution. 1390075Sobrien * 1490075Sobrien * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1590075Sobrien * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1690075Sobrien * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1718334Speter * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 1818334Speter * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 1990075Sobrien * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20169689Skan * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21169689Skan * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2218334Speter * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2318334Speter * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2418334Speter * SUCH DAMAGE. 2518334Speter */ 2618334Speter 2718334Speter#include <sys/cdefs.h> 2818334Speter__FBSDID("$FreeBSD: stable/10/sys/boot/common/module.c 273658 2014-10-26 02:21:54Z ian $"); 2918334Speter 3018334Speter/* 3118334Speter * file/module function dispatcher, support, etc. 3218334Speter */ 3318334Speter 3418334Speter#include <stand.h> 3518334Speter#include <string.h> 3618334Speter#include <sys/param.h> 3718334Speter#include <sys/linker.h> 3818334Speter#include <sys/module.h> 3918334Speter#include <sys/queue.h> 4018334Speter 4118334Speter#include "bootstrap.h" 4218334Speter 4318334Speter#define MDIR_REMOVED 0x0001 4418334Speter#define MDIR_NOHINTS 0x0002 4518334Speter 4618334Speterstruct moduledir { 4718334Speter char *d_path; /* path of modules directory */ 4818334Speter u_char *d_hints; /* content of linker.hints file */ 4918334Speter int d_hintsz; /* size of hints data */ 5018334Speter int d_flags; 5118334Speter STAILQ_ENTRY(moduledir) d_link; 5290075Sobrien}; 5318334Speter 5418334Speterstatic int file_load(char *filename, vm_offset_t dest, struct preloaded_file **result); 5518334Speterstatic int file_load_dependencies(struct preloaded_file *base_mod); 5618334Speterstatic char * file_search(const char *name, char **extlist); 5718334Speterstatic struct kernel_module * file_findmodule(struct preloaded_file *fp, char *modname, struct mod_depend *verinfo); 5818334Speterstatic int file_havepath(const char *name); 5952284Sobrienstatic char *mod_searchmodule(char *name, struct mod_depend *verinfo); 6052284Sobrienstatic void file_insert_tail(struct preloaded_file *mp); 6150397Sobrienstruct file_metadata* metadata_next(struct file_metadata *base_mp, int type); 6250397Sobrienstatic void moduledir_readhints(struct moduledir *mdp); 6318334Speterstatic void moduledir_rebuild(void); 6450397Sobrien 65132718Skan/* load address should be tweaked by first module loaded (kernel) */ 66132718Skanstatic vm_offset_t loadaddr = 0; 67117395Skan 6818334Speter#if defined(LOADER_FDT_SUPPORT) 6990075Sobrienstatic const char *default_searchpath = 7018334Speter "/boot/kernel;/boot/modules;/boot/dtb"; 7118334Speter#else 7290075Sobrienstatic const char *default_searchpath ="/boot/kernel;/boot/modules"; 7318334Speter#endif 7450397Sobrien 7518334Speterstatic STAILQ_HEAD(, moduledir) moduledir_list = STAILQ_HEAD_INITIALIZER(moduledir_list); 7618334Speter 7750397Sobrienstruct preloaded_file *preloaded_files = NULL; 7890075Sobrien 7990075Sobrienstatic char *kld_ext_list[] = { 80169689Skan ".ko", 81169689Skan "", 82169689Skan ".debug", 83169689Skan NULL 8418334Speter}; 8518334Speter 8618334Speter 8718334Speter/* 8818334Speter * load an object, either a disk file or code module. 8990075Sobrien * 9090075Sobrien * To load a file, the syntax is: 9190075Sobrien * 9290075Sobrien * load -t <type> <path> 9318334Speter * 9490075Sobrien * code modules are loaded as: 9518334Speter * 9690075Sobrien * load <path> <options> 9718334Speter */ 9890075Sobrien 9918334SpeterCOMMAND_SET(load, "load", "load a kernel or module", command_load); 10090075Sobrien 10190075Sobrienstatic int 10218334Spetercommand_load(int argc, char *argv[]) 10390075Sobrien{ 10418334Speter char *typestr; 10590075Sobrien int dofile, dokld, ch, error; 10690075Sobrien 10790075Sobrien dokld = dofile = 0; 10890075Sobrien optind = 1; 10990075Sobrien optreset = 1; 11018334Speter typestr = NULL; 11190075Sobrien if (argc == 1) { 11218334Speter command_errmsg = "no filename specified"; 11390075Sobrien return(CMD_ERROR); 11490075Sobrien } 115132718Skan while ((ch = getopt(argc, argv, "kt:")) != -1) { 11690075Sobrien switch(ch) { 11790075Sobrien case 'k': 11890075Sobrien dokld = 1; 11990075Sobrien break; 12018334Speter case 't': 12190075Sobrien typestr = optarg; 12218334Speter dofile = 1; 12390075Sobrien break; 12418334Speter case '?': 12590075Sobrien default: 12618334Speter /* getopt has already reported an error */ 127161651Skan return(CMD_OK); 128161651Skan } 129161651Skan } 130161651Skan argv += (optind - 1); 131161651Skan argc -= (optind - 1); 13290075Sobrien 13390075Sobrien /* 13490075Sobrien * Request to load a raw file? 13518334Speter */ 13690075Sobrien if (dofile) { 13718334Speter if ((argc != 2) || (typestr == NULL) || (*typestr == 0)) { 13890075Sobrien command_errmsg = "invalid load type"; 13990075Sobrien return(CMD_ERROR); 14090075Sobrien } 14118334Speter return(file_loadraw(argv[1], typestr) ? CMD_OK : CMD_ERROR); 14290075Sobrien } 14318334Speter /* 14490075Sobrien * Do we have explicit KLD load ? 14590075Sobrien */ 14618334Speter if (dokld || file_havepath(argv[1])) { 14790075Sobrien error = mod_loadkld(argv[1], argc - 2, argv + 2); 14818334Speter if (error == EEXIST) 14990075Sobrien sprintf(command_errbuf, "warning: KLD '%s' already loaded", argv[1]); 15090075Sobrien return (error == 0 ? CMD_OK : CMD_ERROR); 15118334Speter } 15290075Sobrien /* 15318334Speter * Looks like a request for a module. 15490075Sobrien */ 15590075Sobrien error = mod_load(argv[1], NULL, argc - 2, argv + 2); 15618334Speter if (error == EEXIST) 15790075Sobrien sprintf(command_errbuf, "warning: module '%s' already loaded", argv[1]); 15890075Sobrien return (error == 0 ? CMD_OK : CMD_ERROR); 15918334Speter} 16090075Sobrien 16118334SpeterCOMMAND_SET(load_geli, "load_geli", "load a geli key", command_load_geli); 16290075Sobrien 16318334Speterstatic int 16490075Sobriencommand_load_geli(int argc, char *argv[]) 16590075Sobrien{ 16690075Sobrien char typestr[80]; 16790075Sobrien char *cp; 16818334Speter int ch, num; 16990075Sobrien 17090075Sobrien if (argc < 3) { 17118334Speter command_errmsg = "usage is [-n key#] <prov> <file>"; 17290075Sobrien return(CMD_ERROR); 17390075Sobrien } 17418334Speter 17590075Sobrien num = 0; 17618334Speter optind = 1; 17790075Sobrien optreset = 1; 17890075Sobrien while ((ch = getopt(argc, argv, "n:")) != -1) { 17918334Speter switch(ch) { 18090075Sobrien case 'n': 18118334Speter num = strtol(optarg, &cp, 0); 18290075Sobrien if (cp == optarg) { 18390075Sobrien sprintf(command_errbuf, "bad key index '%s'", optarg); 18490075Sobrien return(CMD_ERROR); 18590075Sobrien } 18690075Sobrien break; 18790075Sobrien case '?': 18890075Sobrien default: 18990075Sobrien /* getopt has already reported an error */ 19018334Speter return(CMD_OK); 19118334Speter } 19290075Sobrien } 19318334Speter argv += (optind - 1); 19418334Speter argc -= (optind - 1); 19518334Speter sprintf(typestr, "%s:geli_keyfile%d", argv[1], num); 19618334Speter return(file_loadraw(argv[2], typestr) ? CMD_OK : CMD_ERROR); 19718334Speter} 19818334Speter 19918334SpeterCOMMAND_SET(unload, "unload", "unload all modules", command_unload); 20018334Speter 20118334Speterstatic int 20218334Spetercommand_unload(int argc, char *argv[]) 20318334Speter{ 20418334Speter struct preloaded_file *fp; 20518334Speter 20618334Speter while (preloaded_files != NULL) { 20718334Speter fp = preloaded_files; 20818334Speter preloaded_files = preloaded_files->f_next; 20918334Speter file_discard(fp); 21018334Speter } 21118334Speter loadaddr = 0; 21218334Speter unsetenv("kernelname"); 21318334Speter return(CMD_OK); 21418334Speter} 21518334Speter 21618334SpeterCOMMAND_SET(lsmod, "lsmod", "list loaded modules", command_lsmod); 21718334Speter 21818334Speterstatic int 21918334Spetercommand_lsmod(int argc, char *argv[]) 22018334Speter{ 22118334Speter struct preloaded_file *fp; 22218334Speter struct kernel_module *mp; 22318334Speter struct file_metadata *md; 22418334Speter char lbuf[80]; 22518334Speter int ch, verbose; 22618334Speter 22718334Speter verbose = 0; 22818334Speter optind = 1; 22918334Speter optreset = 1; 23018334Speter while ((ch = getopt(argc, argv, "v")) != -1) { 23118334Speter switch(ch) { 23218334Speter case 'v': 23318334Speter verbose = 1; 23418334Speter break; 23518334Speter case '?': 23618334Speter default: 23718334Speter /* getopt has already reported an error */ 23818334Speter return(CMD_OK); 23918334Speter } 24018334Speter } 24118334Speter 24218334Speter pager_open(); 24318334Speter for (fp = preloaded_files; fp; fp = fp->f_next) { 24418334Speter sprintf(lbuf, " %p: %s (%s, 0x%lx)\n", 24518334Speter (void *) fp->f_addr, fp->f_name, fp->f_type, (long) fp->f_size); 24618334Speter pager_output(lbuf); 24718334Speter if (fp->f_args != NULL) { 24818334Speter pager_output(" args: "); 24918334Speter pager_output(fp->f_args); 25090075Sobrien pager_output("\n"); 25190075Sobrien } 25290075Sobrien if (fp->f_modules) { 253102780Skan pager_output(" modules: "); 25450397Sobrien for (mp = fp->f_modules; mp; mp = mp->m_next) { 25590075Sobrien sprintf(lbuf, "%s.%d ", mp->m_name, mp->m_version); 25650397Sobrien pager_output(lbuf); 25790075Sobrien } 25890075Sobrien pager_output("\n"); 25990075Sobrien } 26052284Sobrien if (verbose) { 26190075Sobrien /* XXX could add some formatting smarts here to display some better */ 26290075Sobrien for (md = fp->f_metadata; md != NULL; md = md->md_next) { 263102780Skan sprintf(lbuf, " 0x%04x, 0x%lx\n", md->md_type, (long) md->md_size); 26490075Sobrien pager_output(lbuf); 26590075Sobrien } 26690075Sobrien } 26790075Sobrien } 26890075Sobrien pager_close(); 26990075Sobrien return(CMD_OK); 27090075Sobrien} 27190075Sobrien 27290075Sobrien/* 273169689Skan * File level interface, functions file_* 274169689Skan */ 275169689Skanint 276169689Skanfile_load(char *filename, vm_offset_t dest, struct preloaded_file **result) 27790075Sobrien{ 27890075Sobrien static int last_file_format = 0; 27990075Sobrien struct preloaded_file *fp; 28090075Sobrien int error; 28190075Sobrien int i; 28290075Sobrien 28390075Sobrien if (archsw.arch_loadaddr != NULL) 28452284Sobrien dest = archsw.arch_loadaddr(LOAD_RAW, filename, dest); 28552284Sobrien 28652284Sobrien error = EFTYPE; 287132718Skan for (i = last_file_format, fp = NULL; 288132718Skan file_formats[i] && fp == NULL; i++) { 289132718Skan error = (file_formats[i]->l_load)(filename, dest, &fp); 290132718Skan if (error == 0) { 291132718Skan fp->f_loader = last_file_format = i; /* remember the loader */ 292132718Skan *result = fp; 293132718Skan break; 294132718Skan } else if (last_file_format == i && i != 0) { 295132718Skan /* Restart from the beginning */ 296132718Skan i = -1; 297132718Skan last_file_format = 0; 298132718Skan fp = NULL; 299132718Skan continue; 300132718Skan } 301132718Skan if (error == EFTYPE) 302132718Skan continue; /* Unknown to this handler? */ 303132718Skan if (error) { 304132718Skan sprintf(command_errbuf, "can't load file '%s': %s", 305132718Skan filename, strerror(error)); 306132718Skan break; 307132718Skan } 308132718Skan } 309132718Skan return (error); 310132718Skan} 311132718Skan 312132718Skanstatic int 313132718Skanfile_load_dependencies(struct preloaded_file *base_file) 31418334Speter{ 31518334Speter struct file_metadata *md; 31618334Speter struct preloaded_file *fp; 31718334Speter struct mod_depend *verinfo; 31818334Speter struct kernel_module *mp; 31918334Speter char *dmodname; 320132718Skan int error; 32118334Speter 32290075Sobrien md = file_findmetadata(base_file, MODINFOMD_DEPLIST); 32318334Speter if (md == NULL) 32490075Sobrien return (0); 32518334Speter error = 0; 32618334Speter do { 32718334Speter verinfo = (struct mod_depend*)md->md_data; 32890075Sobrien dmodname = (char *)(verinfo + 1); 32990075Sobrien if (file_findmodule(NULL, dmodname, verinfo) == NULL) { 33090075Sobrien printf("loading required module '%s'\n", dmodname); 33190075Sobrien error = mod_load(dmodname, verinfo, 0, NULL); 33290075Sobrien if (error) 333161651Skan break; 33490075Sobrien /* 33590075Sobrien * If module loaded via kld name which isn't listed 33690075Sobrien * in the linker.hints file, we should check if it have 33790075Sobrien * required version. 33818334Speter */ 33918334Speter mp = file_findmodule(NULL, dmodname, verinfo); 34018334Speter if (mp == NULL) { 34118334Speter sprintf(command_errbuf, "module '%s' exists but with wrong version", 342169689Skan dmodname); 343132718Skan error = ENOENT; 34418334Speter break; 345117395Skan } 34618334Speter } 347117395Skan md = metadata_next(md, MODINFOMD_DEPLIST); 34818334Speter } while (md); 34952284Sobrien if (!error) 35052284Sobrien return (0); 35152284Sobrien /* Load failed; discard everything */ 35252284Sobrien while (base_file != NULL) { 35318334Speter fp = base_file; 35418334Speter base_file = base_file->f_next; 35518334Speter file_discard(fp); 35618334Speter } 35718334Speter return (error); 35818334Speter} 35918334Speter 36018334Speter/* 36118334Speter * We've been asked to load (name) as (type), so just suck it in, 362169689Skan * no arguments or anything. 36318334Speter */ 36418334Speterstruct preloaded_file * 36552284Sobrienfile_loadraw(char *name, char *type) 36652284Sobrien{ 36718334Speter struct preloaded_file *fp; 36818334Speter char *cp; 36918334Speter int fd, got; 37018334Speter vm_offset_t laddr; 37118334Speter 372169689Skan /* We can't load first */ 373169689Skan if ((file_findfile(NULL, NULL)) == NULL) { 374169689Skan command_errmsg = "can't load file before kernel"; 375169689Skan return(NULL); 376169689Skan } 37718334Speter 378169689Skan /* locate the file on the load path */ 379169689Skan cp = file_search(name, NULL); 380169689Skan if (cp == NULL) { 38118334Speter sprintf(command_errbuf, "can't find '%s'", name); 38218334Speter return(NULL); 38318334Speter } 38490075Sobrien name = cp; 38518334Speter 38618334Speter if ((fd = open(name, O_RDONLY)) < 0) { 38718334Speter sprintf(command_errbuf, "can't open '%s': %s", name, strerror(errno)); 38818334Speter free(name); 38918334Speter return(NULL); 39018334Speter } 39190075Sobrien 39218334Speter if (archsw.arch_loadaddr != NULL) 39318334Speter loadaddr = archsw.arch_loadaddr(LOAD_RAW, name, loadaddr); 39418334Speter 39518334Speter laddr = loadaddr; 39618334Speter for (;;) { 39718334Speter /* read in 4k chunks; size is not really important */ 39818334Speter got = archsw.arch_readin(fd, laddr, 4096); 39918334Speter if (got == 0) /* end of file */ 40018334Speter break; 40118334Speter if (got < 0) { /* error */ 402117395Skan sprintf(command_errbuf, "error reading '%s': %s", name, strerror(errno)); 40318334Speter free(name); 40418334Speter close(fd); 40518334Speter return(NULL); 40618334Speter } 40718334Speter laddr += got; 40818334Speter } 40918334Speter 41018334Speter /* Looks OK so far; create & populate control structure */ 41118334Speter fp = file_alloc(); 41218334Speter fp->f_name = strdup(name); 41318334Speter fp->f_type = strdup(type); 41418334Speter fp->f_args = NULL; 41518334Speter fp->f_metadata = NULL; 41618334Speter fp->f_loader = -1; 41718334Speter fp->f_addr = loadaddr; 41818334Speter fp->f_size = laddr - loadaddr; 41918334Speter 42018334Speter /* recognise space consumption */ 42118334Speter loadaddr = laddr; 42218334Speter 42318334Speter /* Add to the list of loaded files */ 424132718Skan file_insert_tail(fp); 42518334Speter close(fd); 42618334Speter return(fp); 42718334Speter} 42818334Speter 42918334Speter/* 43018334Speter * Load the module (name), pass it (argc),(argv), add container file 43118334Speter * to the list of loaded files. 43218334Speter * If module is already loaded just assign new argc/argv. 43318334Speter */ 434117395Skanint 43518334Spetermod_load(char *modname, struct mod_depend *verinfo, int argc, char *argv[]) 43650397Sobrien{ 43790075Sobrien struct kernel_module *mp; 43890075Sobrien int err; 43990075Sobrien char *filename; 44090075Sobrien 44190075Sobrien if (file_havepath(modname)) { 44290075Sobrien printf("Warning: mod_load() called instead of mod_loadkld() for module '%s'\n", modname); 44350397Sobrien return (mod_loadkld(modname, argc, argv)); 44450397Sobrien } 44550397Sobrien /* see if module is already loaded */ 44690075Sobrien mp = file_findmodule(NULL, modname, verinfo); 44752284Sobrien if (mp) { 44818334Speter#ifdef moduleargs 44918334Speter if (mp->m_args) 45018334Speter free(mp->m_args); 45118334Speter mp->m_args = unargv(argc, argv); 45218334Speter#endif 45318334Speter sprintf(command_errbuf, "warning: module '%s' already loaded", mp->m_name); 45418334Speter return (0); 45518334Speter } 45618334Speter /* locate file with the module on the search path */ 45718334Speter filename = mod_searchmodule(modname, verinfo); 45818334Speter if (filename == NULL) { 45918334Speter sprintf(command_errbuf, "can't find '%s'", modname); 46018334Speter return (ENOENT); 461132718Skan } 462132718Skan err = mod_loadkld(filename, argc, argv); 46318334Speter return (err); 464169689Skan} 46518334Speter 466169689Skan/* 46750397Sobrien * Load specified KLD. If path is omitted, then try to locate it via 46818334Speter * search path. 46918334Speter */ 47018334Speterint 47118334Spetermod_loadkld(const char *kldname, int argc, char *argv[]) 47218334Speter{ 47318334Speter struct preloaded_file *fp, *last_file; 47418334Speter int err; 47518334Speter char *filename; 47618334Speter 47718334Speter /* 47818334Speter * Get fully qualified KLD name 479132718Skan */ 48018334Speter filename = file_search(kldname, kld_ext_list); 48118334Speter if (filename == NULL) { 48218334Speter sprintf(command_errbuf, "can't find '%s'", kldname); 48318334Speter return (ENOENT); 48418334Speter } 48518334Speter /* 48618334Speter * Check if KLD already loaded 48718334Speter */ 48818334Speter fp = file_findfile(filename, NULL); 48918334Speter if (fp) { 49018334Speter sprintf(command_errbuf, "warning: KLD '%s' already loaded", filename); 49118334Speter free(filename); 49218334Speter return (0); 49318334Speter } 49490075Sobrien for (last_file = preloaded_files; 49518334Speter last_file != NULL && last_file->f_next != NULL; 49618334Speter last_file = last_file->f_next) 49718334Speter ; 49818334Speter 49918334Speter do { 500169689Skan err = file_load(filename, loadaddr, &fp); 50190075Sobrien if (err) 50218334Speter break; 50318334Speter fp->f_args = unargv(argc, argv); 50490075Sobrien loadaddr = fp->f_addr + fp->f_size; 50518334Speter file_insert_tail(fp); /* Add to the list of loaded files */ 50618334Speter if (file_load_dependencies(fp) != 0) { 50718334Speter err = ENOENT; 50818334Speter last_file->f_next = NULL; 50918334Speter loadaddr = last_file->f_addr + last_file->f_size; 51018334Speter fp = NULL; 51118334Speter break; 51218334Speter } 51318334Speter } while(0); 514169689Skan if (err == EFTYPE) 51518334Speter sprintf(command_errbuf, "don't know how to load module '%s'", filename); 51618334Speter if (err && fp) 51718334Speter file_discard(fp); 51818334Speter free(filename); 51918334Speter return (err); 52018334Speter} 52150397Sobrien 52290075Sobrien/* 52350397Sobrien * Find a file matching (name) and (type). 52450397Sobrien * NULL may be passed as a wildcard to either. 525132718Skan */ 52650397Sobrienstruct preloaded_file * 52790075Sobrienfile_findfile(char *name, char *type) 52890075Sobrien{ 52990075Sobrien struct preloaded_file *fp; 53090075Sobrien 53190075Sobrien for (fp = preloaded_files; fp != NULL; fp = fp->f_next) { 53290075Sobrien if (((name == NULL) || !strcmp(name, fp->f_name)) && 53390075Sobrien ((type == NULL) || !strcmp(type, fp->f_type))) 534169689Skan break; 53590075Sobrien } 53690075Sobrien return (fp); 53790075Sobrien} 53890075Sobrien 53996263Sobrien/* 54090075Sobrien * Find a module matching (name) inside of given file. 54190075Sobrien * NULL may be passed as a wildcard. 54290075Sobrien */ 54390075Sobrienstruct kernel_module * 54490075Sobrienfile_findmodule(struct preloaded_file *fp, char *modname, 54590075Sobrien struct mod_depend *verinfo) 54690075Sobrien{ 54790075Sobrien struct kernel_module *mp, *best; 54890075Sobrien int bestver, mver; 54990075Sobrien 55090075Sobrien if (fp == NULL) { 551132718Skan for (fp = preloaded_files; fp; fp = fp->f_next) { 55290075Sobrien mp = file_findmodule(fp, modname, verinfo); 55390075Sobrien if (mp) 55490075Sobrien return (mp); 55590075Sobrien } 55690075Sobrien return (NULL); 55790075Sobrien } 55890075Sobrien best = NULL; 55990075Sobrien bestver = 0; 56090075Sobrien for (mp = fp->f_modules; mp; mp = mp->m_next) { 56190075Sobrien if (strcmp(modname, mp->m_name) == 0) { 56290075Sobrien if (verinfo == NULL) 56390075Sobrien return (mp); 56490075Sobrien mver = mp->m_version; 56590075Sobrien if (mver == verinfo->md_ver_preferred) 56690075Sobrien return (mp); 56790075Sobrien if (mver >= verinfo->md_ver_minimum && 56890075Sobrien mver <= verinfo->md_ver_maximum && 56990075Sobrien mver > bestver) { 57090075Sobrien best = mp; 57190075Sobrien bestver = mver; 57290075Sobrien } 57390075Sobrien } 57490075Sobrien } 575117395Skan return (best); 57690075Sobrien} 57790075Sobrien/* 57890075Sobrien * Make a copy of (size) bytes of data from (p), and associate them as 57990075Sobrien * metadata of (type) to the module (mp). 58090075Sobrien */ 581132718Skanvoid 58290075Sobrienfile_addmetadata(struct preloaded_file *fp, int type, size_t size, void *p) 58350397Sobrien{ 58490075Sobrien struct file_metadata *md; 58550397Sobrien 58650397Sobrien md = malloc(sizeof(struct file_metadata) - sizeof(md->md_data) + size); 58750397Sobrien md->md_size = size; 58850397Sobrien md->md_type = type; 58990075Sobrien bcopy(p, md->md_data, size); 59090075Sobrien md->md_next = fp->f_metadata; 59190075Sobrien fp->f_metadata = md; 59290075Sobrien} 59390075Sobrien 59490075Sobrien/* 59590075Sobrien * Find a metadata object of (type) associated with the file (fp) 59690075Sobrien */ 59790075Sobrienstruct file_metadata * 59890075Sobrienfile_findmetadata(struct preloaded_file *fp, int type) 59990075Sobrien{ 60090075Sobrien struct file_metadata *md; 60190075Sobrien 60290075Sobrien for (md = fp->f_metadata; md != NULL; md = md->md_next) 60390075Sobrien if (md->md_type == type) 60490075Sobrien break; 60590075Sobrien return(md); 60690075Sobrien} 60790075Sobrien 60890075Sobrienstruct file_metadata * 60990075Sobrienmetadata_next(struct file_metadata *md, int type) 61090075Sobrien{ 61190075Sobrien if (md == NULL) 61290075Sobrien return (NULL); 61390075Sobrien while((md = md->md_next) != NULL) 61490075Sobrien if (md->md_type == type) 61590075Sobrien break; 616132718Skan return (md); 61790075Sobrien} 61890075Sobrien 61990075Sobrienstatic char *emptyextlist[] = { "", NULL }; 62090075Sobrien 62190075Sobrien/* 62290075Sobrien * Check if the given file is in place and return full path to it. 62390075Sobrien */ 62490075Sobrienstatic char * 62590075Sobrienfile_lookup(const char *path, const char *name, int namelen, char **extlist) 62690075Sobrien{ 62790075Sobrien struct stat st; 62890075Sobrien char *result, *cp, **cpp; 62990075Sobrien int pathlen, extlen, len; 63090075Sobrien 63190075Sobrien pathlen = strlen(path); 63290075Sobrien extlen = 0; 63390075Sobrien if (extlist == NULL) 63490075Sobrien extlist = emptyextlist; 63590075Sobrien for (cpp = extlist; *cpp; cpp++) { 63690075Sobrien len = strlen(*cpp); 63790075Sobrien if (len > extlen) 63890075Sobrien extlen = len; 63990075Sobrien } 64090075Sobrien result = malloc(pathlen + namelen + extlen + 2); 64190075Sobrien if (result == NULL) 64290075Sobrien return (NULL); 643132718Skan bcopy(path, result, pathlen); 64490075Sobrien if (pathlen > 0 && result[pathlen - 1] != '/') 64590075Sobrien result[pathlen++] = '/'; 64690075Sobrien cp = result + pathlen; 64790075Sobrien bcopy(name, cp, namelen); 64890075Sobrien cp += namelen; 64990075Sobrien for (cpp = extlist; *cpp; cpp++) { 65090075Sobrien strcpy(cp, *cpp); 65150397Sobrien if (stat(result, &st) == 0 && S_ISREG(st.st_mode)) 65250397Sobrien return result; 65350397Sobrien } 65450397Sobrien free(result); 65550397Sobrien return NULL; 65696263Sobrien} 65750397Sobrien 65850397Sobrien/* 65950397Sobrien * Check if file name have any qualifiers 66050397Sobrien */ 66150397Sobrienstatic int 66250397Sobrienfile_havepath(const char *name) 66390075Sobrien{ 66450397Sobrien const char *cp; 66550397Sobrien 66650397Sobrien archsw.arch_getdev(NULL, name, &cp); 66750397Sobrien return (cp != name || strchr(name, '/') != NULL); 66850397Sobrien} 66950397Sobrien 67050397Sobrien/* 67150397Sobrien * Attempt to find the file (name) on the module searchpath. 67250397Sobrien * If (name) is qualified in any way, we simply check it and 67350397Sobrien * return it or NULL. If it is not qualified, then we attempt 67490075Sobrien * to construct a path using entries in the environment variable 67550397Sobrien * module_path. 67650397Sobrien * 67750397Sobrien * The path we return a pointer to need never be freed, as we manage 67850397Sobrien * it internally. 67990075Sobrien */ 68050397Sobrienstatic char * 68150397Sobrienfile_search(const char *name, char **extlist) 68250397Sobrien{ 68350397Sobrien struct moduledir *mdp; 68450397Sobrien struct stat sb; 68550397Sobrien char *result; 68618334Speter int namelen; 68718334Speter 68818334Speter /* Don't look for nothing */ 68918334Speter if (name == NULL) 69018334Speter return(NULL); 691132718Skan 69218334Speter if (*name == 0) 69318334Speter return(strdup(name)); 69490075Sobrien 69518334Speter if (file_havepath(name)) { 69618334Speter /* Qualified, so just see if it exists */ 69718334Speter if (stat(name, &sb) == 0) 69818334Speter return(strdup(name)); 69918334Speter return(NULL); 70018334Speter } 70118334Speter moduledir_rebuild(); 70218334Speter result = NULL; 70318334Speter namelen = strlen(name); 70496263Sobrien STAILQ_FOREACH(mdp, &moduledir_list, d_link) { 70518334Speter result = file_lookup(mdp->d_path, name, namelen, extlist); 70618334Speter if (result) 70718334Speter break; 70818334Speter } 70918334Speter return(result); 71018334Speter} 71150397Sobrien 71290075Sobrien#define INT_ALIGN(base, ptr) ptr = \ 71350397Sobrien (base) + (((ptr) - (base) + sizeof(int) - 1) & ~(sizeof(int) - 1)) 71490075Sobrien 71550397Sobrienstatic char * 71618334Spetermod_search_hints(struct moduledir *mdp, const char *modname, 71750397Sobrien struct mod_depend *verinfo) 71818334Speter{ 71918334Speter u_char *cp, *recptr, *bufend, *best; 72018334Speter char *result; 72118334Speter int *intp, bestver, blen, clen, found, ival, modnamelen, reclen; 72218334Speter 72318334Speter moduledir_readhints(mdp); 724169689Skan modnamelen = strlen(modname); 72518334Speter found = 0; 72618334Speter result = NULL; 72718334Speter bestver = 0; 72818334Speter if (mdp->d_hints == NULL) 72918334Speter goto bad; 73018334Speter recptr = mdp->d_hints; 73118334Speter bufend = recptr + mdp->d_hintsz; 73218334Speter clen = blen = 0; 73390075Sobrien best = cp = NULL; 73450397Sobrien while (recptr < bufend && !found) { 73550397Sobrien intp = (int*)recptr; 73618334Speter reclen = *intp++; 73718334Speter ival = *intp++; 73818334Speter cp = (char*)intp; 73918334Speter switch (ival) { 74018334Speter case MDT_VERSION: 74118334Speter clen = *cp++; 74218334Speter if (clen != modnamelen || bcmp(cp, modname, clen) != 0) 74318334Speter break; 74418334Speter cp += clen; 74518334Speter INT_ALIGN(mdp->d_hints, cp); 74618334Speter ival = *(int*)cp; 74718334Speter cp += sizeof(int); 74818334Speter clen = *cp++; 74918334Speter if (verinfo == NULL || ival == verinfo->md_ver_preferred) { 75018334Speter found = 1; 75118334Speter break; 75218334Speter } 75318334Speter if (ival >= verinfo->md_ver_minimum && 75418334Speter ival <= verinfo->md_ver_maximum && 75518334Speter ival > bestver) { 75618334Speter bestver = ival; 75718334Speter best = cp; 75818334Speter blen = clen; 75918334Speter } 760132718Skan break; 76118334Speter default: 76218334Speter break; 76318334Speter } 76418334Speter recptr += reclen + sizeof(int); 76518334Speter } 766169689Skan /* 767169689Skan * Finally check if KLD is in the place 768169689Skan */ 769169689Skan if (found) 770169689Skan result = file_lookup(mdp->d_path, cp, clen, NULL); 771169689Skan else if (best) 77218334Speter result = file_lookup(mdp->d_path, best, blen, NULL); 773169689Skanbad: 774169689Skan /* 775169689Skan * If nothing found or hints is absent - fallback to the old way 776169689Skan * by using "kldname[.ko]" as module name. 777169689Skan */ 778169689Skan if (!found && !bestver && result == NULL) 779169689Skan result = file_lookup(mdp->d_path, modname, modnamelen, kld_ext_list); 78018334Speter return result; 78118334Speter} 78218334Speter 78318334Speter/* 78418334Speter * Attempt to locate the file containing the module (name) 78518334Speter */ 78618334Speterstatic char * 78718334Spetermod_searchmodule(char *name, struct mod_depend *verinfo) 78818334Speter{ 789169689Skan struct moduledir *mdp; 79018334Speter char *result; 791169689Skan 792169689Skan moduledir_rebuild(); 79318334Speter /* 794132718Skan * Now we ready to lookup module in the given directories 79518334Speter */ 79618334Speter result = NULL; 797117395Skan STAILQ_FOREACH(mdp, &moduledir_list, d_link) { 79890075Sobrien result = mod_search_hints(mdp, name, verinfo); 79990075Sobrien if (result) 80090075Sobrien break; 80118334Speter } 802169689Skan 80390075Sobrien return(result); 804169689Skan} 805169689Skan 80650397Sobrienint 80718334Speterfile_addmodule(struct preloaded_file *fp, char *modname, int version, 80818334Speter struct kernel_module **newmp) 80918334Speter{ 81018334Speter struct kernel_module *mp; 81118334Speter struct mod_depend mdepend; 812117395Skan 81318334Speter bzero(&mdepend, sizeof(mdepend)); 81490075Sobrien mdepend.md_ver_preferred = version; 81518334Speter mp = file_findmodule(fp, modname, &mdepend); 816132718Skan if (mp) 817132718Skan return (EEXIST); 818132718Skan mp = malloc(sizeof(struct kernel_module)); 81918334Speter if (mp == NULL) 82090075Sobrien return (ENOMEM); 82190075Sobrien bzero(mp, sizeof(struct kernel_module)); 82290075Sobrien mp->m_name = strdup(modname); 82390075Sobrien mp->m_version = version; 82418334Speter mp->m_fp = fp; 82590075Sobrien mp->m_next = fp->f_modules; 82690075Sobrien fp->f_modules = mp; 82718334Speter if (newmp) 82890075Sobrien *newmp = mp; 82990075Sobrien return (0); 83090075Sobrien} 83152284Sobrien 83290075Sobrien/* 83352284Sobrien * Throw a file away 83490075Sobrien */ 83590075Sobrienvoid 83690075Sobrienfile_discard(struct preloaded_file *fp) 83752284Sobrien{ 83890075Sobrien struct file_metadata *md, *md1; 83990075Sobrien struct kernel_module *mp, *mp1; 84052284Sobrien if (fp == NULL) 84190075Sobrien return; 84290075Sobrien md = fp->f_metadata; 84390075Sobrien while (md) { 84452284Sobrien md1 = md; 84590075Sobrien md = md->md_next; 84690075Sobrien free(md1); 84790075Sobrien } 84890075Sobrien mp = fp->f_modules; 84990075Sobrien while (mp) { 85090075Sobrien if (mp->m_name) 85190075Sobrien free(mp->m_name); 85218334Speter mp1 = mp; 85390075Sobrien mp = mp->m_next; 85490075Sobrien free(mp1); 85518334Speter } 856169689Skan if (fp->f_name != NULL) 857169689Skan free(fp->f_name); 858169689Skan if (fp->f_type != NULL) 859169689Skan free(fp->f_type); 860169689Skan if (fp->f_args != NULL) 861169689Skan free(fp->f_args); 862169689Skan free(fp); 86350397Sobrien} 864169689Skan 865169689Skan/* 866169689Skan * Allocate a new file; must be used instead of malloc() 86750397Sobrien * to ensure safe initialisation. 868169689Skan */ 869169689Skanstruct preloaded_file * 870169689Skanfile_alloc(void) 871169689Skan{ 87218334Speter struct preloaded_file *fp; 873169689Skan 874169689Skan if ((fp = malloc(sizeof(struct preloaded_file))) != NULL) { 87590075Sobrien bzero(fp, sizeof(struct preloaded_file)); 87652284Sobrien } 877169689Skan return (fp); 878169689Skan} 879169689Skan 88090075Sobrien/* 88190075Sobrien * Add a module to the chain 88290075Sobrien */ 88390075Sobrienstatic void 88490075Sobrienfile_insert_tail(struct preloaded_file *fp) 88590075Sobrien{ 88690075Sobrien struct preloaded_file *cm; 88790075Sobrien 88890075Sobrien /* Append to list of loaded file */ 88990075Sobrien fp->f_next = NULL; 89090075Sobrien if (preloaded_files == NULL) { 89118334Speter preloaded_files = fp; 892169689Skan } else { 89390075Sobrien for (cm = preloaded_files; cm->f_next != NULL; cm = cm->f_next) 89490075Sobrien ; 89590075Sobrien cm->f_next = fp; 896169689Skan } 89790075Sobrien} 898132718Skan 89990075Sobrienstatic char * 90090075Sobrienmoduledir_fullpath(struct moduledir *mdp, const char *fname) 90190075Sobrien{ 90290075Sobrien char *cp; 90318334Speter 90490075Sobrien cp = malloc(strlen(mdp->d_path) + strlen(fname) + 2); 90518334Speter if (cp == NULL) 90690075Sobrien return NULL; 90790075Sobrien strcpy(cp, mdp->d_path); 90890075Sobrien strcat(cp, "/"); 90990075Sobrien strcat(cp, fname); 91090075Sobrien return (cp); 91118334Speter} 91290075Sobrien 91390075Sobrien/* 91490075Sobrien * Read linker.hints file into memory performing some sanity checks. 91590075Sobrien */ 91618334Speterstatic void 91790075Sobrienmoduledir_readhints(struct moduledir *mdp) 91890075Sobrien{ 91990075Sobrien struct stat st; 92090075Sobrien char *path; 92190075Sobrien int fd, size, version; 92290075Sobrien 92390075Sobrien if (mdp->d_hints != NULL || (mdp->d_flags & MDIR_NOHINTS)) 92490075Sobrien return; 92590075Sobrien path = moduledir_fullpath(mdp, "linker.hints"); 92690075Sobrien if (stat(path, &st) != 0 || 92790075Sobrien st.st_size < (ssize_t)(sizeof(version) + sizeof(int)) || 92890075Sobrien st.st_size > 100 * 1024 || (fd = open(path, O_RDONLY)) < 0) { 92990075Sobrien free(path); 93018334Speter mdp->d_flags |= MDIR_NOHINTS; 93190075Sobrien return; 93290075Sobrien } 93390075Sobrien free(path); 93490075Sobrien size = read(fd, &version, sizeof(version)); 93518334Speter if (size != sizeof(version) || version != LINKER_HINTS_VERSION) 93690075Sobrien goto bad; 93790075Sobrien size = st.st_size - size; 93890075Sobrien mdp->d_hints = malloc(size); 93918334Speter if (mdp->d_hints == NULL) 94090075Sobrien goto bad; 94190075Sobrien if (read(fd, mdp->d_hints, size) != size) 94290075Sobrien goto bad; 94390075Sobrien mdp->d_hintsz = size; 94490075Sobrien close(fd); 94518334Speter return; 94690075Sobrienbad: 94790075Sobrien close(fd); 94890075Sobrien if (mdp->d_hints) { 94990075Sobrien free(mdp->d_hints); 95018334Speter mdp->d_hints = NULL; 95190075Sobrien } 95290075Sobrien mdp->d_flags |= MDIR_NOHINTS; 95390075Sobrien return; 954169689Skan} 95590075Sobrien 95690075Sobrien/* 95790075Sobrien * Extract directories from the ';' separated list, remove duplicates. 95890075Sobrien */ 95990075Sobrienstatic void 96050397Sobrienmoduledir_rebuild(void) 96190075Sobrien{ 962169689Skan struct moduledir *mdp, *mtmp; 96318334Speter const char *path, *cp, *ep; 964169689Skan int cplen; 965169689Skan 966169689Skan path = getenv("module_path"); 967169689Skan if (path == NULL) 968169689Skan path = default_searchpath; 969169689Skan /* 97090075Sobrien * Rebuild list of module directories if it changed 97190075Sobrien */ 97290075Sobrien STAILQ_FOREACH(mdp, &moduledir_list, d_link) 97390075Sobrien mdp->d_flags |= MDIR_REMOVED; 97490075Sobrien 975169689Skan for (ep = path; *ep != 0; ep++) { 976169689Skan cp = ep; 977169689Skan for (; *ep != 0 && *ep != ';'; ep++) 978169689Skan ; 97990075Sobrien /* 98018334Speter * Ignore trailing slashes 981169689Skan */ 982102780Skan for (cplen = ep - cp; cplen > 1 && cp[cplen - 1] == '/'; cplen--) 98390075Sobrien ; 98418334Speter STAILQ_FOREACH(mdp, &moduledir_list, d_link) { 98590075Sobrien if (strlen(mdp->d_path) != cplen || bcmp(cp, mdp->d_path, cplen) != 0) 98690075Sobrien continue; 98790075Sobrien mdp->d_flags &= ~MDIR_REMOVED; 98890075Sobrien break; 98990075Sobrien } 99090075Sobrien if (mdp == NULL) { 99150397Sobrien mdp = malloc(sizeof(*mdp) + cplen + 1); 99290075Sobrien if (mdp == NULL) 99390075Sobrien return; 99490075Sobrien mdp->d_path = (char*)(mdp + 1); 99590075Sobrien bcopy(cp, mdp->d_path, cplen); 99690075Sobrien mdp->d_path[cplen] = 0; 99790075Sobrien mdp->d_hints = NULL; 99890075Sobrien mdp->d_flags = 0; 99990075Sobrien STAILQ_INSERT_TAIL(&moduledir_list, mdp, d_link); 100090075Sobrien } 1001169689Skan if (*ep == 0) 1002169689Skan break; 1003169689Skan } 1004169689Skan /* 1005169689Skan * Delete unused directories if any 1006169689Skan */ 100790075Sobrien mdp = STAILQ_FIRST(&moduledir_list); 100850397Sobrien while (mdp) { 100918334Speter if ((mdp->d_flags & MDIR_REMOVED) == 0) { 101018334Speter mdp = STAILQ_NEXT(mdp, d_link); 101118334Speter } else { 1012169689Skan if (mdp->d_hints) 1013169689Skan free(mdp->d_hints); 1014169689Skan mtmp = mdp; 1015169689Skan mdp = STAILQ_NEXT(mdp, d_link); 1016169689Skan STAILQ_REMOVE(&moduledir_list, mtmp, moduledir, d_link); 1017169689Skan free(mtmp); 1018169689Skan } 1019169689Skan } 1020169689Skan return; 1021169689Skan} 1022169689Skan