1/*-
2 * Copyright (c) 2008 John Hay
3 * Copyright (c) 2006 Warner Losh
4 * Copyright (c) 1998 Robert Nordier
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms are freely
8 * permitted provided that the above copyright notice and this
9 * paragraph and the following disclaimer are duplicated in all
10 * such forms.
11 *
12 * This software is provided "AS IS" and without any express or
13 * implied warranties, including, without limitation, the implied
14 * warranties of merchantability and fitness for a particular
15 * purpose.
16 */
17
18#include <sys/cdefs.h>
19__FBSDID("$FreeBSD$");
20
21#include <sys/param.h>
22#include <sys/disklabel.h>
23#include <sys/diskmbr.h>
24#include <sys/dirent.h>
25#include <sys/reboot.h>
26
27#include <machine/elf.h>
28
29#include <stdarg.h>
30
31#include "lib.h"
32#include "board.h"
33
34#define RBX_ASKNAME	0x0	/* -a */
35#define RBX_SINGLE	0x1	/* -s */
36/* 0x2 is reserved for log2(RB_NOSYNC). */
37/* 0x3 is reserved for log2(RB_HALT). */
38/* 0x4 is reserved for log2(RB_INITNAME). */
39#define RBX_DFLTROOT	0x5	/* -r */
40/* #define RBX_KDB 	0x6	   -d */
41/* 0x7 is reserved for log2(RB_RDONLY). */
42/* 0x8 is reserved for log2(RB_DUMP). */
43/* 0x9 is reserved for log2(RB_MINIROOT). */
44#define RBX_CONFIG	0xa	/* -c */
45#define RBX_VERBOSE	0xb	/* -v */
46/* #define RBX_SERIAL	0xc	   -h */
47/* #define RBX_CDROM	0xd	   -C */
48/* 0xe is reserved for log2(RB_POWEROFF). */
49#define RBX_GDB 	0xf	/* -g */
50/* #define RBX_MUTE	0x10	   -m */
51/* 0x11 is reserved for log2(RB_SELFTEST). */
52/* 0x12 is reserved for boot programs. */
53/* 0x13 is reserved for boot programs. */
54/* #define RBX_PAUSE	0x14	   -p */
55/* #define RBX_QUIET	0x15	   -q */
56#define RBX_NOINTR	0x1c	/* -n */
57/* 0x1d is reserved for log2(RB_MULTIPLE) and is just misnamed here. */
58/* #define RBX_DUAL	0x1d	   -D */
59/* 0x1f is reserved for log2(RB_BOOTINFO). */
60
61/* pass: -a, -s, -r, -v, -g */
62#define RBX_MASK	(OPT_SET(RBX_ASKNAME) | OPT_SET(RBX_SINGLE) | \
63			OPT_SET(RBX_DFLTROOT) | \
64			OPT_SET(RBX_VERBOSE) | \
65			OPT_SET(RBX_GDB))
66
67#define PATH_DOTCONFIG	"/boot.config"
68#define PATH_CONFIG	"/boot/config"
69//#define PATH_KERNEL	"/boot/kernel/kernel"
70#define PATH_KERNEL	"/boot/kernel/kernel.gz.tramp"
71
72extern uint32_t _end;
73
74#define NOPT		6
75
76#define OPT_SET(opt)	(1 << (opt))
77#define OPT_CHECK(opt)	((opts) & OPT_SET(opt))
78
79static const char optstr[NOPT] = "agnrsv";
80static const unsigned char flags[NOPT] = {
81	RBX_ASKNAME,
82	RBX_GDB,
83	RBX_NOINTR,
84	RBX_DFLTROOT,
85	RBX_SINGLE,
86	RBX_VERBOSE
87};
88
89unsigned dsk_start;
90static char cmd[512];
91static char kname[1024];
92static uint32_t opts;
93static int dsk_meta;
94
95static void load(void);
96static int parse(void);
97static int xfsread(ino_t, void *, size_t);
98static int dskread(void *, unsigned, unsigned);
99#ifdef FIXUP_BOOT_DRV
100static void fixup_boot_drv(caddr_t, int, int, int);
101#endif
102
103#define	UFS_SMALL_CGBASE
104#include "ufsread.c"
105
106#ifdef DEBUG
107#define	DPRINTF(fmt, ...) printf(fmt, __VA_ARGS__)
108#else
109#define	DPRINTF(fmt, ...)
110#endif
111
112static inline int
113xfsread(ino_t inode, void *buf, size_t nbyte)
114{
115	if ((size_t)fsread(inode, buf, nbyte) != nbyte)
116		return -1;
117	return 0;
118}
119
120static inline void
121getstr(int c)
122{
123	char *s;
124
125	s = cmd;
126	if (c == 0)
127		c = getc(10000);
128	for (;;) {
129		switch (c) {
130		case 0:
131			break;
132		case '\177':
133		case '\b':
134			if (s > cmd) {
135				s--;
136				printf("\b \b");
137			}
138			break;
139		case '\n':
140		case '\r':
141			*s = 0;
142			return;
143		default:
144			if (s - cmd < sizeof(cmd) - 1)
145				*s++ = c;
146			xputchar(c);
147		}
148		c = getc(10000);
149	}
150}
151
152int
153main(void)
154{
155	int autoboot, c = 0;
156	ino_t ino;
157
158	dmadat = (void *)(0x20000000 + (16 << 20));
159	board_init();
160
161	autoboot = 1;
162
163	/* Process configuration file */
164	if ((ino = lookup(PATH_CONFIG)) ||
165	    (ino = lookup(PATH_DOTCONFIG)))
166		fsread(ino, cmd, sizeof(cmd));
167
168	if (*cmd) {
169		if (parse())
170			autoboot = 0;
171		printf("%s: %s\n", PATH_CONFIG, cmd);
172		/* Do not process this command twice */
173		*cmd = 0;
174	}
175
176	if (*kname == '\0')
177		strcpy(kname, PATH_KERNEL);
178
179	/* Present the user with the boot2 prompt. */
180	for (;;) {
181		printf("\nDefault: %s\nboot: ", kname);
182		if (!autoboot ||
183		    (OPT_CHECK(RBX_NOINTR) == 0 && (c = getc(2)) != 0))
184			getstr(c);
185		xputchar('\n');
186		autoboot = 0;
187		c = 0;
188		if (parse())
189			xputchar('\a');
190		else
191			load();
192	}
193}
194
195static void
196load(void)
197{
198	Elf32_Ehdr eh;
199	static Elf32_Phdr ep[2];
200	caddr_t p;
201	ino_t ino;
202	uint32_t addr;
203	int i, j;
204#ifdef FIXUP_BOOT_DRV
205	caddr_t staddr;
206	int klen;
207
208	staddr = (caddr_t)0xffffffff;
209	klen = 0;
210#endif
211	if (!(ino = lookup(kname))) {
212		if (!ls)
213			printf("No %s\n", kname);
214		return;
215	}
216	if (xfsread(ino, &eh, sizeof(eh)))
217		return;
218	if (!IS_ELF(eh)) {
219		printf("Invalid %s\n", "format");
220		return;
221	}
222	fs_off = eh.e_phoff;
223	for (j = i = 0; i < eh.e_phnum && j < 2; i++) {
224		if (xfsread(ino, ep + j, sizeof(ep[0])))
225			return;
226		if (ep[j].p_type == PT_LOAD)
227			j++;
228	}
229	for (i = 0; i < 2; i++) {
230		p = (caddr_t)ep[i].p_paddr;
231		fs_off = ep[i].p_offset;
232#ifdef FIXUP_BOOT_DRV
233		if (staddr == (caddr_t)0xffffffff)
234			staddr = p;
235		klen += ep[i].p_filesz;
236#endif
237		if (xfsread(ino, p, ep[i].p_filesz))
238			return;
239	}
240	addr = eh.e_entry;
241#ifdef FIXUP_BOOT_DRV
242	fixup_boot_drv(staddr, klen, bootslice, bootpart);
243#endif
244	((void(*)(int))addr)(opts & RBX_MASK);
245}
246
247static int
248parse()
249{
250	char *arg = cmd;
251	char *ep, *p;
252	int c, i;
253
254	while ((c = *arg++)) {
255		if (c == ' ' || c == '\t' || c == '\n')
256			continue;
257		for (p = arg; *p && *p != '\n' && *p != ' ' && *p != '\t'; p++);
258		ep = p;
259		if (*p)
260			*p++ = 0;
261		if (c == '-') {
262			while ((c = *arg++)) {
263				for (i = 0; c != optstr[i]; i++)
264					if (i == NOPT - 1)
265						return -1;
266				opts ^= OPT_SET(flags[i]);
267			}
268		} else {
269			arg--;
270			if ((i = ep - arg)) {
271				if ((size_t)i >= sizeof(kname))
272					return -1;
273				memcpy(kname, arg, i + 1);
274			}
275		}
276		arg = p;
277	}
278	return 0;
279}
280
281static int
282dskread(void *buf, unsigned lba, unsigned nblk)
283{
284	struct dos_partition *dp;
285	struct disklabel *d;
286	char *sec;
287	int i;
288
289	if (!dsk_meta) {
290		sec = dmadat->secbuf;
291		dsk_start = 0;
292		if (drvread(sec, DOSBBSECTOR, 1))
293			return -1;
294		dp = (void *)(sec + DOSPARTOFF);
295		for (i = 0; i < NDOSPART; i++) {
296			if (dp[i].dp_typ == DOSPTYP_386BSD)
297				break;
298		}
299		if (i == NDOSPART)
300			return -1;
301		/*
302		 * Although dp_start is aligned within the disk
303		 * partition structure, DOSPARTOFF is 446, which is
304		 * only word (2) aligned, not longword (4) aligned.
305		 * Cope by using memcpy to fetch the start of this
306		 * partition.
307		 */
308		memcpy(&dsk_start, &dp[1].dp_start, 4);
309		if (drvread(sec, dsk_start + LABELSECTOR, 1))
310			return -1;
311		d = (void *)(sec + LABELOFFSET);
312		if (d->d_magic != DISKMAGIC || d->d_magic2 != DISKMAGIC) {
313			printf("Invalid %s\n", "label");
314			return -1;
315		}
316		if (!d->d_partitions[0].p_size) {
317			printf("Invalid %s\n", "partition");
318			return -1;
319		}
320		dsk_start += d->d_partitions[0].p_offset;
321		dsk_start -= d->d_partitions[RAW_PART].p_offset;
322		dsk_meta++;
323	}
324	return drvread(buf, dsk_start + lba, nblk);
325}
326
327#ifdef FIXUP_BOOT_DRV
328/*
329 * fixup_boot_drv() will try to find the ROOTDEVNAME spec in the kernel
330 * and change it to what was specified on the comandline or /boot.conf
331 * file or to what was encountered on the disk. It will try to handle 3
332 * different disk layouts, raw (dangerously dedicated), slice only and
333 * slice + partition. It will look for the following strings in the
334 * kernel, but if it is one of the first three, the string in the kernel
335 * must use the correct form to match the actual disk layout:
336 * - ufs:ad0a
337 * - ufs:ad0s1
338 * - ufs:ad0s1a
339 * - ufs:ROOTDEVNAME
340 * In the case of the first three strings, only the "a" at the end and
341 * the "1" after the "s" will be modified, if they exist. The string
342 * length will not be changed. In the case of the last string, the
343 * whole string will be built up and nul, '\0' terminated.
344 */
345static void
346fixup_boot_drv(caddr_t addr, int klen, int bs, int bp)
347{
348	const u_int8_t op[] = "ufs:ROOTDEVNAME";
349	const u_int8_t op2[] = "ufs:ad0";
350	u_int8_t *p, *ps;
351
352	DPRINTF("fixup_boot_drv: 0x%x, %d, slice %d, partition %d\n",
353	    (int)addr, klen, bs, bp);
354	if (bs > 4)
355		return;
356	if (bp > 7)
357		return;
358	ps = memmem(addr, klen, op, sizeof(op));
359	if (ps != NULL) {
360		p = ps + 4;	/* past ufs: */
361		DPRINTF("Found it at 0x%x\n", (int)ps);
362		p[0] = 'a'; p[1] = 'd'; p[2] = '0';	/* ad0 */
363		p += 3;
364		if (bs > 0) {
365			/* append slice */
366			*p++ = 's';
367			*p++ = bs + '0';
368		}
369		if (disk_layout != DL_SLICE) {
370			/* append partition */
371			*p++ = bp + 'a';
372		}
373		*p = '\0';
374	} else {
375		ps = memmem(addr, klen, op2, sizeof(op2) - 1);
376		if (ps != NULL) {
377			p = ps + sizeof(op2) - 1;
378			DPRINTF("Found it at 0x%x\n", (int)ps);
379			if (*p == 's') {
380				/* fix slice */
381				p++;
382				*p++ = bs + '0';
383			}
384			if (*p == 'a')
385				*p = bp + 'a';
386		}
387	}
388	if (ps == NULL) {
389		printf("Could not locate \"%s\" to fix kernel boot device, "
390		     "check ROOTDEVNAME is set\n", op);
391		return;
392	}
393	DPRINTF("Changed boot device to %s\n", ps);
394}
395#endif
396