diff --git a/thermosphere/src/barrier.c b/thermosphere/src/barrier.c
deleted file mode 100644
index bc191f521..000000000
--- a/thermosphere/src/barrier.c
+++ /dev/null
@@ -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 .
- */
-
-#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);
-}
diff --git a/thermosphere/src/barrier.h b/thermosphere/src/barrier.h
deleted file mode 100644
index b14667427..000000000
--- a/thermosphere/src/barrier.h
+++ /dev/null
@@ -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 .
- */
-
-#pragma once
-
-#include
-#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);
diff --git a/thermosphere/src/breakpoints.c b/thermosphere/src/breakpoints.c
deleted file mode 100644
index adbdffe85..000000000
--- a/thermosphere/src/breakpoints.c
+++ /dev/null
@@ -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 .
- */
-
-#include
-#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;
-}
diff --git a/thermosphere/src/breakpoints.h b/thermosphere/src/breakpoints.h
deleted file mode 100644
index 30876c77c..000000000
--- a/thermosphere/src/breakpoints.h
+++ /dev/null
@@ -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 .
- */
-
-#pragma once
-
-#include "breakpoints_watchpoints_common.h"
-#include "spinlock.h"
-
-#define _REENT_ONLY
-#include
-
-/// 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);
diff --git a/thermosphere/src/breakpoints_watchpoints_common.h b/thermosphere/src/breakpoints_watchpoints_common.h
deleted file mode 100644
index 577ca8934..000000000
--- a/thermosphere/src/breakpoints_watchpoints_common.h
+++ /dev/null
@@ -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 .
- */
-
-#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);
-}
diff --git a/thermosphere/src/breakpoints_watchpoints_load.h b/thermosphere/src/breakpoints_watchpoints_load.h
deleted file mode 100644
index 325e30d70..000000000
--- a/thermosphere/src/breakpoints_watchpoints_load.h
+++ /dev/null
@@ -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 .
- */
-
-#pragma once
-#include "breakpoints_watchpoints_common.h"
-
-void loadBreakpointRegs(const DebugRegisterPair *regs, size_t num);
-void loadWatchpointRegs(const DebugRegisterPair *regs, size_t num);
diff --git a/thermosphere/src/breakpoints_watchpoints_load.s b/thermosphere/src/breakpoints_watchpoints_load.s
deleted file mode 100644
index 9ca9bbd12..000000000
--- a/thermosphere/src/breakpoints_watchpoints_load.s
+++ /dev/null
@@ -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 .
- */
-
-#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
diff --git a/thermosphere/src/execute_function.c b/thermosphere/src/execute_function.c
deleted file mode 100644
index 8a5ed0a48..000000000
--- a/thermosphere/src/execute_function.c
+++ /dev/null
@@ -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 .
- */
-
-#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(¤tCoreCtx->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);
- }
-}
diff --git a/thermosphere/src/execute_function.h b/thermosphere/src/execute_function.h
deleted file mode 100644
index 202dc546c..000000000
--- a/thermosphere/src/execute_function.h
+++ /dev/null
@@ -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 .
- */
-
-
-#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);
\ No newline at end of file
diff --git a/thermosphere/src/irq.c b/thermosphere/src/irq.c
deleted file mode 100644
index 09823914c..000000000
--- a/thermosphere/src/irq.c
+++ /dev/null
@@ -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 .
- */
-
-#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));
- }
-
-}
diff --git a/thermosphere/src/pattern_utils.c b/thermosphere/src/pattern_utils.c
deleted file mode 100644
index e26d9df2f..000000000
--- a/thermosphere/src/pattern_utils.c
+++ /dev/null
@@ -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 .
- */
-
-#include
-#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);
-}
diff --git a/thermosphere/src/pattern_utils.h b/thermosphere/src/pattern_utils.h
deleted file mode 100644
index 03bf5f62d..000000000
--- a/thermosphere/src/pattern_utils.h
+++ /dev/null
@@ -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 .
- */
-
-#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);
diff --git a/thermosphere/src/software_breakpoints.c b/thermosphere/src/software_breakpoints.c
deleted file mode 100644
index 841103eeb..000000000
--- a/thermosphere/src/software_breakpoints.c
+++ /dev/null
@@ -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 .
- */
-
-#include
-#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;
-}
diff --git a/thermosphere/src/software_breakpoints.h b/thermosphere/src/software_breakpoints.h
deleted file mode 100644
index 4a19414fc..000000000
--- a/thermosphere/src/software_breakpoints.h
+++ /dev/null
@@ -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 .
- */
-
-#pragma once
-
-#define _REENT_ONLY
-#include
-
-#include
-#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);
diff --git a/thermosphere/src/spinlock.c b/thermosphere/src/spinlock.c
deleted file mode 100644
index c96e90550..000000000
--- a/thermosphere/src/spinlock.c
+++ /dev/null
@@ -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 .
- */
-
-#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);
- }
-}
diff --git a/thermosphere/src/spinlock.h b/thermosphere/src/spinlock.h
deleted file mode 100644
index 12c0c685e..000000000
--- a/thermosphere/src/spinlock.h
+++ /dev/null
@@ -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 .
- */
-
-#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);
-}
diff --git a/thermosphere/src/spinlock_impl.s b/thermosphere/src/spinlock_impl.s
deleted file mode 100644
index 2382d087c..000000000
--- a/thermosphere/src/spinlock_impl.s
+++ /dev/null
@@ -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 .
- */
-
-#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
diff --git a/thermosphere/src/vgic.c b/thermosphere/src/vgic.c
deleted file mode 100644
index 09fba8f22..000000000
--- a/thermosphere/src/vgic.c
+++ /dev/null
@@ -1,1074 +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 "vgic.h"
-#include "irq.h"
-#include "utils.h"
-#include "core_ctx.h"
-
-#include "debug_log.h"
-
-// Referring to array bounds:
-#define SPI_END_INDEX (GIC_IRQID_MAX + 1 - 32)
-#define MAX_NUM_INTERRUPTS (SPI_END_INDEX + 32 * 4)
-
-#define VIRQLIST_END_ID MAX_NUM_INTERRUPTS
-#define VIRQLIST_INVALID_ID (VIRQLIST_END_ID + 1)
-
-#define GICDOFF(field) (offsetof(ArmGicV2Distributor, field))
-
-typedef struct VirqState {
- u32 listPrev : 11;
- u32 listNext : 11;
- u32 priority : 5;
- bool pending : 1;
- bool active : 1;
- bool handled : 1;
- bool pendingLatch : 1;
- bool levelSensitive : 1;
- u32 coreId : 3; // up to 8 cores, but not implemented yet
- u32 targetList : 8;
- bool enabled : 1;
- u64 : 0;
-} VirqState;
-
-typedef struct VirqStateList {
- VirqState *first, *last;
- size_t size;
-} VirqStateList;
-
-// Note: we reset the GIC from wakeup-from-sleep, and expect the guest OS to save/restore state if needed
-static VirqState TEMPORARY g_virqStates[MAX_NUM_INTERRUPTS] = { 0 };
-static VirqStateList TEMPORARY g_virqPendingQueue = { NULL };
-static u8 TEMPORARY g_vgicIncomingSgiPendingSources[4][32] = { { 0 } };
-static u64 TEMPORARY g_vgicUsedLrMap[4] = { 0 };
-
-static bool TEMPORARY g_vgicIsDistributorEnabled = false;
-
-static inline VirqState *vgicGetVirqState(u32 coreId, u16 id)
-{
- if (id >= 32) {
- return &g_virqStates[id - 32];
- } else if (id <= GIC_IRQID_MAX) {
- return &g_virqStates[SPI_END_INDEX + 32 * coreId + id];
- } else {
- // Shouldn't happen -- 1020 is a multiple of 4 and the switch case should catch it
- return NULL;
- }
-}
-
-static inline VirqState *vgicGetNextQueuedVirqState(VirqState *cur)
-{
- return &g_virqStates[cur->listNext];
-}
-
-static inline VirqState *vgicGetPrevQueuedVirqState(VirqState *cur)
-{
- return &g_virqStates[cur->listPrev];
-}
-
-static inline VirqState *vgicGetQueueEnd(void)
-{
- // Note: not a valid pointer -- one past the end
- return &g_virqStates[VIRQLIST_END_ID];
-}
-
-static inline bool vgicIsStateQueued(VirqState *state)
-{
- return state->listPrev != VIRQLIST_INVALID_ID && state->listNext != VIRQLIST_INVALID_ID;
-}
-
-static inline u32 vgicGetVirqStateIndex(VirqState *cur)
-{
- return (u32)(cur - &g_virqStates[0]);
-}
-
-static inline u16 vgicGetVirqStateInterruptId(VirqState *cur)
-{
- u32 idx = vgicGetVirqStateIndex(cur);
- if (idx >= SPI_END_INDEX) {
- return (idx - SPI_END_INDEX) % 32;
- } else {
- return 32 + idx;
- }
-}
-
-static inline u32 vgicGetVirqStateCoreId(VirqState *cur)
-{
- u32 idx = vgicGetVirqStateIndex(cur);
- if (vgicGetVirqStateInterruptId(cur) < 32) {
- return (idx - SPI_END_INDEX) / 32;
- } else {
- return cur->coreId;
- }
-}
-
-static inline u32 vgicGetSgiCurrentSourceCoreId(VirqState *cur)
-{
- return cur->coreId;
-}
-
-// export symbol to avoid build warnings
-void vgicDebugPrintList(VirqStateList *list)
-{
- (void)list;
- DEBUG("[");
- for (VirqState *pos = list->first; pos != vgicGetQueueEnd(); pos = vgicGetNextQueuedVirqState(pos)) {
- DEBUG("%u,", vgicGetVirqStateIndex(pos));
- }
- DEBUG("]\n");
-}
-
-// export symbol to avoid build warnings
-void vgicDebugPrintLrList(void)
-{
- DEBUG("core %u lr [", currentCoreCtx->coreId);
- for (u32 i = 0; i < g_irqManager.numListRegisters; i++) {
- if (g_vgicUsedLrMap[currentCoreCtx->coreId] & BITL(i)) {
- DEBUG("%u,", gich->lr[i].virtualId);
- } else {
- DEBUG("-,");
- }
- }
- DEBUG("]\n");
-}
-
-static inline void vgicInsertVirqStateBefore(VirqStateList *list, VirqState *pos, VirqState *elem)
-{
- ++list->size;
- // Empty list
- if (list->first == vgicGetQueueEnd()) {
- list->first = list->last = elem;
- elem->listPrev = elem->listNext = VIRQLIST_END_ID;
- return;
- }
-
- if (pos == vgicGetQueueEnd()) {
- // Insert after last
- pos = list->last;
- elem->listPrev = vgicGetVirqStateIndex(pos);
- elem->listNext = pos->listNext;
- pos->listNext = vgicGetVirqStateIndex(elem);
- list->last = elem;
- } else {
- // Otherwise, insert before
- u32 idx = vgicGetVirqStateIndex(elem);
- u32 posidx = vgicGetVirqStateIndex(pos);
-
- u32 previdx = pos->listPrev;
- VirqState *prev = vgicGetPrevQueuedVirqState(pos);
-
- elem->listNext = posidx;
- elem->listPrev = previdx;
-
- pos->listPrev = idx;
-
- if (pos == list->first) {
- list->first = elem;
- } else {
- prev->listNext = idx;
- }
- }
-}
-
-// Currently unused
-static inline void vgicInsertVirqStateAfter(VirqStateList *list, VirqState *pos, VirqState *elem)
-{
- ++list->size;
- // Empty list
- if (list->first == vgicGetQueueEnd()) {
- list->first = list->last = elem;
- elem->listPrev = elem->listNext = VIRQLIST_END_ID;
- return;
- }
-
- if (pos == vgicGetQueueEnd()) {
- // Insert before first
- pos = list->first;
-
- elem->listPrev = pos->listPrev;
- elem->listNext = vgicGetVirqStateIndex(pos);
- pos->listPrev = vgicGetVirqStateIndex(elem);
- list->first = elem;
- } else {
- // Otherwise, insert after
- u32 idx = vgicGetVirqStateIndex(elem);
- u32 posidx = vgicGetVirqStateIndex(pos);
-
- u32 nextidx = pos->listPrev;
- VirqState *next = vgicGetNextQueuedVirqState(pos);
-
- elem->listPrev = posidx;
- elem->listNext = nextidx;
-
- pos->listNext = idx;
-
- if (pos == list->last) {
- list->last = elem;
- } else {
- next->listPrev = idx;
- }
- }
-}
-
-static inline int vgicCompareVirqState(const VirqState *a, const VirqState *b)
-{
- // Lower priority number is higher; we sort by descending priority, ie. ascending priority number
- // Put the interrupts that were previously in the LR before the one which don't
- int n1 = (int)(a->priority - b->priority);
- return n1 == 0 ? (int)b->handled - (int)a->handled : n1;
-}
-
-// Note: ordered by priority
-static void vgicEnqueueVirqState(VirqStateList *list, VirqState *elem)
-{
- VirqState *pos;
-
- ENSURE(!vgicIsStateQueued(elem));
-
- for (pos = list->first; pos != vgicGetQueueEnd(); pos = vgicGetNextQueuedVirqState(pos)) {
- // Sort predicate should be stable
- if (vgicCompareVirqState(elem, pos) < 0) {
- break;
- }
- }
-
- vgicInsertVirqStateBefore(list, pos, elem);
-}
-
-static void vgicDequeueVirqState(VirqStateList *list, VirqState *elem)
-{
- VirqState *prev = vgicGetPrevQueuedVirqState(elem);
- VirqState *next = vgicGetNextQueuedVirqState(elem);
-
- ENSURE(vgicIsStateQueued(elem));
-
- --list->size;
- if (prev != vgicGetQueueEnd()) {
- prev->listNext = elem->listNext;
- } else {
- list->first = next;
- }
-
- if (next != vgicGetQueueEnd()) {
- next->listPrev = elem->listPrev;
- } else {
- list->last = prev;
- }
-
- elem->listPrev = elem->listNext = VIRQLIST_INVALID_ID;
-}
-
-static inline void vgicNotifyOtherCoreList(u32 coreList)
-{
- coreList &= ~BIT(currentCoreCtx->coreId);
- if (coreList != 0) {
- generateSgiForList(ThermosphereSgi_VgicUpdate, coreList);
- }
-}
-
-static inline bool vgicIsVirqPending(VirqState *state)
-{
- // In case we emulate ispendr in the future...
- // Note: this function is not 100% reliable. The interrupt might be active-not-pending or inactive
- // but it shouldn't matter since where we use it, it would only cause one extraneous SGI.
- return state->pendingLatch || (state->levelSensitive && state->pending);
-}
-
-static inline void vgicSetVirqPendingState(VirqState *state, bool val)
-{
- if (state->levelSensitive) {
- state->pending = val;
- } else {
- state->pendingLatch = val;
- }
-}
-
-//////////////////////////////////////////////////
-
-static void vgicSetDistributorControlRegister(u32 value)
-{
- // We implement a virtual distributor/interface w/o security extensions.
- // Moreover, we forward all interrupts as Group 0 so that non-secure code that assumes GICv2
- // *with* security extensions (and thus all interrupts fw as group 1 there) still works (bit are in the same positions).
-
- // We don't implement Group 1 interrupts, either (so that's similar to GICv1).
- bool old = g_vgicIsDistributorEnabled;
- g_vgicIsDistributorEnabled = (value & 1) != 0;
-
- // Enable bit is actually just a global enable bit for all irq forwarding, other functions of the GICD aren't affected by it
- if (old != g_vgicIsDistributorEnabled) {
- generateSgiForAllOthers(ThermosphereSgi_VgicUpdate);
- }
-}
-
-static inline u32 vgicGetDistributorControlRegister(void)
-{
- return g_vgicIsDistributorEnabled ? 1 : 0;
-}
-
-static inline u32 vgicGetDistributorTypeRegister(void)
-{
- // See above comment.
- // Therefore, LSPI = 0, SecurityExtn = 0, rest = from physical distributor
- return gicd->typer & 0x7F;
-}
-
-static inline u32 vgicGetDistributorImplementerIdentificationRegister(void)
-{
- u32 iidr = ((u32)'A' << 24); // Product Id: Atmosphère (?)
- iidr |= 2 << 16; // Major revision 2 (GICv2)
- iidr |= 0 << 12; // Minor revision 0
- iidr |= 0x43B; // Implementer: Arm (value copied from physical GICD)
- return iidr;
-}
-
-static void vgicSetInterruptEnabledState(u16 id)
-{
- VirqState *state = vgicGetVirqState(currentCoreCtx->coreId, id);
-
- if (id < 16 || !irqIsGuest(id) || state->enabled) {
- // Nothing to do...
- // Also, ignore for SGIs
- return;
- }
-
- // Similar effects to setting the target list to non-0 when it was 0...
- if (vgicIsVirqPending(state)) {
- vgicNotifyOtherCoreList(state->targetList);
- }
-
- state->enabled = true;
- gicd->isenabler[id / 32] = BIT(id % 32);
-}
-
-static void vgicClearInterruptEnabledState(u16 id)
-{
- VirqState *state = vgicGetVirqState(currentCoreCtx->coreId, id);
-
- if (id < 16 || !irqIsGuest(id) || !state->enabled) {
- // Nothing to do...
- // Also, ignore for SGIs
- return;
- }
-
- // Similar effects to setting the target list to 0, we may need to notify the core
- // handling the interrupt if it's pending
- if (state->handled) {
- vgicNotifyOtherCoreList(BIT(vgicGetVirqStateCoreId(state)));
- }
-
- state->enabled = false;
- gicd->icenabler[id / 32] = BIT(id % 32);
-}
-
-static inline bool vgicGetInterruptEnabledState(u16 id)
-{
- // SGIs are always enabled
- return id < 16 || (irqIsGuest(id) && vgicGetVirqState(currentCoreCtx->coreId, id)->enabled);
-}
-
-static void vgicSetInterruptPriorityByte(u16 id, u8 priority)
-{
- if (!irqIsGuest(id)) {
- return;
- }
-
- // 32 priority levels max, bits [7:3]
- priority >>= 3;
- priority &= 0x1F;
-
- if (id >= 16) {
- // Ensure we have the correct priority on the physical distributor...
- gicd->ipriorityr[id] = IRQ_PRIORITY_GUEST << g_irqManager.priorityShift;
- }
-
- VirqState *state = vgicGetVirqState(currentCoreCtx->coreId, id);
- if (priority == state->priority) {
- // Nothing to do...
- return;
- }
-
- state->priority = priority;
- u32 targets = state->targetList;
- if (targets != 0 && vgicIsVirqPending(state)) {
- vgicNotifyOtherCoreList(targets);
- }
-}
-
-static inline u8 vgicGetInterruptPriorityByte(u16 id)
-{
- return irqIsGuest(id) ? vgicGetVirqState(currentCoreCtx->coreId, id)->priority << 3 : 0;
-}
-
-static void vgicSetInterruptTargets(u16 id, u8 coreList)
-{
- // Ignored for SGIs and PPIs, and non-guest interrupts
- if (id < 32 || !irqIsGuest(id)) {
- return;
- }
-
- // Interrupt not pending (inactive or active-only): nothing much to do (see reference manual)
- // Otherwise, we may need to migrate the interrupt.
- // In our model, while a physical interrupt can be pending on multiple cores, we decide that a pending SPI
- // can only be handled on a single core (either it's in a LR, or in the global list), therefore we need to
- // send a signal to (oldList XOR newList) to either put the interrupt back in the global list or potentially handle it
-
- // Note that we take into account that the interrupt may be disabled.
- VirqState *state = vgicGetVirqState(currentCoreCtx->coreId, id);
- if (vgicIsVirqPending(state)) {
- u8 oldList = state->targetList;
- u8 diffList = (oldList ^ coreList) & getActiveCoreMask();
- if (diffList != 0) {
- vgicNotifyOtherCoreList(diffList);
- }
- }
-
- state->targetList = coreList;
- gicd->itargetsr[id] = state->targetList;
-}
-
-static inline u8 vgicGetInterruptTargets(u16 id)
-{
- // For SGIs & PPIs, itargetsr is banked and contains the CPU ID
- return (id < 32 || irqIsGuest(id)) ? vgicGetVirqState(currentCoreCtx->coreId, id)->targetList : 0;
-}
-
-static inline void vgicSetInterruptConfigBits(u16 id, u32 config)
-{
- // Ignored for SGIs, implementation defined for PPIs
- if (id < 32 || !irqIsGuest(id)) {
- return;
- }
-
- VirqState *state = vgicGetVirqState(currentCoreCtx->coreId, id);
-
- // Expose bit(2n) as nonprogrammable to the guest no matter what the physical distributor actually behaves
- bool newLvl = ((config & 2) << IRQ_CFGR_SHIFT(id)) == 0;
-
- if (state->levelSensitive != newLvl) {
- u32 cfg = gicd->icfgr[id / 16];
- cfg &= ~(3 << IRQ_CFGR_SHIFT(id));
- cfg |= (!newLvl ? 3 : 1) << IRQ_CFGR_SHIFT(id);
- gicd->icfgr[id / 16] = cfg;
-
- state->levelSensitive = newLvl;
- }
-}
-
-static inline u32 vgicGetInterruptConfigBits(u16 id)
-{
- u32 oneNModel = id < 32 || !irqIsGuest(id) ? 0 : 1;
- return (irqIsGuest(id) && !vgicGetVirqState(currentCoreCtx->coreId, id)->levelSensitive) ? 2 | oneNModel : oneNModel;
-}
-
-static void vgicSetSgiPendingState(u16 id, u32 coreId, u32 srcCoreId)
-{
- //DEBUG("EL2 [core %u]: sending vSGI %hu to core %u\n", srcCoreId, id, coreId);
- VirqState *state = vgicGetVirqState(coreId, id);
- g_vgicIncomingSgiPendingSources[coreId][id] |= BIT(srcCoreId);
- if (!state->handled && !vgicIsStateQueued(state)) {
- // The SGI was inactive on the target core...
- state->pendingLatch = true;
- state->coreId = srcCoreId;
- g_vgicIncomingSgiPendingSources[coreId][id] &= ~BIT(srcCoreId);
- vgicEnqueueVirqState(&g_virqPendingQueue, state);
- vgicNotifyOtherCoreList(BIT(coreId));
- }
-}
-
-static void vgicSendSgi(u16 id, u32 filter, u32 coreList)
-{
- switch (filter) {
- case 0:
- // Forward to coreList
- break;
- case 1:
- // Forward to all but current core
- coreList = getActiveCoreMask() & ~BIT(currentCoreCtx->coreId);
- break;
- case 2:
- // Forward to current core only
- coreList = BIT(currentCoreCtx->coreId);
- break;
- default:
- DEBUG("Emulated GCID_SGIR: invalid TargetListFilter value!\n");
- return;
- }
-
- coreList &= getActiveCoreMask();
-
- FOREACH_BIT(tmp, dstCore, coreList) {
- vgicSetSgiPendingState(id, dstCore, currentCoreCtx->coreId);
- }
-}
-
-static inline u32 vgicGetPeripheralId2Register(void)
-{
- // 2 for Gicv2
- return 2 << 4;
-}
-
-bool vgicValidateGicdRegisterAccess(size_t offset, size_t sz)
-{
- // ipriorityr, itargetsr, *pendsgir are byte-accessible
- if (
- !(offset >= GICDOFF(ipriorityr) && offset < GICDOFF(ipriorityr) + GIC_IRQID_MAX) &&
- !(offset >= GICDOFF(itargetsr) && offset < GICDOFF(itargetsr) + GIC_IRQID_MAX) &&
- !(offset >= GICDOFF(cpendsgir) && offset < GICDOFF(cpendsgir) + 16) &&
- !(offset >= GICDOFF(spendsgir) && offset < GICDOFF(spendsgir) + 16)
- ) {
- return (offset & 3) == 0 && sz == 4;
- } else {
- return sz == 1 || (sz == 4 && ((offset & 3) != 0));
- }
-}
-
-void vgicWriteGicdRegister(u32 val, size_t offset, size_t sz)
-{
- //DEBUG("gicd write off 0x%03llx sz %lx val %x w%d\n", offset, sz, val, (int)dabtIss.srt);
-
- switch (offset) {
- case GICDOFF(typer):
- case GICDOFF(iidr):
- case GICDOFF(icpidr2):
- case GICDOFF(itargetsr) ... GICDOFF(itargetsr) + 31:
- // Write ignored (read-only registers)
- break;
- case GICDOFF(icfgr) ... GICDOFF(icfgr) + 31/4:
- // Write ignored because of an implementation-defined choice
- break;
- case GICDOFF(igroupr) ... GICDOFF(igroupr) + GIC_IRQID_MAX/8:
- // Write ignored because we don't implement Group 1 here
- break;
- case GICDOFF(ispendr) ... GICDOFF(ispendr) + GIC_IRQID_MAX/8:
- case GICDOFF(icpendr) ... GICDOFF(icpendr) + GIC_IRQID_MAX/8:
- case GICDOFF(isactiver) ... GICDOFF(isactiver) + GIC_IRQID_MAX/8:
- case GICDOFF(icactiver) ... GICDOFF(icactiver) + GIC_IRQID_MAX/8:
- case GICDOFF(cpendsgir) ... GICDOFF(cpendsgir) + 15:
- case GICDOFF(spendsgir) ... GICDOFF(spendsgir) + 15:
- // Write ignored, not implemented (at least not yet, TODO)
- break;
-
- case GICDOFF(ctlr):
- vgicSetDistributorControlRegister(val);
- break;
-
- case GICDOFF(isenabler) ... GICDOFF(isenabler) + GIC_IRQID_MAX/8: {
- u32 base = 8 * (offset - GICDOFF(isenabler));
- FOREACH_BIT(tmp, pos, val) {
- vgicSetInterruptEnabledState((u16)(base + pos));
- }
- break;
- }
- case GICDOFF(icenabler) ... GICDOFF(icenabler) + GIC_IRQID_MAX/8: {
- u32 base = 8 * (offset - GICDOFF(icenabler));
- FOREACH_BIT(tmp, pos, val) {
- vgicClearInterruptEnabledState((u16)(base + pos));
- }
- break;
- }
-
- case GICDOFF(ipriorityr) ... GICDOFF(ipriorityr) + GIC_IRQID_MAX: {
- u16 base = (u16)(offset - GICDOFF(ipriorityr));
- for (u16 i = 0; i < sz; i++) {
- vgicSetInterruptPriorityByte(base + i, (u8)val);
- val >>= 8;
- }
- break;
- }
-
- case GICDOFF(itargetsr) + 32 ... GICDOFF(itargetsr) + GIC_IRQID_MAX: {
- u16 base = (u16)(offset - GICDOFF(itargetsr));
- for (u16 i = 0; i < sz; i++) {
- vgicSetInterruptTargets(base + i, (u8)val);
- val >>= 8;
- }
- break;
- }
-
- case GICDOFF(icfgr) + 32/4 ... GICDOFF(icfgr) + GIC_IRQID_MAX/4: {
- u16 base = (u16)(4 * (offset & 0xFF));
- for (u16 i = 0; i < 16; i++) {
- vgicSetInterruptConfigBits(base + i, val & 3);
- val >>= 2;
- }
- break;
- }
-
- case GICDOFF(sgir):
- vgicSendSgi((u16)(val & 0xF), (val >> 24) & 3, (val >> 16) & 0xFF);
- break;
-
- default:
- DEBUG("Write to GICD reserved/implementation-defined register offset=0x%03lx value=0x%08lx", offset, val);
- break;
- }
-}
-
-u32 vgicReadGicdRegister(size_t offset, size_t sz)
-{
- //DEBUG("gicd read off 0x%03llx sz %lx\n", offset, sz);
- u32 val = 0;
-
- switch (offset) {
- case GICDOFF(icfgr) ... GICDOFF(icfgr) + 31/4:
- // RAZ because of an implementation-defined choice
- break;
- case GICDOFF(igroupr) ... GICDOFF(igroupr) + GIC_IRQID_MAX/8:
- // RAZ because we don't implement Group 1 here
- break;
- case GICDOFF(ispendr) ... GICDOFF(ispendr) + GIC_IRQID_MAX/8:
- case GICDOFF(icpendr) ... GICDOFF(icpendr) + GIC_IRQID_MAX/8:
- case GICDOFF(isactiver) ... GICDOFF(isactiver) + GIC_IRQID_MAX/8:
- case GICDOFF(icactiver) ... GICDOFF(icactiver) + GIC_IRQID_MAX/8:
- case GICDOFF(cpendsgir) ... GICDOFF(cpendsgir) + 15:
- case GICDOFF(spendsgir) ... GICDOFF(spendsgir) + 15:
- // RAZ, not implemented (at least not yet, TODO)
- break;
-
- case GICDOFF(ctlr):
- val = vgicGetDistributorControlRegister();
- break;
- case GICDOFF(typer):
- val = vgicGetDistributorTypeRegister();
- break;
- case GICDOFF(iidr):
- val = vgicGetDistributorImplementerIdentificationRegister();
- break;
-
- case GICDOFF(isenabler) ... GICDOFF(isenabler) + GIC_IRQID_MAX/8:
- case GICDOFF(icenabler) ... GICDOFF(icenabler) + GIC_IRQID_MAX/8: {
- u16 base = (u16)(8 * (offset & 0x7F));
- for (u16 i = 0; i < 32; i++) {
- val |= vgicGetInterruptEnabledState(base + i) ? BIT(i) : 0;
- }
- break;
- }
-
- case GICDOFF(ipriorityr) ... GICDOFF(ipriorityr) + GIC_IRQID_MAX: {
- u16 base = (u16)(offset - GICDOFF(ipriorityr));
- for (u16 i = 0; i < sz; i++) {
- val |= vgicGetInterruptPriorityByte(base + i) << (8 * i);
- }
- break;
- }
-
- case GICDOFF(itargetsr) ... GICDOFF(itargetsr) + GIC_IRQID_MAX: {
- u16 base = (u16)(offset - GICDOFF(itargetsr));
- for (u16 i = 0; i < sz; i++) {
- val |= (u32)vgicGetInterruptTargets(base + i) << (8 * i);
- }
- break;
- }
-
- case GICDOFF(icfgr) + 32/4 ... GICDOFF(icfgr) + GIC_IRQID_MAX/4: {
- u16 base = (u16)(4 * (offset & 0xFF));
- for (u16 i = 0; i < 16; i++) {
- val |= vgicGetInterruptConfigBits(base + i) << (2 * i);
- }
- break;
- }
-
- case GICDOFF(sgir):
- // Write-only register
- DEBUG("Read from write-only register GCID_SGIR\n");
- break;
-
- case GICDOFF(icpidr2):
- val = vgicGetPeripheralId2Register();
- break;
-
- default:
- DEBUG("Read from GICD reserved/implementation-defined register offset=0x%03lx\n", offset);
- break;
- }
-
- return val;
-}
-
-static void handleVgicMmioWrite(ExceptionStackFrame *frame, DataAbortIss dabtIss, size_t offset)
-{
- size_t sz = BITL(dabtIss.sas);
- u32 val = (u32)(readFrameRegisterZ(frame, dabtIss.srt) & MASKL(8 * sz));
- vgicWriteGicdRegister(val, offset, sz);
-}
-
-static void handleVgicMmioRead(ExceptionStackFrame *frame, DataAbortIss dabtIss, size_t offset)
-{
- size_t sz = BITL(dabtIss.sas);
- u32 val = vgicReadGicdRegister(offset, sz);
- writeFrameRegisterZ(frame, dabtIss.srt, val);
-}
-
-static void vgicCleanupPendingList(void)
-{
- VirqState *node, *next;
- u16 id;
- bool pending;
- u32 coreId;
-
- for (node = g_virqPendingQueue.first; node != vgicGetQueueEnd(); node = next) {
- next = vgicGetNextQueuedVirqState(node);
-
- // For SGIs, check the pending bits
- id = vgicGetVirqStateInterruptId(node);
- coreId = vgicGetVirqStateCoreId(node);
- if (id < 16) {
- pending = true;
- } else if (node->levelSensitive) {
- // For hardware interrupts, we have kept the interrupt active on the physical GICD
- // For level-sensitive interrupts, we need to check if they're also still physically pending (resampling).
- // If not, there's nothing to service anymore, and therefore we have to deactivate them, so that
- // we're notified when they become pending again.
-
- // Note: we can't touch PPIs for other cores... but each core will call this function anyway.
- if (id >= 32 || coreId == currentCoreCtx->coreId) {
- u32 mask = gicd->ispendr[id / 32] & BIT(id % 32);
- if (mask == 0) {
- gicd->icactiver[id / 32] = BIT(id % 32);
- pending = false;
- } else {
- pending = true;
- }
- } else {
- // Oops, can't handle PPIs of other cores
- // Assume interrupt is still pending and call it a day
- pending = true;
- }
- } else {
- pending = node->pendingLatch;
- }
-
- if (!pending) {
- vgicSetVirqPendingState(node, false);
- vgicDequeueVirqState(&g_virqPendingQueue, node);
- }
- }
-}
-
-static bool vgicTestInterruptEligibility(VirqState *state)
-{
- u16 id = vgicGetVirqStateInterruptId(state);
- u32 coreId = vgicGetVirqStateCoreId(state);
-
- // Precondition: state still in list
-
- if (id < 32 && coreId != currentCoreCtx->coreId) {
- // We can't handle SGIs/PPIs of other cores.
- return false;
- }
-
- return vgicGetInterruptEnabledState(id) && (id < 32 || (state->targetList & BIT(currentCoreCtx->coreId)) != 0);
-}
-
-static void vgicChoosePendingInterrupts(size_t *outNumChosen, VirqState *chosen[], size_t maxNum)
-{
- *outNumChosen = 0;
-
- for (VirqState *node = g_virqPendingQueue.first, *next; node != vgicGetQueueEnd() && *outNumChosen < maxNum; node = next) {
- next = vgicGetNextQueuedVirqState(node);
- if (vgicTestInterruptEligibility(node)) {
- node->handled = true;
- vgicDequeueVirqState(&g_virqPendingQueue, node);
- chosen[(*outNumChosen)++] = node;
- }
- }
-}
-
-static inline u64 vgicGetElrsrRegister(void)
-{
- return (u64)gich->elsr0 | (((u64)gich->elsr1) << 32);
-}
-
-static inline bool vgicIsListRegisterAvailable(u32 id)
-{
- return (id >= g_irqManager.numListRegisters) && (vgicGetElrsrRegister() & BITL(id));
-}
-
-static inline size_t vgicGetNumberOfFreeListRegisters(void)
-{
- return __builtin_popcountll(vgicGetElrsrRegister());
-}
-
-static inline volatile ArmGicV2ListRegister *vgicAllocateListRegister(void)
-{
- u32 ff = __builtin_ffsll(vgicGetElrsrRegister());
- if (ff == 0) {
- return NULL;
- } else {
- g_vgicUsedLrMap[currentCoreCtx->coreId] |= BITL(ff - 1);
- return &gich->lr[ff - 1];
- }
-}
-
-static void vgicPushListRegisters(VirqState *chosen[], size_t num)
-{
- for (size_t i = 0; i < num; i++) {
- VirqState *state = chosen[i];
- u16 irqId = vgicGetVirqStateInterruptId(state);
-
- ArmGicV2ListRegister lr = {0};
- lr.grp1 = false; // group0
- lr.priority = state->priority;
- lr.virtualId = irqId;
-
- // We only add new pending interrupts here...
- lr.pending = true;
- lr.active = false;
-
- // We don't support guests setting the pending latch, so the logic is probably simpler...
-
- if (irqId < 16) {
- // SGI
- u32 sourceCoreId = vgicGetSgiCurrentSourceCoreId(state);
- lr.physicalId = BIT(9) /* EOI notification bit */ | sourceCoreId;
- // ^ IDK how kvm gets away with not setting the EOI notif bits in some cases,
- // what they do seems to be prone to drop interrupts, etc.
-
- lr.hw = false; // software
- } else {
- // Actual physical interrupt
- lr.hw = true;
- lr.physicalId = irqId;
- }
-
- volatile ArmGicV2ListRegister *freeLr = vgicAllocateListRegister();
-
- if (freeLr == NULL) {
- DEBUG("EL2: vgicPushListRegisters: no free LR!\n");
- }
- *freeLr = lr;
- }
-}
-
-static bool vgicUpdateListRegister(volatile ArmGicV2ListRegister *lr)
-{
- ArmGicV2ListRegister lrCopy = *lr;
- ArmGicV2ListRegister zero = {0};
-
- u16 irqId = lrCopy.virtualId;
-
- // Note: this give priority to multi-SGIs than can be immediately handled
-
- // Update the state
- VirqState *state = vgicGetVirqState(currentCoreCtx->coreId, irqId);
- u32 srcCoreId = state->coreId;
- u32 coreId = currentCoreCtx->coreId;
-
- ENSURE(state->handled);
-
- state->active = lrCopy.active;
-
- if (lrCopy.active) {
- // We don't dequeue active interrupts
- if (irqId < 16) {
- // We can allow SGIs to be marked active-pending if it's been made pending from the same source again
- if (g_vgicIncomingSgiPendingSources[coreId][irqId] & BIT(srcCoreId)) {
- lrCopy.pending = true;
- g_vgicIncomingSgiPendingSources[coreId][irqId] &= ~BIT(srcCoreId);
- }
- }
-
- vgicSetVirqPendingState(state, lrCopy.pending);
- *lr = lrCopy;
- return true;
- } else if (lrCopy.pending) {
- // New interrupts might have come, pending status might have been changed, etc.
- // We need to put the interrupt back in the pending list (which we clean up afterwards)
- vgicEnqueueVirqState(&g_virqPendingQueue, state);
- state->handled = false;
- *lr = zero;
- return false;
- } else {
- if (irqId < 16) {
- // Special case for multi-SGIs if they can be immediately handled
- if (g_vgicIncomingSgiPendingSources[coreId][irqId] != 0) {
- srcCoreId = __builtin_ctz(g_vgicIncomingSgiPendingSources[coreId][irqId]);
- state->coreId = srcCoreId;
- g_vgicIncomingSgiPendingSources[coreId][irqId] &= ~BIT(srcCoreId);
- lrCopy.physicalId = BIT(9) /* EOI notification bit */ | srcCoreId;
-
- lrCopy.pending = true;
- *lr = lrCopy;
- }
- }
-
- if (!lrCopy.pending) {
- // Inactive interrupt, cleanup
- vgicSetVirqPendingState(state, false);
- state->handled = false;
- *lr = zero;
- return false;
- } else {
- return true;
- }
- }
-}
-
-void vgicUpdateState(void)
-{
- u32 coreId = currentCoreCtx->coreId;
-
- // First, put back inactive interrupts into the queue, handle some SGI stuff
- // Need to handle the LRs in reverse order to keep list stability
- u64 usedMap = __rbit64(g_vgicUsedLrMap[coreId]);
- FOREACH_BIT (tmp, pos, usedMap) {
- if (!vgicUpdateListRegister(&gich->lr[63 - pos])) {
- usedMap &= ~BITL(pos);
- }
- }
- g_vgicUsedLrMap[coreId] = __rbit64(usedMap);
-
- // Then, clean the list up
- vgicCleanupPendingList();
-
- size_t numChosen;
- size_t numFreeLr = vgicGetNumberOfFreeListRegisters();
- VirqState *chosen[numFreeLr]; // yes this is a VLA, potentially dangerous. Usually max 4 (64 at most)
-
- // Choose interrupts...
- vgicChoosePendingInterrupts(&numChosen, chosen, numFreeLr);
-
- // ...and push them
- vgicPushListRegisters(chosen, numChosen);
-
- // Apparently, the following is not needed because the GIC generates it for us
-
- // Enable underflow interrupt when appropriate to do so
- if (g_irqManager.numListRegisters - vgicGetNumberOfFreeListRegisters() > 1) {
- gich->hcr.uie = true;
- } else {
- gich->hcr.uie = false;
- }
-}
-
-void vgicMaintenanceInterruptHandler(void)
-{
- ArmGicV2MaintenanceIntStatRegister misr = gich->misr;
-
- // Force GICV_CTRL to behave like ns-GICC_CTLR, with group 1 being replaced by group 0
- // Ensure we aren't spammed by maintenance interrupts, either.
- if (misr.vgrp0e || misr.vgrp0d || misr.vgrp1e || misr.vgrp1d) {
- ArmGicV2VmControlRegister vmcr = gich->vmcr;
- vmcr.cbpr = 0;
- vmcr.fiqEn = 0;
- vmcr.ackCtl = 0;
- vmcr.enableGrp1 = 0;
- gich->vmcr = vmcr;
- }
-
- if (misr.vgrp0e) {
- DEBUG("EL2 [core %d]: Group 0 enabled maintenance interrupt\n", (int)currentCoreCtx->coreId);
- gich->hcr.vgrp0eie = false;
- gich->hcr.vgrp0die = true;
- } else if (misr.vgrp0d) {
- DEBUG("EL2 [core %d]: Group 0 disabled maintenance interrupt\n", (int)currentCoreCtx->coreId);
- gich->hcr.vgrp0eie = true;
- gich->hcr.vgrp0die = false;
- }
-
- // Already handled the following 2 above:
- if (misr.vgrp1e) {
- DEBUG("EL2 [core %d]: Group 1 enabled maintenance interrupt\n", (int)currentCoreCtx->coreId);
- }
- if (misr.vgrp1d) {
- DEBUG("EL2 [core %d]: Group 1 disabled maintenance interrupt\n", (int)currentCoreCtx->coreId);
- }
-
- if (misr.eoi) {
- //DEBUG("EL2 [core %d]: SGI EOI maintenance interrupt\n", currentCoreCtx->coreId);
- }
-
- if (misr.u) {
- //DEBUG("EL2 [core %d]: Underflow maintenance interrupt\n", currentCoreCtx->coreId);
- }
-
- ENSURE2(!misr.lrenp, "List Register Entry Not Present maintenance interrupt!\n");
-
- // The rest should be handled by the main loop...
-}
-
-void handleVgicdMmio(ExceptionStackFrame *frame, DataAbortIss dabtIss, size_t offset)
-{
- size_t sz = BITL(dabtIss.sas);
- uintptr_t addr = (uintptr_t)gicd + offset;
- bool oops = !vgicValidateGicdRegisterAccess(offset, sz);
-
- if (oops) {
- dumpUnhandledDataAbort(dabtIss, addr, "invalid GICD register access");
- }
-
- recursiveSpinlockLock(&g_irqManager.lock);
-
- if (dabtIss.wnr && !oops) {
- handleVgicMmioWrite(frame, dabtIss, offset);
- } else if (!oops) {
- handleVgicMmioRead(frame, dabtIss, offset);
- }
-
- vgicUpdateState();
- recursiveSpinlockUnlock(&g_irqManager.lock);
-}
-
-// lock needs to be held by caller
-// note, irqId >= 16
-void vgicEnqueuePhysicalIrq(u16 irqId)
-{
- VirqState *state = vgicGetVirqState(currentCoreCtx->coreId, irqId);
- vgicSetVirqPendingState(state, true);
- vgicEnqueueVirqState(&g_virqPendingQueue, state);
-}
-
-void vgicInit(void)
-{
- if (currentCoreCtx->isBootCore) {
- // All fields are reset to 0 on reset and deep sleep exit
-
- g_virqPendingQueue.first = g_virqPendingQueue.last = vgicGetQueueEnd();
-
- for (u32 i = 0; i < MAX_NUM_INTERRUPTS; i++) {
- g_virqStates[i].listNext = g_virqStates[i].listPrev = VIRQLIST_INVALID_ID;
- g_virqStates[i].priority = 0x1F;
- }
-
- for (u32 i = 0; i < 4; i++) {
- // SGIs, PPIs
- for (u16 j = 0; j < 32; j++) {
- VirqState *state = vgicGetVirqState(i, j);
- state->targetList = BIT(i);
- if (j < 16) {
- state->enabled = true;
- } else {
- state->levelSensitive = (gicd->icfgr[j / 16] & (2 << IRQ_CFGR_SHIFT(j % 16))) == 0;
- }
- }
- }
-
- // All guest interrupts are initially configured as disabled
- // All guest SPIs are initially configured as edge-triggered with no targets
- }
-
- // Deassert vIRQ line, just in case
- // Enable a few maintenance interrupts
- ArmGicV2HypervisorControlRegister hcr = {
- .vgrp1eie = true,
- .vgrp0eie = true,
- .lrenpie = true,
- .en = true,
- };
- gich->hcr = hcr;
-}
diff --git a/thermosphere/src/watchpoints.c b/thermosphere/src/watchpoints.c
deleted file mode 100644
index 6030f244a..000000000
--- a/thermosphere/src/watchpoints.c
+++ /dev/null
@@ -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 .
- */
-
-#include
-#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;
-}
diff --git a/thermosphere/src/watchpoints.h b/thermosphere/src/watchpoints.h
deleted file mode 100644
index 2d29cd3ae..000000000
--- a/thermosphere/src/watchpoints.h
+++ /dev/null
@@ -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 .
- */
-
-#pragma once
-
-#define _REENT_ONLY
-#include
-
-#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);