From c880302c6bd7549182a7a75e9bf0d253e82e687f Mon Sep 17 00:00:00 2001 From: booto Date: Mon, 14 May 2018 20:15:21 -0400 Subject: [PATCH] Prevent paired singles routines clobbering PC,SRR0 Paired single (ps) instructions can call asm_routines that try to update PowerPC::ppcState.pc. At the time the asm_routine is built, emulation has not started and the PC is invalid (0). If the ps instruction causes an exception (e.g. DSI), SRR0 gets clobbered with the invalid PC. This change makes the relevant ps instructions store PC before calling out to asm_routines, and prevents the asm_routine from trying to store PC itself. --- .../Core/PowerPC/Jit64/Jit_LoadStorePaired.cpp | 7 ++++++- .../Core/PowerPC/Jit64Common/EmuCodeBlock.cpp | 18 ++++++++++++++---- .../Core/PowerPC/Jit64Common/EmuCodeBlock.h | 2 ++ .../PowerPC/Jit64Common/Jit64AsmCommon.cpp | 15 +++++++++------ .../Core/Core/PowerPC/JitCommon/JitAsmCommon.h | 4 ++++ 5 files changed, 35 insertions(+), 11 deletions(-) diff --git a/Source/Core/Core/PowerPC/Jit64/Jit_LoadStorePaired.cpp b/Source/Core/Core/PowerPC/Jit64/Jit_LoadStorePaired.cpp index 4c79f33940..e490accab7 100644 --- a/Source/Core/Core/PowerPC/Jit64/Jit_LoadStorePaired.cpp +++ b/Source/Core/Core/PowerPC/Jit64/Jit_LoadStorePaired.cpp @@ -71,6 +71,8 @@ void Jit64::psq_stXX(UGeckoInstruction inst) } else { + // Stash PC in case asm_routine causes exception + MOV(32, PPCSTATE(pc), Imm32(g_jit->js.compilerPC)); // We know what GQR is here, so we can load RSCRATCH2 and call into the store method directly // with just the scale bits. MOV(32, R(RSCRATCH2), Imm32(gqrValue & 0x3F00)); @@ -83,6 +85,8 @@ void Jit64::psq_stXX(UGeckoInstruction inst) } else { + // Stash PC incase asm_routine causes exception + MOV(32, PPCSTATE(pc), Imm32(g_jit->js.compilerPC)); // Some games (e.g. Dirt 2) incorrectly set the unused bits which breaks the lookup table code. // Hence, we need to mask out the unused bits. The layout of the GQR register is // UU[SCALE]UUUUU[TYPE] where SCALE is 6 bits and TYPE is 3 bits, so we have to AND with @@ -148,10 +152,11 @@ void Jit64::psq_lXX(UGeckoInstruction inst) } else { + // Stash PC in case asm_routine causes exception + MOV(32, PPCSTATE(pc), Imm32(g_jit->js.compilerPC)); // Get the high part of the GQR register OpArg gqr = PPCSTATE(spr[SPR_GQR0 + i]); gqr.AddMemOffset(2); - MOV(32, R(RSCRATCH2), Imm32(0x3F07)); AND(32, R(RSCRATCH2), gqr); LEA(64, RSCRATCH, M(w ? asm_routines.singleLoadQuantized : asm_routines.pairedLoadQuantized)); diff --git a/Source/Core/Core/PowerPC/Jit64Common/EmuCodeBlock.cpp b/Source/Core/Core/PowerPC/Jit64Common/EmuCodeBlock.cpp index 6f92d605a7..7036fba0f6 100644 --- a/Source/Core/Core/PowerPC/Jit64Common/EmuCodeBlock.cpp +++ b/Source/Core/Core/PowerPC/Jit64Common/EmuCodeBlock.cpp @@ -317,7 +317,8 @@ void EmuCodeBlock::SafeLoadToReg(X64Reg reg_value, const Gen::OpArg& opAddress, bool slowmem = (flags & SAFE_LOADSTORE_FORCE_SLOWMEM) != 0; registersInUse[reg_value] = false; - if (g_jit->jo.fastmem && !(flags & SAFE_LOADSTORE_NO_FASTMEM) && !slowmem) + if (g_jit->jo.fastmem && !(flags & (SAFE_LOADSTORE_NO_FASTMEM | SAFE_LOADSTORE_NO_UPDATE_PC)) && + !slowmem) { u8* backpatchStart = GetWritableCodePtr(); MovInfo mov; @@ -378,7 +379,11 @@ void EmuCodeBlock::SafeLoadToReg(X64Reg reg_value, const Gen::OpArg& opAddress, } // Helps external systems know which instruction triggered the read. - MOV(32, PPCSTATE(pc), Imm32(g_jit->js.compilerPC)); + // Invalid for calls from Jit64AsmCommon routines + if (!(flags & SAFE_LOADSTORE_NO_UPDATE_PC)) + { + MOV(32, PPCSTATE(pc), Imm32(g_jit->js.compilerPC)); + } size_t rsp_alignment = (flags & SAFE_LOADSTORE_NO_PROLOG) ? 8 : 0; ABI_PushRegistersAndAdjustStack(registersInUse, rsp_alignment); @@ -483,7 +488,8 @@ void EmuCodeBlock::SafeWriteRegToReg(OpArg reg_value, X64Reg reg_addr, int acces // set the correct immediate format reg_value = FixImmediate(accessSize, reg_value); - if (g_jit->jo.fastmem && !(flags & SAFE_LOADSTORE_NO_FASTMEM) && !slowmem) + if (g_jit->jo.fastmem && !(flags & (SAFE_LOADSTORE_NO_FASTMEM | SAFE_LOADSTORE_NO_UPDATE_PC)) && + !slowmem) { u8* backpatchStart = GetWritableCodePtr(); MovInfo mov; @@ -540,7 +546,11 @@ void EmuCodeBlock::SafeWriteRegToReg(OpArg reg_value, X64Reg reg_addr, int acces } // PC is used by memory watchpoints (if enabled) or to print accurate PC locations in debug logs - MOV(32, PPCSTATE(pc), Imm32(g_jit->js.compilerPC)); + // Invalid for calls from Jit64AsmCommon routines + if (!(flags & SAFE_LOADSTORE_NO_UPDATE_PC)) + { + MOV(32, PPCSTATE(pc), Imm32(g_jit->js.compilerPC)); + } size_t rsp_alignment = (flags & SAFE_LOADSTORE_NO_PROLOG) ? 8 : 0; ABI_PushRegistersAndAdjustStack(registersInUse, rsp_alignment); diff --git a/Source/Core/Core/PowerPC/Jit64Common/EmuCodeBlock.h b/Source/Core/Core/PowerPC/Jit64Common/EmuCodeBlock.h index acf10f80a5..0c85ec9ccb 100644 --- a/Source/Core/Core/PowerPC/Jit64Common/EmuCodeBlock.h +++ b/Source/Core/Core/PowerPC/Jit64Common/EmuCodeBlock.h @@ -77,6 +77,8 @@ public: // Force slowmem (used when generating fallbacks in trampolines) SAFE_LOADSTORE_FORCE_SLOWMEM = 16, SAFE_LOADSTORE_DR_ON = 32, + // Generated from a context that doesn't have the PC of the instruction that caused it + SAFE_LOADSTORE_NO_UPDATE_PC = 64, }; void SafeLoadToReg(Gen::X64Reg reg_value, const Gen::OpArg& opAddress, int accessSize, s32 offset, diff --git a/Source/Core/Core/PowerPC/Jit64Common/Jit64AsmCommon.cpp b/Source/Core/Core/PowerPC/Jit64Common/Jit64AsmCommon.cpp index 68e1251dcb..6e309714b1 100644 --- a/Source/Core/Core/PowerPC/Jit64Common/Jit64AsmCommon.cpp +++ b/Source/Core/Core/PowerPC/Jit64Common/Jit64AsmCommon.cpp @@ -419,8 +419,9 @@ void QuantizedMemoryRoutines::GenQuantizedStore(bool single, EQuantizeType type, } } - int flags = - isInline ? 0 : SAFE_LOADSTORE_NO_FASTMEM | SAFE_LOADSTORE_NO_PROLOG | SAFE_LOADSTORE_DR_ON; + int flags = isInline ? 0 : + SAFE_LOADSTORE_NO_FASTMEM | SAFE_LOADSTORE_NO_PROLOG | + SAFE_LOADSTORE_DR_ON | SAFE_LOADSTORE_NO_UPDATE_PC; if (!single) flags |= SAFE_LOADSTORE_NO_SWAP; @@ -478,8 +479,9 @@ void QuantizedMemoryRoutines::GenQuantizedLoad(bool single, EQuantizeType type, if (g_jit->jo.memcheck) { BitSet32 regsToSave = QUANTIZED_REGS_TO_SAVE_LOAD; - int flags = - isInline ? 0 : SAFE_LOADSTORE_NO_FASTMEM | SAFE_LOADSTORE_NO_PROLOG | SAFE_LOADSTORE_DR_ON; + int flags = isInline ? 0 : + SAFE_LOADSTORE_NO_FASTMEM | SAFE_LOADSTORE_NO_PROLOG | + SAFE_LOADSTORE_DR_ON | SAFE_LOADSTORE_NO_UPDATE_PC; SafeLoadToReg(RSCRATCH_EXTRA, R(RSCRATCH_EXTRA), size, 0, regsToSave, extend, flags); if (!single && (type == QUANTIZE_U8 || type == QUANTIZE_S8)) { @@ -604,8 +606,9 @@ void QuantizedMemoryRoutines::GenQuantizedLoadFloat(bool single, bool isInline) if (g_jit->jo.memcheck) { BitSet32 regsToSave = QUANTIZED_REGS_TO_SAVE; - int flags = - isInline ? 0 : SAFE_LOADSTORE_NO_FASTMEM | SAFE_LOADSTORE_NO_PROLOG | SAFE_LOADSTORE_DR_ON; + int flags = isInline ? 0 : + SAFE_LOADSTORE_NO_FASTMEM | SAFE_LOADSTORE_NO_PROLOG | + SAFE_LOADSTORE_DR_ON | SAFE_LOADSTORE_NO_UPDATE_PC; SafeLoadToReg(RSCRATCH_EXTRA, R(RSCRATCH_EXTRA), size, 0, regsToSave, extend, flags); } diff --git a/Source/Core/Core/PowerPC/JitCommon/JitAsmCommon.h b/Source/Core/Core/PowerPC/JitCommon/JitAsmCommon.h index fbcdee675f..d0af9fc7ba 100644 --- a/Source/Core/Core/PowerPC/JitCommon/JitAsmCommon.h +++ b/Source/Core/Core/PowerPC/JitCommon/JitAsmCommon.h @@ -32,12 +32,14 @@ public: // Out: XMM0: Bottom two 32-bit slots hold the read value, // converted to a pair of floats. // Trashes: all three RSCRATCH + // Note: Store PC if this could cause an exception const u8** pairedLoadQuantized; // In: array index: GQR to use. // In: ECX: Address to read from. // Out: XMM0: Bottom 32-bit slot holds the read value. // Trashes: all three RSCRATCH + // Note: Store PC if this could cause an exception const u8** singleLoadQuantized; // In: array index: GQR to use. @@ -45,10 +47,12 @@ public: // In: XMM0: Bottom two 32-bit slots hold the pair of floats to be written. // Out: Nothing. // Trashes: all three RSCRATCH + // Note: Store PC if this could cause an exception const u8** pairedStoreQuantized; // In: array index: GQR to use. // In: ECX: Address to write to. // In: XMM0: Bottom 32-bit slot holds the float to be written. + // Note: Store PC if this could cause an exception const u8** singleStoreQuantized; };