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