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