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