]> CyberLeo.Net >> Repos - FreeBSD/releng/9.2.git/blob - sys/boot/arm/at91/boot2/boot2.c
- Copy stable/9 to releng/9.2 as part of the 9.2-RELEASE cycle.
[FreeBSD/releng/9.2.git] / sys / boot / arm / at91 / boot2 / boot2.c
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
72 extern 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
79 static const char optstr[NOPT] = "agnrsv";
80 static 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
89 unsigned dsk_start;
90 static char cmd[512];
91 static char kname[1024];
92 static uint32_t opts;
93 static int dsk_meta;
94
95 static void load(void);
96 static int parse(void);
97 static int xfsread(ino_t, void *, size_t);
98 static int dskread(void *, unsigned, unsigned);
99 #ifdef FIXUP_BOOT_DRV
100 static 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
112 static inline int
113 xfsread(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
120 static inline void
121 getstr(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
152 int
153 main(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
195 static void
196 load(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
247 static int
248 parse()
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
281 static int
282 dskread(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  */
345 static void
346 fixup_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