mirror of
https://github.com/Atmosphere-NX/Atmosphere
synced 2024-12-22 12:21:18 +00:00
thermosphere: rewrite gdb/reg
This commit is contained in:
parent
97c4595a3a
commit
30a4a0d4c1
6 changed files with 170 additions and 105 deletions
|
@ -135,7 +135,7 @@ static inline void writeFrameRegisterZ(ExceptionStackFrame *frame, u32 id, u64 v
|
|||
}
|
||||
}
|
||||
|
||||
static inline uintptr_t exceptionGetSp(const ExceptionStackFrame *frame)
|
||||
static inline u64 *exceptionGetSpPtr(ExceptionStackFrame *frame)
|
||||
{
|
||||
// Note: the return value is more or less meaningless if we took an exception from A32...
|
||||
// We try our best to reflect which privilege level the exception was took from, nonetheless
|
||||
|
@ -149,7 +149,7 @@ static inline uintptr_t exceptionGetSp(const ExceptionStackFrame *frame)
|
|||
spEl0 = el == 2 || el == 0 || (m & 1) == 0; // note: frame->sp_el2 is aliased to frame->sp_el0
|
||||
}
|
||||
|
||||
return spEl0 ? frame->sp_el0 : frame->sp_el1;
|
||||
return spEl0 ? &frame->sp_el0 : &frame->sp_el1;
|
||||
}
|
||||
|
||||
bool spsrEvaluateConditionCode(u64 spsr, u32 conditionCode);
|
||||
|
|
|
@ -173,7 +173,7 @@ static int GDB_ParseExceptionFrame(char *out, const DebugEventInfo *info, int si
|
|||
n += sprintf(
|
||||
out + n,
|
||||
"1f:%016lx;20:%016lx;21:%08x",
|
||||
__builtin_bswap64(exceptionGetSp(frame)),
|
||||
__builtin_bswap64(*exceptionGetSpPtr(frame)),
|
||||
__builitin_bswap32((u32)frame->spsr_el2)
|
||||
);
|
||||
|
||||
|
|
|
@ -23,7 +23,7 @@ u8 GDB_ComputeChecksum(const char *packetData, size_t len)
|
|||
return (u8)cksum;
|
||||
}
|
||||
|
||||
void GDB_EncodeHex(char *dst, const void *src, size_t len)
|
||||
size_t GDB_EncodeHex(char *dst, const void *src, size_t len)
|
||||
{
|
||||
static const char *alphabet = "0123456789abcdef";
|
||||
const u8 *src8 = (const u8 *)src;
|
||||
|
@ -32,6 +32,8 @@ void GDB_EncodeHex(char *dst, const void *src, size_t len)
|
|||
dst[2 * i] = alphabet[(src8[i] & 0xf0) >> 4];
|
||||
dst[2 * i + 1] = alphabet[src8[i] & 0x0f];
|
||||
}
|
||||
|
||||
return 2 * len;
|
||||
}
|
||||
|
||||
static inline u32 GDB_DecodeHexDigit(char src, bool *ok)
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
#include <errno.h>
|
||||
|
||||
u8 GDB_ComputeChecksum(const char *packetData, size_t len);
|
||||
void GDB_EncodeHex(char *dst, const void *src, size_t len);
|
||||
size_t GDB_EncodeHex(char *dst, const void *src, size_t len);
|
||||
size_t GDB_DecodeHex(void *dst, const char *src, size_t len);
|
||||
size_t GDB_EscapeBinaryData(size_t *encodedCount, void *dst, const void *src, size_t len, size_t maxLen);
|
||||
size_t GDB_UnescapeBinaryData(void *dst, const void *src, size_t len);
|
||||
|
|
|
@ -5,149 +5,212 @@
|
|||
* SPDX-License-Identifier: (MIT OR GPL-2.0-or-later)
|
||||
*/
|
||||
|
||||
#include "gdb/regs.h"
|
||||
#include "gdb/net.h"
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "../exceptions.h"
|
||||
#include "../fpu.h"
|
||||
|
||||
#include "regs.h"
|
||||
#include "net.h"
|
||||
|
||||
// GDB treats cpsr, fpsr, fpcr as 32-bit integers...
|
||||
|
||||
GDB_DECLARE_HANDLER(ReadRegisters)
|
||||
{
|
||||
if(ctx->selectedThreadId == 0)
|
||||
ctx->selectedThreadId = ctx->currentThreadId;
|
||||
ENSURE(ctx->selectedThreadId == currentCoreCtx->coreId);
|
||||
|
||||
ThreadContext regs;
|
||||
Result r = svcGetDebugThreadContext(®s, ctx->debug, ctx->selectedThreadId, THREADCONTEXT_CONTROL_ALL);
|
||||
const ExceptionStackFrame *frame = currentCoreCtx->guestFrame;
|
||||
const FpuRegisterCache *fpuRegCache = fpuReadRegisters();
|
||||
|
||||
if(R_FAILED(r))
|
||||
return GDB_ReplyErrno(ctx, EPERM);
|
||||
char *buf = ctx->buffer + 1;
|
||||
size_t n = 0;
|
||||
|
||||
return GDB_SendHexPacket(ctx, ®s, sizeof(ThreadContext));
|
||||
struct PACKED ALIGN(4) {
|
||||
u64 sp;
|
||||
u64 pc;
|
||||
u32 cpsr;
|
||||
} cpuSprs = {
|
||||
.sp = *exceptionGetSpPtr(frame),
|
||||
.pc = frame->elr_el2,
|
||||
.cpsr = (u32)frame->spsr_el2,
|
||||
};
|
||||
static_assert(sizeof(cpuSprs) == 12, "sizeof(cpuSprs) != 12");
|
||||
|
||||
u32 fpuSprs[2] = {
|
||||
(u32)fpuRegCache->fpsr,
|
||||
(u32)fpuRegCache->fpcr,
|
||||
};
|
||||
|
||||
|
||||
n += GDB_EncodeHex(buf + n, frame->x, sizeof(frame->x));
|
||||
n += GDB_EncodeHex(buf + n, &cpuSprs, sizeof(cpuSprs));
|
||||
n += GDB_EncodeHex(buf + n, fpuRegCache->q, sizeof(fpuRegCache->q));
|
||||
n += GDB_EncodeHex(buf + n, fpuSprs, sizeof(fpuSprs));
|
||||
|
||||
return GDB_SendPacket(ctx, buf, n);
|
||||
}
|
||||
|
||||
GDB_DECLARE_HANDLER(WriteRegisters)
|
||||
{
|
||||
if(ctx->selectedThreadId == 0)
|
||||
ctx->selectedThreadId = ctx->currentThreadId;
|
||||
ENSURE(ctx->selectedThreadId == currentCoreCtx->coreId);
|
||||
|
||||
ThreadContext regs;
|
||||
ExceptionStackFrame *frame = currentCoreCtx->guestFrame;
|
||||
FpuRegisterCache *fpuRegCache = fpuGetRegisterCache();
|
||||
|
||||
if(GDB_DecodeHex(®s, ctx->commandData, sizeof(ThreadContext)) != sizeof(ThreadContext))
|
||||
return GDB_ReplyErrno(ctx, EPERM);
|
||||
char *buf = ctx->commandData;
|
||||
char *tmp = ctx->workBuffer;
|
||||
|
||||
Result r = svcSetDebugThreadContext(ctx->debug, ctx->selectedThreadId, ®s, THREADCONTEXT_CONTROL_ALL);
|
||||
if(R_FAILED(r))
|
||||
return GDB_ReplyErrno(ctx, EPERM);
|
||||
else
|
||||
return GDB_ReplyOk(ctx);
|
||||
size_t n = 0;
|
||||
size_t m = 0;
|
||||
|
||||
struct PACKED ALIGN(4) {
|
||||
u64 sp;
|
||||
u64 pc;
|
||||
u32 cpsr;
|
||||
} cpuSprs;
|
||||
static_assert(sizeof(cpuSprs) == 12, "sizeof(cpuSprs) != 12");
|
||||
|
||||
u32 fpuSprs[2];
|
||||
|
||||
struct {
|
||||
void *dst;
|
||||
size_t sz;
|
||||
} info[4] = {
|
||||
{ frame->x, sizeof(frame->x) },
|
||||
{ &cpuSprs, sizeof(cpuSprs) },
|
||||
{ fpuRegCache->q, sizeof(fpuRegCache->q) },
|
||||
{ fpuSprs, sizeof(fpuSprs) },
|
||||
};
|
||||
|
||||
// Parse & return on error
|
||||
for (u32 i = 0; i < 4; i++) {
|
||||
if (GDB_DecodeHex(tmp + m, buf + n, info[i].sz) != info[i].sz) {
|
||||
return GDB_ReplyErrno(ctx, EPERM);
|
||||
}
|
||||
n += 2 * info[i].sz;
|
||||
m += info[i].sz;
|
||||
}
|
||||
|
||||
// Copy. Note: we don't check if cpsr (spsr_el2) was modified to return to EL2...
|
||||
m = 0;
|
||||
for (u32 i = 0; i < 4; i++) {
|
||||
memcpy(info[i].dst, tmp + m, info[i].sz);
|
||||
m += info[i].sz;
|
||||
}
|
||||
*exceptionGetSpPtr(frame) = cpuSprs.sp;
|
||||
frame->elr_el2 = cpuSprs.pc;
|
||||
frame->spsr_el2 = cpuSprs.cpsr;
|
||||
fpuRegCache->fpsr = fpuSprs[0];
|
||||
fpuRegCache->fpcr = fpuSprs[1];
|
||||
fpuCommitRegisters();
|
||||
|
||||
return GDB_ReplyOk(ctx);
|
||||
}
|
||||
|
||||
static u32 GDB_ConvertRegisterNumber(ThreadContextControlFlags *flags, u32 gdbNum)
|
||||
static void GDB_GetRegisterPointerAndSize(size_t *outSz, void **outPtr, unsigned long id, ExceptionStackFrame *frame, FpuRegisterCache *fpuRegCache)
|
||||
{
|
||||
if(gdbNum <= 15)
|
||||
{
|
||||
*flags = (gdbNum >= 13) ? THREADCONTEXT_CONTROL_CPU_SPRS : THREADCONTEXT_CONTROL_CPU_GPRS;
|
||||
return gdbNum;
|
||||
}
|
||||
else if(gdbNum == 25)
|
||||
{
|
||||
*flags = THREADCONTEXT_CONTROL_CPU_SPRS;
|
||||
return 16;
|
||||
}
|
||||
else if(gdbNum >= 26 && gdbNum <= 41)
|
||||
{
|
||||
*flags = THREADCONTEXT_CONTROL_FPU_GPRS;
|
||||
return gdbNum - 26;
|
||||
}
|
||||
else if(gdbNum == 42 || gdbNum == 43)
|
||||
{
|
||||
*flags = THREADCONTEXT_CONTROL_FPU_SPRS;
|
||||
return gdbNum - 42;
|
||||
}
|
||||
else
|
||||
{
|
||||
*flags = (ThreadContextControlFlags)0;
|
||||
return 0;
|
||||
switch (id) {
|
||||
case 0 ... 30:
|
||||
*outPtr = &frame->x[id];
|
||||
*outSz = 8;
|
||||
break;
|
||||
case 31:
|
||||
*outPtr = exceptionGetSpPtr(frame);
|
||||
*outSz = 8;
|
||||
break;
|
||||
case 32:
|
||||
*outPtr = &frame->spsr_el2;
|
||||
*outSz = 4;
|
||||
break;
|
||||
case 33 ... 64:
|
||||
*outPtr = &fpuRegCache->q[id - 33];
|
||||
*outSz = 16;
|
||||
case 65:
|
||||
*outPtr = &fpuRegCache->fpsr;
|
||||
*outSz = 4;
|
||||
case 66:
|
||||
*outPtr = &fpuRegCache->fpcr;
|
||||
*outSz = 4;
|
||||
default:
|
||||
__builtin_unreachable();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
GDB_DECLARE_HANDLER(ReadRegister)
|
||||
{
|
||||
if(ctx->selectedThreadId == 0)
|
||||
ctx->selectedThreadId = ctx->currentThreadId;
|
||||
ENSURE(ctx->selectedThreadId == currentCoreCtx->coreId);
|
||||
|
||||
ThreadContext regs;
|
||||
ThreadContextControlFlags flags;
|
||||
u32 gdbRegNum;
|
||||
const ExceptionStackFrame *frame = currentCoreCtx->guestFrame;
|
||||
const FpuRegisterCache *fpuRegCache = NULL;
|
||||
|
||||
if(GDB_ParseHexIntegerList(&gdbRegNum, ctx->commandData, 1, 0) == NULL)
|
||||
char *buf = ctx->buffer + 1;
|
||||
unsigned long gdbRegNum;
|
||||
|
||||
if (GDB_ParseHexIntegerList(&gdbRegNum, ctx->commandData, 1, 0) == NULL) {
|
||||
return GDB_ReplyErrno(ctx, EILSEQ);
|
||||
}
|
||||
|
||||
u32 n = GDB_ConvertRegisterNumber(&flags, gdbRegNum);
|
||||
if(!flags)
|
||||
// Check the register number
|
||||
if (gdbRegNum >= 31 + 3 + 32 + 2) {
|
||||
return GDB_ReplyErrno(ctx, EINVAL);
|
||||
}
|
||||
|
||||
Result r = svcGetDebugThreadContext(®s, ctx->debug, ctx->selectedThreadId, flags);
|
||||
if (gdbRegNum > 31 + 3) {
|
||||
// FPU register -- must read the FPU registers first
|
||||
fpuRegCache = fpuReadRegisters();
|
||||
}
|
||||
|
||||
if(R_FAILED(r))
|
||||
return GDB_ReplyErrno(ctx, EPERM);
|
||||
size_t sz;
|
||||
void *regPtr;
|
||||
GDB_GetRegisterPointerAndSize(&sz, ®Ptr, gdbRegNum, frame, fpuRegCache);
|
||||
|
||||
if(flags & THREADCONTEXT_CONTROL_CPU_GPRS)
|
||||
return GDB_SendHexPacket(ctx, ®s.cpu_registers.r[n], 4);
|
||||
else if(flags & THREADCONTEXT_CONTROL_CPU_SPRS)
|
||||
return GDB_SendHexPacket(ctx, ®s.cpu_registers.sp + (n - 13), 4); // hacky
|
||||
else if(flags & THREADCONTEXT_CONTROL_FPU_GPRS)
|
||||
return GDB_SendHexPacket(ctx, ®s.fpu_registers.d[n], 8);
|
||||
else
|
||||
return GDB_SendHexPacket(ctx, ®s.fpu_registers.fpscr + n, 4); // hacky
|
||||
return GDB_SendHexPacket(ctx, regPtr, sz);
|
||||
}
|
||||
|
||||
GDB_DECLARE_HANDLER(WriteRegister)
|
||||
{
|
||||
if(ctx->selectedThreadId == 0)
|
||||
ctx->selectedThreadId = ctx->currentThreadId;
|
||||
ENSURE(ctx->selectedThreadId == currentCoreCtx->coreId);
|
||||
|
||||
ThreadContext regs;
|
||||
ThreadContextControlFlags flags;
|
||||
u32 gdbRegNum;
|
||||
ExceptionStackFrame *frame = currentCoreCtx->guestFrame;
|
||||
FpuRegisterCache *fpuRegCache = fpuGetRegisterCache();
|
||||
|
||||
char *tmp = ctx->workBuffer;
|
||||
unsigned long gdbRegNum;
|
||||
|
||||
const char *valueStart = GDB_ParseHexIntegerList(&gdbRegNum, ctx->commandData, 1, '=');
|
||||
if(valueStart == NULL || *valueStart != '=')
|
||||
if(valueStart == NULL || *valueStart != '=') {
|
||||
return GDB_ReplyErrno(ctx, EILSEQ);
|
||||
|
||||
}
|
||||
valueStart++;
|
||||
|
||||
u32 n = GDB_ConvertRegisterNumber(&flags, gdbRegNum);
|
||||
u32 value;
|
||||
u64 value64;
|
||||
|
||||
if(flags & THREADCONTEXT_CONTROL_FPU_GPRS)
|
||||
{
|
||||
if(GDB_DecodeHex(&value64, valueStart, 8) != 8 || valueStart[16] != 0)
|
||||
return GDB_ReplyErrno(ctx, EINVAL);
|
||||
}
|
||||
else if(flags)
|
||||
{
|
||||
if(GDB_DecodeHex(&value, valueStart, 4) != 4 || valueStart[8] != 0)
|
||||
return GDB_ReplyErrno(ctx, EINVAL);
|
||||
}
|
||||
else
|
||||
// Check the register number
|
||||
if (gdbRegNum >= 31 + 3 + 32 + 2) {
|
||||
return GDB_ReplyErrno(ctx, EINVAL);
|
||||
}
|
||||
|
||||
Result r = svcGetDebugThreadContext(®s, ctx->debug, ctx->selectedThreadId, flags);
|
||||
size_t sz;
|
||||
void *regPtr;
|
||||
GDB_GetRegisterPointerAndSize(&sz, ®Ptr, gdbRegNum, frame, fpuRegCache);
|
||||
|
||||
if(R_FAILED(r))
|
||||
return GDB_ReplyErrno(ctx, EPERM);
|
||||
// Check if we got 2 hex digits per byte
|
||||
if (strlen(valueStart) != 2 * sz) {
|
||||
return GDB_ReplyErrno(ctx, EILSEQ);
|
||||
}
|
||||
|
||||
if(flags & THREADCONTEXT_CONTROL_CPU_GPRS)
|
||||
regs.cpu_registers.r[n] = value;
|
||||
else if(flags & THREADCONTEXT_CONTROL_CPU_SPRS)
|
||||
*(®s.cpu_registers.sp + (n - 13)) = value; // hacky
|
||||
else if(flags & THREADCONTEXT_CONTROL_FPU_GPRS)
|
||||
memcpy(®s.fpu_registers.d[n], &value64, 8);
|
||||
else
|
||||
*(®s.fpu_registers.fpscr + n) = value; // hacky
|
||||
// Decode, check for errors
|
||||
if (GDB_DecodeHex(tmp, valueStart, sz) != sz) {
|
||||
return GDB_ReplyErrno(ctx, EILSEQ);
|
||||
}
|
||||
|
||||
memcpy(regPtr, tmp, sz);
|
||||
|
||||
if (gdbRegNum > 31 + 3) {
|
||||
// FPU register -- must commit the FPU registers
|
||||
fpuCommitRegisters();
|
||||
}
|
||||
|
||||
r = svcSetDebugThreadContext(ctx->debug, ctx->selectedThreadId, ®s, flags);
|
||||
if(R_FAILED(r))
|
||||
return GDB_ReplyErrno(ctx, EPERM);
|
||||
else
|
||||
return GDB_ReplyOk(ctx);
|
||||
}
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include "gdb.h"
|
||||
#include "../gdb.h"
|
||||
|
||||
GDB_DECLARE_HANDLER(ReadRegisters);
|
||||
GDB_DECLARE_HANDLER(WriteRegisters);
|
||||
|
|
Loading…
Reference in a new issue