xref: /kvm-unit-tests/s390x/migration-skey.c (revision e6e1a20143625acb08a3f8ae25ec06846356d891)
11a7b0521SNico Boehr /* SPDX-License-Identifier: GPL-2.0-only */
21a7b0521SNico Boehr /*
31a7b0521SNico Boehr  * Storage Key migration tests
41a7b0521SNico Boehr  *
5544f8b50SNico Boehr  * There are two variants of this test:
6544f8b50SNico Boehr  * - sequential: set storage keys on some pages, migrates the VM and then
7544f8b50SNico Boehr  *   verifies that the storage keys are still as we expect.
8544f8b50SNico Boehr  * - parallel: start migration and set and check storage keys on some
9544f8b50SNico Boehr  *   pages while migration is in process.
10544f8b50SNico Boehr  *
111a7b0521SNico Boehr  * Copyright IBM Corp. 2022
121a7b0521SNico Boehr  *
131a7b0521SNico Boehr  * Authors:
141a7b0521SNico Boehr  *  Nico Boehr <nrb@linux.ibm.com>
151a7b0521SNico Boehr  */
161a7b0521SNico Boehr 
171a7b0521SNico Boehr #include <libcflat.h>
18eef2cdb5SNico Boehr #include <migrate.h>
191a7b0521SNico Boehr #include <asm/facility.h>
201a7b0521SNico Boehr #include <asm/page.h>
211a7b0521SNico Boehr #include <asm/mem.h>
22544f8b50SNico Boehr #include <asm/barrier.h>
231a7b0521SNico Boehr #include <hardware.h>
24544f8b50SNico Boehr #include <smp.h>
25544f8b50SNico Boehr 
26544f8b50SNico Boehr struct verify_result {
27544f8b50SNico Boehr 	bool verify_failed;
28544f8b50SNico Boehr 	union skey expected_key;
29544f8b50SNico Boehr 	union skey actual_key;
30544f8b50SNico Boehr 	unsigned long page_mismatch_idx;
31544f8b50SNico Boehr 	unsigned long page_mismatch_addr;
32544f8b50SNico Boehr };
331a7b0521SNico Boehr 
341a7b0521SNico Boehr #define NUM_PAGES 128
35544f8b50SNico Boehr static uint8_t pagebuf[NUM_PAGES * PAGE_SIZE] __attribute__((aligned(PAGE_SIZE)));
361a7b0521SNico Boehr 
37544f8b50SNico Boehr static struct verify_result result;
38544f8b50SNico Boehr 
39544f8b50SNico Boehr static unsigned int thread_iters;
40544f8b50SNico Boehr static bool thread_should_exit;
41544f8b50SNico Boehr static bool thread_exited;
42544f8b50SNico Boehr 
43544f8b50SNico Boehr static enum {
44544f8b50SNico Boehr 	TEST_INVALID,
45544f8b50SNico Boehr 	TEST_SEQUENTIAL,
46544f8b50SNico Boehr 	TEST_PARALLEL
47544f8b50SNico Boehr } arg_test_to_run;
48544f8b50SNico Boehr 
49544f8b50SNico Boehr /*
50544f8b50SNico Boehr  * Set storage key test pattern on pagebuf with a seed for the storage keys.
51544f8b50SNico Boehr  *
52544f8b50SNico Boehr  * Each page's storage key is generated by taking the page's index in pagebuf,
53544f8b50SNico Boehr  * XOR-ing that with the given seed and then multipling the result with two.
54544f8b50SNico Boehr  *
55544f8b50SNico Boehr  * Only the lower seven bits of the seed are considered.
56544f8b50SNico Boehr  */
set_test_pattern(unsigned char seed)57544f8b50SNico Boehr static void set_test_pattern(unsigned char seed)
581a7b0521SNico Boehr {
59544f8b50SNico Boehr 	unsigned char key_to_set;
60544f8b50SNico Boehr 	unsigned long i;
611a7b0521SNico Boehr 
621a7b0521SNico Boehr 	for (i = 0; i < NUM_PAGES; i++) {
631a7b0521SNico Boehr 		/*
641a7b0521SNico Boehr 		 * Storage keys are 7 bit, lowest bit is always returned as zero
651a7b0521SNico Boehr 		 * by iske.
661a7b0521SNico Boehr 		 * This loop will set all 7 bits which means we set fetch
671a7b0521SNico Boehr 		 * protection as well as reference and change indication for
681a7b0521SNico Boehr 		 * some keys.
691a7b0521SNico Boehr 		 */
70544f8b50SNico Boehr 		key_to_set = (i ^ seed) * 2;
71544f8b50SNico Boehr 		set_storage_key(pagebuf + i * PAGE_SIZE, key_to_set, 1);
72544f8b50SNico Boehr 	}
731a7b0521SNico Boehr }
741a7b0521SNico Boehr 
75544f8b50SNico Boehr /*
76544f8b50SNico Boehr  * Verify storage keys on pagebuf.
77544f8b50SNico Boehr  * Storage keys must have been set by set_test_pattern on pagebuf before.
78544f8b50SNico Boehr  * set_test_pattern must have been called with the same seed value.
79544f8b50SNico Boehr  *
80544f8b50SNico Boehr  * If storage keys match the expected result, will return a verify_result
81544f8b50SNico Boehr  * with verify_failed false. All other fields are then invalid.
82544f8b50SNico Boehr  * If there is a mismatch, returned struct will have verify_failed true and will
83544f8b50SNico Boehr  * be filled with the details on the first mismatch encountered.
84544f8b50SNico Boehr  */
verify_test_pattern(unsigned char seed)85544f8b50SNico Boehr static struct verify_result verify_test_pattern(unsigned char seed)
86544f8b50SNico Boehr {
87544f8b50SNico Boehr 	union skey expected_key, actual_key;
88544f8b50SNico Boehr 	struct verify_result result = {
89544f8b50SNico Boehr 		.verify_failed = true
90544f8b50SNico Boehr 	};
91544f8b50SNico Boehr 	uint8_t *cur_page;
92544f8b50SNico Boehr 	unsigned long i;
931a7b0521SNico Boehr 
941a7b0521SNico Boehr 	for (i = 0; i < NUM_PAGES; i++) {
95544f8b50SNico Boehr 		cur_page = pagebuf + i * PAGE_SIZE;
96544f8b50SNico Boehr 		actual_key.val = get_storage_key(cur_page);
97544f8b50SNico Boehr 		expected_key.val = (i ^ seed) * 2;
981a7b0521SNico Boehr 
991a7b0521SNico Boehr 		/*
1001a7b0521SNico Boehr 		 * The PoP neither gives a guarantee that the reference bit is
1011a7b0521SNico Boehr 		 * accurate nor that it won't be cleared by hardware. Hence we
1021a7b0521SNico Boehr 		 * don't rely on it and just clear the bits to avoid compare
1031a7b0521SNico Boehr 		 * errors.
1041a7b0521SNico Boehr 		 */
1051a7b0521SNico Boehr 		actual_key.str.rf = 0;
1061a7b0521SNico Boehr 		expected_key.str.rf = 0;
1071a7b0521SNico Boehr 
1081a7b0521SNico Boehr 		if (actual_key.val != expected_key.val) {
109544f8b50SNico Boehr 			result.expected_key.val = expected_key.val;
110544f8b50SNico Boehr 			result.actual_key.val = actual_key.val;
111544f8b50SNico Boehr 			result.page_mismatch_idx = i;
112544f8b50SNico Boehr 			result.page_mismatch_addr = (unsigned long)cur_page;
113544f8b50SNico Boehr 			return result;
1141a7b0521SNico Boehr 		}
1151a7b0521SNico Boehr 	}
1161a7b0521SNico Boehr 
117544f8b50SNico Boehr 	result.verify_failed = false;
118544f8b50SNico Boehr 	return result;
1191a7b0521SNico Boehr }
1201a7b0521SNico Boehr 
report_verify_result(const struct verify_result * result)121544f8b50SNico Boehr static void report_verify_result(const struct verify_result * result)
1221a7b0521SNico Boehr {
123544f8b50SNico Boehr 	if (result->verify_failed)
124544f8b50SNico Boehr 		report_fail("page skey mismatch: first page idx = %lu, addr = 0x%lx, "
125544f8b50SNico Boehr 			    "expected_key = 0x%02x, actual_key = 0x%02x",
126544f8b50SNico Boehr 			    result->page_mismatch_idx, result->page_mismatch_addr,
127544f8b50SNico Boehr 			    result->expected_key.val, result->actual_key.val);
128eef2cdb5SNico Boehr 	else
129544f8b50SNico Boehr 		report_pass("skeys match");
130544f8b50SNico Boehr }
131544f8b50SNico Boehr 
test_skey_migration_sequential(void)132544f8b50SNico Boehr static void test_skey_migration_sequential(void)
133544f8b50SNico Boehr {
134544f8b50SNico Boehr 	report_prefix_push("sequential");
135544f8b50SNico Boehr 
136544f8b50SNico Boehr 	set_test_pattern(0);
137eef2cdb5SNico Boehr 
138eef2cdb5SNico Boehr 	migrate_once();
1391a7b0521SNico Boehr 
140544f8b50SNico Boehr 	result = verify_test_pattern(0);
141544f8b50SNico Boehr 	report_verify_result(&result);
142544f8b50SNico Boehr 
143544f8b50SNico Boehr 	report_prefix_pop();
144544f8b50SNico Boehr }
145544f8b50SNico Boehr 
set_skeys_thread(void)146544f8b50SNico Boehr static void set_skeys_thread(void)
147544f8b50SNico Boehr {
148544f8b50SNico Boehr 	while (!READ_ONCE(thread_should_exit)) {
149544f8b50SNico Boehr 		set_test_pattern(thread_iters);
150544f8b50SNico Boehr 
151544f8b50SNico Boehr 		result = verify_test_pattern(thread_iters);
152544f8b50SNico Boehr 
153544f8b50SNico Boehr 		/*
154544f8b50SNico Boehr 		 * Always increment even if the verify fails. This ensures primary CPU knows where
155544f8b50SNico Boehr 		 * we left off and can do an additional verify round after migration finished.
156544f8b50SNico Boehr 		 */
157544f8b50SNico Boehr 		thread_iters++;
158544f8b50SNico Boehr 
159544f8b50SNico Boehr 		if (result.verify_failed)
160544f8b50SNico Boehr 			break;
161544f8b50SNico Boehr 	}
162544f8b50SNico Boehr 
163544f8b50SNico Boehr 	WRITE_ONCE(thread_exited, 1);
164544f8b50SNico Boehr }
165544f8b50SNico Boehr 
test_skey_migration_parallel(void)166544f8b50SNico Boehr static void test_skey_migration_parallel(void)
167544f8b50SNico Boehr {
168544f8b50SNico Boehr 	report_prefix_push("parallel");
169544f8b50SNico Boehr 
170544f8b50SNico Boehr 	if (smp_query_num_cpus() == 1) {
171544f8b50SNico Boehr 		report_skip("need at least 2 cpus for this test");
172*e6e1a201SNicholas Piggin 		migrate_skip();
173544f8b50SNico Boehr 		goto error;
174544f8b50SNico Boehr 	}
175544f8b50SNico Boehr 
176544f8b50SNico Boehr 	smp_cpu_setup(1, PSW_WITH_CUR_MASK(set_skeys_thread));
177544f8b50SNico Boehr 
178544f8b50SNico Boehr 	migrate_once();
179544f8b50SNico Boehr 
180544f8b50SNico Boehr 	WRITE_ONCE(thread_should_exit, 1);
181544f8b50SNico Boehr 
182544f8b50SNico Boehr 	while (!READ_ONCE(thread_exited))
183544f8b50SNico Boehr 		;
184544f8b50SNico Boehr 
185544f8b50SNico Boehr 	/* Ensure we read result and thread_iters below from memory after thread exited */
186544f8b50SNico Boehr 	mb();
187544f8b50SNico Boehr 	report_info("thread completed %u iterations", thread_iters);
188544f8b50SNico Boehr 
189544f8b50SNico Boehr 	report_prefix_push("during migration");
190544f8b50SNico Boehr 	report_verify_result(&result);
191544f8b50SNico Boehr 	report_prefix_pop();
192544f8b50SNico Boehr 
193544f8b50SNico Boehr 	/*
194544f8b50SNico Boehr 	 * Verification of skeys occurs on the thread. We don't know if we
195544f8b50SNico Boehr 	 * were still migrating during the verification.
196544f8b50SNico Boehr 	 * To be sure, make another verification round after the migration
197544f8b50SNico Boehr 	 * finished to catch skeys which might not have been migrated
198544f8b50SNico Boehr 	 * correctly.
199544f8b50SNico Boehr 	 */
200544f8b50SNico Boehr 	report_prefix_push("after migration");
201544f8b50SNico Boehr 	assert(thread_iters > 0);
202544f8b50SNico Boehr 	result = verify_test_pattern(thread_iters - 1);
203544f8b50SNico Boehr 	report_verify_result(&result);
204544f8b50SNico Boehr 	report_prefix_pop();
205544f8b50SNico Boehr 
206544f8b50SNico Boehr error:
207544f8b50SNico Boehr 	report_prefix_pop();
208544f8b50SNico Boehr }
209544f8b50SNico Boehr 
print_usage(void)210544f8b50SNico Boehr static void print_usage(void)
211544f8b50SNico Boehr {
212544f8b50SNico Boehr 	report_info("Usage: migration-skey [--parallel|--sequential]");
213544f8b50SNico Boehr }
214544f8b50SNico Boehr 
parse_args(int argc,char ** argv)215544f8b50SNico Boehr static void parse_args(int argc, char **argv)
216544f8b50SNico Boehr {
217544f8b50SNico Boehr 	if (argc < 2) {
218544f8b50SNico Boehr 		/* default to sequential since it only needs one cpu */
219544f8b50SNico Boehr 		arg_test_to_run = TEST_SEQUENTIAL;
220544f8b50SNico Boehr 		return;
221544f8b50SNico Boehr 	}
222544f8b50SNico Boehr 
223544f8b50SNico Boehr 	if (!strcmp("--parallel", argv[1]))
224544f8b50SNico Boehr 		arg_test_to_run = TEST_PARALLEL;
225544f8b50SNico Boehr 	else if (!strcmp("--sequential", argv[1]))
226544f8b50SNico Boehr 		arg_test_to_run = TEST_SEQUENTIAL;
227544f8b50SNico Boehr 	else
228544f8b50SNico Boehr 		arg_test_to_run = TEST_INVALID;
229544f8b50SNico Boehr }
230544f8b50SNico Boehr 
main(int argc,char ** argv)231544f8b50SNico Boehr int main(int argc, char **argv)
232544f8b50SNico Boehr {
233544f8b50SNico Boehr 	report_prefix_push("migration-skey");
234544f8b50SNico Boehr 
235544f8b50SNico Boehr 	if (test_facility(169)) {
236544f8b50SNico Boehr 		report_skip("storage key removal facility is active");
237*e6e1a201SNicholas Piggin 		migrate_skip();
238544f8b50SNico Boehr 		goto error;
239544f8b50SNico Boehr 	}
240544f8b50SNico Boehr 
241544f8b50SNico Boehr 	parse_args(argc, argv);
242544f8b50SNico Boehr 
243544f8b50SNico Boehr 	switch (arg_test_to_run) {
244544f8b50SNico Boehr 	case TEST_SEQUENTIAL:
245544f8b50SNico Boehr 		test_skey_migration_sequential();
246544f8b50SNico Boehr 		break;
247544f8b50SNico Boehr 	case TEST_PARALLEL:
248544f8b50SNico Boehr 		test_skey_migration_parallel();
249544f8b50SNico Boehr 		break;
250544f8b50SNico Boehr 	default:
251544f8b50SNico Boehr 		print_usage();
252*e6e1a201SNicholas Piggin 		migrate_skip();
253544f8b50SNico Boehr 		break;
254544f8b50SNico Boehr 	}
255544f8b50SNico Boehr 
256544f8b50SNico Boehr error:
2571a7b0521SNico Boehr 	report_prefix_pop();
2581a7b0521SNico Boehr 	return report_summary();
2591a7b0521SNico Boehr }
260