2015-10-19 21:50:37 +07:00
|
|
|
/*
|
|
|
|
* 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.h>
|
|
|
|
#include <linux/kvm_host.h>
|
|
|
|
|
2016-09-06 15:28:46 +07:00
|
|
|
#include <asm/kvm_emulate.h>
|
2016-01-28 20:44:07 +07:00
|
|
|
#include <asm/kvm_hyp.h>
|
2015-10-19 21:50:37 +07:00
|
|
|
|
2016-02-10 00:07:18 +07:00
|
|
|
static void __hyp_text save_elrsr(struct kvm_vcpu *vcpu, void __iomem *base)
|
|
|
|
{
|
|
|
|
struct vgic_v2_cpu_if *cpu_if = &vcpu->arch.vgic_cpu.vgic_v2;
|
2016-05-28 17:27:11 +07:00
|
|
|
int nr_lr = (kern_hyp_va(&kvm_vgic_global_state))->nr_lr;
|
2016-02-10 00:07:18 +07:00
|
|
|
u32 elrsr0, elrsr1;
|
|
|
|
|
|
|
|
elrsr0 = readl_relaxed(base + GICH_ELRSR0);
|
|
|
|
if (unlikely(nr_lr > 32))
|
|
|
|
elrsr1 = readl_relaxed(base + GICH_ELRSR1);
|
|
|
|
else
|
|
|
|
elrsr1 = 0;
|
|
|
|
|
|
|
|
cpu_if->vgic_elrsr = ((u64)elrsr1 << 32) | elrsr0;
|
|
|
|
}
|
|
|
|
|
2016-02-10 00:09:49 +07:00
|
|
|
static void __hyp_text save_lrs(struct kvm_vcpu *vcpu, void __iomem *base)
|
|
|
|
{
|
|
|
|
struct vgic_v2_cpu_if *cpu_if = &vcpu->arch.vgic_cpu.vgic_v2;
|
|
|
|
int i;
|
2016-12-23 06:04:59 +07:00
|
|
|
u64 used_lrs = vcpu->arch.vgic_cpu.used_lrs;
|
2016-02-10 00:09:49 +07:00
|
|
|
|
2016-12-23 06:04:59 +07:00
|
|
|
for (i = 0; i < used_lrs; i++) {
|
2016-05-25 21:26:33 +07:00
|
|
|
if (cpu_if->vgic_elrsr & (1UL << i))
|
2016-02-10 00:09:49 +07:00
|
|
|
cpu_if->vgic_lr[i] &= ~GICH_LR_STATE;
|
2016-05-25 21:26:33 +07:00
|
|
|
else
|
|
|
|
cpu_if->vgic_lr[i] = readl_relaxed(base + GICH_LR0 + (i * 4));
|
2016-02-10 00:09:49 +07:00
|
|
|
|
2016-02-10 00:36:09 +07:00
|
|
|
writel_relaxed(0, base + GICH_LR0 + (i * 4));
|
2016-02-10 00:09:49 +07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-10-19 21:50:37 +07:00
|
|
|
/* vcpu is already in the HYP VA space */
|
|
|
|
void __hyp_text __vgic_v2_save_state(struct kvm_vcpu *vcpu)
|
|
|
|
{
|
|
|
|
struct kvm *kvm = kern_hyp_va(vcpu->kvm);
|
|
|
|
struct vgic_v2_cpu_if *cpu_if = &vcpu->arch.vgic_cpu.vgic_v2;
|
|
|
|
struct vgic_dist *vgic = &kvm->arch.vgic;
|
|
|
|
void __iomem *base = kern_hyp_va(vgic->vctrl_base);
|
2016-12-23 06:04:59 +07:00
|
|
|
u64 used_lrs = vcpu->arch.vgic_cpu.used_lrs;
|
2015-10-19 21:50:37 +07:00
|
|
|
|
|
|
|
if (!base)
|
|
|
|
return;
|
|
|
|
|
2016-12-23 06:04:59 +07:00
|
|
|
if (used_lrs) {
|
2016-02-10 00:07:18 +07:00
|
|
|
cpu_if->vgic_apr = readl_relaxed(base + GICH_APR);
|
2015-10-19 21:50:37 +07:00
|
|
|
|
2016-02-10 00:07:18 +07:00
|
|
|
save_elrsr(vcpu, base);
|
2016-02-10 00:09:49 +07:00
|
|
|
save_lrs(vcpu, base);
|
2015-10-19 21:50:37 +07:00
|
|
|
|
2016-02-03 02:35:34 +07:00
|
|
|
writel_relaxed(0, base + GICH_HCR);
|
|
|
|
} else {
|
|
|
|
cpu_if->vgic_elrsr = ~0UL;
|
|
|
|
cpu_if->vgic_apr = 0;
|
|
|
|
}
|
2015-10-19 21:50:37 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
/* vcpu is already in the HYP VA space */
|
|
|
|
void __hyp_text __vgic_v2_restore_state(struct kvm_vcpu *vcpu)
|
|
|
|
{
|
|
|
|
struct kvm *kvm = kern_hyp_va(vcpu->kvm);
|
|
|
|
struct vgic_v2_cpu_if *cpu_if = &vcpu->arch.vgic_cpu.vgic_v2;
|
|
|
|
struct vgic_dist *vgic = &kvm->arch.vgic;
|
|
|
|
void __iomem *base = kern_hyp_va(vgic->vctrl_base);
|
2016-04-06 19:48:53 +07:00
|
|
|
int i;
|
2016-12-23 06:04:59 +07:00
|
|
|
u64 used_lrs = vcpu->arch.vgic_cpu.used_lrs;
|
2015-10-19 21:50:37 +07:00
|
|
|
|
|
|
|
if (!base)
|
|
|
|
return;
|
|
|
|
|
2016-12-23 06:04:59 +07:00
|
|
|
if (used_lrs) {
|
2016-02-03 02:35:34 +07:00
|
|
|
writel_relaxed(cpu_if->vgic_hcr, base + GICH_HCR);
|
|
|
|
writel_relaxed(cpu_if->vgic_apr, base + GICH_APR);
|
2016-12-23 06:04:59 +07:00
|
|
|
for (i = 0; i < used_lrs; i++) {
|
2016-02-10 00:36:09 +07:00
|
|
|
writel_relaxed(cpu_if->vgic_lr[i],
|
|
|
|
base + GICH_LR0 + (i * 4));
|
2016-02-03 02:35:34 +07:00
|
|
|
}
|
|
|
|
}
|
2015-10-19 21:50:37 +07:00
|
|
|
}
|
arm64: KVM: vgic-v2: Add the GICV emulation infrastructure
In order to efficiently perform the GICV access on behalf of the
guest, we need to be able to avoid going back all the way to
the host kernel.
For this, we introduce a new hook in the world switch code,
conveniently placed just after populating the fault info.
At that point, we only have saved/restored the GP registers,
and we can quickly perform all the required checks (data abort,
translation fault, valid faulting syndrome, not an external
abort, not a PTW).
Coming back from the emulation code, we need to skip the emulated
instruction. This involves an additional bit of save/restore in
order to be able to access the guest's PC (and possibly CPSR if
this is a 32bit guest).
At this stage, no emulation code is provided.
Signed-off-by: Marc Zyngier <marc.zyngier@arm.com>
Reviewed-by: Christoffer Dall <christoffer.dall@linaro.org>
Signed-off-by: Christoffer Dall <christoffer.dall@linaro.org>
2016-09-06 15:28:45 +07:00
|
|
|
|
|
|
|
#ifdef CONFIG_ARM64
|
2016-09-06 20:02:17 +07:00
|
|
|
/*
|
|
|
|
* __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)
|
arm64: KVM: vgic-v2: Add the GICV emulation infrastructure
In order to efficiently perform the GICV access on behalf of the
guest, we need to be able to avoid going back all the way to
the host kernel.
For this, we introduce a new hook in the world switch code,
conveniently placed just after populating the fault info.
At that point, we only have saved/restored the GP registers,
and we can quickly perform all the required checks (data abort,
translation fault, valid faulting syndrome, not an external
abort, not a PTW).
Coming back from the emulation code, we need to skip the emulated
instruction. This involves an additional bit of save/restore in
order to be able to access the guest's PC (and possibly CPSR if
this is a 32bit guest).
At this stage, no emulation code is provided.
Signed-off-by: Marc Zyngier <marc.zyngier@arm.com>
Reviewed-by: Christoffer Dall <christoffer.dall@linaro.org>
Signed-off-by: Christoffer Dall <christoffer.dall@linaro.org>
2016-09-06 15:28:45 +07:00
|
|
|
{
|
2016-09-06 15:28:46 +07:00
|
|
|
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))
|
2016-09-06 20:02:17 +07:00
|
|
|
return 0;
|
2016-09-06 15:28:46 +07:00
|
|
|
|
|
|
|
/* Reject anything but a 32bit access */
|
|
|
|
if (kvm_vcpu_dabt_get_as(vcpu) != sizeof(u32))
|
2016-09-06 20:02:17 +07:00
|
|
|
return -1;
|
2016-09-06 15:28:46 +07:00
|
|
|
|
|
|
|
/* Not aligned? Don't bother */
|
|
|
|
if (fault_ipa & 3)
|
2016-09-06 20:02:17 +07:00
|
|
|
return -1;
|
2016-09-06 15:28:46 +07:00
|
|
|
|
|
|
|
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)));
|
|
|
|
}
|
|
|
|
|
2016-09-06 20:02:17 +07:00
|
|
|
return 1;
|
arm64: KVM: vgic-v2: Add the GICV emulation infrastructure
In order to efficiently perform the GICV access on behalf of the
guest, we need to be able to avoid going back all the way to
the host kernel.
For this, we introduce a new hook in the world switch code,
conveniently placed just after populating the fault info.
At that point, we only have saved/restored the GP registers,
and we can quickly perform all the required checks (data abort,
translation fault, valid faulting syndrome, not an external
abort, not a PTW).
Coming back from the emulation code, we need to skip the emulated
instruction. This involves an additional bit of save/restore in
order to be able to access the guest's PC (and possibly CPSR if
this is a 32bit guest).
At this stage, no emulation code is provided.
Signed-off-by: Marc Zyngier <marc.zyngier@arm.com>
Reviewed-by: Christoffer Dall <christoffer.dall@linaro.org>
Signed-off-by: Christoffer Dall <christoffer.dall@linaro.org>
2016-09-06 15:28:45 +07:00
|
|
|
}
|
|
|
|
#endif
|