From dd96c8b32b177444c37e481065562fa7a04bf022 Mon Sep 17 00:00:00 2001 From: TuxSH <1922548+TuxSH@users.noreply.github.com> Date: Mon, 13 Jan 2020 19:23:53 +0000 Subject: [PATCH] thermosphere: fix ptimer time freezing (again) --- thermosphere/src/core_ctx.h | 4 ++-- thermosphere/src/exceptions.h | 1 + thermosphere/src/guest_timers.h | 34 ++++++++++++++++++++++++++++++++ thermosphere/src/irq.c | 35 +++++++++++++++------------------ thermosphere/src/sysreg_traps.c | 32 +++++++++++------------------- 5 files changed, 64 insertions(+), 42 deletions(-) create mode 100644 thermosphere/src/guest_timers.h diff --git a/thermosphere/src/core_ctx.h b/thermosphere/src/core_ctx.h index 3b29341b6..e4092a2e4 100644 --- a/thermosphere/src/core_ctx.h +++ b/thermosphere/src/core_ctx.h @@ -38,11 +38,11 @@ typedef struct CoreCtx { // Timer stuff u64 totalTimeInHypervisor; // @0x40. cntvoff_el2 is updated to that value. - u64 emulPtimerOffsetThen; // @0x48. When setting cntp_cval_el0 and on interrupt + u64 emulPtimerCval; // @0x48. When setting cntp_cval_el0 and on interrupt } CoreCtx; static_assert(offsetof(CoreCtx, executedFunctionSync) == 0x3C, "Wrong definition for CoreCtx"); -static_assert(offsetof(CoreCtx, emulPtimerOffsetThen) == 0x48, "Wrong definition for CoreCtx"); +static_assert(offsetof(CoreCtx, emulPtimerCval) == 0x48, "Wrong definition for CoreCtx"); extern CoreCtx g_coreCtxs[4]; register CoreCtx *currentCoreCtx asm("x18"); diff --git a/thermosphere/src/exceptions.h b/thermosphere/src/exceptions.h index d43eafd20..649e6040a 100644 --- a/thermosphere/src/exceptions.h +++ b/thermosphere/src/exceptions.h @@ -17,6 +17,7 @@ #pragma once #include #include "utils.h" +#include "core_ctx.h" typedef struct ExceptionStackFrame { u64 x[31]; // x0 .. x30 diff --git a/thermosphere/src/guest_timers.h b/thermosphere/src/guest_timers.h new file mode 100644 index 000000000..70a352065 --- /dev/null +++ b/thermosphere/src/guest_timers.h @@ -0,0 +1,34 @@ +/* + * 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 "exceptions.h" +#include "sysreg.h" + +static inline u64 computeCntvct(const ExceptionStackFrame *frame) +{ + return frame->cntpct_el0 - currentCoreCtx->totalTimeInHypervisor; +} + +static inline void writeEmulatedPhysicalCompareValue(ExceptionStackFrame *frame, u64 val) +{ + // We lied about the value of cntpct, so we need to compute the time delta + // the guest actually intended to use... + u64 vct = computeCntvct(frame); + currentCoreCtx->emulPtimerCval = val; + SET_SYSREG(cntp_cval_el0, frame->cntpct_el0 + (val - vct)); +} diff --git a/thermosphere/src/irq.c b/thermosphere/src/irq.c index 4ca9189df..eb5544b28 100644 --- a/thermosphere/src/irq.c +++ b/thermosphere/src/irq.c @@ -19,6 +19,7 @@ #include "debug_log.h" #include "vgic.h" #include "timer.h" +#include "guest_timers.h" #include "transport_interface.h" IrqManager g_irqManager = {0}; @@ -101,15 +102,15 @@ static void initGic(void) static inline bool checkRescheduleEmulatedPtimer(ExceptionStackFrame *frame) { - // Evaluate if the timer really has expired in the PoV of the guest kernel. If not, reschedule (add missed time delta) it & exit early - u64 cval = GET_SYSREG(cntp_cval_el0); - u64 vct = frame->cntpct_el0 - currentCoreCtx->totalTimeInHypervisor; + // 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 - u64 offsetNow = GET_SYSREG(cntvoff_el2); - SET_SYSREG(cntp_cval_el0, cval + (offsetNow - currentCoreCtx->emulPtimerOffsetThen)); - currentCoreCtx->emulPtimerOffsetThen = offsetNow; - + // Note: this isn't 100% precise esp. on QEMU so it may take a few tries... + writeEmulatedPhysicalCompareValue(frame, cval); return false; } @@ -117,22 +118,17 @@ static inline bool checkRescheduleEmulatedPtimer(ExceptionStackFrame *frame) } -static inline bool checkGuestTimerInterrupts(ExceptionStackFrame *frame, bool isLowerEl, u16 irqId) +static inline bool checkGuestTimerInterrupts(ExceptionStackFrame *frame, u16 irqId) { - if (irqId != TIMER_IRQID(NS_VIRT_TIMER) && irqId != TIMER_IRQID(NS_PHYS_TIMER)) { - return true; - } - // 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 (!isLowerEl) { - return false; - } else if (irqId == TIMER_IRQID(NS_VIRT_TIMER)) { + if (irqId == TIMER_IRQID(NS_VIRT_TIMER)) { u64 cval = GET_SYSREG(cntp_cval_el0); - u64 vct = frame->cntpct_el0 - currentCoreCtx->totalTimeInHypervisor; - return cval <= vct; - } else { + return cval <= computeCntvct(frame); + } else if (irqId == TIMER_IRQID(NS_PHYS_TIMER)) { return checkRescheduleEmulatedPtimer(frame); + } else { + return true; } } @@ -207,6 +203,7 @@ bool irqIsGuest(u16 id) void handleIrqException(ExceptionStackFrame *frame, bool isLowerEl, bool isA32) { + (void)isLowerEl; (void)isA32; volatile ArmGicV2Controller *gicc = g_irqManager.gic.gicc; @@ -220,7 +217,7 @@ void handleIrqException(ExceptionStackFrame *frame, bool isLowerEl, bool isA32) if (irqId == GIC_IRQID_SPURIOUS) { // Spurious interrupt received return; - } else if (!checkGuestTimerInterrupts(frame, isLowerEl, irqId)) { + } else if (!checkGuestTimerInterrupts(frame, irqId)) { // Deactivate the interrupt, return early gicc->eoir = iar; gicc->dir = iar; diff --git a/thermosphere/src/sysreg_traps.c b/thermosphere/src/sysreg_traps.c index 1f05866a8..890568592 100644 --- a/thermosphere/src/sysreg_traps.c +++ b/thermosphere/src/sysreg_traps.c @@ -15,9 +15,7 @@ */ #include "sysreg_traps.h" -#include "sysreg.h" -#include "arm.h" -#include "debug_log.h" +#include "guest_timers.h" #include "software_breakpoints.h" static inline u64 doSystemRegisterRead(const ExceptionStackFrame *frame, u32 normalizedIss) @@ -27,23 +25,22 @@ static inline u64 doSystemRegisterRead(const ExceptionStackFrame *frame, u32 nor u64 val; switch (normalizedIss) { case ENCODE_SYSREG_ISS(CNTPCT_EL0): { - u64 vct = frame->cntpct_el0 - currentCoreCtx->totalTimeInHypervisor; + u64 vct = computeCntvct(frame); val = vct; break; } case ENCODE_SYSREG_ISS(CNTP_TVAL_EL0): { u64 vct = frame->cntpct_el0 - currentCoreCtx->totalTimeInHypervisor; - val = (GET_SYSREG(cntp_cval_el0) - vct) & 0xFFFFFFFF; + u64 cval = currentCoreCtx->emulPtimerCval; + val = (cval - vct) & 0xFFFFFFFF; break; } case ENCODE_SYSREG_ISS(CNTP_CTL_EL0): { - // Passthrough - val = GET_SYSREG(cntp_ctl_el0); + val = frame->cntp_ctl_el0; break; } case ENCODE_SYSREG_ISS(CNTP_CVAL_EL0): { - // Passthrough - val = GET_SYSREG(cntp_cval_el0); + val = currentCoreCtx->emulPtimerCval; break; } @@ -58,30 +55,23 @@ static inline u64 doSystemRegisterRead(const ExceptionStackFrame *frame, u32 nor return val; } -static inline void writeEmulatedPhysicalCompareValue(u64 val) -{ - currentCoreCtx->emulPtimerOffsetThen = currentCoreCtx->totalTimeInHypervisor; - SET_SYSREG(cntp_cval_el0, val); -} - -static inline void doSystemRegisterWrite(const ExceptionStackFrame *frame, u32 normalizedIss, u64 val) +static inline void doSystemRegisterWrite(ExceptionStackFrame *frame, u32 normalizedIss, u64 val) { // See https://developer.arm.com/architectures/learn-the-architecture/generic-timer/single-page switch (normalizedIss) { case ENCODE_SYSREG_ISS(CNTP_TVAL_EL0): { // Sign-extend - u64 vct = frame->cntpct_el0 - currentCoreCtx->totalTimeInHypervisor; - writeEmulatedPhysicalCompareValue(vct + (u64)(s32)val); + u64 vct = computeCntvct(frame); + writeEmulatedPhysicalCompareValue(frame, vct + (u64)(s32)val); break; } case ENCODE_SYSREG_ISS(CNTP_CTL_EL0): { - // Passthrough - SET_SYSREG(cntp_ctl_el0, val); + frame->cntp_ctl_el0 = val; break; } case ENCODE_SYSREG_ISS(CNTP_CVAL_EL0): { - writeEmulatedPhysicalCompareValue(val); + writeEmulatedPhysicalCompareValue(frame, val); break; }