xref: /freebsd/stand/i386/isoboot/isoboot.c (revision 1d386b48a555f61cb7325543adbbb5c3f3407a66)
1 /*-
2  * Copyright (c) 1998 Robert Nordier
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms are freely
6  * permitted provided that the above copyright notice and this
7  * paragraph and the following disclaimer are duplicated in all
8  * such forms.
9  *
10  * This software is provided "AS IS" and without any express or
11  * implied warranties, including, without limitation, the implied
12  * warranties of merchantability and fitness for a particular
13  * purpose.
14  */
15 
16 #include <sys/cdefs.h>
17 #include <sys/param.h>
18 #include <sys/gpt.h>
19 #include <sys/dirent.h>
20 #include <sys/reboot.h>
21 
22 #include <machine/bootinfo.h>
23 #include <machine/elf.h>
24 #include <machine/pc/bios.h>
25 #include <machine/psl.h>
26 
27 #include <stdarg.h>
28 
29 #include <a.out.h>
30 
31 #include <btxv86.h>
32 
33 #include "stand.h"
34 
35 #include "bootargs.h"
36 #include "lib.h"
37 #include "rbx.h"
38 #include "drv.h"
39 #include "cons.h"
40 #include "gpt.h"
41 #include "paths.h"
42 
43 #define ARGS		0x900
44 #define NOPT		14
45 #define NDEV		3
46 #define MEM_BASE	0x12
47 #define MEM_EXT 	0x15
48 
49 #define DRV_HARD	0x80
50 #define DRV_MASK	0x7f
51 
52 #define TYPE_AD		0
53 #define TYPE_DA		1
54 #define TYPE_MAXHARD	TYPE_DA
55 #define TYPE_FD		2
56 
57 extern uint32_t _end;
58 
59 static const char optstr[NOPT] = "DhaCcdgmnpqrsv"; /* Also 'P', 'S' */
60 static const unsigned char flags[NOPT] = {
61 	RBX_DUAL,
62 	RBX_SERIAL,
63 	RBX_ASKNAME,
64 	RBX_CDROM,
65 	RBX_CONFIG,
66 	RBX_KDB,
67 	RBX_GDB,
68 	RBX_MUTE,
69 	RBX_NOINTR,
70 	RBX_PAUSE,
71 	RBX_QUIET,
72 	RBX_DFLTROOT,
73 	RBX_SINGLE,
74 	RBX_VERBOSE
75 };
76 uint32_t opts;
77 
78 static const char *const dev_nm[NDEV] = {"ad", "da", "fd"};
79 static const unsigned char dev_maj[NDEV] = {30, 4, 2};
80 
81 static struct dsk dsk;
82 static char kname[1024];
83 static int comspeed = SIOSPD;
84 static struct bootinfo bootinfo;
85 
86 static vm_offset_t	high_heap_base;
87 static uint32_t		bios_basemem, bios_extmem, high_heap_size;
88 
89 static struct bios_smap smap;
90 
91 /*
92  * The minimum amount of memory to reserve in bios_extmem for the heap.
93  */
94 #define	HEAP_MIN	(3 * 1024 * 1024)
95 
96 static char *heap_next;
97 static char *heap_end;
98 
99 int main(void);
100 
101 static void load(void);
102 static int parse_cmds(char *, int *);
103 
104 static uint8_t ls, dsk_meta;
105 static uint32_t fs_off;
106 
107 #include "cd9660read.c"
108 
109 static inline int
110 xfsread(uint64_t inode, void *buf, size_t nbyte)
111 {
112 
113 	if ((size_t)cd9660_fsread(inode, buf, nbyte) != nbyte) {
114 		printf("Invalid %s\n", "format");
115 		return (-1);
116 	}
117 	return (0);
118 }
119 
120 static void
121 bios_getmem(void)
122 {
123 	uint64_t size;
124 
125 	/* Parse system memory map */
126 	v86.ebx = 0;
127 	do {
128 		v86.ctl = V86_FLAGS;
129 		v86.addr = MEM_EXT;		/* int 0x15 function 0xe820*/
130 		v86.eax = 0xe820;
131 		v86.ecx = sizeof(struct bios_smap);
132 		v86.edx = SMAP_SIG;
133 		v86.es = VTOPSEG(&smap);
134 		v86.edi = VTOPOFF(&smap);
135 		v86int();
136 		if ((v86.efl & 1) || (v86.eax != SMAP_SIG))
137 			break;
138 		/* look for a low-memory segment that's large enough */
139 		if ((smap.type == SMAP_TYPE_MEMORY) && (smap.base == 0) &&
140 		    (smap.length >= (512 * 1024)))
141 			bios_basemem = smap.length;
142 		/* look for the first segment in 'extended' memory */
143 		if ((smap.type == SMAP_TYPE_MEMORY) &&
144 		    (smap.base == 0x100000)) {
145 			bios_extmem = smap.length;
146 		}
147 
148 		/*
149 		 * Look for the largest segment in 'extended' memory beyond
150 		 * 1MB but below 4GB.
151 		 */
152 		if ((smap.type == SMAP_TYPE_MEMORY) &&
153 		    (smap.base > 0x100000) && (smap.base < 0x100000000ull)) {
154 			size = smap.length;
155 
156 			/*
157 			 * If this segment crosses the 4GB boundary,
158 			 * truncate it.
159 			 */
160 			if (smap.base + size > 0x100000000ull)
161 				size = 0x100000000ull - smap.base;
162 
163 			if (size > high_heap_size) {
164 				high_heap_size = size;
165 				high_heap_base = smap.base;
166 			}
167 		}
168 	} while (v86.ebx != 0);
169 
170 	/* Fall back to the old compatibility function for base memory */
171 	if (bios_basemem == 0) {
172 		v86.ctl = 0;
173 		v86.addr = 0x12;		/* int 0x12 */
174 		v86int();
175 
176 		bios_basemem = (v86.eax & 0xffff) * 1024;
177 	}
178 
179 	/*
180 	 * Fall back through several compatibility functions for extended
181 	 * memory
182 	 */
183 	if (bios_extmem == 0) {
184 		v86.ctl = V86_FLAGS;
185 		v86.addr = 0x15;		/* int 0x15 function 0xe801*/
186 		v86.eax = 0xe801;
187 		v86int();
188 		if (!(v86.efl & 1)) {
189 			bios_extmem = ((v86.ecx & 0xffff) +
190 			    ((v86.edx & 0xffff) * 64)) * 1024;
191 		}
192 	}
193 	if (bios_extmem == 0) {
194 		v86.ctl = 0;
195 		v86.addr = 0x15;		/* int 0x15 function 0x88*/
196 		v86.eax = 0x8800;
197 		v86int();
198 		bios_extmem = (v86.eax & 0xffff) * 1024;
199 	}
200 
201 	/*
202 	 * If we have extended memory and did not find a suitable heap
203 	 * region in the SMAP, use the last 3MB of 'extended' memory as a
204 	 * high heap candidate.
205 	 */
206 	if (bios_extmem >= HEAP_MIN && high_heap_size < HEAP_MIN) {
207 		high_heap_size = HEAP_MIN;
208 		high_heap_base = bios_extmem + 0x100000 - HEAP_MIN;
209 	}
210 }
211 
212 int
213 main(void)
214 {
215 	char cmd[512], cmdtmp[512];
216 	ssize_t sz;
217 	int autoboot, dskupdated;
218 	uint64_t ino;
219 
220 	bios_getmem();
221 
222 	if (high_heap_size > 0) {
223 		heap_end = PTOV(high_heap_base + high_heap_size);
224 		heap_next = PTOV(high_heap_base);
225 	} else {
226 		heap_next = (char *)
227 		    (roundup2(__base + (int32_t)&_end, 0x10000) - __base);
228 		heap_end = (char *)PTOV(bios_basemem);
229 	}
230 	setheap(heap_next, heap_end);
231 
232 	v86.ctl = V86_FLAGS;
233 	v86.efl = PSL_RESERVED_DEFAULT | PSL_I;
234 	dsk.drive = *(uint8_t *)PTOV(ARGS);
235 	dsk.type = dsk.drive & DRV_HARD ? TYPE_AD : TYPE_FD;
236 	dsk.unit = dsk.drive & DRV_MASK;
237 	dsk.part = -1;
238 	dsk.start = 0;
239 	bootinfo.bi_version = BOOTINFO_VERSION;
240 	bootinfo.bi_size = sizeof(bootinfo);
241 	bootinfo.bi_basemem = bios_basemem / 1024;
242 	bootinfo.bi_extmem = bios_extmem / 1024;
243 	bootinfo.bi_memsizes_valid++;
244 	bootinfo.bi_bios_dev = dsk.drive;
245 
246 	autoboot = 1;
247 	*cmd = '\0';
248 
249 	for (;;) {
250 		*kname = '\0';
251 		if ((ino = cd9660_lookup(PATH_CONFIG)) ||
252 		    (ino = cd9660_lookup(PATH_DOTCONFIG))) {
253 			sz = cd9660_fsread(ino, cmd, sizeof(cmd) - 1);
254 			cmd[(sz < 0) ? 0 : sz] = '\0';
255 		}
256 		if (*cmd != '\0') {
257 			memcpy(cmdtmp, cmd, sizeof(cmdtmp));
258 			if (parse_cmds(cmdtmp, &dskupdated))
259 				break;
260 			if (!OPT_CHECK(RBX_QUIET))
261 				printf("%s: %s", PATH_CONFIG, cmd);
262 			*cmd = '\0';
263 		}
264 
265 		if (autoboot && keyhit(3)) {
266 			if (*kname == '\0')
267 				memcpy(kname, PATH_LOADER, sizeof(PATH_LOADER));
268 			break;
269 		}
270 		autoboot = 0;
271 
272 		/*
273 		 * Try to exec stage 3 boot loader. If interrupted by a
274 		 * keypress, or in case of failure, try to load a kernel
275 		 * directly instead.
276 		 */
277 		if (*kname != '\0')
278 			load();
279 		memcpy(kname, PATH_LOADER, sizeof(PATH_LOADER));
280 		load();
281 		memcpy(kname, PATH_KERNEL, sizeof(PATH_KERNEL));
282 		load();
283 		dsk_meta = 0;
284 	}
285 
286 	/* Present the user with the boot2 prompt. */
287 
288 	for (;;) {
289 		if (!OPT_CHECK(RBX_QUIET)) {
290 			printf("\nFreeBSD/x86 boot\n"
291 			    "Default: %u:%s(%up%u)%s\n"
292 			    "boot: ",
293 			    dsk.drive & DRV_MASK, dev_nm[dsk.type], dsk.unit,
294 			    dsk.part, kname);
295 		}
296 		if (ioctrl & IO_SERIAL)
297 			sio_flush();
298 		*cmd = '\0';
299 		if (keyhit(0))
300 			getstr(cmd, sizeof(cmd));
301 		else if (!OPT_CHECK(RBX_QUIET))
302 			putchar('\n');
303 		if (parse_cmds(cmd, &dskupdated)) {
304 			putchar('\a');
305 			continue;
306 		}
307 		load();
308 	}
309 	/* NOTREACHED */
310 }
311 
312 /* Needed so btxld can link us properly; do not remove. */
313 void
314 exit(int x)
315 {
316 
317 	while (1);
318 	__unreachable();
319 }
320 
321 static void
322 load(void)
323 {
324 	union {
325 		struct exec ex;
326 		Elf32_Ehdr eh;
327 	} hdr;
328 	static Elf32_Phdr ep[2];
329 	static Elf32_Shdr es[2];
330 	caddr_t p;
331 	uint64_t ino;
332 	uint32_t addr, x;
333 	int fmt, i, j;
334 
335 	if (!(ino = cd9660_lookup(kname))) {
336 		if (!ls) {
337 			printf("%s: No %s on %u:%s(%up%u)\n", BOOTPROG,
338 			    kname, dsk.drive & DRV_MASK, dev_nm[dsk.type],
339 			    dsk.unit,
340 			    dsk.part);
341 		}
342 		return;
343 	}
344 	if (xfsread(ino, &hdr, sizeof(hdr)))
345 		return;
346 	if (N_GETMAGIC(hdr.ex) == ZMAGIC)
347 		fmt = 0;
348 	else if (IS_ELF(hdr.eh))
349 		fmt = 1;
350 	else {
351 		printf("Invalid %s\n", "format");
352 		return;
353 	}
354 	if (fmt == 0) {
355 		addr = hdr.ex.a_entry & 0xffffff;
356 		p = PTOV(addr);
357 		fs_off = PAGE_SIZE;
358 		if (xfsread(ino, p, hdr.ex.a_text))
359 			return;
360 		p += roundup2(hdr.ex.a_text, PAGE_SIZE);
361 		if (xfsread(ino, p, hdr.ex.a_data))
362 			return;
363 		p += hdr.ex.a_data + roundup2(hdr.ex.a_bss, PAGE_SIZE);
364 		bootinfo.bi_symtab = VTOP(p);
365 		memcpy(p, &hdr.ex.a_syms, sizeof(hdr.ex.a_syms));
366 		p += sizeof(hdr.ex.a_syms);
367 		if (hdr.ex.a_syms) {
368 			if (xfsread(ino, p, hdr.ex.a_syms))
369 				return;
370 			p += hdr.ex.a_syms;
371 			if (xfsread(ino, p, sizeof(int)))
372 				return;
373 			x = *(uint32_t *)p;
374 			p += sizeof(int);
375 			x -= sizeof(int);
376 			if (xfsread(ino, p, x))
377 				return;
378 			p += x;
379 		}
380 	} else {
381 		fs_off = hdr.eh.e_phoff;
382 		for (j = i = 0; i < hdr.eh.e_phnum && j < 2; i++) {
383 			if (xfsread(ino, ep + j, sizeof(ep[0])))
384 				return;
385 			if (ep[j].p_type == PT_LOAD)
386 				j++;
387 		}
388 		for (i = 0; i < 2; i++) {
389 			p = PTOV(ep[i].p_paddr & 0xffffff);
390 			fs_off = ep[i].p_offset;
391 			if (xfsread(ino, p, ep[i].p_filesz))
392 				return;
393 		}
394 		p += roundup2(ep[1].p_memsz, PAGE_SIZE);
395 		bootinfo.bi_symtab = VTOP(p);
396 		if (hdr.eh.e_shnum == hdr.eh.e_shstrndx + 3) {
397 			fs_off = hdr.eh.e_shoff + sizeof(es[0]) *
398 			    (hdr.eh.e_shstrndx + 1);
399 			if (xfsread(ino, &es, sizeof(es)))
400 				return;
401 			for (i = 0; i < 2; i++) {
402 				memcpy(p, &es[i].sh_size,
403 				    sizeof(es[i].sh_size));
404 				p += sizeof(es[i].sh_size);
405 				fs_off = es[i].sh_offset;
406 				if (xfsread(ino, p, es[i].sh_size))
407 					return;
408 				p += es[i].sh_size;
409 			}
410 		}
411 		addr = hdr.eh.e_entry & 0xffffff;
412 	}
413 	bootinfo.bi_esymtab = VTOP(p);
414 	bootinfo.bi_kernelname = VTOP(kname);
415 	bootinfo.bi_bios_dev = dsk.drive;
416 	__exec((caddr_t)addr, RB_BOOTINFO | (opts & RBX_MASK),
417 	    MAKEBOOTDEV(dev_maj[dsk.type], 0, dsk.unit, 0),
418 	    0, 0, 0, VTOP(&bootinfo));
419 }
420 
421 static int
422 parse_cmds(char *cmdstr, int *dskupdated)
423 {
424 	char *arg;
425 	char *ep, *p, *q;
426 	const char *cp;
427 	unsigned int drv;
428 	int c, i, j;
429 
430 	arg = cmdstr;
431 	*dskupdated = 0;
432 	while ((c = *arg++)) {
433 		if (c == ' ' || c == '\t' || c == '\n')
434 			continue;
435 		for (p = arg; *p && *p != '\n' && *p != ' ' && *p != '\t'; p++);
436 		ep = p;
437 		if (*p)
438 			*p++ = 0;
439 		if (c == '-') {
440 			while ((c = *arg++)) {
441 				if (c == 'P') {
442 					if (*(uint8_t *)PTOV(0x496) & 0x10) {
443 						cp = "yes";
444 					} else {
445 						opts |= OPT_SET(RBX_DUAL) |
446 						    OPT_SET(RBX_SERIAL);
447 						cp = "no";
448 					}
449 					printf("Keyboard: %s\n", cp);
450 					continue;
451 				} else if (c == 'S') {
452 					j = 0;
453 					while ((unsigned int)(i = *arg++ - '0')
454 					    <= 9)
455 						j = j * 10 + i;
456 					if (j > 0 && i == -'0') {
457 						comspeed = j;
458 						break;
459 					}
460 					/*
461 					 * Fall through to error below
462 					 * ('S' not in optstr[]).
463 					 */
464 				}
465 				for (i = 0; c != optstr[i]; i++)
466 					if (i == NOPT - 1)
467 						return (-1);
468 				opts ^= OPT_SET(flags[i]);
469 			}
470 			ioctrl = OPT_CHECK(RBX_DUAL) ? (IO_SERIAL|IO_KEYBOARD) :
471 			    OPT_CHECK(RBX_SERIAL) ? IO_SERIAL : IO_KEYBOARD;
472 			if (ioctrl & IO_SERIAL) {
473 				if (sio_init(115200 / comspeed) != 0)
474 					ioctrl &= ~IO_SERIAL;
475 			}
476 		} else {
477 			for (q = arg--; *q && *q != '('; q++);
478 			if (*q) {
479 				drv = -1;
480 				if (arg[1] == ':') {
481 					drv = *arg - '0';
482 					if (drv > 9)
483 						return (-1);
484 					arg += 2;
485 				}
486 				if (q - arg != 2)
487 					return (-1);
488 				for (i = 0; arg[0] != dev_nm[i][0] ||
489 				    arg[1] != dev_nm[i][1]; i++)
490 					if (i == NDEV - 1)
491 						return (-1);
492 				dsk.type = i;
493 				arg += 3;
494 				dsk.unit = *arg - '0';
495 				if (arg[1] != 'p' || dsk.unit > 9)
496 					return (-1);
497 				arg += 2;
498 				dsk.part = *arg - '0';
499 				if (dsk.part < 1 || dsk.part > 9)
500 					return (-1);
501 				arg++;
502 				if (arg[0] != ')')
503 					return (-1);
504 				arg++;
505 				if (drv == -1)
506 					drv = dsk.unit;
507 				dsk.drive = (dsk.type <= TYPE_MAXHARD
508 				    ? DRV_HARD : 0) + drv;
509 				*dskupdated = 1;
510 			}
511 			if ((i = ep - arg)) {
512 				if ((size_t)i >= sizeof(kname))
513 					return (-1);
514 				memcpy(kname, arg, i + 1);
515 			}
516 		}
517 		arg = p;
518 	}
519 	return (0);
520 }
521