18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * VGIC: KVM DEVICE API 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2015 ARM Ltd. 68c2ecf20Sopenharmony_ci * Author: Marc Zyngier <marc.zyngier@arm.com> 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci#include <linux/kvm_host.h> 98c2ecf20Sopenharmony_ci#include <kvm/arm_vgic.h> 108c2ecf20Sopenharmony_ci#include <linux/uaccess.h> 118c2ecf20Sopenharmony_ci#include <asm/kvm_mmu.h> 128c2ecf20Sopenharmony_ci#include <asm/cputype.h> 138c2ecf20Sopenharmony_ci#include "vgic.h" 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_ci/* common helpers */ 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ciint vgic_check_ioaddr(struct kvm *kvm, phys_addr_t *ioaddr, 188c2ecf20Sopenharmony_ci phys_addr_t addr, phys_addr_t alignment) 198c2ecf20Sopenharmony_ci{ 208c2ecf20Sopenharmony_ci if (addr & ~kvm_phys_mask(kvm)) 218c2ecf20Sopenharmony_ci return -E2BIG; 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci if (!IS_ALIGNED(addr, alignment)) 248c2ecf20Sopenharmony_ci return -EINVAL; 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci if (!IS_VGIC_ADDR_UNDEF(*ioaddr)) 278c2ecf20Sopenharmony_ci return -EEXIST; 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci return 0; 308c2ecf20Sopenharmony_ci} 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_cistatic int vgic_check_type(struct kvm *kvm, int type_needed) 338c2ecf20Sopenharmony_ci{ 348c2ecf20Sopenharmony_ci if (kvm->arch.vgic.vgic_model != type_needed) 358c2ecf20Sopenharmony_ci return -ENODEV; 368c2ecf20Sopenharmony_ci else 378c2ecf20Sopenharmony_ci return 0; 388c2ecf20Sopenharmony_ci} 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci/** 418c2ecf20Sopenharmony_ci * kvm_vgic_addr - set or get vgic VM base addresses 428c2ecf20Sopenharmony_ci * @kvm: pointer to the vm struct 438c2ecf20Sopenharmony_ci * @type: the VGIC addr type, one of KVM_VGIC_V[23]_ADDR_TYPE_XXX 448c2ecf20Sopenharmony_ci * @addr: pointer to address value 458c2ecf20Sopenharmony_ci * @write: if true set the address in the VM address space, if false read the 468c2ecf20Sopenharmony_ci * address 478c2ecf20Sopenharmony_ci * 488c2ecf20Sopenharmony_ci * Set or get the vgic base addresses for the distributor and the virtual CPU 498c2ecf20Sopenharmony_ci * interface in the VM physical address space. These addresses are properties 508c2ecf20Sopenharmony_ci * of the emulated core/SoC and therefore user space initially knows this 518c2ecf20Sopenharmony_ci * information. 528c2ecf20Sopenharmony_ci * Check them for sanity (alignment, double assignment). We can't check for 538c2ecf20Sopenharmony_ci * overlapping regions in case of a virtual GICv3 here, since we don't know 548c2ecf20Sopenharmony_ci * the number of VCPUs yet, so we defer this check to map_resources(). 558c2ecf20Sopenharmony_ci */ 568c2ecf20Sopenharmony_ciint kvm_vgic_addr(struct kvm *kvm, unsigned long type, u64 *addr, bool write) 578c2ecf20Sopenharmony_ci{ 588c2ecf20Sopenharmony_ci int r = 0; 598c2ecf20Sopenharmony_ci struct vgic_dist *vgic = &kvm->arch.vgic; 608c2ecf20Sopenharmony_ci phys_addr_t *addr_ptr, alignment; 618c2ecf20Sopenharmony_ci u64 undef_value = VGIC_ADDR_UNDEF; 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci mutex_lock(&kvm->lock); 648c2ecf20Sopenharmony_ci switch (type) { 658c2ecf20Sopenharmony_ci case KVM_VGIC_V2_ADDR_TYPE_DIST: 668c2ecf20Sopenharmony_ci r = vgic_check_type(kvm, KVM_DEV_TYPE_ARM_VGIC_V2); 678c2ecf20Sopenharmony_ci addr_ptr = &vgic->vgic_dist_base; 688c2ecf20Sopenharmony_ci alignment = SZ_4K; 698c2ecf20Sopenharmony_ci break; 708c2ecf20Sopenharmony_ci case KVM_VGIC_V2_ADDR_TYPE_CPU: 718c2ecf20Sopenharmony_ci r = vgic_check_type(kvm, KVM_DEV_TYPE_ARM_VGIC_V2); 728c2ecf20Sopenharmony_ci addr_ptr = &vgic->vgic_cpu_base; 738c2ecf20Sopenharmony_ci alignment = SZ_4K; 748c2ecf20Sopenharmony_ci break; 758c2ecf20Sopenharmony_ci case KVM_VGIC_V3_ADDR_TYPE_DIST: 768c2ecf20Sopenharmony_ci r = vgic_check_type(kvm, KVM_DEV_TYPE_ARM_VGIC_V3); 778c2ecf20Sopenharmony_ci addr_ptr = &vgic->vgic_dist_base; 788c2ecf20Sopenharmony_ci alignment = SZ_64K; 798c2ecf20Sopenharmony_ci break; 808c2ecf20Sopenharmony_ci case KVM_VGIC_V3_ADDR_TYPE_REDIST: { 818c2ecf20Sopenharmony_ci struct vgic_redist_region *rdreg; 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci r = vgic_check_type(kvm, KVM_DEV_TYPE_ARM_VGIC_V3); 848c2ecf20Sopenharmony_ci if (r) 858c2ecf20Sopenharmony_ci break; 868c2ecf20Sopenharmony_ci if (write) { 878c2ecf20Sopenharmony_ci r = vgic_v3_set_redist_base(kvm, 0, *addr, 0); 888c2ecf20Sopenharmony_ci goto out; 898c2ecf20Sopenharmony_ci } 908c2ecf20Sopenharmony_ci rdreg = list_first_entry_or_null(&vgic->rd_regions, 918c2ecf20Sopenharmony_ci struct vgic_redist_region, list); 928c2ecf20Sopenharmony_ci if (!rdreg) 938c2ecf20Sopenharmony_ci addr_ptr = &undef_value; 948c2ecf20Sopenharmony_ci else 958c2ecf20Sopenharmony_ci addr_ptr = &rdreg->base; 968c2ecf20Sopenharmony_ci break; 978c2ecf20Sopenharmony_ci } 988c2ecf20Sopenharmony_ci case KVM_VGIC_V3_ADDR_TYPE_REDIST_REGION: 998c2ecf20Sopenharmony_ci { 1008c2ecf20Sopenharmony_ci struct vgic_redist_region *rdreg; 1018c2ecf20Sopenharmony_ci u8 index; 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci r = vgic_check_type(kvm, KVM_DEV_TYPE_ARM_VGIC_V3); 1048c2ecf20Sopenharmony_ci if (r) 1058c2ecf20Sopenharmony_ci break; 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci index = *addr & KVM_VGIC_V3_RDIST_INDEX_MASK; 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci if (write) { 1108c2ecf20Sopenharmony_ci gpa_t base = *addr & KVM_VGIC_V3_RDIST_BASE_MASK; 1118c2ecf20Sopenharmony_ci u32 count = (*addr & KVM_VGIC_V3_RDIST_COUNT_MASK) 1128c2ecf20Sopenharmony_ci >> KVM_VGIC_V3_RDIST_COUNT_SHIFT; 1138c2ecf20Sopenharmony_ci u8 flags = (*addr & KVM_VGIC_V3_RDIST_FLAGS_MASK) 1148c2ecf20Sopenharmony_ci >> KVM_VGIC_V3_RDIST_FLAGS_SHIFT; 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci if (!count || flags) 1178c2ecf20Sopenharmony_ci r = -EINVAL; 1188c2ecf20Sopenharmony_ci else 1198c2ecf20Sopenharmony_ci r = vgic_v3_set_redist_base(kvm, index, 1208c2ecf20Sopenharmony_ci base, count); 1218c2ecf20Sopenharmony_ci goto out; 1228c2ecf20Sopenharmony_ci } 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci rdreg = vgic_v3_rdist_region_from_index(kvm, index); 1258c2ecf20Sopenharmony_ci if (!rdreg) { 1268c2ecf20Sopenharmony_ci r = -ENOENT; 1278c2ecf20Sopenharmony_ci goto out; 1288c2ecf20Sopenharmony_ci } 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci *addr = index; 1318c2ecf20Sopenharmony_ci *addr |= rdreg->base; 1328c2ecf20Sopenharmony_ci *addr |= (u64)rdreg->count << KVM_VGIC_V3_RDIST_COUNT_SHIFT; 1338c2ecf20Sopenharmony_ci goto out; 1348c2ecf20Sopenharmony_ci } 1358c2ecf20Sopenharmony_ci default: 1368c2ecf20Sopenharmony_ci r = -ENODEV; 1378c2ecf20Sopenharmony_ci } 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci if (r) 1408c2ecf20Sopenharmony_ci goto out; 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci if (write) { 1438c2ecf20Sopenharmony_ci r = vgic_check_ioaddr(kvm, addr_ptr, *addr, alignment); 1448c2ecf20Sopenharmony_ci if (!r) 1458c2ecf20Sopenharmony_ci *addr_ptr = *addr; 1468c2ecf20Sopenharmony_ci } else { 1478c2ecf20Sopenharmony_ci *addr = *addr_ptr; 1488c2ecf20Sopenharmony_ci } 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ciout: 1518c2ecf20Sopenharmony_ci mutex_unlock(&kvm->lock); 1528c2ecf20Sopenharmony_ci return r; 1538c2ecf20Sopenharmony_ci} 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_cistatic int vgic_set_common_attr(struct kvm_device *dev, 1568c2ecf20Sopenharmony_ci struct kvm_device_attr *attr) 1578c2ecf20Sopenharmony_ci{ 1588c2ecf20Sopenharmony_ci int r; 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci switch (attr->group) { 1618c2ecf20Sopenharmony_ci case KVM_DEV_ARM_VGIC_GRP_ADDR: { 1628c2ecf20Sopenharmony_ci u64 __user *uaddr = (u64 __user *)(long)attr->addr; 1638c2ecf20Sopenharmony_ci u64 addr; 1648c2ecf20Sopenharmony_ci unsigned long type = (unsigned long)attr->attr; 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci if (copy_from_user(&addr, uaddr, sizeof(addr))) 1678c2ecf20Sopenharmony_ci return -EFAULT; 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci r = kvm_vgic_addr(dev->kvm, type, &addr, true); 1708c2ecf20Sopenharmony_ci return (r == -ENODEV) ? -ENXIO : r; 1718c2ecf20Sopenharmony_ci } 1728c2ecf20Sopenharmony_ci case KVM_DEV_ARM_VGIC_GRP_NR_IRQS: { 1738c2ecf20Sopenharmony_ci u32 __user *uaddr = (u32 __user *)(long)attr->addr; 1748c2ecf20Sopenharmony_ci u32 val; 1758c2ecf20Sopenharmony_ci int ret = 0; 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci if (get_user(val, uaddr)) 1788c2ecf20Sopenharmony_ci return -EFAULT; 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci /* 1818c2ecf20Sopenharmony_ci * We require: 1828c2ecf20Sopenharmony_ci * - at least 32 SPIs on top of the 16 SGIs and 16 PPIs 1838c2ecf20Sopenharmony_ci * - at most 1024 interrupts 1848c2ecf20Sopenharmony_ci * - a multiple of 32 interrupts 1858c2ecf20Sopenharmony_ci */ 1868c2ecf20Sopenharmony_ci if (val < (VGIC_NR_PRIVATE_IRQS + 32) || 1878c2ecf20Sopenharmony_ci val > VGIC_MAX_RESERVED || 1888c2ecf20Sopenharmony_ci (val & 31)) 1898c2ecf20Sopenharmony_ci return -EINVAL; 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci mutex_lock(&dev->kvm->lock); 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci if (vgic_ready(dev->kvm) || dev->kvm->arch.vgic.nr_spis) 1948c2ecf20Sopenharmony_ci ret = -EBUSY; 1958c2ecf20Sopenharmony_ci else 1968c2ecf20Sopenharmony_ci dev->kvm->arch.vgic.nr_spis = 1978c2ecf20Sopenharmony_ci val - VGIC_NR_PRIVATE_IRQS; 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci mutex_unlock(&dev->kvm->lock); 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci return ret; 2028c2ecf20Sopenharmony_ci } 2038c2ecf20Sopenharmony_ci case KVM_DEV_ARM_VGIC_GRP_CTRL: { 2048c2ecf20Sopenharmony_ci switch (attr->attr) { 2058c2ecf20Sopenharmony_ci case KVM_DEV_ARM_VGIC_CTRL_INIT: 2068c2ecf20Sopenharmony_ci mutex_lock(&dev->kvm->lock); 2078c2ecf20Sopenharmony_ci r = vgic_init(dev->kvm); 2088c2ecf20Sopenharmony_ci mutex_unlock(&dev->kvm->lock); 2098c2ecf20Sopenharmony_ci return r; 2108c2ecf20Sopenharmony_ci } 2118c2ecf20Sopenharmony_ci break; 2128c2ecf20Sopenharmony_ci } 2138c2ecf20Sopenharmony_ci } 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ci return -ENXIO; 2168c2ecf20Sopenharmony_ci} 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_cistatic int vgic_get_common_attr(struct kvm_device *dev, 2198c2ecf20Sopenharmony_ci struct kvm_device_attr *attr) 2208c2ecf20Sopenharmony_ci{ 2218c2ecf20Sopenharmony_ci int r = -ENXIO; 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci switch (attr->group) { 2248c2ecf20Sopenharmony_ci case KVM_DEV_ARM_VGIC_GRP_ADDR: { 2258c2ecf20Sopenharmony_ci u64 __user *uaddr = (u64 __user *)(long)attr->addr; 2268c2ecf20Sopenharmony_ci u64 addr; 2278c2ecf20Sopenharmony_ci unsigned long type = (unsigned long)attr->attr; 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci if (copy_from_user(&addr, uaddr, sizeof(addr))) 2308c2ecf20Sopenharmony_ci return -EFAULT; 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci r = kvm_vgic_addr(dev->kvm, type, &addr, false); 2338c2ecf20Sopenharmony_ci if (r) 2348c2ecf20Sopenharmony_ci return (r == -ENODEV) ? -ENXIO : r; 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci if (copy_to_user(uaddr, &addr, sizeof(addr))) 2378c2ecf20Sopenharmony_ci return -EFAULT; 2388c2ecf20Sopenharmony_ci break; 2398c2ecf20Sopenharmony_ci } 2408c2ecf20Sopenharmony_ci case KVM_DEV_ARM_VGIC_GRP_NR_IRQS: { 2418c2ecf20Sopenharmony_ci u32 __user *uaddr = (u32 __user *)(long)attr->addr; 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci r = put_user(dev->kvm->arch.vgic.nr_spis + 2448c2ecf20Sopenharmony_ci VGIC_NR_PRIVATE_IRQS, uaddr); 2458c2ecf20Sopenharmony_ci break; 2468c2ecf20Sopenharmony_ci } 2478c2ecf20Sopenharmony_ci } 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci return r; 2508c2ecf20Sopenharmony_ci} 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_cistatic int vgic_create(struct kvm_device *dev, u32 type) 2538c2ecf20Sopenharmony_ci{ 2548c2ecf20Sopenharmony_ci return kvm_vgic_create(dev->kvm, type); 2558c2ecf20Sopenharmony_ci} 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_cistatic void vgic_destroy(struct kvm_device *dev) 2588c2ecf20Sopenharmony_ci{ 2598c2ecf20Sopenharmony_ci kfree(dev); 2608c2ecf20Sopenharmony_ci} 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ciint kvm_register_vgic_device(unsigned long type) 2638c2ecf20Sopenharmony_ci{ 2648c2ecf20Sopenharmony_ci int ret = -ENODEV; 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci switch (type) { 2678c2ecf20Sopenharmony_ci case KVM_DEV_TYPE_ARM_VGIC_V2: 2688c2ecf20Sopenharmony_ci ret = kvm_register_device_ops(&kvm_arm_vgic_v2_ops, 2698c2ecf20Sopenharmony_ci KVM_DEV_TYPE_ARM_VGIC_V2); 2708c2ecf20Sopenharmony_ci break; 2718c2ecf20Sopenharmony_ci case KVM_DEV_TYPE_ARM_VGIC_V3: 2728c2ecf20Sopenharmony_ci ret = kvm_register_device_ops(&kvm_arm_vgic_v3_ops, 2738c2ecf20Sopenharmony_ci KVM_DEV_TYPE_ARM_VGIC_V3); 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_ci if (ret) 2768c2ecf20Sopenharmony_ci break; 2778c2ecf20Sopenharmony_ci ret = kvm_vgic_register_its_device(); 2788c2ecf20Sopenharmony_ci break; 2798c2ecf20Sopenharmony_ci } 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci return ret; 2828c2ecf20Sopenharmony_ci} 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ciint vgic_v2_parse_attr(struct kvm_device *dev, struct kvm_device_attr *attr, 2858c2ecf20Sopenharmony_ci struct vgic_reg_attr *reg_attr) 2868c2ecf20Sopenharmony_ci{ 2878c2ecf20Sopenharmony_ci int cpuid; 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_ci cpuid = (attr->attr & KVM_DEV_ARM_VGIC_CPUID_MASK) >> 2908c2ecf20Sopenharmony_ci KVM_DEV_ARM_VGIC_CPUID_SHIFT; 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ci if (cpuid >= atomic_read(&dev->kvm->online_vcpus)) 2938c2ecf20Sopenharmony_ci return -EINVAL; 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_ci reg_attr->vcpu = kvm_get_vcpu(dev->kvm, cpuid); 2968c2ecf20Sopenharmony_ci reg_attr->addr = attr->attr & KVM_DEV_ARM_VGIC_OFFSET_MASK; 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_ci return 0; 2998c2ecf20Sopenharmony_ci} 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci/* unlocks vcpus from @vcpu_lock_idx and smaller */ 3028c2ecf20Sopenharmony_cistatic void unlock_vcpus(struct kvm *kvm, int vcpu_lock_idx) 3038c2ecf20Sopenharmony_ci{ 3048c2ecf20Sopenharmony_ci struct kvm_vcpu *tmp_vcpu; 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_ci for (; vcpu_lock_idx >= 0; vcpu_lock_idx--) { 3078c2ecf20Sopenharmony_ci tmp_vcpu = kvm_get_vcpu(kvm, vcpu_lock_idx); 3088c2ecf20Sopenharmony_ci mutex_unlock(&tmp_vcpu->mutex); 3098c2ecf20Sopenharmony_ci } 3108c2ecf20Sopenharmony_ci} 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_civoid unlock_all_vcpus(struct kvm *kvm) 3138c2ecf20Sopenharmony_ci{ 3148c2ecf20Sopenharmony_ci unlock_vcpus(kvm, atomic_read(&kvm->online_vcpus) - 1); 3158c2ecf20Sopenharmony_ci} 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci/* Returns true if all vcpus were locked, false otherwise */ 3188c2ecf20Sopenharmony_cibool lock_all_vcpus(struct kvm *kvm) 3198c2ecf20Sopenharmony_ci{ 3208c2ecf20Sopenharmony_ci struct kvm_vcpu *tmp_vcpu; 3218c2ecf20Sopenharmony_ci int c; 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_ci /* 3248c2ecf20Sopenharmony_ci * Any time a vcpu is run, vcpu_load is called which tries to grab the 3258c2ecf20Sopenharmony_ci * vcpu->mutex. By grabbing the vcpu->mutex of all VCPUs we ensure 3268c2ecf20Sopenharmony_ci * that no other VCPUs are run and fiddle with the vgic state while we 3278c2ecf20Sopenharmony_ci * access it. 3288c2ecf20Sopenharmony_ci */ 3298c2ecf20Sopenharmony_ci kvm_for_each_vcpu(c, tmp_vcpu, kvm) { 3308c2ecf20Sopenharmony_ci if (!mutex_trylock(&tmp_vcpu->mutex)) { 3318c2ecf20Sopenharmony_ci unlock_vcpus(kvm, c - 1); 3328c2ecf20Sopenharmony_ci return false; 3338c2ecf20Sopenharmony_ci } 3348c2ecf20Sopenharmony_ci } 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_ci return true; 3378c2ecf20Sopenharmony_ci} 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_ci/** 3408c2ecf20Sopenharmony_ci * vgic_v2_attr_regs_access - allows user space to access VGIC v2 state 3418c2ecf20Sopenharmony_ci * 3428c2ecf20Sopenharmony_ci * @dev: kvm device handle 3438c2ecf20Sopenharmony_ci * @attr: kvm device attribute 3448c2ecf20Sopenharmony_ci * @reg: address the value is read or written 3458c2ecf20Sopenharmony_ci * @is_write: true if userspace is writing a register 3468c2ecf20Sopenharmony_ci */ 3478c2ecf20Sopenharmony_cistatic int vgic_v2_attr_regs_access(struct kvm_device *dev, 3488c2ecf20Sopenharmony_ci struct kvm_device_attr *attr, 3498c2ecf20Sopenharmony_ci u32 *reg, bool is_write) 3508c2ecf20Sopenharmony_ci{ 3518c2ecf20Sopenharmony_ci struct vgic_reg_attr reg_attr; 3528c2ecf20Sopenharmony_ci gpa_t addr; 3538c2ecf20Sopenharmony_ci struct kvm_vcpu *vcpu; 3548c2ecf20Sopenharmony_ci int ret; 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_ci ret = vgic_v2_parse_attr(dev, attr, ®_attr); 3578c2ecf20Sopenharmony_ci if (ret) 3588c2ecf20Sopenharmony_ci return ret; 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_ci vcpu = reg_attr.vcpu; 3618c2ecf20Sopenharmony_ci addr = reg_attr.addr; 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_ci mutex_lock(&dev->kvm->lock); 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_ci ret = vgic_init(dev->kvm); 3668c2ecf20Sopenharmony_ci if (ret) 3678c2ecf20Sopenharmony_ci goto out; 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_ci if (!lock_all_vcpus(dev->kvm)) { 3708c2ecf20Sopenharmony_ci ret = -EBUSY; 3718c2ecf20Sopenharmony_ci goto out; 3728c2ecf20Sopenharmony_ci } 3738c2ecf20Sopenharmony_ci 3748c2ecf20Sopenharmony_ci switch (attr->group) { 3758c2ecf20Sopenharmony_ci case KVM_DEV_ARM_VGIC_GRP_CPU_REGS: 3768c2ecf20Sopenharmony_ci ret = vgic_v2_cpuif_uaccess(vcpu, is_write, addr, reg); 3778c2ecf20Sopenharmony_ci break; 3788c2ecf20Sopenharmony_ci case KVM_DEV_ARM_VGIC_GRP_DIST_REGS: 3798c2ecf20Sopenharmony_ci ret = vgic_v2_dist_uaccess(vcpu, is_write, addr, reg); 3808c2ecf20Sopenharmony_ci break; 3818c2ecf20Sopenharmony_ci default: 3828c2ecf20Sopenharmony_ci ret = -EINVAL; 3838c2ecf20Sopenharmony_ci break; 3848c2ecf20Sopenharmony_ci } 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_ci unlock_all_vcpus(dev->kvm); 3878c2ecf20Sopenharmony_ciout: 3888c2ecf20Sopenharmony_ci mutex_unlock(&dev->kvm->lock); 3898c2ecf20Sopenharmony_ci return ret; 3908c2ecf20Sopenharmony_ci} 3918c2ecf20Sopenharmony_ci 3928c2ecf20Sopenharmony_cistatic int vgic_v2_set_attr(struct kvm_device *dev, 3938c2ecf20Sopenharmony_ci struct kvm_device_attr *attr) 3948c2ecf20Sopenharmony_ci{ 3958c2ecf20Sopenharmony_ci int ret; 3968c2ecf20Sopenharmony_ci 3978c2ecf20Sopenharmony_ci ret = vgic_set_common_attr(dev, attr); 3988c2ecf20Sopenharmony_ci if (ret != -ENXIO) 3998c2ecf20Sopenharmony_ci return ret; 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_ci switch (attr->group) { 4028c2ecf20Sopenharmony_ci case KVM_DEV_ARM_VGIC_GRP_DIST_REGS: 4038c2ecf20Sopenharmony_ci case KVM_DEV_ARM_VGIC_GRP_CPU_REGS: { 4048c2ecf20Sopenharmony_ci u32 __user *uaddr = (u32 __user *)(long)attr->addr; 4058c2ecf20Sopenharmony_ci u32 reg; 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_ci if (get_user(reg, uaddr)) 4088c2ecf20Sopenharmony_ci return -EFAULT; 4098c2ecf20Sopenharmony_ci 4108c2ecf20Sopenharmony_ci return vgic_v2_attr_regs_access(dev, attr, ®, true); 4118c2ecf20Sopenharmony_ci } 4128c2ecf20Sopenharmony_ci } 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_ci return -ENXIO; 4158c2ecf20Sopenharmony_ci} 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_cistatic int vgic_v2_get_attr(struct kvm_device *dev, 4188c2ecf20Sopenharmony_ci struct kvm_device_attr *attr) 4198c2ecf20Sopenharmony_ci{ 4208c2ecf20Sopenharmony_ci int ret; 4218c2ecf20Sopenharmony_ci 4228c2ecf20Sopenharmony_ci ret = vgic_get_common_attr(dev, attr); 4238c2ecf20Sopenharmony_ci if (ret != -ENXIO) 4248c2ecf20Sopenharmony_ci return ret; 4258c2ecf20Sopenharmony_ci 4268c2ecf20Sopenharmony_ci switch (attr->group) { 4278c2ecf20Sopenharmony_ci case KVM_DEV_ARM_VGIC_GRP_DIST_REGS: 4288c2ecf20Sopenharmony_ci case KVM_DEV_ARM_VGIC_GRP_CPU_REGS: { 4298c2ecf20Sopenharmony_ci u32 __user *uaddr = (u32 __user *)(long)attr->addr; 4308c2ecf20Sopenharmony_ci u32 reg = 0; 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_ci ret = vgic_v2_attr_regs_access(dev, attr, ®, false); 4338c2ecf20Sopenharmony_ci if (ret) 4348c2ecf20Sopenharmony_ci return ret; 4358c2ecf20Sopenharmony_ci return put_user(reg, uaddr); 4368c2ecf20Sopenharmony_ci } 4378c2ecf20Sopenharmony_ci } 4388c2ecf20Sopenharmony_ci 4398c2ecf20Sopenharmony_ci return -ENXIO; 4408c2ecf20Sopenharmony_ci} 4418c2ecf20Sopenharmony_ci 4428c2ecf20Sopenharmony_cistatic int vgic_v2_has_attr(struct kvm_device *dev, 4438c2ecf20Sopenharmony_ci struct kvm_device_attr *attr) 4448c2ecf20Sopenharmony_ci{ 4458c2ecf20Sopenharmony_ci switch (attr->group) { 4468c2ecf20Sopenharmony_ci case KVM_DEV_ARM_VGIC_GRP_ADDR: 4478c2ecf20Sopenharmony_ci switch (attr->attr) { 4488c2ecf20Sopenharmony_ci case KVM_VGIC_V2_ADDR_TYPE_DIST: 4498c2ecf20Sopenharmony_ci case KVM_VGIC_V2_ADDR_TYPE_CPU: 4508c2ecf20Sopenharmony_ci return 0; 4518c2ecf20Sopenharmony_ci } 4528c2ecf20Sopenharmony_ci break; 4538c2ecf20Sopenharmony_ci case KVM_DEV_ARM_VGIC_GRP_DIST_REGS: 4548c2ecf20Sopenharmony_ci case KVM_DEV_ARM_VGIC_GRP_CPU_REGS: 4558c2ecf20Sopenharmony_ci return vgic_v2_has_attr_regs(dev, attr); 4568c2ecf20Sopenharmony_ci case KVM_DEV_ARM_VGIC_GRP_NR_IRQS: 4578c2ecf20Sopenharmony_ci return 0; 4588c2ecf20Sopenharmony_ci case KVM_DEV_ARM_VGIC_GRP_CTRL: 4598c2ecf20Sopenharmony_ci switch (attr->attr) { 4608c2ecf20Sopenharmony_ci case KVM_DEV_ARM_VGIC_CTRL_INIT: 4618c2ecf20Sopenharmony_ci return 0; 4628c2ecf20Sopenharmony_ci } 4638c2ecf20Sopenharmony_ci } 4648c2ecf20Sopenharmony_ci return -ENXIO; 4658c2ecf20Sopenharmony_ci} 4668c2ecf20Sopenharmony_ci 4678c2ecf20Sopenharmony_cistruct kvm_device_ops kvm_arm_vgic_v2_ops = { 4688c2ecf20Sopenharmony_ci .name = "kvm-arm-vgic-v2", 4698c2ecf20Sopenharmony_ci .create = vgic_create, 4708c2ecf20Sopenharmony_ci .destroy = vgic_destroy, 4718c2ecf20Sopenharmony_ci .set_attr = vgic_v2_set_attr, 4728c2ecf20Sopenharmony_ci .get_attr = vgic_v2_get_attr, 4738c2ecf20Sopenharmony_ci .has_attr = vgic_v2_has_attr, 4748c2ecf20Sopenharmony_ci}; 4758c2ecf20Sopenharmony_ci 4768c2ecf20Sopenharmony_ciint vgic_v3_parse_attr(struct kvm_device *dev, struct kvm_device_attr *attr, 4778c2ecf20Sopenharmony_ci struct vgic_reg_attr *reg_attr) 4788c2ecf20Sopenharmony_ci{ 4798c2ecf20Sopenharmony_ci unsigned long vgic_mpidr, mpidr_reg; 4808c2ecf20Sopenharmony_ci 4818c2ecf20Sopenharmony_ci /* 4828c2ecf20Sopenharmony_ci * For KVM_DEV_ARM_VGIC_GRP_DIST_REGS group, 4838c2ecf20Sopenharmony_ci * attr might not hold MPIDR. Hence assume vcpu0. 4848c2ecf20Sopenharmony_ci */ 4858c2ecf20Sopenharmony_ci if (attr->group != KVM_DEV_ARM_VGIC_GRP_DIST_REGS) { 4868c2ecf20Sopenharmony_ci vgic_mpidr = (attr->attr & KVM_DEV_ARM_VGIC_V3_MPIDR_MASK) >> 4878c2ecf20Sopenharmony_ci KVM_DEV_ARM_VGIC_V3_MPIDR_SHIFT; 4888c2ecf20Sopenharmony_ci 4898c2ecf20Sopenharmony_ci mpidr_reg = VGIC_TO_MPIDR(vgic_mpidr); 4908c2ecf20Sopenharmony_ci reg_attr->vcpu = kvm_mpidr_to_vcpu(dev->kvm, mpidr_reg); 4918c2ecf20Sopenharmony_ci } else { 4928c2ecf20Sopenharmony_ci reg_attr->vcpu = kvm_get_vcpu(dev->kvm, 0); 4938c2ecf20Sopenharmony_ci } 4948c2ecf20Sopenharmony_ci 4958c2ecf20Sopenharmony_ci if (!reg_attr->vcpu) 4968c2ecf20Sopenharmony_ci return -EINVAL; 4978c2ecf20Sopenharmony_ci 4988c2ecf20Sopenharmony_ci reg_attr->addr = attr->attr & KVM_DEV_ARM_VGIC_OFFSET_MASK; 4998c2ecf20Sopenharmony_ci 5008c2ecf20Sopenharmony_ci return 0; 5018c2ecf20Sopenharmony_ci} 5028c2ecf20Sopenharmony_ci 5038c2ecf20Sopenharmony_ci/* 5048c2ecf20Sopenharmony_ci * vgic_v3_attr_regs_access - allows user space to access VGIC v3 state 5058c2ecf20Sopenharmony_ci * 5068c2ecf20Sopenharmony_ci * @dev: kvm device handle 5078c2ecf20Sopenharmony_ci * @attr: kvm device attribute 5088c2ecf20Sopenharmony_ci * @reg: address the value is read or written 5098c2ecf20Sopenharmony_ci * @is_write: true if userspace is writing a register 5108c2ecf20Sopenharmony_ci */ 5118c2ecf20Sopenharmony_cistatic int vgic_v3_attr_regs_access(struct kvm_device *dev, 5128c2ecf20Sopenharmony_ci struct kvm_device_attr *attr, 5138c2ecf20Sopenharmony_ci u64 *reg, bool is_write) 5148c2ecf20Sopenharmony_ci{ 5158c2ecf20Sopenharmony_ci struct vgic_reg_attr reg_attr; 5168c2ecf20Sopenharmony_ci gpa_t addr; 5178c2ecf20Sopenharmony_ci struct kvm_vcpu *vcpu; 5188c2ecf20Sopenharmony_ci int ret; 5198c2ecf20Sopenharmony_ci u32 tmp32; 5208c2ecf20Sopenharmony_ci 5218c2ecf20Sopenharmony_ci ret = vgic_v3_parse_attr(dev, attr, ®_attr); 5228c2ecf20Sopenharmony_ci if (ret) 5238c2ecf20Sopenharmony_ci return ret; 5248c2ecf20Sopenharmony_ci 5258c2ecf20Sopenharmony_ci vcpu = reg_attr.vcpu; 5268c2ecf20Sopenharmony_ci addr = reg_attr.addr; 5278c2ecf20Sopenharmony_ci 5288c2ecf20Sopenharmony_ci mutex_lock(&dev->kvm->lock); 5298c2ecf20Sopenharmony_ci 5308c2ecf20Sopenharmony_ci if (unlikely(!vgic_initialized(dev->kvm))) { 5318c2ecf20Sopenharmony_ci ret = -EBUSY; 5328c2ecf20Sopenharmony_ci goto out; 5338c2ecf20Sopenharmony_ci } 5348c2ecf20Sopenharmony_ci 5358c2ecf20Sopenharmony_ci if (!lock_all_vcpus(dev->kvm)) { 5368c2ecf20Sopenharmony_ci ret = -EBUSY; 5378c2ecf20Sopenharmony_ci goto out; 5388c2ecf20Sopenharmony_ci } 5398c2ecf20Sopenharmony_ci 5408c2ecf20Sopenharmony_ci switch (attr->group) { 5418c2ecf20Sopenharmony_ci case KVM_DEV_ARM_VGIC_GRP_DIST_REGS: 5428c2ecf20Sopenharmony_ci if (is_write) 5438c2ecf20Sopenharmony_ci tmp32 = *reg; 5448c2ecf20Sopenharmony_ci 5458c2ecf20Sopenharmony_ci ret = vgic_v3_dist_uaccess(vcpu, is_write, addr, &tmp32); 5468c2ecf20Sopenharmony_ci if (!is_write) 5478c2ecf20Sopenharmony_ci *reg = tmp32; 5488c2ecf20Sopenharmony_ci break; 5498c2ecf20Sopenharmony_ci case KVM_DEV_ARM_VGIC_GRP_REDIST_REGS: 5508c2ecf20Sopenharmony_ci if (is_write) 5518c2ecf20Sopenharmony_ci tmp32 = *reg; 5528c2ecf20Sopenharmony_ci 5538c2ecf20Sopenharmony_ci ret = vgic_v3_redist_uaccess(vcpu, is_write, addr, &tmp32); 5548c2ecf20Sopenharmony_ci if (!is_write) 5558c2ecf20Sopenharmony_ci *reg = tmp32; 5568c2ecf20Sopenharmony_ci break; 5578c2ecf20Sopenharmony_ci case KVM_DEV_ARM_VGIC_GRP_CPU_SYSREGS: { 5588c2ecf20Sopenharmony_ci u64 regid; 5598c2ecf20Sopenharmony_ci 5608c2ecf20Sopenharmony_ci regid = (attr->attr & KVM_DEV_ARM_VGIC_SYSREG_INSTR_MASK); 5618c2ecf20Sopenharmony_ci ret = vgic_v3_cpu_sysregs_uaccess(vcpu, is_write, 5628c2ecf20Sopenharmony_ci regid, reg); 5638c2ecf20Sopenharmony_ci break; 5648c2ecf20Sopenharmony_ci } 5658c2ecf20Sopenharmony_ci case KVM_DEV_ARM_VGIC_GRP_LEVEL_INFO: { 5668c2ecf20Sopenharmony_ci unsigned int info, intid; 5678c2ecf20Sopenharmony_ci 5688c2ecf20Sopenharmony_ci info = (attr->attr & KVM_DEV_ARM_VGIC_LINE_LEVEL_INFO_MASK) >> 5698c2ecf20Sopenharmony_ci KVM_DEV_ARM_VGIC_LINE_LEVEL_INFO_SHIFT; 5708c2ecf20Sopenharmony_ci if (info == VGIC_LEVEL_INFO_LINE_LEVEL) { 5718c2ecf20Sopenharmony_ci intid = attr->attr & 5728c2ecf20Sopenharmony_ci KVM_DEV_ARM_VGIC_LINE_LEVEL_INTID_MASK; 5738c2ecf20Sopenharmony_ci ret = vgic_v3_line_level_info_uaccess(vcpu, is_write, 5748c2ecf20Sopenharmony_ci intid, reg); 5758c2ecf20Sopenharmony_ci } else { 5768c2ecf20Sopenharmony_ci ret = -EINVAL; 5778c2ecf20Sopenharmony_ci } 5788c2ecf20Sopenharmony_ci break; 5798c2ecf20Sopenharmony_ci } 5808c2ecf20Sopenharmony_ci default: 5818c2ecf20Sopenharmony_ci ret = -EINVAL; 5828c2ecf20Sopenharmony_ci break; 5838c2ecf20Sopenharmony_ci } 5848c2ecf20Sopenharmony_ci 5858c2ecf20Sopenharmony_ci unlock_all_vcpus(dev->kvm); 5868c2ecf20Sopenharmony_ciout: 5878c2ecf20Sopenharmony_ci mutex_unlock(&dev->kvm->lock); 5888c2ecf20Sopenharmony_ci return ret; 5898c2ecf20Sopenharmony_ci} 5908c2ecf20Sopenharmony_ci 5918c2ecf20Sopenharmony_cistatic int vgic_v3_set_attr(struct kvm_device *dev, 5928c2ecf20Sopenharmony_ci struct kvm_device_attr *attr) 5938c2ecf20Sopenharmony_ci{ 5948c2ecf20Sopenharmony_ci int ret; 5958c2ecf20Sopenharmony_ci 5968c2ecf20Sopenharmony_ci ret = vgic_set_common_attr(dev, attr); 5978c2ecf20Sopenharmony_ci if (ret != -ENXIO) 5988c2ecf20Sopenharmony_ci return ret; 5998c2ecf20Sopenharmony_ci 6008c2ecf20Sopenharmony_ci switch (attr->group) { 6018c2ecf20Sopenharmony_ci case KVM_DEV_ARM_VGIC_GRP_DIST_REGS: 6028c2ecf20Sopenharmony_ci case KVM_DEV_ARM_VGIC_GRP_REDIST_REGS: { 6038c2ecf20Sopenharmony_ci u32 __user *uaddr = (u32 __user *)(long)attr->addr; 6048c2ecf20Sopenharmony_ci u32 tmp32; 6058c2ecf20Sopenharmony_ci u64 reg; 6068c2ecf20Sopenharmony_ci 6078c2ecf20Sopenharmony_ci if (get_user(tmp32, uaddr)) 6088c2ecf20Sopenharmony_ci return -EFAULT; 6098c2ecf20Sopenharmony_ci 6108c2ecf20Sopenharmony_ci reg = tmp32; 6118c2ecf20Sopenharmony_ci return vgic_v3_attr_regs_access(dev, attr, ®, true); 6128c2ecf20Sopenharmony_ci } 6138c2ecf20Sopenharmony_ci case KVM_DEV_ARM_VGIC_GRP_CPU_SYSREGS: { 6148c2ecf20Sopenharmony_ci u64 __user *uaddr = (u64 __user *)(long)attr->addr; 6158c2ecf20Sopenharmony_ci u64 reg; 6168c2ecf20Sopenharmony_ci 6178c2ecf20Sopenharmony_ci if (get_user(reg, uaddr)) 6188c2ecf20Sopenharmony_ci return -EFAULT; 6198c2ecf20Sopenharmony_ci 6208c2ecf20Sopenharmony_ci return vgic_v3_attr_regs_access(dev, attr, ®, true); 6218c2ecf20Sopenharmony_ci } 6228c2ecf20Sopenharmony_ci case KVM_DEV_ARM_VGIC_GRP_LEVEL_INFO: { 6238c2ecf20Sopenharmony_ci u32 __user *uaddr = (u32 __user *)(long)attr->addr; 6248c2ecf20Sopenharmony_ci u64 reg; 6258c2ecf20Sopenharmony_ci u32 tmp32; 6268c2ecf20Sopenharmony_ci 6278c2ecf20Sopenharmony_ci if (get_user(tmp32, uaddr)) 6288c2ecf20Sopenharmony_ci return -EFAULT; 6298c2ecf20Sopenharmony_ci 6308c2ecf20Sopenharmony_ci reg = tmp32; 6318c2ecf20Sopenharmony_ci return vgic_v3_attr_regs_access(dev, attr, ®, true); 6328c2ecf20Sopenharmony_ci } 6338c2ecf20Sopenharmony_ci case KVM_DEV_ARM_VGIC_GRP_CTRL: { 6348c2ecf20Sopenharmony_ci int ret; 6358c2ecf20Sopenharmony_ci 6368c2ecf20Sopenharmony_ci switch (attr->attr) { 6378c2ecf20Sopenharmony_ci case KVM_DEV_ARM_VGIC_SAVE_PENDING_TABLES: 6388c2ecf20Sopenharmony_ci mutex_lock(&dev->kvm->lock); 6398c2ecf20Sopenharmony_ci 6408c2ecf20Sopenharmony_ci if (!lock_all_vcpus(dev->kvm)) { 6418c2ecf20Sopenharmony_ci mutex_unlock(&dev->kvm->lock); 6428c2ecf20Sopenharmony_ci return -EBUSY; 6438c2ecf20Sopenharmony_ci } 6448c2ecf20Sopenharmony_ci ret = vgic_v3_save_pending_tables(dev->kvm); 6458c2ecf20Sopenharmony_ci unlock_all_vcpus(dev->kvm); 6468c2ecf20Sopenharmony_ci mutex_unlock(&dev->kvm->lock); 6478c2ecf20Sopenharmony_ci return ret; 6488c2ecf20Sopenharmony_ci } 6498c2ecf20Sopenharmony_ci break; 6508c2ecf20Sopenharmony_ci } 6518c2ecf20Sopenharmony_ci } 6528c2ecf20Sopenharmony_ci return -ENXIO; 6538c2ecf20Sopenharmony_ci} 6548c2ecf20Sopenharmony_ci 6558c2ecf20Sopenharmony_cistatic int vgic_v3_get_attr(struct kvm_device *dev, 6568c2ecf20Sopenharmony_ci struct kvm_device_attr *attr) 6578c2ecf20Sopenharmony_ci{ 6588c2ecf20Sopenharmony_ci int ret; 6598c2ecf20Sopenharmony_ci 6608c2ecf20Sopenharmony_ci ret = vgic_get_common_attr(dev, attr); 6618c2ecf20Sopenharmony_ci if (ret != -ENXIO) 6628c2ecf20Sopenharmony_ci return ret; 6638c2ecf20Sopenharmony_ci 6648c2ecf20Sopenharmony_ci switch (attr->group) { 6658c2ecf20Sopenharmony_ci case KVM_DEV_ARM_VGIC_GRP_DIST_REGS: 6668c2ecf20Sopenharmony_ci case KVM_DEV_ARM_VGIC_GRP_REDIST_REGS: { 6678c2ecf20Sopenharmony_ci u32 __user *uaddr = (u32 __user *)(long)attr->addr; 6688c2ecf20Sopenharmony_ci u64 reg; 6698c2ecf20Sopenharmony_ci u32 tmp32; 6708c2ecf20Sopenharmony_ci 6718c2ecf20Sopenharmony_ci ret = vgic_v3_attr_regs_access(dev, attr, ®, false); 6728c2ecf20Sopenharmony_ci if (ret) 6738c2ecf20Sopenharmony_ci return ret; 6748c2ecf20Sopenharmony_ci tmp32 = reg; 6758c2ecf20Sopenharmony_ci return put_user(tmp32, uaddr); 6768c2ecf20Sopenharmony_ci } 6778c2ecf20Sopenharmony_ci case KVM_DEV_ARM_VGIC_GRP_CPU_SYSREGS: { 6788c2ecf20Sopenharmony_ci u64 __user *uaddr = (u64 __user *)(long)attr->addr; 6798c2ecf20Sopenharmony_ci u64 reg; 6808c2ecf20Sopenharmony_ci 6818c2ecf20Sopenharmony_ci ret = vgic_v3_attr_regs_access(dev, attr, ®, false); 6828c2ecf20Sopenharmony_ci if (ret) 6838c2ecf20Sopenharmony_ci return ret; 6848c2ecf20Sopenharmony_ci return put_user(reg, uaddr); 6858c2ecf20Sopenharmony_ci } 6868c2ecf20Sopenharmony_ci case KVM_DEV_ARM_VGIC_GRP_LEVEL_INFO: { 6878c2ecf20Sopenharmony_ci u32 __user *uaddr = (u32 __user *)(long)attr->addr; 6888c2ecf20Sopenharmony_ci u64 reg; 6898c2ecf20Sopenharmony_ci u32 tmp32; 6908c2ecf20Sopenharmony_ci 6918c2ecf20Sopenharmony_ci ret = vgic_v3_attr_regs_access(dev, attr, ®, false); 6928c2ecf20Sopenharmony_ci if (ret) 6938c2ecf20Sopenharmony_ci return ret; 6948c2ecf20Sopenharmony_ci tmp32 = reg; 6958c2ecf20Sopenharmony_ci return put_user(tmp32, uaddr); 6968c2ecf20Sopenharmony_ci } 6978c2ecf20Sopenharmony_ci } 6988c2ecf20Sopenharmony_ci return -ENXIO; 6998c2ecf20Sopenharmony_ci} 7008c2ecf20Sopenharmony_ci 7018c2ecf20Sopenharmony_cistatic int vgic_v3_has_attr(struct kvm_device *dev, 7028c2ecf20Sopenharmony_ci struct kvm_device_attr *attr) 7038c2ecf20Sopenharmony_ci{ 7048c2ecf20Sopenharmony_ci switch (attr->group) { 7058c2ecf20Sopenharmony_ci case KVM_DEV_ARM_VGIC_GRP_ADDR: 7068c2ecf20Sopenharmony_ci switch (attr->attr) { 7078c2ecf20Sopenharmony_ci case KVM_VGIC_V3_ADDR_TYPE_DIST: 7088c2ecf20Sopenharmony_ci case KVM_VGIC_V3_ADDR_TYPE_REDIST: 7098c2ecf20Sopenharmony_ci case KVM_VGIC_V3_ADDR_TYPE_REDIST_REGION: 7108c2ecf20Sopenharmony_ci return 0; 7118c2ecf20Sopenharmony_ci } 7128c2ecf20Sopenharmony_ci break; 7138c2ecf20Sopenharmony_ci case KVM_DEV_ARM_VGIC_GRP_DIST_REGS: 7148c2ecf20Sopenharmony_ci case KVM_DEV_ARM_VGIC_GRP_REDIST_REGS: 7158c2ecf20Sopenharmony_ci case KVM_DEV_ARM_VGIC_GRP_CPU_SYSREGS: 7168c2ecf20Sopenharmony_ci return vgic_v3_has_attr_regs(dev, attr); 7178c2ecf20Sopenharmony_ci case KVM_DEV_ARM_VGIC_GRP_NR_IRQS: 7188c2ecf20Sopenharmony_ci return 0; 7198c2ecf20Sopenharmony_ci case KVM_DEV_ARM_VGIC_GRP_LEVEL_INFO: { 7208c2ecf20Sopenharmony_ci if (((attr->attr & KVM_DEV_ARM_VGIC_LINE_LEVEL_INFO_MASK) >> 7218c2ecf20Sopenharmony_ci KVM_DEV_ARM_VGIC_LINE_LEVEL_INFO_SHIFT) == 7228c2ecf20Sopenharmony_ci VGIC_LEVEL_INFO_LINE_LEVEL) 7238c2ecf20Sopenharmony_ci return 0; 7248c2ecf20Sopenharmony_ci break; 7258c2ecf20Sopenharmony_ci } 7268c2ecf20Sopenharmony_ci case KVM_DEV_ARM_VGIC_GRP_CTRL: 7278c2ecf20Sopenharmony_ci switch (attr->attr) { 7288c2ecf20Sopenharmony_ci case KVM_DEV_ARM_VGIC_CTRL_INIT: 7298c2ecf20Sopenharmony_ci return 0; 7308c2ecf20Sopenharmony_ci case KVM_DEV_ARM_VGIC_SAVE_PENDING_TABLES: 7318c2ecf20Sopenharmony_ci return 0; 7328c2ecf20Sopenharmony_ci } 7338c2ecf20Sopenharmony_ci } 7348c2ecf20Sopenharmony_ci return -ENXIO; 7358c2ecf20Sopenharmony_ci} 7368c2ecf20Sopenharmony_ci 7378c2ecf20Sopenharmony_cistruct kvm_device_ops kvm_arm_vgic_v3_ops = { 7388c2ecf20Sopenharmony_ci .name = "kvm-arm-vgic-v3", 7398c2ecf20Sopenharmony_ci .create = vgic_create, 7408c2ecf20Sopenharmony_ci .destroy = vgic_destroy, 7418c2ecf20Sopenharmony_ci .set_attr = vgic_v3_set_attr, 7428c2ecf20Sopenharmony_ci .get_attr = vgic_v3_get_attr, 7438c2ecf20Sopenharmony_ci .has_attr = vgic_v3_has_attr, 7448c2ecf20Sopenharmony_ci}; 745