[PATCH] RISC-V: KVM: Redirect misaligned MMIO fault to guest

Yicong Yang yang.yicong at picoheart.com
Wed Nov 4 03:54:16 PST 2026


Currently a misaligned emulated MMIO access will cause
VM abort as the KVM simply return to the userspace
with -EIO. VMM doesn't (and cannot) handle this and
usually just abort the VM. No valid exit reason filled
in this case either.

Per riscv-priv spec, non-idempotent regions might not
support misaligned access. If it's not supported,
misaligned access should raise access-fault exception
rather than misaligned exceptions.

As we're not going to handle this, it's more proper
to inject a misaligned fault and leave the handling
to the guest.

Signed-off-by: Yicong Yang <yang.yicong at picoheart.com>
---
 arch/riscv/include/asm/kvm_vcpu_insn.h |  4 +--
 arch/riscv/kvm/vcpu_exit.c             |  4 +--
 arch/riscv/kvm/vcpu_insn.c             | 41 +++++++++++++++++++++-----
 3 files changed, 37 insertions(+), 12 deletions(-)

diff --git a/arch/riscv/include/asm/kvm_vcpu_insn.h b/arch/riscv/include/asm/kvm_vcpu_insn.h
index a5c50dd4a884..cbb83743a1d3 100644
--- a/arch/riscv/include/asm/kvm_vcpu_insn.h
+++ b/arch/riscv/include/asm/kvm_vcpu_insn.h
@@ -38,10 +38,10 @@ int kvm_riscv_vcpu_virtual_insn(struct kvm_vcpu *vcpu, struct kvm_run *run,
 				struct kvm_cpu_trap *trap);
 
 int kvm_riscv_vcpu_mmio_load(struct kvm_vcpu *vcpu, struct kvm_run *run,
-			     gpa_t fault_addr,
+			     struct kvm_cpu_trap *trap, gpa_t fault_addr,
 			     unsigned long htinst);
 int kvm_riscv_vcpu_mmio_store(struct kvm_vcpu *vcpu, struct kvm_run *run,
-			      gpa_t fault_addr,
+			      struct kvm_cpu_trap *trap, gpa_t fault_addr,
 			      unsigned long htinst);
 int kvm_riscv_vcpu_mmio_return(struct kvm_vcpu *vcpu, struct kvm_run *run);
 
