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