1479ab7f0SSascha Wildner /*-
2479ab7f0SSascha Wildner * Copyright (c) 1998 Michael Smith <msmith@freebsd.org>
3479ab7f0SSascha Wildner * All rights reserved.
4479ab7f0SSascha Wildner *
5479ab7f0SSascha Wildner * Redistribution and use in source and binary forms, with or without
6479ab7f0SSascha Wildner * modification, are permitted provided that the following conditions
7479ab7f0SSascha Wildner * are met:
8479ab7f0SSascha Wildner * 1. Redistributions of source code must retain the above copyright
9479ab7f0SSascha Wildner * notice, this list of conditions and the following disclaimer.
10479ab7f0SSascha Wildner * 2. Redistributions in binary form must reproduce the above copyright
11479ab7f0SSascha Wildner * notice, this list of conditions and the following disclaimer in the
12479ab7f0SSascha Wildner * documentation and/or other materials provided with the distribution.
13479ab7f0SSascha Wildner *
14479ab7f0SSascha Wildner * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15479ab7f0SSascha Wildner * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16479ab7f0SSascha Wildner * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17479ab7f0SSascha Wildner * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18479ab7f0SSascha Wildner * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19479ab7f0SSascha Wildner * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20479ab7f0SSascha Wildner * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21479ab7f0SSascha Wildner * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22479ab7f0SSascha Wildner * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23479ab7f0SSascha Wildner * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24479ab7f0SSascha Wildner * SUCH DAMAGE.
25479ab7f0SSascha Wildner *
26479ab7f0SSascha Wildner * $FreeBSD: src/sys/boot/common/module.c,v 1.25 2003/08/25 23:30:41 obrien Exp $
27479ab7f0SSascha Wildner */
28479ab7f0SSascha Wildner
29479ab7f0SSascha Wildner /*
30479ab7f0SSascha Wildner * file/module function dispatcher, support, etc.
31479ab7f0SSascha Wildner */
32479ab7f0SSascha Wildner
33479ab7f0SSascha Wildner #include <stand.h>
34479ab7f0SSascha Wildner #include <string.h>
35479ab7f0SSascha Wildner #include <sys/param.h>
36479ab7f0SSascha Wildner #include <sys/linker.h>
37479ab7f0SSascha Wildner #include <sys/module.h>
38479ab7f0SSascha Wildner #include <sys/queue.h>
39479ab7f0SSascha Wildner #ifndef EFI
40479ab7f0SSascha Wildner #include "libi386/libi386.h"
41479ab7f0SSascha Wildner #endif
42479ab7f0SSascha Wildner
43479ab7f0SSascha Wildner #include "bootstrap.h"
44479ab7f0SSascha Wildner
45479ab7f0SSascha Wildner #define MDIR_REMOVED 0x0001
46479ab7f0SSascha Wildner #define MDIR_NOHINTS 0x0002
47479ab7f0SSascha Wildner
48479ab7f0SSascha Wildner struct moduledir {
49479ab7f0SSascha Wildner char *d_path; /* path of modules directory */
50479ab7f0SSascha Wildner u_char *d_hints; /* content of linker.hints file */
51479ab7f0SSascha Wildner int d_hintsz; /* size of hints data */
52479ab7f0SSascha Wildner int d_flags;
53479ab7f0SSascha Wildner STAILQ_ENTRY(moduledir) d_link;
54479ab7f0SSascha Wildner };
55479ab7f0SSascha Wildner
56479ab7f0SSascha Wildner static int file_load(char *filename, vm_offset_t dest, struct preloaded_file **result);
57479ab7f0SSascha Wildner static int file_loadraw(char *type, char *name);
58479ab7f0SSascha Wildner static int file_load_dependencies(struct preloaded_file *base_mod);
59479ab7f0SSascha Wildner static char * file_search(const char *name, char **extlist);
60479ab7f0SSascha Wildner static struct kernel_module * file_findmodule(struct preloaded_file *fp, char *modname, struct mod_depend *verinfo);
61479ab7f0SSascha Wildner static int file_havepath(const char *name);
62479ab7f0SSascha Wildner static char *mod_searchmodule(char *name, struct mod_depend *verinfo);
63479ab7f0SSascha Wildner static void file_insert_tail(struct preloaded_file *mp);
64d5ee8190SAaron LI static struct file_metadata* metadata_next(struct file_metadata *base_mp, int type);
65479ab7f0SSascha Wildner static void moduledir_readhints(struct moduledir *mdp);
66479ab7f0SSascha Wildner static void moduledir_rebuild(void);
67479ab7f0SSascha Wildner
68479ab7f0SSascha Wildner /* load address should be tweaked by first module loaded (kernel) */
69479ab7f0SSascha Wildner static vm_offset_t loadaddr = 0;
70479ab7f0SSascha Wildner
71479ab7f0SSascha Wildner static const char *default_searchpath = "modules;KERNEL";
72479ab7f0SSascha Wildner static const char *local_module_path = "../modules.local";
73479ab7f0SSascha Wildner
74479ab7f0SSascha Wildner static STAILQ_HEAD(, moduledir) moduledir_list = STAILQ_HEAD_INITIALIZER(moduledir_list);
75479ab7f0SSascha Wildner
76479ab7f0SSascha Wildner struct preloaded_file *preloaded_files = NULL;
77479ab7f0SSascha Wildner
78479ab7f0SSascha Wildner static char *kld_ext_list[] = {
79479ab7f0SSascha Wildner ".ko",
80479ab7f0SSascha Wildner "",
81479ab7f0SSascha Wildner NULL
82479ab7f0SSascha Wildner };
83479ab7f0SSascha Wildner
84479ab7f0SSascha Wildner /*
85479ab7f0SSascha Wildner * load an object, either a disk file or code module.
86479ab7f0SSascha Wildner *
87479ab7f0SSascha Wildner * To load a file, the syntax is:
88479ab7f0SSascha Wildner *
89479ab7f0SSascha Wildner * load -t <type> <path>
90479ab7f0SSascha Wildner *
91479ab7f0SSascha Wildner * code modules are loaded as:
92479ab7f0SSascha Wildner *
93479ab7f0SSascha Wildner * load <path> <options>
94479ab7f0SSascha Wildner */
95479ab7f0SSascha Wildner COMMAND_SET(load, "load", "load a kernel or module", command_load);
96479ab7f0SSascha Wildner
97479ab7f0SSascha Wildner static int
command_load(int argc,char * argv[])98479ab7f0SSascha Wildner command_load(int argc, char *argv[])
99479ab7f0SSascha Wildner {
100479ab7f0SSascha Wildner char *typestr;
101479ab7f0SSascha Wildner int dofile, dokld, ch, error;
102479ab7f0SSascha Wildner
103479ab7f0SSascha Wildner dokld = dofile = 0;
104479ab7f0SSascha Wildner optind = 1;
105479ab7f0SSascha Wildner optreset = 1;
106479ab7f0SSascha Wildner typestr = NULL;
107479ab7f0SSascha Wildner if (argc == 1) {
108479ab7f0SSascha Wildner command_errmsg = "no filename specified";
109479ab7f0SSascha Wildner return(CMD_ERROR);
110479ab7f0SSascha Wildner }
111479ab7f0SSascha Wildner while ((ch = getopt(argc, argv, "kt:")) != -1) {
112479ab7f0SSascha Wildner switch(ch) {
113479ab7f0SSascha Wildner case 'k':
114479ab7f0SSascha Wildner dokld = 1;
115479ab7f0SSascha Wildner break;
116479ab7f0SSascha Wildner case 't':
117479ab7f0SSascha Wildner typestr = optarg;
118479ab7f0SSascha Wildner dofile = 1;
119479ab7f0SSascha Wildner break;
120479ab7f0SSascha Wildner case '?':
121479ab7f0SSascha Wildner default:
122479ab7f0SSascha Wildner /* getopt has already reported an error */
123479ab7f0SSascha Wildner return(CMD_OK);
124479ab7f0SSascha Wildner }
125479ab7f0SSascha Wildner }
126479ab7f0SSascha Wildner argv += (optind - 1);
127479ab7f0SSascha Wildner argc -= (optind - 1);
128479ab7f0SSascha Wildner
129479ab7f0SSascha Wildner /*
130479ab7f0SSascha Wildner * Request to load a raw file?
131479ab7f0SSascha Wildner */
132479ab7f0SSascha Wildner if (dofile) {
133479ab7f0SSascha Wildner if ((argc != 2) || (typestr == NULL) || (*typestr == 0)) {
134479ab7f0SSascha Wildner command_errmsg = "invalid load type";
135479ab7f0SSascha Wildner return(CMD_ERROR);
136479ab7f0SSascha Wildner }
137479ab7f0SSascha Wildner return(file_loadraw(typestr, argv[1]));
138479ab7f0SSascha Wildner }
139479ab7f0SSascha Wildner /*
140479ab7f0SSascha Wildner * Do we have explicit KLD load ?
141479ab7f0SSascha Wildner */
142479ab7f0SSascha Wildner if (dokld || file_havepath(argv[1])) {
143479ab7f0SSascha Wildner error = mod_loadkld(argv[1], argc - 2, argv + 2);
144479ab7f0SSascha Wildner if (error == EEXIST) {
145479ab7f0SSascha Wildner snprintf(command_errbuf, sizeof(command_errbuf),
146479ab7f0SSascha Wildner "warning: KLD '%s' already loaded", argv[1]);
147479ab7f0SSascha Wildner }
148479ab7f0SSascha Wildner return (error == 0 ? CMD_OK : CMD_ERROR);
149479ab7f0SSascha Wildner }
150479ab7f0SSascha Wildner /*
151479ab7f0SSascha Wildner * Looks like a request for a module.
152479ab7f0SSascha Wildner */
153479ab7f0SSascha Wildner error = mod_load(argv[1], NULL, argc - 2, argv + 2);
154479ab7f0SSascha Wildner if (error == EEXIST) {
155479ab7f0SSascha Wildner snprintf(command_errbuf, sizeof(command_errbuf),
156479ab7f0SSascha Wildner "warning: module '%s' already loaded", argv[1]);
157479ab7f0SSascha Wildner }
158479ab7f0SSascha Wildner return (error == 0 ? CMD_OK : CMD_ERROR);
159479ab7f0SSascha Wildner }
160479ab7f0SSascha Wildner
161479ab7f0SSascha Wildner COMMAND_SET(unload, "unload", "unload all modules", command_unload);
162479ab7f0SSascha Wildner
163479ab7f0SSascha Wildner static int
command_unload(int argc,char * argv[])164479ab7f0SSascha Wildner command_unload(int argc, char *argv[])
165479ab7f0SSascha Wildner {
166479ab7f0SSascha Wildner struct preloaded_file *fp;
167479ab7f0SSascha Wildner
168479ab7f0SSascha Wildner while (preloaded_files != NULL) {
169479ab7f0SSascha Wildner fp = preloaded_files;
170479ab7f0SSascha Wildner preloaded_files = preloaded_files->f_next;
171479ab7f0SSascha Wildner file_discard(fp);
172479ab7f0SSascha Wildner }
173479ab7f0SSascha Wildner loadaddr = 0;
174479ab7f0SSascha Wildner unsetenv("kernelname");
175479ab7f0SSascha Wildner return(CMD_OK);
176479ab7f0SSascha Wildner }
177479ab7f0SSascha Wildner
178479ab7f0SSascha Wildner COMMAND_SET(crc, "crc", "calculate crc for file", command_crc);
179479ab7f0SSascha Wildner
180479ab7f0SSascha Wildner uint32_t iscsi_crc32(const void *buf, size_t size);
181479ab7f0SSascha Wildner uint32_t iscsi_crc32_ext(const void *buf, size_t size, uint32_t ocrc);
182479ab7f0SSascha Wildner
183479ab7f0SSascha Wildner static int
command_crc(int argc,char * argv[])184479ab7f0SSascha Wildner command_crc(int argc, char *argv[])
185479ab7f0SSascha Wildner {
186479ab7f0SSascha Wildner char *name;
187479ab7f0SSascha Wildner char *cp;
188479ab7f0SSascha Wildner int i;
189479ab7f0SSascha Wildner int fd, got, tot;
190479ab7f0SSascha Wildner int error;
191479ab7f0SSascha Wildner uint32_t crc;
192479ab7f0SSascha Wildner char *buf;
193479ab7f0SSascha Wildner
194479ab7f0SSascha Wildner if (argc == 1) {
195479ab7f0SSascha Wildner command_errmsg = "no filename specified";
196479ab7f0SSascha Wildner return(CMD_ERROR);
197479ab7f0SSascha Wildner }
198479ab7f0SSascha Wildner buf = malloc(8192);
199479ab7f0SSascha Wildner
200479ab7f0SSascha Wildner error = 0;
201479ab7f0SSascha Wildner printf("size\tcrc\t name\n");
202479ab7f0SSascha Wildner for (i = 1; i < argc; ++i) {
203479ab7f0SSascha Wildner /* locate the file on the load path */
204479ab7f0SSascha Wildner cp = file_search(argv[i], NULL);
205479ab7f0SSascha Wildner if (cp == NULL) {
206479ab7f0SSascha Wildner snprintf(command_errbuf, sizeof(command_errbuf),
207479ab7f0SSascha Wildner "can't find '%s'", argv[i]);
208479ab7f0SSascha Wildner error = CMD_ERROR;
209479ab7f0SSascha Wildner break;
210479ab7f0SSascha Wildner }
211479ab7f0SSascha Wildner name = cp;
212479ab7f0SSascha Wildner
213479ab7f0SSascha Wildner if ((fd = rel_open(name, NULL, O_RDONLY)) < 0) {
214479ab7f0SSascha Wildner snprintf(command_errbuf, sizeof(command_errbuf),
215479ab7f0SSascha Wildner "can't open '%s': %s", name, strerror(errno));
216479ab7f0SSascha Wildner free(name);
217479ab7f0SSascha Wildner error = CMD_ERROR;
218479ab7f0SSascha Wildner break;
219479ab7f0SSascha Wildner }
220479ab7f0SSascha Wildner tot = 0;
221479ab7f0SSascha Wildner crc = 0;
222479ab7f0SSascha Wildner for (;;) {
223479ab7f0SSascha Wildner got = read(fd, buf, 8192);
224479ab7f0SSascha Wildner if (got == 0)
225479ab7f0SSascha Wildner break;
226479ab7f0SSascha Wildner if (got < 0) {
227479ab7f0SSascha Wildner printf("error reading '%s': %s\n",
228479ab7f0SSascha Wildner name, strerror(errno));
229479ab7f0SSascha Wildner break;
230479ab7f0SSascha Wildner }
231479ab7f0SSascha Wildner if (crc == 0)
232479ab7f0SSascha Wildner crc = iscsi_crc32(buf, got);
233479ab7f0SSascha Wildner else
234479ab7f0SSascha Wildner crc = iscsi_crc32_ext(buf, got, crc);
235479ab7f0SSascha Wildner tot += got;
236479ab7f0SSascha Wildner }
237479ab7f0SSascha Wildner printf("%7d %08x %s\n", tot, crc, name);
238479ab7f0SSascha Wildner free(name);
239479ab7f0SSascha Wildner close(fd);
240479ab7f0SSascha Wildner }
241479ab7f0SSascha Wildner free (buf);
242479ab7f0SSascha Wildner if (error == 0)
243479ab7f0SSascha Wildner error = CMD_OK;
244479ab7f0SSascha Wildner return error;
245479ab7f0SSascha Wildner }
246479ab7f0SSascha Wildner
247479ab7f0SSascha Wildner COMMAND_SET(lsmod, "lsmod", "list loaded modules", command_lsmod);
248479ab7f0SSascha Wildner
249479ab7f0SSascha Wildner static int
command_lsmod(int argc,char * argv[])250479ab7f0SSascha Wildner command_lsmod(int argc, char *argv[])
251479ab7f0SSascha Wildner {
252479ab7f0SSascha Wildner struct preloaded_file *fp;
253479ab7f0SSascha Wildner struct kernel_module *mp;
254479ab7f0SSascha Wildner struct file_metadata *md;
255479ab7f0SSascha Wildner char lbuf[80];
256479ab7f0SSascha Wildner int ch, verbose;
257479ab7f0SSascha Wildner
258479ab7f0SSascha Wildner verbose = 0;
259479ab7f0SSascha Wildner optind = 1;
260479ab7f0SSascha Wildner optreset = 1;
261479ab7f0SSascha Wildner while ((ch = getopt(argc, argv, "v")) != -1) {
262479ab7f0SSascha Wildner switch(ch) {
263479ab7f0SSascha Wildner case 'v':
264479ab7f0SSascha Wildner verbose = 1;
265479ab7f0SSascha Wildner break;
266479ab7f0SSascha Wildner case '?':
267479ab7f0SSascha Wildner default:
268479ab7f0SSascha Wildner /* getopt has already reported an error */
269479ab7f0SSascha Wildner return(CMD_OK);
270479ab7f0SSascha Wildner }
271479ab7f0SSascha Wildner }
272479ab7f0SSascha Wildner
273479ab7f0SSascha Wildner pager_open();
274479ab7f0SSascha Wildner for (fp = preloaded_files; fp; fp = fp->f_next) {
275479ab7f0SSascha Wildner sprintf(lbuf, " %p: %s (%s, 0x%lx)\n",
276479ab7f0SSascha Wildner (void *) fp->f_addr, fp->f_name, fp->f_type, (long) fp->f_size);
277479ab7f0SSascha Wildner pager_output(lbuf);
278479ab7f0SSascha Wildner if (fp->f_args != NULL) {
279479ab7f0SSascha Wildner pager_output(" args: ");
280479ab7f0SSascha Wildner pager_output(fp->f_args);
281479ab7f0SSascha Wildner pager_output("\n");
282479ab7f0SSascha Wildner }
283479ab7f0SSascha Wildner if (fp->f_modules) {
284479ab7f0SSascha Wildner pager_output(" modules: ");
285479ab7f0SSascha Wildner for (mp = fp->f_modules; mp; mp = mp->m_next) {
286479ab7f0SSascha Wildner sprintf(lbuf, "%s.%d ", mp->m_name, mp->m_version);
287479ab7f0SSascha Wildner pager_output(lbuf);
288479ab7f0SSascha Wildner }
289479ab7f0SSascha Wildner pager_output("\n");
290479ab7f0SSascha Wildner }
291479ab7f0SSascha Wildner if (verbose) {
292479ab7f0SSascha Wildner /* XXX could add some formatting smarts here to display some better */
293479ab7f0SSascha Wildner for (md = fp->f_metadata; md != NULL; md = md->md_next) {
294479ab7f0SSascha Wildner sprintf(lbuf, " 0x%04x, 0x%lx\n", md->md_type, (long) md->md_size);
295479ab7f0SSascha Wildner pager_output(lbuf);
296479ab7f0SSascha Wildner }
297479ab7f0SSascha Wildner }
298479ab7f0SSascha Wildner }
299479ab7f0SSascha Wildner pager_close();
300479ab7f0SSascha Wildner return(CMD_OK);
301479ab7f0SSascha Wildner }
302479ab7f0SSascha Wildner
303479ab7f0SSascha Wildner /*
304479ab7f0SSascha Wildner * File level interface, functions file_*
305479ab7f0SSascha Wildner */
306d5ee8190SAaron LI static int
file_load(char * filename,vm_offset_t dest,struct preloaded_file ** result)307479ab7f0SSascha Wildner file_load(char *filename, vm_offset_t dest, struct preloaded_file **result)
308479ab7f0SSascha Wildner {
309479ab7f0SSascha Wildner struct preloaded_file *fp;
310479ab7f0SSascha Wildner int error;
311479ab7f0SSascha Wildner int i;
312479ab7f0SSascha Wildner
313479ab7f0SSascha Wildner error = EFTYPE;
314479ab7f0SSascha Wildner for (i = 0, fp = NULL; file_formats[i] && fp == NULL; i++) {
315138a936fSAaron LI error = (file_formats[i]->l_load)(filename, dest, &fp);
316479ab7f0SSascha Wildner if (error == 0) {
317479ab7f0SSascha Wildner fp->f_loader = i; /* remember the loader */
318479ab7f0SSascha Wildner *result = fp;
319479ab7f0SSascha Wildner break;
320479ab7f0SSascha Wildner }
321479ab7f0SSascha Wildner if (error == EFTYPE)
322479ab7f0SSascha Wildner continue; /* Unknown to this handler? */
323479ab7f0SSascha Wildner if (error) {
324479ab7f0SSascha Wildner snprintf(command_errbuf, sizeof(command_errbuf),
325479ab7f0SSascha Wildner "can't load file '%s': %s", filename, strerror(error));
326479ab7f0SSascha Wildner break;
327479ab7f0SSascha Wildner }
328479ab7f0SSascha Wildner }
329479ab7f0SSascha Wildner return (error);
330479ab7f0SSascha Wildner }
331479ab7f0SSascha Wildner
332479ab7f0SSascha Wildner static int
file_load_dependencies(struct preloaded_file * base_file)333479ab7f0SSascha Wildner file_load_dependencies(struct preloaded_file *base_file)
334479ab7f0SSascha Wildner {
335479ab7f0SSascha Wildner struct file_metadata *md;
336479ab7f0SSascha Wildner struct preloaded_file *fp;
337479ab7f0SSascha Wildner struct mod_depend *verinfo;
338479ab7f0SSascha Wildner struct kernel_module *mp;
339479ab7f0SSascha Wildner char *dmodname;
340479ab7f0SSascha Wildner int error;
341479ab7f0SSascha Wildner
342479ab7f0SSascha Wildner md = file_findmetadata(base_file, MODINFOMD_DEPLIST);
343479ab7f0SSascha Wildner if (md == NULL)
344479ab7f0SSascha Wildner return (0);
345d5ee8190SAaron LI
346479ab7f0SSascha Wildner error = 0;
347479ab7f0SSascha Wildner do {
348479ab7f0SSascha Wildner verinfo = (struct mod_depend*)md->md_data;
349479ab7f0SSascha Wildner dmodname = (char *)(verinfo + 1);
350479ab7f0SSascha Wildner if (file_findmodule(NULL, dmodname, verinfo) == NULL) {
351479ab7f0SSascha Wildner printf("loading required module '%s'\n", dmodname);
352479ab7f0SSascha Wildner error = mod_load(dmodname, verinfo, 0, NULL);
353479ab7f0SSascha Wildner if (error)
354479ab7f0SSascha Wildner break;
355479ab7f0SSascha Wildner /*
356479ab7f0SSascha Wildner * If module loaded via kld name which isn't listed
357479ab7f0SSascha Wildner * in the linker.hints file, we should check if it have
358479ab7f0SSascha Wildner * required version.
359479ab7f0SSascha Wildner */
360479ab7f0SSascha Wildner mp = file_findmodule(NULL, dmodname, verinfo);
361479ab7f0SSascha Wildner if (mp == NULL) {
362479ab7f0SSascha Wildner snprintf(command_errbuf, sizeof(command_errbuf),
363479ab7f0SSascha Wildner "module '%s' exists but with wrong version", dmodname);
364479ab7f0SSascha Wildner error = ENOENT;
365479ab7f0SSascha Wildner break;
366479ab7f0SSascha Wildner }
367479ab7f0SSascha Wildner }
368479ab7f0SSascha Wildner md = metadata_next(md, MODINFOMD_DEPLIST);
369479ab7f0SSascha Wildner } while (md);
370479ab7f0SSascha Wildner if (!error)
371479ab7f0SSascha Wildner return (0);
372d5ee8190SAaron LI
373479ab7f0SSascha Wildner /* Load failed; discard everything */
374479ab7f0SSascha Wildner while (base_file != NULL) {
375479ab7f0SSascha Wildner fp = base_file;
376479ab7f0SSascha Wildner base_file = base_file->f_next;
377479ab7f0SSascha Wildner file_discard(fp);
378479ab7f0SSascha Wildner }
379479ab7f0SSascha Wildner return (error);
380479ab7f0SSascha Wildner }
381479ab7f0SSascha Wildner
382479ab7f0SSascha Wildner /*
383479ab7f0SSascha Wildner * We've been asked to load (name) as (type), so just suck it in,
384479ab7f0SSascha Wildner * no arguments or anything.
385479ab7f0SSascha Wildner */
386d5ee8190SAaron LI static int
file_loadraw(char * type,char * name)387479ab7f0SSascha Wildner file_loadraw(char *type, char *name)
388479ab7f0SSascha Wildner {
389479ab7f0SSascha Wildner struct preloaded_file *fp;
390479ab7f0SSascha Wildner char *cp;
391479ab7f0SSascha Wildner int fd, got;
392479ab7f0SSascha Wildner vm_offset_t laddr;
393479ab7f0SSascha Wildner
394479ab7f0SSascha Wildner /* We can't load first */
395d5ee8190SAaron LI if (file_findfile(NULL, NULL) == NULL) {
396479ab7f0SSascha Wildner command_errmsg = "can't load file before kernel";
397479ab7f0SSascha Wildner return(CMD_ERROR);
398479ab7f0SSascha Wildner }
399479ab7f0SSascha Wildner
400479ab7f0SSascha Wildner /* locate the file on the load path */
401479ab7f0SSascha Wildner cp = file_search(name, NULL);
402479ab7f0SSascha Wildner if (cp == NULL) {
403479ab7f0SSascha Wildner snprintf(command_errbuf, sizeof(command_errbuf),
404479ab7f0SSascha Wildner "can't find '%s'", name);
405479ab7f0SSascha Wildner return(CMD_ERROR);
406479ab7f0SSascha Wildner }
407479ab7f0SSascha Wildner name = cp;
408479ab7f0SSascha Wildner
409479ab7f0SSascha Wildner if ((fd = rel_open(name, NULL, O_RDONLY)) < 0) {
410479ab7f0SSascha Wildner snprintf(command_errbuf, sizeof(command_errbuf),
411479ab7f0SSascha Wildner "can't open '%s': %s", name, strerror(errno));
412479ab7f0SSascha Wildner free(name);
413479ab7f0SSascha Wildner return(CMD_ERROR);
414479ab7f0SSascha Wildner }
415479ab7f0SSascha Wildner
416479ab7f0SSascha Wildner laddr = loadaddr;
417479ab7f0SSascha Wildner for (;;) {
418479ab7f0SSascha Wildner /* read in 4k chunks; size is not really important */
419479ab7f0SSascha Wildner #ifndef EFI
420479ab7f0SSascha Wildner if (laddr + 4096 > heapbase) {
421479ab7f0SSascha Wildner snprintf(command_errbuf, sizeof(command_errbuf),
422479ab7f0SSascha Wildner "error reading '%s': out of load memory", name);
423479ab7f0SSascha Wildner free(name);
424479ab7f0SSascha Wildner close(fd);
425479ab7f0SSascha Wildner return(CMD_ERROR);
426479ab7f0SSascha Wildner }
427479ab7f0SSascha Wildner #endif
428479ab7f0SSascha Wildner got = archsw.arch_readin(fd, laddr, 4096);
429479ab7f0SSascha Wildner if (got == 0) /* end of file */
430479ab7f0SSascha Wildner break;
431479ab7f0SSascha Wildner if (got < 0) { /* error */
432479ab7f0SSascha Wildner snprintf(command_errbuf, sizeof(command_errbuf),
433479ab7f0SSascha Wildner "error reading '%s': %s", name, strerror(errno));
434479ab7f0SSascha Wildner free(name);
435479ab7f0SSascha Wildner close(fd);
436479ab7f0SSascha Wildner return(CMD_ERROR);
437479ab7f0SSascha Wildner }
438479ab7f0SSascha Wildner laddr += got;
439479ab7f0SSascha Wildner }
440479ab7f0SSascha Wildner
441479ab7f0SSascha Wildner /* Looks OK so far; create & populate control structure */
442479ab7f0SSascha Wildner fp = file_alloc();
443*34f6038dSAaron LI fp->f_name = rel_rootpath(name);
444479ab7f0SSascha Wildner fp->f_type = strdup(type);
445479ab7f0SSascha Wildner fp->f_args = NULL;
446479ab7f0SSascha Wildner fp->f_metadata = NULL;
447479ab7f0SSascha Wildner fp->f_loader = -1;
448479ab7f0SSascha Wildner fp->f_addr = loadaddr;
449479ab7f0SSascha Wildner fp->f_size = laddr - loadaddr;
450479ab7f0SSascha Wildner
451479ab7f0SSascha Wildner /* recognise space consumption */
452479ab7f0SSascha Wildner loadaddr = laddr;
453479ab7f0SSascha Wildner
454479ab7f0SSascha Wildner /* Add to the list of loaded files */
455479ab7f0SSascha Wildner file_insert_tail(fp);
456479ab7f0SSascha Wildner close(fd);
457479ab7f0SSascha Wildner return(CMD_OK);
458479ab7f0SSascha Wildner }
459479ab7f0SSascha Wildner
460479ab7f0SSascha Wildner /*
461479ab7f0SSascha Wildner * Load the module (name), pass it (argc),(argv), add container file
462479ab7f0SSascha Wildner * to the list of loaded files.
463479ab7f0SSascha Wildner * If module is already loaded just assign new argc/argv.
464479ab7f0SSascha Wildner */
465479ab7f0SSascha Wildner int
mod_load(char * modname,struct mod_depend * verinfo,int argc,char * argv[])466479ab7f0SSascha Wildner mod_load(char *modname, struct mod_depend *verinfo, int argc, char *argv[])
467479ab7f0SSascha Wildner {
468479ab7f0SSascha Wildner struct kernel_module *mp;
469479ab7f0SSascha Wildner int err;
470479ab7f0SSascha Wildner char *filename;
471479ab7f0SSascha Wildner
472479ab7f0SSascha Wildner if (file_havepath(modname)) {
473479ab7f0SSascha Wildner printf("Warning: mod_load() called instead of mod_loadkld() for module '%s'\n", modname);
474479ab7f0SSascha Wildner return (mod_loadkld(modname, argc, argv));
475479ab7f0SSascha Wildner }
476479ab7f0SSascha Wildner /* see if module is already loaded */
477479ab7f0SSascha Wildner mp = file_findmodule(NULL, modname, verinfo);
478479ab7f0SSascha Wildner if (mp) {
479479ab7f0SSascha Wildner #ifdef moduleargs
480479ab7f0SSascha Wildner if (mp->m_args)
481479ab7f0SSascha Wildner free(mp->m_args);
482479ab7f0SSascha Wildner mp->m_args = unargv(argc, argv);
483479ab7f0SSascha Wildner #endif
484479ab7f0SSascha Wildner snprintf(command_errbuf, sizeof(command_errbuf),
485479ab7f0SSascha Wildner "warning: module '%s' already loaded", mp->m_name);
486479ab7f0SSascha Wildner return (0);
487479ab7f0SSascha Wildner }
488479ab7f0SSascha Wildner /* locate file with the module on the search path */
489479ab7f0SSascha Wildner filename = mod_searchmodule(modname, verinfo);
490479ab7f0SSascha Wildner if (filename == NULL) {
491479ab7f0SSascha Wildner snprintf(command_errbuf, sizeof(command_errbuf),
492479ab7f0SSascha Wildner "can't find '%s'", modname);
493479ab7f0SSascha Wildner return (ENOENT);
494479ab7f0SSascha Wildner }
495479ab7f0SSascha Wildner err = mod_loadkld(filename, argc, argv);
496479ab7f0SSascha Wildner return (err);
497479ab7f0SSascha Wildner }
498479ab7f0SSascha Wildner
499479ab7f0SSascha Wildner /*
500479ab7f0SSascha Wildner * Load specified KLD. If path is omitted, then try to locate it via
501479ab7f0SSascha Wildner * search path.
502479ab7f0SSascha Wildner */
503479ab7f0SSascha Wildner int
mod_loadkld(const char * kldname,int argc,char * argv[])504479ab7f0SSascha Wildner mod_loadkld(const char *kldname, int argc, char *argv[])
505479ab7f0SSascha Wildner {
506479ab7f0SSascha Wildner struct preloaded_file *fp, *last_file;
507479ab7f0SSascha Wildner int err;
508479ab7f0SSascha Wildner char *filename;
509479ab7f0SSascha Wildner
510479ab7f0SSascha Wildner /*
511479ab7f0SSascha Wildner * Get fully qualified KLD name
512479ab7f0SSascha Wildner */
513479ab7f0SSascha Wildner filename = file_search(kldname, kld_ext_list);
514479ab7f0SSascha Wildner if (filename == NULL) {
515479ab7f0SSascha Wildner snprintf(command_errbuf, sizeof(command_errbuf),
516479ab7f0SSascha Wildner "can't find '%s'", kldname);
517479ab7f0SSascha Wildner return (ENOENT);
518479ab7f0SSascha Wildner }
519479ab7f0SSascha Wildner /*
520479ab7f0SSascha Wildner * Check if KLD already loaded
521479ab7f0SSascha Wildner */
522479ab7f0SSascha Wildner fp = file_findfile(filename, NULL);
523479ab7f0SSascha Wildner if (fp) {
524479ab7f0SSascha Wildner snprintf(command_errbuf, sizeof(command_errbuf),
525479ab7f0SSascha Wildner "warning: KLD '%s' already loaded", filename);
526479ab7f0SSascha Wildner free(filename);
527479ab7f0SSascha Wildner return (0);
528479ab7f0SSascha Wildner }
529479ab7f0SSascha Wildner for (last_file = preloaded_files;
530479ab7f0SSascha Wildner last_file != NULL && last_file->f_next != NULL;
531479ab7f0SSascha Wildner last_file = last_file->f_next)
532479ab7f0SSascha Wildner ;
533479ab7f0SSascha Wildner
534479ab7f0SSascha Wildner do {
535479ab7f0SSascha Wildner err = file_load(filename, loadaddr, &fp);
536479ab7f0SSascha Wildner if (err)
537479ab7f0SSascha Wildner break;
538479ab7f0SSascha Wildner fp->f_args = unargv(argc, argv);
539479ab7f0SSascha Wildner loadaddr = fp->f_addr + fp->f_size;
540479ab7f0SSascha Wildner file_insert_tail(fp); /* Add to the list of loaded files */
541479ab7f0SSascha Wildner if (file_load_dependencies(fp) != 0) {
542479ab7f0SSascha Wildner err = ENOENT;
543479ab7f0SSascha Wildner last_file->f_next = NULL;
544479ab7f0SSascha Wildner loadaddr = last_file->f_addr + last_file->f_size;
545479ab7f0SSascha Wildner fp = NULL;
546479ab7f0SSascha Wildner break;
547479ab7f0SSascha Wildner }
548479ab7f0SSascha Wildner } while(0);
549479ab7f0SSascha Wildner if (err == EFTYPE)
550479ab7f0SSascha Wildner snprintf(command_errbuf, sizeof(command_errbuf),
551479ab7f0SSascha Wildner "don't know how to load module '%s'", filename);
552479ab7f0SSascha Wildner if (err && fp)
553479ab7f0SSascha Wildner file_discard(fp);
554479ab7f0SSascha Wildner free(filename);
555479ab7f0SSascha Wildner return (err);
556479ab7f0SSascha Wildner }
557479ab7f0SSascha Wildner
558479ab7f0SSascha Wildner /*
559479ab7f0SSascha Wildner * Find a file matching (name) and (type).
560479ab7f0SSascha Wildner * NULL may be passed as a wildcard to either.
561479ab7f0SSascha Wildner */
562479ab7f0SSascha Wildner struct preloaded_file *
file_findfile(char * name,char * type)563479ab7f0SSascha Wildner file_findfile(char *name, char *type)
564479ab7f0SSascha Wildner {
565479ab7f0SSascha Wildner struct preloaded_file *fp;
566*34f6038dSAaron LI char *rootpath;
567*34f6038dSAaron LI
568*34f6038dSAaron LI rootpath = NULL;
569*34f6038dSAaron LI if (name != NULL)
570*34f6038dSAaron LI rootpath = rel_rootpath(name);
571479ab7f0SSascha Wildner
572479ab7f0SSascha Wildner for (fp = preloaded_files; fp != NULL; fp = fp->f_next) {
573*34f6038dSAaron LI if (((rootpath == NULL) || !strcmp(rootpath, fp->f_name)) &&
574479ab7f0SSascha Wildner ((type == NULL) || !strcmp(type, fp->f_type)))
575479ab7f0SSascha Wildner break;
576479ab7f0SSascha Wildner }
577*34f6038dSAaron LI
578*34f6038dSAaron LI if (rootpath != NULL)
579*34f6038dSAaron LI free(rootpath);
580479ab7f0SSascha Wildner return (fp);
581479ab7f0SSascha Wildner }
582479ab7f0SSascha Wildner
583479ab7f0SSascha Wildner /*
584479ab7f0SSascha Wildner * Find a module matching (name) inside of given file.
585479ab7f0SSascha Wildner * NULL may be passed as a wildcard.
586479ab7f0SSascha Wildner */
587d5ee8190SAaron LI static struct kernel_module *
file_findmodule(struct preloaded_file * fp,char * modname,struct mod_depend * verinfo)588479ab7f0SSascha Wildner file_findmodule(struct preloaded_file *fp, char *modname,
589479ab7f0SSascha Wildner struct mod_depend *verinfo)
590479ab7f0SSascha Wildner {
591479ab7f0SSascha Wildner struct kernel_module *mp, *best;
592479ab7f0SSascha Wildner int bestver, mver;
593479ab7f0SSascha Wildner
594479ab7f0SSascha Wildner if (fp == NULL) {
595479ab7f0SSascha Wildner for (fp = preloaded_files; fp; fp = fp->f_next) {
596479ab7f0SSascha Wildner mp = file_findmodule(fp, modname, verinfo);
597479ab7f0SSascha Wildner if (mp)
598479ab7f0SSascha Wildner return (mp);
599479ab7f0SSascha Wildner }
600479ab7f0SSascha Wildner return (NULL);
601479ab7f0SSascha Wildner }
602d5ee8190SAaron LI
603479ab7f0SSascha Wildner best = NULL;
604479ab7f0SSascha Wildner bestver = 0;
605479ab7f0SSascha Wildner for (mp = fp->f_modules; mp; mp = mp->m_next) {
606479ab7f0SSascha Wildner if (strcmp(modname, mp->m_name) == 0) {
607479ab7f0SSascha Wildner if (verinfo == NULL)
608479ab7f0SSascha Wildner return (mp);
609479ab7f0SSascha Wildner mver = mp->m_version;
610479ab7f0SSascha Wildner if (mver == verinfo->md_ver_preferred)
611479ab7f0SSascha Wildner return (mp);
612479ab7f0SSascha Wildner if (mver >= verinfo->md_ver_minimum &&
613479ab7f0SSascha Wildner mver <= verinfo->md_ver_maximum &&
614479ab7f0SSascha Wildner mver > bestver) {
615479ab7f0SSascha Wildner best = mp;
616479ab7f0SSascha Wildner bestver = mver;
617479ab7f0SSascha Wildner }
618479ab7f0SSascha Wildner }
619479ab7f0SSascha Wildner }
620479ab7f0SSascha Wildner return (best);
621479ab7f0SSascha Wildner }
622479ab7f0SSascha Wildner /*
623479ab7f0SSascha Wildner * Make a copy of (size) bytes of data from (p), and associate them as
624479ab7f0SSascha Wildner * metadata of (type) to the module (mp).
625479ab7f0SSascha Wildner */
626479ab7f0SSascha Wildner void
file_addmetadata(struct preloaded_file * fp,int type,size_t size,void * p)627479ab7f0SSascha Wildner file_addmetadata(struct preloaded_file *fp, int type, size_t size, void *p)
628479ab7f0SSascha Wildner {
629479ab7f0SSascha Wildner struct file_metadata *md;
630479ab7f0SSascha Wildner
631479ab7f0SSascha Wildner md = malloc(sizeof(struct file_metadata) - sizeof(md->md_data) + size);
632479ab7f0SSascha Wildner md->md_size = size;
633479ab7f0SSascha Wildner md->md_type = type;
634479ab7f0SSascha Wildner bcopy(p, md->md_data, size);
635479ab7f0SSascha Wildner md->md_next = fp->f_metadata;
636479ab7f0SSascha Wildner fp->f_metadata = md;
637479ab7f0SSascha Wildner }
638479ab7f0SSascha Wildner
639479ab7f0SSascha Wildner /*
640479ab7f0SSascha Wildner * Find a metadata object of (type) associated with the file (fp)
641479ab7f0SSascha Wildner */
642479ab7f0SSascha Wildner struct file_metadata *
file_findmetadata(struct preloaded_file * fp,int type)643479ab7f0SSascha Wildner file_findmetadata(struct preloaded_file *fp, int type)
644479ab7f0SSascha Wildner {
645479ab7f0SSascha Wildner struct file_metadata *md;
646479ab7f0SSascha Wildner
647479ab7f0SSascha Wildner for (md = fp->f_metadata; md != NULL; md = md->md_next)
648479ab7f0SSascha Wildner if (md->md_type == type)
649479ab7f0SSascha Wildner break;
650479ab7f0SSascha Wildner return(md);
651479ab7f0SSascha Wildner }
652479ab7f0SSascha Wildner
653d5ee8190SAaron LI static struct file_metadata *
metadata_next(struct file_metadata * md,int type)654479ab7f0SSascha Wildner metadata_next(struct file_metadata *md, int type)
655479ab7f0SSascha Wildner {
656479ab7f0SSascha Wildner if (md == NULL)
657479ab7f0SSascha Wildner return (NULL);
658479ab7f0SSascha Wildner while((md = md->md_next) != NULL)
659479ab7f0SSascha Wildner if (md->md_type == type)
660479ab7f0SSascha Wildner break;
661479ab7f0SSascha Wildner return (md);
662479ab7f0SSascha Wildner }
663479ab7f0SSascha Wildner
664479ab7f0SSascha Wildner static char *emptyextlist[] = { "", NULL };
665479ab7f0SSascha Wildner
666479ab7f0SSascha Wildner /*
667479ab7f0SSascha Wildner * Check if the given file is in place and return full path to it.
668479ab7f0SSascha Wildner */
669479ab7f0SSascha Wildner static char *
file_lookup(const char * path,const char * name,int namelen,char ** extlist)670479ab7f0SSascha Wildner file_lookup(const char *path, const char *name, int namelen, char **extlist)
671479ab7f0SSascha Wildner {
672479ab7f0SSascha Wildner struct stat st;
673479ab7f0SSascha Wildner char *result, *cp, **cpp;
674479ab7f0SSascha Wildner size_t pathlen, extlen;
675479ab7f0SSascha Wildner
676479ab7f0SSascha Wildner pathlen = strlen(path);
677479ab7f0SSascha Wildner extlen = 0;
678479ab7f0SSascha Wildner if (extlist == NULL)
679479ab7f0SSascha Wildner extlist = emptyextlist;
680479ab7f0SSascha Wildner for (cpp = extlist; *cpp; cpp++)
681479ab7f0SSascha Wildner extlen = MAX(extlen, strlen(*cpp));
682479ab7f0SSascha Wildner result = malloc(pathlen + namelen + extlen + 2 + 7 + 1);
683479ab7f0SSascha Wildner if (result == NULL)
684479ab7f0SSascha Wildner return (NULL);
685479ab7f0SSascha Wildner bcopy(path, result, pathlen);
686479ab7f0SSascha Wildner if (pathlen > 0 && result[pathlen - 1] != '/')
687479ab7f0SSascha Wildner result[pathlen++] = '/';
688479ab7f0SSascha Wildner cp = result + pathlen;
689479ab7f0SSascha Wildner bcopy(name, cp, namelen);
690479ab7f0SSascha Wildner cp += namelen;
691479ab7f0SSascha Wildner for (cpp = extlist; *cpp; cpp++) {
692479ab7f0SSascha Wildner strcpy(cp, *cpp);
693479ab7f0SSascha Wildner if (rel_stat(result, &st) == 0) {
694479ab7f0SSascha Wildner if (S_ISREG(st.st_mode)) {
695479ab7f0SSascha Wildner return result;
696479ab7f0SSascha Wildner } else if (S_ISDIR(st.st_mode)) {
697479ab7f0SSascha Wildner strcat(result, "/kernel");
698479ab7f0SSascha Wildner if (rel_stat(result, &st) == 0 && S_ISREG(st.st_mode)) {
699479ab7f0SSascha Wildner return result;
700479ab7f0SSascha Wildner }
701479ab7f0SSascha Wildner }
702479ab7f0SSascha Wildner }
703479ab7f0SSascha Wildner }
704479ab7f0SSascha Wildner free(result);
705479ab7f0SSascha Wildner return NULL;
706479ab7f0SSascha Wildner }
707479ab7f0SSascha Wildner
708479ab7f0SSascha Wildner /*
709479ab7f0SSascha Wildner * Check if file name have any qualifiers
710479ab7f0SSascha Wildner */
711479ab7f0SSascha Wildner static int
file_havepath(const char * name)712479ab7f0SSascha Wildner file_havepath(const char *name)
713479ab7f0SSascha Wildner {
714479ab7f0SSascha Wildner const char *cp;
715479ab7f0SSascha Wildner
716479ab7f0SSascha Wildner archsw.arch_getdev(NULL, name, &cp);
717479ab7f0SSascha Wildner return (cp != name || strchr(name, '/') != NULL);
718479ab7f0SSascha Wildner }
719479ab7f0SSascha Wildner
720479ab7f0SSascha Wildner /*
721479ab7f0SSascha Wildner * Attempt to find the file (name) on the module searchpath.
722479ab7f0SSascha Wildner * If (name) is qualified in any way, we simply check it and
723479ab7f0SSascha Wildner * return it or NULL. If it is not qualified, then we attempt
724479ab7f0SSascha Wildner * to construct a path using entries in the environment variable
725479ab7f0SSascha Wildner * module_path.
726479ab7f0SSascha Wildner *
727479ab7f0SSascha Wildner * The path we return a pointer to need never be freed, as we manage
728479ab7f0SSascha Wildner * it internally.
729479ab7f0SSascha Wildner */
730479ab7f0SSascha Wildner static char *
file_search(const char * name,char ** extlist)731479ab7f0SSascha Wildner file_search(const char *name, char **extlist)
732479ab7f0SSascha Wildner {
733479ab7f0SSascha Wildner struct moduledir *mdp;
734479ab7f0SSascha Wildner struct stat sb;
735479ab7f0SSascha Wildner char *result;
736479ab7f0SSascha Wildner int namelen;
737479ab7f0SSascha Wildner
738479ab7f0SSascha Wildner /* Don't look for nothing */
739d5ee8190SAaron LI if (name == NULL || *name == 0)
740479ab7f0SSascha Wildner return(NULL);
741479ab7f0SSascha Wildner
742479ab7f0SSascha Wildner /*
743479ab7f0SSascha Wildner * Qualified name. If it is a directory tag on
744479ab7f0SSascha Wildner * a "/kernel" to it.
745479ab7f0SSascha Wildner */
746479ab7f0SSascha Wildner if (file_havepath(name)) {
747479ab7f0SSascha Wildner /* Qualified, so just see if it exists */
748479ab7f0SSascha Wildner if (rel_stat(name, &sb) == 0) {
749479ab7f0SSascha Wildner if (S_ISDIR(sb.st_mode)) {
750479ab7f0SSascha Wildner result = malloc(strlen(name) + 7 + 1);
751479ab7f0SSascha Wildner sprintf(result, "%s/kernel", name);
752479ab7f0SSascha Wildner return(result);
753479ab7f0SSascha Wildner } else {
754479ab7f0SSascha Wildner return(strdup(name));
755479ab7f0SSascha Wildner }
756479ab7f0SSascha Wildner }
757479ab7f0SSascha Wildner return(NULL);
758479ab7f0SSascha Wildner }
759d5ee8190SAaron LI
760479ab7f0SSascha Wildner moduledir_rebuild();
761479ab7f0SSascha Wildner result = NULL;
762479ab7f0SSascha Wildner namelen = strlen(name);
763479ab7f0SSascha Wildner STAILQ_FOREACH(mdp, &moduledir_list, d_link) {
764479ab7f0SSascha Wildner result = file_lookup(mdp->d_path, name, namelen, extlist);
765479ab7f0SSascha Wildner if (result)
766479ab7f0SSascha Wildner break;
767479ab7f0SSascha Wildner }
768479ab7f0SSascha Wildner return(result);
769479ab7f0SSascha Wildner }
770479ab7f0SSascha Wildner
771479ab7f0SSascha Wildner #define INT_ALIGN(base, ptr) \
772479ab7f0SSascha Wildner ptr = (base) + roundup2((ptr) - (base), sizeof(int))
773479ab7f0SSascha Wildner
774479ab7f0SSascha Wildner static char *
mod_search_hints(struct moduledir * mdp,const char * modname,struct mod_depend * verinfo)775479ab7f0SSascha Wildner mod_search_hints(struct moduledir *mdp, const char *modname,
776479ab7f0SSascha Wildner struct mod_depend *verinfo)
777479ab7f0SSascha Wildner {
778479ab7f0SSascha Wildner u_char *cp, *recptr, *bufend, *best;
779479ab7f0SSascha Wildner char *result;
780479ab7f0SSascha Wildner int *intp, bestver, blen, clen, found, ival, modnamelen, reclen;
781479ab7f0SSascha Wildner
782479ab7f0SSascha Wildner moduledir_readhints(mdp);
783479ab7f0SSascha Wildner modnamelen = strlen(modname);
784479ab7f0SSascha Wildner found = 0;
785479ab7f0SSascha Wildner result = NULL;
786479ab7f0SSascha Wildner bestver = 0;
787479ab7f0SSascha Wildner if (mdp->d_hints == NULL)
788479ab7f0SSascha Wildner goto bad;
789479ab7f0SSascha Wildner recptr = mdp->d_hints;
790479ab7f0SSascha Wildner bufend = recptr + mdp->d_hintsz;
791479ab7f0SSascha Wildner clen = blen = 0;
792479ab7f0SSascha Wildner best = cp = NULL;
793479ab7f0SSascha Wildner while (recptr < bufend && !found) {
794479ab7f0SSascha Wildner intp = (int*)recptr;
795479ab7f0SSascha Wildner reclen = *intp++;
796479ab7f0SSascha Wildner ival = *intp++;
797479ab7f0SSascha Wildner cp = (char*)intp;
798479ab7f0SSascha Wildner switch (ival) {
799479ab7f0SSascha Wildner case MDT_VERSION:
800479ab7f0SSascha Wildner clen = *cp++;
801479ab7f0SSascha Wildner if (clen != modnamelen || bcmp(cp, modname, clen) != 0)
802479ab7f0SSascha Wildner break;
803479ab7f0SSascha Wildner cp += clen;
804479ab7f0SSascha Wildner INT_ALIGN(mdp->d_hints, cp);
805479ab7f0SSascha Wildner ival = *(int*)cp;
806479ab7f0SSascha Wildner cp += sizeof(int);
807479ab7f0SSascha Wildner clen = *cp++;
808479ab7f0SSascha Wildner if (verinfo == NULL || ival == verinfo->md_ver_preferred) {
809479ab7f0SSascha Wildner found = 1;
810479ab7f0SSascha Wildner break;
811479ab7f0SSascha Wildner }
812479ab7f0SSascha Wildner if (ival >= verinfo->md_ver_minimum &&
813479ab7f0SSascha Wildner ival <= verinfo->md_ver_maximum &&
814479ab7f0SSascha Wildner ival > bestver) {
815479ab7f0SSascha Wildner bestver = ival;
816479ab7f0SSascha Wildner best = cp;
817479ab7f0SSascha Wildner blen = clen;
818479ab7f0SSascha Wildner }
819479ab7f0SSascha Wildner break;
820479ab7f0SSascha Wildner default:
821479ab7f0SSascha Wildner break;
822479ab7f0SSascha Wildner }
823479ab7f0SSascha Wildner recptr += reclen + sizeof(int);
824479ab7f0SSascha Wildner }
825479ab7f0SSascha Wildner /*
826479ab7f0SSascha Wildner * Finally check if KLD is in the place
827479ab7f0SSascha Wildner */
828479ab7f0SSascha Wildner if (found)
829479ab7f0SSascha Wildner result = file_lookup(mdp->d_path, cp, clen, NULL);
830479ab7f0SSascha Wildner else if (best)
831479ab7f0SSascha Wildner result = file_lookup(mdp->d_path, best, blen, NULL);
832479ab7f0SSascha Wildner bad:
833479ab7f0SSascha Wildner /*
834479ab7f0SSascha Wildner * If nothing found or hints is absent - fallback to the old way
835479ab7f0SSascha Wildner * by using "kldname[.ko]" as module name.
836479ab7f0SSascha Wildner */
837479ab7f0SSascha Wildner if (!found && !bestver && result == NULL)
838479ab7f0SSascha Wildner result = file_lookup(mdp->d_path, modname, modnamelen, kld_ext_list);
839479ab7f0SSascha Wildner return result;
840479ab7f0SSascha Wildner }
841479ab7f0SSascha Wildner
842479ab7f0SSascha Wildner /*
843479ab7f0SSascha Wildner * Attempt to locate the file containing the module (name)
844479ab7f0SSascha Wildner */
845479ab7f0SSascha Wildner static char *
mod_searchmodule(char * name,struct mod_depend * verinfo)846479ab7f0SSascha Wildner mod_searchmodule(char *name, struct mod_depend *verinfo)
847479ab7f0SSascha Wildner {
848479ab7f0SSascha Wildner struct moduledir *mdp;
849479ab7f0SSascha Wildner char *result;
850479ab7f0SSascha Wildner
851479ab7f0SSascha Wildner moduledir_rebuild();
852479ab7f0SSascha Wildner /*
853479ab7f0SSascha Wildner * Now we ready to lookup module in the given directories
854479ab7f0SSascha Wildner */
855479ab7f0SSascha Wildner result = NULL;
856479ab7f0SSascha Wildner STAILQ_FOREACH(mdp, &moduledir_list, d_link) {
857479ab7f0SSascha Wildner result = mod_search_hints(mdp, name, verinfo);
858479ab7f0SSascha Wildner if (result)
859479ab7f0SSascha Wildner break;
860479ab7f0SSascha Wildner }
861479ab7f0SSascha Wildner
862479ab7f0SSascha Wildner return(result);
863479ab7f0SSascha Wildner }
864479ab7f0SSascha Wildner
865479ab7f0SSascha Wildner int
file_addmodule(struct preloaded_file * fp,char * modname,int version,struct kernel_module ** newmp)866479ab7f0SSascha Wildner file_addmodule(struct preloaded_file *fp, char *modname, int version,
867479ab7f0SSascha Wildner struct kernel_module **newmp)
868479ab7f0SSascha Wildner {
869479ab7f0SSascha Wildner struct kernel_module *mp;
870479ab7f0SSascha Wildner struct mod_depend mdepend;
871479ab7f0SSascha Wildner
872479ab7f0SSascha Wildner bzero(&mdepend, sizeof(mdepend));
873479ab7f0SSascha Wildner mdepend.md_ver_preferred = version;
874479ab7f0SSascha Wildner mp = file_findmodule(fp, modname, &mdepend);
875479ab7f0SSascha Wildner if (mp)
876479ab7f0SSascha Wildner return (EEXIST);
877479ab7f0SSascha Wildner mp = malloc(sizeof(struct kernel_module));
878479ab7f0SSascha Wildner if (mp == NULL)
879479ab7f0SSascha Wildner return (ENOMEM);
880479ab7f0SSascha Wildner bzero(mp, sizeof(struct kernel_module));
881479ab7f0SSascha Wildner mp->m_name = strdup(modname);
882479ab7f0SSascha Wildner mp->m_version = version;
883479ab7f0SSascha Wildner mp->m_fp = fp;
884479ab7f0SSascha Wildner mp->m_next = fp->f_modules;
885479ab7f0SSascha Wildner fp->f_modules = mp;
886479ab7f0SSascha Wildner if (newmp)
887479ab7f0SSascha Wildner *newmp = mp;
888479ab7f0SSascha Wildner return (0);
889479ab7f0SSascha Wildner }
890479ab7f0SSascha Wildner
891479ab7f0SSascha Wildner /*
892479ab7f0SSascha Wildner * Throw a file away
893479ab7f0SSascha Wildner */
894479ab7f0SSascha Wildner void
file_discard(struct preloaded_file * fp)895479ab7f0SSascha Wildner file_discard(struct preloaded_file *fp)
896479ab7f0SSascha Wildner {
897479ab7f0SSascha Wildner struct file_metadata *md, *md1;
898479ab7f0SSascha Wildner struct kernel_module *mp, *mp1;
899d5ee8190SAaron LI
900479ab7f0SSascha Wildner if (fp == NULL)
901479ab7f0SSascha Wildner return;
902d5ee8190SAaron LI
903479ab7f0SSascha Wildner md = fp->f_metadata;
904479ab7f0SSascha Wildner while (md) {
905479ab7f0SSascha Wildner md1 = md;
906479ab7f0SSascha Wildner md = md->md_next;
907479ab7f0SSascha Wildner free(md1);
908479ab7f0SSascha Wildner }
909d5ee8190SAaron LI
910479ab7f0SSascha Wildner mp = fp->f_modules;
911479ab7f0SSascha Wildner while (mp) {
912479ab7f0SSascha Wildner if (mp->m_name)
913479ab7f0SSascha Wildner free(mp->m_name);
914d5ee8190SAaron LI #ifdef moduleargs
915d5ee8190SAaron LI if (mp->m_args)
916d5ee8190SAaron LI free(mp->m_args);
917d5ee8190SAaron LI #endif
918479ab7f0SSascha Wildner mp1 = mp;
919479ab7f0SSascha Wildner mp = mp->m_next;
920479ab7f0SSascha Wildner free(mp1);
921479ab7f0SSascha Wildner }
922d5ee8190SAaron LI
923479ab7f0SSascha Wildner if (fp->f_name != NULL)
924479ab7f0SSascha Wildner free(fp->f_name);
925479ab7f0SSascha Wildner if (fp->f_type != NULL)
926479ab7f0SSascha Wildner free(fp->f_type);
927479ab7f0SSascha Wildner if (fp->f_args != NULL)
928479ab7f0SSascha Wildner free(fp->f_args);
929479ab7f0SSascha Wildner free(fp);
930479ab7f0SSascha Wildner }
931479ab7f0SSascha Wildner
932479ab7f0SSascha Wildner /*
933479ab7f0SSascha Wildner * Allocate a new file; must be used instead of malloc()
934479ab7f0SSascha Wildner * to ensure safe initialisation.
935479ab7f0SSascha Wildner */
936479ab7f0SSascha Wildner struct preloaded_file *
file_alloc(void)937479ab7f0SSascha Wildner file_alloc(void)
938479ab7f0SSascha Wildner {
939479ab7f0SSascha Wildner struct preloaded_file *fp;
940479ab7f0SSascha Wildner
941479ab7f0SSascha Wildner if ((fp = malloc(sizeof(struct preloaded_file))) != NULL) {
942479ab7f0SSascha Wildner bzero(fp, sizeof(struct preloaded_file));
943479ab7f0SSascha Wildner }
944479ab7f0SSascha Wildner return (fp);
945479ab7f0SSascha Wildner }
946479ab7f0SSascha Wildner
947479ab7f0SSascha Wildner /*
948479ab7f0SSascha Wildner * Add a module to the chain
949479ab7f0SSascha Wildner */
950479ab7f0SSascha Wildner static void
file_insert_tail(struct preloaded_file * fp)951479ab7f0SSascha Wildner file_insert_tail(struct preloaded_file *fp)
952479ab7f0SSascha Wildner {
953479ab7f0SSascha Wildner struct preloaded_file *cm;
954479ab7f0SSascha Wildner
955479ab7f0SSascha Wildner /* Append to list of loaded file */
956479ab7f0SSascha Wildner fp->f_next = NULL;
957479ab7f0SSascha Wildner if (preloaded_files == NULL) {
958479ab7f0SSascha Wildner preloaded_files = fp;
959479ab7f0SSascha Wildner } else {
960479ab7f0SSascha Wildner for (cm = preloaded_files; cm->f_next != NULL; cm = cm->f_next)
961479ab7f0SSascha Wildner ;
962479ab7f0SSascha Wildner cm->f_next = fp;
963479ab7f0SSascha Wildner }
964479ab7f0SSascha Wildner }
965479ab7f0SSascha Wildner
966479ab7f0SSascha Wildner static char *
moduledir_fullpath(struct moduledir * mdp,const char * fname)967479ab7f0SSascha Wildner moduledir_fullpath(struct moduledir *mdp, const char *fname)
968479ab7f0SSascha Wildner {
969479ab7f0SSascha Wildner char *cp;
970479ab7f0SSascha Wildner
971479ab7f0SSascha Wildner cp = malloc(strlen(mdp->d_path) + strlen(fname) + 2);
972479ab7f0SSascha Wildner if (cp == NULL)
973479ab7f0SSascha Wildner return NULL;
974479ab7f0SSascha Wildner strcpy(cp, mdp->d_path);
975479ab7f0SSascha Wildner strcat(cp, "/");
976479ab7f0SSascha Wildner strcat(cp, fname);
977479ab7f0SSascha Wildner return (cp);
978479ab7f0SSascha Wildner }
979479ab7f0SSascha Wildner
980479ab7f0SSascha Wildner /*
981479ab7f0SSascha Wildner * Read linker.hints file into memory performing some sanity checks.
982479ab7f0SSascha Wildner */
983479ab7f0SSascha Wildner static void
moduledir_readhints(struct moduledir * mdp)984479ab7f0SSascha Wildner moduledir_readhints(struct moduledir *mdp)
985479ab7f0SSascha Wildner {
986479ab7f0SSascha Wildner struct stat st;
987479ab7f0SSascha Wildner char *path;
988479ab7f0SSascha Wildner int fd, size, version;
989479ab7f0SSascha Wildner
990479ab7f0SSascha Wildner if (mdp->d_hints != NULL || (mdp->d_flags & MDIR_NOHINTS))
991479ab7f0SSascha Wildner return;
992479ab7f0SSascha Wildner path = moduledir_fullpath(mdp, "linker.hints");
993479ab7f0SSascha Wildner if (rel_stat(path, &st) != 0 ||
994479ab7f0SSascha Wildner st.st_size < (ssize_t)(sizeof(version) + sizeof(int)) ||
995479ab7f0SSascha Wildner st.st_size > 100 * 1024 || (fd = rel_open(path, NULL, O_RDONLY)) < 0) {
996479ab7f0SSascha Wildner free(path);
997479ab7f0SSascha Wildner mdp->d_flags |= MDIR_NOHINTS;
998479ab7f0SSascha Wildner return;
999479ab7f0SSascha Wildner }
1000479ab7f0SSascha Wildner free(path);
1001479ab7f0SSascha Wildner size = read(fd, &version, sizeof(version));
1002479ab7f0SSascha Wildner if (size != sizeof(version) || version != LINKER_HINTS_VERSION)
1003479ab7f0SSascha Wildner goto bad;
1004479ab7f0SSascha Wildner size = st.st_size - size;
1005479ab7f0SSascha Wildner mdp->d_hints = malloc(size);
1006479ab7f0SSascha Wildner if (mdp->d_hints == NULL)
1007479ab7f0SSascha Wildner goto bad;
1008479ab7f0SSascha Wildner if (read(fd, mdp->d_hints, size) != size)
1009479ab7f0SSascha Wildner goto bad;
1010479ab7f0SSascha Wildner mdp->d_hintsz = size;
1011479ab7f0SSascha Wildner close(fd);
1012479ab7f0SSascha Wildner return;
1013d5ee8190SAaron LI
1014479ab7f0SSascha Wildner bad:
1015479ab7f0SSascha Wildner close(fd);
1016479ab7f0SSascha Wildner if (mdp->d_hints) {
1017479ab7f0SSascha Wildner free(mdp->d_hints);
1018479ab7f0SSascha Wildner mdp->d_hints = NULL;
1019479ab7f0SSascha Wildner }
1020479ab7f0SSascha Wildner mdp->d_flags |= MDIR_NOHINTS;
1021479ab7f0SSascha Wildner }
1022479ab7f0SSascha Wildner
1023479ab7f0SSascha Wildner /*
1024479ab7f0SSascha Wildner * Extract directories from the ';' separated list, remove duplicates.
1025479ab7f0SSascha Wildner */
1026479ab7f0SSascha Wildner static void
moduledir_rebuild(void)1027479ab7f0SSascha Wildner moduledir_rebuild(void)
1028479ab7f0SSascha Wildner {
1029479ab7f0SSascha Wildner struct moduledir *mdp, *mtmp;
1030479ab7f0SSascha Wildner const char *path, *cp, *ep, *modlocal;
1031479ab7f0SSascha Wildner size_t cplen;
1032479ab7f0SSascha Wildner
1033479ab7f0SSascha Wildner path = getenv("module_path");
1034479ab7f0SSascha Wildner if (path == NULL)
1035479ab7f0SSascha Wildner path = default_searchpath;
1036479ab7f0SSascha Wildner /*
1037479ab7f0SSascha Wildner * Rebuild list of module directories if it changed
1038479ab7f0SSascha Wildner */
1039479ab7f0SSascha Wildner STAILQ_FOREACH(mdp, &moduledir_list, d_link)
1040479ab7f0SSascha Wildner mdp->d_flags |= MDIR_REMOVED;
1041479ab7f0SSascha Wildner
1042479ab7f0SSascha Wildner for (ep = path; *ep != 0; ep++) {
1043479ab7f0SSascha Wildner cp = ep;
1044479ab7f0SSascha Wildner for (; *ep != 0 && *ep != ';'; ep++)
1045479ab7f0SSascha Wildner ;
1046479ab7f0SSascha Wildner /*
1047479ab7f0SSascha Wildner * Ignore trailing slashes
1048479ab7f0SSascha Wildner */
1049479ab7f0SSascha Wildner for (cplen = ep - cp; cplen > 1 && cp[cplen - 1] == '/'; cplen--)
1050479ab7f0SSascha Wildner ;
1051479ab7f0SSascha Wildner STAILQ_FOREACH(mdp, &moduledir_list, d_link) {
1052479ab7f0SSascha Wildner if (strlen(mdp->d_path) != cplen || bcmp(cp, mdp->d_path, cplen) != 0)
1053479ab7f0SSascha Wildner continue;
1054479ab7f0SSascha Wildner mdp->d_flags &= ~MDIR_REMOVED;
1055479ab7f0SSascha Wildner break;
1056479ab7f0SSascha Wildner }
1057479ab7f0SSascha Wildner if (mdp == NULL) {
1058479ab7f0SSascha Wildner mdp = malloc(sizeof(*mdp) + cplen + 1);
1059479ab7f0SSascha Wildner if (mdp == NULL)
1060479ab7f0SSascha Wildner return;
1061479ab7f0SSascha Wildner mdp->d_path = (char*)(mdp + 1);
1062479ab7f0SSascha Wildner bcopy(cp, mdp->d_path, cplen);
1063479ab7f0SSascha Wildner mdp->d_path[cplen] = 0;
1064479ab7f0SSascha Wildner mdp->d_hints = NULL;
1065479ab7f0SSascha Wildner mdp->d_flags = 0;
1066479ab7f0SSascha Wildner STAILQ_INSERT_TAIL(&moduledir_list, mdp, d_link);
1067479ab7f0SSascha Wildner }
1068479ab7f0SSascha Wildner if (*ep == 0)
1069479ab7f0SSascha Wildner break;
1070479ab7f0SSascha Wildner }
1071479ab7f0SSascha Wildner /*
1072479ab7f0SSascha Wildner * Include modules.local if requested
1073479ab7f0SSascha Wildner */
1074479ab7f0SSascha Wildner modlocal = getenv("local_modules");
1075479ab7f0SSascha Wildner if (modlocal != NULL && strcmp(modlocal, "YES") == 0) {
1076479ab7f0SSascha Wildner cp = local_module_path;
1077479ab7f0SSascha Wildner cplen = strlen(local_module_path);
1078479ab7f0SSascha Wildner STAILQ_FOREACH(mdp, &moduledir_list, d_link) {
1079479ab7f0SSascha Wildner if (strlen(mdp->d_path) != cplen || bcmp(cp, mdp->d_path, cplen) != 0)
1080479ab7f0SSascha Wildner continue;
1081479ab7f0SSascha Wildner mdp->d_flags &= ~MDIR_REMOVED;
1082479ab7f0SSascha Wildner break;
1083479ab7f0SSascha Wildner }
1084479ab7f0SSascha Wildner if (mdp == NULL) {
1085479ab7f0SSascha Wildner mdp = malloc(sizeof(*mdp) + cplen + 1);
1086479ab7f0SSascha Wildner if (mdp == NULL)
1087479ab7f0SSascha Wildner return;
1088479ab7f0SSascha Wildner mdp->d_path = (char*)(mdp + 1);
1089479ab7f0SSascha Wildner bcopy(local_module_path, mdp->d_path, cplen);
1090479ab7f0SSascha Wildner mdp->d_path[cplen] = 0;
1091479ab7f0SSascha Wildner mdp->d_hints = NULL;
1092479ab7f0SSascha Wildner mdp->d_flags = 0;
1093479ab7f0SSascha Wildner STAILQ_INSERT_TAIL(&moduledir_list, mdp, d_link);
1094479ab7f0SSascha Wildner }
1095479ab7f0SSascha Wildner }
1096479ab7f0SSascha Wildner /*
1097479ab7f0SSascha Wildner * Delete unused directories if any
1098479ab7f0SSascha Wildner */
1099479ab7f0SSascha Wildner mdp = STAILQ_FIRST(&moduledir_list);
1100479ab7f0SSascha Wildner while (mdp) {
1101479ab7f0SSascha Wildner if ((mdp->d_flags & MDIR_REMOVED) == 0) {
1102479ab7f0SSascha Wildner mdp = STAILQ_NEXT(mdp, d_link);
1103479ab7f0SSascha Wildner } else {
1104479ab7f0SSascha Wildner if (mdp->d_hints)
1105479ab7f0SSascha Wildner free(mdp->d_hints);
1106479ab7f0SSascha Wildner mtmp = mdp;
1107479ab7f0SSascha Wildner mdp = STAILQ_NEXT(mdp, d_link);
1108479ab7f0SSascha Wildner STAILQ_REMOVE(&moduledir_list, mtmp, moduledir, d_link);
1109479ab7f0SSascha Wildner free(mtmp);
1110479ab7f0SSascha Wildner }
1111479ab7f0SSascha Wildner }
1112479ab7f0SSascha Wildner }
1113