19ed24f4bSMarc Zyngier // SPDX-License-Identifier: GPL-2.0-only
29ed24f4bSMarc Zyngier /*
39ed24f4bSMarc Zyngier  * VGIC: KVM DEVICE API
49ed24f4bSMarc Zyngier  *
59ed24f4bSMarc Zyngier  * Copyright (C) 2015 ARM Ltd.
69ed24f4bSMarc Zyngier  * Author: Marc Zyngier <marc.zyngier@arm.com>
79ed24f4bSMarc Zyngier  */
89ed24f4bSMarc Zyngier #include <linux/kvm_host.h>
99ed24f4bSMarc Zyngier #include <kvm/arm_vgic.h>
109ed24f4bSMarc Zyngier #include <linux/uaccess.h>
119ed24f4bSMarc Zyngier #include <asm/kvm_mmu.h>
129ed24f4bSMarc Zyngier #include <asm/cputype.h>
139ed24f4bSMarc Zyngier #include "vgic.h"
149ed24f4bSMarc Zyngier 
159ed24f4bSMarc Zyngier /* common helpers */
169ed24f4bSMarc Zyngier 
179ed24f4bSMarc Zyngier int vgic_check_ioaddr(struct kvm *kvm, phys_addr_t *ioaddr,
189ed24f4bSMarc Zyngier 		      phys_addr_t addr, phys_addr_t alignment)
199ed24f4bSMarc Zyngier {
209ed24f4bSMarc Zyngier 	if (addr & ~kvm_phys_mask(kvm))
219ed24f4bSMarc Zyngier 		return -E2BIG;
229ed24f4bSMarc Zyngier 
239ed24f4bSMarc Zyngier 	if (!IS_ALIGNED(addr, alignment))
249ed24f4bSMarc Zyngier 		return -EINVAL;
259ed24f4bSMarc Zyngier 
269ed24f4bSMarc Zyngier 	if (!IS_VGIC_ADDR_UNDEF(*ioaddr))
279ed24f4bSMarc Zyngier 		return -EEXIST;
289ed24f4bSMarc Zyngier 
299ed24f4bSMarc Zyngier 	return 0;
309ed24f4bSMarc Zyngier }
319ed24f4bSMarc Zyngier 
32*f25c5e4dSRicardo Koller int vgic_check_iorange(struct kvm *kvm, phys_addr_t ioaddr,
33*f25c5e4dSRicardo Koller 		       phys_addr_t addr, phys_addr_t alignment,
34*f25c5e4dSRicardo Koller 		       phys_addr_t size)
35*f25c5e4dSRicardo Koller {
36*f25c5e4dSRicardo Koller 	int ret;
37*f25c5e4dSRicardo Koller 
38*f25c5e4dSRicardo Koller 	ret = vgic_check_ioaddr(kvm, &ioaddr, addr, alignment);
39*f25c5e4dSRicardo Koller 	if (ret)
40*f25c5e4dSRicardo Koller 		return ret;
41*f25c5e4dSRicardo Koller 
42*f25c5e4dSRicardo Koller 	if (!IS_ALIGNED(size, alignment))
43*f25c5e4dSRicardo Koller 		return -EINVAL;
44*f25c5e4dSRicardo Koller 
45*f25c5e4dSRicardo Koller 	if (addr + size < addr)
46*f25c5e4dSRicardo Koller 		return -EINVAL;
47*f25c5e4dSRicardo Koller 
48*f25c5e4dSRicardo Koller 	if (addr + size > kvm_phys_size(kvm))
49*f25c5e4dSRicardo Koller 		return -E2BIG;
50*f25c5e4dSRicardo Koller 
51*f25c5e4dSRicardo Koller 	return 0;
52*f25c5e4dSRicardo Koller }
53*f25c5e4dSRicardo Koller 
549ed24f4bSMarc Zyngier static int vgic_check_type(struct kvm *kvm, int type_needed)
559ed24f4bSMarc Zyngier {
569ed24f4bSMarc Zyngier 	if (kvm->arch.vgic.vgic_model != type_needed)
579ed24f4bSMarc Zyngier 		return -ENODEV;
589ed24f4bSMarc Zyngier 	else
599ed24f4bSMarc Zyngier 		return 0;
609ed24f4bSMarc Zyngier }
619ed24f4bSMarc Zyngier 
629ed24f4bSMarc Zyngier /**
639ed24f4bSMarc Zyngier  * kvm_vgic_addr - set or get vgic VM base addresses
649ed24f4bSMarc Zyngier  * @kvm:   pointer to the vm struct
659ed24f4bSMarc Zyngier  * @type:  the VGIC addr type, one of KVM_VGIC_V[23]_ADDR_TYPE_XXX
669ed24f4bSMarc Zyngier  * @addr:  pointer to address value
679ed24f4bSMarc Zyngier  * @write: if true set the address in the VM address space, if false read the
689ed24f4bSMarc Zyngier  *          address
699ed24f4bSMarc Zyngier  *
709ed24f4bSMarc Zyngier  * Set or get the vgic base addresses for the distributor and the virtual CPU
719ed24f4bSMarc Zyngier  * interface in the VM physical address space.  These addresses are properties
729ed24f4bSMarc Zyngier  * of the emulated core/SoC and therefore user space initially knows this
739ed24f4bSMarc Zyngier  * information.
749ed24f4bSMarc Zyngier  * Check them for sanity (alignment, double assignment). We can't check for
759ed24f4bSMarc Zyngier  * overlapping regions in case of a virtual GICv3 here, since we don't know
769ed24f4bSMarc Zyngier  * the number of VCPUs yet, so we defer this check to map_resources().
779ed24f4bSMarc Zyngier  */
789ed24f4bSMarc Zyngier int kvm_vgic_addr(struct kvm *kvm, unsigned long type, u64 *addr, bool write)
799ed24f4bSMarc Zyngier {
809ed24f4bSMarc Zyngier 	int r = 0;
819ed24f4bSMarc Zyngier 	struct vgic_dist *vgic = &kvm->arch.vgic;
829ed24f4bSMarc Zyngier 	phys_addr_t *addr_ptr, alignment;
839ed24f4bSMarc Zyngier 	u64 undef_value = VGIC_ADDR_UNDEF;
849ed24f4bSMarc Zyngier 
859ed24f4bSMarc Zyngier 	mutex_lock(&kvm->lock);
869ed24f4bSMarc Zyngier 	switch (type) {
879ed24f4bSMarc Zyngier 	case KVM_VGIC_V2_ADDR_TYPE_DIST:
889ed24f4bSMarc Zyngier 		r = vgic_check_type(kvm, KVM_DEV_TYPE_ARM_VGIC_V2);
899ed24f4bSMarc Zyngier 		addr_ptr = &vgic->vgic_dist_base;
909ed24f4bSMarc Zyngier 		alignment = SZ_4K;
919ed24f4bSMarc Zyngier 		break;
929ed24f4bSMarc Zyngier 	case KVM_VGIC_V2_ADDR_TYPE_CPU:
939ed24f4bSMarc Zyngier 		r = vgic_check_type(kvm, KVM_DEV_TYPE_ARM_VGIC_V2);
949ed24f4bSMarc Zyngier 		addr_ptr = &vgic->vgic_cpu_base;
959ed24f4bSMarc Zyngier 		alignment = SZ_4K;
969ed24f4bSMarc Zyngier 		break;
979ed24f4bSMarc Zyngier 	case KVM_VGIC_V3_ADDR_TYPE_DIST:
989ed24f4bSMarc Zyngier 		r = vgic_check_type(kvm, KVM_DEV_TYPE_ARM_VGIC_V3);
999ed24f4bSMarc Zyngier 		addr_ptr = &vgic->vgic_dist_base;
1009ed24f4bSMarc Zyngier 		alignment = SZ_64K;
1019ed24f4bSMarc Zyngier 		break;
1029ed24f4bSMarc Zyngier 	case KVM_VGIC_V3_ADDR_TYPE_REDIST: {
1039ed24f4bSMarc Zyngier 		struct vgic_redist_region *rdreg;
1049ed24f4bSMarc Zyngier 
1059ed24f4bSMarc Zyngier 		r = vgic_check_type(kvm, KVM_DEV_TYPE_ARM_VGIC_V3);
1069ed24f4bSMarc Zyngier 		if (r)
1079ed24f4bSMarc Zyngier 			break;
1089ed24f4bSMarc Zyngier 		if (write) {
1099ed24f4bSMarc Zyngier 			r = vgic_v3_set_redist_base(kvm, 0, *addr, 0);
1109ed24f4bSMarc Zyngier 			goto out;
1119ed24f4bSMarc Zyngier 		}
11294ac0835SEric Auger 		rdreg = list_first_entry_or_null(&vgic->rd_regions,
1139ed24f4bSMarc Zyngier 						 struct vgic_redist_region, list);
1149ed24f4bSMarc Zyngier 		if (!rdreg)
1159ed24f4bSMarc Zyngier 			addr_ptr = &undef_value;
1169ed24f4bSMarc Zyngier 		else
1179ed24f4bSMarc Zyngier 			addr_ptr = &rdreg->base;
1189ed24f4bSMarc Zyngier 		break;
1199ed24f4bSMarc Zyngier 	}
1209ed24f4bSMarc Zyngier 	case KVM_VGIC_V3_ADDR_TYPE_REDIST_REGION:
1219ed24f4bSMarc Zyngier 	{
1229ed24f4bSMarc Zyngier 		struct vgic_redist_region *rdreg;
1239ed24f4bSMarc Zyngier 		u8 index;
1249ed24f4bSMarc Zyngier 
1259ed24f4bSMarc Zyngier 		r = vgic_check_type(kvm, KVM_DEV_TYPE_ARM_VGIC_V3);
1269ed24f4bSMarc Zyngier 		if (r)
1279ed24f4bSMarc Zyngier 			break;
1289ed24f4bSMarc Zyngier 
1299ed24f4bSMarc Zyngier 		index = *addr & KVM_VGIC_V3_RDIST_INDEX_MASK;
1309ed24f4bSMarc Zyngier 
1319ed24f4bSMarc Zyngier 		if (write) {
1329ed24f4bSMarc Zyngier 			gpa_t base = *addr & KVM_VGIC_V3_RDIST_BASE_MASK;
1339ed24f4bSMarc Zyngier 			u32 count = (*addr & KVM_VGIC_V3_RDIST_COUNT_MASK)
1349ed24f4bSMarc Zyngier 					>> KVM_VGIC_V3_RDIST_COUNT_SHIFT;
1359ed24f4bSMarc Zyngier 			u8 flags = (*addr & KVM_VGIC_V3_RDIST_FLAGS_MASK)
1369ed24f4bSMarc Zyngier 					>> KVM_VGIC_V3_RDIST_FLAGS_SHIFT;
1379ed24f4bSMarc Zyngier 
1389ed24f4bSMarc Zyngier 			if (!count || flags)
1399ed24f4bSMarc Zyngier 				r = -EINVAL;
1409ed24f4bSMarc Zyngier 			else
1419ed24f4bSMarc Zyngier 				r = vgic_v3_set_redist_base(kvm, index,
1429ed24f4bSMarc Zyngier 							    base, count);
1439ed24f4bSMarc Zyngier 			goto out;
1449ed24f4bSMarc Zyngier 		}
1459ed24f4bSMarc Zyngier 
1469ed24f4bSMarc Zyngier 		rdreg = vgic_v3_rdist_region_from_index(kvm, index);
1479ed24f4bSMarc Zyngier 		if (!rdreg) {
1489ed24f4bSMarc Zyngier 			r = -ENOENT;
1499ed24f4bSMarc Zyngier 			goto out;
1509ed24f4bSMarc Zyngier 		}
1519ed24f4bSMarc Zyngier 
1529ed24f4bSMarc Zyngier 		*addr = index;
1539ed24f4bSMarc Zyngier 		*addr |= rdreg->base;
1549ed24f4bSMarc Zyngier 		*addr |= (u64)rdreg->count << KVM_VGIC_V3_RDIST_COUNT_SHIFT;
1559ed24f4bSMarc Zyngier 		goto out;
1569ed24f4bSMarc Zyngier 	}
1579ed24f4bSMarc Zyngier 	default:
1589ed24f4bSMarc Zyngier 		r = -ENODEV;
1599ed24f4bSMarc Zyngier 	}
1609ed24f4bSMarc Zyngier 
1619ed24f4bSMarc Zyngier 	if (r)
1629ed24f4bSMarc Zyngier 		goto out;
1639ed24f4bSMarc Zyngier 
1649ed24f4bSMarc Zyngier 	if (write) {
1659ed24f4bSMarc Zyngier 		r = vgic_check_ioaddr(kvm, addr_ptr, *addr, alignment);
1669ed24f4bSMarc Zyngier 		if (!r)
1679ed24f4bSMarc Zyngier 			*addr_ptr = *addr;
1689ed24f4bSMarc Zyngier 	} else {
1699ed24f4bSMarc Zyngier 		*addr = *addr_ptr;
1709ed24f4bSMarc Zyngier 	}
1719ed24f4bSMarc Zyngier 
1729ed24f4bSMarc Zyngier out:
1739ed24f4bSMarc Zyngier 	mutex_unlock(&kvm->lock);
1749ed24f4bSMarc Zyngier 	return r;
1759ed24f4bSMarc Zyngier }
1769ed24f4bSMarc Zyngier 
1779ed24f4bSMarc Zyngier static int vgic_set_common_attr(struct kvm_device *dev,
1789ed24f4bSMarc Zyngier 				struct kvm_device_attr *attr)
1799ed24f4bSMarc Zyngier {
1809ed24f4bSMarc Zyngier 	int r;
1819ed24f4bSMarc Zyngier 
1829ed24f4bSMarc Zyngier 	switch (attr->group) {
1839ed24f4bSMarc Zyngier 	case KVM_DEV_ARM_VGIC_GRP_ADDR: {
1849ed24f4bSMarc Zyngier 		u64 __user *uaddr = (u64 __user *)(long)attr->addr;
1859ed24f4bSMarc Zyngier 		u64 addr;
1869ed24f4bSMarc Zyngier 		unsigned long type = (unsigned long)attr->attr;
1879ed24f4bSMarc Zyngier 
1889ed24f4bSMarc Zyngier 		if (copy_from_user(&addr, uaddr, sizeof(addr)))
1899ed24f4bSMarc Zyngier 			return -EFAULT;
1909ed24f4bSMarc Zyngier 
1919ed24f4bSMarc Zyngier 		r = kvm_vgic_addr(dev->kvm, type, &addr, true);
1929ed24f4bSMarc Zyngier 		return (r == -ENODEV) ? -ENXIO : r;
1939ed24f4bSMarc Zyngier 	}
1949ed24f4bSMarc Zyngier 	case KVM_DEV_ARM_VGIC_GRP_NR_IRQS: {
1959ed24f4bSMarc Zyngier 		u32 __user *uaddr = (u32 __user *)(long)attr->addr;
1969ed24f4bSMarc Zyngier 		u32 val;
1979ed24f4bSMarc Zyngier 		int ret = 0;
1989ed24f4bSMarc Zyngier 
1999ed24f4bSMarc Zyngier 		if (get_user(val, uaddr))
2009ed24f4bSMarc Zyngier 			return -EFAULT;
2019ed24f4bSMarc Zyngier 
2029ed24f4bSMarc Zyngier 		/*
2039ed24f4bSMarc Zyngier 		 * We require:
2049ed24f4bSMarc Zyngier 		 * - at least 32 SPIs on top of the 16 SGIs and 16 PPIs
2059ed24f4bSMarc Zyngier 		 * - at most 1024 interrupts
2069ed24f4bSMarc Zyngier 		 * - a multiple of 32 interrupts
2079ed24f4bSMarc Zyngier 		 */
2089ed24f4bSMarc Zyngier 		if (val < (VGIC_NR_PRIVATE_IRQS + 32) ||
2099ed24f4bSMarc Zyngier 		    val > VGIC_MAX_RESERVED ||
2109ed24f4bSMarc Zyngier 		    (val & 31))
2119ed24f4bSMarc Zyngier 			return -EINVAL;
2129ed24f4bSMarc Zyngier 
2139ed24f4bSMarc Zyngier 		mutex_lock(&dev->kvm->lock);
2149ed24f4bSMarc Zyngier 
2159ed24f4bSMarc Zyngier 		if (vgic_ready(dev->kvm) || dev->kvm->arch.vgic.nr_spis)
2169ed24f4bSMarc Zyngier 			ret = -EBUSY;
2179ed24f4bSMarc Zyngier 		else
2189ed24f4bSMarc Zyngier 			dev->kvm->arch.vgic.nr_spis =
2199ed24f4bSMarc Zyngier 				val - VGIC_NR_PRIVATE_IRQS;
2209ed24f4bSMarc Zyngier 
2219ed24f4bSMarc Zyngier 		mutex_unlock(&dev->kvm->lock);
2229ed24f4bSMarc Zyngier 
2239ed24f4bSMarc Zyngier 		return ret;
2249ed24f4bSMarc Zyngier 	}
2259ed24f4bSMarc Zyngier 	case KVM_DEV_ARM_VGIC_GRP_CTRL: {
2269ed24f4bSMarc Zyngier 		switch (attr->attr) {
2279ed24f4bSMarc Zyngier 		case KVM_DEV_ARM_VGIC_CTRL_INIT:
2289ed24f4bSMarc Zyngier 			mutex_lock(&dev->kvm->lock);
2299ed24f4bSMarc Zyngier 			r = vgic_init(dev->kvm);
2309ed24f4bSMarc Zyngier 			mutex_unlock(&dev->kvm->lock);
2319ed24f4bSMarc Zyngier 			return r;
2329ed24f4bSMarc Zyngier 		}
2339ed24f4bSMarc Zyngier 		break;
2349ed24f4bSMarc Zyngier 	}
2359ed24f4bSMarc Zyngier 	}
2369ed24f4bSMarc Zyngier 
2379ed24f4bSMarc Zyngier 	return -ENXIO;
2389ed24f4bSMarc Zyngier }
2399ed24f4bSMarc Zyngier 
2409ed24f4bSMarc Zyngier static int vgic_get_common_attr(struct kvm_device *dev,
2419ed24f4bSMarc Zyngier 				struct kvm_device_attr *attr)
2429ed24f4bSMarc Zyngier {
2439ed24f4bSMarc Zyngier 	int r = -ENXIO;
2449ed24f4bSMarc Zyngier 
2459ed24f4bSMarc Zyngier 	switch (attr->group) {
2469ed24f4bSMarc Zyngier 	case KVM_DEV_ARM_VGIC_GRP_ADDR: {
2479ed24f4bSMarc Zyngier 		u64 __user *uaddr = (u64 __user *)(long)attr->addr;
2489ed24f4bSMarc Zyngier 		u64 addr;
2499ed24f4bSMarc Zyngier 		unsigned long type = (unsigned long)attr->attr;
2509ed24f4bSMarc Zyngier 
25153b16dd6SEric Auger 		if (copy_from_user(&addr, uaddr, sizeof(addr)))
25253b16dd6SEric Auger 			return -EFAULT;
25353b16dd6SEric Auger 
2549ed24f4bSMarc Zyngier 		r = kvm_vgic_addr(dev->kvm, type, &addr, false);
2559ed24f4bSMarc Zyngier 		if (r)
2569ed24f4bSMarc Zyngier 			return (r == -ENODEV) ? -ENXIO : r;
2579ed24f4bSMarc Zyngier 
2589ed24f4bSMarc Zyngier 		if (copy_to_user(uaddr, &addr, sizeof(addr)))
2599ed24f4bSMarc Zyngier 			return -EFAULT;
2609ed24f4bSMarc Zyngier 		break;
2619ed24f4bSMarc Zyngier 	}
2629ed24f4bSMarc Zyngier 	case KVM_DEV_ARM_VGIC_GRP_NR_IRQS: {
2639ed24f4bSMarc Zyngier 		u32 __user *uaddr = (u32 __user *)(long)attr->addr;
2649ed24f4bSMarc Zyngier 
2659ed24f4bSMarc Zyngier 		r = put_user(dev->kvm->arch.vgic.nr_spis +
2669ed24f4bSMarc Zyngier 			     VGIC_NR_PRIVATE_IRQS, uaddr);
2679ed24f4bSMarc Zyngier 		break;
2689ed24f4bSMarc Zyngier 	}
2699ed24f4bSMarc Zyngier 	}
2709ed24f4bSMarc Zyngier 
2719ed24f4bSMarc Zyngier 	return r;
2729ed24f4bSMarc Zyngier }
2739ed24f4bSMarc Zyngier 
2749ed24f4bSMarc Zyngier static int vgic_create(struct kvm_device *dev, u32 type)
2759ed24f4bSMarc Zyngier {
2769ed24f4bSMarc Zyngier 	return kvm_vgic_create(dev->kvm, type);
2779ed24f4bSMarc Zyngier }
2789ed24f4bSMarc Zyngier 
2799ed24f4bSMarc Zyngier static void vgic_destroy(struct kvm_device *dev)
2809ed24f4bSMarc Zyngier {
2819ed24f4bSMarc Zyngier 	kfree(dev);
2829ed24f4bSMarc Zyngier }
2839ed24f4bSMarc Zyngier 
2849ed24f4bSMarc Zyngier int kvm_register_vgic_device(unsigned long type)
2859ed24f4bSMarc Zyngier {
2869ed24f4bSMarc Zyngier 	int ret = -ENODEV;
2879ed24f4bSMarc Zyngier 
2889ed24f4bSMarc Zyngier 	switch (type) {
2899ed24f4bSMarc Zyngier 	case KVM_DEV_TYPE_ARM_VGIC_V2:
2909ed24f4bSMarc Zyngier 		ret = kvm_register_device_ops(&kvm_arm_vgic_v2_ops,
2919ed24f4bSMarc Zyngier 					      KVM_DEV_TYPE_ARM_VGIC_V2);
2929ed24f4bSMarc Zyngier 		break;
2939ed24f4bSMarc Zyngier 	case KVM_DEV_TYPE_ARM_VGIC_V3:
2949ed24f4bSMarc Zyngier 		ret = kvm_register_device_ops(&kvm_arm_vgic_v3_ops,
2959ed24f4bSMarc Zyngier 					      KVM_DEV_TYPE_ARM_VGIC_V3);
2969ed24f4bSMarc Zyngier 
2979ed24f4bSMarc Zyngier 		if (ret)
2989ed24f4bSMarc Zyngier 			break;
2999ed24f4bSMarc Zyngier 		ret = kvm_vgic_register_its_device();
3009ed24f4bSMarc Zyngier 		break;
3019ed24f4bSMarc Zyngier 	}
3029ed24f4bSMarc Zyngier 
3039ed24f4bSMarc Zyngier 	return ret;
3049ed24f4bSMarc Zyngier }
3059ed24f4bSMarc Zyngier 
3069ed24f4bSMarc Zyngier int vgic_v2_parse_attr(struct kvm_device *dev, struct kvm_device_attr *attr,
3079ed24f4bSMarc Zyngier 		       struct vgic_reg_attr *reg_attr)
3089ed24f4bSMarc Zyngier {
3099ed24f4bSMarc Zyngier 	int cpuid;
3109ed24f4bSMarc Zyngier 
3119ed24f4bSMarc Zyngier 	cpuid = (attr->attr & KVM_DEV_ARM_VGIC_CPUID_MASK) >>
3129ed24f4bSMarc Zyngier 		 KVM_DEV_ARM_VGIC_CPUID_SHIFT;
3139ed24f4bSMarc Zyngier 
3149ed24f4bSMarc Zyngier 	if (cpuid >= atomic_read(&dev->kvm->online_vcpus))
3159ed24f4bSMarc Zyngier 		return -EINVAL;
3169ed24f4bSMarc Zyngier 
3179ed24f4bSMarc Zyngier 	reg_attr->vcpu = kvm_get_vcpu(dev->kvm, cpuid);
3189ed24f4bSMarc Zyngier 	reg_attr->addr = attr->attr & KVM_DEV_ARM_VGIC_OFFSET_MASK;
3199ed24f4bSMarc Zyngier 
3209ed24f4bSMarc Zyngier 	return 0;
3219ed24f4bSMarc Zyngier }
3229ed24f4bSMarc Zyngier 
3239ed24f4bSMarc Zyngier /* unlocks vcpus from @vcpu_lock_idx and smaller */
3249ed24f4bSMarc Zyngier static void unlock_vcpus(struct kvm *kvm, int vcpu_lock_idx)
3259ed24f4bSMarc Zyngier {
3269ed24f4bSMarc Zyngier 	struct kvm_vcpu *tmp_vcpu;
3279ed24f4bSMarc Zyngier 
3289ed24f4bSMarc Zyngier 	for (; vcpu_lock_idx >= 0; vcpu_lock_idx--) {
3299ed24f4bSMarc Zyngier 		tmp_vcpu = kvm_get_vcpu(kvm, vcpu_lock_idx);
3309ed24f4bSMarc Zyngier 		mutex_unlock(&tmp_vcpu->mutex);
3319ed24f4bSMarc Zyngier 	}
3329ed24f4bSMarc Zyngier }
3339ed24f4bSMarc Zyngier 
3349ed24f4bSMarc Zyngier void unlock_all_vcpus(struct kvm *kvm)
3359ed24f4bSMarc Zyngier {
3369ed24f4bSMarc Zyngier 	unlock_vcpus(kvm, atomic_read(&kvm->online_vcpus) - 1);
3379ed24f4bSMarc Zyngier }
3389ed24f4bSMarc Zyngier 
3399ed24f4bSMarc Zyngier /* Returns true if all vcpus were locked, false otherwise */
3409ed24f4bSMarc Zyngier bool lock_all_vcpus(struct kvm *kvm)
3419ed24f4bSMarc Zyngier {
3429ed24f4bSMarc Zyngier 	struct kvm_vcpu *tmp_vcpu;
3439ed24f4bSMarc Zyngier 	int c;
3449ed24f4bSMarc Zyngier 
3459ed24f4bSMarc Zyngier 	/*
3469ed24f4bSMarc Zyngier 	 * Any time a vcpu is run, vcpu_load is called which tries to grab the
3479ed24f4bSMarc Zyngier 	 * vcpu->mutex.  By grabbing the vcpu->mutex of all VCPUs we ensure
3489ed24f4bSMarc Zyngier 	 * that no other VCPUs are run and fiddle with the vgic state while we
3499ed24f4bSMarc Zyngier 	 * access it.
3509ed24f4bSMarc Zyngier 	 */
3519ed24f4bSMarc Zyngier 	kvm_for_each_vcpu(c, tmp_vcpu, kvm) {
3529ed24f4bSMarc Zyngier 		if (!mutex_trylock(&tmp_vcpu->mutex)) {
3539ed24f4bSMarc Zyngier 			unlock_vcpus(kvm, c - 1);
3549ed24f4bSMarc Zyngier 			return false;
3559ed24f4bSMarc Zyngier 		}
3569ed24f4bSMarc Zyngier 	}
3579ed24f4bSMarc Zyngier 
3589ed24f4bSMarc Zyngier 	return true;
3599ed24f4bSMarc Zyngier }
3609ed24f4bSMarc Zyngier 
3619ed24f4bSMarc Zyngier /**
3629ed24f4bSMarc Zyngier  * vgic_v2_attr_regs_access - allows user space to access VGIC v2 state
3639ed24f4bSMarc Zyngier  *
3649ed24f4bSMarc Zyngier  * @dev:      kvm device handle
3659ed24f4bSMarc Zyngier  * @attr:     kvm device attribute
3669ed24f4bSMarc Zyngier  * @reg:      address the value is read or written
3679ed24f4bSMarc Zyngier  * @is_write: true if userspace is writing a register
3689ed24f4bSMarc Zyngier  */
3699ed24f4bSMarc Zyngier static int vgic_v2_attr_regs_access(struct kvm_device *dev,
3709ed24f4bSMarc Zyngier 				    struct kvm_device_attr *attr,
3719ed24f4bSMarc Zyngier 				    u32 *reg, bool is_write)
3729ed24f4bSMarc Zyngier {
3739ed24f4bSMarc Zyngier 	struct vgic_reg_attr reg_attr;
3749ed24f4bSMarc Zyngier 	gpa_t addr;
3759ed24f4bSMarc Zyngier 	struct kvm_vcpu *vcpu;
3769ed24f4bSMarc Zyngier 	int ret;
3779ed24f4bSMarc Zyngier 
3789ed24f4bSMarc Zyngier 	ret = vgic_v2_parse_attr(dev, attr, &reg_attr);
3799ed24f4bSMarc Zyngier 	if (ret)
3809ed24f4bSMarc Zyngier 		return ret;
3819ed24f4bSMarc Zyngier 
3829ed24f4bSMarc Zyngier 	vcpu = reg_attr.vcpu;
3839ed24f4bSMarc Zyngier 	addr = reg_attr.addr;
3849ed24f4bSMarc Zyngier 
3859ed24f4bSMarc Zyngier 	mutex_lock(&dev->kvm->lock);
3869ed24f4bSMarc Zyngier 
3879ed24f4bSMarc Zyngier 	ret = vgic_init(dev->kvm);
3889ed24f4bSMarc Zyngier 	if (ret)
3899ed24f4bSMarc Zyngier 		goto out;
3909ed24f4bSMarc Zyngier 
3919ed24f4bSMarc Zyngier 	if (!lock_all_vcpus(dev->kvm)) {
3929ed24f4bSMarc Zyngier 		ret = -EBUSY;
3939ed24f4bSMarc Zyngier 		goto out;
3949ed24f4bSMarc Zyngier 	}
3959ed24f4bSMarc Zyngier 
3969ed24f4bSMarc Zyngier 	switch (attr->group) {
3979ed24f4bSMarc Zyngier 	case KVM_DEV_ARM_VGIC_GRP_CPU_REGS:
3989ed24f4bSMarc Zyngier 		ret = vgic_v2_cpuif_uaccess(vcpu, is_write, addr, reg);
3999ed24f4bSMarc Zyngier 		break;
4009ed24f4bSMarc Zyngier 	case KVM_DEV_ARM_VGIC_GRP_DIST_REGS:
4019ed24f4bSMarc Zyngier 		ret = vgic_v2_dist_uaccess(vcpu, is_write, addr, reg);
4029ed24f4bSMarc Zyngier 		break;
4039ed24f4bSMarc Zyngier 	default:
4049ed24f4bSMarc Zyngier 		ret = -EINVAL;
4059ed24f4bSMarc Zyngier 		break;
4069ed24f4bSMarc Zyngier 	}
4079ed24f4bSMarc Zyngier 
4089ed24f4bSMarc Zyngier 	unlock_all_vcpus(dev->kvm);
4099ed24f4bSMarc Zyngier out:
4109ed24f4bSMarc Zyngier 	mutex_unlock(&dev->kvm->lock);
4119ed24f4bSMarc Zyngier 	return ret;
4129ed24f4bSMarc Zyngier }
4139ed24f4bSMarc Zyngier 
4149ed24f4bSMarc Zyngier static int vgic_v2_set_attr(struct kvm_device *dev,
4159ed24f4bSMarc Zyngier 			    struct kvm_device_attr *attr)
4169ed24f4bSMarc Zyngier {
4179ed24f4bSMarc Zyngier 	int ret;
4189ed24f4bSMarc Zyngier 
4199ed24f4bSMarc Zyngier 	ret = vgic_set_common_attr(dev, attr);
4209ed24f4bSMarc Zyngier 	if (ret != -ENXIO)
4219ed24f4bSMarc Zyngier 		return ret;
4229ed24f4bSMarc Zyngier 
4239ed24f4bSMarc Zyngier 	switch (attr->group) {
4249ed24f4bSMarc Zyngier 	case KVM_DEV_ARM_VGIC_GRP_DIST_REGS:
4259ed24f4bSMarc Zyngier 	case KVM_DEV_ARM_VGIC_GRP_CPU_REGS: {
4269ed24f4bSMarc Zyngier 		u32 __user *uaddr = (u32 __user *)(long)attr->addr;
4279ed24f4bSMarc Zyngier 		u32 reg;
4289ed24f4bSMarc Zyngier 
4299ed24f4bSMarc Zyngier 		if (get_user(reg, uaddr))
4309ed24f4bSMarc Zyngier 			return -EFAULT;
4319ed24f4bSMarc Zyngier 
4329ed24f4bSMarc Zyngier 		return vgic_v2_attr_regs_access(dev, attr, &reg, true);
4339ed24f4bSMarc Zyngier 	}
4349ed24f4bSMarc Zyngier 	}
4359ed24f4bSMarc Zyngier 
4369ed24f4bSMarc Zyngier 	return -ENXIO;
4379ed24f4bSMarc Zyngier }
4389ed24f4bSMarc Zyngier 
4399ed24f4bSMarc Zyngier static int vgic_v2_get_attr(struct kvm_device *dev,
4409ed24f4bSMarc Zyngier 			    struct kvm_device_attr *attr)
4419ed24f4bSMarc Zyngier {
4429ed24f4bSMarc Zyngier 	int ret;
4439ed24f4bSMarc Zyngier 
4449ed24f4bSMarc Zyngier 	ret = vgic_get_common_attr(dev, attr);
4459ed24f4bSMarc Zyngier 	if (ret != -ENXIO)
4469ed24f4bSMarc Zyngier 		return ret;
4479ed24f4bSMarc Zyngier 
4489ed24f4bSMarc Zyngier 	switch (attr->group) {
4499ed24f4bSMarc Zyngier 	case KVM_DEV_ARM_VGIC_GRP_DIST_REGS:
4509ed24f4bSMarc Zyngier 	case KVM_DEV_ARM_VGIC_GRP_CPU_REGS: {
4519ed24f4bSMarc Zyngier 		u32 __user *uaddr = (u32 __user *)(long)attr->addr;
4529ed24f4bSMarc Zyngier 		u32 reg = 0;
4539ed24f4bSMarc Zyngier 
4549ed24f4bSMarc Zyngier 		ret = vgic_v2_attr_regs_access(dev, attr, &reg, false);
4559ed24f4bSMarc Zyngier 		if (ret)
4569ed24f4bSMarc Zyngier 			return ret;
4579ed24f4bSMarc Zyngier 		return put_user(reg, uaddr);
4589ed24f4bSMarc Zyngier 	}
4599ed24f4bSMarc Zyngier 	}
4609ed24f4bSMarc Zyngier 
4619ed24f4bSMarc Zyngier 	return -ENXIO;
4629ed24f4bSMarc Zyngier }
4639ed24f4bSMarc Zyngier 
4649ed24f4bSMarc Zyngier static int vgic_v2_has_attr(struct kvm_device *dev,
4659ed24f4bSMarc Zyngier 			    struct kvm_device_attr *attr)
4669ed24f4bSMarc Zyngier {
4679ed24f4bSMarc Zyngier 	switch (attr->group) {
4689ed24f4bSMarc Zyngier 	case KVM_DEV_ARM_VGIC_GRP_ADDR:
4699ed24f4bSMarc Zyngier 		switch (attr->attr) {
4709ed24f4bSMarc Zyngier 		case KVM_VGIC_V2_ADDR_TYPE_DIST:
4719ed24f4bSMarc Zyngier 		case KVM_VGIC_V2_ADDR_TYPE_CPU:
4729ed24f4bSMarc Zyngier 			return 0;
4739ed24f4bSMarc Zyngier 		}
4749ed24f4bSMarc Zyngier 		break;
4759ed24f4bSMarc Zyngier 	case KVM_DEV_ARM_VGIC_GRP_DIST_REGS:
4769ed24f4bSMarc Zyngier 	case KVM_DEV_ARM_VGIC_GRP_CPU_REGS:
4779ed24f4bSMarc Zyngier 		return vgic_v2_has_attr_regs(dev, attr);
4789ed24f4bSMarc Zyngier 	case KVM_DEV_ARM_VGIC_GRP_NR_IRQS:
4799ed24f4bSMarc Zyngier 		return 0;
4809ed24f4bSMarc Zyngier 	case KVM_DEV_ARM_VGIC_GRP_CTRL:
4819ed24f4bSMarc Zyngier 		switch (attr->attr) {
4829ed24f4bSMarc Zyngier 		case KVM_DEV_ARM_VGIC_CTRL_INIT:
4839ed24f4bSMarc Zyngier 			return 0;
4849ed24f4bSMarc Zyngier 		}
4859ed24f4bSMarc Zyngier 	}
4869ed24f4bSMarc Zyngier 	return -ENXIO;
4879ed24f4bSMarc Zyngier }
4889ed24f4bSMarc Zyngier 
4899ed24f4bSMarc Zyngier struct kvm_device_ops kvm_arm_vgic_v2_ops = {
4909ed24f4bSMarc Zyngier 	.name = "kvm-arm-vgic-v2",
4919ed24f4bSMarc Zyngier 	.create = vgic_create,
4929ed24f4bSMarc Zyngier 	.destroy = vgic_destroy,
4939ed24f4bSMarc Zyngier 	.set_attr = vgic_v2_set_attr,
4949ed24f4bSMarc Zyngier 	.get_attr = vgic_v2_get_attr,
4959ed24f4bSMarc Zyngier 	.has_attr = vgic_v2_has_attr,
4969ed24f4bSMarc Zyngier };
4979ed24f4bSMarc Zyngier 
4989ed24f4bSMarc Zyngier int vgic_v3_parse_attr(struct kvm_device *dev, struct kvm_device_attr *attr,
4999ed24f4bSMarc Zyngier 		       struct vgic_reg_attr *reg_attr)
5009ed24f4bSMarc Zyngier {
5019ed24f4bSMarc Zyngier 	unsigned long vgic_mpidr, mpidr_reg;
5029ed24f4bSMarc Zyngier 
5039ed24f4bSMarc Zyngier 	/*
5049ed24f4bSMarc Zyngier 	 * For KVM_DEV_ARM_VGIC_GRP_DIST_REGS group,
5059ed24f4bSMarc Zyngier 	 * attr might not hold MPIDR. Hence assume vcpu0.
5069ed24f4bSMarc Zyngier 	 */
5079ed24f4bSMarc Zyngier 	if (attr->group != KVM_DEV_ARM_VGIC_GRP_DIST_REGS) {
5089ed24f4bSMarc Zyngier 		vgic_mpidr = (attr->attr & KVM_DEV_ARM_VGIC_V3_MPIDR_MASK) >>
5099ed24f4bSMarc Zyngier 			      KVM_DEV_ARM_VGIC_V3_MPIDR_SHIFT;
5109ed24f4bSMarc Zyngier 
5119ed24f4bSMarc Zyngier 		mpidr_reg = VGIC_TO_MPIDR(vgic_mpidr);
5129ed24f4bSMarc Zyngier 		reg_attr->vcpu = kvm_mpidr_to_vcpu(dev->kvm, mpidr_reg);
5139ed24f4bSMarc Zyngier 	} else {
5149ed24f4bSMarc Zyngier 		reg_attr->vcpu = kvm_get_vcpu(dev->kvm, 0);
5159ed24f4bSMarc Zyngier 	}
5169ed24f4bSMarc Zyngier 
5179ed24f4bSMarc Zyngier 	if (!reg_attr->vcpu)
5189ed24f4bSMarc Zyngier 		return -EINVAL;
5199ed24f4bSMarc Zyngier 
5209ed24f4bSMarc Zyngier 	reg_attr->addr = attr->attr & KVM_DEV_ARM_VGIC_OFFSET_MASK;
5219ed24f4bSMarc Zyngier 
5229ed24f4bSMarc Zyngier 	return 0;
5239ed24f4bSMarc Zyngier }
5249ed24f4bSMarc Zyngier 
5259ed24f4bSMarc Zyngier /*
5269ed24f4bSMarc Zyngier  * vgic_v3_attr_regs_access - allows user space to access VGIC v3 state
5279ed24f4bSMarc Zyngier  *
5289ed24f4bSMarc Zyngier  * @dev:      kvm device handle
5299ed24f4bSMarc Zyngier  * @attr:     kvm device attribute
5309ed24f4bSMarc Zyngier  * @reg:      address the value is read or written
5319ed24f4bSMarc Zyngier  * @is_write: true if userspace is writing a register
5329ed24f4bSMarc Zyngier  */
5339ed24f4bSMarc Zyngier static int vgic_v3_attr_regs_access(struct kvm_device *dev,
5349ed24f4bSMarc Zyngier 				    struct kvm_device_attr *attr,
5359ed24f4bSMarc Zyngier 				    u64 *reg, bool is_write)
5369ed24f4bSMarc Zyngier {
5379ed24f4bSMarc Zyngier 	struct vgic_reg_attr reg_attr;
5389ed24f4bSMarc Zyngier 	gpa_t addr;
5399ed24f4bSMarc Zyngier 	struct kvm_vcpu *vcpu;
5409ed24f4bSMarc Zyngier 	int ret;
5419ed24f4bSMarc Zyngier 	u32 tmp32;
5429ed24f4bSMarc Zyngier 
5439ed24f4bSMarc Zyngier 	ret = vgic_v3_parse_attr(dev, attr, &reg_attr);
5449ed24f4bSMarc Zyngier 	if (ret)
5459ed24f4bSMarc Zyngier 		return ret;
5469ed24f4bSMarc Zyngier 
5479ed24f4bSMarc Zyngier 	vcpu = reg_attr.vcpu;
5489ed24f4bSMarc Zyngier 	addr = reg_attr.addr;
5499ed24f4bSMarc Zyngier 
5509ed24f4bSMarc Zyngier 	mutex_lock(&dev->kvm->lock);
5519ed24f4bSMarc Zyngier 
5529ed24f4bSMarc Zyngier 	if (unlikely(!vgic_initialized(dev->kvm))) {
5539ed24f4bSMarc Zyngier 		ret = -EBUSY;
5549ed24f4bSMarc Zyngier 		goto out;
5559ed24f4bSMarc Zyngier 	}
5569ed24f4bSMarc Zyngier 
5579ed24f4bSMarc Zyngier 	if (!lock_all_vcpus(dev->kvm)) {
5589ed24f4bSMarc Zyngier 		ret = -EBUSY;
5599ed24f4bSMarc Zyngier 		goto out;
5609ed24f4bSMarc Zyngier 	}
5619ed24f4bSMarc Zyngier 
5629ed24f4bSMarc Zyngier 	switch (attr->group) {
5639ed24f4bSMarc Zyngier 	case KVM_DEV_ARM_VGIC_GRP_DIST_REGS:
5649ed24f4bSMarc Zyngier 		if (is_write)
5659ed24f4bSMarc Zyngier 			tmp32 = *reg;
5669ed24f4bSMarc Zyngier 
5679ed24f4bSMarc Zyngier 		ret = vgic_v3_dist_uaccess(vcpu, is_write, addr, &tmp32);
5689ed24f4bSMarc Zyngier 		if (!is_write)
5699ed24f4bSMarc Zyngier 			*reg = tmp32;
5709ed24f4bSMarc Zyngier 		break;
5719ed24f4bSMarc Zyngier 	case KVM_DEV_ARM_VGIC_GRP_REDIST_REGS:
5729ed24f4bSMarc Zyngier 		if (is_write)
5739ed24f4bSMarc Zyngier 			tmp32 = *reg;
5749ed24f4bSMarc Zyngier 
5759ed24f4bSMarc Zyngier 		ret = vgic_v3_redist_uaccess(vcpu, is_write, addr, &tmp32);
5769ed24f4bSMarc Zyngier 		if (!is_write)
5779ed24f4bSMarc Zyngier 			*reg = tmp32;
5789ed24f4bSMarc Zyngier 		break;
5799ed24f4bSMarc Zyngier 	case KVM_DEV_ARM_VGIC_GRP_CPU_SYSREGS: {
5809ed24f4bSMarc Zyngier 		u64 regid;
5819ed24f4bSMarc Zyngier 
5829ed24f4bSMarc Zyngier 		regid = (attr->attr & KVM_DEV_ARM_VGIC_SYSREG_INSTR_MASK);
5839ed24f4bSMarc Zyngier 		ret = vgic_v3_cpu_sysregs_uaccess(vcpu, is_write,
5849ed24f4bSMarc Zyngier 						  regid, reg);
5859ed24f4bSMarc Zyngier 		break;
5869ed24f4bSMarc Zyngier 	}
5879ed24f4bSMarc Zyngier 	case KVM_DEV_ARM_VGIC_GRP_LEVEL_INFO: {
5889ed24f4bSMarc Zyngier 		unsigned int info, intid;
5899ed24f4bSMarc Zyngier 
5909ed24f4bSMarc Zyngier 		info = (attr->attr & KVM_DEV_ARM_VGIC_LINE_LEVEL_INFO_MASK) >>
5919ed24f4bSMarc Zyngier 			KVM_DEV_ARM_VGIC_LINE_LEVEL_INFO_SHIFT;
5929ed24f4bSMarc Zyngier 		if (info == VGIC_LEVEL_INFO_LINE_LEVEL) {
5939ed24f4bSMarc Zyngier 			intid = attr->attr &
5949ed24f4bSMarc Zyngier 				KVM_DEV_ARM_VGIC_LINE_LEVEL_INTID_MASK;
5959ed24f4bSMarc Zyngier 			ret = vgic_v3_line_level_info_uaccess(vcpu, is_write,
5969ed24f4bSMarc Zyngier 							      intid, reg);
5979ed24f4bSMarc Zyngier 		} else {
5989ed24f4bSMarc Zyngier 			ret = -EINVAL;
5999ed24f4bSMarc Zyngier 		}
6009ed24f4bSMarc Zyngier 		break;
6019ed24f4bSMarc Zyngier 	}
6029ed24f4bSMarc Zyngier 	default:
6039ed24f4bSMarc Zyngier 		ret = -EINVAL;
6049ed24f4bSMarc Zyngier 		break;
6059ed24f4bSMarc Zyngier 	}
6069ed24f4bSMarc Zyngier 
6079ed24f4bSMarc Zyngier 	unlock_all_vcpus(dev->kvm);
6089ed24f4bSMarc Zyngier out:
6099ed24f4bSMarc Zyngier 	mutex_unlock(&dev->kvm->lock);
6109ed24f4bSMarc Zyngier 	return ret;
6119ed24f4bSMarc Zyngier }
6129ed24f4bSMarc Zyngier 
6139ed24f4bSMarc Zyngier static int vgic_v3_set_attr(struct kvm_device *dev,
6149ed24f4bSMarc Zyngier 			    struct kvm_device_attr *attr)
6159ed24f4bSMarc Zyngier {
6169ed24f4bSMarc Zyngier 	int ret;
6179ed24f4bSMarc Zyngier 
6189ed24f4bSMarc Zyngier 	ret = vgic_set_common_attr(dev, attr);
6199ed24f4bSMarc Zyngier 	if (ret != -ENXIO)
6209ed24f4bSMarc Zyngier 		return ret;
6219ed24f4bSMarc Zyngier 
6229ed24f4bSMarc Zyngier 	switch (attr->group) {
6239ed24f4bSMarc Zyngier 	case KVM_DEV_ARM_VGIC_GRP_DIST_REGS:
6249ed24f4bSMarc Zyngier 	case KVM_DEV_ARM_VGIC_GRP_REDIST_REGS: {
6259ed24f4bSMarc Zyngier 		u32 __user *uaddr = (u32 __user *)(long)attr->addr;
6269ed24f4bSMarc Zyngier 		u32 tmp32;
6279ed24f4bSMarc Zyngier 		u64 reg;
6289ed24f4bSMarc Zyngier 
6299ed24f4bSMarc Zyngier 		if (get_user(tmp32, uaddr))
6309ed24f4bSMarc Zyngier 			return -EFAULT;
6319ed24f4bSMarc Zyngier 
6329ed24f4bSMarc Zyngier 		reg = tmp32;
6339ed24f4bSMarc Zyngier 		return vgic_v3_attr_regs_access(dev, attr, &reg, true);
6349ed24f4bSMarc Zyngier 	}
6359ed24f4bSMarc Zyngier 	case KVM_DEV_ARM_VGIC_GRP_CPU_SYSREGS: {
6369ed24f4bSMarc Zyngier 		u64 __user *uaddr = (u64 __user *)(long)attr->addr;
6379ed24f4bSMarc Zyngier 		u64 reg;
6389ed24f4bSMarc Zyngier 
6399ed24f4bSMarc Zyngier 		if (get_user(reg, uaddr))
6409ed24f4bSMarc Zyngier 			return -EFAULT;
6419ed24f4bSMarc Zyngier 
6429ed24f4bSMarc Zyngier 		return vgic_v3_attr_regs_access(dev, attr, &reg, true);
6439ed24f4bSMarc Zyngier 	}
6449ed24f4bSMarc Zyngier 	case KVM_DEV_ARM_VGIC_GRP_LEVEL_INFO: {
6459ed24f4bSMarc Zyngier 		u32 __user *uaddr = (u32 __user *)(long)attr->addr;
6469ed24f4bSMarc Zyngier 		u64 reg;
6479ed24f4bSMarc Zyngier 		u32 tmp32;
6489ed24f4bSMarc Zyngier 
6499ed24f4bSMarc Zyngier 		if (get_user(tmp32, uaddr))
6509ed24f4bSMarc Zyngier 			return -EFAULT;
6519ed24f4bSMarc Zyngier 
6529ed24f4bSMarc Zyngier 		reg = tmp32;
6539ed24f4bSMarc Zyngier 		return vgic_v3_attr_regs_access(dev, attr, &reg, true);
6549ed24f4bSMarc Zyngier 	}
6559ed24f4bSMarc Zyngier 	case KVM_DEV_ARM_VGIC_GRP_CTRL: {
6569ed24f4bSMarc Zyngier 		int ret;
6579ed24f4bSMarc Zyngier 
6589ed24f4bSMarc Zyngier 		switch (attr->attr) {
6599ed24f4bSMarc Zyngier 		case KVM_DEV_ARM_VGIC_SAVE_PENDING_TABLES:
6609ed24f4bSMarc Zyngier 			mutex_lock(&dev->kvm->lock);
6619ed24f4bSMarc Zyngier 
6629ed24f4bSMarc Zyngier 			if (!lock_all_vcpus(dev->kvm)) {
6639ed24f4bSMarc Zyngier 				mutex_unlock(&dev->kvm->lock);
6649ed24f4bSMarc Zyngier 				return -EBUSY;
6659ed24f4bSMarc Zyngier 			}
6669ed24f4bSMarc Zyngier 			ret = vgic_v3_save_pending_tables(dev->kvm);
6679ed24f4bSMarc Zyngier 			unlock_all_vcpus(dev->kvm);
6689ed24f4bSMarc Zyngier 			mutex_unlock(&dev->kvm->lock);
6699ed24f4bSMarc Zyngier 			return ret;
6709ed24f4bSMarc Zyngier 		}
6719ed24f4bSMarc Zyngier 		break;
6729ed24f4bSMarc Zyngier 	}
6739ed24f4bSMarc Zyngier 	}
6749ed24f4bSMarc Zyngier 	return -ENXIO;
6759ed24f4bSMarc Zyngier }
6769ed24f4bSMarc Zyngier 
6779ed24f4bSMarc Zyngier static int vgic_v3_get_attr(struct kvm_device *dev,
6789ed24f4bSMarc Zyngier 			    struct kvm_device_attr *attr)
6799ed24f4bSMarc Zyngier {
6809ed24f4bSMarc Zyngier 	int ret;
6819ed24f4bSMarc Zyngier 
6829ed24f4bSMarc Zyngier 	ret = vgic_get_common_attr(dev, attr);
6839ed24f4bSMarc Zyngier 	if (ret != -ENXIO)
6849ed24f4bSMarc Zyngier 		return ret;
6859ed24f4bSMarc Zyngier 
6869ed24f4bSMarc Zyngier 	switch (attr->group) {
6879ed24f4bSMarc Zyngier 	case KVM_DEV_ARM_VGIC_GRP_DIST_REGS:
6889ed24f4bSMarc Zyngier 	case KVM_DEV_ARM_VGIC_GRP_REDIST_REGS: {
6899ed24f4bSMarc Zyngier 		u32 __user *uaddr = (u32 __user *)(long)attr->addr;
6909ed24f4bSMarc Zyngier 		u64 reg;
6919ed24f4bSMarc Zyngier 		u32 tmp32;
6929ed24f4bSMarc Zyngier 
6939ed24f4bSMarc Zyngier 		ret = vgic_v3_attr_regs_access(dev, attr, &reg, false);
6949ed24f4bSMarc Zyngier 		if (ret)
6959ed24f4bSMarc Zyngier 			return ret;
6969ed24f4bSMarc Zyngier 		tmp32 = reg;
6979ed24f4bSMarc Zyngier 		return put_user(tmp32, uaddr);
6989ed24f4bSMarc Zyngier 	}
6999ed24f4bSMarc Zyngier 	case KVM_DEV_ARM_VGIC_GRP_CPU_SYSREGS: {
7009ed24f4bSMarc Zyngier 		u64 __user *uaddr = (u64 __user *)(long)attr->addr;
7019ed24f4bSMarc Zyngier 		u64 reg;
7029ed24f4bSMarc Zyngier 
7039ed24f4bSMarc Zyngier 		ret = vgic_v3_attr_regs_access(dev, attr, &reg, false);
7049ed24f4bSMarc Zyngier 		if (ret)
7059ed24f4bSMarc Zyngier 			return ret;
7069ed24f4bSMarc Zyngier 		return put_user(reg, uaddr);
7079ed24f4bSMarc Zyngier 	}
7089ed24f4bSMarc Zyngier 	case KVM_DEV_ARM_VGIC_GRP_LEVEL_INFO: {
7099ed24f4bSMarc Zyngier 		u32 __user *uaddr = (u32 __user *)(long)attr->addr;
7109ed24f4bSMarc Zyngier 		u64 reg;
7119ed24f4bSMarc Zyngier 		u32 tmp32;
7129ed24f4bSMarc Zyngier 
7139ed24f4bSMarc Zyngier 		ret = vgic_v3_attr_regs_access(dev, attr, &reg, false);
7149ed24f4bSMarc Zyngier 		if (ret)
7159ed24f4bSMarc Zyngier 			return ret;
7169ed24f4bSMarc Zyngier 		tmp32 = reg;
7179ed24f4bSMarc Zyngier 		return put_user(tmp32, uaddr);
7189ed24f4bSMarc Zyngier 	}
7199ed24f4bSMarc Zyngier 	}
7209ed24f4bSMarc Zyngier 	return -ENXIO;
7219ed24f4bSMarc Zyngier }
7229ed24f4bSMarc Zyngier 
7239ed24f4bSMarc Zyngier static int vgic_v3_has_attr(struct kvm_device *dev,
7249ed24f4bSMarc Zyngier 			    struct kvm_device_attr *attr)
7259ed24f4bSMarc Zyngier {
7269ed24f4bSMarc Zyngier 	switch (attr->group) {
7279ed24f4bSMarc Zyngier 	case KVM_DEV_ARM_VGIC_GRP_ADDR:
7289ed24f4bSMarc Zyngier 		switch (attr->attr) {
7299ed24f4bSMarc Zyngier 		case KVM_VGIC_V3_ADDR_TYPE_DIST:
7309ed24f4bSMarc Zyngier 		case KVM_VGIC_V3_ADDR_TYPE_REDIST:
7319ed24f4bSMarc Zyngier 		case KVM_VGIC_V3_ADDR_TYPE_REDIST_REGION:
7329ed24f4bSMarc Zyngier 			return 0;
7339ed24f4bSMarc Zyngier 		}
7349ed24f4bSMarc Zyngier 		break;
7359ed24f4bSMarc Zyngier 	case KVM_DEV_ARM_VGIC_GRP_DIST_REGS:
7369ed24f4bSMarc Zyngier 	case KVM_DEV_ARM_VGIC_GRP_REDIST_REGS:
7379ed24f4bSMarc Zyngier 	case KVM_DEV_ARM_VGIC_GRP_CPU_SYSREGS:
7389ed24f4bSMarc Zyngier 		return vgic_v3_has_attr_regs(dev, attr);
7399ed24f4bSMarc Zyngier 	case KVM_DEV_ARM_VGIC_GRP_NR_IRQS:
7409ed24f4bSMarc Zyngier 		return 0;
7419ed24f4bSMarc Zyngier 	case KVM_DEV_ARM_VGIC_GRP_LEVEL_INFO: {
7429ed24f4bSMarc Zyngier 		if (((attr->attr & KVM_DEV_ARM_VGIC_LINE_LEVEL_INFO_MASK) >>
7439ed24f4bSMarc Zyngier 		      KVM_DEV_ARM_VGIC_LINE_LEVEL_INFO_SHIFT) ==
7449ed24f4bSMarc Zyngier 		      VGIC_LEVEL_INFO_LINE_LEVEL)
7459ed24f4bSMarc Zyngier 			return 0;
7469ed24f4bSMarc Zyngier 		break;
7479ed24f4bSMarc Zyngier 	}
7489ed24f4bSMarc Zyngier 	case KVM_DEV_ARM_VGIC_GRP_CTRL:
7499ed24f4bSMarc Zyngier 		switch (attr->attr) {
7509ed24f4bSMarc Zyngier 		case KVM_DEV_ARM_VGIC_CTRL_INIT:
7519ed24f4bSMarc Zyngier 			return 0;
7529ed24f4bSMarc Zyngier 		case KVM_DEV_ARM_VGIC_SAVE_PENDING_TABLES:
7539ed24f4bSMarc Zyngier 			return 0;
7549ed24f4bSMarc Zyngier 		}
7559ed24f4bSMarc Zyngier 	}
7569ed24f4bSMarc Zyngier 	return -ENXIO;
7579ed24f4bSMarc Zyngier }
7589ed24f4bSMarc Zyngier 
7599ed24f4bSMarc Zyngier struct kvm_device_ops kvm_arm_vgic_v3_ops = {
7609ed24f4bSMarc Zyngier 	.name = "kvm-arm-vgic-v3",
7619ed24f4bSMarc Zyngier 	.create = vgic_create,
7629ed24f4bSMarc Zyngier 	.destroy = vgic_destroy,
7639ed24f4bSMarc Zyngier 	.set_attr = vgic_v3_set_attr,
7649ed24f4bSMarc Zyngier 	.get_attr = vgic_v3_get_attr,
7659ed24f4bSMarc Zyngier 	.has_attr = vgic_v3_has_attr,
7669ed24f4bSMarc Zyngier };
767