Linux s390 Architecture development
 help / color / mirror / Atom feed
From: Janosch Frank <frankja@linux.ibm.com>
To: "Aaron M. Brown" <aaronmbr@linux.ibm.com>, kvm@vger.kernel.org
Cc: 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: Re: [PATCH 1/6] KVM: s390: selftests: Add kvm_exit test for instruction intercepts
Date: Thu, 12 Nov 2026 14:45:13 +0100	[thread overview]
Message-ID: <90cf2229-af79-4f08-b001-753dc7a86aa2@linux.ibm.com> (raw)
In-Reply-To: <20261110111619.61760-2-aaronmbr@linux.ibm.com>

On 11/10/26 12:16 PM, Aaron M. Brown wrote:
> 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;
> +};

In this patch I don't see any benefit in having this struct.
I'm hoping there's some use for it in later patches.

> +
> +#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)

Why aren't we using a pointer to the argument?

> +{
> +	parameters.vm = vm_create_with_one_vcpu(&parameters.vcpu,
> +						parameters.guest_code);
> +	return parameters;
> +}
> +
> +static void cleanup_kvm(struct kvm_parameters *parameters)
> +{
> +	kvm_vm_free(parameters->vm);
> +}

These two function generate >= 4 lines for a single line of useful code.

> +
> +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);

Why are we doing two things at the same time?
What does the IRQ code add to the test?



> +	TEST_ASSERT(interrupts >= 0, "Cannot Inject Interrupt (%d)", errno);

That should be a == 0, no?
Documentations says "0 on success".

> +	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);

Here it's fine since the rc returns the number of bytes KVM copied.
But we could actually check for a number being at least len(irq_state)

> +	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, &regs);
> +	compare_regs(&regs, &param.run->s.regs);
> +	cleanup_kvm(&param);
> +	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");

Instruction is called diagnose according to the POP.

> +	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(&param);
> +	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");

What is this testing?


> +	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,

Which diag assembly code?
There's a huge number of them and this test already uses 2.

> +		    "Failed with Unexpected Diagnose Code of %u\n",
> +		    param.run->s390_sieic.ipb >> 16);
> +	vcpu_regs_get(param.vcpu, &regs);
> +	TEST_ASSERT_EQ(regs.gprs[3], 0x3333);
> +	TEST_ASSERT_EQ(regs.gprs[5], 0x555);
> +	TEST_ASSERT_EQ(regs.gprs[6], 0x3333);
> +	cleanup_kvm(&param);
> +	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();
> +}


  parent reply	other threads:[~2026-11-12 13:45 UTC|newest]

Thread overview: 15+ 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 ` [PATCH 1/6] KVM: s390: selftests: Add kvm_exit test for instruction intercepts Aaron M. Brown
2026-11-10 11:30   ` sashiko-bot
2026-11-12 13:45   ` Janosch Frank [this message]
2026-11-13  7:35     ` Aaron Brown
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=90cf2229-af79-4f08-b001-753dc7a86aa2@linux.ibm.com \
    --to=frankja@linux.ibm.com \
    --cc=aaronmbr@linux.ibm.com \
    --cc=borntraeger@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