Skip to content

Commit 36fe1b2

Browse files
author
Marc Zyngier
committed
Merge branch kvm-arm64/spec-ptw into kvmarm-master/next
* kvm-arm64/spec-ptw: : . : On taking an exception from EL1&0 to EL2(&0), the page table walker is : allowed to carry on with speculative walks started from EL1&0 while : running at EL2 (see R_LFHQG). Given that the PTW may be actively using : the EL1&0 system registers, the only safe way to deal with it is to : issue a DSB before changing any of it. : : We already did the right thing for SPE and TRBE, but ignored the PTW : for unknown reasons (probably because the architecture wasn't crystal : clear at the time). : : This requires a bit of surgery in the nvhe code, though most of these : patches are comments so that my future self can understand the purpose : of these barriers. The VHE code is largely unaffected, thanks to the : DSB in the context switch. : . KVM: arm64: vhe: Drop extra isb() on guest exit KVM: arm64: vhe: Synchronise with page table walker on MMU update KVM: arm64: pkvm: Document the side effects of kvm_flush_dcache_to_poc() KVM: arm64: nvhe: Synchronise with page table walker on TLBI KVM: arm64: nvhe: Synchronise with page table walker on vcpu run Signed-off-by: Marc Zyngier <maz@kernel.org>
2 parents 6dcf731 + bcf3e7d commit 36fe1b2

6 files changed

Lines changed: 69 additions & 15 deletions

File tree

arch/arm64/kvm/hyp/nvhe/debug-sr.c

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,6 @@ static void __debug_save_spe(u64 *pmscr_el1)
3737

3838
/* Now drain all buffered data to memory */
3939
psb_csync();
40-
dsb(nsh);
4140
}
4241

4342
static void __debug_restore_spe(u64 pmscr_el1)
@@ -69,7 +68,6 @@ static void __debug_save_trace(u64 *trfcr_el1)
6968
isb();
7069
/* Drain the trace buffer to memory */
7170
tsb_csync();
72-
dsb(nsh);
7371
}
7472

7573
static void __debug_restore_trace(u64 trfcr_el1)

arch/arm64/kvm/hyp/nvhe/mem_protect.c

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -297,6 +297,13 @@ int __pkvm_prot_finalize(void)
297297
params->vttbr = kvm_get_vttbr(mmu);
298298
params->vtcr = host_mmu.arch.vtcr;
299299
params->hcr_el2 |= HCR_VM;
300+
301+
/*
302+
* The CMO below not only cleans the updated params to the
303+
* PoC, but also provides the DSB that ensures ongoing
304+
* page-table walks that have started before we trapped to EL2
305+
* have completed.
306+
*/
300307
kvm_flush_dcache_to_poc(params, sizeof(*params));
301308

302309
write_sysreg(params->hcr_el2, hcr_el2);

arch/arm64/kvm/hyp/nvhe/switch.c

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -272,6 +272,17 @@ int __kvm_vcpu_run(struct kvm_vcpu *vcpu)
272272
*/
273273
__debug_save_host_buffers_nvhe(vcpu);
274274

275+
/*
276+
* We're about to restore some new MMU state. Make sure
277+
* ongoing page-table walks that have started before we
278+
* trapped to EL2 have completed. This also synchronises the
279+
* above disabling of SPE and TRBE.
280+
*
281+
* See DDI0487I.a D8.1.5 "Out-of-context translation regimes",
282+
* rule R_LFHQG and subsequent information statements.
283+
*/
284+
dsb(nsh);
285+
275286
__kvm_adjust_pc(vcpu);
276287

277288
/*
@@ -306,6 +317,13 @@ int __kvm_vcpu_run(struct kvm_vcpu *vcpu)
306317
__timer_disable_traps(vcpu);
307318
__hyp_vgic_save_state(vcpu);
308319

320+
/*
321+
* Same thing as before the guest run: we're about to switch
322+
* the MMU context, so let's make sure we don't have any
323+
* ongoing EL1&0 translations.
324+
*/
325+
dsb(nsh);
326+
309327
__deactivate_traps(vcpu);
310328
__load_host_stage2();
311329

arch/arm64/kvm/hyp/nvhe/tlb.c

Lines changed: 29 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,31 @@ struct tlb_inv_context {
1515
};
1616

1717
static void __tlb_switch_to_guest(struct kvm_s2_mmu *mmu,
18-
struct tlb_inv_context *cxt)
18+
struct tlb_inv_context *cxt,
19+
bool nsh)
1920
{
21+
/*
22+
* We have two requirements:
23+
*
24+
* - ensure that the page table updates are visible to all
25+
* CPUs, for which a dsb(DOMAIN-st) is what we need, DOMAIN
26+
* being either ish or nsh, depending on the invalidation
27+
* type.
28+
*
29+
* - complete any speculative page table walk started before
30+
* we trapped to EL2 so that we can mess with the MM
31+
* registers out of context, for which dsb(nsh) is enough
32+
*
33+
* The composition of these two barriers is a dsb(DOMAIN), and
34+
* the 'nsh' parameter tracks the distinction between
35+
* Inner-Shareable and Non-Shareable, as specified by the
36+
* callers.
37+
*/
38+
if (nsh)
39+
dsb(nsh);
40+
else
41+
dsb(ish);
42+
2043
if (cpus_have_final_cap(ARM64_WORKAROUND_SPECULATIVE_AT)) {
2144
u64 val;
2245

@@ -60,10 +83,8 @@ void __kvm_tlb_flush_vmid_ipa(struct kvm_s2_mmu *mmu,
6083
{
6184
struct tlb_inv_context cxt;
6285

63-
dsb(ishst);
64-
6586
/* Switch to requested VMID */
66-
__tlb_switch_to_guest(mmu, &cxt);
87+
__tlb_switch_to_guest(mmu, &cxt, false);
6788

6889
/*
6990
* We could do so much better if we had the VA as well.
@@ -113,10 +134,8 @@ void __kvm_tlb_flush_vmid(struct kvm_s2_mmu *mmu)
113134
{
114135
struct tlb_inv_context cxt;
115136

116-
dsb(ishst);
117-
118137
/* Switch to requested VMID */
119-
__tlb_switch_to_guest(mmu, &cxt);
138+
__tlb_switch_to_guest(mmu, &cxt, false);
120139

121140
__tlbi(vmalls12e1is);
122141
dsb(ish);
@@ -130,7 +149,7 @@ void __kvm_flush_cpu_context(struct kvm_s2_mmu *mmu)
130149
struct tlb_inv_context cxt;
131150

132151
/* Switch to requested VMID */
133-
__tlb_switch_to_guest(mmu, &cxt);
152+
__tlb_switch_to_guest(mmu, &cxt, false);
134153

135154
__tlbi(vmalle1);
136155
asm volatile("ic iallu");
@@ -142,7 +161,8 @@ void __kvm_flush_cpu_context(struct kvm_s2_mmu *mmu)
142161

143162
void __kvm_flush_vm_context(void)
144163
{
145-
dsb(ishst);
164+
/* Same remark as in __tlb_switch_to_guest() */
165+
dsb(ish);
146166
__tlbi(alle1is);
147167

148168
/*

arch/arm64/kvm/hyp/vhe/switch.c

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -227,11 +227,10 @@ int __kvm_vcpu_run(struct kvm_vcpu *vcpu)
227227

228228
/*
229229
* When we exit from the guest we change a number of CPU configuration
230-
* parameters, such as traps. Make sure these changes take effect
231-
* before running the host or additional guests.
230+
* parameters, such as traps. We rely on the isb() in kvm_call_hyp*()
231+
* to make sure these changes take effect before running the host or
232+
* additional guests.
232233
*/
233-
isb();
234-
235234
return ret;
236235
}
237236

arch/arm64/kvm/hyp/vhe/sysreg-sr.c

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#include <asm/kvm_asm.h>
1414
#include <asm/kvm_emulate.h>
1515
#include <asm/kvm_hyp.h>
16+
#include <asm/kvm_nested.h>
1617

1718
/*
1819
* VHE: Host and guest must save mdscr_el1 and sp_el0 (and the PC and
@@ -69,6 +70,17 @@ void kvm_vcpu_load_sysregs_vhe(struct kvm_vcpu *vcpu)
6970
host_ctxt = &this_cpu_ptr(&kvm_host_data)->host_ctxt;
7071
__sysreg_save_user_state(host_ctxt);
7172

73+
/*
74+
* When running a normal EL1 guest, we only load a new vcpu
75+
* after a context switch, which imvolves a DSB, so all
76+
* speculative EL1&0 walks will have already completed.
77+
* If running NV, the vcpu may transition between vEL1 and
78+
* vEL2 without a context switch, so make sure we complete
79+
* those walks before loading a new context.
80+
*/
81+
if (vcpu_has_nv(vcpu))
82+
dsb(nsh);
83+
7284
/*
7385
* Load guest EL1 and user state
7486
*

0 commit comments

Comments
 (0)