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