gptboot.c revision 107875
1231200Smm/*
2231200Smm * Copyright (c) 1998 Robert Nordier
3231200Smm * All rights reserved.
4231200Smm *
5231200Smm * Redistribution and use in source and binary forms are freely
6231200Smm * permitted provided that the above copyright notice and this
7231200Smm * paragraph and the following disclaimer are duplicated in all
8231200Smm * such forms.
9231200Smm *
10231200Smm * This software is provided "AS IS" and without any express or
11231200Smm * implied warranties, including, without limitation, the implied
12231200Smm * warranties of merchantability and fitness for a particular
13231200Smm * purpose.
14231200Smm */
15231200Smm
16231200Smm/*
17231200Smm * $FreeBSD: head/sys/boot/i386/gptboot/gptboot.c 107875 2002-12-14 19:15:27Z phk $
18231200Smm */
19231200Smm
20231200Smm#include <sys/param.h>
21231200Smm#include <sys/reboot.h>
22231200Smm#include <sys/diskslice.h>
23231200Smm#include <sys/disklabel.h>
24231200Smm#include <sys/diskmbr.h>
25231200Smm#include <sys/dirent.h>
26231200Smm#include <machine/bootinfo.h>
27231200Smm#include <machine/elf.h>
28231200Smm
29231200Smm#include <stdarg.h>
30231200Smm
31231200Smm#include <a.out.h>
32231200Smm
33231200Smm#include <btxv86.h>
34231200Smm
35231200Smm#include "boot2.h"
36231200Smm#include "lib.h"
37231200Smm
38231200Smm#define IO_KEYBOARD	1
39231200Smm#define IO_SERIAL	2
40231200Smm
41231200Smm#define SECOND		18	/* Circa that many ticks in a second. */
42231200Smm
43231200Smm#define RBX_ASKNAME	0x0	/* -a */
44231200Smm#define RBX_SINGLE	0x1	/* -s */
45231200Smm#define RBX_DFLTROOT	0x5	/* -r */
46231200Smm#define RBX_KDB 	0x6	/* -d */
47231200Smm#define RBX_CONFIG	0xa	/* -c */
48231200Smm#define RBX_VERBOSE	0xb	/* -v */
49231200Smm#define RBX_SERIAL	0xc	/* -h */
50231200Smm#define RBX_CDROM	0xd	/* -C */
51231200Smm#define RBX_GDB 	0xf	/* -g */
52231200Smm#define RBX_MUTE	0x10	/* -m */
53231200Smm#define RBX_PAUSE	0x12	/* -p */
54231200Smm#define RBX_DUAL	0x1d	/* -D */
55231200Smm#define RBX_PROBEKBD	0x1e	/* -P */
56231200Smm#define RBX_NOINTR	0x1f	/* -n */
57231200Smm
58231200Smm#define RBX_MASK	0x2005ffff
59231200Smm
60231200Smm#define PATH_CONFIG	"/boot.config"
61231200Smm#define PATH_BOOT3	"/boot/loader"
62231200Smm#define PATH_KERNEL	"/kernel"
63231200Smm
64231200Smm#define ARGS		0x900
65231200Smm#define NOPT		14
66231200Smm#define NDEV		5
67231200Smm#define MEM_BASE	0x12
68231200Smm#define MEM_EXT 	0x15
69231200Smm#define V86_CY(x)	((x) & 1)
70231200Smm#define V86_ZR(x)	((x) & 0x40)
71231200Smm
72231200Smm#define DRV_HARD	0x80
73231200Smm#define DRV_MASK	0x7f
74231200Smm
75231200Smm#define TYPE_AD		0
76231200Smm#define TYPE_DA		2
77231200Smm#define TYPE_MAXHARD	TYPE_DA
78231200Smm#define TYPE_FD		4
79231200Smm
80231200Smmextern uint32_t _end;
81231200Smm
82231200Smmstatic const char optstr[NOPT] = "DhaCcdgmnPprsv";
83231200Smmstatic const unsigned char flags[NOPT] = {
84231200Smm    RBX_DUAL,
85231200Smm    RBX_SERIAL,
86231200Smm    RBX_ASKNAME,
87231200Smm    RBX_CDROM,
88231200Smm    RBX_CONFIG,
89231200Smm    RBX_KDB,
90231200Smm    RBX_GDB,
91231200Smm    RBX_MUTE,
92231200Smm    RBX_NOINTR,
93231200Smm    RBX_PROBEKBD,
94231200Smm    RBX_PAUSE,
95231200Smm    RBX_DFLTROOT,
96231200Smm    RBX_SINGLE,
97231200Smm    RBX_VERBOSE
98231200Smm};
99231200Smm
100231200Smmstatic const char *const dev_nm[NDEV] = {"ad", "wd", "da", "  ", "fd"};
101231200Smmstatic const unsigned char dev_maj[NDEV] = {30, 0, 4, 1, 2};
102231200Smm
103231200Smmstatic struct dsk {
104231200Smm    unsigned drive;
105231200Smm    unsigned type;
106231200Smm    unsigned unit;
107231200Smm    unsigned slice;
108231200Smm    unsigned part;
109231200Smm    unsigned start;
110231200Smm    int init;
111231200Smm} dsk;
112231200Smmstatic char cmd[512];
113231200Smmstatic char kname[1024];
114231200Smmstatic uint32_t opts = RB_BOOTINFO;
115231200Smmstatic struct bootinfo bootinfo;
116231200Smmstatic uint8_t ioctrl = IO_KEYBOARD;
117231200Smm
118231200Smmvoid exit(int);
119231200Smmstatic void load(const char *);
120231200Smmstatic int parse(char *);
121231200Smmstatic int xfsread(ino_t, void *, size_t);
122231200Smmstatic int dskread(void *, unsigned, unsigned);
123231200Smmstatic void printf(const char *,...);
124231200Smmstatic void putchar(int);
125231200Smmstatic uint32_t memsize(int);
126231200Smmstatic int drvread(void *, unsigned, unsigned);
127231200Smmstatic int keyhit(unsigned);
128231200Smmstatic int xputc(int);
129231200Smmstatic int xgetc(int);
130231200Smmstatic int getc(int);
131231200Smm
132231200Smm#define memcpy __builtin_memcpy
133231200Smm
134231200Smmstatic inline int
135231200Smmstrcmp(const char *s1, const char *s2)
136231200Smm{
137231200Smm    for (; *s1 == *s2 && *s1; s1++, s2++);
138231200Smm    return (u_char)*s1 - (u_char)*s2;
139231200Smm}
140231200Smm
141231200Smm#include "ufsread.c"
142231200Smm
143231200Smmstatic int
144231200Smmxfsread(ino_t inode, void *buf, size_t nbyte)
145231200Smm{
146231200Smm    if (fsread(inode, buf, nbyte) != nbyte) {
147231200Smm	printf("Invalid %s\n", "format");
148231200Smm	return -1;
149231200Smm    }
150231200Smm    return 0;
151231200Smm}
152231200Smm
153231200Smmstatic inline void
154231200Smmgetstr(char *str, int size)
155231200Smm{
156231200Smm    char *s;
157231200Smm    int c;
158231200Smm
159231200Smm    s = str;
160231200Smm    for (;;) {
161231200Smm	switch (c = xgetc(0)) {
162231200Smm	case 0:
163231200Smm	    break;
164231200Smm	case '\177':
165231200Smm	    c = '\b';
166231200Smm	case '\b':
167231200Smm	    if (s > str) {
168231200Smm		s--;
169231200Smm		putchar('\b');
170231200Smm		putchar(' ');
171231200Smm	    } else
172231200Smm		c = 0;
173231200Smm	    break;
174231200Smm	case '\n':
175231200Smm	case '\r':
176231200Smm	    *s = 0;
177231200Smm	    return;
178231200Smm	default:
179231200Smm	    if (s - str < size - 1)
180231200Smm		*s++ = c;
181231200Smm	}
182231200Smm	if (c)
183231200Smm	    putchar(c);
184231200Smm    }
185231200Smm}
186231200Smm
187231200Smmstatic inline void
188231200Smmputc(int c)
189231200Smm{
190231200Smm    v86.addr = 0x10;
191231200Smm    v86.eax = 0xe00 | (c & 0xff);
192231200Smm    v86.ebx = 0x7;
193231200Smm    v86int();
194231200Smm}
195231200Smm
196231200Smmint
197231200Smmmain(void)
198231200Smm{
199231200Smm    int autoboot;
200231200Smm    ino_t ino;
201231200Smm
202231200Smm    dmadat = (void *)(roundup2(__base + (int32_t)&_end, 0x10000) - __base);
203231200Smm    v86.ctl = V86_FLAGS;
204231200Smm    dsk.drive = *(uint8_t *)PTOV(ARGS);
205231200Smm    dsk.type = dsk.drive & DRV_HARD ? TYPE_AD : TYPE_FD;
206231200Smm    dsk.unit = dsk.drive & DRV_MASK;
207231200Smm    dsk.slice = *(uint8_t *)PTOV(ARGS + 1) + 1;
208231200Smm    bootinfo.bi_version = BOOTINFO_VERSION;
209231200Smm    bootinfo.bi_size = sizeof(bootinfo);
210231200Smm    bootinfo.bi_basemem = 0;	/* XXX will be filled by loader or kernel */
211231200Smm    bootinfo.bi_extmem = memsize(MEM_EXT);
212231200Smm    bootinfo.bi_memsizes_valid++;
213231200Smm
214231200Smm    /* Process configuration file */
215231200Smm
216231200Smm    autoboot = 1;
217231200Smm
218231200Smm    if ((ino = lookup(PATH_CONFIG)))
219231200Smm	fsread(ino, cmd, sizeof(cmd));
220231200Smm
221231200Smm    if (*cmd) {
222231200Smm	printf("%s: %s", PATH_CONFIG, cmd);
223231200Smm	if (parse(cmd))
224231200Smm	    autoboot = 0;
225231200Smm	/* Do not process this command twice */
226231200Smm	*cmd = 0;
227231200Smm    }
228231200Smm
229231200Smm    /*
230231200Smm     * Try to exec stage 3 boot loader. If interrupted by a keypress,
231231200Smm     * or in case of failure, try to load a kernel directly instead.
232231200Smm     */
233231200Smm
234231200Smm    if (autoboot && !*kname) {
235231200Smm	memcpy(kname, PATH_BOOT3, sizeof(PATH_BOOT3));
236231200Smm	if (!keyhit(3*SECOND)) {
237231200Smm	    load(kname);
238231200Smm	    memcpy(kname, PATH_KERNEL, sizeof(PATH_KERNEL));
239231200Smm	}
240231200Smm    }
241231200Smm
242231200Smm    /* Present the user with the boot2 prompt. */
243231200Smm
244231200Smm    for (;;) {
245231200Smm#ifdef UFS1_ONLY
246231200Smm	printf(" \n>> FreeBSD/i386/UFS1 BOOT\n"
247231200Smm#else
248231200Smm	printf(" \n>> FreeBSD/i386/UFS[12] BOOT\n"
249231200Smm#endif
250248616Smm	       "Default: %u:%s(%u,%c)%s\n"
251231200Smm	       "boot: ",
252231200Smm	       dsk.drive & DRV_MASK, dev_nm[dsk.type], dsk.unit,
253231200Smm	       'a' + dsk.part, kname);
254231200Smm	if (ioctrl & IO_SERIAL)
255231200Smm	    sio_flush();
256231200Smm	if (!autoboot || keyhit(5*SECOND))
257231200Smm	    getstr(cmd, sizeof(cmd));
258231200Smm	else
259231200Smm	    putchar('\n');
260231200Smm	autoboot = 0;
261231200Smm	if (parse(cmd))
262231200Smm	    putchar('\a');
263231200Smm	else
264231200Smm	    load(kname);
265231200Smm    }
266231200Smm}
267231200Smm
268231200Smm/* XXX - Needed for btxld to link the boot2 binary; do not remove. */
269231200Smmvoid
270231200Smmexit(int x)
271231200Smm{
272231200Smm}
273231200Smm
274231200Smmstatic void
275231200Smmload(const char *fname)
276231200Smm{
277231200Smm    union {
278231200Smm	struct exec ex;
279	Elf32_Ehdr eh;
280    } hdr;
281    Elf32_Phdr ep[2];
282    Elf32_Shdr es[2];
283    caddr_t p;
284    ino_t ino;
285    uint32_t addr, x;
286    int fmt, i, j;
287
288    if (!(ino = lookup(fname))) {
289	if (!ls)
290	    printf("No %s\n", fname);
291	return;
292    }
293    if (xfsread(ino, &hdr, sizeof(hdr)))
294	return;
295    if (N_GETMAGIC(hdr.ex) == ZMAGIC)
296	fmt = 0;
297    else if (IS_ELF(hdr.eh))
298	fmt = 1;
299    else {
300	printf("Invalid %s\n", "format");
301	return;
302    }
303    if (fmt == 0) {
304	addr = hdr.ex.a_entry & 0xffffff;
305	p = PTOV(addr);
306	fs_off = PAGE_SIZE;
307	if (xfsread(ino, p, hdr.ex.a_text))
308	    return;
309	p += roundup2(hdr.ex.a_text, PAGE_SIZE);
310	if (xfsread(ino, p, hdr.ex.a_data))
311	    return;
312	p += hdr.ex.a_data + roundup2(hdr.ex.a_bss, PAGE_SIZE);
313	bootinfo.bi_symtab = VTOP(p);
314	memcpy(p, (char *)&hdr.ex.a_syms, sizeof(hdr.ex.a_syms));
315	p += sizeof(hdr.ex.a_syms);
316	if (hdr.ex.a_syms) {
317	    if (xfsread(ino, p, hdr.ex.a_syms))
318		return;
319	    p += hdr.ex.a_syms;
320	    if (xfsread(ino, p, sizeof(int)))
321		return;
322	    x = *(uint32_t *)p;
323	    p += sizeof(int);
324	    x -= sizeof(int);
325	    if (xfsread(ino, p, x))
326		return;
327	    p += x;
328	}
329    } else {
330	fs_off = hdr.eh.e_phoff;
331	for (j = i = 0; i < hdr.eh.e_phnum && j < 2; i++) {
332	    if (xfsread(ino, ep + j, sizeof(ep[0])))
333		return;
334	    if (ep[j].p_type == PT_LOAD)
335		j++;
336	}
337	for (i = 0; i < 2; i++) {
338	    p = PTOV(ep[i].p_paddr & 0xffffff);
339	    fs_off = ep[i].p_offset;
340	    if (xfsread(ino, p, ep[i].p_filesz))
341		return;
342	}
343	p += roundup2(ep[1].p_memsz, PAGE_SIZE);
344	bootinfo.bi_symtab = VTOP(p);
345	if (hdr.eh.e_shnum == hdr.eh.e_shstrndx + 3) {
346	    fs_off = hdr.eh.e_shoff + sizeof(es[0]) *
347		(hdr.eh.e_shstrndx + 1);
348	    if (xfsread(ino, &es, sizeof(es)))
349		return;
350	    for (i = 0; i < 2; i++) {
351		memcpy(p, (char *)&es[i].sh_size, sizeof(es[i].sh_size));
352		p += sizeof(es[i].sh_size);
353		fs_off = es[i].sh_offset;
354		if (xfsread(ino, p, es[i].sh_size))
355		    return;
356		p += es[i].sh_size;
357	    }
358	}
359	addr = hdr.eh.e_entry & 0xffffff;
360    }
361    bootinfo.bi_esymtab = VTOP(p);
362    bootinfo.bi_kernelname = VTOP(fname);
363    bootinfo.bi_bios_dev = dsk.drive;
364    __exec((caddr_t)addr, opts & RBX_MASK,
365	   MAKEBOOTDEV(dev_maj[dsk.type], 0, dsk.slice, dsk.unit, dsk.part),
366	   0, 0, 0, VTOP(&bootinfo));
367}
368
369static int
370parse(char *arg)
371{
372    char *p, *q;
373    int drv, c, i;
374
375    while ((c = *arg++)) {
376	if (c == ' ' || c == '\t' || c == '\n')
377	    continue;
378	for (p = arg; *p && *p != '\n' && *p != ' ' && *p != '\t'; p++);
379	if (*p)
380	    *p++ = 0;
381	if (c == '-') {
382	    while ((c = *arg++)) {
383		for (i = 0; c != optstr[i]; i++)
384		    if (i == NOPT - 1)
385			return -1;
386		opts ^= 1 << flags[i];
387	    }
388	    if (opts & 1 << RBX_PROBEKBD) {
389		i = *(uint8_t *)PTOV(0x496) & 0x10;
390		/* printf("Keyboard: %s\n", i ? "yes" : "no"); */
391		if (!i)
392		    opts |= 1 << RBX_DUAL | 1 << RBX_SERIAL;
393		opts &= ~(1 << RBX_PROBEKBD);
394	    }
395	    ioctrl = opts & 1 << RBX_DUAL ? (IO_SERIAL|IO_KEYBOARD) :
396		     opts & 1 << RBX_SERIAL ? IO_SERIAL : IO_KEYBOARD;
397	    if (ioctrl & IO_SERIAL)
398	        sio_init();
399	} else {
400	    for (q = arg--; *q && *q != '('; q++);
401	    if (*q) {
402		drv = -1;
403		if (arg[1] == ':') {
404		    if (*arg < '0' || *arg > '9')
405			return -1;
406		    drv = *arg - '0';
407		    arg += 2;
408		}
409		if (q - arg != 2)
410		    return -1;
411		for (i = 0; arg[0] != dev_nm[i][0] ||
412			    arg[1] != dev_nm[i][1]; i++)
413		    if (i == NDEV - 1)
414			return -1;
415		dsk.type = i;
416		arg += 3;
417		if (arg[1] != ',' || *arg < '0' || *arg > '9')
418		    return -1;
419		dsk.unit = *arg - '0';
420		arg += 2;
421		dsk.slice = WHOLE_DISK_SLICE;
422		if (arg[1] == ',') {
423		    if (*arg < '0' || *arg > '0' + NDOSPART)
424			return -1;
425		    if ((dsk.slice = *arg - '0'))
426			dsk.slice++;
427		    arg += 2;
428		}
429		if (arg[1] != ')' || *arg < 'a' || *arg > 'p')
430		    return -1;
431		dsk.part = *arg - 'a';
432		arg += 2;
433		if (drv == -1)
434		    drv = dsk.unit;
435		dsk.drive = (dsk.type <= TYPE_MAXHARD
436			     ? DRV_HARD : 0) + drv;
437		dsk_meta = 0;
438		fsread(0, NULL, 0);
439	    }
440	    if ((i = p - arg - !*(p - 1))) {
441		if (i >= sizeof(kname))
442		    return -1;
443		memcpy(kname, arg, i + 1);
444	    }
445	}
446	arg = p;
447    }
448    return 0;
449}
450
451static int
452dskread(void *buf, unsigned lba, unsigned nblk)
453{
454    struct dos_partition *dp;
455    struct disklabel *d;
456    char *sec;
457    unsigned sl, i;
458
459    if (!dsk_meta) {
460	sec = dmadat->secbuf;
461	dsk.start = 0;
462	if (drvread(sec, DOSBBSECTOR, 1))
463	    return -1;
464	dp = (void *)(sec + DOSPARTOFF);
465	sl = dsk.slice;
466	if (sl < BASE_SLICE) {
467	    for (i = 0; i < NDOSPART; i++)
468		if (dp[i].dp_typ == DOSPTYP_386BSD &&
469		    (dp[i].dp_flag & 0x80 || sl < BASE_SLICE)) {
470		    sl = BASE_SLICE + i;
471		    if (dp[i].dp_flag & 0x80 ||
472			dsk.slice == COMPATIBILITY_SLICE)
473			break;
474		}
475	    if (dsk.slice == WHOLE_DISK_SLICE)
476		dsk.slice = sl;
477	}
478	if (sl != WHOLE_DISK_SLICE) {
479	    if (sl != COMPATIBILITY_SLICE)
480		dp += sl - BASE_SLICE;
481	    if (dp->dp_typ != DOSPTYP_386BSD) {
482		printf("Invalid %s\n", "slice");
483		return -1;
484	    }
485	    dsk.start = dp->dp_start;
486	}
487	if (drvread(sec, dsk.start + LABELSECTOR, 1))
488		return -1;
489	d = (void *)(sec + LABELOFFSET);
490	if (d->d_magic != DISKMAGIC || d->d_magic2 != DISKMAGIC) {
491	    if (dsk.part != RAW_PART) {
492		printf("Invalid %s\n", "label");
493		return -1;
494	    }
495	} else {
496	    if (!dsk.init) {
497		if (d->d_type == DTYPE_SCSI)
498		    dsk.type = TYPE_DA;
499		dsk.init++;
500	    }
501	    if (dsk.part >= d->d_npartitions ||
502		!d->d_partitions[dsk.part].p_size) {
503		printf("Invalid %s\n", "partition");
504		return -1;
505	    }
506	    dsk.start += d->d_partitions[dsk.part].p_offset;
507	    dsk.start -= d->d_partitions[RAW_PART].p_offset;
508	}
509    }
510    return drvread(buf, dsk.start + lba, nblk);
511}
512
513static void
514printf(const char *fmt,...)
515{
516    static const char digits[16] = "0123456789abcdef";
517    va_list ap;
518    char buf[10];
519    char *s;
520    unsigned r, u;
521    int c;
522
523    va_start(ap, fmt);
524    while ((c = *fmt++)) {
525	if (c == '%') {
526	    c = *fmt++;
527	    switch (c) {
528	    case 'c':
529		putchar(va_arg(ap, int));
530		continue;
531	    case 's':
532		for (s = va_arg(ap, char *); *s; s++)
533		    putchar(*s);
534		continue;
535	    case 'u':
536	    case 'x':
537		r = c == 'u' ? 10U : 16U;
538		u = va_arg(ap, unsigned);
539		s = buf;
540		do
541		    *s++ = digits[u % r];
542		while (u /= r);
543		while (--s >= buf)
544		    putchar(*s);
545		continue;
546	    }
547	}
548	putchar(c);
549    }
550    va_end(ap);
551    return;
552}
553
554static void
555putchar(int c)
556{
557    if (c == '\n')
558	xputc('\r');
559    xputc(c);
560}
561
562static uint32_t
563memsize(int type)
564{
565    v86.addr = type;
566    v86.eax = 0x8800;
567    v86int();
568    return v86.eax;
569}
570
571static int
572drvread(void *buf, unsigned lba, unsigned nblk)
573{
574    static unsigned c = 0x2d5c7c2f;
575
576    printf("%c\b", c = c << 8 | c >> 24);
577    v86.ctl = V86_ADDR | V86_CALLF | V86_FLAGS;
578    v86.addr = XREADORG;		/* call to xread in boot1 */
579    v86.es = VTOPSEG(buf);
580    v86.eax = lba;
581    v86.ebx = VTOPOFF(buf);
582    v86.ecx = lba >> 16;
583    v86.edx = nblk << 8 | dsk.drive;
584    v86int();
585    v86.ctl = V86_FLAGS;
586    if (V86_CY(v86.efl)) {
587	printf("Disk error 0x%x lba 0x%x\n", v86.eax >> 8 & 0xff, lba);
588	return -1;
589    }
590    return 0;
591}
592
593static int
594keyhit(unsigned ticks)
595{
596    uint32_t t0, t1;
597
598    if (opts & 1 << RBX_NOINTR)
599	return 0;
600    t0 = 0;
601    for (;;) {
602	if (xgetc(1))
603	    return 1;
604	t1 = *(uint32_t *)PTOV(0x46c);
605	if (!t0)
606	    t0 = t1;
607	if (t1 < t0 || t1 >= t0 + ticks)
608	    return 0;
609    }
610}
611
612static int
613xputc(int c)
614{
615    if (ioctrl & IO_KEYBOARD)
616	putc(c);
617    if (ioctrl & IO_SERIAL)
618	sio_putc(c);
619    return c;
620}
621
622static int
623xgetc(int fn)
624{
625    if (opts & 1 << RBX_NOINTR)
626	return 0;
627    for (;;) {
628	if (ioctrl & IO_KEYBOARD && getc(1))
629	    return fn ? 1 : getc(0);
630	if (ioctrl & IO_SERIAL && sio_ischar())
631	    return fn ? 1 : sio_getc();
632	if (fn)
633	    return 0;
634    }
635}
636
637static int
638getc(int fn)
639{
640    v86.addr = 0x16;
641    v86.eax = fn << 8;
642    v86int();
643    return fn == 0 ? v86.eax & 0xff : !V86_ZR(v86.efl);
644}
645