128 lines
4.3 KiB
Diff
128 lines
4.3 KiB
Diff
From 1cc0b5d1d2f61b76c2b9124646b827abda525828 Mon Sep 17 00:00:00 2001
|
|
From: Sandwich <sandwich@archworks.co>
|
|
Date: Sat, 5 Sep 2026 12:31:16 +0200
|
|
Subject: [PATCH 5/5] KVM: selftests: verify ICEBP #DB reports RIP past the
|
|
ICEBP
|
|
|
|
Execute ICEBP (INT1) in the guest under a #DB handler that records the
|
|
exception frame's RIP, and assert it is the address of the following
|
|
instruction, as on bare metal. The handler advances RIP itself if it
|
|
finds it still on the ICEBP so a failing KVM does not loop forever.
|
|
|
|
The first ICEBP in a fresh VM faults on the unmapped IDT and handler
|
|
pages during delivery, which is the interrupted-delivery case where SVM
|
|
re-injected the #DB with the wrong RIP. Fails on SVM without the
|
|
preceding patch, passes with it, and passes on VMX, which already skips
|
|
the instruction.
|
|
|
|
Signed-off-by: Sandwich <sandwich@archworks.co>
|
|
---
|
|
arch/x86/kvm/svm/svm.c | 7 ++-
|
|
tools/testing/selftests/kvm/Makefile.kvm | 1 +
|
|
tools/testing/selftests/kvm/x86/icebp_test.c | 63 ++++++++++++++++++++
|
|
3 files changed, 68 insertions(+), 3 deletions(-)
|
|
create mode 100644 tools/testing/selftests/kvm/x86/icebp_test.c
|
|
|
|
diff --git a/arch/x86/kvm/svm/svm.c b/arch/x86/kvm/svm/svm.c
|
|
index 5b37855ee..34e025cba 100644
|
|
--- a/arch/x86/kvm/svm/svm.c
|
|
+++ b/arch/x86/kvm/svm/svm.c
|
|
@@ -2087,9 +2087,10 @@ static int icebp_interception(struct kvm_vcpu *vcpu)
|
|
/*
|
|
* ICEBP generates a trap-like #DB, but is intercepted as an
|
|
* instruction, i.e. RIP still points at the ICEBP itself. Skip it
|
|
- * before injecting the #DB so the guest observes the RIP of the next
|
|
- * instruction, as it does on bare metal and under VMX (see is_icebp()).
|
|
- * Use the vendor skip helper: a single-step #DB on the ICEBP is
|
|
+ * before injecting the #DB so that the guest, and any re-injection
|
|
+ * after an exit interrupts the delivery, sees the RIP of the next
|
|
+ * instruction, as on bare metal and under VMX (see is_icebp()). Use
|
|
+ * the vendor skip helper: a single-step #DB on the ICEBP is
|
|
* superseded by the ICEBP's own #DB.
|
|
*/
|
|
if (!svm_skip_emulated_instruction(vcpu))
|
|
diff --git a/tools/testing/selftests/kvm/Makefile.kvm b/tools/testing/selftests/kvm/Makefile.kvm
|
|
index 6fc34e9bf..b45fe0a13 100644
|
|
--- a/tools/testing/selftests/kvm/Makefile.kvm
|
|
+++ b/tools/testing/selftests/kvm/Makefile.kvm
|
|
@@ -138,6 +138,7 @@ TEST_GEN_PROGS_x86 += x86/xapic_tpr_test
|
|
TEST_GEN_PROGS_x86 += x86/xcr0_cpuid_test
|
|
TEST_GEN_PROGS_x86 += x86/xss_msr_test
|
|
TEST_GEN_PROGS_x86 += x86/debug_regs
|
|
+TEST_GEN_PROGS_x86 += x86/icebp_test
|
|
TEST_GEN_PROGS_x86 += x86/tsc_msrs_test
|
|
TEST_GEN_PROGS_x86 += x86/vmx_pmu_caps_test
|
|
TEST_GEN_PROGS_x86 += x86/xen_shinfo_test
|
|
diff --git a/tools/testing/selftests/kvm/x86/icebp_test.c b/tools/testing/selftests/kvm/x86/icebp_test.c
|
|
new file mode 100644
|
|
index 000000000..7890fa949
|
|
--- /dev/null
|
|
+++ b/tools/testing/selftests/kvm/x86/icebp_test.c
|
|
@@ -0,0 +1,63 @@
|
|
+// SPDX-License-Identifier: GPL-2.0-only
|
|
+/*
|
|
+ * icebp_test
|
|
+ *
|
|
+ * Verify that the #DB generated by ICEBP (INT1) is delivered with RIP
|
|
+ * pointing at the instruction following the ICEBP, as on bare metal.
|
|
+ *
|
|
+ * The first ICEBP in a fresh VM faults on the not-yet-mapped IDT and
|
|
+ * handler pages while the #DB is being delivered, so the delivery is
|
|
+ * interrupted by a VM-exit and re-injected by KVM. That is exactly the
|
|
+ * case where SVM used to re-inject with RIP still on the ICEBP.
|
|
+ */
|
|
+#include "test_util.h"
|
|
+#include "kvm_util.h"
|
|
+#include "processor.h"
|
|
+
|
|
+static u64 db_rip;
|
|
+
|
|
+static void db_handler(struct ex_regs *regs)
|
|
+{
|
|
+ db_rip = regs->rip;
|
|
+
|
|
+ /* Don't loop forever if RIP was left on the ICEBP itself. */
|
|
+ if (*(u8 *)regs->rip == 0xf1)
|
|
+ regs->rip++;
|
|
+}
|
|
+
|
|
+static void guest_code(void)
|
|
+{
|
|
+ u64 next_rip;
|
|
+
|
|
+ asm volatile(".byte 0xf1\n\t" /* icebp */
|
|
+ "1: lea 1b(%%rip), %0"
|
|
+ : "=r"(next_rip) : : "memory");
|
|
+
|
|
+ GUEST_ASSERT_EQ(db_rip, next_rip);
|
|
+ GUEST_DONE();
|
|
+}
|
|
+
|
|
+int main(int argc, char *argv[])
|
|
+{
|
|
+ struct kvm_vcpu *vcpu;
|
|
+ struct kvm_vm *vm;
|
|
+ struct ucall uc;
|
|
+
|
|
+ vm = vm_create_with_one_vcpu(&vcpu, guest_code);
|
|
+ vm_install_exception_handler(vm, DB_VECTOR, db_handler);
|
|
+
|
|
+ vcpu_run(vcpu);
|
|
+ TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_IO);
|
|
+
|
|
+ switch (get_ucall(vcpu, &uc)) {
|
|
+ case UCALL_ABORT:
|
|
+ REPORT_GUEST_ASSERT(uc);
|
|
+ case UCALL_DONE:
|
|
+ break;
|
|
+ default:
|
|
+ TEST_FAIL("Unknown ucall 0x%lx.", uc.cmd);
|
|
+ }
|
|
+
|
|
+ kvm_vm_free(vm);
|
|
+ return 0;
|
|
+}
|
|
--
|
|
2.55.0
|
|
|