Commit f94591e2 authored by Eric Auger's avatar Eric Auger Committed by Christoffer Dall
Browse files

KVM: arm/arm64: vgic-new: vgic_kvm_device: access to VGIC registers



This patch implements the switches for KVM_DEV_ARM_VGIC_GRP_DIST_REGS
and KVM_DEV_ARM_VGIC_GRP_CPU_REGS API which allows the userspace to
access VGIC registers.

Signed-off-by: default avatarEric Auger <eric.auger@linaro.org>
Signed-off-by: default avatarAndre Przywara <andre.przywara@arm.com>
Reviewed-by: default avatarChristoffer Dall <christoffer.dall@linaro.org>
parent e5c30294
Loading
Loading
Loading
Loading
+51 −2
Original line number Diff line number Diff line
@@ -226,6 +226,21 @@ void kvm_register_vgic_device(unsigned long type)
	}
}

/** vgic_attr_regs_access: allows user space to read/write VGIC registers
 *
 * @dev: kvm device handle
 * @attr: kvm device attribute
 * @reg: address the value is read or written
 * @is_write: write flag
 *
 */
static int vgic_attr_regs_access(struct kvm_device *dev,
				 struct kvm_device_attr *attr,
				 u32 *reg, bool is_write)
{
	return -ENXIO;
}

/* V2 ops */

static int vgic_v2_set_attr(struct kvm_device *dev,
@@ -234,8 +249,23 @@ static int vgic_v2_set_attr(struct kvm_device *dev,
	int ret;

	ret = vgic_set_common_attr(dev, attr);
	if (ret != -ENXIO)
		return ret;

	switch (attr->group) {
	case KVM_DEV_ARM_VGIC_GRP_DIST_REGS:
	case KVM_DEV_ARM_VGIC_GRP_CPU_REGS: {
		u32 __user *uaddr = (u32 __user *)(long)attr->addr;
		u32 reg;

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

		return vgic_attr_regs_access(dev, attr, &reg, true);
	}
	}

	return -ENXIO;
}

static int vgic_v2_get_attr(struct kvm_device *dev,
@@ -244,7 +274,23 @@ static int vgic_v2_get_attr(struct kvm_device *dev,
	int ret;

	ret = vgic_get_common_attr(dev, attr);
	if (ret != -ENXIO)
		return ret;

	switch (attr->group) {
	case KVM_DEV_ARM_VGIC_GRP_DIST_REGS:
	case KVM_DEV_ARM_VGIC_GRP_CPU_REGS: {
		u32 __user *uaddr = (u32 __user *)(long)attr->addr;
		u32 reg = 0;

		ret = vgic_attr_regs_access(dev, attr, &reg, false);
		if (ret)
			return ret;
		return put_user(reg, uaddr);
	}
	}

	return -ENXIO;
}

static int vgic_v2_has_attr(struct kvm_device *dev,
@@ -258,6 +304,9 @@ static int vgic_v2_has_attr(struct kvm_device *dev,
			return 0;
		}
		break;
	case KVM_DEV_ARM_VGIC_GRP_DIST_REGS:
	case KVM_DEV_ARM_VGIC_GRP_CPU_REGS:
		return vgic_v2_has_attr_regs(dev, attr);
	case KVM_DEV_ARM_VGIC_GRP_NR_IRQS:
		return 0;
	case KVM_DEV_ARM_VGIC_GRP_CTRL:
+38 −0
Original line number Diff line number Diff line
@@ -258,3 +258,41 @@ unsigned int vgic_v2_init_dist_iodev(struct vgic_io_device *dev)

	return SZ_4K;
}

int vgic_v2_has_attr_regs(struct kvm_device *dev, struct kvm_device_attr *attr)
{
	int nr_irqs = dev->kvm->arch.vgic.nr_spis + VGIC_NR_PRIVATE_IRQS;
	const struct vgic_register_region *regions;
	gpa_t addr;
	int nr_regions, i, len;

	addr = attr->attr & KVM_DEV_ARM_VGIC_OFFSET_MASK;

	switch (attr->group) {
	case KVM_DEV_ARM_VGIC_GRP_DIST_REGS:
		regions = vgic_v2_dist_registers;
		nr_regions = ARRAY_SIZE(vgic_v2_dist_registers);
		break;
	case KVM_DEV_ARM_VGIC_GRP_CPU_REGS:
		return -ENXIO;		/* TODO: describe CPU i/f regs also */
	default:
		return -ENXIO;
	}

	/* We only support aligned 32-bit accesses. */
	if (addr & 3)
		return -ENXIO;

	for (i = 0; i < nr_regions; i++) {
		if (regions[i].bits_per_irq)
			len = (regions[i].bits_per_irq * nr_irqs) / 8;
		else
			len = regions[i].len;

		if (regions[i].reg_offset <= addr &&
		    regions[i].reg_offset + len > addr)
			return 0;
	}

	return -ENXIO;
}
+1 −0
Original line number Diff line number Diff line
@@ -37,6 +37,7 @@ void vgic_v2_fold_lr_state(struct kvm_vcpu *vcpu);
void vgic_v2_populate_lr(struct kvm_vcpu *vcpu, struct vgic_irq *irq, int lr);
void vgic_v2_clear_lr(struct kvm_vcpu *vcpu, int lr);
void vgic_v2_set_underflow(struct kvm_vcpu *vcpu);
int vgic_v2_has_attr_regs(struct kvm_device *dev, struct kvm_device_attr *attr);
int vgic_register_dist_iodev(struct kvm *kvm, gpa_t dist_base_address,
			     enum vgic_type);