1
0
Fork 0
mirror of https://github.com/Atmosphere-NX/Atmosphere.git synced 2024-12-22 02:12:06 +00:00

thermosphere: debug manager wip

This commit is contained in:
TuxSH 2020-01-27 00:46:00 +00:00
parent c00672654a
commit 0e47f7f46b
8 changed files with 183 additions and 118 deletions

View file

@ -0,0 +1,146 @@
/*
* Copyright (c) 2019 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdatomic.h>
#include <stdarg.h>
#include "debug_manager.h"
#include "core_ctx.h"
#include "irq.h"
#include "spinlock.h"
#include "single_step.h"
#include "gdb/debug.h"
typedef struct DebugManager {
DebugEventInfo debugEventInfos[MAX_CORE];
ALIGN(64) atomic_uint pausedCoreList;
atomic_uint singleStepCoreList;
atomic_uint eventsSentList;
Barrier pauseBarrier;
atomic_bool nonStop;
} DebugManager;
static DebugManager g_debugManager = { 0 };
static void debugManagerDoPauseCores(u32 coreList)
{
__builtin_prefetch(&g_debugManager.pausedCoreList, 1, 0);
u32 desiredList = coreList;
u32 remainingList = coreList;
u32 readList = atomic_load(&g_debugManager.pausedCoreList);
do {
desiredList |= readList;
remainingList &= ~readList;
} while (atomic_compare_exchange_weak(&g_debugManager.pausedCoreList, &readList, desiredList));
if (remainingList != BIT(currentCoreCtx->coreId)) {
// We need to notify other cores...
u32 otherCores = remainingList & ~BIT(currentCoreCtx->coreId);
barrierInit(&g_debugManager.pauseBarrier, otherCores | BIT(currentCoreCtx->coreId));
generateSgiForList(ThermosphereSgi_DebugPause, otherCores);
barrierWait(&g_debugManager.pauseBarrier);
}
if (remainingList & BIT(currentCoreCtx->coreId)) {
currentCoreCtx->wasPaused = true;
}
}
void debugManagerPauseSgiHandler(void)
{
currentCoreCtx->wasPaused = true;
barrierWait(&g_debugManager.pauseBarrier);
}
void debugManagerHandlePause(void)
{
u32 coreId = currentCoreCtx->coreId;
__builtin_prefetch(&g_debugManager.pausedCoreList, 0, 3);
if (atomic_load(&g_debugManager.pausedCoreList) & BIT(coreId)) {
unmaskIrq();
do {
__wfe();
} while (atomic_load(&g_debugManager.pausedCoreList) & BIT(coreId));
maskIrq();
}
currentCoreCtx->wasPaused = false;
// Single-step: if inactive and requested, start single step; cancel if active and not requested
u32 ssReqd = (atomic_load(&g_debugManager.singleStepCoreList) & BIT(currentCoreCtx->coreId)) != 0;
SingleStepState singleStepState = singleStepGetNextState(currentCoreCtx->guestFrame);
if (ssReqd && singleStepState == SingleStepState_Inactive) {
singleStepSetNextState(currentCoreCtx->guestFrame, SingleStepState_ActiveNotPending);
} else if (!ssReqd && singleStepState != SingleStepState_Inactive) {
singleStepSetNextState(currentCoreCtx->guestFrame, SingleStepState_Inactive);
}
}
void debugManagerPauseCores(u32 coreList)
{
u64 flags = maskIrq();
debugManagerDoPauseCores(coreList);
restoreInterruptFlags(flags);
}
void debugManagerUnpauseCores(u32 coreList, u32 singleStepList)
{
singleStepList &= coreList;
__builtin_prefetch(&g_debugManager.pausedCoreList, 1, 0);
// Since we're using a debugger lock, a simple stlr should be fine...
atomic_store(&g_debugManager.singleStepCoreList, singleStepList);
atomic_fetch_and(&g_debugManager.pausedCoreList, ~coreList);
__sev();
}
void debugManagerReportEvent(DebugEventType type, ...)
{
u64 flags = maskIrq();
u32 coreId = currentCoreCtx->coreId;
DebugEventInfo *info = &g_debugManager.debugEventInfos[coreId];
info->type = type;
info->coreId = coreId;
info->frame = currentCoreCtx->guestFrame;
va_list args;
va_start(args, type);
switch (type) {
case DBGEVENT_OUTPUT_STRING:
info->outputString.address = va_arg(args, uintptr_t);
info->outputString.size = va_arg(args, size_t);
break;
default:
break;
}
va_end(args);
// Now, pause ourselves and try to signal we have a debug event
debugManagerDoPauseCores(BIT(coreId));
// TODO gdb enter leave functions
restoreInterruptFlags(flags);
}

View file

@ -16,15 +16,38 @@
#pragma once
#include "utils.h"
#include "exceptions.h"
void debugPauseSgiHandler(void);
typedef enum DebugEventType {
DBGEVENT_DEBUGGER_BREAK = 0,
DBGEVENT_EXCEPTION,
DBGEVENT_CORE_ON,
DBGEVENT_CORE_OFF,
DBGEVENT_EXIT,
DBGEVENT_OUTPUT_STRING,
} DebugEventType;
typedef struct OutputStringDebugEventInfo {
uintptr_t address;
size_t size;
} OutputStringDebugEventInfo;
typedef struct DebugEventInfo {
DebugEventType type;
u32 coreId;
ExceptionStackFrame *frame;
union {
OutputStringDebugEventInfo outputString;
};
} DebugEventInfo;
void debugManagerPauseSgiHandler(void);
// Hypervisor interrupts will be serviced during the pause-wait
void debugPauseWaitAndUpdateSingleStep(void);
void debugManagerHandlePause(void);
// Note: these functions are not reentrant EXCEPT debugPauseCores(1 << currentCoreId)
// "Pause" makes sure all cores reaches the pause function before proceeding.
// "Unpause" doesn't synchronize, it just makes sure the core resumes & that "pause" can be called again.
void debugPauseCores(u32 coreList);
void debugUnpauseCores(u32 coreList, u32 singleStepList);
void debugManagerPauseCores(u32 coreList);
void debugManagerUnpauseCores(u32 coreList, u32 singleStepList);

View file

@ -1,104 +0,0 @@
/*
* Copyright (c) 2019 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdatomic.h>
#include "debug_pause.h"
#include "core_ctx.h"
#include "irq.h"
#include "spinlock.h"
#include "single_step.h"
static Barrier g_debugPauseBarrier;
static ALIGN(64) atomic_uint g_debugPausePausedCoreList;
static atomic_uint g_debugPauseSingleStepCoreList; // TODO: put this variable on the same cache line as the above
static inline void debugSetThisCorePaused(void)
{
currentCoreCtx->wasPaused = true;
}
void debugPauseSgiHandler(void)
{
debugSetThisCorePaused();
barrierWait(&g_debugPauseBarrier);
}
void debugPauseWaitAndUpdateSingleStep(void)
{
u32 coreId = currentCoreCtx->coreId;
__builtin_prefetch(&g_debugPausePausedCoreList, 0, 3);
if (atomic_load(&g_debugPausePausedCoreList) & BIT(coreId)) {
unmaskIrq();
do {
__wfe();
} while (atomic_load(&g_debugPausePausedCoreList) & BIT(coreId));
maskIrq();
}
currentCoreCtx->wasPaused = false;
// Single-step: if inactive and requested, start single step; cancel if active and not requested
u32 ssReqd = (atomic_load(&g_debugPauseSingleStepCoreList) & BIT(currentCoreCtx->coreId)) != 0;
SingleStepState singleStepState = singleStepGetNextState(currentCoreCtx->guestFrame);
if (ssReqd && singleStepState == SingleStepState_Inactive) {
singleStepSetNextState(currentCoreCtx->guestFrame, SingleStepState_ActiveNotPending);
} else if (!ssReqd && singleStepState != SingleStepState_Inactive) {
singleStepSetNextState(currentCoreCtx->guestFrame, SingleStepState_Inactive);
}
}
void debugPauseCores(u32 coreList)
{
maskIrq();
__builtin_prefetch(&g_debugPausePausedCoreList, 1, 3);
u32 desiredList = coreList;
u32 remainingList = coreList;
u32 readList = atomic_load(&g_debugPausePausedCoreList);
do {
desiredList |= readList;
remainingList &= ~readList;
} while (atomic_compare_exchange_weak(&g_debugPausePausedCoreList, &readList, desiredList));
if (remainingList != BIT(currentCoreCtx->coreId)) {
// We need to notify other cores...
u32 otherCores = remainingList & ~BIT(currentCoreCtx->coreId);
barrierInit(&g_debugPauseBarrier, otherCores | BIT(currentCoreCtx->coreId));
generateSgiForList(ThermosphereSgi_DebugPause, otherCores);
barrierWait(&g_debugPauseBarrier);
}
if (remainingList & BIT(currentCoreCtx->coreId)) {
debugSetThisCorePaused();
}
unmaskIrq();
}
void debugUnpauseCores(u32 coreList, u32 singleStepList)
{
singleStepList &= coreList;
__builtin_prefetch(&g_debugPausePausedCoreList, 1, 0);
// Since we're using a debugger lock, a simple stlr should be fine...
atomic_store(&g_debugPauseSingleStepCoreList, singleStepList);
atomic_fetch_and(&g_debugPausePausedCoreList, ~coreList);
__sev();
}

View file

@ -24,7 +24,7 @@
#include "single_step.h"
#include "data_abort.h"
#include "spinlock.h"
#include "debug_pause.h"
#include "debug_manager.h"
#include "timer.h"
#include "fpu.h"
@ -122,7 +122,7 @@ void exceptionReturnPreprocess(ExceptionStackFrame *frame)
if (currentCoreCtx->wasPaused && frame == currentCoreCtx->guestFrame) {
// Were we paused & are we about to return to the guest?
exceptionEnterInterruptibleHypervisorCode();
debugPauseWaitAndUpdateSingleStep();
debugManagerHandlePause();
fpuCleanInvalidateRegisterCache();
}

View file

@ -25,7 +25,7 @@
#pragma once
#include "defines.h"
#include "transport_interface.h"
#include "../transport_interface.h"
typedef struct PackedGdbHioRequest
{

View file

@ -21,4 +21,4 @@ GDB_DECLARE_HANDLER(GetStopReason);
void GDB_ContinueExecution(GDBContext *ctx);
int GDB_SendStopReply(GDBContext *ctx, const DebugEventInfo *info);
int GDB_HandleDebugEvents(GDBContext *ctx);
void GDB_BreakProcessAndSinkDebugEvents(GDBContext *ctx, DebugFlags flags);
//void GDB_BreakProcessAndSinkDebugEvents(GDBContext *ctx, DebugFlags flags);

View file

@ -21,7 +21,7 @@
#include "timer.h"
#include "guest_timers.h"
#include "transport_interface.h"
#include "debug_pause.h"
#include "debug_manager.h"
IrqManager g_irqManager = {0};
@ -230,7 +230,7 @@ void handleIrqException(ExceptionStackFrame *frame, bool isLowerEl, bool isA32)
// Nothing in particular to do here
break;
case ThermosphereSgi_DebugPause:
debugPauseSgiHandler();
debugManagerPauseSgiHandler();
break;
case GIC_IRQID_MAINTENANCE:
isMaintenanceInterrupt = true;

View file

@ -57,7 +57,7 @@ static void loadKernelViaSemihosting(void)
#include "platform/uart.h"
#include "debug_pause.h"
#include "debug_manager.h"
typedef struct TestCtx {
char buf[512+1];
} TestCtx;
@ -67,7 +67,7 @@ static TestCtx g_testCtx;
size_t testReceiveCallback(TransportInterface *iface, void *p)
{
TestCtx *ctx = (TestCtx *)p;
debugPauseCores(BIT(0));
debugManagerPauseCores(BIT(0));
return transportInterfaceReadDataUntil(iface, ctx->buf, 512, '\r');
}
@ -75,7 +75,7 @@ void testProcessDataCallback(TransportInterface *iface, void *p, size_t sz)
{
(void)iface;
(void)sz;
debugUnpauseCores(BIT(0), BIT(0));
debugManagerUnpauseCores(BIT(0), BIT(0));
TestCtx *ctx = (TestCtx *)p;
DEBUG("EL2 [core %u]: you typed: %s\n", currentCoreCtx->coreId, ctx->buf);
}