xref: /titanic_51/usr/src/boot/sys/boot/arm/ixp425/boot2/boot2.c (revision 4a5d661a82b942b6538acd26209d959ce98b593a)
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$");
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 
34 extern uint32_t _end;
35 
36 #define NOPT		6
37 
38 static const char optstr[NOPT] = "agnrsv";
39 static 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 
48 static unsigned dsk_start;
49 static char cmd[512];
50 static char kname[1024];
51 static uint32_t opts;
52 static uint8_t dsk_meta;
53 static int bootslice;
54 static int bootpart;
55 static 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 
61 static void load(void);
62 static int parse(void);
63 static int dskread(void *, unsigned, unsigned);
64 static int drvread(void *, unsigned, unsigned);
65 #ifdef FIXUP_BOOT_DRV
66 static 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 
77 static inline int
78 xfsread(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 
85 static inline void
86 getstr(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 
117 int
118 main(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 
165 static void
166 load(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 
220 static int
221 parse()
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  */
294 static int
295 dskread(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 
369 static int
370 drvread(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  */
396 static void
397 fixup_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