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