Merge branch kvm-arm64/apple-vgic-mi into kvmarm/next

* kvm-arm64/apple-vgic-mi:
  : VGIC maintenance interrupt support for the AIC, courtesy of Marc Zyngier.
  :
  : The AIC provides a non-maskable VGIC maintenance interrupt, which until
  : now was not supported by KVM. This series (1) allows the registration of
  : a non-maskable maintenance interrupt and (2) wires in support for this
  : with the AIC driver.
  irqchip/apple-aic: Correctly map the vgic maintenance interrupt
  irqchip/apple-aic: Register vgic maintenance interrupt with KVM
  KVM: arm64: vgic: Allow registration of a non-maskable maintenance interrupt

Signed-off-by: Oliver Upton <oliver.upton@linux.dev>
This commit is contained in:
Oliver Upton 2023-02-13 23:31:23 +00:00
commit e4f7417e96
2 changed files with 41 additions and 14 deletions

View File

@ -570,7 +570,7 @@ int kvm_vgic_hyp_init(void)
if (ret)
return ret;
if (!has_mask)
if (!has_mask && !kvm_vgic_global_state.maint_irq)
return 0;
ret = request_percpu_irq(kvm_vgic_global_state.maint_irq,

View File

@ -210,7 +210,6 @@
FIELD_PREP(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_NR_FIQ 6
#define AIC_NR_SWIPI 32
/*
@ -222,11 +221,18 @@
* running at EL2 (with VHE). When the kernel is running at EL1, the
* mapping differs and aic_irq_domain_translate() performs the remapping.
*/
#define AIC_TMR_EL0_PHYS AIC_TMR_HV_PHYS
#define AIC_TMR_EL0_VIRT AIC_TMR_HV_VIRT
#define AIC_TMR_EL02_PHYS AIC_TMR_GUEST_PHYS
#define AIC_TMR_EL02_VIRT AIC_TMR_GUEST_VIRT
enum fiq_hwirq {
/* Must be ordered as in apple-aic.h */
AIC_TMR_EL0_PHYS = AIC_TMR_HV_PHYS,
AIC_TMR_EL0_VIRT = AIC_TMR_HV_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);
@ -384,14 +390,20 @@ static void __exception_irq_entry aic_handle_irq(struct pt_regs *regs)
/*
* vGIC maintenance interrupts end up here too, so we need to check
* for them separately. This should never trigger if KVM is working
* properly, because it will have already taken care of clearing it
* on guest exit before this handler runs.
* for them separately. It should however only trigger when NV is
* in use, and be cleared when coming back from the handler.
*/
if (is_kernel_in_hyp_mode() && (read_sysreg_s(SYS_ICH_HCR_EL2) & ICH_HCR_EN) &&
read_sysreg_s(SYS_ICH_MISR_EL2) != 0) {
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);
if (is_kernel_in_hyp_mode() &&
(read_sysreg_s(SYS_ICH_HCR_EL2) & ICH_HCR_EN) &&
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");
sysreg_clear_set_s(SYS_ICH_HCR_EL2, ICH_HCR_EN, 0);
}
}
}
@ -1178,6 +1190,21 @@ static int __init aic_of_ic_init(struct device_node *node, struct device_node *p
"irqchip/apple-aic/ipi:starting",
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_create_fwspec_mapping(&mi);
WARN_ON(!vgic_info.maint_irq);
}
vgic_set_kvm_info(&vgic_info);
pr_info("Initialized with %d/%d IRQs * %d/%d die(s), %d FIQs, %d vIPIs",