]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - sys/boot/arm/ixp425/boot2/boot2.c
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.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
32 #define RBX_ASKNAME     0x0     /* -a */
33 #define RBX_SINGLE      0x1     /* -s */
34 /* 0x2 is reserved for log2(RB_NOSYNC). */
35 /* 0x3 is reserved for log2(RB_HALT). */
36 /* 0x4 is reserved for log2(RB_INITNAME). */
37 #define RBX_DFLTROOT    0x5     /* -r */
38 /* #define RBX_KDB      0x6        -d */
39 /* 0x7 is reserved for log2(RB_RDONLY). */
40 /* 0x8 is reserved for log2(RB_DUMP). */
41 /* 0x9 is reserved for log2(RB_MINIROOT). */
42 #define RBX_CONFIG      0xa     /* -c */
43 #define RBX_VERBOSE     0xb     /* -v */
44 /* #define RBX_SERIAL   0xc        -h */
45 /* #define RBX_CDROM    0xd        -C */
46 /* 0xe is reserved for log2(RB_POWEROFF). */
47 #define RBX_GDB         0xf     /* -g */
48 /* #define RBX_MUTE     0x10       -m */
49 /* 0x11 is reserved for log2(RB_SELFTEST). */
50 /* 0x12 is reserved for boot programs. */
51 /* 0x13 is reserved for boot programs. */
52 /* #define RBX_PAUSE    0x14       -p */
53 /* #define RBX_QUIET    0x15       -q */
54 #define RBX_NOINTR      0x1c    /* -n */
55 /* 0x1d is reserved for log2(RB_MULTIPLE) and is just misnamed here. */
56 /* #define RBX_DUAL     0x1d       -D */
57 /* 0x1f is reserved for log2(RB_BOOTINFO). */
58
59 /* pass: -a, -s, -r, -v, -g */
60 #define RBX_MASK        (OPT_SET(RBX_ASKNAME) | OPT_SET(RBX_SINGLE) | \
61                         OPT_SET(RBX_DFLTROOT) | \
62                         OPT_SET(RBX_VERBOSE) | \
63                         OPT_SET(RBX_GDB))
64
65 #define PATH_DOTCONFIG  "/boot.config"
66 #define PATH_CONFIG     "/boot/config"
67 #define PATH_KERNEL     "/boot/kernel/kernel"
68
69 extern uint32_t _end;
70
71 #define NOPT            6
72
73 #define OPT_SET(opt)    (1 << (opt))
74 #define OPT_CHECK(opt)  ((opts) & OPT_SET(opt))
75
76 static const char optstr[NOPT] = "agnrsv";
77 static const unsigned char flags[NOPT] = {
78         RBX_ASKNAME,
79         RBX_GDB,
80         RBX_NOINTR,
81         RBX_DFLTROOT,
82         RBX_SINGLE,
83         RBX_VERBOSE
84 };
85
86 static unsigned dsk_start;
87 static char cmd[512];
88 static char kname[1024];
89 static uint32_t opts;
90 static uint8_t dsk_meta;
91 static int bootslice;
92 static int bootpart;
93 static int disk_layout;
94 #define DL_UNKNOWN      0
95 #define DL_RAW          1       /* Dangerously dedicated */
96 #define DL_SLICE        2       /* Use only slices (DOS partitions) */
97 #define DL_SLICEPART    3       /* Use slices and partitions */
98
99 static void load(void);
100 static int parse(void);
101 static int dskread(void *, unsigned, unsigned);
102 static int drvread(void *, unsigned, unsigned);
103 #ifdef FIXUP_BOOT_DRV
104 static void fixup_boot_drv(caddr_t, int, int, int);
105 #endif
106
107 #include "ufsread.c"
108
109 #ifdef DEBUG
110 #define DPRINTF(fmt, ...) printf(fmt, __VA_ARGS__)
111 #else
112 #define DPRINTF(fmt, ...)
113 #endif
114
115 static inline int
116 xfsread(ufs_ino_t inode, void *buf, size_t nbyte)
117 {
118         if ((size_t)fsread(inode, buf, nbyte) != nbyte)
119                 return -1;
120         return 0;
121 }
122
123 static inline void
124 getstr(int c)
125 {
126         char *s;
127
128         s = cmd;
129         if (c == 0)
130                 c = getc(10000);
131         for (;;) {
132                 switch (c) {
133                 case 0:
134                         break;
135                 case '\177':
136                 case '\b':
137                         if (s > cmd) {
138                                 s--;
139                                 printf("\b \b");
140                         }
141                         break;
142                 case '\n':
143                 case '\r':
144                         *s = 0;
145                         return;
146                 default:
147                         if (s - cmd < sizeof(cmd) - 1)
148                                 *s++ = c;
149                         xputchar(c);
150                 }
151                 c = getc(10000);
152         }
153 }
154
155 int
156 main(void)
157 {
158         const char *bt;
159         int autoboot, c = 0;
160         ufs_ino_t ino;
161
162         dmadat = (void *)(0x1c0000);
163         p_memset((char *)dmadat, 0, 32 * 1024);
164         bt = board_init();
165
166         printf("FreeBSD ARM (%s) boot2 v%d.%d\n", bt, 0, 4);
167
168         autoboot = 1;
169
170         /* Process configuration file */
171         if ((ino = lookup(PATH_CONFIG)) ||
172             (ino = lookup(PATH_DOTCONFIG)))
173                 fsread(ino, cmd, sizeof(cmd));
174
175         if (*cmd) {
176                 if (parse())
177                         autoboot = 0;
178                 printf("%s: %s\n", PATH_CONFIG, cmd);
179                 /* Do not process this command twice */
180                 *cmd = 0;
181         }
182
183         if (*kname == '\0')
184                 strcpy(kname, PATH_KERNEL);
185
186         /* Present the user with the boot2 prompt. */
187         for (;;) {
188                 printf("\nDefault: %s\nboot: ", kname);
189                 if (!autoboot ||
190                     (OPT_CHECK(RBX_NOINTR) == 0 && (c = getc(2)) != 0))
191                         getstr(c);
192                 xputchar('\n');
193                 autoboot = 0;
194                 c = 0;
195                 DPRINTF("cmd is '%s'\n", cmd);
196                 if (parse())
197                         xputchar('\a');
198                 else
199                         load();
200         }
201 }
202
203 static void
204 load(void)
205 {
206         Elf32_Ehdr eh;
207         static Elf32_Phdr ep[2];
208         caddr_t p;
209         ufs_ino_t ino;
210         uint32_t addr;
211         int i, j;
212 #ifdef FIXUP_BOOT_DRV
213         caddr_t staddr;
214         int klen;
215
216         staddr = (caddr_t)0xffffffff;
217         klen = 0;
218 #endif
219         if (!(ino = lookup(kname))) {
220                 if (!ls)
221                         printf("No %s\n", kname);
222                 return;
223         }
224         DPRINTF("Found %s\n", kname);
225         if (xfsread(ino, &eh, sizeof(eh)))
226                 return;
227         if (!IS_ELF(eh)) {
228                 printf("Invalid %s\n", "format");
229                 return;
230         }
231         fs_off = eh.e_phoff;
232         for (j = i = 0; i < eh.e_phnum && j < 2; i++) {
233                 if (xfsread(ino, ep + j, sizeof(ep[0])))
234                         return;
235                 if (ep[j].p_type == PT_LOAD)
236                         j++;
237         }
238         for (i = 0; i < 2; i++) {
239                 p = (caddr_t)(ep[i].p_paddr & 0x0fffffff);
240                 fs_off = ep[i].p_offset;
241 #ifdef FIXUP_BOOT_DRV
242                 if (staddr == (caddr_t)0xffffffff)
243                         staddr = p;
244                 klen += ep[i].p_filesz;
245 #endif
246                 if (xfsread(ino, p, ep[i].p_filesz))
247                         return;
248         }
249         addr = eh.e_entry & 0x0fffffff;
250         DPRINTF("Entry point %x for %s\n", addr, kname);
251         clr_board();
252 #ifdef FIXUP_BOOT_DRV
253         fixup_boot_drv(staddr, klen, bootslice, bootpart);
254 #endif
255         ((void(*)(int))addr)(RB_BOOTINFO /* XXX | (opts & RBX_MASK) */);
256 }
257
258 static int
259 parse()
260 {
261         char *arg = cmd;
262         char *ep, *p;
263         int c, i;
264
265         while ((c = *arg++)) {
266                 if (c == ' ' || c == '\t' || c == '\n')
267                         continue;
268                 for (p = arg; *p && *p != '\n' && *p != ' ' && *p != '\t'; p++);
269                 ep = p;
270                 if (*p)
271                         *p++ = 0;
272                 if (c == '-') {
273                         while ((c = *arg++)) {
274                                 for (i = 0; c != optstr[i]; i++)
275                                         if (i == NOPT - 1)
276                                                 return -1;
277                                 opts ^= OPT_SET(flags[i]);
278                         }
279                 } else {
280                         arg--;
281                         /* look for ad0s1a:... | ad0s1:... */
282                         if (strlen(arg) > 6 && arg[0] == 'a' &&
283                             arg[1] == 'd' && arg[3] == 's' &&
284                             (arg[5] == ':' || arg[6] == ':')) {
285                                 /* XXX Should also handle disk. */
286                                 bootslice = arg[4] - '0';
287                                 if (bootslice < 1 || bootslice > 4)
288                                         return (-1);
289                                 bootpart = 0;
290                                 if (arg[5] != ':')
291                                         bootpart = arg[5] - 'a';
292                                 if (bootpart < 0 || bootpart > 7)
293                                         return (-1);
294                                 dsk_meta = 0;
295                                 if (arg[5] == ':')
296                                         arg += 6;
297                                 else
298                                         arg += 7;
299                         /* look for ad0a:... */
300                         } else if (strlen(arg) > 4 && arg[0] == 'a' &&
301                             arg[1] == 'd' && arg[2] == '0' && arg[4] == ':') {
302                                 bootslice = 0;
303                                 bootpart = arg[3] - 'a';
304                                 if (bootpart < 0 || bootpart > 7)
305                                         return (-1);
306                                 dsk_meta = 0;
307                                 arg += 5;
308                         }
309                         if ((i = ep - arg)) {
310                                 if ((size_t)i >= sizeof(kname))
311                                         return -1;
312                                 memcpy(kname, arg, i + 1);
313                         }
314                 }
315                 arg = p;
316         }
317         return 0;
318 }
319
320 /*
321  * dskread() will try to handle the disk layouts that are typically
322  * encountered.
323  * - raw or "Dangerously Dedicated" mode. No real slice table, just the
324  *   default one that is included with bsdlabel -B. Typically this is
325  *   used with ROOTDEVNAME=\"ufs:ad0a\".
326  * - slice only. Only a slice table is installed with no bsd label or
327  *   bsd partition table. This is typically used with
328  *   ROOTDEVNAME=\"ufs:ad0s1\".
329  * - slice + bsd label + partition table. This is typically done with
330  *   with fdisk + bsdlabel and is used with ROOTDEVNAME=\"ufs:ad0s1a\".
331  */
332 static int
333 dskread(void *buf, unsigned lba, unsigned nblk)
334 {
335         struct dos_partition *dp;
336         struct disklabel *d;
337         char *sec;
338         int i;
339
340         if (!dsk_meta) {
341                 sec = dmadat->secbuf;
342                 dsk_start = 0;
343                 if (drvread(sec, DOSBBSECTOR, 1))
344                         return -1;
345                 dp = (void *)(sec + DOSPARTOFF);
346                 if (bootslice != 0) {
347                         i = bootslice - 1;
348                         if (dp[i].dp_typ != DOSPTYP_386BSD)
349                                 return -1;
350                 } else {
351                         for (i = 0; i < NDOSPART; i++) {
352                                 if ((dp[i].dp_typ == DOSPTYP_386BSD) &&
353                                     (dp[i].dp_flag == 0x80))
354                                         break;
355                         }
356                 }
357                 if (i != NDOSPART) {
358                         bootslice = i + 1;
359                         DPRINTF("Found an active fbsd slice. (%d)\n", i + 1);
360                         /*
361                          * Although dp_start is aligned within the disk
362                          * partition structure, DOSPARTOFF is 446, which
363                          * is only word (2) aligned, not longword (4)
364                          * aligned. Cope by using memcpy to fetch the
365                          * start of this partition.
366                          */
367                         memcpy(&dsk_start, &dp[i].dp_start, 4);
368                         dsk_start = swap32(dsk_start);
369                         DPRINTF("dsk_start %x\n", dsk_start);
370                         if ((bootslice == 4) && (dsk_start == 0)) {
371                                 disk_layout = DL_RAW;
372                                 bootslice = 0;
373                         }
374                 }
375                 if (drvread(sec, dsk_start + LABELSECTOR, 1))
376                         return -1;
377                 d = (void *)(sec + LABELOFFSET);
378                 if ((d->d_magic == DISKMAGIC && d->d_magic2 == DISKMAGIC) ||
379                     (swap32(d->d_magic) == DISKMAGIC &&
380                     swap32(d->d_magic2) == DISKMAGIC)) {
381                         DPRINTF("p_size = %x\n",
382                             !d->d_partitions[bootpart].p_size);
383                         if (!d->d_partitions[bootpart].p_size) {
384                                 printf("Invalid partition\n");
385                                 return -1;
386                         }
387                         DPRINTF("p_offset %x, RAW %x\n",
388                             swap32(d->d_partitions[bootpart].p_offset),
389                             swap32(d->d_partitions[RAW_PART].p_offset));
390                         dsk_start += swap32(d->d_partitions[bootpart].p_offset);
391                         dsk_start -= swap32(d->d_partitions[RAW_PART].p_offset);
392                         if ((disk_layout == DL_UNKNOWN) && (bootslice == 0))
393                                 disk_layout = DL_RAW;
394                         else if (disk_layout == DL_UNKNOWN)
395                                 disk_layout = DL_SLICEPART;
396                 } else {
397                         disk_layout = DL_SLICE;
398                         DPRINTF("Invalid %s\n", "label");
399                 }
400                 DPRINTF("bootslice %d, bootpart %d, dsk_start %u\n", bootslice,
401                     bootpart, dsk_start);
402                 dsk_meta++;
403         }
404         return drvread(buf, dsk_start + lba, nblk);
405 }
406
407 static int
408 drvread(void *buf, unsigned lba, unsigned nblk)
409 {
410         static unsigned c = 0x2d5c7c2f;
411
412         printf("%c\b", c = c << 8 | c >> 24);
413         return (avila_read((char *)buf, lba, nblk));
414 }
415
416 #ifdef FIXUP_BOOT_DRV
417 /*
418  * fixup_boot_drv() will try to find the ROOTDEVNAME spec in the kernel
419  * and change it to what was specified on the comandline or /boot.conf
420  * file or to what was encountered on the disk. It will try to handle 3
421  * different disk layouts, raw (dangerously dedicated), slice only and
422  * slice + partition. It will look for the following strings in the
423  * kernel, but if it is one of the first three, the string in the kernel
424  * must use the correct form to match the actual disk layout:
425  * - ufs:ad0a
426  * - ufs:ad0s1
427  * - ufs:ad0s1a
428  * - ufs:ROOTDEVNAME
429  * In the case of the first three strings, only the "a" at the end and
430  * the "1" after the "s" will be modified, if they exist. The string
431  * length will not be changed. In the case of the last string, the
432  * whole string will be built up and nul, '\0' terminated.
433  */
434 static void
435 fixup_boot_drv(caddr_t addr, int klen, int bs, int bp)
436 {
437         const u_int8_t op[] = "ufs:ROOTDEVNAME";
438         const u_int8_t op2[] = "ufs:ad0";
439         u_int8_t *p, *ps;
440
441         DPRINTF("fixup_boot_drv: 0x%x, %d, slice %d, partition %d\n",
442             (int)addr, klen, bs, bp);
443         if (bs > 4)
444                 return;
445         if (bp > 7)
446                 return;
447         ps = memmem(addr, klen, op, sizeof(op));
448         if (ps != NULL) {
449                 p = ps + 4;     /* past ufs: */
450                 DPRINTF("Found it at 0x%x\n", (int)ps);
451                 p[0] = 'a'; p[1] = 'd'; p[2] = '0';     /* ad0 */
452                 p += 3;
453                 if (bs > 0) {
454                         /* append slice */
455                         *p++ = 's';
456                         *p++ = bs + '0';
457                 }
458                 if (disk_layout != DL_SLICE) {
459                         /* append partition */
460                         *p++ = bp + 'a';
461                 }
462                 *p = '\0';
463         } else {
464                 ps = memmem(addr, klen, op2, sizeof(op2) - 1);
465                 if (ps != NULL) {
466                         p = ps + sizeof(op2) - 1;
467                         DPRINTF("Found it at 0x%x\n", (int)ps);
468                         if (*p == 's') {
469                                 /* fix slice */
470                                 p++;
471                                 *p++ = bs + '0';
472                         }
473                         if (*p == 'a')
474                                 *p = bp + 'a';
475                 }
476         }
477         if (ps == NULL) {
478                 printf("Could not locate \"%s\" to fix kernel boot device, "
479                      "check ROOTDEVNAME is set\n", op);
480                 return;
481         }
482         DPRINTF("Changed boot device to %s\n", ps);
483 }
484 #endif