Merge tag 'kvm-arm-for-v4.9' of git://git.kernel.org/pub/scm/linux/kernel/git/kvmarm/kvmarm into next
KVM/ARM Changes for v4.9 - Various cleanups and removal of redundant code - Two important fixes for not using an in-kernel irqchip - A bit of optimizations - Handle SError exceptions and present them to guests if appropriate - Proxying of GICV access at EL2 if guest mappings are unsafe - GICv3 on AArch32 on ARMv8 - Preparations for GICv3 save/restore, including ABI docs
This commit is contained in:
152
virt/kvm/arm/aarch32.c
Normal file
152
virt/kvm/arm/aarch32.c
Normal file
@@ -0,0 +1,152 @@
|
||||
/*
|
||||
* (not much of an) Emulation layer for 32bit guests.
|
||||
*
|
||||
* Copyright (C) 2012,2013 - ARM Ltd
|
||||
* Author: Marc Zyngier <marc.zyngier@arm.com>
|
||||
*
|
||||
* based on arch/arm/kvm/emulate.c
|
||||
* Copyright (C) 2012 - Virtual Open Systems and Columbia University
|
||||
* Author: Christoffer Dall <c.dall@virtualopensystems.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <linux/kvm_host.h>
|
||||
#include <asm/kvm_emulate.h>
|
||||
#include <asm/kvm_hyp.h>
|
||||
|
||||
#ifndef CONFIG_ARM64
|
||||
#define COMPAT_PSR_T_BIT PSR_T_BIT
|
||||
#define COMPAT_PSR_IT_MASK PSR_IT_MASK
|
||||
#endif
|
||||
|
||||
/*
|
||||
* stolen from arch/arm/kernel/opcodes.c
|
||||
*
|
||||
* condition code lookup table
|
||||
* index into the table is test code: EQ, NE, ... LT, GT, AL, NV
|
||||
*
|
||||
* bit position in short is condition code: NZCV
|
||||
*/
|
||||
static const unsigned short cc_map[16] = {
|
||||
0xF0F0, /* EQ == Z set */
|
||||
0x0F0F, /* NE */
|
||||
0xCCCC, /* CS == C set */
|
||||
0x3333, /* CC */
|
||||
0xFF00, /* MI == N set */
|
||||
0x00FF, /* PL */
|
||||
0xAAAA, /* VS == V set */
|
||||
0x5555, /* VC */
|
||||
0x0C0C, /* HI == C set && Z clear */
|
||||
0xF3F3, /* LS == C clear || Z set */
|
||||
0xAA55, /* GE == (N==V) */
|
||||
0x55AA, /* LT == (N!=V) */
|
||||
0x0A05, /* GT == (!Z && (N==V)) */
|
||||
0xF5FA, /* LE == (Z || (N!=V)) */
|
||||
0xFFFF, /* AL always */
|
||||
0 /* NV */
|
||||
};
|
||||
|
||||
/*
|
||||
* Check if a trapped instruction should have been executed or not.
|
||||
*/
|
||||
bool kvm_condition_valid32(const struct kvm_vcpu *vcpu)
|
||||
{
|
||||
unsigned long cpsr;
|
||||
u32 cpsr_cond;
|
||||
int cond;
|
||||
|
||||
/* Top two bits non-zero? Unconditional. */
|
||||
if (kvm_vcpu_get_hsr(vcpu) >> 30)
|
||||
return true;
|
||||
|
||||
/* Is condition field valid? */
|
||||
cond = kvm_vcpu_get_condition(vcpu);
|
||||
if (cond == 0xE)
|
||||
return true;
|
||||
|
||||
cpsr = *vcpu_cpsr(vcpu);
|
||||
|
||||
if (cond < 0) {
|
||||
/* This can happen in Thumb mode: examine IT state. */
|
||||
unsigned long it;
|
||||
|
||||
it = ((cpsr >> 8) & 0xFC) | ((cpsr >> 25) & 0x3);
|
||||
|
||||
/* it == 0 => unconditional. */
|
||||
if (it == 0)
|
||||
return true;
|
||||
|
||||
/* The cond for this insn works out as the top 4 bits. */
|
||||
cond = (it >> 4);
|
||||
}
|
||||
|
||||
cpsr_cond = cpsr >> 28;
|
||||
|
||||
if (!((cc_map[cond] >> cpsr_cond) & 1))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* adjust_itstate - adjust ITSTATE when emulating instructions in IT-block
|
||||
* @vcpu: The VCPU pointer
|
||||
*
|
||||
* When exceptions occur while instructions are executed in Thumb IF-THEN
|
||||
* blocks, the ITSTATE field of the CPSR is not advanced (updated), so we have
|
||||
* to do this little bit of work manually. The fields map like this:
|
||||
*
|
||||
* IT[7:0] -> CPSR[26:25],CPSR[15:10]
|
||||
*/
|
||||
static void __hyp_text kvm_adjust_itstate(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
unsigned long itbits, cond;
|
||||
unsigned long cpsr = *vcpu_cpsr(vcpu);
|
||||
bool is_arm = !(cpsr & COMPAT_PSR_T_BIT);
|
||||
|
||||
if (is_arm || !(cpsr & COMPAT_PSR_IT_MASK))
|
||||
return;
|
||||
|
||||
cond = (cpsr & 0xe000) >> 13;
|
||||
itbits = (cpsr & 0x1c00) >> (10 - 2);
|
||||
itbits |= (cpsr & (0x3 << 25)) >> 25;
|
||||
|
||||
/* Perform ITAdvance (see page A2-52 in ARM DDI 0406C) */
|
||||
if ((itbits & 0x7) == 0)
|
||||
itbits = cond = 0;
|
||||
else
|
||||
itbits = (itbits << 1) & 0x1f;
|
||||
|
||||
cpsr &= ~COMPAT_PSR_IT_MASK;
|
||||
cpsr |= cond << 13;
|
||||
cpsr |= (itbits & 0x1c) << (10 - 2);
|
||||
cpsr |= (itbits & 0x3) << 25;
|
||||
*vcpu_cpsr(vcpu) = cpsr;
|
||||
}
|
||||
|
||||
/**
|
||||
* kvm_skip_instr - skip a trapped instruction and proceed to the next
|
||||
* @vcpu: The vcpu pointer
|
||||
*/
|
||||
void __hyp_text kvm_skip_instr32(struct kvm_vcpu *vcpu, bool is_wide_instr)
|
||||
{
|
||||
bool is_thumb;
|
||||
|
||||
is_thumb = !!(*vcpu_cpsr(vcpu) & COMPAT_PSR_T_BIT);
|
||||
if (is_thumb && !is_wide_instr)
|
||||
*vcpu_pc(vcpu) += 2;
|
||||
else
|
||||
*vcpu_pc(vcpu) += 4;
|
||||
kvm_adjust_itstate(vcpu);
|
||||
}
|
@@ -445,7 +445,7 @@ int kvm_timer_hyp_init(void)
|
||||
if (err) {
|
||||
kvm_err("kvm_arch_timer: can't request interrupt %d (%d)\n",
|
||||
host_vtimer_irq, err);
|
||||
goto out;
|
||||
return err;
|
||||
}
|
||||
|
||||
kvm_info("virtual timer IRQ%d\n", host_vtimer_irq);
|
||||
@@ -453,10 +453,6 @@ int kvm_timer_hyp_init(void)
|
||||
cpuhp_setup_state(CPUHP_AP_KVM_ARM_TIMER_STARTING,
|
||||
"AP_KVM_ARM_TIMER_STARTING", kvm_timer_starting_cpu,
|
||||
kvm_timer_dying_cpu);
|
||||
goto out;
|
||||
out_free:
|
||||
free_percpu_irq(host_vtimer_irq, kvm_get_running_vcpus());
|
||||
out:
|
||||
return err;
|
||||
}
|
||||
|
||||
|
@@ -19,6 +19,7 @@
|
||||
#include <linux/irqchip/arm-gic.h>
|
||||
#include <linux/kvm_host.h>
|
||||
|
||||
#include <asm/kvm_emulate.h>
|
||||
#include <asm/kvm_hyp.h>
|
||||
|
||||
static void __hyp_text save_maint_int_state(struct kvm_vcpu *vcpu,
|
||||
@@ -167,3 +168,59 @@ void __hyp_text __vgic_v2_restore_state(struct kvm_vcpu *vcpu)
|
||||
writel_relaxed(cpu_if->vgic_vmcr, base + GICH_VMCR);
|
||||
vcpu->arch.vgic_cpu.live_lrs = live_lrs;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_ARM64
|
||||
/*
|
||||
* __vgic_v2_perform_cpuif_access -- perform a GICV access on behalf of the
|
||||
* guest.
|
||||
*
|
||||
* @vcpu: the offending vcpu
|
||||
*
|
||||
* Returns:
|
||||
* 1: GICV access successfully performed
|
||||
* 0: Not a GICV access
|
||||
* -1: Illegal GICV access
|
||||
*/
|
||||
int __hyp_text __vgic_v2_perform_cpuif_access(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
struct kvm *kvm = kern_hyp_va(vcpu->kvm);
|
||||
struct vgic_dist *vgic = &kvm->arch.vgic;
|
||||
phys_addr_t fault_ipa;
|
||||
void __iomem *addr;
|
||||
int rd;
|
||||
|
||||
/* Build the full address */
|
||||
fault_ipa = kvm_vcpu_get_fault_ipa(vcpu);
|
||||
fault_ipa |= kvm_vcpu_get_hfar(vcpu) & GENMASK(11, 0);
|
||||
|
||||
/* If not for GICV, move on */
|
||||
if (fault_ipa < vgic->vgic_cpu_base ||
|
||||
fault_ipa >= (vgic->vgic_cpu_base + KVM_VGIC_V2_CPU_SIZE))
|
||||
return 0;
|
||||
|
||||
/* Reject anything but a 32bit access */
|
||||
if (kvm_vcpu_dabt_get_as(vcpu) != sizeof(u32))
|
||||
return -1;
|
||||
|
||||
/* Not aligned? Don't bother */
|
||||
if (fault_ipa & 3)
|
||||
return -1;
|
||||
|
||||
rd = kvm_vcpu_dabt_get_rd(vcpu);
|
||||
addr = kern_hyp_va((kern_hyp_va(&kvm_vgic_global_state))->vcpu_base_va);
|
||||
addr += fault_ipa - vgic->vgic_cpu_base;
|
||||
|
||||
if (kvm_vcpu_dabt_iswrite(vcpu)) {
|
||||
u32 data = vcpu_data_guest_to_host(vcpu,
|
||||
vcpu_get_reg(vcpu, rd),
|
||||
sizeof(u32));
|
||||
writel_relaxed(data, addr);
|
||||
} else {
|
||||
u32 data = readl_relaxed(addr);
|
||||
vcpu_set_reg(vcpu, rd, vcpu_data_host_to_guest(vcpu, data,
|
||||
sizeof(u32)));
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
#endif
|
||||
|
328
virt/kvm/arm/hyp/vgic-v3-sr.c
Normal file
328
virt/kvm/arm/hyp/vgic-v3-sr.c
Normal file
@@ -0,0 +1,328 @@
|
||||
/*
|
||||
* Copyright (C) 2012-2015 - ARM Ltd
|
||||
* Author: Marc Zyngier <marc.zyngier@arm.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <linux/compiler.h>
|
||||
#include <linux/irqchip/arm-gic-v3.h>
|
||||
#include <linux/kvm_host.h>
|
||||
|
||||
#include <asm/kvm_hyp.h>
|
||||
|
||||
#define vtr_to_max_lr_idx(v) ((v) & 0xf)
|
||||
#define vtr_to_nr_pri_bits(v) (((u32)(v) >> 29) + 1)
|
||||
|
||||
static u64 __hyp_text __gic_v3_get_lr(unsigned int lr)
|
||||
{
|
||||
switch (lr & 0xf) {
|
||||
case 0:
|
||||
return read_gicreg(ICH_LR0_EL2);
|
||||
case 1:
|
||||
return read_gicreg(ICH_LR1_EL2);
|
||||
case 2:
|
||||
return read_gicreg(ICH_LR2_EL2);
|
||||
case 3:
|
||||
return read_gicreg(ICH_LR3_EL2);
|
||||
case 4:
|
||||
return read_gicreg(ICH_LR4_EL2);
|
||||
case 5:
|
||||
return read_gicreg(ICH_LR5_EL2);
|
||||
case 6:
|
||||
return read_gicreg(ICH_LR6_EL2);
|
||||
case 7:
|
||||
return read_gicreg(ICH_LR7_EL2);
|
||||
case 8:
|
||||
return read_gicreg(ICH_LR8_EL2);
|
||||
case 9:
|
||||
return read_gicreg(ICH_LR9_EL2);
|
||||
case 10:
|
||||
return read_gicreg(ICH_LR10_EL2);
|
||||
case 11:
|
||||
return read_gicreg(ICH_LR11_EL2);
|
||||
case 12:
|
||||
return read_gicreg(ICH_LR12_EL2);
|
||||
case 13:
|
||||
return read_gicreg(ICH_LR13_EL2);
|
||||
case 14:
|
||||
return read_gicreg(ICH_LR14_EL2);
|
||||
case 15:
|
||||
return read_gicreg(ICH_LR15_EL2);
|
||||
}
|
||||
|
||||
unreachable();
|
||||
}
|
||||
|
||||
static void __hyp_text __gic_v3_set_lr(u64 val, int lr)
|
||||
{
|
||||
switch (lr & 0xf) {
|
||||
case 0:
|
||||
write_gicreg(val, ICH_LR0_EL2);
|
||||
break;
|
||||
case 1:
|
||||
write_gicreg(val, ICH_LR1_EL2);
|
||||
break;
|
||||
case 2:
|
||||
write_gicreg(val, ICH_LR2_EL2);
|
||||
break;
|
||||
case 3:
|
||||
write_gicreg(val, ICH_LR3_EL2);
|
||||
break;
|
||||
case 4:
|
||||
write_gicreg(val, ICH_LR4_EL2);
|
||||
break;
|
||||
case 5:
|
||||
write_gicreg(val, ICH_LR5_EL2);
|
||||
break;
|
||||
case 6:
|
||||
write_gicreg(val, ICH_LR6_EL2);
|
||||
break;
|
||||
case 7:
|
||||
write_gicreg(val, ICH_LR7_EL2);
|
||||
break;
|
||||
case 8:
|
||||
write_gicreg(val, ICH_LR8_EL2);
|
||||
break;
|
||||
case 9:
|
||||
write_gicreg(val, ICH_LR9_EL2);
|
||||
break;
|
||||
case 10:
|
||||
write_gicreg(val, ICH_LR10_EL2);
|
||||
break;
|
||||
case 11:
|
||||
write_gicreg(val, ICH_LR11_EL2);
|
||||
break;
|
||||
case 12:
|
||||
write_gicreg(val, ICH_LR12_EL2);
|
||||
break;
|
||||
case 13:
|
||||
write_gicreg(val, ICH_LR13_EL2);
|
||||
break;
|
||||
case 14:
|
||||
write_gicreg(val, ICH_LR14_EL2);
|
||||
break;
|
||||
case 15:
|
||||
write_gicreg(val, ICH_LR15_EL2);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void __hyp_text save_maint_int_state(struct kvm_vcpu *vcpu, int nr_lr)
|
||||
{
|
||||
struct vgic_v3_cpu_if *cpu_if = &vcpu->arch.vgic_cpu.vgic_v3;
|
||||
int i;
|
||||
bool expect_mi;
|
||||
|
||||
expect_mi = !!(cpu_if->vgic_hcr & ICH_HCR_UIE);
|
||||
|
||||
for (i = 0; i < nr_lr; i++) {
|
||||
if (!(vcpu->arch.vgic_cpu.live_lrs & (1UL << i)))
|
||||
continue;
|
||||
|
||||
expect_mi |= (!(cpu_if->vgic_lr[i] & ICH_LR_HW) &&
|
||||
(cpu_if->vgic_lr[i] & ICH_LR_EOI));
|
||||
}
|
||||
|
||||
if (expect_mi) {
|
||||
cpu_if->vgic_misr = read_gicreg(ICH_MISR_EL2);
|
||||
|
||||
if (cpu_if->vgic_misr & ICH_MISR_EOI)
|
||||
cpu_if->vgic_eisr = read_gicreg(ICH_EISR_EL2);
|
||||
else
|
||||
cpu_if->vgic_eisr = 0;
|
||||
} else {
|
||||
cpu_if->vgic_misr = 0;
|
||||
cpu_if->vgic_eisr = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void __hyp_text __vgic_v3_save_state(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
struct vgic_v3_cpu_if *cpu_if = &vcpu->arch.vgic_cpu.vgic_v3;
|
||||
u64 val;
|
||||
|
||||
/*
|
||||
* Make sure stores to the GIC via the memory mapped interface
|
||||
* are now visible to the system register interface.
|
||||
*/
|
||||
if (!cpu_if->vgic_sre)
|
||||
dsb(st);
|
||||
|
||||
cpu_if->vgic_vmcr = read_gicreg(ICH_VMCR_EL2);
|
||||
|
||||
if (vcpu->arch.vgic_cpu.live_lrs) {
|
||||
int i;
|
||||
u32 max_lr_idx, nr_pri_bits;
|
||||
|
||||
cpu_if->vgic_elrsr = read_gicreg(ICH_ELSR_EL2);
|
||||
|
||||
write_gicreg(0, ICH_HCR_EL2);
|
||||
val = read_gicreg(ICH_VTR_EL2);
|
||||
max_lr_idx = vtr_to_max_lr_idx(val);
|
||||
nr_pri_bits = vtr_to_nr_pri_bits(val);
|
||||
|
||||
save_maint_int_state(vcpu, max_lr_idx + 1);
|
||||
|
||||
for (i = 0; i <= max_lr_idx; i++) {
|
||||
if (!(vcpu->arch.vgic_cpu.live_lrs & (1UL << i)))
|
||||
continue;
|
||||
|
||||
if (cpu_if->vgic_elrsr & (1 << i))
|
||||
cpu_if->vgic_lr[i] &= ~ICH_LR_STATE;
|
||||
else
|
||||
cpu_if->vgic_lr[i] = __gic_v3_get_lr(i);
|
||||
|
||||
__gic_v3_set_lr(0, i);
|
||||
}
|
||||
|
||||
switch (nr_pri_bits) {
|
||||
case 7:
|
||||
cpu_if->vgic_ap0r[3] = read_gicreg(ICH_AP0R3_EL2);
|
||||
cpu_if->vgic_ap0r[2] = read_gicreg(ICH_AP0R2_EL2);
|
||||
case 6:
|
||||
cpu_if->vgic_ap0r[1] = read_gicreg(ICH_AP0R1_EL2);
|
||||
default:
|
||||
cpu_if->vgic_ap0r[0] = read_gicreg(ICH_AP0R0_EL2);
|
||||
}
|
||||
|
||||
switch (nr_pri_bits) {
|
||||
case 7:
|
||||
cpu_if->vgic_ap1r[3] = read_gicreg(ICH_AP1R3_EL2);
|
||||
cpu_if->vgic_ap1r[2] = read_gicreg(ICH_AP1R2_EL2);
|
||||
case 6:
|
||||
cpu_if->vgic_ap1r[1] = read_gicreg(ICH_AP1R1_EL2);
|
||||
default:
|
||||
cpu_if->vgic_ap1r[0] = read_gicreg(ICH_AP1R0_EL2);
|
||||
}
|
||||
|
||||
vcpu->arch.vgic_cpu.live_lrs = 0;
|
||||
} else {
|
||||
cpu_if->vgic_misr = 0;
|
||||
cpu_if->vgic_eisr = 0;
|
||||
cpu_if->vgic_elrsr = 0xffff;
|
||||
cpu_if->vgic_ap0r[0] = 0;
|
||||
cpu_if->vgic_ap0r[1] = 0;
|
||||
cpu_if->vgic_ap0r[2] = 0;
|
||||
cpu_if->vgic_ap0r[3] = 0;
|
||||
cpu_if->vgic_ap1r[0] = 0;
|
||||
cpu_if->vgic_ap1r[1] = 0;
|
||||
cpu_if->vgic_ap1r[2] = 0;
|
||||
cpu_if->vgic_ap1r[3] = 0;
|
||||
}
|
||||
|
||||
val = read_gicreg(ICC_SRE_EL2);
|
||||
write_gicreg(val | ICC_SRE_EL2_ENABLE, ICC_SRE_EL2);
|
||||
|
||||
if (!cpu_if->vgic_sre) {
|
||||
/* Make sure ENABLE is set at EL2 before setting SRE at EL1 */
|
||||
isb();
|
||||
write_gicreg(1, ICC_SRE_EL1);
|
||||
}
|
||||
}
|
||||
|
||||
void __hyp_text __vgic_v3_restore_state(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
struct vgic_v3_cpu_if *cpu_if = &vcpu->arch.vgic_cpu.vgic_v3;
|
||||
u64 val;
|
||||
u32 max_lr_idx, nr_pri_bits;
|
||||
u16 live_lrs = 0;
|
||||
int i;
|
||||
|
||||
/*
|
||||
* VFIQEn is RES1 if ICC_SRE_EL1.SRE is 1. This causes a
|
||||
* Group0 interrupt (as generated in GICv2 mode) to be
|
||||
* delivered as a FIQ to the guest, with potentially fatal
|
||||
* consequences. So we must make sure that ICC_SRE_EL1 has
|
||||
* been actually programmed with the value we want before
|
||||
* starting to mess with the rest of the GIC.
|
||||
*/
|
||||
if (!cpu_if->vgic_sre) {
|
||||
write_gicreg(0, ICC_SRE_EL1);
|
||||
isb();
|
||||
}
|
||||
|
||||
val = read_gicreg(ICH_VTR_EL2);
|
||||
max_lr_idx = vtr_to_max_lr_idx(val);
|
||||
nr_pri_bits = vtr_to_nr_pri_bits(val);
|
||||
|
||||
for (i = 0; i <= max_lr_idx; i++) {
|
||||
if (cpu_if->vgic_lr[i] & ICH_LR_STATE)
|
||||
live_lrs |= (1 << i);
|
||||
}
|
||||
|
||||
write_gicreg(cpu_if->vgic_vmcr, ICH_VMCR_EL2);
|
||||
|
||||
if (live_lrs) {
|
||||
write_gicreg(cpu_if->vgic_hcr, ICH_HCR_EL2);
|
||||
|
||||
switch (nr_pri_bits) {
|
||||
case 7:
|
||||
write_gicreg(cpu_if->vgic_ap0r[3], ICH_AP0R3_EL2);
|
||||
write_gicreg(cpu_if->vgic_ap0r[2], ICH_AP0R2_EL2);
|
||||
case 6:
|
||||
write_gicreg(cpu_if->vgic_ap0r[1], ICH_AP0R1_EL2);
|
||||
default:
|
||||
write_gicreg(cpu_if->vgic_ap0r[0], ICH_AP0R0_EL2);
|
||||
}
|
||||
|
||||
switch (nr_pri_bits) {
|
||||
case 7:
|
||||
write_gicreg(cpu_if->vgic_ap1r[3], ICH_AP1R3_EL2);
|
||||
write_gicreg(cpu_if->vgic_ap1r[2], ICH_AP1R2_EL2);
|
||||
case 6:
|
||||
write_gicreg(cpu_if->vgic_ap1r[1], ICH_AP1R1_EL2);
|
||||
default:
|
||||
write_gicreg(cpu_if->vgic_ap1r[0], ICH_AP1R0_EL2);
|
||||
}
|
||||
|
||||
for (i = 0; i <= max_lr_idx; i++) {
|
||||
if (!(live_lrs & (1 << i)))
|
||||
continue;
|
||||
|
||||
__gic_v3_set_lr(cpu_if->vgic_lr[i], i);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Ensures that the above will have reached the
|
||||
* (re)distributors. This ensure the guest will read the
|
||||
* correct values from the memory-mapped interface.
|
||||
*/
|
||||
if (!cpu_if->vgic_sre) {
|
||||
isb();
|
||||
dsb(sy);
|
||||
}
|
||||
vcpu->arch.vgic_cpu.live_lrs = live_lrs;
|
||||
|
||||
/*
|
||||
* Prevent the guest from touching the GIC system registers if
|
||||
* SRE isn't enabled for GICv3 emulation.
|
||||
*/
|
||||
write_gicreg(read_gicreg(ICC_SRE_EL2) & ~ICC_SRE_EL2_ENABLE,
|
||||
ICC_SRE_EL2);
|
||||
}
|
||||
|
||||
void __hyp_text __vgic_v3_init_lrs(void)
|
||||
{
|
||||
int max_lr_idx = vtr_to_max_lr_idx(read_gicreg(ICH_VTR_EL2));
|
||||
int i;
|
||||
|
||||
for (i = 0; i <= max_lr_idx; i++)
|
||||
__gic_v3_set_lr(0, i);
|
||||
}
|
||||
|
||||
u64 __hyp_text __vgic_v3_get_ich_vtr_el2(void)
|
||||
{
|
||||
return read_gicreg(ICH_VTR_EL2);
|
||||
}
|
@@ -423,6 +423,14 @@ static int kvm_arm_pmu_v3_init(struct kvm_vcpu *vcpu)
|
||||
if (!kvm_arm_support_pmu_v3())
|
||||
return -ENODEV;
|
||||
|
||||
/*
|
||||
* We currently require an in-kernel VGIC to use the PMU emulation,
|
||||
* because we do not support forwarding PMU overflow interrupts to
|
||||
* userspace yet.
|
||||
*/
|
||||
if (!irqchip_in_kernel(vcpu->kvm) || !vgic_initialized(vcpu->kvm))
|
||||
return -ENODEV;
|
||||
|
||||
if (!test_bit(KVM_ARM_VCPU_PMU_V3, vcpu->arch.features) ||
|
||||
!kvm_arm_pmu_irq_initialized(vcpu))
|
||||
return -ENXIO;
|
||||
|
@@ -405,6 +405,10 @@ int kvm_vgic_hyp_init(void)
|
||||
break;
|
||||
case GIC_V3:
|
||||
ret = vgic_v3_probe(gic_kvm_info);
|
||||
if (!ret) {
|
||||
static_branch_enable(&kvm_vgic_global_state.gicv3_cpuif);
|
||||
kvm_info("GIC system register CPU interface enabled\n");
|
||||
}
|
||||
break;
|
||||
default:
|
||||
ret = -ENODEV;
|
||||
|
@@ -46,15 +46,9 @@ static int vgic_irqfd_set_irq(struct kvm_kernel_irq_routing_entry *e,
|
||||
* @ue: user api routing entry handle
|
||||
* return 0 on success, -EINVAL on errors.
|
||||
*/
|
||||
#ifdef KVM_CAP_X2APIC_API
|
||||
int kvm_set_routing_entry(struct kvm *kvm,
|
||||
struct kvm_kernel_irq_routing_entry *e,
|
||||
const struct kvm_irq_routing_entry *ue)
|
||||
#else
|
||||
/* Remove this version and the ifdefery once merged into 4.8 */
|
||||
int kvm_set_routing_entry(struct kvm_kernel_irq_routing_entry *e,
|
||||
const struct kvm_irq_routing_entry *ue)
|
||||
#endif
|
||||
{
|
||||
int r = -EINVAL;
|
||||
|
||||
|
@@ -71,7 +71,6 @@ int kvm_vgic_addr(struct kvm *kvm, unsigned long type, u64 *addr, bool write)
|
||||
addr_ptr = &vgic->vgic_cpu_base;
|
||||
alignment = SZ_4K;
|
||||
break;
|
||||
#ifdef CONFIG_KVM_ARM_VGIC_V3
|
||||
case KVM_VGIC_V3_ADDR_TYPE_DIST:
|
||||
type_needed = KVM_DEV_TYPE_ARM_VGIC_V3;
|
||||
addr_ptr = &vgic->vgic_dist_base;
|
||||
@@ -82,7 +81,6 @@ int kvm_vgic_addr(struct kvm *kvm, unsigned long type, u64 *addr, bool write)
|
||||
addr_ptr = &vgic->vgic_redist_base;
|
||||
alignment = SZ_64K;
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
r = -ENODEV;
|
||||
goto out;
|
||||
@@ -219,52 +217,65 @@ int kvm_register_vgic_device(unsigned long type)
|
||||
ret = kvm_register_device_ops(&kvm_arm_vgic_v2_ops,
|
||||
KVM_DEV_TYPE_ARM_VGIC_V2);
|
||||
break;
|
||||
#ifdef CONFIG_KVM_ARM_VGIC_V3
|
||||
case KVM_DEV_TYPE_ARM_VGIC_V3:
|
||||
ret = kvm_register_device_ops(&kvm_arm_vgic_v3_ops,
|
||||
KVM_DEV_TYPE_ARM_VGIC_V3);
|
||||
|
||||
#ifdef CONFIG_KVM_ARM_VGIC_V3_ITS
|
||||
if (ret)
|
||||
break;
|
||||
ret = kvm_vgic_register_its_device();
|
||||
break;
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/** vgic_attr_regs_access: allows user space to read/write VGIC registers
|
||||
*
|
||||
* @dev: kvm device handle
|
||||
* @attr: kvm device attribute
|
||||
* @reg: address the value is read or written
|
||||
* @is_write: write flag
|
||||
*
|
||||
*/
|
||||
static int vgic_attr_regs_access(struct kvm_device *dev,
|
||||
struct kvm_device_attr *attr,
|
||||
u32 *reg, bool is_write)
|
||||
{
|
||||
struct vgic_reg_attr {
|
||||
struct kvm_vcpu *vcpu;
|
||||
gpa_t addr;
|
||||
int cpuid, ret, c;
|
||||
struct kvm_vcpu *vcpu, *tmp_vcpu;
|
||||
int vcpu_lock_idx = -1;
|
||||
};
|
||||
|
||||
static int parse_vgic_v2_attr(struct kvm_device *dev,
|
||||
struct kvm_device_attr *attr,
|
||||
struct vgic_reg_attr *reg_attr)
|
||||
{
|
||||
int cpuid;
|
||||
|
||||
cpuid = (attr->attr & KVM_DEV_ARM_VGIC_CPUID_MASK) >>
|
||||
KVM_DEV_ARM_VGIC_CPUID_SHIFT;
|
||||
vcpu = kvm_get_vcpu(dev->kvm, cpuid);
|
||||
addr = attr->attr & KVM_DEV_ARM_VGIC_OFFSET_MASK;
|
||||
|
||||
mutex_lock(&dev->kvm->lock);
|
||||
if (cpuid >= atomic_read(&dev->kvm->online_vcpus))
|
||||
return -EINVAL;
|
||||
|
||||
ret = vgic_init(dev->kvm);
|
||||
if (ret)
|
||||
goto out;
|
||||
reg_attr->vcpu = kvm_get_vcpu(dev->kvm, cpuid);
|
||||
reg_attr->addr = attr->attr & KVM_DEV_ARM_VGIC_OFFSET_MASK;
|
||||
|
||||
if (cpuid >= atomic_read(&dev->kvm->online_vcpus)) {
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* unlocks vcpus from @vcpu_lock_idx and smaller */
|
||||
static void unlock_vcpus(struct kvm *kvm, int vcpu_lock_idx)
|
||||
{
|
||||
struct kvm_vcpu *tmp_vcpu;
|
||||
|
||||
for (; vcpu_lock_idx >= 0; vcpu_lock_idx--) {
|
||||
tmp_vcpu = kvm_get_vcpu(kvm, vcpu_lock_idx);
|
||||
mutex_unlock(&tmp_vcpu->mutex);
|
||||
}
|
||||
}
|
||||
|
||||
static void unlock_all_vcpus(struct kvm *kvm)
|
||||
{
|
||||
unlock_vcpus(kvm, atomic_read(&kvm->online_vcpus) - 1);
|
||||
}
|
||||
|
||||
/* Returns true if all vcpus were locked, false otherwise */
|
||||
static bool lock_all_vcpus(struct kvm *kvm)
|
||||
{
|
||||
struct kvm_vcpu *tmp_vcpu;
|
||||
int c;
|
||||
|
||||
/*
|
||||
* Any time a vcpu is run, vcpu_load is called which tries to grab the
|
||||
@@ -272,11 +283,49 @@ static int vgic_attr_regs_access(struct kvm_device *dev,
|
||||
* that no other VCPUs are run and fiddle with the vgic state while we
|
||||
* access it.
|
||||
*/
|
||||
ret = -EBUSY;
|
||||
kvm_for_each_vcpu(c, tmp_vcpu, dev->kvm) {
|
||||
if (!mutex_trylock(&tmp_vcpu->mutex))
|
||||
goto out;
|
||||
vcpu_lock_idx = c;
|
||||
kvm_for_each_vcpu(c, tmp_vcpu, kvm) {
|
||||
if (!mutex_trylock(&tmp_vcpu->mutex)) {
|
||||
unlock_vcpus(kvm, c - 1);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* vgic_attr_regs_access_v2 - allows user space to access VGIC v2 state
|
||||
*
|
||||
* @dev: kvm device handle
|
||||
* @attr: kvm device attribute
|
||||
* @reg: address the value is read or written
|
||||
* @is_write: true if userspace is writing a register
|
||||
*/
|
||||
static int vgic_attr_regs_access_v2(struct kvm_device *dev,
|
||||
struct kvm_device_attr *attr,
|
||||
u32 *reg, bool is_write)
|
||||
{
|
||||
struct vgic_reg_attr reg_attr;
|
||||
gpa_t addr;
|
||||
struct kvm_vcpu *vcpu;
|
||||
int ret;
|
||||
|
||||
ret = parse_vgic_v2_attr(dev, attr, ®_attr);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
vcpu = reg_attr.vcpu;
|
||||
addr = reg_attr.addr;
|
||||
|
||||
mutex_lock(&dev->kvm->lock);
|
||||
|
||||
ret = vgic_init(dev->kvm);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
if (!lock_all_vcpus(dev->kvm)) {
|
||||
ret = -EBUSY;
|
||||
goto out;
|
||||
}
|
||||
|
||||
switch (attr->group) {
|
||||
@@ -291,18 +340,12 @@ static int vgic_attr_regs_access(struct kvm_device *dev,
|
||||
break;
|
||||
}
|
||||
|
||||
unlock_all_vcpus(dev->kvm);
|
||||
out:
|
||||
for (; vcpu_lock_idx >= 0; vcpu_lock_idx--) {
|
||||
tmp_vcpu = kvm_get_vcpu(dev->kvm, vcpu_lock_idx);
|
||||
mutex_unlock(&tmp_vcpu->mutex);
|
||||
}
|
||||
|
||||
mutex_unlock(&dev->kvm->lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* V2 ops */
|
||||
|
||||
static int vgic_v2_set_attr(struct kvm_device *dev,
|
||||
struct kvm_device_attr *attr)
|
||||
{
|
||||
@@ -321,7 +364,7 @@ static int vgic_v2_set_attr(struct kvm_device *dev,
|
||||
if (get_user(reg, uaddr))
|
||||
return -EFAULT;
|
||||
|
||||
return vgic_attr_regs_access(dev, attr, ®, true);
|
||||
return vgic_attr_regs_access_v2(dev, attr, ®, true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -343,7 +386,7 @@ static int vgic_v2_get_attr(struct kvm_device *dev,
|
||||
u32 __user *uaddr = (u32 __user *)(long)attr->addr;
|
||||
u32 reg = 0;
|
||||
|
||||
ret = vgic_attr_regs_access(dev, attr, ®, false);
|
||||
ret = vgic_attr_regs_access_v2(dev, attr, ®, false);
|
||||
if (ret)
|
||||
return ret;
|
||||
return put_user(reg, uaddr);
|
||||
@@ -387,10 +430,6 @@ struct kvm_device_ops kvm_arm_vgic_v2_ops = {
|
||||
.has_attr = vgic_v2_has_attr,
|
||||
};
|
||||
|
||||
/* V3 ops */
|
||||
|
||||
#ifdef CONFIG_KVM_ARM_VGIC_V3
|
||||
|
||||
static int vgic_v3_set_attr(struct kvm_device *dev,
|
||||
struct kvm_device_attr *attr)
|
||||
{
|
||||
@@ -433,5 +472,3 @@ struct kvm_device_ops kvm_arm_vgic_v3_ops = {
|
||||
.get_attr = vgic_v3_get_attr,
|
||||
.has_attr = vgic_v3_has_attr,
|
||||
};
|
||||
|
||||
#endif /* CONFIG_KVM_ARM_VGIC_V3 */
|
||||
|
@@ -23,7 +23,7 @@
|
||||
#include "vgic-mmio.h"
|
||||
|
||||
/* extract @num bytes at @offset bytes offset in data */
|
||||
unsigned long extract_bytes(unsigned long data, unsigned int offset,
|
||||
unsigned long extract_bytes(u64 data, unsigned int offset,
|
||||
unsigned int num)
|
||||
{
|
||||
return (data >> (offset * 8)) & GENMASK_ULL(num * 8 - 1, 0);
|
||||
@@ -42,6 +42,7 @@ u64 update_64bit_reg(u64 reg, unsigned int offset, unsigned int len,
|
||||
return reg | ((u64)val << lower);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_KVM_ARM_VGIC_V3_ITS
|
||||
bool vgic_has_its(struct kvm *kvm)
|
||||
{
|
||||
struct vgic_dist *dist = &kvm->arch.vgic;
|
||||
@@ -51,6 +52,7 @@ bool vgic_has_its(struct kvm *kvm)
|
||||
|
||||
return dist->has_its;
|
||||
}
|
||||
#endif
|
||||
|
||||
static unsigned long vgic_mmio_read_v3_misc(struct kvm_vcpu *vcpu,
|
||||
gpa_t addr, unsigned int len)
|
||||
@@ -179,7 +181,7 @@ static unsigned long vgic_mmio_read_v3r_typer(struct kvm_vcpu *vcpu,
|
||||
int target_vcpu_id = vcpu->vcpu_id;
|
||||
u64 value;
|
||||
|
||||
value = (mpidr & GENMASK(23, 0)) << 32;
|
||||
value = (u64)(mpidr & GENMASK(23, 0)) << 32;
|
||||
value |= ((target_vcpu_id & 0xffff) << 8);
|
||||
if (target_vcpu_id == atomic_read(&vcpu->kvm->online_vcpus) - 1)
|
||||
value |= GICR_TYPER_LAST;
|
||||
@@ -609,7 +611,7 @@ void vgic_v3_dispatch_sgi(struct kvm_vcpu *vcpu, u64 reg)
|
||||
bool broadcast;
|
||||
|
||||
sgi = (reg & ICC_SGI1R_SGI_ID_MASK) >> ICC_SGI1R_SGI_ID_SHIFT;
|
||||
broadcast = reg & BIT(ICC_SGI1R_IRQ_ROUTING_MODE_BIT);
|
||||
broadcast = reg & BIT_ULL(ICC_SGI1R_IRQ_ROUTING_MODE_BIT);
|
||||
target_cpus = (reg & ICC_SGI1R_TARGET_LIST_MASK) >> ICC_SGI1R_TARGET_LIST_SHIFT;
|
||||
mpidr = SGI_AFFINITY_LEVEL(reg, 3);
|
||||
mpidr |= SGI_AFFINITY_LEVEL(reg, 2);
|
||||
|
@@ -550,11 +550,9 @@ int vgic_register_dist_iodev(struct kvm *kvm, gpa_t dist_base_address,
|
||||
case VGIC_V2:
|
||||
len = vgic_v2_init_dist_iodev(io_device);
|
||||
break;
|
||||
#ifdef CONFIG_KVM_ARM_VGIC_V3
|
||||
case VGIC_V3:
|
||||
len = vgic_v3_init_dist_iodev(io_device);
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
BUG_ON(1);
|
||||
}
|
||||
|
@@ -96,7 +96,7 @@ unsigned long vgic_data_mmio_bus_to_host(const void *val, unsigned int len);
|
||||
void vgic_data_host_to_mmio_bus(void *buf, unsigned int len,
|
||||
unsigned long data);
|
||||
|
||||
unsigned long extract_bytes(unsigned long data, unsigned int offset,
|
||||
unsigned long extract_bytes(u64 data, unsigned int offset,
|
||||
unsigned int num);
|
||||
|
||||
u64 update_64bit_reg(u64 reg, unsigned int offset, unsigned int len,
|
||||
@@ -162,12 +162,10 @@ unsigned int vgic_v2_init_dist_iodev(struct vgic_io_device *dev);
|
||||
|
||||
unsigned int vgic_v3_init_dist_iodev(struct vgic_io_device *dev);
|
||||
|
||||
#ifdef CONFIG_KVM_ARM_VGIC_V3
|
||||
u64 vgic_sanitise_outer_cacheability(u64 reg);
|
||||
u64 vgic_sanitise_inner_cacheability(u64 reg);
|
||||
u64 vgic_sanitise_shareability(u64 reg);
|
||||
u64 vgic_sanitise_field(u64 reg, u64 field_mask, int field_shift,
|
||||
u64 (*sanitise_fn)(u64));
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
@@ -278,12 +278,14 @@ int vgic_v2_map_resources(struct kvm *kvm)
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = kvm_phys_addr_ioremap(kvm, dist->vgic_cpu_base,
|
||||
kvm_vgic_global_state.vcpu_base,
|
||||
KVM_VGIC_V2_CPU_SIZE, true);
|
||||
if (ret) {
|
||||
kvm_err("Unable to remap VGIC CPU to VCPU\n");
|
||||
goto out;
|
||||
if (!static_branch_unlikely(&vgic_v2_cpuif_trap)) {
|
||||
ret = kvm_phys_addr_ioremap(kvm, dist->vgic_cpu_base,
|
||||
kvm_vgic_global_state.vcpu_base,
|
||||
KVM_VGIC_V2_CPU_SIZE, true);
|
||||
if (ret) {
|
||||
kvm_err("Unable to remap VGIC CPU to VCPU\n");
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
dist->ready = true;
|
||||
@@ -294,6 +296,8 @@ out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
DEFINE_STATIC_KEY_FALSE(vgic_v2_cpuif_trap);
|
||||
|
||||
/**
|
||||
* vgic_v2_probe - probe for a GICv2 compatible interrupt controller in DT
|
||||
* @node: pointer to the DT node
|
||||
@@ -310,45 +314,51 @@ int vgic_v2_probe(const struct gic_kvm_info *info)
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
if (!PAGE_ALIGNED(info->vcpu.start)) {
|
||||
kvm_err("GICV physical address 0x%llx not page aligned\n",
|
||||
(unsigned long long)info->vcpu.start);
|
||||
return -ENXIO;
|
||||
}
|
||||
if (!PAGE_ALIGNED(info->vcpu.start) ||
|
||||
!PAGE_ALIGNED(resource_size(&info->vcpu))) {
|
||||
kvm_info("GICV region size/alignment is unsafe, using trapping (reduced performance)\n");
|
||||
kvm_vgic_global_state.vcpu_base_va = ioremap(info->vcpu.start,
|
||||
resource_size(&info->vcpu));
|
||||
if (!kvm_vgic_global_state.vcpu_base_va) {
|
||||
kvm_err("Cannot ioremap GICV\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
if (!PAGE_ALIGNED(resource_size(&info->vcpu))) {
|
||||
kvm_err("GICV size 0x%llx not a multiple of page size 0x%lx\n",
|
||||
(unsigned long long)resource_size(&info->vcpu),
|
||||
PAGE_SIZE);
|
||||
return -ENXIO;
|
||||
ret = create_hyp_io_mappings(kvm_vgic_global_state.vcpu_base_va,
|
||||
kvm_vgic_global_state.vcpu_base_va + resource_size(&info->vcpu),
|
||||
info->vcpu.start);
|
||||
if (ret) {
|
||||
kvm_err("Cannot map GICV into hyp\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
static_branch_enable(&vgic_v2_cpuif_trap);
|
||||
}
|
||||
|
||||
kvm_vgic_global_state.vctrl_base = ioremap(info->vctrl.start,
|
||||
resource_size(&info->vctrl));
|
||||
if (!kvm_vgic_global_state.vctrl_base) {
|
||||
kvm_err("Cannot ioremap GICH\n");
|
||||
return -ENOMEM;
|
||||
ret = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
vtr = readl_relaxed(kvm_vgic_global_state.vctrl_base + GICH_VTR);
|
||||
kvm_vgic_global_state.nr_lr = (vtr & 0x3f) + 1;
|
||||
|
||||
ret = kvm_register_vgic_device(KVM_DEV_TYPE_ARM_VGIC_V2);
|
||||
if (ret) {
|
||||
kvm_err("Cannot register GICv2 KVM device\n");
|
||||
iounmap(kvm_vgic_global_state.vctrl_base);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = create_hyp_io_mappings(kvm_vgic_global_state.vctrl_base,
|
||||
kvm_vgic_global_state.vctrl_base +
|
||||
resource_size(&info->vctrl),
|
||||
info->vctrl.start);
|
||||
if (ret) {
|
||||
kvm_err("Cannot map VCTRL into hyp\n");
|
||||
kvm_unregister_device_ops(KVM_DEV_TYPE_ARM_VGIC_V2);
|
||||
iounmap(kvm_vgic_global_state.vctrl_base);
|
||||
return ret;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = kvm_register_vgic_device(KVM_DEV_TYPE_ARM_VGIC_V2);
|
||||
if (ret) {
|
||||
kvm_err("Cannot register GICv2 KVM device\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
kvm_vgic_global_state.can_emulate_gicv2 = true;
|
||||
@@ -359,4 +369,11 @@ int vgic_v2_probe(const struct gic_kvm_info *info)
|
||||
kvm_info("vgic-v2@%llx\n", info->vctrl.start);
|
||||
|
||||
return 0;
|
||||
out:
|
||||
if (kvm_vgic_global_state.vctrl_base)
|
||||
iounmap(kvm_vgic_global_state.vctrl_base);
|
||||
if (kvm_vgic_global_state.vcpu_base_va)
|
||||
iounmap(kvm_vgic_global_state.vcpu_base_va);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
@@ -29,7 +29,7 @@
|
||||
#define DEBUG_SPINLOCK_BUG_ON(p)
|
||||
#endif
|
||||
|
||||
struct vgic_global __section(.hyp.text) kvm_vgic_global_state;
|
||||
struct vgic_global __section(.hyp.text) kvm_vgic_global_state = {.gicv3_cpuif = STATIC_KEY_FALSE_INIT,};
|
||||
|
||||
/*
|
||||
* Locking order is always:
|
||||
@@ -645,6 +645,9 @@ next:
|
||||
/* Sync back the hardware VGIC state into our emulation after a guest's run. */
|
||||
void kvm_vgic_sync_hwstate(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
if (unlikely(!vgic_initialized(vcpu->kvm)))
|
||||
return;
|
||||
|
||||
vgic_process_maintenance_interrupt(vcpu);
|
||||
vgic_fold_lr_state(vcpu);
|
||||
vgic_prune_ap_list(vcpu);
|
||||
@@ -653,6 +656,9 @@ void kvm_vgic_sync_hwstate(struct kvm_vcpu *vcpu)
|
||||
/* Flush our emulation state into the GIC hardware before entering the guest. */
|
||||
void kvm_vgic_flush_hwstate(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
if (unlikely(!vgic_initialized(vcpu->kvm)))
|
||||
return;
|
||||
|
||||
spin_lock(&vcpu->arch.vgic_cpu.ap_list_lock);
|
||||
vgic_flush_lr_state(vcpu);
|
||||
spin_unlock(&vcpu->arch.vgic_cpu.ap_list_lock);
|
||||
|
@@ -72,7 +72,6 @@ static inline void vgic_get_irq_kref(struct vgic_irq *irq)
|
||||
kref_get(&irq->refcount);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_KVM_ARM_VGIC_V3
|
||||
void vgic_v3_process_maintenance(struct kvm_vcpu *vcpu);
|
||||
void vgic_v3_fold_lr_state(struct kvm_vcpu *vcpu);
|
||||
void vgic_v3_populate_lr(struct kvm_vcpu *vcpu, struct vgic_irq *irq, int lr);
|
||||
@@ -84,63 +83,14 @@ void vgic_v3_enable(struct kvm_vcpu *vcpu);
|
||||
int vgic_v3_probe(const struct gic_kvm_info *info);
|
||||
int vgic_v3_map_resources(struct kvm *kvm);
|
||||
int vgic_register_redist_iodevs(struct kvm *kvm, gpa_t dist_base_address);
|
||||
|
||||
#ifdef CONFIG_KVM_ARM_VGIC_V3_ITS
|
||||
int vgic_register_its_iodevs(struct kvm *kvm);
|
||||
bool vgic_has_its(struct kvm *kvm);
|
||||
int kvm_vgic_register_its_device(void);
|
||||
void vgic_enable_lpis(struct kvm_vcpu *vcpu);
|
||||
int vgic_its_inject_msi(struct kvm *kvm, struct kvm_msi *msi);
|
||||
#else
|
||||
static inline void vgic_v3_process_maintenance(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
}
|
||||
|
||||
static inline void vgic_v3_fold_lr_state(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
}
|
||||
|
||||
static inline void vgic_v3_populate_lr(struct kvm_vcpu *vcpu,
|
||||
struct vgic_irq *irq, int lr)
|
||||
{
|
||||
}
|
||||
|
||||
static inline void vgic_v3_clear_lr(struct kvm_vcpu *vcpu, int lr)
|
||||
{
|
||||
}
|
||||
|
||||
static inline void vgic_v3_set_underflow(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
}
|
||||
|
||||
static inline
|
||||
void vgic_v3_set_vmcr(struct kvm_vcpu *vcpu, struct vgic_vmcr *vmcr)
|
||||
{
|
||||
}
|
||||
|
||||
static inline
|
||||
void vgic_v3_get_vmcr(struct kvm_vcpu *vcpu, struct vgic_vmcr *vmcr)
|
||||
{
|
||||
}
|
||||
|
||||
static inline void vgic_v3_enable(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
}
|
||||
|
||||
static inline int vgic_v3_probe(const struct gic_kvm_info *info)
|
||||
{
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
static inline int vgic_v3_map_resources(struct kvm *kvm)
|
||||
{
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
static inline int vgic_register_redist_iodevs(struct kvm *kvm,
|
||||
gpa_t dist_base_address)
|
||||
{
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
static inline int vgic_register_its_iodevs(struct kvm *kvm)
|
||||
{
|
||||
return -ENODEV;
|
||||
|
Reference in New Issue
Block a user