From: "Aaron M. Brown" <aaronmbr@linux.ibm.com>
To: kvm@vger.kernel.org
Cc: Janosch Frank <frankja@linux.ibm.com>,
Claudio Imbrenda <imbrenda@linux.ibm.com>,
Christian Borntraeger <borntraeger@linux.ibm.com>,
linux-s390@vger.kernel.org, linux-kselftest@vger.kernel.org,
Nitya Khamar <nkhamar@linux.ibm.com>
Subject: [PATCH 1/6] KVM: s390: selftests: Add kvm_exit test for instruction intercepts
Date: Tue, 10 Nov 2026 06:16:14 -0500 [thread overview]
Message-ID: <20261110111619.61760-2-aaronmbr@linux.ibm.com> (raw)
In-Reply-To: <20261110111619.61760-1-aaronmbr@linux.ibm.com>
Add a selftest for the exits KVM reports to userspace on s390x, mainly
KVM_EXIT_S390_SIEIC. This patch adds the harness and the instruction
intercept tests:
- DIAG 0x500: inject an external call, check it is pending, resume, and
compare the registers with those synced in kvm_run.
- DIAG 0x501: check the opcode and diagnose code in ipa and ipb.
- DIAG 0x501 with registers set beforehand: check they are intact.
Co-developed-by: Nitya Khamar <nkhamar@linux.ibm.com>
Signed-off-by: Nitya Khamar <nkhamar@linux.ibm.com>
Signed-off-by: Aaron M. Brown <aaronmbr@linux.ibm.com>
---
tools/testing/selftests/kvm/Makefile.kvm | 1 +
.../kvm/include/s390/kvm_lifecycle.h | 35 +++
tools/testing/selftests/kvm/s390/kvm_exit.c | 212 ++++++++++++++++++
3 files changed, 248 insertions(+)
create mode 100644 tools/testing/selftests/kvm/include/s390/kvm_lifecycle.h
create mode 100644 tools/testing/selftests/kvm/s390/kvm_exit.c
diff --git a/tools/testing/selftests/kvm/Makefile.kvm b/tools/testing/selftests/kvm/Makefile.kvm
index 17a4256e9081..a46a75c90e66 100644
--- a/tools/testing/selftests/kvm/Makefile.kvm
+++ b/tools/testing/selftests/kvm/Makefile.kvm
@@ -232,6 +232,7 @@ TEST_GEN_PROGS_s390 += s390/irq_routing
TEST_GEN_PROGS_s390 += mmu_stress_test
TEST_GEN_PROGS_s390 += pre_fault_memory_test
TEST_GEN_PROGS_s390 += s390/irq_injection
+TEST_GEN_PROGS_s390 += s390/kvm_exit
TEST_GEN_PROGS_riscv = $(TEST_GEN_PROGS_COMMON)
TEST_GEN_PROGS_riscv += riscv/sbi_pmu_test
diff --git a/tools/testing/selftests/kvm/include/s390/kvm_lifecycle.h b/tools/testing/selftests/kvm/include/s390/kvm_lifecycle.h
new file mode 100644
index 000000000000..7dec11a6e214
--- /dev/null
+++ b/tools/testing/selftests/kvm/include/s390/kvm_lifecycle.h
@@ -0,0 +1,35 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Definition of structs for KVM lifecycle tests.
+ *
+ * Copyright IBM Corp. 2026
+ *
+ * Authors:
+ * Nitya Khamar (nkhamar@linux.ibm.com)
+ * Aaron Malik Brown (aaronmbr@linux.ibm.com)
+ */
+#ifndef SELFTEST_KVM_LIFECYCLE_H
+#define SELFTEST_KVM_LIFECYCLE_H
+
+#include "kvm_util.h"
+#include <stdbool.h>
+
+struct kvm_lifecycle_testdef {
+ const char *name;
+ bool (*test)(void);
+};
+
+struct kvm_parameters {
+ struct kvm_vm *vm;
+ struct kvm_vcpu *vcpu;
+ void *guest_code;
+ struct kvm_run *run;
+ unsigned int interrupt_type;
+ struct kvm_s390_irq *irq;
+ struct kvm_s390_irq_state *irq_state;
+ struct {
+ bool immediate_exit_condition;
+ } manual_parameters;
+};
+
+#endif /* SELFTEST_KVM_LIFECYCLE_H */
diff --git a/tools/testing/selftests/kvm/s390/kvm_exit.c b/tools/testing/selftests/kvm/s390/kvm_exit.c
new file mode 100644
index 000000000000..140358357c67
--- /dev/null
+++ b/tools/testing/selftests/kvm/s390/kvm_exit.c
@@ -0,0 +1,212 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Test for KVM Exits to userspace on s390x
+ *
+ * Copyright IBM Corp. 2026
+ *
+ * Authors:
+ * Nitya Khamar <nkhamar@linux.ibm.com>
+ * Aaron Malik Brown <aaronmbr@linux.ibm.com>
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include "test_util.h"
+#include "kvm_util.h"
+#include "kselftest.h"
+#include <errno.h>
+#include "processor.h"
+#include "ucall_common.h"
+#include "sie.h"
+#include "kvm_lifecycle.h"
+
+#define NUM_GPRS 16
+#define DIAG_ASSEMBLY_CODE 0x501
+#define IPA_DIAG 0x83
+
+static struct kvm_parameters create_vm_kvm_exit(struct kvm_parameters parameters)
+{
+ parameters.vm = vm_create_with_one_vcpu(¶meters.vcpu,
+ parameters.guest_code);
+ return parameters;
+}
+
+static void cleanup_kvm(struct kvm_parameters *parameters)
+{
+ kvm_vm_free(parameters->vm);
+}
+
+static void compare_regs(struct kvm_regs *regs, struct kvm_sync_regs *sregs)
+{
+ int register_number;
+
+ for (register_number = 0; register_number < NUM_GPRS; register_number++)
+ TEST_ASSERT_EQ(regs->gprs[register_number], sregs->gprs[register_number]);
+}
+
+static void guestcode_instruction_intercept(void)
+{
+ asm volatile("diag 0,0, 0x500\n" :::);
+ GUEST_DONE();
+}
+
+static bool test_instruction_intercept(void)
+{
+ struct kvm_parameters param = {
+ .guest_code = guestcode_instruction_intercept,
+ .interrupt_type = KVM_S390_INT_EXTERNAL_CALL,
+ .manual_parameters.immediate_exit_condition = false,
+ };
+ struct kvm_s390_irq_state irq_state = {};
+ struct kvm_s390_irq irq = {};
+ struct kvm_regs regs;
+ int interrupts;
+
+ ksft_print_msg("Testing Instruction Intercept Exit\n");
+ param = create_vm_kvm_exit(param);
+ vcpu_run(param.vcpu);
+ param.run = param.vcpu->run;
+ irq.type = param.interrupt_type;
+ irq.u.extcall.code = param.vcpu->id;
+ irq_state.len = sizeof(irq);
+ irq_state.buf = (unsigned long)&irq;
+ param.irq = &irq;
+ param.irq_state = &irq_state;
+ TEST_ASSERT_KVM_EXIT_REASON(param.vcpu, KVM_EXIT_S390_SIEIC);
+ TEST_ASSERT(param.run->s390_sieic.icptcode == ICPT_INST,
+ "Failed with Unexpected icptcode of 0x%x\n",
+ param.run->s390_sieic.icptcode);
+ interrupts = __vcpu_ioctl(param.vcpu, KVM_S390_SET_IRQ_STATE,
+ param.irq_state);
+ TEST_ASSERT(interrupts >= 0, "Cannot Inject Interrupt (%d)", errno);
+ irq_state.buf = (unsigned long)&irq;
+ irq_state.len = sizeof(irq);
+ interrupts = __vcpu_ioctl(param.vcpu, KVM_S390_GET_IRQ_STATE,
+ &irq_state);
+ TEST_ASSERT(interrupts > 0,
+ "Expected pending interrupt after injection, got %d", interrupts);
+ TEST_ASSERT_EQ(irq.type, KVM_S390_INT_EXTERNAL_CALL);
+ TEST_ASSERT_EQ(irq.u.extcall.code, param.vcpu->id);
+ vcpu_run(param.vcpu);
+ TEST_ASSERT_KVM_EXIT_REASON(param.vcpu, KVM_EXIT_S390_SIEIC);
+ TEST_ASSERT(param.run->s390_sieic.icptcode == ICPT_INST,
+ "Expected ICPT_INST from GUEST_DONE after inject, got 0x%x\n",
+ param.run->s390_sieic.icptcode);
+ vcpu_regs_get(param.vcpu, ®s);
+ compare_regs(®s, ¶m.run->s.regs);
+ cleanup_kvm(¶m);
+ return true;
+}
+
+static void guestcode_diagnose_hypercall(void)
+{
+ asm volatile("diag 0,0, 0x501\n" :::);
+}
+
+static bool test_diagnose_code(void)
+{
+ struct kvm_parameters param = {
+ .guest_code = guestcode_diagnose_hypercall,
+ .manual_parameters.immediate_exit_condition = false,
+ };
+ ksft_print_msg("Testing Diagnosis Hypercall Exit\n");
+ param = create_vm_kvm_exit(param);
+ vcpu_run(param.vcpu);
+ param.run = param.vcpu->run;
+ TEST_ASSERT_KVM_EXIT_REASON(param.vcpu, KVM_EXIT_S390_SIEIC);
+ TEST_ASSERT(param.run->s390_sieic.icptcode == ICPT_INST,
+ "Failed with Unexpected Exit Reason of %u and icptcode of 0x%x\n",
+ param.run->exit_reason, param.run->s390_sieic.icptcode);
+ TEST_ASSERT(param.run->s390_sieic.ipa >> 8 == IPA_DIAG,
+ "Failed with Unexpected Opcode of %u\n",
+ param.run->s390_sieic.ipa >> 8);
+ TEST_ASSERT(param.run->s390_sieic.ipb >> 16 == DIAG_ASSEMBLY_CODE,
+ "Failed with Unexpected Diagnose Code of %u\n",
+ param.run->s390_sieic.ipb >> 16);
+ cleanup_kvm(¶m);
+ return true;
+}
+
+static void guestcode_diagnose_conditional(void)
+{
+ asm volatile("lghi %%r3,0x3333\n"
+ "lghi %%r5, 0x555\n"
+ "lghi %%r6, 0x3333\n"
+ "cgrjne %%r6, %%r3, 1f\n"
+ "diag 0,0, 0x501\n"
+ "1:\n"
+ :
+ :
+ : "r3", "r5", "r6"
+ );
+ GUEST_DONE();
+}
+
+static bool test_diagnose_conditional_code(void)
+{
+ struct kvm_parameters param = {
+ .guest_code = guestcode_diagnose_conditional,
+ .manual_parameters.immediate_exit_condition = false,
+ };
+ struct kvm_regs regs;
+
+ ksft_print_msg("Testing Diagnosis Hypercall for Conditional Code Exit\n");
+ param = create_vm_kvm_exit(param);
+ vcpu_run(param.vcpu);
+ param.run = param.vcpu->run;
+ TEST_ASSERT_KVM_EXIT_REASON(param.vcpu, KVM_EXIT_S390_SIEIC);
+ TEST_ASSERT(param.run->s390_sieic.icptcode == ICPT_INST,
+ "Failed with Unexpected Exit Reason of %u and icptcode of 0x%x\n",
+ param.run->exit_reason, param.run->s390_sieic.icptcode);
+ TEST_ASSERT(param.run->s390_sieic.ipa >> 8 == IPA_DIAG,
+ "Failed with Unexpected Opcode of %u\n",
+ param.run->s390_sieic.ipa >> 8);
+ TEST_ASSERT(param.run->s390_sieic.ipb >> 16 == DIAG_ASSEMBLY_CODE,
+ "Failed with Unexpected Diagnose Code of %u\n",
+ param.run->s390_sieic.ipb >> 16);
+ vcpu_regs_get(param.vcpu, ®s);
+ TEST_ASSERT_EQ(regs.gprs[3], 0x3333);
+ TEST_ASSERT_EQ(regs.gprs[5], 0x555);
+ TEST_ASSERT_EQ(regs.gprs[6], 0x3333);
+ cleanup_kvm(¶m);
+ return true;
+}
+
+static void test(void)
+{
+ int i;
+ struct kvm_lifecycle_testdef testlist[] = {
+ {
+ .name = "Testing Instruction Intercepts",
+ .test = test_instruction_intercept,
+ },
+ {
+ .name = "Testing DIAG Code",
+ .test = test_diagnose_code,
+ },
+ {
+ .name = "Testing Conditional DIAG Code",
+ .test = test_diagnose_conditional_code,
+ },
+ };
+
+ ksft_print_header();
+ ksft_set_plan(ARRAY_SIZE(testlist));
+
+ for (i = 0; i < ARRAY_SIZE(testlist); i++) {
+ if (testlist[i].test())
+ ksft_test_result_pass("%s\n", testlist[i].name);
+ else
+ ksft_test_result_skip("%s\n", testlist[i].name);
+ }
+}
+
+int main(void)
+{
+ TEST_REQUIRE(kvm_has_cap(KVM_CAP_USER_MEMORY2));
+ TEST_REQUIRE(kvm_has_cap(KVM_CAP_SYNC_REGS));
+ test();
+ ksft_finished();
+}
--
2.50.1 (Apple Git-155)
next prev parent reply other threads:[~2026-11-10 11:16 UTC|newest]
Thread overview: 14+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-11-10 11:16 [PATCH 0/6] KVM: s390: selftests: Add selftests for KVM exits on s390x Aaron M. Brown
2026-11-10 11:16 ` Aaron M. Brown [this message]
2026-11-10 11:30 ` [PATCH 1/6] KVM: s390: selftests: Add kvm_exit test for instruction intercepts sashiko-bot
2026-11-12 13:45 ` Janosch Frank
2026-11-10 11:16 ` [PATCH 2/6] KVM: s390: selftests: Add SIGP and program exception tests Aaron M. Brown
2026-11-10 11:24 ` sashiko-bot
2026-11-10 11:16 ` [PATCH 3/6] KVM: s390: selftests: Add wait state and VM lifecycle tests Aaron M. Brown
2026-11-10 11:21 ` sashiko-bot
2026-11-10 11:16 ` [PATCH 4/6] KVM: s390: selftests: Add ucontrol helpers and instruction tests Aaron M. Brown
2026-11-10 11:24 ` sashiko-bot
2026-11-10 11:16 ` [PATCH 5/6] KVM: s390: selftests: Add ucontrol storage key and immediate exit tests Aaron M. Brown
2026-11-10 11:25 ` sashiko-bot
2026-11-10 11:16 ` [PATCH 6/6] KVM: s390: selftests: Add clock comparator interrupt test Aaron M. Brown
2026-11-10 11:23 ` sashiko-bot
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20261110111619.61760-2-aaronmbr@linux.ibm.com \
--to=aaronmbr@linux.ibm.com \
--cc=borntraeger@linux.ibm.com \
--cc=frankja@linux.ibm.com \
--cc=imbrenda@linux.ibm.com \
--cc=kvm@vger.kernel.org \
--cc=linux-kselftest@vger.kernel.org \
--cc=linux-s390@vger.kernel.org \
--cc=nkhamar@linux.ibm.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox