boot2.c revision 295453
1/*-
2 * Copyright (c) 2008 John Hay
3 * Copyright (c) 2006 M Warner Losh <imp@freebsd.org>
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: stable/10/sys/boot/arm/at91/boot2/boot2.c 295453 2016-02-09 22:32:24Z emaste $");
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#include "paths.h"
34#include "rbx.h"
35
36#undef PATH_KERNEL
37#define PATH_KERNEL	"/boot/kernel/kernel.gz.tramp"
38
39extern uint32_t _end;
40
41#define NOPT		6
42
43static const char optstr[NOPT] = "agnrsv";
44static const unsigned char flags[NOPT] = {
45	RBX_ASKNAME,
46	RBX_GDB,
47	RBX_NOINTR,
48	RBX_DFLTROOT,
49	RBX_SINGLE,
50	RBX_VERBOSE
51};
52
53unsigned board_id; /* board type to pass to kernel, if set by board_* code */
54unsigned dsk_start;
55static char cmd[512];
56static char kname[1024];
57static uint32_t opts;
58static uint8_t dsk_meta;
59
60static void load(void);
61static int parse(void);
62static int dskread(void *, unsigned, unsigned);
63#ifdef FIXUP_BOOT_DRV
64static void fixup_boot_drv(caddr_t, int, int, int);
65#endif
66
67#define	UFS_SMALL_CGBASE
68#include "ufsread.c"
69
70#ifdef DEBUG
71#define	DPRINTF(fmt, ...) printf(fmt, __VA_ARGS__)
72#else
73#define	DPRINTF(fmt, ...)
74#endif
75
76static inline int
77xfsread(ufs_ino_t inode, void *buf, size_t nbyte)
78{
79	if ((size_t)fsread(inode, buf, nbyte) != nbyte)
80		return -1;
81	return 0;
82}
83
84static inline void
85getstr(int c)
86{
87	char *s;
88
89	s = cmd;
90	if (c == 0)
91		c = getc(10000);
92	for (;;) {
93		switch (c) {
94		case 0:
95			break;
96		case '\177':
97		case '\b':
98			if (s > cmd) {
99				s--;
100				printf("\b \b");
101			}
102			break;
103		case '\n':
104		case '\r':
105			*s = 0;
106			return;
107		default:
108			if (s - cmd < sizeof(cmd) - 1)
109				*s++ = c;
110			xputchar(c);
111		}
112		c = getc(10000);
113	}
114}
115
116int
117main(void)
118{
119	int autoboot, c = 0;
120	ufs_ino_t ino;
121
122	dmadat = (void *)(0x20000000 + (16 << 20));
123	board_init();
124
125	autoboot = 1;
126
127	/* Process configuration file */
128	if ((ino = lookup(PATH_CONFIG)) ||
129	    (ino = lookup(PATH_DOTCONFIG)))
130		fsread(ino, cmd, sizeof(cmd));
131
132	if (*cmd) {
133		if (parse())
134			autoboot = 0;
135		printf("%s: %s\n", PATH_CONFIG, cmd);
136		/* Do not process this command twice */
137		*cmd = 0;
138	}
139
140	if (*kname == '\0')
141		strcpy(kname, PATH_KERNEL);
142
143	/* Present the user with the boot2 prompt. */
144	for (;;) {
145		printf("\nDefault: %s\nboot: ", kname);
146		if (!autoboot ||
147		    (OPT_CHECK(RBX_NOINTR) == 0 && (c = getc(2)) != 0))
148			getstr(c);
149		xputchar('\n');
150		autoboot = 0;
151		c = 0;
152		if (parse())
153			xputchar('\a');
154		else
155			load();
156	}
157}
158
159static void
160load(void)
161{
162	Elf32_Ehdr eh;
163	static Elf32_Phdr ep[2];
164	caddr_t p;
165	ufs_ino_t ino;
166	uint32_t addr;
167	int i, j;
168#ifdef FIXUP_BOOT_DRV
169	caddr_t staddr;
170	int klen;
171
172	staddr = (caddr_t)0xffffffff;
173	klen = 0;
174#endif
175	if (!(ino = lookup(kname))) {
176		if (!ls)
177			printf("No %s\n", kname);
178		return;
179	}
180	if (xfsread(ino, &eh, sizeof(eh)))
181		return;
182	if (!IS_ELF(eh)) {
183		printf("Invalid %s\n", "format");
184		return;
185	}
186	fs_off = eh.e_phoff;
187	for (j = i = 0; i < eh.e_phnum && j < 2; i++) {
188		if (xfsread(ino, ep + j, sizeof(ep[0])))
189			return;
190		if (ep[j].p_type == PT_LOAD)
191			j++;
192	}
193	for (i = 0; i < 2; i++) {
194		p = (caddr_t)ep[i].p_paddr;
195		fs_off = ep[i].p_offset;
196#ifdef FIXUP_BOOT_DRV
197		if (staddr == (caddr_t)0xffffffff)
198			staddr = p;
199		klen += ep[i].p_filesz;
200#endif
201		if (xfsread(ino, p, ep[i].p_filesz))
202			return;
203	}
204	addr = eh.e_entry;
205#ifdef FIXUP_BOOT_DRV
206	fixup_boot_drv(staddr, klen, bootslice, bootpart);
207#endif
208	((void(*)(int, int, int, int))addr)(opts & RBX_MASK, board_id, 0, 0);
209}
210
211static int
212parse()
213{
214	char *arg = cmd;
215	char *ep, *p;
216	int c, i;
217
218	while ((c = *arg++)) {
219		if (c == ' ' || c == '\t' || c == '\n')
220			continue;
221		for (p = arg; *p && *p != '\n' && *p != ' ' && *p != '\t'; p++);
222		ep = p;
223		if (*p)
224			*p++ = 0;
225		if (c == '-') {
226			while ((c = *arg++)) {
227				for (i = 0; c != optstr[i]; i++)
228					if (i == NOPT - 1)
229						return -1;
230				opts ^= OPT_SET(flags[i]);
231			}
232		} else {
233			arg--;
234			if ((i = ep - arg)) {
235				if ((size_t)i >= sizeof(kname))
236					return -1;
237				memcpy(kname, arg, i + 1);
238			}
239		}
240		arg = p;
241	}
242	return 0;
243}
244
245static int
246dskread(void *buf, unsigned lba, unsigned nblk)
247{
248	struct dos_partition *dp;
249	struct disklabel *d;
250	char *sec;
251	int i;
252
253	if (!dsk_meta) {
254		sec = dmadat->secbuf;
255		dsk_start = 0;
256		if (drvread(sec, DOSBBSECTOR, 1))
257			return -1;
258		dp = (void *)(sec + DOSPARTOFF);
259		for (i = 0; i < NDOSPART; i++) {
260			if (dp[i].dp_typ == DOSPTYP_386BSD)
261				break;
262		}
263		if (i == NDOSPART)
264			return -1;
265		/*
266		 * Although dp_start is aligned within the disk
267		 * partition structure, DOSPARTOFF is 446, which is
268		 * only word (2) aligned, not longword (4) aligned.
269		 * Cope by using memcpy to fetch the start of this
270		 * partition.
271		 */
272		memcpy(&dsk_start, &dp[1].dp_start, 4);
273		if (drvread(sec, dsk_start + LABELSECTOR, 1))
274			return -1;
275		d = (void *)(sec + LABELOFFSET);
276		if (d->d_magic != DISKMAGIC || d->d_magic2 != DISKMAGIC) {
277			printf("Invalid %s\n", "label");
278			return -1;
279		}
280		if (!d->d_partitions[0].p_size) {
281			printf("Invalid %s\n", "partition");
282			return -1;
283		}
284		dsk_start += d->d_partitions[0].p_offset;
285		dsk_start -= d->d_partitions[RAW_PART].p_offset;
286		dsk_meta++;
287	}
288	return drvread(buf, dsk_start + lba, nblk);
289}
290
291#ifdef FIXUP_BOOT_DRV
292/*
293 * fixup_boot_drv() will try to find the ROOTDEVNAME spec in the kernel
294 * and change it to what was specified on the comandline or /boot.conf
295 * file or to what was encountered on the disk. It will try to handle 3
296 * different disk layouts, raw (dangerously dedicated), slice only and
297 * slice + partition. It will look for the following strings in the
298 * kernel, but if it is one of the first three, the string in the kernel
299 * must use the correct form to match the actual disk layout:
300 * - ufs:ad0a
301 * - ufs:ad0s1
302 * - ufs:ad0s1a
303 * - ufs:ROOTDEVNAME
304 * In the case of the first three strings, only the "a" at the end and
305 * the "1" after the "s" will be modified, if they exist. The string
306 * length will not be changed. In the case of the last string, the
307 * whole string will be built up and nul, '\0' terminated.
308 */
309static void
310fixup_boot_drv(caddr_t addr, int klen, int bs, int bp)
311{
312	const u_int8_t op[] = "ufs:ROOTDEVNAME";
313	const u_int8_t op2[] = "ufs:ad0";
314	u_int8_t *p, *ps;
315
316	DPRINTF("fixup_boot_drv: 0x%x, %d, slice %d, partition %d\n",
317	    (int)addr, klen, bs, bp);
318	if (bs > 4)
319		return;
320	if (bp > 7)
321		return;
322	ps = memmem(addr, klen, op, sizeof(op));
323	if (ps != NULL) {
324		p = ps + 4;	/* past ufs: */
325		DPRINTF("Found it at 0x%x\n", (int)ps);
326		p[0] = 'a'; p[1] = 'd'; p[2] = '0';	/* ad0 */
327		p += 3;
328		if (bs > 0) {
329			/* append slice */
330			*p++ = 's';
331			*p++ = bs + '0';
332		}
333		if (disk_layout != DL_SLICE) {
334			/* append partition */
335			*p++ = bp + 'a';
336		}
337		*p = '\0';
338	} else {
339		ps = memmem(addr, klen, op2, sizeof(op2) - 1);
340		if (ps != NULL) {
341			p = ps + sizeof(op2) - 1;
342			DPRINTF("Found it at 0x%x\n", (int)ps);
343			if (*p == 's') {
344				/* fix slice */
345				p++;
346				*p++ = bs + '0';
347			}
348			if (*p == 'a')
349				*p = bp + 'a';
350		}
351	}
352	if (ps == NULL) {
353		printf("Could not locate \"%s\" to fix kernel boot device, "
354		     "check ROOTDEVNAME is set\n", op);
355		return;
356	}
357	DPRINTF("Changed boot device to %s\n", ps);
358}
359#endif
360