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