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..cc46e7f7
--- /dev/null
+++ b/s390x/pv-ipl.c
@@ -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();
+}
diff --git a/s390x/snippets/asm/pv-diag-308.S b/s390x/snippets/asm/pv-diag-308.S
new file mode 100644
index 00000000..70aea589
--- /dev/null
+++ b/s390x/snippets/asm/pv-diag-308.S
@@ -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
diff --git a/s390x/unittests.cfg b/s390x/unittests.cfg
index df25b483..26bab34a 100644
--- a/s390x/unittests.cfg
+++ b/s390x/unittests.cfg
@@ -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
