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

KVM: arm/arm64: vgic-new: vgic_kvm_device: implement kvm_vgic_addr



kvm_vgic_addr is used by the userspace to set the base address of
the following register regions, as seen by the guest:
- distributor(v2 and v3),
- re-distributors (v3),
- CPU interface (v2).

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 afcc7c50
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -194,6 +194,8 @@ struct vgic_cpu {
	u64 live_lrs;
};

int kvm_vgic_addr(struct kvm *kvm, unsigned long type, u64 *addr, bool write);

int kvm_vgic_inject_irq(struct kvm *kvm, int cpuid, unsigned int intid,
			bool level);

+86 −0
Original line number Diff line number Diff line
@@ -16,10 +16,96 @@
#include <linux/kvm_host.h>
#include <kvm/arm_vgic.h>
#include <linux/uaccess.h>
#include <asm/kvm_mmu.h>
#include "vgic.h"

/* common helpers */

static int vgic_check_ioaddr(struct kvm *kvm, phys_addr_t *ioaddr,
			     phys_addr_t addr, phys_addr_t alignment)
{
	if (addr & ~KVM_PHYS_MASK)
		return -E2BIG;

	if (!IS_ALIGNED(addr, alignment))
		return -EINVAL;

	if (!IS_VGIC_ADDR_UNDEF(*ioaddr))
		return -EEXIST;

	return 0;
}

/**
 * kvm_vgic_addr - set or get vgic VM base addresses
 * @kvm:   pointer to the vm struct
 * @type:  the VGIC addr type, one of KVM_VGIC_V[23]_ADDR_TYPE_XXX
 * @addr:  pointer to address value
 * @write: if true set the address in the VM address space, if false read the
 *          address
 *
 * Set or get the vgic base addresses for the distributor and the virtual CPU
 * interface in the VM physical address space.  These addresses are properties
 * of the emulated core/SoC and therefore user space initially knows this
 * information.
 * Check them for sanity (alignment, double assignment). We can't check for
 * overlapping regions in case of a virtual GICv3 here, since we don't know
 * the number of VCPUs yet, so we defer this check to map_resources().
 */
int kvm_vgic_addr(struct kvm *kvm, unsigned long type, u64 *addr, bool write)
{
	int r = 0;
	struct vgic_dist *vgic = &kvm->arch.vgic;
	int type_needed;
	phys_addr_t *addr_ptr, alignment;

	mutex_lock(&kvm->lock);
	switch (type) {
	case KVM_VGIC_V2_ADDR_TYPE_DIST:
		type_needed = KVM_DEV_TYPE_ARM_VGIC_V2;
		addr_ptr = &vgic->vgic_dist_base;
		alignment = SZ_4K;
		break;
	case KVM_VGIC_V2_ADDR_TYPE_CPU:
		type_needed = KVM_DEV_TYPE_ARM_VGIC_V2;
		addr_ptr = &vgic->vgic_cpu_base;
		alignment = SZ_4K;
		break;
#ifdef CONFIG_KVM_ARM_VGIC_V3
	case KVM_VGIC_V3_ADDR_TYPE_DIST:
		type_needed = KVM_DEV_TYPE_ARM_VGIC_V3;
		addr_ptr = &vgic->vgic_dist_base;
		alignment = SZ_64K;
		break;
	case KVM_VGIC_V3_ADDR_TYPE_REDIST:
		type_needed = KVM_DEV_TYPE_ARM_VGIC_V3;
		addr_ptr = &vgic->vgic_redist_base;
		alignment = SZ_64K;
		break;
#endif
	default:
		r = -ENODEV;
		goto out;
	}

	if (vgic->vgic_model != type_needed) {
		r = -ENODEV;
		goto out;
	}

	if (write) {
		r = vgic_check_ioaddr(kvm, addr_ptr, *addr, alignment);
		if (!r)
			*addr_ptr = *addr;
	} else {
		*addr = *addr_ptr;
	}

out:
	mutex_unlock(&kvm->lock);
	return r;
}

static int vgic_set_common_attr(struct kvm_device *dev,
				struct kvm_device_attr *attr)
{
+3 −0
Original line number Diff line number Diff line
@@ -19,6 +19,9 @@
#define PRODUCT_ID_KVM		0x4b	/* ASCII code K */
#define IMPLEMENTER_ARM		0x43b

#define VGIC_ADDR_UNDEF		(-1)
#define IS_VGIC_ADDR_UNDEF(_x)  ((_x) == VGIC_ADDR_UNDEF)

#define INTERRUPT_ID_BITS_SPIS	10
#define VGIC_PRI_BITS		5