gptboot.c revision 40320
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.3 1998/10/13 21:35:42 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		5
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		dsk.slice = WHOLE_DISK_SLICE;
326		if (arg[1] == ',') {
327		    if (*arg < '0' || *arg > '0' + NDOSPART)
328			return -1;
329		    if ((dsk.slice = *arg - '0'))
330			dsk.slice++;
331		    arg += 2;
332		}
333		if (arg[1] != ')' || *arg < 'a' || *arg > 'p')
334		    return -1;
335		dsk.part = *arg - 'a';
336		arg += 2;
337		if (drv == -1)
338		    drv = dsk.unit;
339		dsk.drive = (dsk.type == MAJ_WD ||
340			     dsk.type == MAJ_DA ? DRV_HARD : 0) + drv;
341		dsk.meta = 0;
342		fsread(0, NULL, 0);
343	    }
344	    if ((i = p - arg - !*(p - 1))) {
345		if (i >= sizeof(kname))
346		    return -1;
347		memcpy(kname, arg, i + 1);
348	    }
349	}
350	arg = p;
351    }
352    return 0;
353}
354
355static void
356readfile(const char *fname, void *buf, size_t size)
357{
358    ino_t ino;
359
360    if ((ino = lookup(fname)))
361	fsread(ino, buf, size);
362}
363
364static ino_t
365lookup(const char *path)
366{
367    char name[MAXNAMLEN + 1];
368    const char *s;
369    ino_t ino;
370    ssize_t n;
371    int dt;
372
373    ino = ROOTINO;
374    dt = DT_DIR;
375    for (;;) {
376	if (*path == '/')
377	    path++;
378	if (!*path)
379	    break;
380	for (s = path; *s && *s != '/'; s++);
381	if ((n = s - path) > MAXNAMLEN)
382	    return 0;
383	ls = *path == '?' && n == 1 && !*s;
384	memcpy(name, path, n);
385	name[n] = 0;
386	if ((dt = fsfind(name, &ino)) <= 0)
387	    break;
388	path = s;
389    }
390    return dt == DT_REG ? ino : 0;
391}
392
393static int
394fsfind(const char *name, ino_t * ino)
395{
396    char buf[DEV_BSIZE];
397    struct dirent *d;
398    char *s;
399    ssize_t n;
400
401    fs_off = 0;
402    while ((n = fsread(*ino, buf, DEV_BSIZE)) > 0)
403	for (s = buf; s < buf + DEV_BSIZE;) {
404	    d = (void *)s;
405	    if (ls)
406		printf("%s ", d->d_name);
407	    else if (!strcmp(name, d->d_name)) {
408		*ino = d->d_fileno;
409		return d->d_type;
410	    }
411	    s += d->d_reclen;
412	}
413    if (n != -1 && ls)
414	putchar('\n');
415    return 0;
416}
417
418static ssize_t
419fsread(ino_t inode, void *buf, size_t nbyte)
420{
421    static struct fs fs;
422    static struct dinode din;
423    static char *blkbuf;
424    static ufs_daddr_t *indbuf;
425    static ino_t inomap;
426    static ufs_daddr_t blkmap, indmap;
427    static unsigned fsblks;
428    char *s;
429    ufs_daddr_t lbn, addr;
430    size_t n, nb, off;
431
432    if (!dsk.meta) {
433	if (!blkbuf)
434	    blkbuf = malloc(BSIZEMAX);
435	inomap = 0;
436	if (dskread(blkbuf, SBOFF / DEV_BSIZE, SBSIZE / DEV_BSIZE))
437	    return -1;
438	memcpy(&fs, blkbuf, sizeof(fs));
439	if (fs.fs_magic != FS_MAGIC) {
440	    printf("Not ufs\n");
441	    return -1;
442	}
443	fsblks = fs.fs_bsize >> DEV_BSHIFT;
444	dsk.meta = 1;
445    }
446    if (!inode)
447	return 0;
448    if (inomap != inode) {
449	if (dskread(blkbuf, fsbtodb(&fs, ino_to_fsba(&fs, inode)),
450		    fsblks))
451	    return -1;
452	din = ((struct dinode *)blkbuf)[inode % INOPB(&fs)];
453	inomap = inode;
454	fs_off = 0;
455	blkmap = indmap = 0;
456    }
457    s = buf;
458    if (nbyte > (n = din.di_size - fs_off))
459	nbyte = n;
460    nb = nbyte;
461    while (nb) {
462	lbn = lblkno(&fs, fs_off);
463	if (lbn < NDADDR)
464	    addr = din.di_db[lbn];
465	else {
466	    if (indmap != din.di_ib[0]) {
467		if (!indbuf)
468		    indbuf = malloc(BSIZEMAX);
469		if (dskread(indbuf, fsbtodb(&fs, din.di_ib[0]),
470			    fsblks))
471		    return -1;
472		indmap = din.di_ib[0];
473	    }
474	    addr = indbuf[(lbn - NDADDR) % NINDIR(&fs)];
475	}
476	n = dblksize(&fs, &din, lbn);
477	if (blkmap != addr) {
478	    if (dskread(blkbuf, fsbtodb(&fs, addr), n >> DEV_BSHIFT))
479		return -1;
480	    blkmap = addr;
481	}
482	off = blkoff(&fs, fs_off);
483	n -= off;
484	if (n > nb)
485	    n = nb;
486	memcpy(s, blkbuf + off, n);
487	s += n;
488	fs_off += n;
489	nb -= n;
490    }
491    return nbyte;
492}
493
494static int
495dskread(void *buf, unsigned lba, unsigned nblk)
496{
497    static char *sec;
498    struct dos_partition *dp;
499    struct disklabel *d;
500    unsigned sl, i;
501
502    if (!dsk.meta) {
503	if (!sec)
504	    sec = malloc(DEV_BSIZE);
505	dsk.start = 0;
506	if (drvread(sec, DOSBBSECTOR, 1))
507	    return -1;
508	dp = (void *)(sec + DOSPARTOFF);
509	sl = dsk.slice;
510	if (sl < BASE_SLICE) {
511	    for (i = 0; i < NDOSPART; i++)
512		if (dp[i].dp_typ == DOSPTYP_386BSD &&
513		    (dp[i].dp_flag & 0x80 || sl < BASE_SLICE)) {
514		    sl = BASE_SLICE + i;
515		    if (dp[i].dp_flag & 0x80 ||
516			dsk.slice == COMPATIBILITY_SLICE)
517			break;
518		}
519	    if (dsk.slice == WHOLE_DISK_SLICE)
520		dsk.slice = sl;
521	}
522	if (sl != WHOLE_DISK_SLICE) {
523	    if (sl != COMPATIBILITY_SLICE)
524		dp += sl - BASE_SLICE;
525	    if (dp->dp_typ != DOSPTYP_386BSD) {
526		printf("Invalid %s\n", "slice");
527		return -1;
528	    }
529	    dsk.start = dp->dp_start;
530	}
531	if (drvread(sec, dsk.start + LABELSECTOR, 1))
532		return -1;
533	d = (void *)(sec + LABELOFFSET);
534	if (d->d_magic != DISKMAGIC || d->d_magic2 != DISKMAGIC) {
535	    if (dsk.part != RAW_PART) {
536		printf("Invalid %s\n", "label");
537		return -1;
538	    }
539	} else {
540	    if (!dsk.init) {
541		if (d->d_type == DTYPE_SCSI)
542		    dsk.type = MAJ_DA;
543		dsk.init++;
544	    }
545	    if (dsk.part >= d->d_npartitions) {
546		printf("Invalid %s\n", "partition");
547		return -1;
548	    }
549	    dsk.start = d->d_partitions[dsk.part].p_offset;
550	}
551    }
552    return drvread(buf, dsk.start + lba, nblk);
553}
554
555static int
556printf(const char *fmt,...)
557{
558    static const char digits[16] = "0123456789abcdef";
559    va_list ap;
560    char buf[10];
561    char *s;
562    unsigned r, u;
563    int c;
564
565    va_start(ap, fmt);
566    while ((c = *fmt++)) {
567	if (c == '%') {
568	    c = *fmt++;
569	    switch (c) {
570	    case 'c':
571		putchar(va_arg(ap, int));
572		continue;
573	    case 's':
574		for (s = va_arg(ap, char *); *s; s++)
575		    putchar(*s);
576		continue;
577	    case 'u':
578	    case 'x':
579		r = c == 'u' ? 10U : 16U;
580		u = va_arg(ap, unsigned);
581		s = buf;
582		do
583		    *s++ = digits[u % r];
584		while (u /= r);
585		while (--s >= buf)
586		    putchar(*s);
587		continue;
588	    }
589	}
590	putchar(c);
591    }
592    va_end(ap);
593    return 0;
594}
595
596static void
597getstr(char *str, int size)
598{
599    char *s;
600    int c;
601
602    s = str;
603    do {
604	switch (c = getchar()) {
605	case '\b':
606	    if (s > str)
607		s--;
608	    break;
609	case '\n':
610	    *s = 0;
611	    break;
612	default:
613	    if (s - str < size - 1)
614		*s++ = c;
615	}
616	putchar(c);
617    } while (c != '\n');
618}
619
620static int
621putchar(int c)
622{
623    if (c == '\n')
624	putch('\r');
625    return putch(c);
626}
627
628static int
629getchar(void)
630{
631    int c;
632
633    c = getch();
634    if (c == '\r')
635	c = '\n';
636    return c;
637}
638
639static void *
640memcpy(void *dst, const void *src, size_t size)
641{
642    const char *s;
643    char *d;
644
645    for (d = dst, s = src; size; size--)
646	*d++ = *s++;
647    return dst;
648}
649
650static int
651strcmp(const char *s1, const char *s2)
652{
653    for (; *s1 == *s2 && *s1; s1++, s2++);
654    return (u_char)*s1 - (u_char)*s2;
655}
656
657static void *
658malloc(size_t size)
659{
660    static uint32_t next;
661    void *p;
662
663    if (!next)
664	next = roundup2(__base + _end, 0x10000) - __base;
665    p = (void *)next;
666    next += size;
667    return p;
668}
669
670static uint32_t
671memsize(int type)
672{
673    v86.ctl = V86_FLAGS;
674    v86.addr = type;
675    v86.eax = 0x8800;
676    v86int();
677    return v86.eax;
678}
679
680static uint32_t
681drvinfo(int drive)
682{
683    v86.addr = 0x13;
684    v86.eax = 0x800;
685    v86.edx = DRV_HARD + drive;
686    v86int();
687    if (V86_CY(v86.efl))
688	return 0x4f010f;
689    return ((v86.ecx & 0xc0) << 18) | ((v86.ecx & 0xff00) << 8) |
690	   (v86.edx & 0xff00) | (v86.ecx & 0x3f);
691}
692
693static int
694drvread(void *buf, unsigned lba, unsigned nblk)
695{
696    v86.ctl = V86_ADDR | V86_CALLF | V86_FLAGS;
697    v86.addr = 0x604;
698    v86.eax = nblk;
699    v86.ebx = VTOPSEG(buf) << 16 | VTOPOFF(buf);
700    v86.ecx = lba;
701    v86.edx = dsk.drive;
702    v86int();
703    v86.ctl = V86_FLAGS;
704    if (V86_CY(v86.efl)) {
705	printf("Disk error 0x%x (lba=0x%x)\n", v86.eax >> 8 & 0xff,
706	       lba);
707	return -1;
708    }
709    return 0;
710}
711
712static int
713keyhit(unsigned ticks)
714{
715    uint32_t t0, t1;
716
717    t0 = 0;
718    for (;;) {
719	v86.addr = 0x16;
720	v86.eax = 0x100;
721	v86int();
722	if (!V86_ZR(v86.efl))
723	    return 1;
724	t1 = *(uint32_t *)PTOV(0x46c);
725	if (!t0)
726	    t0 = t1;
727	if (t1 < t0 || t1 >= t0 + ticks)
728	    return 0;
729    }
730}
731
732static int
733putch(int c)
734{
735    v86.addr = 0x10;
736    v86.eax = 0xe00 | (c & 0xff);
737    v86.ebx = 0x7;
738    v86int();
739    return c;
740}
741
742static int
743getch(void)
744{
745    v86.addr = 0x16;
746    v86.eax = 0;
747    v86int();
748    return v86.eax & 0xff;
749}
750