06b57b0e09
- 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()
521 lines
13 KiB
C
521 lines
13 KiB
C
/*-
|
|
* Copyright (c) 1998 Michael Smith <msmith@freebsd.org>
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
* SUCH DAMAGE.
|
|
*
|
|
* $Id: module.c,v 1.2 1998/08/31 21:10:42 msmith Exp $
|
|
*/
|
|
|
|
/*
|
|
* module function dispatcher, support, etc.
|
|
*
|
|
* XXX need a 'searchmodule' function that takes a name and
|
|
* traverses a search path.
|
|
*/
|
|
|
|
#include <stand.h>
|
|
#include <string.h>
|
|
|
|
#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));
|
|
}
|
|
|
|
COMMAND_SET(unload, "unload", "unload all modules", command_unload);
|
|
|
|
static int
|
|
command_unload(int argc, char *argv[])
|
|
{
|
|
struct loaded_module *mp;
|
|
|
|
while (loaded_modules != NULL) {
|
|
mp = loaded_modules;
|
|
loaded_modules = loaded_modules->m_next;
|
|
mod_discard(mp);
|
|
}
|
|
loadaddr = 0;
|
|
return(CMD_OK);
|
|
}
|
|
|
|
COMMAND_SET(lsmod, "lsmod", "list loaded modules", command_lsmod);
|
|
|
|
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",
|
|
am->m_addr, am->m_name, am->m_type, am->m_size);
|
|
pager_output(lbuf);
|
|
if (am->m_args != NULL) {
|
|
pager_output(" args: ");
|
|
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 *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)
|
|
;
|
|
|
|
/*
|
|
* 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)
|
|
continue;
|
|
|
|
/* Fatal error */
|
|
sprintf(command_errbuf, "can't load module '%s': %s", name, strerror(err));
|
|
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 */
|
|
mp->m_loader = i;
|
|
|
|
/* Add to the list of loaded modules */
|
|
mod_append(mp);
|
|
|
|
break;
|
|
}
|
|
}
|
|
if (err == EFTYPE)
|
|
sprintf(command_errbuf, "don't know how to load module '%s'", name);
|
|
return(mp);
|
|
}
|
|
|
|
/*
|
|
* 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(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)
|
|
{
|
|
struct loaded_module *mp;
|
|
|
|
for (mp = loaded_modules; mp != NULL; mp = mp->m_next) {
|
|
if (((name == NULL) || !strcmp(name, mp->m_name)) &&
|
|
((type == NULL) || !strcmp(type, mp->m_type)))
|
|
break;
|
|
}
|
|
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)
|
|
{
|
|
struct module_metadata *md;
|
|
|
|
md = malloc(sizeof(struct module_metadata) + size);
|
|
md->md_size = size;
|
|
md->md_type = type;
|
|
bcopy(p, md->md_data, size);
|
|
md->md_next = mp->m_metadata;
|
|
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)
|
|
{
|
|
struct module_metadata *md;
|
|
|
|
for (md = mp->m_metadata; md != NULL; md = md->md_next)
|
|
if (md->md_type == 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;
|
|
}
|
|
}
|