thermosphere: file cleanup

This commit is contained in:
TuxSH 2020-02-16 17:40:18 +00:00
parent 2574f68484
commit 613402121a
20 changed files with 0 additions and 3030 deletions

View file

@ -1,44 +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 "barrier.h"
#include "core_ctx.h"
#include "utils.h"
void barrierInit(Barrier *barrier, u32 coreList)
{
atomic_store(&barrier->val, coreList);
}
void barrierInitAllButSelf(Barrier *barrier)
{
barrierInit(barrier, getActiveCoreMask() & ~(BIT(currentCoreCtx->coreId)));
}
void barrierInitAll(Barrier *barrier)
{
barrierInit(barrier, getActiveCoreMask());
}
void barrierWait(Barrier *barrier)
{
atomic_fetch_and(&barrier->val, ~(BIT(currentCoreCtx->coreId)));
__sev();
do {
__wfe();
} while (atomic_load(&barrier->val) != 0);
}

View file

@ -1,30 +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/>.
*/
#pragma once
#include <stdatomic.h>
#include "types.h"
typedef struct Barrier {
atomic_uint val;
} Barrier;
void barrierInit(Barrier *barrier, u32 coreList);
void barrierInitAllButSelf(Barrier *barrier);
void barrierInitAll(Barrier *barrier);
void barrierWait(Barrier *barrier);

View file

@ -1,162 +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 <string.h>
#include "breakpoints.h"
#include "breakpoints_watchpoints_load.h"
#include "utils.h"
#include "core_ctx.h"
BreakpointManager g_breakpointManager = {0};
// Init the structure (already in BSS, so already zero-initialized) and load the registers
void initBreakpoints(void)
{
recursiveSpinlockLock(&g_breakpointManager.lock);
if (currentCoreCtx->isBootCore && !currentCoreCtx->warmboot) {
size_t num = ((GET_SYSREG(id_aa64dfr0_el1) >> 12) & 0xF) + 1;
g_breakpointManager.maxBreakpoints = (u32)num;
g_breakpointManager.freeBitmap = BIT(num) - 1;
g_breakpointManager.usedBitmap = 0;
}
loadBreakpointRegs(g_breakpointManager.breakpoints, g_breakpointManager.maxBreakpoints);
recursiveSpinlockUnlock(&g_breakpointManager.lock);
}
static void commitAndBroadcastBreakpointHandler(void *p)
{
(void)p;
u64 flags = maskIrq();
loadBreakpointRegs(g_breakpointManager.breakpoints, g_breakpointManager.maxBreakpoints);
restoreInterruptFlags(flags);
}
static inline void commitAndBroadcastBreakpoints(void)
{
__dmb();
executeFunctionOnAllCores(commitAndBroadcastBreakpointHandler, NULL, true);
}
static DebugRegisterPair *allocateBreakpoint(void)
{
u32 pos = __builtin_ffs(g_breakpointManager.freeBitmap);
if (pos == 0) {
return NULL;
} else {
g_breakpointManager.freeBitmap &= ~BIT(pos - 1);
g_breakpointManager.usedBitmap |= BIT(pos - 1);
return &g_breakpointManager.breakpoints[pos - 1];
}
}
static void freeBreakpoint(u32 pos)
{
memset(&g_breakpointManager.breakpoints[pos], 0, sizeof(DebugRegisterPair));
g_breakpointManager.freeBitmap |= BIT(pos);
g_breakpointManager.usedBitmap &= ~BIT(pos);
}
static DebugRegisterPair *findBreakpoint(uintptr_t addr)
{
FOREACH_BIT (tmp, i, g_breakpointManager.usedBitmap) {
if (g_breakpointManager.breakpoints[i - 1].vr == addr) {
return &g_breakpointManager.breakpoints[i - 1];
}
}
return NULL;
}
// Note: A32/T32/T16 support intentionnally left out
// Note: addresses are supposed to be well-formed regarding the sign extension bits
int addBreakpoint(uintptr_t addr)
{
recursiveSpinlockLock(&g_breakpointManager.lock);
// Reject misaligned addresses
if (addr & 3) {
recursiveSpinlockUnlock(&g_breakpointManager.lock);
return -EINVAL;
}
// Oops
if (g_breakpointManager.freeBitmap == 0) {
return -EBUSY;
}
// Breakpoint already added
if (findBreakpoint(addr) != NULL) {
recursiveSpinlockUnlock(&g_breakpointManager.lock);
return -EEXIST;
}
DebugRegisterPair *regs = allocateBreakpoint();
regs->cr.bt = BreakpointType_AddressMatch;
regs->cr.linked = false;
// NS EL1&0 only
regs->cr.hmc = DebugHmc_LowerEl;
regs->cr.ssc = DebugSsc_NonSecure;
regs->cr.pmc = DebugPmc_El1And0;
regs->cr.bas = 0xF;
regs->cr.enabled = true;
regs->vr = addr;
commitAndBroadcastBreakpoints();
recursiveSpinlockUnlock(&g_breakpointManager.lock);
return 0;
}
int removeBreakpoint(uintptr_t addr)
{
recursiveSpinlockLock(&g_breakpointManager.lock);
DebugRegisterPair *regs = findBreakpoint(addr);
if (findBreakpoint(addr) == NULL) {
recursiveSpinlockUnlock(&g_breakpointManager.lock);
return -ENOENT;
}
freeBreakpoint(regs - &g_breakpointManager.breakpoints[0]);
commitAndBroadcastBreakpoints();
recursiveSpinlockUnlock(&g_breakpointManager.lock);
return 0;
}
int removeAllBreakpoints(void)
{
recursiveSpinlockLock(&g_breakpointManager.lock);
g_breakpointManager.freeBitmap |= g_breakpointManager.usedBitmap;
g_breakpointManager.usedBitmap = 0;
memset(g_breakpointManager.breakpoints, 0, sizeof(g_breakpointManager.breakpoints));
commitAndBroadcastBreakpoints();
recursiveSpinlockUnlock(&g_breakpointManager.lock);
return 0;
}

View file

@ -1,39 +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/>.
*/
#pragma once
#include "breakpoints_watchpoints_common.h"
#include "spinlock.h"
#define _REENT_ONLY
#include <errno.h>
/// Structure to synchronize and keep track of breakpoints
typedef struct BreakpointManager {
DebugRegisterPair breakpoints[MAX_BCR];
RecursiveSpinlock lock;
u32 maxBreakpoints;
u16 freeBitmap;
u16 usedBitmap;
} BreakpointManager;
extern BreakpointManager g_breakpointManager;
void initBreakpoints(void);
int addBreakpoint(uintptr_t addr);
int removeBreakpoint(uintptr_t addr);
int removeAllBreakpoints(void);

