diff options
author | msmith <msmith@FreeBSD.org> | 1998-09-03 02:10:09 +0000 |
---|---|---|
committer | msmith <msmith@FreeBSD.org> | 1998-09-03 02:10:09 +0000 |
commit | 2feae82d784a79b3fac686b4ca52c044fd75efda (patch) | |
tree | 1696a640576be6f4c71e821ec8a61a75f724a269 /sys/boot/common/module.c | |
parent | 28865a6b9b25eb22c062e822e4a9f3d2da33e70b (diff) | |
download | FreeBSD-src-2feae82d784a79b3fac686b4ca52c044fd75efda.zip FreeBSD-src-2feae82d784a79b3fac686b4ca52c044fd75efda.tar.gz |
Bootstrap updates.
- Move some startup code from MD to MI sections
- Add a 'copyout' and some copyout-related functions. These will be
obsoleted when BTX is available for the 386 and the kernel load
area becomes directly addressable.
- Add the ability load an arbitrary file as a module, associating
and arbitrary type string with it. This can be used eg. for loading
splash-screen images etc.
- Add KLD module dependancy infrastructure. We know how to look for
dependancies inside KLD modules, how to resolve these dependancies
and what to do if things go wrong. Only works for a.out at the
moment, due to lack of an MI ELF loader. Attach KLD module information
to loaded modules as metadata, but don't pass it to the kernel (it
can find it itself).
- Load a.out KLD modules on a page boundary. Only pad the a.out BSS
for the kernel, as it may want to throw symbols away. (We might want
to do this for KLD modules too.)
- Allow commands to be hidden from the '?' display, to avoid cluttering
it with things like 'echo'. Add 'echo'.
- Bring the 'prompt' command into line with the parser syntax.
- Fix the verbose 'ls'; it was using an uninitialised stack variable.
- Add a '-v' flag to 'lsmod' to have it display module metadata as well
(not terribly useful for the average user)
- Support a 'module searchpath' for required modules.
- The bootstrap file on i386 is now called 'loader' to permit the
/boot directory to use that name.
- Discard the old i386 pread() function, as it's replaced by
arch_readin()
Diffstat (limited to 'sys/boot/common/module.c')
-rw-r--r-- | sys/boot/common/module.c | 414 |
1 files changed, 376 insertions, 38 deletions
diff --git a/sys/boot/common/module.c b/sys/boot/common/module.c index 28c10dc..f10fe3b 100644 --- a/sys/boot/common/module.c +++ b/sys/boot/common/module.c @@ -23,7 +23,7 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $Id: module.c,v 1.1.1.1 1998/08/21 03:17:41 msmith Exp $ + * $Id: module.c,v 1.2 1998/08/31 21:10:42 msmith Exp $ */ /* @@ -38,16 +38,68 @@ #include "bootstrap.h" +static struct loaded_module *mod_loadmodule(char *name, int argc, char *argv[]); +static char *mod_searchdep(struct loaded_module *mp); +static char *mod_searchmodule(char *name); +static void mod_append(struct loaded_module *mp); + /* XXX load address should be tweaked by first module loaded (kernel) */ static vm_offset_t loadaddr = 0; struct loaded_module *loaded_modules = NULL; +/* + * load an object, either a disk file or code module. + * + * To load a file, the syntax is: + * + * load -t <type> <path> + * + * code modules are loaded as: + * + * load <path> <options> + */ + COMMAND_SET(load, "load", "load a kernel or module", command_load); static int command_load(int argc, char *argv[]) { + char *typestr; + int dofile, ch; + + dofile = 0; + optind = 1; + typestr = NULL; + while ((ch = getopt(argc, argv, "t:")) != -1) { + switch(ch) { + case 't': + typestr = optarg; + dofile = 1; + break; + case '?': + default: + /* getopt has already reported an error */ + return(CMD_OK); + } + } + argv += (optind - 1); + argc -= (optind - 1); + + /* + * Request to load a raw file? + */ + if (dofile) { + if ((typestr == NULL) || (*typestr == 0)) { + command_errmsg = "invalid load type"; + return(CMD_ERROR); + } + return(mod_loadobj(typestr, argv[1])); + } + + /* + * Looks like a request for a module. + */ return(mod_load(argv[1], argc - 2, argv + 2)); } @@ -57,21 +109,11 @@ static int command_unload(int argc, char *argv[]) { struct loaded_module *mp; - struct module_metadata *md; while (loaded_modules != NULL) { mp = loaded_modules; loaded_modules = loaded_modules->m_next; - while (mp->m_metadata != NULL) { - md = mp->m_metadata; - mp->m_metadata = mp->m_metadata->md_next; - free(md); - } - free(mp->m_name); - free(mp->m_type); - if (mp->m_args != NULL) - free(mp->m_args); - free(mp); + mod_discard(mp); } loadaddr = 0; return(CMD_OK); @@ -83,8 +125,24 @@ static int command_lsmod(int argc, char *argv[]) { struct loaded_module *am; + struct module_metadata *md; char lbuf[80]; - + int ch, verbose; + + verbose = 0; + optind = 1; + while ((ch = getopt(argc, argv, "v")) != -1) { + switch(ch) { + case 'v': + verbose = 1; + break; + case '?': + default: + /* getopt has already reported an error */ + return(CMD_OK); + } + } + pager_open(); for (am = loaded_modules; (am != NULL); am = am->m_next) { sprintf(lbuf, " %x: %s (%s, 0x%x)\n", @@ -95,20 +153,141 @@ command_lsmod(int argc, char *argv[]) pager_output(am->m_args); pager_output("\n"); } + if (verbose) + /* XXX could add some formatting smarts here to display some better */ + for (md = am->m_metadata; md != NULL; md = md->md_next) { + sprintf(lbuf, " 0x%04x, 0x%x\n", md->md_type, md->md_size); + pager_output(lbuf); + } } pager_close(); return(CMD_OK); } +/* + * We've been asked to load (name) and give it (argc),(argv). + * Start by trying to load it, and then attempt to load all of its + * dependancies. If we fail at any point, throw them all away and + * fail the entire load. + * + * XXX if a depended-on module requires arguments, it must be loaded + * explicitly first. + */ int mod_load(char *name, int argc, char *argv[]) { - struct loaded_module *am, *cm; - int i, err; + struct loaded_module *last_mod, *base_mod, *mp; + char *dep_name; + + /* remember previous last module on chain */ + for (last_mod = loaded_modules; + (last_mod != NULL) && (last_mod->m_next != NULL); + last_mod = last_mod->m_next) + ; - for (i = 0, am = NULL; (module_formats[i] != NULL) && (am == NULL); i++) { - /* XXX call searchmodule() to search for module (name) */ - if ((err = (module_formats[i]->l_load)(name, loadaddr, &am)) != 0) { + /* + * Load the first module; note that it's the only one that gets + * arguments explicitly. + */ + if ((base_mod = mod_loadmodule(name, argc, argv)) == NULL) + return(CMD_ERROR); + + /* + * Look for dependancies. + */ + while ((dep_name = mod_searchdep(base_mod)) != NULL) { + printf("loading required module '%s'\n", dep_name); + if ((mp = mod_loadmodule(dep_name, 0, NULL)) == NULL) { + /* Load failed; discard everything */ + while (base_mod != NULL) { + mp = base_mod; + base_mod = base_mod->m_next; + mod_discard(mp); + } + last_mod->m_next = NULL; + loadaddr = last_mod->m_addr + last_mod->m_size; + /* error message already set by mod_loadmodule */ + return(CMD_ERROR); + } + } + return(CMD_OK); +} + +/* + * We've been asked to load (name) as (type), so just suck it in, + * no arguments or anything. + */ +int +mod_loadobj(char *type, char *name) +{ + struct loaded_module *mp; + char *cp; + int fd, got; + vm_offset_t laddr; + + /* We can't load first */ + if ((mod_findmodule(NULL, NULL)) == NULL) { + command_errmsg = "can't load file before kernel"; + return(CMD_ERROR); + } + + /* Try to come up with a fully-qualified name if we don't have one */ + if ((cp = mod_searchmodule(name)) != NULL) + name = cp; + + if ((fd = open(name, O_RDONLY)) < 0) { + sprintf(command_errbuf, "can't open '%s': %s", name, strerror(errno)); + return(CMD_ERROR); + } + + laddr = loadaddr; + for (;;) { + /* read in 4k chunks; size is not really important */ + got = archsw.arch_readin(fd, laddr, 4096); + if (got == 0) /* end of file */ + break; + if (got < 0) { /* error */ + sprintf(command_errbuf, "error reading '%s': %s", name, strerror(errno)); + return(CMD_ERROR); + } + laddr += got; + } + + /* Looks OK so far; create & populate control structure */ + mp = malloc(sizeof(struct loaded_module)); + mp->m_name = strdup(name); + mp->m_type = strdup(type); + mp->m_args = NULL; + mp->m_metadata = NULL; + mp->m_loader = -1; + mp->m_addr = loadaddr; + mp->m_size = laddr - loadaddr; + + /* recognise space consumption */ + loadaddr = laddr; + + /* Add to the list of loaded modules */ + mod_append(mp); + return(CMD_OK); +} + +/* + * Load the module (name), pass it (argc),(argv). + * Don't do any dependancy checking. + */ +static struct loaded_module * +mod_loadmodule(char *name, int argc, char *argv[]) +{ + struct loaded_module *mp; + int i, err; + char *cp; + + /* Try to come up with a fully-qualified name if we don't have one */ + if ((cp = mod_searchmodule(name)) != NULL) + name = cp; + err = 0; + for (i = 0, mp = NULL; (module_formats[i] != NULL) && (mp == NULL); i++) { + if ((err = (module_formats[i]->l_load)(name, loadaddr, &mp)) != 0) { /* Unknown to this handler? */ if (err == EFTYPE) @@ -116,34 +295,87 @@ mod_load(char *name, int argc, char *argv[]) /* Fatal error */ sprintf(command_errbuf, "can't load module '%s': %s", name, strerror(err)); - return(CMD_ERROR); + return(NULL); } else { + + /* Load was OK, set args */ + mp->m_args = unargv(argc, argv); + + /* where can we put the next one? */ + loadaddr = mp->m_addr + mp->m_size; + /* remember the loader */ - am->m_loader = i; + mp->m_loader = i; + + /* Add to the list of loaded modules */ + mod_append(mp); + + break; } } - if (am == NULL) { - sprintf(command_errbuf, "can't work out what to do with '%s'", name); - return(CMD_ERROR); - } - /* where can we put the next one? */ - loadaddr = am->m_addr + am->m_size; - - /* Load was OK, set args */ - am->m_args = unargv(argc, argv); + if (err == EFTYPE) + sprintf(command_errbuf, "don't know how to load module '%s'", name); + return(mp); +} - /* Append to list of loaded modules */ - am->m_next = NULL; - if (loaded_modules == NULL) { - loaded_modules = am; - } else { - for (cm = loaded_modules; cm->m_next != NULL; cm = cm->m_next) - ; - cm->m_next = am; +/* + * Search the modules from (mp) onwards, and return the name of the + * first unresolved dependancy, or NULL if none were found. + */ +static char * +mod_searchdep(struct loaded_module *mp) +{ + struct kld_module_identifier *ident, *dident; + struct kld_module_dependancy *deps, *dp; + struct module_metadata *md; + struct loaded_module *dmp; + int dindex; + + for (; mp != NULL; mp = mp->m_next) { + + /* + * Get KLD module data + */ + ident = NULL; + deps = NULL; + if ((md = mod_findmetadata(mp, MODINFOMD_KLDIDENT)) != NULL) + ident = (struct kld_module_identifier *)md->md_data; + if ((md = mod_findmetadata(mp, MODINFOMD_KLDDEP)) != NULL) + deps = (struct kld_module_dependancy *)md->md_data; + + /* + * Both must exist for this module to depend on anything + */ + if ((ident != NULL) && (deps != NULL)) { + + /* Iterate over dependancies */ + for (dindex = 0; dindex < ident->ki_ndeps; dindex++) { + dp = KLD_GETDEP(ident, deps, dindex); + + /* + * Look for a module matching the dependancy; if we don't have it, + * we need it. + */ + if ((dmp = mod_findmodule(dp->kd_name, NULL)) == NULL) + return(dp->kd_name); + + /* Version check */ + if ((md = mod_findmetadata(dmp, MODINFOMD_KLDIDENT)) != NULL) { + dident = (struct kld_module_identifier *)md->md_data; + if (dp->kd_version != dident->ki_version) + printf("module '%s' requires '%s' version %d, but version %d is loaded\n", + mp->m_name, dp->kd_name, dp->kd_version, dident->ki_version); + } + } + } } - return(CMD_OK); + return(NULL); } +/* + * Find a module matching (name) and (type). + * NULL may be passed as a wildcard to either. + */ struct loaded_module * mod_findmodule(char *name, char *type) { @@ -157,6 +389,10 @@ mod_findmodule(char *name, char *type) return(mp); } +/* + * Make a copy of (size) bytes of data from (p), and associate them as + * metadata of (type) to the module (mp). + */ void mod_addmetadata(struct loaded_module *mp, int type, size_t size, void *p) { @@ -170,6 +406,10 @@ mod_addmetadata(struct loaded_module *mp, int type, size_t size, void *p) mp->m_metadata = md; } +/* + * Find a metadata object of (type) associated with the module + * (mp) + */ struct module_metadata * mod_findmetadata(struct loaded_module *mp, int type) { @@ -180,3 +420,101 @@ mod_findmetadata(struct loaded_module *mp, int type) break; return(md); } + +/* + * Attempt to locate a kernel module file for the module (name). + * If (name) is qualified in any way, we simply check it and + * return it or NULL. If it is not qualified, then we attempt + * to construct a path using entries in the environment variable + * module_path. + * + * The path we return a pointer to need never be freed, as we manage + * it internally. + */ +static char * +mod_searchmodule(char *name) +{ + static char *result = NULL; + static char *defpath = "/boot", *path; + char *cp, *sp; + struct stat sb; + + /* Don't look for nothing */ + if ((name == NULL) || (*name == 0)) + return(name); + + /* + * See if there's a device on the front, or a directory name. + */ + archsw.arch_getdev(NULL, name, &cp); + if ((cp != name) || (strchr(name, '/') != NULL)) { + /* Qualified, so just see if it exists */ + if (stat(name, &sb) == 0) + return(name); + return(NULL); + } + + /* + * Get the module path + */ + if ((cp = getenv("module_path")) == NULL) + cp = defpath; + sp = path = strdup(cp); + + /* + * Traverse the path, splitting off ';'-delimited components. + */ + if (result != NULL) + free(result); + while((cp = strsep(&path, ";")) != NULL) { + result = malloc(strlen(cp) + strlen(name) + 2); + sprintf(result, "%s/%s", cp, name); + if (stat(result, &sb) == 0) + break; + free(result); + result = NULL; + } + free(sp); + return(result); +} + +/* + * Throw a module away + */ +void +mod_discard(struct loaded_module *mp) +{ + struct module_metadata *md; + + while (mp->m_metadata != NULL) { + md = mp->m_metadata; + mp->m_metadata = mp->m_metadata->md_next; + free(md); + } + if (mp->m_name != NULL) + free(mp->m_name); + if (mp->m_type != NULL) + free(mp->m_type); + if (mp->m_args != NULL) + free(mp->m_args); + free(mp); +} + +/* + * Add a module to the chain + */ +static void +mod_append(struct loaded_module *mp) +{ + struct loaded_module *cm; + + /* Append to list of loaded modules */ + mp->m_next = NULL; + if (loaded_modules == NULL) { + loaded_modules = mp; + } else { + for (cm = loaded_modules; cm->m_next != NULL; cm = cm->m_next) + ; + cm->m_next = mp; + } +} |