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