Initial port of luma3ds' gdb_hio to Citra
This commit is contained in:
		| @@ -115,6 +115,8 @@ add_library(core STATIC | ||||
|     frontend/mic.h | ||||
|     gdbstub/gdbstub.cpp | ||||
|     gdbstub/gdbstub.h | ||||
|     gdbstub/hio.cpp | ||||
|     gdbstub/hio.h | ||||
|     hle/applets/applet.cpp | ||||
|     hle/applets/applet.h | ||||
|     hle/applets/erreula.cpp | ||||
|   | ||||
| @@ -35,6 +35,7 @@ | ||||
| #include "core/arm/arm_interface.h" | ||||
| #include "core/core.h" | ||||
| #include "core/gdbstub/gdbstub.h" | ||||
| #include "core/gdbstub/hio.h" | ||||
| #include "core/hle/kernel/process.h" | ||||
| #include "core/loader/loader.h" | ||||
| #include "core/memory.h" | ||||
| @@ -1061,6 +1062,19 @@ void HandlePacket() { | ||||
|  | ||||
|     LOG_DEBUG(Debug_GDBStub, "Packet: {}", command_buffer[0]); | ||||
|  | ||||
|     // HACK: instead of polling DebugEvents properly via SVC, just check for | ||||
|     // whether there's a pending a request, and send it if so. | ||||
|     switch (command_buffer[0]) { | ||||
|     case 'c': | ||||
|     case 'C': | ||||
|     case 's': | ||||
|         if (HasHioRequest()) { | ||||
|             const auto reply = BuildHioReply(); | ||||
|             SendReply(reply.data()); | ||||
|             return; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     switch (command_buffer[0]) { | ||||
|     case 'q': | ||||
|         HandleQuery(); | ||||
| @@ -1075,6 +1089,11 @@ void HandlePacket() { | ||||
|         Shutdown(); | ||||
|         LOG_INFO(Debug_GDBStub, "killed by gdb"); | ||||
|         return; | ||||
|     case 'F': | ||||
|         if (HandleHioRequest(command_buffer, command_length)) { | ||||
|             Continue(); | ||||
|         }; | ||||
|         break; | ||||
|     case 'g': | ||||
|         ReadRegisters(); | ||||
|         break; | ||||
|   | ||||
							
								
								
									
										150
									
								
								src/core/gdbstub/hio.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										150
									
								
								src/core/gdbstub/hio.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,150 @@ | ||||
| #include "core/core.h" | ||||
| #include "core/gdbstub/gdbstub.h" | ||||
| #include "core/gdbstub/hio.h" | ||||
|  | ||||
| namespace GDBStub { | ||||
|  | ||||
| namespace { | ||||
|  | ||||
| static VAddr current_hio_request_addr; | ||||
| static PackedGdbHioRequest current_hio_request; | ||||
|  | ||||
| } // namespace | ||||
|  | ||||
| void SetHioRequest(const VAddr addr) { | ||||
|     if (!IsServerEnabled()) { | ||||
|         LOG_WARNING(Debug_GDBStub, "HIO requested but GDB stub is not running"); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     if (current_hio_request_addr != 0) { | ||||
|         LOG_WARNING(Debug_GDBStub, "HIO requested while already in progress!"); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     auto& memory = Core::System::GetInstance().Memory(); | ||||
|     if (!memory.IsValidVirtualAddress(*Core::System::GetInstance().Kernel().GetCurrentProcess(), | ||||
|                                       addr)) { | ||||
|         LOG_WARNING(Debug_GDBStub, "Invalid address for HIO request"); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     memory.ReadBlock(addr, ¤t_hio_request, sizeof(PackedGdbHioRequest)); | ||||
|     current_hio_request_addr = addr; | ||||
|  | ||||
|     LOG_DEBUG(Debug_GDBStub, "HIO request initiated"); | ||||
| } | ||||
|  | ||||
| bool HandleHioRequest(const u8* const command_buffer, const u32 command_length) { | ||||
|     if (!HasHioRequest()) { | ||||
|         // TODO send error reply packet? | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     u64 retval{0}; | ||||
|  | ||||
|     auto* command_pos = command_buffer; | ||||
|     ++command_pos; | ||||
|  | ||||
|     // TODO: not totally sure what's going on here... | ||||
|     if (*command_pos == 0 || *command_pos == ',') { | ||||
|         // return GDB_ReplyErrno(ctx, EILSEQ); | ||||
|         return false; | ||||
|     } else if (*command_pos == '-') { | ||||
|         command_pos++; | ||||
|         current_hio_request.retval = -1; | ||||
|     } else if (*command_pos == '+') { | ||||
|         command_pos++; | ||||
|         current_hio_request.retval = 1; | ||||
|     } else { | ||||
|         current_hio_request.retval = 1; | ||||
|     } | ||||
|  | ||||
|     // TODO: | ||||
|     // pos = GDB_ParseHexIntegerList64(&retval, pos, 1, ','); | ||||
|  | ||||
|     if (command_pos == nullptr) { | ||||
|         // return GDB_ReplyErrno(ctx, EILSEQ); | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     current_hio_request.retval *= retval; | ||||
|     current_hio_request.gdb_errno = 0; | ||||
|     current_hio_request.ctrl_c = 0; | ||||
|  | ||||
|     if (*command_pos != 0) { | ||||
|         u32 errno_; | ||||
|         // GDB protocol technically allows errno to have a +/- prefix but this will never happen. | ||||
|         // TODO: | ||||
|         // pos = GDB_ParseHexIntegerList(&errno_, ++pos, 1, ','); | ||||
|         current_hio_request.gdb_errno = (int)errno_; | ||||
|         if (command_pos == nullptr) { | ||||
|             return false; | ||||
|             // return GDB_ReplyErrno(ctx, EILSEQ); | ||||
|         } | ||||
|  | ||||
|         if (*command_pos != 0) { | ||||
|             if (*command_pos != 'C') { | ||||
|                 return false; | ||||
|                 // return GDB_ReplyErrno(ctx, EILSEQ); | ||||
|             } | ||||
|  | ||||
|             current_hio_request.ctrl_c = true; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     std::fill(std::begin(current_hio_request.param_format), | ||||
|               std::end(current_hio_request.param_format), 0); | ||||
|  | ||||
|     auto& memory = Core::System::GetInstance().Memory(); | ||||
|     // should have been checked when we first initialized the request: | ||||
|     assert(memory.IsValidVirtualAddress(*Core::System::GetInstance().Kernel().GetCurrentProcess(), | ||||
|                                         current_hio_request_addr)); | ||||
|  | ||||
|     memory.WriteBlock(current_hio_request_addr, ¤t_hio_request, sizeof(PackedGdbHioRequest)); | ||||
|  | ||||
|     current_hio_request = PackedGdbHioRequest{}; | ||||
|     current_hio_request_addr = 0; | ||||
|  | ||||
|     return true; | ||||
| } | ||||
|  | ||||
| bool HasHioRequest() { | ||||
|     return current_hio_request_addr != 0; | ||||
| } | ||||
|  | ||||
| std::string BuildHioReply() { | ||||
|     char buf[256 + 1]; | ||||
|     char tmp[32 + 1]; | ||||
|     u32 nStr = 0; | ||||
|  | ||||
|     // TODO: c++ify this and use the IntToGdbHex funcs instead of snprintf | ||||
|  | ||||
|     snprintf(buf, 256, "F%s", current_hio_request.function_name); | ||||
|  | ||||
|     for (u32 i = 0; i < 8 && current_hio_request.param_format[i] != 0; i++) { | ||||
|         switch (current_hio_request.param_format[i]) { | ||||
|         case 'i': | ||||
|         case 'I': | ||||
|         case 'p': | ||||
|             snprintf(tmp, 32, ",%x", (u32)current_hio_request.parameters[i]); | ||||
|             break; | ||||
|         case 'l': | ||||
|         case 'L': | ||||
|             snprintf(tmp, 32, ",%llx", current_hio_request.parameters[i]); | ||||
|             break; | ||||
|         case 's': | ||||
|             snprintf(tmp, 32, ",%x/%zx", (u32)current_hio_request.parameters[i], | ||||
|                      current_hio_request.string_lengths[nStr++]); | ||||
|             break; | ||||
|         default: | ||||
|             tmp[0] = 0; | ||||
|             break; | ||||
|         } | ||||
|         strcat(buf, tmp); | ||||
|     } | ||||
|  | ||||
|     return std::string{buf, strlen(buf)}; | ||||
| } | ||||
|  | ||||
| } // namespace GDBStub | ||||
							
								
								
									
										32
									
								
								src/core/gdbstub/hio.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								src/core/gdbstub/hio.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,32 @@ | ||||
| #pragma once | ||||
|  | ||||
| #include "common/common_types.h" | ||||
|  | ||||
| namespace GDBStub { | ||||
|  | ||||
| struct PackedGdbHioRequest { | ||||
|     char magic[4]; // "GDB\x00" | ||||
|     u32 version; | ||||
|  | ||||
|     // Request | ||||
|     char function_name[16 + 1]; | ||||
|     char param_format[8 + 1]; | ||||
|  | ||||
|     u64 parameters[8]; | ||||
|     size_t string_lengths[8]; | ||||
|  | ||||
|     // Return | ||||
|     s64 retval; | ||||
|     int gdb_errno; | ||||
|     bool ctrl_c; | ||||
| }; | ||||
|  | ||||
| void SetHioRequest(const VAddr address); | ||||
|  | ||||
| bool HandleHioRequest(const u8* const command_buffer, const u32 command_length); | ||||
|  | ||||
| bool HasHioRequest(); | ||||
|  | ||||
| std::string BuildHioReply(); | ||||
|  | ||||
| } // namespace GDBStub | ||||
| @@ -13,6 +13,7 @@ | ||||
| #include "core/arm/arm_interface.h" | ||||
| #include "core/core.h" | ||||
| #include "core/core_timing.h" | ||||
| #include "core/gdbstub/hio.h" | ||||
| #include "core/hle/kernel/address_arbiter.h" | ||||
| #include "core/hle/kernel/client_port.h" | ||||
| #include "core/hle/kernel/client_session.h" | ||||
| @@ -1140,8 +1141,14 @@ void SVC::Break(u8 break_reason) { | ||||
|     system.SetStatus(Core::System::ResultStatus::ErrorUnknown); | ||||
| } | ||||
|  | ||||
| /// Used to output a message on a debug hardware unit - does nothing on a retail unit | ||||
| /// Used to output a message on a debug hardware unit, or for the GDB HIO | ||||
| // protocol - does nothing on a retail unit. | ||||
| void SVC::OutputDebugString(VAddr address, s32 len) { | ||||
|     if (len == 0) { | ||||
|         GDBStub::SetHioRequest(address); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     if (len <= 0) { | ||||
|         return; | ||||
|     } | ||||
| @@ -2212,7 +2219,7 @@ const std::array<SVC::FunctionDef, 180> SVC::SVC_Table{{ | ||||
|     {0x60, nullptr, "DebugActiveProcess"}, | ||||
|     {0x61, nullptr, "BreakDebugProcess"}, | ||||
|     {0x62, nullptr, "TerminateDebugProcess"}, | ||||
|     {0x63, nullptr, "GetProcessDebugEvent"}, | ||||
|     {0x63, nullptr, "GetProcessDebugEvent"}, // TODO: do we need this for HIO to work? | ||||
|     {0x64, nullptr, "ContinueDebugEvent"}, | ||||
|     {0x65, &SVC::Wrap<&SVC::GetProcessList>, "GetProcessList"}, | ||||
|     {0x66, nullptr, "GetThreadList"}, | ||||
|   | ||||
		Reference in New Issue
	
	Block a user