Commit 49a1a2c7 authored by Marc Zyngier's avatar Marc Zyngier
Browse files

KVM: arm64: vgic-v3: Advertise GICR_CTLR.{IR, CES} as a new GICD_IIDR revision



Since adversising GICR_CTLR.{IC,CES} is directly observable from
a guest, we need to make it selectable from userspace.

For that, bump the default GICD_IIDR revision and let userspace
downgrade it to the previous default. For GICv2, the two distributor
revisions are strictly equivalent.

Signed-off-by: default avatarMarc Zyngier <maz@kernel.org>
Link: https://lore.kernel.org/r/20220405182327.205520-5-maz@kernel.org
parent 4645d11f
Loading
Loading
Loading
Loading
+6 −1
Original line number Diff line number Diff line
@@ -319,7 +319,12 @@ int vgic_init(struct kvm *kvm)

	vgic_debug_init(kvm);

	dist->implementation_rev = 2;
	/*
	 * 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:
+15 −3
Original line number Diff line number Diff line
@@ -73,9 +73,13 @@ static int vgic_mmio_uaccess_write_v2_misc(struct kvm_vcpu *vcpu,
					   gpa_t addr, unsigned int len,
					   unsigned long val)
{
	struct vgic_dist *dist = &vcpu->kvm->arch.vgic;
	u32 reg;

	switch (addr & 0x0c) {
	case GIC_DIST_IIDR:
		if (val != vgic_mmio_read_v2_misc(vcpu, addr, len))
		reg = vgic_mmio_read_v2_misc(vcpu, addr, len);
		if ((reg ^ val) & ~GICD_IIDR_REVISION_MASK)
			return -EINVAL;

		/*
@@ -87,8 +91,16 @@ static int vgic_mmio_uaccess_write_v2_misc(struct kvm_vcpu *vcpu,
		 * migration from old kernels to new kernels with legacy
		 * userspace.
		 */
		reg = FIELD_GET(GICD_IIDR_REVISION_MASK, reg);
		switch (reg) {
		case KVM_VGIC_IMP_REV_2:
		case KVM_VGIC_IMP_REV_3:
			vcpu->kvm->arch.vgic.v2_groups_user_writable = true;
			dist->implementation_rev = reg;
			return 0;
		default:
			return -EINVAL;
		}
	}

	vgic_mmio_write_v2_misc(vcpu, addr, len, val);
+21 −2
Original line number Diff line number Diff line
@@ -155,13 +155,27 @@ static int vgic_mmio_uaccess_write_v3_misc(struct kvm_vcpu *vcpu,
					   unsigned long val)
{
	struct vgic_dist *dist = &vcpu->kvm->arch.vgic;
	u32 reg;

	switch (addr & 0x0c) {
	case GICD_TYPER2:
	case GICD_IIDR:
		if (val != vgic_mmio_read_v3_misc(vcpu, addr, len))
			return -EINVAL;
		return 0;
	case GICD_IIDR:
		reg = vgic_mmio_read_v3_misc(vcpu, addr, len);
		if ((reg ^ val) & ~GICD_IIDR_REVISION_MASK)
			return -EINVAL;

		reg = FIELD_GET(GICD_IIDR_REVISION_MASK, reg);
		switch (reg) {
		case KVM_VGIC_IMP_REV_2:
		case KVM_VGIC_IMP_REV_3:
			dist->implementation_rev = reg;
			return 0;
		default:
			return -EINVAL;
		}
	case GICD_CTLR:
		/* Not a GICv4.1? No HW SGIs */
		if (!kvm_vgic_global_state.has_gicv4_1)
@@ -232,8 +246,13 @@ static unsigned long vgic_mmio_read_v3r_ctlr(struct kvm_vcpu *vcpu,
					     gpa_t addr, unsigned int len)
{
	struct vgic_cpu *vgic_cpu = &vcpu->arch.vgic_cpu;
	unsigned long val;

	val = atomic_read(&vgic_cpu->ctlr);
	if (vgic_get_implementation_rev(vcpu) >= KVM_VGIC_IMP_REV_3)
		val |= GICR_CTLR_IR | GICR_CTLR_CES;

	return vgic_cpu->lpis_enabled ? GICR_CTLR_ENABLE_LPIS : 0;
	return val;
}

static void vgic_mmio_write_v3r_ctlr(struct kvm_vcpu *vcpu,
+5 −0
Original line number Diff line number Diff line
@@ -98,6 +98,11 @@
#define DEBUG_SPINLOCK_BUG_ON(p)
#endif

static inline u32 vgic_get_implementation_rev(struct kvm_vcpu *vcpu)
{
	return vcpu->kvm->arch.vgic.implementation_rev;
}

/* Requires the irq_lock to be held by the caller. */
static inline bool irq_is_pending(struct vgic_irq *irq)
{
+3 −0
Original line number Diff line number Diff line
@@ -231,6 +231,9 @@ struct vgic_dist {

	/* Implementation revision as reported in the GICD_IIDR */
	u32			implementation_rev;
#define KVM_VGIC_IMP_REV_2	2 /* GICv2 restorable groups */
#define KVM_VGIC_IMP_REV_3	3 /* GICv3 GICR_CTLR.{IW,CES,RWP} */
#define KVM_VGIC_IMP_REV_LATEST	KVM_VGIC_IMP_REV_3

	/* Userspace can write to GICv2 IGROUPR */
	bool			v2_groups_user_writable;