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