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