Commit acf2ab28 authored by Marc Zyngier's avatar Marc Zyngier
Browse files

Merge branch kvm-arm64/vgic-sre-traps into kvmarm-master/next



* kvm-arm64/vgic-sre-traps:
  : .
  : Fix the multiple of cases where KVM/arm64 doesn't correctly
  : handle the guest trying to use a GICv3 that isn't advertised.
  :
  : From the cover letter:
  :
  : "It recently appeared that, when running on a GICv3-equipped platform
  : (which is what non-ancient arm64 HW has), *not* configuring a GICv3
  : for the guest could result in less than desirable outcomes.
  :
  : We have multiple issues to fix:
  :
  : - for registers that *always* trap (the SGI registers) or that *may*
  :   trap (the SRE register), we need to check whether a GICv3 has been
  :   instantiated before acting upon the trap.
  :
  : - for registers that only conditionally trap, we must actively trap
  :   them even in the absence of a GICv3 being instantiated, and handle
  :   those traps accordingly.
  :
  : - finally, ID registers must reflect the absence of a GICv3, so that
  :   we are consistent.
  :
  : This series goes through all these requirements. The main complexity
  : here is to apply a GICv3 configuration on the host in the absence of a
  : GICv3 in the guest. This is pretty hackish, but I don't have a much
  : better solution so far.
  :
  : As part of making wider use of of the trap bits, we fully define the
  : trap routing as per the architecture, something that we eventually
  : need for NV anyway."
  : .
  KVM: arm64: selftests: Cope with lack of GICv3 in set_id_regs
  KVM: arm64: Add selftest checking how the absence of GICv3 is handled
  KVM: arm64: Unify UNDEF injection helpers
  KVM: arm64: Make most GICv3 accesses UNDEF if they trap
  KVM: arm64: Honor guest requested traps in GICv3 emulation
  KVM: arm64: Add trap routing information for ICH_HCR_EL2
  KVM: arm64: Add ICH_HCR_EL2 to the vcpu state
  KVM: arm64: Zero ID_AA64PFR0_EL1.GIC when no GICv3 is presented to the guest
  KVM: arm64: Add helper for last ditch idreg adjustments
  KVM: arm64: Force GICv3 trap activation when no irqchip is configured on VHE
  KVM: arm64: Force SRE traps when SRE access is not enabled
  KVM: arm64: Move GICv3 trap configuration to kvm_calculate_traps()

Signed-off-by: default avatarMarc Zyngier <maz@kernel.org>
parents 091258a0 4641c7ea
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -534,6 +534,8 @@ enum vcpu_sysreg {
	VNCR(CNTP_CVAL_EL0),
	VNCR(CNTP_CTL_EL0),

	VNCR(ICH_HCR_EL2),

	NR_SYS_REGS	/* Nothing after this line! */
};

+7 −7
Original line number Diff line number Diff line
@@ -46,6 +46,8 @@
#include <kvm/arm_pmu.h>
#include <kvm/arm_psci.h>

#include "sys_regs.h"

static enum kvm_mode kvm_mode = KVM_MODE_DEFAULT;

enum kvm_wfx_trap_policy {
@@ -821,15 +823,13 @@ int kvm_arch_vcpu_run_pid_change(struct kvm_vcpu *vcpu)
			return ret;
	}

	if (vcpu_has_nv(vcpu)) {
		ret = kvm_init_nv_sysregs(vcpu->kvm);
	ret = kvm_finalize_sys_regs(vcpu);
	if (ret)
		return ret;
	}

	/*
	 * This needs to happen after NV has imposed its own restrictions on
	 * the feature set
	 * This needs to happen after any restriction has been applied
	 * to the feature set.
	 */
	kvm_calculate_traps(vcpu);

