1 /* 2 * Copyright (c) 2003,2004 The DragonFly Project. All rights reserved. 3 * 4 * This code is derived from software contributed to The DragonFly Project 5 * by Matthew Dillon <dillon@backplane.com> 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in 15 * the documentation and/or other materials provided with the 16 * distribution. 17 * 3. Neither the name of The DragonFly Project nor the names of its 18 * contributors may be used to endorse or promote products derived 19 * from this software without specific, prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 22 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 23 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 24 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 25 * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 26 * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, 27 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 28 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 29 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 30 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 31 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 32 * SUCH DAMAGE. 33 * 34 * Copyright (c) 1998 Robert Nordier 35 * All rights reserved. 36 * 37 * Redistribution and use in source and binary forms are freely 38 * permitted provided that the above copyright notice and this 39 * paragraph and the following disclaimer are duplicated in all 40 * such forms. 41 * 42 * This software is provided "AS IS" and without any express or 43 * implied warranties, including, without limitation, the implied 44 * warranties of merchantability and fitness for a particular 45 * purpose. 46 * 47 * $FreeBSD: src/sys/boot/i386/boot2/boot2.c,v 1.64 2003/08/25 23:28:31 obrien Exp $ 48 */ 49 50 #define AOUT_H_FORCE32 51 #include <sys/param.h> 52 #ifdef DISKLABEL64 53 #include <sys/disklabel64.h> 54 #else 55 #include <sys/disklabel32.h> 56 #endif 57 #include <sys/diskslice.h> 58 #include <sys/diskmbr.h> 59 #include <sys/dtype.h> 60 #include <sys/dirent.h> 61 #include <machine/bootinfo.h> 62 #include <machine/elf.h> 63 #include <machine/psl.h> 64 65 #include <stdarg.h> 66 67 #include <a.out.h> 68 69 #include <btxv86.h> 70 71 #ifdef DISKLABEL64 72 #include "boot2_64.h" 73 #else 74 #include "boot2_32.h" 75 #endif 76 #include "boot2.h" 77 #include "lib.h" 78 #include "../bootasm.h" 79 80 #define SECOND 18 /* Circa that many ticks in a second. */ 81 82 #define RBX_ASKNAME 0x0 /* -a */ 83 #define RBX_SINGLE 0x1 /* -s */ 84 #define RBX_DFLTROOT 0x5 /* -r */ 85 #define RBX_KDB 0x6 /* -d */ 86 #define RBX_CONFIG 0xa /* -c */ 87 #define RBX_VERBOSE 0xb /* -v */ 88 #define RBX_SERIAL 0xc /* -h */ 89 #define RBX_CDROM 0xd /* -C */ 90 #define RBX_GDB 0xf /* -g */ 91 #define RBX_MUTE 0x10 /* -m */ 92 #define RBX_PAUSE 0x12 /* -p */ 93 #define RBX_NOINTR 0x1c /* -n */ 94 #define RBX_VIDEO 0x1d /* -V */ 95 #define RBX_PROBEKBD 0x1e /* -P */ 96 /* 0x1f is reserved for the historical RB_BOOTINFO option */ 97 98 #define RBF_MUTE (1 << RBX_MUTE) 99 #define RBF_SERIAL (1 << RBX_SERIAL) 100 #define RBF_VIDEO (1 << RBX_VIDEO) 101 #define RBF_NOINTR (1 << RBX_NOINTR) 102 #define RBF_PROBEKBD (1 << RBX_PROBEKBD) 103 104 /* pass: -a, -s, -r, -d, -c, -v, -h, -C, -g, -m, -p, -V */ 105 #define RBX_MASK 0x2005ffff 106 107 #define PATH_CONFIG "/boot.config" 108 #define PATH_BOOT3 "/loader" /* /boot is dedicated */ 109 #define PATH_BOOT3_ALT "/boot/loader" /* /boot in root */ 110 #define PATH_KERNEL "/kernel" 111 112 #define NOPT 12 113 #define NDEV 3 114 #define MEM_BASE 0x12 115 #define MEM_EXT 0x15 116 #define V86_CY(x) ((x) & PSL_C) 117 #define V86_ZR(x) ((x) & PSL_Z) 118 119 #define DRV_HARD 0x80 120 #define DRV_MASK 0x7f 121 122 #define TYPE_AD 0 123 #define TYPE_DA 1 124 #define TYPE_MAXHARD TYPE_DA 125 #define TYPE_FD 2 126 127 #define INVALID_S "Bad %s\n" 128 129 extern uint32_t _end; 130 131 static const char optstr[NOPT] = { "VhaCgmnPprsv" }; 132 static const unsigned char flags[NOPT] = { 133 RBX_VIDEO, 134 RBX_SERIAL, 135 RBX_ASKNAME, 136 RBX_CDROM, 137 RBX_GDB, 138 RBX_MUTE, 139 RBX_NOINTR, 140 RBX_PROBEKBD, 141 RBX_PAUSE, 142 RBX_DFLTROOT, 143 RBX_SINGLE, 144 RBX_VERBOSE 145 }; 146 147 static const char *const dev_nm[NDEV] = {"ad", "da", "fd"}; 148 static const unsigned char dev_maj[NDEV] = {30, 4, 2}; 149 150 static struct dsk { 151 unsigned drive; 152 unsigned type; 153 unsigned unit; 154 uint8_t slice; 155 uint8_t part; 156 unsigned start; 157 int init; 158 } dsk; 159 160 static char cmd[512]; 161 static const char *kname; 162 static uint32_t opts = RBF_VIDEO; 163 static struct bootinfo bootinfo; 164 165 /* 166 * boot2 encapsulated ABI elements provided to *fsread.c 167 * 168 * NOTE: boot2_dmadat is extended by per-filesystem APIs 169 */ 170 uint32_t fs_off; 171 int no_io_error; 172 int ls; 173 struct boot2_dmadat *boot2_dmadat; 174 175 void exit(int); 176 static void load(void); 177 static int parse(void); 178 static int dskprobe(void); 179 static int xfsread(boot2_ino_t, void *, size_t); 180 static int drvread(void *, unsigned, unsigned); 181 static int keyhit(unsigned); 182 static int xputc(int); 183 static int xgetc(int); 184 static int getc(int); 185 186 void 187 memcpy(void *d, const void *s, int len) 188 { 189 char *dd = d; 190 const char *ss = s; 191 192 while (--len >= 0) 193 dd[len] = ss[len]; 194 } 195 196 int 197 strcmp(const char *s1, const char *s2) 198 { 199 for (; *s1 == *s2 && *s1; s1++, s2++) 200 ; 201 return ((int)((unsigned char)*s1 - (unsigned char)*s2)); 202 } 203 204 #if defined(UFS) && defined(HAMMER2FS) 205 206 const struct boot2_fsapi *fsapi; 207 208 #elif defined(UFS) 209 210 #define fsapi (&boot2_ufs_api) 211 212 #elif defined(HAMMER2FS) 213 214 #define fsapi (&boot2_hammer2_api) 215 216 #endif 217 218 static int 219 xfsread(boot2_ino_t inode, void *buf, size_t nbyte) 220 { 221 if ((size_t)fsapi->fsread(inode, buf, nbyte) != nbyte) { 222 printf(INVALID_S, "format"); 223 return -1; 224 } 225 return 0; 226 } 227 228 static inline void 229 getstr(void) 230 { 231 char *s; 232 int c; 233 234 s = cmd; 235 for (;;) { 236 switch (c = xgetc(0)) { 237 case 0: 238 break; 239 case '\177': 240 case '\b': 241 if (s > cmd) { 242 s--; 243 printf("\b \b"); 244 } 245 break; 246 case '\n': 247 case '\r': 248 *s = 0; 249 return; 250 default: 251 if (s - cmd < sizeof(cmd) - 1) 252 *s++ = c; 253 putchar(c); 254 } 255 } 256 } 257 258 static inline void 259 putc(int c) 260 { 261 v86.addr = 0x10; 262 v86.eax = 0xe00 | (c & 0xff); 263 v86.ebx = 0x7; 264 v86int(); 265 } 266 267 int 268 main(void) 269 { 270 uint8_t autoboot; 271 boot2_ino_t ino; 272 273 kname = NULL; 274 boot2_dmadat = 275 (void *)(roundup2(__base + (int32_t)&_end, 0x10000) - __base); 276 v86.ctl = V86_FLAGS; 277 v86.efl = PSL_RESERVED_DEFAULT | PSL_I; 278 dsk.drive = *(uint8_t *)PTOV(MEM_BTX_USR_ARG); 279 dsk.type = dsk.drive & DRV_HARD ? TYPE_AD : TYPE_FD; 280 dsk.unit = dsk.drive & DRV_MASK; 281 dsk.slice = *(uint8_t *)PTOV(MEM_BTX_USR_ARG + 1) + 1; 282 bootinfo.bi_version = BOOTINFO_VERSION; 283 bootinfo.bi_size = sizeof(bootinfo); 284 285 autoboot = 1; 286 287 /* 288 * Probe the default disk and process the configuration file if 289 * successful. 290 */ 291 if (dskprobe() == 0) { 292 if ((ino = fsapi->fslookup(PATH_CONFIG))) 293 fsapi->fsread(ino, cmd, sizeof(cmd)); 294 } 295 296 /* 297 * Parse config file if present. parse() will re-probe if necessary. 298 */ 299 if (cmd[0]) { 300 printf("%s: %s", PATH_CONFIG, cmd); 301 if (parse()) 302 autoboot = 0; 303 /* Do not process this command twice */ 304 *cmd = 0; 305 } 306 307 /* 308 * Setup our (serial) console after processing the config file. If 309 * the initialization fails, don't try to use the serial port. This 310 * can happen if the serial port is unmaped (happens on new laptops a lot). 311 */ 312 if ((opts & (RBF_MUTE|RBF_SERIAL|RBF_VIDEO)) == 0) 313 opts |= RBF_SERIAL|RBF_VIDEO; 314 if (opts & RBF_SERIAL) { 315 if (sio_init()) 316 opts = RBF_VIDEO; 317 } 318 319 320 /* 321 * Try to exec stage 3 boot loader. If interrupted by a keypress, 322 * or in case of failure, try to load a kernel directly instead. 323 * 324 * We have to try boot /boot/loader and /loader to support booting 325 * from a /boot partition instead of a root partition. 326 */ 327 if (autoboot && !kname) { 328 kname = PATH_BOOT3; 329 if (!keyhit(3*SECOND)) { 330 load(); 331 kname = PATH_BOOT3_ALT; 332 load(); 333 kname = PATH_KERNEL; 334 } 335 } 336 337 /* Present the user with the boot2 prompt. */ 338 339 for (;;) { 340 printf("\nDragonFly boot\n" 341 "%u:%s(%u,%c)%s: ", 342 dsk.drive & DRV_MASK, dev_nm[dsk.type], dsk.unit, 343 'a' + dsk.part, kname); 344 if (!autoboot || keyhit(5*SECOND)) 345 getstr(); 346 else 347 putchar('\n'); 348 autoboot = 0; 349 if (parse()) 350 putchar('\a'); 351 else 352 load(); 353 } 354 } 355 356 /* XXX - Needed for btxld to link the boot2 binary; do not remove. */ 357 void 358 exit(int x) 359 { 360 } 361 362 static void 363 load(void) 364 { 365 union { 366 struct exec ex; 367 Elf32_Ehdr eh; 368 } hdr; 369 static Elf32_Phdr ep[2]; 370 static Elf32_Shdr es[2]; 371 caddr_t p; 372 boot2_ino_t ino; 373 uint32_t addr; 374 int i, j; 375 376 if (!(ino = fsapi->fslookup(kname))) { 377 if (!ls) 378 printf("No %s\n", kname); 379 return; 380 } 381 if (xfsread(ino, &hdr, sizeof(hdr))) 382 return; 383 384 if (N_GETMAGIC(hdr.ex) == ZMAGIC) { 385 addr = hdr.ex.a_entry & 0xffffff; 386 p = PTOV(addr); 387 fs_off = PAGE_SIZE; 388 if (xfsread(ino, p, hdr.ex.a_text)) 389 return; 390 p += roundup2(hdr.ex.a_text, PAGE_SIZE); 391 if (xfsread(ino, p, hdr.ex.a_data)) 392 return; 393 } else if (IS_ELF(hdr.eh)) { 394 fs_off = hdr.eh.e_phoff; 395 for (j = i = 0; i < hdr.eh.e_phnum && j < 2; i++) { 396 if (xfsread(ino, ep + j, sizeof(ep[0]))) 397 return; 398 if (ep[j].p_type == PT_LOAD) 399 j++; 400 } 401 for (i = 0; i < 2; i++) { 402 p = PTOV(ep[i].p_paddr & 0xffffff); 403 fs_off = ep[i].p_offset; 404 if (xfsread(ino, p, ep[i].p_filesz)) 405 return; 406 } 407 p += roundup2(ep[1].p_memsz, PAGE_SIZE); 408 bootinfo.bi_symtab = VTOP(p); 409 if (hdr.eh.e_shnum == hdr.eh.e_shstrndx + 3) { 410 fs_off = hdr.eh.e_shoff + sizeof(es[0]) * 411 (hdr.eh.e_shstrndx + 1); 412 if (xfsread(ino, &es, sizeof(es))) 413 return; 414 for (i = 0; i < 2; i++) { 415 *(Elf32_Word *)p = es[i].sh_size; 416 p += sizeof(es[i].sh_size); 417 fs_off = es[i].sh_offset; 418 if (xfsread(ino, p, es[i].sh_size)) 419 return; 420 p += es[i].sh_size; 421 } 422 } 423 addr = hdr.eh.e_entry & 0xffffff; 424 bootinfo.bi_esymtab = VTOP(p); 425 } else { 426 printf(INVALID_S, "format"); 427 return; 428 } 429 430 bootinfo.bi_kernelname = VTOP(kname); 431 bootinfo.bi_bios_dev = dsk.drive; 432 __exec((caddr_t)addr, opts & RBX_MASK, 433 MAKEBOOTDEV(dev_maj[dsk.type], dsk.slice, dsk.unit, dsk.part), 434 0, 0, 0, VTOP(&bootinfo)); 435 } 436 437 static int 438 parse(void) 439 { 440 char *arg = cmd; 441 char *p, *q; 442 unsigned int drv; 443 int c, i; 444 445 while ((c = *arg++)) { 446 if (c == ' ' || c == '\t' || c == '\n') 447 continue; 448 for (p = arg; *p && *p != '\n' && *p != ' ' && *p != '\t'; p++) 449 ; 450 if (*p) 451 *p++ = 0; 452 if (c == '-') { 453 while ((c = *arg++)) { 454 for (i = NOPT - 1; i >= 0; --i) { 455 if (optstr[i] == c) { 456 opts ^= 1 << flags[i]; 457 goto ok; 458 } 459 } 460 return(-1); 461 ok: ; /* ugly but save space */ 462 } 463 if (opts & RBF_PROBEKBD) { 464 i = *(uint8_t *)PTOV(0x496) & 0x10; 465 if (!i) { 466 printf("NO KB\n"); 467 opts |= RBF_VIDEO | RBF_SERIAL; 468 } 469 opts &= ~RBF_PROBEKBD; 470 } 471 } else { 472 for (q = arg--; *q && *q != '('; q++); 473 if (*q) { 474 drv = -1; 475 if (arg[1] == ':') { 476 drv = *arg - '0'; 477 if (drv > 9) 478 return (-1); 479 arg += 2; 480 } 481 if (q - arg != 2) 482 return -1; 483 for (i = 0; arg[0] != dev_nm[i][0] || 484 arg[1] != dev_nm[i][1]; i++) 485 if (i == NDEV - 1) 486 return -1; 487 dsk.type = i; 488 arg += 3; 489 dsk.unit = *arg - '0'; 490 if (arg[1] != ',' || dsk.unit > 9) 491 return -1; 492 arg += 2; 493 dsk.slice = WHOLE_DISK_SLICE; 494 if (arg[1] == ',') { 495 dsk.slice = *arg - '0' + 1; 496 if (dsk.slice > NDOSPART + 1) 497 return -1; 498 arg += 2; 499 } 500 if (arg[1] != ')') 501 return -1; 502 dsk.part = *arg - 'a'; 503 if (dsk.part > 7) 504 return (-1); 505 arg += 2; 506 if (drv == -1) 507 drv = dsk.unit; 508 dsk.drive = (dsk.type <= TYPE_MAXHARD 509 ? DRV_HARD : 0) + drv; 510 } 511 kname = arg; 512 } 513 arg = p; 514 } 515 return dskprobe(); 516 } 517 518 static int 519 dskprobe(void) 520 { 521 struct dos_partition *dp; 522 #ifdef DISKLABEL64 523 struct disklabel64 *d; 524 #else 525 struct disklabel32 *d; 526 #endif 527 char *sec; 528 unsigned i; 529 uint8_t sl; 530 531 /* 532 * Probe slice table 533 */ 534 sec = boot2_dmadat->secbuf; 535 dsk.start = 0; 536 if (drvread(sec, DOSBBSECTOR, 1)) 537 return -1; 538 dp = (void *)(sec + DOSPARTOFF); 539 sl = dsk.slice; 540 if (sl < BASE_SLICE) { 541 for (i = 0; i < NDOSPART; i++) 542 if ((dp[i].dp_typ == DOSPTYP_386BSD || 543 dp[i].dp_typ == DOSPTYP_DFLYBSD) && 544 (dp[i].dp_flag & 0x80 || sl < BASE_SLICE)) { 545 sl = BASE_SLICE + i; 546 if (dp[i].dp_flag & 0x80 || 547 dsk.slice == COMPATIBILITY_SLICE) 548 break; 549 } 550 if (dsk.slice == WHOLE_DISK_SLICE) 551 dsk.slice = sl; 552 } 553 if (sl != WHOLE_DISK_SLICE) { 554 if (sl != COMPATIBILITY_SLICE) 555 dp += sl - BASE_SLICE; 556 if (dp->dp_typ != DOSPTYP_386BSD && dp->dp_typ != DOSPTYP_DFLYBSD) { 557 printf(INVALID_S, "slice"); 558 return -1; 559 } 560 dsk.start = dp->dp_start; 561 } 562 563 /* 564 * Probe label and partition table 565 */ 566 #ifdef DISKLABEL64 567 if (drvread(sec, dsk.start, (sizeof(struct disklabel64) + 511) / 512)) 568 return -1; 569 d = (void *)sec; 570 if (d->d_magic != DISKMAGIC64) { 571 printf(INVALID_S, "label"); 572 return -1; 573 } else { 574 if (dsk.part >= d->d_npartitions || d->d_partitions[dsk.part].p_bsize == 0) { 575 printf(INVALID_S, "partition"); 576 return -1; 577 } 578 dsk.start += d->d_partitions[dsk.part].p_boffset / 512; 579 } 580 #else 581 if (drvread(sec, dsk.start + LABELSECTOR32, 1)) 582 return -1; 583 d = (void *)(sec + LABELOFFSET32); 584 if (d->d_magic != DISKMAGIC32 || d->d_magic2 != DISKMAGIC32) { 585 if (dsk.part != RAW_PART) { 586 printf(INVALID_S, "label"); 587 return -1; 588 } 589 } else { 590 if (!dsk.init) { 591 if (d->d_type == DTYPE_SCSI) 592 dsk.type = TYPE_DA; 593 dsk.init++; 594 } 595 if (dsk.part >= d->d_npartitions || 596 !d->d_partitions[dsk.part].p_size) { 597 printf(INVALID_S, "partition"); 598 return -1; 599 } 600 dsk.start += d->d_partitions[dsk.part].p_offset; 601 dsk.start -= d->d_partitions[RAW_PART].p_offset; 602 } 603 #endif 604 /* 605 * Probe filesystem 606 */ 607 #if defined(UFS) && defined(HAMMER2FS) 608 if (boot2_ufs_api.fsinit() == 0) { 609 fsapi = &boot2_ufs_api; 610 } else if (boot2_hammer2_api.fsinit() == 0) { 611 fsapi = &boot2_hammer2_api; 612 } else { 613 printf("fs probe failed\n"); 614 fsapi = &boot2_ufs_api; 615 return -1; 616 } 617 return 0; 618 #else 619 return fsapi->fsinit(); 620 #endif 621 } 622 623 624 /* 625 * Read from the probed disk. We have established the slice and partition 626 * base sector. 627 */ 628 int 629 dskread(void *buf, unsigned lba, unsigned nblk) 630 { 631 return drvread(buf, dsk.start + lba, nblk); 632 } 633 634 /* 635 * boot encapsulated ABI 636 */ 637 void 638 printf(const char *fmt,...) 639 { 640 va_list ap; 641 static char buf[10]; 642 char *s; 643 unsigned u; 644 #ifdef HAMMER2FS 645 uint64_t q; 646 #endif 647 int c; 648 649 va_start(ap, fmt); 650 while ((c = *fmt++)) { 651 if (c == '%') { 652 c = *fmt++; 653 switch (c) { 654 case 'c': 655 putchar(va_arg(ap, int)); 656 continue; 657 case 's': 658 for (s = va_arg(ap, char *); *s; s++) 659 putchar(*s); 660 continue; 661 #ifdef HAMMER2FS 662 case 'q': 663 ++fmt; /* skip the 'x' */ 664 q = va_arg(ap, uint64_t); 665 s = buf; 666 do { 667 if ((q & 15) < 10) 668 *s++ = '0' + (q & 15); 669 else 670 *s++ = 'a' + (q & 15) - 10; 671 } while (q >>= 4); 672 while (--s >= buf) 673 putchar(*s); 674 continue; 675 case 'x': 676 u = va_arg(ap, unsigned); 677 s = buf; 678 do { 679 if ((u & 15) < 10) 680 *s++ = '0' + (u & 15); 681 else 682 *s++ = 'a' + (u & 15) - 10; 683 } while (u >>= 4); 684 while (--s >= buf) 685 putchar(*s); 686 continue; 687 #endif 688 case 'u': 689 u = va_arg(ap, unsigned); 690 s = buf; 691 do 692 *s++ = '0' + u % 10U; 693 while (u /= 10U); 694 while (--s >= buf) 695 putchar(*s); 696 continue; 697 } 698 } 699 putchar(c); 700 } 701 va_end(ap); 702 return; 703 } 704 705 /* 706 * boot encapsulated ABI 707 */ 708 void 709 putchar(int c) 710 { 711 if (c == '\n') 712 xputc('\r'); 713 xputc(c); 714 } 715 716 /* 717 * boot encapsulated ABI 718 */ 719 static int 720 drvread(void *buf, unsigned lba, unsigned nblk) 721 { 722 static unsigned c = 0x2d5c7c2f; /* twiddle */ 723 724 c = (c << 8) | (c >> 24); 725 xputc(c); 726 xputc('\b'); 727 v86.ctl = V86_ADDR | V86_CALLF | V86_FLAGS; 728 v86.addr = XREADORG; /* call to xread in boot1 */ 729 v86.es = VTOPSEG(buf); 730 v86.eax = lba; 731 v86.ebx = VTOPOFF(buf); 732 v86.ecx = lba >> 16; 733 v86.edx = nblk << 8 | dsk.drive; 734 v86int(); 735 v86.ctl = V86_FLAGS; 736 if (V86_CY(v86.efl) && !no_io_error) { 737 printf("error %u lba %u\n", v86.eax >> 8 & 0xff, lba); 738 return -1; 739 } 740 return 0; 741 } 742 743 static int 744 keyhit(unsigned ticks) 745 { 746 uint32_t t0, t1; 747 748 if (opts & RBF_NOINTR) 749 return 0; 750 t0 = 0; 751 for (;;) { 752 if (xgetc(1)) 753 return 1; 754 t1 = *(uint32_t *)PTOV(0x46c); 755 if (!t0) 756 t0 = t1; 757 if ((uint32_t)(t1 - t0) >= ticks) 758 return 0; 759 } 760 } 761 762 static int 763 xputc(int c) 764 { 765 if (opts & RBF_VIDEO) 766 putc(c); 767 if (opts & RBF_SERIAL) 768 sio_putc(c); 769 return c; 770 } 771 772 static int 773 getc(int fn) 774 { 775 v86.addr = 0x16; 776 v86.eax = fn << 8; 777 v86int(); 778 return fn == 0 ? v86.eax & 0xff : !V86_ZR(v86.efl); 779 } 780 781 static int 782 xgetc(int fn) 783 { 784 if (opts & RBF_NOINTR) 785 return 0; 786 for (;;) { 787 if ((opts & RBF_VIDEO) && getc(1)) 788 return fn ? 1 : getc(0); 789 if ((opts & RBF_SERIAL) && sio_ischar()) 790 return fn ? 1 : sio_getc(); 791 if (fn) 792 return 0; 793 } 794 } 795