@@ -27,6 +27,7 @@ tests += $(TEST_DIR)/edat.elf
tests += $(TEST_DIR)/mvpg-sie.elf
tests += $(TEST_DIR)/spec_ex-sie.elf
tests += $(TEST_DIR)/firq.elf
+tests += $(TEST_DIR)/epsw.elf
pv-tests += $(TEST_DIR)/pv-diags.elf
new file mode 100644
@@ -0,0 +1,113 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * EPSW Interception Tests
+ *
+ * Copyright IBM Corp. 2022
+ *
+ * Authors:
+ * Nico Boehr <nrb@linux.ibm.com>
+ */
+#include <libcflat.h>
+#include <css.h>
+#include <vm.h>
+
+static uint32_t zero_out_cc_from_epsw_op1(uint32_t epsw_op1)
+{
+ return epsw_op1 & ~GENMASK(31 - 18, 31 - 20);
+}
+
+static void generate_crw(void)
+{
+ int test_device_sid = css_enumerate();
+ int cc, ret;
+
+ if (!(test_device_sid & SCHID_ONE)) {
+ report_fail("No I/O device found");
+ return;
+ }
+
+ cc = css_enable(test_device_sid, IO_SCH_ISC);
+ report(cc == 0, "Enable subchannel %08x", test_device_sid);
+
+ ret = css_generate_crw(test_device_sid);
+ if (ret)
+ report_fail("Couldn't generate CRW");
+}
+
+static void test_epsw(void)
+{
+ const uint64_t MAGIC1 = 0x1234567890abcdefUL;
+ const uint64_t MAGIC2 = 0xcafedeadbeeffaceUL;
+
+ uint64_t op1 = MAGIC1;
+ uint64_t op2 = MAGIC2;
+ uint32_t prev_epsw_op1;
+
+ /*
+ * having machine check interrupts masked and pending CRW ensures
+ * EPSW is intercepted under KVM
+ */
+ generate_crw();
+
+ report_prefix_push("both operands given");
+ asm volatile(
+ "epsw %0, %1\n"
+ : "+&d" (op1), "+&a" (op2));
+ report(upper_32_bits(op1) == upper_32_bits(MAGIC1) &&
+ upper_32_bits(op2) == upper_32_bits(MAGIC2),
+ "upper 32 bits unmodified");
+ report(lower_32_bits(op1) != lower_32_bits(MAGIC1) &&
+ lower_32_bits(op2) != lower_32_bits(MAGIC2),
+ "lower 32 bits modified");
+ prev_epsw_op1 = zero_out_cc_from_epsw_op1(lower_32_bits(op1));
+ report_prefix_pop();
+
+ report_prefix_push("second operand 0");
+ op1 = MAGIC1;
+ op2 = MAGIC2;
+ asm volatile(
+ " lgr 0,%[op2]\n"
+ " epsw %[op1], 0\n"
+ " lgr %[op2],0\n"
+ : [op2] "+&d" (op2), [op1] "+&a" (op1)
+ :
+ : "0");
+ report(upper_32_bits(op1) == upper_32_bits(MAGIC1),
+ "upper 32 bits of first operand unmodified");
+ report(zero_out_cc_from_epsw_op1(lower_32_bits(op1)) == prev_epsw_op1,
+ "first operand matches previous reading");
+ report(op2 == MAGIC2, "r0 unmodified");
+ report_prefix_pop();
+
+ report_prefix_push("both operands 0");
+ op1 = MAGIC1;
+ asm volatile(
+ " lgr 0,%[op1]\n"
+ " epsw 0, 0\n"
+ " lgr %[op1],0\n"
+ : [op1] "+&d" (op1)
+ :
+ : "0");
+ report(upper_32_bits(op1) == upper_32_bits(MAGIC1),
+ "upper 32 bits of first operand unmodified");
+ report(zero_out_cc_from_epsw_op1(lower_32_bits(op1)) == prev_epsw_op1,
+ "first operand matches previous reading");
+ report_prefix_pop();
+}
+
+int main(int argc, char **argv)
+{
+ if (!vm_is_kvm() && !vm_is_tcg()) {
+ report_skip("Not running under QEMU");
+ goto done;
+ }
+
+ report_prefix_push("epsw");
+
+ test_epsw();
+
+done:
+ report_prefix_pop();
+
+ return report_summary();
+}
@@ -125,3 +125,7 @@ extra_params = -smp 1,maxcpus=3 -cpu qemu -device qemu-s390x-cpu,core-id=2 -devi
[sck]
file = sck.elf
+
+[epsw]
+file = epsw.elf
+extra_params = -device virtio-net-ccw