[Dosemu-cvs] SF.net SVN: dosemu: [1627] trunk/src
Brought to you by:
bartoldeman
From: <bar...@us...> - 2006-10-22 16:20:09
|
Revision: 1627 http://svn.sourceforge.net/dosemu/?rev=1627&view=rev Author: bartoldeman Date: 2006-10-22 09:19:56 -0700 (Sun, 22 Oct 2006) Log Message: ----------- Replaced tls_setup hack with something just a little cleaner. The 64bit fs and gs bases are restored in signal handlers. To avoid an expensive syscall it is first checked if the bases really need to be set by comparing the memory fs/gs and the original base point to. There is a very small chance that that involves a page fault, which needs to be fixed up. Modified Paths: -------------- trunk/src/arch/linux/async/signal.c trunk/src/arch/linux/async/sigsegv.c trunk/src/emu-i386/cpu.c trunk/src/include/emu.h Modified: trunk/src/arch/linux/async/signal.c =================================================================== --- trunk/src/arch/linux/async/signal.c 2006-10-22 08:55:06 UTC (rev 1626) +++ trunk/src/arch/linux/async/signal.c 2006-10-22 16:19:56 UTC (rev 1627) @@ -60,6 +60,13 @@ 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; /* DANG_BEGIN_REMARK @@ -230,6 +237,87 @@ 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. */ @@ -262,8 +350,10 @@ if (!config.cpuemu) #endif { - loadregister(fs, eflags_fs_gs.fs); - loadregister(gs, eflags_fs_gs.gs); + 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; @@ -271,12 +361,22 @@ if (scp && _cs == getsegment(cs)) return; - /* else interrupting DPMI code with an LDT %cs*/ + /* else interrupting DPMI code with an LDT %cs */ - /* restore %fs and %gs for compatibility with NPTL. - check to avoid clobbering 64-bit base of fs/gs */ + /* restore %fs and %gs for compatibility with NPTL. */ +#ifdef __x86_64__ + if (eflags_fs_gs.fsbase) + 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) + fix_gsbase(); + else +#endif if (getsegment(gs) != eflags_fs_gs.gs) loadregister(gs, eflags_fs_gs.gs); } @@ -397,6 +497,17 @@ 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: --------------------------------------------- Modified: trunk/src/arch/linux/async/sigsegv.c =================================================================== --- trunk/src/arch/linux/async/sigsegv.c 2006-10-22 08:55:06 UTC (rev 1626) +++ trunk/src/arch/linux/async/sigsegv.c 2006-10-22 16:19:56 UTC (rev 1627) @@ -382,6 +382,20 @@ sigprocmask(SIG_UNBLOCK, &set, NULL); } +#ifdef __x86_64__ + /* see comment in signal.c, fix_fs_gs_base() */ + if (_trapno == 0xe && getsegment(cs) == _cs) { + unsigned char *rip = (unsigned char *)_rip; + /* page fault for fs: or gs: */ + if (*rip == 0x64 || *rip == 0x65) { + if (check_fix_fs_gs_base(*rip)) { + fault_cnt--; + return; + } + } + } +#endif + if (debug_level('g')>7) g_printf("Entering fault handler, signal=%i _trapno=0x%X\n", signal, _trapno); Modified: trunk/src/emu-i386/cpu.c =================================================================== --- trunk/src/emu-i386/cpu.c 2006-10-22 08:55:06 UTC (rev 1626) +++ trunk/src/emu-i386/cpu.c 2006-10-22 16:19:56 UTC (rev 1627) @@ -28,11 +28,6 @@ #include <limits.h> #include <sys/time.h> #include <errno.h> -#ifdef __x86_64__ -#include <sys/mman.h> -#include <sys/syscall.h> -#include "Linux/mman.h" -#endif #include "config.h" #include "dosemu_config.h" @@ -239,42 +234,6 @@ REG(eflags) = 0; } -#ifdef __x86_64__ -static void tls_setup(void) -{ - unsigned long fsbase, pagesize; - unsigned char *oldtls, *newtls; - - /* This is a very ugly hack to alias map the TLS area to the first 4GB of - address space, and point fs to this area. - Otherwise any %fs change will invalidate the base */ - /* FIXME: find out a better way than the page size to obtain the TLS size */ - -#ifndef MREMAP_FIXED -#define MREMAP_FIXED 1 -#endif -#define ARCH_SET_FS 0x1002 -#define ARCH_GET_FS 0x1003 - - if (syscall(SYS_arch_prctl, ARCH_GET_FS, &fsbase) == 0 - && fsbase > 0xffffffff) { - pagesize = sysconf(_SC_PAGESIZE); - oldtls = (unsigned char *)(fsbase & ~(pagesize - 1)); - newtls = mmap(NULL, pagesize, PROT_READ|PROT_WRITE, - MAP_ANONYMOUS|MAP_PRIVATE|MAP_32BIT, 0, 0); - memcpy(newtls, oldtls, pagesize); - mmap(oldtls, pagesize, PROT_READ|PROT_WRITE, - MAP_ANONYMOUS|MAP_SHARED|MAP_FIXED, 0, 0); - memcpy(oldtls, newtls, pagesize); - syscall(SYS_mremap, oldtls, 0, pagesize, MREMAP_MAYMOVE|MREMAP_FIXED, - newtls); - syscall(SYS_arch_prctl, ARCH_SET_FS, fsbase + newtls - oldtls); - } -} -#else -#define tls_setup() -#endif - /* * DANG_BEGIN_FUNCTION cpu_setup * @@ -295,7 +254,6 @@ cpu_reset(); - tls_setup(); savefpstate(vm86_fpu_state); #ifdef __x86_64__ stk_ptr = getregister(rsp); Modified: trunk/src/include/emu.h =================================================================== --- trunk/src/include/emu.h 2006-10-22 08:55:06 UTC (rev 1626) +++ trunk/src/include/emu.h 2006-10-22 16:19:56 UTC (rev 1627) @@ -407,6 +407,9 @@ extern void setsig(int sig, void *handler); extern void newsetsig(int sig, void *handler); extern void init_handler(struct sigcontext_struct *scp); +#ifdef __x86_64__ +extern int check_fix_fs_gs_base(unsigned char prefix); +#endif /* * DANG_BEGIN_REMARK This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |