Merge from vendor branch GCC:
[dragonfly.git] / sys / boot / i386 / boot2 / boot2.c
1 /*-
2  * Copyright (c) 1998 Robert Nordier
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms are freely
6  * permitted provided that the above copyright notice and this
7  * paragraph and the following disclaimer are duplicated in all
8  * such forms.
9  *
10  * This software is provided "AS IS" and without any express or
11  * implied warranties, including, without limitation, the implied
12  * warranties of merchantability and fitness for a particular
13  * purpose.
14  *
15  * $FreeBSD: src/sys/boot/i386/boot2/boot2.c,v 1.64 2003/08/25 23:28:31 obrien Exp $
16  * $DragonFly: src/sys/boot/i386/boot2/Attic/boot2.c,v 1.7 2004/03/04 00:06:58 dillon Exp $
17  */
18 #include <sys/param.h>
19 #include <sys/disklabel.h>
20 #include <sys/diskslice.h>
21 #include <sys/diskmbr.h>
22 #include <sys/dirent.h>
23 #include <machine/bootinfo.h>
24 #include <machine/elf.h>
25
26 #include <stdarg.h>
27
28 #include <a.out.h>
29
30 #include <btxv86.h>
31
32 #include "boot2.h"
33 #include "lib.h"
34
35 #define IO_KEYBOARD     1
36 #define IO_SERIAL       2
37
38 #define SECOND          18      /* Circa that many ticks in a second. */
39
40 #define RBX_ASKNAME     0x0     /* -a */
41 #define RBX_SINGLE      0x1     /* -s */
42 #define RBX_DFLTROOT    0x5     /* -r */
43 #define RBX_KDB         0x6     /* -d */
44 #define RBX_CONFIG      0xa     /* -c */
45 #define RBX_VERBOSE     0xb     /* -v */
46 #define RBX_SERIAL      0xc     /* -h */
47 #define RBX_CDROM       0xd     /* -C */
48 #define RBX_GDB         0xf     /* -g */
49 #define RBX_MUTE        0x10    /* -m */
50 #define RBX_PAUSE       0x12    /* -p */
51 #define RBX_NOINTR      0x1c    /* -n */
52 #define RBX_DUAL        0x1d    /* -D */
53 #define RBX_PROBEKBD    0x1e    /* -P */
54 /* 0x1f is reserved for the historical RB_BOOTINFO option */
55
56 /* pass: -a, -s, -r, -d, -c, -v, -h, -C, -g, -m, -p, -D */
57 #define RBX_MASK        0x2005ffff
58
59 #define PATH_CONFIG     "/boot.config"
60 #define PATH_BOOT3      "/boot/loader"
61 #define PATH_KERNEL     "/kernel"
62
63 #define ARGS            0x900
64 #define NOPT            12
65 #define NDEV            3
66 #define MEM_BASE        0x12
67 #define MEM_EXT         0x15
68 #define V86_CY(x)       ((x) & 1)
69 #define V86_ZR(x)       ((x) & 0x40)
70
71 #define DRV_HARD        0x80
72 #define DRV_MASK        0x7f
73
74 #define TYPE_AD         0
75 #define TYPE_DA         1
76 #define TYPE_MAXHARD    TYPE_DA
77 #define TYPE_FD         2
78
79 extern uint32_t _end;
80
81 static const char optstr[NOPT] = "DhaCgmnPprsv";
82 static const unsigned char flags[NOPT] = {
83     RBX_DUAL,
84     RBX_SERIAL,
85     RBX_ASKNAME,
86     RBX_CDROM,
87     RBX_GDB,
88     RBX_MUTE,
89     RBX_NOINTR,
90     RBX_PROBEKBD,
91     RBX_PAUSE,
92     RBX_DFLTROOT,
93     RBX_SINGLE,
94     RBX_VERBOSE
95 };
96
97 static const char *const dev_nm[NDEV] = {"ad", "da", "fd"};
98 static const unsigned char dev_maj[NDEV] = {30, 4, 2};
99
100 static struct dsk {
101     unsigned drive;
102     unsigned type;
103     unsigned unit;
104     unsigned slice;
105     unsigned part;
106     unsigned start;
107     int init;
108 } dsk;
109 static char cmd[512];
110 static char kname[1024];
111 static uint32_t opts;
112 static struct bootinfo bootinfo;
113 static uint8_t ioctrl = IO_KEYBOARD;
114
115 void exit(int);
116 static void load(void);
117 static int parse(void);
118 static int xfsread(ino_t, void *, size_t);
119 static int dskread(void *, unsigned, unsigned);
120 static void printf(const char *,...);
121 static void putchar(int);
122 static uint32_t memsize(void);
123 static int drvread(void *, unsigned, unsigned);
124 static int keyhit(unsigned);
125 static int xputc(int);
126 static int xgetc(int);
127 static int getc(int);
128
129 static void 
130 memcpy(void *d, const void *s, int len)
131 {
132     char *dd = d;
133     const char *ss = s;
134
135 #if 0
136     if (dd < ss) {
137         while (--len >= 0)
138             *dd++ = *ss++;
139     } else {
140 #endif
141         while (--len >= 0)
142             dd[len] = ss[len];
143 #if 0
144     }
145 #endif
146 }
147
148 static inline int
149 strcmp(const char *s1, const char *s2)
150 {
151     for (; *s1 == *s2 && *s1; s1++, s2++);
152     return (unsigned char)*s1 - (unsigned char)*s2;
153 }
154
155 #include "ufsread.c"
156
157 static int
158 xfsread(ino_t inode, void *buf, size_t nbyte)
159 {
160     if ((size_t)fsread(inode, buf, nbyte) != nbyte) {
161         printf("Invalid %s\n", "format");
162         return -1;
163     }
164     return 0;
165 }
166
167 static inline uint32_t
168 memsize(void)
169 {
170     v86.addr = MEM_EXT;
171     v86.eax = 0x8800;
172     v86int();
173     return v86.eax;
174 }
175
176 static inline void
177 getstr(void)
178 {
179     char *s;
180     int c;
181
182     s = cmd;
183     for (;;) {
184         switch (c = xgetc(0)) {
185         case 0:
186             break;
187         case '\177':
188         case '\b':
189             if (s > cmd) {
190                 s--;
191                 printf("\b \b");
192             }
193             break;
194         case '\n':
195         case '\r':
196             *s = 0;
197             return;
198         default:
199             if (s - cmd < sizeof(cmd) - 1)
200                 *s++ = c;
201             putchar(c);
202         }
203     }
204 }
205
206 static inline void
207 putc(int c)
208 {
209     v86.addr = 0x10;
210     v86.eax = 0xe00 | (c & 0xff);
211     v86.ebx = 0x7;
212     v86int();
213 }
214
215 int
216 main(void)
217 {
218     int autoboot;
219     ino_t ino;
220
221     dmadat = (void *)(roundup2(__base + (int32_t)&_end, 0x10000) - __base);
222     v86.ctl = V86_FLAGS;
223     dsk.drive = *(uint8_t *)PTOV(ARGS);
224     dsk.type = dsk.drive & DRV_HARD ? TYPE_AD : TYPE_FD;
225     dsk.unit = dsk.drive & DRV_MASK;
226     dsk.slice = *(uint8_t *)PTOV(ARGS + 1) + 1;
227     bootinfo.bi_version = BOOTINFO_VERSION;
228     bootinfo.bi_size = sizeof(bootinfo);
229     bootinfo.bi_basemem = 0;    /* XXX will be filled by loader or kernel */
230     bootinfo.bi_extmem = memsize();
231     bootinfo.bi_memsizes_valid++;
232
233     /* Process configuration file */
234
235     autoboot = 1;
236
237     if ((ino = lookup(PATH_CONFIG)))
238         fsread(ino, cmd, sizeof(cmd));
239
240     if (*cmd) {
241         printf("%s: %s", PATH_CONFIG, cmd);
242         if (parse())
243             autoboot = 0;
244         /* Do not process this command twice */
245         *cmd = 0;
246     }
247
248     /*
249      * Try to exec stage 3 boot loader. If interrupted by a keypress,
250      * or in case of failure, try to load a kernel directly instead.
251      */
252
253     if (autoboot && !*kname) {
254         memcpy(kname, PATH_BOOT3, sizeof(PATH_BOOT3));
255         if (!keyhit(3*SECOND)) {
256             load();
257             memcpy(kname, PATH_KERNEL, sizeof(PATH_KERNEL));
258         }
259     }
260
261     /* Present the user with the boot2 prompt. */
262
263     for (;;) {
264         printf("\nDragonFly/i386 boot\n"
265                "Default: %u:%s(%u,%c)%s\n"
266                "boot: ",
267                dsk.drive & DRV_MASK, dev_nm[dsk.type], dsk.unit,
268                'a' + dsk.part, kname);
269         if (ioctrl & IO_SERIAL)
270             sio_flush();
271         if (!autoboot || keyhit(5*SECOND))
272             getstr();
273         else
274             putchar('\n');
275         autoboot = 0;
276         if (parse())
277             putchar('\a');
278         else
279             load();
280     }
281 }
282
283 /* XXX - Needed for btxld to link the boot2 binary; do not remove. */
284 void
285 exit(int x)
286 {
287 }
288
289 static void
290 load(void)
291 {
292     union {
293         struct exec ex;
294         Elf32_Ehdr eh;
295     } hdr;
296     Elf32_Phdr ep[2];
297     Elf32_Shdr es[2];
298     caddr_t p;
299     ino_t ino;
300     uint32_t addr, x;
301     int fmt, i, j;
302
303     if (!(ino = lookup(kname))) {
304         if (!ls)
305             printf("No %s\n", kname);
306         return;
307     }
308     if (xfsread(ino, &hdr, sizeof(hdr)))
309         return;
310     if (N_GETMAGIC(hdr.ex) == ZMAGIC)
311         fmt = 0;
312     else if (IS_ELF(hdr.eh))
313         fmt = 1;
314     else {
315         printf("Invalid %s\n", "format");
316         return;
317     }
318     if (fmt == 0) {
319         addr = hdr.ex.a_entry & 0xffffff;
320         p = PTOV(addr);
321         fs_off = PAGE_SIZE;
322         if (xfsread(ino, p, hdr.ex.a_text))
323             return;
324         p += roundup2(hdr.ex.a_text, PAGE_SIZE);
325         if (xfsread(ino, p, hdr.ex.a_data))
326             return;
327         p += hdr.ex.a_data + roundup2(hdr.ex.a_bss, PAGE_SIZE);
328         bootinfo.bi_symtab = VTOP(p);
329         memcpy(p, &hdr.ex.a_syms, sizeof(hdr.ex.a_syms));
330         p += sizeof(hdr.ex.a_syms);
331         if (hdr.ex.a_syms) {
332             if (xfsread(ino, p, hdr.ex.a_syms))
333                 return;
334             p += hdr.ex.a_syms;
335             if (xfsread(ino, p, sizeof(int)))
336                 return;
337             x = *(uint32_t *)p;
338             p += sizeof(int);
339             x -= sizeof(int);
340             if (xfsread(ino, p, x))
341                 return;
342             p += x;
343         }
344     } else {
345         fs_off = hdr.eh.e_phoff;
346         for (j = i = 0; i < hdr.eh.e_phnum && j < 2; i++) {
347             if (xfsread(ino, ep + j, sizeof(ep[0])))
348                 return;
349             if (ep[j].p_type == PT_LOAD)
350                 j++;
351         }
352         for (i = 0; i < 2; i++) {
353             p = PTOV(ep[i].p_paddr & 0xffffff);
354             fs_off = ep[i].p_offset;
355             if (xfsread(ino, p, ep[i].p_filesz))
356                 return;
357         }
358         p += roundup2(ep[1].p_memsz, PAGE_SIZE);
359         bootinfo.bi_symtab = VTOP(p);
360         if (hdr.eh.e_shnum == hdr.eh.e_shstrndx + 3) {
361             fs_off = hdr.eh.e_shoff + sizeof(es[0]) *
362                 (hdr.eh.e_shstrndx + 1);
363             if (xfsread(ino, &es, sizeof(es)))
364                 return;
365             for (i = 0; i < 2; i++) {
366                 memcpy(p, &es[i].sh_size, sizeof(es[i].sh_size));
367                 p += sizeof(es[i].sh_size);
368                 fs_off = es[i].sh_offset;
369                 if (xfsread(ino, p, es[i].sh_size))
370                     return;
371                 p += es[i].sh_size;
372             }
373         }
374         addr = hdr.eh.e_entry & 0xffffff;
375     }
376     bootinfo.bi_esymtab = VTOP(p);
377     bootinfo.bi_kernelname = VTOP(kname);
378     bootinfo.bi_bios_dev = dsk.drive;
379     __exec((caddr_t)addr, opts & RBX_MASK,
380            MAKEBOOTDEV(dev_maj[dsk.type], 0, dsk.slice, dsk.unit, dsk.part),
381            0, 0, 0, VTOP(&bootinfo));
382 }
383
384 static int
385 parse()
386 {
387     char *arg = cmd;
388     char *p, *q;
389     unsigned int drv;
390     int c, i;
391
392     while ((c = *arg++)) {
393         if (c == ' ' || c == '\t' || c == '\n')
394             continue;
395         for (p = arg; *p && *p != '\n' && *p != ' ' && *p != '\t'; p++);
396         if (*p)
397             *p++ = 0;
398         if (c == '-') {
399             while ((c = *arg++)) {
400                 for (i = 0; c != optstr[i]; i++)
401                     if (i == NOPT - 1)
402                         return -1;
403                 opts ^= 1 << flags[i];
404             }
405             if (opts & 1 << RBX_PROBEKBD) {
406                 i = *(uint8_t *)PTOV(0x496) & 0x10;
407                 if (!i) {
408                     printf("NO KB\n");
409                     opts |= 1 << RBX_DUAL | 1 << RBX_SERIAL;
410                 }
411                 opts &= ~(1 << RBX_PROBEKBD);
412             }
413             ioctrl = opts & 1 << RBX_DUAL ? (IO_SERIAL|IO_KEYBOARD) :
414                      opts & 1 << RBX_SERIAL ? IO_SERIAL : IO_KEYBOARD;
415             if (ioctrl & IO_SERIAL)
416                 sio_init();
417         } else {
418             for (q = arg--; *q && *q != '('; q++);
419             if (*q) {
420                 drv = -1;
421                 if (arg[1] == ':') {
422                     drv = *arg - '0';
423                     if (drv > 9)
424                         return (-1);
425                     arg += 2;
426                 }
427                 if (q - arg != 2)
428                     return -1;
429                 for (i = 0; arg[0] != dev_nm[i][0] ||
430                             arg[1] != dev_nm[i][1]; i++)
431                     if (i == NDEV - 1)
432                         return -1;
433                 dsk.type = i;
434                 arg += 3;
435                 dsk.unit = *arg - '0';
436                 if (arg[1] != ',' || dsk.unit > 9)
437                     return -1;
438                 arg += 2;
439                 dsk.slice = WHOLE_DISK_SLICE;
440                 if (arg[1] == ',') {
441                     dsk.slice = *arg - '0' + 1;
442                     if (dsk.slice > NDOSPART)
443                         return -1;
444                     arg += 2;
445                 }
446                 if (arg[1] != ')')
447                     return -1;
448                 dsk.part = *arg - 'a';
449                 if (dsk.part > 7)
450                     return (-1);
451                 arg += 2;
452                 if (drv == -1)
453                     drv = dsk.unit;
454                 dsk.drive = (dsk.type <= TYPE_MAXHARD
455                              ? DRV_HARD : 0) + drv;
456                 dsk_meta = 0;
457             }
458             if ((i = p - arg - !*(p - 1))) {
459                 if ((size_t)i >= sizeof(kname))
460                     return -1;
461                 memcpy(kname, arg, i + 1);
462             }
463         }
464         arg = p;
465     }
466     return 0;
467 }
468
469 static int
470 dskread(void *buf, unsigned lba, unsigned nblk)
471 {
472     struct dos_partition *dp;
473     struct disklabel *d;
474     char *sec;
475     unsigned sl, i;
476
477     if (!dsk_meta) {
478         sec = dmadat->secbuf;
479         dsk.start = 0;
480         if (drvread(sec, DOSBBSECTOR, 1))
481             return -1;
482         dp = (void *)(sec + DOSPARTOFF);
483         sl = dsk.slice;
484         if (sl < BASE_SLICE) {
485             for (i = 0; i < NDOSPART; i++)
486                 if (dp[i].dp_typ == DOSPTYP_386BSD &&
487                     (dp[i].dp_flag & 0x80 || sl < BASE_SLICE)) {
488                     sl = BASE_SLICE + i;
489                     if (dp[i].dp_flag & 0x80 ||
490                         dsk.slice == COMPATIBILITY_SLICE)
491                         break;
492                 }
493             if (dsk.slice == WHOLE_DISK_SLICE)
494                 dsk.slice = sl;
495         }
496         if (sl != WHOLE_DISK_SLICE) {
497             if (sl != COMPATIBILITY_SLICE)
498                 dp += sl - BASE_SLICE;
499             if (dp->dp_typ != DOSPTYP_386BSD) {
500                 printf("Invalid %s\n", "slice");
501                 return -1;
502             }
503             dsk.start = dp->dp_start;
504         }
505         if (drvread(sec, dsk.start + LABELSECTOR, 1))
506                 return -1;
507         d = (void *)(sec + LABELOFFSET);
508         if (d->d_magic != DISKMAGIC || d->d_magic2 != DISKMAGIC) {
509             if (dsk.part != RAW_PART) {
510                 printf("Invalid %s\n", "label");
511                 return -1;
512             }
513         } else {
514             if (!dsk.init) {
515                 if (d->d_type == DTYPE_SCSI)
516                     dsk.type = TYPE_DA;
517                 dsk.init++;
518             }
519             if (dsk.part >= d->d_npartitions ||
520                 !d->d_partitions[dsk.part].p_size) {
521                 printf("Invalid %s\n", "partition");
522                 return -1;
523             }
524             dsk.start += d->d_partitions[dsk.part].p_offset;
525             dsk.start -= d->d_partitions[RAW_PART].p_offset;
526         }
527     }
528     return drvread(buf, dsk.start + lba, nblk);
529 }
530
531 static void
532 printf(const char *fmt,...)
533 {
534     va_list ap;
535     char buf[10];
536     char *s;
537     unsigned u;
538     int c;
539
540     va_start(ap, fmt);
541     while ((c = *fmt++)) {
542         if (c == '%') {
543             c = *fmt++;
544             switch (c) {
545             case 'c':
546                 putchar(va_arg(ap, int));
547                 continue;
548             case 's':
549                 for (s = va_arg(ap, char *); *s; s++)
550                     putchar(*s);
551                 continue;
552             case 'u':
553                 u = va_arg(ap, unsigned);
554                 s = buf;
555                 do
556                     *s++ = '0' + u % 10U;
557                 while (u /= 10U);
558                 while (--s >= buf)
559                     putchar(*s);
560                 continue;
561             }
562         }
563         putchar(c);
564     }
565     va_end(ap);
566     return;
567 }
568
569 static void
570 putchar(int c)
571 {
572     if (c == '\n')
573         xputc('\r');
574     xputc(c);
575 }
576
577 static int
578 drvread(void *buf, unsigned lba, unsigned nblk)
579 {
580     static unsigned c = 0x2d5c7c2f;
581
582     printf("%c\b", c = c << 8 | c >> 24);
583     v86.ctl = V86_ADDR | V86_CALLF | V86_FLAGS;
584     v86.addr = XREADORG;                /* call to xread in boot1 */
585     v86.es = VTOPSEG(buf);
586     v86.eax = lba;
587     v86.ebx = VTOPOFF(buf);
588     v86.ecx = lba >> 16;
589     v86.edx = nblk << 8 | dsk.drive;
590     v86int();
591     v86.ctl = V86_FLAGS;
592     if (V86_CY(v86.efl)) {
593         printf("error %u lba %u\n", v86.eax >> 8 & 0xff, lba);
594         return -1;
595     }
596     return 0;
597 }
598
599 static int
600 keyhit(unsigned ticks)
601 {
602     uint32_t t0, t1;
603
604     if (opts & 1 << RBX_NOINTR)
605         return 0;
606     t0 = 0;
607     for (;;) {
608         if (xgetc(1))
609             return 1;
610         t1 = *(uint32_t *)PTOV(0x46c);
611         if (!t0)
612             t0 = t1;
613         if (t1 < t0 || t1 >= t0 + ticks)
614             return 0;
615     }
616 }
617
618 static int
619 xputc(int c)
620 {
621     if (ioctrl & IO_KEYBOARD)
622         putc(c);
623     if (ioctrl & IO_SERIAL)
624         sio_putc(c);
625     return c;
626 }
627
628 static int
629 xgetc(int fn)
630 {
631     if (opts & 1 << RBX_NOINTR)
632         return 0;
633     for (;;) {
634         if (ioctrl & IO_KEYBOARD && getc(1))
635             return fn ? 1 : getc(0);
636         if (ioctrl & IO_SERIAL && sio_ischar())
637             return fn ? 1 : sio_getc();
638         if (fn)
639             return 0;
640     }
641 }
642
643 static int
644 getc(int fn)
645 {
646     v86.addr = 0x16;
647     v86.eax = fn << 8;
648     v86int();
649     return fn == 0 ? v86.eax & 0xff : !V86_ZR(v86.efl);
650 }