Commit 0d46e324 authored by Oliver Upton's avatar Oliver Upton
Browse files

Merge branch 'kvm-arm64/vgic-v4-ctl' into kvmarm/next



* kvm-arm64/vgic-v4-ctl:
  : Userspace control of nASSGIcap, courtesy of Raghavendra Rao Ananta
  :
  : Allow userspace to decide if support for SGIs without an active state is
  : advertised to the guest, allowing VMs from GICv3-only hardware to be
  : migrated to to GICv4.1 capable machines.
  Documentation: KVM: arm64: Describe VGICv3 registers writable pre-init
  KVM: arm64: selftests: Add test for nASSGIcap attribute
  KVM: arm64: vgic-v3: Allow userspace to write GICD_TYPER2.nASSGIcap
  KVM: arm64: vgic-v3: Allow access to GICD_IIDR prior to initialization
  KVM: arm64: vgic-v3: Consolidate MAINT_IRQ handling
  KVM: arm64: Disambiguate support for vSGIs v. vLPIs

Signed-off-by: default avatarOliver Upton <oliver.upton@linux.dev>
parents a7f49a9b eed9b142
Loading
Loading
Loading
Loading
+17 −0
Original line number Diff line number Diff line
@@ -78,6 +78,8 @@ Groups:
    -ENXIO   The group or attribute is unknown/unsupported for this device
             or hardware support is missing.
    -EFAULT  Invalid user pointer for attr->addr.
    -EBUSY   Attempt to write a register that is read-only after
             initialization
    =======  =============================================================


@@ -120,6 +122,15 @@ Groups:
    Note that distributor fields are not banked, but return the same value
    regardless of the mpidr used to access the register.

    Userspace is allowed to write the following register fields prior to
    initialization of the VGIC:

      =====================
      GICD_IIDR.Revision
      GICD_TYPER2.nASSGIcap
      =====================


    GICD_IIDR.Revision is updated when the KVM implementation is changed in a
    way directly observable by the guest or userspace.  Userspace should read
    GICD_IIDR from KVM and write back the read value to confirm its expected
@@ -128,6 +139,12 @@ Groups:
    behavior.


    GICD_TYPER2.nASSGIcap allows userspace to control the support of SGIs
    without an active state. At VGIC creation the field resets to the
    maximum capability of the system. Userspace is expected to read the field
    to determine the supported value(s) before writing to the field.


    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
+8 −13
Original line number Diff line number Diff line
@@ -157,6 +157,7 @@ int kvm_vgic_create(struct kvm *kvm, u32 type)

	kvm->arch.vgic.in_kernel = true;
	kvm->arch.vgic.vgic_model = type;
	kvm->arch.vgic.implementation_rev = KVM_VGIC_IMP_REV_LATEST;

	kvm->arch.vgic.vgic_dist_base = VGIC_ADDR_UNDEF;

@@ -165,6 +166,9 @@ int kvm_vgic_create(struct kvm *kvm, u32 type)
	else
		INIT_LIST_HEAD(&kvm->arch.vgic.rd_regions);

	if (type == KVM_DEV_TYPE_ARM_VGIC_V3)
		kvm->arch.vgic.nassgicap = system_supports_direct_sgis();

out_unlock:
	mutex_unlock(&kvm->arch.config_lock);
	kvm_unlock_all_vcpus(kvm);
@@ -391,11 +395,10 @@ int vgic_init(struct kvm *kvm)
		goto out;

	/*
	 * If we have GICv4.1 enabled, unconditionally request enable the
	 * v4 support so that we get HW-accelerated vSGIs. Otherwise, only
	 * enable it if we present a virtual ITS to the guest.
	 * Ensure vPEs are allocated if direct IRQ injection (e.g. vSGIs,
	 * vLPIs) is supported.
	 */
	if (vgic_supports_direct_msis(kvm)) {
	if (vgic_supports_direct_irqs(kvm)) {
		ret = vgic_v4_init(kvm);
		if (ret)
			goto out;
@@ -409,15 +412,7 @@ int vgic_init(struct kvm *kvm)
		goto out;

	vgic_debug_init(kvm);

	/*
	 * If userspace didn't set the GIC implementation revision,
	 * default to the latest and greatest. You know want it.
	 */
	if (!dist->implementation_rev)
		dist->implementation_rev = KVM_VGIC_IMP_REV_LATEST;
	dist->initialized = true;

out:
	return ret;
}
@@ -443,7 +438,7 @@ static void kvm_vgic_dist_destroy(struct kvm *kvm)
		dist->vgic_cpu_base = VGIC_ADDR_UNDEF;
	}

	if (vgic_supports_direct_msis(kvm))
	if (vgic_supports_direct_irqs(kvm))
		vgic_v4_teardown(kvm);

	xa_destroy(&dist->lpi_xa);
+44 −26
Original line number Diff line number Diff line
@@ -5,6 +5,7 @@
 * Copyright (C) 2015 ARM Ltd.
 * Author: Marc Zyngier <marc.zyngier@arm.com>
 */
