arm64: KVM: split GICv2 world switch from hyp code
authorMarc Zyngier <marc.zyngier@arm.com>
Fri, 21 Jun 2013 10:57:56 +0000 (11:57 +0100)
committerChristoffer Dall <christoffer.dall@linaro.org>
Thu, 2 Oct 2014 15:18:55 +0000 (17:18 +0200)
Move the GICv2 world switch code into its own file, and add the
necessary indirection to the arm64 switch code.

Also introduce a new type field to the vgic_params structure.

Acked-by: Catalin Marinas <catalin.marinas@arm.com>
Reviewed-by: Christoffer Dall <christoffer.dall@linaro.org>
Signed-off-by: Marc Zyngier <marc.zyngier@arm.com>
(cherry picked from commit 1a9b13056dde7e3092304d6041ccc60a913042ea)
Signed-off-by: Christoffer Dall <christoffer.dall@linaro.org>
arch/arm/include/asm/kvm_host.h
arch/arm64/include/asm/kvm_asm.h
arch/arm64/include/asm/kvm_host.h
arch/arm64/kernel/asm-offsets.c
arch/arm64/kvm/Makefile
arch/arm64/kvm/hyp.S
arch/arm64/kvm/vgic-v2-switch.S [new file with mode: 0644]
include/kvm/arm_vgic.h
virt/kvm/arm/vgic-v2.c
virt/kvm/arm/vgic.c

index dc4e3edf39cc426633f4f9febc130fc8b301c1de..6dfb404f6c462008bcee3aee58a1bb6a129524b2 100644 (file)
@@ -225,6 +225,11 @@ static inline int kvm_arch_dev_ioctl_check_extension(long ext)
        return 0;
 }
 
+static inline void vgic_arch_setup(const struct vgic_params *vgic)
+{
+       BUG_ON(vgic->type != VGIC_V2);
+}
+
 int kvm_perf_init(void);
 int kvm_perf_teardown(void);
 
index d0bfc4ba82c0d23754dd8a488b7340db03d46788..6252264341c8602a27c3c988c08319b087f3791b 100644 (file)
@@ -105,6 +105,10 @@ extern void __kvm_flush_vm_context(void);
 extern void __kvm_tlb_flush_vmid_ipa(struct kvm *kvm, phys_addr_t ipa);
 
 extern int __kvm_vcpu_run(struct kvm_vcpu *vcpu);
+
+extern char __save_vgic_v2_state[];
+extern char __restore_vgic_v2_state[];
+
 #endif
 
 #endif /* __ARM_KVM_ASM_H__ */
index 92242ce06309c790f60ae1ec3a372ccbc673726e..4c182d0aae701a49ead37aef82179b8d0961e570 100644 (file)
@@ -200,4 +200,25 @@ static inline void __cpu_init_hyp_mode(phys_addr_t boot_pgd_ptr,
                     hyp_stack_ptr, vector_ptr);
 }
 
+struct vgic_sr_vectors {
+       void    *save_vgic;
+       void    *restore_vgic;
+};
+
+static inline void vgic_arch_setup(const struct vgic_params *vgic)
+{
+       extern struct vgic_sr_vectors __vgic_sr_vectors;
+
+       switch(vgic->type)
+       {
+       case VGIC_V2:
+               __vgic_sr_vectors.save_vgic     = __save_vgic_v2_state;
+               __vgic_sr_vectors.restore_vgic  = __restore_vgic_v2_state;
+               break;
+
+       default:
+               BUG();
+       }
+}
+
 #endif /* __ARM64_KVM_HOST_H__ */
index dcfd8a616a94b86322e4fe7dd80ffc689dfffc38..9ff0b2b9739257f2d9fb9e9a2d9696283262835f 100644 (file)
@@ -127,6 +127,9 @@ int main(void)
   DEFINE(KVM_TIMER_ENABLED,    offsetof(struct kvm, arch.timer.enabled));
   DEFINE(VCPU_KVM,             offsetof(struct kvm_vcpu, kvm));
   DEFINE(VCPU_VGIC_CPU,                offsetof(struct kvm_vcpu, arch.vgic_cpu));
+  DEFINE(VGIC_SAVE_FN,         offsetof(struct vgic_sr_vectors, save_vgic));
+  DEFINE(VGIC_RESTORE_FN,      offsetof(struct vgic_sr_vectors, restore_vgic));
+  DEFINE(VGIC_SR_VECTOR_SZ,    sizeof(struct vgic_sr_vectors));
   DEFINE(VGIC_V2_CPU_HCR,      offsetof(struct vgic_cpu, vgic_v2.vgic_hcr));
   DEFINE(VGIC_V2_CPU_VMCR,     offsetof(struct vgic_cpu, vgic_v2.vgic_vmcr));
   DEFINE(VGIC_V2_CPU_MISR,     offsetof(struct vgic_cpu, vgic_v2.vgic_misr));
