diff options
author | dyson <dyson@FreeBSD.org> | 1997-08-09 01:43:15 +0000 |
---|---|---|
committer | dyson <dyson@FreeBSD.org> | 1997-08-09 01:43:15 +0000 |
commit | 305573cb2990c5d329d149cef5a3b5533b1e8fd9 (patch) | |
tree | df06304b637358dbe8a006fdb7a6ea5955fee179 /usr.bin/doscmd/doscmd.c | |
parent | dede28832bba6a9de7a428ff58df92439bddbc9c (diff) | |
download | FreeBSD-src-305573cb2990c5d329d149cef5a3b5533b1e8fd9.zip FreeBSD-src-305573cb2990c5d329d149cef5a3b5533b1e8fd9.tar.gz |
Add our doscmd to the tree. This is a result of work from BSDI, and
a group of dos emulator developers.
Submitted by: Jonathan Lemon <jlemon@americantv.com>
Obtained from: BSDI
Diffstat (limited to 'usr.bin/doscmd/doscmd.c')
-rw-r--r-- | usr.bin/doscmd/doscmd.c | 892 |
1 files changed, 892 insertions, 0 deletions
diff --git a/usr.bin/doscmd/doscmd.c b/usr.bin/doscmd/doscmd.c new file mode 100644 index 0000000..282aeab --- /dev/null +++ b/usr.bin/doscmd/doscmd.c @@ -0,0 +1,892 @@ +/* + * Copyright (c) 1992, 1993, 1996 + * Berkeley Software Design, Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Berkeley Software + * Design, Inc. + * + * THIS SOFTWARE IS PROVIDED BY Berkeley Software Design, Inc. ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL Berkeley Software Design, Inc. BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * BSDI doscmd.c,v 2.3 1996/04/08 19:32:30 bostic Exp + * + * $Id: doscmd.c,v 1.10 1997/03/18 02:36:55 msmith Exp $ + */ + +#include <sys/types.h> +#include <sys/param.h> +#include <sys/mman.h> +#include <sys/time.h> + +#include <errno.h> +#include <limits.h> +#include <pwd.h> +#include <signal.h> +#include <unistd.h> + +#include <machine/param.h> +#include <machine/vmparam.h> + +#include <sys/proc.h> +#include <machine/sysarch.h> +#include <machine/vm86.h> + +#include "doscmd.h" + +/* exports */ +int capture_fd = -1; +int dead = 0; +int xmode = 0; +int booting = 0; +int raw_kbd = 0; +int timer_disable = 0; +struct timeval boot_time; +unsigned long *ivec = (unsigned long *)0; + +u_long pending[256]; /* pending interrupts */ +int n_pending; + +#ifndef USE_VM86 +#define PRB_V86_FORMAT 0x4242 + +struct vconnect_area vconnect_area = { + 0, /* Interrupt state */ + PRB_V86_FORMAT, /* Magic number */ + { 0, }, /* Pass through ints */ + { 0x00000000, 0x00000000 } /* Magic iret location */ +}; +#endif + +/* local prototypes */ +static void setup_boot(regcontext_t *REGS); +static void setup_command(int argc, char *argv[], regcontext_t *REGS); +static FILE *find_doscmdrc(void); +static int do_args(int argc, char *argv[]); +static void usage(void); +static int open_name(char *name, char *ext); +static void init_iomap(void); + +/* Local option flags &c. */ +static int zflag = 0; + +/* DOS environment emulation */ +static int ecnt = 0; +static char *envs[256]; + +/* Search path and command name */ +static char *dos_path = 0; +char cmdname[256]; /* referenced from dos.c */ + +/* high memory mapfile */ +static char *memfile = "/tmp/doscmd.XXXXXX"; + +static struct i386_vm86_args vm86; +static struct vm86_init_args kargs; + +/* lobotomise */ +int +main(int argc, char **argv) +{ +#ifndef USE_VM86 + struct sigcontext sc; +#else + struct vm86_struct vm86s; +#define sc vm86s.substr.regs.vmsc +#endif + regcontext_t *REGS = (regcontext_t *)≻ + int fd; + int i; + char buffer[4096]; + int mfd; + FILE *fp; + + + /* XXX should only be for tty mode */ + fd = open ("/dev/null", O_RDWR); + if (fd != 3) + dup2 (fd, 3); /* stdaux */ + if (fd != 4) + dup2 (fd, 4); /* stdprt */ + if (fd != 3 && fd != 4) + close (fd); + fd = -1; + + debug_set(0); /* debug any D_TRAPS without intnum */ + + /* perform option argument processing */ + optind = do_args(argc, argv); + argc -= optind; + argv += optind; + + if (vflag && debugf == stderr) { + debugf = stdout; + setbuf (stdout, NULL); + } + + mfd = mkstemp(memfile); + + if (mfd < 0) { + fprintf(stderr, "memfile: %s\n", strerror(errno)); + fprintf(stderr, "High memory will not be mapped\n"); + } else { + caddr_t add; + + unlink(memfile); + + mfd = squirrel_fd(mfd); + + lseek(mfd, 64 * 1024 - 1, 0); + write(mfd, "", 1); + add = mmap((caddr_t)0x000000, 64 * 1024, + PROT_EXEC | PROT_READ | PROT_WRITE, + MAP_FILE | MAP_FIXED | MAP_INHERIT | MAP_SHARED, + mfd, 0); + add = mmap((caddr_t)0x100000, 64 * 1024, + PROT_EXEC | PROT_READ | PROT_WRITE, + MAP_FILE | MAP_FIXED | MAP_INHERIT | MAP_SHARED, + mfd, 0); + } + + /* This needs to happen before the executable is loaded */ + mem_init(); + +#ifdef USE_VM86 + memset(&vm86s, 0, sizeof(vm86s)); +#endif + + /* + * With no other arguments we will assume we must boot DOS + */ + if (argc <= 0) + booting = 1; + +#if 1 + /* + * Nominate interrupts to handle here when the kernel is + * performing interrupt handling. + * + * I would like to let INT 2F pass through as well, but I + * need to get my hands on INT 2F:11 to do file redirection. + */ + for (i = 0; i <= 0xff; ++i) { + switch (i) { + case 0x2f: + case 0xff: +#if 1 + kargs.int_map[i >> 3] |= (1 << (i & 7)); +#ifndef USE_VM86 + vconnect_area.passthru[i >> 5] &= ~(1 << (i & 0x1f)); +#else + vm86s.int_byuser[i >> 3] |= (1 << (i & 0x07)); +#endif +#endif + break; + default: +#if 1 + kargs.int_map[i >> 3] &= ~(1 << (i & 7)); +#ifndef USE_VM86 + vconnect_area.passthru[i >> 5] |= (1 << (i & 0x1f)); +#else + vm86s.int_byuser[i >> 3] |= (1 << (i & 0x07)); +#endif +#endif + break; + } + } +#endif + for (i = 0; i < 256; i++) + pending[i] = 0; + n_pending = 0; + + if (booting) { /* are we booting? */ + setup_boot(REGS); + } else { /* no, load a command */ + setup_command(argc, argv, REGS); + } + + /* install signal handlers */ + setsignal (SIGFPE, sigfpe); /* */ + setsignal (SIGALRM, sigalrm); /* */ + setsignal (SIGILL, sigill); /* */ + setsignal (SIGTRAP, sigtrap); /* */ + setsignal (SIGUSR2, sigtrace); /* */ + setsignal (SIGINFO, sigtrace); /* */ +#ifdef USE_VM86 + setsignal (SIGURG, sigurg); /* entry from NetBSD vm86 */ +#else + setsignal (SIGBUS, sigbus); /* entry from FreeBSD, BSD/OS vm86 */ +#endif + + /* Call init functions */ + if (raw_kbd) + console_init(); + init_io_port_handlers(); + bios_init(); + cpu_init(); + video_init(); + if (xmode) + mouse_init(); + video_bios_init(); + disk_bios_init(); + cmos_init(); + xms_init(); + dos_init(); + net_init(); + speaker_init(); + timer_init(); + /* iomap_init(); */ + + gettimeofday(&boot_time, 0); + + if (zflag) for (;;) pause(); /* spin if requested */ + + if (raw_kbd) { + /* + * If we have a raw keyboard, and hence, video, + * sneak in a call to the video BIOS to reinit the + * the video display. + */ + u_long video_vector; + static u_char video_trampoline[] = { + 0x60, /* pusha */ + 0xB8, 0x03, 0x00, /* mov ax,00003h */ + 0xCD, 0x10, /* int 010h */ + 0x61, /* popa */ + 0xCF, /* iret */ + }; + + video_vector = insert_generic_trampoline( + sizeof(video_trampoline), video_trampoline); + + N_PUSH(R_FLAGS, REGS); + N_PUSH(R_CS, REGS); + N_PUSH(R_IP, REGS); + N_PUTVEC(R_CS, R_IP, video_vector); + } + + sc.sc_mask = 0; + sc.sc_onstack = 0; + + if (tmode) + tracetrap(REGS); + +#ifndef USE_VM86 + R_EAX = (booting || raw_kbd) ? (int)&vconnect_area : -1; + R_EFLAGS |= PSL_VM | PSL_VIF; /* request VM86 mode */ + + vm86.sub_op = VM86_INIT; + vm86.sub_args = (char *)&kargs; + i = sysarch(I386_VM86, &vm86); + printf("Init: %d\n", i); + + sigreturn(&sc); + debug(D_ALWAYS,"sigreturn failed : %s\n", strerror(errno)); +#else + vm86s.cpu_type = VCPU_586; + i386_vm86(&vm86s); +#endif + + /* shouldn't get here */ + if (vflag) dump_regs((regcontext_t *)&sc); + fatal ("vm86 returned (no kernel support?)\n"); +#undef sc +} + +/* +** setup_boot +** +** Setup to boot DOS +*/ +static void +setup_boot(regcontext_t *REGS) +{ + FILE *fp; /* doscmdrc handle */ + int fd; /* don't close this! */ + + fp = find_doscmdrc(); /* get doscmdrc */ + if (!fp) { + fprintf(stderr, "You must have a doscmdrc to boot\n"); + quit(1); + } + + booting = read_config(fp); /* where to boot from? */ + fclose(fp); + if (booting < 0) { /* not specified */ + if ((fd = disk_fd(booting = 0)) < 0) /* try A: */ + fd = disk_fd(booting = 2); /* try C: */ + } else { + fd = disk_fd(booting); /* do like the man says */ + } + + if (fd < 0) { /* can we boot it? */ + fprintf(stderr, "Cannot boot from %c: (can't open)\n", + booting + 'A'); + quit(1); + } + + /* read bootblock */ + if (read(fd, (char *)0x7c00, 512) != 512) { + fprintf(stderr, "Short read on boot block from %c:\n", + booting + 'A'); + quit(1); + } + + /* initialise registers for entry to bootblock */ + R_EFLAGS = 0x20202; + R_CS = 0x0000; + R_IP = 0x7c00; + R_SS = 0x9800; + R_SP = 0x8000 - 2; + R_DS = 0x0000; + R_ES = 0x0000; + + R_AX = R_BX = R_CX = R_DX = R_SI = R_DI = R_BP = 0; + +#if defined(__FreeBSD__) || defined(__NetBSD__) + /* + ** init a few other context registers + */ + R_FS = 0x0000; + R_GS = 0x0000; +#endif +} + +/* +** setup_command +** +** Setup to run a single command and emulate DOS +*/ +static void +setup_command(int argc, char *argv[], regcontext_t *REGS) +{ + FILE *fp; + u_short param[7] = {0, 0, 0, 0, 0, 0, 0}; + char *p; + char prog[1024]; + char buffer[PATH_MAX]; + int i; + int fd; + + fp = find_doscmdrc(); /* dig up a doscmdrc */ + if (fp) { + read_config(fp); /* load config for non-boot mode */ + fclose(fp); + } + + if (argc <= 0) /* need some arguments */ + usage(); + + /* look for a working directory XXX ??? */ + if (dos_getcwd('C' - 'A') == NULL) { + + /* try to get our current directory, use '/' if desperate */ + p = getcwd(buffer, sizeof(buffer)); + if (!p || !*p) p = getenv("PWD"); + if (!p || !*p) p = "/"; + init_path('C' - 'A', (u_char *)"/", (u_char *)p); + + /* look for PATH= already set, learn from it if possible */ + for (i = 0; i < ecnt; ++i) { + if (!strncmp(envs[i], "PATH=", 5)) { + dos_path = envs[i] + 5; + break; + } + } + /* no PATH in DOS environment? put current directory there*/ + if (i >= ecnt) { + static char path[256]; + sprintf(path, "PATH=C:%s", + dos_getcwd('C' - 'A')); + put_dosenv(path); + dos_path = envs[ecnt-1] + 5; + } + } + + /* add a COMSPEC if required */ + for (i = 0; i < ecnt; ++i) { + if (!strncmp(envs[i], "COMSPEC=", 8)) + break; + } + if (i >= ecnt) + put_dosenv("COMSPEC=C:\\COMMAND.COM"); + + /* look for PATH already set, learn from it if possible */ + for (i = 0; i < ecnt; ++i) { + if (!strncmp(envs[i], "PATH=", 5)) { + dos_path = envs[i] + 5; + break; + } + } + /* No PATH, default to c:\ */ + if (i >= ecnt) { + dos_path = envs[ecnt-1] + 5; + put_dosenv("PATH=C:\\"); + } + + /* if no PROMPT, default to 'DOS>' */ + for (i = 0; i < ecnt; ++i) { + if (!strncmp(envs[i], "PROMPT=", 7)) + break; + } + if (i >= ecnt) + put_dosenv("PROMPT=DOS> "); + + /* terminate environment */ + envs[ecnt] = 0; + + /* XXX ??? */ + if (dos_getcwd('R' - 'A') == NULL) + init_path('R' - 'A', (u_char *)"/", 0); + + /* get program name */ + strncpy(prog, *argv++, sizeof(prog) -1); + prog[sizeof(prog) -1] = '\0'; + + /* try to open program */ + if ((fd = open_prog(prog)) < 0) { + fprintf (stderr, "%s: command not found\n", prog); + quit(1); + } + + /* load program */ + load_command(REGS, 1, fd, cmdname, param, argv, envs); + close(fd); +} + +/* +** find_doscmdrc +** +** Try to find a doscmdrc file +*/ +static FILE * +find_doscmdrc(void) +{ + FILE *fp; + char buffer[4096]; + int fd; + + if ((fp = fopen(".doscmdrc", "r")) == NULL) { + struct passwd *pwd = getpwuid(geteuid()); + if (pwd) { + sprintf(buffer, "%s/.doscmdrc", pwd->pw_dir); + fp = fopen(buffer, "r"); + } + if (!fp) { + char *home = getenv("HOME"); + if (home) { + sprintf(buffer, "%s/.doscmdrc", home); + fp = fopen(buffer, "r"); + } + } + if (!fp) + fp = fopen("/etc/doscmdrc", "r"); + } + return(fp); +} + +/* +** do_args +** +** commandline argument processing +*/ +static int +do_args(int argc, char *argv[]) +{ + int i,c,p; + FILE *fp; + char *col; + + while ((c = getopt (argc, argv, "234Oc:TkCIEMPRLAU:S:HDtzvVxXfbri:o:d:")) != -1) { + switch (c) { + case 'd': + if (fp = fopen(optarg, "w")) { + debugf = fp; + setbuf (fp, NULL); + } else + perror(optarg); + break; + case '2': + debug_flags |= D_TRAPS2; + break; + case '3': + debug_flags |= D_TRAPS3; + break; + case '4': + debug_flags |= D_DEBUGIN; + break; + case 'O': + debugf = stdout; + setbuf (stdout, NULL); + break; + case 'c': + if ((capture_fd = creat(optarg, 0666)) < 0) { + perror(optarg); + quit(1); + } + break; + case 'i': + i = 1; + if (col = strchr(optarg, ':')) { + *col++ = 0; + i = strtol(col, 0, 0); + } + p = strtol(optarg, 0, 0); + + while (i-- > 0) + define_input_port_handler(p++, inb_traceport); + break; + case 'o': + i = 1; + if (col = strchr(optarg, ':')) { + *col++ = 0; + i = strtol(col, 0, 0); + } + p = strtol(optarg, 0, 0); + + while (i-- > 0) + define_output_port_handler(p++, outb_traceport); + break; + + case 'r': + raw_kbd = 1; + break; + case 'I': + debug_flags |= D_ITRAPS; + for (c = 0; c < 256; ++c) + debug_set(c); + break; + case 'k': + kargs.debug = 1; + break; + case 'T': + timer_disable = 1; + break; + case 'E': + debug_flags |= D_EXEC; + break; + case 'C': + debug_flags |= D_DOSCALL; + break; + case 'M': + debug_flags |= D_MEMORY; + break; + case 'P': + debug_flags |= D_PORT; + break; + case 'R': + debug_flags |= D_REDIR; + break; + case 'L': + debug_flags |= D_PRINTER; + break; + case 'A': + debug_flags |= D_TRAPS|D_ITRAPS; + for (c = 0; c < 256; ++c) + debug_set(c); + break; + case 'U': + debug_unset(strtol(optarg, 0, 0)); + break; + case 'S': + debug_flags |= D_TRAPS|D_ITRAPS; + debug_set(strtol(optarg, 0, 0)); + break; + case 'H': + debug_flags |= D_HALF; + break; + case 'x': +#ifdef NO_X + fatal("X11 support not compiled in.\n"); +#endif + xmode = 1; + break; + case 't': + tmode = 1; + break; + case 'z': + zflag = 1; + break; + case 'D': + debug_flags |= D_DISK | D_FILE_OPS; + break; + case 'v': + debug_flags |= D_TRAPS | D_ITRAPS | D_HALF | 0xff; + break; + case 'V': + vflag = 1; + break; + case 'b': + booting = 1; + break; + default: + usage (); + } + } + return(optind); +} + +/* +** Very helpful 8( +*/ +void +usage (void) +{ + fprintf (stderr, "usage: doscmd cmd args...\n"); + quit (1); +} + +/* +** look up a DOS command name +** +** XXX ordering is wrong! +*/ +static int +open_name(char *name, char *ext) +{ + int fd; + char *p = name + strlen(name); + char *q; + + *ext = 0; + + q = strrchr(name, '/'); + if (q) + q++; + else + q = name; + + if (!strchr(q, '.')) { + strcpy(ext, ".exe"); + strcpy(p, ".exe"); + + if ((fd = open (name, O_RDONLY)) >= 0) + return (fd); + + strcpy(ext, ".com"); + strcpy(p, ".com"); + + if ((fd = open (name, O_RDONLY)) >= 0) + return (fd); + } else { + if ((fd = open (name, O_RDONLY)) >= 0) + return (fd); + } + + return (-1); +} + +/* +** look up a DOS command, search the path as well. +*/ +int +open_prog(char *name) +{ + int fd; + char fullname[1024], tmppath[1024]; + char *p; + char *e; + char ext[5]; + int error; + int drive; + char *path; + + if (strpbrk(name, ":/\\")) { + error = translate_filename(name, fullname, &drive); + if (error) + return (-1); + + fd = open_name(fullname, ext); + + strcpy(cmdname, name); + if (*ext) + strcat(cmdname, ext); + return (fd); + } + + path = dos_path; + + while (*path) { + p = path; + while (*p && *p != ';') + ++p; + + memcpy(tmppath, path, p - path); + e = tmppath + (p - path); + *e++ = '\\'; + strcpy(e, name); + + path = *p ? p + 1 : p; + + error = translate_filename(tmppath, fullname, &drive); + if (error) + continue; + + fd = open_name(fullname, ext); + + if (fd >= 0) { + strcpy(cmdname, tmppath); + if (*ext) + strcat(cmdname, ext); + return (fd); + } + } + + return (-1); +} + +/* +** append a value to the DOS environment +*/ +void +put_dosenv(char *value) +{ + if (ecnt < sizeof(envs)/sizeof(envs[0])) { + if ((envs[ecnt++] = strdup(value)) == NULL) { + perror("put_dosenv"); + quit(1); + } + } else { + fprintf(stderr, "Environment full, ignoring %s\n", value); + } +} + +/* +** replicate a fd up at the top of the range +*/ +int +squirrel_fd(int fd) +{ + int sfd = sysconf(_SC_OPEN_MAX); + struct stat sb; + + do { + errno = 0; + fstat(--sfd, &sb); + } while (sfd > 0 && errno != EBADF); + + if (errno == EBADF && dup2(fd, sfd) >= 0) { + close(fd); + return(sfd); + } + return(fd); +} + +/* +** Exit-time stuff +*/ + +/* +** Going away time +** +** XXX belongs somewhere else perhaps +*/ +void +done (regcontext_t *REGS, int val) +{ + if (curpsp < 2) { + if (xmode) { + char *m; + + tty_move(24, 0); + for (m = "END OF PROGRAM"; *m; ++m) + tty_write(*m, 0x8400); + + for (m = "(PRESS <CTRL-ALT> ANY MOUSE BUTTON TO exit)"; *m; ++m) + tty_write(*m, 0x0900); + tty_move(-1, -1); + for (;;) + tty_pause(); + } else { + quit(val); + } + } + exec_return(REGS, val); +} + +typedef struct COQ { + void (*func)(); + void *arg; + struct COQ *next; +} COQ; + +COQ *coq = 0; + +void +quit(int status) +{ + while (coq) { + COQ *c = coq; + coq = coq->next; + c->func(c->arg); + } + if (!xmode) /* XXX not for bootmode */ + puts("\n"); + exit(status); +} + +void +call_on_quit(void (*func)(void *), void *arg) +{ + COQ *c = (COQ *)malloc(sizeof(COQ)); + if (!c) { + perror("call_on_quit"); + quit(1); + } + c->func = func; + c->arg = arg; + c->next = coq; + coq = c; +} + +struct i386_ioperm_args { + u_short start; + u_short length; + char disable; +}; + +struct sysarch_args { + int op; + char *parms; +}; + +static void +iomap_init(void) +{ + int i; + struct i386_ioperm_args args[] = { +#if 0 + { 0x200, 0x200, 1 }, /* 0x200 - 0x400 */ + { 0x1c80, 2, 1 }, /* 0x1c80 - 0x1c81 */ + { 0x2c80, 2, 1 }, /* 0x2c80 - 0x2c81 */ + { 0x3c80, 2, 1 }, /* 0x3c80 - 0x3c81 */ + { 0x3c4, 2, 1 }, /* 0x3c4 - 0x3c5 */ + { 0x3c5, 2, 1 }, /* 0x3ce - 0x3cf */ +#else + { 0x0, 0xffff, 1 }, /* entire i/o space */ +#endif + { 0, 0, 0 } + }; + + for (i = 0; args[i].length; i++) + if (sysarch(I386_SET_IOPERM, &(args[i])) < 0) + err(1, "sysarch"); +} |