View file

@ -1,89 +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/>.
*/
#pragma once
#include "types.h"
#include "sysreg.h"
/// BT[3:1] or res0. BT[0]/WT[0] is "is linked"
typedef enum BreakpointType {
BreakpointType_AddressMatch = 0,
BreakpointType_VheContextIdMatch = 1,
BreakpointType_ContextIdMatch = 3,
BreakpointType_VmidMatch = 4,
BreakpointType_VmidContextIdMatch = 5,
BreakpointType_VmidVheContextIdMatch = 6,
BreakpointType_FullVheContextIdMatch = 7,
} BreakpointType;
// Note: some SSC HMC PMC combinations are invalid
// Refer to "Table D2-9 Summary of breakpoint HMC, SSC, and PMC encodings"
/// Debug Security State Control
typedef enum DebugSsc {
DebugSsc_Both = 0,
DebugSsc_NonSecure = 1,
DebugSsc_Secure = 2,
DebugSsc_SecureIfLowerOrBoth = 3,
} DebugSsc;
/// Debug Higher Mode Control
typedef enum DebugHmc {
DebugHmc_LowerEl = 0,
DebugHmc_HigherEl = 1,
} DebugHmc;
/// Debug Privilege Mode Control (called PAC for watchpoints)
typedef enum DebugPmc {
DebugPmc_NeitherEl1Nor0 = 0,
DebugPmc_El1 = 1,
DebugPmc_El0 = 2,
DebugPmc_El1And0 = 3,
} DebugPmc;
typedef enum WatchpointLoadStoreControl {
WatchpointLoadStoreControl_Load = 1,
WatchpointLoadStoreControl_Store = 2,
WatchpointLoadStoreControl_LoadStore = 3,
} WatchpointLoadStoreControl;
// bas only 4 bits for breakpoints, other bits res0.
// lsc, mask only for watchpoints, res0 for breakpoints
// bt only from breakpoints, res0 for watchpoints
typedef struct DebugControlRegister {
bool enabled : 1;
DebugPmc pmc : 2;
WatchpointLoadStoreControl lsc : 2;
u32 bas : 8;
DebugHmc hmc : 1;
DebugSsc ssc : 2;
u32 lbn : 4;
bool linked : 1;
BreakpointType bt : 3;
u32 mask : 5;
u64 res0 : 35;
} DebugControlRegister;
typedef struct DebugRegisterPair {
DebugControlRegister cr;
u64 vr;
} DebugRegisterPair;
static inline void enableBreakpointsAndWatchpoints(void)
{
SET_SYSREG(mdscr_el1, GET_SYSREG(mdscr_el1) | MDSCR_MDE);
}

View file

@ -1,21 +0,0 @@
/*
* Copyright (c) 2018-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/>.
*/
#pragma once
#include "breakpoints_watchpoints_common.h"
void loadBreakpointRegs(const DebugRegisterPair *regs, size_t num);
void loadWatchpointRegs(const DebugRegisterPair *regs, size_t num);

View file

@ -1,70 +0,0 @@
/*
* Copyright (c) 2018-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 "asm_macros.s"
.altmacro
.macro LOAD_DBG_REG_PAIRS what, id
ldp x2, x3, [x0, #-0x10]!
msr dbg\what\()cr\id\()_el1, x2
msr dbg\what\()vr\id\()_el1, x3
.if \id != 0
LOAD_DBG_REG_PAIRS \what, %(\id - 1)
.endif
.endm
// Precondition: x1 <= 16
FUNCTION loadBreakpointRegs
// x1 = number
dmb ish
adr x16, 1f
add x0, x0, #(MAX_BCR * 8)
mov x4, #(MAX_BCR * 12)
sub x4, x4, x1,lsl #3
sub x4, x4, x1,lsl #2
add x16, x16, x4
br x16
1:
LOAD_DBG_REG_PAIRS b, %(MAX_BCR - 1)
dsb ish
isb
ret
END_FUNCTION
// Precondition: x1 <= 16
FUNCTION loadWatchpointRegs
// x1 = number
dmb ish
adr x16, 1f
add x0, x0, #(MAX_WCR * 8)
mov x4, #(MAX_WCR * 12)
sub x4, x4, x1,lsl #3
sub x4, x4, x1,lsl #2
add x16, x16, x4
br x16
1:
LOAD_DBG_REG_PAIRS w, %(MAX_WCR - 1)
dsb ish
isb
ret
END_FUNCTION

View file

@ -1,50 +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 "execute_function.h"
#include "utils.h"
#include "core_ctx.h"
#include "irq.h"
void executeFunctionOnCores(ExecutedFunction fun, void *args, bool sync, u32 coreList)
{
barrierInit(&currentCoreCtx->executedFunctionBarrier, coreList);
currentCoreCtx->executedFunction = fun;
currentCoreCtx->executedFunctionArgs = args;
currentCoreCtx->executedFunctionSync = sync;
__compiler_barrier();
generateSgiForList(ThermosphereSgi_ExecuteFunction, coreList);
}
void executeFunctionOnAllCores(ExecutedFunction fun, void *args, bool sync)
{
executeFunctionOnCores(fun, args, sync, getActiveCoreMask());
}
void executeFunctionOnAllCoresButSelf(ExecutedFunction fun, void *args, bool sync)
{
executeFunctionOnCores(fun, args, sync, getActiveCoreMask() & ~(BIT(currentCoreCtx->coreId)));
}
void executeFunctionInterruptHandler(u32 srcCore)
{
CoreCtx *ctx = &g_coreCtxs[srcCore];
currentCoreCtx->executedFunctionSrcCore = srcCore;
ctx->executedFunction(ctx->executedFunctionArgs);
if (ctx->executedFunctionSync) {
barrierWait(&ctx->executedFunctionBarrier);
}
}

View file

@ -1,28 +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/>.
*/
#pragma once
#include "types.h"
typedef void (*ExecutedFunction)(void *args);
void executeFunctionOnCores(ExecutedFunction fun, void *args, bool sync, u32 coreList);
void executeFunctionOnAllCores(ExecutedFunction fun, void *args, bool sync);
void executeFunctionOnAllCoresButSelf(ExecutedFunction fun, void *args, bool sync);
void executeFunctionInterruptHandler(u32 srcCore);

View file

