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 } 300