mirror of
				https://github.com/PabloMK7/citra.git
				synced 2025-10-31 13:50:03 +00:00 
			
		
		
		
	Properly parse incoming hio packet
This also does kind of a hacky way of sending HIO requests, since we don't have a direct way of signaling a request should be sent like the Rosalina implementation. To improve this, it could probably do some kind of signal sending which the main run loop handles instead of GDBStub::HandlePacket();
This commit is contained in:
		
							parent
							
								
									6f23ee43ae
								
							
						
					
					
						commit
						7de1bf3746
					
				
					 4 changed files with 94 additions and 57 deletions
				
			
		|  | @ -262,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]); | ||||||
|  | @ -319,12 +313,7 @@ static void IntToGdbHex(u8* dest, u32 v) { | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /**
 | u32 GdbHexToInt(const u8* src) { | ||||||
|  * Convert a gdb-formatted hex string into a u32. |  | ||||||
|  * |  | ||||||
|  * @param src Pointer to hex string. |  | ||||||
|  */ |  | ||||||
| static u32 GdbHexToInt(const u8* src) { |  | ||||||
|     u32 output = 0; |     u32 output = 0; | ||||||
| 
 | 
 | ||||||
|     for (int i = 0; i < 8; i += 2) { |     for (int i = 0; i < 8; i += 2) { | ||||||
|  | @ -348,12 +337,7 @@ static void LongToGdbHex(u8* dest, u64 v) { | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /**
 | u64 GdbHexToLong(const u8* src) { | ||||||
|  * Convert a gdb-formatted hex string into a u64. |  | ||||||
|  * |  | ||||||
|  * @param src Pointer to hex string. |  | ||||||
|  */ |  | ||||||
| static u64 GdbHexToLong(const u8* src) { |  | ||||||
|     u64 output = 0; |     u64 output = 0; | ||||||
| 
 | 
 | ||||||
|     for (int i = 0; i < 16; i += 2) { |     for (int i = 0; i < 16; i += 2) { | ||||||
|  | @ -1051,6 +1035,12 @@ void HandlePacket() { | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     if (HasHioRequest()) { | ||||||
|  |         const auto reply = BuildHioRequestPacket(); | ||||||
|  |         SendReply(reply.data()); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     if (!IsDataAvailable()) { |     if (!IsDataAvailable()) { | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  | @ -1064,15 +1054,13 @@ void HandlePacket() { | ||||||
| 
 | 
 | ||||||
|     // HACK: instead of polling DebugEvents properly via SVC, just check for
 |     // HACK: instead of polling DebugEvents properly via SVC, just check for
 | ||||||
|     // whether there's a pending a request, and send it if so.
 |     // whether there's a pending a request, and send it if so.
 | ||||||
|  |     // ...This doesn't seem good enough for the general case
 | ||||||
|     switch (command_buffer[0]) { |     switch (command_buffer[0]) { | ||||||
|     case 'c': |     case 'c': | ||||||
|     case 'C': |     case 'C': | ||||||
|     case 's': |     case 's': | ||||||
|         if (HasHioRequest()) { |         //
 | ||||||
|             const auto reply = BuildHioReply(); |         ; | ||||||
|             SendReply(reply.data()); |  | ||||||
|             return; |  | ||||||
|         } |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     switch (command_buffer[0]) { |     switch (command_buffer[0]) { | ||||||
|  | @ -1090,7 +1078,7 @@ void HandlePacket() { | ||||||
|         LOG_INFO(Debug_GDBStub, "killed by gdb"); |         LOG_INFO(Debug_GDBStub, "killed by gdb"); | ||||||
|         return; |         return; | ||||||
|     case 'F': |     case 'F': | ||||||
|         if (HandleHioRequest(command_buffer, command_length)) { |         if (HandleHioReply(command_buffer, command_length)) { | ||||||
|             Continue(); |             Continue(); | ||||||
|         }; |         }; | ||||||
|         break; |         break; | ||||||
|  |  | ||||||
|  | @ -107,4 +107,27 @@ 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); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * 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); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Convert a gdb-formatted hex string into a u32. | ||||||
|  |  * | ||||||
|  |  * @param src Pointer to hex string. | ||||||
|  |  */ | ||||||
|  | u32 GdbHexToInt(const u8* src); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Convert a gdb-formatted hex string into a u64. | ||||||
|  |  * | ||||||
|  |  * @param src Pointer to hex string. | ||||||
|  |  */ | ||||||
|  | u64 GdbHexToLong(const u8* src); | ||||||
|  | 
 | ||||||
| } // namespace GDBStub
 | } // namespace GDBStub
 | ||||||
|  |  | ||||||
|  | @ -1,3 +1,7 @@ | ||||||
|  | // Copyright 2022 Citra Emulator Project
 | ||||||
|  | // Licensed under GPLv2 or any later version
 | ||||||
|  | // Refer to the license.txt file included.
 | ||||||
|  | 
 | ||||||
| #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/gdbstub/hio.h" | ||||||
|  | @ -8,6 +12,7 @@ namespace { | ||||||
| 
 | 
 | ||||||
| static VAddr current_hio_request_addr; | static VAddr current_hio_request_addr; | ||||||
| static PackedGdbHioRequest current_hio_request; | static PackedGdbHioRequest current_hio_request; | ||||||
|  | static std::atomic<bool> sent_request{false}; | ||||||
| 
 | 
 | ||||||
| } // namespace
 | } // namespace
 | ||||||
| 
 | 
 | ||||||
|  | @ -22,35 +27,39 @@ void SetHioRequest(const VAddr addr) { | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     auto& memory = Core::System::GetInstance().Memory(); |     const auto process = Core::System::GetInstance().Kernel().GetCurrentProcess(); | ||||||
|     if (!memory.IsValidVirtualAddress(*Core::System::GetInstance().Kernel().GetCurrentProcess(), |     if (!Memory::IsValidVirtualAddress(*process, addr)) { | ||||||
|                                       addr)) { |  | ||||||
|         LOG_WARNING(Debug_GDBStub, "Invalid address for HIO request"); |         LOG_WARNING(Debug_GDBStub, "Invalid address for HIO request"); | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     memory.ReadBlock(addr, ¤t_hio_request, sizeof(PackedGdbHioRequest)); |     auto& memory = Core::System::GetInstance().Memory(); | ||||||
|  |     memory.ReadBlock(*process, addr, ¤t_hio_request, sizeof(PackedGdbHioRequest)); | ||||||
|  | 
 | ||||||
|  |     // TODO read + check request magic header
 | ||||||
|  | 
 | ||||||
|     current_hio_request_addr = addr; |     current_hio_request_addr = addr; | ||||||
|  |     sent_request = false; | ||||||
| 
 | 
 | ||||||
|     LOG_DEBUG(Debug_GDBStub, "HIO request initiated"); |     LOG_DEBUG(Debug_GDBStub, "HIO request initiated"); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool HandleHioRequest(const u8* const command_buffer, const u32 command_length) { | bool HandleHioReply(const u8* const command_buffer, const u32 command_length) { | ||||||
|     if (!HasHioRequest()) { |     if (current_hio_request_addr == 0 || !sent_request) { | ||||||
|  |         LOG_WARNING(Debug_GDBStub, "Got HIO reply but never sent a request"); | ||||||
|         // TODO send error reply packet?
 |         // TODO send error reply packet?
 | ||||||
|         return false; |         return false; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     u64 retval{0}; |     auto* command_pos = command_buffer + 1; | ||||||
| 
 | 
 | ||||||
|     auto* command_pos = command_buffer; |  | ||||||
|     ++command_pos; |  | ||||||
| 
 |  | ||||||
|     // TODO: not totally sure what's going on here...
 |  | ||||||
|     if (*command_pos == 0 || *command_pos == ',') { |     if (*command_pos == 0 || *command_pos == ',') { | ||||||
|         // return GDB_ReplyErrno(ctx, EILSEQ);
 |         // return GDB_ReplyErrno(ctx, EILSEQ);
 | ||||||
|         return false; |         return false; | ||||||
|     } else if (*command_pos == '-') { |     } | ||||||
|  | 
 | ||||||
|  |     // Set the sign of the retval
 | ||||||
|  |     if (*command_pos == '-') { | ||||||
|         command_pos++; |         command_pos++; | ||||||
|         current_hio_request.retval = -1; |         current_hio_request.retval = -1; | ||||||
|     } else if (*command_pos == '+') { |     } else if (*command_pos == '+') { | ||||||
|  | @ -60,13 +69,13 @@ bool HandleHioRequest(const u8* const command_buffer, const u32 command_length) | ||||||
|         current_hio_request.retval = 1; |         current_hio_request.retval = 1; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     // TODO:
 |     const auto retval_end = std::find(command_pos, command_buffer + command_length, ','); | ||||||
|     // pos = GDB_ParseHexIntegerList64(&retval, pos, 1, ',');
 |     if (retval_end >= command_buffer + command_length) { | ||||||
| 
 |  | ||||||
|     if (command_pos == nullptr) { |  | ||||||
|         // return GDB_ReplyErrno(ctx, EILSEQ);
 |         // return GDB_ReplyErrno(ctx, EILSEQ);
 | ||||||
|         return false; |         return false; | ||||||
|     } |     } | ||||||
|  |     u64 retval = (u64)HexToInt(command_pos, retval_end - command_pos); | ||||||
|  |     command_pos = retval_end + 1; | ||||||
| 
 | 
 | ||||||
|     current_hio_request.retval *= retval; |     current_hio_request.retval *= retval; | ||||||
|     current_hio_request.gdb_errno = 0; |     current_hio_request.gdb_errno = 0; | ||||||
|  | @ -75,13 +84,11 @@ bool HandleHioRequest(const u8* const command_buffer, const u32 command_length) | ||||||
|     if (*command_pos != 0) { |     if (*command_pos != 0) { | ||||||
|         u32 errno_; |         u32 errno_; | ||||||
|         // GDB protocol technically allows errno to have a +/- prefix but this will never happen.
 |         // GDB protocol technically allows errno to have a +/- prefix but this will never happen.
 | ||||||
|         // TODO:
 |         const auto errno_end = std::find(command_pos, command_buffer + command_length, ','); | ||||||
|         // pos = GDB_ParseHexIntegerList(&errno_, ++pos, 1, ',');
 |         errno_ = HexToInt(command_pos, errno_end - command_pos); | ||||||
|  |         command_pos = errno_end + 1; | ||||||
|  | 
 | ||||||
|         current_hio_request.gdb_errno = (int)errno_; |         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 != 0) { | ||||||
|             if (*command_pos != 'C') { |             if (*command_pos != 'C') { | ||||||
|  | @ -96,24 +103,35 @@ bool HandleHioRequest(const u8* const command_buffer, const u32 command_length) | ||||||
|     std::fill(std::begin(current_hio_request.param_format), |     std::fill(std::begin(current_hio_request.param_format), | ||||||
|               std::end(current_hio_request.param_format), 0); |               std::end(current_hio_request.param_format), 0); | ||||||
| 
 | 
 | ||||||
|     auto& memory = Core::System::GetInstance().Memory(); |     LOG_DEBUG(Debug_GDBStub, "HIO reply: {{retval = {}, errno = {}, ctrl_c = {}}}", | ||||||
|     // should have been checked when we first initialized the request:
 |               current_hio_request.retval, current_hio_request.gdb_errno, | ||||||
|     assert(memory.IsValidVirtualAddress(*Core::System::GetInstance().Kernel().GetCurrentProcess(), |               current_hio_request.ctrl_c); | ||||||
|                                         current_hio_request_addr)); |  | ||||||
| 
 | 
 | ||||||
|     memory.WriteBlock(current_hio_request_addr, ¤t_hio_request, sizeof(PackedGdbHioRequest)); |     const auto process = Core::System::GetInstance().Kernel().GetCurrentProcess(); | ||||||
|  |     // 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 request", | ||||||
|  |                     current_hio_request_addr); | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     auto& memory = Core::System::GetInstance().Memory(); | ||||||
|  |     memory.WriteBlock(*process, current_hio_request_addr, ¤t_hio_request, | ||||||
|  |                       sizeof(PackedGdbHioRequest)); | ||||||
| 
 | 
 | ||||||
|     current_hio_request = PackedGdbHioRequest{}; |     current_hio_request = PackedGdbHioRequest{}; | ||||||
|     current_hio_request_addr = 0; |     current_hio_request_addr = 0; | ||||||
|  |     sent_request = false; | ||||||
| 
 | 
 | ||||||
|     return true; |     return true; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool HasHioRequest() { | bool HasHioRequest() { | ||||||
|     return current_hio_request_addr != 0; |     return current_hio_request_addr != 0 && !sent_request; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| std::string BuildHioReply() { | std::string BuildHioRequestPacket() { | ||||||
|     char buf[256 + 1]; |     char buf[256 + 1]; | ||||||
|     char tmp[32 + 1]; |     char tmp[32 + 1]; | ||||||
|     u32 nStr = 0; |     u32 nStr = 0; | ||||||
|  | @ -144,7 +162,11 @@ std::string BuildHioReply() { | ||||||
|         strcat(buf, tmp); |         strcat(buf, tmp); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     return std::string{buf, strlen(buf)}; |     auto packet = std::string{buf, strlen(buf)}; | ||||||
|  |     LOG_DEBUG(Debug_GDBStub, "HIO request packet: {}", packet); | ||||||
|  |     sent_request = true; | ||||||
|  | 
 | ||||||
|  |     return packet; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| } // namespace GDBStub
 | } // namespace GDBStub
 | ||||||
|  |  | ||||||
|  | @ -1,3 +1,7 @@ | ||||||
|  | // Copyright 2022 Citra Emulator Project
 | ||||||
|  | // Licensed under GPLv2 or any later version
 | ||||||
|  | // Refer to the license.txt file included.
 | ||||||
|  | 
 | ||||||
| #pragma once | #pragma once | ||||||
| 
 | 
 | ||||||
| #include "common/common_types.h" | #include "common/common_types.h" | ||||||
|  | @ -23,10 +27,10 @@ struct PackedGdbHioRequest { | ||||||
| 
 | 
 | ||||||
| void SetHioRequest(const VAddr address); | void SetHioRequest(const VAddr address); | ||||||
| 
 | 
 | ||||||
| bool HandleHioRequest(const u8* const command_buffer, const u32 command_length); | bool HandleHioReply(const u8* const command_buffer, const u32 command_length); | ||||||
| 
 | 
 | ||||||
| bool HasHioRequest(); | bool HasHioRequest(); | ||||||
| 
 | 
 | ||||||
| std::string BuildHioReply(); | std::string BuildHioRequestPacket(); | ||||||
| 
 | 
 | ||||||
| } // namespace GDBStub
 | } // namespace GDBStub
 | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue