[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