gptboot.c revision 40307
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/*
17 *	$Id: boot2.c,v 1.1.1.1 1998/10/12 21:16:26 rnordier Exp $
18 */
19
20#include <sys/param.h>
21#include <sys/reboot.h>
22#include <sys/diskslice.h>
23#include <sys/disklabel.h>
24#include <sys/dirent.h>
25#include <machine/bootinfo.h>
26
27#include <ufs/ffs/fs.h>
28#include <ufs/ufs/dinode.h>
29
30#include <stdarg.h>
31
32#include <a.out.h>
33#include <elf.h>
34
35#include <btxv86.h>
36
37#define RBX_ASKNAME	0x0	/* -a */
38#define RBX_SINGLE	0x1	/* -s */
39#define RBX_DFLTROOT	0x5	/* -r */
40#define RBX_KDB 	0x6	/* -d */
41#define RBX_CONFIG	0xa	/* -c */
42#define RBX_VERBOSE	0xb	/* -v */
43#define RBX_CDROM	0xd	/* -C */
44#define RBX_GDB 	0xf	/* -g */
45
46#define PATH_CONFIG	"/boot.config"
47#define PATH_BOOT3	"/boot/loader"
48#define PATH_KERNEL	"/kernel"
49#define PATH_HELP	"boot.help"
50
51#define ARGS		0x800
52#define NOPT		8
53#define BSIZEMAX	8192
54#define NDEV		3
55#define MEM_BASE	0x12
56#define MEM_EXT 	0x15
57#define V86_CY(x)	((x) & 1)
58#define V86_ZR(x)	((x) & 0x40)
59
60#define DRV_HARD	0x80
61#define DRV_MASK	0x7f
62
63#define MAJ_WD		0
64#define MAJ_WFD		1
65#define MAJ_FD		2
66#define MAJ_DA		4
67
68extern uint32_t _end;
69
70static const char optstr[NOPT] = "aCcdgrsv";
71static const unsigned char flags[NOPT] = {
72    RBX_ASKNAME,
73    RBX_CDROM,
74    RBX_CONFIG,
75    RBX_KDB,
76    RBX_GDB,
77    RBX_DFLTROOT,
78    RBX_SINGLE,
79    RBX_VERBOSE
80};
81
82static const char *const dev_nm[] = {"wd", "  ", "fd", "  ", "da"};
83
84static struct dsk {
85    unsigned drive;
86    unsigned type;
87    unsigned unit;
88    unsigned slice;
89    unsigned part;
90    unsigned start;
91    int init;
92    int meta;
93} dsk;
94static char cmd[512];
95static char kname[1024];
96static char help[2048];
97static uint32_t opts;
98static struct bootinfo bootinfo;
99static int ls;
100static uint32_t fs_off;
101
102void exit(int);
103static void load(const char *);
104static int parse(char *);
105static void readfile(const char *, void *, size_t);
106static ino_t lookup(const char *);
107static int fsfind(const char *, ino_t *);
108static ssize_t fsread(ino_t, void *, size_t);
109static int dskread(void *, unsigned, unsigned);
110static int printf(const char *,...);
111static void getstr(char *, int);
112static int putchar(int);
113static int getchar(void);
114static void *memcpy(void *, const void *, size_t);
115static int strcmp(const char *, const char *);
116static void *malloc(size_t);
117static uint32_t memsize(int);
118static uint32_t drvinfo(int);
119static int drvread(void *, unsigned, unsigned);
120static int keyhit(unsigned);
121static int putch(int);
122static int getch(void);
123
124int
125main(void)
126{
127    int autoboot, helpon, i;
128
129    dsk.drive = *(uint8_t *)PTOV(ARGS);
130    dsk.type = dsk.drive & DRV_HARD ? MAJ_WD : MAJ_FD;
131    dsk.unit = dsk.drive & DRV_MASK;
132    dsk.slice = *(uint8_t *)PTOV(ARGS + 1) + 1;
133    bootinfo.bi_version = BOOTINFO_VERSION;
134    bootinfo.bi_size = sizeof(bootinfo);
135    bootinfo.bi_basemem = memsize(MEM_BASE);
136    bootinfo.bi_extmem = memsize(MEM_EXT);
137    bootinfo.bi_memsizes_valid++;
138    for (i = 0; i < N_BIOS_GEOM; i++)
139	bootinfo.bi_bios_geom[i] = drvinfo(i);
140    autoboot = 2;
141    helpon = 1;
142    readfile(PATH_CONFIG, cmd, sizeof(cmd));
143    if (parse(cmd))
144	autoboot = 0;
145    else if (!*kname) {
146	if (autoboot == 2) {
147	    memcpy(kname, PATH_BOOT3, sizeof(PATH_BOOT3));
148	    if (!keyhit(0x37)) {
149		load(kname);
150		autoboot = 1;
151	    }
152	}
153	if (autoboot == 1)
154	    memcpy(kname, PATH_KERNEL, sizeof(PATH_KERNEL));
155    }
156    readfile(PATH_HELP, help, sizeof(help));
157    for (;;) {
158	printf(" \n>> FreeBSD/i386 BOOT\n"
159	       "Default: %u:%s(%u,%c)%s\n"
160	       "%s"
161	       "boot: ",
162	       dsk.drive & DRV_MASK, dev_nm[dsk.type], dsk.unit,
163	       'a' + dsk.part, kname, helpon ? help : "");
164	if (!autoboot || keyhit(0x5a))
165	    getstr(cmd, sizeof(cmd));
166	autoboot = helpon = 0;
167	if (parse(cmd))
168	    helpon = 1;
169	else
170	    load(kname);
171    }
172}
173
174void
175exit(int x)
176{
177}
178
179static void
180load(const char *fname)
181{
182    union {
183	struct exec ex;
184	Elf32_Ehdr eh;
185    } hdr;
186    Elf32_Phdr ep[2];
187    Elf32_Shdr es[2];
188    caddr_t p;
189    ino_t ino;
190    uint32_t addr, x;
191    int fmt, i, j;
192
193    if (!(ino = lookup(fname)) && !ls) {
194	printf("No `%s'\n", fname);
195	return;
196    }
197    if (fsread(ino, &hdr, sizeof(hdr)) != sizeof(hdr))
198	return;
199    if (N_GETMAGIC(hdr.ex) == ZMAGIC)
200	fmt = 0;
201    else if (IS_ELF(hdr.eh))
202	fmt = 1;
203    else {
204	printf("Invalid %s\n", "format");
205	return;
206    }
207    if (fmt == 0) {
208	addr = hdr.ex.a_entry & 0xffffff;
209	p = PTOV(addr);
210	printf("%s=0x%x ", "text", (unsigned)hdr.ex.a_text);
211	fs_off = PAGE_SIZE;
212	if (fsread(ino, p, hdr.ex.a_text) != hdr.ex.a_text)
213	    return;
214	p += roundup2(hdr.ex.a_text, PAGE_SIZE);
215	printf("%s=0x%x ", "data", (unsigned)hdr.ex.a_data);
216	if (fsread(ino, p, hdr.ex.a_data) != hdr.ex.a_data)
217	    return;
218	p += hdr.ex.a_data;
219	printf("%s=0x%x ", "bss", (unsigned)hdr.ex.a_bss);
220	p += roundup2(hdr.ex.a_bss, PAGE_SIZE);
221	bootinfo.bi_symtab = VTOP(p);
222	memcpy(p, &hdr.ex.a_syms, sizeof(hdr.ex.a_syms));
223	p += sizeof(hdr.ex.a_syms);
224	printf("symbols=[");
225	printf("+0x%x", (unsigned)hdr.ex.a_syms);
226	if (hdr.ex.a_syms) {
227	    if (fsread(ino, p, hdr.ex.a_syms) != hdr.ex.a_syms)
228		return;
229	    p += hdr.ex.a_syms;
230	    if (fsread(ino, p, sizeof(int)) != sizeof(int))
231		return;
232	    x = *(uint32_t *)p;
233	    p += sizeof(int);
234	    x -= sizeof(int);
235	    printf("+0x%x", x);
236	    if (fsread(ino, p, x) != x)
237		return;
238	    p += x;
239	}
240    } else {
241	fs_off = hdr.eh.e_phoff;
242	for (j = i = 0; i < hdr.eh.e_phoff && j < 2; i++) {
243	    if (fsread(ino, ep + j, sizeof(ep[0])) != sizeof(ep[0]))
244		return;
245	    if (ep[j].p_type == PT_LOAD)
246		j++;
247	}
248	for (i = 0; i < 2; i++) {
249	    p = PTOV(ep[i].p_paddr & 0xffffff);
250	    printf("%s=0x%x ", !i ? "text" : "data", ep[i].p_filesz);
251	    fs_off = ep[i].p_offset;
252	    if (fsread(ino, p, ep[i].p_filesz) != ep[i].p_filesz)
253		return;
254	}
255	printf("%s=0x%x ", "bss", ep[1].p_memsz - ep[1].p_filesz);
256	p += roundup2(ep[1].p_memsz, PAGE_SIZE);
257	bootinfo.bi_symtab = VTOP(p);
258	printf("symbols=[");
259	if (hdr.eh.e_shnum == hdr.eh.e_shstrndx + 3) {
260	    fs_off = hdr.eh.e_shoff + sizeof(es[0]) *
261		(hdr.eh.e_shstrndx + 1);
262	    if (fsread(ino, &es, sizeof(es)) != sizeof(es))
263		return;
264	    for (i = 0; i < 2; i++) {
265		memcpy(p, &es[i].sh_size, sizeof(es[i].sh_size));
266		p += sizeof(es[i].sh_size);
267		printf("+0x%x", es[i].sh_size);
268		fs_off = es[i].sh_offset;
269		if (fsread(ino, p, es[i].sh_size) != es[i].sh_size)
270		    return;
271		p += es[i].sh_size;
272	    }
273	}
274	addr = hdr.eh.e_entry & 0xffffff;
275    }
276    bootinfo.bi_esymtab = VTOP(p);
277    printf("]\nentry=0x%x\n", addr);
278    bootinfo.bi_kernelname = VTOP(fname);
279    __exec((caddr_t)addr, RB_BOOTINFO | opts,
280	   MAKEBOOTDEV(dsk.type, 0, dsk.slice, dsk.unit, dsk.part),
281	   0, 0, 0, VTOP(&bootinfo));
282}
283
284static int
285parse(char *arg)
286{
287    char *p, *q;
288    int drv, c, i;
289
290    while ((c = *arg++)) {
291	if (c == ' ')
292	    continue;
293	for (p = arg; *p && *p != '\n' && *p != ' '; p++);
294	if (*p)
295	    *p++ = 0;
296	if (c == '-')
297	    while ((c = *arg++)) {
298		for (i = 0; c != optstr[i]; i++)
299		    if (i == NOPT - 1)
300			return -1;
301		opts |= 1 << flags[i];
302	    }
303	else {
304	    for (q = arg--; *q && *q != '('; q++);
305	    if (*q) {
306		drv = -1;
307		if (arg[1] == ':') {
308		    if (*arg < '0' || *arg > '9')
309			return -1;
310		    drv = *arg - '0';
311		    arg += 2;
312		}
313		if (q - arg != 2)
314		    return -1;
315		for (i = 0; arg[0] != dev_nm[i][0] ||
316		     arg[1] != dev_nm[i][1]; i++)
317		    if (i == NDEV - 1)
318			return -1;
319		dsk.type = i;
320		arg += 3;
321		if (arg[1] != ',' || *arg < '0' || *arg > '9')
322		    return -1;
323		dsk.unit = *arg - '0';
324		arg += 2;
325		if (arg[1] == ',') {
326		    if (*arg < '0' || *arg > '4')
327			return -1;
328		    if ((dsk.slice = *arg - '0'))
329			dsk.slice++;
330		    arg += 2;
331		}
332		if (arg[1] != ')' || *arg < 'a' || *arg > 'p')
333		    return -1;
334		dsk.part = *arg - 'a';
335		arg += 2;
336		if (drv == -1)
337		    drv = dsk.unit;
338		dsk.drive = (dsk.type == MAJ_WD ||
339			     dsk.type == MAJ_DA ? DRV_HARD : 0) + drv;
340		dsk.meta = 0;
341		fsread(0, NULL, 0);
342	    }
343	    if ((i = p - arg - !*(p - 1))) {
344		if (i >= sizeof(kname))
345		    return -1;
346		memcpy(kname, arg, i + 1);
347	    }
348	}
349	arg = p;
350    }
351    return 0;
352}
353
354static void
355readfile(const char *fname, void *buf, size_t size)
356{
357    ino_t ino;
358
359    if ((ino = lookup(fname)))
360	fsread(ino, buf, size);
361}
362
363static ino_t
364lookup(const char *path)
365{
366    char name[MAXNAMLEN + 1];
367    const char *s;
368    ino_t ino;
369    ssize_t n;
370    int dt;
371
372    ino = ROOTINO;
373    dt = DT_DIR;
374    for (;;) {
375	if (*path == '/')
376	    path++;
377	if (!*path)
378	    break;
379	for (s = path; *s && *s != '/'; s++);
380	if ((n = s - path) > MAXNAMLEN)
381	    return 0;
382	ls = *path == '?' && n == 1 && !*s;
383	memcpy(name, path, n);
384	name[n] = 0;
385	if ((dt = fsfind(name, &ino)) <= 0)
386	    break;
387	path = s;
388    }
389    return dt == DT_REG ? ino : 0;
390}
391
392static int
393fsfind(const char *name, ino_t * ino)
394{
395    char buf[DEV_BSIZE];
396    struct dirent *d;
397    char *s;
398    ssize_t n;
399
400    fs_off = 0;
401    while ((n = fsread(*ino, buf, DEV_BSIZE)) > 0)
402	for (s = buf; s < buf + DEV_BSIZE;) {
403	    d = (void *)s;
404	    if (ls)
405		printf("%s ", d->d_name);
406	    else if (!strcmp(name, d->d_name)) {
407		*ino = d->d_fileno;
408		return d->d_type;
409	    }
410	    s += d->d_reclen;
411	}
412    if (n != -1 && ls)
413	putchar('\n');
414    return 0;
415}
416
417static ssize_t
418fsread(ino_t inode, void *buf, size_t nbyte)
419{
420    static struct fs fs;
421    static struct dinode din;
422    static char *blkbuf;
423    static ufs_daddr_t *indbuf;
424    static ino_t inomap;
425    static ufs_daddr_t blkmap, indmap;
426    static unsigned fsblks;
427    char *s;
428    ufs_daddr_t lbn, addr;
429    size_t n, nb, off;
430
431    if (!dsk.meta) {
432	if (!blkbuf)
433	    blkbuf = malloc(BSIZEMAX);
434	inomap = 0;
435	if (dskread(blkbuf, SBOFF / DEV_BSIZE, SBSIZE / DEV_BSIZE))
436	    return -1;
437	memcpy(&fs, blkbuf, sizeof(fs));
438	if (fs.fs_magic != FS_MAGIC) {
439	    printf("Not ufs\n");
440	    return -1;
441	}
442	fsblks = fs.fs_bsize >> DEV_BSHIFT;
443	dsk.meta = 1;
444    }
445    if (!inode)
446	return 0;
447    if (inomap != inode) {
448	if (dskread(blkbuf, fsbtodb(&fs, ino_to_fsba(&fs, inode)),
449		    fsblks))
450	    return -1;
451	din = ((struct dinode *)blkbuf)[inode % INOPB(&fs)];
452	inomap = inode;
453	fs_off = 0;
454	blkmap = indmap = 0;
455    }
456    s = buf;
457    if (nbyte > (n = din.di_size - fs_off))
458	nbyte = n;
459    nb = nbyte;
460    while (nb) {
461	lbn = lblkno(&fs, fs_off);
462	if (lbn < NDADDR)
463	    addr = din.di_db[lbn];
464	else {
465	    if (indmap != din.di_ib[0]) {
466		if (!indbuf)
467		    indbuf = malloc(BSIZEMAX);
468		if (dskread(indbuf, fsbtodb(&fs, din.di_ib[0]),
469			    fsblks))
470		    return -1;
471		indmap = din.di_ib[0];
472	    }
473	    addr = indbuf[(lbn - NDADDR) % NINDIR(&fs)];
474	}
475	n = dblksize(&fs, &din, lbn);
476	if (blkmap != addr) {
477	    if (dskread(blkbuf, fsbtodb(&fs, addr), n >> DEV_BSHIFT))
478		return -1;
479	    blkmap = addr;
480	}
481	off = blkoff(&fs, fs_off);
482	n -= off;
483	if (n > nb)
484	    n = nb;
485	memcpy(s, blkbuf + off, n);
486	s += n;
487	fs_off += n;
488	nb -= n;
489    }
490    return nbyte;
491}
492
493static int
494dskread(void *buf, unsigned lba, unsigned nblk)
495{
496    static char *sec;
497    struct dos_partition *dp;
498    struct disklabel *d;
499    unsigned sl, i;
500
501    if (!dsk.meta) {
502	if (!sec)
503	    sec = malloc(DEV_BSIZE);
504	dsk.start = 0;
505	sl = dsk.slice;
506	if (sl != WHOLE_DISK_SLICE) {
507	    if (drvread(sec, DOSBBSECTOR, 1))
508		return -1;
509	    dp = (void *)(sec + DOSPARTOFF);
510	    if (sl == COMPATIBILITY_SLICE)
511		for (i = 0; i < NDOSPART; i++)
512		    if (dp[i].dp_typ == DOSPTYP_386BSD &&
513			(dp[i].dp_flag & 0x80 ||
514			 sl == COMPATIBILITY_SLICE))
515			sl = BASE_SLICE + i;
516	    if (sl != COMPATIBILITY_SLICE)
517		dp += sl - BASE_SLICE;
518	    if (dp->dp_typ != DOSPTYP_386BSD) {
519		printf("Invalid %s\n", "slice");
520		return -1;
521	    }
522	    dsk.start = dp->dp_start;
523	}
524        if (drvread(sec, dsk.start + LABELSECTOR, 1))
525		return -1;
526	d = (void *)(sec + LABELOFFSET);
527	if (d->d_magic != DISKMAGIC || d->d_magic2 != DISKMAGIC) {
528	    if (dsk.part != RAW_PART) {
529	        printf("Invalid %s\n", "label");
530	        return -1;
531	    }
532	} else {
533	    if (!dsk.init) {
534	        if (d->d_type == DTYPE_SCSI)
535		    dsk.type = MAJ_DA;
536		dsk.init++;
537	    }
538	    if (dsk.part >= d->d_npartitions) {
539		printf("Invalid %s\n", "partition");
540		return -1;
541	    }
542	    dsk.start = d->d_partitions[dsk.part].p_offset;
543	}
544    }
545    return drvread(buf, dsk.start + lba, nblk);
546}
547
548static int
549printf(const char *fmt,...)
550{
551    static const char digits[16] = "0123456789abcdef";
552    va_list ap;
553    char buf[10];
554    char *s;
555    unsigned r, u;
556    int c;
557
558    va_start(ap, fmt);
559    while ((c = *fmt++)) {
560	if (c == '%') {
561	    c = *fmt++;
562	    switch (c) {
563	    case 'c':
564		putchar(va_arg(ap, int));
565		continue;
566	    case 's':
567		for (s = va_arg(ap, char *); *s; s++)
568		    putchar(*s);
569		continue;
570	    case 'u':
571	    case 'x':
572		r = c == 'u' ? 10U : 16U;
573		u = va_arg(ap, unsigned);
574		s = buf;
575		do
576		    *s++ = digits[u % r];
577		while (u /= r);
578		while (--s >= buf)
579		    putchar(*s);
580		continue;
581	    }
582	}
583	putchar(c);
584    }
585    va_end(ap);
586    return 0;
587}
588
589static void
590getstr(char *str, int size)
591{
592    char *s;
593    int c;
594
595    s = str;
596    do {
597	switch (c = getchar()) {
598	case '\b':
599	    if (s > str)
600		s--;
601	    break;
602	case '\n':
603	    *s = 0;
604	    break;
605	default:
606	    if (s - str < size - 1)
607		*s++ = c;
608	}
609	putchar(c);
610    } while (c != '\n');
611}
612
613static int
614putchar(int c)
615{
616    if (c == '\n')
617	putch('\r');
618    return putch(c);
619}
620
621static int
622getchar(void)
623{
624    int c;
625
626    c = getch();
627    if (c == '\r')
628	c = '\n';
629    return c;
630}
631
632static void *
633memcpy(void *dst, const void *src, size_t size)
634{
635    const char *s;
636    char *d;
637
638    for (d = dst, s = src; size; size--)
639	*d++ = *s++;
640    return dst;
641}
642
643static int
644strcmp(const char *s1, const char *s2)
645{
646    for (; *s1 == *s2 && *s1; s1++, s2++);
647    return (u_char)*s1 - (u_char)*s2;
648}
649
650static void *
651malloc(size_t size)
652{
653    static uint32_t next;
654    void *p;
655
656    if (!next)
657	next = roundup2(__base + _end, 0x10000) - __base;
658    p = (void *)next;
659    next += size;
660    return p;
661}
662
663static uint32_t
664memsize(int type)
665{
666    v86.ctl = V86_FLAGS;
667    v86.addr = type;
668    v86.eax = 0x8800;
669    v86int();
670    return v86.eax;
671}
672
673static uint32_t
674drvinfo(int drive)
675{
676    v86.addr = 0x13;
677    v86.eax = 0x800;
678    v86.edx = DRV_HARD + drive;
679    v86int();
680    if (V86_CY(v86.efl))
681	return 0x4f010f;
682    return ((v86.ecx & 0xc0) << 18) | ((v86.ecx & 0xff00) << 8) |
683	   (v86.edx & 0xff00) | (v86.ecx & 0x3f);
684}
685
686static int
687drvread(void *buf, unsigned lba, unsigned nblk)
688{
689    v86.ctl = V86_ADDR | V86_CALLF | V86_FLAGS;
690    v86.addr = 0x604;
691    v86.eax = nblk;
692    v86.ebx = VTOPSEG(buf) << 16 | VTOPOFF(buf);
693    v86.ecx = lba;
694    v86.edx = dsk.drive;
695    v86int();
696    v86.ctl = V86_FLAGS;
697    if (V86_CY(v86.efl)) {
698	printf("Disk error 0x%x (lba=0x%x)\n", v86.eax >> 8 & 0xff,
699	       lba);
700	return -1;
701    }
702    return 0;
703}
704
705static int
706keyhit(unsigned ticks)
707{
708    uint32_t x;
709
710    x = 0;
711    for (;;) {
712	v86.addr = 0x16;
713	v86.eax = 0x100;
714	v86int();
715	if (!V86_ZR(v86.efl))
716	    return 1;
717	v86.addr = 0x1a;
718	v86.eax = 0;
719	v86.edx = 0;
720	v86int();
721	if (!x)
722	    x = v86.edx;
723	else if (v86.edx < x || v86.edx > x + ticks)
724	    return 0;
725    }
726}
727
728static int
729putch(int c)
730{
731    v86.addr = 0x10;
732    v86.eax = 0xe00 | (c & 0xff);
733    v86.ebx = 0x7;
734    v86int();
735    return c;
736}
737
738static int
739getch(void)
740{
741    v86.addr = 0x16;
742    v86.eax = 0;
743    v86int();
744    return v86.eax & 0xff;
745}
746