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