Merge tag 'kvm-arm-for-3.17' of git://git.kernel.org/pub/scm/linux/kernel/git/kvmarm...
authorPaolo Bonzini <pbonzini@redhat.com>
Tue, 5 Aug 2014 07:47:45 +0000 (09:47 +0200)
committerPaolo Bonzini <pbonzini@redhat.com>
Tue, 5 Aug 2014 07:47:45 +0000 (09:47 +0200)
KVM/ARM New features for 3.17 include:
 - Fixes and code refactoring for stage2 kvm MMU unmap_range
 - Support unmapping IPAs on deleting memslots for arm and arm64
 - Support MMIO mappings in stage2 faults
 - KVM VGIC v2 emulation on GICv3 hardware
 - Big-Endian support for arm/arm64 (guest and host)
 - Debug Architecture support for arm64 (arm32 is on Christoffer's todo list)

Conflicts:
virt/kvm/arm/vgic.c [last minute cherry-pick from 3.17 to 3.16]

39 files changed:
Documentation/arm64/booting.txt
Documentation/devicetree/bindings/arm/gic-v3.txt [new file with mode: 0644]
arch/arm/include/asm/kvm_asm.h
arch/arm/include/asm/kvm_emulate.h
arch/arm/include/asm/kvm_host.h
arch/arm/include/asm/kvm_mmu.h
arch/arm/kernel/asm-offsets.c
arch/arm/kernel/hyp-stub.S
arch/arm/kvm/Kconfig
arch/arm/kvm/Makefile
arch/arm/kvm/arm.c
arch/arm/kvm/coproc.c
arch/arm/kvm/guest.c
arch/arm/kvm/init.S
arch/arm/kvm/interrupts.S
arch/arm/kvm/interrupts_head.S
arch/arm/kvm/mmu.c
arch/arm64/include/asm/debug-monitors.h
arch/arm64/include/asm/kvm_arm.h
arch/arm64/include/asm/kvm_asm.h
arch/arm64/include/asm/kvm_coproc.h
arch/arm64/include/asm/kvm_emulate.h
arch/arm64/include/asm/kvm_host.h
arch/arm64/include/asm/kvm_mmu.h
arch/arm64/include/asm/virt.h
arch/arm64/kernel/asm-offsets.c
arch/arm64/kernel/debug-monitors.c
arch/arm64/kvm/Makefile
arch/arm64/kvm/guest.c
arch/arm64/kvm/handle_exit.c
arch/arm64/kvm/hyp.S
arch/arm64/kvm/sys_regs.c
arch/arm64/kvm/vgic-v2-switch.S [new file with mode: 0644]
arch/arm64/kvm/vgic-v3-switch.S [new file with mode: 0644]
include/kvm/arm_arch_timer.h
include/kvm/arm_vgic.h
virt/kvm/arm/vgic-v2.c [new file with mode: 0644]
virt/kvm/arm/vgic-v3.c [new file with mode: 0644]
virt/kvm/arm/vgic.c

index 85af34d..f3c05b5 100644 (file)
@@ -168,6 +168,14 @@ Before jumping into the kernel, the following conditions must be met:
   the kernel image will be entered must be initialised by software at a
   higher exception level to prevent execution in an UNKNOWN state.
 
+  For systems with a GICv3 interrupt controller:
+  - If EL3 is present:
+    ICC_SRE_EL3.Enable (bit 3) must be initialiased to 0b1.
+    ICC_SRE_EL3.SRE (bit 0) must be initialised to 0b1.
+  - If the kernel is entered at EL1:
+    ICC.SRE_EL2.Enable (bit 3) must be initialised to 0b1
+    ICC_SRE_EL2.SRE (bit 0) must be initialised to 0b1.
+
 The requirements described above for CPU mode, caches, MMUs, architected
 timers, coherency and system registers apply to all CPUs.  All CPUs must
 enter the kernel in the same exception level.
diff --git a/Documentation/devicetree/bindings/arm/gic-v3.txt b/Documentation/devicetree/bindings/arm/gic-v3.txt
new file mode 100644 (file)
index 0000000..33cd05e
--- /dev/null
@@ -0,0 +1,79 @@
+* ARM Generic Interrupt Controller, version 3
+
+AArch64 SMP cores are often associated with a GICv3, providing Private
+Peripheral Interrupts (PPI), Shared Peripheral Interrupts (SPI),
+Software Generated Interrupts (SGI), and Locality-specific Peripheral
+Interrupts (LPI).
+
+Main node required properties:
+
+- compatible : should at least contain  "arm,gic-v3".
+- interrupt-controller : Identifies the node as an interrupt controller
+- #interrupt-cells : Specifies the number of cells needed to encode an
+  interrupt source. Must be a single cell with a value of at least 3.
+
+  The 1st cell is the interrupt type; 0 for SPI interrupts, 1 for PPI
+  interrupts. Other values are reserved for future use.
+
+  The 2nd cell contains the interrupt number for the interrupt type.
+  SPI interrupts are in the range [0-987]. PPI interrupts are in the
+  range [0-15].
+
+  The 3rd cell is the flags, encoded as follows:
+       bits[3:0] trigger type and level flags.
+               1 = edge triggered
+               4 = level triggered
+
+  Cells 4 and beyond are reserved for future use. When the 1st cell
+  has a value of 0 or 1, cells 4 and beyond act as padding, and may be
+  ignored. It is recommended that padding cells have a value of 0.
+
+- reg : Specifies base physical address(s) and size of the GIC
+  registers, in the following order:
+  - GIC Distributor interface (GICD)
+  - GIC Redistributors (GICR), one range per redistributor region
+  - GIC CPU interface (GICC)
+  - GIC Hypervisor interface (GICH)
+  - GIC Virtual CPU interface (GICV)
+
+  GICC, GICH and GICV are optional.
+
+- interrupts : Interrupt source of the VGIC maintenance interrupt.
+
+Optional
+
+- redistributor-stride : If using padding pages, specifies the stride
+  of consecutive redistributors. Must be a multiple of 64kB.
+
+- #redistributor-regions: The number of independent contiguous regions
+  occupied by the redistributors. Required if more than one such
+  region is present.
+
+Examples:
+
+       gic: interrupt-controller@2cf00000 {
+               compatible = "arm,gic-v3";
+               #interrupt-cells = <3>;
+               interrupt-controller;
+               reg = <0x0 0x2f000000 0 0x10000>,       // GICD
+                     <0x0 0x2f100000 0 0x200000>,      // GICR
+                     <0x0 0x2c000000 0 0x2000>,        // GICC
+                     <0x0 0x2c010000 0 0x2000>,        // GICH
+                     <0x0 0x2c020000 0 0x2000>;        // GICV
+               interrupts = <1 9 4>;
+       };
+
+       gic: interrupt-controller@2c010000 {
+               compatible = "arm,gic-v3";
+               #interrupt-cells = <3>;
+               interrupt-controller;
+               redistributor-stride = <0x0 0x40000>;   // 256kB stride
+               #redistributor-regions = <2>;
+               reg = <0x0 0x2c010000 0 0x10000>,       // GICD
+                     <0x0 0x2d000000 0 0x800000>,      // GICR 1: CPUs 0-31
+                     <0x0 0x2e000000 0 0x800000>;      // GICR 2: CPUs 32-63
+                     <0x0 0x2c040000 0 0x2000>,        // GICC
+                     <0x0 0x2c060000 0 0x2000>,        // GICH
+                     <0x0 0x2c080000 0 0x2000>;        // GICV
+               interrupts = <1 9 4>;
+       };
index 53b3c4a..3a67bec 100644 (file)
 #define ARM_EXCEPTION_FIQ        6
 #define ARM_EXCEPTION_HVC        7
 
+/*
+ * The rr_lo_hi macro swaps a pair of registers depending on
+ * current endianness. It is used in conjunction with ldrd and strd
+ * instructions that load/store a 64-bit value from/to memory to/from
+ * a pair of registers which are used with the mrrc and mcrr instructions.
+ * If used with the ldrd/strd instructions, the a1 parameter is the first
+ * source/destination register and the a2 parameter is the second
+ * source/destination register. Note that the ldrd/strd instructions
+ * already swap the bytes within the words correctly according to the
+ * endianness setting, but the order of the registers need to be effectively
+ * swapped when used with the mrrc/mcrr instructions.
+ */
+#ifdef CONFIG_CPU_ENDIAN_BE8
+#define rr_lo_hi(a1, a2) a2, a1
+#else
+#define rr_lo_hi(a1, a2) a1, a2
+#endif
+
 #ifndef __ASSEMBLY__
 struct kvm;
 struct kvm_vcpu;
index 0fa90c9..69b7469 100644 (file)
@@ -185,9 +185,16 @@ static inline unsigned long vcpu_data_guest_to_host(struct kvm_vcpu *vcpu,
                default:
                        return be32_to_cpu(data);
                }
+       } else {
+               switch (len) {
+               case 1:
+                       return data & 0xff;
+               case 2:
+                       return le16_to_cpu(data & 0xffff);
+               default:
+                       return le32_to_cpu(data);
+               }
        }
-
-       return data;            /* Leave LE untouched */
 }
 
 static inline unsigned long vcpu_data_host_to_guest(struct kvm_vcpu *vcpu,
@@ -203,9 +210,16 @@ static inline unsigned long vcpu_data_host_to_guest(struct kvm_vcpu *vcpu,
                default:
                        return cpu_to_be32(data);
                }
+       } else {
+               switch (len) {
+               case 1:
+                       return data & 0xff;
+               case 2:
+                       return cpu_to_le16(data & 0xffff);
+               default:
+                       return cpu_to_le32(data);
+               }
        }
-
-       return data;            /* Leave LE untouched */
 }
 
 #endif /* __ARM_KVM_EMULATE_H__ */
index 193ceaf..6dfb404 100644 (file)
@@ -225,10 +225,12 @@ 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);
 
-u64 kvm_arm_timer_get_reg(struct kvm_vcpu *, u64 regid);
-int kvm_arm_timer_set_reg(struct kvm_vcpu *, u64 regid, u64 value);
-
 #endif /* __ARM_KVM_HOST_H__ */
index 5c7aa3c..5cc0b0f 100644 (file)
@@ -127,6 +127,18 @@ static inline void kvm_set_s2pmd_writable(pmd_t *pmd)
        (__boundary - 1 < (end) - 1)? __boundary: (end);                \
 })
 
+static inline bool kvm_page_empty(void *ptr)
+{
+       struct page *ptr_page = virt_to_page(ptr);
+       return page_count(ptr_page) == 1;
+}
+
+
+#define kvm_pte_table_empty(ptep) kvm_page_empty(ptep)
+#define kvm_pmd_table_empty(pmdp) kvm_page_empty(pmdp)
+#define kvm_pud_table_empty(pudp) (0)
+
+
 struct kvm;
 
 #define kvm_flush_dcache_to_poc(a,l)   __cpuc_flush_dcache_area((a), (l))
index 85598b5..713e807 100644 (file)
@@ -182,13 +182,13 @@ int main(void)
   DEFINE(VCPU_HYP_PC,          offsetof(struct kvm_vcpu, arch.fault.hyp_pc));
 #ifdef CONFIG_KVM_ARM_VGIC
   DEFINE(VCPU_VGIC_CPU,                offsetof(struct kvm_vcpu, arch.vgic_cpu));
-  DEFINE(VGIC_CPU_HCR,         offsetof(struct vgic_cpu, vgic_hcr));
-  DEFINE(VGIC_CPU_VMCR,                offsetof(struct vgic_cpu, vgic_vmcr));
-  DEFINE(VGIC_CPU_MISR,                offsetof(struct vgic_cpu, vgic_misr));
-  DEFINE(VGIC_CPU_EISR,                offsetof(struct vgic_cpu, vgic_eisr));
-  DEFINE(VGIC_CPU_ELRSR,       offsetof(struct vgic_cpu, vgic_elrsr));
-  DEFINE(VGIC_CPU_APR,         offsetof(struct vgic_cpu, vgic_apr));
-  DEFINE(VGIC_CPU_LR,          offsetof(struct vgic_cpu, vgic_lr));
+  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));
+  DEFINE(VGIC_V2_CPU_EISR,     offsetof(struct vgic_cpu, vgic_v2.vgic_eisr));
+  DEFINE(VGIC_V2_CPU_ELRSR,    offsetof(struct vgic_cpu, vgic_v2.vgic_elrsr));
+  DEFINE(VGIC_V2_CPU_APR,      offsetof(struct vgic_cpu, vgic_v2.vgic_apr));
+  DEFINE(VGIC_V2_CPU_LR,       offsetof(struct vgic_cpu, vgic_v2.vgic_lr));
   DEFINE(VGIC_CPU_NR_LR,       offsetof(struct vgic_cpu, nr_lr));
 #ifdef CONFIG_KVM_ARM_TIMER
   DEFINE(VCPU_TIMER_CNTV_CTL,  offsetof(struct kvm_vcpu, arch.timer_cpu.cntv_ctl));
