DOSBox-X
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Defines
src/cpu/core_dynrec.cpp
00001 /*
00002  *  Copyright (C) 2002-2020  The DOSBox Team
00003  *
00004  *  This program is free software; you can redistribute it and/or modify
00005  *  it under the terms of the GNU General Public License as published by
00006  *  the Free Software Foundation; either version 2 of the License, or
00007  *  (at your option) any later version.
00008  *
00009  *  This program is distributed in the hope that it will be useful,
00010  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00011  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012  *  GNU General Public License for more details.
00013  *
00014  *  You should have received a copy of the GNU General Public License along
00015  *  with this program; if not, write to the Free Software Foundation, Inc.,
00016  *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00017  */
00018 
00019 
00020 #include "dosbox.h"
00021 
00022 #if (C_DYNREC)
00023 
00024 #include <assert.h>
00025 #include <stdarg.h>
00026 #include <stdio.h>
00027 #include <string.h>
00028 #include <stddef.h>
00029 #include <stdlib.h>
00030 
00031 #if defined (WIN32)
00032 #include <windows.h>
00033 #include <winbase.h>
00034 #endif
00035 
00036 #if (C_HAVE_MPROTECT)
00037 #include <sys/mman.h>
00038 
00039 #include <limits.h>
00040 #ifndef PAGESIZE
00041 #define PAGESIZE 4096
00042 #endif
00043 #endif /* C_HAVE_MPROTECT */
00044 
00045 #include "callback.h"
00046 #include "regs.h"
00047 #include "mem.h"
00048 #include "cpu.h"
00049 #include "debug.h"
00050 #include "paging.h"
00051 #include "inout.h"
00052 #include "lazyflags.h"
00053 #include "pic.h"
00054 
00055 #define CACHE_MAXSIZE   (4096*2)
00056 #define CACHE_TOTAL             (1024*1024*8)
00057 #define CACHE_PAGES             (512)
00058 #define CACHE_BLOCKS    (128*1024)
00059 #define CACHE_ALIGN             (16)
00060 #define DYN_HASH_SHIFT  (4)
00061 #define DYN_PAGE_HASH   (4096>>DYN_HASH_SHIFT)
00062 #define DYN_LINKS               (16)
00063 
00064 
00065 //#define DYN_LOG 1 //Turn Logging on.
00066 
00067 
00068 #if C_FPU
00069 #define CPU_FPU 1                                               //Enable FPU escape instructions
00070 #endif
00071 
00072 
00073 // the emulated x86 registers
00074 #define DRC_REG_EAX 0
00075 #define DRC_REG_ECX 1
00076 #define DRC_REG_EDX 2
00077 #define DRC_REG_EBX 3
00078 #define DRC_REG_ESP 4
00079 #define DRC_REG_EBP 5
00080 #define DRC_REG_ESI 6
00081 #define DRC_REG_EDI 7
00082 
00083 // the emulated x86 segment registers
00084 #define DRC_SEG_ES 0
00085 #define DRC_SEG_CS 1
00086 #define DRC_SEG_SS 2
00087 #define DRC_SEG_DS 3
00088 #define DRC_SEG_FS 4
00089 #define DRC_SEG_GS 5
00090 
00091 
00092 // access to a general register
00093 #define DRCD_REG_VAL(reg) (&cpu_regs.regs[reg].dword)
00094 // access to a segment register
00095 #define DRCD_SEG_VAL(seg) (&Segs.val[seg])
00096 // access to the physical value of a segment register/selector
00097 #define DRCD_SEG_PHYS(seg) (&Segs.phys[seg])
00098 
00099 // access to an 8bit general register
00100 #define DRCD_REG_BYTE(reg,idx) (&cpu_regs.regs[reg].byte[idx?BH_INDEX:BL_INDEX])
00101 // access to  16/32bit general registers
00102 #define DRCD_REG_WORD(reg,dwrd) ((dwrd)?((void*)(&cpu_regs.regs[reg].dword[DW_INDEX])):((void*)(&cpu_regs.regs[reg].word[W_INDEX])))
00103 
00104 
00105 enum BlockReturn {
00106         BR_Normal=0,
00107         BR_Cycles,
00108         BR_Link1,BR_Link2,
00109         BR_Opcode,
00110 #if (C_DEBUG)
00111         BR_OpcodeFull,
00112 #endif
00113         BR_Iret,
00114         BR_CallBack,
00115         BR_SMCBlock
00116 };
00117 
00118 // identificator to signal self-modification of the currently executed block
00119 #define SMC_CURRENT_BLOCK       0xffff
00120 
00121 
00122 static void IllegalOptionDynrec(const char* msg) {
00123         E_Exit("DynrecCore: illegal option in %s",msg);
00124 }
00125 
00126 static struct {
00127         BlockReturn (*runcode)(Bit8u*);         // points to code that can start a block
00128         Bitu callback;                          // the occurred callback
00129         Bitu readdata;                          // spare space used when reading from memory
00130         Bit32u protected_regs[8];       // space to save/restore register values
00131 } core_dynrec;
00132 
00133 
00134 #include "core_dynrec/cache.h"
00135 
00136 #define X86                     0x01
00137 #define X86_64          0x02
00138 #define MIPSEL          0x03
00139 #define ARMV4LE         0x04
00140 #define ARMV7LE         0x05
00141 #define ARMV8LE         0x07
00142 
00143 #if !defined(C_TARGETCPU)
00144 # if defined(_MSC_VER) && defined(_M_AMD64)
00145 #  define C_TARGETCPU X86_64
00146 # elif defined(_MSC_VER) && defined(_M_ARM)
00147 #  define C_TARGETCPU ARMV7LE
00148 # elif defined(_MSC_VER) && defined(_M_ARM64)
00149 #  define C_TARGETCPU ARMV8LE
00150 # endif
00151 #endif
00152 
00153 #if C_TARGETCPU == X86_64
00154 #include "core_dynrec/risc_x64.h"
00155 #elif C_TARGETCPU == X86
00156 #include "core_dynrec/risc_x86.h"
00157 #elif C_TARGETCPU == MIPSEL
00158 #include "core_dynrec/risc_mipsel32.h"
00159 #elif (C_TARGETCPU == ARMV4LE) || (C_TARGETCPU == ARMV7LE)
00160 #ifdef _MSC_VER
00161 #pragma message("warning: Using ARMV4 or ARMV7")
00162 #else
00163 #warning Using ARMV4 or ARMV7
00164 #endif
00165 #include "core_dynrec/risc_armv4le.h"
00166 #elif C_TARGETCPU == ARMV8LE
00167 #include "core_dynrec/risc_armv8le.h"
00168 #endif
00169 
00170 #include "core_dynrec/decoder.h"
00171 
00172 /* Dynarec on Windows ARM32 works, but only on Windows 10.
00173  * Windows RT 8.x only runs ARMv7 Thumb-2 code, which we don't have a backend for
00174  * Attempting to use dynarec on RT will result in an instant crash. 
00175  * So we need to detect the Windows version before enabling dynarec. */
00176 #if defined(WIN32) && defined(_M_ARM)
00177 bool IsWin10OrGreater() {
00178     HMODULE handle = GetModuleHandleW(L"ntdll.dll");
00179     if (handle) {
00180         typedef LONG(WINAPI* RtlGetVersionPtr)(RTL_OSVERSIONINFOW*);
00181         RtlGetVersionPtr getVersionPtr = (RtlGetVersionPtr)GetProcAddress(handle, "RtlGetVersion");
00182         if (getVersionPtr != NULL) {
00183             RTL_OSVERSIONINFOW info = { 0 };
00184             info.dwOSVersionInfoSize = sizeof(info);
00185             if (getVersionPtr(&info) == 0) { /* STATUS_SUCCESS == 0 */
00186                 if (info.dwMajorVersion >= 10)
00187                     return true;
00188             }
00189         }
00190     }
00191     return false;
00192 }
00193 
00194 static bool is_win10 = false;
00195 static bool winrt_warning = true;
00196 #endif
00197 
00198 
00199 CacheBlockDynRec * LinkBlocks(BlockReturn ret) {
00200         CacheBlockDynRec * block=NULL;
00201         // the last instruction was a control flow modifying instruction
00202         Bit32u temp_ip=SegPhys(cs)+reg_eip;
00203         CodePageHandlerDynRec * temp_handler=(CodePageHandlerDynRec *)get_tlb_readhandler(temp_ip);
00204         if (temp_handler->flags & PFLAG_HASCODE) {
00205                 // see if the target is an already translated block
00206                 block=temp_handler->FindCacheBlock(temp_ip & 4095);
00207                 if (!block) return NULL;
00208 
00209                 // found it, link the current block to
00210                 cache.block.running->LinkTo(ret==BR_Link2,block);
00211                 return block;
00212         }
00213         return NULL;
00214 }
00215 
00216 /*
00217         The core tries to find the block that should be executed next.
00218         If such a block is found, it is run, otherwise the instruction
00219         stream starting at ip_point is translated (see decoder.h) and
00220         makes up a new code block that will be run.
00221         When control is returned to CPU_Core_Dynrec_Run (which might
00222         be right after the block is run, or somewhen long after that
00223         due to the direct cacheblock linking) the returncode decides
00224         the next action. This might be continuing the translation and
00225         execution process, or returning from the core etc.
00226 */
00227 
00228 extern bool use_dynamic_core_with_paging;
00229 extern int dynamic_core_cache_block_size;
00230 
00231 static bool paging_warning = true;
00232 
00233 Bits CPU_Core_Dynrec_Run(void) {
00234     if (CPU_Cycles <= 0)
00235             return CBRET_NONE;
00236 
00237 #if defined(WIN32) && defined(_M_ARM)
00238     if (!is_win10) {
00239         if (winrt_warning) {
00240             LOG_MSG("Dynamic core warning: Windows RT 8.x requires ARMv7 Thumb-2 dynarec core, which is not supported yet.");
00241             winrt_warning = false;
00242         }
00243         return CPU_Core_Normal_Run();
00244     }
00245 #endif
00246 
00247     /* Dynamic core is NOT compatible with the way page faults
00248      * in the guest are handled in this emulator. Do not use
00249      * dynamic core if paging is enabled. Do not comment this
00250      * out, even if it happens to work for a minute, a half
00251      * hour, a day, because it will turn around and cause
00252      * Windows 95 to crash when you've become most comfortable
00253      * with the idea that it works. This code cannot handle
00254      * the sudden context switch of a page fault and it never
00255      * will. Don't do it. You have been warned. */
00256     if (paging.enabled && !use_dynamic_core_with_paging) {
00257         if (paging_warning) {
00258             LOG_MSG("Dynamic core warning: The guest OS/Application has just switched on 80386 paging, which is not supported by the dynamic core. The normal core will be used until paging is switched off again.");
00259             paging_warning = false;
00260         }
00261 
00262         return CPU_Core_Normal_Run();
00263     }
00264 
00265         for (;;) {
00266                 // Determine the linear address of CS:EIP
00267                 PhysPt ip_point=SegPhys(cs)+reg_eip;
00268                 #if C_HEAVY_DEBUG
00269                         if (DEBUG_HeavyIsBreakpoint()) return (Bits)debugCallback;
00270                 #endif
00271 
00272                 CodePageHandlerDynRec * chandler=0;
00273                 // see if the current page is present and contains code
00274                 if (GCC_UNLIKELY(MakeCodePage(ip_point,chandler))) {
00275                         // page not present, throw the exception
00276                         CPU_Exception(cpu.exception.which,cpu.exception.error);
00277                         continue;
00278                 }
00279 
00280                 // page doesn't contain code or is special
00281                 if (GCC_UNLIKELY(!chandler)) return CPU_Core_Normal_Run();
00282 
00283                 // find correct Dynamic Block to run
00284                 CacheBlockDynRec * block=chandler->FindCacheBlock(ip_point&4095);
00285                 if (!block) {
00286                         // no block found, thus translate the instruction stream
00287                         // unless the instruction is known to be modified
00288                         if (!chandler->invalidation_map || (chandler->invalidation_map[ip_point&4095]<4)) {
00289                                 // translate up to 32 instructions
00290                                 block=CreateCacheBlock(chandler,ip_point,32);
00291                         } else {
00292                                 // let the normal core handle this instruction to avoid zero-sized blocks
00293                                 cpu_cycles_count_t old_cycles=CPU_Cycles;
00294                                 CPU_Cycles=1;
00295                                 Bits nc_retcode=CPU_Core_Normal_Run();
00296                                 if (!nc_retcode) {
00297                                         CPU_Cycles=old_cycles-1;
00298                                         continue;
00299                                 }
00300                                 CPU_CycleLeft+=old_cycles;
00301                                 return nc_retcode;
00302                         }
00303                 }
00304 
00305 run_block:
00306                 cache.block.running=0;
00307                 // now we're ready to run the dynamic code block
00308 //              BlockReturn ret=((BlockReturn (*)(void))(block->cache.start))();
00309                 BlockReturn ret=core_dynrec.runcode(block->cache.start);
00310 
00311         if (sizeof(CPU_Cycles) > 4) {
00312             // HACK: All dynrec cores for each processor assume CPU_Cycles is 32-bit wide.
00313             //       The purpose of this hack is to sign-extend the lower 32 bits so that
00314             //       when CPU_Cycles goes negative it doesn't suddenly appear as a very
00315             //       large integer value.
00316             //
00317             //       This hack is needed for dynrec to work on x86_64 targets.
00318             CPU_Cycles = (Bits)((int32_t)CPU_Cycles);
00319         }
00320 
00321                 switch (ret) {
00322                 case BR_Iret:
00323 #if C_DEBUG
00324 #if C_HEAVY_DEBUG
00325                         if (DEBUG_HeavyIsBreakpoint()) return (Bits)debugCallback;
00326 #endif
00327 #endif
00328                         if (!GETFLAG(TF)) {
00329                                 if (GETFLAG(IF) && PIC_IRQCheck) return CBRET_NONE;
00330                                 break;
00331                         }
00332                         // trapflag is set, switch to the trap-aware decoder
00333                         cpudecoder=CPU_Core_Dynrec_Trap_Run;
00334                         return CBRET_NONE;
00335 
00336                 case BR_Normal:
00337                         // the block was exited due to a non-predictable control flow
00338                         // modifying instruction (like ret) or some nontrivial cpu state
00339                         // changing instruction (for example switch to/from pmode),
00340                         // or the maximum number of instructions to translate was reached
00341 #if C_DEBUG
00342 #if C_HEAVY_DEBUG
00343                         if (DEBUG_HeavyIsBreakpoint()) return (Bits)debugCallback;
00344 #endif
00345 #endif
00346                         break;
00347 
00348                 case BR_Cycles:
00349                         // cycles went negative, return from the core to handle
00350                         // external events, schedule the pic...
00351 #if C_DEBUG
00352 #if C_HEAVY_DEBUG
00353                         if (DEBUG_HeavyIsBreakpoint()) return (Bits)debugCallback;
00354 #endif
00355 #endif
00356                         return CBRET_NONE;
00357 
00358                 case BR_CallBack:
00359                         // the callback code is executed in dosbox.conf, return the callback number
00360                         FillFlags();
00361                         return (Bits)core_dynrec.callback;
00362 
00363                 case BR_SMCBlock:
00364 //                      LOG_MSG("selfmodification of running block at %x:%x",SegValue(cs),reg_eip);
00365                         cpu.exception.which=0;
00366                         // fallthrough, let the normal core handle the block-modifying instruction
00367                 case BR_Opcode:
00368 #if (C_DEBUG)
00369                 case BR_OpcodeFull:
00370 #endif
00371                         // some instruction has been encountered that could not be translated
00372                         // (thus it is not part of the code block), the normal core will
00373                         // handle this instruction
00374                         CPU_CycleLeft+=CPU_Cycles;
00375                         CPU_Cycles=1;
00376                         return CPU_Core_Normal_Run();
00377 
00378                 case BR_Link1:
00379                 case BR_Link2:
00380                         block=LinkBlocks(ret);
00381                         if (block) goto run_block;
00382                         break;
00383 
00384                 default:
00385                         E_Exit("Invalid return code %d", ret);
00386                 }
00387         }
00388         return CBRET_NONE;
00389 }
00390 
00391 Bits CPU_Core_Dynrec_Trap_Run(void) {
00392         Bits oldCycles = CPU_Cycles;
00393         CPU_Cycles = 1;
00394         cpu.trap_skip = false;
00395 
00396         // let the normal core execute the next (only one!) instruction
00397         Bits ret=CPU_Core_Normal_Run();
00398 
00399         // trap to int1 unless the last instruction deferred this
00400         // (allows hardware interrupts to be served without interaction)
00401         if (!cpu.trap_skip) CPU_HW_Interrupt(1);
00402 
00403         CPU_Cycles = oldCycles-1;
00404         // continue (either the trapflag was clear anyways, or the int1 cleared it)
00405         cpudecoder = &CPU_Core_Dynrec_Run;
00406 
00407         return ret;
00408 }
00409 
00410 void CPU_Core_Dynrec_Init(void) {
00411 #if defined(WIN32) && defined(_M_ARM)
00412     is_win10 = IsWin10OrGreater();
00413 #endif
00414 }
00415 
00416 void CPU_Core_Dynrec_Cache_Init(bool enable_cache) {
00417         // Initialize code cache and dynamic blocks
00418         cache_init(enable_cache);
00419 }
00420 
00421 void CPU_Core_Dynrec_Cache_Close(void) {
00422         cache_close();
00423 }
00424 
00425 void CPU_Core_Dynrec_Cache_Reset(void) {
00426         cache_reset();
00427 }
00428 #endif