module.c revision 298230
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 298230 2016-04-18 23:09:22Z allanjude $");
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>
40269614Smarcel#include <sys/stdint.h>
4138465Smsmith
4238465Smsmith#include "bootstrap.h"
4338465Smsmith
4483321Speter#define	MDIR_REMOVED	0x0001
4583321Speter#define	MDIR_NOHINTS	0x0002
4683321Speter
4783321Speterstruct moduledir {
4883321Speter	char	*d_path;	/* path of modules directory */
4983321Speter	u_char	*d_hints;	/* content of linker.hints file */
5083321Speter	int	d_hintsz;	/* size of hints data */
5183321Speter	int	d_flags;
5283321Speter	STAILQ_ENTRY(moduledir) d_link;
5383321Speter};
5483321Speter
5559854Sbpstatic int			file_load(char *filename, vm_offset_t dest, struct preloaded_file **result);
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
69271054Sian#if defined(LOADER_FDT_SUPPORT)
70271054Sianstatic const char	*default_searchpath =
71271054Sian    "/boot/kernel;/boot/modules;/boot/dtb";
72271054Sian#else
73111852Srustatic const char	*default_searchpath ="/boot/kernel;/boot/modules";
74271054Sian#endif
7539178Smsmith
7683321Speterstatic STAILQ_HEAD(, moduledir) moduledir_list = STAILQ_HEAD_INITIALIZER(moduledir_list);
7783321Speter
7859854Sbpstruct preloaded_file *preloaded_files = NULL;
7938465Smsmith
8083321Speterstatic char *kld_ext_list[] = {
8183321Speter    ".ko",
8283321Speter    "",
83172445Sobrien    ".debug",
8483321Speter    NULL
8583321Speter};
8683321Speter
8783321Speter
8838764Smsmith/*
8938764Smsmith * load an object, either a disk file or code module.
9038764Smsmith *
9138764Smsmith * To load a file, the syntax is:
9238764Smsmith *
9338764Smsmith * load -t <type> <path>
9438764Smsmith *
9538764Smsmith * code modules are loaded as:
9638764Smsmith *
9738764Smsmith * load <path> <options>
9838764Smsmith */
9938764Smsmith
10038465SmsmithCOMMAND_SET(load, "load", "load a kernel or module", command_load);
10138465Smsmith
10238465Smsmithstatic int
10338465Smsmithcommand_load(int argc, char *argv[])
10438465Smsmith{
105286234Strasz    struct preloaded_file *fp;
10638764Smsmith    char	*typestr;
10783321Speter    int		dofile, dokld, ch, error;
10838764Smsmith
10983321Speter    dokld = dofile = 0;
11038764Smsmith    optind = 1;
11142512Smsmith    optreset = 1;
11238764Smsmith    typestr = NULL;
11353993Sdcs    if (argc == 1) {
11453993Sdcs	command_errmsg = "no filename specified";
115293835Ssmh	return (CMD_CRIT);
11653993Sdcs    }
11783321Speter    while ((ch = getopt(argc, argv, "kt:")) != -1) {
11838764Smsmith	switch(ch) {
11983321Speter	case 'k':
12083321Speter	    dokld = 1;
12183321Speter	    break;
12238764Smsmith	case 't':
12338764Smsmith	    typestr = optarg;
12438764Smsmith	    dofile = 1;
12538764Smsmith	    break;
12638764Smsmith	case '?':
12738764Smsmith	default:
12838764Smsmith	    /* getopt has already reported an error */
129293835Ssmh	    return (CMD_OK);
13038764Smsmith	}
13138764Smsmith    }
13238764Smsmith    argv += (optind - 1);
13338764Smsmith    argc -= (optind - 1);
13438764Smsmith
13538764Smsmith    /*
13638764Smsmith     * Request to load a raw file?
13738764Smsmith     */
13838764Smsmith    if (dofile) {
13977971Sjesper	if ((argc != 2) || (typestr == NULL) || (*typestr == 0)) {
14038764Smsmith	    command_errmsg = "invalid load type";
141293835Ssmh	    return (CMD_CRIT);
14238764Smsmith	}
143286234Strasz
144286234Strasz	fp = file_findfile(argv[1], typestr);
145286234Strasz	if (fp) {
146286234Strasz		sprintf(command_errbuf, "warning: file '%s' already loaded", argv[1]);
147293835Ssmh		return (CMD_WARN);
148286234Strasz	}
149286234Strasz
150293835Ssmh	if (file_loadraw(argv[1], typestr, 1) != NULL)
151293835Ssmh		return (CMD_OK);
152293835Ssmh
153293835Ssmh	/* Failing to load mfs_root is never going to end well! */
154293835Ssmh	if (strcmp("mfs_root", typestr) == 0)
155293835Ssmh		return (CMD_FATAL);
156293835Ssmh
157293835Ssmh	return (CMD_ERROR);
15838764Smsmith    }
15938764Smsmith    /*
16083321Speter     * Do we have explicit KLD load ?
16183321Speter     */
16283321Speter    if (dokld || file_havepath(argv[1])) {
16383321Speter	error = mod_loadkld(argv[1], argc - 2, argv + 2);
164293835Ssmh	if (error == EEXIST) {
16583321Speter	    sprintf(command_errbuf, "warning: KLD '%s' already loaded", argv[1]);
166293835Ssmh	    return (CMD_WARN);
167293835Ssmh	}
168293835Ssmh
169293835Ssmh	return (error == 0 ? CMD_OK : CMD_CRIT);
17083321Speter    }
17183321Speter    /*
17238764Smsmith     * Looks like a request for a module.
17338764Smsmith     */
17483321Speter    error = mod_load(argv[1], NULL, argc - 2, argv + 2);
175293835Ssmh    if (error == EEXIST) {
17657468Sbp	sprintf(command_errbuf, "warning: module '%s' already loaded", argv[1]);
177293835Ssmh	return (CMD_WARN);
178293835Ssmh    }
179293835Ssmh
180293835Ssmh    return (error == 0 ? CMD_OK : CMD_CRIT);
18138465Smsmith}
18238465Smsmith
183188666SthompsaCOMMAND_SET(load_geli, "load_geli", "load a geli key", command_load_geli);
184188666Sthompsa
185188666Sthompsastatic int
186188666Sthompsacommand_load_geli(int argc, char *argv[])
187188666Sthompsa{
188188666Sthompsa    char	typestr[80];
189188666Sthompsa    char	*cp;
190188666Sthompsa    int		ch, num;
191188666Sthompsa
192188666Sthompsa    if (argc < 3) {
193188666Sthompsa	    command_errmsg = "usage is [-n key#] <prov> <file>";
194188666Sthompsa	    return(CMD_ERROR);
195188666Sthompsa    }
196188666Sthompsa
197188666Sthompsa    num = 0;
198188666Sthompsa    optind = 1;
199188666Sthompsa    optreset = 1;
200188666Sthompsa    while ((ch = getopt(argc, argv, "n:")) != -1) {
201188666Sthompsa	switch(ch) {
202188666Sthompsa	case 'n':
203188666Sthompsa	    num = strtol(optarg, &cp, 0);
204188666Sthompsa	    if (cp == optarg) {
205188666Sthompsa		    sprintf(command_errbuf, "bad key index '%s'", optarg);
206188666Sthompsa		    return(CMD_ERROR);
207188666Sthompsa	    }
208188666Sthompsa	    break;
209188666Sthompsa	case '?':
210188666Sthompsa	default:
211188666Sthompsa	    /* getopt has already reported an error */
212188666Sthompsa	    return(CMD_OK);
213188666Sthompsa	}
214188666Sthompsa    }
215188666Sthompsa    argv += (optind - 1);
216188666Sthompsa    argc -= (optind - 1);
217188666Sthompsa    sprintf(typestr, "%s:geli_keyfile%d", argv[1], num);
218277215Sroyger    return (file_loadraw(argv[2], typestr, 1) ? CMD_OK : CMD_ERROR);
219188666Sthompsa}
220188666Sthompsa
221269618Smarcelvoid
222269618Smarcelunload(void)
223269618Smarcel{
224269618Smarcel    struct preloaded_file *fp;
22538712Smsmith
22659854Sbp    while (preloaded_files != NULL) {
22759854Sbp	fp = preloaded_files;
22859854Sbp	preloaded_files = preloaded_files->f_next;
22959854Sbp	file_discard(fp);
23038712Smsmith    }
23138712Smsmith    loadaddr = 0;
23265613Sdcs    unsetenv("kernelname");
233269618Smarcel}
234269618Smarcel
235269618SmarcelCOMMAND_SET(unload, "unload", "unload all modules", command_unload);
236269618Smarcel
237269618Smarcelstatic int
238269618Smarcelcommand_unload(int argc, char *argv[])
239269618Smarcel{
240269618Smarcel    unload();
24138712Smsmith    return(CMD_OK);
24238712Smsmith}
24338712Smsmith
24438465SmsmithCOMMAND_SET(lsmod, "lsmod", "list loaded modules", command_lsmod);
24538465Smsmith
24638465Smsmithstatic int
24738465Smsmithcommand_lsmod(int argc, char *argv[])
24838465Smsmith{
24959854Sbp    struct preloaded_file	*fp;
25059854Sbp    struct kernel_module	*mp;
25159854Sbp    struct file_metadata	*md;
25238465Smsmith    char			lbuf[80];
25338764Smsmith    int				ch, verbose;
25438764Smsmith
25538764Smsmith    verbose = 0;
25638764Smsmith    optind = 1;
25742512Smsmith    optreset = 1;
25838764Smsmith    while ((ch = getopt(argc, argv, "v")) != -1) {
25938764Smsmith	switch(ch) {
26038764Smsmith	case 'v':
26138764Smsmith	    verbose = 1;
26238764Smsmith	    break;
26338764Smsmith	case '?':
26438764Smsmith	default:
26538764Smsmith	    /* getopt has already reported an error */
26638764Smsmith	    return(CMD_OK);
26738764Smsmith	}
26838764Smsmith    }
26938764Smsmith
27038465Smsmith    pager_open();
27159854Sbp    for (fp = preloaded_files; fp; fp = fp->f_next) {
272269616Smarcel	sprintf(lbuf, " %p: ", (void *) fp->f_addr);
27338465Smsmith	pager_output(lbuf);
274269616Smarcel	pager_output(fp->f_name);
275269616Smarcel	sprintf(lbuf, " (%s, 0x%lx)\n", fp->f_type, (long)fp->f_size);
276269616Smarcel	pager_output(lbuf);
27759854Sbp	if (fp->f_args != NULL) {
27838465Smsmith	    pager_output("    args: ");
27959854Sbp	    pager_output(fp->f_args);
28038465Smsmith	    pager_output("\n");
28138465Smsmith	}
28259854Sbp	if (fp->f_modules) {
28359854Sbp	    pager_output("  modules: ");
28459854Sbp	    for (mp = fp->f_modules; mp; mp = mp->m_next) {
28583321Speter		sprintf(lbuf, "%s.%d ", mp->m_name, mp->m_version);
28659854Sbp		pager_output(lbuf);
28759854Sbp	    }
28859854Sbp	    pager_output("\n");
28959854Sbp	}
29059854Sbp	if (verbose) {
29138764Smsmith	    /* XXX could add some formatting smarts here to display some better */
29259854Sbp	    for (md = fp->f_metadata; md != NULL; md = md->md_next) {
29339673Sdfr		sprintf(lbuf, "      0x%04x, 0x%lx\n", md->md_type, (long) md->md_size);
29438764Smsmith		pager_output(lbuf);
29538764Smsmith	    }
29659854Sbp	}
29738465Smsmith    }
29838465Smsmith    pager_close();
29938465Smsmith    return(CMD_OK);
30038465Smsmith}
30138465Smsmith
30238764Smsmith/*
30359854Sbp * File level interface, functions file_*
30438764Smsmith */
30538465Smsmithint
30659854Sbpfile_load(char *filename, vm_offset_t dest, struct preloaded_file **result)
30738465Smsmith{
308241069Sae    static int last_file_format = 0;
30959854Sbp    struct preloaded_file *fp;
31059854Sbp    int error;
31159854Sbp    int i;
31238764Smsmith
313220311Smarcel    if (archsw.arch_loadaddr != NULL)
314220311Smarcel	dest = archsw.arch_loadaddr(LOAD_RAW, filename, dest);
315220311Smarcel
31659854Sbp    error = EFTYPE;
317241069Sae    for (i = last_file_format, fp = NULL;
318241069Sae	file_formats[i] && fp == NULL; i++) {
319220332Smarcel	error = (file_formats[i]->l_load)(filename, dest, &fp);
32059854Sbp	if (error == 0) {
321241069Sae	    fp->f_loader = last_file_format = i; /* remember the loader */
32259854Sbp	    *result = fp;
32359854Sbp	    break;
324241069Sae	} else if (last_file_format == i && i != 0) {
325241070Sae	    /* Restart from the beginning */
326249719Sae	    i = -1;
327249719Sae	    last_file_format = 0;
328241070Sae	    fp = NULL;
329241070Sae	    continue;
33059854Sbp	}
33159854Sbp	if (error == EFTYPE)
33259854Sbp	    continue;		/* Unknown to this handler? */
33359854Sbp	if (error) {
33459854Sbp	    sprintf(command_errbuf, "can't load file '%s': %s",
33559854Sbp		filename, strerror(error));
33659854Sbp	    break;
33759854Sbp	}
33859854Sbp    }
33959854Sbp    return (error);
34059854Sbp}
34138465Smsmith
34259854Sbpstatic int
343207854Simpfile_load_dependencies(struct preloaded_file *base_file)
344207854Simp{
34559854Sbp    struct file_metadata *md;
34659854Sbp    struct preloaded_file *fp;
34783321Speter    struct mod_depend *verinfo;
34883321Speter    struct kernel_module *mp;
34959854Sbp    char *dmodname;
35059854Sbp    int error;
35159854Sbp
35259854Sbp    md = file_findmetadata(base_file, MODINFOMD_DEPLIST);
35359854Sbp    if (md == NULL)
35459854Sbp	return (0);
35559854Sbp    error = 0;
35659854Sbp    do {
35783321Speter	verinfo = (struct mod_depend*)md->md_data;
35883321Speter	dmodname = (char *)(verinfo + 1);
35983321Speter	if (file_findmodule(NULL, dmodname, verinfo) == NULL) {
36059854Sbp	    printf("loading required module '%s'\n", dmodname);
36183321Speter	    error = mod_load(dmodname, verinfo, 0, NULL);
36259854Sbp	    if (error)
36359854Sbp		break;
36483321Speter	    /*
36583321Speter	     * If module loaded via kld name which isn't listed
36683321Speter	     * in the linker.hints file, we should check if it have
36783321Speter	     * required version.
36883321Speter	     */
36983321Speter	    mp = file_findmodule(NULL, dmodname, verinfo);
37083321Speter	    if (mp == NULL) {
37183321Speter		sprintf(command_errbuf, "module '%s' exists but with wrong version",
37283321Speter		    dmodname);
37383321Speter		error = ENOENT;
37483321Speter		break;
37583321Speter	    }
37659854Sbp	}
37759854Sbp	md = metadata_next(md, MODINFOMD_DEPLIST);
37859854Sbp    } while (md);
37957468Sbp    if (!error)
38057468Sbp	return (0);
38157468Sbp    /* Load failed; discard everything */
38259854Sbp    while (base_file != NULL) {
38359854Sbp        fp = base_file;
38459854Sbp        base_file = base_file->f_next;
38559854Sbp        file_discard(fp);
38638764Smsmith    }
38757468Sbp    return (error);
38838764Smsmith}
389246953Skientzle
39038764Smsmith/*
391294058Ssmh * We've been asked to load (fname) as (type), so just suck it in,
39238764Smsmith * no arguments or anything.
39338764Smsmith */
394262345Sianstruct preloaded_file *
395294058Ssmhfile_loadraw(const char *fname, char *type, int insert)
39638764Smsmith{
39759854Sbp    struct preloaded_file	*fp;
398294058Ssmh    char			*name;
39938764Smsmith    int				fd, got;
40038764Smsmith    vm_offset_t			laddr;
40138764Smsmith
40238764Smsmith    /* We can't load first */
40359854Sbp    if ((file_findfile(NULL, NULL)) == NULL) {
40438764Smsmith	command_errmsg = "can't load file before kernel";
405262345Sian	return(NULL);
40638764Smsmith    }
40738764Smsmith
40839178Smsmith    /* locate the file on the load path */
409294058Ssmh    name = file_search(fname, NULL);
410294058Ssmh    if (name == NULL) {
411294058Ssmh	sprintf(command_errbuf, "can't find '%s'", fname);
412262345Sian	return(NULL);
41339178Smsmith    }
414220311Smarcel
41538764Smsmith    if ((fd = open(name, O_RDONLY)) < 0) {
41638764Smsmith	sprintf(command_errbuf, "can't open '%s': %s", name, strerror(errno));
41744570Sdcs	free(name);
418262345Sian	return(NULL);
41938764Smsmith    }
42038764Smsmith
421220311Smarcel    if (archsw.arch_loadaddr != NULL)
422220311Smarcel	loadaddr = archsw.arch_loadaddr(LOAD_RAW, name, loadaddr);
423201340Snyan
424269614Smarcel    printf("%s ", name);
425269614Smarcel
42638764Smsmith    laddr = loadaddr;
42738764Smsmith    for (;;) {
42838764Smsmith	/* read in 4k chunks; size is not really important */
42938764Smsmith	got = archsw.arch_readin(fd, laddr, 4096);
43038764Smsmith	if (got == 0)				/* end of file */
43138764Smsmith	    break;
43238764Smsmith	if (got < 0) {				/* error */
43338764Smsmith	    sprintf(command_errbuf, "error reading '%s': %s", name, strerror(errno));
43444210Sdcs	    free(name);
43557269Smsmith	    close(fd);
436262345Sian	    return(NULL);
43738764Smsmith	}
43838764Smsmith	laddr += got;
43938764Smsmith    }
440269614Smarcel
441269614Smarcel    printf("size=%#jx\n", (uintmax_t)(laddr - loadaddr));
442269614Smarcel
44338764Smsmith    /* Looks OK so far; create & populate control structure */
44459854Sbp    fp = file_alloc();
445240342Savg    fp->f_name = strdup(name);
44659854Sbp    fp->f_type = strdup(type);
44759854Sbp    fp->f_args = NULL;
44859854Sbp    fp->f_metadata = NULL;
44959854Sbp    fp->f_loader = -1;
45059854Sbp    fp->f_addr = loadaddr;
45159854Sbp    fp->f_size = laddr - loadaddr;
45238764Smsmith
45338764Smsmith    /* recognise space consumption */
45438764Smsmith    loadaddr = laddr;
45538764Smsmith
45659854Sbp    /* Add to the list of loaded files */
457277215Sroyger    if (insert != 0)
458277215Sroyger    	file_insert_tail(fp);
45957269Smsmith    close(fd);
460262345Sian    return(fp);
46138764Smsmith}
46238764Smsmith
46338764Smsmith/*
46459854Sbp * Load the module (name), pass it (argc),(argv), add container file
46559854Sbp * to the list of loaded files.
46659854Sbp * If module is already loaded just assign new argc/argv.
46738764Smsmith */
46859854Sbpint
46983321Spetermod_load(char *modname, struct mod_depend *verinfo, int argc, char *argv[])
47038764Smsmith{
47159854Sbp    struct kernel_module	*mp;
47259854Sbp    int				err;
47359854Sbp    char			*filename;
47438764Smsmith
47583321Speter    if (file_havepath(modname)) {
47683321Speter	printf("Warning: mod_load() called instead of mod_loadkld() for module '%s'\n", modname);
47783321Speter	return (mod_loadkld(modname, argc, argv));
47883321Speter    }
47957468Sbp    /* see if module is already loaded */
48083321Speter    mp = file_findmodule(NULL, modname, verinfo);
48157468Sbp    if (mp) {
48259854Sbp#ifdef moduleargs
48359854Sbp	if (mp->m_args)
48459854Sbp	    free(mp->m_args);
48559854Sbp	mp->m_args = unargv(argc, argv);
48659854Sbp#endif
48759854Sbp	sprintf(command_errbuf, "warning: module '%s' already loaded", mp->m_name);
48859854Sbp	return (0);
48957468Sbp    }
49059854Sbp    /* locate file with the module on the search path */
49183321Speter    filename = mod_searchmodule(modname, verinfo);
49259854Sbp    if (filename == NULL) {
49359854Sbp	sprintf(command_errbuf, "can't find '%s'", modname);
49459854Sbp	return (ENOENT);
49559854Sbp    }
49683321Speter    err = mod_loadkld(filename, argc, argv);
49783321Speter    return (err);
49883321Speter}
49983321Speter
50083321Speter/*
50183321Speter * Load specified KLD. If path is omitted, then try to locate it via
50283321Speter * search path.
50383321Speter */
50483321Speterint
50583321Spetermod_loadkld(const char *kldname, int argc, char *argv[])
50683321Speter{
50783321Speter    struct preloaded_file	*fp, *last_file;
50883321Speter    int				err;
50983321Speter    char			*filename;
51083321Speter
51183321Speter    /*
51283321Speter     * Get fully qualified KLD name
51383321Speter     */
51483321Speter    filename = file_search(kldname, kld_ext_list);
51583321Speter    if (filename == NULL) {
51683321Speter	sprintf(command_errbuf, "can't find '%s'", kldname);
51783321Speter	return (ENOENT);
51883321Speter    }
51983321Speter    /*
52083321Speter     * Check if KLD already loaded
52183321Speter     */
52283321Speter    fp = file_findfile(filename, NULL);
52383321Speter    if (fp) {
52483321Speter	sprintf(command_errbuf, "warning: KLD '%s' already loaded", filename);
52583321Speter	free(filename);
52683321Speter	return (0);
52783321Speter    }
52859854Sbp    for (last_file = preloaded_files;
52959854Sbp	 last_file != NULL && last_file->f_next != NULL;
53059854Sbp	 last_file = last_file->f_next)
53159854Sbp	;
53257468Sbp
53359854Sbp    do {
53459854Sbp	err = file_load(filename, loadaddr, &fp);
53559854Sbp	if (err)
53638764Smsmith	    break;
53759854Sbp	fp->f_args = unargv(argc, argv);
53859854Sbp	loadaddr = fp->f_addr + fp->f_size;
53959854Sbp	file_insert_tail(fp);		/* Add to the list of loaded files */
54086469Siedowse	if (file_load_dependencies(fp) != 0) {
54159854Sbp	    err = ENOENT;
54259854Sbp	    last_file->f_next = NULL;
54359854Sbp	    loadaddr = last_file->f_addr + last_file->f_size;
54459854Sbp	    fp = NULL;
54559854Sbp	    break;
54659854Sbp	}
54759854Sbp    } while(0);
54838764Smsmith    if (err == EFTYPE)
54959854Sbp	sprintf(command_errbuf, "don't know how to load module '%s'", filename);
55059854Sbp    if (err && fp)
55159854Sbp	file_discard(fp);
55259854Sbp    free(filename);
55357468Sbp    return (err);
55438764Smsmith}
55538764Smsmith
55659854Sbp/*
55759854Sbp * Find a file matching (name) and (type).
55859854Sbp * NULL may be passed as a wildcard to either.
55959854Sbp */
56059854Sbpstruct preloaded_file *
561277215Sroygerfile_findfile(const char *name, const char *type)
56238764Smsmith{
56359854Sbp    struct preloaded_file *fp;
56438764Smsmith
56559854Sbp    for (fp = preloaded_files; fp != NULL; fp = fp->f_next) {
56659854Sbp	if (((name == NULL) || !strcmp(name, fp->f_name)) &&
56759854Sbp	    ((type == NULL) || !strcmp(type, fp->f_type)))
56859854Sbp	    break;
56959854Sbp    }
57059854Sbp    return (fp);
57138465Smsmith}
57238465Smsmith
57338764Smsmith/*
57459854Sbp * Find a module matching (name) inside of given file.
57559854Sbp * NULL may be passed as a wildcard.
57638764Smsmith */
57759854Sbpstruct kernel_module *
57883321Speterfile_findmodule(struct preloaded_file *fp, char *modname,
57983321Speter	struct mod_depend *verinfo)
58038465Smsmith{
58183321Speter    struct kernel_module *mp, *best;
58283321Speter    int bestver, mver;
58359854Sbp
58459854Sbp    if (fp == NULL) {
58559854Sbp	for (fp = preloaded_files; fp; fp = fp->f_next) {
58683321Speter	    mp = file_findmodule(fp, modname, verinfo);
58783321Speter    	    if (mp)
58883321Speter		return (mp);
58959854Sbp	}
59059854Sbp	return (NULL);
59138465Smsmith    }
59283321Speter    best = NULL;
59383321Speter    bestver = 0;
59459854Sbp    for (mp = fp->f_modules; mp; mp = mp->m_next) {
59583321Speter        if (strcmp(modname, mp->m_name) == 0) {
59683321Speter	    if (verinfo == NULL)
59783321Speter		return (mp);
59883321Speter	    mver = mp->m_version;
59983321Speter	    if (mver == verinfo->md_ver_preferred)
60083321Speter		return (mp);
60183321Speter	    if (mver >= verinfo->md_ver_minimum &&
60283321Speter		mver <= verinfo->md_ver_maximum &&
60383321Speter		mver > bestver) {
60483321Speter		best = mp;
60583321Speter		bestver = mver;
60683321Speter	    }
60783321Speter	}
60859854Sbp    }
60983321Speter    return (best);
61038465Smsmith}
61138764Smsmith/*
61238764Smsmith * Make a copy of (size) bytes of data from (p), and associate them as
61338764Smsmith * metadata of (type) to the module (mp).
61438764Smsmith */
61538712Smsmithvoid
61659854Sbpfile_addmetadata(struct preloaded_file *fp, int type, size_t size, void *p)
61738712Smsmith{
61859854Sbp    struct file_metadata	*md;
61938712Smsmith
62064187Sjhb    md = malloc(sizeof(struct file_metadata) - sizeof(md->md_data) + size);
62138712Smsmith    md->md_size = size;
62238712Smsmith    md->md_type = type;
62338712Smsmith    bcopy(p, md->md_data, size);
62459854Sbp    md->md_next = fp->f_metadata;
62559854Sbp    fp->f_metadata = md;
62638712Smsmith}
62738712Smsmith
62838764Smsmith/*
62959854Sbp * Find a metadata object of (type) associated with the file (fp)
63038764Smsmith */
63159854Sbpstruct file_metadata *
63259854Sbpfile_findmetadata(struct preloaded_file *fp, int type)
63338712Smsmith{
63459854Sbp    struct file_metadata *md;
63538712Smsmith
63659854Sbp    for (md = fp->f_metadata; md != NULL; md = md->md_next)
63738712Smsmith	if (md->md_type == type)
63838712Smsmith	    break;
63938712Smsmith    return(md);
64038712Smsmith}
64138764Smsmith
64259854Sbpstruct file_metadata *
64359854Sbpmetadata_next(struct file_metadata *md, int type)
64457468Sbp{
64557468Sbp    if (md == NULL)
64657468Sbp	return (NULL);
64757468Sbp    while((md = md->md_next) != NULL)
64857468Sbp	if (md->md_type == type)
64957468Sbp	    break;
65057468Sbp    return (md);
65157468Sbp}
65257468Sbp
65383321Speterstatic char *emptyextlist[] = { "", NULL };
65483321Speter
65538764Smsmith/*
65683321Speter * Check if the given file is in place and return full path to it.
65783321Speter */
65883321Speterstatic char *
65983321Speterfile_lookup(const char *path, const char *name, int namelen, char **extlist)
66083321Speter{
66183321Speter    struct stat	st;
66283321Speter    char	*result, *cp, **cpp;
66383321Speter    int		pathlen, extlen, len;
66483321Speter
66583321Speter    pathlen = strlen(path);
66683321Speter    extlen = 0;
66783321Speter    if (extlist == NULL)
66883321Speter	extlist = emptyextlist;
66983321Speter    for (cpp = extlist; *cpp; cpp++) {
67083321Speter	len = strlen(*cpp);
67183321Speter	if (len > extlen)
67283321Speter	    extlen = len;
67383321Speter    }
67483321Speter    result = malloc(pathlen + namelen + extlen + 2);
67583321Speter    if (result == NULL)
67683321Speter	return (NULL);
67783321Speter    bcopy(path, result, pathlen);
67883321Speter    if (pathlen > 0 && result[pathlen - 1] != '/')
67983321Speter	result[pathlen++] = '/';
68083321Speter    cp = result + pathlen;
68183321Speter    bcopy(name, cp, namelen);
68283321Speter    cp += namelen;
68383321Speter    for (cpp = extlist; *cpp; cpp++) {
68483321Speter	strcpy(cp, *cpp);
68583321Speter	if (stat(result, &st) == 0 && S_ISREG(st.st_mode))
68683321Speter	    return result;
68783321Speter    }
68883321Speter    free(result);
68983321Speter    return NULL;
69083321Speter}
69183321Speter
69283321Speter/*
69383321Speter * Check if file name have any qualifiers
69483321Speter */
69583321Speterstatic int
69683321Speterfile_havepath(const char *name)
69783321Speter{
69883321Speter    const char		*cp;
69983321Speter
70083321Speter    archsw.arch_getdev(NULL, name, &cp);
70183321Speter    return (cp != name || strchr(name, '/') != NULL);
70283321Speter}
70383321Speter
70483321Speter/*
70539178Smsmith * Attempt to find the file (name) on the module searchpath.
70638764Smsmith * If (name) is qualified in any way, we simply check it and
70738764Smsmith * return it or NULL.  If it is not qualified, then we attempt
70838764Smsmith * to construct a path using entries in the environment variable
70938764Smsmith * module_path.
71038764Smsmith *
71138764Smsmith * The path we return a pointer to need never be freed, as we manage
71238764Smsmith * it internally.
71338764Smsmith */
71438764Smsmithstatic char *
71583321Speterfile_search(const char *name, char **extlist)
71638764Smsmith{
71783321Speter    struct moduledir	*mdp;
71883321Speter    struct stat		sb;
71944570Sdcs    char		*result;
72083321Speter    int			namelen;
72138764Smsmith
72238764Smsmith    /* Don't look for nothing */
72344210Sdcs    if (name == NULL)
72483321Speter	return(NULL);
72538764Smsmith
72644210Sdcs    if (*name == 0)
72744210Sdcs	return(strdup(name));
72844210Sdcs
72983321Speter    if (file_havepath(name)) {
73038764Smsmith	/* Qualified, so just see if it exists */
73138764Smsmith	if (stat(name, &sb) == 0)
73244210Sdcs	    return(strdup(name));
73338764Smsmith	return(NULL);
73438764Smsmith    }
73583321Speter    moduledir_rebuild();
73644570Sdcs    result = NULL;
73783321Speter    namelen = strlen(name);
73883321Speter    STAILQ_FOREACH(mdp, &moduledir_list, d_link) {
73983321Speter	result = file_lookup(mdp->d_path, name, namelen, extlist);
74083321Speter	if (result)
74138764Smsmith	    break;
74238764Smsmith    }
74338764Smsmith    return(result);
74438764Smsmith}
74538764Smsmith
74683321Speter#define	INT_ALIGN(base, ptr)	ptr = \
74783321Speter	(base) + (((ptr) - (base) + sizeof(int) - 1) & ~(sizeof(int) - 1))
74883321Speter
74983321Speterstatic char *
75083321Spetermod_search_hints(struct moduledir *mdp, const char *modname,
75183321Speter	struct mod_depend *verinfo)
75283321Speter{
75383321Speter    u_char	*cp, *recptr, *bufend, *best;
75483321Speter    char	*result;
75583321Speter    int		*intp, bestver, blen, clen, found, ival, modnamelen, reclen;
75683321Speter
75783321Speter    moduledir_readhints(mdp);
75883321Speter    modnamelen = strlen(modname);
75983321Speter    found = 0;
76083321Speter    result = NULL;
76183321Speter    bestver = 0;
76283321Speter    if (mdp->d_hints == NULL)
76383321Speter	goto bad;
76483321Speter    recptr = mdp->d_hints;
76583321Speter    bufend = recptr + mdp->d_hintsz;
76683321Speter    clen = blen = 0;
76783321Speter    best = cp = NULL;
76883321Speter    while (recptr < bufend && !found) {
76983321Speter	intp = (int*)recptr;
77083321Speter	reclen = *intp++;
77183321Speter	ival = *intp++;
772298230Sallanjude	cp = (u_char*)intp;
77383321Speter	switch (ival) {
77483321Speter	case MDT_VERSION:
77583321Speter	    clen = *cp++;
77683321Speter	    if (clen != modnamelen || bcmp(cp, modname, clen) != 0)
77783321Speter		break;
77883321Speter	    cp += clen;
77983321Speter	    INT_ALIGN(mdp->d_hints, cp);
78083321Speter	    ival = *(int*)cp;
78183321Speter	    cp += sizeof(int);
78283321Speter	    clen = *cp++;
78383321Speter	    if (verinfo == NULL || ival == verinfo->md_ver_preferred) {
78483321Speter		found = 1;
78583321Speter		break;
78683321Speter	    }
78783321Speter	    if (ival >= verinfo->md_ver_minimum &&
78883321Speter		ival <= verinfo->md_ver_maximum &&
78983321Speter		ival > bestver) {
79083321Speter		bestver = ival;
79183321Speter		best = cp;
79283321Speter		blen = clen;
79383321Speter	    }
79483321Speter	    break;
79583321Speter	default:
79683321Speter	    break;
79783321Speter	}
79883321Speter	recptr += reclen + sizeof(int);
79983321Speter    }
80083321Speter    /*
80183321Speter     * Finally check if KLD is in the place
80283321Speter     */
80383321Speter    if (found)
804298230Sallanjude	result = file_lookup(mdp->d_path, (const char *)cp, clen, NULL);
80583321Speter    else if (best)
806298230Sallanjude	result = file_lookup(mdp->d_path, (const char *)best, blen, NULL);
80783321Speterbad:
80883321Speter    /*
80983321Speter     * If nothing found or hints is absent - fallback to the old way
81083321Speter     * by using "kldname[.ko]" as module name.
81183321Speter     */
81283321Speter    if (!found && !bestver && result == NULL)
81383321Speter	result = file_lookup(mdp->d_path, modname, modnamelen, kld_ext_list);
81483321Speter    return result;
81583321Speter}
81683321Speter
81738764Smsmith/*
81839178Smsmith * Attempt to locate the file containing the module (name)
81939178Smsmith */
82039178Smsmithstatic char *
82183321Spetermod_searchmodule(char *name, struct mod_depend *verinfo)
82239178Smsmith{
82383321Speter    struct	moduledir *mdp;
82483321Speter    char	*result;
82539178Smsmith
82683321Speter    moduledir_rebuild();
82783321Speter    /*
82883321Speter     * Now we ready to lookup module in the given directories
82983321Speter     */
83083321Speter    result = NULL;
83183321Speter    STAILQ_FOREACH(mdp, &moduledir_list, d_link) {
83283321Speter	result = mod_search_hints(mdp, name, verinfo);
83383321Speter	if (result)
83483321Speter	    break;
83583321Speter    }
83683321Speter
83739178Smsmith    return(result);
83839178Smsmith}
83939178Smsmith
84059854Sbpint
84183321Speterfile_addmodule(struct preloaded_file *fp, char *modname, int version,
84259854Sbp	struct kernel_module **newmp)
84359854Sbp{
84459854Sbp    struct kernel_module *mp;
84583321Speter    struct mod_depend mdepend;
84639178Smsmith
84783321Speter    bzero(&mdepend, sizeof(mdepend));
84883321Speter    mdepend.md_ver_preferred = version;
84983321Speter    mp = file_findmodule(fp, modname, &mdepend);
85059854Sbp    if (mp)
85159854Sbp	return (EEXIST);
85259854Sbp    mp = malloc(sizeof(struct kernel_module));
85359854Sbp    if (mp == NULL)
85459854Sbp	return (ENOMEM);
85559854Sbp    bzero(mp, sizeof(struct kernel_module));
85659854Sbp    mp->m_name = strdup(modname);
85783321Speter    mp->m_version = version;
85859854Sbp    mp->m_fp = fp;
85959854Sbp    mp->m_next = fp->f_modules;
86059854Sbp    fp->f_modules = mp;
86159854Sbp    if (newmp)
86259854Sbp	*newmp = mp;
86359854Sbp    return (0);
86459854Sbp}
86559854Sbp
86639178Smsmith/*
86759854Sbp * Throw a file away
86838764Smsmith */
86938764Smsmithvoid
87059854Sbpfile_discard(struct preloaded_file *fp)
87138764Smsmith{
87259854Sbp    struct file_metadata	*md, *md1;
87359854Sbp    struct kernel_module	*mp, *mp1;
87459854Sbp    if (fp == NULL)
87559854Sbp	return;
87659854Sbp    md = fp->f_metadata;
87759854Sbp    while (md) {
87859854Sbp	md1 = md;
87959854Sbp	md = md->md_next;
88059854Sbp	free(md1);
88159854Sbp    }
88259854Sbp    mp = fp->f_modules;
88359854Sbp    while (mp) {
88459854Sbp	if (mp->m_name)
88539178Smsmith	    free(mp->m_name);
88659854Sbp	mp1 = mp;
88759854Sbp	mp = mp->m_next;
88859854Sbp	free(mp1);
88959854Sbp    }
89059854Sbp    if (fp->f_name != NULL)
89159854Sbp	free(fp->f_name);
89259854Sbp    if (fp->f_type != NULL)
89359854Sbp        free(fp->f_type);
89459854Sbp    if (fp->f_args != NULL)
89559854Sbp        free(fp->f_args);
89659854Sbp    free(fp);
89738764Smsmith}
89838764Smsmith
89938764Smsmith/*
90059854Sbp * Allocate a new file; must be used instead of malloc()
90139178Smsmith * to ensure safe initialisation.
90239178Smsmith */
90359854Sbpstruct preloaded_file *
90459854Sbpfile_alloc(void)
90539178Smsmith{
90659854Sbp    struct preloaded_file	*fp;
90739178Smsmith
90859854Sbp    if ((fp = malloc(sizeof(struct preloaded_file))) != NULL) {
90959854Sbp	bzero(fp, sizeof(struct preloaded_file));
91039178Smsmith    }
91159854Sbp    return (fp);
91239178Smsmith}
91339178Smsmith
91439178Smsmith/*
91538764Smsmith * Add a module to the chain
91638764Smsmith */
91738764Smsmithstatic void
91859854Sbpfile_insert_tail(struct preloaded_file *fp)
91938764Smsmith{
92059854Sbp    struct preloaded_file	*cm;
92138764Smsmith
92259854Sbp    /* Append to list of loaded file */
92359854Sbp    fp->f_next = NULL;
92459854Sbp    if (preloaded_files == NULL) {
92559854Sbp	preloaded_files = fp;
92638764Smsmith    } else {
92759854Sbp	for (cm = preloaded_files; cm->f_next != NULL; cm = cm->f_next)
92838764Smsmith	    ;
92959854Sbp	cm->f_next = fp;
93038764Smsmith    }
93138764Smsmith}
93259854Sbp
93383321Speterstatic char *
93483321Spetermoduledir_fullpath(struct moduledir *mdp, const char *fname)
93583321Speter{
93683321Speter    char *cp;
93783321Speter
93883321Speter    cp = malloc(strlen(mdp->d_path) + strlen(fname) + 2);
93983321Speter    if (cp == NULL)
94083321Speter	return NULL;
94183321Speter    strcpy(cp, mdp->d_path);
94283321Speter    strcat(cp, "/");
94383321Speter    strcat(cp, fname);
94483321Speter    return (cp);
94583321Speter}
94683321Speter
94783321Speter/*
94883321Speter * Read linker.hints file into memory performing some sanity checks.
94983321Speter */
95083321Speterstatic void
95183321Spetermoduledir_readhints(struct moduledir *mdp)
95283321Speter{
95383321Speter    struct stat	st;
95483321Speter    char	*path;
95583321Speter    int		fd, size, version;
95683321Speter
95783321Speter    if (mdp->d_hints != NULL || (mdp->d_flags & MDIR_NOHINTS))
95883321Speter	return;
95983321Speter    path = moduledir_fullpath(mdp, "linker.hints");
960154257Smarius    if (stat(path, &st) != 0 ||
961154257Smarius	st.st_size < (ssize_t)(sizeof(version) + sizeof(int)) ||
962275261Simp	st.st_size > LINKER_HINTS_MAX || (fd = open(path, O_RDONLY)) < 0) {
96383321Speter	free(path);
96483321Speter	mdp->d_flags |= MDIR_NOHINTS;
96583321Speter	return;
96683321Speter    }
96783321Speter    free(path);
96883321Speter    size = read(fd, &version, sizeof(version));
96983321Speter    if (size != sizeof(version) || version != LINKER_HINTS_VERSION)
97083321Speter	goto bad;
97183321Speter    size = st.st_size - size;
97283321Speter    mdp->d_hints = malloc(size);
97383321Speter    if (mdp->d_hints == NULL)
97483321Speter	goto bad;
97583321Speter    if (read(fd, mdp->d_hints, size) != size)
97683321Speter	goto bad;
97783321Speter    mdp->d_hintsz = size;
97883321Speter    close(fd);
97983321Speter    return;
98083321Speterbad:
98183321Speter    close(fd);
98283321Speter    if (mdp->d_hints) {
98383321Speter	free(mdp->d_hints);
98483321Speter	mdp->d_hints = NULL;
98583321Speter    }
98683321Speter    mdp->d_flags |= MDIR_NOHINTS;
98783321Speter    return;
98883321Speter}
98983321Speter
99083321Speter/*
99183321Speter * Extract directories from the ';' separated list, remove duplicates.
99283321Speter */
99383321Speterstatic void
99483321Spetermoduledir_rebuild(void)
99583321Speter{
99683321Speter    struct	moduledir *mdp, *mtmp;
99783321Speter    const char	*path, *cp, *ep;
998293724Ssmh    size_t	cplen;
99983321Speter
100083321Speter    path = getenv("module_path");
100183321Speter    if (path == NULL)
100283321Speter	path = default_searchpath;
100383321Speter    /*
100483321Speter     * Rebuild list of module directories if it changed
100583321Speter     */
100683321Speter    STAILQ_FOREACH(mdp, &moduledir_list, d_link)
100783321Speter	mdp->d_flags |= MDIR_REMOVED;
100883321Speter
100983321Speter    for (ep = path; *ep != 0;  ep++) {
101083321Speter	cp = ep;
101183321Speter	for (; *ep != 0 && *ep != ';'; ep++)
101283321Speter	    ;
101383321Speter	/*
101483321Speter	 * Ignore trailing slashes
101583321Speter	 */
101683321Speter	for (cplen = ep - cp; cplen > 1 && cp[cplen - 1] == '/'; cplen--)
101783321Speter	    ;
101883321Speter	STAILQ_FOREACH(mdp, &moduledir_list, d_link) {
101983321Speter	    if (strlen(mdp->d_path) != cplen ||	bcmp(cp, mdp->d_path, cplen) != 0)
102083321Speter		continue;
102183321Speter	    mdp->d_flags &= ~MDIR_REMOVED;
102283321Speter	    break;
102383321Speter	}
102483321Speter	if (mdp == NULL) {
102583321Speter	    mdp = malloc(sizeof(*mdp) + cplen + 1);
102683321Speter	    if (mdp == NULL)
102783321Speter		return;
102883321Speter	    mdp->d_path = (char*)(mdp + 1);
102983321Speter	    bcopy(cp, mdp->d_path, cplen);
103083321Speter	    mdp->d_path[cplen] = 0;
103183321Speter	    mdp->d_hints = NULL;
103283321Speter	    mdp->d_flags = 0;
103383321Speter	    STAILQ_INSERT_TAIL(&moduledir_list, mdp, d_link);
103483321Speter	}
103594419Speter	if (*ep == 0)
103694419Speter	    break;
103783321Speter    }
103883321Speter    /*
103983321Speter     * Delete unused directories if any
104083321Speter     */
104183321Speter    mdp = STAILQ_FIRST(&moduledir_list);
104283321Speter    while (mdp) {
104383321Speter	if ((mdp->d_flags & MDIR_REMOVED) == 0) {
104483321Speter	    mdp = STAILQ_NEXT(mdp, d_link);
104583321Speter	} else {
104683321Speter	    if (mdp->d_hints)
104783321Speter		free(mdp->d_hints);
104883321Speter	    mtmp = mdp;
104983321Speter	    mdp = STAILQ_NEXT(mdp, d_link);
105083321Speter	    STAILQ_REMOVE(&moduledir_list, mtmp, moduledir, d_link);
105183321Speter	    free(mtmp);
105283321Speter	}
105383321Speter    }
105483321Speter    return;
105583321Speter}
1056