162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * VGIC: KVM DEVICE API 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2015 ARM Ltd. 662306a36Sopenharmony_ci * Author: Marc Zyngier <marc.zyngier@arm.com> 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci#include <linux/kvm_host.h> 962306a36Sopenharmony_ci#include <kvm/arm_vgic.h> 1062306a36Sopenharmony_ci#include <linux/uaccess.h> 1162306a36Sopenharmony_ci#include <asm/kvm_mmu.h> 1262306a36Sopenharmony_ci#include <asm/cputype.h> 1362306a36Sopenharmony_ci#include "vgic.h" 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci/* common helpers */ 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ciint vgic_check_iorange(struct kvm *kvm, phys_addr_t ioaddr, 1862306a36Sopenharmony_ci phys_addr_t addr, phys_addr_t alignment, 1962306a36Sopenharmony_ci phys_addr_t size) 2062306a36Sopenharmony_ci{ 2162306a36Sopenharmony_ci if (!IS_VGIC_ADDR_UNDEF(ioaddr)) 2262306a36Sopenharmony_ci return -EEXIST; 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci if (!IS_ALIGNED(addr, alignment) || !IS_ALIGNED(size, alignment)) 2562306a36Sopenharmony_ci return -EINVAL; 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci if (addr + size < addr) 2862306a36Sopenharmony_ci return -EINVAL; 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci if (addr & ~kvm_phys_mask(kvm) || addr + size > kvm_phys_size(kvm)) 3162306a36Sopenharmony_ci return -E2BIG; 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci return 0; 3462306a36Sopenharmony_ci} 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_cistatic int vgic_check_type(struct kvm *kvm, int type_needed) 3762306a36Sopenharmony_ci{ 3862306a36Sopenharmony_ci if (kvm->arch.vgic.vgic_model != type_needed) 3962306a36Sopenharmony_ci return -ENODEV; 4062306a36Sopenharmony_ci else 4162306a36Sopenharmony_ci return 0; 4262306a36Sopenharmony_ci} 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ciint kvm_set_legacy_vgic_v2_addr(struct kvm *kvm, struct kvm_arm_device_addr *dev_addr) 4562306a36Sopenharmony_ci{ 4662306a36Sopenharmony_ci struct vgic_dist *vgic = &kvm->arch.vgic; 4762306a36Sopenharmony_ci int r; 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci mutex_lock(&kvm->arch.config_lock); 5062306a36Sopenharmony_ci switch (FIELD_GET(KVM_ARM_DEVICE_TYPE_MASK, dev_addr->id)) { 5162306a36Sopenharmony_ci case KVM_VGIC_V2_ADDR_TYPE_DIST: 5262306a36Sopenharmony_ci r = vgic_check_type(kvm, KVM_DEV_TYPE_ARM_VGIC_V2); 5362306a36Sopenharmony_ci if (!r) 5462306a36Sopenharmony_ci r = vgic_check_iorange(kvm, vgic->vgic_dist_base, dev_addr->addr, 5562306a36Sopenharmony_ci SZ_4K, KVM_VGIC_V2_DIST_SIZE); 5662306a36Sopenharmony_ci if (!r) 5762306a36Sopenharmony_ci vgic->vgic_dist_base = dev_addr->addr; 5862306a36Sopenharmony_ci break; 5962306a36Sopenharmony_ci case KVM_VGIC_V2_ADDR_TYPE_CPU: 6062306a36Sopenharmony_ci r = vgic_check_type(kvm, KVM_DEV_TYPE_ARM_VGIC_V2); 6162306a36Sopenharmony_ci if (!r) 6262306a36Sopenharmony_ci r = vgic_check_iorange(kvm, vgic->vgic_cpu_base, dev_addr->addr, 6362306a36Sopenharmony_ci SZ_4K, KVM_VGIC_V2_CPU_SIZE); 6462306a36Sopenharmony_ci if (!r) 6562306a36Sopenharmony_ci vgic->vgic_cpu_base = dev_addr->addr; 6662306a36Sopenharmony_ci break; 6762306a36Sopenharmony_ci default: 6862306a36Sopenharmony_ci r = -ENODEV; 6962306a36Sopenharmony_ci } 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci mutex_unlock(&kvm->arch.config_lock); 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci return r; 7462306a36Sopenharmony_ci} 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci/** 7762306a36Sopenharmony_ci * kvm_vgic_addr - set or get vgic VM base addresses 7862306a36Sopenharmony_ci * @kvm: pointer to the vm struct 7962306a36Sopenharmony_ci * @attr: pointer to the attribute being retrieved/updated 8062306a36Sopenharmony_ci * @write: if true set the address in the VM address space, if false read the 8162306a36Sopenharmony_ci * address 8262306a36Sopenharmony_ci * 8362306a36Sopenharmony_ci * Set or get the vgic base addresses for the distributor and the virtual CPU 8462306a36Sopenharmony_ci * interface in the VM physical address space. These addresses are properties 8562306a36Sopenharmony_ci * of the emulated core/SoC and therefore user space initially knows this 8662306a36Sopenharmony_ci * information. 8762306a36Sopenharmony_ci * Check them for sanity (alignment, double assignment). We can't check for 8862306a36Sopenharmony_ci * overlapping regions in case of a virtual GICv3 here, since we don't know 8962306a36Sopenharmony_ci * the number of VCPUs yet, so we defer this check to map_resources(). 9062306a36Sopenharmony_ci */ 9162306a36Sopenharmony_cistatic int kvm_vgic_addr(struct kvm *kvm, struct kvm_device_attr *attr, bool write) 9262306a36Sopenharmony_ci{ 9362306a36Sopenharmony_ci u64 __user *uaddr = (u64 __user *)attr->addr; 9462306a36Sopenharmony_ci struct vgic_dist *vgic = &kvm->arch.vgic; 9562306a36Sopenharmony_ci phys_addr_t *addr_ptr, alignment, size; 9662306a36Sopenharmony_ci u64 undef_value = VGIC_ADDR_UNDEF; 9762306a36Sopenharmony_ci u64 addr; 9862306a36Sopenharmony_ci int r; 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci /* Reading a redistributor region addr implies getting the index */ 10162306a36Sopenharmony_ci if (write || attr->attr == KVM_VGIC_V3_ADDR_TYPE_REDIST_REGION) 10262306a36Sopenharmony_ci if (get_user(addr, uaddr)) 10362306a36Sopenharmony_ci return -EFAULT; 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci /* 10662306a36Sopenharmony_ci * Since we can't hold config_lock while registering the redistributor 10762306a36Sopenharmony_ci * iodevs, take the slots_lock immediately. 10862306a36Sopenharmony_ci */ 10962306a36Sopenharmony_ci mutex_lock(&kvm->slots_lock); 11062306a36Sopenharmony_ci switch (attr->attr) { 11162306a36Sopenharmony_ci case KVM_VGIC_V2_ADDR_TYPE_DIST: 11262306a36Sopenharmony_ci r = vgic_check_type(kvm, KVM_DEV_TYPE_ARM_VGIC_V2); 11362306a36Sopenharmony_ci addr_ptr = &vgic->vgic_dist_base; 11462306a36Sopenharmony_ci alignment = SZ_4K; 11562306a36Sopenharmony_ci size = KVM_VGIC_V2_DIST_SIZE; 11662306a36Sopenharmony_ci break; 11762306a36Sopenharmony_ci case KVM_VGIC_V2_ADDR_TYPE_CPU: 11862306a36Sopenharmony_ci r = vgic_check_type(kvm, KVM_DEV_TYPE_ARM_VGIC_V2); 11962306a36Sopenharmony_ci addr_ptr = &vgic->vgic_cpu_base; 12062306a36Sopenharmony_ci alignment = SZ_4K; 12162306a36Sopenharmony_ci size = KVM_VGIC_V2_CPU_SIZE; 12262306a36Sopenharmony_ci break; 12362306a36Sopenharmony_ci case KVM_VGIC_V3_ADDR_TYPE_DIST: 12462306a36Sopenharmony_ci r = vgic_check_type(kvm, KVM_DEV_TYPE_ARM_VGIC_V3); 12562306a36Sopenharmony_ci addr_ptr = &vgic->vgic_dist_base; 12662306a36Sopenharmony_ci alignment = SZ_64K; 12762306a36Sopenharmony_ci size = KVM_VGIC_V3_DIST_SIZE; 12862306a36Sopenharmony_ci break; 12962306a36Sopenharmony_ci case KVM_VGIC_V3_ADDR_TYPE_REDIST: { 13062306a36Sopenharmony_ci struct vgic_redist_region *rdreg; 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci r = vgic_check_type(kvm, KVM_DEV_TYPE_ARM_VGIC_V3); 13362306a36Sopenharmony_ci if (r) 13462306a36Sopenharmony_ci break; 13562306a36Sopenharmony_ci if (write) { 13662306a36Sopenharmony_ci r = vgic_v3_set_redist_base(kvm, 0, addr, 0); 13762306a36Sopenharmony_ci goto out; 13862306a36Sopenharmony_ci } 13962306a36Sopenharmony_ci rdreg = list_first_entry_or_null(&vgic->rd_regions, 14062306a36Sopenharmony_ci struct vgic_redist_region, list); 14162306a36Sopenharmony_ci if (!rdreg) 14262306a36Sopenharmony_ci addr_ptr = &undef_value; 14362306a36Sopenharmony_ci else 14462306a36Sopenharmony_ci addr_ptr = &rdreg->base; 14562306a36Sopenharmony_ci break; 14662306a36Sopenharmony_ci } 14762306a36Sopenharmony_ci case KVM_VGIC_V3_ADDR_TYPE_REDIST_REGION: 14862306a36Sopenharmony_ci { 14962306a36Sopenharmony_ci struct vgic_redist_region *rdreg; 15062306a36Sopenharmony_ci u8 index; 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci r = vgic_check_type(kvm, KVM_DEV_TYPE_ARM_VGIC_V3); 15362306a36Sopenharmony_ci if (r) 15462306a36Sopenharmony_ci break; 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci index = addr & KVM_VGIC_V3_RDIST_INDEX_MASK; 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci if (write) { 15962306a36Sopenharmony_ci gpa_t base = addr & KVM_VGIC_V3_RDIST_BASE_MASK; 16062306a36Sopenharmony_ci u32 count = FIELD_GET(KVM_VGIC_V3_RDIST_COUNT_MASK, addr); 16162306a36Sopenharmony_ci u8 flags = FIELD_GET(KVM_VGIC_V3_RDIST_FLAGS_MASK, addr); 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci if (!count || flags) 16462306a36Sopenharmony_ci r = -EINVAL; 16562306a36Sopenharmony_ci else 16662306a36Sopenharmony_ci r = vgic_v3_set_redist_base(kvm, index, 16762306a36Sopenharmony_ci base, count); 16862306a36Sopenharmony_ci goto out; 16962306a36Sopenharmony_ci } 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci rdreg = vgic_v3_rdist_region_from_index(kvm, index); 17262306a36Sopenharmony_ci if (!rdreg) { 17362306a36Sopenharmony_ci r = -ENOENT; 17462306a36Sopenharmony_ci goto out; 17562306a36Sopenharmony_ci } 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci addr = index; 17862306a36Sopenharmony_ci addr |= rdreg->base; 17962306a36Sopenharmony_ci addr |= (u64)rdreg->count << KVM_VGIC_V3_RDIST_COUNT_SHIFT; 18062306a36Sopenharmony_ci goto out; 18162306a36Sopenharmony_ci } 18262306a36Sopenharmony_ci default: 18362306a36Sopenharmony_ci r = -ENODEV; 18462306a36Sopenharmony_ci } 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci if (r) 18762306a36Sopenharmony_ci goto out; 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci mutex_lock(&kvm->arch.config_lock); 19062306a36Sopenharmony_ci if (write) { 19162306a36Sopenharmony_ci r = vgic_check_iorange(kvm, *addr_ptr, addr, alignment, size); 19262306a36Sopenharmony_ci if (!r) 19362306a36Sopenharmony_ci *addr_ptr = addr; 19462306a36Sopenharmony_ci } else { 19562306a36Sopenharmony_ci addr = *addr_ptr; 19662306a36Sopenharmony_ci } 19762306a36Sopenharmony_ci mutex_unlock(&kvm->arch.config_lock); 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ciout: 20062306a36Sopenharmony_ci mutex_unlock(&kvm->slots_lock); 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci if (!r && !write) 20362306a36Sopenharmony_ci r = put_user(addr, uaddr); 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci return r; 20662306a36Sopenharmony_ci} 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_cistatic int vgic_set_common_attr(struct kvm_device *dev, 20962306a36Sopenharmony_ci struct kvm_device_attr *attr) 21062306a36Sopenharmony_ci{ 21162306a36Sopenharmony_ci int r; 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci switch (attr->group) { 21462306a36Sopenharmony_ci case KVM_DEV_ARM_VGIC_GRP_ADDR: 21562306a36Sopenharmony_ci r = kvm_vgic_addr(dev->kvm, attr, true); 21662306a36Sopenharmony_ci return (r == -ENODEV) ? -ENXIO : r; 21762306a36Sopenharmony_ci case KVM_DEV_ARM_VGIC_GRP_NR_IRQS: { 21862306a36Sopenharmony_ci u32 __user *uaddr = (u32 __user *)(long)attr->addr; 21962306a36Sopenharmony_ci u32 val; 22062306a36Sopenharmony_ci int ret = 0; 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci if (get_user(val, uaddr)) 22362306a36Sopenharmony_ci return -EFAULT; 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci /* 22662306a36Sopenharmony_ci * We require: 22762306a36Sopenharmony_ci * - at least 32 SPIs on top of the 16 SGIs and 16 PPIs 22862306a36Sopenharmony_ci * - at most 1024 interrupts 22962306a36Sopenharmony_ci * - a multiple of 32 interrupts 23062306a36Sopenharmony_ci */ 23162306a36Sopenharmony_ci if (val < (VGIC_NR_PRIVATE_IRQS + 32) || 23262306a36Sopenharmony_ci val > VGIC_MAX_RESERVED || 23362306a36Sopenharmony_ci (val & 31)) 23462306a36Sopenharmony_ci return -EINVAL; 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci mutex_lock(&dev->kvm->arch.config_lock); 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci if (vgic_ready(dev->kvm) || dev->kvm->arch.vgic.nr_spis) 23962306a36Sopenharmony_ci ret = -EBUSY; 24062306a36Sopenharmony_ci else 24162306a36Sopenharmony_ci dev->kvm->arch.vgic.nr_spis = 24262306a36Sopenharmony_ci val - VGIC_NR_PRIVATE_IRQS; 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci mutex_unlock(&dev->kvm->arch.config_lock); 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci return ret; 24762306a36Sopenharmony_ci } 24862306a36Sopenharmony_ci case KVM_DEV_ARM_VGIC_GRP_CTRL: { 24962306a36Sopenharmony_ci switch (attr->attr) { 25062306a36Sopenharmony_ci case KVM_DEV_ARM_VGIC_CTRL_INIT: 25162306a36Sopenharmony_ci mutex_lock(&dev->kvm->arch.config_lock); 25262306a36Sopenharmony_ci r = vgic_init(dev->kvm); 25362306a36Sopenharmony_ci mutex_unlock(&dev->kvm->arch.config_lock); 25462306a36Sopenharmony_ci return r; 25562306a36Sopenharmony_ci case KVM_DEV_ARM_VGIC_SAVE_PENDING_TABLES: 25662306a36Sopenharmony_ci /* 25762306a36Sopenharmony_ci * OK, this one isn't common at all, but we 25862306a36Sopenharmony_ci * want to handle all control group attributes 25962306a36Sopenharmony_ci * in a single place. 26062306a36Sopenharmony_ci */ 26162306a36Sopenharmony_ci if (vgic_check_type(dev->kvm, KVM_DEV_TYPE_ARM_VGIC_V3)) 26262306a36Sopenharmony_ci return -ENXIO; 26362306a36Sopenharmony_ci mutex_lock(&dev->kvm->lock); 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci if (!lock_all_vcpus(dev->kvm)) { 26662306a36Sopenharmony_ci mutex_unlock(&dev->kvm->lock); 26762306a36Sopenharmony_ci return -EBUSY; 26862306a36Sopenharmony_ci } 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci mutex_lock(&dev->kvm->arch.config_lock); 27162306a36Sopenharmony_ci r = vgic_v3_save_pending_tables(dev->kvm); 27262306a36Sopenharmony_ci mutex_unlock(&dev->kvm->arch.config_lock); 27362306a36Sopenharmony_ci unlock_all_vcpus(dev->kvm); 27462306a36Sopenharmony_ci mutex_unlock(&dev->kvm->lock); 27562306a36Sopenharmony_ci return r; 27662306a36Sopenharmony_ci } 27762306a36Sopenharmony_ci break; 27862306a36Sopenharmony_ci } 27962306a36Sopenharmony_ci } 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci return -ENXIO; 28262306a36Sopenharmony_ci} 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_cistatic int vgic_get_common_attr(struct kvm_device *dev, 28562306a36Sopenharmony_ci struct kvm_device_attr *attr) 28662306a36Sopenharmony_ci{ 28762306a36Sopenharmony_ci int r = -ENXIO; 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_ci switch (attr->group) { 29062306a36Sopenharmony_ci case KVM_DEV_ARM_VGIC_GRP_ADDR: 29162306a36Sopenharmony_ci r = kvm_vgic_addr(dev->kvm, attr, false); 29262306a36Sopenharmony_ci return (r == -ENODEV) ? -ENXIO : r; 29362306a36Sopenharmony_ci case KVM_DEV_ARM_VGIC_GRP_NR_IRQS: { 29462306a36Sopenharmony_ci u32 __user *uaddr = (u32 __user *)(long)attr->addr; 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ci r = put_user(dev->kvm->arch.vgic.nr_spis + 29762306a36Sopenharmony_ci VGIC_NR_PRIVATE_IRQS, uaddr); 29862306a36Sopenharmony_ci break; 29962306a36Sopenharmony_ci } 30062306a36Sopenharmony_ci } 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ci return r; 30362306a36Sopenharmony_ci} 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_cistatic int vgic_create(struct kvm_device *dev, u32 type) 30662306a36Sopenharmony_ci{ 30762306a36Sopenharmony_ci return kvm_vgic_create(dev->kvm, type); 30862306a36Sopenharmony_ci} 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_cistatic void vgic_destroy(struct kvm_device *dev) 31162306a36Sopenharmony_ci{ 31262306a36Sopenharmony_ci kfree(dev); 31362306a36Sopenharmony_ci} 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ciint kvm_register_vgic_device(unsigned long type) 31662306a36Sopenharmony_ci{ 31762306a36Sopenharmony_ci int ret = -ENODEV; 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci switch (type) { 32062306a36Sopenharmony_ci case KVM_DEV_TYPE_ARM_VGIC_V2: 32162306a36Sopenharmony_ci ret = kvm_register_device_ops(&kvm_arm_vgic_v2_ops, 32262306a36Sopenharmony_ci KVM_DEV_TYPE_ARM_VGIC_V2); 32362306a36Sopenharmony_ci break; 32462306a36Sopenharmony_ci case KVM_DEV_TYPE_ARM_VGIC_V3: 32562306a36Sopenharmony_ci ret = kvm_register_device_ops(&kvm_arm_vgic_v3_ops, 32662306a36Sopenharmony_ci KVM_DEV_TYPE_ARM_VGIC_V3); 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_ci if (ret) 32962306a36Sopenharmony_ci break; 33062306a36Sopenharmony_ci ret = kvm_vgic_register_its_device(); 33162306a36Sopenharmony_ci break; 33262306a36Sopenharmony_ci } 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ci return ret; 33562306a36Sopenharmony_ci} 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_ciint vgic_v2_parse_attr(struct kvm_device *dev, struct kvm_device_attr *attr, 33862306a36Sopenharmony_ci struct vgic_reg_attr *reg_attr) 33962306a36Sopenharmony_ci{ 34062306a36Sopenharmony_ci int cpuid; 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_ci cpuid = (attr->attr & KVM_DEV_ARM_VGIC_CPUID_MASK) >> 34362306a36Sopenharmony_ci KVM_DEV_ARM_VGIC_CPUID_SHIFT; 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci if (cpuid >= atomic_read(&dev->kvm->online_vcpus)) 34662306a36Sopenharmony_ci return -EINVAL; 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci reg_attr->vcpu = kvm_get_vcpu(dev->kvm, cpuid); 34962306a36Sopenharmony_ci reg_attr->addr = attr->attr & KVM_DEV_ARM_VGIC_OFFSET_MASK; 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_ci return 0; 35262306a36Sopenharmony_ci} 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_ci/** 35562306a36Sopenharmony_ci * vgic_v2_attr_regs_access - allows user space to access VGIC v2 state 35662306a36Sopenharmony_ci * 35762306a36Sopenharmony_ci * @dev: kvm device handle 35862306a36Sopenharmony_ci * @attr: kvm device attribute 35962306a36Sopenharmony_ci * @is_write: true if userspace is writing a register 36062306a36Sopenharmony_ci */ 36162306a36Sopenharmony_cistatic int vgic_v2_attr_regs_access(struct kvm_device *dev, 36262306a36Sopenharmony_ci struct kvm_device_attr *attr, 36362306a36Sopenharmony_ci bool is_write) 36462306a36Sopenharmony_ci{ 36562306a36Sopenharmony_ci u32 __user *uaddr = (u32 __user *)(unsigned long)attr->addr; 36662306a36Sopenharmony_ci struct vgic_reg_attr reg_attr; 36762306a36Sopenharmony_ci gpa_t addr; 36862306a36Sopenharmony_ci struct kvm_vcpu *vcpu; 36962306a36Sopenharmony_ci int ret; 37062306a36Sopenharmony_ci u32 val; 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_ci ret = vgic_v2_parse_attr(dev, attr, ®_attr); 37362306a36Sopenharmony_ci if (ret) 37462306a36Sopenharmony_ci return ret; 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_ci vcpu = reg_attr.vcpu; 37762306a36Sopenharmony_ci addr = reg_attr.addr; 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_ci if (is_write) 38062306a36Sopenharmony_ci if (get_user(val, uaddr)) 38162306a36Sopenharmony_ci return -EFAULT; 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_ci mutex_lock(&dev->kvm->lock); 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_ci if (!lock_all_vcpus(dev->kvm)) { 38662306a36Sopenharmony_ci mutex_unlock(&dev->kvm->lock); 38762306a36Sopenharmony_ci return -EBUSY; 38862306a36Sopenharmony_ci } 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_ci mutex_lock(&dev->kvm->arch.config_lock); 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ci ret = vgic_init(dev->kvm); 39362306a36Sopenharmony_ci if (ret) 39462306a36Sopenharmony_ci goto out; 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_ci switch (attr->group) { 39762306a36Sopenharmony_ci case KVM_DEV_ARM_VGIC_GRP_CPU_REGS: 39862306a36Sopenharmony_ci ret = vgic_v2_cpuif_uaccess(vcpu, is_write, addr, &val); 39962306a36Sopenharmony_ci break; 40062306a36Sopenharmony_ci case KVM_DEV_ARM_VGIC_GRP_DIST_REGS: 40162306a36Sopenharmony_ci ret = vgic_v2_dist_uaccess(vcpu, is_write, addr, &val); 40262306a36Sopenharmony_ci break; 40362306a36Sopenharmony_ci default: 40462306a36Sopenharmony_ci ret = -EINVAL; 40562306a36Sopenharmony_ci break; 40662306a36Sopenharmony_ci } 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_ciout: 40962306a36Sopenharmony_ci mutex_unlock(&dev->kvm->arch.config_lock); 41062306a36Sopenharmony_ci unlock_all_vcpus(dev->kvm); 41162306a36Sopenharmony_ci mutex_unlock(&dev->kvm->lock); 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_ci if (!ret && !is_write) 41462306a36Sopenharmony_ci ret = put_user(val, uaddr); 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_ci return ret; 41762306a36Sopenharmony_ci} 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_cistatic int vgic_v2_set_attr(struct kvm_device *dev, 42062306a36Sopenharmony_ci struct kvm_device_attr *attr) 42162306a36Sopenharmony_ci{ 42262306a36Sopenharmony_ci switch (attr->group) { 42362306a36Sopenharmony_ci case KVM_DEV_ARM_VGIC_GRP_DIST_REGS: 42462306a36Sopenharmony_ci case KVM_DEV_ARM_VGIC_GRP_CPU_REGS: 42562306a36Sopenharmony_ci return vgic_v2_attr_regs_access(dev, attr, true); 42662306a36Sopenharmony_ci default: 42762306a36Sopenharmony_ci return vgic_set_common_attr(dev, attr); 42862306a36Sopenharmony_ci } 42962306a36Sopenharmony_ci} 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_cistatic int vgic_v2_get_attr(struct kvm_device *dev, 43262306a36Sopenharmony_ci struct kvm_device_attr *attr) 43362306a36Sopenharmony_ci{ 43462306a36Sopenharmony_ci switch (attr->group) { 43562306a36Sopenharmony_ci case KVM_DEV_ARM_VGIC_GRP_DIST_REGS: 43662306a36Sopenharmony_ci case KVM_DEV_ARM_VGIC_GRP_CPU_REGS: 43762306a36Sopenharmony_ci return vgic_v2_attr_regs_access(dev, attr, false); 43862306a36Sopenharmony_ci default: 43962306a36Sopenharmony_ci return vgic_get_common_attr(dev, attr); 44062306a36Sopenharmony_ci } 44162306a36Sopenharmony_ci} 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_cistatic int vgic_v2_has_attr(struct kvm_device *dev, 44462306a36Sopenharmony_ci struct kvm_device_attr *attr) 44562306a36Sopenharmony_ci{ 44662306a36Sopenharmony_ci switch (attr->group) { 44762306a36Sopenharmony_ci case KVM_DEV_ARM_VGIC_GRP_ADDR: 44862306a36Sopenharmony_ci switch (attr->attr) { 44962306a36Sopenharmony_ci case KVM_VGIC_V2_ADDR_TYPE_DIST: 45062306a36Sopenharmony_ci case KVM_VGIC_V2_ADDR_TYPE_CPU: 45162306a36Sopenharmony_ci return 0; 45262306a36Sopenharmony_ci } 45362306a36Sopenharmony_ci break; 45462306a36Sopenharmony_ci case KVM_DEV_ARM_VGIC_GRP_DIST_REGS: 45562306a36Sopenharmony_ci case KVM_DEV_ARM_VGIC_GRP_CPU_REGS: 45662306a36Sopenharmony_ci return vgic_v2_has_attr_regs(dev, attr); 45762306a36Sopenharmony_ci case KVM_DEV_ARM_VGIC_GRP_NR_IRQS: 45862306a36Sopenharmony_ci return 0; 45962306a36Sopenharmony_ci case KVM_DEV_ARM_VGIC_GRP_CTRL: 46062306a36Sopenharmony_ci switch (attr->attr) { 46162306a36Sopenharmony_ci case KVM_DEV_ARM_VGIC_CTRL_INIT: 46262306a36Sopenharmony_ci return 0; 46362306a36Sopenharmony_ci } 46462306a36Sopenharmony_ci } 46562306a36Sopenharmony_ci return -ENXIO; 46662306a36Sopenharmony_ci} 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_cistruct kvm_device_ops kvm_arm_vgic_v2_ops = { 46962306a36Sopenharmony_ci .name = "kvm-arm-vgic-v2", 47062306a36Sopenharmony_ci .create = vgic_create, 47162306a36Sopenharmony_ci .destroy = vgic_destroy, 47262306a36Sopenharmony_ci .set_attr = vgic_v2_set_attr, 47362306a36Sopenharmony_ci .get_attr = vgic_v2_get_attr, 47462306a36Sopenharmony_ci .has_attr = vgic_v2_has_attr, 47562306a36Sopenharmony_ci}; 47662306a36Sopenharmony_ci 47762306a36Sopenharmony_ciint vgic_v3_parse_attr(struct kvm_device *dev, struct kvm_device_attr *attr, 47862306a36Sopenharmony_ci struct vgic_reg_attr *reg_attr) 47962306a36Sopenharmony_ci{ 48062306a36Sopenharmony_ci unsigned long vgic_mpidr, mpidr_reg; 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_ci /* 48362306a36Sopenharmony_ci * For KVM_DEV_ARM_VGIC_GRP_DIST_REGS group, 48462306a36Sopenharmony_ci * attr might not hold MPIDR. Hence assume vcpu0. 48562306a36Sopenharmony_ci */ 48662306a36Sopenharmony_ci if (attr->group != KVM_DEV_ARM_VGIC_GRP_DIST_REGS) { 48762306a36Sopenharmony_ci vgic_mpidr = (attr->attr & KVM_DEV_ARM_VGIC_V3_MPIDR_MASK) >> 48862306a36Sopenharmony_ci KVM_DEV_ARM_VGIC_V3_MPIDR_SHIFT; 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_ci mpidr_reg = VGIC_TO_MPIDR(vgic_mpidr); 49162306a36Sopenharmony_ci reg_attr->vcpu = kvm_mpidr_to_vcpu(dev->kvm, mpidr_reg); 49262306a36Sopenharmony_ci } else { 49362306a36Sopenharmony_ci reg_attr->vcpu = kvm_get_vcpu(dev->kvm, 0); 49462306a36Sopenharmony_ci } 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_ci if (!reg_attr->vcpu) 49762306a36Sopenharmony_ci return -EINVAL; 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_ci reg_attr->addr = attr->attr & KVM_DEV_ARM_VGIC_OFFSET_MASK; 50062306a36Sopenharmony_ci 50162306a36Sopenharmony_ci return 0; 50262306a36Sopenharmony_ci} 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_ci/* 50562306a36Sopenharmony_ci * vgic_v3_attr_regs_access - allows user space to access VGIC v3 state 50662306a36Sopenharmony_ci * 50762306a36Sopenharmony_ci * @dev: kvm device handle 50862306a36Sopenharmony_ci * @attr: kvm device attribute 50962306a36Sopenharmony_ci * @is_write: true if userspace is writing a register 51062306a36Sopenharmony_ci */ 51162306a36Sopenharmony_cistatic int vgic_v3_attr_regs_access(struct kvm_device *dev, 51262306a36Sopenharmony_ci struct kvm_device_attr *attr, 51362306a36Sopenharmony_ci bool is_write) 51462306a36Sopenharmony_ci{ 51562306a36Sopenharmony_ci struct vgic_reg_attr reg_attr; 51662306a36Sopenharmony_ci gpa_t addr; 51762306a36Sopenharmony_ci struct kvm_vcpu *vcpu; 51862306a36Sopenharmony_ci bool uaccess; 51962306a36Sopenharmony_ci u32 val; 52062306a36Sopenharmony_ci int ret; 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_ci ret = vgic_v3_parse_attr(dev, attr, ®_attr); 52362306a36Sopenharmony_ci if (ret) 52462306a36Sopenharmony_ci return ret; 52562306a36Sopenharmony_ci 52662306a36Sopenharmony_ci vcpu = reg_attr.vcpu; 52762306a36Sopenharmony_ci addr = reg_attr.addr; 52862306a36Sopenharmony_ci 52962306a36Sopenharmony_ci switch (attr->group) { 53062306a36Sopenharmony_ci case KVM_DEV_ARM_VGIC_GRP_CPU_SYSREGS: 53162306a36Sopenharmony_ci /* Sysregs uaccess is performed by the sysreg handling code */ 53262306a36Sopenharmony_ci uaccess = false; 53362306a36Sopenharmony_ci break; 53462306a36Sopenharmony_ci default: 53562306a36Sopenharmony_ci uaccess = true; 53662306a36Sopenharmony_ci } 53762306a36Sopenharmony_ci 53862306a36Sopenharmony_ci if (uaccess && is_write) { 53962306a36Sopenharmony_ci u32 __user *uaddr = (u32 __user *)(unsigned long)attr->addr; 54062306a36Sopenharmony_ci if (get_user(val, uaddr)) 54162306a36Sopenharmony_ci return -EFAULT; 54262306a36Sopenharmony_ci } 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_ci mutex_lock(&dev->kvm->lock); 54562306a36Sopenharmony_ci 54662306a36Sopenharmony_ci if (!lock_all_vcpus(dev->kvm)) { 54762306a36Sopenharmony_ci mutex_unlock(&dev->kvm->lock); 54862306a36Sopenharmony_ci return -EBUSY; 54962306a36Sopenharmony_ci } 55062306a36Sopenharmony_ci 55162306a36Sopenharmony_ci mutex_lock(&dev->kvm->arch.config_lock); 55262306a36Sopenharmony_ci 55362306a36Sopenharmony_ci if (unlikely(!vgic_initialized(dev->kvm))) { 55462306a36Sopenharmony_ci ret = -EBUSY; 55562306a36Sopenharmony_ci goto out; 55662306a36Sopenharmony_ci } 55762306a36Sopenharmony_ci 55862306a36Sopenharmony_ci switch (attr->group) { 55962306a36Sopenharmony_ci case KVM_DEV_ARM_VGIC_GRP_DIST_REGS: 56062306a36Sopenharmony_ci ret = vgic_v3_dist_uaccess(vcpu, is_write, addr, &val); 56162306a36Sopenharmony_ci break; 56262306a36Sopenharmony_ci case KVM_DEV_ARM_VGIC_GRP_REDIST_REGS: 56362306a36Sopenharmony_ci ret = vgic_v3_redist_uaccess(vcpu, is_write, addr, &val); 56462306a36Sopenharmony_ci break; 56562306a36Sopenharmony_ci case KVM_DEV_ARM_VGIC_GRP_CPU_SYSREGS: 56662306a36Sopenharmony_ci ret = vgic_v3_cpu_sysregs_uaccess(vcpu, attr, is_write); 56762306a36Sopenharmony_ci break; 56862306a36Sopenharmony_ci case KVM_DEV_ARM_VGIC_GRP_LEVEL_INFO: { 56962306a36Sopenharmony_ci unsigned int info, intid; 57062306a36Sopenharmony_ci 57162306a36Sopenharmony_ci info = (attr->attr & KVM_DEV_ARM_VGIC_LINE_LEVEL_INFO_MASK) >> 57262306a36Sopenharmony_ci KVM_DEV_ARM_VGIC_LINE_LEVEL_INFO_SHIFT; 57362306a36Sopenharmony_ci if (info == VGIC_LEVEL_INFO_LINE_LEVEL) { 57462306a36Sopenharmony_ci intid = attr->attr & 57562306a36Sopenharmony_ci KVM_DEV_ARM_VGIC_LINE_LEVEL_INTID_MASK; 57662306a36Sopenharmony_ci ret = vgic_v3_line_level_info_uaccess(vcpu, is_write, 57762306a36Sopenharmony_ci intid, &val); 57862306a36Sopenharmony_ci } else { 57962306a36Sopenharmony_ci ret = -EINVAL; 58062306a36Sopenharmony_ci } 58162306a36Sopenharmony_ci break; 58262306a36Sopenharmony_ci } 58362306a36Sopenharmony_ci default: 58462306a36Sopenharmony_ci ret = -EINVAL; 58562306a36Sopenharmony_ci break; 58662306a36Sopenharmony_ci } 58762306a36Sopenharmony_ci 58862306a36Sopenharmony_ciout: 58962306a36Sopenharmony_ci mutex_unlock(&dev->kvm->arch.config_lock); 59062306a36Sopenharmony_ci unlock_all_vcpus(dev->kvm); 59162306a36Sopenharmony_ci mutex_unlock(&dev->kvm->lock); 59262306a36Sopenharmony_ci 59362306a36Sopenharmony_ci if (!ret && uaccess && !is_write) { 59462306a36Sopenharmony_ci u32 __user *uaddr = (u32 __user *)(unsigned long)attr->addr; 59562306a36Sopenharmony_ci ret = put_user(val, uaddr); 59662306a36Sopenharmony_ci } 59762306a36Sopenharmony_ci 59862306a36Sopenharmony_ci return ret; 59962306a36Sopenharmony_ci} 60062306a36Sopenharmony_ci 60162306a36Sopenharmony_cistatic int vgic_v3_set_attr(struct kvm_device *dev, 60262306a36Sopenharmony_ci struct kvm_device_attr *attr) 60362306a36Sopenharmony_ci{ 60462306a36Sopenharmony_ci switch (attr->group) { 60562306a36Sopenharmony_ci case KVM_DEV_ARM_VGIC_GRP_DIST_REGS: 60662306a36Sopenharmony_ci case KVM_DEV_ARM_VGIC_GRP_REDIST_REGS: 60762306a36Sopenharmony_ci case KVM_DEV_ARM_VGIC_GRP_CPU_SYSREGS: 60862306a36Sopenharmony_ci case KVM_DEV_ARM_VGIC_GRP_LEVEL_INFO: 60962306a36Sopenharmony_ci return vgic_v3_attr_regs_access(dev, attr, true); 61062306a36Sopenharmony_ci default: 61162306a36Sopenharmony_ci return vgic_set_common_attr(dev, attr); 61262306a36Sopenharmony_ci } 61362306a36Sopenharmony_ci} 61462306a36Sopenharmony_ci 61562306a36Sopenharmony_cistatic int vgic_v3_get_attr(struct kvm_device *dev, 61662306a36Sopenharmony_ci struct kvm_device_attr *attr) 61762306a36Sopenharmony_ci{ 61862306a36Sopenharmony_ci switch (attr->group) { 61962306a36Sopenharmony_ci case KVM_DEV_ARM_VGIC_GRP_DIST_REGS: 62062306a36Sopenharmony_ci case KVM_DEV_ARM_VGIC_GRP_REDIST_REGS: 62162306a36Sopenharmony_ci case KVM_DEV_ARM_VGIC_GRP_CPU_SYSREGS: 62262306a36Sopenharmony_ci case KVM_DEV_ARM_VGIC_GRP_LEVEL_INFO: 62362306a36Sopenharmony_ci return vgic_v3_attr_regs_access(dev, attr, false); 62462306a36Sopenharmony_ci default: 62562306a36Sopenharmony_ci return vgic_get_common_attr(dev, attr); 62662306a36Sopenharmony_ci } 62762306a36Sopenharmony_ci} 62862306a36Sopenharmony_ci 62962306a36Sopenharmony_cistatic int vgic_v3_has_attr(struct kvm_device *dev, 63062306a36Sopenharmony_ci struct kvm_device_attr *attr) 63162306a36Sopenharmony_ci{ 63262306a36Sopenharmony_ci switch (attr->group) { 63362306a36Sopenharmony_ci case KVM_DEV_ARM_VGIC_GRP_ADDR: 63462306a36Sopenharmony_ci switch (attr->attr) { 63562306a36Sopenharmony_ci case KVM_VGIC_V3_ADDR_TYPE_DIST: 63662306a36Sopenharmony_ci case KVM_VGIC_V3_ADDR_TYPE_REDIST: 63762306a36Sopenharmony_ci case KVM_VGIC_V3_ADDR_TYPE_REDIST_REGION: 63862306a36Sopenharmony_ci return 0; 63962306a36Sopenharmony_ci } 64062306a36Sopenharmony_ci break; 64162306a36Sopenharmony_ci case KVM_DEV_ARM_VGIC_GRP_DIST_REGS: 64262306a36Sopenharmony_ci case KVM_DEV_ARM_VGIC_GRP_REDIST_REGS: 64362306a36Sopenharmony_ci case KVM_DEV_ARM_VGIC_GRP_CPU_SYSREGS: 64462306a36Sopenharmony_ci return vgic_v3_has_attr_regs(dev, attr); 64562306a36Sopenharmony_ci case KVM_DEV_ARM_VGIC_GRP_NR_IRQS: 64662306a36Sopenharmony_ci return 0; 64762306a36Sopenharmony_ci case KVM_DEV_ARM_VGIC_GRP_LEVEL_INFO: { 64862306a36Sopenharmony_ci if (((attr->attr & KVM_DEV_ARM_VGIC_LINE_LEVEL_INFO_MASK) >> 64962306a36Sopenharmony_ci KVM_DEV_ARM_VGIC_LINE_LEVEL_INFO_SHIFT) == 65062306a36Sopenharmony_ci VGIC_LEVEL_INFO_LINE_LEVEL) 65162306a36Sopenharmony_ci return 0; 65262306a36Sopenharmony_ci break; 65362306a36Sopenharmony_ci } 65462306a36Sopenharmony_ci case KVM_DEV_ARM_VGIC_GRP_CTRL: 65562306a36Sopenharmony_ci switch (attr->attr) { 65662306a36Sopenharmony_ci case KVM_DEV_ARM_VGIC_CTRL_INIT: 65762306a36Sopenharmony_ci return 0; 65862306a36Sopenharmony_ci case KVM_DEV_ARM_VGIC_SAVE_PENDING_TABLES: 65962306a36Sopenharmony_ci return 0; 66062306a36Sopenharmony_ci } 66162306a36Sopenharmony_ci } 66262306a36Sopenharmony_ci return -ENXIO; 66362306a36Sopenharmony_ci} 66462306a36Sopenharmony_ci 66562306a36Sopenharmony_cistruct kvm_device_ops kvm_arm_vgic_v3_ops = { 66662306a36Sopenharmony_ci .name = "kvm-arm-vgic-v3", 66762306a36Sopenharmony_ci .create = vgic_create, 66862306a36Sopenharmony_ci .destroy = vgic_destroy, 66962306a36Sopenharmony_ci .set_attr = vgic_v3_set_attr, 67062306a36Sopenharmony_ci .get_attr = vgic_v3_get_attr, 67162306a36Sopenharmony_ci .has_attr = vgic_v3_has_attr, 67262306a36Sopenharmony_ci}; 673