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 40 /* Keep track of fresh memory */ 41 static uint8_t *fresh; 42 43 static inline int mvpg(unsigned long r0, void *dest, void *src) 44 { 45 register unsigned long reg0 asm ("0") = r0; 46 int cc; 47 48 asm volatile(" mvpg %1,%2\n" 49 " ipm %0\n" 50 " srl %0,28" 51 : "=&d" (cc) : "a" (dest), "a" (src), "d" (reg0) 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 static void test_exceptions(void) 81 { 82 int i, expected; 83 84 report_prefix_push("exceptions"); 85 86 /* 87 * Key Function Control values 4 and 5 are allowed only in supervisor 88 * state, and even then, only if the move-page-and-set-key facility 89 * is present (STFLE bit 149) 90 */ 91 report_prefix_push("privileged"); 92 if (test_facility(149)) { 93 expected = PGM_INT_CODE_PRIVILEGED_OPERATION; 94 for (i = 4; i < 6; i++) { 95 expect_pgm_int(); 96 enter_pstate(); 97 mvpg(KFC(i), buffer, source); 98 report(clear_pgm_int() == expected, "Key Function Control value %d", i); 99 } 100 } else { 101 report_skip("Key Function Control value %d", 4); 102 report_skip("Key Function Control value %d", 5); 103 i = 4; 104 } 105 report_prefix_pop(); 106 107 /* 108 * Invalid values of the Key Function Control, or setting the 109 * reserved bits, should result in a specification exception 110 */ 111 report_prefix_push("specification"); 112 expected = PGM_INT_CODE_SPECIFICATION; 113 expect_pgm_int(); 114 mvpg(KFC(3), buffer, source); 115 report(clear_pgm_int() == expected, "Key Function Control value 3"); 116 for (; i < 32; i++) { 117 expect_pgm_int(); 118 mvpg(KFC(i), buffer, source); 119 report(clear_pgm_int() == expected, "Key Function Control value %d", i); 120 } 121 report_prefix_pop(); 122 123 /* Operands outside memory result in addressing exceptions, as usual */ 124 report_prefix_push("addressing"); 125 expected = PGM_INT_CODE_ADDRESSING; 126 expect_pgm_int(); 127 mvpg(0, buffer, (void *)PAGE_MASK); 128 report(clear_pgm_int() == expected, "Second operand outside memory"); 129 130 expect_pgm_int(); 131 mvpg(0, (void *)PAGE_MASK, source); 132 report(clear_pgm_int() == expected, "First operand outside memory"); 133 report_prefix_pop(); 134 135 report_prefix_pop(); 136 } 137 138 static void test_success(void) 139 { 140 int cc; 141 142 report_prefix_push("success"); 143 /* Test successful scenarios, both in supervisor and problem state */ 144 cc = mvpg(0, buffer, source); 145 report(page_ok(buffer) && !cc, "Supervisor state MVPG successful"); 146 memset(buffer, 0xff, PAGE_SIZE); 147 148 enter_pstate(); 149 cc = mvpg(0, buffer, source); 150 leave_pstate(); 151 report(page_ok(buffer) && !cc, "Problem state MVPG successful"); 152 153 report_prefix_pop(); 154 } 155 156 static void test_small_loop(const void *string) 157 { 158 uint8_t *dest; 159 int i, cc; 160 161 /* Looping over cold and warm pages helps catch VSIE bugs */ 162 report_prefix_push(string); 163 dest = fresh; 164 for (i = 0; i < ITER; i++) { 165 cc = mvpg(0, fresh, source); 166 report(page_ok(fresh) && !cc, "cold: %p, %p", source, fresh); 167 fresh += PAGE_SIZE; 168 } 169 170 for (i = 0; i < ITER; i++) { 171 memset(dest, 0, PAGE_SIZE); 172 cc = mvpg(0, dest, source); 173 report(page_ok(dest) && !cc, "warm: %p, %p", source, dest); 174 dest += PAGE_SIZE; 175 } 176 report_prefix_pop(); 177 } 178 179 static void test_mmu_prot(void) 180 { 181 int cc; 182 183 report_prefix_push("protection"); 184 report_prefix_push("cco=0"); 185 186 /* MVPG should still succeed when the source is read-only */ 187 protect_page(source, PAGE_ENTRY_P); 188 cc = mvpg(0, fresh, source); 189 report(page_ok(fresh) && !cc, "source read only"); 190 unprotect_page(source, PAGE_ENTRY_P); 191 fresh += PAGE_SIZE; 192 193 /* 194 * When the source or destination are invalid, a page translation 195 * exception should be raised; when the destination is read-only, 196 * a protection exception should be raised. 197 */ 198 protect_page(fresh, PAGE_ENTRY_P); 199 expect_pgm_int(); 200 mvpg(0, fresh, source); 201 report(clear_pgm_int() == PGM_INT_CODE_PROTECTION, "destination read only"); 202 fresh += PAGE_SIZE; 203 204 protect_page(source, PAGE_ENTRY_I); 205 expect_pgm_int(); 206 mvpg(0, fresh, source); 207 report(clear_pgm_int() == PGM_INT_CODE_PAGE_TRANSLATION, "source invalid"); 208 unprotect_page(source, PAGE_ENTRY_I); 209 fresh += PAGE_SIZE; 210 211 protect_page(fresh, PAGE_ENTRY_I); 212 expect_pgm_int(); 213 mvpg(0, fresh, source); 214 report(clear_pgm_int() == PGM_INT_CODE_PAGE_TRANSLATION, "destination invalid"); 215 fresh += PAGE_SIZE; 216 217 report_prefix_pop(); 218 report_prefix_push("cco=1"); 219 /* 220 * Setting the CCO bit should suppress page translation exceptions, 221 * but not protection exceptions. 222 */ 223 protect_page(fresh, PAGE_ENTRY_P); 224 expect_pgm_int(); 225 mvpg(CCO, fresh, source); 226 report(clear_pgm_int() == PGM_INT_CODE_PROTECTION, "destination read only"); 227 fresh += PAGE_SIZE; 228 229 /* Known issue in TCG: CCO flag is not honoured */ 230 if (vm_is_tcg()) { 231 report_prefix_push("TCG"); 232 report_skip("destination invalid"); 233 report_skip("source invalid"); 234 report_skip("source and destination invalid"); 235 report_prefix_pop(); 236 } else { 237 protect_page(fresh, PAGE_ENTRY_I); 238 cc = mvpg(CCO, fresh, source); 239 report(cc == 1, "destination invalid"); 240 fresh += PAGE_SIZE; 241 242 protect_page(source, PAGE_ENTRY_I); 243 cc = mvpg(CCO, fresh, source); 244 report(cc == 2, "source invalid"); 245 fresh += PAGE_SIZE; 246 247 protect_page(fresh, PAGE_ENTRY_I); 248 cc = mvpg(CCO, fresh, source); 249 report(cc == 2, "source and destination invalid"); 250 fresh += PAGE_SIZE; 251 } 252 253 unprotect_page(source, PAGE_ENTRY_I); 254 report_prefix_pop(); 255 report_prefix_pop(); 256 } 257 258 int main(void) 259 { 260 report_prefix_push("mvpg"); 261 262 init_page(source); 263 fresh = alloc_pages_flags(MEM_ORDER, FLAG_DONTZERO | FLAG_FRESH); 264 assert(fresh); 265 266 test_exceptions(); 267 test_success(); 268 test_small_loop("nommu"); 269 270 setup_vm(); 271 272 test_small_loop("mmu"); 273 test_mmu_prot(); 274 275 report_prefix_pop(); 276 return report_summary(); 277 } 278