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