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