xref: /kvm-unit-tests/s390x/emulator.c (revision be704aff683c54fc108deaafacc7cb89ad0648d9)
1 /*
2  * Emulator tests - for s390x CPU instructions that are usually interpreted
3  *                  by the hardware
4  *
5  * Copyright (c) 2017 Red Hat Inc
6  *
7  * Authors:
8  *  David Hildenbrand <david@redhat.com>
9  *
10  * This code is free software; you can redistribute it and/or modify it
11  * under the terms of the GNU Library General Public License version 2.
12  */
13 #include <libcflat.h>
14 #include <asm/cpacf.h>
15 #include <asm/interrupt.h>
16 #include <asm/float.h>
17 #include <linux/compiler.h>
18 
19 struct lowcore *lc = NULL;
20 
21 static inline void __test_spm_ipm(uint8_t cc, uint8_t key)
22 {
23 	uint64_t in = (cc << 28) | (key << 24);
24 	uint64_t out = ~0ULL;
25 
26 	report_prefix_pushf("cc=%d,key=%x", cc, key);
27 
28 	asm volatile ("spm %1\n"
29 		      "ipm %0\n"
30 		      : "+r"(out) : "r"(in) : "cc");
31 
32 	report(!(out & 0xc0000000UL), "bit 32 and 33 set to zero");
33 	report((out & ~0xff000000ULL) == ~0xff000000ULL,
34 	       "bit 0-31, 40-63 unchanged");
35 	report(!((in ^ out) & 0x3f000000UL), "cc and key applied");
36 
37 	report_prefix_pop();
38 }
39 
40 /* Test the SET PROGRAM PARAMETER and INSERT PROGRAM PARAMETER instruction */
41 static void test_spm_ipm(void)
42 {
43 	__test_spm_ipm(0, 0xf);
44 	__test_spm_ipm(1, 0x9);
45 	__test_spm_ipm(2, 0x5);
46 	__test_spm_ipm(3, 0x3);
47 	__test_spm_ipm(0, 0);
48 }
49 
50 static __always_inline void __test_cpacf(unsigned int opcode, unsigned long func,
51 				unsigned int r1, unsigned int r2,
52 				unsigned int r3)
53 {
54 	register unsigned long gr0 asm("0") = func;
55 	cpacf_mask_t mask;
56 	register unsigned long gr1 asm("1") = (unsigned long)&mask;
57 
58 	asm volatile(".insn rrf,%[opc] << 16,%[r1],%[r2],%[r3],0\n"
59 		     : : "d" (gr0), "d" (gr1), [opc] "i" (opcode),
60 		         [r1] "i" (r1), [r2] "i" (r2), [r3] "i" (r3));
61 }
62 
63 static __always_inline void __test_cpacf_r1_odd(unsigned int opcode)
64 {
65 	report_prefix_push("r1 odd");
66 	expect_pgm_int();
67 	__test_cpacf(opcode, 0, 1, 4, 6);
68 	check_pgm_int_code(PGM_INT_CODE_SPECIFICATION);
69 	report_prefix_pop();
70 }
71 
72 static __always_inline void __test_cpacf_r1_null(unsigned int opcode)
73 {
74 	report_prefix_push("r1 null");
75 	expect_pgm_int();
76 	__test_cpacf(opcode, 0, 0, 4, 6);
77 	check_pgm_int_code(PGM_INT_CODE_SPECIFICATION);
78 	report_prefix_pop();
79 }
80 
81 static __always_inline void __test_cpacf_r2_odd(unsigned int opcode)
82 {
83 	report_prefix_push("r2 odd");
84 	expect_pgm_int();
85 	__test_cpacf(opcode, 0, 2, 3, 6);
86 	check_pgm_int_code(PGM_INT_CODE_SPECIFICATION);
87 	report_prefix_pop();
88 }
89 
90 static __always_inline void __test_cpacf_r2_null(unsigned int opcode)
91 {
92 	report_prefix_push("r2 null");
93 	expect_pgm_int();
94 	__test_cpacf(opcode, 0, 2, 0, 6);
95 	check_pgm_int_code(PGM_INT_CODE_SPECIFICATION);
96 	report_prefix_pop();
97 }
98 
99 static __always_inline void __test_cpacf_r3_odd(unsigned int opcode)
100 {
101 	report_prefix_push("r3 odd");
102 	expect_pgm_int();
103 	__test_cpacf(opcode, 0, 2, 4, 5);
104 	check_pgm_int_code(PGM_INT_CODE_SPECIFICATION);
105 	report_prefix_pop();
106 }
107 
108 static __always_inline void __test_cpacf_r3_null(unsigned int opcode)
109 {
110 	report_prefix_push("r3 null");
111 	expect_pgm_int();
112 	__test_cpacf(opcode, 0, 2, 4, 0);
113 	check_pgm_int_code(PGM_INT_CODE_SPECIFICATION);
114 	report_prefix_pop();
115 }
116 
117 static __always_inline void __test_cpacf_mod_bit(unsigned int opcode)
118 {
119 	report_prefix_push("mod bit");
120 	expect_pgm_int();
121 	__test_cpacf(opcode, CPACF_DECRYPT, 2, 4, 6);
122 	check_pgm_int_code(PGM_INT_CODE_SPECIFICATION);
123 	report_prefix_pop();
124 }
125 
126 static __always_inline void __test_cpacf_invalid_func(unsigned int opcode)
127 {
128 	report_prefix_push("invalid subfunction");
129 	expect_pgm_int();
130 	/* 127 is unassigned for now. We don't simply use any, as HW
131 	 * might simply mask valid codes in query but they might still work */
132 	if (cpacf_query_func(opcode, 127)) {
133 		report_skip("127 not invalid");
134 	} else {
135 		__test_cpacf(opcode, 127, 2, 4, 6);
136 	}
137 	check_pgm_int_code(PGM_INT_CODE_SPECIFICATION);
138 	report_prefix_pop();
139 }
140 
141 static __always_inline void __test_cpacf_invalid_parm(unsigned int opcode)
142 {
143 	report_prefix_push("invalid parm address");
144 	expect_pgm_int();
145 	__cpacf_query(opcode, (void *) -1);
146 	check_pgm_int_code(PGM_INT_CODE_ADDRESSING);
147 	report_prefix_pop();
148 }
149 
150 static __always_inline void __test_cpacf_protected_parm(unsigned int opcode)
151 {
152 	report_prefix_push("protected parm address");
153 	expect_pgm_int();
154 	low_prot_enable();
155 	__cpacf_query(opcode, (void *) 8);
156 	low_prot_disable();
157 	check_pgm_int_code(PGM_INT_CODE_PROTECTION);
158 	report_prefix_pop();
159 }
160 
161 static __always_inline void __test_basic_cpacf_opcode(unsigned int opcode)
162 {
163 	bool mod_bit_allowed = false;
164 
165 	if (!__cpacf_check_opcode(opcode)) {
166 		report_skip("not available");
167 		return;
168 	}
169 	report(cpacf_query_func(opcode, 0), "query indicated in query");
170 
171 	switch (opcode) {
172 	case CPACF_KMCTR:
173 		__test_cpacf_r3_odd(opcode);
174 		__test_cpacf_r3_null(opcode);
175 		/* FALL THROUGH */
176 	case CPACF_PRNO:
177 	case CPACF_KMF:
178 	case CPACF_KMC:
179 	case CPACF_KMO:
180 	case CPACF_KM:
181 		__test_cpacf_r1_odd(opcode);
182 		__test_cpacf_r1_null(opcode);
183 		mod_bit_allowed = true;
184 		/* FALL THROUGH */
185 	case CPACF_KMAC:
186 	case CPACF_KIMD:
187 	case CPACF_KLMD:
188 		__test_cpacf_r2_odd(opcode);
189 		__test_cpacf_r2_null(opcode);
190 	        break;
191 	}
192 	if (!mod_bit_allowed)
193 		__test_cpacf_mod_bit(opcode);
194 	__test_cpacf_invalid_func(opcode);
195 	__test_cpacf_invalid_parm(opcode);
196 	__test_cpacf_protected_parm(opcode);
197 }
198 
199 /* COMPUTE MESSAGE AUTHENTICATION CODE */
200 static void test_kmac(void)
201 {
202 	__test_basic_cpacf_opcode(CPACF_KMAC);
203 }
204 
205 /* CIPHER MESSAGE */
206 static void test_km(void)
207 {
208 	__test_basic_cpacf_opcode(CPACF_KM);
209 }
210 /* CIPHER MESSAGE WITH CHAINING */
211 static void test_kmc(void)
212 {
213 	__test_basic_cpacf_opcode(CPACF_KMC);
214 }
215 
216 /* COMPUTE INTERMEDIATE MESSAGE DIGEST */
217 static void test_kimd(void)
218 {
219 	__test_basic_cpacf_opcode(CPACF_KIMD);
220 }
221 
222 /* COMPUTE LAST MESSAGE DIGEST */
223 static void test_klmd(void)
224 {
225 	__test_basic_cpacf_opcode(CPACF_KLMD);
226 }
227 
228 /* PERFORM CRYPTOGRAPHIC KEY MANAGEMENT OPERATION */
229 static void test_pckmo(void)
230 {
231 	__test_basic_cpacf_opcode(CPACF_PCKMO);
232 }
233 
234 /* CIPHER MESSAGE WITH CIPHER FEEDBACK */
235 static void test_kmf(void)
236 {
237 	__test_basic_cpacf_opcode(CPACF_KMF);
238 }
239 
240 /* PERFORM CRYPTOGRAPHIC KEY MANAGEMENT OPERATION */
241 static void test_kmo(void)
242 {
243 	__test_basic_cpacf_opcode(CPACF_KMO);
244 }
245 
246 /* PERFORM CRYPTOGRAPHIC COMPUTATION */
247 static void test_pcc(void)
248 {
249 	__test_basic_cpacf_opcode(CPACF_PCC);
250 }
251 
252 /* CIPHER MESSAGE WITH COUNTER */
253 static void test_kmctr(void)
254 {
255 	__test_basic_cpacf_opcode(CPACF_KMCTR);
256 }
257 
258 /* PERFORM RANDOM NUMBER OPERATION (formerly PPNO) */
259 static void test_prno(void)
260 {
261 	__test_basic_cpacf_opcode(CPACF_PRNO);
262 }
263 
264 static void test_dxc(void)
265 {
266 	/* DXC (0xff) is to be stored in LC and FPC on a trap (CRT) with AFP */
267 	lc->dxc_vxc = 0x12345678;
268 	set_fpc_dxc(0);
269 
270 	report_prefix_push("afp");
271 	expect_pgm_int();
272 	asm volatile("	.insn	rrf,0xb9600000,%0,%0,8,0\n"
273 		     : : "r"(0) : "memory");
274 	check_pgm_int_code(PGM_INT_CODE_DATA);
275 
276 	report(lc->dxc_vxc == 0xff, "dxc in LC");
277 	report(get_fpc_dxc() == 0xff, "dxc in FPC");
278 	report_prefix_pop();
279 
280 	/* DXC (0xff) is to be stored in LC only on a trap (CRT) without AFP */
281 	lc->dxc_vxc = 0x12345678;
282 	set_fpc_dxc(0);
283 
284 	report_prefix_push("no-afp");
285 	expect_pgm_int();
286 	/* temporarily disable AFP */
287 	afp_disable();
288 	asm volatile("	.insn	rrf,0xb9600000,%0,%0,8,0\n"
289 		     : : "r"(0) : "memory");
290 	afp_enable();
291 	check_pgm_int_code(PGM_INT_CODE_DATA);
292 
293 	report(lc->dxc_vxc == 0xff, "dxc in LC");
294 	report(get_fpc_dxc() == 0, "dxc not in FPC");
295 	report_prefix_pop();
296 }
297 
298 static struct {
299 	const char *name;
300 	void (*func)(void);
301 } tests[] = {
302 	{ "spm/ipm", test_spm_ipm },
303 	{ "kmac", test_kmac },
304 	{ "km", test_km },
305 	{ "kmc", test_kmc },
306 	{ "kimd", test_kimd },
307 	{ "klmd", test_klmd },
308 	{ "pckmo", test_pckmo },
309 	{ "kmf", test_kmf },
310 	{ "kmo", test_kmo },
311 	{ "pcc", test_pcc },
312 	{ "kmctr", test_kmctr },
313 	{ "prno", test_prno },
314 	{ "dxc", test_dxc },
315 	{ NULL, NULL }
316 };
317 
318 int main(int argc, char**argv)
319 {
320 	int i;
321 
322 	report_prefix_push("emulator");
323 	for (i = 0; tests[i].name; i++) {
324 		report_prefix_push(tests[i].name);
325 		tests[i].func();
326 		report_prefix_pop();
327 	}
328 	report_prefix_pop();
329 
330 	return report_summary();
331 }
332