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