[PATCH] RISC-V: KVM: Fix sign extension for MMIO loads
Anup Patel
anup at brainfault.org
Sun May 17 21:31:28 PDT 2026
On Thu, May 14, 2026 at 1:47 PM Jiakai Xu <xujiakai2025 at iscas.ac.cn> wrote:
>
> The kvm_riscv_vcpu_mmio_return() function handles MMIO read results
> by writing the data back to the guest register. For signed load
> instructions (LB, LH, LW on RV64), the value needs sign-extension
> from a smaller integer to unsigned long.
>
> The current code uses:
> (ulong)data << shift >> shift
> but (ulong) makes the right shift a logical shift (zero-extend)
> rather than an arithmetic shift (sign-extend), causing incorrect
> results when the MMIO device returns a negative value. For example,
> LB reading 0x80 would return 128 instead of -128.
>
> Fix this by casting to (long) after the left shift so that the
> subsequent right shift is arithmetic and correctly propagates
> the sign bit:
> (long)((ulong)data << shift) >> shift
>
> Additionally, remove the unnecessary shift assignment for LBU
> (unsigned byte load) since it does not need sign extension.
> This makes LBU consistent with LHU and LWU which already keep
> shift = 0.
>
> Fixes: b91f0e4cb8a3 ("RISC-V: KVM: Factor-out instruction emulation into separate sources")
> Signed-off-by: Jiakai Xu <jiakaiPeanut at gmail.com>
> Signed-off-by: Jiakai Xu <xujiakai2025 at iscas.ac.cn>
> Assisted-by: OpenClaw:DeepSeek-V3.2
LGTM.
Reviewed-by: Anup Patel <anup at brainfault.org>
Queued this patch as fix for Linux-7.1-rcX
Thanks,
Anup
> ---
> arch/riscv/kvm/vcpu_insn.c | 9 ++++-----
> 1 file changed, 4 insertions(+), 5 deletions(-)
>
> diff --git a/arch/riscv/kvm/vcpu_insn.c b/arch/riscv/kvm/vcpu_insn.c
> index 4d89b94128aea..f09f9251d1f0a 100644
> --- a/arch/riscv/kvm/vcpu_insn.c
> +++ b/arch/riscv/kvm/vcpu_insn.c
> @@ -415,7 +415,6 @@ int kvm_riscv_vcpu_mmio_load(struct kvm_vcpu *vcpu, struct kvm_run *run,
> shift = 8 * (sizeof(ulong) - len);
> } else if ((insn & INSN_MASK_LBU) == INSN_MATCH_LBU) {
> len = 1;
> - shift = 8 * (sizeof(ulong) - len);
> #ifdef CONFIG_64BIT
> } else if ((insn & INSN_MASK_LD) == INSN_MATCH_LD) {
> len = 8;
> @@ -649,22 +648,22 @@ int kvm_riscv_vcpu_mmio_return(struct kvm_vcpu *vcpu, struct kvm_run *run)
> case 1:
> data8 = *((u8 *)run->mmio.data);
> SET_RD(insn, &vcpu->arch.guest_context,
> - (ulong)data8 << shift >> shift);
> + (long)((ulong)data8 << shift) >> shift);
> break;
> case 2:
> data16 = *((u16 *)run->mmio.data);
> SET_RD(insn, &vcpu->arch.guest_context,
> - (ulong)data16 << shift >> shift);
> + (long)((ulong)data16 << shift) >> shift);
> break;
> case 4:
> data32 = *((u32 *)run->mmio.data);
> SET_RD(insn, &vcpu->arch.guest_context,
> - (ulong)data32 << shift >> shift);
> + (long)((ulong)data32 << shift) >> shift);
> break;
> case 8:
> data64 = *((u64 *)run->mmio.data);
> SET_RD(insn, &vcpu->arch.guest_context,
> - (ulong)data64 << shift >> shift);
> + (long)((ulong)data64 << shift) >> shift);
> break;
> default:
> return -EOPNOTSUPP;
> --
> 2.34.1
>
More information about the linux-riscv
mailing list