mirror of
				https://github.com/PabloMK7/citra.git
				synced 2025-10-30 21:30:04 +00:00 
			
		
		
		
	Merge pull request #6395 from ian-h-chamberlain/feature/gdbstub-hio
Initial port of luma3ds' gdb_hio to Citra
This commit is contained in:
		
						commit
						2a2ee8bc96
					
				
					 6 changed files with 391 additions and 20 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" | ||||||
|  | @ -261,13 +262,7 @@ static u8 NibbleToHex(u8 n) { | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /**
 | u32 HexToInt(const u8* src, std::size_t len) { | ||||||
|  * Converts input hex string characters into an array of equivalent of u8 bytes. |  | ||||||
|  * |  | ||||||
|  * @param src Pointer to array of output hex string characters. |  | ||||||
|  * @param len Length of src array. |  | ||||||
|  */ |  | ||||||
| static u32 HexToInt(const u8* src, std::size_t len) { |  | ||||||
|     u32 output = 0; |     u32 output = 0; | ||||||
|     while (len-- > 0) { |     while (len-- > 0) { | ||||||
|         output = (output << 4) | HexCharToValue(src[0]); |         output = (output << 4) | HexCharToValue(src[0]); | ||||||
|  | @ -492,12 +487,7 @@ static void SendPacket(const char packet) { | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /**
 | void SendReply(const char* reply) { | ||||||
|  * Send reply to gdb client. |  | ||||||
|  * |  | ||||||
|  * @param reply Reply to be sent to client. |  | ||||||
|  */ |  | ||||||
| static void SendReply(const char* reply) { |  | ||||||
|     if (!IsConnected()) { |     if (!IsConnected()) { | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  | @ -653,7 +643,7 @@ static void ReadCommand() { | ||||||
|     memset(command_buffer, 0, sizeof(command_buffer)); |     memset(command_buffer, 0, sizeof(command_buffer)); | ||||||
| 
 | 
 | ||||||
|     u8 c = ReadByte(); |     u8 c = ReadByte(); | ||||||
|     if (c == '+') { |     if (c == GDB_STUB_ACK) { | ||||||
|         // ignore ack
 |         // ignore ack
 | ||||||
|         return; |         return; | ||||||
|     } else if (c == 0x03) { |     } else if (c == 0x03) { | ||||||
|  | @ -843,7 +833,7 @@ static void ReadMemory() { | ||||||
|     u32 len = |     u32 len = | ||||||
|         HexToInt(start_offset, static_cast<u32>((command_buffer + command_length) - start_offset)); |         HexToInt(start_offset, static_cast<u32>((command_buffer + command_length) - start_offset)); | ||||||
| 
 | 
 | ||||||
|     LOG_DEBUG(Debug_GDBStub, "gdb: addr: {:08x} len: {:08x}\n", addr, len); |     LOG_DEBUG(Debug_GDBStub, "ReadMemory addr: {:08x} len: {:08x}", addr, len); | ||||||
| 
 | 
 | ||||||
|     if (len * 2 > sizeof(reply)) { |     if (len * 2 > sizeof(reply)) { | ||||||
|         SendReply("E01"); |         SendReply("E01"); | ||||||
|  | @ -860,7 +850,11 @@ static void ReadMemory() { | ||||||
| 
 | 
 | ||||||
|     MemToGdbHex(reply, data.data(), len); |     MemToGdbHex(reply, data.data(), len); | ||||||
|     reply[len * 2] = '\0'; |     reply[len * 2] = '\0'; | ||||||
|     SendReply(reinterpret_cast<char*>(reply)); | 
 | ||||||
|  |     auto reply_str = reinterpret_cast<char*>(reply); | ||||||
|  | 
 | ||||||
|  |     LOG_DEBUG(Debug_GDBStub, "ReadMemory result: {}", reply_str); | ||||||
|  |     SendReply(reply_str); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /// Modify location in memory with data received from the gdb client.
 | /// Modify location in memory with data received from the gdb client.
 | ||||||
|  | @ -1050,6 +1044,11 @@ void HandlePacket() { | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     if (HandlePendingHioRequestPacket()) { | ||||||
|  |         // Don't do anything else while we wait for the client to respond
 | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     if (!IsDataAvailable()) { |     if (!IsDataAvailable()) { | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  | @ -1059,7 +1058,7 @@ void HandlePacket() { | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     LOG_DEBUG(Debug_GDBStub, "Packet: {}", command_buffer[0]); |     LOG_DEBUG(Debug_GDBStub, "Packet: {0:d} ('{0:c}')", command_buffer[0]); | ||||||
| 
 | 
 | ||||||
|     switch (command_buffer[0]) { |     switch (command_buffer[0]) { | ||||||
|     case 'q': |     case 'q': | ||||||
|  | @ -1072,9 +1071,14 @@ void HandlePacket() { | ||||||
|         SendSignal(current_thread, latest_signal); |         SendSignal(current_thread, latest_signal); | ||||||
|         break; |         break; | ||||||
|     case 'k': |     case 'k': | ||||||
|         Shutdown(); |  | ||||||
|         LOG_INFO(Debug_GDBStub, "killed by gdb"); |         LOG_INFO(Debug_GDBStub, "killed by gdb"); | ||||||
|  |         ToggleServer(false); | ||||||
|  |         // Continue execution so we don't hang forever after shutting down the server
 | ||||||
|  |         Continue(); | ||||||
|         return; |         return; | ||||||
|  |     case 'F': | ||||||
|  |         HandleHioReply(command_buffer, command_length); | ||||||
|  |         break; | ||||||
|     case 'g': |     case 'g': | ||||||
|         ReadRegisters(); |         ReadRegisters(); | ||||||
|         break; |         break; | ||||||
|  | @ -1251,6 +1255,10 @@ bool GetCpuHaltFlag() { | ||||||
|     return halt_loop; |     return halt_loop; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | void SetCpuHaltFlag(bool halt) { | ||||||
|  |     halt_loop = halt; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| bool GetCpuStepFlag() { | bool GetCpuStepFlag() { | ||||||
|     return step_loop; |     return step_loop; | ||||||
| } | } | ||||||
|  | @ -1265,6 +1273,7 @@ void SendTrap(Kernel::Thread* thread, int trap) { | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     current_thread = thread; |     current_thread = thread; | ||||||
|  | 
 | ||||||
|     SendSignal(thread, trap); |     SendSignal(thread, trap); | ||||||
| 
 | 
 | ||||||
|     halt_loop = true; |     halt_loop = true; | ||||||
|  |  | ||||||
|  | @ -90,6 +90,13 @@ bool CheckBreakpoint(VAddr addr, GDBStub::BreakpointType type); | ||||||
| // If set to true, the CPU will halt at the beginning of the next CPU loop.
 | // If set to true, the CPU will halt at the beginning of the next CPU loop.
 | ||||||
| bool GetCpuHaltFlag(); | bool GetCpuHaltFlag(); | ||||||
| 
 | 
 | ||||||
|  | /**
 | ||||||
|  |  * If set to true, the CPU will halt at the beginning of the next CPU loop. | ||||||
|  |  * | ||||||
|  |  * @param halt whether to halt on the next loop | ||||||
|  |  */ | ||||||
|  | void SetCpuHaltFlag(bool halt); | ||||||
|  | 
 | ||||||
| // If set to true and the CPU is halted, the CPU will step one instruction.
 | // If set to true and the CPU is halted, the CPU will step one instruction.
 | ||||||
| bool GetCpuStepFlag(); | bool GetCpuStepFlag(); | ||||||
| 
 | 
 | ||||||
|  | @ -107,4 +114,19 @@ void SetCpuStepFlag(bool is_step); | ||||||
|  * @param trap Trap no. |  * @param trap Trap no. | ||||||
|  */ |  */ | ||||||
| void SendTrap(Kernel::Thread* thread, int trap); | void SendTrap(Kernel::Thread* thread, int trap); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Send reply to gdb client. | ||||||
|  |  * | ||||||
|  |  * @param reply Reply to be sent to client. | ||||||
|  |  */ | ||||||
|  | void SendReply(const char* reply); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Converts input hex string characters into an array of equivalent of u8 bytes. | ||||||
|  |  * | ||||||
|  |  * @param src Pointer to array of output hex string characters. | ||||||
|  |  * @param len Length of src array. | ||||||
|  |  */ | ||||||
|  | u32 HexToInt(const u8* src, std::size_t len); | ||||||
| } // namespace GDBStub
 | } // namespace GDBStub
 | ||||||
|  |  | ||||||
							
								
								
									
										261
									
								
								src/core/gdbstub/hio.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										261
									
								
								src/core/gdbstub/hio.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,261 @@ | ||||||
|  | // Copyright 2023 Citra Emulator Project
 | ||||||
|  | // Licensed under GPLv2 or any later version
 | ||||||
|  | // Refer to the license.txt file included.
 | ||||||
|  | 
 | ||||||
|  | #include <fmt/ranges.h> | ||||||
|  | #include "common/string_util.h" | ||||||
|  | #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; | ||||||
|  | 
 | ||||||
|  | enum class Status { | ||||||
|  |     NoRequest, | ||||||
|  |     NotSent, | ||||||
|  |     SentWaitingReply, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | static std::atomic<Status> request_status{Status::NoRequest}; | ||||||
|  | 
 | ||||||
|  | static std::atomic<bool> was_halted = false; | ||||||
|  | static std::atomic<bool> was_stepping = false; | ||||||
|  | 
 | ||||||
|  | } // namespace
 | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * @return Whether the application has requested I/O, and it has not been sent. | ||||||
|  |  */ | ||||||
|  | static bool HasPendingHioRequest() { | ||||||
|  |     return current_hio_request_addr != 0 && request_status == Status::NotSent; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * @return Whether the GDB stub is awaiting a reply from the client. | ||||||
|  |  */ | ||||||
|  | static bool IsWaitingForHioReply() { | ||||||
|  |     return current_hio_request_addr != 0 && request_status == Status::SentWaitingReply; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Send a response indicating an error back to the application. | ||||||
|  |  * | ||||||
|  |  * @param error_code The error code to respond back to the app. This typically corresponds to errno. | ||||||
|  |  * | ||||||
|  |  * @param retval The return value of the syscall the app requested. | ||||||
|  |  */ | ||||||
|  | static void SendErrorReply(int error_code, int retval = -1) { | ||||||
|  |     auto packet = fmt::format("F{:x},{:x}", retval, error_code); | ||||||
|  |     SendReply(packet.data()); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void SetHioRequest(const VAddr addr) { | ||||||
|  |     if (!IsServerEnabled()) { | ||||||
|  |         LOG_WARNING(Debug_GDBStub, "HIO requested but GDB stub is not running"); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if (IsWaitingForHioReply()) { | ||||||
|  |         LOG_WARNING(Debug_GDBStub, "HIO requested while already in progress"); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if (HasPendingHioRequest()) { | ||||||
|  |         LOG_INFO(Debug_GDBStub, "overwriting existing HIO request that was not sent yet"); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     auto& memory = Core::System::GetInstance().Memory(); | ||||||
|  |     const auto process = Core::System::GetInstance().Kernel().GetCurrentProcess(); | ||||||
|  | 
 | ||||||
|  |     if (!memory.IsValidVirtualAddress(*process, addr)) { | ||||||
|  |         LOG_WARNING(Debug_GDBStub, "Invalid address for HIO request"); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     memory.ReadBlock(*process, addr, ¤t_hio_request, sizeof(PackedGdbHioRequest)); | ||||||
|  | 
 | ||||||
|  |     if (current_hio_request.magic != std::array{'G', 'D', 'B', '\0'}) { | ||||||
|  |         std::string_view bad_magic{ | ||||||
|  |             current_hio_request.magic.data(), | ||||||
|  |             current_hio_request.magic.size(), | ||||||
|  |         }; | ||||||
|  |         LOG_WARNING(Debug_GDBStub, "Invalid HIO request sent by application: bad magic '{}'", | ||||||
|  |                     bad_magic); | ||||||
|  | 
 | ||||||
|  |         current_hio_request_addr = 0; | ||||||
|  |         current_hio_request = {}; | ||||||
|  |         request_status = Status::NoRequest; | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     LOG_DEBUG(Debug_GDBStub, "HIO request initiated at 0x{:X}", addr); | ||||||
|  |     current_hio_request_addr = addr; | ||||||
|  |     request_status = Status::NotSent; | ||||||
|  | 
 | ||||||
|  |     was_halted = GetCpuHaltFlag(); | ||||||
|  |     was_stepping = GetCpuStepFlag(); | ||||||
|  | 
 | ||||||
|  |     // Now halt, so that no further instructions are executed until the request
 | ||||||
|  |     // is processed by the client. We will continue after the reply comes back
 | ||||||
|  |     Break(); | ||||||
|  |     SetCpuHaltFlag(true); | ||||||
|  |     SetCpuStepFlag(false); | ||||||
|  |     Core::GetRunningCore().ClearInstructionCache(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void HandleHioReply(const u8* const command_buffer, const u32 command_length) { | ||||||
|  |     if (!IsWaitingForHioReply()) { | ||||||
|  |         LOG_WARNING(Debug_GDBStub, "Got HIO reply but never sent a request"); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // Skip 'F' header
 | ||||||
|  |     auto* command_pos = command_buffer + 1; | ||||||
|  | 
 | ||||||
|  |     if (*command_pos == 0 || *command_pos == ',') { | ||||||
|  |         LOG_WARNING(Debug_GDBStub, "bad HIO packet format position 0: {}", *command_pos); | ||||||
|  |         SendErrorReply(EILSEQ); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // Set the sign of the retval
 | ||||||
|  |     if (*command_pos == '-') { | ||||||
|  |         command_pos++; | ||||||
|  |         current_hio_request.retval = -1; | ||||||
|  |     } else { | ||||||
|  |         if (*command_pos == '+') { | ||||||
|  |             command_pos++; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         current_hio_request.retval = 1; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     const std::string command_str{command_pos, command_buffer + command_length}; | ||||||
|  |     std::vector<std::string> command_parts; | ||||||
|  |     Common::SplitString(command_str, ',', command_parts); | ||||||
|  | 
 | ||||||
|  |     if (command_parts.empty() || command_parts.size() > 3) { | ||||||
|  |         LOG_WARNING(Debug_GDBStub, "Unexpected reply packet size: {}", command_parts); | ||||||
|  |         SendErrorReply(EILSEQ); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     u64 unsigned_retval = | ||||||
|  |         HexToInt(reinterpret_cast<const u8*>(command_parts[0].data()), command_parts[0].size()); | ||||||
|  |     current_hio_request.retval *= unsigned_retval; | ||||||
|  | 
 | ||||||
|  |     if (command_parts.size() > 1) { | ||||||
|  |         // Technically the errno could be signed but in practice this doesn't happen
 | ||||||
|  |         current_hio_request.gdb_errno = | ||||||
|  |             HexToInt(reinterpret_cast<const u8*>(command_parts[1].data()), command_parts[1].size()); | ||||||
|  |     } else { | ||||||
|  |         current_hio_request.gdb_errno = 0; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     current_hio_request.ctrl_c = false; | ||||||
|  | 
 | ||||||
|  |     if (command_parts.size() > 2 && !command_parts[2].empty()) { | ||||||
|  |         if (command_parts[2][0] != 'C') { | ||||||
|  |             LOG_WARNING(Debug_GDBStub, "expected ctrl-c flag got '{}'", command_parts[2][0]); | ||||||
|  |             SendErrorReply(EILSEQ); | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         // for now we just ignore any trailing ";..." attachments
 | ||||||
|  |         current_hio_request.ctrl_c = true; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     std::fill(std::begin(current_hio_request.param_format), | ||||||
|  |               std::end(current_hio_request.param_format), 0); | ||||||
|  | 
 | ||||||
|  |     LOG_TRACE(Debug_GDBStub, "HIO reply: {{retval = {}, errno = {}, ctrl_c = {}}}", | ||||||
|  |               current_hio_request.retval, current_hio_request.gdb_errno, | ||||||
|  |               current_hio_request.ctrl_c); | ||||||
|  | 
 | ||||||
|  |     const auto process = Core::System::GetInstance().Kernel().GetCurrentProcess(); | ||||||
|  |     auto& memory = Core::System::GetInstance().Memory(); | ||||||
|  | 
 | ||||||
|  |     // should have been checked when we first initialized the request,
 | ||||||
|  |     // but just double check again before we write to memory
 | ||||||
|  |     if (!memory.IsValidVirtualAddress(*process, current_hio_request_addr)) { | ||||||
|  |         LOG_WARNING(Debug_GDBStub, "Invalid address {:#X} to write HIO reply", | ||||||
|  |                     current_hio_request_addr); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     memory.WriteBlock(*process, current_hio_request_addr, ¤t_hio_request, | ||||||
|  |                       sizeof(PackedGdbHioRequest)); | ||||||
|  | 
 | ||||||
|  |     current_hio_request = {}; | ||||||
|  |     current_hio_request_addr = 0; | ||||||
|  |     request_status = Status::NoRequest; | ||||||
|  | 
 | ||||||
|  |     // Restore state from before the request came in
 | ||||||
|  |     SetCpuStepFlag(was_stepping); | ||||||
|  |     SetCpuHaltFlag(was_halted); | ||||||
|  |     Core::GetRunningCore().ClearInstructionCache(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool HandlePendingHioRequestPacket() { | ||||||
|  |     if (!HasPendingHioRequest()) { | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if (IsWaitingForHioReply()) { | ||||||
|  |         // We already sent it, continue waiting for a reply
 | ||||||
|  |         return true; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // We need a null-terminated string from char* instead of using
 | ||||||
|  |     // the full length of the array (like {.begin(), .end()} constructor would)
 | ||||||
|  |     std::string_view function_name{current_hio_request.function_name.data()}; | ||||||
|  | 
 | ||||||
|  |     std::string packet = fmt::format("F{}", function_name); | ||||||
|  | 
 | ||||||
|  |     u32 str_length_idx = 0; | ||||||
|  | 
 | ||||||
|  |     for (u32 i = 0; i < 8 && current_hio_request.param_format[i] != 0; i++) { | ||||||
|  |         u64 param = current_hio_request.parameters[i]; | ||||||
|  | 
 | ||||||
|  |         // TODO: should we use the IntToGdbHex funcs instead of fmt::format_to ?
 | ||||||
|  |         switch (current_hio_request.param_format[i]) { | ||||||
|  |         case 'i': | ||||||
|  |         case 'I': | ||||||
|  |         case 'p': | ||||||
|  |             // For pointers and 32-bit ints, truncate down to size before sending
 | ||||||
|  |             param = static_cast<u32>(param); | ||||||
|  |             [[fallthrough]]; | ||||||
|  | 
 | ||||||
|  |         case 'l': | ||||||
|  |         case 'L': | ||||||
|  |             fmt::format_to(std::back_inserter(packet), ",{:x}", param); | ||||||
|  |             break; | ||||||
|  | 
 | ||||||
|  |         case 's': | ||||||
|  |             // strings are written as {pointer}/{length}
 | ||||||
|  |             fmt::format_to(std::back_inserter(packet), ",{:x}/{:x}", | ||||||
|  |                            static_cast<u32>(current_hio_request.parameters[i]), | ||||||
|  |                            current_hio_request.string_lengths[str_length_idx++]); | ||||||
|  |             break; | ||||||
|  | 
 | ||||||
|  |         default: | ||||||
|  |             LOG_WARNING(Debug_GDBStub, "unexpected hio request param format '{}'", | ||||||
|  |                         current_hio_request.param_format[i]); | ||||||
|  |             SendErrorReply(EILSEQ); | ||||||
|  |             return false; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     LOG_TRACE(Debug_GDBStub, "HIO request packet: '{}'", packet); | ||||||
|  | 
 | ||||||
|  |     SendReply(packet.data()); | ||||||
|  |     request_status = Status::SentWaitingReply; | ||||||
|  |     return true; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | } // namespace GDBStub
 | ||||||
							
								
								
									
										64
									
								
								src/core/gdbstub/hio.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										64
									
								
								src/core/gdbstub/hio.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,64 @@ | ||||||
|  | // Copyright 2023 Citra Emulator Project
 | ||||||
|  | // Licensed under GPLv2 or any later version
 | ||||||
|  | // Refer to the license.txt file included.
 | ||||||
|  | 
 | ||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include "common/common_types.h" | ||||||
|  | 
 | ||||||
|  | namespace GDBStub { | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * A request from a debugged application to perform some I/O with the GDB client. | ||||||
|  |  * This structure is also used to encode the reply back to the application. | ||||||
|  |  * | ||||||
|  |  * Based on the Rosalina + libctru implementations: | ||||||
|  |  * https://github.com/LumaTeam/Luma3DS/blob/master/sysmodules/rosalina/include/gdb.h#L46C27-L62
 | ||||||
|  |  * https://github.com/devkitPro/libctru/blob/master/libctru/source/gdbhio.c#L71-L87
 | ||||||
|  |  */ | ||||||
|  | struct PackedGdbHioRequest { | ||||||
|  |     std::array<char, 4> magic; // "GDB\0"
 | ||||||
|  |     u32 version; | ||||||
|  | 
 | ||||||
|  | private: | ||||||
|  |     static inline constexpr std::size_t MAX_FUNCNAME_LEN = 16; | ||||||
|  |     static inline constexpr std::size_t PARAM_COUNT = 8; | ||||||
|  | 
 | ||||||
|  | public: | ||||||
|  |     // Request. Char arrays have +1 entry for null terminator
 | ||||||
|  |     std::array<char, MAX_FUNCNAME_LEN + 1> function_name; | ||||||
|  |     std::array<char, PARAM_COUNT + 1> param_format; | ||||||
|  | 
 | ||||||
|  |     std::array<u64, PARAM_COUNT> parameters; | ||||||
|  |     std::array<u32, PARAM_COUNT> string_lengths; | ||||||
|  | 
 | ||||||
|  |     // Return
 | ||||||
|  |     s64 retval; | ||||||
|  |     s32 gdb_errno; | ||||||
|  |     bool ctrl_c; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | static_assert(sizeof(PackedGdbHioRequest) == 152, | ||||||
|  |               "HIO request size must match libctru implementation"); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Set the current HIO request to the given address. This is how the debugged | ||||||
|  |  * app indicates to the gdbstub that it wishes to perform a request. | ||||||
|  |  * | ||||||
|  |  *  @param address The memory address of the \ref PackedGdbHioRequest. | ||||||
|  |  */ | ||||||
|  | void SetHioRequest(const VAddr address); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * If there is a pending HIO request, send it to the client. | ||||||
|  |  * | ||||||
|  |  * @returns whethere any request was sent to the client. | ||||||
|  |  */ | ||||||
|  | bool HandlePendingHioRequestPacket(); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Process an HIO reply from the client. | ||||||
|  |  */ | ||||||
|  | void HandleHioReply(const u8* const command_buffer, const u32 command_length); | ||||||
|  | 
 | ||||||
|  | } // 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,9 +1141,21 @@ 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 file I/O
 | ||||||
|  | /// (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) { |     if (!memory.IsValidVirtualAddress(*kernel.GetCurrentProcess(), address)) { | ||||||
|  |         LOG_WARNING(Kernel_SVC, "OutputDebugString called with invalid address {:X}", address); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if (len == 0) { | ||||||
|  |         GDBStub::SetHioRequest(address); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if (len < 0) { | ||||||
|  |         LOG_WARNING(Kernel_SVC, "OutputDebugString called with invalid length {}", len); | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue