gptboot.c revision 132764
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 132764 2004-07-28 06:03:27Z 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(char *, const char *, int); 143static void 144memcpy(char *dst, const char *src, int len) 145{ 146 while (len--) 147 *dst++ = *src++; 148} 149 150static inline int 151strcmp(const char *s1, const char *s2) 152{ 153 for (; *s1 == *s2 && *s1; s1++, s2++); 154 return (unsigned char)*s1 - (unsigned char)*s2; 155} 156 157#include "ufsread.c" 158 159static int 160xfsread(ino_t inode, void *buf, size_t nbyte) 161{ 162 if ((size_t)fsread(inode, buf, nbyte) != nbyte) { 163 printf("Invalid %s\n", "format"); 164 return -1; 165 } 166 return 0; 167} 168 169static inline uint32_t 170memsize(void) 171{ 172 v86.addr = MEM_EXT; 173 v86.eax = 0x8800; 174 v86int(); 175 return v86.eax; 176} 177 178static inline void 179getstr(void) 180{ 181 char *s; 182 int c; 183 184 s = cmd; 185 for (;;) { 186 switch (c = xgetc(0)) { 187 case 0: 188 break; 189 case '\177': 190 case '\b': 191 if (s > cmd) { 192 s--; 193 printf("\b \b"); 194 } 195 break; 196 case '\n': 197 case '\r': 198 *s = 0; 199 return; 200 default: 201 if (s - cmd < sizeof(cmd) - 1) 202 *s++ = c; 203 putchar(c); 204 } 205 } 206} 207 208static inline void 209putc(int c) 210{ 211 v86.addr = 0x10; 212 v86.eax = 0xe00 | (c & 0xff); 213 v86.ebx = 0x7; 214 v86int(); 215} 216 217int 218main(void) 219{ 220 int autoboot; 221 ino_t ino; 222 223 dmadat = (void *)(roundup2(__base + (int32_t)&_end, 0x10000) - __base); 224 v86.ctl = V86_FLAGS; 225 dsk.drive = *(uint8_t *)PTOV(ARGS); 226 dsk.type = dsk.drive & DRV_HARD ? TYPE_AD : TYPE_FD; 227 dsk.unit = dsk.drive & DRV_MASK; 228 dsk.slice = *(uint8_t *)PTOV(ARGS + 1) + 1; 229 bootinfo.bi_version = BOOTINFO_VERSION; 230 bootinfo.bi_size = sizeof(bootinfo); 231 bootinfo.bi_basemem = 0; /* XXX will be filled by loader or kernel */ 232 bootinfo.bi_extmem = memsize(); 233 bootinfo.bi_memsizes_valid++; 234 235 /* Process configuration file */ 236 237 autoboot = 1; 238 239 if ((ino = lookup(PATH_CONFIG))) 240 fsread(ino, cmd, sizeof(cmd)); 241 242 if (*cmd) { 243 printf("%s: %s", PATH_CONFIG, cmd); 244 if (parse()) 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(); 259 memcpy(kname, PATH_KERNEL, sizeof(PATH_KERNEL)); 260 } 261 } 262 263 /* Present the user with the boot2 prompt. */ 264 265 for (;;) { 266 printf("\nFreeBSD/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(); 275 else 276 putchar('\n'); 277 autoboot = 0; 278 if (parse()) 279 putchar('\a'); 280 else 281 load(); 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(void) 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(kname))) { 306 if (!ls) 307 printf("No %s\n", kname); 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(kname); 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() 388{ 389 char *arg = cmd; 390 char *p, *q; 391 unsigned int drv; 392 int c, i; 393 394 while ((c = *arg++)) { 395 if (c == ' ' || c == '\t' || c == '\n') 396 continue; 397 for (p = arg; *p && *p != '\n' && *p != ' ' && *p != '\t'; p++); 398 if (*p) 399 *p++ = 0; 400 if (c == '-') { 401 while ((c = *arg++)) { 402 for (i = 0; c != optstr[i]; i++) 403 if (i == NOPT - 1) 404 return -1; 405 opts ^= 1 << flags[i]; 406 } 407 if (opts & 1 << RBX_PROBEKBD) { 408 i = *(uint8_t *)PTOV(0x496) & 0x10; 409 printf("Keyboard: %s\n", i ? "yes" : "no"); 410 if (!i) 411 opts |= 1 << RBX_DUAL | 1 << RBX_SERIAL; 412 opts &= ~(1 << RBX_PROBEKBD); 413 } 414 ioctrl = opts & 1 << RBX_DUAL ? (IO_SERIAL|IO_KEYBOARD) : 415 opts & 1 << RBX_SERIAL ? IO_SERIAL : IO_KEYBOARD; 416 if (ioctrl & IO_SERIAL) 417 sio_init(); 418 } else { 419 for (q = arg--; *q && *q != '('; q++); 420 if (*q) { 421 drv = -1; 422 if (arg[1] == ':') { 423 drv = *arg - '0'; 424 if (drv > 9) 425 return (-1); 426 arg += 2; 427 } 428 if (q - arg != 2) 429 return -1; 430 for (i = 0; arg[0] != dev_nm[i][0] || 431 arg[1] != dev_nm[i][1]; i++) 432 if (i == NDEV - 1) 433 return -1; 434 dsk.type = i; 435 arg += 3; 436 dsk.unit = *arg - '0'; 437 if (arg[1] != ',' || dsk.unit > 9) 438 return -1; 439 arg += 2; 440 dsk.slice = WHOLE_DISK_SLICE; 441 if (arg[1] == ',') { 442 dsk.slice = *arg - '0' + 1; 443 if (dsk.slice > NDOSPART) 444 return -1; 445 arg += 2; 446 } 447 if (arg[1] != ')') 448 return -1; 449 dsk.part = *arg - 'a'; 450 if (dsk.part > 7) 451 return (-1); 452 arg += 2; 453 if (drv == -1) 454 drv = dsk.unit; 455 dsk.drive = (dsk.type <= TYPE_MAXHARD 456 ? DRV_HARD : 0) + drv; 457 dsk_meta = 0; 458 } 459 if ((i = p - arg - !*(p - 1))) { 460 if ((size_t)i >= sizeof(kname)) 461 return -1; 462 memcpy(kname, arg, i + 1); 463 } 464 } 465 arg = p; 466 } 467 return 0; 468} 469 470static int 471dskread(void *buf, unsigned lba, unsigned nblk) 472{ 473 struct dos_partition *dp; 474 struct disklabel *d; 475 char *sec; 476 unsigned sl, i; 477 478 if (!dsk_meta) { 479 sec = dmadat->secbuf; 480 dsk.start = 0; 481 if (drvread(sec, DOSBBSECTOR, 1)) 482 return -1; 483 dp = (void *)(sec + DOSPARTOFF); 484 sl = dsk.slice; 485 if (sl < BASE_SLICE) { 486 for (i = 0; i < NDOSPART; i++) 487 if (dp[i].dp_typ == DOSPTYP_386BSD && 488 (dp[i].dp_flag & 0x80 || sl < BASE_SLICE)) { 489 sl = BASE_SLICE + i; 490 if (dp[i].dp_flag & 0x80 || 491 dsk.slice == COMPATIBILITY_SLICE) 492 break; 493 } 494 if (dsk.slice == WHOLE_DISK_SLICE) 495 dsk.slice = sl; 496 } 497 if (sl != WHOLE_DISK_SLICE) { 498 if (sl != COMPATIBILITY_SLICE) 499 dp += sl - BASE_SLICE; 500 if (dp->dp_typ != DOSPTYP_386BSD) { 501 printf("Invalid %s\n", "slice"); 502 return -1; 503 } 504 dsk.start = dp->dp_start; 505 } 506 if (drvread(sec, dsk.start + LABELSECTOR, 1)) 507 return -1; 508 d = (void *)(sec + LABELOFFSET); 509 if (d->d_magic != DISKMAGIC || d->d_magic2 != DISKMAGIC) { 510 if (dsk.part != RAW_PART) { 511 printf("Invalid %s\n", "label"); 512 return -1; 513 } 514 } else { 515 if (!dsk.init) { 516 if (d->d_type == DTYPE_SCSI) 517 dsk.type = TYPE_DA; 518 dsk.init++; 519 } 520 if (dsk.part >= d->d_npartitions || 521 !d->d_partitions[dsk.part].p_size) { 522 printf("Invalid %s\n", "partition"); 523 return -1; 524 } 525 dsk.start += d->d_partitions[dsk.part].p_offset; 526 dsk.start -= d->d_partitions[RAW_PART].p_offset; 527 } 528 } 529 return drvread(buf, dsk.start + lba, nblk); 530} 531 532static void 533printf(const char *fmt,...) 534{ 535 va_list ap; 536 char buf[10]; 537 char *s; 538 unsigned u; 539 int c; 540 541 va_start(ap, fmt); 542 while ((c = *fmt++)) { 543 if (c == '%') { 544 c = *fmt++; 545 switch (c) { 546 case 'c': 547 putchar(va_arg(ap, int)); 548 continue; 549 case 's': 550 for (s = va_arg(ap, char *); *s; s++) 551 putchar(*s); 552 continue; 553 case 'u': 554 u = va_arg(ap, unsigned); 555 s = buf; 556 do 557 *s++ = '0' + u % 10U; 558 while (u /= 10U); 559 while (--s >= buf) 560 putchar(*s); 561 continue; 562 } 563 } 564 putchar(c); 565 } 566 va_end(ap); 567 return; 568} 569 570static void 571putchar(int c) 572{ 573 if (c == '\n') 574 xputc('\r'); 575 xputc(c); 576} 577 578static int 579drvread(void *buf, unsigned lba, unsigned nblk) 580{ 581 static unsigned c = 0x2d5c7c2f; 582 583 printf("%c\b", c = c << 8 | c >> 24); 584 v86.ctl = V86_ADDR | V86_CALLF | V86_FLAGS; 585 v86.addr = XREADORG; /* call to xread in boot1 */ 586 v86.es = VTOPSEG(buf); 587 v86.eax = lba; 588 v86.ebx = VTOPOFF(buf); 589 v86.ecx = lba >> 16; 590 v86.edx = nblk << 8 | dsk.drive; 591 v86int(); 592 v86.ctl = V86_FLAGS; 593 if (V86_CY(v86.efl)) { 594 printf("error %u lba %u\n", v86.eax >> 8 & 0xff, lba); 595 return -1; 596 } 597 return 0; 598} 599 600static int 601keyhit(unsigned ticks) 602{ 603 uint32_t t0, t1; 604 605 if (opts & 1 << RBX_NOINTR) 606 return 0; 607 t0 = 0; 608 for (;;) { 609 if (xgetc(1)) 610 return 1; 611 t1 = *(uint32_t *)PTOV(0x46c); 612 if (!t0) 613 t0 = t1; 614 if (t1 < t0 || t1 >= t0 + ticks) 615 return 0; 616 } 617} 618 619static int 620xputc(int c) 621{ 622 if (ioctrl & IO_KEYBOARD) 623 putc(c); 624 if (ioctrl & IO_SERIAL) 625 sio_putc(c); 626 return c; 627} 628 629static int 630xgetc(int fn) 631{ 632 if (opts & 1 << RBX_NOINTR) 633 return 0; 634 for (;;) { 635 if (ioctrl & IO_KEYBOARD && getc(1)) 636 return fn ? 1 : getc(0); 637 if (ioctrl & IO_SERIAL && sio_ischar()) 638 return fn ? 1 : sio_getc(); 639 if (fn) 640 return 0; 641 } 642} 643 644static int 645getc(int fn) 646{ 647 v86.addr = 0x16; 648 v86.eax = fn << 8; 649 v86int(); 650 return fn == 0 ? v86.eax & 0xff : !V86_ZR(v86.efl); 651} 652