summaryrefslogtreecommitdiffstats
path: root/src/core/arm/dynarmic/arm_dynarmic_64.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/core/arm/dynarmic/arm_dynarmic_64.cpp')
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic_64.cpp320
1 files changed, 320 insertions, 0 deletions
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 <cinttypes>
+#include <memory>
+#include <dynarmic/A64/a64.h>
+#include <dynarmic/A64/config.h>
+#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<std::size_t>(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<u64>(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<Dynarmic::A64::Jit> 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<void**>(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<Dynarmic::A64::Jit>(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<DynarmicCallbacks64>(*this)), inner_unicorn{system},
+ core_index{core_index}, exclusive_monitor{
+ dynamic_cast<DynarmicExclusiveMonitor&>(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