diff --git a/src/dynarmic/backend/x64/a32_emit_x64.h b/src/dynarmic/backend/x64/a32_emit_x64.h index e7d4c352..b037071b 100644 --- a/src/dynarmic/backend/x64/a32_emit_x64.h +++ b/src/dynarmic/backend/x64/a32_emit_x64.h @@ -110,6 +110,7 @@ protected: FakeCall FastmemCallback(u64 rip); // Memory access helpers + void EmitCheckMemoryAbort(A32EmitContext& ctx, IR::Inst* inst, Xbyak::Label* end = nullptr); template void EmitMemoryRead(A32EmitContext& ctx, IR::Inst* inst); template diff --git a/src/dynarmic/backend/x64/a32_emit_x64_memory.cpp b/src/dynarmic/backend/x64/a32_emit_x64_memory.cpp index f41c3b9b..34dcd3e4 100644 --- a/src/dynarmic/backend/x64/a32_emit_x64_memory.cpp +++ b/src/dynarmic/backend/x64/a32_emit_x64_memory.cpp @@ -235,4 +235,32 @@ void A32EmitX64::EmitA32ExclusiveWriteMemory64(A32EmitContext& ctx, IR::Inst* in } } +void A32EmitX64::EmitCheckMemoryAbort(A32EmitContext& ctx, IR::Inst* inst, Xbyak::Label* end) { + if (!conf.check_halt_on_memory_access) { + return; + } + + const A32::LocationDescriptor current_location{IR::LocationDescriptor{inst->GetArg(0).GetU64()}}; + + code.test(dword[r15 + offsetof(A32JitState, halt_reason)], static_cast(HaltReason::MemoryAbort)); + + Xbyak::Label memory_abort; + + if (!end) { + code.jnz(memory_abort, code.T_NEAR); + code.SwitchToFarCode(); + } else { + code.jz(*end, code.T_NEAR); + } + + code.L(memory_abort); + EmitSetUpperLocationDescriptor(current_location, ctx.Location()); + code.mov(dword[r15 + offsetof(A32JitState, Reg) + sizeof(u32) * 15], current_location.PC()); + code.ForceReturnFromRunCode(); + + if (!end) { + code.SwitchToNearCode(); + } +} + } // namespace Dynarmic::Backend::X64 diff --git a/src/dynarmic/backend/x64/a32_interface.cpp b/src/dynarmic/backend/x64/a32_interface.cpp index 860c70bc..3de0b968 100644 --- a/src/dynarmic/backend/x64/a32_interface.cpp +++ b/src/dynarmic/backend/x64/a32_interface.cpp @@ -173,7 +173,7 @@ private: IR::Block ir_block = A32::Translate(A32::LocationDescriptor{descriptor}, conf.callbacks, {conf.arch_version, conf.define_unpredictable_behaviour, conf.hook_hint_instructions}); Optimization::PolyfillPass(ir_block, polyfill_options); - if (conf.HasOptimization(OptimizationFlag::GetSetElimination)) { + if (conf.HasOptimization(OptimizationFlag::GetSetElimination) && !conf.check_halt_on_memory_access) { Optimization::A32GetSetElimination(ir_block); Optimization::DeadCodeElimination(ir_block); } diff --git a/src/dynarmic/backend/x64/a64_emit_x64.h b/src/dynarmic/backend/x64/a64_emit_x64.h index 644628dc..b5e1a746 100644 --- a/src/dynarmic/backend/x64/a64_emit_x64.h +++ b/src/dynarmic/backend/x64/a64_emit_x64.h @@ -108,6 +108,7 @@ protected: FakeCall FastmemCallback(u64 rip); // Memory access helpers + void EmitCheckMemoryAbort(A64EmitContext& ctx, IR::Inst* inst, Xbyak::Label* end = nullptr); template void EmitMemoryRead(A64EmitContext& ctx, IR::Inst* inst); template diff --git a/src/dynarmic/backend/x64/a64_emit_x64_memory.cpp b/src/dynarmic/backend/x64/a64_emit_x64_memory.cpp index ecfab343..7fdbcfe4 100644 --- a/src/dynarmic/backend/x64/a64_emit_x64_memory.cpp +++ b/src/dynarmic/backend/x64/a64_emit_x64_memory.cpp @@ -407,4 +407,32 @@ void A64EmitX64::EmitA64ExclusiveWriteMemory128(A64EmitContext& ctx, IR::Inst* i } } +void A64EmitX64::EmitCheckMemoryAbort(A64EmitContext&, IR::Inst* inst, Xbyak::Label* end) { + if (!conf.check_halt_on_memory_access) { + return; + } + + const A64::LocationDescriptor current_location{IR::LocationDescriptor{inst->GetArg(0).GetU64()}}; + + code.test(dword[r15 + offsetof(A64JitState, halt_reason)], static_cast(HaltReason::MemoryAbort)); + + Xbyak::Label memory_abort; + + if (!end) { + code.jnz(memory_abort, code.T_NEAR); + code.SwitchToFarCode(); + } else { + code.jz(*end, code.T_NEAR); + } + + code.L(memory_abort); + code.mov(rax, current_location.PC()); + code.mov(qword[r15 + offsetof(A64JitState, pc)], rax); + code.ForceReturnFromRunCode(); + + if (!end) { + code.SwitchToNearCode(); + } +} + } // namespace Dynarmic::Backend::X64 diff --git a/src/dynarmic/backend/x64/a64_interface.cpp b/src/dynarmic/backend/x64/a64_interface.cpp index 9a8714fb..0a7a45df 100644 --- a/src/dynarmic/backend/x64/a64_interface.cpp +++ b/src/dynarmic/backend/x64/a64_interface.cpp @@ -272,7 +272,7 @@ private: {conf.define_unpredictable_behaviour, conf.wall_clock_cntpct}); Optimization::PolyfillPass(ir_block, polyfill_options); Optimization::A64CallbackConfigPass(ir_block, conf); - if (conf.HasOptimization(OptimizationFlag::GetSetElimination)) { + if (conf.HasOptimization(OptimizationFlag::GetSetElimination) && !conf.check_halt_on_memory_access) { Optimization::A64GetSetElimination(ir_block); Optimization::DeadCodeElimination(ir_block); } diff --git a/src/dynarmic/backend/x64/emit_x64_memory.cpp.inc b/src/dynarmic/backend/x64/emit_x64_memory.cpp.inc index b82aabb1..4c18b437 100644 --- a/src/dynarmic/backend/x64/emit_x64_memory.cpp.inc +++ b/src/dynarmic/backend/x64/emit_x64_memory.cpp.inc @@ -52,26 +52,27 @@ FakeCall AxxEmitX64::FastmemCallback(u64 rip_) { template void AxxEmitX64::EmitMemoryRead(AxxEmitContext& ctx, IR::Inst* inst) { auto args = ctx.reg_alloc.GetArgumentInfo(inst); - const bool ordered = IsOrdered(args[1].GetImmediateAccType()); + const bool ordered = IsOrdered(args[2].GetImmediateAccType()); const auto fastmem_marker = ShouldFastmem(ctx, inst); if (!conf.page_table && !fastmem_marker) { // Neither fastmem nor page table: Use callbacks if constexpr (bitsize == 128) { - ctx.reg_alloc.HostCall(nullptr, {}, args[0]); + ctx.reg_alloc.HostCall(nullptr, {}, args[1]); if (ordered) { code.mfence(); } code.CallFunction(memory_read_128); ctx.reg_alloc.DefineValue(inst, xmm1); } else { - ctx.reg_alloc.HostCall(inst, {}, args[0]); + ctx.reg_alloc.HostCall(inst, {}, args[1]); if (ordered) { code.mfence(); } Devirtualize(conf.callbacks).EmitCall(code); code.ZeroExtendFrom(bitsize, code.ABI_RETURN); } + EmitCheckMemoryAbort(ctx, inst); return; } @@ -83,20 +84,24 @@ void AxxEmitX64::EmitMemoryRead(AxxEmitContext& ctx, IR::Inst* inst) { ctx.reg_alloc.ScratchGpr(HostLoc::RDX); } - const Xbyak::Reg64 vaddr = ctx.reg_alloc.UseGpr(args[0]); + const Xbyak::Reg64 vaddr = ctx.reg_alloc.UseGpr(args[1]); const int value_idx = bitsize == 128 ? ctx.reg_alloc.ScratchXmm().getIdx() : ctx.reg_alloc.ScratchGpr().getIdx(); const auto wrapped_fn = read_fallbacks[std::make_tuple(ordered, bitsize, vaddr.getIdx(), value_idx)]; Xbyak::Label abort, end; - bool require_abort_handling = false; if (fastmem_marker) { // Use fastmem + bool require_abort_handling; const auto src_ptr = EmitFastmemVAddr(code, ctx, abort, vaddr, require_abort_handling); const auto location = EmitReadMemoryMov(code, value_idx, src_ptr, ordered); + code.SwitchToFarCode(); + code.L(abort); + code.call(wrapped_fn); + fastmem_patch_info.emplace( mcl::bit_cast(location), FastmemPatchInfo{ @@ -105,22 +110,24 @@ void AxxEmitX64::EmitMemoryRead(AxxEmitContext& ctx, IR::Inst* inst) { *fastmem_marker, conf.recompile_on_fastmem_failure, }); + + EmitCheckMemoryAbort(ctx, inst, &end); + code.jmp(end, code.T_NEAR); + code.SwitchToNearCode(); } else { // Use page table ASSERT(conf.page_table); const auto src_ptr = EmitVAddrLookup(code, ctx, bitsize, abort, vaddr); - require_abort_handling = true; EmitReadMemoryMov(code, value_idx, src_ptr, ordered); - } - code.L(end); - if (require_abort_handling) { code.SwitchToFarCode(); code.L(abort); code.call(wrapped_fn); + EmitCheckMemoryAbort(ctx, inst, &end); code.jmp(end, code.T_NEAR); code.SwitchToNearCode(); } + code.L(end); if constexpr (bitsize == 128) { ctx.reg_alloc.DefineValue(inst, Xbyak::Xmm{value_idx}); @@ -132,24 +139,25 @@ void AxxEmitX64::EmitMemoryRead(AxxEmitContext& ctx, IR::Inst* inst) { template void AxxEmitX64::EmitMemoryWrite(AxxEmitContext& ctx, IR::Inst* inst) { auto args = ctx.reg_alloc.GetArgumentInfo(inst); - const bool ordered = IsOrdered(args[2].GetImmediateAccType()); + const bool ordered = IsOrdered(args[3].GetImmediateAccType()); const auto fastmem_marker = ShouldFastmem(ctx, inst); if (!conf.page_table && !fastmem_marker) { // Neither fastmem nor page table: Use callbacks if constexpr (bitsize == 128) { - ctx.reg_alloc.Use(args[0], ABI_PARAM2); - ctx.reg_alloc.Use(args[1], HostLoc::XMM1); + ctx.reg_alloc.Use(args[1], ABI_PARAM2); + ctx.reg_alloc.Use(args[2], HostLoc::XMM1); ctx.reg_alloc.EndOfAllocScope(); ctx.reg_alloc.HostCall(nullptr); code.CallFunction(memory_write_128); } else { - ctx.reg_alloc.HostCall(nullptr, {}, args[0], args[1]); + ctx.reg_alloc.HostCall(nullptr, {}, args[1], args[2]); Devirtualize(conf.callbacks).EmitCall(code); } if (ordered) { code.mfence(); } + EmitCheckMemoryAbort(ctx, inst); return; } @@ -161,22 +169,26 @@ void AxxEmitX64::EmitMemoryWrite(AxxEmitContext& ctx, IR::Inst* inst) { ctx.reg_alloc.ScratchGpr(HostLoc::RDX); } - const Xbyak::Reg64 vaddr = ctx.reg_alloc.UseGpr(args[0]); + const Xbyak::Reg64 vaddr = ctx.reg_alloc.UseGpr(args[1]); const int value_idx = bitsize == 128 - ? ctx.reg_alloc.UseXmm(args[1]).getIdx() - : (ordered ? ctx.reg_alloc.UseScratchGpr(args[1]).getIdx() : ctx.reg_alloc.UseGpr(args[1]).getIdx()); + ? ctx.reg_alloc.UseXmm(args[2]).getIdx() + : (ordered ? ctx.reg_alloc.UseScratchGpr(args[2]).getIdx() : ctx.reg_alloc.UseGpr(args[2]).getIdx()); const auto wrapped_fn = write_fallbacks[std::make_tuple(ordered, bitsize, vaddr.getIdx(), value_idx)]; Xbyak::Label abort, end; - bool require_abort_handling = false; if (fastmem_marker) { // Use fastmem + bool require_abort_handling; const auto dest_ptr = EmitFastmemVAddr(code, ctx, abort, vaddr, require_abort_handling); const auto location = EmitWriteMemoryMov(code, dest_ptr, value_idx, ordered); + code.SwitchToFarCode(); + code.L(abort); + code.call(wrapped_fn); + fastmem_patch_info.emplace( mcl::bit_cast(location), FastmemPatchInfo{ @@ -185,34 +197,36 @@ void AxxEmitX64::EmitMemoryWrite(AxxEmitContext& ctx, IR::Inst* inst) { *fastmem_marker, conf.recompile_on_fastmem_failure, }); + + EmitCheckMemoryAbort(ctx, inst, &end); + code.jmp(end, code.T_NEAR); + code.SwitchToNearCode(); } else { // Use page table ASSERT(conf.page_table); const auto dest_ptr = EmitVAddrLookup(code, ctx, bitsize, abort, vaddr); - require_abort_handling = true; EmitWriteMemoryMov(code, dest_ptr, value_idx, ordered); - } - code.L(end); - if (require_abort_handling) { code.SwitchToFarCode(); code.L(abort); code.call(wrapped_fn); + EmitCheckMemoryAbort(ctx, inst, &end); code.jmp(end, code.T_NEAR); code.SwitchToNearCode(); } + code.L(end); } template void AxxEmitX64::EmitExclusiveReadMemory(AxxEmitContext& ctx, IR::Inst* inst) { ASSERT(conf.global_monitor != nullptr); auto args = ctx.reg_alloc.GetArgumentInfo(inst); - const bool ordered = IsOrdered(args[1].GetImmediateAccType()); + const bool ordered = IsOrdered(args[2].GetImmediateAccType()); if constexpr (bitsize != 128) { using T = mcl::unsigned_integer_of_size; - ctx.reg_alloc.HostCall(inst, {}, args[0]); + ctx.reg_alloc.HostCall(inst, {}, args[1]); code.mov(code.byte[r15 + offsetof(AxxJitState, exclusive_state)], u8(1)); code.mov(code.ABI_PARAM1, reinterpret_cast(&conf)); @@ -228,7 +242,7 @@ void AxxEmitX64::EmitExclusiveReadMemory(AxxEmitContext& ctx, IR::Inst* inst) { code.ZeroExtendFrom(bitsize, code.ABI_RETURN); } else { const Xbyak::Xmm result = ctx.reg_alloc.ScratchXmm(); - ctx.reg_alloc.Use(args[0], ABI_PARAM2); + ctx.reg_alloc.Use(args[1], ABI_PARAM2); ctx.reg_alloc.EndOfAllocScope(); ctx.reg_alloc.HostCall(nullptr); @@ -250,19 +264,21 @@ void AxxEmitX64::EmitExclusiveReadMemory(AxxEmitContext& ctx, IR::Inst* inst) { ctx.reg_alloc.DefineValue(inst, result); } + + EmitCheckMemoryAbort(ctx, inst); } template void AxxEmitX64::EmitExclusiveWriteMemory(AxxEmitContext& ctx, IR::Inst* inst) { ASSERT(conf.global_monitor != nullptr); auto args = ctx.reg_alloc.GetArgumentInfo(inst); - const bool ordered = IsOrdered(args[2].GetImmediateAccType()); + const bool ordered = IsOrdered(args[3].GetImmediateAccType()); if constexpr (bitsize != 128) { - ctx.reg_alloc.HostCall(inst, {}, args[0], args[1]); + ctx.reg_alloc.HostCall(inst, {}, args[1], args[2]); } else { - ctx.reg_alloc.Use(args[0], ABI_PARAM2); - ctx.reg_alloc.Use(args[1], HostLoc::XMM1); + ctx.reg_alloc.Use(args[1], ABI_PARAM2); + ctx.reg_alloc.Use(args[2], HostLoc::XMM1); ctx.reg_alloc.EndOfAllocScope(); ctx.reg_alloc.HostCall(inst); } @@ -308,6 +324,8 @@ void AxxEmitX64::EmitExclusiveWriteMemory(AxxEmitContext& ctx, IR::Inst* inst) { ctx.reg_alloc.ReleaseStackSpace(16 + ABI_SHADOW_SPACE); } code.L(end); + + EmitCheckMemoryAbort(ctx, inst); } template @@ -329,7 +347,7 @@ void AxxEmitX64::EmitExclusiveReadMemoryInline(AxxEmitContext& ctx, IR::Inst* in ctx.reg_alloc.ScratchGpr(HostLoc::RDX); } - const Xbyak::Reg64 vaddr = ctx.reg_alloc.UseGpr(args[0]); + const Xbyak::Reg64 vaddr = ctx.reg_alloc.UseGpr(args[1]); const int value_idx = bitsize == 128 ? ctx.reg_alloc.ScratchXmm().getIdx() : ctx.reg_alloc.ScratchGpr().getIdx(); const Xbyak::Reg64 tmp = ctx.reg_alloc.ScratchGpr(); const Xbyak::Reg64 tmp2 = ctx.reg_alloc.ScratchGpr(); @@ -383,6 +401,8 @@ void AxxEmitX64::EmitExclusiveReadMemoryInline(AxxEmitContext& ctx, IR::Inst* in } else { ctx.reg_alloc.DefineValue(inst, Xbyak::Reg64{value_idx}); } + + EmitCheckMemoryAbort(ctx, inst); } template @@ -402,13 +422,13 @@ void AxxEmitX64::EmitExclusiveWriteMemoryInline(AxxEmitContext& ctx, IR::Inst* i ctx.reg_alloc.ScratchGpr(HostLoc::RBX); ctx.reg_alloc.ScratchGpr(HostLoc::RCX); ctx.reg_alloc.ScratchGpr(HostLoc::RDX); - return ctx.reg_alloc.UseXmm(args[1]); + return ctx.reg_alloc.UseXmm(args[2]); } else { ctx.reg_alloc.ScratchGpr(HostLoc::RAX); - return ctx.reg_alloc.UseGpr(args[1]); + return ctx.reg_alloc.UseGpr(args[2]); } }(); - const Xbyak::Reg64 vaddr = ctx.reg_alloc.UseGpr(args[0]); + const Xbyak::Reg64 vaddr = ctx.reg_alloc.UseGpr(args[1]); const Xbyak::Reg32 status = ctx.reg_alloc.ScratchGpr().cvt32(); const Xbyak::Reg64 tmp = ctx.reg_alloc.ScratchGpr(); @@ -513,6 +533,8 @@ void AxxEmitX64::EmitExclusiveWriteMemoryInline(AxxEmitContext& ctx, IR::Inst* i EmitExclusiveUnlock(code, conf, tmp, eax); ctx.reg_alloc.DefineValue(inst, status); + + EmitCheckMemoryAbort(ctx, inst); } #undef AxxEmitX64 diff --git a/src/dynarmic/frontend/A32/a32_ir_emitter.cpp b/src/dynarmic/frontend/A32/a32_ir_emitter.cpp index 9b9e645d..7f686b32 100644 --- a/src/dynarmic/frontend/A32/a32_ir_emitter.cpp +++ b/src/dynarmic/frontend/A32/a32_ir_emitter.cpp @@ -245,40 +245,40 @@ IR::UAny IREmitter::ReadMemory(size_t bitsize, const IR::U32& vaddr, IR::AccType } IR::U8 IREmitter::ReadMemory8(const IR::U32& vaddr, IR::AccType acc_type) { - return Inst(Opcode::A32ReadMemory8, vaddr, IR::Value{acc_type}); + return Inst(Opcode::A32ReadMemory8, ImmCurrentLocationDescriptor(), vaddr, IR::Value{acc_type}); } IR::U16 IREmitter::ReadMemory16(const IR::U32& vaddr, IR::AccType acc_type) { - const auto value = Inst(Opcode::A32ReadMemory16, vaddr, IR::Value{acc_type}); + const auto value = Inst(Opcode::A32ReadMemory16, ImmCurrentLocationDescriptor(), vaddr, IR::Value{acc_type}); return current_location.EFlag() ? ByteReverseHalf(value) : value; } IR::U32 IREmitter::ReadMemory32(const IR::U32& vaddr, IR::AccType acc_type) { - const auto value = Inst(Opcode::A32ReadMemory32, vaddr, IR::Value{acc_type}); + const auto value = Inst(Opcode::A32ReadMemory32, ImmCurrentLocationDescriptor(), vaddr, IR::Value{acc_type}); return current_location.EFlag() ? ByteReverseWord(value) : value; } IR::U64 IREmitter::ReadMemory64(const IR::U32& vaddr, IR::AccType acc_type) { - const auto value = Inst(Opcode::A32ReadMemory64, vaddr, IR::Value{acc_type}); + const auto value = Inst(Opcode::A32ReadMemory64, ImmCurrentLocationDescriptor(), vaddr, IR::Value{acc_type}); return current_location.EFlag() ? ByteReverseDual(value) : value; } IR::U8 IREmitter::ExclusiveReadMemory8(const IR::U32& vaddr, IR::AccType acc_type) { - return Inst(Opcode::A32ExclusiveReadMemory8, vaddr, IR::Value{acc_type}); + return Inst(Opcode::A32ExclusiveReadMemory8, ImmCurrentLocationDescriptor(), vaddr, IR::Value{acc_type}); } IR::U16 IREmitter::ExclusiveReadMemory16(const IR::U32& vaddr, IR::AccType acc_type) { - const auto value = Inst(Opcode::A32ExclusiveReadMemory16, vaddr, IR::Value{acc_type}); + const auto value = Inst(Opcode::A32ExclusiveReadMemory16, ImmCurrentLocationDescriptor(), vaddr, IR::Value{acc_type}); return current_location.EFlag() ? ByteReverseHalf(value) : value; } IR::U32 IREmitter::ExclusiveReadMemory32(const IR::U32& vaddr, IR::AccType acc_type) { - const auto value = Inst(Opcode::A32ExclusiveReadMemory32, vaddr, IR::Value{acc_type}); + const auto value = Inst(Opcode::A32ExclusiveReadMemory32, ImmCurrentLocationDescriptor(), vaddr, IR::Value{acc_type}); return current_location.EFlag() ? ByteReverseWord(value) : value; } std::pair IREmitter::ExclusiveReadMemory64(const IR::U32& vaddr, IR::AccType acc_type) { - const auto value = Inst(Opcode::A32ExclusiveReadMemory64, vaddr, IR::Value{acc_type}); + const auto value = Inst(Opcode::A32ExclusiveReadMemory64, ImmCurrentLocationDescriptor(), vaddr, IR::Value{acc_type}); const auto lo = LeastSignificantWord(value); const auto hi = MostSignificantWord(value).result; if (current_location.EFlag()) { @@ -303,55 +303,55 @@ void IREmitter::WriteMemory(size_t bitsize, const IR::U32& vaddr, const IR::UAny } void IREmitter::WriteMemory8(const IR::U32& vaddr, const IR::U8& value, IR::AccType acc_type) { - Inst(Opcode::A32WriteMemory8, vaddr, value, IR::Value{acc_type}); + Inst(Opcode::A32WriteMemory8, ImmCurrentLocationDescriptor(), vaddr, value, IR::Value{acc_type}); } void IREmitter::WriteMemory16(const IR::U32& vaddr, const IR::U16& value, IR::AccType acc_type) { if (current_location.EFlag()) { const auto v = ByteReverseHalf(value); - Inst(Opcode::A32WriteMemory16, vaddr, v, IR::Value{acc_type}); + Inst(Opcode::A32WriteMemory16, ImmCurrentLocationDescriptor(), vaddr, v, IR::Value{acc_type}); } else { - Inst(Opcode::A32WriteMemory16, vaddr, value, IR::Value{acc_type}); + Inst(Opcode::A32WriteMemory16, ImmCurrentLocationDescriptor(), vaddr, value, IR::Value{acc_type}); } } void IREmitter::WriteMemory32(const IR::U32& vaddr, const IR::U32& value, IR::AccType acc_type) { if (current_location.EFlag()) { const auto v = ByteReverseWord(value); - Inst(Opcode::A32WriteMemory32, vaddr, v, IR::Value{acc_type}); + Inst(Opcode::A32WriteMemory32, ImmCurrentLocationDescriptor(), vaddr, v, IR::Value{acc_type}); } else { - Inst(Opcode::A32WriteMemory32, vaddr, value, IR::Value{acc_type}); + Inst(Opcode::A32WriteMemory32, ImmCurrentLocationDescriptor(), vaddr, value, IR::Value{acc_type}); } } void IREmitter::WriteMemory64(const IR::U32& vaddr, const IR::U64& value, IR::AccType acc_type) { if (current_location.EFlag()) { const auto v = ByteReverseDual(value); - Inst(Opcode::A32WriteMemory64, vaddr, v, IR::Value{acc_type}); + Inst(Opcode::A32WriteMemory64, ImmCurrentLocationDescriptor(), vaddr, v, IR::Value{acc_type}); } else { - Inst(Opcode::A32WriteMemory64, vaddr, value, IR::Value{acc_type}); + Inst(Opcode::A32WriteMemory64, ImmCurrentLocationDescriptor(), vaddr, value, IR::Value{acc_type}); } } IR::U32 IREmitter::ExclusiveWriteMemory8(const IR::U32& vaddr, const IR::U8& value, IR::AccType acc_type) { - return Inst(Opcode::A32ExclusiveWriteMemory8, vaddr, value, IR::Value{acc_type}); + return Inst(Opcode::A32ExclusiveWriteMemory8, ImmCurrentLocationDescriptor(), vaddr, value, IR::Value{acc_type}); } IR::U32 IREmitter::ExclusiveWriteMemory16(const IR::U32& vaddr, const IR::U16& value, IR::AccType acc_type) { if (current_location.EFlag()) { const auto v = ByteReverseHalf(value); - return Inst(Opcode::A32ExclusiveWriteMemory16, vaddr, v, IR::Value{acc_type}); + return Inst(Opcode::A32ExclusiveWriteMemory16, ImmCurrentLocationDescriptor(), vaddr, v, IR::Value{acc_type}); } else { - return Inst(Opcode::A32ExclusiveWriteMemory16, vaddr, value, IR::Value{acc_type}); + return Inst(Opcode::A32ExclusiveWriteMemory16, ImmCurrentLocationDescriptor(), vaddr, value, IR::Value{acc_type}); } } IR::U32 IREmitter::ExclusiveWriteMemory32(const IR::U32& vaddr, const IR::U32& value, IR::AccType acc_type) { if (current_location.EFlag()) { const auto v = ByteReverseWord(value); - return Inst(Opcode::A32ExclusiveWriteMemory32, vaddr, v, IR::Value{acc_type}); + return Inst(Opcode::A32ExclusiveWriteMemory32, ImmCurrentLocationDescriptor(), vaddr, v, IR::Value{acc_type}); } else { - return Inst(Opcode::A32ExclusiveWriteMemory32, vaddr, value, IR::Value{acc_type}); + return Inst(Opcode::A32ExclusiveWriteMemory32, ImmCurrentLocationDescriptor(), vaddr, value, IR::Value{acc_type}); } } @@ -359,9 +359,9 @@ IR::U32 IREmitter::ExclusiveWriteMemory64(const IR::U32& vaddr, const IR::U32& v if (current_location.EFlag()) { const auto vlo = ByteReverseWord(value_lo); const auto vhi = ByteReverseWord(value_hi); - return Inst(Opcode::A32ExclusiveWriteMemory64, vaddr, Pack2x32To1x64(vlo, vhi), IR::Value{acc_type}); + return Inst(Opcode::A32ExclusiveWriteMemory64, ImmCurrentLocationDescriptor(), vaddr, Pack2x32To1x64(vlo, vhi), IR::Value{acc_type}); } else { - return Inst(Opcode::A32ExclusiveWriteMemory64, vaddr, Pack2x32To1x64(value_lo, value_hi), IR::Value{acc_type}); + return Inst(Opcode::A32ExclusiveWriteMemory64, ImmCurrentLocationDescriptor(), vaddr, Pack2x32To1x64(value_lo, value_hi), IR::Value{acc_type}); } } @@ -439,4 +439,8 @@ void IREmitter::CoprocStoreWords(size_t coproc_no, bool two, bool long_transfer, Inst(Opcode::A32CoprocStoreWords, IR::Value(coproc_info), address); } +IR::U64 IREmitter::ImmCurrentLocationDescriptor() { + return Imm64(IR::LocationDescriptor{current_location}.Value()); +} + } // namespace Dynarmic::A32 diff --git a/src/dynarmic/frontend/A32/a32_ir_emitter.h b/src/dynarmic/frontend/A32/a32_ir_emitter.h index 8cdebe49..96d162b0 100644 --- a/src/dynarmic/frontend/A32/a32_ir_emitter.h +++ b/src/dynarmic/frontend/A32/a32_ir_emitter.h @@ -110,6 +110,7 @@ public: private: enum ArchVersion arch_version; + IR::U64 ImmCurrentLocationDescriptor(); }; } // namespace Dynarmic::A32 diff --git a/src/dynarmic/frontend/A64/a64_ir_emitter.cpp b/src/dynarmic/frontend/A64/a64_ir_emitter.cpp index d0c038b4..cc471289 100644 --- a/src/dynarmic/frontend/A64/a64_ir_emitter.cpp +++ b/src/dynarmic/frontend/A64/a64_ir_emitter.cpp @@ -107,83 +107,83 @@ void IREmitter::ClearExclusive() { } IR::U8 IREmitter::ReadMemory8(const IR::U64& vaddr, IR::AccType acc_type) { - return Inst(Opcode::A64ReadMemory8, vaddr, IR::Value{acc_type}); + return Inst(Opcode::A64ReadMemory8, ImmCurrentLocationDescriptor(), vaddr, IR::Value{acc_type}); } IR::U16 IREmitter::ReadMemory16(const IR::U64& vaddr, IR::AccType acc_type) { - return Inst(Opcode::A64ReadMemory16, vaddr, IR::Value{acc_type}); + return Inst(Opcode::A64ReadMemory16, ImmCurrentLocationDescriptor(), vaddr, IR::Value{acc_type}); } IR::U32 IREmitter::ReadMemory32(const IR::U64& vaddr, IR::AccType acc_type) { - return Inst(Opcode::A64ReadMemory32, vaddr, IR::Value{acc_type}); + return Inst(Opcode::A64ReadMemory32, ImmCurrentLocationDescriptor(), vaddr, IR::Value{acc_type}); } IR::U64 IREmitter::ReadMemory64(const IR::U64& vaddr, IR::AccType acc_type) { - return Inst(Opcode::A64ReadMemory64, vaddr, IR::Value{acc_type}); + return Inst(Opcode::A64ReadMemory64, ImmCurrentLocationDescriptor(), vaddr, IR::Value{acc_type}); } IR::U128 IREmitter::ReadMemory128(const IR::U64& vaddr, IR::AccType acc_type) { - return Inst(Opcode::A64ReadMemory128, vaddr, IR::Value{acc_type}); + return Inst(Opcode::A64ReadMemory128, ImmCurrentLocationDescriptor(), vaddr, IR::Value{acc_type}); } IR::U8 IREmitter::ExclusiveReadMemory8(const IR::U64& vaddr, IR::AccType acc_type) { - return Inst(Opcode::A64ExclusiveReadMemory8, vaddr, IR::Value{acc_type}); + return Inst(Opcode::A64ExclusiveReadMemory8, ImmCurrentLocationDescriptor(), vaddr, IR::Value{acc_type}); } IR::U16 IREmitter::ExclusiveReadMemory16(const IR::U64& vaddr, IR::AccType acc_type) { - return Inst(Opcode::A64ExclusiveReadMemory16, vaddr, IR::Value{acc_type}); + return Inst(Opcode::A64ExclusiveReadMemory16, ImmCurrentLocationDescriptor(), vaddr, IR::Value{acc_type}); } IR::U32 IREmitter::ExclusiveReadMemory32(const IR::U64& vaddr, IR::AccType acc_type) { - return Inst(Opcode::A64ExclusiveReadMemory32, vaddr, IR::Value{acc_type}); + return Inst(Opcode::A64ExclusiveReadMemory32, ImmCurrentLocationDescriptor(), vaddr, IR::Value{acc_type}); } IR::U64 IREmitter::ExclusiveReadMemory64(const IR::U64& vaddr, IR::AccType acc_type) { - return Inst(Opcode::A64ExclusiveReadMemory64, vaddr, IR::Value{acc_type}); + return Inst(Opcode::A64ExclusiveReadMemory64, ImmCurrentLocationDescriptor(), vaddr, IR::Value{acc_type}); } IR::U128 IREmitter::ExclusiveReadMemory128(const IR::U64& vaddr, IR::AccType acc_type) { - return Inst(Opcode::A64ExclusiveReadMemory128, vaddr, IR::Value{acc_type}); + return Inst(Opcode::A64ExclusiveReadMemory128, ImmCurrentLocationDescriptor(), vaddr, IR::Value{acc_type}); } void IREmitter::WriteMemory8(const IR::U64& vaddr, const IR::U8& value, IR::AccType acc_type) { - Inst(Opcode::A64WriteMemory8, vaddr, value, IR::Value{acc_type}); + Inst(Opcode::A64WriteMemory8, ImmCurrentLocationDescriptor(), vaddr, value, IR::Value{acc_type}); } void IREmitter::WriteMemory16(const IR::U64& vaddr, const IR::U16& value, IR::AccType acc_type) { - Inst(Opcode::A64WriteMemory16, vaddr, value, IR::Value{acc_type}); + Inst(Opcode::A64WriteMemory16, ImmCurrentLocationDescriptor(), vaddr, value, IR::Value{acc_type}); } void IREmitter::WriteMemory32(const IR::U64& vaddr, const IR::U32& value, IR::AccType acc_type) { - Inst(Opcode::A64WriteMemory32, vaddr, value, IR::Value{acc_type}); + Inst(Opcode::A64WriteMemory32, ImmCurrentLocationDescriptor(), vaddr, value, IR::Value{acc_type}); } void IREmitter::WriteMemory64(const IR::U64& vaddr, const IR::U64& value, IR::AccType acc_type) { - Inst(Opcode::A64WriteMemory64, vaddr, value, IR::Value{acc_type}); + Inst(Opcode::A64WriteMemory64, ImmCurrentLocationDescriptor(), vaddr, value, IR::Value{acc_type}); } void IREmitter::WriteMemory128(const IR::U64& vaddr, const IR::U128& value, IR::AccType acc_type) { - Inst(Opcode::A64WriteMemory128, vaddr, value, IR::Value{acc_type}); + Inst(Opcode::A64WriteMemory128, ImmCurrentLocationDescriptor(), vaddr, value, IR::Value{acc_type}); } IR::U32 IREmitter::ExclusiveWriteMemory8(const IR::U64& vaddr, const IR::U8& value, IR::AccType acc_type) { - return Inst(Opcode::A64ExclusiveWriteMemory8, vaddr, value, IR::Value{acc_type}); + return Inst(Opcode::A64ExclusiveWriteMemory8, ImmCurrentLocationDescriptor(), vaddr, value, IR::Value{acc_type}); } IR::U32 IREmitter::ExclusiveWriteMemory16(const IR::U64& vaddr, const IR::U16& value, IR::AccType acc_type) { - return Inst(Opcode::A64ExclusiveWriteMemory16, vaddr, value, IR::Value{acc_type}); + return Inst(Opcode::A64ExclusiveWriteMemory16, ImmCurrentLocationDescriptor(), vaddr, value, IR::Value{acc_type}); } IR::U32 IREmitter::ExclusiveWriteMemory32(const IR::U64& vaddr, const IR::U32& value, IR::AccType acc_type) { - return Inst(Opcode::A64ExclusiveWriteMemory32, vaddr, value, IR::Value{acc_type}); + return Inst(Opcode::A64ExclusiveWriteMemory32, ImmCurrentLocationDescriptor(), vaddr, value, IR::Value{acc_type}); } IR::U32 IREmitter::ExclusiveWriteMemory64(const IR::U64& vaddr, const IR::U64& value, IR::AccType acc_type) { - return Inst(Opcode::A64ExclusiveWriteMemory64, vaddr, value, IR::Value{acc_type}); + return Inst(Opcode::A64ExclusiveWriteMemory64, ImmCurrentLocationDescriptor(), vaddr, value, IR::Value{acc_type}); } IR::U32 IREmitter::ExclusiveWriteMemory128(const IR::U64& vaddr, const IR::U128& value, IR::AccType acc_type) { - return Inst(Opcode::A64ExclusiveWriteMemory128, vaddr, value, IR::Value{acc_type}); + return Inst(Opcode::A64ExclusiveWriteMemory128, ImmCurrentLocationDescriptor(), vaddr, value, IR::Value{acc_type}); } IR::U32 IREmitter::GetW(Reg reg) { @@ -262,4 +262,8 @@ void IREmitter::SetPC(const IR::U64& value) { Inst(Opcode::A64SetPC, value); } +IR::U64 IREmitter::ImmCurrentLocationDescriptor() { + return Imm64(IR::LocationDescriptor{*current_location}.Value()); +} + } // namespace Dynarmic::A64 diff --git a/src/dynarmic/frontend/A64/a64_ir_emitter.h b/src/dynarmic/frontend/A64/a64_ir_emitter.h index 7bb71bea..2d3797a3 100644 --- a/src/dynarmic/frontend/A64/a64_ir_emitter.h +++ b/src/dynarmic/frontend/A64/a64_ir_emitter.h @@ -95,6 +95,9 @@ public: void SetFPCR(const IR::U32& value); void SetFPSR(const IR::U32& value); void SetPC(const IR::U64& value); + +private: + IR::U64 ImmCurrentLocationDescriptor(); }; } // namespace Dynarmic::A64 diff --git a/src/dynarmic/interface/A32/config.h b/src/dynarmic/interface/A32/config.h index 68759353..ef13b0e0 100644 --- a/src/dynarmic/interface/A32/config.h +++ b/src/dynarmic/interface/A32/config.h @@ -212,6 +212,10 @@ struct UserConfig { /// to avoid writting certain unnecessary code only needed for cycle timers. bool wall_clock_cntpct = false; + /// This allows accurately emulating protection fault handlers. If true, we check + /// for exit after every data memory access by the emulated program. + bool check_halt_on_memory_access = false; + /// This option allows you to disable cycle counting. If this is set to false, /// AddTicks and GetTicksRemaining are never called, and no cycle counting is done. bool enable_cycle_counting = true; diff --git a/src/dynarmic/interface/A64/config.h b/src/dynarmic/interface/A64/config.h index 1dc1ad2a..35b6b1ad 100644 --- a/src/dynarmic/interface/A64/config.h +++ b/src/dynarmic/interface/A64/config.h @@ -277,6 +277,10 @@ struct UserConfig { /// to avoid writting certain unnecessary code only needed for cycle timers. bool wall_clock_cntpct = false; + /// This allows accurately emulating protection fault handlers. If true, we check + /// for exit after every data memory access by the emulated program. + bool check_halt_on_memory_access = false; + /// This option allows you to disable cycle counting. If this is set to false, /// AddTicks and GetTicksRemaining are never called, and no cycle counting is done. bool enable_cycle_counting = true; diff --git a/src/dynarmic/interface/halt_reason.h b/src/dynarmic/interface/halt_reason.h index 28da2b13..121a8455 100644 --- a/src/dynarmic/interface/halt_reason.h +++ b/src/dynarmic/interface/halt_reason.h @@ -12,6 +12,7 @@ namespace Dynarmic { enum class HaltReason : std::uint32_t { Step = 0x00000001, CacheInvalidation = 0x00000002, + MemoryAbort = 0x00000004, UserDefined1 = 0x01000000, UserDefined2 = 0x02000000, UserDefined3 = 0x04000000, diff --git a/src/dynarmic/ir/opcodes.inc b/src/dynarmic/ir/opcodes.inc index 79719306..7642d1fc 100644 --- a/src/dynarmic/ir/opcodes.inc +++ b/src/dynarmic/ir/opcodes.inc @@ -688,45 +688,45 @@ OPCODE(FPVectorToUnsignedFixed64, U128, U128 // A32 Memory access A32OPC(ClearExclusive, Void, ) -A32OPC(ReadMemory8, U8, U32, AccType ) -A32OPC(ReadMemory16, U16, U32, AccType ) -A32OPC(ReadMemory32, U32, U32, AccType ) -A32OPC(ReadMemory64, U64, U32, AccType ) -A32OPC(ExclusiveReadMemory8, U8, U32, AccType ) -A32OPC(ExclusiveReadMemory16, U16, U32, AccType ) -A32OPC(ExclusiveReadMemory32, U32, U32, AccType ) -A32OPC(ExclusiveReadMemory64, U64, U32, AccType ) -A32OPC(WriteMemory8, Void, U32, U8, AccType ) -A32OPC(WriteMemory16, Void, U32, U16, AccType ) -A32OPC(WriteMemory32, Void, U32, U32, AccType ) -A32OPC(WriteMemory64, Void, U32, U64, AccType ) -A32OPC(ExclusiveWriteMemory8, U32, U32, U8, AccType ) -A32OPC(ExclusiveWriteMemory16, U32, U32, U16, AccType ) -A32OPC(ExclusiveWriteMemory32, U32, U32, U32, AccType ) -A32OPC(ExclusiveWriteMemory64, U32, U32, U64, AccType ) +A32OPC(ReadMemory8, U8, U64, U32, AccType ) +A32OPC(ReadMemory16, U16, U64, U32, AccType ) +A32OPC(ReadMemory32, U32, U64, U32, AccType ) +A32OPC(ReadMemory64, U64, U64, U32, AccType ) +A32OPC(ExclusiveReadMemory8, U8, U64, U32, AccType ) +A32OPC(ExclusiveReadMemory16, U16, U64, U32, AccType ) +A32OPC(ExclusiveReadMemory32, U32, U64, U32, AccType ) +A32OPC(ExclusiveReadMemory64, U64, U64, U32, AccType ) +A32OPC(WriteMemory8, Void, U64, U32, U8, AccType ) +A32OPC(WriteMemory16, Void, U64, U32, U16, AccType ) +A32OPC(WriteMemory32, Void, U64, U32, U32, AccType ) +A32OPC(WriteMemory64, Void, U64, U32, U64, AccType ) +A32OPC(ExclusiveWriteMemory8, U32, U64, U32, U8, AccType ) +A32OPC(ExclusiveWriteMemory16, U32, U64, U32, U16, AccType ) +A32OPC(ExclusiveWriteMemory32, U32, U64, U32, U32, AccType ) +A32OPC(ExclusiveWriteMemory64, U32, U64, U32, U64, AccType ) // A64 Memory access A64OPC(ClearExclusive, Void, ) -A64OPC(ReadMemory8, U8, U64, AccType ) -A64OPC(ReadMemory16, U16, U64, AccType ) -A64OPC(ReadMemory32, U32, U64, AccType ) -A64OPC(ReadMemory64, U64, U64, AccType ) -A64OPC(ReadMemory128, U128, U64, AccType ) -A64OPC(ExclusiveReadMemory8, U8, U64, AccType ) -A64OPC(ExclusiveReadMemory16, U16, U64, AccType ) -A64OPC(ExclusiveReadMemory32, U32, U64, AccType ) -A64OPC(ExclusiveReadMemory64, U64, U64, AccType ) -A64OPC(ExclusiveReadMemory128, U128, U64, AccType ) -A64OPC(WriteMemory8, Void, U64, U8, AccType ) -A64OPC(WriteMemory16, Void, U64, U16, AccType ) -A64OPC(WriteMemory32, Void, U64, U32, AccType ) -A64OPC(WriteMemory64, Void, U64, U64, AccType ) -A64OPC(WriteMemory128, Void, U64, U128, AccType ) -A64OPC(ExclusiveWriteMemory8, U32, U64, U8, AccType ) -A64OPC(ExclusiveWriteMemory16, U32, U64, U16, AccType ) -A64OPC(ExclusiveWriteMemory32, U32, U64, U32, AccType ) -A64OPC(ExclusiveWriteMemory64, U32, U64, U64, AccType ) -A64OPC(ExclusiveWriteMemory128, U32, U64, U128, AccType ) +A64OPC(ReadMemory8, U8, U64, U64, AccType ) +A64OPC(ReadMemory16, U16, U64, U64, AccType ) +A64OPC(ReadMemory32, U32, U64, U64, AccType ) +A64OPC(ReadMemory64, U64, U64, U64, AccType ) +A64OPC(ReadMemory128, U128, U64, U64, AccType ) +A64OPC(ExclusiveReadMemory8, U8, U64, U64, AccType ) +A64OPC(ExclusiveReadMemory16, U16, U64, U64, AccType ) +A64OPC(ExclusiveReadMemory32, U32, U64, U64, AccType ) +A64OPC(ExclusiveReadMemory64, U64, U64, U64, AccType ) +A64OPC(ExclusiveReadMemory128, U128, U64, U64, AccType ) +A64OPC(WriteMemory8, Void, U64, U64, U8, AccType ) +A64OPC(WriteMemory16, Void, U64, U64, U16, AccType ) +A64OPC(WriteMemory32, Void, U64, U64, U32, AccType ) +A64OPC(WriteMemory64, Void, U64, U64, U64, AccType ) +A64OPC(WriteMemory128, Void, U64, U64, U128, AccType ) +A64OPC(ExclusiveWriteMemory8, U32, U64, U64, U8, AccType ) +A64OPC(ExclusiveWriteMemory16, U32, U64, U64, U16, AccType ) +A64OPC(ExclusiveWriteMemory32, U32, U64, U64, U32, AccType ) +A64OPC(ExclusiveWriteMemory64, U32, U64, U64, U64, AccType ) +A64OPC(ExclusiveWriteMemory128, U32, U64, U64, U128, AccType ) // Coprocessor A32OPC(CoprocInternalOperation, Void, CoprocInfo ) diff --git a/src/dynarmic/ir/opt/a32_constant_memory_reads_pass.cpp b/src/dynarmic/ir/opt/a32_constant_memory_reads_pass.cpp index a3ff8568..828a7687 100644 --- a/src/dynarmic/ir/opt/a32_constant_memory_reads_pass.cpp +++ b/src/dynarmic/ir/opt/a32_constant_memory_reads_pass.cpp @@ -25,7 +25,7 @@ void A32ConstantMemoryReads(IR::Block& block, A32::UserCallbacks* cb) { break; } - const u32 vaddr = inst.GetArg(0).GetU32(); + const u32 vaddr = inst.GetArg(1).GetU32(); if (cb->IsReadOnlyMemory(vaddr)) { const u8 value_from_memory = cb->MemoryRead8(vaddr); inst.ReplaceUsesWith(IR::Value{value_from_memory}); @@ -37,7 +37,7 @@ void A32ConstantMemoryReads(IR::Block& block, A32::UserCallbacks* cb) { break; } - const u32 vaddr = inst.GetArg(0).GetU32(); + const u32 vaddr = inst.GetArg(1).GetU32(); if (cb->IsReadOnlyMemory(vaddr)) { const u16 value_from_memory = cb->MemoryRead16(vaddr); inst.ReplaceUsesWith(IR::Value{value_from_memory}); @@ -49,7 +49,7 @@ void A32ConstantMemoryReads(IR::Block& block, A32::UserCallbacks* cb) { break; } - const u32 vaddr = inst.GetArg(0).GetU32(); + const u32 vaddr = inst.GetArg(1).GetU32(); if (cb->IsReadOnlyMemory(vaddr)) { const u32 value_from_memory = cb->MemoryRead32(vaddr); inst.ReplaceUsesWith(IR::Value{value_from_memory}); @@ -61,7 +61,7 @@ void A32ConstantMemoryReads(IR::Block& block, A32::UserCallbacks* cb) { break; } - const u32 vaddr = inst.GetArg(0).GetU32(); + const u32 vaddr = inst.GetArg(1).GetU32(); if (cb->IsReadOnlyMemory(vaddr)) { const u64 value_from_memory = cb->MemoryRead64(vaddr); inst.ReplaceUsesWith(IR::Value{value_from_memory});