gptboot.c revision 80751
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/* 17 * $FreeBSD: head/sys/boot/i386/gptboot/gptboot.c 80751 2001-07-31 19:50:09Z jhb $ 18 */ 19 20#include <sys/param.h> 21#include <sys/reboot.h> 22#include <sys/diskslice.h> 23#include <sys/disklabel.h> 24#include <sys/dirent.h> 25#include <machine/bootinfo.h> 26#include <machine/elf.h> 27 28#include <ufs/ffs/fs.h> 29#include <ufs/ufs/dinode.h> 30 31#include <stdarg.h> 32 33#include <a.out.h> 34 35#include <btxv86.h> 36 37#include "boot2.h" 38#include "lib.h" 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_DUAL 0x1d /* -D */ 50#define RBX_PROBEKBD 0x1e /* -P */ 51 52#define RBX_MASK 0xffff 53 54#define PATH_CONFIG "/boot.config" 55#define PATH_BOOT3 "/boot/loader" 56#define PATH_KERNEL "/kernel" 57 58#define ARGS 0x900 59#define NOPT 11 60#define BSIZEMAX 8192 61#define NDEV 5 62#define MEM_BASE 0x12 63#define MEM_EXT 0x15 64#define V86_CY(x) ((x) & 1) 65#define V86_ZR(x) ((x) & 0x40) 66 67#define DRV_HARD 0x80 68#define DRV_MASK 0x7f 69 70#define TYPE_AD 0 71#define TYPE_WD 1 72#define TYPE_WFD 2 73#define TYPE_FD 3 74#define TYPE_DA 4 75 76extern uint32_t _end; 77 78static const char optstr[NOPT] = "DhaCcdgPrsv"; 79static const unsigned char flags[NOPT] = { 80 RBX_DUAL, 81 RBX_SERIAL, 82 RBX_ASKNAME, 83 RBX_CDROM, 84 RBX_CONFIG, 85 RBX_KDB, 86 RBX_GDB, 87 RBX_PROBEKBD, 88 RBX_DFLTROOT, 89 RBX_SINGLE, 90 RBX_VERBOSE 91}; 92 93static const char *const dev_nm[] = {"ad", "wd", " ", "fd", "da"}; 94static const unsigned dev_maj[] = {30, 0, 1, 2, 4}; 95 96static struct dsk { 97 unsigned drive; 98 unsigned type; 99 unsigned unit; 100 unsigned slice; 101 unsigned part; 102 unsigned start; 103 int init; 104 int meta; 105} dsk; 106static char cmd[512]; 107static char kname[1024]; 108static uint32_t opts; 109static struct bootinfo bootinfo; 110static int ls; 111static uint32_t fs_off; 112static uint8_t ioctrl = 0x1; 113 114void exit(int); 115static void load(const char *); 116static int parse(char *); 117static ino_t lookup(const char *); 118static int xfsread(ino_t, void *, size_t); 119static ssize_t fsread(ino_t, void *, size_t); 120static int dskread(void *, unsigned, unsigned); 121static int printf(const char *,...); 122static int putchar(int); 123static void *memcpy(void *, const void *, size_t); 124static void *malloc(size_t); 125static uint32_t memsize(int); 126static int drvread(void *, unsigned, unsigned); 127static int keyhit(unsigned); 128static int xputc(int); 129static int xgetc(int); 130static int getc(int); 131 132static inline void 133readfile(const char *fname, void *buf, size_t size) 134{ 135 ino_t ino; 136 137 if ((ino = lookup(fname))) 138 fsread(ino, buf, size); 139} 140 141static inline int 142strcmp(const char *s1, const char *s2) 143{ 144 for (; *s1 == *s2 && *s1; s1++, s2++); 145 return (u_char)*s1 - (u_char)*s2; 146} 147 148static inline int 149fsfind(const char *name, ino_t * ino) 150{ 151 char buf[DEV_BSIZE]; 152 struct dirent *d; 153 char *s; 154 ssize_t n; 155 156 fs_off = 0; 157 while ((n = fsread(*ino, buf, DEV_BSIZE)) > 0) 158 for (s = buf; s < buf + DEV_BSIZE;) { 159 d = (void *)s; 160 if (ls) 161 printf("%s ", d->d_name); 162 else if (!strcmp(name, d->d_name)) { 163 *ino = d->d_fileno; 164 return d->d_type; 165 } 166 s += d->d_reclen; 167 } 168 if (n != -1 && ls) 169 putchar('\n'); 170 return 0; 171} 172 173static inline int 174getchar(void) 175{ 176 int c; 177 178 c = xgetc(0); 179 if (c == '\r') 180 c = '\n'; 181 return c; 182} 183 184static inline void 185getstr(char *str, int size) 186{ 187 char *s; 188 int c; 189 190 s = str; 191 do { 192 switch (c = getchar()) { 193 case 0: 194 break; 195 case '\b': 196 case '\177': 197 if (s > str) { 198 s--; 199 putchar('\b'); 200 putchar(' '); 201 } else 202 c = 0; 203 break; 204 case '\n': 205 *s = 0; 206 break; 207 default: 208 if (s - str < size - 1) 209 *s++ = c; 210 } 211 if (c) 212 putchar(c); 213 } while (c != '\n'); 214} 215 216static inline uint32_t 217drvinfo(int drive) 218{ 219 v86.addr = 0x13; 220 v86.eax = 0x800; 221 v86.edx = DRV_HARD + drive; 222 v86int(); 223 if (V86_CY(v86.efl)) 224 return 0x4f010f; 225 return ((v86.ecx & 0xc0) << 18) | ((v86.ecx & 0xff00) << 8) | 226 (v86.edx & 0xff00) | (v86.ecx & 0x3f); 227} 228 229static inline void 230putc(int c) 231{ 232 v86.addr = 0x10; 233 v86.eax = 0xe00 | (c & 0xff); 234 v86.ebx = 0x7; 235 v86int(); 236} 237 238int 239main(void) 240{ 241 int autoboot, i; 242 243 v86.ctl = V86_FLAGS; 244 dsk.drive = *(uint8_t *)PTOV(ARGS); 245 dsk.type = dsk.drive & DRV_HARD ? TYPE_AD : TYPE_FD; 246 dsk.unit = dsk.drive & DRV_MASK; 247 dsk.slice = *(uint8_t *)PTOV(ARGS + 1) + 1; 248 bootinfo.bi_version = BOOTINFO_VERSION; 249 bootinfo.bi_size = sizeof(bootinfo); 250 bootinfo.bi_basemem = memsize(MEM_BASE); 251 bootinfo.bi_extmem = memsize(MEM_EXT); 252 bootinfo.bi_memsizes_valid++; 253 for (i = 0; i < N_BIOS_GEOM; i++) 254 bootinfo.bi_bios_geom[i] = drvinfo(i); 255 autoboot = 2; 256 readfile(PATH_CONFIG, cmd, sizeof(cmd)); 257 if (*cmd) { 258 printf("%s: %s", PATH_CONFIG, cmd); 259 if (parse(cmd)) 260 autoboot = 0; 261 *cmd = 0; 262 } 263 if (autoboot && !*kname) { 264 if (autoboot == 2) { 265 memcpy(kname, PATH_BOOT3, sizeof(PATH_BOOT3)); 266 if (!keyhit(0x37)) { 267 load(kname); 268 autoboot = 1; 269 } 270 } 271 if (autoboot == 1) 272 memcpy(kname, PATH_KERNEL, sizeof(PATH_KERNEL)); 273 } 274 for (;;) { 275 printf(" \n>> FreeBSD/i386 BOOT\n" 276 "Default: %u:%s(%u,%c)%s\n" 277 "boot: ", 278 dsk.drive & DRV_MASK, dev_nm[dsk.type], dsk.unit, 279 'a' + dsk.part, kname); 280 if (ioctrl & 0x2) 281 sio_flush(); 282 if (!autoboot || keyhit(0x5a)) 283 getstr(cmd, sizeof(cmd)); 284 else 285 putchar('\n'); 286 autoboot = 0; 287 if (parse(cmd)) 288 putchar('\a'); 289 else 290 load(kname); 291 } 292} 293 294/* XXX - Needed for btxld to link the boot2 binary; do not remove. */ 295void 296exit(int x) 297{ 298} 299 300static void 301load(const char *fname) 302{ 303 union { 304 struct exec ex; 305 Elf32_Ehdr eh; 306 } hdr; 307 Elf32_Phdr ep[2]; 308 Elf32_Shdr es[2]; 309 caddr_t p; 310 ino_t ino; 311 uint32_t addr, x; 312 int fmt, i, j; 313 314 if (!(ino = lookup(fname))) { 315 if (!ls) 316 printf("No %s\n", fname); 317 return; 318 } 319 if (xfsread(ino, &hdr, sizeof(hdr))) 320 return; 321 if (N_GETMAGIC(hdr.ex) == ZMAGIC) 322 fmt = 0; 323 else if (IS_ELF(hdr.eh)) 324 fmt = 1; 325 else { 326 printf("Invalid %s\n", "format"); 327 return; 328 } 329 if (fmt == 0) { 330 addr = hdr.ex.a_entry & 0xffffff; 331 p = PTOV(addr); 332 fs_off = PAGE_SIZE; 333 if (xfsread(ino, p, hdr.ex.a_text)) 334 return; 335 p += roundup2(hdr.ex.a_text, PAGE_SIZE); 336 if (xfsread(ino, p, hdr.ex.a_data)) 337 return; 338 p += hdr.ex.a_data + roundup2(hdr.ex.a_bss, PAGE_SIZE); 339 bootinfo.bi_symtab = VTOP(p); 340 memcpy(p, &hdr.ex.a_syms, sizeof(hdr.ex.a_syms)); 341 p += sizeof(hdr.ex.a_syms); 342 if (hdr.ex.a_syms) { 343 if (xfsread(ino, p, hdr.ex.a_syms)) 344 return; 345 p += hdr.ex.a_syms; 346 if (xfsread(ino, p, sizeof(int))) 347 return; 348 x = *(uint32_t *)p; 349 p += sizeof(int); 350 x -= sizeof(int); 351 if (xfsread(ino, p, x)) 352 return; 353 p += x; 354 } 355 } else { 356 fs_off = hdr.eh.e_phoff; 357 for (j = i = 0; i < hdr.eh.e_phnum && j < 2; i++) { 358 if (xfsread(ino, ep + j, sizeof(ep[0]))) 359 return; 360 if (ep[j].p_type == PT_LOAD) 361 j++; 362 } 363 for (i = 0; i < 2; i++) { 364 p = PTOV(ep[i].p_paddr & 0xffffff); 365 fs_off = ep[i].p_offset; 366 if (xfsread(ino, p, ep[i].p_filesz)) 367 return; 368 } 369 p += roundup2(ep[1].p_memsz, PAGE_SIZE); 370 bootinfo.bi_symtab = VTOP(p); 371 if (hdr.eh.e_shnum == hdr.eh.e_shstrndx + 3) { 372 fs_off = hdr.eh.e_shoff + sizeof(es[0]) * 373 (hdr.eh.e_shstrndx + 1); 374 if (xfsread(ino, &es, sizeof(es))) 375 return; 376 for (i = 0; i < 2; i++) { 377 memcpy(p, &es[i].sh_size, sizeof(es[i].sh_size)); 378 p += sizeof(es[i].sh_size); 379 fs_off = es[i].sh_offset; 380 if (xfsread(ino, p, es[i].sh_size)) 381 return; 382 p += es[i].sh_size; 383 } 384 } 385 addr = hdr.eh.e_entry & 0xffffff; 386 } 387 bootinfo.bi_esymtab = VTOP(p); 388 bootinfo.bi_kernelname = VTOP(fname); 389 bootinfo.bi_bios_dev = dsk.drive; 390 __exec((caddr_t)addr, RB_BOOTINFO | (opts & RBX_MASK), 391 MAKEBOOTDEV(dev_maj[dsk.type], 0, dsk.slice, dsk.unit, dsk.part), 392 0, 0, 0, VTOP(&bootinfo)); 393} 394 395static int 396parse(char *arg) 397{ 398 char *p, *q; 399 int drv, c, i; 400 401 while ((c = *arg++)) { 402 if (c == ' ' || c == '\t' || c == '\n') 403 continue; 404 for (p = arg; *p && *p != '\n' && *p != ' ' && *p != '\t'; p++); 405 if (*p) 406 *p++ = 0; 407 if (c == '-') { 408 while ((c = *arg++)) { 409 for (i = 0; c != optstr[i]; i++) 410 if (i == NOPT - 1) 411 return -1; 412 opts ^= 1 << flags[i]; 413 } 414 if (opts & 1 << RBX_PROBEKBD) { 415 i = *(uint8_t *)PTOV(0x496) & 0x10; 416 printf("Keyboard: %s\n", i ? "yes" : "no"); 417 if (!i) 418 opts |= 1 << RBX_DUAL | 1 << RBX_SERIAL; 419 opts &= ~(1 << RBX_PROBEKBD); 420 } 421 ioctrl = opts & 1 << RBX_DUAL ? 0x3 : 422 opts & 1 << RBX_SERIAL ? 0x2 : 0x1; 423 if (ioctrl & 0x2) 424 sio_init(); 425 } else { 426 for (q = arg--; *q && *q != '('; q++); 427 if (*q) { 428 drv = -1; 429 if (arg[1] == ':') { 430 if (*arg < '0' || *arg > '9') 431 return -1; 432 drv = *arg - '0'; 433 arg += 2; 434 } 435 if (q - arg != 2) 436 return -1; 437 for (i = 0; arg[0] != dev_nm[i][0] || 438 arg[1] != dev_nm[i][1]; i++) 439 if (i == NDEV - 1) 440 return -1; 441 dsk.type = i; 442 arg += 3; 443 if (arg[1] != ',' || *arg < '0' || *arg > '9') 444 return -1; 445 dsk.unit = *arg - '0'; 446 arg += 2; 447 dsk.slice = WHOLE_DISK_SLICE; 448 if (arg[1] == ',') { 449 if (*arg < '0' || *arg > '0' + NDOSPART) 450 return -1; 451 if ((dsk.slice = *arg - '0')) 452 dsk.slice++; 453 arg += 2; 454 } 455 if (arg[1] != ')' || *arg < 'a' || *arg > 'p') 456 return -1; 457 dsk.part = *arg - 'a'; 458 arg += 2; 459 if (drv == -1) 460 drv = dsk.unit; 461 dsk.drive = (dsk.type == TYPE_WD || 462 dsk.type == TYPE_AD || 463 dsk.type == TYPE_DA ? DRV_HARD : 0) + drv; 464 dsk.meta = 0; 465 fsread(0, NULL, 0); 466 } 467 if ((i = p - arg - !*(p - 1))) { 468 if (i >= sizeof(kname)) 469 return -1; 470 memcpy(kname, arg, i + 1); 471 } 472 } 473 arg = p; 474 } 475 return 0; 476} 477 478static ino_t 479lookup(const char *path) 480{ 481 char name[MAXNAMLEN + 1]; 482 const char *s; 483 ino_t ino; 484 ssize_t n; 485 int dt; 486 487 ino = ROOTINO; 488 dt = DT_DIR; 489 for (;;) { 490 if (*path == '/') 491 path++; 492 if (!*path) 493 break; 494 for (s = path; *s && *s != '/'; s++); 495 if ((n = s - path) > MAXNAMLEN) 496 return 0; 497 ls = *path == '?' && n == 1 && !*s; 498 memcpy(name, path, n); 499 name[n] = 0; 500 if ((dt = fsfind(name, &ino)) <= 0) 501 break; 502 path = s; 503 } 504 return dt == DT_REG ? ino : 0; 505} 506static int 507xfsread(ino_t inode, void *buf, size_t nbyte) 508{ 509 if (fsread(inode, buf, nbyte) != nbyte) { 510 printf("Invalid %s\n", "format"); 511 return -1; 512 } 513 return 0; 514} 515 516static ssize_t 517fsread(ino_t inode, void *buf, size_t nbyte) 518{ 519 static struct fs fs; 520 static struct dinode din; 521 static char *blkbuf; 522 static ufs_daddr_t *indbuf; 523 static ino_t inomap; 524 static ufs_daddr_t blkmap, indmap; 525 static unsigned fsblks; 526 char *s; 527 ufs_daddr_t lbn, addr; 528 size_t n, nb, off; 529 530 if (!dsk.meta) { 531 if (!blkbuf) 532 blkbuf = malloc(BSIZEMAX); 533 inomap = 0; 534 if (dskread(blkbuf, SBOFF / DEV_BSIZE, SBSIZE / DEV_BSIZE)) 535 return -1; 536 memcpy(&fs, blkbuf, sizeof(fs)); 537 if (fs.fs_magic != FS_MAGIC) { 538 printf("Not ufs\n"); 539 return -1; 540 } 541 fsblks = fs.fs_bsize >> DEV_BSHIFT; 542 dsk.meta++; 543 } 544 if (!inode) 545 return 0; 546 if (inomap != inode) { 547 if (dskread(blkbuf, fsbtodb(&fs, ino_to_fsba(&fs, inode)), 548 fsblks)) 549 return -1; 550 din = ((struct dinode *)blkbuf)[inode % INOPB(&fs)]; 551 inomap = inode; 552 fs_off = 0; 553 blkmap = indmap = 0; 554 } 555 s = buf; 556 if (nbyte > (n = din.di_size - fs_off)) 557 nbyte = n; 558 nb = nbyte; 559 while (nb) { 560 lbn = lblkno(&fs, fs_off); 561 if (lbn < NDADDR) 562 addr = din.di_db[lbn]; 563 else { 564 if (indmap != din.di_ib[0]) { 565 if (!indbuf) 566 indbuf = malloc(BSIZEMAX); 567 if (dskread(indbuf, fsbtodb(&fs, din.di_ib[0]), 568 fsblks)) 569 return -1; 570 indmap = din.di_ib[0]; 571 } 572 addr = indbuf[(lbn - NDADDR) % NINDIR(&fs)]; 573 } 574 n = dblksize(&fs, &din, lbn); 575 if (blkmap != addr) { 576 if (dskread(blkbuf, fsbtodb(&fs, addr), n >> DEV_BSHIFT)) 577 return -1; 578 blkmap = addr; 579 } 580 off = blkoff(&fs, fs_off); 581 n -= off; 582 if (n > nb) 583 n = nb; 584 memcpy(s, blkbuf + off, n); 585 s += n; 586 fs_off += n; 587 nb -= n; 588 } 589 return nbyte; 590} 591 592static int 593dskread(void *buf, unsigned lba, unsigned nblk) 594{ 595 static char *sec; 596 struct dos_partition *dp; 597 struct disklabel *d; 598 unsigned sl, i; 599 600 if (!dsk.meta) { 601 if (!sec) 602 sec = malloc(DEV_BSIZE); 603 dsk.start = 0; 604 if (drvread(sec, DOSBBSECTOR, 1)) 605 return -1; 606 dp = (void *)(sec + DOSPARTOFF); 607 sl = dsk.slice; 608 if (sl < BASE_SLICE) { 609 for (i = 0; i < NDOSPART; i++) 610 if (dp[i].dp_typ == DOSPTYP_386BSD && 611 (dp[i].dp_flag & 0x80 || sl < BASE_SLICE)) { 612 sl = BASE_SLICE + i; 613 if (dp[i].dp_flag & 0x80 || 614 dsk.slice == COMPATIBILITY_SLICE) 615 break; 616 } 617 if (dsk.slice == WHOLE_DISK_SLICE) 618 dsk.slice = sl; 619 } 620 if (sl != WHOLE_DISK_SLICE) { 621 if (sl != COMPATIBILITY_SLICE) 622 dp += sl - BASE_SLICE; 623 if (dp->dp_typ != DOSPTYP_386BSD) { 624 printf("Invalid %s\n", "slice"); 625 return -1; 626 } 627 dsk.start = dp->dp_start; 628 } 629 if (drvread(sec, dsk.start + LABELSECTOR, 1)) 630 return -1; 631 d = (void *)(sec + LABELOFFSET); 632 if (d->d_magic != DISKMAGIC || d->d_magic2 != DISKMAGIC) { 633 if (dsk.part != RAW_PART) { 634 printf("Invalid %s\n", "label"); 635 return -1; 636 } 637 } else { 638 if (!dsk.init) { 639 if (d->d_type == DTYPE_SCSI) 640 dsk.type = TYPE_DA; 641 dsk.init++; 642 } 643 if (dsk.part >= d->d_npartitions || 644 !d->d_partitions[dsk.part].p_size) { 645 printf("Invalid %s\n", "partition"); 646 return -1; 647 } 648 dsk.start = d->d_partitions[dsk.part].p_offset; 649 } 650 } 651 return drvread(buf, dsk.start + lba, nblk); 652} 653 654static int 655printf(const char *fmt,...) 656{ 657 static const char digits[16] = "0123456789abcdef"; 658 va_list ap; 659 char buf[10]; 660 char *s; 661 unsigned r, u; 662 int c; 663 664 va_start(ap, fmt); 665 while ((c = *fmt++)) { 666 if (c == '%') { 667 c = *fmt++; 668 switch (c) { 669 case 'c': 670 putchar(va_arg(ap, int)); 671 continue; 672 case 's': 673 for (s = va_arg(ap, char *); *s; s++) 674 putchar(*s); 675 continue; 676 case 'u': 677 case 'x': 678 r = c == 'u' ? 10U : 16U; 679 u = va_arg(ap, unsigned); 680 s = buf; 681 do 682 *s++ = digits[u % r]; 683 while (u /= r); 684 while (--s >= buf) 685 putchar(*s); 686 continue; 687 } 688 } 689 putchar(c); 690 } 691 va_end(ap); 692 return 0; 693} 694 695static int 696putchar(int c) 697{ 698 if (c == '\n') 699 xputc('\r'); 700 return xputc(c); 701} 702 703static void * 704memcpy(void *dst, const void *src, size_t size) 705{ 706 const char *s; 707 char *d; 708 709 for (d = dst, s = src; size; size--) 710 *d++ = *s++; 711 return dst; 712} 713 714static void * 715malloc(size_t size) 716{ 717 static uint32_t next; 718 void *p; 719 720 if (!next) 721 next = roundup2(__base + _end, 0x10000) - __base; 722 p = (void *)next; 723 next += size; 724 return p; 725} 726 727static uint32_t 728memsize(int type) 729{ 730 v86.addr = type; 731 v86.eax = 0x8800; 732 v86int(); 733 return v86.eax; 734} 735 736static int 737drvread(void *buf, unsigned lba, unsigned nblk) 738{ 739 static unsigned c = 0x2d5c7c2f; 740 741 printf("%c\b", c = c << 8 | c >> 24); 742 v86.ctl = V86_ADDR | V86_CALLF | V86_FLAGS; 743 v86.addr = XREADORG; /* call to xread in boot1 */ 744 v86.es = VTOPSEG(buf); 745 v86.eax = lba; 746 v86.ebx = VTOPOFF(buf); 747 v86.ecx = lba >> 16; 748 v86.edx = nblk << 8 | dsk.drive; 749 v86int(); 750 v86.ctl = V86_FLAGS; 751 if (V86_CY(v86.efl)) { 752 printf("Disk error 0x%x (lba=0x%x)\n", v86.eax >> 8 & 0xff, 753 lba); 754 return -1; 755 } 756 return 0; 757} 758 759static int 760keyhit(unsigned ticks) 761{ 762 uint32_t t0, t1; 763 764 t0 = 0; 765 for (;;) { 766 if (xgetc(1)) 767 return 1; 768 t1 = *(uint32_t *)PTOV(0x46c); 769 if (!t0) 770 t0 = t1; 771 if (t1 < t0 || t1 >= t0 + ticks) 772 return 0; 773 } 774} 775 776static int 777xputc(int c) 778{ 779 if (ioctrl & 0x1) 780 putc(c); 781 if (ioctrl & 0x2) 782 sio_putc(c); 783 return c; 784} 785 786static int 787xgetc(int fn) 788{ 789 for (;;) { 790 if (ioctrl & 0x1 && getc(1)) 791 return fn ? 1 : getc(0); 792 if (ioctrl & 0x2 && sio_ischar()) 793 return fn ? 1 : sio_getc(); 794 if (fn) 795 return 0; 796 } 797} 798 799static int 800getc(int fn) 801{ 802 v86.addr = 0x16; 803 v86.eax = fn << 8; 804 v86int(); 805 return fn == 0 ? v86.eax & 0xff : !V86_ZR(v86.efl); 806} 807