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