| @@ -21,11 +21,26 @@ · 20 unmodified lines |
| 21 | #include "gaccess.h" | 21 | #include "gaccess.h" |
| 22 | #include "trace-s390.h" | 22 | #include "trace-s390.h" |
| 23 | | 23 | |
| | 24 | #define IOINT_SCHID_MASK 0x0000ffff |
| | 25 | #define IOINT_SSID_MASK 0x00030000 |
| | 26 | #define IOINT_CSSID_MASK 0x03fc0000 |
| | 27 | #define IOINT_AI_MASK 0x04000000 |
| | 28 | |
| | 29 | static int is_ioint(u64 type) |
| | 30 | { |
| | 31 | return ((type & 0xfffe0000u) != 0xfffe0000u); |
| | 32 | } |
| | 33 | |
| 24 | static int psw_extint_disabled(struct kvm_vcpu *vcpu) | 34 | static int psw_extint_disabled(struct kvm_vcpu *vcpu) |
| 25 | { | 35 | { |
| 26 | return !(vcpu->arch.sie_block->gpsw.mask & PSW_MASK_EXT); | 36 | return !(vcpu->arch.sie_block->gpsw.mask & PSW_MASK_EXT); |
| 27 | } | 37 | } |
| 28 | | 38 | |
| | 39 | static int psw_ioint_disabled(struct kvm_vcpu *vcpu) |
| | 40 | { |
| | 41 | return !(vcpu->arch.sie_block->gpsw.mask & PSW_MASK_IO); |
| | 42 | } |
| | 43 | |
| 29 | static int psw_interrupts_disabled(struct kvm_vcpu *vcpu) | 44 | static int psw_interrupts_disabled(struct kvm_vcpu *vcpu) |
| 30 | { | 45 | { |
| 31 | if ((vcpu->arch.sie_block->gpsw.mask & PSW_MASK_PER) || | 46 | if ((vcpu->arch.sie_block->gpsw.mask & PSW_MASK_PER) || |
| @@ -67,7 +82,15 @@ static int __interrupt_is_deliverable(struct kvm_vcpu *vcpu, · 35 unmodified lines |
| 67 | case KVM_S390_SIGP_SET_PREFIX: | 82 | case KVM_S390_SIGP_SET_PREFIX: |
| 68 | case KVM_S390_RESTART: | 83 | case KVM_S390_RESTART: |
| 69 | return 1; | 84 | return 1; |
| | 85 | case KVM_S390_INT_IO_MIN...KVM_S390_INT_IO_MAX: |
| | 86 | if (psw_ioint_disabled(vcpu)) |
| | 87 | return 0; |
| | 88 | if (vcpu->arch.sie_block->gcr[6] & inti->io.io_int_word) |
| | 89 | return 1; |
| | 90 | return 0; |
| 70 | default: | 91 | default: |
| | 92 | printk(KERN_WARNING "illegal interrupt type %llx\n", |
| | 93 | inti->type); |
| 71 | BUG(); | 94 | BUG(); |
| 72 | } | 95 | } |
| 73 | return 0; | 96 | return 0; |
| @@ -116,6 +139,12 @@ static void __set_intercept_indicator(struct kvm_vcpu *vcpu, · 42 unmodified lines |
| 116 | case KVM_S390_SIGP_STOP: | 139 | case KVM_S390_SIGP_STOP: |
| 117 | __set_cpuflag(vcpu, CPUSTAT_STOP_INT); | 140 | __set_cpuflag(vcpu, CPUSTAT_STOP_INT); |
| 118 | break; | 141 | break; |
| | 142 | case KVM_S390_INT_IO_MIN...KVM_S390_INT_IO_MAX: |
| | 143 | if (psw_ioint_disabled(vcpu)) |
| | 144 | __set_cpuflag(vcpu, CPUSTAT_IO_INT); |
| | 145 | else |
| | 146 | vcpu->arch.sie_block->lctl |= LCTL_CR6; |
| | 147 | break; |
| 119 | default: | 148 | default: |
| 120 | BUG(); | 149 | BUG(); |
| 121 | } | 150 | } |
| @@ -297,6 +326,47 @@ static void __do_deliver_interrupt(struct kvm_vcpu *vcpu, · 175 unmodified lines |
| 297 | exception = 1; | 326 | exception = 1; |
| 298 | break; | 327 | break; |
| 299 | | 328 | |
| | 329 | case KVM_S390_INT_IO_MIN...KVM_S390_INT_IO_MAX: |
| | 330 | { |
| | 331 | __u32 param0 = ((__u32)inti->io.subchannel_id << 16) | |
| | 332 | inti->io.subchannel_nr; |
| | 333 | __u64 param1 = ((__u64)inti->io.io_int_parm << 32) | |
| | 334 | inti->io.io_int_word; |
| | 335 | VCPU_EVENT(vcpu, 4, "interrupt: I/O %llx", inti->type); |
| | 336 | vcpu->stat.deliver_io_int++; |
| | 337 | trace_kvm_s390_deliver_interrupt(vcpu->vcpu_id, inti->type, |
| | 338 | param0, param1); |
| | 339 | rc = put_guest_u16(vcpu, __LC_SUBCHANNEL_ID, |
| | 340 | inti->io.subchannel_id); |
| | 341 | if (rc == -EFAULT) |
| | 342 | exception = 1; |
| | 343 | |
| | 344 | rc = put_guest_u16(vcpu, __LC_SUBCHANNEL_NR, |
| | 345 | inti->io.subchannel_nr); |
| | 346 | if (rc == -EFAULT) |
| | 347 | exception = 1; |
| | 348 | |
| | 349 | rc = put_guest_u32(vcpu, __LC_IO_INT_PARM, |
| | 350 | inti->io.io_int_parm); |
| | 351 | if (rc == -EFAULT) |
| | 352 | exception = 1; |
| | 353 | |
| | 354 | rc = put_guest_u32(vcpu, __LC_IO_INT_WORD, |
| | 355 | inti->io.io_int_word); |
| | 356 | if (rc == -EFAULT) |
| | 357 | exception = 1; |
| | 358 | |
| | 359 | rc = copy_to_guest(vcpu, __LC_IO_OLD_PSW, |
| | 360 | &vcpu->arch.sie_block->gpsw, sizeof(psw_t)); |
| | 361 | if (rc == -EFAULT) |
| | 362 | exception = 1; |
| | 363 | |
| | 364 | rc = copy_from_guest(vcpu, &vcpu->arch.sie_block->gpsw, |
| | 365 | __LC_IO_NEW_PSW, sizeof(psw_t)); |
| | 366 | if (rc == -EFAULT) |
| | 367 | exception = 1; |
| | 368 | break; |
| | 369 | } |
| 300 | default: | 370 | default: |
| 301 | BUG(); | 371 | BUG(); |
| 302 | } | 372 | } |
| @@ -545,7 +615,7 @@ int kvm_s390_inject_vm(struct kvm *kvm, · 242 unmodified lines |
| 545 | { | 615 | { |
| 546 | struct kvm_s390_local_interrupt *li; | 616 | struct kvm_s390_local_interrupt *li; |
| 547 | struct kvm_s390_float_interrupt *fi; | 617 | struct kvm_s390_float_interrupt *fi; |
| 548 | struct kvm_s390_interrupt_info *inti; | 618 | struct kvm_s390_interrupt_info *inti, *iter; |
| 549 | int sigcpu; | 619 | int sigcpu; |
| 550 | | 620 | |
| 551 | inti = kzalloc(sizeof(*inti), GFP_KERNEL); | 621 | inti = kzalloc(sizeof(*inti), GFP_KERNEL); |
| @@ -569,6 +639,22 @@ int kvm_s390_inject_vm(struct kvm *kvm, · 17 unmodified lines |
| 569 | case KVM_S390_SIGP_STOP: | 639 | case KVM_S390_SIGP_STOP: |
| 570 | case KVM_S390_INT_EXTERNAL_CALL: | 640 | case KVM_S390_INT_EXTERNAL_CALL: |
| 571 | case KVM_S390_INT_EMERGENCY: | 641 | case KVM_S390_INT_EMERGENCY: |
| | 642 | kfree(inti); |
| | 643 | return -EINVAL; |
| | 644 | case KVM_S390_INT_IO_MIN...KVM_S390_INT_IO_MAX: |
| | 645 | if (s390int->type & IOINT_AI_MASK) |
| | 646 | VM_EVENT(kvm, 5, "%s", "inject: I/O (AI)"); |
| | 647 | else |
| | 648 | VM_EVENT(kvm, 5, "inject: I/O css %x ss %x schid %04x", |
| | 649 | s390int->type & IOINT_CSSID_MASK, |
| | 650 | s390int->type & IOINT_SSID_MASK, |
| | 651 | s390int->type & IOINT_SCHID_MASK); |
| | 652 | inti->type = s390int->type; |
| | 653 | inti->io.subchannel_id = s390int->parm >> 16; |
| | 654 | inti->io.subchannel_nr = s390int->parm & 0x0000ffffu; |
| | 655 | inti->io.io_int_parm = s390int->parm64 >> 32; |
| | 656 | inti->io.io_int_word = s390int->parm64 & 0x00000000ffffffffull; |
| | 657 | break; |
| 572 | default: | 658 | default: |
| 573 | kfree(inti); | 659 | kfree(inti); |
| 574 | return -EINVAL; | 660 | return -EINVAL; |
| @@ -579,7 +665,19 @@ int kvm_s390_inject_vm(struct kvm *kvm, · 4 unmodified lines |
| 579 | mutex_lock(&kvm->lock); | 665 | mutex_lock(&kvm->lock); |
| 580 | fi = &kvm->arch.float_int; | 666 | fi = &kvm->arch.float_int; |
| 581 | spin_lock(&fi->lock); | 667 | spin_lock(&fi->lock); |
| 582 | list_add_tail(&inti->list, &fi->list); | 668 | if (!is_ioint(inti->type)) |
| | 669 | list_add_tail(&inti->list, &fi->list); |
| | 670 | else { |
| | 671 | /* Keep I/O interrupts sorted in isc order. */ |
| | 672 | list_for_each_entry(iter, &fi->list, list) { |
| | 673 | if (!is_ioint(iter->type)) |
| | 674 | continue; |
| | 675 | if (iter->io.io_int_word <= inti->io.io_int_word) |
| | 676 | continue; |
| | 677 | break; |
| | 678 | } |
| | 679 | list_add_tail(&inti->list, &iter->list); |
| | 680 | } |
| 583 | atomic_set(&fi->active, 1); | 681 | atomic_set(&fi->active, 1); |
| 584 | sigcpu = find_first_bit(fi->idle_mask, KVM_MAX_VCPUS); | 682 | sigcpu = find_first_bit(fi->idle_mask, KVM_MAX_VCPUS); |
| 585 | if (sigcpu == KVM_MAX_VCPUS) { | 683 | if (sigcpu == KVM_MAX_VCPUS) { |
| @@ -653,6 +751,7 @@ int kvm_s390_inject_vcpu(struct kvm_vcpu *vcpu, · 67 unmodified lines |
| 653 | break; | 751 | break; |
| 654 | case KVM_S390_INT_VIRTIO: | 752 | case KVM_S390_INT_VIRTIO: |
| 655 | case KVM_S390_INT_SERVICE: | 753 | case KVM_S390_INT_SERVICE: |
| | 754 | case KVM_S390_INT_IO_MIN...KVM_S390_INT_IO_MAX: |
| 656 | default: | 755 | default: |
| 657 | kfree(inti); | 756 | kfree(inti); |
| 658 | return -EINVAL; | 757 | return -EINVAL; |