2 * Copyright (c) 1992, 1993, 1996
3 * Berkeley Software Design, Inc. All rights reserved.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
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
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
30 * BSDI doscmd.c,v 2.3 1996/04/08 19:32:30 bostic Exp
32 * $FreeBSD: src/usr.bin/doscmd/doscmd.c,v 1.13.2.6 2002/04/25 11:04:51 tg Exp $
35 #include <sys/types.h>
36 #include <sys/param.h>
49 #include <machine/param.h>
50 #include <machine/vmparam.h>
52 #include <machine/sysarch.h>
53 #include <machine/vm86.h>
67 int timer_disable = 0;
68 struct timeval boot_time;
69 unsigned long *ivec = NULL;
72 #define PRB_V86_FORMAT 0x4242
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 */
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);
91 /* Local option flags &c. */
94 /* DOS environment emulation */
95 static unsigned ecnt = 0;
96 static char *envs[256];
98 /* Search path and command name */
99 static char *dos_path = NULL;
100 char cmdname[256]; /* referenced from dos.c */
102 static struct vm86_init_args kargs;
106 main(int argc, char **argv)
111 struct vm86_struct vm86s;
112 #define sc vm86s.substr.regs.vmsc
114 regcontext_t *REGS = (regcontext_t *)&uc.uc_mcontext;
119 sigemptyset(&sigset);
120 sigaddset(&sigset, SIGIO);
121 sigaddset(&sigset, SIGALRM);
122 sigprocmask(SIG_BLOCK, &sigset, 0);
127 /* XXX should only be for tty mode */
128 fd = open (_PATH_DEVNULL, O_RDWR);
130 dup2 (fd, 3); /* stdaux */
132 dup2 (fd, 4); /* stdprt */
133 if (fd != 3 && fd != 4)
137 debug_set(0); /* debug any D_TRAPS without intnum */
139 /* perform option argument processing */
144 if (vflag && debugf == stderr) {
146 setbuf (stdout, NULL);
151 /* This needs to happen before the executable is loaded */
155 memset(&vm86s, 0, sizeof(vm86s));
159 * With no other arguments we will assume we must boot DOS
166 * Nominate interrupts to handle here when the kernel is
167 * performing interrupt handling.
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.
172 for (i = 0; i <= 0xff; ++i) {
177 kargs.int_map[i >> 3] |= (1 << (i & 7));
179 vconnect_area.passthru[i >> 5] &= ~(1 << (i & 0x1f));
181 vm86s.int_byuser[i >> 3] |= (1 << (i & 0x07));
187 kargs.int_map[i >> 3] &= ~(1 << (i & 7));
189 vconnect_area.passthru[i >> 5] |= (1 << (i & 0x1f));
191 vm86s.int_byuser[i >> 3] |= (1 << (i & 0x07));
199 if (booting) { /* are we booting? */
201 } else { /* no, load a command */
202 setup_command(argc, argv, REGS);
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); /* */
213 setsignal(SIGURG, sigurg); /* entry from NetBSD vm86 */
215 setsignal(SIGBUS, sigbus); /* entry from FreeBSD, BSD/OS vm86 */
218 /* Call init functions */
221 init_io_port_handlers();
239 gettimeofday(&boot_time, 0);
241 if (zflag) for (;;) pause(); /* spin if requested */
245 * If we have a raw keyboard, and hence, video,
246 * sneak in a call to the video BIOS to reinit the
250 static u_char video_trampoline[] = {
252 0xB8, 0x03, 0x00, /* mov ax,00003h */
253 0xCD, 0x10, /* int 010h */
258 video_vector = insert_generic_trampoline(
259 sizeof(video_trampoline), video_trampoline);
264 PUTVEC(R_CS, R_IP, video_vector);
267 sigemptyset(&uc.uc_sigmask);
268 sigaltstack(NULL, &uc.uc_stack);
269 uc.uc_mcontext.mc_onstack = 0;
275 R_EAX = (booting || raw_kbd) ? (int)&vconnect_area : -1;
276 R_EFLAGS |= PSL_VM | PSL_VIF; /* request VM86 mode */
278 i386_vm86(VM86_INIT, &kargs);
281 debug(D_ALWAYS,"sigreturn failed : %s\n", strerror(errno));
283 vm86s.cpu_type = VCPU_586;
287 /* shouldn't get here */
288 if (vflag) dump_regs(REGS);
289 fatal ("vm86 returned (no kernel support?)\n");
301 setup_boot(regcontext_t *REGS)
303 FILE *fp; /* doscmdrc handle */
304 int fd; /* don't close this! */
306 fp = find_doscmdrc(); /* get doscmdrc */
308 fprintf(stderr, "You must have a doscmdrc to boot\n");
312 booting = read_config(fp); /* where to boot from? */
314 if (booting < 0) { /* not specified */
315 if ((fd = try_boot(booting = 0)) < 0) /* try A: */
316 fd = try_boot(booting = 2); /* try C: */
318 fd = try_boot(booting); /* do like the man says */
322 errx(1, "Failed to boot");
324 /* initialise registers for entry to bootblock */
333 R_AX = R_BX = R_CX = R_DX = R_SI = R_DI = R_BP = 0;
335 #if defined(__FreeBSD__) || defined(__NetBSD__) || defined (__DragonFly__)
337 ** init a few other context registers
347 ** try to read the boot sector from the specified disk
350 try_boot(int bootdrv)
354 fd = disk_fd(bootdrv);
355 if (fd < 0) { /* can we boot it? */
356 debug(D_DISK, "Cannot boot from %c\n", drntol(bootdrv));
361 if (read(fd, (char *)0x7c00, 512) != 512) {
362 debug(D_DISK, "Short read on boot block from %c:\n", drntol(bootdrv));
372 ** Setup to run a single command and emulate DOS
375 setup_command(int argc, char *argv[], regcontext_t *REGS)
378 u_short param[7] = {0, 0, 0, 0, 0, 0, 0};
381 char buffer[PATH_MAX];
385 fp = find_doscmdrc(); /* dig up a doscmdrc */
387 read_config(fp); /* load config for non-boot mode */
391 if (argc <= 0) /* need some arguments */
394 /* look for a working directory XXX ??? */
395 if (dos_getcwd(drlton('C')) == NULL) {
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);
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;
410 /* no PATH in DOS environment? put current directory there*/
412 static char path[256];
413 snprintf(path, sizeof(path), "PATH=C:%s", dos_getcwd(drlton('C')));
415 dos_path = envs[ecnt-1] + 5;
419 /* add a COMSPEC if required */
420 for (i = 0; i < ecnt; ++i) {
421 if (!strncmp(envs[i], "COMSPEC=", 8))
425 put_dosenv("COMSPEC=C:\\COMMAND.COM");
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;
434 /* No PATH, default to c:\ */
436 put_dosenv("PATH=C:\\");
437 dos_path = envs[ecnt-1] + 5;
440 /* if no PROMPT, default to 'DOS>' */
441 for (i = 0; i < ecnt; ++i) {
442 if (!strncmp(envs[i], "PROMPT=", 7))
446 put_dosenv("PROMPT=DOS> ");
448 /* terminate environment */
452 if (dos_getcwd(drlton('R')) == NULL)
453 init_path(drlton('R'), "/", 0);
455 /* get program name */
456 strncpy(prog, *argv++, sizeof(prog) -1);
457 prog[sizeof(prog) -1] = '\0';
459 /* try to open program */
460 if ((fd = open_prog(prog)) < 0) {
461 fprintf (stderr, "%s: command not found\n", prog);
466 load_command(REGS, 1, fd, cmdname, param, argv, envs);
473 ** Try to find a doscmdrc file
481 if ((fp = fopen(".doscmdrc", "r")) == NULL) {
482 struct passwd *pwd = getpwuid(geteuid());
484 snprintf(buffer, sizeof(buffer), "%s/.doscmdrc", pwd->pw_dir);
485 fp = fopen(buffer, "r");
488 char *home = getenv("HOME");
490 snprintf(buffer, sizeof(buffer), "%s/.doscmdrc", home);
491 fp = fopen(buffer, "r");
495 fp = fopen("/etc/doscmdrc", "r");
503 ** commandline argument processing
506 do_args(int argc, char *argv[])
512 while ((c = getopt(argc, argv, "234AbCc:Dd:EGHIi:kLMOo:Pp:RrS:TtU:vVxXYz")) != -1) {
515 debug_flags |= D_TRAPS2;
518 debug_flags |= D_TRAPS3;
521 debug_flags |= D_DEBUGIN;
524 debug_flags |= D_TRAPS | D_ITRAPS;
525 for (c = 0; c < 256; ++c)
532 debug_flags |= D_DOSCALL;
535 if ((capture_fd = creat(optarg, 0666)) < 0) {
541 debug_flags |= D_DISK | D_FILE_OPS;
544 if ((fp = fopen(optarg, "w")) != NULL) {
551 debug_flags |= D_EXEC;
554 debug_flags |= D_VIDEO;
557 debug_flags |= D_HALF;
560 debug_flags |= D_ITRAPS;
561 for (c = 0; c < 256; ++c)
566 if ((col = strchr(optarg, ':')) != NULL) {
568 i = strtol(col, 0, 0);
570 p = strtol(optarg, 0, 0);
574 define_input_port_handler(p++, inb_traceport);
580 debug_flags |= D_PRINTER;
583 debug_flags |= D_MEMORY;
587 setbuf (stdout, NULL);
591 if ((col = strchr(optarg, ':')) != NULL) {
593 i = strtol(col, 0, 0);
595 p = strtol(optarg, 0, 0);
599 define_output_port_handler(p++, outb_traceport);
602 debug_flags |= D_PORT;
606 if ((col = strchr(optarg, ':')) != NULL) {
608 i = strtol(col, 0, 0);
610 p = strtol(optarg, 0, 0);
614 define_input_port_handler(p++, inb_port);
615 define_output_port_handler(p++, outb_port);
619 debug_flags |= D_REDIR;
625 debug_flags |= D_TRAPS | D_ITRAPS;
626 debug_set(strtol(optarg, 0, 0));
635 debug_unset(strtol(optarg, 0, 0));
641 debug_flags |= D_TRAPS | D_ITRAPS | D_HALF | 0xff;
644 debug_flags |= D_XMS;
648 fatal("X11 support not compiled in.\n");
653 debug_flags |= D_EMS;
671 fprintf (stderr, "usage: doscmd cmd args...\n");
676 ** look up a DOS command name
678 ** XXX ordering is wrong!
681 open_name(char *name, char *ext)
684 char *p = name + strlen(name);
689 q = strrchr(name, '/');
695 if (!strchr(q, '.')) {
699 if ((fd = open (name, O_RDONLY)) >= 0)
705 if ((fd = open (name, O_RDONLY)) >= 0)
708 if ((fd = open (name, O_RDONLY)) >= 0)
716 ** look up a DOS command, search the path as well.
719 open_prog(char *name)
722 char fullname[1024], tmppath[1024];
730 if (strpbrk(name, ":/\\")) {
731 error = translate_filename(name, fullname, &drive);
735 fd = open_name(fullname, ext);
737 strcpy(cmdname, name);
739 strcat(cmdname, ext);
747 while (*p && *p != ';')
750 memcpy(tmppath, path, p - path);
751 e = tmppath + (p - path);
755 path = *p ? p + 1 : p;
757 error = translate_filename(tmppath, fullname, &drive);
761 fd = open_name(fullname, ext);
764 strcpy(cmdname, tmppath);
766 strcat(cmdname, ext);
775 ** append a value to the DOS environment
778 put_dosenv(const char *value)
780 if (ecnt < sizeof(envs)/sizeof(envs[0])) {
781 if ((envs[ecnt++] = strdup(value)) == NULL) {
782 perror("put_dosenv");
786 fprintf(stderr, "Environment full, ignoring %s\n", value);
791 ** replicate a fd up at the top of the range
796 int sfd = sysconf(_SC_OPEN_MAX);
802 } while (sfd > 0 && errno != EBADF);
804 if (errno == EBADF && dup2(fd, sfd) >= 0) {
818 ** XXX belongs somewhere else perhaps
821 done(regcontext_t *REGS, int val)
828 for (m = "END OF PROGRAM"; *m; ++m)
829 tty_write(*m, 0x8400);
831 for (m = "(PRESS <CTRL-ALT> ANY MOUSE BUTTON TO exit)"; *m; ++m)
832 tty_write(*m, 0x0900);
840 exec_return(REGS, val);
844 void (*func)(void *);
859 if (!xmode) /* XXX not for bootmode */
865 call_on_quit(void (*func)(void *), void *arg)
867 COQ *c = (COQ *)malloc(sizeof(COQ));
869 perror("call_on_quit");
884 /* This is commented out as it is never called. Turn it back on if needed.
891 struct io_range io[] = {
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 */
901 { 0x0, 0x10000, 1 }, /* entire i/o space */
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");
912 /* This is used to map in only the specified port range, instead of all
913 the ports or only certain port ranges.
916 iomap_port(int port, int count)
918 if (i386_set_ioperm(port, count, 1) < 0)
919 err(1, "i386_set_ioperm");
921 debug(D_PORT,"mapped I/O port: port=%#x count=%d\n", port, count);