forked from Imagelibrary/seL4
arm_hyp: Save and restore vtimer state on switches
Added support for reading and writing additional virtual timer registers for vcpu hw read and write accesses. These include the compare value register (CNTV_CVAL) and offset register (CNTV_OFF), each represented as two 32 bit (high and low) registers on aarch32 and as single 64 bit registers on aarch64. Added support for explicitly saving and restoring the virtual timer registers when the vcpu is enabled and disabled. This ensures when the vcpu is switched in and out, the virtual timer registers are restored to a state that is consistent to when it was last run. By default the CNTVOFF register will be updated by the kernel to accumulate the time the VCPU is not running. From the guest this will result in the VCNT register not increasing when the VCPU is suspended. This behavior can be turned off by disabling the KernelArmVtimerUpdateVOffset config option.
This commit is contained in:
committed by
Kent Mcleod
parent
f795e7c0ec
commit
71d636f8b3
3
CHANGES
3
CHANGES
@@ -108,6 +108,9 @@ Upcoming release: BINARY COMPATIBLE
|
||||
VCPU faults for these interrupt numbers.
|
||||
- Additionally a new VCPU invocation is introduced: seL4_ARM_VCPU_AckVPPI.
|
||||
This is used to acknowledge a virtual PPI that was delivered as a fault.
|
||||
* Virtualise Arm VTimer and Vtimer interrupts to support sharing across VCPUs.
|
||||
- A VCPU will now save and restore VTimer registers for the generic timer and also deliver a VTimer IRQ via a
|
||||
seL4_Fault_VPPIEvent fault. This enables multiple VCPUs bound to the same physical core to share this device.
|
||||
|
||||
## Upgrade Notes
|
||||
|
||||
|
||||
@@ -61,7 +61,14 @@ struct gicVCpuIface {
|
||||
virq_t lr[GIC_VCPU_MAX_NUM_LR];
|
||||
};
|
||||
|
||||
#ifdef CONFIG_VTIMER_UPDATE_VOFFSET
|
||||
struct vTimer {
|
||||
uint64_t last_pcount;
|
||||
};
|
||||
#endif
|
||||
|
||||
enum VPPIEventIRQ {
|
||||
VPPIEventIRQ_VTimer,
|
||||
n_VPPIEventIRQ,
|
||||
VPPIEventIRQ_invalid = n_VPPIEventIRQ,
|
||||
};
|
||||
@@ -73,6 +80,9 @@ struct vcpu {
|
||||
struct gicVCpuIface vgic;
|
||||
word_t regs[seL4_VCPUReg_Num];
|
||||
bool_t vppi_masked[n_VPPIEventIRQ];
|
||||
#ifdef CONFIG_VTIMER_UPDATE_VOFFSET
|
||||
struct vTimer virtTimer;
|
||||
#endif
|
||||
};
|
||||
typedef struct vcpu vcpu_t;
|
||||
compile_assert(vcpu_size_correct, sizeof(struct vcpu) <= BIT(VCPU_SIZE_BITS))
|
||||
@@ -174,6 +184,9 @@ static inline void vcpu_write_reg(vcpu_t *vcpu, word_t reg, word_t value)
|
||||
static inline VPPIEventIRQ_t irqVPPIEventIndex(irq_t irq)
|
||||
{
|
||||
switch (IRQT_TO_IRQ(irq)) {
|
||||
case INTERRUPT_VTIMER_EVENT:
|
||||
return VPPIEventIRQ_VTimer;
|
||||
|
||||
default:
|
||||
return VPPIEventIRQ_invalid;
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
#ifdef CONFIG_ARM_HYPERVISOR_SUPPORT
|
||||
|
||||
#include <arch/object/vcpu.h>
|
||||
#include <drivers/timer/arm_generic.h>
|
||||
|
||||
/* Trap WFI/WFE/SMC and override CPSR.AIF */
|
||||
#define HCR_COMMON ( HCR_TSC | HCR_TWE | HCR_TWI | HCR_AMO | HCR_IMO \
|
||||
@@ -296,6 +297,85 @@ static inline void set_cntv_ctl(word_t val)
|
||||
MCR(CNTV_CTL, val);
|
||||
}
|
||||
|
||||
static inline void set_cntv_cval_64(uint64_t val)
|
||||
{
|
||||
MCRR(CNTV_CVAL, val);
|
||||
}
|
||||
|
||||
static inline uint64_t get_cntv_cval_64(void)
|
||||
{
|
||||
uint64_t ret = 0;
|
||||
MRRC(CNTV_CVAL, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline void set_cntv_cval_high(word_t val)
|
||||
{
|
||||
uint64_t ret = get_cntv_cval_64();
|
||||
uint64_t cval_high = (uint64_t) val << 32 ;
|
||||
uint64_t cval_low = (ret << 32) >> 32;
|
||||
set_cntv_cval_64(cval_high | cval_low);
|
||||
}
|
||||
|
||||
static inline word_t get_cntv_cval_high(void)
|
||||
{
|
||||
uint64_t ret = get_cntv_cval_64();
|
||||
return (word_t)(ret >> 32);
|
||||
}
|
||||
|
||||
static inline void set_cntv_cval_low(word_t val)
|
||||
{
|
||||
uint64_t ret = get_cntv_cval_64();
|
||||
uint64_t cval_high = (ret >> 32) << 32;
|
||||
uint64_t cval_low = (uint64_t) val;
|
||||
set_cntv_cval_64(cval_high | cval_low);
|
||||
}
|
||||
|
||||
static inline word_t get_cntv_cval_low(void)
|
||||
{
|
||||
uint64_t ret = get_cntv_cval_64();
|
||||
return (word_t) ret;
|
||||
}
|
||||
|
||||
static inline void set_cntv_off_64(uint64_t val)
|
||||
{
|
||||
MCRR(CNTVOFF, val);
|
||||
}
|
||||
|
||||
static inline uint64_t get_cntv_off_64(void)
|
||||
{
|
||||
uint64_t ret = 0;
|
||||
MRRC(CNTVOFF, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline void set_cntv_off_high(word_t val)
|
||||
{
|
||||
uint64_t ret = get_cntv_off_64();
|
||||
uint64_t cv_off_high = (uint64_t) val << 32 ;
|
||||
uint64_t cv_off_low = (ret << 32) >> 32;
|
||||
set_cntv_off_64(cv_off_high | cv_off_low);
|
||||
}
|
||||
|
||||
static inline word_t get_cntv_off_high(void)
|
||||
{
|
||||
uint64_t ret = get_cntv_off_64();
|
||||
return (word_t)(ret >> 32);
|
||||
}
|
||||
|
||||
static inline void set_cntv_off_low(word_t val)
|
||||
{
|
||||
uint64_t ret = get_cntv_off_64();
|
||||
uint64_t cv_off_high = (ret >> 32) << 32;
|
||||
uint64_t cv_off_low = (uint64_t) val;
|
||||
set_cntv_off_64(cv_off_high | cv_off_low);
|
||||
}
|
||||
|
||||
static inline word_t get_cntv_off_low(void)
|
||||
{
|
||||
uint64_t ret = get_cntv_off_64();
|
||||
return (word_t) ret;
|
||||
}
|
||||
|
||||
static word_t vcpu_hw_read_reg(word_t reg_index)
|
||||
{
|
||||
@@ -337,10 +417,6 @@ static word_t vcpu_hw_read_reg(word_t reg_index)
|
||||
return readTPIDRURO();
|
||||
case seL4_VCPUReg_FPEXC:
|
||||
return reg;
|
||||
case seL4_VCPUReg_CNTV_TVAL:
|
||||
return get_cntv_tval();
|
||||
case seL4_VCPUReg_CNTV_CTL:
|
||||
return get_cntv_ctl();
|
||||
case seL4_VCPUReg_LRsvc:
|
||||
return get_lr_svc();
|
||||
case seL4_VCPUReg_SPsvc:
|
||||
@@ -381,6 +457,16 @@ static word_t vcpu_hw_read_reg(word_t reg_index)
|
||||
return get_spsr_irq();
|
||||
case seL4_VCPUReg_SPSRfiq:
|
||||
return get_spsr_fiq();
|
||||
case seL4_VCPUReg_CNTV_CTL:
|
||||
return get_cntv_ctl();
|
||||
case seL4_VCPUReg_CNTV_CVALhigh:
|
||||
return get_cntv_cval_high();
|
||||
case seL4_VCPUReg_CNTV_CVALlow:
|
||||
return get_cntv_cval_low();
|
||||
case seL4_VCPUReg_CNTVOFFhigh:
|
||||
return get_cntv_off_high();
|
||||
case seL4_VCPUReg_CNTVOFFlow:
|
||||
return get_cntv_off_low();
|
||||
default:
|
||||
fail("ARM/HYP: Invalid register index");
|
||||
}
|
||||
@@ -442,12 +528,6 @@ static void vcpu_hw_write_reg(word_t reg_index, word_t reg)
|
||||
break;
|
||||
case seL4_VCPUReg_FPEXC:
|
||||
break;
|
||||
case seL4_VCPUReg_CNTV_TVAL:
|
||||
set_cntv_tval(reg);
|
||||
break;
|
||||
case seL4_VCPUReg_CNTV_CTL:
|
||||
set_cntv_ctl(reg);
|
||||
break;
|
||||
case seL4_VCPUReg_LRsvc:
|
||||
set_lr_svc(reg);
|
||||
break;
|
||||
@@ -508,6 +588,21 @@ static void vcpu_hw_write_reg(word_t reg_index, word_t reg)
|
||||
case seL4_VCPUReg_SPSRfiq:
|
||||
set_spsr_fiq(reg);
|
||||
break;
|
||||
case seL4_VCPUReg_CNTV_CTL:
|
||||
set_cntv_ctl(reg);
|
||||
break;
|
||||
case seL4_VCPUReg_CNTV_CVALhigh:
|
||||
set_cntv_cval_high(reg);
|
||||
break;
|
||||
case seL4_VCPUReg_CNTV_CVALlow:
|
||||
set_cntv_cval_low(reg);
|
||||
break;
|
||||
case seL4_VCPUReg_CNTVOFFhigh:
|
||||
set_cntv_off_high(reg);
|
||||
break;
|
||||
case seL4_VCPUReg_CNTVOFFlow:
|
||||
set_cntv_off_low(reg);
|
||||
break;
|
||||
default:
|
||||
fail("ARM/HYP: Invalid register index");
|
||||
}
|
||||
@@ -657,6 +752,9 @@ static inline void vcpu_enable(vcpu_t *vcpu)
|
||||
vcpu->vcpuTCB->tcbArch.tcbContext.fpuState.fpexc = vcpu_read_reg(vcpu, seL4_VCPUReg_FPEXC);
|
||||
access_fpexc(vcpu, true);
|
||||
#endif
|
||||
/* Restore virtual timer state */
|
||||
restore_virt_timer(vcpu);
|
||||
|
||||
}
|
||||
|
||||
static inline void vcpu_disable(vcpu_t *vcpu)
|
||||
@@ -699,6 +797,12 @@ static inline void vcpu_disable(vcpu_t *vcpu)
|
||||
setHDCRTrapDebugExceptionState(true);
|
||||
#endif
|
||||
isb();
|
||||
if (likely(vcpu)) {
|
||||
/* Save virtual timer state */
|
||||
save_virt_timer(vcpu);
|
||||
/* Mask the virtual timer interrupt */
|
||||
maskInterrupt(true, CORE_IRQ_TO_IRQT(CURRENT_CPU_INDEX(), INTERRUPT_VTIMER_EVENT));
|
||||
}
|
||||
}
|
||||
|
||||
static inline void armv_vcpu_init(vcpu_t *vcpu)
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
#ifdef CONFIG_ARM_HYPERVISOR_SUPPORT
|
||||
|
||||
#include <arch/object/vcpu.h>
|
||||
#include <drivers/timer/arm_generic.h>
|
||||
|
||||
/* Note that the HCR_DC for ARMv8 disables S1 translation if enabled */
|
||||
/* Trap WFI/WFE/SMC and override CPSR.AIF */
|
||||
@@ -112,6 +113,8 @@
|
||||
#define REG_CPACR_EL1 "cpacr_el1"
|
||||
#define REG_CNTV_TVAL_EL0 "cntv_tval_el0"
|
||||
#define REG_CNTV_CTL_EL0 "cntv_ctl_el0"
|
||||
#define REG_CNTV_CVAL_EL0 "cntv_cval_el0"
|
||||
#define REG_CNTVOFF_EL2 "cntvoff_el2"
|
||||
#define REG_HCR_EL2 "hcr_el2"
|
||||
#define REG_VTCR_EL2 "vtcr_el2"
|
||||
#define REG_ID_AA64MMFR0_EL1 "id_aa64mmfr0_el1"
|
||||
@@ -351,6 +354,30 @@ static inline void writeCNTV_CTL_EL0(word_t reg)
|
||||
MSR(REG_CNTV_CTL_EL0, reg);
|
||||
}
|
||||
|
||||
static inline word_t readCNTV_CVAL_EL0(void)
|
||||
{
|
||||
word_t reg;
|
||||
MRS(REG_CNTV_CVAL_EL0, reg);
|
||||
return reg;
|
||||
}
|
||||
|
||||
static inline void writeCNTV_CVAL_EL0(word_t reg)
|
||||
{
|
||||
MSR(REG_CNTV_CVAL_EL0, reg);
|
||||
}
|
||||
|
||||
static inline word_t readCNTVOFF_EL2(void)
|
||||
{
|
||||
word_t reg;
|
||||
MRS(REG_CNTVOFF_EL2, reg);
|
||||
return reg;
|
||||
}
|
||||
|
||||
static inline void writeCNTVOFF_EL2(word_t reg)
|
||||
{
|
||||
MSR(REG_CNTVOFF_EL2, reg);
|
||||
}
|
||||
|
||||
static word_t vcpu_hw_read_reg(word_t reg_index)
|
||||
{
|
||||
word_t reg = 0;
|
||||
@@ -387,18 +414,18 @@ static word_t vcpu_hw_read_reg(word_t reg_index)
|
||||
return readVBAR();
|
||||
case seL4_VCPUReg_TPIDR_EL1:
|
||||
return readTPIDR_EL1();
|
||||
case seL4_VCPUReg_CNTV_TVAL:
|
||||
return readCNTV_TVAL_EL0();
|
||||
case seL4_VCPUReg_CNTV_CTL:
|
||||
return readCNTV_CTL_EL0();
|
||||
case seL4_VCPUReg_CNTV_CVAL:
|
||||
return 0;
|
||||
case seL4_VCPUReg_SP_EL1:
|
||||
return readSP_EL1();
|
||||
case seL4_VCPUReg_ELR_EL1:
|
||||
return readELR_EL1();
|
||||
case seL4_VCPUReg_SPSR_EL1:
|
||||
return readSPSR_EL1();
|
||||
case seL4_VCPUReg_CNTV_CTL:
|
||||
return readCNTV_CTL_EL0();
|
||||
case seL4_VCPUReg_CNTV_CVAL:
|
||||
return readCNTV_CVAL_EL0();
|
||||
case seL4_VCPUReg_CNTVOFF:
|
||||
return readCNTVOFF_EL2();
|
||||
default:
|
||||
fail("ARM/HYP: Invalid register index");
|
||||
}
|
||||
@@ -442,18 +469,18 @@ static void vcpu_hw_write_reg(word_t reg_index, word_t reg)
|
||||
return writeVBAR(reg);
|
||||
case seL4_VCPUReg_TPIDR_EL1:
|
||||
return writeTPIDR_EL1(reg);
|
||||
case seL4_VCPUReg_CNTV_TVAL:
|
||||
return writeCNTV_TVAL_EL0(reg);
|
||||
case seL4_VCPUReg_CNTV_CTL:
|
||||
return writeCNTV_CTL_EL0(reg);
|
||||
case seL4_VCPUReg_CNTV_CVAL:
|
||||
return;
|
||||
case seL4_VCPUReg_SP_EL1:
|
||||
return writeSP_EL1(reg);
|
||||
case seL4_VCPUReg_ELR_EL1:
|
||||
return writeELR_EL1(reg);
|
||||
case seL4_VCPUReg_SPSR_EL1:
|
||||
return writeSPSR_EL1(reg);
|
||||
case seL4_VCPUReg_CNTV_CTL:
|
||||
return writeCNTV_CTL_EL0(reg);
|
||||
case seL4_VCPUReg_CNTV_CVAL:
|
||||
return writeCNTV_CVAL_EL0(reg);
|
||||
case seL4_VCPUReg_CNTVOFF:
|
||||
return writeCNTVOFF_EL2(reg);
|
||||
default:
|
||||
fail("ARM/HYP: Invalid register index");
|
||||
}
|
||||
@@ -531,6 +558,8 @@ static inline void vcpu_enable(vcpu_t *vcpu)
|
||||
#ifdef CONFIG_HAVE_FPU
|
||||
vcpu_restore_reg(vcpu, seL4_VCPUReg_CPACR);
|
||||
#endif
|
||||
/* Restore virtual timer state */
|
||||
restore_virt_timer(vcpu);
|
||||
}
|
||||
|
||||
static inline void vcpu_disable(vcpu_t *vcpu)
|
||||
@@ -564,6 +593,12 @@ static inline void vcpu_disable(vcpu_t *vcpu)
|
||||
*/
|
||||
enableFpuEL01();
|
||||
#endif
|
||||
if (likely(vcpu)) {
|
||||
/* Save virtual timer state */
|
||||
save_virt_timer(vcpu);
|
||||
/* Mask the virtual timer interrupt */
|
||||
maskInterrupt(true, CORE_IRQ_TO_IRQT(CURRENT_CPU_INDEX(), INTERRUPT_VTIMER_EVENT));
|
||||
}
|
||||
}
|
||||
|
||||
static inline void armv_vcpu_init(vcpu_t *vcpu)
|
||||
|
||||
@@ -51,4 +51,10 @@ static inline void resetTimer(void)
|
||||
|
||||
BOOT_CODE void initGenericTimer(void);
|
||||
|
||||
#ifdef CONFIG_ARM_HYPERVISOR_SUPPORT
|
||||
static uint64_t read_cntpct(void) UNUSED;
|
||||
static void save_virt_timer(vcpu_t *vcpu);
|
||||
static void restore_virt_timer(vcpu_t *vcpu);
|
||||
#endif /* CONFIG_ARM_HYPERVISOR_SUPPORT */
|
||||
|
||||
#endif /* __DRIVERS_TIMER_ARM_GENERIC_H_ */
|
||||
|
||||
@@ -103,8 +103,6 @@ enum {
|
||||
seL4_VCPUReg_TPIDRPRW,
|
||||
seL4_VCPUReg_TPIDRURO,
|
||||
seL4_VCPUReg_FPEXC,
|
||||
seL4_VCPUReg_CNTV_TVAL,
|
||||
seL4_VCPUReg_CNTV_CTL,
|
||||
seL4_VCPUReg_LRsvc,
|
||||
seL4_VCPUReg_SPsvc,
|
||||
seL4_VCPUReg_LRabt,
|
||||
@@ -125,6 +123,11 @@ enum {
|
||||
seL4_VCPUReg_SPSRund,
|
||||
seL4_VCPUReg_SPSRirq,
|
||||
seL4_VCPUReg_SPSRfiq,
|
||||
seL4_VCPUReg_CNTV_CTL,
|
||||
seL4_VCPUReg_CNTV_CVALhigh,
|
||||
seL4_VCPUReg_CNTV_CVALlow,
|
||||
seL4_VCPUReg_CNTVOFFhigh,
|
||||
seL4_VCPUReg_CNTVOFFlow,
|
||||
seL4_VCPUReg_Num,
|
||||
} seL4_VCPUReg;
|
||||
#endif /* CONFIG_ARM_HYPERVISOR_SUPPORT */
|
||||
|
||||
@@ -105,15 +105,16 @@ enum {
|
||||
/* thread pointer/ID registers EL0/EL1 */
|
||||
seL4_VCPUReg_TPIDR_EL1,
|
||||
|
||||
/* generic timer registers, to be completed */
|
||||
seL4_VCPUReg_CNTV_CTL,
|
||||
seL4_VCPUReg_CNTV_TVAL,
|
||||
seL4_VCPUReg_CNTV_CVAL,
|
||||
|
||||
/* general registers x0 to x30 have been saved by traps.S */
|
||||
seL4_VCPUReg_SP_EL1,
|
||||
seL4_VCPUReg_ELR_EL1,
|
||||
seL4_VCPUReg_SPSR_EL1, // 32-bit
|
||||
|
||||
/* generic timer registers, to be completed */
|
||||
seL4_VCPUReg_CNTV_CTL,
|
||||
seL4_VCPUReg_CNTV_CVAL,
|
||||
seL4_VCPUReg_CNTVOFF,
|
||||
|
||||
seL4_VCPUReg_Num,
|
||||
} seL4_VCPUReg;
|
||||
|
||||
|
||||
@@ -100,6 +100,7 @@ BOOT_CODE static void init_irqs(cap_t root_cnode_cap)
|
||||
setIRQState(IRQTimer, CORE_IRQ_TO_IRQT(0, KERNEL_TIMER_IRQ));
|
||||
#ifdef CONFIG_ARM_HYPERVISOR_SUPPORT
|
||||
setIRQState(IRQReserved, CORE_IRQ_TO_IRQT(0, INTERRUPT_VGIC_MAINTENANCE));
|
||||
setIRQState(IRQReserved, CORE_IRQ_TO_IRQT(0, INTERRUPT_VTIMER_EVENT));
|
||||
#endif
|
||||
#ifdef CONFIG_ARM_SMMU
|
||||
setIRQState(IRQReserved, CORE_IRQ_TO_IRQT(0, INTERRUPT_SMMU));
|
||||
|
||||
@@ -17,8 +17,7 @@
|
||||
#include <arch/machine/debug.h> /* Arch_debug[A/Di]ssociateVCPUTCB() */
|
||||
#include <arch/machine/debug_conf.h>
|
||||
#include <arch/machine/gic_v2.h>
|
||||
|
||||
|
||||
#include <drivers/timer/arm_generic.h>
|
||||
|
||||
BOOT_CODE void vcpu_boot_init(void)
|
||||
{
|
||||
@@ -46,6 +45,7 @@ static void vcpu_save(vcpu_t *vcpu, bool_t active)
|
||||
if (active) {
|
||||
vcpu_save_reg(vcpu, seL4_VCPUReg_SCTLR);
|
||||
vcpu->vgic.hcr = get_gic_vcpu_ctrl_hcr();
|
||||
save_virt_timer(vcpu);
|
||||
}
|
||||
|
||||
/* Store GIC VCPU control state */
|
||||
@@ -205,6 +205,10 @@ void vcpu_init(vcpu_t *vcpu)
|
||||
armv_vcpu_init(vcpu);
|
||||
/* GICH VCPU interface control */
|
||||
vcpu->vgic.hcr = VGIC_HCR_EN;
|
||||
#ifdef CONFIG_VTIMER_UPDATE_VOFFSET
|
||||
/* Virtual Timer interface */
|
||||
vcpu->virtTimer.last_pcount = 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
void vcpu_switch(vcpu_t *new)
|
||||
|
||||
@@ -117,3 +117,12 @@ config_option(
|
||||
DEFAULT OFF
|
||||
DEPENDS "KernelArmHaveGenericTimer"
|
||||
)
|
||||
|
||||
config_option(
|
||||
KernelArmVtimerUpdateVOffset VTIMER_UPDATE_VOFFSET
|
||||
"When set the kernel will update the VOFFSET \
|
||||
register of a VCPU when restoring it so that its view of Virtual time hasn't increased while it \
|
||||
was suspended. When unset the VOFFSET won't be updated other than by the read and write register api."
|
||||
DEFAULT ON
|
||||
DEPENDS "KernelArmHypervisorSupport"
|
||||
)
|
||||
|
||||
@@ -48,3 +48,82 @@ BOOT_CODE void initTimer(void)
|
||||
initGenericTimer();
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_ARM_HYPERVISOR_SUPPORT
|
||||
|
||||
#include <arch/object/vcpu.h>
|
||||
#include <armv/vcpu.h>
|
||||
|
||||
static inline uint64_t read_cntpct(void)
|
||||
{
|
||||
uint64_t val;
|
||||
SYSTEM_READ_64(CNTPCT, val);
|
||||
return val;
|
||||
}
|
||||
|
||||
static void save_virt_timer(vcpu_t *vcpu)
|
||||
{
|
||||
/* Save control register */
|
||||
vcpu_save_reg(vcpu, seL4_VCPUReg_CNTV_CTL);
|
||||
vcpu_hw_write_reg(seL4_VCPUReg_CNTV_CTL, 0);
|
||||
/* Save Compare Value and Offset registers */
|
||||
#ifdef CONFIG_ARCH_AARCH64
|
||||
vcpu_save_reg(vcpu, seL4_VCPUReg_CNTV_CVAL);
|
||||
vcpu_save_reg(vcpu, seL4_VCPUReg_CNTVOFF);
|
||||
#else
|
||||
uint64_t cval = get_cntv_cval_64();
|
||||
uint64_t cntvoff = get_cntv_off_64();
|
||||
vcpu_write_reg(vcpu, seL4_VCPUReg_CNTV_CVALhigh, (word_t)(cval >> 32));
|
||||
vcpu_write_reg(vcpu, seL4_VCPUReg_CNTV_CVALlow, (word_t)cval);
|
||||
vcpu_write_reg(vcpu, seL4_VCPUReg_CNTVOFFhigh, (word_t)(cntvoff >> 32));
|
||||
vcpu_write_reg(vcpu, seL4_VCPUReg_CNTVOFFlow, (word_t)cntvoff);
|
||||
#endif
|
||||
#ifdef CONFIG_VTIMER_UPDATE_VOFFSET
|
||||
/* Save counter value at the time the vcpu is disabled */
|
||||
vcpu->virtTimer.last_pcount = read_cntpct();
|
||||
#endif
|
||||
}
|
||||
|
||||
static void restore_virt_timer(vcpu_t *vcpu)
|
||||
{
|
||||
/* Restore virtual timer state */
|
||||
#ifdef CONFIG_ARCH_AARCH64
|
||||
vcpu_restore_reg(vcpu, seL4_VCPUReg_CNTV_CVAL);
|
||||
#else
|
||||
uint32_t cval_high = vcpu_read_reg(vcpu, seL4_VCPUReg_CNTV_CVALhigh);
|
||||
uint32_t cval_low = vcpu_read_reg(vcpu, seL4_VCPUReg_CNTV_CVALlow);
|
||||
uint64_t cval = ((uint64_t)cval_high << 32) | (uint64_t) cval_low;
|
||||
set_cntv_cval_64(cval);
|
||||
#endif
|
||||
|
||||
/* Set virtual timer offset */
|
||||
#ifdef CONFIG_VTIMER_UPDATE_VOFFSET
|
||||
uint64_t pcount_delta;
|
||||
uint64_t current_cntpct = read_cntpct();
|
||||
pcount_delta = current_cntpct - vcpu->virtTimer.last_pcount;
|
||||
#endif
|
||||
#ifdef CONFIG_ARCH_AARCH64
|
||||
#ifdef CONFIG_VTIMER_UPDATE_VOFFSET
|
||||
uint64_t offset = vcpu_read_reg(vcpu, seL4_VCPUReg_CNTVOFF);
|
||||
offset += pcount_delta;
|
||||
vcpu_write_reg(vcpu, seL4_VCPUReg_CNTVOFF, offset);
|
||||
#endif
|
||||
vcpu_restore_reg(vcpu, seL4_VCPUReg_CNTVOFF);
|
||||
#else
|
||||
uint32_t offset_high = vcpu_read_reg(vcpu, seL4_VCPUReg_CNTVOFFhigh);
|
||||
uint32_t offset_low = vcpu_read_reg(vcpu, seL4_VCPUReg_CNTVOFFlow);
|
||||
uint64_t offset = ((uint64_t)offset_high << 32) | (uint64_t) offset_low;
|
||||
#ifdef CONFIG_VTIMER_UPDATE_VOFFSET
|
||||
offset += pcount_delta;
|
||||
vcpu_write_reg(vcpu, seL4_VCPUReg_CNTVOFFhigh, (word_t)(offset >> 32));
|
||||
vcpu_write_reg(vcpu, seL4_VCPUReg_CNTVOFFlow, (word_t) offset);
|
||||
#endif
|
||||
set_cntv_off_64(offset);
|
||||
#endif
|
||||
/* Restore interrupt mask state */
|
||||
maskInterrupt(vcpu->vppi_masked[irqVPPIEventIndex(INTERRUPT_VTIMER_EVENT)], INTERRUPT_VTIMER_EVENT);
|
||||
/* Restore virtual timer control register */
|
||||
vcpu_restore_reg(vcpu, seL4_VCPUReg_CNTV_CTL);
|
||||
}
|
||||
|
||||
#endif /* CONFIG_ARM_HYPERVISOR_SUPPORT */
|
||||
|
||||
@@ -135,6 +135,7 @@ devices:
|
||||
sel_macro: CONFIG_ARM_HYPERVISOR_SUPPORT
|
||||
index: 3
|
||||
undef_index: 2
|
||||
INTERRUPT_VTIMER_EVENT: 2
|
||||
# Allwinner A10 Timer (timer/allwinner,sun4i-timer.txt)
|
||||
- compatible:
|
||||
- allwinner,sun4i-a10-timer
|
||||
|
||||
Reference in New Issue
Block a user