]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/boot/i386/gptboot/gptboot.c
This commit was generated by cvs2svn to compensate for changes in r56173,
[FreeBSD/FreeBSD.git] / sys / boot / i386 / gptboot / gptboot.c
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$
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
74 extern uint32_t _end;
75
76 static const char optstr[NOPT] = "DhaCcdgPrsv";
77 static 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
91 static const char *const dev_nm[] = {"wd", "  ", "fd", "  ", "da"};
92
93 static 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;
103 static char cmd[512];
104 static char kname[1024];
105 static uint32_t opts;
106 static struct bootinfo bootinfo;
107 static int ls;
108 static uint32_t fs_off;
109 static uint8_t ioctrl = 0x1;
110
111 void exit(int);
112 static void load(const char *);
113 static int parse(char *);
114 static void readfile(const char *, void *, size_t);
115 static ino_t lookup(const char *);
116 static int fsfind(const char *, ino_t *);
117 static int xfsread(ino_t, void *, size_t);
118 static ssize_t fsread(ino_t, void *, size_t);
119 static int dskread(void *, unsigned, unsigned);
120 static int printf(const char *,...);
121 static void getstr(char *, int);
122 static int putchar(int);
123 static int getchar(void);
124 static void *memcpy(void *, const void *, size_t);
125 static int strcmp(const char *, const char *);
126 static void *malloc(size_t);
127 static uint32_t memsize(int);
128 static uint32_t drvinfo(int);
129 static int drvread(void *, unsigned, unsigned);
130 static int keyhit(unsigned);
131 static int xputc(int);
132 static int xgetc(int);
133 static void putc(int);
134 static int getc(int);
135
136 int
137 main(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
192 void
193 exit(int x)
194 {
195 }
196
197 static void
198 load(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
292 static int
293 parse(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
374 static void
375 readfile(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
383 static ino_t
384 lookup(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
412 static int
413 fsfind(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
437 static int
438 xfsread(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
447 static ssize_t
448 fsread(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
523 static int
524 dskread(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
585 static int
586 printf(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
626 static void
627 getstr(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
658 static int
659 putchar(int c)
660 {
661     if (c == '\n')
662         xputc('\r');
663     return xputc(c);
664 }
665
666 static int
667 getchar(void)
668 {
669     int c;
670
671     c = xgetc(0);
672     if (c == '\r')
673         c = '\n';
674     return c;
675 }
676
677 static void *
678 memcpy(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
688 static int
689 strcmp(const char *s1, const char *s2)
690 {
691     for (; *s1 == *s2 && *s1; s1++, s2++);
692     return (u_char)*s1 - (u_char)*s2;
693 }
694
695 static void *
696 malloc(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
708 static uint32_t
709 memsize(int type)
710 {
711     v86.addr = type;
712     v86.eax = 0x8800;
713     v86int();
714     return v86.eax;
715 }
716
717 static uint32_t
718 drvinfo(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
730 static int
731 drvread(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
753 static int
754 keyhit(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
770 static int
771 xputc(int c)
772 {
773     if (ioctrl & 0x1)
774         putc(c);
775     if (ioctrl & 0x2)
776         sio_putc(c);
777     return c;
778 }
779
780 static int
781 xgetc(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
793 static void
794 putc(int c)
795 {
796     v86.addr = 0x10;
797     v86.eax = 0xe00 | (c & 0xff);
798     v86.ebx = 0x7;
799     v86int();
800 }
801
802 static int
803 getc(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 }