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);
 
 
 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__ */
 
                     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__ */
 
   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));
 
 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
 
  */
 
 #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
        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.
 
--- /dev/null
+/*
+ * 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
 
 #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
 
 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)
 };
 
 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 */
 
        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;
 
 
        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: