thermosphere: refactor exception handlers & add stolen time/emulated ptimer logic

This commit is contained in:
TuxSH 2020-01-08 22:18:56 +00:00
parent b9d07fccd6
commit 501472324f
9 changed files with 151 additions and 118 deletions

View file

@ -15,6 +15,7 @@
*/ */
#pragma once #pragma once
#include <assert.h>
#include "utils.h" #include "utils.h"
#include "barrier.h" #include "barrier.h"
#include "execute_function.h" #include "execute_function.h"
@ -34,8 +35,14 @@ typedef struct CoreCtx {
void *executedFunctionArgs; // @0x30 void *executedFunctionArgs; // @0x30
Barrier executedFunctionBarrier; // @0x38 Barrier executedFunctionBarrier; // @0x38
bool executedFunctionSync; // @0x3C bool executedFunctionSync; // @0x3C
// Timer stuff
u64 emulPtimerOffsetThen; // @0x40. When setting cntp_cval_el0 and on interrupt
} CoreCtx; } CoreCtx;
static_assert(offsetof(CoreCtx, executedFunctionSync) == 0x3C, "Wrong definition for CoreCtx");
static_assert(offsetof(CoreCtx, emulPtimerOffsetThen) == 0x40, "Wrong definition for CoreCtx");
extern CoreCtx g_coreCtxs[4]; extern CoreCtx g_coreCtxs[4];
register CoreCtx *currentCoreCtx asm("x18"); register CoreCtx *currentCoreCtx asm("x18");

View file

