summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorGravatar Lioncash2020-11-27 10:50:48 -0500
committerGravatar Lioncash2020-11-27 11:40:45 -0500
commit5bc4eabe36b7ef4dcd5ad8db1e944705655be432 (patch)
tree285ffb3801aa773cb3537f77153a9e432390f6b8 /src
parentMerge pull request #5023 from lioncash/save-global (diff)
downloadyuzu-5bc4eabe36b7ef4dcd5ad8db1e944705655be432.tar.gz
yuzu-5bc4eabe36b7ef4dcd5ad8db1e944705655be432.tar.xz
yuzu-5bc4eabe36b7ef4dcd5ad8db1e944705655be432.zip
core: Eliminate remaining usages of the global system instance
Removes all remaining usages of the global system instance. After this, migration can begin to migrate to being constructed and managed entirely by the various frontends.
Diffstat (limited to 'src')
-rw-r--r--src/core/CMakeLists.txt2
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic_64.cpp11
-rw-r--r--src/core/core.cpp7
-rw-r--r--src/core/cpu_manager.cpp1
-rw-r--r--src/core/gdbstub/gdbstub.cpp1397
-rw-r--r--src/core/gdbstub/gdbstub.h114
-rw-r--r--src/core/loader/deconstructed_rom_directory.cpp3
-rw-r--r--src/core/loader/kip.cpp3
-rw-r--r--src/core/loader/nro.cpp6
-rw-r--r--src/core/loader/nso.cpp5
-rw-r--r--src/core/settings.cpp17
-rw-r--r--src/core/settings.h8
-rw-r--r--src/yuzu/configuration/config.cpp3
-rw-r--r--src/yuzu/configuration/configure_dialog.cpp3
-rw-r--r--src/yuzu/configuration/configure_per_game.cpp2
-rw-r--r--src/yuzu/configuration/configure_profile_manager.cpp2
-rw-r--r--src/yuzu/configuration/configure_system.cpp12
-rw-r--r--src/yuzu/configuration/configure_ui.cpp3
-rw-r--r--src/yuzu/main.cpp36
-rw-r--r--src/yuzu_cmd/yuzu.cpp9
-rw-r--r--src/yuzu_tester/yuzu.cpp7
21 files changed, 58 insertions, 1593 deletions
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index e370fd225..66de33799 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -135,8 +135,6 @@ add_library(core STATIC
135 frontend/framebuffer_layout.cpp 135 frontend/framebuffer_layout.cpp
136 frontend/framebuffer_layout.h 136 frontend/framebuffer_layout.h
137 frontend/input.h 137 frontend/input.h
138 gdbstub/gdbstub.cpp
139 gdbstub/gdbstub.h
140 hardware_interrupt_manager.cpp 138 hardware_interrupt_manager.cpp
141 hardware_interrupt_manager.h 139 hardware_interrupt_manager.h
142 hle/ipc.h 140 hle/ipc.h
diff --git a/src/core/arm/dynarmic/arm_dynarmic_64.cpp b/src/core/arm/dynarmic/arm_dynarmic_64.cpp
index 9f170a224..5c2060d78 100644
--- a/src/core/arm/dynarmic/arm_dynarmic_64.cpp
+++ b/src/core/arm/dynarmic/arm_dynarmic_64.cpp
@@ -14,7 +14,6 @@
14#include "core/arm/dynarmic/arm_exclusive_monitor.h" 14#include "core/arm/dynarmic/arm_exclusive_monitor.h"
15#include "core/core.h" 15#include "core/core.h"
16#include "core/core_timing.h" 16#include "core/core_timing.h"
17#include "core/gdbstub/gdbstub.h"
18#include "core/hardware_properties.h" 17#include "core/hardware_properties.h"
19#include "core/hle/kernel/process.h" 18#include "core/hle/kernel/process.h"
20#include "core/hle/kernel/scheduler.h" 19#include "core/hle/kernel/scheduler.h"
@@ -96,16 +95,6 @@ public:
96 case Dynarmic::A64::Exception::Yield: 95 case Dynarmic::A64::Exception::Yield:
97 return; 96 return;
98 case Dynarmic::A64::Exception::Breakpoint: 97 case Dynarmic::A64::Exception::Breakpoint:
99 if (GDBStub::IsServerEnabled()) {
100 parent.jit->HaltExecution();
101 parent.SetPC(pc);
102 Kernel::Thread* const thread = parent.system.CurrentScheduler().GetCurrentThread();
103 parent.SaveContext(thread->GetContext64());
104 GDBStub::Break();
105 GDBStub::SendTrap(thread, 5);
106 return;
107 }
108 [[fallthrough]];
109 default: 98 default:
110 ASSERT_MSG(false, "ExceptionRaised(exception = {}, pc = {:08X}, code = {:08X})", 99 ASSERT_MSG(false, "ExceptionRaised(exception = {}, pc = {:08X}, code = {:08X})",
111 static_cast<std::size_t>(exception), pc, MemoryReadCode(pc)); 100 static_cast<std::size_t>(exception), pc, MemoryReadCode(pc));
diff --git a/src/core/core.cpp b/src/core/core.cpp
index 7ca3652af..f4bbc9ec3 100644
--- a/src/core/core.cpp
+++ b/src/core/core.cpp
@@ -25,7 +25,6 @@
25#include "core/file_sys/sdmc_factory.h" 25#include "core/file_sys/sdmc_factory.h"
26#include "core/file_sys/vfs_concat.h" 26#include "core/file_sys/vfs_concat.h"
27#include "core/file_sys/vfs_real.h" 27#include "core/file_sys/vfs_real.h"
28#include "core/gdbstub/gdbstub.h"
29#include "core/hardware_interrupt_manager.h" 28#include "core/hardware_interrupt_manager.h"
30#include "core/hle/kernel/client_port.h" 29#include "core/hle/kernel/client_port.h"
31#include "core/hle/kernel/kernel.h" 30#include "core/hle/kernel/kernel.h"
@@ -186,11 +185,8 @@ struct System::Impl {
186 } 185 }
187 186
188 service_manager = std::make_shared<Service::SM::ServiceManager>(kernel); 187 service_manager = std::make_shared<Service::SM::ServiceManager>(kernel);
189
190 services = std::make_unique<Service::Services>(service_manager, system); 188 services = std::make_unique<Service::Services>(service_manager, system);
191 GDBStub::DeferStart(); 189 interrupt_manager = std::make_unique<Hardware::InterruptManager>(system);
192
193 interrupt_manager = std::make_unique<Core::Hardware::InterruptManager>(system);
194 190
195 // Initialize time manager, which must happen after kernel is created 191 // Initialize time manager, which must happen after kernel is created
196 time_manager.Initialize(); 192 time_manager.Initialize();
@@ -297,7 +293,6 @@ struct System::Impl {
297 } 293 }
298 294
299 // Shutdown emulation session 295 // Shutdown emulation session
300 GDBStub::Shutdown();
301 services.reset(); 296 services.reset();
302 service_manager.reset(); 297 service_manager.reset();
303 cheat_engine.reset(); 298 cheat_engine.reset();
diff --git a/src/core/cpu_manager.cpp b/src/core/cpu_manager.cpp
index 100e90d82..eeeb6e8df 100644
--- a/src/core/cpu_manager.cpp
+++ b/src/core/cpu_manager.cpp
@@ -10,7 +10,6 @@
10#include "core/core.h" 10#include "core/core.h"
11#include "core/core_timing.h" 11#include "core/core_timing.h"
12#include "core/cpu_manager.h" 12#include "core/cpu_manager.h"
13#include "core/gdbstub/gdbstub.h"
14#include "core/hle/kernel/kernel.h" 13#include "core/hle/kernel/kernel.h"
15#include "core/hle/kernel/physical_core.h" 14#include "core/hle/kernel/physical_core.h"
16#include "core/hle/kernel/scheduler.h" 15#include "core/hle/kernel/scheduler.h"
diff --git a/src/core/gdbstub/gdbstub.cpp b/src/core/gdbstub/gdbstub.cpp
deleted file mode 100644
index 97ee65464..000000000
--- a/src/core/gdbstub/gdbstub.cpp
+++ /dev/null
@@ -1,1397 +0,0 @@
1// Copyright 2013 Dolphin Emulator Project
2// Licensed under GPLv2+
3// Refer to the license.txt file included.
4
5// Originally written by Sven Peter <sven@fail0verflow.com> for anergistic.
6
7#include <algorithm>
8#include <atomic>
9#include <climits>
10#include <csignal>
11#include <cstdarg>
12#include <cstdio>
13#include <cstring>
14#include <map>
15#include <numeric>
16#include <fcntl.h>
17
18#ifdef _WIN32
19#include <winsock2.h>
20// winsock2.h needs to be included first to prevent winsock.h being included by other includes
21#include <io.h>
22#include <iphlpapi.h>
23#include <ws2tcpip.h>
24#define SHUT_RDWR 2
25#else
26#include <netinet/in.h>
27#include <sys/select.h>
28#include <sys/socket.h>
29#include <sys/un.h>
30#include <unistd.h>
31#endif
32
33#include "common/logging/log.h"
34#include "common/string_util.h"
35#include "common/swap.h"
36#include "core/arm/arm_interface.h"
37#include "core/core.h"
38#include "core/gdbstub/gdbstub.h"
39#include "core/hle/kernel/memory/page_table.h"
40#include "core/hle/kernel/process.h"
41#include "core/hle/kernel/scheduler.h"
42#include "core/loader/loader.h"
43#include "core/memory.h"
44
45namespace GDBStub {
46namespace {
47constexpr int GDB_BUFFER_SIZE = 10000;
48
49constexpr char GDB_STUB_START = '$';
50constexpr char GDB_STUB_END = '#';
51constexpr char GDB_STUB_ACK = '+';
52constexpr char GDB_STUB_NACK = '-';
53
54#ifndef SIGTRAP
55constexpr u32 SIGTRAP = 5;
56#endif
57
58#ifndef SIGTERM
59constexpr u32 SIGTERM = 15;
60#endif
61
62#ifndef MSG_WAITALL
63constexpr u32 MSG_WAITALL = 8;
64#endif
65
66constexpr u32 LR_REGISTER = 30;
67constexpr u32 SP_REGISTER = 31;
68constexpr u32 PC_REGISTER = 32;
69constexpr u32 PSTATE_REGISTER = 33;
70constexpr u32 UC_ARM64_REG_Q0 = 34;
71constexpr u32 FPCR_REGISTER = 66;
72
73// For sample XML files see the GDB source /gdb/features
74// GDB also wants the l character at the start
75// This XML defines what the registers are for this specific ARM device
76constexpr char target_xml[] =
77 R"(l<?xml version="1.0"?>
78<!DOCTYPE target SYSTEM "gdb-target.dtd">
79<target version="1.0">
80 <feature name="org.gnu.gdb.aarch64.core">
81 <reg name="x0" bitsize="64"/>
82 <reg name="x1" bitsize="64"/>
83 <reg name="x2" bitsize="64"/>
84 <reg name="x3" bitsize="64"/>
85 <reg name="x4" bitsize="64"/>
86 <reg name="x5" bitsize="64"/>
87 <reg name="x6" bitsize="64"/>
88 <reg name="x7" bitsize="64"/>
89 <reg name="x8" bitsize="64"/>
90 <reg name="x9" bitsize="64"/>
91 <reg name="x10" bitsize="64"/>
92 <reg name="x11" bitsize="64"/>
93 <reg name="x12" bitsize="64"/>
94 <reg name="x13" bitsize="64"/>
95 <reg name="x14" bitsize="64"/>
96 <reg name="x15" bitsize="64"/>
97 <reg name="x16" bitsize="64"/>
98 <reg name="x17" bitsize="64"/>
99 <reg name="x18" bitsize="64"/>
100 <reg name="x19" bitsize="64"/>
101 <reg name="x20" bitsize="64"/>
102 <reg name="x21" bitsize="64"/>
103 <reg name="x22" bitsize="64"/>
104 <reg name="x23" bitsize="64"/>
105 <reg name="x24" bitsize="64"/>
106 <reg name="x25" bitsize="64"/>
107 <reg name="x26" bitsize="64"/>
108 <reg name="x27" bitsize="64"/>
109 <reg name="x28" bitsize="64"/>
110 <reg name="x29" bitsize="64"/>
111 <reg name="x30" bitsize="64"/>
112 <reg name="sp" bitsize="64" type="data_ptr"/>
113
114 <reg name="pc" bitsize="64" type="code_ptr"/>
115
116 <flags id="pstate_flags" size="4">
117 <field name="SP" start="0" end="0"/>
118 <field name="" start="1" end="1"/>
119 <field name="EL" start="2" end="3"/>
120 <field name="nRW" start="4" end="4"/>
121 <field name="" start="5" end="5"/>
122 <field name="F" start="6" end="6"/>
123 <field name="I" start="7" end="7"/>
124 <field name="A" start="8" end="8"/>
125 <field name="D" start="9" end="9"/>
126
127 <field name="IL" start="20" end="20"/>
128 <field name="SS" start="21" end="21"/>
129
130 <field name="V" start="28" end="28"/>
131 <field name="C" start="29" end="29"/>
132 <field name="Z" start="30" end="30"/>
133 <field name="N" start="31" end="31"/>
134 </flags>
135 <reg name="pstate" bitsize="32" type="pstate_flags"/>
136 </feature>
137 <feature name="org.gnu.gdb.aarch64.fpu">
138 </feature>
139</target>
140)";
141
142int gdbserver_socket = -1;
143bool defer_start = false;
144
145u8 command_buffer[GDB_BUFFER_SIZE];
146u32 command_length;
147
148u32 latest_signal = 0;
149bool memory_break = false;
150
151Kernel::Thread* current_thread = nullptr;
152u32 current_core = 0;
153
154// Binding to a port within the reserved ports range (0-1023) requires root permissions,
155// so default to a port outside of that range.
156u16 gdbstub_port = 24689;
157
158bool halt_loop = true;
159bool step_loop = false;
160bool send_trap = false;
161
162// If set to false, the server will never be started and no
163// gdbstub-related functions will be executed.
164std::atomic<bool> server_enabled(false);
165
166#ifdef _WIN32
167WSADATA InitData;
168#endif
169
170struct Breakpoint {
171 bool active;
172 VAddr addr;
173 u64 len;
174 std::array<u8, 4> inst;
175};
176
177using BreakpointMap = std::map<VAddr, Breakpoint>;
178BreakpointMap breakpoints_execute;
179BreakpointMap breakpoints_read;
180BreakpointMap breakpoints_write;
181
182struct Module {
183 std::string name;
184 VAddr beg;
185 VAddr end;
186};
187
188std::vector<Module> modules;
189} // Anonymous namespace
190
191void RegisterModule(std::string name, VAddr beg, VAddr end, bool add_elf_ext) {
192 Module module;
193 if (add_elf_ext) {
194 Common::SplitPath(name, nullptr, &module.name, nullptr);
195 module.name += ".elf";
196 } else {
197 module.name = std::move(name);
198 }
199 module.beg = beg;
200 module.end = end;
201 modules.push_back(std::move(module));
202}
203
204static Kernel::Thread* FindThreadById(s64 id) {
205 const auto& threads = Core::System::GetInstance().GlobalScheduler().GetThreadList();
206 for (auto& thread : threads) {
207 if (thread->GetThreadID() == static_cast<u64>(id)) {
208 current_core = thread->GetProcessorID();
209 return thread.get();
210 }
211 }
212 return nullptr;
213}
214
215static u64 RegRead(std::size_t id, Kernel::Thread* thread = nullptr) {
216 if (!thread) {
217 return 0;
218 }
219
220 const auto& thread_context = thread->GetContext64();
221
222 if (id < SP_REGISTER) {
223 return thread_context.cpu_registers[id];
224 } else if (id == SP_REGISTER) {
225 return thread_context.sp;
226 } else if (id == PC_REGISTER) {
227 return thread_context.pc;
228 } else if (id == PSTATE_REGISTER) {
229 return thread_context.pstate;
230 } else if (id > PSTATE_REGISTER && id < FPCR_REGISTER) {
231 return thread_context.vector_registers[id - UC_ARM64_REG_Q0][0];
232 } else {
233 return 0;
234 }
235}
236
237static void RegWrite(std::size_t id, u64 val, Kernel::Thread* thread = nullptr) {
238 if (!thread) {
239 return;
240 }
241
242 auto& thread_context = thread->GetContext64();
243
244 if (id < SP_REGISTER) {
245 thread_context.cpu_registers[id] = val;
246 } else if (id == SP_REGISTER) {
247 thread_context.sp = val;
248 } else if (id == PC_REGISTER) {
249 thread_context.pc = val;
250 } else if (id == PSTATE_REGISTER) {
251 thread_context.pstate = static_cast<u32>(val);
252 } else if (id > PSTATE_REGISTER && id < FPCR_REGISTER) {
253 thread_context.vector_registers[id - (PSTATE_REGISTER + 1)][0] = val;
254 }
255}
256
257static u128 FpuRead(std::size_t id, Kernel::Thread* thread = nullptr) {
258 if (!thread) {
259 return u128{0};
260 }
261
262 auto& thread_context = thread->GetContext64();
263
264 if (id >= UC_ARM64_REG_Q0 && id < FPCR_REGISTER) {
265 return thread_context.vector_registers[id - UC_ARM64_REG_Q0];
266 } else if (id == FPCR_REGISTER) {
267 return u128{thread_context.fpcr, 0};
268 } else {
269 return u128{0};
270 }
271}
272
273static void FpuWrite(std::size_t id, u128 val, Kernel::Thread* thread = nullptr) {
274 if (!thread) {
275 return;
276 }
277
278 auto& thread_context = thread->GetContext64();
279
280 if (id >= UC_ARM64_REG_Q0 && id < FPCR_REGISTER) {
281 thread_context.vector_registers[id - UC_ARM64_REG_Q0] = val;
282 } else if (id == FPCR_REGISTER) {
283 thread_context.fpcr = static_cast<u32>(val[0]);
284 }
285}
286
287/**
288 * Turns hex string character into the equivalent byte.
289 *
290 * @param hex Input hex character to be turned into byte.
291 */
292static u8 HexCharToValue(u8 hex) {
293 if (hex >= '0' && hex <= '9') {
294 return static_cast<u8>(hex - '0');
295 } else if (hex >= 'a' && hex <= 'f') {
296 return static_cast<u8>(hex - 'a' + 0xA);
297 } else if (hex >= 'A' && hex <= 'F') {
298 return static_cast<u8>(hex - 'A' + 0xA);
299 }
300
301 LOG_ERROR(Debug_GDBStub, "Invalid nibble: {} ({:02X})", hex, hex);
302 return 0;
303}
304
305/**
306 * Turn nibble of byte into hex string character.
307 *
308 * @param n Nibble to be turned into hex character.
309 */
310static u8 NibbleToHex(u8 n) {
311 n &= 0xF;
312 if (n < 0xA) {
313 return static_cast<u8>('0' + n);
314 } else {
315 return static_cast<u8>('a' + n - 0xA);
316 }
317}
318
319/**
320 * Converts input hex string characters into an array of equivalent of u8 bytes.
321 *
322 * @param src Pointer to array of output hex string characters.
323 * @param len Length of src array.
324 */
325static u32 HexToInt(const u8* src, std::size_t len) {
326 u32 output = 0;
327 while (len-- > 0) {
328 output = (output << 4) | HexCharToValue(src[0]);
329 src++;
330 }
331 return output;
332}
333
334/**
335 * Converts input hex string characters into an array of equivalent of u8 bytes.
336 *
337 * @param src Pointer to array of output hex string characters.
338 * @param len Length of src array.
339 */
340static u64 HexToLong(const u8* src, std::size_t len) {
341 u64 output = 0;
342 while (len-- > 0) {
343 output = (output << 4) | HexCharToValue(src[0]);
344 src++;
345 }
346 return output;
347}
348
349/**
350 * Converts input array of u8 bytes into their equivalent hex string characters.
351 *
352 * @param dest Pointer to buffer to store output hex string characters.
353 * @param src Pointer to array of u8 bytes.
354 * @param len Length of src array.
355 */
356static void MemToGdbHex(u8* dest, const u8* src, std::size_t len) {
357 while (len-- > 0) {
358 const u8 tmp = *src++;
359 *dest++ = NibbleToHex(static_cast<u8>(tmp >> 4));
360 *dest++ = NibbleToHex(tmp);
361 }
362}
363
364/**
365 * Converts input gdb-formatted hex string characters into an array of equivalent of u8 bytes.
366 *
367 * @param dest Pointer to buffer to store u8 bytes.
368 * @param src Pointer to array of output hex string characters.
369 * @param len Length of src array.
370 */
371static void GdbHexToMem(u8* dest, const u8* src, std::size_t len) {
372 while (len-- > 0) {
373 *dest++ = static_cast<u8>((HexCharToValue(src[0]) << 4) | HexCharToValue(src[1]));
374 src += 2;
375 }
376}
377
378/**
379 * Convert a u32 into a gdb-formatted hex string.
380 *
381 * @param dest Pointer to buffer to store output hex string characters.
382 * @param v Value to convert.
383 */
384static void IntToGdbHex(u8* dest, u32 v) {
385 for (int i = 0; i < 8; i += 2) {
386 dest[i + 1] = NibbleToHex(static_cast<u8>(v >> (4 * i)));
387 dest[i] = NibbleToHex(static_cast<u8>(v >> (4 * (i + 1))));
388 }
389}
390
391/**
392 * Convert a u64 into a gdb-formatted hex string.
393 *
394 * @param dest Pointer to buffer to store output hex string characters.
395 * @param v Value to convert.
396 */
397static void LongToGdbHex(u8* dest, u64 v) {
398 for (int i = 0; i < 16; i += 2) {
399 dest[i + 1] = NibbleToHex(static_cast<u8>(v >> (4 * i)));
400 dest[i] = NibbleToHex(static_cast<u8>(v >> (4 * (i + 1))));
401 }
402}
403
404/**
405 * Convert a gdb-formatted hex string into a u32.
406 *
407 * @param src Pointer to hex string.
408 */
409static u32 GdbHexToInt(const u8* src) {
410 u32 output = 0;
411
412 for (int i = 0; i < 8; i += 2) {
413 output = (output << 4) | HexCharToValue(src[7 - i - 1]);
414 output = (output << 4) | HexCharToValue(src[7 - i]);
415 }
416
417 return output;
418}
419
420/**
421 * Convert a gdb-formatted hex string into a u64.
422 *
423 * @param src Pointer to hex string.
424 */
425static u64 GdbHexToLong(const u8* src) {
426 u64 output = 0;
427
428 for (int i = 0; i < 16; i += 2) {
429 output = (output << 4) | HexCharToValue(src[15 - i - 1]);
430 output = (output << 4) | HexCharToValue(src[15 - i]);
431 }
432
433 return output;
434}
435
436/**
437 * Convert a gdb-formatted hex string into a u128.
438 *
439 * @param src Pointer to hex string.
440 */
441static u128 GdbHexToU128(const u8* src) {
442 u128 output;
443
444 for (int i = 0; i < 16; i += 2) {
445 output[0] = (output[0] << 4) | HexCharToValue(src[15 - i - 1]);
446 output[0] = (output[0] << 4) | HexCharToValue(src[15 - i]);
447 }
448
449 for (int i = 0; i < 16; i += 2) {
450 output[1] = (output[1] << 4) | HexCharToValue(src[16 + 15 - i - 1]);
451 output[1] = (output[1] << 4) | HexCharToValue(src[16 + 15 - i]);
452 }
453
454 return output;
455}
456
457/// Read a byte from the gdb client.
458static u8 ReadByte() {
459 u8 c;
460 std::size_t received_size = recv(gdbserver_socket, reinterpret_cast<char*>(&c), 1, MSG_WAITALL);
461 if (received_size != 1) {
462 LOG_ERROR(Debug_GDBStub, "recv failed: {}", received_size);
463 Shutdown();
464 }
465
466 return c;
467}
468
469/// Calculate the checksum of the current command buffer.
470static u8 CalculateChecksum(const u8* buffer, std::size_t length) {
471 return static_cast<u8>(std::accumulate(buffer, buffer + length, u8{0},
472 [](u8 lhs, u8 rhs) { return u8(lhs + rhs); }));
473}
474
475/**
476 * Get the map of breakpoints for a given breakpoint type.
477 *
478 * @param type Type of breakpoint map.
479 */
480static BreakpointMap& GetBreakpointMap(BreakpointType type) {
481 switch (type) {
482 case BreakpointType::Execute:
483 return breakpoints_execute;
484 case BreakpointType::Read:
485 return breakpoints_read;
486 case BreakpointType::Write:
487 return breakpoints_write;
488 default:
489 return breakpoints_read;
490 }
491}
492
493/**
494 * Remove the breakpoint from the given address of the specified type.
495 *
496 * @param type Type of breakpoint.
497 * @param addr Address of breakpoint.
498 */
499static void RemoveBreakpoint(BreakpointType type, VAddr addr) {
500 BreakpointMap& p = GetBreakpointMap(type);
501
502 const auto bp = p.find(addr);
503 if (bp == p.end()) {
504 return;
505 }
506
507 LOG_DEBUG(Debug_GDBStub, "gdb: removed a breakpoint: {:016X} bytes at {:016X} of type {}",
508 bp->second.len, bp->second.addr, static_cast<int>(type));
509
510 if (type == BreakpointType::Execute) {
511 auto& system = Core::System::GetInstance();
512 system.Memory().WriteBlock(bp->second.addr, bp->second.inst.data(), bp->second.inst.size());
513 system.InvalidateCpuInstructionCaches();
514 }
515 p.erase(addr);
516}
517
518BreakpointAddress GetNextBreakpointFromAddress(VAddr addr, BreakpointType type) {
519 const BreakpointMap& p = GetBreakpointMap(type);
520 const auto next_breakpoint = p.lower_bound(addr);
521 BreakpointAddress breakpoint;
522
523 if (next_breakpoint != p.end()) {
524 breakpoint.address = next_breakpoint->first;
525 breakpoint.type = type;
526 } else {
527 breakpoint.address = 0;
528 breakpoint.type = BreakpointType::None;
529 }
530
531 return breakpoint;
532}
533
534bool CheckBreakpoint(VAddr addr, BreakpointType type) {
535 if (!IsConnected()) {
536 return false;
537 }
538
539 const BreakpointMap& p = GetBreakpointMap(type);
540 const auto bp = p.find(addr);
541
542 if (bp == p.end()) {
543 return false;
544 }
545
546 u64 len = bp->second.len;
547
548 // IDA Pro defaults to 4-byte breakpoints for all non-hardware breakpoints
549 // no matter if it's a 4-byte or 2-byte instruction. When you execute a
550 // Thumb instruction with a 4-byte breakpoint set, it will set a breakpoint on
551 // two instructions instead of the single instruction you placed the breakpoint
552 // on. So, as a way to make sure that execution breakpoints are only breaking
553 // on the instruction that was specified, set the length of an execution
554 // breakpoint to 1. This should be fine since the CPU should never begin executing
555 // an instruction anywhere except the beginning of the instruction.
556 if (type == BreakpointType::Execute) {
557 len = 1;
558 }
559
560 if (bp->second.active && (addr >= bp->second.addr && addr < bp->second.addr + len)) {
561 LOG_DEBUG(Debug_GDBStub,
562 "Found breakpoint type {} @ {:016X}, range: {:016X}"
563 " - {:016X} ({:X} bytes)",
564 static_cast<int>(type), addr, bp->second.addr, bp->second.addr + len, len);
565 return true;
566 }
567
568 return false;
569}
570
571/**
572 * Send packet to gdb client.
573 *
574 * @param packet Packet to be sent to client.
575 */
576static void SendPacket(const char packet) {
577 std::size_t sent_size = send(gdbserver_socket, &packet, 1, 0);
578 if (sent_size != 1) {
579 LOG_ERROR(Debug_GDBStub, "send failed");
580 }
581}
582
583/**
584 * Send reply to gdb client.
585 *
586 * @param reply Reply to be sent to client.
587 */
588static void SendReply(const char* reply) {
589 if (!IsConnected()) {
590 return;
591 }
592
593 LOG_DEBUG(Debug_GDBStub, "Reply: {}", reply);
594
595 memset(command_buffer, 0, sizeof(command_buffer));
596
597 command_length = static_cast<u32>(strlen(reply));
598 if (command_length + 4 > sizeof(command_buffer)) {
599 LOG_ERROR(Debug_GDBStub, "command_buffer overflow in SendReply");
600 return;
601 }
602
603 memcpy(command_buffer + 1, reply, command_length);
604
605 const u8 checksum = CalculateChecksum(command_buffer, command_length + 1);
606 command_buffer[0] = GDB_STUB_START;
607 command_buffer[command_length + 1] = GDB_STUB_END;
608 command_buffer[command_length + 2] = NibbleToHex(static_cast<u8>(checksum >> 4));
609 command_buffer[command_length + 3] = NibbleToHex(checksum);
610
611 u8* ptr = command_buffer;
612 u32 left = command_length + 4;
613 while (left > 0) {
614 const auto sent_size = send(gdbserver_socket, reinterpret_cast<char*>(ptr), left, 0);
615 if (sent_size < 0) {
616 LOG_ERROR(Debug_GDBStub, "gdb: send failed");
617 return Shutdown();
618 }
619
620 left -= static_cast<u32>(sent_size);
621 ptr += sent_size;
622 }
623}
624
625/// Handle query command from gdb client.
626static void HandleQuery() {
627 LOG_DEBUG(Debug_GDBStub, "gdb: query '{}'", command_buffer + 1);
628
629 const char* query = reinterpret_cast<const char*>(command_buffer + 1);
630
631 if (strcmp(query, "TStatus") == 0) {
632 SendReply("T0");
633 } else if (strncmp(query, "Supported", strlen("Supported")) == 0) {
634 // PacketSize needs to be large enough for target xml
635 std::string buffer = "PacketSize=2000;qXfer:features:read+;qXfer:threads:read+";
636 if (!modules.empty()) {
637 buffer += ";qXfer:libraries:read+";
638 }
639 SendReply(buffer.c_str());
640 } else if (strncmp(query, "Xfer:features:read:target.xml:",
641 strlen("Xfer:features:read:target.xml:")) == 0) {
642 SendReply(target_xml);
643 } else if (strncmp(query, "Offsets", strlen("Offsets")) == 0) {
644 const VAddr base_address =
645 Core::System::GetInstance().CurrentProcess()->PageTable().GetCodeRegionStart();
646 std::string buffer = fmt::format("TextSeg={:0x}", base_address);
647 SendReply(buffer.c_str());
648 } else if (strncmp(query, "fThreadInfo", strlen("fThreadInfo")) == 0) {
649 std::string val = "m";
650 const auto& threads = Core::System::GetInstance().GlobalScheduler().GetThreadList();
651 for (const auto& thread : threads) {
652 val += fmt::format("{:x},", thread->GetThreadID());
653 }
654 val.pop_back();
655 SendReply(val.c_str());
656 } else if (strncmp(query, "sThreadInfo", strlen("sThreadInfo")) == 0) {
657 SendReply("l");
658 } else if (strncmp(query, "Xfer:threads:read", strlen("Xfer:threads:read")) == 0) {
659 std::string buffer;
660 buffer += "l<?xml version=\"1.0\"?>";
661 buffer += "<threads>";
662 const auto& threads = Core::System::GetInstance().GlobalScheduler().GetThreadList();
663 for (const auto& thread : threads) {
664 buffer +=
665 fmt::format(R"*(<thread id="{:x}" core="{:d}" name="Thread {:x}"></thread>)*",
666 thread->GetThreadID(), thread->GetProcessorID(), thread->GetThreadID());
667 }
668 buffer += "</threads>";
669 SendReply(buffer.c_str());
670 } else if (strncmp(query, "Xfer:libraries:read", strlen("Xfer:libraries:read")) == 0) {
671 std::string buffer;
672 buffer += "l<?xml version=\"1.0\"?>";
673 buffer += "<library-list>";
674 for (const auto& module : modules) {
675 buffer +=
676 fmt::format(R"*("<library name = "{}"><segment address = "0x{:x}"/></library>)*",
677 module.name, module.beg);
678 }
679 buffer += "</library-list>";
680 SendReply(buffer.c_str());
681 } else {
682 SendReply("");
683 }
684}
685
686/// Handle set thread command from gdb client.
687static void HandleSetThread() {
688 int thread_id = -1;
689 if (command_buffer[2] != '-') {
690 thread_id = static_cast<int>(HexToInt(command_buffer + 2, command_length - 2));
691 }
692 if (thread_id >= 1) {
693 current_thread = FindThreadById(thread_id);
694 }
695 if (!current_thread) {
696 thread_id = 1;
697 current_thread = FindThreadById(thread_id);
698 }
699 if (current_thread) {
700 SendReply("OK");
701 return;
702 }
703 SendReply("E01");
704}
705
706/// Handle thread alive command from gdb client.
707static void HandleThreadAlive() {
708 int thread_id = static_cast<int>(HexToInt(command_buffer + 1, command_length - 1));
709 if (thread_id == 0) {
710 thread_id = 1;
711 }
712 if (FindThreadById(thread_id)) {
713 SendReply("OK");
714 return;
715 }
716 SendReply("E01");
717}
718
719/**
720 * Send signal packet to client.
721 *
722 * @param signal Signal to be sent to client.
723 */
724static void SendSignal(Kernel::Thread* thread, u32 signal, bool full = true) {
725 if (gdbserver_socket == -1) {
726 return;
727 }
728
729 latest_signal = signal;
730
731 if (!thread) {
732 full = false;
733 }
734
735 std::string buffer;
736 if (full) {
737 buffer = fmt::format("T{:02x}{:02x}:{:016x};{:02x}:{:016x};{:02x}:{:016x}", latest_signal,
738 PC_REGISTER, Common::swap64(RegRead(PC_REGISTER, thread)), SP_REGISTER,
739 Common::swap64(RegRead(SP_REGISTER, thread)), LR_REGISTER,
740 Common::swap64(RegRead(LR_REGISTER, thread)));
741 } else {
742 buffer = fmt::format("T{:02x}", latest_signal);
743 }
744
745 if (thread) {
746 buffer += fmt::format(";thread:{:x};", thread->GetThreadID());
747 }
748
749 SendReply(buffer.c_str());
750}
751
752/// Read command from gdb client.
753static void ReadCommand() {
754 command_length = 0;
755 memset(command_buffer, 0, sizeof(command_buffer));
756
757 u8 c = ReadByte();
758 if (c == '+') {
759 // ignore ack
760 return;
761 } else if (c == 0x03) {
762 LOG_INFO(Debug_GDBStub, "gdb: found break command");
763 halt_loop = true;
764 SendSignal(current_thread, SIGTRAP);
765 return;
766 } else if (c != GDB_STUB_START) {
767 LOG_DEBUG(Debug_GDBStub, "gdb: read invalid byte {:02X}", c);
768 return;
769 }
770
771 while ((c = ReadByte()) != GDB_STUB_END) {
772 if (command_length >= sizeof(command_buffer)) {
773 LOG_ERROR(Debug_GDBStub, "gdb: command_buffer overflow");
774 SendPacket(GDB_STUB_NACK);
775 return;
776 }
777 command_buffer[command_length++] = c;
778 }
779
780 auto checksum_received = static_cast<u32>(HexCharToValue(ReadByte()) << 4);
781 checksum_received |= static_cast<u32>(HexCharToValue(ReadByte()));
782
783 const u32 checksum_calculated = CalculateChecksum(command_buffer, command_length);
784
785 if (checksum_received != checksum_calculated) {
786 LOG_ERROR(Debug_GDBStub,
787 "gdb: invalid checksum: calculated {:02X} and read {:02X} for ${}# (length: {})",
788 checksum_calculated, checksum_received, command_buffer, command_length);
789
790 command_length = 0;
791
792 SendPacket(GDB_STUB_NACK);
793 return;
794 }
795
796 SendPacket(GDB_STUB_ACK);
797}
798
799/// Check if there is data to be read from the gdb client.
800static bool IsDataAvailable() {
801 if (!IsConnected()) {
802 return false;
803 }
804
805 fd_set fd_socket;
806
807 FD_ZERO(&fd_socket);
808 FD_SET(static_cast<u32>(gdbserver_socket), &fd_socket);
809
810 struct timeval t;
811 t.tv_sec = 0;
812 t.tv_usec = 0;
813
814 if (select(gdbserver_socket + 1, &fd_socket, nullptr, nullptr, &t) < 0) {
815 LOG_ERROR(Debug_GDBStub, "select failed");
816 return false;
817 }
818
819 return FD_ISSET(gdbserver_socket, &fd_socket) != 0;
820}
821
822/// Send requested register to gdb client.
823static void ReadRegister() {
824 static u8 reply[64];
825 memset(reply, 0, sizeof(reply));
826
827 u32 id = HexCharToValue(command_buffer[1]);
828 if (command_buffer[2] != '\0') {
829 id <<= 4;
830 id |= HexCharToValue(command_buffer[2]);
831 }
832
833 if (id <= SP_REGISTER) {
834 LongToGdbHex(reply, RegRead(id, current_thread));
835 } else if (id == PC_REGISTER) {
836 LongToGdbHex(reply, RegRead(id, current_thread));
837 } else if (id == PSTATE_REGISTER) {
838 IntToGdbHex(reply, static_cast<u32>(RegRead(id, current_thread)));
839 } else if (id >= UC_ARM64_REG_Q0 && id < FPCR_REGISTER) {
840 u128 r = FpuRead(id, current_thread);
841 LongToGdbHex(reply, r[0]);
842 LongToGdbHex(reply + 16, r[1]);
843 } else if (id == FPCR_REGISTER) {
844 u128 r = FpuRead(id, current_thread);
845 IntToGdbHex(reply, static_cast<u32>(r[0]));
846 } else if (id == FPCR_REGISTER + 1) {
847 u128 r = FpuRead(id, current_thread);
848 IntToGdbHex(reply, static_cast<u32>(r[0] >> 32));
849 }
850
851 SendReply(reinterpret_cast<char*>(reply));
852}
853
854/// Send all registers to the gdb client.
855static void ReadRegisters() {
856 static u8 buffer[GDB_BUFFER_SIZE - 4];
857 memset(buffer, 0, sizeof(buffer));
858
859 u8* bufptr = buffer;
860
861 for (u32 reg = 0; reg <= SP_REGISTER; reg++) {
862 LongToGdbHex(bufptr + reg * 16, RegRead(reg, current_thread));
863 }
864
865 bufptr += 32 * 16;
866
867 LongToGdbHex(bufptr, RegRead(PC_REGISTER, current_thread));
868
869 bufptr += 16;
870
871 IntToGdbHex(bufptr, static_cast<u32>(RegRead(PSTATE_REGISTER, current_thread)));
872
873 bufptr += 8;
874
875 u128 r;
876
877 for (u32 reg = UC_ARM64_REG_Q0; reg < FPCR_REGISTER; reg++) {
878 r = FpuRead(reg, current_thread);
879 LongToGdbHex(bufptr + reg * 32, r[0]);
880 LongToGdbHex(bufptr + reg * 32 + 16, r[1]);
881 }
882
883 bufptr += 32 * 32;
884
885 r = FpuRead(FPCR_REGISTER, current_thread);
886 IntToGdbHex(bufptr, static_cast<u32>(r[0]));
887
888 bufptr += 8;
889
890 SendReply(reinterpret_cast<char*>(buffer));
891}
892
893/// Modify data of register specified by gdb client.
894static void WriteRegister() {
895 const u8* buffer_ptr = command_buffer + 3;
896
897 u32 id = HexCharToValue(command_buffer[1]);
898 if (command_buffer[2] != '=') {
899 ++buffer_ptr;
900 id <<= 4;
901 id |= HexCharToValue(command_buffer[2]);
902 }
903
904 if (id <= SP_REGISTER) {
905 RegWrite(id, GdbHexToLong(buffer_ptr), current_thread);
906 } else if (id == PC_REGISTER) {
907 RegWrite(id, GdbHexToLong(buffer_ptr), current_thread);
908 } else if (id == PSTATE_REGISTER) {
909 RegWrite(id, GdbHexToInt(buffer_ptr), current_thread);
910 } else if (id >= UC_ARM64_REG_Q0 && id < FPCR_REGISTER) {
911 FpuWrite(id, GdbHexToU128(buffer_ptr), current_thread);
912 } else if (id == FPCR_REGISTER) {
913 } else if (id == FPCR_REGISTER + 1) {
914 }
915
916 // Update ARM context, skipping scheduler - no running threads at this point
917 Core::System::GetInstance()
918 .ArmInterface(current_core)
919 .LoadContext(current_thread->GetContext64());
920
921 SendReply("OK");
922}
923
924/// Modify all registers with data received from the client.
925static void WriteRegisters() {
926 const u8* buffer_ptr = command_buffer + 1;
927
928 if (command_buffer[0] != 'G')
929 return SendReply("E01");
930
931 for (u32 i = 0, reg = 0; reg <= FPCR_REGISTER; i++, reg++) {
932 if (reg <= SP_REGISTER) {
933 RegWrite(reg, GdbHexToLong(buffer_ptr + i * 16), current_thread);
934 } else if (reg == PC_REGISTER) {
935 RegWrite(PC_REGISTER, GdbHexToLong(buffer_ptr + i * 16), current_thread);
936 } else if (reg == PSTATE_REGISTER) {
937 RegWrite(PSTATE_REGISTER, GdbHexToInt(buffer_ptr + i * 16), current_thread);
938 } else if (reg >= UC_ARM64_REG_Q0 && reg < FPCR_REGISTER) {
939 RegWrite(reg, GdbHexToLong(buffer_ptr + i * 16), current_thread);
940 } else if (reg == FPCR_REGISTER) {
941 RegWrite(FPCR_REGISTER, GdbHexToLong(buffer_ptr + i * 16), current_thread);
942 } else if (reg == FPCR_REGISTER + 1) {
943 RegWrite(FPCR_REGISTER, GdbHexToLong(buffer_ptr + i * 16), current_thread);
944 }
945 }
946
947 // Update ARM context, skipping scheduler - no running threads at this point
948 Core::System::GetInstance()
949 .ArmInterface(current_core)
950 .LoadContext(current_thread->GetContext64());
951
952 SendReply("OK");
953}
954
955/// Read location in memory specified by gdb client.
956static void ReadMemory() {
957 static u8 reply[GDB_BUFFER_SIZE - 4];
958
959 auto start_offset = command_buffer + 1;
960 const auto addr_pos = std::find(start_offset, command_buffer + command_length, ',');
961 const VAddr addr = HexToLong(start_offset, static_cast<u64>(addr_pos - start_offset));
962
963 start_offset = addr_pos + 1;
964 const u64 len =
965 HexToLong(start_offset, static_cast<u64>((command_buffer + command_length) - start_offset));
966
967 LOG_DEBUG(Debug_GDBStub, "gdb: addr: {:016X} len: {:016X}", addr, len);
968
969 if (len * 2 > sizeof(reply)) {
970 SendReply("E01");
971 }
972
973 auto& memory = Core::System::GetInstance().Memory();
974 if (!memory.IsValidVirtualAddress(addr)) {
975 return SendReply("E00");
976 }
977
978 std::vector<u8> data(len);
979 memory.ReadBlock(addr, data.data(), len);
980
981 MemToGdbHex(reply, data.data(), len);
982 reply[len * 2] = '\0';
983 SendReply(reinterpret_cast<char*>(reply));
984}
985
986/// Modify location in memory with data received from the gdb client.
987static void WriteMemory() {
988 auto start_offset = command_buffer + 1;
989 const auto addr_pos = std::find(start_offset, command_buffer + command_length, ',');
990 const VAddr addr = HexToLong(start_offset, static_cast<u64>(addr_pos - start_offset));
991
992 start_offset = addr_pos + 1;
993 const auto len_pos = std::find(start_offset, command_buffer + command_length, ':');
994 const u64 len = HexToLong(start_offset, static_cast<u64>(len_pos - start_offset));
995
996 auto& system = Core::System::GetInstance();
997 auto& memory = system.Memory();
998 if (!memory.IsValidVirtualAddress(addr)) {
999 return SendReply("E00");
1000 }
1001
1002 std::vector<u8> data(len);
1003 GdbHexToMem(data.data(), len_pos + 1, len);
1004 memory.WriteBlock(addr, data.data(), len);
1005 system.InvalidateCpuInstructionCaches();
1006 SendReply("OK");
1007}
1008
1009void Break(bool is_memory_break) {
1010 send_trap = true;
1011
1012 memory_break = is_memory_break;
1013}
1014
1015/// Tell the CPU that it should perform a single step.
1016static void Step() {
1017 if (command_length > 1) {
1018 RegWrite(PC_REGISTER, GdbHexToLong(command_buffer + 1), current_thread);
1019 // Update ARM context, skipping scheduler - no running threads at this point
1020 Core::System::GetInstance()
1021 .ArmInterface(current_core)
1022 .LoadContext(current_thread->GetContext64());
1023 }
1024 step_loop = true;
1025 halt_loop = true;
1026 send_trap = true;
1027 Core::System::GetInstance().InvalidateCpuInstructionCaches();
1028}
1029
1030/// Tell the CPU if we hit a memory breakpoint.
1031bool IsMemoryBreak() {
1032 if (!IsConnected()) {
1033 return false;
1034 }
1035
1036 return memory_break;
1037}
1038
1039/// Tell the CPU to continue executing.
1040static void Continue() {
1041 memory_break = false;
1042 step_loop = false;
1043 halt_loop = false;
1044 Core::System::GetInstance().InvalidateCpuInstructionCaches();
1045}
1046
1047/**
1048 * Commit breakpoint to list of breakpoints.
1049 *
1050 * @param type Type of breakpoint.
1051 * @param addr Address of breakpoint.
1052 * @param len Length of breakpoint.
1053 */
1054static bool CommitBreakpoint(BreakpointType type, VAddr addr, u64 len) {
1055 BreakpointMap& p = GetBreakpointMap(type);
1056
1057 Breakpoint breakpoint;
1058 breakpoint.active = true;
1059 breakpoint.addr = addr;
1060 breakpoint.len = len;
1061
1062 auto& system = Core::System::GetInstance();
1063 auto& memory = system.Memory();
1064 memory.ReadBlock(addr, breakpoint.inst.data(), breakpoint.inst.size());
1065
1066 static constexpr std::array<u8, 4> btrap{0x00, 0x7d, 0x20, 0xd4};
1067 if (type == BreakpointType::Execute) {
1068 memory.WriteBlock(addr, btrap.data(), btrap.size());
1069 system.InvalidateCpuInstructionCaches();
1070 }
1071 p.insert({addr, breakpoint});
1072
1073 LOG_DEBUG(Debug_GDBStub, "gdb: added {} breakpoint: {:016X} bytes at {:016X}",
1074 static_cast<int>(type), breakpoint.len, breakpoint.addr);
1075
1076 return true;
1077}
1078
1079/// Handle add breakpoint command from gdb client.
1080static void AddBreakpoint() {
1081 BreakpointType type;
1082
1083 u8 type_id = HexCharToValue(command_buffer[1]);
1084 switch (type_id) {
1085 case 0:
1086 case 1:
1087 type = BreakpointType::Execute;
1088 break;
1089 case 2:
1090 type = BreakpointType::Write;
1091 break;
1092 case 3:
1093 type = BreakpointType::Read;
1094 break;
1095 case 4:
1096 type = BreakpointType::Access;
1097 break;
1098 default:
1099 return SendReply("E01");
1100 }
1101
1102 auto start_offset = command_buffer + 3;
1103 auto addr_pos = std::find(start_offset, command_buffer + command_length, ',');
1104 VAddr addr = HexToLong(start_offset, static_cast<u64>(addr_pos - start_offset));
1105
1106 start_offset = addr_pos + 1;
1107 u64 len =
1108 HexToLong(start_offset, static_cast<u64>((command_buffer + command_length) - start_offset));
1109
1110 if (type == BreakpointType::Access) {
1111 // Access is made up of Read and Write types, so add both breakpoints
1112 type = BreakpointType::Read;
1113
1114 if (!CommitBreakpoint(type, addr, len)) {
1115 return SendReply("E02");
1116 }
1117
1118 type = BreakpointType::Write;
1119 }
1120
1121 if (!CommitBreakpoint(type, addr, len)) {
1122 return SendReply("E02");
1123 }
1124
1125 SendReply("OK");
1126}
1127
1128/// Handle remove breakpoint command from gdb client.
1129static void RemoveBreakpoint() {
1130 BreakpointType type;
1131
1132 u8 type_id = HexCharToValue(command_buffer[1]);
1133 switch (type_id) {
1134 case 0:
1135 case 1:
1136 type = BreakpointType::Execute;
1137 break;
1138 case 2:
1139 type = BreakpointType::Write;
1140 break;
1141 case 3:
1142 type = BreakpointType::Read;
1143 break;
1144 case 4:
1145 type = BreakpointType::Access;
1146 break;
1147 default:
1148 return SendReply("E01");
1149 }
1150
1151 auto start_offset = command_buffer + 3;
1152 auto addr_pos = std::find(start_offset, command_buffer + command_length, ',');
1153 VAddr addr = HexToLong(start_offset, static_cast<u64>(addr_pos - start_offset));
1154
1155 if (type == BreakpointType::Access) {
1156 // Access is made up of Read and Write types, so add both breakpoints
1157 type = BreakpointType::Read;
1158 RemoveBreakpoint(type, addr);
1159
1160 type = BreakpointType::Write;
1161 }
1162
1163 RemoveBreakpoint(type, addr);
1164 SendReply("OK");
1165}
1166
1167void HandlePacket() {
1168 if (!IsConnected()) {
1169 if (defer_start) {
1170 ToggleServer(true);
1171 }
1172 return;
1173 }
1174
1175 if (!IsDataAvailable()) {
1176 return;
1177 }
1178
1179 ReadCommand();
1180 if (command_length == 0) {
1181 return;
1182 }
1183
1184 LOG_DEBUG(Debug_GDBStub, "Packet: {}", command_buffer);
1185
1186 switch (command_buffer[0]) {
1187 case 'q':
1188 HandleQuery();
1189 break;
1190 case 'H':
1191 HandleSetThread();
1192 break;
1193 case '?':
1194 SendSignal(current_thread, latest_signal);
1195 break;
1196 case 'k':
1197 Shutdown();
1198 LOG_INFO(Debug_GDBStub, "killed by gdb");
1199 return;
1200 case 'g':
1201 ReadRegisters();
1202 break;
1203 case 'G':
1204 WriteRegisters();
1205 break;
1206 case 'p':
1207 ReadRegister();
1208 break;
1209 case 'P':
1210 WriteRegister();
1211 break;
1212 case 'm':
1213 ReadMemory();
1214 break;
1215 case 'M':
1216 WriteMemory();
1217 break;
1218 case 's':
1219 Step();
1220 return;
1221 case 'C':
1222 case 'c':
1223 Continue();
1224 return;
1225 case 'z':
1226 RemoveBreakpoint();
1227 break;
1228 case 'Z':
1229 AddBreakpoint();
1230 break;
1231 case 'T':
1232 HandleThreadAlive();
1233 break;
1234 default:
1235 SendReply("");
1236 break;
1237 }
1238}
1239
1240void SetServerPort(u16 port) {
1241 gdbstub_port = port;
1242}
1243
1244void ToggleServer(bool status) {
1245 if (status) {
1246 server_enabled = status;
1247
1248 // Start server
1249 if (!IsConnected() && Core::System::GetInstance().IsPoweredOn()) {
1250 Init();
1251 }
1252 } else {
1253 // Stop server
1254 if (IsConnected()) {
1255 Shutdown();
1256 }
1257
1258 server_enabled = status;
1259 }
1260}
1261
1262void DeferStart() {
1263 defer_start = true;
1264}
1265
1266static void Init(u16 port) {
1267 if (!server_enabled) {
1268 // Set the halt loop to false in case the user enabled the gdbstub mid-execution.
1269 // This way the CPU can still execute normally.
1270 halt_loop = false;
1271 step_loop = false;
1272 return;
1273 }
1274
1275 // Setup initial gdbstub status
1276 halt_loop = true;
1277 step_loop = false;
1278
1279 breakpoints_execute.clear();
1280 breakpoints_read.clear();
1281 breakpoints_write.clear();
1282
1283 modules.clear();
1284
1285 // Start gdb server
1286 LOG_INFO(Debug_GDBStub, "Starting GDB server on port {}...", port);
1287
1288 sockaddr_in saddr_server = {};
1289 saddr_server.sin_family = AF_INET;
1290 saddr_server.sin_port = htons(port);
1291 saddr_server.sin_addr.s_addr = INADDR_ANY;
1292
1293#ifdef _WIN32
1294 WSAStartup(MAKEWORD(2, 2), &InitData);
1295#endif
1296
1297 int tmpsock = static_cast<int>(socket(PF_INET, SOCK_STREAM, 0));
1298 if (tmpsock == -1) {
1299 LOG_ERROR(Debug_GDBStub, "Failed to create gdb socket");
1300 }
1301
1302 // Set socket to SO_REUSEADDR so it can always bind on the same port
1303 int reuse_enabled = 1;
1304 if (setsockopt(tmpsock, SOL_SOCKET, SO_REUSEADDR, (const char*)&reuse_enabled,
1305 sizeof(reuse_enabled)) < 0) {
1306 LOG_ERROR(Debug_GDBStub, "Failed to set gdb socket option");
1307 }
1308
1309 const sockaddr* server_addr = reinterpret_cast<const sockaddr*>(&saddr_server);
1310 socklen_t server_addrlen = sizeof(saddr_server);
1311 if (bind(tmpsock, server_addr, server_addrlen) < 0) {
1312 LOG_ERROR(Debug_GDBStub, "Failed to bind gdb socket");
1313 }
1314
1315 if (listen(tmpsock, 1) < 0) {
1316 LOG_ERROR(Debug_GDBStub, "Failed to listen to gdb socket");
1317 }
1318
1319 // Wait for gdb to connect
1320 LOG_INFO(Debug_GDBStub, "Waiting for gdb to connect...");
1321 sockaddr_in saddr_client;
1322 sockaddr* client_addr = reinterpret_cast<sockaddr*>(&saddr_client);
1323 socklen_t client_addrlen = sizeof(saddr_client);
1324 gdbserver_socket = static_cast<int>(accept(tmpsock, client_addr, &client_addrlen));
1325 if (gdbserver_socket < 0) {
1326 // In the case that we couldn't start the server for whatever reason, just start CPU
1327 // execution like normal.
1328 halt_loop = false;
1329 step_loop = false;
1330
1331 LOG_ERROR(Debug_GDBStub, "Failed to accept gdb client");
1332 } else {
1333 LOG_INFO(Debug_GDBStub, "Client connected.");
1334 saddr_client.sin_addr.s_addr = ntohl(saddr_client.sin_addr.s_addr);
1335 }
1336
1337 // Clean up temporary socket if it's still alive at this point.
1338 if (tmpsock != -1) {
1339 shutdown(tmpsock, SHUT_RDWR);
1340 }
1341}
1342
1343void Init() {
1344 Init(gdbstub_port);
1345}
1346
1347void Shutdown() {
1348 if (!server_enabled) {
1349 return;
1350 }
1351 defer_start = false;
1352
1353 LOG_INFO(Debug_GDBStub, "Stopping GDB ...");
1354 if (gdbserver_socket != -1) {
1355 shutdown(gdbserver_socket, SHUT_RDWR);
1356 gdbserver_socket = -1;
1357 }
1358
1359#ifdef _WIN32
1360 WSACleanup();
1361#endif
1362
1363 LOG_INFO(Debug_GDBStub, "GDB stopped.");
1364}
1365
1366bool IsServerEnabled() {
1367 return server_enabled;
1368}
1369
1370bool IsConnected() {
1371 return IsServerEnabled() && gdbserver_socket != -1;
1372}
1373
1374bool GetCpuHaltFlag() {
1375 return halt_loop;
1376}
1377
1378bool GetCpuStepFlag() {
1379 return step_loop;
1380}
1381
1382void SetCpuStepFlag(bool is_step) {
1383 step_loop = is_step;
1384}
1385
1386void SendTrap(Kernel::Thread* thread, int trap) {
1387 if (!send_trap) {
1388 return;
1389 }
1390
1391 current_thread = thread;
1392 SendSignal(thread, trap);
1393
1394 halt_loop = true;
1395 send_trap = false;
1396}
1397}; // namespace GDBStub
diff --git a/src/core/gdbstub/gdbstub.h b/src/core/gdbstub/gdbstub.h
deleted file mode 100644
index 8fe3c320b..000000000
--- a/src/core/gdbstub/gdbstub.h
+++ /dev/null
@@ -1,114 +0,0 @@
1// Copyright 2013 Dolphin Emulator Project
2// Licensed under GPLv2+
3// Refer to the license.txt file included.
4
5// Originally written by Sven Peter <sven@fail0verflow.com> for anergistic.
6
7#pragma once
8
9#include <string>
10#include "common/common_types.h"
11#include "core/hle/kernel/thread.h"
12
13namespace GDBStub {
14
15/// Breakpoint Method
16enum class BreakpointType {
17 None, ///< None
18 Execute, ///< Execution Breakpoint
19 Read, ///< Read Breakpoint
20 Write, ///< Write Breakpoint
21 Access ///< Access (R/W) Breakpoint
22};
23
24struct BreakpointAddress {
25 VAddr address;
26 BreakpointType type;
27};
28
29/**
30 * Set the port the gdbstub should use to listen for connections.
31 *
32 * @param port Port to listen for connection
33 */
34void SetServerPort(u16 port);
35
36/**
37 * Starts or stops the server if possible.
38 *
39 * @param status Set the server to enabled or disabled.
40 */
41void ToggleServer(bool status);
42
43/// Start the gdbstub server.
44void Init();
45
46/**
47 * Defer initialization of the gdbstub to the first packet processing functions.
48 * This avoids a case where the gdbstub thread is frozen after initialization
49 * and fails to respond in time to packets.
50 */
51void DeferStart();
52
53/// Stop gdbstub server.
54void Shutdown();
55
56/// Checks if the gdbstub server is enabled.
57bool IsServerEnabled();
58
59/// Returns true if there is an active socket connection.
60bool IsConnected();
61
62/// Register module.
63void RegisterModule(std::string name, VAddr beg, VAddr end, bool add_elf_ext = true);
64
65/**
66 * Signal to the gdbstub server that it should halt CPU execution.
67 *
68 * @param is_memory_break If true, the break resulted from a memory breakpoint.
69 */
70void Break(bool is_memory_break = false);
71
72/// Determine if there was a memory breakpoint.
73bool IsMemoryBreak();
74
75/// Read and handle packet from gdb client.
76void HandlePacket();
77
78/**
79 * Get the nearest breakpoint of the specified type at the given address.
80 *
81 * @param addr Address to search from.
82 * @param type Type of breakpoint.
83 */
84BreakpointAddress GetNextBreakpointFromAddress(VAddr addr, GDBStub::BreakpointType type);
85
86/**
87 * Check if a breakpoint of the specified type exists at the given address.
88 *
89 * @param addr Address of breakpoint.
90 * @param type Type of breakpoint.
91 */
92bool CheckBreakpoint(VAddr addr, GDBStub::BreakpointType type);
93
94/// If set to true, the CPU will halt at the beginning of the next CPU loop.
95bool GetCpuHaltFlag();
96
97/// If set to true and the CPU is halted, the CPU will step one instruction.
98bool GetCpuStepFlag();
99
100/**
101 * When set to true, the CPU will step one instruction when the CPU is halted next.
102 *
103 * @param is_step
104 */
105void SetCpuStepFlag(bool is_step);
106
107/**
108 * Send trap signal from thread back to the gdbstub server.
109 *
110 * @param thread Sending thread.
111 * @param trap Trap no.
112 */
113void SendTrap(Kernel::Thread* thread, int trap);
114} // namespace GDBStub
diff --git a/src/core/loader/deconstructed_rom_directory.cpp b/src/core/loader/deconstructed_rom_directory.cpp
index 2002dc4f2..79ebf11de 100644
--- a/src/core/loader/deconstructed_rom_directory.cpp
+++ b/src/core/loader/deconstructed_rom_directory.cpp
@@ -12,7 +12,6 @@
12#include "core/file_sys/control_metadata.h" 12#include "core/file_sys/control_metadata.h"
13#include "core/file_sys/patch_manager.h" 13#include "core/file_sys/patch_manager.h"
14#include "core/file_sys/romfs_factory.h" 14#include "core/file_sys/romfs_factory.h"
15#include "core/gdbstub/gdbstub.h"
16#include "core/hle/kernel/kernel.h" 15#include "core/hle/kernel/kernel.h"
17#include "core/hle/kernel/memory/page_table.h" 16#include "core/hle/kernel/memory/page_table.h"
18#include "core/hle/kernel/process.h" 17#include "core/hle/kernel/process.h"
@@ -180,8 +179,6 @@ AppLoader_DeconstructedRomDirectory::LoadResult AppLoader_DeconstructedRomDirect
180 next_load_addr = *tentative_next_load_addr; 179 next_load_addr = *tentative_next_load_addr;
181 modules.insert_or_assign(load_addr, module); 180 modules.insert_or_assign(load_addr, module);
182 LOG_DEBUG(Loader, "loaded module {} @ 0x{:X}", module, load_addr); 181 LOG_DEBUG(Loader, "loaded module {} @ 0x{:X}", module, load_addr);
183 // Register module with GDBStub
184 GDBStub::RegisterModule(module, load_addr, next_load_addr - 1, false);
185 } 182 }
186 183
187 // Find the RomFS by searching for a ".romfs" file in this directory 184 // Find the RomFS by searching for a ".romfs" file in this directory
diff --git a/src/core/loader/kip.cpp b/src/core/loader/kip.cpp
index 2a905d3e4..e162c4ff0 100644
--- a/src/core/loader/kip.cpp
+++ b/src/core/loader/kip.cpp
@@ -5,7 +5,6 @@
5#include <cstring> 5#include <cstring>
6#include "core/file_sys/kernel_executable.h" 6#include "core/file_sys/kernel_executable.h"
7#include "core/file_sys/program_metadata.h" 7#include "core/file_sys/program_metadata.h"
8#include "core/gdbstub/gdbstub.h"
9#include "core/hle/kernel/code_set.h" 8#include "core/hle/kernel/code_set.h"
10#include "core/hle/kernel/memory/page_table.h" 9#include "core/hle/kernel/memory/page_table.h"
11#include "core/hle/kernel/process.h" 10#include "core/hle/kernel/process.h"
@@ -91,8 +90,6 @@ AppLoader::LoadResult AppLoader_KIP::Load(Kernel::Process& process,
91 program_image.resize(PageAlignSize(kip->GetBSSOffset()) + kip->GetBSSSize()); 90 program_image.resize(PageAlignSize(kip->GetBSSOffset()) + kip->GetBSSSize());
92 codeset.DataSegment().size += kip->GetBSSSize(); 91 codeset.DataSegment().size += kip->GetBSSSize();
93 92
94 GDBStub::RegisterModule(kip->GetName(), base_address, base_address + program_image.size());
95
96 codeset.memory = std::move(program_image); 93 codeset.memory = std::move(program_image);
97 process.LoadModule(std::move(codeset), base_address); 94 process.LoadModule(std::move(codeset), base_address);
98 95
diff --git a/src/core/loader/nro.cpp b/src/core/loader/nro.cpp
index 5f4b3104b..ccf8cc153 100644
--- a/src/core/loader/nro.cpp
+++ b/src/core/loader/nro.cpp
@@ -14,10 +14,10 @@
14#include "core/file_sys/control_metadata.h" 14#include "core/file_sys/control_metadata.h"
15#include "core/file_sys/romfs_factory.h" 15#include "core/file_sys/romfs_factory.h"
16#include "core/file_sys/vfs_offset.h" 16#include "core/file_sys/vfs_offset.h"
17#include "core/gdbstub/gdbstub.h"
18#include "core/hle/kernel/code_set.h" 17#include "core/hle/kernel/code_set.h"
19#include "core/hle/kernel/memory/page_table.h" 18#include "core/hle/kernel/memory/page_table.h"
20#include "core/hle/kernel/process.h" 19#include "core/hle/kernel/process.h"
20#include "core/hle/kernel/thread.h"
21#include "core/hle/service/filesystem/filesystem.h" 21#include "core/hle/service/filesystem/filesystem.h"
22#include "core/loader/nro.h" 22#include "core/loader/nro.h"
23#include "core/loader/nso.h" 23#include "core/loader/nso.h"
@@ -197,10 +197,6 @@ static bool LoadNroImpl(Kernel::Process& process, const std::vector<u8>& data,
197 codeset.memory = std::move(program_image); 197 codeset.memory = std::move(program_image);
198 process.LoadModule(std::move(codeset), process.PageTable().GetCodeRegionStart()); 198 process.LoadModule(std::move(codeset), process.PageTable().GetCodeRegionStart());
199 199
200 // Register module with GDBStub
201 GDBStub::RegisterModule(name, process.PageTable().GetCodeRegionStart(),
202 process.PageTable().GetCodeRegionEnd());
203
204 return true; 200 return true;
205} 201}
206 202
diff --git a/src/core/loader/nso.cpp b/src/core/loader/nso.cpp
index aa85c1a29..95b6f339a 100644
--- a/src/core/loader/nso.cpp
+++ b/src/core/loader/nso.cpp
@@ -14,10 +14,10 @@
14#include "common/swap.h" 14#include "common/swap.h"
15#include "core/core.h" 15#include "core/core.h"
16#include "core/file_sys/patch_manager.h" 16#include "core/file_sys/patch_manager.h"
17#include "core/gdbstub/gdbstub.h"
18#include "core/hle/kernel/code_set.h" 17#include "core/hle/kernel/code_set.h"
19#include "core/hle/kernel/memory/page_table.h" 18#include "core/hle/kernel/memory/page_table.h"
20#include "core/hle/kernel/process.h" 19#include "core/hle/kernel/process.h"
20#include "core/hle/kernel/thread.h"
21#include "core/loader/nso.h" 21#include "core/loader/nso.h"
22#include "core/memory.h" 22#include "core/memory.h"
23#include "core/settings.h" 23#include "core/settings.h"
@@ -159,9 +159,6 @@ std::optional<VAddr> AppLoader_NSO::LoadModule(Kernel::Process& process, Core::S
159 codeset.memory = std::move(program_image); 159 codeset.memory = std::move(program_image);
160 process.LoadModule(std::move(codeset), load_base); 160 process.LoadModule(std::move(codeset), load_base);
161 161
162 // Register module with GDBStub
163 GDBStub::RegisterModule(file.GetName(), load_base, load_base);
164
165 return load_base + image_size; 162 return load_base + image_size;
166} 163}
167 164
diff --git a/src/core/settings.cpp b/src/core/settings.cpp
index aadbc3932..e9997a263 100644
--- a/src/core/settings.cpp
+++ b/src/core/settings.cpp
@@ -4,9 +4,10 @@
4 4
5#include <string_view> 5#include <string_view>
6 6
7#include "common/assert.h"
7#include "common/file_util.h" 8#include "common/file_util.h"
9#include "common/logging/log.h"
8#include "core/core.h" 10#include "core/core.h"
9#include "core/gdbstub/gdbstub.h"
10#include "core/hle/service/hid/hid.h" 11#include "core/hle/service/hid/hid.h"
11#include "core/settings.h" 12#include "core/settings.h"
12#include "video_core/renderer_base.h" 13#include "video_core/renderer_base.h"
@@ -31,13 +32,9 @@ std::string GetTimeZoneString() {
31 return timezones[time_zone_index]; 32 return timezones[time_zone_index];
32} 33}
33 34
34void Apply() { 35void Apply(Core::System& system) {
35 GDBStub::SetServerPort(values.gdbstub_port); 36 if (system.IsPoweredOn()) {
36 GDBStub::ToggleServer(values.use_gdbstub); 37 system.Renderer().RefreshBaseSettings();
37
38 auto& system_instance = Core::System::GetInstance();
39 if (system_instance.IsPoweredOn()) {
40 system_instance.Renderer().RefreshBaseSettings();
41 } 38 }
42 39
43 Service::HID::ReloadInputDevices(); 40 Service::HID::ReloadInputDevices();
@@ -106,9 +103,9 @@ float Volume() {
106 return values.volume.GetValue(); 103 return values.volume.GetValue();
107} 104}
108 105
109void RestoreGlobalState() { 106void RestoreGlobalState(bool is_powered_on) {
110 // If a game is running, DO NOT restore the global settings state 107 // If a game is running, DO NOT restore the global settings state
111 if (Core::System::GetInstance().IsPoweredOn()) { 108 if (is_powered_on) {
112 return; 109 return;
113 } 110 }
114 111
diff --git a/src/core/settings.h b/src/core/settings.h
index 1143aba5d..3df611d5b 100644
--- a/src/core/settings.h
+++ b/src/core/settings.h
@@ -14,6 +14,10 @@
14#include "common/common_types.h" 14#include "common/common_types.h"
15#include "input_common/settings.h" 15#include "input_common/settings.h"
16 16
17namespace Core {
18class System;
19}
20
17namespace Settings { 21namespace Settings {
18 22
19enum class RendererBackend { 23enum class RendererBackend {
@@ -247,11 +251,11 @@ float Volume();
247 251
248std::string GetTimeZoneString(); 252std::string GetTimeZoneString();
249 253
250void Apply(); 254void Apply(Core::System& system);
251void LogSettings(); 255void LogSettings();
252 256
253// Restore the global state of all applicable settings in the Values struct 257// Restore the global state of all applicable settings in the Values struct
254void RestoreGlobalState(); 258void RestoreGlobalState(bool is_powered_on);
255 259
256// Fixes settings that are known to cause issues with the emulator 260// Fixes settings that are known to cause issues with the emulator
257void Sanitize(); 261void Sanitize();
diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp
index 3c423a271..8be9e93c3 100644
--- a/src/yuzu/configuration/config.cpp
+++ b/src/yuzu/configuration/config.cpp
@@ -7,6 +7,7 @@
7#include <QSettings> 7#include <QSettings>
8#include "common/common_paths.h" 8#include "common/common_paths.h"
9#include "common/file_util.h" 9#include "common/file_util.h"
10#include "core/core.h"
10#include "core/hle/service/acc/profile_manager.h" 11#include "core/hle/service/acc/profile_manager.h"
11#include "core/hle/service/hid/controllers/npad.h" 12#include "core/hle/service/hid/controllers/npad.h"
12#include "input_common/main.h" 13#include "input_common/main.h"
@@ -1598,7 +1599,7 @@ void Config::Reload() {
1598 Settings::Sanitize(); 1599 Settings::Sanitize();
1599 // To apply default value changes 1600 // To apply default value changes
1600 SaveValues(); 1601 SaveValues();
1601 Settings::Apply(); 1602 Settings::Apply(Core::System::GetInstance());
1602} 1603}
1603 1604
1604void Config::Save() { 1605void Config::Save() {
diff --git a/src/yuzu/configuration/configure_dialog.cpp b/src/yuzu/configuration/configure_dialog.cpp
index 5041e0bf8..b33f8437a 100644
--- a/src/yuzu/configuration/configure_dialog.cpp
+++ b/src/yuzu/configuration/configure_dialog.cpp
@@ -5,6 +5,7 @@
5#include <QHash> 5#include <QHash>
6#include <QListWidgetItem> 6#include <QListWidgetItem>
7#include <QSignalBlocker> 7#include <QSignalBlocker>
8#include "core/core.h"
8#include "core/settings.h" 9#include "core/settings.h"
9#include "ui_configure.h" 10#include "ui_configure.h"
10#include "yuzu/configuration/config.h" 11#include "yuzu/configuration/config.h"
@@ -54,7 +55,7 @@ void ConfigureDialog::ApplyConfiguration() {
54 ui->debugTab->ApplyConfiguration(); 55 ui->debugTab->ApplyConfiguration();
55 ui->webTab->ApplyConfiguration(); 56 ui->webTab->ApplyConfiguration();
56 ui->serviceTab->ApplyConfiguration(); 57 ui->serviceTab->ApplyConfiguration();
57 Settings::Apply(); 58 Settings::Apply(Core::System::GetInstance());
58 Settings::LogSettings(); 59 Settings::LogSettings();
59} 60}
60 61
diff --git a/src/yuzu/configuration/configure_per_game.cpp b/src/yuzu/configuration/configure_per_game.cpp
index 8eac3bd9d..f598513df 100644
--- a/src/yuzu/configuration/configure_per_game.cpp
+++ b/src/yuzu/configuration/configure_per_game.cpp
@@ -57,7 +57,7 @@ void ConfigurePerGame::ApplyConfiguration() {
57 ui->graphicsAdvancedTab->ApplyConfiguration(); 57 ui->graphicsAdvancedTab->ApplyConfiguration();
58 ui->audioTab->ApplyConfiguration(); 58 ui->audioTab->ApplyConfiguration();
59 59
60 Settings::Apply(); 60 Settings::Apply(Core::System::GetInstance());
61 Settings::LogSettings(); 61 Settings::LogSettings();
62 62
63 game_config->Save(); 63 game_config->Save();
diff --git a/src/yuzu/configuration/configure_profile_manager.cpp b/src/yuzu/configuration/configure_profile_manager.cpp
index 6334c4c50..13d9a4757 100644
--- a/src/yuzu/configuration/configure_profile_manager.cpp
+++ b/src/yuzu/configuration/configure_profile_manager.cpp
@@ -180,7 +180,7 @@ void ConfigureProfileManager::ApplyConfiguration() {
180 return; 180 return;
181 } 181 }
182 182
183 Settings::Apply(); 183 Settings::Apply(Core::System::GetInstance());
184} 184}
185 185
186void ConfigureProfileManager::SelectUser(const QModelIndex& index) { 186void ConfigureProfileManager::SelectUser(const QModelIndex& index) {
diff --git a/src/yuzu/configuration/configure_system.cpp b/src/yuzu/configuration/configure_system.cpp
index 59a58d92c..6cf2032da 100644
--- a/src/yuzu/configuration/configure_system.cpp
+++ b/src/yuzu/configuration/configure_system.cpp
@@ -105,16 +105,18 @@ void ConfigureSystem::SetConfiguration() {
105void ConfigureSystem::ReadSystemSettings() {} 105void ConfigureSystem::ReadSystemSettings() {}
106 106
107void ConfigureSystem::ApplyConfiguration() { 107void ConfigureSystem::ApplyConfiguration() {
108 // Allow setting custom RTC even if system is powered on, to allow in-game time to be fast 108 auto& system = Core::System::GetInstance();
109 // forwared 109
110 // Allow setting custom RTC even if system is powered on,
111 // to allow in-game time to be fast forwarded
110 if (Settings::values.custom_rtc.UsingGlobal()) { 112 if (Settings::values.custom_rtc.UsingGlobal()) {
111 if (ui->custom_rtc_checkbox->isChecked()) { 113 if (ui->custom_rtc_checkbox->isChecked()) {
112 Settings::values.custom_rtc.SetValue( 114 Settings::values.custom_rtc.SetValue(
113 std::chrono::seconds(ui->custom_rtc_edit->dateTime().toSecsSinceEpoch())); 115 std::chrono::seconds(ui->custom_rtc_edit->dateTime().toSecsSinceEpoch()));
114 if (Core::System::GetInstance().IsPoweredOn()) { 116 if (system.IsPoweredOn()) {
115 const s64 posix_time{Settings::values.custom_rtc.GetValue()->count() + 117 const s64 posix_time{Settings::values.custom_rtc.GetValue()->count() +
116 Service::Time::TimeManager::GetExternalTimeZoneOffset()}; 118 Service::Time::TimeManager::GetExternalTimeZoneOffset()};
117 Core::System::GetInstance().GetTimeManager().UpdateLocalSystemClockTime(posix_time); 119 system.GetTimeManager().UpdateLocalSystemClockTime(posix_time);
118 } 120 }
119 } else { 121 } else {
120 Settings::values.custom_rtc.SetValue(std::nullopt); 122 Settings::values.custom_rtc.SetValue(std::nullopt);
@@ -197,7 +199,7 @@ void ConfigureSystem::ApplyConfiguration() {
197 } 199 }
198 } 200 }
199 201
200 Settings::Apply(); 202 Settings::Apply(system);
201} 203}
202 204
203void ConfigureSystem::RefreshConsoleID() { 205void ConfigureSystem::RefreshConsoleID() {
diff --git a/src/yuzu/configuration/configure_ui.cpp b/src/yuzu/configuration/configure_ui.cpp
index dbe3f78c8..aed876008 100644
--- a/src/yuzu/configuration/configure_ui.cpp
+++ b/src/yuzu/configuration/configure_ui.cpp
@@ -9,6 +9,7 @@
9#include <QDirIterator> 9#include <QDirIterator>
10#include "common/common_types.h" 10#include "common/common_types.h"
11#include "common/file_util.h" 11#include "common/file_util.h"
12#include "core/core.h"
12#include "core/settings.h" 13#include "core/settings.h"
13#include "ui_configure_ui.h" 14#include "ui_configure_ui.h"
14#include "yuzu/configuration/configure_ui.h" 15#include "yuzu/configuration/configure_ui.h"
@@ -84,7 +85,7 @@ void ConfigureUi::ApplyConfiguration() {
84 UISettings::values.enable_screenshot_save_as = ui->enable_screenshot_save_as->isChecked(); 85 UISettings::values.enable_screenshot_save_as = ui->enable_screenshot_save_as->isChecked();
85 Common::FS::GetUserPath(Common::FS::UserPath::ScreenshotsDir, 86 Common::FS::GetUserPath(Common::FS::UserPath::ScreenshotsDir,
86 ui->screenshot_path_edit->text().toStdString()); 87 ui->screenshot_path_edit->text().toStdString());
87 Settings::Apply(); 88 Settings::Apply(Core::System::GetInstance());
88} 89}
89 90
90void ConfigureUi::RequestGameListUpdate() { 91void ConfigureUi::RequestGameListUpdate() {
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index 07fa85741..871ff4ae4 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -172,7 +172,7 @@ void GMainWindow::ShowTelemetryCallout() {
172 "<br/><br/>Would you like to share your usage data with us?"); 172 "<br/><br/>Would you like to share your usage data with us?");
173 if (QMessageBox::question(this, tr("Telemetry"), telemetry_message) != QMessageBox::Yes) { 173 if (QMessageBox::question(this, tr("Telemetry"), telemetry_message) != QMessageBox::Yes) {
174 Settings::values.enable_telemetry = false; 174 Settings::values.enable_telemetry = false;
175 Settings::Apply(); 175 Settings::Apply(Core::System::GetInstance());
176 } 176 }
177} 177}
178 178
@@ -302,7 +302,7 @@ void GMainWindow::ControllerSelectorReconfigureControllers(
302 emit ControllerSelectorReconfigureFinished(); 302 emit ControllerSelectorReconfigureFinished();
303 303
304 // Don't forget to apply settings. 304 // Don't forget to apply settings.
305 Settings::Apply(); 305 Settings::Apply(Core::System::GetInstance());
306 config->Save(); 306 config->Save();
307 307
308 UpdateStatusButtons(); 308 UpdateStatusButtons();
@@ -571,11 +571,11 @@ void GMainWindow::InitializeWidgets() {
571 if (emulation_running) { 571 if (emulation_running) {
572 return; 572 return;
573 } 573 }
574 bool is_async = !Settings::values.use_asynchronous_gpu_emulation.GetValue() || 574 const bool is_async = !Settings::values.use_asynchronous_gpu_emulation.GetValue() ||
575 Settings::values.use_multi_core.GetValue(); 575 Settings::values.use_multi_core.GetValue();
576 Settings::values.use_asynchronous_gpu_emulation.SetValue(is_async); 576 Settings::values.use_asynchronous_gpu_emulation.SetValue(is_async);
577 async_status_button->setChecked(Settings::values.use_asynchronous_gpu_emulation.GetValue()); 577 async_status_button->setChecked(Settings::values.use_asynchronous_gpu_emulation.GetValue());
578 Settings::Apply(); 578 Settings::Apply(Core::System::GetInstance());
579 }); 579 });
580 async_status_button->setText(tr("ASYNC")); 580 async_status_button->setText(tr("ASYNC"));
581 async_status_button->setCheckable(true); 581 async_status_button->setCheckable(true);
@@ -590,12 +590,12 @@ void GMainWindow::InitializeWidgets() {
590 return; 590 return;
591 } 591 }
592 Settings::values.use_multi_core.SetValue(!Settings::values.use_multi_core.GetValue()); 592 Settings::values.use_multi_core.SetValue(!Settings::values.use_multi_core.GetValue());
593 bool is_async = Settings::values.use_asynchronous_gpu_emulation.GetValue() || 593 const bool is_async = Settings::values.use_asynchronous_gpu_emulation.GetValue() ||
594 Settings::values.use_multi_core.GetValue(); 594 Settings::values.use_multi_core.GetValue();
595 Settings::values.use_asynchronous_gpu_emulation.SetValue(is_async); 595 Settings::values.use_asynchronous_gpu_emulation.SetValue(is_async);
596 async_status_button->setChecked(Settings::values.use_asynchronous_gpu_emulation.GetValue()); 596 async_status_button->setChecked(Settings::values.use_asynchronous_gpu_emulation.GetValue());
597 multicore_status_button->setChecked(Settings::values.use_multi_core.GetValue()); 597 multicore_status_button->setChecked(Settings::values.use_multi_core.GetValue());
598 Settings::Apply(); 598 Settings::Apply(Core::System::GetInstance());
599 }); 599 });
600 multicore_status_button->setText(tr("MULTICORE")); 600 multicore_status_button->setText(tr("MULTICORE"));
601 multicore_status_button->setCheckable(true); 601 multicore_status_button->setCheckable(true);
@@ -630,7 +630,7 @@ void GMainWindow::InitializeWidgets() {
630 Settings::values.renderer_backend.SetValue(Settings::RendererBackend::OpenGL); 630 Settings::values.renderer_backend.SetValue(Settings::RendererBackend::OpenGL);
631 } 631 }
632 632
633 Settings::Apply(); 633 Settings::Apply(Core::System::GetInstance());
634 }); 634 });
635#endif // HAS_VULKAN 635#endif // HAS_VULKAN
636 statusBar()->insertPermanentWidget(0, renderer_status_button); 636 statusBar()->insertPermanentWidget(0, renderer_status_button);
@@ -2130,14 +2130,14 @@ void GMainWindow::OnPauseGame() {
2130} 2130}
2131 2131
2132void GMainWindow::OnStopGame() { 2132void GMainWindow::OnStopGame() {
2133 Core::System& system{Core::System::GetInstance()}; 2133 auto& system{Core::System::GetInstance()};
2134 if (system.GetExitLock() && !ConfirmForceLockedExit()) { 2134 if (system.GetExitLock() && !ConfirmForceLockedExit()) {
2135 return; 2135 return;
2136 } 2136 }
2137 2137
2138 ShutdownGame(); 2138 ShutdownGame();
2139 2139
2140 Settings::RestoreGlobalState(); 2140 Settings::RestoreGlobalState(system.IsPoweredOn());
2141 UpdateStatusButtons(); 2141 UpdateStatusButtons();
2142} 2142}
2143 2143
@@ -2312,10 +2312,11 @@ void GMainWindow::OnConfigurePerGame() {
2312 2312
2313void GMainWindow::OpenPerGameConfiguration(u64 title_id, const std::string& file_name) { 2313void GMainWindow::OpenPerGameConfiguration(u64 title_id, const std::string& file_name) {
2314 const auto v_file = Core::GetGameFileFromPath(vfs, file_name); 2314 const auto v_file = Core::GetGameFileFromPath(vfs, file_name);
2315 const auto& system = Core::System::GetInstance();
2315 2316
2316 ConfigurePerGame dialog(this, title_id); 2317 ConfigurePerGame dialog(this, title_id);
2317 dialog.LoadFromFile(v_file); 2318 dialog.LoadFromFile(v_file);
2318 auto result = dialog.exec(); 2319 const auto result = dialog.exec();
2319 if (result == QDialog::Accepted) { 2320 if (result == QDialog::Accepted) {
2320 dialog.ApplyConfiguration(); 2321 dialog.ApplyConfiguration();
2321 2322
@@ -2325,13 +2326,14 @@ void GMainWindow::OpenPerGameConfiguration(u64 title_id, const std::string& file
2325 } 2326 }
2326 2327
2327 // Do not cause the global config to write local settings into the config file 2328 // Do not cause the global config to write local settings into the config file
2328 Settings::RestoreGlobalState(); 2329 const bool is_powered_on = system.IsPoweredOn();
2330 Settings::RestoreGlobalState(is_powered_on);
2329 2331
2330 if (!Core::System::GetInstance().IsPoweredOn()) { 2332 if (!is_powered_on) {
2331 config->Save(); 2333 config->Save();
2332 } 2334 }
2333 } else { 2335 } else {
2334 Settings::RestoreGlobalState(); 2336 Settings::RestoreGlobalState(system.IsPoweredOn());
2335 } 2337 }
2336} 2338}
2337 2339
@@ -2602,7 +2604,7 @@ void GMainWindow::OnCoreError(Core::System::ResultStatus result, std::string det
2602 if (emu_thread) { 2604 if (emu_thread) {
2603 ShutdownGame(); 2605 ShutdownGame();
2604 2606
2605 Settings::RestoreGlobalState(); 2607 Settings::RestoreGlobalState(Core::System::GetInstance().IsPoweredOn());
2606 UpdateStatusButtons(); 2608 UpdateStatusButtons();
2607 } 2609 }
2608 } else { 2610 } else {
@@ -2774,7 +2776,7 @@ void GMainWindow::closeEvent(QCloseEvent* event) {
2774 if (emu_thread != nullptr) { 2776 if (emu_thread != nullptr) {
2775 ShutdownGame(); 2777 ShutdownGame();
2776 2778
2777 Settings::RestoreGlobalState(); 2779 Settings::RestoreGlobalState(Core::System::GetInstance().IsPoweredOn());
2778 UpdateStatusButtons(); 2780 UpdateStatusButtons();
2779 } 2781 }
2780 2782
diff --git a/src/yuzu_cmd/yuzu.cpp b/src/yuzu_cmd/yuzu.cpp
index ba6e89249..c2efe1ee6 100644
--- a/src/yuzu_cmd/yuzu.cpp
+++ b/src/yuzu_cmd/yuzu.cpp
@@ -25,7 +25,6 @@
25#include "core/crypto/key_manager.h" 25#include "core/crypto/key_manager.h"
26#include "core/file_sys/registered_cache.h" 26#include "core/file_sys/registered_cache.h"
27#include "core/file_sys/vfs_real.h" 27#include "core/file_sys/vfs_real.h"
28#include "core/gdbstub/gdbstub.h"
29#include "core/hle/kernel/process.h" 28#include "core/hle/kernel/process.h"
30#include "core/hle/service/filesystem/filesystem.h" 29#include "core/hle/service/filesystem/filesystem.h"
31#include "core/loader/loader.h" 30#include "core/loader/loader.h"
@@ -174,13 +173,13 @@ int main(int argc, char** argv) {
174 return -1; 173 return -1;
175 } 174 }
176 175
176 auto& system{Core::System::GetInstance()};
177 InputCommon::InputSubsystem input_subsystem;
178
177 // Apply the command line arguments 179 // Apply the command line arguments
178 Settings::values.gdbstub_port = gdb_port; 180 Settings::values.gdbstub_port = gdb_port;
179 Settings::values.use_gdbstub = use_gdbstub; 181 Settings::values.use_gdbstub = use_gdbstub;
180 Settings::Apply(); 182 Settings::Apply(system);
181
182 Core::System& system{Core::System::GetInstance()};
183 InputCommon::InputSubsystem input_subsystem;
184 183
185 std::unique_ptr<EmuWindow_SDL2> emu_window; 184 std::unique_ptr<EmuWindow_SDL2> emu_window;
186 switch (Settings::values.renderer_backend.GetValue()) { 185 switch (Settings::values.renderer_backend.GetValue()) {
diff --git a/src/yuzu_tester/yuzu.cpp b/src/yuzu_tester/yuzu.cpp
index ea94a6537..50bd7ae41 100644
--- a/src/yuzu_tester/yuzu.cpp
+++ b/src/yuzu_tester/yuzu.cpp
@@ -160,10 +160,12 @@ int main(int argc, char** argv) {
160 return -1; 160 return -1;
161 } 161 }
162 162
163 Core::System& system{Core::System::GetInstance()};
164
163 Settings::values.use_gdbstub = false; 165 Settings::values.use_gdbstub = false;
164 Settings::Apply(); 166 Settings::Apply(system);
165 167
166 std::unique_ptr<EmuWindow_SDL2_Hide> emu_window{std::make_unique<EmuWindow_SDL2_Hide>()}; 168 const auto emu_window{std::make_unique<EmuWindow_SDL2_Hide>()};
167 169
168 bool finished = false; 170 bool finished = false;
169 int return_value = 0; 171 int return_value = 0;
@@ -212,7 +214,6 @@ int main(int argc, char** argv) {
212 return_value = -1; 214 return_value = -1;
213 }; 215 };
214 216
215 Core::System& system{Core::System::GetInstance()};
216 system.SetContentProvider(std::make_unique<FileSys::ContentProviderUnion>()); 217 system.SetContentProvider(std::make_unique<FileSys::ContentProviderUnion>());
217 system.SetFilesystem(std::make_shared<FileSys::RealVfsFilesystem>()); 218 system.SetFilesystem(std::make_shared<FileSys::RealVfsFilesystem>());
218 system.GetFileSystemController().CreateFactories(*system.GetFilesystem()); 219 system.GetFileSystemController().CreateFactories(*system.GetFilesystem());