xref: /illumos-gate/usr/src/boot/common/module.c (revision 9fbdb45a)
122028508SToomas Soome /*
222028508SToomas Soome  * Copyright (c) 1998 Michael Smith <msmith@freebsd.org>
322028508SToomas Soome  * All rights reserved.
422028508SToomas Soome  *
522028508SToomas Soome  * Redistribution and use in source and binary forms, with or without
622028508SToomas Soome  * modification, are permitted provided that the following conditions
722028508SToomas Soome  * are met:
822028508SToomas Soome  * 1. Redistributions of source code must retain the above copyright
922028508SToomas Soome  *    notice, this list of conditions and the following disclaimer.
1022028508SToomas Soome  * 2. Redistributions in binary form must reproduce the above copyright
1122028508SToomas Soome  *    notice, this list of conditions and the following disclaimer in the
1222028508SToomas Soome  *    documentation and/or other materials provided with the distribution.
1322028508SToomas Soome  *
1422028508SToomas Soome  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1522028508SToomas Soome  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1622028508SToomas Soome  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1722028508SToomas Soome  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1822028508SToomas Soome  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
1922028508SToomas Soome  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2022028508SToomas Soome  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2122028508SToomas Soome  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2222028508SToomas Soome  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2322028508SToomas Soome  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2422028508SToomas Soome  * SUCH DAMAGE.
2522028508SToomas Soome  */
2622028508SToomas Soome 
2722028508SToomas Soome #include <sys/cdefs.h>
2822028508SToomas Soome 
2922028508SToomas Soome /*
3022028508SToomas Soome  * file/module function dispatcher, support, etc.
3122028508SToomas Soome  */
3222028508SToomas Soome 
3322028508SToomas Soome #include <stand.h>
3422028508SToomas Soome #include <string.h>
3522028508SToomas Soome #include <sys/param.h>
3622028508SToomas Soome #include <sys/linker.h>
3722028508SToomas Soome #include <sys/module.h>
3822028508SToomas Soome #include <sys/queue.h>
3922028508SToomas Soome #include <sys/stdint.h>
4022028508SToomas Soome #include <sys/tem_impl.h>
4122028508SToomas Soome #include <sys/font.h>
4222028508SToomas Soome #include <sys/sha1.h>
4322028508SToomas Soome #include <libcrypto.h>
4422028508SToomas Soome 
4522028508SToomas Soome #include "bootstrap.h"
4622028508SToomas Soome 
4722028508SToomas Soome #if defined(EFI)
4822028508SToomas Soome #define	PTOV(pa)	((void *)pa)
4922028508SToomas Soome #else
5022028508SToomas Soome #include "../i386/btx/lib/btxv86.h"
5122028508SToomas Soome #endif
5222028508SToomas Soome 
5322028508SToomas Soome #define	MDIR_REMOVED	0x0001
5422028508SToomas Soome #define	MDIR_NOHINTS	0x0002
5522028508SToomas Soome 
5622028508SToomas Soome struct moduledir {
5722028508SToomas Soome 	char	*d_path;	/* path of modules directory */
5822028508SToomas Soome 	uchar_t	*d_hints;	/* content of linker.hints file */
5922028508SToomas Soome 	int	d_hintsz;	/* size of hints data */
6022028508SToomas Soome 	int	d_flags;
6122028508SToomas Soome 	STAILQ_ENTRY(moduledir) d_link;
6222028508SToomas Soome };
6322028508SToomas Soome 
6422028508SToomas Soome static int file_load(char *, vm_offset_t, struct preloaded_file **);
6522028508SToomas Soome static int file_load_dependencies(struct preloaded_file *);
6622028508SToomas Soome static char *file_search(const char *, const char **);
6722028508SToomas Soome static struct kernel_module *file_findmodule(struct preloaded_file *, char *,
6822028508SToomas Soome     struct mod_depend *);
6922028508SToomas Soome static int file_havepath(const char *);
7022028508SToomas Soome static char *mod_searchmodule(char *, struct mod_depend *);
7122028508SToomas Soome static void file_insert_tail(struct preloaded_file *);
7222028508SToomas Soome static void file_remove(struct preloaded_file *);
7322028508SToomas Soome struct file_metadata *metadata_next(struct file_metadata *, int);
7422028508SToomas Soome static void moduledir_readhints(struct moduledir *);
7522028508SToomas Soome static void moduledir_rebuild(void);
7622028508SToomas Soome 
7722028508SToomas Soome /* load address should be tweaked by first module loaded (kernel) */
7822028508SToomas Soome static vm_offset_t loadaddr = 0;
7922028508SToomas Soome 
8022028508SToomas Soome #if defined(LOADER_FDT_SUPPORT)
8122028508SToomas Soome static const char *default_searchpath = "/boot/kernel;/boot/modules;/boot/dtb";
8222028508SToomas Soome #else
8322028508SToomas Soome static const char *default_searchpath = "/platform/i86pc";
8422028508SToomas Soome #endif
8522028508SToomas Soome 
8622028508SToomas Soome static STAILQ_HEAD(, moduledir) moduledir_list =
8722028508SToomas Soome     STAILQ_HEAD_INITIALIZER(moduledir_list);
8822028508SToomas Soome 
8922028508SToomas Soome struct preloaded_file *preloaded_files = NULL;
9022028508SToomas Soome 
9122028508SToomas Soome static const char *kld_ext_list[] = {
9222028508SToomas Soome 	".ko",
9322028508SToomas Soome 	"",
9422028508SToomas Soome 	".debug",
9522028508SToomas Soome 	NULL
9622028508SToomas Soome };
9722028508SToomas Soome 
9822028508SToomas Soome 
9922028508SToomas Soome /*
10022028508SToomas Soome  * load an object, either a disk file or code module.
10122028508SToomas Soome  *
10222028508SToomas Soome  * To load a file, the syntax is:
10322028508SToomas Soome  *
10422028508SToomas Soome  * load -t <type> <path>
10522028508SToomas Soome  *
10622028508SToomas Soome  * code modules are loaded as:
10722028508SToomas Soome  *
10822028508SToomas Soome  * load <path> <options>
10922028508SToomas Soome  */
11022028508SToomas Soome 
11122028508SToomas Soome COMMAND_SET(load, "load", "load a kernel or module", command_load);
11222028508SToomas Soome 
11322028508SToomas Soome static int
command_load(int argc,char * argv[])11422028508SToomas Soome command_load(int argc, char *argv[])
11522028508SToomas Soome {
11622028508SToomas Soome 	char *typestr;
11722028508SToomas Soome 	int dofile, dokld, ch, error;
11822028508SToomas Soome 
11922028508SToomas Soome 	dokld = dofile = 0;
12022028508SToomas Soome 	optind = 1;
12122028508SToomas Soome 	optreset = 1;
12222028508SToomas Soome 	typestr = NULL;
12322028508SToomas Soome 	if (argc == 1) {
12422028508SToomas Soome 		command_errmsg = "no filename specified";
12522028508SToomas Soome 		return (CMD_CRIT);
12622028508SToomas Soome 	}
12722028508SToomas Soome 	while ((ch = getopt(argc, argv, "kt:")) != -1) {
12822028508SToomas Soome 		switch (ch) {
12922028508SToomas Soome 		case 'k':
13022028508SToomas Soome 			dokld = 1;
13122028508SToomas Soome 			break;
13222028508SToomas Soome 		case 't':
13322028508SToomas Soome 			typestr = optarg;
13422028508SToomas Soome 			dofile = 1;
13522028508SToomas Soome 			break;
13622028508SToomas Soome 		case '?':
13722028508SToomas Soome 		default:
13822028508SToomas Soome 			/* getopt has already reported an error */
13922028508SToomas Soome 			return (CMD_OK);
14022028508SToomas Soome 		}
14122028508SToomas Soome 	}
14222028508SToomas Soome 	argv += (optind - 1);
14322028508SToomas Soome 	argc -= (optind - 1);
14422028508SToomas Soome 
14522028508SToomas Soome 	printf("Loading %s...\n", argv[1]);
14622028508SToomas Soome 	/*
14722028508SToomas Soome 	 * Request to load a raw file?
14822028508SToomas Soome 	 */
14922028508SToomas Soome 	if (dofile) {
15022028508SToomas Soome 		struct preloaded_file *fp;
15122028508SToomas Soome 
15222028508SToomas Soome 		if ((typestr == NULL) || (*typestr == 0)) {
15322028508SToomas Soome 			command_errmsg = "invalid load type";
15422028508SToomas Soome 			return (CMD_CRIT);
15522028508SToomas Soome 		}
15622028508SToomas Soome 
15722028508SToomas Soome 		if (file_findfile(argv[1], typestr) != NULL) {
158fc39bce2SToomas Soome 			(void) snprintf(command_errbuf, sizeof (command_errbuf),
15922028508SToomas Soome 			    "warning: file '%s' already loaded", argv[1]);
16022028508SToomas Soome 			return (CMD_WARN);
16122028508SToomas Soome 		}
16222028508SToomas Soome 
16322028508SToomas Soome 		fp = file_loadraw(argv[1], typestr, argc - 2, argv + 2, 1);
16422028508SToomas Soome 		if (fp != NULL)
16522028508SToomas Soome 			return (CMD_OK);
16622028508SToomas Soome 
16722028508SToomas Soome 		/* Failing to load mfs_root is never going to end well! */
16822028508SToomas Soome 		if (strcmp("mfs_root", typestr) == 0)
16922028508SToomas Soome 			return (CMD_FATAL);
17022028508SToomas Soome 
17122028508SToomas Soome 		return (CMD_ERROR);
17222028508SToomas Soome 	}
17322028508SToomas Soome 	/*
17422028508SToomas Soome 	 * Do we have explicit KLD load ?
17522028508SToomas Soome 	 */
17622028508SToomas Soome 	if (dokld || file_havepath(argv[1])) {
17722028508SToomas Soome 		error = mod_loadkld(argv[1], argc - 2, argv + 2);
17822028508SToomas Soome 		if (error == EEXIST) {
179fc39bce2SToomas Soome 			(void) snprintf(command_errbuf, sizeof (command_errbuf),
18022028508SToomas Soome 			    "warning: KLD '%s' already loaded", argv[1]);
18122028508SToomas Soome 			return (CMD_WARN);
18222028508SToomas Soome 		}
18322028508SToomas Soome 
18422028508SToomas Soome 		return (error == 0 ? CMD_OK : CMD_CRIT);
18522028508SToomas Soome 	}
18622028508SToomas Soome 	/*
18722028508SToomas Soome 	 * Looks like a request for a module.
18822028508SToomas Soome 	 */
18922028508SToomas Soome 	error = mod_load(argv[1], NULL, argc - 2, argv + 2);
19022028508SToomas Soome 	if (error == EEXIST) {
191fc39bce2SToomas Soome 		(void) snprintf(command_errbuf, sizeof (command_errbuf),
19222028508SToomas Soome 		    "warning: module '%s' already loaded", argv[1]);
19322028508SToomas Soome 		return (CMD_WARN);
19422028508SToomas Soome 	}
19522028508SToomas Soome 
19622028508SToomas Soome 	return (error == 0 ? CMD_OK : CMD_CRIT);
19722028508SToomas Soome }
19822028508SToomas Soome 
19922028508SToomas Soome void
unload(void)20022028508SToomas Soome unload(void)
20122028508SToomas Soome {
20222028508SToomas Soome 	struct preloaded_file *fp;
20322028508SToomas Soome 
20422028508SToomas Soome 	while (preloaded_files != NULL) {
20522028508SToomas Soome 		fp = preloaded_files;
20622028508SToomas Soome 		preloaded_files = preloaded_files->f_next;
20722028508SToomas Soome 		file_discard(fp);
20822028508SToomas Soome 	}
20922028508SToomas Soome 	loadaddr = 0;
210fc39bce2SToomas Soome 	(void) unsetenv("kernelname");
21122028508SToomas Soome }
21222028508SToomas Soome 
21322028508SToomas Soome COMMAND_SET(unload, "unload", "unload all modules", command_unload);
21422028508SToomas Soome 
21522028508SToomas Soome static int
command_unload(int argc __unused,char * argv[]__unused)21622028508SToomas Soome command_unload(int argc __unused, char *argv[] __unused)
21722028508SToomas Soome {
21822028508SToomas Soome 	unload();
21922028508SToomas Soome 	return (CMD_OK);
22022028508SToomas Soome }
22122028508SToomas Soome 
22222028508SToomas Soome COMMAND_SET(lsmod, "lsmod", "list loaded modules", command_lsmod);
22322028508SToomas Soome 
22422028508SToomas Soome static int
command_lsmod(int argc,char * argv[])22522028508SToomas Soome command_lsmod(int argc, char *argv[])
22622028508SToomas Soome {
22722028508SToomas Soome 	struct preloaded_file *fp;
22822028508SToomas Soome 	struct kernel_module *mp;
22922028508SToomas Soome 	struct file_metadata *md;
23022028508SToomas Soome 	char lbuf[80];
23122028508SToomas Soome 	int ch, verbose, hash, ret = 0;
23222028508SToomas Soome 
23322028508SToomas Soome 	verbose = 0;
23422028508SToomas Soome 	hash = 0;
23522028508SToomas Soome 	optind = 1;
23622028508SToomas Soome 	optreset = 1;
23722028508SToomas Soome 	while ((ch = getopt(argc, argv, "vs")) != -1) {
23822028508SToomas Soome 		switch (ch) {
23922028508SToomas Soome 		case 'v':
24022028508SToomas Soome 			verbose = 1;
24122028508SToomas Soome 			break;
24222028508SToomas Soome 		case 's':
24322028508SToomas Soome 			hash = 1;
24422028508SToomas Soome 			break;
24522028508SToomas Soome 		case '?':
24622028508SToomas Soome 		default:
24722028508SToomas Soome 			/* getopt has already reported an error */
24822028508SToomas Soome 			return (CMD_OK);
24922028508SToomas Soome 		}
25022028508SToomas Soome 	}
25122028508SToomas Soome 
25222028508SToomas Soome 	pager_open();
25322028508SToomas Soome 	for (fp = preloaded_files; fp; fp = fp->f_next) {
254fc39bce2SToomas Soome 		(void) snprintf(lbuf, sizeof (lbuf), " %p: ",
255fc39bce2SToomas Soome 		    (void *) fp->f_addr);
256fc39bce2SToomas Soome 		(void) pager_output(lbuf);
257fc39bce2SToomas Soome 		(void) pager_output(fp->f_name);
258fc39bce2SToomas Soome 		(void) snprintf(lbuf, sizeof (lbuf), " (%s, 0x%lx)\n",
259fc39bce2SToomas Soome 		    fp->f_type, (long)fp->f_size);
26022028508SToomas Soome 		if (pager_output(lbuf))
26122028508SToomas Soome 			break;
26222028508SToomas Soome 		if (fp->f_args != NULL) {
263fc39bce2SToomas Soome 			(void) pager_output("    args: ");
264fc39bce2SToomas Soome 			(void) pager_output(fp->f_args);
26522028508SToomas Soome 			if (pager_output("\n"))
26622028508SToomas Soome 				break;
26722028508SToomas Soome 			if (strcmp(fp->f_type, "hash") == 0) {
268fc39bce2SToomas Soome 				size_t dsize;
269fc39bce2SToomas Soome 
270fc39bce2SToomas Soome 				(void) pager_output("    contents: ");
271fc39bce2SToomas Soome 				dsize = fp->f_size + 1;
272fc39bce2SToomas Soome 				if (sizeof (lbuf) < dsize)
273fc39bce2SToomas Soome 					dsize = sizeof (lbuf);
274fc39bce2SToomas Soome 				(void) strlcpy(lbuf, ptov(fp->f_addr), dsize);
27522028508SToomas Soome 				if (pager_output(lbuf))
27622028508SToomas Soome 					break;
27722028508SToomas Soome 			}
27822028508SToomas Soome 		}
27922028508SToomas Soome 
28022028508SToomas Soome 		if (hash == 1) {
28122028508SToomas Soome 			void *ptr = PTOV(fp->f_addr);
28222028508SToomas Soome 
283fc39bce2SToomas Soome 			(void) pager_output("  hash: ");
28422028508SToomas Soome 			sha1(ptr, fp->f_size, (uint8_t *)lbuf);
28522028508SToomas Soome 			for (int i = 0; i < SHA1_DIGEST_LENGTH; i++)
28622028508SToomas Soome 				printf("%02x", (int)(lbuf[i] & 0xff));
28722028508SToomas Soome 			if (pager_output("\n"))
28822028508SToomas Soome 				break;
28922028508SToomas Soome 		}
29022028508SToomas Soome 
29122028508SToomas Soome 		if (fp->f_modules) {
292fc39bce2SToomas Soome 			(void) pager_output("  modules: ");
29322028508SToomas Soome 			for (mp = fp->f_modules; mp; mp = mp->m_next) {
294fc39bce2SToomas Soome 				(void) snprintf(lbuf, sizeof (lbuf), "%s.%d ",
295fc39bce2SToomas Soome 				    mp->m_name, mp->m_version);
296fc39bce2SToomas Soome 				(void) pager_output(lbuf);
29722028508SToomas Soome 			}
29822028508SToomas Soome 			if (pager_output("\n"))
29922028508SToomas Soome 				break;
30022028508SToomas Soome 		}
30122028508SToomas Soome 		if (verbose) {
30222028508SToomas Soome 			/*
30322028508SToomas Soome 			 * XXX could add some formatting smarts here to
30422028508SToomas Soome 			 * display some better
30522028508SToomas Soome 			 */
30622028508SToomas Soome 			for (md = fp->f_metadata; md != NULL;
30722028508SToomas Soome 			    md = md->md_next) {
308fc39bce2SToomas Soome 				(void) snprintf(lbuf, sizeof (lbuf),
309fc39bce2SToomas Soome 				    "      0x%04x, 0x%lx\n",
31022028508SToomas Soome 				    md->md_type, (long)md->md_size);
31122028508SToomas Soome 				if ((ret = pager_output(lbuf)))
31222028508SToomas Soome 					break;
31322028508SToomas Soome 			}
31422028508SToomas Soome 		}
31522028508SToomas Soome 		if (ret != 0)
31622028508SToomas Soome 			break;
31722028508SToomas Soome 	}
31822028508SToomas Soome 	pager_close();
31922028508SToomas Soome 	return (CMD_OK);
32022028508SToomas Soome }
32122028508SToomas Soome 
32222028508SToomas Soome /*
32322028508SToomas Soome  * File level interface, functions file_*
32422028508SToomas Soome  */
32522028508SToomas Soome int
file_load(char * filename,vm_offset_t dest,struct preloaded_file ** result)32622028508SToomas Soome file_load(char *filename, vm_offset_t dest, struct preloaded_file **result)
32722028508SToomas Soome {
32822028508SToomas Soome 	static int last_file_format = 0;
32922028508SToomas Soome 	struct preloaded_file *fp;
33022028508SToomas Soome 	int error;
33122028508SToomas Soome 	int i;
33222028508SToomas Soome 
33322028508SToomas Soome 	if (preloaded_files == NULL)
33422028508SToomas Soome 		last_file_format = 0;
33522028508SToomas Soome 
33622028508SToomas Soome 	if (archsw.arch_loadaddr != NULL)
33722028508SToomas Soome 		dest = archsw.arch_loadaddr(LOAD_RAW, filename, dest);
33822028508SToomas Soome 
33922028508SToomas Soome 	error = EFTYPE;
34022028508SToomas Soome 	for (i = last_file_format, fp = NULL;
34122028508SToomas Soome 	    file_formats[i] && fp == NULL; i++) {
34222028508SToomas Soome 		error = (file_formats[i]->l_load)(filename, dest, &fp);
34322028508SToomas Soome 		if (error == 0) {
34422028508SToomas Soome 			/* remember the loader */
34522028508SToomas Soome 			fp->f_loader = last_file_format = i;
34622028508SToomas Soome 			*result = fp;
34722028508SToomas Soome 			break;
34822028508SToomas Soome 		} else if (last_file_format == i && i != 0) {
34922028508SToomas Soome 			/* Restart from the beginning */
35022028508SToomas Soome 			i = -1;
35122028508SToomas Soome 			last_file_format = 0;
35222028508SToomas Soome 			fp = NULL;
35322028508SToomas Soome 			continue;
35422028508SToomas Soome 		}
35522028508SToomas Soome 		if (error == EFTYPE)
35622028508SToomas Soome 			continue;	/* Unknown to this handler? */
35722028508SToomas Soome 		if (error) {
358fc39bce2SToomas Soome 			(void) snprintf(command_errbuf, sizeof (command_errbuf),
35922028508SToomas Soome 			    "can't load file '%s': %s", filename,
36022028508SToomas Soome 			    strerror(error));
36122028508SToomas Soome 			break;
36222028508SToomas Soome 		}
36322028508SToomas Soome 	}
36422028508SToomas Soome 	return (error);
36522028508SToomas Soome }
36622028508SToomas Soome 
36722028508SToomas Soome static int
file_load_dependencies(struct preloaded_file * base_file)36822028508SToomas Soome file_load_dependencies(struct preloaded_file *base_file)
36922028508SToomas Soome {
37022028508SToomas Soome 	struct file_metadata *md;
37122028508SToomas Soome 	struct preloaded_file *fp;
37222028508SToomas Soome 	struct mod_depend *verinfo;
37322028508SToomas Soome 	struct kernel_module *mp;
37422028508SToomas Soome 	char *dmodname;
37522028508SToomas Soome 	int error;
37622028508SToomas Soome 
37722028508SToomas Soome 	md = file_findmetadata(base_file, MODINFOMD_DEPLIST);
37822028508SToomas Soome 	if (md == NULL)
37922028508SToomas Soome 		return (0);
38022028508SToomas Soome 	error = 0;
38122028508SToomas Soome 	do {
38222028508SToomas Soome 		verinfo = (struct mod_depend *)md->md_data;
38322028508SToomas Soome 		dmodname = (char *)(verinfo + 1);
38422028508SToomas Soome 		if (file_findmodule(NULL, dmodname, verinfo) == NULL) {
38522028508SToomas Soome 			printf("loading required module '%s'\n", dmodname);
38622028508SToomas Soome 			error = mod_load(dmodname, verinfo, 0, NULL);
38722028508SToomas Soome 			if (error)
38822028508SToomas Soome 				break;
38922028508SToomas Soome 			/*
39022028508SToomas Soome 			 * If module loaded via kld name which isn't listed
39122028508SToomas Soome 			 * in the linker.hints file, we should check if it have
39222028508SToomas Soome 			 * required version.
39322028508SToomas Soome 			 */
39422028508SToomas Soome 			mp = file_findmodule(NULL, dmodname, verinfo);
39522028508SToomas Soome 			if (mp == NULL) {
396fc39bce2SToomas Soome 				(void) snprintf(command_errbuf,
39722028508SToomas Soome 				    sizeof (command_errbuf),
39822028508SToomas Soome 				    "module '%s' exists but with wrong version",
39922028508SToomas Soome 				    dmodname);
40022028508SToomas Soome 				error = ENOENT;
40122028508SToomas Soome 				break;
40222028508SToomas Soome 			}
40322028508SToomas Soome 		}
40422028508SToomas Soome 		md = metadata_next(md, MODINFOMD_DEPLIST);
40522028508SToomas Soome 	} while (md);
40622028508SToomas Soome 	if (!error)
40722028508SToomas Soome 		return (0);
40822028508SToomas Soome 	/* Load failed; discard everything */
40922028508SToomas Soome 	while (base_file != NULL) {
41022028508SToomas Soome 		fp = base_file;
41122028508SToomas Soome 		base_file = base_file->f_next;
41222028508SToomas Soome 		file_discard(fp);
41322028508SToomas Soome 	}
41422028508SToomas Soome 	return (error);
41522028508SToomas Soome }
41622028508SToomas Soome 
41722028508SToomas Soome /*
41822028508SToomas Soome  * Calculate the size of the environment module.
41922028508SToomas Soome  * The environment is list of name=value C strings, ending with a '\0' byte.
42022028508SToomas Soome  */
42122028508SToomas Soome static size_t
env_get_size(void)42222028508SToomas Soome env_get_size(void)
42322028508SToomas Soome {
42422028508SToomas Soome 	size_t size = 0;
42522028508SToomas Soome 	struct env_var *ep;
42622028508SToomas Soome 
42722028508SToomas Soome 	/* Traverse the environment. */
42822028508SToomas Soome 	for (ep = environ; ep != NULL; ep = ep->ev_next) {
42922028508SToomas Soome 		size += strlen(ep->ev_name);
43022028508SToomas Soome 		size++;		/* "=" */
43122028508SToomas Soome 		if (ep->ev_value != NULL)
43222028508SToomas Soome 			size += strlen(ep->ev_value);
43322028508SToomas Soome 		size++;		/* nul byte */
43422028508SToomas Soome 	}
43522028508SToomas Soome 	size++;			/* nul byte */
43622028508SToomas Soome 	return (size);
43722028508SToomas Soome }
43822028508SToomas Soome 
43922028508SToomas Soome static void
module_hash(struct preloaded_file * fp,void * addr,size_t size)44022028508SToomas Soome module_hash(struct preloaded_file *fp, void *addr, size_t size)
44122028508SToomas Soome {
44222028508SToomas Soome 	uint8_t hash[SHA1_DIGEST_LENGTH];
44322028508SToomas Soome 	char ascii[2 * SHA1_DIGEST_LENGTH + 1];
44422028508SToomas Soome 	int i;
44522028508SToomas Soome 
44622028508SToomas Soome 	sha1(addr, size, hash);
44722028508SToomas Soome 	for (i = 0; i < SHA1_DIGEST_LENGTH; i++) {
448fc39bce2SToomas Soome 		(void) snprintf(ascii + 2 * i, sizeof (ascii) - 2 * i, "%02x",
44922028508SToomas Soome 		    hash[i] & 0xff);
45022028508SToomas Soome 	}
45122028508SToomas Soome 	/* Out of memory here is not fatal issue. */
452fc39bce2SToomas Soome 	(void) asprintf(&fp->f_args, "hash=%s", ascii);
45322028508SToomas Soome }
45422028508SToomas Soome 
45522028508SToomas Soome /*
45622028508SToomas Soome  * Create virtual module for environment variables.
45722028508SToomas Soome  * This module should be created as late as possible before executing
45822028508SToomas Soome  * the OS kernel, or we may miss some environment variable updates.
45922028508SToomas Soome  */
46022028508SToomas Soome void
build_environment_module(void)46122028508SToomas Soome build_environment_module(void)
46222028508SToomas Soome {
46322028508SToomas Soome 	struct preloaded_file *fp;
46422028508SToomas Soome 	size_t size;
46522028508SToomas Soome 	char *name = "environment";
46622028508SToomas Soome 	vm_offset_t laddr;
46722028508SToomas Soome 
46822028508SToomas Soome 	/* We can't load first */
469ab4969f8SToomas Soome 	if (file_findfile(NULL, NULL) == NULL) {
47022028508SToomas Soome 		printf("Can not load environment module: %s\n",
47122028508SToomas Soome 		    "the kernel is not loaded");
47222028508SToomas Soome 		return;
47322028508SToomas Soome 	}
47422028508SToomas Soome 
475ab4969f8SToomas Soome 	if (file_findfile(name, name) != NULL) {
476ab4969f8SToomas Soome 		printf("warning: '%s' is already loaded\n", name);
477ab4969f8SToomas Soome 		return;
478ab4969f8SToomas Soome 	}
479ab4969f8SToomas Soome 
48022028508SToomas Soome 	tem_save_state();	/* Ask tem to save it's state in env. */
48122028508SToomas Soome 	size = env_get_size();
48222028508SToomas Soome 
48322028508SToomas Soome 	fp = file_alloc();
48422028508SToomas Soome 	if (fp != NULL) {
48522028508SToomas Soome 		fp->f_name = strdup(name);
48622028508SToomas Soome 		fp->f_type = strdup(name);
48722028508SToomas Soome 	}
48822028508SToomas Soome 
48922028508SToomas Soome 	if (fp == NULL || fp->f_name == NULL || fp->f_type == NULL) {
49022028508SToomas Soome 		printf("Can not load environment module: %s\n",
49122028508SToomas Soome 		    "out of memory");
49222028508SToomas Soome 		file_discard(fp);
49322028508SToomas Soome 		return;
49422028508SToomas Soome 	}
49522028508SToomas Soome 
49622028508SToomas Soome 
49722028508SToomas Soome 	if (archsw.arch_loadaddr != NULL)
49822028508SToomas Soome 		loadaddr = archsw.arch_loadaddr(LOAD_MEM, &size, loadaddr);
49922028508SToomas Soome 
50022028508SToomas Soome 	if (loadaddr == 0) {
50122028508SToomas Soome 		printf("Can not load environment module: %s\n",
50222028508SToomas Soome 		    "out of memory");
50322028508SToomas Soome 		file_discard(fp);
50422028508SToomas Soome 		return;
50522028508SToomas Soome 	}
50622028508SToomas Soome 
50722028508SToomas Soome 	laddr = bi_copyenv(loadaddr);
50822028508SToomas Soome 	/* Looks OK so far; populate control structure */
50922028508SToomas Soome 	module_hash(fp, PTOV(loadaddr), laddr - loadaddr);
51022028508SToomas Soome 	fp->f_loader = -1;
51122028508SToomas Soome 	fp->f_addr = loadaddr;
51222028508SToomas Soome 	fp->f_size = laddr - loadaddr;
51322028508SToomas Soome 
51422028508SToomas Soome 	/* recognise space consumption */
51522028508SToomas Soome 	loadaddr = laddr;
51622028508SToomas Soome 
51722028508SToomas Soome 	file_insert_tail(fp);
51822028508SToomas Soome }
51922028508SToomas Soome 
52022028508SToomas Soome void
build_font_module(void)52122028508SToomas Soome build_font_module(void)
52222028508SToomas Soome {
52322028508SToomas Soome 	bitmap_data_t *bd;
52422028508SToomas Soome 	struct font *fd;
52522028508SToomas Soome 	struct preloaded_file *fp;
52622028508SToomas Soome 	size_t size;
52722028508SToomas Soome 	uint32_t checksum;
52822028508SToomas Soome 	int i;
52922028508SToomas Soome 	char *name = "console-font";
53022028508SToomas Soome 	vm_offset_t laddr;
53122028508SToomas Soome 	struct font_info fi;
53222028508SToomas Soome 	struct fontlist *fl;
53322028508SToomas Soome 
53422028508SToomas Soome 	if (STAILQ_EMPTY(&fonts))
53522028508SToomas Soome 		return;
53622028508SToomas Soome 
53722028508SToomas Soome 	/* We can't load first */
538ab4969f8SToomas Soome 	if (file_findfile(NULL, NULL) == NULL) {
53922028508SToomas Soome 		printf("Can not load font module: %s\n",
54022028508SToomas Soome 		    "the kernel is not loaded");
54122028508SToomas Soome 		return;
54222028508SToomas Soome 	}
54322028508SToomas Soome 
544ab4969f8SToomas Soome 	if (file_findfile(name, name) != NULL) {
545ab4969f8SToomas Soome 		printf("warning: '%s' is already loaded\n", name);
546ab4969f8SToomas Soome 		return;
547ab4969f8SToomas Soome 	}
548ab4969f8SToomas Soome 
54922028508SToomas Soome 	/* helper pointers */
55022028508SToomas Soome 	bd = NULL;
55122028508SToomas Soome 	STAILQ_FOREACH(fl, &fonts, font_next) {
55222028508SToomas Soome 		if (tems.ts_font.vf_width == fl->font_data->width &&
55322028508SToomas Soome 		    tems.ts_font.vf_height == fl->font_data->height) {
55422028508SToomas Soome 			/*
55522028508SToomas Soome 			 * Kernel does have better built in font.
55622028508SToomas Soome 			 */
55722028508SToomas Soome 			if (fl->font_flags == FONT_BUILTIN)
55822028508SToomas Soome 				return;
55922028508SToomas Soome 
56022028508SToomas Soome 			bd = fl->font_data;
56122028508SToomas Soome 			break;
56222028508SToomas Soome 		}
56322028508SToomas Soome 	}
56422028508SToomas Soome 	if (bd == NULL)
56522028508SToomas Soome 		return;
56622028508SToomas Soome 	fd = bd->font;
56722028508SToomas Soome 
56822028508SToomas Soome 	fi.fi_width = fd->vf_width;
56922028508SToomas Soome 	checksum = fi.fi_width;
57022028508SToomas Soome 	fi.fi_height = fd->vf_height;
57122028508SToomas Soome 	checksum += fi.fi_height;
57222028508SToomas Soome 	fi.fi_bitmap_size = bd->uncompressed_size;
57322028508SToomas Soome 	checksum += fi.fi_bitmap_size;
57422028508SToomas Soome 
57522028508SToomas Soome 	size = roundup2(sizeof (struct font_info), 8);
57622028508SToomas Soome 	for (i = 0; i < VFNT_MAPS; i++) {
57722028508SToomas Soome 		fi.fi_map_count[i] = fd->vf_map_count[i];
57822028508SToomas Soome 		checksum += fi.fi_map_count[i];
57922028508SToomas Soome 		size += fd->vf_map_count[i] * sizeof (struct font_map);
58022028508SToomas Soome 		size += roundup2(size, 8);
58122028508SToomas Soome 	}
58222028508SToomas Soome 	size += bd->uncompressed_size;
58322028508SToomas Soome 
58422028508SToomas Soome 	fi.fi_checksum = -checksum;
58522028508SToomas Soome 
58622028508SToomas Soome 	fp = file_alloc();
58722028508SToomas Soome 	if (fp != NULL) {
58822028508SToomas Soome 		fp->f_name = strdup(name);
58922028508SToomas Soome 		fp->f_type = strdup(name);
59022028508SToomas Soome 	}
59122028508SToomas Soome 
59222028508SToomas Soome 	if (fp == NULL || fp->f_name == NULL || fp->f_type == NULL) {
59322028508SToomas Soome 		printf("Can not load font module: %s\n",
59422028508SToomas Soome 		    "out of memory");
59522028508SToomas Soome 		file_discard(fp);
59622028508SToomas Soome 		return;
59722028508SToomas Soome 	}
59822028508SToomas Soome 
59922028508SToomas Soome 	if (archsw.arch_loadaddr != NULL)
60022028508SToomas Soome 		loadaddr = archsw.arch_loadaddr(LOAD_MEM, &size, loadaddr);
60122028508SToomas Soome 
60222028508SToomas Soome 	if (loadaddr == 0) {
60322028508SToomas Soome 		printf("Can not load font module: %s\n",
60422028508SToomas Soome 		    "out of memory");
60522028508SToomas Soome 		file_discard(fp);
60622028508SToomas Soome 		return;
60722028508SToomas Soome 	}
60822028508SToomas Soome 
60922028508SToomas Soome 	laddr = loadaddr;
61022028508SToomas Soome 	laddr += archsw.arch_copyin(&fi, laddr, sizeof (struct font_info));
61122028508SToomas Soome 	laddr = roundup2(laddr, 8);
61222028508SToomas Soome 
61322028508SToomas Soome 	/* Copy maps. */
61422028508SToomas Soome 	for (i = 0; i < VFNT_MAPS; i++) {
61522028508SToomas Soome 		if (fd->vf_map_count[i] != 0) {
61622028508SToomas Soome 			laddr += archsw.arch_copyin(fd->vf_map[i], laddr,
61722028508SToomas Soome 			    fd->vf_map_count[i] * sizeof (struct font_map));
61822028508SToomas Soome 			laddr = roundup2(laddr, 8);
61922028508SToomas Soome 		}
62022028508SToomas Soome 	}
62122028508SToomas Soome 
62222028508SToomas Soome 	/* Copy the bitmap. */
62322028508SToomas Soome 	laddr += archsw.arch_copyin(fd->vf_bytes, laddr, fi.fi_bitmap_size);
62422028508SToomas Soome 
62522028508SToomas Soome 	/* Looks OK so far; populate control structure */
62622028508SToomas Soome 	module_hash(fp, PTOV(loadaddr), laddr - loadaddr);
62722028508SToomas Soome 	fp->f_loader = -1;
62822028508SToomas Soome 	fp->f_addr = loadaddr;
62922028508SToomas Soome 	fp->f_size = laddr - loadaddr;
63022028508SToomas Soome 
63122028508SToomas Soome 	/* recognise space consumption */
63222028508SToomas Soome 	loadaddr = laddr;
63322028508SToomas Soome 
63422028508SToomas Soome 	file_insert_tail(fp);
63522028508SToomas Soome }
63622028508SToomas Soome 
63722028508SToomas Soome /*
63822028508SToomas Soome  * We've been asked to load (fname) as (type), so just suck it in,
63922028508SToomas Soome  * no arguments or anything.
64022028508SToomas Soome  */
64122028508SToomas Soome struct preloaded_file *
file_loadraw(const char * fname,char * type,int argc,char ** argv,int insert)64222028508SToomas Soome file_loadraw(const char *fname, char *type, int argc, char **argv, int insert)
64322028508SToomas Soome {
64422028508SToomas Soome 	struct preloaded_file *fp;
64522028508SToomas Soome 	char *name;
646*9fbdb45aSToomas Soome 	int fd;
647*9fbdb45aSToomas Soome 	ssize_t got;
64822028508SToomas Soome 	struct stat st;
64922028508SToomas Soome 
65022028508SToomas Soome 	/* We can't load first */
65122028508SToomas Soome 	if ((file_findfile(NULL, NULL)) == NULL) {
65222028508SToomas Soome 		command_errmsg = "can't load file before kernel";
65322028508SToomas Soome 		return (NULL);
65422028508SToomas Soome 	}
65522028508SToomas Soome 
65622028508SToomas Soome 	/* locate the file on the load path */
65722028508SToomas Soome 	name = file_search(fname, NULL);
65822028508SToomas Soome 	if (name == NULL) {
659fc39bce2SToomas Soome 		(void) snprintf(command_errbuf, sizeof (command_errbuf),
66022028508SToomas Soome 		    "can't find '%s'", fname);
66122028508SToomas Soome 		return (NULL);
66222028508SToomas Soome 	}
66322028508SToomas Soome 
66422028508SToomas Soome 	if ((fd = open(name, O_RDONLY)) < 0) {
665fc39bce2SToomas Soome 		(void) snprintf(command_errbuf, sizeof (command_errbuf),
66622028508SToomas Soome 		    "can't open '%s': %s", name, strerror(errno));
66722028508SToomas Soome 		free(name);
66822028508SToomas Soome 		return (NULL);
66922028508SToomas Soome 	}
67022028508SToomas Soome 	if (fstat(fd, &st) < 0) {
671fc39bce2SToomas Soome 		(void) close(fd);
672fc39bce2SToomas Soome 		(void) snprintf(command_errbuf, sizeof (command_errbuf),
67322028508SToomas Soome 		    "stat error '%s': %s", name, strerror(errno));
67422028508SToomas Soome 		free(name);
67522028508SToomas Soome 		return (NULL);
67622028508SToomas Soome 	}
67722028508SToomas Soome 
67822028508SToomas Soome 	if (archsw.arch_loadaddr != NULL)
67922028508SToomas Soome 		loadaddr = archsw.arch_loadaddr(LOAD_RAW, name, loadaddr);
68022028508SToomas Soome 	if (loadaddr == 0) {
681fc39bce2SToomas Soome 		(void) close(fd);
682fc39bce2SToomas Soome 		(void) snprintf(command_errbuf, sizeof (command_errbuf),
68322028508SToomas Soome 		    "no memory to load %s", name);
68422028508SToomas Soome 		free(name);
68522028508SToomas Soome 		return (NULL);
68622028508SToomas Soome 	}
68722028508SToomas Soome 
688*9fbdb45aSToomas Soome 	got = archsw.arch_readin(fd, loadaddr, st.st_size);
689*9fbdb45aSToomas Soome 	if ((size_t)got != st.st_size) {
690fc39bce2SToomas Soome 		(void) snprintf(command_errbuf, sizeof (command_errbuf),
69122028508SToomas Soome 		    "error reading '%s': %s", name, strerror(errno));
69222028508SToomas Soome 		free(name);
693fc39bce2SToomas Soome 		(void) close(fd);
694*9fbdb45aSToomas Soome 		if (archsw.arch_free_loadaddr != NULL && st.st_size != 0) {
69522028508SToomas Soome 			archsw.arch_free_loadaddr(loadaddr,
696*9fbdb45aSToomas Soome 			    (uint64_t)(roundup2(st.st_size, PAGE_SIZE) >> 12));
69722028508SToomas Soome 		}
69822028508SToomas Soome 		return (NULL);
69922028508SToomas Soome 	}
70022028508SToomas Soome 
70122028508SToomas Soome 	/* Looks OK so far; create & populate control structure */
70222028508SToomas Soome 	fp = file_alloc();
70322028508SToomas Soome 	if (fp == NULL) {
70422028508SToomas Soome 		if (archsw.arch_free_loadaddr != NULL && st.st_size != 0)
70522028508SToomas Soome 			archsw.arch_free_loadaddr(loadaddr,
70622028508SToomas Soome 			    (uint64_t)(roundup2(st.st_size, PAGE_SIZE) >> 12));
707fc39bce2SToomas Soome 		(void) snprintf(command_errbuf, sizeof (command_errbuf),
70822028508SToomas Soome 		    "no memory to load %s", name);
70922028508SToomas Soome 		free(name);
710fc39bce2SToomas Soome 		(void) close(fd);
71122028508SToomas Soome 		return (NULL);
71222028508SToomas Soome 	}
71322028508SToomas Soome 
71422028508SToomas Soome 	fp->f_name = name;
71522028508SToomas Soome 	fp->f_args = unargv(argc, argv);
71622028508SToomas Soome 	fp->f_type = strdup(type);
71722028508SToomas Soome 	fp->f_metadata = NULL;
71822028508SToomas Soome 	fp->f_loader = -1;
71922028508SToomas Soome 	fp->f_addr = loadaddr;
720*9fbdb45aSToomas Soome 	fp->f_size = st.st_size;
72122028508SToomas Soome 
72222028508SToomas Soome 	if (fp->f_type == NULL ||
72322028508SToomas Soome 	    (argc != 0 && fp->f_args == NULL)) {
724fc39bce2SToomas Soome 		(void) close(fd);
725fc39bce2SToomas Soome 		(void) snprintf(command_errbuf, sizeof (command_errbuf),
72622028508SToomas Soome 		    "no memory to load %s", name);
72722028508SToomas Soome 		file_discard(fp);
72822028508SToomas Soome 		return (NULL);
72922028508SToomas Soome 	}
73022028508SToomas Soome 	/* recognise space consumption */
731*9fbdb45aSToomas Soome 	loadaddr += st.st_size;
73222028508SToomas Soome 
73322028508SToomas Soome 	/* Add to the list of loaded files */
73422028508SToomas Soome 	if (insert != 0)
73522028508SToomas Soome 		file_insert_tail(fp);
736fc39bce2SToomas Soome 	(void) close(fd);
73722028508SToomas Soome 	return (fp);
73822028508SToomas Soome }
73922028508SToomas Soome 
74022028508SToomas Soome /*
74122028508SToomas Soome  * Load the module (name), pass it (argc),(argv), add container file
74222028508SToomas Soome  * to the list of loaded files.
74322028508SToomas Soome  * If module is already loaded just assign new argc/argv.
74422028508SToomas Soome  */
74522028508SToomas Soome int
mod_load(char * modname,struct mod_depend * verinfo,int argc,char * argv[])74622028508SToomas Soome mod_load(char *modname, struct mod_depend *verinfo, int argc, char *argv[])
74722028508SToomas Soome {
74822028508SToomas Soome 	struct kernel_module *mp;
74922028508SToomas Soome 	int err;
75022028508SToomas Soome 	char *filename;
75122028508SToomas Soome 
75222028508SToomas Soome 	if (file_havepath(modname)) {
75322028508SToomas Soome 		printf("Warning: mod_load() called instead of mod_loadkld() "
75422028508SToomas Soome 		    "for module '%s'\n", modname);
75522028508SToomas Soome 		return (mod_loadkld(modname, argc, argv));
75622028508SToomas Soome 	}
75722028508SToomas Soome 	/* see if module is already loaded */
75822028508SToomas Soome 	mp = file_findmodule(NULL, modname, verinfo);
75922028508SToomas Soome 	if (mp != NULL) {
76022028508SToomas Soome 		free(mp->m_args);
76122028508SToomas Soome 		mp->m_args = unargv(argc, argv);
762fc39bce2SToomas Soome 		(void) snprintf(command_errbuf, sizeof (command_errbuf),
76322028508SToomas Soome 		    "warning: module '%s' already loaded", mp->m_name);
76422028508SToomas Soome 		return (0);
76522028508SToomas Soome 	}
76622028508SToomas Soome 	/* locate file with the module on the search path */
76722028508SToomas Soome 	filename = mod_searchmodule(modname, verinfo);
76822028508SToomas Soome 	if (filename == NULL) {
769fc39bce2SToomas Soome 		(void) snprintf(command_errbuf, sizeof (command_errbuf),
77022028508SToomas Soome 		    "can't find '%s'", modname);
77122028508SToomas Soome 		return (ENOENT);
77222028508SToomas Soome 	}
77322028508SToomas Soome 	err = mod_loadkld(filename, argc, argv);
77422028508SToomas Soome 	free(filename);
77522028508SToomas Soome 	return (err);
77622028508SToomas Soome }
77722028508SToomas Soome 
77822028508SToomas Soome /*
77922028508SToomas Soome  * Load specified KLD. If path is omitted, then try to locate it via
78022028508SToomas Soome  * search path.
78122028508SToomas Soome  */
78222028508SToomas Soome int
mod_loadkld(const char * kldname,int argc,char * argv[])78322028508SToomas Soome mod_loadkld(const char *kldname, int argc, char *argv[])
78422028508SToomas Soome {
78522028508SToomas Soome 	struct preloaded_file *fp;
78622028508SToomas Soome 	int err;
78722028508SToomas Soome 	char *filename;
78822028508SToomas Soome 	vm_offset_t loadaddr_saved;
78922028508SToomas Soome 
79022028508SToomas Soome 	/*
79122028508SToomas Soome 	 * Get fully qualified KLD name
79222028508SToomas Soome 	 */
79322028508SToomas Soome 	filename = file_search(kldname, kld_ext_list);
79422028508SToomas Soome 	if (filename == NULL) {
795fc39bce2SToomas Soome 		(void) snprintf(command_errbuf, sizeof (command_errbuf),
79622028508SToomas Soome 		    "can't find '%s'", kldname);
79722028508SToomas Soome 		return (ENOENT);
79822028508SToomas Soome 	}
79922028508SToomas Soome 	/*
80022028508SToomas Soome 	 * Check if KLD already loaded
80122028508SToomas Soome 	 */
80222028508SToomas Soome 	fp = file_findfile(filename, NULL);
80322028508SToomas Soome 	if (fp != NULL) {
804fc39bce2SToomas Soome 		(void) snprintf(command_errbuf, sizeof (command_errbuf),
80522028508SToomas Soome 		    "warning: KLD '%s' already loaded", filename);
80622028508SToomas Soome 		free(filename);
80722028508SToomas Soome 		return (0);
80822028508SToomas Soome 	}
80922028508SToomas Soome 
81022028508SToomas Soome 	do {
81122028508SToomas Soome 		err = file_load(filename, loadaddr, &fp);
81222028508SToomas Soome 		if (err)
81322028508SToomas Soome 			break;
81422028508SToomas Soome 		fp->f_args = unargv(argc, argv);
81522028508SToomas Soome 		loadaddr_saved = loadaddr;
81622028508SToomas Soome 		loadaddr = fp->f_addr + fp->f_size;
81722028508SToomas Soome 		file_insert_tail(fp);	/* Add to the list of loaded files */
81822028508SToomas Soome 		if (file_load_dependencies(fp) != 0) {
81922028508SToomas Soome 			err = ENOENT;
82022028508SToomas Soome 			file_remove(fp);
82122028508SToomas Soome 			loadaddr = loadaddr_saved;
82222028508SToomas Soome 			fp = NULL;
82322028508SToomas Soome 			break;
82422028508SToomas Soome 		}
82522028508SToomas Soome 	} while (0);
82622028508SToomas Soome 	if (err == EFTYPE) {
827fc39bce2SToomas Soome 		(void) snprintf(command_errbuf, sizeof (command_errbuf),
82822028508SToomas Soome 		    "don't know how to load module '%s'", filename);
82922028508SToomas Soome 	}
83022028508SToomas Soome 	if (err)
83122028508SToomas Soome 		file_discard(fp);
83222028508SToomas Soome 	free(filename);
83322028508SToomas Soome 	return (err);
83422028508SToomas Soome }
83522028508SToomas Soome 
83622028508SToomas Soome /*
83722028508SToomas Soome  * Find a file matching (name) and (type).
83822028508SToomas Soome  * NULL may be passed as a wildcard to either.
83922028508SToomas Soome  */
84022028508SToomas Soome struct preloaded_file *
file_findfile(const char * name,const char * type)84122028508SToomas Soome file_findfile(const char *name, const char *type)
84222028508SToomas Soome {
84322028508SToomas Soome 	struct preloaded_file *fp;
84422028508SToomas Soome 
84522028508SToomas Soome 	for (fp = preloaded_files; fp != NULL; fp = fp->f_next) {
84622028508SToomas Soome 		if (((name == NULL) || strcmp(name, fp->f_name) == 0) &&
84722028508SToomas Soome 		    ((type == NULL) || strcmp(type, fp->f_type) == 0))
84822028508SToomas Soome 			break;
84922028508SToomas Soome 	}
85022028508SToomas Soome 	return (fp);
85122028508SToomas Soome }
85222028508SToomas Soome 
85322028508SToomas Soome /*
85422028508SToomas Soome  * Find a module matching (name) inside of given file.
85522028508SToomas Soome  * NULL may be passed as a wildcard.
85622028508SToomas Soome  */
85722028508SToomas Soome struct kernel_module *
file_findmodule(struct preloaded_file * fp,char * modname,struct mod_depend * verinfo)85822028508SToomas Soome file_findmodule(struct preloaded_file *fp, char *modname,
85922028508SToomas Soome     struct mod_depend *verinfo)
86022028508SToomas Soome {
86122028508SToomas Soome 	struct kernel_module *mp, *best;
86222028508SToomas Soome 	int bestver, mver;
86322028508SToomas Soome 
86422028508SToomas Soome 	if (fp == NULL) {
86522028508SToomas Soome 		for (fp = preloaded_files; fp; fp = fp->f_next) {
86622028508SToomas Soome 			mp = file_findmodule(fp, modname, verinfo);
86722028508SToomas Soome 			if (mp != NULL)
86822028508SToomas Soome 				return (mp);
86922028508SToomas Soome 		}
87022028508SToomas Soome 		return (NULL);
87122028508SToomas Soome 	}
87222028508SToomas Soome 	best = NULL;
87322028508SToomas Soome 	bestver = 0;
87422028508SToomas Soome 	for (mp = fp->f_modules; mp; mp = mp->m_next) {
87522028508SToomas Soome 		if (strcmp(modname, mp->m_name) == 0) {
87622028508SToomas Soome 			if (verinfo == NULL)
87722028508SToomas Soome 				return (mp);
87822028508SToomas Soome 			mver = mp->m_version;
87922028508SToomas Soome 			if (mver == verinfo->md_ver_preferred)
88022028508SToomas Soome 				return (mp);
88122028508SToomas Soome 			if (mver >= verinfo->md_ver_minimum &&
88222028508SToomas Soome 			    mver <= verinfo->md_ver_maximum &&
88322028508SToomas Soome 			    mver > bestver) {
88422028508SToomas Soome 				best = mp;
88522028508SToomas Soome 				bestver = mver;
88622028508SToomas Soome 			}
88722028508SToomas Soome 		}
88822028508SToomas Soome 	}
88922028508SToomas Soome 	return (best);
89022028508SToomas Soome }
89122028508SToomas Soome /*
89222028508SToomas Soome  * Make a copy of (size) bytes of data from (p), and associate them as
89322028508SToomas Soome  * metadata of (type) to the module (mp).
89422028508SToomas Soome  */
89522028508SToomas Soome void
file_addmetadata(struct preloaded_file * fp,int type,size_t size,void * p)89622028508SToomas Soome file_addmetadata(struct preloaded_file *fp, int type, size_t size, void *p)
89722028508SToomas Soome {
89822028508SToomas Soome 	struct file_metadata	*md;
89922028508SToomas Soome 
90022028508SToomas Soome 	md = malloc(sizeof (struct file_metadata) - sizeof (md->md_data) +
90122028508SToomas Soome 	    size);
90222028508SToomas Soome 	if (md != NULL) {
90322028508SToomas Soome 		md->md_size = size;
90422028508SToomas Soome 		md->md_type = type;
90522028508SToomas Soome 		bcopy(p, md->md_data, size);
90622028508SToomas Soome 		md->md_next = fp->f_metadata;
90722028508SToomas Soome 	}
90822028508SToomas Soome 	fp->f_metadata = md;
90922028508SToomas Soome }
91022028508SToomas Soome 
91122028508SToomas Soome /*
91222028508SToomas Soome  * Find a metadata object of (type) associated with the file (fp)
91322028508SToomas Soome  */
91422028508SToomas Soome struct file_metadata *
file_findmetadata(struct preloaded_file * fp,int type)91522028508SToomas Soome file_findmetadata(struct preloaded_file *fp, int type)
91622028508SToomas Soome {
91722028508SToomas Soome 	struct file_metadata *md;
91822028508SToomas Soome 
91922028508SToomas Soome 	for (md = fp->f_metadata; md != NULL; md = md->md_next)
92022028508SToomas Soome 		if (md->md_type == type)
92122028508SToomas Soome 			break;
92222028508SToomas Soome 	return (md);
92322028508SToomas Soome }
92422028508SToomas Soome 
92522028508SToomas Soome struct file_metadata *
metadata_next(struct file_metadata * md,int type)92622028508SToomas Soome metadata_next(struct file_metadata *md, int type)
92722028508SToomas Soome {
92822028508SToomas Soome 
92922028508SToomas Soome 	if (md == NULL)
93022028508SToomas Soome 		return (NULL);
93122028508SToomas Soome 	while ((md = md->md_next) != NULL)
93222028508SToomas Soome 		if (md->md_type == type)
93322028508SToomas Soome 			break;
93422028508SToomas Soome 	return (md);
93522028508SToomas Soome }
93622028508SToomas Soome 
93722028508SToomas Soome static const char *emptyextlist[] = { "", NULL };
93822028508SToomas Soome 
93922028508SToomas Soome /*
94022028508SToomas Soome  * Check if the given file is in place and return full path to it.
94122028508SToomas Soome  */
94222028508SToomas Soome static char *
file_lookup(const char * path,const char * name,int namelen,const char ** extlist)94322028508SToomas Soome file_lookup(const char *path, const char *name, int namelen,
94422028508SToomas Soome     const char **extlist)
94522028508SToomas Soome {
94622028508SToomas Soome 	struct stat st;
94722028508SToomas Soome 	char *result, *cp;
94822028508SToomas Soome 	const char **cpp;
94922028508SToomas Soome 	int pathlen, extlen, len;
95022028508SToomas Soome 
95122028508SToomas Soome 	pathlen = strlen(path);
95222028508SToomas Soome 	extlen = 0;
95322028508SToomas Soome 	if (extlist == NULL)
95422028508SToomas Soome 		extlist = emptyextlist;
95522028508SToomas Soome 	for (cpp = extlist; *cpp; cpp++) {
95622028508SToomas Soome 		len = strlen(*cpp);
95722028508SToomas Soome 		if (len > extlen)
95822028508SToomas Soome 			extlen = len;
95922028508SToomas Soome 	}
96022028508SToomas Soome 	result = malloc(pathlen + namelen + extlen + 2);
96122028508SToomas Soome 	if (result == NULL)
96222028508SToomas Soome 		return (NULL);
96322028508SToomas Soome 	bcopy(path, result, pathlen);
96422028508SToomas Soome 	if (pathlen > 0 && result[pathlen - 1] != '/')
96522028508SToomas Soome 		result[pathlen++] = '/';
96622028508SToomas Soome 	cp = result + pathlen;
96722028508SToomas Soome 	bcopy(name, cp, namelen);
96822028508SToomas Soome 	cp += namelen;
96922028508SToomas Soome 	for (cpp = extlist; *cpp; cpp++) {
970fc39bce2SToomas Soome 		(void) strcpy(cp, *cpp);
97122028508SToomas Soome 		if (stat(result, &st) == 0 && S_ISREG(st.st_mode))
97222028508SToomas Soome 			return (result);
97322028508SToomas Soome 	}
97422028508SToomas Soome 	free(result);
97522028508SToomas Soome 	return (NULL);
97622028508SToomas Soome }
97722028508SToomas Soome 
97822028508SToomas Soome /*
97922028508SToomas Soome  * Check if file name have any qualifiers
98022028508SToomas Soome  */
98122028508SToomas Soome static int
file_havepath(const char * name)98222028508SToomas Soome file_havepath(const char *name)
98322028508SToomas Soome {
98422028508SToomas Soome 	const char *cp;
98522028508SToomas Soome 
986fc39bce2SToomas Soome 	(void) archsw.arch_getdev(NULL, name, &cp);
98722028508SToomas Soome 	return (cp != name || strchr(name, '/') != NULL);
98822028508SToomas Soome }
98922028508SToomas Soome 
99022028508SToomas Soome /*
99122028508SToomas Soome  * Attempt to find the file (name) on the module searchpath.
99222028508SToomas Soome  * If (name) is qualified in any way, we simply check it and
99322028508SToomas Soome  * return it or NULL.  If it is not qualified, then we attempt
99422028508SToomas Soome  * to construct a path using entries in the environment variable
99522028508SToomas Soome  * module_path.
99622028508SToomas Soome  *
99722028508SToomas Soome  * The path we return a pointer to need never be freed, as we manage
99822028508SToomas Soome  * it internally.
99922028508SToomas Soome  */
100022028508SToomas Soome static char *
file_search(const char * name,const char ** extlist)100122028508SToomas Soome file_search(const char *name, const char **extlist)
100222028508SToomas Soome {
100322028508SToomas Soome 	struct moduledir *mdp;
100422028508SToomas Soome 	struct stat sb;
100522028508SToomas Soome 	char *result;
100622028508SToomas Soome 	int namelen;
100722028508SToomas Soome 
100822028508SToomas Soome 	/* Don't look for nothing */
100922028508SToomas Soome 	if (name == NULL)
101022028508SToomas Soome 		return (NULL);
101122028508SToomas Soome 
101222028508SToomas Soome 	if (*name == '\0')
101322028508SToomas Soome 		return (strdup(name));
101422028508SToomas Soome 
101522028508SToomas Soome 	if (file_havepath(name)) {
101622028508SToomas Soome 		/* Qualified, so just see if it exists */
101722028508SToomas Soome 		if (stat(name, &sb) == 0)
101822028508SToomas Soome 			return (strdup(name));
101922028508SToomas Soome 		return (NULL);
102022028508SToomas Soome 	}
102122028508SToomas Soome 	moduledir_rebuild();
102222028508SToomas Soome 	result = NULL;
102322028508SToomas Soome 	namelen = strlen(name);
102422028508SToomas Soome 	STAILQ_FOREACH(mdp, &moduledir_list, d_link) {
102522028508SToomas Soome 		result = file_lookup(mdp->d_path, name, namelen, extlist);
102622028508SToomas Soome 		if (result != NULL)
102722028508SToomas Soome 			break;
102822028508SToomas Soome 	}
102922028508SToomas Soome 	return (result);
103022028508SToomas Soome }
103122028508SToomas Soome 
103222028508SToomas Soome #define	INT_ALIGN(base, ptr)	ptr = \
103322028508SToomas Soome 	(base) + (((ptr) - (base) + sizeof (int) - 1) & ~(sizeof (int) - 1))
103422028508SToomas Soome 
103522028508SToomas Soome static char *
mod_search_hints(struct moduledir * mdp,const char * modname,struct mod_depend * verinfo)103622028508SToomas Soome mod_search_hints(struct moduledir *mdp, const char *modname,
103722028508SToomas Soome     struct mod_depend *verinfo)
103822028508SToomas Soome {
103922028508SToomas Soome 	uchar_t *cp, *recptr, *bufend, *best;
104022028508SToomas Soome 	char *result;
104122028508SToomas Soome 	int *intp, bestver, blen, clen, ival, modnamelen, reclen;
104222028508SToomas Soome 	bool found;
104322028508SToomas Soome 
104422028508SToomas Soome 	moduledir_readhints(mdp);
104522028508SToomas Soome 	modnamelen = strlen(modname);
104622028508SToomas Soome 	found = false;
104722028508SToomas Soome 	result = NULL;
104822028508SToomas Soome 	bestver = 0;
104922028508SToomas Soome 	if (mdp->d_hints == NULL)
105022028508SToomas Soome 		goto bad;
105122028508SToomas Soome 	recptr = mdp->d_hints;
105222028508SToomas Soome 	bufend = recptr + mdp->d_hintsz;
105322028508SToomas Soome 	clen = blen = 0;
105422028508SToomas Soome 	best = cp = NULL;
105522028508SToomas Soome 	while (recptr < bufend && !found) {
105622028508SToomas Soome 		intp = (int *)recptr;
105722028508SToomas Soome 		reclen = *intp++;
105822028508SToomas Soome 		ival = *intp++;
105922028508SToomas Soome 		cp = (uchar_t *)intp;
106022028508SToomas Soome 		switch (ival) {
106122028508SToomas Soome 		case MDT_VERSION:
106222028508SToomas Soome 			clen = *cp++;
106322028508SToomas Soome 			if (clen != modnamelen || bcmp(cp, modname, clen) != 0)
106422028508SToomas Soome 				break;
106522028508SToomas Soome 			cp += clen;
106622028508SToomas Soome 			INT_ALIGN(mdp->d_hints, cp);
106722028508SToomas Soome 			ival = *(int *)cp;
106822028508SToomas Soome 			cp += sizeof (int);
106922028508SToomas Soome 			clen = *cp++;
107022028508SToomas Soome 			if (verinfo == NULL ||
107122028508SToomas Soome 			    ival == verinfo->md_ver_preferred) {
107222028508SToomas Soome 				found = true;
107322028508SToomas Soome 				break;
107422028508SToomas Soome 			}
107522028508SToomas Soome 			if (ival >= verinfo->md_ver_minimum &&
107622028508SToomas Soome 			    ival <= verinfo->md_ver_maximum &&
107722028508SToomas Soome 			    ival > bestver) {
107822028508SToomas Soome 				bestver = ival;
107922028508SToomas Soome 				best = cp;
108022028508SToomas Soome 				blen = clen;
108122028508SToomas Soome 			}
108222028508SToomas Soome 			break;
108322028508SToomas Soome 		default:
108422028508SToomas Soome 			break;
108522028508SToomas Soome 		}
108622028508SToomas Soome 		recptr += reclen + sizeof (int);
108722028508SToomas Soome 	}
108822028508SToomas Soome 	/*
108922028508SToomas Soome 	 * Finally check if KLD is in the place
109022028508SToomas Soome 	 */
109122028508SToomas Soome 	if (found)
109222028508SToomas Soome 		result = file_lookup(mdp->d_path, (char *)cp, clen, NULL);
109322028508SToomas Soome 	else if (best)
109422028508SToomas Soome 		result = file_lookup(mdp->d_path, (char *)best, blen, NULL);
109522028508SToomas Soome bad:
109622028508SToomas Soome 	/*
109722028508SToomas Soome 	 * If nothing found or hints is absent - fallback to the old way
109822028508SToomas Soome 	 * by using "kldname[.ko]" as module name.
109922028508SToomas Soome 	 */
110022028508SToomas Soome 	if (!found && bestver == 0 && result == NULL) {
110122028508SToomas Soome 		result = file_lookup(mdp->d_path, modname, modnamelen,
110222028508SToomas Soome 		    kld_ext_list);
110322028508SToomas Soome 	}
110422028508SToomas Soome 	return (result);
110522028508SToomas Soome }
110622028508SToomas Soome 
110722028508SToomas Soome /*
110822028508SToomas Soome  * Attempt to locate the file containing the module (name)
110922028508SToomas Soome  */
111022028508SToomas Soome static char *
mod_searchmodule(char * name,struct mod_depend * verinfo)111122028508SToomas Soome mod_searchmodule(char *name, struct mod_depend *verinfo)
111222028508SToomas Soome {
111322028508SToomas Soome 	struct moduledir *mdp;
111422028508SToomas Soome 	char *result;
111522028508SToomas Soome 
111622028508SToomas Soome 	moduledir_rebuild();
111722028508SToomas Soome 	/*
111822028508SToomas Soome 	 * Now we ready to lookup module in the given directories
111922028508SToomas Soome 	 */
112022028508SToomas Soome 	result = NULL;
112122028508SToomas Soome 	STAILQ_FOREACH(mdp, &moduledir_list, d_link) {
112222028508SToomas Soome 		result = mod_search_hints(mdp, name, verinfo);
112322028508SToomas Soome 		if (result != NULL)
112422028508SToomas Soome 			break;
112522028508SToomas Soome 	}
112622028508SToomas Soome 
112722028508SToomas Soome 	return (result);
112822028508SToomas Soome }
112922028508SToomas Soome 
113022028508SToomas Soome int
file_addmodule(struct preloaded_file * fp,char * modname,int version,struct kernel_module ** newmp)113122028508SToomas Soome file_addmodule(struct preloaded_file *fp, char *modname, int version,
113222028508SToomas Soome     struct kernel_module **newmp)
113322028508SToomas Soome {
113422028508SToomas Soome 	struct kernel_module *mp;
113522028508SToomas Soome 	struct mod_depend mdepend;
113622028508SToomas Soome 
113722028508SToomas Soome 	bzero(&mdepend, sizeof (mdepend));
113822028508SToomas Soome 	mdepend.md_ver_preferred = version;
113922028508SToomas Soome 	mp = file_findmodule(fp, modname, &mdepend);
114022028508SToomas Soome 	if (mp != NULL)
114122028508SToomas Soome 		return (EEXIST);
114222028508SToomas Soome 	mp = calloc(1, sizeof (struct kernel_module));
114322028508SToomas Soome 	if (mp == NULL)
114422028508SToomas Soome 		return (ENOMEM);
114522028508SToomas Soome 	mp->m_name = strdup(modname);
114622028508SToomas Soome 	if (mp->m_name == NULL) {
114722028508SToomas Soome 		free(mp);
114822028508SToomas Soome 		return (ENOMEM);
114922028508SToomas Soome 	}
115022028508SToomas Soome 	mp->m_version = version;
115122028508SToomas Soome 	mp->m_fp = fp;
115222028508SToomas Soome 	mp->m_next = fp->f_modules;
115322028508SToomas Soome 	fp->f_modules = mp;
115422028508SToomas Soome 	if (newmp)
115522028508SToomas Soome 		*newmp = mp;
115622028508SToomas Soome 	return (0);
115722028508SToomas Soome }
115822028508SToomas Soome 
115922028508SToomas Soome /*
116022028508SToomas Soome  * Throw a file away
116122028508SToomas Soome  */
116222028508SToomas Soome void
file_discard(struct preloaded_file * fp)116322028508SToomas Soome file_discard(struct preloaded_file *fp)
116422028508SToomas Soome {
116522028508SToomas Soome 	struct file_metadata *md, *md1;
116622028508SToomas Soome 	struct kernel_module *mp, *mp1;
116722028508SToomas Soome 
116822028508SToomas Soome 	if (fp == NULL)
116922028508SToomas Soome 		return;
117022028508SToomas Soome 
117122028508SToomas Soome 	if (archsw.arch_free_loadaddr != NULL && fp->f_addr &&
117222028508SToomas Soome 	    fp->f_size != 0) {
117322028508SToomas Soome 		archsw.arch_free_loadaddr(fp->f_addr,
117422028508SToomas Soome 		    (uint64_t)(roundup2(fp->f_size, PAGE_SIZE) >> 12));
117522028508SToomas Soome 	}
117622028508SToomas Soome 
117722028508SToomas Soome 	md = fp->f_metadata;
117822028508SToomas Soome 	while (md != NULL) {
117922028508SToomas Soome 		md1 = md;
118022028508SToomas Soome 		md = md->md_next;
118122028508SToomas Soome 		free(md1);
118222028508SToomas Soome 	}
118322028508SToomas Soome 	mp = fp->f_modules;
118422028508SToomas Soome 	while (mp != NULL) {
118522028508SToomas Soome 		free(mp->m_name);
118622028508SToomas Soome 		mp1 = mp;
118722028508SToomas Soome 		mp = mp->m_next;
118822028508SToomas Soome 		free(mp1);
118922028508SToomas Soome 	}
119022028508SToomas Soome 	free(fp->f_name);
119122028508SToomas Soome 	free(fp->f_type);
119222028508SToomas Soome 	free(fp->f_args);
119322028508SToomas Soome 	free(fp);
119422028508SToomas Soome }
119522028508SToomas Soome 
119622028508SToomas Soome /*
119722028508SToomas Soome  * Allocate a new file; must be used instead of malloc()
119822028508SToomas Soome  * to ensure safe initialisation.
119922028508SToomas Soome  */
120022028508SToomas Soome struct preloaded_file *
file_alloc(void)120122028508SToomas Soome file_alloc(void)
120222028508SToomas Soome {
120322028508SToomas Soome 
120422028508SToomas Soome 	return (calloc(1, sizeof (struct preloaded_file)));
120522028508SToomas Soome }
120622028508SToomas Soome 
120722028508SToomas Soome /*
120822028508SToomas Soome  * Add a module to the chain
120922028508SToomas Soome  */
121022028508SToomas Soome static void
file_insert_tail(struct preloaded_file * fp)121122028508SToomas Soome file_insert_tail(struct preloaded_file *fp)
121222028508SToomas Soome {
121322028508SToomas Soome 	struct preloaded_file *cm;
121422028508SToomas Soome 
121522028508SToomas Soome 	/* Append to list of loaded file */
121622028508SToomas Soome 	fp->f_next = NULL;
121722028508SToomas Soome 	if (preloaded_files == NULL) {
121822028508SToomas Soome 		preloaded_files = fp;
121922028508SToomas Soome 	} else {
122022028508SToomas Soome 		for (cm = preloaded_files; cm->f_next != NULL; cm = cm->f_next)
122122028508SToomas Soome 			;
122222028508SToomas Soome 		cm->f_next = fp;
122322028508SToomas Soome 	}
122422028508SToomas Soome }
122522028508SToomas Soome 
122622028508SToomas Soome /*
122722028508SToomas Soome  * Remove module from the chain
122822028508SToomas Soome  */
122922028508SToomas Soome static void
file_remove(struct preloaded_file * fp)123022028508SToomas Soome file_remove(struct preloaded_file *fp)
123122028508SToomas Soome {
123222028508SToomas Soome 	struct preloaded_file   *cm;
123322028508SToomas Soome 
123422028508SToomas Soome 	if (preloaded_files == NULL)
123522028508SToomas Soome 		return;
123622028508SToomas Soome 
123722028508SToomas Soome 	if (preloaded_files == fp) {
123822028508SToomas Soome 		preloaded_files = fp->f_next;
123922028508SToomas Soome 		return;
124022028508SToomas Soome 	}
124122028508SToomas Soome 	for (cm = preloaded_files; cm->f_next != NULL; cm = cm->f_next) {
124222028508SToomas Soome 		if (cm->f_next == fp) {
124322028508SToomas Soome 			cm->f_next = fp->f_next;
124422028508SToomas Soome 			return;
124522028508SToomas Soome 		}
124622028508SToomas Soome 	}
124722028508SToomas Soome }
124822028508SToomas Soome 
124922028508SToomas Soome static char *
moduledir_fullpath(struct moduledir * mdp,const char * fname)125022028508SToomas Soome moduledir_fullpath(struct moduledir *mdp, const char *fname)
125122028508SToomas Soome {
125222028508SToomas Soome 	char *cp;
125322028508SToomas Soome 
125422028508SToomas Soome 	cp = malloc(strlen(mdp->d_path) + strlen(fname) + 2);
125522028508SToomas Soome 	if (cp == NULL)
125622028508SToomas Soome 		return (NULL);
125722028508SToomas Soome 	strcpy(cp, mdp->d_path);
125822028508SToomas Soome 	strcat(cp, "/");
125922028508SToomas Soome 	strcat(cp, fname);
126022028508SToomas Soome 	return (cp);
126122028508SToomas Soome }
126222028508SToomas Soome 
126322028508SToomas Soome /*
126422028508SToomas Soome  * Read linker.hints file into memory performing some sanity checks.
126522028508SToomas Soome  */
126622028508SToomas Soome static void
moduledir_readhints(struct moduledir * mdp)126722028508SToomas Soome moduledir_readhints(struct moduledir *mdp)
126822028508SToomas Soome {
126922028508SToomas Soome 	struct stat st;
127022028508SToomas Soome 	char *path;
127122028508SToomas Soome 	int fd, size, version;
127222028508SToomas Soome 
127322028508SToomas Soome 	if (mdp->d_hints != NULL || (mdp->d_flags & MDIR_NOHINTS))
127422028508SToomas Soome 		return;
127522028508SToomas Soome 	path = moduledir_fullpath(mdp, "linker.hints");
127622028508SToomas Soome 	if (stat(path, &st) != 0 ||
127722028508SToomas Soome 	    st.st_size < (ssize_t)(sizeof (version) + sizeof (int)) ||
127822028508SToomas Soome 	    st.st_size > LINKER_HINTS_MAX ||
127922028508SToomas Soome 	    (fd = open(path, O_RDONLY)) < 0) {
128022028508SToomas Soome 		free(path);
128122028508SToomas Soome 		mdp->d_flags |= MDIR_NOHINTS;
128222028508SToomas Soome 		return;
128322028508SToomas Soome 	}
128422028508SToomas Soome 	free(path);
128522028508SToomas Soome 	size = read(fd, &version, sizeof (version));
128622028508SToomas Soome 	if (size != sizeof (version) || version != LINKER_HINTS_VERSION)
128722028508SToomas Soome 		goto bad;
128822028508SToomas Soome 	size = st.st_size - size;
128922028508SToomas Soome 	mdp->d_hints = malloc(size);
129022028508SToomas Soome 	if (mdp->d_hints == NULL)
129122028508SToomas Soome 		goto bad;
129222028508SToomas Soome 	if (read(fd, mdp->d_hints, size) != size)
129322028508SToomas Soome 		goto bad;
129422028508SToomas Soome 	mdp->d_hintsz = size;
1295fc39bce2SToomas Soome 	(void) close(fd);
129622028508SToomas Soome 	return;
129722028508SToomas Soome bad:
1298fc39bce2SToomas Soome 	(void) close(fd);
129922028508SToomas Soome 	free(mdp->d_hints);
130022028508SToomas Soome 	mdp->d_hints = NULL;
130122028508SToomas Soome 	mdp->d_flags |= MDIR_NOHINTS;
130222028508SToomas Soome }
130322028508SToomas Soome 
130422028508SToomas Soome /*
130522028508SToomas Soome  * Extract directories from the ';' separated list, remove duplicates.
130622028508SToomas Soome  */
130722028508SToomas Soome static void
moduledir_rebuild(void)130822028508SToomas Soome moduledir_rebuild(void)
130922028508SToomas Soome {
131022028508SToomas Soome 	struct moduledir *mdp, *mtmp;
131122028508SToomas Soome 	const char *path, *cp, *ep;
131222028508SToomas Soome 	size_t cplen;
131322028508SToomas Soome 
131422028508SToomas Soome 	path = getenv("module_path");
131522028508SToomas Soome 	if (path == NULL)
131622028508SToomas Soome 		path = default_searchpath;
131722028508SToomas Soome 	/*
131822028508SToomas Soome 	 * Rebuild list of module directories if it changed
131922028508SToomas Soome 	 */
132022028508SToomas Soome 	STAILQ_FOREACH(mdp, &moduledir_list, d_link)
132122028508SToomas Soome 		mdp->d_flags |= MDIR_REMOVED;
132222028508SToomas Soome 
132322028508SToomas Soome 	for (ep = path; *ep != 0; ep++) {
132422028508SToomas Soome 		cp = ep;
132522028508SToomas Soome 		for (; *ep != 0 && *ep != ';'; ep++)
132622028508SToomas Soome 			;
132722028508SToomas Soome 		/*
132822028508SToomas Soome 		 * Ignore trailing slashes
132922028508SToomas Soome 		 */
133022028508SToomas Soome 		for (cplen = ep - cp; cplen > 1 && cp[cplen - 1] == '/';
133122028508SToomas Soome 		    cplen--)
133222028508SToomas Soome 			;
133322028508SToomas Soome 		STAILQ_FOREACH(mdp, &moduledir_list, d_link) {
133422028508SToomas Soome 			if (strlen(mdp->d_path) != cplen ||
133522028508SToomas Soome 			    bcmp(cp, mdp->d_path, cplen) != 0)
133622028508SToomas Soome 				continue;
133722028508SToomas Soome 			mdp->d_flags &= ~MDIR_REMOVED;
133822028508SToomas Soome 			break;
133922028508SToomas Soome 		}
134022028508SToomas Soome 		if (mdp == NULL) {
134122028508SToomas Soome 			mdp = malloc(sizeof (*mdp) + cplen + 1);
134222028508SToomas Soome 			if (mdp == NULL)
134322028508SToomas Soome 				return;
134422028508SToomas Soome 			mdp->d_path = (char *)(mdp + 1);
134522028508SToomas Soome 			bcopy(cp, mdp->d_path, cplen);
134622028508SToomas Soome 			mdp->d_path[cplen] = 0;
134722028508SToomas Soome 			mdp->d_hints = NULL;
134822028508SToomas Soome 			mdp->d_flags = 0;
134922028508SToomas Soome 			STAILQ_INSERT_TAIL(&moduledir_list, mdp, d_link);
135022028508SToomas Soome 		}
135122028508SToomas Soome 		if (*ep == '\0')
135222028508SToomas Soome 			break;
135322028508SToomas Soome 	}
135422028508SToomas Soome 	/*
135522028508SToomas Soome 	 * Delete unused directories if any
135622028508SToomas Soome 	 */
135722028508SToomas Soome 	mdp = STAILQ_FIRST(&moduledir_list);
135822028508SToomas Soome 	while (mdp) {
135922028508SToomas Soome 		if ((mdp->d_flags & MDIR_REMOVED) == 0) {
136022028508SToomas Soome 			mdp = STAILQ_NEXT(mdp, d_link);
136122028508SToomas Soome 		} else {
136222028508SToomas Soome 			free(mdp->d_hints);
136322028508SToomas Soome 			mtmp = mdp;
136422028508SToomas Soome 			mdp = STAILQ_NEXT(mdp, d_link);
136522028508SToomas Soome 			STAILQ_REMOVE(&moduledir_list, mtmp, moduledir, d_link);
136622028508SToomas Soome 			free(mtmp);
136722028508SToomas Soome 		}
136822028508SToomas Soome 	}
136922028508SToomas Soome }
1370