@ -50,19 +50,12 @@
.endif .endif
.endm .endm
.macro save_all_regs .macro SAVE_ALL_REGISTERS
stp x30, xzr, [sp, #-0x130] stp x30, xzr, [sp, #-0x130]
bl _save_all_regs bl _saveAllRegisters
.endm .endm
.macro save_all_regs_reload_x18 .macro PIVOT_STACK_FOR_CRASH
save_all_regs
// Reload our x18 value (currentCoreCtx)
ldp x18, xzr, [sp, #0x120]
.endm
.macro pivot_stack_for_crash
// Note: x18 assumed uncorrupted // Note: x18 assumed uncorrupted
// Note: replace sp_el0 with crashing sp // Note: replace sp_el0 with crashing sp
str x16, [x18, #0x18] // currentCoreCtx->scratch = x16 str x16, [x18, #0x18] // currentCoreCtx->scratch = x16
@ -73,30 +66,58 @@
ldr x16, [x18, #0x18] ldr x16, [x18, #0x18]
.endm .endm
.equ EXCEPTION_TYPE_HOST, 0
.equ EXCEPTION_TYPE_GUEST, 1
.equ EXCEPTION_TYPE_HOST_CRASH, 2
.macro EXCEPTION_HANDLER_START name, type
vector_entry \name
.if \type == EXCEPTION_TYPE_HOST_CRASH
PIVOT_STACK_FOR_CRASH
.endif
SAVE_ALL_REGISTERS
.if \type == EXCEPTION_TYPE_GUEST
ldp x18, xzr, [sp, #0x120]
.endif
.endm
.macro EXCEPTION_HANDLER_END name, type
.if \type != EXCEPTION_TYPE_HOST_CRASH
b _restoreAllRegisters
.else
b .
.endif
check_vector_size \name
.endm
.macro UNKNOWN_EXCEPTION name
vector_entry \name
bl _unknownException
check_vector_size \name
.endm
/* Actual Vectors for Thermosphere. */ /* Actual Vectors for Thermosphere. */
.global thermosphere_vectors .global g_thermosphereVectors
vector_base thermosphere_vectors vector_base g_thermosphereVectors
/* Current EL, SP0 */ /* Current EL, SP0 */
/* Those are unused by us, except on same-EL double-faults. */ /* Those are unused by us, except on same-EL double-faults. */
.global unknown_exception UNKNOWN_EXCEPTION _synchSp0
unknown_exception:
vector_entry synch_sp0 _unknownException:
pivot_stack_for_crash pivot_stack_for_crash
mov x0, x30 mov x0, x30
adr x1, thermosphere_vectors + 4 adr x1, g_thermosphereVectors + 4
sub x0, x0, x1 sub x0, x0, x1
bl handleUnknownException bl handleUnknownException
b . b .
check_vector_size synch_sp0
vector_entry irq_sp0 UNKNOWN_EXCEPTION _irqSp0
bl unknown_exception
.endfunc
.cfi_endproc
/* To save space, insert in an unused vector segment. */
_save_all_regs:
/* To save space, insert in an unused vector segment. */
_saveAllRegisters:
sub sp, sp, #0x120 sub sp, sp, #0x120
stp x0, x1, [sp, #0x00] stp x0, x1, [sp, #0x00]
stp x2, x3, [sp, #0x10] stp x2, x3, [sp, #0x10]
@ -115,37 +136,44 @@ vector_entry irq_sp0
stp x28, x29, [sp, #0xE0] stp x28, x29, [sp, #0xE0]
mov x29, x30 mov x29, x30
ldp x30, xzr, [sp, #-0x10] // See save_all_regs macro ldp x30, xzr, [sp, #-0x10] // See SAVE_ALL_REGISTERS macro
mrs x20, sp_el1 mrs x20, sp_el1
mrs x21, sp_el0 mrs x21, sp_el0
mrs x22, elr_el2 mrs x22, elr_el2
mrs x23, spsr_el2 mrs x23, spsr_el2
mrs x24, cntvct_el0
stp x30, x20, [sp, #0xF0] stp x30, x20, [sp, #0xF0]
stp x21, x22, [sp, #0x100] stp x21, x22, [sp, #0x100]
stp x23, xzr, [sp, #0x110] stp x23, x24, [sp, #0x110]
mov x30, x29 mov x30, x29
ret ret
vector_entry fiq_sp0 UNKNOWN_EXCEPTION _fiqSp0
bl unknown_exception
.endfunc /* To save space, insert in an unused vector segment. */
.cfi_endproc
/* To save space, insert in an unused vector segment. */ // Accessed by start.s
.global _restore_all_regs .global _restoreAllRegisters
_restore_all_regs: .type _restoreAllRegisters, %function
_restoreAllRegisters:
ldp x30, x20, [sp, #0xF0] ldp x30, x20, [sp, #0xF0]
ldp x21, x22, [sp, #0x100] ldp x21, x22, [sp, #0x100]
ldp x23, xzr, [sp, #0x110] ldp x23, x24, [sp, #0x110]
msr sp_el1, x20 msr sp_el1, x20
msr sp_el0, x21 msr sp_el0, x21
msr elr_el2, x22 msr elr_el2, x22
msr spsr_el2, x23 msr spsr_el2, x23
// Update timer offset: offset = ptimer - vtimer; here the time elapsed is vct(now) - vct(before)
mrs x23, cntvct_el0
sub x24, x23, x24
msr cntvoff_el2, x24
ldp x0, x1, [sp, #0x00] ldp x0, x1, [sp, #0x00]
ldp x2, x3, [sp, #0x10] ldp x2, x3, [sp, #0x10]
ldp x4, x5, [sp, #0x20] ldp x4, x5, [sp, #0x20]
@ -165,20 +193,20 @@ vector_entry fiq_sp0
add sp, sp, #0x120 add sp, sp, #0x120
eret eret
vector_entry serror_sp0 UNKNOWN_EXCEPTION _serrorSp0
bl unknown_exception
.endfunc
.cfi_endproc
/* To save space, insert in an unused vector segment. */
/* To save space, insert in an unused vector segment. */
.global semihosting_call .global semihosting_call
.type semihosting_call, %function .type semihosting_call, %function
.func semihosting_call
.cfi_startproc
semihosting_call: semihosting_call:
hlt #0xF000 hlt #0xF000
ret ret
.cfi_endproc
.endfunc
.global doSmcIndirectCallImpl .global doSmcIndirectCallImpl
//.type doSmcIndirectCallImpl, %function
doSmcIndirectCallImpl: doSmcIndirectCallImpl:
stp x19, x20, [sp, #-0x10]! stp x19, x20, [sp, #-0x10]!
mov x19, x0 mov x19, x0
@ -208,89 +236,55 @@ doSmcIndirectCallImplSize:
.word _doSmcIndirectCallImplEnd - doSmcIndirectCallImpl .word _doSmcIndirectCallImplEnd - doSmcIndirectCallImpl
/* Current EL, SPx */ /* Current EL, SPx */
vector_entry synch_spx
/* Only crashes go through there! */
pivot_stack_for_crash
save_all_regs
EXCEPTION_HANDLER_START _synchSpx, EXCEPTION_TYPE_HOST_CRASH
mov x0, sp mov x0, sp
mrs x1, esr_el2 mrs x1, esr_el2
bl handleSameElSyncException bl handleSameElSyncException
b . EXCEPTION_HANDLER_END _synchSpx, EXCEPTION_TYPE_HOST_CRASH
check_vector_size synch_spx
vector_entry irq_spx
save_all_regs
EXCEPTION_HANDLER_START _irqSpx, EXCEPTION_TYPE_HOST
mov x0, sp mov x0, sp
mov w1, wzr mov w1, wzr
mov w2, wzr mov w2, wzr
bl handleIrqException bl handleIrqException
EXCEPTION_HANDLER_END _irqSpx, EXCEPTION_TYPE_HOST
b _restore_all_regs UNKNOWN_EXCEPTION _fiqSpx
UNKNOWN_EXCEPTION _serrorSpx
check_vector_size irq_spx
vector_entry fiq_spx
bl unknown_exception
check_vector_size fiq_spx
vector_entry serror_spx
bl unknown_exception
check_vector_size serror_spx
/* Lower EL, A64 */ /* Lower EL, A64 */
vector_entry synch_a64
save_all_regs_reload_x18
EXCEPTION_HANDLER_START _synchA64, EXCEPTION_TYPE_GUEST
mov x0, sp mov x0, sp
mrs x1, esr_el2 mrs x1, esr_el2
bl handleLowerElSyncException bl handleLowerElSyncException
EXCEPTION_HANDLER_END _synchA64, EXCEPTION_TYPE_GUEST
b _restore_all_regs EXCEPTION_HANDLER_START _irqA64, EXCEPTION_TYPE_GUEST
check_vector_size synch_a64
vector_entry irq_a64
save_all_regs_reload_x18
mov x0, sp mov x0, sp
mov w1, #1 mov w1, #1
mov w2, wzr mov w2, #0
bl handleIrqException bl handleIrqException
EXCEPTION_HANDLER_END _irqA64, EXCEPTION_TYPE_GUEST
b _restore_all_regs UNKNOWN_EXCEPTION _fiqA64
check_vector_size irq_a64 UNKNOWN_EXCEPTION _serrorA64
vector_entry fiq_a64
bl unknown_exception
check_vector_size fiq_a64
vector_entry serror_a64
bl unknown_exception
check_vector_size serror_a64
/* Lower EL, A32 */ /* Lower EL, A32 */
vector_entry synch_a32
bl unknown_exception
check_vector_size synch_a32
vector_entry irq_a32 EXCEPTION_HANDLER_START _synchA32, EXCEPTION_TYPE_GUEST
save_all_regs_reload_x18 mov x0, sp
mrs x1, esr_el2
bl handleLowerElSyncException
EXCEPTION_HANDLER_END _synchA32, EXCEPTION_TYPE_GUEST
EXCEPTION_HANDLER_START _irqA32, EXCEPTION_TYPE_GUEST
mov x0, sp mov x0, sp
mov w1, #1 mov w1, #1
mov w2, #1 mov w2, #1
bl handleIrqException bl handleIrqException
EXCEPTION_HANDLER_END _irqA32, EXCEPTION_TYPE_GUEST
b _restore_all_regs UNKNOWN_EXCEPTION _fiqA32
check_vector_size irq_a32 UNKNOWN_EXCEPTION _serrorA32
vector_entry fiq_a32
b fiq_a64
check_vector_size fiq_a32
vector_entry serror_a32
bl unknown_exception
check_vector_size serror_a32

View file

@ -66,6 +66,7 @@ void dumpStackFrame(const ExceptionStackFrame *frame, bool sameEl)
DEBUG("sp_el0\t\t%016llx\n", frame->sp_el0); DEBUG("sp_el0\t\t%016llx\n", frame->sp_el0);
} }
DEBUG("sp_el1\t\t%016llx\n", frame->sp_el1); DEBUG("sp_el1\t\t%016llx\n", frame->sp_el1);
DEBUG("cntvct_el0\t\t%016llx\n", frame->cntvct_el0);
#else #else
(void)frame; (void)frame;
(void)sameEl; (void)sameEl;
@ -93,7 +94,6 @@ void skipFaultingInstruction(ExceptionStackFrame *frame, u32 size)
void handleLowerElSyncException(ExceptionStackFrame *frame, ExceptionSyndromeRegister esr) void handleLowerElSyncException(ExceptionStackFrame *frame, ExceptionSyndromeRegister esr)
{ {
switch (esr.ec) { switch (esr.ec) {
case Exception_CP15RTTrap: case Exception_CP15RTTrap:
handleMcrMrcCP15Trap(frame, esr); handleMcrMrcCP15Trap(frame, esr);

View file

@ -26,6 +26,7 @@ typedef struct ExceptionStackFrame {
}; };
u64 elr_el2; u64 elr_el2;
u64 spsr_el2; u64 spsr_el2;
u64 cntvct_el0;
} ExceptionStackFrame; } ExceptionStackFrame;
// Adapted from https://developer.arm.com/docs/ddi0596/a/a64-shared-pseudocode-functions/shared-exceptions-pseudocode // Adapted from https://developer.arm.com/docs/ddi0596/a/a64-shared-pseudocode-functions/shared-exceptions-pseudocode

View file

@ -35,6 +35,8 @@ static void initSysregs(void)
SET_SYSREG(mdcr_el2, 0x00000000); SET_SYSREG(mdcr_el2, 0x00000000);
SET_SYSREG(mdscr_el1, 0x00000000); SET_SYSREG(mdscr_el1, 0x00000000);
SET_SYSREG(cntvoff_el2, 0x00000000);
} }
void initSystem(u32 coreId, bool isBootCore, u64 argument) void initSystem(u32 coreId, bool isBootCore, u64 argument)

View file

@ -131,6 +131,22 @@ void initIrq(void)
recursiveSpinlockUnlockRestoreIrq(&g_irqManager.lock, flags); recursiveSpinlockUnlockRestoreIrq(&g_irqManager.lock, flags);
} }
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);
if (cval > frame->cntvct_el0) {
// It has not: reschedule the timer
u64 offsetNow = GET_SYSREG(cntvoff_el2);
SET_SYSREG(cntp_cval_el0, cval + (offsetNow - currentCoreCtx->emulPtimerOffsetThen));
currentCoreCtx->emulPtimerOffsetThen = offsetNow;
return false;
}
return true;
}
void handleIrqException(ExceptionStackFrame *frame, bool isLowerEl, bool isA32) void handleIrqException(ExceptionStackFrame *frame, bool isLowerEl, bool isA32)
{ {
(void)isLowerEl; (void)isLowerEl;
@ -147,6 +163,11 @@ void handleIrqException(ExceptionStackFrame *frame, bool isLowerEl, bool isA32)
if (irqId == GIC_IRQID_SPURIOUS) { if (irqId == GIC_IRQID_SPURIOUS) {
// Spurious interrupt received // Spurious interrupt received
return; return;
} else if (irqId == GIC_IRQID_NS_PHYS_TIMER && !checkRescheduleEmulatedPtimer(frame)) {
// Deactivate the ptimer interrupt, return early
gicc->eoir = iar;
gicc->dir = iar;
return;
} }
bool isGuestInterrupt = false; bool isGuestInterrupt = false;

View file

@ -64,11 +64,12 @@ void thermosphereMain(ExceptionStackFrame *frame)
DEBUG("EL2: core %u reached main!\n", currentCoreCtx->coreId); DEBUG("EL2: core %u reached main!\n", currentCoreCtx->coreId);
} }
setCurrentCoreActive();
// Set up exception frame: init regs to 0, set spsr, elr, etc. // Set up exception frame: init regs to 0, set spsr, elr, etc.
memset(frame, 0, sizeof(ExceptionStackFrame)); memset(frame, 0, sizeof(ExceptionStackFrame));
frame->spsr_el2 = (0xF << 6) | (1 << 2) | 1; // EL1h+DAIF frame->spsr_el2 = (0xF << 6) | (1 << 2) | 1; // EL1h+DAIF
frame->elr_el2 = currentCoreCtx->kernelEntrypoint; frame->elr_el2 = currentCoreCtx->kernelEntrypoint;
frame->x[0] = currentCoreCtx->kernelArgument; frame->x[0] = currentCoreCtx->kernelArgument;
frame->cntvct_el0 = GET_SYSREG(cntvct_el0);
setCurrentCoreActive();
} }

View file

@ -81,6 +81,6 @@ _startCommon:
isb isb
// Jump to kernel // Jump to kernel
b _restore_all_regs b _restoreAllRegisters
.pool .pool

View file

@ -20,18 +20,18 @@
#include "debug_log.h" #include "debug_log.h"
#include "software_breakpoints.h" #include "software_breakpoints.h"
static inline u64 doSystemRegisterRead(u32 normalizedIss) static inline u64 doSystemRegisterRead(const ExceptionStackFrame *frame, u32 normalizedIss)
{ {
// See https://developer.arm.com/architectures/learn-the-architecture/generic-timer/single-page
u64 val; u64 val;
switch (normalizedIss) { switch (normalizedIss) {
case ENCODE_SYSREG_ISS(CNTPCT_EL0): { case ENCODE_SYSREG_ISS(CNTPCT_EL0): {
// FIXME val = frame->cntvct_el0;
val = GET_SYSREG(cntpct_el0);
break; break;
} }
case ENCODE_SYSREG_ISS(CNTP_TVAL_EL0): { case ENCODE_SYSREG_ISS(CNTP_TVAL_EL0): {
// FIXME too val = (GET_SYSREG(cntp_cval_el0) - frame->cntvct_el0) & 0xFFFFFFFF;
val = GET_SYSREG(cntp_tval_el0);
break; break;
} }
case ENCODE_SYSREG_ISS(CNTP_CTL_EL0): { case ENCODE_SYSREG_ISS(CNTP_CTL_EL0): {
@ -56,12 +56,20 @@ static inline u64 doSystemRegisterRead(u32 normalizedIss)
return val; return val;
} }
static inline void doSystemRegisterWrite(u32 normalizedIss, u64 val) static inline void writeEmulatedPhysicalCompareValue(u64 val)
{ {
currentCoreCtx->emulPtimerOffsetThen = GET_SYSREG(cntvoff_el2);
SET_SYSREG(cntp_cval_el0, val);
}
static inline void doSystemRegisterWrite(const ExceptionStackFrame *frame, u32 normalizedIss, u64 val)
{
// See https://developer.arm.com/architectures/learn-the-architecture/generic-timer/single-page
switch (normalizedIss) { switch (normalizedIss) {
case ENCODE_SYSREG_ISS(CNTP_TVAL_EL0): { case ENCODE_SYSREG_ISS(CNTP_TVAL_EL0): {
// FIXME // Sign-extend
SET_SYSREG(cntp_tval_el0, val); writeEmulatedPhysicalCompareValue(frame->cntvct_el0 + (u64)(s32)val);
break; break;
} }
case ENCODE_SYSREG_ISS(CNTP_CTL_EL0): { case ENCODE_SYSREG_ISS(CNTP_CTL_EL0): {
@ -70,8 +78,7 @@ static inline void doSystemRegisterWrite(u32 normalizedIss, u64 val)
break; break;
} }
case ENCODE_SYSREG_ISS(CNTP_CVAL_EL0): { case ENCODE_SYSREG_ISS(CNTP_CVAL_EL0): {
// Passthrough writeEmulatedPhysicalCompareValue(val);
SET_SYSREG(cntp_cval_el0, val);
break; break;
} }
@ -85,33 +92,33 @@ static inline void doSystemRegisterWrite(u32 normalizedIss, u64 val)
static inline void doMrs(ExceptionStackFrame *frame, u32 normalizedIss, u32 reg) static inline void doMrs(ExceptionStackFrame *frame, u32 normalizedIss, u32 reg)
{ {
writeFrameRegisterZ(frame, reg, doSystemRegisterRead(normalizedIss)); writeFrameRegisterZ(frame, reg, doSystemRegisterRead(frame, normalizedIss));
skipFaultingInstruction(frame, 4); skipFaultingInstruction(frame, 4);
} }
static inline void doMsr(ExceptionStackFrame *frame, u32 normalizedIss, u32 reg) static inline void doMsr(ExceptionStackFrame *frame, u32 normalizedIss, u32 reg)
{ {
u64 val = readFrameRegisterZ(frame, reg); u64 val = readFrameRegisterZ(frame, reg);
doSystemRegisterWrite(normalizedIss, val); doSystemRegisterWrite(frame, normalizedIss, val);
skipFaultingInstruction(frame, 4); skipFaultingInstruction(frame, 4);
} }
static inline void doMrc(ExceptionStackFrame *frame, u32 normalizedIss, u32 instructionLength, u32 reg) static inline void doMrc(ExceptionStackFrame *frame, u32 normalizedIss, u32 instructionLength, u32 reg)
{ {
writeFrameRegisterZ(frame, reg, doSystemRegisterRead(normalizedIss) & 0xFFFFFFFF); writeFrameRegisterZ(frame, reg, doSystemRegisterRead(frame, normalizedIss) & 0xFFFFFFFF);
skipFaultingInstruction(frame, instructionLength); skipFaultingInstruction(frame, instructionLength);
} }
static inline void doMcr(ExceptionStackFrame *frame, u32 normalizedIss, u32 instructionLength, u32 reg) static inline void doMcr(ExceptionStackFrame *frame, u32 normalizedIss, u32 instructionLength, u32 reg)
{ {
u64 val = readFrameRegisterZ(frame, reg) & 0xFFFFFFFF; u64 val = readFrameRegisterZ(frame, reg) & 0xFFFFFFFF;
doSystemRegisterWrite(normalizedIss, val); doSystemRegisterWrite(frame, normalizedIss, val);
skipFaultingInstruction(frame, instructionLength); skipFaultingInstruction(frame, instructionLength);
} }
static inline void doMrrc(ExceptionStackFrame *frame, u32 normalizedIss, u32 instructionLength, u32 reg, u32 reg2) static inline void doMrrc(ExceptionStackFrame *frame, u32 normalizedIss, u32 instructionLength, u32 reg, u32 reg2)
{ {
u64 val = doSystemRegisterRead(normalizedIss); u64 val = doSystemRegisterRead(frame, normalizedIss);
writeFrameRegister(frame, reg, val & 0xFFFFFFFF); writeFrameRegister(frame, reg, val & 0xFFFFFFFF);
writeFrameRegister(frame, reg2, val >> 32); writeFrameRegister(frame, reg2, val >> 32);
skipFaultingInstruction(frame, instructionLength); skipFaultingInstruction(frame, instructionLength);
@ -121,7 +128,7 @@ static inline void doMcrr(ExceptionStackFrame *frame, u32 normalizedIss, u32 ins
{ {
u64 valLo = readFrameRegister(frame, reg) & 0xFFFFFFFF; u64 valLo = readFrameRegister(frame, reg) & 0xFFFFFFFF;
u64 valHi = readFrameRegister(frame, reg2) << 32; u64 valHi = readFrameRegister(frame, reg2) << 32;
doSystemRegisterWrite(normalizedIss, valHi | valLo); doSystemRegisterWrite(frame, normalizedIss, valHi | valLo);
skipFaultingInstruction(frame, instructionLength); skipFaultingInstruction(frame, instructionLength);
} }