summaryrefslogtreecommitdiff
path: root/src/core/arm
diff options
context:
space:
mode:
authorGravatar Levi2021-01-10 22:09:56 -0700
committerGravatar Levi2021-01-10 22:09:56 -0700
commit7a3c884e39fccfbb498b855080bffabc9ce2e7f1 (patch)
tree5056f9406dec188439cb0deb87603498243a9412 /src/core/arm
parentMore forgetting... duh (diff)
parentMerge pull request #5229 from Morph1984/fullscreen-opt (diff)
downloadyuzu-7a3c884e39fccfbb498b855080bffabc9ce2e7f1.tar.gz
yuzu-7a3c884e39fccfbb498b855080bffabc9ce2e7f1.tar.xz
yuzu-7a3c884e39fccfbb498b855080bffabc9ce2e7f1.zip
Merge remote-tracking branch 'upstream/master' into int-flags
Diffstat (limited to 'src/core/arm')
-rw-r--r--src/core/arm/arm_interface.cpp12
-rw-r--r--src/core/arm/arm_interface.h22
-rw-r--r--src/core/arm/cpu_interrupt_handler.h4
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic_32.cpp28
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic_32.h2
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic_64.cpp60
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic_64.h4
-rw-r--r--src/core/arm/unicorn/arm_unicorn.cpp295
-rw-r--r--src/core/arm/unicorn/arm_unicorn.h63
9 files changed, 79 insertions, 411 deletions
diff --git a/src/core/arm/arm_interface.cpp b/src/core/arm/arm_interface.cpp
index d2295ed90..0951e1976 100644
--- a/src/core/arm/arm_interface.cpp
+++ b/src/core/arm/arm_interface.cpp
@@ -147,10 +147,18 @@ std::vector<ARM_Interface::BacktraceEntry> ARM_Interface::GetBacktraceFromContex
147 auto fp = ctx.cpu_registers[29]; 147 auto fp = ctx.cpu_registers[29];
148 auto lr = ctx.cpu_registers[30]; 148 auto lr = ctx.cpu_registers[30];
149 while (true) { 149 while (true) {
150 out.push_back({"", 0, lr, 0}); 150 out.push_back({
151 if (!fp) { 151 .module = "",
152 .address = 0,
153 .original_address = lr,
154 .offset = 0,
155 .name = {},
156 });
157
158 if (fp == 0) {
152 break; 159 break;
153 } 160 }
161
154 lr = memory.Read64(fp + 8) - 4; 162 lr = memory.Read64(fp + 8) - 4;
155 fp = memory.Read64(fp); 163 fp = memory.Read64(fp);
156 } 164 }
diff --git a/src/core/arm/arm_interface.h b/src/core/arm/arm_interface.h
index 1f24051e4..70098c526 100644
--- a/src/core/arm/arm_interface.h
+++ b/src/core/arm/arm_interface.h
@@ -64,15 +64,25 @@ public:
64 /// Step CPU by one instruction 64 /// Step CPU by one instruction
65 virtual void Step() = 0; 65 virtual void Step() = 0;
66 66
67 /// Exits execution from a callback, the callback must rewind the stack
68 virtual void ExceptionalExit() = 0;
69
67 /// Clear all instruction cache 70 /// Clear all instruction cache
68 virtual void ClearInstructionCache() = 0; 71 virtual void ClearInstructionCache() = 0;
69 72
70 /// Notifies CPU emulation that the current page table has changed. 73 /**
71 /// 74 * Clear instruction cache range
72 /// @param new_page_table The new page table. 75 * @param addr Start address of the cache range to clear
73 /// @param new_address_space_size_in_bits The new usable size of the address space in bits. 76 * @param size Size of the cache range to clear, starting at addr
74 /// This can be either 32, 36, or 39 on official software. 77 */
75 /// 78 virtual void InvalidateCacheRange(VAddr addr, std::size_t size) = 0;
79
80 /**
81 * Notifies CPU emulation that the current page table has changed.
82 * @param new_page_table The new page table.
83 * @param new_address_space_size_in_bits The new usable size of the address space in bits.
84 * This can be either 32, 36, or 39 on official software.
85 */
76 virtual void PageTableChanged(Common::PageTable& new_page_table, 86 virtual void PageTableChanged(Common::PageTable& new_page_table,
77 std::size_t new_address_space_size_in_bits) = 0; 87 std::size_t new_address_space_size_in_bits) = 0;
78 88
diff --git a/src/core/arm/cpu_interrupt_handler.h b/src/core/arm/cpu_interrupt_handler.h
index 71e582f79..c20c280f1 100644
--- a/src/core/arm/cpu_interrupt_handler.h
+++ b/src/core/arm/cpu_interrupt_handler.h
@@ -21,8 +21,8 @@ public:
21 CPUInterruptHandler(const CPUInterruptHandler&) = delete; 21 CPUInterruptHandler(const CPUInterruptHandler&) = delete;
22 CPUInterruptHandler& operator=(const CPUInterruptHandler&) = delete; 22 CPUInterruptHandler& operator=(const CPUInterruptHandler&) = delete;
23 23
24 CPUInterruptHandler(CPUInterruptHandler&&) = default; 24 CPUInterruptHandler(CPUInterruptHandler&&) = delete;
25 CPUInterruptHandler& operator=(CPUInterruptHandler&&) = default; 25 CPUInterruptHandler& operator=(CPUInterruptHandler&&) = delete;
26 26
27 bool IsInterrupted() const { 27 bool IsInterrupted() const {
28 return is_interrupted; 28 return is_interrupted;
diff --git a/src/core/arm/dynarmic/arm_dynarmic_32.cpp b/src/core/arm/dynarmic/arm_dynarmic_32.cpp
index b5f28a86e..6c4c8e9e4 100644
--- a/src/core/arm/dynarmic/arm_dynarmic_32.cpp
+++ b/src/core/arm/dynarmic/arm_dynarmic_32.cpp
@@ -7,6 +7,7 @@
7#include <dynarmic/A32/a32.h> 7#include <dynarmic/A32/a32.h>
8#include <dynarmic/A32/config.h> 8#include <dynarmic/A32/config.h>
9#include <dynarmic/A32/context.h> 9#include <dynarmic/A32/context.h>
10#include "common/assert.h"
10#include "common/logging/log.h" 11#include "common/logging/log.h"
11#include "common/page_table.h" 12#include "common/page_table.h"
12#include "core/arm/cpu_interrupt_handler.h" 13#include "core/arm/cpu_interrupt_handler.h"
@@ -70,15 +71,8 @@ public:
70 } 71 }
71 72
72 void ExceptionRaised(u32 pc, Dynarmic::A32::Exception exception) override { 73 void ExceptionRaised(u32 pc, Dynarmic::A32::Exception exception) override {
73 switch (exception) {
74 case Dynarmic::A32::Exception::UndefinedInstruction:
75 case Dynarmic::A32::Exception::UnpredictableInstruction:
76 break;
77 case Dynarmic::A32::Exception::Breakpoint:
78 break;
79 }
80 LOG_CRITICAL(Core_ARM, "ExceptionRaised(exception = {}, pc = {:08X}, code = {:08X})", 74 LOG_CRITICAL(Core_ARM, "ExceptionRaised(exception = {}, pc = {:08X}, code = {:08X})",
81 static_cast<std::size_t>(exception), pc, MemoryReadCode(pc)); 75 exception, pc, MemoryReadCode(pc));
82 UNIMPLEMENTED(); 76 UNIMPLEMENTED();
83 } 77 }
84 78
@@ -132,6 +126,7 @@ std::shared_ptr<Dynarmic::A32::Jit> ARM_Dynarmic_32::MakeJit(Common::PageTable&
132 config.page_table = reinterpret_cast<std::array<std::uint8_t*, NUM_PAGE_TABLE_ENTRIES>*>( 126 config.page_table = reinterpret_cast<std::array<std::uint8_t*, NUM_PAGE_TABLE_ENTRIES>*>(
133 page_table.pointers.data()); 127 page_table.pointers.data());
134 config.absolute_offset_page_table = true; 128 config.absolute_offset_page_table = true;
129 config.page_table_pointer_mask_bits = Common::PageTable::ATTRIBUTE_BITS;
135 config.detect_misaligned_access_via_page_table = 16 | 32 | 64 | 128; 130 config.detect_misaligned_access_via_page_table = 16 | 32 | 64 | 128;
136 config.only_detect_misalignment_via_page_table_on_page_boundary = true; 131 config.only_detect_misalignment_via_page_table_on_page_boundary = true;
137 132
@@ -179,6 +174,9 @@ std::shared_ptr<Dynarmic::A32::Jit> ARM_Dynarmic_32::MakeJit(Common::PageTable&
179 if (Settings::values.cpuopt_unsafe_reduce_fp_error) { 174 if (Settings::values.cpuopt_unsafe_reduce_fp_error) {
180 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_ReducedErrorFP; 175 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_ReducedErrorFP;
181 } 176 }
177 if (Settings::values.cpuopt_unsafe_inaccurate_nan) {
178 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_InaccurateNaN;
179 }
182 } 180 }
183 181
184 return std::make_unique<Dynarmic::A32::Jit>(config); 182 return std::make_unique<Dynarmic::A32::Jit>(config);
@@ -188,6 +186,10 @@ void ARM_Dynarmic_32::Run() {
188 jit->Run(); 186 jit->Run();
189} 187}
190 188
189void ARM_Dynarmic_32::ExceptionalExit() {
190 jit->ExceptionalExit();
191}
192
191void ARM_Dynarmic_32::Step() { 193void ARM_Dynarmic_32::Step() {
192 jit->Step(); 194 jit->Step();
193} 195}
@@ -281,7 +283,17 @@ void ARM_Dynarmic_32::ClearInstructionCache() {
281 jit->ClearCache(); 283 jit->ClearCache();
282} 284}
283 285
286void ARM_Dynarmic_32::InvalidateCacheRange(VAddr addr, std::size_t size) {
287 if (!jit) {
288 return;
289 }
290 jit->InvalidateCacheRange(static_cast<u32>(addr), size);
291}
292
284void ARM_Dynarmic_32::ClearExclusiveState() { 293void ARM_Dynarmic_32::ClearExclusiveState() {
294 if (!jit) {
295 return;
296 }
285 jit->ClearExclusiveState(); 297 jit->ClearExclusiveState();
286} 298}
287 299
diff --git a/src/core/arm/dynarmic/arm_dynarmic_32.h b/src/core/arm/dynarmic/arm_dynarmic_32.h
index 2bab31b92..35e9ced48 100644
--- a/src/core/arm/dynarmic/arm_dynarmic_32.h
+++ b/src/core/arm/dynarmic/arm_dynarmic_32.h
@@ -42,6 +42,7 @@ public:
42 u32 GetPSTATE() const override; 42 u32 GetPSTATE() const override;
43 void SetPSTATE(u32 pstate) override; 43 void SetPSTATE(u32 pstate) override;
44 void Run() override; 44 void Run() override;
45 void ExceptionalExit() override;
45 void Step() override; 46 void Step() override;
46 VAddr GetTlsAddress() const override; 47 VAddr GetTlsAddress() const override;
47 void SetTlsAddress(VAddr address) override; 48 void SetTlsAddress(VAddr address) override;
@@ -58,6 +59,7 @@ public:
58 void ClearExclusiveState() override; 59 void ClearExclusiveState() override;
59 60
60 void ClearInstructionCache() override; 61 void ClearInstructionCache() override;
62 void InvalidateCacheRange(VAddr addr, std::size_t size) override;
61 void PageTableChanged(Common::PageTable& new_page_table, 63 void PageTableChanged(Common::PageTable& new_page_table,
62 std::size_t new_address_space_size_in_bits) override; 64 std::size_t new_address_space_size_in_bits) override;
63 65
diff --git a/src/core/arm/dynarmic/arm_dynarmic_64.cpp b/src/core/arm/dynarmic/arm_dynarmic_64.cpp
index ce9968724..4c5ebca22 100644
--- a/src/core/arm/dynarmic/arm_dynarmic_64.cpp
+++ b/src/core/arm/dynarmic/arm_dynarmic_64.cpp
@@ -6,6 +6,7 @@
6#include <memory> 6#include <memory>
7#include <dynarmic/A64/a64.h> 7#include <dynarmic/A64/a64.h>
8#include <dynarmic/A64/config.h> 8#include <dynarmic/A64/config.h>
9#include "common/assert.h"
9#include "common/logging/log.h" 10#include "common/logging/log.h"
10#include "common/page_table.h" 11#include "common/page_table.h"
11#include "core/arm/cpu_interrupt_handler.h" 12#include "core/arm/cpu_interrupt_handler.h"
@@ -13,11 +14,9 @@
13#include "core/arm/dynarmic/arm_exclusive_monitor.h" 14#include "core/arm/dynarmic/arm_exclusive_monitor.h"
14#include "core/core.h" 15#include "core/core.h"
15#include "core/core_timing.h" 16#include "core/core_timing.h"
16#include "core/core_timing_util.h"
17#include "core/gdbstub/gdbstub.h"
18#include "core/hardware_properties.h" 17#include "core/hardware_properties.h"
18#include "core/hle/kernel/k_scheduler.h"
19#include "core/hle/kernel/process.h" 19#include "core/hle/kernel/process.h"
20#include "core/hle/kernel/scheduler.h"
21#include "core/hle/kernel/svc.h" 20#include "core/hle/kernel/svc.h"
22#include "core/memory.h" 21#include "core/memory.h"
23#include "core/settings.h" 22#include "core/settings.h"
@@ -82,16 +81,9 @@ public:
82 } 81 }
83 82
84 void InterpreterFallback(u64 pc, std::size_t num_instructions) override { 83 void InterpreterFallback(u64 pc, std::size_t num_instructions) override {
85 LOG_INFO(Core_ARM, "Unicorn fallback @ 0x{:X} for {} instructions (instr = {:08X})", pc, 84 LOG_ERROR(Core_ARM,
86 num_instructions, MemoryReadCode(pc)); 85 "Unimplemented instruction @ 0x{:X} for {} instructions (instr = {:08X})", pc,
87 86 num_instructions, MemoryReadCode(pc));
88 ARM_Interface::ThreadContext64 ctx;
89 parent.SaveContext(ctx);
90 parent.inner_unicorn.LoadContext(ctx);
91 parent.inner_unicorn.ExecuteInstructions(num_instructions);
92 parent.inner_unicorn.SaveContext(ctx);
93 parent.LoadContext(ctx);
94 num_interpreted_instructions += num_instructions;
95 } 87 }
96 88
97 void ExceptionRaised(u64 pc, Dynarmic::A64::Exception exception) override { 89 void ExceptionRaised(u64 pc, Dynarmic::A64::Exception exception) override {
@@ -103,16 +95,6 @@ public:
103 case Dynarmic::A64::Exception::Yield: 95 case Dynarmic::A64::Exception::Yield:
104 return; 96 return;
105 case Dynarmic::A64::Exception::Breakpoint: 97 case Dynarmic::A64::Exception::Breakpoint:
106 if (GDBStub::IsServerEnabled()) {
107 parent.jit->HaltExecution();
108 parent.SetPC(pc);
109 Kernel::Thread* const thread = parent.system.CurrentScheduler().GetCurrentThread();
110 parent.SaveContext(thread->GetContext64());
111 GDBStub::Break();
112 GDBStub::SendTrap(thread, 5);
113 return;
114 }
115 [[fallthrough]];
116 default: 98 default:
117 ASSERT_MSG(false, "ExceptionRaised(exception = {}, pc = {:08X}, code = {:08X})", 99 ASSERT_MSG(false, "ExceptionRaised(exception = {}, pc = {:08X}, code = {:08X})",
118 static_cast<std::size_t>(exception), pc, MemoryReadCode(pc)); 100 static_cast<std::size_t>(exception), pc, MemoryReadCode(pc));
@@ -127,18 +109,17 @@ public:
127 if (parent.uses_wall_clock) { 109 if (parent.uses_wall_clock) {
128 return; 110 return;
129 } 111 }
112
130 // Divide the number of ticks by the amount of CPU cores. TODO(Subv): This yields only a 113 // Divide the number of ticks by the amount of CPU cores. TODO(Subv): This yields only a
131 // rough approximation of the amount of executed ticks in the system, it may be thrown off 114 // rough approximation of the amount of executed ticks in the system, it may be thrown off
132 // if not all cores are doing a similar amount of work. Instead of doing this, we should 115 // if not all cores are doing a similar amount of work. Instead of doing this, we should
133 // device a way so that timing is consistent across all cores without increasing the ticks 4 116 // device a way so that timing is consistent across all cores without increasing the ticks 4
134 // times. 117 // times.
135 u64 amortized_ticks = 118 u64 amortized_ticks = ticks / Core::Hardware::NUM_CPU_CORES;
136 (ticks - num_interpreted_instructions) / Core::Hardware::NUM_CPU_CORES;
137 // Always execute at least one tick. 119 // Always execute at least one tick.
138 amortized_ticks = std::max<u64>(amortized_ticks, 1); 120 amortized_ticks = std::max<u64>(amortized_ticks, 1);
139 121
140 parent.system.CoreTiming().AddTicks(amortized_ticks); 122 parent.system.CoreTiming().AddTicks(amortized_ticks);
141 num_interpreted_instructions = 0;
142 } 123 }
143 124
144 u64 GetTicksRemaining() override { 125 u64 GetTicksRemaining() override {
@@ -156,7 +137,6 @@ public:
156 } 137 }
157 138
158 ARM_Dynarmic_64& parent; 139 ARM_Dynarmic_64& parent;
159 std::size_t num_interpreted_instructions = 0;
160 u64 tpidrro_el0 = 0; 140 u64 tpidrro_el0 = 0;
161 u64 tpidr_el0 = 0; 141 u64 tpidr_el0 = 0;
162 static constexpr u64 minimum_run_cycles = 1000U; 142 static constexpr u64 minimum_run_cycles = 1000U;
@@ -172,6 +152,7 @@ std::shared_ptr<Dynarmic::A64::Jit> ARM_Dynarmic_64::MakeJit(Common::PageTable&
172 // Memory 152 // Memory
173 config.page_table = reinterpret_cast<void**>(page_table.pointers.data()); 153 config.page_table = reinterpret_cast<void**>(page_table.pointers.data());
174 config.page_table_address_space_bits = address_space_bits; 154 config.page_table_address_space_bits = address_space_bits;
155 config.page_table_pointer_mask_bits = Common::PageTable::ATTRIBUTE_BITS;
175 config.silently_mirror_page_table = false; 156 config.silently_mirror_page_table = false;
176 config.absolute_offset_page_table = true; 157 config.absolute_offset_page_table = true;
177 config.detect_misaligned_access_via_page_table = 16 | 32 | 64 | 128; 158 config.detect_misaligned_access_via_page_table = 16 | 32 | 64 | 128;
@@ -231,6 +212,9 @@ std::shared_ptr<Dynarmic::A64::Jit> ARM_Dynarmic_64::MakeJit(Common::PageTable&
231 if (Settings::values.cpuopt_unsafe_reduce_fp_error) { 212 if (Settings::values.cpuopt_unsafe_reduce_fp_error) {
232 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_ReducedErrorFP; 213 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_ReducedErrorFP;
233 } 214 }
215 if (Settings::values.cpuopt_unsafe_inaccurate_nan) {
216 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_InaccurateNaN;
217 }
234 } 218 }
235 219
236 return std::make_shared<Dynarmic::A64::Jit>(config); 220 return std::make_shared<Dynarmic::A64::Jit>(config);
@@ -240,6 +224,10 @@ void ARM_Dynarmic_64::Run() {
240 jit->Run(); 224 jit->Run();
241} 225}
242 226
227void ARM_Dynarmic_64::ExceptionalExit() {
228 jit->ExceptionalExit();
229}
230
243void ARM_Dynarmic_64::Step() { 231void ARM_Dynarmic_64::Step() {
244 cb->InterpreterFallback(jit->GetPC(), 1); 232 cb->InterpreterFallback(jit->GetPC(), 1);
245} 233}
@@ -248,12 +236,8 @@ ARM_Dynarmic_64::ARM_Dynarmic_64(System& system, CPUInterrupts& interrupt_handle
248 bool uses_wall_clock, ExclusiveMonitor& exclusive_monitor, 236 bool uses_wall_clock, ExclusiveMonitor& exclusive_monitor,
249 std::size_t core_index) 237 std::size_t core_index)
250 : ARM_Interface{system, interrupt_handlers, uses_wall_clock}, 238 : ARM_Interface{system, interrupt_handlers, uses_wall_clock},
251 cb(std::make_unique<DynarmicCallbacks64>(*this)), inner_unicorn{system, interrupt_handlers, 239 cb(std::make_unique<DynarmicCallbacks64>(*this)), core_index{core_index},
252 uses_wall_clock, 240 exclusive_monitor{dynamic_cast<DynarmicExclusiveMonitor&>(exclusive_monitor)} {}
253 ARM_Unicorn::Arch::AArch64,
254 core_index},
255 core_index{core_index}, exclusive_monitor{
256 dynamic_cast<DynarmicExclusiveMonitor&>(exclusive_monitor)} {}
257 241
258ARM_Dynarmic_64::~ARM_Dynarmic_64() = default; 242ARM_Dynarmic_64::~ARM_Dynarmic_64() = default;
259 243
@@ -342,7 +326,17 @@ void ARM_Dynarmic_64::ClearInstructionCache() {
342 jit->ClearCache(); 326 jit->ClearCache();
343} 327}
344 328
329void ARM_Dynarmic_64::InvalidateCacheRange(VAddr addr, std::size_t size) {
330 if (!jit) {
331 return;
332 }
333 jit->InvalidateCacheRange(addr, size);
334}
335
345void ARM_Dynarmic_64::ClearExclusiveState() { 336void ARM_Dynarmic_64::ClearExclusiveState() {
337 if (!jit) {
338 return;
339 }
346 jit->ClearExclusiveState(); 340 jit->ClearExclusiveState();
347} 341}
348 342
diff --git a/src/core/arm/dynarmic/arm_dynarmic_64.h b/src/core/arm/dynarmic/arm_dynarmic_64.h
index 403c55961..329b59a32 100644
--- a/src/core/arm/dynarmic/arm_dynarmic_64.h
+++ b/src/core/arm/dynarmic/arm_dynarmic_64.h
@@ -12,7 +12,6 @@
12#include "common/hash.h" 12#include "common/hash.h"
13#include "core/arm/arm_interface.h" 13#include "core/arm/arm_interface.h"
14#include "core/arm/exclusive_monitor.h" 14#include "core/arm/exclusive_monitor.h"
15#include "core/arm/unicorn/arm_unicorn.h"
16 15
17namespace Core::Memory { 16namespace Core::Memory {
18class Memory; 17class Memory;
@@ -41,6 +40,7 @@ public:
41 void SetPSTATE(u32 pstate) override; 40 void SetPSTATE(u32 pstate) override;
42 void Run() override; 41 void Run() override;
43 void Step() override; 42 void Step() override;
43 void ExceptionalExit() override;
44 VAddr GetTlsAddress() const override; 44 VAddr GetTlsAddress() const override;
45 void SetTlsAddress(VAddr address) override; 45 void SetTlsAddress(VAddr address) override;
46 void SetTPIDR_EL0(u64 value) override; 46 void SetTPIDR_EL0(u64 value) override;
@@ -56,6 +56,7 @@ public:
56 void ClearExclusiveState() override; 56 void ClearExclusiveState() override;
57 57
58 void ClearInstructionCache() override; 58 void ClearInstructionCache() override;
59 void InvalidateCacheRange(VAddr addr, std::size_t size) override;
59 void PageTableChanged(Common::PageTable& new_page_table, 60 void PageTableChanged(Common::PageTable& new_page_table,
60 std::size_t new_address_space_size_in_bits) override; 61 std::size_t new_address_space_size_in_bits) override;
61 62
@@ -71,7 +72,6 @@ private:
71 std::unique_ptr<DynarmicCallbacks64> cb; 72 std::unique_ptr<DynarmicCallbacks64> cb;
72 JitCacheType jit_cache; 73 JitCacheType jit_cache;
73 std::shared_ptr<Dynarmic::A64::Jit> jit; 74 std::shared_ptr<Dynarmic::A64::Jit> jit;
74 ARM_Unicorn inner_unicorn;
75 75
76 std::size_t core_index; 76 std::size_t core_index;
77 DynarmicExclusiveMonitor& exclusive_monitor; 77 DynarmicExclusiveMonitor& exclusive_monitor;
diff --git a/src/core/arm/unicorn/arm_unicorn.cpp b/src/core/arm/unicorn/arm_unicorn.cpp
deleted file mode 100644
index 1df3f3ed1..000000000
--- a/src/core/arm/unicorn/arm_unicorn.cpp
+++ /dev/null
@@ -1,295 +0,0 @@
1// Copyright 2018 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <algorithm>
6#include <unicorn/arm64.h>
7#include "common/assert.h"
8#include "common/microprofile.h"
9#include "core/arm/cpu_interrupt_handler.h"
10#include "core/arm/unicorn/arm_unicorn.h"
11#include "core/core.h"
12#include "core/core_timing.h"
13#include "core/hle/kernel/scheduler.h"
14#include "core/hle/kernel/svc.h"
15#include "core/memory.h"
16
17namespace Core {
18
19// Load Unicorn DLL once on Windows using RAII
20#ifdef _MSC_VER
21#include <unicorn_dynload.h>
22struct LoadDll {
23private:
24 LoadDll() {
25 ASSERT(uc_dyn_load(NULL, 0));
26 }
27 ~LoadDll() {
28 ASSERT(uc_dyn_free());
29 }
30 static LoadDll g_load_dll;
31};
32LoadDll LoadDll::g_load_dll;
33#endif
34
35#define CHECKED(expr) \
36 do { \
37 if (auto _cerr = (expr)) { \
38 ASSERT_MSG(false, "Call " #expr " failed with error: {} ({})\n", _cerr, \
39 uc_strerror(_cerr)); \
40 } \
41 } while (0)
42
43static void CodeHook(uc_engine* uc, uint64_t address, uint32_t size, void* user_data) {
44 GDBStub::BreakpointAddress bkpt =
45 GDBStub::GetNextBreakpointFromAddress(address, GDBStub::BreakpointType::Execute);
46 if (GDBStub::IsMemoryBreak() ||
47 (bkpt.type != GDBStub::BreakpointType::None && address == bkpt.address)) {
48 auto core = static_cast<ARM_Unicorn*>(user_data);
49 core->RecordBreak(bkpt);
50 uc_emu_stop(uc);
51 }
52}
53
54static bool UnmappedMemoryHook(uc_engine* uc, uc_mem_type type, u64 addr, int size, u64 value,
55 void* user_data) {
56 auto* const system = static_cast<System*>(user_data);
57
58 ARM_Interface::ThreadContext64 ctx{};
59 system->CurrentArmInterface().SaveContext(ctx);
60 ASSERT_MSG(false, "Attempted to read from unmapped memory: 0x{:X}, pc=0x{:X}, lr=0x{:X}", addr,
61 ctx.pc, ctx.cpu_registers[30]);
62
63 return false;
64}
65
66ARM_Unicorn::ARM_Unicorn(System& system, CPUInterrupts& interrupt_handlers, bool uses_wall_clock,
67 Arch architecture, std::size_t core_index)
68 : ARM_Interface{system, interrupt_handlers, uses_wall_clock}, core_index{core_index} {
69 const auto arch = architecture == Arch::AArch32 ? UC_ARCH_ARM : UC_ARCH_ARM64;
70 CHECKED(uc_open(arch, UC_MODE_ARM, &uc));
71
72 auto fpv = 3 << 20;
73 CHECKED(uc_reg_write(uc, UC_ARM64_REG_CPACR_EL1, &fpv));
74
75 uc_hook hook{};
76 CHECKED(uc_hook_add(uc, &hook, UC_HOOK_INTR, (void*)InterruptHook, this, 0, UINT64_MAX));
77 CHECKED(uc_hook_add(uc, &hook, UC_HOOK_MEM_INVALID, (void*)UnmappedMemoryHook, &system, 0,
78 UINT64_MAX));
79 if (GDBStub::IsServerEnabled()) {
80 CHECKED(uc_hook_add(uc, &hook, UC_HOOK_CODE, (void*)CodeHook, this, 0, UINT64_MAX));
81 last_bkpt_hit = false;
82 }
83}
84
85ARM_Unicorn::~ARM_Unicorn() {
86 CHECKED(uc_close(uc));
87}
88
89void ARM_Unicorn::SetPC(u64 pc) {
90 CHECKED(uc_reg_write(uc, UC_ARM64_REG_PC, &pc));
91}
92
93u64 ARM_Unicorn::GetPC() const {
94 u64 val{};
95 CHECKED(uc_reg_read(uc, UC_ARM64_REG_PC, &val));
96 return val;
97}
98
99u64 ARM_Unicorn::GetReg(int regn) const {
100 u64 val{};
101 auto treg = UC_ARM64_REG_SP;
102 if (regn <= 28) {
103 treg = (uc_arm64_reg)(UC_ARM64_REG_X0 + regn);
104 } else if (regn < 31) {
105 treg = (uc_arm64_reg)(UC_ARM64_REG_X29 + regn - 29);
106 }
107 CHECKED(uc_reg_read(uc, treg, &val));
108 return val;
109}
110
111void ARM_Unicorn::SetReg(int regn, u64 val) {
112 auto treg = UC_ARM64_REG_SP;
113 if (regn <= 28) {
114 treg = (uc_arm64_reg)(UC_ARM64_REG_X0 + regn);
115 } else if (regn < 31) {
116 treg = (uc_arm64_reg)(UC_ARM64_REG_X29 + regn - 29);
117 }
118 CHECKED(uc_reg_write(uc, treg, &val));
119}
120
121u128 ARM_Unicorn::GetVectorReg(int /*index*/) const {
122 UNIMPLEMENTED();
123 static constexpr u128 res{};
124 return res;
125}
126
127void ARM_Unicorn::SetVectorReg(int /*index*/, u128 /*value*/) {
128 UNIMPLEMENTED();
129}
130
131u32 ARM_Unicorn::GetPSTATE() const {
132 u64 nzcv{};
133 CHECKED(uc_reg_read(uc, UC_ARM64_REG_NZCV, &nzcv));
134 return static_cast<u32>(nzcv);
135}
136
137void ARM_Unicorn::SetPSTATE(u32 pstate) {
138 u64 nzcv = pstate;
139 CHECKED(uc_reg_write(uc, UC_ARM64_REG_NZCV, &nzcv));
140}
141
142VAddr ARM_Unicorn::GetTlsAddress() const {
143 u64 base{};
144 CHECKED(uc_reg_read(uc, UC_ARM64_REG_TPIDRRO_EL0, &base));
145 return base;
146}
147
148void ARM_Unicorn::SetTlsAddress(VAddr base) {
149 CHECKED(uc_reg_write(uc, UC_ARM64_REG_TPIDRRO_EL0, &base));
150}
151
152u64 ARM_Unicorn::GetTPIDR_EL0() const {
153 u64 value{};
154 CHECKED(uc_reg_read(uc, UC_ARM64_REG_TPIDR_EL0, &value));
155 return value;
156}
157
158void ARM_Unicorn::SetTPIDR_EL0(u64 value) {
159 CHECKED(uc_reg_write(uc, UC_ARM64_REG_TPIDR_EL0, &value));
160}
161
162void ARM_Unicorn::ChangeProcessorID(std::size_t new_core_id) {
163 core_index = new_core_id;
164}
165
166void ARM_Unicorn::Run() {
167 if (GDBStub::IsServerEnabled()) {
168 ExecuteInstructions(std::max(4000000U, 0U));
169 } else {
170 while (true) {
171 if (interrupt_handlers[core_index].IsInterrupted()) {
172 return;
173 }
174 ExecuteInstructions(10);
175 }
176 }
177}
178
179void ARM_Unicorn::Step() {
180 ExecuteInstructions(1);
181}
182
183MICROPROFILE_DEFINE(ARM_Jit_Unicorn, "ARM JIT", "Unicorn", MP_RGB(255, 64, 64));
184
185void ARM_Unicorn::ExecuteInstructions(std::size_t num_instructions) {
186 MICROPROFILE_SCOPE(ARM_Jit_Unicorn);
187
188 // Temporarily map the code page for Unicorn
189 u64 map_addr{GetPC() & ~Memory::PAGE_MASK};
190 std::vector<u8> page_buffer(Memory::PAGE_SIZE);
191 system.Memory().ReadBlock(map_addr, page_buffer.data(), page_buffer.size());
192
193 CHECKED(uc_mem_map_ptr(uc, map_addr, page_buffer.size(),
194 UC_PROT_READ | UC_PROT_WRITE | UC_PROT_EXEC, page_buffer.data()));
195 CHECKED(uc_emu_start(uc, GetPC(), 1ULL << 63, 0, num_instructions));
196 CHECKED(uc_mem_unmap(uc, map_addr, page_buffer.size()));
197 if (GDBStub::IsServerEnabled()) {
198 if (last_bkpt_hit && last_bkpt.type == GDBStub::BreakpointType::Execute) {
199 uc_reg_write(uc, UC_ARM64_REG_PC, &last_bkpt.address);
200 }
201
202 Kernel::Thread* const thread = system.CurrentScheduler().GetCurrentThread();
203 SaveContext(thread->GetContext64());
204 if (last_bkpt_hit || GDBStub::IsMemoryBreak() || GDBStub::GetCpuStepFlag()) {
205 last_bkpt_hit = false;
206 GDBStub::Break();
207 GDBStub::SendTrap(thread, 5);
208 }
209 }
210}
211
212void ARM_Unicorn::SaveContext(ThreadContext64& ctx) {
213 int uregs[32];
214 void* tregs[32];
215
216 CHECKED(uc_reg_read(uc, UC_ARM64_REG_SP, &ctx.sp));
217 CHECKED(uc_reg_read(uc, UC_ARM64_REG_PC, &ctx.pc));
218 CHECKED(uc_reg_read(uc, UC_ARM64_REG_NZCV, &ctx.pstate));
219
220 for (auto i = 0; i < 29; ++i) {
221 uregs[i] = UC_ARM64_REG_X0 + i;
222 tregs[i] = &ctx.cpu_registers[i];
223 }
224 uregs[29] = UC_ARM64_REG_X29;
225 tregs[29] = (void*)&ctx.cpu_registers[29];
226 uregs[30] = UC_ARM64_REG_X30;
227 tregs[30] = (void*)&ctx.cpu_registers[30];
228
229 CHECKED(uc_reg_read_batch(uc, uregs, tregs, 31));
230
231 for (int i = 0; i < 32; ++i) {
232 uregs[i] = UC_ARM64_REG_Q0 + i;
233 tregs[i] = &ctx.vector_registers[i];
234 }
235
236 CHECKED(uc_reg_read_batch(uc, uregs, tregs, 32));
237}
238
239void ARM_Unicorn::LoadContext(const ThreadContext64& ctx) {
240 int uregs[32];
241 void* tregs[32];
242
243 CHECKED(uc_reg_write(uc, UC_ARM64_REG_SP, &ctx.sp));
244 CHECKED(uc_reg_write(uc, UC_ARM64_REG_PC, &ctx.pc));
245 CHECKED(uc_reg_write(uc, UC_ARM64_REG_NZCV, &ctx.pstate));
246
247 for (int i = 0; i < 29; ++i) {
248 uregs[i] = UC_ARM64_REG_X0 + i;
249 tregs[i] = (void*)&ctx.cpu_registers[i];
250 }
251 uregs[29] = UC_ARM64_REG_X29;
252 tregs[29] = (void*)&ctx.cpu_registers[29];
253 uregs[30] = UC_ARM64_REG_X30;
254 tregs[30] = (void*)&ctx.cpu_registers[30];
255
256 CHECKED(uc_reg_write_batch(uc, uregs, tregs, 31));
257
258 for (auto i = 0; i < 32; ++i) {
259 uregs[i] = UC_ARM64_REG_Q0 + i;
260 tregs[i] = (void*)&ctx.vector_registers[i];
261 }
262
263 CHECKED(uc_reg_write_batch(uc, uregs, tregs, 32));
264}
265
266void ARM_Unicorn::PrepareReschedule() {
267 CHECKED(uc_emu_stop(uc));
268}
269
270void ARM_Unicorn::ClearExclusiveState() {}
271
272void ARM_Unicorn::ClearInstructionCache() {}
273
274void ARM_Unicorn::RecordBreak(GDBStub::BreakpointAddress bkpt) {
275 last_bkpt = bkpt;
276 last_bkpt_hit = true;
277}
278
279void ARM_Unicorn::InterruptHook(uc_engine* uc, u32 int_no, void* user_data) {
280 u32 esr{};
281 CHECKED(uc_reg_read(uc, UC_ARM64_REG_ESR, &esr));
282
283 const auto ec = esr >> 26;
284 const auto iss = esr & 0xFFFFFF;
285
286 auto* const arm_instance = static_cast<ARM_Unicorn*>(user_data);
287
288 switch (ec) {
289 case 0x15: // SVC
290 Kernel::Svc::Call(arm_instance->system, iss);
291 break;
292 }
293}
294
295} // namespace Core
diff --git a/src/core/arm/unicorn/arm_unicorn.h b/src/core/arm/unicorn/arm_unicorn.h
deleted file mode 100644
index 810aff311..000000000
--- a/src/core/arm/unicorn/arm_unicorn.h
+++ /dev/null
@@ -1,63 +0,0 @@
1// Copyright 2018 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <unicorn/unicorn.h>
8#include "common/common_types.h"
9#include "core/arm/arm_interface.h"
10#include "core/gdbstub/gdbstub.h"
11
12namespace Core {
13
14class System;
15
16class ARM_Unicorn final : public ARM_Interface {
17public:
18 enum class Arch {
19 AArch32, // 32-bit ARM
20 AArch64, // 64-bit ARM
21 };
22
23 explicit ARM_Unicorn(System& system, CPUInterrupts& interrupt_handlers, bool uses_wall_clock,
24 Arch architecture, std::size_t core_index);
25 ~ARM_Unicorn() override;
26
27 void SetPC(u64 pc) override;
28 u64 GetPC() const override;
29 u64 GetReg(int index) const override;
30 void SetReg(int index, u64 value) override;
31 u128 GetVectorReg(int index) const override;
32 void SetVectorReg(int index, u128 value) override;
33 u32 GetPSTATE() const override;
34 void SetPSTATE(u32 pstate) override;
35 VAddr GetTlsAddress() const override;
36 void SetTlsAddress(VAddr address) override;
37 void SetTPIDR_EL0(u64 value) override;
38 u64 GetTPIDR_EL0() const override;
39 void ChangeProcessorID(std::size_t new_core_id) override;
40 void PrepareReschedule() override;
41 void ClearExclusiveState() override;
42 void ExecuteInstructions(std::size_t num_instructions);
43 void Run() override;
44 void Step() override;
45 void ClearInstructionCache() override;
46 void PageTableChanged(Common::PageTable&, std::size_t) override {}
47 void RecordBreak(GDBStub::BreakpointAddress bkpt);
48
49 void SaveContext(ThreadContext32& ctx) override {}
50 void SaveContext(ThreadContext64& ctx) override;
51 void LoadContext(const ThreadContext32& ctx) override {}
52 void LoadContext(const ThreadContext64& ctx) override;
53
54private:
55 static void InterruptHook(uc_engine* uc, u32 int_no, void* user_data);
56
57 uc_engine* uc{};
58 GDBStub::BreakpointAddress last_bkpt{};
59 bool last_bkpt_hit = false;
60 std::size_t core_index;
61};
62
63} // namespace Core