/* * (C) Copyright 1992, ..., 2006 the "DOSEMU-Development-Team". * * for details see file COPYING.DOSEMU in the DOSEMU distribution */ /* * Extensions by Robert Sanders, 1992-93 * * DANG_BEGIN_MODULE * * REMARK * Here is where DOSEMU gets booted. From emu.c external calls are made to * the specific I/O systems (video/keyboard/serial/etc...) to initialize * them. Memory is cleared/set up and the boot sector is read from the * boot drive. Many SIGNALS are set so that DOSEMU can exploit things like * timers, I/O signals, illegal instructions, etc... When every system * gives the green light, vm86() is called to switch into vm86 mode and * start executing i86 code. * * The vm86() function will return to DOSEMU when certain `exceptions` occur * as when some interrupt instructions occur (0xcd). * * The top level function emulate() is called from dos.c by way of a dll * entry point. * * /REMARK * DANG_END_MODULE * */ /* * DANG_BEGIN_REMARK * DOSEMU must not work within the 1 meg DOS limit, so * start of code is loaded at a higher address, at some time this could * conflict with other shared libs. If DOSEMU is compiled statically * (without shared libs), and org instruction is used to provide the jump * above 1 meg. * DANG_END_REMARK */ #include "config.h" #ifndef __ELF__ /* * DANG_BEGIN_FUNCTION jmp_emulate * * description: This function allows the startup program `dos` to know how to * call the emulate function by way of the dll headers. Always make sure * that this line is the first of emu.c and link emu.o as the first object * file to the lib * * DANG_END_FUNCTION */ __asm__("___START___: jmp _emulate\n"); #endif #include #include #include #include #include #include #ifndef EDEADLOCK #define EDEADLOCK EDEADLK #endif #include #include #include #include #include #include #include #include #include #include #include #ifdef __linux__ #include #include #include "Linux/fd.h" #include "Linux/hdreg.h" #include #endif #include "memory.h" #ifdef USE_MHPDBG #include "mhpdbg.h" #endif #include "debug.h" #include "emu.h" #include "vm86plus.h" #include "bios.h" #include "termio.h" #include "video.h" #include "timers.h" #include "cmos.h" #include "mouse.h" #include "disks.h" #include "xms.h" #include "ipx.h" /* TRB - add support for ipx */ #include "serial.h" #include "int.h" #include "bitops.h" #include "pic.h" #include "dpmi.h" #include "priv.h" /* for priv_init */ #include "port.h" /* for port_init */ #include "pci.h" #include "speaker.h" #include "utilities.h" #include "dos2linux.h" #include "iodev.h" #include "mapping.h" #include "dosemu_config.h" #include "userhook.h" #include "pktdrvr.h" #include "dma.h" #include "keyb_server.h" #include "keyb_clients.h" #ifdef USE_SBEMU #include "sound.h" #endif #ifdef X86_EMULATOR #include "cpu-emu.h" #endif sigjmp_buf NotJEnv; void boot(void) { char *buffer; struct disk *dp = NULL; switch (config.hdiskboot) { case 0: if (config.bootdisk) dp = &bootdisk; else dp = &disktab[0]; break; case 1: dp = &hdisktab[0]; break; case 2: dp = &disktab[1]; break; default: error("unexpected value for config.hdiskboot\n"); leavedos(15); } disk_close(); disk_open(dp); buffer = LOWMEM(0x7c00); if (dp->boot_name) {/* Boot from the specified file */ int bfd; d_printf ("Booting from bootfile=%s...\n",dp->boot_name); bfd = open (dp->boot_name, O_RDONLY); if (bfd == -1) {/* Abort with error */ error("Boot file %s missing\n",dp->boot_name); leavedos(16); } if (read(bfd, buffer, SECTOR_SIZE) != SECTOR_SIZE) { error("Failed to read exactly %d bytes from %s\n", SECTOR_SIZE, dp->boot_name); leavedos(16); } close(bfd); } else if (dp->type == PARTITION) {/* we boot partition boot record, not MBR! */ d_printf("Booting partition boot record from part=%s....\n", dp->dev_name); if (RPT_SYSCALL(read(dp->fdesc, buffer, SECTOR_SIZE)) != SECTOR_SIZE) { error("reading partition boot sector using partition %s.\n", dp->dev_name); leavedos(16); } } else if (read_sectors(dp, buffer, 0, 0, 0, 1) != SECTOR_SIZE) { error("can't boot from %s, using harddisk\n", dp->dev_name); dp = hdisktab; if (read_sectors(dp, buffer, 0, 0, 0, 1) != SECTOR_SIZE) { error("can't boot from hard disk\n"); leavedos(16); } } disk_close(); } static inline void vm86plus_init(void) { #ifdef __i386__ if (!vm86_plus(VM86_PLUS_INSTALL_CHECK,0)) return; if (!syscall(SYS_vm86old, (void *)0xffffff01)) { fprintf(stderr, "your kernel contains an older (interim) vm86plus version\n\r" "please upgrade to an newer one\n\r"); } else #endif { #ifdef X86_EMULATOR warn("WARN: vm86plus service not available in your kernel\n"); warn("WARN: using CPU emulation for vm86()\n"); if (config.cpuemu < 3) { config.cpuemu = 3; } if (config.cpuemu < 3) config.cpuemu = 3; return; #else fprintf(stderr, "vm86plus service not available in your kernel\n\r"); #endif } fflush(stdout); fflush(stderr); _exit(1); } static inline void module_init(void) { vm86plus_init(); /* emumodule support */ memcheck_init(); /* lower 1M memory map support */ } void do_liability_disclaimer_prompt(int dosboot, int prompt) { FILE *f; char buf[32]; char *disclaimer_file_name; static char text[] = "\nWelcome to DOSEMU "VERSTR", a DOS emulator" #ifdef __linux__ " for Linux" #endif ".\nCopyright (C) 1992-2006 the 'DOSEMU-Development-Team'.\n" "This program is distributed in the hope that it will be useful,\n" "but WITHOUT ANY WARRANTY; without even the implied warranty of\n" "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the files\n" "COPYING.DOSEMU and COPYING for more details.\n" "Use this program at your own risk!\n\n"; static char text2[] = "Press ENTER to confirm, and boot DOSEMU, or [Ctrl-C] to abort\n"; disclaimer_file_name = assemble_path(LOCALDIR, "disclaimer", 0); if (exists_file(disclaimer_file_name)) { free(disclaimer_file_name); return; } if (dosboot) { p_dos_str(text); } else { fputs(text, stdout); } buf[0] = '\0'; if (prompt) { int size = 0; if (dosboot) { p_dos_str(text2); size = com_biosread(buf, sizeof(buf)-2); } else { fputs(text2, stdout); fgets(buf, sizeof(buf), stdin); } if (buf[0] == 3) { leavedos(1); } } /* * We now remember this by writing the above text to a * in LOCALDIR/disclaimer */ f = fopen(disclaimer_file_name, "w"); if (!f) { fprintf(stderr, "cannot create %s\n", disclaimer_file_name); leavedos(1); } fprintf(f, "%s%s\n", text, buf); fclose(f); free(disclaimer_file_name); } /* * DANG_BEGIN_FUNCTION emulate * * arguments: * argc - Argument count. * argv - Arguments. * * description: * Emulate gets called from dos.c. It initializes DOSEMU to * prepare it for running in vm86 mode. This involves catching signals, * preparing memory, calling all the initialization functions for the I/O * subsystems (video/serial/etc...), getting the boot sector instructions * and calling vm86(). * * DANG_END_FUNCTION * */ #ifdef __ELF__ int main(int argc, char **argv) #else int emulate(int argc, char **argv) #endif { char *signalstack[16384]; int e; srand(time(NULL)); memset(&config, 0, sizeof(config)); cstack = &signalstack; if ((e=sigsetjmp(NotJEnv, 1))) { flush_log(); fprintf(stderr,"EMERGENCY JUMP %x!!!\n",e); /* there's no other way to stop a signal 11 from hanging dosemu * but politely ask the kernel to terminate ourselves */ kill(0, SIGKILL); _exit(1); /* just in case */ } setlocale(LC_ALL,""); /* NOW! it is safe to touch the priv code. */ priv_init(); /* This must come first! */ /* Before we even try to give options to the parser, * we pre-filter some dangerous options and delete them * from the arguments list */ secure_option_preparse(&argc, argv); /* This has to come next: * Parse dosemu.users _before_ any argument usage to catch * a not allowed user playing with overruns and kick him out * Additionally, we check a non-suid root condition, if dosemu.user * says so and exit if needed. */ parse_dosemu_users(); /* the transposal of (config_|stdio_)init allows the addition of -o */ /* to specify a debug out filename, if you're wondering */ get_time_init(); /* debug can use CPUtime */ io_select_init(); port_init(); /* setup port structures, before config! */ version_init(); /* Check the OS version */ config_init(argc, argv); /* parse the commands & config file(s) */ #ifdef X86_EMULATOR #ifdef DONT_DEBUG_BOOT /* cpuemu only */ memcpy(&debug_save, &debug, sizeof(debug)); set_debug_level('e', 0); #ifdef TRACE_DPMI set_debug_level('t', 0); #endif #endif #endif get_time_init(); stdio_init(); /* initialize stdio & open debug file */ print_version(); /* log version information */ module_init(); time_setting_init(); /* get the startup time */ cpu_setup(); /* setup the CPU */ pci_setup(); device_init(); /* priv initialization of video etc. */ extra_port_init(); /* setup ports dependent on config */ SIG_init(); /* priv part of the signal init */ pkt_priv_init(); /* here we include the hooks to possible plug-ins */ #include "plugin_init.h" if (config.exitearly) { dbug_printf("Leaving DOS before booting\n"); leavedos(0); } if (can_do_root_stuff && !under_root_login) { g_printf("dropping root privileges\n"); open_kmem(); } priv_drop(); mapping_init(); /* initialize mapping drivers */ low_mem_init(); /* initialize the lower 1Meg */ map_hardware_ram(); /* map the direct hardware ram */ map_video_bios(); /* map (really: copy) the video bios */ close_kmem(); /* * Do the 'liability disclaimer' stuff we need to avoid problems * with laws in some countries. * * show it at the beginning only for terminals and direct console * else the banner code in int.c does it. */ if (!config.X) install_dos(0); HMA_init(); /* HMA can only be done now after mapping is initialized*/ memory_init(); /* initialize the memory contents */ /* iodev_init() can load plugins, like SDL, that can spawn a thread. * This must be done before initializing signals, or problems ensue. * This also must be done when the signals are blocked, so after * the io_select_init(), which right now blocks the signals. */ iodev_init(); /* initialize devices */ signal_init(); /* initialize sig's & sig handlers */ ems_init(); /* initialize ems */ xms_init(); /* initialize xms */ dpmi_setup(); if (not_use_sigio) k_printf("Atleast 1 NON-SIGIO file handle in use.\n"); else k_printf("No NON-SIGIO file handles in use.\n"); g_printf("EMULATE\n"); fflush(stdout); #ifdef USE_MHPDBG mhp_debug(DBG_INIT, 0, 0); #endif timer_interrupt_init(); /* start sending int 8h int signals */ /* remap conventional memory just before booting */ mmap_mapping(MAPPING_LOWMEM, 0, config.mem_size * 1024, PROT_READ | PROT_WRITE | PROT_EXEC, 0); while (!fatalerr && !config.exitearly) { loopstep_run_vm86(); } sync(); fprintf(stderr, "Not a good day to die!!!!!\n"); leavedos(99); return 0; /* just to make gcc happy */ } void dos_ctrl_alt_del(void) { dbug_printf("DOS ctrl-alt-del requested. Rebooting!\n"); while (in_dpmi) { in_dpmi_dos_int = 1; dpmi_cleanup(); } cpu_reset(); } /* "graceful" shutdown */ void leavedos(int sig) { struct itimerval itv; if (in_leavedos) { error("leavedos called recursively, forgetting the graceful exit!\n"); gdb_debug(); flush_log(); siglongjmp(NotJEnv, sig); } in_leavedos++; dbug_printf("leavedos(%d|0x%x) called - shutting down\n", sig, sig); #if 1 /* BUG CATCHER */ if (in_vm86) { g_printf("\nkilled while in vm86(), trying to dump DOS-registers:\n"); show_regs(__FILE__, __LINE__); } #endif in_vm86 = 0; /* try to notify dosdebug */ #ifdef USE_MHPDBG if (fault_cnt > 0) dbug_printf("leavedos() called from within a signal context!\n"); else mhp_exit_intercept(sig); g_printf("closing debugger pipes\n"); mhp_close(); #endif itv.it_interval.tv_sec = itv.it_interval.tv_usec = 0; itv.it_value = itv.it_interval; if (setitimer(ITIMER_REAL, &itv, NULL) == -1) { g_printf("can't turn off timer at shutdown: %s\n", strerror(errno)); } registersig(SIGALRM, NULL); /* here we include the hooks to possible plug-ins */ #include "plugin_close.h" if (config.speaker == SPKR_EMULATED) { g_printf("SPEAKER: sound off\n"); speaker_off(); /* turn off any sound */ } else if (config.speaker==SPKR_NATIVE) { g_printf("SPEAKER: sound off\n"); /* Since the speaker is native hardware use port manipulation, * we don't know what is actually implementing the kernel's * ioctls. * My port logic is actually stolen from kd_nosound in the kernel. * --EB 21 September 1997 */ if (portserver_pid) port_safe_outb(0x61, port_safe_inb(0x61)&0xFC); /* turn off any sound */ } #ifdef SIG g_printf("calling SIG_close\n"); #endif SIG_close(); /* try to regain control of keyboard and video first */ g_printf("calling keyboard_close\n"); iodev_term(); #if defined(X86_EMULATOR) /* if we are here with config.cpuemu>1 something went wrong... */ if (config.cpuemu>1) { leave_cpu_emu(); } #endif show_ints(0, 0x33); g_printf("calling disk_close_all\n"); disk_close_all(); if (config.emuretrace) { do_r3da_pending (); set_ioperm (0x3da, 1, 1); set_ioperm (0x3c0, 1, 1); config.emuretrace = 0; } /* terminate port server */ port_exit(); g_printf("releasing ports and blocked devices\n"); release_ports(); g_printf("calling shared memory exit\n"); g_printf("calling HMA exit\n"); hma_exit(); close_uhook(); g_printf("calling mapping_close()\n"); mapping_close(); if (config.detach) { restore_vt(config.detach); disallocate_vt(); } #ifdef IPX ipx_close(); #endif g_printf("calling close_all_printers\n"); close_all_printers(); flush_log(); _exit(sig); } #if 0 /* check the fd for data ready for reading */ int d_ready(int fd) { int selrtn; struct timeval w_time; fd_set checkset; w_time.tv_sec = 0; w_time.tv_usec = 200000; FD_ZERO(&checkset); FD_SET(fd, &checkset); do { selrtn = select(fd + 1, &checkset, NULL, NULL, &w_time); } while(selrtn == -1 && errno == EINTR); if (selrtn == 1) { if (FD_ISSET(fd, &checkset)) return (1); else return (0); } else return (0); } void activate(int con_num) { ioctl(kbd_fd, VT_ACTIVATE, con_num); } #endif void hardware_run(void) { dma_run (); #ifdef USE_SBEMU run_sb(); /* Beat Karcher to this one .. 8-) - AM */ #endif keyb_server_run(); rtc_run(); }