From c083ea7d7846ee40cfc889ed1d415f73ec78364c Mon Sep 17 00:00:00 2001 From: bunnei Date: Sun, 1 Mar 2020 23:46:10 -0500 Subject: core: Implement separate A32/A64 ARM interfaces. --- src/core/arm/arm_interface.h | 32 +-- src/core/arm/dynarmic/arm_dynarmic.cpp | 320 ------------------------------ src/core/arm/dynarmic/arm_dynarmic.h | 96 --------- src/core/arm/dynarmic/arm_dynarmic_32.cpp | 208 +++++++++++++++++++ src/core/arm/dynarmic/arm_dynarmic_32.h | 77 +++++++ src/core/arm/dynarmic/arm_dynarmic_64.cpp | 320 ++++++++++++++++++++++++++++++ src/core/arm/dynarmic/arm_dynarmic_64.h | 98 +++++++++ src/core/arm/exclusive_monitor.cpp | 2 +- src/core/arm/unicorn/arm_unicorn.cpp | 8 +- src/core/arm/unicorn/arm_unicorn.h | 7 +- 10 files changed, 732 insertions(+), 436 deletions(-) delete mode 100644 src/core/arm/dynarmic/arm_dynarmic.cpp delete mode 100644 src/core/arm/dynarmic/arm_dynarmic.h create mode 100644 src/core/arm/dynarmic/arm_dynarmic_32.cpp create mode 100644 src/core/arm/dynarmic/arm_dynarmic_32.h create mode 100644 src/core/arm/dynarmic/arm_dynarmic_64.cpp create mode 100644 src/core/arm/dynarmic/arm_dynarmic_64.h (limited to 'src/core/arm') diff --git a/src/core/arm/arm_interface.h b/src/core/arm/arm_interface.h index 47b964eb7..57eae839e 100644 --- a/src/core/arm/arm_interface.h +++ b/src/core/arm/arm_interface.h @@ -25,7 +25,20 @@ public: explicit ARM_Interface(System& system_) : system{system_} {} virtual ~ARM_Interface() = default; - struct ThreadContext { + struct ThreadContext32 { + std::array cpu_registers; + u32 cpsr; + std::array padding; + std::array fprs; + u32 fpscr; + u32 fpexc; + u32 tpidr; + }; + // Internally within the kernel, it expects the AArch32 version of the + // thread context to be 344 bytes in size. + static_assert(sizeof(ThreadContext32) == 0x158); + + struct ThreadContext64 { std::array cpu_registers; u64 sp; u64 pc; @@ -38,7 +51,7 @@ public: }; // Internally within the kernel, it expects the AArch64 version of the // thread context to be 800 bytes in size. - static_assert(sizeof(ThreadContext) == 0x320); + static_assert(sizeof(ThreadContext64) == 0x320); /// Runs the CPU until an event happens virtual void Run() = 0; @@ -130,17 +143,10 @@ public: */ virtual void SetTPIDR_EL0(u64 value) = 0; - /** - * Saves the current CPU context - * @param ctx Thread context to save - */ - virtual void SaveContext(ThreadContext& ctx) = 0; - - /** - * Loads a CPU context - * @param ctx Thread context to load - */ - virtual void LoadContext(const ThreadContext& ctx) = 0; + virtual void SaveContext(ThreadContext32& ctx) = 0; + virtual void SaveContext(ThreadContext64& ctx) = 0; + virtual void LoadContext(const ThreadContext32& ctx) = 0; + virtual void LoadContext(const ThreadContext64& ctx) = 0; /// Clears the exclusive monitor's state. virtual void ClearExclusiveState() = 0; diff --git a/src/core/arm/dynarmic/arm_dynarmic.cpp b/src/core/arm/dynarmic/arm_dynarmic.cpp deleted file mode 100644 index 7c9d59ab8..000000000 --- a/src/core/arm/dynarmic/arm_dynarmic.cpp +++ /dev/null @@ -1,320 +0,0 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#include -#include -#include -#include -#include "common/logging/log.h" -#include "common/microprofile.h" -#include "core/arm/dynarmic/arm_dynarmic.h" -#include "core/core.h" -#include "core/core_manager.h" -#include "core/core_timing.h" -#include "core/core_timing_util.h" -#include "core/gdbstub/gdbstub.h" -#include "core/hardware_properties.h" -#include "core/hle/kernel/process.h" -#include "core/hle/kernel/scheduler.h" -#include "core/hle/kernel/svc.h" -#include "core/hle/kernel/vm_manager.h" -#include "core/memory.h" - -namespace Core { - -using Vector = Dynarmic::A64::Vector; - -class ARM_Dynarmic_Callbacks : public Dynarmic::A64::UserCallbacks { -public: - explicit ARM_Dynarmic_Callbacks(ARM_Dynarmic& parent) : parent(parent) {} - - u8 MemoryRead8(u64 vaddr) override { - return parent.system.Memory().Read8(vaddr); - } - u16 MemoryRead16(u64 vaddr) override { - return parent.system.Memory().Read16(vaddr); - } - u32 MemoryRead32(u64 vaddr) override { - return parent.system.Memory().Read32(vaddr); - } - u64 MemoryRead64(u64 vaddr) override { - return parent.system.Memory().Read64(vaddr); - } - Vector MemoryRead128(u64 vaddr) override { - auto& memory = parent.system.Memory(); - return {memory.Read64(vaddr), memory.Read64(vaddr + 8)}; - } - - void MemoryWrite8(u64 vaddr, u8 value) override { - parent.system.Memory().Write8(vaddr, value); - } - void MemoryWrite16(u64 vaddr, u16 value) override { - parent.system.Memory().Write16(vaddr, value); - } - void MemoryWrite32(u64 vaddr, u32 value) override { - parent.system.Memory().Write32(vaddr, value); - } - void MemoryWrite64(u64 vaddr, u64 value) override { - parent.system.Memory().Write64(vaddr, value); - } - void MemoryWrite128(u64 vaddr, Vector value) override { - auto& memory = parent.system.Memory(); - memory.Write64(vaddr, value[0]); - memory.Write64(vaddr + 8, value[1]); - } - - void InterpreterFallback(u64 pc, std::size_t num_instructions) override { - LOG_INFO(Core_ARM, "Unicorn fallback @ 0x{:X} for {} instructions (instr = {:08X})", pc, - num_instructions, MemoryReadCode(pc)); - - ARM_Interface::ThreadContext ctx; - parent.SaveContext(ctx); - parent.inner_unicorn.LoadContext(ctx); - parent.inner_unicorn.ExecuteInstructions(num_instructions); - parent.inner_unicorn.SaveContext(ctx); - parent.LoadContext(ctx); - num_interpreted_instructions += num_instructions; - } - - void ExceptionRaised(u64 pc, Dynarmic::A64::Exception exception) override { - switch (exception) { - case Dynarmic::A64::Exception::WaitForInterrupt: - case Dynarmic::A64::Exception::WaitForEvent: - case Dynarmic::A64::Exception::SendEvent: - case Dynarmic::A64::Exception::SendEventLocal: - case Dynarmic::A64::Exception::Yield: - return; - case Dynarmic::A64::Exception::Breakpoint: - if (GDBStub::IsServerEnabled()) { - parent.jit->HaltExecution(); - parent.SetPC(pc); - Kernel::Thread* const thread = parent.system.CurrentScheduler().GetCurrentThread(); - parent.SaveContext(thread->GetContext()); - GDBStub::Break(); - GDBStub::SendTrap(thread, 5); - return; - } - [[fallthrough]]; - default: - ASSERT_MSG(false, "ExceptionRaised(exception = {}, pc = {:X})", - static_cast(exception), pc); - } - } - - void CallSVC(u32 swi) override { - Kernel::CallSVC(parent.system, swi); - } - - void AddTicks(u64 ticks) override { - // Divide the number of ticks by the amount of CPU cores. TODO(Subv): This yields only a - // rough approximation of the amount of executed ticks in the system, it may be thrown off - // if not all cores are doing a similar amount of work. Instead of doing this, we should - // device a way so that timing is consistent across all cores without increasing the ticks 4 - // times. - u64 amortized_ticks = (ticks - num_interpreted_instructions) / Core::NUM_CPU_CORES; - // Always execute at least one tick. - amortized_ticks = std::max(amortized_ticks, 1); - - parent.system.CoreTiming().AddTicks(amortized_ticks); - num_interpreted_instructions = 0; - } - u64 GetTicksRemaining() override { - return std::max(parent.system.CoreTiming().GetDowncount(), s64{0}); - } - u64 GetCNTPCT() override { - return Timing::CpuCyclesToClockCycles(parent.system.CoreTiming().GetTicks()); - } - - ARM_Dynarmic& parent; - std::size_t num_interpreted_instructions = 0; - u64 tpidrro_el0 = 0; - u64 tpidr_el0 = 0; -}; - -std::shared_ptr ARM_Dynarmic::MakeJit(Common::PageTable& page_table, - std::size_t address_space_bits) const { - Dynarmic::A64::UserConfig config; - - // Callbacks - config.callbacks = cb.get(); - - // Memory - config.page_table = reinterpret_cast(page_table.pointers.data()); - config.page_table_address_space_bits = address_space_bits; - config.silently_mirror_page_table = false; - config.absolute_offset_page_table = true; - - // Multi-process state - config.processor_id = core_index; - config.global_monitor = &exclusive_monitor.monitor; - - // System registers - config.tpidrro_el0 = &cb->tpidrro_el0; - config.tpidr_el0 = &cb->tpidr_el0; - config.dczid_el0 = 4; - config.ctr_el0 = 0x8444c004; - config.cntfrq_el0 = Hardware::CNTFREQ; - - // Unpredictable instructions - config.define_unpredictable_behaviour = true; - - return std::make_shared(config); -} - -MICROPROFILE_DEFINE(ARM_Jit_Dynarmic, "ARM JIT", "Dynarmic", MP_RGB(255, 64, 64)); - -void ARM_Dynarmic::Run() { - MICROPROFILE_SCOPE(ARM_Jit_Dynarmic); - - jit->Run(); -} - -void ARM_Dynarmic::Step() { - cb->InterpreterFallback(jit->GetPC(), 1); -} - -ARM_Dynarmic::ARM_Dynarmic(System& system, ExclusiveMonitor& exclusive_monitor, - std::size_t core_index) - : ARM_Interface{system}, - cb(std::make_unique(*this)), inner_unicorn{system}, - core_index{core_index}, exclusive_monitor{ - dynamic_cast(exclusive_monitor)} {} - -ARM_Dynarmic::~ARM_Dynarmic() = default; - -void ARM_Dynarmic::SetPC(u64 pc) { - jit->SetPC(pc); -} - -u64 ARM_Dynarmic::GetPC() const { - return jit->GetPC(); -} - -u64 ARM_Dynarmic::GetReg(int index) const { - return jit->GetRegister(index); -} - -void ARM_Dynarmic::SetReg(int index, u64 value) { - jit->SetRegister(index, value); -} - -u128 ARM_Dynarmic::GetVectorReg(int index) const { - return jit->GetVector(index); -} - -void ARM_Dynarmic::SetVectorReg(int index, u128 value) { - jit->SetVector(index, value); -} - -u32 ARM_Dynarmic::GetPSTATE() const { - return jit->GetPstate(); -} - -void ARM_Dynarmic::SetPSTATE(u32 pstate) { - jit->SetPstate(pstate); -} - -u64 ARM_Dynarmic::GetTlsAddress() const { - return cb->tpidrro_el0; -} - -void ARM_Dynarmic::SetTlsAddress(VAddr address) { - cb->tpidrro_el0 = address; -} - -u64 ARM_Dynarmic::GetTPIDR_EL0() const { - return cb->tpidr_el0; -} - -void ARM_Dynarmic::SetTPIDR_EL0(u64 value) { - cb->tpidr_el0 = value; -} - -void ARM_Dynarmic::SaveContext(ThreadContext& ctx) { - ctx.cpu_registers = jit->GetRegisters(); - ctx.sp = jit->GetSP(); - ctx.pc = jit->GetPC(); - ctx.pstate = jit->GetPstate(); - ctx.vector_registers = jit->GetVectors(); - ctx.fpcr = jit->GetFpcr(); - ctx.fpsr = jit->GetFpsr(); - ctx.tpidr = cb->tpidr_el0; -} - -void ARM_Dynarmic::LoadContext(const ThreadContext& ctx) { - jit->SetRegisters(ctx.cpu_registers); - jit->SetSP(ctx.sp); - jit->SetPC(ctx.pc); - jit->SetPstate(ctx.pstate); - jit->SetVectors(ctx.vector_registers); - jit->SetFpcr(ctx.fpcr); - jit->SetFpsr(ctx.fpsr); - SetTPIDR_EL0(ctx.tpidr); -} - -void ARM_Dynarmic::PrepareReschedule() { - jit->HaltExecution(); -} - -void ARM_Dynarmic::ClearInstructionCache() { - jit->ClearCache(); -} - -void ARM_Dynarmic::ClearExclusiveState() { - jit->ClearExclusiveState(); -} - -void ARM_Dynarmic::PageTableChanged(Common::PageTable& page_table, - std::size_t new_address_space_size_in_bits) { - auto key = std::make_pair(&page_table, new_address_space_size_in_bits); - auto iter = jit_cache.find(key); - if (iter != jit_cache.end()) { - jit = iter->second; - return; - } - jit = MakeJit(page_table, new_address_space_size_in_bits); - jit_cache.emplace(key, jit); -} - -DynarmicExclusiveMonitor::DynarmicExclusiveMonitor(Memory::Memory& memory_, std::size_t core_count) - : monitor(core_count), memory{memory_} {} - -DynarmicExclusiveMonitor::~DynarmicExclusiveMonitor() = default; - -void DynarmicExclusiveMonitor::SetExclusive(std::size_t core_index, VAddr addr) { - // Size doesn't actually matter. - monitor.Mark(core_index, addr, 16); -} - -void DynarmicExclusiveMonitor::ClearExclusive() { - monitor.Clear(); -} - -bool DynarmicExclusiveMonitor::ExclusiveWrite8(std::size_t core_index, VAddr vaddr, u8 value) { - return monitor.DoExclusiveOperation(core_index, vaddr, 1, [&] { memory.Write8(vaddr, value); }); -} - -bool DynarmicExclusiveMonitor::ExclusiveWrite16(std::size_t core_index, VAddr vaddr, u16 value) { - return monitor.DoExclusiveOperation(core_index, vaddr, 2, - [&] { memory.Write16(vaddr, value); }); -} - -bool DynarmicExclusiveMonitor::ExclusiveWrite32(std::size_t core_index, VAddr vaddr, u32 value) { - return monitor.DoExclusiveOperation(core_index, vaddr, 4, - [&] { memory.Write32(vaddr, value); }); -} - -bool DynarmicExclusiveMonitor::ExclusiveWrite64(std::size_t core_index, VAddr vaddr, u64 value) { - return monitor.DoExclusiveOperation(core_index, vaddr, 8, - [&] { memory.Write64(vaddr, value); }); -} - -bool DynarmicExclusiveMonitor::ExclusiveWrite128(std::size_t core_index, VAddr vaddr, u128 value) { - return monitor.DoExclusiveOperation(core_index, vaddr, 16, [&] { - memory.Write64(vaddr + 0, value[0]); - memory.Write64(vaddr + 8, value[1]); - }); -} - -} // namespace Core diff --git a/src/core/arm/dynarmic/arm_dynarmic.h b/src/core/arm/dynarmic/arm_dynarmic.h deleted file mode 100644 index ffbb69d76..000000000 --- a/src/core/arm/dynarmic/arm_dynarmic.h +++ /dev/null @@ -1,96 +0,0 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#pragma once - -#include -#include - -#include -#include -#include "common/common_types.h" -#include "common/hash.h" -#include "core/arm/arm_interface.h" -#include "core/arm/exclusive_monitor.h" -#include "core/arm/unicorn/arm_unicorn.h" - -namespace Memory { -class Memory; -} - -namespace Core { - -class ARM_Dynarmic_Callbacks; -class DynarmicExclusiveMonitor; -class System; - -using JitCacheKey = std::pair; -using JitCacheType = - std::unordered_map, Common::PairHash>; - -class ARM_Dynarmic final : public ARM_Interface { -public: - ARM_Dynarmic(System& system, ExclusiveMonitor& exclusive_monitor, std::size_t core_index); - ~ARM_Dynarmic() override; - - void SetPC(u64 pc) override; - u64 GetPC() const override; - u64 GetReg(int index) const override; - void SetReg(int index, u64 value) override; - u128 GetVectorReg(int index) const override; - void SetVectorReg(int index, u128 value) override; - u32 GetPSTATE() const override; - void SetPSTATE(u32 pstate) override; - void Run() override; - void Step() override; - VAddr GetTlsAddress() const override; - void SetTlsAddress(VAddr address) override; - void SetTPIDR_EL0(u64 value) override; - u64 GetTPIDR_EL0() const override; - - void SaveContext(ThreadContext& ctx) override; - void LoadContext(const ThreadContext& ctx) override; - - void PrepareReschedule() override; - void ClearExclusiveState() override; - - void ClearInstructionCache() override; - void PageTableChanged(Common::PageTable& new_page_table, - std::size_t new_address_space_size_in_bits) override; - -private: - std::shared_ptr MakeJit(Common::PageTable& page_table, - std::size_t address_space_bits) const; - - friend class ARM_Dynarmic_Callbacks; - std::unique_ptr cb; - JitCacheType jit_cache; - std::shared_ptr jit; - ARM_Unicorn inner_unicorn; - - std::size_t core_index; - DynarmicExclusiveMonitor& exclusive_monitor; -}; - -class DynarmicExclusiveMonitor final : public ExclusiveMonitor { -public: - explicit DynarmicExclusiveMonitor(Memory::Memory& memory_, std::size_t core_count); - ~DynarmicExclusiveMonitor() override; - - void SetExclusive(std::size_t core_index, VAddr addr) override; - void ClearExclusive() override; - - bool ExclusiveWrite8(std::size_t core_index, VAddr vaddr, u8 value) override; - bool ExclusiveWrite16(std::size_t core_index, VAddr vaddr, u16 value) override; - bool ExclusiveWrite32(std::size_t core_index, VAddr vaddr, u32 value) override; - bool ExclusiveWrite64(std::size_t core_index, VAddr vaddr, u64 value) override; - bool ExclusiveWrite128(std::size_t core_index, VAddr vaddr, u128 value) override; - -private: - friend class ARM_Dynarmic; - Dynarmic::A64::ExclusiveMonitor monitor; - Memory::Memory& memory; -}; - -} // namespace Core diff --git a/src/core/arm/dynarmic/arm_dynarmic_32.cpp b/src/core/arm/dynarmic/arm_dynarmic_32.cpp new file mode 100644 index 000000000..187a972ac --- /dev/null +++ b/src/core/arm/dynarmic/arm_dynarmic_32.cpp @@ -0,0 +1,208 @@ +// Copyright 2020 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include +#include +#include +#include +#include +#include "common/microprofile.h" +#include "core/arm/dynarmic/arm_dynarmic_32.h" +#include "core/arm/dynarmic/arm_dynarmic_64.h" +#include "core/arm/dynarmic/arm_dynarmic_cp15.h" +#include "core/core.h" +#include "core/core_manager.h" +#include "core/core_timing.h" +#include "core/hle/kernel/svc.h" +#include "core/memory.h" + +namespace Core { + +class DynarmicCallbacks32 : public Dynarmic::A32::UserCallbacks { +public: + explicit DynarmicCallbacks32(ARM_Dynarmic_32& parent) : parent(parent) {} + + u8 MemoryRead8(u32 vaddr) override { + return parent.system.Memory().Read8(vaddr); + } + u16 MemoryRead16(u32 vaddr) override { + return parent.system.Memory().Read16(vaddr); + } + u32 MemoryRead32(u32 vaddr) override { + return parent.system.Memory().Read32(vaddr); + } + u64 MemoryRead64(u32 vaddr) override { + return parent.system.Memory().Read64(vaddr); + } + + void MemoryWrite8(u32 vaddr, u8 value) override { + parent.system.Memory().Write8(vaddr, value); + } + void MemoryWrite16(u32 vaddr, u16 value) override { + parent.system.Memory().Write16(vaddr, value); + } + void MemoryWrite32(u32 vaddr, u32 value) override { + parent.system.Memory().Write32(vaddr, value); + } + void MemoryWrite64(u32 vaddr, u64 value) override { + parent.system.Memory().Write64(vaddr, value); + } + + void InterpreterFallback(u32 pc, std::size_t num_instructions) override { + UNIMPLEMENTED(); + } + + void ExceptionRaised(u32 pc, Dynarmic::A32::Exception exception) override { + switch (exception) { + case Dynarmic::A32::Exception::UndefinedInstruction: + case Dynarmic::A32::Exception::UnpredictableInstruction: + break; + case Dynarmic::A32::Exception::Breakpoint: + break; + } + LOG_CRITICAL(HW_GPU, "ExceptionRaised(exception = {}, pc = {:08X}, code = {:08X})", + static_cast(exception), pc, MemoryReadCode(pc)); + UNIMPLEMENTED(); + } + + void CallSVC(u32 swi) override { + Kernel::CallSVC(parent.system, swi); + } + + void AddTicks(u64 ticks) override { + // Divide the number of ticks by the amount of CPU cores. TODO(Subv): This yields only a + // rough approximation of the amount of executed ticks in the system, it may be thrown off + // if not all cores are doing a similar amount of work. Instead of doing this, we should + // device a way so that timing is consistent across all cores without increasing the ticks 4 + // times. + u64 amortized_ticks = (ticks - num_interpreted_instructions) / Core::NUM_CPU_CORES; + // Always execute at least one tick. + amortized_ticks = std::max(amortized_ticks, 1); + + parent.system.CoreTiming().AddTicks(amortized_ticks); + num_interpreted_instructions = 0; + } + u64 GetTicksRemaining() override { + return std::max(parent.system.CoreTiming().GetDowncount(), {}); + } + + ARM_Dynarmic_32& parent; + std::size_t num_interpreted_instructions{}; + u64 tpidrro_el0{}; + u64 tpidr_el0{}; +}; + +std::shared_ptr ARM_Dynarmic_32::MakeJit(Common::PageTable& page_table, + std::size_t address_space_bits) const { + Dynarmic::A32::UserConfig config; + config.callbacks = cb.get(); + // TODO(bunnei): Implement page table for 32-bit + // config.page_table = &page_table.pointers; + config.coprocessors[15] = std::make_shared((u32*)&CP15_regs[0]); + config.define_unpredictable_behaviour = true; + return std::make_unique(config); +} + +MICROPROFILE_DEFINE(ARM_Jit_Dynarmic_32, "ARM JIT", "Dynarmic", MP_RGB(255, 64, 64)); + +void ARM_Dynarmic_32::Run() { + MICROPROFILE_SCOPE(ARM_Jit_Dynarmic_32); + jit->Run(); +} + +void ARM_Dynarmic_32::Step() { + cb->InterpreterFallback(jit->Regs()[15], 1); +} + +ARM_Dynarmic_32::ARM_Dynarmic_32(System& system, ExclusiveMonitor& exclusive_monitor, + std::size_t core_index) + : ARM_Interface{system}, + cb(std::make_unique(*this)), core_index{core_index}, + exclusive_monitor{dynamic_cast(exclusive_monitor)} {} + +ARM_Dynarmic_32::~ARM_Dynarmic_32() = default; + +void ARM_Dynarmic_32::SetPC(u64 pc) { + jit->Regs()[15] = static_cast(pc); +} + +u64 ARM_Dynarmic_32::GetPC() const { + return jit->Regs()[15]; +} + +u64 ARM_Dynarmic_32::GetReg(int index) const { + return jit->Regs()[index]; +} + +void ARM_Dynarmic_32::SetReg(int index, u64 value) { + jit->Regs()[index] = static_cast(value); +} + +u128 ARM_Dynarmic_32::GetVectorReg(int index) const { + return {}; +} + +void ARM_Dynarmic_32::SetVectorReg(int index, u128 value) {} + +u32 ARM_Dynarmic_32::GetPSTATE() const { + return jit->Cpsr(); +} + +void ARM_Dynarmic_32::SetPSTATE(u32 cpsr) { + jit->SetCpsr(cpsr); +} + +u64 ARM_Dynarmic_32::GetTlsAddress() const { + return CP15_regs[static_cast(CP15Register::CP15_THREAD_URO)]; +} + +void ARM_Dynarmic_32::SetTlsAddress(VAddr address) { + CP15_regs[static_cast(CP15Register::CP15_THREAD_URO)] = static_cast(address); +} + +u64 ARM_Dynarmic_32::GetTPIDR_EL0() const { + return cb->tpidr_el0; +} + +void ARM_Dynarmic_32::SetTPIDR_EL0(u64 value) { + cb->tpidr_el0 = value; +} + +void ARM_Dynarmic_32::SaveContext(ThreadContext32& ctx) { + Dynarmic::A32::Context context; + jit->SaveContext(context); + ctx.cpu_registers = context.Regs(); + ctx.cpsr = context.Cpsr(); +} + +void ARM_Dynarmic_32::LoadContext(const ThreadContext32& ctx) { + Dynarmic::A32::Context context; + context.Regs() = ctx.cpu_registers; + context.SetCpsr(ctx.cpsr); + jit->LoadContext(context); +} + +void ARM_Dynarmic_32::PrepareReschedule() { + jit->HaltExecution(); +} + +void ARM_Dynarmic_32::ClearInstructionCache() { + jit->ClearCache(); +} + +void ARM_Dynarmic_32::ClearExclusiveState() {} + +void ARM_Dynarmic_32::PageTableChanged(Common::PageTable& page_table, + std::size_t new_address_space_size_in_bits) { + auto key = std::make_pair(&page_table, new_address_space_size_in_bits); + auto iter = jit_cache.find(key); + if (iter != jit_cache.end()) { + jit = iter->second; + return; + } + jit = MakeJit(page_table, new_address_space_size_in_bits); + jit_cache.emplace(key, jit); +} + +} // namespace Core diff --git a/src/core/arm/dynarmic/arm_dynarmic_32.h b/src/core/arm/dynarmic/arm_dynarmic_32.h new file mode 100644 index 000000000..143e46e4d --- /dev/null +++ b/src/core/arm/dynarmic/arm_dynarmic_32.h @@ -0,0 +1,77 @@ +// Copyright 2020 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include +#include + +#include +#include +#include +#include "common/common_types.h" +#include "common/hash.h" +#include "core/arm/arm_interface.h" +#include "core/arm/exclusive_monitor.h" + +namespace Memory { +class Memory; +} + +namespace Core { + +class DynarmicCallbacks32; +class DynarmicExclusiveMonitor; +class System; + +class ARM_Dynarmic_32 final : public ARM_Interface { +public: + ARM_Dynarmic_32(System& system, ExclusiveMonitor& exclusive_monitor, std::size_t core_index); + ~ARM_Dynarmic_32() override; + + void SetPC(u64 pc) override; + u64 GetPC() const override; + u64 GetReg(int index) const override; + void SetReg(int index, u64 value) override; + u128 GetVectorReg(int index) const override; + void SetVectorReg(int index, u128 value) override; + u32 GetPSTATE() const override; + void SetPSTATE(u32 pstate) override; + void Run() override; + void Step() override; + VAddr GetTlsAddress() const override; + void SetTlsAddress(VAddr address) override; + void SetTPIDR_EL0(u64 value) override; + u64 GetTPIDR_EL0() const override; + + void SaveContext(ThreadContext32& ctx) override; + void SaveContext(ThreadContext64& ctx) override {} + void LoadContext(const ThreadContext32& ctx) override; + void LoadContext(const ThreadContext64& ctx) override {} + + void PrepareReschedule() override; + void ClearExclusiveState() override; + + void ClearInstructionCache() override; + void PageTableChanged(Common::PageTable& new_page_table, + std::size_t new_address_space_size_in_bits) override; + +private: + std::shared_ptr MakeJit(Common::PageTable& page_table, + std::size_t address_space_bits) const; + + using JitCacheKey = std::pair; + using JitCacheType = + std::unordered_map, Common::PairHash>; + + friend class DynarmicCallbacks32; + std::unique_ptr cb; + JitCacheType jit_cache; + std::shared_ptr jit; + std::size_t core_index; + DynarmicExclusiveMonitor& exclusive_monitor; + std::array CP15_regs{}; +}; + +} // namespace Core diff --git a/src/core/arm/dynarmic/arm_dynarmic_64.cpp b/src/core/arm/dynarmic/arm_dynarmic_64.cpp new file mode 100644 index 000000000..a53a58ba0 --- /dev/null +++ b/src/core/arm/dynarmic/arm_dynarmic_64.cpp @@ -0,0 +1,320 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include +#include +#include +#include +#include "common/logging/log.h" +#include "common/microprofile.h" +#include "core/arm/dynarmic/arm_dynarmic_64.h" +#include "core/core.h" +#include "core/core_manager.h" +#include "core/core_timing.h" +#include "core/core_timing_util.h" +#include "core/gdbstub/gdbstub.h" +#include "core/hardware_properties.h" +#include "core/hle/kernel/process.h" +#include "core/hle/kernel/scheduler.h" +#include "core/hle/kernel/svc.h" +#include "core/hle/kernel/vm_manager.h" +#include "core/memory.h" + +namespace Core { + +using Vector = Dynarmic::A64::Vector; + +class DynarmicCallbacks64 : public Dynarmic::A64::UserCallbacks { +public: + explicit DynarmicCallbacks64(ARM_Dynarmic_64& parent) : parent(parent) {} + + u8 MemoryRead8(u64 vaddr) override { + return parent.system.Memory().Read8(vaddr); + } + u16 MemoryRead16(u64 vaddr) override { + return parent.system.Memory().Read16(vaddr); + } + u32 MemoryRead32(u64 vaddr) override { + return parent.system.Memory().Read32(vaddr); + } + u64 MemoryRead64(u64 vaddr) override { + return parent.system.Memory().Read64(vaddr); + } + Vector MemoryRead128(u64 vaddr) override { + auto& memory = parent.system.Memory(); + return {memory.Read64(vaddr), memory.Read64(vaddr + 8)}; + } + + void MemoryWrite8(u64 vaddr, u8 value) override { + parent.system.Memory().Write8(vaddr, value); + } + void MemoryWrite16(u64 vaddr, u16 value) override { + parent.system.Memory().Write16(vaddr, value); + } + void MemoryWrite32(u64 vaddr, u32 value) override { + parent.system.Memory().Write32(vaddr, value); + } + void MemoryWrite64(u64 vaddr, u64 value) override { + parent.system.Memory().Write64(vaddr, value); + } + void MemoryWrite128(u64 vaddr, Vector value) override { + auto& memory = parent.system.Memory(); + memory.Write64(vaddr, value[0]); + memory.Write64(vaddr + 8, value[1]); + } + + void InterpreterFallback(u64 pc, std::size_t num_instructions) override { + LOG_INFO(Core_ARM, "Unicorn fallback @ 0x{:X} for {} instructions (instr = {:08X})", pc, + num_instructions, MemoryReadCode(pc)); + + ARM_Interface::ThreadContext64 ctx; + parent.SaveContext(ctx); + parent.inner_unicorn.LoadContext(ctx); + parent.inner_unicorn.ExecuteInstructions(num_instructions); + parent.inner_unicorn.SaveContext(ctx); + parent.LoadContext(ctx); + num_interpreted_instructions += num_instructions; + } + + void ExceptionRaised(u64 pc, Dynarmic::A64::Exception exception) override { + switch (exception) { + case Dynarmic::A64::Exception::WaitForInterrupt: + case Dynarmic::A64::Exception::WaitForEvent: + case Dynarmic::A64::Exception::SendEvent: + case Dynarmic::A64::Exception::SendEventLocal: + case Dynarmic::A64::Exception::Yield: + return; + case Dynarmic::A64::Exception::Breakpoint: + if (GDBStub::IsServerEnabled()) { + parent.jit->HaltExecution(); + parent.SetPC(pc); + Kernel::Thread* const thread = parent.system.CurrentScheduler().GetCurrentThread(); + parent.SaveContext(thread->GetContext64()); + GDBStub::Break(); + GDBStub::SendTrap(thread, 5); + return; + } + [[fallthrough]]; + default: + ASSERT_MSG(false, "ExceptionRaised(exception = {}, pc = {:X})", + static_cast(exception), pc); + } + } + + void CallSVC(u32 swi) override { + Kernel::CallSVC(parent.system, swi); + } + + void AddTicks(u64 ticks) override { + // Divide the number of ticks by the amount of CPU cores. TODO(Subv): This yields only a + // rough approximation of the amount of executed ticks in the system, it may be thrown off + // if not all cores are doing a similar amount of work. Instead of doing this, we should + // device a way so that timing is consistent across all cores without increasing the ticks 4 + // times. + u64 amortized_ticks = (ticks - num_interpreted_instructions) / Core::NUM_CPU_CORES; + // Always execute at least one tick. + amortized_ticks = std::max(amortized_ticks, 1); + + parent.system.CoreTiming().AddTicks(amortized_ticks); + num_interpreted_instructions = 0; + } + u64 GetTicksRemaining() override { + return std::max(parent.system.CoreTiming().GetDowncount(), s64{0}); + } + u64 GetCNTPCT() override { + return Timing::CpuCyclesToClockCycles(parent.system.CoreTiming().GetTicks()); + } + + ARM_Dynarmic_64& parent; + std::size_t num_interpreted_instructions = 0; + u64 tpidrro_el0 = 0; + u64 tpidr_el0 = 0; +}; + +std::shared_ptr ARM_Dynarmic_64::MakeJit(Common::PageTable& page_table, + std::size_t address_space_bits) const { + Dynarmic::A64::UserConfig config; + + // Callbacks + config.callbacks = cb.get(); + + // Memory + config.page_table = reinterpret_cast(page_table.pointers.data()); + config.page_table_address_space_bits = address_space_bits; + config.silently_mirror_page_table = false; + config.absolute_offset_page_table = true; + + // Multi-process state + config.processor_id = core_index; + config.global_monitor = &exclusive_monitor.monitor; + + // System registers + config.tpidrro_el0 = &cb->tpidrro_el0; + config.tpidr_el0 = &cb->tpidr_el0; + config.dczid_el0 = 4; + config.ctr_el0 = 0x8444c004; + config.cntfrq_el0 = Hardware::CNTFREQ; + + // Unpredictable instructions + config.define_unpredictable_behaviour = true; + + return std::make_shared(config); +} + +MICROPROFILE_DEFINE(ARM_Jit_Dynarmic_64, "ARM JIT", "Dynarmic", MP_RGB(255, 64, 64)); + +void ARM_Dynarmic_64::Run() { + MICROPROFILE_SCOPE(ARM_Jit_Dynarmic_64); + + jit->Run(); +} + +void ARM_Dynarmic_64::Step() { + cb->InterpreterFallback(jit->GetPC(), 1); +} + +ARM_Dynarmic_64::ARM_Dynarmic_64(System& system, ExclusiveMonitor& exclusive_monitor, + std::size_t core_index) + : ARM_Interface{system}, + cb(std::make_unique(*this)), inner_unicorn{system}, + core_index{core_index}, exclusive_monitor{ + dynamic_cast(exclusive_monitor)} {} + +ARM_Dynarmic_64::~ARM_Dynarmic_64() = default; + +void ARM_Dynarmic_64::SetPC(u64 pc) { + jit->SetPC(pc); +} + +u64 ARM_Dynarmic_64::GetPC() const { + return jit->GetPC(); +} + +u64 ARM_Dynarmic_64::GetReg(int index) const { + return jit->GetRegister(index); +} + +void ARM_Dynarmic_64::SetReg(int index, u64 value) { + jit->SetRegister(index, value); +} + +u128 ARM_Dynarmic_64::GetVectorReg(int index) const { + return jit->GetVector(index); +} + +void ARM_Dynarmic_64::SetVectorReg(int index, u128 value) { + jit->SetVector(index, value); +} + +u32 ARM_Dynarmic_64::GetPSTATE() const { + return jit->GetPstate(); +} + +void ARM_Dynarmic_64::SetPSTATE(u32 pstate) { + jit->SetPstate(pstate); +} + +u64 ARM_Dynarmic_64::GetTlsAddress() const { + return cb->tpidrro_el0; +} + +void ARM_Dynarmic_64::SetTlsAddress(VAddr address) { + cb->tpidrro_el0 = address; +} + +u64 ARM_Dynarmic_64::GetTPIDR_EL0() const { + return cb->tpidr_el0; +} + +void ARM_Dynarmic_64::SetTPIDR_EL0(u64 value) { + cb->tpidr_el0 = value; +} + +void ARM_Dynarmic_64::SaveContext(ThreadContext64& ctx) { + ctx.cpu_registers = jit->GetRegisters(); + ctx.sp = jit->GetSP(); + ctx.pc = jit->GetPC(); + ctx.pstate = jit->GetPstate(); + ctx.vector_registers = jit->GetVectors(); + ctx.fpcr = jit->GetFpcr(); + ctx.fpsr = jit->GetFpsr(); + ctx.tpidr = cb->tpidr_el0; +} + +void ARM_Dynarmic_64::LoadContext(const ThreadContext64& ctx) { + jit->SetRegisters(ctx.cpu_registers); + jit->SetSP(ctx.sp); + jit->SetPC(ctx.pc); + jit->SetPstate(ctx.pstate); + jit->SetVectors(ctx.vector_registers); + jit->SetFpcr(ctx.fpcr); + jit->SetFpsr(ctx.fpsr); + SetTPIDR_EL0(ctx.tpidr); +} + +void ARM_Dynarmic_64::PrepareReschedule() { + jit->HaltExecution(); +} + +void ARM_Dynarmic_64::ClearInstructionCache() { + jit->ClearCache(); +} + +void ARM_Dynarmic_64::ClearExclusiveState() { + jit->ClearExclusiveState(); +} + +void ARM_Dynarmic_64::PageTableChanged(Common::PageTable& page_table, + std::size_t new_address_space_size_in_bits) { + auto key = std::make_pair(&page_table, new_address_space_size_in_bits); + auto iter = jit_cache.find(key); + if (iter != jit_cache.end()) { + jit = iter->second; + return; + } + jit = MakeJit(page_table, new_address_space_size_in_bits); + jit_cache.emplace(key, jit); +} + +DynarmicExclusiveMonitor::DynarmicExclusiveMonitor(Memory::Memory& memory, std::size_t core_count) + : monitor(core_count), memory{memory} {} + +DynarmicExclusiveMonitor::~DynarmicExclusiveMonitor() = default; + +void DynarmicExclusiveMonitor::SetExclusive(std::size_t core_index, VAddr addr) { + // Size doesn't actually matter. + monitor.Mark(core_index, addr, 16); +} + +void DynarmicExclusiveMonitor::ClearExclusive() { + monitor.Clear(); +} + +bool DynarmicExclusiveMonitor::ExclusiveWrite8(std::size_t core_index, VAddr vaddr, u8 value) { + return monitor.DoExclusiveOperation(core_index, vaddr, 1, [&] { memory.Write8(vaddr, value); }); +} + +bool DynarmicExclusiveMonitor::ExclusiveWrite16(std::size_t core_index, VAddr vaddr, u16 value) { + return monitor.DoExclusiveOperation(core_index, vaddr, 2, + [&] { memory.Write16(vaddr, value); }); +} + +bool DynarmicExclusiveMonitor::ExclusiveWrite32(std::size_t core_index, VAddr vaddr, u32 value) { + return monitor.DoExclusiveOperation(core_index, vaddr, 4, + [&] { memory.Write32(vaddr, value); }); +} + +bool DynarmicExclusiveMonitor::ExclusiveWrite64(std::size_t core_index, VAddr vaddr, u64 value) { + return monitor.DoExclusiveOperation(core_index, vaddr, 8, + [&] { memory.Write64(vaddr, value); }); +} + +bool DynarmicExclusiveMonitor::ExclusiveWrite128(std::size_t core_index, VAddr vaddr, u128 value) { + return monitor.DoExclusiveOperation(core_index, vaddr, 16, [&] { + memory.Write64(vaddr + 0, value[0]); + memory.Write64(vaddr + 8, value[1]); + }); +} + +} // namespace Core diff --git a/src/core/arm/dynarmic/arm_dynarmic_64.h b/src/core/arm/dynarmic/arm_dynarmic_64.h new file mode 100644 index 000000000..e71240a96 --- /dev/null +++ b/src/core/arm/dynarmic/arm_dynarmic_64.h @@ -0,0 +1,98 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include +#include + +#include +#include +#include "common/common_types.h" +#include "common/hash.h" +#include "core/arm/arm_interface.h" +#include "core/arm/exclusive_monitor.h" +#include "core/arm/unicorn/arm_unicorn.h" + +namespace Memory { +class Memory; +} + +namespace Core { + +class DynarmicCallbacks64; +class DynarmicExclusiveMonitor; +class System; + +class ARM_Dynarmic_64 final : public ARM_Interface { +public: + ARM_Dynarmic_64(System& system, ExclusiveMonitor& exclusive_monitor, std::size_t core_index); + ~ARM_Dynarmic_64() override; + + void SetPC(u64 pc) override; + u64 GetPC() const override; + u64 GetReg(int index) const override; + void SetReg(int index, u64 value) override; + u128 GetVectorReg(int index) const override; + void SetVectorReg(int index, u128 value) override; + u32 GetPSTATE() const override; + void SetPSTATE(u32 pstate) override; + void Run() override; + void Step() override; + VAddr GetTlsAddress() const override; + void SetTlsAddress(VAddr address) override; + void SetTPIDR_EL0(u64 value) override; + u64 GetTPIDR_EL0() const override; + + void SaveContext(ThreadContext32& ctx) override {} + void SaveContext(ThreadContext64& ctx) override; + void LoadContext(const ThreadContext32& ctx) override {} + void LoadContext(const ThreadContext64& ctx) override; + + void PrepareReschedule() override; + void ClearExclusiveState() override; + + void ClearInstructionCache() override; + void PageTableChanged(Common::PageTable& new_page_table, + std::size_t new_address_space_size_in_bits) override; + +private: + std::shared_ptr MakeJit(Common::PageTable& page_table, + std::size_t address_space_bits) const; + + using JitCacheKey = std::pair; + using JitCacheType = + std::unordered_map, Common::PairHash>; + + friend class DynarmicCallbacks64; + std::unique_ptr cb; + JitCacheType jit_cache; + std::shared_ptr jit; + ARM_Unicorn inner_unicorn; + + std::size_t core_index; + DynarmicExclusiveMonitor& exclusive_monitor; +}; + +class DynarmicExclusiveMonitor final : public ExclusiveMonitor { +public: + explicit DynarmicExclusiveMonitor(Memory::Memory& memory, std::size_t core_count); + ~DynarmicExclusiveMonitor() override; + + void SetExclusive(std::size_t core_index, VAddr addr) override; + void ClearExclusive() override; + + bool ExclusiveWrite8(std::size_t core_index, VAddr vaddr, u8 value) override; + bool ExclusiveWrite16(std::size_t core_index, VAddr vaddr, u16 value) override; + bool ExclusiveWrite32(std::size_t core_index, VAddr vaddr, u32 value) override; + bool ExclusiveWrite64(std::size_t core_index, VAddr vaddr, u64 value) override; + bool ExclusiveWrite128(std::size_t core_index, VAddr vaddr, u128 value) override; + +private: + friend class ARM_Dynarmic_64; + Dynarmic::A64::ExclusiveMonitor monitor; + Memory::Memory& memory; +}; + +} // namespace Core diff --git a/src/core/arm/exclusive_monitor.cpp b/src/core/arm/exclusive_monitor.cpp index 94570e520..b32401e0b 100644 --- a/src/core/arm/exclusive_monitor.cpp +++ b/src/core/arm/exclusive_monitor.cpp @@ -3,7 +3,7 @@ // Refer to the license.txt file included. #ifdef ARCHITECTURE_x86_64 -#include "core/arm/dynarmic/arm_dynarmic.h" +#include "core/arm/dynarmic/arm_dynarmic_64.h" #endif #include "core/arm/exclusive_monitor.h" #include "core/memory.h" diff --git a/src/core/arm/unicorn/arm_unicorn.cpp b/src/core/arm/unicorn/arm_unicorn.cpp index f99ad5802..8a9800a96 100644 --- a/src/core/arm/unicorn/arm_unicorn.cpp +++ b/src/core/arm/unicorn/arm_unicorn.cpp @@ -53,7 +53,7 @@ static bool UnmappedMemoryHook(uc_engine* uc, uc_mem_type type, u64 addr, int si void* user_data) { auto* const system = static_cast(user_data); - ARM_Interface::ThreadContext ctx{}; + ARM_Interface::ThreadContext64 ctx{}; system->CurrentArmInterface().SaveContext(ctx); ASSERT_MSG(false, "Attempted to read from unmapped memory: 0x{:X}, pc=0x{:X}, lr=0x{:X}", addr, ctx.pc, ctx.cpu_registers[30]); @@ -179,7 +179,7 @@ void ARM_Unicorn::ExecuteInstructions(std::size_t num_instructions) { } Kernel::Thread* const thread = system.CurrentScheduler().GetCurrentThread(); - SaveContext(thread->GetContext()); + SaveContext(thread->GetContext64()); if (last_bkpt_hit || GDBStub::IsMemoryBreak() || GDBStub::GetCpuStepFlag()) { last_bkpt_hit = false; GDBStub::Break(); @@ -188,7 +188,7 @@ void ARM_Unicorn::ExecuteInstructions(std::size_t num_instructions) { } } -void ARM_Unicorn::SaveContext(ThreadContext& ctx) { +void ARM_Unicorn::SaveContext(ThreadContext64& ctx) { int uregs[32]; void* tregs[32]; @@ -215,7 +215,7 @@ void ARM_Unicorn::SaveContext(ThreadContext& ctx) { CHECKED(uc_reg_read_batch(uc, uregs, tregs, 32)); } -void ARM_Unicorn::LoadContext(const ThreadContext& ctx) { +void ARM_Unicorn::LoadContext(const ThreadContext64& ctx) { int uregs[32]; void* tregs[32]; diff --git a/src/core/arm/unicorn/arm_unicorn.h b/src/core/arm/unicorn/arm_unicorn.h index 3c5b155f9..f30d13cb6 100644 --- a/src/core/arm/unicorn/arm_unicorn.h +++ b/src/core/arm/unicorn/arm_unicorn.h @@ -30,8 +30,6 @@ public: void SetTlsAddress(VAddr address) override; void SetTPIDR_EL0(u64 value) override; u64 GetTPIDR_EL0() const override; - void SaveContext(ThreadContext& ctx) override; - void LoadContext(const ThreadContext& ctx) override; void PrepareReschedule() override; void ClearExclusiveState() override; void ExecuteInstructions(std::size_t num_instructions); @@ -41,6 +39,11 @@ public: void PageTableChanged(Common::PageTable&, std::size_t) override {} void RecordBreak(GDBStub::BreakpointAddress bkpt); + void SaveContext(ThreadContext32& ctx) override {} + void SaveContext(ThreadContext64& ctx) override; + void LoadContext(const ThreadContext32& ctx) override {} + void LoadContext(const ThreadContext64& ctx) override; + private: static void InterruptHook(uc_engine* uc, u32 int_no, void* user_data); -- cgit v1.2.3