summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
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());