+66 −5
Original line number Diff line number Diff line
@@ -86,12 +86,17 @@ enum cgt_group_id {
	CGT_HCRX_EnFPM,
	CGT_HCRX_TCR2En,

	CGT_ICH_HCR_TC,
	CGT_ICH_HCR_TALL0,
	CGT_ICH_HCR_TALL1,
	CGT_ICH_HCR_TDIR,

	/*
	 * Anything after this point is a combination of coarse trap
	 * controls, which must all be evaluated to decide what to do.
	 */
	__MULTIPLE_CONTROL_BITS__,
	CGT_HCR_IMO_FMO = __MULTIPLE_CONTROL_BITS__,
	CGT_HCR_IMO_FMO_ICH_HCR_TC = __MULTIPLE_CONTROL_BITS__,
	CGT_HCR_TID2_TID4,
	CGT_HCR_TTLB_TTLBIS,
	CGT_HCR_TTLB_TTLBOS,
@@ -106,6 +111,8 @@ enum cgt_group_id {
	CGT_MDCR_TDE_TDRA,
	CGT_MDCR_TDCC_TDE_TDA,

	CGT_ICH_HCR_TC_TDIR,

	/*
	 * Anything after this point requires a callback evaluating a
	 * complex trap condition. Ugly stuff.
@@ -385,6 +392,30 @@ static const struct trap_bits coarse_trap_bits[] = {
		.mask		= HCRX_EL2_TCR2En,
		.behaviour	= BEHAVE_FORWARD_ANY,
	},
	[CGT_ICH_HCR_TC] = {
		.index		= ICH_HCR_EL2,
		.value		= ICH_HCR_TC,
		.mask		= ICH_HCR_TC,
		.behaviour	= BEHAVE_FORWARD_ANY,
	},
	[CGT_ICH_HCR_TALL0] = {
		.index		= ICH_HCR_EL2,
		.value		= ICH_HCR_TALL0,
		.mask		= ICH_HCR_TALL0,
		.behaviour	= BEHAVE_FORWARD_ANY,
	},
	[CGT_ICH_HCR_TALL1] = {
		.index		= ICH_HCR_EL2,
		.value		= ICH_HCR_TALL1,
		.mask		= ICH_HCR_TALL1,
		.behaviour	= BEHAVE_FORWARD_ANY,
	},
	[CGT_ICH_HCR_TDIR] = {
		.index		= ICH_HCR_EL2,
		.value		= ICH_HCR_TDIR,
		.mask		= ICH_HCR_TDIR,
		.behaviour	= BEHAVE_FORWARD_ANY,
	},
};

#define MCB(id, ...)						\
@@ -394,7 +425,6 @@ static const struct trap_bits coarse_trap_bits[] = {
		}

static const enum cgt_group_id *coarse_control_combo[] = {
	MCB(CGT_HCR_IMO_FMO,		CGT_HCR_IMO, CGT_HCR_FMO),
	MCB(CGT_HCR_TID2_TID4,		CGT_HCR_TID2, CGT_HCR_TID4),
	MCB(CGT_HCR_TTLB_TTLBIS,	CGT_HCR_TTLB, CGT_HCR_TTLBIS),
	MCB(CGT_HCR_TTLB_TTLBOS,	CGT_HCR_TTLB, CGT_HCR_TTLBOS),
@@ -409,6 +439,9 @@ static const enum cgt_group_id *coarse_control_combo[] = {
	MCB(CGT_MDCR_TDE_TDOSA,		CGT_MDCR_TDE, CGT_MDCR_TDOSA),
	MCB(CGT_MDCR_TDE_TDRA,		CGT_MDCR_TDE, CGT_MDCR_TDRA),
	MCB(CGT_MDCR_TDCC_TDE_TDA,	CGT_MDCR_TDCC, CGT_MDCR_TDE, CGT_MDCR_TDA),

	MCB(CGT_HCR_IMO_FMO_ICH_HCR_TC,	CGT_HCR_IMO, CGT_HCR_FMO, CGT_ICH_HCR_TC),
	MCB(CGT_ICH_HCR_TC_TDIR,	CGT_ICH_HCR_TC, CGT_ICH_HCR_TDIR),
};

typedef enum trap_behaviour (*complex_condition_check)(struct kvm_vcpu *);
@@ -543,9 +576,9 @@ static const struct encoding_to_trap_config encoding_to_cgt[] __initconst = {
	SR_TRAP(SYS_CSSELR_EL1,		CGT_HCR_TID2_TID4),
	SR_RANGE_TRAP(SYS_ID_PFR0_EL1,
		      sys_reg(3, 0, 0, 7, 7), CGT_HCR_TID3),
	SR_TRAP(SYS_ICC_SGI0R_EL1,	CGT_HCR_IMO_FMO),
	SR_TRAP(SYS_ICC_ASGI1R_EL1,	CGT_HCR_IMO_FMO),
	SR_TRAP(SYS_ICC_SGI1R_EL1,	CGT_HCR_IMO_FMO),
	SR_TRAP(SYS_ICC_SGI0R_EL1,	CGT_HCR_IMO_FMO_ICH_HCR_TC),
	SR_TRAP(SYS_ICC_ASGI1R_EL1,	CGT_HCR_IMO_FMO_ICH_HCR_TC),
	SR_TRAP(SYS_ICC_SGI1R_EL1,	CGT_HCR_IMO_FMO_ICH_HCR_TC),
	SR_RANGE_TRAP(sys_reg(3, 0, 11, 0, 0),
		      sys_reg(3, 0, 11, 15, 7), CGT_HCR_TIDCP),
	SR_RANGE_TRAP(sys_reg(3, 1, 11, 0, 0),
@@ -1116,6 +1149,34 @@ static const struct encoding_to_trap_config encoding_to_cgt[] __initconst = {
	SR_TRAP(SYS_CNTPCT_EL0,		CGT_CNTHCTL_EL1PCTEN),
	SR_TRAP(SYS_CNTPCTSS_EL0,	CGT_CNTHCTL_EL1PCTEN),
	SR_TRAP(SYS_FPMR,		CGT_HCRX_EnFPM),
	/*
	 * IMPDEF choice:
	 * We treat ICC_SRE_EL2.{SRE,Enable) and ICV_SRE_EL1.SRE as
	 * RAO/WI. We therefore never consider ICC_SRE_EL2.Enable for
	 * ICC_SRE_EL1 access, and always handle it locally.
	 */
	SR_TRAP(SYS_ICC_AP0R0_EL1,	CGT_ICH_HCR_TALL0),
	SR_TRAP(SYS_ICC_AP0R1_EL1,	CGT_ICH_HCR_TALL0),
	SR_TRAP(SYS_ICC_AP0R2_EL1,	CGT_ICH_HCR_TALL0),
	SR_TRAP(SYS_ICC_AP0R3_EL1,	CGT_ICH_HCR_TALL0),
	SR_TRAP(SYS_ICC_AP1R0_EL1,	CGT_ICH_HCR_TALL1),
	SR_TRAP(SYS_ICC_AP1R1_EL1,	CGT_ICH_HCR_TALL1),
	SR_TRAP(SYS_ICC_AP1R2_EL1,	CGT_ICH_HCR_TALL1),
	SR_TRAP(SYS_ICC_AP1R3_EL1,	CGT_ICH_HCR_TALL1),
	SR_TRAP(SYS_ICC_BPR0_EL1,	CGT_ICH_HCR_TALL0),
	SR_TRAP(SYS_ICC_BPR1_EL1,	CGT_ICH_HCR_TALL1),
	SR_TRAP(SYS_ICC_CTLR_EL1,	CGT_ICH_HCR_TC),
	SR_TRAP(SYS_ICC_DIR_EL1,	CGT_ICH_HCR_TC_TDIR),
	SR_TRAP(SYS_ICC_EOIR0_EL1,	CGT_ICH_HCR_TALL0),
	SR_TRAP(SYS_ICC_EOIR1_EL1,	CGT_ICH_HCR_TALL1),
	SR_TRAP(SYS_ICC_HPPIR0_EL1,	CGT_ICH_HCR_TALL0),
	SR_TRAP(SYS_ICC_HPPIR1_EL1,	CGT_ICH_HCR_TALL1),
	SR_TRAP(SYS_ICC_IAR0_EL1,	CGT_ICH_HCR_TALL0),
	SR_TRAP(SYS_ICC_IAR1_EL1,	CGT_ICH_HCR_TALL1),
	SR_TRAP(SYS_ICC_IGRPEN0_EL1,	CGT_ICH_HCR_TALL0),
	SR_TRAP(SYS_ICC_IGRPEN1_EL1,	CGT_ICH_HCR_TALL1),
	SR_TRAP(SYS_ICC_PMR_EL1,	CGT_ICH_HCR_TC),
	SR_TRAP(SYS_ICC_RPR_EL1,	CGT_ICH_HCR_TC),
};

static DEFINE_XARRAY(sr_forward_xa);
+91 −6
Original line number Diff line number Diff line
@@ -268,8 +268,16 @@ void __vgic_v3_activate_traps(struct vgic_v3_cpu_if *cpu_if)
	 * starting to mess with the rest of the GIC, and VMCR_EL2 in
	 * particular.  This logic must be called before
	 * __vgic_v3_restore_state().
	 *
	 * However, if the vgic is disabled (ICH_HCR_EL2.EN==0), no GIC is
	 * provisioned at all. In order to prevent illegal accesses to the
	 * system registers to trap to EL1 (duh), force ICC_SRE_EL1.SRE to 1
	 * so that the trap bits can take effect. Yes, we *loves* the GIC.
	 */
	if (!cpu_if->vgic_sre) {
	if (!(cpu_if->vgic_hcr & ICH_HCR_EN)) {
		write_gicreg(ICC_SRE_EL1_SRE, ICC_SRE_EL1);
		isb();
	} else if (!cpu_if->vgic_sre) {
		write_gicreg(0, ICC_SRE_EL1);
		isb();
		write_gicreg(cpu_if->vgic_vmcr, ICH_VMCR_EL2);
@@ -288,8 +296,9 @@ void __vgic_v3_activate_traps(struct vgic_v3_cpu_if *cpu_if)
	}

	/*
	 * Prevent the guest from touching the GIC system registers if
	 * SRE isn't enabled for GICv3 emulation.
	 * Prevent the guest from touching the ICC_SRE_EL1 system
	 * register. Note that this may not have any effect, as
	 * ICC_SRE_EL2.Enable being RAO/WI is a valid implementation.
	 */
	write_gicreg(read_gicreg(ICC_SRE_EL2) & ~ICC_SRE_EL2_ENABLE,
		     ICC_SRE_EL2);
@@ -297,10 +306,11 @@ void __vgic_v3_activate_traps(struct vgic_v3_cpu_if *cpu_if)
	/*
	 * If we need to trap system registers, we must write
	 * ICH_HCR_EL2 anyway, even if no interrupts are being
	 * injected,
	 * injected. Note that this also applies if we don't expect
	 * any system register access (no vgic at all).
	 */
	if (static_branch_unlikely(&vgic_v3_cpuif_trap) ||
	    cpu_if->its_vpe.its_vm)
	    cpu_if->its_vpe.its_vm || !cpu_if->vgic_sre)
		write_gicreg(cpu_if->vgic_hcr, ICH_HCR_EL2);
}

