xref: /freebsd/stand/i386/boot2/boot2.c (revision 5857eb21)
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 }
647