@@ -79,6 +79,33 @@ asm("test_mem_asm:\n"
7979 " j 0b\n"
8080);
8181
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+
82109FIXTURE (uc_kvm )
83110{
84111 struct kvm_s390_sie_block * sie_block ;
@@ -298,8 +325,50 @@ static void uc_handle_exit_ucontrol(FIXTURE_DATA(uc_kvm) *self)
298325 }
299326}
300327
301- /* verify SIEIC exit
328+ /*
329+ * Handle the SIEIC exit
302330 * * fail on codes not expected in the test cases
331+ * Returns if interception is handled / execution can be continued
332+ */
333+ static void uc_skey_enable (FIXTURE_DATA (uc_kvm ) * self )
334+ {
335+ struct kvm_s390_sie_block * sie_block = self -> sie_block ;
336+
337+ /* disable KSS */
338+ sie_block -> cpuflags &= ~CPUSTAT_KSS ;
339+ /* disable skey inst interception */
340+ sie_block -> ictl &= ~(ICTL_ISKE | ICTL_SSKE | ICTL_RRBE );
341+ }
342+
343+ /*
344+ * Handle the instruction intercept
345+ * Returns if interception is handled / execution can be continued
346+ */
347+ static bool uc_handle_insn_ic (FIXTURE_DATA (uc_kvm ) * self )
348+ {
349+ struct kvm_s390_sie_block * sie_block = self -> sie_block ;
350+ int ilen = insn_length (sie_block -> ipa >> 8 );
351+ struct kvm_run * run = self -> run ;
352+
353+ switch (run -> s390_sieic .ipa ) {
354+ case 0xB229 : /* ISKE */
355+ case 0xB22b : /* SSKE */
356+ case 0xB22a : /* RRBE */
357+ uc_skey_enable (self );
358+
359+ /* rewind to reexecute intercepted instruction */
360+ run -> psw_addr = run -> psw_addr - ilen ;
361+ pr_info ("rewind guest addr to 0x%.16llx\n" , run -> psw_addr );
362+ return true;
363+ default :
364+ return false;
365+ }
366+ }
367+
368+ /*
369+ * Handle the SIEIC exit
370+ * * fail on codes not expected in the test cases
371+ * Returns if interception is handled / execution can be continued
303372 */
304373static bool uc_handle_sieic (FIXTURE_DATA (uc_kvm ) * self )
305374{
@@ -315,7 +384,10 @@ static bool uc_handle_sieic(FIXTURE_DATA(uc_kvm) * self)
315384 case ICPT_INST :
316385 /* end execution in caller on intercepted instruction */
317386 pr_info ("sie instruction interception\n" );
318- return false;
387+ return uc_handle_insn_ic (self );
388+ case ICPT_KSS :
389+ uc_skey_enable (self );
390+ return true;
319391 case ICPT_OPEREXC :
320392 /* operation exception */
321393 TEST_FAIL ("sie exception on %.4x%.8x" , sie_block -> ipa , sie_block -> ipb );
@@ -472,4 +544,73 @@ TEST_F(uc_kvm, uc_gprs)
472544 ASSERT_EQ (1 , sync_regs -> gprs [0 ]);
473545}
474546
547+ TEST_F (uc_kvm , uc_skey )
548+ {
549+ struct kvm_s390_sie_block * sie_block = self -> sie_block ;
550+ struct kvm_sync_regs * sync_regs = & self -> run -> s .regs ;
551+ u64 test_vaddr = VM_MEM_SIZE - (SZ_1M / 2 );
552+ struct kvm_run * run = self -> run ;
553+ const u8 skeyvalue = 0x34 ;
554+
555+ /* copy test_skey_asm to code_hva / code_gpa */
556+ TH_LOG ("copy code %p to vm mapped memory %p / %p" ,
557+ & test_skey_asm , (void * )self -> code_hva , (void * )self -> code_gpa );
558+ memcpy ((void * )self -> code_hva , & test_skey_asm , PAGE_SIZE );
559+
560+ /* set register content for test_skey_asm to access not mapped memory */
561+ sync_regs -> gprs [1 ] = skeyvalue ;
562+ sync_regs -> gprs [5 ] = self -> base_gpa ;
563+ sync_regs -> gprs [6 ] = test_vaddr ;
564+ run -> kvm_dirty_regs |= KVM_SYNC_GPRS ;
565+
566+ /* DAT disabled + 64 bit mode */
567+ run -> psw_mask = 0x0000000180000000ULL ;
568+ run -> psw_addr = self -> code_gpa ;
569+
570+ ASSERT_EQ (0 , uc_run_once (self ));
571+ ASSERT_EQ (true, uc_handle_exit (self ));
572+ ASSERT_EQ (1 , sync_regs -> gprs [0 ]);
573+
574+ /* ISKE */
575+ ASSERT_EQ (0 , uc_run_once (self ));
576+
577+ /*
578+ * Bail out and skip the test after uc_skey_enable was executed but iske
579+ * is still intercepted. Instructions are not handled by the kernel.
580+ * Thus there is no need to test this here.
581+ */
582+ TEST_ASSERT_EQ (0 , sie_block -> cpuflags & CPUSTAT_KSS );
583+ TEST_ASSERT_EQ (0 , sie_block -> ictl & (ICTL_ISKE | ICTL_SSKE | ICTL_RRBE ));
584+ TEST_ASSERT_EQ (KVM_EXIT_S390_SIEIC , self -> run -> exit_reason );
585+ TEST_ASSERT_EQ (ICPT_INST , sie_block -> icptcode );
586+ TEST_REQUIRE (sie_block -> ipa != 0xb229 );
587+
588+ /* ISKE contd. */
589+ ASSERT_EQ (false, uc_handle_exit (self ));
590+ ASSERT_EQ (2 , sync_regs -> gprs [0 ]);
591+ /* assert initial skey (ACC = 0, R & C = 1) */
592+ ASSERT_EQ (0x06 , sync_regs -> gprs [1 ]);
593+ uc_assert_diag44 (self );
594+
595+ /* SSKE + ISKE */
596+ sync_regs -> gprs [1 ] = skeyvalue ;
597+ run -> kvm_dirty_regs |= KVM_SYNC_GPRS ;
598+ ASSERT_EQ (0 , uc_run_once (self ));
599+ ASSERT_EQ (false, uc_handle_exit (self ));
600+ ASSERT_EQ (3 , sync_regs -> gprs [0 ]);
601+ ASSERT_EQ (skeyvalue , sync_regs -> gprs [1 ]);
602+ uc_assert_diag44 (self );
603+
604+ /* RRBE + ISKE */
605+ sync_regs -> gprs [1 ] = skeyvalue ;
606+ run -> kvm_dirty_regs |= KVM_SYNC_GPRS ;
607+ ASSERT_EQ (0 , uc_run_once (self ));
608+ ASSERT_EQ (false, uc_handle_exit (self ));
609+ ASSERT_EQ (4 , sync_regs -> gprs [0 ]);
610+ /* assert R reset but rest of skey unchanged */
611+ ASSERT_EQ (skeyvalue & 0xfa , sync_regs -> gprs [1 ]);
612+ ASSERT_EQ (0 , sync_regs -> gprs [1 ] & 0x04 );
613+ uc_assert_diag44 (self );
614+ }
615+
475616TEST_HARNESS_MAIN
0 commit comments