[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