xref: /kvm-unit-tests/s390x/diag308.c (revision e0e5509bc97838de4d74a15aa7137d61d0b7a162)
1 /*
2  * Diagnose 0x308 hypercall tests
3  *
4  * Copyright (c) 2019 Thomas Huth, Red Hat Inc.
5  *
6  * This code is free software; you can redistribute it and/or modify it
7  * under the terms of the GNU General Public License version 2, or (at
8  * your option) any later version.
9  */
10 
11 #include <libcflat.h>
12 #include <asm/asm-offsets.h>
13 #include <asm/interrupt.h>
14 
15 /* The diagnose calls should be blocked in problem state */
16 static void test_priv(void)
17 {
18 	expect_pgm_int();
19 	enter_pstate();
20 	asm volatile ("diag %0,%1,0x308" :: "d"(0), "d"(3));
21 	check_pgm_int_code(PGM_INT_CODE_PRIVILEGED_OPERATION);
22 }
23 
24 /*
25  * Check that diag308 with subcode 1 loads the PSW at address 0, i.e.
26  * that we can put a pointer into address 4 which then gets executed.
27  */
28 static void test_subcode1(void)
29 {
30 	uint64_t saved_psw = *(uint64_t *)0;
31 	long subcode = 1;
32 	long ret, tmp;
33 
34 	asm volatile (
35 		"	epsw	%0,%1\n"
36 		"	st	%0,0\n"
37 		"	larl	%0,0f\n"
38 		"	oilh	%0,0x8000\n"
39 		"	st	%0,4\n"
40 		"	diag	0,%2,0x308\n"
41 		"	lghi	%0,0\n"
42 		"	j	1f\n"
43 		"0:	lghi	%0,1\n"
44 		"1:"
45 		: "=&d"(ret), "=&d"(tmp) : "d"(subcode) : "memory");
46 
47 	*(uint64_t *)0 = saved_psw;
48 
49 	report("load normal reset done", ret == 1);
50 }
51 
52 /* Expect a specification exception when using an uneven register */
53 static void test_uneven_reg(unsigned int subcode)
54 {
55 	register unsigned long sc asm("6") = subcode;
56 	register unsigned long r3 asm("9") = 0x2000;
57 
58 	report_prefix_push("uneven register");
59 	expect_pgm_int();
60 	asm volatile ("diag %0,%1,0x308" :: "d"(r3), "d"(sc));
61 	check_pgm_int_code(PGM_INT_CODE_SPECIFICATION);
62 	report_prefix_pop();
63 }
64 
65 /* Expect a specification exception when using an unaligned address */
66 static void test_unaligned_address(unsigned int subcode)
67 {
68 	register unsigned long sc asm("6") = subcode;
69 	register unsigned long addr asm("8") = 54321;
70 
71 	report_prefix_push("unaligned address");
72 	expect_pgm_int();
73 	asm volatile ("diag %0,%1,0x308" :: "d"(addr), "d"(sc));
74 	check_pgm_int_code(PGM_INT_CODE_SPECIFICATION);
75 	report_prefix_pop();
76 }
77 
78 static void test_subcode5(void)
79 {
80 	test_uneven_reg(5);
81 	test_unaligned_address(5);
82 }
83 
84 static void test_subcode6(void)
85 {
86 	test_uneven_reg(6);
87 	test_unaligned_address(6);
88 }
89 
90 /* Unsupported subcodes should generate a specification exception */
91 static void test_unsupported_subcode(void)
92 {
93 	int subcodes[] = { 2, 0x101, 0xffff, 0x10001, -1 };
94 	int idx;
95 
96 	for (idx = 0; idx < ARRAY_SIZE(subcodes); idx++) {
97 		report_prefix_pushf("0x%04x", subcodes[idx]);
98 		expect_pgm_int();
99 		asm volatile ("diag %0,%1,0x308" :: "d"(0), "d"(subcodes[idx]));
100 		check_pgm_int_code(PGM_INT_CODE_SPECIFICATION);
101 		report_prefix_pop();
102 	}
103 }
104 
105 static struct {
106 	const char *name;
107 	void (*func)(void);
108 } tests[] = {
109 	{ "privileged", test_priv },
110 	{ "subcode 1", test_subcode1 },
111 	{ "subcode 5", test_subcode5 },
112 	{ "subcode 6", test_subcode6 },
113 	{ "unsupported", test_unsupported_subcode },
114 	{ NULL, NULL }
115 };
116 
117 int main(int argc, char**argv)
118 {
119 	int i;
120 
121 	report_prefix_push("diag308");
122 	for (i = 0; tests[i].name; i++) {
123 		report_prefix_push(tests[i].name);
124 		tests[i].func();
125 		report_prefix_pop();
126 	}
127 	report_prefix_pop();
128 
129 	return report_summary();
130 }
131