]> CyberLeo.Net >> Repos - FreeBSD/releng/10.3.git/blob - sys/boot/arm/ixp425/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 / ixp425 / boot2 / boot2.c
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