mirror of
git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2025-09-04 20:19:47 +08:00
Merge branch kvm-arm64/nv-timers into kvmarm-master/next
* kvm-arm64/nv-timers: : . : Nested Virt support for the EL2 timers. From the initial cover letter: : : "Here's another batch of NV-related patches, this time bringing in most : of the timer support for EL2 as well as nested guests. : : The code is pretty convoluted for a bunch of reasons: : : - FEAT_NV2 breaks the timer semantics by redirecting HW controls to : memory, meaning that a guest could setup a timer and never see it : firing until the next exit : : - We go try hard to reflect the timer state in memory, but that's not : great. : : - With FEAT_ECV, we can finally correctly emulate the virtual timer, : but this emulation is pretty costly : : - As a way to make things suck less, we handle timer reads as early as : possible, and only defer writes to the normal trap handling : : - Finally, some implementations are badly broken, and require some : hand-holding, irrespective of NV support. So we try and reuse the NV : infrastructure to make them usable. This could be further optimised, : but I'm running out of patience for this sort of HW. : : [...]" : . KVM: arm64: nv: Fix doc header layout for timers KVM: arm64: nv: Document EL2 timer API KVM: arm64: Work around x1e's CNTVOFF_EL2 bogosity KVM: arm64: nv: Sanitise CNTHCTL_EL2 KVM: arm64: nv: Propagate CNTHCTL_EL2.EL1NV{P,V}CT bits KVM: arm64: nv: Add trap routing for CNTHCTL_EL2.EL1{NVPCT,NVVCT,TVT,TVCT} KVM: arm64: Handle counter access early in non-HYP context KVM: arm64: nv: Accelerate EL0 counter accesses from hypervisor context KVM: arm64: nv: Accelerate EL0 timer read accesses when FEAT_ECV in use KVM: arm64: nv: Use FEAT_ECV to trap access to EL0 timers KVM: arm64: nv: Publish emulated timer interrupt state in the in-memory state KVM: arm64: nv: Sync nested timer state with FEAT_NV2 KVM: arm64: nv: Add handling of EL2-specific timer registers Signed-off-by: Marc Zyngier <maz@kernel.org>
This commit is contained in:
commit
080612b294
@ -142,8 +142,8 @@ the cpu field to the processor id.
|
||||
|
||||
:Architectures: ARM64
|
||||
|
||||
2.1. ATTRIBUTES: KVM_ARM_VCPU_TIMER_IRQ_VTIMER, KVM_ARM_VCPU_TIMER_IRQ_PTIMER
|
||||
-----------------------------------------------------------------------------
|
||||
2.1. ATTRIBUTES: KVM_ARM_VCPU_TIMER_IRQ_{VTIMER,PTIMER,HVTIMER,HPTIMER}
|
||||
-----------------------------------------------------------------------
|
||||
|
||||
:Parameters: in kvm_device_attr.addr the address for the timer interrupt is a
|
||||
pointer to an int
|
||||
@ -159,10 +159,12 @@ A value describing the architected timer interrupt number when connected to an
|
||||
in-kernel virtual GIC. These must be a PPI (16 <= intid < 32). Setting the
|
||||
attribute overrides the default values (see below).
|
||||
|
||||
============================= ==========================================
|
||||
============================== ==========================================
|
||||
KVM_ARM_VCPU_TIMER_IRQ_VTIMER The EL1 virtual timer intid (default: 27)
|
||||
KVM_ARM_VCPU_TIMER_IRQ_PTIMER The EL1 physical timer intid (default: 30)
|
||||
============================= ==========================================
|
||||
KVM_ARM_VCPU_TIMER_IRQ_HVTIMER The EL2 virtual timer intid (default: 28)
|
||||
KVM_ARM_VCPU_TIMER_IRQ_HPTIMER The EL2 physical timer intid (default: 26)
|
||||
============================== ==========================================
|
||||
|
||||
Setting the same PPI for different timers will prevent the VCPUs from running.
|
||||
Setting the interrupt number on a VCPU configures all VCPUs created at that
|
||||
|
@ -122,6 +122,7 @@
|
||||
#define QCOM_CPU_PART_KRYO_3XX_SILVER 0x803
|
||||
#define QCOM_CPU_PART_KRYO_4XX_GOLD 0x804
|
||||
#define QCOM_CPU_PART_KRYO_4XX_SILVER 0x805
|
||||
#define QCOM_CPU_PART_ORYON_X1 0x001
|
||||
|
||||
#define NVIDIA_CPU_PART_DENVER 0x003
|
||||
#define NVIDIA_CPU_PART_CARMEL 0x004
|
||||
@ -198,6 +199,7 @@
|
||||
#define MIDR_QCOM_KRYO_3XX_SILVER MIDR_CPU_MODEL(ARM_CPU_IMP_QCOM, QCOM_CPU_PART_KRYO_3XX_SILVER)
|
||||
#define MIDR_QCOM_KRYO_4XX_GOLD MIDR_CPU_MODEL(ARM_CPU_IMP_QCOM, QCOM_CPU_PART_KRYO_4XX_GOLD)
|
||||
#define MIDR_QCOM_KRYO_4XX_SILVER MIDR_CPU_MODEL(ARM_CPU_IMP_QCOM, QCOM_CPU_PART_KRYO_4XX_SILVER)
|
||||
#define MIDR_QCOM_ORYON_X1 MIDR_CPU_MODEL(ARM_CPU_IMP_QCOM, QCOM_CPU_PART_ORYON_X1)
|
||||
#define MIDR_NVIDIA_DENVER MIDR_CPU_MODEL(ARM_CPU_IMP_NVIDIA, NVIDIA_CPU_PART_DENVER)
|
||||
#define MIDR_NVIDIA_CARMEL MIDR_CPU_MODEL(ARM_CPU_IMP_NVIDIA, NVIDIA_CPU_PART_CARMEL)
|
||||
#define MIDR_FUJITSU_A64FX MIDR_CPU_MODEL(ARM_CPU_IMP_FUJITSU, FUJITSU_CPU_PART_A64FX)
|
||||
|
@ -493,7 +493,6 @@ enum vcpu_sysreg {
|
||||
VBAR_EL2, /* Vector Base Address Register (EL2) */
|
||||
RVBAR_EL2, /* Reset Vector Base Address Register */
|
||||
CONTEXTIDR_EL2, /* Context ID Register (EL2) */
|
||||
CNTHCTL_EL2, /* Counter-timer Hypervisor Control register */
|
||||
SP_EL2, /* EL2 Stack Pointer */
|
||||
CNTHP_CTL_EL2,
|
||||
CNTHP_CVAL_EL2,
|
||||
@ -504,6 +503,7 @@ enum vcpu_sysreg {
|
||||
MARKER(__SANITISED_REG_START__),
|
||||
TCR2_EL2, /* Extended Translation Control Register (EL2) */
|
||||
MDCR_EL2, /* Monitor Debug Configuration Register (EL2) */
|
||||
CNTHCTL_EL2, /* Counter-timer Hypervisor Control register */
|
||||
|
||||
/* Any VNCR-capable reg goes after this point */
|
||||
MARKER(__VNCR_START__),
|
||||
|
@ -477,6 +477,7 @@
|
||||
#define SYS_CNTFRQ_EL0 sys_reg(3, 3, 14, 0, 0)
|
||||
|
||||
#define SYS_CNTPCT_EL0 sys_reg(3, 3, 14, 0, 1)
|
||||
#define SYS_CNTVCT_EL0 sys_reg(3, 3, 14, 0, 2)
|
||||
#define SYS_CNTPCTSS_EL0 sys_reg(3, 3, 14, 0, 5)
|
||||
#define SYS_CNTVCTSS_EL0 sys_reg(3, 3, 14, 0, 6)
|
||||
|
||||
@ -484,14 +485,17 @@
|
||||
#define SYS_CNTP_CTL_EL0 sys_reg(3, 3, 14, 2, 1)
|
||||
#define SYS_CNTP_CVAL_EL0 sys_reg(3, 3, 14, 2, 2)
|
||||
|
||||
#define SYS_CNTV_TVAL_EL0 sys_reg(3, 3, 14, 3, 0)
|
||||
#define SYS_CNTV_CTL_EL0 sys_reg(3, 3, 14, 3, 1)
|
||||
#define SYS_CNTV_CVAL_EL0 sys_reg(3, 3, 14, 3, 2)
|
||||
|
||||
#define SYS_AARCH32_CNTP_TVAL sys_reg(0, 0, 14, 2, 0)
|
||||
#define SYS_AARCH32_CNTP_CTL sys_reg(0, 0, 14, 2, 1)
|
||||
#define SYS_AARCH32_CNTPCT sys_reg(0, 0, 0, 14, 0)
|
||||
#define SYS_AARCH32_CNTVCT sys_reg(0, 1, 0, 14, 0)
|
||||
#define SYS_AARCH32_CNTP_CVAL sys_reg(0, 2, 0, 14, 0)
|
||||
#define SYS_AARCH32_CNTPCTSS sys_reg(0, 8, 0, 14, 0)
|
||||
#define SYS_AARCH32_CNTVCTSS sys_reg(0, 9, 0, 14, 0)
|
||||
|
||||
#define __PMEV_op2(n) ((n) & 0x7)
|
||||
#define __CNTR_CRm(n) (0x8 | (((n) >> 3) & 0x3))
|
||||
|
@ -786,6 +786,14 @@ const struct arm64_cpu_capabilities arm64_errata[] = {
|
||||
ERRATA_MIDR_RANGE_LIST(erratum_ac03_cpu_38_list),
|
||||
},
|
||||
#endif
|
||||
{
|
||||
.desc = "Broken CNTVOFF_EL2",
|
||||
.capability = ARM64_WORKAROUND_QCOM_ORYON_CNTVOFF,
|
||||
ERRATA_MIDR_RANGE_LIST(((const struct midr_range[]) {
|
||||
MIDR_ALL_VERSIONS(MIDR_QCOM_ORYON_X1),
|
||||
{}
|
||||
})),
|
||||
},
|
||||
{
|
||||
}
|
||||
};
|
||||
|
@ -105,6 +105,9 @@ KVM_NVHE_ALIAS(__hyp_stub_vectors);
|
||||
KVM_NVHE_ALIAS(vgic_v2_cpuif_trap);
|
||||
KVM_NVHE_ALIAS(vgic_v3_cpuif_trap);
|
||||
|
||||
/* Static key which is set if CNTVOFF_EL2 is unusable */
|
||||
KVM_NVHE_ALIAS(broken_cntvoff_key);
|
||||
|
||||
/* EL2 exception handling */
|
||||
KVM_NVHE_ALIAS(__start___kvm_ex_table);
|
||||
KVM_NVHE_ALIAS(__stop___kvm_ex_table);
|
||||
|
@ -30,6 +30,7 @@ static u32 host_vtimer_irq_flags;
|
||||
static u32 host_ptimer_irq_flags;
|
||||
|
||||
static DEFINE_STATIC_KEY_FALSE(has_gic_active_state);
|
||||
DEFINE_STATIC_KEY_FALSE(broken_cntvoff_key);
|
||||
|
||||
static const u8 default_ppi[] = {
|
||||
[TIMER_PTIMER] = 30,
|
||||
@ -101,21 +102,6 @@ u64 timer_get_cval(struct arch_timer_context *ctxt)
|
||||
}
|
||||
}
|
||||
|
||||
static u64 timer_get_offset(struct arch_timer_context *ctxt)
|
||||
{
|
||||
u64 offset = 0;
|
||||
|
||||
if (!ctxt)
|
||||
return 0;
|
||||
|
||||
if (ctxt->offset.vm_offset)
|
||||
offset += *ctxt->offset.vm_offset;
|
||||
if (ctxt->offset.vcpu_offset)
|
||||
offset += *ctxt->offset.vcpu_offset;
|
||||
|
||||
return offset;
|
||||
}
|
||||
|
||||
static void timer_set_ctl(struct arch_timer_context *ctxt, u32 ctl)
|
||||
{
|
||||
struct kvm_vcpu *vcpu = ctxt->vcpu;
|
||||
@ -441,11 +427,30 @@ void kvm_timer_update_run(struct kvm_vcpu *vcpu)
|
||||
regs->device_irq_level |= KVM_ARM_DEV_EL1_PTIMER;
|
||||
}
|
||||
|
||||
static void kvm_timer_update_status(struct arch_timer_context *ctx, bool level)
|
||||
{
|
||||
/*
|
||||
* Paper over NV2 brokenness by publishing the interrupt status
|
||||
* bit. This still results in a poor quality of emulation (guest
|
||||
* writes will have no effect until the next exit).
|
||||
*
|
||||
* But hey, it's fast, right?
|
||||
*/
|
||||
if (is_hyp_ctxt(ctx->vcpu) &&
|
||||
(ctx == vcpu_vtimer(ctx->vcpu) || ctx == vcpu_ptimer(ctx->vcpu))) {
|
||||
unsigned long val = timer_get_ctl(ctx);
|
||||
__assign_bit(__ffs(ARCH_TIMER_CTRL_IT_STAT), &val, level);
|
||||
timer_set_ctl(ctx, val);
|
||||
}
|
||||
}
|
||||
|
||||
static void kvm_timer_update_irq(struct kvm_vcpu *vcpu, bool new_level,
|
||||
struct arch_timer_context *timer_ctx)
|
||||
{
|
||||
int ret;
|
||||
|
||||
kvm_timer_update_status(timer_ctx, new_level);
|
||||
|
||||
timer_ctx->irq.level = new_level;
|
||||
trace_kvm_timer_update_irq(vcpu->vcpu_id, timer_irq(timer_ctx),
|
||||
timer_ctx->irq.level);
|
||||
@ -471,6 +476,8 @@ static void timer_emulate(struct arch_timer_context *ctx)
|
||||
return;
|
||||
}
|
||||
|
||||
kvm_timer_update_status(ctx, should_fire);
|
||||
|
||||
/*
|
||||
* If the timer can fire now, we don't need to have a soft timer
|
||||
* scheduled for the future. If the timer cannot fire at all,
|
||||
@ -513,7 +520,12 @@ static void timer_save_state(struct arch_timer_context *ctx)
|
||||
case TIMER_VTIMER:
|
||||
case TIMER_HVTIMER:
|
||||
timer_set_ctl(ctx, read_sysreg_el0(SYS_CNTV_CTL));
|
||||
timer_set_cval(ctx, read_sysreg_el0(SYS_CNTV_CVAL));
|
||||
cval = read_sysreg_el0(SYS_CNTV_CVAL);
|
||||
|
||||
if (has_broken_cntvoff())
|
||||
cval -= timer_get_offset(ctx);
|
||||
|
||||
timer_set_cval(ctx, cval);
|
||||
|
||||
/* Disable the timer */
|
||||
write_sysreg_el0(0, SYS_CNTV_CTL);
|
||||
@ -618,8 +630,15 @@ static void timer_restore_state(struct arch_timer_context *ctx)
|
||||
|
||||
case TIMER_VTIMER:
|
||||
case TIMER_HVTIMER:
|
||||
set_cntvoff(timer_get_offset(ctx));
|
||||
write_sysreg_el0(timer_get_cval(ctx), SYS_CNTV_CVAL);
|
||||
cval = timer_get_cval(ctx);
|
||||
offset = timer_get_offset(ctx);
|
||||
if (has_broken_cntvoff()) {
|
||||
set_cntvoff(0);
|
||||
cval += offset;
|
||||
} else {
|
||||
set_cntvoff(offset);
|
||||
}
|
||||
write_sysreg_el0(cval, SYS_CNTV_CVAL);
|
||||
isb();
|
||||
write_sysreg_el0(timer_get_ctl(ctx), SYS_CNTV_CTL);
|
||||
break;
|
||||
@ -762,7 +781,7 @@ static void kvm_timer_vcpu_load_nested_switch(struct kvm_vcpu *vcpu,
|
||||
|
||||
static void timer_set_traps(struct kvm_vcpu *vcpu, struct timer_map *map)
|
||||
{
|
||||
bool tpt, tpc;
|
||||
bool tvt, tpt, tvc, tpc, tvt02, tpt02;
|
||||
u64 clr, set;
|
||||
|
||||
/*
|
||||
@ -777,7 +796,29 @@ static void timer_set_traps(struct kvm_vcpu *vcpu, struct timer_map *map)
|
||||
* within this function, reality kicks in and we start adding
|
||||
* traps based on emulation requirements.
|
||||
*/
|
||||
tpt = tpc = false;
|
||||
tvt = tpt = tvc = tpc = false;
|
||||
tvt02 = tpt02 = false;
|
||||
|
||||
/*
|
||||
* NV2 badly breaks the timer semantics by redirecting accesses to
|
||||
* the EL1 timer state to memory, so let's call ECV to the rescue if
|
||||
* available: we trap all CNT{P,V}_{CTL,CVAL,TVAL}_EL0 accesses.
|
||||
*
|
||||
* The treatment slightly varies depending whether we run a nVHE or
|
||||
* VHE guest: nVHE will use the _EL0 registers directly, while VHE
|
||||
* will use the _EL02 accessors. This translates in different trap
|
||||
* bits.
|
||||
*
|
||||
* None of the trapping is required when running in non-HYP context,
|
||||
* unless required by the L1 hypervisor settings once we advertise
|
||||
* ECV+NV in the guest, or that we need trapping for other reasons.
|
||||
*/
|
||||
if (cpus_have_final_cap(ARM64_HAS_ECV) && is_hyp_ctxt(vcpu)) {
|
||||
if (vcpu_el2_e2h_is_set(vcpu))
|
||||
tvt02 = tpt02 = true;
|
||||
else
|
||||
tvt = tpt = true;
|
||||
}
|
||||
|
||||
/*
|
||||
* We have two possibility to deal with a physical offset:
|
||||
@ -792,10 +833,21 @@ static void timer_set_traps(struct kvm_vcpu *vcpu, struct timer_map *map)
|
||||
if (!has_cntpoff() && timer_get_offset(map->direct_ptimer))
|
||||
tpt = tpc = true;
|
||||
|
||||
/*
|
||||
* For the poor sods that could not correctly substract one value
|
||||
* from another, trap the full virtual timer and counter.
|
||||
*/
|
||||
if (has_broken_cntvoff() && timer_get_offset(map->direct_vtimer))
|
||||
tvt = tvc = true;
|
||||
|
||||
/*
|
||||
* Apply the enable bits that the guest hypervisor has requested for
|
||||
* its own guest. We can only add traps that wouldn't have been set
|
||||
* above.
|
||||
* Implementation choices: we do not support NV when E2H=0 in the
|
||||
* guest, and we don't support configuration where E2H is writable
|
||||
* by the guest (either FEAT_VHE or FEAT_E2H0 is implemented, but
|
||||
* not both). This simplifies the handling of the EL1NV* bits.
|
||||
*/
|
||||
if (vcpu_has_nv(vcpu) && !is_hyp_ctxt(vcpu)) {
|
||||
u64 val = __vcpu_sys_reg(vcpu, CNTHCTL_EL2);
|
||||
@ -806,6 +858,9 @@ static void timer_set_traps(struct kvm_vcpu *vcpu, struct timer_map *map)
|
||||
|
||||
tpt |= !(val & (CNTHCTL_EL1PCEN << 10));
|
||||
tpc |= !(val & (CNTHCTL_EL1PCTEN << 10));
|
||||
|
||||
tpt02 |= (val & CNTHCTL_EL1NVPCT);
|
||||
tvt02 |= (val & CNTHCTL_EL1NVVCT);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -817,6 +872,10 @@ static void timer_set_traps(struct kvm_vcpu *vcpu, struct timer_map *map)
|
||||
|
||||
assign_clear_set_bit(tpt, CNTHCTL_EL1PCEN << 10, set, clr);
|
||||
assign_clear_set_bit(tpc, CNTHCTL_EL1PCTEN << 10, set, clr);
|
||||
assign_clear_set_bit(tvt, CNTHCTL_EL1TVT, clr, set);
|
||||
assign_clear_set_bit(tvc, CNTHCTL_EL1TVCT, clr, set);
|
||||
assign_clear_set_bit(tvt02, CNTHCTL_EL1NVVCT, clr, set);
|
||||
assign_clear_set_bit(tpt02, CNTHCTL_EL1NVPCT, clr, set);
|
||||
|
||||
/* This only happens on VHE, so use the CNTHCTL_EL2 accessor. */
|
||||
sysreg_clear_set(cnthctl_el2, clr, set);
|
||||
@ -905,6 +964,54 @@ void kvm_timer_vcpu_put(struct kvm_vcpu *vcpu)
|
||||
kvm_timer_blocking(vcpu);
|
||||
}
|
||||
|
||||
void kvm_timer_sync_nested(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
/*
|
||||
* When NV2 is on, guest hypervisors have their EL1 timer register
|
||||
* accesses redirected to the VNCR page. Any guest action taken on
|
||||
* the timer is postponed until the next exit, leading to a very
|
||||
* poor quality of emulation.
|
||||
*
|
||||
* This is an unmitigated disaster, only papered over by FEAT_ECV,
|
||||
* which allows trapping of the timer registers even with NV2.
|
||||
* Still, this is still worse than FEAT_NV on its own. Meh.
|
||||
*/
|
||||
if (!vcpu_el2_e2h_is_set(vcpu)) {
|
||||
if (cpus_have_final_cap(ARM64_HAS_ECV))
|
||||
return;
|
||||
|
||||
/*
|
||||
* A non-VHE guest hypervisor doesn't have any direct access
|
||||
* to its timers: the EL2 registers trap (and the HW is
|
||||
* fully emulated), while the EL0 registers access memory
|
||||
* despite the access being notionally direct. Boo.
|
||||
*
|
||||
* We update the hardware timer registers with the
|
||||
* latest value written by the guest to the VNCR page
|
||||
* and let the hardware take care of the rest.
|
||||
*/
|
||||
write_sysreg_el0(__vcpu_sys_reg(vcpu, CNTV_CTL_EL0), SYS_CNTV_CTL);
|
||||
write_sysreg_el0(__vcpu_sys_reg(vcpu, CNTV_CVAL_EL0), SYS_CNTV_CVAL);
|
||||
write_sysreg_el0(__vcpu_sys_reg(vcpu, CNTP_CTL_EL0), SYS_CNTP_CTL);
|
||||
write_sysreg_el0(__vcpu_sys_reg(vcpu, CNTP_CVAL_EL0), SYS_CNTP_CVAL);
|
||||
} else {
|
||||
/*
|
||||
* For a VHE guest hypervisor, the EL2 state is directly
|
||||
* stored in the host EL1 timers, while the emulated EL0
|
||||
* state is stored in the VNCR page. The latter could have
|
||||
* been updated behind our back, and we must reset the
|
||||
* emulation of the timers.
|
||||
*/
|
||||
struct timer_map map;
|
||||
get_timer_map(vcpu, &map);
|
||||
|
||||
soft_timer_cancel(&map.emul_vtimer->hrtimer);
|
||||
soft_timer_cancel(&map.emul_ptimer->hrtimer);
|
||||
timer_emulate(map.emul_vtimer);
|
||||
timer_emulate(map.emul_ptimer);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* With a userspace irqchip we have to check if the guest de-asserted the
|
||||
* timer and if so, unmask the timer irq signal on the host interrupt
|
||||
@ -1363,6 +1470,37 @@ static int kvm_irq_init(struct arch_timer_kvm_info *info)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void kvm_timer_handle_errata(void)
|
||||
{
|
||||
u64 mmfr0, mmfr1, mmfr4;
|
||||
|
||||
/*
|
||||
* CNTVOFF_EL2 is broken on some implementations. For those, we trap
|
||||
* all virtual timer/counter accesses, requiring FEAT_ECV.
|
||||
*
|
||||
* However, a hypervisor supporting nesting is likely to mitigate the
|
||||
* erratum at L0, and not require other levels to mitigate it (which
|
||||
* would otherwise be a terrible performance sink due to trap
|
||||
* amplification).
|
||||
*
|
||||
* Given that the affected HW implements both FEAT_VHE and FEAT_E2H0,
|
||||
* and that NV is likely not to (because of limitations of the
|
||||
* architecture), only enable the workaround when FEAT_VHE and
|
||||
* FEAT_E2H0 are both detected. Time will tell if this actually holds.
|
||||
*/
|
||||
mmfr0 = read_sanitised_ftr_reg(SYS_ID_AA64MMFR0_EL1);
|
||||
mmfr1 = read_sanitised_ftr_reg(SYS_ID_AA64MMFR1_EL1);
|
||||
mmfr4 = read_sanitised_ftr_reg(SYS_ID_AA64MMFR4_EL1);
|
||||
if (SYS_FIELD_GET(ID_AA64MMFR1_EL1, VH, mmfr1) &&
|
||||
!SYS_FIELD_GET(ID_AA64MMFR4_EL1, E2H0, mmfr4) &&
|
||||
SYS_FIELD_GET(ID_AA64MMFR0_EL1, ECV, mmfr0) &&
|
||||
(has_vhe() || has_hvhe()) &&
|
||||
cpus_have_final_cap(ARM64_WORKAROUND_QCOM_ORYON_CNTVOFF)) {
|
||||
static_branch_enable(&broken_cntvoff_key);
|
||||
kvm_info("Broken CNTVOFF_EL2, trapping virtual timer\n");
|
||||
}
|
||||
}
|
||||
|
||||
int __init kvm_timer_hyp_init(bool has_gic)
|
||||
{
|
||||
struct arch_timer_kvm_info *info;
|
||||
@ -1431,6 +1569,7 @@ int __init kvm_timer_hyp_init(bool has_gic)
|
||||
goto out_free_vtimer_irq;
|
||||
}
|
||||
|
||||
kvm_timer_handle_errata();
|
||||
return 0;
|
||||
|
||||
out_free_ptimer_irq:
|
||||
|
@ -1216,6 +1216,9 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu)
|
||||
if (unlikely(!irqchip_in_kernel(vcpu->kvm)))
|
||||
kvm_timer_sync_user(vcpu);
|
||||
|
||||
if (is_hyp_ctxt(vcpu))
|
||||
kvm_timer_sync_nested(vcpu);
|
||||
|
||||
kvm_arch_vcpu_ctxsync_fp(vcpu);
|
||||
|
||||
/*
|
||||
|
@ -89,6 +89,9 @@ enum cgt_group_id {
|
||||
CGT_HCRX_EnFPM,
|
||||
CGT_HCRX_TCR2En,
|
||||
|
||||
CGT_CNTHCTL_EL1TVT,
|
||||
CGT_CNTHCTL_EL1TVCT,
|
||||
|
||||
CGT_ICH_HCR_TC,
|
||||
CGT_ICH_HCR_TALL0,
|
||||
CGT_ICH_HCR_TALL1,
|
||||
@ -124,6 +127,8 @@ enum cgt_group_id {
|
||||
__COMPLEX_CONDITIONS__,
|
||||
CGT_CNTHCTL_EL1PCTEN = __COMPLEX_CONDITIONS__,
|
||||
CGT_CNTHCTL_EL1PTEN,
|
||||
CGT_CNTHCTL_EL1NVPCT,
|
||||
CGT_CNTHCTL_EL1NVVCT,
|
||||
|
||||
CGT_CPTR_TTA,
|
||||
CGT_MDCR_HPMN,
|
||||
@ -393,6 +398,18 @@ static const struct trap_bits coarse_trap_bits[] = {
|
||||
.mask = HCRX_EL2_TCR2En,
|
||||
.behaviour = BEHAVE_FORWARD_RW,
|
||||
},
|
||||
[CGT_CNTHCTL_EL1TVT] = {
|
||||
.index = CNTHCTL_EL2,
|
||||
.value = CNTHCTL_EL1TVT,
|
||||
.mask = CNTHCTL_EL1TVT,
|
||||
.behaviour = BEHAVE_FORWARD_RW,
|
||||
},
|
||||
[CGT_CNTHCTL_EL1TVCT] = {
|
||||
.index = CNTHCTL_EL2,
|
||||
.value = CNTHCTL_EL1TVCT,
|
||||
.mask = CNTHCTL_EL1TVCT,
|
||||
.behaviour = BEHAVE_FORWARD_READ,
|
||||
},
|
||||
[CGT_ICH_HCR_TC] = {
|
||||
.index = ICH_HCR_EL2,
|
||||
.value = ICH_HCR_TC,
|
||||
@ -487,6 +504,32 @@ static enum trap_behaviour check_cnthctl_el1pten(struct kvm_vcpu *vcpu)
|
||||
return BEHAVE_FORWARD_RW;
|
||||
}
|
||||
|
||||
static bool is_nested_nv2_guest(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
u64 val;
|
||||
|
||||
val = __vcpu_sys_reg(vcpu, HCR_EL2);
|
||||
return ((val & (HCR_E2H | HCR_TGE | HCR_NV2 | HCR_NV1 | HCR_NV)) == (HCR_E2H | HCR_NV2 | HCR_NV));
|
||||
}
|
||||
|
||||
static enum trap_behaviour check_cnthctl_el1nvpct(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
if (!is_nested_nv2_guest(vcpu) ||
|
||||
!(__vcpu_sys_reg(vcpu, CNTHCTL_EL2) & CNTHCTL_EL1NVPCT))
|
||||
return BEHAVE_HANDLE_LOCALLY;
|
||||
|
||||
return BEHAVE_FORWARD_RW;
|
||||
}
|
||||
|
||||
static enum trap_behaviour check_cnthctl_el1nvvct(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
if (!is_nested_nv2_guest(vcpu) ||
|
||||
!(__vcpu_sys_reg(vcpu, CNTHCTL_EL2) & CNTHCTL_EL1NVVCT))
|
||||
return BEHAVE_HANDLE_LOCALLY;
|
||||
|
||||
return BEHAVE_FORWARD_RW;
|
||||
}
|
||||
|
||||
static enum trap_behaviour check_cptr_tta(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
u64 val = __vcpu_sys_reg(vcpu, CPTR_EL2);
|
||||
@ -534,6 +577,8 @@ static enum trap_behaviour check_mdcr_hpmn(struct kvm_vcpu *vcpu)
|
||||
static const complex_condition_check ccc[] = {
|
||||
CCC(CGT_CNTHCTL_EL1PCTEN, check_cnthctl_el1pcten),
|
||||
CCC(CGT_CNTHCTL_EL1PTEN, check_cnthctl_el1pten),
|
||||
CCC(CGT_CNTHCTL_EL1NVPCT, check_cnthctl_el1nvpct),
|
||||
CCC(CGT_CNTHCTL_EL1NVVCT, check_cnthctl_el1nvvct),
|
||||
CCC(CGT_CPTR_TTA, check_cptr_tta),
|
||||
CCC(CGT_MDCR_HPMN, check_mdcr_hpmn),
|
||||
};
|
||||
@ -850,11 +895,15 @@ static const struct encoding_to_trap_config encoding_to_cgt[] __initconst = {
|
||||
SYS_CNTHP_CVAL_EL2, CGT_HCR_NV),
|
||||
SR_RANGE_TRAP(SYS_CNTHV_TVAL_EL2,
|
||||
SYS_CNTHV_CVAL_EL2, CGT_HCR_NV),
|
||||
/* All _EL02, _EL12 registers */
|
||||
/* All _EL02, _EL12 registers up to CNTKCTL_EL12*/
|
||||
SR_RANGE_TRAP(sys_reg(3, 5, 0, 0, 0),
|
||||
sys_reg(3, 5, 10, 15, 7), CGT_HCR_NV),
|
||||
SR_RANGE_TRAP(sys_reg(3, 5, 12, 0, 0),
|
||||
sys_reg(3, 5, 14, 15, 7), CGT_HCR_NV),
|
||||
sys_reg(3, 5, 14, 1, 0), CGT_HCR_NV),
|
||||
SR_TRAP(SYS_CNTP_CTL_EL02, CGT_CNTHCTL_EL1NVPCT),
|
||||
SR_TRAP(SYS_CNTP_CVAL_EL02, CGT_CNTHCTL_EL1NVPCT),
|
||||
SR_TRAP(SYS_CNTV_CTL_EL02, CGT_CNTHCTL_EL1NVVCT),
|
||||
SR_TRAP(SYS_CNTV_CVAL_EL02, CGT_CNTHCTL_EL1NVVCT),
|
||||
SR_TRAP(OP_AT_S1E2R, CGT_HCR_NV),
|
||||
SR_TRAP(OP_AT_S1E2W, CGT_HCR_NV),
|
||||
SR_TRAP(OP_AT_S12E1R, CGT_HCR_NV),
|
||||
@ -1184,6 +1233,11 @@ static const struct encoding_to_trap_config encoding_to_cgt[] __initconst = {
|
||||
SR_TRAP(SYS_CNTP_CTL_EL0, CGT_CNTHCTL_EL1PTEN),
|
||||
SR_TRAP(SYS_CNTPCT_EL0, CGT_CNTHCTL_EL1PCTEN),
|
||||
SR_TRAP(SYS_CNTPCTSS_EL0, CGT_CNTHCTL_EL1PCTEN),
|
||||
SR_TRAP(SYS_CNTV_TVAL_EL0, CGT_CNTHCTL_EL1TVT),
|
||||
SR_TRAP(SYS_CNTV_CVAL_EL0, CGT_CNTHCTL_EL1TVT),
|
||||
SR_TRAP(SYS_CNTV_CTL_EL0, CGT_CNTHCTL_EL1TVT),
|
||||
SR_TRAP(SYS_CNTVCT_EL0, CGT_CNTHCTL_EL1TVCT),
|
||||
SR_TRAP(SYS_CNTVCTSS_EL0, CGT_CNTHCTL_EL1TVCT),
|
||||
SR_TRAP(SYS_FPMR, CGT_HCRX_EnFPM),
|
||||
/*
|
||||
* IMPDEF choice:
|
||||
|
@ -501,7 +501,12 @@ static inline bool handle_tx2_tvm(struct kvm_vcpu *vcpu)
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool kvm_hyp_handle_cntpct(struct kvm_vcpu *vcpu)
|
||||
static inline u64 compute_counter_value(struct arch_timer_context *ctxt)
|
||||
{
|
||||
return arch_timer_read_cntpct_el0() - timer_get_offset(ctxt);
|
||||
}
|
||||
|
||||
static bool kvm_handle_cntxct(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
struct arch_timer_context *ctxt;
|
||||
u32 sysreg;
|
||||
@ -511,18 +516,19 @@ static bool kvm_hyp_handle_cntpct(struct kvm_vcpu *vcpu)
|
||||
* We only get here for 64bit guests, 32bit guests will hit
|
||||
* the long and winding road all the way to the standard
|
||||
* handling. Yes, it sucks to be irrelevant.
|
||||
*
|
||||
* Also, we only deal with non-hypervisor context here (either
|
||||
* an EL1 guest, or a non-HYP context of an EL2 guest).
|
||||
*/
|
||||
if (is_hyp_ctxt(vcpu))
|
||||
return false;
|
||||
|
||||
sysreg = esr_sys64_to_sysreg(kvm_vcpu_get_esr(vcpu));
|
||||
|
||||
switch (sysreg) {
|
||||
case SYS_CNTPCT_EL0:
|
||||
case SYS_CNTPCTSS_EL0:
|
||||
if (vcpu_has_nv(vcpu)) {
|
||||
if (is_hyp_ctxt(vcpu)) {
|
||||
ctxt = vcpu_hptimer(vcpu);
|
||||
break;
|
||||
}
|
||||
|
||||
/* Check for guest hypervisor trapping */
|
||||
val = __vcpu_sys_reg(vcpu, CNTHCTL_EL2);
|
||||
if (!vcpu_el2_e2h_is_set(vcpu))
|
||||
@ -534,16 +540,23 @@ static bool kvm_hyp_handle_cntpct(struct kvm_vcpu *vcpu)
|
||||
|
||||
ctxt = vcpu_ptimer(vcpu);
|
||||
break;
|
||||
case SYS_CNTVCT_EL0:
|
||||
case SYS_CNTVCTSS_EL0:
|
||||
if (vcpu_has_nv(vcpu)) {
|
||||
/* Check for guest hypervisor trapping */
|
||||
val = __vcpu_sys_reg(vcpu, CNTHCTL_EL2);
|
||||
|
||||
if (val & CNTHCTL_EL1TVCT)
|
||||
return false;
|
||||
}
|
||||
|
||||
ctxt = vcpu_vtimer(vcpu);
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
val = arch_timer_read_cntpct_el0();
|
||||
|
||||
if (ctxt->offset.vm_offset)
|
||||
val -= *kern_hyp_va(ctxt->offset.vm_offset);
|
||||
if (ctxt->offset.vcpu_offset)
|
||||
val -= *kern_hyp_va(ctxt->offset.vcpu_offset);
|
||||
val = compute_counter_value(ctxt);
|
||||
|
||||
vcpu_set_reg(vcpu, kvm_vcpu_sys_get_rt(vcpu), val);
|
||||
__kvm_skip_instr(vcpu);
|
||||
@ -588,7 +601,7 @@ static bool kvm_hyp_handle_sysreg(struct kvm_vcpu *vcpu, u64 *exit_code)
|
||||
__vgic_v3_perform_cpuif_access(vcpu) == 1)
|
||||
return true;
|
||||
|
||||
if (kvm_hyp_handle_cntpct(vcpu))
|
||||
if (kvm_handle_cntxct(vcpu))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
|
@ -22,15 +22,16 @@ void __kvm_timer_set_cntvoff(u64 cntvoff)
|
||||
*/
|
||||
void __timer_disable_traps(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
u64 val, shift = 0;
|
||||
u64 set, clr, shift = 0;
|
||||
|
||||
if (has_hvhe())
|
||||
shift = 10;
|
||||
|
||||
/* Allow physical timer/counter access for the host */
|
||||
val = read_sysreg(cnthctl_el2);
|
||||
val |= (CNTHCTL_EL1PCTEN | CNTHCTL_EL1PCEN) << shift;
|
||||
write_sysreg(val, cnthctl_el2);
|
||||
set = (CNTHCTL_EL1PCTEN | CNTHCTL_EL1PCEN) << shift;
|
||||
clr = CNTHCTL_EL1TVT | CNTHCTL_EL1TVCT;
|
||||
|
||||
sysreg_clear_set(cnthctl_el2, clr, set);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -58,5 +59,12 @@ void __timer_enable_traps(struct kvm_vcpu *vcpu)
|
||||
set <<= 10;
|
||||
}
|
||||
|
||||
/*
|
||||
* Trap the virtual counter/timer if we have a broken cntvoff
|
||||
* implementation.
|
||||
*/
|
||||
if (has_broken_cntvoff())
|
||||
set |= CNTHCTL_EL1TVT | CNTHCTL_EL1TVCT;
|
||||
|
||||
sysreg_clear_set(cnthctl_el2, clr, set);
|
||||
}
|
||||
|
@ -256,6 +256,110 @@ void kvm_vcpu_put_vhe(struct kvm_vcpu *vcpu)
|
||||
host_data_ptr(host_ctxt)->__hyp_running_vcpu = NULL;
|
||||
}
|
||||
|
||||
static u64 compute_emulated_cntx_ctl_el0(struct kvm_vcpu *vcpu,
|
||||
enum vcpu_sysreg reg)
|
||||
{
|
||||
unsigned long ctl;
|
||||
u64 cval, cnt;
|
||||
bool stat;
|
||||
|
||||
switch (reg) {
|
||||
case CNTP_CTL_EL0:
|
||||
cval = __vcpu_sys_reg(vcpu, CNTP_CVAL_EL0);
|
||||
ctl = __vcpu_sys_reg(vcpu, CNTP_CTL_EL0);
|
||||
cnt = compute_counter_value(vcpu_ptimer(vcpu));
|
||||
break;
|
||||
case CNTV_CTL_EL0:
|
||||
cval = __vcpu_sys_reg(vcpu, CNTV_CVAL_EL0);
|
||||
ctl = __vcpu_sys_reg(vcpu, CNTV_CTL_EL0);
|
||||
cnt = compute_counter_value(vcpu_vtimer(vcpu));
|
||||
break;
|
||||
default:
|
||||
BUG();
|
||||
}
|
||||
|
||||
stat = cval <= cnt;
|
||||
__assign_bit(__ffs(ARCH_TIMER_CTRL_IT_STAT), &ctl, stat);
|
||||
|
||||
return ctl;
|
||||
}
|
||||
|
||||
static bool kvm_hyp_handle_timer(struct kvm_vcpu *vcpu, u64 *exit_code)
|
||||
{
|
||||
u64 esr, val;
|
||||
|
||||
/*
|
||||
* Having FEAT_ECV allows for a better quality of timer emulation.
|
||||
* However, this comes at a huge cost in terms of traps. Try and
|
||||
* satisfy the reads from guest's hypervisor context without
|
||||
* returning to the kernel if we can.
|
||||
*/
|
||||
if (!is_hyp_ctxt(vcpu))
|
||||
return false;
|
||||
|
||||
esr = kvm_vcpu_get_esr(vcpu);
|
||||
if ((esr & ESR_ELx_SYS64_ISS_DIR_MASK) != ESR_ELx_SYS64_ISS_DIR_READ)
|
||||
return false;
|
||||
|
||||
switch (esr_sys64_to_sysreg(esr)) {
|
||||
case SYS_CNTP_CTL_EL02:
|
||||
val = compute_emulated_cntx_ctl_el0(vcpu, CNTP_CTL_EL0);
|
||||
break;
|
||||
case SYS_CNTP_CTL_EL0:
|
||||
if (vcpu_el2_e2h_is_set(vcpu))
|
||||
val = read_sysreg_el0(SYS_CNTP_CTL);
|
||||
else
|
||||
val = compute_emulated_cntx_ctl_el0(vcpu, CNTP_CTL_EL0);
|
||||
break;
|
||||
case SYS_CNTP_CVAL_EL02:
|
||||
val = __vcpu_sys_reg(vcpu, CNTP_CVAL_EL0);
|
||||
break;
|
||||
case SYS_CNTP_CVAL_EL0:
|
||||
if (vcpu_el2_e2h_is_set(vcpu)) {
|
||||
val = read_sysreg_el0(SYS_CNTP_CVAL);
|
||||
|
||||
if (!has_cntpoff())
|
||||
val -= timer_get_offset(vcpu_hptimer(vcpu));
|
||||
} else {
|
||||
val = __vcpu_sys_reg(vcpu, CNTP_CVAL_EL0);
|
||||
}
|
||||
break;
|
||||
case SYS_CNTPCT_EL0:
|
||||
case SYS_CNTPCTSS_EL0:
|
||||
val = compute_counter_value(vcpu_hptimer(vcpu));
|
||||
break;
|
||||
case SYS_CNTV_CTL_EL02:
|
||||
val = compute_emulated_cntx_ctl_el0(vcpu, CNTV_CTL_EL0);
|
||||
break;
|
||||
case SYS_CNTV_CTL_EL0:
|
||||
if (vcpu_el2_e2h_is_set(vcpu))
|
||||
val = read_sysreg_el0(SYS_CNTV_CTL);
|
||||
else
|
||||
val = compute_emulated_cntx_ctl_el0(vcpu, CNTV_CTL_EL0);
|
||||
break;
|
||||
case SYS_CNTV_CVAL_EL02:
|
||||
val = __vcpu_sys_reg(vcpu, CNTV_CVAL_EL0);
|
||||
break;
|
||||
case SYS_CNTV_CVAL_EL0:
|
||||
if (vcpu_el2_e2h_is_set(vcpu))
|
||||
val = read_sysreg_el0(SYS_CNTV_CVAL);
|
||||
else
|
||||
val = __vcpu_sys_reg(vcpu, CNTV_CVAL_EL0);
|
||||
break;
|
||||
case SYS_CNTVCT_EL0:
|
||||
case SYS_CNTVCTSS_EL0:
|
||||
val = compute_counter_value(vcpu_hvtimer(vcpu));
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
vcpu_set_reg(vcpu, kvm_vcpu_sys_get_rt(vcpu), val);
|
||||
__kvm_skip_instr(vcpu);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool kvm_hyp_handle_eret(struct kvm_vcpu *vcpu, u64 *exit_code)
|
||||
{
|
||||
u64 esr = kvm_vcpu_get_esr(vcpu);
|
||||
@ -409,6 +513,9 @@ static bool kvm_hyp_handle_sysreg_vhe(struct kvm_vcpu *vcpu, u64 *exit_code)
|
||||
if (kvm_hyp_handle_tlbi_el2(vcpu, exit_code))
|
||||
return true;
|
||||
|
||||
if (kvm_hyp_handle_timer(vcpu, exit_code))
|
||||
return true;
|
||||
|
||||
if (kvm_hyp_handle_cpacr_el1(vcpu, exit_code))
|
||||
return true;
|
||||
|
||||
|
@ -1271,6 +1271,21 @@ int kvm_init_nv_sysregs(struct kvm *kvm)
|
||||
res0 |= MDCR_EL2_EnSTEPOP;
|
||||
set_sysreg_masks(kvm, MDCR_EL2, res0, res1);
|
||||
|
||||
/* CNTHCTL_EL2 */
|
||||
res0 = GENMASK(63, 20);
|
||||
res1 = 0;
|
||||
if (!kvm_has_feat(kvm, ID_AA64PFR0_EL1, RME, IMP))
|
||||
res0 |= CNTHCTL_CNTPMASK | CNTHCTL_CNTVMASK;
|
||||
if (!kvm_has_feat(kvm, ID_AA64MMFR0_EL1, ECV, CNTPOFF)) {
|
||||
res0 |= CNTHCTL_ECV;
|
||||
if (!kvm_has_feat(kvm, ID_AA64MMFR0_EL1, ECV, IMP))
|
||||
res0 |= (CNTHCTL_EL1TVT | CNTHCTL_EL1TVCT |
|
||||
CNTHCTL_EL1NVPCT | CNTHCTL_EL1NVVCT);
|
||||
}
|
||||
if (!kvm_has_feat(kvm, ID_AA64MMFR1_EL1, VH, IMP))
|
||||
res0 |= GENMASK(11, 8);
|
||||
set_sysreg_masks(kvm, CNTHCTL_EL2, res0, res1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -1301,26 +1301,146 @@ static bool access_arch_timer(struct kvm_vcpu *vcpu,
|
||||
|
||||
switch (reg) {
|
||||
case SYS_CNTP_TVAL_EL0:
|
||||
case SYS_AARCH32_CNTP_TVAL:
|
||||
if (is_hyp_ctxt(vcpu) && vcpu_el2_e2h_is_set(vcpu))
|
||||
tmr = TIMER_HPTIMER;
|
||||
else
|
||||
tmr = TIMER_PTIMER;
|
||||
treg = TIMER_REG_TVAL;
|
||||
break;
|
||||
|
||||
case SYS_CNTV_TVAL_EL0:
|
||||
if (is_hyp_ctxt(vcpu) && vcpu_el2_e2h_is_set(vcpu))
|
||||
tmr = TIMER_HVTIMER;
|
||||
else
|
||||
tmr = TIMER_VTIMER;
|
||||
treg = TIMER_REG_TVAL;
|
||||
break;
|
||||
|
||||
case SYS_AARCH32_CNTP_TVAL:
|
||||
case SYS_CNTP_TVAL_EL02:
|
||||
tmr = TIMER_PTIMER;
|
||||
treg = TIMER_REG_TVAL;
|
||||
break;
|
||||
|
||||
case SYS_CNTV_TVAL_EL02:
|
||||
tmr = TIMER_VTIMER;
|
||||
treg = TIMER_REG_TVAL;
|
||||
break;
|
||||
|
||||
case SYS_CNTHP_TVAL_EL2:
|
||||
tmr = TIMER_HPTIMER;
|
||||
treg = TIMER_REG_TVAL;
|
||||
break;
|
||||
|
||||
case SYS_CNTHV_TVAL_EL2:
|
||||
tmr = TIMER_HVTIMER;
|
||||
treg = TIMER_REG_TVAL;
|
||||
break;
|
||||
|
||||
case SYS_CNTP_CTL_EL0:
|
||||
case SYS_AARCH32_CNTP_CTL:
|
||||
if (is_hyp_ctxt(vcpu) && vcpu_el2_e2h_is_set(vcpu))
|
||||
tmr = TIMER_HPTIMER;
|
||||
else
|
||||
tmr = TIMER_PTIMER;
|
||||
treg = TIMER_REG_CTL;
|
||||
break;
|
||||
|
||||
case SYS_CNTV_CTL_EL0:
|
||||
if (is_hyp_ctxt(vcpu) && vcpu_el2_e2h_is_set(vcpu))
|
||||
tmr = TIMER_HVTIMER;
|
||||
else
|
||||
tmr = TIMER_VTIMER;
|
||||
treg = TIMER_REG_CTL;
|
||||
break;
|
||||
|
||||
case SYS_AARCH32_CNTP_CTL:
|
||||
case SYS_CNTP_CTL_EL02:
|
||||
tmr = TIMER_PTIMER;
|
||||
treg = TIMER_REG_CTL;
|
||||
break;
|
||||
|
||||
case SYS_CNTV_CTL_EL02:
|
||||
tmr = TIMER_VTIMER;
|
||||
treg = TIMER_REG_CTL;
|
||||
break;
|
||||
|
||||
case SYS_CNTHP_CTL_EL2:
|
||||
tmr = TIMER_HPTIMER;
|
||||
treg = TIMER_REG_CTL;
|
||||
break;
|
||||
|
||||
case SYS_CNTHV_CTL_EL2:
|
||||
tmr = TIMER_HVTIMER;
|
||||
treg = TIMER_REG_CTL;
|
||||
break;
|
||||
|
||||
case SYS_CNTP_CVAL_EL0:
|
||||
case SYS_AARCH32_CNTP_CVAL:
|
||||
if (is_hyp_ctxt(vcpu) && vcpu_el2_e2h_is_set(vcpu))
|
||||
tmr = TIMER_HPTIMER;
|
||||
else
|
||||
tmr = TIMER_PTIMER;
|
||||
treg = TIMER_REG_CVAL;
|
||||
break;
|
||||
|
||||
case SYS_CNTV_CVAL_EL0:
|
||||
if (is_hyp_ctxt(vcpu) && vcpu_el2_e2h_is_set(vcpu))
|
||||
tmr = TIMER_HVTIMER;
|
||||
else
|
||||
tmr = TIMER_VTIMER;
|
||||
treg = TIMER_REG_CVAL;
|
||||
break;
|
||||
|
||||
case SYS_AARCH32_CNTP_CVAL:
|
||||
case SYS_CNTP_CVAL_EL02:
|
||||
tmr = TIMER_PTIMER;
|
||||
treg = TIMER_REG_CVAL;
|
||||
break;
|
||||
|
||||
case SYS_CNTV_CVAL_EL02:
|
||||
tmr = TIMER_VTIMER;
|
||||
treg = TIMER_REG_CVAL;
|
||||
break;
|
||||
|
||||
case SYS_CNTHP_CVAL_EL2:
|
||||
tmr = TIMER_HPTIMER;
|
||||
treg = TIMER_REG_CVAL;
|
||||
break;
|
||||
|
||||
case SYS_CNTHV_CVAL_EL2:
|
||||
tmr = TIMER_HVTIMER;
|
||||
treg = TIMER_REG_CVAL;
|
||||
break;
|
||||
|
||||
case SYS_CNTPCT_EL0:
|
||||
case SYS_CNTPCTSS_EL0:
|
||||
case SYS_AARCH32_CNTPCT:
|
||||
if (is_hyp_ctxt(vcpu))
|
||||
tmr = TIMER_HPTIMER;
|
||||
else
|
||||
tmr = TIMER_PTIMER;
|
||||
treg = TIMER_REG_CNT;
|
||||
break;
|
||||
|
||||
case SYS_AARCH32_CNTPCT:
|
||||
case SYS_AARCH32_CNTPCTSS:
|
||||
tmr = TIMER_PTIMER;
|
||||
treg = TIMER_REG_CNT;
|
||||
break;
|
||||
|
||||
case SYS_CNTVCT_EL0:
|
||||
case SYS_CNTVCTSS_EL0:
|
||||
if (is_hyp_ctxt(vcpu))
|
||||
tmr = TIMER_HVTIMER;
|
||||
else
|
||||
tmr = TIMER_VTIMER;
|
||||
treg = TIMER_REG_CNT;
|
||||
break;
|
||||
|
||||
case SYS_AARCH32_CNTVCT:
|
||||
case SYS_AARCH32_CNTVCTSS:
|
||||
tmr = TIMER_VTIMER;
|
||||
treg = TIMER_REG_CNT;
|
||||
break;
|
||||
|
||||
default:
|
||||
print_sys_reg_msg(p, "%s", "Unhandled trapped timer register");
|
||||
return undef_access(vcpu, p, r);
|
||||
@ -1490,7 +1610,8 @@ static u64 __kvm_read_sanitised_id_reg(const struct kvm_vcpu *vcpu,
|
||||
if (!vcpu_has_ptrauth(vcpu))
|
||||
val &= ~(ARM64_FEATURE_MASK(ID_AA64ISAR2_EL1_APA3) |
|
||||
ARM64_FEATURE_MASK(ID_AA64ISAR2_EL1_GPA3));
|
||||
if (!cpus_have_final_cap(ARM64_HAS_WFXT))
|
||||
if (!cpus_have_final_cap(ARM64_HAS_WFXT) ||
|
||||
has_broken_cntvoff())
|
||||
val &= ~ARM64_FEATURE_MASK(ID_AA64ISAR2_EL1_WFxT);
|
||||
break;
|
||||
case SYS_ID_AA64MMFR2_EL1:
|
||||
@ -2791,11 +2912,17 @@ static const struct sys_reg_desc sys_reg_descs[] = {
|
||||
AMU_AMEVTYPER1_EL0(15),
|
||||
|
||||
{ SYS_DESC(SYS_CNTPCT_EL0), access_arch_timer },
|
||||
{ SYS_DESC(SYS_CNTVCT_EL0), access_arch_timer },
|
||||
{ SYS_DESC(SYS_CNTPCTSS_EL0), access_arch_timer },
|
||||
{ SYS_DESC(SYS_CNTVCTSS_EL0), access_arch_timer },
|
||||
{ SYS_DESC(SYS_CNTP_TVAL_EL0), access_arch_timer },
|
||||
{ SYS_DESC(SYS_CNTP_CTL_EL0), access_arch_timer },
|
||||
{ SYS_DESC(SYS_CNTP_CVAL_EL0), access_arch_timer },
|
||||
|
||||
{ SYS_DESC(SYS_CNTV_TVAL_EL0), access_arch_timer },
|
||||
{ SYS_DESC(SYS_CNTV_CTL_EL0), access_arch_timer },
|
||||
{ SYS_DESC(SYS_CNTV_CVAL_EL0), access_arch_timer },
|
||||
|
||||
/* PMEVCNTRn_EL0 */
|
||||
PMU_PMEVCNTR_EL0(0),
|
||||
PMU_PMEVCNTR_EL0(1),
|
||||
@ -2947,9 +3074,24 @@ static const struct sys_reg_desc sys_reg_descs[] = {
|
||||
|
||||
EL2_REG_VNCR(CNTVOFF_EL2, reset_val, 0),
|
||||
EL2_REG(CNTHCTL_EL2, access_rw, reset_val, 0),
|
||||
{ SYS_DESC(SYS_CNTHP_TVAL_EL2), access_arch_timer },
|
||||
EL2_REG(CNTHP_CTL_EL2, access_arch_timer, reset_val, 0),
|
||||
EL2_REG(CNTHP_CVAL_EL2, access_arch_timer, reset_val, 0),
|
||||
|
||||
{ SYS_DESC(SYS_CNTHV_TVAL_EL2), access_arch_timer },
|
||||
EL2_REG(CNTHV_CTL_EL2, access_arch_timer, reset_val, 0),
|
||||
EL2_REG(CNTHV_CVAL_EL2, access_arch_timer, reset_val, 0),
|
||||
|
||||
{ SYS_DESC(SYS_CNTKCTL_EL12), access_cntkctl_el12 },
|
||||
|
||||
{ SYS_DESC(SYS_CNTP_TVAL_EL02), access_arch_timer },
|
||||
{ SYS_DESC(SYS_CNTP_CTL_EL02), access_arch_timer },
|
||||
{ SYS_DESC(SYS_CNTP_CVAL_EL02), access_arch_timer },
|
||||
|
||||
{ SYS_DESC(SYS_CNTV_TVAL_EL02), access_arch_timer },
|
||||
{ SYS_DESC(SYS_CNTV_CTL_EL02), access_arch_timer },
|
||||
{ SYS_DESC(SYS_CNTV_CVAL_EL02), access_arch_timer },
|
||||
|
||||
EL2_REG(SP_EL2, NULL, reset_unknown, 0),
|
||||
};
|
||||
|
||||
@ -3771,9 +3913,11 @@ static const struct sys_reg_desc cp15_64_regs[] = {
|
||||
{ SYS_DESC(SYS_AARCH32_CNTPCT), access_arch_timer },
|
||||
{ Op1( 1), CRn( 0), CRm( 2), Op2( 0), access_vm_reg, NULL, TTBR1_EL1 },
|
||||
{ Op1( 1), CRn( 0), CRm(12), Op2( 0), access_gic_sgi }, /* ICC_ASGI1R */
|
||||
{ SYS_DESC(SYS_AARCH32_CNTVCT), access_arch_timer },
|
||||
{ Op1( 2), CRn( 0), CRm(12), Op2( 0), access_gic_sgi }, /* ICC_SGI0R */
|
||||
{ SYS_DESC(SYS_AARCH32_CNTP_CVAL), access_arch_timer },
|
||||
{ SYS_DESC(SYS_AARCH32_CNTPCTSS), access_arch_timer },
|
||||
{ SYS_DESC(SYS_AARCH32_CNTVCTSS), access_arch_timer },
|
||||
};
|
||||
|
||||
static bool check_sysreg_table(const struct sys_reg_desc *table, unsigned int n,
|
||||
|
@ -105,6 +105,7 @@ WORKAROUND_CLEAN_CACHE
|
||||
WORKAROUND_DEVICE_LOAD_ACQUIRE
|
||||
WORKAROUND_NVIDIA_CARMEL_CNP
|
||||
WORKAROUND_QCOM_FALKOR_E1003
|
||||
WORKAROUND_QCOM_ORYON_CNTVOFF
|
||||
WORKAROUND_REPEAT_TLBI
|
||||
WORKAROUND_SPECULATIVE_AT
|
||||
WORKAROUND_SPECULATIVE_SSBS
|
||||
|
@ -22,6 +22,12 @@
|
||||
#define CNTHCTL_EVNTDIR (1 << 3)
|
||||
#define CNTHCTL_EVNTI (0xF << 4)
|
||||
#define CNTHCTL_ECV (1 << 12)
|
||||
#define CNTHCTL_EL1TVT (1 << 13)
|
||||
#define CNTHCTL_EL1TVCT (1 << 14)
|
||||
#define CNTHCTL_EL1NVPCT (1 << 15)
|
||||
#define CNTHCTL_EL1NVVCT (1 << 16)
|
||||
#define CNTHCTL_CNTVMASK (1 << 18)
|
||||
#define CNTHCTL_CNTPMASK (1 << 19)
|
||||
|
||||
enum arch_timer_reg {
|
||||
ARCH_TIMER_REG_CTRL,
|
||||
|
@ -98,6 +98,7 @@ int __init kvm_timer_hyp_init(bool has_gic);
|
||||
int kvm_timer_enable(struct kvm_vcpu *vcpu);
|
||||
void kvm_timer_vcpu_reset(struct kvm_vcpu *vcpu);
|
||||
void kvm_timer_vcpu_init(struct kvm_vcpu *vcpu);
|
||||
void kvm_timer_sync_nested(struct kvm_vcpu *vcpu);
|
||||
void kvm_timer_sync_user(struct kvm_vcpu *vcpu);
|
||||
bool kvm_timer_should_notify_user(struct kvm_vcpu *vcpu);
|
||||
void kvm_timer_update_run(struct kvm_vcpu *vcpu);
|
||||
@ -150,9 +151,31 @@ void kvm_timer_cpu_down(void);
|
||||
/* CNTKCTL_EL1 valid bits as of DDI0487J.a */
|
||||
#define CNTKCTL_VALID_BITS (BIT(17) | GENMASK_ULL(9, 0))
|
||||
|
||||
DECLARE_STATIC_KEY_FALSE(broken_cntvoff_key);
|
||||
|
||||
static inline bool has_broken_cntvoff(void)
|
||||
{
|
||||
return static_branch_unlikely(&broken_cntvoff_key);
|
||||
}
|
||||
|
||||
static inline bool has_cntpoff(void)
|
||||
{
|
||||
return (has_vhe() && cpus_have_final_cap(ARM64_HAS_ECV_CNTPOFF));
|
||||
}
|
||||
|
||||
static inline u64 timer_get_offset(struct arch_timer_context *ctxt)
|
||||
{
|
||||
u64 offset = 0;
|
||||
|
||||
if (!ctxt)
|
||||
return 0;
|
||||
|
||||
if (ctxt->offset.vm_offset)
|
||||
offset += *ctxt->offset.vm_offset;
|
||||
if (ctxt->offset.vcpu_offset)
|
||||
offset += *ctxt->offset.vcpu_offset;
|
||||
|
||||
return offset;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
Loading…
Reference in New Issue
Block a user