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

KVM: s390: selftests: Add uc_skey VM test case

Add a test case manipulating s390 storage keys from within the ucontrol
VM.

Storage key instruction (ISKE, SSKE and RRBE) intercepts and
Keyless-subset facility are disabled on first use, where the skeys are
setup by KVM in non ucontrol VMs.

Signed-off-by: Christoph Schlameuss <schlameuss@linux.ibm.com>
Link: https://lore.kernel.org/r/20241108091620.289406-1-schlameuss@linux.ibm.com
Acked-by: Janosch Frank <frankja@linux.ibm.com>
[frankja@linux.ibm.com: Fixed patch prefix]
Signed-off-by: Janosch Frank <frankja@linux.ibm.com>
Message-ID: <20241108091620.289406-1-schlameuss@linux.ibm.com>

authored by

Christoph Schlameuss and committed by
Janosch Frank
0185fbc6 ae16b0ab

+149 -2
+6
tools/testing/selftests/kvm/include/s390x/processor.h
··· 32 32 barrier(); 33 33 } 34 34 35 + /* Get the instruction length */ 36 + static inline int insn_length(unsigned char code) 37 + { 38 + return ((((int)code + 64) >> 7) + 1) << 1; 39 + } 40 + 35 41 #endif
+143 -2
tools/testing/selftests/kvm/s390x/ucontrol_test.c
··· 79 79 " j 0b\n" 80 80 ); 81 81 82 + /* Test program manipulating storage keys */ 83 + extern char test_skey_asm[]; 84 + asm("test_skey_asm:\n" 85 + "xgr %r0, %r0\n" 86 + 87 + "0:\n" 88 + " ahi %r0,1\n" 89 + " st %r1,0(%r5,%r6)\n" 90 + 91 + " iske %r1,%r6\n" 92 + " ahi %r0,1\n" 93 + " diag 0,0,0x44\n" 94 + 95 + " sske %r1,%r6\n" 96 + " xgr %r1,%r1\n" 97 + " iske %r1,%r6\n" 98 + " ahi %r0,1\n" 99 + " diag 0,0,0x44\n" 100 + 101 + " rrbe %r1,%r6\n" 102 + " iske %r1,%r6\n" 103 + " ahi %r0,1\n" 104 + " diag 0,0,0x44\n" 105 + 106 + " j 0b\n" 107 + ); 108 + 82 109 FIXTURE(uc_kvm) 83 110 { 84 111 struct kvm_s390_sie_block *sie_block; ··· 325 298 } 326 299 } 327 300 328 - /* verify SIEIC exit 301 + /* 302 + * Handle the SIEIC exit 329 303 * * fail on codes not expected in the test cases 304 + * Returns if interception is handled / execution can be continued 305 + */ 306 + static void uc_skey_enable(FIXTURE_DATA(uc_kvm) *self) 307 + { 308 + struct kvm_s390_sie_block *sie_block = self->sie_block; 309 + 310 + /* disable KSS */ 311 + sie_block->cpuflags &= ~CPUSTAT_KSS; 312 + /* disable skey inst interception */ 313 + sie_block->ictl &= ~(ICTL_ISKE | ICTL_SSKE | ICTL_RRBE); 314 + } 315 + 316 + /* 317 + * Handle the instruction intercept 318 + * Returns if interception is handled / execution can be continued 319 + */ 320 + static bool uc_handle_insn_ic(FIXTURE_DATA(uc_kvm) *self) 321 + { 322 + struct kvm_s390_sie_block *sie_block = self->sie_block; 323 + int ilen = insn_length(sie_block->ipa >> 8); 324 + struct kvm_run *run = self->run; 325 + 326 + switch (run->s390_sieic.ipa) { 327 + case 0xB229: /* ISKE */ 328 + case 0xB22b: /* SSKE */ 329 + case 0xB22a: /* RRBE */ 330 + uc_skey_enable(self); 331 + 332 + /* rewind to reexecute intercepted instruction */ 333 + run->psw_addr = run->psw_addr - ilen; 334 + pr_info("rewind guest addr to 0x%.16llx\n", run->psw_addr); 335 + return true; 336 + default: 337 + return false; 338 + } 339 + } 340 + 341 + /* 342 + * Handle the SIEIC exit 343 + * * fail on codes not expected in the test cases 344 + * Returns if interception is handled / execution can be continued 330 345 */ 331 346 static bool uc_handle_sieic(FIXTURE_DATA(uc_kvm) * self) 332 347 { ··· 384 315 case ICPT_INST: 385 316 /* end execution in caller on intercepted instruction */ 386 317 pr_info("sie instruction interception\n"); 387 - return false; 318 + return uc_handle_insn_ic(self); 319 + case ICPT_KSS: 320 + uc_skey_enable(self); 321 + return true; 388 322 case ICPT_OPEREXC: 389 323 /* operation exception */ 390 324 TEST_FAIL("sie exception on %.4x%.8x", sie_block->ipa, sie_block->ipb); ··· 542 470 ASSERT_EQ(0, ioctl(self->vcpu_fd, KVM_GET_REGS, &regs)); 543 471 ASSERT_EQ(1, regs.gprs[0]); 544 472 ASSERT_EQ(1, sync_regs->gprs[0]); 473 + } 474 + 475 + TEST_F(uc_kvm, uc_skey) 476 + { 477 + struct kvm_s390_sie_block *sie_block = self->sie_block; 478 + struct kvm_sync_regs *sync_regs = &self->run->s.regs; 479 + u64 test_vaddr = VM_MEM_SIZE - (SZ_1M / 2); 480 + struct kvm_run *run = self->run; 481 + const u8 skeyvalue = 0x34; 482 + 483 + /* copy test_skey_asm to code_hva / code_gpa */ 484 + TH_LOG("copy code %p to vm mapped memory %p / %p", 485 + &test_skey_asm, (void *)self->code_hva, (void *)self->code_gpa); 486 + memcpy((void *)self->code_hva, &test_skey_asm, PAGE_SIZE); 487 + 488 + /* set register content for test_skey_asm to access not mapped memory */ 489 + sync_regs->gprs[1] = skeyvalue; 490 + sync_regs->gprs[5] = self->base_gpa; 491 + sync_regs->gprs[6] = test_vaddr; 492 + run->kvm_dirty_regs |= KVM_SYNC_GPRS; 493 + 494 + /* DAT disabled + 64 bit mode */ 495 + run->psw_mask = 0x0000000180000000ULL; 496 + run->psw_addr = self->code_gpa; 497 + 498 + ASSERT_EQ(0, uc_run_once(self)); 499 + ASSERT_EQ(true, uc_handle_exit(self)); 500 + ASSERT_EQ(1, sync_regs->gprs[0]); 501 + 502 + /* ISKE */ 503 + ASSERT_EQ(0, uc_run_once(self)); 504 + 505 + /* 506 + * Bail out and skip the test after uc_skey_enable was executed but iske 507 + * is still intercepted. Instructions are not handled by the kernel. 508 + * Thus there is no need to test this here. 509 + */ 510 + TEST_ASSERT_EQ(0, sie_block->cpuflags & CPUSTAT_KSS); 511 + TEST_ASSERT_EQ(0, sie_block->ictl & (ICTL_ISKE | ICTL_SSKE | ICTL_RRBE)); 512 + TEST_ASSERT_EQ(KVM_EXIT_S390_SIEIC, self->run->exit_reason); 513 + TEST_ASSERT_EQ(ICPT_INST, sie_block->icptcode); 514 + TEST_REQUIRE(sie_block->ipa != 0xb229); 515 + 516 + /* ISKE contd. */ 517 + ASSERT_EQ(false, uc_handle_exit(self)); 518 + ASSERT_EQ(2, sync_regs->gprs[0]); 519 + /* assert initial skey (ACC = 0, R & C = 1) */ 520 + ASSERT_EQ(0x06, sync_regs->gprs[1]); 521 + uc_assert_diag44(self); 522 + 523 + /* SSKE + ISKE */ 524 + sync_regs->gprs[1] = skeyvalue; 525 + run->kvm_dirty_regs |= KVM_SYNC_GPRS; 526 + ASSERT_EQ(0, uc_run_once(self)); 527 + ASSERT_EQ(false, uc_handle_exit(self)); 528 + ASSERT_EQ(3, sync_regs->gprs[0]); 529 + ASSERT_EQ(skeyvalue, sync_regs->gprs[1]); 530 + uc_assert_diag44(self); 531 + 532 + /* RRBE + ISKE */ 533 + sync_regs->gprs[1] = skeyvalue; 534 + run->kvm_dirty_regs |= KVM_SYNC_GPRS; 535 + ASSERT_EQ(0, uc_run_once(self)); 536 + ASSERT_EQ(false, uc_handle_exit(self)); 537 + ASSERT_EQ(4, sync_regs->gprs[0]); 538 + /* assert R reset but rest of skey unchanged */ 539 + ASSERT_EQ(skeyvalue & 0xfa, sync_regs->gprs[1]); 540 + ASSERT_EQ(0, sync_regs->gprs[1] & 0x04); 541 + uc_assert_diag44(self); 545 542 } 546 543 547 544 TEST_HARNESS_MAIN