diff options
Diffstat (limited to 'virt')
| -rw-r--r-- | virt/kvm/arm/vgic/vgic-v4.c | 98 | ||||
| -rw-r--r-- | virt/kvm/arm/vgic/vgic.h | 1 | 
2 files changed, 99 insertions, 0 deletions
diff --git a/virt/kvm/arm/vgic/vgic-v4.c b/virt/kvm/arm/vgic/vgic-v4.c index c2fcde104ea2..27ac833e5ec7 100644 --- a/virt/kvm/arm/vgic/vgic-v4.c +++ b/virt/kvm/arm/vgic/vgic-v4.c @@ -97,6 +97,104 @@ static irqreturn_t vgic_v4_doorbell_handler(int irq, void *info)  	return IRQ_HANDLED;  } +static void vgic_v4_sync_sgi_config(struct its_vpe *vpe, struct vgic_irq *irq) +{ +	vpe->sgi_config[irq->intid].enabled	= irq->enabled; +	vpe->sgi_config[irq->intid].group 	= irq->group; +	vpe->sgi_config[irq->intid].priority	= irq->priority; +} + +static void vgic_v4_enable_vsgis(struct kvm_vcpu *vcpu) +{ +	struct its_vpe *vpe = &vcpu->arch.vgic_cpu.vgic_v3.its_vpe; +	int i; + +	/* +	 * With GICv4.1, every virtual SGI can be directly injected. So +	 * let's pretend that they are HW interrupts, tied to a host +	 * IRQ. The SGI code will do its magic. +	 */ +	for (i = 0; i < VGIC_NR_SGIS; i++) { +		struct vgic_irq *irq = vgic_get_irq(vcpu->kvm, vcpu, i); +		struct irq_desc *desc; +		unsigned long flags; +		int ret; + +		raw_spin_lock_irqsave(&irq->irq_lock, flags); + +		if (irq->hw) +			goto unlock; + +		irq->hw = true; +		irq->host_irq = irq_find_mapping(vpe->sgi_domain, i); + +		/* Transfer the full irq state to the vPE */ +		vgic_v4_sync_sgi_config(vpe, irq); +		desc = irq_to_desc(irq->host_irq); +		ret = irq_domain_activate_irq(irq_desc_get_irq_data(desc), +					      false); +		if (!WARN_ON(ret)) { +			/* Transfer pending state */ +			ret = irq_set_irqchip_state(irq->host_irq, +						    IRQCHIP_STATE_PENDING, +						    irq->pending_latch); +			WARN_ON(ret); +			irq->pending_latch = false; +		} +	unlock: +		raw_spin_unlock_irqrestore(&irq->irq_lock, flags); +		vgic_put_irq(vcpu->kvm, irq); +	} +} + +static void vgic_v4_disable_vsgis(struct kvm_vcpu *vcpu) +{ +	int i; + +	for (i = 0; i < VGIC_NR_SGIS; i++) { +		struct vgic_irq *irq = vgic_get_irq(vcpu->kvm, vcpu, i); +		struct irq_desc *desc; +		unsigned long flags; +		int ret; + +		raw_spin_lock_irqsave(&irq->irq_lock, flags); + +		if (!irq->hw) +			goto unlock; + +		irq->hw = false; +		ret = irq_get_irqchip_state(irq->host_irq, +					    IRQCHIP_STATE_PENDING, +					    &irq->pending_latch); +		WARN_ON(ret); + +		desc = irq_to_desc(irq->host_irq); +		irq_domain_deactivate_irq(irq_desc_get_irq_data(desc)); +	unlock: +		raw_spin_unlock_irqrestore(&irq->irq_lock, flags); +		vgic_put_irq(vcpu->kvm, irq); +	} +} + +/* Must be called with the kvm lock held */ +void vgic_v4_configure_vsgis(struct kvm *kvm) +{ +	struct vgic_dist *dist = &kvm->arch.vgic; +	struct kvm_vcpu *vcpu; +	int i; + +	kvm_arm_halt_guest(kvm); + +	kvm_for_each_vcpu(i, vcpu, kvm) { +		if (dist->nassgireq) +			vgic_v4_enable_vsgis(vcpu); +		else +			vgic_v4_disable_vsgis(vcpu); +	} + +	kvm_arm_resume_guest(kvm); +} +  /**   * vgic_v4_init - Initialize the GICv4 data structures   * @kvm:	Pointer to the VM being initialized diff --git a/virt/kvm/arm/vgic/vgic.h b/virt/kvm/arm/vgic/vgic.h index c7fefd6b1c80..769e4802645e 100644 --- a/virt/kvm/arm/vgic/vgic.h +++ b/virt/kvm/arm/vgic/vgic.h @@ -316,5 +316,6 @@ void vgic_its_invalidate_cache(struct kvm *kvm);  bool vgic_supports_direct_msis(struct kvm *kvm);  int vgic_v4_init(struct kvm *kvm);  void vgic_v4_teardown(struct kvm *kvm); +void vgic_v4_configure_vsgis(struct kvm *kvm);  #endif  |