@@ -79,6 +79,33 @@ asm("test_mem_asm:\n"
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 ;
@@ -298,8 +325,50 @@ static void uc_handle_exit_ucontrol(FIXTURE_DATA(uc_kvm) *self)
298
325
}
299
326
}
300
327
301
- /* verify SIEIC exit
328
+ /*
329
+ * Handle the SIEIC exit
302
330
* * 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
303
372
*/
304
373
static bool uc_handle_sieic (FIXTURE_DATA (uc_kvm ) * self )
305
374
{
@@ -315,7 +384,10 @@ static bool uc_handle_sieic(FIXTURE_DATA(uc_kvm) * self)
315
384
case ICPT_INST :
316
385
/* end execution in caller on intercepted instruction */
317
386
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;
319
391
case ICPT_OPEREXC :
320
392
/* operation exception */
321
393
TEST_FAIL ("sie exception on %.4x%.8x" , sie_block -> ipa , sie_block -> ipb );
@@ -472,4 +544,73 @@ TEST_F(uc_kvm, uc_gprs)
472
544
ASSERT_EQ (1 , sync_regs -> gprs [0 ]);
473
545
}
474
546
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
+
475
616
TEST_HARNESS_MAIN
0 commit comments