@ -1,291 +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 "irq.h"
#include "core_ctx.h"
#include "debug_log.h"
#include "vgic.h"
#include "timer.h"
#include "guest_timers.h"
#include "transport_interface.h"
#include "debug_manager.h"
IrqManager g_irqManager = {0};
static void initGic(void)
{
// Reinits the GICD and GICC (for non-secure mode, obviously)
if (currentCoreCtx->isBootCore && !currentCoreCtx->warmboot) {
// Disable interrupt handling & global interrupt distribution
gicd->ctlr = 0;
// Get some info
g_irqManager.numSharedInterrupts = 32 * (gicd->typer & 0x1F); // number of interrupt lines / 32
// unimplemented priority bits (lowest significant) are RAZ/WI
gicd->ipriorityr[0] = 0xFF;
g_irqManager.priorityShift = 8 - __builtin_popcount(gicd->ipriorityr[0]);
g_irqManager.numPriorityLevels = (u8)BIT(__builtin_popcount(gicd->ipriorityr[0]));
g_irqManager.numCpuInterfaces = (u8)(1 + ((gicd->typer >> 5) & 7));
g_irqManager.numListRegisters = (u8)(1 + (gich->vtr & 0x3F));
}
// Only one core will reset the GIC state for the shared peripheral interrupts
u32 numInterrupts = 32;
if (currentCoreCtx->isBootCore) {
numInterrupts += g_irqManager.numSharedInterrupts;
}
// Filter all interrupts
gicc->pmr = 0;
// Disable interrupt preemption
gicc->bpr = 7;
// Note: the GICD I...n regs are banked for private interrupts
// Disable all interrupts, clear active status, clear pending status
for (u32 i = 0; i < numInterrupts / 32; i++) {
gicd->icenabler[i] = 0xFFFFFFFF;
gicd->icactiver[i] = 0xFFFFFFFF;
gicd->icpendr[i] = 0xFFFFFFFF;
}
// Set priorities to lowest
for (u32 i = 0; i < numInterrupts; i++) {
gicd->ipriorityr[i] = 0xFF;
}
// Reset icfgr, itargetsr for shared peripheral interrupts
for (u32 i = 32 / 16; i < numInterrupts / 16; i++) {
gicd->icfgr[i] = 0x55555555;
}
for (u32 i = 32; i < numInterrupts; i++) {
gicd->itargetsr[i] = 0;
}
// Now, reenable interrupts
// Enable the distributor
if (currentCoreCtx->isBootCore) {
gicd->ctlr = 1;
}
// Enable the CPU interface. Set EOIModeNS=1 (split prio drop & deactivate priority)
gicc->ctlr = BIT(9) | 1;
// Disable interrupt filtering
gicc->pmr = 0xFF;
currentCoreCtx->gicInterfaceMask = gicd->itargetsr[0];
}
static inline bool checkRescheduleEmulatedPtimer(ExceptionStackFrame *frame)
{
// Evaluate if the timer has really expired in the PoV of the guest kernel.
// If not, reschedule (add missed time delta) it & exit early
u64 cval = currentCoreCtx->emulPtimerCval;
u64 vct = computeCntvct(frame);
if (cval > vct) {
// It has not: reschedule the timer
// Note: this isn't 100% precise esp. on QEMU so it may take a few tries...
writeEmulatedPhysicalCompareValue(frame, cval);
return false;
}
return true;
}
static inline bool checkGuestTimerInterrupts(ExceptionStackFrame *frame, u16 irqId)
{
// A thing that might have happened is losing the race vs disabling the guest interrupts
// Another thing is that the virtual timer might have fired before us updating voff when executing a top half?
if (irqId == TIMER_IRQID(NS_VIRT_TIMER)) {
u64 cval = GET_SYSREG(cntp_cval_el0);
return cval <= computeCntvct(frame);
} else if (irqId == TIMER_IRQID(NS_PHYS_TIMER)) {
return checkRescheduleEmulatedPtimer(frame);
} else {
return true;
}
}
static void doConfigureInterrupt(u16 id, u8 prio, bool isLevelSensitive)
{
gicd->icenabler[id / 32] = BIT(id % 32);
if (id >= 32) {
u32 cfgr = gicd->icfgr[id / 16];
cfgr &= ~(3 << IRQ_CFGR_SHIFT(id));
cfgr |= (!isLevelSensitive ? 3 : 1) << IRQ_CFGR_SHIFT(id);
gicd->icfgr[id / 16] = cfgr;
gicd->itargetsr[id] = 0xFF; // all cpu interfaces
}
gicd->icpendr[id / 32] = BIT(id % 32);
gicd->ipriorityr[id] = (prio << g_irqManager.priorityShift) & 0xFF;
gicd->isenabler[id / 32] = BIT(id % 32);
}
void initIrq(void)
{
u64 flags = recursiveSpinlockLockMaskIrq(&g_irqManager.lock);
initGic();
vgicInit();
// Configure the interrupts we use here
for (u32 i = 0; i < ThermosphereSgi_Max; i++) {
doConfigureInterrupt(i, IRQ_PRIORITY_HOST, false);
}
doConfigureInterrupt(GIC_IRQID_MAINTENANCE, IRQ_PRIORITY_HOST, true);
recursiveSpinlockUnlockRestoreIrq(&g_irqManager.lock, flags);
}
void configureInterrupt(u16 id, u8 prio, bool isLevelSensitive)
{
u64 flags = recursiveSpinlockLockMaskIrq(&g_irqManager.lock);
doConfigureInterrupt(id, prio, isLevelSensitive);
recursiveSpinlockUnlockRestoreIrq(&g_irqManager.lock, flags);
}
void irqSetAffinity(u16 id, u8 affinity)
{
u64 flags = recursiveSpinlockLockMaskIrq(&g_irqManager.lock);
gicd->itargetsr[id] = affinity;
recursiveSpinlockUnlockRestoreIrq(&g_irqManager.lock, flags);
}
bool irqIsGuest(u16 id)
{
if (id >= 32 + g_irqManager.numSharedInterrupts) {
DEBUG("vgic: %u not supported by physical distributor\n", (u32)id);
return false;
}
bool ret = true;
ret = ret && id != GIC_IRQID_MAINTENANCE;
ret = ret && id != GIC_IRQID_NS_PHYS_HYP_TIMER;
// If the following interrupts don't exist, that's fine, they're defined as GIC_IRQID_SPURIOUS in that case
// (for which the function isn't called, anyway)
ret = ret && id != GIC_IRQID_NS_VIRT_HYP_TIMER;
ret = ret && id != GIC_IRQID_SEC_PHYS_HYP_TIMER;
ret = ret && id != GIC_IRQID_SEC_VIRT_HYP_TIMER;
ret = ret && transportInterfaceFindByIrqId(id) == NULL;
return ret;
}
void handleIrqException(ExceptionStackFrame *frame, bool isLowerEl, bool isA32)
{
(void)isLowerEl;
(void)isA32;
// Acknowledge the interrupt. Interrupt goes from pending to active.
u32 iar = gicc->iar;
u32 irqId = iar & 0x3FF;
u32 srcCore = (iar >> 10) & 7;
//DEBUG("EL2 [core %d]: Received irq %x\n", (int)currentCoreCtx->coreId, irqId);
if (irqId == GIC_IRQID_SPURIOUS) {
// Spurious interrupt received
return;
} else if (!checkGuestTimerInterrupts(frame, irqId)) {
// Deactivate the interrupt, return early
gicc->eoir = iar;
gicc->dir = iar;
return;
}
bool isGuestInterrupt = false;
bool isMaintenanceInterrupt = false;
bool isPaused = false;
bool hasDebugEvent = false;
switch (irqId) {
case ThermosphereSgi_ExecuteFunction:
executeFunctionInterruptHandler(srcCore);
break;
case ThermosphereSgi_VgicUpdate:
// Nothing in particular to do here
break;
case ThermosphereSgi_DebugPause:
debugManagerPauseSgiHandler();
break;
case ThermosphereSgi_ReportDebuggerBreak:
case ThermosphereSgi_DebuggerContinue:
// See bottom halves
// Because exceptions (other debug events) are handling w/ interrupts off, if
// we get there, there's no race condition possible with debugManagerReportEvent
break;
case GIC_IRQID_MAINTENANCE:
isMaintenanceInterrupt = true;
break;
case TIMER_IRQID(CURRENT_TIMER):
timerInterruptHandler();
break;
default:
isGuestInterrupt = irqId >= 16;
break;
}
TransportInterface *transportIface = irqId >= 32 ? transportInterfaceIrqHandlerTopHalf(irqId) : NULL;
// Priority drop
gicc->eoir = iar;
isGuestInterrupt = isGuestInterrupt && transportIface == NULL && irqIsGuest(irqId);
recursiveSpinlockLock(&g_irqManager.lock);
if (!isGuestInterrupt) {
if (isMaintenanceInterrupt) {
vgicMaintenanceInterruptHandler();
}
// Deactivate the interrupt
gicc->dir = iar;
} else {
vgicEnqueuePhysicalIrq(irqId);
}
// Update vgic state
vgicUpdateState();
recursiveSpinlockUnlock(&g_irqManager.lock);
isPaused = debugManagerIsCorePaused(currentCoreCtx->coreId);
hasDebugEvent = debugManagerHasDebugEvent(currentCoreCtx->coreId);
if (irqId == ThermosphereSgi_ReportDebuggerBreak) DEBUG("debug event=%d\n", (int)debugManagerGetDebugEvent(currentCoreCtx->coreId)->type);
// Bottom half part
if (transportIface != NULL) {
exceptionEnterInterruptibleHypervisorCode();
unmaskIrq();
transportInterfaceIrqHandlerBottomHalf(transportIface);
} else if (irqId == ThermosphereSgi_ReportDebuggerBreak && !hasDebugEvent) {
debugManagerReportEvent(DBGEVENT_DEBUGGER_BREAK);
} else if (irqId == ThermosphereSgi_DebuggerContinue && isPaused) {
debugManagerUnpauseCores(BIT(currentCoreCtx->coreId));
}
}

