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