xref: /kvm-unit-tests/s390x/mvpg.c (revision ccb37496f132d93e748c2a146d48835b7160a0cc)
1 /* SPDX-License-Identifier: GPL-2.0-only */
2 /*
3  * Move Page instruction tests
4  *
5  * Copyright (c) 2021 IBM Corp
6  *
7  * Authors:
8  *  Claudio Imbrenda <imbrenda@linux.ibm.com>
9  */
10 #include <libcflat.h>
11 #include <asm/asm-offsets.h>
12 #include <asm/interrupt.h>
13 #include <asm/pgtable.h>
14 #include <mmu.h>
15 #include <asm/page.h>
16 #include <asm/facility.h>
17 #include <asm/mem.h>
18 #include <alloc_page.h>
19 #include <bitops.h>
20 #include <hardware.h>
21 
22 /* Used to build the appropriate test values for register 0 */
23 #define KFC(x) ((x) << 10)
24 #define CCO 0x100
25 
26 /* How much memory to allocate for the test */
27 #define MEM_ORDER 12
28 /* How many iterations to perform in the loops */
29 #define ITER 8
30 
31 /* Used to generate the simple pattern */
32 #define MAGIC 42
33 
34 static uint8_t source[PAGE_SIZE]  __attribute__((aligned(PAGE_SIZE)));
35 static uint8_t buffer[PAGE_SIZE] __attribute__((aligned(PAGE_SIZE)));
36 
37 /* Keep track of fresh memory */
38 static uint8_t *fresh;
39 
40 static inline int mvpg(unsigned long r0, void *dest, void *src)
41 {
42 	register unsigned long reg0 asm ("0") = r0;
43 	uint64_t bogus_cc = 3;
44 	int cc;
45 
46 	asm volatile("	tmll	%[bogus_cc],3\n"
47 		     "	mvpg    %1,%2\n"
48 		     "	ipm     %0\n"
49 		     "	srl     %0,28"
50 		: "=&d" (cc) : "a" (dest), "a" (src), "d" (reg0), [bogus_cc] "d" (bogus_cc)
51 		: "memory", "cc");
52 	return cc;
53 }
54 
55 /*
56  * Initialize a page with a simple pattern
57  */
58 static void init_page(uint8_t *p)
59 {
60 	int i;
61 
62 	for (i = 0; i < PAGE_SIZE; i++)
63 		p[i] = i + MAGIC;
64 }
65 
66 /*
67  * Check if the given page contains the simple pattern
68  */
69 static int page_ok(const uint8_t *p)
70 {
71 	int i;
72 
73 	for (i = 0; i < PAGE_SIZE; i++)
74 		if (p[i] != (uint8_t)(i + MAGIC))
75 			return 0;
76 	return 1;
77 }
78 
79 /*
80  * Check that the Operand Access Identification matches with the values of
81  * the r1 and r2 fields in the instruction format. The r1 and r2 fields are
82  * in the last byte of the instruction, and the Program Old PSW will point
83  * to the beginning of the instruction after the one that caused the fault
84  * (the fixup code in the interrupt handler takes care of that for
85  * nullifying instructions). Therefore it is enough to compare the byte
86  * before the one contained in the Program Old PSW with the value of the
87  * Operand Access Identification.
88  */
89 static inline bool check_oai(void)
90 {
91 	return *(uint8_t *)(lowcore.pgm_old_psw.addr - 1) == lowcore.op_acc_id;
92 }
93 
94 static void test_exceptions(void)
95 {
96 	int i, expected;
97 
98 	report_prefix_push("exceptions");
99 
100 	/*
101 	 * Key Function Control values 4 and 5 are allowed only in supervisor
102 	 * state, and even then, only if the move-page-and-set-key facility
103 	 * is present (STFLE bit 149)
104 	 */
105 	report_prefix_push("privileged");
106 	if (test_facility(149)) {
107 		expected = PGM_INT_CODE_PRIVILEGED_OPERATION;
108 		for (i = 4; i < 6; i++) {
109 			expect_pgm_int();
110 			enter_pstate();
111 			mvpg(KFC(i), buffer, source);
112 			report(clear_pgm_int() == expected, "Key Function Control value %d", i);
113 		}
114 	} else {
115 		report_skip("Key Function Control value %d", 4);
116 		report_skip("Key Function Control value %d", 5);
117 		i = 4;
118 	}
119 	report_prefix_pop();
120 
121 	/*
122 	 * Invalid values of the Key Function Control, or setting the
123 	 * reserved bits, should result in a specification exception
124 	 */
125 	report_prefix_push("specification");
126 	expected = PGM_INT_CODE_SPECIFICATION;
127 	expect_pgm_int();
128 	mvpg(KFC(3), buffer, source);
129 	report(clear_pgm_int() == expected, "Key Function Control value 3");
130 	for (; i < 32; i++) {
131 		expect_pgm_int();
132 		mvpg(KFC(i), buffer, source);
133 		report(clear_pgm_int() == expected, "Key Function Control value %d", i);
134 	}
135 	report_prefix_pop();
136 
137 	/* Operands outside memory result in addressing exceptions, as usual */
138 	report_prefix_push("addressing");
139 	expected = PGM_INT_CODE_ADDRESSING;
140 	expect_pgm_int();
141 	mvpg(0, buffer, (void *)PAGE_MASK);
142 	report(clear_pgm_int() == expected, "Second operand outside memory");
143 
144 	expect_pgm_int();
145 	mvpg(0, (void *)PAGE_MASK, source);
146 	report(clear_pgm_int() == expected, "First operand outside memory");
147 	report_prefix_pop();
148 
149 	report_prefix_pop();
150 }
151 
152 static void test_success(void)
153 {
154 	int cc;
155 
156 	report_prefix_push("success");
157 	/* Test successful scenarios, both in supervisor and problem state */
158 	cc = mvpg(0, buffer, source);
159 	report(page_ok(buffer) && !cc, "Supervisor state MVPG successful");
160 	memset(buffer, 0xff, PAGE_SIZE);
161 
162 	enter_pstate();
163 	cc = mvpg(0, buffer, source);
164 	leave_pstate();
165 	report(page_ok(buffer) && !cc, "Problem state MVPG successful");
166 
167 	report_prefix_pop();
168 }
169 
170 static void test_small_loop(const void *string)
171 {
172 	uint8_t *dest;
173 	int i, cc;
174 
175 	/* Looping over cold and warm pages helps catch VSIE bugs */
176 	report_prefix_push(string);
177 	dest = fresh;
178 	for (i = 0; i < ITER; i++) {
179 		cc = mvpg(0, fresh, source);
180 		report(page_ok(fresh) && !cc, "cold: %p, %p", source, fresh);
181 		fresh += PAGE_SIZE;
182 	}
183 
184 	for (i = 0; i < ITER; i++) {
185 		memset(dest, 0, PAGE_SIZE);
186 		cc = mvpg(0, dest, source);
187 		report(page_ok(dest) && !cc, "warm: %p, %p", source, dest);
188 		dest += PAGE_SIZE;
189 	}
190 	report_prefix_pop();
191 }
192 
193 static void test_mmu_prot(void)
194 {
195 	int cc;
196 
197 	report_prefix_push("protection");
198 	report_prefix_push("cco=0");
199 
200 	/* MVPG should still succeed when the source is read-only */
201 	protect_page(source, PAGE_ENTRY_P);
202 	cc = mvpg(0, fresh, source);
203 	report(page_ok(fresh) && !cc, "source read only");
204 	unprotect_page(source, PAGE_ENTRY_P);
205 	fresh += PAGE_SIZE;
206 
207 	/*
208 	 * When the source or destination are invalid, a page translation
209 	 * exception should be raised; when the destination is read-only,
210 	 * a protection exception should be raised.
211 	 */
212 	protect_page(fresh, PAGE_ENTRY_P);
213 	expect_pgm_int();
214 	mvpg(0, fresh, source);
215 	report(clear_pgm_int() == PGM_INT_CODE_PROTECTION, "destination read only");
216 	fresh += PAGE_SIZE;
217 
218 	report_prefix_push("source invalid");
219 	protect_page(source, PAGE_ENTRY_I);
220 	lowcore.op_acc_id = 0;
221 	expect_pgm_int();
222 	mvpg(0, fresh, source);
223 	report(clear_pgm_int() == PGM_INT_CODE_PAGE_TRANSLATION, "exception");
224 	unprotect_page(source, PAGE_ENTRY_I);
225 	report(check_oai(), "operand access ident");
226 	report_prefix_pop();
227 	fresh += PAGE_SIZE;
228 
229 	report_prefix_push("destination invalid");
230 	protect_page(fresh, PAGE_ENTRY_I);
231 	lowcore.op_acc_id = 0;
232 	expect_pgm_int();
233 	mvpg(0, fresh, source);
234 	report(clear_pgm_int() == PGM_INT_CODE_PAGE_TRANSLATION, "exception");
235 	report(check_oai(), "operand access ident");
236 	report_prefix_pop();
237 	fresh += PAGE_SIZE;
238 
239 	report_prefix_pop();
240 	report_prefix_push("cco=1");
241 	/*
242 	 * Setting the CCO bit should suppress page translation exceptions,
243 	 * but not protection exceptions.
244 	 */
245 	protect_page(fresh, PAGE_ENTRY_P);
246 	expect_pgm_int();
247 	mvpg(CCO, fresh, source);
248 	report(clear_pgm_int() == PGM_INT_CODE_PROTECTION, "destination read only");
249 	fresh += PAGE_SIZE;
250 
251 	/* Known issue in TCG: CCO flag is not honoured */
252 	if (host_is_tcg()) {
253 		report_prefix_push("TCG");
254 		report_skip("destination invalid");
255 		report_skip("source invalid");
256 		report_skip("source and destination invalid");
257 		report_prefix_pop();
258 	} else {
259 		protect_page(fresh, PAGE_ENTRY_I);
260 		cc = mvpg(CCO, fresh, source);
261 		report(cc == 1, "destination invalid");
262 		fresh += PAGE_SIZE;
263 
264 		protect_page(source, PAGE_ENTRY_I);
265 		cc = mvpg(CCO, fresh, source);
266 		report(cc == 2, "source invalid");
267 		fresh += PAGE_SIZE;
268 
269 		protect_page(fresh, PAGE_ENTRY_I);
270 		cc = mvpg(CCO, fresh, source);
271 		report(cc == 2, "source and destination invalid");
272 		fresh += PAGE_SIZE;
273 	}
274 
275 	unprotect_page(source, PAGE_ENTRY_I);
276 	report_prefix_pop();
277 	report_prefix_pop();
278 }
279 
280 int main(void)
281 {
282 	report_prefix_push("mvpg");
283 
284 	init_page(source);
285 	fresh = alloc_pages_flags(MEM_ORDER, FLAG_DONTZERO | FLAG_FRESH);
286 	assert(fresh);
287 
288 	test_exceptions();
289 	test_success();
290 	test_small_loop("nommu");
291 
292 	setup_vm();
293 
294 	test_small_loop("mmu");
295 	test_mmu_prot();
296 
297 	report_prefix_pop();
298 	return report_summary();
299 }
300