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