View file

@ -1,352 +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 <string.h>
#include "pattern_utils.h"
u32 hexItoa(u64 number, char *out, u32 digits, bool uppercase)
{
static const char hexDigits[] = "0123456789ABCDEF";
static const char hexDigitsLowercase[] = "0123456789abcdef";
u32 i = 0;
while (number > 0) {
out[digits - 1 - i++] = uppercase ? hexDigits[number & 0xF] : hexDigitsLowercase[number & 0xF];
number >>= 4;
}
while (i < digits) out[digits - 1 - i++] = '0';
return digits;
}
//Boyer-Moore Horspool algorithm, adapted from http://www-igm.univ-mlv.fr/~lecroq/string/node18.html#SECTION00180
//u32 size to limit stack usage
u8 *memsearch(u8 *startPos, const void *pattern, u32 size, u32 patternSize)
{
const u8 *patternc = (const u8 *)pattern;
u32 table[256];
//Preprocessing
for(u32 i = 0; i < 256; i++)
table[i] = patternSize;
for(u32 i = 0; i < patternSize - 1; i++)
table[patternc[i]] = patternSize - i - 1;
//Searching
u32 j = 0;
while(j <= size - patternSize)
{
u8 c = startPos[j + patternSize - 1];
if(patternc[patternSize - 1] == c && memcmp(pattern, startPos + j, patternSize - 1) == 0)
return startPos + j;
j += table[c];
}
return NULL;
}
// Copied from newlib, without the reent stuff + some other stuff
/*
* Copyright (c) 1990 Regents of the University of California.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
unsigned int xstrtoui(const char *nptr, char **endptr, int base, bool allowPrefix, bool *ok)
{
register const unsigned char *s = (const unsigned char *)nptr;
register unsigned int acc;
register int c;
register unsigned int cutoff;
register int neg = 0, any, cutlim;
if(ok != NULL)
*ok = true;
/*
* See strtol for comments as to the logic used.
*/
do {
c = *s++;
} while ((c >= 9 && c <= 13) || c == ' ');
if (c == '-') {
if(!allowPrefix) {
if(ok != NULL)
*ok = false;
return 0;
}
neg = 1;
c = *s++;
} else if (c == '+'){
if(!allowPrefix) {
if(ok != NULL)
*ok = false;
return 0;
}
c = *s++;
}
if ((base == 0 || base == 16) &&
c == '0' && (*s == 'x' || *s == 'X')) {
if(!allowPrefix) {
if(ok != NULL)
*ok = false;
return 0;
}
c = s[1];
s += 2;
base = 16;
}
if (base == 0) {
if(!allowPrefix) {
if(ok != NULL)
*ok = false;
return 0;
}
base = c == '0' ? 8 : 10;
}
cutoff = (unsigned int)(-1) / (unsigned int)base;
cutlim = (unsigned int)(-1) % (unsigned int)base;
for (acc = 0, any = 0;; c = *s++) {
if (c >= '0' && c <= '9')
c -= '0';
else if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z'))
c -= c >= 'A' && c <= 'Z' ? 'A' - 10 : 'a' - 10;
else
break;
if (c >= base)
break;
if (any < 0 || acc > cutoff || (acc == cutoff && c > cutlim))
any = -1;
else {
any = 1;
acc *= base;
acc += c;
}
}
if (any < 0) {
acc = (unsigned int)-1;
if(ok != NULL)
*ok = false;
// rptr->_errno = ERANGE;
} else if (neg)
acc = -acc;
if (endptr != 0)
*endptr = (char *) (any ? (char *)s - 1 : nptr);
return (acc);
}
// Copied from newlib, without the reent stuff + some other stuff
unsigned long int xstrtoul(const char *nptr, char **endptr, int base, bool allowPrefix, bool *ok)
{
register const unsigned char *s = (const unsigned char *)nptr;
register unsigned long acc;
register int c;
register unsigned long cutoff;
register int neg = 0, any, cutlim;
if(ok != NULL)
*ok = true;
/*
* See strtol for comments as to the logic used.
*/
do {
c = *s++;
} while ((c >= 9 && c <= 13) || c == ' ');
if (c == '-') {
if(!allowPrefix) {
if(ok != NULL)
*ok = false;
return 0;
}
neg = 1;
c = *s++;
} else if (c == '+'){
if(!allowPrefix) {
if(ok != NULL)
*ok = false;
return 0;
}
c = *s++;
}
if ((base == 0 || base == 16) &&
c == '0' && (*s == 'x' || *s == 'X')) {
if(!allowPrefix) {
if(ok != NULL)
*ok = false;
return 0;
}
c = s[1];
s += 2;
base = 16;
}
if (base == 0) {
if(!allowPrefix) {
if(ok != NULL)
*ok = false;
return 0;
}
base = c == '0' ? 8 : 10;
}
cutoff = (unsigned long)(-1) / (unsigned long)base;
cutlim = (unsigned long)(-1) % (unsigned long)base;
for (acc = 0, any = 0;; c = *s++) {
if (c >= '0' && c <= '9')
c -= '0';
else if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z'))
c -= c >= 'A' && c <= 'Z' ? 'A' - 10 : 'a' - 10;
else
break;
if (c >= base)
break;
if (any < 0 || acc > cutoff || (acc == cutoff && c > cutlim))
any = -1;
else {
any = 1;
acc *= base;
acc += c;
}
}
if (any < 0) {
acc = (unsigned long)-1;
if(ok != NULL)
*ok = false;
// rptr->_errno = ERANGE;
} else if (neg)
acc = -acc;
if (endptr != 0)
*endptr = (char *) (any ? (char *)s - 1 : nptr);
return (acc);
}
// Copied from newlib, without the reent stuff + some other stuff
unsigned long long int xstrtoull(const char *nptr, char **endptr, int base, bool allowPrefix, bool *ok)
{
register const unsigned char *s = (const unsigned char *)nptr;
register unsigned long long acc;
register int c;
register unsigned long long cutoff;
register int neg = 0, any, cutlim;
if(ok != NULL)
*ok = true;
/*
* See strtol for comments as to the logic used.
*/
do {
c = *s++;
} while ((c >= 9 && c <= 13) || c == ' ');
if (c == '-') {
if(!allowPrefix) {
if(ok != NULL)
*ok = false;
return 0;
}
neg = 1;
c = *s++;
} else if (c == '+'){
if(!allowPrefix) {
if(ok != NULL)
*ok = false;
return 0;
}
c = *s++;
}
if ((base == 0 || base == 16) &&
c == '0' && (*s == 'x' || *s == 'X')) {
if(!allowPrefix) {
if(ok != NULL)
*ok = false;
return 0;
}
c = s[1];
s += 2;
base = 16;
}
if (base == 0) {
if(!allowPrefix) {
if(ok != NULL)
*ok = false;
return 0;
}
base = c == '0' ? 8 : 10;
}
cutoff = (unsigned long long)(-1ull) / (unsigned long long)base;
cutlim = (unsigned long long)(-1ull) % (unsigned long long)base;
for (acc = 0, any = 0;; c = *s++) {
if (c >= '0' && c <= '9')
c -= '0';
else if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z'))
c -= c >= 'A' && c <= 'Z' ? 'A' - 10 : 'a' - 10;
else
break;
if (c >= base)
break;
if (any < 0 || acc > cutoff || (acc == cutoff && c > cutlim))
any = -1;
else {
any = 1;
acc *= base;
acc += c;
}
}
if (any < 0) {
acc = (unsigned long long)-1ull;
if(ok != NULL)
*ok = false;
// rptr->_errno = ERANGE;
} else if (neg)
acc = -acc;
if (endptr != 0)
*endptr = (char *) (any ? (char *)s - 1 : nptr);
return (acc);
}

