Commit 1f315e99 authored by Oliver Upton's avatar Oliver Upton
Browse files

Merge branch 'kvm-arm64/gcie-legacy' into kvmarm/next



* kvm-arm64/gcie-legacy:
  : Support for GICv3 emulation on GICv5, courtesy of Sascha Bischoff
  :
  : FEAT_GCIE_LEGACY adds the necessary hardware for GICv5 systems to
  : support the legacy GICv3 for VMs, including a backwards-compatible VGIC
  : implementation that we all know and love.
  :
  : As a starting point for GICv5 enablement in KVM, enable + use the
  : GICv3-compatible feature when running VMs on GICv5 hardware.
  KVM: arm64: gic-v5: Probe for GICv5
  KVM: arm64: gic-v5: Support GICv3 compat
  arm64/sysreg: Add ICH_VCTLR_EL2
  irqchip/gic-v5: Populate struct gic_kvm_info
  irqchip/gic-v5: Skip deactivate for forwarded PPI interrupts

Signed-off-by: default avatarOliver Upton <oliver.upton@linux.dev>
parents ccd73c57 ff2aa649
Loading
Loading
Loading
Loading
+2 −1
Original line number Diff line number Diff line
@@ -23,7 +23,8 @@ kvm-y += arm.o mmu.o mmio.o psci.o hypercalls.o pvtime.o \
	 vgic/vgic-v3.o vgic/vgic-v4.o \
	 vgic/vgic-mmio.o vgic/vgic-mmio-v2.o \
	 vgic/vgic-mmio-v3.o vgic/vgic-kvm-device.o \
	 vgic/vgic-its.o vgic/vgic-debug.o vgic/vgic-v3-nested.o
	 vgic/vgic-its.o vgic/vgic-debug.o vgic/vgic-v3-nested.o \
	 vgic/vgic-v5.o

