]> CyberLeo.Net >> Repos - FreeBSD/releng/10.3.git/blob - sys/boot/arm/at91/boot2/boot2.c
- Copy stable/10@296371 to releng/10.3 in preparation for 10.3-RC1
[FreeBSD/releng/10.3.git] / sys / boot / arm / at91 / boot2 / boot2.c
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$");
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
39 extern uint32_t _end;
40
41 #define NOPT            6
42
43 static const char optstr[NOPT] = "agnrsv";
44 static 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
53 unsigned board_id; /* board type to pass to kernel, if set by board_* code */
54 unsigned dsk_start;
55 static char cmd[512];
56 static char kname[1024];
57 static uint32_t opts;
58 static uint8_t dsk_meta;
59
60 static void load(void);
61 static int parse(void);
62 static int dskread(void *, unsigned, unsigned);
63 #ifdef FIXUP_BOOT_DRV
64 static 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
76 static inline int
77 xfsread(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
84 static inline void
85 getstr(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
116 int
117 main(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
159 static void
160 load(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
211 static int
212 parse()
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
245 static int
246 dskread(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  */
309 static void
310 fixup_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