xref: /kvm-unit-tests/s390x/epsw.c (revision 0b7501c3b516bde6bd3d74b17e6af76b6aa2a116)
1 /* SPDX-License-Identifier: GPL-2.0-only */
2 /*
3  * EPSW Interception Tests
4  *
5  * Copyright IBM Corp. 2022
6  *
7  * Authors:
8  *  Nico Boehr <nrb@linux.ibm.com>
9  */
10 #include <libcflat.h>
11 #include <css.h>
12 #include <hardware.h>
13 
14 static uint32_t zero_out_cc_from_epsw_op1(uint32_t epsw_op1)
15 {
16 	return epsw_op1 & ~GENMASK(31 - 18, 31 - 20);
17 }
18 
19 static void generate_crw(void)
20 {
21 	int test_device_sid = css_enumerate();
22 	int cc, ret;
23 
24 	if (!(test_device_sid & SCHID_ONE)) {
25 		report_fail("No I/O device found");
26 		return;
27 	}
28 
29 	cc = css_enable(test_device_sid, IO_SCH_ISC);
30 	report(cc == 0, "Enable subchannel %08x", test_device_sid);
31 
32 	ret = css_generate_crw(test_device_sid);
33 	if (ret)
34 		report_fail("Couldn't generate CRW");
35 }
36 
37 static void test_epsw(void)
38 {
39 	const uint64_t MAGIC1 = 0x1234567890abcdefUL;
40 	const uint64_t MAGIC2 = 0xcafedeadbeeffaceUL;
41 
42 	uint64_t op1 = MAGIC1;
43 	uint64_t op2 = MAGIC2;
44 	uint32_t prev_epsw_op1;
45 
46 	/*
47 	 * having machine check interrupts masked and pending CRW ensures
48 	 * EPSW is intercepted under KVM
49 	 */
50 	generate_crw();
51 
52 	report_prefix_push("both operands given");
53 	asm volatile(
54 		"epsw %0, %1\n"
55 		: "+&d" (op1), "+&a" (op2));
56 	report(upper_32_bits(op1) == upper_32_bits(MAGIC1) &&
57 	       upper_32_bits(op2) == upper_32_bits(MAGIC2),
58 	       "upper 32 bits unmodified");
59 	report(lower_32_bits(op1) != lower_32_bits(MAGIC1) &&
60 	       lower_32_bits(op2) != lower_32_bits(MAGIC2),
61 	       "lower 32 bits modified");
62 	prev_epsw_op1 = zero_out_cc_from_epsw_op1(lower_32_bits(op1));
63 	report_prefix_pop();
64 
65 	report_prefix_push("second operand 0");
66 	op1 = MAGIC1;
67 	op2 = MAGIC2;
68 	asm volatile(
69 		"	lgr 0,%[op2]\n"
70 		"	epsw %[op1], 0\n"
71 		"	lgr %[op2],0\n"
72 		: [op2] "+&d" (op2), [op1] "+&a" (op1)
73 		:
74 		: "0");
75 	report(upper_32_bits(op1) == upper_32_bits(MAGIC1),
76 	       "upper 32 bits of first operand unmodified");
77 	report(zero_out_cc_from_epsw_op1(lower_32_bits(op1)) == prev_epsw_op1,
78 	       "first operand matches previous reading");
79 	report(op2 == MAGIC2, "r0 unmodified");
80 	report_prefix_pop();
81 
82 	report_prefix_push("both operands 0");
83 	op1 = MAGIC1;
84 	asm volatile(
85 		"	lgr 0,%[op1]\n"
86 		"	epsw 0, 0\n"
87 		"	lgr %[op1],0\n"
88 		: [op1] "+&d" (op1)
89 		:
90 		: "0");
91 	report(upper_32_bits(op1) == upper_32_bits(MAGIC1),
92 	       "upper 32 bits of first operand unmodified");
93 	report(zero_out_cc_from_epsw_op1(lower_32_bits(op1)) == prev_epsw_op1,
94 	       "first operand matches previous reading");
95 	report_prefix_pop();
96 }
97 
98 int main(int argc, char **argv)
99 {
100 	report_prefix_push("epsw");
101 
102 	if (!host_is_kvm() && !host_is_tcg()) {
103 		report_skip("Not running under QEMU");
104 		goto done;
105 	}
106 
107 	test_epsw();
108 
109 done:
110 	report_prefix_pop();
111 
112 	return report_summary();
113 }
114