]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - usr.bin/doscmd/doscmd.c
This commit was generated by cvs2svn to compensate for changes in r80060,
[FreeBSD/FreeBSD.git] / usr.bin / doscmd / doscmd.c
1 /*
2  * Copyright (c) 1992, 1993, 1996
3  *      Berkeley Software Design, Inc.  All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. All advertising materials mentioning features or use of this software
14  *    must display the following acknowledgement:
15  *      This product includes software developed by Berkeley Software
16  *      Design, Inc.
17  *
18  * THIS SOFTWARE IS PROVIDED BY Berkeley Software Design, Inc. ``AS IS'' AND
19  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21  * ARE DISCLAIMED.  IN NO EVENT SHALL Berkeley Software Design, Inc. BE LIABLE
22  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28  * SUCH DAMAGE.
29  *
30  *      BSDI doscmd.c,v 2.3 1996/04/08 19:32:30 bostic Exp
31  *
32  * $FreeBSD$
33  */
34
35 #include <sys/types.h>
36 #include <sys/param.h>
37 #include <sys/mman.h>
38 #include <sys/time.h>
39
40 #include <errno.h>
41 #include <limits.h>
42 #include <paths.h>
43 #include <pwd.h>
44 #include <signal.h>
45 #include <unistd.h>
46
47 #include <machine/param.h>
48 #include <machine/vmparam.h>
49
50 #include <sys/proc.h>
51 #include <machine/sysarch.h>
52 #include <machine/vm86.h>
53
54 #include "doscmd.h"
55
56 /* exports */
57 int             capture_fd = -1;
58 int             dead = 0;
59 int             xmode = 0;
60 int             booting = 0;
61 int             raw_kbd = 0;
62 int             timer_disable = 0;
63 struct timeval  boot_time;
64 unsigned long *ivec = (unsigned long *)0;
65
66 u_long  pending[256];                   /* pending interrupts */
67 int     n_pending;
68
69 #ifndef USE_VM86
70 #define PRB_V86_FORMAT  0x4242
71
72 struct vconnect_area vconnect_area = {
73         0,                              /* Interrupt state */
74         PRB_V86_FORMAT,                 /* Magic number */
75         { 0, },                         /* Pass through ints */
76         { 0x00000000, 0x00000000 }      /* Magic iret location */
77 };
78 #endif
79
80 /* local prototypes */
81 static void     setup_boot(regcontext_t *REGS);
82 static int      try_boot(int);
83 static void     setup_command(int argc, char *argv[], regcontext_t *REGS);
84 static FILE     *find_doscmdrc(void);
85 static int      do_args(int argc, char *argv[]);
86 static void     usage(void);
87 static int      open_name(char *name, char *ext);
88 static void     init_iomap(void);
89
90 /* Local option flags &c. */
91 static int      zflag = 0;
92
93 /* DOS environment emulation */
94 static int      ecnt = 0;
95 static char     *envs[256];
96
97 /* Search path and command name */
98 static char     *dos_path = 0;
99 char            cmdname[256];   /* referenced from dos.c */
100
101 static struct vm86_init_args kargs;
102
103 /* lobotomise */
104 int
105 main(int argc, char **argv)
106 {
107 #ifndef USE_VM86
108     ucontext_t uc;
109 #else
110     struct vm86_struct vm86s;
111 #define sc      vm86s.substr.regs.vmsc
112 #endif
113     regcontext_t *REGS = (regcontext_t *)&uc.uc_mcontext;
114     int fd;
115     int i;    
116     char buffer[4096];
117     FILE *fp;
118
119
120     /* XXX should only be for tty mode */
121     fd = open (_PATH_DEVNULL, O_RDWR);
122     if (fd != 3)
123         dup2 (fd, 3); /* stdaux */
124     if (fd != 4)
125         dup2 (fd, 4); /* stdprt */
126     if (fd != 3 && fd != 4)
127         close (fd);
128     fd = -1;
129
130     debug_set(0);               /* debug any D_TRAPS without intnum */
131
132     /* perform option argument processing */
133     do_args(argc, argv);
134     argc -= optind;
135     argv += optind;
136
137     if (getenv("DISPLAY") != NULL)
138         xmode = 1;
139
140     if (vflag && debugf == stderr) {
141         debugf = stdout;
142         setbuf (stdout, NULL);
143     }
144
145     initHMA();
146
147     /* This needs to happen before the executable is loaded */
148     mem_init();
149
150 #ifdef USE_VM86 
151     memset(&vm86s, 0, sizeof(vm86s));
152 #endif
153
154     /*
155      * With no other arguments we will assume we must boot DOS
156      */
157     if (argc <= 0)
158         booting = 1;
159
160 #if 1
161     /*
162      * Nominate interrupts to handle here when the kernel is 
163      * performing interrupt handling.
164      *
165      * I would like to let INT 2F pass through as well, but I
166      * need to get my hands on INT 2F:11 to do file redirection.
167      */
168     for (i = 0; i <= 0xff; ++i) {
169         switch (i) {
170         case 0x2f:
171         case 0xff:
172 #if 1
173             kargs.int_map[i >> 3] |= (1 << (i & 7));
174 #ifndef USE_VM86
175             vconnect_area.passthru[i >> 5] &= ~(1 << (i & 0x1f));
176 #else
177             vm86s.int_byuser[i >> 3] |= (1 << (i & 0x07));
178 #endif
179 #endif
180             break;
181         default:
182 #if 1
183             kargs.int_map[i >> 3] &= ~(1 << (i & 7));
184 #ifndef USE_VM86
185             vconnect_area.passthru[i >> 5] |= (1 << (i & 0x1f));
186 #else
187             vm86s.int_byuser[i >> 3] |= (1 << (i & 0x07));
188 #endif
189 #endif
190             break;
191         }
192     }
193 #endif
194     for (i = 0; i < 256; i++)
195         pending[i] = 0;
196     n_pending = 0;
197
198     if (booting) {                      /* are we booting? */
199         setup_boot(REGS);
200     } else {                            /* no, load a command */
201         setup_command(argc, argv, REGS);
202     }
203
204     /* install signal handlers */
205     setsignal (SIGFPE, sigfpe);         /* */
206     setsignal (SIGALRM, sigalrm);       /* */
207     setsignal (SIGILL, sigill);         /* */
208     setsignal (SIGTRAP, sigtrap);       /* */
209     setsignal (SIGUSR2, sigtrace);      /* */
210     setsignal (SIGINFO, sigtrace);      /* */
211 #ifdef USE_VM86
212     setsignal (SIGURG, sigurg);         /* entry from NetBSD vm86 */
213 #else
214     setsignal (SIGBUS, sigbus);         /* entry from FreeBSD, BSD/OS vm86 */
215 #endif
216         
217     /* Call init functions */
218     if (raw_kbd)
219         console_init();
220     init_io_port_handlers();
221     bios_init();
222     cpu_init();
223     video_init();
224     if (xmode)
225         mouse_init();
226     video_bios_init();
227     disk_bios_init();
228     cmos_init();
229     xms_init();
230     dos_init();
231     net_init();
232     speaker_init();
233     timer_init();
234     /* iomap_init(); */
235
236     gettimeofday(&boot_time, 0);
237
238     if (zflag) for (;;) pause();        /* spin if requested */
239
240     if (raw_kbd) {
241         /*
242          * If we have a raw keyboard, and hence, video,
243          * sneak in a call to the video BIOS to reinit the
244          * the video display.
245          */
246         u_long video_vector;
247         static u_char video_trampoline[] = {
248             0x60,                       /* pusha */
249             0xB8, 0x03, 0x00,           /* mov ax,00003h */
250             0xCD, 0x10,                 /* int 010h */
251             0x61,                       /* popa */
252             0xCF,                       /* iret */
253         };
254
255         video_vector = insert_generic_trampoline(
256             sizeof(video_trampoline), video_trampoline);
257         
258         PUSH(R_FLAGS, REGS);
259         PUSH(R_CS, REGS);
260         PUSH(R_IP, REGS);
261         PUTVEC(R_CS, R_IP, video_vector);
262     }
263
264     sigemptyset(&uc.uc_sigmask);
265     sigaltstack(NULL, &uc.uc_stack);
266     uc.uc_mcontext.mc_onstack = 0;
267
268     if (tmode)
269         tracetrap(REGS);
270
271 #ifndef USE_VM86
272     R_EAX = (booting || raw_kbd) ? (int)&vconnect_area : -1;
273     R_EFLAGS |= PSL_VM | PSL_VIF;                       /* request VM86 mode */
274
275     i386_vm86(VM86_INIT, &kargs);
276
277     sigreturn(&uc);
278     debug(D_ALWAYS,"sigreturn failed : %s\n", strerror(errno));
279 #else
280     vm86s.cpu_type = VCPU_586;
281     i386_vm86(&vm86s);
282 #endif
283
284     /* shouldn't get here */
285     if (vflag) dump_regs(REGS);
286     fatal ("vm86 returned (no kernel support?)\n");
287 #undef  sc
288 }
289
290 /*
291 ** setup_boot
292 **
293 ** Setup to boot DOS
294 */
295 static void
296 setup_boot(regcontext_t *REGS)
297 {
298     FILE        *fp;            /* doscmdrc handle */
299     int         fd;             /* don't close this! */
300
301     fp = find_doscmdrc();       /* get doscmdrc */
302     if (!fp) {
303         fprintf(stderr, "You must have a doscmdrc to boot\n");
304         quit(1);
305     }
306
307     booting = read_config(fp);                  /* where to boot from? */
308     fclose(fp);
309     if (booting < 0) {                          /* not specified */
310         if ((fd = try_boot(booting = 0)) < 0)   /* try A: */
311             fd = try_boot(booting = 2);         /* try C: */
312     } else {
313         fd = try_boot(booting); /* do like the man says */
314     }
315
316     if (fd < 0)
317         errx(1, "Failed to boot");
318     
319     /* initialise registers for entry to bootblock */
320     R_EFLAGS = 0x20202;
321     R_CS = 0x0000;
322     R_IP = 0x7c00;
323     R_SS = 0x9800;
324     R_SP = 0x8000 - 2;
325     R_DS = 0x0000;
326     R_ES = 0x0000;
327
328     R_AX = R_BX = R_CX = R_DX = R_SI = R_DI = R_BP = 0;
329
330 #if defined(__FreeBSD__) || defined(__NetBSD__)
331     /*
332     ** init a few other context registers 
333     */
334     R_FS = 0x0000;
335     R_GS = 0x0000;
336 #endif  
337 }
338
339 /*
340 ** try_boot
341 **
342 ** try to read the boot sector from the specified disk
343 */
344 static int
345 try_boot(int booting)
346 {
347     int fd;
348
349     fd = disk_fd(booting);
350     if (fd < 0) {                       /* can we boot it? */
351         debug(D_DISK, "Cannot boot from %c\n", drntol(booting));
352         return -1;
353     }
354     
355     /* read bootblock */
356     if (read(fd, (char *)0x7c00, 512) != 512) {
357         debug(D_DISK, "Short read on boot block from %c:\n", drntol(booting));
358         return -1;
359     }
360     
361     return fd;
362 }
363
364 /*
365 ** setup_command
366 **
367 ** Setup to run a single command and emulate DOS
368 */
369 static void
370 setup_command(int argc, char *argv[], regcontext_t *REGS)
371 {
372     FILE        *fp;
373     u_short     param[7] = {0, 0, 0, 0, 0, 0, 0};
374     char        *p;
375     char        prog[1024];
376     char        buffer[PATH_MAX];
377     int         i;
378     int         fd;
379     
380     fp = find_doscmdrc();               /* dig up a doscmdrc */
381     if (fp) {
382         read_config(fp);                /* load config for non-boot mode */
383         fclose(fp);
384     }
385     
386     if (argc <= 0)                      /* need some arguments */
387         usage();
388
389     /* look for a working directory  XXX ??? */
390     if (dos_getcwd(drlton('C')) == NULL) {
391         
392         /* try to get our current directory, use '/' if desperate */
393         p = getcwd(buffer, sizeof(buffer));
394         if (!p || !*p) p = getenv("PWD");
395         if (!p || !*p) p = "/";
396         init_path(drlton('C'), (u_char *)"/", (u_char *)p);
397
398         /* look for PATH= already set, learn from it if possible */
399         for (i = 0; i < ecnt; ++i) {
400             if (!strncmp(envs[i], "PATH=", 5)) {
401                 dos_path = envs[i] + 5;
402                 break;
403             }
404         }
405         /* no PATH in DOS environment? put current directory there*/
406         if (i >= ecnt) {
407             static char path[256];
408             snprintf(path, sizeof(path), "PATH=C:%s", dos_getcwd(drlton('C')));
409             put_dosenv(path);
410             dos_path = envs[ecnt-1] + 5;
411         }
412     }
413
414     /* add a COMSPEC if required */
415     for (i = 0; i < ecnt; ++i) {
416         if (!strncmp(envs[i], "COMSPEC=", 8))
417             break;
418     }
419     if (i >= ecnt)
420         put_dosenv("COMSPEC=C:\\COMMAND.COM");
421
422     /* look for PATH already set, learn from it if possible */
423     for (i = 0; i < ecnt; ++i) {
424         if (!strncmp(envs[i], "PATH=", 5)) {
425             dos_path = envs[i] + 5;
426             break;
427         }
428     }
429     /* No PATH, default to c:\ */
430     if (i >= ecnt) {
431         put_dosenv("PATH=C:\\");
432         dos_path = envs[ecnt-1] + 5;
433     }
434
435     /* if no PROMPT, default to 'DOS>' */
436     for (i = 0; i < ecnt; ++i) {
437         if (!strncmp(envs[i], "PROMPT=", 7))
438             break;
439     }
440     if (i >= ecnt)
441         put_dosenv("PROMPT=DOS> ");
442
443     /* terminate environment */
444     envs[ecnt] = 0;
445
446     /* XXX ??? */
447     if (dos_getcwd(drlton('R')) == NULL)
448         init_path(drlton('R'), (u_char *)"/", 0);
449
450     /* get program name */
451     strncpy(prog, *argv++, sizeof(prog) -1);
452     prog[sizeof(prog) -1] = '\0';
453
454     /* try to open program */
455     if ((fd = open_prog(prog)) < 0) {
456         fprintf (stderr, "%s: command not found\n", prog);
457         quit(1);
458     }
459     
460     /* load program */
461     load_command(REGS, 1, fd, cmdname, param, argv, envs);
462     close(fd);
463 }
464
465 /*
466 ** find_doscmdrc
467 **
468 ** Try to find a doscmdrc file
469 */
470 static FILE *
471 find_doscmdrc(void)
472 {
473     FILE        *fp;
474     char        buffer[4096];
475     int         fd;
476     
477     if ((fp = fopen(".doscmdrc", "r")) == NULL) {
478         struct passwd *pwd = getpwuid(geteuid());
479         if (pwd) {
480             snprintf(buffer, sizeof(buffer), "%s/.doscmdrc", pwd->pw_dir);
481             fp = fopen(buffer, "r");
482         }
483         if (!fp) {
484             char *home = getenv("HOME");
485             if (home) {
486                 snprintf(buffer, sizeof(buffer), "%s/.doscmdrc", home);
487                 fp = fopen(buffer, "r");
488             }
489         }
490         if (!fp)
491             fp = fopen("/etc/doscmdrc", "r");
492     }
493     return(fp);
494 }
495
496 /*
497 ** do_args
498 **
499 ** commandline argument processing
500 */
501 static int
502 do_args(int argc, char *argv[])
503 {
504     int         i,c,p;
505     FILE        *fp;
506     char        *col;
507
508     while ((c = getopt (argc, argv, "234Oc:TkCIEMPRLAU:S:HDtzvVxXYfbri:o:p:d:")) != -1) {
509         switch (c) {
510         case 'd':
511             if (fp = fopen(optarg, "w")) {
512                 debugf = fp;
513                 setbuf (fp, NULL);
514             } else
515                 perror(optarg);
516             break;
517         case '2':
518             debug_flags |= D_TRAPS2;
519             break;
520         case '3':
521             debug_flags |= D_TRAPS3;
522             break;
523         case '4':
524             debug_flags |= D_DEBUGIN;
525             break;
526         case 'O':
527             debugf = stdout;
528             setbuf (stdout, NULL);
529             break;
530         case 'c':
531             if ((capture_fd = creat(optarg, 0666)) < 0) {
532                 perror(optarg);
533                 quit(1);
534             }
535             break;
536         case 'i':
537             i = 1;
538             if (col = strchr(optarg, ':')) {
539                 *col++ = 0;
540                 i = strtol(col, 0, 0);
541             }
542             p = strtol(optarg, 0, 0);
543             iomap_port(p, i);
544
545             while (i-- > 0)
546                 define_input_port_handler(p++, inb_traceport);
547             break;
548         case 'o':
549             i = 1;
550             if (col = strchr(optarg, ':')) {
551                 *col++ = 0;
552                 i = strtol(col, 0, 0);
553             }
554             p = strtol(optarg, 0, 0);
555             iomap_port(p, i);
556
557             while (i-- > 0)
558                 define_output_port_handler(p++, outb_traceport);
559             break;
560         case 'p':
561             i = 1;
562             if (col = strchr(optarg, ':')) {
563                 *col++ = 0;
564                 i = strtol(col, 0, 0);
565             }
566             p = strtol(optarg, 0, 0);
567             iomap_port(p, i);
568
569             while (i-- > 0) {
570                 define_input_port_handler(p++, inb_port);
571                 define_output_port_handler(p++, outb_port);
572             }
573             break;
574
575         case 'r':
576             raw_kbd = 1;
577             break;
578         case 'I':
579             debug_flags |= D_ITRAPS;
580             for (c = 0; c < 256; ++c)
581                 debug_set(c);
582             break;
583         case 'k':
584             kargs.debug = 1;
585             break;
586         case 'T':
587             timer_disable = 1;
588             break;
589         case 'E':
590             debug_flags |= D_EXEC;
591             break;
592         case 'C':
593             debug_flags |= D_DOSCALL;
594             break;
595         case 'M':
596             debug_flags |= D_MEMORY;
597             break;
598         case 'P':
599             debug_flags |= D_PORT;
600             break;
601         case 'R':
602             debug_flags |= D_REDIR;
603             break;
604         case 'X':
605             debug_flags |= D_XMS;
606             break;
607         case 'Y':
608             debug_flags |= D_EMS;
609             break;
610         case 'L':
611             debug_flags |= D_PRINTER;
612             break;
613         case 'A':
614             debug_flags |= D_TRAPS|D_ITRAPS;
615             for (c = 0; c < 256; ++c)
616                 debug_set(c);
617             break;
618         case 'U':
619             debug_unset(strtol(optarg, 0, 0));
620             break;
621         case 'S':
622             debug_flags |= D_TRAPS|D_ITRAPS;
623             debug_set(strtol(optarg, 0, 0));
624             break;
625         case 'H':
626             debug_flags |= D_HALF;
627             break;
628         case 'x':
629 #ifdef NO_X
630             fatal("X11 support not compiled in.\n");
631 #endif
632             xmode = 1;
633             break;
634         case 't':
635             tmode = 1;
636             break;
637         case 'z':
638             zflag = 1;
639             break;
640         case 'D':
641             debug_flags |= D_DISK | D_FILE_OPS;
642             break;
643         case 'v':
644             debug_flags |= D_TRAPS | D_ITRAPS | D_HALF | 0xff;
645             break;
646         case 'V':
647             vflag = 1;
648             break;
649         case 'b':
650             booting = 1;
651             break;
652         default:
653             usage ();
654         }
655     }
656     return(optind);
657 }
658
659 /*
660 ** Very helpful 8(
661 */
662 void
663 usage (void)
664 {
665         fprintf (stderr, "usage: doscmd cmd args...\n");
666         quit (1);
667 }
668
669 /*
670 ** look up a DOS command name
671 **
672 ** XXX ordering is wrong!
673 */
674 static int
675 open_name(char *name, char *ext)
676 {
677     int fd;
678     char *p = name + strlen(name);
679     char *q;
680
681     *ext = 0;
682
683     q = strrchr(name, '/');
684     if (q)
685         q++;
686     else
687         q = name;
688
689     if (!strchr(q, '.')) {
690         strcpy(ext, ".exe");
691         strcpy(p, ".exe");
692
693         if ((fd = open (name, O_RDONLY)) >= 0)
694             return (fd);
695
696         strcpy(ext, ".com");
697         strcpy(p, ".com");
698
699         if ((fd = open (name, O_RDONLY)) >= 0)
700             return (fd);
701     } else {
702         if ((fd = open (name, O_RDONLY)) >= 0)
703             return (fd);
704     }
705
706     return (-1);
707 }
708
709 /*
710 ** look up a DOS command, search the path as well.
711 */
712 int
713 open_prog(char *name)
714 {
715     int fd;
716     char fullname[1024], tmppath[1024];
717     char *p;
718     char *e;
719     char ext[5];
720     int error;
721     int drive;
722     char *path;
723
724     if (strpbrk(name, ":/\\")) {
725         error = translate_filename(name, fullname, &drive);
726         if (error)
727             return (-1);
728
729         fd = open_name(fullname, ext);
730
731         strcpy(cmdname, name);
732         if (*ext)
733             strcat(cmdname, ext);
734         return (fd);
735     }
736
737     path = dos_path;
738
739     while (*path) {
740         p = path;
741         while (*p && *p != ';')
742             ++p;
743
744         memcpy(tmppath, path, p - path);
745         e = tmppath + (p - path);
746         *e++ = '\\';
747         strcpy(e, name);
748
749         path = *p ? p + 1 : p;
750
751         error = translate_filename(tmppath, fullname, &drive);
752         if (error)
753             continue;
754
755         fd = open_name(fullname, ext);
756
757         if (fd >= 0) {
758             strcpy(cmdname, tmppath);
759             if (*ext)
760                 strcat(cmdname, ext);
761             return (fd);
762         }
763     }
764
765     return (-1);
766 }
767
768 /*
769 ** append a value to the DOS environment
770 */
771 void
772 put_dosenv(char *value)
773 {
774     if (ecnt < sizeof(envs)/sizeof(envs[0])) {
775         if ((envs[ecnt++] = strdup(value)) == NULL) {
776             perror("put_dosenv");
777             quit(1);
778         }
779     } else {
780         fprintf(stderr, "Environment full, ignoring %s\n", value);
781     }
782 }
783
784 /*
785 ** replicate a fd up at the top of the range
786 */
787 int
788 squirrel_fd(int fd)
789 {
790     int sfd = sysconf(_SC_OPEN_MAX);
791     struct stat sb;
792
793     do {
794         errno = 0;
795         fstat(--sfd, &sb);
796     } while (sfd > 0 && errno != EBADF);
797
798     if (errno == EBADF && dup2(fd, sfd) >= 0) {
799         close(fd);
800         return(sfd);
801     }
802     return(fd);
803 }
804
805 /*
806 ** Exit-time stuff
807 */
808
809 /*
810 ** Going away time
811 **
812 ** XXX belongs somewhere else perhaps
813 */
814 void
815 done (regcontext_t *REGS, int val)
816 {
817     if (curpsp < 2) {
818         if (xmode) {
819             char *m;
820
821             tty_move(24, 0);
822             for (m = "END OF PROGRAM"; *m; ++m)
823                 tty_write(*m, 0x8400);
824
825             for (m = "(PRESS <CTRL-ALT> ANY MOUSE BUTTON TO exit)"; *m; ++m)
826                 tty_write(*m, 0x0900);
827             tty_move(-1, -1);
828             for (;;)
829                 tty_pause();
830         } else {
831             quit(val);
832         }
833     }
834     exec_return(REGS, val);
835 }
836
837 typedef struct COQ {
838     void        (*func)();
839     void        *arg;
840     struct COQ  *next;
841 } COQ;
842
843 COQ *coq = 0;
844
845 void
846 quit(int status)
847 {
848     while (coq) {
849         COQ *c = coq;
850         coq = coq->next;
851         c->func(c->arg);
852     }
853     if (!xmode)         /* XXX not for bootmode */
854         puts("\n");
855     exit(status);
856 }
857
858 void
859 call_on_quit(void (*func)(void *), void *arg)
860 {
861     COQ *c = (COQ *)malloc(sizeof(COQ));
862     if (!c) {
863         perror("call_on_quit");
864         quit(1);
865     }
866     c->func = func;
867     c->arg = arg;
868     c->next = coq;
869     coq = c;
870 }
871
872 struct io_range {
873         u_int start;
874         u_int length;
875         int enable;
876 };
877
878 /* This is commented out as it is never called.  Turn it back on if needed.
879  */
880 #if COMMENTED_OUT
881 static void
882 iomap_init(void)
883 {
884         int i;
885         struct io_range io[] = {
886 #if 0
887                 { 0x200, 0x200, 1 },            /* 0x200 - 0x400 */
888                 { 0x1c80, 2, 1 },               /* 0x1c80 - 0x1c81 */
889                 { 0x2c80, 2, 1 },               /* 0x2c80 - 0x2c81 */
890                 { 0x3c80, 2, 1 },               /* 0x3c80 - 0x3c81 */
891                 { 0x378,  8, 1 },               /* 0x378 - 0x37F */
892                 { 0x3c4,  2, 1 },               /* 0x3c4 - 0x3c5 */
893                 { 0x3c5,  2, 1 },               /* 0x3ce - 0x3cf */
894 #else
895                 { 0x0, 0x10000, 1 },            /* entire i/o space */
896 #endif
897                 { 0, 0, 0 }
898         };
899         
900         for (i = 0; io[i].length; i++)
901                 if (i386_set_ioperm(io[i].start, io[i].length, io[i].enable) < 0)
902                         err(1, "i386_set_ioperm");
903 }
904 #endif
905
906 /* This is used to map in only the specified port range, instead of all
907    the ports or only certain port ranges.
908  */
909 void
910 iomap_port(int port, int count)
911 {
912     if (i386_set_ioperm(port, count, 1) < 0)
913         err(1, "i386_set_ioperm");
914
915     debug(D_PORT,"mapped I/O port: port=%#x count=%d\n", port, count);
916 }