#include <linux/irqchip/arm-gic-v3.h>
#include <linux/kvm_host.h>
#include <kvm/arm_vgic.h>
#include <linux/uaccess.h>
@@ -303,12 +304,6 @@ static int vgic_get_common_attr(struct kvm_device *dev,
			     VGIC_NR_PRIVATE_IRQS, uaddr);
		break;
	}
	case KVM_DEV_ARM_VGIC_GRP_MAINT_IRQ: {
		u32 __user *uaddr = (u32 __user *)(long)attr->addr;

		r = put_user(dev->kvm->arch.vgic.mi_intid, uaddr);
		break;
	}
	}

	return r;
@@ -509,6 +504,24 @@ int vgic_v3_parse_attr(struct kvm_device *dev, struct kvm_device_attr *attr,
	return 0;
}

/*
 * Allow access to certain ID-like registers prior to VGIC initialization,
 * thereby allowing the VMM to provision the features / sizing of the VGIC.
 */
static bool reg_allowed_pre_init(struct kvm_device_attr *attr)
{
	if (attr->group != KVM_DEV_ARM_VGIC_GRP_DIST_REGS)
		return false;

	switch (attr->attr & KVM_DEV_ARM_VGIC_OFFSET_MASK) {
	case GICD_IIDR:
	case GICD_TYPER2:
		return true;
	default:
		return false;
	}
}

/*
 * vgic_v3_attr_regs_access - allows user space to access VGIC v3 state
 *
@@ -523,7 +536,7 @@ static int vgic_v3_attr_regs_access(struct kvm_device *dev,
	struct vgic_reg_attr reg_attr;
	gpa_t addr;
	struct kvm_vcpu *vcpu;
	bool uaccess, post_init = true;
	bool uaccess;
	u32 val;
	int ret;

@@ -539,9 +552,6 @@ static int vgic_v3_attr_regs_access(struct kvm_device *dev,
		/* Sysregs uaccess is performed by the sysreg handling code */
		uaccess = false;
		break;
	case KVM_DEV_ARM_VGIC_GRP_MAINT_IRQ:
		post_init = false;
		fallthrough;
	default:
		uaccess = true;
	}
