module.c revision 119483
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 119483 2003-08-25 23:30:41Z obrien $");
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    "",
7883321Speter    NULL
7983321Speter};
8083321Speter
8183321Speter
8238764Smsmith/*
8338764Smsmith * load an object, either a disk file or code module.
8438764Smsmith *
8538764Smsmith * To load a file, the syntax is:
8638764Smsmith *
8738764Smsmith * load -t <type> <path>
8838764Smsmith *
8938764Smsmith * code modules are loaded as:
9038764Smsmith *
9138764Smsmith * load <path> <options>
9238764Smsmith */
9338764Smsmith
9438465SmsmithCOMMAND_SET(load, "load", "load a kernel or module", command_load);
9538465Smsmith
9638465Smsmithstatic int
9738465Smsmithcommand_load(int argc, char *argv[])
9838465Smsmith{
9938764Smsmith    char	*typestr;
10083321Speter    int		dofile, dokld, ch, error;
10138764Smsmith
10283321Speter    dokld = dofile = 0;
10338764Smsmith    optind = 1;
10442512Smsmith    optreset = 1;
10538764Smsmith    typestr = NULL;
10653993Sdcs    if (argc == 1) {
10753993Sdcs	command_errmsg = "no filename specified";
10853993Sdcs	return(CMD_ERROR);
10953993Sdcs    }
11083321Speter    while ((ch = getopt(argc, argv, "kt:")) != -1) {
11138764Smsmith	switch(ch) {
11283321Speter	case 'k':
11383321Speter	    dokld = 1;
11483321Speter	    break;
11538764Smsmith	case 't':
11638764Smsmith	    typestr = optarg;
11738764Smsmith	    dofile = 1;
11838764Smsmith	    break;
11938764Smsmith	case '?':
12038764Smsmith	default:
12138764Smsmith	    /* getopt has already reported an error */
12238764Smsmith	    return(CMD_OK);
12338764Smsmith	}
12438764Smsmith    }
12538764Smsmith    argv += (optind - 1);
12638764Smsmith    argc -= (optind - 1);
12738764Smsmith
12838764Smsmith    /*
12938764Smsmith     * Request to load a raw file?
13038764Smsmith     */
13138764Smsmith    if (dofile) {
13277971Sjesper	if ((argc != 2) || (typestr == NULL) || (*typestr == 0)) {
13338764Smsmith	    command_errmsg = "invalid load type";
13438764Smsmith	    return(CMD_ERROR);
13538764Smsmith	}
13659854Sbp	return(file_loadraw(typestr, argv[1]));
13738764Smsmith    }
13838764Smsmith    /*
13983321Speter     * Do we have explicit KLD load ?
14083321Speter     */
14183321Speter    if (dokld || file_havepath(argv[1])) {
14283321Speter	error = mod_loadkld(argv[1], argc - 2, argv + 2);
14383321Speter	if (error == EEXIST)
14483321Speter	    sprintf(command_errbuf, "warning: KLD '%s' already loaded", argv[1]);
14583321Speter	return (error == 0 ? CMD_OK : CMD_ERROR);
14683321Speter    }
14783321Speter    /*
14838764Smsmith     * Looks like a request for a module.
14938764Smsmith     */
15083321Speter    error = mod_load(argv[1], NULL, argc - 2, argv + 2);
15157468Sbp    if (error == EEXIST)
15257468Sbp	sprintf(command_errbuf, "warning: module '%s' already loaded", argv[1]);
15357468Sbp    return (error == 0 ? CMD_OK : CMD_ERROR);
15438465Smsmith}
15538465Smsmith
15638712SmsmithCOMMAND_SET(unload, "unload", "unload all modules", command_unload);
15738712Smsmith
15838712Smsmithstatic int
15938712Smsmithcommand_unload(int argc, char *argv[])
16038712Smsmith{
16159854Sbp    struct preloaded_file	*fp;
16238712Smsmith
16359854Sbp    while (preloaded_files != NULL) {
16459854Sbp	fp = preloaded_files;
16559854Sbp	preloaded_files = preloaded_files->f_next;
16659854Sbp	file_discard(fp);
16738712Smsmith    }
16838712Smsmith    loadaddr = 0;
16965613Sdcs    unsetenv("kernelname");
17038712Smsmith    return(CMD_OK);
17138712Smsmith}
17238712Smsmith
17338465SmsmithCOMMAND_SET(lsmod, "lsmod", "list loaded modules", command_lsmod);
17438465Smsmith
17538465Smsmithstatic int
17638465Smsmithcommand_lsmod(int argc, char *argv[])
17738465Smsmith{
17859854Sbp    struct preloaded_file	*fp;
17959854Sbp    struct kernel_module	*mp;
18059854Sbp    struct file_metadata	*md;
18138465Smsmith    char			lbuf[80];
18238764Smsmith    int				ch, verbose;
18338764Smsmith
18438764Smsmith    verbose = 0;
18538764Smsmith    optind = 1;
18642512Smsmith    optreset = 1;
18738764Smsmith    while ((ch = getopt(argc, argv, "v")) != -1) {
18838764Smsmith	switch(ch) {
18938764Smsmith	case 'v':
19038764Smsmith	    verbose = 1;
19138764Smsmith	    break;
19238764Smsmith	case '?':
19338764Smsmith	default:
19438764Smsmith	    /* getopt has already reported an error */
19538764Smsmith	    return(CMD_OK);
19638764Smsmith	}
19738764Smsmith    }
19838764Smsmith
19938465Smsmith    pager_open();
20059854Sbp    for (fp = preloaded_files; fp; fp = fp->f_next) {
20139673Sdfr	sprintf(lbuf, " %p: %s (%s, 0x%lx)\n",
20259854Sbp		(void *) fp->f_addr, fp->f_name, fp->f_type, (long) fp->f_size);
20338465Smsmith	pager_output(lbuf);
20459854Sbp	if (fp->f_args != NULL) {
20538465Smsmith	    pager_output("    args: ");
20659854Sbp	    pager_output(fp->f_args);
20738465Smsmith	    pager_output("\n");
20838465Smsmith	}
20959854Sbp	if (fp->f_modules) {
21059854Sbp	    pager_output("  modules: ");
21159854Sbp	    for (mp = fp->f_modules; mp; mp = mp->m_next) {
21283321Speter		sprintf(lbuf, "%s.%d ", mp->m_name, mp->m_version);
21359854Sbp		pager_output(lbuf);
21459854Sbp	    }
21559854Sbp	    pager_output("\n");
21659854Sbp	}
21759854Sbp	if (verbose) {
21838764Smsmith	    /* XXX could add some formatting smarts here to display some better */
21959854Sbp	    for (md = fp->f_metadata; md != NULL; md = md->md_next) {
22039673Sdfr		sprintf(lbuf, "      0x%04x, 0x%lx\n", md->md_type, (long) md->md_size);
22138764Smsmith		pager_output(lbuf);
22238764Smsmith	    }
22359854Sbp	}
22438465Smsmith    }
22538465Smsmith    pager_close();
22638465Smsmith    return(CMD_OK);
22738465Smsmith}
22838465Smsmith
22938764Smsmith/*
23059854Sbp * File level interface, functions file_*
23138764Smsmith */
23238465Smsmithint
23359854Sbpfile_load(char *filename, vm_offset_t dest, struct preloaded_file **result)
23438465Smsmith{
23559854Sbp    struct preloaded_file *fp;
23659854Sbp    int error;
23759854Sbp    int i;
23838764Smsmith
23959854Sbp    error = EFTYPE;
24059854Sbp    for (i = 0, fp = NULL; file_formats[i] && fp == NULL; i++) {
24159854Sbp	error = (file_formats[i]->l_load)(filename, loadaddr, &fp);
24259854Sbp	if (error == 0) {
24359854Sbp	    fp->f_loader = i;		/* remember the loader */
24459854Sbp	    *result = fp;
24559854Sbp	    break;
24659854Sbp	}
24759854Sbp	if (error == EFTYPE)
24859854Sbp	    continue;		/* Unknown to this handler? */
24959854Sbp	if (error) {
25059854Sbp	    sprintf(command_errbuf, "can't load file '%s': %s",
25159854Sbp		filename, strerror(error));
25259854Sbp	    break;
25359854Sbp	}
25459854Sbp    }
25559854Sbp    return (error);
25659854Sbp}
25738465Smsmith
25859854Sbpstatic int
25986469Siedowsefile_load_dependencies(struct preloaded_file *base_file) {
26059854Sbp    struct file_metadata *md;
26159854Sbp    struct preloaded_file *fp;
26283321Speter    struct mod_depend *verinfo;
26383321Speter    struct kernel_module *mp;
26459854Sbp    char *dmodname;
26559854Sbp    int error;
26659854Sbp
26759854Sbp    md = file_findmetadata(base_file, MODINFOMD_DEPLIST);
26859854Sbp    if (md == NULL)
26959854Sbp	return (0);
27059854Sbp    error = 0;
27159854Sbp    do {
27283321Speter	verinfo = (struct mod_depend*)md->md_data;
27383321Speter	dmodname = (char *)(verinfo + 1);
27483321Speter	if (file_findmodule(NULL, dmodname, verinfo) == NULL) {
27559854Sbp	    printf("loading required module '%s'\n", dmodname);
27683321Speter	    error = mod_load(dmodname, verinfo, 0, NULL);
27759854Sbp	    if (error)
27859854Sbp		break;
27983321Speter	    /*
28083321Speter	     * If module loaded via kld name which isn't listed
28183321Speter	     * in the linker.hints file, we should check if it have
28283321Speter	     * required version.
28383321Speter	     */
28483321Speter	    mp = file_findmodule(NULL, dmodname, verinfo);
28583321Speter	    if (mp == NULL) {
28683321Speter		sprintf(command_errbuf, "module '%s' exists but with wrong version",
28783321Speter		    dmodname);
28883321Speter		error = ENOENT;
28983321Speter		break;
29083321Speter	    }
29159854Sbp	}
29259854Sbp	md = metadata_next(md, MODINFOMD_DEPLIST);
29359854Sbp    } while (md);
29457468Sbp    if (!error)
29557468Sbp	return (0);
29657468Sbp    /* Load failed; discard everything */
29759854Sbp    while (base_file != NULL) {
29859854Sbp        fp = base_file;
29959854Sbp        base_file = base_file->f_next;
30059854Sbp        file_discard(fp);
30138764Smsmith    }
30257468Sbp    return (error);
30338764Smsmith}
30438764Smsmith/*
30538764Smsmith * We've been asked to load (name) as (type), so just suck it in,
30638764Smsmith * no arguments or anything.
30738764Smsmith */
30838764Smsmithint
30959854Sbpfile_loadraw(char *type, char *name)
31038764Smsmith{
31159854Sbp    struct preloaded_file	*fp;
31238764Smsmith    char			*cp;
31338764Smsmith    int				fd, got;
31438764Smsmith    vm_offset_t			laddr;
31538764Smsmith
31638764Smsmith    /* We can't load first */
31759854Sbp    if ((file_findfile(NULL, NULL)) == NULL) {
31838764Smsmith	command_errmsg = "can't load file before kernel";
31938764Smsmith	return(CMD_ERROR);
32038764Smsmith    }
32138764Smsmith
32239178Smsmith    /* locate the file on the load path */
32383321Speter    cp = file_search(name, NULL);
32439178Smsmith    if (cp == NULL) {
32539178Smsmith	sprintf(command_errbuf, "can't find '%s'", name);
32639178Smsmith	return(CMD_ERROR);
32739178Smsmith    }
32839178Smsmith    name = cp;
32938764Smsmith
33038764Smsmith    if ((fd = open(name, O_RDONLY)) < 0) {
33138764Smsmith	sprintf(command_errbuf, "can't open '%s': %s", name, strerror(errno));
33244570Sdcs	free(name);
33338764Smsmith	return(CMD_ERROR);
33438764Smsmith    }
33538764Smsmith
33638764Smsmith    laddr = loadaddr;
33738764Smsmith    for (;;) {
33838764Smsmith	/* read in 4k chunks; size is not really important */
33938764Smsmith	got = archsw.arch_readin(fd, laddr, 4096);
34038764Smsmith	if (got == 0)				/* end of file */
34138764Smsmith	    break;
34238764Smsmith	if (got < 0) {				/* error */
34338764Smsmith	    sprintf(command_errbuf, "error reading '%s': %s", name, strerror(errno));
34444210Sdcs	    free(name);
34557269Smsmith	    close(fd);
34638764Smsmith	    return(CMD_ERROR);
34738764Smsmith	}
34838764Smsmith	laddr += got;
34938764Smsmith    }
35038764Smsmith
35138764Smsmith    /* Looks OK so far; create & populate control structure */
35259854Sbp    fp = file_alloc();
35359854Sbp    fp->f_name = name;
35459854Sbp    fp->f_type = strdup(type);
35559854Sbp    fp->f_args = NULL;
35659854Sbp    fp->f_metadata = NULL;
35759854Sbp    fp->f_loader = -1;
35859854Sbp    fp->f_addr = loadaddr;
35959854Sbp    fp->f_size = laddr - loadaddr;
36038764Smsmith
36138764Smsmith    /* recognise space consumption */
36238764Smsmith    loadaddr = laddr;
36338764Smsmith
36459854Sbp    /* Add to the list of loaded files */
36559854Sbp    file_insert_tail(fp);
36657269Smsmith    close(fd);
36738764Smsmith    return(CMD_OK);
36838764Smsmith}
36938764Smsmith
37038764Smsmith/*
37159854Sbp * Load the module (name), pass it (argc),(argv), add container file
37259854Sbp * to the list of loaded files.
37359854Sbp * If module is already loaded just assign new argc/argv.
37438764Smsmith */
37559854Sbpint
37683321Spetermod_load(char *modname, struct mod_depend *verinfo, int argc, char *argv[])
37738764Smsmith{
37859854Sbp    struct kernel_module	*mp;
37959854Sbp    int				err;
38059854Sbp    char			*filename;
38138764Smsmith
38283321Speter    if (file_havepath(modname)) {
38383321Speter	printf("Warning: mod_load() called instead of mod_loadkld() for module '%s'\n", modname);
38483321Speter	return (mod_loadkld(modname, argc, argv));
38583321Speter    }
38657468Sbp    /* see if module is already loaded */
38783321Speter    mp = file_findmodule(NULL, modname, verinfo);
38857468Sbp    if (mp) {
38959854Sbp#ifdef moduleargs
39059854Sbp	if (mp->m_args)
39159854Sbp	    free(mp->m_args);
39259854Sbp	mp->m_args = unargv(argc, argv);
39359854Sbp#endif
39459854Sbp	sprintf(command_errbuf, "warning: module '%s' already loaded", mp->m_name);
39559854Sbp	return (0);
39657468Sbp    }
39759854Sbp    /* locate file with the module on the search path */
39883321Speter    filename = mod_searchmodule(modname, verinfo);
39959854Sbp    if (filename == NULL) {
40059854Sbp	sprintf(command_errbuf, "can't find '%s'", modname);
40159854Sbp	return (ENOENT);
40259854Sbp    }
40383321Speter    err = mod_loadkld(filename, argc, argv);
40483321Speter    return (err);
40583321Speter}
40683321Speter
40783321Speter/*
40883321Speter * Load specified KLD. If path is omitted, then try to locate it via
40983321Speter * search path.
41083321Speter */
41183321Speterint
41283321Spetermod_loadkld(const char *kldname, int argc, char *argv[])
41383321Speter{
41483321Speter    struct preloaded_file	*fp, *last_file;
41583321Speter    int				err;
41683321Speter    char			*filename;
41783321Speter
41883321Speter    /*
41983321Speter     * Get fully qualified KLD name
42083321Speter     */
42183321Speter    filename = file_search(kldname, kld_ext_list);
42283321Speter    if (filename == NULL) {
42383321Speter	sprintf(command_errbuf, "can't find '%s'", kldname);
42483321Speter	return (ENOENT);
42583321Speter    }
42683321Speter    /*
42783321Speter     * Check if KLD already loaded
42883321Speter     */
42983321Speter    fp = file_findfile(filename, NULL);
43083321Speter    if (fp) {
43183321Speter	sprintf(command_errbuf, "warning: KLD '%s' already loaded", filename);
43283321Speter	free(filename);
43383321Speter	return (0);
43483321Speter    }
43559854Sbp    for (last_file = preloaded_files;
43659854Sbp	 last_file != NULL && last_file->f_next != NULL;
43759854Sbp	 last_file = last_file->f_next)
43859854Sbp	;
43957468Sbp
44059854Sbp    do {
44159854Sbp	err = file_load(filename, loadaddr, &fp);
44259854Sbp	if (err)
44338764Smsmith	    break;
44459854Sbp	fp->f_args = unargv(argc, argv);
44559854Sbp	loadaddr = fp->f_addr + fp->f_size;
44659854Sbp	file_insert_tail(fp);		/* Add to the list of loaded files */
44786469Siedowse	if (file_load_dependencies(fp) != 0) {
44859854Sbp	    err = ENOENT;
44959854Sbp	    last_file->f_next = NULL;
45059854Sbp	    loadaddr = last_file->f_addr + last_file->f_size;
45159854Sbp	    fp = NULL;
45259854Sbp	    break;
45359854Sbp	}
45459854Sbp    } while(0);
45538764Smsmith    if (err == EFTYPE)
45659854Sbp	sprintf(command_errbuf, "don't know how to load module '%s'", filename);
45759854Sbp    if (err && fp)
45859854Sbp	file_discard(fp);
45959854Sbp    free(filename);
46057468Sbp    return (err);
46138764Smsmith}
46238764Smsmith
46359854Sbp/*
46459854Sbp * Find a file matching (name) and (type).
46559854Sbp * NULL may be passed as a wildcard to either.
46659854Sbp */
46759854Sbpstruct preloaded_file *
46859854Sbpfile_findfile(char *name, char *type)
46938764Smsmith{
47059854Sbp    struct preloaded_file *fp;
47138764Smsmith
47259854Sbp    for (fp = preloaded_files; fp != NULL; fp = fp->f_next) {
47359854Sbp	if (((name == NULL) || !strcmp(name, fp->f_name)) &&
47459854Sbp	    ((type == NULL) || !strcmp(type, fp->f_type)))
47559854Sbp	    break;
47659854Sbp    }
47759854Sbp    return (fp);
47838465Smsmith}
47938465Smsmith
48038764Smsmith/*
48159854Sbp * Find a module matching (name) inside of given file.
48259854Sbp * NULL may be passed as a wildcard.
48338764Smsmith */
48459854Sbpstruct kernel_module *
48583321Speterfile_findmodule(struct preloaded_file *fp, char *modname,
48683321Speter	struct mod_depend *verinfo)
48738465Smsmith{
48883321Speter    struct kernel_module *mp, *best;
48983321Speter    int bestver, mver;
49059854Sbp
49159854Sbp    if (fp == NULL) {
49259854Sbp	for (fp = preloaded_files; fp; fp = fp->f_next) {
49383321Speter	    mp = file_findmodule(fp, modname, verinfo);
49483321Speter    	    if (mp)
49583321Speter		return (mp);
49659854Sbp	}
49759854Sbp	return (NULL);
49838465Smsmith    }
49983321Speter    best = NULL;
50083321Speter    bestver = 0;
50159854Sbp    for (mp = fp->f_modules; mp; mp = mp->m_next) {
50283321Speter        if (strcmp(modname, mp->m_name) == 0) {
50383321Speter	    if (verinfo == NULL)
50483321Speter		return (mp);
50583321Speter	    mver = mp->m_version;
50683321Speter	    if (mver == verinfo->md_ver_preferred)
50783321Speter		return (mp);
50883321Speter	    if (mver >= verinfo->md_ver_minimum &&
50983321Speter		mver <= verinfo->md_ver_maximum &&
51083321Speter		mver > bestver) {
51183321Speter		best = mp;
51283321Speter		bestver = mver;
51383321Speter	    }
51483321Speter	}
51559854Sbp    }
51683321Speter    return (best);
51738465Smsmith}
51838764Smsmith/*
51938764Smsmith * Make a copy of (size) bytes of data from (p), and associate them as
52038764Smsmith * metadata of (type) to the module (mp).
52138764Smsmith */
52238712Smsmithvoid
52359854Sbpfile_addmetadata(struct preloaded_file *fp, int type, size_t size, void *p)
52438712Smsmith{
52559854Sbp    struct file_metadata	*md;
52638712Smsmith
52764187Sjhb    md = malloc(sizeof(struct file_metadata) - sizeof(md->md_data) + size);
52838712Smsmith    md->md_size = size;
52938712Smsmith    md->md_type = type;
53038712Smsmith    bcopy(p, md->md_data, size);
53159854Sbp    md->md_next = fp->f_metadata;
53259854Sbp    fp->f_metadata = md;
53338712Smsmith}
53438712Smsmith
53538764Smsmith/*
53659854Sbp * Find a metadata object of (type) associated with the file (fp)
53738764Smsmith */
53859854Sbpstruct file_metadata *
53959854Sbpfile_findmetadata(struct preloaded_file *fp, int type)
54038712Smsmith{
54159854Sbp    struct file_metadata *md;
54238712Smsmith
54359854Sbp    for (md = fp->f_metadata; md != NULL; md = md->md_next)
54438712Smsmith	if (md->md_type == type)
54538712Smsmith	    break;
54638712Smsmith    return(md);
54738712Smsmith}
54838764Smsmith
54959854Sbpstruct file_metadata *
55059854Sbpmetadata_next(struct file_metadata *md, int type)
55157468Sbp{
55257468Sbp    if (md == NULL)
55357468Sbp	return (NULL);
55457468Sbp    while((md = md->md_next) != NULL)
55557468Sbp	if (md->md_type == type)
55657468Sbp	    break;
55757468Sbp    return (md);
55857468Sbp}
55957468Sbp
56083321Speterstatic char *emptyextlist[] = { "", NULL };
56183321Speter
56238764Smsmith/*
56383321Speter * Check if the given file is in place and return full path to it.
56483321Speter */
56583321Speterstatic char *
56683321Speterfile_lookup(const char *path, const char *name, int namelen, char **extlist)
56783321Speter{
56883321Speter    struct stat	st;
56983321Speter    char	*result, *cp, **cpp;
57083321Speter    int		pathlen, extlen, len;
57183321Speter
57283321Speter    pathlen = strlen(path);
57383321Speter    extlen = 0;
57483321Speter    if (extlist == NULL)
57583321Speter	extlist = emptyextlist;
57683321Speter    for (cpp = extlist; *cpp; cpp++) {
57783321Speter	len = strlen(*cpp);
57883321Speter	if (len > extlen)
57983321Speter	    extlen = len;
58083321Speter    }
58183321Speter    result = malloc(pathlen + namelen + extlen + 2);
58283321Speter    if (result == NULL)
58383321Speter	return (NULL);
58483321Speter    bcopy(path, result, pathlen);
58583321Speter    if (pathlen > 0 && result[pathlen - 1] != '/')
58683321Speter	result[pathlen++] = '/';
58783321Speter    cp = result + pathlen;
58883321Speter    bcopy(name, cp, namelen);
58983321Speter    cp += namelen;
59083321Speter    for (cpp = extlist; *cpp; cpp++) {
59183321Speter	strcpy(cp, *cpp);
59283321Speter	if (stat(result, &st) == 0 && S_ISREG(st.st_mode))
59383321Speter	    return result;
59483321Speter    }
59583321Speter    free(result);
59683321Speter    return NULL;
59783321Speter}
59883321Speter
59983321Speter/*
60083321Speter * Check if file name have any qualifiers
60183321Speter */
60283321Speterstatic int
60383321Speterfile_havepath(const char *name)
60483321Speter{
60583321Speter    const char		*cp;
60683321Speter
60783321Speter    archsw.arch_getdev(NULL, name, &cp);
60883321Speter    return (cp != name || strchr(name, '/') != NULL);
60983321Speter}
61083321Speter
61183321Speter/*
61239178Smsmith * Attempt to find the file (name) on the module searchpath.
61338764Smsmith * If (name) is qualified in any way, we simply check it and
61438764Smsmith * return it or NULL.  If it is not qualified, then we attempt
61538764Smsmith * to construct a path using entries in the environment variable
61638764Smsmith * module_path.
61738764Smsmith *
61838764Smsmith * The path we return a pointer to need never be freed, as we manage
61938764Smsmith * it internally.
62038764Smsmith */
62138764Smsmithstatic char *
62283321Speterfile_search(const char *name, char **extlist)
62338764Smsmith{
62483321Speter    struct moduledir	*mdp;
62583321Speter    struct stat		sb;
62644570Sdcs    char		*result;
62783321Speter    int			namelen;
62838764Smsmith
62938764Smsmith    /* Don't look for nothing */
63044210Sdcs    if (name == NULL)
63183321Speter	return(NULL);
63238764Smsmith
63344210Sdcs    if (*name == 0)
63444210Sdcs	return(strdup(name));
63544210Sdcs
63683321Speter    if (file_havepath(name)) {
63738764Smsmith	/* Qualified, so just see if it exists */
63838764Smsmith	if (stat(name, &sb) == 0)
63944210Sdcs	    return(strdup(name));
64038764Smsmith	return(NULL);
64138764Smsmith    }
64283321Speter    moduledir_rebuild();
64344570Sdcs    result = NULL;
64483321Speter    namelen = strlen(name);
64583321Speter    STAILQ_FOREACH(mdp, &moduledir_list, d_link) {
64683321Speter	result = file_lookup(mdp->d_path, name, namelen, extlist);
64783321Speter	if (result)
64838764Smsmith	    break;
64938764Smsmith    }
65038764Smsmith    return(result);
65138764Smsmith}
65238764Smsmith
65383321Speter#define	INT_ALIGN(base, ptr)	ptr = \
65483321Speter	(base) + (((ptr) - (base) + sizeof(int) - 1) & ~(sizeof(int) - 1))
65583321Speter
65683321Speterstatic char *
65783321Spetermod_search_hints(struct moduledir *mdp, const char *modname,
65883321Speter	struct mod_depend *verinfo)
65983321Speter{
66083321Speter    u_char	*cp, *recptr, *bufend, *best;
66183321Speter    char	*result;
66283321Speter    int		*intp, bestver, blen, clen, found, ival, modnamelen, reclen;
66383321Speter
66483321Speter    moduledir_readhints(mdp);
66583321Speter    modnamelen = strlen(modname);
66683321Speter    found = 0;
66783321Speter    result = NULL;
66883321Speter    bestver = 0;
66983321Speter    if (mdp->d_hints == NULL)
67083321Speter	goto bad;
67183321Speter    recptr = mdp->d_hints;
67283321Speter    bufend = recptr + mdp->d_hintsz;
67383321Speter    clen = blen = 0;
67483321Speter    best = cp = NULL;
67583321Speter    while (recptr < bufend && !found) {
67683321Speter	intp = (int*)recptr;
67783321Speter	reclen = *intp++;
67883321Speter	ival = *intp++;
67983321Speter	cp = (char*)intp;
68083321Speter	switch (ival) {
68183321Speter	case MDT_VERSION:
68283321Speter	    clen = *cp++;
68383321Speter	    if (clen != modnamelen || bcmp(cp, modname, clen) != 0)
68483321Speter		break;
68583321Speter	    cp += clen;
68683321Speter	    INT_ALIGN(mdp->d_hints, cp);
68783321Speter	    ival = *(int*)cp;
68883321Speter	    cp += sizeof(int);
68983321Speter	    clen = *cp++;
69083321Speter	    if (verinfo == NULL || ival == verinfo->md_ver_preferred) {
69183321Speter		found = 1;
69283321Speter		break;
69383321Speter	    }
69483321Speter	    if (ival >= verinfo->md_ver_minimum &&
69583321Speter		ival <= verinfo->md_ver_maximum &&
69683321Speter		ival > bestver) {
69783321Speter		bestver = ival;
69883321Speter		best = cp;
69983321Speter		blen = clen;
70083321Speter	    }
70183321Speter	    break;
70283321Speter	default:
70383321Speter	    break;
70483321Speter	}
70583321Speter	recptr += reclen + sizeof(int);
70683321Speter    }
70783321Speter    /*
70883321Speter     * Finally check if KLD is in the place
70983321Speter     */
71083321Speter    if (found)
71183321Speter	result = file_lookup(mdp->d_path, cp, clen, NULL);
71283321Speter    else if (best)
71383321Speter	result = file_lookup(mdp->d_path, best, blen, NULL);
71483321Speterbad:
71583321Speter    /*
71683321Speter     * If nothing found or hints is absent - fallback to the old way
71783321Speter     * by using "kldname[.ko]" as module name.
71883321Speter     */
71983321Speter    if (!found && !bestver && result == NULL)
72083321Speter	result = file_lookup(mdp->d_path, modname, modnamelen, kld_ext_list);
72183321Speter    return result;
72283321Speter}
72383321Speter
72438764Smsmith/*
72539178Smsmith * Attempt to locate the file containing the module (name)
72639178Smsmith */
72739178Smsmithstatic char *
72883321Spetermod_searchmodule(char *name, struct mod_depend *verinfo)
72939178Smsmith{
73083321Speter    struct	moduledir *mdp;
73183321Speter    char	*result;
73239178Smsmith
73383321Speter    moduledir_rebuild();
73483321Speter    /*
73583321Speter     * Now we ready to lookup module in the given directories
73683321Speter     */
73783321Speter    result = NULL;
73883321Speter    STAILQ_FOREACH(mdp, &moduledir_list, d_link) {
73983321Speter	result = mod_search_hints(mdp, name, verinfo);
74083321Speter	if (result)
74183321Speter	    break;
74283321Speter    }
74383321Speter
74439178Smsmith    return(result);
74539178Smsmith}
74639178Smsmith
74759854Sbpint
74883321Speterfile_addmodule(struct preloaded_file *fp, char *modname, int version,
74959854Sbp	struct kernel_module **newmp)
75059854Sbp{
75159854Sbp    struct kernel_module *mp;
75283321Speter    struct mod_depend mdepend;
75339178Smsmith
75483321Speter    bzero(&mdepend, sizeof(mdepend));
75583321Speter    mdepend.md_ver_preferred = version;
75683321Speter    mp = file_findmodule(fp, modname, &mdepend);
75759854Sbp    if (mp)
75859854Sbp	return (EEXIST);
75959854Sbp    mp = malloc(sizeof(struct kernel_module));
76059854Sbp    if (mp == NULL)
76159854Sbp	return (ENOMEM);
76259854Sbp    bzero(mp, sizeof(struct kernel_module));
76359854Sbp    mp->m_name = strdup(modname);
76483321Speter    mp->m_version = version;
76559854Sbp    mp->m_fp = fp;
76659854Sbp    mp->m_next = fp->f_modules;
76759854Sbp    fp->f_modules = mp;
76859854Sbp    if (newmp)
76959854Sbp	*newmp = mp;
77059854Sbp    return (0);
77159854Sbp}
77259854Sbp
77339178Smsmith/*
77459854Sbp * Throw a file away
77538764Smsmith */
77638764Smsmithvoid
77759854Sbpfile_discard(struct preloaded_file *fp)
77838764Smsmith{
77959854Sbp    struct file_metadata	*md, *md1;
78059854Sbp    struct kernel_module	*mp, *mp1;
78159854Sbp    if (fp == NULL)
78259854Sbp	return;
78359854Sbp    md = fp->f_metadata;
78459854Sbp    while (md) {
78559854Sbp	md1 = md;
78659854Sbp	md = md->md_next;
78759854Sbp	free(md1);
78859854Sbp    }
78959854Sbp    mp = fp->f_modules;
79059854Sbp    while (mp) {
79159854Sbp	if (mp->m_name)
79239178Smsmith	    free(mp->m_name);
79359854Sbp	mp1 = mp;
79459854Sbp	mp = mp->m_next;
79559854Sbp	free(mp1);
79659854Sbp    }
79759854Sbp    if (fp->f_name != NULL)
79859854Sbp	free(fp->f_name);
79959854Sbp    if (fp->f_type != NULL)
80059854Sbp        free(fp->f_type);
80159854Sbp    if (fp->f_args != NULL)
80259854Sbp        free(fp->f_args);
80359854Sbp    free(fp);
80438764Smsmith}
80538764Smsmith
80638764Smsmith/*
80759854Sbp * Allocate a new file; must be used instead of malloc()
80839178Smsmith * to ensure safe initialisation.
80939178Smsmith */
81059854Sbpstruct preloaded_file *
81159854Sbpfile_alloc(void)
81239178Smsmith{
81359854Sbp    struct preloaded_file	*fp;
81439178Smsmith
81559854Sbp    if ((fp = malloc(sizeof(struct preloaded_file))) != NULL) {
81659854Sbp	bzero(fp, sizeof(struct preloaded_file));
81739178Smsmith    }
81859854Sbp    return (fp);
81939178Smsmith}
82039178Smsmith
82139178Smsmith/*
82238764Smsmith * Add a module to the chain
82338764Smsmith */
82438764Smsmithstatic void
82559854Sbpfile_insert_tail(struct preloaded_file *fp)
82638764Smsmith{
82759854Sbp    struct preloaded_file	*cm;
82838764Smsmith
82959854Sbp    /* Append to list of loaded file */
83059854Sbp    fp->f_next = NULL;
83159854Sbp    if (preloaded_files == NULL) {
83259854Sbp	preloaded_files = fp;
83338764Smsmith    } else {
83459854Sbp	for (cm = preloaded_files; cm->f_next != NULL; cm = cm->f_next)
83538764Smsmith	    ;
83659854Sbp	cm->f_next = fp;
83738764Smsmith    }
83838764Smsmith}
83959854Sbp
84083321Speterstatic char *
84183321Spetermoduledir_fullpath(struct moduledir *mdp, const char *fname)
84283321Speter{
84383321Speter    char *cp;
84483321Speter
84583321Speter    cp = malloc(strlen(mdp->d_path) + strlen(fname) + 2);
84683321Speter    if (cp == NULL)
84783321Speter	return NULL;
84883321Speter    strcpy(cp, mdp->d_path);
84983321Speter    strcat(cp, "/");
85083321Speter    strcat(cp, fname);
85183321Speter    return (cp);
85283321Speter}
85383321Speter
85483321Speter/*
85583321Speter * Read linker.hints file into memory performing some sanity checks.
85683321Speter */
85783321Speterstatic void
85883321Spetermoduledir_readhints(struct moduledir *mdp)
85983321Speter{
86083321Speter    struct stat	st;
86183321Speter    char	*path;
86283321Speter    int		fd, size, version;
86383321Speter
86483321Speter    if (mdp->d_hints != NULL || (mdp->d_flags & MDIR_NOHINTS))
86583321Speter	return;
86683321Speter    path = moduledir_fullpath(mdp, "linker.hints");
86783321Speter    if (stat(path, &st) != 0 || st.st_size < (sizeof(version) + sizeof(int)) ||
86883321Speter	st.st_size > 100 * 1024 || (fd = open(path, O_RDONLY)) < 0) {
86983321Speter	free(path);
87083321Speter	mdp->d_flags |= MDIR_NOHINTS;
87183321Speter	return;
87283321Speter    }
87383321Speter    free(path);
87483321Speter    size = read(fd, &version, sizeof(version));
87583321Speter    if (size != sizeof(version) || version != LINKER_HINTS_VERSION)
87683321Speter	goto bad;
87783321Speter    size = st.st_size - size;
87883321Speter    mdp->d_hints = malloc(size);
87983321Speter    if (mdp->d_hints == NULL)
88083321Speter	goto bad;
88183321Speter    if (read(fd, mdp->d_hints, size) != size)
88283321Speter	goto bad;
88383321Speter    mdp->d_hintsz = size;
88483321Speter    close(fd);
88583321Speter    return;
88683321Speterbad:
88783321Speter    close(fd);
88883321Speter    if (mdp->d_hints) {
88983321Speter	free(mdp->d_hints);
89083321Speter	mdp->d_hints = NULL;
89183321Speter    }
89283321Speter    mdp->d_flags |= MDIR_NOHINTS;
89383321Speter    return;
89483321Speter}
89583321Speter
89683321Speter/*
89783321Speter * Extract directories from the ';' separated list, remove duplicates.
89883321Speter */
89983321Speterstatic void
90083321Spetermoduledir_rebuild(void)
90183321Speter{
90283321Speter    struct	moduledir *mdp, *mtmp;
90383321Speter    const char	*path, *cp, *ep;
90483321Speter    int		cplen;
90583321Speter
90683321Speter    path = getenv("module_path");
90783321Speter    if (path == NULL)
90883321Speter	path = default_searchpath;
90983321Speter    /*
91083321Speter     * Rebuild list of module directories if it changed
91183321Speter     */
91283321Speter    STAILQ_FOREACH(mdp, &moduledir_list, d_link)
91383321Speter	mdp->d_flags |= MDIR_REMOVED;
91483321Speter
91583321Speter    for (ep = path; *ep != 0;  ep++) {
91683321Speter	cp = ep;
91783321Speter	for (; *ep != 0 && *ep != ';'; ep++)
91883321Speter	    ;
91983321Speter	/*
92083321Speter	 * Ignore trailing slashes
92183321Speter	 */
92283321Speter	for (cplen = ep - cp; cplen > 1 && cp[cplen - 1] == '/'; cplen--)
92383321Speter	    ;
92483321Speter	STAILQ_FOREACH(mdp, &moduledir_list, d_link) {
92583321Speter	    if (strlen(mdp->d_path) != cplen ||	bcmp(cp, mdp->d_path, cplen) != 0)
92683321Speter		continue;
92783321Speter	    mdp->d_flags &= ~MDIR_REMOVED;
92883321Speter	    break;
92983321Speter	}
93083321Speter	if (mdp == NULL) {
93183321Speter	    mdp = malloc(sizeof(*mdp) + cplen + 1);
93283321Speter	    if (mdp == NULL)
93383321Speter		return;
93483321Speter	    mdp->d_path = (char*)(mdp + 1);
93583321Speter	    bcopy(cp, mdp->d_path, cplen);
93683321Speter	    mdp->d_path[cplen] = 0;
93783321Speter	    mdp->d_hints = NULL;
93883321Speter	    mdp->d_flags = 0;
93983321Speter	    STAILQ_INSERT_TAIL(&moduledir_list, mdp, d_link);
94083321Speter	}
94194419Speter	if (*ep == 0)
94294419Speter	    break;
94383321Speter    }
94483321Speter    /*
94583321Speter     * Delete unused directories if any
94683321Speter     */
94783321Speter    mdp = STAILQ_FIRST(&moduledir_list);
94883321Speter    while (mdp) {
94983321Speter	if ((mdp->d_flags & MDIR_REMOVED) == 0) {
95083321Speter	    mdp = STAILQ_NEXT(mdp, d_link);
95183321Speter	} else {
95283321Speter	    if (mdp->d_hints)
95383321Speter		free(mdp->d_hints);
95483321Speter	    mtmp = mdp;
95583321Speter	    mdp = STAILQ_NEXT(mdp, d_link);
95683321Speter	    STAILQ_REMOVE(&moduledir_list, mtmp, moduledir, d_link);
95783321Speter	    free(mtmp);
95883321Speter	}
95983321Speter    }
96083321Speter    return;
96183321Speter}
962