Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

arm64: KVM: Correctly handle zero register during MMIO

On ARM64 register index of 31 corresponds to both zero register and SP.
However, all memory access instructions, use ZR as transfer register. SP
is used only as a base register in indirect memory addressing, or by
register-register arithmetics, which cannot be trapped here.

Correct emulation is achieved by introducing new register accessor
functions, which can do special handling for reg_num == 31. These new
accessors intentionally do not rely on old vcpu_reg() on ARM64, because
it is to be removed. Since the affected code is shared by both ARM
flavours, implementations of these accessors are also added to ARM32 code.

This patch fixes setting MMIO register to a random value (actually SP)
instead of zero by something like:

*((volatile int *)reg) = 0;

compilers tend to generate "str wzr, [xx]" here

[Marc: Fixed 32bit splat]

Signed-off-by: Pavel Fedin <p.fedin@samsung.com>
Reviewed-by: Marc Zyngier <marc.zyngier@arm.com>
Signed-off-by: Marc Zyngier <marc.zyngier@arm.com>

authored by

Pavel Fedin and committed by
Marc Zyngier
bc45a516 fbb4574c

+28 -2
+12
arch/arm/include/asm/kvm_emulate.h
··· 28 28 unsigned long *vcpu_reg(struct kvm_vcpu *vcpu, u8 reg_num); 29 29 unsigned long *vcpu_spsr(struct kvm_vcpu *vcpu); 30 30 31 + static inline unsigned long vcpu_get_reg(struct kvm_vcpu *vcpu, 32 + u8 reg_num) 33 + { 34 + return *vcpu_reg(vcpu, reg_num); 35 + } 36 + 37 + static inline void vcpu_set_reg(struct kvm_vcpu *vcpu, u8 reg_num, 38 + unsigned long val) 39 + { 40 + *vcpu_reg(vcpu, reg_num) = val; 41 + } 42 + 31 43 bool kvm_condition_valid(struct kvm_vcpu *vcpu); 32 44 void kvm_skip_instr(struct kvm_vcpu *vcpu, bool is_wide_instr); 33 45 void kvm_inject_undefined(struct kvm_vcpu *vcpu);
+3 -2
arch/arm/kvm/mmio.c
··· 115 115 trace_kvm_mmio(KVM_TRACE_MMIO_READ, len, run->mmio.phys_addr, 116 116 data); 117 117 data = vcpu_data_host_to_guest(vcpu, data, len); 118 - *vcpu_reg(vcpu, vcpu->arch.mmio_decode.rt) = data; 118 + vcpu_set_reg(vcpu, vcpu->arch.mmio_decode.rt, data); 119 119 } 120 120 121 121 return 0; ··· 186 186 rt = vcpu->arch.mmio_decode.rt; 187 187 188 188 if (is_write) { 189 - data = vcpu_data_guest_to_host(vcpu, *vcpu_reg(vcpu, rt), len); 189 + data = vcpu_data_guest_to_host(vcpu, vcpu_get_reg(vcpu, rt), 190 + len); 190 191 191 192 trace_kvm_mmio(KVM_TRACE_MMIO_WRITE, len, fault_ipa, data); 192 193 mmio_write_buf(data_buf, len, data);
+13
arch/arm64/include/asm/kvm_emulate.h
··· 109 109 return (unsigned long *)&vcpu_gp_regs(vcpu)->regs.regs[reg_num]; 110 110 } 111 111 112 + static inline unsigned long vcpu_get_reg(const struct kvm_vcpu *vcpu, 113 + u8 reg_num) 114 + { 115 + return (reg_num == 31) ? 0 : vcpu_gp_regs(vcpu)->regs.regs[reg_num]; 116 + } 117 + 118 + static inline void vcpu_set_reg(struct kvm_vcpu *vcpu, u8 reg_num, 119 + unsigned long val) 120 + { 121 + if (reg_num != 31) 122 + vcpu_gp_regs(vcpu)->regs.regs[reg_num] = val; 123 + } 124 + 112 125 /* Get vcpu SPSR for current mode */ 113 126 static inline unsigned long *vcpu_spsr(const struct kvm_vcpu *vcpu) 114 127 {