@@ -326,7 +336,7 @@ void __vgic_v3_deactivate_traps(struct vgic_v3_cpu_if *cpu_if)
	 * no interrupts were being injected, and we disable it again here.
	 */
	if (static_branch_unlikely(&vgic_v3_cpuif_trap) ||
	    cpu_if->its_vpe.its_vm)
	    cpu_if->its_vpe.its_vm || !cpu_if->vgic_sre)
		write_gicreg(0, ICH_HCR_EL2);
}

@@ -1032,6 +1042,75 @@ static void __vgic_v3_write_ctlr(struct kvm_vcpu *vcpu, u32 vmcr, int rt)
	write_gicreg(vmcr, ICH_VMCR_EL2);
}

static bool __vgic_v3_check_trap_forwarding(struct kvm_vcpu *vcpu,
					    u32 sysreg, bool is_read)
{
	u64 ich_hcr;

	if (!vcpu_has_nv(vcpu) || is_hyp_ctxt(vcpu))
		return false;

	ich_hcr = __vcpu_sys_reg(vcpu, ICH_HCR_EL2);

	switch (sysreg) {
	case SYS_ICC_IGRPEN0_EL1:
		if (is_read &&
		    (__vcpu_sys_reg(vcpu, HFGRTR_EL2) & HFGxTR_EL2_ICC_IGRPENn_EL1))
			return true;

		if (!is_read &&
		    (__vcpu_sys_reg(vcpu, HFGWTR_EL2) & HFGxTR_EL2_ICC_IGRPENn_EL1))
			return true;

		fallthrough;

	case SYS_ICC_AP0Rn_EL1(0):
	case SYS_ICC_AP0Rn_EL1(1):
	case SYS_ICC_AP0Rn_EL1(2):
	case SYS_ICC_AP0Rn_EL1(3):
	case SYS_ICC_BPR0_EL1:
	case SYS_ICC_EOIR0_EL1:
	case SYS_ICC_HPPIR0_EL1:
	case SYS_ICC_IAR0_EL1:
		return ich_hcr & ICH_HCR_TALL0;

	case SYS_ICC_IGRPEN1_EL1:
		if (is_read &&
		    (__vcpu_sys_reg(vcpu, HFGRTR_EL2) & HFGxTR_EL2_ICC_IGRPENn_EL1))
			return true;

		if (!is_read &&
		    (__vcpu_sys_reg(vcpu, HFGWTR_EL2) & HFGxTR_EL2_ICC_IGRPENn_EL1))
			return true;

		fallthrough;

	case SYS_ICC_AP1Rn_EL1(0):
	case SYS_ICC_AP1Rn_EL1(1):
	case SYS_ICC_AP1Rn_EL1(2):
	case SYS_ICC_AP1Rn_EL1(3):
	case SYS_ICC_BPR1_EL1:
	case SYS_ICC_EOIR1_EL1:
	case SYS_ICC_HPPIR1_EL1:
	case SYS_ICC_IAR1_EL1:
		return ich_hcr & ICH_HCR_TALL1;

	case SYS_ICC_DIR_EL1:
		if (ich_hcr & ICH_HCR_TDIR)
			return true;

		fallthrough;

	case SYS_ICC_RPR_EL1:
	case SYS_ICC_CTLR_EL1:
	case SYS_ICC_PMR_EL1:
		return ich_hcr & ICH_HCR_TC;

	default:
		return false;
	}
}

