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