View file

@ -1,28 +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/>.
*/
#pragma once
#include "utils.h"
u32 hexItoa(u64 number, char *out, u32 digits, bool uppercase);
// u32 size to limit stack usage
// Not sure if we need this function because we can only map one guest page at a time...
u8 *memsearch(u8 *startPos, const void *pattern, u32 size, u32 patternSize);
unsigned int xstrtoui(const char *nptr, char **endptr, int base, bool allowPrefix, bool *ok);
unsigned long int xstrtoul(const char *nptr, char **endptr, int base, bool allowPrefix, bool *ok);
unsigned long long int xstrtoull(const char *nptr, char **endptr, int base, bool allowPrefix, bool *ok);

View file

@ -1,243 +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 <string.h>
#include "software_breakpoints.h"
#include "utils.h"
#include "guest_memory.h"
#include "core_ctx.h"
SoftwareBreakpointManager g_softwareBreakpointManager = {0};
/*
Consider the following:
- Breakpoints are based on VA
- Translation tables may change
- Translation tables may differ from core to core
We also define sw breakpoints on invalid addresses (for one or more cores) UNPREDICTABLE.
*/
static size_t findClosestSoftwareBreakpointSlot(uintptr_t address)
{
if(g_softwareBreakpointManager.numBreakpoints == 0 || address <= g_softwareBreakpointManager.breakpoints[0].address) {
return 0;
} else if(address > g_softwareBreakpointManager.breakpoints[g_softwareBreakpointManager.numBreakpoints - 1].address) {
return g_softwareBreakpointManager.numBreakpoints;
}
size_t a = 0, b = g_softwareBreakpointManager.numBreakpoints - 1, m;
do {
m = (a + b) / 2;
if(g_softwareBreakpointManager.breakpoints[m].address < address) {
a = m;
} else if(g_softwareBreakpointManager.breakpoints[m].address > address) {
b = m;
} else {
return m;
}
} while(b - a > 1);
return b;
}
static inline bool doApplySoftwareBreakpoint(size_t id)
{
SoftwareBreakpoint *bp = &g_softwareBreakpointManager.breakpoints[id];
u32 brkInst = 0xD4200000 | (bp->uid << 5);
size_t sz = guestReadWriteMemory(bp->address, 4, &bp->savedInstruction, &brkInst);
bp->applied = sz == 4;
atomic_store(&bp->triedToApplyOrRevert, true);
return sz == 4;
}
static void applySoftwareBreakpointHandler(void *p)
{
size_t id = *(size_t *)p;
if (currentCoreCtx->coreId == currentCoreCtx->executedFunctionSrcCore) {
doApplySoftwareBreakpoint(id);
__sev();
} else {
SoftwareBreakpoint *bp = &g_softwareBreakpointManager.breakpoints[id];
while (!atomic_load(&bp->triedToApplyOrRevert)) {
__wfe();
}
}
}
static bool applySoftwareBreakpoint(size_t id)
{
if (g_softwareBreakpointManager.breakpoints[id].applied) {
return true;
}
// This is okay for non-stop mode if sync isn't perfect here
atomic_store(&g_softwareBreakpointManager.breakpoints[id].triedToApplyOrRevert, false);
executeFunctionOnAllCores(applySoftwareBreakpointHandler, &id, true);
atomic_signal_fence(memory_order_seq_cst);
return g_softwareBreakpointManager.breakpoints[id].applied;
}
static inline bool doRevertSoftwareBreakpoint(size_t id)
{
SoftwareBreakpoint *bp = &g_softwareBreakpointManager.breakpoints[id];
size_t sz = guestWriteMemory(bp->address, 4, &bp->savedInstruction);
bp->applied = sz != 4;
atomic_store(&bp->triedToApplyOrRevert, true);
return sz == 4;
}
static void revertSoftwareBreakpointHandler(void *p)
{
size_t id = *(size_t *)p;
if (currentCoreCtx->coreId == currentCoreCtx->executedFunctionSrcCore) {
doRevertSoftwareBreakpoint(id);
__sev();
} else {
SoftwareBreakpoint *bp = &g_softwareBreakpointManager.breakpoints[id];
while (!atomic_load(&bp->triedToApplyOrRevert)) {
__wfe();
}
}
}
static bool revertSoftwareBreakpoint(size_t id)
{
if (!g_softwareBreakpointManager.breakpoints[id].applied) {
return true;
}
atomic_store(&g_softwareBreakpointManager.breakpoints[id].triedToApplyOrRevert, false);
executeFunctionOnAllCores(revertSoftwareBreakpointHandler, &id, true);
atomic_signal_fence(memory_order_seq_cst);
return !g_softwareBreakpointManager.breakpoints[id].applied;
}
bool applyAllSoftwareBreakpoints(void)
{
u64 flags = recursiveSpinlockLockMaskIrq(&g_softwareBreakpointManager.lock);
bool ret = true;
for (size_t i = 0; i < g_softwareBreakpointManager.numBreakpoints; i++) {
ret = ret && doApplySoftwareBreakpoint(i); // note: no broadcast intentional
}
recursiveSpinlockUnlockRestoreIrq(&g_softwareBreakpointManager.lock, flags);
return ret;
}
bool revertAllSoftwareBreakpoints(void)
{
u64 flags = recursiveSpinlockLockMaskIrq(&g_softwareBreakpointManager.lock);
bool ret = true;
for (size_t i = 0; i < g_softwareBreakpointManager.numBreakpoints; i++) {
ret = ret && doRevertSoftwareBreakpoint(i); // note: no broadcast intentional
}
recursiveSpinlockUnlockRestoreIrq(&g_softwareBreakpointManager.lock, flags);
return ret;
}
int addSoftwareBreakpoint(uintptr_t addr, bool persistent)
{
if ((addr & 3) != 0) {
return -EINVAL;
}
recursiveSpinlockLock(&g_softwareBreakpointManager.lock);
size_t id = findClosestSoftwareBreakpointSlot(addr);
if(id != g_softwareBreakpointManager.numBreakpoints && g_softwareBreakpointManager.breakpoints[id].address == addr) {
recursiveSpinlockUnlock(&g_softwareBreakpointManager.lock);
return -EEXIST;
} else if(g_softwareBreakpointManager.numBreakpoints == MAX_SW_BREAKPOINTS) {
recursiveSpinlockUnlock(&g_softwareBreakpointManager.lock);
return -EBUSY;
}
for(size_t i = g_softwareBreakpointManager.numBreakpoints; i > id && i != 0; i--) {
g_softwareBreakpointManager.breakpoints[i] = g_softwareBreakpointManager.breakpoints[i - 1];
}
++g_softwareBreakpointManager.numBreakpoints;
SoftwareBreakpoint *bp = &g_softwareBreakpointManager.breakpoints[id];
bp->address = addr;
bp->persistent = persistent;
bp->applied = false;
bp->uid = (u16)(0x2000 + g_softwareBreakpointManager.bpUniqueCounter++);
int rc = applySoftwareBreakpoint(id) ? 0 : -EFAULT;
recursiveSpinlockUnlock(&g_softwareBreakpointManager.lock);
return rc;
}
int removeSoftwareBreakpoint(uintptr_t addr, bool keepPersistent)
{
if ((addr & 3) != 0) {
return -EINVAL;
}
recursiveSpinlockLock(&g_softwareBreakpointManager.lock);
size_t id = findClosestSoftwareBreakpointSlot(addr);
bool ok = true;
if(id == g_softwareBreakpointManager.numBreakpoints || g_softwareBreakpointManager.breakpoints[id].address != addr) {
recursiveSpinlockUnlock(&g_softwareBreakpointManager.lock);
return -ENOENT;
}
SoftwareBreakpoint *bp = &g_softwareBreakpointManager.breakpoints[id];
if (!keepPersistent || !bp->persistent) {
ok = revertSoftwareBreakpoint(id);
}
for(size_t i = id; i < g_softwareBreakpointManager.numBreakpoints - 1; i++) {
g_softwareBreakpointManager.breakpoints[i] = g_softwareBreakpointManager.breakpoints[i + 1];
}
memset(&g_softwareBreakpointManager.breakpoints[--g_softwareBreakpointManager.numBreakpoints], 0, sizeof(SoftwareBreakpoint));
recursiveSpinlockUnlock(&g_softwareBreakpointManager.lock);
return ok ? 0 : -EFAULT;
}
int removeAllSoftwareBreakpoints(bool keepPersistent)
{
bool ok = true;
recursiveSpinlockLock(&g_softwareBreakpointManager.lock);
for (size_t id = 0; id < g_softwareBreakpointManager.numBreakpoints; id++) {
SoftwareBreakpoint *bp = &g_softwareBreakpointManager.breakpoints[id];
if (!keepPersistent || !bp->persistent) {
ok = ok && revertSoftwareBreakpoint(id);
}
}
g_softwareBreakpointManager.numBreakpoints = 0;
g_softwareBreakpointManager.bpUniqueCounter = 0;
memset(g_softwareBreakpointManager.breakpoints, 0, sizeof(g_softwareBreakpointManager.breakpoints));
recursiveSpinlockUnlock(&g_softwareBreakpointManager.lock);
return ok ? 0 : -EFAULT;
}

