module.c revision 241069
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: head/sys/boot/common/module.c 241069 2012-09-30 13:14:37Z ae $");
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);
5559854Sbpstatic int			file_loadraw(char *type, char *name);
5686469Siedowsestatic int			file_load_dependencies(struct preloaded_file *base_mod);
5783321Speterstatic char *			file_search(const char *name, char **extlist);
5883321Speterstatic struct kernel_module *	file_findmodule(struct preloaded_file *fp, char *modname, struct mod_depend *verinfo);
5983321Speterstatic int			file_havepath(const char *name);
6083321Speterstatic char			*mod_searchmodule(char *name, struct mod_depend *verinfo);
6159854Sbpstatic void			file_insert_tail(struct preloaded_file *mp);
6259854Sbpstruct file_metadata*		metadata_next(struct file_metadata *base_mp, int type);
6383321Speterstatic void			moduledir_readhints(struct moduledir *mdp);
6483321Speterstatic void			moduledir_rebuild(void);
6538764Smsmith
6639178Smsmith/* load address should be tweaked by first module loaded (kernel) */
6738465Smsmithstatic vm_offset_t	loadaddr = 0;
6838465Smsmith
69111852Srustatic const char	*default_searchpath ="/boot/kernel;/boot/modules";
7039178Smsmith
7183321Speterstatic STAILQ_HEAD(, moduledir) moduledir_list = STAILQ_HEAD_INITIALIZER(moduledir_list);
7283321Speter
7359854Sbpstruct preloaded_file *preloaded_files = NULL;
7438465Smsmith
7583321Speterstatic char *kld_ext_list[] = {
7683321Speter    ".ko",
7783321Speter    "",
78172445Sobrien    ".debug",
7983321Speter    NULL
8083321Speter};
8183321Speter
8283321Speter
8338764Smsmith/*
8438764Smsmith * load an object, either a disk file or code module.
8538764Smsmith *
8638764Smsmith * To load a file, the syntax is:
8738764Smsmith *
8838764Smsmith * load -t <type> <path>
8938764Smsmith *
9038764Smsmith * code modules are loaded as:
9138764Smsmith *
9238764Smsmith * load <path> <options>
9338764Smsmith */
9438764Smsmith
9538465SmsmithCOMMAND_SET(load, "load", "load a kernel or module", command_load);
9638465Smsmith
9738465Smsmithstatic int
9838465Smsmithcommand_load(int argc, char *argv[])
9938465Smsmith{
10038764Smsmith    char	*typestr;
10183321Speter    int		dofile, dokld, ch, error;
10238764Smsmith
10383321Speter    dokld = dofile = 0;
10438764Smsmith    optind = 1;
10542512Smsmith    optreset = 1;
10638764Smsmith    typestr = NULL;
10753993Sdcs    if (argc == 1) {
10853993Sdcs	command_errmsg = "no filename specified";
10953993Sdcs	return(CMD_ERROR);
11053993Sdcs    }
11183321Speter    while ((ch = getopt(argc, argv, "kt:")) != -1) {
11238764Smsmith	switch(ch) {
11383321Speter	case 'k':
11483321Speter	    dokld = 1;
11583321Speter	    break;
11638764Smsmith	case 't':
11738764Smsmith	    typestr = optarg;
11838764Smsmith	    dofile = 1;
11938764Smsmith	    break;
12038764Smsmith	case '?':
12138764Smsmith	default:
12238764Smsmith	    /* getopt has already reported an error */
12338764Smsmith	    return(CMD_OK);
12438764Smsmith	}
12538764Smsmith    }
12638764Smsmith    argv += (optind - 1);
12738764Smsmith    argc -= (optind - 1);
12838764Smsmith
12938764Smsmith    /*
13038764Smsmith     * Request to load a raw file?
13138764Smsmith     */
13238764Smsmith    if (dofile) {
13377971Sjesper	if ((argc != 2) || (typestr == NULL) || (*typestr == 0)) {
13438764Smsmith	    command_errmsg = "invalid load type";
13538764Smsmith	    return(CMD_ERROR);
13638764Smsmith	}
13759854Sbp	return(file_loadraw(typestr, argv[1]));
13838764Smsmith    }
13938764Smsmith    /*
14083321Speter     * Do we have explicit KLD load ?
14183321Speter     */
14283321Speter    if (dokld || file_havepath(argv[1])) {
14383321Speter	error = mod_loadkld(argv[1], argc - 2, argv + 2);
14483321Speter	if (error == EEXIST)
14583321Speter	    sprintf(command_errbuf, "warning: KLD '%s' already loaded", argv[1]);
14683321Speter	return (error == 0 ? CMD_OK : CMD_ERROR);
14783321Speter    }
14883321Speter    /*
14938764Smsmith     * Looks like a request for a module.
15038764Smsmith     */
15183321Speter    error = mod_load(argv[1], NULL, argc - 2, argv + 2);
15257468Sbp    if (error == EEXIST)
15357468Sbp	sprintf(command_errbuf, "warning: module '%s' already loaded", argv[1]);
15457468Sbp    return (error == 0 ? CMD_OK : CMD_ERROR);
15538465Smsmith}
15638465Smsmith
157188666SthompsaCOMMAND_SET(load_geli, "load_geli", "load a geli key", command_load_geli);
158188666Sthompsa
159188666Sthompsastatic int
160188666Sthompsacommand_load_geli(int argc, char *argv[])
161188666Sthompsa{
162188666Sthompsa    char	typestr[80];
163188666Sthompsa    char	*cp;
164188666Sthompsa    int		ch, num;
165188666Sthompsa
166188666Sthompsa    if (argc < 3) {
167188666Sthompsa	    command_errmsg = "usage is [-n key#] <prov> <file>";
168188666Sthompsa	    return(CMD_ERROR);
169188666Sthompsa    }
170188666Sthompsa
171188666Sthompsa    num = 0;
172188666Sthompsa    optind = 1;
173188666Sthompsa    optreset = 1;
174188666Sthompsa    while ((ch = getopt(argc, argv, "n:")) != -1) {
175188666Sthompsa	switch(ch) {
176188666Sthompsa	case 'n':
177188666Sthompsa	    num = strtol(optarg, &cp, 0);
178188666Sthompsa	    if (cp == optarg) {
179188666Sthompsa		    sprintf(command_errbuf, "bad key index '%s'", optarg);
180188666Sthompsa		    return(CMD_ERROR);
181188666Sthompsa	    }
182188666Sthompsa	    break;
183188666Sthompsa	case '?':
184188666Sthompsa	default:
185188666Sthompsa	    /* getopt has already reported an error */
186188666Sthompsa	    return(CMD_OK);
187188666Sthompsa	}
188188666Sthompsa    }
189188666Sthompsa    argv += (optind - 1);
190188666Sthompsa    argc -= (optind - 1);
191188666Sthompsa    sprintf(typestr, "%s:geli_keyfile%d", argv[1], num);
192188666Sthompsa    return(file_loadraw(typestr, argv[2]));
193188666Sthompsa}
194188666Sthompsa
19538712SmsmithCOMMAND_SET(unload, "unload", "unload all modules", command_unload);
19638712Smsmith
19738712Smsmithstatic int
19838712Smsmithcommand_unload(int argc, char *argv[])
19938712Smsmith{
20059854Sbp    struct preloaded_file	*fp;
20138712Smsmith
20259854Sbp    while (preloaded_files != NULL) {
20359854Sbp	fp = preloaded_files;
20459854Sbp	preloaded_files = preloaded_files->f_next;
20559854Sbp	file_discard(fp);
20638712Smsmith    }
20738712Smsmith    loadaddr = 0;
20865613Sdcs    unsetenv("kernelname");
20938712Smsmith    return(CMD_OK);
21038712Smsmith}
21138712Smsmith
21238465SmsmithCOMMAND_SET(lsmod, "lsmod", "list loaded modules", command_lsmod);
21338465Smsmith
21438465Smsmithstatic int
21538465Smsmithcommand_lsmod(int argc, char *argv[])
21638465Smsmith{
21759854Sbp    struct preloaded_file	*fp;
21859854Sbp    struct kernel_module	*mp;
21959854Sbp    struct file_metadata	*md;
22038465Smsmith    char			lbuf[80];
22138764Smsmith    int				ch, verbose;
22238764Smsmith
22338764Smsmith    verbose = 0;
22438764Smsmith    optind = 1;
22542512Smsmith    optreset = 1;
22638764Smsmith    while ((ch = getopt(argc, argv, "v")) != -1) {
22738764Smsmith	switch(ch) {
22838764Smsmith	case 'v':
22938764Smsmith	    verbose = 1;
23038764Smsmith	    break;
23138764Smsmith	case '?':
23238764Smsmith	default:
23338764Smsmith	    /* getopt has already reported an error */
23438764Smsmith	    return(CMD_OK);
23538764Smsmith	}
23638764Smsmith    }
23738764Smsmith
23838465Smsmith    pager_open();
23959854Sbp    for (fp = preloaded_files; fp; fp = fp->f_next) {
24039673Sdfr	sprintf(lbuf, " %p: %s (%s, 0x%lx)\n",
24159854Sbp		(void *) fp->f_addr, fp->f_name, fp->f_type, (long) fp->f_size);
24238465Smsmith	pager_output(lbuf);
24359854Sbp	if (fp->f_args != NULL) {
24438465Smsmith	    pager_output("    args: ");
24559854Sbp	    pager_output(fp->f_args);
24638465Smsmith	    pager_output("\n");
24738465Smsmith	}
24859854Sbp	if (fp->f_modules) {
24959854Sbp	    pager_output("  modules: ");
25059854Sbp	    for (mp = fp->f_modules; mp; mp = mp->m_next) {
25183321Speter		sprintf(lbuf, "%s.%d ", mp->m_name, mp->m_version);
25259854Sbp		pager_output(lbuf);
25359854Sbp	    }
25459854Sbp	    pager_output("\n");
25559854Sbp	}
25659854Sbp	if (verbose) {
25738764Smsmith	    /* XXX could add some formatting smarts here to display some better */
25859854Sbp	    for (md = fp->f_metadata; md != NULL; md = md->md_next) {
25939673Sdfr		sprintf(lbuf, "      0x%04x, 0x%lx\n", md->md_type, (long) md->md_size);
26038764Smsmith		pager_output(lbuf);
26138764Smsmith	    }
26259854Sbp	}
26338465Smsmith    }
26438465Smsmith    pager_close();
26538465Smsmith    return(CMD_OK);
26638465Smsmith}
26738465Smsmith
26838764Smsmith/*
26959854Sbp * File level interface, functions file_*
27038764Smsmith */
27138465Smsmithint
27259854Sbpfile_load(char *filename, vm_offset_t dest, struct preloaded_file **result)
27338465Smsmith{
274241069Sae    static int last_file_format = 0;
27559854Sbp    struct preloaded_file *fp;
27659854Sbp    int error;
27759854Sbp    int i;
27838764Smsmith
279220311Smarcel    if (archsw.arch_loadaddr != NULL)
280220311Smarcel	dest = archsw.arch_loadaddr(LOAD_RAW, filename, dest);
281220311Smarcel
28259854Sbp    error = EFTYPE;
283241069Sae    for (i = last_file_format, fp = NULL;
284241069Sae	file_formats[i] && fp == NULL; i++) {
285220332Smarcel	error = (file_formats[i]->l_load)(filename, dest, &fp);
28659854Sbp	if (error == 0) {
287241069Sae	    fp->f_loader = last_file_format = i; /* remember the loader */
28859854Sbp	    *result = fp;
28959854Sbp	    break;
290241069Sae	} else if (last_file_format == i && i != 0) {
291241069Sae		/* Restart from the beginning */
292241069Sae		last_file_format = i = 0;
293241069Sae		fp = NULL;
294241069Sae		continue;
29559854Sbp	}
29659854Sbp	if (error == EFTYPE)
29759854Sbp	    continue;		/* Unknown to this handler? */
29859854Sbp	if (error) {
29959854Sbp	    sprintf(command_errbuf, "can't load file '%s': %s",
30059854Sbp		filename, strerror(error));
30159854Sbp	    break;
30259854Sbp	}
30359854Sbp    }
30459854Sbp    return (error);
30559854Sbp}
30638465Smsmith
30759854Sbpstatic int
308207854Simpfile_load_dependencies(struct preloaded_file *base_file)
309207854Simp{
31059854Sbp    struct file_metadata *md;
31159854Sbp    struct preloaded_file *fp;
31283321Speter    struct mod_depend *verinfo;
31383321Speter    struct kernel_module *mp;
31459854Sbp    char *dmodname;
31559854Sbp    int error;
31659854Sbp
31759854Sbp    md = file_findmetadata(base_file, MODINFOMD_DEPLIST);
31859854Sbp    if (md == NULL)
31959854Sbp	return (0);
32059854Sbp    error = 0;
32159854Sbp    do {
32283321Speter	verinfo = (struct mod_depend*)md->md_data;
32383321Speter	dmodname = (char *)(verinfo + 1);
32483321Speter	if (file_findmodule(NULL, dmodname, verinfo) == NULL) {
32559854Sbp	    printf("loading required module '%s'\n", dmodname);
32683321Speter	    error = mod_load(dmodname, verinfo, 0, NULL);
32759854Sbp	    if (error)
32859854Sbp		break;
32983321Speter	    /*
33083321Speter	     * If module loaded via kld name which isn't listed
33183321Speter	     * in the linker.hints file, we should check if it have
33283321Speter	     * required version.
33383321Speter	     */
33483321Speter	    mp = file_findmodule(NULL, dmodname, verinfo);
33583321Speter	    if (mp == NULL) {
33683321Speter		sprintf(command_errbuf, "module '%s' exists but with wrong version",
33783321Speter		    dmodname);
33883321Speter		error = ENOENT;
33983321Speter		break;
34083321Speter	    }
34159854Sbp	}
34259854Sbp	md = metadata_next(md, MODINFOMD_DEPLIST);
34359854Sbp    } while (md);
34457468Sbp    if (!error)
34557468Sbp	return (0);
34657468Sbp    /* Load failed; discard everything */
34759854Sbp    while (base_file != NULL) {
34859854Sbp        fp = base_file;
34959854Sbp        base_file = base_file->f_next;
35059854Sbp        file_discard(fp);
35138764Smsmith    }
35257468Sbp    return (error);
35338764Smsmith}
35438764Smsmith/*
35538764Smsmith * We've been asked to load (name) as (type), so just suck it in,
35638764Smsmith * no arguments or anything.
35738764Smsmith */
35838764Smsmithint
35959854Sbpfile_loadraw(char *type, char *name)
36038764Smsmith{
36159854Sbp    struct preloaded_file	*fp;
36238764Smsmith    char			*cp;
36338764Smsmith    int				fd, got;
36438764Smsmith    vm_offset_t			laddr;
36538764Smsmith
36638764Smsmith    /* We can't load first */
36759854Sbp    if ((file_findfile(NULL, NULL)) == NULL) {
36838764Smsmith	command_errmsg = "can't load file before kernel";
36938764Smsmith	return(CMD_ERROR);
37038764Smsmith    }
37138764Smsmith
37239178Smsmith    /* locate the file on the load path */
37383321Speter    cp = file_search(name, NULL);
37439178Smsmith    if (cp == NULL) {
37539178Smsmith	sprintf(command_errbuf, "can't find '%s'", name);
37639178Smsmith	return(CMD_ERROR);
37739178Smsmith    }
37839178Smsmith    name = cp;
379220311Smarcel
38038764Smsmith    if ((fd = open(name, O_RDONLY)) < 0) {
38138764Smsmith	sprintf(command_errbuf, "can't open '%s': %s", name, strerror(errno));
38244570Sdcs	free(name);
38338764Smsmith	return(CMD_ERROR);
38438764Smsmith    }
38538764Smsmith
386220311Smarcel    if (archsw.arch_loadaddr != NULL)
387220311Smarcel	loadaddr = archsw.arch_loadaddr(LOAD_RAW, name, loadaddr);
388201340Snyan
38938764Smsmith    laddr = loadaddr;
39038764Smsmith    for (;;) {
39138764Smsmith	/* read in 4k chunks; size is not really important */
39238764Smsmith	got = archsw.arch_readin(fd, laddr, 4096);
39338764Smsmith	if (got == 0)				/* end of file */
39438764Smsmith	    break;
39538764Smsmith	if (got < 0) {				/* error */
39638764Smsmith	    sprintf(command_errbuf, "error reading '%s': %s", name, strerror(errno));
39744210Sdcs	    free(name);
39857269Smsmith	    close(fd);
39938764Smsmith	    return(CMD_ERROR);
40038764Smsmith	}
40138764Smsmith	laddr += got;
40238764Smsmith    }
40338764Smsmith
40438764Smsmith    /* Looks OK so far; create & populate control structure */
40559854Sbp    fp = file_alloc();
406240342Savg    fp->f_name = strdup(name);
40759854Sbp    fp->f_type = strdup(type);
40859854Sbp    fp->f_args = NULL;
40959854Sbp    fp->f_metadata = NULL;
41059854Sbp    fp->f_loader = -1;
41159854Sbp    fp->f_addr = loadaddr;
41259854Sbp    fp->f_size = laddr - loadaddr;
41338764Smsmith
41438764Smsmith    /* recognise space consumption */
41538764Smsmith    loadaddr = laddr;
41638764Smsmith
41759854Sbp    /* Add to the list of loaded files */
41859854Sbp    file_insert_tail(fp);
41957269Smsmith    close(fd);
42038764Smsmith    return(CMD_OK);
42138764Smsmith}
42238764Smsmith
42338764Smsmith/*
42459854Sbp * Load the module (name), pass it (argc),(argv), add container file
42559854Sbp * to the list of loaded files.
42659854Sbp * If module is already loaded just assign new argc/argv.
42738764Smsmith */
42859854Sbpint
42983321Spetermod_load(char *modname, struct mod_depend *verinfo, int argc, char *argv[])
43038764Smsmith{
43159854Sbp    struct kernel_module	*mp;
43259854Sbp    int				err;
43359854Sbp    char			*filename;
43438764Smsmith
43583321Speter    if (file_havepath(modname)) {
43683321Speter	printf("Warning: mod_load() called instead of mod_loadkld() for module '%s'\n", modname);
43783321Speter	return (mod_loadkld(modname, argc, argv));
43883321Speter    }
43957468Sbp    /* see if module is already loaded */
44083321Speter    mp = file_findmodule(NULL, modname, verinfo);
44157468Sbp    if (mp) {
44259854Sbp#ifdef moduleargs
44359854Sbp	if (mp->m_args)
44459854Sbp	    free(mp->m_args);
44559854Sbp	mp->m_args = unargv(argc, argv);
44659854Sbp#endif
44759854Sbp	sprintf(command_errbuf, "warning: module '%s' already loaded", mp->m_name);
44859854Sbp	return (0);
44957468Sbp    }
45059854Sbp    /* locate file with the module on the search path */
45183321Speter    filename = mod_searchmodule(modname, verinfo);
45259854Sbp    if (filename == NULL) {
45359854Sbp	sprintf(command_errbuf, "can't find '%s'", modname);
45459854Sbp	return (ENOENT);
45559854Sbp    }
45683321Speter    err = mod_loadkld(filename, argc, argv);
45783321Speter    return (err);
45883321Speter}
45983321Speter
46083321Speter/*
46183321Speter * Load specified KLD. If path is omitted, then try to locate it via
46283321Speter * search path.
46383321Speter */
46483321Speterint
46583321Spetermod_loadkld(const char *kldname, int argc, char *argv[])
46683321Speter{
46783321Speter    struct preloaded_file	*fp, *last_file;
46883321Speter    int				err;
46983321Speter    char			*filename;
47083321Speter
47183321Speter    /*
47283321Speter     * Get fully qualified KLD name
47383321Speter     */
47483321Speter    filename = file_search(kldname, kld_ext_list);
47583321Speter    if (filename == NULL) {
47683321Speter	sprintf(command_errbuf, "can't find '%s'", kldname);
47783321Speter	return (ENOENT);
47883321Speter    }
47983321Speter    /*
48083321Speter     * Check if KLD already loaded
48183321Speter     */
48283321Speter    fp = file_findfile(filename, NULL);
48383321Speter    if (fp) {
48483321Speter	sprintf(command_errbuf, "warning: KLD '%s' already loaded", filename);
48583321Speter	free(filename);
48683321Speter	return (0);
48783321Speter    }
48859854Sbp    for (last_file = preloaded_files;
48959854Sbp	 last_file != NULL && last_file->f_next != NULL;
49059854Sbp	 last_file = last_file->f_next)
49159854Sbp	;
49257468Sbp
49359854Sbp    do {
49459854Sbp	err = file_load(filename, loadaddr, &fp);
49559854Sbp	if (err)
49638764Smsmith	    break;
49759854Sbp	fp->f_args = unargv(argc, argv);
49859854Sbp	loadaddr = fp->f_addr + fp->f_size;
49959854Sbp	file_insert_tail(fp);		/* Add to the list of loaded files */
50086469Siedowse	if (file_load_dependencies(fp) != 0) {
50159854Sbp	    err = ENOENT;
50259854Sbp	    last_file->f_next = NULL;
50359854Sbp	    loadaddr = last_file->f_addr + last_file->f_size;
50459854Sbp	    fp = NULL;
50559854Sbp	    break;
50659854Sbp	}
50759854Sbp    } while(0);
50838764Smsmith    if (err == EFTYPE)
50959854Sbp	sprintf(command_errbuf, "don't know how to load module '%s'", filename);
51059854Sbp    if (err && fp)
51159854Sbp	file_discard(fp);
51259854Sbp    free(filename);
51357468Sbp    return (err);
51438764Smsmith}
51538764Smsmith
51659854Sbp/*
51759854Sbp * Find a file matching (name) and (type).
51859854Sbp * NULL may be passed as a wildcard to either.
51959854Sbp */
52059854Sbpstruct preloaded_file *
52159854Sbpfile_findfile(char *name, char *type)
52238764Smsmith{
52359854Sbp    struct preloaded_file *fp;
52438764Smsmith
52559854Sbp    for (fp = preloaded_files; fp != NULL; fp = fp->f_next) {
52659854Sbp	if (((name == NULL) || !strcmp(name, fp->f_name)) &&
52759854Sbp	    ((type == NULL) || !strcmp(type, fp->f_type)))
52859854Sbp	    break;
52959854Sbp    }
53059854Sbp    return (fp);
53138465Smsmith}
53238465Smsmith
53338764Smsmith/*
53459854Sbp * Find a module matching (name) inside of given file.
53559854Sbp * NULL may be passed as a wildcard.
53638764Smsmith */
53759854Sbpstruct kernel_module *
53883321Speterfile_findmodule(struct preloaded_file *fp, char *modname,
53983321Speter	struct mod_depend *verinfo)
54038465Smsmith{
54183321Speter    struct kernel_module *mp, *best;
54283321Speter    int bestver, mver;
54359854Sbp
54459854Sbp    if (fp == NULL) {
54559854Sbp	for (fp = preloaded_files; fp; fp = fp->f_next) {
54683321Speter	    mp = file_findmodule(fp, modname, verinfo);
54783321Speter    	    if (mp)
54883321Speter		return (mp);
54959854Sbp	}
55059854Sbp	return (NULL);
55138465Smsmith    }
55283321Speter    best = NULL;
55383321Speter    bestver = 0;
55459854Sbp    for (mp = fp->f_modules; mp; mp = mp->m_next) {
55583321Speter        if (strcmp(modname, mp->m_name) == 0) {
55683321Speter	    if (verinfo == NULL)
55783321Speter		return (mp);
55883321Speter	    mver = mp->m_version;
55983321Speter	    if (mver == verinfo->md_ver_preferred)
56083321Speter		return (mp);
56183321Speter	    if (mver >= verinfo->md_ver_minimum &&
56283321Speter		mver <= verinfo->md_ver_maximum &&
56383321Speter		mver > bestver) {
56483321Speter		best = mp;
56583321Speter		bestver = mver;
56683321Speter	    }
56783321Speter	}
56859854Sbp    }
56983321Speter    return (best);
57038465Smsmith}
57138764Smsmith/*
57238764Smsmith * Make a copy of (size) bytes of data from (p), and associate them as
57338764Smsmith * metadata of (type) to the module (mp).
57438764Smsmith */
57538712Smsmithvoid
57659854Sbpfile_addmetadata(struct preloaded_file *fp, int type, size_t size, void *p)
57738712Smsmith{
57859854Sbp    struct file_metadata	*md;
57938712Smsmith
58064187Sjhb    md = malloc(sizeof(struct file_metadata) - sizeof(md->md_data) + size);
58138712Smsmith    md->md_size = size;
58238712Smsmith    md->md_type = type;
58338712Smsmith    bcopy(p, md->md_data, size);
58459854Sbp    md->md_next = fp->f_metadata;
58559854Sbp    fp->f_metadata = md;
58638712Smsmith}
58738712Smsmith
58838764Smsmith/*
58959854Sbp * Find a metadata object of (type) associated with the file (fp)
59038764Smsmith */
59159854Sbpstruct file_metadata *
59259854Sbpfile_findmetadata(struct preloaded_file *fp, int type)
59338712Smsmith{
59459854Sbp    struct file_metadata *md;
59538712Smsmith
59659854Sbp    for (md = fp->f_metadata; md != NULL; md = md->md_next)
59738712Smsmith	if (md->md_type == type)
59838712Smsmith	    break;
59938712Smsmith    return(md);
60038712Smsmith}
60138764Smsmith
60259854Sbpstruct file_metadata *
60359854Sbpmetadata_next(struct file_metadata *md, int type)
60457468Sbp{
60557468Sbp    if (md == NULL)
60657468Sbp	return (NULL);
60757468Sbp    while((md = md->md_next) != NULL)
60857468Sbp	if (md->md_type == type)
60957468Sbp	    break;
61057468Sbp    return (md);
61157468Sbp}
61257468Sbp
61383321Speterstatic char *emptyextlist[] = { "", NULL };
61483321Speter
61538764Smsmith/*
61683321Speter * Check if the given file is in place and return full path to it.
61783321Speter */
61883321Speterstatic char *
61983321Speterfile_lookup(const char *path, const char *name, int namelen, char **extlist)
62083321Speter{
62183321Speter    struct stat	st;
62283321Speter    char	*result, *cp, **cpp;
62383321Speter    int		pathlen, extlen, len;
62483321Speter
62583321Speter    pathlen = strlen(path);
62683321Speter    extlen = 0;
62783321Speter    if (extlist == NULL)
62883321Speter	extlist = emptyextlist;
62983321Speter    for (cpp = extlist; *cpp; cpp++) {
63083321Speter	len = strlen(*cpp);
63183321Speter	if (len > extlen)
63283321Speter	    extlen = len;
63383321Speter    }
63483321Speter    result = malloc(pathlen + namelen + extlen + 2);
63583321Speter    if (result == NULL)
63683321Speter	return (NULL);
63783321Speter    bcopy(path, result, pathlen);
63883321Speter    if (pathlen > 0 && result[pathlen - 1] != '/')
63983321Speter	result[pathlen++] = '/';
64083321Speter    cp = result + pathlen;
64183321Speter    bcopy(name, cp, namelen);
64283321Speter    cp += namelen;
64383321Speter    for (cpp = extlist; *cpp; cpp++) {
64483321Speter	strcpy(cp, *cpp);
64583321Speter	if (stat(result, &st) == 0 && S_ISREG(st.st_mode))
64683321Speter	    return result;
64783321Speter    }
64883321Speter    free(result);
64983321Speter    return NULL;
65083321Speter}
65183321Speter
65283321Speter/*
65383321Speter * Check if file name have any qualifiers
65483321Speter */
65583321Speterstatic int
65683321Speterfile_havepath(const char *name)
65783321Speter{
65883321Speter    const char		*cp;
65983321Speter
66083321Speter    archsw.arch_getdev(NULL, name, &cp);
66183321Speter    return (cp != name || strchr(name, '/') != NULL);
66283321Speter}
66383321Speter
66483321Speter/*
66539178Smsmith * Attempt to find the file (name) on the module searchpath.
66638764Smsmith * If (name) is qualified in any way, we simply check it and
66738764Smsmith * return it or NULL.  If it is not qualified, then we attempt
66838764Smsmith * to construct a path using entries in the environment variable
66938764Smsmith * module_path.
67038764Smsmith *
67138764Smsmith * The path we return a pointer to need never be freed, as we manage
67238764Smsmith * it internally.
67338764Smsmith */
67438764Smsmithstatic char *
67583321Speterfile_search(const char *name, char **extlist)
67638764Smsmith{
67783321Speter    struct moduledir	*mdp;
67883321Speter    struct stat		sb;
67944570Sdcs    char		*result;
68083321Speter    int			namelen;
68138764Smsmith
68238764Smsmith    /* Don't look for nothing */
68344210Sdcs    if (name == NULL)
68483321Speter	return(NULL);
68538764Smsmith
68644210Sdcs    if (*name == 0)
68744210Sdcs	return(strdup(name));
68844210Sdcs
68983321Speter    if (file_havepath(name)) {
69038764Smsmith	/* Qualified, so just see if it exists */
69138764Smsmith	if (stat(name, &sb) == 0)
69244210Sdcs	    return(strdup(name));
69338764Smsmith	return(NULL);
69438764Smsmith    }
69583321Speter    moduledir_rebuild();
69644570Sdcs    result = NULL;
69783321Speter    namelen = strlen(name);
69883321Speter    STAILQ_FOREACH(mdp, &moduledir_list, d_link) {
69983321Speter	result = file_lookup(mdp->d_path, name, namelen, extlist);
70083321Speter	if (result)
70138764Smsmith	    break;
70238764Smsmith    }
70338764Smsmith    return(result);
70438764Smsmith}
70538764Smsmith
70683321Speter#define	INT_ALIGN(base, ptr)	ptr = \
70783321Speter	(base) + (((ptr) - (base) + sizeof(int) - 1) & ~(sizeof(int) - 1))
70883321Speter
70983321Speterstatic char *
71083321Spetermod_search_hints(struct moduledir *mdp, const char *modname,
71183321Speter	struct mod_depend *verinfo)
71283321Speter{
71383321Speter    u_char	*cp, *recptr, *bufend, *best;
71483321Speter    char	*result;
71583321Speter    int		*intp, bestver, blen, clen, found, ival, modnamelen, reclen;
71683321Speter
71783321Speter    moduledir_readhints(mdp);
71883321Speter    modnamelen = strlen(modname);
71983321Speter    found = 0;
72083321Speter    result = NULL;
72183321Speter    bestver = 0;
72283321Speter    if (mdp->d_hints == NULL)
72383321Speter	goto bad;
72483321Speter    recptr = mdp->d_hints;
72583321Speter    bufend = recptr + mdp->d_hintsz;
72683321Speter    clen = blen = 0;
72783321Speter    best = cp = NULL;
72883321Speter    while (recptr < bufend && !found) {
72983321Speter	intp = (int*)recptr;
73083321Speter	reclen = *intp++;
73183321Speter	ival = *intp++;
73283321Speter	cp = (char*)intp;
73383321Speter	switch (ival) {
73483321Speter	case MDT_VERSION:
73583321Speter	    clen = *cp++;
73683321Speter	    if (clen != modnamelen || bcmp(cp, modname, clen) != 0)
73783321Speter		break;
73883321Speter	    cp += clen;
73983321Speter	    INT_ALIGN(mdp->d_hints, cp);
74083321Speter	    ival = *(int*)cp;
74183321Speter	    cp += sizeof(int);
74283321Speter	    clen = *cp++;
74383321Speter	    if (verinfo == NULL || ival == verinfo->md_ver_preferred) {
74483321Speter		found = 1;
74583321Speter		break;
74683321Speter	    }
74783321Speter	    if (ival >= verinfo->md_ver_minimum &&
74883321Speter		ival <= verinfo->md_ver_maximum &&
74983321Speter		ival > bestver) {
75083321Speter		bestver = ival;
75183321Speter		best = cp;
75283321Speter		blen = clen;
75383321Speter	    }
75483321Speter	    break;
75583321Speter	default:
75683321Speter	    break;
75783321Speter	}
75883321Speter	recptr += reclen + sizeof(int);
75983321Speter    }
76083321Speter    /*
76183321Speter     * Finally check if KLD is in the place
76283321Speter     */
76383321Speter    if (found)
76483321Speter	result = file_lookup(mdp->d_path, cp, clen, NULL);
76583321Speter    else if (best)
76683321Speter	result = file_lookup(mdp->d_path, best, blen, NULL);
76783321Speterbad:
76883321Speter    /*
76983321Speter     * If nothing found or hints is absent - fallback to the old way
77083321Speter     * by using "kldname[.ko]" as module name.
77183321Speter     */
77283321Speter    if (!found && !bestver && result == NULL)
77383321Speter	result = file_lookup(mdp->d_path, modname, modnamelen, kld_ext_list);
77483321Speter    return result;
77583321Speter}
77683321Speter
77738764Smsmith/*
77839178Smsmith * Attempt to locate the file containing the module (name)
77939178Smsmith */
78039178Smsmithstatic char *
78183321Spetermod_searchmodule(char *name, struct mod_depend *verinfo)
78239178Smsmith{
78383321Speter    struct	moduledir *mdp;
78483321Speter    char	*result;
78539178Smsmith
78683321Speter    moduledir_rebuild();
78783321Speter    /*
78883321Speter     * Now we ready to lookup module in the given directories
78983321Speter     */
79083321Speter    result = NULL;
79183321Speter    STAILQ_FOREACH(mdp, &moduledir_list, d_link) {
79283321Speter	result = mod_search_hints(mdp, name, verinfo);
79383321Speter	if (result)
79483321Speter	    break;
79583321Speter    }
79683321Speter
79739178Smsmith    return(result);
79839178Smsmith}
79939178Smsmith
80059854Sbpint
80183321Speterfile_addmodule(struct preloaded_file *fp, char *modname, int version,
80259854Sbp	struct kernel_module **newmp)
80359854Sbp{
80459854Sbp    struct kernel_module *mp;
80583321Speter    struct mod_depend mdepend;
80639178Smsmith
80783321Speter    bzero(&mdepend, sizeof(mdepend));
80883321Speter    mdepend.md_ver_preferred = version;
80983321Speter    mp = file_findmodule(fp, modname, &mdepend);
81059854Sbp    if (mp)
81159854Sbp	return (EEXIST);
81259854Sbp    mp = malloc(sizeof(struct kernel_module));
81359854Sbp    if (mp == NULL)
81459854Sbp	return (ENOMEM);
81559854Sbp    bzero(mp, sizeof(struct kernel_module));
81659854Sbp    mp->m_name = strdup(modname);
81783321Speter    mp->m_version = version;
81859854Sbp    mp->m_fp = fp;
81959854Sbp    mp->m_next = fp->f_modules;
82059854Sbp    fp->f_modules = mp;
82159854Sbp    if (newmp)
82259854Sbp	*newmp = mp;
82359854Sbp    return (0);
82459854Sbp}
82559854Sbp
82639178Smsmith/*
82759854Sbp * Throw a file away
82838764Smsmith */
82938764Smsmithvoid
83059854Sbpfile_discard(struct preloaded_file *fp)
83138764Smsmith{
83259854Sbp    struct file_metadata	*md, *md1;
83359854Sbp    struct kernel_module	*mp, *mp1;
83459854Sbp    if (fp == NULL)
83559854Sbp	return;
83659854Sbp    md = fp->f_metadata;
83759854Sbp    while (md) {
83859854Sbp	md1 = md;
83959854Sbp	md = md->md_next;
84059854Sbp	free(md1);
84159854Sbp    }
84259854Sbp    mp = fp->f_modules;
84359854Sbp    while (mp) {
84459854Sbp	if (mp->m_name)
84539178Smsmith	    free(mp->m_name);
84659854Sbp	mp1 = mp;
84759854Sbp	mp = mp->m_next;
84859854Sbp	free(mp1);
84959854Sbp    }
85059854Sbp    if (fp->f_name != NULL)
85159854Sbp	free(fp->f_name);
85259854Sbp    if (fp->f_type != NULL)
85359854Sbp        free(fp->f_type);
85459854Sbp    if (fp->f_args != NULL)
85559854Sbp        free(fp->f_args);
85659854Sbp    free(fp);
85738764Smsmith}
85838764Smsmith
85938764Smsmith/*
86059854Sbp * Allocate a new file; must be used instead of malloc()
86139178Smsmith * to ensure safe initialisation.
86239178Smsmith */
86359854Sbpstruct preloaded_file *
86459854Sbpfile_alloc(void)
86539178Smsmith{
86659854Sbp    struct preloaded_file	*fp;
86739178Smsmith
86859854Sbp    if ((fp = malloc(sizeof(struct preloaded_file))) != NULL) {
86959854Sbp	bzero(fp, sizeof(struct preloaded_file));
87039178Smsmith    }
87159854Sbp    return (fp);
87239178Smsmith}
87339178Smsmith
87439178Smsmith/*
87538764Smsmith * Add a module to the chain
87638764Smsmith */
87738764Smsmithstatic void
87859854Sbpfile_insert_tail(struct preloaded_file *fp)
87938764Smsmith{
88059854Sbp    struct preloaded_file	*cm;
88138764Smsmith
88259854Sbp    /* Append to list of loaded file */
88359854Sbp    fp->f_next = NULL;
88459854Sbp    if (preloaded_files == NULL) {
88559854Sbp	preloaded_files = fp;
88638764Smsmith    } else {
88759854Sbp	for (cm = preloaded_files; cm->f_next != NULL; cm = cm->f_next)
88838764Smsmith	    ;
88959854Sbp	cm->f_next = fp;
89038764Smsmith    }
89138764Smsmith}
89259854Sbp
89383321Speterstatic char *
89483321Spetermoduledir_fullpath(struct moduledir *mdp, const char *fname)
89583321Speter{
89683321Speter    char *cp;
89783321Speter
89883321Speter    cp = malloc(strlen(mdp->d_path) + strlen(fname) + 2);
89983321Speter    if (cp == NULL)
90083321Speter	return NULL;
90183321Speter    strcpy(cp, mdp->d_path);
90283321Speter    strcat(cp, "/");
90383321Speter    strcat(cp, fname);
90483321Speter    return (cp);
90583321Speter}
90683321Speter
90783321Speter/*
90883321Speter * Read linker.hints file into memory performing some sanity checks.
90983321Speter */
91083321Speterstatic void
91183321Spetermoduledir_readhints(struct moduledir *mdp)
91283321Speter{
91383321Speter    struct stat	st;
91483321Speter    char	*path;
91583321Speter    int		fd, size, version;
91683321Speter
91783321Speter    if (mdp->d_hints != NULL || (mdp->d_flags & MDIR_NOHINTS))
91883321Speter	return;
91983321Speter    path = moduledir_fullpath(mdp, "linker.hints");
920154257Smarius    if (stat(path, &st) != 0 ||
921154257Smarius	st.st_size < (ssize_t)(sizeof(version) + sizeof(int)) ||
92283321Speter	st.st_size > 100 * 1024 || (fd = open(path, O_RDONLY)) < 0) {
92383321Speter	free(path);
92483321Speter	mdp->d_flags |= MDIR_NOHINTS;
92583321Speter	return;
92683321Speter    }
92783321Speter    free(path);
92883321Speter    size = read(fd, &version, sizeof(version));
92983321Speter    if (size != sizeof(version) || version != LINKER_HINTS_VERSION)
93083321Speter	goto bad;
93183321Speter    size = st.st_size - size;
93283321Speter    mdp->d_hints = malloc(size);
93383321Speter    if (mdp->d_hints == NULL)
93483321Speter	goto bad;
93583321Speter    if (read(fd, mdp->d_hints, size) != size)
93683321Speter	goto bad;
93783321Speter    mdp->d_hintsz = size;
93883321Speter    close(fd);
93983321Speter    return;
94083321Speterbad:
94183321Speter    close(fd);
94283321Speter    if (mdp->d_hints) {
94383321Speter	free(mdp->d_hints);
94483321Speter	mdp->d_hints = NULL;
94583321Speter    }
94683321Speter    mdp->d_flags |= MDIR_NOHINTS;
94783321Speter    return;
94883321Speter}
94983321Speter
95083321Speter/*
95183321Speter * Extract directories from the ';' separated list, remove duplicates.
95283321Speter */
95383321Speterstatic void
95483321Spetermoduledir_rebuild(void)
95583321Speter{
95683321Speter    struct	moduledir *mdp, *mtmp;
95783321Speter    const char	*path, *cp, *ep;
95883321Speter    int		cplen;
95983321Speter
96083321Speter    path = getenv("module_path");
96183321Speter    if (path == NULL)
96283321Speter	path = default_searchpath;
96383321Speter    /*
96483321Speter     * Rebuild list of module directories if it changed
96583321Speter     */
96683321Speter    STAILQ_FOREACH(mdp, &moduledir_list, d_link)
96783321Speter	mdp->d_flags |= MDIR_REMOVED;
96883321Speter
96983321Speter    for (ep = path; *ep != 0;  ep++) {
97083321Speter	cp = ep;
97183321Speter	for (; *ep != 0 && *ep != ';'; ep++)
97283321Speter	    ;
97383321Speter	/*
97483321Speter	 * Ignore trailing slashes
97583321Speter	 */
97683321Speter	for (cplen = ep - cp; cplen > 1 && cp[cplen - 1] == '/'; cplen--)
97783321Speter	    ;
97883321Speter	STAILQ_FOREACH(mdp, &moduledir_list, d_link) {
97983321Speter	    if (strlen(mdp->d_path) != cplen ||	bcmp(cp, mdp->d_path, cplen) != 0)
98083321Speter		continue;
98183321Speter	    mdp->d_flags &= ~MDIR_REMOVED;
98283321Speter	    break;
98383321Speter	}
98483321Speter	if (mdp == NULL) {
98583321Speter	    mdp = malloc(sizeof(*mdp) + cplen + 1);
98683321Speter	    if (mdp == NULL)
98783321Speter		return;
98883321Speter	    mdp->d_path = (char*)(mdp + 1);
98983321Speter	    bcopy(cp, mdp->d_path, cplen);
99083321Speter	    mdp->d_path[cplen] = 0;
99183321Speter	    mdp->d_hints = NULL;
99283321Speter	    mdp->d_flags = 0;
99383321Speter	    STAILQ_INSERT_TAIL(&moduledir_list, mdp, d_link);
99483321Speter	}
99594419Speter	if (*ep == 0)
99694419Speter	    break;
99783321Speter    }
99883321Speter    /*
99983321Speter     * Delete unused directories if any
100083321Speter     */
100183321Speter    mdp = STAILQ_FIRST(&moduledir_list);
100283321Speter    while (mdp) {
100383321Speter	if ((mdp->d_flags & MDIR_REMOVED) == 0) {
100483321Speter	    mdp = STAILQ_NEXT(mdp, d_link);
100583321Speter	} else {
100683321Speter	    if (mdp->d_hints)
100783321Speter		free(mdp->d_hints);
100883321Speter	    mtmp = mdp;
100983321Speter	    mdp = STAILQ_NEXT(mdp, d_link);
101083321Speter	    STAILQ_REMOVE(&moduledir_list, mtmp, moduledir, d_link);
101183321Speter	    free(mtmp);
101283321Speter	}
101383321Speter    }
101483321Speter    return;
101583321Speter}
1016