1 /* SPDX-License-Identifier: GPL-2.0-only */ 2 /* 3 * Storage Key migration tests 4 * 5 * There are two variants of this test: 6 * - sequential: set storage keys on some pages, migrates the VM and then 7 * verifies that the storage keys are still as we expect. 8 * - parallel: start migration and set and check storage keys on some 9 * pages while migration is in process. 10 * 11 * Copyright IBM Corp. 2022 12 * 13 * Authors: 14 * Nico Boehr <nrb@linux.ibm.com> 15 */ 16 17 #include <libcflat.h> 18 #include <migrate.h> 19 #include <asm/facility.h> 20 #include <asm/page.h> 21 #include <asm/mem.h> 22 #include <asm/barrier.h> 23 #include <hardware.h> 24 #include <smp.h> 25 26 struct verify_result { 27 bool verify_failed; 28 union skey expected_key; 29 union skey actual_key; 30 unsigned long page_mismatch_idx; 31 unsigned long page_mismatch_addr; 32 }; 33 34 #define NUM_PAGES 128 35 static uint8_t pagebuf[NUM_PAGES * PAGE_SIZE] __attribute__((aligned(PAGE_SIZE))); 36 37 static struct verify_result result; 38 39 static unsigned int thread_iters; 40 static bool thread_should_exit; 41 static bool thread_exited; 42 43 static enum { 44 TEST_INVALID, 45 TEST_SEQUENTIAL, 46 TEST_PARALLEL 47 } arg_test_to_run; 48 49 /* 50 * Set storage key test pattern on pagebuf with a seed for the storage keys. 51 * 52 * Each page's storage key is generated by taking the page's index in pagebuf, 53 * XOR-ing that with the given seed and then multipling the result with two. 54 * 55 * Only the lower seven bits of the seed are considered. 56 */ 57 static void set_test_pattern(unsigned char seed) 58 { 59 unsigned char key_to_set; 60 unsigned long i; 61 62 for (i = 0; i < NUM_PAGES; i++) { 63 /* 64 * Storage keys are 7 bit, lowest bit is always returned as zero 65 * by iske. 66 * This loop will set all 7 bits which means we set fetch 67 * protection as well as reference and change indication for 68 * some keys. 69 */ 70 key_to_set = (i ^ seed) * 2; 71 set_storage_key(pagebuf + i * PAGE_SIZE, key_to_set, 1); 72 } 73 } 74 75 /* 76 * Verify storage keys on pagebuf. 77 * Storage keys must have been set by set_test_pattern on pagebuf before. 78 * set_test_pattern must have been called with the same seed value. 79 * 80 * If storage keys match the expected result, will return a verify_result 81 * with verify_failed false. All other fields are then invalid. 82 * If there is a mismatch, returned struct will have verify_failed true and will 83 * be filled with the details on the first mismatch encountered. 84 */ 85 static struct verify_result verify_test_pattern(unsigned char seed) 86 { 87 union skey expected_key, actual_key; 88 struct verify_result result = { 89 .verify_failed = true 90 }; 91 uint8_t *cur_page; 92 unsigned long i; 93 94 for (i = 0; i < NUM_PAGES; i++) { 95 cur_page = pagebuf + i * PAGE_SIZE; 96 actual_key.val = get_storage_key(cur_page); 97 expected_key.val = (i ^ seed) * 2; 98 99 /* 100 * The PoP neither gives a guarantee that the reference bit is 101 * accurate nor that it won't be cleared by hardware. Hence we 102 * don't rely on it and just clear the bits to avoid compare 103 * errors. 104 */ 105 actual_key.str.rf = 0; 106 expected_key.str.rf = 0; 107 108 if (actual_key.val != expected_key.val) { 109 result.expected_key.val = expected_key.val; 110 result.actual_key.val = actual_key.val; 111 result.page_mismatch_idx = i; 112 result.page_mismatch_addr = (unsigned long)cur_page; 113 return result; 114 } 115 } 116 117 result.verify_failed = false; 118 return result; 119 } 120 121 static void report_verify_result(const struct verify_result * result) 122 { 123 if (result->verify_failed) 124 report_fail("page skey mismatch: first page idx = %lu, addr = 0x%lx, " 125 "expected_key = 0x%02x, actual_key = 0x%02x", 126 result->page_mismatch_idx, result->page_mismatch_addr, 127 result->expected_key.val, result->actual_key.val); 128 else 129 report_pass("skeys match"); 130 } 131 132 static void test_skey_migration_sequential(void) 133 { 134 report_prefix_push("sequential"); 135 136 set_test_pattern(0); 137 138 migrate_once(); 139 140 result = verify_test_pattern(0); 141 report_verify_result(&result); 142 143 report_prefix_pop(); 144 } 145 146 static void set_skeys_thread(void) 147 { 148 while (!READ_ONCE(thread_should_exit)) { 149 set_test_pattern(thread_iters); 150 151 result = verify_test_pattern(thread_iters); 152 153 /* 154 * Always increment even if the verify fails. This ensures primary CPU knows where 155 * we left off and can do an additional verify round after migration finished. 156 */ 157 thread_iters++; 158 159 if (result.verify_failed) 160 break; 161 } 162 163 WRITE_ONCE(thread_exited, 1); 164 } 165 166 static void test_skey_migration_parallel(void) 167 { 168 report_prefix_push("parallel"); 169 170 if (smp_query_num_cpus() == 1) { 171 report_skip("need at least 2 cpus for this test"); 172 goto error; 173 } 174 175 smp_cpu_setup(1, PSW_WITH_CUR_MASK(set_skeys_thread)); 176 177 migrate_once(); 178 179 WRITE_ONCE(thread_should_exit, 1); 180 181 while (!READ_ONCE(thread_exited)) 182 ; 183 184 /* Ensure we read result and thread_iters below from memory after thread exited */ 185 mb(); 186 report_info("thread completed %u iterations", thread_iters); 187 188 report_prefix_push("during migration"); 189 report_verify_result(&result); 190 report_prefix_pop(); 191 192 /* 193 * Verification of skeys occurs on the thread. We don't know if we 194 * were still migrating during the verification. 195 * To be sure, make another verification round after the migration 196 * finished to catch skeys which might not have been migrated 197 * correctly. 198 */ 199 report_prefix_push("after migration"); 200 assert(thread_iters > 0); 201 result = verify_test_pattern(thread_iters - 1); 202 report_verify_result(&result); 203 report_prefix_pop(); 204 205 error: 206 report_prefix_pop(); 207 } 208 209 static void print_usage(void) 210 { 211 report_info("Usage: migration-skey [--parallel|--sequential]"); 212 } 213 214 static void parse_args(int argc, char **argv) 215 { 216 if (argc < 2) { 217 /* default to sequential since it only needs one cpu */ 218 arg_test_to_run = TEST_SEQUENTIAL; 219 return; 220 } 221 222 if (!strcmp("--parallel", argv[1])) 223 arg_test_to_run = TEST_PARALLEL; 224 else if (!strcmp("--sequential", argv[1])) 225 arg_test_to_run = TEST_SEQUENTIAL; 226 else 227 arg_test_to_run = TEST_INVALID; 228 } 229 230 int main(int argc, char **argv) 231 { 232 report_prefix_push("migration-skey"); 233 234 if (test_facility(169)) { 235 report_skip("storage key removal facility is active"); 236 goto error; 237 } 238 239 parse_args(argc, argv); 240 241 switch (arg_test_to_run) { 242 case TEST_SEQUENTIAL: 243 test_skey_migration_sequential(); 244 break; 245 case TEST_PARALLEL: 246 test_skey_migration_parallel(); 247 break; 248 default: 249 print_usage(); 250 break; 251 } 252 253 error: 254 migrate_once(); 255 report_prefix_pop(); 256 return report_summary(); 257 } 258