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