/*
* 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"
// Reminder: use these functions behind a lock
static Barrier g_debugPauseBarrier;
static atomic_uint g_debugPausePausedCoreList;
static atomic_uint g_debugPauseSingleStepCoreList;
void debugPauseSgiHandler(void)
{
currentCoreCtx->wasPaused = true;
barrierWait(&g_debugPauseBarrier);
}
void debugPauseWaitAndUpdateSingleStep(void)
{
u32 coreId = currentCoreCtx->coreId;
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();
// Since we're using a debugger lock, a simple stlr should be fine...
atomic_store(&g_debugPausePausedCoreList, coreList);
if (coreList != BIT(currentCoreCtx->coreId)) {
// We need to notify other cores...
u32 otherCores = coreList & ~BIT(currentCoreCtx->coreId);
barrierInit(&g_debugPauseBarrier, otherCores | BIT(currentCoreCtx->coreId));
generateSgiForList(ThermosphereSgi_DebugPause, otherCores);
barrierWait(&g_debugPauseBarrier);
}
if (coreList & BIT(currentCoreCtx->coreId)) {
currentCoreCtx->wasPaused = true;
}
unmaskIrq();
}
void debugUnpauseCores(u32 coreList, u32 singleStepList)
{
singleStepList &= coreList;
// Since we're using a debugger lock, a simple stlr should be fine...
atomic_store(&g_debugPauseSingleStepCoreList, singleStepList);
atomic_store(&g_debugPausePausedCoreList, 0);
__sev();
}