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 TEST BLOCK instruction */ 109 static void test_testblock(void) 110 { 111 int cc; 112 113 memset(pagebuf, 0xaa, PAGE_SIZE); 114 115 asm volatile ( 116 " lghi %%r0,0\n" 117 " tb %1\n" 118 " ipm %0\n" 119 " srl %0,28\n" 120 : "=d" (cc) 121 : "a"(pagebuf + 0x123) 122 : "memory", "0", "cc"); 123 report("page cleared", 124 cc == 0 && pagebuf[0] == 0 && pagebuf[PAGE_SIZE - 1] == 0); 125 126 expect_pgm_int(); 127 low_prot_enable(); 128 asm volatile (" tb %0 " : : "r"(4096)); 129 low_prot_disable(); 130 check_pgm_int_code(PGM_INT_CODE_PROTECTION); 131 132 expect_pgm_int(); 133 asm volatile (" tb %0 " : : "r"(-4096)); 134 check_pgm_int_code(PGM_INT_CODE_ADDRESSING); 135 } 136 137 static uint64_t get_clock_ms(void) 138 { 139 uint64_t clk; 140 141 asm volatile(" stck %0 " : : "Q"(clk) : "memory"); 142 143 /* Bit 51 is incrememented each microsecond */ 144 return (clk >> (63 - 51)) / 1000; 145 } 146 147 struct { 148 const char *name; 149 void (*func)(void); 150 bool run_it; 151 } tests[] = { 152 { "stpx", test_stpx, false }, 153 { "spx", test_spx, false }, 154 { "stap", test_stap, false }, 155 { "testblock", test_testblock, false }, 156 { NULL, NULL, false } 157 }; 158 159 void parse_intercept_test_args(int argc, char **argv) 160 { 161 int i, ti; 162 bool run_all = true; 163 164 for (i = 1; i < argc; i++) { 165 if (!strcmp("-i", argv[i])) { 166 i++; 167 if (i >= argc) 168 report_abort("-i needs a parameter"); 169 nr_iterations = atol(argv[i]); 170 } else if (!strcmp("-t", argv[i])) { 171 i++; 172 if (i >= argc) 173 report_abort("-t needs a parameter"); 174 time_to_run = atol(argv[i]); 175 } else for (ti = 0; tests[ti].name != NULL; ti++) { 176 if (!strcmp(tests[ti].name, argv[i])) { 177 run_all = false; 178 tests[ti].run_it = true; 179 break; 180 } else if (tests[ti + 1].name == NULL) { 181 report_abort("Unsupported parameter '%s'", 182 argv[i]); 183 } 184 } 185 } 186 187 if (run_all) { 188 for (ti = 0; tests[ti].name != NULL; ti++) 189 tests[ti].run_it = true; 190 } 191 } 192 193 int main(int argc, char **argv) 194 { 195 uint64_t startclk; 196 int ti; 197 198 parse_intercept_test_args(argc, argv); 199 200 if (nr_iterations == 0 && time_to_run == 0) 201 nr_iterations = 1; 202 203 report_prefix_push("intercept"); 204 205 startclk = get_clock_ms(); 206 for (;;) { 207 for (ti = 0; tests[ti].name != NULL; ti++) { 208 report_prefix_push(tests[ti].name); 209 if (tests[ti].run_it) 210 tests[ti].func(); 211 report_prefix_pop(); 212 } 213 if (nr_iterations) { 214 nr_iterations -= 1; 215 if (nr_iterations == 0) 216 break; 217 } 218 if (time_to_run) { 219 if (get_clock_ms() - startclk > time_to_run) 220 break; 221 } 222 } 223 224 report_prefix_pop(); 225 226 return report_summary(); 227 } 228