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