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