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