thermopshere: gdb: rewrite stop point handling

This commit is contained in:
TuxSH
2020-01-23 22:00:39 +00:00
parent 5de05ed8a8
commit 779aeaa538
14 changed files with 94 additions and 388 deletions

View File

@@ -1,116 +0,0 @@
/*
* This file is part of Luma3DS.
* Copyright (C) 2016-2019 Aurora Wright, TuxSH
*
* SPDX-License-Identifier: (MIT OR GPL-2.0-or-later)
*/
#include "gdb/breakpoints.h"
#define _REENT_ONLY
#include <errno.h>
u32 GDB_FindClosestBreakpointSlot(GDBContext *ctx, u32 address)
{
if(ctx->nbBreakpoints == 0 || address <= ctx->breakpoints[0].address)
return 0;
else if(address > ctx->breakpoints[ctx->nbBreakpoints - 1].address)
return ctx->nbBreakpoints;
u32 a = 0, b = ctx->nbBreakpoints - 1, m;
do
{
m = (a + b) / 2;
if(ctx->breakpoints[m].address < address)
a = m;
else if(ctx->breakpoints[m].address > address)
b = m;
else
return m;
}
while(b - a > 1);
return b;
}
int GDB_GetBreakpointInstruction(u32 *instruction, GDBContext *ctx, u32 address)
{
u32 id = GDB_FindClosestBreakpointSlot(ctx, address);
if(id == ctx->nbBreakpoints || ctx->breakpoints[id].address != address)
return -EINVAL;
if(instruction != NULL)
*instruction = ctx->breakpoints[id].savedInstruction;
return 0;
}
int GDB_AddBreakpoint(GDBContext *ctx, u32 address, bool thumb, bool persist)
{
if(!thumb && (address & 3) != 0)
return -EINVAL;
address &= ~1;
u32 id = GDB_FindClosestBreakpointSlot(ctx, address);
if(id != ctx->nbBreakpoints && ctx->breakpoints[id].instructionSize != 0 && ctx->breakpoints[id].address == address)
return 0;
else if(ctx->nbBreakpoints == MAX_BREAKPOINT)
return -EBUSY;
for(u32 i = ctx->nbBreakpoints; i > id && i != 0; i--)
ctx->breakpoints[i] = ctx->breakpoints[i - 1];
ctx->nbBreakpoints++;
Breakpoint *bkpt = &ctx->breakpoints[id];
u32 instr = thumb ? BREAKPOINT_INSTRUCTION_THUMB : BREAKPOINT_INSTRUCTION_ARM;
if(R_FAILED(svcReadProcessMemory(&bkpt->savedInstruction, ctx->debug, address, thumb ? 2 : 4)) ||
R_FAILED(svcWriteProcessMemory(ctx->debug, &instr, address, thumb ? 2 : 4)))
{
for(u32 i = id; i < ctx->nbBreakpoints - 1; i++)
ctx->breakpoints[i] = ctx->breakpoints[i + 1];
memset(&ctx->breakpoints[--ctx->nbBreakpoints], 0, sizeof(Breakpoint));
return -EFAULT;
}
bkpt->instructionSize = thumb ? 2 : 4;
bkpt->address = address;
bkpt->persistent = persist;
return 0;
}
int GDB_DisableBreakpointById(GDBContext *ctx, u32 id)
{
Breakpoint *bkpt = &ctx->breakpoints[id];
if(R_FAILED(svcWriteProcessMemory(ctx->debug, &bkpt->savedInstruction, bkpt->address, bkpt->instructionSize)))
return -EFAULT;
else return 0;
}
int GDB_RemoveBreakpoint(GDBContext *ctx, u32 address)
{
address &= ~1;
u32 id = GDB_FindClosestBreakpointSlot(ctx, address);
if(id == ctx->nbBreakpoints || ctx->breakpoints[id].address != address)
return -EINVAL;
int r = GDB_DisableBreakpointById(ctx, id);
if(r != 0)
return r;
else
{
for(u32 i = id; i < ctx->nbBreakpoints - 1; i++)
ctx->breakpoints[i] = ctx->breakpoints[i + 1];
memset(&ctx->breakpoints[--ctx->nbBreakpoints], 0, sizeof(Breakpoint));
return 0;
}
}

