KVM: arm64: GICv4.1: Allow SGIs to switch between HW and SW interrupts
In order to let a guest buy in the new, active-less SGIs, we need to be able to switch between the two modes. Handle this by stopping all guest activity, transfer the state from one mode to the other, and resume the guest. Nothing calls this code so far, but a later patch will plug it into the MMIO emulation. Signed-off-by: Marc Zyngier <maz@kernel.org> Reviewed-by: Zenghui Yu <yuzenghui@huawei.com> Link: https://lore.kernel.org/r/20200304203330.4967-20-maz@kernel.org
This commit is contained in:
Родитель
ef1820be47
Коммит
bacf2c6054
|
@ -231,6 +231,9 @@ struct vgic_dist {
|
||||||
/* distributor enabled */
|
/* distributor enabled */
|
||||||
bool enabled;
|
bool enabled;
|
||||||
|
|
||||||
|
/* Wants SGIs without active state */
|
||||||
|
bool nassgireq;
|
||||||
|
|
||||||
struct vgic_irq *spis;
|
struct vgic_irq *spis;
|
||||||
|
|
||||||
struct vgic_io_device dist_iodev;
|
struct vgic_io_device dist_iodev;
|
||||||
|
|
|
@ -97,6 +97,104 @@ static irqreturn_t vgic_v4_doorbell_handler(int irq, void *info)
|
||||||
return IRQ_HANDLED;
|
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
|
* vgic_v4_init - Initialize the GICv4 data structures
|
||||||
* @kvm: Pointer to the VM being initialized
|
* @kvm: Pointer to the VM being initialized
|
||||||
|
|
|
@ -316,5 +316,6 @@ void vgic_its_invalidate_cache(struct kvm *kvm);
|
||||||
bool vgic_supports_direct_msis(struct kvm *kvm);
|
bool vgic_supports_direct_msis(struct kvm *kvm);
|
||||||
int vgic_v4_init(struct kvm *kvm);
|
int vgic_v4_init(struct kvm *kvm);
|
||||||
void vgic_v4_teardown(struct kvm *kvm);
|
void vgic_v4_teardown(struct kvm *kvm);
|
||||||
|
void vgic_v4_configure_vsgis(struct kvm *kvm);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
Загрузка…
Ссылка в новой задаче