gptboot.c revision 98542
1343171Sdim/*
2343171Sdim * Copyright (c) 1998 Robert Nordier
3353358Sdim * All rights reserved.
4353358Sdim *
5353358Sdim * Redistribution and use in source and binary forms are freely
6343171Sdim * permitted provided that the above copyright notice and this
7343171Sdim * paragraph and the following disclaimer are duplicated in all
8343171Sdim * such forms.
9343171Sdim *
10343171Sdim * This software is provided "AS IS" and without any express or
11343171Sdim * implied warranties, including, without limitation, the implied
12343171Sdim * warranties of merchantability and fitness for a particular
13343171Sdim * purpose.
14343171Sdim */
15343171Sdim
16343171Sdim/*
17343171Sdim * $FreeBSD: head/sys/boot/i386/gptboot/gptboot.c 98542 2002-06-21 06:18:05Z mckusick $
18343171Sdim */
19343171Sdim
20343171Sdim#include <sys/param.h>
21343171Sdim#include <sys/reboot.h>
22343171Sdim#include <sys/diskslice.h>
23343171Sdim#include <sys/disklabel.h>
24343171Sdim#include <sys/dirent.h>
25343171Sdim#include <machine/bootinfo.h>
26343171Sdim#include <machine/elf.h>
27343171Sdim
28343171Sdim#include <stdarg.h>
29343171Sdim
30343171Sdim#include <a.out.h>
31343171Sdim
32343171Sdim#include <btxv86.h>
33343171Sdim
34343171Sdim#include "boot2.h"
35343171Sdim#include "lib.h"
36343171Sdim
37343171Sdim#define IO_KEYBOARD	1
38343171Sdim#define IO_SERIAL	2
39343171Sdim
40343171Sdim#define SECOND		18	/* Circa that many ticks in a second. */
41343171Sdim
42343171Sdim#define RBX_ASKNAME	0x0	/* -a */
43343171Sdim#define RBX_SINGLE	0x1	/* -s */
44343171Sdim#define RBX_DFLTROOT	0x5	/* -r */
45343171Sdim#define RBX_KDB 	0x6	/* -d */
46343171Sdim#define RBX_CONFIG	0xa	/* -c */
47343171Sdim#define RBX_VERBOSE	0xb	/* -v */
48343171Sdim#define RBX_SERIAL	0xc	/* -h */
49343171Sdim#define RBX_CDROM	0xd	/* -C */
50343171Sdim#define RBX_GDB 	0xf	/* -g */
51343171Sdim#define RBX_MUTE	0x10	/* -m */
52343171Sdim#define RBX_PAUSE	0x12	/* -p */
53343171Sdim#define RBX_DUAL	0x1d	/* -D */
54343171Sdim#define RBX_PROBEKBD	0x1e	/* -P */
55343171Sdim#define RBX_NOINTR	0x1f	/* -n */
56343171Sdim
57343171Sdim#define RBX_MASK	0x2005ffff
58343171Sdim
59343171Sdim#define PATH_CONFIG	"/boot.config"
60343171Sdim#define PATH_BOOT3	"/boot/loader"
61343171Sdim#define PATH_KERNEL	"/kernel"
62343171Sdim
63343171Sdim#define ARGS		0x900
64343171Sdim#define NOPT		14
65343171Sdim#define NDEV		5
66343171Sdim#define MEM_BASE	0x12
67343171Sdim#define MEM_EXT 	0x15
68343171Sdim#define V86_CY(x)	((x) & 1)
69343171Sdim#define V86_ZR(x)	((x) & 0x40)
70343171Sdim
71343171Sdim#define DRV_HARD	0x80
72343171Sdim#define DRV_MASK	0x7f
73343171Sdim
74343171Sdim#define TYPE_AD		0
75#define TYPE_WD		1
76#define TYPE_DA		2
77#define TYPE_MAXHARD	TYPE_DA
78#define TYPE_WFD 	3
79#define TYPE_FD		4
80
81extern uint32_t _end;
82
83static const char optstr[NOPT] = "DhaCcdgmnPprsv";
84static const unsigned char flags[NOPT] = {
85    RBX_DUAL,
86    RBX_SERIAL,
87    RBX_ASKNAME,
88    RBX_CDROM,
89    RBX_CONFIG,
90    RBX_KDB,
91    RBX_GDB,
92    RBX_MUTE,
93    RBX_NOINTR,
94    RBX_PROBEKBD,
95    RBX_PAUSE,
96    RBX_DFLTROOT,
97    RBX_SINGLE,
98    RBX_VERBOSE
99};
100
101static const char *const dev_nm[NDEV] = {"ad", "wd", "da", "  ", "fd"};
102static const unsigned char dev_maj[NDEV] = {30, 0, 4, 1, 2};
103
104static struct dsk {
105    unsigned drive;
106    unsigned type;
107    unsigned unit;
108    unsigned slice;
109    unsigned part;
110    unsigned start;
111    int init;
112} dsk;
113static char cmd[512];
114static char kname[1024];
115static uint32_t opts = RB_BOOTINFO;
116static struct bootinfo bootinfo;
117static uint8_t ioctrl = IO_KEYBOARD;
118
119void exit(int);
120static void load(const char *);
121static int parse(char *);
122static int xfsread(ino_t, void *, size_t);
123static int dskread(void *, unsigned, unsigned);
124static int printf(const char *,...);
125static int putchar(int);
126static uint32_t memsize(int);
127static int drvread(void *, unsigned, unsigned);
128static int keyhit(unsigned);
129static int xputc(int);
130static int xgetc(int);
131static int getc(int);
132
133#if 1
134#define memcpy __builtin_memcpy
135#else
136static void memcpy(char *, const char *, int);
137static void
138memcpy(char *dst, const char *src, int len)
139{
140    while (len--)
141	*dst++ = *src++;
142}
143#endif
144
145static inline int
146strcmp(const char *s1, const char *s2)
147{
148    for (; *s1 == *s2 && *s1; s1++, s2++);
149    return (u_char)*s1 - (u_char)*s2;
150}
151
152#include "ufsread.c"
153
154static int
155xfsread(ino_t inode, void *buf, size_t nbyte)
156{
157    if (fsread(inode, buf, nbyte) != nbyte) {
158	printf("Invalid %s\n", "format");
159	return -1;
160    }
161    return 0;
162}
163
164static inline void
165getstr(char *str, int size)
166{
167    char *s;
168    int c;
169
170    s = str;
171    for (;;) {
172	switch (c = xgetc(0)) {
173	case 0:
174	    break;
175	case '\177':
176	    c = '\b';
177	case '\b':
178	    if (s > str) {
179		s--;
180		putchar('\b');
181		putchar(' ');
182	    } else
183		c = 0;
184	    break;
185	case '\n':
186	case '\r':
187	    *s = 0;
188	    return;
189	default:
190	    if (s - str < size - 1)
191		*s++ = c;
192	}
193	if (c)
194	    putchar(c);
195    }
196}
197
198static inline uint32_t
199drvinfo(int drive)
200{
201    v86.addr = 0x13;
202    v86.eax = 0x800;
203    v86.edx = DRV_HARD + drive;
204    v86int();
205    if (V86_CY(v86.efl))
206	return 0x4f010f;
207    return ((v86.ecx & 0xc0) << 18) | ((v86.ecx & 0xff00) << 8) |
208	   (v86.edx & 0xff00) | (v86.ecx & 0x3f);
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, i;
224    ino_t ino;
225
226    dmadat = (void *)(roundup2(__base + _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 = memsize(MEM_BASE);
235    bootinfo.bi_extmem = memsize(MEM_EXT);
236    bootinfo.bi_memsizes_valid++;
237    for (i = 0; i < N_BIOS_GEOM; i++)
238	bootinfo.bi_bios_geom[i] = drvinfo(i);
239
240    /* Process configuration file */
241
242    autoboot = 1;
243
244    if ((ino = lookup(PATH_CONFIG)))
245	fsread(ino, cmd, sizeof(cmd));
246
247    if (*cmd) {
248	printf("%s: %s", PATH_CONFIG, cmd);
249	if (parse(cmd))
250	    autoboot = 0;
251	/* Do not process this command twice */
252	*cmd = 0;
253    }
254
255    /*
256     * Try to exec stage 3 boot loader. If interrupted by a keypress,
257     * or in case of failure, try to load a kernel directly instead.
258     */
259
260    if (autoboot && !*kname) {
261	memcpy(kname, PATH_BOOT3, sizeof(PATH_BOOT3));
262	if (!keyhit(3*SECOND)) {
263	    load(kname);
264	    memcpy(kname, PATH_KERNEL, sizeof(PATH_KERNEL));
265	}
266    }
267
268    /* Present the user with the boot2 prompt. */
269
270    for (;;) {
271	printf(" \n>> FreeBSD/i386 BOOT\n"
272	       "Default: %u:%s(%u,%c)%s\n"
273	       "boot: ",
274	       dsk.drive & DRV_MASK, dev_nm[dsk.type], dsk.unit,
275	       'a' + dsk.part, kname);
276	if (ioctrl & IO_SERIAL)
277	    sio_flush();
278	if (!autoboot || keyhit(5*SECOND))
279	    getstr(cmd, sizeof(cmd));
280	else
281	    putchar('\n');
282	autoboot = 0;
283	if (parse(cmd))
284	    putchar('\a');
285	else
286	    load(kname);
287    }
288}
289
290/* XXX - Needed for btxld to link the boot2 binary; do not remove. */
291void
292exit(int x)
293{
294}
295
296static void
297load(const char *fname)
298{
299    union {
300	struct exec ex;
301	Elf32_Ehdr eh;
302    } hdr;
303    Elf32_Phdr ep[2];
304    Elf32_Shdr es[2];
305    caddr_t p;
306    ino_t ino;
307    uint32_t addr, x;
308    int fmt, i, j;
309
310    if (!(ino = lookup(fname))) {
311	if (!ls)
312	    printf("No %s\n", fname);
313	return;
314    }
315    if (xfsread(ino, &hdr, sizeof(hdr)))
316	return;
317    if (N_GETMAGIC(hdr.ex) == ZMAGIC)
318	fmt = 0;
319    else if (IS_ELF(hdr.eh))
320	fmt = 1;
321    else {
322	printf("Invalid %s\n", "format");
323	return;
324    }
325    if (fmt == 0) {
326	addr = hdr.ex.a_entry & 0xffffff;
327	p = PTOV(addr);
328	fs_off = PAGE_SIZE;
329	if (xfsread(ino, p, hdr.ex.a_text))
330	    return;
331	p += roundup2(hdr.ex.a_text, PAGE_SIZE);
332	if (xfsread(ino, p, hdr.ex.a_data))
333	    return;
334	p += hdr.ex.a_data + roundup2(hdr.ex.a_bss, PAGE_SIZE);
335	bootinfo.bi_symtab = VTOP(p);
336	memcpy(p, (char *)&hdr.ex.a_syms, sizeof(hdr.ex.a_syms));
337	p += sizeof(hdr.ex.a_syms);
338	if (hdr.ex.a_syms) {
339	    if (xfsread(ino, p, hdr.ex.a_syms))
340		return;
341	    p += hdr.ex.a_syms;
342	    if (xfsread(ino, p, sizeof(int)))
343		return;
344	    x = *(uint32_t *)p;
345	    p += sizeof(int);
346	    x -= sizeof(int);
347	    if (xfsread(ino, p, x))
348		return;
349	    p += x;
350	}
351    } else {
352	fs_off = hdr.eh.e_phoff;
353	for (j = i = 0; i < hdr.eh.e_phnum && j < 2; i++) {
354	    if (xfsread(ino, ep + j, sizeof(ep[0])))
355		return;
356	    if (ep[j].p_type == PT_LOAD)
357		j++;
358	}
359	for (i = 0; i < 2; i++) {
360	    p = PTOV(ep[i].p_paddr & 0xffffff);
361	    fs_off = ep[i].p_offset;
362	    if (xfsread(ino, p, ep[i].p_filesz))
363		return;
364	}
365	p += roundup2(ep[1].p_memsz, PAGE_SIZE);
366	bootinfo.bi_symtab = VTOP(p);
367	if (hdr.eh.e_shnum == hdr.eh.e_shstrndx + 3) {
368	    fs_off = hdr.eh.e_shoff + sizeof(es[0]) *
369		(hdr.eh.e_shstrndx + 1);
370	    if (xfsread(ino, &es, sizeof(es)))
371		return;
372	    for (i = 0; i < 2; i++) {
373		memcpy(p, (char *)&es[i].sh_size, sizeof(es[i].sh_size));
374		p += sizeof(es[i].sh_size);
375		fs_off = es[i].sh_offset;
376		if (xfsread(ino, p, es[i].sh_size))
377		    return;
378		p += es[i].sh_size;
379	    }
380	}
381	addr = hdr.eh.e_entry & 0xffffff;
382    }
383    bootinfo.bi_esymtab = VTOP(p);
384    bootinfo.bi_kernelname = VTOP(fname);
385    bootinfo.bi_bios_dev = dsk.drive;
386    __exec((caddr_t)addr, opts & RBX_MASK,
387	   MAKEBOOTDEV(dev_maj[dsk.type], 0, dsk.slice, dsk.unit, dsk.part),
388	   0, 0, 0, VTOP(&bootinfo));
389}
390
391static int
392parse(char *arg)
393{
394    char *p, *q;
395    int drv, 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		    if (*arg < '0' || *arg > '9')
427			return -1;
428		    drv = *arg - '0';
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		if (arg[1] != ',' || *arg < '0' || *arg > '9')
440		    return -1;
441		dsk.unit = *arg - '0';
442		arg += 2;
443		dsk.slice = WHOLE_DISK_SLICE;
444		if (arg[1] == ',') {
445		    if (*arg < '0' || *arg > '0' + NDOSPART)
446			return -1;
447		    if ((dsk.slice = *arg - '0'))
448			dsk.slice++;
449		    arg += 2;
450		}
451		if (arg[1] != ')' || *arg < 'a' || *arg > 'p')
452		    return -1;
453		dsk.part = *arg - 'a';
454		arg += 2;
455		if (drv == -1)
456		    drv = dsk.unit;
457		dsk.drive = (dsk.type <= TYPE_MAXHARD
458			     ? DRV_HARD : 0) + drv;
459		dsk_meta = 0;
460		fsread(0, NULL, 0);
461	    }
462	    if ((i = p - arg - !*(p - 1))) {
463		if (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	}
530    }
531    return drvread(buf, dsk.start + lba, nblk);
532}
533
534static int
535printf(const char *fmt,...)
536{
537    static const char digits[16] = "0123456789abcdef";
538    va_list ap;
539    char buf[10];
540    char *s;
541    unsigned r, 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	    case 'x':
558		r = c == 'u' ? 10U : 16U;
559		u = va_arg(ap, unsigned);
560		s = buf;
561		do
562		    *s++ = digits[u % r];
563		while (u /= r);
564		while (--s >= buf)
565		    putchar(*s);
566		continue;
567	    }
568	}
569	putchar(c);
570    }
571    va_end(ap);
572    return 0;
573}
574
575static int
576putchar(int c)
577{
578    if (c == '\n')
579	xputc('\r');
580    return xputc(c);
581}
582
583static uint32_t
584memsize(int type)
585{
586    v86.addr = type;
587    v86.eax = 0x8800;
588    v86int();
589    return v86.eax;
590}
591
592static int
593drvread(void *buf, unsigned lba, unsigned nblk)
594{
595    static unsigned c = 0x2d5c7c2f;
596
597    printf("%c\b", c = c << 8 | c >> 24);
598    v86.ctl = V86_ADDR | V86_CALLF | V86_FLAGS;
599    v86.addr = XREADORG;		/* call to xread in boot1 */
600    v86.es = VTOPSEG(buf);
601    v86.eax = lba;
602    v86.ebx = VTOPOFF(buf);
603    v86.ecx = lba >> 16;
604    v86.edx = nblk << 8 | dsk.drive;
605    v86int();
606    v86.ctl = V86_FLAGS;
607    if (V86_CY(v86.efl)) {
608	printf("Disk error 0x%x (lba=0x%x)\n", v86.eax >> 8 & 0xff,
609	       lba);
610	return -1;
611    }
612    return 0;
613}
614
615static int
616keyhit(unsigned ticks)
617{
618    uint32_t t0, t1;
619
620    if (opts & 1 << RBX_NOINTR)
621	return 0;
622    t0 = 0;
623    for (;;) {
624	if (xgetc(1))
625	    return 1;
626	t1 = *(uint32_t *)PTOV(0x46c);
627	if (!t0)
628	    t0 = t1;
629	if (t1 < t0 || t1 >= t0 + ticks)
630	    return 0;
631    }
632}
633
634static int
635xputc(int c)
636{
637    if (ioctrl & IO_KEYBOARD)
638	putc(c);
639    if (ioctrl & IO_SERIAL)
640	sio_putc(c);
641    return c;
642}
643
644static int
645xgetc(int fn)
646{
647    if (opts & 1 << RBX_NOINTR)
648	return 0;
649    for (;;) {
650	if (ioctrl & IO_KEYBOARD && getc(1))
651	    return fn ? 1 : getc(0);
652	if (ioctrl & IO_SERIAL && sio_ischar())
653	    return fn ? 1 : sio_getc();
654	if (fn)
655	    return 0;
656    }
657}
658
659static int
660getc(int fn)
661{
662    v86.addr = 0x16;
663    v86.eax = fn << 8;
664    v86int();
665    return fn == 0 ? v86.eax & 0xff : !V86_ZR(v86.efl);
666}
667