gptboot.c revision 40269
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 * $Id:$ 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/dirent.h> 25#include <machine/bootinfo.h> 26 27#include <ufs/ffs/fs.h> 28#include <ufs/ufs/dinode.h> 29 30#include <stdarg.h> 31 32#include <a.out.h> 33#include <elf.h> 34 35#include <btxv86.h> 36 37#define RBX_ASKNAME 0x0 /* -a */ 38#define RBX_SINGLE 0x1 /* -s */ 39#define RBX_DFLTROOT 0x5 /* -r */ 40#define RBX_KDB 0x6 /* -d */ 41#define RBX_CONFIG 0xa /* -c */ 42#define RBX_VERBOSE 0xb /* -v */ 43#define RBX_CDROM 0xd /* -C */ 44#define RBX_GDB 0xf /* -g */ 45 46#define PATH_CONFIG "/boot.config" 47#define PATH_BOOT3 "/boot/loader" 48#define PATH_KERNEL "/kernel" 49#define PATH_HELP "boot.help" 50 51#define ARGS 0x800 52#define NOPT 8 53#define BSIZEMAX 8192 54#define NDEV 3 55#define MEM_BASE 0x12 56#define MEM_EXT 0x15 57#define V86_CY(x) ((x) & 1) 58#define V86_ZR(x) ((x) & 0x40) 59 60extern uint32_t _end; 61 62static const char optstr[NOPT] = "aCcdgrsv"; 63static const unsigned char flags[NOPT] = { 64 RBX_ASKNAME, 65 RBX_CDROM, 66 RBX_CONFIG, 67 RBX_KDB, 68 RBX_GDB, 69 RBX_DFLTROOT, 70 RBX_SINGLE, 71 RBX_VERBOSE 72}; 73 74static const char *const dev_nm[] = {"fd", "wd", "da"}; 75static const uint8_t dev_bios[] = {0, 0x80, 0x82}; 76static const uint8_t dev_bsd[] = {2, 0, 4}; 77 78static struct dsk { 79 unsigned drive; 80 unsigned type; 81 unsigned unit; 82 unsigned slice; 83 unsigned part; 84 int meta; 85} dsk; 86static char cmd[512]; 87static char kname[1024]; 88static char help[2048]; 89static uint32_t opts; 90static struct bootinfo bootinfo; 91static int ls; 92static uint32_t fs_off; 93 94void exit(int); 95static void load(const char *); 96static int parse(char *); 97static void readfile(const char *, void *, size_t); 98static ino_t lookup(const char *); 99static int fsfind(const char *, ino_t *); 100static ssize_t fsread(ino_t, void *, size_t); 101static int dskread(void *, unsigned, unsigned); 102static int printf(const char *,...); 103static void getstr(char *, int); 104static int putchar(int); 105static int getchar(void); 106static void *memcpy(void *, const void *, size_t); 107static int strcmp(const char *, const char *); 108static void *malloc(size_t); 109static uint32_t memsize(int); 110static uint32_t drvinfo(int); 111static int drvread(void *, unsigned, unsigned); 112static int keyhit(unsigned); 113static int putch(int); 114static int getch(void); 115 116int 117main(void) 118{ 119 int autoboot, helpon, i; 120 121 dsk.drive = *(uint8_t *)PTOV(ARGS); 122 for (dsk.type = NDEV - 1; 123 (i = dsk.drive - dev_bios[dsk.type]) < 0; 124 dsk.type--); 125 dsk.unit = i; 126 dsk.slice = *(uint8_t *)PTOV(ARGS + 1) + 1; 127 bootinfo.bi_version = BOOTINFO_VERSION; 128 bootinfo.bi_size = sizeof(bootinfo); 129 bootinfo.bi_basemem = memsize(MEM_BASE); 130 bootinfo.bi_extmem = memsize(MEM_EXT); 131 bootinfo.bi_memsizes_valid++; 132 for (i = 0; i < N_BIOS_GEOM; i++) 133 bootinfo.bi_bios_geom[i] = drvinfo(i); 134 autoboot = 2; 135 helpon = 1; 136 readfile(PATH_CONFIG, cmd, sizeof(cmd)); 137 if (parse(cmd)) 138 autoboot = 0; 139 else if (!*kname) { 140 if (autoboot == 2) { 141 memcpy(kname, PATH_BOOT3, sizeof(PATH_BOOT3)); 142 if (!keyhit(0x37)) { 143 load(kname); 144 autoboot = 1; 145 } 146 } 147 if (autoboot == 1) 148 memcpy(kname, PATH_KERNEL, sizeof(PATH_KERNEL)); 149 } 150 readfile(PATH_HELP, help, sizeof(help)); 151 for (;;) { 152 printf(" \n>> FreeBSD/i386 BOOT\n" 153 "Default: %u:%s(%u,%c)%s\n" 154 "%s" 155 "boot: ", 156 dsk.drive & 0x7f, dev_nm[dsk.type], dsk.unit, 157 'a' + dsk.part, kname, helpon ? help : ""); 158 if (!autoboot || keyhit(0x5a)) 159 getstr(cmd, sizeof(cmd)); 160 autoboot = helpon = 0; 161 if (parse(cmd)) 162 helpon = 1; 163 else 164 load(kname); 165 } 166} 167 168void 169exit(int x) 170{ 171} 172 173static void 174load(const char *fname) 175{ 176 union { 177 struct exec ex; 178 Elf32_Ehdr eh; 179 } hdr; 180 Elf32_Phdr ep[2]; 181 Elf32_Shdr es[2]; 182 caddr_t p; 183 ino_t ino; 184 uint32_t addr, x; 185 int fmt, i, j; 186 187 if (!(ino = lookup(fname)) && !ls) { 188 printf("No `%s'\n", fname); 189 return; 190 } 191 if (fsread(ino, &hdr, sizeof(hdr)) != sizeof(hdr)) 192 return; 193 if (N_GETMAGIC(hdr.ex) == ZMAGIC) 194 fmt = 0; 195 else if (IS_ELF(hdr.eh)) 196 fmt = 1; 197 else { 198 printf("Invalid %s\n", "format"); 199 return; 200 } 201 if (fmt == 0) { 202 addr = hdr.ex.a_entry & 0xffffff; 203 p = PTOV(addr); 204 printf("%s=0x%x ", "text", (unsigned)hdr.ex.a_text); 205 fs_off = PAGE_SIZE; 206 if (fsread(ino, p, hdr.ex.a_text) != hdr.ex.a_text) 207 return; 208 p += roundup2(hdr.ex.a_text, PAGE_SIZE); 209 printf("%s=0x%x ", "data", (unsigned)hdr.ex.a_data); 210 if (fsread(ino, p, hdr.ex.a_data) != hdr.ex.a_data) 211 return; 212 p += hdr.ex.a_data; 213 printf("%s=0x%x ", "bss", (unsigned)hdr.ex.a_bss); 214 p += roundup2(hdr.ex.a_bss, PAGE_SIZE); 215 bootinfo.bi_symtab = VTOP(p); 216 memcpy(p, &hdr.ex.a_syms, sizeof(hdr.ex.a_syms)); 217 p += sizeof(hdr.ex.a_syms); 218 printf("symbols=["); 219 printf("+0x%x", (unsigned)hdr.ex.a_syms); 220 if (hdr.ex.a_syms) { 221 if (fsread(ino, p, hdr.ex.a_syms) != hdr.ex.a_syms) 222 return; 223 p += hdr.ex.a_syms; 224 if (fsread(ino, p, sizeof(int)) != sizeof(int)) 225 return; 226 x = *(uint32_t *)p; 227 p += sizeof(int); 228 x -= sizeof(int); 229 printf("+0x%x", x); 230 if (fsread(ino, p, x) != x) 231 return; 232 p += x; 233 } 234 } else { 235 fs_off = hdr.eh.e_phoff; 236 for (j = i = 0; i < hdr.eh.e_phoff && j < 2; i++) { 237 if (fsread(ino, ep + j, sizeof(ep[0])) != sizeof(ep[0])) 238 return; 239 if (ep[j].p_type == PT_LOAD) 240 j++; 241 } 242 for (i = 0; i < 2; i++) { 243 p = PTOV(ep[i].p_paddr & 0xffffff); 244 printf("%s=0x%x ", !i ? "text" : "data", ep[i].p_filesz); 245 fs_off = ep[i].p_offset; 246 if (fsread(ino, p, ep[i].p_filesz) != ep[i].p_filesz) 247 return; 248 } 249 printf("%s=0x%x ", "bss", ep[1].p_memsz - ep[1].p_filesz); 250 p += roundup2(ep[1].p_memsz, PAGE_SIZE); 251 bootinfo.bi_symtab = VTOP(p); 252 printf("symbols=["); 253 if (hdr.eh.e_shnum == hdr.eh.e_shstrndx + 3) { 254 fs_off = hdr.eh.e_shoff + sizeof(es[0]) * 255 (hdr.eh.e_shstrndx + 1); 256 if (fsread(ino, &es, sizeof(es)) != sizeof(es)) 257 return; 258 for (i = 0; i < 2; i++) { 259 memcpy(p, &es[i].sh_size, sizeof(es[i].sh_size)); 260 p += sizeof(es[i].sh_size); 261 printf("+0x%x", es[i].sh_size); 262 fs_off = es[i].sh_offset; 263 if (fsread(ino, p, es[i].sh_size) != es[i].sh_size) 264 return; 265 p += es[i].sh_size; 266 } 267 } 268 addr = hdr.eh.e_entry & 0xffffff; 269 } 270 bootinfo.bi_esymtab = VTOP(p); 271 printf("]\nentry=0x%x\n", addr); 272 bootinfo.bi_kernelname = VTOP(fname); 273 __exec((caddr_t)addr, RB_BOOTINFO | opts, 274 MAKEBOOTDEV(dev_bsd[dsk.type], 0, dsk.slice, dsk.unit, 275 dsk.part), 0, 0, 0, VTOP(&bootinfo)); 276} 277 278static int 279parse(char *arg) 280{ 281 char *p, *q; 282 int drv, c, i; 283 284 while ((c = *arg++)) { 285 if (c == ' ') 286 continue; 287 for (p = arg; *p && *p != '\n' && *p != ' '; p++); 288 if (*p) 289 *p++ = 0; 290 if (c == '-') 291 while ((c = *arg++)) { 292 for (i = 0; c != optstr[i]; i++) 293 if (i == NOPT - 1) 294 return -1; 295 opts |= 1 << flags[i]; 296 } 297 else { 298 for (q = arg--; *q && *q != '('; q++); 299 if (*q) { 300 drv = -1; 301 if (arg[1] == ':') { 302 if (*arg < '0' || *arg > '9') 303 return -1; 304 drv = *arg - '0'; 305 arg += 2; 306 } 307 if (q - arg != 2) 308 return -1; 309 for (i = 0; arg[0] != dev_nm[i][0] || 310 arg[1] != dev_nm[i][1]; i++) 311 if (i == NDEV - 1) 312 return -1; 313 dsk.type = i; 314 arg += 3; 315 if (arg[1] != ',' || *arg < '0' || *arg > '9') 316 return -1; 317 dsk.unit = *arg - '0'; 318 arg += 2; 319 if (arg[1] == ',') { 320 if (*arg < '0' || *arg > '4') 321 return -1; 322 if ((dsk.slice = *arg - '0')) 323 dsk.slice++; 324 arg += 2; 325 } 326 if (arg[1] != ')' || *arg < 'a' || *arg > 'p') 327 return -1; 328 dsk.part = *arg - 'a'; 329 arg += 2; 330 if (drv == -1) 331 dsk.drive = dev_bios[dsk.type] + dsk.unit; 332 else 333 dsk.drive = (dsk.type ? 0x80 : 0) + drv; 334 dsk.meta = 0; 335 } 336 if ((i = p - arg - !*(p - 1))) { 337 if (i >= sizeof(kname)) 338 return -1; 339 memcpy(kname, arg, i + 1); 340 } 341 } 342 arg = p; 343 } 344 return 0; 345} 346 347static void 348readfile(const char *fname, void *buf, size_t size) 349{ 350 ino_t ino; 351 352 if ((ino = lookup(fname))) 353 fsread(ino, buf, size); 354} 355 356static ino_t 357lookup(const char *path) 358{ 359 char name[MAXNAMLEN + 1]; 360 const char *s; 361 ino_t ino; 362 ssize_t n; 363 int dt; 364 365 ino = ROOTINO; 366 dt = DT_DIR; 367 for (;;) { 368 if (*path == '/') 369 path++; 370 if (!*path) 371 break; 372 for (s = path; *s && *s != '/'; s++); 373 if ((n = s - path) > MAXNAMLEN) 374 return 0; 375 ls = *path == '?' && n == 1 && !*s; 376 memcpy(name, path, n); 377 name[n] = 0; 378 if ((dt = fsfind(name, &ino)) <= 0) 379 break; 380 path = s; 381 } 382 return dt == DT_REG ? ino : 0; 383} 384 385static int 386fsfind(const char *name, ino_t * ino) 387{ 388 char buf[DEV_BSIZE]; 389 struct dirent *d; 390 char *s; 391 ssize_t n; 392 393 fs_off = 0; 394 while ((n = fsread(*ino, buf, DEV_BSIZE)) > 0) 395 for (s = buf; s < buf + DEV_BSIZE;) { 396 d = (void *)s; 397 if (ls) 398 printf("%s ", d->d_name); 399 else if (!strcmp(name, d->d_name)) { 400 *ino = d->d_fileno; 401 return d->d_type; 402 } 403 s += d->d_reclen; 404 } 405 if (n != -1 && ls) 406 putchar('\n'); 407 return 0; 408} 409 410static ssize_t 411fsread(ino_t inode, void *buf, size_t nbyte) 412{ 413 static struct fs fs; 414 static struct dinode din; 415 static char *blkbuf; 416 static ufs_daddr_t *indbuf; 417 static ino_t inomap; 418 static ufs_daddr_t blkmap, indmap; 419 static unsigned fsblks; 420 char *s; 421 ufs_daddr_t lbn, addr; 422 size_t n, nb, off; 423 424 if (!dsk.meta) { 425 if (!blkbuf) 426 blkbuf = malloc(BSIZEMAX); 427 inomap = 0; 428 if (dskread(blkbuf, SBOFF / DEV_BSIZE, SBSIZE / DEV_BSIZE)) 429 return -1; 430 memcpy(&fs, blkbuf, sizeof(fs)); 431 if (fs.fs_magic != FS_MAGIC) { 432 printf("Not ufs\n"); 433 return -1; 434 } 435 fsblks = fs.fs_bsize >> DEV_BSHIFT; 436 dsk.meta = 1; 437 } 438 if (inomap != inode) { 439 if (dskread(blkbuf, fsbtodb(&fs, ino_to_fsba(&fs, inode)), 440 fsblks)) 441 return -1; 442 din = ((struct dinode *)blkbuf)[inode % INOPB(&fs)]; 443 inomap = inode; 444 fs_off = 0; 445 blkmap = indmap = 0; 446 } 447 s = buf; 448 if (nbyte > (n = din.di_size - fs_off)) 449 nbyte = n; 450 nb = nbyte; 451 while (nb) { 452 lbn = lblkno(&fs, fs_off); 453 if (lbn < NDADDR) 454 addr = din.di_db[lbn]; 455 else { 456 if (indmap != din.di_ib[0]) { 457 if (!indbuf) 458 indbuf = malloc(BSIZEMAX); 459 if (dskread(indbuf, fsbtodb(&fs, din.di_ib[0]), 460 fsblks)) 461 return -1; 462 indmap = din.di_ib[0]; 463 } 464 addr = indbuf[(lbn - NDADDR) % NINDIR(&fs)]; 465 } 466 n = dblksize(&fs, &din, lbn); 467 if (blkmap != addr) { 468 if (dskread(blkbuf, fsbtodb(&fs, addr), n >> DEV_BSHIFT)) 469 return -1; 470 blkmap = addr; 471 } 472 off = blkoff(&fs, fs_off); 473 n -= off; 474 if (n > nb) 475 n = nb; 476 memcpy(s, blkbuf + off, n); 477 s += n; 478 fs_off += n; 479 nb -= n; 480 } 481 return nbyte; 482} 483 484static int 485dskread(void *buf, unsigned lba, unsigned nblk) 486{ 487 static char *sec; 488 static unsigned hidden; 489 struct dos_partition *dp; 490 struct disklabel *d; 491 unsigned sl, i; 492 493 if (!dsk.meta) { 494 if (!sec) 495 sec = malloc(DEV_BSIZE); 496 hidden = 0; 497 sl = dsk.slice; 498 if (sl != WHOLE_DISK_SLICE) { 499 if (drvread(sec, DOSBBSECTOR, 1)) 500 return -1; 501 dp = (void *)(sec + DOSPARTOFF); 502 if (sl == COMPATIBILITY_SLICE) 503 for (i = 0; i < NDOSPART; i++) 504 if (dp[i].dp_typ == DOSPTYP_386BSD && 505 (dp[i].dp_flag & 0x80 || 506 sl == COMPATIBILITY_SLICE)) 507 sl = BASE_SLICE + i; 508 if (sl != COMPATIBILITY_SLICE) 509 dp += sl - BASE_SLICE; 510 if (dp->dp_typ != DOSPTYP_386BSD) { 511 printf("Invalid %s\n", "slice"); 512 return -1; 513 } 514 hidden = dp->dp_start; 515 } 516 if (dsk.part != RAW_PART) { 517 if (drvread(sec, hidden + LABELSECTOR, 1)) 518 return -1; 519 d = (void *)(sec + LABELOFFSET); 520 if (d->d_magic != DISKMAGIC || d->d_magic2 != DISKMAGIC) { 521 printf("Invalid %s\n", "label"); 522 return -1; 523 } 524 if (dsk.part >= d->d_npartitions) { 525 printf("Invalid %s\n", "partition"); 526 return -1; 527 } 528 hidden = d->d_partitions[dsk.part].p_offset; 529 } 530 } 531 return drvread(buf, hidden + 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 void 576getstr(char *str, int size) 577{ 578 char *s; 579 int c; 580 581 s = str; 582 do { 583 switch (c = getchar()) { 584 case '\b': 585 if (s > str) 586 s--; 587 break; 588 case '\n': 589 *s = 0; 590 break; 591 default: 592 if (s - str < size - 1) 593 *s++ = c; 594 } 595 putchar(c); 596 } while (c != '\n'); 597} 598 599static int 600putchar(int c) 601{ 602 if (c == '\n') 603 putch('\r'); 604 return putch(c); 605} 606 607static int 608getchar(void) 609{ 610 int c; 611 612 c = getch(); 613 if (c == '\r') 614 c = '\n'; 615 return c; 616} 617 618static void * 619memcpy(void *dst, const void *src, size_t size) 620{ 621 const char *s; 622 char *d; 623 624 for (d = dst, s = src; size; size--) 625 *d++ = *s++; 626 return dst; 627} 628 629static int 630strcmp(const char *s1, const char *s2) 631{ 632 for (; *s1 == *s2 && *s1; s1++, s2++); 633 return (u_char)*s1 - (u_char)*s2; 634} 635 636static void * 637malloc(size_t size) 638{ 639 static uint32_t next; 640 void *p; 641 642 if (!next) 643 next = roundup2(__base + _end, 0x10000) - __base; 644 p = (void *)next; 645 next += size; 646 return p; 647} 648 649static uint32_t 650memsize(int type) 651{ 652 v86.ctl = V86_FLAGS; 653 v86.addr = type; 654 v86.eax = 0x8800; 655 v86int(); 656 return v86.eax; 657} 658 659static uint32_t 660drvinfo(int drive) 661{ 662 v86.addr = 0x13; 663 v86.eax = 0x800; 664 v86.edx = 0x80 + drive; 665 v86int(); 666 if (V86_CY(v86.efl)) 667 return 0x4f010f; 668 return ((v86.ecx & 0xc0) << 18) | ((v86.ecx & 0xff00) << 8) | 669 (v86.edx & 0xff00) | (v86.ecx & 0x3f); 670} 671 672static int 673drvread(void *buf, unsigned lba, unsigned nblk) 674{ 675 v86.ctl = V86_ADDR | V86_CALLF | V86_FLAGS; 676 v86.addr = 0x604; 677 v86.eax = nblk; 678 v86.ebx = VTOPSEG(buf) << 16 | VTOPOFF(buf); 679 v86.ecx = lba; 680 v86.edx = dsk.drive; 681 v86int(); 682 v86.ctl = V86_FLAGS; 683 if (V86_CY(v86.efl)) { 684 printf("Disk error 0x%x (lba=0x%x)\n", v86.eax >> 8 & 0xff, 685 lba); 686 return -1; 687 } 688 return 0; 689} 690 691static int 692keyhit(unsigned ticks) 693{ 694 uint32_t x; 695 696 x = 0; 697 for (;;) { 698 v86.addr = 0x16; 699 v86.eax = 0x100; 700 v86int(); 701 if (!V86_ZR(v86.efl)) 702 return 1; 703 v86.addr = 0x1a; 704 v86.eax = 0; 705 v86.edx = 0; 706 v86int(); 707 if (!x) 708 x = v86.edx; 709 else if (v86.edx < x || v86.edx > x + ticks) 710 return 0; 711 } 712} 713 714static int 715putch(int c) 716{ 717 v86.addr = 0x10; 718 v86.eax = 0xe00 | (c & 0xff); 719 v86.ebx = 0x7; 720 v86int(); 721 return c; 722} 723 724static int 725getch(void) 726{ 727 v86.addr = 0x16; 728 v86.eax = 0; 729 v86int(); 730 return v86.eax & 0xff; 731} 732