18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * VGICv2 MMIO handling functions 48c2ecf20Sopenharmony_ci */ 58c2ecf20Sopenharmony_ci 68c2ecf20Sopenharmony_ci#include <linux/irqchip/arm-gic.h> 78c2ecf20Sopenharmony_ci#include <linux/kvm.h> 88c2ecf20Sopenharmony_ci#include <linux/kvm_host.h> 98c2ecf20Sopenharmony_ci#include <linux/nospec.h> 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci#include <kvm/iodev.h> 128c2ecf20Sopenharmony_ci#include <kvm/arm_vgic.h> 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci#include "vgic.h" 158c2ecf20Sopenharmony_ci#include "vgic-mmio.h" 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci/* 188c2ecf20Sopenharmony_ci * The Revision field in the IIDR have the following meanings: 198c2ecf20Sopenharmony_ci * 208c2ecf20Sopenharmony_ci * Revision 1: Report GICv2 interrupts as group 0 instead of group 1 218c2ecf20Sopenharmony_ci * Revision 2: Interrupt groups are guest-configurable and signaled using 228c2ecf20Sopenharmony_ci * their configured groups. 238c2ecf20Sopenharmony_ci */ 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_cistatic unsigned long vgic_mmio_read_v2_misc(struct kvm_vcpu *vcpu, 268c2ecf20Sopenharmony_ci gpa_t addr, unsigned int len) 278c2ecf20Sopenharmony_ci{ 288c2ecf20Sopenharmony_ci struct vgic_dist *vgic = &vcpu->kvm->arch.vgic; 298c2ecf20Sopenharmony_ci u32 value; 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci switch (addr & 0x0c) { 328c2ecf20Sopenharmony_ci case GIC_DIST_CTRL: 338c2ecf20Sopenharmony_ci value = vgic->enabled ? GICD_ENABLE : 0; 348c2ecf20Sopenharmony_ci break; 358c2ecf20Sopenharmony_ci case GIC_DIST_CTR: 368c2ecf20Sopenharmony_ci value = vgic->nr_spis + VGIC_NR_PRIVATE_IRQS; 378c2ecf20Sopenharmony_ci value = (value >> 5) - 1; 388c2ecf20Sopenharmony_ci value |= (atomic_read(&vcpu->kvm->online_vcpus) - 1) << 5; 398c2ecf20Sopenharmony_ci break; 408c2ecf20Sopenharmony_ci case GIC_DIST_IIDR: 418c2ecf20Sopenharmony_ci value = (PRODUCT_ID_KVM << GICD_IIDR_PRODUCT_ID_SHIFT) | 428c2ecf20Sopenharmony_ci (vgic->implementation_rev << GICD_IIDR_REVISION_SHIFT) | 438c2ecf20Sopenharmony_ci (IMPLEMENTER_ARM << GICD_IIDR_IMPLEMENTER_SHIFT); 448c2ecf20Sopenharmony_ci break; 458c2ecf20Sopenharmony_ci default: 468c2ecf20Sopenharmony_ci return 0; 478c2ecf20Sopenharmony_ci } 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci return value; 508c2ecf20Sopenharmony_ci} 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_cistatic void vgic_mmio_write_v2_misc(struct kvm_vcpu *vcpu, 538c2ecf20Sopenharmony_ci gpa_t addr, unsigned int len, 548c2ecf20Sopenharmony_ci unsigned long val) 558c2ecf20Sopenharmony_ci{ 568c2ecf20Sopenharmony_ci struct vgic_dist *dist = &vcpu->kvm->arch.vgic; 578c2ecf20Sopenharmony_ci bool was_enabled = dist->enabled; 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci switch (addr & 0x0c) { 608c2ecf20Sopenharmony_ci case GIC_DIST_CTRL: 618c2ecf20Sopenharmony_ci dist->enabled = val & GICD_ENABLE; 628c2ecf20Sopenharmony_ci if (!was_enabled && dist->enabled) 638c2ecf20Sopenharmony_ci vgic_kick_vcpus(vcpu->kvm); 648c2ecf20Sopenharmony_ci break; 658c2ecf20Sopenharmony_ci case GIC_DIST_CTR: 668c2ecf20Sopenharmony_ci case GIC_DIST_IIDR: 678c2ecf20Sopenharmony_ci /* Nothing to do */ 688c2ecf20Sopenharmony_ci return; 698c2ecf20Sopenharmony_ci } 708c2ecf20Sopenharmony_ci} 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_cistatic int vgic_mmio_uaccess_write_v2_misc(struct kvm_vcpu *vcpu, 738c2ecf20Sopenharmony_ci gpa_t addr, unsigned int len, 748c2ecf20Sopenharmony_ci unsigned long val) 758c2ecf20Sopenharmony_ci{ 768c2ecf20Sopenharmony_ci switch (addr & 0x0c) { 778c2ecf20Sopenharmony_ci case GIC_DIST_IIDR: 788c2ecf20Sopenharmony_ci if (val != vgic_mmio_read_v2_misc(vcpu, addr, len)) 798c2ecf20Sopenharmony_ci return -EINVAL; 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci /* 828c2ecf20Sopenharmony_ci * If we observe a write to GICD_IIDR we know that userspace 838c2ecf20Sopenharmony_ci * has been updated and has had a chance to cope with older 848c2ecf20Sopenharmony_ci * kernels (VGICv2 IIDR.Revision == 0) incorrectly reporting 858c2ecf20Sopenharmony_ci * interrupts as group 1, and therefore we now allow groups to 868c2ecf20Sopenharmony_ci * be user writable. Doing this by default would break 878c2ecf20Sopenharmony_ci * migration from old kernels to new kernels with legacy 888c2ecf20Sopenharmony_ci * userspace. 898c2ecf20Sopenharmony_ci */ 908c2ecf20Sopenharmony_ci vcpu->kvm->arch.vgic.v2_groups_user_writable = true; 918c2ecf20Sopenharmony_ci return 0; 928c2ecf20Sopenharmony_ci } 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci vgic_mmio_write_v2_misc(vcpu, addr, len, val); 958c2ecf20Sopenharmony_ci return 0; 968c2ecf20Sopenharmony_ci} 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_cistatic int vgic_mmio_uaccess_write_v2_group(struct kvm_vcpu *vcpu, 998c2ecf20Sopenharmony_ci gpa_t addr, unsigned int len, 1008c2ecf20Sopenharmony_ci unsigned long val) 1018c2ecf20Sopenharmony_ci{ 1028c2ecf20Sopenharmony_ci if (vcpu->kvm->arch.vgic.v2_groups_user_writable) 1038c2ecf20Sopenharmony_ci vgic_mmio_write_group(vcpu, addr, len, val); 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci return 0; 1068c2ecf20Sopenharmony_ci} 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_cistatic void vgic_mmio_write_sgir(struct kvm_vcpu *source_vcpu, 1098c2ecf20Sopenharmony_ci gpa_t addr, unsigned int len, 1108c2ecf20Sopenharmony_ci unsigned long val) 1118c2ecf20Sopenharmony_ci{ 1128c2ecf20Sopenharmony_ci int nr_vcpus = atomic_read(&source_vcpu->kvm->online_vcpus); 1138c2ecf20Sopenharmony_ci int intid = val & 0xf; 1148c2ecf20Sopenharmony_ci int targets = (val >> 16) & 0xff; 1158c2ecf20Sopenharmony_ci int mode = (val >> 24) & 0x03; 1168c2ecf20Sopenharmony_ci int c; 1178c2ecf20Sopenharmony_ci struct kvm_vcpu *vcpu; 1188c2ecf20Sopenharmony_ci unsigned long flags; 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci switch (mode) { 1218c2ecf20Sopenharmony_ci case 0x0: /* as specified by targets */ 1228c2ecf20Sopenharmony_ci break; 1238c2ecf20Sopenharmony_ci case 0x1: 1248c2ecf20Sopenharmony_ci targets = (1U << nr_vcpus) - 1; /* all, ... */ 1258c2ecf20Sopenharmony_ci targets &= ~(1U << source_vcpu->vcpu_id); /* but self */ 1268c2ecf20Sopenharmony_ci break; 1278c2ecf20Sopenharmony_ci case 0x2: /* this very vCPU only */ 1288c2ecf20Sopenharmony_ci targets = (1U << source_vcpu->vcpu_id); 1298c2ecf20Sopenharmony_ci break; 1308c2ecf20Sopenharmony_ci case 0x3: /* reserved */ 1318c2ecf20Sopenharmony_ci return; 1328c2ecf20Sopenharmony_ci } 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci kvm_for_each_vcpu(c, vcpu, source_vcpu->kvm) { 1358c2ecf20Sopenharmony_ci struct vgic_irq *irq; 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci if (!(targets & (1U << c))) 1388c2ecf20Sopenharmony_ci continue; 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci irq = vgic_get_irq(source_vcpu->kvm, vcpu, intid); 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci raw_spin_lock_irqsave(&irq->irq_lock, flags); 1438c2ecf20Sopenharmony_ci irq->pending_latch = true; 1448c2ecf20Sopenharmony_ci irq->source |= 1U << source_vcpu->vcpu_id; 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci vgic_queue_irq_unlock(source_vcpu->kvm, irq, flags); 1478c2ecf20Sopenharmony_ci vgic_put_irq(source_vcpu->kvm, irq); 1488c2ecf20Sopenharmony_ci } 1498c2ecf20Sopenharmony_ci} 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_cistatic unsigned long vgic_mmio_read_target(struct kvm_vcpu *vcpu, 1528c2ecf20Sopenharmony_ci gpa_t addr, unsigned int len) 1538c2ecf20Sopenharmony_ci{ 1548c2ecf20Sopenharmony_ci u32 intid = VGIC_ADDR_TO_INTID(addr, 8); 1558c2ecf20Sopenharmony_ci int i; 1568c2ecf20Sopenharmony_ci u64 val = 0; 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci for (i = 0; i < len; i++) { 1598c2ecf20Sopenharmony_ci struct vgic_irq *irq = vgic_get_irq(vcpu->kvm, vcpu, intid + i); 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_ci val |= (u64)irq->targets << (i * 8); 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci vgic_put_irq(vcpu->kvm, irq); 1648c2ecf20Sopenharmony_ci } 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci return val; 1678c2ecf20Sopenharmony_ci} 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_cistatic void vgic_mmio_write_target(struct kvm_vcpu *vcpu, 1708c2ecf20Sopenharmony_ci gpa_t addr, unsigned int len, 1718c2ecf20Sopenharmony_ci unsigned long val) 1728c2ecf20Sopenharmony_ci{ 1738c2ecf20Sopenharmony_ci u32 intid = VGIC_ADDR_TO_INTID(addr, 8); 1748c2ecf20Sopenharmony_ci u8 cpu_mask = GENMASK(atomic_read(&vcpu->kvm->online_vcpus) - 1, 0); 1758c2ecf20Sopenharmony_ci int i; 1768c2ecf20Sopenharmony_ci unsigned long flags; 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci /* GICD_ITARGETSR[0-7] are read-only */ 1798c2ecf20Sopenharmony_ci if (intid < VGIC_NR_PRIVATE_IRQS) 1808c2ecf20Sopenharmony_ci return; 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci for (i = 0; i < len; i++) { 1838c2ecf20Sopenharmony_ci struct vgic_irq *irq = vgic_get_irq(vcpu->kvm, NULL, intid + i); 1848c2ecf20Sopenharmony_ci int target; 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci raw_spin_lock_irqsave(&irq->irq_lock, flags); 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci irq->targets = (val >> (i * 8)) & cpu_mask; 1898c2ecf20Sopenharmony_ci target = irq->targets ? __ffs(irq->targets) : 0; 1908c2ecf20Sopenharmony_ci irq->target_vcpu = kvm_get_vcpu(vcpu->kvm, target); 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci raw_spin_unlock_irqrestore(&irq->irq_lock, flags); 1938c2ecf20Sopenharmony_ci vgic_put_irq(vcpu->kvm, irq); 1948c2ecf20Sopenharmony_ci } 1958c2ecf20Sopenharmony_ci} 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_cistatic unsigned long vgic_mmio_read_sgipend(struct kvm_vcpu *vcpu, 1988c2ecf20Sopenharmony_ci gpa_t addr, unsigned int len) 1998c2ecf20Sopenharmony_ci{ 2008c2ecf20Sopenharmony_ci u32 intid = addr & 0x0f; 2018c2ecf20Sopenharmony_ci int i; 2028c2ecf20Sopenharmony_ci u64 val = 0; 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci for (i = 0; i < len; i++) { 2058c2ecf20Sopenharmony_ci struct vgic_irq *irq = vgic_get_irq(vcpu->kvm, vcpu, intid + i); 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci val |= (u64)irq->source << (i * 8); 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci vgic_put_irq(vcpu->kvm, irq); 2108c2ecf20Sopenharmony_ci } 2118c2ecf20Sopenharmony_ci return val; 2128c2ecf20Sopenharmony_ci} 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_cistatic void vgic_mmio_write_sgipendc(struct kvm_vcpu *vcpu, 2158c2ecf20Sopenharmony_ci gpa_t addr, unsigned int len, 2168c2ecf20Sopenharmony_ci unsigned long val) 2178c2ecf20Sopenharmony_ci{ 2188c2ecf20Sopenharmony_ci u32 intid = addr & 0x0f; 2198c2ecf20Sopenharmony_ci int i; 2208c2ecf20Sopenharmony_ci unsigned long flags; 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci for (i = 0; i < len; i++) { 2238c2ecf20Sopenharmony_ci struct vgic_irq *irq = vgic_get_irq(vcpu->kvm, vcpu, intid + i); 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci raw_spin_lock_irqsave(&irq->irq_lock, flags); 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci irq->source &= ~((val >> (i * 8)) & 0xff); 2288c2ecf20Sopenharmony_ci if (!irq->source) 2298c2ecf20Sopenharmony_ci irq->pending_latch = false; 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci raw_spin_unlock_irqrestore(&irq->irq_lock, flags); 2328c2ecf20Sopenharmony_ci vgic_put_irq(vcpu->kvm, irq); 2338c2ecf20Sopenharmony_ci } 2348c2ecf20Sopenharmony_ci} 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_cistatic void vgic_mmio_write_sgipends(struct kvm_vcpu *vcpu, 2378c2ecf20Sopenharmony_ci gpa_t addr, unsigned int len, 2388c2ecf20Sopenharmony_ci unsigned long val) 2398c2ecf20Sopenharmony_ci{ 2408c2ecf20Sopenharmony_ci u32 intid = addr & 0x0f; 2418c2ecf20Sopenharmony_ci int i; 2428c2ecf20Sopenharmony_ci unsigned long flags; 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci for (i = 0; i < len; i++) { 2458c2ecf20Sopenharmony_ci struct vgic_irq *irq = vgic_get_irq(vcpu->kvm, vcpu, intid + i); 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci raw_spin_lock_irqsave(&irq->irq_lock, flags); 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci irq->source |= (val >> (i * 8)) & 0xff; 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci if (irq->source) { 2528c2ecf20Sopenharmony_ci irq->pending_latch = true; 2538c2ecf20Sopenharmony_ci vgic_queue_irq_unlock(vcpu->kvm, irq, flags); 2548c2ecf20Sopenharmony_ci } else { 2558c2ecf20Sopenharmony_ci raw_spin_unlock_irqrestore(&irq->irq_lock, flags); 2568c2ecf20Sopenharmony_ci } 2578c2ecf20Sopenharmony_ci vgic_put_irq(vcpu->kvm, irq); 2588c2ecf20Sopenharmony_ci } 2598c2ecf20Sopenharmony_ci} 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci#define GICC_ARCH_VERSION_V2 0x2 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci/* These are for userland accesses only, there is no guest-facing emulation. */ 2648c2ecf20Sopenharmony_cistatic unsigned long vgic_mmio_read_vcpuif(struct kvm_vcpu *vcpu, 2658c2ecf20Sopenharmony_ci gpa_t addr, unsigned int len) 2668c2ecf20Sopenharmony_ci{ 2678c2ecf20Sopenharmony_ci struct vgic_vmcr vmcr; 2688c2ecf20Sopenharmony_ci u32 val; 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_ci vgic_get_vmcr(vcpu, &vmcr); 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci switch (addr & 0xff) { 2738c2ecf20Sopenharmony_ci case GIC_CPU_CTRL: 2748c2ecf20Sopenharmony_ci val = vmcr.grpen0 << GIC_CPU_CTRL_EnableGrp0_SHIFT; 2758c2ecf20Sopenharmony_ci val |= vmcr.grpen1 << GIC_CPU_CTRL_EnableGrp1_SHIFT; 2768c2ecf20Sopenharmony_ci val |= vmcr.ackctl << GIC_CPU_CTRL_AckCtl_SHIFT; 2778c2ecf20Sopenharmony_ci val |= vmcr.fiqen << GIC_CPU_CTRL_FIQEn_SHIFT; 2788c2ecf20Sopenharmony_ci val |= vmcr.cbpr << GIC_CPU_CTRL_CBPR_SHIFT; 2798c2ecf20Sopenharmony_ci val |= vmcr.eoim << GIC_CPU_CTRL_EOImodeNS_SHIFT; 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci break; 2828c2ecf20Sopenharmony_ci case GIC_CPU_PRIMASK: 2838c2ecf20Sopenharmony_ci /* 2848c2ecf20Sopenharmony_ci * Our KVM_DEV_TYPE_ARM_VGIC_V2 device ABI exports the 2858c2ecf20Sopenharmony_ci * the PMR field as GICH_VMCR.VMPriMask rather than 2868c2ecf20Sopenharmony_ci * GICC_PMR.Priority, so we expose the upper five bits of 2878c2ecf20Sopenharmony_ci * priority mask to userspace using the lower bits in the 2888c2ecf20Sopenharmony_ci * unsigned long. 2898c2ecf20Sopenharmony_ci */ 2908c2ecf20Sopenharmony_ci val = (vmcr.pmr & GICV_PMR_PRIORITY_MASK) >> 2918c2ecf20Sopenharmony_ci GICV_PMR_PRIORITY_SHIFT; 2928c2ecf20Sopenharmony_ci break; 2938c2ecf20Sopenharmony_ci case GIC_CPU_BINPOINT: 2948c2ecf20Sopenharmony_ci val = vmcr.bpr; 2958c2ecf20Sopenharmony_ci break; 2968c2ecf20Sopenharmony_ci case GIC_CPU_ALIAS_BINPOINT: 2978c2ecf20Sopenharmony_ci val = vmcr.abpr; 2988c2ecf20Sopenharmony_ci break; 2998c2ecf20Sopenharmony_ci case GIC_CPU_IDENT: 3008c2ecf20Sopenharmony_ci val = ((PRODUCT_ID_KVM << 20) | 3018c2ecf20Sopenharmony_ci (GICC_ARCH_VERSION_V2 << 16) | 3028c2ecf20Sopenharmony_ci IMPLEMENTER_ARM); 3038c2ecf20Sopenharmony_ci break; 3048c2ecf20Sopenharmony_ci default: 3058c2ecf20Sopenharmony_ci return 0; 3068c2ecf20Sopenharmony_ci } 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ci return val; 3098c2ecf20Sopenharmony_ci} 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_cistatic void vgic_mmio_write_vcpuif(struct kvm_vcpu *vcpu, 3128c2ecf20Sopenharmony_ci gpa_t addr, unsigned int len, 3138c2ecf20Sopenharmony_ci unsigned long val) 3148c2ecf20Sopenharmony_ci{ 3158c2ecf20Sopenharmony_ci struct vgic_vmcr vmcr; 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci vgic_get_vmcr(vcpu, &vmcr); 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_ci switch (addr & 0xff) { 3208c2ecf20Sopenharmony_ci case GIC_CPU_CTRL: 3218c2ecf20Sopenharmony_ci vmcr.grpen0 = !!(val & GIC_CPU_CTRL_EnableGrp0); 3228c2ecf20Sopenharmony_ci vmcr.grpen1 = !!(val & GIC_CPU_CTRL_EnableGrp1); 3238c2ecf20Sopenharmony_ci vmcr.ackctl = !!(val & GIC_CPU_CTRL_AckCtl); 3248c2ecf20Sopenharmony_ci vmcr.fiqen = !!(val & GIC_CPU_CTRL_FIQEn); 3258c2ecf20Sopenharmony_ci vmcr.cbpr = !!(val & GIC_CPU_CTRL_CBPR); 3268c2ecf20Sopenharmony_ci vmcr.eoim = !!(val & GIC_CPU_CTRL_EOImodeNS); 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_ci break; 3298c2ecf20Sopenharmony_ci case GIC_CPU_PRIMASK: 3308c2ecf20Sopenharmony_ci /* 3318c2ecf20Sopenharmony_ci * Our KVM_DEV_TYPE_ARM_VGIC_V2 device ABI exports the 3328c2ecf20Sopenharmony_ci * the PMR field as GICH_VMCR.VMPriMask rather than 3338c2ecf20Sopenharmony_ci * GICC_PMR.Priority, so we expose the upper five bits of 3348c2ecf20Sopenharmony_ci * priority mask to userspace using the lower bits in the 3358c2ecf20Sopenharmony_ci * unsigned long. 3368c2ecf20Sopenharmony_ci */ 3378c2ecf20Sopenharmony_ci vmcr.pmr = (val << GICV_PMR_PRIORITY_SHIFT) & 3388c2ecf20Sopenharmony_ci GICV_PMR_PRIORITY_MASK; 3398c2ecf20Sopenharmony_ci break; 3408c2ecf20Sopenharmony_ci case GIC_CPU_BINPOINT: 3418c2ecf20Sopenharmony_ci vmcr.bpr = val; 3428c2ecf20Sopenharmony_ci break; 3438c2ecf20Sopenharmony_ci case GIC_CPU_ALIAS_BINPOINT: 3448c2ecf20Sopenharmony_ci vmcr.abpr = val; 3458c2ecf20Sopenharmony_ci break; 3468c2ecf20Sopenharmony_ci } 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ci vgic_set_vmcr(vcpu, &vmcr); 3498c2ecf20Sopenharmony_ci} 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_cistatic unsigned long vgic_mmio_read_apr(struct kvm_vcpu *vcpu, 3528c2ecf20Sopenharmony_ci gpa_t addr, unsigned int len) 3538c2ecf20Sopenharmony_ci{ 3548c2ecf20Sopenharmony_ci int n; /* which APRn is this */ 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_ci n = (addr >> 2) & 0x3; 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_ci if (kvm_vgic_global_state.type == VGIC_V2) { 3598c2ecf20Sopenharmony_ci /* GICv2 hardware systems support max. 32 groups */ 3608c2ecf20Sopenharmony_ci if (n != 0) 3618c2ecf20Sopenharmony_ci return 0; 3628c2ecf20Sopenharmony_ci return vcpu->arch.vgic_cpu.vgic_v2.vgic_apr; 3638c2ecf20Sopenharmony_ci } else { 3648c2ecf20Sopenharmony_ci struct vgic_v3_cpu_if *vgicv3 = &vcpu->arch.vgic_cpu.vgic_v3; 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_ci if (n > vgic_v3_max_apr_idx(vcpu)) 3678c2ecf20Sopenharmony_ci return 0; 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_ci n = array_index_nospec(n, 4); 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_ci /* GICv3 only uses ICH_AP1Rn for memory mapped (GICv2) guests */ 3728c2ecf20Sopenharmony_ci return vgicv3->vgic_ap1r[n]; 3738c2ecf20Sopenharmony_ci } 3748c2ecf20Sopenharmony_ci} 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_cistatic void vgic_mmio_write_apr(struct kvm_vcpu *vcpu, 3778c2ecf20Sopenharmony_ci gpa_t addr, unsigned int len, 3788c2ecf20Sopenharmony_ci unsigned long val) 3798c2ecf20Sopenharmony_ci{ 3808c2ecf20Sopenharmony_ci int n; /* which APRn is this */ 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_ci n = (addr >> 2) & 0x3; 3838c2ecf20Sopenharmony_ci 3848c2ecf20Sopenharmony_ci if (kvm_vgic_global_state.type == VGIC_V2) { 3858c2ecf20Sopenharmony_ci /* GICv2 hardware systems support max. 32 groups */ 3868c2ecf20Sopenharmony_ci if (n != 0) 3878c2ecf20Sopenharmony_ci return; 3888c2ecf20Sopenharmony_ci vcpu->arch.vgic_cpu.vgic_v2.vgic_apr = val; 3898c2ecf20Sopenharmony_ci } else { 3908c2ecf20Sopenharmony_ci struct vgic_v3_cpu_if *vgicv3 = &vcpu->arch.vgic_cpu.vgic_v3; 3918c2ecf20Sopenharmony_ci 3928c2ecf20Sopenharmony_ci if (n > vgic_v3_max_apr_idx(vcpu)) 3938c2ecf20Sopenharmony_ci return; 3948c2ecf20Sopenharmony_ci 3958c2ecf20Sopenharmony_ci n = array_index_nospec(n, 4); 3968c2ecf20Sopenharmony_ci 3978c2ecf20Sopenharmony_ci /* GICv3 only uses ICH_AP1Rn for memory mapped (GICv2) guests */ 3988c2ecf20Sopenharmony_ci vgicv3->vgic_ap1r[n] = val; 3998c2ecf20Sopenharmony_ci } 4008c2ecf20Sopenharmony_ci} 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_cistatic const struct vgic_register_region vgic_v2_dist_registers[] = { 4038c2ecf20Sopenharmony_ci REGISTER_DESC_WITH_LENGTH_UACCESS(GIC_DIST_CTRL, 4048c2ecf20Sopenharmony_ci vgic_mmio_read_v2_misc, vgic_mmio_write_v2_misc, 4058c2ecf20Sopenharmony_ci NULL, vgic_mmio_uaccess_write_v2_misc, 4068c2ecf20Sopenharmony_ci 12, VGIC_ACCESS_32bit), 4078c2ecf20Sopenharmony_ci REGISTER_DESC_WITH_BITS_PER_IRQ(GIC_DIST_IGROUP, 4088c2ecf20Sopenharmony_ci vgic_mmio_read_group, vgic_mmio_write_group, 4098c2ecf20Sopenharmony_ci NULL, vgic_mmio_uaccess_write_v2_group, 1, 4108c2ecf20Sopenharmony_ci VGIC_ACCESS_32bit), 4118c2ecf20Sopenharmony_ci REGISTER_DESC_WITH_BITS_PER_IRQ(GIC_DIST_ENABLE_SET, 4128c2ecf20Sopenharmony_ci vgic_mmio_read_enable, vgic_mmio_write_senable, 4138c2ecf20Sopenharmony_ci NULL, vgic_uaccess_write_senable, 1, 4148c2ecf20Sopenharmony_ci VGIC_ACCESS_32bit), 4158c2ecf20Sopenharmony_ci REGISTER_DESC_WITH_BITS_PER_IRQ(GIC_DIST_ENABLE_CLEAR, 4168c2ecf20Sopenharmony_ci vgic_mmio_read_enable, vgic_mmio_write_cenable, 4178c2ecf20Sopenharmony_ci NULL, vgic_uaccess_write_cenable, 1, 4188c2ecf20Sopenharmony_ci VGIC_ACCESS_32bit), 4198c2ecf20Sopenharmony_ci REGISTER_DESC_WITH_BITS_PER_IRQ(GIC_DIST_PENDING_SET, 4208c2ecf20Sopenharmony_ci vgic_mmio_read_pending, vgic_mmio_write_spending, 4218c2ecf20Sopenharmony_ci vgic_uaccess_read_pending, vgic_uaccess_write_spending, 1, 4228c2ecf20Sopenharmony_ci VGIC_ACCESS_32bit), 4238c2ecf20Sopenharmony_ci REGISTER_DESC_WITH_BITS_PER_IRQ(GIC_DIST_PENDING_CLEAR, 4248c2ecf20Sopenharmony_ci vgic_mmio_read_pending, vgic_mmio_write_cpending, 4258c2ecf20Sopenharmony_ci vgic_uaccess_read_pending, vgic_uaccess_write_cpending, 1, 4268c2ecf20Sopenharmony_ci VGIC_ACCESS_32bit), 4278c2ecf20Sopenharmony_ci REGISTER_DESC_WITH_BITS_PER_IRQ(GIC_DIST_ACTIVE_SET, 4288c2ecf20Sopenharmony_ci vgic_mmio_read_active, vgic_mmio_write_sactive, 4298c2ecf20Sopenharmony_ci vgic_uaccess_read_active, vgic_mmio_uaccess_write_sactive, 1, 4308c2ecf20Sopenharmony_ci VGIC_ACCESS_32bit), 4318c2ecf20Sopenharmony_ci REGISTER_DESC_WITH_BITS_PER_IRQ(GIC_DIST_ACTIVE_CLEAR, 4328c2ecf20Sopenharmony_ci vgic_mmio_read_active, vgic_mmio_write_cactive, 4338c2ecf20Sopenharmony_ci vgic_uaccess_read_active, vgic_mmio_uaccess_write_cactive, 1, 4348c2ecf20Sopenharmony_ci VGIC_ACCESS_32bit), 4358c2ecf20Sopenharmony_ci REGISTER_DESC_WITH_BITS_PER_IRQ(GIC_DIST_PRI, 4368c2ecf20Sopenharmony_ci vgic_mmio_read_priority, vgic_mmio_write_priority, NULL, NULL, 4378c2ecf20Sopenharmony_ci 8, VGIC_ACCESS_32bit | VGIC_ACCESS_8bit), 4388c2ecf20Sopenharmony_ci REGISTER_DESC_WITH_BITS_PER_IRQ(GIC_DIST_TARGET, 4398c2ecf20Sopenharmony_ci vgic_mmio_read_target, vgic_mmio_write_target, NULL, NULL, 8, 4408c2ecf20Sopenharmony_ci VGIC_ACCESS_32bit | VGIC_ACCESS_8bit), 4418c2ecf20Sopenharmony_ci REGISTER_DESC_WITH_BITS_PER_IRQ(GIC_DIST_CONFIG, 4428c2ecf20Sopenharmony_ci vgic_mmio_read_config, vgic_mmio_write_config, NULL, NULL, 2, 4438c2ecf20Sopenharmony_ci VGIC_ACCESS_32bit), 4448c2ecf20Sopenharmony_ci REGISTER_DESC_WITH_LENGTH(GIC_DIST_SOFTINT, 4458c2ecf20Sopenharmony_ci vgic_mmio_read_raz, vgic_mmio_write_sgir, 4, 4468c2ecf20Sopenharmony_ci VGIC_ACCESS_32bit), 4478c2ecf20Sopenharmony_ci REGISTER_DESC_WITH_LENGTH(GIC_DIST_SGI_PENDING_CLEAR, 4488c2ecf20Sopenharmony_ci vgic_mmio_read_sgipend, vgic_mmio_write_sgipendc, 16, 4498c2ecf20Sopenharmony_ci VGIC_ACCESS_32bit | VGIC_ACCESS_8bit), 4508c2ecf20Sopenharmony_ci REGISTER_DESC_WITH_LENGTH(GIC_DIST_SGI_PENDING_SET, 4518c2ecf20Sopenharmony_ci vgic_mmio_read_sgipend, vgic_mmio_write_sgipends, 16, 4528c2ecf20Sopenharmony_ci VGIC_ACCESS_32bit | VGIC_ACCESS_8bit), 4538c2ecf20Sopenharmony_ci}; 4548c2ecf20Sopenharmony_ci 4558c2ecf20Sopenharmony_cistatic const struct vgic_register_region vgic_v2_cpu_registers[] = { 4568c2ecf20Sopenharmony_ci REGISTER_DESC_WITH_LENGTH(GIC_CPU_CTRL, 4578c2ecf20Sopenharmony_ci vgic_mmio_read_vcpuif, vgic_mmio_write_vcpuif, 4, 4588c2ecf20Sopenharmony_ci VGIC_ACCESS_32bit), 4598c2ecf20Sopenharmony_ci REGISTER_DESC_WITH_LENGTH(GIC_CPU_PRIMASK, 4608c2ecf20Sopenharmony_ci vgic_mmio_read_vcpuif, vgic_mmio_write_vcpuif, 4, 4618c2ecf20Sopenharmony_ci VGIC_ACCESS_32bit), 4628c2ecf20Sopenharmony_ci REGISTER_DESC_WITH_LENGTH(GIC_CPU_BINPOINT, 4638c2ecf20Sopenharmony_ci vgic_mmio_read_vcpuif, vgic_mmio_write_vcpuif, 4, 4648c2ecf20Sopenharmony_ci VGIC_ACCESS_32bit), 4658c2ecf20Sopenharmony_ci REGISTER_DESC_WITH_LENGTH(GIC_CPU_ALIAS_BINPOINT, 4668c2ecf20Sopenharmony_ci vgic_mmio_read_vcpuif, vgic_mmio_write_vcpuif, 4, 4678c2ecf20Sopenharmony_ci VGIC_ACCESS_32bit), 4688c2ecf20Sopenharmony_ci REGISTER_DESC_WITH_LENGTH(GIC_CPU_ACTIVEPRIO, 4698c2ecf20Sopenharmony_ci vgic_mmio_read_apr, vgic_mmio_write_apr, 16, 4708c2ecf20Sopenharmony_ci VGIC_ACCESS_32bit), 4718c2ecf20Sopenharmony_ci REGISTER_DESC_WITH_LENGTH(GIC_CPU_IDENT, 4728c2ecf20Sopenharmony_ci vgic_mmio_read_vcpuif, vgic_mmio_write_vcpuif, 4, 4738c2ecf20Sopenharmony_ci VGIC_ACCESS_32bit), 4748c2ecf20Sopenharmony_ci}; 4758c2ecf20Sopenharmony_ci 4768c2ecf20Sopenharmony_ciunsigned int vgic_v2_init_dist_iodev(struct vgic_io_device *dev) 4778c2ecf20Sopenharmony_ci{ 4788c2ecf20Sopenharmony_ci dev->regions = vgic_v2_dist_registers; 4798c2ecf20Sopenharmony_ci dev->nr_regions = ARRAY_SIZE(vgic_v2_dist_registers); 4808c2ecf20Sopenharmony_ci 4818c2ecf20Sopenharmony_ci kvm_iodevice_init(&dev->dev, &kvm_io_gic_ops); 4828c2ecf20Sopenharmony_ci 4838c2ecf20Sopenharmony_ci return SZ_4K; 4848c2ecf20Sopenharmony_ci} 4858c2ecf20Sopenharmony_ci 4868c2ecf20Sopenharmony_ciint vgic_v2_has_attr_regs(struct kvm_device *dev, struct kvm_device_attr *attr) 4878c2ecf20Sopenharmony_ci{ 4888c2ecf20Sopenharmony_ci const struct vgic_register_region *region; 4898c2ecf20Sopenharmony_ci struct vgic_io_device iodev; 4908c2ecf20Sopenharmony_ci struct vgic_reg_attr reg_attr; 4918c2ecf20Sopenharmony_ci struct kvm_vcpu *vcpu; 4928c2ecf20Sopenharmony_ci gpa_t addr; 4938c2ecf20Sopenharmony_ci int ret; 4948c2ecf20Sopenharmony_ci 4958c2ecf20Sopenharmony_ci ret = vgic_v2_parse_attr(dev, attr, ®_attr); 4968c2ecf20Sopenharmony_ci if (ret) 4978c2ecf20Sopenharmony_ci return ret; 4988c2ecf20Sopenharmony_ci 4998c2ecf20Sopenharmony_ci vcpu = reg_attr.vcpu; 5008c2ecf20Sopenharmony_ci addr = reg_attr.addr; 5018c2ecf20Sopenharmony_ci 5028c2ecf20Sopenharmony_ci switch (attr->group) { 5038c2ecf20Sopenharmony_ci case KVM_DEV_ARM_VGIC_GRP_DIST_REGS: 5048c2ecf20Sopenharmony_ci iodev.regions = vgic_v2_dist_registers; 5058c2ecf20Sopenharmony_ci iodev.nr_regions = ARRAY_SIZE(vgic_v2_dist_registers); 5068c2ecf20Sopenharmony_ci iodev.base_addr = 0; 5078c2ecf20Sopenharmony_ci break; 5088c2ecf20Sopenharmony_ci case KVM_DEV_ARM_VGIC_GRP_CPU_REGS: 5098c2ecf20Sopenharmony_ci iodev.regions = vgic_v2_cpu_registers; 5108c2ecf20Sopenharmony_ci iodev.nr_regions = ARRAY_SIZE(vgic_v2_cpu_registers); 5118c2ecf20Sopenharmony_ci iodev.base_addr = 0; 5128c2ecf20Sopenharmony_ci break; 5138c2ecf20Sopenharmony_ci default: 5148c2ecf20Sopenharmony_ci return -ENXIO; 5158c2ecf20Sopenharmony_ci } 5168c2ecf20Sopenharmony_ci 5178c2ecf20Sopenharmony_ci /* We only support aligned 32-bit accesses. */ 5188c2ecf20Sopenharmony_ci if (addr & 3) 5198c2ecf20Sopenharmony_ci return -ENXIO; 5208c2ecf20Sopenharmony_ci 5218c2ecf20Sopenharmony_ci region = vgic_get_mmio_region(vcpu, &iodev, addr, sizeof(u32)); 5228c2ecf20Sopenharmony_ci if (!region) 5238c2ecf20Sopenharmony_ci return -ENXIO; 5248c2ecf20Sopenharmony_ci 5258c2ecf20Sopenharmony_ci return 0; 5268c2ecf20Sopenharmony_ci} 5278c2ecf20Sopenharmony_ci 5288c2ecf20Sopenharmony_ciint vgic_v2_cpuif_uaccess(struct kvm_vcpu *vcpu, bool is_write, 5298c2ecf20Sopenharmony_ci int offset, u32 *val) 5308c2ecf20Sopenharmony_ci{ 5318c2ecf20Sopenharmony_ci struct vgic_io_device dev = { 5328c2ecf20Sopenharmony_ci .regions = vgic_v2_cpu_registers, 5338c2ecf20Sopenharmony_ci .nr_regions = ARRAY_SIZE(vgic_v2_cpu_registers), 5348c2ecf20Sopenharmony_ci .iodev_type = IODEV_CPUIF, 5358c2ecf20Sopenharmony_ci }; 5368c2ecf20Sopenharmony_ci 5378c2ecf20Sopenharmony_ci return vgic_uaccess(vcpu, &dev, is_write, offset, val); 5388c2ecf20Sopenharmony_ci} 5398c2ecf20Sopenharmony_ci 5408c2ecf20Sopenharmony_ciint vgic_v2_dist_uaccess(struct kvm_vcpu *vcpu, bool is_write, 5418c2ecf20Sopenharmony_ci int offset, u32 *val) 5428c2ecf20Sopenharmony_ci{ 5438c2ecf20Sopenharmony_ci struct vgic_io_device dev = { 5448c2ecf20Sopenharmony_ci .regions = vgic_v2_dist_registers, 5458c2ecf20Sopenharmony_ci .nr_regions = ARRAY_SIZE(vgic_v2_dist_registers), 5468c2ecf20Sopenharmony_ci .iodev_type = IODEV_DIST, 5478c2ecf20Sopenharmony_ci }; 5488c2ecf20Sopenharmony_ci 5498c2ecf20Sopenharmony_ci return vgic_uaccess(vcpu, &dev, is_write, offset, val); 5508c2ecf20Sopenharmony_ci} 551