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 $
33 * $DragonFly: src/usr.bin/doscmd/doscmd.c,v 1.4 2004/01/21 21:48:21 rob Exp $
36 #include <sys/types.h>
37 #include <sys/param.h>
50 #include <machine/param.h>
51 #include <machine/vmparam.h>
53 #include <machine/sysarch.h>
54 #include <machine/vm86.h>
68 int timer_disable
= 0;
69 struct timeval boot_time
;
70 unsigned long *ivec
= (unsigned long *)0;
73 #define PRB_V86_FORMAT 0x4242
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 */
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
);
92 /* Local option flags &c. */
95 /* DOS environment emulation */
96 static unsigned ecnt
= 0;
97 static char *envs
[256];
99 /* Search path and command name */
100 static char *dos_path
= 0;
101 char cmdname
[256]; /* referenced from dos.c */
103 static struct vm86_init_args kargs
;
107 main(int argc
, char **argv
)
112 struct vm86_struct vm86s
;
113 #define sc vm86s.substr.regs.vmsc
115 regcontext_t
*REGS
= (regcontext_t
*)&uc
.uc_mcontext
;
120 sigemptyset(&sigset
);
121 sigaddset(&sigset
, SIGIO
);
122 sigaddset(&sigset
, SIGALRM
);
123 sigprocmask(SIG_BLOCK
, &sigset
, 0);
128 /* XXX should only be for tty mode */
129 fd
= open (_PATH_DEVNULL
, O_RDWR
);
131 dup2 (fd
, 3); /* stdaux */
133 dup2 (fd
, 4); /* stdprt */
134 if (fd
!= 3 && fd
!= 4)
138 debug_set(0); /* debug any D_TRAPS without intnum */
140 /* perform option argument processing */
145 if (vflag
&& debugf
== stderr
) {
147 setbuf (stdout
, NULL
);
152 /* This needs to happen before the executable is loaded */
156 memset(&vm86s
, 0, sizeof(vm86s
));
160 * With no other arguments we will assume we must boot DOS
167 * Nominate interrupts to handle here when the kernel is
168 * performing interrupt handling.
170 * I would like to let INT 2F pass through as well, but I
171 * need to get my hands on INT 2F:11 to do file redirection.
173 for (i
= 0; i
<= 0xff; ++i
) {
178 kargs
.int_map
[i
>> 3] |= (1 << (i
& 7));
180 vconnect_area
.passthru
[i
>> 5] &= ~(1 << (i
& 0x1f));
182 vm86s
.int_byuser
[i
>> 3] |= (1 << (i
& 0x07));
188 kargs
.int_map
[i
>> 3] &= ~(1 << (i
& 7));
190 vconnect_area
.passthru
[i
>> 5] |= (1 << (i
& 0x1f));
192 vm86s
.int_byuser
[i
>> 3] |= (1 << (i
& 0x07));
200 if (booting
) { /* are we booting? */
202 } else { /* no, load a command */
203 setup_command(argc
, argv
, REGS
);
206 /* install signal handlers */
207 setsignal(SIGFPE
, sigfpe
); /* */
208 setsignal(SIGALRM
, sigalrm
); /* */
209 setsignal(SIGILL
, sigill
); /* */
210 setsignal(SIGTRAP
, sigtrap
); /* */
211 setsignal(SIGUSR2
, sigtrace
); /* */
212 setsignal(SIGINFO
, sigtrace
); /* */
214 setsignal(SIGURG
, sigurg
); /* entry from NetBSD vm86 */
216 setsignal(SIGBUS
, sigbus
); /* entry from FreeBSD, BSD/OS vm86 */
219 /* Call init functions */
222 init_io_port_handlers();
240 gettimeofday(&boot_time
, 0);
242 if (zflag
) for (;;) pause(); /* spin if requested */
246 * If we have a raw keyboard, and hence, video,
247 * sneak in a call to the video BIOS to reinit the
251 static u_char video_trampoline
[] = {
253 0xB8, 0x03, 0x00, /* mov ax,00003h */
254 0xCD, 0x10, /* int 010h */
259 video_vector
= insert_generic_trampoline(
260 sizeof(video_trampoline
), video_trampoline
);
265 PUTVEC(R_CS
, R_IP
, video_vector
);
268 sigemptyset(&uc
.uc_sigmask
);
269 sigaltstack(NULL
, &uc
.uc_stack
);
270 uc
.uc_mcontext
.mc_onstack
= 0;
276 R_EAX
= (booting
|| raw_kbd
) ? (int)&vconnect_area
: -1;
277 R_EFLAGS
|= PSL_VM
| PSL_VIF
; /* request VM86 mode */
279 i386_vm86(VM86_INIT
, &kargs
);
282 debug(D_ALWAYS
,"sigreturn failed : %s\n", strerror(errno
));
284 vm86s
.cpu_type
= VCPU_586
;
288 /* shouldn't get here */
289 if (vflag
) dump_regs(REGS
);
290 fatal ("vm86 returned (no kernel support?)\n");
302 setup_boot(regcontext_t
*REGS
)
304 FILE *fp
; /* doscmdrc handle */
305 int fd
; /* don't close this! */
307 fp
= find_doscmdrc(); /* get doscmdrc */
309 fprintf(stderr
, "You must have a doscmdrc to boot\n");
313 booting
= read_config(fp
); /* where to boot from? */
315 if (booting
< 0) { /* not specified */
316 if ((fd
= try_boot(booting
= 0)) < 0) /* try A: */
317 fd
= try_boot(booting
= 2); /* try C: */
319 fd
= try_boot(booting
); /* do like the man says */
323 errx(1, "Failed to boot");
325 /* initialise registers for entry to bootblock */
334 R_AX
= R_BX
= R_CX
= R_DX
= R_SI
= R_DI
= R_BP
= 0;
336 #if defined(__FreeBSD__) || defined(__NetBSD__) || defined (__DragonFly__)
338 ** init a few other context registers
348 ** try to read the boot sector from the specified disk
351 try_boot(int bootdrv
)
355 fd
= disk_fd(bootdrv
);
356 if (fd
< 0) { /* can we boot it? */
357 debug(D_DISK
, "Cannot boot from %c\n", drntol(bootdrv
));
362 if (read(fd
, (char *)0x7c00, 512) != 512) {
363 debug(D_DISK
, "Short read on boot block from %c:\n", drntol(bootdrv
));
373 ** Setup to run a single command and emulate DOS
376 setup_command(int argc
, char *argv
[], regcontext_t
*REGS
)
379 u_short param
[7] = {0, 0, 0, 0, 0, 0, 0};
382 char buffer
[PATH_MAX
];
386 fp
= find_doscmdrc(); /* dig up a doscmdrc */
388 read_config(fp
); /* load config for non-boot mode */
392 if (argc
<= 0) /* need some arguments */
395 /* look for a working directory XXX ??? */
396 if (dos_getcwd(drlton('C')) == NULL
) {
398 /* try to get our current directory, use '/' if desperate */
399 p
= getcwd(buffer
, sizeof(buffer
));
400 if (!p
|| !*p
) p
= getenv("PWD");
401 if (!p
|| !*p
) p
= "/";
402 init_path(drlton('C'), "/", p
);
404 /* look for PATH= already set, learn from it if possible */
405 for (i
= 0; i
< ecnt
; ++i
) {
406 if (!strncmp(envs
[i
], "PATH=", 5)) {
407 dos_path
= envs
[i
] + 5;
411 /* no PATH in DOS environment? put current directory there*/
413 static char path
[256];
414 snprintf(path
, sizeof(path
), "PATH=C:%s", dos_getcwd(drlton('C')));
416 dos_path
= envs
[ecnt
-1] + 5;
420 /* add a COMSPEC if required */
421 for (i
= 0; i
< ecnt
; ++i
) {
422 if (!strncmp(envs
[i
], "COMSPEC=", 8))
426 put_dosenv("COMSPEC=C:\\COMMAND.COM");
428 /* look for PATH already set, learn from it if possible */
429 for (i
= 0; i
< ecnt
; ++i
) {
430 if (!strncmp(envs
[i
], "PATH=", 5)) {
431 dos_path
= envs
[i
] + 5;
435 /* No PATH, default to c:\ */
437 put_dosenv("PATH=C:\\");
438 dos_path
= envs
[ecnt
-1] + 5;
441 /* if no PROMPT, default to 'DOS>' */
442 for (i
= 0; i
< ecnt
; ++i
) {
443 if (!strncmp(envs
[i
], "PROMPT=", 7))
447 put_dosenv("PROMPT=DOS> ");
449 /* terminate environment */
453 if (dos_getcwd(drlton('R')) == NULL
)
454 init_path(drlton('R'), "/", 0);
456 /* get program name */
457 strncpy(prog
, *argv
++, sizeof(prog
) -1);
458 prog
[sizeof(prog
) -1] = '\0';
460 /* try to open program */
461 if ((fd
= open_prog(prog
)) < 0) {
462 fprintf (stderr
, "%s: command not found\n", prog
);
467 load_command(REGS
, 1, fd
, cmdname
, param
, argv
, envs
);
474 ** Try to find a doscmdrc file
482 if ((fp
= fopen(".doscmdrc", "r")) == NULL
) {
483 struct passwd
*pwd
= getpwuid(geteuid());
485 snprintf(buffer
, sizeof(buffer
), "%s/.doscmdrc", pwd
->pw_dir
);
486 fp
= fopen(buffer
, "r");
489 char *home
= getenv("HOME");
491 snprintf(buffer
, sizeof(buffer
), "%s/.doscmdrc", home
);
492 fp
= fopen(buffer
, "r");
496 fp
= fopen("/etc/doscmdrc", "r");
504 ** commandline argument processing
507 do_args(int argc
, char *argv
[])
513 while ((c
= getopt(argc
, argv
, "234AbCc:Dd:EGHIi:kLMOo:Pp:RrS:TtU:vVxXYz")) != -1) {
516 debug_flags
|= D_TRAPS2
;
519 debug_flags
|= D_TRAPS3
;
522 debug_flags
|= D_DEBUGIN
;
525 debug_flags
|= D_TRAPS
| D_ITRAPS
;
526 for (c
= 0; c
< 256; ++c
)
533 debug_flags
|= D_DOSCALL
;
536 if ((capture_fd
= creat(optarg
, 0666)) < 0) {
542 debug_flags
|= D_DISK
| D_FILE_OPS
;
545 if ((fp
= fopen(optarg
, "w")) != 0) {
552 debug_flags
|= D_EXEC
;
555 debug_flags
|= D_VIDEO
;
558 debug_flags
|= D_HALF
;
561 debug_flags
|= D_ITRAPS
;
562 for (c
= 0; c
< 256; ++c
)
567 if ((col
= strchr(optarg
, ':')) != 0) {
569 i
= strtol(col
, 0, 0);
571 p
= strtol(optarg
, 0, 0);
575 define_input_port_handler(p
++, inb_traceport
);
581 debug_flags
|= D_PRINTER
;
584 debug_flags
|= D_MEMORY
;
588 setbuf (stdout
, NULL
);
592 if ((col
= strchr(optarg
, ':')) != 0) {
594 i
= strtol(col
, 0, 0);
596 p
= strtol(optarg
, 0, 0);
600 define_output_port_handler(p
++, outb_traceport
);
603 debug_flags
|= D_PORT
;
607 if ((col
= strchr(optarg
, ':')) != 0) {
609 i
= strtol(col
, 0, 0);
611 p
= strtol(optarg
, 0, 0);
615 define_input_port_handler(p
++, inb_port
);
616 define_output_port_handler(p
++, outb_port
);
620 debug_flags
|= D_REDIR
;
626 debug_flags
|= D_TRAPS
| D_ITRAPS
;
627 debug_set(strtol(optarg
, 0, 0));
636 debug_unset(strtol(optarg
, 0, 0));
642 debug_flags
|= D_TRAPS
| D_ITRAPS
| D_HALF
| 0xff;
645 debug_flags
|= D_XMS
;
649 fatal("X11 support not compiled in.\n");
654 debug_flags
|= D_EMS
;
672 fprintf (stderr
, "usage: doscmd cmd args...\n");
677 ** look up a DOS command name
679 ** XXX ordering is wrong!
682 open_name(char *name
, char *ext
)
685 char *p
= name
+ strlen(name
);
690 q
= strrchr(name
, '/');
696 if (!strchr(q
, '.')) {
700 if ((fd
= open (name
, O_RDONLY
)) >= 0)
706 if ((fd
= open (name
, O_RDONLY
)) >= 0)
709 if ((fd
= open (name
, O_RDONLY
)) >= 0)
717 ** look up a DOS command, search the path as well.
720 open_prog(char *name
)
723 char fullname
[1024], tmppath
[1024];
731 if (strpbrk(name
, ":/\\")) {
732 error
= translate_filename(name
, fullname
, &drive
);
736 fd
= open_name(fullname
, ext
);
738 strcpy(cmdname
, name
);
740 strcat(cmdname
, ext
);
748 while (*p
&& *p
!= ';')
751 memcpy(tmppath
, path
, p
- path
);
752 e
= tmppath
+ (p
- path
);
756 path
= *p
? p
+ 1 : p
;
758 error
= translate_filename(tmppath
, fullname
, &drive
);
762 fd
= open_name(fullname
, ext
);
765 strcpy(cmdname
, tmppath
);
767 strcat(cmdname
, ext
);
776 ** append a value to the DOS environment
779 put_dosenv(const char *value
)
781 if (ecnt
< sizeof(envs
)/sizeof(envs
[0])) {
782 if ((envs
[ecnt
++] = strdup(value
)) == NULL
) {
783 perror("put_dosenv");
787 fprintf(stderr
, "Environment full, ignoring %s\n", value
);
792 ** replicate a fd up at the top of the range
797 int sfd
= sysconf(_SC_OPEN_MAX
);
803 } while (sfd
> 0 && errno
!= EBADF
);
805 if (errno
== EBADF
&& dup2(fd
, sfd
) >= 0) {
819 ** XXX belongs somewhere else perhaps
822 done(regcontext_t
*REGS
, int val
)
829 for (m
= "END OF PROGRAM"; *m
; ++m
)
830 tty_write(*m
, 0x8400);
832 for (m
= "(PRESS <CTRL-ALT> ANY MOUSE BUTTON TO exit)"; *m
; ++m
)
833 tty_write(*m
, 0x0900);
841 exec_return(REGS
, val
);
845 void (*func
)(void *);
860 if (!xmode
) /* XXX not for bootmode */
866 call_on_quit(void (*func
)(void *), void *arg
)
868 COQ
*c
= (COQ
*)malloc(sizeof(COQ
));
870 perror("call_on_quit");
885 /* This is commented out as it is never called. Turn it back on if needed.
892 struct io_range io
[] = {
894 { 0x200, 0x200, 1 }, /* 0x200 - 0x400 */
895 { 0x1c80, 2, 1 }, /* 0x1c80 - 0x1c81 */
896 { 0x2c80, 2, 1 }, /* 0x2c80 - 0x2c81 */
897 { 0x3c80, 2, 1 }, /* 0x3c80 - 0x3c81 */
898 { 0x378, 8, 1 }, /* 0x378 - 0x37F */
899 { 0x3c4, 2, 1 }, /* 0x3c4 - 0x3c5 */
900 { 0x3c5, 2, 1 }, /* 0x3ce - 0x3cf */
902 { 0x0, 0x10000, 1 }, /* entire i/o space */
907 for (i
= 0; io
[i
].length
; i
++)
908 if (i386_set_ioperm(io
[i
].start
, io
[i
].length
, io
[i
].enable
) < 0)
909 err(1, "i386_set_ioperm");
913 /* This is used to map in only the specified port range, instead of all
914 the ports or only certain port ranges.
917 iomap_port(int port
, int count
)
919 if (i386_set_ioperm(port
, count
, 1) < 0)
920 err(1, "i386_set_ioperm");
922 debug(D_PORT
,"mapped I/O port: port=%#x count=%d\n", port
, count
);