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