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