Skip to content

Commit 52940a3

Browse files
author
Claudio Imbrenda
committed
KVM: s390: selftests: Add selftest for the KVM_S390_KEYOP ioctl
This test allows to test the various storage key handling functions. Acked-by: Heiko Carstens <hca@linux.ibm.com> Signed-off-by: Claudio Imbrenda <imbrenda@linux.ibm.com>
1 parent 0ee4ddc commit 52940a3

2 files changed

Lines changed: 300 additions & 0 deletions

File tree

tools/testing/selftests/kvm/Makefile.kvm

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,7 @@ TEST_GEN_PROGS_s390 += s390/cpumodel_subfuncs_test
199199
TEST_GEN_PROGS_s390 += s390/shared_zeropage_test
200200
TEST_GEN_PROGS_s390 += s390/ucontrol_test
201201
TEST_GEN_PROGS_s390 += s390/user_operexec
202+
TEST_GEN_PROGS_s390 += s390/keyop
202203
TEST_GEN_PROGS_s390 += rseq_test
203204

204205
TEST_GEN_PROGS_riscv = $(TEST_GEN_PROGS_COMMON)
Lines changed: 299 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,299 @@
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+
36+
static unsigned char tmp[BUF_PAGES];
37+
static unsigned char old[BUF_PAGES];
38+
static unsigned char expected[BUF_PAGES];
39+
40+
static 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+
51+
static 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+
58+
static 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+
71+
static 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+
89+
static 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+
103+
static 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+
111+
static 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+
124+
static 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+
140+
static 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+
147+
static 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+
167+
static 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+
197+
static 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+
221+
static 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+
247+
static 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+
258+
static 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+
279+
int 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+
}

0 commit comments

Comments
 (0)