blob: da4b40f7ffa9c34446e8974efaed11660f131ddb [file] [log] [blame]
// SPDX-License-Identifier: GPL-2.0-only
#include <linux/cpu.h>
#include <linux/kvm.h>
#include <linux/kvm_host.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/uaccess.h>
#include <linux/irqchip/arm-gic-v3.h>
#include <asm/kvm_emulate.h>
#include <asm/kvm_arm.h>
#include <kvm/arm_vgic.h>
#include "vgic.h"
#define CREATE_TRACE_POINTS
#include "vgic-nested-trace.h"
/*
* The shadow registers loaded to the hardware when running a L2 guest
* with the virtual IMO/FMO bits set.
*/
static DEFINE_PER_CPU(struct vgic_v3_cpu_if, shadow_cpuif);
static inline struct vgic_v3_cpu_if *vcpu_shadow_if(struct kvm_vcpu *vcpu)
{
return this_cpu_ptr(&shadow_cpuif);
}
static inline bool lr_triggers_eoi(u64 lr)
{
return !(lr & (ICH_LR_STATE | ICH_LR_HW)) && (lr & ICH_LR_EOI);
}
u16 vgic_v3_get_eisr(struct kvm_vcpu *vcpu)
{
u16 reg = 0;
int i;
if (vgic_state_is_nested(vcpu))
return read_sysreg_s(SYS_ICH_EISR_EL2);
for (i = 0; i < kvm_vgic_global_state.nr_lr; i++) {
if (lr_triggers_eoi(__vcpu_sys_reg(vcpu, ICH_LRN(i))))
reg |= BIT(i);
}
return reg;
}
u16 vgic_v3_get_elrsr(struct kvm_vcpu *vcpu)
{
u16 reg = 0;
int i;
if (vgic_state_is_nested(vcpu))
return read_sysreg_s(SYS_ICH_ELRSR_EL2);
for (i = 0; i < kvm_vgic_global_state.nr_lr; i++) {
if (!(__vcpu_sys_reg(vcpu, ICH_LRN(i)) & ICH_LR_STATE))
reg |= BIT(i);
}
return reg;
}
u64 vgic_v3_get_misr(struct kvm_vcpu *vcpu)
{
int nr_lr = kvm_vgic_global_state.nr_lr;
u64 reg = 0;
if (vgic_state_is_nested(vcpu))
return read_sysreg_s(SYS_ICH_MISR_EL2);
if (vgic_v3_get_eisr(vcpu))
reg |= ICH_MISR_EOI;
if (__vcpu_sys_reg(vcpu, ICH_HCR_EL2) & ICH_HCR_UIE) {
int used_lrs;
used_lrs = nr_lr - hweight16(vgic_v3_get_elrsr(vcpu));
if (used_lrs <= 1)
reg |= ICH_MISR_U;
}
/* TODO: Support remaining bits in this register */
return reg;
}
/*
* For LRs which have HW bit set such as timer interrupts, we modify them to
* have the host hardware interrupt number instead of the virtual one programmed
* by the guest hypervisor.
*/
static void vgic_v3_create_shadow_lr(struct kvm_vcpu *vcpu)
{
struct vgic_v3_cpu_if *s_cpu_if = vcpu_shadow_if(vcpu);
struct vgic_irq *irq;
int i, used_lrs = 0;
for (i = 0; i < kvm_vgic_global_state.nr_lr; i++) {
u64 lr = __vcpu_sys_reg(vcpu, ICH_LRN(i));
int l1_irq;
if (!(lr & ICH_LR_STATE))
lr = 0;
if (!(lr & ICH_LR_HW))
goto next;
/* We have the HW bit set */
l1_irq = (lr & ICH_LR_PHYS_ID_MASK) >> ICH_LR_PHYS_ID_SHIFT;
irq = vgic_get_irq(vcpu->kvm, vcpu, l1_irq);
if (!irq || !irq->hw) {
/* There was no real mapping, so nuke the HW bit */
lr &= ~ICH_LR_HW;
if (irq)
vgic_put_irq(vcpu->kvm, irq);
goto next;
}
/* Translate the virtual mapping to the real one */
lr &= ~ICH_LR_EOI; /* Why? */
lr &= ~ICH_LR_PHYS_ID_MASK;
lr |= (u64)irq->hwintid << ICH_LR_PHYS_ID_SHIFT;
vgic_put_irq(vcpu->kvm, irq);
next:
lr &= ~ICH_LR_EOI; /* Why? */
s_cpu_if->vgic_lr[i] = lr;
used_lrs = i + 1;
}
trace_vgic_create_shadow_lrs(vcpu, kvm_vgic_global_state.nr_lr,
s_cpu_if->vgic_lr,
__ctxt_sys_reg(&vcpu->arch.ctxt, ICH_LR0_EL2));
s_cpu_if->used_lrs = used_lrs;
}
void vgic_v3_sync_nested(struct kvm_vcpu *vcpu)
{
struct vgic_v3_cpu_if *s_cpu_if = vcpu_shadow_if(vcpu);
struct vgic_irq *irq;
int i;
for (i = 0; i < s_cpu_if->used_lrs; i++) {
u64 lr = __vcpu_sys_reg(vcpu, ICH_LRN(i));
int l1_irq;
if (!(lr & ICH_LR_HW) || !(lr & ICH_LR_STATE))
continue;
/*
* If we had a HW lr programmed by the guest hypervisor, we
* need to emulate the HW effect between the guest hypervisor
* and the nested guest.
*/
l1_irq = (lr & ICH_LR_PHYS_ID_MASK) >> ICH_LR_PHYS_ID_SHIFT;
irq = vgic_get_irq(vcpu->kvm, vcpu, l1_irq);
if (!irq)
continue; /* oh well, the guest hyp is broken */
lr = __gic_v3_get_lr(i);
if (!(lr & ICH_LR_STATE)) {
trace_vgic_nested_hw_emulate(i, lr, l1_irq);
irq->active = false;
}
vgic_put_irq(vcpu->kvm, irq);
}
}
void vgic_v3_create_shadow_state(struct kvm_vcpu *vcpu)
{
struct vgic_v3_cpu_if *s_cpu_if = vcpu_shadow_if(vcpu);
struct vgic_v3_cpu_if *host_if = &vcpu->arch.vgic_cpu.vgic_v3;
u64 val = 0;
int i;
/*
* If we're on a system with a broken vgic that requires
* trapping, propagate the trapping requirements.
*
* Ah, the smell of rotten fruits...
*/
if (static_branch_unlikely(&vgic_v3_cpuif_trap))
val = host_if->vgic_hcr & (ICH_HCR_TALL0 | ICH_HCR_TALL1 |
ICH_HCR_TC | ICH_HCR_TDIR);
s_cpu_if->vgic_hcr = __vcpu_sys_reg(vcpu, ICH_HCR_EL2) | val;
s_cpu_if->vgic_vmcr = __vcpu_sys_reg(vcpu, ICH_VMCR_EL2);
s_cpu_if->vgic_sre = host_if->vgic_sre;
for (i = 0; i < 4; i++) {
s_cpu_if->vgic_ap0r[i] = __vcpu_sys_reg(vcpu, ICH_AP0RN(i));
s_cpu_if->vgic_ap1r[i] = __vcpu_sys_reg(vcpu, ICH_AP1RN(i));
}
vgic_v3_create_shadow_lr(vcpu);
vcpu->arch.vgic_cpu.current_cpu_if = s_cpu_if;
}
void vgic_v3_load_nested(struct kvm_vcpu *vcpu)
{
struct vgic_irq *irq;
unsigned long flags;
__vgic_v3_restore_state(vcpu_shadow_if(vcpu));
irq = vgic_get_irq(vcpu->kvm, vcpu, vcpu->kvm->arch.vgic.maint_irq);
raw_spin_lock_irqsave(&irq->irq_lock, flags);
if (irq->line_level || irq->active)
irq_set_irqchip_state(kvm_vgic_global_state.maint_irq,
IRQCHIP_STATE_ACTIVE, true);
raw_spin_unlock_irqrestore(&irq->irq_lock, flags);
vgic_put_irq(vcpu->kvm, irq);
}
void vgic_v3_put_nested(struct kvm_vcpu *vcpu)
{
struct vgic_v3_cpu_if *s_cpu_if = vcpu_shadow_if(vcpu);
u64 val;
int i;
__vgic_v3_save_state(s_cpu_if);
trace_vgic_put_nested(vcpu, kvm_vgic_global_state.nr_lr, s_cpu_if->vgic_lr);
/*
* Translate the shadow state HW fields back to the virtual ones
* before copying the shadow struct back to the nested one.
*/
val = __vcpu_sys_reg(vcpu, ICH_HCR_EL2);
val &= ~ICH_HCR_EOIcount_MASK;
val |= (s_cpu_if->vgic_hcr & ICH_HCR_EOIcount_MASK);
__vcpu_sys_reg(vcpu, ICH_HCR_EL2) = val;
__vcpu_sys_reg(vcpu, ICH_VMCR_EL2) = s_cpu_if->vgic_vmcr;
for (i = 0; i < 4; i++) {
__vcpu_sys_reg(vcpu, ICH_AP0RN(i)) = s_cpu_if->vgic_ap0r[i];
__vcpu_sys_reg(vcpu, ICH_AP1RN(i)) = s_cpu_if->vgic_ap1r[i];
}
for (i = 0; i < s_cpu_if->used_lrs; i++) {
val = __vcpu_sys_reg(vcpu, ICH_LRN(i));
val &= ~ICH_LR_STATE;
val |= s_cpu_if->vgic_lr[i] & ICH_LR_STATE;
__vcpu_sys_reg(vcpu, ICH_LRN(i)) = val;
s_cpu_if->vgic_lr[i] = 0;
}
irq_set_irqchip_state(kvm_vgic_global_state.maint_irq,
IRQCHIP_STATE_ACTIVE, false);
}
void vgic_v3_check_nested_maint_irq(struct kvm_vcpu *vcpu)
{
/*
* If we exit a nested VM with a pending maintenance interrupt from the
* GIC, then we need to forward this to the guest hypervisor so that it
* can re-sync the appropriate LRs and sample level triggered interrupts
* again.
*/
if (vgic_state_is_nested(vcpu)) {
bool state;
state = __vcpu_sys_reg(vcpu, ICH_HCR_EL2) & ICH_HCR_EN;
state &= vgic_v3_get_misr(vcpu);
#if 0
if (state) {
vcpu_set_flag(vcpu, VGIC_MI_PENDING);
} else {
vcpu_clear_flag(vcpu, VGIC_MI_PENDING);
}
#else
kvm_vgic_inject_irq(vcpu->kvm, vcpu,
vcpu->kvm->arch.vgic.maint_irq, state, vcpu);
#endif
}
if (unlikely(kvm_vgic_global_state.no_hw_deactivation)) {
sysreg_clear_set_s(SYS_ICH_HCR_EL2, ICH_HCR_EN, 0);
isb();
}
}
void vgic_v3_sync_nested_maint_irq(struct kvm_vcpu *vcpu)
{
#if 0
if (vgic_state_is_nested(vcpu)) {
kvm_vgic_inject_irq(vcpu->kvm, vcpu,
vcpu->kvm->arch.vgic.maint_irq,
vcpu_get_flag(vcpu, VGIC_MI_PENDING), vcpu);
vcpu_clear_flag(vcpu, VGIC_MI_PENDING);
}
#endif
}