Initial port of luma3ds' gdb_hio to Citra
This commit is contained in:
parent
5346ca27b5
commit
6f23ee43ae
|
@ -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;
|
||||||
|
|
|
@ -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
|
|
@ -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"},
|
||||||
|
|
Reference in New Issue