mirror of
https://github.com/Atmosphere-NX/Atmosphere
synced 2025-01-03 11:11:14 +00:00
thermosphere: gdb add break & vCont handling
This commit is contained in:
parent
c0252e07f6
commit
cbf3b305ca
10 changed files with 204 additions and 86 deletions
|
@ -151,6 +151,11 @@ const DebugEventInfo *debugManagerMarkAndGetCoreDebugEvent(u32 coreId)
|
|||
return &g_debugManager.debugEventInfos[coreId];
|
||||
}
|
||||
|
||||
const DebugEventInfo *debugManagerGetCoreDebugEvent(u32 coreId)
|
||||
{
|
||||
return &g_debugManager.debugEventInfos[coreId];
|
||||
}
|
||||
|
||||
void debugManagerReportEvent(DebugEventType type, ...)
|
||||
{
|
||||
u64 flags = maskIrq();
|
||||
|
@ -186,3 +191,14 @@ void debugManagerReportEvent(DebugEventType type, ...)
|
|||
|
||||
restoreInterruptFlags(flags);
|
||||
}
|
||||
|
||||
void debugManagerBreakCores(u32 coreList)
|
||||
{
|
||||
u32 coreId = currentCoreCtx->coreId;
|
||||
if (coreList & ~BIT(coreId)) {
|
||||
generateSgiForList(ThermosphereSgi_ReportDebuggerBreak, coreList & ~BIT(coreId));
|
||||
}
|
||||
if (coreList & BIT(coreId)) {
|
||||
debugManagerReportEvent(DBGEVENT_DEBUGGER_BREAK);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -62,6 +62,11 @@ void debugManagerSetSteppingRange(u32 coreId, uintptr_t startAddr, uintptr_t end
|
|||
u32 debugManagerGetPausedCoreList(void);
|
||||
|
||||
const DebugEventInfo *debugManagerMarkAndGetCoreDebugEvent(u32 coreId);
|
||||
const DebugEventInfo *debugManagerGetCoreDebugEvent(u32 coreId);
|
||||
|
||||
void debugManagerReportEvent(DebugEventType type, ...);
|
||||
|
||||
void debugManagerBreakCores(u32 coreList);
|
||||
|
||||
static inline bool debugManagerIsCorePaused(u32 coreId)
|
||||
{
|
||||
|
|
|
@ -74,8 +74,8 @@ typedef struct GDBContext {
|
|||
u32 attachedCoreList;
|
||||
|
||||
u32 currentThreadId;
|
||||
u32 selectedThreadId;
|
||||
u32 selectedThreadIdForContinuing;
|
||||
int selectedThreadId;
|
||||
int selectedThreadIdForContinuing;
|
||||
|
||||
u32 sentDebugEventCoreList;
|
||||
|
||||
|
|
|
@ -235,6 +235,15 @@ int GDB_TrySignalDebugEvent(GDBContext *ctx, DebugEventInfo *info)
|
|||
return ret;
|
||||
}
|
||||
|
||||
void GDB_BreakAllCores(GDBContext *ctx)
|
||||
{
|
||||
if (ctx->flags & GDB_FLAG_NONSTOP) {
|
||||
debugManagerBreakCores(ctx->attachedCoreList);
|
||||
} else {
|
||||
debugManagerBreakCores(BIT(currentCoreCtx->coreId));
|
||||
}
|
||||
}
|
||||
|
||||
GDB_DECLARE_VERBOSE_HANDLER(Stopped)
|
||||
{
|
||||
u32 coreList = debugManagerGetPausedCoreList() & ctx->attachedCoreList;
|
||||
|
@ -282,102 +291,175 @@ GDB_DECLARE_HANDLER(Kill)
|
|||
return 0;
|
||||
}
|
||||
|
||||
GDB_DECLARE_HANDLER(Break)
|
||||
GDB_DECLARE_VERBOSE_HANDLER(CtrlC)
|
||||
{
|
||||
// TODO
|
||||
if(!(ctx->flags & GDB_FLAG_CONTINUING)) // Is this ever reached?
|
||||
return GDB_SendPacket(ctx, "S02", 3);
|
||||
else
|
||||
{
|
||||
ctx->flags &= ~GDB_FLAG_CONTINUING;
|
||||
return 0;
|
||||
}
|
||||
int ret = GDB_ReplyOk(ctx);
|
||||
GDB_BreakAllCores(ctx);
|
||||
}
|
||||
|
||||
void GDB_ContinueExecution(GDBContext *ctx)
|
||||
GDB_DECLARE_HANDLER(ContinueOrStepDeprecated)
|
||||
{
|
||||
ctx->selectedThreadId = ctx->selectedThreadIdForContinuing = 0;
|
||||
svcContinueDebugEvent(ctx->debug, ctx->continueFlags);
|
||||
ctx->flags |= GDB_FLAG_CONTINUING;
|
||||
}
|
||||
|
||||
GDB_DECLARE_HANDLER(Continue)
|
||||
{
|
||||
// TODO
|
||||
char *addrStart = NULL;
|
||||
u32 addr = 0;
|
||||
uintptr_t addr = 0;
|
||||
|
||||
if(ctx->selectedThreadIdForContinuing != 0 && ctx->selectedThreadIdForContinuing != ctx->currentThreadId)
|
||||
return 0;
|
||||
char cmd = ctx->commandData[-1];
|
||||
|
||||
if(ctx->commandData[-1] == 'C')
|
||||
{
|
||||
if(ctx->commandData[0] == 0 || ctx->commandData[1] == 0 || (ctx->commandData[2] != 0 && ctx->commandData[2] == ';'))
|
||||
// This deprecated command should not be permitted in non-stop mode
|
||||
if (ctx->flags & GDB_FLAG_NONSTOP) {
|
||||
return GDB_ReplyErrno(ctx, EPERM);
|
||||
}
|
||||
|
||||
if(cmd == 'C' || cmd == 'S') {
|
||||
// Check the presence of the two-digit signature, even if we ignore it.
|
||||
u8 sg;
|
||||
if (GDB_DecodeHex(&sg, ctx->commandData, 1) != 1) {
|
||||
return GDB_ReplyErrno(ctx, EILSEQ);
|
||||
}
|
||||
|
||||
// Signal ignored...
|
||||
// Check: [;addr] or [nothing]
|
||||
if (ctx->commandData[2] != 0 && ctx->commandData[2] != ';') {
|
||||
return GDB_ReplyErrno(ctx, EILSEQ);
|
||||
}
|
||||
|
||||
if(ctx->commandData[2] == ';')
|
||||
if(ctx->commandData[2] == ';') {
|
||||
addrStart = ctx->commandData + 3;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(ctx->commandData[0] != 0)
|
||||
}
|
||||
else {
|
||||
// 'c', 's'
|
||||
if (ctx->commandData[0] != 0) {
|
||||
addrStart = ctx->commandData;
|
||||
}
|
||||
|
||||
if(addrStart != NULL && ctx->currentThreadId != 0)
|
||||
{
|
||||
ThreadContext regs;
|
||||
if(GDB_ParseHexIntegerList(&addr, ctx->commandData + 3, 1, 0) == NULL)
|
||||
return GDB_ReplyErrno(ctx, EILSEQ);
|
||||
|
||||
Result r = svcGetDebugThreadContext(®s, ctx->debug, ctx->currentThreadId, THREADCONTEXT_CONTROL_CPU_SPRS);
|
||||
if(R_SUCCEEDED(r))
|
||||
{
|
||||
regs.cpu_registers.pc = addr;
|
||||
r = svcSetDebugThreadContext(ctx->debug, ctx->currentThreadId, ®s, THREADCONTEXT_CONTROL_CPU_SPRS);
|
||||
}
|
||||
}
|
||||
|
||||
GDB_ContinueExecution(ctx);
|
||||
// Only support the simplest form, with no address
|
||||
// Only degenerate clients will use ;addr, anyway (and the packets are deprecated in favor
|
||||
// of vCont anyway)
|
||||
|
||||
if (addrStart != NULL) {
|
||||
return GDB_ReplyErrno(ctx, ENOSYS);
|
||||
}
|
||||
|
||||
u32 coreList = ctx->selectedThreadIdForContinuing == -1 ? ctx->attachedCoreList : BIT(ctx->selectedThreadIdForContinuing);
|
||||
u32 ssMask = (cmd == 's' || cmd == 'S') ? coreList : 0;
|
||||
|
||||
FOREACH_BIT (tmp, coreId, ssMask) {
|
||||
debugManagerSetSteppingRange(coreId, 0, 0);
|
||||
}
|
||||
|
||||
debugManagerUnpauseCores(coreList, ssMask);
|
||||
return 0;
|
||||
}
|
||||
|
||||
GDB_DECLARE_VERBOSE_HANDLER(Continue)
|
||||
{
|
||||
// TODO
|
||||
char *pos = ctx->commandData;
|
||||
bool currentThreadFound = false;
|
||||
while(pos != NULL && *pos != 0 && !currentThreadFound)
|
||||
{
|
||||
if(*pos != 'c' && *pos != 'C')
|
||||
return GDB_ReplyErrno(ctx, EPERM);
|
||||
u32 parsedCoreList = 0;
|
||||
u32 continueCoreList = 0;
|
||||
u32 stepCoreList = 0;
|
||||
u32 stopCoreList = 0;
|
||||
|
||||
pos += *pos == 'C' ? 3 : 1;
|
||||
char *cmd = ctx->commandData;
|
||||
|
||||
if(*pos++ != ':') // default action found
|
||||
{
|
||||
currentThreadFound = true;
|
||||
while (cmd != NULL) {
|
||||
char *nextCmd;
|
||||
char *threadIdPart;
|
||||
int threadId;
|
||||
u32 curMask = 0;
|
||||
char *cmdEnd;
|
||||
|
||||
// It it always fine if we set the single-stepping range to 0,0 by default
|
||||
// Because the fields we set are the shadow fields copied to the real fields after debug unpause
|
||||
uintptr_t ssStartAddr = 0;
|
||||
uintptr_t ssEndAddr = 0;
|
||||
|
||||
// Locate next command, replace delimiter by NUL
|
||||
nextCmd = strchr(cmd, ';');
|
||||
if (nextCmd != NULL && *nextCmd == ';') {
|
||||
*nextCmd++ = 0;
|
||||
}
|
||||
|
||||
// Locate thread-id part, parse thread id
|
||||
threadIdPart = strchr(cmd, ':');
|
||||
if (threadIdPart == NULL || strcmp(threadIdPart, "-1") == 0) {
|
||||
// Default action...
|
||||
threadId = -1;
|
||||
curMask = ctx->attachedCoreList;
|
||||
} else {
|
||||
unsigned long id;
|
||||
if(GDB_ParseHexIntegerList(&id, ctx->commandData + 1, 1, 0) == NULL) {
|
||||
return GDB_ReplyErrno(ctx, EILSEQ);
|
||||
} else if (id >= MAX_CORE + 1) {
|
||||
return GDB_ReplyErrno(ctx, EINVAL);
|
||||
}
|
||||
|
||||
threadId = id == 0 ? (int)currentCoreCtx->coreId : (int)id;
|
||||
curMask = BIT(threadId - 1) & ctx->attachedCoreList;
|
||||
}
|
||||
|
||||
// Parse the command itself
|
||||
// Note that we may already have handled that thread in a previous command
|
||||
curMask &= ~parsedCoreList;
|
||||
switch (cmd[0]) {
|
||||
case 'S':
|
||||
case 'C': {
|
||||
// Check the presence of the two-digit signature, even if we ignore it.
|
||||
u8 sg;
|
||||
if (GDB_DecodeHex(&sg, ctx->commandData, 1) != 1) {
|
||||
return GDB_ReplyErrno(ctx, EILSEQ);
|
||||
}
|
||||
stepCoreList |= cmd[0] == 'S' ? curMask : 0;
|
||||
continueCoreList |= curMask;
|
||||
cmdEnd = cmd + 3;
|
||||
break;
|
||||
}
|
||||
case 's':
|
||||
stepCoreList |= curMask;
|
||||
continueCoreList |= curMask;
|
||||
cmdEnd = cmd + 1;
|
||||
break;
|
||||
case 'c':
|
||||
continueCoreList |= curMask;
|
||||
cmdEnd = cmd + 1;
|
||||
break;
|
||||
case 't':
|
||||
stopCoreList |= curMask;
|
||||
cmdEnd = cmd + 1;
|
||||
break;
|
||||
case 'r': {
|
||||
// Range step
|
||||
unsigned long tmp[2];
|
||||
cmdEnd = GDB_ParseHexIntegerList(tmp, cmd, 2, 0);
|
||||
if (cmdEnd == NULL) {
|
||||
return GDB_ReplyErrno(ctx, EILSEQ);
|
||||
}
|
||||
|
||||
ssStartAddr = tmp[0];
|
||||
ssEndAddr = tmp[1];
|
||||
stepCoreList |= curMask;
|
||||
continueCoreList |= curMask;
|
||||
break;
|
||||
}
|
||||
|
||||
char *nextpos = (char *)strchr(pos, ';');
|
||||
if(strncmp(pos, "-1", 2) == 0)
|
||||
currentThreadFound = true;
|
||||
else
|
||||
{
|
||||
u32 threadId;
|
||||
if(GDB_ParseHexIntegerList(&threadId, pos, 1, ';') == NULL)
|
||||
default:
|
||||
return GDB_ReplyErrno(ctx, EILSEQ);
|
||||
currentThreadFound = currentThreadFound || threadId == ctx->currentThreadId;
|
||||
}
|
||||
|
||||
pos = nextpos;
|
||||
if (*cmdEnd != 0) {
|
||||
// We've got garbage data...
|
||||
return GDB_ReplyErrno(ctx, EILSEQ);
|
||||
}
|
||||
|
||||
if(ctx->currentThreadId == 0 || currentThreadFound)
|
||||
GDB_ContinueExecution(ctx);
|
||||
FOREACH_BIT (tmp, t, curMask) {
|
||||
// Set/unset stepping range for all threads affected by this command
|
||||
debugManagerSetSteppingRange(t - 1, ssStartAddr, ssEndAddr);
|
||||
}
|
||||
|
||||
parsedCoreList |= curMask;
|
||||
cmd = nextCmd;
|
||||
}
|
||||
|
||||
debugManagerBreakCores(stopCoreList);
|
||||
debugManagerUnpauseCores(continueCoreList, stepCoreList);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -19,8 +19,8 @@ GDB_DECLARE_VERBOSE_HANDLER(Stopped);
|
|||
|
||||
GDB_DECLARE_HANDLER(Detach);
|
||||
GDB_DECLARE_HANDLER(Kill);
|
||||
GDB_DECLARE_HANDLER(Break);
|
||||
GDB_DECLARE_HANDLER(Continue);
|
||||
GDB_DECLARE_VERBOSE_HANDLER(CtrlC);
|
||||
GDB_DECLARE_HANDLER(ContinueOrStepDeprecated);
|
||||
GDB_DECLARE_VERBOSE_HANDLER(Continue);
|
||||
GDB_DECLARE_HANDLER(GetStopReason);
|
||||
|
||||
|
|
|
@ -13,27 +13,33 @@
|
|||
|
||||
GDB_DECLARE_HANDLER(SetThreadId)
|
||||
{
|
||||
// Id = 0 means any thread
|
||||
if (ctx->commandData[0] == 'g') {
|
||||
if(strncmp(ctx->commandData + 1, "-1", 2) == 0) {
|
||||
if(strcmp(ctx->commandData + 1, "-1") == 0) {
|
||||
return GDB_ReplyErrno(ctx, EINVAL);
|
||||
}
|
||||
|
||||
unsigned long id;
|
||||
if (GDB_ParseHexIntegerList(&id, ctx->commandData + 1, 1, 0) == NULL) {
|
||||
return GDB_ReplyErrno(ctx, EILSEQ);
|
||||
} else if (id >= MAX_CORE + 1) {
|
||||
return GDB_ReplyErrno(ctx, EINVAL);
|
||||
}
|
||||
|
||||
ctx->selectedThreadId = id;
|
||||
ctx->selectedThreadId = id == 0 ? (int)currentCoreCtx->coreId + 1 : (int)id;
|
||||
// TODO: change irq affinity (and remove selectedThreadId?)
|
||||
return GDB_ReplyOk(ctx);
|
||||
} else if (ctx->commandData[0] == 'c') {
|
||||
if(strncmp(ctx->commandData + 1, "-1", 2) == 0) {
|
||||
ctx->selectedThreadIdForContinuing = 0;
|
||||
if(strcmp(ctx->commandData + 1, "-1") == 0) {
|
||||
ctx->selectedThreadIdForContinuing = -1;
|
||||
} else {
|
||||
unsigned long id;
|
||||
if(GDB_ParseHexIntegerList(&id, ctx->commandData + 1, 1, 0) == NULL)
|
||||
if (GDB_ParseHexIntegerList(&id, ctx->commandData + 1, 1, 0) == NULL) {
|
||||
return GDB_ReplyErrno(ctx, EILSEQ);
|
||||
ctx->selectedThreadIdForContinuing = id;
|
||||
} else if (id >= MAX_CORE + 1) {
|
||||
return GDB_ReplyErrno(ctx, EINVAL);
|
||||
}
|
||||
ctx->selectedThreadIdForContinuing = id == 0 ? (int)currentCoreCtx->coreId + 1 : (int)id;
|
||||
}
|
||||
|
||||
return GDB_ReplyOk(ctx);
|
||||
|
|
|
@ -13,11 +13,12 @@
|
|||
|
||||
static const struct {
|
||||
const char *name;
|
||||
char trailingCharacter;
|
||||
GDBCommandHandler handler;
|
||||
} gdbVerboseCommandHandlers[] = {
|
||||
{ "Cont?", GDB_VERBOSE_HANDLER(ContinueSupported) },
|
||||
{ "Cont", GDB_VERBOSE_HANDLER(Continue) },
|
||||
{ "MustReplyEmpty", GDB_HANDLER(Unsupported) },
|
||||
{ "Cont?", '\0', GDB_VERBOSE_HANDLER(ContinueSupported) },
|
||||
{ "Cont", ';', GDB_VERBOSE_HANDLER(Continue) },
|
||||
{ "MustReplyEmpty", '\0', GDB_HANDLER(Unsupported) },
|
||||
};
|
||||
|
||||
GDB_DECLARE_HANDLER(VerboseCommand)
|
||||
|
@ -31,6 +32,7 @@ GDB_DECLARE_HANDLER(VerboseCommand)
|
|||
char *vData = NULL;
|
||||
|
||||
for (nameEnd = nameBegin; *nameEnd != 0 && *nameEnd != ';' && *nameEnd != ':'; nameEnd++);
|
||||
char oldNameEnd = *nameEnd;
|
||||
if (*nameEnd != 0) {
|
||||
*nameEnd = 0;
|
||||
vData = nameEnd + 1;
|
||||
|
@ -39,7 +41,11 @@ GDB_DECLARE_HANDLER(VerboseCommand)
|
|||
for (size_t i = 0; i < sizeof(gdbVerboseCommandHandlers) / sizeof(gdbVerboseCommandHandlers[0]); i++) {
|
||||
if (strcmp(gdbVerboseCommandHandlers[i].name, nameBegin) == 0) {
|
||||
ctx->commandData = vData;
|
||||
if (oldNameEnd == gdbVerboseCommandHandlers[i].trailingCharacter) {
|
||||
return gdbVerboseCommandHandlers[i].handler(ctx);
|
||||
} else {
|
||||
return GDB_ReplyErrno(ctx, EILSEQ);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -220,7 +220,6 @@ void handleIrqException(ExceptionStackFrame *frame, bool isLowerEl, bool isA32)
|
|||
|
||||
bool isGuestInterrupt = false;
|
||||
bool isMaintenanceInterrupt = false;
|
||||
bool hasBottomHalf = false;
|
||||
|
||||
switch (irqId) {
|
||||
case ThermosphereSgi_ExecuteFunction:
|
||||
|
@ -232,6 +231,9 @@ void handleIrqException(ExceptionStackFrame *frame, bool isLowerEl, bool isA32)
|
|||
case ThermosphereSgi_DebugPause:
|
||||
debugManagerPauseSgiHandler();
|
||||
break;
|
||||
case ThermosphereSgi_ReportDebuggerBreak:
|
||||
// See bottom half
|
||||
break;
|
||||
case GIC_IRQID_MAINTENANCE:
|
||||
isMaintenanceInterrupt = true;
|
||||
break;
|
||||
|
@ -244,7 +246,6 @@ void handleIrqException(ExceptionStackFrame *frame, bool isLowerEl, bool isA32)
|
|||
}
|
||||
|
||||
TransportInterface *transportIface = irqId >= 32 ? transportInterfaceIrqHandlerTopHalf(irqId) : NULL;
|
||||
hasBottomHalf = hasBottomHalf || transportIface != NULL;
|
||||
|
||||
// Priority drop
|
||||
gicc->eoir = iar;
|
||||
|
@ -269,12 +270,12 @@ void handleIrqException(ExceptionStackFrame *frame, bool isLowerEl, bool isA32)
|
|||
recursiveSpinlockUnlock(&g_irqManager.lock);
|
||||
|
||||
// Bottom half part
|
||||
if (hasBottomHalf) {
|
||||
if (transportIface != NULL) {
|
||||
exceptionEnterInterruptibleHypervisorCode();
|
||||
unmaskIrq();
|
||||
if (transportIface != NULL) {
|
||||
transportInterfaceIrqHandlerBottomHalf(transportIface);
|
||||
}
|
||||
} else if (irqId == ThermosphereSgi_ReportDebuggerBreak) {
|
||||
debugManagerReportEvent(DBGEVENT_DEBUGGER_BREAK);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -41,6 +41,7 @@ typedef enum ThermosphereSgi {
|
|||
ThermosphereSgi_ExecuteFunction = 0,
|
||||
ThermosphereSgi_VgicUpdate = 1,
|
||||
ThermosphereSgi_DebugPause = 2,
|
||||
ThermosphereSgi_ReportDebuggerBreak = 3,
|
||||
|
||||
ThermosphereSgi_Max,
|
||||
} ThermosphereSgi;
|
||||
|
|
|
@ -95,6 +95,7 @@ _postMmuEnableReturnAddr:
|
|||
|
||||
mov x0, sp
|
||||
mov x1, x20
|
||||
str x0, [x18, #CORECTX_GUEST_FRAME_OFFSET]
|
||||
bl thermosphereMain
|
||||
|
||||
prfm pldl1keep, [x18]
|
||||
|
|
Loading…
Reference in a new issue