Stop execution when performing HIO request
Since the HIO request is basically treated like a syscall, we need to stop emulation while waiting for the GDB client to reply with the result. This ensures any memory queries etc. that GDB makes to fulfill the HIO request are accessing memory as it was at the time of the request, instead of afterwards.
This commit is contained in:
parent
b9c11e71d7
commit
f92f494cab
|
@ -638,7 +638,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) {
|
||||||
|
@ -828,7 +828,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");
|
||||||
|
@ -845,6 +845,9 @@ static void ReadMemory() {
|
||||||
|
|
||||||
MemToGdbHex(reply, data.data(), len);
|
MemToGdbHex(reply, data.data(), len);
|
||||||
reply[len * 2] = '\0';
|
reply[len * 2] = '\0';
|
||||||
|
|
||||||
|
LOG_DEBUG(Debug_GDBStub, "ReadMemory result: {}", reply);
|
||||||
|
|
||||||
SendReply(reinterpret_cast<char*>(reply));
|
SendReply(reinterpret_cast<char*>(reply));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1046,21 +1049,6 @@ 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.
|
|
||||||
// ...This doesn't seem good enough for the general case
|
|
||||||
switch (command_buffer[0]) {
|
|
||||||
case 'c':
|
|
||||||
case 'C':
|
|
||||||
case 's':
|
|
||||||
if (HasHioRequest()) {
|
|
||||||
// Really, this request needs to get sent _after_ the step or continue
|
|
||||||
// began, but not sure how to schedule that...
|
|
||||||
const auto request_packet = BuildHioRequestPacket();
|
|
||||||
SendReply(request_packet.data());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (command_buffer[0]) {
|
switch (command_buffer[0]) {
|
||||||
case 'q':
|
case 'q':
|
||||||
HandleQuery();
|
HandleQuery();
|
||||||
|
@ -1077,8 +1065,8 @@ void HandlePacket() {
|
||||||
return;
|
return;
|
||||||
case 'F':
|
case 'F':
|
||||||
if (HandleHioReply(command_buffer, command_length)) {
|
if (HandleHioReply(command_buffer, command_length)) {
|
||||||
Continue();
|
// TODO figure out how to "resume" the last command
|
||||||
};
|
}
|
||||||
break;
|
break;
|
||||||
case 'g':
|
case 'g':
|
||||||
ReadRegisters();
|
ReadRegisters();
|
||||||
|
@ -1256,6 +1244,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;
|
||||||
}
|
}
|
||||||
|
@ -1270,7 +1262,13 @@ void SendTrap(Kernel::Thread* thread, int trap) {
|
||||||
}
|
}
|
||||||
|
|
||||||
current_thread = thread;
|
current_thread = thread;
|
||||||
SendSignal(thread, trap);
|
|
||||||
|
if (HasPendingHioRequest()) {
|
||||||
|
const auto request_packet = BuildHioRequestPacket();
|
||||||
|
SendReply(request_packet.data());
|
||||||
|
} else {
|
||||||
|
SendSignal(thread, trap);
|
||||||
|
}
|
||||||
|
|
||||||
halt_loop = true;
|
halt_loop = true;
|
||||||
send_trap = false;
|
send_trap = false;
|
||||||
|
|
|
@ -90,6 +90,8 @@ 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();
|
||||||
|
|
||||||
|
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();
|
||||||
|
|
||||||
|
|
|
@ -12,7 +12,14 @@ 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};
|
|
||||||
|
enum class Status {
|
||||||
|
NoRequest,
|
||||||
|
NotSent,
|
||||||
|
SentWaitingReply,
|
||||||
|
};
|
||||||
|
|
||||||
|
static std::atomic<Status> request_status{Status::NoRequest};
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
|
@ -40,15 +47,23 @@ void SetHioRequest(const VAddr addr) {
|
||||||
LOG_WARNING(Debug_GDBStub, "Invalid HIO request sent by application");
|
LOG_WARNING(Debug_GDBStub, "Invalid HIO request sent by application");
|
||||||
current_hio_request_addr = 0;
|
current_hio_request_addr = 0;
|
||||||
current_hio_request = {};
|
current_hio_request = {};
|
||||||
|
request_status = Status::NoRequest;
|
||||||
} else {
|
} else {
|
||||||
|
LOG_DEBUG(Debug_GDBStub, "HIO request initiated at 0x{:X}", addr);
|
||||||
current_hio_request_addr = addr;
|
current_hio_request_addr = addr;
|
||||||
sent_request = false;
|
request_status = Status::NotSent;
|
||||||
LOG_DEBUG(Debug_GDBStub, "HIO request initiated");
|
|
||||||
|
// Now halt, so that no further instructions are executed until the request
|
||||||
|
// is processed by the client. We auto-continue after the reply comes back
|
||||||
|
Break();
|
||||||
|
SetCpuHaltFlag(true);
|
||||||
|
SetCpuStepFlag(false);
|
||||||
|
Core::GetRunningCore().ClearInstructionCache();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool HandleHioReply(const u8* const command_buffer, const u32 command_length) {
|
bool HandleHioReply(const u8* const command_buffer, const u32 command_length) {
|
||||||
if (current_hio_request_addr == 0 || !sent_request) {
|
if (!WaitingForHioReply()) {
|
||||||
LOG_WARNING(Debug_GDBStub, "Got HIO reply but never sent a 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;
|
||||||
|
@ -73,10 +88,6 @@ bool HandleHioReply(const u8* const command_buffer, const u32 command_length) {
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto retval_end = std::find(command_pos, command_buffer + command_length, ',');
|
const auto retval_end = std::find(command_pos, command_buffer + command_length, ',');
|
||||||
if (retval_end >= command_buffer + command_length) {
|
|
||||||
// return GDB_ReplyErrno(ctx, EILSEQ);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
u64 retval = (u64)HexToInt(command_pos, retval_end - command_pos);
|
u64 retval = (u64)HexToInt(command_pos, retval_end - command_pos);
|
||||||
command_pos = retval_end + 1;
|
command_pos = retval_end + 1;
|
||||||
|
|
||||||
|
@ -86,7 +97,7 @@ bool HandleHioReply(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 should never happen.
|
||||||
const auto errno_end = std::find(command_pos, command_buffer + command_length, ',');
|
const auto errno_end = std::find(command_pos, command_buffer + command_length, ',');
|
||||||
errno_ = HexToInt(command_pos, errno_end - command_pos);
|
errno_ = HexToInt(command_pos, errno_end - command_pos);
|
||||||
command_pos = errno_end + 1;
|
command_pos = errno_end + 1;
|
||||||
|
@ -123,15 +134,19 @@ bool HandleHioReply(const u8* const command_buffer, const u32 command_length) {
|
||||||
memory.WriteBlock(*process, current_hio_request_addr, ¤t_hio_request,
|
memory.WriteBlock(*process, current_hio_request_addr, ¤t_hio_request,
|
||||||
sizeof(PackedGdbHioRequest));
|
sizeof(PackedGdbHioRequest));
|
||||||
|
|
||||||
current_hio_request = PackedGdbHioRequest{};
|
current_hio_request = {};
|
||||||
current_hio_request_addr = 0;
|
current_hio_request_addr = 0;
|
||||||
sent_request = false;
|
request_status = Status::NoRequest;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool HasHioRequest() {
|
bool HasPendingHioRequest() {
|
||||||
return current_hio_request_addr != 0 && !sent_request;
|
return current_hio_request_addr != 0 && request_status == Status::NotSent;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool WaitingForHioReply() {
|
||||||
|
return current_hio_request_addr != 0 && request_status == Status::SentWaitingReply;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string BuildHioRequestPacket() {
|
std::string BuildHioRequestPacket() {
|
||||||
|
@ -163,7 +178,8 @@ std::string BuildHioRequestPacket() {
|
||||||
}
|
}
|
||||||
|
|
||||||
LOG_DEBUG(Debug_GDBStub, "HIO request packet: {}", packet.str());
|
LOG_DEBUG(Debug_GDBStub, "HIO request packet: {}", packet.str());
|
||||||
sent_request = true;
|
|
||||||
|
request_status = Status::SentWaitingReply;
|
||||||
|
|
||||||
return packet.str();
|
return packet.str();
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,8 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
|
|
||||||
namespace GDBStub {
|
namespace GDBStub {
|
||||||
|
@ -29,7 +31,9 @@ void SetHioRequest(const VAddr address);
|
||||||
|
|
||||||
bool HandleHioReply(const u8* const command_buffer, const u32 command_length);
|
bool HandleHioReply(const u8* const command_buffer, const u32 command_length);
|
||||||
|
|
||||||
bool HasHioRequest();
|
bool HasPendingHioRequest();
|
||||||
|
|
||||||
|
bool WaitingForHioReply();
|
||||||
|
|
||||||
std::string BuildHioRequestPacket();
|
std::string BuildHioRequestPacket();
|
||||||
|
|
||||||
|
|
|
@ -1144,6 +1144,11 @@ void SVC::Break(u8 break_reason) {
|
||||||
/// Used to output a message on a debug hardware unit, or for the GDB HIO
|
/// Used to output a message on a debug hardware unit, or for the GDB HIO
|
||||||
// protocol - does nothing on a retail unit.
|
// protocol - does nothing on a retail unit.
|
||||||
void SVC::OutputDebugString(VAddr address, s32 len) {
|
void SVC::OutputDebugString(VAddr address, s32 len) {
|
||||||
|
if (!Memory::IsValidVirtualAddress(*kernel.GetCurrentProcess(), address)) {
|
||||||
|
LOG_WARNING(Kernel_SVC, "OutputDebugString called with invalid address {:X}", address);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (len == 0) {
|
if (len == 0) {
|
||||||
GDBStub::SetHioRequest(address);
|
GDBStub::SetHioRequest(address);
|
||||||
return;
|
return;
|
||||||
|
|
Loading…
Reference in New Issue