index 7e92952d139efa8cc67d892d85e8b81ed1813e40..daf24dc59e2c7eb3ff6630f89c1991d73a2f882f 100644 (file)
@@ -19,5 +19,7 @@ kvm-$(CONFIG_KVM_ARM_HOST) += emulate.o inject_fault.o regmap.o
 kvm-$(CONFIG_KVM_ARM_HOST) += hyp.o hyp-init.o handle_exit.o
 kvm-$(CONFIG_KVM_ARM_HOST) += guest.o reset.o sys_regs.o sys_regs_generic_v8.o
 
-kvm-$(CONFIG_KVM_ARM_VGIC) += $(KVM)/arm/vgic.o $(KVM)/arm/vgic-v2.o
+kvm-$(CONFIG_KVM_ARM_VGIC) += $(KVM)/arm/vgic.o
+kvm-$(CONFIG_KVM_ARM_VGIC) += $(KVM)/arm/vgic-v2.o
+kvm-$(CONFIG_KVM_ARM_VGIC) += vgic-v2-switch.o
 kvm-$(CONFIG_KVM_ARM_TIMER) += $(KVM)/arm/arch_timer.o
index 9c5d0acb36540f93b3e954010eeb03ba7b2aa038..56df9a352a811e5a59227831e3b3015668d3a2b7 100644 (file)
@@ -16,7 +16,6 @@
  */
 
 #include <linux/linkage.h>
-#include <linux/irqchip/arm-gic.h>
 
 #include <asm/assembler.h>
 #include <asm/memory.h>
 .endm
 
 /*
- * Save the VGIC CPU state into memory
- * x0: Register pointing to VCPU struct
- * Do not corrupt x1!!!
+ * Call into the vgic backend for state saving
  */
 .macro save_vgic_state
