gptboot.c revision 107875
1231200Smm/* 2231200Smm * Copyright (c) 1998 Robert Nordier 3231200Smm * All rights reserved. 4231200Smm * 5231200Smm * Redistribution and use in source and binary forms are freely 6231200Smm * permitted provided that the above copyright notice and this 7231200Smm * paragraph and the following disclaimer are duplicated in all 8231200Smm * such forms. 9231200Smm * 10231200Smm * This software is provided "AS IS" and without any express or 11231200Smm * implied warranties, including, without limitation, the implied 12231200Smm * warranties of merchantability and fitness for a particular 13231200Smm * purpose. 14231200Smm */ 15231200Smm 16231200Smm/* 17231200Smm * $FreeBSD: head/sys/boot/i386/gptboot/gptboot.c 107875 2002-12-14 19:15:27Z phk $ 18231200Smm */ 19231200Smm 20231200Smm#include <sys/param.h> 21231200Smm#include <sys/reboot.h> 22231200Smm#include <sys/diskslice.h> 23231200Smm#include <sys/disklabel.h> 24231200Smm#include <sys/diskmbr.h> 25231200Smm#include <sys/dirent.h> 26231200Smm#include <machine/bootinfo.h> 27231200Smm#include <machine/elf.h> 28231200Smm 29231200Smm#include <stdarg.h> 30231200Smm 31231200Smm#include <a.out.h> 32231200Smm 33231200Smm#include <btxv86.h> 34231200Smm 35231200Smm#include "boot2.h" 36231200Smm#include "lib.h" 37231200Smm 38231200Smm#define IO_KEYBOARD 1 39231200Smm#define IO_SERIAL 2 40231200Smm 41231200Smm#define SECOND 18 /* Circa that many ticks in a second. */ 42231200Smm 43231200Smm#define RBX_ASKNAME 0x0 /* -a */ 44231200Smm#define RBX_SINGLE 0x1 /* -s */ 45231200Smm#define RBX_DFLTROOT 0x5 /* -r */ 46231200Smm#define RBX_KDB 0x6 /* -d */ 47231200Smm#define RBX_CONFIG 0xa /* -c */ 48231200Smm#define RBX_VERBOSE 0xb /* -v */ 49231200Smm#define RBX_SERIAL 0xc /* -h */ 50231200Smm#define RBX_CDROM 0xd /* -C */ 51231200Smm#define RBX_GDB 0xf /* -g */ 52231200Smm#define RBX_MUTE 0x10 /* -m */ 53231200Smm#define RBX_PAUSE 0x12 /* -p */ 54231200Smm#define RBX_DUAL 0x1d /* -D */ 55231200Smm#define RBX_PROBEKBD 0x1e /* -P */ 56231200Smm#define RBX_NOINTR 0x1f /* -n */ 57231200Smm 58231200Smm#define RBX_MASK 0x2005ffff 59231200Smm 60231200Smm#define PATH_CONFIG "/boot.config" 61231200Smm#define PATH_BOOT3 "/boot/loader" 62231200Smm#define PATH_KERNEL "/kernel" 63231200Smm 64231200Smm#define ARGS 0x900 65231200Smm#define NOPT 14 66231200Smm#define NDEV 5 67231200Smm#define MEM_BASE 0x12 68231200Smm#define MEM_EXT 0x15 69231200Smm#define V86_CY(x) ((x) & 1) 70231200Smm#define V86_ZR(x) ((x) & 0x40) 71231200Smm 72231200Smm#define DRV_HARD 0x80 73231200Smm#define DRV_MASK 0x7f 74231200Smm 75231200Smm#define TYPE_AD 0 76231200Smm#define TYPE_DA 2 77231200Smm#define TYPE_MAXHARD TYPE_DA 78231200Smm#define TYPE_FD 4 79231200Smm 80231200Smmextern uint32_t _end; 81231200Smm 82231200Smmstatic const char optstr[NOPT] = "DhaCcdgmnPprsv"; 83231200Smmstatic const unsigned char flags[NOPT] = { 84231200Smm RBX_DUAL, 85231200Smm RBX_SERIAL, 86231200Smm RBX_ASKNAME, 87231200Smm RBX_CDROM, 88231200Smm RBX_CONFIG, 89231200Smm RBX_KDB, 90231200Smm RBX_GDB, 91231200Smm RBX_MUTE, 92231200Smm RBX_NOINTR, 93231200Smm RBX_PROBEKBD, 94231200Smm RBX_PAUSE, 95231200Smm RBX_DFLTROOT, 96231200Smm RBX_SINGLE, 97231200Smm RBX_VERBOSE 98231200Smm}; 99231200Smm 100231200Smmstatic const char *const dev_nm[NDEV] = {"ad", "wd", "da", " ", "fd"}; 101231200Smmstatic const unsigned char dev_maj[NDEV] = {30, 0, 4, 1, 2}; 102231200Smm 103231200Smmstatic struct dsk { 104231200Smm unsigned drive; 105231200Smm unsigned type; 106231200Smm unsigned unit; 107231200Smm unsigned slice; 108231200Smm unsigned part; 109231200Smm unsigned start; 110231200Smm int init; 111231200Smm} dsk; 112231200Smmstatic char cmd[512]; 113231200Smmstatic char kname[1024]; 114231200Smmstatic uint32_t opts = RB_BOOTINFO; 115231200Smmstatic struct bootinfo bootinfo; 116231200Smmstatic uint8_t ioctrl = IO_KEYBOARD; 117231200Smm 118231200Smmvoid exit(int); 119231200Smmstatic void load(const char *); 120231200Smmstatic int parse(char *); 121231200Smmstatic int xfsread(ino_t, void *, size_t); 122231200Smmstatic int dskread(void *, unsigned, unsigned); 123231200Smmstatic void printf(const char *,...); 124231200Smmstatic void putchar(int); 125231200Smmstatic uint32_t memsize(int); 126231200Smmstatic int drvread(void *, unsigned, unsigned); 127231200Smmstatic int keyhit(unsigned); 128231200Smmstatic int xputc(int); 129231200Smmstatic int xgetc(int); 130231200Smmstatic int getc(int); 131231200Smm 132231200Smm#define memcpy __builtin_memcpy 133231200Smm 134231200Smmstatic inline int 135231200Smmstrcmp(const char *s1, const char *s2) 136231200Smm{ 137231200Smm for (; *s1 == *s2 && *s1; s1++, s2++); 138231200Smm return (u_char)*s1 - (u_char)*s2; 139231200Smm} 140231200Smm 141231200Smm#include "ufsread.c" 142231200Smm 143231200Smmstatic int 144231200Smmxfsread(ino_t inode, void *buf, size_t nbyte) 145231200Smm{ 146231200Smm if (fsread(inode, buf, nbyte) != nbyte) { 147231200Smm printf("Invalid %s\n", "format"); 148231200Smm return -1; 149231200Smm } 150231200Smm return 0; 151231200Smm} 152231200Smm 153231200Smmstatic inline void 154231200Smmgetstr(char *str, int size) 155231200Smm{ 156231200Smm char *s; 157231200Smm int c; 158231200Smm 159231200Smm s = str; 160231200Smm for (;;) { 161231200Smm switch (c = xgetc(0)) { 162231200Smm case 0: 163231200Smm break; 164231200Smm case '\177': 165231200Smm c = '\b'; 166231200Smm case '\b': 167231200Smm if (s > str) { 168231200Smm s--; 169231200Smm putchar('\b'); 170231200Smm putchar(' '); 171231200Smm } else 172231200Smm c = 0; 173231200Smm break; 174231200Smm case '\n': 175231200Smm case '\r': 176231200Smm *s = 0; 177231200Smm return; 178231200Smm default: 179231200Smm if (s - str < size - 1) 180231200Smm *s++ = c; 181231200Smm } 182231200Smm if (c) 183231200Smm putchar(c); 184231200Smm } 185231200Smm} 186231200Smm 187231200Smmstatic inline void 188231200Smmputc(int c) 189231200Smm{ 190231200Smm v86.addr = 0x10; 191231200Smm v86.eax = 0xe00 | (c & 0xff); 192231200Smm v86.ebx = 0x7; 193231200Smm v86int(); 194231200Smm} 195231200Smm 196231200Smmint 197231200Smmmain(void) 198231200Smm{ 199231200Smm int autoboot; 200231200Smm ino_t ino; 201231200Smm 202231200Smm dmadat = (void *)(roundup2(__base + (int32_t)&_end, 0x10000) - __base); 203231200Smm v86.ctl = V86_FLAGS; 204231200Smm dsk.drive = *(uint8_t *)PTOV(ARGS); 205231200Smm dsk.type = dsk.drive & DRV_HARD ? TYPE_AD : TYPE_FD; 206231200Smm dsk.unit = dsk.drive & DRV_MASK; 207231200Smm dsk.slice = *(uint8_t *)PTOV(ARGS + 1) + 1; 208231200Smm bootinfo.bi_version = BOOTINFO_VERSION; 209231200Smm bootinfo.bi_size = sizeof(bootinfo); 210231200Smm bootinfo.bi_basemem = 0; /* XXX will be filled by loader or kernel */ 211231200Smm bootinfo.bi_extmem = memsize(MEM_EXT); 212231200Smm bootinfo.bi_memsizes_valid++; 213231200Smm 214231200Smm /* Process configuration file */ 215231200Smm 216231200Smm autoboot = 1; 217231200Smm 218231200Smm if ((ino = lookup(PATH_CONFIG))) 219231200Smm fsread(ino, cmd, sizeof(cmd)); 220231200Smm 221231200Smm if (*cmd) { 222231200Smm printf("%s: %s", PATH_CONFIG, cmd); 223231200Smm if (parse(cmd)) 224231200Smm autoboot = 0; 225231200Smm /* Do not process this command twice */ 226231200Smm *cmd = 0; 227231200Smm } 228231200Smm 229231200Smm /* 230231200Smm * Try to exec stage 3 boot loader. If interrupted by a keypress, 231231200Smm * or in case of failure, try to load a kernel directly instead. 232231200Smm */ 233231200Smm 234231200Smm if (autoboot && !*kname) { 235231200Smm memcpy(kname, PATH_BOOT3, sizeof(PATH_BOOT3)); 236231200Smm if (!keyhit(3*SECOND)) { 237231200Smm load(kname); 238231200Smm memcpy(kname, PATH_KERNEL, sizeof(PATH_KERNEL)); 239231200Smm } 240231200Smm } 241231200Smm 242231200Smm /* Present the user with the boot2 prompt. */ 243231200Smm 244231200Smm for (;;) { 245231200Smm#ifdef UFS1_ONLY 246231200Smm printf(" \n>> FreeBSD/i386/UFS1 BOOT\n" 247231200Smm#else 248231200Smm printf(" \n>> FreeBSD/i386/UFS[12] BOOT\n" 249231200Smm#endif 250248616Smm "Default: %u:%s(%u,%c)%s\n" 251231200Smm "boot: ", 252231200Smm dsk.drive & DRV_MASK, dev_nm[dsk.type], dsk.unit, 253231200Smm 'a' + dsk.part, kname); 254231200Smm if (ioctrl & IO_SERIAL) 255231200Smm sio_flush(); 256231200Smm if (!autoboot || keyhit(5*SECOND)) 257231200Smm getstr(cmd, sizeof(cmd)); 258231200Smm else 259231200Smm putchar('\n'); 260231200Smm autoboot = 0; 261231200Smm if (parse(cmd)) 262231200Smm putchar('\a'); 263231200Smm else 264231200Smm load(kname); 265231200Smm } 266231200Smm} 267231200Smm 268231200Smm/* XXX - Needed for btxld to link the boot2 binary; do not remove. */ 269231200Smmvoid 270231200Smmexit(int x) 271231200Smm{ 272231200Smm} 273231200Smm 274231200Smmstatic void 275231200Smmload(const char *fname) 276231200Smm{ 277231200Smm union { 278231200Smm struct exec ex; 279 Elf32_Ehdr eh; 280 } hdr; 281 Elf32_Phdr ep[2]; 282 Elf32_Shdr es[2]; 283 caddr_t p; 284 ino_t ino; 285 uint32_t addr, x; 286 int fmt, i, j; 287 288 if (!(ino = lookup(fname))) { 289 if (!ls) 290 printf("No %s\n", fname); 291 return; 292 } 293 if (xfsread(ino, &hdr, sizeof(hdr))) 294 return; 295 if (N_GETMAGIC(hdr.ex) == ZMAGIC) 296 fmt = 0; 297 else if (IS_ELF(hdr.eh)) 298 fmt = 1; 299 else { 300 printf("Invalid %s\n", "format"); 301 return; 302 } 303 if (fmt == 0) { 304 addr = hdr.ex.a_entry & 0xffffff; 305 p = PTOV(addr); 306 fs_off = PAGE_SIZE; 307 if (xfsread(ino, p, hdr.ex.a_text)) 308 return; 309 p += roundup2(hdr.ex.a_text, PAGE_SIZE); 310 if (xfsread(ino, p, hdr.ex.a_data)) 311 return; 312 p += hdr.ex.a_data + roundup2(hdr.ex.a_bss, PAGE_SIZE); 313 bootinfo.bi_symtab = VTOP(p); 314 memcpy(p, (char *)&hdr.ex.a_syms, sizeof(hdr.ex.a_syms)); 315 p += sizeof(hdr.ex.a_syms); 316 if (hdr.ex.a_syms) { 317 if (xfsread(ino, p, hdr.ex.a_syms)) 318 return; 319 p += hdr.ex.a_syms; 320 if (xfsread(ino, p, sizeof(int))) 321 return; 322 x = *(uint32_t *)p; 323 p += sizeof(int); 324 x -= sizeof(int); 325 if (xfsread(ino, p, x)) 326 return; 327 p += x; 328 } 329 } else { 330 fs_off = hdr.eh.e_phoff; 331 for (j = i = 0; i < hdr.eh.e_phnum && j < 2; i++) { 332 if (xfsread(ino, ep + j, sizeof(ep[0]))) 333 return; 334 if (ep[j].p_type == PT_LOAD) 335 j++; 336 } 337 for (i = 0; i < 2; i++) { 338 p = PTOV(ep[i].p_paddr & 0xffffff); 339 fs_off = ep[i].p_offset; 340 if (xfsread(ino, p, ep[i].p_filesz)) 341 return; 342 } 343 p += roundup2(ep[1].p_memsz, PAGE_SIZE); 344 bootinfo.bi_symtab = VTOP(p); 345 if (hdr.eh.e_shnum == hdr.eh.e_shstrndx + 3) { 346 fs_off = hdr.eh.e_shoff + sizeof(es[0]) * 347 (hdr.eh.e_shstrndx + 1); 348 if (xfsread(ino, &es, sizeof(es))) 349 return; 350 for (i = 0; i < 2; i++) { 351 memcpy(p, (char *)&es[i].sh_size, sizeof(es[i].sh_size)); 352 p += sizeof(es[i].sh_size); 353 fs_off = es[i].sh_offset; 354 if (xfsread(ino, p, es[i].sh_size)) 355 return; 356 p += es[i].sh_size; 357 } 358 } 359 addr = hdr.eh.e_entry & 0xffffff; 360 } 361 bootinfo.bi_esymtab = VTOP(p); 362 bootinfo.bi_kernelname = VTOP(fname); 363 bootinfo.bi_bios_dev = dsk.drive; 364 __exec((caddr_t)addr, opts & RBX_MASK, 365 MAKEBOOTDEV(dev_maj[dsk.type], 0, dsk.slice, dsk.unit, dsk.part), 366 0, 0, 0, VTOP(&bootinfo)); 367} 368 369static int 370parse(char *arg) 371{ 372 char *p, *q; 373 int drv, c, i; 374 375 while ((c = *arg++)) { 376 if (c == ' ' || c == '\t' || c == '\n') 377 continue; 378 for (p = arg; *p && *p != '\n' && *p != ' ' && *p != '\t'; p++); 379 if (*p) 380 *p++ = 0; 381 if (c == '-') { 382 while ((c = *arg++)) { 383 for (i = 0; c != optstr[i]; i++) 384 if (i == NOPT - 1) 385 return -1; 386 opts ^= 1 << flags[i]; 387 } 388 if (opts & 1 << RBX_PROBEKBD) { 389 i = *(uint8_t *)PTOV(0x496) & 0x10; 390 /* printf("Keyboard: %s\n", i ? "yes" : "no"); */ 391 if (!i) 392 opts |= 1 << RBX_DUAL | 1 << RBX_SERIAL; 393 opts &= ~(1 << RBX_PROBEKBD); 394 } 395 ioctrl = opts & 1 << RBX_DUAL ? (IO_SERIAL|IO_KEYBOARD) : 396 opts & 1 << RBX_SERIAL ? IO_SERIAL : IO_KEYBOARD; 397 if (ioctrl & IO_SERIAL) 398 sio_init(); 399 } else { 400 for (q = arg--; *q && *q != '('; q++); 401 if (*q) { 402 drv = -1; 403 if (arg[1] == ':') { 404 if (*arg < '0' || *arg > '9') 405 return -1; 406 drv = *arg - '0'; 407 arg += 2; 408 } 409 if (q - arg != 2) 410 return -1; 411 for (i = 0; arg[0] != dev_nm[i][0] || 412 arg[1] != dev_nm[i][1]; i++) 413 if (i == NDEV - 1) 414 return -1; 415 dsk.type = i; 416 arg += 3; 417 if (arg[1] != ',' || *arg < '0' || *arg > '9') 418 return -1; 419 dsk.unit = *arg - '0'; 420 arg += 2; 421 dsk.slice = WHOLE_DISK_SLICE; 422 if (arg[1] == ',') { 423 if (*arg < '0' || *arg > '0' + NDOSPART) 424 return -1; 425 if ((dsk.slice = *arg - '0')) 426 dsk.slice++; 427 arg += 2; 428 } 429 if (arg[1] != ')' || *arg < 'a' || *arg > 'p') 430 return -1; 431 dsk.part = *arg - 'a'; 432 arg += 2; 433 if (drv == -1) 434 drv = dsk.unit; 435 dsk.drive = (dsk.type <= TYPE_MAXHARD 436 ? DRV_HARD : 0) + drv; 437 dsk_meta = 0; 438 fsread(0, NULL, 0); 439 } 440 if ((i = p - arg - !*(p - 1))) { 441 if (i >= sizeof(kname)) 442 return -1; 443 memcpy(kname, arg, i + 1); 444 } 445 } 446 arg = p; 447 } 448 return 0; 449} 450 451static int 452dskread(void *buf, unsigned lba, unsigned nblk) 453{ 454 struct dos_partition *dp; 455 struct disklabel *d; 456 char *sec; 457 unsigned sl, i; 458 459 if (!dsk_meta) { 460 sec = dmadat->secbuf; 461 dsk.start = 0; 462 if (drvread(sec, DOSBBSECTOR, 1)) 463 return -1; 464 dp = (void *)(sec + DOSPARTOFF); 465 sl = dsk.slice; 466 if (sl < BASE_SLICE) { 467 for (i = 0; i < NDOSPART; i++) 468 if (dp[i].dp_typ == DOSPTYP_386BSD && 469 (dp[i].dp_flag & 0x80 || sl < BASE_SLICE)) { 470 sl = BASE_SLICE + i; 471 if (dp[i].dp_flag & 0x80 || 472 dsk.slice == COMPATIBILITY_SLICE) 473 break; 474 } 475 if (dsk.slice == WHOLE_DISK_SLICE) 476 dsk.slice = sl; 477 } 478 if (sl != WHOLE_DISK_SLICE) { 479 if (sl != COMPATIBILITY_SLICE) 480 dp += sl - BASE_SLICE; 481 if (dp->dp_typ != DOSPTYP_386BSD) { 482 printf("Invalid %s\n", "slice"); 483 return -1; 484 } 485 dsk.start = dp->dp_start; 486 } 487 if (drvread(sec, dsk.start + LABELSECTOR, 1)) 488 return -1; 489 d = (void *)(sec + LABELOFFSET); 490 if (d->d_magic != DISKMAGIC || d->d_magic2 != DISKMAGIC) { 491 if (dsk.part != RAW_PART) { 492 printf("Invalid %s\n", "label"); 493 return -1; 494 } 495 } else { 496 if (!dsk.init) { 497 if (d->d_type == DTYPE_SCSI) 498 dsk.type = TYPE_DA; 499 dsk.init++; 500 } 501 if (dsk.part >= d->d_npartitions || 502 !d->d_partitions[dsk.part].p_size) { 503 printf("Invalid %s\n", "partition"); 504 return -1; 505 } 506 dsk.start += d->d_partitions[dsk.part].p_offset; 507 dsk.start -= d->d_partitions[RAW_PART].p_offset; 508 } 509 } 510 return drvread(buf, dsk.start + lba, nblk); 511} 512 513static void 514printf(const char *fmt,...) 515{ 516 static const char digits[16] = "0123456789abcdef"; 517 va_list ap; 518 char buf[10]; 519 char *s; 520 unsigned r, u; 521 int c; 522 523 va_start(ap, fmt); 524 while ((c = *fmt++)) { 525 if (c == '%') { 526 c = *fmt++; 527 switch (c) { 528 case 'c': 529 putchar(va_arg(ap, int)); 530 continue; 531 case 's': 532 for (s = va_arg(ap, char *); *s; s++) 533 putchar(*s); 534 continue; 535 case 'u': 536 case 'x': 537 r = c == 'u' ? 10U : 16U; 538 u = va_arg(ap, unsigned); 539 s = buf; 540 do 541 *s++ = digits[u % r]; 542 while (u /= r); 543 while (--s >= buf) 544 putchar(*s); 545 continue; 546 } 547 } 548 putchar(c); 549 } 550 va_end(ap); 551 return; 552} 553 554static void 555putchar(int c) 556{ 557 if (c == '\n') 558 xputc('\r'); 559 xputc(c); 560} 561 562static uint32_t 563memsize(int type) 564{ 565 v86.addr = type; 566 v86.eax = 0x8800; 567 v86int(); 568 return v86.eax; 569} 570 571static int 572drvread(void *buf, unsigned lba, unsigned nblk) 573{ 574 static unsigned c = 0x2d5c7c2f; 575 576 printf("%c\b", c = c << 8 | c >> 24); 577 v86.ctl = V86_ADDR | V86_CALLF | V86_FLAGS; 578 v86.addr = XREADORG; /* call to xread in boot1 */ 579 v86.es = VTOPSEG(buf); 580 v86.eax = lba; 581 v86.ebx = VTOPOFF(buf); 582 v86.ecx = lba >> 16; 583 v86.edx = nblk << 8 | dsk.drive; 584 v86int(); 585 v86.ctl = V86_FLAGS; 586 if (V86_CY(v86.efl)) { 587 printf("Disk error 0x%x lba 0x%x\n", v86.eax >> 8 & 0xff, lba); 588 return -1; 589 } 590 return 0; 591} 592 593static int 594keyhit(unsigned ticks) 595{ 596 uint32_t t0, t1; 597 598 if (opts & 1 << RBX_NOINTR) 599 return 0; 600 t0 = 0; 601 for (;;) { 602 if (xgetc(1)) 603 return 1; 604 t1 = *(uint32_t *)PTOV(0x46c); 605 if (!t0) 606 t0 = t1; 607 if (t1 < t0 || t1 >= t0 + ticks) 608 return 0; 609 } 610} 611 612static int 613xputc(int c) 614{ 615 if (ioctrl & IO_KEYBOARD) 616 putc(c); 617 if (ioctrl & IO_SERIAL) 618 sio_putc(c); 619 return c; 620} 621 622static int 623xgetc(int fn) 624{ 625 if (opts & 1 << RBX_NOINTR) 626 return 0; 627 for (;;) { 628 if (ioctrl & IO_KEYBOARD && getc(1)) 629 return fn ? 1 : getc(0); 630 if (ioctrl & IO_SERIAL && sio_ischar()) 631 return fn ? 1 : sio_getc(); 632 if (fn) 633 return 0; 634 } 635} 636 637static int 638getc(int fn) 639{ 640 v86.addr = 0x16; 641 v86.eax = fn << 8; 642 v86int(); 643 return fn == 0 ? v86.eax & 0xff : !V86_ZR(v86.efl); 644} 645