boot2.c revision 122433
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#include <sys/cdefs.h> 17__FBSDID("$FreeBSD: head/sys/boot/i386/boot2/boot2.c 122433 2003-11-10 19:06:09Z bde $"); 18 19#include <sys/param.h> 20#include <sys/disklabel.h> 21#include <sys/diskmbr.h> 22#include <sys/dirent.h> 23#include <machine/bootinfo.h> 24#include <machine/elf.h> 25 26#include <stdarg.h> 27 28#include <a.out.h> 29 30#include <btxv86.h> 31 32#include "boot2.h" 33#include "lib.h" 34 35#define IO_KEYBOARD 1 36#define IO_SERIAL 2 37 38#define SECOND 18 /* Circa that many ticks in a second. */ 39 40#define RBX_ASKNAME 0x0 /* -a */ 41#define RBX_SINGLE 0x1 /* -s */ 42#define RBX_DFLTROOT 0x5 /* -r */ 43#define RBX_KDB 0x6 /* -d */ 44#define RBX_CONFIG 0xa /* -c */ 45#define RBX_VERBOSE 0xb /* -v */ 46#define RBX_SERIAL 0xc /* -h */ 47#define RBX_CDROM 0xd /* -C */ 48#define RBX_GDB 0xf /* -g */ 49#define RBX_MUTE 0x10 /* -m */ 50#define RBX_PAUSE 0x12 /* -p */ 51#define RBX_NOINTR 0x1c /* -n */ 52#define RBX_DUAL 0x1d /* -D */ 53#define RBX_PROBEKBD 0x1e /* -P */ 54/* 0x1f is reserved for the RB_BOOTINFO flag. */ 55 56/* pass: -a, -s, -r, -d, -c, -v, -h, -C, -g, -m, -p, -D */ 57#define RBX_MASK 0x2005ffff 58 59#define PATH_CONFIG "/boot.config" 60#define PATH_BOOT3 "/boot/loader" 61#define PATH_KERNEL "/kernel" 62 63#define ARGS 0x900 64#define NOPT 12 65#define NDEV 3 66#define MEM_BASE 0x12 67#define MEM_EXT 0x15 68#define V86_CY(x) ((x) & 1) 69#define V86_ZR(x) ((x) & 0x40) 70 71#define DRV_HARD 0x80 72#define DRV_MASK 0x7f 73 74#define TYPE_AD 0 75#define TYPE_DA 1 76#define TYPE_MAXHARD TYPE_DA 77#define TYPE_FD 2 78 79extern uint32_t _end; 80 81static const char optstr[NOPT] = "DhaCgmnPprsv"; 82static const unsigned char flags[NOPT] = { 83 RBX_DUAL, 84 RBX_SERIAL, 85 RBX_ASKNAME, 86 RBX_CDROM, 87 RBX_GDB, 88 RBX_MUTE, 89 RBX_NOINTR, 90 RBX_PROBEKBD, 91 RBX_PAUSE, 92 RBX_DFLTROOT, 93 RBX_SINGLE, 94 RBX_VERBOSE 95}; 96 97static const char *const dev_nm[NDEV] = {"ad", "da", "fd"}; 98static const unsigned char dev_maj[NDEV] = {30, 4, 2}; 99 100static struct dsk { 101 unsigned drive; 102 unsigned type; 103 unsigned unit; 104 unsigned slice; 105 unsigned part; 106 unsigned start; 107 int init; 108} dsk; 109static char cmd[512]; 110static char kname[1024]; 111static uint32_t opts; 112static struct bootinfo bootinfo; 113static uint8_t ioctrl = IO_KEYBOARD; 114 115void exit(int); 116static void load(void); 117static int parse(void); 118static int xfsread(ino_t, void *, size_t); 119static int dskread(void *, unsigned, unsigned); 120static void printf(const char *,...); 121static void putchar(int); 122static uint32_t memsize(void); 123static int drvread(void *, unsigned, unsigned); 124static int keyhit(unsigned); 125static int xputc(int); 126static int xgetc(int); 127static int getc(int); 128 129#define memcpy __builtin_memcpy 130 131static inline int 132strcmp(const char *s1, const char *s2) 133{ 134 for (; *s1 == *s2 && *s1; s1++, s2++); 135 return (unsigned char)*s1 - (unsigned char)*s2; 136} 137 138#include "ufsread.c" 139 140static int 141xfsread(ino_t inode, void *buf, size_t nbyte) 142{ 143 if ((size_t)fsread(inode, buf, nbyte) != nbyte) { 144 printf("Invalid %s\n", "format"); 145 return -1; 146 } 147 return 0; 148} 149 150static inline uint32_t 151memsize(void) 152{ 153 v86.addr = MEM_EXT; 154 v86.eax = 0x8800; 155 v86int(); 156 return v86.eax; 157} 158 159static inline void 160getstr(void) 161{ 162 char *s; 163 int c; 164 165 s = cmd; 166 for (;;) { 167 switch (c = xgetc(0)) { 168 case 0: 169 break; 170 case '\177': 171 case '\b': 172 if (s > cmd) { 173 s--; 174 printf("\b \b"); 175 } 176 break; 177 case '\n': 178 case '\r': 179 *s = 0; 180 return; 181 default: 182 if (s - cmd < sizeof(cmd) - 1) 183 *s++ = c; 184 putchar(c); 185 } 186 } 187} 188 189static inline void 190putc(int c) 191{ 192 v86.addr = 0x10; 193 v86.eax = 0xe00 | (c & 0xff); 194 v86.ebx = 0x7; 195 v86int(); 196} 197 198int 199main(void) 200{ 201 int autoboot; 202 ino_t ino; 203 204 dmadat = (void *)(roundup2(__base + (int32_t)&_end, 0x10000) - __base); 205 v86.ctl = V86_FLAGS; 206 dsk.drive = *(uint8_t *)PTOV(ARGS); 207 dsk.type = dsk.drive & DRV_HARD ? TYPE_AD : TYPE_FD; 208 dsk.unit = dsk.drive & DRV_MASK; 209 dsk.slice = *(uint8_t *)PTOV(ARGS + 1) + 1; 210 bootinfo.bi_version = BOOTINFO_VERSION; 211 bootinfo.bi_size = sizeof(bootinfo); 212 bootinfo.bi_basemem = 0; /* XXX will be filled by loader or kernel */ 213 bootinfo.bi_extmem = memsize(); 214 bootinfo.bi_memsizes_valid++; 215 216 /* Process configuration file */ 217 218 autoboot = 1; 219 220 if ((ino = lookup(PATH_CONFIG))) 221 fsread(ino, cmd, sizeof(cmd)); 222 223 if (*cmd) { 224 printf("%s: %s", PATH_CONFIG, cmd); 225 if (parse()) 226 autoboot = 0; 227 /* Do not process this command twice */ 228 *cmd = 0; 229 } 230 231 /* 232 * Try to exec stage 3 boot loader. If interrupted by a keypress, 233 * or in case of failure, try to load a kernel directly instead. 234 */ 235 236 if (autoboot && !*kname) { 237 memcpy(kname, PATH_BOOT3, sizeof(PATH_BOOT3)); 238 if (!keyhit(3*SECOND)) { 239 load(); 240 memcpy(kname, PATH_KERNEL, sizeof(PATH_KERNEL)); 241 } 242 } 243 244 /* Present the user with the boot2 prompt. */ 245 246 for (;;) { 247 printf("\nFreeBSD/i386 boot\n" 248 "Default: %u:%s(%u,%c)%s\n" 249 "boot: ", 250 dsk.drive & DRV_MASK, dev_nm[dsk.type], dsk.unit, 251 'a' + dsk.part, kname); 252 if (ioctrl & IO_SERIAL) 253 sio_flush(); 254 if (!autoboot || keyhit(5*SECOND)) 255 getstr(); 256 else 257 putchar('\n'); 258 autoboot = 0; 259 if (parse()) 260 putchar('\a'); 261 else 262 load(); 263 } 264} 265 266/* XXX - Needed for btxld to link the boot2 binary; do not remove. */ 267void 268exit(int x) 269{ 270} 271 272static void 273load(void) 274{ 275 union { 276 struct exec ex; 277 Elf32_Ehdr eh; 278 } hdr; 279 Elf32_Phdr ep[2]; 280 Elf32_Shdr es[2]; 281 caddr_t p; 282 ino_t ino; 283 uint32_t addr, x; 284 int fmt, i, j; 285 286 if (!(ino = lookup(kname))) { 287 if (!ls) 288 printf("No %s\n", kname); 289 return; 290 } 291 if (xfsread(ino, &hdr, sizeof(hdr))) 292 return; 293 if (N_GETMAGIC(hdr.ex) == ZMAGIC) 294 fmt = 0; 295 else if (IS_ELF(hdr.eh)) 296 fmt = 1; 297 else { 298 printf("Invalid %s\n", "format"); 299 return; 300 } 301 if (fmt == 0) { 302 addr = hdr.ex.a_entry & 0xffffff; 303 p = PTOV(addr); 304 fs_off = PAGE_SIZE; 305 if (xfsread(ino, p, hdr.ex.a_text)) 306 return; 307 p += roundup2(hdr.ex.a_text, PAGE_SIZE); 308 if (xfsread(ino, p, hdr.ex.a_data)) 309 return; 310 p += hdr.ex.a_data + roundup2(hdr.ex.a_bss, PAGE_SIZE); 311 bootinfo.bi_symtab = VTOP(p); 312 memcpy(p, &hdr.ex.a_syms, sizeof(hdr.ex.a_syms)); 313 p += sizeof(hdr.ex.a_syms); 314 if (hdr.ex.a_syms) { 315 if (xfsread(ino, p, hdr.ex.a_syms)) 316 return; 317 p += hdr.ex.a_syms; 318 if (xfsread(ino, p, sizeof(int))) 319 return; 320 x = *(uint32_t *)p; 321 p += sizeof(int); 322 x -= sizeof(int); 323 if (xfsread(ino, p, x)) 324 return; 325 p += x; 326 } 327 } else { 328 fs_off = hdr.eh.e_phoff; 329 for (j = i = 0; i < hdr.eh.e_phnum && j < 2; i++) { 330 if (xfsread(ino, ep + j, sizeof(ep[0]))) 331 return; 332 if (ep[j].p_type == PT_LOAD) 333 j++; 334 } 335 for (i = 0; i < 2; i++) { 336 p = PTOV(ep[i].p_paddr & 0xffffff); 337 fs_off = ep[i].p_offset; 338 if (xfsread(ino, p, ep[i].p_filesz)) 339 return; 340 } 341 p += roundup2(ep[1].p_memsz, PAGE_SIZE); 342 bootinfo.bi_symtab = VTOP(p); 343 if (hdr.eh.e_shnum == hdr.eh.e_shstrndx + 3) { 344 fs_off = hdr.eh.e_shoff + sizeof(es[0]) * 345 (hdr.eh.e_shstrndx + 1); 346 if (xfsread(ino, &es, sizeof(es))) 347 return; 348 for (i = 0; i < 2; i++) { 349 memcpy(p, &es[i].sh_size, sizeof(es[i].sh_size)); 350 p += sizeof(es[i].sh_size); 351 fs_off = es[i].sh_offset; 352 if (xfsread(ino, p, es[i].sh_size)) 353 return; 354 p += es[i].sh_size; 355 } 356 } 357 addr = hdr.eh.e_entry & 0xffffff; 358 } 359 bootinfo.bi_esymtab = VTOP(p); 360 bootinfo.bi_kernelname = VTOP(kname); 361 bootinfo.bi_bios_dev = dsk.drive; 362 __exec((caddr_t)addr, RB_BOOTINFO | (opts & RBX_MASK), 363 MAKEBOOTDEV(dev_maj[dsk.type], 0, dsk.slice, dsk.unit, dsk.part), 364 0, 0, 0, VTOP(&bootinfo)); 365} 366 367static int 368parse() 369{ 370 char *arg = cmd; 371 char *p, *q; 372 unsigned int drv; 373 int 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 drv = *arg - '0'; 405 if (drv > 9) 406 return (-1); 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 dsk.unit = *arg - '0'; 418 if (arg[1] != ',' || dsk.unit > 9) 419 return -1; 420 arg += 2; 421 dsk.slice = WHOLE_DISK_SLICE; 422 if (arg[1] == ',') { 423 dsk.slice = *arg - '0' + 1; 424 if (dsk.slice > NDOSPART) 425 return -1; 426 arg += 2; 427 } 428 if (arg[1] != ')') 429 return -1; 430 dsk.part = *arg - 'a'; 431 if (dsk.part > 7) 432 return (-1); 433 arg += 2; 434 if (drv == -1) 435 drv = dsk.unit; 436 dsk.drive = (dsk.type <= TYPE_MAXHARD 437 ? DRV_HARD : 0) + drv; 438 dsk_meta = 0; 439 } 440 if ((i = p - arg - !*(p - 1))) { 441 if ((size_t)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 va_list ap; 517 char buf[10]; 518 char *s; 519 unsigned u; 520 int c; 521 522 va_start(ap, fmt); 523 while ((c = *fmt++)) { 524 if (c == '%') { 525 c = *fmt++; 526 switch (c) { 527 case 'c': 528 putchar(va_arg(ap, int)); 529 continue; 530 case 's': 531 for (s = va_arg(ap, char *); *s; s++) 532 putchar(*s); 533 continue; 534 case 'u': 535 u = va_arg(ap, unsigned); 536 s = buf; 537 do 538 *s++ = '0' + u % 10U; 539 while (u /= 10U); 540 while (--s >= buf) 541 putchar(*s); 542 continue; 543 } 544 } 545 putchar(c); 546 } 547 va_end(ap); 548 return; 549} 550 551static void 552putchar(int c) 553{ 554 if (c == '\n') 555 xputc('\r'); 556 xputc(c); 557} 558 559static int 560drvread(void *buf, unsigned lba, unsigned nblk) 561{ 562 static unsigned c = 0x2d5c7c2f; 563 564 printf("%c\b", c = c << 8 | c >> 24); 565 v86.ctl = V86_ADDR | V86_CALLF | V86_FLAGS; 566 v86.addr = XREADORG; /* call to xread in boot1 */ 567 v86.es = VTOPSEG(buf); 568 v86.eax = lba; 569 v86.ebx = VTOPOFF(buf); 570 v86.ecx = lba >> 16; 571 v86.edx = nblk << 8 | dsk.drive; 572 v86int(); 573 v86.ctl = V86_FLAGS; 574 if (V86_CY(v86.efl)) { 575 printf("error %u lba %u\n", v86.eax >> 8 & 0xff, lba); 576 return -1; 577 } 578 return 0; 579} 580 581static int 582keyhit(unsigned ticks) 583{ 584 uint32_t t0, t1; 585 586 if (opts & 1 << RBX_NOINTR) 587 return 0; 588 t0 = 0; 589 for (;;) { 590 if (xgetc(1)) 591 return 1; 592 t1 = *(uint32_t *)PTOV(0x46c); 593 if (!t0) 594 t0 = t1; 595 if (t1 < t0 || t1 >= t0 + ticks) 596 return 0; 597 } 598} 599 600static int 601xputc(int c) 602{ 603 if (ioctrl & IO_KEYBOARD) 604 putc(c); 605 if (ioctrl & IO_SERIAL) 606 sio_putc(c); 607 return c; 608} 609 610static int 611xgetc(int fn) 612{ 613 if (opts & 1 << RBX_NOINTR) 614 return 0; 615 for (;;) { 616 if (ioctrl & IO_KEYBOARD && getc(1)) 617 return fn ? 1 : getc(0); 618 if (ioctrl & IO_SERIAL && sio_ischar()) 619 return fn ? 1 : sio_getc(); 620 if (fn) 621 return 0; 622 } 623} 624 625static int 626getc(int fn) 627{ 628 v86.addr = 0x16; 629 v86.eax = fn << 8; 630 v86int(); 631 return fn == 0 ? v86.eax & 0xff : !V86_ZR(v86.efl); 632} 633