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