arm64: KVM: Correctly handle zero register during MMIO
authorPavel Fedin <p.fedin@samsung.com>
Fri, 4 Dec 2015 12:03:11 +0000 (15:03 +0300)
committerSasha Levin <alexander.levin@microsoft.com>
Wed, 21 Mar 2018 03:49:54 +0000 (23:49 -0400)
[ Upstream commit bc45a516fa90b43b1898758d8b53b74c24b954e4 ]

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>
Signed-off-by: Sasha Levin <alexander.levin@microsoft.com>
arch/arm/include/asm/kvm_emulate.h
arch/arm/kvm/mmio.c
arch/arm64/include/asm/kvm_emulate.h

index 642934a5ae9b156bd50d78864a4b5026b1aa3ba1..fc2acaefafb0280342822ceed7a99f59faf8f476 100644 (file)
 unsigned long *vcpu_reg(struct kvm_vcpu *vcpu, u8 reg_num);
 unsigned long *vcpu_spsr(struct kvm_vcpu *vcpu);
 
+static inline unsigned long vcpu_get_reg(struct kvm_vcpu *vcpu,
+                                        u8 reg_num)
+{
+       return *vcpu_reg(vcpu, reg_num);
+}
+
+static inline void vcpu_set_reg(struct kvm_vcpu *vcpu, u8 reg_num,
+                               unsigned long val)
+{
+       *vcpu_reg(vcpu, reg_num) = val;
+}
+
 bool kvm_condition_valid(struct kvm_vcpu *vcpu);
 void kvm_skip_instr(struct kvm_vcpu *vcpu, bool is_wide_instr);
 void kvm_inject_undefined(struct kvm_vcpu *vcpu);
index 04e5004b34e16f4ffdbcd1cdaf7ecf159166af83..387ee2a11e3607c652eec15cd2b8b356335163d8 100644 (file)
@@ -115,7 +115,7 @@ int kvm_handle_mmio_return(struct kvm_vcpu *vcpu, struct kvm_run *run)
                trace_kvm_mmio(KVM_TRACE_MMIO_READ, len, run->mmio.phys_addr,
                               &data);
                data = vcpu_data_host_to_guest(vcpu, data, len);
-               *vcpu_reg(vcpu, vcpu->arch.mmio_decode.rt) = data;
+               vcpu_set_reg(vcpu, vcpu->arch.mmio_decode.rt, data);
        }
 
        return 0;
@@ -186,7 +186,8 @@ int io_mem_abort(struct kvm_vcpu *vcpu, struct kvm_run *run,
        rt = vcpu->arch.mmio_decode.rt;
 
        if (is_write) {
-               data = vcpu_data_guest_to_host(vcpu, *vcpu_reg(vcpu, rt), len);
+               data = vcpu_data_guest_to_host(vcpu, vcpu_get_reg(vcpu, rt),
+                                              len);
 
                trace_kvm_mmio(KVM_TRACE_MMIO_WRITE, len, fault_ipa, &data);
                mmio_write_buf(data_buf, len, data);
index 3e3c4c7a50822d0be60100e389699ead642ea58e..0504f1347af0d3a65b7378c7ece0d845a2e315d9 100644 (file)
@@ -109,6 +109,19 @@ static inline unsigned long *vcpu_reg(const struct kvm_vcpu *vcpu, u8 reg_num)
        return (unsigned long *)&vcpu_gp_regs(vcpu)->regs.regs[reg_num];
 }
 
+static inline unsigned long vcpu_get_reg(const struct kvm_vcpu *vcpu,
+                                        u8 reg_num)
+{
+       return (reg_num == 31) ? 0 : vcpu_gp_regs(vcpu)->regs.regs[reg_num];
+}
+
+static inline void vcpu_set_reg(struct kvm_vcpu *vcpu, u8 reg_num,
+                               unsigned long val)
+{
+       if (reg_num != 31)
+               vcpu_gp_regs(vcpu)->regs.regs[reg_num] = val;
+}
+
 /* Get vcpu SPSR for current mode */
 static inline unsigned long *vcpu_spsr(const struct kvm_vcpu *vcpu)
 {