1/*- 2 * Copyright (c) 2008-2009 TAKAHASHI Yoshihiro 3 * Copyright (c) 1998 Robert Nordier 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms are freely 7 * permitted provided that the above copyright notice and this 8 * paragraph and the following disclaimer are duplicated in all 9 * such forms. 10 * 11 * This software is provided "AS IS" and without any express or 12 * implied warranties, including, without limitation, the implied 13 * warranties of merchantability and fitness for a particular 14 * purpose. 15 */ 16 17#include <sys/cdefs.h> 18__FBSDID("$FreeBSD$"); 19 20#include <sys/param.h> 21#include <sys/disklabel.h> 22#include <sys/diskpc98.h> 23#include <sys/dirent.h> 24#include <sys/reboot.h> 25 26#include <machine/bootinfo.h> 27#include <machine/cpufunc.h> 28#include <machine/elf.h> 29 30#include <stdarg.h> 31 32#include <a.out.h> 33 34#include <btxv86.h> 35 36#include "boot2.h" 37#include "lib.h" 38#include "paths.h" 39#include "rbx.h" 40 41/* Define to 0 to omit serial support */ 42#ifndef SERIAL 43#define SERIAL 0 44#endif 45 46#define IO_KEYBOARD 1 47#define IO_SERIAL 2 48 49#if SERIAL 50#define DO_KBD (ioctrl & IO_KEYBOARD) 51#define DO_SIO (ioctrl & IO_SERIAL) 52#else 53#define DO_KBD (1) 54#define DO_SIO (0) 55#endif 56 57#define SECOND 1 /* Circa that many ticks in a second. */ 58 59#define ARGS 0x900 60#define NOPT 14 61#define NDEV 3 62 63#define DRV_DISK 0xf0 64#define DRV_UNIT 0x0f 65 66#define TYPE_AD 0 67#define TYPE_DA 1 68#define TYPE_FD 2 69 70extern uint32_t _end; 71 72static const char optstr[NOPT] = "DhaCcdgmnpqrsv"; /* Also 'P', 'S' */ 73static const unsigned char flags[NOPT] = { 74 RBX_DUAL, 75 RBX_SERIAL, 76 RBX_ASKNAME, 77 RBX_CDROM, 78 RBX_CONFIG, 79 RBX_KDB, 80 RBX_GDB, 81 RBX_MUTE, 82 RBX_NOINTR, 83 RBX_PAUSE, 84 RBX_QUIET, 85 RBX_DFLTROOT, 86 RBX_SINGLE, 87 RBX_VERBOSE 88}; 89 90static const char *const dev_nm[NDEV] = {"ad", "da", "fd"}; 91static const unsigned char dev_maj[NDEV] = {30, 4, 2}; 92static const unsigned char dev_daua[NDEV] = {0x80, 0xa0, 0x90}; 93 94static struct dsk { 95 unsigned daua; 96 unsigned type; 97 unsigned disk; 98 unsigned unit; 99 unsigned head; 100 unsigned sec; 101 uint8_t slice; 102 uint8_t part; 103 unsigned start; 104} dsk; 105static char cmd[512], cmddup[512], knamebuf[1024]; 106static const char *kname; 107uint32_t opts; 108static struct bootinfo bootinfo; 109#if SERIAL 110static int comspeed = SIOSPD; 111static uint8_t ioctrl = IO_KEYBOARD; 112#endif 113 114void exit(int); 115static void load(void); 116static int parse(void); 117static int dskread(void *, unsigned, unsigned); 118static void printf(const char *,...); 119static void putchar(int); 120static int drvread(void *, unsigned); 121static int keyhit(unsigned); 122static int xputc(int); 123static int xgetc(int); 124static inline int getc(int); 125 126static void memcpy(void *, const void *, int); 127static void 128memcpy(void *dst, const void *src, int len) 129{ 130 const char *s = src; 131 char *d = dst; 132 133 while (len--) 134 *d++ = *s++; 135} 136 137static inline int 138strcmp(const char *s1, const char *s2) 139{ 140 for (; *s1 == *s2 && *s1; s1++, s2++); 141 return (unsigned char)*s1 - (unsigned char)*s2; 142} 143 144#define UFS_SMALL_CGBASE 145#include "ufsread.c" 146 147static inline int 148xfsread(ufs_ino_t inode, void *buf, size_t nbyte) 149{ 150 if ((size_t)fsread(inode, buf, nbyte) != nbyte) { 151 printf("Invalid %s\n", "format"); 152 return -1; 153 } 154 return 0; 155} 156 157static inline void 158getstr(void) 159{ 160 char *s; 161 int c; 162 163 s = cmd; 164 for (;;) { 165 switch (c = xgetc(0)) { 166 case 0: 167 break; 168 case '\177': 169 case '\b': 170 if (s > cmd) { 171 s--; 172 printf("\b \b"); 173 } 174 break; 175 case '\n': 176 case '\r': 177 *s = 0; 178 return; 179 default: 180 if (s - cmd < sizeof(cmd) - 1) 181 *s++ = c; 182 putchar(c); 183 } 184 } 185} 186 187static inline void 188putc(int c) 189{ 190 191 v86.ctl = V86_ADDR | V86_CALLF | V86_FLAGS; 192 v86.addr = PUTCORG; /* call to putc in boot1 */ 193 v86.eax = c; 194 v86int(); 195 v86.ctl = V86_FLAGS; 196} 197 198static inline int 199is_scsi_hd(void) 200{ 201 202 if ((*(u_char *)PTOV(0x482) >> dsk.unit) & 0x01) 203 return 1; 204 205 return 0; 206} 207 208static inline void 209fix_sector_size(void) 210{ 211 u_char *p; 212 213 p = (u_char *)PTOV(0x460 + dsk.unit * 4); /* SCSI equipment parameter */ 214 215 if ((p[0] & 0x1f) == 7) { /* SCSI MO */ 216 if (!(p[3] & 0x30)) { /* 256B / sector */ 217 p[3] |= 0x10; /* forced set 512B / sector */ 218 p[3 + 0xa1000] |= 0x10; 219 } 220 } 221} 222 223static inline uint32_t 224get_diskinfo(void) 225{ 226 227 if (dsk.disk == 0x30) { /* 1440KB FD */ 228 /* 80 cylinders, 2 heads, 18 sectors */ 229 return (80 << 16) | (2 << 8) | 18; 230 } else if (dsk.disk == 0x90) { /* 1200KB FD */ 231 /* 80 cylinders, 2 heads, 15 sectors */ 232 return (80 << 16) | (2 << 8) | 15; 233 } else if (dsk.disk == 0x80 || is_scsi_hd()) { /* IDE or SCSI HDD */ 234 v86.addr = 0x1b; 235 v86.eax = 0x8400 | dsk.daua; 236 v86int(); 237 return (v86.ecx << 16) | v86.edx; 238 } 239 240 /* SCSI MO or CD */ 241 fix_sector_size(); /* SCSI MO */ 242 243 /* other SCSI devices */ 244 return (65535 << 16) | (8 << 8) | 32; 245} 246 247static void 248set_dsk(void) 249{ 250 uint32_t di; 251 252 di = get_diskinfo(); 253 254 dsk.head = (di >> 8) & 0xff; 255 dsk.sec = di & 0xff; 256 dsk.start = 0; 257} 258 259#ifdef GET_BIOSGEOM 260static uint32_t 261bd_getbigeom(int bunit) 262{ 263 int hds = 0; 264 int unit = 0x80; /* IDE HDD */ 265 u_int addr = 0x55d; 266 267 while (unit < 0xa7) { 268 if (*(u_char *)PTOV(addr) & (1 << (unit & 0x0f))) 269 if (hds++ == bunit) 270 break; 271 272 if (unit >= 0xA0) { 273 int media = ((unsigned *)PTOV(0x460))[unit & 0x0F] & 0x1F; 274 275 if (media == 7 && hds++ == bunit) /* SCSI MO */ 276 return(0xFFFE0820); /* C:65535 H:8 S:32 */ 277 } 278 if (++unit == 0x84) { 279 unit = 0xA0; /* SCSI HDD */ 280 addr = 0x482; 281 } 282 } 283 if (unit == 0xa7) 284 return 0x4F020F; /* 1200KB FD C:80 H:2 S:15 */ 285 v86.addr = 0x1b; 286 v86.eax = 0x8400 | unit; 287 v86int(); 288 if (V86_CY(v86.efl)) 289 return 0x4F020F; /* 1200KB FD C:80 H:2 S:15 */ 290 return ((v86.ecx & 0xffff) << 16) | (v86.edx & 0xffff); 291} 292#endif 293 294static int 295check_slice(void) 296{ 297 struct pc98_partition *dp; 298 char *sec; 299 unsigned i, cyl; 300 301 sec = dmadat->secbuf; 302 cyl = *(uint16_t *)PTOV(ARGS); 303 set_dsk(); 304 305 if (dsk.type == TYPE_FD) 306 return (WHOLE_DISK_SLICE); 307 if (drvread(sec, PC98_BBSECTOR)) 308 return (WHOLE_DISK_SLICE); /* Read error */ 309 dp = (void *)(sec + PC98_PARTOFF); 310 for (i = 0; i < PC98_NPARTS; i++) { 311 if (dp[i].dp_mid == DOSMID_386BSD) { 312 if (dp[i].dp_scyl <= cyl && cyl <= dp[i].dp_ecyl) 313 return (BASE_SLICE + i); 314 } 315 } 316 317 return (WHOLE_DISK_SLICE); 318} 319 320int 321main(void) 322{ 323#ifdef GET_BIOSGEOM 324 int i; 325#endif 326 uint8_t autoboot; 327 ufs_ino_t ino; 328 size_t nbyte; 329 330 dmadat = (void *)(roundup2(__base + (int32_t)&_end, 0x10000) - __base); 331 v86.ctl = V86_FLAGS; 332 v86.efl = PSL_RESERVED_DEFAULT | PSL_I; 333 dsk.daua = *(uint8_t *)PTOV(0x584); 334 dsk.disk = dsk.daua & DRV_DISK; 335 dsk.unit = dsk.daua & DRV_UNIT; 336 if (dsk.disk == 0x80) 337 dsk.type = TYPE_AD; 338 else if (dsk.disk == 0xa0) 339 dsk.type = TYPE_DA; 340 else /* if (dsk.disk == 0x30 || dsk.disk == 0x90) */ 341 dsk.type = TYPE_FD; 342 dsk.slice = check_slice(); 343#ifdef GET_BIOSGEOM 344 for (i = 0; i < N_BIOS_GEOM; i++) 345 bootinfo.bi_bios_geom[i] = bd_getbigeom(i); 346#endif 347 bootinfo.bi_version = BOOTINFO_VERSION; 348 bootinfo.bi_size = sizeof(bootinfo); 349 350 /* Process configuration file */ 351 352 autoboot = 1; 353 354 if ((ino = lookup(PATH_CONFIG)) || 355 (ino = lookup(PATH_DOTCONFIG))) { 356 nbyte = fsread(ino, cmd, sizeof(cmd) - 1); 357 cmd[nbyte] = '\0'; 358 } 359 360 if (*cmd) { 361 memcpy(cmddup, cmd, sizeof(cmd)); 362 if (parse()) 363 autoboot = 0; 364 if (!OPT_CHECK(RBX_QUIET)) 365 printf("%s: %s", PATH_CONFIG, cmddup); 366 /* Do not process this command twice */ 367 *cmd = 0; 368 } 369 370 /* 371 * Try to exec stage 3 boot loader. If interrupted by a keypress, 372 * or in case of failure, try to load a kernel directly instead. 373 */ 374 375 if (!kname) { 376 kname = PATH_LOADER; 377 if (autoboot && !keyhit(3*SECOND)) { 378 load(); 379 kname = PATH_KERNEL; 380 } 381 } 382 383 /* Present the user with the boot2 prompt. */ 384 385 for (;;) { 386 if (!autoboot || !OPT_CHECK(RBX_QUIET)) 387 printf("\nFreeBSD/pc98 boot\n" 388 "Default: %u:%s(%u,%c)%s\n" 389 "boot: ", 390 dsk.unit, dev_nm[dsk.type], dsk.unit, 391 'a' + dsk.part, kname); 392 if (DO_SIO) 393 sio_flush(); 394 if (!autoboot || keyhit(3*SECOND)) 395 getstr(); 396 else if (!autoboot || !OPT_CHECK(RBX_QUIET)) 397 putchar('\n'); 398 autoboot = 0; 399 if (parse()) 400 putchar('\a'); 401 else 402 load(); 403 } 404} 405 406/* XXX - Needed for btxld to link the boot2 binary; do not remove. */ 407void 408exit(int x) 409{ 410} 411 412static void 413load(void) 414{ 415 union { 416 struct exec ex; 417 Elf32_Ehdr eh; 418 } hdr; 419 static Elf32_Phdr ep[2]; 420 static Elf32_Shdr es[2]; 421 caddr_t p; 422 ufs_ino_t ino; 423 uint32_t addr; 424 int i, j; 425 426 if (!(ino = lookup(kname))) { 427 if (!ls) 428 printf("No %s\n", kname); 429 return; 430 } 431 if (xfsread(ino, &hdr, sizeof(hdr))) 432 return; 433 434 if (N_GETMAGIC(hdr.ex) == ZMAGIC) { 435 addr = hdr.ex.a_entry & 0xffffff; 436 p = PTOV(addr); 437 fs_off = PAGE_SIZE; 438 if (xfsread(ino, p, hdr.ex.a_text)) 439 return; 440 p += roundup2(hdr.ex.a_text, PAGE_SIZE); 441 if (xfsread(ino, p, hdr.ex.a_data)) 442 return; 443 } else if (IS_ELF(hdr.eh)) { 444 fs_off = hdr.eh.e_phoff; 445 for (j = i = 0; i < hdr.eh.e_phnum && j < 2; i++) { 446 if (xfsread(ino, ep + j, sizeof(ep[0]))) 447 return; 448 if (ep[j].p_type == PT_LOAD) 449 j++; 450 } 451 for (i = 0; i < 2; i++) { 452 p = PTOV(ep[i].p_paddr & 0xffffff); 453 fs_off = ep[i].p_offset; 454 if (xfsread(ino, p, ep[i].p_filesz)) 455 return; 456 } 457 p += roundup2(ep[1].p_memsz, PAGE_SIZE); 458 bootinfo.bi_symtab = VTOP(p); 459 if (hdr.eh.e_shnum == hdr.eh.e_shstrndx + 3) { 460 fs_off = hdr.eh.e_shoff + sizeof(es[0]) * 461 (hdr.eh.e_shstrndx + 1); 462 if (xfsread(ino, &es, sizeof(es))) 463 return; 464 for (i = 0; i < 2; i++) { 465 *(Elf32_Word *)p = es[i].sh_size; 466 p += sizeof(es[i].sh_size); 467 fs_off = es[i].sh_offset; 468 if (xfsread(ino, p, es[i].sh_size)) 469 return; 470 p += es[i].sh_size; 471 } 472 } 473 addr = hdr.eh.e_entry & 0xffffff; 474 bootinfo.bi_esymtab = VTOP(p); 475 } else { 476 printf("Invalid %s\n", "format"); 477 return; 478 } 479 480 bootinfo.bi_kernelname = VTOP(kname); 481 bootinfo.bi_bios_dev = dsk.daua; 482 __exec((caddr_t)addr, RB_BOOTINFO | (opts & RBX_MASK), 483 MAKEBOOTDEV(dev_maj[dsk.type], dsk.slice, dsk.unit, dsk.part), 484 0, 0, 0, VTOP(&bootinfo)); 485} 486 487static int 488parse() 489{ 490 char *arg = cmd; 491 char *ep, *p, *q; 492 const char *cp; 493 unsigned int drv; 494 int c, i, j; 495 496 while ((c = *arg++)) { 497 if (c == ' ' || c == '\t' || c == '\n') 498 continue; 499 for (p = arg; *p && *p != '\n' && *p != ' ' && *p != '\t'; p++); 500 ep = p; 501 if (*p) 502 *p++ = 0; 503 if (c == '-') { 504 while ((c = *arg++)) { 505 if (c == 'P') { 506 if (*(uint8_t *)PTOV(0x481) & 0x48) { 507 cp = "yes"; 508 } else { 509 opts |= OPT_SET(RBX_DUAL) | OPT_SET(RBX_SERIAL); 510 cp = "no"; 511 } 512 printf("Keyboard: %s\n", cp); 513 continue; 514#if SERIAL 515 } else if (c == 'S') { 516 j = 0; 517 while ((unsigned int)(i = *arg++ - '0') <= 9) 518 j = j * 10 + i; 519 if (j > 0 && i == -'0') { 520 comspeed = j; 521 break; 522 } 523 /* Fall through to error below ('S' not in optstr[]). */ 524#endif 525 } 526 for (i = 0; c != optstr[i]; i++) 527 if (i == NOPT - 1) 528 return -1; 529 opts ^= OPT_SET(flags[i]); 530 } 531#if SERIAL 532 ioctrl = OPT_CHECK(RBX_DUAL) ? (IO_SERIAL|IO_KEYBOARD) : 533 OPT_CHECK(RBX_SERIAL) ? IO_SERIAL : IO_KEYBOARD; 534 if (DO_SIO) { 535 if (sio_init(115200 / comspeed) != 0) 536 ioctrl &= ~IO_SERIAL; 537 } 538#endif 539 } else { 540 for (q = arg--; *q && *q != '('; q++); 541 if (*q) { 542 drv = -1; 543 if (arg[1] == ':') { 544 drv = *arg - '0'; 545 if (drv > 9) 546 return (-1); 547 arg += 2; 548 } 549 if (q - arg != 2) 550 return -1; 551 for (i = 0; arg[0] != dev_nm[i][0] || 552 arg[1] != dev_nm[i][1]; i++) 553 if (i == NDEV - 1) 554 return -1; 555 dsk.type = i; 556 arg += 3; 557 dsk.unit = *arg - '0'; 558 if (arg[1] != ',' || dsk.unit > 9) 559 return -1; 560 arg += 2; 561 dsk.slice = WHOLE_DISK_SLICE; 562 if (arg[1] == ',') { 563 dsk.slice = *arg - '0' + 1; 564 if (dsk.slice > PC98_NPARTS + 1) 565 return -1; 566 arg += 2; 567 } 568 if (arg[1] != ')') 569 return -1; 570 dsk.part = *arg - 'a'; 571 if (dsk.part > 7) 572 return (-1); 573 arg += 2; 574 if (drv == -1) 575 drv = dsk.unit; 576 dsk.disk = dev_daua[dsk.type]; 577 dsk.daua = dsk.disk | dsk.unit; 578 dsk_meta = 0; 579 } 580 if ((i = ep - arg)) { 581 if ((size_t)i >= sizeof(knamebuf)) 582 return -1; 583 memcpy(knamebuf, arg, i + 1); 584 kname = knamebuf; 585 } 586 } 587 arg = p; 588 } 589 return 0; 590} 591 592static int 593dskread(void *buf, unsigned lba, unsigned nblk) 594{ 595 struct pc98_partition *dp; 596 struct disklabel *d; 597 char *sec; 598 unsigned i; 599 uint8_t sl; 600 u_char *p; 601 602 if (!dsk_meta) { 603 sec = dmadat->secbuf; 604 set_dsk(); 605 if (dsk.type == TYPE_FD) 606 goto unsliced; 607 if (drvread(sec, PC98_BBSECTOR)) 608 return -1; 609 dp = (void *)(sec + PC98_PARTOFF); 610 sl = dsk.slice; 611 if (sl < BASE_SLICE) { 612 for (i = 0; i < PC98_NPARTS; i++) 613 if (dp[i].dp_mid == DOSMID_386BSD) { 614 sl = BASE_SLICE + i; 615 break; 616 } 617 dsk.slice = sl; 618 } 619 if (sl != WHOLE_DISK_SLICE) { 620 dp += sl - BASE_SLICE; 621 if (dp->dp_mid != DOSMID_386BSD) { 622 printf("Invalid %s\n", "slice"); 623 return -1; 624 } 625 dsk.start = dp->dp_scyl * dsk.head * dsk.sec + 626 dp->dp_shd * dsk.sec + dp->dp_ssect; 627 } 628 if (drvread(sec, dsk.start + LABELSECTOR)) 629 return -1; 630 d = (void *)(sec + LABELOFFSET); 631 if (d->d_magic != DISKMAGIC || d->d_magic2 != DISKMAGIC) { 632 if (dsk.part != RAW_PART) { 633 printf("Invalid %s\n", "label"); 634 return -1; 635 } 636 } else { 637 if (dsk.part >= d->d_npartitions || 638 !d->d_partitions[dsk.part].p_size) { 639 printf("Invalid %s\n", "partition"); 640 return -1; 641 } 642 dsk.start += d->d_partitions[dsk.part].p_offset; 643 dsk.start -= d->d_partitions[RAW_PART].p_offset; 644 } 645 unsliced: ; 646 } 647 for (p = buf; nblk; p += 512, lba++, nblk--) { 648 if ((i = drvread(p, dsk.start + lba))) 649 return i; 650 } 651 return 0; 652} 653 654static void 655printf(const char *fmt,...) 656{ 657 va_list ap; 658 static char buf[10]; 659 char *s; 660 unsigned u; 661 int c; 662 663 va_start(ap, fmt); 664 while ((c = *fmt++)) { 665 if (c == '%') { 666 c = *fmt++; 667 switch (c) { 668 case 'c': 669 putchar(va_arg(ap, int)); 670 continue; 671 case 's': 672 for (s = va_arg(ap, char *); *s; s++) 673 putchar(*s); 674 continue; 675 case 'u': 676 u = va_arg(ap, unsigned); 677 s = buf; 678 do 679 *s++ = '0' + u % 10U; 680 while (u /= 10U); 681 while (--s >= buf) 682 putchar(*s); 683 continue; 684 } 685 } 686 putchar(c); 687 } 688 va_end(ap); 689 return; 690} 691 692static void 693putchar(int c) 694{ 695 if (c == '\n') 696 xputc('\r'); 697 xputc(c); 698} 699 700static int 701drvread(void *buf, unsigned lba) 702{ 703 static unsigned c = 0x2d5c7c2f; 704 unsigned bpc, x, cyl, head, sec; 705 706 bpc = dsk.sec * dsk.head; 707 cyl = lba / bpc; 708 x = lba % bpc; 709 head = x / dsk.sec; 710 sec = x % dsk.sec; 711 712 if (!OPT_CHECK(RBX_QUIET)) 713 printf("%c\b", c = c << 8 | c >> 24); 714 v86.ctl = V86_ADDR | V86_CALLF | V86_FLAGS; 715 v86.addr = READORG; /* call to read in boot1 */ 716 v86.ecx = cyl; 717 v86.edx = (head << 8) | sec; 718 v86.edi = lba; 719 v86.ebx = 512; 720 v86.es = VTOPSEG(buf); 721 v86.ebp = VTOPOFF(buf); 722 v86int(); 723 v86.ctl = V86_FLAGS; 724 if (V86_CY(v86.efl)) { 725 printf("error %u c/h/s %u/%u/%u lba %u\n", v86.eax >> 8 & 0xff, 726 cyl, head, sec, lba); 727 return -1; 728 } 729 return 0; 730} 731 732static inline void 733delay(void) 734{ 735 int i; 736 737 i = 800; 738 do { 739 outb(0x5f, 0); /* about 600ns */ 740 } while (--i >= 0); 741} 742 743static int 744keyhit(unsigned sec) 745{ 746 unsigned i; 747 748 if (OPT_CHECK(RBX_NOINTR)) 749 return 0; 750 for (i = 0; i < sec * 1000; i++) { 751 if (xgetc(1)) 752 return 1; 753 delay(); 754 } 755 return 0; 756} 757 758static int 759xputc(int c) 760{ 761 if (DO_KBD) 762 putc(c); 763 if (DO_SIO) 764 sio_putc(c); 765 return c; 766} 767 768static int 769getc(int fn) 770{ 771 v86.addr = 0x18; 772 v86.eax = fn << 8; 773 v86int(); 774 if (fn) 775 return (v86.ebx >> 8) & 0x01; 776 else 777 return v86.eax & 0xff; 778} 779 780static int 781xgetc(int fn) 782{ 783 if (OPT_CHECK(RBX_NOINTR)) 784 return 0; 785 for (;;) { 786 if (DO_KBD && getc(1)) 787 return fn ? 1 : getc(0); 788 if (DO_SIO && sio_ischar()) 789 return fn ? 1 : sio_getc(); 790 if (fn) 791 return 0; 792 } 793} 794