@@ -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)
new file mode 100644
@@ -0,0 +1,143 @@
+/* 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;
+ int cc;
+
+ report_prefix_pushf("subcode %d", subcode);
+ 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 by
+ * putting the value 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_host_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();
+}
new file mode 100644
@@ -0,0 +1,51 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Diagnose 0x308 snippet used for PV IPL and reset testing
+ *
+ * Copyright (c) 2023 IBM Corp
+ *
+ * Authors:
+ * Janosch Frank <frankja@linux.ibm.com>
+ */
+#include <asm/asm-offsets.h>
+.section .text
+
+/*
+ * Entry
+ * Execute the diag500 which will set the diag 308 subcode in gr2
+ */
+diag 0, 0, 0x500
+
+/*
+ * A valid PGM new PSW can be a real problem since we never fall out
+ * of SIE and therefore effectively loop forever. 0 is a valid PSW
+ * therefore we re-use the reset_psw as this has the short PSW
+ * bit set which is invalid for a long PSW like the exception new
+ * PSWs.
+ *
+ * For subcode 0/1 there are no PGMs to consider.
+ */
+lgrl %r5, reset_psw
+stg %r5, GEN_LC_PGM_NEW_PSW
+
+/* Set up the reset psw at 0x0 */
+lgrl %r5, reset_psw
+larl %r6, done
+ogr %r5, %r6
+stg %r5, 0
+
+/* Diag 308, subcode is in gr2 */
+diag %r0, %r2, 0x308
+
+/* Should never be executed because of the reset PSW */
+diag 0, 0, 0x44
+
+/* Pass on a special value indicating success */
+done:
+lghi %r1, 42
+diag %r1, 0, 0x9c
+
+
+ .align 8
+reset_psw:
+ .quad 0x0008000180000000
@@ -224,3 +224,8 @@ file = pv-icptcode.elf
smp = 3
groups = pv-host
extra_params = -m 2200
+
+[pv-ipl]
+file = pv-ipl.elf
+groups = pv-host
+extra_params = -m 2200