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