int __vgic_v3_perform_cpuif_access(struct kvm_vcpu *vcpu)
{
	int rt;
@@ -1041,6 +1120,9 @@ int __vgic_v3_perform_cpuif_access(struct kvm_vcpu *vcpu)
	bool is_read;
	u32 sysreg;

	if (kern_hyp_va(vcpu->kvm)->arch.vgic.vgic_model != KVM_DEV_TYPE_ARM_VGIC_V3)
		return 0;

	esr = kvm_vcpu_get_esr(vcpu);
	if (vcpu_mode_is_32bit(vcpu)) {
		if (!kvm_condition_valid(vcpu)) {
@@ -1055,6 +1137,9 @@ int __vgic_v3_perform_cpuif_access(struct kvm_vcpu *vcpu)

	is_read = (esr & ESR_ELx_SYS64_ISS_DIR_MASK) == ESR_ELx_SYS64_ISS_DIR_READ;

	if (__vgic_v3_check_trap_forwarding(vcpu, sysreg, is_read))
		return 0;

	switch (sysreg) {
	case SYS_ICC_IAR0_EL1:
	case SYS_ICC_IAR1_EL1:
+5 −10
Original line number Diff line number Diff line
@@ -954,19 +954,16 @@ static void set_sysreg_masks(struct kvm *kvm, int sr, u64 res0, u64 res1)
int kvm_init_nv_sysregs(struct kvm *kvm)
{
	u64 res0, res1;
	int ret = 0;

	mutex_lock(&kvm->arch.config_lock);
	lockdep_assert_held(&kvm->arch.config_lock);

	if (kvm->arch.sysreg_masks)
		goto out;
		return 0;

	kvm->arch.sysreg_masks = kzalloc(sizeof(*(kvm->arch.sysreg_masks)),
					 GFP_KERNEL_ACCOUNT);
	if (!kvm->arch.sysreg_masks) {
		ret = -ENOMEM;
		goto out;
	}
	if (!kvm->arch.sysreg_masks)
		return -ENOMEM;

	limit_nv_id_regs(kvm);

@@ -1195,8 +1192,6 @@ int kvm_init_nv_sysregs(struct kvm *kvm)
	if (!kvm_has_feat(kvm, ID_AA64PFR0_EL1, AMU, V1P1))
		res0 |= ~(res0 | res1);
	set_sysreg_masks(kvm, HAFGRTR_EL2, res0, res1);
out:
	mutex_unlock(&kvm->arch.config_lock);

	return ret;
	return 0;
}
Loading