1 /* 2 * Interception tests - for s390x CPU instruction that cause a VM exit 3 * 4 * Copyright (c) 2017 Red Hat Inc 5 * 6 * Authors: 7 * Thomas Huth <thuth@redhat.com> 8 * 9 * This code is free software; you can redistribute it and/or modify it 10 * under the terms of the GNU Library General Public License version 2. 11 */ 12 #include <libcflat.h> 13 #include <asm/asm-offsets.h> 14 #include <asm/interrupt.h> 15 #include <asm/page.h> 16 17 static uint8_t pagebuf[PAGE_SIZE * 2] __attribute__((aligned(PAGE_SIZE * 2))); 18 19 static unsigned long nr_iterations; 20 static unsigned long time_to_run; 21 22 /* Test the STORE PREFIX instruction */ 23 static void test_stpx(void) 24 { 25 uint32_t old_prefix = -1U, tst_prefix = -1U; 26 uint32_t new_prefix = (uint32_t)(intptr_t)pagebuf; 27 28 /* Can we successfully change the prefix? */ 29 asm volatile ( 30 " stpx %0\n" 31 " spx %2\n" 32 " stpx %1\n" 33 " spx %0\n" 34 : "+Q"(old_prefix), "+Q"(tst_prefix) 35 : "Q"(new_prefix)); 36 report("store prefix", old_prefix == 0 && tst_prefix == new_prefix); 37 38 expect_pgm_int(); 39 low_prot_enable(); 40 asm volatile(" stpx 0(%0) " : : "r"(8)); 41 low_prot_disable(); 42 check_pgm_int_code(PGM_INT_CODE_PROTECTION); 43 44 expect_pgm_int(); 45 asm volatile(" stpx 0(%0) " : : "r"(1)); 46 check_pgm_int_code(PGM_INT_CODE_SPECIFICATION); 47 48 expect_pgm_int(); 49 asm volatile(" stpx 0(%0) " : : "r"(-8)); 50 check_pgm_int_code(PGM_INT_CODE_ADDRESSING); 51 } 52 53 /* Test the SET PREFIX instruction */ 54 static void test_spx(void) 55 { 56 uint32_t new_prefix = (uint32_t)(intptr_t)pagebuf; 57 uint32_t old_prefix; 58 59 memset(pagebuf, 0, PAGE_SIZE * 2); 60 61 /* 62 * Temporarily change the prefix page to our buffer, and store 63 * some facility bits there ... at least some of them should be 64 * set in our buffer afterwards. 65 */ 66 asm volatile ( 67 " stpx %0\n" 68 " spx %1\n" 69 " stfl 0\n" 70 " spx %0\n" 71 : "+Q"(old_prefix) 72 : "Q"(new_prefix) 73 : "memory"); 74 report("stfl to new prefix", pagebuf[GEN_LC_STFL] != 0); 75 76 expect_pgm_int(); 77 asm volatile(" spx 0(%0) " : : "r"(1)); 78 check_pgm_int_code(PGM_INT_CODE_SPECIFICATION); 79 80 expect_pgm_int(); 81 asm volatile(" spx 0(%0) " : : "r"(-8)); 82 check_pgm_int_code(PGM_INT_CODE_ADDRESSING); 83 } 84 85 /* Test the STORE CPU ADDRESS instruction */ 86 static void test_stap(void) 87 { 88 uint16_t cpuid = 0xffff; 89 90 asm volatile ("stap %0\n" : "+Q"(cpuid)); 91 report("get cpu address", cpuid != 0xffff); 92 93 expect_pgm_int(); 94 low_prot_enable(); 95 asm volatile ("stap 0(%0)\n" : : "r"(8)); 96 low_prot_disable(); 97 check_pgm_int_code(PGM_INT_CODE_PROTECTION); 98 99 expect_pgm_int(); 100 asm volatile ("stap 0(%0)\n" : : "r"(1)); 101 check_pgm_int_code(PGM_INT_CODE_SPECIFICATION); 102 103 expect_pgm_int(); 104 asm volatile ("stap 0(%0)\n" : : "r"(-8)); 105 check_pgm_int_code(PGM_INT_CODE_ADDRESSING); 106 } 107 108 /* Test the STORE CPU ID instruction */ 109 static void test_stidp(void) 110 { 111 struct cpuid id = {}; 112 113 asm volatile ("stidp %0\n" : "+Q"(id)); 114 report("type set", id.type); 115 report("version valid", !id.version || id.version == 0xff); 116 report("reserved bits not set", !id.reserved); 117 118 expect_pgm_int(); 119 low_prot_enable(); 120 asm volatile ("stidp 0(%0)\n" : : "r"(8)); 121 low_prot_disable(); 122 check_pgm_int_code(PGM_INT_CODE_PROTECTION); 123 124 expect_pgm_int(); 125 asm volatile ("stidp 0(%0)\n" : : "r"(1)); 126 check_pgm_int_code(PGM_INT_CODE_SPECIFICATION); 127 128 expect_pgm_int(); 129 asm volatile ("stidp 0(%0)\n" : : "r"(-8)); 130 check_pgm_int_code(PGM_INT_CODE_ADDRESSING); 131 } 132 133 /* Test the TEST BLOCK instruction */ 134 static void test_testblock(void) 135 { 136 int cc; 137 138 memset(pagebuf, 0xaa, PAGE_SIZE); 139 140 asm volatile ( 141 " lghi %%r0,0\n" 142 " tb %1\n" 143 " ipm %0\n" 144 " srl %0,28\n" 145 : "=d" (cc) 146 : "a"(pagebuf + 0x123) 147 : "memory", "0", "cc"); 148 report("page cleared", 149 cc == 0 && pagebuf[0] == 0 && pagebuf[PAGE_SIZE - 1] == 0); 150 151 expect_pgm_int(); 152 low_prot_enable(); 153 asm volatile (" tb %0 " : : "r"(4096)); 154 low_prot_disable(); 155 check_pgm_int_code(PGM_INT_CODE_PROTECTION); 156 157 expect_pgm_int(); 158 asm volatile (" tb %0 " : : "r"(-4096)); 159 check_pgm_int_code(PGM_INT_CODE_ADDRESSING); 160 } 161 162 static uint64_t get_clock_ms(void) 163 { 164 uint64_t clk; 165 166 asm volatile(" stck %0 " : : "Q"(clk) : "memory"); 167 168 /* Bit 51 is incrememented each microsecond */ 169 return (clk >> (63 - 51)) / 1000; 170 } 171 172 struct { 173 const char *name; 174 void (*func)(void); 175 bool run_it; 176 } tests[] = { 177 { "stpx", test_stpx, false }, 178 { "spx", test_spx, false }, 179 { "stap", test_stap, false }, 180 { "stidp", test_stidp, false }, 181 { "testblock", test_testblock, false }, 182 { NULL, NULL, false } 183 }; 184 185 static void parse_intercept_test_args(int argc, char **argv) 186 { 187 int i, ti; 188 bool run_all = true; 189 190 for (i = 1; i < argc; i++) { 191 if (!strcmp("-i", argv[i])) { 192 i++; 193 if (i >= argc) 194 report_abort("-i needs a parameter"); 195 nr_iterations = atol(argv[i]); 196 } else if (!strcmp("-t", argv[i])) { 197 i++; 198 if (i >= argc) 199 report_abort("-t needs a parameter"); 200 time_to_run = atol(argv[i]); 201 } else for (ti = 0; tests[ti].name != NULL; ti++) { 202 if (!strcmp(tests[ti].name, argv[i])) { 203 run_all = false; 204 tests[ti].run_it = true; 205 break; 206 } else if (tests[ti + 1].name == NULL) { 207 report_abort("Unsupported parameter '%s'", 208 argv[i]); 209 } 210 } 211 } 212 213 if (run_all) { 214 for (ti = 0; tests[ti].name != NULL; ti++) 215 tests[ti].run_it = true; 216 } 217 } 218 219 int main(int argc, char **argv) 220 { 221 uint64_t startclk; 222 int ti; 223 224 parse_intercept_test_args(argc, argv); 225 226 if (nr_iterations == 0 && time_to_run == 0) 227 nr_iterations = 1; 228 229 report_prefix_push("intercept"); 230 231 startclk = get_clock_ms(); 232 for (;;) { 233 for (ti = 0; tests[ti].name != NULL; ti++) { 234 report_prefix_push(tests[ti].name); 235 if (tests[ti].run_it) 236 tests[ti].func(); 237 report_prefix_pop(); 238 } 239 if (nr_iterations) { 240 nr_iterations -= 1; 241 if (nr_iterations == 0) 242 break; 243 } 244 if (time_to_run) { 245 if (get_clock_ms() - startclk > time_to_run) 246 break; 247 } 248 } 249 250 report_prefix_pop(); 251 252 return report_summary(); 253 } 254