162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * VGIC MMIO handling functions 462306a36Sopenharmony_ci */ 562306a36Sopenharmony_ci 662306a36Sopenharmony_ci#include <linux/bitops.h> 762306a36Sopenharmony_ci#include <linux/bsearch.h> 862306a36Sopenharmony_ci#include <linux/interrupt.h> 962306a36Sopenharmony_ci#include <linux/irq.h> 1062306a36Sopenharmony_ci#include <linux/kvm.h> 1162306a36Sopenharmony_ci#include <linux/kvm_host.h> 1262306a36Sopenharmony_ci#include <kvm/iodev.h> 1362306a36Sopenharmony_ci#include <kvm/arm_arch_timer.h> 1462306a36Sopenharmony_ci#include <kvm/arm_vgic.h> 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci#include "vgic.h" 1762306a36Sopenharmony_ci#include "vgic-mmio.h" 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ciunsigned long vgic_mmio_read_raz(struct kvm_vcpu *vcpu, 2062306a36Sopenharmony_ci gpa_t addr, unsigned int len) 2162306a36Sopenharmony_ci{ 2262306a36Sopenharmony_ci return 0; 2362306a36Sopenharmony_ci} 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ciunsigned long vgic_mmio_read_rao(struct kvm_vcpu *vcpu, 2662306a36Sopenharmony_ci gpa_t addr, unsigned int len) 2762306a36Sopenharmony_ci{ 2862306a36Sopenharmony_ci return -1UL; 2962306a36Sopenharmony_ci} 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_civoid vgic_mmio_write_wi(struct kvm_vcpu *vcpu, gpa_t addr, 3262306a36Sopenharmony_ci unsigned int len, unsigned long val) 3362306a36Sopenharmony_ci{ 3462306a36Sopenharmony_ci /* Ignore */ 3562306a36Sopenharmony_ci} 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ciint vgic_mmio_uaccess_write_wi(struct kvm_vcpu *vcpu, gpa_t addr, 3862306a36Sopenharmony_ci unsigned int len, unsigned long val) 3962306a36Sopenharmony_ci{ 4062306a36Sopenharmony_ci /* Ignore */ 4162306a36Sopenharmony_ci return 0; 4262306a36Sopenharmony_ci} 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ciunsigned long vgic_mmio_read_group(struct kvm_vcpu *vcpu, 4562306a36Sopenharmony_ci gpa_t addr, unsigned int len) 4662306a36Sopenharmony_ci{ 4762306a36Sopenharmony_ci u32 intid = VGIC_ADDR_TO_INTID(addr, 1); 4862306a36Sopenharmony_ci u32 value = 0; 4962306a36Sopenharmony_ci int i; 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci /* Loop over all IRQs affected by this read */ 5262306a36Sopenharmony_ci for (i = 0; i < len * 8; i++) { 5362306a36Sopenharmony_ci struct vgic_irq *irq = vgic_get_irq(vcpu->kvm, vcpu, intid + i); 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci if (irq->group) 5662306a36Sopenharmony_ci value |= BIT(i); 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci vgic_put_irq(vcpu->kvm, irq); 5962306a36Sopenharmony_ci } 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci return value; 6262306a36Sopenharmony_ci} 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_cistatic void vgic_update_vsgi(struct vgic_irq *irq) 6562306a36Sopenharmony_ci{ 6662306a36Sopenharmony_ci WARN_ON(its_prop_update_vsgi(irq->host_irq, irq->priority, irq->group)); 6762306a36Sopenharmony_ci} 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_civoid vgic_mmio_write_group(struct kvm_vcpu *vcpu, gpa_t addr, 7062306a36Sopenharmony_ci unsigned int len, unsigned long val) 7162306a36Sopenharmony_ci{ 7262306a36Sopenharmony_ci u32 intid = VGIC_ADDR_TO_INTID(addr, 1); 7362306a36Sopenharmony_ci int i; 7462306a36Sopenharmony_ci unsigned long flags; 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci for (i = 0; i < len * 8; i++) { 7762306a36Sopenharmony_ci struct vgic_irq *irq = vgic_get_irq(vcpu->kvm, vcpu, intid + i); 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci raw_spin_lock_irqsave(&irq->irq_lock, flags); 8062306a36Sopenharmony_ci irq->group = !!(val & BIT(i)); 8162306a36Sopenharmony_ci if (irq->hw && vgic_irq_is_sgi(irq->intid)) { 8262306a36Sopenharmony_ci vgic_update_vsgi(irq); 8362306a36Sopenharmony_ci raw_spin_unlock_irqrestore(&irq->irq_lock, flags); 8462306a36Sopenharmony_ci } else { 8562306a36Sopenharmony_ci vgic_queue_irq_unlock(vcpu->kvm, irq, flags); 8662306a36Sopenharmony_ci } 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci vgic_put_irq(vcpu->kvm, irq); 8962306a36Sopenharmony_ci } 9062306a36Sopenharmony_ci} 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci/* 9362306a36Sopenharmony_ci * Read accesses to both GICD_ICENABLER and GICD_ISENABLER return the value 9462306a36Sopenharmony_ci * of the enabled bit, so there is only one function for both here. 9562306a36Sopenharmony_ci */ 9662306a36Sopenharmony_ciunsigned long vgic_mmio_read_enable(struct kvm_vcpu *vcpu, 9762306a36Sopenharmony_ci gpa_t addr, unsigned int len) 9862306a36Sopenharmony_ci{ 9962306a36Sopenharmony_ci u32 intid = VGIC_ADDR_TO_INTID(addr, 1); 10062306a36Sopenharmony_ci u32 value = 0; 10162306a36Sopenharmony_ci int i; 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci /* Loop over all IRQs affected by this read */ 10462306a36Sopenharmony_ci for (i = 0; i < len * 8; i++) { 10562306a36Sopenharmony_ci struct vgic_irq *irq = vgic_get_irq(vcpu->kvm, vcpu, intid + i); 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci if (irq->enabled) 10862306a36Sopenharmony_ci value |= (1U << i); 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci vgic_put_irq(vcpu->kvm, irq); 11162306a36Sopenharmony_ci } 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci return value; 11462306a36Sopenharmony_ci} 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_civoid vgic_mmio_write_senable(struct kvm_vcpu *vcpu, 11762306a36Sopenharmony_ci gpa_t addr, unsigned int len, 11862306a36Sopenharmony_ci unsigned long val) 11962306a36Sopenharmony_ci{ 12062306a36Sopenharmony_ci u32 intid = VGIC_ADDR_TO_INTID(addr, 1); 12162306a36Sopenharmony_ci int i; 12262306a36Sopenharmony_ci unsigned long flags; 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci for_each_set_bit(i, &val, len * 8) { 12562306a36Sopenharmony_ci struct vgic_irq *irq = vgic_get_irq(vcpu->kvm, vcpu, intid + i); 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci raw_spin_lock_irqsave(&irq->irq_lock, flags); 12862306a36Sopenharmony_ci if (irq->hw && vgic_irq_is_sgi(irq->intid)) { 12962306a36Sopenharmony_ci if (!irq->enabled) { 13062306a36Sopenharmony_ci struct irq_data *data; 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci irq->enabled = true; 13362306a36Sopenharmony_ci data = &irq_to_desc(irq->host_irq)->irq_data; 13462306a36Sopenharmony_ci while (irqd_irq_disabled(data)) 13562306a36Sopenharmony_ci enable_irq(irq->host_irq); 13662306a36Sopenharmony_ci } 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci raw_spin_unlock_irqrestore(&irq->irq_lock, flags); 13962306a36Sopenharmony_ci vgic_put_irq(vcpu->kvm, irq); 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci continue; 14262306a36Sopenharmony_ci } else if (vgic_irq_is_mapped_level(irq)) { 14362306a36Sopenharmony_ci bool was_high = irq->line_level; 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci /* 14662306a36Sopenharmony_ci * We need to update the state of the interrupt because 14762306a36Sopenharmony_ci * the guest might have changed the state of the device 14862306a36Sopenharmony_ci * while the interrupt was disabled at the VGIC level. 14962306a36Sopenharmony_ci */ 15062306a36Sopenharmony_ci irq->line_level = vgic_get_phys_line_level(irq); 15162306a36Sopenharmony_ci /* 15262306a36Sopenharmony_ci * Deactivate the physical interrupt so the GIC will let 15362306a36Sopenharmony_ci * us know when it is asserted again. 15462306a36Sopenharmony_ci */ 15562306a36Sopenharmony_ci if (!irq->active && was_high && !irq->line_level) 15662306a36Sopenharmony_ci vgic_irq_set_phys_active(irq, false); 15762306a36Sopenharmony_ci } 15862306a36Sopenharmony_ci irq->enabled = true; 15962306a36Sopenharmony_ci vgic_queue_irq_unlock(vcpu->kvm, irq, flags); 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci vgic_put_irq(vcpu->kvm, irq); 16262306a36Sopenharmony_ci } 16362306a36Sopenharmony_ci} 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_civoid vgic_mmio_write_cenable(struct kvm_vcpu *vcpu, 16662306a36Sopenharmony_ci gpa_t addr, unsigned int len, 16762306a36Sopenharmony_ci unsigned long val) 16862306a36Sopenharmony_ci{ 16962306a36Sopenharmony_ci u32 intid = VGIC_ADDR_TO_INTID(addr, 1); 17062306a36Sopenharmony_ci int i; 17162306a36Sopenharmony_ci unsigned long flags; 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci for_each_set_bit(i, &val, len * 8) { 17462306a36Sopenharmony_ci struct vgic_irq *irq = vgic_get_irq(vcpu->kvm, vcpu, intid + i); 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci raw_spin_lock_irqsave(&irq->irq_lock, flags); 17762306a36Sopenharmony_ci if (irq->hw && vgic_irq_is_sgi(irq->intid) && irq->enabled) 17862306a36Sopenharmony_ci disable_irq_nosync(irq->host_irq); 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci irq->enabled = false; 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci raw_spin_unlock_irqrestore(&irq->irq_lock, flags); 18362306a36Sopenharmony_ci vgic_put_irq(vcpu->kvm, irq); 18462306a36Sopenharmony_ci } 18562306a36Sopenharmony_ci} 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ciint vgic_uaccess_write_senable(struct kvm_vcpu *vcpu, 18862306a36Sopenharmony_ci gpa_t addr, unsigned int len, 18962306a36Sopenharmony_ci unsigned long val) 19062306a36Sopenharmony_ci{ 19162306a36Sopenharmony_ci u32 intid = VGIC_ADDR_TO_INTID(addr, 1); 19262306a36Sopenharmony_ci int i; 19362306a36Sopenharmony_ci unsigned long flags; 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci for_each_set_bit(i, &val, len * 8) { 19662306a36Sopenharmony_ci struct vgic_irq *irq = vgic_get_irq(vcpu->kvm, vcpu, intid + i); 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci raw_spin_lock_irqsave(&irq->irq_lock, flags); 19962306a36Sopenharmony_ci irq->enabled = true; 20062306a36Sopenharmony_ci vgic_queue_irq_unlock(vcpu->kvm, irq, flags); 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci vgic_put_irq(vcpu->kvm, irq); 20362306a36Sopenharmony_ci } 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci return 0; 20662306a36Sopenharmony_ci} 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ciint vgic_uaccess_write_cenable(struct kvm_vcpu *vcpu, 20962306a36Sopenharmony_ci gpa_t addr, unsigned int len, 21062306a36Sopenharmony_ci unsigned long val) 21162306a36Sopenharmony_ci{ 21262306a36Sopenharmony_ci u32 intid = VGIC_ADDR_TO_INTID(addr, 1); 21362306a36Sopenharmony_ci int i; 21462306a36Sopenharmony_ci unsigned long flags; 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci for_each_set_bit(i, &val, len * 8) { 21762306a36Sopenharmony_ci struct vgic_irq *irq = vgic_get_irq(vcpu->kvm, vcpu, intid + i); 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci raw_spin_lock_irqsave(&irq->irq_lock, flags); 22062306a36Sopenharmony_ci irq->enabled = false; 22162306a36Sopenharmony_ci raw_spin_unlock_irqrestore(&irq->irq_lock, flags); 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci vgic_put_irq(vcpu->kvm, irq); 22462306a36Sopenharmony_ci } 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci return 0; 22762306a36Sopenharmony_ci} 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_cistatic unsigned long __read_pending(struct kvm_vcpu *vcpu, 23062306a36Sopenharmony_ci gpa_t addr, unsigned int len, 23162306a36Sopenharmony_ci bool is_user) 23262306a36Sopenharmony_ci{ 23362306a36Sopenharmony_ci u32 intid = VGIC_ADDR_TO_INTID(addr, 1); 23462306a36Sopenharmony_ci u32 value = 0; 23562306a36Sopenharmony_ci int i; 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci /* Loop over all IRQs affected by this read */ 23862306a36Sopenharmony_ci for (i = 0; i < len * 8; i++) { 23962306a36Sopenharmony_ci struct vgic_irq *irq = vgic_get_irq(vcpu->kvm, vcpu, intid + i); 24062306a36Sopenharmony_ci unsigned long flags; 24162306a36Sopenharmony_ci bool val; 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci /* 24462306a36Sopenharmony_ci * When used from userspace with a GICv3 model: 24562306a36Sopenharmony_ci * 24662306a36Sopenharmony_ci * Pending state of interrupt is latched in pending_latch 24762306a36Sopenharmony_ci * variable. Userspace will save and restore pending state 24862306a36Sopenharmony_ci * and line_level separately. 24962306a36Sopenharmony_ci * Refer to Documentation/virt/kvm/devices/arm-vgic-v3.rst 25062306a36Sopenharmony_ci * for handling of ISPENDR and ICPENDR. 25162306a36Sopenharmony_ci */ 25262306a36Sopenharmony_ci raw_spin_lock_irqsave(&irq->irq_lock, flags); 25362306a36Sopenharmony_ci if (irq->hw && vgic_irq_is_sgi(irq->intid)) { 25462306a36Sopenharmony_ci int err; 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci val = false; 25762306a36Sopenharmony_ci err = irq_get_irqchip_state(irq->host_irq, 25862306a36Sopenharmony_ci IRQCHIP_STATE_PENDING, 25962306a36Sopenharmony_ci &val); 26062306a36Sopenharmony_ci WARN_RATELIMIT(err, "IRQ %d", irq->host_irq); 26162306a36Sopenharmony_ci } else if (!is_user && vgic_irq_is_mapped_level(irq)) { 26262306a36Sopenharmony_ci val = vgic_get_phys_line_level(irq); 26362306a36Sopenharmony_ci } else { 26462306a36Sopenharmony_ci switch (vcpu->kvm->arch.vgic.vgic_model) { 26562306a36Sopenharmony_ci case KVM_DEV_TYPE_ARM_VGIC_V3: 26662306a36Sopenharmony_ci if (is_user) { 26762306a36Sopenharmony_ci val = irq->pending_latch; 26862306a36Sopenharmony_ci break; 26962306a36Sopenharmony_ci } 27062306a36Sopenharmony_ci fallthrough; 27162306a36Sopenharmony_ci default: 27262306a36Sopenharmony_ci val = irq_is_pending(irq); 27362306a36Sopenharmony_ci break; 27462306a36Sopenharmony_ci } 27562306a36Sopenharmony_ci } 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci value |= ((u32)val << i); 27862306a36Sopenharmony_ci raw_spin_unlock_irqrestore(&irq->irq_lock, flags); 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ci vgic_put_irq(vcpu->kvm, irq); 28162306a36Sopenharmony_ci } 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci return value; 28462306a36Sopenharmony_ci} 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ciunsigned long vgic_mmio_read_pending(struct kvm_vcpu *vcpu, 28762306a36Sopenharmony_ci gpa_t addr, unsigned int len) 28862306a36Sopenharmony_ci{ 28962306a36Sopenharmony_ci return __read_pending(vcpu, addr, len, false); 29062306a36Sopenharmony_ci} 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ciunsigned long vgic_uaccess_read_pending(struct kvm_vcpu *vcpu, 29362306a36Sopenharmony_ci gpa_t addr, unsigned int len) 29462306a36Sopenharmony_ci{ 29562306a36Sopenharmony_ci return __read_pending(vcpu, addr, len, true); 29662306a36Sopenharmony_ci} 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_cistatic bool is_vgic_v2_sgi(struct kvm_vcpu *vcpu, struct vgic_irq *irq) 29962306a36Sopenharmony_ci{ 30062306a36Sopenharmony_ci return (vgic_irq_is_sgi(irq->intid) && 30162306a36Sopenharmony_ci vcpu->kvm->arch.vgic.vgic_model == KVM_DEV_TYPE_ARM_VGIC_V2); 30262306a36Sopenharmony_ci} 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_civoid vgic_mmio_write_spending(struct kvm_vcpu *vcpu, 30562306a36Sopenharmony_ci gpa_t addr, unsigned int len, 30662306a36Sopenharmony_ci unsigned long val) 30762306a36Sopenharmony_ci{ 30862306a36Sopenharmony_ci u32 intid = VGIC_ADDR_TO_INTID(addr, 1); 30962306a36Sopenharmony_ci int i; 31062306a36Sopenharmony_ci unsigned long flags; 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_ci for_each_set_bit(i, &val, len * 8) { 31362306a36Sopenharmony_ci struct vgic_irq *irq = vgic_get_irq(vcpu->kvm, vcpu, intid + i); 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ci /* GICD_ISPENDR0 SGI bits are WI */ 31662306a36Sopenharmony_ci if (is_vgic_v2_sgi(vcpu, irq)) { 31762306a36Sopenharmony_ci vgic_put_irq(vcpu->kvm, irq); 31862306a36Sopenharmony_ci continue; 31962306a36Sopenharmony_ci } 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_ci raw_spin_lock_irqsave(&irq->irq_lock, flags); 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ci if (irq->hw && vgic_irq_is_sgi(irq->intid)) { 32462306a36Sopenharmony_ci /* HW SGI? Ask the GIC to inject it */ 32562306a36Sopenharmony_ci int err; 32662306a36Sopenharmony_ci err = irq_set_irqchip_state(irq->host_irq, 32762306a36Sopenharmony_ci IRQCHIP_STATE_PENDING, 32862306a36Sopenharmony_ci true); 32962306a36Sopenharmony_ci WARN_RATELIMIT(err, "IRQ %d", irq->host_irq); 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ci raw_spin_unlock_irqrestore(&irq->irq_lock, flags); 33262306a36Sopenharmony_ci vgic_put_irq(vcpu->kvm, irq); 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ci continue; 33562306a36Sopenharmony_ci } 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_ci irq->pending_latch = true; 33862306a36Sopenharmony_ci if (irq->hw) 33962306a36Sopenharmony_ci vgic_irq_set_phys_active(irq, true); 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci vgic_queue_irq_unlock(vcpu->kvm, irq, flags); 34262306a36Sopenharmony_ci vgic_put_irq(vcpu->kvm, irq); 34362306a36Sopenharmony_ci } 34462306a36Sopenharmony_ci} 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_ciint vgic_uaccess_write_spending(struct kvm_vcpu *vcpu, 34762306a36Sopenharmony_ci gpa_t addr, unsigned int len, 34862306a36Sopenharmony_ci unsigned long val) 34962306a36Sopenharmony_ci{ 35062306a36Sopenharmony_ci u32 intid = VGIC_ADDR_TO_INTID(addr, 1); 35162306a36Sopenharmony_ci int i; 35262306a36Sopenharmony_ci unsigned long flags; 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_ci for_each_set_bit(i, &val, len * 8) { 35562306a36Sopenharmony_ci struct vgic_irq *irq = vgic_get_irq(vcpu->kvm, vcpu, intid + i); 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_ci raw_spin_lock_irqsave(&irq->irq_lock, flags); 35862306a36Sopenharmony_ci irq->pending_latch = true; 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_ci /* 36162306a36Sopenharmony_ci * GICv2 SGIs are terribly broken. We can't restore 36262306a36Sopenharmony_ci * the source of the interrupt, so just pick the vcpu 36362306a36Sopenharmony_ci * itself as the source... 36462306a36Sopenharmony_ci */ 36562306a36Sopenharmony_ci if (is_vgic_v2_sgi(vcpu, irq)) 36662306a36Sopenharmony_ci irq->source |= BIT(vcpu->vcpu_id); 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_ci vgic_queue_irq_unlock(vcpu->kvm, irq, flags); 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_ci vgic_put_irq(vcpu->kvm, irq); 37162306a36Sopenharmony_ci } 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_ci return 0; 37462306a36Sopenharmony_ci} 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_ci/* Must be called with irq->irq_lock held */ 37762306a36Sopenharmony_cistatic void vgic_hw_irq_cpending(struct kvm_vcpu *vcpu, struct vgic_irq *irq) 37862306a36Sopenharmony_ci{ 37962306a36Sopenharmony_ci irq->pending_latch = false; 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_ci /* 38262306a36Sopenharmony_ci * We don't want the guest to effectively mask the physical 38362306a36Sopenharmony_ci * interrupt by doing a write to SPENDR followed by a write to 38462306a36Sopenharmony_ci * CPENDR for HW interrupts, so we clear the active state on 38562306a36Sopenharmony_ci * the physical side if the virtual interrupt is not active. 38662306a36Sopenharmony_ci * This may lead to taking an additional interrupt on the 38762306a36Sopenharmony_ci * host, but that should not be a problem as the worst that 38862306a36Sopenharmony_ci * can happen is an additional vgic injection. We also clear 38962306a36Sopenharmony_ci * the pending state to maintain proper semantics for edge HW 39062306a36Sopenharmony_ci * interrupts. 39162306a36Sopenharmony_ci */ 39262306a36Sopenharmony_ci vgic_irq_set_phys_pending(irq, false); 39362306a36Sopenharmony_ci if (!irq->active) 39462306a36Sopenharmony_ci vgic_irq_set_phys_active(irq, false); 39562306a36Sopenharmony_ci} 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_civoid vgic_mmio_write_cpending(struct kvm_vcpu *vcpu, 39862306a36Sopenharmony_ci gpa_t addr, unsigned int len, 39962306a36Sopenharmony_ci unsigned long val) 40062306a36Sopenharmony_ci{ 40162306a36Sopenharmony_ci u32 intid = VGIC_ADDR_TO_INTID(addr, 1); 40262306a36Sopenharmony_ci int i; 40362306a36Sopenharmony_ci unsigned long flags; 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_ci for_each_set_bit(i, &val, len * 8) { 40662306a36Sopenharmony_ci struct vgic_irq *irq = vgic_get_irq(vcpu->kvm, vcpu, intid + i); 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_ci /* GICD_ICPENDR0 SGI bits are WI */ 40962306a36Sopenharmony_ci if (is_vgic_v2_sgi(vcpu, irq)) { 41062306a36Sopenharmony_ci vgic_put_irq(vcpu->kvm, irq); 41162306a36Sopenharmony_ci continue; 41262306a36Sopenharmony_ci } 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_ci raw_spin_lock_irqsave(&irq->irq_lock, flags); 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_ci if (irq->hw && vgic_irq_is_sgi(irq->intid)) { 41762306a36Sopenharmony_ci /* HW SGI? Ask the GIC to clear its pending bit */ 41862306a36Sopenharmony_ci int err; 41962306a36Sopenharmony_ci err = irq_set_irqchip_state(irq->host_irq, 42062306a36Sopenharmony_ci IRQCHIP_STATE_PENDING, 42162306a36Sopenharmony_ci false); 42262306a36Sopenharmony_ci WARN_RATELIMIT(err, "IRQ %d", irq->host_irq); 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_ci raw_spin_unlock_irqrestore(&irq->irq_lock, flags); 42562306a36Sopenharmony_ci vgic_put_irq(vcpu->kvm, irq); 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_ci continue; 42862306a36Sopenharmony_ci } 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_ci if (irq->hw) 43162306a36Sopenharmony_ci vgic_hw_irq_cpending(vcpu, irq); 43262306a36Sopenharmony_ci else 43362306a36Sopenharmony_ci irq->pending_latch = false; 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_ci raw_spin_unlock_irqrestore(&irq->irq_lock, flags); 43662306a36Sopenharmony_ci vgic_put_irq(vcpu->kvm, irq); 43762306a36Sopenharmony_ci } 43862306a36Sopenharmony_ci} 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_ciint vgic_uaccess_write_cpending(struct kvm_vcpu *vcpu, 44162306a36Sopenharmony_ci gpa_t addr, unsigned int len, 44262306a36Sopenharmony_ci unsigned long val) 44362306a36Sopenharmony_ci{ 44462306a36Sopenharmony_ci u32 intid = VGIC_ADDR_TO_INTID(addr, 1); 44562306a36Sopenharmony_ci int i; 44662306a36Sopenharmony_ci unsigned long flags; 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_ci for_each_set_bit(i, &val, len * 8) { 44962306a36Sopenharmony_ci struct vgic_irq *irq = vgic_get_irq(vcpu->kvm, vcpu, intid + i); 45062306a36Sopenharmony_ci 45162306a36Sopenharmony_ci raw_spin_lock_irqsave(&irq->irq_lock, flags); 45262306a36Sopenharmony_ci /* 45362306a36Sopenharmony_ci * More fun with GICv2 SGIs! If we're clearing one of them 45462306a36Sopenharmony_ci * from userspace, which source vcpu to clear? Let's not 45562306a36Sopenharmony_ci * even think of it, and blow the whole set. 45662306a36Sopenharmony_ci */ 45762306a36Sopenharmony_ci if (is_vgic_v2_sgi(vcpu, irq)) 45862306a36Sopenharmony_ci irq->source = 0; 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_ci irq->pending_latch = false; 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_ci raw_spin_unlock_irqrestore(&irq->irq_lock, flags); 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_ci vgic_put_irq(vcpu->kvm, irq); 46562306a36Sopenharmony_ci } 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_ci return 0; 46862306a36Sopenharmony_ci} 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_ci/* 47162306a36Sopenharmony_ci * If we are fiddling with an IRQ's active state, we have to make sure the IRQ 47262306a36Sopenharmony_ci * is not queued on some running VCPU's LRs, because then the change to the 47362306a36Sopenharmony_ci * active state can be overwritten when the VCPU's state is synced coming back 47462306a36Sopenharmony_ci * from the guest. 47562306a36Sopenharmony_ci * 47662306a36Sopenharmony_ci * For shared interrupts as well as GICv3 private interrupts accessed from the 47762306a36Sopenharmony_ci * non-owning CPU, we have to stop all the VCPUs because interrupts can be 47862306a36Sopenharmony_ci * migrated while we don't hold the IRQ locks and we don't want to be chasing 47962306a36Sopenharmony_ci * moving targets. 48062306a36Sopenharmony_ci * 48162306a36Sopenharmony_ci * For GICv2 private interrupts we don't have to do anything because 48262306a36Sopenharmony_ci * userspace accesses to the VGIC state already require all VCPUs to be 48362306a36Sopenharmony_ci * stopped, and only the VCPU itself can modify its private interrupts 48462306a36Sopenharmony_ci * active state, which guarantees that the VCPU is not running. 48562306a36Sopenharmony_ci */ 48662306a36Sopenharmony_cistatic void vgic_access_active_prepare(struct kvm_vcpu *vcpu, u32 intid) 48762306a36Sopenharmony_ci{ 48862306a36Sopenharmony_ci if ((vcpu->kvm->arch.vgic.vgic_model == KVM_DEV_TYPE_ARM_VGIC_V3 && 48962306a36Sopenharmony_ci vcpu != kvm_get_running_vcpu()) || 49062306a36Sopenharmony_ci intid >= VGIC_NR_PRIVATE_IRQS) 49162306a36Sopenharmony_ci kvm_arm_halt_guest(vcpu->kvm); 49262306a36Sopenharmony_ci} 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_ci/* See vgic_access_active_prepare */ 49562306a36Sopenharmony_cistatic void vgic_access_active_finish(struct kvm_vcpu *vcpu, u32 intid) 49662306a36Sopenharmony_ci{ 49762306a36Sopenharmony_ci if ((vcpu->kvm->arch.vgic.vgic_model == KVM_DEV_TYPE_ARM_VGIC_V3 && 49862306a36Sopenharmony_ci vcpu != kvm_get_running_vcpu()) || 49962306a36Sopenharmony_ci intid >= VGIC_NR_PRIVATE_IRQS) 50062306a36Sopenharmony_ci kvm_arm_resume_guest(vcpu->kvm); 50162306a36Sopenharmony_ci} 50262306a36Sopenharmony_ci 50362306a36Sopenharmony_cistatic unsigned long __vgic_mmio_read_active(struct kvm_vcpu *vcpu, 50462306a36Sopenharmony_ci gpa_t addr, unsigned int len) 50562306a36Sopenharmony_ci{ 50662306a36Sopenharmony_ci u32 intid = VGIC_ADDR_TO_INTID(addr, 1); 50762306a36Sopenharmony_ci u32 value = 0; 50862306a36Sopenharmony_ci int i; 50962306a36Sopenharmony_ci 51062306a36Sopenharmony_ci /* Loop over all IRQs affected by this read */ 51162306a36Sopenharmony_ci for (i = 0; i < len * 8; i++) { 51262306a36Sopenharmony_ci struct vgic_irq *irq = vgic_get_irq(vcpu->kvm, vcpu, intid + i); 51362306a36Sopenharmony_ci 51462306a36Sopenharmony_ci /* 51562306a36Sopenharmony_ci * Even for HW interrupts, don't evaluate the HW state as 51662306a36Sopenharmony_ci * all the guest is interested in is the virtual state. 51762306a36Sopenharmony_ci */ 51862306a36Sopenharmony_ci if (irq->active) 51962306a36Sopenharmony_ci value |= (1U << i); 52062306a36Sopenharmony_ci 52162306a36Sopenharmony_ci vgic_put_irq(vcpu->kvm, irq); 52262306a36Sopenharmony_ci } 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_ci return value; 52562306a36Sopenharmony_ci} 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_ciunsigned long vgic_mmio_read_active(struct kvm_vcpu *vcpu, 52862306a36Sopenharmony_ci gpa_t addr, unsigned int len) 52962306a36Sopenharmony_ci{ 53062306a36Sopenharmony_ci u32 intid = VGIC_ADDR_TO_INTID(addr, 1); 53162306a36Sopenharmony_ci u32 val; 53262306a36Sopenharmony_ci 53362306a36Sopenharmony_ci mutex_lock(&vcpu->kvm->arch.config_lock); 53462306a36Sopenharmony_ci vgic_access_active_prepare(vcpu, intid); 53562306a36Sopenharmony_ci 53662306a36Sopenharmony_ci val = __vgic_mmio_read_active(vcpu, addr, len); 53762306a36Sopenharmony_ci 53862306a36Sopenharmony_ci vgic_access_active_finish(vcpu, intid); 53962306a36Sopenharmony_ci mutex_unlock(&vcpu->kvm->arch.config_lock); 54062306a36Sopenharmony_ci 54162306a36Sopenharmony_ci return val; 54262306a36Sopenharmony_ci} 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_ciunsigned long vgic_uaccess_read_active(struct kvm_vcpu *vcpu, 54562306a36Sopenharmony_ci gpa_t addr, unsigned int len) 54662306a36Sopenharmony_ci{ 54762306a36Sopenharmony_ci return __vgic_mmio_read_active(vcpu, addr, len); 54862306a36Sopenharmony_ci} 54962306a36Sopenharmony_ci 55062306a36Sopenharmony_ci/* Must be called with irq->irq_lock held */ 55162306a36Sopenharmony_cistatic void vgic_hw_irq_change_active(struct kvm_vcpu *vcpu, struct vgic_irq *irq, 55262306a36Sopenharmony_ci bool active, bool is_uaccess) 55362306a36Sopenharmony_ci{ 55462306a36Sopenharmony_ci if (is_uaccess) 55562306a36Sopenharmony_ci return; 55662306a36Sopenharmony_ci 55762306a36Sopenharmony_ci irq->active = active; 55862306a36Sopenharmony_ci vgic_irq_set_phys_active(irq, active); 55962306a36Sopenharmony_ci} 56062306a36Sopenharmony_ci 56162306a36Sopenharmony_cistatic void vgic_mmio_change_active(struct kvm_vcpu *vcpu, struct vgic_irq *irq, 56262306a36Sopenharmony_ci bool active) 56362306a36Sopenharmony_ci{ 56462306a36Sopenharmony_ci unsigned long flags; 56562306a36Sopenharmony_ci struct kvm_vcpu *requester_vcpu = kvm_get_running_vcpu(); 56662306a36Sopenharmony_ci 56762306a36Sopenharmony_ci raw_spin_lock_irqsave(&irq->irq_lock, flags); 56862306a36Sopenharmony_ci 56962306a36Sopenharmony_ci if (irq->hw && !vgic_irq_is_sgi(irq->intid)) { 57062306a36Sopenharmony_ci vgic_hw_irq_change_active(vcpu, irq, active, !requester_vcpu); 57162306a36Sopenharmony_ci } else if (irq->hw && vgic_irq_is_sgi(irq->intid)) { 57262306a36Sopenharmony_ci /* 57362306a36Sopenharmony_ci * GICv4.1 VSGI feature doesn't track an active state, 57462306a36Sopenharmony_ci * so let's not kid ourselves, there is nothing we can 57562306a36Sopenharmony_ci * do here. 57662306a36Sopenharmony_ci */ 57762306a36Sopenharmony_ci irq->active = false; 57862306a36Sopenharmony_ci } else { 57962306a36Sopenharmony_ci u32 model = vcpu->kvm->arch.vgic.vgic_model; 58062306a36Sopenharmony_ci u8 active_source; 58162306a36Sopenharmony_ci 58262306a36Sopenharmony_ci irq->active = active; 58362306a36Sopenharmony_ci 58462306a36Sopenharmony_ci /* 58562306a36Sopenharmony_ci * The GICv2 architecture indicates that the source CPUID for 58662306a36Sopenharmony_ci * an SGI should be provided during an EOI which implies that 58762306a36Sopenharmony_ci * the active state is stored somewhere, but at the same time 58862306a36Sopenharmony_ci * this state is not architecturally exposed anywhere and we 58962306a36Sopenharmony_ci * have no way of knowing the right source. 59062306a36Sopenharmony_ci * 59162306a36Sopenharmony_ci * This may lead to a VCPU not being able to receive 59262306a36Sopenharmony_ci * additional instances of a particular SGI after migration 59362306a36Sopenharmony_ci * for a GICv2 VM on some GIC implementations. Oh well. 59462306a36Sopenharmony_ci */ 59562306a36Sopenharmony_ci active_source = (requester_vcpu) ? requester_vcpu->vcpu_id : 0; 59662306a36Sopenharmony_ci 59762306a36Sopenharmony_ci if (model == KVM_DEV_TYPE_ARM_VGIC_V2 && 59862306a36Sopenharmony_ci active && vgic_irq_is_sgi(irq->intid)) 59962306a36Sopenharmony_ci irq->active_source = active_source; 60062306a36Sopenharmony_ci } 60162306a36Sopenharmony_ci 60262306a36Sopenharmony_ci if (irq->active) 60362306a36Sopenharmony_ci vgic_queue_irq_unlock(vcpu->kvm, irq, flags); 60462306a36Sopenharmony_ci else 60562306a36Sopenharmony_ci raw_spin_unlock_irqrestore(&irq->irq_lock, flags); 60662306a36Sopenharmony_ci} 60762306a36Sopenharmony_ci 60862306a36Sopenharmony_cistatic void __vgic_mmio_write_cactive(struct kvm_vcpu *vcpu, 60962306a36Sopenharmony_ci gpa_t addr, unsigned int len, 61062306a36Sopenharmony_ci unsigned long val) 61162306a36Sopenharmony_ci{ 61262306a36Sopenharmony_ci u32 intid = VGIC_ADDR_TO_INTID(addr, 1); 61362306a36Sopenharmony_ci int i; 61462306a36Sopenharmony_ci 61562306a36Sopenharmony_ci for_each_set_bit(i, &val, len * 8) { 61662306a36Sopenharmony_ci struct vgic_irq *irq = vgic_get_irq(vcpu->kvm, vcpu, intid + i); 61762306a36Sopenharmony_ci vgic_mmio_change_active(vcpu, irq, false); 61862306a36Sopenharmony_ci vgic_put_irq(vcpu->kvm, irq); 61962306a36Sopenharmony_ci } 62062306a36Sopenharmony_ci} 62162306a36Sopenharmony_ci 62262306a36Sopenharmony_civoid vgic_mmio_write_cactive(struct kvm_vcpu *vcpu, 62362306a36Sopenharmony_ci gpa_t addr, unsigned int len, 62462306a36Sopenharmony_ci unsigned long val) 62562306a36Sopenharmony_ci{ 62662306a36Sopenharmony_ci u32 intid = VGIC_ADDR_TO_INTID(addr, 1); 62762306a36Sopenharmony_ci 62862306a36Sopenharmony_ci mutex_lock(&vcpu->kvm->arch.config_lock); 62962306a36Sopenharmony_ci vgic_access_active_prepare(vcpu, intid); 63062306a36Sopenharmony_ci 63162306a36Sopenharmony_ci __vgic_mmio_write_cactive(vcpu, addr, len, val); 63262306a36Sopenharmony_ci 63362306a36Sopenharmony_ci vgic_access_active_finish(vcpu, intid); 63462306a36Sopenharmony_ci mutex_unlock(&vcpu->kvm->arch.config_lock); 63562306a36Sopenharmony_ci} 63662306a36Sopenharmony_ci 63762306a36Sopenharmony_ciint vgic_mmio_uaccess_write_cactive(struct kvm_vcpu *vcpu, 63862306a36Sopenharmony_ci gpa_t addr, unsigned int len, 63962306a36Sopenharmony_ci unsigned long val) 64062306a36Sopenharmony_ci{ 64162306a36Sopenharmony_ci __vgic_mmio_write_cactive(vcpu, addr, len, val); 64262306a36Sopenharmony_ci return 0; 64362306a36Sopenharmony_ci} 64462306a36Sopenharmony_ci 64562306a36Sopenharmony_cistatic void __vgic_mmio_write_sactive(struct kvm_vcpu *vcpu, 64662306a36Sopenharmony_ci gpa_t addr, unsigned int len, 64762306a36Sopenharmony_ci unsigned long val) 64862306a36Sopenharmony_ci{ 64962306a36Sopenharmony_ci u32 intid = VGIC_ADDR_TO_INTID(addr, 1); 65062306a36Sopenharmony_ci int i; 65162306a36Sopenharmony_ci 65262306a36Sopenharmony_ci for_each_set_bit(i, &val, len * 8) { 65362306a36Sopenharmony_ci struct vgic_irq *irq = vgic_get_irq(vcpu->kvm, vcpu, intid + i); 65462306a36Sopenharmony_ci vgic_mmio_change_active(vcpu, irq, true); 65562306a36Sopenharmony_ci vgic_put_irq(vcpu->kvm, irq); 65662306a36Sopenharmony_ci } 65762306a36Sopenharmony_ci} 65862306a36Sopenharmony_ci 65962306a36Sopenharmony_civoid vgic_mmio_write_sactive(struct kvm_vcpu *vcpu, 66062306a36Sopenharmony_ci gpa_t addr, unsigned int len, 66162306a36Sopenharmony_ci unsigned long val) 66262306a36Sopenharmony_ci{ 66362306a36Sopenharmony_ci u32 intid = VGIC_ADDR_TO_INTID(addr, 1); 66462306a36Sopenharmony_ci 66562306a36Sopenharmony_ci mutex_lock(&vcpu->kvm->arch.config_lock); 66662306a36Sopenharmony_ci vgic_access_active_prepare(vcpu, intid); 66762306a36Sopenharmony_ci 66862306a36Sopenharmony_ci __vgic_mmio_write_sactive(vcpu, addr, len, val); 66962306a36Sopenharmony_ci 67062306a36Sopenharmony_ci vgic_access_active_finish(vcpu, intid); 67162306a36Sopenharmony_ci mutex_unlock(&vcpu->kvm->arch.config_lock); 67262306a36Sopenharmony_ci} 67362306a36Sopenharmony_ci 67462306a36Sopenharmony_ciint vgic_mmio_uaccess_write_sactive(struct kvm_vcpu *vcpu, 67562306a36Sopenharmony_ci gpa_t addr, unsigned int len, 67662306a36Sopenharmony_ci unsigned long val) 67762306a36Sopenharmony_ci{ 67862306a36Sopenharmony_ci __vgic_mmio_write_sactive(vcpu, addr, len, val); 67962306a36Sopenharmony_ci return 0; 68062306a36Sopenharmony_ci} 68162306a36Sopenharmony_ci 68262306a36Sopenharmony_ciunsigned long vgic_mmio_read_priority(struct kvm_vcpu *vcpu, 68362306a36Sopenharmony_ci gpa_t addr, unsigned int len) 68462306a36Sopenharmony_ci{ 68562306a36Sopenharmony_ci u32 intid = VGIC_ADDR_TO_INTID(addr, 8); 68662306a36Sopenharmony_ci int i; 68762306a36Sopenharmony_ci u64 val = 0; 68862306a36Sopenharmony_ci 68962306a36Sopenharmony_ci for (i = 0; i < len; i++) { 69062306a36Sopenharmony_ci struct vgic_irq *irq = vgic_get_irq(vcpu->kvm, vcpu, intid + i); 69162306a36Sopenharmony_ci 69262306a36Sopenharmony_ci val |= (u64)irq->priority << (i * 8); 69362306a36Sopenharmony_ci 69462306a36Sopenharmony_ci vgic_put_irq(vcpu->kvm, irq); 69562306a36Sopenharmony_ci } 69662306a36Sopenharmony_ci 69762306a36Sopenharmony_ci return val; 69862306a36Sopenharmony_ci} 69962306a36Sopenharmony_ci 70062306a36Sopenharmony_ci/* 70162306a36Sopenharmony_ci * We currently don't handle changing the priority of an interrupt that 70262306a36Sopenharmony_ci * is already pending on a VCPU. If there is a need for this, we would 70362306a36Sopenharmony_ci * need to make this VCPU exit and re-evaluate the priorities, potentially 70462306a36Sopenharmony_ci * leading to this interrupt getting presented now to the guest (if it has 70562306a36Sopenharmony_ci * been masked by the priority mask before). 70662306a36Sopenharmony_ci */ 70762306a36Sopenharmony_civoid vgic_mmio_write_priority(struct kvm_vcpu *vcpu, 70862306a36Sopenharmony_ci gpa_t addr, unsigned int len, 70962306a36Sopenharmony_ci unsigned long val) 71062306a36Sopenharmony_ci{ 71162306a36Sopenharmony_ci u32 intid = VGIC_ADDR_TO_INTID(addr, 8); 71262306a36Sopenharmony_ci int i; 71362306a36Sopenharmony_ci unsigned long flags; 71462306a36Sopenharmony_ci 71562306a36Sopenharmony_ci for (i = 0; i < len; i++) { 71662306a36Sopenharmony_ci struct vgic_irq *irq = vgic_get_irq(vcpu->kvm, vcpu, intid + i); 71762306a36Sopenharmony_ci 71862306a36Sopenharmony_ci raw_spin_lock_irqsave(&irq->irq_lock, flags); 71962306a36Sopenharmony_ci /* Narrow the priority range to what we actually support */ 72062306a36Sopenharmony_ci irq->priority = (val >> (i * 8)) & GENMASK(7, 8 - VGIC_PRI_BITS); 72162306a36Sopenharmony_ci if (irq->hw && vgic_irq_is_sgi(irq->intid)) 72262306a36Sopenharmony_ci vgic_update_vsgi(irq); 72362306a36Sopenharmony_ci raw_spin_unlock_irqrestore(&irq->irq_lock, flags); 72462306a36Sopenharmony_ci 72562306a36Sopenharmony_ci vgic_put_irq(vcpu->kvm, irq); 72662306a36Sopenharmony_ci } 72762306a36Sopenharmony_ci} 72862306a36Sopenharmony_ci 72962306a36Sopenharmony_ciunsigned long vgic_mmio_read_config(struct kvm_vcpu *vcpu, 73062306a36Sopenharmony_ci gpa_t addr, unsigned int len) 73162306a36Sopenharmony_ci{ 73262306a36Sopenharmony_ci u32 intid = VGIC_ADDR_TO_INTID(addr, 2); 73362306a36Sopenharmony_ci u32 value = 0; 73462306a36Sopenharmony_ci int i; 73562306a36Sopenharmony_ci 73662306a36Sopenharmony_ci for (i = 0; i < len * 4; i++) { 73762306a36Sopenharmony_ci struct vgic_irq *irq = vgic_get_irq(vcpu->kvm, vcpu, intid + i); 73862306a36Sopenharmony_ci 73962306a36Sopenharmony_ci if (irq->config == VGIC_CONFIG_EDGE) 74062306a36Sopenharmony_ci value |= (2U << (i * 2)); 74162306a36Sopenharmony_ci 74262306a36Sopenharmony_ci vgic_put_irq(vcpu->kvm, irq); 74362306a36Sopenharmony_ci } 74462306a36Sopenharmony_ci 74562306a36Sopenharmony_ci return value; 74662306a36Sopenharmony_ci} 74762306a36Sopenharmony_ci 74862306a36Sopenharmony_civoid vgic_mmio_write_config(struct kvm_vcpu *vcpu, 74962306a36Sopenharmony_ci gpa_t addr, unsigned int len, 75062306a36Sopenharmony_ci unsigned long val) 75162306a36Sopenharmony_ci{ 75262306a36Sopenharmony_ci u32 intid = VGIC_ADDR_TO_INTID(addr, 2); 75362306a36Sopenharmony_ci int i; 75462306a36Sopenharmony_ci unsigned long flags; 75562306a36Sopenharmony_ci 75662306a36Sopenharmony_ci for (i = 0; i < len * 4; i++) { 75762306a36Sopenharmony_ci struct vgic_irq *irq; 75862306a36Sopenharmony_ci 75962306a36Sopenharmony_ci /* 76062306a36Sopenharmony_ci * The configuration cannot be changed for SGIs in general, 76162306a36Sopenharmony_ci * for PPIs this is IMPLEMENTATION DEFINED. The arch timer 76262306a36Sopenharmony_ci * code relies on PPIs being level triggered, so we also 76362306a36Sopenharmony_ci * make them read-only here. 76462306a36Sopenharmony_ci */ 76562306a36Sopenharmony_ci if (intid + i < VGIC_NR_PRIVATE_IRQS) 76662306a36Sopenharmony_ci continue; 76762306a36Sopenharmony_ci 76862306a36Sopenharmony_ci irq = vgic_get_irq(vcpu->kvm, vcpu, intid + i); 76962306a36Sopenharmony_ci raw_spin_lock_irqsave(&irq->irq_lock, flags); 77062306a36Sopenharmony_ci 77162306a36Sopenharmony_ci if (test_bit(i * 2 + 1, &val)) 77262306a36Sopenharmony_ci irq->config = VGIC_CONFIG_EDGE; 77362306a36Sopenharmony_ci else 77462306a36Sopenharmony_ci irq->config = VGIC_CONFIG_LEVEL; 77562306a36Sopenharmony_ci 77662306a36Sopenharmony_ci raw_spin_unlock_irqrestore(&irq->irq_lock, flags); 77762306a36Sopenharmony_ci vgic_put_irq(vcpu->kvm, irq); 77862306a36Sopenharmony_ci } 77962306a36Sopenharmony_ci} 78062306a36Sopenharmony_ci 78162306a36Sopenharmony_ciu32 vgic_read_irq_line_level_info(struct kvm_vcpu *vcpu, u32 intid) 78262306a36Sopenharmony_ci{ 78362306a36Sopenharmony_ci int i; 78462306a36Sopenharmony_ci u32 val = 0; 78562306a36Sopenharmony_ci int nr_irqs = vcpu->kvm->arch.vgic.nr_spis + VGIC_NR_PRIVATE_IRQS; 78662306a36Sopenharmony_ci 78762306a36Sopenharmony_ci for (i = 0; i < 32; i++) { 78862306a36Sopenharmony_ci struct vgic_irq *irq; 78962306a36Sopenharmony_ci 79062306a36Sopenharmony_ci if ((intid + i) < VGIC_NR_SGIS || (intid + i) >= nr_irqs) 79162306a36Sopenharmony_ci continue; 79262306a36Sopenharmony_ci 79362306a36Sopenharmony_ci irq = vgic_get_irq(vcpu->kvm, vcpu, intid + i); 79462306a36Sopenharmony_ci if (irq->config == VGIC_CONFIG_LEVEL && irq->line_level) 79562306a36Sopenharmony_ci val |= (1U << i); 79662306a36Sopenharmony_ci 79762306a36Sopenharmony_ci vgic_put_irq(vcpu->kvm, irq); 79862306a36Sopenharmony_ci } 79962306a36Sopenharmony_ci 80062306a36Sopenharmony_ci return val; 80162306a36Sopenharmony_ci} 80262306a36Sopenharmony_ci 80362306a36Sopenharmony_civoid vgic_write_irq_line_level_info(struct kvm_vcpu *vcpu, u32 intid, 80462306a36Sopenharmony_ci const u32 val) 80562306a36Sopenharmony_ci{ 80662306a36Sopenharmony_ci int i; 80762306a36Sopenharmony_ci int nr_irqs = vcpu->kvm->arch.vgic.nr_spis + VGIC_NR_PRIVATE_IRQS; 80862306a36Sopenharmony_ci unsigned long flags; 80962306a36Sopenharmony_ci 81062306a36Sopenharmony_ci for (i = 0; i < 32; i++) { 81162306a36Sopenharmony_ci struct vgic_irq *irq; 81262306a36Sopenharmony_ci bool new_level; 81362306a36Sopenharmony_ci 81462306a36Sopenharmony_ci if ((intid + i) < VGIC_NR_SGIS || (intid + i) >= nr_irqs) 81562306a36Sopenharmony_ci continue; 81662306a36Sopenharmony_ci 81762306a36Sopenharmony_ci irq = vgic_get_irq(vcpu->kvm, vcpu, intid + i); 81862306a36Sopenharmony_ci 81962306a36Sopenharmony_ci /* 82062306a36Sopenharmony_ci * Line level is set irrespective of irq type 82162306a36Sopenharmony_ci * (level or edge) to avoid dependency that VM should 82262306a36Sopenharmony_ci * restore irq config before line level. 82362306a36Sopenharmony_ci */ 82462306a36Sopenharmony_ci new_level = !!(val & (1U << i)); 82562306a36Sopenharmony_ci raw_spin_lock_irqsave(&irq->irq_lock, flags); 82662306a36Sopenharmony_ci irq->line_level = new_level; 82762306a36Sopenharmony_ci if (new_level) 82862306a36Sopenharmony_ci vgic_queue_irq_unlock(vcpu->kvm, irq, flags); 82962306a36Sopenharmony_ci else 83062306a36Sopenharmony_ci raw_spin_unlock_irqrestore(&irq->irq_lock, flags); 83162306a36Sopenharmony_ci 83262306a36Sopenharmony_ci vgic_put_irq(vcpu->kvm, irq); 83362306a36Sopenharmony_ci } 83462306a36Sopenharmony_ci} 83562306a36Sopenharmony_ci 83662306a36Sopenharmony_cistatic int match_region(const void *key, const void *elt) 83762306a36Sopenharmony_ci{ 83862306a36Sopenharmony_ci const unsigned int offset = (unsigned long)key; 83962306a36Sopenharmony_ci const struct vgic_register_region *region = elt; 84062306a36Sopenharmony_ci 84162306a36Sopenharmony_ci if (offset < region->reg_offset) 84262306a36Sopenharmony_ci return -1; 84362306a36Sopenharmony_ci 84462306a36Sopenharmony_ci if (offset >= region->reg_offset + region->len) 84562306a36Sopenharmony_ci return 1; 84662306a36Sopenharmony_ci 84762306a36Sopenharmony_ci return 0; 84862306a36Sopenharmony_ci} 84962306a36Sopenharmony_ci 85062306a36Sopenharmony_ciconst struct vgic_register_region * 85162306a36Sopenharmony_civgic_find_mmio_region(const struct vgic_register_region *regions, 85262306a36Sopenharmony_ci int nr_regions, unsigned int offset) 85362306a36Sopenharmony_ci{ 85462306a36Sopenharmony_ci return bsearch((void *)(uintptr_t)offset, regions, nr_regions, 85562306a36Sopenharmony_ci sizeof(regions[0]), match_region); 85662306a36Sopenharmony_ci} 85762306a36Sopenharmony_ci 85862306a36Sopenharmony_civoid vgic_set_vmcr(struct kvm_vcpu *vcpu, struct vgic_vmcr *vmcr) 85962306a36Sopenharmony_ci{ 86062306a36Sopenharmony_ci if (kvm_vgic_global_state.type == VGIC_V2) 86162306a36Sopenharmony_ci vgic_v2_set_vmcr(vcpu, vmcr); 86262306a36Sopenharmony_ci else 86362306a36Sopenharmony_ci vgic_v3_set_vmcr(vcpu, vmcr); 86462306a36Sopenharmony_ci} 86562306a36Sopenharmony_ci 86662306a36Sopenharmony_civoid vgic_get_vmcr(struct kvm_vcpu *vcpu, struct vgic_vmcr *vmcr) 86762306a36Sopenharmony_ci{ 86862306a36Sopenharmony_ci if (kvm_vgic_global_state.type == VGIC_V2) 86962306a36Sopenharmony_ci vgic_v2_get_vmcr(vcpu, vmcr); 87062306a36Sopenharmony_ci else 87162306a36Sopenharmony_ci vgic_v3_get_vmcr(vcpu, vmcr); 87262306a36Sopenharmony_ci} 87362306a36Sopenharmony_ci 87462306a36Sopenharmony_ci/* 87562306a36Sopenharmony_ci * kvm_mmio_read_buf() returns a value in a format where it can be converted 87662306a36Sopenharmony_ci * to a byte array and be directly observed as the guest wanted it to appear 87762306a36Sopenharmony_ci * in memory if it had done the store itself, which is LE for the GIC, as the 87862306a36Sopenharmony_ci * guest knows the GIC is always LE. 87962306a36Sopenharmony_ci * 88062306a36Sopenharmony_ci * We convert this value to the CPUs native format to deal with it as a data 88162306a36Sopenharmony_ci * value. 88262306a36Sopenharmony_ci */ 88362306a36Sopenharmony_ciunsigned long vgic_data_mmio_bus_to_host(const void *val, unsigned int len) 88462306a36Sopenharmony_ci{ 88562306a36Sopenharmony_ci unsigned long data = kvm_mmio_read_buf(val, len); 88662306a36Sopenharmony_ci 88762306a36Sopenharmony_ci switch (len) { 88862306a36Sopenharmony_ci case 1: 88962306a36Sopenharmony_ci return data; 89062306a36Sopenharmony_ci case 2: 89162306a36Sopenharmony_ci return le16_to_cpu(data); 89262306a36Sopenharmony_ci case 4: 89362306a36Sopenharmony_ci return le32_to_cpu(data); 89462306a36Sopenharmony_ci default: 89562306a36Sopenharmony_ci return le64_to_cpu(data); 89662306a36Sopenharmony_ci } 89762306a36Sopenharmony_ci} 89862306a36Sopenharmony_ci 89962306a36Sopenharmony_ci/* 90062306a36Sopenharmony_ci * kvm_mmio_write_buf() expects a value in a format such that if converted to 90162306a36Sopenharmony_ci * a byte array it is observed as the guest would see it if it could perform 90262306a36Sopenharmony_ci * the load directly. Since the GIC is LE, and the guest knows this, the 90362306a36Sopenharmony_ci * guest expects a value in little endian format. 90462306a36Sopenharmony_ci * 90562306a36Sopenharmony_ci * We convert the data value from the CPUs native format to LE so that the 90662306a36Sopenharmony_ci * value is returned in the proper format. 90762306a36Sopenharmony_ci */ 90862306a36Sopenharmony_civoid vgic_data_host_to_mmio_bus(void *buf, unsigned int len, 90962306a36Sopenharmony_ci unsigned long data) 91062306a36Sopenharmony_ci{ 91162306a36Sopenharmony_ci switch (len) { 91262306a36Sopenharmony_ci case 1: 91362306a36Sopenharmony_ci break; 91462306a36Sopenharmony_ci case 2: 91562306a36Sopenharmony_ci data = cpu_to_le16(data); 91662306a36Sopenharmony_ci break; 91762306a36Sopenharmony_ci case 4: 91862306a36Sopenharmony_ci data = cpu_to_le32(data); 91962306a36Sopenharmony_ci break; 92062306a36Sopenharmony_ci default: 92162306a36Sopenharmony_ci data = cpu_to_le64(data); 92262306a36Sopenharmony_ci } 92362306a36Sopenharmony_ci 92462306a36Sopenharmony_ci kvm_mmio_write_buf(buf, len, data); 92562306a36Sopenharmony_ci} 92662306a36Sopenharmony_ci 92762306a36Sopenharmony_cistatic 92862306a36Sopenharmony_cistruct vgic_io_device *kvm_to_vgic_iodev(const struct kvm_io_device *dev) 92962306a36Sopenharmony_ci{ 93062306a36Sopenharmony_ci return container_of(dev, struct vgic_io_device, dev); 93162306a36Sopenharmony_ci} 93262306a36Sopenharmony_ci 93362306a36Sopenharmony_cistatic bool check_region(const struct kvm *kvm, 93462306a36Sopenharmony_ci const struct vgic_register_region *region, 93562306a36Sopenharmony_ci gpa_t addr, int len) 93662306a36Sopenharmony_ci{ 93762306a36Sopenharmony_ci int flags, nr_irqs = kvm->arch.vgic.nr_spis + VGIC_NR_PRIVATE_IRQS; 93862306a36Sopenharmony_ci 93962306a36Sopenharmony_ci switch (len) { 94062306a36Sopenharmony_ci case sizeof(u8): 94162306a36Sopenharmony_ci flags = VGIC_ACCESS_8bit; 94262306a36Sopenharmony_ci break; 94362306a36Sopenharmony_ci case sizeof(u32): 94462306a36Sopenharmony_ci flags = VGIC_ACCESS_32bit; 94562306a36Sopenharmony_ci break; 94662306a36Sopenharmony_ci case sizeof(u64): 94762306a36Sopenharmony_ci flags = VGIC_ACCESS_64bit; 94862306a36Sopenharmony_ci break; 94962306a36Sopenharmony_ci default: 95062306a36Sopenharmony_ci return false; 95162306a36Sopenharmony_ci } 95262306a36Sopenharmony_ci 95362306a36Sopenharmony_ci if ((region->access_flags & flags) && IS_ALIGNED(addr, len)) { 95462306a36Sopenharmony_ci if (!region->bits_per_irq) 95562306a36Sopenharmony_ci return true; 95662306a36Sopenharmony_ci 95762306a36Sopenharmony_ci /* Do we access a non-allocated IRQ? */ 95862306a36Sopenharmony_ci return VGIC_ADDR_TO_INTID(addr, region->bits_per_irq) < nr_irqs; 95962306a36Sopenharmony_ci } 96062306a36Sopenharmony_ci 96162306a36Sopenharmony_ci return false; 96262306a36Sopenharmony_ci} 96362306a36Sopenharmony_ci 96462306a36Sopenharmony_ciconst struct vgic_register_region * 96562306a36Sopenharmony_civgic_get_mmio_region(struct kvm_vcpu *vcpu, struct vgic_io_device *iodev, 96662306a36Sopenharmony_ci gpa_t addr, int len) 96762306a36Sopenharmony_ci{ 96862306a36Sopenharmony_ci const struct vgic_register_region *region; 96962306a36Sopenharmony_ci 97062306a36Sopenharmony_ci region = vgic_find_mmio_region(iodev->regions, iodev->nr_regions, 97162306a36Sopenharmony_ci addr - iodev->base_addr); 97262306a36Sopenharmony_ci if (!region || !check_region(vcpu->kvm, region, addr, len)) 97362306a36Sopenharmony_ci return NULL; 97462306a36Sopenharmony_ci 97562306a36Sopenharmony_ci return region; 97662306a36Sopenharmony_ci} 97762306a36Sopenharmony_ci 97862306a36Sopenharmony_cistatic int vgic_uaccess_read(struct kvm_vcpu *vcpu, struct vgic_io_device *iodev, 97962306a36Sopenharmony_ci gpa_t addr, u32 *val) 98062306a36Sopenharmony_ci{ 98162306a36Sopenharmony_ci const struct vgic_register_region *region; 98262306a36Sopenharmony_ci struct kvm_vcpu *r_vcpu; 98362306a36Sopenharmony_ci 98462306a36Sopenharmony_ci region = vgic_get_mmio_region(vcpu, iodev, addr, sizeof(u32)); 98562306a36Sopenharmony_ci if (!region) { 98662306a36Sopenharmony_ci *val = 0; 98762306a36Sopenharmony_ci return 0; 98862306a36Sopenharmony_ci } 98962306a36Sopenharmony_ci 99062306a36Sopenharmony_ci r_vcpu = iodev->redist_vcpu ? iodev->redist_vcpu : vcpu; 99162306a36Sopenharmony_ci if (region->uaccess_read) 99262306a36Sopenharmony_ci *val = region->uaccess_read(r_vcpu, addr, sizeof(u32)); 99362306a36Sopenharmony_ci else 99462306a36Sopenharmony_ci *val = region->read(r_vcpu, addr, sizeof(u32)); 99562306a36Sopenharmony_ci 99662306a36Sopenharmony_ci return 0; 99762306a36Sopenharmony_ci} 99862306a36Sopenharmony_ci 99962306a36Sopenharmony_cistatic int vgic_uaccess_write(struct kvm_vcpu *vcpu, struct vgic_io_device *iodev, 100062306a36Sopenharmony_ci gpa_t addr, const u32 *val) 100162306a36Sopenharmony_ci{ 100262306a36Sopenharmony_ci const struct vgic_register_region *region; 100362306a36Sopenharmony_ci struct kvm_vcpu *r_vcpu; 100462306a36Sopenharmony_ci 100562306a36Sopenharmony_ci region = vgic_get_mmio_region(vcpu, iodev, addr, sizeof(u32)); 100662306a36Sopenharmony_ci if (!region) 100762306a36Sopenharmony_ci return 0; 100862306a36Sopenharmony_ci 100962306a36Sopenharmony_ci r_vcpu = iodev->redist_vcpu ? iodev->redist_vcpu : vcpu; 101062306a36Sopenharmony_ci if (region->uaccess_write) 101162306a36Sopenharmony_ci return region->uaccess_write(r_vcpu, addr, sizeof(u32), *val); 101262306a36Sopenharmony_ci 101362306a36Sopenharmony_ci region->write(r_vcpu, addr, sizeof(u32), *val); 101462306a36Sopenharmony_ci return 0; 101562306a36Sopenharmony_ci} 101662306a36Sopenharmony_ci 101762306a36Sopenharmony_ci/* 101862306a36Sopenharmony_ci * Userland access to VGIC registers. 101962306a36Sopenharmony_ci */ 102062306a36Sopenharmony_ciint vgic_uaccess(struct kvm_vcpu *vcpu, struct vgic_io_device *dev, 102162306a36Sopenharmony_ci bool is_write, int offset, u32 *val) 102262306a36Sopenharmony_ci{ 102362306a36Sopenharmony_ci if (is_write) 102462306a36Sopenharmony_ci return vgic_uaccess_write(vcpu, dev, offset, val); 102562306a36Sopenharmony_ci else 102662306a36Sopenharmony_ci return vgic_uaccess_read(vcpu, dev, offset, val); 102762306a36Sopenharmony_ci} 102862306a36Sopenharmony_ci 102962306a36Sopenharmony_cistatic int dispatch_mmio_read(struct kvm_vcpu *vcpu, struct kvm_io_device *dev, 103062306a36Sopenharmony_ci gpa_t addr, int len, void *val) 103162306a36Sopenharmony_ci{ 103262306a36Sopenharmony_ci struct vgic_io_device *iodev = kvm_to_vgic_iodev(dev); 103362306a36Sopenharmony_ci const struct vgic_register_region *region; 103462306a36Sopenharmony_ci unsigned long data = 0; 103562306a36Sopenharmony_ci 103662306a36Sopenharmony_ci region = vgic_get_mmio_region(vcpu, iodev, addr, len); 103762306a36Sopenharmony_ci if (!region) { 103862306a36Sopenharmony_ci memset(val, 0, len); 103962306a36Sopenharmony_ci return 0; 104062306a36Sopenharmony_ci } 104162306a36Sopenharmony_ci 104262306a36Sopenharmony_ci switch (iodev->iodev_type) { 104362306a36Sopenharmony_ci case IODEV_CPUIF: 104462306a36Sopenharmony_ci data = region->read(vcpu, addr, len); 104562306a36Sopenharmony_ci break; 104662306a36Sopenharmony_ci case IODEV_DIST: 104762306a36Sopenharmony_ci data = region->read(vcpu, addr, len); 104862306a36Sopenharmony_ci break; 104962306a36Sopenharmony_ci case IODEV_REDIST: 105062306a36Sopenharmony_ci data = region->read(iodev->redist_vcpu, addr, len); 105162306a36Sopenharmony_ci break; 105262306a36Sopenharmony_ci case IODEV_ITS: 105362306a36Sopenharmony_ci data = region->its_read(vcpu->kvm, iodev->its, addr, len); 105462306a36Sopenharmony_ci break; 105562306a36Sopenharmony_ci } 105662306a36Sopenharmony_ci 105762306a36Sopenharmony_ci vgic_data_host_to_mmio_bus(val, len, data); 105862306a36Sopenharmony_ci return 0; 105962306a36Sopenharmony_ci} 106062306a36Sopenharmony_ci 106162306a36Sopenharmony_cistatic int dispatch_mmio_write(struct kvm_vcpu *vcpu, struct kvm_io_device *dev, 106262306a36Sopenharmony_ci gpa_t addr, int len, const void *val) 106362306a36Sopenharmony_ci{ 106462306a36Sopenharmony_ci struct vgic_io_device *iodev = kvm_to_vgic_iodev(dev); 106562306a36Sopenharmony_ci const struct vgic_register_region *region; 106662306a36Sopenharmony_ci unsigned long data = vgic_data_mmio_bus_to_host(val, len); 106762306a36Sopenharmony_ci 106862306a36Sopenharmony_ci region = vgic_get_mmio_region(vcpu, iodev, addr, len); 106962306a36Sopenharmony_ci if (!region) 107062306a36Sopenharmony_ci return 0; 107162306a36Sopenharmony_ci 107262306a36Sopenharmony_ci switch (iodev->iodev_type) { 107362306a36Sopenharmony_ci case IODEV_CPUIF: 107462306a36Sopenharmony_ci region->write(vcpu, addr, len, data); 107562306a36Sopenharmony_ci break; 107662306a36Sopenharmony_ci case IODEV_DIST: 107762306a36Sopenharmony_ci region->write(vcpu, addr, len, data); 107862306a36Sopenharmony_ci break; 107962306a36Sopenharmony_ci case IODEV_REDIST: 108062306a36Sopenharmony_ci region->write(iodev->redist_vcpu, addr, len, data); 108162306a36Sopenharmony_ci break; 108262306a36Sopenharmony_ci case IODEV_ITS: 108362306a36Sopenharmony_ci region->its_write(vcpu->kvm, iodev->its, addr, len, data); 108462306a36Sopenharmony_ci break; 108562306a36Sopenharmony_ci } 108662306a36Sopenharmony_ci 108762306a36Sopenharmony_ci return 0; 108862306a36Sopenharmony_ci} 108962306a36Sopenharmony_ci 109062306a36Sopenharmony_ciconst struct kvm_io_device_ops kvm_io_gic_ops = { 109162306a36Sopenharmony_ci .read = dispatch_mmio_read, 109262306a36Sopenharmony_ci .write = dispatch_mmio_write, 109362306a36Sopenharmony_ci}; 109462306a36Sopenharmony_ci 109562306a36Sopenharmony_ciint vgic_register_dist_iodev(struct kvm *kvm, gpa_t dist_base_address, 109662306a36Sopenharmony_ci enum vgic_type type) 109762306a36Sopenharmony_ci{ 109862306a36Sopenharmony_ci struct vgic_io_device *io_device = &kvm->arch.vgic.dist_iodev; 109962306a36Sopenharmony_ci unsigned int len; 110062306a36Sopenharmony_ci 110162306a36Sopenharmony_ci switch (type) { 110262306a36Sopenharmony_ci case VGIC_V2: 110362306a36Sopenharmony_ci len = vgic_v2_init_dist_iodev(io_device); 110462306a36Sopenharmony_ci break; 110562306a36Sopenharmony_ci case VGIC_V3: 110662306a36Sopenharmony_ci len = vgic_v3_init_dist_iodev(io_device); 110762306a36Sopenharmony_ci break; 110862306a36Sopenharmony_ci default: 110962306a36Sopenharmony_ci BUG_ON(1); 111062306a36Sopenharmony_ci } 111162306a36Sopenharmony_ci 111262306a36Sopenharmony_ci io_device->base_addr = dist_base_address; 111362306a36Sopenharmony_ci io_device->iodev_type = IODEV_DIST; 111462306a36Sopenharmony_ci io_device->redist_vcpu = NULL; 111562306a36Sopenharmony_ci 111662306a36Sopenharmony_ci return kvm_io_bus_register_dev(kvm, KVM_MMIO_BUS, dist_base_address, 111762306a36Sopenharmony_ci len, &io_device->dev); 111862306a36Sopenharmony_ci} 1119