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