From 613402121aa1fcd21ea0158b70e07dd3d315c735 Mon Sep 17 00:00:00 2001 From: TuxSH <1922548+TuxSH@users.noreply.github.com> Date: Sun, 16 Feb 2020 17:40:18 +0000 Subject: [PATCH] thermosphere: file cleanup --- thermosphere/src/barrier.c | 44 - thermosphere/src/barrier.h | 30 - thermosphere/src/breakpoints.c | 162 --- thermosphere/src/breakpoints.h | 39 - .../src/breakpoints_watchpoints_common.h | 89 -- .../src/breakpoints_watchpoints_load.h | 21 - .../src/breakpoints_watchpoints_load.s | 70 -- thermosphere/src/execute_function.c | 50 - thermosphere/src/execute_function.h | 28 - thermosphere/src/irq.c | 291 ----- thermosphere/src/pattern_utils.c | 352 ------ thermosphere/src/pattern_utils.h | 28 - thermosphere/src/software_breakpoints.c | 243 ---- thermosphere/src/software_breakpoints.h | 50 - thermosphere/src/spinlock.c | 53 - thermosphere/src/spinlock.h | 84 -- thermosphere/src/spinlock_impl.s | 59 - thermosphere/src/vgic.c | 1074 ----------------- thermosphere/src/watchpoints.c | 225 ---- thermosphere/src/watchpoints.h | 38 - 20 files changed, 3030 deletions(-) delete mode 100644 thermosphere/src/barrier.c delete mode 100644 thermosphere/src/barrier.h delete mode 100644 thermosphere/src/breakpoints.c delete mode 100644 thermosphere/src/breakpoints.h delete mode 100644 thermosphere/src/breakpoints_watchpoints_common.h delete mode 100644 thermosphere/src/breakpoints_watchpoints_load.h delete mode 100644 thermosphere/src/breakpoints_watchpoints_load.s delete mode 100644 thermosphere/src/execute_function.c delete mode 100644 thermosphere/src/execute_function.h delete mode 100644 thermosphere/src/irq.c delete mode 100644 thermosphere/src/pattern_utils.c delete mode 100644 thermosphere/src/pattern_utils.h delete mode 100644 thermosphere/src/software_breakpoints.c delete mode 100644 thermosphere/src/software_breakpoints.h delete mode 100644 thermosphere/src/spinlock.c delete mode 100644 thermosphere/src/spinlock.h delete mode 100644 thermosphere/src/spinlock_impl.s delete mode 100644 thermosphere/src/vgic.c delete mode 100644 thermosphere/src/watchpoints.c delete mode 100644 thermosphere/src/watchpoints.h 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);