|
7 | 7 | * Authors:
|
8 | 8 | * Christoph Schlameuss <[email protected]>
|
9 | 9 | */
|
| 10 | +#include "debug_print.h" |
10 | 11 | #include "kselftest_harness.h"
|
11 | 12 | #include "kvm_util.h"
|
12 | 13 | #include "processor.h"
|
@@ -40,6 +41,23 @@ void require_ucontrol_admin(void)
|
40 | 41 | TEST_REQUIRE(kvm_has_cap(KVM_CAP_S390_UCONTROL));
|
41 | 42 | }
|
42 | 43 |
|
| 44 | +/* Test program setting some registers and looping */ |
| 45 | +extern char test_gprs_asm[]; |
| 46 | +asm("test_gprs_asm:\n" |
| 47 | + "xgr %r0, %r0\n" |
| 48 | + "lgfi %r1,1\n" |
| 49 | + "lgfi %r2,2\n" |
| 50 | + "lgfi %r3,3\n" |
| 51 | + "lgfi %r4,4\n" |
| 52 | + "lgfi %r5,5\n" |
| 53 | + "lgfi %r6,6\n" |
| 54 | + "lgfi %r7,7\n" |
| 55 | + "0:\n" |
| 56 | + " diag 0,0,0x44\n" |
| 57 | + " ahi %r0,1\n" |
| 58 | + " j 0b\n" |
| 59 | +); |
| 60 | + |
43 | 61 | FIXTURE(uc_kvm)
|
44 | 62 | {
|
45 | 63 | struct kvm_s390_sie_block *sie_block;
|
@@ -204,4 +222,111 @@ TEST(uc_cap_hpage)
|
204 | 222 | close(kvm_fd);
|
205 | 223 | }
|
206 | 224 |
|
| 225 | +/* verify SIEIC exit |
| 226 | + * * fail on codes not expected in the test cases |
| 227 | + */ |
| 228 | +static bool uc_handle_sieic(FIXTURE_DATA(uc_kvm) * self) |
| 229 | +{ |
| 230 | + struct kvm_s390_sie_block *sie_block = self->sie_block; |
| 231 | + struct kvm_run *run = self->run; |
| 232 | + |
| 233 | + /* check SIE interception code */ |
| 234 | + pr_info("sieic: 0x%.2x 0x%.4x 0x%.4x\n", |
| 235 | + run->s390_sieic.icptcode, |
| 236 | + run->s390_sieic.ipa, |
| 237 | + run->s390_sieic.ipb); |
| 238 | + switch (run->s390_sieic.icptcode) { |
| 239 | + case ICPT_INST: |
| 240 | + /* end execution in caller on intercepted instruction */ |
| 241 | + pr_info("sie instruction interception\n"); |
| 242 | + return false; |
| 243 | + case ICPT_OPEREXC: |
| 244 | + /* operation exception */ |
| 245 | + TEST_FAIL("sie exception on %.4x%.8x", sie_block->ipa, sie_block->ipb); |
| 246 | + default: |
| 247 | + TEST_FAIL("UNEXPECTED SIEIC CODE %d", run->s390_sieic.icptcode); |
| 248 | + } |
| 249 | + return true; |
| 250 | +} |
| 251 | + |
| 252 | +/* verify VM state on exit */ |
| 253 | +static bool uc_handle_exit(FIXTURE_DATA(uc_kvm) * self) |
| 254 | +{ |
| 255 | + struct kvm_run *run = self->run; |
| 256 | + |
| 257 | + switch (run->exit_reason) { |
| 258 | + case KVM_EXIT_S390_SIEIC: |
| 259 | + return uc_handle_sieic(self); |
| 260 | + default: |
| 261 | + pr_info("exit_reason %2d not handled\n", run->exit_reason); |
| 262 | + } |
| 263 | + return true; |
| 264 | +} |
| 265 | + |
| 266 | +/* run the VM until interrupted */ |
| 267 | +static int uc_run_once(FIXTURE_DATA(uc_kvm) * self) |
| 268 | +{ |
| 269 | + int rc; |
| 270 | + |
| 271 | + rc = ioctl(self->vcpu_fd, KVM_RUN, NULL); |
| 272 | + print_run(self->run, self->sie_block); |
| 273 | + print_regs(self->run); |
| 274 | + pr_debug("run %d / %d %s\n", rc, errno, strerror(errno)); |
| 275 | + return rc; |
| 276 | +} |
| 277 | + |
| 278 | +static void uc_assert_diag44(FIXTURE_DATA(uc_kvm) * self) |
| 279 | +{ |
| 280 | + struct kvm_s390_sie_block *sie_block = self->sie_block; |
| 281 | + |
| 282 | + /* assert vm was interrupted by diag 0x0044 */ |
| 283 | + TEST_ASSERT_EQ(KVM_EXIT_S390_SIEIC, self->run->exit_reason); |
| 284 | + TEST_ASSERT_EQ(ICPT_INST, sie_block->icptcode); |
| 285 | + TEST_ASSERT_EQ(0x8300, sie_block->ipa); |
| 286 | + TEST_ASSERT_EQ(0x440000, sie_block->ipb); |
| 287 | +} |
| 288 | + |
| 289 | +TEST_F(uc_kvm, uc_gprs) |
| 290 | +{ |
| 291 | + struct kvm_sync_regs *sync_regs = &self->run->s.regs; |
| 292 | + struct kvm_run *run = self->run; |
| 293 | + struct kvm_regs regs = {}; |
| 294 | + |
| 295 | + /* Set registers to values that are different from the ones that we expect below */ |
| 296 | + for (int i = 0; i < 8; i++) |
| 297 | + sync_regs->gprs[i] = 8; |
| 298 | + run->kvm_dirty_regs |= KVM_SYNC_GPRS; |
| 299 | + |
| 300 | + /* copy test_gprs_asm to code_hva / code_gpa */ |
| 301 | + TH_LOG("copy code %p to vm mapped memory %p / %p", |
| 302 | + &test_gprs_asm, (void *)self->code_hva, (void *)self->code_gpa); |
| 303 | + memcpy((void *)self->code_hva, &test_gprs_asm, PAGE_SIZE); |
| 304 | + |
| 305 | + /* DAT disabled + 64 bit mode */ |
| 306 | + run->psw_mask = 0x0000000180000000ULL; |
| 307 | + run->psw_addr = self->code_gpa; |
| 308 | + |
| 309 | + /* run and expect interception of diag 44 */ |
| 310 | + ASSERT_EQ(0, uc_run_once(self)); |
| 311 | + ASSERT_EQ(false, uc_handle_exit(self)); |
| 312 | + uc_assert_diag44(self); |
| 313 | + |
| 314 | + /* Retrieve and check guest register values */ |
| 315 | + ASSERT_EQ(0, ioctl(self->vcpu_fd, KVM_GET_REGS, ®s)); |
| 316 | + for (int i = 0; i < 8; i++) { |
| 317 | + ASSERT_EQ(i, regs.gprs[i]); |
| 318 | + ASSERT_EQ(i, sync_regs->gprs[i]); |
| 319 | + } |
| 320 | + |
| 321 | + /* run and expect interception of diag 44 again */ |
| 322 | + ASSERT_EQ(0, uc_run_once(self)); |
| 323 | + ASSERT_EQ(false, uc_handle_exit(self)); |
| 324 | + uc_assert_diag44(self); |
| 325 | + |
| 326 | + /* check continued increment of register 0 value */ |
| 327 | + ASSERT_EQ(0, ioctl(self->vcpu_fd, KVM_GET_REGS, ®s)); |
| 328 | + ASSERT_EQ(1, regs.gprs[0]); |
| 329 | + ASSERT_EQ(1, sync_regs->gprs[0]); |
| 330 | +} |
| 331 | + |
207 | 332 | TEST_HARNESS_MAIN
|
0 commit comments