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