On 2025/6/26 16:09, Yicong Yang wrote: > From: Yicong Yang <yangyic...@hisilicon.com> > > The unsupported exclusive/atomic DABT exception is hand to the > userspace. Provide a way for the userspace to inject this DABT > to the guest if they want to imitate how this is handled on the > host. >
Tested LS64 fault in VM using kvmtool with below patch, debug information added. The LS64 DABT injection works as expected. # Perform LS64 on emulated MMIO root@localhost:/mnt# lspci -tv -[0000:00]-+-00.0 Device 1af4:1049 \-01.0 Device 1af4:1041 root@localhost:/mnt# ./ls64.o -d 0000:00:00.0 -b 2 Start address of 0000:00:00.0 BAR2 is 0x0 mappded va is 0xffff82d20000 addr is 0x4120e8 Info: esr_iss 93c09000 fault_ipa 50000000 // kvmtool debug information Info: correct mapping but emulated MMIO // kvmtool debug information test FEAT_LS64 Bus error # Perform LS64 on normal memory root@localhost:/mnt# ./ls64.o -a mappded va is 0xffffa5400000 addr is 0x4120e8 test FEAT_LS64 Info: esr_iss 35 fault_ipa 83971000 // kvmtool debug information Info: Injecting DABT since incorrect Guest memory attribute // kvmtool debug information Bus error diff --git a/arm/aarch64/include/asm/kvm.h b/arm/aarch64/include/asm/kvm.h index 66736ff..d3cd866 100644 --- a/arm/aarch64/include/asm/kvm.h +++ b/arm/aarch64/include/asm/kvm.h @@ -186,8 +186,9 @@ struct kvm_vcpu_events { __u8 serror_pending; __u8 serror_has_esr; __u8 ext_dabt_pending; + __u8 ext_dabt_excl_atom_pending; /* Align it to 8 bytes */ - __u8 pad[5]; + __u8 pad[4]; __u64 serror_esr; } exception; __u32 reserved[12]; diff --git a/include/kvm/kvm.h b/include/kvm/kvm.h index eb23e2f..56b985d 100644 --- a/include/kvm/kvm.h +++ b/include/kvm/kvm.h @@ -129,6 +129,8 @@ bool kvm__emulate_mmio(struct kvm_cpu *vcpu, u64 phys_addr, u8 *data, u32 len, u int kvm__destroy_mem(struct kvm *kvm, u64 guest_phys, u64 size, void *userspace_addr); int kvm__register_mem(struct kvm *kvm, u64 guest_phys, u64 size, void *userspace_addr, enum kvm_mem_type type); +bool kvm__valid_mmio(struct kvm_cpu *vcpu, u64 phys_addr, u32 len); + static inline int kvm__register_ram(struct kvm *kvm, u64 guest_phys, u64 size, void *userspace_addr) { diff --git a/include/linux/kvm.h b/include/linux/kvm.h index 502ea63..fa01051 100644 --- a/include/linux/kvm.h +++ b/include/linux/kvm.h @@ -178,6 +178,7 @@ struct kvm_xen_exit { #define KVM_EXIT_NOTIFY 37 #define KVM_EXIT_LOONGARCH_IOCSR 38 #define KVM_EXIT_MEMORY_FAULT 39 +#define KVM_EXIT_ARM_LDST64B 41 /* For KVM_EXIT_INTERNAL_ERROR */ /* Emulate instruction failed. */ diff --git a/kvm-cpu.c b/kvm-cpu.c index 7362f2e..f544cf4 100644 --- a/kvm-cpu.c +++ b/kvm-cpu.c @@ -238,6 +238,42 @@ int kvm_cpu__start(struct kvm_cpu *cpu) goto exit_kvm; }; break; + case KVM_EXIT_ARM_LDST64B: { + struct kvm_run *kvm_run = cpu->kvm_run; + __u64 ipa = kvm_run->arm_nisv.fault_ipa; + int ret; + + pr_info("esr_iss %llx fault_ipa %llx", + kvm_run->arm_nisv.esr_iss, ipa); + + if (!kvm__valid_mmio(cpu, ipa, 64)) { + struct kvm_vcpu_events events = { + .exception.ext_dabt_excl_atom_pending = 1, + }; + + pr_info("Injecting DABT since incorrect Guest memory attribute"); + + ret = ioctl(cpu->vcpu_fd, KVM_SET_VCPU_EVENTS, &events); + if (ret) { + pr_err("err inject DABT"); + goto panic_kvm; + } + } else { + struct kvm_vcpu_events events = { + .exception.ext_dabt_excl_atom_pending = 1, + }; + + pr_info("correct mapping but emulated MMIO"); + + ret = ioctl(cpu->vcpu_fd, KVM_SET_VCPU_EVENTS, &events); + if (ret) { + pr_err("err inject DABT"); + goto panic_kvm; + } + } + + break; + } default: { bool ret; diff --git a/mmio.c b/mmio.c index 231ce91..7071d3a 100644 --- a/mmio.c +++ b/mmio.c @@ -195,6 +195,11 @@ bool kvm__deregister_iotrap(struct kvm *kvm, u64 phys_addr, unsigned int flags) return true; } +bool kvm__valid_mmio(struct kvm_cpu *vcpu, u64 phys_addr, u32 len) +{ + return !!mmio_get(&mmio_tree, phys_addr, len); +} + bool kvm__emulate_mmio(struct kvm_cpu *vcpu, u64 phys_addr, u8 *data, u32 len, u8 is_write) { > Signed-off-by: Yicong Yang <yangyic...@hisilicon.com> > --- > arch/arm64/include/asm/kvm_emulate.h | 1 + > arch/arm64/include/uapi/asm/kvm.h | 3 ++- > arch/arm64/kvm/guest.c | 4 ++++ > arch/arm64/kvm/inject_fault.c | 29 ++++++++++++++++++++++++++++ > 4 files changed, 36 insertions(+), 1 deletion(-) > > diff --git a/arch/arm64/include/asm/kvm_emulate.h > b/arch/arm64/include/asm/kvm_emulate.h > index 0720898f563e..df141ae77019 100644 > --- a/arch/arm64/include/asm/kvm_emulate.h > +++ b/arch/arm64/include/asm/kvm_emulate.h > @@ -47,6 +47,7 @@ void kvm_skip_instr32(struct kvm_vcpu *vcpu); > void kvm_inject_undefined(struct kvm_vcpu *vcpu); > void kvm_inject_vabt(struct kvm_vcpu *vcpu); > void kvm_inject_dabt(struct kvm_vcpu *vcpu, unsigned long addr); > +void kvm_inject_dabt_excl_atomic(struct kvm_vcpu *vcpu, unsigned long addr); > void kvm_inject_pabt(struct kvm_vcpu *vcpu, unsigned long addr); > void kvm_inject_size_fault(struct kvm_vcpu *vcpu); > > diff --git a/arch/arm64/include/uapi/asm/kvm.h > b/arch/arm64/include/uapi/asm/kvm.h > index ed5f3892674c..69985acda668 100644 > --- a/arch/arm64/include/uapi/asm/kvm.h > +++ b/arch/arm64/include/uapi/asm/kvm.h > @@ -184,8 +184,9 @@ struct kvm_vcpu_events { > __u8 serror_pending; > __u8 serror_has_esr; > __u8 ext_dabt_pending; > + __u8 ext_dabt_excl_atom_pending; > /* Align it to 8 bytes */ > - __u8 pad[5]; > + __u8 pad[4]; > __u64 serror_esr; > } exception; > __u32 reserved[12]; > diff --git a/arch/arm64/kvm/guest.c b/arch/arm64/kvm/guest.c > index 2196979a24a3..47bc09ea50c3 100644 > --- a/arch/arm64/kvm/guest.c > +++ b/arch/arm64/kvm/guest.c > @@ -839,6 +839,7 @@ int __kvm_arm_vcpu_set_events(struct kvm_vcpu *vcpu, > bool serror_pending = events->exception.serror_pending; > bool has_esr = events->exception.serror_has_esr; > bool ext_dabt_pending = events->exception.ext_dabt_pending; > + bool ext_dabt_excl_atom_pending = > events->exception.ext_dabt_excl_atom_pending; > > if (serror_pending && has_esr) { > if (!cpus_have_final_cap(ARM64_HAS_RAS_EXTN)) > @@ -855,6 +856,9 @@ int __kvm_arm_vcpu_set_events(struct kvm_vcpu *vcpu, > if (ext_dabt_pending) > kvm_inject_dabt(vcpu, kvm_vcpu_get_hfar(vcpu)); > > + if (ext_dabt_excl_atom_pending) > + kvm_inject_dabt_excl_atomic(vcpu, kvm_vcpu_get_hfar(vcpu)); > + > return 0; > } > > diff --git a/arch/arm64/kvm/inject_fault.c b/arch/arm64/kvm/inject_fault.c > index a640e839848e..d64650a1aefe 100644 > --- a/arch/arm64/kvm/inject_fault.c > +++ b/arch/arm64/kvm/inject_fault.c > @@ -171,6 +171,35 @@ void kvm_inject_dabt(struct kvm_vcpu *vcpu, unsigned > long addr) > inject_abt64(vcpu, false, addr); > } > > +/** > + * kvm_inject_dabt_excl_atomic - inject a data abort for unsupported > exclusive > + * or atomic access > + * @vcpu: The VCPU to receive the data abort > + * @addr: The address to report in the DFAR > + * > + * It is assumed that this code is called from the VCPU thread and that the > + * VCPU therefore is not currently executing guest code. > + */ > +void kvm_inject_dabt_excl_atomic(struct kvm_vcpu *vcpu, unsigned long addr) > +{ > + u64 esr = 0; > + > + /* Reuse the general DABT injection routine and modify the DFSC */ > + kvm_inject_dabt(vcpu, addr); > + > + if (match_target_el(vcpu, unpack_vcpu_flag(EXCEPT_AA64_EL1_SYNC))) { > + esr = vcpu_read_sys_reg(vcpu, ESR_EL1); > + esr &= ~ESR_ELx_FSC; > + esr |= ESR_ELx_FSC_EXCL_ATOMIC; > + vcpu_write_sys_reg(vcpu, esr, ESR_EL1); > + } else { > + esr = vcpu_read_sys_reg(vcpu, ESR_EL2); > + esr &= ~ESR_ELx_FSC; > + esr |= ESR_ELx_FSC_EXCL_ATOMIC; > + vcpu_write_sys_reg(vcpu, esr, ESR_EL2); > + } > +} > + > /** > * kvm_inject_pabt - inject a prefetch abort into the guest > * @vcpu: The VCPU to receive the prefetch abort >