1 /*
2  *  Copyright (C) 2002-2021  The DOSBox Team
3  *
4  *  This program is free software; you can redistribute it and/or modify
5  *  it under the terms of the GNU General Public License as published by
6  *  the Free Software Foundation; either version 2 of the License, or
7  *  (at your option) any later version.
8  *
9  *  This program is distributed in the hope that it will be useful,
10  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
11  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  *  GNU General Public License for more details.
13  *
14  *  You should have received a copy of the GNU General Public License along
15  *  with this program; if not, write to the Free Software Foundation, Inc.,
16  *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17  */
18 
19 
20 #include "dosbox.h"
21 
22 #if (C_DYNAMIC_X86)
23 
24 #include <cassert>
25 #include <cstdarg>
26 #include <cstdio>
27 #include <cstring>
28 #include <cstddef>
29 #include <cstdlib>
30 
31 #if defined (WIN32)
32 #include <windows.h>
33 #include <winbase.h>
34 #endif
35 
36 #if defined(HAVE_MPROTECT) || defined(HAVE_MMAP)
37 #include <sys/mman.h>
38 
39 #include <limits.h>
40 
41 #ifndef PAGESIZE
42 #define PAGESIZE 4096
43 #endif
44 
45 #endif // HAVE_MPROTECT
46 
47 #include "callback.h"
48 #include "regs.h"
49 #include "mem.h"
50 #include "cpu.h"
51 #include "debug.h"
52 #include "paging.h"
53 #include "inout.h"
54 #include "fpu.h"
55 
56 #define CACHE_MAXSIZE	(4096*3)
57 #define CACHE_TOTAL		(1024*1024*8)
58 #define CACHE_PAGES		(512)
59 #define CACHE_BLOCKS	(64*1024)
60 #define CACHE_ALIGN		(16)
61 #define DYN_HASH_SHIFT	(4)
62 #define DYN_PAGE_HASH	(4096>>DYN_HASH_SHIFT)
63 #define DYN_LINKS		(16)
64 
65 //#define DYN_LOG 1 //Turn logging on
66 
67 
68 #if C_FPU
69 #define CPU_FPU 1                                               //Enable FPU escape instructions
70 #define X86_DYNFPU_DH_ENABLED
71 #endif
72 
73 enum {
74 	G_EAX,G_ECX,G_EDX,G_EBX,
75 	G_ESP,G_EBP,G_ESI,G_EDI,
76 	G_ES,G_CS,G_SS,G_DS,G_FS,G_GS,
77 	G_FLAGS,G_NEWESP,G_EIP,
78 	G_EA,G_STACK,G_CYCLES,
79 	G_TMPB,G_TMPW,G_SHIFT,
80 	G_EXIT,
81 	G_MAX,
82 };
83 
84 enum SingleOps {
85 	SOP_INC,SOP_DEC,
86 	SOP_NOT,SOP_NEG,
87 };
88 
89 enum DualOps {
90 	DOP_ADD,DOP_ADC,
91 	DOP_SUB,DOP_SBB,
92 	DOP_CMP,DOP_XOR,
93 	DOP_AND,DOP_OR,
94 	DOP_TEST,
95 	DOP_MOV,
96 	DOP_XCHG,
97 };
98 
99 enum ShiftOps {
100 	SHIFT_ROL,SHIFT_ROR,
101 	SHIFT_RCL,SHIFT_RCR,
102 	SHIFT_SHL,SHIFT_SHR,
103 	SHIFT_SAL,SHIFT_SAR,
104 };
105 
106 enum BranchTypes {
107 	BR_O,BR_NO,BR_B,BR_NB,
108 	BR_Z,BR_NZ,BR_BE,BR_NBE,
109 	BR_S,BR_NS,BR_P,BR_NP,
110 	BR_L,BR_NL,BR_LE,BR_NLE
111 };
112 
113 
114 enum BlockReturn {
115 	BR_Normal=0,
116 	BR_Cycles,
117 	BR_Link1,BR_Link2,
118 	BR_Opcode,
119 	BR_Iret,
120 	BR_CallBack,
121 	BR_SMCBlock
122 };
123 
124 #define SMC_CURRENT_BLOCK	0xffff
125 
126 
127 #define DYNFLG_HAS16		0x1		//Would like 8-bit host reg support
128 #define DYNFLG_HAS8			0x2		//Would like 16-bit host reg support
129 #define DYNFLG_LOAD			0x4		//Load value when accessed
130 #define DYNFLG_SAVE			0x8		//Needs to be saved back at the end of block
131 #define DYNFLG_CHANGED		0x10	//Value is in a register and changed from load
132 #define DYNFLG_ACTIVE		0x20	//Register has an active value
133 
134 class GenReg;
135 
136 struct DynReg {
137 	Bitu flags;
138 	GenReg * genreg;
139 	void * data;
140 };
141 
142 enum DynAccess {
143 	DA_d,DA_w,
144 	DA_bh,DA_bl
145 };
146 
147 enum ByteCombo {
148 	BC_ll,BC_lh,
149 	BC_hl,BC_hh,
150 };
151 
152 static DynReg DynRegs[G_MAX];
153 #define DREG(_WHICH_) &DynRegs[G_ ## _WHICH_ ]
154 
155 static struct {
156 	Bit32u ea,tmpb,tmpd,stack,shift,newesp;
157 } extra_regs;
158 
159 #define IllegalOption(msg) E_Exit("DYNX86: illegal option in " msg)
160 
161 #define dyn_return(a,b) gen_return(a)
162 #include "dyn_cache.h"
163 
164 static struct {
165 	Bitu callback;
166 	uint32_t readdata;
167 } core_dyn;
168 
169 #if defined(X86_DYNFPU_DH_ENABLED)
170 static struct dyn_dh_fpu {
171 	Bit16u		cw,host_cw;
172 	bool		state_used;
173 	// some fields expanded here for alignment purposes
174 	struct {
175 		Bit32u cw;
176 		Bit32u sw;
177 		Bit32u tag;
178 		Bit32u ip;
179 		Bit32u cs;
180 		Bit32u ea;
181 		Bit32u ds;
182 		Bit8u st_reg[8][10];
183 	} state;
184 	FPU_P_Reg	temp,temp2;
185 	Bit32u		dh_fpu_enabled;
186 	Bit8u		temp_state[128];
187 } dyn_dh_fpu;
188 #endif
189 
190 #define X86         0x01
191 #define X86_64      0x02
192 
193 #if C_TARGETCPU == X86_64
194 #include "core_dyn_x86/risc_x64.h"
195 #elif C_TARGETCPU == X86
196 #include "core_dyn_x86/risc_x86.h"
197 #else
198 #error DYN_X86 core not supported for this CPU target.
199 #endif
200 
201 struct DynState {
202 	DynReg regs[G_MAX];
203 };
204 
dyn_flags_host_to_gen(void)205 static void dyn_flags_host_to_gen(void) {
206 	gen_dop_word(DOP_MOV,true,DREG(EXIT),DREG(FLAGS));
207 	gen_dop_word_imm(DOP_AND,true,DREG(EXIT),FMASK_TEST);
208 	gen_load_flags(DREG(EXIT));
209 	gen_releasereg(DREG(EXIT));
210 	gen_releasereg(DREG(FLAGS));
211 }
212 
dyn_flags_gen_to_host(void)213 static void dyn_flags_gen_to_host(void) {
214 	gen_save_flags(DREG(EXIT));
215 	gen_dop_word_imm(DOP_AND,true,DREG(EXIT),FMASK_TEST);
216 	gen_dop_word_imm(DOP_AND,true,DREG(FLAGS),~FMASK_TEST);
217 	gen_dop_word(DOP_OR,true,DREG(FLAGS),DREG(EXIT)); //flags are marked for save
218 	gen_releasereg(DREG(EXIT));
219 	gen_releasereg(DREG(FLAGS));
220 }
221 
dyn_savestate(DynState * state)222 static void dyn_savestate(DynState * state) {
223 	for (Bitu i=0;i<G_MAX;i++) {
224 		state->regs[i].flags=DynRegs[i].flags;
225 		state->regs[i].genreg=DynRegs[i].genreg;
226 	}
227 }
228 
dyn_loadstate(DynState * state)229 static void dyn_loadstate(DynState * state) {
230 	for (Bitu i=0;i<G_MAX;i++) {
231 		gen_setupreg(&DynRegs[i],&state->regs[i]);
232 	}
233 }
234 
dyn_synchstate(DynState * state)235 static void dyn_synchstate(DynState * state) {
236 	for (Bitu i=0;i<G_MAX;i++) {
237 		gen_synchreg(&DynRegs[i],&state->regs[i]);
238 	}
239 }
240 
dyn_saveregister(DynReg * src_reg,DynReg * dst_reg)241 static void dyn_saveregister(DynReg * src_reg, DynReg * dst_reg) {
242 	dst_reg->flags=src_reg->flags;
243 	dst_reg->genreg=src_reg->genreg;
244 }
245 
dyn_restoreregister(DynReg * src_reg,DynReg * dst_reg)246 static void dyn_restoreregister(DynReg * src_reg, DynReg * dst_reg) {
247 	dst_reg->flags=src_reg->flags;
248 	dst_reg->genreg=src_reg->genreg;
249 	dst_reg->genreg->dynreg=dst_reg;	// necessary when register has been released
250 }
251 
252 #include "core_dyn_x86/decoder.h"
253 
CPU_Core_Dyn_X86_Run(void)254 Bits CPU_Core_Dyn_X86_Run(void) {
255 	// helper class to auto-save DH_FPU state on function exit
256 	class auto_dh_fpu {
257 	public:
258 		~auto_dh_fpu()
259 		{
260 #if defined(X86_DYNFPU_DH_ENABLED)
261 			if (dyn_dh_fpu.state_used)
262 				gen_dh_fpu_save();
263 #endif
264 		}
265 	};
266 	auto_dh_fpu fpu_saver;
267 
268 	/* Determine the linear address of CS:EIP */
269 restart_core:
270 	PhysPt ip_point=SegPhys(cs)+reg_eip;
271 #if C_DEBUG
272 #if C_HEAVY_DEBUG
273 		if (DEBUG_HeavyIsBreakpoint()) return debugCallback;
274 #endif
275 #endif
276 	CodePageHandler * chandler=0;
277 	if (GCC_UNLIKELY(MakeCodePage(ip_point,chandler))) {
278 		CPU_Exception(cpu.exception.which,cpu.exception.error);
279 		goto restart_core;
280 	}
281 	if (!chandler) {
282 		return CPU_Core_Normal_Run();
283 	}
284 	/* Find correct Dynamic Block to run */
285 	CacheBlock * block=chandler->FindCacheBlock(ip_point&4095);
286 	if (!block) {
287 		if (!chandler->invalidation_map || (chandler->invalidation_map[ip_point&4095]<4)) {
288 			block=CreateCacheBlock(chandler,ip_point,32);
289 		} else {
290 			Bit32s old_cycles=CPU_Cycles;
291 			CPU_Cycles=1;
292 			// manually save
293 			fpu_saver = auto_dh_fpu();
294 			Bits nc_retcode=CPU_Core_Normal_Run();
295 			if (!nc_retcode) {
296 				CPU_Cycles=old_cycles-1;
297 				goto restart_core;
298 			}
299 			CPU_CycleLeft+=old_cycles;
300 			return nc_retcode;
301 		}
302 	}
303 run_block:
304 	cache.block.running=0;
305 	BlockReturn ret=gen_runcode(block->cache.start);
306 #if C_DEBUG
307 	cycle_count += 32;
308 #endif
309 	switch (ret) {
310 	case BR_Iret:
311 #if C_DEBUG
312 #if C_HEAVY_DEBUG
313 		if (DEBUG_HeavyIsBreakpoint()) {
314 			return debugCallback;
315 		}
316 #endif
317 #endif
318 		if (!GETFLAG(TF)) {
319 			if (GETFLAG(IF) && PIC_IRQCheck) {
320 				return CBRET_NONE;
321 			}
322 			goto restart_core;
323 		}
324 		cpudecoder=CPU_Core_Dyn_X86_Trap_Run;
325 		return CBRET_NONE;
326 	case BR_Normal:
327 		/* Maybe check if we staying in the same page? */
328 #if C_DEBUG
329 #if C_HEAVY_DEBUG
330 		if (DEBUG_HeavyIsBreakpoint()) return debugCallback;
331 #endif
332 #endif
333 		goto restart_core;
334 	case BR_Cycles:
335 #if C_DEBUG
336 #if C_HEAVY_DEBUG
337 		if (DEBUG_HeavyIsBreakpoint()) return debugCallback;
338 #endif
339 #endif
340 		return CBRET_NONE;
341 	case BR_CallBack:
342 		return core_dyn.callback;
343 	case BR_SMCBlock:
344 		// LOG_MSG("selfmodification of running block at %x:%x",
345 		//         SegValue(cs), reg_eip);
346 		cpu.exception.which=0;
347 		[[fallthrough]]; // let the normal core handle the block-modifying
348 		             // instruction
349 	case BR_Opcode:
350 		CPU_CycleLeft+=CPU_Cycles;
351 		CPU_Cycles=1;
352 		return CPU_Core_Normal_Run();
353 	case BR_Link1:
354 	case BR_Link2:
355 		{
356 			Bit32u temp_ip=SegPhys(cs)+reg_eip;
357 			CodePageHandler* temp_handler = reinterpret_cast<CodePageHandler *>(get_tlb_readhandler(temp_ip));
358 			if (temp_handler->flags & (cpu.code.big ? PFLAG_HASCODE32:PFLAG_HASCODE16)) {
359 				block=temp_handler->FindCacheBlock(temp_ip & 4095);
360 				if (!block || !cache.block.running) goto restart_core;
361 				cache.block.running->LinkTo(ret==BR_Link2,block);
362 				goto run_block;
363 			}
364 		}
365 		goto restart_core;
366 	}
367 	return CBRET_NONE;
368 }
369 
CPU_Core_Dyn_X86_Trap_Run(void)370 Bits CPU_Core_Dyn_X86_Trap_Run(void) {
371 	Bit32s oldCycles = CPU_Cycles;
372 	CPU_Cycles = 1;
373 	cpu.trap_skip = false;
374 
375 	Bits ret=CPU_Core_Normal_Run();
376 	if (!cpu.trap_skip) CPU_DebugException(DBINT_STEP,reg_eip);
377 	CPU_Cycles = oldCycles-1;
378 	cpudecoder = &CPU_Core_Dyn_X86_Run;
379 
380 	return ret;
381 }
382 
CPU_Core_Dyn_X86_Init(void)383 void CPU_Core_Dyn_X86_Init(void) {
384 	Bits i;
385 	/* Setup the global registers and their flags */
386 	for (i=0;i<G_MAX;i++) DynRegs[i].genreg=0;
387 	DynRegs[G_EAX].data=&reg_eax;
388 	DynRegs[G_EAX].flags=DYNFLG_HAS8|DYNFLG_HAS16|DYNFLG_LOAD|DYNFLG_SAVE;
389 	DynRegs[G_ECX].data=&reg_ecx;
390 	DynRegs[G_ECX].flags=DYNFLG_HAS8|DYNFLG_HAS16|DYNFLG_LOAD|DYNFLG_SAVE;
391 	DynRegs[G_EDX].data=&reg_edx;
392 	DynRegs[G_EDX].flags=DYNFLG_HAS8|DYNFLG_HAS16|DYNFLG_LOAD|DYNFLG_SAVE;
393 	DynRegs[G_EBX].data=&reg_ebx;
394 	DynRegs[G_EBX].flags=DYNFLG_HAS8|DYNFLG_HAS16|DYNFLG_LOAD|DYNFLG_SAVE;
395 
396 	DynRegs[G_EBP].data=&reg_ebp;
397 	DynRegs[G_EBP].flags=DYNFLG_HAS16|DYNFLG_LOAD|DYNFLG_SAVE;
398 	DynRegs[G_ESP].data=&reg_esp;
399 	DynRegs[G_ESP].flags=DYNFLG_HAS16|DYNFLG_LOAD|DYNFLG_SAVE;
400 	DynRegs[G_EDI].data=&reg_edi;
401 	DynRegs[G_EDI].flags=DYNFLG_HAS16|DYNFLG_LOAD|DYNFLG_SAVE;
402 	DynRegs[G_ESI].data=&reg_esi;
403 	DynRegs[G_ESI].flags=DYNFLG_HAS16|DYNFLG_LOAD|DYNFLG_SAVE;
404 
405 	DynRegs[G_ES].data=&Segs.phys[es];
406 	DynRegs[G_ES].flags=DYNFLG_LOAD|DYNFLG_SAVE;
407 	DynRegs[G_CS].data=&Segs.phys[cs];
408 	DynRegs[G_CS].flags=DYNFLG_LOAD|DYNFLG_SAVE;
409 	DynRegs[G_SS].data=&Segs.phys[ss];
410 	DynRegs[G_SS].flags=DYNFLG_LOAD|DYNFLG_SAVE;
411 	DynRegs[G_DS].data=&Segs.phys[ds];
412 	DynRegs[G_DS].flags=DYNFLG_LOAD|DYNFLG_SAVE;
413 	DynRegs[G_FS].data=&Segs.phys[fs];
414 	DynRegs[G_FS].flags=DYNFLG_LOAD|DYNFLG_SAVE;
415 	DynRegs[G_GS].data=&Segs.phys[gs];
416 	DynRegs[G_GS].flags=DYNFLG_LOAD|DYNFLG_SAVE;
417 
418 	DynRegs[G_FLAGS].data=&reg_flags;
419 	DynRegs[G_FLAGS].flags=DYNFLG_LOAD|DYNFLG_SAVE;
420 
421 	DynRegs[G_NEWESP].data=&extra_regs.newesp;
422 	DynRegs[G_NEWESP].flags=0;
423 
424 	DynRegs[G_EIP].data=&reg_eip;
425 	DynRegs[G_EIP].flags=DYNFLG_LOAD|DYNFLG_SAVE;
426 
427 	DynRegs[G_EA].data=&extra_regs.ea;
428 	DynRegs[G_EA].flags=0;
429 	DynRegs[G_STACK].data=&extra_regs.stack;
430 	DynRegs[G_STACK].flags=0;
431 	DynRegs[G_CYCLES].data=&CPU_Cycles;
432 	DynRegs[G_CYCLES].flags=DYNFLG_LOAD|DYNFLG_SAVE;
433 	DynRegs[G_TMPB].data=&extra_regs.tmpb;
434 	DynRegs[G_TMPB].flags=DYNFLG_HAS8|DYNFLG_HAS16;
435 	DynRegs[G_TMPW].data=&extra_regs.tmpd;
436 	DynRegs[G_TMPW].flags=DYNFLG_HAS16;
437 	DynRegs[G_SHIFT].data=&extra_regs.shift;
438 	DynRegs[G_SHIFT].flags=DYNFLG_HAS8|DYNFLG_HAS16;
439 	DynRegs[G_EXIT].data=0;
440 	DynRegs[G_EXIT].flags=DYNFLG_HAS16;
441 	/* Init the generator */
442 	gen_init();
443 
444 #if defined(X86_DYNFPU_DH_ENABLED)
445 	/* Init the fpu state */
446 	dyn_dh_fpu.dh_fpu_enabled=true;
447 	dyn_dh_fpu.state_used=false;
448 	dyn_dh_fpu.cw=0x37f;
449 	// FINIT
450 	memset(&dyn_dh_fpu.state, 0, sizeof(dyn_dh_fpu.state));
451 	dyn_dh_fpu.state.cw = 0x37F;
452 	dyn_dh_fpu.state.tag = 0xFFFF;
453 #endif
454 
455 	return;
456 }
457 
CPU_Core_Dyn_X86_Cache_Init(bool enable_cache)458 void CPU_Core_Dyn_X86_Cache_Init(bool enable_cache) {
459 	/* Initialize code cache and dynamic blocks */
460 	cache_init(enable_cache);
461 }
462 
CPU_Core_Dyn_X86_Cache_Close(void)463 void CPU_Core_Dyn_X86_Cache_Close(void) {
464 	cache_close();
465 }
466 
CPU_Core_Dyn_X86_SetFPUMode(bool dh_fpu)467 void CPU_Core_Dyn_X86_SetFPUMode(bool dh_fpu) {
468 #if defined(X86_DYNFPU_DH_ENABLED)
469 	dyn_dh_fpu.dh_fpu_enabled=dh_fpu;
470 #endif
471 }
472 
473 #endif
474