diff options
Diffstat (limited to 'src/core/arm')
| -rw-r--r-- | src/core/arm/arm_interface.cpp | 332 | ||||
| -rw-r--r-- | src/core/arm/arm_interface.h | 69 | ||||
| -rw-r--r-- | src/core/arm/cpu_interrupt_handler.cpp | 25 | ||||
| -rw-r--r-- | src/core/arm/cpu_interrupt_handler.h | 40 | ||||
| -rw-r--r-- | src/core/arm/dynarmic/arm_dynarmic_32.cpp | 311 | ||||
| -rw-r--r-- | src/core/arm/dynarmic/arm_dynarmic_32.h | 41 | ||||
| -rw-r--r-- | src/core/arm/dynarmic/arm_dynarmic_64.cpp | 333 | ||||
| -rw-r--r-- | src/core/arm/dynarmic/arm_dynarmic_64.h | 41 | ||||
| -rw-r--r-- | src/core/arm/dynarmic/arm_dynarmic_cp15.cpp | 38 | ||||
| -rw-r--r-- | src/core/arm/dynarmic/arm_dynarmic_cp15.h | 7 | ||||
| -rw-r--r-- | src/core/arm/dynarmic/arm_exclusive_monitor.cpp | 9 | ||||
| -rw-r--r-- | src/core/arm/dynarmic/arm_exclusive_monitor.h | 9 | ||||
| -rw-r--r-- | src/core/arm/exclusive_monitor.cpp | 5 | ||||
| -rw-r--r-- | src/core/arm/exclusive_monitor.h | 7 | ||||
| -rw-r--r-- | src/core/arm/symbols.cpp | 130 | ||||
| -rw-r--r-- | src/core/arm/symbols.h | 26 |
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 = ℘ } -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 |