View File

@@ -1,20 +0,0 @@
/*
* This file is part of Luma3DS.
* Copyright (C) 2016-2019 Aurora Wright, TuxSH
*
* SPDX-License-Identifier: (MIT OR GPL-2.0-or-later)
*/
#pragma once
#include "gdb.h"
// We'll actually use SVC 0xFF for breakpoints :P
#define BREAKPOINT_INSTRUCTION_ARM 0xEF0000FF
#define BREAKPOINT_INSTRUCTION_THUMB 0xDFFF
u32 GDB_FindClosestBreakpointSlot(GDBContext *ctx, u32 address);
int GDB_GetBreakpointInstruction(u32 *instr, GDBContext *ctx, u32 address);
int GDB_AddBreakpoint(GDBContext *ctx, u32 address, bool thumb, bool persist);
int GDB_DisableBreakpointById(GDBContext *ctx, u32 id);
int GDB_RemoveBreakpoint(GDBContext *ctx, u32 address);

View File

@@ -16,7 +16,7 @@
#include "gdb/hio.h"
#include "gdb/watchpoints.h"
#include "gdb/breakpoints.h"
#include "gdb/stop_point.h"
#include "gdb/stop_points.h"
void GDB_InitializeServer(GDBServer *server)
{

View File

@@ -1,52 +0,0 @@
/*
* This file is part of Luma3DS.
* Copyright (C) 2016-2019 Aurora Wright, TuxSH
*
* SPDX-License-Identifier: (MIT OR GPL-2.0-or-later)
*/
#include "gdb.h"
#include "gdb/net.h"
#include "gdb/watchpoints.h"
#include "gdb/breakpoints.h"
GDB_DECLARE_HANDLER(ToggleStopPoint)
{
bool add = ctx->commandData[-1] == 'Z';
u32 lst[3];
const char *pos = GDB_ParseHexIntegerList(lst, ctx->commandData, 3, ';');
if(pos == NULL)
return GDB_ReplyErrno(ctx, EILSEQ);
bool persist = *pos != 0 && strncmp(pos, ";cmds:1", 7) == 0;
u32 kind = lst[0];
u32 addr = lst[1];
u32 size = lst[2];
int res;
static const WatchpointKind kinds[3] = { WATCHPOINT_WRITE, WATCHPOINT_READ, WATCHPOINT_READWRITE };
switch(kind)
{
case 0: // software breakpoint
if(size != 2 && size != 4)
return GDB_ReplyEmpty(ctx);
else
{
res = add ? GDB_AddBreakpoint(ctx, addr, size == 2, persist) :
GDB_RemoveBreakpoint(ctx, addr);
return res == 0 ? GDB_ReplyOk(ctx) : GDB_ReplyErrno(ctx, -res);
}
// Watchpoints
case 2:
case 3:
case 4:
res = add ? GDB_AddWatchpoint(ctx, addr, size, kinds[kind - 2]) :
GDB_RemoveWatchpoint(ctx, addr, kinds[kind - 2]);
return res == 0 ? GDB_ReplyOk(ctx) : GDB_ReplyErrno(ctx, -res);
default:
return GDB_ReplyEmpty(ctx);
}
}

View File

@@ -0,0 +1,70 @@
/*
* This file is part of Luma3DS.
* Copyright (C) 2016-2019 Aurora Wright, TuxSH
*
* SPDX-License-Identifier: (MIT OR GPL-2.0-or-later)
*/
#include <string.h>
#include "../gdb.h"
#include "net.h"
#include "../breakpoints.h"
#include "../software_breakpoints.h"
#include "../watchpoints.h"
GDB_DECLARE_HANDLER(ToggleStopPoint)
{
bool add = ctx->commandData[-1] == 'Z';
unsigned long lst[3];
const char *pos = GDB_ParseHexIntegerList(lst, ctx->commandData, 3, ';');
if (pos == NULL) {
return GDB_ReplyErrno(ctx, EILSEQ);
}
bool persist = *pos != 0 && strncmp(pos, ";cmds:1", 7) == 0;
// In theory we should reject leading zeroes in "kind". Oh well...
unsigned long kind = lst[0];
uintptr_t addr = lst[1];
size_t size = lst[2];
int res;
static const WatchpointLoadStoreControl kinds[3] = {
WatchpointLoadStoreControl_Store,
WatchpointLoadStoreControl_Load,
WatchpointLoadStoreControl_LoadStore,
};
switch(kind) {
// Software breakpoint
case 0: {
if(size != 4) {
return GDB_ReplyErrno(ctx, EINVAL);
}
res = add ? addSoftwareBreakpoint(addr, persist) : removeSoftwareBreakpoint(addr, false);
return res == 0 ? GDB_ReplyOk(ctx) : GDB_ReplyErrno(ctx, -res);
}
// Hardware breakpoint
case 1: {
if(size != 4) {
return GDB_ReplyErrno(ctx, EINVAL);
}
res = add ? addBreakpoint(addr) : removeBreakpoint(addr);
return res == 0 ? GDB_ReplyOk(ctx) : GDB_ReplyErrno(ctx, -res);
}
// Watchpoints
case 2:
case 3:
case 4: {
res = add ? addWatchpoint(addr, size, kinds[kind - 2]) : removeWatchpoint(addr, size, kinds[kind - 2]);
return res == 0 ? GDB_ReplyOk(ctx) : GDB_ReplyErrno(ctx, -res);
}
default: {
return GDB_ReplyEmpty(ctx);
}
}
}

View File

@@ -7,6 +7,6 @@
#pragma once
#include "gdb.h"
#include "../gdb.h"
GDB_DECLARE_HANDLER(ToggleStopPoint);

View File

@@ -1,151 +0,0 @@
/*
* This file is part of Luma3DS.
* Copyright (C) 2016-2019 Aurora Wright, TuxSH
*
* SPDX-License-Identifier: (MIT OR GPL-2.0-or-later)
*/
#include "gdb/watchpoints.h"
#include "csvc.h"
#define _REENT_ONLY
#include <errno.h>
/*
There are only 2 Watchpoint Register Pairs on MPCORE ARM11 CPUs,
and only 2 Breakpoint Register Pairs with context ID capabilities (BRP4-5) as well.
We'll reserve and use all 4 of them
*/
RecursiveLock watchpointManagerLock;
typedef struct Watchpoint
{
u32 address;
u32 size;
WatchpointKind kind;
Handle debug; // => context ID
} Watchpoint;
typedef struct WatchpointManager
{
u32 total;
Watchpoint watchpoints[2];
} WatchpointManager;
static WatchpointManager manager;
void GDB_ResetWatchpoints(void)
{
static bool lockInitialized = false;
if(!lockInitialized)
{
RecursiveLock_Init(&watchpointManagerLock);
lockInitialized = true;
}
recursiveSpinlockLock(&watchpointManagerLock);
svcKernelSetState(0x10003); // enable monitor mode debugging
svcKernelSetState(0x10004, 0); // disable watchpoint 0
svcKernelSetState(0x10004, 1); // disable watchpoint 1
memset(&manager, 0, sizeof(WatchpointManager));
recursiveSpinlockUnlock(&watchpointManagerLock);
}
int GDB_AddWatchpoint(GDBContext *ctx, u32 address, u32 size, WatchpointKind kind)
{
recursiveSpinlockLock(&watchpointManagerLock);
u32 offset = address - (address & ~3);
if(manager.total == 2)
return -EBUSY;
if(size == 0 || (offset + size) > 4 || kind == WATCHPOINT_DISABLED)
return -EINVAL;
if(GDB_GetWatchpointKind(ctx, address) != WATCHPOINT_DISABLED)
// Disallow duplicate watchpoints: the kernel doesn't give us sufficient info to differentiate them by kind (DFSR)
return -EINVAL;
u32 id = manager.watchpoints[0].kind == WATCHPOINT_DISABLED ? 0 : 1;
u32 selectMask = ((1 << size) - 1) << offset;
u32 WCR = (1 << 20) | /* linked */
((4 + id) << 16) | /* ID of the linked BRP */
(selectMask << 5) | /* byte address select */
((u32)kind << 3) | /* kind */
(2 << 1) | /* user mode only */
(1 << 0) ; /* enabled */
s64 out;
Result r = svcGetHandleInfo(&out, ctx->debug, 0x10000); // context ID
if(R_SUCCEEDED(r))
{
svcKernelSetState(id == 0 ? 0x10005 : 0x10006, address, WCR, (u32)out); // set watchpoint
Watchpoint *watchpoint = &manager.watchpoints[id];
manager.total++;
watchpoint->address = address;
watchpoint->size = size;
watchpoint->kind = kind;
watchpoint->debug = ctx->debug;
ctx->watchpoints[ctx->nbWatchpoints++] = address;
recursiveSpinlockUnlock(&watchpointManagerLock);
return 0;
}
else
{
recursiveSpinlockUnlock(&watchpointManagerLock);
return -EINVAL;
}
}
int GDB_RemoveWatchpoint(GDBContext *ctx, u32 address, WatchpointKind kind)
{
recursiveSpinlockLock(&watchpointManagerLock);
u32 id;
for(id = 0; id < 2 && manager.watchpoints[id].address != address && manager.watchpoints[id].debug != ctx->debug; id++);
if(id == 2 || (kind != WATCHPOINT_DISABLED && manager.watchpoints[id].kind != kind))
{
recursiveSpinlockUnlock(&watchpointManagerLock);
return -EINVAL;
}
else
{
svcKernelSetState(0x10004, id); // disable watchpoint
memset(&manager.watchpoints[id], 0, sizeof(Watchpoint));
manager.total--;
if(ctx->watchpoints[0] == address)
{
ctx->watchpoints[0] = ctx->watchpoints[1];
ctx->watchpoints[1] = 0;
ctx->nbWatchpoints--;
}
else if(ctx->watchpoints[1] == address)
{
ctx->watchpoints[1] = 0;
ctx->nbWatchpoints--;
}
recursiveSpinlockUnlock(&watchpointManagerLock);
return 0;
}
}
WatchpointKind GDB_GetWatchpointKind(GDBContext *ctx, u32 address)
{
u32 id;
for(id = 0; id < 2 && (manager.watchpoints[id].address != address || manager.watchpoints[id].debug != ctx->debug); id++);
return id == 2 ? WATCHPOINT_DISABLED : manager.watchpoints[id].kind;
}

View File

@@ -1,25 +0,0 @@
/*
* This file is part of Luma3DS.
* Copyright (C) 2016-2019 Aurora Wright, TuxSH
*
* SPDX-License-Identifier: (MIT OR GPL-2.0-or-later)
*/
#pragma once
#include "gdb.h"
typedef enum WatchpointKind
{
WATCHPOINT_DISABLED = 0,
WATCHPOINT_READ,
WATCHPOINT_WRITE,
WATCHPOINT_READWRITE
} WatchpointKind;
void GDB_ResetWatchpoints(void); // needed for software breakpoints to be detected as debug events as well
int GDB_AddWatchpoint(GDBContext *ctx, u32 address, u32 size, WatchpointKind kind);
int GDB_RemoveWatchpoint(GDBContext *ctx, u32 address, WatchpointKind kind);
WatchpointKind GDB_GetWatchpointKind(GDBContext *ctx, u32 address);