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