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