@@ -561,7 +571,7 @@ static int vgic_v3_attr_regs_access(struct kvm_device *dev,

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

	if (post_init != vgic_initialized(dev->kvm)) {
	if (!(vgic_initialized(dev->kvm) || reg_allowed_pre_init(attr))) {
		ret = -EBUSY;
		goto out;
	}
@@ -591,19 +601,6 @@ static int vgic_v3_attr_regs_access(struct kvm_device *dev,
		}
		break;
	}
	case KVM_DEV_ARM_VGIC_GRP_MAINT_IRQ:
		if (!is_write) {
			val = dev->kvm->arch.vgic.mi_intid;
			ret = 0;
			break;
		}

		ret = -EINVAL;
		if ((val < VGIC_NR_PRIVATE_IRQS) && (val >= VGIC_NR_SGIS)) {
			dev->kvm->arch.vgic.mi_intid = val;
			ret = 0;
		}
		break;
	default:
		ret = -EINVAL;
		break;
@@ -630,8 +627,24 @@ static int vgic_v3_set_attr(struct kvm_device *dev,
	case KVM_DEV_ARM_VGIC_GRP_REDIST_REGS:
	case KVM_DEV_ARM_VGIC_GRP_CPU_SYSREGS:
	case KVM_DEV_ARM_VGIC_GRP_LEVEL_INFO:
	case KVM_DEV_ARM_VGIC_GRP_MAINT_IRQ:
		return vgic_v3_attr_regs_access(dev, attr, true);
	case KVM_DEV_ARM_VGIC_GRP_MAINT_IRQ: {
		u32 __user *uaddr = (u32 __user *)attr->addr;
		u32 val;

		if (get_user(val, uaddr))
			return -EFAULT;

		guard(mutex)(&dev->kvm->arch.config_lock);
		if (vgic_initialized(dev->kvm))
			return -EBUSY;

		if (!irq_is_ppi(val))
			return -EINVAL;

		dev->kvm->arch.vgic.mi_intid = val;
		return 0;
	}
	default:
		return vgic_set_common_attr(dev, attr);
	}
@@ -645,8 +658,13 @@ static int vgic_v3_get_attr(struct kvm_device *dev,
	case KVM_DEV_ARM_VGIC_GRP_REDIST_REGS:
	case KVM_DEV_ARM_VGIC_GRP_CPU_SYSREGS:
	case KVM_DEV_ARM_VGIC_GRP_LEVEL_INFO:
	case KVM_DEV_ARM_VGIC_GRP_MAINT_IRQ:
		return vgic_v3_attr_regs_access(dev, attr, false);
	case KVM_DEV_ARM_VGIC_GRP_MAINT_IRQ: {
		u32 __user *uaddr = (u32 __user *)(long)attr->addr;

		guard(mutex)(&dev->kvm->arch.config_lock);
		return put_user(dev->kvm->arch.vgic.mi_intid, uaddr);
	}
	default:
		return vgic_get_common_attr(dev, attr);
	}
+26 −7
Original line number Diff line number Diff line
@@ -50,8 +50,17 @@ bool vgic_has_its(struct kvm *kvm)

bool vgic_supports_direct_msis(struct kvm *kvm)
{
	return (kvm_vgic_global_state.has_gicv4_1 ||
		(kvm_vgic_global_state.has_gicv4 && vgic_has_its(kvm)));
	return kvm_vgic_global_state.has_gicv4 && vgic_has_its(kvm);
}

bool system_supports_direct_sgis(void)
{
	return kvm_vgic_global_state.has_gicv4_1 && gic_cpuif_has_vsgi();
}

bool vgic_supports_direct_sgis(struct kvm *kvm)
{
	return kvm->arch.vgic.nassgicap;
}

/*
@@ -86,7 +95,7 @@ static unsigned long vgic_mmio_read_v3_misc(struct kvm_vcpu *vcpu,
		}
		break;
	case GICD_TYPER2:
		if (kvm_vgic_global_state.has_gicv4_1 && gic_cpuif_has_vsgi())
		if (vgic_supports_direct_sgis(vcpu->kvm))
			value = GICD_TYPER2_nASSGIcap;
		break;
	case GICD_IIDR:
@@ -119,7 +128,7 @@ static void vgic_mmio_write_v3_misc(struct kvm_vcpu *vcpu,
		dist->enabled = val & GICD_CTLR_ENABLE_SS_G1;

		/* Not a GICv4.1? No HW SGIs */
		if (!kvm_vgic_global_state.has_gicv4_1 || !gic_cpuif_has_vsgi())
		if (!vgic_supports_direct_sgis(vcpu->kvm))
			val &= ~GICD_CTLR_nASSGIreq;

		/* Dist stays enabled? nASSGIreq is RO */
@@ -133,7 +142,7 @@ static void vgic_mmio_write_v3_misc(struct kvm_vcpu *vcpu,
		if (is_hwsgi != dist->nassgireq)
			vgic_v4_configure_vsgis(vcpu->kvm);

		if (kvm_vgic_global_state.has_gicv4_1 &&
		if (vgic_supports_direct_sgis(vcpu->kvm) &&
		    was_enabled != dist->enabled)
			kvm_make_all_cpus_request(vcpu->kvm, KVM_REQ_RELOAD_GICv4);
		else if (!was_enabled && dist->enabled)
@@ -159,8 +168,18 @@ static int vgic_mmio_uaccess_write_v3_misc(struct kvm_vcpu *vcpu,

	switch (addr & 0x0c) {
	case GICD_TYPER2:
		if (val != vgic_mmio_read_v3_misc(vcpu, addr, len))
		reg = vgic_mmio_read_v3_misc(vcpu, addr, len);

		if (reg == val)
			return 0;
		if (vgic_initialized(vcpu->kvm))
			return -EBUSY;
		if ((reg ^ val) & ~GICD_TYPER2_nASSGIcap)
			return -EINVAL;
		if (!system_supports_direct_sgis() && val)
			return -EINVAL;

		dist->nassgicap = val & GICD_TYPER2_nASSGIcap;
		return 0;
	case GICD_IIDR:
		reg = vgic_mmio_read_v3_misc(vcpu, addr, len);
@@ -178,7 +197,7 @@ static int vgic_mmio_uaccess_write_v3_misc(struct kvm_vcpu *vcpu,
		}
	case GICD_CTLR:
		/* Not a GICv4.1? No HW SGIs */
		if (!kvm_vgic_global_state.has_gicv4_1)
		if (!vgic_supports_direct_sgis(vcpu->kvm))
			val &= ~GICD_CTLR_nASSGIreq;

		dist->enabled = val & GICD_CTLR_ENABLE_SS_G1;
+2 −2
Original line number Diff line number Diff line
@@ -356,7 +356,7 @@ int vgic_v4_put(struct kvm_vcpu *vcpu)
{
	struct its_vpe *vpe = &vcpu->arch.vgic_cpu.vgic_v3.its_vpe;

	if (!vgic_supports_direct_msis(vcpu->kvm) || !vpe->resident)
	if (!vgic_supports_direct_irqs(vcpu->kvm) || !vpe->resident)
		return 0;

	return its_make_vpe_non_resident(vpe, vgic_v4_want_doorbell(vcpu));
@@ -367,7 +367,7 @@ int vgic_v4_load(struct kvm_vcpu *vcpu)
	struct its_vpe *vpe = &vcpu->arch.vgic_cpu.vgic_v3.its_vpe;
	int err;

	if (!vgic_supports_direct_msis(vcpu->kvm) || vpe->resident)
	if (!vgic_supports_direct_irqs(vcpu->kvm) || vpe->resident)
		return 0;

	if (vcpu_get_flag(vcpu, IN_WFI))
Loading