-       /* Get VGIC VCTRL base into x2 */
-       ldr     x2, [x0, #VCPU_KVM]
-       kern_hyp_va     x2
-       ldr     x2, [x2, #KVM_VGIC_VCTRL]
-       kern_hyp_va     x2
-       cbz     x2, 2f          // disabled
-
-       /* Compute the address of struct vgic_cpu */
-       add     x3, x0, #VCPU_VGIC_CPU
-
-       /* Save all interesting registers */
-       ldr     w4, [x2, #GICH_HCR]
-       ldr     w5, [x2, #GICH_VMCR]
-       ldr     w6, [x2, #GICH_MISR]
-       ldr     w7, [x2, #GICH_EISR0]
-       ldr     w8, [x2, #GICH_EISR1]
-       ldr     w9, [x2, #GICH_ELRSR0]
-       ldr     w10, [x2, #GICH_ELRSR1]
-       ldr     w11, [x2, #GICH_APR]
-CPU_BE(        rev     w4,  w4  )
-CPU_BE(        rev     w5,  w5  )
-CPU_BE(        rev     w6,  w6  )
-CPU_BE(        rev     w7,  w7  )
-CPU_BE(        rev     w8,  w8  )
-CPU_BE(        rev     w9,  w9  )
-CPU_BE(        rev     w10, w10 )
-CPU_BE(        rev     w11, w11 )
-
-       str     w4, [x3, #VGIC_V2_CPU_HCR]
-       str     w5, [x3, #VGIC_V2_CPU_VMCR]
-       str     w6, [x3, #VGIC_V2_CPU_MISR]
-       str     w7, [x3, #VGIC_V2_CPU_EISR]
-       str     w8, [x3, #(VGIC_V2_CPU_EISR + 4)]
-       str     w9, [x3, #VGIC_V2_CPU_ELRSR]
-       str     w10, [x3, #(VGIC_V2_CPU_ELRSR + 4)]
-       str     w11, [x3, #VGIC_V2_CPU_APR]
-
-       /* Clear GICH_HCR */
-       str     wzr, [x2, #GICH_HCR]
-
-       /* Save list registers */
-       add     x2, x2, #GICH_LR0
-       ldr     w4, [x3, #VGIC_CPU_NR_LR]
-       add     x3, x3, #VGIC_V2_CPU_LR
-1:     ldr     w5, [x2], #4
-CPU_BE(        rev     w5, w5 )
-       str     w5, [x3], #4
-       sub     w4, w4, #1
-       cbnz    w4, 1b
-2:
+       adr     x24, __vgic_sr_vectors
+       ldr     x24, [x24, VGIC_SAVE_FN]
+       kern_hyp_va     x24
+       blr     x24
 .endm
 
 /*
- * Restore the VGIC CPU state from memory
- * x0: Register pointing to VCPU struct
+ * Call into the vgic backend for state restoring
  */
 .macro restore_vgic_state
-       /* Get VGIC VCTRL base into x2 */
-       ldr     x2, [x0, #VCPU_KVM]
-       kern_hyp_va     x2
-       ldr     x2, [x2, #KVM_VGIC_VCTRL]
-       kern_hyp_va     x2
-       cbz     x2, 2f          // disabled
-
-       /* Compute the address of struct vgic_cpu */
-       add     x3, x0, #VCPU_VGIC_CPU
-
-       /* We only restore a minimal set of registers */
-       ldr     w4, [x3, #VGIC_V2_CPU_HCR]
-       ldr     w5, [x3, #VGIC_V2_CPU_VMCR]
-       ldr     w6, [x3, #VGIC_V2_CPU_APR]
-CPU_BE(        rev     w4, w4 )
-CPU_BE(        rev     w5, w5 )
-CPU_BE(        rev     w6, w6 )
-
-       str     w4, [x2, #GICH_HCR]
-       str     w5, [x2, #GICH_VMCR]
-       str     w6, [x2, #GICH_APR]
-
-       /* Restore list registers */
-       add     x2, x2, #GICH_LR0
-       ldr     w4, [x3, #VGIC_CPU_NR_LR]
-       add     x3, x3, #VGIC_V2_CPU_LR
-1:     ldr     w5, [x3], #4
-CPU_BE(        rev     w5, w5 )
-       str     w5, [x2], #4
-       sub     w4, w4, #1
-       cbnz    w4, 1b
-2:
+       adr     x24, __vgic_sr_vectors
+       ldr     x24, [x24, #VGIC_RESTORE_FN]
+       kern_hyp_va     x24
+       blr     x24
 .endm
 
 .macro save_timer_state
@@ -650,6 +572,12 @@ ENTRY(__kvm_flush_vm_context)
        ret
 ENDPROC(__kvm_flush_vm_context)
 
+       // struct vgic_sr_vectors __vgi_sr_vectors;
+       .align 3
+ENTRY(__vgic_sr_vectors)
+       .skip   VGIC_SR_VECTOR_SZ
+ENDPROC(__vgic_sr_vectors)
+
 __kvm_hyp_panic:
        // Guess the context by looking at VTTBR:
        // If zero, then we're already a host.
diff --git a/arch/arm64/kvm/vgic-v2-switch.S b/arch/arm64/kvm/vgic-v2-switch.S
new file mode 100644 (file)
index 0000000..ae21177
--- /dev/null
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 2012,2013 - 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/linkage.h>
+#include <linux/irqchip/arm-gic.h>
+
+#include <asm/assembler.h>
+#include <asm/memory.h>
+#include <asm/asm-offsets.h>
+#include <asm/kvm.h>
+#include <asm/kvm_asm.h>
+#include <asm/kvm_arm.h>
+#include <asm/kvm_mmu.h>
+
+       .text
+       .pushsection    .hyp.text, "ax"
+
+/*
+ * Save the VGIC CPU state into memory
+ * x0: Register pointing to VCPU struct
+ * Do not corrupt x1!!!
+ */
+ENTRY(__save_vgic_v2_state)
+__save_vgic_v2_state:
+       /* Get VGIC VCTRL base into x2 */
+       ldr     x2, [x0, #VCPU_KVM]
+       kern_hyp_va     x2
+       ldr     x2, [x2, #KVM_VGIC_VCTRL]
+       kern_hyp_va     x2
+       cbz     x2, 2f          // disabled
+
+       /* Compute the address of struct vgic_cpu */
+       add     x3, x0, #VCPU_VGIC_CPU
+
+       /* Save all interesting registers */
+       ldr     w4, [x2, #GICH_HCR]
+       ldr     w5, [x2, #GICH_VMCR]
+       ldr     w6, [x2, #GICH_MISR]
+       ldr     w7, [x2, #GICH_EISR0]
+       ldr     w8, [x2, #GICH_EISR1]
+       ldr     w9, [x2, #GICH_ELRSR0]
+       ldr     w10, [x2, #GICH_ELRSR1]
+       ldr     w11, [x2, #GICH_APR]
+CPU_BE(        rev     w4,  w4  )
+CPU_BE(        rev     w5,  w5  )
+CPU_BE(        rev     w6,  w6  )
+CPU_BE(        rev     w7,  w7  )
+CPU_BE(        rev     w8,  w8  )
+CPU_BE(        rev     w9,  w9  )
+CPU_BE(        rev     w10, w10 )
+CPU_BE(        rev     w11, w11 )
+
+       str     w4, [x3, #VGIC_V2_CPU_HCR]
+       str     w5, [x3, #VGIC_V2_CPU_VMCR]
+       str     w6, [x3, #VGIC_V2_CPU_MISR]
+       str     w7, [x3, #VGIC_V2_CPU_EISR]
+       str     w8, [x3, #(VGIC_V2_CPU_EISR + 4)]
+       str     w9, [x3, #VGIC_V2_CPU_ELRSR]
+       str     w10, [x3, #(VGIC_V2_CPU_ELRSR + 4)]
+       str     w11, [x3, #VGIC_V2_CPU_APR]
+
+       /* Clear GICH_HCR */
+       str     wzr, [x2, #GICH_HCR]
+
+       /* Save list registers */
+       add     x2, x2, #GICH_LR0
+       ldr     w4, [x3, #VGIC_CPU_NR_LR]
+       add     x3, x3, #VGIC_V2_CPU_LR
+1:     ldr     w5, [x2], #4
+CPU_BE(        rev     w5, w5 )
+       str     w5, [x3], #4
+       sub     w4, w4, #1
+       cbnz    w4, 1b
+2:
+       ret
+ENDPROC(__save_vgic_v2_state)
+
+/*
+ * Restore the VGIC CPU state from memory
+ * x0: Register pointing to VCPU struct
+ */
+ENTRY(__restore_vgic_v2_state)
+__restore_vgic_v2_state:
+       /* Get VGIC VCTRL base into x2 */
+       ldr     x2, [x0, #VCPU_KVM]
+       kern_hyp_va     x2
+       ldr     x2, [x2, #KVM_VGIC_VCTRL]
+       kern_hyp_va     x2
+       cbz     x2, 2f          // disabled
+
+       /* Compute the address of struct vgic_cpu */
+       add     x3, x0, #VCPU_VGIC_CPU
+
+       /* We only restore a minimal set of registers */
+       ldr     w4, [x3, #VGIC_V2_CPU_HCR]
+       ldr     w5, [x3, #VGIC_V2_CPU_VMCR]
+       ldr     w6, [x3, #VGIC_V2_CPU_APR]
+CPU_BE(        rev     w4, w4 )
+CPU_BE(        rev     w5, w5 )
+CPU_BE(        rev     w6, w6 )
+
+       str     w4, [x2, #GICH_HCR]
+       str     w5, [x2, #GICH_VMCR]
+       str     w6, [x2, #GICH_APR]
+
+       /* Restore list registers */
+       add     x2, x2, #GICH_LR0
+       ldr     w4, [x3, #VGIC_CPU_NR_LR]
+       add     x3, x3, #VGIC_V2_CPU_LR
+1:     ldr     w5, [x3], #4
+CPU_BE(        rev     w5, w5 )
+       str     w5, [x2], #4
+       sub     w4, w4, #1
+       cbnz    w4, 1b
+2:
+       ret
+ENDPROC(__restore_vgic_v2_state)
+
+       .popsection
index f6b9fec6fcac61a5c59aca3d240899335c41c2d7..65f1121a3bebc17817384c550a95d1396425567c 100644 (file)
@@ -24,7 +24,6 @@
 #include <linux/irqreturn.h>
 #include <linux/spinlock.h>
 #include <linux/types.h>
-#include <linux/irqchip/arm-gic.h>
 
 #define VGIC_NR_IRQS           256
 #define VGIC_NR_SGIS           16
@@ -71,6 +70,10 @@ struct vgic_bytemap {
 
 struct kvm_vcpu;
 
+enum vgic_type {
+       VGIC_V2,                /* Good ol' GICv2 */
+};
+
 #define LR_STATE_PENDING       (1 << 0)
 #define LR_STATE_ACTIVE                (1 << 1)
 #define LR_STATE_MASK          (3 << 0)
@@ -104,6 +107,8 @@ struct vgic_ops {
 };
 
 struct vgic_params {
+       /* vgic type */
+       enum vgic_type  type;
        /* Physical address of vgic virtual cpu interface */
        phys_addr_t     vcpu_base;
        /* Number of list registers */
index 940418ebd0d058038826549414cc8ed5a33ce5b9..d6c9c142f8138ee9a29b51e7ebd3ec515a672a3f 100644 (file)
@@ -236,6 +236,7 @@ int vgic_v2_probe(struct device_node *vgic_node,
        kvm_info("%s@%llx IRQ%d\n", vgic_node->name,
                 vctrl_res.start, vgic->maint_irq);
 
+       vgic->type = VGIC_V2;
        *ops = &vgic_v2_ops;
        *params = vgic;
        goto out;
index 1348e74c4d110daa53d95f48a5a27d77061b325f..7867b9a1f6943a1ee6c4bd3383a29bbd00b1cbaa 100644 (file)
@@ -1568,6 +1568,9 @@ int kvm_vgic_hyp_init(void)
 
        on_each_cpu(vgic_init_maintenance_interrupt, NULL, 1);
 
+       /* Callback into for arch code for setup */
+       vgic_arch_setup(vgic);
+
        return 0;
 
 out_free_irq: