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