1119482Sobrien/*-
240269Srnordier * Copyright (c) 1998 Robert Nordier
340269Srnordier * All rights reserved.
440269Srnordier *
540269Srnordier * Redistribution and use in source and binary forms are freely
640269Srnordier * permitted provided that the above copyright notice and this
740269Srnordier * paragraph and the following disclaimer are duplicated in all
840269Srnordier * such forms.
940269Srnordier *
1040269Srnordier * This software is provided "AS IS" and without any express or
1140269Srnordier * implied warranties, including, without limitation, the implied
1240269Srnordier * warranties of merchantability and fitness for a particular
1340269Srnordier * purpose.
1440269Srnordier */
1540269Srnordier
16119482Sobrien#include <sys/cdefs.h>
17119482Sobrien__FBSDID("$FreeBSD$");
1840269Srnordier
1940269Srnordier#include <sys/param.h>
20172940Sjhb#include <sys/gpt.h>
2140269Srnordier#include <sys/dirent.h>
22122463Sbde#include <sys/reboot.h>
23122463Sbde
2440269Srnordier#include <machine/bootinfo.h>
2576224Sobrien#include <machine/elf.h>
26181436Sjhb#include <machine/psl.h>
2740269Srnordier
2840269Srnordier#include <stdarg.h>
2940269Srnordier
3040269Srnordier#include <a.out.h>
3140269Srnordier
3240269Srnordier#include <btxv86.h>
3340269Srnordier
3440404Srnordier#include "lib.h"
35213136Spjd#include "rbx.h"
36213136Spjd#include "drv.h"
37213136Spjd#include "util.h"
38213136Spjd#include "cons.h"
39213136Spjd#include "gpt.h"
4040404Srnordier
41231287Sbapt#define PATH_DOTCONFIG  "/boot.config"
42231287Sbapt#define PATH_CONFIG	"/boot/config"
4340269Srnordier#define PATH_BOOT3	"/boot/loader"
44148767Sssouhlal#define PATH_KERNEL	"/boot/kernel/kernel"
4540269Srnordier
4642478Speter#define ARGS		0x900
47163707Sru#define NOPT		14
48108000Simp#define NDEV		3
4940269Srnordier#define MEM_BASE	0x12
5040269Srnordier#define MEM_EXT 	0x15
5140269Srnordier
5240307Srnordier#define DRV_HARD	0x80
5340307Srnordier#define DRV_MASK	0x7f
5440307Srnordier
5557090Sru#define TYPE_AD		0
56108000Simp#define TYPE_DA		1
5798542Smckusick#define TYPE_MAXHARD	TYPE_DA
58108000Simp#define TYPE_FD		2
5940307Srnordier
6040269Srnordierextern uint32_t _end;
6140269Srnordier
62172940Sjhbstatic const uuid_t freebsd_ufs_uuid = GPT_ENT_TYPE_FREEBSD_UFS;
63163707Srustatic const char optstr[NOPT] = "DhaCcdgmnpqrsv"; /* Also 'P', 'S' */
6440269Srnordierstatic const unsigned char flags[NOPT] = {
65213136Spjd	RBX_DUAL,
66213136Spjd	RBX_SERIAL,
67213136Spjd	RBX_ASKNAME,
68213136Spjd	RBX_CDROM,
69213136Spjd	RBX_CONFIG,
70213136Spjd	RBX_KDB,
71213136Spjd	RBX_GDB,
72213136Spjd	RBX_MUTE,
73213136Spjd	RBX_NOINTR,
74213136Spjd	RBX_PAUSE,
75213136Spjd	RBX_QUIET,
76213136Spjd	RBX_DFLTROOT,
77213136Spjd	RBX_SINGLE,
78213136Spjd	RBX_VERBOSE
7940269Srnordier};
80213136Spjduint32_t opts;
8140269Srnordier
82108000Simpstatic const char *const dev_nm[NDEV] = {"ad", "da", "fd"};
83108000Simpstatic const unsigned char dev_maj[NDEV] = {30, 4, 2};
8440269Srnordier
85213136Spjdstatic struct dsk dsk;
8653159Sobrienstatic char kname[1024];
87149212Siedowsestatic int comspeed = SIOSPD;
8840269Srnordierstatic struct bootinfo bootinfo;
8940269Srnordier
9040269Srnordiervoid exit(int);
91108000Simpstatic void load(void);
92213136Spjdstatic int parse(char *, int *);
9340323Srnordierstatic int xfsread(ino_t, void *, size_t);
94172940Sjhbstatic int dskread(void *, daddr_t, unsigned);
95108000Simpstatic uint32_t memsize(void);
9640269Srnordier
9797860Sphk#include "ufsread.c"
9862665Sjhb
99151382Ssobomaxstatic inline int
10098542Smckusickxfsread(ino_t inode, void *buf, size_t nbyte)
10162665Sjhb{
102213136Spjd
103213136Spjd	if ((size_t)fsread(inode, buf, nbyte) != nbyte) {
104213136Spjd		printf("Invalid %s\n", "format");
105213136Spjd		return (-1);
106213136Spjd	}
107213136Spjd	return (0);
10862665Sjhb}
10962665Sjhb
110108000Simpstatic inline uint32_t
111108000Simpmemsize(void)
112108000Simp{
113213136Spjd
114213136Spjd	v86.addr = MEM_EXT;
115213136Spjd	v86.eax = 0x8800;
116213136Spjd	v86int();
117213136Spjd	return (v86.eax);
118108000Simp}
119108000Simp
120213136Spjdstatic int
121213136Spjdgptinit(void)
12262665Sjhb{
12362665Sjhb
124213136Spjd	if (gptread(&freebsd_ufs_uuid, &dsk, dmadat->secbuf) == -1) {
125213136Spjd		printf("%s: unable to load GPT\n", BOOTPROG);
126213136Spjd		return (-1);
12762665Sjhb	}
128213136Spjd	if (gptfind(&freebsd_ufs_uuid, &dsk, dsk.part) == -1) {
129213136Spjd		printf("%s: no UFS partition was found\n", BOOTPROG);
130213136Spjd		return (-1);
131213136Spjd	}
132213136Spjd	dsk_meta = 0;
133213136Spjd	return (0);
13462665Sjhb}
13562665Sjhb
13640269Srnordierint
13740269Srnordiermain(void)
13840269Srnordier{
139213136Spjd	char cmd[512], cmdtmp[512];
140213136Spjd	int autoboot, dskupdated;
141213136Spjd	ino_t ino;
14240269Srnordier
143213136Spjd	dmadat = (void *)(roundup2(__base + (int32_t)&_end, 0x10000) - __base);
144213136Spjd	v86.ctl = V86_FLAGS;
145213136Spjd	v86.efl = PSL_RESERVED_DEFAULT | PSL_I;
146213136Spjd	dsk.drive = *(uint8_t *)PTOV(ARGS);
147213136Spjd	dsk.type = dsk.drive & DRV_HARD ? TYPE_AD : TYPE_FD;
148213136Spjd	dsk.unit = dsk.drive & DRV_MASK;
149213136Spjd	dsk.part = -1;
150213136Spjd	dsk.start = 0;
151213136Spjd	bootinfo.bi_version = BOOTINFO_VERSION;
152213136Spjd	bootinfo.bi_size = sizeof(bootinfo);
153213136Spjd	bootinfo.bi_basemem = 0;	/* XXX will be filled by loader or kernel */
154213136Spjd	bootinfo.bi_extmem = memsize();
155213136Spjd	bootinfo.bi_memsizes_valid++;
15694411Spb
157213136Spjd	/* Process configuration file */
15894411Spb
159213136Spjd	if (gptinit() != 0)
160213136Spjd		return (-1);
16198542Smckusick
162213136Spjd	autoboot = 1;
163213136Spjd	*cmd = '\0';
16498542Smckusick
165213136Spjd	for (;;) {
166213136Spjd		*kname = '\0';
167231287Sbapt		if ((ino = lookup(PATH_CONFIG)) ||
168231287Sbapt		    (ino = lookup(PATH_DOTCONFIG)))
169213136Spjd			fsread(ino, cmd, sizeof(cmd));
17094411Spb
171213136Spjd		if (*cmd != '\0') {
172213136Spjd			memcpy(cmdtmp, cmd, sizeof(cmdtmp));
173213136Spjd			if (parse(cmdtmp, &dskupdated))
174213136Spjd				break;
175213136Spjd			if (dskupdated && gptinit() != 0)
176213136Spjd				break;
177213136Spjd			if (!OPT_CHECK(RBX_QUIET))
178213136Spjd				printf("%s: %s", PATH_CONFIG, cmd);
179213136Spjd			*cmd = '\0';
180213136Spjd		}
18194411Spb
182213136Spjd		if (autoboot && keyhit(3)) {
183213136Spjd			if (*kname == '\0')
184213136Spjd				memcpy(kname, PATH_BOOT3, sizeof(PATH_BOOT3));
185213136Spjd			break;
186213136Spjd		}
187213136Spjd		autoboot = 0;
188213136Spjd
189213136Spjd		/*
190213136Spjd		 * Try to exec stage 3 boot loader. If interrupted by a
191213136Spjd		 * keypress, or in case of failure, try to load a kernel
192213136Spjd		 * directly instead.
193213136Spjd		 */
194213136Spjd		if (*kname != '\0')
195213136Spjd			load();
196213136Spjd		memcpy(kname, PATH_BOOT3, sizeof(PATH_BOOT3));
197213136Spjd		load();
198213136Spjd		memcpy(kname, PATH_KERNEL, sizeof(PATH_KERNEL));
199213136Spjd		load();
200213136Spjd		gptbootfailed(&dsk);
201213136Spjd		if (gptfind(&freebsd_ufs_uuid, &dsk, -1) == -1)
202213136Spjd			break;
203213136Spjd		dsk_meta = 0;
20440269Srnordier	}
20594411Spb
206213136Spjd	/* Present the user with the boot2 prompt. */
20794411Spb
208213136Spjd	for (;;) {
209213136Spjd		if (!OPT_CHECK(RBX_QUIET)) {
210213136Spjd			printf("\nFreeBSD/x86 boot\n"
211213136Spjd			    "Default: %u:%s(%up%u)%s\n"
212213136Spjd			    "boot: ",
213213136Spjd			    dsk.drive & DRV_MASK, dev_nm[dsk.type], dsk.unit,
214213136Spjd			    dsk.part, kname);
215213136Spjd		}
216213136Spjd		if (ioctrl & IO_SERIAL)
217213136Spjd			sio_flush();
218213136Spjd		*cmd = '\0';
219213136Spjd		if (keyhit(0))
220213136Spjd			getstr(cmd, sizeof(cmd));
221213136Spjd		else if (!OPT_CHECK(RBX_QUIET))
222213136Spjd			putchar('\n');
223213136Spjd		if (parse(cmd, &dskupdated)) {
224213136Spjd			putchar('\a');
225213136Spjd			continue;
226213136Spjd		}
227213136Spjd		if (dskupdated && gptinit() != 0)
228213136Spjd			continue;
229213136Spjd		load();
230213136Spjd	}
231213136Spjd	/* NOTREACHED */
23240269Srnordier}
23340269Srnordier
23462665Sjhb/* XXX - Needed for btxld to link the boot2 binary; do not remove. */
23540269Srnordiervoid
23640269Srnordierexit(int x)
23740269Srnordier{
23840269Srnordier}
23940269Srnordier
24040269Srnordierstatic void
241108000Simpload(void)
24240269Srnordier{
243151381Ssobomax    union {
24440269Srnordier	struct exec ex;
24540269Srnordier	Elf32_Ehdr eh;
24640269Srnordier    } hdr;
247151382Ssobomax    static Elf32_Phdr ep[2];
248151382Ssobomax    static Elf32_Shdr es[2];
24940269Srnordier    caddr_t p;
25040269Srnordier    ino_t ino;
25140269Srnordier    uint32_t addr, x;
25240269Srnordier    int fmt, i, j;
25340269Srnordier
254108000Simp    if (!(ino = lookup(kname))) {
255213136Spjd	if (!ls) {
256213136Spjd	    printf("%s: No %s on %u:%s(%up%u)\n", BOOTPROG,
257213136Spjd		kname, dsk.drive & DRV_MASK, dev_nm[dsk.type], dsk.unit,
258213136Spjd		dsk.part);
259213136Spjd	}
26040269Srnordier	return;
26140269Srnordier    }
26240323Srnordier    if (xfsread(ino, &hdr, sizeof(hdr)))
26340269Srnordier	return;
26440269Srnordier    if (N_GETMAGIC(hdr.ex) == ZMAGIC)
26540269Srnordier	fmt = 0;
26640269Srnordier    else if (IS_ELF(hdr.eh))
26740269Srnordier	fmt = 1;
26840269Srnordier    else {
26940269Srnordier	printf("Invalid %s\n", "format");
27040269Srnordier	return;
27140269Srnordier    }
27240269Srnordier    if (fmt == 0) {
273163914Sru	addr = hdr.ex.a_entry & 0xffffff;
27440269Srnordier	p = PTOV(addr);
27540269Srnordier	fs_off = PAGE_SIZE;
27640323Srnordier	if (xfsread(ino, p, hdr.ex.a_text))
27740269Srnordier	    return;
27840269Srnordier	p += roundup2(hdr.ex.a_text, PAGE_SIZE);
27940323Srnordier	if (xfsread(ino, p, hdr.ex.a_data))
28040269Srnordier	    return;
28141010Srnordier	p += hdr.ex.a_data + roundup2(hdr.ex.a_bss, PAGE_SIZE);
28240269Srnordier	bootinfo.bi_symtab = VTOP(p);
283107889Sobrien	memcpy(p, &hdr.ex.a_syms, sizeof(hdr.ex.a_syms));
28440269Srnordier	p += sizeof(hdr.ex.a_syms);
28540269Srnordier	if (hdr.ex.a_syms) {
28640323Srnordier	    if (xfsread(ino, p, hdr.ex.a_syms))
28740269Srnordier		return;
28840269Srnordier	    p += hdr.ex.a_syms;
28940323Srnordier	    if (xfsread(ino, p, sizeof(int)))
29040269Srnordier		return;
29140269Srnordier	    x = *(uint32_t *)p;
29240269Srnordier	    p += sizeof(int);
29340269Srnordier	    x -= sizeof(int);
29440323Srnordier	    if (xfsread(ino, p, x))
29540269Srnordier		return;
29640269Srnordier	    p += x;
29740269Srnordier	}
29840269Srnordier    } else {
29940269Srnordier	fs_off = hdr.eh.e_phoff;
30048034Srnordier	for (j = i = 0; i < hdr.eh.e_phnum && j < 2; i++) {
30140323Srnordier	    if (xfsread(ino, ep + j, sizeof(ep[0])))
30240269Srnordier		return;
30340269Srnordier	    if (ep[j].p_type == PT_LOAD)
30440269Srnordier		j++;
30540269Srnordier	}
30640269Srnordier	for (i = 0; i < 2; i++) {
307163914Sru	    p = PTOV(ep[i].p_paddr & 0xffffff);
30840269Srnordier	    fs_off = ep[i].p_offset;
30940323Srnordier	    if (xfsread(ino, p, ep[i].p_filesz))
31040269Srnordier		return;
31140269Srnordier	}
31240269Srnordier	p += roundup2(ep[1].p_memsz, PAGE_SIZE);
31340269Srnordier	bootinfo.bi_symtab = VTOP(p);
31440269Srnordier	if (hdr.eh.e_shnum == hdr.eh.e_shstrndx + 3) {
31540269Srnordier	    fs_off = hdr.eh.e_shoff + sizeof(es[0]) *
31640269Srnordier		(hdr.eh.e_shstrndx + 1);
31740323Srnordier	    if (xfsread(ino, &es, sizeof(es)))
31840269Srnordier		return;
31940269Srnordier	    for (i = 0; i < 2; i++) {
320107889Sobrien		memcpy(p, &es[i].sh_size, sizeof(es[i].sh_size));
32140269Srnordier		p += sizeof(es[i].sh_size);
32240269Srnordier		fs_off = es[i].sh_offset;
32340323Srnordier		if (xfsread(ino, p, es[i].sh_size))
32440269Srnordier		    return;
32540269Srnordier		p += es[i].sh_size;
32640269Srnordier	    }
32740269Srnordier	}
328163914Sru	addr = hdr.eh.e_entry & 0xffffff;
32940269Srnordier    }
33040269Srnordier    bootinfo.bi_esymtab = VTOP(p);
331108000Simp    bootinfo.bi_kernelname = VTOP(kname);
33243113Smsmith    bootinfo.bi_bios_dev = dsk.drive;
333151381Ssobomax    __exec((caddr_t)addr, RB_BOOTINFO | (opts & RBX_MASK),
334172940Sjhb	   MAKEBOOTDEV(dev_maj[dsk.type], dsk.part + 1, dsk.unit, 0xff),
33540307Srnordier	   0, 0, 0, VTOP(&bootinfo));
33640269Srnordier}
33740269Srnordier
33840269Srnordierstatic int
339213136Spjdparse(char *cmdstr, int *dskupdated)
34040269Srnordier{
341213136Spjd    char *arg = cmdstr;
342149212Siedowse    char *ep, *p, *q;
343149212Siedowse    const char *cp;
344108000Simp    unsigned int drv;
345149212Siedowse    int c, i, j;
34640269Srnordier
347213136Spjd    *dskupdated = 0;
34840269Srnordier    while ((c = *arg++)) {
34961627Sru	if (c == ' ' || c == '\t' || c == '\n')
35040269Srnordier	    continue;
35161627Sru	for (p = arg; *p && *p != '\n' && *p != ' ' && *p != '\t'; p++);
352149212Siedowse	ep = p;
35340269Srnordier	if (*p)
35440269Srnordier	    *p++ = 0;
35540404Srnordier	if (c == '-') {
35640269Srnordier	    while ((c = *arg++)) {
357149212Siedowse		if (c == 'P') {
358149212Siedowse		    if (*(uint8_t *)PTOV(0x496) & 0x10) {
359149212Siedowse			cp = "yes";
360149212Siedowse		    } else {
361151999Sru			opts |= OPT_SET(RBX_DUAL) | OPT_SET(RBX_SERIAL);
362149212Siedowse			cp = "no";
363149212Siedowse		    }
364149212Siedowse		    printf("Keyboard: %s\n", cp);
365149212Siedowse		    continue;
366149212Siedowse		} else if (c == 'S') {
367149212Siedowse		    j = 0;
368149212Siedowse		    while ((unsigned int)(i = *arg++ - '0') <= 9)
369149212Siedowse			j = j * 10 + i;
370149212Siedowse		    if (j > 0 && i == -'0') {
371149212Siedowse			comspeed = j;
372149212Siedowse			break;
373149212Siedowse		    }
374149212Siedowse		    /* Fall through to error below ('S' not in optstr[]). */
375149212Siedowse		}
37640269Srnordier		for (i = 0; c != optstr[i]; i++)
37740269Srnordier		    if (i == NOPT - 1)
37840269Srnordier			return -1;
379151999Sru		opts ^= OPT_SET(flags[i]);
38040269Srnordier	    }
381151999Sru	    ioctrl = OPT_CHECK(RBX_DUAL) ? (IO_SERIAL|IO_KEYBOARD) :
382151999Sru		     OPT_CHECK(RBX_SERIAL) ? IO_SERIAL : IO_KEYBOARD;
383242562Savg	    if (ioctrl & IO_SERIAL) {
384242562Savg	        if (sio_init(115200 / comspeed) != 0)
385242562Savg		    ioctrl &= ~IO_SERIAL;
386242562Savg	    }
38740404Srnordier	} else {
38840269Srnordier	    for (q = arg--; *q && *q != '('; q++);
38940269Srnordier	    if (*q) {
39040269Srnordier		drv = -1;
39140269Srnordier		if (arg[1] == ':') {
39240269Srnordier		    drv = *arg - '0';
393108000Simp		    if (drv > 9)
394108000Simp			return (-1);
39540269Srnordier		    arg += 2;
39640269Srnordier		}
39740269Srnordier		if (q - arg != 2)
39840269Srnordier		    return -1;
39940269Srnordier		for (i = 0; arg[0] != dev_nm[i][0] ||
40040314Srnordier			    arg[1] != dev_nm[i][1]; i++)
40140269Srnordier		    if (i == NDEV - 1)
40240269Srnordier			return -1;
40340269Srnordier		dsk.type = i;
40440269Srnordier		arg += 3;
405108000Simp		dsk.unit = *arg - '0';
406196326Sjhay		if (arg[1] != 'p' || dsk.unit > 9)
40740269Srnordier		    return -1;
40840269Srnordier		arg += 2;
409196326Sjhay		dsk.part = *arg - '0';
410196326Sjhay		if (dsk.part < 1 || dsk.part > 9)
411196326Sjhay		    return -1;
412196326Sjhay		arg++;
413172940Sjhb		if (arg[0] != ')')
41440269Srnordier		    return -1;
415172940Sjhb		arg++;
41640269Srnordier		if (drv == -1)
41740307Srnordier		    drv = dsk.unit;
41898542Smckusick		dsk.drive = (dsk.type <= TYPE_MAXHARD
41998542Smckusick			     ? DRV_HARD : 0) + drv;
420213136Spjd		*dskupdated = 1;
42140269Srnordier	    }
422149212Siedowse	    if ((i = ep - arg)) {
423107889Sobrien		if ((size_t)i >= sizeof(kname))
42440269Srnordier		    return -1;
42540269Srnordier		memcpy(kname, arg, i + 1);
42640269Srnordier	    }
42740269Srnordier	}
42840269Srnordier	arg = p;
42940269Srnordier    }
43040269Srnordier    return 0;
43140269Srnordier}
43240269Srnordier
43340269Srnordierstatic int
434172940Sjhbdskread(void *buf, daddr_t lba, unsigned nblk)
43540269Srnordier{
43640269Srnordier
437213136Spjd	return drvread(&dsk, buf, lba + dsk.start, nblk);
43840269Srnordier}
439