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#include "paths.h" 37#include "rbx.h" 38 39/* Define to 0 to omit serial support */ 40#ifndef SERIAL 41#define SERIAL 1 42#endif 43 44#define IO_KEYBOARD 1 45#define IO_SERIAL 2 46 47#if SERIAL 48#define DO_KBD (ioctrl & IO_KEYBOARD) 49#define DO_SIO (ioctrl & IO_SERIAL) 50#else 51#define DO_KBD (1) 52#define DO_SIO (0) 53#endif 54 55#define SECOND 18 /* Circa that many ticks in a second. */ 56 57#define ARGS 0x900 58#define NOPT 14 59#define NDEV 3 60#define MEM_BASE 0x12 61#define MEM_EXT 0x15 62 63#define DRV_HARD 0x80 64#define DRV_MASK 0x7f 65 66#define TYPE_AD 0 67#define TYPE_DA 1 68#define TYPE_MAXHARD TYPE_DA 69#define TYPE_FD 2 70 71extern uint32_t _end; 72 73static const char optstr[NOPT] = "DhaCcdgmnpqrsv"; /* Also 'P', 'S' */ 74static const unsigned char flags[NOPT] = { 75 RBX_DUAL, 76 RBX_SERIAL, 77 RBX_ASKNAME, 78 RBX_CDROM, 79 RBX_CONFIG, 80 RBX_KDB, 81 RBX_GDB, 82 RBX_MUTE, 83 RBX_NOINTR, 84 RBX_PAUSE, 85 RBX_QUIET, 86 RBX_DFLTROOT, 87 RBX_SINGLE, 88 RBX_VERBOSE 89}; 90 91static const char *const dev_nm[NDEV] = {"ad", "da", "fd"}; 92static const unsigned char dev_maj[NDEV] = {30, 4, 2}; 93 94static struct dsk { 95 unsigned drive; 96 unsigned type; 97 unsigned unit; 98 uint8_t slice; 99 uint8_t part; 100 unsigned start; 101 int init; 102} dsk; 103static char cmd[512], cmddup[512], knamebuf[1024]; 104static const char *kname; 105uint32_t opts; 106static struct bootinfo bootinfo; 107#if SERIAL 108static int comspeed = SIOSPD; 109static uint8_t ioctrl = IO_KEYBOARD; 110#endif 111 112void exit(int); 113static void load(void); 114static int parse(void); 115static int dskread(void *, unsigned, unsigned); 116static void printf(const char *,...); 117static void putchar(int); 118static int drvread(void *, unsigned, unsigned); 119static int keyhit(unsigned); 120static int xputc(int); 121static int xgetc(int); 122static inline int getc(int); 123 124static void memcpy(void *, const void *, int); 125static void 126memcpy(void *dst, const void *src, int len) 127{ 128 const char *s = src; 129 char *d = dst; 130 131 while (len--) 132 *d++ = *s++; 133} 134 135static inline int 136strcmp(const char *s1, const char *s2) 137{ 138 for (; *s1 == *s2 && *s1; s1++, s2++); 139 return (unsigned char)*s1 - (unsigned char)*s2; 140} 141 142#define UFS_SMALL_CGBASE 143#include "ufsread.c" 144 145static inline int 146xfsread(ufs_ino_t inode, void *buf, size_t nbyte) 147{ 148 if ((size_t)fsread(inode, buf, nbyte) != nbyte) { 149 printf("Invalid %s\n", "format"); 150 return -1; 151 } 152 return 0; 153} 154 155static inline void 156getstr(void) 157{ 158 char *s; 159 int c; 160 161 s = cmd; 162 for (;;) { 163 switch (c = xgetc(0)) { 164 case 0: 165 break; 166 case '\177': 167 case '\b': 168 if (s > cmd) { 169 s--; 170 printf("\b \b"); 171 } 172 break; 173 case '\n': 174 case '\r': 175 *s = 0; 176 return; 177 default: 178 if (s - cmd < sizeof(cmd) - 1) 179 *s++ = c; 180 putchar(c); 181 } 182 } 183} 184 185static inline void 186putc(int c) 187{ 188 v86.addr = 0x10; 189 v86.eax = 0xe00 | (c & 0xff); 190 v86.ebx = 0x7; 191 v86int(); 192} 193 194int 195main(void) 196{ 197 uint8_t autoboot; 198 ufs_ino_t ino; 199 size_t nbyte; 200 201 dmadat = (void *)(roundup2(__base + (int32_t)&_end, 0x10000) - __base); 202 v86.ctl = V86_FLAGS; 203 v86.efl = PSL_RESERVED_DEFAULT | PSL_I; 204 dsk.drive = *(uint8_t *)PTOV(ARGS); 205 dsk.type = dsk.drive & DRV_HARD ? TYPE_AD : TYPE_FD; 206 dsk.unit = dsk.drive & DRV_MASK; 207 dsk.slice = *(uint8_t *)PTOV(ARGS + 1) + 1; 208 bootinfo.bi_version = BOOTINFO_VERSION; 209 bootinfo.bi_size = sizeof(bootinfo); 210 211 /* Process configuration file */ 212 213 autoboot = 1; 214 215 if ((ino = lookup(PATH_CONFIG)) || 216 (ino = lookup(PATH_DOTCONFIG))) { 217 nbyte = fsread(ino, cmd, sizeof(cmd) - 1); 218 cmd[nbyte] = '\0'; 219 } 220 221 if (*cmd) { 222 memcpy(cmddup, cmd, sizeof(cmd)); 223 if (parse()) 224 autoboot = 0; 225 if (!OPT_CHECK(RBX_QUIET)) 226 printf("%s: %s", PATH_CONFIG, cmddup); 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 (!kname) { 237 kname = PATH_LOADER; 238 if (autoboot && !keyhit(3*SECOND)) { 239 load(); 240 kname = PATH_KERNEL; 241 } 242 } 243 244 /* Present the user with the boot2 prompt. */ 245 246 for (;;) { 247 if (!autoboot || !OPT_CHECK(RBX_QUIET)) 248 printf("\nFreeBSD/x86 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 (DO_SIO) 254 sio_flush(); 255 if (!autoboot || keyhit(3*SECOND)) 256 getstr(); 257 else if (!autoboot || !OPT_CHECK(RBX_QUIET)) 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 static Elf32_Phdr ep[2]; 281 static Elf32_Shdr es[2]; 282 caddr_t p; 283 ufs_ino_t ino; 284 uint32_t addr; 285 int 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 295 if (N_GETMAGIC(hdr.ex) == ZMAGIC) { 296 addr = hdr.ex.a_entry & 0xffffff; 297 p = PTOV(addr); 298 fs_off = PAGE_SIZE; 299 if (xfsread(ino, p, hdr.ex.a_text)) 300 return; 301 p += roundup2(hdr.ex.a_text, PAGE_SIZE); 302 if (xfsread(ino, p, hdr.ex.a_data)) 303 return; 304 } else if (IS_ELF(hdr.eh)) { 305 fs_off = hdr.eh.e_phoff; 306 for (j = i = 0; i < hdr.eh.e_phnum && j < 2; i++) { 307 if (xfsread(ino, ep + j, sizeof(ep[0]))) 308 return; 309 if (ep[j].p_type == PT_LOAD) 310 j++; 311 } 312 for (i = 0; i < 2; i++) { 313 p = PTOV(ep[i].p_paddr & 0xffffff); 314 fs_off = ep[i].p_offset; 315 if (xfsread(ino, p, ep[i].p_filesz)) 316 return; 317 } 318 p += roundup2(ep[1].p_memsz, PAGE_SIZE); 319 bootinfo.bi_symtab = VTOP(p); 320 if (hdr.eh.e_shnum == hdr.eh.e_shstrndx + 3) { 321 fs_off = hdr.eh.e_shoff + sizeof(es[0]) * 322 (hdr.eh.e_shstrndx + 1); 323 if (xfsread(ino, &es, sizeof(es))) 324 return; 325 for (i = 0; i < 2; i++) { 326 *(Elf32_Word *)p = es[i].sh_size; 327 p += sizeof(es[i].sh_size); 328 fs_off = es[i].sh_offset; 329 if (xfsread(ino, p, es[i].sh_size)) 330 return; 331 p += es[i].sh_size; 332 } 333 } 334 addr = hdr.eh.e_entry & 0xffffff; 335 bootinfo.bi_esymtab = VTOP(p); 336 } else { 337 printf("Invalid %s\n", "format"); 338 return; 339 } 340 341 bootinfo.bi_kernelname = VTOP(kname); 342 bootinfo.bi_bios_dev = dsk.drive; 343 __exec((caddr_t)addr, RB_BOOTINFO | (opts & RBX_MASK), 344 MAKEBOOTDEV(dev_maj[dsk.type], dsk.slice, dsk.unit, dsk.part), 345 0, 0, 0, VTOP(&bootinfo)); 346} 347 348static int 349parse() 350{ 351 char *arg = cmd; 352 char *ep, *p, *q; 353 const char *cp; 354 unsigned int drv; 355 int c, i, j; 356 357 while ((c = *arg++)) { 358 if (c == ' ' || c == '\t' || c == '\n') 359 continue; 360 for (p = arg; *p && *p != '\n' && *p != ' ' && *p != '\t'; p++); 361 ep = p; 362 if (*p) 363 *p++ = 0; 364 if (c == '-') { 365 while ((c = *arg++)) { 366 if (c == 'P') { 367 if (*(uint8_t *)PTOV(0x496) & 0x10) { 368 cp = "yes"; 369 } else { 370 opts |= OPT_SET(RBX_DUAL) | OPT_SET(RBX_SERIAL); 371 cp = "no"; 372 } 373 printf("Keyboard: %s\n", cp); 374 continue; 375#if SERIAL 376 } else if (c == 'S') { 377 j = 0; 378 while ((unsigned int)(i = *arg++ - '0') <= 9) 379 j = j * 10 + i; 380 if (j > 0 && i == -'0') { 381 comspeed = j; 382 break; 383 } 384 /* Fall through to error below ('S' not in optstr[]). */ 385#endif 386 } 387 for (i = 0; c != optstr[i]; i++) 388 if (i == NOPT - 1) 389 return -1; 390 opts ^= OPT_SET(flags[i]); 391 } 392#if SERIAL 393 ioctrl = OPT_CHECK(RBX_DUAL) ? (IO_SERIAL|IO_KEYBOARD) : 394 OPT_CHECK(RBX_SERIAL) ? IO_SERIAL : IO_KEYBOARD; 395 if (DO_SIO) { 396 if (sio_init(115200 / comspeed) != 0) 397 ioctrl &= ~IO_SERIAL; 398 } 399#endif 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 + 1) 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 = ep - arg)) { 442 if ((size_t)i >= sizeof(knamebuf)) 443 return -1; 444 memcpy(knamebuf, arg, i + 1); 445 kname = knamebuf; 446 } 447 } 448 arg = p; 449 } 450 return 0; 451} 452 453static int 454dskread(void *buf, unsigned lba, unsigned nblk) 455{ 456 struct dos_partition *dp; 457 struct disklabel *d; 458 char *sec; 459 unsigned i; 460 uint8_t sl; 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 static 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 if (!OPT_CHECK(RBX_QUIET)) 568 printf("%c\b", c = c << 8 | c >> 24); 569 v86.ctl = V86_ADDR | V86_CALLF | V86_FLAGS; 570 v86.addr = XREADORG; /* call to xread in boot1 */ 571 v86.es = VTOPSEG(buf); 572 v86.eax = lba; 573 v86.ebx = VTOPOFF(buf); 574 v86.ecx = lba >> 16; 575 v86.edx = nblk << 8 | dsk.drive; 576 v86int(); 577 v86.ctl = V86_FLAGS; 578 if (V86_CY(v86.efl)) { 579 printf("error %u lba %u\n", v86.eax >> 8 & 0xff, lba); 580 return -1; 581 } 582 return 0; 583} 584 585static int 586keyhit(unsigned ticks) 587{ 588 uint32_t t0, t1; 589 590 if (OPT_CHECK(RBX_NOINTR)) 591 return 0; 592 t0 = 0; 593 for (;;) { 594 if (xgetc(1)) 595 return 1; 596 t1 = *(uint32_t *)PTOV(0x46c); 597 if (!t0) 598 t0 = t1; 599 if ((uint32_t)(t1 - t0) >= ticks) 600 return 0; 601 } 602} 603 604static int 605xputc(int c) 606{ 607 if (DO_KBD) 608 putc(c); 609 if (DO_SIO) 610 sio_putc(c); 611 return c; 612} 613 614static int 615getc(int fn) 616{ 617 v86.addr = 0x16; 618 v86.eax = fn << 8; 619 v86int(); 620 return fn == 0 ? v86.eax & 0xff : !V86_ZR(v86.efl); 621} 622 623static int 624xgetc(int fn) 625{ 626 if (OPT_CHECK(RBX_NOINTR)) 627 return 0; 628 for (;;) { 629 if (DO_KBD && getc(1)) 630 return fn ? 1 : getc(0); 631 if (DO_SIO && sio_ischar()) 632 return fn ? 1 : sio_getc(); 633 if (fn) 634 return 0; 635 } 636} 637