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