[PATCH v3 4/7] KVM: arm/arm64: Allow user injection of unsupported exclusive/atomic DABT
Yicong Yang
yangyicong at huawei.com
Thu Jun 26 01:11:40 PDT 2025
On 2025/6/26 16:09, Yicong Yang wrote:
> From: Yicong Yang <yangyicong at 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 at localhost:/mnt# lspci -tv
-[0000:00]-+-00.0 Device 1af4:1049
\-01.0 Device 1af4:1041
root at 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 at 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 <yangyicong at 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
>
More information about the linux-arm-kernel
mailing list