1
0
Fork 0
mirror of synced 2025-03-06 20:59:54 +01:00

irqchip/apple-aic: Register vgic maintenance interrupt with KVM

In order to deliver vgic maintenance interrupts that Nested Virt
requires, hook it into the FIQ space, even if it is delivered
as an IRQ (we don't distinguish between the two anyway).

Signed-off-by: Marc Zyngier <maz@kernel.org>
Link: https://lore.kernel.org/r/20230103095022.3230946-4-maz@kernel.org
Signed-off-by: Oliver Upton <oliver.upton@linux.dev>
This commit is contained in:
Marc Zyngier 2023-01-03 09:50:22 +00:00 committed by Oliver Upton
parent 43c5c868bd
commit 13aad0c00b

View file

@ -210,7 +210,6 @@
FIELD_PREP(AIC_EVENT_NUM, x)) FIELD_PREP(AIC_EVENT_NUM, x))
#define AIC_HWIRQ_IRQ(x) FIELD_GET(AIC_EVENT_NUM, x) #define AIC_HWIRQ_IRQ(x) FIELD_GET(AIC_EVENT_NUM, x)
#define AIC_HWIRQ_DIE(x) FIELD_GET(AIC_EVENT_DIE, x) #define AIC_HWIRQ_DIE(x) FIELD_GET(AIC_EVENT_DIE, x)
#define AIC_NR_FIQ 6
#define AIC_NR_SWIPI 32 #define AIC_NR_SWIPI 32
/* /*
@ -222,11 +221,18 @@
* running at EL2 (with VHE). When the kernel is running at EL1, the * running at EL2 (with VHE). When the kernel is running at EL1, the
* mapping differs and aic_irq_domain_translate() performs the remapping. * mapping differs and aic_irq_domain_translate() performs the remapping.
*/ */
enum fiq_hwirq {
#define AIC_TMR_EL0_PHYS AIC_TMR_HV_PHYS /* Must be ordered as in apple-aic.h */
#define AIC_TMR_EL0_VIRT AIC_TMR_HV_VIRT AIC_TMR_EL0_PHYS = AIC_TMR_HV_PHYS,
#define AIC_TMR_EL02_PHYS AIC_TMR_GUEST_PHYS AIC_TMR_EL0_VIRT = AIC_TMR_HV_VIRT,
#define AIC_TMR_EL02_VIRT AIC_TMR_GUEST_VIRT AIC_TMR_EL02_PHYS = AIC_TMR_GUEST_PHYS,
AIC_TMR_EL02_VIRT = AIC_TMR_GUEST_VIRT,
AIC_CPU_PMU_Effi = AIC_CPU_PMU_E,
AIC_CPU_PMU_Perf = AIC_CPU_PMU_P,
/* No need for this to be discovered from DT */
AIC_VGIC_MI,
AIC_NR_FIQ
};
static DEFINE_STATIC_KEY_TRUE(use_fast_ipi); static DEFINE_STATIC_KEY_TRUE(use_fast_ipi);
@ -384,16 +390,22 @@ static void __exception_irq_entry aic_handle_irq(struct pt_regs *regs)
/* /*
* vGIC maintenance interrupts end up here too, so we need to check * vGIC maintenance interrupts end up here too, so we need to check
* for them separately. This should never trigger if KVM is working * for them separately. It should however only trigger when NV is
* properly, because it will have already taken care of clearing it * in use, and be cleared when coming back from the handler.
* on guest exit before this handler runs.
*/ */
if (is_kernel_in_hyp_mode() && (read_sysreg_s(SYS_ICH_HCR_EL2) & ICH_HCR_EN) && if (is_kernel_in_hyp_mode() &&
(read_sysreg_s(SYS_ICH_HCR_EL2) & ICH_HCR_EN) &&
read_sysreg_s(SYS_ICH_MISR_EL2) != 0) { read_sysreg_s(SYS_ICH_MISR_EL2) != 0) {
generic_handle_domain_irq(aic_irqc->hw_domain,
AIC_FIQ_HWIRQ(AIC_VGIC_MI));
if (unlikely((read_sysreg_s(SYS_ICH_HCR_EL2) & ICH_HCR_EN) &&
read_sysreg_s(SYS_ICH_MISR_EL2))) {
pr_err_ratelimited("vGIC IRQ fired and not handled by KVM, disabling.\n"); pr_err_ratelimited("vGIC IRQ fired and not handled by KVM, disabling.\n");
sysreg_clear_set_s(SYS_ICH_HCR_EL2, ICH_HCR_EN, 0); sysreg_clear_set_s(SYS_ICH_HCR_EL2, ICH_HCR_EN, 0);
} }
} }
}
static int aic_irq_set_affinity(struct irq_data *d, static int aic_irq_set_affinity(struct irq_data *d,
const struct cpumask *mask_val, bool force) const struct cpumask *mask_val, bool force)
@ -1178,6 +1190,23 @@ static int __init aic_of_ic_init(struct device_node *node, struct device_node *p
"irqchip/apple-aic/ipi:starting", "irqchip/apple-aic/ipi:starting",
aic_init_cpu, NULL); aic_init_cpu, NULL);
if (is_kernel_in_hyp_mode()) {
struct irq_fwspec mi = {
.fwnode = of_node_to_fwnode(node),
.param_count = 3,
.param = {
[0] = AIC_FIQ, /* This is a lie */
[1] = AIC_VGIC_MI,
[2] = IRQ_TYPE_LEVEL_HIGH,
},
};
vgic_info.maint_irq = irq_domain_alloc_irqs(irqc->hw_domain,
1, NUMA_NO_NODE,
&mi);
WARN_ON(!vgic_info.maint_irq);
}
vgic_set_kvm_info(&vgic_info); vgic_set_kvm_info(&vgic_info);
pr_info("Initialized with %d/%d IRQs * %d/%d die(s), %d FIQs, %d vIPIs", pr_info("Initialized with %d/%d IRQs * %d/%d die(s), %d FIQs, %d vIPIs",