View file

@ -1,50 +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/>.
*/
#pragma once
#define _REENT_ONLY
#include <errno.h>
#include <stdatomic.h>
#include "spinlock.h"
#define MAX_SW_BREAKPOINTS 16
typedef struct SoftwareBreakpoint {
uintptr_t address; // VA
u32 savedInstruction;
u16 uid;
bool persistent;
bool applied;
atomic_bool triedToApplyOrRevert;
} SoftwareBreakpoint;
typedef struct SoftwareBreakpointManager {
RecursiveSpinlock lock;
size_t numBreakpoints;
SoftwareBreakpoint breakpoints[MAX_SW_BREAKPOINTS];
u32 bpUniqueCounter;
} SoftwareBreakpointManager;
extern SoftwareBreakpointManager g_softwareBreakpointManager;
bool revertAllSoftwareBreakpoints(void);
bool applyAllSoftwareBreakpoints(void);
int addSoftwareBreakpoint(uintptr_t addr, bool persistent);
int removeSoftwareBreakpoint(uintptr_t addr, bool keepPersistent);
int removeAllSoftwareBreakpoints(bool keepPersistent);

View file

@ -1,53 +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 "spinlock.h"
#include "core_ctx.h"
void recursiveSpinlockLock(RecursiveSpinlock *lock)
{
if (LIKELY(lock->tag != currentCoreCtx->coreId + 1)) {
spinlockLock(&lock->lock);
lock->tag = currentCoreCtx->coreId + 1;
lock->count = 1;
} else {
++lock->count;
}
}
bool recursiveSpinlockTryLock(RecursiveSpinlock *lock)
{
if (LIKELY(lock->tag != currentCoreCtx->coreId + 1)) {
if (!spinlockTryLock(&lock->lock)) {
return false;
} else {
lock->tag = currentCoreCtx->coreId + 1;
lock->count = 1;
return true;
}
} else {
++lock->count;
return true;
}
}
void recursiveSpinlockUnlock(RecursiveSpinlock *lock)
{
if (LIKELY(--lock->count == 0)) {
lock->tag = 0;
spinlockUnlock(&lock->lock);
}
}

