]> asedeno.scripts.mit.edu Git - linux.git/commitdiff
arm64: KVM: vgic-v2: Add the GICV emulation infrastructure
authorMarc Zyngier <marc.zyngier@arm.com>
Tue, 6 Sep 2016 08:28:45 +0000 (09:28 +0100)
committerChristoffer Dall <christoffer.dall@linaro.org>
Thu, 8 Sep 2016 10:53:00 +0000 (12:53 +0200)
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>
arch/arm64/include/asm/kvm_hyp.h
arch/arm64/kvm/hyp/switch.c
include/kvm/arm_vgic.h
virt/kvm/arm/hyp/vgic-v2-sr.c
virt/kvm/arm/vgic/vgic-v2.c

index cff510574fae03f210bb1ef947bcaa5c4dd694a6..88ec3ac35a3217526d397852e915f8a255e46e93 100644 (file)
@@ -123,6 +123,7 @@ typeof(orig) * __hyp_text fname(void)                                       \
 
 void __vgic_v2_save_state(struct kvm_vcpu *vcpu);
 void __vgic_v2_restore_state(struct kvm_vcpu *vcpu);
+bool __vgic_v2_perform_cpuif_access(struct kvm_vcpu *vcpu);
 
 void __vgic_v3_save_state(struct kvm_vcpu *vcpu);
 void __vgic_v3_restore_state(struct kvm_vcpu *vcpu);
index 35d2e09ac695d4d0e4266f1d649c00d6f51369b3..b3a66c50d12b98970b23533e9237633ae6acea0c 100644 (file)
@@ -17,6 +17,7 @@
 
 #include <linux/types.h>
 #include <asm/kvm_asm.h>
+#include <asm/kvm_emulate.h>
 #include <asm/kvm_hyp.h>
 
 static bool __hyp_text __fpsimd_enabled_nvhe(void)
@@ -232,6 +233,21 @@ static bool __hyp_text __populate_fault_info(struct kvm_vcpu *vcpu)
        return true;
 }
 
+static void __hyp_text __skip_instr(struct kvm_vcpu *vcpu)
+{
+       *vcpu_pc(vcpu) = read_sysreg_el2(elr);
+
+       if (vcpu_mode_is_32bit(vcpu)) {
+               vcpu->arch.ctxt.gp_regs.regs.pstate = read_sysreg_el2(spsr);
+               kvm_skip_instr32(vcpu, kvm_vcpu_trap_il_is32bit(vcpu));
+               write_sysreg_el2(vcpu->arch.ctxt.gp_regs.regs.pstate, spsr);
+       } else {
+               *vcpu_pc(vcpu) += 4;
+       }
+
+       write_sysreg_el2(*vcpu_pc(vcpu), elr);
+}
+
 int __hyp_text __kvm_vcpu_run(struct kvm_vcpu *vcpu)
 {
        struct kvm_cpu_context *host_ctxt;
@@ -270,6 +286,22 @@ int __hyp_text __kvm_vcpu_run(struct kvm_vcpu *vcpu)
        if (exit_code == ARM_EXCEPTION_TRAP && !__populate_fault_info(vcpu))
                goto again;
 
+       if (static_branch_unlikely(&vgic_v2_cpuif_trap) &&
+           exit_code == ARM_EXCEPTION_TRAP) {
+               bool valid;
+
+               valid = kvm_vcpu_trap_get_class(vcpu) == ESR_ELx_EC_DABT_LOW &&
+                       kvm_vcpu_trap_get_fault_type(vcpu) == FSC_FAULT &&
+                       kvm_vcpu_dabt_isvalid(vcpu) &&
+                       !kvm_vcpu_dabt_isextabt(vcpu) &&
+                       !kvm_vcpu_dabt_iss1tw(vcpu);
+
+               if (valid && __vgic_v2_perform_cpuif_access(vcpu)) {
+                       __skip_instr(vcpu);
+                       goto again;
+               }
+       }
+
        fp_enabled = __fpsimd_enabled();
 
        __sysreg_save_guest_state(guest_ctxt);
index 19b698ef3336ffc947b4c01b62e026c779eb5af3..8eb1501fc1d0eac85096591d2088ae2192c8bb0c 100644 (file)
@@ -20,6 +20,7 @@
 #include <linux/kvm.h>
 #include <linux/irqreturn.h>
 #include <linux/spinlock.h>
+#include <linux/static_key.h>
 #include <linux/types.h>
 #include <kvm/iodev.h>
 #include <linux/list.h>
@@ -265,6 +266,8 @@ struct vgic_cpu {
        bool lpis_enabled;
 };
 
+extern struct static_key_false vgic_v2_cpuif_trap;
+
 int kvm_vgic_addr(struct kvm *kvm, unsigned long type, u64 *addr, bool write);
 void kvm_vgic_early_init(struct kvm *kvm);
 int kvm_vgic_create(struct kvm *kvm, u32 type);
index 7cffd9338c494b5959426457c7ac2bd4aa8ccb36..3e2a62e27ebac5672ed5a79f199110d1723171a0 100644 (file)
@@ -167,3 +167,10 @@ 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
+bool __hyp_text __vgic_v2_perform_cpuif_access(struct kvm_vcpu *vcpu)
+{
+               return false;
+}
+#endif
index 0bf6709d1006c4bc5d148d4a3164d31b0ac96ecd..b8da9011889d9b80b59726f4f87b1f516694ff6a 100644 (file)
@@ -294,6 +294,8 @@ int vgic_v2_map_resources(struct kvm *kvm)
        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