diff mbox series

[kvm-unit-tests,v3,6/7] s390x: pv: Add IPL reset tests

Message ID 20230421113647.134536-7-frankja@linux.ibm.com (mailing list archive)
State New, archived
Headers show
Series s390x: Add PV SIE intercepts and ipl tests | expand

Commit Message

Janosch Frank April 21, 2023, 11:36 a.m. UTC
The diag308 requires extensive cooperation between the hypervisor and
the Ultravisor so the Ultravisor can make sure all necessary reset
steps have been done.

Let's check if we get the correct validity errors.

Signed-off-by: Janosch Frank <frankja@linux.ibm.com>
---
 s390x/Makefile      |   2 +
 s390x/pv-ipl.c      | 145 ++++++++++++++++++++++++++++++++++++++++++++
 s390x/unittests.cfg |   4 ++
 3 files changed, 151 insertions(+)
 create mode 100644 s390x/pv-ipl.c

Comments

Claudio Imbrenda April 21, 2023, 3:32 p.m. UTC | #1
On Fri, 21 Apr 2023 11:36:46 +0000
Janosch Frank <frankja@linux.ibm.com> wrote:

> The diag308 requires extensive cooperation between the hypervisor and
> the Ultravisor so the Ultravisor can make sure all necessary reset
> steps have been done.
> 
> Let's check if we get the correct validity errors.
> 
> Signed-off-by: Janosch Frank <frankja@linux.ibm.com>
> ---
>  s390x/Makefile      |   2 +
>  s390x/pv-ipl.c      | 145 ++++++++++++++++++++++++++++++++++++++++++++
>  s390x/unittests.cfg |   4 ++
>  3 files changed, 151 insertions(+)
>  create mode 100644 s390x/pv-ipl.c
> 
> diff --git a/s390x/Makefile b/s390x/Makefile
> index 67be5360..b5b94810 100644
> --- a/s390x/Makefile
> +++ b/s390x/Makefile
> @@ -43,6 +43,7 @@ tests += $(TEST_DIR)/ex.elf
>  
>  pv-tests += $(TEST_DIR)/pv-diags.elf
>  pv-tests += $(TEST_DIR)/pv-icptcode.elf
> +pv-tests += $(TEST_DIR)/pv-ipl.elf
>  
>  ifneq ($(HOST_KEY_DOCUMENT),)
>  ifneq ($(GEN_SE_HEADER),)
> @@ -130,6 +131,7 @@ $(TEST_DIR)/pv-icptcode.elf: pv-snippets += $(SNIPPET_DIR)/asm/pv-icpt-112.gbin
>  $(TEST_DIR)/pv-icptcode.elf: pv-snippets += $(SNIPPET_DIR)/asm/icpt-loop.gbin
>  $(TEST_DIR)/pv-icptcode.elf: pv-snippets += $(SNIPPET_DIR)/asm/loop.gbin
>  $(TEST_DIR)/pv-icptcode.elf: pv-snippets += $(SNIPPET_DIR)/asm/pv-icpt-vir-timing.gbin
> +$(TEST_DIR)/pv-ipl.elf: pv-snippets += $(SNIPPET_DIR)/asm/pv-diag-308.gbin
>  
>  ifneq ($(GEN_SE_HEADER),)
>  snippets += $(pv-snippets)
> diff --git a/s390x/pv-ipl.c b/s390x/pv-ipl.c
> new file mode 100644
> index 00000000..aad1275e
> --- /dev/null
> +++ b/s390x/pv-ipl.c
> @@ -0,0 +1,145 @@
> +/* SPDX-License-Identifier: GPL-2.0-only */
> +/*
> + * PV diagnose 308 (IPL) tests
> + *
> + * Copyright (c) 2023 IBM Corp
> + *
> + * Authors:
> + *  Janosch Frank <frankja@linux.ibm.com>
> + */
> +#include <libcflat.h>
> +#include <sie.h>
> +#include <sclp.h>
> +#include <snippet.h>
> +#include <pv_icptdata.h>
> +#include <asm/facility.h>
> +#include <asm/uv.h>
> +
> +static struct vm vm;
> +
> +static void test_diag_308(int subcode)
> +{
> +	extern const char SNIPPET_NAME_START(asm, pv_diag_308)[];
> +	extern const char SNIPPET_NAME_END(asm, pv_diag_308)[];
> +	extern const char SNIPPET_HDR_START(asm, pv_diag_308)[];
> +	extern const char SNIPPET_HDR_END(asm, pv_diag_308)[];
> +	int size_hdr = SNIPPET_HDR_LEN(asm, pv_diag_308);
> +	int size_gbin = SNIPPET_LEN(asm, pv_diag_308);
> +	uint16_t rc, rrc;
> +	char prefix[10];
> +	int cc;
> +
> +	snprintf(prefix, sizeof(prefix), "subcode %d", subcode);
> +
> +	report_prefix_push(prefix);

report_prefix_pushf

> +	snippet_pv_init(&vm, SNIPPET_NAME_START(asm, pv_diag_308),
> +			SNIPPET_HDR_START(asm, pv_diag_308),
> +			size_gbin, size_hdr, SNIPPET_UNPACK_OFF);
> +
> +	/* First exit is a diag 0x500 */
> +	sie(&vm);
> +	assert(pv_icptdata_check_diag(&vm, 0x500));
> +
> +	/*
> +	 * The snippet asked us for the subcode and we answer with 0 in gr2

not always 0 I guess?

> +	 * SIE will copy gr2 to the guest
> +	 */
> +	vm.save_area.guest.grs[2] = subcode;
> +
> +	/* Continue after diag 0x500, next icpt should be the 0x308 */
> +	sie(&vm);
> +	assert(pv_icptdata_check_diag(&vm, 0x308));
> +	assert(vm.save_area.guest.grs[2] == subcode);
> +
> +	/*
> +	 * We need to perform several UV calls to emulate the subcode
> +	 * 0/1. Failing to do that should result in a validity.
> +	 *
> +	 * - Mark all cpus as stopped
> +	 * - Unshare all memory
> +	 * - Prepare the reset
> +	 * - Reset the cpus
> +	 * - Load the reset PSW
> +	 */
> +	sie_expect_validity(&vm);
> +	sie(&vm);
> +	report(uv_validity_check(&vm), "validity, no action");
> +
> +	/* Mark the CPU as stopped so we can unshare and reset */
> +	cc = uv_set_cpu_state(vm.sblk->pv_handle_cpu, PV_CPU_STATE_STP);
> +	report(!cc, "Set cpu stopped");
> +
> +	sie_expect_validity(&vm);
> +	sie(&vm);
> +	report(uv_validity_check(&vm), "validity, stopped");
> +
> +	/* Unshare all memory */
> +	cc = uv_cmd_nodata(vm.sblk->pv_handle_config,
> +			   UVC_CMD_SET_UNSHARED_ALL, &rc, &rrc);
> +	report(cc == 0 && rc == 1, "Unshare all");
> +
> +	sie_expect_validity(&vm);
> +	sie(&vm);
> +	report(uv_validity_check(&vm), "validity, stopped, unshared");
> +
> +	/* Prepare the CPU reset */
> +	cc = uv_cmd_nodata(vm.sblk->pv_handle_config,
> +			   UVC_CMD_PREPARE_RESET, &rc, &rrc);
> +	report(cc == 0 && rc == 1, "Prepare reset call");
> +
> +	sie_expect_validity(&vm);
> +	sie(&vm);
> +	report(uv_validity_check(&vm), "validity, stopped, unshared, prep reset");
> +
> +	/*
> +	 * Do the reset on the initiating cpu
> +	 *
> +	 * Reset clear for subcode 0
> +	 * Reset initial for subcode 1
> +	 */
> +	if (subcode == 0) {
> +		cc = uv_cmd_nodata(vm.sblk->pv_handle_cpu,
> +				   UVC_CMD_CPU_RESET_CLEAR, &rc, &rrc);
> +		report(cc == 0 && rc == 1, "Clear reset cpu");
> +	} else {
> +		cc = uv_cmd_nodata(vm.sblk->pv_handle_cpu,
> +				   UVC_CMD_CPU_RESET_INITIAL, &rc, &rrc);
> +		report(cc == 0 && rc == 1, "Initial reset cpu");
> +	}
> +
> +	sie_expect_validity(&vm);
> +	sie(&vm);
> +	report(uv_validity_check(&vm), "validity, stopped, unshared, prep reset, cpu reset");
> +
> +	/* Load the PSW from 0x0 */
> +	cc = uv_set_cpu_state(vm.sblk->pv_handle_cpu, PV_CPU_STATE_OPR_LOAD);
> +	report(!cc, "Set cpu load");
> +
> +	/*
> +	 * Check if we executed the iaddr of the reset PSW, we should
> +	 * see a diagnose 0x9c PV instruction notification.
> +	 */
> +	sie(&vm);
> +	report(pv_icptdata_check_diag(&vm, 0x9c) &&
> +	       vm.save_area.guest.grs[0] == 42,
> +	       "continue after load");
> +
> +	uv_destroy_guest(&vm);
> +	report_prefix_pop();
> +}
> +
> +int main(void)
> +{
> +	report_prefix_push("uv-sie");
> +	if (!uv_guest_requirement_checks())
> +		goto done;
> +
> +	snippet_setup_guest(&vm, true);
> +	test_diag_308(0);
> +	test_diag_308(1);
> +	sie_guest_destroy(&vm);
> +
> +done:
> +	report_prefix_pop();
> +	return report_summary();
> +}
> diff --git a/s390x/unittests.cfg b/s390x/unittests.cfg
> index e2d3478e..e08e5c84 100644
> --- a/s390x/unittests.cfg
> +++ b/s390x/unittests.cfg
> @@ -223,3 +223,7 @@ file = ex.elf
>  file = pv-icptcode.elf
>  smp = 3
>  extra_params = -m 2200
> +
> +[pv-ipl]
> +file = pv-ipl.elf
> +extra_params = -m 2200
diff mbox series

