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 #include <asm/facility.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 79 /* Test the STORE CPU ADDRESS instruction */ 80 static void test_stap(void) 81 { 82 uint16_t cpuid = 0xffff; 83 84 asm volatile ("stap %0\n" : "+Q"(cpuid)); 85 report(cpuid != 0xffff, "get cpu address"); 86 87 expect_pgm_int(); 88 low_prot_enable(); 89 asm volatile ("stap 0(%0)\n" : : "r"(8)); 90 low_prot_disable(); 91 check_pgm_int_code(PGM_INT_CODE_PROTECTION); 92 93 expect_pgm_int(); 94 asm volatile ("stap 0(%0)\n" : : "r"(1)); 95 check_pgm_int_code(PGM_INT_CODE_SPECIFICATION); 96 97 expect_pgm_int(); 98 asm volatile ("stap 0(%0)\n" : : "r"(-8L)); 99 check_pgm_int_code(PGM_INT_CODE_ADDRESSING); 100 } 101 102 /* Test the STORE CPU ID instruction */ 103 static void test_stidp(void) 104 { 105 struct cpuid id = {}; 106 107 asm volatile ("stidp %0\n" : "+Q"(id)); 108 report(id.type, "type set"); 109 report(!id.version || id.version == 0xff, "version valid"); 110 report(!id.reserved, "reserved bits not set"); 111 112 expect_pgm_int(); 113 low_prot_enable(); 114 asm volatile ("stidp 0(%0)\n" : : "r"(8)); 115 low_prot_disable(); 116 check_pgm_int_code(PGM_INT_CODE_PROTECTION); 117 118 expect_pgm_int(); 119 asm volatile ("stidp 0(%0)\n" : : "r"(1)); 120 check_pgm_int_code(PGM_INT_CODE_SPECIFICATION); 121 122 expect_pgm_int(); 123 asm volatile ("stidp 0(%0)\n" : : "r"(-8L)); 124 check_pgm_int_code(PGM_INT_CODE_ADDRESSING); 125 } 126 127 /* Test the TEST BLOCK instruction */ 128 static void test_testblock(void) 129 { 130 int cc; 131 132 memset(pagebuf, 0xaa, PAGE_SIZE); 133 134 asm volatile ( 135 " lghi %%r0,0\n" 136 " .insn rre,0xb22c0000,0,%1\n" 137 " ipm %0\n" 138 " srl %0,28\n" 139 : "=d" (cc) 140 : "a"(pagebuf + 0x123) 141 : "memory", "0", "cc"); 142 report(cc == 0 && pagebuf[0] == 0 && pagebuf[PAGE_SIZE - 1] == 0, 143 "page cleared"); 144 145 expect_pgm_int(); 146 low_prot_enable(); 147 asm volatile (" .insn rre,0xb22c0000,0,%0\n" : : "r"(4096)); 148 low_prot_disable(); 149 check_pgm_int_code(PGM_INT_CODE_PROTECTION); 150 151 expect_pgm_int(); 152 asm volatile (" .insn rre,0xb22c0000,0,%0\n" : : "r"(-4096L)); 153 check_pgm_int_code(PGM_INT_CODE_ADDRESSING); 154 } 155 156 static uint64_t get_clock_ms(void) 157 { 158 uint64_t clk; 159 160 asm volatile(" stck %0 " : : "Q"(clk) : "memory"); 161 162 /* Bit 51 is incrememented each microsecond */ 163 return (clk >> (63 - 51)) / 1000; 164 } 165 166 struct { 167 const char *name; 168 void (*func)(void); 169 bool run_it; 170 } tests[] = { 171 { "stpx", test_stpx, false }, 172 { "spx", test_spx, false }, 173 { "stap", test_stap, false }, 174 { "stidp", test_stidp, false }, 175 { "testblock", test_testblock, false }, 176 { NULL, NULL, false } 177 }; 178 179 static void parse_intercept_test_args(int argc, char **argv) 180 { 181 int i, ti; 182 bool run_all = true; 183 184 for (i = 1; i < argc; i++) { 185 if (!strcmp("-i", argv[i])) { 186 i++; 187 if (i >= argc) 188 report_abort("-i needs a parameter"); 189 nr_iterations = atol(argv[i]); 190 } else if (!strcmp("-t", argv[i])) { 191 i++; 192 if (i >= argc) 193 report_abort("-t needs a parameter"); 194 time_to_run = atol(argv[i]); 195 } else for (ti = 0; tests[ti].name != NULL; ti++) { 196 if (!strcmp(tests[ti].name, argv[i])) { 197 run_all = false; 198 tests[ti].run_it = true; 199 break; 200 } else if (tests[ti + 1].name == NULL) { 201 report_abort("Unsupported parameter '%s'", 202 argv[i]); 203 } 204 } 205 } 206 207 if (run_all) { 208 for (ti = 0; tests[ti].name != NULL; ti++) 209 tests[ti].run_it = true; 210 } 211 } 212 213 int main(int argc, char **argv) 214 { 215 uint64_t startclk; 216 int ti; 217 218 parse_intercept_test_args(argc, argv); 219 220 if (nr_iterations == 0 && time_to_run == 0) 221 nr_iterations = 1; 222 223 report_prefix_push("intercept"); 224 225 startclk = get_clock_ms(); 226 for (;;) { 227 for (ti = 0; tests[ti].name != NULL; ti++) { 228 report_prefix_push(tests[ti].name); 229 if (tests[ti].run_it) 230 tests[ti].func(); 231 report_prefix_pop(); 232 } 233 if (nr_iterations) { 234 nr_iterations -= 1; 235 if (nr_iterations == 0) 236 break; 237 } 238 if (time_to_run) { 239 if (get_clock_ms() - startclk > time_to_run) 240 break; 241 } 242 } 243 244 report_prefix_pop(); 245 246 return report_summary(); 247 } 248