diff options
Diffstat (limited to 'virt')
-rw-r--r-- | virt/kvm/arm/vgic.c | 70 |
1 files changed, 66 insertions, 4 deletions
diff --git a/virt/kvm/arm/vgic.c b/virt/kvm/arm/vgic.c index d08ba28e729a..e59aaa4c64e5 100644 --- a/virt/kvm/arm/vgic.c +++ b/virt/kvm/arm/vgic.c | |||
@@ -663,18 +663,80 @@ static void vgic_unqueue_irqs(struct kvm_vcpu *vcpu) | |||
663 | } | 663 | } |
664 | } | 664 | } |
665 | 665 | ||
666 | static bool handle_mmio_sgi_clear(struct kvm_vcpu *vcpu, | 666 | /* Handle reads of GICD_CPENDSGIRn and GICD_SPENDSGIRn */ |
667 | struct kvm_exit_mmio *mmio, | 667 | static bool read_set_clear_sgi_pend_reg(struct kvm_vcpu *vcpu, |
668 | phys_addr_t offset) | 668 | struct kvm_exit_mmio *mmio, |
669 | phys_addr_t offset) | ||
669 | { | 670 | { |
671 | struct vgic_dist *dist = &vcpu->kvm->arch.vgic; | ||
672 | int sgi; | ||
673 | int min_sgi = (offset & ~0x3) * 4; | ||
674 | int max_sgi = min_sgi + 3; | ||
675 | int vcpu_id = vcpu->vcpu_id; | ||
676 | u32 reg = 0; | ||
677 | |||
678 | /* Copy source SGIs from distributor side */ | ||
679 | for (sgi = min_sgi; sgi <= max_sgi; sgi++) { | ||
680 | int shift = 8 * (sgi - min_sgi); | ||
681 | reg |= (u32)dist->irq_sgi_sources[vcpu_id][sgi] << shift; | ||
682 | } | ||
683 | |||
684 | mmio_data_write(mmio, ~0, reg); | ||
670 | return false; | 685 | return false; |
671 | } | 686 | } |
672 | 687 | ||
688 | static bool write_set_clear_sgi_pend_reg(struct kvm_vcpu *vcpu, | ||
689 | struct kvm_exit_mmio *mmio, | ||
690 | phys_addr_t offset, bool set) | ||
691 | { | ||
692 | struct vgic_dist *dist = &vcpu->kvm->arch.vgic; | ||
693 | int sgi; | ||
694 | int min_sgi = (offset & ~0x3) * 4; | ||
695 | int max_sgi = min_sgi + 3; | ||
696 | int vcpu_id = vcpu->vcpu_id; | ||
697 | u32 reg; | ||
698 | bool updated = false; | ||
699 | |||
700 | reg = mmio_data_read(mmio, ~0); | ||
701 | |||
702 | /* Clear pending SGIs on the distributor */ | ||
703 | for (sgi = min_sgi; sgi <= max_sgi; sgi++) { | ||
704 | u8 mask = reg >> (8 * (sgi - min_sgi)); | ||
705 | if (set) { | ||
706 | if ((dist->irq_sgi_sources[vcpu_id][sgi] & mask) != mask) | ||
707 | updated = true; | ||
708 | dist->irq_sgi_sources[vcpu_id][sgi] |= mask; | ||
709 | } else { | ||
710 | if (dist->irq_sgi_sources[vcpu_id][sgi] & mask) | ||
711 | updated = true; | ||
712 | dist->irq_sgi_sources[vcpu_id][sgi] &= ~mask; | ||
713 | } | ||
714 | } | ||
715 | |||
716 | if (updated) | ||
717 | vgic_update_state(vcpu->kvm); | ||
718 | |||
719 | return updated; | ||
720 | } | ||
721 | |||
673 | static bool handle_mmio_sgi_set(struct kvm_vcpu *vcpu, | 722 | static bool handle_mmio_sgi_set(struct kvm_vcpu *vcpu, |
674 | struct kvm_exit_mmio *mmio, | 723 | struct kvm_exit_mmio *mmio, |
675 | phys_addr_t offset) | 724 | phys_addr_t offset) |
676 | { | 725 | { |
677 | return false; | 726 | if (!mmio->is_write) |
727 | return read_set_clear_sgi_pend_reg(vcpu, mmio, offset); | ||
728 | else | ||
729 | return write_set_clear_sgi_pend_reg(vcpu, mmio, offset, true); | ||
730 | } | ||
731 | |||
732 | static bool handle_mmio_sgi_clear(struct kvm_vcpu *vcpu, | ||
733 | struct kvm_exit_mmio *mmio, | ||
734 | phys_addr_t offset) | ||
735 | { | ||
736 | if (!mmio->is_write) | ||
737 | return read_set_clear_sgi_pend_reg(vcpu, mmio, offset); | ||
738 | else | ||
739 | return write_set_clear_sgi_pend_reg(vcpu, mmio, offset, false); | ||
678 | } | 740 | } |
679 | 741 | ||
680 | /* | 742 | /* |