xref: /kvm-unit-tests/s390x/diag258.c (revision 1f08a91a41402b0e032ecce8ed1b5952cbfca0ea)
1 /* SPDX-License-Identifier: GPL-2.0-only */
2 /*
3  * Diag 258: Async Page Fault Handler
4  *
5  * Copyright (c) 2024 IBM Corp
6  *
7  * Authors:
8  *  Nico Boehr <nrb@linux.ibm.com>
9  */
10 
11 #include <libcflat.h>
12 #include <asm-generic/barrier.h>
13 #include <asm/asm-offsets.h>
14 #include <asm/interrupt.h>
15 #include <asm/mem.h>
16 #include <asm/pgtable.h>
17 #include <mmu.h>
18 #include <sclp.h>
19 #include <vmalloc.h>
20 
21 static uint8_t prefix_buf[LC_SIZE] __attribute__((aligned(LC_SIZE)));
22 
23 #define __PF_RES_FIELD 0x8000000000000000UL
24 
25 /* copied from Linux arch/s390/mm/pfault.c */
26 struct pfault_refbk {
27 	u16 refdiagc;
28 	u16 reffcode;
29 	u16 refdwlen;
30 	u16 refversn;
31 	u64 refgaddr;
32 	u64 refselmk;
33 	u64 refcmpmk;
34 	u64 reserved;
35 };
36 
37 uint64_t pfault_token = 0x0123fadec0fe3210UL;
38 
39 static struct pfault_refbk pfault_init_refbk __attribute__((aligned(8))) = {
40 	.refdiagc = 0x258,
41 	.reffcode = 0, /* TOKEN */
42 	.refdwlen = sizeof(struct pfault_refbk) / sizeof(uint64_t),
43 	.refversn = 2,
44 	.refgaddr = (u64)&pfault_token,
45 	.refselmk = 1UL << 48,
46 	.refcmpmk = 1UL << 48,
47 	.reserved = __PF_RES_FIELD
48 };
49 
50 static struct pfault_refbk pfault_cancel_refbk __attribute((aligned(8))) = {
51 	.refdiagc = 0x258,
52 	.reffcode = 1, /* CANCEL */
53 	.refdwlen = sizeof(struct pfault_refbk) / sizeof(uint64_t),
54 	.refversn = 2,
55 	.refgaddr = 0,
56 	.refselmk = 0,
57 	.refcmpmk = 0,
58 	.reserved = 0
59 };
60 
diag258(struct pfault_refbk * refbk)61 static inline int diag258(struct pfault_refbk *refbk)
62 {
63 	int rc = -1;
64 
65 	asm volatile(
66 		"	diag	%[refbk],%[rc],0x258\n"
67 		: [rc] "+d" (rc)
68 		: [refbk] "a" (refbk), "m" (*(refbk))
69 		: "cc");
70 	return rc;
71 }
72 
test_priv(void)73 static void test_priv(void)
74 {
75 	report_prefix_push("privileged");
76 	expect_pgm_int();
77 	enter_pstate();
78 	diag258(&pfault_init_refbk);
79 	check_pgm_int_code(PGM_INT_CODE_PRIVILEGED_OPERATION);
80 	report_prefix_pop();
81 }
82 
page_map_outside_real_space(phys_addr_t page_real)83 static void *page_map_outside_real_space(phys_addr_t page_real)
84 {
85 	pgd_t *root = (pgd_t *)(stctg(1) & PAGE_MASK);
86 	void *vaddr = alloc_vpage();
87 
88 	install_page(root, page_real, vaddr);
89 
90 	return vaddr;
91 }
92 
93 /*
94  * Verify that the refbk pointer is a real address and not a virtual
95  * address. This is tested by enabling DAT and establishing a mapping
96  * for the refbk that is outside of the bounds of our (guest-)physical
97  * address space.
98  */
test_refbk_real(void)99 static void test_refbk_real(void)
100 {
101 	struct pfault_refbk *refbk;
102 	void *refbk_page;
103 	pgd_t *root;
104 
105 	report_prefix_push("refbk is real");
106 
107 	/* Set up virtual memory and allocate a physical page for storing the refbk */
108 	setup_vm();
109 	refbk_page = alloc_page();
110 
111 	/* Map refblk page outside of physical memory identity mapping */
112 	root = (pgd_t *)(stctg(1) & PAGE_MASK);
113 	refbk = page_map_outside_real_space(virt_to_pte_phys(root, refbk_page));
114 
115 	/* Assert the mapping really is outside identity mapping */
116 	report_info("refbk is at 0x%lx", (u64)refbk);
117 	report_info("ram size is 0x%lx", get_ram_size());
118 	assert((u64)refbk > get_ram_size());
119 
120 	/* Copy the init refbk to the page */
121 	memcpy(refbk, &pfault_init_refbk, sizeof(struct pfault_refbk));
122 
123 	/* Protect the virtual mapping to avoid diag258 actually doing something */
124 	protect_page(refbk, PAGE_ENTRY_I);
125 
126 	expect_pgm_int();
127 	diag258(refbk);
128 	check_pgm_int_code(PGM_INT_CODE_ADDRESSING);
129 	report_prefix_pop();
130 
131 	free_page(refbk_page);
132 	disable_dat();
133 	irq_set_dat_mode(false, 0);
134 }
135 
136 /*
137  * Verify diag258 correctly applies prefixing.
138  */
test_refbk_prefixing(void)139 static void test_refbk_prefixing(void)
140 {
141 	const size_t lowcore_offset_for_refbk = offsetof(struct lowcore, pad_0x03a0);
142 	struct pfault_refbk *refbk_in_prefix, *refbk_in_reverse_prefix;
143 	uint32_t old_prefix;
144 	uint64_t ry;
145 
146 	report_prefix_push("refbk prefixing");
147 
148 	report_info("refbk at lowcore offset 0x%lx", lowcore_offset_for_refbk);
149 
150 	assert((unsigned long)&prefix_buf < SZ_2G);
151 
152 	memcpy(prefix_buf, 0, LC_SIZE);
153 
154 	/*
155 	 * After the call to set_prefix() below, this will refer to absolute
156 	 * address lowcore_offset_for_refbk (reverse prefixing).
157 	 */
158 	refbk_in_reverse_prefix = (struct pfault_refbk *)(&prefix_buf[0] + lowcore_offset_for_refbk);
159 
160 	/*
161 	 * After the call to set_prefix() below, this will refer to absolute
162 	 * address &prefix_buf[0] + lowcore_offset_for_refbk (forward prefixing).
163 	 */
164 	refbk_in_prefix = (struct pfault_refbk *)OPAQUE_PTR(lowcore_offset_for_refbk);
165 
166 	old_prefix = get_prefix();
167 	set_prefix((uint32_t)(uintptr_t)prefix_buf);
168 
169 	/*
170 	 * If diag258 would not be applying prefixing on access to
171 	 * refbk_in_reverse_prefix correctly, it would access absolute address
172 	 * refbk_in_reverse_prefix (which to us is accessible at real address
173 	 * refbk_in_prefix).
174 	 * Make sure it really fails by putting invalid function code
175 	 * at refbk_in_prefix.
176 	 */
177 	refbk_in_prefix->refdiagc = 0xc0fe;
178 
179 	/*
180 	 * Put a valid refbk at refbk_in_reverse_prefix.
181 	 */
182 	memcpy(refbk_in_reverse_prefix, &pfault_init_refbk, sizeof(pfault_init_refbk));
183 
184 	ry = diag258(refbk_in_reverse_prefix);
185 	report(!ry, "real address refbk accessed");
186 
187 	/*
188 	 * Activating should have worked. Cancel the activation and expect
189 	 * return 0. If activation would not have worked, this should return with
190 	 * 4 (pfault handshaking not active).
191 	 */
192 	ry = diag258(&pfault_cancel_refbk);
193 	report(!ry, "handshaking canceled");
194 
195 	set_prefix(old_prefix);
196 
197 	report_prefix_pop();
198 }
199 
200 /*
201  * Verify that a refbk exceeding physical memory is not accepted, even
202  * when crossing a frame boundary.
203  */
test_refbk_crossing(void)204 static void test_refbk_crossing(void)
205 {
206 	const size_t bytes_in_last_page = 8;
207 	struct pfault_refbk *refbk = (struct pfault_refbk *)(get_ram_size() - bytes_in_last_page);
208 
209 	report_prefix_push("refbk crossing");
210 
211 	report_info("refbk is at 0x%lx", (u64)refbk);
212 	report_info("ram size is 0x%lx", get_ram_size());
213 	assert(sizeof(struct pfault_refbk) > bytes_in_last_page);
214 
215 	/* Copy bytes_in_last_page bytes of the init refbk to the page */
216 	memcpy(refbk, &pfault_init_refbk, bytes_in_last_page);
217 
218 	expect_pgm_int();
219 	diag258(refbk);
220 	check_pgm_int_code(PGM_INT_CODE_ADDRESSING);
221 	report_prefix_pop();
222 }
223 
224 /*
225  * Verify that a refbk with an invalid refdiagc is not accepted.
226  */
test_refbk_invalid_diagcode(void)227 static void test_refbk_invalid_diagcode(void)
228 {
229 	struct pfault_refbk refbk __attribute__((aligned(8))) = pfault_init_refbk;
230 
231 	report_prefix_push("invalid refdiagc");
232 	refbk.refdiagc = 0xc0fe;
233 
234 	expect_pgm_int();
235 	diag258(&refbk);
236 	check_pgm_int_code(PGM_INT_CODE_SPECIFICATION);
237 	report_prefix_pop();
238 }
239 
main(void)240 int main(void)
241 {
242 	report_prefix_push("diag258");
243 
244 	expect_pgm_int();
245 	diag258((struct pfault_refbk *)0xfffffffffffffff0);
246 	if (clear_pgm_int() == PGM_INT_CODE_SPECIFICATION) {
247 		report_skip("diag258 not supported");
248 	} else {
249 		test_priv();
250 		/* Other tests rely on invalid diagcodes doing nothing */
251 		test_refbk_invalid_diagcode();
252 		test_refbk_real();
253 		test_refbk_prefixing();
254 		test_refbk_crossing();
255 	}
256 
257 	report_prefix_pop();
258 	return report_summary();
259 }
260