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