gptboot.c revision 126891
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: head/sys/boot/i386/gptboot/gptboot.c 126891 2004-03-12 21:45:33Z trhodes $");
18
19#include <sys/param.h>
20#include <sys/disklabel.h>
21#include <sys/diskmbr.h>
22#include <sys/dirent.h>
23#include <sys/reboot.h>
24
25#include <machine/bootinfo.h>
26#include <machine/elf.h>
27
28#include <stdarg.h>
29
30#include <a.out.h>
31
32#include <btxv86.h>
33
34#include "boot2.h"
35#include "lib.h"
36
37#define IO_KEYBOARD	1
38#define IO_SERIAL	2
39
40#define SECOND		18	/* Circa that many ticks in a second. */
41
42#define RBX_ASKNAME	0x0	/* -a */
43#define RBX_SINGLE	0x1	/* -s */
44/* 0x2 is reserved for log2(RB_NOSYNC). */
45/* 0x3 is reserved for log2(RB_HALT). */
46/* 0x4 is reserved for log2(RB_INITNAME). */
47#define RBX_DFLTROOT	0x5	/* -r */
48#define RBX_KDB 	0x6	/* -d */
49/* 0x7 is reserved for log2(RB_RDONLY). */
50/* 0x8 is reserved for log2(RB_DUMP). */
51/* 0x9 is reserved for log2(RB_MINIROOT). */
52#define RBX_CONFIG	0xa	/* -c */
53#define RBX_VERBOSE	0xb	/* -v */
54#define RBX_SERIAL	0xc	/* -h */
55#define RBX_CDROM	0xd	/* -C */
56/* 0xe is reserved for log2(RB_POWEROFF). */
57#define RBX_GDB 	0xf	/* -g */
58#define RBX_MUTE	0x10	/* -m */
59/* 0x11 is reserved for log2(RB_SELFTEST). */
60/* 0x12 is reserved for boot programs. */
61/* 0x13 is reserved for boot programs. */
62#define RBX_PAUSE	0x14	/* -p */
63#define RBX_NOINTR	0x1c	/* -n */
64/* 0x1d is reserved for log2(RB_MULTIPLE) and is just misnamed here. */
65#define RBX_DUAL	0x1d	/* -D */
66#define RBX_PROBEKBD	0x1e	/* -P */
67/* 0x1f is reserved for log2(RB_BOOTINFO). */
68
69/* pass: -a, -s, -r, -d, -c, -v, -h, -C, -g, -m, -p, -D */
70#define RBX_MASK	0x2005ffff
71
72#define PATH_CONFIG	"/boot.config"
73#define PATH_BOOT3	"/boot/loader"
74#define PATH_KERNEL	"/kernel"
75
76#define ARGS		0x900
77#define NOPT		12
78#define NDEV		3
79#define MEM_BASE	0x12
80#define MEM_EXT 	0x15
81#define V86_CY(x)	((x) & 1)
82#define V86_ZR(x)	((x) & 0x40)
83
84#define DRV_HARD	0x80
85#define DRV_MASK	0x7f
86
87#define TYPE_AD		0
88#define TYPE_DA		1
89#define TYPE_MAXHARD	TYPE_DA
90#define TYPE_FD		2
91
92extern uint32_t _end;
93
94static const char optstr[NOPT] = "DhaCgmnPprsv";
95static const unsigned char flags[NOPT] = {
96    RBX_DUAL,
97    RBX_SERIAL,
98    RBX_ASKNAME,
99    RBX_CDROM,
100    RBX_GDB,
101    RBX_MUTE,
102    RBX_NOINTR,
103    RBX_PROBEKBD,
104    RBX_PAUSE,
105    RBX_DFLTROOT,
106    RBX_SINGLE,
107    RBX_VERBOSE
108};
109
110static const char *const dev_nm[NDEV] = {"ad", "da", "fd"};
111static const unsigned char dev_maj[NDEV] = {30, 4, 2};
112
113static struct dsk {
114    unsigned drive;
115    unsigned type;
116    unsigned unit;
117    unsigned slice;
118    unsigned part;
119    unsigned start;
120    int init;
121} dsk;
122static char cmd[512];
123static char kname[1024];
124static uint32_t opts;
125static struct bootinfo bootinfo;
126static uint8_t ioctrl = IO_KEYBOARD;
127
128void exit(int);
129static void load(void);
130static int parse(void);
131static int xfsread(ino_t, void *, size_t);
132static int dskread(void *, unsigned, unsigned);
133static void printf(const char *,...);
134static void putchar(int);
135static uint32_t memsize(void);
136static int drvread(void *, unsigned, unsigned);
137static int keyhit(unsigned);
138static int xputc(int);
139static int xgetc(int);
140static int getc(int);
141
142#if defined(__GNUC__) || defined(__INTEL_COMPILER)
143#define memcpy __builtin_memcpy
144#else
145static void memcpy(char *, const char *, int);
146static void
147memcpy(char *dst, const char *src, int len)
148{
149    while (len--)
150        *dst++ = *src++;
151}
152#endif
153
154static inline int
155strcmp(const char *s1, const char *s2)
156{
157    for (; *s1 == *s2 && *s1; s1++, s2++);
158    return (unsigned char)*s1 - (unsigned char)*s2;
159}
160
161#include "ufsread.c"
162
163static int
164xfsread(ino_t inode, void *buf, size_t nbyte)
165{
166    if ((size_t)fsread(inode, buf, nbyte) != nbyte) {
167	printf("Invalid %s\n", "format");
168	return -1;
169    }
170    return 0;
171}
172
173static inline uint32_t
174memsize(void)
175{
176    v86.addr = MEM_EXT;
177    v86.eax = 0x8800;
178    v86int();
179    return v86.eax;
180}
181
182static inline void
183getstr(void)
184{
185    char *s;
186    int c;
187
188    s = cmd;
189    for (;;) {
190	switch (c = xgetc(0)) {
191	case 0:
192	    break;
193	case '\177':
194	case '\b':
195	    if (s > cmd) {
196		s--;
197		printf("\b \b");
198	    }
199	    break;
200	case '\n':
201	case '\r':
202	    *s = 0;
203	    return;
204	default:
205	    if (s - cmd < sizeof(cmd) - 1)
206		*s++ = c;
207	    putchar(c);
208	}
209    }
210}
211
212static inline void
213putc(int c)
214{
215    v86.addr = 0x10;
216    v86.eax = 0xe00 | (c & 0xff);
217    v86.ebx = 0x7;
218    v86int();
219}
220
221int
222main(void)
223{
224    int autoboot;
225    ino_t ino;
226
227    dmadat = (void *)(roundup2(__base + (int32_t)&_end, 0x10000) - __base);
228    v86.ctl = V86_FLAGS;
229    dsk.drive = *(uint8_t *)PTOV(ARGS);
230    dsk.type = dsk.drive & DRV_HARD ? TYPE_AD : TYPE_FD;
231    dsk.unit = dsk.drive & DRV_MASK;
232    dsk.slice = *(uint8_t *)PTOV(ARGS + 1) + 1;
233    bootinfo.bi_version = BOOTINFO_VERSION;
234    bootinfo.bi_size = sizeof(bootinfo);
235    bootinfo.bi_basemem = 0;	/* XXX will be filled by loader or kernel */
236    bootinfo.bi_extmem = memsize();
237    bootinfo.bi_memsizes_valid++;
238
239    /* Process configuration file */
240
241    autoboot = 1;
242
243    if ((ino = lookup(PATH_CONFIG)))
244	fsread(ino, cmd, sizeof(cmd));
245
246    if (*cmd) {
247	printf("%s: %s", PATH_CONFIG, cmd);
248	if (parse())
249	    autoboot = 0;
250	/* Do not process this command twice */
251	*cmd = 0;
252    }
253
254    /*
255     * Try to exec stage 3 boot loader. If interrupted by a keypress,
256     * or in case of failure, try to load a kernel directly instead.
257     */
258
259    if (autoboot && !*kname) {
260	memcpy(kname, PATH_BOOT3, sizeof(PATH_BOOT3));
261	if (!keyhit(3*SECOND)) {
262	    load();
263	    memcpy(kname, PATH_KERNEL, sizeof(PATH_KERNEL));
264	}
265    }
266
267    /* Present the user with the boot2 prompt. */
268
269    for (;;) {
270	printf("\nFreeBSD/i386 boot\n"
271	       "Default: %u:%s(%u,%c)%s\n"
272	       "boot: ",
273	       dsk.drive & DRV_MASK, dev_nm[dsk.type], dsk.unit,
274	       'a' + dsk.part, kname);
275	if (ioctrl & IO_SERIAL)
276	    sio_flush();
277	if (!autoboot || keyhit(5*SECOND))
278	    getstr();
279	else
280	    putchar('\n');
281	autoboot = 0;
282	if (parse())
283	    putchar('\a');
284	else
285	    load();
286    }
287}
288
289/* XXX - Needed for btxld to link the boot2 binary; do not remove. */
290void
291exit(int x)
292{
293}
294
295static void
296load(void)
297{
298    union {
299	struct exec ex;
300	Elf32_Ehdr eh;
301    } hdr;
302    Elf32_Phdr ep[2];
303    Elf32_Shdr es[2];
304    caddr_t p;
305    ino_t ino;
306    uint32_t addr, x;
307    int fmt, i, j;
308
309    if (!(ino = lookup(kname))) {
310	if (!ls)
311	    printf("No %s\n", kname);
312	return;
313    }
314    if (xfsread(ino, &hdr, sizeof(hdr)))
315	return;
316    if (N_GETMAGIC(hdr.ex) == ZMAGIC)
317	fmt = 0;
318    else if (IS_ELF(hdr.eh))
319	fmt = 1;
320    else {
321	printf("Invalid %s\n", "format");
322	return;
323    }
324    if (fmt == 0) {
325	addr = hdr.ex.a_entry & 0xffffff;
326	p = PTOV(addr);
327	fs_off = PAGE_SIZE;
328	if (xfsread(ino, p, hdr.ex.a_text))
329	    return;
330	p += roundup2(hdr.ex.a_text, PAGE_SIZE);
331	if (xfsread(ino, p, hdr.ex.a_data))
332	    return;
333	p += hdr.ex.a_data + roundup2(hdr.ex.a_bss, PAGE_SIZE);
334	bootinfo.bi_symtab = VTOP(p);
335	memcpy(p, &hdr.ex.a_syms, sizeof(hdr.ex.a_syms));
336	p += sizeof(hdr.ex.a_syms);
337	if (hdr.ex.a_syms) {
338	    if (xfsread(ino, p, hdr.ex.a_syms))
339		return;
340	    p += hdr.ex.a_syms;
341	    if (xfsread(ino, p, sizeof(int)))
342		return;
343	    x = *(uint32_t *)p;
344	    p += sizeof(int);
345	    x -= sizeof(int);
346	    if (xfsread(ino, p, x))
347		return;
348	    p += x;
349	}
350    } else {
351	fs_off = hdr.eh.e_phoff;
352	for (j = i = 0; i < hdr.eh.e_phnum && j < 2; i++) {
353	    if (xfsread(ino, ep + j, sizeof(ep[0])))
354		return;
355	    if (ep[j].p_type == PT_LOAD)
356		j++;
357	}
358	for (i = 0; i < 2; i++) {
359	    p = PTOV(ep[i].p_paddr & 0xffffff);
360	    fs_off = ep[i].p_offset;
361	    if (xfsread(ino, p, ep[i].p_filesz))
362		return;
363	}
364	p += roundup2(ep[1].p_memsz, PAGE_SIZE);
365	bootinfo.bi_symtab = VTOP(p);
366	if (hdr.eh.e_shnum == hdr.eh.e_shstrndx + 3) {
367	    fs_off = hdr.eh.e_shoff + sizeof(es[0]) *
368		(hdr.eh.e_shstrndx + 1);
369	    if (xfsread(ino, &es, sizeof(es)))
370		return;
371	    for (i = 0; i < 2; i++) {
372		memcpy(p, &es[i].sh_size, sizeof(es[i].sh_size));
373		p += sizeof(es[i].sh_size);
374		fs_off = es[i].sh_offset;
375		if (xfsread(ino, p, es[i].sh_size))
376		    return;
377		p += es[i].sh_size;
378	    }
379	}
380	addr = hdr.eh.e_entry & 0xffffff;
381    }
382    bootinfo.bi_esymtab = VTOP(p);
383    bootinfo.bi_kernelname = VTOP(kname);
384    bootinfo.bi_bios_dev = dsk.drive;
385    __exec((caddr_t)addr, RB_BOOTINFO | (opts & RBX_MASK),
386	   MAKEBOOTDEV(dev_maj[dsk.type], 0, dsk.slice, dsk.unit, dsk.part),
387	   0, 0, 0, VTOP(&bootinfo));
388}
389
390static int
391parse()
392{
393    char *arg = cmd;
394    char *p, *q;
395    unsigned int drv;
396    int c, i;
397
398    while ((c = *arg++)) {
399	if (c == ' ' || c == '\t' || c == '\n')
400	    continue;
401	for (p = arg; *p && *p != '\n' && *p != ' ' && *p != '\t'; p++);
402	if (*p)
403	    *p++ = 0;
404	if (c == '-') {
405	    while ((c = *arg++)) {
406		for (i = 0; c != optstr[i]; i++)
407		    if (i == NOPT - 1)
408			return -1;
409		opts ^= 1 << flags[i];
410	    }
411	    if (opts & 1 << RBX_PROBEKBD) {
412		i = *(uint8_t *)PTOV(0x496) & 0x10;
413		printf("Keyboard: %s\n", i ? "yes" : "no");
414		if (!i)
415		    opts |= 1 << RBX_DUAL | 1 << RBX_SERIAL;
416		opts &= ~(1 << RBX_PROBEKBD);
417	    }
418	    ioctrl = opts & 1 << RBX_DUAL ? (IO_SERIAL|IO_KEYBOARD) :
419		     opts & 1 << RBX_SERIAL ? IO_SERIAL : IO_KEYBOARD;
420	    if (ioctrl & IO_SERIAL)
421	        sio_init();
422	} else {
423	    for (q = arg--; *q && *q != '('; q++);
424	    if (*q) {
425		drv = -1;
426		if (arg[1] == ':') {
427		    drv = *arg - '0';
428		    if (drv > 9)
429			return (-1);
430		    arg += 2;
431		}
432		if (q - arg != 2)
433		    return -1;
434		for (i = 0; arg[0] != dev_nm[i][0] ||
435			    arg[1] != dev_nm[i][1]; i++)
436		    if (i == NDEV - 1)
437			return -1;
438		dsk.type = i;
439		arg += 3;
440		dsk.unit = *arg - '0';
441		if (arg[1] != ',' || dsk.unit > 9)
442		    return -1;
443		arg += 2;
444		dsk.slice = WHOLE_DISK_SLICE;
445		if (arg[1] == ',') {
446		    dsk.slice = *arg - '0' + 1;
447		    if (dsk.slice > NDOSPART)
448			return -1;
449		    arg += 2;
450		}
451		if (arg[1] != ')')
452		    return -1;
453		dsk.part = *arg - 'a';
454		if (dsk.part > 7)
455		    return (-1);
456		arg += 2;
457		if (drv == -1)
458		    drv = dsk.unit;
459		dsk.drive = (dsk.type <= TYPE_MAXHARD
460			     ? DRV_HARD : 0) + drv;
461		dsk_meta = 0;
462	    }
463	    if ((i = p - arg - !*(p - 1))) {
464		if ((size_t)i >= sizeof(kname))
465		    return -1;
466		memcpy(kname, arg, i + 1);
467	    }
468	}
469	arg = p;
470    }
471    return 0;
472}
473
474static int
475dskread(void *buf, unsigned lba, unsigned nblk)
476{
477    struct dos_partition *dp;
478    struct disklabel *d;
479    char *sec;
480    unsigned sl, i;
481
482    if (!dsk_meta) {
483	sec = dmadat->secbuf;
484	dsk.start = 0;
485	if (drvread(sec, DOSBBSECTOR, 1))
486	    return -1;
487	dp = (void *)(sec + DOSPARTOFF);
488	sl = dsk.slice;
489	if (sl < BASE_SLICE) {
490	    for (i = 0; i < NDOSPART; i++)
491		if (dp[i].dp_typ == DOSPTYP_386BSD &&
492		    (dp[i].dp_flag & 0x80 || sl < BASE_SLICE)) {
493		    sl = BASE_SLICE + i;
494		    if (dp[i].dp_flag & 0x80 ||
495			dsk.slice == COMPATIBILITY_SLICE)
496			break;
497		}
498	    if (dsk.slice == WHOLE_DISK_SLICE)
499		dsk.slice = sl;
500	}
501	if (sl != WHOLE_DISK_SLICE) {
502	    if (sl != COMPATIBILITY_SLICE)
503		dp += sl - BASE_SLICE;
504	    if (dp->dp_typ != DOSPTYP_386BSD) {
505		printf("Invalid %s\n", "slice");
506		return -1;
507	    }
508	    dsk.start = dp->dp_start;
509	}
510	if (drvread(sec, dsk.start + LABELSECTOR, 1))
511		return -1;
512	d = (void *)(sec + LABELOFFSET);
513	if (d->d_magic != DISKMAGIC || d->d_magic2 != DISKMAGIC) {
514	    if (dsk.part != RAW_PART) {
515		printf("Invalid %s\n", "label");
516		return -1;
517	    }
518	} else {
519	    if (!dsk.init) {
520		if (d->d_type == DTYPE_SCSI)
521		    dsk.type = TYPE_DA;
522		dsk.init++;
523	    }
524	    if (dsk.part >= d->d_npartitions ||
525		!d->d_partitions[dsk.part].p_size) {
526		printf("Invalid %s\n", "partition");
527		return -1;
528	    }
529	    dsk.start += d->d_partitions[dsk.part].p_offset;
530	    dsk.start -= d->d_partitions[RAW_PART].p_offset;
531	}
532    }
533    return drvread(buf, dsk.start + lba, nblk);
534}
535
536static void
537printf(const char *fmt,...)
538{
539    va_list ap;
540    char buf[10];
541    char *s;
542    unsigned u;
543    int c;
544
545    va_start(ap, fmt);
546    while ((c = *fmt++)) {
547	if (c == '%') {
548	    c = *fmt++;
549	    switch (c) {
550	    case 'c':
551		putchar(va_arg(ap, int));
552		continue;
553	    case 's':
554		for (s = va_arg(ap, char *); *s; s++)
555		    putchar(*s);
556		continue;
557	    case 'u':
558		u = va_arg(ap, unsigned);
559		s = buf;
560		do
561		    *s++ = '0' + u % 10U;
562		while (u /= 10U);
563		while (--s >= buf)
564		    putchar(*s);
565		continue;
566	    }
567	}
568	putchar(c);
569    }
570    va_end(ap);
571    return;
572}
573
574static void
575putchar(int c)
576{
577    if (c == '\n')
578	xputc('\r');
579    xputc(c);
580}
581
582static int
583drvread(void *buf, unsigned lba, unsigned nblk)
584{
585    static unsigned c = 0x2d5c7c2f;
586
587    printf("%c\b", c = c << 8 | c >> 24);
588    v86.ctl = V86_ADDR | V86_CALLF | V86_FLAGS;
589    v86.addr = XREADORG;		/* call to xread in boot1 */
590    v86.es = VTOPSEG(buf);
591    v86.eax = lba;
592    v86.ebx = VTOPOFF(buf);
593    v86.ecx = lba >> 16;
594    v86.edx = nblk << 8 | dsk.drive;
595    v86int();
596    v86.ctl = V86_FLAGS;
597    if (V86_CY(v86.efl)) {
598	printf("error %u lba %u\n", v86.eax >> 8 & 0xff, lba);
599	return -1;
600    }
601    return 0;
602}
603
604static int
605keyhit(unsigned ticks)
606{
607    uint32_t t0, t1;
608
609    if (opts & 1 << RBX_NOINTR)
610	return 0;
611    t0 = 0;
612    for (;;) {
613	if (xgetc(1))
614	    return 1;
615	t1 = *(uint32_t *)PTOV(0x46c);
616	if (!t0)
617	    t0 = t1;
618	if (t1 < t0 || t1 >= t0 + ticks)
619	    return 0;
620    }
621}
622
623static int
624xputc(int c)
625{
626    if (ioctrl & IO_KEYBOARD)
627	putc(c);
628    if (ioctrl & IO_SERIAL)
629	sio_putc(c);
630    return c;
631}
632
633static int
634xgetc(int fn)
635{
636    if (opts & 1 << RBX_NOINTR)
637	return 0;
638    for (;;) {
639	if (ioctrl & IO_KEYBOARD && getc(1))
640	    return fn ? 1 : getc(0);
641	if (ioctrl & IO_SERIAL && sio_ischar())
642	    return fn ? 1 : sio_getc();
643	if (fn)
644	    return 0;
645    }
646}
647
648static int
649getc(int fn)
650{
651    v86.addr = 0x16;
652    v86.eax = fn << 8;
653    v86int();
654    return fn == 0 ? v86.eax & 0xff : !V86_ZR(v86.efl);
655}
656