1213d110cSClaudio Imbrenda /* SPDX-License-Identifier: GPL-2.0-only */
2213d110cSClaudio Imbrenda /*
3213d110cSClaudio Imbrenda * Move Page instruction tests
4213d110cSClaudio Imbrenda *
5213d110cSClaudio Imbrenda * Copyright (c) 2021 IBM Corp
6213d110cSClaudio Imbrenda *
7213d110cSClaudio Imbrenda * Authors:
8213d110cSClaudio Imbrenda * Claudio Imbrenda <imbrenda@linux.ibm.com>
9213d110cSClaudio Imbrenda */
10213d110cSClaudio Imbrenda #include <libcflat.h>
11213d110cSClaudio Imbrenda #include <asm/asm-offsets.h>
12213d110cSClaudio Imbrenda #include <asm/interrupt.h>
13213d110cSClaudio Imbrenda #include <asm/pgtable.h>
14213d110cSClaudio Imbrenda #include <mmu.h>
15213d110cSClaudio Imbrenda #include <asm/page.h>
16213d110cSClaudio Imbrenda #include <asm/facility.h>
17213d110cSClaudio Imbrenda #include <asm/mem.h>
18*95a94088SNicholas Piggin #include <vmalloc.h>
19213d110cSClaudio Imbrenda #include <alloc_page.h>
20213d110cSClaudio Imbrenda #include <bitops.h>
21f489dfa5SClaudio Imbrenda #include <hardware.h>
22213d110cSClaudio Imbrenda
23213d110cSClaudio Imbrenda /* Used to build the appropriate test values for register 0 */
24213d110cSClaudio Imbrenda #define KFC(x) ((x) << 10)
25213d110cSClaudio Imbrenda #define CCO 0x100
26213d110cSClaudio Imbrenda
27213d110cSClaudio Imbrenda /* How much memory to allocate for the test */
28213d110cSClaudio Imbrenda #define MEM_ORDER 12
29213d110cSClaudio Imbrenda /* How many iterations to perform in the loops */
30213d110cSClaudio Imbrenda #define ITER 8
31213d110cSClaudio Imbrenda
32213d110cSClaudio Imbrenda /* Used to generate the simple pattern */
33213d110cSClaudio Imbrenda #define MAGIC 42
34213d110cSClaudio Imbrenda
35213d110cSClaudio Imbrenda static uint8_t source[PAGE_SIZE] __attribute__((aligned(PAGE_SIZE)));
36213d110cSClaudio Imbrenda static uint8_t buffer[PAGE_SIZE] __attribute__((aligned(PAGE_SIZE)));
37213d110cSClaudio Imbrenda
38213d110cSClaudio Imbrenda /* Keep track of fresh memory */
39213d110cSClaudio Imbrenda static uint8_t *fresh;
40213d110cSClaudio Imbrenda
mvpg(unsigned long r0,void * dest,void * src)41213d110cSClaudio Imbrenda static inline int mvpg(unsigned long r0, void *dest, void *src)
42213d110cSClaudio Imbrenda {
43213d110cSClaudio Imbrenda register unsigned long reg0 asm ("0") = r0;
449946f192SJanosch Frank uint64_t bogus_cc = 3;
45213d110cSClaudio Imbrenda int cc;
46213d110cSClaudio Imbrenda
479946f192SJanosch Frank asm volatile(" tmll %[bogus_cc],3\n"
489946f192SJanosch Frank " mvpg %1,%2\n"
49213d110cSClaudio Imbrenda " ipm %0\n"
50213d110cSClaudio Imbrenda " srl %0,28"
519946f192SJanosch Frank : "=&d" (cc) : "a" (dest), "a" (src), "d" (reg0), [bogus_cc] "d" (bogus_cc)
52213d110cSClaudio Imbrenda : "memory", "cc");
53213d110cSClaudio Imbrenda return cc;
54213d110cSClaudio Imbrenda }
55213d110cSClaudio Imbrenda
56213d110cSClaudio Imbrenda /*
57213d110cSClaudio Imbrenda * Initialize a page with a simple pattern
58213d110cSClaudio Imbrenda */
init_page(uint8_t * p)59213d110cSClaudio Imbrenda static void init_page(uint8_t *p)
60213d110cSClaudio Imbrenda {
61213d110cSClaudio Imbrenda int i;
62213d110cSClaudio Imbrenda
63213d110cSClaudio Imbrenda for (i = 0; i < PAGE_SIZE; i++)
64213d110cSClaudio Imbrenda p[i] = i + MAGIC;
65213d110cSClaudio Imbrenda }
66213d110cSClaudio Imbrenda
67213d110cSClaudio Imbrenda /*
68213d110cSClaudio Imbrenda * Check if the given page contains the simple pattern
69213d110cSClaudio Imbrenda */
page_ok(const uint8_t * p)70213d110cSClaudio Imbrenda static int page_ok(const uint8_t *p)
71213d110cSClaudio Imbrenda {
72213d110cSClaudio Imbrenda int i;
73213d110cSClaudio Imbrenda
74213d110cSClaudio Imbrenda for (i = 0; i < PAGE_SIZE; i++)
75213d110cSClaudio Imbrenda if (p[i] != (uint8_t)(i + MAGIC))
76213d110cSClaudio Imbrenda return 0;
77213d110cSClaudio Imbrenda return 1;
78213d110cSClaudio Imbrenda }
79213d110cSClaudio Imbrenda
80f65b8467SClaudio Imbrenda /*
81f65b8467SClaudio Imbrenda * Check that the Operand Access Identification matches with the values of
82f65b8467SClaudio Imbrenda * the r1 and r2 fields in the instruction format. The r1 and r2 fields are
83f65b8467SClaudio Imbrenda * in the last byte of the instruction, and the Program Old PSW will point
84f65b8467SClaudio Imbrenda * to the beginning of the instruction after the one that caused the fault
85f65b8467SClaudio Imbrenda * (the fixup code in the interrupt handler takes care of that for
86f65b8467SClaudio Imbrenda * nullifying instructions). Therefore it is enough to compare the byte
87f65b8467SClaudio Imbrenda * before the one contained in the Program Old PSW with the value of the
88f65b8467SClaudio Imbrenda * Operand Access Identification.
89f65b8467SClaudio Imbrenda */
check_oai(void)90f65b8467SClaudio Imbrenda static inline bool check_oai(void)
91f65b8467SClaudio Imbrenda {
92cd719531SJanis Schoetterl-Glausch return *(uint8_t *)(lowcore.pgm_old_psw.addr - 1) == lowcore.op_acc_id;
93f65b8467SClaudio Imbrenda }
94f65b8467SClaudio Imbrenda
test_exceptions(void)95213d110cSClaudio Imbrenda static void test_exceptions(void)
96213d110cSClaudio Imbrenda {
97213d110cSClaudio Imbrenda int i, expected;
98213d110cSClaudio Imbrenda
99213d110cSClaudio Imbrenda report_prefix_push("exceptions");
100213d110cSClaudio Imbrenda
101213d110cSClaudio Imbrenda /*
102213d110cSClaudio Imbrenda * Key Function Control values 4 and 5 are allowed only in supervisor
103213d110cSClaudio Imbrenda * state, and even then, only if the move-page-and-set-key facility
104213d110cSClaudio Imbrenda * is present (STFLE bit 149)
105213d110cSClaudio Imbrenda */
106213d110cSClaudio Imbrenda report_prefix_push("privileged");
107213d110cSClaudio Imbrenda if (test_facility(149)) {
108213d110cSClaudio Imbrenda expected = PGM_INT_CODE_PRIVILEGED_OPERATION;
109213d110cSClaudio Imbrenda for (i = 4; i < 6; i++) {
110213d110cSClaudio Imbrenda expect_pgm_int();
111213d110cSClaudio Imbrenda enter_pstate();
112213d110cSClaudio Imbrenda mvpg(KFC(i), buffer, source);
113213d110cSClaudio Imbrenda report(clear_pgm_int() == expected, "Key Function Control value %d", i);
114213d110cSClaudio Imbrenda }
115213d110cSClaudio Imbrenda } else {
116213d110cSClaudio Imbrenda report_skip("Key Function Control value %d", 4);
117213d110cSClaudio Imbrenda report_skip("Key Function Control value %d", 5);
118213d110cSClaudio Imbrenda i = 4;
119213d110cSClaudio Imbrenda }
120213d110cSClaudio Imbrenda report_prefix_pop();
121213d110cSClaudio Imbrenda
122213d110cSClaudio Imbrenda /*
123213d110cSClaudio Imbrenda * Invalid values of the Key Function Control, or setting the
124213d110cSClaudio Imbrenda * reserved bits, should result in a specification exception
125213d110cSClaudio Imbrenda */
126213d110cSClaudio Imbrenda report_prefix_push("specification");
127213d110cSClaudio Imbrenda expected = PGM_INT_CODE_SPECIFICATION;
128213d110cSClaudio Imbrenda expect_pgm_int();
129213d110cSClaudio Imbrenda mvpg(KFC(3), buffer, source);
130213d110cSClaudio Imbrenda report(clear_pgm_int() == expected, "Key Function Control value 3");
131213d110cSClaudio Imbrenda for (; i < 32; i++) {
132213d110cSClaudio Imbrenda expect_pgm_int();
133213d110cSClaudio Imbrenda mvpg(KFC(i), buffer, source);
134213d110cSClaudio Imbrenda report(clear_pgm_int() == expected, "Key Function Control value %d", i);
135213d110cSClaudio Imbrenda }
136213d110cSClaudio Imbrenda report_prefix_pop();
137213d110cSClaudio Imbrenda
138213d110cSClaudio Imbrenda /* Operands outside memory result in addressing exceptions, as usual */
139213d110cSClaudio Imbrenda report_prefix_push("addressing");
140213d110cSClaudio Imbrenda expected = PGM_INT_CODE_ADDRESSING;
141213d110cSClaudio Imbrenda expect_pgm_int();
142213d110cSClaudio Imbrenda mvpg(0, buffer, (void *)PAGE_MASK);
143213d110cSClaudio Imbrenda report(clear_pgm_int() == expected, "Second operand outside memory");
144213d110cSClaudio Imbrenda
145213d110cSClaudio Imbrenda expect_pgm_int();
146213d110cSClaudio Imbrenda mvpg(0, (void *)PAGE_MASK, source);
147213d110cSClaudio Imbrenda report(clear_pgm_int() == expected, "First operand outside memory");
148213d110cSClaudio Imbrenda report_prefix_pop();
149213d110cSClaudio Imbrenda
150213d110cSClaudio Imbrenda report_prefix_pop();
151213d110cSClaudio Imbrenda }
152213d110cSClaudio Imbrenda
test_success(void)153213d110cSClaudio Imbrenda static void test_success(void)
154213d110cSClaudio Imbrenda {
155213d110cSClaudio Imbrenda int cc;
156213d110cSClaudio Imbrenda
157213d110cSClaudio Imbrenda report_prefix_push("success");
158213d110cSClaudio Imbrenda /* Test successful scenarios, both in supervisor and problem state */
159213d110cSClaudio Imbrenda cc = mvpg(0, buffer, source);
160213d110cSClaudio Imbrenda report(page_ok(buffer) && !cc, "Supervisor state MVPG successful");
161213d110cSClaudio Imbrenda memset(buffer, 0xff, PAGE_SIZE);
162213d110cSClaudio Imbrenda
163213d110cSClaudio Imbrenda enter_pstate();
164213d110cSClaudio Imbrenda cc = mvpg(0, buffer, source);
165213d110cSClaudio Imbrenda leave_pstate();
166213d110cSClaudio Imbrenda report(page_ok(buffer) && !cc, "Problem state MVPG successful");
167213d110cSClaudio Imbrenda
168213d110cSClaudio Imbrenda report_prefix_pop();
169213d110cSClaudio Imbrenda }
170213d110cSClaudio Imbrenda
test_small_loop(const void * string)171213d110cSClaudio Imbrenda static void test_small_loop(const void *string)
172213d110cSClaudio Imbrenda {
173213d110cSClaudio Imbrenda uint8_t *dest;
174213d110cSClaudio Imbrenda int i, cc;
175213d110cSClaudio Imbrenda
176213d110cSClaudio Imbrenda /* Looping over cold and warm pages helps catch VSIE bugs */
177213d110cSClaudio Imbrenda report_prefix_push(string);
178213d110cSClaudio Imbrenda dest = fresh;
179213d110cSClaudio Imbrenda for (i = 0; i < ITER; i++) {
180213d110cSClaudio Imbrenda cc = mvpg(0, fresh, source);
181213d110cSClaudio Imbrenda report(page_ok(fresh) && !cc, "cold: %p, %p", source, fresh);
182213d110cSClaudio Imbrenda fresh += PAGE_SIZE;
183213d110cSClaudio Imbrenda }
184213d110cSClaudio Imbrenda
185213d110cSClaudio Imbrenda for (i = 0; i < ITER; i++) {
186213d110cSClaudio Imbrenda memset(dest, 0, PAGE_SIZE);
187213d110cSClaudio Imbrenda cc = mvpg(0, dest, source);
188213d110cSClaudio Imbrenda report(page_ok(dest) && !cc, "warm: %p, %p", source, dest);
189213d110cSClaudio Imbrenda dest += PAGE_SIZE;
190213d110cSClaudio Imbrenda }
191213d110cSClaudio Imbrenda report_prefix_pop();
192213d110cSClaudio Imbrenda }
193213d110cSClaudio Imbrenda
test_mmu_prot(void)194213d110cSClaudio Imbrenda static void test_mmu_prot(void)
195213d110cSClaudio Imbrenda {
196213d110cSClaudio Imbrenda int cc;
197213d110cSClaudio Imbrenda
198213d110cSClaudio Imbrenda report_prefix_push("protection");
199213d110cSClaudio Imbrenda report_prefix_push("cco=0");
200213d110cSClaudio Imbrenda
201213d110cSClaudio Imbrenda /* MVPG should still succeed when the source is read-only */
202213d110cSClaudio Imbrenda protect_page(source, PAGE_ENTRY_P);
203213d110cSClaudio Imbrenda cc = mvpg(0, fresh, source);
204213d110cSClaudio Imbrenda report(page_ok(fresh) && !cc, "source read only");
205213d110cSClaudio Imbrenda unprotect_page(source, PAGE_ENTRY_P);
206213d110cSClaudio Imbrenda fresh += PAGE_SIZE;
207213d110cSClaudio Imbrenda
208213d110cSClaudio Imbrenda /*
209213d110cSClaudio Imbrenda * When the source or destination are invalid, a page translation
210213d110cSClaudio Imbrenda * exception should be raised; when the destination is read-only,
211213d110cSClaudio Imbrenda * a protection exception should be raised.
212213d110cSClaudio Imbrenda */
213213d110cSClaudio Imbrenda protect_page(fresh, PAGE_ENTRY_P);
214213d110cSClaudio Imbrenda expect_pgm_int();
215213d110cSClaudio Imbrenda mvpg(0, fresh, source);
216213d110cSClaudio Imbrenda report(clear_pgm_int() == PGM_INT_CODE_PROTECTION, "destination read only");
217213d110cSClaudio Imbrenda fresh += PAGE_SIZE;
218213d110cSClaudio Imbrenda
219f65b8467SClaudio Imbrenda report_prefix_push("source invalid");
220213d110cSClaudio Imbrenda protect_page(source, PAGE_ENTRY_I);
221cd719531SJanis Schoetterl-Glausch lowcore.op_acc_id = 0;
222213d110cSClaudio Imbrenda expect_pgm_int();
223213d110cSClaudio Imbrenda mvpg(0, fresh, source);
224f65b8467SClaudio Imbrenda report(clear_pgm_int() == PGM_INT_CODE_PAGE_TRANSLATION, "exception");
225213d110cSClaudio Imbrenda unprotect_page(source, PAGE_ENTRY_I);
226f65b8467SClaudio Imbrenda report(check_oai(), "operand access ident");
227f65b8467SClaudio Imbrenda report_prefix_pop();
228213d110cSClaudio Imbrenda fresh += PAGE_SIZE;
229213d110cSClaudio Imbrenda
230f65b8467SClaudio Imbrenda report_prefix_push("destination invalid");
231213d110cSClaudio Imbrenda protect_page(fresh, PAGE_ENTRY_I);
232cd719531SJanis Schoetterl-Glausch lowcore.op_acc_id = 0;
233213d110cSClaudio Imbrenda expect_pgm_int();
234213d110cSClaudio Imbrenda mvpg(0, fresh, source);
235f65b8467SClaudio Imbrenda report(clear_pgm_int() == PGM_INT_CODE_PAGE_TRANSLATION, "exception");
236f65b8467SClaudio Imbrenda report(check_oai(), "operand access ident");
237f65b8467SClaudio Imbrenda report_prefix_pop();
238213d110cSClaudio Imbrenda fresh += PAGE_SIZE;
239213d110cSClaudio Imbrenda
240213d110cSClaudio Imbrenda report_prefix_pop();
241213d110cSClaudio Imbrenda report_prefix_push("cco=1");
242213d110cSClaudio Imbrenda /*
243213d110cSClaudio Imbrenda * Setting the CCO bit should suppress page translation exceptions,
244213d110cSClaudio Imbrenda * but not protection exceptions.
245213d110cSClaudio Imbrenda */
246213d110cSClaudio Imbrenda protect_page(fresh, PAGE_ENTRY_P);
247213d110cSClaudio Imbrenda expect_pgm_int();
248213d110cSClaudio Imbrenda mvpg(CCO, fresh, source);
249213d110cSClaudio Imbrenda report(clear_pgm_int() == PGM_INT_CODE_PROTECTION, "destination read only");
250213d110cSClaudio Imbrenda fresh += PAGE_SIZE;
251213d110cSClaudio Imbrenda
252b7f0f9a2SClaudio Imbrenda /* Known issue in TCG: CCO flag is not honoured */
253f489dfa5SClaudio Imbrenda if (host_is_tcg()) {
254b7f0f9a2SClaudio Imbrenda report_prefix_push("TCG");
255b7f0f9a2SClaudio Imbrenda report_skip("destination invalid");
256b7f0f9a2SClaudio Imbrenda report_skip("source invalid");
257b7f0f9a2SClaudio Imbrenda report_skip("source and destination invalid");
258b7f0f9a2SClaudio Imbrenda report_prefix_pop();
259b7f0f9a2SClaudio Imbrenda } else {
260213d110cSClaudio Imbrenda protect_page(fresh, PAGE_ENTRY_I);
261213d110cSClaudio Imbrenda cc = mvpg(CCO, fresh, source);
262213d110cSClaudio Imbrenda report(cc == 1, "destination invalid");
263213d110cSClaudio Imbrenda fresh += PAGE_SIZE;
264213d110cSClaudio Imbrenda
265213d110cSClaudio Imbrenda protect_page(source, PAGE_ENTRY_I);
266213d110cSClaudio Imbrenda cc = mvpg(CCO, fresh, source);
267213d110cSClaudio Imbrenda report(cc == 2, "source invalid");
268213d110cSClaudio Imbrenda fresh += PAGE_SIZE;
269213d110cSClaudio Imbrenda
270213d110cSClaudio Imbrenda protect_page(fresh, PAGE_ENTRY_I);
271213d110cSClaudio Imbrenda cc = mvpg(CCO, fresh, source);
272213d110cSClaudio Imbrenda report(cc == 2, "source and destination invalid");
273213d110cSClaudio Imbrenda fresh += PAGE_SIZE;
274b7f0f9a2SClaudio Imbrenda }
275213d110cSClaudio Imbrenda
276213d110cSClaudio Imbrenda unprotect_page(source, PAGE_ENTRY_I);
277213d110cSClaudio Imbrenda report_prefix_pop();
278213d110cSClaudio Imbrenda report_prefix_pop();
279213d110cSClaudio Imbrenda }
280213d110cSClaudio Imbrenda
main(void)281213d110cSClaudio Imbrenda int main(void)
282213d110cSClaudio Imbrenda {
283213d110cSClaudio Imbrenda report_prefix_push("mvpg");
284213d110cSClaudio Imbrenda
285213d110cSClaudio Imbrenda init_page(source);
286213d110cSClaudio Imbrenda fresh = alloc_pages_flags(MEM_ORDER, FLAG_DONTZERO | FLAG_FRESH);
287213d110cSClaudio Imbrenda assert(fresh);
288213d110cSClaudio Imbrenda
289213d110cSClaudio Imbrenda test_exceptions();
290213d110cSClaudio Imbrenda test_success();
291213d110cSClaudio Imbrenda test_small_loop("nommu");
292213d110cSClaudio Imbrenda
293213d110cSClaudio Imbrenda setup_vm();
294213d110cSClaudio Imbrenda
295213d110cSClaudio Imbrenda test_small_loop("mmu");
296213d110cSClaudio Imbrenda test_mmu_prot();
297213d110cSClaudio Imbrenda
298213d110cSClaudio Imbrenda report_prefix_pop();
299213d110cSClaudio Imbrenda return report_summary();
300213d110cSClaudio Imbrenda }
301