]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - stand/i386/boot2/boot2.c
MFC r325834,r325997,326502: Move sys/boot to stand/
[FreeBSD/FreeBSD.git] / stand / i386 / 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
16 #include <sys/cdefs.h>
17 __FBSDID("$FreeBSD$");
18
19 #include <sys/param.h>
20 #include <sys/disklabel.h>
21 #include <sys/diskmbr.h>
22 #include <sys/dirent.h>
23 #include <sys/reboot.h>
24
25 #include <machine/bootinfo.h>
26 #include <machine/elf.h>
27
28 #include <stdarg.h>
29
30 #include <a.out.h>
31
32 #include <btxv86.h>
33
34 #include "boot2.h"
35 #include "lib.h"
36 #include "paths.h"
37 #include "rbx.h"
38
39 /* Define to 0 to omit serial support */
40 #ifndef SERIAL
41 #define SERIAL 1
42 #endif
43
44 #define IO_KEYBOARD     1
45 #define IO_SERIAL       2
46
47 #if SERIAL
48 #define DO_KBD (ioctrl & IO_KEYBOARD)
49 #define DO_SIO (ioctrl & IO_SERIAL)
50 #else
51 #define DO_KBD (1)
52 #define DO_SIO (0)
53 #endif
54
55 #define SECOND          18      /* Circa that many ticks in a second. */
56
57 #define ARGS            0x900
58 #define NOPT            14
59 #define NDEV            3
60 #define MEM_BASE        0x12
61 #define MEM_EXT         0x15
62
63 #define DRV_HARD        0x80
64 #define DRV_MASK        0x7f
65
66 #define TYPE_AD         0
67 #define TYPE_DA         1
68 #define TYPE_MAXHARD    TYPE_DA
69 #define TYPE_FD         2
70
71 extern uint32_t _end;
72
73 static const char optstr[NOPT] = "DhaCcdgmnpqrsv"; /* Also 'P', 'S' */
74 static const unsigned char flags[NOPT] = {
75     RBX_DUAL,
76     RBX_SERIAL,
77     RBX_ASKNAME,
78     RBX_CDROM,
79     RBX_CONFIG,
80     RBX_KDB,
81     RBX_GDB,
82     RBX_MUTE,
83     RBX_NOINTR,
84     RBX_PAUSE,
85     RBX_QUIET,
86     RBX_DFLTROOT,
87     RBX_SINGLE,
88     RBX_VERBOSE
89 };
90
91 static const char *const dev_nm[NDEV] = {"ad", "da", "fd"};
92 static const unsigned char dev_maj[NDEV] = {30, 4, 2};
93
94 static struct dsk {
95     unsigned drive;
96     unsigned type;
97     unsigned unit;
98     uint8_t slice;
99     uint8_t part;
100     unsigned start;
101     int init;
102 } dsk;
103 static char cmd[512], cmddup[512], knamebuf[1024];
104 static const char *kname;
105 uint32_t opts;
106 static struct bootinfo bootinfo;
107 #if SERIAL
108 static int comspeed = SIOSPD;
109 static uint8_t ioctrl = IO_KEYBOARD;
110 #endif
111
112 int main(void);
113 void exit(int);
114 static void load(void);
115 static int parse(void);
116 static int dskread(void *, unsigned, unsigned);
117 static void printf(const char *,...);
118 static void putchar(int);
119 static int drvread(void *, unsigned, unsigned);
120 static int keyhit(unsigned);
121 static int xputc(int);
122 static int xgetc(int);
123 static inline int getc(int);
124
125 static void memcpy(void *, const void *, int);
126 static void
127 memcpy(void *dst, const void *src, int len)
128 {
129     const char *s = src;
130     char *d = dst;
131
132     while (len--)
133         *d++ = *s++;
134 }
135
136 static inline int
137 strcmp(const char *s1, const char *s2)
138 {
139     for (; *s1 == *s2 && *s1; s1++, s2++);
140     return (unsigned char)*s1 - (unsigned char)*s2;
141 }
142
143 #define UFS_SMALL_CGBASE
144 #include "ufsread.c"
145
146 static int
147 xfsread(ufs_ino_t inode, void *buf, size_t nbyte)
148 {
149     if ((size_t)fsread(inode, buf, nbyte) != nbyte) {
150         printf("Invalid %s\n", "format");
151         return -1;
152     }
153     return 0;
154 }
155
156 static inline void
157 getstr(void)
158 {
159     char *s;
160     int c;
161
162     s = cmd;
163     for (;;) {
164         switch (c = xgetc(0)) {
165         case 0:
166             break;
167         case '\177':
168         case '\b':
169             if (s > cmd) {
170                 s--;
171                 printf("\b \b");
172             }
173             break;
174         case '\n':
175         case '\r':
176             *s = 0;
177             return;
178         default:
179             if (s - cmd < sizeof(cmd) - 1)
180                 *s++ = c;
181             putchar(c);
182         }
183     }
184 }
185
186 static inline void
187 putc(int c)
188 {
189     v86.addr = 0x10;
190     v86.eax = 0xe00 | (c & 0xff);
191     v86.ebx = 0x7;
192     v86int();
193 }
194
195 int
196 main(void)
197 {
198     uint8_t autoboot;
199     ufs_ino_t ino;
200     size_t nbyte;
201
202     dmadat = (void *)(roundup2(__base + (int32_t)&_end, 0x10000) - __base);
203     v86.ctl = V86_FLAGS;
204     v86.efl = PSL_RESERVED_DEFAULT | PSL_I;
205     dsk.drive = *(uint8_t *)PTOV(ARGS);
206     dsk.type = dsk.drive & DRV_HARD ? TYPE_AD : TYPE_FD;
207     dsk.unit = dsk.drive & DRV_MASK;
208     dsk.slice = *(uint8_t *)PTOV(ARGS + 1) + 1;
209     bootinfo.bi_version = BOOTINFO_VERSION;
210     bootinfo.bi_size = sizeof(bootinfo);
211
212     /* Process configuration file */
213
214     autoboot = 1;
215
216     if ((ino = lookup(PATH_CONFIG)) ||
217         (ino = lookup(PATH_DOTCONFIG))) {
218         nbyte = fsread(ino, cmd, sizeof(cmd) - 1);
219         cmd[nbyte] = '\0';
220     }
221
222     if (*cmd) {
223         memcpy(cmddup, cmd, sizeof(cmd));
224         if (parse())
225             autoboot = 0;
226         if (!OPT_CHECK(RBX_QUIET))
227             printf("%s: %s", PATH_CONFIG, cmddup);
228         /* Do not process this command twice */
229         *cmd = 0;
230     }
231
232     /*
233      * Try to exec stage 3 boot loader. If interrupted by a keypress,
234      * or in case of failure, try to load a kernel directly instead.
235      */
236
237     if (!kname) {
238         kname = PATH_LOADER;
239         if (autoboot && !keyhit(3*SECOND)) {
240             load();
241             kname = PATH_KERNEL;
242         }
243     }
244
245     /* Present the user with the boot2 prompt. */
246
247     for (;;) {
248         if (!autoboot || !OPT_CHECK(RBX_QUIET))
249             printf("\nFreeBSD/x86 boot\n"
250                    "Default: %u:%s(%u,%c)%s\n"
251                    "boot: ",
252                    dsk.drive & DRV_MASK, dev_nm[dsk.type], dsk.unit,
253                    'a' + dsk.part, kname);
254         if (DO_SIO)
255             sio_flush();
256         if (!autoboot || keyhit(3*SECOND))
257             getstr();
258         else if (!autoboot || !OPT_CHECK(RBX_QUIET))
259             putchar('\n');
260         autoboot = 0;
261         if (parse())
262             putchar('\a');
263         else
264             load();
265     }
266 }
267
268 /* XXX - Needed for btxld to link the boot2 binary; do not remove. */
269 void
270 exit(int x)
271 {
272 }
273
274 static void
275 load(void)
276 {
277     union {
278         struct exec ex;
279         Elf32_Ehdr eh;
280     } hdr;
281     static Elf32_Phdr ep[2];
282     static Elf32_Shdr es[2];
283     caddr_t p;
284     ufs_ino_t ino;
285     uint32_t addr;
286     int k;
287     uint8_t i, j;
288
289     if (!(ino = lookup(kname))) {
290         if (!ls)
291             printf("No %s\n", kname);
292         return;
293     }
294     if (xfsread(ino, &hdr, sizeof(hdr)))
295         return;
296
297     if (N_GETMAGIC(hdr.ex) == ZMAGIC) {
298         addr = hdr.ex.a_entry & 0xffffff;
299         p = PTOV(addr);
300         fs_off = PAGE_SIZE;
301         if (xfsread(ino, p, hdr.ex.a_text))
302             return;
303         p += roundup2(hdr.ex.a_text, PAGE_SIZE);
304         if (xfsread(ino, p, hdr.ex.a_data))
305             return;
306     } else if (IS_ELF(hdr.eh)) {
307         fs_off = hdr.eh.e_phoff;
308         for (j = k = 0; k < hdr.eh.e_phnum && j < 2; k++) {
309             if (xfsread(ino, ep + j, sizeof(ep[0])))
310                 return;
311             if (ep[j].p_type == PT_LOAD)
312                 j++;
313         }
314         for (i = 0; i < 2; i++) {
315             p = PTOV(ep[i].p_paddr & 0xffffff);
316             fs_off = ep[i].p_offset;
317             if (xfsread(ino, p, ep[i].p_filesz))
318                 return;
319         }
320         p += roundup2(ep[1].p_memsz, PAGE_SIZE);
321         bootinfo.bi_symtab = VTOP(p);
322         if (hdr.eh.e_shnum == hdr.eh.e_shstrndx + 3) {
323             fs_off = hdr.eh.e_shoff + sizeof(es[0]) *
324                 (hdr.eh.e_shstrndx + 1);
325             if (xfsread(ino, &es, sizeof(es)))
326                 return;
327             for (i = 0; i < 2; i++) {
328                 *(Elf32_Word *)p = es[i].sh_size;
329                 p += sizeof(es[i].sh_size);
330                 fs_off = es[i].sh_offset;
331                 if (xfsread(ino, p, es[i].sh_size))
332                     return;
333                 p += es[i].sh_size;
334             }
335         }
336         addr = hdr.eh.e_entry & 0xffffff;
337         bootinfo.bi_esymtab = VTOP(p);
338     } else {
339         printf("Invalid %s\n", "format");
340         return;
341     }
342
343     bootinfo.bi_kernelname = VTOP(kname);
344     bootinfo.bi_bios_dev = dsk.drive;
345     __exec((caddr_t)addr, RB_BOOTINFO | (opts & RBX_MASK),
346            MAKEBOOTDEV(dev_maj[dsk.type], dsk.slice, dsk.unit, dsk.part),
347            0, 0, 0, VTOP(&bootinfo));
348 }
349
350 static int
351 parse()
352 {
353     char *arg = cmd;
354     char *ep, *p, *q;
355     const char *cp;
356     unsigned int drv;
357     int c, i, j;
358     size_t k;
359
360     while ((c = *arg++)) {
361         if (c == ' ' || c == '\t' || c == '\n')
362             continue;
363         for (p = arg; *p && *p != '\n' && *p != ' ' && *p != '\t'; p++);
364         ep = p;
365         if (*p)
366             *p++ = 0;
367         if (c == '-') {
368             while ((c = *arg++)) {
369                 if (c == 'P') {
370                     if (*(uint8_t *)PTOV(0x496) & 0x10) {
371                         cp = "yes";
372                     } else {
373                         opts |= OPT_SET(RBX_DUAL) | OPT_SET(RBX_SERIAL);
374                         cp = "no";
375                     }
376                     printf("Keyboard: %s\n", cp);
377                     continue;
378 #if SERIAL
379                 } else if (c == 'S') {
380                     j = 0;
381                     while ((unsigned int)(i = *arg++ - '0') <= 9)
382                         j = j * 10 + i;
383                     if (j > 0 && i == -'0') {
384                         comspeed = j;
385                         break;
386                     }
387                     /* Fall through to error below ('S' not in optstr[]). */
388 #endif
389                 }
390                 for (i = 0; c != optstr[i]; i++)
391                     if (i == NOPT - 1)
392                         return -1;
393                 opts ^= OPT_SET(flags[i]);
394             }
395 #if SERIAL
396             ioctrl = OPT_CHECK(RBX_DUAL) ? (IO_SERIAL|IO_KEYBOARD) :
397                      OPT_CHECK(RBX_SERIAL) ? IO_SERIAL : IO_KEYBOARD;
398             if (DO_SIO) {
399                 if (sio_init(115200 / comspeed) != 0)
400                     ioctrl &= ~IO_SERIAL;
401             }
402 #endif
403         } else {
404             for (q = arg--; *q && *q != '('; q++);
405             if (*q) {
406                 drv = -1;
407                 if (arg[1] == ':') {
408                     drv = *arg - '0';
409                     if (drv > 9)
410                         return (-1);
411                     arg += 2;
412                 }
413                 if (q - arg != 2)
414                     return -1;
415                 for (i = 0; arg[0] != dev_nm[i][0] ||
416                             arg[1] != dev_nm[i][1]; i++)
417                     if (i == NDEV - 1)
418                         return -1;
419                 dsk.type = i;
420                 arg += 3;
421                 dsk.unit = *arg - '0';
422                 if (arg[1] != ',' || dsk.unit > 9)
423                     return -1;
424                 arg += 2;
425                 dsk.slice = WHOLE_DISK_SLICE;
426                 if (arg[1] == ',') {
427                     dsk.slice = *arg - '0' + 1;
428                     if (dsk.slice > NDOSPART + 1)
429                         return -1;
430                     arg += 2;
431                 }
432                 if (arg[1] != ')')
433                     return -1;
434                 dsk.part = *arg - 'a';
435                 if (dsk.part > 7)
436                     return (-1);
437                 arg += 2;
438                 if (drv == -1)
439                     drv = dsk.unit;
440                 dsk.drive = (dsk.type <= TYPE_MAXHARD
441                              ? DRV_HARD : 0) + drv;
442                 dsk_meta = 0;
443             }
444             k = ep - arg;
445             if (k > 0) {
446                 if (k >= sizeof(knamebuf))
447                     return -1;
448                 memcpy(knamebuf, arg, k + 1);
449                 kname = knamebuf;
450             }
451         }
452         arg = p;
453     }
454     return 0;
455 }
456
457 static int
458 dskread(void *buf, unsigned lba, unsigned nblk)
459 {
460     struct dos_partition *dp;
461     struct disklabel *d;
462     char *sec;
463     unsigned i;
464     uint8_t sl;
465     const char *reason;
466
467     if (!dsk_meta) {
468         sec = dmadat->secbuf;
469         dsk.start = 0;
470         if (drvread(sec, DOSBBSECTOR, 1))
471             return -1;
472         dp = (void *)(sec + DOSPARTOFF);
473         sl = dsk.slice;
474         if (sl < BASE_SLICE) {
475             for (i = 0; i < NDOSPART; i++)
476                 if (dp[i].dp_typ == DOSPTYP_386BSD &&
477                     (dp[i].dp_flag & 0x80 || sl < BASE_SLICE)) {
478                     sl = BASE_SLICE + i;
479                     if (dp[i].dp_flag & 0x80 ||
480                         dsk.slice == COMPATIBILITY_SLICE)
481                         break;
482                 }
483             if (dsk.slice == WHOLE_DISK_SLICE)
484                 dsk.slice = sl;
485         }
486         if (sl != WHOLE_DISK_SLICE) {
487             if (sl != COMPATIBILITY_SLICE)
488                 dp += sl - BASE_SLICE;
489             if (dp->dp_typ != DOSPTYP_386BSD) {
490                 reason = "slice";
491                 goto error;
492             }
493             dsk.start = dp->dp_start;
494         }
495         if (drvread(sec, dsk.start + LABELSECTOR, 1))
496                 return -1;
497         d = (void *)(sec + LABELOFFSET);
498         if (d->d_magic != DISKMAGIC || d->d_magic2 != DISKMAGIC) {
499             if (dsk.part != RAW_PART) {
500                 reason = "label";
501                 goto error;
502             }
503         } else {
504             if (!dsk.init) {
505                 if (d->d_type == DTYPE_SCSI)
506                     dsk.type = TYPE_DA;
507                 dsk.init++;
508             }
509             if (dsk.part >= d->d_npartitions ||
510                 !d->d_partitions[dsk.part].p_size) {
511                 reason = "partition";
512                 goto error;
513             }
514             dsk.start += d->d_partitions[dsk.part].p_offset;
515             dsk.start -= d->d_partitions[RAW_PART].p_offset;
516         }
517     }
518     return drvread(buf, dsk.start + lba, nblk);
519 error:
520     printf("Invalid %s\n", reason);
521     return -1;
522 }
523
524 static void
525 printf(const char *fmt,...)
526 {
527     va_list ap;
528     static char buf[10];
529     char *s;
530     unsigned u;
531     int c;
532
533     va_start(ap, fmt);
534     while ((c = *fmt++)) {
535         if (c == '%') {
536             c = *fmt++;
537             switch (c) {
538             case 'c':
539                 putchar(va_arg(ap, int));
540                 continue;
541             case 's':
542                 for (s = va_arg(ap, char *); *s; s++)
543                     putchar(*s);
544                 continue;
545             case 'u':
546                 u = va_arg(ap, unsigned);
547                 s = buf;
548                 do
549                     *s++ = '0' + u % 10U;
550                 while (u /= 10U);
551                 while (--s >= buf)
552                     putchar(*s);
553                 continue;
554             }
555         }
556         putchar(c);
557     }
558     va_end(ap);
559     return;
560 }
561
562 static void
563 putchar(int c)
564 {
565     if (c == '\n')
566         xputc('\r');
567     xputc(c);
568 }
569
570 static int
571 drvread(void *buf, unsigned lba, unsigned nblk)
572 {
573     static unsigned c = 0x2d5c7c2f;
574
575     if (!OPT_CHECK(RBX_QUIET)) {
576         xputc(c = c << 8 | c >> 24);
577         xputc('\b');
578     }
579     v86.ctl = V86_ADDR | V86_CALLF | V86_FLAGS;
580     v86.addr = XREADORG;                /* call to xread in boot1 */
581     v86.es = VTOPSEG(buf);
582     v86.eax = lba;
583     v86.ebx = VTOPOFF(buf);
584     v86.ecx = lba >> 16;
585     v86.edx = nblk << 8 | dsk.drive;
586     v86int();
587     v86.ctl = V86_FLAGS;
588     if (V86_CY(v86.efl)) {
589         printf("error %u lba %u\n", v86.eax >> 8 & 0xff, lba);
590         return -1;
591     }
592     return 0;
593 }
594
595 static int
596 keyhit(unsigned ticks)
597 {
598     uint32_t t0, t1;
599
600     if (OPT_CHECK(RBX_NOINTR))
601         return 0;
602     t0 = 0;
603     for (;;) {
604         if (xgetc(1))
605             return 1;
606         t1 = *(uint32_t *)PTOV(0x46c);
607         if (!t0)
608             t0 = t1;
609         if ((uint32_t)(t1 - t0) >= ticks)
610             return 0;
611     }
612 }
613
614 static int
615 xputc(int c)
616 {
617     if (DO_KBD)
618         putc(c);
619     if (DO_SIO)
620         sio_putc(c);
621     return c;
622 }
623
624 static int
625 getc(int fn)
626 {
627     v86.addr = 0x16;
628     v86.eax = fn << 8;
629     v86int();
630     return fn == 0 ? v86.eax & 0xff : !V86_ZR(v86.efl);
631 }
632
633 static int
634 xgetc(int fn)
635 {
636     if (OPT_CHECK(RBX_NOINTR))
637         return 0;
638     for (;;) {
639         if (DO_KBD && getc(1))
640             return fn ? 1 : getc(0);
641         if (DO_SIO && sio_ischar())
642             return fn ? 1 : sio_getc();
643         if (fn)
644             return 0;
645     }
646 }