gptboot.c revision 132764
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 132764 2004-07-28 06:03:27Z kan $");
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
142static void memcpy(char *, const char *, int);
143static void
144memcpy(char *dst, const char *src, int len)
145{
146    while (len--)
147        *dst++ = *src++;
148}
149
150static inline int
151strcmp(const char *s1, const char *s2)
152{
153    for (; *s1 == *s2 && *s1; s1++, s2++);
154    return (unsigned char)*s1 - (unsigned char)*s2;
155}
156
157#include "ufsread.c"
158
159static int
160xfsread(ino_t inode, void *buf, size_t nbyte)
161{
162    if ((size_t)fsread(inode, buf, nbyte) != nbyte) {
163	printf("Invalid %s\n", "format");
164	return -1;
165    }
166    return 0;
167}
168
169static inline uint32_t
170memsize(void)
171{
172    v86.addr = MEM_EXT;
173    v86.eax = 0x8800;
174    v86int();
175    return v86.eax;
176}
177
178static inline void
179getstr(void)
180{
181    char *s;
182    int c;
183
184    s = cmd;
185    for (;;) {
186	switch (c = xgetc(0)) {
187	case 0:
188	    break;
189	case '\177':
190	case '\b':
191	    if (s > cmd) {
192		s--;
193		printf("\b \b");
194	    }
195	    break;
196	case '\n':
197	case '\r':
198	    *s = 0;
199	    return;
200	default:
201	    if (s - cmd < sizeof(cmd) - 1)
202		*s++ = c;
203	    putchar(c);
204	}
205    }
206}
207
208static inline void
209putc(int c)
210{
211    v86.addr = 0x10;
212    v86.eax = 0xe00 | (c & 0xff);
213    v86.ebx = 0x7;
214    v86int();
215}
216
217int
218main(void)
219{
220    int autoboot;
221    ino_t ino;
222
223    dmadat = (void *)(roundup2(__base + (int32_t)&_end, 0x10000) - __base);
224    v86.ctl = V86_FLAGS;
225    dsk.drive = *(uint8_t *)PTOV(ARGS);
226    dsk.type = dsk.drive & DRV_HARD ? TYPE_AD : TYPE_FD;
227    dsk.unit = dsk.drive & DRV_MASK;
228    dsk.slice = *(uint8_t *)PTOV(ARGS + 1) + 1;
229    bootinfo.bi_version = BOOTINFO_VERSION;
230    bootinfo.bi_size = sizeof(bootinfo);
231    bootinfo.bi_basemem = 0;	/* XXX will be filled by loader or kernel */
232    bootinfo.bi_extmem = memsize();
233    bootinfo.bi_memsizes_valid++;
234
235    /* Process configuration file */
236
237    autoboot = 1;
238
239    if ((ino = lookup(PATH_CONFIG)))
240	fsread(ino, cmd, sizeof(cmd));
241
242    if (*cmd) {
243	printf("%s: %s", PATH_CONFIG, cmd);
244	if (parse())
245	    autoboot = 0;
246	/* Do not process this command twice */
247	*cmd = 0;
248    }
249
250    /*
251     * Try to exec stage 3 boot loader. If interrupted by a keypress,
252     * or in case of failure, try to load a kernel directly instead.
253     */
254
255    if (autoboot && !*kname) {
256	memcpy(kname, PATH_BOOT3, sizeof(PATH_BOOT3));
257	if (!keyhit(3*SECOND)) {
258	    load();
259	    memcpy(kname, PATH_KERNEL, sizeof(PATH_KERNEL));
260	}
261    }
262
263    /* Present the user with the boot2 prompt. */
264
265    for (;;) {
266	printf("\nFreeBSD/i386 boot\n"
267	       "Default: %u:%s(%u,%c)%s\n"
268	       "boot: ",
269	       dsk.drive & DRV_MASK, dev_nm[dsk.type], dsk.unit,
270	       'a' + dsk.part, kname);
271	if (ioctrl & IO_SERIAL)
272	    sio_flush();
273	if (!autoboot || keyhit(5*SECOND))
274	    getstr();
275	else
276	    putchar('\n');
277	autoboot = 0;
278	if (parse())
279	    putchar('\a');
280	else
281	    load();
282    }
283}
284
285/* XXX - Needed for btxld to link the boot2 binary; do not remove. */
286void
287exit(int x)
288{
289}
290
291static void
292load(void)
293{
294    union {
295	struct exec ex;
296	Elf32_Ehdr eh;
297    } hdr;
298    Elf32_Phdr ep[2];
299    Elf32_Shdr es[2];
300    caddr_t p;
301    ino_t ino;
302    uint32_t addr, x;
303    int fmt, i, j;
304
305    if (!(ino = lookup(kname))) {
306	if (!ls)
307	    printf("No %s\n", kname);
308	return;
309    }
310    if (xfsread(ino, &hdr, sizeof(hdr)))
311	return;
312    if (N_GETMAGIC(hdr.ex) == ZMAGIC)
313	fmt = 0;
314    else if (IS_ELF(hdr.eh))
315	fmt = 1;
316    else {
317	printf("Invalid %s\n", "format");
318	return;
319    }
320    if (fmt == 0) {
321	addr = hdr.ex.a_entry & 0xffffff;
322	p = PTOV(addr);
323	fs_off = PAGE_SIZE;
324	if (xfsread(ino, p, hdr.ex.a_text))
325	    return;
326	p += roundup2(hdr.ex.a_text, PAGE_SIZE);
327	if (xfsread(ino, p, hdr.ex.a_data))
328	    return;
329	p += hdr.ex.a_data + roundup2(hdr.ex.a_bss, PAGE_SIZE);
330	bootinfo.bi_symtab = VTOP(p);
331	memcpy(p, &hdr.ex.a_syms, sizeof(hdr.ex.a_syms));
332	p += sizeof(hdr.ex.a_syms);
333	if (hdr.ex.a_syms) {
334	    if (xfsread(ino, p, hdr.ex.a_syms))
335		return;
336	    p += hdr.ex.a_syms;
337	    if (xfsread(ino, p, sizeof(int)))
338		return;
339	    x = *(uint32_t *)p;
340	    p += sizeof(int);
341	    x -= sizeof(int);
342	    if (xfsread(ino, p, x))
343		return;
344	    p += x;
345	}
346    } else {
347	fs_off = hdr.eh.e_phoff;
348	for (j = i = 0; i < hdr.eh.e_phnum && j < 2; i++) {
349	    if (xfsread(ino, ep + j, sizeof(ep[0])))
350		return;
351	    if (ep[j].p_type == PT_LOAD)
352		j++;
353	}
354	for (i = 0; i < 2; i++) {
355	    p = PTOV(ep[i].p_paddr & 0xffffff);
356	    fs_off = ep[i].p_offset;
357	    if (xfsread(ino, p, ep[i].p_filesz))
358		return;
359	}
360	p += roundup2(ep[1].p_memsz, PAGE_SIZE);
361	bootinfo.bi_symtab = VTOP(p);
362	if (hdr.eh.e_shnum == hdr.eh.e_shstrndx + 3) {
363	    fs_off = hdr.eh.e_shoff + sizeof(es[0]) *
364		(hdr.eh.e_shstrndx + 1);
365	    if (xfsread(ino, &es, sizeof(es)))
366		return;
367	    for (i = 0; i < 2; i++) {
368		memcpy(p, &es[i].sh_size, sizeof(es[i].sh_size));
369		p += sizeof(es[i].sh_size);
370		fs_off = es[i].sh_offset;
371		if (xfsread(ino, p, es[i].sh_size))
372		    return;
373		p += es[i].sh_size;
374	    }
375	}
376	addr = hdr.eh.e_entry & 0xffffff;
377    }
378    bootinfo.bi_esymtab = VTOP(p);
379    bootinfo.bi_kernelname = VTOP(kname);
380    bootinfo.bi_bios_dev = dsk.drive;
381    __exec((caddr_t)addr, RB_BOOTINFO | (opts & RBX_MASK),
382	   MAKEBOOTDEV(dev_maj[dsk.type], 0, dsk.slice, dsk.unit, dsk.part),
383	   0, 0, 0, VTOP(&bootinfo));
384}
385
386static int
387parse()
388{
389    char *arg = cmd;
390    char *p, *q;
391    unsigned int drv;
392    int c, i;
393
394    while ((c = *arg++)) {
395	if (c == ' ' || c == '\t' || c == '\n')
396	    continue;
397	for (p = arg; *p && *p != '\n' && *p != ' ' && *p != '\t'; p++);
398	if (*p)
399	    *p++ = 0;
400	if (c == '-') {
401	    while ((c = *arg++)) {
402		for (i = 0; c != optstr[i]; i++)
403		    if (i == NOPT - 1)
404			return -1;
405		opts ^= 1 << flags[i];
406	    }
407	    if (opts & 1 << RBX_PROBEKBD) {
408		i = *(uint8_t *)PTOV(0x496) & 0x10;
409		printf("Keyboard: %s\n", i ? "yes" : "no");
410		if (!i)
411		    opts |= 1 << RBX_DUAL | 1 << RBX_SERIAL;
412		opts &= ~(1 << RBX_PROBEKBD);
413	    }
414	    ioctrl = opts & 1 << RBX_DUAL ? (IO_SERIAL|IO_KEYBOARD) :
415		     opts & 1 << RBX_SERIAL ? IO_SERIAL : IO_KEYBOARD;
416	    if (ioctrl & IO_SERIAL)
417	        sio_init();
418	} else {
419	    for (q = arg--; *q && *q != '('; q++);
420	    if (*q) {
421		drv = -1;
422		if (arg[1] == ':') {
423		    drv = *arg - '0';
424		    if (drv > 9)
425			return (-1);
426		    arg += 2;
427		}
428		if (q - arg != 2)
429		    return -1;
430		for (i = 0; arg[0] != dev_nm[i][0] ||
431			    arg[1] != dev_nm[i][1]; i++)
432		    if (i == NDEV - 1)
433			return -1;
434		dsk.type = i;
435		arg += 3;
436		dsk.unit = *arg - '0';
437		if (arg[1] != ',' || dsk.unit > 9)
438		    return -1;
439		arg += 2;
440		dsk.slice = WHOLE_DISK_SLICE;
441		if (arg[1] == ',') {
442		    dsk.slice = *arg - '0' + 1;
443		    if (dsk.slice > NDOSPART)
444			return -1;
445		    arg += 2;
446		}
447		if (arg[1] != ')')
448		    return -1;
449		dsk.part = *arg - 'a';
450		if (dsk.part > 7)
451		    return (-1);
452		arg += 2;
453		if (drv == -1)
454		    drv = dsk.unit;
455		dsk.drive = (dsk.type <= TYPE_MAXHARD
456			     ? DRV_HARD : 0) + drv;
457		dsk_meta = 0;
458	    }
459	    if ((i = p - arg - !*(p - 1))) {
460		if ((size_t)i >= sizeof(kname))
461		    return -1;
462		memcpy(kname, arg, i + 1);
463	    }
464	}
465	arg = p;
466    }
467    return 0;
468}
469
470static int
471dskread(void *buf, unsigned lba, unsigned nblk)
472{
473    struct dos_partition *dp;
474    struct disklabel *d;
475    char *sec;
476    unsigned sl, i;
477
478    if (!dsk_meta) {
479	sec = dmadat->secbuf;
480	dsk.start = 0;
481	if (drvread(sec, DOSBBSECTOR, 1))
482	    return -1;
483	dp = (void *)(sec + DOSPARTOFF);
484	sl = dsk.slice;
485	if (sl < BASE_SLICE) {
486	    for (i = 0; i < NDOSPART; i++)
487		if (dp[i].dp_typ == DOSPTYP_386BSD &&
488		    (dp[i].dp_flag & 0x80 || sl < BASE_SLICE)) {
489		    sl = BASE_SLICE + i;
490		    if (dp[i].dp_flag & 0x80 ||
491			dsk.slice == COMPATIBILITY_SLICE)
492			break;
493		}
494	    if (dsk.slice == WHOLE_DISK_SLICE)
495		dsk.slice = sl;
496	}
497	if (sl != WHOLE_DISK_SLICE) {
498	    if (sl != COMPATIBILITY_SLICE)
499		dp += sl - BASE_SLICE;
500	    if (dp->dp_typ != DOSPTYP_386BSD) {
501		printf("Invalid %s\n", "slice");
502		return -1;
503	    }
504	    dsk.start = dp->dp_start;
505	}
506	if (drvread(sec, dsk.start + LABELSECTOR, 1))
507		return -1;
508	d = (void *)(sec + LABELOFFSET);
509	if (d->d_magic != DISKMAGIC || d->d_magic2 != DISKMAGIC) {
510	    if (dsk.part != RAW_PART) {
511		printf("Invalid %s\n", "label");
512		return -1;
513	    }
514	} else {
515	    if (!dsk.init) {
516		if (d->d_type == DTYPE_SCSI)
517		    dsk.type = TYPE_DA;
518		dsk.init++;
519	    }
520	    if (dsk.part >= d->d_npartitions ||
521		!d->d_partitions[dsk.part].p_size) {
522		printf("Invalid %s\n", "partition");
523		return -1;
524	    }
525	    dsk.start += d->d_partitions[dsk.part].p_offset;
526	    dsk.start -= d->d_partitions[RAW_PART].p_offset;
527	}
528    }
529    return drvread(buf, dsk.start + lba, nblk);
530}
531
532static void
533printf(const char *fmt,...)
534{
535    va_list ap;
536    char buf[10];
537    char *s;
538    unsigned u;
539    int c;
540
541    va_start(ap, fmt);
542    while ((c = *fmt++)) {
543	if (c == '%') {
544	    c = *fmt++;
545	    switch (c) {
546	    case 'c':
547		putchar(va_arg(ap, int));
548		continue;
549	    case 's':
550		for (s = va_arg(ap, char *); *s; s++)
551		    putchar(*s);
552		continue;
553	    case 'u':
554		u = va_arg(ap, unsigned);
555		s = buf;
556		do
557		    *s++ = '0' + u % 10U;
558		while (u /= 10U);
559		while (--s >= buf)
560		    putchar(*s);
561		continue;
562	    }
563	}
564	putchar(c);
565    }
566    va_end(ap);
567    return;
568}
569
570static void
571putchar(int c)
572{
573    if (c == '\n')
574	xputc('\r');
575    xputc(c);
576}
577
578static int
579drvread(void *buf, unsigned lba, unsigned nblk)
580{
581    static unsigned c = 0x2d5c7c2f;
582
583    printf("%c\b", c = c << 8 | c >> 24);
584    v86.ctl = V86_ADDR | V86_CALLF | V86_FLAGS;
585    v86.addr = XREADORG;		/* call to xread in boot1 */
586    v86.es = VTOPSEG(buf);
587    v86.eax = lba;
588    v86.ebx = VTOPOFF(buf);
589    v86.ecx = lba >> 16;
590    v86.edx = nblk << 8 | dsk.drive;
591    v86int();
592    v86.ctl = V86_FLAGS;
593    if (V86_CY(v86.efl)) {
594	printf("error %u lba %u\n", v86.eax >> 8 & 0xff, lba);
595	return -1;
596    }
597    return 0;
598}
599
600static int
601keyhit(unsigned ticks)
602{
603    uint32_t t0, t1;
604
605    if (opts & 1 << RBX_NOINTR)
606	return 0;
607    t0 = 0;
608    for (;;) {
609	if (xgetc(1))
610	    return 1;
611	t1 = *(uint32_t *)PTOV(0x46c);
612	if (!t0)
613	    t0 = t1;
614	if (t1 < t0 || t1 >= t0 + ticks)
615	    return 0;
616    }
617}
618
619static int
620xputc(int c)
621{
622    if (ioctrl & IO_KEYBOARD)
623	putc(c);
624    if (ioctrl & IO_SERIAL)
625	sio_putc(c);
626    return c;
627}
628
629static int
630xgetc(int fn)
631{
632    if (opts & 1 << RBX_NOINTR)
633	return 0;
634    for (;;) {
635	if (ioctrl & IO_KEYBOARD && getc(1))
636	    return fn ? 1 : getc(0);
637	if (ioctrl & IO_SERIAL && sio_ischar())
638	    return fn ? 1 : sio_getc();
639	if (fn)
640	    return 0;
641    }
642}
643
644static int
645getc(int fn)
646{
647    v86.addr = 0x16;
648    v86.eax = fn << 8;
649    v86int();
650    return fn == 0 ? v86.eax & 0xff : !V86_ZR(v86.efl);
651}
652