On Fri, Dec 12, 2025 at 1:22 AM Zenghui Yu yuzenghui@huawei.com wrote:
On 2025/12/12 9:53, Jiaqi Yan wrote:
On Thu, Dec 11, 2025 at 5:02 AM Zenghui Yu yuzenghui@huawei.com wrote:
I can also hit this ASSERT with:
Random seed: 0x6b8b4567 # Mapped 0x40000 pages: gva=0x80000000 to gpa=0xff80000000 # Before EINJect: data=0xbaadcafe # EINJ_GVA=0x81234bad, einj_gpa=0xff81234bad, einj_hva=0xffff41234bad, einj_hpa=0x2841234bad # echo 0x10 > /sys/kernel/debug/apei/einj/error_type - done # echo 0x2 > /sys/kernel/debug/apei/einj/flags - done # echo 0x2841234bad > /sys/kernel/debug/apei/einj/param1 - done # echo 0xffffffffffffffff > /sys/kernel/debug/apei/einj/param2 - done # echo 0x1 > /sys/kernel/debug/apei/einj/notrigger - done # echo 0x1 > /sys/kernel/debug/apei/einj/error_inject - done # Memory UER EINJected # Dump kvm_run info about KVM_EXIT_MMIO # kvm_run.arm_sea: esr=0xffff90ba0040, flags=0x691000 # kvm_run.arm_sea: gva=0x100000008, gpa=0 ==== Test Assertion Failure ==== arm64/sea_to_user.c:207: exit_reason == (41) pid=38023 tid=38023 errno=4 - Interrupted system call 1 0x0000000000402d1b: run_vm at sea_to_user.c:207 2 0x0000000000402467: main at sea_to_user.c:330 3 0x0000ffff9122b03f: ?? ??:0 4 0x0000ffff9122b117: ?? ??:0 5 0x00000000004026ef: _start at ??:? Wanted KVM exit reason: 41 (ARM_SEA), got: 6 (MMIO)
Not sure what's wrong it..
Does your test machine have SDEI or SCI enabled for host APEI? Do you see any kernel log from "Memory failure:" saying hugetlb page recovered, and recovered significant earlier than the KVM exit here. It maybe the kernel has already unmapped hugepage in response to SDEI or SCI before this test actually consumes memory error, so no SEA is actually triggered.
No kernel log was printed when I saw this failure.
Hmm, and even no CPER logged by APEI/GHES? That makes me suspect the error wasn't injected successfully.
In that case, I found a bug in sea_to_user.c: GUEST_FAIL is not handled by run_vm and results in unhandled MMIO.
Here is a fix I tested on my side, with some other minor fixes. Do you mind trying it?
commit b96a92d1006fbe2752ba133eb76b0c45c9c54265 Author: Jiaqi Yan jiaqiyan@google.com Date: Fri Dec 12 22:27:53 2025 +0000
KVM: selftests: Improve sea_to_user test
Several improvments to the test for KVM_EXIT_ARM_SEA: - refactor run_vm to catch GUEST_FAIL, instead of causing confusing kvm exit with unhandled MMIO - sync far_invalid to guest - exit with KSFT_SKIP or KSFT_FAIL when should
Change-Id: I8b735de3b669297f0638bea2d32a0b36211f7f5c
diff --git a/tools/testing/selftests/kvm/arm64/sea_to_user.c b/tools/testing/selftests/kvm/arm64/sea_to_user.c index 573dd790aeb8e..6fd3cd881b415 100644 --- a/tools/testing/selftests/kvm/arm64/sea_to_user.c +++ b/tools/testing/selftests/kvm/arm64/sea_to_user.c @@ -98,11 +98,15 @@ static void write_einj_entry(const char *einj_path, uint64_t val)
static void inject_uer(uint64_t paddr) { - if (access("/sys/firmware/acpi/tables/EINJ", R_OK) == -1) - ksft_test_result_skip("EINJ table no available in firmware"); + if (access("/sys/firmware/acpi/tables/EINJ", R_OK) == -1) { + ksft_test_result_skip("EINJ table not available in firmware\n"); + exit(KSFT_SKIP); + }
- if (access(EINJ_ETYPE, R_OK | W_OK) == -1) + if (access(EINJ_ETYPE, R_OK | W_OK) == -1) { ksft_test_result_skip("EINJ module probably not loaded?\n"); + exit(KSFT_SKIP); + }
write_einj_entry(EINJ_ETYPE, ERROR_TYPE_MEMORY_UER); write_einj_entry(EINJ_FLAGS, MASK_MEMORY_UER); @@ -123,12 +127,13 @@ static void sigbus_signal_handler(int sig, siginfo_t *si, void *v) ksft_print_msg("SIGBUS (%d) received, dumping siginfo...\n", sig); ksft_print_msg("si_signo=%d, si_errno=%d, si_code=%d, si_addr=%p\n", si->si_signo, si->si_errno, si->si_code, si->si_addr); - if (si->si_code == BUS_MCEERR_AR) + if (si->si_code == BUS_MCEERR_AR) { ksft_test_result_skip("SEA is claimed by host APEI\n"); - else + exit(KSFT_SKIP); + } else { ksft_test_result_fail("Exit with signal unhandled\n"); - - exit(0); + exit(KSFT_FAIL); + } }
static void setup_sigbus_handler(void) @@ -158,7 +163,6 @@ static void expect_sea_handler(struct ex_regs *regs) { u64 esr = read_sysreg(esr_el1); u64 far = read_sysreg(far_el1); - bool expect_far_invalid = far_invalid;
GUEST_PRINTF("Handling Guest SEA\n"); GUEST_PRINTF("ESR_EL1=%#lx, FAR_EL1=%#lx\n", esr, far); @@ -166,7 +170,7 @@ static void expect_sea_handler(struct ex_regs *regs) GUEST_ASSERT_EQ(ESR_ELx_EC(esr), ESR_ELx_EC_DABT_CUR); GUEST_ASSERT_EQ(esr & ESR_ELx_FSC_TYPE, ESR_ELx_FSC_EXTABT);
- if (expect_far_invalid) { + if (far_invalid) { GUEST_ASSERT_EQ(esr & ESR_ELx_FnV, ESR_ELx_FnV); GUEST_PRINTF("Guest observed garbage value in FAR\n"); } else { @@ -185,25 +189,19 @@ static void vcpu_inject_sea(struct kvm_vcpu *vcpu) vcpu_events_set(vcpu, &events); }
-static void run_vm(struct kvm_vm *vm, struct kvm_vcpu *vcpu) +static void validate_kvm_exit_arm_sea(struct kvm_vm *vm, struct kvm_vcpu *vcpu) { - struct ucall uc; - bool guest_done = false; struct kvm_run *run = vcpu->run; u64 esr;
- /* Resume the vCPU after error injection to consume the error. */ - vcpu_run(vcpu); + TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_ARM_SEA);
- ksft_print_msg("Dump kvm_run info about KVM_EXIT_%s\n", - exit_reason_str(run->exit_reason)); + ksft_print_msg("Dumping kvm_run as arm_sea:\n"); ksft_print_msg("kvm_run.arm_sea: esr=%#llx, flags=%#llx\n", run->arm_sea.esr, run->arm_sea.flags); ksft_print_msg("kvm_run.arm_sea: gva=%#llx, gpa=%#llx\n", run->arm_sea.gva, run->arm_sea.gpa);
- TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_ARM_SEA); - esr = run->arm_sea.esr; TEST_ASSERT_EQ(ESR_ELx_EC(esr), ESR_ELx_EC_DABT_LOW); TEST_ASSERT_EQ(esr & ESR_ELx_FSC_TYPE, ESR_ELx_FSC_EXTABT); @@ -211,39 +209,48 @@ static void run_vm(struct kvm_vm *vm, struct kvm_vcpu *vcpu) TEST_ASSERT_EQ((esr & ESR_ELx_INST_SYNDROME), 0); TEST_ASSERT_EQ(esr & ESR_ELx_VNCR, 0);
- if (!(esr & ESR_ELx_FnV)) { - ksft_print_msg("Expect gva to match given FnV bit is 0\n"); + far_invalid = esr & ESR_ELx_FnV; + sync_global_to_guest(vm, far_invalid); + + if (!far_invalid) { + ksft_print_msg("Expect gva to match\n"); TEST_ASSERT_EQ(run->arm_sea.gva, EINJ_GVA); }
if (run->arm_sea.flags & KVM_EXIT_ARM_SEA_FLAG_GPA_VALID) { - ksft_print_msg("Expect gpa to match given KVM_EXIT_ARM_SEA_FLAG_GPA_VALID is set\n"); + ksft_print_msg("Expect gpa to match\n"); TEST_ASSERT_EQ(run->arm_sea.gpa, einj_gpa & PAGE_ADDR_MASK); } +}
- far_invalid = esr & ESR_ELx_FnV; - - /* Inject a SEA into guest and expect handled in SEA handler. */ - vcpu_inject_sea(vcpu); +static void run_vm(struct kvm_vm *vm, struct kvm_vcpu *vcpu) +{ + struct ucall uc; + bool guest_done = false;
/* Expect the guest to reach GUEST_DONE gracefully. */ do { vcpu_run(vcpu); - switch (get_ucall(vcpu, &uc)) { - case UCALL_PRINTF: - ksft_print_msg("From guest: %s", uc.buffer); - break; - case UCALL_DONE: - ksft_print_msg("Guest done gracefully!\n"); - guest_done = 1; - break; - case UCALL_ABORT: - ksft_print_msg("Guest aborted!\n"); - guest_done = 1; - REPORT_GUEST_ASSERT(uc); - break; - default: - TEST_FAIL("Unexpected ucall: %lu\n", uc.cmd); + if (vcpu->run->exit_reason == KVM_EXIT_ARM_SEA) { + validate_kvm_exit_arm_sea(vm, vcpu); + vcpu_inject_sea(vcpu); + } else { + switch (get_ucall(vcpu, &uc)) { + case UCALL_PRINTF: + ksft_print_msg("From guest: %s", uc.buffer); + break; + case UCALL_DONE: + ksft_print_msg("Guest done gracefully!\n"); + guest_done = 1; + break; + case UCALL_ABORT: + ksft_print_msg("Guest aborted!\n"); + guest_done = 1; + REPORT_GUEST_ASSERT(uc); + break; + default: + TEST_FAIL("Unexpected ucall: %lu\n", uc.cmd); + } } } while (!guest_done); }
Thanks, Zenghui