View file

@ -1,84 +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/>.
*/
#pragma once
#include "utils.h"
#include "sysreg.h"
typedef struct Spinlock {
u32 lock;
} Spinlock;
typedef struct RecursiveSpinlock {
Spinlock lock;
u32 count;
vu32 tag;
} RecursiveSpinlock;
static inline u64 maskIrq(void)
{
u64 ret = GET_SYSREG(daif);
SET_SYSREG_IMM(daifset, BITL(1));
return ret;
}
static inline u64 unmaskIrq(void)
{
u64 ret = GET_SYSREG(daif);
SET_SYSREG_IMM(daifclr, BITL(1));
return ret;
}
static inline void restoreInterruptFlags(u64 flags)
{
SET_SYSREG(daif, flags);
}
// spinlock_impl.s
void spinlockLock(Spinlock *lock);
bool spinlockTryLock(Spinlock *lock);
void spinlockUnlock(Spinlock *lock);
void recursiveSpinlockLock(RecursiveSpinlock *lock);
bool recursiveSpinlockTryLock(RecursiveSpinlock *lock);
void recursiveSpinlockUnlock(RecursiveSpinlock *lock);
static inline u64 spinlockLockMaskIrq(Spinlock *lock)
{
u64 ret = maskIrq();
spinlockLock(lock);
return ret;
}
static inline void spinlockUnlockRestoreIrq(Spinlock *lock, u64 flags)
{
spinlockUnlock(lock);
restoreInterruptFlags(flags);
}
static inline u64 recursiveSpinlockLockMaskIrq(RecursiveSpinlock *lock)
{
u64 ret = maskIrq();
recursiveSpinlockLock(lock);
return ret;
}
static inline void recursiveSpinlockUnlockRestoreIrq(RecursiveSpinlock *lock, u64 flags)
{
recursiveSpinlockUnlock(lock);
restoreInterruptFlags(flags);
}

View file

