initial commit
[freebsd-arm:freebsd-arm.git] / 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_CONFIG     "/boot.config"
66 #define PATH_KERNEL     "/boot/kernel/kernel"
67
68 extern uint32_t _end;
69
70 #define NOPT            6
71
72 #define OPT_SET(opt)    (1 << (opt))
73 #define OPT_CHECK(opt)  ((opts) & OPT_SET(opt))
74
75 static const char optstr[NOPT] = "agnrsv";
76 static const unsigned char flags[NOPT] = {
77         RBX_ASKNAME,
78         RBX_GDB,
79         RBX_NOINTR,
80         RBX_DFLTROOT,
81         RBX_SINGLE,
82         RBX_VERBOSE
83 };
84
85 static unsigned dsk_start;
86 static char cmd[512];
87 static char kname[1024];
88 static uint32_t opts;
89 static int dsk_meta;
90 static int bootslice;
91 static int bootpart;
92 static int disk_layout;
93 #define DL_UNKNOWN      0
94 #define DL_RAW          1       /* Dangerously dedicated */
95 #define DL_SLICE        2       /* Use only slices (DOS partitions) */
96 #define DL_SLICEPART    3       /* Use slices and partitions */
97
98 static void load(void);
99 static int parse(void);
100 static int xfsread(ino_t, void *, size_t);
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(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         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                 fsread(ino, cmd, sizeof(cmd));
173
174         if (*cmd) {
175                 if (parse())
176                         autoboot = 0;
177                 printf("%s: %s\n", PATH_CONFIG, cmd);
178                 /* Do not process this command twice */
179                 *cmd = 0;
180         }
181
182         if (*kname == '\0')
183                 strcpy(kname, PATH_KERNEL);
184
185         /* Present the user with the boot2 prompt. */
186         for (;;) {
187                 printf("\nDefault: %s\nboot: ", kname);
188                 if (!autoboot ||
189                     (OPT_CHECK(RBX_NOINTR) == 0 && (c = getc(2)) != 0))
190                         getstr(c);
191                 xputchar('\n');
192                 autoboot = 0;
193                 c = 0;
194                 DPRINTF("cmd is '%s'\n", cmd);
195                 if (parse())
196                         xputchar('\a');
197                 else
198                         load();
199         }
200 }
201
202 static void
203 load(void)
204 {
205         Elf32_Ehdr eh;
206         static Elf32_Phdr ep[2];
207         caddr_t p;
208         ino_t ino;
209         uint32_t addr;
210         int i, j;
211 #ifdef FIXUP_BOOT_DRV
212         caddr_t staddr;
213         int klen;
214
215         staddr = (caddr_t)0xffffffff;
216         klen = 0;
217 #endif
218         if (!(ino = lookup(kname))) {
219                 if (!ls)
220                         printf("No %s\n", kname);
221                 return;
222         }
223         DPRINTF("Found %s\n", kname);
224         if (xfsread(ino, &eh, sizeof(eh)))
225                 return;
226         if (!IS_ELF(eh)) {
227                 printf("Invalid %s\n", "format");
228                 return;
229         }
230         fs_off = eh.e_phoff;
231         for (j = i = 0; i < eh.e_phnum && j < 2; i++) {
232                 if (xfsread(ino, ep + j, sizeof(ep[0])))
233                         return;
234                 if (ep[j].p_type == PT_LOAD)
235                         j++;
236         }
237         for (i = 0; i < 2; i++) {
238                 p = (caddr_t)(ep[i].p_paddr & 0x0fffffff);
239                 fs_off = ep[i].p_offset;
240 #ifdef FIXUP_BOOT_DRV
241                 if (staddr == (caddr_t)0xffffffff)
242                         staddr = p;
243                 klen += ep[i].p_filesz;
244 #endif
245                 if (xfsread(ino, p, ep[i].p_filesz))
246                         return;
247         }
248         addr = eh.e_entry & 0x0fffffff;
249         DPRINTF("Entry point %x for %s\n", addr, kname);
250         clr_board();
251 #ifdef FIXUP_BOOT_DRV
252         fixup_boot_drv(staddr, klen, bootslice, bootpart);
253 #endif
254         ((void(*)(int))addr)(RB_BOOTINFO /* XXX | (opts & RBX_MASK) */);
255 }
256
257 static int
258 parse()
259 {
260         char *arg = cmd;
261         char *ep, *p;
262         int c, i;
263
264         while ((c = *arg++)) {
265                 if (c == ' ' || c == '\t' || c == '\n')
266                         continue;
267                 for (p = arg; *p && *p != '\n' && *p != ' ' && *p != '\t'; p++);
268                 ep = p;
269                 if (*p)
270                         *p++ = 0;
271                 if (c == '-') {
272                         while ((c = *arg++)) {
273                                 for (i = 0; c != optstr[i]; i++)
274                                         if (i == NOPT - 1)
275                                                 return -1;
276                                 opts ^= OPT_SET(flags[i]);
277                         }
278                 } else {
279                         arg--;
280                         /* look for ad0s1a:... | ad0s1:... */
281                         if (strlen(arg) > 6 && arg[0] == 'a' &&
282                             arg[1] == 'd' && arg[3] == 's' &&
283                             (arg[5] == ':' || arg[6] == ':')) {
284                                 /* XXX Should also handle disk. */
285                                 bootslice = arg[4] - '0';
286                                 if (bootslice < 1 || bootslice > 4)
287                                         return (-1);
288                                 bootpart = 0;
289                                 if (arg[5] != ':')
290                                         bootpart = arg[5] - 'a';
291                                 if (bootpart < 0 || bootpart > 7)
292                                         return (-1);
293                                 dsk_meta = 0;
294                                 if (arg[5] == ':')
295                                         arg += 6;
296                                 else
297                                         arg += 7;
298                         /* look for ad0a:... */
299                         } else if (strlen(arg) > 4 && arg[0] == 'a' &&
300                             arg[1] == 'd' && arg[2] == '0' && arg[4] == ':') {
301                                 bootslice = 0;
302                                 bootpart = arg[3] - 'a';
303                                 if (bootpart < 0 || bootpart > 7)
304                                         return (-1);
305                                 dsk_meta = 0;
306                                 arg += 5;
307                         }
308                         if ((i = ep - arg)) {
309                                 if ((size_t)i >= sizeof(kname))
310                                         return -1;
311                                 memcpy(kname, arg, i + 1);
312                         }
313                 }
314                 arg = p;
315         }
316         return 0;
317 }
318
319 /*
320  * dskread() will try to handle the disk layouts that are typically
321  * encountered.
322  * - raw or "Dangerously Dedicated" mode. No real slice table, just the
323  *   default one that is included with bsdlabel -B. Typically this is
324  *   used with ROOTDEVNAME=\"ufs:ad0a\".
325  * - slice only. Only a slice table is installed with no bsd label or
326  *   bsd partition table. This is typically used with
327  *   ROOTDEVNAME=\"ufs:ad0s1\".
328  * - slice + bsd label + partition table. This is typically done with
329  *   with fdisk + bsdlabel and is used with ROOTDEVNAME=\"ufs:ad0s1a\".
330  */
331 static int
332 dskread(void *buf, unsigned lba, unsigned nblk)
333 {
334         struct dos_partition *dp;
335         struct disklabel *d;
336         char *sec;
337         int i;
338
339         if (!dsk_meta) {
340                 sec = dmadat->secbuf;
341                 dsk_start = 0;
342                 if (drvread(sec, DOSBBSECTOR, 1))
343                         return -1;
344                 dp = (void *)(sec + DOSPARTOFF);
345                 if (bootslice != 0) {
346                         i = bootslice - 1;
347                         if (dp[i].dp_typ != DOSPTYP_386BSD)
348                                 return -1;
349                 } else {
350                         for (i = 0; i < NDOSPART; i++) {
351                                 if ((dp[i].dp_typ == DOSPTYP_386BSD) &&
352                                     (dp[i].dp_flag == 0x80))
353                                         break;
354                         }
355                 }
356                 if (i != NDOSPART) {
357                         bootslice = i + 1;
358                         DPRINTF("Found an active fbsd slice. (%d)\n", i + 1);
359                         /*
360                          * Although dp_start is aligned within the disk
361                          * partition structure, DOSPARTOFF is 446, which
362                          * is only word (2) aligned, not longword (4)
363                          * aligned. Cope by using memcpy to fetch the
364                          * start of this partition.
365                          */
366                         memcpy(&dsk_start, &dp[i].dp_start, 4);
367                         dsk_start = swap32(dsk_start);
368                         DPRINTF("dsk_start %x\n", dsk_start);
369                         if ((bootslice == 4) && (dsk_start == 0)) {
370                                 disk_layout = DL_RAW;
371                                 bootslice = 0;
372                         }
373                 }
374                 if (drvread(sec, dsk_start + LABELSECTOR, 1))
375                         return -1;
376                 d = (void *)(sec + LABELOFFSET);
377                 if ((d->d_magic == DISKMAGIC && d->d_magic2 == DISKMAGIC) ||
378                     (swap32(d->d_magic) == DISKMAGIC &&
379                     swap32(d->d_magic2) == DISKMAGIC)) {
380                         DPRINTF("p_size = %x\n",
381                             !d->d_partitions[bootpart].p_size);
382                         if (!d->d_partitions[bootpart].p_size) {
383                                 printf("Invalid partition\n");
384                                 return -1;
385                         }
386                         DPRINTF("p_offset %x, RAW %x\n",
387                             swap32(d->d_partitions[bootpart].p_offset),
388                             swap32(d->d_partitions[RAW_PART].p_offset));
389                         dsk_start += swap32(d->d_partitions[bootpart].p_offset);
390                         dsk_start -= swap32(d->d_partitions[RAW_PART].p_offset);
391                         if ((disk_layout == DL_UNKNOWN) && (bootslice == 0))
392                                 disk_layout = DL_RAW;
393                         else if (disk_layout == DL_UNKNOWN)
394                                 disk_layout = DL_SLICEPART;
395                 } else {
396                         disk_layout = DL_SLICE;
397                         DPRINTF("Invalid %s\n", "label");
398                 }
399                 DPRINTF("bootslice %d, bootpart %d, dsk_start %u\n", bootslice,
400                     bootpart, dsk_start);
401                 dsk_meta++;
402         }
403         return drvread(buf, dsk_start + lba, nblk);
404 }
405
406 static int
407 drvread(void *buf, unsigned lba, unsigned nblk)
408 {
409         static unsigned c = 0x2d5c7c2f;
410
411         printf("%c\b", c = c << 8 | c >> 24);
412         return (avila_read((char *)buf, lba, nblk));
413 }
414
415 #ifdef FIXUP_BOOT_DRV
416 /*
417  * fixup_boot_drv() will try to find the ROOTDEVNAME spec in the kernel
418  * and change it to what was specified on the comandline or /boot.conf
419  * file or to what was encountered on the disk. It will try to handle 3
420  * different disk layouts, raw (dangerously dedicated), slice only and
421  * slice + partition. It will look for the following strings in the
422  * kernel, but if it is one of the first three, the string in the kernel
423  * must use the correct form to match the actual disk layout:
424  * - ufs:ad0a
425  * - ufs:ad0s1
426  * - ufs:ad0s1a
427  * - ufs:ROOTDEVNAME
428  * In the case of the first three strings, only the "a" at the end and
429  * the "1" after the "s" will be modified, if they exist. The string
430  * length will not be changed. In the case of the last string, the
431  * whole string will be built up and nul, '\0' terminated.
432  */
433 static void
434 fixup_boot_drv(caddr_t addr, int klen, int bs, int bp)
435 {
436         const u_int8_t op[] = "ufs:ROOTDEVNAME";
437         const u_int8_t op2[] = "ufs:ad0";
438         u_int8_t *p, *ps;
439
440         DPRINTF("fixup_boot_drv: 0x%x, %d, slice %d, partition %d\n",
441             (int)addr, klen, bs, bp);
442         if (bs > 4)
443                 return;
444         if (bp > 7)
445                 return;
446         ps = memmem(addr, klen, op, sizeof(op));
447         if (ps != NULL) {
448                 p = ps + 4;     /* past ufs: */
449                 DPRINTF("Found it at 0x%x\n", (int)ps);
450                 p[0] = 'a'; p[1] = 'd'; p[2] = '0';     /* ad0 */
451                 p += 3;
452                 if (bs > 0) {
453                         /* append slice */
454                         *p++ = 's';
455                         *p++ = bs + '0';
456                 }
457                 if (disk_layout != DL_SLICE) {
458                         /* append partition */
459                         *p++ = bp + 'a';
460                 }
461                 *p = '\0';
462         } else {
463                 ps = memmem(addr, klen, op2, sizeof(op2) - 1);
464                 if (ps != NULL) {
465                         p = ps + sizeof(op2) - 1;
466                         DPRINTF("Found it at 0x%x\n", (int)ps);
467                         if (*p == 's') {
468                                 /* fix slice */
469                                 p++;
470                                 *p++ = bs + '0';
471                         }
472                         if (*p == 'a')
473                                 *p = bp + 'a';
474                 }
475         }
476         if (ps == NULL) {
477                 printf("Could not locate \"%s\" to fix kernel boot device, "
478                      "check ROOTDEVNAME is set\n", op);
479                 return;
480         }
481         DPRINTF("Changed boot device to %s\n", ps);
482 }
483 #endif