2022-12-03 21:09:13 +00:00
|
|
|
// Copyright 2022 Citra Emulator Project
|
|
|
|
// Licensed under GPLv2 or any later version
|
|
|
|
// Refer to the license.txt file included.
|
|
|
|
|
2022-12-03 01:31:31 +00:00
|
|
|
#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;
|
2022-12-04 20:34:06 +00:00
|
|
|
|
|
|
|
enum class Status {
|
|
|
|
NoRequest,
|
|
|
|
NotSent,
|
|
|
|
SentWaitingReply,
|
|
|
|
};
|
|
|
|
|
|
|
|
static std::atomic<Status> request_status{Status::NoRequest};
|
2022-12-03 01:31:31 +00:00
|
|
|
|
|
|
|
} // 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;
|
|
|
|
}
|
|
|
|
|
2022-12-03 21:09:13 +00:00
|
|
|
const auto process = Core::System::GetInstance().Kernel().GetCurrentProcess();
|
|
|
|
if (!Memory::IsValidVirtualAddress(*process, addr)) {
|
2022-12-03 01:31:31 +00:00
|
|
|
LOG_WARNING(Debug_GDBStub, "Invalid address for HIO request");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2022-12-03 21:09:13 +00:00
|
|
|
auto& memory = Core::System::GetInstance().Memory();
|
|
|
|
memory.ReadBlock(*process, addr, ¤t_hio_request, sizeof(PackedGdbHioRequest));
|
|
|
|
|
2022-12-03 21:48:21 +00:00
|
|
|
if (std::string_view{current_hio_request.magic} != "GDB") {
|
|
|
|
LOG_WARNING(Debug_GDBStub, "Invalid HIO request sent by application");
|
|
|
|
current_hio_request_addr = 0;
|
|
|
|
current_hio_request = {};
|
2022-12-04 20:34:06 +00:00
|
|
|
request_status = Status::NoRequest;
|
2022-12-03 21:48:21 +00:00
|
|
|
} else {
|
2022-12-04 20:34:06 +00:00
|
|
|
LOG_DEBUG(Debug_GDBStub, "HIO request initiated at 0x{:X}", addr);
|
2022-12-03 21:48:21 +00:00
|
|
|
current_hio_request_addr = addr;
|
2022-12-04 20:34:06 +00:00
|
|
|
request_status = Status::NotSent;
|
|
|
|
|
|
|
|
// 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();
|
2022-12-03 21:48:21 +00:00
|
|
|
}
|
2022-12-03 01:31:31 +00:00
|
|
|
}
|
|
|
|
|
2022-12-03 21:09:13 +00:00
|
|
|
bool HandleHioReply(const u8* const command_buffer, const u32 command_length) {
|
2022-12-04 20:34:06 +00:00
|
|
|
if (!WaitingForHioReply()) {
|
2022-12-03 21:09:13 +00:00
|
|
|
LOG_WARNING(Debug_GDBStub, "Got HIO reply but never sent a request");
|
2022-12-03 01:31:31 +00:00
|
|
|
// TODO send error reply packet?
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2022-12-03 21:09:13 +00:00
|
|
|
auto* command_pos = command_buffer + 1;
|
2022-12-03 01:31:31 +00:00
|
|
|
|
|
|
|
if (*command_pos == 0 || *command_pos == ',') {
|
|
|
|
// return GDB_ReplyErrno(ctx, EILSEQ);
|
|
|
|
return false;
|
2022-12-03 21:09:13 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Set the sign of the retval
|
|
|
|
if (*command_pos == '-') {
|
2022-12-03 01:31:31 +00:00
|
|
|
command_pos++;
|
|
|
|
current_hio_request.retval = -1;
|
|
|
|
} else if (*command_pos == '+') {
|
|
|
|
command_pos++;
|
|
|
|
current_hio_request.retval = 1;
|
|
|
|
} else {
|
|
|
|
current_hio_request.retval = 1;
|
|
|
|
}
|
|
|
|
|
2022-12-03 21:09:13 +00:00
|
|
|
const auto retval_end = std::find(command_pos, command_buffer + command_length, ',');
|
|
|
|
u64 retval = (u64)HexToInt(command_pos, retval_end - command_pos);
|
|
|
|
command_pos = retval_end + 1;
|
2022-12-03 01:31:31 +00:00
|
|
|
|
|
|
|
current_hio_request.retval *= retval;
|
|
|
|
current_hio_request.gdb_errno = 0;
|
|
|
|
current_hio_request.ctrl_c = 0;
|
|
|
|
|
|
|
|
if (*command_pos != 0) {
|
|
|
|
u32 errno_;
|
2022-12-04 20:34:06 +00:00
|
|
|
// GDB protocol technically allows errno to have a +/- prefix but this should never happen.
|
2022-12-03 21:09:13 +00:00
|
|
|
const auto errno_end = std::find(command_pos, command_buffer + command_length, ',');
|
|
|
|
errno_ = HexToInt(command_pos, errno_end - command_pos);
|
|
|
|
command_pos = errno_end + 1;
|
|
|
|
|
2022-12-03 01:31:31 +00:00
|
|
|
current_hio_request.gdb_errno = (int)errno_;
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
2022-12-03 21:09:13 +00:00
|
|
|
LOG_DEBUG(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();
|
|
|
|
// 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;
|
|
|
|
}
|
2022-12-03 01:31:31 +00:00
|
|
|
|
2022-12-03 21:09:13 +00:00
|
|
|
auto& memory = Core::System::GetInstance().Memory();
|
|
|
|
memory.WriteBlock(*process, current_hio_request_addr, ¤t_hio_request,
|
|
|
|
sizeof(PackedGdbHioRequest));
|
2022-12-03 01:31:31 +00:00
|
|
|
|
2022-12-04 20:34:06 +00:00
|
|
|
current_hio_request = {};
|
2022-12-03 01:31:31 +00:00
|
|
|
current_hio_request_addr = 0;
|
2022-12-04 20:34:06 +00:00
|
|
|
request_status = Status::NoRequest;
|
2022-12-03 01:31:31 +00:00
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2022-12-04 20:34:06 +00:00
|
|
|
bool HasPendingHioRequest() {
|
|
|
|
return current_hio_request_addr != 0 && request_status == Status::NotSent;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool WaitingForHioReply() {
|
|
|
|
return current_hio_request_addr != 0 && request_status == Status::SentWaitingReply;
|
2022-12-03 01:31:31 +00:00
|
|
|
}
|
|
|
|
|
2022-12-03 21:09:13 +00:00
|
|
|
std::string BuildHioRequestPacket() {
|
2022-12-03 21:48:21 +00:00
|
|
|
std::stringstream packet;
|
|
|
|
// TODO:use the IntToGdbHex funcs instead std::hex ?
|
|
|
|
packet << "F" << current_hio_request.function_name << std::hex;
|
2022-12-03 01:31:31 +00:00
|
|
|
|
2022-12-03 21:48:21 +00:00
|
|
|
u32 nStr = 0;
|
2022-12-03 01:31:31 +00:00
|
|
|
|
|
|
|
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':
|
2022-12-03 21:48:21 +00:00
|
|
|
packet << "," << (u32)current_hio_request.parameters[i];
|
2022-12-03 01:31:31 +00:00
|
|
|
break;
|
|
|
|
case 'l':
|
|
|
|
case 'L':
|
2022-12-03 21:48:21 +00:00
|
|
|
packet << "," << current_hio_request.parameters[i];
|
2022-12-03 01:31:31 +00:00
|
|
|
break;
|
|
|
|
case 's':
|
2022-12-03 21:48:21 +00:00
|
|
|
packet << "," << (u32)current_hio_request.parameters[i] << "/"
|
|
|
|
<< current_hio_request.string_lengths[nStr++];
|
2022-12-03 01:31:31 +00:00
|
|
|
break;
|
|
|
|
default:
|
2022-12-03 21:48:21 +00:00
|
|
|
packet << '\0';
|
2022-12-03 01:31:31 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-12-03 21:48:21 +00:00
|
|
|
LOG_DEBUG(Debug_GDBStub, "HIO request packet: {}", packet.str());
|
2022-12-04 20:34:06 +00:00
|
|
|
|
|
|
|
request_status = Status::SentWaitingReply;
|
2022-12-03 21:09:13 +00:00
|
|
|
|
2022-12-03 21:48:21 +00:00
|
|
|
return packet.str();
|
2022-12-03 01:31:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
} // namespace GDBStub
|