gptboot.c revision 98542
1343171Sdim/* 2343171Sdim * Copyright (c) 1998 Robert Nordier 3353358Sdim * All rights reserved. 4353358Sdim * 5353358Sdim * Redistribution and use in source and binary forms are freely 6343171Sdim * permitted provided that the above copyright notice and this 7343171Sdim * paragraph and the following disclaimer are duplicated in all 8343171Sdim * such forms. 9343171Sdim * 10343171Sdim * This software is provided "AS IS" and without any express or 11343171Sdim * implied warranties, including, without limitation, the implied 12343171Sdim * warranties of merchantability and fitness for a particular 13343171Sdim * purpose. 14343171Sdim */ 15343171Sdim 16343171Sdim/* 17343171Sdim * $FreeBSD: head/sys/boot/i386/gptboot/gptboot.c 98542 2002-06-21 06:18:05Z mckusick $ 18343171Sdim */ 19343171Sdim 20343171Sdim#include <sys/param.h> 21343171Sdim#include <sys/reboot.h> 22343171Sdim#include <sys/diskslice.h> 23343171Sdim#include <sys/disklabel.h> 24343171Sdim#include <sys/dirent.h> 25343171Sdim#include <machine/bootinfo.h> 26343171Sdim#include <machine/elf.h> 27343171Sdim 28343171Sdim#include <stdarg.h> 29343171Sdim 30343171Sdim#include <a.out.h> 31343171Sdim 32343171Sdim#include <btxv86.h> 33343171Sdim 34343171Sdim#include "boot2.h" 35343171Sdim#include "lib.h" 36343171Sdim 37343171Sdim#define IO_KEYBOARD 1 38343171Sdim#define IO_SERIAL 2 39343171Sdim 40343171Sdim#define SECOND 18 /* Circa that many ticks in a second. */ 41343171Sdim 42343171Sdim#define RBX_ASKNAME 0x0 /* -a */ 43343171Sdim#define RBX_SINGLE 0x1 /* -s */ 44343171Sdim#define RBX_DFLTROOT 0x5 /* -r */ 45343171Sdim#define RBX_KDB 0x6 /* -d */ 46343171Sdim#define RBX_CONFIG 0xa /* -c */ 47343171Sdim#define RBX_VERBOSE 0xb /* -v */ 48343171Sdim#define RBX_SERIAL 0xc /* -h */ 49343171Sdim#define RBX_CDROM 0xd /* -C */ 50343171Sdim#define RBX_GDB 0xf /* -g */ 51343171Sdim#define RBX_MUTE 0x10 /* -m */ 52343171Sdim#define RBX_PAUSE 0x12 /* -p */ 53343171Sdim#define RBX_DUAL 0x1d /* -D */ 54343171Sdim#define RBX_PROBEKBD 0x1e /* -P */ 55343171Sdim#define RBX_NOINTR 0x1f /* -n */ 56343171Sdim 57343171Sdim#define RBX_MASK 0x2005ffff 58343171Sdim 59343171Sdim#define PATH_CONFIG "/boot.config" 60343171Sdim#define PATH_BOOT3 "/boot/loader" 61343171Sdim#define PATH_KERNEL "/kernel" 62343171Sdim 63343171Sdim#define ARGS 0x900 64343171Sdim#define NOPT 14 65343171Sdim#define NDEV 5 66343171Sdim#define MEM_BASE 0x12 67343171Sdim#define MEM_EXT 0x15 68343171Sdim#define V86_CY(x) ((x) & 1) 69343171Sdim#define V86_ZR(x) ((x) & 0x40) 70343171Sdim 71343171Sdim#define DRV_HARD 0x80 72343171Sdim#define DRV_MASK 0x7f 73343171Sdim 74343171Sdim#define TYPE_AD 0 75#define TYPE_WD 1 76#define TYPE_DA 2 77#define TYPE_MAXHARD TYPE_DA 78#define TYPE_WFD 3 79#define TYPE_FD 4 80 81extern uint32_t _end; 82 83static const char optstr[NOPT] = "DhaCcdgmnPprsv"; 84static const unsigned char flags[NOPT] = { 85 RBX_DUAL, 86 RBX_SERIAL, 87 RBX_ASKNAME, 88 RBX_CDROM, 89 RBX_CONFIG, 90 RBX_KDB, 91 RBX_GDB, 92 RBX_MUTE, 93 RBX_NOINTR, 94 RBX_PROBEKBD, 95 RBX_PAUSE, 96 RBX_DFLTROOT, 97 RBX_SINGLE, 98 RBX_VERBOSE 99}; 100 101static const char *const dev_nm[NDEV] = {"ad", "wd", "da", " ", "fd"}; 102static const unsigned char dev_maj[NDEV] = {30, 0, 4, 1, 2}; 103 104static struct dsk { 105 unsigned drive; 106 unsigned type; 107 unsigned unit; 108 unsigned slice; 109 unsigned part; 110 unsigned start; 111 int init; 112} dsk; 113static char cmd[512]; 114static char kname[1024]; 115static uint32_t opts = RB_BOOTINFO; 116static struct bootinfo bootinfo; 117static uint8_t ioctrl = IO_KEYBOARD; 118 119void exit(int); 120static void load(const char *); 121static int parse(char *); 122static int xfsread(ino_t, void *, size_t); 123static int dskread(void *, unsigned, unsigned); 124static int printf(const char *,...); 125static int putchar(int); 126static uint32_t memsize(int); 127static int drvread(void *, unsigned, unsigned); 128static int keyhit(unsigned); 129static int xputc(int); 130static int xgetc(int); 131static int getc(int); 132 133#if 1 134#define memcpy __builtin_memcpy 135#else 136static void memcpy(char *, const char *, int); 137static void 138memcpy(char *dst, const char *src, int len) 139{ 140 while (len--) 141 *dst++ = *src++; 142} 143#endif 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 int 155xfsread(ino_t inode, void *buf, size_t nbyte) 156{ 157 if (fsread(inode, buf, nbyte) != nbyte) { 158 printf("Invalid %s\n", "format"); 159 return -1; 160 } 161 return 0; 162} 163 164static inline void 165getstr(char *str, int size) 166{ 167 char *s; 168 int c; 169 170 s = str; 171 for (;;) { 172 switch (c = xgetc(0)) { 173 case 0: 174 break; 175 case '\177': 176 c = '\b'; 177 case '\b': 178 if (s > str) { 179 s--; 180 putchar('\b'); 181 putchar(' '); 182 } else 183 c = 0; 184 break; 185 case '\n': 186 case '\r': 187 *s = 0; 188 return; 189 default: 190 if (s - str < size - 1) 191 *s++ = c; 192 } 193 if (c) 194 putchar(c); 195 } 196} 197 198static inline uint32_t 199drvinfo(int drive) 200{ 201 v86.addr = 0x13; 202 v86.eax = 0x800; 203 v86.edx = DRV_HARD + drive; 204 v86int(); 205 if (V86_CY(v86.efl)) 206 return 0x4f010f; 207 return ((v86.ecx & 0xc0) << 18) | ((v86.ecx & 0xff00) << 8) | 208 (v86.edx & 0xff00) | (v86.ecx & 0x3f); 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, i; 224 ino_t ino; 225 226 dmadat = (void *)(roundup2(__base + _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 = memsize(MEM_BASE); 235 bootinfo.bi_extmem = memsize(MEM_EXT); 236 bootinfo.bi_memsizes_valid++; 237 for (i = 0; i < N_BIOS_GEOM; i++) 238 bootinfo.bi_bios_geom[i] = drvinfo(i); 239 240 /* Process configuration file */ 241 242 autoboot = 1; 243 244 if ((ino = lookup(PATH_CONFIG))) 245 fsread(ino, cmd, sizeof(cmd)); 246 247 if (*cmd) { 248 printf("%s: %s", PATH_CONFIG, cmd); 249 if (parse(cmd)) 250 autoboot = 0; 251 /* Do not process this command twice */ 252 *cmd = 0; 253 } 254 255 /* 256 * Try to exec stage 3 boot loader. If interrupted by a keypress, 257 * or in case of failure, try to load a kernel directly instead. 258 */ 259 260 if (autoboot && !*kname) { 261 memcpy(kname, PATH_BOOT3, sizeof(PATH_BOOT3)); 262 if (!keyhit(3*SECOND)) { 263 load(kname); 264 memcpy(kname, PATH_KERNEL, sizeof(PATH_KERNEL)); 265 } 266 } 267 268 /* Present the user with the boot2 prompt. */ 269 270 for (;;) { 271 printf(" \n>> FreeBSD/i386 BOOT\n" 272 "Default: %u:%s(%u,%c)%s\n" 273 "boot: ", 274 dsk.drive & DRV_MASK, dev_nm[dsk.type], dsk.unit, 275 'a' + dsk.part, kname); 276 if (ioctrl & IO_SERIAL) 277 sio_flush(); 278 if (!autoboot || keyhit(5*SECOND)) 279 getstr(cmd, sizeof(cmd)); 280 else 281 putchar('\n'); 282 autoboot = 0; 283 if (parse(cmd)) 284 putchar('\a'); 285 else 286 load(kname); 287 } 288} 289 290/* XXX - Needed for btxld to link the boot2 binary; do not remove. */ 291void 292exit(int x) 293{ 294} 295 296static void 297load(const char *fname) 298{ 299 union { 300 struct exec ex; 301 Elf32_Ehdr eh; 302 } hdr; 303 Elf32_Phdr ep[2]; 304 Elf32_Shdr es[2]; 305 caddr_t p; 306 ino_t ino; 307 uint32_t addr, x; 308 int fmt, i, j; 309 310 if (!(ino = lookup(fname))) { 311 if (!ls) 312 printf("No %s\n", fname); 313 return; 314 } 315 if (xfsread(ino, &hdr, sizeof(hdr))) 316 return; 317 if (N_GETMAGIC(hdr.ex) == ZMAGIC) 318 fmt = 0; 319 else if (IS_ELF(hdr.eh)) 320 fmt = 1; 321 else { 322 printf("Invalid %s\n", "format"); 323 return; 324 } 325 if (fmt == 0) { 326 addr = hdr.ex.a_entry & 0xffffff; 327 p = PTOV(addr); 328 fs_off = PAGE_SIZE; 329 if (xfsread(ino, p, hdr.ex.a_text)) 330 return; 331 p += roundup2(hdr.ex.a_text, PAGE_SIZE); 332 if (xfsread(ino, p, hdr.ex.a_data)) 333 return; 334 p += hdr.ex.a_data + roundup2(hdr.ex.a_bss, PAGE_SIZE); 335 bootinfo.bi_symtab = VTOP(p); 336 memcpy(p, (char *)&hdr.ex.a_syms, sizeof(hdr.ex.a_syms)); 337 p += sizeof(hdr.ex.a_syms); 338 if (hdr.ex.a_syms) { 339 if (xfsread(ino, p, hdr.ex.a_syms)) 340 return; 341 p += hdr.ex.a_syms; 342 if (xfsread(ino, p, sizeof(int))) 343 return; 344 x = *(uint32_t *)p; 345 p += sizeof(int); 346 x -= sizeof(int); 347 if (xfsread(ino, p, x)) 348 return; 349 p += x; 350 } 351 } else { 352 fs_off = hdr.eh.e_phoff; 353 for (j = i = 0; i < hdr.eh.e_phnum && j < 2; i++) { 354 if (xfsread(ino, ep + j, sizeof(ep[0]))) 355 return; 356 if (ep[j].p_type == PT_LOAD) 357 j++; 358 } 359 for (i = 0; i < 2; i++) { 360 p = PTOV(ep[i].p_paddr & 0xffffff); 361 fs_off = ep[i].p_offset; 362 if (xfsread(ino, p, ep[i].p_filesz)) 363 return; 364 } 365 p += roundup2(ep[1].p_memsz, PAGE_SIZE); 366 bootinfo.bi_symtab = VTOP(p); 367 if (hdr.eh.e_shnum == hdr.eh.e_shstrndx + 3) { 368 fs_off = hdr.eh.e_shoff + sizeof(es[0]) * 369 (hdr.eh.e_shstrndx + 1); 370 if (xfsread(ino, &es, sizeof(es))) 371 return; 372 for (i = 0; i < 2; i++) { 373 memcpy(p, (char *)&es[i].sh_size, sizeof(es[i].sh_size)); 374 p += sizeof(es[i].sh_size); 375 fs_off = es[i].sh_offset; 376 if (xfsread(ino, p, es[i].sh_size)) 377 return; 378 p += es[i].sh_size; 379 } 380 } 381 addr = hdr.eh.e_entry & 0xffffff; 382 } 383 bootinfo.bi_esymtab = VTOP(p); 384 bootinfo.bi_kernelname = VTOP(fname); 385 bootinfo.bi_bios_dev = dsk.drive; 386 __exec((caddr_t)addr, opts & RBX_MASK, 387 MAKEBOOTDEV(dev_maj[dsk.type], 0, dsk.slice, dsk.unit, dsk.part), 388 0, 0, 0, VTOP(&bootinfo)); 389} 390 391static int 392parse(char *arg) 393{ 394 char *p, *q; 395 int drv, 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 if (*arg < '0' || *arg > '9') 427 return -1; 428 drv = *arg - '0'; 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 if (arg[1] != ',' || *arg < '0' || *arg > '9') 440 return -1; 441 dsk.unit = *arg - '0'; 442 arg += 2; 443 dsk.slice = WHOLE_DISK_SLICE; 444 if (arg[1] == ',') { 445 if (*arg < '0' || *arg > '0' + NDOSPART) 446 return -1; 447 if ((dsk.slice = *arg - '0')) 448 dsk.slice++; 449 arg += 2; 450 } 451 if (arg[1] != ')' || *arg < 'a' || *arg > 'p') 452 return -1; 453 dsk.part = *arg - 'a'; 454 arg += 2; 455 if (drv == -1) 456 drv = dsk.unit; 457 dsk.drive = (dsk.type <= TYPE_MAXHARD 458 ? DRV_HARD : 0) + drv; 459 dsk_meta = 0; 460 fsread(0, NULL, 0); 461 } 462 if ((i = p - arg - !*(p - 1))) { 463 if (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 } 530 } 531 return drvread(buf, dsk.start + lba, nblk); 532} 533 534static int 535printf(const char *fmt,...) 536{ 537 static const char digits[16] = "0123456789abcdef"; 538 va_list ap; 539 char buf[10]; 540 char *s; 541 unsigned r, 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 case 'x': 558 r = c == 'u' ? 10U : 16U; 559 u = va_arg(ap, unsigned); 560 s = buf; 561 do 562 *s++ = digits[u % r]; 563 while (u /= r); 564 while (--s >= buf) 565 putchar(*s); 566 continue; 567 } 568 } 569 putchar(c); 570 } 571 va_end(ap); 572 return 0; 573} 574 575static int 576putchar(int c) 577{ 578 if (c == '\n') 579 xputc('\r'); 580 return xputc(c); 581} 582 583static uint32_t 584memsize(int type) 585{ 586 v86.addr = type; 587 v86.eax = 0x8800; 588 v86int(); 589 return v86.eax; 590} 591 592static int 593drvread(void *buf, unsigned lba, unsigned nblk) 594{ 595 static unsigned c = 0x2d5c7c2f; 596 597 printf("%c\b", c = c << 8 | c >> 24); 598 v86.ctl = V86_ADDR | V86_CALLF | V86_FLAGS; 599 v86.addr = XREADORG; /* call to xread in boot1 */ 600 v86.es = VTOPSEG(buf); 601 v86.eax = lba; 602 v86.ebx = VTOPOFF(buf); 603 v86.ecx = lba >> 16; 604 v86.edx = nblk << 8 | dsk.drive; 605 v86int(); 606 v86.ctl = V86_FLAGS; 607 if (V86_CY(v86.efl)) { 608 printf("Disk error 0x%x (lba=0x%x)\n", v86.eax >> 8 & 0xff, 609 lba); 610 return -1; 611 } 612 return 0; 613} 614 615static int 616keyhit(unsigned ticks) 617{ 618 uint32_t t0, t1; 619 620 if (opts & 1 << RBX_NOINTR) 621 return 0; 622 t0 = 0; 623 for (;;) { 624 if (xgetc(1)) 625 return 1; 626 t1 = *(uint32_t *)PTOV(0x46c); 627 if (!t0) 628 t0 = t1; 629 if (t1 < t0 || t1 >= t0 + ticks) 630 return 0; 631 } 632} 633 634static int 635xputc(int c) 636{ 637 if (ioctrl & IO_KEYBOARD) 638 putc(c); 639 if (ioctrl & IO_SERIAL) 640 sio_putc(c); 641 return c; 642} 643 644static int 645xgetc(int fn) 646{ 647 if (opts & 1 << RBX_NOINTR) 648 return 0; 649 for (;;) { 650 if (ioctrl & IO_KEYBOARD && getc(1)) 651 return fn ? 1 : getc(0); 652 if (ioctrl & IO_SERIAL && sio_ischar()) 653 return fn ? 1 : sio_getc(); 654 if (fn) 655 return 0; 656 } 657} 658 659static int 660getc(int fn) 661{ 662 v86.addr = 0x16; 663 v86.eax = fn << 8; 664 v86int(); 665 return fn == 0 ? v86.eax & 0xff : !V86_ZR(v86.efl); 666} 667