Patch

diff --git a/s390x/Makefile b/s390x/Makefile
index 67be5360..b5b94810 100644
--- a/s390x/Makefile
+++ b/s390x/Makefile
@@ -43,6 +43,7 @@  tests += $(TEST_DIR)/ex.elf
 
 pv-tests += $(TEST_DIR)/pv-diags.elf
 pv-tests += $(TEST_DIR)/pv-icptcode.elf
+pv-tests += $(TEST_DIR)/pv-ipl.elf
 
 ifneq ($(HOST_KEY_DOCUMENT),)
 ifneq ($(GEN_SE_HEADER),)
@@ -130,6 +131,7 @@  $(TEST_DIR)/pv-icptcode.elf: pv-snippets += $(SNIPPET_DIR)/asm/pv-icpt-112.gbin
 $(TEST_DIR)/pv-icptcode.elf: pv-snippets += $(SNIPPET_DIR)/asm/icpt-loop.gbin
 $(TEST_DIR)/pv-icptcode.elf: pv-snippets += $(SNIPPET_DIR)/asm/loop.gbin
 $(TEST_DIR)/pv-icptcode.elf: pv-snippets += $(SNIPPET_DIR)/asm/pv-icpt-vir-timing.gbin
+$(TEST_DIR)/pv-ipl.elf: pv-snippets += $(SNIPPET_DIR)/asm/pv-diag-308.gbin
 
 ifneq ($(GEN_SE_HEADER),)
 snippets += $(pv-snippets)
