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