Skip to content

Commit 2cd2a77

Browse files
author
Marc Zyngier
committed
KVM: arm64: nv: Use FEAT_ECV to trap access to EL0 timers
Although FEAT_NV2 makes most things fast, it also makes it impossible to correctly emulate the timers, as the sysreg accesses are redirected to memory. FEAT_ECV addresses this by giving a hypervisor the ability to trap the EL02 sysregs as well as the virtual timer. Add the required trap setting to make use of the feature, allowing us to elide the ugly resync in the middle of the run loop. Acked-by: Oliver Upton <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Marc Zyngier <[email protected]>
1 parent cc45963 commit 2cd2a77

File tree

2 files changed

+35
-3
lines changed

2 files changed

+35
-3
lines changed

arch/arm64/kvm/arch_timer.c

Lines changed: 33 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -783,7 +783,7 @@ static void kvm_timer_vcpu_load_nested_switch(struct kvm_vcpu *vcpu,
783783

784784
static void timer_set_traps(struct kvm_vcpu *vcpu, struct timer_map *map)
785785
{
786-
bool tpt, tpc;
786+
bool tvt, tpt, tvc, tpc, tvt02, tpt02;
787787
u64 clr, set;
788788

789789
/*
@@ -798,7 +798,29 @@ static void timer_set_traps(struct kvm_vcpu *vcpu, struct timer_map *map)
798798
* within this function, reality kicks in and we start adding
799799
* traps based on emulation requirements.
800800
*/
801-
tpt = tpc = false;
801+
tvt = tpt = tvc = tpc = false;
802+
tvt02 = tpt02 = false;
803+
804+
/*
805+
* NV2 badly breaks the timer semantics by redirecting accesses to
806+
* the EL1 timer state to memory, so let's call ECV to the rescue if
807+
* available: we trap all CNT{P,V}_{CTL,CVAL,TVAL}_EL0 accesses.
808+
*
809+
* The treatment slightly varies depending whether we run a nVHE or
810+
* VHE guest: nVHE will use the _EL0 registers directly, while VHE
811+
* will use the _EL02 accessors. This translates in different trap
812+
* bits.
813+
*
814+
* None of the trapping is required when running in non-HYP context,
815+
* unless required by the L1 hypervisor settings once we advertise
816+
* ECV+NV in the guest, or that we need trapping for other reasons.
817+
*/
818+
if (cpus_have_final_cap(ARM64_HAS_ECV) && is_hyp_ctxt(vcpu)) {
819+
if (vcpu_el2_e2h_is_set(vcpu))
820+
tvt02 = tpt02 = true;
821+
else
822+
tvt = tpt = true;
823+
}
802824

803825
/*
804826
* We have two possibility to deal with a physical offset:
@@ -838,6 +860,10 @@ static void timer_set_traps(struct kvm_vcpu *vcpu, struct timer_map *map)
838860

839861
assign_clear_set_bit(tpt, CNTHCTL_EL1PCEN << 10, set, clr);
840862
assign_clear_set_bit(tpc, CNTHCTL_EL1PCTEN << 10, set, clr);
863+
assign_clear_set_bit(tvt, CNTHCTL_EL1TVT, clr, set);
864+
assign_clear_set_bit(tvc, CNTHCTL_EL1TVCT, clr, set);
865+
assign_clear_set_bit(tvt02, CNTHCTL_EL1NVVCT, clr, set);
866+
assign_clear_set_bit(tpt02, CNTHCTL_EL1NVPCT, clr, set);
841867

842868
/* This only happens on VHE, so use the CNTHCTL_EL2 accessor. */
843869
sysreg_clear_set(cnthctl_el2, clr, set);
@@ -933,8 +959,12 @@ void kvm_timer_sync_nested(struct kvm_vcpu *vcpu)
933959
* accesses redirected to the VNCR page. Any guest action taken on
934960
* the timer is postponed until the next exit, leading to a very
935961
* poor quality of emulation.
962+
*
963+
* This is an unmitigated disaster, only papered over by FEAT_ECV,
964+
* which allows trapping of the timer registers even with NV2.
965+
* Still, this is still worse than FEAT_NV on its own. Meh.
936966
*/
937-
if (!is_hyp_ctxt(vcpu))
967+
if (cpus_have_final_cap(ARM64_HAS_ECV) || !is_hyp_ctxt(vcpu))
938968
return;
939969

940970
if (!vcpu_el2_e2h_is_set(vcpu)) {

include/clocksource/arm_arch_timer.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@
2424
#define CNTHCTL_ECV (1 << 12)
2525
#define CNTHCTL_EL1TVT (1 << 13)
2626
#define CNTHCTL_EL1TVCT (1 << 14)
27+
#define CNTHCTL_EL1NVPCT (1 << 15)
28+
#define CNTHCTL_EL1NVVCT (1 << 16)
2729

2830
enum arch_timer_reg {
2931
ARCH_TIMER_REG_CTRL,

0 commit comments

Comments
 (0)