// Copyright 2016 Citra Emulator Project // Licensed under GPLv2 or any later version // Refer to the license.txt file included. #include #include #include #include "common/assert.h" #include "common/microprofile.h" #include "core/arm/dynarmic/arm_dynarmic.h" #include "core/arm/dynarmic/arm_dynarmic_cp15.h" #include "core/arm/dynarmic/arm_exclusive_monitor.h" #include "core/arm/dynarmic/arm_tick_counts.h" #include "core/core.h" #include "core/core_timing.h" #include "core/gdbstub/gdbstub.h" #include "core/hle/kernel/svc.h" #include "core/memory.h" namespace Core { class DynarmicUserCallbacks final : public Dynarmic::A32::UserCallbacks { public: explicit DynarmicUserCallbacks(ARM_Dynarmic& parent) : parent(parent), svc_context(parent.system), memory(parent.memory) {} ~DynarmicUserCallbacks() = default; std::uint8_t MemoryRead8(VAddr vaddr) override { return memory.Read8(vaddr); } std::uint16_t MemoryRead16(VAddr vaddr) override { return memory.Read16(vaddr); } std::uint32_t MemoryRead32(VAddr vaddr) override { return memory.Read32(vaddr); } std::uint64_t MemoryRead64(VAddr vaddr) override { return memory.Read64(vaddr); } void MemoryWrite8(VAddr vaddr, std::uint8_t value) override { memory.Write8(vaddr, value); } void MemoryWrite16(VAddr vaddr, std::uint16_t value) override { memory.Write16(vaddr, value); } void MemoryWrite32(VAddr vaddr, std::uint32_t value) override { memory.Write32(vaddr, value); } void MemoryWrite64(VAddr vaddr, std::uint64_t value) override { memory.Write64(vaddr, value); } bool MemoryWriteExclusive8(u32 vaddr, u8 value, u8 expected) override { return memory.WriteExclusive8(vaddr, value, expected); } bool MemoryWriteExclusive16(u32 vaddr, u16 value, u16 expected) override { return memory.WriteExclusive16(vaddr, value, expected); } bool MemoryWriteExclusive32(u32 vaddr, u32 value, u32 expected) override { return memory.WriteExclusive32(vaddr, value, expected); } bool MemoryWriteExclusive64(u32 vaddr, u64 value, u64 expected) override { return memory.WriteExclusive64(vaddr, value, expected); } void InterpreterFallback(VAddr pc, std::size_t num_instructions) override { // Should never happen. UNREACHABLE_MSG("InterpeterFallback reached with pc = 0x{:08x}, code = 0x{:08x}, num = {}", pc, MemoryReadCode(pc).value(), num_instructions); } void CallSVC(std::uint32_t swi) override { svc_context.CallSVC(swi); } void ExceptionRaised(VAddr pc, Dynarmic::A32::Exception exception) override { switch (exception) { case Dynarmic::A32::Exception::UndefinedInstruction: case Dynarmic::A32::Exception::UnpredictableInstruction: case Dynarmic::A32::Exception::DecodeError: case Dynarmic::A32::Exception::NoExecuteFault: break; case Dynarmic::A32::Exception::Breakpoint: if (GDBStub::IsConnected()) { parent.jit->HaltExecution(); parent.SetPC(pc); parent.ServeBreak(); return; } break; case Dynarmic::A32::Exception::SendEvent: case Dynarmic::A32::Exception::SendEventLocal: case Dynarmic::A32::Exception::WaitForInterrupt: case Dynarmic::A32::Exception::WaitForEvent: case Dynarmic::A32::Exception::Yield: case Dynarmic::A32::Exception::PreloadData: case Dynarmic::A32::Exception::PreloadDataWithIntentToWrite: case Dynarmic::A32::Exception::PreloadInstruction: return; } ASSERT_MSG(false, "ExceptionRaised(exception = {}, pc = {:08X}, code = {:08X})", exception, pc, MemoryReadCode(pc).value()); } void AddTicks(std::uint64_t ticks) override { parent.GetTimer().AddTicks(ticks); } std::uint64_t GetTicksRemaining() override { s64 ticks = parent.GetTimer().GetDowncount(); return static_cast(ticks <= 0 ? 0 : ticks); } std::uint64_t GetTicksForCode(bool is_thumb, VAddr, std::uint32_t instruction) override { return Core::TicksForInstruction(is_thumb, instruction); } ARM_Dynarmic& parent; Kernel::SVCContext svc_context; Memory::MemorySystem& memory; }; ARM_Dynarmic::ARM_Dynarmic(Core::System& system_, Memory::MemorySystem& memory_, u32 core_id_, std::shared_ptr timer_, Core::ExclusiveMonitor& exclusive_monitor_) : ARM_Interface(core_id_, timer_), system(system_), memory(memory_), cb(std::make_unique(*this)), exclusive_monitor{dynamic_cast(exclusive_monitor_)} { SetPageTable(memory.GetCurrentPageTable()); } ARM_Dynarmic::~ARM_Dynarmic() = default; MICROPROFILE_DEFINE(ARM_Jit, "ARM JIT", "ARM JIT", MP_RGB(255, 64, 64)); void ARM_Dynarmic::Run() { ASSERT(memory.GetCurrentPageTable() == current_page_table); MICROPROFILE_SCOPE(ARM_Jit); jit->Run(); } void ARM_Dynarmic::Step() { jit->Step(); if (GDBStub::IsConnected()) { ServeBreak(); } } void ARM_Dynarmic::SetPC(u32 pc) { jit->Regs()[15] = pc; } u32 ARM_Dynarmic::GetPC() const { return jit->Regs()[15]; } u32 ARM_Dynarmic::GetReg(int index) const { return jit->Regs()[index]; } void ARM_Dynarmic::SetReg(int index, u32 value) { jit->Regs()[index] = value; } u32 ARM_Dynarmic::GetVFPReg(int index) const { return jit->ExtRegs()[index]; } void ARM_Dynarmic::SetVFPReg(int index, u32 value) { jit->ExtRegs()[index] = value; } u32 ARM_Dynarmic::GetVFPSystemReg(VFPSystemRegister reg) const { switch (reg) { case VFP_FPSCR: return jit->Fpscr(); case VFP_FPEXC: return fpexc; default: UNREACHABLE_MSG("Unknown VFP system register: {}", reg); } return UINT_MAX; } void ARM_Dynarmic::SetVFPSystemReg(VFPSystemRegister reg, u32 value) { switch (reg) { case VFP_FPSCR: jit->SetFpscr(value); return; case VFP_FPEXC: fpexc = value; return; default: UNREACHABLE_MSG("Unknown VFP system register: {}", reg); } } u32 ARM_Dynarmic::GetCPSR() const { return jit->Cpsr(); } void ARM_Dynarmic::SetCPSR(u32 cpsr) { jit->SetCpsr(cpsr); } u32 ARM_Dynarmic::GetCP15Register(CP15Register reg) const { switch (reg) { case CP15_THREAD_UPRW: return cp15_state.cp15_thread_uprw; case CP15_THREAD_URO: return cp15_state.cp15_thread_uro; default: UNREACHABLE_MSG("Unknown CP15 register: {}", reg); } return 0; } void ARM_Dynarmic::SetCP15Register(CP15Register reg, u32 value) { switch (reg) { case CP15_THREAD_UPRW: cp15_state.cp15_thread_uprw = value; return; case CP15_THREAD_URO: cp15_state.cp15_thread_uro = value; return; default: UNREACHABLE_MSG("Unknown CP15 register: {}", reg); } } void ARM_Dynarmic::SaveContext(ThreadContext& ctx) { ctx.cpu_registers = jit->Regs(); ctx.cpsr = jit->Cpsr(); ctx.fpu_registers = jit->ExtRegs(); ctx.fpscr = jit->Fpscr(); ctx.fpexc = fpexc; } void ARM_Dynarmic::LoadContext(const ThreadContext& ctx) { jit->Regs() = ctx.cpu_registers; jit->SetCpsr(ctx.cpsr); jit->ExtRegs() = ctx.fpu_registers; jit->SetFpscr(ctx.fpscr); fpexc = ctx.fpexc; } void ARM_Dynarmic::PrepareReschedule() { if (jit->IsExecuting()) { jit->HaltExecution(); } } void ARM_Dynarmic::ClearInstructionCache() { for (const auto& j : jits) { j.second->ClearCache(); } } void ARM_Dynarmic::InvalidateCacheRange(u32 start_address, std::size_t length) { jit->InvalidateCacheRange(start_address, length); } void ARM_Dynarmic::ClearExclusiveState() { jit->ClearExclusiveState(); } std::shared_ptr ARM_Dynarmic::GetPageTable() const { return current_page_table; } void ARM_Dynarmic::SetPageTable(const std::shared_ptr& page_table) { current_page_table = page_table; ThreadContext ctx{}; if (jit) { SaveContext(ctx); } auto iter = jits.find(current_page_table); if (iter != jits.end()) { jit = iter->second.get(); LoadContext(ctx); return; } auto new_jit = MakeJit(); jit = new_jit.get(); LoadContext(ctx); jits.emplace(current_page_table, std::move(new_jit)); } void ARM_Dynarmic::ServeBreak() { Kernel::Thread* thread = system.Kernel().GetCurrentThreadManager().GetCurrentThread(); SaveContext(thread->context); GDBStub::Break(); GDBStub::SendTrap(thread, 5); } std::unique_ptr ARM_Dynarmic::MakeJit() { Dynarmic::A32::UserConfig config; config.callbacks = cb.get(); if (current_page_table) { config.page_table = ¤t_page_table->GetPointerArray(); } config.coprocessors[15] = std::make_shared(cp15_state); config.define_unpredictable_behaviour = true; // Multi-process state config.processor_id = GetID(); config.global_monitor = &exclusive_monitor.monitor; return std::make_unique(config); } } // namespace Core