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