@ -1,59 +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 "asm_macros.s"
// From Arm TF
/*
* Copyright (c) 2013-2019, ARM Limited and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
FUNCTION spinlockLock
mov w2, #1
sevl
prfm pstl1keep, [x0]
1:
wfe
2:
ldaxr w1, [x0]
cbnz w1, 1b
stxr w1, w2, [x0]
cbnz w1, 2b
ret
END_FUNCTION
FUNCTION spinlockTryLock
mov x1, x0
mov w2, #1
prfm pstl1keep, [x1]
1:
ldaxr w0, [x1]
cbnz w0, 2f
stxr w0, w2, [x1]
cbnz w0, 1b
2:
eor w0, w0, #1
ret
END_FUNCTION
FUNCTION spinlockUnlock
stlr wzr, [x0]
ret
END_FUNCTION

File diff suppressed because it is too large Load diff

View file

@ -1,225 +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 <string.h>
#include "watchpoints.h"
#include "breakpoints_watchpoints_load.h"
#include "utils.h"
#include "core_ctx.h"
#include "execute_function.h"
#include "debug_log.h"
static WatchpointManager g_watchpointManager = {0};
// Init the structure (already in BSS, so already zero-initialized) and load the registers
void initWatchpoints(void)
{
recursiveSpinlockLock(&g_watchpointManager.lock);
if (currentCoreCtx->isBootCore && !currentCoreCtx->warmboot) {
size_t num = ((GET_SYSREG(id_aa64dfr0_el1) >> 20) & 0xF) + 1;
g_watchpointManager.maxWatchpoints = (u32)num;
g_watchpointManager.freeBitmap = BIT(num) - 1;
g_watchpointManager.usedBitmap = 0;
}
loadWatchpointRegs(g_watchpointManager.watchpoints, g_watchpointManager.maxWatchpoints);
recursiveSpinlockUnlock(&g_watchpointManager.lock);
}
static void commitAndBroadcastWatchpointHandler(void *p)
{
(void)p;
u64 flags = maskIrq();
loadWatchpointRegs(g_watchpointManager.watchpoints, g_watchpointManager.maxWatchpoints);
restoreInterruptFlags(flags);
}
static inline void commitAndBroadcastWatchpoints(void)
{
__dmb();
executeFunctionOnAllCores(commitAndBroadcastWatchpointHandler, NULL, true);
}
static DebugRegisterPair *allocateWatchpoint(void)
{
u32 pos = __builtin_ffs(g_watchpointManager.freeBitmap);
if (pos == 0) {
return NULL;
} else {
g_watchpointManager.freeBitmap &= ~BIT(pos - 1);
g_watchpointManager.usedBitmap |= BIT(pos - 1);
return &g_watchpointManager.watchpoints[pos - 1];
}
}
static void freeWatchpoint(u32 pos)
{
memset(&g_watchpointManager.watchpoints[pos], 0, sizeof(DebugRegisterPair));
g_watchpointManager.freeBitmap |= BIT(pos);
g_watchpointManager.usedBitmap &= ~BIT(pos);
}
static inline bool isRangeMaskWatchpoint(uintptr_t addr, size_t size)
{
// size needs to be a power of 2, at least 8 (we'll only allow 16+ though), addr needs to be aligned.
bool ret = (size & (size - 1)) == 0 && size >= 16 && (addr & (size - 1)) == 0;
return ret;
}
// Size = 0 means nonstrict
static DebugRegisterPair *findWatchpoint(uintptr_t addr, size_t size, WatchpointLoadStoreControl direction)
{
FOREACH_BIT (tmp, i, g_watchpointManager.usedBitmap) {
DebugRegisterPair *wp = &g_watchpointManager.watchpoints[i];
size_t off;
size_t sz;
size_t nmask;
if (wp->cr.mask != 0) {
off = 0;
sz = MASK(wp->cr.mask);
nmask = ~sz;
} else {
off = __builtin_ffs(wp->cr.bas) - 1;
sz = __builtin_popcount(wp->cr.bas);
nmask = ~7ul;
}
if (size != 0) {
// Strict watchpoint check
if (addr == wp->vr + off && direction == wp->cr.lsc && sz == size) {
return wp;
}
} else {
// Return first wp that could have triggered the exception
if ((addr & nmask) == wp->vr && (direction & wp->cr.lsc) != 0) {
return wp;
}
}
}
return NULL;
}
DebugControlRegister retrieveWatchpointConfig(uintptr_t addr, WatchpointLoadStoreControl direction)
{
recursiveSpinlockLock(&g_watchpointManager.lock);
DebugRegisterPair *wp = findWatchpoint(addr, 0, direction);
DebugControlRegister ret = { 0 };
if (wp != NULL) {
ret = wp->cr;
}
recursiveSpinlockUnlock(&g_watchpointManager.lock);
return ret;
}
static inline bool checkWatchpointAddressAndSizeParams(uintptr_t addr, size_t size)
{
if (size == 0) {
return false;
} else if (size > 8) {
return isRangeMaskWatchpoint(addr, size);
} else {
return ((addr + size) & ~7ul) == (addr & ~7ul);
}
}
int addWatchpoint(uintptr_t addr, size_t size, WatchpointLoadStoreControl direction)
{
if (!checkWatchpointAddressAndSizeParams(addr, size)) {
return -EINVAL;
}
recursiveSpinlockLock(&g_watchpointManager.lock);
if (g_watchpointManager.freeBitmap == 0) {
recursiveSpinlockUnlock(&g_watchpointManager.lock);
return -EBUSY;
}
if (findWatchpoint(addr, size, direction)) {
recursiveSpinlockUnlock(&g_watchpointManager.lock);
return -EEXIST;
}
DebugRegisterPair *wp = allocateWatchpoint();
memset(wp, 0, sizeof(DebugRegisterPair));
wp->cr.lsc = direction;
if (isRangeMaskWatchpoint(addr, size)) {
wp->vr = addr;
wp->cr.bas = 0xFF; // TRM-mandated
wp->cr.mask = (u32)__builtin_ffsl(size) - 1;
} else {
size_t off = addr & 7ull;
wp->vr = addr & ~7ul;
wp->cr.bas = MASK2(off + size, off);
}
wp->cr.linked = false;
wp->cr.hmc = DebugHmc_LowerEl;
wp->cr.ssc = DebugSsc_NonSecure;
wp->cr.pmc = DebugPmc_El1And0;
wp->cr.enabled = true;
commitAndBroadcastWatchpoints();
recursiveSpinlockUnlock(&g_watchpointManager.lock);
return 0;
}
int removeWatchpoint(uintptr_t addr, size_t size, WatchpointLoadStoreControl direction)
{
if (!checkWatchpointAddressAndSizeParams(addr, size)) {
return -EINVAL;
}
recursiveSpinlockLock(&g_watchpointManager.lock);
DebugRegisterPair *wp = findWatchpoint(addr, size, direction);
if (wp != NULL) {
freeWatchpoint(wp - &g_watchpointManager.watchpoints[0]);
} else {
recursiveSpinlockUnlock(&g_watchpointManager.lock);
return -ENOENT;
}
commitAndBroadcastWatchpoints();
recursiveSpinlockUnlock(&g_watchpointManager.lock);
return 0;
}
int removeAllWatchpoints(void)
{
// Yeet it all
recursiveSpinlockLock(&g_watchpointManager.lock);
g_watchpointManager.freeBitmap |= g_watchpointManager.usedBitmap;
g_watchpointManager.usedBitmap = 0;
memset(g_watchpointManager.watchpoints, 0, sizeof(g_watchpointManager.watchpoints));
commitAndBroadcastWatchpoints();
recursiveSpinlockUnlock(&g_watchpointManager.lock);
return 0;
}

View file

@ -1,38 +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/>.
*/
#pragma once
#define _REENT_ONLY
#include <errno.h>
#include "breakpoints_watchpoints_common.h"
#include "spinlock.h"
/// Structure to synchronize and keep track of watchpoints
typedef struct WatchpointManager {
DebugRegisterPair watchpoints[MAX_WCR];
RecursiveSpinlock lock;
u32 maxWatchpoints;
u16 freeBitmap;
u16 usedBitmap;
} WatchpointManager;
void initWatchpoints(void);
DebugControlRegister retrieveWatchpointConfig(uintptr_t addr, WatchpointLoadStoreControl direction);
int addWatchpoint(uintptr_t addr, size_t size, WatchpointLoadStoreControl direction);
int removeWatchpoint(uintptr_t addr, size_t size, WatchpointLoadStoreControl direction);
int removeAllWatchpoints(void);