1163533Simp/*-
2183636Simp * Copyright (c) 2008 John Hay
3183636Simp * Copyright (c) 2006 Warner Losh
4163533Simp * Copyright (c) 1998 Robert Nordier
5163533Simp * All rights reserved.
6163533Simp *
7163533Simp * Redistribution and use in source and binary forms are freely
8163533Simp * permitted provided that the above copyright notice and this
9163533Simp * paragraph and the following disclaimer are duplicated in all
10163533Simp * such forms.
11163533Simp *
12163533Simp * This software is provided "AS IS" and without any express or
13163533Simp * implied warranties, including, without limitation, the implied
14163533Simp * warranties of merchantability and fitness for a particular
15163533Simp * purpose.
16163533Simp */
17163533Simp
18163533Simp#include <sys/cdefs.h>
19163533Simp__FBSDID("$FreeBSD$");
20163533Simp
21163533Simp#include <sys/param.h>
22163533Simp#include <sys/disklabel.h>
23163533Simp#include <sys/diskmbr.h>
24163533Simp#include <sys/dirent.h>
25163533Simp#include <sys/reboot.h>
26163533Simp
27163533Simp#include <machine/elf.h>
28163533Simp
29163533Simp#include <stdarg.h>
30163533Simp
31163533Simp#include "lib.h"
32164134Simp#include "board.h"
33163533Simp
34163533Simp#define RBX_ASKNAME	0x0	/* -a */
35163533Simp#define RBX_SINGLE	0x1	/* -s */
36163533Simp/* 0x2 is reserved for log2(RB_NOSYNC). */
37163533Simp/* 0x3 is reserved for log2(RB_HALT). */
38163533Simp/* 0x4 is reserved for log2(RB_INITNAME). */
39163533Simp#define RBX_DFLTROOT	0x5	/* -r */
40163533Simp/* #define RBX_KDB 	0x6	   -d */
41163533Simp/* 0x7 is reserved for log2(RB_RDONLY). */
42163533Simp/* 0x8 is reserved for log2(RB_DUMP). */
43163533Simp/* 0x9 is reserved for log2(RB_MINIROOT). */
44163533Simp#define RBX_CONFIG	0xa	/* -c */
45163533Simp#define RBX_VERBOSE	0xb	/* -v */
46163533Simp/* #define RBX_SERIAL	0xc	   -h */
47163533Simp/* #define RBX_CDROM	0xd	   -C */
48163533Simp/* 0xe is reserved for log2(RB_POWEROFF). */
49163533Simp#define RBX_GDB 	0xf	/* -g */
50163533Simp/* #define RBX_MUTE	0x10	   -m */
51163533Simp/* 0x11 is reserved for log2(RB_SELFTEST). */
52163533Simp/* 0x12 is reserved for boot programs. */
53163533Simp/* 0x13 is reserved for boot programs. */
54163533Simp/* #define RBX_PAUSE	0x14	   -p */
55163533Simp/* #define RBX_QUIET	0x15	   -q */
56183636Simp#define RBX_NOINTR	0x1c	/* -n */
57163533Simp/* 0x1d is reserved for log2(RB_MULTIPLE) and is just misnamed here. */
58163533Simp/* #define RBX_DUAL	0x1d	   -D */
59163533Simp/* 0x1f is reserved for log2(RB_BOOTINFO). */
60163533Simp
61163533Simp/* pass: -a, -s, -r, -v, -g */
62163533Simp#define RBX_MASK	(OPT_SET(RBX_ASKNAME) | OPT_SET(RBX_SINGLE) | \
63163533Simp			OPT_SET(RBX_DFLTROOT) | \
64163533Simp			OPT_SET(RBX_VERBOSE) | \
65163533Simp			OPT_SET(RBX_GDB))
66163533Simp
67231287Sbapt#define PATH_DOTCONFIG	"/boot.config"
68231287Sbapt#define PATH_CONFIG	"/boot/config"
69163533Simp//#define PATH_KERNEL	"/boot/kernel/kernel"
70164134Simp#define PATH_KERNEL	"/boot/kernel/kernel.gz.tramp"
71163533Simp
72183636Simpextern uint32_t _end;
73163533Simp
74183636Simp#define NOPT		6
75183636Simp
76163533Simp#define OPT_SET(opt)	(1 << (opt))
77163533Simp#define OPT_CHECK(opt)	((opts) & OPT_SET(opt))
78163533Simp
79183636Simpstatic const char optstr[NOPT] = "agnrsv";
80163533Simpstatic const unsigned char flags[NOPT] = {
81183636Simp	RBX_ASKNAME,
82183636Simp	RBX_GDB,
83183636Simp	RBX_NOINTR,
84183636Simp	RBX_DFLTROOT,
85183636Simp	RBX_SINGLE,
86183636Simp	RBX_VERBOSE
87163533Simp};
88163533Simp
89163533Simpunsigned dsk_start;
90163533Simpstatic char cmd[512];
91163533Simpstatic char kname[1024];
92163533Simpstatic uint32_t opts;
93163533Simpstatic int dsk_meta;
94163533Simp
95163533Simpstatic void load(void);
96163533Simpstatic int parse(void);
97163533Simpstatic int xfsread(ino_t, void *, size_t);
98163533Simpstatic int dskread(void *, unsigned, unsigned);
99183673Simp#ifdef FIXUP_BOOT_DRV
100183673Simpstatic void fixup_boot_drv(caddr_t, int, int, int);
101183673Simp#endif
102163533Simp
103173040Sjhb#define	UFS_SMALL_CGBASE
104163533Simp#include "ufsread.c"
105163533Simp
106183636Simp#ifdef DEBUG
107183636Simp#define	DPRINTF(fmt, ...) printf(fmt, __VA_ARGS__)
108183636Simp#else
109183636Simp#define	DPRINTF(fmt, ...)
110183636Simp#endif
111183636Simp
112163533Simpstatic inline int
113163533Simpxfsread(ino_t inode, void *buf, size_t nbyte)
114163533Simp{
115183634Simp	if ((size_t)fsread(inode, buf, nbyte) != nbyte)
116183634Simp		return -1;
117183634Simp	return 0;
118163533Simp}
119163533Simp
120163533Simpstatic inline void
121163533Simpgetstr(int c)
122163533Simp{
123183634Simp	char *s;
124163533Simp
125183634Simp	s = cmd;
126183634Simp	if (c == 0)
127183634Simp		c = getc(10000);
128183634Simp	for (;;) {
129183634Simp		switch (c) {
130183634Simp		case 0:
131183634Simp			break;
132183634Simp		case '\177':
133183634Simp		case '\b':
134183634Simp			if (s > cmd) {
135183634Simp				s--;
136183634Simp				printf("\b \b");
137183634Simp			}
138183634Simp			break;
139183634Simp		case '\n':
140183634Simp		case '\r':
141183634Simp			*s = 0;
142183634Simp			return;
143183634Simp		default:
144183634Simp			if (s - cmd < sizeof(cmd) - 1)
145183634Simp				*s++ = c;
146183634Simp			xputchar(c);
147183634Simp		}
148183634Simp		c = getc(10000);
149163533Simp	}
150163533Simp}
151163533Simp
152163533Simpint
153163533Simpmain(void)
154163533Simp{
155183634Simp	int autoboot, c = 0;
156183634Simp	ino_t ino;
157163533Simp
158183636Simp	dmadat = (void *)(0x20000000 + (16 << 20));
159183634Simp	board_init();
160163533Simp
161183634Simp	autoboot = 1;
162163533Simp
163183636Simp	/* Process configuration file */
164231287Sbapt	if ((ino = lookup(PATH_CONFIG)) ||
165231287Sbapt	    (ino = lookup(PATH_DOTCONFIG)))
166183634Simp		fsread(ino, cmd, sizeof(cmd));
167163533Simp
168183634Simp	if (*cmd) {
169183634Simp		if (parse())
170183634Simp			autoboot = 0;
171183636Simp		printf("%s: %s\n", PATH_CONFIG, cmd);
172183634Simp		/* Do not process this command twice */
173183634Simp		*cmd = 0;
174183634Simp	}
175163533Simp
176183634Simp	if (*kname == '\0')
177183634Simp		strcpy(kname, PATH_KERNEL);
178183636Simp
179183636Simp	/* Present the user with the boot2 prompt. */
180183634Simp	for (;;) {
181183634Simp		printf("\nDefault: %s\nboot: ", kname);
182183636Simp		if (!autoboot ||
183183636Simp		    (OPT_CHECK(RBX_NOINTR) == 0 && (c = getc(2)) != 0))
184183634Simp			getstr(c);
185183634Simp		xputchar('\n');
186183634Simp		autoboot = 0;
187183634Simp		c = 0;
188183634Simp		if (parse())
189183634Simp			xputchar('\a');
190183634Simp		else
191183634Simp			load();
192183634Simp	}
193163533Simp}
194163533Simp
195163533Simpstatic void
196163533Simpload(void)
197163533Simp{
198183634Simp	Elf32_Ehdr eh;
199183634Simp	static Elf32_Phdr ep[2];
200183634Simp	caddr_t p;
201183634Simp	ino_t ino;
202183634Simp	uint32_t addr;
203183634Simp	int i, j;
204183673Simp#ifdef FIXUP_BOOT_DRV
205183673Simp	caddr_t staddr;
206183673Simp	int klen;
207163533Simp
208183673Simp	staddr = (caddr_t)0xffffffff;
209183673Simp	klen = 0;
210183673Simp#endif
211183634Simp	if (!(ino = lookup(kname))) {
212183634Simp		if (!ls)
213183634Simp			printf("No %s\n", kname);
214183634Simp		return;
215183634Simp	}
216183634Simp	if (xfsread(ino, &eh, sizeof(eh)))
217183634Simp		return;
218183634Simp	if (!IS_ELF(eh)) {
219183634Simp		printf("Invalid %s\n", "format");
220183634Simp		return;
221183634Simp	}
222183634Simp	fs_off = eh.e_phoff;
223183634Simp	for (j = i = 0; i < eh.e_phnum && j < 2; i++) {
224183634Simp		if (xfsread(ino, ep + j, sizeof(ep[0])))
225183634Simp			return;
226183634Simp		if (ep[j].p_type == PT_LOAD)
227183634Simp			j++;
228183634Simp	}
229183634Simp	for (i = 0; i < 2; i++) {
230183634Simp		p = (caddr_t)ep[i].p_paddr;
231183634Simp		fs_off = ep[i].p_offset;
232183673Simp#ifdef FIXUP_BOOT_DRV
233183673Simp		if (staddr == (caddr_t)0xffffffff)
234183673Simp			staddr = p;
235183673Simp		klen += ep[i].p_filesz;
236183673Simp#endif
237183634Simp		if (xfsread(ino, p, ep[i].p_filesz))
238183634Simp			return;
239183634Simp	}
240183634Simp	addr = eh.e_entry;
241183673Simp#ifdef FIXUP_BOOT_DRV
242183673Simp	fixup_boot_drv(staddr, klen, bootslice, bootpart);
243183673Simp#endif
244183634Simp	((void(*)(int))addr)(opts & RBX_MASK);
245163533Simp}
246163533Simp
247163533Simpstatic int
248163533Simpparse()
249163533Simp{
250183634Simp	char *arg = cmd;
251183634Simp	char *ep, *p;
252183634Simp	int c, i;
253163533Simp
254183634Simp	while ((c = *arg++)) {
255183634Simp		if (c == ' ' || c == '\t' || c == '\n')
256183634Simp			continue;
257183634Simp		for (p = arg; *p && *p != '\n' && *p != ' ' && *p != '\t'; p++);
258183634Simp		ep = p;
259183634Simp		if (*p)
260183634Simp			*p++ = 0;
261183634Simp		if (c == '-') {
262183634Simp			while ((c = *arg++)) {
263183634Simp				for (i = 0; c != optstr[i]; i++)
264183634Simp					if (i == NOPT - 1)
265183634Simp						return -1;
266183634Simp				opts ^= OPT_SET(flags[i]);
267183634Simp			}
268183634Simp		} else {
269183634Simp			arg--;
270183634Simp			if ((i = ep - arg)) {
271183634Simp				if ((size_t)i >= sizeof(kname))
272183634Simp					return -1;
273183634Simp				memcpy(kname, arg, i + 1);
274183634Simp			}
275183634Simp		}
276183634Simp		arg = p;
277163533Simp	}
278183634Simp	return 0;
279163533Simp}
280163533Simp
281163533Simpstatic int
282163533Simpdskread(void *buf, unsigned lba, unsigned nblk)
283163533Simp{
284183634Simp	struct dos_partition *dp;
285183634Simp	struct disklabel *d;
286183634Simp	char *sec;
287183634Simp	int i;
288163533Simp
289183634Simp	if (!dsk_meta) {
290183634Simp		sec = dmadat->secbuf;
291183634Simp		dsk_start = 0;
292183634Simp		if (drvread(sec, DOSBBSECTOR, 1))
293183634Simp			return -1;
294183634Simp		dp = (void *)(sec + DOSPARTOFF);
295183634Simp		for (i = 0; i < NDOSPART; i++) {
296183634Simp			if (dp[i].dp_typ == DOSPTYP_386BSD)
297183634Simp				break;
298183634Simp		}
299183634Simp		if (i == NDOSPART)
300183634Simp			return -1;
301183634Simp		/*
302183634Simp		 * Although dp_start is aligned within the disk
303183634Simp		 * partition structure, DOSPARTOFF is 446, which is
304183634Simp		 * only word (2) aligned, not longword (4) aligned.
305183634Simp		 * Cope by using memcpy to fetch the start of this
306183634Simp		 * partition.
307183634Simp		 */
308183634Simp		memcpy(&dsk_start, &dp[1].dp_start, 4);
309183634Simp		if (drvread(sec, dsk_start + LABELSECTOR, 1))
310183634Simp			return -1;
311183634Simp		d = (void *)(sec + LABELOFFSET);
312183634Simp		if (d->d_magic != DISKMAGIC || d->d_magic2 != DISKMAGIC) {
313183634Simp			printf("Invalid %s\n", "label");
314183634Simp			return -1;
315183634Simp		}
316183634Simp		if (!d->d_partitions[0].p_size) {
317183634Simp			printf("Invalid %s\n", "partition");
318183634Simp			return -1;
319183634Simp		}
320183634Simp		dsk_start += d->d_partitions[0].p_offset;
321183634Simp		dsk_start -= d->d_partitions[RAW_PART].p_offset;
322183634Simp		dsk_meta++;
323163533Simp	}
324183634Simp	return drvread(buf, dsk_start + lba, nblk);
325163533Simp}
326183673Simp
327183673Simp#ifdef FIXUP_BOOT_DRV
328183673Simp/*
329183673Simp * fixup_boot_drv() will try to find the ROOTDEVNAME spec in the kernel
330183673Simp * and change it to what was specified on the comandline or /boot.conf
331183673Simp * file or to what was encountered on the disk. It will try to handle 3
332183673Simp * different disk layouts, raw (dangerously dedicated), slice only and
333183673Simp * slice + partition. It will look for the following strings in the
334183673Simp * kernel, but if it is one of the first three, the string in the kernel
335183673Simp * must use the correct form to match the actual disk layout:
336183673Simp * - ufs:ad0a
337183673Simp * - ufs:ad0s1
338183673Simp * - ufs:ad0s1a
339183673Simp * - ufs:ROOTDEVNAME
340183673Simp * In the case of the first three strings, only the "a" at the end and
341183673Simp * the "1" after the "s" will be modified, if they exist. The string
342183673Simp * length will not be changed. In the case of the last string, the
343183673Simp * whole string will be built up and nul, '\0' terminated.
344183673Simp */
345183673Simpstatic void
346183673Simpfixup_boot_drv(caddr_t addr, int klen, int bs, int bp)
347183673Simp{
348183673Simp	const u_int8_t op[] = "ufs:ROOTDEVNAME";
349183673Simp	const u_int8_t op2[] = "ufs:ad0";
350183673Simp	u_int8_t *p, *ps;
351183673Simp
352183673Simp	DPRINTF("fixup_boot_drv: 0x%x, %d, slice %d, partition %d\n",
353183673Simp	    (int)addr, klen, bs, bp);
354183673Simp	if (bs > 4)
355183673Simp		return;
356183673Simp	if (bp > 7)
357183673Simp		return;
358183673Simp	ps = memmem(addr, klen, op, sizeof(op));
359183673Simp	if (ps != NULL) {
360183673Simp		p = ps + 4;	/* past ufs: */
361183673Simp		DPRINTF("Found it at 0x%x\n", (int)ps);
362183673Simp		p[0] = 'a'; p[1] = 'd'; p[2] = '0';	/* ad0 */
363183673Simp		p += 3;
364183673Simp		if (bs > 0) {
365183673Simp			/* append slice */
366183673Simp			*p++ = 's';
367183673Simp			*p++ = bs + '0';
368183673Simp		}
369183673Simp		if (disk_layout != DL_SLICE) {
370183673Simp			/* append partition */
371183673Simp			*p++ = bp + 'a';
372183673Simp		}
373183673Simp		*p = '\0';
374183673Simp	} else {
375183673Simp		ps = memmem(addr, klen, op2, sizeof(op2) - 1);
376183673Simp		if (ps != NULL) {
377183673Simp			p = ps + sizeof(op2) - 1;
378183673Simp			DPRINTF("Found it at 0x%x\n", (int)ps);
379183673Simp			if (*p == 's') {
380183673Simp				/* fix slice */
381183673Simp				p++;
382183673Simp				*p++ = bs + '0';
383183673Simp			}
384183673Simp			if (*p == 'a')
385183673Simp				*p = bp + 'a';
386183673Simp		}
387183673Simp	}
388183673Simp	if (ps == NULL) {
389183673Simp		printf("Could not locate \"%s\" to fix kernel boot device, "
390183673Simp		     "check ROOTDEVNAME is set\n", op);
391183673Simp		return;
392183673Simp	}
393183673Simp	DPRINTF("Changed boot device to %s\n", ps);
394183673Simp}
395183673Simp#endif
396