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