thermosphere: gdb add break & vCont handling

This commit is contained in:
TuxSH 2020-01-29 01:19:38 +00:00
parent c0252e07f6
commit cbf3b305ca
10 changed files with 204 additions and 86 deletions

View file

@ -151,6 +151,11 @@ const DebugEventInfo *debugManagerMarkAndGetCoreDebugEvent(u32 coreId)
return &g_debugManager.debugEventInfos[coreId]; return &g_debugManager.debugEventInfos[coreId];
} }
const DebugEventInfo *debugManagerGetCoreDebugEvent(u32 coreId)
{
return &g_debugManager.debugEventInfos[coreId];
}
void debugManagerReportEvent(DebugEventType type, ...) void debugManagerReportEvent(DebugEventType type, ...)
{ {
u64 flags = maskIrq(); u64 flags = maskIrq();
@ -186,3 +191,14 @@ void debugManagerReportEvent(DebugEventType type, ...)
restoreInterruptFlags(flags); 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);
}
}

View file

@ -62,6 +62,11 @@ void debugManagerSetSteppingRange(u32 coreId, uintptr_t startAddr, uintptr_t end
u32 debugManagerGetPausedCoreList(void); u32 debugManagerGetPausedCoreList(void);
const DebugEventInfo *debugManagerMarkAndGetCoreDebugEvent(u32 coreId); const DebugEventInfo *debugManagerMarkAndGetCoreDebugEvent(u32 coreId);
const DebugEventInfo *debugManagerGetCoreDebugEvent(u32 coreId);
void debugManagerReportEvent(DebugEventType type, ...);
void debugManagerBreakCores(u32 coreList);
static inline bool debugManagerIsCorePaused(u32 coreId) static inline bool debugManagerIsCorePaused(u32 coreId)
{ {

View file

@ -74,8 +74,8 @@ typedef struct GDBContext {
u32 attachedCoreList; u32 attachedCoreList;
u32 currentThreadId; u32 currentThreadId;
u32 selectedThreadId; int selectedThreadId;
u32 selectedThreadIdForContinuing; int selectedThreadIdForContinuing;
u32 sentDebugEventCoreList; u32 sentDebugEventCoreList;

View file

@ -235,6 +235,15 @@ int GDB_TrySignalDebugEvent(GDBContext *ctx, DebugEventInfo *info)
return ret; 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) GDB_DECLARE_VERBOSE_HANDLER(Stopped)
{ {
u32 coreList = debugManagerGetPausedCoreList() & ctx->attachedCoreList; u32 coreList = debugManagerGetPausedCoreList() & ctx->attachedCoreList;
@ -282,102 +291,175 @@ GDB_DECLARE_HANDLER(Kill)
return 0; return 0;
} }
GDB_DECLARE_HANDLER(Break) GDB_DECLARE_VERBOSE_HANDLER(CtrlC)
{ {
// TODO int ret = GDB_ReplyOk(ctx);
if(!(ctx->flags & GDB_FLAG_CONTINUING)) // Is this ever reached? GDB_BreakAllCores(ctx);
return GDB_SendPacket(ctx, "S02", 3);
else
{
ctx->flags &= ~GDB_FLAG_CONTINUING;
return 0;
}
} }
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; char *addrStart = NULL;
u32 addr = 0; uintptr_t addr = 0;
if(ctx->selectedThreadIdForContinuing != 0 && ctx->selectedThreadIdForContinuing != ctx->currentThreadId) char cmd = ctx->commandData[-1];
return 0;
if(ctx->commandData[-1] == 'C') // This deprecated command should not be permitted in non-stop mode
{ if (ctx->flags & GDB_FLAG_NONSTOP) {
if(ctx->commandData[0] == 0 || ctx->commandData[1] == 0 || (ctx->commandData[2] != 0 && ctx->commandData[2] == ';')) 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); 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; addrStart = ctx->commandData + 3;
}
} }
else else {
{ // 'c', 's'
if(ctx->commandData[0] != 0) if (ctx->commandData[0] != 0) {
addrStart = ctx->commandData; 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(&regs, ctx->debug, ctx->currentThreadId, THREADCONTEXT_CONTROL_CPU_SPRS);
if(R_SUCCEEDED(r))
{
regs.cpu_registers.pc = addr;
r = svcSetDebugThreadContext(ctx->debug, ctx->currentThreadId, &regs, 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; return 0;
} }
GDB_DECLARE_VERBOSE_HANDLER(Continue) GDB_DECLARE_VERBOSE_HANDLER(Continue)
{ {
// TODO u32 parsedCoreList = 0;
char *pos = ctx->commandData; u32 continueCoreList = 0;
bool currentThreadFound = false; u32 stepCoreList = 0;
while(pos != NULL && *pos != 0 && !currentThreadFound) u32 stopCoreList = 0;
{
if(*pos != 'c' && *pos != 'C')
return GDB_ReplyErrno(ctx, EPERM);
pos += *pos == 'C' ? 3 : 1; char *cmd = ctx->commandData;
if(*pos++ != ':') // default action found while (cmd != NULL) {
{ char *nextCmd;
currentThreadFound = true; char *threadIdPart;
break; 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;
} }
char *nextpos = (char *)strchr(pos, ';'); // Locate thread-id part, parse thread id
if(strncmp(pos, "-1", 2) == 0) threadIdPart = strchr(cmd, ':');
currentThreadFound = true; if (threadIdPart == NULL || strcmp(threadIdPart, "-1") == 0) {
else // Default action...
{ threadId = -1;
u32 threadId; curMask = ctx->attachedCoreList;
if(GDB_ParseHexIntegerList(&threadId, pos, 1, ';') == NULL) } else {
unsigned long id;
if(GDB_ParseHexIntegerList(&id, ctx->commandData + 1, 1, 0) == NULL) {
return GDB_ReplyErrno(ctx, EILSEQ); return GDB_ReplyErrno(ctx, EILSEQ);
currentThreadFound = currentThreadFound || threadId == ctx->currentThreadId; } 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;
} }
pos = nextpos; // 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;
}
default:
return GDB_ReplyErrno(ctx, EILSEQ);
}
if (*cmdEnd != 0) {
// We've got garbage data...
return GDB_ReplyErrno(ctx, EILSEQ);
}
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;
} }
if(ctx->currentThreadId == 0 || currentThreadFound) debugManagerBreakCores(stopCoreList);
GDB_ContinueExecution(ctx); debugManagerUnpauseCores(continueCoreList, stepCoreList);
return 0; return 0;
} }

