Commit 7d450e28 authored by Andre Przywara's avatar Andre Przywara Committed by Christoffer Dall
Browse files

KVM: arm/arm64: vgic-new: Add userland access to VGIC dist registers



Userland may want to save and restore the state of the in-kernel VGIC,
so we provide the code which takes a userland request and translate
that into calls to our MMIO framework.

From Christoffer:
When accessing the VGIC state from userspace we really don't want a VCPU
to be messing with the state at the same time, and the API specifies
that we should return -EBUSY if any VCPUs are running.
Check and prevent VCPUs from running by grabbing their mutexes, one by
one, and error out if we fail.
(Note: This could potentially be simplified to just do a simple check
and see if any VCPUs are running, and return -EBUSY then, without
enforcing the locking throughout the duration of the uaccess, if we
think that taking/releasing all these mutexes for every single GIC
register access is too heavyweight.)

Signed-off-by: default avatarAndre Przywara <andre.przywara@arm.com>
Signed-off-by: default avatarChristoffer Dall <christoffer.dall@linaro.org>
parent c3199f28
Loading
Loading
Loading
Loading
+54 −1
Original line number Diff line number Diff line
@@ -238,7 +238,60 @@ static int vgic_attr_regs_access(struct kvm_device *dev,
				 struct kvm_device_attr *attr,
				 u32 *reg, bool is_write)
{
	return -ENXIO;
	gpa_t addr;
	int cpuid, ret, c;
	struct kvm_vcpu *vcpu, *tmp_vcpu;
	int vcpu_lock_idx = -1;

	cpuid = (attr->attr & KVM_DEV_ARM_VGIC_CPUID_MASK) >>
		 KVM_DEV_ARM_VGIC_CPUID_SHIFT;
	vcpu = kvm_get_vcpu(dev->kvm, cpuid);
	addr = attr->attr & KVM_DEV_ARM_VGIC_OFFSET_MASK;

	mutex_lock(&dev->kvm->lock);

	ret = vgic_init(dev->kvm);
	if (ret)
		goto out;

	if (cpuid >= atomic_read(&dev->kvm->online_vcpus)) {
		ret = -EINVAL;
		goto out;
	}

	/*
	 * Any time a vcpu is run, vcpu_load is called which tries to grab the
	 * vcpu->mutex.  By grabbing the vcpu->mutex of all VCPUs we ensure
	 * that no other VCPUs are run and fiddle with the vgic state while we
	 * access it.
	 */
	ret = -EBUSY;
	kvm_for_each_vcpu(c, tmp_vcpu, dev->kvm) {
		if (!mutex_trylock(&tmp_vcpu->mutex))
			goto out;
		vcpu_lock_idx = c;
	}

	switch (attr->group) {
	case KVM_DEV_ARM_VGIC_GRP_CPU_REGS:
		ret = -EINVAL;
		break;
	case KVM_DEV_ARM_VGIC_GRP_DIST_REGS:
		ret = vgic_v2_dist_uaccess(vcpu, is_write, addr, reg);
		break;
	default:
		ret = -EINVAL;
		break;
	}

out:
	for (; vcpu_lock_idx >= 0; vcpu_lock_idx--) {
		tmp_vcpu = kvm_get_vcpu(dev->kvm, vcpu_lock_idx);
		mutex_unlock(&tmp_vcpu->mutex);
	}

	mutex_unlock(&dev->kvm->lock);
	return ret;
}

/* V2 ops */