summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--arch/arm64/kernel/image-vars.h1
-rw-r--r--arch/arm64/kvm/vgic/vgic-v3.c55
-rw-r--r--arch/arm64/kvm/vgic/vgic.h17
3 files changed, 73 insertions, 0 deletions
diff --git a/arch/arm64/kernel/image-vars.h b/arch/arm64/kernel/image-vars.h
index d4c7d45ae6bc..459c33634ebd 100644
--- a/arch/arm64/kernel/image-vars.h
+++ b/arch/arm64/kernel/image-vars.h
@@ -92,6 +92,7 @@ KVM_NVHE_ALIAS(spectre_bhb_patch_wa3);
KVM_NVHE_ALIAS(spectre_bhb_patch_clearbhb);
KVM_NVHE_ALIAS(alt_cb_patch_nops);
KVM_NVHE_ALIAS(kvm_compute_ich_hcr_trap_bits);
+KVM_NVHE_ALIAS(kvm_patch_ich_vtr_el2);
/* Global kernel state accessed by nVHE hyp code. */
KVM_NVHE_ALIAS(kvm_vgic_global_state);
diff --git a/arch/arm64/kvm/vgic/vgic-v3.c b/arch/arm64/kvm/vgic/vgic-v3.c
index 0991a649a5d1..098187e34c58 100644
--- a/arch/arm64/kvm/vgic/vgic-v3.c
+++ b/arch/arm64/kvm/vgic/vgic-v3.c
@@ -858,6 +858,61 @@ void noinstr kvm_compute_ich_hcr_trap_bits(struct alt_instr *alt,
*updptr = cpu_to_le32(insn);
}
+void noinstr kvm_patch_ich_vtr_el2(struct alt_instr *alt,
+ __le32 *origptr, __le32 *updptr,
+ int nr_inst)
+{
+ struct arm_smccc_res res = {};
+ u32 insn, oinsn, rd, vtr;
+
+ /* No KVM? Nothing to do */
+ if (!is_hyp_mode_available())
+ return;
+
+ /* No v3, compat, nor the fruity erzatz of a GIC? Bugger off */
+ if (!cpus_have_cap(ARM64_HAS_GICV5_LEGACY) &&
+ !cpus_have_cap(ARM64_HAS_GICV3_CPUIF) &&
+ !vgic_v3_broken_seis())
+ return;
+
+ /*
+ * At the point where this is called, we are guaranteed that if
+ * we're running at EL1, then the EL2 stubs are still in place.
+ */
+ if (is_kernel_in_hyp_mode())
+ res.a1 = read_sysreg_s(SYS_ICH_VTR_EL2);
+ else
+ arm_smccc_1_1_hvc(HVC_GET_ICH_VTR_EL2, &res);
+
+ if (res.a0 == HVC_STUB_ERR)
+ return;
+
+ vtr = res.a1;
+
+ if (vgic_v3_broken_seis())
+ vtr &= ~ICH_VTR_EL2_SEIS;
+
+ /* Compute target register */
+ oinsn = le32_to_cpu(*origptr);
+ rd = aarch64_insn_decode_register(AARCH64_INSN_REGTYPE_RD, oinsn);
+
+ /* movz rd, #(vtr & 0xffff) */
+ insn = aarch64_insn_gen_movewide(rd,
+ (u16)vtr,
+ 0,
+ AARCH64_INSN_VARIANT_64BIT,
+ AARCH64_INSN_MOVEWIDE_ZERO);
+ *updptr++ = cpu_to_le32(insn);
+
+ /* movk rd, #((vtr >> 16) & 0xffff), lsl #16 */
+ insn = aarch64_insn_gen_movewide(rd,
+ (u16)(vtr >> 16),
+ 16,
+ AARCH64_INSN_VARIANT_64BIT,
+ AARCH64_INSN_MOVEWIDE_KEEP);
+ *updptr++ = cpu_to_le32(insn);
+}
+
void vgic_v3_enable_cpuif_traps(void)
{
u64 traps = vgic_ich_hcr_trap_bits();
diff --git a/arch/arm64/kvm/vgic/vgic.h b/arch/arm64/kvm/vgic/vgic.h
index f45f7e3ec4d6..b76bb6e56de3 100644
--- a/arch/arm64/kvm/vgic/vgic.h
+++ b/arch/arm64/kvm/vgic/vgic.h
@@ -71,6 +71,23 @@
ICH_VTR_EL2_IDbits)
#define KVM_ICH_VTR_EL2_RES1 ICH_VTR_EL2_nV4
+void kvm_patch_ich_vtr_el2(struct alt_instr *alt,
+ __le32 *origptr, __le32 *updptr, int nr_inst);
+
+static inline u64 vgic_ich_vtr(void)
+{
+ u64 vtr;
+
+ /* All non-RES0 bits are in the bottom 32bits */
+ asm volatile(ALTERNATIVE_CB("movz %0, #0\n"
+ "movk %0, #0, lsl #16\n",
+ ARM64_ALWAYS_SYSTEM,
+ kvm_patch_ich_vtr_el2)
+ : "=r" (vtr));
+
+ return vtr;
+}
+
static inline u64 kvm_get_guest_vtr_el2(void)
{
u64 vtr;