diff --git a/arch/riscv/kvm/vcpu_exit.c b/arch/riscv/kvm/vcpu_exit.c
index abe0dab60ca5..cbf3530e73d8 100644
--- a/arch/riscv/kvm/vcpu_exit.c
+++ b/arch/riscv/kvm/vcpu_exit.c
@@ -84,11 +84,11 @@ static int gstage_page_fault(struct kvm_vcpu *vcpu, struct kvm_run *run,
 	    (trap->scause == EXC_STORE_GUEST_PAGE_FAULT && !writable)) {
 		switch (trap->scause) {
 		case EXC_LOAD_GUEST_PAGE_FAULT:
-			return kvm_riscv_vcpu_mmio_load(vcpu, run,
+			return kvm_riscv_vcpu_mmio_load(vcpu, run, trap,
 							fault_addr,
 							trap->htinst);
 		case EXC_STORE_GUEST_PAGE_FAULT:
-			return kvm_riscv_vcpu_mmio_store(vcpu, run,
+			return kvm_riscv_vcpu_mmio_store(vcpu, run, trap,
 							 fault_addr,
 							 trap->htinst);
 		case EXC_INST_GUEST_PAGE_FAULT: {
diff --git a/arch/riscv/kvm/vcpu_insn.c b/arch/riscv/kvm/vcpu_insn.c
index 2a8dc2b88ba8..0aac89e9638d 100644
--- a/arch/riscv/kvm/vcpu_insn.c
+++ b/arch/riscv/kvm/vcpu_insn.c
@@ -359,11 +359,37 @@ int kvm_riscv_vcpu_virtual_insn(struct kvm_vcpu *vcpu, struct kvm_run *run,
 	}
 }
 
+/*
+ * Per riscv-priv spec, non-idempotent regions might not support misaligned
+ * access. If it's not supported, misaligned access should raise access-fault
+ * exception rather than misaligned exceptions.
+ *
+ * Check for misaligned MMIO access. Inject an access fault if it's misalgined.
+ */
+static bool kvm_riscv_check_misaligned_mmio(struct kvm_vcpu *vcpu,
+					    struct kvm_cpu_trap *trap,
+					    bool write, gpa_t fault_addr, int len)
+{
+	struct kvm_cpu_trap misalign_trap = { 0 };
+
+	if (fault_addr & (len - 1)) {
+		misalign_trap.scause = write ? EXC_STORE_ACCESS : EXC_LOAD_ACCESS;
+		misalign_trap.stval = trap->stval;
+		misalign_trap.sepc = trap->sepc;
+
+		kvm_riscv_vcpu_trap_redirect(vcpu, &misalign_trap);
+		return true;
+	}
+
+	return false;
+}
+
 /**
  * kvm_riscv_vcpu_mmio_load -- Emulate MMIO load instruction
  *
  * @vcpu: The VCPU pointer
  * @run:  The VCPU run struct containing the mmio data
+ * @trap: The VCPU trap context of this fault
  * @fault_addr: Guest physical address to load
  * @htinst: Transformed encoding of the load instruction
  *
@@ -372,7 +398,7 @@ int kvm_riscv_vcpu_virtual_insn(struct kvm_vcpu *vcpu, struct kvm_run *run,
  * Returns < 0 to report failure and exit run-loop
  */
 int kvm_riscv_vcpu_mmio_load(struct kvm_vcpu *vcpu, struct kvm_run *run,
-			     gpa_t fault_addr,
+			     struct kvm_cpu_trap *trap, gpa_t fault_addr,
 			     unsigned long htinst)
 {
 	u8 data_buf[8];
@@ -451,9 +477,8 @@ int kvm_riscv_vcpu_mmio_load(struct kvm_vcpu *vcpu, struct kvm_run *run,
 		return -EOPNOTSUPP;
 	}
 
-	/* Fault address should be aligned to length of MMIO */
-	if (fault_addr & (len - 1))
-		return -EIO;
+	if (kvm_riscv_check_misaligned_mmio(vcpu, trap, false, fault_addr, len))
+		return 1;
 
 	trace_kvm_mmio_emulate(vcpu->vcpu_id, ct->sepc, raw_insn, fault_addr,
 			       false, len);
@@ -491,6 +516,7 @@ int kvm_riscv_vcpu_mmio_load(struct kvm_vcpu *vcpu, struct kvm_run *run,
  *
  * @vcpu: The VCPU pointer
  * @run:  The VCPU run struct containing the mmio data
+ * @trap: The VCPU trap context of this fault
  * @fault_addr: Guest physical address to store
  * @htinst: Transformed encoding of the store instruction
  *
@@ -499,7 +525,7 @@ int kvm_riscv_vcpu_mmio_load(struct kvm_vcpu *vcpu, struct kvm_run *run,
  * Returns < 0 to report failure and exit run-loop
  */
 int kvm_riscv_vcpu_mmio_store(struct kvm_vcpu *vcpu, struct kvm_run *run,
-			      gpa_t fault_addr,
+			      struct kvm_cpu_trap *trap, gpa_t fault_addr,
 			      unsigned long htinst)
 {
 	u8 data8;
@@ -572,9 +598,8 @@ int kvm_riscv_vcpu_mmio_store(struct kvm_vcpu *vcpu, struct kvm_run *run,
 		return -EOPNOTSUPP;
 	}
 
-	/* Fault address should be aligned to length of MMIO */
-	if (fault_addr & (len - 1))
-		return -EIO;
+	if (kvm_riscv_check_misaligned_mmio(vcpu, trap, true, fault_addr, len))
+		return 1;
 
 	trace_kvm_mmio_emulate(vcpu->vcpu_id, ct->sepc, raw_insn, fault_addr,
 			       true, len);
-- 
2.54.0 (Apple Git-157)



More information about the linux-riscv mailing list