summaryrefslogtreecommitdiffstats
path: root/src/core/arm
diff options
context:
space:
mode:
Diffstat (limited to 'src/core/arm')
-rw-r--r--src/core/arm/arm_interface.cpp332
-rw-r--r--src/core/arm/arm_interface.h69
-rw-r--r--src/core/arm/cpu_interrupt_handler.cpp25
-rw-r--r--src/core/arm/cpu_interrupt_handler.h40
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic_32.cpp311
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic_32.h41
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic_64.cpp333
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic_64.h41
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic_cp15.cpp38
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic_cp15.h7
-rw-r--r--src/core/arm/dynarmic/arm_exclusive_monitor.cpp9
-rw-r--r--src/core/arm/dynarmic/arm_exclusive_monitor.h9
-rw-r--r--src/core/arm/exclusive_monitor.cpp5
-rw-r--r--src/core/arm/exclusive_monitor.h7
-rw-r--r--src/core/arm/symbols.cpp130
-rw-r--r--src/core/arm/symbols.h26
16 files changed, 896 insertions, 527 deletions
diff --git a/src/core/arm/arm_interface.cpp b/src/core/arm/arm_interface.cpp
index 0951e1976..953d96439 100644
--- a/src/core/arm/arm_interface.cpp
+++ b/src/core/arm/arm_interface.cpp
@@ -1,6 +1,9 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#ifndef _MSC_VER
+#include <cxxabi.h>
+#endif
#include <map>
#include <optional>
@@ -8,170 +11,43 @@
#include "common/common_types.h"
#include "common/logging/log.h"
#include "core/arm/arm_interface.h"
+#include "core/arm/symbols.h"
#include "core/core.h"
+#include "core/debugger/debugger.h"
+#include "core/hle/kernel/k_process.h"
+#include "core/hle/kernel/svc.h"
#include "core/loader/loader.h"
#include "core/memory.h"
-namespace Core {
-namespace {
-
-constexpr u64 ELF_DYNAMIC_TAG_NULL = 0;
-constexpr u64 ELF_DYNAMIC_TAG_STRTAB = 5;
-constexpr u64 ELF_DYNAMIC_TAG_SYMTAB = 6;
-constexpr u64 ELF_DYNAMIC_TAG_SYMENT = 11;
-
-enum class ELFSymbolType : u8 {
- None = 0,
- Object = 1,
- Function = 2,
- Section = 3,
- File = 4,
- Common = 5,
- TLS = 6,
-};
-
-enum class ELFSymbolBinding : u8 {
- Local = 0,
- Global = 1,
- Weak = 2,
-};
-
-enum class ELFSymbolVisibility : u8 {
- Default = 0,
- Internal = 1,
- Hidden = 2,
- Protected = 3,
-};
-
-struct ELFSymbol {
- u32 name_index;
- union {
- u8 info;
-
- BitField<0, 4, ELFSymbolType> type;
- BitField<4, 4, ELFSymbolBinding> binding;
- };
- ELFSymbolVisibility visibility;
- u16 sh_index;
- u64 value;
- u64 size;
-};
-static_assert(sizeof(ELFSymbol) == 0x18, "ELFSymbol has incorrect size.");
-
-using Symbols = std::vector<std::pair<ELFSymbol, std::string>>;
-
-Symbols GetSymbols(VAddr text_offset, Core::Memory::Memory& memory) {
- const auto mod_offset = text_offset + memory.Read32(text_offset + 4);
-
- if (mod_offset < text_offset || (mod_offset & 0b11) != 0 ||
- memory.Read32(mod_offset) != Common::MakeMagic('M', 'O', 'D', '0')) {
- return {};
- }
-
- const auto dynamic_offset = memory.Read32(mod_offset + 0x4) + mod_offset;
-
- VAddr string_table_offset{};
- VAddr symbol_table_offset{};
- u64 symbol_entry_size{};
-
- VAddr dynamic_index = dynamic_offset;
- while (true) {
- const u64 tag = memory.Read64(dynamic_index);
- const u64 value = memory.Read64(dynamic_index + 0x8);
- dynamic_index += 0x10;
-
- if (tag == ELF_DYNAMIC_TAG_NULL) {
- break;
- }
-
- if (tag == ELF_DYNAMIC_TAG_STRTAB) {
- string_table_offset = value;
- } else if (tag == ELF_DYNAMIC_TAG_SYMTAB) {
- symbol_table_offset = value;
- } else if (tag == ELF_DYNAMIC_TAG_SYMENT) {
- symbol_entry_size = value;
- }
- }
-
- if (string_table_offset == 0 || symbol_table_offset == 0 || symbol_entry_size == 0) {
- return {};
- }
-
- const auto string_table_address = text_offset + string_table_offset;
- const auto symbol_table_address = text_offset + symbol_table_offset;
-
- Symbols out;
-
- VAddr symbol_index = symbol_table_address;
- while (symbol_index < string_table_address) {
- ELFSymbol symbol{};
- memory.ReadBlock(symbol_index, &symbol, sizeof(ELFSymbol));
-
- VAddr string_offset = string_table_address + symbol.name_index;
- std::string name;
- for (u8 c = memory.Read8(string_offset); c != 0; c = memory.Read8(++string_offset)) {
- name += static_cast<char>(c);
- }
-
- symbol_index += symbol_entry_size;
- out.push_back({symbol, name});
- }
-
- return out;
-}
-
-std::optional<std::string> GetSymbolName(const Symbols& symbols, VAddr func_address) {
- const auto iter =
- std::find_if(symbols.begin(), symbols.end(), [func_address](const auto& pair) {
- const auto& symbol = pair.first;
- const auto end_address = symbol.value + symbol.size;
- return func_address >= symbol.value && func_address < end_address;
- });
-
- if (iter == symbols.end()) {
- return std::nullopt;
- }
+#include "core/arm/dynarmic/arm_dynarmic_32.h"
+#include "core/arm/dynarmic/arm_dynarmic_64.h"
- return iter->second;
-}
-
-} // Anonymous namespace
+namespace Core {
constexpr u64 SEGMENT_BASE = 0x7100000000ull;
std::vector<ARM_Interface::BacktraceEntry> ARM_Interface::GetBacktraceFromContext(
- System& system, const ThreadContext64& ctx) {
- std::vector<BacktraceEntry> out;
- auto& memory = system.Memory();
-
- auto fp = ctx.cpu_registers[29];
- auto lr = ctx.cpu_registers[30];
- while (true) {
- out.push_back({
- .module = "",
- .address = 0,
- .original_address = lr,
- .offset = 0,
- .name = {},
- });
-
- if (fp == 0) {
- break;
- }
+ Core::System& system, const ARM_Interface::ThreadContext32& ctx) {
+ return ARM_Dynarmic_32::GetBacktraceFromContext(system, ctx);
+}
- lr = memory.Read64(fp + 8) - 4;
- fp = memory.Read64(fp);
- }
+std::vector<ARM_Interface::BacktraceEntry> ARM_Interface::GetBacktraceFromContext(
+ Core::System& system, const ARM_Interface::ThreadContext64& ctx) {
+ return ARM_Dynarmic_64::GetBacktraceFromContext(system, ctx);
+}
+void ARM_Interface::SymbolicateBacktrace(Core::System& system, std::vector<BacktraceEntry>& out) {
std::map<VAddr, std::string> modules;
auto& loader{system.GetAppLoader()};
if (loader.ReadNSOModules(modules) != Loader::ResultStatus::Success) {
- return {};
+ return;
}
- std::map<std::string, Symbols> symbols;
+ std::map<std::string, Symbols::Symbols> symbols;
for (const auto& module : modules) {
- symbols.insert_or_assign(module.second, GetSymbols(module.first, memory));
+ symbols.insert_or_assign(module.second,
+ Symbols::GetSymbols(module.first, system.Memory(),
+ system.CurrentProcess()->Is64BitProcess()));
}
for (auto& entry : out) {
@@ -188,91 +64,137 @@ std::vector<ARM_Interface::BacktraceEntry> ARM_Interface::GetBacktraceFromContex
entry.offset = entry.original_address - base;
entry.address = SEGMENT_BASE + entry.offset;
- if (entry.module.empty())
+ if (entry.module.empty()) {
entry.module = "unknown";
+ }
const auto symbol_set = symbols.find(entry.module);
if (symbol_set != symbols.end()) {
- const auto symbol = GetSymbolName(symbol_set->second, entry.offset);
+ const auto symbol = Symbols::GetSymbolName(symbol_set->second, entry.offset);
if (symbol.has_value()) {
+#ifdef _MSC_VER
// TODO(DarkLordZach): Add demangling of symbol names.
entry.name = *symbol;
+#else
+ int status{-1};
+ char* demangled{abi::__cxa_demangle(symbol->c_str(), nullptr, nullptr, &status)};
+ if (status == 0 && demangled != nullptr) {
+ entry.name = demangled;
+ std::free(demangled);
+ } else {
+ entry.name = *symbol;
+ }
+#endif
}
}
}
+}
+
+void ARM_Interface::LogBacktrace() const {
+ const VAddr sp = GetSP();
+ const VAddr pc = GetPC();
+ LOG_ERROR(Core_ARM, "Backtrace, sp={:016X}, pc={:016X}", sp, pc);
+ LOG_ERROR(Core_ARM, "{:20}{:20}{:20}{:20}{}", "Module Name", "Address", "Original Address",
+ "Offset", "Symbol");
+ LOG_ERROR(Core_ARM, "");
- return out;
+ const auto backtrace = GetBacktrace();
+ for (const auto& entry : backtrace) {
+ LOG_ERROR(Core_ARM, "{:20}{:016X} {:016X} {:016X} {}", entry.module, entry.address,
+ entry.original_address, entry.offset, entry.name);
+ }
}
-std::vector<ARM_Interface::BacktraceEntry> ARM_Interface::GetBacktrace() const {
- std::vector<BacktraceEntry> out;
- auto& memory = system.Memory();
+void ARM_Interface::Run() {
+ using Kernel::StepState;
+ using Kernel::SuspendType;
- auto fp = GetReg(29);
- auto lr = GetReg(30);
while (true) {
- out.push_back({"", 0, lr, 0, ""});
- if (!fp) {
+ Kernel::KThread* current_thread{Kernel::GetCurrentThreadPointer(system.Kernel())};
+ Dynarmic::HaltReason hr{};
+
+ // Notify the debugger and go to sleep if a step was performed
+ // and this thread has been scheduled again.
+ if (current_thread->GetStepState() == StepState::StepPerformed) {
+ system.GetDebugger().NotifyThreadStopped(current_thread);
+ current_thread->RequestSuspend(SuspendType::Debug);
break;
}
- lr = memory.Read64(fp + 8) - 4;
- fp = memory.Read64(fp);
- }
- std::map<VAddr, std::string> modules;
- auto& loader{system.GetAppLoader()};
- if (loader.ReadNSOModules(modules) != Loader::ResultStatus::Success) {
- return {};
- }
-
- std::map<std::string, Symbols> symbols;
- for (const auto& module : modules) {
- symbols.insert_or_assign(module.second, GetSymbols(module.first, memory));
- }
+ // Otherwise, run the thread.
+ system.EnterDynarmicProfile();
+ if (current_thread->GetStepState() == StepState::StepPending) {
+ hr = StepJit();
- for (auto& entry : out) {
- VAddr base = 0;
- for (auto iter = modules.rbegin(); iter != modules.rend(); ++iter) {
- const auto& module{*iter};
- if (entry.original_address >= module.first) {
- entry.module = module.second;
- base = module.first;
- break;
+ if (Has(hr, step_thread)) {
+ current_thread->SetStepState(StepState::StepPerformed);
}
+ } else {
+ hr = RunJit();
+ }
+ system.ExitDynarmicProfile();
+
+ // Notify the debugger and go to sleep if a breakpoint was hit,
+ // or if the thread is unable to continue for any reason.
+ if (Has(hr, breakpoint) || Has(hr, no_execute)) {
+ RewindBreakpointInstruction();
+ if (system.DebuggerEnabled()) {
+ system.GetDebugger().NotifyThreadStopped(current_thread);
+ }
+ current_thread->RequestSuspend(Kernel::SuspendType::Debug);
+ break;
}
- entry.offset = entry.original_address - base;
- entry.address = SEGMENT_BASE + entry.offset;
-
- if (entry.module.empty())
- entry.module = "unknown";
-
- const auto symbol_set = symbols.find(entry.module);
- if (symbol_set != symbols.end()) {
- const auto symbol = GetSymbolName(symbol_set->second, entry.offset);
- if (symbol.has_value()) {
- // TODO(DarkLordZach): Add demangling of symbol names.
- entry.name = *symbol;
+ // Notify the debugger and go to sleep if a watchpoint was hit.
+ if (Has(hr, watchpoint)) {
+ if (system.DebuggerEnabled()) {
+ system.GetDebugger().NotifyThreadWatchpoint(current_thread, *HaltedWatchpoint());
}
+ current_thread->RequestSuspend(SuspendType::Debug);
+ break;
+ }
+
+ // Handle syscalls and scheduling (this may change the current thread/core)
+ if (Has(hr, svc_call)) {
+ Kernel::Svc::Call(system, GetSvcNumber());
+ break;
+ }
+ if (Has(hr, break_loop) || !uses_wall_clock) {
+ break;
}
}
+}
- return out;
+void ARM_Interface::LoadWatchpointArray(const WatchpointArray& wp) {
+ watchpoints = &wp;
}
-void ARM_Interface::LogBacktrace() const {
- const VAddr sp = GetReg(13);
- const VAddr pc = GetPC();
- LOG_ERROR(Core_ARM, "Backtrace, sp={:016X}, pc={:016X}", sp, pc);
- LOG_ERROR(Core_ARM, "{:20}{:20}{:20}{:20}{}", "Module Name", "Address", "Original Address",
- "Offset", "Symbol");
- LOG_ERROR(Core_ARM, "");
+const Kernel::DebugWatchpoint* ARM_Interface::MatchingWatchpoint(
+ VAddr addr, u64 size, Kernel::DebugWatchpointType access_type) const {
+ if (!watchpoints) {
+ return nullptr;
+ }
- const auto backtrace = GetBacktrace();
- for (const auto& entry : backtrace) {
- LOG_ERROR(Core_ARM, "{:20}{:016X} {:016X} {:016X} {}", entry.module, entry.address,
- entry.original_address, entry.offset, entry.name);
+ const VAddr start_address{addr};
+ const VAddr end_address{addr + size};
+
+ for (size_t i = 0; i < Core::Hardware::NUM_WATCHPOINTS; i++) {
+ const auto& watch{(*watchpoints)[i]};
+
+ if (end_address <= watch.start_address) {
+ continue;
+ }
+ if (start_address >= watch.end_address) {
+ continue;
+ }
+ if ((access_type & watch.type) == Kernel::DebugWatchpointType::None) {
+ continue;
+ }
+
+ return &watch;
}
+
+ return nullptr;
}
} // namespace Core
diff --git a/src/core/arm/arm_interface.h b/src/core/arm/arm_interface.h
index c60322442..7d62d030e 100644
--- a/src/core/arm/arm_interface.h
+++ b/src/core/arm/arm_interface.h
@@ -1,11 +1,14 @@
-// Copyright 2014 Citra Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: 2014 Citra Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <array>
+#include <span>
#include <vector>
+
+#include <dynarmic/interface/halt_reason.h>
+
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "core/hardware_properties.h"
@@ -16,13 +19,15 @@ struct PageTable;
namespace Kernel {
enum class VMAPermission : u8;
-}
+enum class DebugWatchpointType : u8;
+struct DebugWatchpoint;
+} // namespace Kernel
namespace Core {
class System;
class CPUInterruptHandler;
-using CPUInterrupts = std::array<CPUInterruptHandler, Core::Hardware::NUM_CPU_CORES>;
+using WatchpointArray = std::array<Kernel::DebugWatchpoint, Core::Hardware::NUM_WATCHPOINTS>;
/// Generic ARMv8 CPU interface
class ARM_Interface {
@@ -30,10 +35,8 @@ public:
YUZU_NON_COPYABLE(ARM_Interface);
YUZU_NON_MOVEABLE(ARM_Interface);
- explicit ARM_Interface(System& system_, CPUInterrupts& interrupt_handlers_,
- bool uses_wall_clock_)
- : system{system_}, interrupt_handlers{interrupt_handlers_}, uses_wall_clock{
- uses_wall_clock_} {}
+ explicit ARM_Interface(System& system_, bool uses_wall_clock_)
+ : system{system_}, uses_wall_clock{uses_wall_clock_} {}
virtual ~ARM_Interface() = default;
struct ThreadContext32 {
@@ -64,10 +67,7 @@ public:
static_assert(sizeof(ThreadContext64) == 0x320);
/// Runs the CPU until an event happens
- virtual void Run() = 0;
-
- /// Step CPU by one instruction
- virtual void Step() = 0;
+ void Run();
/// Clear all instruction cache
virtual void ClearInstructionCache() = 0;
@@ -101,6 +101,12 @@ public:
virtual u64 GetPC() const = 0;
/**
+ * Get the current Stack Pointer
+ * @return Returns current SP
+ */
+ virtual u64 GetSP() const = 0;
+
+ /**
* Get an ARM register
* @param index Register index
* @return Returns the value in the register
@@ -164,12 +170,16 @@ public:
virtual void SaveContext(ThreadContext64& ctx) = 0;
virtual void LoadContext(const ThreadContext32& ctx) = 0;
virtual void LoadContext(const ThreadContext64& ctx) = 0;
+ void LoadWatchpointArray(const WatchpointArray& wp);
/// Clears the exclusive monitor's state.
virtual void ClearExclusiveState() = 0;
- /// Prepare core for thread reschedule (if needed to correctly handle state)
- virtual void PrepareReschedule() = 0;
+ /// Signal an interrupt and ask the core to halt as soon as possible.
+ virtual void SignalInterrupt() = 0;
+
+ /// Clear a previous interrupt.
+ virtual void ClearInterrupt() = 0;
struct BacktraceEntry {
std::string module;
@@ -180,23 +190,36 @@ public:
};
static std::vector<BacktraceEntry> GetBacktraceFromContext(System& system,
+ const ThreadContext32& ctx);
+ static std::vector<BacktraceEntry> GetBacktraceFromContext(System& system,
const ThreadContext64& ctx);
- std::vector<BacktraceEntry> GetBacktrace() const;
+ virtual std::vector<BacktraceEntry> GetBacktrace() const = 0;
- /// fp (= r29) points to the last frame record.
- /// Note that this is the frame record for the *previous* frame, not the current one.
- /// Note we need to subtract 4 from our last read to get the proper address
- /// Frame records are two words long:
- /// fp+0 : pointer to previous frame record
- /// fp+8 : value of lr for frame
void LogBacktrace() const;
+ static constexpr Dynarmic::HaltReason step_thread = Dynarmic::HaltReason::Step;
+ static constexpr Dynarmic::HaltReason break_loop = Dynarmic::HaltReason::UserDefined2;
+ static constexpr Dynarmic::HaltReason svc_call = Dynarmic::HaltReason::UserDefined3;
+ static constexpr Dynarmic::HaltReason breakpoint = Dynarmic::HaltReason::UserDefined4;
+ static constexpr Dynarmic::HaltReason watchpoint = Dynarmic::HaltReason::MemoryAbort;
+ static constexpr Dynarmic::HaltReason no_execute = Dynarmic::HaltReason::UserDefined6;
+
protected:
/// System context that this ARM interface is running under.
System& system;
- CPUInterrupts& interrupt_handlers;
+ const WatchpointArray* watchpoints;
bool uses_wall_clock;
+
+ static void SymbolicateBacktrace(Core::System& system, std::vector<BacktraceEntry>& out);
+ const Kernel::DebugWatchpoint* MatchingWatchpoint(
+ VAddr addr, u64 size, Kernel::DebugWatchpointType access_type) const;
+
+ virtual Dynarmic::HaltReason RunJit() = 0;
+ virtual Dynarmic::HaltReason StepJit() = 0;
+ virtual u32 GetSvcNumber() const = 0;
+ virtual const Kernel::DebugWatchpoint* HaltedWatchpoint() const = 0;
+ virtual void RewindBreakpointInstruction() = 0;
};
} // namespace Core
diff --git a/src/core/arm/cpu_interrupt_handler.cpp b/src/core/arm/cpu_interrupt_handler.cpp
deleted file mode 100644
index 9c8898700..000000000
--- a/src/core/arm/cpu_interrupt_handler.cpp
+++ /dev/null
@@ -1,25 +0,0 @@
-// Copyright 2020 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
-
-#include "common/thread.h"
-#include "core/arm/cpu_interrupt_handler.h"
-
-namespace Core {
-
-CPUInterruptHandler::CPUInterruptHandler() : interrupt_event{std::make_unique<Common::Event>()} {}
-
-CPUInterruptHandler::~CPUInterruptHandler() = default;
-
-void CPUInterruptHandler::SetInterrupt(bool is_interrupted_) {
- if (is_interrupted_) {
- interrupt_event->Set();
- }
- is_interrupted = is_interrupted_;
-}
-
-void CPUInterruptHandler::AwaitInterrupt() {
- interrupt_event->Wait();
-}
-
-} // namespace Core
diff --git a/src/core/arm/cpu_interrupt_handler.h b/src/core/arm/cpu_interrupt_handler.h
deleted file mode 100644
index c20c280f1..000000000
--- a/src/core/arm/cpu_interrupt_handler.h
+++ /dev/null
@@ -1,40 +0,0 @@
-// Copyright 2020 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
-
-#pragma once
-
-#include <atomic>
-#include <memory>
-
-namespace Common {
-class Event;
-}
-
-namespace Core {
-
-class CPUInterruptHandler {
-public:
- CPUInterruptHandler();
- ~CPUInterruptHandler();
-
- CPUInterruptHandler(const CPUInterruptHandler&) = delete;
- CPUInterruptHandler& operator=(const CPUInterruptHandler&) = delete;
-
- CPUInterruptHandler(CPUInterruptHandler&&) = delete;
- CPUInterruptHandler& operator=(CPUInterruptHandler&&) = delete;
-
- bool IsInterrupted() const {
- return is_interrupted;
- }
-
- void SetInterrupt(bool is_interrupted);
-
- void AwaitInterrupt();
-
-private:
- std::unique_ptr<Common::Event> interrupt_event;
- std::atomic_bool is_interrupted{false};
-};
-
-} // namespace Core
diff --git a/src/core/arm/dynarmic/arm_dynarmic_32.cpp b/src/core/arm/dynarmic/arm_dynarmic_32.cpp
index b0d89c539..d1e70f19d 100644
--- a/src/core/arm/dynarmic/arm_dynarmic_32.cpp
+++ b/src/core/arm/dynarmic/arm_dynarmic_32.cpp
@@ -1,6 +1,5 @@
-// Copyright 2020 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include <cinttypes>
#include <memory>
@@ -12,12 +11,13 @@
#include "common/logging/log.h"
#include "common/page_table.h"
#include "common/settings.h"
-#include "core/arm/cpu_interrupt_handler.h"
#include "core/arm/dynarmic/arm_dynarmic_32.h"
#include "core/arm/dynarmic/arm_dynarmic_cp15.h"
#include "core/arm/dynarmic/arm_exclusive_monitor.h"
#include "core/core.h"
#include "core/core_timing.h"
+#include "core/debugger/debugger.h"
+#include "core/hle/kernel/k_process.h"
#include "core/hle/kernel/svc.h"
#include "core/memory.h"
@@ -28,69 +28,106 @@ using namespace Common::Literals;
class DynarmicCallbacks32 : public Dynarmic::A32::UserCallbacks {
public:
explicit DynarmicCallbacks32(ARM_Dynarmic_32& parent_)
- : parent{parent_}, memory(parent.system.Memory()) {}
+ : parent{parent_},
+ memory(parent.system.Memory()), debugger_enabled{parent.system.DebuggerEnabled()} {}
u8 MemoryRead8(u32 vaddr) override {
+ CheckMemoryAccess(vaddr, 1, Kernel::DebugWatchpointType::Read);
return memory.Read8(vaddr);
}
u16 MemoryRead16(u32 vaddr) override {
+ CheckMemoryAccess(vaddr, 2, Kernel::DebugWatchpointType::Read);
return memory.Read16(vaddr);
}
u32 MemoryRead32(u32 vaddr) override {
+ CheckMemoryAccess(vaddr, 4, Kernel::DebugWatchpointType::Read);
return memory.Read32(vaddr);
}
u64 MemoryRead64(u32 vaddr) override {
+ CheckMemoryAccess(vaddr, 8, Kernel::DebugWatchpointType::Read);
return memory.Read64(vaddr);
}
+ std::optional<u32> MemoryReadCode(u32 vaddr) override {
+ if (!memory.IsValidVirtualAddressRange(vaddr, sizeof(u32))) {
+ return std::nullopt;
+ }
+ return memory.Read32(vaddr);
+ }
void MemoryWrite8(u32 vaddr, u8 value) override {
- memory.Write8(vaddr, value);
+ if (CheckMemoryAccess(vaddr, 1, Kernel::DebugWatchpointType::Write)) {
+ memory.Write8(vaddr, value);
+ }
}
void MemoryWrite16(u32 vaddr, u16 value) override {
- memory.Write16(vaddr, value);
+ if (CheckMemoryAccess(vaddr, 2, Kernel::DebugWatchpointType::Write)) {
+ memory.Write16(vaddr, value);
+ }
}
void MemoryWrite32(u32 vaddr, u32 value) override {
- memory.Write32(vaddr, value);
+ if (CheckMemoryAccess(vaddr, 4, Kernel::DebugWatchpointType::Write)) {
+ memory.Write32(vaddr, value);
+ }
}
void MemoryWrite64(u32 vaddr, u64 value) override {
- memory.Write64(vaddr, value);
+ if (CheckMemoryAccess(vaddr, 8, Kernel::DebugWatchpointType::Write)) {
+ memory.Write64(vaddr, value);
+ }
}
bool MemoryWriteExclusive8(u32 vaddr, u8 value, u8 expected) override {
- return memory.WriteExclusive8(vaddr, value, expected);
+ return CheckMemoryAccess(vaddr, 1, Kernel::DebugWatchpointType::Write) &&
+ memory.WriteExclusive8(vaddr, value, expected);
}
bool MemoryWriteExclusive16(u32 vaddr, u16 value, u16 expected) override {
- return memory.WriteExclusive16(vaddr, value, expected);
+ return CheckMemoryAccess(vaddr, 2, Kernel::DebugWatchpointType::Write) &&
+ memory.WriteExclusive16(vaddr, value, expected);
}
bool MemoryWriteExclusive32(u32 vaddr, u32 value, u32 expected) override {
- return memory.WriteExclusive32(vaddr, value, expected);
+ return CheckMemoryAccess(vaddr, 4, Kernel::DebugWatchpointType::Write) &&
+ memory.WriteExclusive32(vaddr, value, expected);
}
bool MemoryWriteExclusive64(u32 vaddr, u64 value, u64 expected) override {
- return memory.WriteExclusive64(vaddr, value, expected);
+ return CheckMemoryAccess(vaddr, 8, Kernel::DebugWatchpointType::Write) &&
+ memory.WriteExclusive64(vaddr, value, expected);
}
void InterpreterFallback(u32 pc, std::size_t num_instructions) override {
- UNIMPLEMENTED_MSG("This should never happen, pc = {:08X}, code = {:08X}", pc,
- MemoryReadCode(pc));
+ parent.LogBacktrace();
+ LOG_ERROR(Core_ARM,
+ "Unimplemented instruction @ 0x{:X} for {} instructions (instr = {:08X})", pc,
+ num_instructions, memory.Read32(pc));
}
void ExceptionRaised(u32 pc, Dynarmic::A32::Exception exception) override {
- LOG_CRITICAL(Core_ARM,
- "ExceptionRaised(exception = {}, pc = {:08X}, code = {:08X}, thumb = {})",
- exception, pc, MemoryReadCode(pc), parent.IsInThumbMode());
- UNIMPLEMENTED();
+ switch (exception) {
+ case Dynarmic::A32::Exception::NoExecuteFault:
+ LOG_CRITICAL(Core_ARM, "Cannot execute instruction at unmapped address {:#08x}", pc);
+ ReturnException(pc, ARM_Interface::no_execute);
+ return;
+ default:
+ if (debugger_enabled) {
+ ReturnException(pc, ARM_Interface::breakpoint);
+ return;
+ }
+
+ parent.LogBacktrace();
+ LOG_CRITICAL(Core_ARM,
+ "ExceptionRaised(exception = {}, pc = {:08X}, code = {:08X}, thumb = {})",
+ exception, pc, memory.Read32(pc), parent.IsInThumbMode());
+ }
}
void CallSVC(u32 swi) override {
- parent.svc_called = true;
parent.svc_swi = swi;
- parent.jit->HaltExecution();
+ parent.jit.load()->HaltExecution(ARM_Interface::svc_call);
}
void AddTicks(u64 ticks) override {
if (parent.uses_wall_clock) {
return;
}
+
// 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
@@ -107,18 +144,45 @@ public:
u64 GetTicksRemaining() override {
if (parent.uses_wall_clock) {
- if (!parent.interrupt_handlers[parent.core_index].IsInterrupted()) {
+ if (!IsInterrupted()) {
return minimum_run_cycles;
}
return 0U;
}
+
return std::max<s64>(parent.system.CoreTiming().GetDowncount(), 0);
}
+ bool CheckMemoryAccess(VAddr addr, u64 size, Kernel::DebugWatchpointType type) {
+ if (!debugger_enabled) {
+ return true;
+ }
+
+ const auto match{parent.MatchingWatchpoint(addr, size, type)};
+ if (match) {
+ parent.halted_watchpoint = match;
+ parent.jit.load()->HaltExecution(ARM_Interface::watchpoint);
+ return false;
+ }
+
+ return true;
+ }
+
+ void ReturnException(u32 pc, Dynarmic::HaltReason hr) {
+ parent.SaveContext(parent.breakpoint_context);
+ parent.breakpoint_context.cpu_registers[15] = pc;
+ parent.jit.load()->HaltExecution(hr);
+ }
+
+ bool IsInterrupted() {
+ return parent.system.Kernel().PhysicalCore(parent.core_index).IsInterrupted();
+ }
+
ARM_Dynarmic_32& parent;
Core::Memory::Memory& memory;
std::size_t num_interpreted_instructions{};
- static constexpr u64 minimum_run_cycles = 1000U;
+ bool debugger_enabled{};
+ static constexpr u64 minimum_run_cycles = 10000U;
};
std::shared_ptr<Dynarmic::A32::Jit> ARM_Dynarmic_32::MakeJit(Common::PageTable* page_table) const {
@@ -126,17 +190,21 @@ std::shared_ptr<Dynarmic::A32::Jit> ARM_Dynarmic_32::MakeJit(Common::PageTable*
config.callbacks = cb.get();
config.coprocessors[15] = cp15;
config.define_unpredictable_behaviour = true;
- static constexpr std::size_t PAGE_BITS = 12;
- static constexpr std::size_t NUM_PAGE_TABLE_ENTRIES = 1 << (32 - PAGE_BITS);
+ static constexpr std::size_t YUZU_PAGEBITS = 12;
+ static constexpr std::size_t NUM_PAGE_TABLE_ENTRIES = 1 << (32 - YUZU_PAGEBITS);
if (page_table) {
config.page_table = reinterpret_cast<std::array<std::uint8_t*, NUM_PAGE_TABLE_ENTRIES>*>(
page_table->pointers.data());
+ config.absolute_offset_page_table = true;
+ config.page_table_pointer_mask_bits = Common::PageTable::ATTRIBUTE_BITS;
+ config.detect_misaligned_access_via_page_table = 16 | 32 | 64 | 128;
+ config.only_detect_misalignment_via_page_table_on_page_boundary = true;
+
config.fastmem_pointer = page_table->fastmem_arena;
+
+ config.fastmem_exclusive_access = config.fastmem_pointer != nullptr;
+ config.recompile_on_exclusive_fastmem_failure = true;
}
- config.absolute_offset_page_table = true;
- config.page_table_pointer_mask_bits = Common::PageTable::ATTRIBUTE_BITS;
- config.detect_misaligned_access_via_page_table = 16 | 32 | 64 | 128;
- config.only_detect_misalignment_via_page_table_on_page_boundary = true;
// Multi-process state
config.processor_id = core_index;
@@ -144,10 +212,21 @@ std::shared_ptr<Dynarmic::A32::Jit> ARM_Dynarmic_32::MakeJit(Common::PageTable*
// Timing
config.wall_clock_cntpct = uses_wall_clock;
+ config.enable_cycle_counting = true;
// Code cache size
config.code_cache_size = 512_MiB;
- config.far_code_offset = 400_MiB;
+
+ // Allow memory fault handling to work
+ if (system.DebuggerEnabled()) {
+ config.check_halt_on_memory_access = true;
+ }
+
+ // null_jit
+ if (!page_table) {
+ // Don't waste too much memory on null_jit
+ config.code_cache_size = 8_MiB;
+ }
// Safe optimizations
if (Settings::values.cpu_debug_mode) {
@@ -177,80 +256,101 @@ std::shared_ptr<Dynarmic::A32::Jit> ARM_Dynarmic_32::MakeJit(Common::PageTable*
}
if (!Settings::values.cpuopt_fastmem) {
config.fastmem_pointer = nullptr;
+ config.fastmem_exclusive_access = false;
}
- }
-
- // Unsafe optimizations
- if (Settings::values.cpu_accuracy.GetValue() == Settings::CPUAccuracy::Unsafe) {
- config.unsafe_optimizations = true;
- if (Settings::values.cpuopt_unsafe_unfuse_fma) {
- config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_UnfuseFMA;
+ if (!Settings::values.cpuopt_fastmem_exclusives) {
+ config.fastmem_exclusive_access = false;
}
- if (Settings::values.cpuopt_unsafe_reduce_fp_error) {
- config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_ReducedErrorFP;
+ if (!Settings::values.cpuopt_recompile_exclusives) {
+ config.recompile_on_exclusive_fastmem_failure = false;
}
- if (Settings::values.cpuopt_unsafe_ignore_standard_fpcr) {
- config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_IgnoreStandardFPCRValue;
+ } else {
+ // Unsafe optimizations
+ if (Settings::values.cpu_accuracy.GetValue() == Settings::CPUAccuracy::Unsafe) {
+ config.unsafe_optimizations = true;
+ if (Settings::values.cpuopt_unsafe_unfuse_fma) {
+ config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_UnfuseFMA;
+ }
+ if (Settings::values.cpuopt_unsafe_reduce_fp_error) {
+ config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_ReducedErrorFP;
+ }
+ if (Settings::values.cpuopt_unsafe_ignore_standard_fpcr) {
+ config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_IgnoreStandardFPCRValue;
+ }
+ if (Settings::values.cpuopt_unsafe_inaccurate_nan) {
+ config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_InaccurateNaN;
+ }
+ if (Settings::values.cpuopt_unsafe_ignore_global_monitor) {
+ config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_IgnoreGlobalMonitor;
+ }
}
- if (Settings::values.cpuopt_unsafe_inaccurate_nan) {
+
+ // Curated optimizations
+ if (Settings::values.cpu_accuracy.GetValue() == Settings::CPUAccuracy::Auto) {
+ config.unsafe_optimizations = true;
+ config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_UnfuseFMA;
+ config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_IgnoreStandardFPCRValue;
config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_InaccurateNaN;
+ config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_IgnoreGlobalMonitor;
}
- }
- // Curated optimizations
- if (Settings::values.cpu_accuracy.GetValue() == Settings::CPUAccuracy::Auto) {
- config.unsafe_optimizations = true;
- config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_UnfuseFMA;
- config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_IgnoreStandardFPCRValue;
- config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_InaccurateNaN;
+ // Paranoia mode for debugging optimizations
+ if (Settings::values.cpu_accuracy.GetValue() == Settings::CPUAccuracy::Paranoid) {
+ config.unsafe_optimizations = false;
+ config.optimizations = Dynarmic::no_optimizations;
+ }
}
return std::make_unique<Dynarmic::A32::Jit>(config);
}
-void ARM_Dynarmic_32::Run() {
- while (true) {
- jit->Run();
- if (!svc_called) {
- break;
- }
- svc_called = false;
- Kernel::Svc::Call(system, svc_swi);
- if (shutdown) {
- break;
- }
- }
+Dynarmic::HaltReason ARM_Dynarmic_32::RunJit() {
+ return jit.load()->Run();
+}
+
+Dynarmic::HaltReason ARM_Dynarmic_32::StepJit() {
+ return jit.load()->Step();
+}
+
+u32 ARM_Dynarmic_32::GetSvcNumber() const {
+ return svc_swi;
+}
+
+const Kernel::DebugWatchpoint* ARM_Dynarmic_32::HaltedWatchpoint() const {
+ return halted_watchpoint;
}
-void ARM_Dynarmic_32::Step() {
- jit->Step();
+void ARM_Dynarmic_32::RewindBreakpointInstruction() {
+ LoadContext(breakpoint_context);
}
-ARM_Dynarmic_32::ARM_Dynarmic_32(System& system_, CPUInterrupts& interrupt_handlers_,
- bool uses_wall_clock_, ExclusiveMonitor& exclusive_monitor_,
- std::size_t core_index_)
- : ARM_Interface{system_, interrupt_handlers_, uses_wall_clock_},
- cb(std::make_unique<DynarmicCallbacks32>(*this)),
+ARM_Dynarmic_32::ARM_Dynarmic_32(System& system_, bool uses_wall_clock_,
+ ExclusiveMonitor& exclusive_monitor_, std::size_t core_index_)
+ : ARM_Interface{system_, uses_wall_clock_}, cb(std::make_unique<DynarmicCallbacks32>(*this)),
cp15(std::make_shared<DynarmicCP15>(*this)), core_index{core_index_},
exclusive_monitor{dynamic_cast<DynarmicExclusiveMonitor&>(exclusive_monitor_)},
- jit(MakeJit(nullptr)) {}
+ null_jit{MakeJit(nullptr)}, jit{null_jit.get()} {}
ARM_Dynarmic_32::~ARM_Dynarmic_32() = default;
void ARM_Dynarmic_32::SetPC(u64 pc) {
- jit->Regs()[15] = static_cast<u32>(pc);
+ jit.load()->Regs()[15] = static_cast<u32>(pc);
}
u64 ARM_Dynarmic_32::GetPC() const {
- return jit->Regs()[15];
+ return jit.load()->Regs()[15];
+}
+
+u64 ARM_Dynarmic_32::GetSP() const {
+ return jit.load()->Regs()[13];
}
u64 ARM_Dynarmic_32::GetReg(int index) const {
- return jit->Regs()[index];
+ return jit.load()->Regs()[index];
}
void ARM_Dynarmic_32::SetReg(int index, u64 value) {
- jit->Regs()[index] = static_cast<u32>(value);
+ jit.load()->Regs()[index] = static_cast<u32>(value);
}
u128 ARM_Dynarmic_32::GetVectorReg(int index) const {
@@ -260,11 +360,11 @@ u128 ARM_Dynarmic_32::GetVectorReg(int index) const {
void ARM_Dynarmic_32::SetVectorReg(int index, u128 value) {}
u32 ARM_Dynarmic_32::GetPSTATE() const {
- return jit->Cpsr();
+ return jit.load()->Cpsr();
}
void ARM_Dynarmic_32::SetPSTATE(u32 cpsr) {
- jit->SetCpsr(cpsr);
+ jit.load()->SetCpsr(cpsr);
}
u64 ARM_Dynarmic_32::GetTlsAddress() const {
@@ -285,7 +385,7 @@ void ARM_Dynarmic_32::SetTPIDR_EL0(u64 value) {
void ARM_Dynarmic_32::SaveContext(ThreadContext32& ctx) {
Dynarmic::A32::Context context;
- jit->SaveContext(context);
+ jit.load()->SaveContext(context);
ctx.cpu_registers = context.Regs();
ctx.extension_registers = context.ExtRegs();
ctx.cpsr = context.Cpsr();
@@ -298,24 +398,27 @@ void ARM_Dynarmic_32::LoadContext(const ThreadContext32& ctx) {
context.ExtRegs() = ctx.extension_registers;
context.SetCpsr(ctx.cpsr);
context.SetFpscr(ctx.fpscr);
- jit->LoadContext(context);
+ jit.load()->LoadContext(context);
}
-void ARM_Dynarmic_32::PrepareReschedule() {
- jit->HaltExecution();
- shutdown = true;
+void ARM_Dynarmic_32::SignalInterrupt() {
+ jit.load()->HaltExecution(break_loop);
+}
+
+void ARM_Dynarmic_32::ClearInterrupt() {
+ jit.load()->ClearHalt(break_loop);
}
void ARM_Dynarmic_32::ClearInstructionCache() {
- jit->ClearCache();
+ jit.load()->ClearCache();
}
void ARM_Dynarmic_32::InvalidateCacheRange(VAddr addr, std::size_t size) {
- jit->InvalidateCacheRange(static_cast<u32>(addr), size);
+ jit.load()->InvalidateCacheRange(static_cast<u32>(addr), size);
}
void ARM_Dynarmic_32::ClearExclusiveState() {
- jit->ClearExclusiveState();
+ jit.load()->ClearExclusiveState();
}
void ARM_Dynarmic_32::PageTableChanged(Common::PageTable& page_table,
@@ -326,13 +429,49 @@ void ARM_Dynarmic_32::PageTableChanged(Common::PageTable& page_table,
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;
+ jit.store(iter->second.get());
LoadContext(ctx);
return;
}
- jit = MakeJit(&page_table);
+ std::shared_ptr new_jit = MakeJit(&page_table);
+ jit.store(new_jit.get());
LoadContext(ctx);
- jit_cache.emplace(key, jit);
+ jit_cache.emplace(key, std::move(new_jit));
+}
+
+std::vector<ARM_Interface::BacktraceEntry> ARM_Dynarmic_32::GetBacktrace(Core::System& system,
+ u64 fp, u64 lr, u64 pc) {
+ std::vector<BacktraceEntry> out;
+ auto& memory = system.Memory();
+
+ out.push_back({"", 0, pc, 0, ""});
+
+ // fp (= r11) points to the last frame record.
+ // Frame records are two words long:
+ // fp+0 : pointer to previous frame record
+ // fp+4 : value of lr for frame
+ while (true) {
+ out.push_back({"", 0, lr, 0, ""});
+ if (!fp || (fp % 4 != 0) || !memory.IsValidVirtualAddressRange(fp, 8)) {
+ break;
+ }
+ lr = memory.Read32(fp + 4);
+ fp = memory.Read32(fp);
+ }
+
+ SymbolicateBacktrace(system, out);
+
+ return out;
+}
+
+std::vector<ARM_Interface::BacktraceEntry> ARM_Dynarmic_32::GetBacktraceFromContext(
+ System& system, const ThreadContext32& ctx) {
+ const auto& reg = ctx.cpu_registers;
+ return GetBacktrace(system, reg[11], reg[14], reg[15]);
+}
+
+std::vector<ARM_Interface::BacktraceEntry> ARM_Dynarmic_32::GetBacktrace() const {
+ return GetBacktrace(system, GetReg(11), GetReg(14), GetReg(15));
}
} // namespace Core
diff --git a/src/core/arm/dynarmic/arm_dynarmic_32.h b/src/core/arm/dynarmic/arm_dynarmic_32.h
index 5d47b600d..d24ba2289 100644
--- a/src/core/arm/dynarmic/arm_dynarmic_32.h
+++ b/src/core/arm/dynarmic/arm_dynarmic_32.h
@@ -1,9 +1,9 @@
-// Copyright 2020 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
+#include <atomic>
#include <memory>
#include <unordered_map>
@@ -28,20 +28,19 @@ class System;
class ARM_Dynarmic_32 final : public ARM_Interface {
public:
- ARM_Dynarmic_32(System& system_, CPUInterrupts& interrupt_handlers_, bool uses_wall_clock_,
- ExclusiveMonitor& exclusive_monitor_, std::size_t core_index_);
+ ARM_Dynarmic_32(System& system_, bool uses_wall_clock_, ExclusiveMonitor& exclusive_monitor_,
+ std::size_t core_index_);
~ARM_Dynarmic_32() override;
void SetPC(u64 pc) override;
u64 GetPC() const override;
+ u64 GetSP() 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;
@@ -56,7 +55,8 @@ public:
void LoadContext(const ThreadContext32& ctx) override;
void LoadContext(const ThreadContext64& ctx) override {}
- void PrepareReschedule() override;
+ void SignalInterrupt() override;
+ void ClearInterrupt() override;
void ClearExclusiveState() override;
void ClearInstructionCache() override;
@@ -64,9 +64,23 @@ public:
void PageTableChanged(Common::PageTable& new_page_table,
std::size_t new_address_space_size_in_bits) override;
+ static std::vector<BacktraceEntry> GetBacktraceFromContext(System& system,
+ const ThreadContext32& ctx);
+
+ std::vector<BacktraceEntry> GetBacktrace() const override;
+
+protected:
+ Dynarmic::HaltReason RunJit() override;
+ Dynarmic::HaltReason StepJit() override;
+ u32 GetSvcNumber() const override;
+ const Kernel::DebugWatchpoint* HaltedWatchpoint() const override;
+ void RewindBreakpointInstruction() override;
+
private:
std::shared_ptr<Dynarmic::A32::Jit> MakeJit(Common::PageTable* page_table) const;
+ static std::vector<BacktraceEntry> GetBacktrace(Core::System& system, u64 fp, u64 lr, u64 pc);
+
using JitCacheKey = std::pair<Common::PageTable*, std::size_t>;
using JitCacheType =
std::unordered_map<JitCacheKey, std::shared_ptr<Dynarmic::A32::Jit>, Common::PairHash>;
@@ -79,13 +93,18 @@ private:
std::shared_ptr<DynarmicCP15> cp15;
std::size_t core_index;
DynarmicExclusiveMonitor& exclusive_monitor;
- std::shared_ptr<Dynarmic::A32::Jit> jit;
+
+ std::shared_ptr<Dynarmic::A32::Jit> null_jit;
+
+ // A raw pointer here is fine; we never delete Jit instances.
+ std::atomic<Dynarmic::A32::Jit*> jit;
// SVC callback
u32 svc_swi{};
- bool svc_called{};
- bool shutdown{};
+ // Watchpoint info
+ const Kernel::DebugWatchpoint* halted_watchpoint;
+ ThreadContext32 breakpoint_context;
};
} // namespace Core
diff --git a/src/core/arm/dynarmic/arm_dynarmic_64.cpp b/src/core/arm/dynarmic/arm_dynarmic_64.cpp
index 56836bd05..1d46f6d40 100644
--- a/src/core/arm/dynarmic/arm_dynarmic_64.cpp
+++ b/src/core/arm/dynarmic/arm_dynarmic_64.cpp
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include <cinttypes>
#include <memory>
@@ -11,11 +10,11 @@
#include "common/logging/log.h"
#include "common/page_table.h"
#include "common/settings.h"
-#include "core/arm/cpu_interrupt_handler.h"
#include "core/arm/dynarmic/arm_dynarmic_64.h"
#include "core/arm/dynarmic/arm_exclusive_monitor.h"
#include "core/core.h"
#include "core/core_timing.h"
+#include "core/debugger/debugger.h"
#include "core/hardware_properties.h"
#include "core/hle/kernel/k_process.h"
#include "core/hle/kernel/svc.h"
@@ -29,61 +28,89 @@ using namespace Common::Literals;
class DynarmicCallbacks64 : public Dynarmic::A64::UserCallbacks {
public:
explicit DynarmicCallbacks64(ARM_Dynarmic_64& parent_)
- : parent{parent_}, memory(parent.system.Memory()) {}
+ : parent{parent_},
+ memory(parent.system.Memory()), debugger_enabled{parent.system.DebuggerEnabled()} {}
u8 MemoryRead8(u64 vaddr) override {
+ CheckMemoryAccess(vaddr, 1, Kernel::DebugWatchpointType::Read);
return memory.Read8(vaddr);
}
u16 MemoryRead16(u64 vaddr) override {
+ CheckMemoryAccess(vaddr, 2, Kernel::DebugWatchpointType::Read);
return memory.Read16(vaddr);
}
u32 MemoryRead32(u64 vaddr) override {
+ CheckMemoryAccess(vaddr, 4, Kernel::DebugWatchpointType::Read);
return memory.Read32(vaddr);
}
u64 MemoryRead64(u64 vaddr) override {
+ CheckMemoryAccess(vaddr, 8, Kernel::DebugWatchpointType::Read);
return memory.Read64(vaddr);
}
Vector MemoryRead128(u64 vaddr) override {
+ CheckMemoryAccess(vaddr, 16, Kernel::DebugWatchpointType::Read);
return {memory.Read64(vaddr), memory.Read64(vaddr + 8)};
}
+ std::optional<u32> MemoryReadCode(u64 vaddr) override {
+ if (!memory.IsValidVirtualAddressRange(vaddr, sizeof(u32))) {
+ return std::nullopt;
+ }
+ return memory.Read32(vaddr);
+ }
void MemoryWrite8(u64 vaddr, u8 value) override {
- memory.Write8(vaddr, value);
+ if (CheckMemoryAccess(vaddr, 1, Kernel::DebugWatchpointType::Write)) {
+ memory.Write8(vaddr, value);
+ }
}
void MemoryWrite16(u64 vaddr, u16 value) override {
- memory.Write16(vaddr, value);
+ if (CheckMemoryAccess(vaddr, 2, Kernel::DebugWatchpointType::Write)) {
+ memory.Write16(vaddr, value);
+ }
}
void MemoryWrite32(u64 vaddr, u32 value) override {
- memory.Write32(vaddr, value);
+ if (CheckMemoryAccess(vaddr, 4, Kernel::DebugWatchpointType::Write)) {
+ memory.Write32(vaddr, value);
+ }
}
void MemoryWrite64(u64 vaddr, u64 value) override {
- memory.Write64(vaddr, value);
+ if (CheckMemoryAccess(vaddr, 8, Kernel::DebugWatchpointType::Write)) {
+ memory.Write64(vaddr, value);
+ }
}
void MemoryWrite128(u64 vaddr, Vector value) override {
- memory.Write64(vaddr, value[0]);
- memory.Write64(vaddr + 8, value[1]);
+ if (CheckMemoryAccess(vaddr, 16, Kernel::DebugWatchpointType::Write)) {
+ memory.Write64(vaddr, value[0]);
+ memory.Write64(vaddr + 8, value[1]);
+ }
}
bool MemoryWriteExclusive8(u64 vaddr, std::uint8_t value, std::uint8_t expected) override {
- return memory.WriteExclusive8(vaddr, value, expected);
+ return CheckMemoryAccess(vaddr, 1, Kernel::DebugWatchpointType::Write) &&
+ memory.WriteExclusive8(vaddr, value, expected);
}
bool MemoryWriteExclusive16(u64 vaddr, std::uint16_t value, std::uint16_t expected) override {
- return memory.WriteExclusive16(vaddr, value, expected);
+ return CheckMemoryAccess(vaddr, 2, Kernel::DebugWatchpointType::Write) &&
+ memory.WriteExclusive16(vaddr, value, expected);
}
bool MemoryWriteExclusive32(u64 vaddr, std::uint32_t value, std::uint32_t expected) override {
- return memory.WriteExclusive32(vaddr, value, expected);
+ return CheckMemoryAccess(vaddr, 4, Kernel::DebugWatchpointType::Write) &&
+ memory.WriteExclusive32(vaddr, value, expected);
}
bool MemoryWriteExclusive64(u64 vaddr, std::uint64_t value, std::uint64_t expected) override {
- return memory.WriteExclusive64(vaddr, value, expected);
+ return CheckMemoryAccess(vaddr, 8, Kernel::DebugWatchpointType::Write) &&
+ memory.WriteExclusive64(vaddr, value, expected);
}
bool MemoryWriteExclusive128(u64 vaddr, Vector value, Vector expected) override {
- return memory.WriteExclusive128(vaddr, value, expected);
+ return CheckMemoryAccess(vaddr, 16, Kernel::DebugWatchpointType::Write) &&
+ memory.WriteExclusive128(vaddr, value, expected);
}
void InterpreterFallback(u64 pc, std::size_t num_instructions) override {
+ parent.LogBacktrace();
LOG_ERROR(Core_ARM,
"Unimplemented instruction @ 0x{:X} for {} instructions (instr = {:08X})", pc,
- num_instructions, MemoryReadCode(pc));
+ num_instructions, memory.Read32(pc));
}
void InstructionCacheOperationRaised(Dynarmic::A64::InstructionCacheOperation op,
@@ -93,17 +120,19 @@ public:
static constexpr u64 ICACHE_LINE_SIZE = 64;
const u64 cache_line_start = value & ~(ICACHE_LINE_SIZE - 1);
- parent.InvalidateCacheRange(cache_line_start, ICACHE_LINE_SIZE);
+ parent.system.InvalidateCpuInstructionCacheRange(cache_line_start, ICACHE_LINE_SIZE);
break;
}
case Dynarmic::A64::InstructionCacheOperation::InvalidateAllToPoU:
- parent.ClearInstructionCache();
+ parent.system.InvalidateCpuInstructionCaches();
break;
case Dynarmic::A64::InstructionCacheOperation::InvalidateAllToPoUInnerSharable:
default:
LOG_DEBUG(Core_ARM, "Unprocesseed instruction cache operation: {}", op);
break;
}
+
+ parent.jit.load()->HaltExecution(Dynarmic::HaltReason::CacheInvalidation);
}
void ExceptionRaised(u64 pc, Dynarmic::A64::Exception exception) override {
@@ -114,17 +143,25 @@ public:
case Dynarmic::A64::Exception::SendEventLocal:
case Dynarmic::A64::Exception::Yield:
return;
- case Dynarmic::A64::Exception::Breakpoint:
+ case Dynarmic::A64::Exception::NoExecuteFault:
+ LOG_CRITICAL(Core_ARM, "Cannot execute instruction at unmapped address {:#016x}", pc);
+ ReturnException(pc, ARM_Interface::no_execute);
+ return;
default:
- ASSERT_MSG(false, "ExceptionRaised(exception = {}, pc = {:08X}, code = {:08X})",
- static_cast<std::size_t>(exception), pc, MemoryReadCode(pc));
+ if (debugger_enabled) {
+ ReturnException(pc, ARM_Interface::breakpoint);
+ return;
+ }
+
+ parent.LogBacktrace();
+ LOG_CRITICAL(Core_ARM, "ExceptionRaised(exception = {}, pc = {:08X}, code = {:08X})",
+ static_cast<std::size_t>(exception), pc, memory.Read32(pc));
}
}
void CallSVC(u32 swi) override {
- parent.svc_called = true;
parent.svc_swi = swi;
- parent.jit->HaltExecution();
+ parent.jit.load()->HaltExecution(ARM_Interface::svc_call);
}
void AddTicks(u64 ticks) override {
@@ -146,11 +183,12 @@ public:
u64 GetTicksRemaining() override {
if (parent.uses_wall_clock) {
- if (!parent.interrupt_handlers[parent.core_index].IsInterrupted()) {
+ if (!IsInterrupted()) {
return minimum_run_cycles;
}
return 0U;
}
+
return std::max<s64>(parent.system.CoreTiming().GetDowncount(), 0);
}
@@ -158,11 +196,37 @@ public:
return parent.system.CoreTiming().GetClockTicks();
}
+ bool CheckMemoryAccess(VAddr addr, u64 size, Kernel::DebugWatchpointType type) {
+ if (!debugger_enabled) {
+ return true;
+ }
+
+ const auto match{parent.MatchingWatchpoint(addr, size, type)};
+ if (match) {
+ parent.halted_watchpoint = match;
+ parent.jit.load()->HaltExecution(ARM_Interface::watchpoint);
+ return false;
+ }
+
+ return true;
+ }
+
+ void ReturnException(u64 pc, Dynarmic::HaltReason hr) {
+ parent.SaveContext(parent.breakpoint_context);
+ parent.breakpoint_context.pc = pc;
+ parent.jit.load()->HaltExecution(hr);
+ }
+
+ bool IsInterrupted() {
+ return parent.system.Kernel().PhysicalCore(parent.core_index).IsInterrupted();
+ }
+
ARM_Dynarmic_64& parent;
Core::Memory::Memory& memory;
u64 tpidrro_el0 = 0;
u64 tpidr_el0 = 0;
- static constexpr u64 minimum_run_cycles = 1000U;
+ bool debugger_enabled{};
+ static constexpr u64 minimum_run_cycles = 10000U;
};
std::shared_ptr<Dynarmic::A64::Jit> ARM_Dynarmic_64::MakeJit(Common::PageTable* page_table,
@@ -185,6 +249,9 @@ std::shared_ptr<Dynarmic::A64::Jit> ARM_Dynarmic_64::MakeJit(Common::PageTable*
config.fastmem_pointer = page_table->fastmem_arena;
config.fastmem_address_space_bits = address_space_bits;
config.silently_mirror_fastmem = false;
+
+ config.fastmem_exclusive_access = config.fastmem_pointer != nullptr;
+ config.recompile_on_exclusive_fastmem_failure = true;
}
// Multi-process state
@@ -203,10 +270,21 @@ std::shared_ptr<Dynarmic::A64::Jit> ARM_Dynarmic_64::MakeJit(Common::PageTable*
// Timing
config.wall_clock_cntpct = uses_wall_clock;
+ config.enable_cycle_counting = true;
// Code cache size
config.code_cache_size = 512_MiB;
- config.far_code_offset = 400_MiB;
+
+ // Allow memory fault handling to work
+ if (system.DebuggerEnabled()) {
+ config.check_halt_on_memory_access = true;
+ }
+
+ // null_jit
+ if (!page_table) {
+ // Don't waste too much memory on null_jit
+ config.code_cache_size = 8_MiB;
+ }
// Safe optimizations
if (Settings::values.cpu_debug_mode) {
@@ -236,95 +314,117 @@ std::shared_ptr<Dynarmic::A64::Jit> ARM_Dynarmic_64::MakeJit(Common::PageTable*
}
if (!Settings::values.cpuopt_fastmem) {
config.fastmem_pointer = nullptr;
+ config.fastmem_exclusive_access = false;
}
- }
-
- // Unsafe optimizations
- if (Settings::values.cpu_accuracy.GetValue() == Settings::CPUAccuracy::Unsafe) {
- config.unsafe_optimizations = true;
- if (Settings::values.cpuopt_unsafe_unfuse_fma) {
- config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_UnfuseFMA;
+ if (!Settings::values.cpuopt_fastmem_exclusives) {
+ config.fastmem_exclusive_access = false;
}
- if (Settings::values.cpuopt_unsafe_reduce_fp_error) {
- config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_ReducedErrorFP;
+ if (!Settings::values.cpuopt_recompile_exclusives) {
+ config.recompile_on_exclusive_fastmem_failure = false;
}
- if (Settings::values.cpuopt_unsafe_inaccurate_nan) {
- config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_InaccurateNaN;
+ } else {
+ // Unsafe optimizations
+ if (Settings::values.cpu_accuracy.GetValue() == Settings::CPUAccuracy::Unsafe) {
+ config.unsafe_optimizations = true;
+ if (Settings::values.cpuopt_unsafe_unfuse_fma) {
+ config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_UnfuseFMA;
+ }
+ if (Settings::values.cpuopt_unsafe_reduce_fp_error) {
+ config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_ReducedErrorFP;
+ }
+ if (Settings::values.cpuopt_unsafe_inaccurate_nan) {
+ config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_InaccurateNaN;
+ }
+ if (Settings::values.cpuopt_unsafe_fastmem_check) {
+ config.fastmem_address_space_bits = 64;
+ }
+ if (Settings::values.cpuopt_unsafe_ignore_global_monitor) {
+ config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_IgnoreGlobalMonitor;
+ }
}
- if (Settings::values.cpuopt_unsafe_fastmem_check) {
+
+ // Curated optimizations
+ if (Settings::values.cpu_accuracy.GetValue() == Settings::CPUAccuracy::Auto) {
+ config.unsafe_optimizations = true;
+ config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_UnfuseFMA;
+ config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_InaccurateNaN;
config.fastmem_address_space_bits = 64;
+ config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_IgnoreGlobalMonitor;
}
- }
- // Curated optimizations
- if (Settings::values.cpu_accuracy.GetValue() == Settings::CPUAccuracy::Auto) {
- config.unsafe_optimizations = true;
- config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_UnfuseFMA;
- config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_InaccurateNaN;
- config.fastmem_address_space_bits = 64;
+ // Paranoia mode for debugging optimizations
+ if (Settings::values.cpu_accuracy.GetValue() == Settings::CPUAccuracy::Paranoid) {
+ config.unsafe_optimizations = false;
+ config.optimizations = Dynarmic::no_optimizations;
+ }
}
return std::make_shared<Dynarmic::A64::Jit>(config);
}
-void ARM_Dynarmic_64::Run() {
- while (true) {
- jit->Run();
- if (!svc_called) {
- break;
- }
- svc_called = false;
- Kernel::Svc::Call(system, svc_swi);
- if (shutdown) {
- break;
- }
- }
+Dynarmic::HaltReason ARM_Dynarmic_64::RunJit() {
+ return jit.load()->Run();
+}
+
+Dynarmic::HaltReason ARM_Dynarmic_64::StepJit() {
+ return jit.load()->Step();
+}
+
+u32 ARM_Dynarmic_64::GetSvcNumber() const {
+ return svc_swi;
+}
+
+const Kernel::DebugWatchpoint* ARM_Dynarmic_64::HaltedWatchpoint() const {
+ return halted_watchpoint;
}
-void ARM_Dynarmic_64::Step() {
- jit->Step();
+void ARM_Dynarmic_64::RewindBreakpointInstruction() {
+ LoadContext(breakpoint_context);
}
-ARM_Dynarmic_64::ARM_Dynarmic_64(System& system_, CPUInterrupts& interrupt_handlers_,
- bool uses_wall_clock_, ExclusiveMonitor& exclusive_monitor_,
- std::size_t core_index_)
- : ARM_Interface{system_, interrupt_handlers_, uses_wall_clock_},
+ARM_Dynarmic_64::ARM_Dynarmic_64(System& system_, bool uses_wall_clock_,
+ ExclusiveMonitor& exclusive_monitor_, std::size_t core_index_)
+ : ARM_Interface{system_, uses_wall_clock_},
cb(std::make_unique<DynarmicCallbacks64>(*this)), core_index{core_index_},
exclusive_monitor{dynamic_cast<DynarmicExclusiveMonitor&>(exclusive_monitor_)},
- jit(MakeJit(nullptr, 48)) {}
+ null_jit{MakeJit(nullptr, 48)}, jit{null_jit.get()} {}
ARM_Dynarmic_64::~ARM_Dynarmic_64() = default;
void ARM_Dynarmic_64::SetPC(u64 pc) {
- jit->SetPC(pc);
+ jit.load()->SetPC(pc);
}
u64 ARM_Dynarmic_64::GetPC() const {
- return jit->GetPC();
+ return jit.load()->GetPC();
+}
+
+u64 ARM_Dynarmic_64::GetSP() const {
+ return jit.load()->GetSP();
}
u64 ARM_Dynarmic_64::GetReg(int index) const {
- return jit->GetRegister(index);
+ return jit.load()->GetRegister(index);
}
void ARM_Dynarmic_64::SetReg(int index, u64 value) {
- jit->SetRegister(index, value);
+ jit.load()->SetRegister(index, value);
}
u128 ARM_Dynarmic_64::GetVectorReg(int index) const {
- return jit->GetVector(index);
+ return jit.load()->GetVector(index);
}
void ARM_Dynarmic_64::SetVectorReg(int index, u128 value) {
- jit->SetVector(index, value);
+ jit.load()->SetVector(index, value);
}
u32 ARM_Dynarmic_64::GetPSTATE() const {
- return jit->GetPstate();
+ return jit.load()->GetPstate();
}
void ARM_Dynarmic_64::SetPSTATE(u32 pstate) {
- jit->SetPstate(pstate);
+ jit.load()->SetPstate(pstate);
}
u64 ARM_Dynarmic_64::GetTlsAddress() const {
@@ -344,42 +444,47 @@ void ARM_Dynarmic_64::SetTPIDR_EL0(u64 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();
+ Dynarmic::A64::Jit* j = jit.load();
+ ctx.cpu_registers = j->GetRegisters();
+ ctx.sp = j->GetSP();
+ ctx.pc = j->GetPC();
+ ctx.pstate = j->GetPstate();
+ ctx.vector_registers = j->GetVectors();
+ ctx.fpcr = j->GetFpcr();
+ ctx.fpsr = j->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);
+ Dynarmic::A64::Jit* j = jit.load();
+ j->SetRegisters(ctx.cpu_registers);
+ j->SetSP(ctx.sp);
+ j->SetPC(ctx.pc);
+ j->SetPstate(ctx.pstate);
+ j->SetVectors(ctx.vector_registers);
+ j->SetFpcr(ctx.fpcr);
+ j->SetFpsr(ctx.fpsr);
SetTPIDR_EL0(ctx.tpidr);
}
-void ARM_Dynarmic_64::PrepareReschedule() {
- jit->HaltExecution();
- shutdown = true;
+void ARM_Dynarmic_64::SignalInterrupt() {
+ jit.load()->HaltExecution(break_loop);
+}
+
+void ARM_Dynarmic_64::ClearInterrupt() {
+ jit.load()->ClearHalt(break_loop);
}
void ARM_Dynarmic_64::ClearInstructionCache() {
- jit->ClearCache();
+ jit.load()->ClearCache();
}
void ARM_Dynarmic_64::InvalidateCacheRange(VAddr addr, std::size_t size) {
- jit->InvalidateCacheRange(addr, size);
+ jit.load()->InvalidateCacheRange(addr, size);
}
void ARM_Dynarmic_64::ClearExclusiveState() {
- jit->ClearExclusiveState();
+ jit.load()->ClearExclusiveState();
}
void ARM_Dynarmic_64::PageTableChanged(Common::PageTable& page_table,
@@ -390,13 +495,49 @@ void ARM_Dynarmic_64::PageTableChanged(Common::PageTable& page_table,
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;
+ jit.store(iter->second.get());
LoadContext(ctx);
return;
}
- jit = MakeJit(&page_table, new_address_space_size_in_bits);
+ std::shared_ptr new_jit = MakeJit(&page_table, new_address_space_size_in_bits);
+ jit.store(new_jit.get());
LoadContext(ctx);
- jit_cache.emplace(key, jit);
+ jit_cache.emplace(key, std::move(new_jit));
+}
+
+std::vector<ARM_Interface::BacktraceEntry> ARM_Dynarmic_64::GetBacktrace(Core::System& system,
+ u64 fp, u64 lr, u64 pc) {
+ std::vector<BacktraceEntry> out;
+ auto& memory = system.Memory();
+
+ out.push_back({"", 0, pc, 0, ""});
+
+ // fp (= x29) points to the previous frame record.
+ // Frame records are two words long:
+ // fp+0 : pointer to previous frame record
+ // fp+8 : value of lr for frame
+ while (true) {
+ out.push_back({"", 0, lr, 0, ""});
+ if (!fp || (fp % 4 != 0) || !memory.IsValidVirtualAddressRange(fp, 16)) {
+ break;
+ }
+ lr = memory.Read64(fp + 8);
+ fp = memory.Read64(fp);
+ }
+
+ SymbolicateBacktrace(system, out);
+
+ return out;
+}
+
+std::vector<ARM_Interface::BacktraceEntry> ARM_Dynarmic_64::GetBacktraceFromContext(
+ System& system, const ThreadContext64& ctx) {
+ const auto& reg = ctx.cpu_registers;
+ return GetBacktrace(system, reg[29], reg[30], ctx.pc);
+}
+
+std::vector<ARM_Interface::BacktraceEntry> ARM_Dynarmic_64::GetBacktrace() const {
+ return GetBacktrace(system, GetReg(29), GetReg(30), GetPC());
}
} // namespace Core
diff --git a/src/core/arm/dynarmic/arm_dynarmic_64.h b/src/core/arm/dynarmic/arm_dynarmic_64.h
index 0c4e46c64..ed1a5eb96 100644
--- a/src/core/arm/dynarmic/arm_dynarmic_64.h
+++ b/src/core/arm/dynarmic/arm_dynarmic_64.h
@@ -1,9 +1,9 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
+#include <atomic>
#include <memory>
#include <unordered_map>
@@ -20,26 +20,24 @@ class Memory;
namespace Core {
class DynarmicCallbacks64;
-class CPUInterruptHandler;
class DynarmicExclusiveMonitor;
class System;
class ARM_Dynarmic_64 final : public ARM_Interface {
public:
- ARM_Dynarmic_64(System& system_, CPUInterrupts& interrupt_handlers_, bool uses_wall_clock_,
- ExclusiveMonitor& exclusive_monitor_, std::size_t core_index_);
+ ARM_Dynarmic_64(System& system_, bool uses_wall_clock_, ExclusiveMonitor& exclusive_monitor_,
+ std::size_t core_index_);
~ARM_Dynarmic_64() override;
void SetPC(u64 pc) override;
u64 GetPC() const override;
+ u64 GetSP() 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;
@@ -50,7 +48,8 @@ public:
void LoadContext(const ThreadContext32& ctx) override {}
void LoadContext(const ThreadContext64& ctx) override;
- void PrepareReschedule() override;
+ void SignalInterrupt() override;
+ void ClearInterrupt() override;
void ClearExclusiveState() override;
void ClearInstructionCache() override;
@@ -58,10 +57,24 @@ public:
void PageTableChanged(Common::PageTable& new_page_table,
std::size_t new_address_space_size_in_bits) override;
+ static std::vector<BacktraceEntry> GetBacktraceFromContext(System& system,
+ const ThreadContext64& ctx);
+
+ std::vector<BacktraceEntry> GetBacktrace() const override;
+
+protected:
+ Dynarmic::HaltReason RunJit() override;
+ Dynarmic::HaltReason StepJit() override;
+ u32 GetSvcNumber() const override;
+ const Kernel::DebugWatchpoint* HaltedWatchpoint() const override;
+ void RewindBreakpointInstruction() override;
+
private:
std::shared_ptr<Dynarmic::A64::Jit> MakeJit(Common::PageTable* page_table,
std::size_t address_space_bits) const;
+ static std::vector<BacktraceEntry> GetBacktrace(Core::System& system, u64 fp, u64 lr, u64 pc);
+
using JitCacheKey = std::pair<Common::PageTable*, std::size_t>;
using JitCacheType =
std::unordered_map<JitCacheKey, std::shared_ptr<Dynarmic::A64::Jit>, Common::PairHash>;
@@ -73,13 +86,17 @@ private:
std::size_t core_index;
DynarmicExclusiveMonitor& exclusive_monitor;
- std::shared_ptr<Dynarmic::A64::Jit> jit;
+ std::shared_ptr<Dynarmic::A64::Jit> null_jit;
+
+ // A raw pointer here is fine; we never delete Jit instances.
+ std::atomic<Dynarmic::A64::Jit*> jit;
// SVC callback
u32 svc_swi{};
- bool svc_called{};
- bool shutdown{};
+ // Breakpoint info
+ const Kernel::DebugWatchpoint* halted_watchpoint;
+ ThreadContext64 breakpoint_context;
};
} // namespace Core
diff --git a/src/core/arm/dynarmic/arm_dynarmic_cp15.cpp b/src/core/arm/dynarmic/arm_dynarmic_cp15.cpp
index a043e6735..200efe4db 100644
--- a/src/core/arm/dynarmic/arm_dynarmic_cp15.cpp
+++ b/src/core/arm/dynarmic/arm_dynarmic_cp15.cpp
@@ -1,6 +1,5 @@
-// Copyright 2017 Citra Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: 2017 Citra Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include <fmt/format.h>
#include "common/logging/log.h"
@@ -9,6 +8,10 @@
#include "core/core.h"
#include "core/core_timing.h"
+#ifdef _MSC_VER
+#include <intrin.h>
+#endif
+
using Callback = Dynarmic::A32::Coprocessor::Callback;
using CallbackOrAccessOneWord = Dynarmic::A32::Coprocessor::CallbackOrAccessOneWord;
using CallbackOrAccessTwoWords = Dynarmic::A32::Coprocessor::CallbackOrAccessTwoWords;
@@ -20,7 +23,7 @@ struct fmt::formatter<Dynarmic::A32::CoprocReg> {
}
template <typename FormatContext>
auto format(const Dynarmic::A32::CoprocReg& reg, FormatContext& ctx) {
- return format_to(ctx.out(), "cp{}", static_cast<size_t>(reg));
+ return fmt::format_to(ctx.out(), "cp{}", static_cast<size_t>(reg));
}
};
@@ -48,12 +51,31 @@ CallbackOrAccessOneWord DynarmicCP15::CompileSendOneWord(bool two, unsigned opc1
switch (opc2) {
case 4:
// CP15_DATA_SYNC_BARRIER
- // This is a dummy write, we ignore the value written here.
- return &dummy_value;
+ return Callback{
+ [](Dynarmic::A32::Jit*, void*, std::uint32_t, std::uint32_t) -> std::uint64_t {
+#ifdef _MSC_VER
+ _mm_mfence();
+ _mm_lfence();
+#else
+ asm volatile("mfence\n\tlfence\n\t" : : : "memory");
+#endif
+ return 0;
+ },
+ std::nullopt,
+ };
case 5:
// CP15_DATA_MEMORY_BARRIER
- // This is a dummy write, we ignore the value written here.
- return &dummy_value;
+ return Callback{
+ [](Dynarmic::A32::Jit*, void*, std::uint32_t, std::uint32_t) -> std::uint64_t {
+#ifdef _MSC_VER
+ _mm_mfence();
+#else
+ asm volatile("mfence\n\t" : : : "memory");
+#endif
+ return 0;
+ },
+ std::nullopt,
+ };
}
}
diff --git a/src/core/arm/dynarmic/arm_dynarmic_cp15.h b/src/core/arm/dynarmic/arm_dynarmic_cp15.h
index f271b2070..d90b3e568 100644
--- a/src/core/arm/dynarmic/arm_dynarmic_cp15.h
+++ b/src/core/arm/dynarmic/arm_dynarmic_cp15.h
@@ -1,6 +1,5 @@
-// Copyright 2017 Citra Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: 2017 Citra Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
@@ -36,6 +35,8 @@ public:
ARM_Dynarmic_32& parent;
u32 uprw = 0;
u32 uro = 0;
+
+ friend class ARM_Dynarmic_32;
};
} // namespace Core
diff --git a/src/core/arm/dynarmic/arm_exclusive_monitor.cpp b/src/core/arm/dynarmic/arm_exclusive_monitor.cpp
index 397d054a8..fa0c48b25 100644
--- a/src/core/arm/dynarmic/arm_exclusive_monitor.cpp
+++ b/src/core/arm/dynarmic/arm_exclusive_monitor.cpp
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/arm/dynarmic/arm_exclusive_monitor.h"
#include "core/memory.h"
@@ -37,8 +36,8 @@ u128 DynarmicExclusiveMonitor::ExclusiveRead128(std::size_t core_index, VAddr ad
});
}
-void DynarmicExclusiveMonitor::ClearExclusive() {
- monitor.Clear();
+void DynarmicExclusiveMonitor::ClearExclusive(std::size_t core_index) {
+ monitor.ClearProcessor(core_index);
}
bool DynarmicExclusiveMonitor::ExclusiveWrite8(std::size_t core_index, VAddr vaddr, u8 value) {
diff --git a/src/core/arm/dynarmic/arm_exclusive_monitor.h b/src/core/arm/dynarmic/arm_exclusive_monitor.h
index 265c4ecef..57e6dd0d0 100644
--- a/src/core/arm/dynarmic/arm_exclusive_monitor.h
+++ b/src/core/arm/dynarmic/arm_exclusive_monitor.h
@@ -1,11 +1,8 @@
-// Copyright 2020 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
-#include <unordered_map>
-
#include <dynarmic/interface/exclusive_monitor.h>
#include "common/common_types.h"
@@ -29,7 +26,7 @@ public:
u32 ExclusiveRead32(std::size_t core_index, VAddr addr) override;
u64 ExclusiveRead64(std::size_t core_index, VAddr addr) override;
u128 ExclusiveRead128(std::size_t core_index, VAddr addr) override;
- void ClearExclusive() override;
+ void ClearExclusive(std::size_t core_index) 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;
diff --git a/src/core/arm/exclusive_monitor.cpp b/src/core/arm/exclusive_monitor.cpp
index d8cba369d..2db0b035d 100644
--- a/src/core/arm/exclusive_monitor.cpp
+++ b/src/core/arm/exclusive_monitor.cpp
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#ifdef ARCHITECTURE_x86_64
#include "core/arm/dynarmic/arm_exclusive_monitor.h"
diff --git a/src/core/arm/exclusive_monitor.h b/src/core/arm/exclusive_monitor.h
index 62f6e6023..15d3c96c1 100644
--- a/src/core/arm/exclusive_monitor.h
+++ b/src/core/arm/exclusive_monitor.h
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
@@ -23,7 +22,7 @@ public:
virtual u32 ExclusiveRead32(std::size_t core_index, VAddr addr) = 0;
virtual u64 ExclusiveRead64(std::size_t core_index, VAddr addr) = 0;
virtual u128 ExclusiveRead128(std::size_t core_index, VAddr addr) = 0;
- virtual void ClearExclusive() = 0;
+ virtual void ClearExclusive(std::size_t core_index) = 0;
virtual bool ExclusiveWrite8(std::size_t core_index, VAddr vaddr, u8 value) = 0;
virtual bool ExclusiveWrite16(std::size_t core_index, VAddr vaddr, u16 value) = 0;
diff --git a/src/core/arm/symbols.cpp b/src/core/arm/symbols.cpp
new file mode 100644
index 000000000..0259c7ea2
--- /dev/null
+++ b/src/core/arm/symbols.cpp
@@ -0,0 +1,130 @@
+// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "common/bit_field.h"
+#include "common/common_funcs.h"
+#include "common/elf.h"
+#include "core/arm/symbols.h"
+#include "core/core.h"
+#include "core/memory.h"
+
+using namespace Common::ELF;
+
+namespace Core {
+namespace Symbols {
+
+template <typename Word, typename ELFSymbol, typename ByteReader>
+static Symbols GetSymbols(ByteReader ReadBytes) {
+ const auto Read8{[&](u64 index) {
+ u8 ret;
+ ReadBytes(&ret, index, sizeof(u8));
+ return ret;
+ }};
+
+ const auto Read32{[&](u64 index) {
+ u32 ret;
+ ReadBytes(&ret, index, sizeof(u32));
+ return ret;
+ }};
+
+ const auto ReadWord{[&](u64 index) {
+ Word ret;
+ ReadBytes(&ret, index, sizeof(Word));
+ return ret;
+ }};
+
+ const u32 mod_offset = Read32(4);
+
+ if (Read32(mod_offset) != Common::MakeMagic('M', 'O', 'D', '0')) {
+ return {};
+ }
+
+ VAddr string_table_offset{};
+ VAddr symbol_table_offset{};
+ u64 symbol_entry_size{};
+
+ const auto dynamic_offset = Read32(mod_offset + 0x4) + mod_offset;
+
+ VAddr dynamic_index = dynamic_offset;
+ while (true) {
+ const Word tag = ReadWord(dynamic_index);
+ const Word value = ReadWord(dynamic_index + sizeof(Word));
+ dynamic_index += 2 * sizeof(Word);
+
+ if (tag == ElfDtNull) {
+ break;
+ }
+
+ if (tag == ElfDtStrtab) {
+ string_table_offset = value;
+ } else if (tag == ElfDtSymtab) {
+ symbol_table_offset = value;
+ } else if (tag == ElfDtSyment) {
+ symbol_entry_size = value;
+ }
+ }
+
+ if (string_table_offset == 0 || symbol_table_offset == 0 || symbol_entry_size == 0) {
+ return {};
+ }
+
+ Symbols out;
+
+ VAddr symbol_index = symbol_table_offset;
+ while (symbol_index < string_table_offset) {
+ ELFSymbol symbol{};
+ ReadBytes(&symbol, symbol_index, sizeof(ELFSymbol));
+
+ VAddr string_offset = string_table_offset + symbol.st_name;
+ std::string name;
+ for (u8 c = Read8(string_offset); c != 0; c = Read8(++string_offset)) {
+ name += static_cast<char>(c);
+ }
+
+ symbol_index += symbol_entry_size;
+ out[name] = std::make_pair(symbol.st_value, symbol.st_size);
+ }
+
+ return out;
+}
+
+Symbols GetSymbols(VAddr base, Core::Memory::Memory& memory, bool is_64) {
+ const auto ReadBytes{
+ [&](void* ptr, size_t offset, size_t size) { memory.ReadBlock(base + offset, ptr, size); }};
+
+ if (is_64) {
+ return GetSymbols<u64, Elf64_Sym>(ReadBytes);
+ } else {
+ return GetSymbols<u32, Elf32_Sym>(ReadBytes);
+ }
+}
+
+Symbols GetSymbols(std::span<const u8> data, bool is_64) {
+ const auto ReadBytes{[&](void* ptr, size_t offset, size_t size) {
+ std::memcpy(ptr, data.data() + offset, size);
+ }};
+
+ if (is_64) {
+ return GetSymbols<u64, Elf64_Sym>(ReadBytes);
+ } else {
+ return GetSymbols<u32, Elf32_Sym>(ReadBytes);
+ }
+}
+
+std::optional<std::string> GetSymbolName(const Symbols& symbols, VAddr addr) {
+ const auto iter = std::find_if(symbols.cbegin(), symbols.cend(), [addr](const auto& pair) {
+ const auto& [name, sym_info] = pair;
+ const auto& [start_address, size] = sym_info;
+ const auto end_address = start_address + size;
+ return addr >= start_address && addr < end_address;
+ });
+
+ if (iter == symbols.cend()) {
+ return std::nullopt;
+ }
+
+ return iter->first;
+}
+
+} // namespace Symbols
+} // namespace Core
diff --git a/src/core/arm/symbols.h b/src/core/arm/symbols.h
new file mode 100644
index 000000000..42631b09a
--- /dev/null
+++ b/src/core/arm/symbols.h
@@ -0,0 +1,26 @@
+// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include <map>
+#include <optional>
+#include <span>
+#include <string>
+#include <utility>
+
+#include "common/common_types.h"
+
+namespace Core::Memory {
+class Memory;
+} // namespace Core::Memory
+
+namespace Core::Symbols {
+
+using Symbols = std::map<std::string, std::pair<VAddr, std::size_t>, std::less<>>;
+
+Symbols GetSymbols(VAddr base, Core::Memory::Memory& memory, bool is_64 = true);
+Symbols GetSymbols(std::span<const u8> data, bool is_64 = true);
+std::optional<std::string> GetSymbolName(const Symbols& symbols, VAddr addr);
+
+} // namespace Core::Symbols