Bring boot2 and the loader into line with our new dual-console support.
[dragonfly.git] / sys / boot / pc32 / 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/pc32/boot2/boot2.c,v 1.8 2004/06/26 22:37:09 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 SECOND          18      /* Circa that many ticks in a second. */
36
37 #define RBX_ASKNAME     0x0     /* -a */
38 #define RBX_SINGLE      0x1     /* -s */
39 #define RBX_DFLTROOT    0x5     /* -r */
40 #define RBX_KDB         0x6     /* -d */
41 #define RBX_CONFIG      0xa     /* -c */
42 #define RBX_VERBOSE     0xb     /* -v */
43 #define RBX_SERIAL      0xc     /* -h */
44 #define RBX_CDROM       0xd     /* -C */
45 #define RBX_GDB         0xf     /* -g */
46 #define RBX_MUTE        0x10    /* -m */
47 #define RBX_PAUSE       0x12    /* -p */
48 #define RBX_NOINTR      0x1c    /* -n */
49 #define RBX_VIDEO       0x1d    /* -V */
50 #define RBX_PROBEKBD    0x1e    /* -P */
51 /* 0x1f is reserved for the historical RB_BOOTINFO option */
52
53 #define RBF_SERIAL      (1 << RBX_SERIAL)
54 #define RBF_VIDEO       (1 << RBX_VIDEO)
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 NDEV            3
65 #define MEM_BASE        0x12
66 #define MEM_EXT         0x15
67 #define V86_CY(x)       ((x) & 1)
68 #define V86_ZR(x)       ((x) & 0x40)
69
70 #define DRV_HARD        0x80
71 #define DRV_MASK        0x7f
72
73 #define TYPE_AD         0
74 #define TYPE_DA         1
75 #define TYPE_MAXHARD    TYPE_DA
76 #define TYPE_FD         2
77
78 #define NOPT            12
79
80 extern uint32_t _end;
81
82 static const char optstr[NOPT] = { "VhaCgmnPprsv" };
83 static const unsigned char flags[NOPT] = {
84     RBX_VIDEO,
85     RBX_SERIAL,
86     RBX_ASKNAME,
87     RBX_CDROM,
88     RBX_GDB,
89     RBX_MUTE,
90     RBX_NOINTR,
91     RBX_PROBEKBD,
92     RBX_PAUSE,
93     RBX_DFLTROOT,
94     RBX_SINGLE,
95     RBX_VERBOSE
96 };
97
98 static const char *const dev_nm[NDEV] = {"ad", "da", "fd"};
99 static const unsigned char dev_maj[NDEV] = {30, 4, 2};
100
101 static struct dsk {
102     unsigned drive;
103     unsigned type;
104     unsigned unit;
105     unsigned slice;
106     unsigned part;
107     unsigned start;
108     int init;
109 } dsk;
110 static char cmd[512];
111 static char kname[1024];
112 static uint32_t opts;
113 static struct bootinfo bootinfo;
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[0]) {
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      * Setup our (serial) console after processing the config file
250      */
251     if ((opts & (RBF_SERIAL|RBF_VIDEO)) == 0)
252         opts |= RBF_SERIAL|RBF_VIDEO;
253     if (opts & RBF_SERIAL)
254         sio_init();
255
256
257     /*
258      * Try to exec stage 3 boot loader. If interrupted by a keypress,
259      * or in case of failure, try to load a kernel directly instead.
260      */
261
262     if (autoboot && !*kname) {
263         memcpy(kname, PATH_BOOT3, sizeof(PATH_BOOT3));
264         if (!keyhit(3*SECOND)) {
265             load();
266             memcpy(kname, PATH_KERNEL, sizeof(PATH_KERNEL));
267         }
268     }
269
270     /* Present the user with the boot2 prompt. */
271
272     for (;;) {
273         printf("\nDragonFly/i386 boot\n"
274                "Default: %u:%s(%u,%c)%s\n"
275                "boot: ",
276                dsk.drive & DRV_MASK, dev_nm[dsk.type], dsk.unit,
277                'a' + dsk.part, kname);
278         if (opts & RBF_SERIAL)
279             sio_flush();
280         if (!autoboot || keyhit(5*SECOND))
281             getstr();
282         else
283             putchar('\n');
284         autoboot = 0;
285         if (cmd[0] == 0 || parse())
286             putchar('\a');
287         else
288             load();
289     }
290 }
291
292 /* XXX - Needed for btxld to link the boot2 binary; do not remove. */
293 void
294 exit(int x)
295 {
296 }
297
298 static void
299 load(void)
300 {
301     union {
302         struct exec ex;
303         Elf32_Ehdr eh;
304     } hdr;
305     Elf32_Phdr ep[2];
306     Elf32_Shdr es[2];
307     caddr_t p;
308     ino_t ino;
309     uint32_t addr, x;
310     int fmt, i, j;
311
312     if (!(ino = lookup(kname))) {
313         if (!ls)
314             printf("No %s\n", kname);
315         return;
316     }
317     if (xfsread(ino, &hdr, sizeof(hdr)))
318         return;
319     if (N_GETMAGIC(hdr.ex) == ZMAGIC)
320         fmt = 0;
321     else if (IS_ELF(hdr.eh))
322         fmt = 1;
323     else {
324         printf("Invalid %s\n", "format");
325         return;
326     }
327     if (fmt == 0) {
328         addr = hdr.ex.a_entry & 0xffffff;
329         p = PTOV(addr);
330         fs_off = PAGE_SIZE;
331         if (xfsread(ino, p, hdr.ex.a_text))
332             return;
333         p += roundup2(hdr.ex.a_text, PAGE_SIZE);
334         if (xfsread(ino, p, hdr.ex.a_data))
335             return;
336         p += hdr.ex.a_data + roundup2(hdr.ex.a_bss, PAGE_SIZE);
337         bootinfo.bi_symtab = VTOP(p);
338         memcpy(p, &hdr.ex.a_syms, sizeof(hdr.ex.a_syms));
339         p += sizeof(hdr.ex.a_syms);
340         if (hdr.ex.a_syms) {
341             if (xfsread(ino, p, hdr.ex.a_syms))
342                 return;
343             p += hdr.ex.a_syms;
344             if (xfsread(ino, p, sizeof(int)))
345                 return;
346             x = *(uint32_t *)p;
347             p += sizeof(int);
348             x -= sizeof(int);
349             if (xfsread(ino, p, x))
350                 return;
351             p += x;
352         }
353     } else {
354         fs_off = hdr.eh.e_phoff;
355         for (j = i = 0; i < hdr.eh.e_phnum && j < 2; i++) {
356             if (xfsread(ino, ep + j, sizeof(ep[0])))
357                 return;
358             if (ep[j].p_type == PT_LOAD)
359                 j++;
360         }
361         for (i = 0; i < 2; i++) {
362             p = PTOV(ep[i].p_paddr & 0xffffff);
363             fs_off = ep[i].p_offset;
364             if (xfsread(ino, p, ep[i].p_filesz))
365                 return;
366         }
367         p += roundup2(ep[1].p_memsz, PAGE_SIZE);
368         bootinfo.bi_symtab = VTOP(p);
369         if (hdr.eh.e_shnum == hdr.eh.e_shstrndx + 3) {
370             fs_off = hdr.eh.e_shoff + sizeof(es[0]) *
371                 (hdr.eh.e_shstrndx + 1);
372             if (xfsread(ino, &es, sizeof(es)))
373                 return;
374             for (i = 0; i < 2; i++) {
375                 memcpy(p, &es[i].sh_size, sizeof(es[i].sh_size));
376                 p += sizeof(es[i].sh_size);
377                 fs_off = es[i].sh_offset;
378                 if (xfsread(ino, p, es[i].sh_size))
379                     return;
380                 p += es[i].sh_size;
381             }
382         }
383         addr = hdr.eh.e_entry & 0xffffff;
384     }
385     bootinfo.bi_esymtab = VTOP(p);
386     bootinfo.bi_kernelname = VTOP(kname);
387     bootinfo.bi_bios_dev = dsk.drive;
388     __exec((caddr_t)addr, opts & RBX_MASK,
389            MAKEBOOTDEV(dev_maj[dsk.type], 0, dsk.slice, dsk.unit, dsk.part),
390            0, 0, 0, VTOP(&bootinfo));
391 }
392
393 static int
394 parse()
395 {
396     char *arg = cmd;
397     char *p, *q;
398     unsigned int drv;
399     int c, i;
400
401     while ((c = *arg++)) {
402         if (c == ' ' || c == '\t' || c == '\n')
403             continue;
404         for (p = arg; *p && *p != '\n' && *p != ' ' && *p != '\t'; p++)
405             ;
406         if (*p)
407             *p++ = 0;
408         if (c == '-') {
409             while ((c = *arg++)) {
410                 for (i = NOPT - 1; i >= 0; --i) {
411                     if (optstr[i] == c) {
412                         opts ^= 1 << flags[i];
413                         goto ok;
414                     }
415                 }
416                 return(-1);
417                 ok: ;   /* ugly but save space */
418             }
419             if (opts & (1 << RBX_PROBEKBD)) {
420                 i = *(uint8_t *)PTOV(0x496) & 0x10;
421                 if (!i) {
422                     printf("NO KB\n");
423                     opts |= RBF_VIDEO | RBF_SERIAL;
424                 }
425                 opts &= ~(1 << RBX_PROBEKBD);
426             }
427         } else {
428             for (q = arg--; *q && *q != '('; q++);
429             if (*q) {
430                 drv = -1;
431                 if (arg[1] == ':') {
432                     drv = *arg - '0';
433                     if (drv > 9)
434                         return (-1);
435                     arg += 2;
436                 }
437                 if (q - arg != 2)
438                     return -1;
439                 for (i = 0; arg[0] != dev_nm[i][0] ||
440                             arg[1] != dev_nm[i][1]; i++)
441                     if (i == NDEV - 1)
442                         return -1;
443                 dsk.type = i;
444                 arg += 3;
445                 dsk.unit = *arg - '0';
446                 if (arg[1] != ',' || dsk.unit > 9)
447                     return -1;
448                 arg += 2;
449                 dsk.slice = WHOLE_DISK_SLICE;
450                 if (arg[1] == ',') {
451                     dsk.slice = *arg - '0' + 1;
452                     if (dsk.slice > NDOSPART)
453                         return -1;
454                     arg += 2;
455                 }
456                 if (arg[1] != ')')
457                     return -1;
458                 dsk.part = *arg - 'a';
459                 if (dsk.part > 7)
460                     return (-1);
461                 arg += 2;
462                 if (drv == -1)
463                     drv = dsk.unit;
464                 dsk.drive = (dsk.type <= TYPE_MAXHARD
465                              ? DRV_HARD : 0) + drv;
466                 dsk_meta = 0;
467             }
468             if ((i = p - arg - !*(p - 1))) {
469                 if ((size_t)i >= sizeof(kname))
470                     return -1;
471                 memcpy(kname, arg, i + 1);
472             }
473         }
474         arg = p;
475     }
476     return 0;
477 }
478
479 static int
480 dskread(void *buf, unsigned lba, unsigned nblk)
481 {
482     struct dos_partition *dp;
483     struct disklabel *d;
484     char *sec;
485     unsigned sl, i;
486
487     if (!dsk_meta) {
488         sec = dmadat->secbuf;
489         dsk.start = 0;
490         if (drvread(sec, DOSBBSECTOR, 1))
491             return -1;
492         dp = (void *)(sec + DOSPARTOFF);
493         sl = dsk.slice;
494         if (sl < BASE_SLICE) {
495             for (i = 0; i < NDOSPART; i++)
496                 if (dp[i].dp_typ == DOSPTYP_386BSD &&
497                     (dp[i].dp_flag & 0x80 || sl < BASE_SLICE)) {
498                     sl = BASE_SLICE + i;
499                     if (dp[i].dp_flag & 0x80 ||
500                         dsk.slice == COMPATIBILITY_SLICE)
501                         break;
502                 }
503             if (dsk.slice == WHOLE_DISK_SLICE)
504                 dsk.slice = sl;
505         }
506         if (sl != WHOLE_DISK_SLICE) {
507             if (sl != COMPATIBILITY_SLICE)
508                 dp += sl - BASE_SLICE;
509             if (dp->dp_typ != DOSPTYP_386BSD) {
510                 printf("Invalid %s\n", "slice");
511                 return -1;
512             }
513             dsk.start = dp->dp_start;
514         }
515         if (drvread(sec, dsk.start + LABELSECTOR, 1))
516                 return -1;
517         d = (void *)(sec + LABELOFFSET);
518         if (d->d_magic != DISKMAGIC || d->d_magic2 != DISKMAGIC) {
519             if (dsk.part != RAW_PART) {
520                 printf("Invalid %s\n", "label");
521                 return -1;
522             }
523         } else {
524             if (!dsk.init) {
525                 if (d->d_type == DTYPE_SCSI)
526                     dsk.type = TYPE_DA;
527                 dsk.init++;
528             }
529             if (dsk.part >= d->d_npartitions ||
530                 !d->d_partitions[dsk.part].p_size) {
531                 printf("Invalid %s\n", "partition");
532                 return -1;
533             }
534             dsk.start += d->d_partitions[dsk.part].p_offset;
535             dsk.start -= d->d_partitions[RAW_PART].p_offset;
536         }
537     }
538     return drvread(buf, dsk.start + lba, nblk);
539 }
540
541 static void
542 printf(const char *fmt,...)
543 {
544     va_list ap;
545     char buf[10];
546     char *s;
547     unsigned u;
548     int c;
549
550     va_start(ap, fmt);
551     while ((c = *fmt++)) {
552         if (c == '%') {
553             c = *fmt++;
554             switch (c) {
555             case 'c':
556                 putchar(va_arg(ap, int));
557                 continue;
558             case 's':
559                 for (s = va_arg(ap, char *); *s; s++)
560                     putchar(*s);
561                 continue;
562             case 'u':
563                 u = va_arg(ap, unsigned);
564                 s = buf;
565                 do
566                     *s++ = '0' + u % 10U;
567                 while (u /= 10U);
568                 while (--s >= buf)
569                     putchar(*s);
570                 continue;
571             }
572         }
573         putchar(c);
574     }
575     va_end(ap);
576     return;
577 }
578
579 static void
580 putchar(int c)
581 {
582     if (c == '\n')
583         xputc('\r');
584     xputc(c);
585 }
586
587 static int
588 drvread(void *buf, unsigned lba, unsigned nblk)
589 {
590     static unsigned c = 0x2d5c7c2f;
591
592     printf("%c\b", c = c << 8 | c >> 24);
593     v86.ctl = V86_ADDR | V86_CALLF | V86_FLAGS;
594     v86.addr = XREADORG;                /* call to xread in boot1 */
595     v86.es = VTOPSEG(buf);
596     v86.eax = lba;
597     v86.ebx = VTOPOFF(buf);
598     v86.ecx = lba >> 16;
599     v86.edx = nblk << 8 | dsk.drive;
600     v86int();
601     v86.ctl = V86_FLAGS;
602     if (V86_CY(v86.efl)) {
603         printf("error %u lba %u\n", v86.eax >> 8 & 0xff, lba);
604         return -1;
605     }
606     return 0;
607 }
608
609 static int
610 keyhit(unsigned ticks)
611 {
612     uint32_t t0, t1;
613
614     if (opts & 1 << RBX_NOINTR)
615         return 0;
616     t0 = 0;
617     for (;;) {
618         if (xgetc(1))
619             return 1;
620         t1 = *(uint32_t *)PTOV(0x46c);
621         if (!t0)
622             t0 = t1;
623         if (t1 < t0 || t1 >= t0 + ticks)
624             return 0;
625     }
626 }
627
628 static int
629 xputc(int c)
630 {
631     if (opts & RBF_VIDEO)
632         putc(c);
633     if (opts & RBF_SERIAL)
634         sio_putc(c);
635     return c;
636 }
637
638 static int
639 xgetc(int fn)
640 {
641     if (opts & 1 << RBX_NOINTR)
642         return 0;
643     for (;;) {
644         if ((opts & RBF_VIDEO) && getc(1))
645             return fn ? 1 : getc(0);
646         if ((opts & RBF_SERIAL) && sio_ischar())
647             return fn ? 1 : sio_getc();
648         if (fn)
649             return 0;
650     }
651 }
652
653 static int
654 getc(int fn)
655 {
656     v86.addr = 0x16;
657     v86.eax = fn << 8;
658     v86int();
659     return fn == 0 ? v86.eax & 0xff : !V86_ZR(v86.efl);
660 }