Merge remote-tracking branch 'kvm-arm/next'
authorStephen Rothwell <sfr@canb.auug.org.au>
Tue, 13 Sep 2016 02:23:19 +0000 (12:23 +1000)
committerStephen Rothwell <sfr@canb.auug.org.au>
Tue, 13 Sep 2016 02:23:19 +0000 (12:23 +1000)
37 files changed:
Documentation/virtual/kvm/devices/arm-vgic-its.txt [new file with mode: 0644]
Documentation/virtual/kvm/devices/arm-vgic-v3.txt [new file with mode: 0644]
Documentation/virtual/kvm/devices/arm-vgic.txt
arch/arm/include/asm/kvm_asm.h
arch/arm/include/asm/kvm_emulate.h
arch/arm/include/asm/kvm_mmu.h
arch/arm/kvm/Makefile
arch/arm/kvm/emulate.c
arch/arm/kvm/handle_exit.c
arch/arm/kvm/hyp/entry.S
arch/arm/kvm/hyp/hyp-entry.S
arch/arm/kvm/hyp/switch.c
arch/arm/kvm/hyp/tlb.c
arch/arm/kvm/mmio.c
arch/arm/kvm/mmu.c
arch/arm64/include/asm/kvm_arm.h
arch/arm64/include/asm/kvm_asm.h
arch/arm64/include/asm/kvm_emulate.h
arch/arm64/include/asm/kvm_hyp.h
arch/arm64/include/asm/kvm_mmu.h
arch/arm64/kvm/Makefile
arch/arm64/kvm/emulate.c [deleted file]
arch/arm64/kvm/handle_exit.c
arch/arm64/kvm/hyp/debug-sr.c
arch/arm64/kvm/hyp/entry.S
arch/arm64/kvm/hyp/hyp-entry.S
arch/arm64/kvm/hyp/switch.c
arch/arm64/kvm/hyp/tlb.c
arch/arm64/kvm/hyp/vgic-v3-sr.c
arch/arm64/kvm/inject_fault.c
include/kvm/arm_vgic.h
virt/kvm/arm/aarch32.c [new file with mode: 0644]
virt/kvm/arm/arch_timer.c
virt/kvm/arm/hyp/vgic-v2-sr.c
virt/kvm/arm/vgic/vgic-irqfd.c
virt/kvm/arm/vgic/vgic-kvm-device.c
virt/kvm/arm/vgic/vgic-v2.c

diff --git a/Documentation/virtual/kvm/devices/arm-vgic-its.txt b/Documentation/virtual/kvm/devices/arm-vgic-its.txt
new file mode 100644 (file)
index 0000000..6081a5b
--- /dev/null
@@ -0,0 +1,38 @@
+ARM Virtual Interrupt Translation Service (ITS)
+===============================================
+
+Device types supported:
+  KVM_DEV_TYPE_ARM_VGIC_ITS    ARM Interrupt Translation Service Controller
+
+The ITS allows MSI(-X) interrupts to be injected into guests. This extension is
+optional.  Creating a virtual ITS controller also requires a host GICv3 (see
+arm-vgic-v3.txt), but does not depend on having physical ITS controllers.
+
+There can be multiple ITS controllers per guest, each of them has to have
+a separate, non-overlapping MMIO region.
+
+
+Groups:
+  KVM_DEV_ARM_VGIC_GRP_ADDR
+  Attributes:
+    KVM_VGIC_ITS_ADDR_TYPE (rw, 64-bit)
+      Base address in the guest physical address space of the GICv3 ITS
+      control register frame.
+      This address needs to be 64K aligned and the region covers 128K.
+  Errors:
+    -E2BIG:  Address outside of addressable IPA range
+    -EINVAL: Incorrectly aligned address
+    -EEXIST: Address already configured
+    -EFAULT: Invalid user pointer for attr->addr.
+    -ENODEV: Incorrect attribute or the ITS is not supported.
+
+
+  KVM_DEV_ARM_VGIC_GRP_CTRL
+  Attributes:
+    KVM_DEV_ARM_VGIC_CTRL_INIT
+      request the initialization of the ITS, no additional parameter in
+      kvm_device_attr.addr.
+  Errors:
+    -ENXIO:  ITS not properly configured as required prior to setting
+             this attribute
+    -ENOMEM: Memory shortage when allocating ITS internal data
diff --git a/Documentation/virtual/kvm/devices/arm-vgic-v3.txt b/Documentation/virtual/kvm/devices/arm-vgic-v3.txt
new file mode 100644 (file)
index 0000000..9348b3c
--- /dev/null
@@ -0,0 +1,206 @@
+ARM Virtual Generic Interrupt Controller v3 and later (VGICv3)
+==============================================================
+
+
+Device types supported:
+  KVM_DEV_TYPE_ARM_VGIC_V3     ARM Generic Interrupt Controller v3.0
+
+Only one VGIC instance may be instantiated through this API.  The created VGIC
+will act as the VM interrupt controller, requiring emulated user-space devices
+to inject interrupts to the VGIC instead of directly to CPUs.  It is not
+possible to create both a GICv3 and GICv2 on the same VM.
+
+Creating a guest GICv3 device requires a host GICv3 as well.
+
+
+Groups:
+  KVM_DEV_ARM_VGIC_GRP_ADDR
+  Attributes:
+    KVM_VGIC_V3_ADDR_TYPE_DIST (rw, 64-bit)
+      Base address in the guest physical address space of the GICv3 distributor
+      register mappings. Only valid for KVM_DEV_TYPE_ARM_VGIC_V3.
+      This address needs to be 64K aligned and the region covers 64 KByte.
+
+    KVM_VGIC_V3_ADDR_TYPE_REDIST (rw, 64-bit)
+      Base address in the guest physical address space of the GICv3
+      redistributor register mappings. There are two 64K pages for each
+      VCPU and all of the redistributor pages are contiguous.
+      Only valid for KVM_DEV_TYPE_ARM_VGIC_V3.
+      This address needs to be 64K aligned.
+  Errors:
+    -E2BIG:  Address outside of addressable IPA range
+    -EINVAL: Incorrectly aligned address
+    -EEXIST: Address already configured
+    -ENXIO:  The group or attribute is unknown/unsupported for this device
+             or hardware support is missing.
+    -EFAULT: Invalid user pointer for attr->addr.
+
+
+
+  KVM_DEV_ARM_VGIC_GRP_DIST_REGS
+  KVM_DEV_ARM_VGIC_GRP_REDIST_REGS
+  Attributes:
+    The attr field of kvm_device_attr encodes two values:
+    bits:     | 63   ....  32  |  31   ....    0 |
+    values:   |      mpidr     |      offset     |
+
+    All distributor regs are (rw, 32-bit) and kvm_device_attr.addr points to a
+    __u32 value.  64-bit registers must be accessed by separately accessing the
+    lower and higher word.
+
+    Writes to read-only registers are ignored by the kernel.
+
+    KVM_DEV_ARM_VGIC_GRP_DIST_REGS accesses the main distributor registers.
+    KVM_DEV_ARM_VGIC_GRP_REDIST_REGS accesses the redistributor of the CPU
+    specified by the mpidr.
+
+    The offset is relative to the "[Re]Distributor base address" as defined
+    in the GICv3/4 specs.  Getting or setting such a register has the same
+    effect as reading or writing the register on real hardware, except for the
+    following registers: GICD_STATUSR, GICR_STATUSR, GICD_ISPENDR,
+    GICR_ISPENDR0, GICD_ICPENDR, and GICR_ICPENDR0.  These registers behave
+    differently when accessed via this interface compared to their
+    architecturally defined behavior to allow software a full view of the
+    VGIC's internal state.
+
+    The mpidr field is used to specify which
+    redistributor is accessed.  The mpidr is ignored for the distributor.
+
+    The mpidr encoding is based on the affinity information in the
+    architecture defined MPIDR, and the field is encoded as follows:
+      | 63 .... 56 | 55 .... 48 | 47 .... 40 | 39 .... 32 |
+      |    Aff3    |    Aff2    |    Aff1    |    Aff0    |
+
+    Note that distributor fields are not banked, but return the same value
+    regardless of the mpidr used to access the register.
+
+    The GICD_STATUSR and GICR_STATUSR registers are architecturally defined such
+    that a write of a clear bit has no effect, whereas a write with a set bit
+    clears that value.  To allow userspace to freely set the values of these two
+    registers, setting the attributes with the register offsets for these two
+    registers simply sets the non-reserved bits to the value written.
+
+
+    Accesses (reads and writes) to the GICD_ISPENDR register region and
+    GICR_ISPENDR0 registers get/set the value of the latched pending state for
+    the interrupts.
+
+    This is identical to the value returned by a guest read from ISPENDR for an
+    edge triggered interrupt, but may differ for level triggered interrupts.
+    For edge triggered interrupts, once an interrupt becomes pending (whether
+    because of an edge detected on the input line or because of a guest write
+    to ISPENDR) this state is "latched", and only cleared when either the
+    interrupt is activated or when the guest writes to ICPENDR. A level
+    triggered interrupt may be pending either because the level input is held
+    high by a device, or because of a guest write to the ISPENDR register. Only
+    ISPENDR writes are latched; if the device lowers the line level then the
+    interrupt is no longer pending unless the guest also wrote to ISPENDR, and
+    conversely writes to ICPENDR or activations of the interrupt do not clear
+    the pending status if the line level is still being held high.  (These
+    rules are documented in the GICv3 specification descriptions of the ICPENDR
+    and ISPENDR registers.) For a level triggered interrupt the value accessed
+    here is that of the latch which is set by ISPENDR and cleared by ICPENDR or
+    interrupt activation, whereas the value returned by a guest read from
+    ISPENDR is the logical OR of the latch value and the input line level.
+
+    Raw access to the latch state is provided to userspace so that it can save
+    and restore the entire GIC internal state (which is defined by the
+    combination of the current input line level and the latch state, and cannot
+    be deduced from purely the line level and the value of the ISPENDR
+    registers).
+
+    Accesses to GICD_ICPENDR register region and GICR_ICPENDR0 registers have
+    RAZ/WI semantics, meaning that reads always return 0 and writes are always
+    ignored.
+
+  Errors:
+    -ENXIO: Getting or setting this register is not yet supported
+    -EBUSY: One or more VCPUs are running
+
+
+  KVM_DEV_ARM_VGIC_CPU_SYSREGS
+  Attributes:
+    The attr field of kvm_device_attr encodes two values:
+    bits:     | 63      ....       32 | 31  ....  16 | 15  ....  0 |
+    values:   |         mpidr         |      RES     |    instr    |
+
+    The mpidr field encodes the CPU ID based on the affinity information in the
+    architecture defined MPIDR, and the field is encoded as follows:
+      | 63 .... 56 | 55 .... 48 | 47 .... 40 | 39 .... 32 |
+      |    Aff3    |    Aff2    |    Aff1    |    Aff0    |
+
+    The instr field encodes the system register to access based on the fields
+    defined in the A64 instruction set encoding for system register access
+    (RES means the bits are reserved for future use and should be zero):
+
+      | 15 ... 14 | 13 ... 11 | 10 ... 7 | 6 ... 3 | 2 ... 0 |
+      |   Op 0    |    Op1    |    CRn   |   CRm   |   Op2   |
+
+    All system regs accessed through this API are (rw, 64-bit) and
+    kvm_device_attr.addr points to a __u64 value.
+
+    KVM_DEV_ARM_VGIC_CPU_SYSREGS accesses the CPU interface registers for the
+    CPU specified by the mpidr field.
+
+  Errors:
+    -ENXIO: Getting or setting this register is not yet supported
+    -EBUSY: VCPU is running
+    -EINVAL: Invalid mpidr supplied
+
+
+  KVM_DEV_ARM_VGIC_GRP_NR_IRQS
+  Attributes:
+    A value describing the number of interrupts (SGI, PPI and SPI) for
+    this GIC instance, ranging from 64 to 1024, in increments of 32.
+
+    kvm_device_attr.addr points to a __u32 value.
+
+  Errors:
+    -EINVAL: Value set is out of the expected range
+    -EBUSY: Value has already be set.
+
+
+  KVM_DEV_ARM_VGIC_GRP_CTRL
+  Attributes:
+    KVM_DEV_ARM_VGIC_CTRL_INIT
+      request the initialization of the VGIC, no additional parameter in
+      kvm_device_attr.addr.
+  Errors:
+    -ENXIO: VGIC not properly configured as required prior to calling
+     this attribute
+    -ENODEV: no online VCPU
+    -ENOMEM: memory shortage when allocating vgic internal data
+
+
+  KVM_DEV_ARM_VGIC_GRP_LEVEL_INFO
+  Attributes:
+    The attr field of kvm_device_attr encodes the following values:
+    bits:     | 63      ....       32 | 31   ....    10 | 9  ....  0 |
+    values:   |         mpidr         |      info       |   vINTID   |
+
+    The vINTID specifies which set of IRQs is reported on.
+
+    The info field specifies which information userspace wants to get or set
+    using this interface.  Currently we support the following info values:
+
+      VGIC_LEVEL_INFO_LINE_LEVEL:
+       Get/Set the input level of the IRQ line for a set of 32 contiguously
+       numbered interrupts.
+       vINTID must be a multiple of 32.
+
+       kvm_device_attr.addr points to a __u32 value which will contain a
+       bitmap where a set bit means the interrupt level is asserted.
+
+       Bit[n] indicates the status for interrupt vINTID + n.
+
+    SGIs and any interrupt with a higher ID than the number of interrupts
+    supported, will be RAZ/WI.  LPIs are always edge-triggered and are
+    therefore not supported by this interface.
+
+    PPIs are reported per VCPU as specified in the mpidr field, and SPIs are
+    reported with the same value regardless of the mpidr specified.
+
+    The mpidr field encodes the CPU ID based on the affinity information in the
+    architecture defined MPIDR, and the field is encoded as follows:
+      | 63 .... 56 | 55 .... 48 | 47 .... 40 | 39 .... 32 |
+      |    Aff3    |    Aff2    |    Aff1    |    Aff0    |
index 89182f80cc7f21a6329205795d2ceab6cbe01786..76e61c883347415f22242060b46ec13ac68cc09c 100644 (file)
@@ -1,24 +1,19 @@
-ARM Virtual Generic Interrupt Controller (VGIC)
-===============================================
+ARM Virtual Generic Interrupt Controller v2 (VGIC)
+==================================================
 
 Device types supported:
   KVM_DEV_TYPE_ARM_VGIC_V2     ARM Generic Interrupt Controller v2.0
