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