diff --git a/thermosphere/src/debug_manager.c b/thermosphere/src/debug_manager.c
new file mode 100644
index 000000000..0888329e5
--- /dev/null
+++ b/thermosphere/src/debug_manager.c
@@ -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 .
+ */
+
+#include
+#include
+
+#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);
+}
diff --git a/thermosphere/src/debug_pause.h b/thermosphere/src/debug_manager.h
similarity index 58%
rename from thermosphere/src/debug_pause.h
rename to thermosphere/src/debug_manager.h
index edaa43487..3c5bf501e 100644
--- a/thermosphere/src/debug_pause.h
+++ b/thermosphere/src/debug_manager.h
@@ -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);
diff --git a/thermosphere/src/debug_pause.c b/thermosphere/src/debug_pause.c
deleted file mode 100644
index e3714ba91..000000000
--- a/thermosphere/src/debug_pause.c
+++ /dev/null
@@ -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 .
- */
-
-#include
-
-#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();
-}
diff --git a/thermosphere/src/exceptions.c b/thermosphere/src/exceptions.c
index 266197c1c..dcd649264 100644
--- a/thermosphere/src/exceptions.c
+++ b/thermosphere/src/exceptions.c
@@ -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();
}
diff --git a/thermosphere/src/gdb/context.h b/thermosphere/src/gdb/context.h
index ce64c2139..55d596983 100644
--- a/thermosphere/src/gdb/context.h
+++ b/thermosphere/src/gdb/context.h
@@ -25,7 +25,7 @@
#pragma once
#include "defines.h"
-#include "transport_interface.h"
+#include "../transport_interface.h"
typedef struct PackedGdbHioRequest
{
diff --git a/thermosphere/src/gdb/debug.h b/thermosphere/src/gdb/debug.h
index 3c5389ab4..87c09e5d2 100644
--- a/thermosphere/src/gdb/debug.h
+++ b/thermosphere/src/gdb/debug.h
@@ -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);
diff --git a/thermosphere/src/irq.c b/thermosphere/src/irq.c
index 62787c205..557e9136a 100644
--- a/thermosphere/src/irq.c
+++ b/thermosphere/src/irq.c
@@ -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;
diff --git a/thermosphere/src/main.c b/thermosphere/src/main.c
index 56f915299..dd8173d85 100644
--- a/thermosphere/src/main.c
+++ b/thermosphere/src/main.c
@@ -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);
}