/*************************************************************************** * * All modifications in this file to the original code are * (C) Copyright 1992, ..., 2014 the "DOSEMU-Development-Team". * * for details see file COPYING in the DOSEMU distribution * * * SIMX86 a Intel 80x86 cpu emulator * Copyright (C) 1997,2001 Alberto Vignani, FIAT Research Center * a.vignani@crf.it * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * Additional copyright notes: * * 1. The kernel-level vm86 handling was taken out of the Linux kernel * (linux/arch/i386/kernel/vm86.c). This code originaly was written by * Linus Torvalds with later enhancements by Lutz Molgedey and Hans Lermen. * ***************************************************************************/ #include #include #include #include #include #include #include "mapping.h" #include "dosemu_debug.h" #include "dlmalloc.h" #include "emu86.h" #include "trees.h" #include "codegen.h" #include "dpmi.h" #define CGRAN 0 /* 2^n */ #define CGRMASK (0xfffff>>CGRAN) #ifndef UINT64_WIDTH #define UINT64_WIDTH 64 #endif typedef struct _mpmap { struct _mpmap *next; int mega; unsigned char pagemap[32]; /* (32*8)=256 pages *4096 = 1M */ uint64_t subpage[(0x100000>>CGRAN)/UINT64_WIDTH]; /* 2^CGRAN-byte granularity, 1M/2^CGRAN bits */ } tMpMap; static tMpMap *MpH = NULL; unsigned int mMaxMem = 0; int PageFaults = 0; static tMpMap *LastMp = NULL; static int e_munprotect(unsigned int addr, size_t len); ///////////////////////////////////////////////////////////////////////////// static inline tMpMap *FindM(unsigned int addr) { register int a2l = addr >> (PAGE_SHIFT+8); register tMpMap *M = LastMp; if (M && (M->mega==a2l)) return M; M = MpH; while (M) { if (M->mega==a2l) { LastMp = M; break; } M = M->next; } return M; } static int AddMpMap(unsigned int addr, unsigned int aend, int onoff) { int bs=0, bp=0; register int page; tMpMap *M; do { page = addr >> PAGE_SHIFT; M = MpH; while (M) { if (M->mega==(page>>8)) break; M = M->next; } if (M==NULL) { M = (tMpMap *)calloc(1,sizeof(tMpMap)); M->next = MpH; MpH = M; M->mega = (page>>8); } if (bp < 32) { bs |= (((unsigned)(onoff? set_bit(page&255, M->pagemap) : clear_bit(page&255, M->pagemap)) & 1) << bp); bp++; } if (debug_level('e')>1) { if (addr > mMaxMem) mMaxMem = addr; if (onoff) dbug_printf("MPMAP: protect page=%08x was %x\n",addr,bs); else dbug_printf("MPMAP: unprotect page=%08x was %x\n",addr,bs); } addr += PAGE_SIZE; } while (addr <= aend); return bs; } static inline int e_querymprot(unsigned int addr) { register int a2 = addr >> PAGE_SHIFT; tMpMap *M = FindM(addr); if (M==NULL) return 0; return test_bit(a2&255, M->pagemap); } int e_querymprotrange(unsigned int addr, size_t len) { int a2l, a2h; tMpMap *M = FindM(addr); a2l = addr >> PAGE_SHIFT; a2h = (addr+len-1) >> PAGE_SHIFT; while (M && a2l <= a2h) { if (test_bit(a2l&255, M->pagemap)) return 1; a2l++; if ((a2l&255)==0) M = M->next; } return 0; } ///////////////////////////////////////////////////////////////////////////// int e_markpage(unsigned int addr, size_t len) { unsigned int abeg, aend; tMpMap *M = FindM(addr); if (M == NULL || len == 0) return 0; abeg = addr >> CGRAN; aend = (addr+len-1) >> CGRAN; if (debug_level('e')>1) dbug_printf("MARK from %08x to %08x for %08x\n", abeg<subpage); abeg++; if ((abeg&CGRMASK) == 0) M = M->next; } return 1; } int e_unmarkpage(unsigned int addr, size_t len) { unsigned int abeg, aend; tMpMap *M = FindM(addr); if (M == NULL || len == 0) return 0; abeg = addr >> CGRAN; aend = (addr+len-1) >> CGRAN; if (debug_level('e')>1) dbug_printf("UNMARK from %08x to %08x for %08x\n", abeg<subpage); abeg++; if ((abeg&CGRMASK) == 0) M = M->next; } /* check if unmarked pages have no more code, and if so, unprotect */ abeg = addr & PAGE_MASK; aend = (addr + len) & PAGE_MASK; /* don't unprotect partial first page with code (if not also last) */ if (aend != abeg && abeg != addr && e_querymark(abeg, PAGE_SIZE)) abeg += PAGE_SIZE; /* unprotect partial last page without code */ if (aend != addr+len && !e_querymark(aend, PAGE_SIZE)) aend += PAGE_SIZE; if (aend > abeg) e_munprotect(abeg, aend - abeg); return 1; } int e_querymark(unsigned int addr, size_t len) { unsigned int abeg, aend, idx; tMpMap *M = FindM(addr); uint64_t mask; if (M == NULL) return 0; abeg = addr >> CGRAN; aend = ((addr+len-1) >> CGRAN) + 1; if (debug_level('e')>2) dbug_printf("QUERY MARK from %08x to %08x for %08x\n", abeg<subpage)) goto found; return 0; } idx = (abeg&CGRMASK) / UINT64_WIDTH; // mask for first partial longword mask = ~0ULL << (abeg & (UINT64_WIDTH-1)); while (abeg < (aend & ~(UINT64_WIDTH-1))) { if (M->subpage[idx] & mask) goto found; abeg = (abeg + UINT64_WIDTH) & ~(UINT64_WIDTH-1); idx++; mask = ~0ULL; if (idx == sizeof(M->subpage)/sizeof(M->subpage[0])) { M = M->next; idx = 0; } } if (aend & (UINT64_WIDTH-1)) { // mask for last partial longword mask &= ~0ULL >> (UINT64_WIDTH - (aend & (UINT64_WIDTH-1))); if (M->subpage[idx] & mask) goto found; } return 0; found: if (debug_level('e')>1) { if (len > 1) abeg += ffsll(M->subpage[idx] & mask) - 1; dbug_printf("QUERY MARK found code at " "%08x to %08x for %08x\n", abeg<> CGRAN; aend = (addr+len-1) >> CGRAN; while (M && abeg <= aend) { if (!test_bit(abeg&CGRMASK, M->subpage)) return 0; abeg++; if ((abeg&CGRMASK) == 0) M = M->next; } return 1; } ///////////////////////////////////////////////////////////////////////////// int e_mprotect(unsigned int addr, size_t len) { int e; unsigned int abeg, aend, aend1; unsigned int abeg1 = (unsigned)-1; unsigned a; int ret = 1; abeg = addr & PAGE_MASK; if (len==0) { return 0; } else { aend = (addr+len-1) & PAGE_MASK; } /* only protect ranges that were not already protected by e_mprotect */ for (a = abeg; a <= aend; a += PAGE_SIZE) { int qp = e_querymprot(a); if (!qp) { if (abeg1 == (unsigned)-1) abeg1 = a; aend1 = a; } if ((a == aend || qp) && abeg1 != (unsigned)-1) { e = mprotect_mapping(MAPPING_CPUEMU, abeg1, aend1-abeg1+PAGE_SIZE, PROT_READ|PROT_EXEC); if (e<0) { e_printf("MPMAP: %s\n",strerror(errno)); return -1; } ret = AddMpMap(abeg1, aend1+PAGE_SIZE-1, 1); abeg1 = (unsigned)-1; } } return ret; } static int e_munprotect(unsigned int addr, size_t len) { int e; unsigned int abeg, aend, aend1; unsigned int abeg1 = (unsigned)-1; unsigned a; int ret = 0; abeg = addr & PAGE_MASK; if (len==0) { aend = abeg; } else { aend = (addr+len-1) & PAGE_MASK; } /* only unprotect ranges that were protected by e_mprotect */ for (a = abeg; a <= aend; a += PAGE_SIZE) { int qp = e_querymprot(a); if (qp) { if (abeg1 == (unsigned)-1) abeg1 = a; aend1 = a; } if ((a == aend || !qp) && abeg1 != (unsigned)-1) { e = mprotect_mapping(MAPPING_CPUEMU, abeg1, aend1-abeg1+PAGE_SIZE, PROT_READ|PROT_WRITE|PROT_EXEC); if (e<0) { e_printf("MPUNMAP: %s\n",strerror(errno)); return -1; } ret = AddMpMap(abeg1, aend1+PAGE_SIZE-1, 0); abeg1 = (unsigned)-1; } } return ret; } #ifdef HOST_ARCH_X86 int e_handle_pagefault(dosaddr_t addr, unsigned err, sigcontext_t *scp) { register int v; unsigned char *p; int in_dosemu; /* err: * bit 0 = 1 page protect * bit 1 = 1 writing * bit 2 = 1 user mode * bit 3 = 0 no reserved bit err */ if ((err & 0x06) != 0x06) return 0; /* additionally check if page is not mapped */ if (addr >= LOWMEM_SIZE + HMASIZE && /* can't fully trust err as page may be swapped out */ !(err & 1) && !dpmi_read_access(addr)) return 0; if (!e_querymprot(addr)) return 0; /* Got a fault in a write-protected memory page, that is, * a page _containing_code_. 99% of the time we are * hitting data or stack in the same page, NOT code. * * We check using e_querymprot whether we protected the * page ourselves. Additionally an error code of 7 should * have been given. * * _cr2 keeps the address where the code tries to write * _rip keeps the address of the faulting instruction * (in the code buffer or in the tree) * * The faults can also come from * - native/KVM DPMI code if only vm86() is emulated * - native/KVM vm86 code if only DPMI is emulated * - DOSEMU itself (to be avoided) * * Possible instructions we'll find here are (see sigsegv.v): * 8807 movb %%al,(%%edi) * (66)8907 mov{wl} %%{e}ax,(%%edi) * (f3)(66)a4,a5 movs * (f3)(66)aa,ab stos */ #ifdef PROFILE if (debug_level('e')) PageFaults++; #endif in_dosemu = !(InCompiledCode || in_vm86 || DPMIValidSelector(_cs)); if (in_vm86) p = SEG_ADR((unsigned char *), cs, ip); else if (DPMIValidSelector(_cs)) p = (unsigned char *)MEM_BASE32(GetSegmentBase(_cs) + _rip); else p = (unsigned char *) _rip; if (debug_level('e')>1 || in_dosemu) { v = *((int *)p); __asm__("bswap %0" : "=r" (v) : "0" (v)); e_printf("Faulting ops: %08x\n",v); if (!InCompiledCode) { unsigned int cs = in_vm86 ? _CS : _cs; greg_t eip = in_vm86 ? _IP : _rip; e_printf("*\tFault out of %scode, cs:eip=%x:%llx," " cr2=%x, fault_cnt=%d\n", in_dosemu ? "DOSEMU " : "", cs, (unsigned long long) eip, addr, fault_cnt); } if (e_querymark(addr, 1)) { e_printf("CODE node hit at %08x\n",addr); } else if (InCompiledCode) { e_printf("DATA node hit at %08x\n",addr); } } /* the page is not unprotected here, the code * linked by Cpatch will do it */ /* ACH: we can set up a data patch for code * which has not yet been executed! */ #ifndef SKIP_CPATCH if (InCompiledCode && Cpatch(scp)) return 1; #endif /* We HAVE to invalidate all the code in the page * if the page is going to be unprotected */ addr &= PAGE_MASK; return InvalidateNodeRange(addr, PAGE_SIZE, p); } int e_handle_fault(sigcontext_t *scp) { if (!InCompiledCode) return 0; /* page-faults are handled not here and only DE remains */ if (_trapno != 0) { error("Fault %i in jit-compiled code\n", _trapno); return 0; } TheCPU.err = EXCP00_DIVZ + _trapno; _eax = TheCPU.cr2; _edx = _eflags; TheCPU.cr2 = _cr2; _rip = *(long *)_rsp; _rsp += sizeof(long); return 1; } #endif ///////////////////////////////////////////////////////////////////////////// void mprot_init(void) { MpH = NULL; AddMpMap(0,0,0); /* first mega in first entry */ PageFaults = 0; } void mprot_end(void) { tMpMap *M = MpH; int i; unsigned char b; while (M) { tMpMap *M2 = M; for (i=0; i<32; i++) if ((b=M->pagemap[i])) { unsigned int addr = (M->mega<<20) | (i<<15); while (b) { if (b & 1) { if (debug_level('e')>1) dbug_printf("MP_END %08x = RWX\n",addr); mprotect_mapping(MAPPING_CPUEMU, addr, PAGE_SIZE, PROT_READ|PROT_WRITE|PROT_EXEC); } addr += PAGE_SIZE; b >>= 1; } } M = M->next; free(M2); } MpH = LastMp = NULL; } /////////////////////////////////////////////////////////////////////////////