mirror of
				https://github.com/PabloMK7/citra.git
				synced 2025-10-31 05:40:04 +00:00 
			
		
		
		
	Initial port of luma3ds' gdb_hio to Citra
This commit is contained in:
		
							parent
							
								
									5346ca27b5
								
							
						
					
					
						commit
						6f23ee43ae
					
				
					 5 changed files with 212 additions and 2 deletions
				
			
		|  | @ -115,6 +115,8 @@ add_library(core STATIC | ||||||
|     frontend/mic.h |     frontend/mic.h | ||||||
|     gdbstub/gdbstub.cpp |     gdbstub/gdbstub.cpp | ||||||
|     gdbstub/gdbstub.h |     gdbstub/gdbstub.h | ||||||
|  |     gdbstub/hio.cpp | ||||||
|  |     gdbstub/hio.h | ||||||
|     hle/applets/applet.cpp |     hle/applets/applet.cpp | ||||||
|     hle/applets/applet.h |     hle/applets/applet.h | ||||||
|     hle/applets/erreula.cpp |     hle/applets/erreula.cpp | ||||||
|  |  | ||||||
|  | @ -35,6 +35,7 @@ | ||||||
| #include "core/arm/arm_interface.h" | #include "core/arm/arm_interface.h" | ||||||
| #include "core/core.h" | #include "core/core.h" | ||||||
| #include "core/gdbstub/gdbstub.h" | #include "core/gdbstub/gdbstub.h" | ||||||
|  | #include "core/gdbstub/hio.h" | ||||||
| #include "core/hle/kernel/process.h" | #include "core/hle/kernel/process.h" | ||||||
| #include "core/loader/loader.h" | #include "core/loader/loader.h" | ||||||
| #include "core/memory.h" | #include "core/memory.h" | ||||||
|  | @ -1061,6 +1062,19 @@ void HandlePacket() { | ||||||
| 
 | 
 | ||||||
|     LOG_DEBUG(Debug_GDBStub, "Packet: {}", command_buffer[0]); |     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]) { |     switch (command_buffer[0]) { | ||||||
|     case 'q': |     case 'q': | ||||||
|         HandleQuery(); |         HandleQuery(); | ||||||
|  | @ -1075,6 +1089,11 @@ void HandlePacket() { | ||||||
|         Shutdown(); |         Shutdown(); | ||||||
|         LOG_INFO(Debug_GDBStub, "killed by gdb"); |         LOG_INFO(Debug_GDBStub, "killed by gdb"); | ||||||
|         return; |         return; | ||||||
|  |     case 'F': | ||||||
|  |         if (HandleHioRequest(command_buffer, command_length)) { | ||||||
|  |             Continue(); | ||||||
|  |         }; | ||||||
|  |         break; | ||||||
|     case 'g': |     case 'g': | ||||||
|         ReadRegisters(); |         ReadRegisters(); | ||||||
|         break; |         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/arm/arm_interface.h" | ||||||
| #include "core/core.h" | #include "core/core.h" | ||||||
| #include "core/core_timing.h" | #include "core/core_timing.h" | ||||||
|  | #include "core/gdbstub/hio.h" | ||||||
| #include "core/hle/kernel/address_arbiter.h" | #include "core/hle/kernel/address_arbiter.h" | ||||||
| #include "core/hle/kernel/client_port.h" | #include "core/hle/kernel/client_port.h" | ||||||
| #include "core/hle/kernel/client_session.h" | #include "core/hle/kernel/client_session.h" | ||||||
|  | @ -1140,8 +1141,14 @@ void SVC::Break(u8 break_reason) { | ||||||
|     system.SetStatus(Core::System::ResultStatus::ErrorUnknown); |     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) { | void SVC::OutputDebugString(VAddr address, s32 len) { | ||||||
|  |     if (len == 0) { | ||||||
|  |         GDBStub::SetHioRequest(address); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     if (len <= 0) { |     if (len <= 0) { | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  | @ -2212,7 +2219,7 @@ const std::array<SVC::FunctionDef, 180> SVC::SVC_Table{{ | ||||||
|     {0x60, nullptr, "DebugActiveProcess"}, |     {0x60, nullptr, "DebugActiveProcess"}, | ||||||
|     {0x61, nullptr, "BreakDebugProcess"}, |     {0x61, nullptr, "BreakDebugProcess"}, | ||||||
|     {0x62, nullptr, "TerminateDebugProcess"}, |     {0x62, nullptr, "TerminateDebugProcess"}, | ||||||
|     {0x63, nullptr, "GetProcessDebugEvent"}, |     {0x63, nullptr, "GetProcessDebugEvent"}, // TODO: do we need this for HIO to work?
 | ||||||
|     {0x64, nullptr, "ContinueDebugEvent"}, |     {0x64, nullptr, "ContinueDebugEvent"}, | ||||||
|     {0x65, &SVC::Wrap<&SVC::GetProcessList>, "GetProcessList"}, |     {0x65, &SVC::Wrap<&SVC::GetProcessList>, "GetProcessList"}, | ||||||
|     {0x66, nullptr, "GetThreadList"}, |     {0x66, nullptr, "GetThreadList"}, | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue