Skip to content

Commit b9527b3

Browse files
Marc Zyngieroupton
authored andcommitted
KVM: arm64: nv: Save/Restore vEL2 sysregs
Whenever we need to restore the guest's system registers to the CPU, we now need to take care of the EL2 system registers as well. Most of them are accessed via traps only, but some have an immediate effect and also a guest running in VHE mode would expect them to be accessible via their EL1 encoding, which we do not trap. For vEL2 we write the virtual EL2 registers with an identical format directly into their EL1 counterpart, and translate the few registers that have a different format for the same effect on the execution when running a non-VHE guest guest hypervisor. Based on an initial patch from Andre Przywara, rewritten many times since. Reviewed-by: Alexandru Elisei <[email protected]> Signed-off-by: Marc Zyngier <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Oliver Upton <[email protected]>
1 parent 164b5e2 commit b9527b3

File tree

3 files changed

+138
-5
lines changed

3 files changed

+138
-5
lines changed

arch/arm64/kvm/hyp/include/hyp/sysreg-sr.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -152,9 +152,10 @@ static inline void __sysreg_restore_user_state(struct kvm_cpu_context *ctxt)
152152
write_sysreg(ctxt_sys_reg(ctxt, TPIDRRO_EL0), tpidrro_el0);
153153
}
154154

