module.c revision 273658
118334Speter/*-
290075Sobrien * Copyright (c) 1998 Michael Smith <msmith@freebsd.org>
3169689Skan * All rights reserved.
4169689Skan *
518334Speter * Redistribution and use in source and binary forms, with or without
690075Sobrien * modification, are permitted provided that the following conditions
718334Speter * are met:
890075Sobrien * 1. Redistributions of source code must retain the above copyright
990075Sobrien *    notice, this list of conditions and the following disclaimer.
1090075Sobrien * 2. Redistributions in binary form must reproduce the above copyright
1190075Sobrien *    notice, this list of conditions and the following disclaimer in the
1218334Speter *    documentation and/or other materials provided with the distribution.
1390075Sobrien *
1490075Sobrien * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1590075Sobrien * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1690075Sobrien * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1718334Speter * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1818334Speter * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
1990075Sobrien * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20169689Skan * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21169689Skan * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2218334Speter * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2318334Speter * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2418334Speter * SUCH DAMAGE.
2518334Speter */
2618334Speter
2718334Speter#include <sys/cdefs.h>
2818334Speter__FBSDID("$FreeBSD: stable/10/sys/boot/common/module.c 273658 2014-10-26 02:21:54Z ian $");
2918334Speter
3018334Speter/*
3118334Speter * file/module function dispatcher, support, etc.
3218334Speter */
3318334Speter
3418334Speter#include <stand.h>
3518334Speter#include <string.h>
3618334Speter#include <sys/param.h>
3718334Speter#include <sys/linker.h>
3818334Speter#include <sys/module.h>
3918334Speter#include <sys/queue.h>
4018334Speter
4118334Speter#include "bootstrap.h"
4218334Speter
4318334Speter#define	MDIR_REMOVED	0x0001
4418334Speter#define	MDIR_NOHINTS	0x0002
4518334Speter
4618334Speterstruct moduledir {
4718334Speter	char	*d_path;	/* path of modules directory */
4818334Speter	u_char	*d_hints;	/* content of linker.hints file */
4918334Speter	int	d_hintsz;	/* size of hints data */
5018334Speter	int	d_flags;
5118334Speter	STAILQ_ENTRY(moduledir) d_link;
5290075Sobrien};
5318334Speter
5418334Speterstatic int			file_load(char *filename, vm_offset_t dest, struct preloaded_file **result);
5518334Speterstatic int			file_load_dependencies(struct preloaded_file *base_mod);
5618334Speterstatic char *			file_search(const char *name, char **extlist);
5718334Speterstatic struct kernel_module *	file_findmodule(struct preloaded_file *fp, char *modname, struct mod_depend *verinfo);
5818334Speterstatic int			file_havepath(const char *name);
5952284Sobrienstatic char			*mod_searchmodule(char *name, struct mod_depend *verinfo);
6052284Sobrienstatic void			file_insert_tail(struct preloaded_file *mp);
6150397Sobrienstruct file_metadata*		metadata_next(struct file_metadata *base_mp, int type);
6250397Sobrienstatic void			moduledir_readhints(struct moduledir *mdp);
6318334Speterstatic void			moduledir_rebuild(void);
6450397Sobrien
65132718Skan/* load address should be tweaked by first module loaded (kernel) */
66132718Skanstatic vm_offset_t	loadaddr = 0;
67117395Skan
6818334Speter#if defined(LOADER_FDT_SUPPORT)
6990075Sobrienstatic const char	*default_searchpath =
7018334Speter    "/boot/kernel;/boot/modules;/boot/dtb";
7118334Speter#else
7290075Sobrienstatic const char	*default_searchpath ="/boot/kernel;/boot/modules";
7318334Speter#endif
7450397Sobrien
7518334Speterstatic STAILQ_HEAD(, moduledir) moduledir_list = STAILQ_HEAD_INITIALIZER(moduledir_list);
7618334Speter
7750397Sobrienstruct preloaded_file *preloaded_files = NULL;
7890075Sobrien
7990075Sobrienstatic char *kld_ext_list[] = {
80169689Skan    ".ko",
81169689Skan    "",
82169689Skan    ".debug",
83169689Skan    NULL
8418334Speter};
8518334Speter
8618334Speter
8718334Speter/*
8818334Speter * load an object, either a disk file or code module.
8990075Sobrien *
9090075Sobrien * To load a file, the syntax is:
9190075Sobrien *
9290075Sobrien * load -t <type> <path>
9318334Speter *
9490075Sobrien * code modules are loaded as:
9518334Speter *
9690075Sobrien * load <path> <options>
9718334Speter */
9890075Sobrien
9918334SpeterCOMMAND_SET(load, "load", "load a kernel or module", command_load);
10090075Sobrien
10190075Sobrienstatic int
10218334Spetercommand_load(int argc, char *argv[])
10390075Sobrien{
10418334Speter    char	*typestr;
10590075Sobrien    int		dofile, dokld, ch, error;
10690075Sobrien
10790075Sobrien    dokld = dofile = 0;
10890075Sobrien    optind = 1;
10990075Sobrien    optreset = 1;
11018334Speter    typestr = NULL;
11190075Sobrien    if (argc == 1) {
11218334Speter	command_errmsg = "no filename specified";
11390075Sobrien	return(CMD_ERROR);
11490075Sobrien    }
115132718Skan    while ((ch = getopt(argc, argv, "kt:")) != -1) {
11690075Sobrien	switch(ch) {
11790075Sobrien	case 'k':
11890075Sobrien	    dokld = 1;
11990075Sobrien	    break;
12018334Speter	case 't':
12190075Sobrien	    typestr = optarg;
12218334Speter	    dofile = 1;
12390075Sobrien	    break;
12418334Speter	case '?':
12590075Sobrien	default:
12618334Speter	    /* getopt has already reported an error */
127161651Skan	    return(CMD_OK);
128161651Skan	}
129161651Skan    }
130161651Skan    argv += (optind - 1);
131161651Skan    argc -= (optind - 1);
13290075Sobrien
13390075Sobrien    /*
13490075Sobrien     * Request to load a raw file?
13518334Speter     */
13690075Sobrien    if (dofile) {
13718334Speter	if ((argc != 2) || (typestr == NULL) || (*typestr == 0)) {
13890075Sobrien	    command_errmsg = "invalid load type";
13990075Sobrien	    return(CMD_ERROR);
14090075Sobrien	}
14118334Speter	return(file_loadraw(argv[1], typestr) ? CMD_OK : CMD_ERROR);
14290075Sobrien    }
14318334Speter    /*
14490075Sobrien     * Do we have explicit KLD load ?
14590075Sobrien     */
14618334Speter    if (dokld || file_havepath(argv[1])) {
14790075Sobrien	error = mod_loadkld(argv[1], argc - 2, argv + 2);
14818334Speter	if (error == EEXIST)
14990075Sobrien	    sprintf(command_errbuf, "warning: KLD '%s' already loaded", argv[1]);
15090075Sobrien	return (error == 0 ? CMD_OK : CMD_ERROR);
15118334Speter    }
15290075Sobrien    /*
15318334Speter     * Looks like a request for a module.
15490075Sobrien     */
15590075Sobrien    error = mod_load(argv[1], NULL, argc - 2, argv + 2);
15618334Speter    if (error == EEXIST)
15790075Sobrien	sprintf(command_errbuf, "warning: module '%s' already loaded", argv[1]);
15890075Sobrien    return (error == 0 ? CMD_OK : CMD_ERROR);
15918334Speter}
16090075Sobrien
16118334SpeterCOMMAND_SET(load_geli, "load_geli", "load a geli key", command_load_geli);
16290075Sobrien
16318334Speterstatic int
16490075Sobriencommand_load_geli(int argc, char *argv[])
16590075Sobrien{
16690075Sobrien    char	typestr[80];
16790075Sobrien    char	*cp;
16818334Speter    int		ch, num;
16990075Sobrien
17090075Sobrien    if (argc < 3) {
17118334Speter	    command_errmsg = "usage is [-n key#] <prov> <file>";
17290075Sobrien	    return(CMD_ERROR);
17390075Sobrien    }
17418334Speter
17590075Sobrien    num = 0;
17618334Speter    optind = 1;
17790075Sobrien    optreset = 1;
17890075Sobrien    while ((ch = getopt(argc, argv, "n:")) != -1) {
17918334Speter	switch(ch) {
18090075Sobrien	case 'n':
18118334Speter	    num = strtol(optarg, &cp, 0);
18290075Sobrien	    if (cp == optarg) {
18390075Sobrien		    sprintf(command_errbuf, "bad key index '%s'", optarg);
18490075Sobrien		    return(CMD_ERROR);
18590075Sobrien	    }
18690075Sobrien	    break;
18790075Sobrien	case '?':
18890075Sobrien	default:
18990075Sobrien	    /* getopt has already reported an error */
19018334Speter	    return(CMD_OK);
19118334Speter	}
19290075Sobrien    }
19318334Speter    argv += (optind - 1);
19418334Speter    argc -= (optind - 1);
19518334Speter    sprintf(typestr, "%s:geli_keyfile%d", argv[1], num);
19618334Speter    return(file_loadraw(argv[2], typestr) ? CMD_OK : CMD_ERROR);
19718334Speter}
19818334Speter
19918334SpeterCOMMAND_SET(unload, "unload", "unload all modules", command_unload);
20018334Speter
20118334Speterstatic int
20218334Spetercommand_unload(int argc, char *argv[])
20318334Speter{
20418334Speter    struct preloaded_file	*fp;
20518334Speter
20618334Speter    while (preloaded_files != NULL) {
20718334Speter	fp = preloaded_files;
20818334Speter	preloaded_files = preloaded_files->f_next;
20918334Speter	file_discard(fp);
21018334Speter    }
21118334Speter    loadaddr = 0;
21218334Speter    unsetenv("kernelname");
21318334Speter    return(CMD_OK);
21418334Speter}
21518334Speter
21618334SpeterCOMMAND_SET(lsmod, "lsmod", "list loaded modules", command_lsmod);
21718334Speter
21818334Speterstatic int
21918334Spetercommand_lsmod(int argc, char *argv[])
22018334Speter{
22118334Speter    struct preloaded_file	*fp;
22218334Speter    struct kernel_module	*mp;
22318334Speter    struct file_metadata	*md;
22418334Speter    char			lbuf[80];
22518334Speter    int				ch, verbose;
22618334Speter
22718334Speter    verbose = 0;
22818334Speter    optind = 1;
22918334Speter    optreset = 1;
23018334Speter    while ((ch = getopt(argc, argv, "v")) != -1) {
23118334Speter	switch(ch) {
23218334Speter	case 'v':
23318334Speter	    verbose = 1;
23418334Speter	    break;
23518334Speter	case '?':
23618334Speter	default:
23718334Speter	    /* getopt has already reported an error */
23818334Speter	    return(CMD_OK);
23918334Speter	}
24018334Speter    }
24118334Speter
24218334Speter    pager_open();
24318334Speter    for (fp = preloaded_files; fp; fp = fp->f_next) {
24418334Speter	sprintf(lbuf, " %p: %s (%s, 0x%lx)\n",
24518334Speter		(void *) fp->f_addr, fp->f_name, fp->f_type, (long) fp->f_size);
24618334Speter	pager_output(lbuf);
24718334Speter	if (fp->f_args != NULL) {
24818334Speter	    pager_output("    args: ");
24918334Speter	    pager_output(fp->f_args);
25090075Sobrien	    pager_output("\n");
25190075Sobrien	}
25290075Sobrien	if (fp->f_modules) {
253102780Skan	    pager_output("  modules: ");
25450397Sobrien	    for (mp = fp->f_modules; mp; mp = mp->m_next) {
25590075Sobrien		sprintf(lbuf, "%s.%d ", mp->m_name, mp->m_version);
25650397Sobrien		pager_output(lbuf);
25790075Sobrien	    }
25890075Sobrien	    pager_output("\n");
25990075Sobrien	}
26052284Sobrien	if (verbose) {
26190075Sobrien	    /* XXX could add some formatting smarts here to display some better */
26290075Sobrien	    for (md = fp->f_metadata; md != NULL; md = md->md_next) {
263102780Skan		sprintf(lbuf, "      0x%04x, 0x%lx\n", md->md_type, (long) md->md_size);
26490075Sobrien		pager_output(lbuf);
26590075Sobrien	    }
26690075Sobrien	}
26790075Sobrien    }
26890075Sobrien    pager_close();
26990075Sobrien    return(CMD_OK);
27090075Sobrien}
27190075Sobrien
27290075Sobrien/*
273169689Skan * File level interface, functions file_*
274169689Skan */
275169689Skanint
276169689Skanfile_load(char *filename, vm_offset_t dest, struct preloaded_file **result)
27790075Sobrien{
27890075Sobrien    static int last_file_format = 0;
27990075Sobrien    struct preloaded_file *fp;
28090075Sobrien    int error;
28190075Sobrien    int i;
28290075Sobrien
28390075Sobrien    if (archsw.arch_loadaddr != NULL)
28452284Sobrien	dest = archsw.arch_loadaddr(LOAD_RAW, filename, dest);
28552284Sobrien
28652284Sobrien    error = EFTYPE;
287132718Skan    for (i = last_file_format, fp = NULL;
288132718Skan	file_formats[i] && fp == NULL; i++) {
289132718Skan	error = (file_formats[i]->l_load)(filename, dest, &fp);
290132718Skan	if (error == 0) {
291132718Skan	    fp->f_loader = last_file_format = i; /* remember the loader */
292132718Skan	    *result = fp;
293132718Skan	    break;
294132718Skan	} else if (last_file_format == i && i != 0) {
295132718Skan	    /* Restart from the beginning */
296132718Skan	    i = -1;
297132718Skan	    last_file_format = 0;
298132718Skan	    fp = NULL;
299132718Skan	    continue;
300132718Skan	}
301132718Skan	if (error == EFTYPE)
302132718Skan	    continue;		/* Unknown to this handler? */
303132718Skan	if (error) {
304132718Skan	    sprintf(command_errbuf, "can't load file '%s': %s",
305132718Skan		filename, strerror(error));
306132718Skan	    break;
307132718Skan	}
308132718Skan    }
309132718Skan    return (error);
310132718Skan}
311132718Skan
312132718Skanstatic int
313132718Skanfile_load_dependencies(struct preloaded_file *base_file)
31418334Speter{
31518334Speter    struct file_metadata *md;
31618334Speter    struct preloaded_file *fp;
31718334Speter    struct mod_depend *verinfo;
31818334Speter    struct kernel_module *mp;
31918334Speter    char *dmodname;
320132718Skan    int error;
32118334Speter
32290075Sobrien    md = file_findmetadata(base_file, MODINFOMD_DEPLIST);
32318334Speter    if (md == NULL)
32490075Sobrien	return (0);
32518334Speter    error = 0;
32618334Speter    do {
32718334Speter	verinfo = (struct mod_depend*)md->md_data;
32890075Sobrien	dmodname = (char *)(verinfo + 1);
32990075Sobrien	if (file_findmodule(NULL, dmodname, verinfo) == NULL) {
33090075Sobrien	    printf("loading required module '%s'\n", dmodname);
33190075Sobrien	    error = mod_load(dmodname, verinfo, 0, NULL);
33290075Sobrien	    if (error)
333161651Skan		break;
33490075Sobrien	    /*
33590075Sobrien	     * If module loaded via kld name which isn't listed
33690075Sobrien	     * in the linker.hints file, we should check if it have
33790075Sobrien	     * required version.
33818334Speter	     */
33918334Speter	    mp = file_findmodule(NULL, dmodname, verinfo);
34018334Speter	    if (mp == NULL) {
34118334Speter		sprintf(command_errbuf, "module '%s' exists but with wrong version",
342169689Skan		    dmodname);
343132718Skan		error = ENOENT;
34418334Speter		break;
345117395Skan	    }
34618334Speter	}
347117395Skan	md = metadata_next(md, MODINFOMD_DEPLIST);
34818334Speter    } while (md);
34952284Sobrien    if (!error)
35052284Sobrien	return (0);
35152284Sobrien    /* Load failed; discard everything */
35252284Sobrien    while (base_file != NULL) {
35318334Speter        fp = base_file;
35418334Speter        base_file = base_file->f_next;
35518334Speter        file_discard(fp);
35618334Speter    }
35718334Speter    return (error);
35818334Speter}
35918334Speter
36018334Speter/*
36118334Speter * We've been asked to load (name) as (type), so just suck it in,
362169689Skan * no arguments or anything.
36318334Speter */
36418334Speterstruct preloaded_file *
36552284Sobrienfile_loadraw(char *name, char *type)
36652284Sobrien{
36718334Speter    struct preloaded_file	*fp;
36818334Speter    char			*cp;
36918334Speter    int				fd, got;
37018334Speter    vm_offset_t			laddr;
37118334Speter
372169689Skan    /* We can't load first */
373169689Skan    if ((file_findfile(NULL, NULL)) == NULL) {
374169689Skan	command_errmsg = "can't load file before kernel";
375169689Skan	return(NULL);
376169689Skan    }
37718334Speter
378169689Skan    /* locate the file on the load path */
379169689Skan    cp = file_search(name, NULL);
380169689Skan    if (cp == NULL) {
38118334Speter	sprintf(command_errbuf, "can't find '%s'", name);
38218334Speter	return(NULL);
38318334Speter    }
38490075Sobrien    name = cp;
38518334Speter
38618334Speter    if ((fd = open(name, O_RDONLY)) < 0) {
38718334Speter	sprintf(command_errbuf, "can't open '%s': %s", name, strerror(errno));
38818334Speter	free(name);
38918334Speter	return(NULL);
39018334Speter    }
39190075Sobrien
39218334Speter    if (archsw.arch_loadaddr != NULL)
39318334Speter	loadaddr = archsw.arch_loadaddr(LOAD_RAW, name, loadaddr);
39418334Speter
39518334Speter    laddr = loadaddr;
39618334Speter    for (;;) {
39718334Speter	/* read in 4k chunks; size is not really important */
39818334Speter	got = archsw.arch_readin(fd, laddr, 4096);
39918334Speter	if (got == 0)				/* end of file */
40018334Speter	    break;
40118334Speter	if (got < 0) {				/* error */
402117395Skan	    sprintf(command_errbuf, "error reading '%s': %s", name, strerror(errno));
40318334Speter	    free(name);
40418334Speter	    close(fd);
40518334Speter	    return(NULL);
40618334Speter	}
40718334Speter	laddr += got;
40818334Speter    }
40918334Speter
41018334Speter    /* Looks OK so far; create & populate control structure */
41118334Speter    fp = file_alloc();
41218334Speter    fp->f_name = strdup(name);
41318334Speter    fp->f_type = strdup(type);
41418334Speter    fp->f_args = NULL;
41518334Speter    fp->f_metadata = NULL;
41618334Speter    fp->f_loader = -1;
41718334Speter    fp->f_addr = loadaddr;
41818334Speter    fp->f_size = laddr - loadaddr;
41918334Speter
42018334Speter    /* recognise space consumption */
42118334Speter    loadaddr = laddr;
42218334Speter
42318334Speter    /* Add to the list of loaded files */
424132718Skan    file_insert_tail(fp);
42518334Speter    close(fd);
42618334Speter    return(fp);
42718334Speter}
42818334Speter
42918334Speter/*
43018334Speter * Load the module (name), pass it (argc),(argv), add container file
43118334Speter * to the list of loaded files.
43218334Speter * If module is already loaded just assign new argc/argv.
43318334Speter */
434117395Skanint
43518334Spetermod_load(char *modname, struct mod_depend *verinfo, int argc, char *argv[])
43650397Sobrien{
43790075Sobrien    struct kernel_module	*mp;
43890075Sobrien    int				err;
43990075Sobrien    char			*filename;
44090075Sobrien
44190075Sobrien    if (file_havepath(modname)) {
44290075Sobrien	printf("Warning: mod_load() called instead of mod_loadkld() for module '%s'\n", modname);
44350397Sobrien	return (mod_loadkld(modname, argc, argv));
44450397Sobrien    }
44550397Sobrien    /* see if module is already loaded */
44690075Sobrien    mp = file_findmodule(NULL, modname, verinfo);
44752284Sobrien    if (mp) {
44818334Speter#ifdef moduleargs
44918334Speter	if (mp->m_args)
45018334Speter	    free(mp->m_args);
45118334Speter	mp->m_args = unargv(argc, argv);
45218334Speter#endif
45318334Speter	sprintf(command_errbuf, "warning: module '%s' already loaded", mp->m_name);
45418334Speter	return (0);
45518334Speter    }
45618334Speter    /* locate file with the module on the search path */
45718334Speter    filename = mod_searchmodule(modname, verinfo);
45818334Speter    if (filename == NULL) {
45918334Speter	sprintf(command_errbuf, "can't find '%s'", modname);
46018334Speter	return (ENOENT);
461132718Skan    }
462132718Skan    err = mod_loadkld(filename, argc, argv);
46318334Speter    return (err);
464169689Skan}
46518334Speter
466169689Skan/*
46750397Sobrien * Load specified KLD. If path is omitted, then try to locate it via
46818334Speter * search path.
46918334Speter */
47018334Speterint
47118334Spetermod_loadkld(const char *kldname, int argc, char *argv[])
47218334Speter{
47318334Speter    struct preloaded_file	*fp, *last_file;
47418334Speter    int				err;
47518334Speter    char			*filename;
47618334Speter
47718334Speter    /*
47818334Speter     * Get fully qualified KLD name
479132718Skan     */
48018334Speter    filename = file_search(kldname, kld_ext_list);
48118334Speter    if (filename == NULL) {
48218334Speter	sprintf(command_errbuf, "can't find '%s'", kldname);
48318334Speter	return (ENOENT);
48418334Speter    }
48518334Speter    /*
48618334Speter     * Check if KLD already loaded
48718334Speter     */
48818334Speter    fp = file_findfile(filename, NULL);
48918334Speter    if (fp) {
49018334Speter	sprintf(command_errbuf, "warning: KLD '%s' already loaded", filename);
49118334Speter	free(filename);
49218334Speter	return (0);
49318334Speter    }
49490075Sobrien    for (last_file = preloaded_files;
49518334Speter	 last_file != NULL && last_file->f_next != NULL;
49618334Speter	 last_file = last_file->f_next)
49718334Speter	;
49818334Speter
49918334Speter    do {
500169689Skan	err = file_load(filename, loadaddr, &fp);
50190075Sobrien	if (err)
50218334Speter	    break;
50318334Speter	fp->f_args = unargv(argc, argv);
50490075Sobrien	loadaddr = fp->f_addr + fp->f_size;
50518334Speter	file_insert_tail(fp);		/* Add to the list of loaded files */
50618334Speter	if (file_load_dependencies(fp) != 0) {
50718334Speter	    err = ENOENT;
50818334Speter	    last_file->f_next = NULL;
50918334Speter	    loadaddr = last_file->f_addr + last_file->f_size;
51018334Speter	    fp = NULL;
51118334Speter	    break;
51218334Speter	}
51318334Speter    } while(0);
514169689Skan    if (err == EFTYPE)
51518334Speter	sprintf(command_errbuf, "don't know how to load module '%s'", filename);
51618334Speter    if (err && fp)
51718334Speter	file_discard(fp);
51818334Speter    free(filename);
51918334Speter    return (err);
52018334Speter}
52150397Sobrien
52290075Sobrien/*
52350397Sobrien * Find a file matching (name) and (type).
52450397Sobrien * NULL may be passed as a wildcard to either.
525132718Skan */
52650397Sobrienstruct preloaded_file *
52790075Sobrienfile_findfile(char *name, char *type)
52890075Sobrien{
52990075Sobrien    struct preloaded_file *fp;
53090075Sobrien
53190075Sobrien    for (fp = preloaded_files; fp != NULL; fp = fp->f_next) {
53290075Sobrien	if (((name == NULL) || !strcmp(name, fp->f_name)) &&
53390075Sobrien	    ((type == NULL) || !strcmp(type, fp->f_type)))
534169689Skan	    break;
53590075Sobrien    }
53690075Sobrien    return (fp);
53790075Sobrien}
53890075Sobrien
53996263Sobrien/*
54090075Sobrien * Find a module matching (name) inside of given file.
54190075Sobrien * NULL may be passed as a wildcard.
54290075Sobrien */
54390075Sobrienstruct kernel_module *
54490075Sobrienfile_findmodule(struct preloaded_file *fp, char *modname,
54590075Sobrien	struct mod_depend *verinfo)
54690075Sobrien{
54790075Sobrien    struct kernel_module *mp, *best;
54890075Sobrien    int bestver, mver;
54990075Sobrien
55090075Sobrien    if (fp == NULL) {
551132718Skan	for (fp = preloaded_files; fp; fp = fp->f_next) {
55290075Sobrien	    mp = file_findmodule(fp, modname, verinfo);
55390075Sobrien    	    if (mp)
55490075Sobrien		return (mp);
55590075Sobrien	}
55690075Sobrien	return (NULL);
55790075Sobrien    }
55890075Sobrien    best = NULL;
55990075Sobrien    bestver = 0;
56090075Sobrien    for (mp = fp->f_modules; mp; mp = mp->m_next) {
56190075Sobrien        if (strcmp(modname, mp->m_name) == 0) {
56290075Sobrien	    if (verinfo == NULL)
56390075Sobrien		return (mp);
56490075Sobrien	    mver = mp->m_version;
56590075Sobrien	    if (mver == verinfo->md_ver_preferred)
56690075Sobrien		return (mp);
56790075Sobrien	    if (mver >= verinfo->md_ver_minimum &&
56890075Sobrien		mver <= verinfo->md_ver_maximum &&
56990075Sobrien		mver > bestver) {
57090075Sobrien		best = mp;
57190075Sobrien		bestver = mver;
57290075Sobrien	    }
57390075Sobrien	}
57490075Sobrien    }
575117395Skan    return (best);
57690075Sobrien}
57790075Sobrien/*
57890075Sobrien * Make a copy of (size) bytes of data from (p), and associate them as
57990075Sobrien * metadata of (type) to the module (mp).
58090075Sobrien */
581132718Skanvoid
58290075Sobrienfile_addmetadata(struct preloaded_file *fp, int type, size_t size, void *p)
58350397Sobrien{
58490075Sobrien    struct file_metadata	*md;
58550397Sobrien
58650397Sobrien    md = malloc(sizeof(struct file_metadata) - sizeof(md->md_data) + size);
58750397Sobrien    md->md_size = size;
58850397Sobrien    md->md_type = type;
58990075Sobrien    bcopy(p, md->md_data, size);
59090075Sobrien    md->md_next = fp->f_metadata;
59190075Sobrien    fp->f_metadata = md;
59290075Sobrien}
59390075Sobrien
59490075Sobrien/*
59590075Sobrien * Find a metadata object of (type) associated with the file (fp)
59690075Sobrien */
59790075Sobrienstruct file_metadata *
59890075Sobrienfile_findmetadata(struct preloaded_file *fp, int type)
59990075Sobrien{
60090075Sobrien    struct file_metadata *md;
60190075Sobrien
60290075Sobrien    for (md = fp->f_metadata; md != NULL; md = md->md_next)
60390075Sobrien	if (md->md_type == type)
60490075Sobrien	    break;
60590075Sobrien    return(md);
60690075Sobrien}
60790075Sobrien
60890075Sobrienstruct file_metadata *
60990075Sobrienmetadata_next(struct file_metadata *md, int type)
61090075Sobrien{
61190075Sobrien    if (md == NULL)
61290075Sobrien	return (NULL);
61390075Sobrien    while((md = md->md_next) != NULL)
61490075Sobrien	if (md->md_type == type)
61590075Sobrien	    break;
616132718Skan    return (md);
61790075Sobrien}
61890075Sobrien
61990075Sobrienstatic char *emptyextlist[] = { "", NULL };
62090075Sobrien
62190075Sobrien/*
62290075Sobrien * Check if the given file is in place and return full path to it.
62390075Sobrien */
62490075Sobrienstatic char *
62590075Sobrienfile_lookup(const char *path, const char *name, int namelen, char **extlist)
62690075Sobrien{
62790075Sobrien    struct stat	st;
62890075Sobrien    char	*result, *cp, **cpp;
62990075Sobrien    int		pathlen, extlen, len;
63090075Sobrien
63190075Sobrien    pathlen = strlen(path);
63290075Sobrien    extlen = 0;
63390075Sobrien    if (extlist == NULL)
63490075Sobrien	extlist = emptyextlist;
63590075Sobrien    for (cpp = extlist; *cpp; cpp++) {
63690075Sobrien	len = strlen(*cpp);
63790075Sobrien	if (len > extlen)
63890075Sobrien	    extlen = len;
63990075Sobrien    }
64090075Sobrien    result = malloc(pathlen + namelen + extlen + 2);
64190075Sobrien    if (result == NULL)
64290075Sobrien	return (NULL);
643132718Skan    bcopy(path, result, pathlen);
64490075Sobrien    if (pathlen > 0 && result[pathlen - 1] != '/')
64590075Sobrien	result[pathlen++] = '/';
64690075Sobrien    cp = result + pathlen;
64790075Sobrien    bcopy(name, cp, namelen);
64890075Sobrien    cp += namelen;
64990075Sobrien    for (cpp = extlist; *cpp; cpp++) {
65090075Sobrien	strcpy(cp, *cpp);
65150397Sobrien	if (stat(result, &st) == 0 && S_ISREG(st.st_mode))
65250397Sobrien	    return result;
65350397Sobrien    }
65450397Sobrien    free(result);
65550397Sobrien    return NULL;
65696263Sobrien}
65750397Sobrien
65850397Sobrien/*
65950397Sobrien * Check if file name have any qualifiers
66050397Sobrien */
66150397Sobrienstatic int
66250397Sobrienfile_havepath(const char *name)
66390075Sobrien{
66450397Sobrien    const char		*cp;
66550397Sobrien
66650397Sobrien    archsw.arch_getdev(NULL, name, &cp);
66750397Sobrien    return (cp != name || strchr(name, '/') != NULL);
66850397Sobrien}
66950397Sobrien
67050397Sobrien/*
67150397Sobrien * Attempt to find the file (name) on the module searchpath.
67250397Sobrien * If (name) is qualified in any way, we simply check it and
67350397Sobrien * return it or NULL.  If it is not qualified, then we attempt
67490075Sobrien * to construct a path using entries in the environment variable
67550397Sobrien * module_path.
67650397Sobrien *
67750397Sobrien * The path we return a pointer to need never be freed, as we manage
67850397Sobrien * it internally.
67990075Sobrien */
68050397Sobrienstatic char *
68150397Sobrienfile_search(const char *name, char **extlist)
68250397Sobrien{
68350397Sobrien    struct moduledir	*mdp;
68450397Sobrien    struct stat		sb;
68550397Sobrien    char		*result;
68618334Speter    int			namelen;
68718334Speter
68818334Speter    /* Don't look for nothing */
68918334Speter    if (name == NULL)
69018334Speter	return(NULL);
691132718Skan
69218334Speter    if (*name == 0)
69318334Speter	return(strdup(name));
69490075Sobrien
69518334Speter    if (file_havepath(name)) {
69618334Speter	/* Qualified, so just see if it exists */
69718334Speter	if (stat(name, &sb) == 0)
69818334Speter	    return(strdup(name));
69918334Speter	return(NULL);
70018334Speter    }
70118334Speter    moduledir_rebuild();
70218334Speter    result = NULL;
70318334Speter    namelen = strlen(name);
70496263Sobrien    STAILQ_FOREACH(mdp, &moduledir_list, d_link) {
70518334Speter	result = file_lookup(mdp->d_path, name, namelen, extlist);
70618334Speter	if (result)
70718334Speter	    break;
70818334Speter    }
70918334Speter    return(result);
71018334Speter}
71150397Sobrien
71290075Sobrien#define	INT_ALIGN(base, ptr)	ptr = \
71350397Sobrien	(base) + (((ptr) - (base) + sizeof(int) - 1) & ~(sizeof(int) - 1))
71490075Sobrien
71550397Sobrienstatic char *
71618334Spetermod_search_hints(struct moduledir *mdp, const char *modname,
71750397Sobrien	struct mod_depend *verinfo)
71818334Speter{
71918334Speter    u_char	*cp, *recptr, *bufend, *best;
72018334Speter    char	*result;
72118334Speter    int		*intp, bestver, blen, clen, found, ival, modnamelen, reclen;
72218334Speter
72318334Speter    moduledir_readhints(mdp);
724169689Skan    modnamelen = strlen(modname);
72518334Speter    found = 0;
72618334Speter    result = NULL;
72718334Speter    bestver = 0;
72818334Speter    if (mdp->d_hints == NULL)
72918334Speter	goto bad;
73018334Speter    recptr = mdp->d_hints;
73118334Speter    bufend = recptr + mdp->d_hintsz;
73218334Speter    clen = blen = 0;
73390075Sobrien    best = cp = NULL;
73450397Sobrien    while (recptr < bufend && !found) {
73550397Sobrien	intp = (int*)recptr;
73618334Speter	reclen = *intp++;
73718334Speter	ival = *intp++;
73818334Speter	cp = (char*)intp;
73918334Speter	switch (ival) {
74018334Speter	case MDT_VERSION:
74118334Speter	    clen = *cp++;
74218334Speter	    if (clen != modnamelen || bcmp(cp, modname, clen) != 0)
74318334Speter		break;
74418334Speter	    cp += clen;
74518334Speter	    INT_ALIGN(mdp->d_hints, cp);
74618334Speter	    ival = *(int*)cp;
74718334Speter	    cp += sizeof(int);
74818334Speter	    clen = *cp++;
74918334Speter	    if (verinfo == NULL || ival == verinfo->md_ver_preferred) {
75018334Speter		found = 1;
75118334Speter		break;
75218334Speter	    }
75318334Speter	    if (ival >= verinfo->md_ver_minimum &&
75418334Speter		ival <= verinfo->md_ver_maximum &&
75518334Speter		ival > bestver) {
75618334Speter		bestver = ival;
75718334Speter		best = cp;
75818334Speter		blen = clen;
75918334Speter	    }
760132718Skan	    break;
76118334Speter	default:
76218334Speter	    break;
76318334Speter	}
76418334Speter	recptr += reclen + sizeof(int);
76518334Speter    }
766169689Skan    /*
767169689Skan     * Finally check if KLD is in the place
768169689Skan     */
769169689Skan    if (found)
770169689Skan	result = file_lookup(mdp->d_path, cp, clen, NULL);
771169689Skan    else if (best)
77218334Speter	result = file_lookup(mdp->d_path, best, blen, NULL);
773169689Skanbad:
774169689Skan    /*
775169689Skan     * If nothing found or hints is absent - fallback to the old way
776169689Skan     * by using "kldname[.ko]" as module name.
777169689Skan     */
778169689Skan    if (!found && !bestver && result == NULL)
779169689Skan	result = file_lookup(mdp->d_path, modname, modnamelen, kld_ext_list);
78018334Speter    return result;
78118334Speter}
78218334Speter
78318334Speter/*
78418334Speter * Attempt to locate the file containing the module (name)
78518334Speter */
78618334Speterstatic char *
78718334Spetermod_searchmodule(char *name, struct mod_depend *verinfo)
78818334Speter{
789169689Skan    struct	moduledir *mdp;
79018334Speter    char	*result;
791169689Skan
792169689Skan    moduledir_rebuild();
79318334Speter    /*
794132718Skan     * Now we ready to lookup module in the given directories
79518334Speter     */
79618334Speter    result = NULL;
797117395Skan    STAILQ_FOREACH(mdp, &moduledir_list, d_link) {
79890075Sobrien	result = mod_search_hints(mdp, name, verinfo);
79990075Sobrien	if (result)
80090075Sobrien	    break;
80118334Speter    }
802169689Skan
80390075Sobrien    return(result);
804169689Skan}
805169689Skan
80650397Sobrienint
80718334Speterfile_addmodule(struct preloaded_file *fp, char *modname, int version,
80818334Speter	struct kernel_module **newmp)
80918334Speter{
81018334Speter    struct kernel_module *mp;
81118334Speter    struct mod_depend mdepend;
812117395Skan
81318334Speter    bzero(&mdepend, sizeof(mdepend));
81490075Sobrien    mdepend.md_ver_preferred = version;
81518334Speter    mp = file_findmodule(fp, modname, &mdepend);
816132718Skan    if (mp)
817132718Skan	return (EEXIST);
818132718Skan    mp = malloc(sizeof(struct kernel_module));
81918334Speter    if (mp == NULL)
82090075Sobrien	return (ENOMEM);
82190075Sobrien    bzero(mp, sizeof(struct kernel_module));
82290075Sobrien    mp->m_name = strdup(modname);
82390075Sobrien    mp->m_version = version;
82418334Speter    mp->m_fp = fp;
82590075Sobrien    mp->m_next = fp->f_modules;
82690075Sobrien    fp->f_modules = mp;
82718334Speter    if (newmp)
82890075Sobrien	*newmp = mp;
82990075Sobrien    return (0);
83090075Sobrien}
83152284Sobrien
83290075Sobrien/*
83352284Sobrien * Throw a file away
83490075Sobrien */
83590075Sobrienvoid
83690075Sobrienfile_discard(struct preloaded_file *fp)
83752284Sobrien{
83890075Sobrien    struct file_metadata	*md, *md1;
83990075Sobrien    struct kernel_module	*mp, *mp1;
84052284Sobrien    if (fp == NULL)
84190075Sobrien	return;
84290075Sobrien    md = fp->f_metadata;
84390075Sobrien    while (md) {
84452284Sobrien	md1 = md;
84590075Sobrien	md = md->md_next;
84690075Sobrien	free(md1);
84790075Sobrien    }
84890075Sobrien    mp = fp->f_modules;
84990075Sobrien    while (mp) {
85090075Sobrien	if (mp->m_name)
85190075Sobrien	    free(mp->m_name);
85218334Speter	mp1 = mp;
85390075Sobrien	mp = mp->m_next;
85490075Sobrien	free(mp1);
85518334Speter    }
856169689Skan    if (fp->f_name != NULL)
857169689Skan	free(fp->f_name);
858169689Skan    if (fp->f_type != NULL)
859169689Skan        free(fp->f_type);
860169689Skan    if (fp->f_args != NULL)
861169689Skan        free(fp->f_args);
862169689Skan    free(fp);
86350397Sobrien}
864169689Skan
865169689Skan/*
866169689Skan * Allocate a new file; must be used instead of malloc()
86750397Sobrien * to ensure safe initialisation.
868169689Skan */
869169689Skanstruct preloaded_file *
870169689Skanfile_alloc(void)
871169689Skan{
87218334Speter    struct preloaded_file	*fp;
873169689Skan
874169689Skan    if ((fp = malloc(sizeof(struct preloaded_file))) != NULL) {
87590075Sobrien	bzero(fp, sizeof(struct preloaded_file));
87652284Sobrien    }
877169689Skan    return (fp);
878169689Skan}
879169689Skan
88090075Sobrien/*
88190075Sobrien * Add a module to the chain
88290075Sobrien */
88390075Sobrienstatic void
88490075Sobrienfile_insert_tail(struct preloaded_file *fp)
88590075Sobrien{
88690075Sobrien    struct preloaded_file	*cm;
88790075Sobrien
88890075Sobrien    /* Append to list of loaded file */
88990075Sobrien    fp->f_next = NULL;
89090075Sobrien    if (preloaded_files == NULL) {
89118334Speter	preloaded_files = fp;
892169689Skan    } else {
89390075Sobrien	for (cm = preloaded_files; cm->f_next != NULL; cm = cm->f_next)
89490075Sobrien	    ;
89590075Sobrien	cm->f_next = fp;
896169689Skan    }
89790075Sobrien}
898132718Skan
89990075Sobrienstatic char *
90090075Sobrienmoduledir_fullpath(struct moduledir *mdp, const char *fname)
90190075Sobrien{
90290075Sobrien    char *cp;
90318334Speter
90490075Sobrien    cp = malloc(strlen(mdp->d_path) + strlen(fname) + 2);
90518334Speter    if (cp == NULL)
90690075Sobrien	return NULL;
90790075Sobrien    strcpy(cp, mdp->d_path);
90890075Sobrien    strcat(cp, "/");
90990075Sobrien    strcat(cp, fname);
91090075Sobrien    return (cp);
91118334Speter}
91290075Sobrien
91390075Sobrien/*
91490075Sobrien * Read linker.hints file into memory performing some sanity checks.
91590075Sobrien */
91618334Speterstatic void
91790075Sobrienmoduledir_readhints(struct moduledir *mdp)
91890075Sobrien{
91990075Sobrien    struct stat	st;
92090075Sobrien    char	*path;
92190075Sobrien    int		fd, size, version;
92290075Sobrien
92390075Sobrien    if (mdp->d_hints != NULL || (mdp->d_flags & MDIR_NOHINTS))
92490075Sobrien	return;
92590075Sobrien    path = moduledir_fullpath(mdp, "linker.hints");
92690075Sobrien    if (stat(path, &st) != 0 ||
92790075Sobrien	st.st_size < (ssize_t)(sizeof(version) + sizeof(int)) ||
92890075Sobrien	st.st_size > 100 * 1024 || (fd = open(path, O_RDONLY)) < 0) {
92990075Sobrien	free(path);
93018334Speter	mdp->d_flags |= MDIR_NOHINTS;
93190075Sobrien	return;
93290075Sobrien    }
93390075Sobrien    free(path);
93490075Sobrien    size = read(fd, &version, sizeof(version));
93518334Speter    if (size != sizeof(version) || version != LINKER_HINTS_VERSION)
93690075Sobrien	goto bad;
93790075Sobrien    size = st.st_size - size;
93890075Sobrien    mdp->d_hints = malloc(size);
93918334Speter    if (mdp->d_hints == NULL)
94090075Sobrien	goto bad;
94190075Sobrien    if (read(fd, mdp->d_hints, size) != size)
94290075Sobrien	goto bad;
94390075Sobrien    mdp->d_hintsz = size;
94490075Sobrien    close(fd);
94518334Speter    return;
94690075Sobrienbad:
94790075Sobrien    close(fd);
94890075Sobrien    if (mdp->d_hints) {
94990075Sobrien	free(mdp->d_hints);
95018334Speter	mdp->d_hints = NULL;
95190075Sobrien    }
95290075Sobrien    mdp->d_flags |= MDIR_NOHINTS;
95390075Sobrien    return;
954169689Skan}
95590075Sobrien
95690075Sobrien/*
95790075Sobrien * Extract directories from the ';' separated list, remove duplicates.
95890075Sobrien */
95990075Sobrienstatic void
96050397Sobrienmoduledir_rebuild(void)
96190075Sobrien{
962169689Skan    struct	moduledir *mdp, *mtmp;
96318334Speter    const char	*path, *cp, *ep;
964169689Skan    int		cplen;
965169689Skan
966169689Skan    path = getenv("module_path");
967169689Skan    if (path == NULL)
968169689Skan	path = default_searchpath;
969169689Skan    /*
97090075Sobrien     * Rebuild list of module directories if it changed
97190075Sobrien     */
97290075Sobrien    STAILQ_FOREACH(mdp, &moduledir_list, d_link)
97390075Sobrien	mdp->d_flags |= MDIR_REMOVED;
97490075Sobrien
975169689Skan    for (ep = path; *ep != 0;  ep++) {
976169689Skan	cp = ep;
977169689Skan	for (; *ep != 0 && *ep != ';'; ep++)
978169689Skan	    ;
97990075Sobrien	/*
98018334Speter	 * Ignore trailing slashes
981169689Skan	 */
982102780Skan	for (cplen = ep - cp; cplen > 1 && cp[cplen - 1] == '/'; cplen--)
98390075Sobrien	    ;
98418334Speter	STAILQ_FOREACH(mdp, &moduledir_list, d_link) {
98590075Sobrien	    if (strlen(mdp->d_path) != cplen ||	bcmp(cp, mdp->d_path, cplen) != 0)
98690075Sobrien		continue;
98790075Sobrien	    mdp->d_flags &= ~MDIR_REMOVED;
98890075Sobrien	    break;
98990075Sobrien	}
99090075Sobrien	if (mdp == NULL) {
99150397Sobrien	    mdp = malloc(sizeof(*mdp) + cplen + 1);
99290075Sobrien	    if (mdp == NULL)
99390075Sobrien		return;
99490075Sobrien	    mdp->d_path = (char*)(mdp + 1);
99590075Sobrien	    bcopy(cp, mdp->d_path, cplen);
99690075Sobrien	    mdp->d_path[cplen] = 0;
99790075Sobrien	    mdp->d_hints = NULL;
99890075Sobrien	    mdp->d_flags = 0;
99990075Sobrien	    STAILQ_INSERT_TAIL(&moduledir_list, mdp, d_link);
100090075Sobrien	}
1001169689Skan	if (*ep == 0)
1002169689Skan	    break;
1003169689Skan    }
1004169689Skan    /*
1005169689Skan     * Delete unused directories if any
1006169689Skan     */
100790075Sobrien    mdp = STAILQ_FIRST(&moduledir_list);
100850397Sobrien    while (mdp) {
100918334Speter	if ((mdp->d_flags & MDIR_REMOVED) == 0) {
101018334Speter	    mdp = STAILQ_NEXT(mdp, d_link);
101118334Speter	} else {
1012169689Skan	    if (mdp->d_hints)
1013169689Skan		free(mdp->d_hints);
1014169689Skan	    mtmp = mdp;
1015169689Skan	    mdp = STAILQ_NEXT(mdp, d_link);
1016169689Skan	    STAILQ_REMOVE(&moduledir_list, mtmp, moduledir, d_link);
1017169689Skan	    free(mtmp);
1018169689Skan	}
1019169689Skan    }
1020169689Skan    return;
1021169689Skan}
1022169689Skan