1 /* SPDX-License-Identifier: GPL-2.0-only */ 2 /* 3 * Move Page instruction tests 4 * 5 * Copyright (c) 2021 IBM Corp 6 * 7 * Authors: 8 * Claudio Imbrenda <imbrenda@linux.ibm.com> 9 */ 10 #include <libcflat.h> 11 #include <asm/asm-offsets.h> 12 #include <asm-generic/barrier.h> 13 #include <asm/interrupt.h> 14 #include <asm/pgtable.h> 15 #include <mmu.h> 16 #include <asm/page.h> 17 #include <asm/facility.h> 18 #include <asm/mem.h> 19 #include <asm/sigp.h> 20 #include <smp.h> 21 #include <alloc_page.h> 22 #include <bitops.h> 23 #include <vm.h> 24 25 /* Used to build the appropriate test values for register 0 */ 26 #define KFC(x) ((x) << 10) 27 #define CCO 0x100 28 29 /* How much memory to allocate for the test */ 30 #define MEM_ORDER 12 31 /* How many iterations to perform in the loops */ 32 #define ITER 8 33 34 /* Used to generate the simple pattern */ 35 #define MAGIC 42 36 37 static uint8_t source[PAGE_SIZE] __attribute__((aligned(PAGE_SIZE))); 38 static uint8_t buffer[PAGE_SIZE] __attribute__((aligned(PAGE_SIZE))); 39 static struct lowcore * const lc; 40 41 /* Keep track of fresh memory */ 42 static uint8_t *fresh; 43 44 static inline int mvpg(unsigned long r0, void *dest, void *src) 45 { 46 register unsigned long reg0 asm ("0") = r0; 47 int cc; 48 49 asm volatile(" mvpg %1,%2\n" 50 " ipm %0\n" 51 " srl %0,28" 52 : "=&d" (cc) : "a" (dest), "a" (src), "d" (reg0) 53 : "memory", "cc"); 54 return cc; 55 } 56 57 /* 58 * Initialize a page with a simple pattern 59 */ 60 static void init_page(uint8_t *p) 61 { 62 int i; 63 64 for (i = 0; i < PAGE_SIZE; i++) 65 p[i] = i + MAGIC; 66 } 67 68 /* 69 * Check if the given page contains the simple pattern 70 */ 71 static int page_ok(const uint8_t *p) 72 { 73 int i; 74 75 for (i = 0; i < PAGE_SIZE; i++) 76 if (p[i] != (uint8_t)(i + MAGIC)) 77 return 0; 78 return 1; 79 } 80 81 /* 82 * Check that the Operand Access Identification matches with the values of 83 * the r1 and r2 fields in the instruction format. The r1 and r2 fields are 84 * in the last byte of the instruction, and the Program Old PSW will point 85 * to the beginning of the instruction after the one that caused the fault 86 * (the fixup code in the interrupt handler takes care of that for 87 * nullifying instructions). Therefore it is enough to compare the byte 88 * before the one contained in the Program Old PSW with the value of the 89 * Operand Access Identification. 90 */ 91 static inline bool check_oai(void) 92 { 93 return *(uint8_t *)(lc->pgm_old_psw.addr - 1) == lc->op_acc_id; 94 } 95 96 static void test_exceptions(void) 97 { 98 int i, expected; 99 100 report_prefix_push("exceptions"); 101 102 /* 103 * Key Function Control values 4 and 5 are allowed only in supervisor 104 * state, and even then, only if the move-page-and-set-key facility 105 * is present (STFLE bit 149) 106 */ 107 report_prefix_push("privileged"); 108 if (test_facility(149)) { 109 expected = PGM_INT_CODE_PRIVILEGED_OPERATION; 110 for (i = 4; i < 6; i++) { 111 expect_pgm_int(); 112 enter_pstate(); 113 mvpg(KFC(i), buffer, source); 114 report(clear_pgm_int() == expected, "Key Function Control value %d", i); 115 } 116 } else { 117 report_skip("Key Function Control value %d", 4); 118 report_skip("Key Function Control value %d", 5); 119 i = 4; 120 } 121 report_prefix_pop(); 122 123 /* 124 * Invalid values of the Key Function Control, or setting the 125 * reserved bits, should result in a specification exception 126 */ 127 report_prefix_push("specification"); 128 expected = PGM_INT_CODE_SPECIFICATION; 129 expect_pgm_int(); 130 mvpg(KFC(3), buffer, source); 131 report(clear_pgm_int() == expected, "Key Function Control value 3"); 132 for (; i < 32; i++) { 133 expect_pgm_int(); 134 mvpg(KFC(i), buffer, source); 135 report(clear_pgm_int() == expected, "Key Function Control value %d", i); 136 } 137 report_prefix_pop(); 138 139 /* Operands outside memory result in addressing exceptions, as usual */ 140 report_prefix_push("addressing"); 141 expected = PGM_INT_CODE_ADDRESSING; 142 expect_pgm_int(); 143 mvpg(0, buffer, (void *)PAGE_MASK); 144 report(clear_pgm_int() == expected, "Second operand outside memory"); 145 146 expect_pgm_int(); 147 mvpg(0, (void *)PAGE_MASK, source); 148 report(clear_pgm_int() == expected, "First operand outside memory"); 149 report_prefix_pop(); 150 151 report_prefix_pop(); 152 } 153 154 static void test_success(void) 155 { 156 int cc; 157 158 report_prefix_push("success"); 159 /* Test successful scenarios, both in supervisor and problem state */ 160 cc = mvpg(0, buffer, source); 161 report(page_ok(buffer) && !cc, "Supervisor state MVPG successful"); 162 memset(buffer, 0xff, PAGE_SIZE); 163 164 enter_pstate(); 165 cc = mvpg(0, buffer, source); 166 leave_pstate(); 167 report(page_ok(buffer) && !cc, "Problem state MVPG successful"); 168 169 report_prefix_pop(); 170 } 171 172 static void test_small_loop(const void *string) 173 { 174 uint8_t *dest; 175 int i, cc; 176 177 /* Looping over cold and warm pages helps catch VSIE bugs */ 178 report_prefix_push(string); 179 dest = fresh; 180 for (i = 0; i < ITER; i++) { 181 cc = mvpg(0, fresh, source); 182 report(page_ok(fresh) && !cc, "cold: %p, %p", source, fresh); 183 fresh += PAGE_SIZE; 184 } 185 186 for (i = 0; i < ITER; i++) { 187 memset(dest, 0, PAGE_SIZE); 188 cc = mvpg(0, dest, source); 189 report(page_ok(dest) && !cc, "warm: %p, %p", source, dest); 190 dest += PAGE_SIZE; 191 } 192 report_prefix_pop(); 193 } 194 195 static void test_mmu_prot(void) 196 { 197 int cc; 198 199 report_prefix_push("protection"); 200 report_prefix_push("cco=0"); 201 202 /* MVPG should still succeed when the source is read-only */ 203 protect_page(source, PAGE_ENTRY_P); 204 cc = mvpg(0, fresh, source); 205 report(page_ok(fresh) && !cc, "source read only"); 206 unprotect_page(source, PAGE_ENTRY_P); 207 fresh += PAGE_SIZE; 208 209 /* 210 * When the source or destination are invalid, a page translation 211 * exception should be raised; when the destination is read-only, 212 * a protection exception should be raised. 213 */ 214 protect_page(fresh, PAGE_ENTRY_P); 215 expect_pgm_int(); 216 mvpg(0, fresh, source); 217 report(clear_pgm_int() == PGM_INT_CODE_PROTECTION, "destination read only"); 218 fresh += PAGE_SIZE; 219 220 report_prefix_push("source invalid"); 221 protect_page(source, PAGE_ENTRY_I); 222 lc->op_acc_id = 0; 223 expect_pgm_int(); 224 mvpg(0, fresh, source); 225 report(clear_pgm_int() == PGM_INT_CODE_PAGE_TRANSLATION, "exception"); 226 unprotect_page(source, PAGE_ENTRY_I); 227 report(check_oai(), "operand access ident"); 228 report_prefix_pop(); 229 fresh += PAGE_SIZE; 230 231 report_prefix_push("destination invalid"); 232 protect_page(fresh, PAGE_ENTRY_I); 233 lc->op_acc_id = 0; 234 expect_pgm_int(); 235 mvpg(0, fresh, source); 236 report(clear_pgm_int() == PGM_INT_CODE_PAGE_TRANSLATION, "exception"); 237 report(check_oai(), "operand access ident"); 238 report_prefix_pop(); 239 fresh += PAGE_SIZE; 240 241 report_prefix_pop(); 242 report_prefix_push("cco=1"); 243 /* 244 * Setting the CCO bit should suppress page translation exceptions, 245 * but not protection exceptions. 246 */ 247 protect_page(fresh, PAGE_ENTRY_P); 248 expect_pgm_int(); 249 mvpg(CCO, fresh, source); 250 report(clear_pgm_int() == PGM_INT_CODE_PROTECTION, "destination read only"); 251 fresh += PAGE_SIZE; 252 253 /* Known issue in TCG: CCO flag is not honoured */ 254 if (vm_is_tcg()) { 255 report_prefix_push("TCG"); 256 report_skip("destination invalid"); 257 report_skip("source invalid"); 258 report_skip("source and destination invalid"); 259 report_prefix_pop(); 260 } else { 261 protect_page(fresh, PAGE_ENTRY_I); 262 cc = mvpg(CCO, fresh, source); 263 report(cc == 1, "destination invalid"); 264 fresh += PAGE_SIZE; 265 266 protect_page(source, PAGE_ENTRY_I); 267 cc = mvpg(CCO, fresh, source); 268 report(cc == 2, "source invalid"); 269 fresh += PAGE_SIZE; 270 271 protect_page(fresh, PAGE_ENTRY_I); 272 cc = mvpg(CCO, fresh, source); 273 report(cc == 2, "source and destination invalid"); 274 fresh += PAGE_SIZE; 275 } 276 277 unprotect_page(source, PAGE_ENTRY_I); 278 report_prefix_pop(); 279 report_prefix_pop(); 280 } 281 282 int main(void) 283 { 284 report_prefix_push("mvpg"); 285 286 init_page(source); 287 fresh = alloc_pages_flags(MEM_ORDER, FLAG_DONTZERO | FLAG_FRESH); 288 assert(fresh); 289 290 test_exceptions(); 291 test_success(); 292 test_small_loop("nommu"); 293 294 setup_vm(); 295 296 test_small_loop("mmu"); 297 test_mmu_prot(); 298 299 report_prefix_pop(); 300 return report_summary(); 301 } 302