KVM: arm64: Add a helper providing an inlined literal value for ICH_VTR_EL2

We already have two ways to deal with ICH_VTR_EL2:

- directly read the system register
- read a cached copy in the vgic global state

Add a third way, in the form of an inlined literal value that will
eventually replace all of the above. This literal value is computed
at boot time, and patched in the relevant code locations.

Signed-off-by: Marc Zyngier <maz@kernel.org>
Link: https://patch.msgid.link/20260721170754.3150521-4-maz@kernel.org
Signed-off-by: Oliver Upton <oupton@kernel.org>
This commit is contained in:
Marc Zyngier
2026-07-21 18:07:51 +01:00
committed by Oliver Upton
parent 27a263e1a0
commit 4edfdc8dde
3 changed files with 73 additions and 0 deletions

View File

@@ -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);

View File

@@ -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();

View File

@@ -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;