]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
KVM: arm64: Fix sign-extension of MMIO loads
authorFuad Tabba <fuad.tabba@linux.dev>
Mon, 6 Jul 2026 11:55:21 +0000 (12:55 +0100)
committerMarc Zyngier <maz@kernel.org>
Mon, 6 Jul 2026 16:18:34 +0000 (17:18 +0100)
A sign-extending load (LDRSB, LDRSH, LDRSW) from MMIO returns a
zero-extended value to the guest. The architecture performs such a load
as a memory read of the access size, then a sign-extension to the
register width. For LDRSH (DDI 0487 M.b C6.2.225, with the Mem accessor
at J1.2.3.111):

    data = Mem{16}(address, accdesc);
    X{regsize}(t) = SignExtend{regsize}(data);

The byte order is handled inside the Mem accessor, keyed on the access
size; the register width is separate, applied afterwards by SignExtend().

kvm_handle_mmio_return() runs these in the wrong order: it sign-extends
the access-width data, then calls vcpu_data_host_to_guest(), which masks
the value back to the access width (the size-keyed byte-order step). The
mask drops the sign bits that sign-extension produced.

Reorder so vcpu_data_host_to_guest() runs first, with the sign-extension
to register width after it. trace_kvm_mmio() moves with it and now logs
the access-width data before sign-extension.

Fixes: b30070862edbd ("ARM64: KVM: MMIO support BE host running LE code")
Reviewed-by: Oliver Upton <oupton@kernel.org>
Signed-off-by: Fuad Tabba <fuad.tabba@linux.dev>
Link: https://patch.msgid.link/20260706115522.954913-2-fuad.tabba@linux.dev
Signed-off-by: Marc Zyngier <maz@kernel.org>
arch/arm64/kvm/mmio.c

index e2285ed8c91de6ecc7db57124b70195478f81563..d1c3a352d5a22e4b6055eeefc52fb988ad9edced 100644 (file)
@@ -126,6 +126,10 @@ int kvm_handle_mmio_return(struct kvm_vcpu *vcpu)
                len = kvm_vcpu_dabt_get_as(vcpu);
                data = kvm_mmio_read_buf(run->mmio.data, len);
 
+               trace_kvm_mmio(KVM_TRACE_MMIO_READ, len, run->mmio.phys_addr,
+                              &data);
+               data = vcpu_data_host_to_guest(vcpu, data, len);
+
                if (kvm_vcpu_dabt_issext(vcpu) &&
                    len < sizeof(unsigned long)) {
                        mask = 1U << ((len * 8) - 1);
@@ -135,9 +139,6 @@ int kvm_handle_mmio_return(struct kvm_vcpu *vcpu)
                if (!kvm_vcpu_dabt_issf(vcpu))
                        data = data & 0xffffffff;
 
-               trace_kvm_mmio(KVM_TRACE_MMIO_READ, len, run->mmio.phys_addr,
-                              &data);
-               data = vcpu_data_host_to_guest(vcpu, data, len);
                vcpu_set_reg(vcpu, kvm_vcpu_dabt_get_rd(vcpu), data);
        }