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