-  KVM_DEV_TYPE_ARM_VGIC_V3     ARM Generic Interrupt Controller v3.0
-  KVM_DEV_TYPE_ARM_VGIC_ITS    ARM Interrupt Translation Service Controller
 
-Only one VGIC instance of the V2/V3 types above may be instantiated through
-either this API or the legacy KVM_CREATE_IRQCHIP api.  The created VGIC will
-act as the VM interrupt controller, requiring emulated user-space devices to
-inject interrupts to the VGIC instead of directly to CPUs.
+Only one VGIC instance may be instantiated through either this API or the
+legacy KVM_CREATE_IRQCHIP API.  The created VGIC will act as the VM interrupt
+controller, requiring emulated user-space devices to inject interrupts to the
+VGIC instead of directly to CPUs.
 
-Creating a guest GICv3 device requires a host GICv3 as well.
-GICv3 implementations with hardware compatibility support allow a guest GICv2
-as well.
+GICv3 implementations with hardware compatibility support allow creating a
+guest GICv2 through this interface.  For information on creating a guest GICv3
+device and guest ITS devices, see arm-vgic-v3.txt.  It is not possible to
+create both a GICv3 and GICv2 device on the same VM.
 
-Creating a virtual ITS controller requires a host GICv3 (but does not depend
-on having physical ITS controllers).
-There can be multiple ITS controllers per guest, each of them has to have
-a separate, non-overlapping MMIO region.
 
 Groups:
   KVM_DEV_ARM_VGIC_GRP_ADDR
@@ -32,26 +27,13 @@ Groups:
       Base address in the guest physical address space of the GIC virtual cpu
       interface register mappings. Only valid for KVM_DEV_TYPE_ARM_VGIC_V2.
       This address needs to be 4K aligned and the region covers 4 KByte.
-
-    KVM_VGIC_V3_ADDR_TYPE_DIST (rw, 64-bit)
-      Base address in the guest physical address space of the GICv3 distributor
-      register mappings. Only valid for KVM_DEV_TYPE_ARM_VGIC_V3.
-      This address needs to be 64K aligned and the region covers 64 KByte.
-
-    KVM_VGIC_V3_ADDR_TYPE_REDIST (rw, 64-bit)
-      Base address in the guest physical address space of the GICv3
-      redistributor register mappings. There are two 64K pages for each
-      VCPU and all of the redistributor pages are contiguous.
-      Only valid for KVM_DEV_TYPE_ARM_VGIC_V3.
-      This address needs to be 64K aligned.
-
-    KVM_VGIC_V3_ADDR_TYPE_ITS (rw, 64-bit)
-      Base address in the guest physical address space of the GICv3 ITS
-      control register frame. The ITS allows MSI(-X) interrupts to be
-      injected into guests. This extension is optional. If the kernel
-      does not support the ITS, the call returns -ENODEV.
-      Only valid for KVM_DEV_TYPE_ARM_VGIC_ITS.
-      This address needs to be 64K aligned and the region covers 128K.
+  Errors:
+    -E2BIG:  Address outside of addressable IPA range
+    -EINVAL: Incorrectly aligned address
+    -EEXIST: Address already configured
+    -ENXIO:  The group or attribute is unknown/unsupported for this device
+             or hardware support is missing.
+    -EFAULT: Invalid user pointer for attr->addr.
 
   KVM_DEV_ARM_VGIC_GRP_DIST_REGS
   Attributes:
index 58faff5f1eb2f39e5e82e691ddc3ce000a3a5b7a..05e47faf96a15f708590ccd002c95b12ef0c3709 100644 (file)
 
 #include <asm/virt.h>
 
+#define ARM_EXIT_WITH_ABORT_BIT  31
+#define ARM_EXCEPTION_CODE(x)    ((x) & ~(1U << ARM_EXIT_WITH_ABORT_BIT))
+#define ARM_ABORT_PENDING(x)     !!((x) & (1U << ARM_EXIT_WITH_ABORT_BIT))
+
 #define ARM_EXCEPTION_RESET      0
 #define ARM_EXCEPTION_UNDEFINED   1
 #define ARM_EXCEPTION_SOFTWARE    2
index ee5328fc4b066eaf68c252d375f76758392a08c7..9a8a45aaf19a4d4eeeece7d2616833644fe35090 100644 (file)
@@ -40,18 +40,29 @@ static inline void vcpu_set_reg(struct kvm_vcpu *vcpu, u8 reg_num,
        *vcpu_reg(vcpu, reg_num) = val;
 }
 
-bool kvm_condition_valid(struct kvm_vcpu *vcpu);
-void kvm_skip_instr(struct kvm_vcpu *vcpu, bool is_wide_instr);
+bool kvm_condition_valid32(const struct kvm_vcpu *vcpu);
+void kvm_skip_instr32(struct kvm_vcpu *vcpu, bool is_wide_instr);
 void kvm_inject_undefined(struct kvm_vcpu *vcpu);
+void kvm_inject_vabt(struct kvm_vcpu *vcpu);
 void kvm_inject_dabt(struct kvm_vcpu *vcpu, unsigned long addr);
 void kvm_inject_pabt(struct kvm_vcpu *vcpu, unsigned long addr);
 
+static inline bool kvm_condition_valid(const struct kvm_vcpu *vcpu)
+{
+       return kvm_condition_valid32(vcpu);
+}
+
+static inline void kvm_skip_instr(struct kvm_vcpu *vcpu, bool is_wide_instr)
+{
+       kvm_skip_instr32(vcpu, is_wide_instr);
+}
+
 static inline void vcpu_reset_hcr(struct kvm_vcpu *vcpu)
 {
        vcpu->arch.hcr = HCR_GUEST_MASK;
 }
 
-static inline unsigned long vcpu_get_hcr(struct kvm_vcpu *vcpu)
+static inline unsigned long vcpu_get_hcr(const struct kvm_vcpu *vcpu)
 {
        return vcpu->arch.hcr;
 }
@@ -61,7 +72,7 @@ static inline void vcpu_set_hcr(struct kvm_vcpu *vcpu, unsigned long hcr)
        vcpu->arch.hcr = hcr;
 }
 
-static inline bool vcpu_mode_is_32bit(struct kvm_vcpu *vcpu)
+static inline bool vcpu_mode_is_32bit(const struct kvm_vcpu *vcpu)
 {
        return 1;
 }
@@ -71,9 +82,9 @@ static inline unsigned long *vcpu_pc(struct kvm_vcpu *vcpu)
        return &vcpu->arch.ctxt.gp_regs.usr_regs.ARM_pc;
 }
 
-static inline unsigned long *vcpu_cpsr(struct kvm_vcpu *vcpu)
+static inline unsigned long *vcpu_cpsr(const struct kvm_vcpu *vcpu)
 {
-       return &vcpu->arch.ctxt.gp_regs.usr_regs.ARM_cpsr;
+       return (unsigned long *)&vcpu->arch.ctxt.gp_regs.usr_regs.ARM_cpsr;
 }
 
 static inline void vcpu_set_thumb(struct kvm_vcpu *vcpu)
@@ -93,11 +104,21 @@ static inline bool vcpu_mode_priv(struct kvm_vcpu *vcpu)
        return cpsr_mode > USR_MODE;;
 }
 
-static inline u32 kvm_vcpu_get_hsr(struct kvm_vcpu *vcpu)
+static inline u32 kvm_vcpu_get_hsr(const struct kvm_vcpu *vcpu)
 {
        return vcpu->arch.fault.hsr;
 }
 
+static inline int kvm_vcpu_get_condition(const struct kvm_vcpu *vcpu)
+{
+       u32 hsr = kvm_vcpu_get_hsr(vcpu);
+
+       if (hsr & HSR_CV)
+               return (hsr & HSR_COND) >> HSR_COND_SHIFT;
+
+       return -1;
+}
+
 static inline unsigned long kvm_vcpu_get_hfar(struct kvm_vcpu *vcpu)
 {
        return vcpu->arch.fault.hxfar;
index 3bb803d6814b2a15434cc512abe1b0018d343f36..74a44727f8e1e4dfbcfdea0f0c302e9201a56f00 100644 (file)
@@ -63,37 +63,13 @@ void kvm_clear_hyp_idmap(void);
 static inline void kvm_set_pmd(pmd_t *pmd, pmd_t new_pmd)
 {
        *pmd = new_pmd;
-       flush_pmd_entry(pmd);
+       dsb(ishst);
 }
 
 static inline void kvm_set_pte(pte_t *pte, pte_t new_pte)
 {
        *pte = new_pte;
-       /*
-        * flush_pmd_entry just takes a void pointer and cleans the necessary
-        * cache entries, so we can reuse the function for ptes.
-        */
-       flush_pmd_entry(pte);
-}
-
-static inline void kvm_clean_pgd(pgd_t *pgd)
-{
-       clean_dcache_area(pgd, PTRS_PER_S2_PGD * sizeof(pgd_t));
-}
-
-static inline void kvm_clean_pmd(pmd_t *pmd)
-{
-       clean_dcache_area(pmd, PTRS_PER_PMD * sizeof(pmd_t));
-}
-
-static inline void kvm_clean_pmd_entry(pmd_t *pmd)
-{
-       clean_pmd_entry(pmd);
-}
-
-static inline void kvm_clean_pte(pte_t *pte)
-{
-       clean_pte_table(pte);
+       dsb(ishst);
 }
 
 static inline pte_t kvm_s2pte_mkwrite(pte_t pte)
index 10d77a66cad5d83de04ea02987733169518c22f0..339ec88a15a67d18b029c5b205b4ec67c85f96b0 100644 (file)
@@ -21,6 +21,7 @@ obj-$(CONFIG_KVM_ARM_HOST) += hyp/
 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-y += $(KVM)/arm/aarch32.o
 
 obj-y += $(KVM)/arm/vgic/vgic.o
 obj-y += $(KVM)/arm/vgic/vgic-init.o
index af93e3ffc9f308d80e8e93b80c4563aec6057628..0064b86a2c87936ccc69cb3d413ce151e85f5933 100644 (file)
@@ -161,105 +161,6 @@ unsigned long *vcpu_spsr(struct kvm_vcpu *vcpu)
        }
 }
 
