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$"); 18 19#include <sys/param.h> 20#include <sys/disklabel.h> 21#include <sys/diskmbr.h> 22#include <sys/dirent.h> 23#include <sys/reboot.h> 24 25#include <machine/bootinfo.h> 26#include <machine/elf.h> 27 28#include <stdarg.h> 29 30#include <a.out.h> 31 32#include <btxv86.h> 33 34#include "boot2.h" 35#include "lib.h" 36 37#define IO_KEYBOARD 1 38#define IO_SERIAL 2 39 40#define SECOND 18 /* Circa that many ticks in a second. */ 41 42#define RBX_ASKNAME 0x0 /* -a */ 43#define RBX_SINGLE 0x1 /* -s */ 44/* 0x2 is reserved for log2(RB_NOSYNC). */ 45/* 0x3 is reserved for log2(RB_HALT). */ 46/* 0x4 is reserved for log2(RB_INITNAME). */ 47#define RBX_DFLTROOT 0x5 /* -r */ 48#define RBX_KDB 0x6 /* -d */ 49/* 0x7 is reserved for log2(RB_RDONLY). */ 50/* 0x8 is reserved for log2(RB_DUMP). */ 51/* 0x9 is reserved for log2(RB_MINIROOT). */ 52#define RBX_CONFIG 0xa /* -c */ 53#define RBX_VERBOSE 0xb /* -v */ 54#define RBX_SERIAL 0xc /* -h */ 55#define RBX_CDROM 0xd /* -C */ 56/* 0xe is reserved for log2(RB_POWEROFF). */ 57#define RBX_GDB 0xf /* -g */ 58#define RBX_MUTE 0x10 /* -m */ 59/* 0x11 is reserved for log2(RB_SELFTEST). */ 60/* 0x12 is reserved for boot programs. */ 61/* 0x13 is reserved for boot programs. */ 62#define RBX_PAUSE 0x14 /* -p */ 63#define RBX_QUIET 0x15 /* -q */ 64#define RBX_NOINTR 0x1c /* -n */ 65/* 0x1d is reserved for log2(RB_MULTIPLE) and is just misnamed here. */ 66#define RBX_DUAL 0x1d /* -D */ 67/* 0x1f is reserved for log2(RB_BOOTINFO). */ 68 69/* pass: -a, -s, -r, -d, -c, -v, -h, -C, -g, -m, -p, -D */ 70#define RBX_MASK (OPT_SET(RBX_ASKNAME) | OPT_SET(RBX_SINGLE) | \ 71 OPT_SET(RBX_DFLTROOT) | OPT_SET(RBX_KDB ) | \ 72 OPT_SET(RBX_CONFIG) | OPT_SET(RBX_VERBOSE) | \ 73 OPT_SET(RBX_SERIAL) | OPT_SET(RBX_CDROM) | \ 74 OPT_SET(RBX_GDB ) | OPT_SET(RBX_MUTE) | \ 75 OPT_SET(RBX_PAUSE) | OPT_SET(RBX_DUAL)) 76 77#define PATH_DOTCONFIG "/boot.config" 78#define PATH_CONFIG "/boot/config" 79#define PATH_BOOT3 "/boot/loader" 80#define PATH_KERNEL "/boot/kernel/kernel" 81 82#define ARGS 0x900 83#define NOPT 14 84#define NDEV 3 85#define MEM_BASE 0x12 86#define MEM_EXT 0x15 87 88#define DRV_HARD 0x80 89#define DRV_MASK 0x7f 90 91#define TYPE_AD 0 92#define TYPE_DA 1 93#define TYPE_MAXHARD TYPE_DA 94#define TYPE_FD 2 95 96#define OPT_SET(opt) (1 << (opt)) 97#define OPT_CHECK(opt) ((opts) & OPT_SET(opt)) 98 99extern uint32_t _end; 100 101static const char optstr[NOPT] = "DhaCcdgmnpqrsv"; /* Also 'P', 'S' */ 102static const unsigned char flags[NOPT] = { 103 RBX_DUAL, 104 RBX_SERIAL, 105 RBX_ASKNAME, 106 RBX_CDROM, 107 RBX_CONFIG, 108 RBX_KDB, 109 RBX_GDB, 110 RBX_MUTE, 111 RBX_NOINTR, 112 RBX_PAUSE, 113 RBX_QUIET, 114 RBX_DFLTROOT, 115 RBX_SINGLE, 116 RBX_VERBOSE 117}; 118 119static const char *const dev_nm[NDEV] = {"ad", "da", "fd"}; 120static const unsigned char dev_maj[NDEV] = {30, 4, 2}; 121 122static struct dsk { 123 unsigned drive; 124 unsigned type; 125 unsigned unit; 126 uint8_t slice; 127 uint8_t part; 128 unsigned start; 129 int init; 130} dsk; 131static char cmd[512], cmddup[512], knamebuf[1024]; 132static const char *kname; 133static uint32_t opts; 134static int comspeed = SIOSPD; 135static struct bootinfo bootinfo; 136static uint8_t ioctrl = IO_KEYBOARD; 137 138void exit(int); 139static void load(void); 140static int parse(void); 141static int dskread(void *, unsigned, unsigned); 142static void printf(const char *,...); 143static void putchar(int); 144static int drvread(void *, unsigned, unsigned); 145static int keyhit(unsigned); 146static int xputc(int); 147static int xgetc(int); 148static inline int getc(int); 149 150static void memcpy(void *, const void *, int); 151static void 152memcpy(void *dst, const void *src, int len) 153{ 154 const char *s = src; 155 char *d = dst; 156 157 while (len--) 158 *d++ = *s++; 159} 160 161static inline int 162strcmp(const char *s1, const char *s2) 163{ 164 for (; *s1 == *s2 && *s1; s1++, s2++); 165 return (unsigned char)*s1 - (unsigned char)*s2; 166} 167 168#define UFS_SMALL_CGBASE 169#include "ufsread.c" 170 171static inline int 172xfsread(ufs_ino_t inode, void *buf, size_t nbyte) 173{ 174 if ((size_t)fsread(inode, buf, nbyte) != nbyte) { 175 printf("Invalid %s\n", "format"); 176 return -1; 177 } 178 return 0; 179} 180 181static inline void 182getstr(void) 183{ 184 char *s; 185 int c; 186 187 s = cmd; 188 for (;;) { 189 switch (c = xgetc(0)) { 190 case 0: 191 break; 192 case '\177': 193 case '\b': 194 if (s > cmd) { 195 s--; 196 printf("\b \b"); 197 } 198 break; 199 case '\n': 200 case '\r': 201 *s = 0; 202 return; 203 default: 204 if (s - cmd < sizeof(cmd) - 1) 205 *s++ = c; 206 putchar(c); 207 } 208 } 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 uint8_t autoboot; 224 ufs_ino_t ino; 225 size_t nbyte; 226 227 dmadat = (void *)(roundup2(__base + (int32_t)&_end, 0x10000) - __base); 228 v86.ctl = V86_FLAGS; 229 v86.efl = PSL_RESERVED_DEFAULT | PSL_I; 230 dsk.drive = *(uint8_t *)PTOV(ARGS); 231 dsk.type = dsk.drive & DRV_HARD ? TYPE_AD : TYPE_FD; 232 dsk.unit = dsk.drive & DRV_MASK; 233 dsk.slice = *(uint8_t *)PTOV(ARGS + 1) + 1; 234 bootinfo.bi_version = BOOTINFO_VERSION; 235 bootinfo.bi_size = sizeof(bootinfo); 236 237 /* Process configuration file */ 238 239 autoboot = 1; 240 241 if ((ino = lookup(PATH_CONFIG)) || 242 (ino = lookup(PATH_DOTCONFIG))) { 243 nbyte = fsread(ino, cmd, sizeof(cmd) - 1); 244 cmd[nbyte] = '\0'; 245 } 246 247 if (*cmd) { 248 memcpy(cmddup, cmd, sizeof(cmd)); 249 if (parse()) 250 autoboot = 0; 251 if (!OPT_CHECK(RBX_QUIET)) 252 printf("%s: %s", PATH_CONFIG, cmddup); 253 /* Do not process this command twice */ 254 *cmd = 0; 255 } 256 257 /* 258 * Try to exec stage 3 boot loader. If interrupted by a keypress, 259 * or in case of failure, try to load a kernel directly instead. 260 */ 261 262 if (!kname) { 263 kname = PATH_BOOT3; 264 if (autoboot && !keyhit(3*SECOND)) { 265 load(); 266 kname = PATH_KERNEL; 267 } 268 } 269 270 /* Present the user with the boot2 prompt. */ 271 272 for (;;) { 273 if (!autoboot || !OPT_CHECK(RBX_QUIET)) 274 printf("\nFreeBSD/x86 boot\n" 275 "Default: %u:%s(%u,%c)%s\n" 276 "boot: ", 277 dsk.drive & DRV_MASK, dev_nm[dsk.type], dsk.unit, 278 'a' + dsk.part, kname); 279 if (ioctrl & IO_SERIAL) 280 sio_flush(); 281 if (!autoboot || keyhit(3*SECOND)) 282 getstr(); 283 else if (!autoboot || !OPT_CHECK(RBX_QUIET)) 284 putchar('\n'); 285 autoboot = 0; 286 if (parse()) 287 putchar('\a'); 288 else 289 load(); 290 } 291} 292 293/* XXX - Needed for btxld to link the boot2 binary; do not remove. */ 294void 295exit(int x) 296{ 297} 298 299static void 300load(void) 301{ 302 union { 303 struct exec ex; 304 Elf32_Ehdr eh; 305 } hdr; 306 static Elf32_Phdr ep[2]; 307 static Elf32_Shdr es[2]; 308 caddr_t p; 309 ufs_ino_t ino; 310 uint32_t addr; 311 int i, j; 312 313 if (!(ino = lookup(kname))) { 314 if (!ls) 315 printf("No %s\n", kname); 316 return; 317 } 318 if (xfsread(ino, &hdr, sizeof(hdr))) 319 return; 320 321 if (N_GETMAGIC(hdr.ex) == ZMAGIC) { 322 addr = hdr.ex.a_entry & 0xffffff; 323 p = PTOV(addr); 324 fs_off = PAGE_SIZE; 325 if (xfsread(ino, p, hdr.ex.a_text)) 326 return; 327 p += roundup2(hdr.ex.a_text, PAGE_SIZE); 328 if (xfsread(ino, p, hdr.ex.a_data)) 329 return; 330 } else if (IS_ELF(hdr.eh)) { 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 *(Elf32_Word *)p = 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 bootinfo.bi_esymtab = VTOP(p); 362 } else { 363 printf("Invalid %s\n", "format"); 364 return; 365 } 366 367 bootinfo.bi_kernelname = VTOP(kname); 368 bootinfo.bi_bios_dev = dsk.drive; 369 __exec((caddr_t)addr, RB_BOOTINFO | (opts & RBX_MASK), 370 MAKEBOOTDEV(dev_maj[dsk.type], dsk.slice, dsk.unit, dsk.part), 371 0, 0, 0, VTOP(&bootinfo)); 372} 373 374static int 375parse() 376{ 377 char *arg = cmd; 378 char *ep, *p, *q; 379 const char *cp; 380 unsigned int drv; 381 int c, i, j; 382 383 while ((c = *arg++)) { 384 if (c == ' ' || c == '\t' || c == '\n') 385 continue; 386 for (p = arg; *p && *p != '\n' && *p != ' ' && *p != '\t'; p++); 387 ep = p; 388 if (*p) 389 *p++ = 0; 390 if (c == '-') { 391 while ((c = *arg++)) { 392 if (c == 'P') { 393 if (*(uint8_t *)PTOV(0x496) & 0x10) { 394 cp = "yes"; 395 } else { 396 opts |= OPT_SET(RBX_DUAL) | OPT_SET(RBX_SERIAL); 397 cp = "no"; 398 } 399 printf("Keyboard: %s\n", cp); 400 continue; 401 } else if (c == 'S') { 402 j = 0; 403 while ((unsigned int)(i = *arg++ - '0') <= 9) 404 j = j * 10 + i; 405 if (j > 0 && i == -'0') { 406 comspeed = j; 407 break; 408 } 409 /* Fall through to error below ('S' not in optstr[]). */ 410 } 411 for (i = 0; c != optstr[i]; i++) 412 if (i == NOPT - 1) 413 return -1; 414 opts ^= OPT_SET(flags[i]); 415 } 416 ioctrl = OPT_CHECK(RBX_DUAL) ? (IO_SERIAL|IO_KEYBOARD) : 417 OPT_CHECK(RBX_SERIAL) ? IO_SERIAL : IO_KEYBOARD; 418 if (ioctrl & IO_SERIAL) { 419 if (sio_init(115200 / comspeed) != 0) 420 ioctrl &= ~IO_SERIAL; 421 } 422 } else { 423 for (q = arg--; *q && *q != '('; q++); 424 if (*q) { 425 drv = -1; 426 if (arg[1] == ':') { 427 drv = *arg - '0'; 428 if (drv > 9) 429 return (-1); 430 arg += 2; 431 } 432 if (q - arg != 2) 433 return -1; 434 for (i = 0; arg[0] != dev_nm[i][0] || 435 arg[1] != dev_nm[i][1]; i++) 436 if (i == NDEV - 1) 437 return -1; 438 dsk.type = i; 439 arg += 3; 440 dsk.unit = *arg - '0'; 441 if (arg[1] != ',' || dsk.unit > 9) 442 return -1; 443 arg += 2; 444 dsk.slice = WHOLE_DISK_SLICE; 445 if (arg[1] == ',') { 446 dsk.slice = *arg - '0' + 1; 447 if (dsk.slice > NDOSPART + 1) 448 return -1; 449 arg += 2; 450 } 451 if (arg[1] != ')') 452 return -1; 453 dsk.part = *arg - 'a'; 454 if (dsk.part > 7) 455 return (-1); 456 arg += 2; 457 if (drv == -1) 458 drv = dsk.unit; 459 dsk.drive = (dsk.type <= TYPE_MAXHARD 460 ? DRV_HARD : 0) + drv; 461 dsk_meta = 0; 462 } 463 if ((i = ep - arg)) { 464 if ((size_t)i >= sizeof(knamebuf)) 465 return -1; 466 memcpy(knamebuf, arg, i + 1); 467 kname = knamebuf; 468 } 469 } 470 arg = p; 471 } 472 return 0; 473} 474 475static int 476dskread(void *buf, unsigned lba, unsigned nblk) 477{ 478 struct dos_partition *dp; 479 struct disklabel *d; 480 char *sec; 481 unsigned i; 482 uint8_t sl; 483 484 if (!dsk_meta) { 485 sec = dmadat->secbuf; 486 dsk.start = 0; 487 if (drvread(sec, DOSBBSECTOR, 1)) 488 return -1; 489 dp = (void *)(sec + DOSPARTOFF); 490 sl = dsk.slice; 491 if (sl < BASE_SLICE) { 492 for (i = 0; i < NDOSPART; i++) 493 if (dp[i].dp_typ == DOSPTYP_386BSD && 494 (dp[i].dp_flag & 0x80 || sl < BASE_SLICE)) { 495 sl = BASE_SLICE + i; 496 if (dp[i].dp_flag & 0x80 || 497 dsk.slice == COMPATIBILITY_SLICE) 498 break; 499 } 500 if (dsk.slice == WHOLE_DISK_SLICE) 501 dsk.slice = sl; 502 } 503 if (sl != WHOLE_DISK_SLICE) { 504 if (sl != COMPATIBILITY_SLICE) 505 dp += sl - BASE_SLICE; 506 if (dp->dp_typ != DOSPTYP_386BSD) { 507 printf("Invalid %s\n", "slice"); 508 return -1; 509 } 510 dsk.start = dp->dp_start; 511 } 512 if (drvread(sec, dsk.start + LABELSECTOR, 1)) 513 return -1; 514 d = (void *)(sec + LABELOFFSET); 515 if (d->d_magic != DISKMAGIC || d->d_magic2 != DISKMAGIC) { 516 if (dsk.part != RAW_PART) { 517 printf("Invalid %s\n", "label"); 518 return -1; 519 } 520 } else { 521 if (!dsk.init) { 522 if (d->d_type == DTYPE_SCSI) 523 dsk.type = TYPE_DA; 524 dsk.init++; 525 } 526 if (dsk.part >= d->d_npartitions || 527 !d->d_partitions[dsk.part].p_size) { 528 printf("Invalid %s\n", "partition"); 529 return -1; 530 } 531 dsk.start += d->d_partitions[dsk.part].p_offset; 532 dsk.start -= d->d_partitions[RAW_PART].p_offset; 533 } 534 } 535 return drvread(buf, dsk.start + lba, nblk); 536} 537 538static void 539printf(const char *fmt,...) 540{ 541 va_list ap; 542 static char buf[10]; 543 char *s; 544 unsigned u; 545 int c; 546 547 va_start(ap, fmt); 548 while ((c = *fmt++)) { 549 if (c == '%') { 550 c = *fmt++; 551 switch (c) { 552 case 'c': 553 putchar(va_arg(ap, int)); 554 continue; 555 case 's': 556 for (s = va_arg(ap, char *); *s; s++) 557 putchar(*s); 558 continue; 559 case 'u': 560 u = va_arg(ap, unsigned); 561 s = buf; 562 do 563 *s++ = '0' + u % 10U; 564 while (u /= 10U); 565 while (--s >= buf) 566 putchar(*s); 567 continue; 568 } 569 } 570 putchar(c); 571 } 572 va_end(ap); 573 return; 574} 575 576static void 577putchar(int c) 578{ 579 if (c == '\n') 580 xputc('\r'); 581 xputc(c); 582} 583 584static int 585drvread(void *buf, unsigned lba, unsigned nblk) 586{ 587 static unsigned c = 0x2d5c7c2f; 588 589 if (!OPT_CHECK(RBX_QUIET)) 590 printf("%c\b", c = c << 8 | c >> 24); 591 v86.ctl = V86_ADDR | V86_CALLF | V86_FLAGS; 592 v86.addr = XREADORG; /* call to xread in boot1 */ 593 v86.es = VTOPSEG(buf); 594 v86.eax = lba; 595 v86.ebx = VTOPOFF(buf); 596 v86.ecx = lba >> 16; 597 v86.edx = nblk << 8 | dsk.drive; 598 v86int(); 599 v86.ctl = V86_FLAGS; 600 if (V86_CY(v86.efl)) { 601 printf("error %u lba %u\n", v86.eax >> 8 & 0xff, lba); 602 return -1; 603 } 604 return 0; 605} 606 607static int 608keyhit(unsigned ticks) 609{ 610 uint32_t t0, t1; 611 612 if (OPT_CHECK(RBX_NOINTR)) 613 return 0; 614 t0 = 0; 615 for (;;) { 616 if (xgetc(1)) 617 return 1; 618 t1 = *(uint32_t *)PTOV(0x46c); 619 if (!t0) 620 t0 = t1; 621 if ((uint32_t)(t1 - t0) >= ticks) 622 return 0; 623 } 624} 625 626static int 627xputc(int c) 628{ 629 if (ioctrl & IO_KEYBOARD) 630 putc(c); 631 if (ioctrl & IO_SERIAL) 632 sio_putc(c); 633 return c; 634} 635 636static int 637getc(int fn) 638{ 639 v86.addr = 0x16; 640 v86.eax = fn << 8; 641 v86int(); 642 return fn == 0 ? v86.eax & 0xff : !V86_ZR(v86.efl); 643} 644 645static int 646xgetc(int fn) 647{ 648 if (OPT_CHECK(RBX_NOINTR)) 649 return 0; 650 for (;;) { 651 if (ioctrl & IO_KEYBOARD && getc(1)) 652 return fn ? 1 : getc(0); 653 if (ioctrl & IO_SERIAL && sio_ischar()) 654 return fn ? 1 : sio_getc(); 655 if (fn) 656 return 0; 657 } 658} 659