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