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