-/*
- * A conditional instruction is allowed to trap, even though it
- * wouldn't be executed.  So let's re-implement the hardware, in
- * software!
- */
-bool kvm_condition_valid(struct kvm_vcpu *vcpu)
-{
-       unsigned long cpsr, cond, insn;
-
-       /*
-        * Exception Code 0 can only happen if we set HCR.TGE to 1, to
-        * catch undefined instructions, and then we won't get past
-        * the arm_exit_handlers test anyway.
-        */
-       BUG_ON(!kvm_vcpu_trap_get_class(vcpu));
-
-       /* Top two bits non-zero?  Unconditional. */
-       if (kvm_vcpu_get_hsr(vcpu) >> 30)
-               return true;
-
-       cpsr = *vcpu_cpsr(vcpu);
-
-       /* Is condition field valid? */
-       if ((kvm_vcpu_get_hsr(vcpu) & HSR_CV) >> HSR_CV_SHIFT)
-               cond = (kvm_vcpu_get_hsr(vcpu) & HSR_COND) >> HSR_COND_SHIFT;
-       else {
-               /* This can happen in Thumb mode: examine IT state. */
-               unsigned long it;
-
-               it = ((cpsr >> 8) & 0xFC) | ((cpsr >> 25) & 0x3);
-
-               /* it == 0 => unconditional. */
-               if (it == 0)
-                       return true;
-
-               /* The cond for this insn works out as the top 4 bits. */
-               cond = (it >> 4);
-       }
-
-       /* Shift makes it look like an ARM-mode instruction */
-       insn = cond << 28;
-       return arm_check_condition(insn, cpsr) != ARM_OPCODE_CONDTEST_FAIL;
-}
-
-/**
- * adjust_itstate - adjust ITSTATE when emulating instructions in IT-block
- * @vcpu:      The VCPU pointer
- *
- * When exceptions occur while instructions are executed in Thumb IF-THEN
- * blocks, the ITSTATE field of the CPSR is not advanced (updated), so we have
- * to do this little bit of work manually. The fields map like this:
- *
- * IT[7:0] -> CPSR[26:25],CPSR[15:10]
- */
-static void kvm_adjust_itstate(struct kvm_vcpu *vcpu)
-{
-       unsigned long itbits, cond;
-       unsigned long cpsr = *vcpu_cpsr(vcpu);
-       bool is_arm = !(cpsr & PSR_T_BIT);
-
-       BUG_ON(is_arm && (cpsr & PSR_IT_MASK));
-
-       if (!(cpsr & PSR_IT_MASK))
-               return;
-
-       cond = (cpsr & 0xe000) >> 13;
-       itbits = (cpsr & 0x1c00) >> (10 - 2);
-       itbits |= (cpsr & (0x3 << 25)) >> 25;
-
-       /* Perform ITAdvance (see page A-52 in ARM DDI 0406C) */
-       if ((itbits & 0x7) == 0)
-               itbits = cond = 0;
-       else
-               itbits = (itbits << 1) & 0x1f;
-
-       cpsr &= ~PSR_IT_MASK;
-       cpsr |= cond << 13;
-       cpsr |= (itbits & 0x1c) << (10 - 2);
-       cpsr |= (itbits & 0x3) << 25;
-       *vcpu_cpsr(vcpu) = cpsr;
-}
-
-/**
- * kvm_skip_instr - skip a trapped instruction and proceed to the next
- * @vcpu: The vcpu pointer
- */
-void kvm_skip_instr(struct kvm_vcpu *vcpu, bool is_wide_instr)
-{
-       bool is_thumb;
-
-       is_thumb = !!(*vcpu_cpsr(vcpu) & PSR_T_BIT);
-       if (is_thumb && !is_wide_instr)
-               *vcpu_pc(vcpu) += 2;
-       else
-               *vcpu_pc(vcpu) += 4;
-       kvm_adjust_itstate(vcpu);
-}
-
-
 /******************************************************************************
  * Inject exceptions into the guest
  */
@@ -402,3 +303,15 @@ void kvm_inject_pabt(struct kvm_vcpu *vcpu, unsigned long addr)
 {
        inject_abt(vcpu, true, addr);
 }
+
+/**
+ * kvm_inject_vabt - inject an async abort / SError into the guest
+ * @vcpu: The VCPU to receive the exception
+ *
+ * It is assumed that this code is called from the VCPU thread and that the
+ * VCPU therefore is not currently executing guest code.
+ */
+void kvm_inject_vabt(struct kvm_vcpu *vcpu)
+{
+       vcpu_set_hcr(vcpu, vcpu_get_hcr(vcpu) | HCR_VA);
+}
index 3f1ef0dbc899182cae378773064c67e2e2289eb3..4e40d1955e35341b7756efe72f2da6bf2360b224 100644 (file)
 
 typedef int (*exit_handle_fn)(struct kvm_vcpu *, struct kvm_run *);
 
-static int handle_svc_hyp(struct kvm_vcpu *vcpu, struct kvm_run *run)
-{
-       /* SVC called from Hyp mode should never get here */
-       kvm_debug("SVC called from Hyp mode shouldn't go here\n");
-       BUG();
-       return -EINVAL; /* Squash warning */
-}
-
 static int handle_hvc(struct kvm_vcpu *vcpu, struct kvm_run *run)
 {
        int ret;
@@ -59,22 +51,6 @@ static int handle_smc(struct kvm_vcpu *vcpu, struct kvm_run *run)
        return 1;
 }
 
