1 /* SPDX-License-Identifier: GPL-2.0-only */ 2 /* 3 * Interception tests - for s390x CPU instruction that cause a VM exit 4 * 5 * Copyright (c) 2017 Red Hat Inc 6 * 7 * Authors: 8 * Thomas Huth <thuth@redhat.com> 9 */ 10 #include <libcflat.h> 11 #include <sclp.h> 12 #include <asm/asm-offsets.h> 13 #include <asm/interrupt.h> 14 #include <asm/page.h> 15 #include <asm/facility.h> 16 #include <asm/time.h> 17 #include <hardware.h> 18 19 static uint8_t pagebuf[PAGE_SIZE * 2] __attribute__((aligned(PAGE_SIZE * 2))); 20 21 static unsigned long nr_iterations; 22 static unsigned long time_to_run; 23 24 /* Test the STORE PREFIX instruction */ 25 static void test_stpx(void) 26 { 27 uint32_t old_prefix = -1U, tst_prefix = -1U; 28 uint32_t new_prefix = (uint32_t)(intptr_t)pagebuf; 29 30 /* Can we successfully change the prefix? */ 31 old_prefix = get_prefix(); 32 set_prefix(new_prefix); 33 tst_prefix = get_prefix(); 34 set_prefix(old_prefix); 35 report(old_prefix == 0 && tst_prefix == new_prefix, "store prefix"); 36 37 expect_pgm_int(); 38 low_prot_enable(); 39 asm volatile(" stpx 0(%0) " : : "r"(8)); 40 low_prot_disable(); 41 check_pgm_int_code(PGM_INT_CODE_PROTECTION); 42 43 expect_pgm_int(); 44 asm volatile(" stpx 0(%0) " : : "r"(1)); 45 check_pgm_int_code(PGM_INT_CODE_SPECIFICATION); 46 47 expect_pgm_int(); 48 asm volatile(" stpx 0(%0) " : : "r"(-8L)); 49 check_pgm_int_code(PGM_INT_CODE_ADDRESSING); 50 } 51 52 /* Test the SET PREFIX instruction */ 53 static void test_spx(void) 54 { 55 uint32_t new_prefix = (uint32_t)(intptr_t)pagebuf; 56 uint32_t old_prefix; 57 58 memset(pagebuf, 0, PAGE_SIZE * 2); 59 60 /* 61 * Temporarily change the prefix page to our buffer, and store 62 * some facility bits there ... at least some of them should be 63 * set in our buffer afterwards. 64 */ 65 old_prefix = get_prefix(); 66 set_prefix(new_prefix); 67 stfl(); 68 set_prefix(old_prefix); 69 report(pagebuf[GEN_LC_STFL] != 0, "stfl to new prefix"); 70 71 report_prefix_push("operand not word aligned"); 72 expect_pgm_int(); 73 asm volatile(" spx 0(%0) " : : "r"(1)); 74 check_pgm_int_code(PGM_INT_CODE_SPECIFICATION); 75 report_prefix_pop(); 76 77 report_prefix_push("operand outside memory"); 78 expect_pgm_int(); 79 asm volatile(" spx 0(%0) " : : "r"(-8L)); 80 check_pgm_int_code(PGM_INT_CODE_ADDRESSING); 81 report_prefix_pop(); 82 83 report_prefix_push("new prefix outside memory"); 84 new_prefix = get_ram_size() & 0x7fffe000; 85 /* TODO: Remove host_is_tcg() checks once CIs are using QEMU >= 7.1 */ 86 if (!host_is_tcg() && (get_ram_size() - new_prefix < 2 * PAGE_SIZE)) { 87 expect_pgm_int(); 88 asm volatile("spx %0 " : : "Q"(new_prefix)); 89 check_pgm_int_code(PGM_INT_CODE_ADDRESSING); 90 91 /* 92 * Cannot test inaccessibility of the second page the same way. 93 * If we try to use the last page as first half of the prefix 94 * area and our ram size is a multiple of 8k, after SPX aligns 95 * the address to 8k we have a completely accessible area. 96 */ 97 } else { 98 if (host_is_tcg()) 99 report_skip("inaccessible prefix area (workaround for TCG bug)"); 100 else 101 report_skip("inaccessible prefix area"); 102 } 103 report_prefix_pop(); 104 } 105 106 /* Test the STORE CPU ADDRESS instruction */ 107 static void test_stap(void) 108 { 109 uint16_t cpuid = 0xffff; 110 111 asm volatile ("stap %0\n" : "+Q"(cpuid)); 112 report(cpuid != 0xffff, "get cpu address"); 113 114 expect_pgm_int(); 115 low_prot_enable(); 116 asm volatile ("stap 0(%0)\n" : : "r"(8)); 117 low_prot_disable(); 118 check_pgm_int_code(PGM_INT_CODE_PROTECTION); 119 120 expect_pgm_int(); 121 asm volatile ("stap 0(%0)\n" : : "r"(1)); 122 check_pgm_int_code(PGM_INT_CODE_SPECIFICATION); 123 124 expect_pgm_int(); 125 asm volatile ("stap 0(%0)\n" : : "r"(-8L)); 126 check_pgm_int_code(PGM_INT_CODE_ADDRESSING); 127 } 128 129 /* Test the STORE CPU ID instruction */ 130 static void test_stidp(void) 131 { 132 struct cpuid id = {}; 133 134 asm volatile ("stidp %0\n" : "+Q"(id)); 135 report(id.type, "type set"); 136 report(!id.version || id.version == 0xff, "version valid"); 137 report(!id.reserved, "reserved bits not set"); 138 139 expect_pgm_int(); 140 low_prot_enable(); 141 asm volatile ("stidp 0(%0)\n" : : "r"(8)); 142 low_prot_disable(); 143 check_pgm_int_code(PGM_INT_CODE_PROTECTION); 144 145 expect_pgm_int(); 146 asm volatile ("stidp 0(%0)\n" : : "r"(1)); 147 check_pgm_int_code(PGM_INT_CODE_SPECIFICATION); 148 149 expect_pgm_int(); 150 asm volatile ("stidp 0(%0)\n" : : "r"(-8L)); 151 check_pgm_int_code(PGM_INT_CODE_ADDRESSING); 152 } 153 154 /* Test the TEST BLOCK instruction */ 155 static void test_testblock(void) 156 { 157 int cc; 158 159 memset(pagebuf, 0xaa, PAGE_SIZE); 160 161 asm volatile ( 162 " lghi %%r0,0\n" 163 " .insn rre,0xb22c0000,0,%1\n" 164 " ipm %0\n" 165 " srl %0,28\n" 166 : "=d" (cc) 167 : "a"(pagebuf + 0x123) 168 : "memory", "0", "cc"); 169 report(cc == 0 && pagebuf[0] == 0 && pagebuf[PAGE_SIZE - 1] == 0, 170 "page cleared"); 171 172 expect_pgm_int(); 173 low_prot_enable(); 174 asm volatile (" .insn rre,0xb22c0000,0,%0\n" : : "r"(4096)); 175 low_prot_disable(); 176 check_pgm_int_code(PGM_INT_CODE_PROTECTION); 177 178 expect_pgm_int(); 179 asm volatile (" .insn rre,0xb22c0000,0,%0\n" : : "r"(-4096L)); 180 check_pgm_int_code(PGM_INT_CODE_ADDRESSING); 181 } 182 183 static void test_diag318(void) 184 { 185 expect_pgm_int(); 186 enter_pstate(); 187 asm volatile("diag %0,0,0x318\n" : : "d" (0x42)); 188 check_pgm_int_code(PGM_INT_CODE_PRIVILEGED_OPERATION); 189 190 if (!sclp_facilities.has_diag318) 191 expect_pgm_int(); 192 193 asm volatile("diag %0,0,0x318\n" : : "d" (0x42)); 194 195 if (!sclp_facilities.has_diag318) 196 check_pgm_int_code(PGM_INT_CODE_SPECIFICATION); 197 198 } 199 200 struct { 201 const char *name; 202 void (*func)(void); 203 bool run_it; 204 } tests[] = { 205 { "stpx", test_stpx, false }, 206 { "spx", test_spx, false }, 207 { "stap", test_stap, false }, 208 { "stidp", test_stidp, false }, 209 { "testblock", test_testblock, false }, 210 { "diag318", test_diag318, false }, 211 { NULL, NULL, false } 212 }; 213 214 static void parse_intercept_test_args(int argc, char **argv) 215 { 216 int i, ti; 217 bool run_all = true; 218 219 for (i = 1; i < argc; i++) { 220 if (!strcmp("-i", argv[i])) { 221 i++; 222 if (i >= argc) 223 report_abort("-i needs a parameter"); 224 nr_iterations = atol(argv[i]); 225 } else if (!strcmp("-t", argv[i])) { 226 i++; 227 if (i >= argc) 228 report_abort("-t needs a parameter"); 229 time_to_run = atol(argv[i]); 230 } else for (ti = 0; tests[ti].name != NULL; ti++) { 231 if (!strcmp(tests[ti].name, argv[i])) { 232 run_all = false; 233 tests[ti].run_it = true; 234 break; 235 } else if (tests[ti + 1].name == NULL) { 236 report_abort("Unsupported parameter '%s'", 237 argv[i]); 238 } 239 } 240 } 241 242 if (run_all) { 243 for (ti = 0; tests[ti].name != NULL; ti++) 244 tests[ti].run_it = true; 245 } 246 } 247 248 int main(int argc, char **argv) 249 { 250 uint64_t startclk; 251 int ti; 252 253 parse_intercept_test_args(argc, argv); 254 255 if (nr_iterations == 0 && time_to_run == 0) 256 nr_iterations = 1; 257 258 report_prefix_push("intercept"); 259 260 startclk = get_clock_ms(); 261 for (;;) { 262 for (ti = 0; tests[ti].name != NULL; ti++) { 263 report_prefix_push(tests[ti].name); 264 if (tests[ti].run_it) 265 tests[ti].func(); 266 report_prefix_pop(); 267 } 268 if (nr_iterations) { 269 nr_iterations -= 1; 270 if (nr_iterations == 0) 271 break; 272 } 273 if (time_to_run) { 274 if (get_clock_ms() - startclk > time_to_run) 275 break; 276 } 277 } 278 279 report_prefix_pop(); 280 281 return report_summary(); 282 } 283