/* * (C) Copyright 1992, ..., 2007 the "DOSEMU-Development-Team". * * for details see file COPYING.DOSEMU in the DOSEMU distribution */ #include "config.h" #include #include #include #include #include #include #include #include #include #include #include "emu.h" #include "vm86plus.h" #include "bios.h" #include "mouse.h" #include "video.h" #include "vgaemu.h" #include "vgatext.h" #include "timers.h" #include "int.h" #include "dpmi.h" #include "pic.h" #include "ipx.h" #include "pktdrvr.h" #include "iodev.h" #include "serial.h" #include "debug.h" #include "dosemu_config.h" #include "keyb_clients.h" #include "keyb_server.h" #ifdef USE_SBEMU #include "sound.h" #endif #include "cpu-emu.h" /* Variables for keeping track of signals */ #define MAX_SIG_QUEUE_SIZE 50 static u_short SIGNAL_head=0; u_short SIGNAL_tail=0; struct SIGNAL_queue { /* struct sigcontext_struct context; */ void (* signal_handler)(void); }; static struct SIGNAL_queue signal_queue[MAX_SIG_QUEUE_SIZE]; static struct { unsigned long eflags; unsigned short fs, gs; #ifdef __x86_64__ unsigned char *fsbase, *gsbase; #define ARCH_SET_GS 0x1001 #define ARCH_SET_FS 0x1002 #define ARCH_GET_FS 0x1003 #define ARCH_GET_GS 0x1004 #endif } eflags_fs_gs; static void (*sighandlers[NSIG])(struct sigcontext *); static void sigquit(struct sigcontext *); static void sigalrm(struct sigcontext *); static void sigio(struct sigcontext *); static void sigasync(int sig, siginfo_t *si, void *uc); static void dosemu_sigaction_wrapper(int sig, void *fun, int flags) { struct sigaction sa; sigset_t mask; sa.sa_flags = flags; sigemptyset(&mask); addset_signals_that_queue(&mask); sa.sa_mask = mask; if (sa.sa_flags & SA_ONSTACK) { sa.sa_flags |= SA_SIGINFO; sa.sa_sigaction = fun; } else sa.sa_handler = fun; sigaction(sig, &sa, NULL); } /* DANG_BEGIN_FUNCTION NEWSETQSIG * * arguments: * sig - the signal to have a handler installed to. * fun - the signal handler function to install * * description: * All signals that wish to be handled properly in context with the * execution of vm86() mode, and signals that wish to use non-reentrant * functions should add themselves to the ADDSET_SIGNALS_THAT_QUEUE define * and use SETQSIG(). To that end they will also need to be set up in an * order such as SIGIO. * * DANG_END_FUNCTION * */ void addset_signals_that_queue(sigset_t *x) { sigaddset(x, SIGIO); sigaddset(x, SIGALRM); sigaddset(x, SIGPROF); sigaddset(x, SIGWINCH); sigaddset(x, SIG_RELEASE); sigaddset(x, SIG_ACQUIRE); } static void newsetqsig(int sig, void *fun) { dosemu_sigaction_wrapper(sig, fun, SA_RESTART|SA_ONSTACK); } void registersig(int sig, void (*fun)(struct sigcontext *)) { sighandlers[sig] = fun; } static void setsig(int sig, void *fun) { dosemu_sigaction_wrapper(sig, fun, SA_RESTART); } static void newsetsig(int sig, void *fun) { int flags = SA_RESTART|SA_ONSTACK; if (kernel_version_code >= 0x20600+14) flags |= SA_NODEFER; dosemu_sigaction_wrapper(sig, fun, flags); } #ifdef __x86_64__ static int dosemu_arch_prctl(int code, void *addr) { return syscall(SYS_arch_prctl, code, addr); } /* Check if fs or gs point to the base without always needing a syscall (12 vs. 800 CPU cycles last time I measured). The DPMI client code may have changed fs/gs and then restored it to 0, and that way the long base is gone (its base is still equal to the fs/gs base used by the DPMI client; 64bit code doesn't trap NULL selector references). There is a very small chance that the mov from %fs:0 page faults. In that case we fix it up in dosemu_fault0->check_fix_fs_gs_base. */ #define getfs0(byte) asm volatile ("movb %%fs:0, %0" : "=r"(byte)) #define getgs0(byte) asm volatile ("movb %%gs:0, %0" : "=r"(byte)) #define fix_fs_gs_base(seg,SEG) \ static void fix_##seg##base(void) \ { \ unsigned char segbyte, basebyte; \ \ /* always fix fsbase/gsbase the DPMI client changed fs or gs */ \ if (getsegment(seg) == eflags_fs_gs.seg) { \ volatile unsigned char *base = eflags_fs_gs.seg##base; \ \ /* if the two locations have different bytes they must be different */ \ get##seg##0(segbyte); \ basebyte = *base; \ if (segbyte == basebyte) { \ \ /* else we must modify one to make sure it's ok */ \ *base = basebyte + 1; \ get##seg##0(segbyte); \ *base = basebyte; \ if (segbyte != basebyte) \ return; \ } \ } \ dosemu_arch_prctl(ARCH_SET_##SEG, eflags_fs_gs.fsbase); \ D_printf("DPMI: Set " #seg "base in signal handler\n"); \ } fix_fs_gs_base(fs,FS); fix_fs_gs_base(gs,GS); /* this function is called from dosemu_fault0 to check if fsbase/gsbase need to be fixed up, if the above asm codes cause a page fault. */ int check_fix_fs_gs_base(unsigned char prefix) { unsigned char *addr, *base; int getcode, setcode; if (prefix == 0x65) { /* gs: */ getcode = ARCH_GET_GS; setcode = ARCH_SET_GS; base = eflags_fs_gs.gsbase; } else { getcode = ARCH_GET_FS; setcode = ARCH_SET_FS; base = eflags_fs_gs.fsbase; } if (dosemu_arch_prctl(getcode, &addr) != 0) return 0; /* already fine, not fixing it up, but then the dosemu fault is fatal */ if (addr == base) return 0; dosemu_arch_prctl(setcode, base); D_printf("DPMI: Fixed up %csbase in fault handler\n", prefix + 2); return 1; } #endif /* init_handler puts the handler in a sane state that glibc expects. That means restoring fs and gs for vm86 (necessary for 2.4 kernels) and fs, gs and eflags for DPMI. */ __attribute__((no_instrument_function)) void init_handler(struct sigcontext_struct *scp) { /* * FIRST thing to do in signal handlers - to avoid being trapped into int0x11 * forever, we must restore the eflags. */ loadflags(eflags_fs_gs.eflags); #ifdef __x86_64__ /* ds,es, and ss are ignored in 64-bit mode and not present or saved in the sigcontext, so we need to do it ourselves (using the 3 high words of the trapno field). fs and gs are set to 0 in the sigcontext, so we also need to save those ourselves */ if (scp) { _ds = getsegment(ds); _es = getsegment(es); _ss = getsegment(ss); _fs = getsegment(fs); _gs = getsegment(gs); if (config.cpuemu > 3) { _cs = getsegment(cs); } else if (_cs == 0) { if (config.dpmi) { fprintf(stderr, "Cannot run DPMI code natively "); if (kernel_version_code < 0x20600 + 15) fprintf(stderr, "because your Linux kernel is older than version 2.6.15.\n"); else fprintf(stderr, "for unknown reasons.\nPlease contact linux-msdos@vger.kernel.org.\n"); fprintf(stderr, "Set $_cpu_emu=\"full\" or \"fullsim\" to avoid this message.\n"); } config.cpuemu = 4; _cs = getsegment(cs); } } #endif if (in_vm86) { #ifdef __i386__ #ifdef X86_EMULATOR if (!config.cpuemu) #endif { if (getsegment(fs) != eflags_fs_gs.fs) loadregister(fs, eflags_fs_gs.fs); if (getsegment(gs) != eflags_fs_gs.gs) loadregister(gs, eflags_fs_gs.gs); } #endif return; } if (scp && !DPMIValidSelector(_cs)) return; /* else interrupting DPMI code with an LDT %cs */ /* restore %fs and %gs for compatibility with NPTL. */ #ifdef __x86_64__ if (eflags_fs_gs.fsbase) if (mem_base) /* too many faults in fix_fsbase() with the 0-page unmapped... */ dosemu_arch_prctl(ARCH_SET_FS, eflags_fs_gs.fsbase); else fix_fsbase(); else #endif if (getsegment(fs) != eflags_fs_gs.fs) loadregister(fs, eflags_fs_gs.fs); #ifdef __x86_64__ if (eflags_fs_gs.gsbase) if (mem_base) dosemu_arch_prctl(ARCH_SET_GS, eflags_fs_gs.gsbase); else fix_gsbase(); else #endif if (getsegment(gs) != eflags_fs_gs.gs) loadregister(gs, eflags_fs_gs.gs); } /* this cleaning up is necessary to avoid the port server becoming a zombie process */ __attribute__((no_instrument_function)) static void cleanup_child(struct sigcontext_struct *scp) { int status; init_handler(scp); if (portserver_pid && waitpid(portserver_pid, &status, WNOHANG) > 0 && WIFSIGNALED(status)) { error("port server terminated, exiting\n"); leavedos(1); } } __attribute__((no_instrument_function)) static void leavedos_signal(int sig) { init_handler(NULL); leavedos(sig); } /* Silly Interrupt Generator Initialization/Closedown */ #ifdef SIG SillyG_t *SillyG = 0; static SillyG_t SillyG_[16 + 1]; #endif /* * DANG_BEGIN_FUNCTION SIG_init * * description: Allow DOSEMU to be made aware when a hard interrupt occurs * The IRQ numbers to monitor are taken from config.sillyint, each bit * corresponding to one IRQ. The higher 16 bit are defining the use of * SIGIO * * DANG_END_FUNCTION */ void SIG_init(void) { #if defined(SIG) PRIV_SAVE_AREA /* Get in touch with Silly Interrupt Handling */ if (config.sillyint) { char prio_table[] = {8, 9, 10, 11, 12, 14, 15, 3, 4, 5, 6, 7}; int i, irq; SillyG_t *sg = SillyG_; for (i = 0; i < sizeof(prio_table); i++) { irq = prio_table[i]; if (config.sillyint & (1 << irq)) { int ret; enter_priv_on(); ret = vm86_plus(VM86_REQUEST_IRQ, (SIGIO << 8) | irq); leave_priv_setting(); if ( ret > 0) { g_printf("Gonna monitor the IRQ %d you requested\n", irq); sg->fd = -1; sg->irq = irq; g_printf("SIG: IRQ%d, enabling PIC-level %ld\n", irq, pic_irq_list[irq]); sg++; } } } sg->fd = 0; if (sg != SillyG_) SillyG = SillyG_; } #endif } void SIG_close(void) { #if defined(SIG) if (SillyG) { SillyG_t *sg = SillyG; while (sg->fd) { vm86_plus(VM86_FREE_IRQ, sg->irq); sg++; } g_printf("Closing all IRQ you opened!\n"); } #endif } /* DANG_BEGIN_FUNCTION signal_init * * description: * Initialize the signals to have NONE being blocked. * Currently this is NOT of much use to DOSEMU. * * DANG_END_FUNCTION * */ void signal_init(void) { sigset_t set; struct sigaction oldact; stack_t ss; ss.ss_sp = cstack; ss.ss_size = sizeof(*cstack); ss.ss_flags = SS_ONSTACK; sigaltstack(&ss, NULL); /* initialize user data & code selector values (used by DPMI code) */ /* And save %fs, %gs for NPTL */ eflags_fs_gs.fs = getsegment(fs); eflags_fs_gs.gs = getsegment(gs); eflags_fs_gs.eflags = getflags(); #ifdef __x86_64__ /* get long fs and gs bases. If they are in the first 32 bits normal 386-style fs/gs switching can happen so we can ignore fsbase/gsbase */ dosemu_arch_prctl(ARCH_GET_FS, &eflags_fs_gs.fsbase); if ((unsigned long)eflags_fs_gs.fsbase <= 0xffffffff) eflags_fs_gs.fsbase = 0; dosemu_arch_prctl(ARCH_GET_GS, &eflags_fs_gs.gsbase); if ((unsigned long)eflags_fs_gs.gsbase <= 0xffffffff) eflags_fs_gs.gsbase = 0; #endif /* init signal handlers - these are the defined signals: --------------------------------------------- SIGHUP 1 S leavedos SIGINT 2 S leavedos SIGQUIT 3 N sigquit SIGILL 4 N dosemu_fault SIGTRAP 5 N dosemu_fault SIGABRT 6 S leavedos SIGBUS 7 N dosemu_fault SIGFPE 8 N dosemu_fault SIGKILL 9 na SIGUSR1 10 NQ (SIG_RELEASE)sigasync SIGSEGV 11 N dosemu_fault SIGUSR2 12 NQ (SIG_ACQUIRE)sigasync SIGPIPE 13 S SIG_IGN SIGALRM 14 NQ (SIG_TIME)sigasync SIGTERM 15 S leavedos SIGSTKFLT 16 SIGCHLD 17 N cleanup_child SIGCONT 18 SIGSTOP 19 SIGTSTP 20 SIGTTIN 21 unused was SIG_SER?? SIGTTOU 22 SIGURG 23 SIGXCPU 24 SIGXFSZ 25 SIGVTALRM 26 SIGPROF 27 NQ (cpuemu,sigprof)sigasync SIGWINCH 28 NQ (sigwinch)sigasync SIGIO 29 NQ (sigio)sigasync SIGPWR 30 SIGUNUSED 31 na ------------------------------------------------ */ newsetsig(SIGILL, dosemu_fault); newsetqsig(SIGALRM, sigasync); registersig(SIGALRM, sigalrm); newsetsig(SIGFPE, dosemu_fault); newsetsig(SIGTRAP, dosemu_fault); #ifdef SIGBUS /* for newer kernels */ newsetsig(SIGBUS, dosemu_fault); #endif setsig(SIGINT, leavedos_signal); /* for "graceful" shutdown for ^C too*/ setsig(SIGHUP, leavedos_signal); /* for "graceful" shutdown */ setsig(SIGTERM, leavedos_signal); #if 0 /* Richard Stevens says it can't be caught. It's returning an * error anyway */ setsig(SIGKILL, leavedos_signal); #endif newsetsig(SIGQUIT, sigasync); registersig(SIGQUIT, sigquit); setsig(SIGPIPE, SIG_IGN); /* setsig(SIGUNUSED, timint); */ newsetqsig(SIGIO, sigasync); registersig(SIGIO, sigio); newsetqsig(SIGUSR1, sigasync); newsetqsig(SIGUSR2, sigasync); sigaction(SIGPROF, NULL, &oldact); /* don't set SIGPROF if already used via -pg */ if (oldact.sa_handler == SIG_DFL) newsetqsig(SIGPROF, sigasync); newsetqsig(SIGWINCH, sigasync); newsetsig(SIGSEGV, dosemu_fault); newsetsig(SIGCHLD, sigasync); registersig(SIGCHLD, cleanup_child); /* unblock SIGIO, SIGALRM, SIG_ACQUIRE, SIG_RELEASE */ sigemptyset(&set); addset_signals_that_queue(&set); sigprocmask(SIG_UNBLOCK, &set, NULL); } /* * DANG_BEGIN_FUNCTION handle_signals * * description: * Due to signals happening at any time, the actual work to be done * because a signal occurs is done here in a serial fashion. * * The concept, should this eventualy work, is that a signal should only * flag that it has occurred and let DOSEMU deal with it in an orderly * fashion as it executes the rest of it's code. * * DANG_END_FUNCTION * */ static void handle_signals_force(int force_reentry) { static int in_handle_signals = 0; void (*signal_handler)(void); if (in_handle_signals++ && !force_reentry) error("BUG: handle_signals() re-entered!\n"); if ( SIGNAL_head != SIGNAL_tail ) { #ifdef X86_EMULATOR if ((config.cpuemu>1) && (debug_level('e')>3)) {e_printf("EMU86: SIGNAL at %d\n",SIGNAL_head);} #endif signal_pending = 0; signal_handler = signal_queue[SIGNAL_head].signal_handler; SIGNAL_head = (SIGNAL_head + 1) % MAX_SIG_QUEUE_SIZE; signal_handler(); /* * If more SIGNALS need to be dealt with, make sure we request interruption * by the kernel ASAP. */ if (SIGNAL_head != SIGNAL_tail) { signal_pending = 1; } } if (signal_pending) { dpmi_return_request(); } in_handle_signals--; } void handle_signals(void) { handle_signals_force(0); } void handle_signals_force_reentry(void) { handle_signals_force(1); } /* ============================================================== * * This is called by default at around 100Hz. * (see timer_interrupt_init() in init.c) * * The actual formulas, starting with the configurable parameter * config.freq, are: * config.freq default=18 * config.update = 1E6/config.freq default=54945 * timer tick(us) = config.update/6 default=9157.5us * = 166667/config.freq * timer tick(Hz) = 6*config.freq default=100Hz * * 6 is the magical TIMER_DIVISOR macro used to get 100Hz * * This call should NOT be used if you need timing accuracy - many * signals can get lost e.g. when kernel accesses disk, and the whole * idea of timing-by-counting is plain wrong. We'll need the Pentium * counter here. * ============================================================== */ static void SIGALRM_call(void) { static int first = 0; static hitimer_t cnt200 = 0; static hitimer_t cnt1000 = 0; static volatile int running = 0; #if VIDEO_CHECK_DIRTY static int update_pending = 0; #endif int retval; if (first==0) { cnt200 = cnt1000 = pic_sys_time; /* initialize */ first = 1; } /* update mouse cursor before updating the screen */ mouse_curtick(); /* If it is running in termcap mode, then update the screen. * First it sets a running flag, so as to avoid re-entrancy of * update_screen while it is in use. After update_screen is done, * it returns a nonzero value if there was any updates to the screen. * If there were any updates to the screen, then set a countdown value * in order to give DOSEMU more CPU time, between screen updates. * This increases the DOSEMU-to-termcap update efficiency greatly. * The countdown counter is currently at a value of 2. */ /* This now (again) tests screen_bitmap, i.e. checks if the screen * was written to at all. This doesn't seem to achieve much for now, * but it will be helpful when implementing X graphics. * It's a bit tricky, however, because previous calls of update_screen * might not have updated the entire screen. Therefore update_pending * is set to 1 if only part of the screen was updated (update_screen * returns 2), meaning that update_screen will in any case be called * next time. * (*** this only applies if VIDEO_CHECK_DIRTY is set, which is * currently not the default! ***) * * return vales for update_screen are now: * 0 nothing changed * 1 changed, entire screen updated * 2 changed, only partially updated * * note that update_screen also updates the cursor. */ if (Video->update_screen && config.X_blinkrate) { blink_cursor(); } if (!running) { if (Video->update_screen #if VIDEO_CHECK_DIRTY && (update_pending || vm86s.screen_bitmap&screen_mask) #endif ) { running = -1; retval = Video->update_screen(); #if 0 v_printf("update_screen returned %d\n",retval); #endif running = retval ? (config.X?config.X_updatefreq:config.term_updatefreq) : 0; #if VIDEO_CHECK_DIRTY update_pending=(retval==2); vm86s.screen_bitmap=0; #endif } else if (Video->update_cursor) { v_printf("update cursor\n"); Video->update_cursor(); } } else if (running > 0) { running--; } if (Video->handle_events) Video->handle_events(); /* for the SLang terminal we'll delay the release of shift, ctrl, ... keystrokes a bit */ /* although actually the event handler handles the keyboard in X, keyb_client_run * still needs to be called in order to handle pasting. */ if (!config.console_keyb) keyb_client_run(); #ifdef USE_SBEMU /* This is a macro */ run_sb(); #endif serial_run(); /* TRB - perform processing for the IPX Asynchronous Event Service */ #ifdef IPX if (config.ipxsup) AESTimerTick(); #endif if (config.rdtsc) update_cputime_TSCBase(); timer_tick(); #if 0 /* * DANG_BEGIN_REMARK * Check for keyboard coming from client * For now, first byte is interrupt requests from Client * DANG_END_REMARK */ if (*(u_char *)(shared_qf_memory + CLIENT_REQUEST_FLAG_AREA) & 0x40) { k_printf("KBD: Client sent key\n"); pic_request (PIC_IRQ1); *(u_char *)(shared_qf_memory + CLIENT_REQUEST_FLAG_AREA) &= ~0x40; } #endif io_select(fds_sigio); /* we need this in order to catch lost SIGIOs */ if (not_use_sigio) io_select(fds_no_sigio); alarm_idle(); /* Here we 'type in' prestrokes from commandline, as long as there are any * Were won't overkill dosemu, hence we type at a speed of 14cps */ if (config.pre_stroke) { static int count=-1; if (--count < 0) { count = type_in_pre_strokes(); if (count <0) count =7; /* with HZ=100 we have a stroke rate of 14cps */ } } /* this should be for per-second activities, it is actually at * 200ms more or less (PARTIALS=5) */ if ((pic_sys_time-cnt200) >= (PIT_TICK_RATE/PARTIALS)) { cnt200 = pic_sys_time; /* g_printf("**** ALRM: %dms\n",(1000/PARTIALS)); */ printer_tick(0); if (config.fastfloppy) floppy_tick(); } /* We update the RTC from here if it has not been defined as a thread */ /* this is for EXACT per-second activities (can produce bursts) */ if ((pic_sys_time-cnt1000) >= PIT_TICK_RATE) { cnt1000 += PIT_TICK_RATE; /* g_printf("**** ALRM: 1sec\n"); */ rtc_update(); } } /* DANG_BEGIN_FUNCTION SIGNAL_save * * arguments: * context - signal context to save. * signal_call - signal handling routine to be called. * * description: * Save into an array structure queue the signal context of the current * signal as well as the function to call for dealing with this signal. * This is a queue because any signal may occur multiple times before * DOSEMU deals with it down the road. * * DANG_END_FUNCTION * */ inline void SIGNAL_save( void (*signal_call)(void) ) { signal_queue[SIGNAL_tail].signal_handler=signal_call; SIGNAL_tail = (SIGNAL_tail + 1) % MAX_SIG_QUEUE_SIZE; signal_pending = 1; if (in_dpmi) dpmi_return_request(); } /* * DANG_BEGIN_FUNCTION SIGIO_call * * description: * Whenever I/O occurs on devices allowing SIGIO to occur, DOSEMU * will be flagged to run this call which inturn checks which * fd(s) was set and execute the proper routine to get the I/O * from that device. * * DANG_END_FUNCTION * */ static void SIGIO_call(void){ /* Call select to see if any I/O is ready on devices */ io_select(fds_sigio); } #ifdef __linux__ static void sigio(struct sigcontext_struct *scp) { SIGNAL_save(SIGIO_call); if (in_dpmi && !in_vm86) dpmi_sigio(scp); } static void sigalrm(struct sigcontext_struct *scp) { if(e_gen_sigalrm(scp)) { SIGNAL_save(SIGALRM_call); if (in_dpmi && !in_vm86) dpmi_sigio(scp); } } __attribute__((no_instrument_function)) static void sigasync0(int sig, struct sigcontext_struct *scp) { init_handler(scp); if (sighandlers[sig]) sighandlers[sig](scp); dpmi_iret_setup(scp); } __attribute__((no_instrument_function)) static void sigasync(int sig, siginfo_t *si, void *uc) { sigasync0(sig, (struct sigcontext_struct *) &((ucontext_t *)uc)->uc_mcontext); } #endif static void sigquit(struct sigcontext_struct *scp) { in_vm86 = 0; error("sigquit called\n"); show_ints(0, 0x33); show_regs(__FILE__, __LINE__); WRITE_BYTE(BIOS_KEYBOARD_FLAGS, 0x80); /* ctrl-break flag */ do_soft_int(0x1b); }