xref: /linux/tools/testing/selftests/kvm/s390/keyop.c (revision c17ee635fd3a482b2ad2bf5e269755c2eae5f25e)
1*52940a34SClaudio Imbrenda // SPDX-License-Identifier: GPL-2.0-only
2*52940a34SClaudio Imbrenda /*
3*52940a34SClaudio Imbrenda  * Test for s390x KVM_S390_KEYOP
4*52940a34SClaudio Imbrenda  *
5*52940a34SClaudio Imbrenda  * Copyright IBM Corp. 2026
6*52940a34SClaudio Imbrenda  *
7*52940a34SClaudio Imbrenda  * Authors:
8*52940a34SClaudio Imbrenda  *  Claudio Imbrenda <imbrenda@linux.ibm.com>
9*52940a34SClaudio Imbrenda  */
10*52940a34SClaudio Imbrenda #include <stdio.h>
11*52940a34SClaudio Imbrenda #include <stdlib.h>
12*52940a34SClaudio Imbrenda #include <string.h>
13*52940a34SClaudio Imbrenda #include <sys/ioctl.h>
14*52940a34SClaudio Imbrenda 
15*52940a34SClaudio Imbrenda #include <linux/bits.h>
16*52940a34SClaudio Imbrenda 
17*52940a34SClaudio Imbrenda #include "test_util.h"
18*52940a34SClaudio Imbrenda #include "kvm_util.h"
19*52940a34SClaudio Imbrenda #include "kselftest.h"
20*52940a34SClaudio Imbrenda #include "processor.h"
21*52940a34SClaudio Imbrenda 
22*52940a34SClaudio Imbrenda #define BUF_PAGES 128UL
23*52940a34SClaudio Imbrenda #define GUEST_PAGES 256UL
24*52940a34SClaudio Imbrenda 
25*52940a34SClaudio Imbrenda #define BUF_START_GFN	(GUEST_PAGES - BUF_PAGES)
26*52940a34SClaudio Imbrenda #define BUF_START_ADDR	(BUF_START_GFN << PAGE_SHIFT)
27*52940a34SClaudio Imbrenda 
28*52940a34SClaudio Imbrenda #define KEY_BITS_ACC	0xf0
29*52940a34SClaudio Imbrenda #define KEY_BIT_F	0x08
30*52940a34SClaudio Imbrenda #define KEY_BIT_R	0x04
31*52940a34SClaudio Imbrenda #define KEY_BIT_C	0x02
32*52940a34SClaudio Imbrenda 
33*52940a34SClaudio Imbrenda #define KEY_BITS_RC	(KEY_BIT_R | KEY_BIT_C)
34*52940a34SClaudio Imbrenda #define KEY_BITS_ALL	(KEY_BITS_ACC | KEY_BIT_F | KEY_BITS_RC)
35*52940a34SClaudio Imbrenda 
36*52940a34SClaudio Imbrenda static unsigned char tmp[BUF_PAGES];
37*52940a34SClaudio Imbrenda static unsigned char old[BUF_PAGES];
38*52940a34SClaudio Imbrenda static unsigned char expected[BUF_PAGES];
39*52940a34SClaudio Imbrenda 
40*52940a34SClaudio Imbrenda static int _get_skeys(struct kvm_vcpu *vcpu, unsigned char skeys[])
41*52940a34SClaudio Imbrenda {
42*52940a34SClaudio Imbrenda 	struct kvm_s390_skeys skeys_ioctl = {
43*52940a34SClaudio Imbrenda 		.start_gfn = BUF_START_GFN,
44*52940a34SClaudio Imbrenda 		.count = BUF_PAGES,
45*52940a34SClaudio Imbrenda 		.skeydata_addr = (unsigned long)skeys,
46*52940a34SClaudio Imbrenda 	};
47*52940a34SClaudio Imbrenda 
48*52940a34SClaudio Imbrenda 	return __vm_ioctl(vcpu->vm, KVM_S390_GET_SKEYS, &skeys_ioctl);
49*52940a34SClaudio Imbrenda }
50*52940a34SClaudio Imbrenda 
51*52940a34SClaudio Imbrenda static void get_skeys(struct kvm_vcpu *vcpu, unsigned char skeys[])
52*52940a34SClaudio Imbrenda {
53*52940a34SClaudio Imbrenda 	int r = _get_skeys(vcpu, skeys);
54*52940a34SClaudio Imbrenda 
55*52940a34SClaudio Imbrenda 	TEST_ASSERT(!r, "Failed to get storage keys, r=%d", r);
56*52940a34SClaudio Imbrenda }
57*52940a34SClaudio Imbrenda 
58*52940a34SClaudio Imbrenda static void set_skeys(struct kvm_vcpu *vcpu, unsigned char skeys[])
59*52940a34SClaudio Imbrenda {
60*52940a34SClaudio Imbrenda 	struct kvm_s390_skeys skeys_ioctl = {
61*52940a34SClaudio Imbrenda 		.start_gfn = BUF_START_GFN,
62*52940a34SClaudio Imbrenda 		.count = BUF_PAGES,
63*52940a34SClaudio Imbrenda 		.skeydata_addr = (unsigned long)skeys,
64*52940a34SClaudio Imbrenda 	};
65*52940a34SClaudio Imbrenda 	int r;
66*52940a34SClaudio Imbrenda 
67*52940a34SClaudio Imbrenda 	r = __vm_ioctl(vcpu->vm, KVM_S390_SET_SKEYS, &skeys_ioctl);
68*52940a34SClaudio Imbrenda 	TEST_ASSERT(!r, "Failed to set storage keys, r=%d", r);
69*52940a34SClaudio Imbrenda }
70*52940a34SClaudio Imbrenda 
71*52940a34SClaudio Imbrenda static int do_keyop(struct kvm_vcpu *vcpu, int op, unsigned long page_idx, unsigned char skey)
72*52940a34SClaudio Imbrenda {
73*52940a34SClaudio Imbrenda 	struct kvm_s390_keyop keyop = {
74*52940a34SClaudio Imbrenda 		.guest_addr = BUF_START_ADDR + page_idx * PAGE_SIZE,
75*52940a34SClaudio Imbrenda 		.key = skey,
76*52940a34SClaudio Imbrenda 		.operation = op,
77*52940a34SClaudio Imbrenda 	};
78*52940a34SClaudio Imbrenda 	int r;
79*52940a34SClaudio Imbrenda 
80*52940a34SClaudio Imbrenda 	r = __vm_ioctl(vcpu->vm, KVM_S390_KEYOP, &keyop);
81*52940a34SClaudio Imbrenda 	TEST_ASSERT(!r, "Failed to perform keyop, r=%d", r);
82*52940a34SClaudio Imbrenda 	TEST_ASSERT((keyop.key & 1) == 0,
83*52940a34SClaudio Imbrenda 		    "Last bit of key is 1, should be 0! page %lu, new key=%#x, old key=%#x",
84*52940a34SClaudio Imbrenda 		    page_idx, skey, keyop.key);
85*52940a34SClaudio Imbrenda 
86*52940a34SClaudio Imbrenda 	return keyop.key;
87*52940a34SClaudio Imbrenda }
88*52940a34SClaudio Imbrenda 
89*52940a34SClaudio Imbrenda static void fault_in_buffer(struct kvm_vcpu *vcpu, int where, int cur_loc)
90*52940a34SClaudio Imbrenda {
91*52940a34SClaudio Imbrenda 	unsigned long i;
92*52940a34SClaudio Imbrenda 	int r;
93*52940a34SClaudio Imbrenda 
94*52940a34SClaudio Imbrenda 	if (where != cur_loc)
95*52940a34SClaudio Imbrenda 		return;
96*52940a34SClaudio Imbrenda 
97*52940a34SClaudio Imbrenda 	for (i = 0; i < BUF_PAGES; i++) {
98*52940a34SClaudio Imbrenda 		r = ioctl(vcpu->fd, KVM_S390_VCPU_FAULT, BUF_START_ADDR + i * PAGE_SIZE);
99*52940a34SClaudio Imbrenda 		TEST_ASSERT(!r, "Faulting in buffer page %lu, r=%d", i, r);
100*52940a34SClaudio Imbrenda 	}
101*52940a34SClaudio Imbrenda }
102*52940a34SClaudio Imbrenda 
103*52940a34SClaudio Imbrenda static inline void set_pattern(unsigned char skeys[])
104*52940a34SClaudio Imbrenda {
105*52940a34SClaudio Imbrenda 	int i;
106*52940a34SClaudio Imbrenda 
107*52940a34SClaudio Imbrenda 	for (i = 0; i < BUF_PAGES; i++)
108*52940a34SClaudio Imbrenda 		skeys[i] = i << 1;
109*52940a34SClaudio Imbrenda }
110*52940a34SClaudio Imbrenda 
111*52940a34SClaudio Imbrenda static void dump_sk(const unsigned char skeys[], const char *descr)
112*52940a34SClaudio Imbrenda {
113*52940a34SClaudio Imbrenda 	int i, j;
114*52940a34SClaudio Imbrenda 
115*52940a34SClaudio Imbrenda 	fprintf(stderr, "# %s:\n", descr);
116*52940a34SClaudio Imbrenda 	for (i = 0; i < BUF_PAGES; i += 32) {
117*52940a34SClaudio Imbrenda 		fprintf(stderr, "# %3d: ", i);
118*52940a34SClaudio Imbrenda 		for (j = 0; j < 32; j++)
119*52940a34SClaudio Imbrenda 			fprintf(stderr, "%02x ", skeys[i + j]);
120*52940a34SClaudio Imbrenda 		fprintf(stderr, "\n");
121*52940a34SClaudio Imbrenda 	}
122*52940a34SClaudio Imbrenda }
123*52940a34SClaudio Imbrenda 
124*52940a34SClaudio Imbrenda static inline void compare(const unsigned char what[], const unsigned char expected[],
125*52940a34SClaudio Imbrenda 			   const char *descr, int fault_in_loc)
126*52940a34SClaudio Imbrenda {
127*52940a34SClaudio Imbrenda 	int i;
128*52940a34SClaudio Imbrenda 
129*52940a34SClaudio Imbrenda 	for (i = 0; i < BUF_PAGES; i++) {
130*52940a34SClaudio Imbrenda 		if (expected[i] != what[i]) {
131*52940a34SClaudio Imbrenda 			dump_sk(expected, "Expected");
132*52940a34SClaudio Imbrenda 			dump_sk(what, "Got");
133*52940a34SClaudio Imbrenda 		}
134*52940a34SClaudio Imbrenda 		TEST_ASSERT(expected[i] == what[i],
135*52940a34SClaudio Imbrenda 			    "%s! fault-in location %d, page %d, expected %#x, got %#x",
136*52940a34SClaudio Imbrenda 			    descr, fault_in_loc, i, expected[i], what[i]);
137*52940a34SClaudio Imbrenda 	}
138*52940a34SClaudio Imbrenda }
139*52940a34SClaudio Imbrenda 
140*52940a34SClaudio Imbrenda static inline void clear_all(void)
141*52940a34SClaudio Imbrenda {
142*52940a34SClaudio Imbrenda 	memset(tmp, 0, BUF_PAGES);
143*52940a34SClaudio Imbrenda 	memset(old, 0, BUF_PAGES);
144*52940a34SClaudio Imbrenda 	memset(expected, 0, BUF_PAGES);
145*52940a34SClaudio Imbrenda }
146*52940a34SClaudio Imbrenda 
147*52940a34SClaudio Imbrenda static void test_init(struct kvm_vcpu *vcpu, int fault_in)
148*52940a34SClaudio Imbrenda {
149*52940a34SClaudio Imbrenda 	/* Set all storage keys to zero */
150*52940a34SClaudio Imbrenda 	fault_in_buffer(vcpu, fault_in, 1);
151*52940a34SClaudio Imbrenda 	set_skeys(vcpu, expected);
152*52940a34SClaudio Imbrenda 
153*52940a34SClaudio Imbrenda 	fault_in_buffer(vcpu, fault_in, 2);
154*52940a34SClaudio Imbrenda 	get_skeys(vcpu, tmp);
155*52940a34SClaudio Imbrenda 	compare(tmp, expected, "Setting keys not zero", fault_in);
156*52940a34SClaudio Imbrenda 
157*52940a34SClaudio Imbrenda 	/* Set storage keys to a sequential pattern */
158*52940a34SClaudio Imbrenda 	fault_in_buffer(vcpu, fault_in, 3);
159*52940a34SClaudio Imbrenda 	set_pattern(expected);
160*52940a34SClaudio Imbrenda 	set_skeys(vcpu, expected);
161*52940a34SClaudio Imbrenda 
162*52940a34SClaudio Imbrenda 	fault_in_buffer(vcpu, fault_in, 4);
163*52940a34SClaudio Imbrenda 	get_skeys(vcpu, tmp);
164*52940a34SClaudio Imbrenda 	compare(tmp, expected, "Setting storage keys failed", fault_in);
165*52940a34SClaudio Imbrenda }
166*52940a34SClaudio Imbrenda 
167*52940a34SClaudio Imbrenda static void test_rrbe(struct kvm_vcpu *vcpu, int fault_in)
168*52940a34SClaudio Imbrenda {
169*52940a34SClaudio Imbrenda 	unsigned char k;
170*52940a34SClaudio Imbrenda 	int i;
171*52940a34SClaudio Imbrenda 
172*52940a34SClaudio Imbrenda 	/* Set storage keys to a sequential pattern */
173*52940a34SClaudio Imbrenda 	fault_in_buffer(vcpu, fault_in, 1);
174*52940a34SClaudio Imbrenda 	set_pattern(expected);
175*52940a34SClaudio Imbrenda 	set_skeys(vcpu, expected);
176*52940a34SClaudio Imbrenda 
177*52940a34SClaudio Imbrenda 	/* Call the RRBE KEYOP ioctl on each page and verify the result */
178*52940a34SClaudio Imbrenda 	fault_in_buffer(vcpu, fault_in, 2);
179*52940a34SClaudio Imbrenda 	for (i = 0; i < BUF_PAGES; i++) {
180*52940a34SClaudio Imbrenda 		k = do_keyop(vcpu, KVM_S390_KEYOP_RRBE, i, 0xff);
181*52940a34SClaudio Imbrenda 		TEST_ASSERT((expected[i] & KEY_BITS_RC) == k,
182*52940a34SClaudio Imbrenda 			    "Old R or C value mismatch! expected: %#x, got %#x",
183*52940a34SClaudio Imbrenda 			    expected[i] & KEY_BITS_RC, k);
184*52940a34SClaudio Imbrenda 		if (i == BUF_PAGES / 2)
185*52940a34SClaudio Imbrenda 			fault_in_buffer(vcpu, fault_in, 3);
186*52940a34SClaudio Imbrenda 	}
187*52940a34SClaudio Imbrenda 
188*52940a34SClaudio Imbrenda 	for (i = 0; i < BUF_PAGES; i++)
189*52940a34SClaudio Imbrenda 		expected[i] &= ~KEY_BIT_R;
190*52940a34SClaudio Imbrenda 
191*52940a34SClaudio Imbrenda 	/* Verify that only the R bit has been cleared */
192*52940a34SClaudio Imbrenda 	fault_in_buffer(vcpu, fault_in, 4);
193*52940a34SClaudio Imbrenda 	get_skeys(vcpu, tmp);
194*52940a34SClaudio Imbrenda 	compare(tmp, expected, "New value mismatch", fault_in);
195*52940a34SClaudio Imbrenda }
196*52940a34SClaudio Imbrenda 
197*52940a34SClaudio Imbrenda static void test_iske(struct kvm_vcpu *vcpu, int fault_in)
198*52940a34SClaudio Imbrenda {
199*52940a34SClaudio Imbrenda 	int i;
200*52940a34SClaudio Imbrenda 
201*52940a34SClaudio Imbrenda 	/* Set storage keys to a sequential pattern */
202*52940a34SClaudio Imbrenda 	fault_in_buffer(vcpu, fault_in, 1);
203*52940a34SClaudio Imbrenda 	set_pattern(expected);
204*52940a34SClaudio Imbrenda 	set_skeys(vcpu, expected);
205*52940a34SClaudio Imbrenda 
206*52940a34SClaudio Imbrenda 	/* Call the ISKE KEYOP ioctl on each page and verify the result */
207*52940a34SClaudio Imbrenda 	fault_in_buffer(vcpu, fault_in, 2);
208*52940a34SClaudio Imbrenda 	for (i = 0; i < BUF_PAGES; i++) {
209*52940a34SClaudio Imbrenda 		tmp[i] = do_keyop(vcpu, KVM_S390_KEYOP_ISKE, i, 0xff);
210*52940a34SClaudio Imbrenda 		if (i == BUF_PAGES / 2)
211*52940a34SClaudio Imbrenda 			fault_in_buffer(vcpu, fault_in, 3);
212*52940a34SClaudio Imbrenda 	}
213*52940a34SClaudio Imbrenda 	compare(tmp, expected, "Old value mismatch", fault_in);
214*52940a34SClaudio Imbrenda 
215*52940a34SClaudio Imbrenda 	/* Check storage keys have not changed */
216*52940a34SClaudio Imbrenda 	fault_in_buffer(vcpu, fault_in, 4);
217*52940a34SClaudio Imbrenda 	get_skeys(vcpu, tmp);
218*52940a34SClaudio Imbrenda 	compare(tmp, expected, "Storage keys values changed", fault_in);
219*52940a34SClaudio Imbrenda }
220*52940a34SClaudio Imbrenda 
221*52940a34SClaudio Imbrenda static void test_sske(struct kvm_vcpu *vcpu, int fault_in)
222*52940a34SClaudio Imbrenda {
223*52940a34SClaudio Imbrenda 	int i;
224*52940a34SClaudio Imbrenda 
225*52940a34SClaudio Imbrenda 	/* Set storage keys to a sequential pattern */
226*52940a34SClaudio Imbrenda 	fault_in_buffer(vcpu, fault_in, 1);
227*52940a34SClaudio Imbrenda 	set_pattern(tmp);
228*52940a34SClaudio Imbrenda 	set_skeys(vcpu, tmp);
229*52940a34SClaudio Imbrenda 
230*52940a34SClaudio Imbrenda 	/* Call the SSKE KEYOP ioctl on each page and verify the result */
231*52940a34SClaudio Imbrenda 	fault_in_buffer(vcpu, fault_in, 2);
232*52940a34SClaudio Imbrenda 	for (i = 0; i < BUF_PAGES; i++) {
233*52940a34SClaudio Imbrenda 		expected[i] = ~tmp[i] & KEY_BITS_ALL;
234*52940a34SClaudio Imbrenda 		/* Set the new storage keys to be the bit-inversion of the previous ones */
235*52940a34SClaudio Imbrenda 		old[i] = do_keyop(vcpu, KVM_S390_KEYOP_SSKE, i, expected[i] | 1);
236*52940a34SClaudio Imbrenda 		if (i == BUF_PAGES / 2)
237*52940a34SClaudio Imbrenda 			fault_in_buffer(vcpu, fault_in, 3);
238*52940a34SClaudio Imbrenda 	}
239*52940a34SClaudio Imbrenda 	compare(old, tmp, "Old value mismatch", fault_in);
240*52940a34SClaudio Imbrenda 
241*52940a34SClaudio Imbrenda 	/* Verify that the storage keys have been set correctly */
242*52940a34SClaudio Imbrenda 	fault_in_buffer(vcpu, fault_in, 4);
243*52940a34SClaudio Imbrenda 	get_skeys(vcpu, tmp);
244*52940a34SClaudio Imbrenda 	compare(tmp, expected, "New value mismatch", fault_in);
245*52940a34SClaudio Imbrenda }
246*52940a34SClaudio Imbrenda 
247*52940a34SClaudio Imbrenda static struct testdef {
248*52940a34SClaudio Imbrenda 	const char *name;
249*52940a34SClaudio Imbrenda 	void (*test)(struct kvm_vcpu *vcpu, int fault_in_location);
250*52940a34SClaudio Imbrenda 	int n_fault_in_locations;
251*52940a34SClaudio Imbrenda } testplan[] = {
252*52940a34SClaudio Imbrenda 	{ "Initialization", test_init, 5 },
253*52940a34SClaudio Imbrenda 	{ "RRBE", test_rrbe, 5 },
254*52940a34SClaudio Imbrenda 	{ "ISKE", test_iske, 5 },
255*52940a34SClaudio Imbrenda 	{ "SSKE", test_sske, 5 },
256*52940a34SClaudio Imbrenda };
257*52940a34SClaudio Imbrenda 
258*52940a34SClaudio Imbrenda static void run_test(void (*the_test)(struct kvm_vcpu *, int), int fault_in_location)
259*52940a34SClaudio Imbrenda {
260*52940a34SClaudio Imbrenda 	struct kvm_vcpu *vcpu;
261*52940a34SClaudio Imbrenda 	struct kvm_vm *vm;
262*52940a34SClaudio Imbrenda 	int r;
263*52940a34SClaudio Imbrenda 
264*52940a34SClaudio Imbrenda 	vm = vm_create_barebones();
265*52940a34SClaudio Imbrenda 	vm_userspace_mem_region_add(vm, VM_MEM_SRC_ANONYMOUS, 0, 0, GUEST_PAGES, 0);
266*52940a34SClaudio Imbrenda 	vcpu = __vm_vcpu_add(vm, 0);
267*52940a34SClaudio Imbrenda 
268*52940a34SClaudio Imbrenda 	r = _get_skeys(vcpu, tmp);
269*52940a34SClaudio Imbrenda 	TEST_ASSERT(r == KVM_S390_GET_SKEYS_NONE,
270*52940a34SClaudio Imbrenda 		    "Storage keys are not disabled initially, r=%d", r);
271*52940a34SClaudio Imbrenda 
272*52940a34SClaudio Imbrenda 	clear_all();
273*52940a34SClaudio Imbrenda 
274*52940a34SClaudio Imbrenda 	the_test(vcpu, fault_in_location);
275*52940a34SClaudio Imbrenda 
276*52940a34SClaudio Imbrenda 	kvm_vm_free(vm);
277*52940a34SClaudio Imbrenda }
278*52940a34SClaudio Imbrenda 
279*52940a34SClaudio Imbrenda int main(int argc, char *argv[])
280*52940a34SClaudio Imbrenda {
281*52940a34SClaudio Imbrenda 	int i, f;
282*52940a34SClaudio Imbrenda 
283*52940a34SClaudio Imbrenda 	TEST_REQUIRE(kvm_has_cap(KVM_CAP_S390_KEYOP));
284*52940a34SClaudio Imbrenda 	TEST_REQUIRE(kvm_has_cap(KVM_CAP_S390_UCONTROL));
285*52940a34SClaudio Imbrenda 
286*52940a34SClaudio Imbrenda 	ksft_print_header();
287*52940a34SClaudio Imbrenda 	for (i = 0, f = 0; i < ARRAY_SIZE(testplan); i++)
288*52940a34SClaudio Imbrenda 		f += testplan[i].n_fault_in_locations;
289*52940a34SClaudio Imbrenda 	ksft_set_plan(f);
290*52940a34SClaudio Imbrenda 
291*52940a34SClaudio Imbrenda 	for (i = 0; i < ARRAY_SIZE(testplan); i++) {
292*52940a34SClaudio Imbrenda 		for (f = 0; f < testplan[i].n_fault_in_locations; f++) {
293*52940a34SClaudio Imbrenda 			run_test(testplan[i].test, f);
294*52940a34SClaudio Imbrenda 			ksft_test_result_pass("%s (fault-in location %d)\n", testplan[i].name, f);
295*52940a34SClaudio Imbrenda 		}
296*52940a34SClaudio Imbrenda 	}
297*52940a34SClaudio Imbrenda 
298*52940a34SClaudio Imbrenda 	ksft_finished();	/* Print results and exit() accordingly */
299*52940a34SClaudio Imbrenda }
300