1 /* $OpenBSD: modload.c,v 1.41 2003/08/06 20:37:25 millert Exp $ */
2 /* $NetBSD: modload.c,v 1.30 2001/11/08 15:33:15 christos Exp $ */
3
4 /*
5 * Copyright (c) 2010, 2013
6 * Thorsten Glaser <tg@mirbsd.org>
7 * Copyright (c) 1993 Terrence R. Lambert.
8 * All rights reserved.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 * 3. All advertising materials mentioning features or use of this software
19 * must display the following acknowledgement:
20 * This product includes software developed by Terrence R. Lambert.
21 * 4. The name Terrence R. Lambert may not be used to endorse or promote
22 * products derived from this software without specific prior written
23 * permission.
24 *
25 * THIS SOFTWARE IS PROVIDED BY TERRENCE R. LAMBERT ``AS IS'' AND ANY
26 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
27 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
28 * ARE DISCLAIMED. IN NO EVENT SHALL THE TERRENCE R. LAMBERT BE LIABLE
29 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
30 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
31 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
32 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
33 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
34 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
35 * SUCH DAMAGE.
36 */
37
38 #include <sys/param.h>
39 #include <sys/ioctl.h>
40 #include <sys/conf.h>
41 #include <sys/mount.h>
42 #include <sys/lkm.h>
43 #include <sys/stat.h>
44 #include <sys/file.h>
45 #include <sys/sysctl.h>
46
47 #include <err.h>
48 #include <errno.h>
49 #include <stdbool.h>
50 #include <stdio.h>
51 #include <stdlib.h>
52 #include <string.h>
53 #include <unistd.h>
54 #include <nlist.h>
55
56 #include "modload.h"
57 #include "pathnames.h"
58
59 __RCSID("$MirOS: src/sbin/modload/modload.c,v 1.4 2013/10/31 20:06:44 tg Exp $");
60
61 #ifndef DFLT_ENTRY
62 #define DFLT_ENTRY "xxxinit"
63 #endif /* !DFLT_ENTRY */
64 #ifndef DFLT_ENTRYEXT
65 #define DFLT_ENTRYEXT "_lkmentry"
66 #endif /* !DFLT_ENTRYEXT */
67
68 #ifdef USE_AOUT
69 #define ENTRY_FMT "_%s"
70 #else
71 #define ENTRY_FMT "%s"
72 #endif
73
74 int debug = 0;
75 int verbose = 0;
76 char *out = NULL;
77 int symtab = 1;
78 int Sflag;
79
80 static void cleanup(void);
81 static void usage(void) __attribute__((__noreturn__));
82 static int verify_kernel(const char *);
83
84 /* prelink the module */
85 static int
prelink(const char * kernel,const char * entry,const char * outfile,const void * address,const char * object)86 prelink(const char *kernel, const char *entry, const char *outfile,
87 const void *address, const char *object)
88 {
89 char cmdbuf[1024];
90 int error = 0;
91
92 linkcmd(cmdbuf, sizeof(cmdbuf),
93 kernel, entry, outfile, address, object);
94
95 if (debug)
96 fprintf(stderr, "%s\n", cmdbuf);
97
98 switch (system(cmdbuf)) {
99 case 0: /* SUCCESS! */
100 break;
101 case 1: /* uninformitive error */
102 /*
103 * Someone needs to fix the return values from the NetBSD
104 * ld program -- it's totally uninformative.
105 *
106 * No such file (4 on SunOS)
107 * Can't write output (2 on SunOS)
108 * Undefined symbol (1 on SunOS)
109 * etc.
110 */
111 case 127: /* can't load shell */
112 case 32512:
113 default:
114 error = 1;
115 break;
116 }
117 return error;
118 }
119
120 static void
usage(void)121 usage(void)
122 {
123 extern char *__progname;
124
125 fprintf(stderr, "usage: %s [-dnsvS] [-A system] [-e entry]\n",
126 __progname);
127 fprintf(stderr, "\t[-p postinstall] [-o outputfile] <input file>\n");
128 exit(1);
129 }
130
131 int fileopen = 0;
132 #define DEV_OPEN 0x01
133 #define MOD_OPEN 0x02
134 #define PART_RESRV 0x04
135 #define OUTFILE_CREAT 0x08
136
137 int devfd, modfd;
138 struct lmc_resrv resrv;
139
140 static void
cleanup(void)141 cleanup(void)
142 {
143
144 if (fileopen & PART_RESRV) {
145 /*
146 * Free up kernel memory
147 */
148 if (ioctl(devfd, LMUNRESRV, 0) == -1)
149 warn("can't release slot 0x%08x memory", resrv.slot);
150 }
151
152 if (fileopen & DEV_OPEN)
153 close(devfd);
154
155 if (fileopen & MOD_OPEN)
156 close(modfd);
157
158 if (fileopen & OUTFILE_CREAT)
159 unlink(out);
160 }
161
162 static int
verify_entry(const char * entry,char * filename)163 verify_entry(const char *entry, char *filename)
164 {
165 struct nlist names[2];
166 int n;
167 char *s;
168
169 memset(names, 0, sizeof(names));
170 if (asprintf(&s, ENTRY_FMT, entry) == -1)
171 err(1, "malloc");
172 #ifdef _AOUT_INCLUDE_
173 names[0].n_un.n_name = s;
174 #else
175 names[0].n_name = s;
176 #endif
177
178 n = nlist(filename, names);
179 if (n == -1)
180 err(1, "nlist %s", filename);
181 free(s);
182 return n;
183 }
184
185 /*
186 * Transfer data to kernel memory in chunks
187 * of MODIOBUF size at a time.
188 */
189 void
loadbuf(void * buf,size_t len)190 loadbuf(void *buf, size_t len)
191 {
192 struct lmc_loadbuf ldbuf;
193 size_t n;
194 char *p = buf;
195
196 while (len) {
197 n = MIN(len, MODIOBUF);
198 ldbuf.cnt = n;
199 ldbuf.data = p;
200 if (ioctl(devfd, LMLOADBUF, &ldbuf) == -1)
201 err(11, "error loading buffer");
202 len -= n;
203 p += n;
204 }
205 }
206
207 /* Transfer some empty space. */
208 void
loadspace(size_t len)209 loadspace(size_t len)
210 {
211 char buf[MODIOBUF];
212 size_t n;
213
214 memset(buf, 0, sizeof(buf));
215 while (len) {
216 n = MIN(len, sizeof(buf));
217 loadbuf(buf, n);
218 len -= n;
219 }
220 }
221
222 /*
223 * Transfer symbol table to kernel memory in chunks
224 * of MODIOBUF size at a time.
225 */
226 void
loadsym(void * buf,size_t len)227 loadsym(void *buf, size_t len)
228 {
229 struct lmc_loadbuf ldbuf;
230 size_t n;
231 char *p = buf;
232
233 while (len) {
234 n = MIN(len, MODIOBUF);
235 ldbuf.cnt = n;
236 ldbuf.data = p;
237 if (ioctl(devfd, LMLOADSYMS, &ldbuf) == -1)
238 err(11, "error loading buffer");
239 len -= n;
240 p += n;
241 }
242 }
243
244 /* Transfer some empty space. */
245 int
main(int argc,char * argv[])246 main(int argc, char *argv[])
247 {
248 int strtablen, c, noready = 0;
249 bool old = false;
250 const char *kname = _PATH_UNIX;
251 char *entry = strdup(DFLT_ENTRY);
252 char *post = NULL;
253 char *modobj;
254 char modout[80], *p;
255 struct stat stb;
256 size_t modsize; /* XXX */
257 void* modentry; /* XXX */
258
259 while ((c = getopt(argc, argv, "dnvsA:Se:p:o:")) != -1) {
260 switch (c) {
261 case 'd':
262 debug = 1;
263 break; /* debug */
264 case 'v':
265 verbose = 1;
266 break; /* verbose */
267 case 'A':
268 kname = optarg;
269 break; /* kernel */
270 case 'e':
271 entry = optarg;
272 break; /* entry point */
273 case 'p':
274 post = optarg;
275 break; /* postinstall */
276 case 'o':
277 out = optarg;
278 break; /* output file */
279 case 'n':
280 noready = 1;
281 break;
282 case 's':
283 symtab = 0;
284 break;
285 case 'S':
286 Sflag = 1;
287 break;
288 default:
289 usage();
290 }
291 }
292 argc -= optind;
293 argv += optind;
294
295 if (argc != 1)
296 usage();
297
298 modobj = argv[0];
299
300 atexit(cleanup);
301
302 /*
303 * Open the virtual device device driver for exclusive use (needed
304 * to write the new module to it as our means of getting it in the
305 * kernel).
306 */
307 if ((devfd = open(_PATH_LKM, O_RDWR, 0)) == -1)
308 err(3, "%s", _PATH_LKM);
309 fileopen |= DEV_OPEN;
310
311 strncpy(modout, modobj, sizeof(modout) - 1);
312 modout[sizeof(modout) - 1] = '\0';
313
314 p = strrchr(modout, '.');
315 if (!p || (strcmp(p, ".o") && strcmp(p, ".ko")))
316 errx(2, "module object must end in .o or .ko");
317 *p = '\0';
318 if (out == NULL)
319 out = modout;
320
321 /*
322 * Verify that the entry point for the module exists.
323 */
324 if (verify_entry(entry, modobj)) {
325 /*
326 * Try <modobj>_init if entry is DFLT_ENTRY.
327 */
328 if (strcmp(entry, DFLT_ENTRY) == 0) {
329 if ((p = strrchr(modout, '/')))
330 p++;
331 else
332 p = modout;
333 if (asprintf(&entry, "%s%s", p, DFLT_ENTRYEXT) == -1)
334 err(1, "asprintf");
335 if (verify_entry(entry, modobj))
336 errx(1, "entry point " ENTRY_FMT
337 " not found in %s", entry, modobj);
338 } else
339 errx(1, "entry point " ENTRY_FMT " not found in %s",
340 entry, modobj);
341 }
342
343 if (verify_kernel(kname))
344 errx(1, "kernel version mismatch");
345
346 /*
347 * Prelink to get file size
348 */
349 if (prelink(kname, entry, out, 0, modobj))
350 errx(1, "can't prelink `%s' creating `%s'", modobj, out);
351 if (Sflag == 0)
352 fileopen |= OUTFILE_CREAT;
353
354 /*
355 * Pre-open the 0-linked module to get the size information
356 */
357 if ((modfd = open(out, O_RDONLY, 0)) == -1)
358 err(4, "%s", out);
359 fileopen |= MOD_OPEN;
360
361 /*
362 * stat for filesize to figure out string table size
363 */
364 if (fstat(modfd, &stb) == -1)
365 err(3, "fstat `%s'", out);
366
367 /*
368 * work out various sizes and fill in resrv bits
369 */
370 if (mod_sizes(modfd, &modsize, &strtablen, &resrv, &stb) != 0)
371 err(1, "can't get module sizes");
372
373 /*
374 * Close the dummy module -- we have our sizing information.
375 */
376 close(modfd);
377 fileopen &= ~MOD_OPEN;
378
379 /*
380 * Reserve the required amount of kernel memory -- this may fail
381 * to be successful.
382 */
383 resrv.size = modsize; /* size in bytes */
384 resrv.name = modout; /* objname w/o ".o" */
385 resrv.slot = -1; /* returned */
386 resrv.addr = 0; /* returned */
387
388 if (verbose)
389 warnx("reserving %lu bytes of memory", (unsigned long)modsize);
390
391 if (ioctl(devfd, LMRESERV, &resrv) == -1) {
392 if (symtab) {
393 warn("not loading symbols: kernel does not support "
394 "symbol table loading");
395 }
396 doold:
397 symtab = 0;
398 if (ioctl(devfd, LMRESERV_O, &resrv) == -1)
399 err(9, "can't reserve memory");
400 old = true;
401 }
402 fileopen |= PART_RESRV;
403
404 /*
405 * Relink at kernel load address
406 */
407 if (prelink(kname, entry, out, (void*)resrv.addr, modobj))
408 errx(1, "can't link `%s' creating `%s' bound to %p",
409 modobj, out, (void*)resrv.addr);
410
411 /*
412 * Open the relinked module to load it...
413 */
414 if ((modfd = open(out, O_RDONLY, 0)) == -1)
415 err(4, "%s", out);
416 fileopen |= MOD_OPEN;
417
418 modentry = mod_load(modfd);
419 if (debug)
420 (void)fprintf(stderr, "modentry = %p\n", modentry);
421
422 if (symtab)
423 mod_symload(strtablen);
424
425 /*
426 * Save ourselves before disaster (potentitally) strikes...
427 */
428 sync();
429
430 if (noready)
431 return 0;
432
433 /*
434 * Trigger the module as loaded by calling the entry procedure;
435 * this will do all necessary table fixup to ensure that state
436 * is maintained on success, or blow everything back to ground
437 * zero on failure.
438 */
439 if (ioctl(devfd, LMREADY, &modentry) == -1) {
440 if (errno == EINVAL && !old) {
441 if (fileopen & MOD_OPEN)
442 close(modfd);
443 /*
444 * PART_RESRV is not true since the kernel cleans
445 * up after a failed LMREADY.
446 */
447 fileopen &= ~(MOD_OPEN|PART_RESRV);
448 /* try using oldstyle */
449 warn("module failed to load using new version; "
450 "trying old version");
451 goto doold;
452 } else
453 err(14, "error initializing module");
454 }
455
456 /*
457 * Success!
458 */
459 fileopen &= ~PART_RESRV; /* loaded */
460 printf("Module loaded as ID %d\n", resrv.slot);
461
462 /*
463 * Execute the post-install program, if specified.
464 */
465 if (post) {
466 struct lmc_stat sbuf;
467 char name[MAXLKMNAME] = "";
468 char id[16], type[32], offset[32];
469
470 sbuf.id = resrv.slot;
471 sbuf.name = name;
472 if (ioctl(devfd, LMSTAT, &sbuf) == -1)
473 err(15, "error fetching module stats for post-install");
474 (void)snprintf(id, sizeof(id), "%d", sbuf.id);
475 (void)snprintf(type, sizeof(type), "0x%x", sbuf.type);
476 (void)snprintf(offset, sizeof(offset), "%ld",
477 (long)sbuf.offset);
478 /*
479 * XXX
480 * The modload docs say that drivers can install bdevsw &
481 * cdevsw, but the interface only supports one at a time.
482 */
483 execl(post, post, id, type, offset, (char *)NULL);
484 err(16, "can't exec `%s'", post);
485 }
486
487 exit (0);
488 }
489
490 /* from usr.sbin/config/exec.c */
491 extern void loadkernel(const char *);
492 extern caddr_t adjust(caddr_t);
493
494 static int
verify_kernel(const char * filename)495 verify_kernel(const char *filename)
496 {
497 struct nlist names[2];
498 int n, mib[2];
499 size_t sz;
500 char *s;
501 const char *k;
502
503 memset(names, '\0', sizeof(names));
504 if (asprintf(&s, ENTRY_FMT, "version") == -1)
505 err(1, "malloc");
506 #ifdef _AOUT_INCLUDE_
507 names[0].n_un.n_name = s;
508 #else
509 names[0].n_name = s;
510 #endif
511
512 n = nlist(filename, names);
513 if (n == -1)
514 err(1, "nlist %s", filename);
515 free(s);
516 if (n)
517 return (n);
518
519 loadkernel(filename);
520 k = (const char *)adjust((caddr_t)names[0].n_value);
521
522 mib[0] = CTL_KERN;
523 mib[1] = KERN_VERSION;
524 if (sysctl(mib, 2, NULL, &sz, NULL, 0) == -1)
525 err(1, "sysctl");
526 if ((s = malloc(sz)) == NULL)
527 err(1, "malloc");
528 if (sysctl(mib, 2, s, &sz, NULL, 0) == -1)
529 err(1, "sysctl");
530
531 n = strcmp(s, k);
532 if (n)
533 printf("kernel image booted {\n %s}\n"
534 "kernel file '%s' {\n %s}\n", s, filename, k);
535 return (n);
536 }
537