summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/citra/citra.cpp3
-rw-r--r--src/citra/config.cpp4
-rw-r--r--src/citra/default_ini.h5
-rw-r--r--src/citra_qt/config.cpp10
-rw-r--r--src/citra_qt/main.cpp11
-rw-r--r--src/citra_qt/main.h1
-rw-r--r--src/citra_qt/main.ui9
-rw-r--r--src/common/logging/backend.cpp1
-rw-r--r--src/common/logging/log.h1
-rw-r--r--src/core/CMakeLists.txt2
-rw-r--r--src/core/arm/dyncom/arm_dyncom_interpreter.cpp40
-rw-r--r--src/core/arm/skyeye_common/armstate.cpp35
-rw-r--r--src/core/arm/skyeye_common/armstate.h2
-rw-r--r--src/core/core.cpp17
-rw-r--r--src/core/gdbstub/gdbstub.cpp955
-rw-r--r--src/core/gdbstub/gdbstub.h94
-rw-r--r--src/core/settings.h5
-rw-r--r--src/core/system.cpp4
18 files changed, 1190 insertions, 9 deletions
diff --git a/src/citra/citra.cpp b/src/citra/citra.cpp
index 46f4a07c9..c96fc1374 100644
--- a/src/citra/citra.cpp
+++ b/src/citra/citra.cpp
@@ -23,6 +23,7 @@
23#include "core/settings.h" 23#include "core/settings.h"
24#include "core/system.h" 24#include "core/system.h"
25#include "core/core.h" 25#include "core/core.h"
26#include "core/gdbstub/gdbstub.h"
26#include "core/loader/loader.h" 27#include "core/loader/loader.h"
27 28
28#include "citra/config.h" 29#include "citra/config.h"
@@ -72,6 +73,8 @@ int main(int argc, char **argv) {
72 Config config; 73 Config config;
73 log_filter.ParseFilterString(Settings::values.log_filter); 74 log_filter.ParseFilterString(Settings::values.log_filter);
74 75
76 GDBStub::ToggleServer(Settings::values.use_gdbstub);
77 GDBStub::SetServerPort(static_cast<u32>(Settings::values.gdbstub_port));
75 78
76 EmuWindow_GLFW* emu_window = new EmuWindow_GLFW; 79 EmuWindow_GLFW* emu_window = new EmuWindow_GLFW;
77 80
diff --git a/src/citra/config.cpp b/src/citra/config.cpp
index 8a98bda87..2f13c29a2 100644
--- a/src/citra/config.cpp
+++ b/src/citra/config.cpp
@@ -75,6 +75,10 @@ void Config::ReadValues() {
75 75
76 // Miscellaneous 76 // Miscellaneous
77 Settings::values.log_filter = glfw_config->Get("Miscellaneous", "log_filter", "*:Info"); 77 Settings::values.log_filter = glfw_config->Get("Miscellaneous", "log_filter", "*:Info");
78
79 // Debugging
80 Settings::values.use_gdbstub = glfw_config->GetBoolean("Debugging", "use_gdbstub", false);
81 Settings::values.gdbstub_port = glfw_config->GetInteger("Debugging", "gdbstub_port", 24689);
78} 82}
79 83
80void Config::Reload() { 84void Config::Reload() {
diff --git a/src/citra/default_ini.h b/src/citra/default_ini.h
index 7e5d49729..5ba40a8ed 100644
--- a/src/citra/default_ini.h
+++ b/src/citra/default_ini.h
@@ -66,6 +66,11 @@ region_value =
66# A filter which removes logs below a certain logging level. 66# A filter which removes logs below a certain logging level.
67# Examples: *:Debug Kernel.SVC:Trace Service.*:Critical 67# Examples: *:Debug Kernel.SVC:Trace Service.*:Critical
68log_filter = *:Info 68log_filter = *:Info
69
70[Debugging]
71# Port for listening to GDB connections.
72use_gdbstub=false
73gdbstub_port=24689
69)"; 74)";
70 75
71} 76}
diff --git a/src/citra_qt/config.cpp b/src/citra_qt/config.cpp
index 1f4981ce1..8e247ff5c 100644
--- a/src/citra_qt/config.cpp
+++ b/src/citra_qt/config.cpp
@@ -62,6 +62,11 @@ void Config::ReadValues() {
62 qt_config->beginGroup("Miscellaneous"); 62 qt_config->beginGroup("Miscellaneous");
63 Settings::values.log_filter = qt_config->value("log_filter", "*:Info").toString().toStdString(); 63 Settings::values.log_filter = qt_config->value("log_filter", "*:Info").toString().toStdString();
64 qt_config->endGroup(); 64 qt_config->endGroup();
65
66 qt_config->beginGroup("Debugging");
67 Settings::values.use_gdbstub = qt_config->value("use_gdbstub", false).toBool();
68 Settings::values.gdbstub_port = qt_config->value("gdbstub_port", 24689).toInt();
69 qt_config->endGroup();
65} 70}
66 71
67void Config::SaveValues() { 72void Config::SaveValues() {
@@ -97,6 +102,11 @@ void Config::SaveValues() {
97 qt_config->beginGroup("Miscellaneous"); 102 qt_config->beginGroup("Miscellaneous");
98 qt_config->setValue("log_filter", QString::fromStdString(Settings::values.log_filter)); 103 qt_config->setValue("log_filter", QString::fromStdString(Settings::values.log_filter));
99 qt_config->endGroup(); 104 qt_config->endGroup();
105
106 qt_config->beginGroup("Debugging");
107 qt_config->setValue("use_gdbstub", Settings::values.use_gdbstub);
108 qt_config->setValue("gdbstub_port", Settings::values.gdbstub_port);
109 qt_config->endGroup();
100} 110}
101 111
102void Config::Reload() { 112void Config::Reload() {
diff --git a/src/citra_qt/main.cpp b/src/citra_qt/main.cpp
index bcff6be64..d6c27f0df 100644
--- a/src/citra_qt/main.cpp
+++ b/src/citra_qt/main.cpp
@@ -44,6 +44,7 @@
44#include "core/settings.h" 44#include "core/settings.h"
45#include "core/system.h" 45#include "core/system.h"
46#include "core/arm/disassembler/load_symbol_map.h" 46#include "core/arm/disassembler/load_symbol_map.h"
47#include "core/gdbstub/gdbstub.h"
47#include "core/loader/loader.h" 48#include "core/loader/loader.h"
48 49
49#include "video_core/video_core.h" 50#include "video_core/video_core.h"
@@ -143,6 +144,11 @@ GMainWindow::GMainWindow() : emu_thread(nullptr)
143 144
144 game_list->LoadInterfaceLayout(settings); 145 game_list->LoadInterfaceLayout(settings);
145 146
147 ui.action_Use_Gdbstub->setChecked(Settings::values.use_gdbstub);
148 SetGdbstubEnabled(ui.action_Use_Gdbstub->isChecked());
149
150 GDBStub::SetServerPort(static_cast<u32>(Settings::values.gdbstub_port));
151
146 ui.action_Use_Hardware_Renderer->setChecked(Settings::values.use_hw_renderer); 152 ui.action_Use_Hardware_Renderer->setChecked(Settings::values.use_hw_renderer);
147 SetHardwareRendererEnabled(ui.action_Use_Hardware_Renderer->isChecked()); 153 SetHardwareRendererEnabled(ui.action_Use_Hardware_Renderer->isChecked());
148 154
@@ -175,6 +181,7 @@ GMainWindow::GMainWindow() : emu_thread(nullptr)
175 connect(ui.action_Stop, SIGNAL(triggered()), this, SLOT(OnStopGame())); 181 connect(ui.action_Stop, SIGNAL(triggered()), this, SLOT(OnStopGame()));
176 connect(ui.action_Use_Hardware_Renderer, SIGNAL(triggered(bool)), this, SLOT(SetHardwareRendererEnabled(bool))); 182 connect(ui.action_Use_Hardware_Renderer, SIGNAL(triggered(bool)), this, SLOT(SetHardwareRendererEnabled(bool)));
177 connect(ui.action_Use_Shader_JIT, SIGNAL(triggered(bool)), this, SLOT(SetShaderJITEnabled(bool))); 183 connect(ui.action_Use_Shader_JIT, SIGNAL(triggered(bool)), this, SLOT(SetShaderJITEnabled(bool)));
184 connect(ui.action_Use_Gdbstub, SIGNAL(triggered(bool)), this, SLOT(SetGdbstubEnabled(bool)));
178 connect(ui.action_Single_Window_Mode, SIGNAL(triggered(bool)), this, SLOT(ToggleWindowMode())); 185 connect(ui.action_Single_Window_Mode, SIGNAL(triggered(bool)), this, SLOT(ToggleWindowMode()));
179 connect(ui.action_Hotkeys, SIGNAL(triggered()), this, SLOT(OnOpenHotkeysDialog())); 186 connect(ui.action_Hotkeys, SIGNAL(triggered()), this, SLOT(OnOpenHotkeysDialog()));
180 187
@@ -449,6 +456,10 @@ void GMainWindow::SetHardwareRendererEnabled(bool enabled) {
449 config.Save(); 456 config.Save();
450} 457}
451 458
459void GMainWindow::SetGdbstubEnabled(bool enabled) {
460 GDBStub::ToggleServer(enabled);
461}
462
452void GMainWindow::SetShaderJITEnabled(bool enabled) { 463void GMainWindow::SetShaderJITEnabled(bool enabled) {
453 VideoCore::g_shader_jit_enabled = enabled; 464 VideoCore::g_shader_jit_enabled = enabled;
454 465
diff --git a/src/citra_qt/main.h b/src/citra_qt/main.h
index 6d27ce6a9..f6d429cd9 100644
--- a/src/citra_qt/main.h
+++ b/src/citra_qt/main.h
@@ -99,6 +99,7 @@ private slots:
99 void OnConfigure(); 99 void OnConfigure();
100 void OnDisplayTitleBars(bool); 100 void OnDisplayTitleBars(bool);
101 void SetHardwareRendererEnabled(bool); 101 void SetHardwareRendererEnabled(bool);
102 void SetGdbstubEnabled(bool);
102 void SetShaderJITEnabled(bool); 103 void SetShaderJITEnabled(bool);
103 void ToggleWindowMode(); 104 void ToggleWindowMode();
104 105
diff --git a/src/citra_qt/main.ui b/src/citra_qt/main.ui
index 997597642..1e8a07cfb 100644
--- a/src/citra_qt/main.ui
+++ b/src/citra_qt/main.ui
@@ -75,6 +75,7 @@
75 <addaction name="separator"/> 75 <addaction name="separator"/>
76 <addaction name="action_Use_Hardware_Renderer"/> 76 <addaction name="action_Use_Hardware_Renderer"/>
77 <addaction name="action_Use_Shader_JIT"/> 77 <addaction name="action_Use_Shader_JIT"/>
78 <addaction name="action_Use_Gdbstub"/>
78 <addaction name="action_Configure"/> 79 <addaction name="action_Configure"/>
79 </widget> 80 </widget>
80 <widget class="QMenu" name="menu_View"> 81 <widget class="QMenu" name="menu_View">
@@ -170,6 +171,14 @@
170 <string>Use Shader JIT</string> 171 <string>Use Shader JIT</string>
171 </property> 172 </property>
172 </action> 173 </action>
174 <action name="action_Use_Gdbstub">
175 <property name="checkable">
176 <bool>true</bool>
177 </property>
178 <property name="text">
179 <string>Use Gdbstub</string>
180 </property>
181 </action>
173 <action name="action_Configure"> 182 <action name="action_Configure">
174 <property name="text"> 183 <property name="text">
175 <string>Configure ...</string> 184 <string>Configure ...</string>
diff --git a/src/common/logging/backend.cpp b/src/common/logging/backend.cpp
index 92e8e742d..21a9ae8d0 100644
--- a/src/common/logging/backend.cpp
+++ b/src/common/logging/backend.cpp
@@ -29,6 +29,7 @@ namespace Log {
29 SUB(Debug, Emulated) \ 29 SUB(Debug, Emulated) \
30 SUB(Debug, GPU) \ 30 SUB(Debug, GPU) \
31 SUB(Debug, Breakpoint) \ 31 SUB(Debug, Breakpoint) \
32 SUB(Debug, GDBStub) \
32 CLS(Kernel) \ 33 CLS(Kernel) \
33 SUB(Kernel, SVC) \ 34 SUB(Kernel, SVC) \
34 CLS(Service) \ 35 CLS(Service) \
diff --git a/src/common/logging/log.h b/src/common/logging/log.h
index 5fd3bd7f5..43f0c59e4 100644
--- a/src/common/logging/log.h
+++ b/src/common/logging/log.h
@@ -43,6 +43,7 @@ enum class Class : ClassType {
43 Debug_Emulated, ///< Debug messages from the emulated programs 43 Debug_Emulated, ///< Debug messages from the emulated programs
44 Debug_GPU, ///< GPU debugging tools 44 Debug_GPU, ///< GPU debugging tools
45 Debug_Breakpoint, ///< Logging breakpoints and watchpoints 45 Debug_Breakpoint, ///< Logging breakpoints and watchpoints
46 Debug_GDBStub, ///< GDB Stub
46 Kernel, ///< The HLE implementation of the CTR kernel 47 Kernel, ///< The HLE implementation of the CTR kernel
47 Kernel_SVC, ///< Kernel system calls 48 Kernel_SVC, ///< Kernel system calls
48 Service, ///< HLE implementation of system services. Each major service 49 Service, ///< HLE implementation of system services. Each major service
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index c17290b9b..861b711c7 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -22,6 +22,7 @@ set(SRCS
22 file_sys/archive_systemsavedata.cpp 22 file_sys/archive_systemsavedata.cpp
23 file_sys/disk_archive.cpp 23 file_sys/disk_archive.cpp
24 file_sys/ivfc_archive.cpp 24 file_sys/ivfc_archive.cpp
25 gdbstub/gdbstub.cpp
25 hle/config_mem.cpp 26 hle/config_mem.cpp
26 hle/hle.cpp 27 hle/hle.cpp
27 hle/applets/applet.cpp 28 hle/applets/applet.cpp
@@ -149,6 +150,7 @@ set(HEADERS
149 file_sys/disk_archive.h 150 file_sys/disk_archive.h
150 file_sys/file_backend.h 151 file_sys/file_backend.h
151 file_sys/ivfc_archive.h 152 file_sys/ivfc_archive.h
153 gdbstub/gdbstub.h
152 hle/config_mem.h 154 hle/config_mem.h
153 hle/function_wrappers.h 155 hle/function_wrappers.h
154 hle/hle.h 156 hle/hle.h
diff --git a/src/core/arm/dyncom/arm_dyncom_interpreter.cpp b/src/core/arm/dyncom/arm_dyncom_interpreter.cpp
index fbd6f94f9..96c88c83a 100644
--- a/src/core/arm/dyncom/arm_dyncom_interpreter.cpp
+++ b/src/core/arm/dyncom/arm_dyncom_interpreter.cpp
@@ -23,6 +23,8 @@
23#include "core/arm/skyeye_common/armsupp.h" 23#include "core/arm/skyeye_common/armsupp.h"
24#include "core/arm/skyeye_common/vfp/vfp.h" 24#include "core/arm/skyeye_common/vfp/vfp.h"
25 25
26#include "core/gdbstub/gdbstub.h"
27
26Common::Profiling::TimingCategory profile_execute("DynCom::Execute"); 28Common::Profiling::TimingCategory profile_execute("DynCom::Execute");
27Common::Profiling::TimingCategory profile_decode("DynCom::Decode"); 29Common::Profiling::TimingCategory profile_decode("DynCom::Decode");
28 30
@@ -3548,6 +3550,7 @@ static int InterpreterTranslate(ARMul_State* cpu, int& bb_start, u32 addr) {
3548 CITRA_IGNORE_EXIT(-1); 3550 CITRA_IGNORE_EXIT(-1);
3549 } 3551 }
3550 inst_base = arm_instruction_trans[idx](inst, idx); 3552 inst_base = arm_instruction_trans[idx](inst, idx);
3553
3551translated: 3554translated:
3552 phys_addr += inst_size; 3555 phys_addr += inst_size;
3553 3556
@@ -3580,6 +3583,8 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) {
3580 Common::Profiling::ScopeTimer timer_execute(profile_execute); 3583 Common::Profiling::ScopeTimer timer_execute(profile_execute);
3581 MICROPROFILE_SCOPE(DynCom_Execute); 3584 MICROPROFILE_SCOPE(DynCom_Execute);
3582 3585
3586 GDBStub::BreakpointAddress breakpoint_data;
3587
3583 #undef RM 3588 #undef RM
3584 #undef RS 3589 #undef RS
3585 3590
@@ -3604,15 +3609,27 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) {
3604 #define INC_PC(l) ptr += sizeof(arm_inst) + l 3609 #define INC_PC(l) ptr += sizeof(arm_inst) + l
3605 #define INC_PC_STUB ptr += sizeof(arm_inst) 3610 #define INC_PC_STUB ptr += sizeof(arm_inst)
3606 3611
3612#define GDB_BP_CHECK \
3613 cpu->Cpsr &= ~(1 << 5); \
3614 cpu->Cpsr |= cpu->TFlag << 5; \
3615 if (GDBStub::g_server_enabled) { \
3616 if (GDBStub::IsMemoryBreak() || (breakpoint_data.type != GDBStub::BreakpointType::None && PC == breakpoint_data.address)) { \
3617 GDBStub::Break(); \
3618 goto END; \
3619 } \
3620 }
3621
3607// GCC and Clang have a C++ extension to support a lookup table of labels. Otherwise, fallback to a 3622// GCC and Clang have a C++ extension to support a lookup table of labels. Otherwise, fallback to a
3608// clunky switch statement. 3623// clunky switch statement.
3609#if defined __GNUC__ || defined __clang__ 3624#if defined __GNUC__ || defined __clang__
3610#define GOTO_NEXT_INST \ 3625#define GOTO_NEXT_INST \
3626 GDB_BP_CHECK; \
3611 if (num_instrs >= cpu->NumInstrsToExecute) goto END; \ 3627 if (num_instrs >= cpu->NumInstrsToExecute) goto END; \
3612 num_instrs++; \ 3628 num_instrs++; \
3613 goto *InstLabel[inst_base->idx] 3629 goto *InstLabel[inst_base->idx]
3614#else 3630#else
3615#define GOTO_NEXT_INST \ 3631#define GOTO_NEXT_INST \
3632 GDB_BP_CHECK; \
3616 if (num_instrs >= cpu->NumInstrsToExecute) goto END; \ 3633 if (num_instrs >= cpu->NumInstrsToExecute) goto END; \
3617 num_instrs++; \ 3634 num_instrs++; \
3618 switch(inst_base->idx) { \ 3635 switch(inst_base->idx) { \
@@ -3903,6 +3920,11 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) {
3903 goto END; 3920 goto END;
3904 } 3921 }
3905 3922
3923 // Find breakpoint if one exists within the block
3924 if (GDBStub::g_server_enabled && GDBStub::IsConnected()) {
3925 breakpoint_data = GDBStub::GetNextBreakpointFromAddress(cpu->Reg[15], GDBStub::BreakpointType::Execute);
3926 }
3927
3906 inst_base = (arm_inst *)&inst_buf[ptr]; 3928 inst_base = (arm_inst *)&inst_buf[ptr];
3907 GOTO_NEXT_INST; 3929 GOTO_NEXT_INST;
3908 } 3930 }
@@ -4454,7 +4476,7 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) {
4454 ldst_inst* inst_cream = (ldst_inst*)inst_base->component; 4476 ldst_inst* inst_cream = (ldst_inst*)inst_base->component;
4455 inst_cream->get_addr(cpu, inst_cream->inst, addr); 4477 inst_cream->get_addr(cpu, inst_cream->inst, addr);
4456 4478
4457 cpu->Reg[BITS(inst_cream->inst, 12, 15)] = Memory::Read8(addr); 4479 cpu->Reg[BITS(inst_cream->inst, 12, 15)] = cpu->ReadMemory8(addr);
4458 4480
4459 if (BITS(inst_cream->inst, 12, 15) == 15) { 4481 if (BITS(inst_cream->inst, 12, 15) == 15) {
4460 INC_PC(sizeof(ldst_inst)); 4482 INC_PC(sizeof(ldst_inst));
@@ -4472,7 +4494,7 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) {
4472 ldst_inst* inst_cream = (ldst_inst*)inst_base->component; 4494 ldst_inst* inst_cream = (ldst_inst*)inst_base->component;
4473 inst_cream->get_addr(cpu, inst_cream->inst, addr); 4495 inst_cream->get_addr(cpu, inst_cream->inst, addr);
4474 4496
4475 cpu->Reg[BITS(inst_cream->inst, 12, 15)] = Memory::Read8(addr); 4497 cpu->Reg[BITS(inst_cream->inst, 12, 15)] = cpu->ReadMemory8(addr);
4476 4498
4477 if (BITS(inst_cream->inst, 12, 15) == 15) { 4499 if (BITS(inst_cream->inst, 12, 15) == 15) {
4478 INC_PC(sizeof(ldst_inst)); 4500 INC_PC(sizeof(ldst_inst));
@@ -4531,7 +4553,7 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) {
4531 4553
4532 cpu->SetExclusiveMemoryAddress(read_addr); 4554 cpu->SetExclusiveMemoryAddress(read_addr);
4533 4555
4534 RD = Memory::Read8(read_addr); 4556 RD = cpu->ReadMemory8(read_addr);
4535 if (inst_cream->Rd == 15) { 4557 if (inst_cream->Rd == 15) {
4536 INC_PC(sizeof(generic_arm_inst)); 4558 INC_PC(sizeof(generic_arm_inst));
4537 goto DISPATCH; 4559 goto DISPATCH;
@@ -4604,7 +4626,7 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) {
4604 if (inst_base->cond == ConditionCode::AL || CondPassed(cpu, inst_base->cond)) { 4626 if (inst_base->cond == ConditionCode::AL || CondPassed(cpu, inst_base->cond)) {
4605 ldst_inst* inst_cream = (ldst_inst*)inst_base->component; 4627 ldst_inst* inst_cream = (ldst_inst*)inst_base->component;
4606 inst_cream->get_addr(cpu, inst_cream->inst, addr); 4628 inst_cream->get_addr(cpu, inst_cream->inst, addr);
4607 unsigned int value = Memory::Read8(addr); 4629 unsigned int value = cpu->ReadMemory8(addr);
4608 if (BIT(value, 7)) { 4630 if (BIT(value, 7)) {
4609 value |= 0xffffff00; 4631 value |= 0xffffff00;
4610 } 4632 }
@@ -6027,7 +6049,7 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) {
6027 ldst_inst* inst_cream = (ldst_inst*)inst_base->component; 6049 ldst_inst* inst_cream = (ldst_inst*)inst_base->component;
6028 inst_cream->get_addr(cpu, inst_cream->inst, addr); 6050 inst_cream->get_addr(cpu, inst_cream->inst, addr);
6029 unsigned int value = cpu->Reg[BITS(inst_cream->inst, 12, 15)] & 0xff; 6051 unsigned int value = cpu->Reg[BITS(inst_cream->inst, 12, 15)] & 0xff;
6030 Memory::Write8(addr, value); 6052 cpu->WriteMemory8(addr, value);
6031 } 6053 }
6032 cpu->Reg[15] += cpu->GetInstructionSize(); 6054 cpu->Reg[15] += cpu->GetInstructionSize();
6033 INC_PC(sizeof(ldst_inst)); 6055 INC_PC(sizeof(ldst_inst));
@@ -6040,7 +6062,7 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) {
6040 ldst_inst* inst_cream = (ldst_inst*)inst_base->component; 6062 ldst_inst* inst_cream = (ldst_inst*)inst_base->component;
6041 inst_cream->get_addr(cpu, inst_cream->inst, addr); 6063 inst_cream->get_addr(cpu, inst_cream->inst, addr);
6042 unsigned int value = cpu->Reg[BITS(inst_cream->inst, 12, 15)] & 0xff; 6064 unsigned int value = cpu->Reg[BITS(inst_cream->inst, 12, 15)] & 0xff;
6043 Memory::Write8(addr, value); 6065 cpu->WriteMemory8(addr, value);
6044 } 6066 }
6045 cpu->Reg[15] += cpu->GetInstructionSize(); 6067 cpu->Reg[15] += cpu->GetInstructionSize();
6046 INC_PC(sizeof(ldst_inst)); 6068 INC_PC(sizeof(ldst_inst));
@@ -6091,7 +6113,7 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) {
6091 6113
6092 if (cpu->IsExclusiveMemoryAccess(write_addr)) { 6114 if (cpu->IsExclusiveMemoryAccess(write_addr)) {
6093 cpu->UnsetExclusiveMemoryAddress(); 6115 cpu->UnsetExclusiveMemoryAddress();
6094 Memory::Write8(write_addr, cpu->Reg[inst_cream->Rm]); 6116 cpu->WriteMemory8(write_addr, cpu->Reg[inst_cream->Rm]);
6095 RD = 0; 6117 RD = 0;
6096 } else { 6118 } else {
6097 // Failed to write due to mutex access 6119 // Failed to write due to mutex access
@@ -6250,8 +6272,8 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) {
6250 if (inst_base->cond == ConditionCode::AL || CondPassed(cpu, inst_base->cond)) { 6272 if (inst_base->cond == ConditionCode::AL || CondPassed(cpu, inst_base->cond)) {
6251 swp_inst* inst_cream = (swp_inst*)inst_base->component; 6273 swp_inst* inst_cream = (swp_inst*)inst_base->component;
6252 addr = RN; 6274 addr = RN;
6253 unsigned int value = Memory::Read8(addr); 6275 unsigned int value = cpu->ReadMemory8(addr);
6254 Memory::Write8(addr, (RM & 0xFF)); 6276 cpu->WriteMemory8(addr, (RM & 0xFF));
6255 RD = value; 6277 RD = value;
6256 } 6278 }
6257 cpu->Reg[15] += cpu->GetInstructionSize(); 6279 cpu->Reg[15] += cpu->GetInstructionSize();
diff --git a/src/core/arm/skyeye_common/armstate.cpp b/src/core/arm/skyeye_common/armstate.cpp
index 0491717dc..2d814345a 100644
--- a/src/core/arm/skyeye_common/armstate.cpp
+++ b/src/core/arm/skyeye_common/armstate.cpp
@@ -7,6 +7,7 @@
7#include "core/memory.h" 7#include "core/memory.h"
8#include "core/arm/skyeye_common/armstate.h" 8#include "core/arm/skyeye_common/armstate.h"
9#include "core/arm/skyeye_common/vfp/vfp.h" 9#include "core/arm/skyeye_common/vfp/vfp.h"
10#include "core/gdbstub/gdbstub.h"
10 11
11ARMul_State::ARMul_State(PrivilegeMode initial_mode) 12ARMul_State::ARMul_State(PrivilegeMode initial_mode)
12{ 13{
@@ -185,8 +186,25 @@ void ARMul_State::ResetMPCoreCP15Registers()
185 CP15[CP15_TLB_DEBUG_CONTROL] = 0x00000000; 186 CP15[CP15_TLB_DEBUG_CONTROL] = 0x00000000;
186} 187}
187 188
189static void CheckMemoryBreakpoint(u32 address, GDBStub::BreakpointType type)
190{
191 if (GDBStub::g_server_enabled && GDBStub::CheckBreakpoint(address, type)) {
192 LOG_DEBUG(Debug, "Found memory breakpoint @ %08x", address);
193 GDBStub::Break(true);
194 }
195}
196
197u8 ARMul_State::ReadMemory8(u32 address) const
198{
199 CheckMemoryBreakpoint(address, GDBStub::BreakpointType::Read);
200
201 return Memory::Read8(address);
202}
203
188u16 ARMul_State::ReadMemory16(u32 address) const 204u16 ARMul_State::ReadMemory16(u32 address) const
189{ 205{
206 CheckMemoryBreakpoint(address, GDBStub::BreakpointType::Read);
207
190 u16 data = Memory::Read16(address); 208 u16 data = Memory::Read16(address);
191 209
192 if (InBigEndianMode()) 210 if (InBigEndianMode())
@@ -197,6 +215,8 @@ u16 ARMul_State::ReadMemory16(u32 address) const
197 215
198u32 ARMul_State::ReadMemory32(u32 address) const 216u32 ARMul_State::ReadMemory32(u32 address) const
199{ 217{
218 CheckMemoryBreakpoint(address, GDBStub::BreakpointType::Read);
219
200 u32 data = Memory::Read32(address); 220 u32 data = Memory::Read32(address);
201 221
202 if (InBigEndianMode()) 222 if (InBigEndianMode())
@@ -207,6 +227,8 @@ u32 ARMul_State::ReadMemory32(u32 address) const
207 227
208u64 ARMul_State::ReadMemory64(u32 address) const 228u64 ARMul_State::ReadMemory64(u32 address) const
209{ 229{
230 CheckMemoryBreakpoint(address, GDBStub::BreakpointType::Read);
231
210 u64 data = Memory::Read64(address); 232 u64 data = Memory::Read64(address);
211 233
212 if (InBigEndianMode()) 234 if (InBigEndianMode())
@@ -215,8 +237,17 @@ u64 ARMul_State::ReadMemory64(u32 address) const
215 return data; 237 return data;
216} 238}
217 239
240void ARMul_State::WriteMemory8(u32 address, u8 data)
241{
242 CheckMemoryBreakpoint(address, GDBStub::BreakpointType::Write);
243
244 Memory::Write8(address, data);
245}
246
218void ARMul_State::WriteMemory16(u32 address, u16 data) 247void ARMul_State::WriteMemory16(u32 address, u16 data)
219{ 248{
249 CheckMemoryBreakpoint(address, GDBStub::BreakpointType::Write);
250
220 if (InBigEndianMode()) 251 if (InBigEndianMode())
221 data = Common::swap16(data); 252 data = Common::swap16(data);
222 253
@@ -225,6 +256,8 @@ void ARMul_State::WriteMemory16(u32 address, u16 data)
225 256
226void ARMul_State::WriteMemory32(u32 address, u32 data) 257void ARMul_State::WriteMemory32(u32 address, u32 data)
227{ 258{
259 CheckMemoryBreakpoint(address, GDBStub::BreakpointType::Write);
260
228 if (InBigEndianMode()) 261 if (InBigEndianMode())
229 data = Common::swap32(data); 262 data = Common::swap32(data);
230 263
@@ -233,6 +266,8 @@ void ARMul_State::WriteMemory32(u32 address, u32 data)
233 266
234void ARMul_State::WriteMemory64(u32 address, u64 data) 267void ARMul_State::WriteMemory64(u32 address, u64 data)
235{ 268{
269 CheckMemoryBreakpoint(address, GDBStub::BreakpointType::Write);
270
236 if (InBigEndianMode()) 271 if (InBigEndianMode())
237 data = Common::swap64(data); 272 data = Common::swap64(data);
238 273
diff --git a/src/core/arm/skyeye_common/armstate.h b/src/core/arm/skyeye_common/armstate.h
index ceb159d14..98dad9b1f 100644
--- a/src/core/arm/skyeye_common/armstate.h
+++ b/src/core/arm/skyeye_common/armstate.h
@@ -153,9 +153,11 @@ public:
153 153
154 // Reads/writes data in big/little endian format based on the 154 // Reads/writes data in big/little endian format based on the
155 // state of the E (endian) bit in the APSR. 155 // state of the E (endian) bit in the APSR.
156 u8 ReadMemory8(u32 address) const;
156 u16 ReadMemory16(u32 address) const; 157 u16 ReadMemory16(u32 address) const;
157 u32 ReadMemory32(u32 address) const; 158 u32 ReadMemory32(u32 address) const;
158 u64 ReadMemory64(u32 address) const; 159 u64 ReadMemory64(u32 address) const;
160 void WriteMemory8(u32 address, u8 data);
159 void WriteMemory16(u32 address, u16 data); 161 void WriteMemory16(u32 address, u16 data);
160 void WriteMemory32(u32 address, u32 data); 162 void WriteMemory32(u32 address, u32 data);
161 void WriteMemory64(u32 address, u64 data); 163 void WriteMemory64(u32 address, u64 data);
diff --git a/src/core/core.cpp b/src/core/core.cpp
index dddc16708..219b03af4 100644
--- a/src/core/core.cpp
+++ b/src/core/core.cpp
@@ -13,6 +13,8 @@
13#include "core/hle/kernel/thread.h" 13#include "core/hle/kernel/thread.h"
14#include "core/hw/hw.h" 14#include "core/hw/hw.h"
15 15
16#include "core/gdbstub/gdbstub.h"
17
16namespace Core { 18namespace Core {
17 19
18ARM_Interface* g_app_core = nullptr; ///< ARM11 application core 20ARM_Interface* g_app_core = nullptr; ///< ARM11 application core
@@ -20,6 +22,21 @@ ARM_Interface* g_sys_core = nullptr; ///< ARM11 system (OS) core
20 22
21/// Run the core CPU loop 23/// Run the core CPU loop
22void RunLoop(int tight_loop) { 24void RunLoop(int tight_loop) {
25 if (GDBStub::g_server_enabled) {
26 GDBStub::HandlePacket();
27
28 // If the loop is halted and we want to step, use a tiny (1) number of instructions to execute.
29 // Otherwise get out of the loop function.
30 if (GDBStub::GetCpuHaltFlag()) {
31 if (GDBStub::GetCpuStepFlag()) {
32 GDBStub::SetCpuStepFlag(false);
33 tight_loop = 1;
34 } else {
35 return;
36 }
37 }
38 }
39
23 // If we don't have a currently active thread then don't execute instructions, 40 // If we don't have a currently active thread then don't execute instructions,
24 // instead advance to the next event and try to yield to the next thread 41 // instead advance to the next event and try to yield to the next thread
25 if (Kernel::GetCurrentThread() == nullptr) { 42 if (Kernel::GetCurrentThread() == nullptr) {
diff --git a/src/core/gdbstub/gdbstub.cpp b/src/core/gdbstub/gdbstub.cpp
new file mode 100644
index 000000000..003ce4167
--- /dev/null
+++ b/src/core/gdbstub/gdbstub.cpp
@@ -0,0 +1,955 @@
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 <climits>
9#include <csignal>
10#include <cstdarg>
11#include <cstdio>
12#include <cstring>
13#include <fcntl.h>
14#include <map>
15#include <numeric>
16
17#ifdef _MSC_VER
18#include <WinSock2.h>
19#include <ws2tcpip.h>
20#include <common/x64/abi.h>
21#include <io.h>
22#include <iphlpapi.h>
23#define SHUT_RDWR 2
24#else
25#include <unistd.h>
26#include <sys/select.h>
27#include <sys/socket.h>
28#include <sys/un.h>
29#include <netinet/in.h>
30#endif
31
32#include "common/logging/log.h"
33#include "common/string_util.h"
34#include "core/core.h"
35#include "core/memory.h"
36#include "core/arm/arm_interface.h"
37#include "core/gdbstub/gdbstub.h"
38
39const int GDB_BUFFER_SIZE = 10000;
40
41const char GDB_STUB_START = '$';
42const char GDB_STUB_END = '#';
43const char GDB_STUB_ACK = '+';
44const char GDB_STUB_NACK = '-';
45
46#ifndef SIGTRAP
47const u32 SIGTRAP = 5;
48#endif
49
50#ifndef SIGTERM
51const u32 SIGTERM = 15;
52#endif
53
54#ifndef MSG_WAITALL
55const u32 MSG_WAITALL = 8;
56#endif
57
58const u32 R0_REGISTER = 0;
59const u32 R15_REGISTER = 15;
60const u32 CSPR_REGISTER = 25;
61const u32 FPSCR_REGISTER = 58;
62const u32 MAX_REGISTERS = 90;
63
64namespace GDBStub {
65
66static int gdbserver_socket = -1;
67
68static u8 command_buffer[GDB_BUFFER_SIZE];
69static u32 command_length;
70
71static u32 latest_signal = 0;
72static bool step_break = false;
73static bool memory_break = false;
74
75// Binding to a port within the reserved ports range (0-1023) requires root permissions,
76// so default to a port outside of that range.
77static u16 gdbstub_port = 24689;
78
79static bool halt_loop = true;
80static bool step_loop = false;
81std::atomic<bool> g_server_enabled(false);
82
83#ifdef _WIN32
84WSADATA InitData;
85#endif
86
87struct Breakpoint {
88 bool active;
89 PAddr addr;
90 u32 len;
91};
92
93static std::map<u32, Breakpoint> breakpoints_execute;
94static std::map<u32, Breakpoint> breakpoints_read;
95static std::map<u32, Breakpoint> breakpoints_write;
96
97/**
98 * Turns hex string character into the equivalent byte.
99 *
100 * @param hex Input hex character to be turned into byte.
101 */
102static u8 HexCharToValue(u8 hex) {
103 if (hex >= '0' && hex <= '9') {
104 return hex - '0';
105 } else if (hex >= 'a' && hex <= 'f') {
106 return hex - 'a' + 0xA;
107 } else if (hex >= 'A' && hex <= 'F') {
108 return hex - 'A' + 0xA;
109 }
110
111 LOG_ERROR(Debug_GDBStub, "Invalid nibble: %c (%02x)\n", hex, hex);
112 return 0;
113}
114
115/**
116 * Turn nibble of byte into hex string character.
117 *
118 * @param n Nibble to be turned into hex character.
119 */
120static u8 NibbleToHex(u8 n) {
121 n &= 0xF;
122 if (n < 0xA) {
123 return '0' + n;
124 } else {
125 return 'A' + n - 0xA;
126 }
127}
128
129/**
130* Converts input hex string characters into an array of equivalent of u8 bytes.
131*
132* @param dest Pointer to buffer to store u8 bytes.
133* @param src Pointer to array of output hex string characters.
134* @param len Length of src array.
135*/
136static u32 HexToInt(u8* src, u32 len) {
137 u32 output = 0;
138 while (len-- > 0) {
139 output = (output << 4) | HexCharToValue(src[0]);
140 src++;
141 }
142 return output;
143}
144
145/**
146 * Converts input array of u8 bytes into their equivalent hex string characters.
147 *
148 * @param dest Pointer to buffer to store output hex string characters.
149 * @param src Pointer to array of u8 bytes.
150 * @param len Length of src array.
151 */
152static void MemToGdbHex(u8* dest, u8* src, u32 len) {
153 while (len-- > 0) {
154 u8 tmp = *src++;
155 *dest++ = NibbleToHex(tmp >> 4);
156 *dest++ = NibbleToHex(tmp);
157 }
158}
159
160/**
161 * Converts input gdb-formatted hex string characters into an array of equivalent of u8 bytes.
162 *
163 * @param dest Pointer to buffer to store u8 bytes.
164 * @param src Pointer to array of output hex string characters.
165 * @param len Length of src array.
166 */
167static void GdbHexToMem(u8* dest, u8* src, u32 len) {
168 while (len-- > 0) {
169 *dest++ = (HexCharToValue(src[0]) << 4) | HexCharToValue(src[1]);
170 src += 2;
171 }
172}
173
174/**
175 * Convert a u32 into a gdb-formatted hex string.
176 *
177 * @param dest Pointer to buffer to store output hex string characters.
178 */
179static void IntToGdbHex(u8* dest, u32 v) {
180 for (int i = 0; i < 8; i += 2) {
181 dest[i + 1] = NibbleToHex(v >> (4 * i));
182 dest[i] = NibbleToHex(v >> (4 * (i + 1)));
183 }
184}
185
186/**
187 * Convert a gdb-formatted hex string into a u32.
188 *
189 * @param src Pointer to hex string.
190 */
191static u32 GdbHexToInt(u8* src) {
192 u32 output = 0;
193
194 for (int i = 0; i < 8; i += 2) {
195 output = (output << 4) | HexCharToValue(src[7 - i - 1]);
196 output = (output << 4) | HexCharToValue(src[7 - i]);
197 }
198
199 return output;
200}
201
202/// Read a byte from the gdb client.
203static u8 ReadByte() {
204 u8 c;
205 size_t received_size = recv(gdbserver_socket, reinterpret_cast<char*>(&c), 1, MSG_WAITALL);
206 if (received_size != 1) {
207 LOG_ERROR(Debug_GDBStub, "recv failed : %ld", received_size);
208 Shutdown();
209 }
210
211 return c;
212}
213
214/// Calculate the checksum of the current command buffer.
215static u8 CalculateChecksum(u8 *buffer, u32 length) {
216 return static_cast<u8>(std::accumulate(buffer, buffer + length, 0, std::plus<u8>()));
217}
218
219/**
220 * Get the list of breakpoints for a given breakpoint type.
221 *
222 * @param type Type of breakpoint list.
223 */
224static std::map<u32, Breakpoint>& GetBreakpointList(BreakpointType type) {
225 switch (type) {
226 case BreakpointType::Execute:
227 return breakpoints_execute;
228 case BreakpointType::Read:
229 return breakpoints_read;
230 case BreakpointType::Write:
231 return breakpoints_write;
232 default:
233 return breakpoints_read;
234 }
235}
236
237/**
238 * Remove the breakpoint from the given address of the specified type.
239 *
240 * @param type Type of breakpoint.
241 * @param addr Address of breakpoint.
242 */
243static void RemoveBreakpoint(BreakpointType type, PAddr addr) {
244 std::map<u32, Breakpoint>& p = GetBreakpointList(type);
245
246 auto bp = p.find(addr);
247 if (bp != p.end()) {
248 LOG_DEBUG(Debug_GDBStub, "gdb: removed a breakpoint: %08x bytes at %08x of type %d\n", bp->second.len, bp->second.addr, type);
249 p.erase(addr);
250 }
251}
252
253BreakpointAddress GetNextBreakpointFromAddress(PAddr addr, BreakpointType type) {
254 std::map<u32, Breakpoint>& p = GetBreakpointList(type);
255 auto next_breakpoint = p.lower_bound(addr);
256 BreakpointAddress breakpoint;
257
258 if (next_breakpoint != p.end()) {
259 breakpoint.address = next_breakpoint->first;
260 breakpoint.type = type;
261 } else {
262 breakpoint.address = 0;
263 breakpoint.type = BreakpointType::None;
264 }
265
266 return breakpoint;
267}
268
269bool CheckBreakpoint(PAddr addr, BreakpointType type) {
270 if (!IsConnected()) {
271 return false;
272 }
273
274 std::map<u32, Breakpoint>& p = GetBreakpointList(type);
275
276 auto bp = p.find(addr);
277 if (bp != p.end()) {
278 u32 len = bp->second.len;
279
280 // IDA Pro defaults to 4-byte breakpoints for all non-hardware breakpoints
281 // no matter if it's a 4-byte or 2-byte instruction. When you execute a
282 // Thumb instruction with a 4-byte breakpoint set, it will set a breakpoint on
283 // two instructions instead of the single instruction you placed the breakpoint
284 // on. So, as a way to make sure that execution breakpoints are only breaking
285 // on the instruction that was specified, set the length of an execution
286 // breakpoint to 1. This should be fine since the CPU should never begin executing
287 // an instruction anywhere except the beginning of the instruction.
288 if (type == BreakpointType::Execute) {
289 len = 1;
290 }
291
292 if (bp->second.active && (addr >= bp->second.addr && addr < bp->second.addr + len)) {
293 LOG_DEBUG(Debug_GDBStub, "Found breakpoint type %d @ %08x, range: %08x - %08x (%d bytes)\n", type, addr, bp->second.addr, bp->second.addr + len, len);
294 return true;
295 }
296 }
297
298 return false;
299}
300
301/**
302 * Send packet to gdb client.
303 *
304 * @param packet Packet to be sent to client.
305 */
306static void SendPacket(const char packet) {
307 size_t sent_size = send(gdbserver_socket, &packet, 1, 0);
308 if (sent_size != 1) {
309 LOG_ERROR(Debug_GDBStub, "send failed");
310 }
311}
312
313/**
314 * Send reply to gdb client.
315 *
316 * @param reply Reply to be sent to client.
317 */
318static void SendReply(const char* reply) {
319 if (!IsConnected()) {
320 return;
321 }
322
323 memset(command_buffer, 0, sizeof(command_buffer));
324
325 command_length = strlen(reply);
326 if (command_length + 4 > sizeof(command_buffer)) {
327 LOG_ERROR(Debug_GDBStub, "command_buffer overflow in SendReply");
328 return;
329 }
330
331 memcpy(command_buffer + 1, reply, command_length);
332
333 u8 checksum = CalculateChecksum(command_buffer, command_length + 1);
334 command_buffer[0] = GDB_STUB_START;
335 command_buffer[command_length + 1] = GDB_STUB_END;
336 command_buffer[command_length + 2] = NibbleToHex(checksum >> 4);
337 command_buffer[command_length + 3] = NibbleToHex(checksum);
338
339 u8* ptr = command_buffer;
340 u32 left = command_length + 4;
341 while (left > 0) {
342 int sent_size = send(gdbserver_socket, reinterpret_cast<char*>(ptr), left, 0);
343 if (sent_size < 0) {
344 LOG_ERROR(Debug_GDBStub, "gdb: send failed");
345 return Shutdown();
346 }
347
348 left -= sent_size;
349 ptr += sent_size;
350 }
351}
352
353/// Handle query command from gdb client.
354static void HandleQuery() {
355 LOG_DEBUG(Debug_GDBStub, "gdb: query '%s'\n", command_buffer + 1);
356
357 if (!strcmp(reinterpret_cast<const char*>(command_buffer + 1), "TStatus")) {
358 SendReply("T0");
359 } else {
360 SendReply("");
361 }
362}
363
364/// Handle set thread command from gdb client.
365static void HandleSetThread() {
366 if (memcmp(command_buffer, "Hg0", 3) == 0 ||
367 memcmp(command_buffer, "Hc-1", 4) == 0 ||
368 memcmp(command_buffer, "Hc0", 4) == 0 ||
369 memcmp(command_buffer, "Hc1", 4) == 0) {
370 return SendReply("OK");
371 }
372
373 SendReply("E01");
374}
375
376/**
377 * Send signal packet to client.
378 *
379 * @param signal Signal to be sent to client.
380 */
381void SendSignal(u32 signal) {
382 if (gdbserver_socket == -1) {
383 return;
384 }
385
386 latest_signal = signal;
387
388 std::string buffer = Common::StringFromFormat("T%02x%02x:%08x;%02x:%08x;", latest_signal, 15, htonl(Core::g_app_core->GetPC()), 13, htonl(Core::g_app_core->GetReg(13)));
389 LOG_DEBUG(Debug_GDBStub, "Response: %s", buffer.c_str());
390 SendReply(buffer.c_str());
391}
392
393/// Read command from gdb client.
394static void ReadCommand() {
395 command_length = 0;
396 memset(command_buffer, 0, sizeof(command_buffer));
397
398 u8 c = ReadByte();
399 if (c == '+') {
400 //ignore ack
401 return;
402 } else if (c == 0x03) {
403 LOG_INFO(Debug_GDBStub, "gdb: found break command\n");
404 halt_loop = true;
405 SendSignal(SIGTRAP);
406 return;
407 } else if (c != GDB_STUB_START) {
408 LOG_DEBUG(Debug_GDBStub, "gdb: read invalid byte %02x\n", c);
409 return;
410 }
411
412 while ((c = ReadByte()) != GDB_STUB_END) {
413 if (command_length >= sizeof(command_buffer)) {
414 LOG_ERROR(Debug_GDBStub, "gdb: command_buffer overflow\n");
415 SendPacket(GDB_STUB_NACK);
416 return;
417 }
418 command_buffer[command_length++] = c;
419 }
420
421 u8 checksum_received = HexCharToValue(ReadByte()) << 4;
422 checksum_received |= HexCharToValue(ReadByte());
423
424 u8 checksum_calculated = CalculateChecksum(command_buffer, command_length);
425
426 if (checksum_received != checksum_calculated) {
427 LOG_ERROR(Debug_GDBStub, "gdb: invalid checksum: calculated %02x and read %02x for $%s# (length: %d)\n",
428 checksum_calculated, checksum_received, command_buffer, command_length);
429
430 command_length = 0;
431
432 SendPacket(GDB_STUB_NACK);
433 return;
434 }
435
436 SendPacket(GDB_STUB_ACK);
437}
438
439/// Check if there is data to be read from the gdb client.
440static bool IsDataAvailable() {
441 if (!IsConnected()) {
442 return false;
443 }
444
445 fd_set fd_socket;
446
447 FD_ZERO(&fd_socket);
448 FD_SET(gdbserver_socket, &fd_socket);
449
450 struct timeval t;
451 t.tv_sec = 0;
452 t.tv_usec = 0;
453
454 if (select(gdbserver_socket + 1, &fd_socket, nullptr, nullptr, &t) < 0) {
455 LOG_ERROR(Debug_GDBStub, "select failed");
456 return false;
457 }
458
459 return FD_ISSET(gdbserver_socket, &fd_socket);
460}
461
462/// Send requested register to gdb client.
463static void ReadRegister() {
464 static u8 reply[64];
465 memset(reply, 0, sizeof(reply));
466
467 u32 id = HexCharToValue(command_buffer[1]);
468 if (command_buffer[2] != '\0') {
469 id <<= 4;
470 id |= HexCharToValue(command_buffer[2]);
471 }
472
473 if (id >= R0_REGISTER && id <= R15_REGISTER) {
474 IntToGdbHex(reply, Core::g_app_core->GetReg(id));
475 } else if (id == CSPR_REGISTER) {
476 IntToGdbHex(reply, Core::g_app_core->GetCPSR());
477 } else if (id > CSPR_REGISTER && id < FPSCR_REGISTER) {
478 IntToGdbHex(reply, Core::g_app_core->GetVFPReg(id - CSPR_REGISTER - 1)); // VFP registers should start at 26, so one after CSPR_REGISTER
479 } else if (id == FPSCR_REGISTER) {
480 IntToGdbHex(reply, Core::g_app_core->GetVFPSystemReg(VFP_FPSCR)); // Get FPSCR
481 IntToGdbHex(reply + 8, 0);
482 } else {
483 return SendReply("E01");
484 }
485
486 SendReply(reinterpret_cast<char*>(reply));
487}
488
489/// Send all registers to the gdb client.
490static void ReadRegisters() {
491 static u8 buffer[GDB_BUFFER_SIZE - 4];
492 memset(buffer, 0, sizeof(buffer));
493
494 u8* bufptr = buffer;
495 for (int i = 0, reg = 0; i <= MAX_REGISTERS; i++, reg++) {
496 if (i <= R15_REGISTER) {
497 IntToGdbHex(bufptr + i * CHAR_BIT, Core::g_app_core->GetReg(reg));
498 } else if (i == CSPR_REGISTER) {
499 IntToGdbHex(bufptr + i * CHAR_BIT, Core::g_app_core->GetCPSR());
500 } else if (i < CSPR_REGISTER) {
501 IntToGdbHex(bufptr + i * CHAR_BIT, 0);
502 IntToGdbHex(bufptr + (i + 1) * CHAR_BIT, 0);
503 i++; // These registers seem to be all 64bit instead of 32bit, so skip two instead of one
504 reg++;
505 } else if (i > CSPR_REGISTER && i < MAX_REGISTERS) {
506 IntToGdbHex(bufptr + i * CHAR_BIT, Core::g_app_core->GetVFPReg(reg - CSPR_REGISTER - 1));
507 IntToGdbHex(bufptr + (i + 1) * CHAR_BIT, 0);
508 i++;
509 } else if (i == MAX_REGISTERS) {
510 IntToGdbHex(bufptr + i * CHAR_BIT, Core::g_app_core->GetVFPSystemReg(VFP_FPSCR));
511 }
512 }
513
514 SendReply(reinterpret_cast<char*>(buffer));
515}
516
517/// Modify data of register specified by gdb client.
518static void WriteRegister() {
519 u8* buffer_ptr = command_buffer + 3;
520
521 u32 id = HexCharToValue(command_buffer[1]);
522 if (command_buffer[2] != '=') {
523 ++buffer_ptr;
524 id <<= 4;
525 id |= HexCharToValue(command_buffer[2]);
526 }
527
528 if (id >= R0_REGISTER && id <= R15_REGISTER) {
529 Core::g_app_core->SetReg(id, GdbHexToInt(buffer_ptr));
530 } else if (id == CSPR_REGISTER) {
531 Core::g_app_core->SetCPSR(GdbHexToInt(buffer_ptr));
532 } else if (id > CSPR_REGISTER && id < FPSCR_REGISTER) {
533 Core::g_app_core->SetVFPReg(id - CSPR_REGISTER - 1, GdbHexToInt(buffer_ptr));
534 } else if (id == FPSCR_REGISTER) {
535 Core::g_app_core->SetVFPSystemReg(VFP_FPSCR, GdbHexToInt(buffer_ptr));
536 } else {
537 return SendReply("E01");
538 }
539
540 SendReply("OK");
541}
542
543/// Modify all registers with data received from the client.
544static void WriteRegisters() {
545 u8* buffer_ptr = command_buffer + 1;
546
547 if (command_buffer[0] != 'G')
548 return SendReply("E01");
549
550 for (int i = 0, reg = 0; i <= MAX_REGISTERS; i++, reg++) {
551 if (i <= R15_REGISTER) {
552 Core::g_app_core->SetReg(reg, GdbHexToInt(buffer_ptr + i * CHAR_BIT));
553 } else if (i == CSPR_REGISTER) {
554 Core::g_app_core->SetCPSR(GdbHexToInt(buffer_ptr + i * CHAR_BIT));
555 } else if (i < CSPR_REGISTER) {
556 i++; // These registers seem to be all 64bit instead of 32bit, so skip two instead of one
557 reg++;
558 } else if (i > CSPR_REGISTER && i < MAX_REGISTERS) {
559 Core::g_app_core->SetVFPReg(reg - CSPR_REGISTER - 1, GdbHexToInt(buffer_ptr + i * CHAR_BIT));
560 i++; // Skip padding
561 } else if (i == MAX_REGISTERS) {
562 Core::g_app_core->SetVFPSystemReg(VFP_FPSCR, GdbHexToInt(buffer_ptr + i * CHAR_BIT));
563 }
564 }
565
566 SendReply("OK");
567}
568
569/// Read location in memory specified by gdb client.
570static void ReadMemory() {
571 static u8 reply[GDB_BUFFER_SIZE - 4];
572
573 auto start_offset = command_buffer+1;
574 auto addr_pos = std::find(start_offset, command_buffer+command_length, ',');
575 PAddr addr = HexToInt(start_offset, addr_pos - start_offset);
576
577 start_offset = addr_pos+1;
578 u32 len = HexToInt(start_offset, (command_buffer + command_length) - start_offset);
579
580 LOG_DEBUG(Debug_GDBStub, "gdb: addr: %08x len: %08x\n", addr, len);
581
582 if (len * 2 > sizeof(reply)) {
583 SendReply("E01");
584 }
585
586 u8* data = Memory::GetPointer(addr);
587 if (!data) {
588 return SendReply("E0");
589 }
590
591 MemToGdbHex(reply, data, len);
592 reply[len * 2] = '\0';
593 SendReply(reinterpret_cast<char*>(reply));
594}
595
596/// Modify location in memory with data received from the gdb client.
597static void WriteMemory() {
598 auto start_offset = command_buffer+1;
599 auto addr_pos = std::find(start_offset, command_buffer+command_length, ',');
600 PAddr addr = HexToInt(start_offset, addr_pos - start_offset);
601
602 start_offset = addr_pos+1;
603 auto len_pos = std::find(start_offset, command_buffer+command_length, ':');
604 u32 len = HexToInt(start_offset, len_pos - start_offset);
605
606 u8* dst = Memory::GetPointer(addr);
607 if (!dst) {
608 return SendReply("E00");
609 }
610
611 GdbHexToMem(dst, len_pos + 1, len);
612 SendReply("OK");
613}
614
615void Break(bool is_memory_break) {
616 if (!halt_loop) {
617 halt_loop = true;
618 SendSignal(SIGTRAP);
619 }
620
621 memory_break = is_memory_break;
622}
623
624/// Tell the CPU that it should perform a single step.
625static void Step() {
626 step_loop = true;
627 halt_loop = true;
628 step_break = true;
629 SendSignal(SIGTRAP);
630}
631
632bool IsMemoryBreak() {
633 if (IsConnected()) {
634 return false;
635 }
636
637 return memory_break;
638}
639
640/// Tell the CPU to continue executing.
641static void Continue() {
642 memory_break = false;
643 step_break = false;
644 step_loop = false;
645 halt_loop = false;
646}
647
648/**
649 * Commit breakpoint to list of breakpoints.
650 *
651 * @param type Type of breakpoint.
652 * @param addr Address of breakpoint.
653 * @param len Length of breakpoint.
654 */
655bool CommitBreakpoint(BreakpointType type, PAddr addr, u32 len) {
656 std::map<u32, Breakpoint>& p = GetBreakpointList(type);
657
658 Breakpoint breakpoint;
659 breakpoint.active = true;
660 breakpoint.addr = addr;
661 breakpoint.len = len;
662 p.insert({ addr, breakpoint });
663
664 LOG_DEBUG(Debug_GDBStub, "gdb: added %d breakpoint: %08x bytes at %08x\n", type, breakpoint.len, breakpoint.addr);
665
666 return true;
667}
668
669/// Handle add breakpoint command from gdb client.
670static void AddBreakpoint() {
671 BreakpointType type;
672
673 u8 type_id = HexCharToValue(command_buffer[1]);
674 switch (type_id) {
675 case 0:
676 case 1:
677 type = BreakpointType::Execute;
678 break;
679 case 2:
680 type = BreakpointType::Write;
681 break;
682 case 3:
683 type = BreakpointType::Read;
684 break;
685 case 4:
686 type = BreakpointType::Access;
687 break;
688 default:
689 return SendReply("E01");
690 }
691
692 auto start_offset = command_buffer+3;
693 auto addr_pos = std::find(start_offset, command_buffer+command_length, ',');
694 PAddr addr = HexToInt(start_offset, addr_pos - start_offset);
695
696 start_offset = addr_pos+1;
697 u32 len = HexToInt(start_offset, (command_buffer + command_length) - start_offset);
698
699 if (type == BreakpointType::Access) {
700 // Access is made up of Read and Write types, so add both breakpoints
701 type = BreakpointType::Read;
702
703 if (!CommitBreakpoint(type, addr, len)) {
704 return SendReply("E02");
705 }
706
707 type = BreakpointType::Write;
708 }
709
710 if (!CommitBreakpoint(type, addr, len)) {
711 return SendReply("E02");
712 }
713
714 SendReply("OK");
715}
716
717/// Handle remove breakpoint command from gdb client.
718static void RemoveBreakpoint() {
719 BreakpointType type;
720
721 u8 type_id = HexCharToValue(command_buffer[1]);
722 switch (type_id) {
723 case 0:
724 case 1:
725 type = BreakpointType::Execute;
726 break;
727 case 2:
728 type = BreakpointType::Write;
729 break;
730 case 3:
731 type = BreakpointType::Read;
732 break;
733 case 4:
734 type = BreakpointType::Access;
735 break;
736 default:
737 return SendReply("E01");
738 }
739
740 auto start_offset = command_buffer+3;
741 auto addr_pos = std::find(start_offset, command_buffer+command_length, ',');
742 PAddr addr = HexToInt(start_offset, addr_pos - start_offset);
743
744 start_offset = addr_pos+1;
745 u32 len = HexToInt(start_offset, (command_buffer + command_length) - start_offset);
746
747 if (type == BreakpointType::Access) {
748 // Access is made up of Read and Write types, so add both breakpoints
749 type = BreakpointType::Read;
750 RemoveBreakpoint(type, addr);
751
752 type = BreakpointType::Write;
753 }
754
755 RemoveBreakpoint(type, addr);
756 SendReply("OK");
757}
758
759void HandlePacket() {
760 if (!IsConnected()) {
761 return;
762 }
763
764 if (!IsDataAvailable()) {
765 return;
766 }
767
768 ReadCommand();
769 if (command_length == 0) {
770 return;
771 }
772
773 LOG_DEBUG(Debug_GDBStub, "Packet: %s", command_buffer);
774
775 switch (command_buffer[0]) {
776 case 'q':
777 HandleQuery();
778 break;
779 case 'H':
780 HandleSetThread();
781 break;
782 case '?':
783 SendSignal(latest_signal);
784 break;
785 case 'k':
786 Shutdown();
787 LOG_INFO(Debug_GDBStub, "killed by gdb");
788 return;
789 case 'g':
790 ReadRegisters();
791 break;
792 case 'G':
793 WriteRegisters();
794 break;
795 case 'p':
796 ReadRegister();
797 break;
798 case 'P':
799 WriteRegister();
800 break;
801 case 'm':
802 ReadMemory();
803 break;
804 case 'M':
805 WriteMemory();
806 break;
807 case 's':
808 Step();
809 return;
810 case 'C':
811 case 'c':
812 Continue();
813 return;
814 case 'z':
815 RemoveBreakpoint();
816 break;
817 case 'Z':
818 AddBreakpoint();
819 break;
820 default:
821 SendReply("");
822 break;
823 }
824}
825
826void SetServerPort(u16 port) {
827 gdbstub_port = port;
828}
829
830void ToggleServer(bool status) {
831 if (status) {
832 g_server_enabled = status;
833
834 // Start server
835 if (!IsConnected() && Core::g_sys_core != nullptr) {
836 Init();
837 }
838 }
839 else {
840 // Stop server
841 if (IsConnected()) {
842 Shutdown();
843 }
844
845 g_server_enabled = status;
846 }
847}
848
849void Init(u16 port) {
850 if (!g_server_enabled) {
851 // Set the halt loop to false in case the user enabled the gdbstub mid-execution.
852 // This way the CPU can still execute normally.
853 halt_loop = false;
854 step_loop = false;
855 return;
856 }
857
858 // Setup initial gdbstub status
859 halt_loop = true;
860 step_loop = false;
861
862 breakpoints_execute.clear();
863 breakpoints_read.clear();
864 breakpoints_write.clear();
865
866 // Start gdb server
867 LOG_INFO(Debug_GDBStub, "Starting GDB server on port %d...", port);
868
869 sockaddr_in saddr_server = {};
870 saddr_server.sin_family = AF_INET;
871 saddr_server.sin_port = htons(port);
872 saddr_server.sin_addr.s_addr = INADDR_ANY;
873
874#ifdef _WIN32
875 WSAStartup(MAKEWORD(2, 2), &InitData);
876#endif
877
878 int tmpsock = socket(PF_INET, SOCK_STREAM, 0);
879 if (tmpsock == -1) {
880 LOG_ERROR(Debug_GDBStub, "Failed to create gdb socket");
881 }
882
883 const sockaddr* server_addr = reinterpret_cast<const sockaddr*>(&saddr_server);
884 socklen_t server_addrlen = sizeof(saddr_server);
885 if (bind(tmpsock, server_addr, server_addrlen) < 0) {
886 LOG_ERROR(Debug_GDBStub, "Failed to bind gdb socket");
887 }
888
889 if (listen(tmpsock, 1) < 0) {
890 LOG_ERROR(Debug_GDBStub, "Failed to listen to gdb socket");
891 }
892
893 // Wait for gdb to connect
894 LOG_INFO(Debug_GDBStub, "Waiting for gdb to connect...\n");
895 sockaddr_in saddr_client;
896 sockaddr* client_addr = reinterpret_cast<sockaddr*>(&saddr_client);
897 socklen_t client_addrlen = sizeof(saddr_client);
898 gdbserver_socket = accept(tmpsock, client_addr, &client_addrlen);
899 if (gdbserver_socket < 0) {
900 // In the case that we couldn't start the server for whatever reason, just start CPU execution like normal.
901 halt_loop = false;
902 step_loop = false;
903
904 LOG_ERROR(Debug_GDBStub, "Failed to accept gdb client");
905 }
906 else {
907 LOG_INFO(Debug_GDBStub, "Client connected.\n");
908 saddr_client.sin_addr.s_addr = ntohl(saddr_client.sin_addr.s_addr);
909 }
910
911 // Clean up temporary socket if it's still alive at this point.
912 if (tmpsock != -1) {
913 shutdown(tmpsock, SHUT_RDWR);
914 }
915}
916
917void Init() {
918 Init(gdbstub_port);
919}
920
921void Shutdown() {
922 if (!g_server_enabled) {
923 return;
924 }
925
926 LOG_INFO(Debug_GDBStub, "Stopping GDB ...");
927 if (gdbserver_socket != -1) {
928 shutdown(gdbserver_socket, SHUT_RDWR);
929 gdbserver_socket = -1;
930 }
931
932#ifdef _WIN32
933 WSACleanup();
934#endif
935
936 LOG_INFO(Debug_GDBStub, "GDB stopped.");
937}
938
939bool IsConnected() {
940 return g_server_enabled && gdbserver_socket != -1;
941}
942
943bool GetCpuHaltFlag() {
944 return halt_loop;
945}
946
947bool GetCpuStepFlag() {
948 return step_loop;
949}
950
951void SetCpuStepFlag(bool is_step) {
952 step_loop = is_step;
953}
954
955};
diff --git a/src/core/gdbstub/gdbstub.h b/src/core/gdbstub/gdbstub.h
new file mode 100644
index 000000000..aff705a32
--- /dev/null
+++ b/src/core/gdbstub/gdbstub.h
@@ -0,0 +1,94 @@
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#include <atomic>
9
10namespace GDBStub {
11
12/// Breakpoint Method
13enum class BreakpointType {
14 None, ///< None
15 Execute, ///< Execution Breakpoint
16 Read, ///< Read Breakpoint
17 Write, ///< Write Breakpoint
18 Access ///< Access (R/W) Breakpoint
19};
20
21struct BreakpointAddress {
22 PAddr address;
23 BreakpointType type;
24};
25
26/// If set to false, the server will never be started and no gdbstub-related functions will be executed.
27extern std::atomic<bool> g_server_enabled;
28
29/**
30 * Set the port the gdbstub should use to listen for connections.
31 *
32 * @param port Port to listen for connection
33 */
34void SetServerPort(u16 port);
35
36/**
37 * Set the g_server_enabled flag and start or stop the server if possible.
38 *
39 * @param status Set the server to enabled or disabled.
40 */
41void ToggleServer(bool status);
42
43/// Start the gdbstub server.
44void Init();
45
46/// Stop gdbstub server.
47void Shutdown();
48
49/// Returns true if there is an active socket connection.
50bool IsConnected();
51
52/**
53 * Signal to the gdbstub server that it should halt CPU execution.
54 *
55 * @param is_memory_break If true, the break resulted from a memory breakpoint.
56 */
57void Break(bool is_memory_break = false);
58
59/// Determine if there was a memory breakpoint.
60bool IsMemoryBreak();
61
62/// Read and handle packet from gdb client.
63void HandlePacket();
64
65/**
66 * Get the nearest breakpoint of the specified type at the given address.
67 *
68 * @param addr Address to search from.
69 * @param type Type of breakpoint.
70 */
71BreakpointAddress GetNextBreakpointFromAddress(u32 addr, GDBStub::BreakpointType type);
72
73/**
74 * Check if a breakpoint of the specified type exists at the given address.
75 *
76 * @param addr Address of breakpoint.
77 * @param type Type of breakpoint.
78 */
79bool CheckBreakpoint(u32 addr, GDBStub::BreakpointType type);
80
81// If set to true, the CPU will halt at the beginning of the next CPU loop.
82bool GetCpuHaltFlag();
83
84// If set to true and the CPU is halted, the CPU will step one instruction.
85bool GetCpuStepFlag();
86
87/**
88 * When set to true, the CPU will step one instruction when the CPU is halted next.
89 *
90 * @param is_step
91 */
92void SetCpuStepFlag(bool is_step);
93
94}
diff --git a/src/core/settings.h b/src/core/settings.h
index 0b05e5bee..97ddcdff9 100644
--- a/src/core/settings.h
+++ b/src/core/settings.h
@@ -6,6 +6,7 @@
6 6
7#include <string> 7#include <string>
8#include <array> 8#include <array>
9#include <common/file_util.h>
9 10
10namespace Settings { 11namespace Settings {
11 12
@@ -60,6 +61,10 @@ struct Values {
60 float bg_blue; 61 float bg_blue;
61 62
62 std::string log_filter; 63 std::string log_filter;
64
65 // Debugging
66 bool use_gdbstub;
67 u16 gdbstub_port;
63} extern values; 68} extern values;
64 69
65} 70}
diff --git a/src/core/system.cpp b/src/core/system.cpp
index 3cd84bf5e..7e9c56538 100644
--- a/src/core/system.cpp
+++ b/src/core/system.cpp
@@ -12,6 +12,8 @@
12 12
13#include "video_core/video_core.h" 13#include "video_core/video_core.h"
14 14
15#include "core/gdbstub/gdbstub.h"
16
15namespace System { 17namespace System {
16 18
17void Init(EmuWindow* emu_window) { 19void Init(EmuWindow* emu_window) {
@@ -22,9 +24,11 @@ void Init(EmuWindow* emu_window) {
22 Kernel::Init(); 24 Kernel::Init();
23 HLE::Init(); 25 HLE::Init();
24 VideoCore::Init(emu_window); 26 VideoCore::Init(emu_window);
27 GDBStub::Init();
25} 28}
26 29
27void Shutdown() { 30void Shutdown() {
31 GDBStub::Shutdown();
28 VideoCore::Shutdown(); 32 VideoCore::Shutdown();
29 HLE::Shutdown(); 33 HLE::Shutdown();
30 Kernel::Shutdown(); 34 Kernel::Shutdown();