index 797b1a6..7e666cf 100644 (file)
@@ -134,9 +134,7 @@ ENTRY(__hyp_stub_install_secondary)
        mcr     p15, 4, r7, c1, c1, 3   @ HSTR
 
 THUMB( orr     r7, #(1 << 30)  )       @ HSCTLR.TE
-#ifdef CONFIG_CPU_BIG_ENDIAN
-       orr     r7, #(1 << 9)           @ HSCTLR.EE
-#endif
+ARM_BE8(orr    r7, r7, #(1 << 25))     @ HSCTLR.EE
        mcr     p15, 4, r7, c1, c0, 0   @ HSCTLR
 
        mrc     p15, 4, r7, c1, c1, 1   @ HDCR
index 4be5bb1..466bd29 100644 (file)
@@ -23,7 +23,7 @@ config KVM
        select HAVE_KVM_CPU_RELAX_INTERCEPT
        select KVM_MMIO
        select KVM_ARM_HOST
-       depends on ARM_VIRT_EXT && ARM_LPAE && !CPU_BIG_ENDIAN
+       depends on ARM_VIRT_EXT && ARM_LPAE
        ---help---
          Support hosting virtualized guest machines. You will also
          need to select one or more of the processor modules below.
index 789bca9..f7057ed 100644 (file)
@@ -21,4 +21,5 @@ obj-y += kvm-arm.o init.o interrupts.o
 obj-y += arm.o handle_exit.o guest.o mmu.o emulate.o reset.o
 obj-y += coproc.o coproc_a15.o coproc_a7.o mmio.o psci.o perf.o
 obj-$(CONFIG_KVM_ARM_VGIC) += $(KVM)/arm/vgic.o
+obj-$(CONFIG_KVM_ARM_VGIC) += $(KVM)/arm/vgic-v2.o
 obj-$(CONFIG_KVM_ARM_TIMER) += $(KVM)/arm/arch_timer.o
index 3c82b37..d7424ef 100644 (file)
@@ -155,16 +155,6 @@ int kvm_arch_vcpu_fault(struct kvm_vcpu *vcpu, struct vm_fault *vmf)
        return VM_FAULT_SIGBUS;
 }
 
-void kvm_arch_free_memslot(struct kvm *kvm, struct kvm_memory_slot *free,
-                          struct kvm_memory_slot *dont)
-{
-}
-
-int kvm_arch_create_memslot(struct kvm *kvm, struct kvm_memory_slot *slot,
-                           unsigned long npages)
-{
-       return 0;
-}
 
 /**
  * kvm_arch_destroy_vm - destroy the VM data structure
@@ -225,33 +215,6 @@ long kvm_arch_dev_ioctl(struct file *filp,
        return -EINVAL;
 }
 
-void kvm_arch_memslots_updated(struct kvm *kvm)
-{
-}
-
-int kvm_arch_prepare_memory_region(struct kvm *kvm,
-                                  struct kvm_memory_slot *memslot,
-                                  struct kvm_userspace_memory_region *mem,
-                                  enum kvm_mr_change change)
-{
-       return 0;
-}
-
-void kvm_arch_commit_memory_region(struct kvm *kvm,
-                                  struct kvm_userspace_memory_region *mem,
-                                  const struct kvm_memory_slot *old,
-                                  enum kvm_mr_change change)
-{
-}
-
-void kvm_arch_flush_shadow_all(struct kvm *kvm)
-{
-}
-
-void kvm_arch_flush_shadow_memslot(struct kvm *kvm,
-                                  struct kvm_memory_slot *slot)
-{
-}
 
 struct kvm_vcpu *kvm_arch_vcpu_create(struct kvm *kvm, unsigned int id)
 {
index c58a351..37a0fe1 100644 (file)
@@ -44,6 +44,31 @@ static u32 cache_levels;
 /* CSSELR values; used to index KVM_REG_ARM_DEMUX_ID_CCSIDR */
 #define CSSELR_MAX 12
 
+/*
+ * kvm_vcpu_arch.cp15 holds cp15 registers as an array of u32, but some
+ * of cp15 registers can be viewed either as couple of two u32 registers
+ * or one u64 register. Current u64 register encoding is that least
+ * significant u32 word is followed by most significant u32 word.
+ */
+static inline void vcpu_cp15_reg64_set(struct kvm_vcpu *vcpu,
+                                      const struct coproc_reg *r,
+                                      u64 val)
+{
+       vcpu->arch.cp15[r->reg] = val & 0xffffffff;
+       vcpu->arch.cp15[r->reg + 1] = val >> 32;
+}
+
+static inline u64 vcpu_cp15_reg64_get(struct kvm_vcpu *vcpu,
+                                     const struct coproc_reg *r)
+{
+       u64 val;
+
+       val = vcpu->arch.cp15[r->reg + 1];
+       val = val << 32;
+       val = val | vcpu->arch.cp15[r->reg];
+       return val;
+}
+
 int kvm_handle_cp10_id(struct kvm_vcpu *vcpu, struct kvm_run *run)
 {
        kvm_inject_undefined(vcpu);
@@ -682,17 +707,23 @@ static struct coproc_reg invariant_cp15[] = {
        { CRn( 0), CRm( 0), Op1( 1), Op2( 7), is32, NULL, get_AIDR },
 };
 
+/*
+ * Reads a register value from a userspace address to a kernel
+ * variable. Make sure that register size matches sizeof(*__val).
+ */
 static int reg_from_user(void *val, const void __user *uaddr, u64 id)
 {
-       /* This Just Works because we are little endian. */
        if (copy_from_user(val, uaddr, KVM_REG_SIZE(id)) != 0)
                return -EFAULT;
        return 0;
 }
 
+/*
+ * Writes a register value to a userspace address from a kernel variable.
+ * Make sure that register size matches sizeof(*__val).
+ */
 static int reg_to_user(void __user *uaddr, const void *val, u64 id)
 {
-       /* This Just Works because we are little endian. */
        if (copy_to_user(uaddr, val, KVM_REG_SIZE(id)) != 0)
                return -EFAULT;
        return 0;
@@ -702,6 +733,7 @@ static int get_invariant_cp15(u64 id, void __user *uaddr)
 {
        struct coproc_params params;
        const struct coproc_reg *r;
+       int ret;
 
        if (!index_to_params(id, &params))
                return -ENOENT;
@@ -710,7 +742,15 @@ static int get_invariant_cp15(u64 id, void __user *uaddr)
        if (!r)
                return -ENOENT;
 
-       return reg_to_user(uaddr, &r->val, id);
+       ret = -ENOENT;
+       if (KVM_REG_SIZE(id) == 4) {
+               u32 val = r->val;
+
+               ret = reg_to_user(uaddr, &val, id);
+       } else if (KVM_REG_SIZE(id) == 8) {
+               ret = reg_to_user(uaddr, &r->val, id);
+       }
+       return ret;
 }
 
 static int set_invariant_cp15(u64 id, void __user *uaddr)
@@ -718,7 +758,7 @@ static int set_invariant_cp15(u64 id, void __user *uaddr)
        struct coproc_params params;
        const struct coproc_reg *r;
        int err;
-       u64 val = 0; /* Make sure high bits are 0 for 32-bit regs */
+       u64 val;
 
        if (!index_to_params(id, &params))
                return -ENOENT;
@@ -726,7 +766,16 @@ static int set_invariant_cp15(u64 id, void __user *uaddr)
        if (!r)
                return -ENOENT;
 
-       err = reg_from_user(&val, uaddr, id);
+       err = -ENOENT;
+       if (KVM_REG_SIZE(id) == 4) {
+               u32 val32;
+
+               err = reg_from_user(&val32, uaddr, id);
+               if (!err)
+                       val = val32;
+       } else if (KVM_REG_SIZE(id) == 8) {
+               err = reg_from_user(&val, uaddr, id);
+       }
        if (err)
                return err;
 
@@ -1004,6 +1053,7 @@ int kvm_arm_coproc_get_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
 {
        const struct coproc_reg *r;
        void __user *uaddr = (void __user *)(long)reg->addr;
+       int ret;
 
        if ((reg->id & KVM_REG_ARM_COPROC_MASK) == KVM_REG_ARM_DEMUX)
                return demux_c15_get(reg->id, uaddr);
@@ -1015,14 +1065,24 @@ int kvm_arm_coproc_get_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
        if (!r)
                return get_invariant_cp15(reg->id, uaddr);
 
-       /* Note: copies two regs if size is 64 bit. */
-       return reg_to_user(uaddr, &vcpu->arch.cp15[r->reg], reg->id);
+       ret = -ENOENT;
+       if (KVM_REG_SIZE(reg->id) == 8) {
+               u64 val;
+
+               val = vcpu_cp15_reg64_get(vcpu, r);
+               ret = reg_to_user(uaddr, &val, reg->id);
+       } else if (KVM_REG_SIZE(reg->id) == 4) {
+               ret = reg_to_user(uaddr, &vcpu->arch.cp15[r->reg], reg->id);
+       }
+
+       return ret;
 }
 
 int kvm_arm_coproc_set_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
 {
        const struct coproc_reg *r;
        void __user *uaddr = (void __user *)(long)reg->addr;
+       int ret;
 
        if ((reg->id & KVM_REG_ARM_COPROC_MASK) == KVM_REG_ARM_DEMUX)
                return demux_c15_set(reg->id, uaddr);
@@ -1034,8 +1094,18 @@ int kvm_arm_coproc_set_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
        if (!r)
                return set_invariant_cp15(reg->id, uaddr);
 
-       /* Note: copies two regs if size is 64 bit */
-       return reg_from_user(&vcpu->arch.cp15[r->reg], uaddr, reg->id);
+       ret = -ENOENT;
+       if (KVM_REG_SIZE(reg->id) == 8) {
+               u64 val;
+
+               ret = reg_from_user(&val, uaddr, reg->id);
+               if (!ret)
+                       vcpu_cp15_reg64_set(vcpu, r, val);
+       } else if (KVM_REG_SIZE(reg->id) == 4) {
+               ret = reg_from_user(&vcpu->arch.cp15[r->reg], uaddr, reg->id);
+       }
+
+       return ret;
 }
 
 static unsigned int num_demux_regs(void)
index b23a59c..986e625 100644 (file)
@@ -124,16 +124,6 @@ static bool is_timer_reg(u64 index)
        return false;
 }
 
-int kvm_arm_timer_set_reg(struct kvm_vcpu *vcpu, u64 regid, u64 value)
-{
-       return 0;
-}
-
-u64 kvm_arm_timer_get_reg(struct kvm_vcpu *vcpu, u64 regid)
-{
-       return 0;
-}
-
 #else
 
 #define NUM_TIMER_REGS 3
index 1b9844d..2cc14df 100644 (file)
@@ -71,7 +71,7 @@ __do_hyp_init:
        bne     phase2                  @ Yes, second stage init
 
        @ Set the HTTBR to point to the hypervisor PGD pointer passed
-       mcrr    p15, 4, r2, r3, c2
+       mcrr    p15, 4, rr_lo_hi(r2, r3), c2
 
        @ Set the HTCR and VTCR to the same shareability and cacheability
        @ settings as the non-secure TTBCR and with T0SZ == 0.
@@ -137,7 +137,7 @@ phase2:
        mov     pc, r0
 
 target:        @ We're now in the trampoline code, switch page tables
-       mcrr    p15, 4, r2, r3, c2
+       mcrr    p15, 4, rr_lo_hi(r2, r3), c2
        isb
 
        @ Invalidate the old TLBs
index 0d68d40..01dcb0e 100644 (file)
@@ -52,7 +52,7 @@ ENTRY(__kvm_tlb_flush_vmid_ipa)
        dsb     ishst
        add     r0, r0, #KVM_VTTBR
        ldrd    r2, r3, [r0]
-       mcrr    p15, 6, r2, r3, c2      @ Write VTTBR
+       mcrr    p15, 6, rr_lo_hi(r2, r3), c2    @ Write VTTBR
        isb
        mcr     p15, 0, r0, c8, c3, 0   @ TLBIALLIS (rt ignored)
        dsb     ish
@@ -135,7 +135,7 @@ ENTRY(__kvm_vcpu_run)
        ldr     r1, [vcpu, #VCPU_KVM]
        add     r1, r1, #KVM_VTTBR
        ldrd    r2, r3, [r1]
-       mcrr    p15, 6, r2, r3, c2      @ Write VTTBR
+       mcrr    p15, 6, rr_lo_hi(r2, r3), c2    @ Write VTTBR
 
        @ We're all done, just restore the GPRs and go to the guest
        restore_guest_regs
@@ -199,8 +199,13 @@ after_vfp_restore:
 
        restore_host_regs
        clrex                           @ Clear exclusive monitor
+#ifndef CONFIG_CPU_ENDIAN_BE8
        mov     r0, r1                  @ Return the return code
        mov     r1, #0                  @ Clear upper bits in return value
+#else
+       @ r1 already has return code
+       mov     r0, #0                  @ Clear upper bits in return value
+#endif /* CONFIG_CPU_ENDIAN_BE8 */
        bx      lr                      @ return to IOCTL
 
 /********************************************************************
index 76af930..98c8c5b 100644 (file)
@@ -1,4 +1,5 @@
 #include <linux/irqchip/arm-gic.h>
+#include <asm/assembler.h>
 
 #define VCPU_USR_REG(_reg_nr)  (VCPU_USR_REGS + (_reg_nr * 4))
 #define VCPU_USR_SP            (VCPU_USR_REG(13))
@@ -420,15 +421,23 @@ vcpu      .req    r0              @ vcpu pointer always in r0
        ldr     r8, [r2, #GICH_ELRSR0]
        ldr     r9, [r2, #GICH_ELRSR1]
        ldr     r10, [r2, #GICH_APR]
-
-       str     r3, [r11, #VGIC_CPU_HCR]
-       str     r4, [r11, #VGIC_CPU_VMCR]
-       str     r5, [r11, #VGIC_CPU_MISR]
-       str     r6, [r11, #VGIC_CPU_EISR]
-       str     r7, [r11, #(VGIC_CPU_EISR + 4)]
-       str     r8, [r11, #VGIC_CPU_ELRSR]
-       str     r9, [r11, #(VGIC_CPU_ELRSR + 4)]
-       str     r10, [r11, #VGIC_CPU_APR]
+ARM_BE8(rev    r3, r3  )
+ARM_BE8(rev    r4, r4  )
+ARM_BE8(rev    r5, r5  )
+ARM_BE8(rev    r6, r6  )
+ARM_BE8(rev    r7, r7  )
+ARM_BE8(rev    r8, r8  )
+ARM_BE8(rev    r9, r9  )
+ARM_BE8(rev    r10, r10        )
+
+       str     r3, [r11, #VGIC_V2_CPU_HCR]
+       str     r4, [r11, #VGIC_V2_CPU_VMCR]
+       str     r5, [r11, #VGIC_V2_CPU_MISR]
+       str     r6, [r11, #VGIC_V2_CPU_EISR]
+       str     r7, [r11, #(VGIC_V2_CPU_EISR + 4)]
+       str     r8, [r11, #VGIC_V2_CPU_ELRSR]
+       str     r9, [r11, #(VGIC_V2_CPU_ELRSR + 4)]
+       str     r10, [r11, #VGIC_V2_CPU_APR]
 
        /* Clear GICH_HCR */
        mov     r5, #0
@@ -436,9 +445,10 @@ vcpu       .req    r0              @ vcpu pointer always in r0
 
        /* Save list registers */
        add     r2, r2, #GICH_LR0
-       add     r3, r11, #VGIC_CPU_LR
+       add     r3, r11, #VGIC_V2_CPU_LR
        ldr     r4, [r11, #VGIC_CPU_NR_LR]
 1:     ldr     r6, [r2], #4
+ARM_BE8(rev    r6, r6  )
        str     r6, [r3], #4
        subs    r4, r4, #1
        bne     1b
@@ -463,9 +473,12 @@ vcpu       .req    r0              @ vcpu pointer always in r0
        add     r11, vcpu, #VCPU_VGIC_CPU
 
        /* We only restore a minimal set of registers */
-       ldr     r3, [r11, #VGIC_CPU_HCR]
-       ldr     r4, [r11, #VGIC_CPU_VMCR]
-       ldr     r8, [r11, #VGIC_CPU_APR]
+       ldr     r3, [r11, #VGIC_V2_CPU_HCR]
+       ldr     r4, [r11, #VGIC_V2_CPU_VMCR]
+       ldr     r8, [r11, #VGIC_V2_CPU_APR]
+ARM_BE8(rev    r3, r3  )
+ARM_BE8(rev    r4, r4  )
+ARM_BE8(rev    r8, r8  )
 
        str     r3, [r2, #GICH_HCR]
        str     r4, [r2, #GICH_VMCR]
@@ -473,9 +486,10 @@ vcpu       .req    r0              @ vcpu pointer always in r0
 
        /* Restore list registers */
        add     r2, r2, #GICH_LR0
-       add     r3, r11, #VGIC_CPU_LR
+       add     r3, r11, #VGIC_V2_CPU_LR
        ldr     r4, [r11, #VGIC_CPU_NR_LR]
 1:     ldr     r6, [r3], #4
+ARM_BE8(rev    r6, r6  )
        str     r6, [r2], #4
        subs    r4, r4, #1
        bne     1b
@@ -506,7 +520,7 @@ vcpu        .req    r0              @ vcpu pointer always in r0
        mcr     p15, 0, r2, c14, c3, 1  @ CNTV_CTL
        isb
 
-       mrrc    p15, 3, r2, r3, c14     @ CNTV_CVAL
+       mrrc    p15, 3, rr_lo_hi(r2, r3), c14   @ CNTV_CVAL
        ldr     r4, =VCPU_TIMER_CNTV_CVAL
        add     r5, vcpu, r4
        strd    r2, r3, [r5]
@@ -546,12 +560,12 @@ vcpu      .req    r0              @ vcpu pointer always in r0
 
        ldr     r2, [r4, #KVM_TIMER_CNTVOFF]
        ldr     r3, [r4, #(KVM_TIMER_CNTVOFF + 4)]
-       mcrr    p15, 4, r2, r3, c14     @ CNTVOFF
+       mcrr    p15, 4, rr_lo_hi(r2, r3), c14   @ CNTVOFF
 
        ldr     r4, =VCPU_TIMER_CNTV_CVAL
        add     r5, vcpu, r4
        ldrd    r2, r3, [r5]
-       mcrr    p15, 3, r2, r3, c14     @ CNTV_CVAL
+       mcrr    p15, 3, rr_lo_hi(r2, r3), c14   @ CNTV_CVAL
        isb
 
        ldr     r2, [vcpu, #VCPU_TIMER_CNTV_CTL]
index 16f8049..16e7994 100644 (file)
@@ -90,104 +90,115 @@ static void *mmu_memory_cache_alloc(struct kvm_mmu_memory_cache *mc)
        return p;
 }
 
-static bool page_empty(void *ptr)
+static void clear_pgd_entry(struct kvm *kvm, pgd_t *pgd, phys_addr_t addr)
 {
-       struct page *ptr_page = virt_to_page(ptr);
-       return page_count(ptr_page) == 1;
+       pud_t *pud_table __maybe_unused = pud_offset(pgd, 0);
+       pgd_clear(pgd);
+       kvm_tlb_flush_vmid_ipa(kvm, addr);
+       pud_free(NULL, pud_table);
+       put_page(virt_to_page(pgd));
 }
 
 static void clear_pud_entry(struct kvm *kvm, pud_t *pud, phys_addr_t addr)
 {
-       if (pud_huge(*pud)) {
-               pud_clear(pud);
-               kvm_tlb_flush_vmid_ipa(kvm, addr);
-       } else {
-               pmd_t *pmd_table = pmd_offset(pud, 0);
-               pud_clear(pud);
-               kvm_tlb_flush_vmid_ipa(kvm, addr);
-               pmd_free(NULL, pmd_table);
-       }
+       pmd_t *pmd_table = pmd_offset(pud, 0);
+       VM_BUG_ON(pud_huge(*pud));
+       pud_clear(pud);
+       kvm_tlb_flush_vmid_ipa(kvm, addr);
+       pmd_free(NULL, pmd_table);
        put_page(virt_to_page(pud));
 }
 
 static void clear_pmd_entry(struct kvm *kvm, pmd_t *pmd, phys_addr_t addr)
 {
-       if (kvm_pmd_huge(*pmd)) {
-               pmd_clear(pmd);
-               kvm_tlb_flush_vmid_ipa(kvm, addr);
-       } else {
-               pte_t *pte_table = pte_offset_kernel(pmd, 0);
-               pmd_clear(pmd);
-               kvm_tlb_flush_vmid_ipa(kvm, addr);
-               pte_free_kernel(NULL, pte_table);
-       }
+       pte_t *pte_table = pte_offset_kernel(pmd, 0);
+       VM_BUG_ON(kvm_pmd_huge(*pmd));
+       pmd_clear(pmd);
+       kvm_tlb_flush_vmid_ipa(kvm, addr);
+       pte_free_kernel(NULL, pte_table);
        put_page(virt_to_page(pmd));
 }
 
-static void clear_pte_entry(struct kvm *kvm, pte_t *pte, phys_addr_t addr)
+static void unmap_ptes(struct kvm *kvm, pmd_t *pmd,
+                      phys_addr_t addr, phys_addr_t end)
 {
-       if (pte_present(*pte)) {
-               kvm_set_pte(pte, __pte(0));
-               put_page(virt_to_page(pte));
-               kvm_tlb_flush_vmid_ipa(kvm, addr);
-       }
+       phys_addr_t start_addr = addr;
+       pte_t *pte, *start_pte;
+
+       start_pte = pte = pte_offset_kernel(pmd, addr);
+       do {
+               if (!pte_none(*pte)) {
+                       kvm_set_pte(pte, __pte(0));
+                       put_page(virt_to_page(pte));
+                       kvm_tlb_flush_vmid_ipa(kvm, addr);
+               }
+       } while (pte++, addr += PAGE_SIZE, addr != end);
+
+       if (kvm_pte_table_empty(start_pte))
+               clear_pmd_entry(kvm, pmd, start_addr);
 }
 
-static void unmap_range(struct kvm *kvm, pgd_t *pgdp,
-                       unsigned long long start, u64 size)
+static void unmap_pmds(struct kvm *kvm, pud_t *pud,
+                      phys_addr_t addr, phys_addr_t end)
 {
-       pgd_t *pgd;
-       pud_t *pud;
-       pmd_t *pmd;
-       pte_t *pte;
-       unsigned long long addr = start, end = start + size;
-       u64 next;
-
-       while (addr < end) {
-               pgd = pgdp + pgd_index(addr);
-               pud = pud_offset(pgd, addr);
-               pte = NULL;
-               if (pud_none(*pud)) {
-                       addr = kvm_pud_addr_end(addr, end);
-                       continue;
-               }
+       phys_addr_t next, start_addr = addr;
+       pmd_t *pmd, *start_pmd;
 
-               if (pud_huge(*pud)) {
-                       /*
-                        * If we are dealing with a huge pud, just clear it and
-                        * move on.
-                        */
-                       clear_pud_entry(kvm, pud, addr);
-                       addr = kvm_pud_addr_end(addr, end);
-                       continue;
+       start_pmd = pmd = pmd_offset(pud, addr);
+       do {
+               next = kvm_pmd_addr_end(addr, end);
+               if (!pmd_none(*pmd)) {
+                       if (kvm_pmd_huge(*pmd)) {
+                               pmd_clear(pmd);
+                               kvm_tlb_flush_vmid_ipa(kvm, addr);
+                               put_page(virt_to_page(pmd));
+                       } else {
+                               unmap_ptes(kvm, pmd, addr, next);
+                       }
                }
+       } while (pmd++, addr = next, addr != end);
 
-               pmd = pmd_offset(pud, addr);
-               if (pmd_none(*pmd)) {
-                       addr = kvm_pmd_addr_end(addr, end);
-                       continue;
-               }
+       if (kvm_pmd_table_empty(start_pmd))
+               clear_pud_entry(kvm, pud, start_addr);
+}
 
-               if (!kvm_pmd_huge(*pmd)) {
-                       pte = pte_offset_kernel(pmd, addr);
-                       clear_pte_entry(kvm, pte, addr);
-                       next = addr + PAGE_SIZE;
-               }
+static void unmap_puds(struct kvm *kvm, pgd_t *pgd,
+                      phys_addr_t addr, phys_addr_t end)
+{
+       phys_addr_t next, start_addr = addr;
+       pud_t *pud, *start_pud;
 
-               /*
-                * If the pmd entry is to be cleared, walk back up the ladder
-                */
-               if (kvm_pmd_huge(*pmd) || (pte && page_empty(pte))) {
-                       clear_pmd_entry(kvm, pmd, addr);
-                       next = kvm_pmd_addr_end(addr, end);
-                       if (page_empty(pmd) && !page_empty(pud)) {
-                               clear_pud_entry(kvm, pud, addr);
-                               next = kvm_pud_addr_end(addr, end);
+       start_pud = pud = pud_offset(pgd, addr);
+       do {
+               next = kvm_pud_addr_end(addr, end);
+               if (!pud_none(*pud)) {
+                       if (pud_huge(*pud)) {
+                               pud_clear(pud);
+                               kvm_tlb_flush_vmid_ipa(kvm, addr);
+                               put_page(virt_to_page(pud));
+                       } else {
+                               unmap_pmds(kvm, pud, addr, next);
                        }
                }
+       } while (pud++, addr = next, addr != end);
 
-               addr = next;
-       }
+       if (kvm_pud_table_empty(start_pud))
+               clear_pgd_entry(kvm, pgd, start_addr);
+}
+
+
+static void unmap_range(struct kvm *kvm, pgd_t *pgdp,
+                       phys_addr_t start, u64 size)
+{
+       pgd_t *pgd;
+       phys_addr_t addr = start, end = start + size;
+       phys_addr_t next;
+
+       pgd = pgdp + pgd_index(addr);
+       do {
+               next = kvm_pgd_addr_end(addr, end);
+               unmap_puds(kvm, pgd, addr, next);
+       } while (pgd++, addr = next, addr != end);
 }
 
 static void stage2_flush_ptes(struct kvm *kvm, pmd_t *pmd,
@@ -748,6 +759,7 @@ static int user_mem_abort(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa,
        struct kvm_mmu_memory_cache *memcache = &vcpu->arch.mmu_page_cache;
        struct vm_area_struct *vma;
        pfn_t pfn;
+       pgprot_t mem_type = PAGE_S2;
 
        write_fault = kvm_is_write_fault(kvm_vcpu_get_hsr(vcpu));
        if (fault_status == FSC_PERM && !write_fault) {
@@ -798,6 +810,9 @@ static int user_mem_abort(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa,
        if (is_error_pfn(pfn))
                return -EFAULT;
 
+       if (kvm_is_mmio_pfn(pfn))
+               mem_type = PAGE_S2_DEVICE;
+
        spin_lock(&kvm->mmu_lock);
        if (mmu_notifier_retry(kvm, mmu_seq))
                goto out_unlock;
@@ -805,7 +820,7 @@ static int user_mem_abort(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa,
                hugetlb = transparent_hugepage_adjust(&pfn, &fault_ipa);
 
        if (hugetlb) {
-               pmd_t new_pmd = pfn_pmd(pfn, PAGE_S2);
+               pmd_t new_pmd = pfn_pmd(pfn, mem_type);
                new_pmd = pmd_mkhuge(new_pmd);
                if (writable) {
                        kvm_set_s2pmd_writable(&new_pmd);
@@ -814,13 +829,14 @@ static int user_mem_abort(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa,
                coherent_cache_guest_page(vcpu, hva & PMD_MASK, PMD_SIZE);
                ret = stage2_set_pmd_huge(kvm, memcache, fault_ipa, &new_pmd);
        } else {
-               pte_t new_pte = pfn_pte(pfn, PAGE_S2);
+               pte_t new_pte = pfn_pte(pfn, mem_type);
                if (writable) {
                        kvm_set_s2pte_writable(&new_pte);
                        kvm_set_pfn_dirty(pfn);
                }
                coherent_cache_guest_page(vcpu, hva, PAGE_SIZE);
-               ret = stage2_set_pte(kvm, memcache, fault_ipa, &new_pte, false);
+               ret = stage2_set_pte(kvm, memcache, fault_ipa, &new_pte,
+                                    mem_type == PAGE_S2_DEVICE);
        }
 
 
@@ -1100,3 +1116,49 @@ out:
        free_hyp_pgds();
        return err;
 }
+
+void kvm_arch_commit_memory_region(struct kvm *kvm,
+                                  struct kvm_userspace_memory_region *mem,
+                                  const struct kvm_memory_slot *old,
+                                  enum kvm_mr_change change)
+{
+       gpa_t gpa = old->base_gfn << PAGE_SHIFT;
+       phys_addr_t size = old->npages << PAGE_SHIFT;
+       if (change == KVM_MR_DELETE || change == KVM_MR_MOVE) {
+               spin_lock(&kvm->mmu_lock);
+               unmap_stage2_range(kvm, gpa, size);
+               spin_unlock(&kvm->mmu_lock);
+       }
+}
+
+int kvm_arch_prepare_memory_region(struct kvm *kvm,
+                                  struct kvm_memory_slot *memslot,
+                                  struct kvm_userspace_memory_region *mem,
+                                  enum kvm_mr_change change)
+{
+       return 0;
+}
+
+void kvm_arch_free_memslot(struct kvm *kvm, struct kvm_memory_slot *free,
+                          struct kvm_memory_slot *dont)
+{
+}
+
+int kvm_arch_create_memslot(struct kvm *kvm, struct kvm_memory_slot *slot,
+                           unsigned long npages)
+{
+       return 0;
+}
+
+void kvm_arch_memslots_updated(struct kvm *kvm)
+{
+}
+
+void kvm_arch_flush_shadow_all(struct kvm *kvm)
+{
+}
+
+void kvm_arch_flush_shadow_memslot(struct kvm *kvm,
+                                  struct kvm_memory_slot *slot)
+{
+}
index 6e9b5b3..7fb3437 100644 (file)
 
 #ifdef __KERNEL__
 
+/* Low-level stepping controls. */
+#define DBG_MDSCR_SS           (1 << 0)
+#define DBG_SPSR_SS            (1 << 21)
+
+/* MDSCR_EL1 enabling bits */
+#define DBG_MDSCR_KDE          (1 << 13)
+#define DBG_MDSCR_MDE          (1 << 15)
+#define DBG_MDSCR_MASK         ~(DBG_MDSCR_KDE | DBG_MDSCR_MDE)
+
 #define        DBG_ESR_EVT(x)          (((x) >> 27) & 0x7)
 
 /* AArch64 */
 
 #define CACHE_FLUSH_IS_SAFE            1
 
-enum debug_el {
-       DBG_ACTIVE_EL0 = 0,
-       DBG_ACTIVE_EL1,
-};
-
 /* AArch32 */
 #define DBG_ESR_EVT_BKPT       0x4
 #define DBG_ESR_EVT_VECC       0x5
@@ -115,6 +119,11 @@ void unregister_break_hook(struct break_hook *hook);
 
 u8 debug_monitors_arch(void);
 
+enum debug_el {
+       DBG_ACTIVE_EL0 = 0,
+       DBG_ACTIVE_EL1,
+};
+
 void enable_debug_monitors(enum debug_el el);
 void disable_debug_monitors(enum debug_el el);
 
index 3d69030..cc83520 100644 (file)
  */
 #define HCR_GUEST_FLAGS (HCR_TSC | HCR_TSW | HCR_TWE | HCR_TWI | HCR_VM | \
                         HCR_TVM | HCR_BSU_IS | HCR_FB | HCR_TAC | \
-                        HCR_AMO | HCR_IMO | HCR_FMO | \
-                        HCR_SWIO | HCR_TIDCP | HCR_RW)
+                        HCR_AMO | HCR_SWIO | HCR_TIDCP | HCR_RW)
 #define HCR_VIRT_EXCP_MASK (HCR_VA | HCR_VI | HCR_VF)
+#define HCR_INT_OVERRIDE   (HCR_FMO | HCR_IMO)
+
 
 /* Hyp System Control Register (SCTLR_EL2) bits */
 #define SCTLR_EL2_EE   (1 << 25)
index 9fcd54b..4838421 100644 (file)
@@ -18,6 +18,8 @@
 #ifndef __ARM_KVM_ASM_H__
 #define __ARM_KVM_ASM_H__
 
+#include <asm/virt.h>
+
 /*
  * 0 is reserved as an invalid value.
  * Order *must* be kept in sync with the hyp switch code.
 #define        AMAIR_EL1       19      /* Aux Memory Attribute Indirection Register */
 #define        CNTKCTL_EL1     20      /* Timer Control Register (EL1) */
 #define        PAR_EL1         21      /* Physical Address Register */
+#define MDSCR_EL1      22      /* Monitor Debug System Control Register */
+#define DBGBCR0_EL1    23      /* Debug Breakpoint Control Registers (0-15) */
+#define DBGBCR15_EL1   38
+#define DBGBVR0_EL1    39      /* Debug Breakpoint Value Registers (0-15) */
+#define DBGBVR15_EL1   54
+#define DBGWCR0_EL1    55      /* Debug Watchpoint Control Registers (0-15) */
+#define DBGWCR15_EL1   70
+#define DBGWVR0_EL1    71      /* Debug Watchpoint Value Registers (0-15) */
+#define DBGWVR15_EL1   86
+#define MDCCINT_EL1    87      /* Monitor Debug Comms Channel Interrupt Enable Reg */
+
 /* 32bit specific registers. Keep them at the end of the range */
-#define        DACR32_EL2      22      /* Domain Access Control Register */
-#define        IFSR32_EL2      23      /* Instruction Fault Status Register */
-#define        FPEXC32_EL2     24      /* Floating-Point Exception Control Register */
-#define        DBGVCR32_EL2    25      /* Debug Vector Catch Register */
-#define        TEECR32_EL1     26      /* ThumbEE Configuration Register */
-#define        TEEHBR32_EL1    27      /* ThumbEE Handler Base Register */
-#define        NR_SYS_REGS     28
+#define        DACR32_EL2      88      /* Domain Access Control Register */
+#define        IFSR32_EL2      89      /* Instruction Fault Status Register */
+#define        FPEXC32_EL2     90      /* Floating-Point Exception Control Register */
+#define        DBGVCR32_EL2    91      /* Debug Vector Catch Register */
+#define        TEECR32_EL1     92      /* ThumbEE Configuration Register */
+#define        TEEHBR32_EL1    93      /* ThumbEE Handler Base Register */
+#define        NR_SYS_REGS     94
 
 /* 32bit mapping */
 #define c0_MPIDR       (MPIDR_EL1 * 2) /* MultiProcessor ID Register */
 #define c10_AMAIR0     (AMAIR_EL1 * 2) /* Aux Memory Attr Indirection Reg */
 #define c10_AMAIR1     (c10_AMAIR0 + 1)/* Aux Memory Attr Indirection Reg */
 #define c14_CNTKCTL    (CNTKCTL_EL1 * 2) /* Timer Control Register (PL1) */
-#define NR_CP15_REGS   (NR_SYS_REGS * 2)
+
+#define cp14_DBGDSCRext        (MDSCR_EL1 * 2)
+#define cp14_DBGBCR0   (DBGBCR0_EL1 * 2)
+#define cp14_DBGBVR0   (DBGBVR0_EL1 * 2)
+#define cp14_DBGBXVR0  (cp14_DBGBVR0 + 1)
+#define cp14_DBGWCR0   (DBGWCR0_EL1 * 2)
+#define cp14_DBGWVR0   (DBGWVR0_EL1 * 2)
+#define cp14_DBGDCCINT (MDCCINT_EL1 * 2)
+
+#define NR_COPRO_REGS  (NR_SYS_REGS * 2)
 
 #define ARM_EXCEPTION_IRQ        0
 #define ARM_EXCEPTION_TRAP       1
 
+#define KVM_ARM64_DEBUG_DIRTY_SHIFT    0
+#define KVM_ARM64_DEBUG_DIRTY          (1 << KVM_ARM64_DEBUG_DIRTY_SHIFT)
+
 #ifndef __ASSEMBLY__
 struct kvm;
 struct kvm_vcpu;
@@ -96,13 +121,21 @@ extern char __kvm_hyp_init_end[];
 
 extern char __kvm_hyp_vector[];
 
-extern char __kvm_hyp_code_start[];
-extern char __kvm_hyp_code_end[];
+#define        __kvm_hyp_code_start    __hyp_text_start
+#define        __kvm_hyp_code_end      __hyp_text_end
 
 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 u64 __vgic_v3_get_ich_vtr_el2(void);
+
+extern char __save_vgic_v2_state[];
+extern char __restore_vgic_v2_state[];
+extern char __save_vgic_v3_state[];
+extern char __restore_vgic_v3_state[];
+
 #endif
 
 #endif /* __ARM_KVM_ASM_H__ */
index 9a59301..0b52377 100644 (file)
@@ -39,7 +39,8 @@ void kvm_register_target_sys_reg_table(unsigned int target,
                                       struct kvm_sys_reg_target_table *table);
 
 int kvm_handle_cp14_load_store(struct kvm_vcpu *vcpu, struct kvm_run *run);
-int kvm_handle_cp14_access(struct kvm_vcpu *vcpu, struct kvm_run *run);
+int kvm_handle_cp14_32(struct kvm_vcpu *vcpu, struct kvm_run *run);
+int kvm_handle_cp14_64(struct kvm_vcpu *vcpu, struct kvm_run *run);
 int kvm_handle_cp15_32(struct kvm_vcpu *vcpu, struct kvm_run *run);
 int kvm_handle_cp15_64(struct kvm_vcpu *vcpu, struct kvm_run *run);
 int kvm_handle_sys_reg(struct kvm_vcpu *vcpu, struct kvm_run *run);
index dd8ecfc..fdc3e21 100644 (file)
@@ -213,6 +213,17 @@ static inline unsigned long vcpu_data_guest_to_host(struct kvm_vcpu *vcpu,
                default:
                        return be64_to_cpu(data);
                }
+       } else {
+               switch (len) {
+               case 1:
+                       return data & 0xff;
+               case 2:
+                       return le16_to_cpu(data & 0xffff);
+               case 4:
+                       return le32_to_cpu(data & 0xffffffff);
+               default:
+                       return le64_to_cpu(data);
+               }
        }
 
        return data;            /* Leave LE untouched */
@@ -233,6 +244,17 @@ static inline unsigned long vcpu_data_host_to_guest(struct kvm_vcpu *vcpu,
                default:
                        return cpu_to_be64(data);
                }
+       } else {
+               switch (len) {
+               case 1:
+                       return data & 0xff;
+               case 2:
+                       return cpu_to_le16(data & 0xffff);
+               case 4:
+                       return cpu_to_le32(data & 0xffffffff);
+               default:
+                       return cpu_to_le64(data);
+               }
        }
 
        return data;            /* Leave LE untouched */
index 92242ce..e10c45a 100644 (file)
@@ -86,7 +86,7 @@ struct kvm_cpu_context {
        struct kvm_regs gp_regs;
        union {
                u64 sys_regs[NR_SYS_REGS];
-               u32 cp15[NR_CP15_REGS];
+               u32 copro[NR_COPRO_REGS];
        };
 };
 
@@ -101,6 +101,9 @@ struct kvm_vcpu_arch {
        /* Exception Information */
        struct kvm_vcpu_fault_info fault;
 
+       /* Debug state */
+       u64 debug_flags;
+
        /* Pointer to host CPU context */
        kvm_cpu_context_t *host_cpu_context;
 
@@ -138,7 +141,20 @@ struct kvm_vcpu_arch {
 
 #define vcpu_gp_regs(v)                (&(v)->arch.ctxt.gp_regs)
 #define vcpu_sys_reg(v,r)      ((v)->arch.ctxt.sys_regs[(r)])
-#define vcpu_cp15(v,r)         ((v)->arch.ctxt.cp15[(r)])
+/*
+ * CP14 and CP15 live in the same array, as they are backed by the
+ * same system registers.
+ */
+#define vcpu_cp14(v,r)         ((v)->arch.ctxt.copro[(r)])
+#define vcpu_cp15(v,r)         ((v)->arch.ctxt.copro[(r)])
+
+#ifdef CONFIG_CPU_BIG_ENDIAN
+#define vcpu_cp15_64_high(v,r) vcpu_cp15((v),(r))
+#define vcpu_cp15_64_low(v,r)  vcpu_cp15((v),(r) + 1)
+#else
+#define vcpu_cp15_64_high(v,r) vcpu_cp15((v),(r) + 1)
+#define vcpu_cp15_64_low(v,r)  vcpu_cp15((v),(r))
+#endif
 
 struct kvm_vm_stat {
        u32 remote_tlb_flush;
@@ -200,4 +216,32 @@ 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;
+
+#ifdef CONFIG_ARM_GIC_V3
+       case VGIC_V3:
+               __vgic_sr_vectors.save_vgic     = __save_vgic_v3_state;
+               __vgic_sr_vectors.restore_vgic  = __restore_vgic_v3_state;
+               break;
+#endif
+
+       default:
+               BUG();
+       }
+}
+
 #endif /* __ARM64_KVM_HOST_H__ */
index 7d29847..8e138c7 100644 (file)
@@ -125,6 +125,21 @@ static inline void kvm_set_s2pmd_writable(pmd_t *pmd)
 #define kvm_pud_addr_end(addr, end)    pud_addr_end(addr, end)
 #define kvm_pmd_addr_end(addr, end)    pmd_addr_end(addr, end)
 
+static inline bool kvm_page_empty(void *ptr)
+{
+       struct page *ptr_page = virt_to_page(ptr);
+       return page_count(ptr_page) == 1;
+}
+
+#define kvm_pte_table_empty(ptep) kvm_page_empty(ptep)
+#ifndef CONFIG_ARM64_64K_PAGES
+#define kvm_pmd_table_empty(pmdp) kvm_page_empty(pmdp)
+#else
+#define kvm_pmd_table_empty(pmdp) (0)
+#endif
+#define kvm_pud_table_empty(pudp) (0)
+
+
 struct kvm;
 
 #define kvm_flush_dcache_to_poc(a,l)   __flush_dcache_area((a), (l))
index 215ad46..7a5df52 100644 (file)
@@ -50,6 +50,10 @@ static inline bool is_hyp_mode_mismatched(void)
        return __boot_cpu_mode[0] != __boot_cpu_mode[1];
 }
 
+/* The section containing the hypervisor text */
+extern char __hyp_text_start[];
+extern char __hyp_text_end[];
+
 #endif /* __ASSEMBLY__ */
 
 #endif /* ! __ASM__VIRT_H */
index 646f888..9a9fce0 100644 (file)
@@ -120,6 +120,7 @@ int main(void)
   DEFINE(VCPU_ESR_EL2,         offsetof(struct kvm_vcpu, arch.fault.esr_el2));
   DEFINE(VCPU_FAR_EL2,         offsetof(struct kvm_vcpu, arch.fault.far_el2));
   DEFINE(VCPU_HPFAR_EL2,       offsetof(struct kvm_vcpu, arch.fault.hpfar_el2));
+  DEFINE(VCPU_DEBUG_FLAGS,     offsetof(struct kvm_vcpu, arch.debug_flags));
   DEFINE(VCPU_HCR_EL2,         offsetof(struct kvm_vcpu, arch.hcr_el2));
   DEFINE(VCPU_IRQ_LINES,       offsetof(struct kvm_vcpu, arch.irq_lines));
   DEFINE(VCPU_HOST_CONTEXT,    offsetof(struct kvm_vcpu, arch.host_cpu_context));
@@ -129,13 +130,24 @@ 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_CPU_HCR,         offsetof(struct vgic_cpu, vgic_hcr));
-  DEFINE(VGIC_CPU_VMCR,                offsetof(struct vgic_cpu, vgic_vmcr));
-  DEFINE(VGIC_CPU_MISR,                offsetof(struct vgic_cpu, vgic_misr));
-  DEFINE(VGIC_CPU_EISR,                offsetof(struct vgic_cpu, vgic_eisr));
-  DEFINE(VGIC_CPU_ELRSR,       offsetof(struct vgic_cpu, vgic_elrsr));
-  DEFINE(VGIC_CPU_APR,         offsetof(struct vgic_cpu, vgic_apr));
-  DEFINE(VGIC_CPU_LR,          offsetof(struct vgic_cpu, vgic_lr));
+  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));
+  DEFINE(VGIC_V2_CPU_EISR,     offsetof(struct vgic_cpu, vgic_v2.vgic_eisr));
+  DEFINE(VGIC_V2_CPU_ELRSR,    offsetof(struct vgic_cpu, vgic_v2.vgic_elrsr));
+  DEFINE(VGIC_V2_CPU_APR,      offsetof(struct vgic_cpu, vgic_v2.vgic_apr));
+  DEFINE(VGIC_V2_CPU_LR,       offsetof(struct vgic_cpu, vgic_v2.vgic_lr));
+  DEFINE(VGIC_V3_CPU_HCR,      offsetof(struct vgic_cpu, vgic_v3.vgic_hcr));
+  DEFINE(VGIC_V3_CPU_VMCR,     offsetof(struct vgic_cpu, vgic_v3.vgic_vmcr));
+  DEFINE(VGIC_V3_CPU_MISR,     offsetof(struct vgic_cpu, vgic_v3.vgic_misr));
+  DEFINE(VGIC_V3_CPU_EISR,     offsetof(struct vgic_cpu, vgic_v3.vgic_eisr));
+  DEFINE(VGIC_V3_CPU_ELRSR,    offsetof(struct vgic_cpu, vgic_v3.vgic_elrsr));
+  DEFINE(VGIC_V3_CPU_AP0R,     offsetof(struct vgic_cpu, vgic_v3.vgic_ap0r));
+  DEFINE(VGIC_V3_CPU_AP1R,     offsetof(struct vgic_cpu, vgic_v3.vgic_ap1r));
+  DEFINE(VGIC_V3_CPU_LR,       offsetof(struct vgic_cpu, vgic_v3.vgic_lr));
   DEFINE(VGIC_CPU_NR_LR,       offsetof(struct vgic_cpu, nr_lr));
   DEFINE(KVM_VTTBR,            offsetof(struct kvm, arch.vttbr));
   DEFINE(KVM_VGIC_VCTRL,       offsetof(struct kvm, arch.vgic.vctrl_base));
index fe5b940..b056369 100644 (file)
 #include <asm/cputype.h>
 #include <asm/system_misc.h>
 
-/* Low-level stepping controls. */
-#define DBG_MDSCR_SS           (1 << 0)
-#define DBG_SPSR_SS            (1 << 21)
-
-/* MDSCR_EL1 enabling bits */
-#define DBG_MDSCR_KDE          (1 << 13)
-#define DBG_MDSCR_MDE          (1 << 15)
-#define DBG_MDSCR_MASK         ~(DBG_MDSCR_KDE | DBG_MDSCR_MDE)
-
 /* Determine debug architecture. */
 u8 debug_monitors_arch(void)
 {
index 72a9fd5..32a0961 100644 (file)
@@ -20,4 +20,8 @@ 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-$(CONFIG_KVM_ARM_VGIC) += $(KVM)/arm/vgic-v2.o
+kvm-$(CONFIG_KVM_ARM_VGIC) += vgic-v2-switch.o
+kvm-$(CONFIG_KVM_ARM_VGIC) += $(KVM)/arm/vgic-v3.o
+kvm-$(CONFIG_KVM_ARM_VGIC) += vgic-v3-switch.o
 kvm-$(CONFIG_KVM_ARM_TIMER) += $(KVM)/arm/arch_timer.o
index 60b5c31..8d1ec28 100644 (file)
@@ -135,6 +135,59 @@ static unsigned long num_core_regs(void)
        return sizeof(struct kvm_regs) / sizeof(__u32);
 }
 
+/**
+ * ARM64 versions of the TIMER registers, always available on arm64
+ */
+
+#define NUM_TIMER_REGS 3
+
+static bool is_timer_reg(u64 index)
+{
+       switch (index) {
+       case KVM_REG_ARM_TIMER_CTL:
+       case KVM_REG_ARM_TIMER_CNT:
+       case KVM_REG_ARM_TIMER_CVAL:
+               return true;
+       }
+       return false;
+}
+
+static int copy_timer_indices(struct kvm_vcpu *vcpu, u64 __user *uindices)
+{
+       if (put_user(KVM_REG_ARM_TIMER_CTL, uindices))
+               return -EFAULT;
+       uindices++;
+       if (put_user(KVM_REG_ARM_TIMER_CNT, uindices))
+               return -EFAULT;
+       uindices++;
+       if (put_user(KVM_REG_ARM_TIMER_CVAL, uindices))
+               return -EFAULT;
+
+       return 0;
+}
+
+static int set_timer_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
+{
+       void __user *uaddr = (void __user *)(long)reg->addr;
+       u64 val;
+       int ret;
+
+       ret = copy_from_user(&val, uaddr, KVM_REG_SIZE(reg->id));
+       if (ret != 0)
+               return ret;
+
+       return kvm_arm_timer_set_reg(vcpu, reg->id, val);
+}
+
+static int get_timer_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
+{
+       void __user *uaddr = (void __user *)(long)reg->addr;
+       u64 val;
+
+       val = kvm_arm_timer_get_reg(vcpu, reg->id);
+       return copy_to_user(uaddr, &val, KVM_REG_SIZE(reg->id));
+}
+
 /**
  * kvm_arm_num_regs - how many registers do we present via KVM_GET_ONE_REG
  *
@@ -142,7 +195,8 @@ static unsigned long num_core_regs(void)
  */
 unsigned long kvm_arm_num_regs(struct kvm_vcpu *vcpu)
 {
-       return num_core_regs() + kvm_arm_num_sys_reg_descs(vcpu);
+       return num_core_regs() + kvm_arm_num_sys_reg_descs(vcpu)
+                + NUM_TIMER_REGS;
 }
 
 /**
@@ -154,6 +208,7 @@ int kvm_arm_copy_reg_indices(struct kvm_vcpu *vcpu, u64 __user *uindices)
 {
        unsigned int i;
        const u64 core_reg = KVM_REG_ARM64 | KVM_REG_SIZE_U64 | KVM_REG_ARM_CORE;
+       int ret;
 
        for (i = 0; i < sizeof(struct kvm_regs) / sizeof(__u32); i++) {
                if (put_user(core_reg | i, uindices))
@@ -161,6 +216,11 @@ int kvm_arm_copy_reg_indices(struct kvm_vcpu *vcpu, u64 __user *uindices)
                uindices++;
        }
 
+       ret = copy_timer_indices(vcpu, uindices);
+       if (ret)
+               return ret;
+       uindices += NUM_TIMER_REGS;
+
        return kvm_arm_copy_sys_reg_indices(vcpu, uindices);
 }
 
@@ -174,6 +234,9 @@ int kvm_arm_get_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
        if ((reg->id & KVM_REG_ARM_COPROC_MASK) == KVM_REG_ARM_CORE)
                return get_core_reg(vcpu, reg);
 
+       if (is_timer_reg(reg->id))
+               return get_timer_reg(vcpu, reg);
+
        return kvm_arm_sys_reg_get_reg(vcpu, reg);
 }
 
@@ -187,6 +250,9 @@ int kvm_arm_set_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
        if ((reg->id & KVM_REG_ARM_COPROC_MASK) == KVM_REG_ARM_CORE)
                return set_core_reg(vcpu, reg);
 
+       if (is_timer_reg(reg->id))
+               return set_timer_reg(vcpu, reg);
+
        return kvm_arm_sys_reg_set_reg(vcpu, reg);
 }
 
index 182415e..e28be51 100644 (file)
@@ -73,9 +73,9 @@ static exit_handle_fn arm_exit_handlers[] = {
        [ESR_EL2_EC_WFI]        = kvm_handle_wfx,
        [ESR_EL2_EC_CP15_32]    = kvm_handle_cp15_32,
        [ESR_EL2_EC_CP15_64]    = kvm_handle_cp15_64,
-       [ESR_EL2_EC_CP14_MR]    = kvm_handle_cp14_access,
+       [ESR_EL2_EC_CP14_MR]    = kvm_handle_cp14_32,
        [ESR_EL2_EC_CP14_LS]    = kvm_handle_cp14_load_store,
-       [ESR_EL2_EC_CP14_64]    = kvm_handle_cp14_access,
+       [ESR_EL2_EC_CP14_64]    = kvm_handle_cp14_64,
        [ESR_EL2_EC_HVC32]      = handle_hvc,
        [ESR_EL2_EC_SMC32]      = handle_smc,
        [ESR_EL2_EC_HVC64]      = handle_hvc,
index b0d1512..b72aa9f 100644 (file)
  */
 
 #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/debug-monitors.h>
 #include <asm/fpsimdmacros.h>
 #include <asm/kvm.h>
 #include <asm/kvm_asm.h>
@@ -36,9 +36,6 @@
        .pushsection    .hyp.text, "ax"
        .align  PAGE_SHIFT
 
-__kvm_hyp_code_start:
-       .globl __kvm_hyp_code_start
-
 .macro save_common_regs
        // x2: base address for cpu context
        // x3: tmp register
@@ -215,6 +212,7 @@ __kvm_hyp_code_start:
        mrs     x22,    amair_el1
        mrs     x23,    cntkctl_el1
        mrs     x24,    par_el1
+       mrs     x25,    mdscr_el1
 
        stp     x4, x5, [x3]
        stp     x6, x7, [x3, #16]
@@ -226,7 +224,202 @@ __kvm_hyp_code_start:
        stp     x18, x19, [x3, #112]
        stp     x20, x21, [x3, #128]
        stp     x22, x23, [x3, #144]
-       str     x24, [x3, #160]
+       stp     x24, x25, [x3, #160]
+.endm
+
+.macro save_debug
+       // x2: base address for cpu context
+       // x3: tmp register
+
+       mrs     x26, id_aa64dfr0_el1
+       ubfx    x24, x26, #12, #4       // Extract BRPs
+       ubfx    x25, x26, #20, #4       // Extract WRPs
+       mov     w26, #15
+       sub     w24, w26, w24           // How many BPs to skip
+       sub     w25, w26, w25           // How many WPs to skip
+
+       add     x3, x2, #CPU_SYSREG_OFFSET(DBGBCR0_EL1)
+
+       adr     x26, 1f
+       add     x26, x26, x24, lsl #2
+       br      x26
+1:
+       mrs     x20, dbgbcr15_el1
+       mrs     x19, dbgbcr14_el1
+       mrs     x18, dbgbcr13_el1
+       mrs     x17, dbgbcr12_el1
+       mrs     x16, dbgbcr11_el1
+       mrs     x15, dbgbcr10_el1
+       mrs     x14, dbgbcr9_el1
+       mrs     x13, dbgbcr8_el1
+       mrs     x12, dbgbcr7_el1
+       mrs     x11, dbgbcr6_el1
+       mrs     x10, dbgbcr5_el1
+       mrs     x9, dbgbcr4_el1
+       mrs     x8, dbgbcr3_el1
+       mrs     x7, dbgbcr2_el1
+       mrs     x6, dbgbcr1_el1
+       mrs     x5, dbgbcr0_el1
+
+       adr     x26, 1f
+       add     x26, x26, x24, lsl #2
+       br      x26
+
+1:
+       str     x20, [x3, #(15 * 8)]
+       str     x19, [x3, #(14 * 8)]
+       str     x18, [x3, #(13 * 8)]
+       str     x17, [x3, #(12 * 8)]
+       str     x16, [x3, #(11 * 8)]
+       str     x15, [x3, #(10 * 8)]
+       str     x14, [x3, #(9 * 8)]
+       str     x13, [x3, #(8 * 8)]
+       str     x12, [x3, #(7 * 8)]
+       str     x11, [x3, #(6 * 8)]
+       str     x10, [x3, #(5 * 8)]
+       str     x9, [x3, #(4 * 8)]
+       str     x8, [x3, #(3 * 8)]
+       str     x7, [x3, #(2 * 8)]
+       str     x6, [x3, #(1 * 8)]
+       str     x5, [x3, #(0 * 8)]
+
+       add     x3, x2, #CPU_SYSREG_OFFSET(DBGBVR0_EL1)
+
+       adr     x26, 1f
+       add     x26, x26, x24, lsl #2
+       br      x26
+1:
+       mrs     x20, dbgbvr15_el1
+       mrs     x19, dbgbvr14_el1
+       mrs     x18, dbgbvr13_el1
+       mrs     x17, dbgbvr12_el1
+       mrs     x16, dbgbvr11_el1
+       mrs     x15, dbgbvr10_el1
+       mrs     x14, dbgbvr9_el1
+       mrs     x13, dbgbvr8_el1
+       mrs     x12, dbgbvr7_el1
+       mrs     x11, dbgbvr6_el1
+       mrs     x10, dbgbvr5_el1
+       mrs     x9, dbgbvr4_el1
+       mrs     x8, dbgbvr3_el1
+       mrs     x7, dbgbvr2_el1
+       mrs     x6, dbgbvr1_el1
+       mrs     x5, dbgbvr0_el1
+
+       adr     x26, 1f
+       add     x26, x26, x24, lsl #2
+       br      x26
+
+1:
+       str     x20, [x3, #(15 * 8)]
+       str     x19, [x3, #(14 * 8)]
+       str     x18, [x3, #(13 * 8)]
+       str     x17, [x3, #(12 * 8)]
+       str     x16, [x3, #(11 * 8)]
+       str     x15, [x3, #(10 * 8)]
+       str     x14, [x3, #(9 * 8)]
+       str     x13, [x3, #(8 * 8)]
+       str     x12, [x3, #(7 * 8)]
+       str     x11, [x3, #(6 * 8)]
+       str     x10, [x3, #(5 * 8)]
+       str     x9, [x3, #(4 * 8)]
+       str     x8, [x3, #(3 * 8)]
+       str     x7, [x3, #(2 * 8)]
+       str     x6, [x3, #(1 * 8)]
+       str     x5, [x3, #(0 * 8)]
+
+       add     x3, x2, #CPU_SYSREG_OFFSET(DBGWCR0_EL1)
+
+       adr     x26, 1f
+       add     x26, x26, x25, lsl #2
+       br      x26
+1:
+       mrs     x20, dbgwcr15_el1
+       mrs     x19, dbgwcr14_el1
+       mrs     x18, dbgwcr13_el1
+       mrs     x17, dbgwcr12_el1
+       mrs     x16, dbgwcr11_el1
+       mrs     x15, dbgwcr10_el1
+       mrs     x14, dbgwcr9_el1
+       mrs     x13, dbgwcr8_el1
+       mrs     x12, dbgwcr7_el1
+       mrs     x11, dbgwcr6_el1
+       mrs     x10, dbgwcr5_el1
+       mrs     x9, dbgwcr4_el1
+       mrs     x8, dbgwcr3_el1
+       mrs     x7, dbgwcr2_el1
+       mrs     x6, dbgwcr1_el1
+       mrs     x5, dbgwcr0_el1
+
+       adr     x26, 1f
+       add     x26, x26, x25, lsl #2
+       br      x26
+
+1:
+       str     x20, [x3, #(15 * 8)]
+       str     x19, [x3, #(14 * 8)]
+       str     x18, [x3, #(13 * 8)]
+       str     x17, [x3, #(12 * 8)]
+       str     x16, [x3, #(11 * 8)]
+       str     x15, [x3, #(10 * 8)]
+       str     x14, [x3, #(9 * 8)]
+       str     x13, [x3, #(8 * 8)]
+       str     x12, [x3, #(7 * 8)]
+       str     x11, [x3, #(6 * 8)]
+       str     x10, [x3, #(5 * 8)]
+       str     x9, [x3, #(4 * 8)]
+       str     x8, [x3, #(3 * 8)]
+       str     x7, [x3, #(2 * 8)]
+       str     x6, [x3, #(1 * 8)]
+       str     x5, [x3, #(0 * 8)]
+
+       add     x3, x2, #CPU_SYSREG_OFFSET(DBGWVR0_EL1)
+
+       adr     x26, 1f
+       add     x26, x26, x25, lsl #2
+       br      x26
+1:
+       mrs     x20, dbgwvr15_el1
+       mrs     x19, dbgwvr14_el1
+       mrs     x18, dbgwvr13_el1
+       mrs     x17, dbgwvr12_el1
+       mrs     x16, dbgwvr11_el1
+       mrs     x15, dbgwvr10_el1
+       mrs     x14, dbgwvr9_el1
+       mrs     x13, dbgwvr8_el1
+       mrs     x12, dbgwvr7_el1
+       mrs     x11, dbgwvr6_el1
+       mrs     x10, dbgwvr5_el1
+       mrs     x9, dbgwvr4_el1
+       mrs     x8, dbgwvr3_el1
+       mrs     x7, dbgwvr2_el1
+       mrs     x6, dbgwvr1_el1
+       mrs     x5, dbgwvr0_el1
+
+       adr     x26, 1f
+       add     x26, x26, x25, lsl #2
+       br      x26
+
+1:
+       str     x20, [x3, #(15 * 8)]
+       str     x19, [x3, #(14 * 8)]
+       str     x18, [x3, #(13 * 8)]
+       str     x17, [x3, #(12 * 8)]
+       str     x16, [x3, #(11 * 8)]
+       str     x15, [x3, #(10 * 8)]
+       str     x14, [x3, #(9 * 8)]
+       str     x13, [x3, #(8 * 8)]
+       str     x12, [x3, #(7 * 8)]
+       str     x11, [x3, #(6 * 8)]
+       str     x10, [x3, #(5 * 8)]
+       str     x9, [x3, #(4 * 8)]
+       str     x8, [x3, #(3 * 8)]
+       str     x7, [x3, #(2 * 8)]
+       str     x6, [x3, #(1 * 8)]
+       str     x5, [x3, #(0 * 8)]
+
+       mrs     x21, mdccint_el1
+       str     x21, [x2, #CPU_SYSREG_OFFSET(MDCCINT_EL1)]
 .endm
 
 .macro restore_sysregs
@@ -245,7 +438,7 @@ __kvm_hyp_code_start:
        ldp     x18, x19, [x3, #112]
        ldp     x20, x21, [x3, #128]
        ldp     x22, x23, [x3, #144]
-       ldr     x24, [x3, #160]
+       ldp     x24, x25, [x3, #160]
 
        msr     vmpidr_el2,     x4
        msr     csselr_el1,     x5
@@ -268,6 +461,198 @@ __kvm_hyp_code_start:
        msr     amair_el1,      x22
        msr     cntkctl_el1,    x23
        msr     par_el1,        x24
+       msr     mdscr_el1,      x25
+.endm
+
+.macro restore_debug
+       // x2: base address for cpu context
+       // x3: tmp register
+
+       mrs     x26, id_aa64dfr0_el1
+       ubfx    x24, x26, #12, #4       // Extract BRPs
+       ubfx    x25, x26, #20, #4       // Extract WRPs
+       mov     w26, #15
+       sub     w24, w26, w24           // How many BPs to skip
+       sub     w25, w26, w25           // How many WPs to skip
+
+       add     x3, x2, #CPU_SYSREG_OFFSET(DBGBCR0_EL1)
+
+       adr     x26, 1f
+       add     x26, x26, x24, lsl #2
+       br      x26
+1:
+       ldr     x20, [x3, #(15 * 8)]
+       ldr     x19, [x3, #(14 * 8)]
+       ldr     x18, [x3, #(13 * 8)]
+       ldr     x17, [x3, #(12 * 8)]
+       ldr     x16, [x3, #(11 * 8)]
+       ldr     x15, [x3, #(10 * 8)]
+       ldr     x14, [x3, #(9 * 8)]
+       ldr     x13, [x3, #(8 * 8)]
+       ldr     x12, [x3, #(7 * 8)]
+       ldr     x11, [x3, #(6 * 8)]
+       ldr     x10, [x3, #(5 * 8)]
+       ldr     x9, [x3, #(4 * 8)]
+       ldr     x8, [x3, #(3 * 8)]
+       ldr     x7, [x3, #(2 * 8)]
+       ldr     x6, [x3, #(1 * 8)]
+       ldr     x5, [x3, #(0 * 8)]
+
+       adr     x26, 1f
+       add     x26, x26, x24, lsl #2
+       br      x26
+1:
+       msr     dbgbcr15_el1, x20
+       msr     dbgbcr14_el1, x19
+       msr     dbgbcr13_el1, x18
+       msr     dbgbcr12_el1, x17
+       msr     dbgbcr11_el1, x16
+       msr     dbgbcr10_el1, x15
+       msr     dbgbcr9_el1, x14
+       msr     dbgbcr8_el1, x13
+       msr     dbgbcr7_el1, x12
+       msr     dbgbcr6_el1, x11
+       msr     dbgbcr5_el1, x10
+       msr     dbgbcr4_el1, x9
+       msr     dbgbcr3_el1, x8
+       msr     dbgbcr2_el1, x7
+       msr     dbgbcr1_el1, x6
+       msr     dbgbcr0_el1, x5
+
+       add     x3, x2, #CPU_SYSREG_OFFSET(DBGBVR0_EL1)
+
+       adr     x26, 1f
+       add     x26, x26, x24, lsl #2
+       br      x26
+1:
+       ldr     x20, [x3, #(15 * 8)]
+       ldr     x19, [x3, #(14 * 8)]
+       ldr     x18, [x3, #(13 * 8)]
+       ldr     x17, [x3, #(12 * 8)]
+       ldr     x16, [x3, #(11 * 8)]
+       ldr     x15, [x3, #(10 * 8)]
+       ldr     x14, [x3, #(9 * 8)]
+       ldr     x13, [x3, #(8 * 8)]
+       ldr     x12, [x3, #(7 * 8)]
+       ldr     x11, [x3, #(6 * 8)]
+       ldr     x10, [x3, #(5 * 8)]
+       ldr     x9, [x3, #(4 * 8)]
+       ldr     x8, [x3, #(3 * 8)]
+       ldr     x7, [x3, #(2 * 8)]
+       ldr     x6, [x3, #(1 * 8)]
+       ldr     x5, [x3, #(0 * 8)]
+
+       adr     x26, 1f
+       add     x26, x26, x24, lsl #2
+       br      x26
+1:
+       msr     dbgbvr15_el1, x20
+       msr     dbgbvr14_el1, x19
+       msr     dbgbvr13_el1, x18
+       msr     dbgbvr12_el1, x17
+       msr     dbgbvr11_el1, x16
+       msr     dbgbvr10_el1, x15
+       msr     dbgbvr9_el1, x14
+       msr     dbgbvr8_el1, x13
+       msr     dbgbvr7_el1, x12
+       msr     dbgbvr6_el1, x11
+       msr     dbgbvr5_el1, x10
+       msr     dbgbvr4_el1, x9
+       msr     dbgbvr3_el1, x8
+       msr     dbgbvr2_el1, x7
+       msr     dbgbvr1_el1, x6
+       msr     dbgbvr0_el1, x5
+
+       add     x3, x2, #CPU_SYSREG_OFFSET(DBGWCR0_EL1)
+
+       adr     x26, 1f
+       add     x26, x26, x25, lsl #2
+       br      x26
+1:
+       ldr     x20, [x3, #(15 * 8)]
+       ldr     x19, [x3, #(14 * 8)]
+       ldr     x18, [x3, #(13 * 8)]
+       ldr     x17, [x3, #(12 * 8)]
+       ldr     x16, [x3, #(11 * 8)]
+       ldr     x15, [x3, #(10 * 8)]
+       ldr     x14, [x3, #(9 * 8)]
+       ldr     x13, [x3, #(8 * 8)]
+       ldr     x12, [x3, #(7 * 8)]
+       ldr     x11, [x3, #(6 * 8)]
+       ldr     x10, [x3, #(5 * 8)]
+       ldr     x9, [x3, #(4 * 8)]
+       ldr     x8, [x3, #(3 * 8)]
+       ldr     x7, [x3, #(2 * 8)]
+       ldr     x6, [x3, #(1 * 8)]
+       ldr     x5, [x3, #(0 * 8)]
+
+       adr     x26, 1f
+       add     x26, x26, x25, lsl #2
+       br      x26
+1:
+       msr     dbgwcr15_el1, x20
+       msr     dbgwcr14_el1, x19
+       msr     dbgwcr13_el1, x18
+       msr     dbgwcr12_el1, x17
+       msr     dbgwcr11_el1, x16
+       msr     dbgwcr10_el1, x15
+       msr     dbgwcr9_el1, x14
+       msr     dbgwcr8_el1, x13
+       msr     dbgwcr7_el1, x12
+       msr     dbgwcr6_el1, x11
+       msr     dbgwcr5_el1, x10
+       msr     dbgwcr4_el1, x9
+       msr     dbgwcr3_el1, x8
+       msr     dbgwcr2_el1, x7
+       msr     dbgwcr1_el1, x6
+       msr     dbgwcr0_el1, x5
+
+       add     x3, x2, #CPU_SYSREG_OFFSET(DBGWVR0_EL1)
+
+       adr     x26, 1f
+       add     x26, x26, x25, lsl #2
+       br      x26
+1:
+       ldr     x20, [x3, #(15 * 8)]
+       ldr     x19, [x3, #(14 * 8)]
+       ldr     x18, [x3, #(13 * 8)]
+       ldr     x17, [x3, #(12 * 8)]
+       ldr     x16, [x3, #(11 * 8)]
+       ldr     x15, [x3, #(10 * 8)]
+       ldr     x14, [x3, #(9 * 8)]
+       ldr     x13, [x3, #(8 * 8)]
+       ldr     x12, [x3, #(7 * 8)]
+       ldr     x11, [x3, #(6 * 8)]
+       ldr     x10, [x3, #(5 * 8)]
+       ldr     x9, [x3, #(4 * 8)]
+       ldr     x8, [x3, #(3 * 8)]
+       ldr     x7, [x3, #(2 * 8)]
+       ldr     x6, [x3, #(1 * 8)]
+       ldr     x5, [x3, #(0 * 8)]
+
+       adr     x26, 1f
+       add     x26, x26, x25, lsl #2
+       br      x26
+1:
+       msr     dbgwvr15_el1, x20
+       msr     dbgwvr14_el1, x19
+       msr     dbgwvr13_el1, x18
+       msr     dbgwvr12_el1, x17
+       msr     dbgwvr11_el1, x16
+       msr     dbgwvr10_el1, x15
+       msr     dbgwvr9_el1, x14
+       msr     dbgwvr8_el1, x13
+       msr     dbgwvr7_el1, x12
+       msr     dbgwvr6_el1, x11
+       msr     dbgwvr5_el1, x10
+       msr     dbgwvr4_el1, x9
+       msr     dbgwvr3_el1, x8
+       msr     dbgwvr2_el1, x7
+       msr     dbgwvr1_el1, x6
+       msr     dbgwvr0_el1, x5
+
+       ldr     x21, [x2, #CPU_SYSREG_OFFSET(MDCCINT_EL1)]
+       msr     mdccint_el1, x21
 .endm
 
 .macro skip_32bit_state tmp, target
@@ -282,6 +667,35 @@ __kvm_hyp_code_start:
        tbz     \tmp, #12, \target
 .endm
 
+.macro skip_debug_state tmp, target
+       ldr     \tmp, [x0, #VCPU_DEBUG_FLAGS]
+       tbz     \tmp, #KVM_ARM64_DEBUG_DIRTY_SHIFT, \target
+.endm
+
+.macro compute_debug_state target
+       // Compute debug state: If any of KDE, MDE or KVM_ARM64_DEBUG_DIRTY
+       // is set, we do a full save/restore cycle and disable trapping.
+       add     x25, x0, #VCPU_CONTEXT
+
+       // Check the state of MDSCR_EL1
+       ldr     x25, [x25, #CPU_SYSREG_OFFSET(MDSCR_EL1)]
+       and     x26, x25, #DBG_MDSCR_KDE
+       and     x25, x25, #DBG_MDSCR_MDE
+       adds    xzr, x25, x26
+       b.eq    9998f           // Nothing to see there
+
+       // If any interesting bits was set, we must set the flag
+       mov     x26, #KVM_ARM64_DEBUG_DIRTY
+       str     x26, [x0, #VCPU_DEBUG_FLAGS]
+       b       9999f           // Don't skip restore
+
+9998:
+       // Otherwise load the flags from memory in case we recently
+       // trapped
+       skip_debug_state x25, \target
+9999:
+.endm
+
 .macro save_guest_32bit_state
        skip_32bit_state x3, 1f
 
@@ -297,10 +711,13 @@ __kvm_hyp_code_start:
        mrs     x4, dacr32_el2
        mrs     x5, ifsr32_el2
        mrs     x6, fpexc32_el2
-       mrs     x7, dbgvcr32_el2
        stp     x4, x5, [x3]
-       stp     x6, x7, [x3, #16]
+       str     x6, [x3, #16]
 
+       skip_debug_state x8, 2f
+       mrs     x7, dbgvcr32_el2
+       str     x7, [x3, #24]
+2:
        skip_tee_state x8, 1f
 
        add     x3, x2, #CPU_SYSREG_OFFSET(TEECR32_EL1)
@@ -323,12 +740,15 @@ __kvm_hyp_code_start:
 
        add     x3, x2, #CPU_SYSREG_OFFSET(DACR32_EL2)
        ldp     x4, x5, [x3]
-       ldp     x6, x7, [x3, #16]
+       ldr     x6, [x3, #16]
        msr     dacr32_el2, x4
        msr     ifsr32_el2, x5
        msr     fpexc32_el2, x6
-       msr     dbgvcr32_el2, x7
 
+       skip_debug_state x8, 2f
+       ldr     x7, [x3, #24]
+       msr     dbgvcr32_el2, x7
+2:
        skip_tee_state x8, 1f
 
        add     x3, x2, #CPU_SYSREG_OFFSET(TEECR32_EL1)
@@ -339,11 +759,8 @@ __kvm_hyp_code_start:
 .endm
 
 .macro activate_traps
-       ldr     x2, [x0, #VCPU_IRQ_LINES]
-       ldr     x1, [x0, #VCPU_HCR_EL2]
-       orr     x2, x2, x1
-       msr     hcr_el2, x2
-
+       ldr     x2, [x0, #VCPU_HCR_EL2]
+       msr     hcr_el2, x2
        ldr     x2, =(CPTR_EL2_TTA)
        msr     cptr_el2, x2
 
@@ -353,6 +770,14 @@ __kvm_hyp_code_start:
        mrs     x2, mdcr_el2
        and     x2, x2, #MDCR_EL2_HPMN_MASK
        orr     x2, x2, #(MDCR_EL2_TPM | MDCR_EL2_TPMCR)
+       orr     x2, x2, #(MDCR_EL2_TDRA | MDCR_EL2_TDOSA)
+
+       // Check for KVM_ARM64_DEBUG_DIRTY, and set debug to trap
+       // if not dirty.
+       ldr     x3, [x0, #VCPU_DEBUG_FLAGS]
+       tbnz    x3, #KVM_ARM64_DEBUG_DIRTY_SHIFT, 1f
+       orr     x2, x2,  #MDCR_EL2_TDA
+1:
        msr     mdcr_el2, x2
 .endm
 
@@ -379,100 +804,33 @@ __kvm_hyp_code_start:
 .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_CPU_HCR]
-       str     w5, [x3, #VGIC_CPU_VMCR]
-       str     w6, [x3, #VGIC_CPU_MISR]
-       str     w7, [x3, #VGIC_CPU_EISR]
-       str     w8, [x3, #(VGIC_CPU_EISR + 4)]
-       str     w9, [x3, #VGIC_CPU_ELRSR]
-       str     w10, [x3, #(VGIC_CPU_ELRSR + 4)]
-       str     w11, [x3, #VGIC_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_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
+       mrs     x24, hcr_el2
+       mov     x25, #HCR_INT_OVERRIDE
+       neg     x25, x25
+       and     x24, x24, x25
+       msr     hcr_el2, 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_CPU_HCR]
-       ldr     w5, [x3, #VGIC_CPU_VMCR]
-       ldr     w6, [x3, #VGIC_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_CPU_LR
-1:     ldr     w5, [x3], #4
-CPU_BE(        rev     w5, w5 )
-       str     w5, [x2], #4
-       sub     w4, w4, #1
-       cbnz    w4, 1b
-2:
+       mrs     x24, hcr_el2
+       ldr     x25, [x0, #VCPU_IRQ_LINES]
+       orr     x24, x24, #HCR_INT_OVERRIDE
+       orr     x24, x24, x25
+       msr     hcr_el2, x24
+       adr     x24, __vgic_sr_vectors
+       ldr     x24, [x24, #VGIC_RESTORE_FN]
+       kern_hyp_va     x24
+       blr     x24
 .endm
 
 .macro save_timer_state
@@ -537,6 +895,14 @@ __restore_sysregs:
        restore_sysregs
        ret
 
+__save_debug:
+       save_debug
+       ret
+
+__restore_debug:
+       restore_debug
+       ret
+
 __save_fpsimd:
        save_fpsimd
        ret
@@ -568,6 +934,9 @@ ENTRY(__kvm_vcpu_run)
        bl __save_fpsimd
        bl __save_sysregs
 
+       compute_debug_state 1f
+       bl      __save_debug
+1:
        activate_traps
        activate_vm
 
@@ -579,6 +948,10 @@ ENTRY(__kvm_vcpu_run)
 
        bl __restore_sysregs
        bl __restore_fpsimd
+
+       skip_debug_state x3, 1f
+       bl      __restore_debug
+1:
        restore_guest_32bit_state
        restore_guest_regs
 
@@ -595,6 +968,10 @@ __kvm_vcpu_return:
        save_guest_regs
        bl __save_fpsimd
        bl __save_sysregs
+
+       skip_debug_state x3, 1f
+       bl      __save_debug
+1:
        save_guest_32bit_state
 
        save_timer_state
@@ -609,6 +986,14 @@ __kvm_vcpu_return:
 
        bl __restore_sysregs
        bl __restore_fpsimd
+
+       skip_debug_state x3, 1f
+       // Clear the dirty flag for the next run, as all the state has
+       // already been saved. Note that we nuke the whole 64bit word.
+       // If we ever add more flags, we'll have to be more careful...
+       str     xzr, [x0, #VCPU_DEBUG_FLAGS]
+       bl      __restore_debug
+1:
        restore_host_regs
 
        mov     x0, x1
@@ -653,6 +1038,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.
@@ -830,7 +1221,7 @@ el1_trap:
        mrs     x2, far_el2
 
 2:     mrs     x0, tpidr_el2
-       str     x1, [x0, #VCPU_ESR_EL2]
+       str     w1, [x0, #VCPU_ESR_EL2]
        str     x2, [x0, #VCPU_FAR_EL2]
        str     x3, [x0, #VCPU_HPFAR_EL2]
 
@@ -880,7 +1271,4 @@ ENTRY(__kvm_hyp_vector)
        ventry  el1_error_invalid               // Error 32-bit EL1
 ENDPROC(__kvm_hyp_vector)
 
-__kvm_hyp_code_end:
-       .globl  __kvm_hyp_code_end
-
        .popsection
index c59a1bd..5805e7c 100644 (file)
@@ -30,6 +30,7 @@
 #include <asm/kvm_mmu.h>
 #include <asm/cacheflush.h>
 #include <asm/cputype.h>
+#include <asm/debug-monitors.h>
 #include <trace/events/kvm.h>
 
 #include "sys_regs.h"
@@ -137,10 +138,11 @@ static bool access_vm_reg(struct kvm_vcpu *vcpu,
        if (!p->is_aarch32) {
                vcpu_sys_reg(vcpu, r->reg) = val;
        } else {
-               vcpu_cp15(vcpu, r->reg) = val & 0xffffffffUL;
                if (!p->is_32bit)
-                       vcpu_cp15(vcpu, r->reg + 1) = val >> 32;
+                       vcpu_cp15_64_high(vcpu, r->reg) = val >> 32;
+               vcpu_cp15_64_low(vcpu, r->reg) = val & 0xffffffffUL;
        }
+
        return true;
 }
 
@@ -163,18 +165,9 @@ static bool access_sctlr(struct kvm_vcpu *vcpu,
        return true;
 }
 
-/*
- * We could trap ID_DFR0 and tell the guest we don't support performance
- * monitoring.  Unfortunately the patch to make the kernel check ID_DFR0 was
- * NAKed, so it will read the PMCR anyway.
- *
- * Therefore we tell the guest we have 0 counters.  Unfortunately, we
- * must always support PMCCNTR (the cycle counter): we just RAZ/WI for
- * all PM registers, which doesn't crash the guest kernel at least.
- */
-static bool pm_fake(struct kvm_vcpu *vcpu,
-                   const struct sys_reg_params *p,
-                   const struct sys_reg_desc *r)
+static bool trap_raz_wi(struct kvm_vcpu *vcpu,
+                       const struct sys_reg_params *p,
+                       const struct sys_reg_desc *r)
 {
        if (p->is_write)
                return ignore_write(vcpu, p);
@@ -182,6 +175,73 @@ static bool pm_fake(struct kvm_vcpu *vcpu,
                return read_zero(vcpu, p);
 }
 
+static bool trap_oslsr_el1(struct kvm_vcpu *vcpu,
+                          const struct sys_reg_params *p,
+                          const struct sys_reg_desc *r)
+{
+       if (p->is_write) {
+               return ignore_write(vcpu, p);
+       } else {
+               *vcpu_reg(vcpu, p->Rt) = (1 << 3);
+               return true;
+       }
+}
+
+static bool trap_dbgauthstatus_el1(struct kvm_vcpu *vcpu,
+                                  const struct sys_reg_params *p,
+                                  const struct sys_reg_desc *r)
+{
+       if (p->is_write) {
+               return ignore_write(vcpu, p);
+       } else {
+               u32 val;
+               asm volatile("mrs %0, dbgauthstatus_el1" : "=r" (val));
+               *vcpu_reg(vcpu, p->Rt) = val;
+               return true;
+       }
+}
+
+/*
+ * We want to avoid world-switching all the DBG registers all the
+ * time:
+ * 
+ * - If we've touched any debug register, it is likely that we're
+ *   going to touch more of them. It then makes sense to disable the
+ *   traps and start doing the save/restore dance
+ * - If debug is active (DBG_MDSCR_KDE or DBG_MDSCR_MDE set), it is
+ *   then mandatory to save/restore the registers, as the guest
+ *   depends on them.
+ * 
+ * For this, we use a DIRTY bit, indicating the guest has modified the
+ * debug registers, used as follow:
+ *
+ * On guest entry:
+ * - If the dirty bit is set (because we're coming back from trapping),
+ *   disable the traps, save host registers, restore guest registers.
+ * - If debug is actively in use (DBG_MDSCR_KDE or DBG_MDSCR_MDE set),
+ *   set the dirty bit, disable the traps, save host registers,
+ *   restore guest registers.
+ * - Otherwise, enable the traps
+ *
+ * On guest exit:
+ * - If the dirty bit is set, save guest registers, restore host
+ *   registers and clear the dirty bit. This ensure that the host can
+ *   now use the debug registers.
+ */
+static bool trap_debug_regs(struct kvm_vcpu *vcpu,
+                           const struct sys_reg_params *p,
+                           const struct sys_reg_desc *r)
+{
+       if (p->is_write) {
+               vcpu_sys_reg(vcpu, r->reg) = *vcpu_reg(vcpu, p->Rt);
+               vcpu->arch.debug_flags |= KVM_ARM64_DEBUG_DIRTY;
+       } else {
+               *vcpu_reg(vcpu, p->Rt) = vcpu_sys_reg(vcpu, r->reg);
+       }
+
+       return true;
+}
+
 static void reset_amair_el1(struct kvm_vcpu *vcpu, const struct sys_reg_desc *r)
 {
        u64 amair;
@@ -198,9 +258,39 @@ static void reset_mpidr(struct kvm_vcpu *vcpu, const struct sys_reg_desc *r)
        vcpu_sys_reg(vcpu, MPIDR_EL1) = (1UL << 31) | (vcpu->vcpu_id & 0xff);
 }
 
+/* Silly macro to expand the DBG{BCR,BVR,WVR,WCR}n_EL1 registers in one go */
+#define DBG_BCR_BVR_WCR_WVR_EL1(n)                                     \
+       /* DBGBVRn_EL1 */                                               \
+       { Op0(0b10), Op1(0b000), CRn(0b0000), CRm((n)), Op2(0b100),     \
+         trap_debug_regs, reset_val, (DBGBVR0_EL1 + (n)), 0 },         \
+       /* DBGBCRn_EL1 */                                               \
+       { Op0(0b10), Op1(0b000), CRn(0b0000), CRm((n)), Op2(0b101),     \
+         trap_debug_regs, reset_val, (DBGBCR0_EL1 + (n)), 0 },         \
+       /* DBGWVRn_EL1 */                                               \
+       { Op0(0b10), Op1(0b000), CRn(0b0000), CRm((n)), Op2(0b110),     \
+         trap_debug_regs, reset_val, (DBGWVR0_EL1 + (n)), 0 },         \
+       /* DBGWCRn_EL1 */                                               \
+       { Op0(0b10), Op1(0b000), CRn(0b0000), CRm((n)), Op2(0b111),     \
+         trap_debug_regs, reset_val, (DBGWCR0_EL1 + (n)), 0 }
+
 /*
  * Architected system registers.
  * Important: Must be sorted ascending by Op0, Op1, CRn, CRm, Op2
+ *
+ * We could trap ID_DFR0 and tell the guest we don't support performance
+ * monitoring.  Unfortunately the patch to make the kernel check ID_DFR0 was
+ * NAKed, so it will read the PMCR anyway.
+ *
+ * Therefore we tell the guest we have 0 counters.  Unfortunately, we
+ * must always support PMCCNTR (the cycle counter): we just RAZ/WI for
+ * all PM registers, which doesn't crash the guest kernel at least.
+ *
+ * Debug handling: We do trap most, if not all debug related system
+ * registers. The implementation is good enough to ensure that a guest
+ * can use these with minimal performance degradation. The drawback is
+ * that we don't implement any of the external debug, none of the
+ * OSlock protocol. This should be revisited if we ever encounter a
+ * more demanding guest...
  */
 static const struct sys_reg_desc sys_reg_descs[] = {
        /* DC ISW */
@@ -213,12 +303,71 @@ static const struct sys_reg_desc sys_reg_descs[] = {
        { Op0(0b01), Op1(0b000), CRn(0b0111), CRm(0b1110), Op2(0b010),
          access_dcsw },
 
+       DBG_BCR_BVR_WCR_WVR_EL1(0),
+       DBG_BCR_BVR_WCR_WVR_EL1(1),
+       /* MDCCINT_EL1 */
+       { Op0(0b10), Op1(0b000), CRn(0b0000), CRm(0b0010), Op2(0b000),
+         trap_debug_regs, reset_val, MDCCINT_EL1, 0 },
+       /* MDSCR_EL1 */
+       { Op0(0b10), Op1(0b000), CRn(0b0000), CRm(0b0010), Op2(0b010),
+         trap_debug_regs, reset_val, MDSCR_EL1, 0 },
+       DBG_BCR_BVR_WCR_WVR_EL1(2),
+       DBG_BCR_BVR_WCR_WVR_EL1(3),
+       DBG_BCR_BVR_WCR_WVR_EL1(4),
+       DBG_BCR_BVR_WCR_WVR_EL1(5),
+       DBG_BCR_BVR_WCR_WVR_EL1(6),
+       DBG_BCR_BVR_WCR_WVR_EL1(7),
+       DBG_BCR_BVR_WCR_WVR_EL1(8),
+       DBG_BCR_BVR_WCR_WVR_EL1(9),
+       DBG_BCR_BVR_WCR_WVR_EL1(10),
+       DBG_BCR_BVR_WCR_WVR_EL1(11),
+       DBG_BCR_BVR_WCR_WVR_EL1(12),
+       DBG_BCR_BVR_WCR_WVR_EL1(13),
+       DBG_BCR_BVR_WCR_WVR_EL1(14),
+       DBG_BCR_BVR_WCR_WVR_EL1(15),
+
+       /* MDRAR_EL1 */
+       { Op0(0b10), Op1(0b000), CRn(0b0001), CRm(0b0000), Op2(0b000),
+         trap_raz_wi },
+       /* OSLAR_EL1 */
+       { Op0(0b10), Op1(0b000), CRn(0b0001), CRm(0b0000), Op2(0b100),
+         trap_raz_wi },
+       /* OSLSR_EL1 */
+       { Op0(0b10), Op1(0b000), CRn(0b0001), CRm(0b0001), Op2(0b100),
+         trap_oslsr_el1 },
+       /* OSDLR_EL1 */
+       { Op0(0b10), Op1(0b000), CRn(0b0001), CRm(0b0011), Op2(0b100),
+         trap_raz_wi },
+       /* DBGPRCR_EL1 */
+       { Op0(0b10), Op1(0b000), CRn(0b0001), CRm(0b0100), Op2(0b100),
+         trap_raz_wi },
+       /* DBGCLAIMSET_EL1 */
+       { Op0(0b10), Op1(0b000), CRn(0b0111), CRm(0b1000), Op2(0b110),
+         trap_raz_wi },
+       /* DBGCLAIMCLR_EL1 */
+       { Op0(0b10), Op1(0b000), CRn(0b0111), CRm(0b1001), Op2(0b110),
+         trap_raz_wi },
+       /* DBGAUTHSTATUS_EL1 */
+       { Op0(0b10), Op1(0b000), CRn(0b0111), CRm(0b1110), Op2(0b110),
+         trap_dbgauthstatus_el1 },
+
        /* TEECR32_EL1 */
        { Op0(0b10), Op1(0b010), CRn(0b0000), CRm(0b0000), Op2(0b000),
          NULL, reset_val, TEECR32_EL1, 0 },
        /* TEEHBR32_EL1 */
        { Op0(0b10), Op1(0b010), CRn(0b0001), CRm(0b0000), Op2(0b000),
          NULL, reset_val, TEEHBR32_EL1, 0 },
+
+       /* MDCCSR_EL1 */
+       { Op0(0b10), Op1(0b011), CRn(0b0000), CRm(0b0001), Op2(0b000),
+         trap_raz_wi },
+       /* DBGDTR_EL0 */
+       { Op0(0b10), Op1(0b011), CRn(0b0000), CRm(0b0100), Op2(0b000),
+         trap_raz_wi },
+       /* DBGDTR[TR]X_EL0 */
+       { Op0(0b10), Op1(0b011), CRn(0b0000), CRm(0b0101), Op2(0b000),
+         trap_raz_wi },
+
        /* DBGVCR32_EL2 */
        { Op0(0b10), Op1(0b100), CRn(0b0000), CRm(0b0111), Op2(0b000),
          NULL, reset_val, DBGVCR32_EL2, 0 },
@@ -260,10 +409,10 @@ static const struct sys_reg_desc sys_reg_descs[] = {
 
        /* PMINTENSET_EL1 */
        { Op0(0b11), Op1(0b000), CRn(0b1001), CRm(0b1110), Op2(0b001),
-         pm_fake },
+         trap_raz_wi },
        /* PMINTENCLR_EL1 */
        { Op0(0b11), Op1(0b000), CRn(0b1001), CRm(0b1110), Op2(0b010),
-         pm_fake },
+         trap_raz_wi },
 
        /* MAIR_EL1 */
        { Op0(0b11), Op1(0b000), CRn(0b1010), CRm(0b0010), Op2(0b000),
@@ -292,43 +441,43 @@ static const struct sys_reg_desc sys_reg_descs[] = {
 
        /* PMCR_EL0 */
        { Op0(0b11), Op1(0b011), CRn(0b1001), CRm(0b1100), Op2(0b000),
-         pm_fake },
+         trap_raz_wi },
        /* PMCNTENSET_EL0 */
        { Op0(0b11), Op1(0b011), CRn(0b1001), CRm(0b1100), Op2(0b001),
-         pm_fake },
+         trap_raz_wi },
        /* PMCNTENCLR_EL0 */
        { Op0(0b11), Op1(0b011), CRn(0b1001), CRm(0b1100), Op2(0b010),
-         pm_fake },
+         trap_raz_wi },
        /* PMOVSCLR_EL0 */
        { Op0(0b11), Op1(0b011), CRn(0b1001), CRm(0b1100), Op2(0b011),
-         pm_fake },
+         trap_raz_wi },
        /* PMSWINC_EL0 */
        { Op0(0b11), Op1(0b011), CRn(0b1001), CRm(0b1100), Op2(0b100),
-         pm_fake },
+         trap_raz_wi },
        /* PMSELR_EL0 */
        { Op0(0b11), Op1(0b011), CRn(0b1001), CRm(0b1100), Op2(0b101),
-         pm_fake },
+         trap_raz_wi },
        /* PMCEID0_EL0 */
        { Op0(0b11), Op1(0b011), CRn(0b1001), CRm(0b1100), Op2(0b110),
-         pm_fake },
+         trap_raz_wi },
        /* PMCEID1_EL0 */
        { Op0(0b11), Op1(0b011), CRn(0b1001), CRm(0b1100), Op2(0b111),
-         pm_fake },
+         trap_raz_wi },
        /* PMCCNTR_EL0 */
        { Op0(0b11), Op1(0b011), CRn(0b1001), CRm(0b1101), Op2(0b000),
-         pm_fake },
+         trap_raz_wi },
        /* PMXEVTYPER_EL0 */
        { Op0(0b11), Op1(0b011), CRn(0b1001), CRm(0b1101), Op2(0b001),
-         pm_fake },
+         trap_raz_wi },
        /* PMXEVCNTR_EL0 */
        { Op0(0b11), Op1(0b011), CRn(0b1001), CRm(0b1101), Op2(0b010),
-         pm_fake },
+         trap_raz_wi },
        /* PMUSERENR_EL0 */
        { Op0(0b11), Op1(0b011), CRn(0b1001), CRm(0b1110), Op2(0b000),
-         pm_fake },
+         trap_raz_wi },
        /* PMOVSSET_EL0 */
        { Op0(0b11), Op1(0b011), CRn(0b1001), CRm(0b1110), Op2(0b011),
-         pm_fake },
+         trap_raz_wi },
 
        /* TPIDR_EL0 */
        { Op0(0b11), Op1(0b011), CRn(0b1101), CRm(0b0000), Op2(0b010),
@@ -348,13 +497,161 @@ static const struct sys_reg_desc sys_reg_descs[] = {
          NULL, reset_val, FPEXC32_EL2, 0x70 },
 };
 
+static bool trap_dbgidr(struct kvm_vcpu *vcpu,
+                       const struct sys_reg_params *p,
+                       const struct sys_reg_desc *r)
+{
+       if (p->is_write) {
+               return ignore_write(vcpu, p);
+       } else {
+               u64 dfr = read_cpuid(ID_AA64DFR0_EL1);
+               u64 pfr = read_cpuid(ID_AA64PFR0_EL1);
+               u32 el3 = !!((pfr >> 12) & 0xf);
+
+               *vcpu_reg(vcpu, p->Rt) = ((((dfr >> 20) & 0xf) << 28) |
+                                         (((dfr >> 12) & 0xf) << 24) |
+                                         (((dfr >> 28) & 0xf) << 20) |
+                                         (6 << 16) | (el3 << 14) | (el3 << 12));
+               return true;
+       }
+}
+
+static bool trap_debug32(struct kvm_vcpu *vcpu,
+                        const struct sys_reg_params *p,
+                        const struct sys_reg_desc *r)
+{
+       if (p->is_write) {
+               vcpu_cp14(vcpu, r->reg) = *vcpu_reg(vcpu, p->Rt);
+               vcpu->arch.debug_flags |= KVM_ARM64_DEBUG_DIRTY;
+       } else {
+               *vcpu_reg(vcpu, p->Rt) = vcpu_cp14(vcpu, r->reg);
+       }
+
+       return true;
+}
+
+#define DBG_BCR_BVR_WCR_WVR(n)                                 \
+       /* DBGBVRn */                                           \
+       { Op1( 0), CRn( 0), CRm((n)), Op2( 4), trap_debug32,    \
+         NULL, (cp14_DBGBVR0 + (n) * 2) },                     \
+       /* DBGBCRn */                                           \
+       { Op1( 0), CRn( 0), CRm((n)), Op2( 5), trap_debug32,    \
+         NULL, (cp14_DBGBCR0 + (n) * 2) },                     \
+       /* DBGWVRn */                                           \
+       { Op1( 0), CRn( 0), CRm((n)), Op2( 6), trap_debug32,    \
+         NULL, (cp14_DBGWVR0 + (n) * 2) },                     \
+       /* DBGWCRn */                                           \
+       { Op1( 0), CRn( 0), CRm((n)), Op2( 7), trap_debug32,    \
+         NULL, (cp14_DBGWCR0 + (n) * 2) }
+
+#define DBGBXVR(n)                                             \
+       { Op1( 0), CRn( 1), CRm((n)), Op2( 1), trap_debug32,    \
+         NULL, cp14_DBGBXVR0 + n * 2 }
+
+/*
+ * Trapped cp14 registers. We generally ignore most of the external
+ * debug, on the principle that they don't really make sense to a
+ * guest. Revisit this one day, whould this principle change.
+ */
+static const struct sys_reg_desc cp14_regs[] = {
+       /* DBGIDR */
+       { Op1( 0), CRn( 0), CRm( 0), Op2( 0), trap_dbgidr },
+       /* DBGDTRRXext */
+       { Op1( 0), CRn( 0), CRm( 0), Op2( 2), trap_raz_wi },
+
+       DBG_BCR_BVR_WCR_WVR(0),
+       /* DBGDSCRint */
+       { Op1( 0), CRn( 0), CRm( 1), Op2( 0), trap_raz_wi },
+       DBG_BCR_BVR_WCR_WVR(1),
+       /* DBGDCCINT */
+       { Op1( 0), CRn( 0), CRm( 2), Op2( 0), trap_debug32 },
+       /* DBGDSCRext */
+       { Op1( 0), CRn( 0), CRm( 2), Op2( 2), trap_debug32 },
+       DBG_BCR_BVR_WCR_WVR(2),
+       /* DBGDTR[RT]Xint */
+       { Op1( 0), CRn( 0), CRm( 3), Op2( 0), trap_raz_wi },
+       /* DBGDTR[RT]Xext */
+       { Op1( 0), CRn( 0), CRm( 3), Op2( 2), trap_raz_wi },
+       DBG_BCR_BVR_WCR_WVR(3),
+       DBG_BCR_BVR_WCR_WVR(4),
+       DBG_BCR_BVR_WCR_WVR(5),
+       /* DBGWFAR */
+       { Op1( 0), CRn( 0), CRm( 6), Op2( 0), trap_raz_wi },
+       /* DBGOSECCR */
+       { Op1( 0), CRn( 0), CRm( 6), Op2( 2), trap_raz_wi },
+       DBG_BCR_BVR_WCR_WVR(6),
+       /* DBGVCR */
+       { Op1( 0), CRn( 0), CRm( 7), Op2( 0), trap_debug32 },
+       DBG_BCR_BVR_WCR_WVR(7),
+       DBG_BCR_BVR_WCR_WVR(8),
+       DBG_BCR_BVR_WCR_WVR(9),
+       DBG_BCR_BVR_WCR_WVR(10),
+       DBG_BCR_BVR_WCR_WVR(11),
+       DBG_BCR_BVR_WCR_WVR(12),
+       DBG_BCR_BVR_WCR_WVR(13),
+       DBG_BCR_BVR_WCR_WVR(14),
+       DBG_BCR_BVR_WCR_WVR(15),
+
+       /* DBGDRAR (32bit) */
+       { Op1( 0), CRn( 1), CRm( 0), Op2( 0), trap_raz_wi },
+
+       DBGBXVR(0),
+       /* DBGOSLAR */
+       { Op1( 0), CRn( 1), CRm( 0), Op2( 4), trap_raz_wi },
+       DBGBXVR(1),
+       /* DBGOSLSR */
+       { Op1( 0), CRn( 1), CRm( 1), Op2( 4), trap_oslsr_el1 },
+       DBGBXVR(2),
+       DBGBXVR(3),
+       /* DBGOSDLR */
+       { Op1( 0), CRn( 1), CRm( 3), Op2( 4), trap_raz_wi },
+       DBGBXVR(4),
+       /* DBGPRCR */
+       { Op1( 0), CRn( 1), CRm( 4), Op2( 4), trap_raz_wi },
+       DBGBXVR(5),
+       DBGBXVR(6),
+       DBGBXVR(7),
+       DBGBXVR(8),
+       DBGBXVR(9),
+       DBGBXVR(10),
+       DBGBXVR(11),
+       DBGBXVR(12),
+       DBGBXVR(13),
+       DBGBXVR(14),
+       DBGBXVR(15),
+
+       /* DBGDSAR (32bit) */
+       { Op1( 0), CRn( 2), CRm( 0), Op2( 0), trap_raz_wi },
+
+       /* DBGDEVID2 */
+       { Op1( 0), CRn( 7), CRm( 0), Op2( 7), trap_raz_wi },
+       /* DBGDEVID1 */
+       { Op1( 0), CRn( 7), CRm( 1), Op2( 7), trap_raz_wi },
+       /* DBGDEVID */
+       { Op1( 0), CRn( 7), CRm( 2), Op2( 7), trap_raz_wi },
+       /* DBGCLAIMSET */
+       { Op1( 0), CRn( 7), CRm( 8), Op2( 6), trap_raz_wi },
+       /* DBGCLAIMCLR */
+       { Op1( 0), CRn( 7), CRm( 9), Op2( 6), trap_raz_wi },
+       /* DBGAUTHSTATUS */
+       { Op1( 0), CRn( 7), CRm(14), Op2( 6), trap_dbgauthstatus_el1 },
+};
+
+/* Trapped cp14 64bit registers */
+static const struct sys_reg_desc cp14_64_regs[] = {
+       /* DBGDRAR (64bit) */
+       { Op1( 0), CRm( 1), .access = trap_raz_wi },
+
+       /* DBGDSAR (64bit) */
+       { Op1( 0), CRm( 2), .access = trap_raz_wi },
+};
+
 /*
  * Trapped cp15 registers. TTBR0/TTBR1 get a double encoding,
  * depending on the way they are accessed (as a 32bit or a 64bit
  * register).
  */
 static const struct sys_reg_desc cp15_regs[] = {
-       { Op1( 0), CRn( 0), CRm( 2), Op2( 0), access_vm_reg, NULL, c2_TTBR0 },
        { Op1( 0), CRn( 1), CRm( 0), Op2( 0), access_sctlr, NULL, c1_SCTLR },
        { Op1( 0), CRn( 2), CRm( 0), Op2( 0), access_vm_reg, NULL, c2_TTBR0 },
        { Op1( 0), CRn( 2), CRm( 0), Op2( 1), access_vm_reg, NULL, c2_TTBR1 },
@@ -374,26 +671,30 @@ static const struct sys_reg_desc cp15_regs[] = {
        { Op1( 0), CRn( 7), CRm(10), Op2( 2), access_dcsw },
        { Op1( 0), CRn( 7), CRm(14), Op2( 2), access_dcsw },
 
-       { Op1( 0), CRn( 9), CRm(12), Op2( 0), pm_fake },
-       { Op1( 0), CRn( 9), CRm(12), Op2( 1), pm_fake },
-       { Op1( 0), CRn( 9), CRm(12), Op2( 2), pm_fake },
-       { Op1( 0), CRn( 9), CRm(12), Op2( 3), pm_fake },
-       { Op1( 0), CRn( 9), CRm(12), Op2( 5), pm_fake },
-       { Op1( 0), CRn( 9), CRm(12), Op2( 6), pm_fake },
-       { Op1( 0), CRn( 9), CRm(12), Op2( 7), pm_fake },
-       { Op1( 0), CRn( 9), CRm(13), Op2( 0), pm_fake },
-       { Op1( 0), CRn( 9), CRm(13), Op2( 1), pm_fake },
-       { Op1( 0), CRn( 9), CRm(13), Op2( 2), pm_fake },
-       { Op1( 0), CRn( 9), CRm(14), Op2( 0), pm_fake },
-       { Op1( 0), CRn( 9), CRm(14), Op2( 1), pm_fake },
-       { Op1( 0), CRn( 9), CRm(14), Op2( 2), pm_fake },
+       /* PMU */
+       { Op1( 0), CRn( 9), CRm(12), Op2( 0), trap_raz_wi },
+       { Op1( 0), CRn( 9), CRm(12), Op2( 1), trap_raz_wi },
+       { Op1( 0), CRn( 9), CRm(12), Op2( 2), trap_raz_wi },
+       { Op1( 0), CRn( 9), CRm(12), Op2( 3), trap_raz_wi },
+       { Op1( 0), CRn( 9), CRm(12), Op2( 5), trap_raz_wi },
+       { Op1( 0), CRn( 9), CRm(12), Op2( 6), trap_raz_wi },
+       { Op1( 0), CRn( 9), CRm(12), Op2( 7), trap_raz_wi },
+       { Op1( 0), CRn( 9), CRm(13), Op2( 0), trap_raz_wi },
+       { Op1( 0), CRn( 9), CRm(13), Op2( 1), trap_raz_wi },
+       { Op1( 0), CRn( 9), CRm(13), Op2( 2), trap_raz_wi },
+       { Op1( 0), CRn( 9), CRm(14), Op2( 0), trap_raz_wi },
+       { Op1( 0), CRn( 9), CRm(14), Op2( 1), trap_raz_wi },
+       { Op1( 0), CRn( 9), CRm(14), Op2( 2), trap_raz_wi },
 
        { Op1( 0), CRn(10), CRm( 2), Op2( 0), access_vm_reg, NULL, c10_PRRR },
        { Op1( 0), CRn(10), CRm( 2), Op2( 1), access_vm_reg, NULL, c10_NMRR },
        { Op1( 0), CRn(10), CRm( 3), Op2( 0), access_vm_reg, NULL, c10_AMAIR0 },
        { Op1( 0), CRn(10), CRm( 3), Op2( 1), access_vm_reg, NULL, c10_AMAIR1 },
        { Op1( 0), CRn(13), CRm( 0), Op2( 1), access_vm_reg, NULL, c13_CID },
+};
 
+static const struct sys_reg_desc cp15_64_regs[] = {
+       { Op1( 0), CRn( 0), CRm( 2), Op2( 0), access_vm_reg, NULL, c2_TTBR0 },
        { Op1( 1), CRn( 0), CRm( 2), Op2( 0), access_vm_reg, NULL, c2_TTBR1 },
 };
 
@@ -454,26 +755,29 @@ int kvm_handle_cp14_load_store(struct kvm_vcpu *vcpu, struct kvm_run *run)
        return 1;
 }
 
-int kvm_handle_cp14_access(struct kvm_vcpu *vcpu, struct kvm_run *run)
-{
-       kvm_inject_undefined(vcpu);
-       return 1;
-}
-
-static void emulate_cp15(struct kvm_vcpu *vcpu,
-                        const struct sys_reg_params *params)
+/*
+ * emulate_cp --  tries to match a sys_reg access in a handling table, and
+ *                call the corresponding trap handler.
+ *
+ * @params: pointer to the descriptor of the access
+ * @table: array of trap descriptors
+ * @num: size of the trap descriptor array
+ *
+ * Return 0 if the access has been handled, and -1 if not.
+ */
+static int emulate_cp(struct kvm_vcpu *vcpu,
+                     const struct sys_reg_params *params,
+                     const struct sys_reg_desc *table,
+                     size_t num)
 {
-       size_t num;
-       const struct sys_reg_desc *table, *r;
+       const struct sys_reg_desc *r;
 
-       table = get_target_table(vcpu->arch.target, false, &num);
+       if (!table)
+               return -1;      /* Not handled */
 
-       /* Search target-specific then generic table. */
        r = find_reg(params, table, num);
-       if (!r)
-               r = find_reg(params, cp15_regs, ARRAY_SIZE(cp15_regs));
 
-       if (likely(r)) {
+       if (r) {
                /*
                 * Not having an accessor means that we have
                 * configured a trap that we don't know how to
@@ -485,22 +789,51 @@ static void emulate_cp15(struct kvm_vcpu *vcpu,
                if (likely(r->access(vcpu, params, r))) {
                        /* Skip instruction, since it was emulated */
                        kvm_skip_instr(vcpu, kvm_vcpu_trap_il_is32bit(vcpu));
-                       return;
                }
-               /* If access function fails, it should complain. */
+
+               /* Handled */
+               return 0;
        }
 
-       kvm_err("Unsupported guest CP15 access at: %08lx\n", *vcpu_pc(vcpu));
+       /* Not handled */
+       return -1;
+}
+
+static void unhandled_cp_access(struct kvm_vcpu *vcpu,
+                               struct sys_reg_params *params)
+{
+       u8 hsr_ec = kvm_vcpu_trap_get_class(vcpu);
+       int cp;
+
+       switch(hsr_ec) {
+       case ESR_EL2_EC_CP15_32:
+       case ESR_EL2_EC_CP15_64:
+               cp = 15;
+               break;
+       case ESR_EL2_EC_CP14_MR:
+       case ESR_EL2_EC_CP14_64:
+               cp = 14;
+               break;
+       default:
+               WARN_ON((cp = -1));
+       }
+
+       kvm_err("Unsupported guest CP%d access at: %08lx\n",
+               cp, *vcpu_pc(vcpu));
        print_sys_reg_instr(params);
        kvm_inject_undefined(vcpu);
 }
 
 /**
- * kvm_handle_cp15_64 -- handles a mrrc/mcrr trap on a guest CP15 access
+ * kvm_handle_cp_64 -- handles a mrrc/mcrr trap on a guest CP15 access
  * @vcpu: The VCPU pointer
  * @run:  The kvm_run struct
  */
-int kvm_handle_cp15_64(struct kvm_vcpu *vcpu, struct kvm_run *run)
+static int kvm_handle_cp_64(struct kvm_vcpu *vcpu,
+                           const struct sys_reg_desc *global,
+                           size_t nr_global,
+                           const struct sys_reg_desc *target_specific,
+                           size_t nr_specific)
 {
        struct sys_reg_params params;
        u32 hsr = kvm_vcpu_get_hsr(vcpu);
@@ -529,8 +862,14 @@ int kvm_handle_cp15_64(struct kvm_vcpu *vcpu, struct kvm_run *run)
                *vcpu_reg(vcpu, params.Rt) = val;
        }
 
-       emulate_cp15(vcpu, &params);
+       if (!emulate_cp(vcpu, &params, target_specific, nr_specific))
+               goto out;
+       if (!emulate_cp(vcpu, &params, global, nr_global))
+               goto out;
 
+       unhandled_cp_access(vcpu, &params);
+
+out:
        /* Do the opposite hack for the read side */
        if (!params.is_write) {
                u64 val = *vcpu_reg(vcpu, params.Rt);
@@ -546,7 +885,11 @@ int kvm_handle_cp15_64(struct kvm_vcpu *vcpu, struct kvm_run *run)
  * @vcpu: The VCPU pointer
  * @run:  The kvm_run struct
  */
-int kvm_handle_cp15_32(struct kvm_vcpu *vcpu, struct kvm_run *run)
+static int kvm_handle_cp_32(struct kvm_vcpu *vcpu,
+                           const struct sys_reg_desc *global,
+                           size_t nr_global,
+                           const struct sys_reg_desc *target_specific,
+                           size_t nr_specific)
 {
        struct sys_reg_params params;
        u32 hsr = kvm_vcpu_get_hsr(vcpu);
@@ -561,10 +904,51 @@ int kvm_handle_cp15_32(struct kvm_vcpu *vcpu, struct kvm_run *run)
        params.Op1 = (hsr >> 14) & 0x7;
        params.Op2 = (hsr >> 17) & 0x7;
 
-       emulate_cp15(vcpu, &params);
+       if (!emulate_cp(vcpu, &params, target_specific, nr_specific))
+               return 1;
+       if (!emulate_cp(vcpu, &params, global, nr_global))
+               return 1;
+
+       unhandled_cp_access(vcpu, &params);
        return 1;
 }
 
+int kvm_handle_cp15_64(struct kvm_vcpu *vcpu, struct kvm_run *run)
+{
+       const struct sys_reg_desc *target_specific;
+       size_t num;
+
+       target_specific = get_target_table(vcpu->arch.target, false, &num);
+       return kvm_handle_cp_64(vcpu,
+                               cp15_64_regs, ARRAY_SIZE(cp15_64_regs),
+                               target_specific, num);
+}
+
+int kvm_handle_cp15_32(struct kvm_vcpu *vcpu, struct kvm_run *run)
+{
+       const struct sys_reg_desc *target_specific;
+       size_t num;
+
+       target_specific = get_target_table(vcpu->arch.target, false, &num);
+       return kvm_handle_cp_32(vcpu,
+                               cp15_regs, ARRAY_SIZE(cp15_regs),
+                               target_specific, num);
+}
+
+int kvm_handle_cp14_64(struct kvm_vcpu *vcpu, struct kvm_run *run)
+{
+       return kvm_handle_cp_64(vcpu,
+                               cp14_64_regs, ARRAY_SIZE(cp14_64_regs),
+                               NULL, 0);
+}
+
+int kvm_handle_cp14_32(struct kvm_vcpu *vcpu, struct kvm_run *run)
+{
+       return kvm_handle_cp_32(vcpu,
+                               cp14_regs, ARRAY_SIZE(cp14_regs),
+                               NULL, 0);
+}
+
 static int emulate_sys_reg(struct kvm_vcpu *vcpu,
                           const struct sys_reg_params *params)
 {
@@ -776,17 +1160,15 @@ static struct sys_reg_desc invariant_sys_regs[] = {
          NULL, get_ctr_el0 },
 };
 
-static int reg_from_user(void *val, const void __user *uaddr, u64 id)
+static int reg_from_user(u64 *val, const void __user *uaddr, u64 id)
 {
-       /* This Just Works because we are little endian. */
        if (copy_from_user(val, uaddr, KVM_REG_SIZE(id)) != 0)
                return -EFAULT;
        return 0;
 }
 
-static int reg_to_user(void __user *uaddr, const void *val, u64 id)
+static int reg_to_user(void __user *uaddr, const u64 *val, u64 id)
 {
-       /* This Just Works because we are little endian. */
        if (copy_to_user(uaddr, val, KVM_REG_SIZE(id)) != 0)
                return -EFAULT;
        return 0;
@@ -962,7 +1344,7 @@ static unsigned int num_demux_regs(void)
 
 static int write_demux_regids(u64 __user *uindices)
 {
-       u64 val = KVM_REG_ARM | KVM_REG_SIZE_U32 | KVM_REG_ARM_DEMUX;
+       u64 val = KVM_REG_ARM64 | KVM_REG_SIZE_U32 | KVM_REG_ARM_DEMUX;
        unsigned int i;
 
        val |= KVM_REG_ARM_DEMUX_ID_CCSIDR;
@@ -1069,14 +1451,32 @@ int kvm_arm_copy_sys_reg_indices(struct kvm_vcpu *vcpu, u64 __user *uindices)
        return write_demux_regids(uindices);
 }
 
+static int check_sysreg_table(const struct sys_reg_desc *table, unsigned int n)
+{
+       unsigned int i;
+
+       for (i = 1; i < n; i++) {
+               if (cmp_sys_reg(&table[i-1], &table[i]) >= 0) {
+                       kvm_err("sys_reg table %p out of order (%d)\n", table, i - 1);
+                       return 1;
+               }
+       }
+
+       return 0;
+}
+
 void kvm_sys_reg_table_init(void)
 {
        unsigned int i;
        struct sys_reg_desc clidr;
 
        /* Make sure tables are unique and in order. */
-       for (i = 1; i < ARRAY_SIZE(sys_reg_descs); i++)
-               BUG_ON(cmp_sys_reg(&sys_reg_descs[i-1], &sys_reg_descs[i]) >= 0);
+       BUG_ON(check_sysreg_table(sys_reg_descs, ARRAY_SIZE(sys_reg_descs)));
+       BUG_ON(check_sysreg_table(cp14_regs, ARRAY_SIZE(cp14_regs)));
+       BUG_ON(check_sysreg_table(cp14_64_regs, ARRAY_SIZE(cp14_64_regs)));
+       BUG_ON(check_sysreg_table(cp15_regs, ARRAY_SIZE(cp15_regs)));
+       BUG_ON(check_sysreg_table(cp15_64_regs, ARRAY_SIZE(cp15_64_regs)));
+       BUG_ON(check_sysreg_table(invariant_sys_regs, ARRAY_SIZE(invariant_sys_regs)));
 
        /* We abuse the reset function to overwrite the table itself. */
        for (i = 0; i < ARRAY_SIZE(invariant_sys_regs); i++)
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
diff --git a/arch/arm64/kvm/vgic-v3-switch.S b/arch/arm64/kvm/vgic-v3-switch.S
new file mode 100644 (file)
index 0000000..d160469
--- /dev/null
@@ -0,0 +1,267 @@
+/*
+ * 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-v3.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>
+
+       .text
+       .pushsection    .hyp.text, "ax"
+
+/*
+ * We store LRs in reverse order to let the CPU deal with streaming
+ * access. Use this macro to make it look saner...
+ */
+#define LR_OFFSET(n)   (VGIC_V3_CPU_LR + (15 - n) * 8)
+
+/*
+ * Save the VGIC CPU state into memory
+ * x0: Register pointing to VCPU struct
+ * Do not corrupt x1!!!
+ */
+.macro save_vgic_v3_state
+       // Compute the address of struct vgic_cpu
+       add     x3, x0, #VCPU_VGIC_CPU
+
+       // Make sure stores to the GIC via the memory mapped interface
+       // are now visible to the system register interface
+       dsb     st
+
+       // Save all interesting registers
+       mrs_s   x4, ICH_HCR_EL2
+       mrs_s   x5, ICH_VMCR_EL2
+       mrs_s   x6, ICH_MISR_EL2
+       mrs_s   x7, ICH_EISR_EL2
+       mrs_s   x8, ICH_ELSR_EL2
+
+       str     w4, [x3, #VGIC_V3_CPU_HCR]
+       str     w5, [x3, #VGIC_V3_CPU_VMCR]
+       str     w6, [x3, #VGIC_V3_CPU_MISR]
+       str     w7, [x3, #VGIC_V3_CPU_EISR]
+       str     w8, [x3, #VGIC_V3_CPU_ELRSR]
+
+       msr_s   ICH_HCR_EL2, xzr
+
+       mrs_s   x21, ICH_VTR_EL2
+       mvn     w22, w21
+       ubfiz   w23, w22, 2, 4  // w23 = (15 - ListRegs) * 4
+
+       adr     x24, 1f
+       add     x24, x24, x23
+       br      x24
+
+1:
+       mrs_s   x20, ICH_LR15_EL2
+       mrs_s   x19, ICH_LR14_EL2
+       mrs_s   x18, ICH_LR13_EL2
+       mrs_s   x17, ICH_LR12_EL2
+       mrs_s   x16, ICH_LR11_EL2
+       mrs_s   x15, ICH_LR10_EL2
+       mrs_s   x14, ICH_LR9_EL2
+       mrs_s   x13, ICH_LR8_EL2
+       mrs_s   x12, ICH_LR7_EL2
+       mrs_s   x11, ICH_LR6_EL2
+       mrs_s   x10, ICH_LR5_EL2
+       mrs_s   x9, ICH_LR4_EL2
+       mrs_s   x8, ICH_LR3_EL2
+       mrs_s   x7, ICH_LR2_EL2
+       mrs_s   x6, ICH_LR1_EL2
+       mrs_s   x5, ICH_LR0_EL2
+
+       adr     x24, 1f
+       add     x24, x24, x23
+       br      x24
+
+1:
+       str     x20, [x3, #LR_OFFSET(15)]
+       str     x19, [x3, #LR_OFFSET(14)]
+       str     x18, [x3, #LR_OFFSET(13)]
+       str     x17, [x3, #LR_OFFSET(12)]
+       str     x16, [x3, #LR_OFFSET(11)]
+       str     x15, [x3, #LR_OFFSET(10)]
+       str     x14, [x3, #LR_OFFSET(9)]
+       str     x13, [x3, #LR_OFFSET(8)]
+       str     x12, [x3, #LR_OFFSET(7)]
+       str     x11, [x3, #LR_OFFSET(6)]
+       str     x10, [x3, #LR_OFFSET(5)]
+       str     x9, [x3, #LR_OFFSET(4)]
+       str     x8, [x3, #LR_OFFSET(3)]
+       str     x7, [x3, #LR_OFFSET(2)]
+       str     x6, [x3, #LR_OFFSET(1)]
+       str     x5, [x3, #LR_OFFSET(0)]
+
+       tbnz    w21, #29, 6f    // 6 bits
+       tbz     w21, #30, 5f    // 5 bits
+                               // 7 bits
+       mrs_s   x20, ICH_AP0R3_EL2
+       str     w20, [x3, #(VGIC_V3_CPU_AP0R + 3*4)]
+       mrs_s   x19, ICH_AP0R2_EL2
+       str     w19, [x3, #(VGIC_V3_CPU_AP0R + 2*4)]
+6:     mrs_s   x18, ICH_AP0R1_EL2
+       str     w18, [x3, #(VGIC_V3_CPU_AP0R + 1*4)]
+5:     mrs_s   x17, ICH_AP0R0_EL2
+       str     w17, [x3, #VGIC_V3_CPU_AP0R]
+
+       tbnz    w21, #29, 6f    // 6 bits
+       tbz     w21, #30, 5f    // 5 bits
+                               // 7 bits
+       mrs_s   x20, ICH_AP1R3_EL2
+       str     w20, [x3, #(VGIC_V3_CPU_AP1R + 3*4)]
+       mrs_s   x19, ICH_AP1R2_EL2
+       str     w19, [x3, #(VGIC_V3_CPU_AP1R + 2*4)]
+6:     mrs_s   x18, ICH_AP1R1_EL2
+       str     w18, [x3, #(VGIC_V3_CPU_AP1R + 1*4)]
+5:     mrs_s   x17, ICH_AP1R0_EL2
+       str     w17, [x3, #VGIC_V3_CPU_AP1R]
+
+       // Restore SRE_EL1 access and re-enable SRE at EL1.
+       mrs_s   x5, ICC_SRE_EL2
+       orr     x5, x5, #ICC_SRE_EL2_ENABLE
+       msr_s   ICC_SRE_EL2, x5
+       isb
+       mov     x5, #1
+       msr_s   ICC_SRE_EL1, x5
+.endm
+
+/*
+ * Restore the VGIC CPU state from memory
+ * x0: Register pointing to VCPU struct
+ */
+.macro restore_vgic_v3_state
+       // Disable SRE_EL1 access. Necessary, otherwise
+       // ICH_VMCR_EL2.VFIQEn becomes one, and FIQ happens...
+       msr_s   ICC_SRE_EL1, xzr
+       isb
+
+       // Compute the address of struct vgic_cpu
+       add     x3, x0, #VCPU_VGIC_CPU
+
+       // Restore all interesting registers
+       ldr     w4, [x3, #VGIC_V3_CPU_HCR]
+       ldr     w5, [x3, #VGIC_V3_CPU_VMCR]
+
+       msr_s   ICH_HCR_EL2, x4
+       msr_s   ICH_VMCR_EL2, x5
+
+       mrs_s   x21, ICH_VTR_EL2
+
+       tbnz    w21, #29, 6f    // 6 bits
+       tbz     w21, #30, 5f    // 5 bits
+                               // 7 bits
+       ldr     w20, [x3, #(VGIC_V3_CPU_AP1R + 3*4)]
+       msr_s   ICH_AP1R3_EL2, x20
+       ldr     w19, [x3, #(VGIC_V3_CPU_AP1R + 2*4)]
+       msr_s   ICH_AP1R2_EL2, x19
+6:     ldr     w18, [x3, #(VGIC_V3_CPU_AP1R + 1*4)]
+       msr_s   ICH_AP1R1_EL2, x18
+5:     ldr     w17, [x3, #VGIC_V3_CPU_AP1R]
+       msr_s   ICH_AP1R0_EL2, x17
+
+       tbnz    w21, #29, 6f    // 6 bits
+       tbz     w21, #30, 5f    // 5 bits
+                               // 7 bits
+       ldr     w20, [x3, #(VGIC_V3_CPU_AP0R + 3*4)]
+       msr_s   ICH_AP0R3_EL2, x20
+       ldr     w19, [x3, #(VGIC_V3_CPU_AP0R + 2*4)]
+       msr_s   ICH_AP0R2_EL2, x19
+6:     ldr     w18, [x3, #(VGIC_V3_CPU_AP0R + 1*4)]
+       msr_s   ICH_AP0R1_EL2, x18
+5:     ldr     w17, [x3, #VGIC_V3_CPU_AP0R]
+       msr_s   ICH_AP0R0_EL2, x17
+
+       and     w22, w21, #0xf
+       mvn     w22, w21
+       ubfiz   w23, w22, 2, 4  // w23 = (15 - ListRegs) * 4
+
+       adr     x24, 1f
+       add     x24, x24, x23
+       br      x24
+
+1:
+       ldr     x20, [x3, #LR_OFFSET(15)]
+       ldr     x19, [x3, #LR_OFFSET(14)]
+       ldr     x18, [x3, #LR_OFFSET(13)]
+       ldr     x17, [x3, #LR_OFFSET(12)]
+       ldr     x16, [x3, #LR_OFFSET(11)]
+       ldr     x15, [x3, #LR_OFFSET(10)]
+       ldr     x14, [x3, #LR_OFFSET(9)]
+       ldr     x13, [x3, #LR_OFFSET(8)]
+       ldr     x12, [x3, #LR_OFFSET(7)]
+       ldr     x11, [x3, #LR_OFFSET(6)]
+       ldr     x10, [x3, #LR_OFFSET(5)]
+       ldr     x9, [x3, #LR_OFFSET(4)]
+       ldr     x8, [x3, #LR_OFFSET(3)]
+       ldr     x7, [x3, #LR_OFFSET(2)]
+       ldr     x6, [x3, #LR_OFFSET(1)]
+       ldr     x5, [x3, #LR_OFFSET(0)]
+
+       adr     x24, 1f
+       add     x24, x24, x23
+       br      x24
+
+1:
+       msr_s   ICH_LR15_EL2, x20
+       msr_s   ICH_LR14_EL2, x19
+       msr_s   ICH_LR13_EL2, x18
+       msr_s   ICH_LR12_EL2, x17
+       msr_s   ICH_LR11_EL2, x16
+       msr_s   ICH_LR10_EL2, x15
+       msr_s   ICH_LR9_EL2,  x14
+       msr_s   ICH_LR8_EL2,  x13
+       msr_s   ICH_LR7_EL2,  x12
+       msr_s   ICH_LR6_EL2,  x11
+       msr_s   ICH_LR5_EL2,  x10
+       msr_s   ICH_LR4_EL2,   x9
+       msr_s   ICH_LR3_EL2,   x8
+       msr_s   ICH_LR2_EL2,   x7
+       msr_s   ICH_LR1_EL2,   x6
+       msr_s   ICH_LR0_EL2,   x5
+
+       // Ensure that the above will have reached the
+       // (re)distributors. This ensure the guest will read
+       // the correct values from the memory-mapped interface.
+       isb
+       dsb     sy
+
+       // Prevent the guest from touching the GIC system registers
+       mrs_s   x5, ICC_SRE_EL2
+       and     x5, x5, #~ICC_SRE_EL2_ENABLE
+       msr_s   ICC_SRE_EL2, x5
+.endm
+
+ENTRY(__save_vgic_v3_state)
+       save_vgic_v3_state
+       ret
+ENDPROC(__save_vgic_v3_state)
+
+ENTRY(__restore_vgic_v3_state)
+       restore_vgic_v3_state
+       ret
+ENDPROC(__restore_vgic_v3_state)
+
+ENTRY(__vgic_v3_get_ich_vtr_el2)
+       mrs_s   x0, ICH_VTR_EL2
+       ret
+ENDPROC(__vgic_v3_get_ich_vtr_el2)
+
+       .popsection
index 6d9aedd..ad9db60 100644 (file)
@@ -67,6 +67,10 @@ void kvm_timer_vcpu_init(struct kvm_vcpu *vcpu);
 void kvm_timer_flush_hwstate(struct kvm_vcpu *vcpu);
 void kvm_timer_sync_hwstate(struct kvm_vcpu *vcpu);
 void kvm_timer_vcpu_terminate(struct kvm_vcpu *vcpu);
+
+u64 kvm_arm_timer_get_reg(struct kvm_vcpu *, u64 regid);
+int kvm_arm_timer_set_reg(struct kvm_vcpu *, u64 regid, u64 value);
+
 #else
 static inline int kvm_timer_hyp_init(void)
 {
@@ -84,6 +88,16 @@ static inline void kvm_timer_vcpu_init(struct kvm_vcpu *vcpu) {}
 static inline void kvm_timer_flush_hwstate(struct kvm_vcpu *vcpu) {}
 static inline void kvm_timer_sync_hwstate(struct kvm_vcpu *vcpu) {}
 static inline void kvm_timer_vcpu_terminate(struct kvm_vcpu *vcpu) {}
+
+static inline int kvm_arm_timer_set_reg(struct kvm_vcpu *vcpu, u64 regid, u64 value)
+{
+       return 0;
+}
+
+static inline u64 kvm_arm_timer_get_reg(struct kvm_vcpu *vcpu, u64 regid)
+{
+       return 0;
+}
 #endif
 
 #endif
index f27000f..35b0c12 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
@@ -32,7 +31,9 @@
 #define VGIC_NR_PRIVATE_IRQS   (VGIC_NR_SGIS + VGIC_NR_PPIS)
 #define VGIC_NR_SHARED_IRQS    (VGIC_NR_IRQS - VGIC_NR_PRIVATE_IRQS)
 #define VGIC_MAX_CPUS          KVM_MAX_VCPUS
-#define VGIC_MAX_LRS           (1 << 6)
+
+#define VGIC_V2_MAX_LRS                (1 << 6)
+#define VGIC_V3_MAX_LRS                16
 
 /* Sanity checks... */
 #if (VGIC_MAX_CPUS > 8)
@@ -68,9 +69,62 @@ struct vgic_bytemap {
        u32 shared[VGIC_NR_SHARED_IRQS  / 4];
 };
 
+struct kvm_vcpu;
+
+enum vgic_type {
+       VGIC_V2,                /* Good ol' GICv2 */
+       VGIC_V3,                /* New fancy GICv3 */
+};
+
+#define LR_STATE_PENDING       (1 << 0)
+#define LR_STATE_ACTIVE                (1 << 1)
+#define LR_STATE_MASK          (3 << 0)
+#define LR_EOI_INT             (1 << 2)
+
+struct vgic_lr {
+       u16     irq;
+       u8      source;
+       u8      state;
+};
+
+struct vgic_vmcr {
+       u32     ctlr;
+       u32     abpr;
+       u32     bpr;
+       u32     pmr;
+};
+
+struct vgic_ops {
+       struct vgic_lr  (*get_lr)(const struct kvm_vcpu *, int);
+       void    (*set_lr)(struct kvm_vcpu *, int, struct vgic_lr);
+       void    (*sync_lr_elrsr)(struct kvm_vcpu *, int, struct vgic_lr);
+       u64     (*get_elrsr)(const struct kvm_vcpu *vcpu);
+       u64     (*get_eisr)(const struct kvm_vcpu *vcpu);
+       u32     (*get_interrupt_status)(const struct kvm_vcpu *vcpu);
+       void    (*enable_underflow)(struct kvm_vcpu *vcpu);
+       void    (*disable_underflow)(struct kvm_vcpu *vcpu);
+       void    (*get_vmcr)(struct kvm_vcpu *vcpu, struct vgic_vmcr *vmcr);
+       void    (*set_vmcr)(struct kvm_vcpu *vcpu, struct vgic_vmcr *vmcr);
+       void    (*enable)(struct kvm_vcpu *vcpu);
+};
+
+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 */
+       u32             nr_lr;
+       /* Interrupt number */
+       unsigned int    maint_irq;
+       /* Virtual control interface base address */
+       void __iomem    *vctrl_base;
+};
+
 struct vgic_dist {
 #ifdef CONFIG_KVM_ARM_VGIC
        spinlock_t              lock;
+       bool                    in_kernel;
        bool                    ready;
 
        /* Virtual control interface mapping */
@@ -110,6 +164,29 @@ struct vgic_dist {
 #endif
 };
 
+struct vgic_v2_cpu_if {
+       u32             vgic_hcr;
+       u32             vgic_vmcr;
+       u32             vgic_misr;      /* Saved only */
+       u32             vgic_eisr[2];   /* Saved only */
+       u32             vgic_elrsr[2];  /* Saved only */
+       u32             vgic_apr;
+       u32             vgic_lr[VGIC_V2_MAX_LRS];
+};
+
+struct vgic_v3_cpu_if {
+#ifdef CONFIG_ARM_GIC_V3
+       u32             vgic_hcr;
+       u32             vgic_vmcr;
+       u32             vgic_misr;      /* Saved only */
+       u32             vgic_eisr;      /* Saved only */
+       u32             vgic_elrsr;     /* Saved only */
+       u32             vgic_ap0r[4];
+       u32             vgic_ap1r[4];
+       u64             vgic_lr[VGIC_V3_MAX_LRS];
+#endif
+};
+
 struct vgic_cpu {
 #ifdef CONFIG_KVM_ARM_VGIC
        /* per IRQ to LR mapping */
@@ -120,24 +197,24 @@ struct vgic_cpu {
        DECLARE_BITMAP( pending_shared, VGIC_NR_SHARED_IRQS);
 
        /* Bitmap of used/free list registers */
-       DECLARE_BITMAP( lr_used, VGIC_MAX_LRS);
+       DECLARE_BITMAP( lr_used, VGIC_V2_MAX_LRS);
 
        /* Number of list registers on this CPU */
        int             nr_lr;
 
        /* CPU vif control registers for world switch */
-       u32             vgic_hcr;
-       u32             vgic_vmcr;
-       u32             vgic_misr;      /* Saved only */
-       u32             vgic_eisr[2];   /* Saved only */
-       u32             vgic_elrsr[2];  /* Saved only */
-       u32             vgic_apr;
-       u32             vgic_lr[VGIC_MAX_LRS];
+       union {
+               struct vgic_v2_cpu_if   vgic_v2;
+               struct vgic_v3_cpu_if   vgic_v3;
+       };
 #endif
 };
 
 #define LR_EMPTY       0xff
 
+#define INT_STATUS_EOI         (1 << 0)
+#define INT_STATUS_UNDERFLOW   (1 << 1)
+
 struct kvm;
 struct kvm_vcpu;
 struct kvm_run;
@@ -157,9 +234,25 @@ int kvm_vgic_vcpu_pending_irq(struct kvm_vcpu *vcpu);
 bool vgic_handle_mmio(struct kvm_vcpu *vcpu, struct kvm_run *run,
                      struct kvm_exit_mmio *mmio);
 
-#define irqchip_in_kernel(k)   (!!((k)->arch.vgic.vctrl_base))
+#define irqchip_in_kernel(k)   (!!((k)->arch.vgic.in_kernel))
 #define vgic_initialized(k)    ((k)->arch.vgic.ready)
 
+int vgic_v2_probe(struct device_node *vgic_node,
+                 const struct vgic_ops **ops,
+                 const struct vgic_params **params);
+#ifdef CONFIG_ARM_GIC_V3
+int vgic_v3_probe(struct device_node *vgic_node,
+                 const struct vgic_ops **ops,
+                 const struct vgic_params **params);
+#else
+static inline int vgic_v3_probe(struct device_node *vgic_node,
+                               const struct vgic_ops **ops,
+                               const struct vgic_params **params)
+{
+       return -ENODEV;
+}
+#endif
+
 #else
 static inline int kvm_vgic_hyp_init(void)
 {
diff --git a/virt/kvm/arm/vgic-v2.c b/virt/kvm/arm/vgic-v2.c
new file mode 100644 (file)
index 0000000..01124ef
--- /dev/null
@@ -0,0 +1,265 @@
+/*
+ * Copyright (C) 2012,2013 ARM Limited, All Rights Reserved.
+ * 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/cpu.h>
+#include <linux/kvm.h>
+#include <linux/kvm_host.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+
+#include <linux/irqchip/arm-gic.h>
+
+#include <asm/kvm_emulate.h>
+#include <asm/kvm_arm.h>
+#include <asm/kvm_mmu.h>
+
+static struct vgic_lr vgic_v2_get_lr(const struct kvm_vcpu *vcpu, int lr)
+{
+       struct vgic_lr lr_desc;
+       u32 val = vcpu->arch.vgic_cpu.vgic_v2.vgic_lr[lr];
+
+       lr_desc.irq     = val & GICH_LR_VIRTUALID;
+       if (lr_desc.irq <= 15)
+               lr_desc.source  = (val >> GICH_LR_PHYSID_CPUID_SHIFT) & 0x7;
+       else
+               lr_desc.source = 0;
+       lr_desc.state   = 0;
+
+       if (val & GICH_LR_PENDING_BIT)
+               lr_desc.state |= LR_STATE_PENDING;
+       if (val & GICH_LR_ACTIVE_BIT)
+               lr_desc.state |= LR_STATE_ACTIVE;
+       if (val & GICH_LR_EOI)
+               lr_desc.state |= LR_EOI_INT;
+
+       return lr_desc;
+}
+
+static void vgic_v2_set_lr(struct kvm_vcpu *vcpu, int lr,
+                          struct vgic_lr lr_desc)
+{
+       u32 lr_val = (lr_desc.source << GICH_LR_PHYSID_CPUID_SHIFT) | lr_desc.irq;
+
+       if (lr_desc.state & LR_STATE_PENDING)
+               lr_val |= GICH_LR_PENDING_BIT;
+       if (lr_desc.state & LR_STATE_ACTIVE)
+               lr_val |= GICH_LR_ACTIVE_BIT;
+       if (lr_desc.state & LR_EOI_INT)
+               lr_val |= GICH_LR_EOI;
+
+       vcpu->arch.vgic_cpu.vgic_v2.vgic_lr[lr] = lr_val;
+}
+
+static void vgic_v2_sync_lr_elrsr(struct kvm_vcpu *vcpu, int lr,
+                                 struct vgic_lr lr_desc)
+{
+       if (!(lr_desc.state & LR_STATE_MASK))
+               set_bit(lr, (unsigned long *)vcpu->arch.vgic_cpu.vgic_v2.vgic_elrsr);
+}
+
+static u64 vgic_v2_get_elrsr(const struct kvm_vcpu *vcpu)
+{
+       u64 val;
+
+#if BITS_PER_LONG == 64
+       val  = vcpu->arch.vgic_cpu.vgic_v2.vgic_elrsr[1];
+       val <<= 32;
+       val |= vcpu->arch.vgic_cpu.vgic_v2.vgic_elrsr[0];
+#else
+       val = *(u64 *)vcpu->arch.vgic_cpu.vgic_v2.vgic_elrsr;
+#endif
+       return val;
+}
+
+static u64 vgic_v2_get_eisr(const struct kvm_vcpu *vcpu)
+{
+       u64 val;
+
+#if BITS_PER_LONG == 64
+       val  = vcpu->arch.vgic_cpu.vgic_v2.vgic_eisr[1];
+       val <<= 32;
+       val |= vcpu->arch.vgic_cpu.vgic_v2.vgic_eisr[0];
+#else
+       val = *(u64 *)vcpu->arch.vgic_cpu.vgic_v2.vgic_eisr;
+#endif
+       return val;
+}
+
+static u32 vgic_v2_get_interrupt_status(const struct kvm_vcpu *vcpu)
+{
+       u32 misr = vcpu->arch.vgic_cpu.vgic_v2.vgic_misr;
+       u32 ret = 0;
+
+       if (misr & GICH_MISR_EOI)
+               ret |= INT_STATUS_EOI;
+       if (misr & GICH_MISR_U)
+               ret |= INT_STATUS_UNDERFLOW;
+
+       return ret;
+}
+
+static void vgic_v2_enable_underflow(struct kvm_vcpu *vcpu)
+{
+       vcpu->arch.vgic_cpu.vgic_v2.vgic_hcr |= GICH_HCR_UIE;
+}
+
+static void vgic_v2_disable_underflow(struct kvm_vcpu *vcpu)
+{
+       vcpu->arch.vgic_cpu.vgic_v2.vgic_hcr &= ~GICH_HCR_UIE;
+}
+
+static void vgic_v2_get_vmcr(struct kvm_vcpu *vcpu, struct vgic_vmcr *vmcrp)
+{
+       u32 vmcr = vcpu->arch.vgic_cpu.vgic_v2.vgic_vmcr;
+
+       vmcrp->ctlr = (vmcr & GICH_VMCR_CTRL_MASK) >> GICH_VMCR_CTRL_SHIFT;
+       vmcrp->abpr = (vmcr & GICH_VMCR_ALIAS_BINPOINT_MASK) >> GICH_VMCR_ALIAS_BINPOINT_SHIFT;
+       vmcrp->bpr  = (vmcr & GICH_VMCR_BINPOINT_MASK) >> GICH_VMCR_BINPOINT_SHIFT;
+       vmcrp->pmr  = (vmcr & GICH_VMCR_PRIMASK_MASK) >> GICH_VMCR_PRIMASK_SHIFT;
+}
+
+static void vgic_v2_set_vmcr(struct kvm_vcpu *vcpu, struct vgic_vmcr *vmcrp)
+{
+       u32 vmcr;
+
+       vmcr  = (vmcrp->ctlr << GICH_VMCR_CTRL_SHIFT) & GICH_VMCR_CTRL_MASK;
+       vmcr |= (vmcrp->abpr << GICH_VMCR_ALIAS_BINPOINT_SHIFT) & GICH_VMCR_ALIAS_BINPOINT_MASK;
+       vmcr |= (vmcrp->bpr << GICH_VMCR_BINPOINT_SHIFT) & GICH_VMCR_BINPOINT_MASK;
+       vmcr |= (vmcrp->pmr << GICH_VMCR_PRIMASK_SHIFT) & GICH_VMCR_PRIMASK_MASK;
+
+       vcpu->arch.vgic_cpu.vgic_v2.vgic_vmcr = vmcr;
+}
+
+static void vgic_v2_enable(struct kvm_vcpu *vcpu)
+{
+       /*
+        * By forcing VMCR to zero, the GIC will restore the binary
+        * points to their reset values. Anything else resets to zero
+        * anyway.
+        */
+       vcpu->arch.vgic_cpu.vgic_v2.vgic_vmcr = 0;
+
+       /* Get the show on the road... */
+       vcpu->arch.vgic_cpu.vgic_v2.vgic_hcr = GICH_HCR_EN;
+}
+
+static const struct vgic_ops vgic_v2_ops = {
+       .get_lr                 = vgic_v2_get_lr,
+       .set_lr                 = vgic_v2_set_lr,
+       .sync_lr_elrsr          = vgic_v2_sync_lr_elrsr,
+       .get_elrsr              = vgic_v2_get_elrsr,
+       .get_eisr               = vgic_v2_get_eisr,
+       .get_interrupt_status   = vgic_v2_get_interrupt_status,
+       .enable_underflow       = vgic_v2_enable_underflow,
+       .disable_underflow      = vgic_v2_disable_underflow,
+       .get_vmcr               = vgic_v2_get_vmcr,
+       .set_vmcr               = vgic_v2_set_vmcr,
+       .enable                 = vgic_v2_enable,
+};
+
+static struct vgic_params vgic_v2_params;
+
+/**
+ * vgic_v2_probe - probe for a GICv2 compatible interrupt controller in DT
+ * @node:      pointer to the DT node
+ * @ops:       address of a pointer to the GICv2 operations
+ * @params:    address of a pointer to HW-specific parameters
+ *
+ * Returns 0 if a GICv2 has been found, with the low level operations
+ * in *ops and the HW parameters in *params. Returns an error code
+ * otherwise.
+ */
+int vgic_v2_probe(struct device_node *vgic_node,
+                 const struct vgic_ops **ops,
+                 const struct vgic_params **params)
+{
+       int ret;
+       struct resource vctrl_res;
+       struct resource vcpu_res;
+       struct vgic_params *vgic = &vgic_v2_params;
+
+       vgic->maint_irq = irq_of_parse_and_map(vgic_node, 0);
+       if (!vgic->maint_irq) {
+               kvm_err("error getting vgic maintenance irq from DT\n");
+               ret = -ENXIO;
+               goto out;
+       }
+
+       ret = of_address_to_resource(vgic_node, 2, &vctrl_res);
+       if (ret) {
+               kvm_err("Cannot obtain GICH resource\n");
+               goto out;
+       }
+
+       vgic->vctrl_base = of_iomap(vgic_node, 2);
+       if (!vgic->vctrl_base) {
+               kvm_err("Cannot ioremap GICH\n");
+               ret = -ENOMEM;
+               goto out;
+       }
+
+       vgic->nr_lr = readl_relaxed(vgic->vctrl_base + GICH_VTR);
+       vgic->nr_lr = (vgic->nr_lr & 0x3f) + 1;
+
+       ret = create_hyp_io_mappings(vgic->vctrl_base,
+                                    vgic->vctrl_base + resource_size(&vctrl_res),
+                                    vctrl_res.start);
+       if (ret) {
+               kvm_err("Cannot map VCTRL into hyp\n");
+               goto out_unmap;
+       }
+
+       if (of_address_to_resource(vgic_node, 3, &vcpu_res)) {
+               kvm_err("Cannot obtain GICV resource\n");
+               ret = -ENXIO;
+               goto out_unmap;
+       }
+
+       if (!PAGE_ALIGNED(vcpu_res.start)) {
+               kvm_err("GICV physical address 0x%llx not page aligned\n",
+                       (unsigned long long)vcpu_res.start);
+               ret = -ENXIO;
+               goto out_unmap;
+       }
+
+       if (!PAGE_ALIGNED(resource_size(&vcpu_res))) {
+               kvm_err("GICV size 0x%llx not a multiple of page size 0x%lx\n",
+                       (unsigned long long)resource_size(&vcpu_res),
+                       PAGE_SIZE);
+               ret = -ENXIO;
+               goto out_unmap;
+       }
+
+       vgic->vcpu_base = vcpu_res.start;
+
+       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;
+
+out_unmap:
+       iounmap(vgic->vctrl_base);
+out:
+       of_node_put(vgic_node);
+       return ret;
+}
diff --git a/virt/kvm/arm/vgic-v3.c b/virt/kvm/arm/vgic-v3.c
new file mode 100644 (file)
index 0000000..1c2c8ee
--- /dev/null
@@ -0,0 +1,247 @@
+/*
+ * Copyright (C) 2013 ARM Limited, All Rights Reserved.
+ * 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/cpu.h>
+#include <linux/kvm.h>
+#include <linux/kvm_host.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+
+#include <linux/irqchip/arm-gic-v3.h>
+
+#include <asm/kvm_emulate.h>
+#include <asm/kvm_arm.h>
+#include <asm/kvm_mmu.h>
+
+/* These are for GICv2 emulation only */
+#define GICH_LR_VIRTUALID              (0x3ffUL << 0)
+#define GICH_LR_PHYSID_CPUID_SHIFT     (10)
+#define GICH_LR_PHYSID_CPUID           (7UL << GICH_LR_PHYSID_CPUID_SHIFT)
+
+/*
+ * LRs are stored in reverse order in memory. make sure we index them
+ * correctly.
+ */
+#define LR_INDEX(lr)                   (VGIC_V3_MAX_LRS - 1 - lr)
+
+static u32 ich_vtr_el2;
+
+static struct vgic_lr vgic_v3_get_lr(const struct kvm_vcpu *vcpu, int lr)
+{
+       struct vgic_lr lr_desc;
+       u64 val = vcpu->arch.vgic_cpu.vgic_v3.vgic_lr[LR_INDEX(lr)];
+
+       lr_desc.irq     = val & GICH_LR_VIRTUALID;
+       if (lr_desc.irq <= 15)
+               lr_desc.source  = (val >> GICH_LR_PHYSID_CPUID_SHIFT) & 0x7;
+       else
+               lr_desc.source = 0;
+       lr_desc.state   = 0;
+
+       if (val & ICH_LR_PENDING_BIT)
+               lr_desc.state |= LR_STATE_PENDING;
+       if (val & ICH_LR_ACTIVE_BIT)
+               lr_desc.state |= LR_STATE_ACTIVE;
+       if (val & ICH_LR_EOI)
+               lr_desc.state |= LR_EOI_INT;
+
+       return lr_desc;
+}
+
+static void vgic_v3_set_lr(struct kvm_vcpu *vcpu, int lr,
+                          struct vgic_lr lr_desc)
+{
+       u64 lr_val = (((u32)lr_desc.source << GICH_LR_PHYSID_CPUID_SHIFT) |
+                     lr_desc.irq);
+
+       if (lr_desc.state & LR_STATE_PENDING)
+               lr_val |= ICH_LR_PENDING_BIT;
+       if (lr_desc.state & LR_STATE_ACTIVE)
+               lr_val |= ICH_LR_ACTIVE_BIT;
+       if (lr_desc.state & LR_EOI_INT)
+               lr_val |= ICH_LR_EOI;
+
+       vcpu->arch.vgic_cpu.vgic_v3.vgic_lr[LR_INDEX(lr)] = lr_val;
+}
+
+static void vgic_v3_sync_lr_elrsr(struct kvm_vcpu *vcpu, int lr,
+                                 struct vgic_lr lr_desc)
+{
+       if (!(lr_desc.state & LR_STATE_MASK))
+               vcpu->arch.vgic_cpu.vgic_v3.vgic_elrsr |= (1U << lr);
+}
+
+static u64 vgic_v3_get_elrsr(const struct kvm_vcpu *vcpu)
+{
+       return vcpu->arch.vgic_cpu.vgic_v3.vgic_elrsr;
+}
+
+static u64 vgic_v3_get_eisr(const struct kvm_vcpu *vcpu)
+{
+       return vcpu->arch.vgic_cpu.vgic_v3.vgic_eisr;
+}
+
+static u32 vgic_v3_get_interrupt_status(const struct kvm_vcpu *vcpu)
+{
+       u32 misr = vcpu->arch.vgic_cpu.vgic_v3.vgic_misr;
+       u32 ret = 0;
+
+       if (misr & ICH_MISR_EOI)
+               ret |= INT_STATUS_EOI;
+       if (misr & ICH_MISR_U)
+               ret |= INT_STATUS_UNDERFLOW;
+
+       return ret;
+}
+
+static void vgic_v3_get_vmcr(struct kvm_vcpu *vcpu, struct vgic_vmcr *vmcrp)
+{
+       u32 vmcr = vcpu->arch.vgic_cpu.vgic_v3.vgic_vmcr;
+
+       vmcrp->ctlr = (vmcr & ICH_VMCR_CTLR_MASK) >> ICH_VMCR_CTLR_SHIFT;
+       vmcrp->abpr = (vmcr & ICH_VMCR_BPR1_MASK) >> ICH_VMCR_BPR1_SHIFT;
+       vmcrp->bpr  = (vmcr & ICH_VMCR_BPR0_MASK) >> ICH_VMCR_BPR0_SHIFT;
+       vmcrp->pmr  = (vmcr & ICH_VMCR_PMR_MASK) >> ICH_VMCR_PMR_SHIFT;
+}
+
+static void vgic_v3_enable_underflow(struct kvm_vcpu *vcpu)
+{
+       vcpu->arch.vgic_cpu.vgic_v3.vgic_hcr |= ICH_HCR_UIE;
+}
+
+static void vgic_v3_disable_underflow(struct kvm_vcpu *vcpu)
+{
+       vcpu->arch.vgic_cpu.vgic_v3.vgic_hcr &= ~ICH_HCR_UIE;
+}
+
+static void vgic_v3_set_vmcr(struct kvm_vcpu *vcpu, struct vgic_vmcr *vmcrp)
+{
+       u32 vmcr;
+
+       vmcr  = (vmcrp->ctlr << ICH_VMCR_CTLR_SHIFT) & ICH_VMCR_CTLR_MASK;
+       vmcr |= (vmcrp->abpr << ICH_VMCR_BPR1_SHIFT) & ICH_VMCR_BPR1_MASK;
+       vmcr |= (vmcrp->bpr << ICH_VMCR_BPR0_SHIFT) & ICH_VMCR_BPR0_MASK;
+       vmcr |= (vmcrp->pmr << ICH_VMCR_PMR_SHIFT) & ICH_VMCR_PMR_MASK;
+
+       vcpu->arch.vgic_cpu.vgic_v3.vgic_vmcr = vmcr;
+}
+
+static void vgic_v3_enable(struct kvm_vcpu *vcpu)
+{
+       /*
+        * By forcing VMCR to zero, the GIC will restore the binary
+        * points to their reset values. Anything else resets to zero
+        * anyway.
+        */
+       vcpu->arch.vgic_cpu.vgic_v3.vgic_vmcr = 0;
+
+       /* Get the show on the road... */
+       vcpu->arch.vgic_cpu.vgic_v3.vgic_hcr = ICH_HCR_EN;
+}
+
+static const struct vgic_ops vgic_v3_ops = {
+       .get_lr                 = vgic_v3_get_lr,
+       .set_lr                 = vgic_v3_set_lr,
+       .sync_lr_elrsr          = vgic_v3_sync_lr_elrsr,
+       .get_elrsr              = vgic_v3_get_elrsr,
+       .get_eisr               = vgic_v3_get_eisr,
+       .get_interrupt_status   = vgic_v3_get_interrupt_status,
+       .enable_underflow       = vgic_v3_enable_underflow,
+       .disable_underflow      = vgic_v3_disable_underflow,
+       .get_vmcr               = vgic_v3_get_vmcr,
+       .set_vmcr               = vgic_v3_set_vmcr,
+       .enable                 = vgic_v3_enable,
+};
+
+static struct vgic_params vgic_v3_params;
+
+/**
+ * vgic_v3_probe - probe for a GICv3 compatible interrupt controller in DT
+ * @node:      pointer to the DT node
+ * @ops:       address of a pointer to the GICv3 operations
+ * @params:    address of a pointer to HW-specific parameters
+ *
+ * Returns 0 if a GICv3 has been found, with the low level operations
+ * in *ops and the HW parameters in *params. Returns an error code
+ * otherwise.
+ */
+int vgic_v3_probe(struct device_node *vgic_node,
+                 const struct vgic_ops **ops,
+                 const struct vgic_params **params)
+{
+       int ret = 0;
+       u32 gicv_idx;
+       struct resource vcpu_res;
+       struct vgic_params *vgic = &vgic_v3_params;
+
+       vgic->maint_irq = irq_of_parse_and_map(vgic_node, 0);
+       if (!vgic->maint_irq) {
+               kvm_err("error getting vgic maintenance irq from DT\n");
+               ret = -ENXIO;
+               goto out;
+       }
+
+       ich_vtr_el2 = kvm_call_hyp(__vgic_v3_get_ich_vtr_el2);
+
+       /*
+        * The ListRegs field is 5 bits, but there is a architectural
+        * maximum of 16 list registers. Just ignore bit 4...
+        */
+       vgic->nr_lr = (ich_vtr_el2 & 0xf) + 1;
+
+       if (of_property_read_u32(vgic_node, "#redistributor-regions", &gicv_idx))
+               gicv_idx = 1;
+
+       gicv_idx += 3; /* Also skip GICD, GICC, GICH */
+       if (of_address_to_resource(vgic_node, gicv_idx, &vcpu_res)) {
+               kvm_err("Cannot obtain GICV region\n");
+               ret = -ENXIO;
+               goto out;
+       }
+
+       if (!PAGE_ALIGNED(vcpu_res.start)) {
+               kvm_err("GICV physical address 0x%llx not page aligned\n",
+                       (unsigned long long)vcpu_res.start);
+               ret = -ENXIO;
+               goto out;
+       }
+
+       if (!PAGE_ALIGNED(resource_size(&vcpu_res))) {
+               kvm_err("GICV size 0x%llx not a multiple of page size 0x%lx\n",
+                       (unsigned long long)resource_size(&vcpu_res),
+                       PAGE_SIZE);
+               ret = -ENXIO;
+               goto out;
+       }
+
+       vgic->vcpu_base = vcpu_res.start;
+       vgic->vctrl_base = NULL;
+       vgic->type = VGIC_V3;
+
+       kvm_info("%s@%llx IRQ%d\n", vgic_node->name,
+                vcpu_res.start, vgic->maint_irq);
+
+       *ops = &vgic_v3_ops;
+       *params = vgic;
+
+out:
+       of_node_put(vgic_node);
+       return ret;
+}
index 476d3bf..73eba79 100644 (file)
 #define IMPLEMENTER_ARM                0x43b
 #define GICC_ARCH_VERSION_V2   0x2
 
-/* Physical address of vgic virtual cpu interface */
-static phys_addr_t vgic_vcpu_base;
-
-/* Virtual control interface base address */
-static void __iomem *vgic_vctrl_base;
-
-static struct device_node *vgic_node;
-
 #define ACCESS_READ_VALUE      (1 << 0)
 #define ACCESS_READ_RAZ                (0 << 0)
 #define ACCESS_READ_MASK(x)    ((x) & (1 << 0))
@@ -94,21 +86,46 @@ static struct device_node *vgic_node;
 #define ACCESS_WRITE_MASK(x)   ((x) & (3 << 1))
 
 static void vgic_retire_disabled_irqs(struct kvm_vcpu *vcpu);
+static void vgic_retire_lr(int lr_nr, int irq, struct kvm_vcpu *vcpu);
 static void vgic_update_state(struct kvm *kvm);
 static void vgic_kick_vcpus(struct kvm *kvm);
 static void vgic_dispatch_sgi(struct kvm_vcpu *vcpu, u32 reg);
-static u32 vgic_nr_lr;
+static struct vgic_lr vgic_get_lr(const struct kvm_vcpu *vcpu, int lr);
+static void vgic_set_lr(struct kvm_vcpu *vcpu, int lr, struct vgic_lr lr_desc);
+static void vgic_get_vmcr(struct kvm_vcpu *vcpu, struct vgic_vmcr *vmcr);
+static void vgic_set_vmcr(struct kvm_vcpu *vcpu, struct vgic_vmcr *vmcr);
 
-static unsigned int vgic_maint_irq;
+static const struct vgic_ops *vgic_ops;
+static const struct vgic_params *vgic;
+
+/*
+ * struct vgic_bitmap contains unions that provide two views of
+ * the same data. In one case it is an array of registers of
+ * u32's, and in the other case it is a bitmap of unsigned
+ * longs.
+ *
+ * This does not work on 64-bit BE systems, because the bitmap access
+ * will store two consecutive 32-bit words with the higher-addressed
+ * register's bits at the lower index and the lower-addressed register's
+ * bits at the higher index.
+ *
+ * Therefore, swizzle the register index when accessing the 32-bit word
+ * registers to access the right register's value.
+ */
+#if defined(CONFIG_CPU_BIG_ENDIAN) && BITS_PER_LONG == 64
+#define REG_OFFSET_SWIZZLE     1
+#else
+#define REG_OFFSET_SWIZZLE     0
+#endif
 
 static u32 *vgic_bitmap_get_reg(struct vgic_bitmap *x,
                                int cpuid, u32 offset)
 {
        offset >>= 2;
        if (!offset)
-               return x->percpu[cpuid].reg;
+               return x->percpu[cpuid].reg + (offset ^ REG_OFFSET_SWIZZLE);
        else
-               return x->shared.reg + offset - 1;
+               return x->shared.reg + ((offset - 1) ^ REG_OFFSET_SWIZZLE);
 }
 
 static int vgic_bitmap_get_irq_val(struct vgic_bitmap *x,
@@ -241,12 +258,12 @@ static void vgic_cpu_irq_clear(struct kvm_vcpu *vcpu, int irq)
 
 static u32 mmio_data_read(struct kvm_exit_mmio *mmio, u32 mask)
 {
-       return *((u32 *)mmio->data) & mask;
+       return le32_to_cpu(*((u32 *)mmio->data)) & mask;
 }
 
 static void mmio_data_write(struct kvm_exit_mmio *mmio, u32 mask, u32 value)
 {
-       *((u32 *)mmio->data) = value & mask;
+       *((u32 *)mmio->data) = cpu_to_le32(value) & mask;
 }
 
 /**
@@ -593,18 +610,6 @@ static bool handle_mmio_sgi_reg(struct kvm_vcpu *vcpu,
        return false;
 }
 
-#define LR_CPUID(lr)   \
-       (((lr) & GICH_LR_PHYSID_CPUID) >> GICH_LR_PHYSID_CPUID_SHIFT)
-#define LR_IRQID(lr)   \
-       ((lr) & GICH_LR_VIRTUALID)
-
-static void vgic_retire_lr(int lr_nr, int irq, struct vgic_cpu *vgic_cpu)
-{
-       clear_bit(lr_nr, vgic_cpu->lr_used);
-       vgic_cpu->vgic_lr[lr_nr] &= ~GICH_LR_STATE;
-       vgic_cpu->vgic_irq_lr_map[irq] = LR_EMPTY;
-}
-
 /**
  * vgic_unqueue_irqs - move pending IRQs from LRs to the distributor
  * @vgic_cpu: Pointer to the vgic_cpu struct holding the LRs
@@ -622,13 +627,10 @@ static void vgic_unqueue_irqs(struct kvm_vcpu *vcpu)
        struct vgic_dist *dist = &vcpu->kvm->arch.vgic;
        struct vgic_cpu *vgic_cpu = &vcpu->arch.vgic_cpu;
        int vcpu_id = vcpu->vcpu_id;
-       int i, irq, source_cpu;
-       u32 *lr;
+       int i;
 
        for_each_set_bit(i, vgic_cpu->lr_used, vgic_cpu->nr_lr) {
-               lr = &vgic_cpu->vgic_lr[i];
-               irq = LR_IRQID(*lr);
-               source_cpu = LR_CPUID(*lr);
+               struct vgic_lr lr = vgic_get_lr(vcpu, i);
 
                /*
                 * There are three options for the state bits:
@@ -640,7 +642,7 @@ static void vgic_unqueue_irqs(struct kvm_vcpu *vcpu)
                 * If the LR holds only an active interrupt (not pending) then
                 * just leave it alone.
                 */
-               if ((*lr & GICH_LR_STATE) == GICH_LR_ACTIVE_BIT)
+               if ((lr.state & LR_STATE_MASK) == LR_STATE_ACTIVE)
                        continue;
 
                /*
@@ -649,18 +651,19 @@ static void vgic_unqueue_irqs(struct kvm_vcpu *vcpu)
                 * is fine, then we are only setting a few bits that were
                 * already set.
                 */
-               vgic_dist_irq_set(vcpu, irq);
-               if (irq < VGIC_NR_SGIS)
-                       dist->irq_sgi_sources[vcpu_id][irq] |= 1 << source_cpu;
-               *lr &= ~GICH_LR_PENDING_BIT;
+               vgic_dist_irq_set(vcpu, lr.irq);
+               if (lr.irq < VGIC_NR_SGIS)
+                       dist->irq_sgi_sources[vcpu_id][lr.irq] |= 1 << lr.source;
+               lr.state &= ~LR_STATE_PENDING;
+               vgic_set_lr(vcpu, i, lr);
 
                /*
                 * If there's no state left on the LR (it could still be
                 * active), then the LR does not hold any useful info and can
                 * be marked as free for other use.
                 */
-               if (!(*lr & GICH_LR_STATE))
-                       vgic_retire_lr(i, irq, vgic_cpu);
+               if (!(lr.state & LR_STATE_MASK))
+                       vgic_retire_lr(i, lr.irq, vcpu);
 
                /* Finally update the VGIC state. */
                vgic_update_state(vcpu->kvm);
@@ -989,8 +992,73 @@ static void vgic_update_state(struct kvm *kvm)
        }
 }
 
-#define MK_LR_PEND(src, irq)   \
-       (GICH_LR_PENDING_BIT | ((src) << GICH_LR_PHYSID_CPUID_SHIFT) | (irq))
+static struct vgic_lr vgic_get_lr(const struct kvm_vcpu *vcpu, int lr)
+{
+       return vgic_ops->get_lr(vcpu, lr);
+}
+
+static void vgic_set_lr(struct kvm_vcpu *vcpu, int lr,
+                              struct vgic_lr vlr)
+{
+       vgic_ops->set_lr(vcpu, lr, vlr);
+}
+
+static void vgic_sync_lr_elrsr(struct kvm_vcpu *vcpu, int lr,
+                              struct vgic_lr vlr)
+{
+       vgic_ops->sync_lr_elrsr(vcpu, lr, vlr);
+}
+
+static inline u64 vgic_get_elrsr(struct kvm_vcpu *vcpu)
+{
+       return vgic_ops->get_elrsr(vcpu);
+}
+
+static inline u64 vgic_get_eisr(struct kvm_vcpu *vcpu)
+{
+       return vgic_ops->get_eisr(vcpu);
+}
+
+static inline u32 vgic_get_interrupt_status(struct kvm_vcpu *vcpu)
+{
+       return vgic_ops->get_interrupt_status(vcpu);
+}
+
+static inline void vgic_enable_underflow(struct kvm_vcpu *vcpu)
+{
+       vgic_ops->enable_underflow(vcpu);
+}
+
+static inline void vgic_disable_underflow(struct kvm_vcpu *vcpu)
+{
+       vgic_ops->disable_underflow(vcpu);
+}
+
+static inline void vgic_get_vmcr(struct kvm_vcpu *vcpu, struct vgic_vmcr *vmcr)
+{
+       vgic_ops->get_vmcr(vcpu, vmcr);
+}
+
+static void vgic_set_vmcr(struct kvm_vcpu *vcpu, struct vgic_vmcr *vmcr)
+{
+       vgic_ops->set_vmcr(vcpu, vmcr);
+}
+
+static inline void vgic_enable(struct kvm_vcpu *vcpu)
+{
+       vgic_ops->enable(vcpu);
+}
+
+static void vgic_retire_lr(int lr_nr, int irq, struct kvm_vcpu *vcpu)
+{
+       struct vgic_cpu *vgic_cpu = &vcpu->arch.vgic_cpu;
+       struct vgic_lr vlr = vgic_get_lr(vcpu, lr_nr);
+
+       vlr.state = 0;
+       vgic_set_lr(vcpu, lr_nr, vlr);
+       clear_bit(lr_nr, vgic_cpu->lr_used);
+       vgic_cpu->vgic_irq_lr_map[irq] = LR_EMPTY;
+}
 
 /*
  * An interrupt may have been disabled after being made pending on the
@@ -1006,13 +1074,13 @@ static void vgic_retire_disabled_irqs(struct kvm_vcpu *vcpu)
        struct vgic_cpu *vgic_cpu = &vcpu->arch.vgic_cpu;
        int lr;
 
-       for_each_set_bit(lr, vgic_cpu->lr_used, vgic_cpu->nr_lr) {
-               int irq = vgic_cpu->vgic_lr[lr] & GICH_LR_VIRTUALID;
+       for_each_set_bit(lr, vgic_cpu->lr_used, vgic->nr_lr) {
+               struct vgic_lr vlr = vgic_get_lr(vcpu, lr);
 
-               if (!vgic_irq_is_enabled(vcpu, irq)) {
-                       vgic_retire_lr(lr, irq, vgic_cpu);
-                       if (vgic_irq_is_active(vcpu, irq))
-                               vgic_irq_clear_active(vcpu, irq);
+               if (!vgic_irq_is_enabled(vcpu, vlr.irq)) {
+                       vgic_retire_lr(lr, vlr.irq, vcpu);
+                       if (vgic_irq_is_active(vcpu, vlr.irq))
+                               vgic_irq_clear_active(vcpu, vlr.irq);
                }
        }
 }
@@ -1024,6 +1092,7 @@ static void vgic_retire_disabled_irqs(struct kvm_vcpu *vcpu)
 static bool vgic_queue_irq(struct kvm_vcpu *vcpu, u8 sgi_source_id, int irq)
 {
        struct vgic_cpu *vgic_cpu = &vcpu->arch.vgic_cpu;
+       struct vgic_lr vlr;
        int lr;
 
        /* Sanitize the input... */
@@ -1036,28 +1105,34 @@ static bool vgic_queue_irq(struct kvm_vcpu *vcpu, u8 sgi_source_id, int irq)
        lr = vgic_cpu->vgic_irq_lr_map[irq];
 
        /* Do we have an active interrupt for the same CPUID? */
-       if (lr != LR_EMPTY &&
-           (LR_CPUID(vgic_cpu->vgic_lr[lr]) == sgi_source_id)) {
-               kvm_debug("LR%d piggyback for IRQ%d %x\n",
-                         lr, irq, vgic_cpu->vgic_lr[lr]);
-               BUG_ON(!test_bit(lr, vgic_cpu->lr_used));
-               vgic_cpu->vgic_lr[lr] |= GICH_LR_PENDING_BIT;
-               return true;
+       if (lr != LR_EMPTY) {
+               vlr = vgic_get_lr(vcpu, lr);
+               if (vlr.source == sgi_source_id) {
+                       kvm_debug("LR%d piggyback for IRQ%d\n", lr, vlr.irq);
+                       BUG_ON(!test_bit(lr, vgic_cpu->lr_used));
+                       vlr.state |= LR_STATE_PENDING;
+                       vgic_set_lr(vcpu, lr, vlr);
+                       return true;
+               }
        }
 
        /* Try to use another LR for this interrupt */
        lr = find_first_zero_bit((unsigned long *)vgic_cpu->lr_used,
-                              vgic_cpu->nr_lr);
-       if (lr >= vgic_cpu->nr_lr)
+                              vgic->nr_lr);
+       if (lr >= vgic->nr_lr)
                return false;
 
        kvm_debug("LR%d allocated for IRQ%d %x\n", lr, irq, sgi_source_id);
-       vgic_cpu->vgic_lr[lr] = MK_LR_PEND(sgi_source_id, irq);
        vgic_cpu->vgic_irq_lr_map[irq] = lr;
        set_bit(lr, vgic_cpu->lr_used);
 
+       vlr.irq = irq;
+       vlr.source = sgi_source_id;
+       vlr.state = LR_STATE_PENDING;
        if (!vgic_irq_is_edge(vcpu, irq))
-               vgic_cpu->vgic_lr[lr] |= GICH_LR_EOI;
+               vlr.state |= LR_EOI_INT;
+
+       vgic_set_lr(vcpu, lr, vlr);
 
        return true;
 }
@@ -1155,9 +1230,9 @@ static void __kvm_vgic_flush_hwstate(struct kvm_vcpu *vcpu)
 
 epilog:
        if (overflow) {
-               vgic_cpu->vgic_hcr |= GICH_HCR_UIE;
+               vgic_enable_underflow(vcpu);
        } else {
-               vgic_cpu->vgic_hcr &= ~GICH_HCR_UIE;
+               vgic_disable_underflow(vcpu);
                /*
                 * We're about to run this VCPU, and we've consumed
                 * everything the distributor had in store for
@@ -1170,44 +1245,46 @@ epilog:
 
 static bool vgic_process_maintenance(struct kvm_vcpu *vcpu)
 {
-       struct vgic_cpu *vgic_cpu = &vcpu->arch.vgic_cpu;
+       u32 status = vgic_get_interrupt_status(vcpu);
        bool level_pending = false;
 
-       kvm_debug("MISR = %08x\n", vgic_cpu->vgic_misr);
+       kvm_debug("STATUS = %08x\n", status);
 
-       if (vgic_cpu->vgic_misr & GICH_MISR_EOI) {
+       if (status & INT_STATUS_EOI) {
                /*
                 * Some level interrupts have been EOIed. Clear their
                 * active bit.
                 */
-               int lr, irq;
+               u64 eisr = vgic_get_eisr(vcpu);
+               unsigned long *eisr_ptr = (unsigned long *)&eisr;
+               int lr;
 
-               for_each_set_bit(lr, (unsigned long *)vgic_cpu->vgic_eisr,
-                                vgic_cpu->nr_lr) {
-                       irq = vgic_cpu->vgic_lr[lr] & GICH_LR_VIRTUALID;
+               for_each_set_bit(lr, eisr_ptr, vgic->nr_lr) {
+                       struct vgic_lr vlr = vgic_get_lr(vcpu, lr);
 
-                       vgic_irq_clear_active(vcpu, irq);
-                       vgic_cpu->vgic_lr[lr] &= ~GICH_LR_EOI;
+                       vgic_irq_clear_active(vcpu, vlr.irq);
+                       WARN_ON(vlr.state & LR_STATE_MASK);
+                       vlr.state = 0;
+                       vgic_set_lr(vcpu, lr, vlr);
 
                        /* Any additional pending interrupt? */
-                       if (vgic_dist_irq_is_pending(vcpu, irq)) {
-                               vgic_cpu_irq_set(vcpu, irq);
+                       if (vgic_dist_irq_is_pending(vcpu, vlr.irq)) {
+                               vgic_cpu_irq_set(vcpu, vlr.irq);
                                level_pending = true;
                        } else {
-                               vgic_cpu_irq_clear(vcpu, irq);
+                               vgic_cpu_irq_clear(vcpu, vlr.irq);
                        }
 
                        /*
                         * Despite being EOIed, the LR may not have
                         * been marked as empty.
                         */
-                       set_bit(lr, (unsigned long *)vgic_cpu->vgic_elrsr);
-                       vgic_cpu->vgic_lr[lr] &= ~GICH_LR_ACTIVE_BIT;
+                       vgic_sync_lr_elrsr(vcpu, lr, vlr);
                }
        }
 
-       if (vgic_cpu->vgic_misr & GICH_MISR_U)
-               vgic_cpu->vgic_hcr &= ~GICH_HCR_UIE;
+       if (status & INT_STATUS_UNDERFLOW)
+               vgic_disable_underflow(vcpu);
 
        return level_pending;
 }
@@ -1220,29 +1297,31 @@ static void __kvm_vgic_sync_hwstate(struct kvm_vcpu *vcpu)
 {
        struct vgic_cpu *vgic_cpu = &vcpu->arch.vgic_cpu;
        struct vgic_dist *dist = &vcpu->kvm->arch.vgic;
+       u64 elrsr;
+       unsigned long *elrsr_ptr;
        int lr, pending;
        bool level_pending;
 
        level_pending = vgic_process_maintenance(vcpu);
+       elrsr = vgic_get_elrsr(vcpu);
+       elrsr_ptr = (unsigned long *)&elrsr;
 
        /* Clear mappings for empty LRs */
-       for_each_set_bit(lr, (unsigned long *)vgic_cpu->vgic_elrsr,
-                        vgic_cpu->nr_lr) {
-               int irq;
+       for_each_set_bit(lr, elrsr_ptr, vgic->nr_lr) {
+               struct vgic_lr vlr;
 
                if (!test_and_clear_bit(lr, vgic_cpu->lr_used))
                        continue;
 
-               irq = vgic_cpu->vgic_lr[lr] & GICH_LR_VIRTUALID;
+               vlr = vgic_get_lr(vcpu, lr);
 
-               BUG_ON(irq >= VGIC_NR_IRQS);
-               vgic_cpu->vgic_irq_lr_map[irq] = LR_EMPTY;
+               BUG_ON(vlr.irq >= VGIC_NR_IRQS);
+               vgic_cpu->vgic_irq_lr_map[vlr.irq] = LR_EMPTY;
        }
 
        /* Check if we still have something up our sleeve... */
-       pending = find_first_zero_bit((unsigned long *)vgic_cpu->vgic_elrsr,
-                                     vgic_cpu->nr_lr);
-       if (level_pending || pending < vgic_cpu->nr_lr)
+       pending = find_first_zero_bit(elrsr_ptr, vgic->nr_lr);
+       if (level_pending || pending < vgic->nr_lr)
                set_bit(vcpu->vcpu_id, &dist->irq_pending_on_cpu);
 }
 
@@ -1432,21 +1511,20 @@ int kvm_vgic_vcpu_init(struct kvm_vcpu *vcpu)
        }
 
        /*
-        * By forcing VMCR to zero, the GIC will restore the binary
-        * points to their reset values. Anything else resets to zero
-        * anyway.
+        * Store the number of LRs per vcpu, so we don't have to go
+        * all the way to the distributor structure to find out. Only
+        * assembly code should use this one.
         */
-       vgic_cpu->vgic_vmcr = 0;
+       vgic_cpu->nr_lr = vgic->nr_lr;
 
-       vgic_cpu->nr_lr = vgic_nr_lr;
-       vgic_cpu->vgic_hcr = GICH_HCR_EN; /* Get the show on the road... */
+       vgic_enable(vcpu);
 
        return 0;
 }
 
 static void vgic_init_maintenance_interrupt(void *info)
 {
-       enable_percpu_irq(vgic_maint_irq, 0);
+       enable_percpu_irq(vgic->maint_irq, 0);
 }
 
 static int vgic_cpu_notify(struct notifier_block *self,
@@ -1459,7 +1537,7 @@ static int vgic_cpu_notify(struct notifier_block *self,
                break;
        case CPU_DYING:
        case CPU_DYING_FROZEN:
-               disable_percpu_irq(vgic_maint_irq);
+               disable_percpu_irq(vgic->maint_irq);
                break;
        }
 
@@ -1470,30 +1548,37 @@ static struct notifier_block vgic_cpu_nb = {
        .notifier_call = vgic_cpu_notify,
 };
 
+static const struct of_device_id vgic_ids[] = {
+       { .compatible = "arm,cortex-a15-gic", .data = vgic_v2_probe, },
+       { .compatible = "arm,gic-v3", .data = vgic_v3_probe, },
+       {},
+};
+
 int kvm_vgic_hyp_init(void)
 {
+       const struct of_device_id *matched_id;
+       int (*vgic_probe)(struct device_node *,const struct vgic_ops **,
+                         const struct vgic_params **);
+       struct device_node *vgic_node;
        int ret;
-       struct resource vctrl_res;
-       struct resource vcpu_res;
 
-       vgic_node = of_find_compatible_node(NULL, NULL, "arm,cortex-a15-gic");
+       vgic_node = of_find_matching_node_and_match(NULL,
+                                                   vgic_ids, &matched_id);
        if (!vgic_node) {
-               kvm_err("error: no compatible vgic node in DT\n");
+               kvm_err("error: no compatible GIC node found\n");
                return -ENODEV;
        }
 
-       vgic_maint_irq = irq_of_parse_and_map(vgic_node, 0);
-       if (!vgic_maint_irq) {
-               kvm_err("error getting vgic maintenance irq from DT\n");
-               ret = -ENXIO;
-               goto out;
-       }
+       vgic_probe = matched_id->data;
+       ret = vgic_probe(vgic_node, &vgic_ops, &vgic);
+       if (ret)
+               return ret;
 
-       ret = request_percpu_irq(vgic_maint_irq, vgic_maintenance_handler,
+       ret = request_percpu_irq(vgic->maint_irq, vgic_maintenance_handler,
                                 "vgic", kvm_get_running_vcpus());
        if (ret) {
-               kvm_err("Cannot register interrupt %d\n", vgic_maint_irq);
-               goto out;
+               kvm_err("Cannot register interrupt %d\n", vgic->maint_irq);
+               return ret;
        }
 
        ret = __register_cpu_notifier(&vgic_cpu_nb);
@@ -1502,65 +1587,15 @@ int kvm_vgic_hyp_init(void)
                goto out_free_irq;
        }
 
-       ret = of_address_to_resource(vgic_node, 2, &vctrl_res);
-       if (ret) {
-               kvm_err("Cannot obtain VCTRL resource\n");
-               goto out_free_irq;
-       }
+       /* Callback into for arch code for setup */
+       vgic_arch_setup(vgic);
 
-       vgic_vctrl_base = of_iomap(vgic_node, 2);
-       if (!vgic_vctrl_base) {
-               kvm_err("Cannot ioremap VCTRL\n");
-               ret = -ENOMEM;
-               goto out_free_irq;
-       }
-
-       vgic_nr_lr = readl_relaxed(vgic_vctrl_base + GICH_VTR);
-       vgic_nr_lr = (vgic_nr_lr & 0x3f) + 1;
-
-       ret = create_hyp_io_mappings(vgic_vctrl_base,
-                                    vgic_vctrl_base + resource_size(&vctrl_res),
-                                    vctrl_res.start);
-       if (ret) {
-               kvm_err("Cannot map VCTRL into hyp\n");
-               goto out_unmap;
-       }
-
-       if (of_address_to_resource(vgic_node, 3, &vcpu_res)) {
-               kvm_err("Cannot obtain VCPU resource\n");
-               ret = -ENXIO;
-               goto out_unmap;
-       }
-
-       if (!PAGE_ALIGNED(vcpu_res.start)) {
-               kvm_err("GICV physical address 0x%llx not page aligned\n",
-                       (unsigned long long)vcpu_res.start);
-               ret = -ENXIO;
-               goto out_unmap;
-       }
-
-       if (!PAGE_ALIGNED(resource_size(&vcpu_res))) {
-               kvm_err("GICV size 0x%llx not a multiple of page size 0x%lx\n",
-                       (unsigned long long)resource_size(&vcpu_res),
-                       PAGE_SIZE);
-               ret = -ENXIO;
-               goto out_unmap;
-       }
-
-       vgic_vcpu_base = vcpu_res.start;
-
-       kvm_info("%s@%llx IRQ%d\n", vgic_node->name,
-                vctrl_res.start, vgic_maint_irq);
        on_each_cpu(vgic_init_maintenance_interrupt, NULL, 1);
 
-       goto out;
+       return 0;
 
-out_unmap:
-       iounmap(vgic_vctrl_base);
 out_free_irq:
-       free_percpu_irq(vgic_maint_irq, kvm_get_running_vcpus());
-out:
-       of_node_put(vgic_node);
+       free_percpu_irq(vgic->maint_irq, kvm_get_running_vcpus());
        return ret;
 }
 
@@ -1593,7 +1628,7 @@ int kvm_vgic_init(struct kvm *kvm)
        }
 
        ret = kvm_phys_addr_ioremap(kvm, kvm->arch.vgic.vgic_cpu_base,
-                                   vgic_vcpu_base, KVM_VGIC_V2_CPU_SIZE);
+                                   vgic->vcpu_base, KVM_VGIC_V2_CPU_SIZE);
        if (ret) {
                kvm_err("Unable to remap VGIC CPU to VCPU\n");
                goto out;
@@ -1639,7 +1674,8 @@ int kvm_vgic_create(struct kvm *kvm)
        }
 
        spin_lock_init(&kvm->arch.vgic.lock);
-       kvm->arch.vgic.vctrl_base = vgic_vctrl_base;
+       kvm->arch.vgic.in_kernel = true;
+       kvm->arch.vgic.vctrl_base = vgic->vctrl_base;
        kvm->arch.vgic.vgic_dist_base = VGIC_ADDR_UNDEF;
        kvm->arch.vgic.vgic_cpu_base = VGIC_ADDR_UNDEF;
 
@@ -1738,39 +1774,40 @@ int kvm_vgic_addr(struct kvm *kvm, unsigned long type, u64 *addr, bool write)
 static bool handle_cpu_mmio_misc(struct kvm_vcpu *vcpu,
                                 struct kvm_exit_mmio *mmio, phys_addr_t offset)
 {
-       struct vgic_cpu *vgic_cpu = &vcpu->arch.vgic_cpu;
-       u32 reg, mask = 0, shift = 0;
        bool updated = false;
+       struct vgic_vmcr vmcr;
+       u32 *vmcr_field;
+       u32 reg;
+
+       vgic_get_vmcr(vcpu, &vmcr);
 
        switch (offset & ~0x3) {
        case GIC_CPU_CTRL:
-               mask = GICH_VMCR_CTRL_MASK;
-               shift = GICH_VMCR_CTRL_SHIFT;
+               vmcr_field = &vmcr.ctlr;
                break;
        case GIC_CPU_PRIMASK:
-               mask = GICH_VMCR_PRIMASK_MASK;
-               shift = GICH_VMCR_PRIMASK_SHIFT;
+               vmcr_field = &vmcr.pmr;
                break;
        case GIC_CPU_BINPOINT:
-               mask = GICH_VMCR_BINPOINT_MASK;
-               shift = GICH_VMCR_BINPOINT_SHIFT;
+               vmcr_field = &vmcr.bpr;
                break;
        case GIC_CPU_ALIAS_BINPOINT:
-               mask = GICH_VMCR_ALIAS_BINPOINT_MASK;
-               shift = GICH_VMCR_ALIAS_BINPOINT_SHIFT;
+               vmcr_field = &vmcr.abpr;
                break;
+       default:
+               BUG();
        }
 
        if (!mmio->is_write) {
-               reg = (vgic_cpu->vgic_vmcr & mask) >> shift;
+               reg = *vmcr_field;
                mmio_data_write(mmio, ~0, reg);
        } else {
                reg = mmio_data_read(mmio, ~0);
-               reg = (reg << shift) & mask;
-               if (reg != (vgic_cpu->vgic_vmcr & mask))
+               if (reg != *vmcr_field) {
+                       *vmcr_field = reg;
+                       vgic_set_vmcr(vcpu, &vmcr);
                        updated = true;
-               vgic_cpu->vgic_vmcr &= ~mask;
-               vgic_cpu->vgic_vmcr |= reg;
+               }
        }
        return updated;
 }