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