| .. | .. |
|---|
| 2 | 2 | /* |
|---|
| 3 | 3 | * in-kernel handling for sie intercepts |
|---|
| 4 | 4 | * |
|---|
| 5 | | - * Copyright IBM Corp. 2008, 2014 |
|---|
| 5 | + * Copyright IBM Corp. 2008, 2020 |
|---|
| 6 | 6 | * |
|---|
| 7 | 7 | * Author(s): Carsten Otte <cotte@de.ibm.com> |
|---|
| 8 | 8 | * Christian Borntraeger <borntraeger@de.ibm.com> |
|---|
| .. | .. |
|---|
| 12 | 12 | #include <linux/errno.h> |
|---|
| 13 | 13 | #include <linux/pagemap.h> |
|---|
| 14 | 14 | |
|---|
| 15 | | -#include <asm/kvm_host.h> |
|---|
| 16 | 15 | #include <asm/asm-offsets.h> |
|---|
| 17 | 16 | #include <asm/irq.h> |
|---|
| 18 | 17 | #include <asm/sysinfo.h> |
|---|
| 18 | +#include <asm/uv.h> |
|---|
| 19 | 19 | |
|---|
| 20 | 20 | #include "kvm-s390.h" |
|---|
| 21 | 21 | #include "gaccess.h" |
|---|
| .. | .. |
|---|
| 79 | 79 | return rc; |
|---|
| 80 | 80 | } |
|---|
| 81 | 81 | |
|---|
| 82 | + /* |
|---|
| 83 | + * no need to check the return value of vcpu_stop as it can only have |
|---|
| 84 | + * an error for protvirt, but protvirt means user cpu state |
|---|
| 85 | + */ |
|---|
| 82 | 86 | if (!kvm_s390_user_cpu_state_ctrl(vcpu->kvm)) |
|---|
| 83 | 87 | kvm_s390_vcpu_stop(vcpu); |
|---|
| 84 | 88 | return -EOPNOTSUPP; |
|---|
| .. | .. |
|---|
| 230 | 234 | int rc; |
|---|
| 231 | 235 | |
|---|
| 232 | 236 | vcpu->stat.exit_program_interruption++; |
|---|
| 237 | + |
|---|
| 238 | + /* |
|---|
| 239 | + * Intercept 8 indicates a loop of specification exceptions |
|---|
| 240 | + * for protected guests. |
|---|
| 241 | + */ |
|---|
| 242 | + if (kvm_s390_pv_cpu_is_protected(vcpu)) |
|---|
| 243 | + return -EOPNOTSUPP; |
|---|
| 233 | 244 | |
|---|
| 234 | 245 | if (guestdbg_enabled(vcpu) && per_event(vcpu)) { |
|---|
| 235 | 246 | rc = kvm_s390_handle_per_event(vcpu); |
|---|
| .. | .. |
|---|
| 384 | 395 | goto out; |
|---|
| 385 | 396 | } |
|---|
| 386 | 397 | |
|---|
| 387 | | - if (addr & ~PAGE_MASK) |
|---|
| 398 | + if (!kvm_s390_pv_cpu_is_protected(vcpu) && (addr & ~PAGE_MASK)) |
|---|
| 388 | 399 | return kvm_s390_inject_program_int(vcpu, PGM_SPECIFICATION); |
|---|
| 389 | 400 | |
|---|
| 390 | 401 | sctns = (void *)get_zeroed_page(GFP_KERNEL); |
|---|
| .. | .. |
|---|
| 395 | 406 | |
|---|
| 396 | 407 | out: |
|---|
| 397 | 408 | if (!cc) { |
|---|
| 398 | | - r = write_guest(vcpu, addr, reg2, sctns, PAGE_SIZE); |
|---|
| 399 | | - if (r) { |
|---|
| 400 | | - free_page((unsigned long)sctns); |
|---|
| 401 | | - return kvm_s390_inject_prog_cond(vcpu, r); |
|---|
| 409 | + if (kvm_s390_pv_cpu_is_protected(vcpu)) { |
|---|
| 410 | + memcpy((void *)(sida_origin(vcpu->arch.sie_block)), |
|---|
| 411 | + sctns, PAGE_SIZE); |
|---|
| 412 | + } else { |
|---|
| 413 | + r = write_guest(vcpu, addr, reg2, sctns, PAGE_SIZE); |
|---|
| 414 | + if (r) { |
|---|
| 415 | + free_page((unsigned long)sctns); |
|---|
| 416 | + return kvm_s390_inject_prog_cond(vcpu, r); |
|---|
| 417 | + } |
|---|
| 402 | 418 | } |
|---|
| 403 | 419 | } |
|---|
| 404 | 420 | |
|---|
| .. | .. |
|---|
| 444 | 460 | return kvm_s390_inject_program_int(vcpu, PGM_OPERATION); |
|---|
| 445 | 461 | } |
|---|
| 446 | 462 | |
|---|
| 463 | +static int handle_pv_spx(struct kvm_vcpu *vcpu) |
|---|
| 464 | +{ |
|---|
| 465 | + u32 pref = *(u32 *)vcpu->arch.sie_block->sidad; |
|---|
| 466 | + |
|---|
| 467 | + kvm_s390_set_prefix(vcpu, pref); |
|---|
| 468 | + trace_kvm_s390_handle_prefix(vcpu, 1, pref); |
|---|
| 469 | + return 0; |
|---|
| 470 | +} |
|---|
| 471 | + |
|---|
| 472 | +static int handle_pv_sclp(struct kvm_vcpu *vcpu) |
|---|
| 473 | +{ |
|---|
| 474 | + struct kvm_s390_float_interrupt *fi = &vcpu->kvm->arch.float_int; |
|---|
| 475 | + |
|---|
| 476 | + spin_lock(&fi->lock); |
|---|
| 477 | + /* |
|---|
| 478 | + * 2 cases: |
|---|
| 479 | + * a: an sccb answering interrupt was already pending or in flight. |
|---|
| 480 | + * As the sccb value is not known we can simply set some value to |
|---|
| 481 | + * trigger delivery of a saved SCCB. UV will then use its saved |
|---|
| 482 | + * copy of the SCCB value. |
|---|
| 483 | + * b: an error SCCB interrupt needs to be injected so we also inject |
|---|
| 484 | + * a fake SCCB address. Firmware will use the proper one. |
|---|
| 485 | + * This makes sure, that both errors and real sccb returns will only |
|---|
| 486 | + * be delivered after a notification intercept (instruction has |
|---|
| 487 | + * finished) but not after others. |
|---|
| 488 | + */ |
|---|
| 489 | + fi->srv_signal.ext_params |= 0x43000; |
|---|
| 490 | + set_bit(IRQ_PEND_EXT_SERVICE, &fi->pending_irqs); |
|---|
| 491 | + clear_bit(IRQ_PEND_EXT_SERVICE, &fi->masked_irqs); |
|---|
| 492 | + spin_unlock(&fi->lock); |
|---|
| 493 | + return 0; |
|---|
| 494 | +} |
|---|
| 495 | + |
|---|
| 496 | +static int handle_pv_uvc(struct kvm_vcpu *vcpu) |
|---|
| 497 | +{ |
|---|
| 498 | + struct uv_cb_share *guest_uvcb = (void *)vcpu->arch.sie_block->sidad; |
|---|
| 499 | + struct uv_cb_cts uvcb = { |
|---|
| 500 | + .header.cmd = UVC_CMD_UNPIN_PAGE_SHARED, |
|---|
| 501 | + .header.len = sizeof(uvcb), |
|---|
| 502 | + .guest_handle = kvm_s390_pv_get_handle(vcpu->kvm), |
|---|
| 503 | + .gaddr = guest_uvcb->paddr, |
|---|
| 504 | + }; |
|---|
| 505 | + int rc; |
|---|
| 506 | + |
|---|
| 507 | + if (guest_uvcb->header.cmd != UVC_CMD_REMOVE_SHARED_ACCESS) { |
|---|
| 508 | + WARN_ONCE(1, "Unexpected notification intercept for UVC 0x%x\n", |
|---|
| 509 | + guest_uvcb->header.cmd); |
|---|
| 510 | + return 0; |
|---|
| 511 | + } |
|---|
| 512 | + rc = gmap_make_secure(vcpu->arch.gmap, uvcb.gaddr, &uvcb); |
|---|
| 513 | + /* |
|---|
| 514 | + * If the unpin did not succeed, the guest will exit again for the UVC |
|---|
| 515 | + * and we will retry the unpin. |
|---|
| 516 | + */ |
|---|
| 517 | + if (rc == -EINVAL) |
|---|
| 518 | + return 0; |
|---|
| 519 | + return rc; |
|---|
| 520 | +} |
|---|
| 521 | + |
|---|
| 522 | +static int handle_pv_notification(struct kvm_vcpu *vcpu) |
|---|
| 523 | +{ |
|---|
| 524 | + int ret; |
|---|
| 525 | + |
|---|
| 526 | + if (vcpu->arch.sie_block->ipa == 0xb210) |
|---|
| 527 | + return handle_pv_spx(vcpu); |
|---|
| 528 | + if (vcpu->arch.sie_block->ipa == 0xb220) |
|---|
| 529 | + return handle_pv_sclp(vcpu); |
|---|
| 530 | + if (vcpu->arch.sie_block->ipa == 0xb9a4) |
|---|
| 531 | + return handle_pv_uvc(vcpu); |
|---|
| 532 | + if (vcpu->arch.sie_block->ipa >> 8 == 0xae) { |
|---|
| 533 | + /* |
|---|
| 534 | + * Besides external call, other SIGP orders also cause a |
|---|
| 535 | + * 108 (pv notify) intercept. In contrast to external call, |
|---|
| 536 | + * these orders need to be emulated and hence the appropriate |
|---|
| 537 | + * place to handle them is in handle_instruction(). |
|---|
| 538 | + * So first try kvm_s390_handle_sigp_pei() and if that isn't |
|---|
| 539 | + * successful, go on with handle_instruction(). |
|---|
| 540 | + */ |
|---|
| 541 | + ret = kvm_s390_handle_sigp_pei(vcpu); |
|---|
| 542 | + if (!ret) |
|---|
| 543 | + return ret; |
|---|
| 544 | + } |
|---|
| 545 | + |
|---|
| 546 | + return handle_instruction(vcpu); |
|---|
| 547 | +} |
|---|
| 548 | + |
|---|
| 447 | 549 | int kvm_handle_sie_intercept(struct kvm_vcpu *vcpu) |
|---|
| 448 | 550 | { |
|---|
| 449 | 551 | int rc, per_rc = 0; |
|---|
| .. | .. |
|---|
| 480 | 582 | case ICPT_KSS: |
|---|
| 481 | 583 | rc = kvm_s390_skey_check_enable(vcpu); |
|---|
| 482 | 584 | break; |
|---|
| 585 | + case ICPT_MCHKREQ: |
|---|
| 586 | + case ICPT_INT_ENABLE: |
|---|
| 587 | + /* |
|---|
| 588 | + * PSW bit 13 or a CR (0, 6, 14) changed and we might |
|---|
| 589 | + * now be able to deliver interrupts. The pre-run code |
|---|
| 590 | + * will take care of this. |
|---|
| 591 | + */ |
|---|
| 592 | + rc = 0; |
|---|
| 593 | + break; |
|---|
| 594 | + case ICPT_PV_INSTR: |
|---|
| 595 | + rc = handle_instruction(vcpu); |
|---|
| 596 | + break; |
|---|
| 597 | + case ICPT_PV_NOTIFY: |
|---|
| 598 | + rc = handle_pv_notification(vcpu); |
|---|
| 599 | + break; |
|---|
| 600 | + case ICPT_PV_PREF: |
|---|
| 601 | + rc = 0; |
|---|
| 602 | + gmap_convert_to_secure(vcpu->arch.gmap, |
|---|
| 603 | + kvm_s390_get_prefix(vcpu)); |
|---|
| 604 | + gmap_convert_to_secure(vcpu->arch.gmap, |
|---|
| 605 | + kvm_s390_get_prefix(vcpu) + PAGE_SIZE); |
|---|
| 606 | + break; |
|---|
| 483 | 607 | default: |
|---|
| 484 | 608 | return -EOPNOTSUPP; |
|---|
| 485 | 609 | } |
|---|