-static int handle_pabt_hyp(struct kvm_vcpu *vcpu, struct kvm_run *run)
-{
-       /* The hypervisor should never cause aborts */
-       kvm_err("Prefetch Abort taken from Hyp mode at %#08lx (HSR: %#08x)\n",
-               kvm_vcpu_get_hfar(vcpu), kvm_vcpu_get_hsr(vcpu));
-       return -EFAULT;
-}
-
-static int handle_dabt_hyp(struct kvm_vcpu *vcpu, struct kvm_run *run)
-{
-       /* This is either an error in the ws. code or an external abort */
-       kvm_err("Data Abort taken from Hyp mode at %#08lx (HSR: %#08x)\n",
-               kvm_vcpu_get_hfar(vcpu), kvm_vcpu_get_hsr(vcpu));
-       return -EFAULT;
-}
-
 /**
  * kvm_handle_wfx - handle a WFI or WFE instructions trapped in guests
  * @vcpu:      the vcpu pointer
@@ -112,13 +88,10 @@ static exit_handle_fn arm_exit_handlers[] = {
        [HSR_EC_CP14_64]        = kvm_handle_cp14_access,
        [HSR_EC_CP_0_13]        = kvm_handle_cp_0_13_access,
        [HSR_EC_CP10_ID]        = kvm_handle_cp10_id,
-       [HSR_EC_SVC_HYP]        = handle_svc_hyp,
        [HSR_EC_HVC]            = handle_hvc,
        [HSR_EC_SMC]            = handle_smc,
        [HSR_EC_IABT]           = kvm_handle_guest_abort,
-       [HSR_EC_IABT_HYP]       = handle_pabt_hyp,
        [HSR_EC_DABT]           = kvm_handle_guest_abort,
-       [HSR_EC_DABT_HYP]       = handle_dabt_hyp,
 };
 
 static exit_handle_fn kvm_get_exit_handler(struct kvm_vcpu *vcpu)
@@ -144,6 +117,25 @@ int handle_exit(struct kvm_vcpu *vcpu, struct kvm_run *run,
 {
        exit_handle_fn exit_handler;
 
+       if (ARM_ABORT_PENDING(exception_index)) {
+               u8 hsr_ec = kvm_vcpu_trap_get_class(vcpu);
+
+               /*
+                * HVC/SMC already have an adjusted PC, which we need
+                * to correct in order to return to after having
+                * injected the abort.
+                */
+               if (hsr_ec == HSR_EC_HVC || hsr_ec == HSR_EC_SMC) {
+                       u32 adj =  kvm_vcpu_trap_il_is32bit(vcpu) ? 4 : 2;
+                       *vcpu_pc(vcpu) -= adj;
+               }
+
+               kvm_inject_vabt(vcpu);
+               return 1;
+       }
+
+       exception_index = ARM_EXCEPTION_CODE(exception_index);
+
        switch (exception_index) {
        case ARM_EXCEPTION_IRQ:
                return 1;
@@ -160,6 +152,9 @@ int handle_exit(struct kvm_vcpu *vcpu, struct kvm_run *run,
                exit_handler = kvm_get_exit_handler(vcpu);
 
                return exit_handler(vcpu, run);
+       case ARM_EXCEPTION_DATA_ABORT:
+               kvm_inject_vabt(vcpu);
+               return 1;
        default:
                kvm_pr_unimpl("Unsupported exception type: %d",
                              exception_index);
index 21c238871c9e2e11ea02599c20f238cfacda2ba7..60783f3b57cc3ce300155f9a50867b892f95273c 100644 (file)
@@ -18,6 +18,7 @@
 #include <linux/linkage.h>
 #include <asm/asm-offsets.h>
 #include <asm/kvm_arm.h>
+#include <asm/kvm_asm.h>
 
        .arch_extension     virt
 
@@ -63,6 +64,36 @@ ENTRY(__guest_exit)
        ldr     lr, [r0, #4]
 
        mov     r0, r1
+       mrs     r1, SPSR
+       mrs     r2, ELR_hyp
+       mrc     p15, 4, r3, c5, c2, 0   @ HSR
+
+       /*
+        * Force loads and stores to complete before unmasking aborts
+        * and forcing the delivery of the exception. This gives us a
+        * single instruction window, which the handler will try to
+        * match.
+        */
+       dsb     sy
+       cpsie   a
+
+       .global abort_guest_exit_start
+abort_guest_exit_start:
+
+       isb
+
+       .global abort_guest_exit_end
+abort_guest_exit_end:
+
+       /*
+        * If we took an abort, r0[31] will be set, and cmp will set
+        * the N bit in PSTATE.
+        */
+       cmp     r0, #0
+       msrmi   SPSR_cxsf, r1
+       msrmi   ELR_hyp, r2
+       mcrmi   p15, 4, r3, c5, c2, 0   @ HSR
+
        bx      lr
 ENDPROC(__guest_exit)
 
index 78091383a5d91e02a57629b732989a51d81bcabc..96beb53934c9769d13f7c58f5e574a065638c42c 100644 (file)
@@ -81,7 +81,6 @@ __kvm_hyp_vector:
        invalid_vector  hyp_undef       ARM_EXCEPTION_UNDEFINED
        invalid_vector  hyp_svc         ARM_EXCEPTION_SOFTWARE
        invalid_vector  hyp_pabt        ARM_EXCEPTION_PREF_ABORT
-       invalid_vector  hyp_dabt        ARM_EXCEPTION_DATA_ABORT
        invalid_vector  hyp_fiq         ARM_EXCEPTION_FIQ
 
 ENTRY(__hyp_do_panic)
@@ -164,6 +163,21 @@ hyp_irq:
        load_vcpu r0                    @ Load VCPU pointer to r0
        b       __guest_exit
 
+hyp_dabt:
+       push    {r0, r1}
+       mrs     r0, ELR_hyp
+       ldr     r1, =abort_guest_exit_start
+THUMB( add     r1, r1, #1)
+       cmp     r0, r1
+       ldrne   r1, =abort_guest_exit_end
+THUMB( addne   r1, r1, #1)
+       cmpne   r0, r1
+       pop     {r0, r1}
+       bne     __hyp_panic
+
+       orr     r0, r0, #(1 << ARM_EXIT_WITH_ABORT_BIT)
+       eret
+
        .ltorg
 
        .popsection
index b13caa90cd44a55d8dfc60f0405ee514ecfd2dff..9da16fd1005f343e0fa046cc43461d8ebf060271 100644 (file)
@@ -54,6 +54,15 @@ static void __hyp_text __deactivate_traps(struct kvm_vcpu *vcpu)
 {
        u32 val;
 
+       /*
+        * If we pended a virtual abort, preserve it until it gets
+        * cleared. See B1.9.9 (Virtual Abort exception) for details,
+        * but the crucial bit is the zeroing of HCR.VA in the
+        * pseudocode.
+        */
+       if (vcpu->arch.hcr & HCR_VA)
+               vcpu->arch.hcr = read_sysreg(HCR);
+
        write_sysreg(0, HCR);
        write_sysreg(0, HSTR);
        val = read_sysreg(HDCR);
@@ -134,7 +143,7 @@ static bool __hyp_text __populate_fault_info(struct kvm_vcpu *vcpu)
        return true;
 }
 
-static int __hyp_text __guest_run(struct kvm_vcpu *vcpu)
+int __hyp_text __kvm_vcpu_run(struct kvm_vcpu *vcpu)
 {
        struct kvm_cpu_context *host_ctxt;
        struct kvm_cpu_context *guest_ctxt;
@@ -191,8 +200,6 @@ again:
        return exit_code;
 }
 
-__alias(__guest_run) int __kvm_vcpu_run(struct kvm_vcpu *vcpu);
-
 static const char * const __hyp_panic_string[] = {
        [ARM_EXCEPTION_RESET]      = "\nHYP panic: RST   PC:%08x CPSR:%08x",
        [ARM_EXCEPTION_UNDEFINED]  = "\nHYP panic: UNDEF PC:%08x CPSR:%08x",
index a2636001e616b70836dcea74b4d1486709282df3..729652854f9098d677bd59871452c7b8c1ea240b 100644 (file)
@@ -34,7 +34,7 @@
  * As v7 does not support flushing per IPA, just nuke the whole TLB
  * instead, ignoring the ipa value.
  */
-static void __hyp_text __tlb_flush_vmid(struct kvm *kvm)
+void __hyp_text __kvm_tlb_flush_vmid(struct kvm *kvm)
 {
        dsb(ishst);
 
@@ -50,21 +50,14 @@ static void __hyp_text __tlb_flush_vmid(struct kvm *kvm)
        write_sysreg(0, VTTBR);
 }
 
-__alias(__tlb_flush_vmid) void __kvm_tlb_flush_vmid(struct kvm *kvm);
-
-static void __hyp_text __tlb_flush_vmid_ipa(struct kvm *kvm, phys_addr_t ipa)
+void __hyp_text __kvm_tlb_flush_vmid_ipa(struct kvm *kvm, phys_addr_t ipa)
 {
-       __tlb_flush_vmid(kvm);
+       __kvm_tlb_flush_vmid(kvm);
 }
 
-__alias(__tlb_flush_vmid_ipa) void __kvm_tlb_flush_vmid_ipa(struct kvm *kvm,
-                                                           phys_addr_t ipa);
-
-static void __hyp_text __tlb_flush_vm_context(void)
+void __hyp_text __kvm_flush_vm_context(void)
 {
        write_sysreg(0, TLBIALLNSNHIS);
        write_sysreg(0, ICIALLUIS);
        dsb(ish);
 }
-
-__alias(__tlb_flush_vm_context) void __kvm_flush_vm_context(void);
index 10f80a6c797a2b6b67363b6f163ef5b85b28465e..b6e715fd3c90af8c74408b72652f9974a3fb894d 100644 (file)
@@ -126,12 +126,6 @@ static int decode_hsr(struct kvm_vcpu *vcpu, bool *is_write, int *len)
        int access_size;
        bool sign_extend;
 
-       if (kvm_vcpu_dabt_isextabt(vcpu)) {
-               /* cache operation on I/O addr, tell guest unsupported */
-               kvm_inject_dabt(vcpu, kvm_vcpu_get_hfar(vcpu));
-               return 1;
-       }
-
        if (kvm_vcpu_dabt_iss1tw(vcpu)) {
                /* page table accesses IO mem: tell guest to fix its TTBR */
                kvm_inject_dabt(vcpu, kvm_vcpu_get_hfar(vcpu));
index 29d0b23af2a9dec3b2199ac3851c9ae74b0731ee..60e0c1ac86e823f1d0ae4ce19920f9a4de2643c5 100644 (file)
@@ -744,7 +744,6 @@ int kvm_alloc_stage2_pgd(struct kvm *kvm)
        if (!pgd)
                return -ENOMEM;
 
-       kvm_clean_pgd(pgd);
        kvm->arch.pgd = pgd;
        return 0;
 }
@@ -936,7 +935,6 @@ static int stage2_set_pte(struct kvm *kvm, struct kvm_mmu_memory_cache *cache,
                if (!cache)
                        return 0; /* ignore calls from kvm_set_spte_hva */
                pte = mmu_memory_cache_alloc(cache);
-               kvm_clean_pte(pte);
                pmd_populate_kernel(NULL, pmd, pte);
                get_page(virt_to_page(pmd));
        }
@@ -1434,6 +1432,11 @@ int kvm_handle_guest_abort(struct kvm_vcpu *vcpu, struct kvm_run *run)
        int ret, idx;
 
        is_iabt = kvm_vcpu_trap_is_iabt(vcpu);
+       if (unlikely(!is_iabt && kvm_vcpu_dabt_isextabt(vcpu))) {
+               kvm_inject_vabt(vcpu);
+               return 1;
+       }
+
        fault_ipa = kvm_vcpu_get_fault_ipa(vcpu);
 
        trace_kvm_guest_fault(*vcpu_pc(vcpu), kvm_vcpu_get_hsr(vcpu),
index 4b5c977af4653d69a6b2e5a8db863f71c0eca1cb..2a2752b5b6aa7a424c956d4dd0ec2642644762de 100644 (file)
@@ -50,7 +50,7 @@
 #define HCR_BSU                (3 << 10)
 #define HCR_BSU_IS     (UL(1) << 10)
 #define HCR_FB         (UL(1) << 9)
-#define HCR_VA         (UL(1) << 8)
+#define HCR_VSE                (UL(1) << 8)
 #define HCR_VI         (UL(1) << 7)
 #define HCR_VF         (UL(1) << 6)
 #define HCR_AMO                (UL(1) << 5)
@@ -80,7 +80,7 @@
 #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_SWIO | HCR_TIDCP | HCR_RW)
-#define HCR_VIRT_EXCP_MASK (HCR_VA | HCR_VI | HCR_VF)
+#define HCR_VIRT_EXCP_MASK (HCR_VSE | HCR_VI | HCR_VF)
 #define HCR_INT_OVERRIDE   (HCR_FMO | HCR_IMO)
 #define HCR_HOST_VHE_FLAGS (HCR_RW | HCR_TGE | HCR_E2H)
 
index 7561f63f1c28202ddd15aae74c16db4f35228d0d..18f746551bf632cc88a3a406359463e9d49340eb 100644 (file)
 
 #include <asm/virt.h>
 
+#define ARM_EXIT_WITH_SERROR_BIT  31
+#define ARM_EXCEPTION_CODE(x)    ((x) & ~(1U << ARM_EXIT_WITH_SERROR_BIT))
+#define ARM_SERROR_PENDING(x)    !!((x) & (1U << ARM_EXIT_WITH_SERROR_BIT))
+
 #define ARM_EXCEPTION_IRQ        0
-#define ARM_EXCEPTION_TRAP       1
+#define ARM_EXCEPTION_EL1_SERROR  1
+#define ARM_EXCEPTION_TRAP       2
 /* The hyp-stub will return this for any kvm_call_hyp() call */
-#define ARM_EXCEPTION_HYP_GONE   2
+#define ARM_EXCEPTION_HYP_GONE   3
 
 #define KVM_ARM64_DEBUG_DIRTY_SHIFT    0
 #define KVM_ARM64_DEBUG_DIRTY          (1 << KVM_ARM64_DEBUG_DIRTY_SHIFT)
index 4cdeae3b17c6015bc1ec24df7e49f361aff54152..fd9d5fd788f5f1df75febd7849bc89bc15eaf144 100644 (file)
@@ -38,6 +38,7 @@ bool kvm_condition_valid32(const struct kvm_vcpu *vcpu);
 void kvm_skip_instr32(struct kvm_vcpu *vcpu, bool is_wide_instr);
 
 void kvm_inject_undefined(struct kvm_vcpu *vcpu);
+void kvm_inject_vabt(struct kvm_vcpu *vcpu);
 void kvm_inject_dabt(struct kvm_vcpu *vcpu, unsigned long addr);
 void kvm_inject_pabt(struct kvm_vcpu *vcpu, unsigned long addr);
 
@@ -147,6 +148,16 @@ static inline u32 kvm_vcpu_get_hsr(const struct kvm_vcpu *vcpu)
        return vcpu->arch.fault.esr_el2;
 }
 
+static inline int kvm_vcpu_get_condition(const struct kvm_vcpu *vcpu)
+{
+       u32 esr = kvm_vcpu_get_hsr(vcpu);
+
+       if (esr & ESR_ELx_CV)
+               return (esr & ESR_ELx_COND_MASK) >> ESR_ELx_COND_SHIFT;
+
+       return -1;
+}
+
 static inline unsigned long kvm_vcpu_get_hfar(const struct kvm_vcpu *vcpu)
 {
        return vcpu->arch.fault.far_el2;
index cff510574fae03f210bb1ef947bcaa5c4dd694a6..b18e852d27e85728db336fe3b7af10a1f97286e0 100644 (file)
@@ -123,6 +123,7 @@ typeof(orig) * __hyp_text fname(void)                                       \
 
 void __vgic_v2_save_state(struct kvm_vcpu *vcpu);
 void __vgic_v2_restore_state(struct kvm_vcpu *vcpu);
+int __vgic_v2_perform_cpuif_access(struct kvm_vcpu *vcpu);
 
 void __vgic_v3_save_state(struct kvm_vcpu *vcpu);
 void __vgic_v3_restore_state(struct kvm_vcpu *vcpu);
index dff109871f2af92ddf4ce31088f5e1a8b054b510..a79b969c26fca7dcd73e06c4bfc36c6519cb646b 100644 (file)
@@ -162,12 +162,6 @@ void kvm_clear_hyp_idmap(void);
 #define        kvm_set_pte(ptep, pte)          set_pte(ptep, pte)
 #define        kvm_set_pmd(pmdp, pmd)          set_pmd(pmdp, pmd)
 
-static inline void kvm_clean_pgd(pgd_t *pgd) {}
-static inline void kvm_clean_pmd(pmd_t *pmd) {}
-static inline void kvm_clean_pmd_entry(pmd_t *pmd) {}
-static inline void kvm_clean_pte(pte_t *pte) {}
-static inline void kvm_clean_pte_entry(pte_t *pte) {}
-
 static inline pte_t kvm_s2pte_mkwrite(pte_t pte)
 {
        pte_val(pte) |= PTE_S2_RDWR;
index 695eb3c7ef41fa0436caa9e32b911f7268032cd6..d50a82a16ff68cfb5f57808a0f20b08166a75788 100644 (file)
@@ -16,9 +16,10 @@ kvm-$(CONFIG_KVM_ARM_HOST) += $(KVM)/kvm_main.o $(KVM)/coalesced_mmio.o $(KVM)/e
 kvm-$(CONFIG_KVM_ARM_HOST) += $(ARM)/arm.o $(ARM)/mmu.o $(ARM)/mmio.o
 kvm-$(CONFIG_KVM_ARM_HOST) += $(ARM)/psci.o $(ARM)/perf.o
 
-kvm-$(CONFIG_KVM_ARM_HOST) += emulate.o inject_fault.o regmap.o
+kvm-$(CONFIG_KVM_ARM_HOST) += inject_fault.o regmap.o
 kvm-$(CONFIG_KVM_ARM_HOST) += hyp.o hyp-init.o handle_exit.o
 kvm-$(CONFIG_KVM_ARM_HOST) += guest.o debug.o reset.o sys_regs.o sys_regs_generic_v8.o
+kvm-$(CONFIG_KVM_ARM_HOST) += $(KVM)/arm/aarch32.o
 
 kvm-$(CONFIG_KVM_ARM_HOST) += $(KVM)/arm/vgic/vgic.o
 kvm-$(CONFIG_KVM_ARM_HOST) += $(KVM)/arm/vgic/vgic-init.o
diff --git a/arch/arm64/kvm/emulate.c b/arch/arm64/kvm/emulate.c
deleted file mode 100644 (file)
index f87d8fb..0000000
+++ /dev/null
@@ -1,159 +0,0 @@
-/*
- * (not much of an) Emulation layer for 32bit guests.
- *
- * Copyright (C) 2012,2013 - ARM Ltd
- * Author: Marc Zyngier <marc.zyngier@arm.com>
- *
- * based on arch/arm/kvm/emulate.c
- * Copyright (C) 2012 - Virtual Open Systems and Columbia University
- * Author: Christoffer Dall <c.dall@virtualopensystems.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/kvm_host.h>
-#include <asm/esr.h>
-#include <asm/kvm_emulate.h>
-
-/*
- * stolen from arch/arm/kernel/opcodes.c
- *
- * condition code lookup table
- * index into the table is test code: EQ, NE, ... LT, GT, AL, NV
- *
- * bit position in short is condition code: NZCV
- */
-static const unsigned short cc_map[16] = {
-       0xF0F0,                 /* EQ == Z set            */
-       0x0F0F,                 /* NE                     */
-       0xCCCC,                 /* CS == C set            */
-       0x3333,                 /* CC                     */
-       0xFF00,                 /* MI == N set            */
-       0x00FF,                 /* PL                     */
-       0xAAAA,                 /* VS == V set            */
-       0x5555,                 /* VC                     */
-       0x0C0C,                 /* HI == C set && Z clear */
-       0xF3F3,                 /* LS == C clear || Z set */
-       0xAA55,                 /* GE == (N==V)           */
-       0x55AA,                 /* LT == (N!=V)           */
-       0x0A05,                 /* GT == (!Z && (N==V))   */
-       0xF5FA,                 /* LE == (Z || (N!=V))    */
-       0xFFFF,                 /* AL always              */
-       0                       /* NV                     */
-};
-
-static int kvm_vcpu_get_condition(const struct kvm_vcpu *vcpu)
-{
-       u32 esr = kvm_vcpu_get_hsr(vcpu);
-
-       if (esr & ESR_ELx_CV)
-               return (esr & ESR_ELx_COND_MASK) >> ESR_ELx_COND_SHIFT;
-
-       return -1;
-}
-
-/*
- * Check if a trapped instruction should have been executed or not.
- */
-bool kvm_condition_valid32(const struct kvm_vcpu *vcpu)
-{
-       unsigned long cpsr;
-       u32 cpsr_cond;
-       int cond;
-
-       /* Top two bits non-zero?  Unconditional. */
-       if (kvm_vcpu_get_hsr(vcpu) >> 30)
-               return true;
-
-       /* Is condition field valid? */
-       cond = kvm_vcpu_get_condition(vcpu);
-       if (cond == 0xE)
-               return true;
-
-       cpsr = *vcpu_cpsr(vcpu);
-
-       if (cond < 0) {
-               /* This can happen in Thumb mode: examine IT state. */
-               unsigned long it;
-
-               it = ((cpsr >> 8) & 0xFC) | ((cpsr >> 25) & 0x3);
-
-               /* it == 0 => unconditional. */
-               if (it == 0)
-                       return true;
-
-               /* The cond for this insn works out as the top 4 bits. */
-               cond = (it >> 4);
-       }
-
-       cpsr_cond = cpsr >> 28;
-
-       if (!((cc_map[cond] >> cpsr_cond) & 1))
-               return false;
-
-       return true;
-}
-
-/**
- * adjust_itstate - adjust ITSTATE when emulating instructions in IT-block
- * @vcpu:      The VCPU pointer
- *
- * When exceptions occur while instructions are executed in Thumb IF-THEN
- * blocks, the ITSTATE field of the CPSR is not advanced (updated), so we have
- * to do this little bit of work manually. The fields map like this:
- *
- * IT[7:0] -> CPSR[26:25],CPSR[15:10]
- */
-static void kvm_adjust_itstate(struct kvm_vcpu *vcpu)
-{
-       unsigned long itbits, cond;
-       unsigned long cpsr = *vcpu_cpsr(vcpu);
-       bool is_arm = !(cpsr & COMPAT_PSR_T_BIT);
-
-       BUG_ON(is_arm && (cpsr & COMPAT_PSR_IT_MASK));
-
-       if (!(cpsr & COMPAT_PSR_IT_MASK))
-               return;
-
-       cond = (cpsr & 0xe000) >> 13;
-       itbits = (cpsr & 0x1c00) >> (10 - 2);
-       itbits |= (cpsr & (0x3 << 25)) >> 25;
-
-       /* Perform ITAdvance (see page A2-52 in ARM DDI 0406C) */
-       if ((itbits & 0x7) == 0)
-               itbits = cond = 0;
-       else
-               itbits = (itbits << 1) & 0x1f;
-
-       cpsr &= ~COMPAT_PSR_IT_MASK;
-       cpsr |= cond << 13;
-       cpsr |= (itbits & 0x1c) << (10 - 2);
-       cpsr |= (itbits & 0x3) << 25;
-       *vcpu_cpsr(vcpu) = cpsr;
-}
-
-/**
- * kvm_skip_instr - skip a trapped instruction and proceed to the next
- * @vcpu: The vcpu pointer
- */
-void kvm_skip_instr32(struct kvm_vcpu *vcpu, bool is_wide_instr)
-{
-       bool is_thumb;
-
-       is_thumb = !!(*vcpu_cpsr(vcpu) & COMPAT_PSR_T_BIT);
-       if (is_thumb && !is_wide_instr)
-               *vcpu_pc(vcpu) += 2;
-       else
-               *vcpu_pc(vcpu) += 4;
-       kvm_adjust_itstate(vcpu);
-}
index fa96fe2bd46969130e2af69c02a8baa33f35a107..a204adf29f0a6dedd1c47485564086ef9682f82b 100644 (file)
@@ -170,9 +170,32 @@ int handle_exit(struct kvm_vcpu *vcpu, struct kvm_run *run,
 {
        exit_handle_fn exit_handler;
 
+       if (ARM_SERROR_PENDING(exception_index)) {
+               u8 hsr_ec = ESR_ELx_EC(kvm_vcpu_get_hsr(vcpu));
+
+               /*
+                * HVC/SMC already have an adjusted PC, which we need
+                * to correct in order to return to after having
+                * injected the SError.
+                */
+               if (hsr_ec == ESR_ELx_EC_HVC32 || hsr_ec == ESR_ELx_EC_HVC64 ||
+                   hsr_ec == ESR_ELx_EC_SMC32 || hsr_ec == ESR_ELx_EC_SMC64) {
+                       u32 adj =  kvm_vcpu_trap_il_is32bit(vcpu) ? 4 : 2;
+                       *vcpu_pc(vcpu) -= adj;
+               }
+
+               kvm_inject_vabt(vcpu);
+               return 1;
+       }
+
+       exception_index = ARM_EXCEPTION_CODE(exception_index);
+
        switch (exception_index) {
        case ARM_EXCEPTION_IRQ:
                return 1;
+       case ARM_EXCEPTION_EL1_SERROR:
+               kvm_inject_vabt(vcpu);
+               return 1;
        case ARM_EXCEPTION_TRAP:
                /*
                 * See ARM ARM B1.14.1: "Hyp traps on instructions
index 33342a776ec75845069a93381a3465cdf435509a..4ba5c9095d0326216d7b7cda164d81149b9b1e2d 100644 (file)
@@ -131,9 +131,7 @@ void __hyp_text __debug_cond_restore_host_state(struct kvm_vcpu *vcpu)
                vcpu->arch.debug_flags &= ~KVM_ARM64_DEBUG_DIRTY;
 }
 
-static u32 __hyp_text __debug_read_mdcr_el2(void)
+u32 __hyp_text __kvm_get_mdcr_el2(void)
 {
        return read_sysreg(mdcr_el2);
 }
-
-__alias(__debug_read_mdcr_el2) u32 __kvm_get_mdcr_el2(void);
index ce9e5e5f28cfb782c15090aea84bb282cd0b56c2..12ee62d6d410803e2aa7a10dd0df9acb41cf3076 100644 (file)
  */
 ENTRY(__guest_enter)
        // x0: vcpu
-       // x1: host/guest context
-       // x2-x18: clobbered by macros
+       // x1: host context
+       // x2-x17: clobbered by macros
+       // x18: guest context
 
        // Store the host regs
        save_callee_saved_regs x1
 
-       // Preserve vcpu & host_ctxt for use at exit time
-       stp     x0, x1, [sp, #-16]!
+       // Store the host_ctxt for use at exit time
+       str     x1, [sp, #-16]!
 
-       add     x1, x0, #VCPU_CONTEXT
+       add     x18, x0, #VCPU_CONTEXT
 
-       // Prepare x0-x1 for later restore by pushing them onto the stack
-       ldp     x2, x3, [x1, #CPU_XREG_OFFSET(0)]
-       stp     x2, x3, [sp, #-16]!
+       // Restore guest regs x0-x17
+       ldp     x0, x1,   [x18, #CPU_XREG_OFFSET(0)]
+       ldp     x2, x3,   [x18, #CPU_XREG_OFFSET(2)]
+       ldp     x4, x5,   [x18, #CPU_XREG_OFFSET(4)]
+       ldp     x6, x7,   [x18, #CPU_XREG_OFFSET(6)]
+       ldp     x8, x9,   [x18, #CPU_XREG_OFFSET(8)]
+       ldp     x10, x11, [x18, #CPU_XREG_OFFSET(10)]
+       ldp     x12, x13, [x18, #CPU_XREG_OFFSET(12)]
+       ldp     x14, x15, [x18, #CPU_XREG_OFFSET(14)]
+       ldp     x16, x17, [x18, #CPU_XREG_OFFSET(16)]
 
-       // x2-x18
-       ldp     x2, x3,   [x1, #CPU_XREG_OFFSET(2)]
-       ldp     x4, x5,   [x1, #CPU_XREG_OFFSET(4)]
-       ldp     x6, x7,   [x1, #CPU_XREG_OFFSET(6)]
-       ldp     x8, x9,   [x1, #CPU_XREG_OFFSET(8)]
-       ldp     x10, x11, [x1, #CPU_XREG_OFFSET(10)]
-       ldp     x12, x13, [x1, #CPU_XREG_OFFSET(12)]
-       ldp     x14, x15, [x1, #CPU_XREG_OFFSET(14)]
-       ldp     x16, x17, [x1, #CPU_XREG_OFFSET(16)]
-       ldr     x18,      [x1, #CPU_XREG_OFFSET(18)]
-
-       // x19-x29, lr
-       restore_callee_saved_regs x1
-
-       // Last bits of the 64bit state
-       ldp     x0, x1, [sp], #16
+       // Restore guest regs x19-x29, lr
+       restore_callee_saved_regs x18
+
+       // Restore guest reg x18
+       ldr     x18,      [x18, #CPU_XREG_OFFSET(18)]
 
        // Do not touch any register after this!
        eret
 ENDPROC(__guest_enter)
 
 ENTRY(__guest_exit)
-       // x0: vcpu
-       // x1: return code
-       // x2-x3: free
-       // x4-x29,lr: vcpu regs
-       // vcpu x0-x3 on the stack
+       // x0: return code
+       // x1: vcpu
+       // x2-x29,lr: vcpu regs
+       // vcpu x0-x1 on the stack
 
-       add     x2, x0, #VCPU_CONTEXT
+       add     x1, x1, #VCPU_CONTEXT
 
-       stp     x4, x5,   [x2, #CPU_XREG_OFFSET(4)]
-       stp     x6, x7,   [x2, #CPU_XREG_OFFSET(6)]
-       stp     x8, x9,   [x2, #CPU_XREG_OFFSET(8)]
-       stp     x10, x11, [x2, #CPU_XREG_OFFSET(10)]
-       stp     x12, x13, [x2, #CPU_XREG_OFFSET(12)]
-       stp     x14, x15, [x2, #CPU_XREG_OFFSET(14)]
-       stp     x16, x17, [x2, #CPU_XREG_OFFSET(16)]
-       str     x18,      [x2, #CPU_XREG_OFFSET(18)]
+       ALTERNATIVE(nop, SET_PSTATE_PAN(1), ARM64_HAS_PAN, CONFIG_ARM64_PAN)
 
-       ldp     x6, x7, [sp], #16       // x2, x3
-       ldp     x4, x5, [sp], #16       // x0, x1
+       // Store the guest regs x2 and x3
+       stp     x2, x3,   [x1, #CPU_XREG_OFFSET(2)]
 
-       stp     x4, x5, [x2, #CPU_XREG_OFFSET(0)]
-       stp     x6, x7, [x2, #CPU_XREG_OFFSET(2)]
+       // Retrieve the guest regs x0-x1 from the stack
+       ldp     x2, x3, [sp], #16       // x0, x1
+
+       // Store the guest regs x0-x1 and x4-x18
+       stp     x2, x3,   [x1, #CPU_XREG_OFFSET(0)]
+       stp     x4, x5,   [x1, #CPU_XREG_OFFSET(4)]
+       stp     x6, x7,   [x1, #CPU_XREG_OFFSET(6)]
+       stp     x8, x9,   [x1, #CPU_XREG_OFFSET(8)]
+       stp     x10, x11, [x1, #CPU_XREG_OFFSET(10)]
+       stp     x12, x13, [x1, #CPU_XREG_OFFSET(12)]
+       stp     x14, x15, [x1, #CPU_XREG_OFFSET(14)]
+       stp     x16, x17, [x1, #CPU_XREG_OFFSET(16)]
+       str     x18,      [x1, #CPU_XREG_OFFSET(18)]
+
+       // Store the guest regs x19-x29, lr
+       save_callee_saved_regs x1
 
-       save_callee_saved_regs x2
+       // Restore the host_ctxt from the stack
+       ldr     x2, [sp], #16
 
-       // Restore vcpu & host_ctxt from the stack
-       // (preserving return code in x1)
-       ldp     x0, x2, [sp], #16
        // Now restore the host regs
        restore_callee_saved_regs x2
 
-       mov     x0, x1
-       ret
+       // If we have a pending asynchronous abort, now is the
+       // time to find out. From your VAXorcist book, page 666:
+       // "Threaten me not, oh Evil one!  For I speak with
+       // the power of DEC, and I command thee to show thyself!"
+       mrs     x2, elr_el2
+       mrs     x3, esr_el2
+       mrs     x4, spsr_el2
+       mov     x5, x0
+
+       dsb     sy              // Synchronize against in-flight ld/st
+       msr     daifclr, #4     // Unmask aborts
+
+       // This is our single instruction exception window. A pending
+       // SError is guaranteed to occur at the earliest when we unmask
+       // it, and at the latest just after the ISB.
+       .global abort_guest_exit_start
+abort_guest_exit_start:
+
+       isb
+
+       .global abort_guest_exit_end
+abort_guest_exit_end:
+
+       // If the exception took place, restore the EL1 exception
+       // context so that we can report some information.
+       // Merge the exception code with the SError pending bit.
+       tbz     x0, #ARM_EXIT_WITH_SERROR_BIT, 1f
+       msr     elr_el2, x2
+       msr     esr_el2, x3
+       msr     spsr_el2, x4
+       orr     x0, x0, x5
+1:     ret
 ENDPROC(__guest_exit)
 
 ENTRY(__fpsimd_guest_restore)
+       stp     x2, x3, [sp, #-16]!
        stp     x4, lr, [sp, #-16]!
 
 alternative_if_not ARM64_HAS_VIRT_HOST_EXTN
index f6d9694ae3b13f32771f2661a4f905b8983cfba4..4e92399f71054347e064cab9f9610dc64bf4f86f 100644 (file)
        .text
        .pushsection    .hyp.text, "ax"
 
-.macro save_x0_to_x3
-       stp     x0, x1, [sp, #-16]!
-       stp     x2, x3, [sp, #-16]!
-.endm
-
-.macro restore_x0_to_x3
-       ldp     x2, x3, [sp], #16
-       ldp     x0, x1, [sp], #16
-.endm
-
 .macro do_el2_call
        /*
         * Shuffle the parameters before calling the function
@@ -79,23 +69,23 @@ ENTRY(__kvm_hyp_teardown)
 ENDPROC(__kvm_hyp_teardown)
        
 el1_sync:                              // Guest trapped into EL2
-       save_x0_to_x3
+       stp     x0, x1, [sp, #-16]!
 
 alternative_if_not ARM64_HAS_VIRT_HOST_EXTN
        mrs     x1, esr_el2
 alternative_else
        mrs     x1, esr_el1
 alternative_endif
-       lsr     x2, x1, #ESR_ELx_EC_SHIFT
+       lsr     x0, x1, #ESR_ELx_EC_SHIFT
 
-       cmp     x2, #ESR_ELx_EC_HVC64
+       cmp     x0, #ESR_ELx_EC_HVC64
        b.ne    el1_trap
 
-       mrs     x3, vttbr_el2           // If vttbr is valid, the 64bit guest
-       cbnz    x3, el1_trap            // called HVC
+       mrs     x1, vttbr_el2           // If vttbr is valid, the 64bit guest
+       cbnz    x1, el1_trap            // called HVC
 
        /* Here, we're pretty sure the host called HVC. */
-       restore_x0_to_x3
+       ldp     x0, x1, [sp], #16
 
        cmp     x0, #HVC_GET_VECTORS
        b.ne    1f
@@ -113,24 +103,51 @@ alternative_endif
 
 el1_trap:
        /*
-        * x1: ESR
-        * x2: ESR_EC
+        * x0: ESR_EC
         */
 
        /* Guest accessed VFP/SIMD registers, save host, restore Guest */
-       cmp     x2, #ESR_ELx_EC_FP_ASIMD
+       cmp     x0, #ESR_ELx_EC_FP_ASIMD
        b.eq    __fpsimd_guest_restore
 
-       mrs     x0, tpidr_el2
-       mov     x1, #ARM_EXCEPTION_TRAP
+       mrs     x1, tpidr_el2
+       mov     x0, #ARM_EXCEPTION_TRAP
        b       __guest_exit
 
 el1_irq:
-       save_x0_to_x3
-       mrs     x0, tpidr_el2
-       mov     x1, #ARM_EXCEPTION_IRQ
+       stp     x0, x1, [sp, #-16]!
+       mrs     x1, tpidr_el2
+       mov     x0, #ARM_EXCEPTION_IRQ
+       b       __guest_exit
+
+el1_error:
+       stp     x0, x1, [sp, #-16]!
+       mrs     x1, tpidr_el2
+       mov     x0, #ARM_EXCEPTION_EL1_SERROR
        b       __guest_exit
 
+el2_error:
+       /*
+        * Only two possibilities:
+        * 1) Either we come from the exit path, having just unmasked
+        *    PSTATE.A: change the return code to an EL2 fault, and
+        *    carry on, as we're already in a sane state to handle it.
+        * 2) Or we come from anywhere else, and that's a bug: we panic.
+        *
+        * For (1), x0 contains the original return code and x1 doesn't
+        * contain anything meaningful at that stage. We can reuse them
+        * as temp registers.
+        * For (2), who cares?
+        */
+       mrs     x0, elr_el2
+       adr     x1, abort_guest_exit_start
+       cmp     x0, x1
+       adr     x1, abort_guest_exit_end
+       ccmp    x0, x1, #4, ne
+       b.ne    __hyp_panic
+       mov     x0, #(1 << ARM_EXIT_WITH_SERROR_BIT)
+       eret
+
 ENTRY(__hyp_do_panic)
        mov     lr, #(PSR_F_BIT | PSR_I_BIT | PSR_A_BIT | PSR_D_BIT |\
                      PSR_MODE_EL1h)
@@ -155,11 +172,9 @@ ENDPROC(\label)
        invalid_vector  el2h_sync_invalid
        invalid_vector  el2h_irq_invalid
        invalid_vector  el2h_fiq_invalid
-       invalid_vector  el2h_error_invalid
        invalid_vector  el1_sync_invalid
        invalid_vector  el1_irq_invalid
        invalid_vector  el1_fiq_invalid
-       invalid_vector  el1_error_invalid
 
        .ltorg
 
@@ -174,15 +189,15 @@ ENTRY(__kvm_hyp_vector)
        ventry  el2h_sync_invalid               // Synchronous EL2h
        ventry  el2h_irq_invalid                // IRQ EL2h
        ventry  el2h_fiq_invalid                // FIQ EL2h
-       ventry  el2h_error_invalid              // Error EL2h
+       ventry  el2_error                       // Error EL2h
 
        ventry  el1_sync                        // Synchronous 64-bit EL1
        ventry  el1_irq                         // IRQ 64-bit EL1
        ventry  el1_fiq_invalid                 // FIQ 64-bit EL1
-       ventry  el1_error_invalid               // Error 64-bit EL1
+       ventry  el1_error                       // Error 64-bit EL1
 
        ventry  el1_sync                        // Synchronous 32-bit EL1
        ventry  el1_irq                         // IRQ 32-bit EL1
        ventry  el1_fiq_invalid                 // FIQ 32-bit EL1
-       ventry  el1_error_invalid               // Error 32-bit EL1
+       ventry  el1_error                       // Error 32-bit EL1
 ENDPROC(__kvm_hyp_vector)
index 5a84b45626032ea1260eea2536da42231dcbfa4f..731519cfee8e840dc3bbb16234655907b58c2573 100644 (file)
@@ -17,6 +17,7 @@
 
 #include <linux/types.h>
 #include <asm/kvm_asm.h>
+#include <asm/kvm_emulate.h>
 #include <asm/kvm_hyp.h>
 
 static bool __hyp_text __fpsimd_enabled_nvhe(void)
@@ -109,6 +110,15 @@ static hyp_alternate_select(__deactivate_traps_arch,
 
 static void __hyp_text __deactivate_traps(struct kvm_vcpu *vcpu)
 {
+       /*
+        * If we pended a virtual abort, preserve it until it gets
+        * cleared. See D1.14.3 (Virtual Interrupts) for details, but
+        * the crucial bit is "On taking a vSError interrupt,
+        * HCR_EL2.VSE is cleared to 0."
+        */
+       if (vcpu->arch.hcr_el2 & HCR_VSE)
+               vcpu->arch.hcr_el2 = read_sysreg(hcr_el2);
+
        __deactivate_traps_arch()();
        write_sysreg(0, hstr_el2);
        write_sysreg(read_sysreg(mdcr_el2) & MDCR_EL2_HPMN_MASK, mdcr_el2);
@@ -232,7 +242,22 @@ static bool __hyp_text __populate_fault_info(struct kvm_vcpu *vcpu)
        return true;
 }
 
-static int __hyp_text __guest_run(struct kvm_vcpu *vcpu)
+static void __hyp_text __skip_instr(struct kvm_vcpu *vcpu)
+{
+       *vcpu_pc(vcpu) = read_sysreg_el2(elr);
+
+       if (vcpu_mode_is_32bit(vcpu)) {
+               vcpu->arch.ctxt.gp_regs.regs.pstate = read_sysreg_el2(spsr);
+               kvm_skip_instr32(vcpu, kvm_vcpu_trap_il_is32bit(vcpu));
+               write_sysreg_el2(vcpu->arch.ctxt.gp_regs.regs.pstate, spsr);
+       } else {
+               *vcpu_pc(vcpu) += 4;
+       }
+
+       write_sysreg_el2(*vcpu_pc(vcpu), elr);
+}
+
+int __hyp_text __kvm_vcpu_run(struct kvm_vcpu *vcpu)
 {
        struct kvm_cpu_context *host_ctxt;
        struct kvm_cpu_context *guest_ctxt;
@@ -267,9 +292,43 @@ again:
        exit_code = __guest_enter(vcpu, host_ctxt);
        /* And we're baaack! */
 
+       /*
+        * We're using the raw exception code in order to only process
+        * the trap if no SError is pending. We will come back to the
+        * same PC once the SError has been injected, and replay the
+        * trapping instruction.
+        */
        if (exit_code == ARM_EXCEPTION_TRAP && !__populate_fault_info(vcpu))
                goto again;
 
+       if (static_branch_unlikely(&vgic_v2_cpuif_trap) &&
+           exit_code == ARM_EXCEPTION_TRAP) {
+               bool valid;
+
+               valid = kvm_vcpu_trap_get_class(vcpu) == ESR_ELx_EC_DABT_LOW &&
+                       kvm_vcpu_trap_get_fault_type(vcpu) == FSC_FAULT &&
+                       kvm_vcpu_dabt_isvalid(vcpu) &&
+                       !kvm_vcpu_dabt_isextabt(vcpu) &&
+                       !kvm_vcpu_dabt_iss1tw(vcpu);
+
+               if (valid) {
+                       int ret = __vgic_v2_perform_cpuif_access(vcpu);
+
+                       if (ret == 1) {
+                               __skip_instr(vcpu);
+                               goto again;
+                       }
+
+                       if (ret == -1) {
+                               /* Promote an illegal access to an SError */
+                               __skip_instr(vcpu);
+                               exit_code = ARM_EXCEPTION_EL1_SERROR;
+                       }
+
+                       /* 0 falls through to be handler out of EL2 */
+               }
+       }
+
        fp_enabled = __fpsimd_enabled();
 
        __sysreg_save_guest_state(guest_ctxt);
@@ -293,8 +352,6 @@ again:
        return exit_code;
 }
 
-__alias(__guest_run) int __kvm_vcpu_run(struct kvm_vcpu *vcpu);
-
 static const char __hyp_panic_string[] = "HYP panic:\nPS:%08llx PC:%016llx ESR:%08llx\nFAR:%016llx HPFAR:%016llx PAR:%016llx\nVCPU:%p\n";
 
 static void __hyp_text __hyp_call_panic_nvhe(u64 spsr, u64 elr, u64 par)
index be8177cdd3bf8ef3d2d03a580e494080c88f4029..9cc0ea784ae60a0a5086f1c5f6d129cd7d360f13 100644 (file)
@@ -17,7 +17,7 @@
 
 #include <asm/kvm_hyp.h>
 
-static void __hyp_text __tlb_flush_vmid_ipa(struct kvm *kvm, phys_addr_t ipa)
+void __hyp_text __kvm_tlb_flush_vmid_ipa(struct kvm *kvm, phys_addr_t ipa)
 {
        dsb(ishst);
 
@@ -48,10 +48,7 @@ static void __hyp_text __tlb_flush_vmid_ipa(struct kvm *kvm, phys_addr_t ipa)
        write_sysreg(0, vttbr_el2);
 }
 
-__alias(__tlb_flush_vmid_ipa) void __kvm_tlb_flush_vmid_ipa(struct kvm *kvm,
-                                                           phys_addr_t ipa);
-
-static void __hyp_text __tlb_flush_vmid(struct kvm *kvm)
+void __hyp_text __kvm_tlb_flush_vmid(struct kvm *kvm)
 {
        dsb(ishst);
 
@@ -67,14 +64,10 @@ static void __hyp_text __tlb_flush_vmid(struct kvm *kvm)
        write_sysreg(0, vttbr_el2);
 }
 
-__alias(__tlb_flush_vmid) void __kvm_tlb_flush_vmid(struct kvm *kvm);
-
-static void __hyp_text __tlb_flush_vm_context(void)
+void __hyp_text __kvm_flush_vm_context(void)
 {
        dsb(ishst);
        asm volatile("tlbi alle1is      \n"
                     "ic ialluis          ": : );
        dsb(ish);
 }
-
-__alias(__tlb_flush_vm_context) void __kvm_flush_vm_context(void);
index 5f8f80b4a2240614baf9d10fee0b21ae2d5d5cc1..ee1ea6303554421287e7225e498de617553913aa 100644 (file)
@@ -335,9 +335,7 @@ void __hyp_text __vgic_v3_init_lrs(void)
                __gic_v3_set_lr(0, i);
 }
 
-static u64 __hyp_text __vgic_v3_read_ich_vtr_el2(void)
+u64 __hyp_text __vgic_v3_get_ich_vtr_el2(void)
 {
        return read_gicreg(ICH_VTR_EL2);
 }
-
-__alias(__vgic_v3_read_ich_vtr_el2) u64 __vgic_v3_get_ich_vtr_el2(void);
index 898c0e6aedd45e8e4a7e2e2cb89d04fa5e8c2f1c..da6a8cfa54a08f11874863bc643f84e103027768 100644 (file)
@@ -231,3 +231,15 @@ void kvm_inject_undefined(struct kvm_vcpu *vcpu)
        else
                inject_undef64(vcpu);
 }
+
+/**
+ * kvm_inject_vabt - inject an async abort / SError into the guest
+ * @vcpu: The VCPU to receive the exception
+ *
+ * It is assumed that this code is called from the VCPU thread and that the
+ * VCPU therefore is not currently executing guest code.
+ */
+void kvm_inject_vabt(struct kvm_vcpu *vcpu)
+{
+       vcpu_set_hcr(vcpu, vcpu_get_hcr(vcpu) | HCR_VSE);
+}
index 19b698ef3336ffc947b4c01b62e026c779eb5af3..bb46c03eb3c730e7ece3ab447ad6e44dbf756478 100644 (file)
@@ -20,6 +20,7 @@
 #include <linux/kvm.h>
 #include <linux/irqreturn.h>
 #include <linux/spinlock.h>
+#include <linux/static_key.h>
 #include <linux/types.h>
 #include <kvm/iodev.h>
 #include <linux/list.h>
@@ -49,6 +50,9 @@ struct vgic_global {
        /* Physical address of vgic virtual cpu interface */
        phys_addr_t             vcpu_base;
 
+       /* GICV mapping */
+       void __iomem            *vcpu_base_va;
+
        /* virtual control interface mapping */
        void __iomem            *vctrl_base;
 
@@ -265,6 +269,8 @@ struct vgic_cpu {
        bool lpis_enabled;
 };
 
+extern struct static_key_false vgic_v2_cpuif_trap;
+
 int kvm_vgic_addr(struct kvm *kvm, unsigned long type, u64 *addr, bool write);
 void kvm_vgic_early_init(struct kvm *kvm);
 int kvm_vgic_create(struct kvm *kvm, u32 type);
diff --git a/virt/kvm/arm/aarch32.c b/virt/kvm/arm/aarch32.c
new file mode 100644 (file)
index 0000000..528af4b
--- /dev/null
@@ -0,0 +1,152 @@
+/*
+ * (not much of an) Emulation layer for 32bit guests.
+ *
+ * Copyright (C) 2012,2013 - ARM Ltd
+ * Author: Marc Zyngier <marc.zyngier@arm.com>
+ *
+ * based on arch/arm/kvm/emulate.c
+ * Copyright (C) 2012 - Virtual Open Systems and Columbia University
+ * Author: Christoffer Dall <c.dall@virtualopensystems.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/kvm_host.h>
+#include <asm/kvm_emulate.h>
+#include <asm/kvm_hyp.h>
+
+#ifndef CONFIG_ARM64
+#define COMPAT_PSR_T_BIT       PSR_T_BIT
+#define COMPAT_PSR_IT_MASK     PSR_IT_MASK
+#endif
+
+/*
+ * stolen from arch/arm/kernel/opcodes.c
+ *
+ * condition code lookup table
+ * index into the table is test code: EQ, NE, ... LT, GT, AL, NV
+ *
+ * bit position in short is condition code: NZCV
+ */
+static const unsigned short cc_map[16] = {
+       0xF0F0,                 /* EQ == Z set            */
+       0x0F0F,                 /* NE                     */
+       0xCCCC,                 /* CS == C set            */
+       0x3333,                 /* CC                     */
+       0xFF00,                 /* MI == N set            */
+       0x00FF,                 /* PL                     */
+       0xAAAA,                 /* VS == V set            */
+       0x5555,                 /* VC                     */
+       0x0C0C,                 /* HI == C set && Z clear */
+       0xF3F3,                 /* LS == C clear || Z set */
+       0xAA55,                 /* GE == (N==V)           */
+       0x55AA,                 /* LT == (N!=V)           */
+       0x0A05,                 /* GT == (!Z && (N==V))   */
+       0xF5FA,                 /* LE == (Z || (N!=V))    */
+       0xFFFF,                 /* AL always              */
+       0                       /* NV                     */
+};
+
+/*
+ * Check if a trapped instruction should have been executed or not.
+ */
+bool kvm_condition_valid32(const struct kvm_vcpu *vcpu)
+{
+       unsigned long cpsr;
+       u32 cpsr_cond;
+       int cond;
+
+       /* Top two bits non-zero?  Unconditional. */
+       if (kvm_vcpu_get_hsr(vcpu) >> 30)
+               return true;
+
+       /* Is condition field valid? */
+       cond = kvm_vcpu_get_condition(vcpu);
+       if (cond == 0xE)
+               return true;
+
+       cpsr = *vcpu_cpsr(vcpu);
+
+       if (cond < 0) {
+               /* This can happen in Thumb mode: examine IT state. */
+               unsigned long it;
+
+               it = ((cpsr >> 8) & 0xFC) | ((cpsr >> 25) & 0x3);
+
+               /* it == 0 => unconditional. */
+               if (it == 0)
+                       return true;
+
+               /* The cond for this insn works out as the top 4 bits. */
+               cond = (it >> 4);
+       }
+
+       cpsr_cond = cpsr >> 28;
+
+       if (!((cc_map[cond] >> cpsr_cond) & 1))
+               return false;
+
+       return true;
+}
+
+/**
+ * adjust_itstate - adjust ITSTATE when emulating instructions in IT-block
+ * @vcpu:      The VCPU pointer
+ *
+ * When exceptions occur while instructions are executed in Thumb IF-THEN
+ * blocks, the ITSTATE field of the CPSR is not advanced (updated), so we have
+ * to do this little bit of work manually. The fields map like this:
+ *
+ * IT[7:0] -> CPSR[26:25],CPSR[15:10]
+ */
+static void __hyp_text kvm_adjust_itstate(struct kvm_vcpu *vcpu)
+{
+       unsigned long itbits, cond;
+       unsigned long cpsr = *vcpu_cpsr(vcpu);
+       bool is_arm = !(cpsr & COMPAT_PSR_T_BIT);
+
+       if (is_arm || !(cpsr & COMPAT_PSR_IT_MASK))
+               return;
+
+       cond = (cpsr & 0xe000) >> 13;
+       itbits = (cpsr & 0x1c00) >> (10 - 2);
+       itbits |= (cpsr & (0x3 << 25)) >> 25;
+
+       /* Perform ITAdvance (see page A2-52 in ARM DDI 0406C) */
+       if ((itbits & 0x7) == 0)
+               itbits = cond = 0;
+       else
+               itbits = (itbits << 1) & 0x1f;
+
+       cpsr &= ~COMPAT_PSR_IT_MASK;
+       cpsr |= cond << 13;
+       cpsr |= (itbits & 0x1c) << (10 - 2);
+       cpsr |= (itbits & 0x3) << 25;
+       *vcpu_cpsr(vcpu) = cpsr;
+}
+
+/**
+ * kvm_skip_instr - skip a trapped instruction and proceed to the next
+ * @vcpu: The vcpu pointer
+ */
+void __hyp_text kvm_skip_instr32(struct kvm_vcpu *vcpu, bool is_wide_instr)
+{
+       bool is_thumb;
+
+       is_thumb = !!(*vcpu_cpsr(vcpu) & COMPAT_PSR_T_BIT);
+       if (is_thumb && !is_wide_instr)
+               *vcpu_pc(vcpu) += 2;
+       else
+               *vcpu_pc(vcpu) += 4;
+       kvm_adjust_itstate(vcpu);
+}
index 4309b60ebf171606467e8b4615c8830bf76d6a52..27a1f6341d4111d923501ab1033750d53c4eaed7 100644 (file)
@@ -445,7 +445,7 @@ int kvm_timer_hyp_init(void)
        if (err) {
                kvm_err("kvm_arch_timer: can't request interrupt %d (%d)\n",
                        host_vtimer_irq, err);
-               goto out;
+               return err;
        }
 
        kvm_info("virtual timer IRQ%d\n", host_vtimer_irq);
@@ -453,10 +453,6 @@ int kvm_timer_hyp_init(void)
        cpuhp_setup_state(CPUHP_AP_KVM_ARM_TIMER_STARTING,
                          "AP_KVM_ARM_TIMER_STARTING", kvm_timer_starting_cpu,
                          kvm_timer_dying_cpu);
-       goto out;
-out_free:
-       free_percpu_irq(host_vtimer_irq, kvm_get_running_vcpus());
-out:
        return err;
 }
 
index 7cffd9338c494b5959426457c7ac2bd4aa8ccb36..c8aeb7b91ec89bdf023151fc3ba5456be67f4d53 100644 (file)
@@ -19,6 +19,7 @@
 #include <linux/irqchip/arm-gic.h>
 #include <linux/kvm_host.h>
 
+#include <asm/kvm_emulate.h>
 #include <asm/kvm_hyp.h>
 
 static void __hyp_text save_maint_int_state(struct kvm_vcpu *vcpu,
@@ -167,3 +168,59 @@ void __hyp_text __vgic_v2_restore_state(struct kvm_vcpu *vcpu)
        writel_relaxed(cpu_if->vgic_vmcr, base + GICH_VMCR);
        vcpu->arch.vgic_cpu.live_lrs = live_lrs;
 }
+
+#ifdef CONFIG_ARM64
+/*
+ * __vgic_v2_perform_cpuif_access -- perform a GICV access on behalf of the
+ *                                  guest.
+ *
+ * @vcpu: the offending vcpu
+ *
+ * Returns:
+ *  1: GICV access successfully performed
+ *  0: Not a GICV access
+ * -1: Illegal GICV access
+ */
+int __hyp_text __vgic_v2_perform_cpuif_access(struct kvm_vcpu *vcpu)
+{
+       struct kvm *kvm = kern_hyp_va(vcpu->kvm);
+       struct vgic_dist *vgic = &kvm->arch.vgic;
+       phys_addr_t fault_ipa;
+       void __iomem *addr;
+       int rd;
+
+       /* Build the full address */
+       fault_ipa  = kvm_vcpu_get_fault_ipa(vcpu);
+       fault_ipa |= kvm_vcpu_get_hfar(vcpu) & GENMASK(11, 0);
+
+       /* If not for GICV, move on */
+       if (fault_ipa <  vgic->vgic_cpu_base ||
+           fault_ipa >= (vgic->vgic_cpu_base + KVM_VGIC_V2_CPU_SIZE))
+               return 0;
+
+       /* Reject anything but a 32bit access */
+       if (kvm_vcpu_dabt_get_as(vcpu) != sizeof(u32))
+               return -1;
+
+       /* Not aligned? Don't bother */
+       if (fault_ipa & 3)
+               return -1;
+
+       rd = kvm_vcpu_dabt_get_rd(vcpu);
+       addr  = kern_hyp_va((kern_hyp_va(&kvm_vgic_global_state))->vcpu_base_va);
+       addr += fault_ipa - vgic->vgic_cpu_base;
+
+       if (kvm_vcpu_dabt_iswrite(vcpu)) {
+               u32 data = vcpu_data_guest_to_host(vcpu,
+                                                  vcpu_get_reg(vcpu, rd),
+                                                  sizeof(u32));
+               writel_relaxed(data, addr);
+       } else {
+               u32 data = readl_relaxed(addr);
+               vcpu_set_reg(vcpu, rd, vcpu_data_host_to_guest(vcpu, data,
+                                                              sizeof(u32)));
+       }
+
+       return 1;
+}
+#endif
index b31a51a14efbe9e26abda69c6998b316a10aaf39..d918dcf26a5ab5790b16088d5d87a4107a86baac 100644 (file)
@@ -46,15 +46,9 @@ static int vgic_irqfd_set_irq(struct kvm_kernel_irq_routing_entry *e,
  * @ue: user api routing entry handle
  * return 0 on success, -EINVAL on errors.
  */
-#ifdef KVM_CAP_X2APIC_API
 int kvm_set_routing_entry(struct kvm *kvm,
                          struct kvm_kernel_irq_routing_entry *e,
                          const struct kvm_irq_routing_entry *ue)
-#else
-/* Remove this version and the ifdefery once merged into 4.8 */
-int kvm_set_routing_entry(struct kvm_kernel_irq_routing_entry *e,
-                         const struct kvm_irq_routing_entry *ue)
-#endif
 {
        int r = -EINVAL;
 
index 1813f93b5cde0ac95edbe1719abc827cfc47cd4a..163b0578efc76e111983c5b74f302d453334dc65 100644 (file)
@@ -233,38 +233,50 @@ int kvm_register_vgic_device(unsigned long type)
        return ret;
 }
 
-/** vgic_attr_regs_access: allows user space to read/write VGIC registers
- *
- * @dev: kvm device handle
- * @attr: kvm device attribute
- * @reg: address the value is read or written
- * @is_write: write flag
- *
- */
-static int vgic_attr_regs_access(struct kvm_device *dev,
-                                struct kvm_device_attr *attr,
-                                u32 *reg, bool is_write)
-{
+struct vgic_reg_attr {
+       struct kvm_vcpu *vcpu;
        gpa_t addr;
-       int cpuid, ret, c;
-       struct kvm_vcpu *vcpu, *tmp_vcpu;
-       int vcpu_lock_idx = -1;
+};
+
+static int parse_vgic_v2_attr(struct kvm_device *dev,
+                             struct kvm_device_attr *attr,
+                             struct vgic_reg_attr *reg_attr)
+{
+       int cpuid;
 
        cpuid = (attr->attr & KVM_DEV_ARM_VGIC_CPUID_MASK) >>
                 KVM_DEV_ARM_VGIC_CPUID_SHIFT;
-       vcpu = kvm_get_vcpu(dev->kvm, cpuid);
-       addr = attr->attr & KVM_DEV_ARM_VGIC_OFFSET_MASK;
 
-       mutex_lock(&dev->kvm->lock);
+       if (cpuid >= atomic_read(&dev->kvm->online_vcpus))
+               return -EINVAL;
 
-       ret = vgic_init(dev->kvm);
-       if (ret)
-               goto out;
+       reg_attr->vcpu = kvm_get_vcpu(dev->kvm, cpuid);
+       reg_attr->addr = attr->attr & KVM_DEV_ARM_VGIC_OFFSET_MASK;
 
-       if (cpuid >= atomic_read(&dev->kvm->online_vcpus)) {
-               ret = -EINVAL;
-               goto out;
+       return 0;
+}
+
+/* unlocks vcpus from @vcpu_lock_idx and smaller */
+static void unlock_vcpus(struct kvm *kvm, int vcpu_lock_idx)
+{
+       struct kvm_vcpu *tmp_vcpu;
+
+       for (; vcpu_lock_idx >= 0; vcpu_lock_idx--) {
+               tmp_vcpu = kvm_get_vcpu(kvm, vcpu_lock_idx);
+               mutex_unlock(&tmp_vcpu->mutex);
        }
+}
+
+static void unlock_all_vcpus(struct kvm *kvm)
+{
+       unlock_vcpus(kvm, atomic_read(&kvm->online_vcpus) - 1);
+}
+
+/* Returns true if all vcpus were locked, false otherwise */
+static bool lock_all_vcpus(struct kvm *kvm)
+{
+       struct kvm_vcpu *tmp_vcpu;
+       int c;
 
        /*
         * Any time a vcpu is run, vcpu_load is called which tries to grab the
@@ -272,11 +284,49 @@ static int vgic_attr_regs_access(struct kvm_device *dev,
         * that no other VCPUs are run and fiddle with the vgic state while we
         * access it.
         */
-       ret = -EBUSY;
-       kvm_for_each_vcpu(c, tmp_vcpu, dev->kvm) {
-               if (!mutex_trylock(&tmp_vcpu->mutex))
-                       goto out;
-               vcpu_lock_idx = c;
+       kvm_for_each_vcpu(c, tmp_vcpu, kvm) {
+               if (!mutex_trylock(&tmp_vcpu->mutex)) {
+                       unlock_vcpus(kvm, c - 1);
+                       return false;
+               }
+       }
+
+       return true;
+}
+
+/**
+ * vgic_attr_regs_access_v2 - allows user space to access VGIC v2 state
+ *
+ * @dev:      kvm device handle
+ * @attr:     kvm device attribute
+ * @reg:      address the value is read or written
+ * @is_write: true if userspace is writing a register
+ */
+static int vgic_attr_regs_access_v2(struct kvm_device *dev,
+                                   struct kvm_device_attr *attr,
+                                   u32 *reg, bool is_write)
+{
+       struct vgic_reg_attr reg_attr;
+       gpa_t addr;
+       struct kvm_vcpu *vcpu;
+       int ret;
+
+       ret = parse_vgic_v2_attr(dev, attr, &reg_attr);
+       if (ret)
+               return ret;
+
+       vcpu = reg_attr.vcpu;
+       addr = reg_attr.addr;
+
+       mutex_lock(&dev->kvm->lock);
+
+       ret = vgic_init(dev->kvm);
+       if (ret)
+               goto out;
+
+       if (!lock_all_vcpus(dev->kvm)) {
+               ret = -EBUSY;
+               goto out;
        }
 
        switch (attr->group) {
@@ -291,18 +341,12 @@ static int vgic_attr_regs_access(struct kvm_device *dev,
                break;
        }
 
+       unlock_all_vcpus(dev->kvm);
 out:
-       for (; vcpu_lock_idx >= 0; vcpu_lock_idx--) {
-               tmp_vcpu = kvm_get_vcpu(dev->kvm, vcpu_lock_idx);
-               mutex_unlock(&tmp_vcpu->mutex);
-       }
-
        mutex_unlock(&dev->kvm->lock);
        return ret;
 }
 
-/* V2 ops */
-
 static int vgic_v2_set_attr(struct kvm_device *dev,
                            struct kvm_device_attr *attr)
 {
@@ -321,7 +365,7 @@ static int vgic_v2_set_attr(struct kvm_device *dev,
                if (get_user(reg, uaddr))
                        return -EFAULT;
 
-               return vgic_attr_regs_access(dev, attr, &reg, true);
+               return vgic_attr_regs_access_v2(dev, attr, &reg, true);
        }
        }
 
@@ -343,7 +387,7 @@ static int vgic_v2_get_attr(struct kvm_device *dev,
                u32 __user *uaddr = (u32 __user *)(long)attr->addr;
                u32 reg = 0;
 
-               ret = vgic_attr_regs_access(dev, attr, &reg, false);
+               ret = vgic_attr_regs_access_v2(dev, attr, &reg, false);
                if (ret)
                        return ret;
                return put_user(reg, uaddr);
@@ -387,8 +431,6 @@ struct kvm_device_ops kvm_arm_vgic_v2_ops = {
        .has_attr = vgic_v2_has_attr,
 };
 
-/* V3 ops */
-
 #ifdef CONFIG_KVM_ARM_VGIC_V3
 
 static int vgic_v3_set_attr(struct kvm_device *dev,
index 0bf6709d1006c4bc5d148d4a3164d31b0ac96ecd..0a063af4056546c886d6d7efba987ff5528fcce0 100644 (file)
@@ -278,12 +278,14 @@ int vgic_v2_map_resources(struct kvm *kvm)
                goto out;
        }
 
-       ret = kvm_phys_addr_ioremap(kvm, dist->vgic_cpu_base,
-                                   kvm_vgic_global_state.vcpu_base,
-                                   KVM_VGIC_V2_CPU_SIZE, true);
-       if (ret) {
-               kvm_err("Unable to remap VGIC CPU to VCPU\n");
-               goto out;
+       if (!static_branch_unlikely(&vgic_v2_cpuif_trap)) {
+               ret = kvm_phys_addr_ioremap(kvm, dist->vgic_cpu_base,
+                                           kvm_vgic_global_state.vcpu_base,
+                                           KVM_VGIC_V2_CPU_SIZE, true);
+               if (ret) {
+                       kvm_err("Unable to remap VGIC CPU to VCPU\n");
+                       goto out;
+               }
        }
 
        dist->ready = true;
@@ -294,6 +296,8 @@ out:
        return ret;
 }
 
+DEFINE_STATIC_KEY_FALSE(vgic_v2_cpuif_trap);
+
 /**
  * vgic_v2_probe - probe for a GICv2 compatible interrupt controller in DT
  * @node:      pointer to the DT node
@@ -310,45 +314,51 @@ int vgic_v2_probe(const struct gic_kvm_info *info)
                return -ENXIO;
        }
 
-       if (!PAGE_ALIGNED(info->vcpu.start)) {
-               kvm_err("GICV physical address 0x%llx not page aligned\n",
-                       (unsigned long long)info->vcpu.start);
-               return -ENXIO;
-       }
+       if (!PAGE_ALIGNED(info->vcpu.start) ||
+           !PAGE_ALIGNED(resource_size(&info->vcpu))) {
+               kvm_info("GICV region size/alignment is unsafe, using trapping (reduced performance)\n");
+               kvm_vgic_global_state.vcpu_base_va = ioremap(info->vcpu.start,
+                                                            resource_size(&info->vcpu));
+               if (!kvm_vgic_global_state.vcpu_base_va) {
+                       kvm_err("Cannot ioremap GICV\n");
+                       return -ENOMEM;
+               }
 
-       if (!PAGE_ALIGNED(resource_size(&info->vcpu))) {
-               kvm_err("GICV size 0x%llx not a multiple of page size 0x%lx\n",
-                       (unsigned long long)resource_size(&info->vcpu),
-                       PAGE_SIZE);
-               return -ENXIO;
+               ret = create_hyp_io_mappings(kvm_vgic_global_state.vcpu_base_va,
+                                            kvm_vgic_global_state.vcpu_base_va + resource_size(&info->vcpu),
+                                            info->vcpu.start);
+               if (ret) {
+                       kvm_err("Cannot map GICV into hyp\n");
+                       goto out;
+               }
+
+               static_branch_enable(&vgic_v2_cpuif_trap);
        }
 
        kvm_vgic_global_state.vctrl_base = ioremap(info->vctrl.start,
                                                   resource_size(&info->vctrl));
        if (!kvm_vgic_global_state.vctrl_base) {
                kvm_err("Cannot ioremap GICH\n");
-               return -ENOMEM;
+               ret = -ENOMEM;
+               goto out;
        }
 
        vtr = readl_relaxed(kvm_vgic_global_state.vctrl_base + GICH_VTR);
        kvm_vgic_global_state.nr_lr = (vtr & 0x3f) + 1;
 
-       ret = kvm_register_vgic_device(KVM_DEV_TYPE_ARM_VGIC_V2);
-       if (ret) {
-               kvm_err("Cannot register GICv2 KVM device\n");
-               iounmap(kvm_vgic_global_state.vctrl_base);
-               return ret;
-       }
-
        ret = create_hyp_io_mappings(kvm_vgic_global_state.vctrl_base,
                                     kvm_vgic_global_state.vctrl_base +
                                         resource_size(&info->vctrl),
                                     info->vctrl.start);
        if (ret) {
                kvm_err("Cannot map VCTRL into hyp\n");
-               kvm_unregister_device_ops(KVM_DEV_TYPE_ARM_VGIC_V2);
-               iounmap(kvm_vgic_global_state.vctrl_base);
-               return ret;
+               goto out;
+       }
+
+       ret = kvm_register_vgic_device(KVM_DEV_TYPE_ARM_VGIC_V2);
+       if (ret) {
+               kvm_err("Cannot register GICv2 KVM device\n");
+               goto out;
        }
 
        kvm_vgic_global_state.can_emulate_gicv2 = true;
@@ -359,4 +369,11 @@ int vgic_v2_probe(const struct gic_kvm_info *info)
        kvm_info("vgic-v2@%llx\n", info->vctrl.start);
 
        return 0;
+out:
+       if (kvm_vgic_global_state.vctrl_base)
+               iounmap(kvm_vgic_global_state.vctrl_base);
+       if (kvm_vgic_global_state.vcpu_base_va)
+               iounmap(kvm_vgic_global_state.vcpu_base_va);
+
+       return ret;
 }
This page took 0.057788 seconds and 5 git commands to generate.