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