diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/core/CMakeLists.txt | 2 | ||||
| -rw-r--r-- | src/core/arm/dynarmic/arm_dynarmic_64.cpp | 11 | ||||
| -rw-r--r-- | src/core/core.cpp | 7 | ||||
| -rw-r--r-- | src/core/cpu_manager.cpp | 1 | ||||
| -rw-r--r-- | src/core/gdbstub/gdbstub.cpp | 1397 | ||||
| -rw-r--r-- | src/core/gdbstub/gdbstub.h | 114 | ||||
| -rw-r--r-- | src/core/loader/deconstructed_rom_directory.cpp | 3 | ||||
| -rw-r--r-- | src/core/loader/kip.cpp | 3 | ||||
| -rw-r--r-- | src/core/loader/nro.cpp | 6 | ||||
| -rw-r--r-- | src/core/loader/nso.cpp | 5 | ||||
| -rw-r--r-- | src/core/settings.cpp | 17 | ||||
| -rw-r--r-- | src/core/settings.h | 8 | ||||
| -rw-r--r-- | src/yuzu/configuration/config.cpp | 3 | ||||
| -rw-r--r-- | src/yuzu/configuration/configure_dialog.cpp | 3 | ||||
| -rw-r--r-- | src/yuzu/configuration/configure_per_game.cpp | 2 | ||||
| -rw-r--r-- | src/yuzu/configuration/configure_profile_manager.cpp | 2 | ||||
| -rw-r--r-- | src/yuzu/configuration/configure_system.cpp | 12 | ||||
| -rw-r--r-- | src/yuzu/configuration/configure_ui.cpp | 3 | ||||
| -rw-r--r-- | src/yuzu/main.cpp | 36 | ||||
| -rw-r--r-- | src/yuzu_cmd/yuzu.cpp | 9 | ||||
| -rw-r--r-- | src/yuzu_tester/yuzu.cpp | 7 |
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 | |||
| 45 | namespace GDBStub { | ||
| 46 | namespace { | ||
| 47 | constexpr int GDB_BUFFER_SIZE = 10000; | ||
| 48 | |||
| 49 | constexpr char GDB_STUB_START = '$'; | ||
| 50 | constexpr char GDB_STUB_END = '#'; | ||
| 51 | constexpr char GDB_STUB_ACK = '+'; | ||
| 52 | constexpr char GDB_STUB_NACK = '-'; | ||
| 53 | |||
| 54 | #ifndef SIGTRAP | ||
| 55 | constexpr u32 SIGTRAP = 5; | ||
| 56 | #endif | ||
| 57 | |||
| 58 | #ifndef SIGTERM | ||
| 59 | constexpr u32 SIGTERM = 15; | ||
| 60 | #endif | ||
| 61 | |||
| 62 | #ifndef MSG_WAITALL | ||
| 63 | constexpr u32 MSG_WAITALL = 8; | ||
| 64 | #endif | ||
| 65 | |||
| 66 | constexpr u32 LR_REGISTER = 30; | ||
| 67 | constexpr u32 SP_REGISTER = 31; | ||
| 68 | constexpr u32 PC_REGISTER = 32; | ||
| 69 | constexpr u32 PSTATE_REGISTER = 33; | ||
| 70 | constexpr u32 UC_ARM64_REG_Q0 = 34; | ||
| 71 | constexpr 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 | ||
| 76 | constexpr 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 | |||
| 142 | int gdbserver_socket = -1; | ||
| 143 | bool defer_start = false; | ||
| 144 | |||
| 145 | u8 command_buffer[GDB_BUFFER_SIZE]; | ||
| 146 | u32 command_length; | ||
| 147 | |||
| 148 | u32 latest_signal = 0; | ||
| 149 | bool memory_break = false; | ||
| 150 | |||
| 151 | Kernel::Thread* current_thread = nullptr; | ||
| 152 | u32 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. | ||
| 156 | u16 gdbstub_port = 24689; | ||
| 157 | |||
| 158 | bool halt_loop = true; | ||
| 159 | bool step_loop = false; | ||
| 160 | bool send_trap = false; | ||
| 161 | |||
| 162 | // If set to false, the server will never be started and no | ||
| 163 | // gdbstub-related functions will be executed. | ||
| 164 | std::atomic<bool> server_enabled(false); | ||
| 165 | |||
| 166 | #ifdef _WIN32 | ||
| 167 | WSADATA InitData; | ||
| 168 | #endif | ||
| 169 | |||
| 170 | struct Breakpoint { | ||
| 171 | bool active; | ||
| 172 | VAddr addr; | ||
| 173 | u64 len; | ||
| 174 | std::array<u8, 4> inst; | ||
| 175 | }; | ||
| 176 | |||
| 177 | using BreakpointMap = std::map<VAddr, Breakpoint>; | ||
| 178 | BreakpointMap breakpoints_execute; | ||
| 179 | BreakpointMap breakpoints_read; | ||
| 180 | BreakpointMap breakpoints_write; | ||
| 181 | |||
| 182 | struct Module { | ||
| 183 | std::string name; | ||
| 184 | VAddr beg; | ||
| 185 | VAddr end; | ||
| 186 | }; | ||
| 187 | |||
| 188 | std::vector<Module> modules; | ||
| 189 | } // Anonymous namespace | ||
| 190 | |||
| 191 | void 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 | |||
| 204 | static 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 | |||
| 215 | static 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 | |||
| 237 | static 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 | |||
| 257 | static 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 | |||
| 273 | static 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 | */ | ||
| 292 | static 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 | */ | ||
| 310 | static 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 | */ | ||
| 325 | static 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 | */ | ||
| 340 | static 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 | */ | ||
| 356 | static 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 | */ | ||
| 371 | static 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 | */ | ||
| 384 | static 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 | */ | ||
| 397 | static 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 | */ | ||
| 409 | static 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 | */ | ||
| 425 | static 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 | */ | ||
| 441 | static 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. | ||
| 458 | static 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. | ||
| 470 | static 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 | */ | ||
| 480 | static 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 | */ | ||
| 499 | static 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 | |||
| 518 | BreakpointAddress 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 | |||
| 534 | bool 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 | */ | ||
| 576 | static 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 | */ | ||
| 588 | static 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. | ||
| 626 | static 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. | ||
| 687 | static 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. | ||
| 707 | static 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 | */ | ||
| 724 | static 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. | ||
| 753 | static 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. | ||
| 800 | static 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. | ||
| 823 | static 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. | ||
| 855 | static 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. | ||
| 894 | static 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. | ||
| 925 | static 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. | ||
| 956 | static 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. | ||
| 987 | static 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 | |||
| 1009 | void 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. | ||
| 1016 | static 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. | ||
| 1031 | bool IsMemoryBreak() { | ||
| 1032 | if (!IsConnected()) { | ||
| 1033 | return false; | ||
| 1034 | } | ||
| 1035 | |||
| 1036 | return memory_break; | ||
| 1037 | } | ||
| 1038 | |||
| 1039 | /// Tell the CPU to continue executing. | ||
| 1040 | static 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 | */ | ||
| 1054 | static 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. | ||
| 1080 | static 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. | ||
| 1129 | static 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 | |||
| 1167 | void 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 | |||
| 1240 | void SetServerPort(u16 port) { | ||
| 1241 | gdbstub_port = port; | ||
| 1242 | } | ||
| 1243 | |||
| 1244 | void 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 | |||
| 1262 | void DeferStart() { | ||
| 1263 | defer_start = true; | ||
| 1264 | } | ||
| 1265 | |||
| 1266 | static 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 | |||
| 1343 | void Init() { | ||
| 1344 | Init(gdbstub_port); | ||
| 1345 | } | ||
| 1346 | |||
| 1347 | void 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 | |||
| 1366 | bool IsServerEnabled() { | ||
| 1367 | return server_enabled; | ||
| 1368 | } | ||
| 1369 | |||
| 1370 | bool IsConnected() { | ||
| 1371 | return IsServerEnabled() && gdbserver_socket != -1; | ||
| 1372 | } | ||
| 1373 | |||
| 1374 | bool GetCpuHaltFlag() { | ||
| 1375 | return halt_loop; | ||
| 1376 | } | ||
| 1377 | |||
| 1378 | bool GetCpuStepFlag() { | ||
| 1379 | return step_loop; | ||
| 1380 | } | ||
| 1381 | |||
| 1382 | void SetCpuStepFlag(bool is_step) { | ||
| 1383 | step_loop = is_step; | ||
| 1384 | } | ||
| 1385 | |||
| 1386 | void 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 | |||
| 13 | namespace GDBStub { | ||
| 14 | |||
| 15 | /// Breakpoint Method | ||
| 16 | enum 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 | |||
| 24 | struct 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 | */ | ||
| 34 | void 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 | */ | ||
| 41 | void ToggleServer(bool status); | ||
| 42 | |||
| 43 | /// Start the gdbstub server. | ||
| 44 | void 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 | */ | ||
| 51 | void DeferStart(); | ||
| 52 | |||
| 53 | /// Stop gdbstub server. | ||
| 54 | void Shutdown(); | ||
| 55 | |||
| 56 | /// Checks if the gdbstub server is enabled. | ||
| 57 | bool IsServerEnabled(); | ||
| 58 | |||
| 59 | /// Returns true if there is an active socket connection. | ||
| 60 | bool IsConnected(); | ||
| 61 | |||
| 62 | /// Register module. | ||
| 63 | void 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 | */ | ||
| 70 | void Break(bool is_memory_break = false); | ||
| 71 | |||
| 72 | /// Determine if there was a memory breakpoint. | ||
| 73 | bool IsMemoryBreak(); | ||
| 74 | |||
| 75 | /// Read and handle packet from gdb client. | ||
| 76 | void 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 | */ | ||
| 84 | BreakpointAddress 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 | */ | ||
| 92 | bool CheckBreakpoint(VAddr addr, GDBStub::BreakpointType type); | ||
| 93 | |||
| 94 | /// If set to true, the CPU will halt at the beginning of the next CPU loop. | ||
| 95 | bool GetCpuHaltFlag(); | ||
| 96 | |||
| 97 | /// If set to true and the CPU is halted, the CPU will step one instruction. | ||
| 98 | bool 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 | */ | ||
| 105 | void 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 | */ | ||
| 113 | void 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 | ||
| 34 | void Apply() { | 35 | void 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 | ||
| 109 | void RestoreGlobalState() { | 106 | void 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 | ||
| 17 | namespace Core { | ||
| 18 | class System; | ||
| 19 | } | ||
| 20 | |||
| 17 | namespace Settings { | 21 | namespace Settings { |
| 18 | 22 | ||
| 19 | enum class RendererBackend { | 23 | enum class RendererBackend { |
| @@ -247,11 +251,11 @@ float Volume(); | |||
| 247 | 251 | ||
| 248 | std::string GetTimeZoneString(); | 252 | std::string GetTimeZoneString(); |
| 249 | 253 | ||
| 250 | void Apply(); | 254 | void Apply(Core::System& system); |
| 251 | void LogSettings(); | 255 | void 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 |
| 254 | void RestoreGlobalState(); | 258 | void 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 |
| 257 | void Sanitize(); | 261 | void 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 | ||
| 1604 | void Config::Save() { | 1605 | void 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 | ||
| 186 | void ConfigureProfileManager::SelectUser(const QModelIndex& index) { | 186 | void 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() { | |||
| 105 | void ConfigureSystem::ReadSystemSettings() {} | 105 | void ConfigureSystem::ReadSystemSettings() {} |
| 106 | 106 | ||
| 107 | void ConfigureSystem::ApplyConfiguration() { | 107 | void 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 | ||
| 203 | void ConfigureSystem::RefreshConsoleID() { | 205 | void 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 | ||
| 90 | void ConfigureUi::RequestGameListUpdate() { | 91 | void 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 | ||
| 2132 | void GMainWindow::OnStopGame() { | 2132 | void 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 | ||
| 2313 | void GMainWindow::OpenPerGameConfiguration(u64 title_id, const std::string& file_name) { | 2313 | void 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()); |