xref: /illumos-gate/usr/src/boot/common/module.c (revision 9fbdb45a)
19890ff83SToomas Soome /*
2199767f8SToomas Soome  * Copyright (c) 1998 Michael Smith <msmith@freebsd.org>
3199767f8SToomas Soome  * All rights reserved.
4199767f8SToomas Soome  *
5199767f8SToomas Soome  * Redistribution and use in source and binary forms, with or without
6199767f8SToomas Soome  * modification, are permitted provided that the following conditions
7199767f8SToomas Soome  * are met:
8199767f8SToomas Soome  * 1. Redistributions of source code must retain the above copyright
9199767f8SToomas Soome  *    notice, this list of conditions and the following disclaimer.
10199767f8SToomas Soome  * 2. Redistributions in binary form must reproduce the above copyright
11199767f8SToomas Soome  *    notice, this list of conditions and the following disclaimer in the
12199767f8SToomas Soome  *    documentation and/or other materials provided with the distribution.
13199767f8SToomas Soome  *
14199767f8SToomas Soome  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15199767f8SToomas Soome  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16199767f8SToomas Soome  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17199767f8SToomas Soome  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18199767f8SToomas Soome  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19199767f8SToomas Soome  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20199767f8SToomas Soome  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21199767f8SToomas Soome  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22199767f8SToomas Soome  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23199767f8SToomas Soome  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24199767f8SToomas Soome  * SUCH DAMAGE.
25199767f8SToomas Soome  */
26199767f8SToomas Soome 
27199767f8SToomas Soome #include <sys/cdefs.h>
28199767f8SToomas Soome 
29199767f8SToomas Soome /*
30199767f8SToomas Soome  * file/module function dispatcher, support, etc.
31199767f8SToomas Soome  */
32199767f8SToomas Soome 
33199767f8SToomas Soome #include <stand.h>
34199767f8SToomas Soome #include <string.h>
35199767f8SToomas Soome #include <sys/param.h>
36199767f8SToomas Soome #include <sys/linker.h>
37199767f8SToomas Soome #include <sys/module.h>
38199767f8SToomas Soome #include <sys/queue.h>
39199767f8SToomas Soome #include <sys/stdint.h>
409890ff83SToomas Soome #include <sys/tem_impl.h>
419890ff83SToomas Soome #include <sys/font.h>
42a103f15bSToomas Soome #include <sys/sha1.h>
4306469a5cSToomas Soome #include <libcrypto.h>
44199767f8SToomas Soome 
45199767f8SToomas Soome #include "bootstrap.h"
46199767f8SToomas Soome 
47a103f15bSToomas Soome #if defined(EFI)
48a103f15bSToomas Soome #define	PTOV(pa)	((void *)pa)
49a103f15bSToomas Soome #else
50a103f15bSToomas Soome #include "../i386/btx/lib/btxv86.h"
51a103f15bSToomas Soome #endif
52a103f15bSToomas Soome 
53199767f8SToomas Soome #define	MDIR_REMOVED	0x0001
54199767f8SToomas Soome #define	MDIR_NOHINTS	0x0002
55199767f8SToomas Soome 
56199767f8SToomas Soome struct moduledir {
57199767f8SToomas Soome 	char	*d_path;	/* path of modules directory */
580eb1a43fSToomas Soome 	uchar_t	*d_hints;	/* content of linker.hints file */
59199767f8SToomas Soome 	int	d_hintsz;	/* size of hints data */
60199767f8SToomas Soome 	int	d_flags;
61199767f8SToomas Soome 	STAILQ_ENTRY(moduledir) d_link;
62199767f8SToomas Soome };
63199767f8SToomas Soome 
640eb1a43fSToomas Soome static int file_load(char *, vm_offset_t, struct preloaded_file **);
650eb1a43fSToomas Soome static int file_load_dependencies(struct preloaded_file *);
660eb1a43fSToomas Soome static char *file_search(const char *, const char **);
670eb1a43fSToomas Soome static struct kernel_module *file_findmodule(struct preloaded_file *, char *,
680eb1a43fSToomas Soome     struct mod_depend *);
690eb1a43fSToomas Soome static int file_havepath(const char *);
700eb1a43fSToomas Soome static char *mod_searchmodule(char *, struct mod_depend *);
710eb1a43fSToomas Soome static void file_insert_tail(struct preloaded_file *);
72a8412dc1SToomas Soome static void file_remove(struct preloaded_file *);
730eb1a43fSToomas Soome struct file_metadata *metadata_next(struct file_metadata *, int);
740eb1a43fSToomas Soome static void moduledir_readhints(struct moduledir *);
750eb1a43fSToomas Soome static void moduledir_rebuild(void);
76199767f8SToomas Soome 
77199767f8SToomas Soome /* load address should be tweaked by first module loaded (kernel) */
780eb1a43fSToomas Soome static vm_offset_t loadaddr = 0;
79199767f8SToomas Soome 
80199767f8SToomas Soome #if defined(LOADER_FDT_SUPPORT)
810eb1a43fSToomas Soome static const char *default_searchpath = "/boot/kernel;/boot/modules;/boot/dtb";
82199767f8SToomas Soome #else
830eb1a43fSToomas Soome static const char *default_searchpath = "/platform/i86pc";
84199767f8SToomas Soome #endif
85199767f8SToomas Soome 
860eb1a43fSToomas Soome static STAILQ_HEAD(, moduledir) moduledir_list =
870eb1a43fSToomas Soome     STAILQ_HEAD_INITIALIZER(moduledir_list);
88199767f8SToomas Soome 
89199767f8SToomas Soome struct preloaded_file *preloaded_files = NULL;
90199767f8SToomas Soome 
91199767f8SToomas Soome static const char *kld_ext_list[] = {
920eb1a43fSToomas Soome 	".ko",
930eb1a43fSToomas Soome 	"",
940eb1a43fSToomas Soome 	".debug",
950eb1a43fSToomas Soome 	NULL
96199767f8SToomas Soome };
97199767f8SToomas Soome 
98199767f8SToomas Soome 
99199767f8SToomas Soome /*
100199767f8SToomas Soome  * load an object, either a disk file or code module.
101199767f8SToomas Soome  *
102199767f8SToomas Soome  * To load a file, the syntax is:
103199767f8SToomas Soome  *
104199767f8SToomas Soome  * load -t <type> <path>
105199767f8SToomas Soome  *
106199767f8SToomas Soome  * code modules are loaded as:
107199767f8SToomas Soome  *
108199767f8SToomas Soome  * load <path> <options>
109199767f8SToomas Soome  */
110199767f8SToomas Soome 
111199767f8SToomas Soome COMMAND_SET(load, "load", "load a kernel or module", command_load);
112199767f8SToomas Soome 
113199767f8SToomas Soome static int
command_load(int argc,char * argv[])114199767f8SToomas Soome command_load(int argc, char *argv[])
115199767f8SToomas Soome {
1160eb1a43fSToomas Soome 	char *typestr;
1170eb1a43fSToomas Soome 	int dofile, dokld, ch, error;
1180eb1a43fSToomas Soome 
1190eb1a43fSToomas Soome 	dokld = dofile = 0;
1200eb1a43fSToomas Soome 	optind = 1;
1210eb1a43fSToomas Soome 	optreset = 1;
1220eb1a43fSToomas Soome 	typestr = NULL;
1230eb1a43fSToomas Soome 	if (argc == 1) {
1240eb1a43fSToomas Soome 		command_errmsg = "no filename specified";
1250eb1a43fSToomas Soome 		return (CMD_CRIT);
126199767f8SToomas Soome 	}
1270eb1a43fSToomas Soome 	while ((ch = getopt(argc, argv, "kt:")) != -1) {
1280eb1a43fSToomas Soome 		switch (ch) {
1290eb1a43fSToomas Soome 		case 'k':
1300eb1a43fSToomas Soome 			dokld = 1;
1310eb1a43fSToomas Soome 			break;
1320eb1a43fSToomas Soome 		case 't':
1330eb1a43fSToomas Soome 			typestr = optarg;
1340eb1a43fSToomas Soome 			dofile = 1;
1350eb1a43fSToomas Soome 			break;
1360eb1a43fSToomas Soome 		case '?':
1370eb1a43fSToomas Soome 		default:
1380eb1a43fSToomas Soome 			/* getopt has already reported an error */
1390eb1a43fSToomas Soome 			return (CMD_OK);
1400eb1a43fSToomas Soome 		}
141199767f8SToomas Soome 	}
1420eb1a43fSToomas Soome 	argv += (optind - 1);
1430eb1a43fSToomas Soome 	argc -= (optind - 1);
144199767f8SToomas Soome 
1450eb1a43fSToomas Soome 	printf("Loading %s...\n", argv[1]);
1460eb1a43fSToomas Soome 	/*
1470eb1a43fSToomas Soome 	 * Request to load a raw file?
1480eb1a43fSToomas Soome 	 */
1490eb1a43fSToomas Soome 	if (dofile) {
1500eb1a43fSToomas Soome 		struct preloaded_file *fp;
151199767f8SToomas Soome 
1520eb1a43fSToomas Soome 		if ((typestr == NULL) || (*typestr == 0)) {
1530eb1a43fSToomas Soome 			command_errmsg = "invalid load type";
1540eb1a43fSToomas Soome 			return (CMD_CRIT);
1550eb1a43fSToomas Soome 		}
156199767f8SToomas Soome 
1570eb1a43fSToomas Soome 		if (file_findfile(argv[1], typestr) != NULL) {
158fc39bce2SToomas Soome 			(void) snprintf(command_errbuf, sizeof (command_errbuf),
1590eb1a43fSToomas Soome 			    "warning: file '%s' already loaded", argv[1]);
1600eb1a43fSToomas Soome 			return (CMD_WARN);
1610eb1a43fSToomas Soome 		}
162199767f8SToomas Soome 
1630eb1a43fSToomas Soome 		fp = file_loadraw(argv[1], typestr, argc - 2, argv + 2, 1);
1640eb1a43fSToomas Soome 		if (fp != NULL)
1650eb1a43fSToomas Soome 			return (CMD_OK);
1669890ff83SToomas Soome 
1670eb1a43fSToomas Soome 		/* Failing to load mfs_root is never going to end well! */
1680eb1a43fSToomas Soome 		if (strcmp("mfs_root", typestr) == 0)
1690eb1a43fSToomas Soome 			return (CMD_FATAL);
170199767f8SToomas Soome 
1710eb1a43fSToomas Soome 		return (CMD_ERROR);
1720eb1a43fSToomas Soome 	}
1730eb1a43fSToomas Soome 	/*
1740eb1a43fSToomas Soome 	 * Do we have explicit KLD load ?
1750eb1a43fSToomas Soome 	 */
1760eb1a43fSToomas Soome 	if (dokld || file_havepath(argv[1])) {
1770eb1a43fSToomas Soome 		error = mod_loadkld(argv[1], argc - 2, argv + 2);
1780eb1a43fSToomas Soome 		if (error == EEXIST) {
179fc39bce2SToomas Soome 			(void) snprintf(command_errbuf, sizeof (command_errbuf),
1800eb1a43fSToomas Soome 			    "warning: KLD '%s' already loaded", argv[1]);
1810eb1a43fSToomas Soome 			return (CMD_WARN);
1820eb1a43fSToomas Soome 		}
183199767f8SToomas Soome 
1840eb1a43fSToomas Soome 		return (error == 0 ? CMD_OK : CMD_CRIT);
1850eb1a43fSToomas Soome 	}
1860eb1a43fSToomas Soome 	/*
1870eb1a43fSToomas Soome 	 * Looks like a request for a module.
1880eb1a43fSToomas Soome 	 */
1890eb1a43fSToomas Soome 	error = mod_load(argv[1], NULL, argc - 2, argv + 2);
1900eb1a43fSToomas Soome 	if (error == EEXIST) {
191fc39bce2SToomas Soome 		(void) snprintf(command_errbuf, sizeof (command_errbuf),
1920eb1a43fSToomas Soome 		    "warning: module '%s' already loaded", argv[1]);
1930eb1a43fSToomas Soome 		return (CMD_WARN);
194199767f8SToomas Soome 	}
1950eb1a43fSToomas Soome 
1960eb1a43fSToomas Soome 	return (error == 0 ? CMD_OK : CMD_CRIT);
197199767f8SToomas Soome }
198199767f8SToomas Soome 
199199767f8SToomas Soome void
unload(void)200199767f8SToomas Soome unload(void)
201199767f8SToomas Soome {
2020eb1a43fSToomas Soome 	struct preloaded_file *fp;
2030eb1a43fSToomas Soome 
2040eb1a43fSToomas Soome 	while (preloaded_files != NULL) {
2050eb1a43fSToomas Soome 		fp = preloaded_files;
2060eb1a43fSToomas Soome 		preloaded_files = preloaded_files->f_next;
2070eb1a43fSToomas Soome 		file_discard(fp);
2080eb1a43fSToomas Soome 	}
2090eb1a43fSToomas Soome 	loadaddr = 0;
210fc39bce2SToomas Soome 	(void) unsetenv("kernelname");
211199767f8SToomas Soome }
212199767f8SToomas Soome 
213199767f8SToomas Soome COMMAND_SET(unload, "unload", "unload all modules", command_unload);
214199767f8SToomas Soome 
215199767f8SToomas Soome static int
command_unload(int argc __unused,char * argv[]__unused)2160eb1a43fSToomas Soome command_unload(int argc __unused, char *argv[] __unused)
217199767f8SToomas Soome {
2180eb1a43fSToomas Soome 	unload();
2190eb1a43fSToomas Soome 	return (CMD_OK);
220199767f8SToomas Soome }
221199767f8SToomas Soome 
222199767f8SToomas Soome COMMAND_SET(lsmod, "lsmod", "list loaded modules", command_lsmod);
223199767f8SToomas Soome 
224199767f8SToomas Soome static int
command_lsmod(int argc,char * argv[])225199767f8SToomas Soome command_lsmod(int argc, char *argv[])
226199767f8SToomas Soome {
2270eb1a43fSToomas Soome 	struct preloaded_file *fp;
2280eb1a43fSToomas Soome 	struct kernel_module *mp;
2290eb1a43fSToomas Soome 	struct file_metadata *md;
2300eb1a43fSToomas Soome 	char lbuf[80];
2310eb1a43fSToomas Soome 	int ch, verbose, hash, ret = 0;
2320eb1a43fSToomas Soome 
2330eb1a43fSToomas Soome 	verbose = 0;
2340eb1a43fSToomas Soome 	hash = 0;
2350eb1a43fSToomas Soome 	optind = 1;
2360eb1a43fSToomas Soome 	optreset = 1;
2370eb1a43fSToomas Soome 	while ((ch = getopt(argc, argv, "vs")) != -1) {
2380eb1a43fSToomas Soome 		switch (ch) {
2390eb1a43fSToomas Soome 		case 'v':
2400eb1a43fSToomas Soome 			verbose = 1;
2410eb1a43fSToomas Soome 			break;
2420eb1a43fSToomas Soome 		case 's':
2430eb1a43fSToomas Soome 			hash = 1;
2440eb1a43fSToomas Soome 			break;
2450eb1a43fSToomas Soome 		case '?':
2460eb1a43fSToomas Soome 		default:
2470eb1a43fSToomas Soome 			/* getopt has already reported an error */
2480eb1a43fSToomas Soome 			return (CMD_OK);
2490eb1a43fSToomas Soome 		}
250199767f8SToomas Soome 	}
2510eb1a43fSToomas Soome 
2520eb1a43fSToomas Soome 	pager_open();
2530eb1a43fSToomas 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);
260a103f15bSToomas Soome 		if (pager_output(lbuf))
261a103f15bSToomas Soome 			break;
2620eb1a43fSToomas Soome 		if (fp->f_args != NULL) {
263fc39bce2SToomas Soome 			(void) pager_output("    args: ");
264fc39bce2SToomas Soome 			(void) pager_output(fp->f_args);
2650eb1a43fSToomas Soome 			if (pager_output("\n"))
2660eb1a43fSToomas Soome 				break;
2670eb1a43fSToomas 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);
2750eb1a43fSToomas Soome 				if (pager_output(lbuf))
2760eb1a43fSToomas Soome 					break;
2770eb1a43fSToomas Soome 			}
2780eb1a43fSToomas Soome 		}
279a103f15bSToomas Soome 
2800eb1a43fSToomas Soome 		if (hash == 1) {
2810eb1a43fSToomas Soome 			void *ptr = PTOV(fp->f_addr);
282a103f15bSToomas Soome 
283fc39bce2SToomas Soome 			(void) pager_output("  hash: ");
2840eb1a43fSToomas Soome 			sha1(ptr, fp->f_size, (uint8_t *)lbuf);
2850eb1a43fSToomas Soome 			for (int i = 0; i < SHA1_DIGEST_LENGTH; i++)
2860eb1a43fSToomas Soome 				printf("%02x", (int)(lbuf[i] & 0xff));
2870eb1a43fSToomas Soome 			if (pager_output("\n"))
2880eb1a43fSToomas Soome 				break;
2890eb1a43fSToomas Soome 		}
290a103f15bSToomas Soome 
2910eb1a43fSToomas Soome 		if (fp->f_modules) {
292fc39bce2SToomas Soome 			(void) pager_output("  modules: ");
2930eb1a43fSToomas 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);
2970eb1a43fSToomas Soome 			}
2980eb1a43fSToomas Soome 			if (pager_output("\n"))
2990eb1a43fSToomas Soome 				break;
3000eb1a43fSToomas Soome 		}
3010eb1a43fSToomas Soome 		if (verbose) {
3020eb1a43fSToomas Soome 			/*
3030eb1a43fSToomas Soome 			 * XXX could add some formatting smarts here to
3040eb1a43fSToomas Soome 			 * display some better
3050eb1a43fSToomas Soome 			 */
3060eb1a43fSToomas Soome 			for (md = fp->f_metadata; md != NULL;
3070eb1a43fSToomas Soome 			    md = md->md_next) {
308fc39bce2SToomas Soome 				(void) snprintf(lbuf, sizeof (lbuf),
309fc39bce2SToomas Soome 				    "      0x%04x, 0x%lx\n",
3100eb1a43fSToomas Soome 				    md->md_type, (long)md->md_size);
3110eb1a43fSToomas Soome 				if ((ret = pager_output(lbuf)))
3120eb1a43fSToomas Soome 					break;
3130eb1a43fSToomas Soome 			}
3140eb1a43fSToomas Soome 		}
3150eb1a43fSToomas Soome 		if (ret != 0)
3160eb1a43fSToomas Soome 			break;
317199767f8SToomas Soome 	}
3180eb1a43fSToomas Soome 	pager_close();
3190eb1a43fSToomas Soome 	return (CMD_OK);
320199767f8SToomas Soome }
321199767f8SToomas Soome 
322199767f8SToomas Soome /*
323199767f8SToomas Soome  * File level interface, functions file_*
324199767f8SToomas Soome  */
325199767f8SToomas Soome int
file_load(char * filename,vm_offset_t dest,struct preloaded_file ** result)326199767f8SToomas Soome file_load(char *filename, vm_offset_t dest, struct preloaded_file **result)
327199767f8SToomas Soome {
3280eb1a43fSToomas Soome 	static int last_file_format = 0;
3290eb1a43fSToomas Soome 	struct preloaded_file *fp;
3300eb1a43fSToomas Soome 	int error;
3310eb1a43fSToomas Soome 	int i;
3320eb1a43fSToomas Soome 
3330eb1a43fSToomas Soome 	if (preloaded_files == NULL)
3340eb1a43fSToomas Soome 		last_file_format = 0;
3350eb1a43fSToomas Soome 
3360eb1a43fSToomas Soome 	if (archsw.arch_loadaddr != NULL)
3370eb1a43fSToomas Soome 		dest = archsw.arch_loadaddr(LOAD_RAW, filename, dest);
3380eb1a43fSToomas Soome 
3390eb1a43fSToomas Soome 	error = EFTYPE;
3400eb1a43fSToomas Soome 	for (i = last_file_format, fp = NULL;
3410eb1a43fSToomas Soome 	    file_formats[i] && fp == NULL; i++) {
3420eb1a43fSToomas Soome 		error = (file_formats[i]->l_load)(filename, dest, &fp);
3430eb1a43fSToomas Soome 		if (error == 0) {
3440eb1a43fSToomas Soome 			/* remember the loader */
3450eb1a43fSToomas Soome 			fp->f_loader = last_file_format = i;
3460eb1a43fSToomas Soome 			*result = fp;
3470eb1a43fSToomas Soome 			break;
3480eb1a43fSToomas Soome 		} else if (last_file_format == i && i != 0) {
3490eb1a43fSToomas Soome 			/* Restart from the beginning */
3500eb1a43fSToomas Soome 			i = -1;
3510eb1a43fSToomas Soome 			last_file_format = 0;
3520eb1a43fSToomas Soome 			fp = NULL;
3530eb1a43fSToomas Soome 			continue;
3540eb1a43fSToomas Soome 		}
3550eb1a43fSToomas Soome 		if (error == EFTYPE)
3560eb1a43fSToomas Soome 			continue;	/* Unknown to this handler? */
3570eb1a43fSToomas Soome 		if (error) {
358fc39bce2SToomas Soome 			(void) snprintf(command_errbuf, sizeof (command_errbuf),
3590eb1a43fSToomas Soome 			    "can't load file '%s': %s", filename,
3600eb1a43fSToomas Soome 			    strerror(error));
3610eb1a43fSToomas Soome 			break;
3620eb1a43fSToomas Soome 		}
363199767f8SToomas Soome 	}
3640eb1a43fSToomas Soome 	return (error);
365199767f8SToomas Soome }
366199767f8SToomas Soome 
367199767f8SToomas Soome static int
file_load_dependencies(struct preloaded_file * base_file)368199767f8SToomas Soome file_load_dependencies(struct preloaded_file *base_file)
369199767f8SToomas Soome {
3700eb1a43fSToomas Soome 	struct file_metadata *md;
3710eb1a43fSToomas Soome 	struct preloaded_file *fp;
3720eb1a43fSToomas Soome 	struct mod_depend *verinfo;
3730eb1a43fSToomas Soome 	struct kernel_module *mp;
3740eb1a43fSToomas Soome 	char *dmodname;
3750eb1a43fSToomas Soome 	int error;
3760eb1a43fSToomas Soome 
3770eb1a43fSToomas Soome 	md = file_findmetadata(base_file, MODINFOMD_DEPLIST);
3780eb1a43fSToomas Soome 	if (md == NULL)
3790eb1a43fSToomas Soome 		return (0);
3800eb1a43fSToomas Soome 	error = 0;
3810eb1a43fSToomas Soome 	do {
3820eb1a43fSToomas Soome 		verinfo = (struct mod_depend *)md->md_data;
3830eb1a43fSToomas Soome 		dmodname = (char *)(verinfo + 1);
3840eb1a43fSToomas Soome 		if (file_findmodule(NULL, dmodname, verinfo) == NULL) {
3850eb1a43fSToomas Soome 			printf("loading required module '%s'\n", dmodname);
3860eb1a43fSToomas Soome 			error = mod_load(dmodname, verinfo, 0, NULL);
3870eb1a43fSToomas Soome 			if (error)
3880eb1a43fSToomas Soome 				break;
3890eb1a43fSToomas Soome 			/*
3900eb1a43fSToomas Soome 			 * If module loaded via kld name which isn't listed
3910eb1a43fSToomas Soome 			 * in the linker.hints file, we should check if it have
3920eb1a43fSToomas Soome 			 * required version.
3930eb1a43fSToomas Soome 			 */
3940eb1a43fSToomas Soome 			mp = file_findmodule(NULL, dmodname, verinfo);
3950eb1a43fSToomas Soome 			if (mp == NULL) {
396fc39bce2SToomas Soome 				(void) snprintf(command_errbuf,
3970eb1a43fSToomas Soome 				    sizeof (command_errbuf),
3980eb1a43fSToomas Soome 				    "module '%s' exists but with wrong version",
3990eb1a43fSToomas Soome 				    dmodname);
4000eb1a43fSToomas Soome 				error = ENOENT;
4010eb1a43fSToomas Soome 				break;
4020eb1a43fSToomas Soome 			}
4030eb1a43fSToomas Soome 		}
4040eb1a43fSToomas Soome 		md = metadata_next(md, MODINFOMD_DEPLIST);
4050eb1a43fSToomas Soome 	} while (md);
4060eb1a43fSToomas Soome 	if (!error)
4070eb1a43fSToomas Soome 		return (0);
4080eb1a43fSToomas Soome 	/* Load failed; discard everything */
4090eb1a43fSToomas Soome 	while (base_file != NULL) {
4100eb1a43fSToomas Soome 		fp = base_file;
4110eb1a43fSToomas Soome 		base_file = base_file->f_next;
4120eb1a43fSToomas Soome 		file_discard(fp);
413199767f8SToomas Soome 	}
4140eb1a43fSToomas Soome 	return (error);
415199767f8SToomas Soome }
416199767f8SToomas Soome 
41776608ff7SToomas Soome /*
41876608ff7SToomas Soome  * Calculate the size of the environment module.
41976608ff7SToomas Soome  * The environment is list of name=value C strings, ending with a '\0' byte.
42076608ff7SToomas Soome  */
42176608ff7SToomas Soome static size_t
env_get_size(void)42276608ff7SToomas Soome env_get_size(void)
42376608ff7SToomas Soome {
42476608ff7SToomas Soome 	size_t size = 0;
42576608ff7SToomas Soome 	struct env_var *ep;
42676608ff7SToomas Soome 
42776608ff7SToomas Soome 	/* Traverse the environment. */
42876608ff7SToomas Soome 	for (ep = environ; ep != NULL; ep = ep->ev_next) {
42976608ff7SToomas Soome 		size += strlen(ep->ev_name);
43076608ff7SToomas Soome 		size++;		/* "=" */
43176608ff7SToomas Soome 		if (ep->ev_value != NULL)
43276608ff7SToomas Soome 			size += strlen(ep->ev_value);
43376608ff7SToomas Soome 		size++;		/* nul byte */
43476608ff7SToomas Soome 	}
43576608ff7SToomas Soome 	size++;			/* nul byte */
43676608ff7SToomas Soome 	return (size);
43776608ff7SToomas Soome }
43876608ff7SToomas Soome 
439a103f15bSToomas Soome static void
module_hash(struct preloaded_file * fp,void * addr,size_t size)440a103f15bSToomas Soome module_hash(struct preloaded_file *fp, void *addr, size_t size)
441a103f15bSToomas Soome {
442a103f15bSToomas Soome 	uint8_t hash[SHA1_DIGEST_LENGTH];
443a103f15bSToomas Soome 	char ascii[2 * SHA1_DIGEST_LENGTH + 1];
444a103f15bSToomas Soome 	int i;
445a103f15bSToomas Soome 
446a103f15bSToomas Soome 	sha1(addr, size, hash);
447a103f15bSToomas Soome 	for (i = 0; i < SHA1_DIGEST_LENGTH; i++) {
448fc39bce2SToomas Soome 		(void) snprintf(ascii + 2 * i, sizeof (ascii) - 2 * i, "%02x",
449a103f15bSToomas Soome 		    hash[i] & 0xff);
450a103f15bSToomas Soome 	}
451a103f15bSToomas Soome 	/* Out of memory here is not fatal issue. */
452fc39bce2SToomas Soome 	(void) asprintf(&fp->f_args, "hash=%s", ascii);
453a103f15bSToomas Soome }
454a103f15bSToomas Soome 
45576608ff7SToomas Soome /*
45676608ff7SToomas Soome  * Create virtual module for environment variables.
45776608ff7SToomas Soome  * This module should be created as late as possible before executing
45876608ff7SToomas Soome  * the OS kernel, or we may miss some environment variable updates.
45976608ff7SToomas Soome  */
46076608ff7SToomas Soome void
build_environment_module(void)46176608ff7SToomas Soome build_environment_module(void)
46276608ff7SToomas Soome {
46376608ff7SToomas Soome 	struct preloaded_file *fp;
46476608ff7SToomas Soome 	size_t size;
46576608ff7SToomas Soome 	char *name = "environment";
46676608ff7SToomas Soome 	vm_offset_t laddr;
46776608ff7SToomas Soome 
46876608ff7SToomas Soome 	/* We can't load first */
469ab4969f8SToomas Soome 	if (file_findfile(NULL, NULL) == NULL) {
47076608ff7SToomas Soome 		printf("Can not load environment module: %s\n",
47176608ff7SToomas Soome 		    "the kernel is not loaded");
47276608ff7SToomas Soome 		return;
47376608ff7SToomas Soome 	}
47476608ff7SToomas 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 
4809890ff83SToomas Soome 	tem_save_state();	/* Ask tem to save it's state in env. */
48176608ff7SToomas Soome 	size = env_get_size();
48276608ff7SToomas Soome 
48376608ff7SToomas Soome 	fp = file_alloc();
48476608ff7SToomas Soome 	if (fp != NULL) {
48576608ff7SToomas Soome 		fp->f_name = strdup(name);
48676608ff7SToomas Soome 		fp->f_type = strdup(name);
48776608ff7SToomas Soome 	}
48876608ff7SToomas Soome 
48976608ff7SToomas Soome 	if (fp == NULL || fp->f_name == NULL || fp->f_type == NULL) {
49076608ff7SToomas Soome 		printf("Can not load environment module: %s\n",
49176608ff7SToomas Soome 		    "out of memory");
49208e3b8cfSToomas Soome 		file_discard(fp);
49376608ff7SToomas Soome 		return;
49476608ff7SToomas Soome 	}
49576608ff7SToomas Soome 
49676608ff7SToomas Soome 
49776608ff7SToomas Soome 	if (archsw.arch_loadaddr != NULL)
49876608ff7SToomas Soome 		loadaddr = archsw.arch_loadaddr(LOAD_MEM, &size, loadaddr);
49976608ff7SToomas Soome 
50076608ff7SToomas Soome 	if (loadaddr == 0) {
50176608ff7SToomas Soome 		printf("Can not load environment module: %s\n",
50276608ff7SToomas Soome 		    "out of memory");
50376608ff7SToomas Soome 		file_discard(fp);
50476608ff7SToomas Soome 		return;
50576608ff7SToomas Soome 	}
50676608ff7SToomas Soome 
50776608ff7SToomas Soome 	laddr = bi_copyenv(loadaddr);
50876608ff7SToomas Soome 	/* Looks OK so far; populate control structure */
509a103f15bSToomas Soome 	module_hash(fp, PTOV(loadaddr), laddr - loadaddr);
51076608ff7SToomas Soome 	fp->f_loader = -1;
51176608ff7SToomas Soome 	fp->f_addr = loadaddr;
51276608ff7SToomas Soome 	fp->f_size = laddr - loadaddr;
51376608ff7SToomas Soome 
51476608ff7SToomas Soome 	/* recognise space consumption */
51576608ff7SToomas Soome 	loadaddr = laddr;
51676608ff7SToomas Soome 
51776608ff7SToomas Soome 	file_insert_tail(fp);
51876608ff7SToomas Soome }
51976608ff7SToomas Soome 
5209890ff83SToomas Soome void
build_font_module(void)5219890ff83SToomas Soome build_font_module(void)
5229890ff83SToomas Soome {
5239890ff83SToomas Soome 	bitmap_data_t *bd;
5249890ff83SToomas Soome 	struct font *fd;
5259890ff83SToomas Soome 	struct preloaded_file *fp;
5269890ff83SToomas Soome 	size_t size;
5279890ff83SToomas Soome 	uint32_t checksum;
5289890ff83SToomas Soome 	int i;
5299890ff83SToomas Soome 	char *name = "console-font";
5309890ff83SToomas Soome 	vm_offset_t laddr;
5319890ff83SToomas Soome 	struct font_info fi;
5329890ff83SToomas Soome 	struct fontlist *fl;
5339890ff83SToomas Soome 
5349890ff83SToomas Soome 	if (STAILQ_EMPTY(&fonts))
5359890ff83SToomas Soome 		return;
5369890ff83SToomas Soome 
5379890ff83SToomas Soome 	/* We can't load first */
538ab4969f8SToomas Soome 	if (file_findfile(NULL, NULL) == NULL) {
5399890ff83SToomas Soome 		printf("Can not load font module: %s\n",
5409890ff83SToomas Soome 		    "the kernel is not loaded");
5419890ff83SToomas Soome 		return;
5429890ff83SToomas Soome 	}
5439890ff83SToomas 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 
5499890ff83SToomas Soome 	/* helper pointers */
5509890ff83SToomas Soome 	bd = NULL;
5519890ff83SToomas Soome 	STAILQ_FOREACH(fl, &fonts, font_next) {
552e0721d5aSToomas Soome 		if (tems.ts_font.vf_width == fl->font_data->width &&
553e0721d5aSToomas Soome 		    tems.ts_font.vf_height == fl->font_data->height) {
554e0721d5aSToomas Soome 			/*
555e0721d5aSToomas Soome 			 * Kernel does have better built in font.
556e0721d5aSToomas Soome 			 */
557e0721d5aSToomas Soome 			if (fl->font_flags == FONT_BUILTIN)
558e0721d5aSToomas Soome 				return;
559e0721d5aSToomas Soome 
5609890ff83SToomas Soome 			bd = fl->font_data;
5619890ff83SToomas Soome 			break;
5629890ff83SToomas Soome 		}
5639890ff83SToomas Soome 	}
5649890ff83SToomas Soome 	if (bd == NULL)
5659890ff83SToomas Soome 		return;
5669890ff83SToomas Soome 	fd = bd->font;
5679890ff83SToomas Soome 
5689890ff83SToomas Soome 	fi.fi_width = fd->vf_width;
5699890ff83SToomas Soome 	checksum = fi.fi_width;
5709890ff83SToomas Soome 	fi.fi_height = fd->vf_height;
5719890ff83SToomas Soome 	checksum += fi.fi_height;
5729890ff83SToomas Soome 	fi.fi_bitmap_size = bd->uncompressed_size;
5739890ff83SToomas Soome 	checksum += fi.fi_bitmap_size;
5749890ff83SToomas Soome 
5759890ff83SToomas Soome 	size = roundup2(sizeof (struct font_info), 8);
5769890ff83SToomas Soome 	for (i = 0; i < VFNT_MAPS; i++) {
5779890ff83SToomas Soome 		fi.fi_map_count[i] = fd->vf_map_count[i];
5789890ff83SToomas Soome 		checksum += fi.fi_map_count[i];
5799890ff83SToomas Soome 		size += fd->vf_map_count[i] * sizeof (struct font_map);
5809890ff83SToomas Soome 		size += roundup2(size, 8);
5819890ff83SToomas Soome 	}
5829890ff83SToomas Soome 	size += bd->uncompressed_size;
5839890ff83SToomas Soome 
5849890ff83SToomas Soome 	fi.fi_checksum = -checksum;
5859890ff83SToomas Soome 
5869890ff83SToomas Soome 	fp = file_alloc();
5879890ff83SToomas Soome 	if (fp != NULL) {
5889890ff83SToomas Soome 		fp->f_name = strdup(name);
5899890ff83SToomas Soome 		fp->f_type = strdup(name);
5909890ff83SToomas Soome 	}
5919890ff83SToomas Soome 
5929890ff83SToomas Soome 	if (fp == NULL || fp->f_name == NULL || fp->f_type == NULL) {
5939890ff83SToomas Soome 		printf("Can not load font module: %s\n",
5949890ff83SToomas Soome 		    "out of memory");
59508e3b8cfSToomas Soome 		file_discard(fp);
5969890ff83SToomas Soome 		return;
5979890ff83SToomas Soome 	}
5989890ff83SToomas Soome 
5999890ff83SToomas Soome 	if (archsw.arch_loadaddr != NULL)
6009890ff83SToomas Soome 		loadaddr = archsw.arch_loadaddr(LOAD_MEM, &size, loadaddr);
6019890ff83SToomas Soome 
6029890ff83SToomas Soome 	if (loadaddr == 0) {
6039890ff83SToomas Soome 		printf("Can not load font module: %s\n",
6049890ff83SToomas Soome 		    "out of memory");
6059890ff83SToomas Soome 		file_discard(fp);
6069890ff83SToomas Soome 		return;
6079890ff83SToomas Soome 	}
6089890ff83SToomas Soome 
6099890ff83SToomas Soome 	laddr = loadaddr;
6109890ff83SToomas Soome 	laddr += archsw.arch_copyin(&fi, laddr, sizeof (struct font_info));
6119890ff83SToomas Soome 	laddr = roundup2(laddr, 8);
6129890ff83SToomas Soome 
6139890ff83SToomas Soome 	/* Copy maps. */
6149890ff83SToomas Soome 	for (i = 0; i < VFNT_MAPS; i++) {
6159890ff83SToomas Soome 		if (fd->vf_map_count[i] != 0) {
6169890ff83SToomas Soome 			laddr += archsw.arch_copyin(fd->vf_map[i], laddr,
6179890ff83SToomas Soome 			    fd->vf_map_count[i] * sizeof (struct font_map));
6189890ff83SToomas Soome 			laddr = roundup2(laddr, 8);
6199890ff83SToomas Soome 		}
6209890ff83SToomas Soome 	}
6219890ff83SToomas Soome 
6229890ff83SToomas Soome 	/* Copy the bitmap. */
6239890ff83SToomas Soome 	laddr += archsw.arch_copyin(fd->vf_bytes, laddr, fi.fi_bitmap_size);
6249890ff83SToomas Soome 
6259890ff83SToomas Soome 	/* Looks OK so far; populate control structure */
626a103f15bSToomas Soome 	module_hash(fp, PTOV(loadaddr), laddr - loadaddr);
6279890ff83SToomas Soome 	fp->f_loader = -1;
6289890ff83SToomas Soome 	fp->f_addr = loadaddr;
6299890ff83SToomas Soome 	fp->f_size = laddr - loadaddr;
6309890ff83SToomas Soome 
6319890ff83SToomas Soome 	/* recognise space consumption */
6329890ff83SToomas Soome 	loadaddr = laddr;
6339890ff83SToomas Soome 
6349890ff83SToomas Soome 	file_insert_tail(fp);
6359890ff83SToomas Soome }
6369890ff83SToomas Soome 
637199767f8SToomas Soome /*
638199767f8SToomas Soome  * We've been asked to load (fname) as (type), so just suck it in,
639199767f8SToomas Soome  * no arguments or anything.
640199767f8SToomas Soome  */
641199767f8SToomas Soome struct preloaded_file *
file_loadraw(const char * fname,char * type,int argc,char ** argv,int insert)642199767f8SToomas Soome file_loadraw(const char *fname, char *type, int argc, char **argv, int insert)
643199767f8SToomas Soome {
6440eb1a43fSToomas Soome 	struct preloaded_file *fp;
6450eb1a43fSToomas Soome 	char *name;
646*9fbdb45aSToomas Soome 	int fd;
647*9fbdb45aSToomas Soome 	ssize_t got;
6480eb1a43fSToomas Soome 	struct stat st;
6490eb1a43fSToomas Soome 
6500eb1a43fSToomas Soome 	/* We can't load first */
6510eb1a43fSToomas Soome 	if ((file_findfile(NULL, NULL)) == NULL) {
6520eb1a43fSToomas Soome 		command_errmsg = "can't load file before kernel";
6530eb1a43fSToomas Soome 		return (NULL);
654199767f8SToomas Soome 	}
655199767f8SToomas Soome 
6560eb1a43fSToomas Soome 	/* locate the file on the load path */
6570eb1a43fSToomas Soome 	name = file_search(fname, NULL);
6580eb1a43fSToomas Soome 	if (name == NULL) {
659fc39bce2SToomas Soome 		(void) snprintf(command_errbuf, sizeof (command_errbuf),
6600eb1a43fSToomas Soome 		    "can't find '%s'", fname);
6610eb1a43fSToomas Soome 		return (NULL);
6620eb1a43fSToomas Soome 	}
6630eb1a43fSToomas Soome 
6640eb1a43fSToomas Soome 	if ((fd = open(name, O_RDONLY)) < 0) {
665fc39bce2SToomas Soome 		(void) snprintf(command_errbuf, sizeof (command_errbuf),
6660eb1a43fSToomas Soome 		    "can't open '%s': %s", name, strerror(errno));
6670eb1a43fSToomas Soome 		free(name);
6680eb1a43fSToomas Soome 		return (NULL);
6690eb1a43fSToomas Soome 	}
6700eb1a43fSToomas Soome 	if (fstat(fd, &st) < 0) {
671fc39bce2SToomas Soome 		(void) close(fd);
672fc39bce2SToomas Soome 		(void) snprintf(command_errbuf, sizeof (command_errbuf),
6730eb1a43fSToomas Soome 		    "stat error '%s': %s", name, strerror(errno));
6740eb1a43fSToomas Soome 		free(name);
6750eb1a43fSToomas Soome 		return (NULL);
6760eb1a43fSToomas Soome 	}
6770eb1a43fSToomas Soome 
6780eb1a43fSToomas Soome 	if (archsw.arch_loadaddr != NULL)
6790eb1a43fSToomas Soome 		loadaddr = archsw.arch_loadaddr(LOAD_RAW, name, loadaddr);
6800eb1a43fSToomas Soome 	if (loadaddr == 0) {
681fc39bce2SToomas Soome 		(void) close(fd);
682fc39bce2SToomas Soome 		(void) snprintf(command_errbuf, sizeof (command_errbuf),
6830eb1a43fSToomas Soome 		    "no memory to load %s", name);
6840eb1a43fSToomas Soome 		free(name);
6850eb1a43fSToomas Soome 		return (NULL);
6860eb1a43fSToomas Soome 	}
6870eb1a43fSToomas Soome 
688*9fbdb45aSToomas Soome 	got = archsw.arch_readin(fd, loadaddr, st.st_size);
689*9fbdb45aSToomas Soome 	if ((size_t)got != st.st_size) {
690*9fbdb45aSToomas Soome 		(void) snprintf(command_errbuf, sizeof (command_errbuf),
691*9fbdb45aSToomas Soome 		    "error reading '%s': %s", name, strerror(errno));
692*9fbdb45aSToomas Soome 		free(name);
693*9fbdb45aSToomas Soome 		(void) close(fd);
694*9fbdb45aSToomas Soome 		if (archsw.arch_free_loadaddr != NULL && st.st_size != 0) {
695*9fbdb45aSToomas Soome 			archsw.arch_free_loadaddr(loadaddr,
696*9fbdb45aSToomas Soome 			    (uint64_t)(roundup2(st.st_size, PAGE_SIZE) >> 12));
6970eb1a43fSToomas Soome 		}
698*9fbdb45aSToomas Soome 		return (NULL);
6990eb1a43fSToomas Soome 	}
7000eb1a43fSToomas Soome 
7010eb1a43fSToomas Soome 	/* Looks OK so far; create & populate control structure */
7020eb1a43fSToomas Soome 	fp = file_alloc();
7030eb1a43fSToomas Soome 	if (fp == NULL) {
704873f5d07SToomas Soome 		if (archsw.arch_free_loadaddr != NULL && st.st_size != 0)
7050eb1a43fSToomas Soome 			archsw.arch_free_loadaddr(loadaddr,
7060eb1a43fSToomas Soome 			    (uint64_t)(roundup2(st.st_size, PAGE_SIZE) >> 12));
707fc39bce2SToomas Soome 		(void) snprintf(command_errbuf, sizeof (command_errbuf),
7080eb1a43fSToomas Soome 		    "no memory to load %s", name);
7090eb1a43fSToomas Soome 		free(name);
710fc39bce2SToomas Soome 		(void) close(fd);
7110eb1a43fSToomas Soome 		return (NULL);
7120eb1a43fSToomas Soome 	}
7130eb1a43fSToomas Soome 
7140eb1a43fSToomas Soome 	fp->f_name = name;
7150eb1a43fSToomas Soome 	fp->f_args = unargv(argc, argv);
7160eb1a43fSToomas Soome 	fp->f_type = strdup(type);
7170eb1a43fSToomas Soome 	fp->f_metadata = NULL;
7180eb1a43fSToomas Soome 	fp->f_loader = -1;
7190eb1a43fSToomas Soome 	fp->f_addr = loadaddr;
720*9fbdb45aSToomas Soome 	fp->f_size = st.st_size;
7210eb1a43fSToomas Soome 
7220eb1a43fSToomas Soome 	if (fp->f_type == NULL ||
7230eb1a43fSToomas Soome 	    (argc != 0 && fp->f_args == NULL)) {
724fc39bce2SToomas Soome 		(void) close(fd);
725fc39bce2SToomas Soome 		(void) snprintf(command_errbuf, sizeof (command_errbuf),
7260eb1a43fSToomas Soome 		    "no memory to load %s", name);
7270eb1a43fSToomas Soome 		file_discard(fp);
7280eb1a43fSToomas Soome 		return (NULL);
7290eb1a43fSToomas Soome 	}
7300eb1a43fSToomas Soome 	/* recognise space consumption */
731*9fbdb45aSToomas Soome 	loadaddr += st.st_size;
7320eb1a43fSToomas Soome 
7330eb1a43fSToomas Soome 	/* Add to the list of loaded files */
7340eb1a43fSToomas Soome 	if (insert != 0)
7350eb1a43fSToomas Soome 		file_insert_tail(fp);
736fc39bce2SToomas Soome 	(void) close(fd);
7370eb1a43fSToomas Soome 	return (fp);
738199767f8SToomas Soome }
739199767f8SToomas Soome 
740199767f8SToomas Soome /*
741199767f8SToomas Soome  * Load the module (name), pass it (argc),(argv), add container file
742199767f8SToomas Soome  * to the list of loaded files.
743199767f8SToomas Soome  * If module is already loaded just assign new argc/argv.
744199767f8SToomas Soome  */
745199767f8SToomas Soome int
mod_load(char * modname,struct mod_depend * verinfo,int argc,char * argv[])746199767f8SToomas Soome mod_load(char *modname, struct mod_depend *verinfo, int argc, char *argv[])
747199767f8SToomas Soome {
7480eb1a43fSToomas Soome 	struct kernel_module *mp;
7490eb1a43fSToomas Soome 	int err;
7500eb1a43fSToomas Soome 	char *filename;
7510eb1a43fSToomas Soome 
7520eb1a43fSToomas Soome 	if (file_havepath(modname)) {
7530eb1a43fSToomas Soome 		printf("Warning: mod_load() called instead of mod_loadkld() "
7540eb1a43fSToomas Soome 		    "for module '%s'\n", modname);
7550eb1a43fSToomas Soome 		return (mod_loadkld(modname, argc, argv));
7560eb1a43fSToomas Soome 	}
7570eb1a43fSToomas Soome 	/* see if module is already loaded */
7580eb1a43fSToomas Soome 	mp = file_findmodule(NULL, modname, verinfo);
7590eb1a43fSToomas Soome 	if (mp != NULL) {
7600eb1a43fSToomas Soome 		free(mp->m_args);
7610eb1a43fSToomas Soome 		mp->m_args = unargv(argc, argv);
762fc39bce2SToomas Soome 		(void) snprintf(command_errbuf, sizeof (command_errbuf),
7630eb1a43fSToomas Soome 		    "warning: module '%s' already loaded", mp->m_name);
7640eb1a43fSToomas Soome 		return (0);
7650eb1a43fSToomas Soome 	}
7660eb1a43fSToomas Soome 	/* locate file with the module on the search path */
7670eb1a43fSToomas Soome 	filename = mod_searchmodule(modname, verinfo);
7680eb1a43fSToomas Soome 	if (filename == NULL) {
769fc39bce2SToomas Soome 		(void) snprintf(command_errbuf, sizeof (command_errbuf),
7700eb1a43fSToomas Soome 		    "can't find '%s'", modname);
7710eb1a43fSToomas Soome 		return (ENOENT);
7720eb1a43fSToomas Soome 	}
7730eb1a43fSToomas Soome 	err = mod_loadkld(filename, argc, argv);
7740eb1a43fSToomas Soome 	free(filename);
7750eb1a43fSToomas Soome 	return (err);
776199767f8SToomas Soome }
777199767f8SToomas Soome 
778199767f8SToomas Soome /*
779199767f8SToomas Soome  * Load specified KLD. If path is omitted, then try to locate it via
780199767f8SToomas Soome  * search path.
781199767f8SToomas Soome  */
782199767f8SToomas Soome int
mod_loadkld(const char * kldname,int argc,char * argv[])783199767f8SToomas Soome mod_loadkld(const char *kldname, int argc, char *argv[])
784199767f8SToomas Soome {
785a8412dc1SToomas Soome 	struct preloaded_file *fp;
7860eb1a43fSToomas Soome 	int err;
7870eb1a43fSToomas Soome 	char *filename;
788a8412dc1SToomas Soome 	vm_offset_t loadaddr_saved;
7890eb1a43fSToomas Soome 
7900eb1a43fSToomas Soome 	/*
7910eb1a43fSToomas Soome 	 * Get fully qualified KLD name
7920eb1a43fSToomas Soome 	 */
7930eb1a43fSToomas Soome 	filename = file_search(kldname, kld_ext_list);
7940eb1a43fSToomas Soome 	if (filename == NULL) {
795fc39bce2SToomas Soome 		(void) snprintf(command_errbuf, sizeof (command_errbuf),
7960eb1a43fSToomas Soome 		    "can't find '%s'", kldname);
7970eb1a43fSToomas Soome 		return (ENOENT);
798199767f8SToomas Soome 	}
7990eb1a43fSToomas Soome 	/*
8000eb1a43fSToomas Soome 	 * Check if KLD already loaded
8010eb1a43fSToomas Soome 	 */
8020eb1a43fSToomas Soome 	fp = file_findfile(filename, NULL);
8030eb1a43fSToomas Soome 	if (fp != NULL) {
804fc39bce2SToomas Soome 		(void) snprintf(command_errbuf, sizeof (command_errbuf),
8050eb1a43fSToomas Soome 		    "warning: KLD '%s' already loaded", filename);
8060eb1a43fSToomas Soome 		free(filename);
8070eb1a43fSToomas Soome 		return (0);
8080eb1a43fSToomas Soome 	}
8090eb1a43fSToomas Soome 
8100eb1a43fSToomas Soome 	do {
8110eb1a43fSToomas Soome 		err = file_load(filename, loadaddr, &fp);
8120eb1a43fSToomas Soome 		if (err)
8130eb1a43fSToomas Soome 			break;
8140eb1a43fSToomas Soome 		fp->f_args = unargv(argc, argv);
815a8412dc1SToomas Soome 		loadaddr_saved = loadaddr;
8160eb1a43fSToomas Soome 		loadaddr = fp->f_addr + fp->f_size;
8170eb1a43fSToomas Soome 		file_insert_tail(fp);	/* Add to the list of loaded files */
8180eb1a43fSToomas Soome 		if (file_load_dependencies(fp) != 0) {
8190eb1a43fSToomas Soome 			err = ENOENT;
820a8412dc1SToomas Soome 			file_remove(fp);
821a8412dc1SToomas Soome 			loadaddr = loadaddr_saved;
8220eb1a43fSToomas Soome 			fp = NULL;
8230eb1a43fSToomas Soome 			break;
8240eb1a43fSToomas Soome 		}
8250eb1a43fSToomas Soome 	} while (0);
8260eb1a43fSToomas Soome 	if (err == EFTYPE) {
827fc39bce2SToomas Soome 		(void) snprintf(command_errbuf, sizeof (command_errbuf),
8280eb1a43fSToomas Soome 		    "don't know how to load module '%s'", filename);
8290eb1a43fSToomas Soome 	}
8300eb1a43fSToomas Soome 	if (err)
8310eb1a43fSToomas Soome 		file_discard(fp);
8320eb1a43fSToomas Soome 	free(filename);
8330eb1a43fSToomas Soome 	return (err);
834199767f8SToomas Soome }
835199767f8SToomas Soome 
836199767f8SToomas Soome /*
837199767f8SToomas Soome  * Find a file matching (name) and (type).
838199767f8SToomas Soome  * NULL may be passed as a wildcard to either.
839199767f8SToomas Soome  */
840199767f8SToomas Soome struct preloaded_file *
file_findfile(const char * name,const char * type)841199767f8SToomas Soome file_findfile(const char *name, const char *type)
842199767f8SToomas Soome {
8430eb1a43fSToomas Soome 	struct preloaded_file *fp;
8440eb1a43fSToomas Soome 
8450eb1a43fSToomas Soome 	for (fp = preloaded_files; fp != NULL; fp = fp->f_next) {
8460eb1a43fSToomas Soome 		if (((name == NULL) || strcmp(name, fp->f_name) == 0) &&
8470eb1a43fSToomas Soome 		    ((type == NULL) || strcmp(type, fp->f_type) == 0))
8480eb1a43fSToomas Soome 			break;
8490eb1a43fSToomas Soome 	}
8500eb1a43fSToomas Soome 	return (fp);
851199767f8SToomas Soome }
852199767f8SToomas Soome 
853199767f8SToomas Soome /*
854199767f8SToomas Soome  * Find a module matching (name) inside of given file.
855199767f8SToomas Soome  * NULL may be passed as a wildcard.
856199767f8SToomas Soome  */
857199767f8SToomas Soome struct kernel_module *
file_findmodule(struct preloaded_file * fp,char * modname,struct mod_depend * verinfo)858199767f8SToomas Soome file_findmodule(struct preloaded_file *fp, char *modname,
8590eb1a43fSToomas Soome     struct mod_depend *verinfo)
860199767f8SToomas Soome {
8610eb1a43fSToomas Soome 	struct kernel_module *mp, *best;
8620eb1a43fSToomas Soome 	int bestver, mver;
8630eb1a43fSToomas Soome 
8640eb1a43fSToomas Soome 	if (fp == NULL) {
8650eb1a43fSToomas Soome 		for (fp = preloaded_files; fp; fp = fp->f_next) {
8660eb1a43fSToomas Soome 			mp = file_findmodule(fp, modname, verinfo);
8670eb1a43fSToomas Soome 			if (mp != NULL)
8680eb1a43fSToomas Soome 				return (mp);
8690eb1a43fSToomas Soome 		}
8700eb1a43fSToomas Soome 		return (NULL);
871199767f8SToomas Soome 	}
8720eb1a43fSToomas Soome 	best = NULL;
8730eb1a43fSToomas Soome 	bestver = 0;
8740eb1a43fSToomas Soome 	for (mp = fp->f_modules; mp; mp = mp->m_next) {
8750eb1a43fSToomas Soome 		if (strcmp(modname, mp->m_name) == 0) {
8760eb1a43fSToomas Soome 			if (verinfo == NULL)
8770eb1a43fSToomas Soome 				return (mp);
8780eb1a43fSToomas Soome 			mver = mp->m_version;
8790eb1a43fSToomas Soome 			if (mver == verinfo->md_ver_preferred)
8800eb1a43fSToomas Soome 				return (mp);
8810eb1a43fSToomas Soome 			if (mver >= verinfo->md_ver_minimum &&
8820eb1a43fSToomas Soome 			    mver <= verinfo->md_ver_maximum &&
8830eb1a43fSToomas Soome 			    mver > bestver) {
8840eb1a43fSToomas Soome 				best = mp;
8850eb1a43fSToomas Soome 				bestver = mver;
8860eb1a43fSToomas Soome 			}
8870eb1a43fSToomas Soome 		}
888199767f8SToomas Soome 	}
8890eb1a43fSToomas Soome 	return (best);
890199767f8SToomas Soome }
891199767f8SToomas Soome /*
892199767f8SToomas Soome  * Make a copy of (size) bytes of data from (p), and associate them as
893199767f8SToomas Soome  * metadata of (type) to the module (mp).
894199767f8SToomas Soome  */
895199767f8SToomas Soome void
file_addmetadata(struct preloaded_file * fp,int type,size_t size,void * p)896199767f8SToomas Soome file_addmetadata(struct preloaded_file *fp, int type, size_t size, void *p)
897199767f8SToomas Soome {
89808e3b8cfSToomas Soome 	struct file_metadata	*md;
89908e3b8cfSToomas Soome 
90008e3b8cfSToomas Soome 	md = malloc(sizeof (struct file_metadata) - sizeof (md->md_data) +
90108e3b8cfSToomas Soome 	    size);
90208e3b8cfSToomas Soome 	if (md != NULL) {
90308e3b8cfSToomas Soome 		md->md_size = size;
90408e3b8cfSToomas Soome 		md->md_type = type;
90508e3b8cfSToomas Soome 		bcopy(p, md->md_data, size);
90608e3b8cfSToomas Soome 		md->md_next = fp->f_metadata;
90708e3b8cfSToomas Soome 	}
90808e3b8cfSToomas Soome 	fp->f_metadata = md;
909199767f8SToomas Soome }
910199767f8SToomas Soome 
911199767f8SToomas Soome /*
912199767f8SToomas Soome  * Find a metadata object of (type) associated with the file (fp)
913199767f8SToomas Soome  */
914199767f8SToomas Soome struct file_metadata *
file_findmetadata(struct preloaded_file * fp,int type)915199767f8SToomas Soome file_findmetadata(struct preloaded_file *fp, int type)
916199767f8SToomas Soome {
9170eb1a43fSToomas Soome 	struct file_metadata *md;
918199767f8SToomas Soome 
9190eb1a43fSToomas Soome 	for (md = fp->f_metadata; md != NULL; md = md->md_next)
9200eb1a43fSToomas Soome 		if (md->md_type == type)
9210eb1a43fSToomas Soome 			break;
9220eb1a43fSToomas Soome 	return (md);
923199767f8SToomas Soome }
924199767f8SToomas Soome 
925199767f8SToomas Soome struct file_metadata *
metadata_next(struct file_metadata * md,int type)926199767f8SToomas Soome metadata_next(struct file_metadata *md, int type)
927199767f8SToomas Soome {
9280eb1a43fSToomas Soome 
9290eb1a43fSToomas Soome 	if (md == NULL)
9300eb1a43fSToomas Soome 		return (NULL);
9310eb1a43fSToomas Soome 	while ((md = md->md_next) != NULL)
9320eb1a43fSToomas Soome 		if (md->md_type == type)
9330eb1a43fSToomas Soome 			break;
9340eb1a43fSToomas Soome 	return (md);
935199767f8SToomas Soome }
936199767f8SToomas Soome 
937199767f8SToomas Soome static const char *emptyextlist[] = { "", NULL };
938199767f8SToomas Soome 
939199767f8SToomas Soome /*
940199767f8SToomas Soome  * Check if the given file is in place and return full path to it.
941199767f8SToomas Soome  */
942199767f8SToomas Soome static char *
file_lookup(const char * path,const char * name,int namelen,const char ** extlist)9430eb1a43fSToomas Soome file_lookup(const char *path, const char *name, int namelen,
9440eb1a43fSToomas Soome     const char **extlist)
945199767f8SToomas Soome {
9460eb1a43fSToomas Soome 	struct stat st;
9470eb1a43fSToomas Soome 	char *result, *cp;
9480eb1a43fSToomas Soome 	const char **cpp;
9490eb1a43fSToomas Soome 	int pathlen, extlen, len;
9500eb1a43fSToomas Soome 
9510eb1a43fSToomas Soome 	pathlen = strlen(path);
9520eb1a43fSToomas Soome 	extlen = 0;
9530eb1a43fSToomas Soome 	if (extlist == NULL)
9540eb1a43fSToomas Soome 		extlist = emptyextlist;
9550eb1a43fSToomas Soome 	for (cpp = extlist; *cpp; cpp++) {
9560eb1a43fSToomas Soome 		len = strlen(*cpp);
9570eb1a43fSToomas Soome 		if (len > extlen)
9580eb1a43fSToomas Soome 			extlen = len;
9590eb1a43fSToomas Soome 	}
9600eb1a43fSToomas Soome 	result = malloc(pathlen + namelen + extlen + 2);
9610eb1a43fSToomas Soome 	if (result == NULL)
9620eb1a43fSToomas Soome 		return (NULL);
9630eb1a43fSToomas Soome 	bcopy(path, result, pathlen);
9640eb1a43fSToomas Soome 	if (pathlen > 0 && result[pathlen - 1] != '/')
9650eb1a43fSToomas Soome 		result[pathlen++] = '/';
9660eb1a43fSToomas Soome 	cp = result + pathlen;
9670eb1a43fSToomas Soome 	bcopy(name, cp, namelen);
9680eb1a43fSToomas Soome 	cp += namelen;
9690eb1a43fSToomas Soome 	for (cpp = extlist; *cpp; cpp++) {
970fc39bce2SToomas Soome 		(void) strcpy(cp, *cpp);
9710eb1a43fSToomas Soome 		if (stat(result, &st) == 0 && S_ISREG(st.st_mode))
9720eb1a43fSToomas Soome 			return (result);
9730eb1a43fSToomas Soome 	}
9740eb1a43fSToomas Soome 	free(result);
975199767f8SToomas Soome 	return (NULL);
976199767f8SToomas Soome }
977199767f8SToomas Soome 
978199767f8SToomas Soome /*
979199767f8SToomas Soome  * Check if file name have any qualifiers
980199767f8SToomas Soome  */
981199767f8SToomas Soome static int
file_havepath(const char * name)982199767f8SToomas Soome file_havepath(const char *name)
983199767f8SToomas Soome {
9840eb1a43fSToomas Soome 	const char *cp;
985199767f8SToomas Soome 
986fc39bce2SToomas Soome 	(void) archsw.arch_getdev(NULL, name, &cp);
9870eb1a43fSToomas Soome 	return (cp != name || strchr(name, '/') != NULL);
988199767f8SToomas Soome }
989199767f8SToomas Soome 
990199767f8SToomas Soome /*
991199767f8SToomas Soome  * Attempt to find the file (name) on the module searchpath.
992199767f8SToomas Soome  * If (name) is qualified in any way, we simply check it and
993199767f8SToomas Soome  * return it or NULL.  If it is not qualified, then we attempt
994199767f8SToomas Soome  * to construct a path using entries in the environment variable
995199767f8SToomas Soome  * module_path.
996199767f8SToomas Soome  *
997199767f8SToomas Soome  * The path we return a pointer to need never be freed, as we manage
998199767f8SToomas Soome  * it internally.
999199767f8SToomas Soome  */
1000199767f8SToomas Soome static char *
file_search(const char * name,const char ** extlist)1001199767f8SToomas Soome file_search(const char *name, const char **extlist)
1002199767f8SToomas Soome {
10030eb1a43fSToomas Soome 	struct moduledir *mdp;
10040eb1a43fSToomas Soome 	struct stat sb;
10050eb1a43fSToomas Soome 	char *result;
10060eb1a43fSToomas Soome 	int namelen;
10070eb1a43fSToomas Soome 
10080eb1a43fSToomas Soome 	/* Don't look for nothing */
10090eb1a43fSToomas Soome 	if (name == NULL)
10100eb1a43fSToomas Soome 		return (NULL);
10110eb1a43fSToomas Soome 
10120eb1a43fSToomas Soome 	if (*name == '\0')
10130eb1a43fSToomas Soome 		return (strdup(name));
10140eb1a43fSToomas Soome 
10150eb1a43fSToomas Soome 	if (file_havepath(name)) {
10160eb1a43fSToomas Soome 		/* Qualified, so just see if it exists */
10170eb1a43fSToomas Soome 		if (stat(name, &sb) == 0)
10180eb1a43fSToomas Soome 			return (strdup(name));
10190eb1a43fSToomas Soome 		return (NULL);
10200eb1a43fSToomas Soome 	}
10210eb1a43fSToomas Soome 	moduledir_rebuild();
10220eb1a43fSToomas Soome 	result = NULL;
10230eb1a43fSToomas Soome 	namelen = strlen(name);
10240eb1a43fSToomas Soome 	STAILQ_FOREACH(mdp, &moduledir_list, d_link) {
10250eb1a43fSToomas Soome 		result = file_lookup(mdp->d_path, name, namelen, extlist);
10260eb1a43fSToomas Soome 		if (result != NULL)
10270eb1a43fSToomas Soome 			break;
10280eb1a43fSToomas Soome 	}
10290eb1a43fSToomas Soome 	return (result);
1030199767f8SToomas Soome }
1031199767f8SToomas Soome 
1032199767f8SToomas Soome #define	INT_ALIGN(base, ptr)	ptr = \
10330eb1a43fSToomas Soome 	(base) + (((ptr) - (base) + sizeof (int) - 1) & ~(sizeof (int) - 1))
1034199767f8SToomas Soome 
1035199767f8SToomas Soome static char *
mod_search_hints(struct moduledir * mdp,const char * modname,struct mod_depend * verinfo)1036199767f8SToomas Soome mod_search_hints(struct moduledir *mdp, const char *modname,
10370eb1a43fSToomas Soome     struct mod_depend *verinfo)
1038199767f8SToomas Soome {
10390eb1a43fSToomas Soome 	uchar_t *cp, *recptr, *bufend, *best;
10400eb1a43fSToomas Soome 	char *result;
10410eb1a43fSToomas Soome 	int *intp, bestver, blen, clen, ival, modnamelen, reclen;
10420eb1a43fSToomas Soome 	bool found;
10430eb1a43fSToomas Soome 
10440eb1a43fSToomas Soome 	moduledir_readhints(mdp);
10450eb1a43fSToomas Soome 	modnamelen = strlen(modname);
10460eb1a43fSToomas Soome 	found = false;
10470eb1a43fSToomas Soome 	result = NULL;
10480eb1a43fSToomas Soome 	bestver = 0;
10490eb1a43fSToomas Soome 	if (mdp->d_hints == NULL)
10500eb1a43fSToomas Soome 		goto bad;
10510eb1a43fSToomas Soome 	recptr = mdp->d_hints;
10520eb1a43fSToomas Soome 	bufend = recptr + mdp->d_hintsz;
10530eb1a43fSToomas Soome 	clen = blen = 0;
10540eb1a43fSToomas Soome 	best = cp = NULL;
10550eb1a43fSToomas Soome 	while (recptr < bufend && !found) {
10560eb1a43fSToomas Soome 		intp = (int *)recptr;
10570eb1a43fSToomas Soome 		reclen = *intp++;
10580eb1a43fSToomas Soome 		ival = *intp++;
10590eb1a43fSToomas Soome 		cp = (uchar_t *)intp;
10600eb1a43fSToomas Soome 		switch (ival) {
10610eb1a43fSToomas Soome 		case MDT_VERSION:
10620eb1a43fSToomas Soome 			clen = *cp++;
10630eb1a43fSToomas Soome 			if (clen != modnamelen || bcmp(cp, modname, clen) != 0)
10640eb1a43fSToomas Soome 				break;
10650eb1a43fSToomas Soome 			cp += clen;
10660eb1a43fSToomas Soome 			INT_ALIGN(mdp->d_hints, cp);
10670eb1a43fSToomas Soome 			ival = *(int *)cp;
10680eb1a43fSToomas Soome 			cp += sizeof (int);
10690eb1a43fSToomas Soome 			clen = *cp++;
10700eb1a43fSToomas Soome 			if (verinfo == NULL ||
10710eb1a43fSToomas Soome 			    ival == verinfo->md_ver_preferred) {
10720eb1a43fSToomas Soome 				found = true;
10730eb1a43fSToomas Soome 				break;
10740eb1a43fSToomas Soome 			}
10750eb1a43fSToomas Soome 			if (ival >= verinfo->md_ver_minimum &&
10760eb1a43fSToomas Soome 			    ival <= verinfo->md_ver_maximum &&
10770eb1a43fSToomas Soome 			    ival > bestver) {
10780eb1a43fSToomas Soome 				bestver = ival;
10790eb1a43fSToomas Soome 				best = cp;
10800eb1a43fSToomas Soome 				blen = clen;
10810eb1a43fSToomas Soome 			}
10820eb1a43fSToomas Soome 			break;
10830eb1a43fSToomas Soome 		default:
10840eb1a43fSToomas Soome 			break;
10850eb1a43fSToomas Soome 		}
10860eb1a43fSToomas Soome 		recptr += reclen + sizeof (int);
1087199767f8SToomas Soome 	}
10880eb1a43fSToomas Soome 	/*
10890eb1a43fSToomas Soome 	 * Finally check if KLD is in the place
10900eb1a43fSToomas Soome 	 */
10910eb1a43fSToomas Soome 	if (found)
10920eb1a43fSToomas Soome 		result = file_lookup(mdp->d_path, (char *)cp, clen, NULL);
10930eb1a43fSToomas Soome 	else if (best)
10940eb1a43fSToomas Soome 		result = file_lookup(mdp->d_path, (char *)best, blen, NULL);
1095199767f8SToomas Soome bad:
10960eb1a43fSToomas Soome 	/*
10970eb1a43fSToomas Soome 	 * If nothing found or hints is absent - fallback to the old way
10980eb1a43fSToomas Soome 	 * by using "kldname[.ko]" as module name.
10990eb1a43fSToomas Soome 	 */
11000eb1a43fSToomas Soome 	if (!found && bestver == 0 && result == NULL) {
11010eb1a43fSToomas Soome 		result = file_lookup(mdp->d_path, modname, modnamelen,
11020eb1a43fSToomas Soome 		    kld_ext_list);
11030eb1a43fSToomas Soome 	}
11040eb1a43fSToomas Soome 	return (result);
1105199767f8SToomas Soome }
1106199767f8SToomas Soome 
1107199767f8SToomas Soome /*
1108199767f8SToomas Soome  * Attempt to locate the file containing the module (name)
1109199767f8SToomas Soome  */
1110199767f8SToomas Soome static char *
mod_searchmodule(char * name,struct mod_depend * verinfo)1111199767f8SToomas Soome mod_searchmodule(char *name, struct mod_depend *verinfo)
1112199767f8SToomas Soome {
11130eb1a43fSToomas Soome 	struct moduledir *mdp;
11140eb1a43fSToomas Soome 	char *result;
11150eb1a43fSToomas Soome 
11160eb1a43fSToomas Soome 	moduledir_rebuild();
11170eb1a43fSToomas Soome 	/*
11180eb1a43fSToomas Soome 	 * Now we ready to lookup module in the given directories
11190eb1a43fSToomas Soome 	 */
11200eb1a43fSToomas Soome 	result = NULL;
11210eb1a43fSToomas Soome 	STAILQ_FOREACH(mdp, &moduledir_list, d_link) {
11220eb1a43fSToomas Soome 		result = mod_search_hints(mdp, name, verinfo);
11230eb1a43fSToomas Soome 		if (result != NULL)
11240eb1a43fSToomas Soome 			break;
11250eb1a43fSToomas Soome 	}
11260eb1a43fSToomas Soome 
11270eb1a43fSToomas Soome 	return (result);
1128199767f8SToomas Soome }
1129199767f8SToomas Soome 
1130199767f8SToomas Soome int
file_addmodule(struct preloaded_file * fp,char * modname,int version,struct kernel_module ** newmp)1131199767f8SToomas Soome file_addmodule(struct preloaded_file *fp, char *modname, int version,
11320eb1a43fSToomas Soome     struct kernel_module **newmp)
1133199767f8SToomas Soome {
11340eb1a43fSToomas Soome 	struct kernel_module *mp;
11350eb1a43fSToomas Soome 	struct mod_depend mdepend;
11360eb1a43fSToomas Soome 
11370eb1a43fSToomas Soome 	bzero(&mdepend, sizeof (mdepend));
11380eb1a43fSToomas Soome 	mdepend.md_ver_preferred = version;
11390eb1a43fSToomas Soome 	mp = file_findmodule(fp, modname, &mdepend);
11400eb1a43fSToomas Soome 	if (mp != NULL)
11410eb1a43fSToomas Soome 		return (EEXIST);
11420eb1a43fSToomas Soome 	mp = calloc(1, sizeof (struct kernel_module));
11430eb1a43fSToomas Soome 	if (mp == NULL)
11440eb1a43fSToomas Soome 		return (ENOMEM);
11450eb1a43fSToomas Soome 	mp->m_name = strdup(modname);
11460eb1a43fSToomas Soome 	if (mp->m_name == NULL) {
11470eb1a43fSToomas Soome 		free(mp);
11480eb1a43fSToomas Soome 		return (ENOMEM);
11490eb1a43fSToomas Soome 	}
11500eb1a43fSToomas Soome 	mp->m_version = version;
11510eb1a43fSToomas Soome 	mp->m_fp = fp;
11520eb1a43fSToomas Soome 	mp->m_next = fp->f_modules;
11530eb1a43fSToomas Soome 	fp->f_modules = mp;
11540eb1a43fSToomas Soome 	if (newmp)
11550eb1a43fSToomas Soome 		*newmp = mp;
11560eb1a43fSToomas Soome 	return (0);
1157199767f8SToomas Soome }
1158199767f8SToomas Soome 
1159199767f8SToomas Soome /*
1160199767f8SToomas Soome  * Throw a file away
1161199767f8SToomas Soome  */
1162199767f8SToomas Soome void
file_discard(struct preloaded_file * fp)1163199767f8SToomas Soome file_discard(struct preloaded_file *fp)
1164199767f8SToomas Soome {
11650eb1a43fSToomas Soome 	struct file_metadata *md, *md1;
11660eb1a43fSToomas Soome 	struct kernel_module *mp, *mp1;
11670eb1a43fSToomas Soome 
11680eb1a43fSToomas Soome 	if (fp == NULL)
11690eb1a43fSToomas Soome 		return;
11700eb1a43fSToomas Soome 
1171873f5d07SToomas Soome 	if (archsw.arch_free_loadaddr != NULL && fp->f_addr &&
1172873f5d07SToomas Soome 	    fp->f_size != 0) {
11730eb1a43fSToomas Soome 		archsw.arch_free_loadaddr(fp->f_addr,
11740eb1a43fSToomas Soome 		    (uint64_t)(roundup2(fp->f_size, PAGE_SIZE) >> 12));
11750eb1a43fSToomas Soome 	}
117614ee0d29SToomas Soome 
11770eb1a43fSToomas Soome 	md = fp->f_metadata;
11780eb1a43fSToomas Soome 	while (md != NULL) {
11790eb1a43fSToomas Soome 		md1 = md;
11800eb1a43fSToomas Soome 		md = md->md_next;
11810eb1a43fSToomas Soome 		free(md1);
11820eb1a43fSToomas Soome 	}
11830eb1a43fSToomas Soome 	mp = fp->f_modules;
11840eb1a43fSToomas Soome 	while (mp != NULL) {
11850eb1a43fSToomas Soome 		free(mp->m_name);
11860eb1a43fSToomas Soome 		mp1 = mp;
11870eb1a43fSToomas Soome 		mp = mp->m_next;
11880eb1a43fSToomas Soome 		free(mp1);
11890eb1a43fSToomas Soome 	}
11900eb1a43fSToomas Soome 	free(fp->f_name);
11910eb1a43fSToomas Soome 	free(fp->f_type);
11920eb1a43fSToomas Soome 	free(fp->f_args);
11930eb1a43fSToomas Soome 	free(fp);
1194199767f8SToomas Soome }
1195199767f8SToomas Soome 
1196199767f8SToomas Soome /*
1197199767f8SToomas Soome  * Allocate a new file; must be used instead of malloc()
1198199767f8SToomas Soome  * to ensure safe initialisation.
1199199767f8SToomas Soome  */
1200199767f8SToomas Soome struct preloaded_file *
file_alloc(void)1201199767f8SToomas Soome file_alloc(void)
1202199767f8SToomas Soome {
12039890ff83SToomas Soome 
12040eb1a43fSToomas Soome 	return (calloc(1, sizeof (struct preloaded_file)));
1205199767f8SToomas Soome }
1206199767f8SToomas Soome 
1207199767f8SToomas Soome /*
1208199767f8SToomas Soome  * Add a module to the chain
1209199767f8SToomas Soome  */
1210199767f8SToomas Soome static void
file_insert_tail(struct preloaded_file * fp)1211199767f8SToomas Soome file_insert_tail(struct preloaded_file *fp)
1212199767f8SToomas Soome {
12130eb1a43fSToomas Soome 	struct preloaded_file *cm;
12140eb1a43fSToomas Soome 
12150eb1a43fSToomas Soome 	/* Append to list of loaded file */
12160eb1a43fSToomas Soome 	fp->f_next = NULL;
12170eb1a43fSToomas Soome 	if (preloaded_files == NULL) {
12180eb1a43fSToomas Soome 		preloaded_files = fp;
12190eb1a43fSToomas Soome 	} else {
12200eb1a43fSToomas Soome 		for (cm = preloaded_files; cm->f_next != NULL; cm = cm->f_next)
12210eb1a43fSToomas Soome 			;
12220eb1a43fSToomas Soome 		cm->f_next = fp;
12230eb1a43fSToomas Soome 	}
1224199767f8SToomas Soome }
1225199767f8SToomas Soome 
1226a8412dc1SToomas Soome /*
1227a8412dc1SToomas Soome  * Remove module from the chain
1228a8412dc1SToomas Soome  */
1229a8412dc1SToomas Soome static void
file_remove(struct preloaded_file * fp)1230a8412dc1SToomas Soome file_remove(struct preloaded_file *fp)
1231a8412dc1SToomas Soome {
1232a8412dc1SToomas Soome 	struct preloaded_file   *cm;
1233a8412dc1SToomas Soome 
1234a8412dc1SToomas Soome 	if (preloaded_files == NULL)
1235a8412dc1SToomas Soome 		return;
1236a8412dc1SToomas Soome 
1237a8412dc1SToomas Soome 	if (preloaded_files == fp) {
1238a8412dc1SToomas Soome 		preloaded_files = fp->f_next;
1239a8412dc1SToomas Soome 		return;
1240a8412dc1SToomas Soome 	}
1241a8412dc1SToomas Soome 	for (cm = preloaded_files; cm->f_next != NULL; cm = cm->f_next) {
1242a8412dc1SToomas Soome 		if (cm->f_next == fp) {
1243a8412dc1SToomas Soome 			cm->f_next = fp->f_next;
1244a8412dc1SToomas Soome 			return;
1245a8412dc1SToomas Soome 		}
1246a8412dc1SToomas Soome 	}
1247a8412dc1SToomas Soome }
1248a8412dc1SToomas Soome 
1249199767f8SToomas Soome static char *
moduledir_fullpath(struct moduledir * mdp,const char * fname)1250199767f8SToomas Soome moduledir_fullpath(struct moduledir *mdp, const char *fname)
1251199767f8SToomas Soome {
12520eb1a43fSToomas Soome 	char *cp;
12530eb1a43fSToomas Soome 
12540eb1a43fSToomas Soome 	cp = malloc(strlen(mdp->d_path) + strlen(fname) + 2);
12550eb1a43fSToomas Soome 	if (cp == NULL)
12560eb1a43fSToomas Soome 		return (NULL);
12570eb1a43fSToomas Soome 	strcpy(cp, mdp->d_path);
12580eb1a43fSToomas Soome 	strcat(cp, "/");
12590eb1a43fSToomas Soome 	strcat(cp, fname);
12600eb1a43fSToomas Soome 	return (cp);
1261199767f8SToomas Soome }
1262199767f8SToomas Soome 
1263199767f8SToomas Soome /*
1264199767f8SToomas Soome  * Read linker.hints file into memory performing some sanity checks.
1265199767f8SToomas Soome  */
1266199767f8SToomas Soome static void
moduledir_readhints(struct moduledir * mdp)1267199767f8SToomas Soome moduledir_readhints(struct moduledir *mdp)
1268199767f8SToomas Soome {
12690eb1a43fSToomas Soome 	struct stat st;
12700eb1a43fSToomas Soome 	char *path;
12710eb1a43fSToomas Soome 	int fd, size, version;
1272199767f8SToomas Soome 
12730eb1a43fSToomas Soome 	if (mdp->d_hints != NULL || (mdp->d_flags & MDIR_NOHINTS))
12740eb1a43fSToomas Soome 		return;
12750eb1a43fSToomas Soome 	path = moduledir_fullpath(mdp, "linker.hints");
12760eb1a43fSToomas Soome 	if (stat(path, &st) != 0 ||
12770eb1a43fSToomas Soome 	    st.st_size < (ssize_t)(sizeof (version) + sizeof (int)) ||
12780eb1a43fSToomas Soome 	    st.st_size > LINKER_HINTS_MAX ||
12790eb1a43fSToomas Soome 	    (fd = open(path, O_RDONLY)) < 0) {
12800eb1a43fSToomas Soome 		free(path);
12810eb1a43fSToomas Soome 		mdp->d_flags |= MDIR_NOHINTS;
12820eb1a43fSToomas Soome 		return;
12830eb1a43fSToomas Soome 	}
1284199767f8SToomas Soome 	free(path);
12850eb1a43fSToomas Soome 	size = read(fd, &version, sizeof (version));
12860eb1a43fSToomas Soome 	if (size != sizeof (version) || version != LINKER_HINTS_VERSION)
12870eb1a43fSToomas Soome 		goto bad;
12880eb1a43fSToomas Soome 	size = st.st_size - size;
12890eb1a43fSToomas Soome 	mdp->d_hints = malloc(size);
12900eb1a43fSToomas Soome 	if (mdp->d_hints == NULL)
12910eb1a43fSToomas Soome 		goto bad;
12920eb1a43fSToomas Soome 	if (read(fd, mdp->d_hints, size) != size)
12930eb1a43fSToomas Soome 		goto bad;
12940eb1a43fSToomas Soome 	mdp->d_hintsz = size;
1295fc39bce2SToomas Soome 	(void) close(fd);
1296199767f8SToomas Soome 	return;
1297199767f8SToomas Soome bad:
1298fc39bce2SToomas Soome 	(void) close(fd);
12990eb1a43fSToomas Soome 	free(mdp->d_hints);
13000eb1a43fSToomas Soome 	mdp->d_hints = NULL;
13010eb1a43fSToomas Soome 	mdp->d_flags |= MDIR_NOHINTS;
1302199767f8SToomas Soome }
1303199767f8SToomas Soome 
1304199767f8SToomas Soome /*
1305199767f8SToomas Soome  * Extract directories from the ';' separated list, remove duplicates.
1306199767f8SToomas Soome  */
1307199767f8SToomas Soome static void
moduledir_rebuild(void)1308199767f8SToomas Soome moduledir_rebuild(void)
1309199767f8SToomas Soome {
13100eb1a43fSToomas Soome 	struct moduledir *mdp, *mtmp;
13110eb1a43fSToomas Soome 	const char *path, *cp, *ep;
13120eb1a43fSToomas Soome 	size_t cplen;
13130eb1a43fSToomas Soome 
13140eb1a43fSToomas Soome 	path = getenv("module_path");
13150eb1a43fSToomas Soome 	if (path == NULL)
13160eb1a43fSToomas Soome 		path = default_searchpath;
1317199767f8SToomas Soome 	/*
13180eb1a43fSToomas Soome 	 * Rebuild list of module directories if it changed
1319199767f8SToomas Soome 	 */
13200eb1a43fSToomas Soome 	STAILQ_FOREACH(mdp, &moduledir_list, d_link)
13210eb1a43fSToomas Soome 		mdp->d_flags |= MDIR_REMOVED;
13220eb1a43fSToomas Soome 
13230eb1a43fSToomas Soome 	for (ep = path; *ep != 0; ep++) {
13240eb1a43fSToomas Soome 		cp = ep;
13250eb1a43fSToomas Soome 		for (; *ep != 0 && *ep != ';'; ep++)
13260eb1a43fSToomas Soome 			;
13270eb1a43fSToomas Soome 		/*
13280eb1a43fSToomas Soome 		 * Ignore trailing slashes
13290eb1a43fSToomas Soome 		 */
13300eb1a43fSToomas Soome 		for (cplen = ep - cp; cplen > 1 && cp[cplen - 1] == '/';
13310eb1a43fSToomas Soome 		    cplen--)
13320eb1a43fSToomas Soome 			;
13330eb1a43fSToomas Soome 		STAILQ_FOREACH(mdp, &moduledir_list, d_link) {
13340eb1a43fSToomas Soome 			if (strlen(mdp->d_path) != cplen ||
13350eb1a43fSToomas Soome 			    bcmp(cp, mdp->d_path, cplen) != 0)
13360eb1a43fSToomas Soome 				continue;
13370eb1a43fSToomas Soome 			mdp->d_flags &= ~MDIR_REMOVED;
13380eb1a43fSToomas Soome 			break;
13390eb1a43fSToomas Soome 		}
13400eb1a43fSToomas Soome 		if (mdp == NULL) {
13410eb1a43fSToomas Soome 			mdp = malloc(sizeof (*mdp) + cplen + 1);
13420eb1a43fSToomas Soome 			if (mdp == NULL)
13430eb1a43fSToomas Soome 				return;
13440eb1a43fSToomas Soome 			mdp->d_path = (char *)(mdp + 1);
13450eb1a43fSToomas Soome 			bcopy(cp, mdp->d_path, cplen);
13460eb1a43fSToomas Soome 			mdp->d_path[cplen] = 0;
13470eb1a43fSToomas Soome 			mdp->d_hints = NULL;
13480eb1a43fSToomas Soome 			mdp->d_flags = 0;
13490eb1a43fSToomas Soome 			STAILQ_INSERT_TAIL(&moduledir_list, mdp, d_link);
13500eb1a43fSToomas Soome 		}
13510eb1a43fSToomas Soome 		if (*ep == '\0')
13520eb1a43fSToomas Soome 			break;
1353199767f8SToomas Soome 	}
13540eb1a43fSToomas Soome 	/*
13550eb1a43fSToomas Soome 	 * Delete unused directories if any
13560eb1a43fSToomas Soome 	 */
13570eb1a43fSToomas Soome 	mdp = STAILQ_FIRST(&moduledir_list);
13580eb1a43fSToomas Soome 	while (mdp) {
13590eb1a43fSToomas Soome 		if ((mdp->d_flags & MDIR_REMOVED) == 0) {
13600eb1a43fSToomas Soome 			mdp = STAILQ_NEXT(mdp, d_link);
13610eb1a43fSToomas Soome 		} else {
13620eb1a43fSToomas Soome 			free(mdp->d_hints);
13630eb1a43fSToomas Soome 			mtmp = mdp;
13640eb1a43fSToomas Soome 			mdp = STAILQ_NEXT(mdp, d_link);
13650eb1a43fSToomas Soome 			STAILQ_REMOVE(&moduledir_list, mtmp, moduledir, d_link);
13660eb1a43fSToomas Soome 			free(mtmp);
13670eb1a43fSToomas Soome 		}
1368199767f8SToomas Soome 	}
1369199767f8SToomas Soome }
1370