gptboot.c revision 181436
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 181436 2008-08-08 19:41:20Z jhb $"); 18 19#include <sys/param.h> 20#include <sys/gpt.h> 21#include <sys/dirent.h> 22#include <sys/reboot.h> 23 24#include <machine/bootinfo.h> 25#include <machine/elf.h> 26#include <machine/psl.h> 27 28#include <stdarg.h> 29 30#include <a.out.h> 31 32#include <btxv86.h> 33 34#include "lib.h" 35 36#define IO_KEYBOARD 1 37#define IO_SERIAL 2 38 39#define SECOND 18 /* Circa that many ticks in a second. */ 40 41#define RBX_ASKNAME 0x0 /* -a */ 42#define RBX_SINGLE 0x1 /* -s */ 43/* 0x2 is reserved for log2(RB_NOSYNC). */ 44/* 0x3 is reserved for log2(RB_HALT). */ 45/* 0x4 is reserved for log2(RB_INITNAME). */ 46#define RBX_DFLTROOT 0x5 /* -r */ 47#define RBX_KDB 0x6 /* -d */ 48/* 0x7 is reserved for log2(RB_RDONLY). */ 49/* 0x8 is reserved for log2(RB_DUMP). */ 50/* 0x9 is reserved for log2(RB_MINIROOT). */ 51#define RBX_CONFIG 0xa /* -c */ 52#define RBX_VERBOSE 0xb /* -v */ 53#define RBX_SERIAL 0xc /* -h */ 54#define RBX_CDROM 0xd /* -C */ 55/* 0xe is reserved for log2(RB_POWEROFF). */ 56#define RBX_GDB 0xf /* -g */ 57#define RBX_MUTE 0x10 /* -m */ 58/* 0x11 is reserved for log2(RB_SELFTEST). */ 59/* 0x12 is reserved for boot programs. */ 60/* 0x13 is reserved for boot programs. */ 61#define RBX_PAUSE 0x14 /* -p */ 62#define RBX_QUIET 0x15 /* -q */ 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 (OPT_SET(RBX_ASKNAME) | OPT_SET(RBX_SINGLE) | \ 70 OPT_SET(RBX_DFLTROOT) | OPT_SET(RBX_KDB ) | \ 71 OPT_SET(RBX_CONFIG) | OPT_SET(RBX_VERBOSE) | \ 72 OPT_SET(RBX_SERIAL) | OPT_SET(RBX_CDROM) | \ 73 OPT_SET(RBX_GDB ) | OPT_SET(RBX_MUTE) | \ 74 OPT_SET(RBX_PAUSE) | OPT_SET(RBX_DUAL)) 75 76#define PATH_CONFIG "/boot.config" 77#define PATH_BOOT3 "/boot/loader" 78#define PATH_KERNEL "/boot/kernel/kernel" 79 80#define ARGS 0x900 81#define NOPT 14 82#define NDEV 3 83#define MEM_BASE 0x12 84#define MEM_EXT 0x15 85#define V86_CY(x) ((x) & PSL_C) 86#define V86_ZR(x) ((x) & PSL_Z) 87 88#define DRV_HARD 0x80 89#define DRV_MASK 0x7f 90 91#define TYPE_AD 0 92#define TYPE_DA 1 93#define TYPE_MAXHARD TYPE_DA 94#define TYPE_FD 2 95 96#define OPT_SET(opt) (1 << (opt)) 97#define OPT_CHECK(opt) ((opts) & OPT_SET(opt)) 98 99extern uint32_t _end; 100 101static const uuid_t freebsd_ufs_uuid = GPT_ENT_TYPE_FREEBSD_UFS; 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 int part; 128 daddr_t start; 129 int init; 130} dsk; 131static char cmd[512], cmddup[512]; 132static char kname[1024]; 133static uint32_t opts; 134static int comspeed = SIOSPD; 135static struct bootinfo bootinfo; 136static uint8_t ioctrl = IO_KEYBOARD; 137 138void exit(int); 139static int bcmp(const void *, const void *, size_t); 140static void load(void); 141static int parse(void); 142static int xfsread(ino_t, void *, size_t); 143static int dskread(void *, daddr_t, unsigned); 144static void printf(const char *,...); 145static void putchar(int); 146static void memcpy(void *, const void *, int); 147static uint32_t memsize(void); 148static int drvread(void *, daddr_t, unsigned); 149static int keyhit(unsigned); 150static int xputc(int); 151static int xgetc(int); 152static int getc(int); 153 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 v86.efl = PSL_RESERVED_DEFAULT | PSL_I; 240 dsk.drive = *(uint8_t *)PTOV(ARGS); 241 dsk.type = dsk.drive & DRV_HARD ? TYPE_AD : TYPE_FD; 242 dsk.unit = dsk.drive & DRV_MASK; 243 dsk.part = -1; 244 bootinfo.bi_version = BOOTINFO_VERSION; 245 bootinfo.bi_size = sizeof(bootinfo); 246 bootinfo.bi_basemem = 0; /* XXX will be filled by loader or kernel */ 247 bootinfo.bi_extmem = memsize(); 248 bootinfo.bi_memsizes_valid++; 249 250 /* Process configuration file */ 251 252 autoboot = 1; 253 254 if ((ino = lookup(PATH_CONFIG))) 255 fsread(ino, cmd, sizeof(cmd)); 256 257 if (*cmd) { 258 memcpy(cmddup, cmd, sizeof(cmd)); 259 if (parse()) 260 autoboot = 0; 261 if (!OPT_CHECK(RBX_QUIET)) 262 printf("%s: %s", PATH_CONFIG, cmddup); 263 /* Do not process this command twice */ 264 *cmd = 0; 265 } 266 267 /* 268 * Try to exec stage 3 boot loader. If interrupted by a keypress, 269 * or in case of failure, try to load a kernel directly instead. 270 */ 271 272 if (autoboot && !*kname) { 273 memcpy(kname, PATH_BOOT3, sizeof(PATH_BOOT3)); 274 if (!keyhit(3*SECOND)) { 275 load(); 276 memcpy(kname, PATH_KERNEL, sizeof(PATH_KERNEL)); 277 } 278 } 279 280 /* Present the user with the boot2 prompt. */ 281 282 for (;;) { 283 if (!autoboot || !OPT_CHECK(RBX_QUIET)) 284 printf("\nFreeBSD/i386 boot\n" 285 "Default: %u:%s(%up%u)%s\n" 286 "boot: ", 287 dsk.drive & DRV_MASK, dev_nm[dsk.type], dsk.unit, 288 dsk.part, kname); 289 if (ioctrl & IO_SERIAL) 290 sio_flush(); 291 if (!autoboot || keyhit(5*SECOND)) 292 getstr(); 293 else if (!autoboot || !OPT_CHECK(RBX_QUIET)) 294 putchar('\n'); 295 autoboot = 0; 296 if (parse()) 297 putchar('\a'); 298 else 299 load(); 300 } 301} 302 303/* XXX - Needed for btxld to link the boot2 binary; do not remove. */ 304void 305exit(int x) 306{ 307} 308 309static void 310load(void) 311{ 312 union { 313 struct exec ex; 314 Elf32_Ehdr eh; 315 } hdr; 316 static Elf32_Phdr ep[2]; 317 static Elf32_Shdr es[2]; 318 caddr_t p; 319 ino_t ino; 320 uint32_t addr, x; 321 int fmt, i, j; 322 323 if (!(ino = lookup(kname))) { 324 if (!ls) 325 printf("No %s\n", kname); 326 return; 327 } 328 if (xfsread(ino, &hdr, sizeof(hdr))) 329 return; 330 if (N_GETMAGIC(hdr.ex) == ZMAGIC) 331 fmt = 0; 332 else if (IS_ELF(hdr.eh)) 333 fmt = 1; 334 else { 335 printf("Invalid %s\n", "format"); 336 return; 337 } 338 if (fmt == 0) { 339 addr = hdr.ex.a_entry & 0xffffff; 340 p = PTOV(addr); 341 fs_off = PAGE_SIZE; 342 if (xfsread(ino, p, hdr.ex.a_text)) 343 return; 344 p += roundup2(hdr.ex.a_text, PAGE_SIZE); 345 if (xfsread(ino, p, hdr.ex.a_data)) 346 return; 347 p += hdr.ex.a_data + roundup2(hdr.ex.a_bss, PAGE_SIZE); 348 bootinfo.bi_symtab = VTOP(p); 349 memcpy(p, &hdr.ex.a_syms, sizeof(hdr.ex.a_syms)); 350 p += sizeof(hdr.ex.a_syms); 351 if (hdr.ex.a_syms) { 352 if (xfsread(ino, p, hdr.ex.a_syms)) 353 return; 354 p += hdr.ex.a_syms; 355 if (xfsread(ino, p, sizeof(int))) 356 return; 357 x = *(uint32_t *)p; 358 p += sizeof(int); 359 x -= sizeof(int); 360 if (xfsread(ino, p, x)) 361 return; 362 p += x; 363 } 364 } else { 365 fs_off = hdr.eh.e_phoff; 366 for (j = i = 0; i < hdr.eh.e_phnum && j < 2; i++) { 367 if (xfsread(ino, ep + j, sizeof(ep[0]))) 368 return; 369 if (ep[j].p_type == PT_LOAD) 370 j++; 371 } 372 for (i = 0; i < 2; i++) { 373 p = PTOV(ep[i].p_paddr & 0xffffff); 374 fs_off = ep[i].p_offset; 375 if (xfsread(ino, p, ep[i].p_filesz)) 376 return; 377 } 378 p += roundup2(ep[1].p_memsz, PAGE_SIZE); 379 bootinfo.bi_symtab = VTOP(p); 380 if (hdr.eh.e_shnum == hdr.eh.e_shstrndx + 3) { 381 fs_off = hdr.eh.e_shoff + sizeof(es[0]) * 382 (hdr.eh.e_shstrndx + 1); 383 if (xfsread(ino, &es, sizeof(es))) 384 return; 385 for (i = 0; i < 2; i++) { 386 memcpy(p, &es[i].sh_size, sizeof(es[i].sh_size)); 387 p += sizeof(es[i].sh_size); 388 fs_off = es[i].sh_offset; 389 if (xfsread(ino, p, es[i].sh_size)) 390 return; 391 p += es[i].sh_size; 392 } 393 } 394 addr = hdr.eh.e_entry & 0xffffff; 395 } 396 bootinfo.bi_esymtab = VTOP(p); 397 bootinfo.bi_kernelname = VTOP(kname); 398 bootinfo.bi_bios_dev = dsk.drive; 399 __exec((caddr_t)addr, RB_BOOTINFO | (opts & RBX_MASK), 400 MAKEBOOTDEV(dev_maj[dsk.type], dsk.part + 1, dsk.unit, 0xff), 401 0, 0, 0, VTOP(&bootinfo)); 402} 403 404static int 405parse(void) 406{ 407 char *arg = cmd; 408 char *ep, *p, *q; 409 const char *cp; 410 unsigned int drv; 411 int c, i, j; 412 413 while ((c = *arg++)) { 414 if (c == ' ' || c == '\t' || c == '\n') 415 continue; 416 for (p = arg; *p && *p != '\n' && *p != ' ' && *p != '\t'; p++); 417 ep = p; 418 if (*p) 419 *p++ = 0; 420 if (c == '-') { 421 while ((c = *arg++)) { 422 if (c == 'P') { 423 if (*(uint8_t *)PTOV(0x496) & 0x10) { 424 cp = "yes"; 425 } else { 426 opts |= OPT_SET(RBX_DUAL) | OPT_SET(RBX_SERIAL); 427 cp = "no"; 428 } 429 printf("Keyboard: %s\n", cp); 430 continue; 431 } else if (c == 'S') { 432 j = 0; 433 while ((unsigned int)(i = *arg++ - '0') <= 9) 434 j = j * 10 + i; 435 if (j > 0 && i == -'0') { 436 comspeed = j; 437 break; 438 } 439 /* Fall through to error below ('S' not in optstr[]). */ 440 } 441 for (i = 0; c != optstr[i]; i++) 442 if (i == NOPT - 1) 443 return -1; 444 opts ^= OPT_SET(flags[i]); 445 } 446 ioctrl = OPT_CHECK(RBX_DUAL) ? (IO_SERIAL|IO_KEYBOARD) : 447 OPT_CHECK(RBX_SERIAL) ? IO_SERIAL : IO_KEYBOARD; 448 if (ioctrl & IO_SERIAL) 449 sio_init(115200 / comspeed); 450 } else { 451 for (q = arg--; *q && *q != '('; q++); 452 if (*q) { 453 drv = -1; 454 if (arg[1] == ':') { 455 drv = *arg - '0'; 456 if (drv > 9) 457 return (-1); 458 arg += 2; 459 } 460 if (q - arg != 2) 461 return -1; 462 for (i = 0; arg[0] != dev_nm[i][0] || 463 arg[1] != dev_nm[i][1]; i++) 464 if (i == NDEV - 1) 465 return -1; 466 dsk.type = i; 467 arg += 3; 468 dsk.unit = *arg - '0'; 469 if (arg[1] != ',' || dsk.unit > 9) 470 return -1; 471 arg += 2; 472 dsk.part = -1; 473 if (arg[1] == ',') { 474 dsk.part = *arg - '0'; 475 if (dsk.part < 1 || dsk.part > 9) 476 return -1; 477 arg += 2; 478 } 479 if (arg[0] != ')') 480 return -1; 481 arg++; 482 if (drv == -1) 483 drv = dsk.unit; 484 dsk.drive = (dsk.type <= TYPE_MAXHARD 485 ? DRV_HARD : 0) + drv; 486 dsk_meta = 0; 487 } 488 if ((i = ep - arg)) { 489 if ((size_t)i >= sizeof(kname)) 490 return -1; 491 memcpy(kname, arg, i + 1); 492 } 493 } 494 arg = p; 495 } 496 return 0; 497} 498 499static int 500dskread(void *buf, daddr_t lba, unsigned nblk) 501{ 502 struct gpt_hdr hdr; 503 struct gpt_ent *ent; 504 char *sec; 505 daddr_t slba, elba; 506 int part, entries_per_sec; 507 508 if (!dsk_meta) { 509 /* Read and verify GPT. */ 510 sec = dmadat->secbuf; 511 dsk.start = 0; 512 if (drvread(sec, 1, 1)) 513 return -1; 514 memcpy(&hdr, sec, sizeof(hdr)); 515 if (bcmp(hdr.hdr_sig, GPT_HDR_SIG, sizeof(hdr.hdr_sig)) != 0 || 516 hdr.hdr_lba_self != 1 || hdr.hdr_revision < 0x00010000 || 517 hdr.hdr_entsz < sizeof(*ent) || DEV_BSIZE % hdr.hdr_entsz != 0) { 518 printf("Invalid GPT header\n"); 519 return -1; 520 } 521 522 /* XXX: CRC check? */ 523 524 /* 525 * If the partition isn't specified, then search for the first UFS 526 * partition and hope it is /. Perhaps we should be using an OS 527 * flag in the GPT entry to mark / partitions. 528 * 529 * If the partition is specified, then figure out the LBA for the 530 * sector containing that partition index and load it. 531 */ 532 entries_per_sec = DEV_BSIZE / hdr.hdr_entsz; 533 if (dsk.part == -1) { 534 slba = hdr.hdr_lba_table; 535 elba = slba + hdr.hdr_entries / entries_per_sec; 536 while (slba < elba && dsk.part == -1) { 537 if (drvread(sec, slba, 1)) 538 return -1; 539 for (part = 0; part < entries_per_sec; part++) { 540 ent = (struct gpt_ent *)(sec + part * hdr.hdr_entsz); 541 if (bcmp(&ent->ent_type, &freebsd_ufs_uuid, 542 sizeof(uuid_t)) == 0) { 543 dsk.part = (slba - hdr.hdr_lba_table) * 544 entries_per_sec + part + 1; 545 dsk.start = ent->ent_lba_start; 546 break; 547 } 548 } 549 slba++; 550 } 551 if (dsk.part == -1) { 552 printf("No UFS partition was found\n"); 553 return -1; 554 } 555 } else { 556 if (dsk.part > hdr.hdr_entries) { 557 printf("Invalid partition index\n"); 558 return -1; 559 } 560 slba = hdr.hdr_lba_table + (dsk.part - 1) / entries_per_sec; 561 if (drvread(sec, slba, 1)) 562 return -1; 563 part = (dsk.part - 1) % entries_per_sec; 564 ent = (struct gpt_ent *)(sec + part * hdr.hdr_entsz); 565 if (bcmp(&ent->ent_type, &freebsd_ufs_uuid, sizeof(uuid_t)) != 0) { 566 printf("Specified partition is not UFS\n"); 567 return -1; 568 } 569 dsk.start = ent->ent_lba_start; 570 } 571 /* 572 * XXX: No way to detect SCSI vs. ATA currently. 573 */ 574#if 0 575 if (!dsk.init) { 576 if (d->d_type == DTYPE_SCSI) 577 dsk.type = TYPE_DA; 578 dsk.init++; 579 } 580#endif 581 } 582 return drvread(buf, dsk.start + lba, nblk); 583} 584 585static void 586printf(const char *fmt,...) 587{ 588 va_list ap; 589 char buf[10]; 590 char *s; 591 unsigned u; 592 int c; 593 594 va_start(ap, fmt); 595 while ((c = *fmt++)) { 596 if (c == '%') { 597 c = *fmt++; 598 switch (c) { 599 case 'c': 600 putchar(va_arg(ap, int)); 601 continue; 602 case 's': 603 for (s = va_arg(ap, char *); *s; s++) 604 putchar(*s); 605 continue; 606 case 'u': 607 u = va_arg(ap, unsigned); 608 s = buf; 609 do 610 *s++ = '0' + u % 10U; 611 while (u /= 10U); 612 while (--s >= buf) 613 putchar(*s); 614 continue; 615 } 616 } 617 putchar(c); 618 } 619 va_end(ap); 620 return; 621} 622 623static void 624putchar(int c) 625{ 626 if (c == '\n') 627 xputc('\r'); 628 xputc(c); 629} 630 631static int 632bcmp(const void *b1, const void *b2, size_t length) 633{ 634 const char *p1 = b1, *p2 = b2; 635 636 if (length == 0) 637 return (0); 638 do { 639 if (*p1++ != *p2++) 640 break; 641 } while (--length); 642 return (length); 643} 644 645static struct { 646 uint16_t len; 647 uint16_t count; 648 uint16_t seg; 649 uint16_t off; 650 uint64_t lba; 651} packet; 652 653static int 654drvread(void *buf, daddr_t lba, unsigned nblk) 655{ 656 static unsigned c = 0x2d5c7c2f; 657 658 if (!OPT_CHECK(RBX_QUIET)) 659 printf("%c\b", c = c << 8 | c >> 24); 660 packet.len = 0x10; 661 packet.count = nblk; 662 packet.seg = VTOPOFF(buf); 663 packet.off = VTOPSEG(buf); 664 packet.lba = lba; 665 v86.ctl = V86_FLAGS; 666 v86.addr = 0x13; 667 v86.eax = 0x4200; 668 v86.edx = dsk.drive; 669 v86.ds = VTOPSEG(&packet); 670 v86.esi = VTOPOFF(&packet); 671 v86int(); 672 if (V86_CY(v86.efl)) { 673 printf("error %u lba %u\n", v86.eax >> 8 & 0xff, lba); 674 return -1; 675 } 676 return 0; 677} 678 679static int 680keyhit(unsigned ticks) 681{ 682 uint32_t t0, t1; 683 684 if (OPT_CHECK(RBX_NOINTR)) 685 return 0; 686 t0 = 0; 687 for (;;) { 688 if (xgetc(1)) 689 return 1; 690 t1 = *(uint32_t *)PTOV(0x46c); 691 if (!t0) 692 t0 = t1; 693 if (t1 < t0 || t1 >= t0 + ticks) 694 return 0; 695 } 696} 697 698static int 699xputc(int c) 700{ 701 if (ioctrl & IO_KEYBOARD) 702 putc(c); 703 if (ioctrl & IO_SERIAL) 704 sio_putc(c); 705 return c; 706} 707 708static int 709xgetc(int fn) 710{ 711 if (OPT_CHECK(RBX_NOINTR)) 712 return 0; 713 for (;;) { 714 if (ioctrl & IO_KEYBOARD && getc(1)) 715 return fn ? 1 : getc(0); 716 if (ioctrl & IO_SERIAL && sio_ischar()) 717 return fn ? 1 : sio_getc(); 718 if (fn) 719 return 0; 720 } 721} 722 723static int 724getc(int fn) 725{ 726 v86.addr = 0x16; 727 v86.eax = fn << 8; 728 v86int(); 729 return fn == 0 ? v86.eax & 0xff : !V86_ZR(v86.efl); 730} 731