diff --git a/s390x/pv-ipl.c b/s390x/pv-ipl.c
new file mode 100644
index 00000000..aad1275e
--- /dev/null
+++ b/s390x/pv-ipl.c
@@ -0,0 +1,145 @@ 
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * PV diagnose 308 (IPL) tests
+ *
+ * Copyright (c) 2023 IBM Corp
+ *
+ * Authors:
+ *  Janosch Frank <frankja@linux.ibm.com>
+ */
+#include <libcflat.h>
+#include <sie.h>
+#include <sclp.h>
+#include <snippet.h>
+#include <pv_icptdata.h>
+#include <asm/facility.h>
+#include <asm/uv.h>
+
+static struct vm vm;
+
+static void test_diag_308(int subcode)
+{
+	extern const char SNIPPET_NAME_START(asm, pv_diag_308)[];
+	extern const char SNIPPET_NAME_END(asm, pv_diag_308)[];
+	extern const char SNIPPET_HDR_START(asm, pv_diag_308)[];
+	extern const char SNIPPET_HDR_END(asm, pv_diag_308)[];
+	int size_hdr = SNIPPET_HDR_LEN(asm, pv_diag_308);
+	int size_gbin = SNIPPET_LEN(asm, pv_diag_308);
+	uint16_t rc, rrc;
+	char prefix[10];
+	int cc;
+
+	snprintf(prefix, sizeof(prefix), "subcode %d", subcode);
+
+	report_prefix_push(prefix);
+	snippet_pv_init(&vm, SNIPPET_NAME_START(asm, pv_diag_308),
+			SNIPPET_HDR_START(asm, pv_diag_308),
+			size_gbin, size_hdr, SNIPPET_UNPACK_OFF);
+
+	/* First exit is a diag 0x500 */
+	sie(&vm);
+	assert(pv_icptdata_check_diag(&vm, 0x500));
+
+	/*
+	 * The snippet asked us for the subcode and we answer with 0 in gr2
+	 * SIE will copy gr2 to the guest
+	 */
+	vm.save_area.guest.grs[2] = subcode;
+
+	/* Continue after diag 0x500, next icpt should be the 0x308 */
+	sie(&vm);
+	assert(pv_icptdata_check_diag(&vm, 0x308));
+	assert(vm.save_area.guest.grs[2] == subcode);
+
+	/*
+	 * We need to perform several UV calls to emulate the subcode
+	 * 0/1. Failing to do that should result in a validity.
+	 *
+	 * - Mark all cpus as stopped
+	 * - Unshare all memory
+	 * - Prepare the reset
+	 * - Reset the cpus
+	 * - Load the reset PSW
+	 */
+	sie_expect_validity(&vm);
+	sie(&vm);
+	report(uv_validity_check(&vm), "validity, no action");
+
+	/* Mark the CPU as stopped so we can unshare and reset */
+	cc = uv_set_cpu_state(vm.sblk->pv_handle_cpu, PV_CPU_STATE_STP);
+	report(!cc, "Set cpu stopped");
+
+	sie_expect_validity(&vm);
+	sie(&vm);
+	report(uv_validity_check(&vm), "validity, stopped");
+
+	/* Unshare all memory */
+	cc = uv_cmd_nodata(vm.sblk->pv_handle_config,
+			   UVC_CMD_SET_UNSHARED_ALL, &rc, &rrc);
+	report(cc == 0 && rc == 1, "Unshare all");
+
+	sie_expect_validity(&vm);
+	sie(&vm);
+	report(uv_validity_check(&vm), "validity, stopped, unshared");
+
+	/* Prepare the CPU reset */
+	cc = uv_cmd_nodata(vm.sblk->pv_handle_config,
+			   UVC_CMD_PREPARE_RESET, &rc, &rrc);
+	report(cc == 0 && rc == 1, "Prepare reset call");
+
+	sie_expect_validity(&vm);
+	sie(&vm);
+	report(uv_validity_check(&vm), "validity, stopped, unshared, prep reset");
+
+	/*
+	 * Do the reset on the initiating cpu
+	 *
+	 * Reset clear for subcode 0
+	 * Reset initial for subcode 1
+	 */
+	if (subcode == 0) {
+		cc = uv_cmd_nodata(vm.sblk->pv_handle_cpu,
+				   UVC_CMD_CPU_RESET_CLEAR, &rc, &rrc);
+		report(cc == 0 && rc == 1, "Clear reset cpu");
+	} else {
+		cc = uv_cmd_nodata(vm.sblk->pv_handle_cpu,
+				   UVC_CMD_CPU_RESET_INITIAL, &rc, &rrc);
+		report(cc == 0 && rc == 1, "Initial reset cpu");
+	}
+
+	sie_expect_validity(&vm);
+	sie(&vm);
+	report(uv_validity_check(&vm), "validity, stopped, unshared, prep reset, cpu reset");
+
+	/* Load the PSW from 0x0 */
+	cc = uv_set_cpu_state(vm.sblk->pv_handle_cpu, PV_CPU_STATE_OPR_LOAD);
+	report(!cc, "Set cpu load");
+
+	/*
+	 * Check if we executed the iaddr of the reset PSW, we should
+	 * see a diagnose 0x9c PV instruction notification.
+	 */
+	sie(&vm);
+	report(pv_icptdata_check_diag(&vm, 0x9c) &&
+	       vm.save_area.guest.grs[0] == 42,
+	       "continue after load");
+
+	uv_destroy_guest(&vm);
+	report_prefix_pop();
+}
+
+int main(void)
+{
+	report_prefix_push("uv-sie");
+	if (!uv_guest_requirement_checks())
+		goto done;
+
+	snippet_setup_guest(&vm, true);
+	test_diag_308(0);
+	test_diag_308(1);
+	sie_guest_destroy(&vm);
+
+done:
+	report_prefix_pop();
+	return report_summary();
+}
diff --git a/s390x/unittests.cfg b/s390x/unittests.cfg
index e2d3478e..e08e5c84 100644
--- a/s390x/unittests.cfg
+++ b/s390x/unittests.cfg
@@ -223,3 +223,7 @@  file = ex.elf
 file = pv-icptcode.elf
 smp = 3
 extra_params = -m 2200
+
+[pv-ipl]
+file = pv-ipl.elf
+extra_params = -m 2200