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 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 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 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 */ 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 */ 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 */ 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 */ 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 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