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