* Use id(1) instead of grep(1) to detect the presence of the smmsp
[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.6 2003/11/30 17:04:35 drhodus 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                 printf("Keyboard: %s\n", i ? "yes" : "no");
408                 if (!i)
409                     opts |= 1 << RBX_DUAL | 1 << RBX_SERIAL;
410                 opts &= ~(1 << RBX_PROBEKBD);
411             }
412             ioctrl = opts & 1 << RBX_DUAL ? (IO_SERIAL|IO_KEYBOARD) :
413                      opts & 1 << RBX_SERIAL ? IO_SERIAL : IO_KEYBOARD;
414             if (ioctrl & IO_SERIAL)
415                 sio_init();
416         } else {
417             for (q = arg--; *q && *q != '('; q++);
418             if (*q) {
419                 drv = -1;
420                 if (arg[1] == ':') {
421                     drv = *arg - '0';
422                     if (drv > 9)
423                         return (-1);
424                     arg += 2;
425                 }
426                 if (q - arg != 2)
427                     return -1;
428                 for (i = 0; arg[0] != dev_nm[i][0] ||
429                             arg[1] != dev_nm[i][1]; i++)
430                     if (i == NDEV - 1)
431                         return -1;
432                 dsk.type = i;
433                 arg += 3;
434                 dsk.unit = *arg - '0';
435                 if (arg[1] != ',' || dsk.unit > 9)
436                     return -1;
437                 arg += 2;
438                 dsk.slice = WHOLE_DISK_SLICE;
439                 if (arg[1] == ',') {
440                     dsk.slice = *arg - '0' + 1;
441                     if (dsk.slice > NDOSPART)
442                         return -1;
443                     arg += 2;
444                 }
445                 if (arg[1] != ')')
446                     return -1;
447                 dsk.part = *arg - 'a';
448                 if (dsk.part > 7)
449                     return (-1);
450                 arg += 2;
451                 if (drv == -1)
452                     drv = dsk.unit;
453                 dsk.drive = (dsk.type <= TYPE_MAXHARD
454                              ? DRV_HARD : 0) + drv;
455                 dsk_meta = 0;
456             }
457             if ((i = p - arg - !*(p - 1))) {
458                 if ((size_t)i >= sizeof(kname))
459                     return -1;
460                 memcpy(kname, arg, i + 1);
461             }
462         }
463         arg = p;
464     }
465     return 0;
466 }
467
468 static int
469 dskread(void *buf, unsigned lba, unsigned nblk)
470 {
471     struct dos_partition *dp;
472     struct disklabel *d;
473     char *sec;
474     unsigned sl, i;
475
476     if (!dsk_meta) {
477         sec = dmadat->secbuf;
478         dsk.start = 0;
479         if (drvread(sec, DOSBBSECTOR, 1))
480             return -1;
481         dp = (void *)(sec + DOSPARTOFF);
482         sl = dsk.slice;
483         if (sl < BASE_SLICE) {
484             for (i = 0; i < NDOSPART; i++)
485                 if (dp[i].dp_typ == DOSPTYP_386BSD &&
486                     (dp[i].dp_flag & 0x80 || sl < BASE_SLICE)) {
487                     sl = BASE_SLICE + i;
488                     if (dp[i].dp_flag & 0x80 ||
489                         dsk.slice == COMPATIBILITY_SLICE)
490                         break;
491                 }
492             if (dsk.slice == WHOLE_DISK_SLICE)
493                 dsk.slice = sl;
494         }
495         if (sl != WHOLE_DISK_SLICE) {
496             if (sl != COMPATIBILITY_SLICE)
497                 dp += sl - BASE_SLICE;
498             if (dp->dp_typ != DOSPTYP_386BSD) {
499                 printf("Invalid %s\n", "slice");
500                 return -1;
501             }
502             dsk.start = dp->dp_start;
503         }
504         if (drvread(sec, dsk.start + LABELSECTOR, 1))
505                 return -1;
506         d = (void *)(sec + LABELOFFSET);
507         if (d->d_magic != DISKMAGIC || d->d_magic2 != DISKMAGIC) {
508             if (dsk.part != RAW_PART) {
509                 printf("Invalid %s\n", "label");
510                 return -1;
511             }
512         } else {
513             if (!dsk.init) {
514                 if (d->d_type == DTYPE_SCSI)
515                     dsk.type = TYPE_DA;
516                 dsk.init++;
517             }
518             if (dsk.part >= d->d_npartitions ||
519                 !d->d_partitions[dsk.part].p_size) {
520                 printf("Invalid %s\n", "partition");
521                 return -1;
522             }
523             dsk.start += d->d_partitions[dsk.part].p_offset;
524             dsk.start -= d->d_partitions[RAW_PART].p_offset;
525         }
526     }
527     return drvread(buf, dsk.start + lba, nblk);
528 }
529
530 static void
531 printf(const char *fmt,...)
532 {
533     va_list ap;
534     char buf[10];
535     char *s;
536     unsigned u;
537     int c;
538
539     va_start(ap, fmt);
540     while ((c = *fmt++)) {
541         if (c == '%') {
542             c = *fmt++;
543             switch (c) {
544             case 'c':
545                 putchar(va_arg(ap, int));
546                 continue;
547             case 's':
548                 for (s = va_arg(ap, char *); *s; s++)
549                     putchar(*s);
550                 continue;
551             case 'u':
552                 u = va_arg(ap, unsigned);
553                 s = buf;
554                 do
555                     *s++ = '0' + u % 10U;
556                 while (u /= 10U);
557                 while (--s >= buf)
558                     putchar(*s);
559                 continue;
560             }
561         }
562         putchar(c);
563     }
564     va_end(ap);
565     return;
566 }
567
568 static void
569 putchar(int c)
570 {
571     if (c == '\n')
572         xputc('\r');
573     xputc(c);
574 }
575
576 static int
577 drvread(void *buf, unsigned lba, unsigned nblk)
578 {
579     static unsigned c = 0x2d5c7c2f;
580
581     printf("%c\b", c = c << 8 | c >> 24);
582     v86.ctl = V86_ADDR | V86_CALLF | V86_FLAGS;
583     v86.addr = XREADORG;                /* call to xread in boot1 */
584     v86.es = VTOPSEG(buf);
585     v86.eax = lba;
586     v86.ebx = VTOPOFF(buf);
587     v86.ecx = lba >> 16;
588     v86.edx = nblk << 8 | dsk.drive;
589     v86int();
590     v86.ctl = V86_FLAGS;
591     if (V86_CY(v86.efl)) {
592         printf("error %u lba %u\n", v86.eax >> 8 & 0xff, lba);
593         return -1;
594     }
595     return 0;
596 }
597
598 static int
599 keyhit(unsigned ticks)
600 {
601     uint32_t t0, t1;
602
603     if (opts & 1 << RBX_NOINTR)
604         return 0;
605     t0 = 0;
606     for (;;) {
607         if (xgetc(1))
608             return 1;
609         t1 = *(uint32_t *)PTOV(0x46c);
610         if (!t0)
611             t0 = t1;
612         if (t1 < t0 || t1 >= t0 + ticks)
613             return 0;
614     }
615 }
616
617 static int
618 xputc(int c)
619 {
620     if (ioctrl & IO_KEYBOARD)
621         putc(c);
622     if (ioctrl & IO_SERIAL)
623         sio_putc(c);
624     return c;
625 }
626
627 static int
628 xgetc(int fn)
629 {
630     if (opts & 1 << RBX_NOINTR)
631         return 0;
632     for (;;) {
633         if (ioctrl & IO_KEYBOARD && getc(1))
634             return fn ? 1 : getc(0);
635         if (ioctrl & IO_SERIAL && sio_ischar())
636             return fn ? 1 : sio_getc();
637         if (fn)
638             return 0;
639     }
640 }
641
642 static int
643 getc(int fn)
644 {
645     v86.addr = 0x16;
646     v86.eax = fn << 8;
647     v86int();
648     return fn == 0 ? v86.eax & 0xff : !V86_ZR(v86.efl);
649 }