View file

@ -19,8 +19,8 @@ GDB_DECLARE_VERBOSE_HANDLER(Stopped);
GDB_DECLARE_HANDLER(Detach); GDB_DECLARE_HANDLER(Detach);
GDB_DECLARE_HANDLER(Kill); GDB_DECLARE_HANDLER(Kill);
GDB_DECLARE_HANDLER(Break); GDB_DECLARE_VERBOSE_HANDLER(CtrlC);
GDB_DECLARE_HANDLER(Continue); GDB_DECLARE_HANDLER(ContinueOrStepDeprecated);
GDB_DECLARE_VERBOSE_HANDLER(Continue); GDB_DECLARE_VERBOSE_HANDLER(Continue);
GDB_DECLARE_HANDLER(GetStopReason); GDB_DECLARE_HANDLER(GetStopReason);

View file

@ -13,27 +13,33 @@
GDB_DECLARE_HANDLER(SetThreadId) GDB_DECLARE_HANDLER(SetThreadId)
{ {
// Id = 0 means any thread
if (ctx->commandData[0] == 'g') { 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); return GDB_ReplyErrno(ctx, EINVAL);
} }
unsigned long id; 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); 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?) // TODO: change irq affinity (and remove selectedThreadId?)
return GDB_ReplyOk(ctx); return GDB_ReplyOk(ctx);
} else if (ctx->commandData[0] == 'c') { } else if (ctx->commandData[0] == 'c') {
if(strncmp(ctx->commandData + 1, "-1", 2) == 0) { if(strcmp(ctx->commandData + 1, "-1") == 0) {
ctx->selectedThreadIdForContinuing = 0; ctx->selectedThreadIdForContinuing = -1;
} else { } else {
unsigned long id; 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); 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); return GDB_ReplyOk(ctx);

View file

@ -13,11 +13,12 @@
static const struct { static const struct {
const char *name; const char *name;
char trailingCharacter;
GDBCommandHandler handler; GDBCommandHandler handler;
} gdbVerboseCommandHandlers[] = { } gdbVerboseCommandHandlers[] = {
{ "Cont?", GDB_VERBOSE_HANDLER(ContinueSupported) }, { "Cont?", '\0', GDB_VERBOSE_HANDLER(ContinueSupported) },
{ "Cont", GDB_VERBOSE_HANDLER(Continue) }, { "Cont", ';', GDB_VERBOSE_HANDLER(Continue) },
{ "MustReplyEmpty", GDB_HANDLER(Unsupported) }, { "MustReplyEmpty", '\0', GDB_HANDLER(Unsupported) },
}; };
GDB_DECLARE_HANDLER(VerboseCommand) GDB_DECLARE_HANDLER(VerboseCommand)
@ -31,6 +32,7 @@ GDB_DECLARE_HANDLER(VerboseCommand)
char *vData = NULL; char *vData = NULL;
for (nameEnd = nameBegin; *nameEnd != 0 && *nameEnd != ';' && *nameEnd != ':'; nameEnd++); for (nameEnd = nameBegin; *nameEnd != 0 && *nameEnd != ';' && *nameEnd != ':'; nameEnd++);
char oldNameEnd = *nameEnd;
if (*nameEnd != 0) { if (*nameEnd != 0) {
*nameEnd = 0; *nameEnd = 0;
vData = nameEnd + 1; vData = nameEnd + 1;
@ -39,7 +41,11 @@ GDB_DECLARE_HANDLER(VerboseCommand)
for (size_t i = 0; i < sizeof(gdbVerboseCommandHandlers) / sizeof(gdbVerboseCommandHandlers[0]); i++) { for (size_t i = 0; i < sizeof(gdbVerboseCommandHandlers) / sizeof(gdbVerboseCommandHandlers[0]); i++) {
if (strcmp(gdbVerboseCommandHandlers[i].name, nameBegin) == 0) { if (strcmp(gdbVerboseCommandHandlers[i].name, nameBegin) == 0) {
ctx->commandData = vData; ctx->commandData = vData;
return gdbVerboseCommandHandlers[i].handler(ctx); if (oldNameEnd == gdbVerboseCommandHandlers[i].trailingCharacter) {
return gdbVerboseCommandHandlers[i].handler(ctx);
} else {
return GDB_ReplyErrno(ctx, EILSEQ);
}
} }
} }

View file

@ -220,7 +220,6 @@ void handleIrqException(ExceptionStackFrame *frame, bool isLowerEl, bool isA32)
bool isGuestInterrupt = false; bool isGuestInterrupt = false;
bool isMaintenanceInterrupt = false; bool isMaintenanceInterrupt = false;
bool hasBottomHalf = false;
switch (irqId) { switch (irqId) {
case ThermosphereSgi_ExecuteFunction: case ThermosphereSgi_ExecuteFunction:
@ -232,6 +231,9 @@ void handleIrqException(ExceptionStackFrame *frame, bool isLowerEl, bool isA32)
case ThermosphereSgi_DebugPause: case ThermosphereSgi_DebugPause:
debugManagerPauseSgiHandler(); debugManagerPauseSgiHandler();
break; break;
case ThermosphereSgi_ReportDebuggerBreak:
// See bottom half
break;
case GIC_IRQID_MAINTENANCE: case GIC_IRQID_MAINTENANCE:
isMaintenanceInterrupt = true; isMaintenanceInterrupt = true;
break; break;
@ -244,7 +246,6 @@ void handleIrqException(ExceptionStackFrame *frame, bool isLowerEl, bool isA32)
} }
TransportInterface *transportIface = irqId >= 32 ? transportInterfaceIrqHandlerTopHalf(irqId) : NULL; TransportInterface *transportIface = irqId >= 32 ? transportInterfaceIrqHandlerTopHalf(irqId) : NULL;
hasBottomHalf = hasBottomHalf || transportIface != NULL;
// Priority drop // Priority drop
gicc->eoir = iar; gicc->eoir = iar;
@ -269,12 +270,12 @@ void handleIrqException(ExceptionStackFrame *frame, bool isLowerEl, bool isA32)
recursiveSpinlockUnlock(&g_irqManager.lock); recursiveSpinlockUnlock(&g_irqManager.lock);
// Bottom half part // Bottom half part
if (hasBottomHalf) { if (transportIface != NULL) {
exceptionEnterInterruptibleHypervisorCode(); exceptionEnterInterruptibleHypervisorCode();
unmaskIrq(); unmaskIrq();
if (transportIface != NULL) { transportInterfaceIrqHandlerBottomHalf(transportIface);
transportInterfaceIrqHandlerBottomHalf(transportIface); } else if (irqId == ThermosphereSgi_ReportDebuggerBreak) {
} debugManagerReportEvent(DBGEVENT_DEBUGGER_BREAK);
} }
} }

View file

@ -41,6 +41,7 @@ typedef enum ThermosphereSgi {
ThermosphereSgi_ExecuteFunction = 0, ThermosphereSgi_ExecuteFunction = 0,
ThermosphereSgi_VgicUpdate = 1, ThermosphereSgi_VgicUpdate = 1,
ThermosphereSgi_DebugPause = 2, ThermosphereSgi_DebugPause = 2,
ThermosphereSgi_ReportDebuggerBreak = 3,
ThermosphereSgi_Max, ThermosphereSgi_Max,
} ThermosphereSgi; } ThermosphereSgi;

View file

@ -95,6 +95,7 @@ _postMmuEnableReturnAddr:
mov x0, sp mov x0, sp
mov x1, x20 mov x1, x20
str x0, [x18, #CORECTX_GUEST_FRAME_OFFSET]
bl thermosphereMain bl thermosphereMain
prfm pldl1keep, [x18] prfm pldl1keep, [x18]