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

@ -69,7 +69,7 @@ static void freeBreakpoint(u32 pos)
g_breakpointManager.allocationBitmap |= BIT(pos);
}
static DebugRegisterPair *findBreakpoint(u64 addr)
static DebugRegisterPair *findBreakpoint(uintptr_t addr)
{
u16 bitmap = ~g_breakpointManager.allocationBitmap & 0xFFFF;
while (bitmap != 0) {
@ -90,7 +90,7 @@ static DebugRegisterPair *findBreakpoint(u64 addr)
// Note: A32/T32/T16 support intentionnally left out
// Note: addresses are supposed to be well-formed regarding the sign extension bits
int addBreakpoint(u64 addr)
int addBreakpoint(uintptr_t addr)
{
recursiveSpinlockLock(&g_breakpointManager.lock);
@ -127,7 +127,7 @@ int addBreakpoint(u64 addr)
return 0;
}
int removeBreakpoint(u64 addr)
int removeBreakpoint(uintptr_t addr)
{
recursiveSpinlockLock(&g_breakpointManager.lock);

View file

@ -33,6 +33,6 @@ typedef struct BreakpointManager {
extern BreakpointManager g_breakpointManager;
void initBreakpoints(void);
int addBreakpoint(u64 addr);
int removeBreakpoint(u64 addr);
int addBreakpoint(uintptr_t addr);
int removeBreakpoint(uintptr_t addr);
int removeAllBreakpoints(void);

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);

View file

@ -31,7 +31,7 @@ SoftwareBreakpointManager g_softwareBreakpointManager = {0};
We also define sw breakpoints on invalid addresses (for one or more cores) UNPREDICTABLE.
*/
static size_t findClosestSoftwareBreakpointSlot(u64 address)
static size_t findClosestSoftwareBreakpointSlot(uintptr_t address)
{
if(g_softwareBreakpointManager.numBreakpoints == 0 || address <= g_softwareBreakpointManager.breakpoints[0].address) {
return 0;
@ -154,7 +154,7 @@ bool revertAllSoftwareBreakpoints(void)
return ret;
}
int addSoftwareBreakpoint(u64 addr, bool persistent)
int addSoftwareBreakpoint(uintptr_t addr, bool persistent)
{
if ((addr & 3) != 0) {
return -EINVAL;
@ -189,7 +189,7 @@ int addSoftwareBreakpoint(u64 addr, bool persistent)
return rc;
}
int removeSoftwareBreakpoint(u64 addr, bool keepPersistent)
int removeSoftwareBreakpoint(uintptr_t addr, bool keepPersistent)
{
if ((addr & 3) != 0) {
return -EINVAL;

View file

@ -45,6 +45,6 @@ extern SoftwareBreakpointManager g_softwareBreakpointManager;
bool revertAllSoftwareBreakpoints(void);
bool applyAllSoftwareBreakpoints(void);
int addSoftwareBreakpoint(u64 addr, bool persistent);
int removeSoftwareBreakpoint(u64 addr, bool keepPersistent);
int addSoftwareBreakpoint(uintptr_t addr, bool persistent);
int removeSoftwareBreakpoint(uintptr_t addr, bool keepPersistent);
int removeAllSoftwareBreakpoints(bool keepPersistent);

View file

@ -60,7 +60,7 @@ static inline void commitAndBroadcastWatchpoints(void)
executeFunctionOnAllCores(commitAndBroadcastWatchpointHandler, NULL, true);
}
static DebugRegisterPair *findCombinedWatchpoint(u64 addr)
static DebugRegisterPair *findCombinedWatchpoint(uintptr_t addr)
{
addr &= ~7ull;
u16 bitmap = ~g_watchpointManager.allocationBitmap & 0xFFFF;
@ -91,7 +91,7 @@ static DebugRegisterPair *allocateCombinedWatchpoint(u16 *bitmap)
}
// Precondition: not a MASK-based watchpoint
static bool checkNormalWatchpointRange(u64 addr, size_t size)
static bool checkNormalWatchpointRange(uintptr_t addr, size_t size)
{
u16 bitmap = g_watchpointManager.allocationBitmap;
if (findCombinedWatchpoint(addr) == NULL) {
@ -101,7 +101,7 @@ static bool checkNormalWatchpointRange(u64 addr, size_t size)
}
// if it overlaps...
u64 addr2 = (addr + size) & ~7ull;
uintptr_t addr2 = (addr + size) & ~7ull;
if (addr2 != (addr & ~7ull)) {
if (findCombinedWatchpoint(addr2) == NULL) {
@ -112,7 +112,7 @@ static bool checkNormalWatchpointRange(u64 addr, size_t size)
return true;
}
static inline bool isRangeMaskWatchpoint(u64 addr, size_t size)
static inline bool isRangeMaskWatchpoint(uintptr_t addr, size_t size)
{
// size needs to be a power of 2, at least 8 (we'll only allow 16+ though), addr needs to be aligned.
bool ret = (size & (size - 1)) == 0 && size >= 16 && (addr & (size - 1)) == 0;
@ -152,7 +152,7 @@ static bool combineWatchpoint(const DebugRegisterPair *wp)
return true;
}
static DebugRegisterPair *doFindSplitWatchpoint(u64 addr, size_t size, WatchpointLoadStoreControl direction, bool strict)
static DebugRegisterPair *doFindSplitWatchpoint(uintptr_t addr, size_t size, WatchpointLoadStoreControl direction, bool strict)
{
// Note: we will use RES0 bit0_1 of wr in case of overlapping
for (u32 i = 0; i < g_watchpointManager.numSplitWatchpoints; i++) {
@ -189,7 +189,7 @@ static DebugRegisterPair *doFindSplitWatchpoint(u64 addr, size_t size, Watchpoin
return NULL;
}
DebugControlRegister retrieveSplitWatchpointConfig(u64 addr, size_t size, WatchpointLoadStoreControl direction, bool strict)
DebugControlRegister retrieveSplitWatchpointConfig(uintptr_t addr, size_t size, WatchpointLoadStoreControl direction, bool strict)
{
recursiveSpinlockLock(&g_watchpointManager.lock);
DebugRegisterPair *wp = doFindSplitWatchpoint(addr, size, direction, strict);
@ -201,7 +201,7 @@ DebugControlRegister retrieveSplitWatchpointConfig(u64 addr, size_t size, Watchp
return ret;
}
int addWatchpoint(u64 addr, size_t size, WatchpointLoadStoreControl direction)
int addWatchpoint(uintptr_t addr, size_t size, WatchpointLoadStoreControl direction)
{
if (size == 0) {
return -EINVAL;
@ -242,7 +242,7 @@ int addWatchpoint(u64 addr, size_t size, WatchpointLoadStoreControl direction)
return -EINVAL;
}
u64 addr2 = (addr + size) & ~7ull;
uintptr_t addr2 = (addr + size) & ~7ull;
size_t off1 = addr & 7ull;
size_t size1 = (addr != addr2) ? 8 - off1 : size;
size_t size2 = size - size1;
@ -290,7 +290,7 @@ static void combineAllCurrentWatchpoints(void)
}
}
int removeWatchpoint(u64 addr, size_t size, WatchpointLoadStoreControl direction)
int removeWatchpoint(uintptr_t addr, size_t size, WatchpointLoadStoreControl direction)
{
if (size == 0) {
return -EINVAL;

View file

@ -35,7 +35,7 @@ typedef struct WatchpointManager {
extern WatchpointManager g_watchpointManager;
void initWatchpoints(void);
DebugControlRegister retrieveSplitWatchpointConfig(u64 addr, size_t size, WatchpointLoadStoreControl direction, bool strict);
int addWatchpoint(u64 addr, size_t size, WatchpointLoadStoreControl direction);
int removeWatchpoint(u64 addr, size_t size, WatchpointLoadStoreControl direction);
DebugControlRegister retrieveSplitWatchpointConfig(uintptr_t addr, size_t size, WatchpointLoadStoreControl direction, bool strict);
int addWatchpoint(uintptr_t addr, size_t size, WatchpointLoadStoreControl direction);
int removeWatchpoint(uintptr_t addr, size_t size, WatchpointLoadStoreControl direction);
int removeAllWatchpoints(void);