18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (C) 2015, 2016 ARM Ltd. 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 <kvm/arm_vgic.h> 108c2ecf20Sopenharmony_ci#include <asm/kvm_mmu.h> 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci#include "vgic.h" 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_cistatic inline void vgic_v2_write_lr(int lr, u32 val) 158c2ecf20Sopenharmony_ci{ 168c2ecf20Sopenharmony_ci void __iomem *base = kvm_vgic_global_state.vctrl_base; 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci writel_relaxed(val, base + GICH_LR0 + (lr * 4)); 198c2ecf20Sopenharmony_ci} 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_civoid vgic_v2_init_lrs(void) 228c2ecf20Sopenharmony_ci{ 238c2ecf20Sopenharmony_ci int i; 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci for (i = 0; i < kvm_vgic_global_state.nr_lr; i++) 268c2ecf20Sopenharmony_ci vgic_v2_write_lr(i, 0); 278c2ecf20Sopenharmony_ci} 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_civoid vgic_v2_set_underflow(struct kvm_vcpu *vcpu) 308c2ecf20Sopenharmony_ci{ 318c2ecf20Sopenharmony_ci struct vgic_v2_cpu_if *cpuif = &vcpu->arch.vgic_cpu.vgic_v2; 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci cpuif->vgic_hcr |= GICH_HCR_UIE; 348c2ecf20Sopenharmony_ci} 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_cistatic bool lr_signals_eoi_mi(u32 lr_val) 378c2ecf20Sopenharmony_ci{ 388c2ecf20Sopenharmony_ci return !(lr_val & GICH_LR_STATE) && (lr_val & GICH_LR_EOI) && 398c2ecf20Sopenharmony_ci !(lr_val & GICH_LR_HW); 408c2ecf20Sopenharmony_ci} 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci/* 438c2ecf20Sopenharmony_ci * transfer the content of the LRs back into the corresponding ap_list: 448c2ecf20Sopenharmony_ci * - active bit is transferred as is 458c2ecf20Sopenharmony_ci * - pending bit is 468c2ecf20Sopenharmony_ci * - transferred as is in case of edge sensitive IRQs 478c2ecf20Sopenharmony_ci * - set to the line-level (resample time) for level sensitive IRQs 488c2ecf20Sopenharmony_ci */ 498c2ecf20Sopenharmony_civoid vgic_v2_fold_lr_state(struct kvm_vcpu *vcpu) 508c2ecf20Sopenharmony_ci{ 518c2ecf20Sopenharmony_ci struct vgic_cpu *vgic_cpu = &vcpu->arch.vgic_cpu; 528c2ecf20Sopenharmony_ci struct vgic_v2_cpu_if *cpuif = &vgic_cpu->vgic_v2; 538c2ecf20Sopenharmony_ci int lr; 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci DEBUG_SPINLOCK_BUG_ON(!irqs_disabled()); 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci cpuif->vgic_hcr &= ~GICH_HCR_UIE; 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci for (lr = 0; lr < vgic_cpu->vgic_v2.used_lrs; lr++) { 608c2ecf20Sopenharmony_ci u32 val = cpuif->vgic_lr[lr]; 618c2ecf20Sopenharmony_ci u32 cpuid, intid = val & GICH_LR_VIRTUALID; 628c2ecf20Sopenharmony_ci struct vgic_irq *irq; 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci /* Extract the source vCPU id from the LR */ 658c2ecf20Sopenharmony_ci cpuid = val & GICH_LR_PHYSID_CPUID; 668c2ecf20Sopenharmony_ci cpuid >>= GICH_LR_PHYSID_CPUID_SHIFT; 678c2ecf20Sopenharmony_ci cpuid &= 7; 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci /* Notify fds when the guest EOI'ed a level-triggered SPI */ 708c2ecf20Sopenharmony_ci if (lr_signals_eoi_mi(val) && vgic_valid_spi(vcpu->kvm, intid)) 718c2ecf20Sopenharmony_ci kvm_notify_acked_irq(vcpu->kvm, 0, 728c2ecf20Sopenharmony_ci intid - VGIC_NR_PRIVATE_IRQS); 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci irq = vgic_get_irq(vcpu->kvm, vcpu, intid); 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci raw_spin_lock(&irq->irq_lock); 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci /* Always preserve the active bit */ 798c2ecf20Sopenharmony_ci irq->active = !!(val & GICH_LR_ACTIVE_BIT); 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci if (irq->active && vgic_irq_is_sgi(intid)) 828c2ecf20Sopenharmony_ci irq->active_source = cpuid; 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci /* Edge is the only case where we preserve the pending bit */ 858c2ecf20Sopenharmony_ci if (irq->config == VGIC_CONFIG_EDGE && 868c2ecf20Sopenharmony_ci (val & GICH_LR_PENDING_BIT)) { 878c2ecf20Sopenharmony_ci irq->pending_latch = true; 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci if (vgic_irq_is_sgi(intid)) 908c2ecf20Sopenharmony_ci irq->source |= (1 << cpuid); 918c2ecf20Sopenharmony_ci } 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci /* 948c2ecf20Sopenharmony_ci * Clear soft pending state when level irqs have been acked. 958c2ecf20Sopenharmony_ci */ 968c2ecf20Sopenharmony_ci if (irq->config == VGIC_CONFIG_LEVEL && !(val & GICH_LR_STATE)) 978c2ecf20Sopenharmony_ci irq->pending_latch = false; 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci /* 1008c2ecf20Sopenharmony_ci * Level-triggered mapped IRQs are special because we only 1018c2ecf20Sopenharmony_ci * observe rising edges as input to the VGIC. 1028c2ecf20Sopenharmony_ci * 1038c2ecf20Sopenharmony_ci * If the guest never acked the interrupt we have to sample 1048c2ecf20Sopenharmony_ci * the physical line and set the line level, because the 1058c2ecf20Sopenharmony_ci * device state could have changed or we simply need to 1068c2ecf20Sopenharmony_ci * process the still pending interrupt later. 1078c2ecf20Sopenharmony_ci * 1088c2ecf20Sopenharmony_ci * If this causes us to lower the level, we have to also clear 1098c2ecf20Sopenharmony_ci * the physical active state, since we will otherwise never be 1108c2ecf20Sopenharmony_ci * told when the interrupt becomes asserted again. 1118c2ecf20Sopenharmony_ci */ 1128c2ecf20Sopenharmony_ci if (vgic_irq_is_mapped_level(irq) && (val & GICH_LR_PENDING_BIT)) { 1138c2ecf20Sopenharmony_ci irq->line_level = vgic_get_phys_line_level(irq); 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci if (!irq->line_level) 1168c2ecf20Sopenharmony_ci vgic_irq_set_phys_active(irq, false); 1178c2ecf20Sopenharmony_ci } 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci raw_spin_unlock(&irq->irq_lock); 1208c2ecf20Sopenharmony_ci vgic_put_irq(vcpu->kvm, irq); 1218c2ecf20Sopenharmony_ci } 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci cpuif->used_lrs = 0; 1248c2ecf20Sopenharmony_ci} 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci/* 1278c2ecf20Sopenharmony_ci * Populates the particular LR with the state of a given IRQ: 1288c2ecf20Sopenharmony_ci * - for an edge sensitive IRQ the pending state is cleared in struct vgic_irq 1298c2ecf20Sopenharmony_ci * - for a level sensitive IRQ the pending state value is unchanged; 1308c2ecf20Sopenharmony_ci * it is dictated directly by the input level 1318c2ecf20Sopenharmony_ci * 1328c2ecf20Sopenharmony_ci * If @irq describes an SGI with multiple sources, we choose the 1338c2ecf20Sopenharmony_ci * lowest-numbered source VCPU and clear that bit in the source bitmap. 1348c2ecf20Sopenharmony_ci * 1358c2ecf20Sopenharmony_ci * The irq_lock must be held by the caller. 1368c2ecf20Sopenharmony_ci */ 1378c2ecf20Sopenharmony_civoid vgic_v2_populate_lr(struct kvm_vcpu *vcpu, struct vgic_irq *irq, int lr) 1388c2ecf20Sopenharmony_ci{ 1398c2ecf20Sopenharmony_ci u32 val = irq->intid; 1408c2ecf20Sopenharmony_ci bool allow_pending = true; 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci if (irq->active) { 1438c2ecf20Sopenharmony_ci val |= GICH_LR_ACTIVE_BIT; 1448c2ecf20Sopenharmony_ci if (vgic_irq_is_sgi(irq->intid)) 1458c2ecf20Sopenharmony_ci val |= irq->active_source << GICH_LR_PHYSID_CPUID_SHIFT; 1468c2ecf20Sopenharmony_ci if (vgic_irq_is_multi_sgi(irq)) { 1478c2ecf20Sopenharmony_ci allow_pending = false; 1488c2ecf20Sopenharmony_ci val |= GICH_LR_EOI; 1498c2ecf20Sopenharmony_ci } 1508c2ecf20Sopenharmony_ci } 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci if (irq->group) 1538c2ecf20Sopenharmony_ci val |= GICH_LR_GROUP1; 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci if (irq->hw) { 1568c2ecf20Sopenharmony_ci val |= GICH_LR_HW; 1578c2ecf20Sopenharmony_ci val |= irq->hwintid << GICH_LR_PHYSID_CPUID_SHIFT; 1588c2ecf20Sopenharmony_ci /* 1598c2ecf20Sopenharmony_ci * Never set pending+active on a HW interrupt, as the 1608c2ecf20Sopenharmony_ci * pending state is kept at the physical distributor 1618c2ecf20Sopenharmony_ci * level. 1628c2ecf20Sopenharmony_ci */ 1638c2ecf20Sopenharmony_ci if (irq->active) 1648c2ecf20Sopenharmony_ci allow_pending = false; 1658c2ecf20Sopenharmony_ci } else { 1668c2ecf20Sopenharmony_ci if (irq->config == VGIC_CONFIG_LEVEL) { 1678c2ecf20Sopenharmony_ci val |= GICH_LR_EOI; 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci /* 1708c2ecf20Sopenharmony_ci * Software resampling doesn't work very well 1718c2ecf20Sopenharmony_ci * if we allow P+A, so let's not do that. 1728c2ecf20Sopenharmony_ci */ 1738c2ecf20Sopenharmony_ci if (irq->active) 1748c2ecf20Sopenharmony_ci allow_pending = false; 1758c2ecf20Sopenharmony_ci } 1768c2ecf20Sopenharmony_ci } 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci if (allow_pending && irq_is_pending(irq)) { 1798c2ecf20Sopenharmony_ci val |= GICH_LR_PENDING_BIT; 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci if (irq->config == VGIC_CONFIG_EDGE) 1828c2ecf20Sopenharmony_ci irq->pending_latch = false; 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci if (vgic_irq_is_sgi(irq->intid)) { 1858c2ecf20Sopenharmony_ci u32 src = ffs(irq->source); 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci if (WARN_RATELIMIT(!src, "No SGI source for INTID %d\n", 1888c2ecf20Sopenharmony_ci irq->intid)) 1898c2ecf20Sopenharmony_ci return; 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci val |= (src - 1) << GICH_LR_PHYSID_CPUID_SHIFT; 1928c2ecf20Sopenharmony_ci irq->source &= ~(1 << (src - 1)); 1938c2ecf20Sopenharmony_ci if (irq->source) { 1948c2ecf20Sopenharmony_ci irq->pending_latch = true; 1958c2ecf20Sopenharmony_ci val |= GICH_LR_EOI; 1968c2ecf20Sopenharmony_ci } 1978c2ecf20Sopenharmony_ci } 1988c2ecf20Sopenharmony_ci } 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci /* 2018c2ecf20Sopenharmony_ci * Level-triggered mapped IRQs are special because we only observe 2028c2ecf20Sopenharmony_ci * rising edges as input to the VGIC. We therefore lower the line 2038c2ecf20Sopenharmony_ci * level here, so that we can take new virtual IRQs. See 2048c2ecf20Sopenharmony_ci * vgic_v2_fold_lr_state for more info. 2058c2ecf20Sopenharmony_ci */ 2068c2ecf20Sopenharmony_ci if (vgic_irq_is_mapped_level(irq) && (val & GICH_LR_PENDING_BIT)) 2078c2ecf20Sopenharmony_ci irq->line_level = false; 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci /* The GICv2 LR only holds five bits of priority. */ 2108c2ecf20Sopenharmony_ci val |= (irq->priority >> 3) << GICH_LR_PRIORITY_SHIFT; 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci vcpu->arch.vgic_cpu.vgic_v2.vgic_lr[lr] = val; 2138c2ecf20Sopenharmony_ci} 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_civoid vgic_v2_clear_lr(struct kvm_vcpu *vcpu, int lr) 2168c2ecf20Sopenharmony_ci{ 2178c2ecf20Sopenharmony_ci vcpu->arch.vgic_cpu.vgic_v2.vgic_lr[lr] = 0; 2188c2ecf20Sopenharmony_ci} 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_civoid vgic_v2_set_vmcr(struct kvm_vcpu *vcpu, struct vgic_vmcr *vmcrp) 2218c2ecf20Sopenharmony_ci{ 2228c2ecf20Sopenharmony_ci struct vgic_v2_cpu_if *cpu_if = &vcpu->arch.vgic_cpu.vgic_v2; 2238c2ecf20Sopenharmony_ci u32 vmcr; 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci vmcr = (vmcrp->grpen0 << GICH_VMCR_ENABLE_GRP0_SHIFT) & 2268c2ecf20Sopenharmony_ci GICH_VMCR_ENABLE_GRP0_MASK; 2278c2ecf20Sopenharmony_ci vmcr |= (vmcrp->grpen1 << GICH_VMCR_ENABLE_GRP1_SHIFT) & 2288c2ecf20Sopenharmony_ci GICH_VMCR_ENABLE_GRP1_MASK; 2298c2ecf20Sopenharmony_ci vmcr |= (vmcrp->ackctl << GICH_VMCR_ACK_CTL_SHIFT) & 2308c2ecf20Sopenharmony_ci GICH_VMCR_ACK_CTL_MASK; 2318c2ecf20Sopenharmony_ci vmcr |= (vmcrp->fiqen << GICH_VMCR_FIQ_EN_SHIFT) & 2328c2ecf20Sopenharmony_ci GICH_VMCR_FIQ_EN_MASK; 2338c2ecf20Sopenharmony_ci vmcr |= (vmcrp->cbpr << GICH_VMCR_CBPR_SHIFT) & 2348c2ecf20Sopenharmony_ci GICH_VMCR_CBPR_MASK; 2358c2ecf20Sopenharmony_ci vmcr |= (vmcrp->eoim << GICH_VMCR_EOI_MODE_SHIFT) & 2368c2ecf20Sopenharmony_ci GICH_VMCR_EOI_MODE_MASK; 2378c2ecf20Sopenharmony_ci vmcr |= (vmcrp->abpr << GICH_VMCR_ALIAS_BINPOINT_SHIFT) & 2388c2ecf20Sopenharmony_ci GICH_VMCR_ALIAS_BINPOINT_MASK; 2398c2ecf20Sopenharmony_ci vmcr |= (vmcrp->bpr << GICH_VMCR_BINPOINT_SHIFT) & 2408c2ecf20Sopenharmony_ci GICH_VMCR_BINPOINT_MASK; 2418c2ecf20Sopenharmony_ci vmcr |= ((vmcrp->pmr >> GICV_PMR_PRIORITY_SHIFT) << 2428c2ecf20Sopenharmony_ci GICH_VMCR_PRIMASK_SHIFT) & GICH_VMCR_PRIMASK_MASK; 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci cpu_if->vgic_vmcr = vmcr; 2458c2ecf20Sopenharmony_ci} 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_civoid vgic_v2_get_vmcr(struct kvm_vcpu *vcpu, struct vgic_vmcr *vmcrp) 2488c2ecf20Sopenharmony_ci{ 2498c2ecf20Sopenharmony_ci struct vgic_v2_cpu_if *cpu_if = &vcpu->arch.vgic_cpu.vgic_v2; 2508c2ecf20Sopenharmony_ci u32 vmcr; 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_ci vmcr = cpu_if->vgic_vmcr; 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci vmcrp->grpen0 = (vmcr & GICH_VMCR_ENABLE_GRP0_MASK) >> 2558c2ecf20Sopenharmony_ci GICH_VMCR_ENABLE_GRP0_SHIFT; 2568c2ecf20Sopenharmony_ci vmcrp->grpen1 = (vmcr & GICH_VMCR_ENABLE_GRP1_MASK) >> 2578c2ecf20Sopenharmony_ci GICH_VMCR_ENABLE_GRP1_SHIFT; 2588c2ecf20Sopenharmony_ci vmcrp->ackctl = (vmcr & GICH_VMCR_ACK_CTL_MASK) >> 2598c2ecf20Sopenharmony_ci GICH_VMCR_ACK_CTL_SHIFT; 2608c2ecf20Sopenharmony_ci vmcrp->fiqen = (vmcr & GICH_VMCR_FIQ_EN_MASK) >> 2618c2ecf20Sopenharmony_ci GICH_VMCR_FIQ_EN_SHIFT; 2628c2ecf20Sopenharmony_ci vmcrp->cbpr = (vmcr & GICH_VMCR_CBPR_MASK) >> 2638c2ecf20Sopenharmony_ci GICH_VMCR_CBPR_SHIFT; 2648c2ecf20Sopenharmony_ci vmcrp->eoim = (vmcr & GICH_VMCR_EOI_MODE_MASK) >> 2658c2ecf20Sopenharmony_ci GICH_VMCR_EOI_MODE_SHIFT; 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_ci vmcrp->abpr = (vmcr & GICH_VMCR_ALIAS_BINPOINT_MASK) >> 2688c2ecf20Sopenharmony_ci GICH_VMCR_ALIAS_BINPOINT_SHIFT; 2698c2ecf20Sopenharmony_ci vmcrp->bpr = (vmcr & GICH_VMCR_BINPOINT_MASK) >> 2708c2ecf20Sopenharmony_ci GICH_VMCR_BINPOINT_SHIFT; 2718c2ecf20Sopenharmony_ci vmcrp->pmr = ((vmcr & GICH_VMCR_PRIMASK_MASK) >> 2728c2ecf20Sopenharmony_ci GICH_VMCR_PRIMASK_SHIFT) << GICV_PMR_PRIORITY_SHIFT; 2738c2ecf20Sopenharmony_ci} 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_civoid vgic_v2_enable(struct kvm_vcpu *vcpu) 2768c2ecf20Sopenharmony_ci{ 2778c2ecf20Sopenharmony_ci /* 2788c2ecf20Sopenharmony_ci * By forcing VMCR to zero, the GIC will restore the binary 2798c2ecf20Sopenharmony_ci * points to their reset values. Anything else resets to zero 2808c2ecf20Sopenharmony_ci * anyway. 2818c2ecf20Sopenharmony_ci */ 2828c2ecf20Sopenharmony_ci vcpu->arch.vgic_cpu.vgic_v2.vgic_vmcr = 0; 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ci /* Get the show on the road... */ 2858c2ecf20Sopenharmony_ci vcpu->arch.vgic_cpu.vgic_v2.vgic_hcr = GICH_HCR_EN; 2868c2ecf20Sopenharmony_ci} 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci/* check for overlapping regions and for regions crossing the end of memory */ 2898c2ecf20Sopenharmony_cistatic bool vgic_v2_check_base(gpa_t dist_base, gpa_t cpu_base) 2908c2ecf20Sopenharmony_ci{ 2918c2ecf20Sopenharmony_ci if (dist_base + KVM_VGIC_V2_DIST_SIZE < dist_base) 2928c2ecf20Sopenharmony_ci return false; 2938c2ecf20Sopenharmony_ci if (cpu_base + KVM_VGIC_V2_CPU_SIZE < cpu_base) 2948c2ecf20Sopenharmony_ci return false; 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_ci if (dist_base + KVM_VGIC_V2_DIST_SIZE <= cpu_base) 2978c2ecf20Sopenharmony_ci return true; 2988c2ecf20Sopenharmony_ci if (cpu_base + KVM_VGIC_V2_CPU_SIZE <= dist_base) 2998c2ecf20Sopenharmony_ci return true; 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci return false; 3028c2ecf20Sopenharmony_ci} 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_ciint vgic_v2_map_resources(struct kvm *kvm) 3058c2ecf20Sopenharmony_ci{ 3068c2ecf20Sopenharmony_ci struct vgic_dist *dist = &kvm->arch.vgic; 3078c2ecf20Sopenharmony_ci int ret = 0; 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_ci if (vgic_ready(kvm)) 3108c2ecf20Sopenharmony_ci goto out; 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_ci if (IS_VGIC_ADDR_UNDEF(dist->vgic_dist_base) || 3138c2ecf20Sopenharmony_ci IS_VGIC_ADDR_UNDEF(dist->vgic_cpu_base)) { 3148c2ecf20Sopenharmony_ci kvm_err("Need to set vgic cpu and dist addresses first\n"); 3158c2ecf20Sopenharmony_ci ret = -ENXIO; 3168c2ecf20Sopenharmony_ci goto out; 3178c2ecf20Sopenharmony_ci } 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_ci if (!vgic_v2_check_base(dist->vgic_dist_base, dist->vgic_cpu_base)) { 3208c2ecf20Sopenharmony_ci kvm_err("VGIC CPU and dist frames overlap\n"); 3218c2ecf20Sopenharmony_ci ret = -EINVAL; 3228c2ecf20Sopenharmony_ci goto out; 3238c2ecf20Sopenharmony_ci } 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_ci /* 3268c2ecf20Sopenharmony_ci * Initialize the vgic if this hasn't already been done on demand by 3278c2ecf20Sopenharmony_ci * accessing the vgic state from userspace. 3288c2ecf20Sopenharmony_ci */ 3298c2ecf20Sopenharmony_ci ret = vgic_init(kvm); 3308c2ecf20Sopenharmony_ci if (ret) { 3318c2ecf20Sopenharmony_ci kvm_err("Unable to initialize VGIC dynamic data structures\n"); 3328c2ecf20Sopenharmony_ci goto out; 3338c2ecf20Sopenharmony_ci } 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_ci ret = vgic_register_dist_iodev(kvm, dist->vgic_dist_base, VGIC_V2); 3368c2ecf20Sopenharmony_ci if (ret) { 3378c2ecf20Sopenharmony_ci kvm_err("Unable to register VGIC MMIO regions\n"); 3388c2ecf20Sopenharmony_ci goto out; 3398c2ecf20Sopenharmony_ci } 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_ci if (!static_branch_unlikely(&vgic_v2_cpuif_trap)) { 3428c2ecf20Sopenharmony_ci ret = kvm_phys_addr_ioremap(kvm, dist->vgic_cpu_base, 3438c2ecf20Sopenharmony_ci kvm_vgic_global_state.vcpu_base, 3448c2ecf20Sopenharmony_ci KVM_VGIC_V2_CPU_SIZE, true); 3458c2ecf20Sopenharmony_ci if (ret) { 3468c2ecf20Sopenharmony_ci kvm_err("Unable to remap VGIC CPU to VCPU\n"); 3478c2ecf20Sopenharmony_ci goto out; 3488c2ecf20Sopenharmony_ci } 3498c2ecf20Sopenharmony_ci } 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_ci dist->ready = true; 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_ciout: 3548c2ecf20Sopenharmony_ci return ret; 3558c2ecf20Sopenharmony_ci} 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_ciDEFINE_STATIC_KEY_FALSE(vgic_v2_cpuif_trap); 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_ci/** 3608c2ecf20Sopenharmony_ci * vgic_v2_probe - probe for a VGICv2 compatible interrupt controller 3618c2ecf20Sopenharmony_ci * @info: pointer to the GIC description 3628c2ecf20Sopenharmony_ci * 3638c2ecf20Sopenharmony_ci * Returns 0 if the VGICv2 has been probed successfully, returns an error code 3648c2ecf20Sopenharmony_ci * otherwise 3658c2ecf20Sopenharmony_ci */ 3668c2ecf20Sopenharmony_ciint vgic_v2_probe(const struct gic_kvm_info *info) 3678c2ecf20Sopenharmony_ci{ 3688c2ecf20Sopenharmony_ci int ret; 3698c2ecf20Sopenharmony_ci u32 vtr; 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_ci if (!info->vctrl.start) { 3728c2ecf20Sopenharmony_ci kvm_err("GICH not present in the firmware table\n"); 3738c2ecf20Sopenharmony_ci return -ENXIO; 3748c2ecf20Sopenharmony_ci } 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_ci if (!PAGE_ALIGNED(info->vcpu.start) || 3778c2ecf20Sopenharmony_ci !PAGE_ALIGNED(resource_size(&info->vcpu))) { 3788c2ecf20Sopenharmony_ci kvm_info("GICV region size/alignment is unsafe, using trapping (reduced performance)\n"); 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_ci ret = create_hyp_io_mappings(info->vcpu.start, 3818c2ecf20Sopenharmony_ci resource_size(&info->vcpu), 3828c2ecf20Sopenharmony_ci &kvm_vgic_global_state.vcpu_base_va, 3838c2ecf20Sopenharmony_ci &kvm_vgic_global_state.vcpu_hyp_va); 3848c2ecf20Sopenharmony_ci if (ret) { 3858c2ecf20Sopenharmony_ci kvm_err("Cannot map GICV into hyp\n"); 3868c2ecf20Sopenharmony_ci goto out; 3878c2ecf20Sopenharmony_ci } 3888c2ecf20Sopenharmony_ci 3898c2ecf20Sopenharmony_ci static_branch_enable(&vgic_v2_cpuif_trap); 3908c2ecf20Sopenharmony_ci } 3918c2ecf20Sopenharmony_ci 3928c2ecf20Sopenharmony_ci ret = create_hyp_io_mappings(info->vctrl.start, 3938c2ecf20Sopenharmony_ci resource_size(&info->vctrl), 3948c2ecf20Sopenharmony_ci &kvm_vgic_global_state.vctrl_base, 3958c2ecf20Sopenharmony_ci &kvm_vgic_global_state.vctrl_hyp); 3968c2ecf20Sopenharmony_ci if (ret) { 3978c2ecf20Sopenharmony_ci kvm_err("Cannot map VCTRL into hyp\n"); 3988c2ecf20Sopenharmony_ci goto out; 3998c2ecf20Sopenharmony_ci } 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_ci vtr = readl_relaxed(kvm_vgic_global_state.vctrl_base + GICH_VTR); 4028c2ecf20Sopenharmony_ci kvm_vgic_global_state.nr_lr = (vtr & 0x3f) + 1; 4038c2ecf20Sopenharmony_ci 4048c2ecf20Sopenharmony_ci ret = kvm_register_vgic_device(KVM_DEV_TYPE_ARM_VGIC_V2); 4058c2ecf20Sopenharmony_ci if (ret) { 4068c2ecf20Sopenharmony_ci kvm_err("Cannot register GICv2 KVM device\n"); 4078c2ecf20Sopenharmony_ci goto out; 4088c2ecf20Sopenharmony_ci } 4098c2ecf20Sopenharmony_ci 4108c2ecf20Sopenharmony_ci kvm_vgic_global_state.can_emulate_gicv2 = true; 4118c2ecf20Sopenharmony_ci kvm_vgic_global_state.vcpu_base = info->vcpu.start; 4128c2ecf20Sopenharmony_ci kvm_vgic_global_state.type = VGIC_V2; 4138c2ecf20Sopenharmony_ci kvm_vgic_global_state.max_gic_vcpus = VGIC_V2_MAX_CPUS; 4148c2ecf20Sopenharmony_ci 4158c2ecf20Sopenharmony_ci kvm_debug("vgic-v2@%llx\n", info->vctrl.start); 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_ci return 0; 4188c2ecf20Sopenharmony_ciout: 4198c2ecf20Sopenharmony_ci if (kvm_vgic_global_state.vctrl_base) 4208c2ecf20Sopenharmony_ci iounmap(kvm_vgic_global_state.vctrl_base); 4218c2ecf20Sopenharmony_ci if (kvm_vgic_global_state.vcpu_base_va) 4228c2ecf20Sopenharmony_ci iounmap(kvm_vgic_global_state.vcpu_base_va); 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_ci return ret; 4258c2ecf20Sopenharmony_ci} 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_cistatic void save_lrs(struct kvm_vcpu *vcpu, void __iomem *base) 4288c2ecf20Sopenharmony_ci{ 4298c2ecf20Sopenharmony_ci struct vgic_v2_cpu_if *cpu_if = &vcpu->arch.vgic_cpu.vgic_v2; 4308c2ecf20Sopenharmony_ci u64 used_lrs = cpu_if->used_lrs; 4318c2ecf20Sopenharmony_ci u64 elrsr; 4328c2ecf20Sopenharmony_ci int i; 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_ci elrsr = readl_relaxed(base + GICH_ELRSR0); 4358c2ecf20Sopenharmony_ci if (unlikely(used_lrs > 32)) 4368c2ecf20Sopenharmony_ci elrsr |= ((u64)readl_relaxed(base + GICH_ELRSR1)) << 32; 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_ci for (i = 0; i < used_lrs; i++) { 4398c2ecf20Sopenharmony_ci if (elrsr & (1UL << i)) 4408c2ecf20Sopenharmony_ci cpu_if->vgic_lr[i] &= ~GICH_LR_STATE; 4418c2ecf20Sopenharmony_ci else 4428c2ecf20Sopenharmony_ci cpu_if->vgic_lr[i] = readl_relaxed(base + GICH_LR0 + (i * 4)); 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_ci writel_relaxed(0, base + GICH_LR0 + (i * 4)); 4458c2ecf20Sopenharmony_ci } 4468c2ecf20Sopenharmony_ci} 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_civoid vgic_v2_save_state(struct kvm_vcpu *vcpu) 4498c2ecf20Sopenharmony_ci{ 4508c2ecf20Sopenharmony_ci void __iomem *base = kvm_vgic_global_state.vctrl_base; 4518c2ecf20Sopenharmony_ci u64 used_lrs = vcpu->arch.vgic_cpu.vgic_v2.used_lrs; 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_ci if (!base) 4548c2ecf20Sopenharmony_ci return; 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_ci if (used_lrs) { 4578c2ecf20Sopenharmony_ci save_lrs(vcpu, base); 4588c2ecf20Sopenharmony_ci writel_relaxed(0, base + GICH_HCR); 4598c2ecf20Sopenharmony_ci } 4608c2ecf20Sopenharmony_ci} 4618c2ecf20Sopenharmony_ci 4628c2ecf20Sopenharmony_civoid vgic_v2_restore_state(struct kvm_vcpu *vcpu) 4638c2ecf20Sopenharmony_ci{ 4648c2ecf20Sopenharmony_ci struct vgic_v2_cpu_if *cpu_if = &vcpu->arch.vgic_cpu.vgic_v2; 4658c2ecf20Sopenharmony_ci void __iomem *base = kvm_vgic_global_state.vctrl_base; 4668c2ecf20Sopenharmony_ci u64 used_lrs = cpu_if->used_lrs; 4678c2ecf20Sopenharmony_ci int i; 4688c2ecf20Sopenharmony_ci 4698c2ecf20Sopenharmony_ci if (!base) 4708c2ecf20Sopenharmony_ci return; 4718c2ecf20Sopenharmony_ci 4728c2ecf20Sopenharmony_ci if (used_lrs) { 4738c2ecf20Sopenharmony_ci writel_relaxed(cpu_if->vgic_hcr, base + GICH_HCR); 4748c2ecf20Sopenharmony_ci for (i = 0; i < used_lrs; i++) { 4758c2ecf20Sopenharmony_ci writel_relaxed(cpu_if->vgic_lr[i], 4768c2ecf20Sopenharmony_ci base + GICH_LR0 + (i * 4)); 4778c2ecf20Sopenharmony_ci } 4788c2ecf20Sopenharmony_ci } 4798c2ecf20Sopenharmony_ci} 4808c2ecf20Sopenharmony_ci 4818c2ecf20Sopenharmony_civoid vgic_v2_load(struct kvm_vcpu *vcpu) 4828c2ecf20Sopenharmony_ci{ 4838c2ecf20Sopenharmony_ci struct vgic_v2_cpu_if *cpu_if = &vcpu->arch.vgic_cpu.vgic_v2; 4848c2ecf20Sopenharmony_ci 4858c2ecf20Sopenharmony_ci writel_relaxed(cpu_if->vgic_vmcr, 4868c2ecf20Sopenharmony_ci kvm_vgic_global_state.vctrl_base + GICH_VMCR); 4878c2ecf20Sopenharmony_ci writel_relaxed(cpu_if->vgic_apr, 4888c2ecf20Sopenharmony_ci kvm_vgic_global_state.vctrl_base + GICH_APR); 4898c2ecf20Sopenharmony_ci} 4908c2ecf20Sopenharmony_ci 4918c2ecf20Sopenharmony_civoid vgic_v2_vmcr_sync(struct kvm_vcpu *vcpu) 4928c2ecf20Sopenharmony_ci{ 4938c2ecf20Sopenharmony_ci struct vgic_v2_cpu_if *cpu_if = &vcpu->arch.vgic_cpu.vgic_v2; 4948c2ecf20Sopenharmony_ci 4958c2ecf20Sopenharmony_ci cpu_if->vgic_vmcr = readl_relaxed(kvm_vgic_global_state.vctrl_base + GICH_VMCR); 4968c2ecf20Sopenharmony_ci} 4978c2ecf20Sopenharmony_ci 4988c2ecf20Sopenharmony_civoid vgic_v2_put(struct kvm_vcpu *vcpu) 4998c2ecf20Sopenharmony_ci{ 5008c2ecf20Sopenharmony_ci struct vgic_v2_cpu_if *cpu_if = &vcpu->arch.vgic_cpu.vgic_v2; 5018c2ecf20Sopenharmony_ci 5028c2ecf20Sopenharmony_ci vgic_v2_vmcr_sync(vcpu); 5038c2ecf20Sopenharmony_ci cpu_if->vgic_apr = readl_relaxed(kvm_vgic_global_state.vctrl_base + GICH_APR); 5048c2ecf20Sopenharmony_ci} 505