module.c revision 57468
1/*-
2 * Copyright (c) 1998 Michael Smith <msmith@freebsd.org>
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 *
26 * $FreeBSD: head/sys/boot/common/module.c 57468 2000-02-25 05:10:44Z bp $
27 */
28
29/*
30 * module function dispatcher, support, etc.
31 */
32
33#include <stand.h>
34#include <string.h>
35#include <sys/param.h>
36#include <sys/linker.h>
37
38#include "bootstrap.h"
39
40static int			mod_loadmodule(char *name, int argc, char *argv[], struct loaded_module **mpp);
41static int			file_load_dependancies(struct loaded_module *base_file);
42static char			*mod_searchfile(char *name);
43static char			*mod_searchmodule(char *name);
44static void			mod_append(struct loaded_module *mp);
45static struct module_metadata 	*metadata_next(struct module_metadata *md, int type);
46
47/* load address should be tweaked by first module loaded (kernel) */
48static vm_offset_t	loadaddr = 0;
49
50static char		*default_searchpath ="/;/boot;/modules";
51
52struct loaded_module *loaded_modules = NULL;
53
54/*
55 * load an object, either a disk file or code module.
56 *
57 * To load a file, the syntax is:
58 *
59 * load -t <type> <path>
60 *
61 * code modules are loaded as:
62 *
63 * load <path> <options>
64 */
65
66COMMAND_SET(load, "load", "load a kernel or module", command_load);
67
68static int
69command_load(int argc, char *argv[])
70{
71    char	*typestr;
72    int		dofile, ch, error;
73
74    dofile = 0;
75    optind = 1;
76    optreset = 1;
77    typestr = NULL;
78    if (argc == 1) {
79	command_errmsg = "no filename specified";
80	return(CMD_ERROR);
81    }
82    while ((ch = getopt(argc, argv, "t:")) != -1) {
83	switch(ch) {
84	case 't':
85	    typestr = optarg;
86	    dofile = 1;
87	    break;
88	case '?':
89	default:
90	    /* getopt has already reported an error */
91	    return(CMD_OK);
92	}
93    }
94    argv += (optind - 1);
95    argc -= (optind - 1);
96
97    /*
98     * Request to load a raw file?
99     */
100    if (dofile) {
101	if ((typestr == NULL) || (*typestr == 0)) {
102	    command_errmsg = "invalid load type";
103	    return(CMD_ERROR);
104	}
105	return(mod_loadobj(typestr, argv[1]));
106    }
107
108    /*
109     * Looks like a request for a module.
110     */
111    error = mod_load(argv[1], argc - 2, argv + 2);
112    if (error == EEXIST)
113	sprintf(command_errbuf, "warning: module '%s' already loaded", argv[1]);
114    return (error == 0 ? CMD_OK : CMD_ERROR);
115}
116
117COMMAND_SET(unload, "unload", "unload all modules", command_unload);
118
119static int
120command_unload(int argc, char *argv[])
121{
122    struct loaded_module	*mp;
123
124    while (loaded_modules != NULL) {
125	mp = loaded_modules;
126	loaded_modules = loaded_modules->m_next;
127	mod_discard(mp);
128    }
129    loadaddr = 0;
130    return(CMD_OK);
131}
132
133COMMAND_SET(lsmod, "lsmod", "list loaded modules", command_lsmod);
134
135static int
136command_lsmod(int argc, char *argv[])
137{
138    struct loaded_module	*am;
139    struct module_metadata	*md;
140    char			lbuf[80];
141    int				ch, verbose;
142
143    verbose = 0;
144    optind = 1;
145    optreset = 1;
146    while ((ch = getopt(argc, argv, "v")) != -1) {
147	switch(ch) {
148	case 'v':
149	    verbose = 1;
150	    break;
151	case '?':
152	default:
153	    /* getopt has already reported an error */
154	    return(CMD_OK);
155	}
156    }
157
158    pager_open();
159    for (am = loaded_modules; (am != NULL); am = am->m_next) {
160	sprintf(lbuf, " %p: %s (%s, 0x%lx)\n",
161		(void *) am->m_addr, am->m_name, am->m_type, (long) am->m_size);
162	pager_output(lbuf);
163	if (am->m_args != NULL) {
164	    pager_output("    args: ");
165	    pager_output(am->m_args);
166	    pager_output("\n");
167	}
168	if (verbose)
169	    /* XXX could add some formatting smarts here to display some better */
170	    for (md = am->m_metadata; md != NULL; md = md->md_next) {
171		sprintf(lbuf, "      0x%04x, 0x%lx\n", md->md_type, (long) md->md_size);
172		pager_output(lbuf);
173	    }
174    }
175    pager_close();
176    return(CMD_OK);
177}
178
179/*
180 * We've been asked to load (name) and give it (argc),(argv).
181 * Start by trying to load it, and then attempt to load all of its
182 * dependancies.  If we fail at any point, throw them all away and
183 * fail the entire load.
184 *
185 * XXX if a depended-on module requires arguments, it must be loaded
186 *     explicitly first.
187 */
188int
189mod_load(char *name, int argc, char *argv[])
190{
191    struct loaded_module	*last_mod, *base_mod, *mp;
192    int				error;
193
194    /* remember previous last module on chain */
195    for (last_mod = loaded_modules;
196	 (last_mod != NULL) && (last_mod->m_next != NULL);
197	 last_mod = last_mod->m_next)
198	;
199
200    /*
201     * Load the first module; note that it's the only one that gets
202     * arguments explicitly.
203     */
204    error = mod_loadmodule(name, argc, argv, &base_mod);
205    if (error)
206	return (error);
207
208    error = file_load_dependancies(base_mod);
209    if (!error)
210	return (0);
211
212    /* Load failed; discard everything */
213    last_mod->m_next = NULL;
214    loadaddr = last_mod->m_addr + last_mod->m_size;
215    while (base_mod != NULL) {
216        mp = base_mod;
217        base_mod = base_mod->m_next;
218        mod_discard(mp);
219    }
220    return (error);
221}
222
223/*
224 * We've been asked to load (name) as (type), so just suck it in,
225 * no arguments or anything.
226 */
227int
228mod_loadobj(char *type, char *name)
229{
230    struct loaded_module	*mp;
231    char			*cp;
232    int				fd, got;
233    vm_offset_t			laddr;
234
235    /* We can't load first */
236    if ((mod_findmodule(NULL, NULL)) == NULL) {
237	command_errmsg = "can't load file before kernel";
238	return(CMD_ERROR);
239    }
240
241    /* locate the file on the load path */
242    cp = mod_searchfile(name);
243    if (cp == NULL) {
244	sprintf(command_errbuf, "can't find '%s'", name);
245	return(CMD_ERROR);
246    }
247    name = cp;
248
249    if ((fd = open(name, O_RDONLY)) < 0) {
250	sprintf(command_errbuf, "can't open '%s': %s", name, strerror(errno));
251	free(name);
252	return(CMD_ERROR);
253    }
254
255    laddr = loadaddr;
256    for (;;) {
257	/* read in 4k chunks; size is not really important */
258	got = archsw.arch_readin(fd, laddr, 4096);
259	if (got == 0)				/* end of file */
260	    break;
261	if (got < 0) {				/* error */
262	    sprintf(command_errbuf, "error reading '%s': %s", name, strerror(errno));
263	    free(name);
264	    close(fd);
265	    return(CMD_ERROR);
266	}
267	laddr += got;
268    }
269
270    /* Looks OK so far; create & populate control structure */
271    mp = malloc(sizeof(struct loaded_module));
272    mp->m_name = name;
273    mp->m_type = strdup(type);
274    mp->m_args = NULL;
275    mp->m_metadata = NULL;
276    mp->m_loader = -1;
277    mp->m_addr = loadaddr;
278    mp->m_size = laddr - loadaddr;
279
280    /* recognise space consumption */
281    loadaddr = laddr;
282
283    /* Add to the list of loaded modules */
284    mod_append(mp);
285    close(fd);
286    return(CMD_OK);
287}
288
289/*
290 * Load the module (name), pass it (argc),(argv).
291 * Don't do any dependancy checking.
292 */
293static int
294mod_loadmodule(char *name, int argc, char *argv[], struct loaded_module **mpp)
295{
296    struct loaded_module	*mp;
297    int				i, err;
298    char			*cp;
299
300    /* locate the module on the search path */
301    cp = mod_searchmodule(name);
302    if (cp == NULL) {
303	sprintf(command_errbuf, "can't find '%s'", name);
304	return (ENOENT);
305    }
306    name = cp;
307
308    cp = strrchr(name, '/');
309    if (cp)
310        cp++;
311    else
312        cp = name;
313    /* see if module is already loaded */
314    mp = mod_findmodule(cp, NULL);
315    if (mp) {
316	*mpp = mp;
317	return (EEXIST);
318    }
319
320    err = 0;
321    for (i = 0, mp = NULL; (module_formats[i] != NULL) && (mp == NULL); i++) {
322	if ((err = (module_formats[i]->l_load)(name, loadaddr, &mp)) != 0) {
323
324	    /* Unknown to this handler? */
325	    if (err == EFTYPE)
326		continue;
327
328	    /* Fatal error */
329	    sprintf(command_errbuf, "can't load module '%s': %s", name, strerror(err));
330	    free(name);
331	    return (err);
332	} else {
333
334	    /* Load was OK, set args */
335	    mp->m_args = unargv(argc, argv);
336
337	    /* where can we put the next one? */
338	    loadaddr = mp->m_addr + mp->m_size;
339
340	    /* remember the loader */
341	    mp->m_loader = i;
342
343	    /* Add to the list of loaded modules */
344	    mod_append(mp);
345	    *mpp = mp;
346
347	    break;
348	}
349    }
350    if (err == EFTYPE)
351	sprintf(command_errbuf, "don't know how to load module '%s'", name);
352    free(name);
353    return (err);
354}
355
356static int
357file_load_dependancies(struct loaded_module *base_file)
358{
359    struct module_metadata	*md;
360    char 			*dmodname;
361    int				error;
362
363    md = mod_findmetadata(base_file, MODINFOMD_DEPLIST);
364    if (md == NULL)
365	return (0);
366    error = 0;
367    do {
368	dmodname = (char *)md->md_data;
369	if (mod_findmodule(NULL, dmodname) == NULL) {
370	    printf("loading required module '%s'\n", dmodname);
371	    error = mod_load(dmodname, 0, NULL);
372	    if (error && error != EEXIST)
373		break;
374	}
375	md = metadata_next(md, MODINFOMD_DEPLIST);
376    } while (md);
377    return (error);
378}
379
380/*
381 * Find a module matching (name) and (type).
382 * NULL may be passed as a wildcard to either.
383 */
384struct loaded_module *
385mod_findmodule(char *name, char *type)
386{
387    struct loaded_module	*mp;
388
389    for (mp = loaded_modules; mp != NULL; mp = mp->m_next) {
390	if (((name == NULL) || !strcmp(name, mp->m_name)) &&
391	    ((type == NULL) || !strcmp(type, mp->m_type)))
392	    break;
393    }
394    return(mp);
395}
396
397/*
398 * Make a copy of (size) bytes of data from (p), and associate them as
399 * metadata of (type) to the module (mp).
400 */
401void
402mod_addmetadata(struct loaded_module *mp, int type, size_t size, void *p)
403{
404    struct module_metadata	*md;
405
406    md = malloc(sizeof(struct module_metadata) + size);
407    md->md_size = size;
408    md->md_type = type;
409    bcopy(p, md->md_data, size);
410    md->md_next = mp->m_metadata;
411    mp->m_metadata = md;
412}
413
414/*
415 * Find a metadata object of (type) associated with the module
416 * (mp)
417 */
418struct module_metadata *
419mod_findmetadata(struct loaded_module *mp, int type)
420{
421    struct module_metadata	*md;
422
423    for (md = mp->m_metadata; md != NULL; md = md->md_next)
424	if (md->md_type == type)
425	    break;
426    return(md);
427}
428
429struct module_metadata *
430metadata_next(struct module_metadata *md, int type)
431{
432    if (md == NULL)
433	return (NULL);
434    while((md = md->md_next) != NULL)
435	if (md->md_type == type)
436	    break;
437    return (md);
438}
439
440/*
441 * Attempt to find the file (name) on the module searchpath.
442 * If (name) is qualified in any way, we simply check it and
443 * return it or NULL.  If it is not qualified, then we attempt
444 * to construct a path using entries in the environment variable
445 * module_path.
446 *
447 * The path we return a pointer to need never be freed, as we manage
448 * it internally.
449 */
450static char *
451mod_searchfile(char *name)
452{
453    char		*result;
454    char		*path, *sp;
455    const char		*cp;
456    struct stat		sb;
457
458    /* Don't look for nothing */
459    if (name == NULL)
460	return(name);
461
462    if (*name == 0)
463	return(strdup(name));
464
465    /*
466     * See if there's a device on the front, or a directory name.
467     */
468    archsw.arch_getdev(NULL, name, &cp);
469    if ((cp != name) || (strchr(name, '/') != NULL)) {
470	/* Qualified, so just see if it exists */
471	if (stat(name, &sb) == 0)
472	    return(strdup(name));
473	return(NULL);
474    }
475
476    /*
477     * Get the module path
478     */
479    if ((cp = getenv("module_path")) == NULL)
480	cp = default_searchpath;
481    sp = path = strdup(cp);
482
483    /*
484     * Traverse the path, splitting off ';'-delimited components.
485     */
486    result = NULL;
487    while((cp = strsep(&path, ";")) != NULL) {
488	result = malloc(strlen(cp) + strlen(name) + 5);
489	strcpy(result, cp);
490	if (cp[strlen(cp) - 1] != '/')
491	    strcat(result, "/");
492	strcat(result, name);
493/*	printf("search '%s'\n", result); */
494	if ((stat(result, &sb) == 0) &&
495	    S_ISREG(sb.st_mode))
496	    break;
497	free(result);
498	result = NULL;
499    }
500    free(sp);
501    return(result);
502}
503
504/*
505 * Attempt to locate the file containing the module (name)
506 */
507static char *
508mod_searchmodule(char *name)
509{
510    char	*tn, *result;
511
512    /* Look for (name).ko */
513    tn = malloc(strlen(name) + 3 + 1);
514    strcpy(tn, name);
515    strcat(tn, ".ko");
516    result = mod_searchfile(tn);
517    free(tn);
518    /* Look for just (name) (useful for finding kernels) */
519    if (result == NULL)
520	result = mod_searchfile(name);
521
522    return(result);
523}
524
525
526/*
527 * Throw a module away
528 */
529void
530mod_discard(struct loaded_module *mp)
531{
532    struct module_metadata	*md;
533
534    if (mp != NULL) {
535	while (mp->m_metadata != NULL) {
536	    md = mp->m_metadata;
537	    mp->m_metadata = mp->m_metadata->md_next;
538	    free(md);
539	}
540	if (mp->m_name != NULL)
541	    free(mp->m_name);
542	if (mp->m_type != NULL)
543	    free(mp->m_type);
544	if (mp->m_args != NULL)
545	    free(mp->m_args);
546	free(mp);
547    }
548}
549
550/*
551 * Allocate a new module; must be used instead of malloc()
552 * to ensure safe initialisation.
553 */
554struct loaded_module *
555mod_allocmodule(void)
556{
557    struct loaded_module	*mp;
558
559    if ((mp = malloc(sizeof(struct loaded_module))) != NULL) {
560	bzero(mp, sizeof(struct loaded_module));
561    }
562    return(mp);
563}
564
565
566/*
567 * Add a module to the chain
568 */
569static void
570mod_append(struct loaded_module *mp)
571{
572    struct loaded_module	*cm;
573
574    /* Append to list of loaded modules */
575    mp->m_next = NULL;
576    if (loaded_modules == NULL) {
577	loaded_modules = mp;
578    } else {
579	for (cm = loaded_modules; cm->m_next != NULL; cm = cm->m_next)
580	    ;
581	cm->m_next = mp;
582    }
583}
584