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$");
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 uuid_t freebsd_ufs_uuid = GPT_ENT_TYPE_FREEBSD_UFS;
62static const char optstr[NOPT] = "DhaCcdgmnpqrsv"; /* Also 'P', 'S' */
63static const unsigned char flags[NOPT] = {
64	RBX_DUAL,
65	RBX_SERIAL,
66	RBX_ASKNAME,
67	RBX_CDROM,
68	RBX_CONFIG,
69	RBX_KDB,
70	RBX_GDB,
71	RBX_MUTE,
72	RBX_NOINTR,
73	RBX_PAUSE,
74	RBX_QUIET,
75	RBX_DFLTROOT,
76	RBX_SINGLE,
77	RBX_VERBOSE
78};
79uint32_t opts;
80
81static const char *const dev_nm[NDEV] = {"ad", "da", "fd"};
82static const unsigned char dev_maj[NDEV] = {30, 4, 2};
83
84static char kname[1024];
85static int comspeed = SIOSPD;
86static struct bootinfo bootinfo;
87#ifdef LOADER_GELI_SUPPORT
88static struct geli_boot_args geliargs;
89#endif
90
91static vm_offset_t	high_heap_base;
92static uint32_t		bios_basemem, bios_extmem, high_heap_size;
93
94static struct bios_smap smap;
95
96/*
97 * The minimum amount of memory to reserve in bios_extmem for the heap.
98 */
99#define	HEAP_MIN	(3 * 1024 * 1024)
100
101static char *heap_next;
102static char *heap_end;
103
104static void load(void);
105static int parse_cmds(char *, int *);
106static int dskread(void *, daddr_t, unsigned);
107#ifdef LOADER_GELI_SUPPORT
108static int vdev_read(void *vdev __unused, void *priv, off_t off, void *buf,
109	size_t bytes);
110#endif
111
112#include "ufsread.c"
113#include "gpt.c"
114#ifdef LOADER_GELI_SUPPORT
115#include "geliboot.h"
116static char gelipw[GELI_PW_MAXLEN];
117#endif
118
119struct gptdsk {
120	struct dsk       dsk;
121#ifdef LOADER_GELI_SUPPORT
122	struct geli_dev *gdev;
123#endif
124};
125
126static struct gptdsk gdsk;
127
128static inline int
129xfsread(ufs_ino_t inode, void *buf, size_t nbyte)
130{
131
132	if ((size_t)fsread(inode, buf, nbyte) != nbyte) {
133		printf("Invalid %s\n", "format");
134		return (-1);
135	}
136	return (0);
137}
138
139static void
140bios_getmem(void)
141{
142	uint64_t size;
143
144	/* Parse system memory map */
145	v86.ebx = 0;
146	do {
147		v86.ctl = V86_FLAGS;
148		v86.addr = MEM_EXT;		/* int 0x15 function 0xe820*/
149		v86.eax = 0xe820;
150		v86.ecx = sizeof(struct bios_smap);
151		v86.edx = SMAP_SIG;
152		v86.es = VTOPSEG(&smap);
153		v86.edi = VTOPOFF(&smap);
154		v86int();
155		if ((v86.efl & 1) || (v86.eax != SMAP_SIG))
156			break;
157		/* look for a low-memory segment that's large enough */
158		if ((smap.type == SMAP_TYPE_MEMORY) && (smap.base == 0) &&
159		    (smap.length >= (512 * 1024)))
160			bios_basemem = smap.length;
161		/* look for the first segment in 'extended' memory */
162		if ((smap.type == SMAP_TYPE_MEMORY) &&
163		    (smap.base == 0x100000)) {
164			bios_extmem = smap.length;
165		}
166
167		/*
168		 * Look for the largest segment in 'extended' memory beyond
169		 * 1MB but below 4GB.
170		 */
171		if ((smap.type == SMAP_TYPE_MEMORY) &&
172		    (smap.base > 0x100000) && (smap.base < 0x100000000ull)) {
173			size = smap.length;
174
175			/*
176			 * If this segment crosses the 4GB boundary,
177			 * truncate it.
178			 */
179			if (smap.base + size > 0x100000000ull)
180				size = 0x100000000ull - smap.base;
181
182			if (size > high_heap_size) {
183				high_heap_size = size;
184				high_heap_base = smap.base;
185			}
186		}
187	} while (v86.ebx != 0);
188
189	/* Fall back to the old compatibility function for base memory */
190	if (bios_basemem == 0) {
191		v86.ctl = 0;
192		v86.addr = 0x12;		/* int 0x12 */
193		v86int();
194
195		bios_basemem = (v86.eax & 0xffff) * 1024;
196	}
197
198	/*
199	 * Fall back through several compatibility functions for extended
200	 * memory
201	 */
202	if (bios_extmem == 0) {
203		v86.ctl = V86_FLAGS;
204		v86.addr = 0x15;		/* int 0x15 function 0xe801*/
205		v86.eax = 0xe801;
206		v86int();
207		if (!(v86.efl & 1)) {
208			bios_extmem = ((v86.ecx & 0xffff) +
209			    ((v86.edx & 0xffff) * 64)) * 1024;
210		}
211	}
212	if (bios_extmem == 0) {
213		v86.ctl = 0;
214		v86.addr = 0x15;		/* int 0x15 function 0x88*/
215		v86.eax = 0x8800;
216		v86int();
217		bios_extmem = (v86.eax & 0xffff) * 1024;
218	}
219
220	/*
221	 * If we have extended memory and did not find a suitable heap
222	 * region in the SMAP, use the last 3MB of 'extended' memory as a
223	 * high heap candidate.
224	 */
225	if (bios_extmem >= HEAP_MIN && high_heap_size < HEAP_MIN) {
226		high_heap_size = HEAP_MIN;
227		high_heap_base = bios_extmem + 0x100000 - HEAP_MIN;
228	}
229}
230
231static int
232gptinit(void)
233{
234
235	if (gptread(&gdsk.dsk, dmadat->secbuf) == -1) {
236		printf("%s: unable to load GPT\n", BOOTPROG);
237		return (-1);
238	}
239	if (gptfind(&freebsd_ufs_uuid, &gdsk.dsk, gdsk.dsk.part) == -1) {
240		printf("%s: no UFS partition was found\n", BOOTPROG);
241		return (-1);
242	}
243#ifdef LOADER_GELI_SUPPORT
244	gdsk.gdev = geli_taste(vdev_read, &gdsk.dsk,
245	    (gpttable[curent].ent_lba_end - gpttable[curent].ent_lba_start),
246	    "disk%up%u:", gdsk.dsk.unit, curent + 1);
247	if (gdsk.gdev != NULL) {
248		if (geli_havekey(gdsk.gdev) != 0 &&
249		    geli_passphrase(gdsk.gdev, gelipw) != 0) {
250			printf("%s: unable to decrypt GELI key\n", BOOTPROG);
251			return (-1);
252		}
253	}
254#endif
255
256	dsk_meta = 0;
257	return (0);
258}
259
260int main(void);
261
262int
263main(void)
264{
265	char cmd[512], cmdtmp[512];
266	ssize_t sz;
267	int autoboot, dskupdated;
268	ufs_ino_t ino;
269
270	dmadat = (void *)(roundup2(__base + (int32_t)&_end, 0x10000) - __base);
271
272	bios_getmem();
273
274	if (high_heap_size > 0) {
275		heap_end = PTOV(high_heap_base + high_heap_size);
276		heap_next = PTOV(high_heap_base);
277	} else {
278		heap_next = (char *)dmadat + sizeof(*dmadat);
279		heap_end = (char *)PTOV(bios_basemem);
280	}
281	setheap(heap_next, heap_end);
282
283	v86.ctl = V86_FLAGS;
284	v86.efl = PSL_RESERVED_DEFAULT | PSL_I;
285	gdsk.dsk.drive = *(uint8_t *)PTOV(ARGS);
286	gdsk.dsk.type = gdsk.dsk.drive & DRV_HARD ? TYPE_AD : TYPE_FD;
287	gdsk.dsk.unit = gdsk.dsk.drive & DRV_MASK;
288	gdsk.dsk.part = -1;
289	gdsk.dsk.start = 0;
290	bootinfo.bi_version = BOOTINFO_VERSION;
291	bootinfo.bi_size = sizeof(bootinfo);
292	bootinfo.bi_basemem = bios_basemem / 1024;
293	bootinfo.bi_extmem = bios_extmem / 1024;
294	bootinfo.bi_memsizes_valid++;
295	bootinfo.bi_bios_dev = gdsk.dsk.drive;
296
297	/* Process configuration file */
298
299	if (gptinit() != 0)
300		return (-1);
301
302	autoboot = 1;
303	*cmd = '\0';
304
305	for (;;) {
306		*kname = '\0';
307		if ((ino = lookup(PATH_CONFIG)) ||
308		    (ino = lookup(PATH_DOTCONFIG))) {
309			sz = fsread(ino, cmd, sizeof(cmd) - 1);
310			cmd[(sz < 0) ? 0 : sz] = '\0';
311		}
312		if (*cmd != '\0') {
313			memcpy(cmdtmp, cmd, sizeof(cmdtmp));
314			if (parse_cmds(cmdtmp, &dskupdated))
315				break;
316			if (dskupdated && gptinit() != 0)
317				break;
318			if (!OPT_CHECK(RBX_QUIET))
319				printf("%s: %s", PATH_CONFIG, cmd);
320			*cmd = '\0';
321		}
322
323		if (autoboot && keyhit(3)) {
324			if (*kname == '\0')
325				memcpy(kname, PATH_LOADER, sizeof(PATH_LOADER));
326			break;
327		}
328		autoboot = 0;
329
330		/*
331		 * Try to exec stage 3 boot loader. If interrupted by a
332		 * keypress, or in case of failure, try to load a kernel
333		 * directly instead.
334		 */
335		if (*kname != '\0')
336			load();
337		memcpy(kname, PATH_LOADER, sizeof(PATH_LOADER));
338		load();
339		memcpy(kname, PATH_KERNEL, sizeof(PATH_KERNEL));
340		load();
341		gptbootfailed(&gdsk.dsk);
342		if (gptfind(&freebsd_ufs_uuid, &gdsk.dsk, -1) == -1)
343			break;
344		dsk_meta = 0;
345	}
346
347	/* Present the user with the boot2 prompt. */
348
349	for (;;) {
350		if (!OPT_CHECK(RBX_QUIET)) {
351			printf("\nFreeBSD/x86 boot\n"
352			    "Default: %u:%s(%up%u)%s\n"
353			    "boot: ",
354			    gdsk.dsk.drive & DRV_MASK, dev_nm[gdsk.dsk.type],
355			    gdsk.dsk.unit, gdsk.dsk.part, kname);
356		}
357		if (ioctrl & IO_SERIAL)
358			sio_flush();
359		*cmd = '\0';
360		if (keyhit(0))
361			getstr(cmd, sizeof(cmd));
362		else if (!OPT_CHECK(RBX_QUIET))
363			putchar('\n');
364		if (parse_cmds(cmd, &dskupdated)) {
365			putchar('\a');
366			continue;
367		}
368		if (dskupdated && gptinit() != 0)
369			continue;
370		load();
371	}
372	/* NOTREACHED */
373}
374
375/* XXX - Needed for btxld to link the boot2 binary; do not remove. */
376void
377exit(int x)
378{
379
380	while (1);
381	__unreachable();
382}
383
384static void
385load(void)
386{
387	union {
388		struct exec ex;
389		Elf32_Ehdr eh;
390	} hdr;
391	static Elf32_Phdr ep[2];
392	static Elf32_Shdr es[2];
393	caddr_t p;
394	ufs_ino_t ino;
395	uint32_t addr, x;
396	int fmt, i, j;
397
398	if (!(ino = lookup(kname))) {
399		if (!ls) {
400			printf("%s: No %s on %u:%s(%up%u)\n", BOOTPROG,
401			    kname, gdsk.dsk.drive & DRV_MASK,
402			    dev_nm[gdsk.dsk.type], gdsk.dsk.unit,
403			    gdsk.dsk.part);
404		}
405		return;
406	}
407	if (xfsread(ino, &hdr, sizeof(hdr)))
408		return;
409	if (N_GETMAGIC(hdr.ex) == ZMAGIC)
410		fmt = 0;
411	else if (IS_ELF(hdr.eh))
412		fmt = 1;
413	else {
414		printf("Invalid %s\n", "format");
415		return;
416	}
417	if (fmt == 0) {
418		addr = hdr.ex.a_entry & 0xffffff;
419		p = PTOV(addr);
420		fs_off = PAGE_SIZE;
421		if (xfsread(ino, p, hdr.ex.a_text))
422			return;
423		p += roundup2(hdr.ex.a_text, PAGE_SIZE);
424		if (xfsread(ino, p, hdr.ex.a_data))
425			return;
426		p += hdr.ex.a_data + roundup2(hdr.ex.a_bss, PAGE_SIZE);
427		bootinfo.bi_symtab = VTOP(p);
428		memcpy(p, &hdr.ex.a_syms, sizeof(hdr.ex.a_syms));
429		p += sizeof(hdr.ex.a_syms);
430		if (hdr.ex.a_syms) {
431			if (xfsread(ino, p, hdr.ex.a_syms))
432				return;
433			p += hdr.ex.a_syms;
434			if (xfsread(ino, p, sizeof(int)))
435				return;
436			x = *(uint32_t *)p;
437			p += sizeof(int);
438			x -= sizeof(int);
439			if (xfsread(ino, p, x))
440				return;
441			p += x;
442		}
443	} else {
444		fs_off = hdr.eh.e_phoff;
445		for (j = i = 0; i < hdr.eh.e_phnum && j < 2; i++) {
446			if (xfsread(ino, ep + j, sizeof(ep[0])))
447				return;
448			if (ep[j].p_type == PT_LOAD)
449				j++;
450		}
451		for (i = 0; i < 2; i++) {
452			p = PTOV(ep[i].p_paddr & 0xffffff);
453			fs_off = ep[i].p_offset;
454			if (xfsread(ino, p, ep[i].p_filesz))
455				return;
456		}
457		p += roundup2(ep[1].p_memsz, PAGE_SIZE);
458		bootinfo.bi_symtab = VTOP(p);
459		if (hdr.eh.e_shnum == hdr.eh.e_shstrndx + 3) {
460			fs_off = hdr.eh.e_shoff + sizeof(es[0]) *
461			    (hdr.eh.e_shstrndx + 1);
462			if (xfsread(ino, &es, sizeof(es)))
463				return;
464			for (i = 0; i < 2; i++) {
465				memcpy(p, &es[i].sh_size,
466				    sizeof(es[i].sh_size));
467				p += sizeof(es[i].sh_size);
468				fs_off = es[i].sh_offset;
469				if (xfsread(ino, p, es[i].sh_size))
470					return;
471				p += es[i].sh_size;
472			}
473		}
474		addr = hdr.eh.e_entry & 0xffffff;
475	}
476	bootinfo.bi_esymtab = VTOP(p);
477	bootinfo.bi_kernelname = VTOP(kname);
478	bootinfo.bi_bios_dev = gdsk.dsk.drive;
479#ifdef LOADER_GELI_SUPPORT
480	geliargs.size = sizeof(geliargs);
481	explicit_bzero(gelipw, sizeof(gelipw));
482	export_geli_boot_data(&geliargs.gelidata);
483#endif
484	/*
485	 * Note that the geliargs struct is passed by value, not by pointer.
486	 * Code in btxldr.S copies the values from the entry stack to a fixed
487	 * location within loader(8) at startup due to the presence of the
488	 * KARGS_FLAGS_EXTARG flag.
489	 */
490	__exec((caddr_t)addr, RB_BOOTINFO | (opts & RBX_MASK),
491	    MAKEBOOTDEV(dev_maj[gdsk.dsk.type], gdsk.dsk.part + 1, gdsk.dsk.unit, 0xff),
492#ifdef LOADER_GELI_SUPPORT
493	    KARGS_FLAGS_GELI | KARGS_FLAGS_EXTARG, 0, 0, VTOP(&bootinfo), geliargs
494#else
495	    0, 0, 0, VTOP(&bootinfo)
496#endif
497	    );
498}
499
500static int
501parse_cmds(char *cmdstr, int *dskupdated)
502{
503	char *arg;
504	char *ep, *p, *q;
505	const char *cp;
506	unsigned int drv;
507	int c, i, j;
508
509	arg = cmdstr;
510	*dskupdated = 0;
511	while ((c = *arg++)) {
512		if (c == ' ' || c == '\t' || c == '\n')
513			continue;
514		for (p = arg; *p && *p != '\n' && *p != ' ' && *p != '\t'; p++);
515		ep = p;
516		if (*p)
517			*p++ = 0;
518		if (c == '-') {
519			while ((c = *arg++)) {
520				if (c == 'P') {
521					if (*(uint8_t *)PTOV(0x496) & 0x10) {
522						cp = "yes";
523					} else {
524						opts |= OPT_SET(RBX_DUAL) |
525						    OPT_SET(RBX_SERIAL);
526						cp = "no";
527					}
528					printf("Keyboard: %s\n", cp);
529					continue;
530				} else if (c == 'S') {
531					j = 0;
532					while ((unsigned int)(i = *arg++ - '0')
533					    <= 9)
534						j = j * 10 + i;
535					if (j > 0 && i == -'0') {
536						comspeed = j;
537						break;
538					}
539					/*
540					 * Fall through to error below
541					 * ('S' not in optstr[]).
542					 */
543				}
544				for (i = 0; c != optstr[i]; i++)
545					if (i == NOPT - 1)
546						return (-1);
547				opts ^= OPT_SET(flags[i]);
548			}
549			ioctrl = OPT_CHECK(RBX_DUAL) ? (IO_SERIAL|IO_KEYBOARD) :
550			    OPT_CHECK(RBX_SERIAL) ? IO_SERIAL : IO_KEYBOARD;
551			if (ioctrl & IO_SERIAL) {
552				if (sio_init(115200 / comspeed) != 0)
553					ioctrl &= ~IO_SERIAL;
554			}
555		} else {
556			for (q = arg--; *q && *q != '('; q++);
557			if (*q) {
558				drv = -1;
559				if (arg[1] == ':') {
560					drv = *arg - '0';
561					if (drv > 9)
562						return (-1);
563					arg += 2;
564				}
565				if (q - arg != 2)
566					return (-1);
567				for (i = 0; arg[0] != dev_nm[i][0] ||
568				    arg[1] != dev_nm[i][1]; i++)
569					if (i == NDEV - 1)
570						return (-1);
571				gdsk.dsk.type = i;
572				arg += 3;
573				gdsk.dsk.unit = *arg - '0';
574				if (arg[1] != 'p' || gdsk.dsk.unit > 9)
575					return (-1);
576				arg += 2;
577				gdsk.dsk.part = *arg - '0';
578				if (gdsk.dsk.part < 1 || gdsk.dsk.part > 9)
579					return (-1);
580				arg++;
581				if (arg[0] != ')')
582					return (-1);
583				arg++;
584				if (drv == -1)
585					drv = gdsk.dsk.unit;
586				gdsk.dsk.drive = (gdsk.dsk.type <= TYPE_MAXHARD
587				    ? DRV_HARD : 0) + drv;
588				*dskupdated = 1;
589			}
590			if ((i = ep - arg)) {
591				if ((size_t)i >= sizeof(kname))
592					return (-1);
593				memcpy(kname, arg, i + 1);
594			}
595		}
596		arg = p;
597	}
598	return (0);
599}
600
601static int
602dskread(void *buf, daddr_t lba, unsigned nblk)
603{
604	int err;
605
606	err = drvread(&gdsk.dsk, buf, lba + gdsk.dsk.start, nblk);
607
608#ifdef LOADER_GELI_SUPPORT
609	if (err == 0 && gdsk.gdev != NULL) {
610		/* Decrypt */
611		if (geli_read(gdsk.gdev, lba * DEV_BSIZE, buf,
612		    nblk * DEV_BSIZE))
613			return (err);
614	}
615#endif
616
617	return (err);
618}
619
620#ifdef LOADER_GELI_SUPPORT
621/*
622 * Read function compatible with the ZFS callback, required to keep the GELI
623 * implementation the same for both UFS and ZFS.
624 */
625static int
626vdev_read(void *vdev __unused, void *priv, off_t off, void *buf, size_t bytes)
627{
628	char *p;
629	daddr_t lba;
630	unsigned int nb;
631	struct gptdsk *dskp;
632
633	dskp = (struct gptdsk *)priv;
634
635	if ((off & (DEV_BSIZE - 1)) || (bytes & (DEV_BSIZE - 1)))
636		return (-1);
637
638	p = buf;
639	lba = off / DEV_BSIZE;
640	lba += dskp->dsk.start;
641
642	while (bytes > 0) {
643		nb = bytes / DEV_BSIZE;
644		if (nb > VBLKSIZE / DEV_BSIZE)
645			nb = VBLKSIZE / DEV_BSIZE;
646		if (drvread(&dskp->dsk, dmadat->blkbuf, lba, nb))
647			return (-1);
648		memcpy(p, dmadat->blkbuf, nb * DEV_BSIZE);
649		p += nb * DEV_BSIZE;
650		lba += nb;
651		bytes -= nb * DEV_BSIZE;
652	}
653
654	return (0);
655}
656#endif /* LOADER_GELI_SUPPORT */
657