kvm-$(CONFIG_HW_PERF_EVENTS)  += pmu-emul.o pmu.o
kvm-$(CONFIG_ARM64_PTR_AUTH)  += pauth.o
+43 −8
Original line number Diff line number Diff line
@@ -295,6 +295,12 @@ void __vgic_v3_activate_traps(struct vgic_v3_cpu_if *cpu_if)
		}
	}

	/*
	 * GICv5 BET0 FEAT_GCIE_LEGACY doesn't include ICC_SRE_EL2. This is due
	 * to be relaxed in a future spec release, at which point this in
	 * condition can be dropped.
	 */
	if (!cpus_have_final_cap(ARM64_HAS_GICV5_CPUIF)) {
		/*
		 * Prevent the guest from touching the ICC_SRE_EL1 system
		 * register. Note that this may not have any effect, as
@@ -302,6 +308,7 @@ void __vgic_v3_activate_traps(struct vgic_v3_cpu_if *cpu_if)
		 */
		write_gicreg(read_gicreg(ICC_SRE_EL2) & ~ICC_SRE_EL2_ENABLE,
			     ICC_SRE_EL2);
	}

	/*
	 * If we need to trap system registers, we must write
@@ -322,8 +329,14 @@ void __vgic_v3_deactivate_traps(struct vgic_v3_cpu_if *cpu_if)
		cpu_if->vgic_vmcr = read_gicreg(ICH_VMCR_EL2);
	}

	/*
	 * Can be dropped in the future when GICv5 spec is relaxed. See comment
	 * above.
	 */
	if (!cpus_have_final_cap(ARM64_HAS_GICV5_CPUIF)) {
		val = read_gicreg(ICC_SRE_EL2);
		write_gicreg(val | ICC_SRE_EL2_ENABLE, ICC_SRE_EL2);
	}

	if (!cpu_if->vgic_sre) {
		/* Make sure ENABLE is set at EL2 before setting SRE at EL1 */
@@ -423,9 +436,19 @@ void __vgic_v3_init_lrs(void)
 */
u64 __vgic_v3_get_gic_config(void)
{
	u64 val, sre = read_gicreg(ICC_SRE_EL1);
	u64 val, sre;
	unsigned long flags = 0;

	/*
	 * In compat mode, we cannot access ICC_SRE_EL1 at any EL
	 * other than EL1 itself; just return the
	 * ICH_VTR_EL2. ICC_IDR0_EL1 is only implemented on a GICv5
	 * system, so we first check if we have GICv5 support.
	 */
	if (cpus_have_final_cap(ARM64_HAS_GICV5_CPUIF))
		return read_gicreg(ICH_VTR_EL2);

	sre = read_gicreg(ICC_SRE_EL1);
	/*
	 * To check whether we have a MMIO-based (GICv2 compatible)
	 * CPU interface, we need to disable the system register
@@ -471,6 +494,16 @@ u64 __vgic_v3_get_gic_config(void)
	return val;
}

static void __vgic_v3_compat_mode_enable(void)
{
	if (!cpus_have_final_cap(ARM64_HAS_GICV5_CPUIF))
		return;

	sysreg_clear_set_s(SYS_ICH_VCTLR_EL2, 0, ICH_VCTLR_EL2_V3);
	/* Wait for V3 to become enabled */
	isb();
}

static u64 __vgic_v3_read_vmcr(void)
{
	return read_gicreg(ICH_VMCR_EL2);
@@ -490,6 +523,8 @@ void __vgic_v3_save_vmcr_aprs(struct vgic_v3_cpu_if *cpu_if)

void __vgic_v3_restore_vmcr_aprs(struct vgic_v3_cpu_if *cpu_if)
{
	__vgic_v3_compat_mode_enable();

	/*
	 * If dealing with a GICv2 emulation on GICv3, VMCR_EL2.VFIQen
	 * is dependent on ICC_SRE_EL1.SRE, and we have to perform the
+9 −1
Original line number Diff line number Diff line
@@ -1813,7 +1813,7 @@ static u64 sanitise_id_aa64pfr0_el1(const struct kvm_vcpu *vcpu, u64 val)
		val |= SYS_FIELD_PREP_ENUM(ID_AA64PFR0_EL1, CSV3, IMP);
	}

	if (kvm_vgic_global_state.type == VGIC_V3) {
	if (vgic_is_v3(vcpu->kvm)) {
		val &= ~ID_AA64PFR0_EL1_GIC_MASK;
		val |= SYS_FIELD_PREP_ENUM(ID_AA64PFR0_EL1, GIC, IMP);
	}
@@ -1955,6 +1955,14 @@ static int set_id_aa64pfr0_el1(struct kvm_vcpu *vcpu,
	    (vcpu_has_nv(vcpu) && !FIELD_GET(ID_AA64PFR0_EL1_EL2, user_val)))
		return -EINVAL;

	/*
	 * If we are running on a GICv5 host and support FEAT_GCIE_LEGACY, then
	 * we support GICv3. Fail attempts to do anything but set that to IMP.
	 */
	if (vgic_is_v3_compat(vcpu->kvm) &&
	    FIELD_GET(ID_AA64PFR0_EL1_GIC_MASK, user_val) != ID_AA64PFR0_EL1_GIC_IMP)
		return -EINVAL;

	return set_id_reg(vcpu, rd, user_val);
}

+7 −2
Original line number Diff line number Diff line
@@ -674,11 +674,13 @@ void kvm_vgic_init_cpu_hardware(void)
	 * We want to make sure the list registers start out clear so that we
	 * only have the program the used registers.
	 */
	if (kvm_vgic_global_state.type == VGIC_V2)
	if (kvm_vgic_global_state.type == VGIC_V2) {
		vgic_v2_init_lrs();
	else
	} else if (kvm_vgic_global_state.type == VGIC_V3 ||
		   kvm_vgic_global_state.has_gcie_v3_compat) {
		kvm_call_hyp(__vgic_v3_init_lrs);
	}
}

/**
 * kvm_vgic_hyp_init: populates the kvm_vgic_global_state variable
@@ -722,6 +724,9 @@ int kvm_vgic_hyp_init(void)
			kvm_info("GIC system register CPU interface enabled\n");
		}
		break;
	case GIC_V5:
		ret = vgic_v5_probe(gic_kvm_info);
		break;
	default:
		ret = -ENODEV;
	}
+52 −0
Original line number Diff line number Diff line
// SPDX-License-Identifier: GPL-2.0-only

#include <kvm/arm_vgic.h>
#include <linux/irqchip/arm-vgic-info.h>

#include "vgic.h"

/*
 * Probe for a vGICv5 compatible interrupt controller, returning 0 on success.
 * Currently only supports GICv3-based VMs on a GICv5 host, and hence only
 * registers a VGIC_V3 device.
 */
int vgic_v5_probe(const struct gic_kvm_info *info)
{
	u64 ich_vtr_el2;
	int ret;

	if (!info->has_gcie_v3_compat)
		return -ENODEV;

	kvm_vgic_global_state.type = VGIC_V5;
	kvm_vgic_global_state.has_gcie_v3_compat = true;

	/* We only support v3 compat mode - use vGICv3 limits */
	kvm_vgic_global_state.max_gic_vcpus = VGIC_V3_MAX_CPUS;

	kvm_vgic_global_state.vcpu_base = 0;
	kvm_vgic_global_state.vctrl_base = NULL;
	kvm_vgic_global_state.can_emulate_gicv2 = false;
	kvm_vgic_global_state.has_gicv4 = false;
	kvm_vgic_global_state.has_gicv4_1 = false;

	ich_vtr_el2 =  kvm_call_hyp_ret(__vgic_v3_get_gic_config);
	kvm_vgic_global_state.ich_vtr_el2 = (u32)ich_vtr_el2;

	/*
	 * The ListRegs field is 5 bits, but there is an architectural
	 * maximum of 16 list registers. Just ignore bit 4...
	 */
	kvm_vgic_global_state.nr_lr = (ich_vtr_el2 & 0xf) + 1;

	ret = kvm_register_vgic_device(KVM_DEV_TYPE_ARM_VGIC_V3);
	if (ret) {
		kvm_err("Cannot register GICv3-legacy KVM device.\n");
		return ret;
	}

	static_branch_enable(&kvm_vgic_global_state.gicv3_cpuif);
	kvm_info("GCIE legacy system register CPU interface\n");

	return 0;
}
Loading