155-
static inline void __sysreg_restore_el1_state(struct kvm_cpu_context *ctxt)
155+
static inline void __sysreg_restore_el1_state(struct kvm_cpu_context *ctxt,
156+
u64 mpidr)
156157
{
157-
write_sysreg(ctxt_sys_reg(ctxt, MPIDR_EL1), vmpidr_el2);
158+
write_sysreg(mpidr, vmpidr_el2);
158159

159160
if (has_vhe() ||
160161
!cpus_have_final_cap(ARM64_WORKAROUND_SPECULATIVE_AT)) {

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ void __sysreg_save_state_nvhe(struct kvm_cpu_context *ctxt)
2828

2929
void __sysreg_restore_state_nvhe(struct kvm_cpu_context *ctxt)
3030
{
31-
__sysreg_restore_el1_state(ctxt);
31+
__sysreg_restore_el1_state(ctxt, ctxt_sys_reg(ctxt, MPIDR_EL1));
3232
__sysreg_restore_common_state(ctxt);
3333
__sysreg_restore_user_state(ctxt);
3434
__sysreg_restore_el2_return_state(ctxt);

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

Lines changed: 134 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,107 @@
1515
#include <asm/kvm_hyp.h>
1616
#include <asm/kvm_nested.h>
1717

18+
static void __sysreg_save_vel2_state(struct kvm_vcpu *vcpu)
19+
{
20+
/* These registers are common with EL1 */
21+
__vcpu_sys_reg(vcpu, PAR_EL1) = read_sysreg(par_el1);
22+
__vcpu_sys_reg(vcpu, TPIDR_EL1) = read_sysreg(tpidr_el1);
23+
24+
__vcpu_sys_reg(vcpu, ESR_EL2) = read_sysreg_el1(SYS_ESR);
25+
__vcpu_sys_reg(vcpu, AFSR0_EL2) = read_sysreg_el1(SYS_AFSR0);
26+
__vcpu_sys_reg(vcpu, AFSR1_EL2) = read_sysreg_el1(SYS_AFSR1);
27+
__vcpu_sys_reg(vcpu, FAR_EL2) = read_sysreg_el1(SYS_FAR);
28+
__vcpu_sys_reg(vcpu, MAIR_EL2) = read_sysreg_el1(SYS_MAIR);
29+
__vcpu_sys_reg(vcpu, VBAR_EL2) = read_sysreg_el1(SYS_VBAR);
30+
__vcpu_sys_reg(vcpu, CONTEXTIDR_EL2) = read_sysreg_el1(SYS_CONTEXTIDR);
31+
__vcpu_sys_reg(vcpu, AMAIR_EL2) = read_sysreg_el1(SYS_AMAIR);
32+
33+
/*
34+
* In VHE mode those registers are compatible between EL1 and EL2,
35+
* and the guest uses the _EL1 versions on the CPU naturally.
36+
* So we save them into their _EL2 versions here.
37+
* For nVHE mode we trap accesses to those registers, so our
38+
* _EL2 copy in sys_regs[] is always up-to-date and we don't need
39+
* to save anything here.
40+
*/
41+
if (vcpu_el2_e2h_is_set(vcpu)) {
42+
u64 val;
43+
44+
/*
45+
* We don't save CPTR_EL2, as accesses to CPACR_EL1
46+
* are always trapped, ensuring that the in-memory
47+
* copy is always up-to-date. A small blessing...
48+
*/
49+
__vcpu_sys_reg(vcpu, SCTLR_EL2) = read_sysreg_el1(SYS_SCTLR);
50+
__vcpu_sys_reg(vcpu, TTBR0_EL2) = read_sysreg_el1(SYS_TTBR0);
51+
__vcpu_sys_reg(vcpu, TTBR1_EL2) = read_sysreg_el1(SYS_TTBR1);
52+
__vcpu_sys_reg(vcpu, TCR_EL2) = read_sysreg_el1(SYS_TCR);
53+
54+
/*
55+
* The EL1 view of CNTKCTL_EL1 has a bunch of RES0 bits where
56+
* the interesting CNTHCTL_EL2 bits live. So preserve these
57+
* bits when reading back the guest-visible value.
58+
*/
59+
val = read_sysreg_el1(SYS_CNTKCTL);
60+
val &= CNTKCTL_VALID_BITS;
61+
__vcpu_sys_reg(vcpu, CNTHCTL_EL2) &= ~CNTKCTL_VALID_BITS;
62+
__vcpu_sys_reg(vcpu, CNTHCTL_EL2) |= val;
63+
}
64+
65+
__vcpu_sys_reg(vcpu, SP_EL2) = read_sysreg(sp_el1);
66+
__vcpu_sys_reg(vcpu, ELR_EL2) = read_sysreg_el1(SYS_ELR);
67+
__vcpu_sys_reg(vcpu, SPSR_EL2) = read_sysreg_el1(SYS_SPSR);
68+
}
69+
70+
static void __sysreg_restore_vel2_state(struct kvm_vcpu *vcpu)
71+
{
72+
u64 val;
73+
74+
/* These registers are common with EL1 */
75+
write_sysreg(__vcpu_sys_reg(vcpu, PAR_EL1), par_el1);
76+
write_sysreg(__vcpu_sys_reg(vcpu, TPIDR_EL1), tpidr_el1);
77+
78+
write_sysreg(__vcpu_sys_reg(vcpu, MPIDR_EL1), vmpidr_el2);
79+
write_sysreg_el1(__vcpu_sys_reg(vcpu, MAIR_EL2), SYS_MAIR);
80+
write_sysreg_el1(__vcpu_sys_reg(vcpu, VBAR_EL2), SYS_VBAR);
81+
write_sysreg_el1(__vcpu_sys_reg(vcpu, CONTEXTIDR_EL2), SYS_CONTEXTIDR);
82+
write_sysreg_el1(__vcpu_sys_reg(vcpu, AMAIR_EL2), SYS_AMAIR);
83+
84+
if (vcpu_el2_e2h_is_set(vcpu)) {
85+
/*
86+
* In VHE mode those registers are compatible between
87+
* EL1 and EL2.
88+
*/
89+
write_sysreg_el1(__vcpu_sys_reg(vcpu, SCTLR_EL2), SYS_SCTLR);
90+
write_sysreg_el1(__vcpu_sys_reg(vcpu, CPTR_EL2), SYS_CPACR);
91+
write_sysreg_el1(__vcpu_sys_reg(vcpu, TTBR0_EL2), SYS_TTBR0);
92+
write_sysreg_el1(__vcpu_sys_reg(vcpu, TTBR1_EL2), SYS_TTBR1);
93+
write_sysreg_el1(__vcpu_sys_reg(vcpu, TCR_EL2), SYS_TCR);
94+
write_sysreg_el1(__vcpu_sys_reg(vcpu, CNTHCTL_EL2), SYS_CNTKCTL);
95+
} else {
96+
/*
97+
* CNTHCTL_EL2 only affects EL1 when running nVHE, so
98+
* no need to restore it.
99+
*/
100+
val = translate_sctlr_el2_to_sctlr_el1(__vcpu_sys_reg(vcpu, SCTLR_EL2));
101+
write_sysreg_el1(val, SYS_SCTLR);
102+
val = translate_cptr_el2_to_cpacr_el1(__vcpu_sys_reg(vcpu, CPTR_EL2));
103+
write_sysreg_el1(val, SYS_CPACR);
104+
val = translate_ttbr0_el2_to_ttbr0_el1(__vcpu_sys_reg(vcpu, TTBR0_EL2));
105+
write_sysreg_el1(val, SYS_TTBR0);
106+
val = translate_tcr_el2_to_tcr_el1(__vcpu_sys_reg(vcpu, TCR_EL2));
107+
write_sysreg_el1(val, SYS_TCR);
108+
}
109+
110+
write_sysreg_el1(__vcpu_sys_reg(vcpu, ESR_EL2), SYS_ESR);
111+
write_sysreg_el1(__vcpu_sys_reg(vcpu, AFSR0_EL2), SYS_AFSR0);
112+
write_sysreg_el1(__vcpu_sys_reg(vcpu, AFSR1_EL2), SYS_AFSR1);
113+
write_sysreg_el1(__vcpu_sys_reg(vcpu, FAR_EL2), SYS_FAR);
114+
write_sysreg(__vcpu_sys_reg(vcpu, SP_EL2), sp_el1);
115+
write_sysreg_el1(__vcpu_sys_reg(vcpu, ELR_EL2), SYS_ELR);
116+
write_sysreg_el1(__vcpu_sys_reg(vcpu, SPSR_EL2), SYS_SPSR);
117+
}
118+
18119
/*
19120
* VHE: Host and guest must save mdscr_el1 and sp_el0 (and the PC and
20121
* pstate, which are handled as part of the el2 return state) on every
@@ -66,6 +167,7 @@ void __vcpu_load_switch_sysregs(struct kvm_vcpu *vcpu)
66167
{
67168
struct kvm_cpu_context *guest_ctxt = &vcpu->arch.ctxt;
68169
struct kvm_cpu_context *host_ctxt;
170+
u64 mpidr;
69171

70172
host_ctxt = host_data_ptr(host_ctxt);
71173
__sysreg_save_user_state(host_ctxt);
@@ -89,7 +191,29 @@ void __vcpu_load_switch_sysregs(struct kvm_vcpu *vcpu)
89191
*/
90192
__sysreg32_restore_state(vcpu);
91193
__sysreg_restore_user_state(guest_ctxt);
92-
__sysreg_restore_el1_state(guest_ctxt);
194+
195+
if (unlikely(__is_hyp_ctxt(guest_ctxt))) {
196+
__sysreg_restore_vel2_state(vcpu);
197+
} else {
198+
if (vcpu_has_nv(vcpu)) {
199+
/*
200+
* Use the guest hypervisor's VPIDR_EL2 when in a
201+
* nested state. The hardware value of MIDR_EL1 gets
202+
* restored on put.
203+
*/
204+
write_sysreg(ctxt_sys_reg(guest_ctxt, VPIDR_EL2), vpidr_el2);
205+
206+
/*
207+
* As we're restoring a nested guest, set the value
208+
* provided by the guest hypervisor.
209+
*/
210+
mpidr = ctxt_sys_reg(guest_ctxt, VMPIDR_EL2);
211+
} else {
212+
mpidr = ctxt_sys_reg(guest_ctxt, MPIDR_EL1);
213+
}
214+
215+
__sysreg_restore_el1_state(guest_ctxt, mpidr);
216+
}
93217

94218
vcpu_set_flag(vcpu, SYSREGS_ON_CPU);
95219
}
@@ -112,12 +236,20 @@ void __vcpu_put_switch_sysregs(struct kvm_vcpu *vcpu)
112236

113237
host_ctxt = host_data_ptr(host_ctxt);
114238

115-
__sysreg_save_el1_state(guest_ctxt);
239+
if (unlikely(__is_hyp_ctxt(guest_ctxt)))
240+
__sysreg_save_vel2_state(vcpu);
241+
else
242+
__sysreg_save_el1_state(guest_ctxt);
243+
116244
__sysreg_save_user_state(guest_ctxt);
117245
__sysreg32_save_state(vcpu);
118246

119247
/* Restore host user state */
120248
__sysreg_restore_user_state(host_ctxt);
121249

250+
/* If leaving a nesting guest, restore MIDR_EL1 default view */
251+
if (vcpu_has_nv(vcpu))
252+
write_sysreg(read_cpuid_id(), vpidr_el2);
253+
122254
vcpu_clear_flag(vcpu, SYSREGS_ON_CPU);
123255
}

0 commit comments

Comments
 (0)