Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

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>

+300
+1
tools/testing/selftests/kvm/Makefile.kvm
··· 199 199 TEST_GEN_PROGS_s390 += s390/shared_zeropage_test 200 200 TEST_GEN_PROGS_s390 += s390/ucontrol_test 201 201 TEST_GEN_PROGS_s390 += s390/user_operexec 202 + TEST_GEN_PROGS_s390 += s390/keyop 202 203 TEST_GEN_PROGS_s390 += rseq_test 203 204 204 205 TEST_GEN_PROGS_riscv = $(TEST_GEN_PROGS_COMMON)
+299
tools/testing/selftests/kvm/s390/keyop.c
··· 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 + }