| /* |
| * Contains GICv2 specific emulation code, was in vgic.c before. |
| * |
| * Copyright (C) 2012 ARM Ltd. |
| * Author: Marc Zyngier <marc.zyngier@arm.com> |
| * |
| * This program is free software; you can redistribute it and/or modify |
| * it under the terms of the GNU General Public License version 2 as |
| * published by the Free Software Foundation. |
| * |
| * This program is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| * GNU General Public License for more details. |
| * |
| * You should have received a copy of the GNU General Public License |
| * along with this program. If not, see <http://www.gnu.org/licenses/>. |
| */ |
| |
| #include <linux/cpu.h> |
| #include <linux/kvm.h> |
| #include <linux/kvm_host.h> |
| #include <linux/interrupt.h> |
| #include <linux/io.h> |
| #include <linux/uaccess.h> |
| |
| #include <linux/irqchip/arm-gic.h> |
| |
| #include <asm/kvm_emulate.h> |
| #include <asm/kvm_arm.h> |
| #include <asm/kvm_mmu.h> |
| |
| #include "vgic.h" |
| |
| #define GICC_ARCH_VERSION_V2 0x2 |
| |
| static void vgic_dispatch_sgi(struct kvm_vcpu *vcpu, u32 reg); |
| static u8 *vgic_get_sgi_sources(struct vgic_dist *dist, int vcpu_id, int sgi) |
| { |
| return dist->irq_sgi_sources + vcpu_id * VGIC_NR_SGIS + sgi; |
| } |
| |
| static bool handle_mmio_misc(struct kvm_vcpu *vcpu, |
| struct kvm_exit_mmio *mmio, phys_addr_t offset) |
| { |
| u32 reg; |
| u32 word_offset = offset & 3; |
| |
| switch (offset & ~3) { |
| case 0: /* GICD_CTLR */ |
| reg = vcpu->kvm->arch.vgic.enabled; |
| vgic_reg_access(mmio, ®, word_offset, |
| ACCESS_READ_VALUE | ACCESS_WRITE_VALUE); |
| if (mmio->is_write) { |
| vcpu->kvm->arch.vgic.enabled = reg & 1; |
| vgic_update_state(vcpu->kvm); |
| return true; |
| } |
| break; |
| |
| case 4: /* GICD_TYPER */ |
| reg = (atomic_read(&vcpu->kvm->online_vcpus) - 1) << 5; |
| reg |= (vcpu->kvm->arch.vgic.nr_irqs >> 5) - 1; |
| vgic_reg_access(mmio, ®, word_offset, |
| ACCESS_READ_VALUE | ACCESS_WRITE_IGNORED); |
| break; |
| |
| case 8: /* GICD_IIDR */ |
| reg = (PRODUCT_ID_KVM << 24) | (IMPLEMENTER_ARM << 0); |
| vgic_reg_access(mmio, ®, word_offset, |
| ACCESS_READ_VALUE | ACCESS_WRITE_IGNORED); |
| break; |
| } |
| |
| return false; |
| } |
| |
| static bool handle_mmio_set_enable_reg(struct kvm_vcpu *vcpu, |
| struct kvm_exit_mmio *mmio, |
| phys_addr_t offset) |
| { |
| return vgic_handle_enable_reg(vcpu->kvm, mmio, offset, |
| vcpu->vcpu_id, ACCESS_WRITE_SETBIT); |
| } |
| |
| static bool handle_mmio_clear_enable_reg(struct kvm_vcpu *vcpu, |
| struct kvm_exit_mmio *mmio, |
| phys_addr_t offset) |
| { |
| return vgic_handle_enable_reg(vcpu->kvm, mmio, offset, |
| vcpu->vcpu_id, ACCESS_WRITE_CLEARBIT); |
| } |
| |
| static bool handle_mmio_set_pending_reg(struct kvm_vcpu *vcpu, |
| struct kvm_exit_mmio *mmio, |
| phys_addr_t offset) |
| { |
| return vgic_handle_set_pending_reg(vcpu->kvm, mmio, offset, |
| vcpu->vcpu_id); |
| } |
| |
| static bool handle_mmio_clear_pending_reg(struct kvm_vcpu *vcpu, |
| struct kvm_exit_mmio *mmio, |
| phys_addr_t offset) |
| { |
| return vgic_handle_clear_pending_reg(vcpu->kvm, mmio, offset, |
| vcpu->vcpu_id); |
| } |
| |
| static bool handle_mmio_set_active_reg(struct kvm_vcpu *vcpu, |
| struct kvm_exit_mmio *mmio, |
| phys_addr_t offset) |
| { |
| return vgic_handle_set_active_reg(vcpu->kvm, mmio, offset, |
| vcpu->vcpu_id); |
| } |
| |
| static bool handle_mmio_clear_active_reg(struct kvm_vcpu *vcpu, |
| struct kvm_exit_mmio *mmio, |
| phys_addr_t offset) |
| { |
| return vgic_handle_clear_active_reg(vcpu->kvm, mmio, offset, |
| vcpu->vcpu_id); |
| } |
| |
| static bool handle_mmio_priority_reg(struct kvm_vcpu *vcpu, |
| struct kvm_exit_mmio *mmio, |
| phys_addr_t offset) |
| { |
| u32 *reg = vgic_bytemap_get_reg(&vcpu->kvm->arch.vgic.irq_priority, |
| vcpu->vcpu_id, offset); |
| vgic_reg_access(mmio, reg, offset, |
| ACCESS_READ_VALUE | ACCESS_WRITE_VALUE); |
| return false; |
| } |
| |
| #define GICD_ITARGETSR_SIZE 32 |
| #define GICD_CPUTARGETS_BITS 8 |
| #define GICD_IRQS_PER_ITARGETSR (GICD_ITARGETSR_SIZE / GICD_CPUTARGETS_BITS) |
| static u32 vgic_get_target_reg(struct kvm *kvm, int irq) |
| { |
| struct vgic_dist *dist = &kvm->arch.vgic; |
| int i; |
| u32 val = 0; |
| |
| irq -= VGIC_NR_PRIVATE_IRQS; |
| |
| for (i = 0; i < GICD_IRQS_PER_ITARGETSR; i++) |
| val |= 1 << (dist->irq_spi_cpu[irq + i] + i * 8); |
| |
| return val; |
| } |
| |
| static void vgic_set_target_reg(struct kvm *kvm, u32 val, int irq) |
| { |
| struct vgic_dist *dist = &kvm->arch.vgic; |
| struct kvm_vcpu *vcpu; |
| int i, c; |
| unsigned long *bmap; |
| u32 target; |
| |
| irq -= VGIC_NR_PRIVATE_IRQS; |
| |
| /* |
| * Pick the LSB in each byte. This ensures we target exactly |
| * one vcpu per IRQ. If the byte is null, assume we target |
| * CPU0. |
| */ |
| for (i = 0; i < GICD_IRQS_PER_ITARGETSR; i++) { |
| int shift = i * GICD_CPUTARGETS_BITS; |
| |
| target = ffs((val >> shift) & 0xffU); |
| target = target ? (target - 1) : 0; |
| dist->irq_spi_cpu[irq + i] = target; |
| kvm_for_each_vcpu(c, vcpu, kvm) { |
| bmap = vgic_bitmap_get_shared_map(&dist->irq_spi_target[c]); |
| if (c == target) |
| set_bit(irq + i, bmap); |
| else |
| clear_bit(irq + i, bmap); |
| } |
| } |
| } |
| |
| static bool handle_mmio_target_reg(struct kvm_vcpu *vcpu, |
| struct kvm_exit_mmio *mmio, |
| phys_addr_t offset) |
| { |
| u32 reg; |
| |
| /* We treat the banked interrupts targets as read-only */ |
| if (offset < 32) { |
| u32 roreg; |
| |
| roreg = 1 << vcpu->vcpu_id; |
| roreg |= roreg << 8; |
| roreg |= roreg << 16; |
| |
| vgic_reg_access(mmio, &roreg, offset, |
| ACCESS_READ_VALUE | ACCESS_WRITE_IGNORED); |
| return false; |
| } |
| |
| reg = vgic_get_target_reg(vcpu->kvm, offset & ~3U); |
| vgic_reg_access(mmio, ®, offset, |
| ACCESS_READ_VALUE | ACCESS_WRITE_VALUE); |
| if (mmio->is_write) { |
| vgic_set_target_reg(vcpu->kvm, reg, offset & ~3U); |
| vgic_update_state(vcpu->kvm); |
| return true; |
| } |
| |
| return false; |
| } |
| |
| static bool handle_mmio_cfg_reg(struct kvm_vcpu *vcpu, |
| struct kvm_exit_mmio *mmio, phys_addr_t offset) |
| { |
| u32 *reg; |
| |
| reg = vgic_bitmap_get_reg(&vcpu->kvm->arch.vgic.irq_cfg, |
| vcpu->vcpu_id, offset >> 1); |
| |
| return vgic_handle_cfg_reg(reg, mmio, offset); |
| } |
| |
| static bool handle_mmio_sgi_reg(struct kvm_vcpu *vcpu, |
| struct kvm_exit_mmio *mmio, phys_addr_t offset) |
| { |
| u32 reg; |
| |
| vgic_reg_access(mmio, ®, offset, |
| ACCESS_READ_RAZ | ACCESS_WRITE_VALUE); |
| if (mmio->is_write) { |
| vgic_dispatch_sgi(vcpu, reg); |
| vgic_update_state(vcpu->kvm); |
| return true; |
| } |
| |
| return false; |
| } |
| |
| /* Handle reads of GICD_CPENDSGIRn and GICD_SPENDSGIRn */ |
| static bool read_set_clear_sgi_pend_reg(struct kvm_vcpu *vcpu, |
| struct kvm_exit_mmio *mmio, |
| phys_addr_t offset) |
| { |
| struct vgic_dist *dist = &vcpu->kvm->arch.vgic; |
| int sgi; |
| int min_sgi = (offset & ~0x3); |
| int max_sgi = min_sgi + 3; |
| int vcpu_id = vcpu->vcpu_id; |
| u32 reg = 0; |
| |
| /* Copy source SGIs from distributor side */ |
| for (sgi = min_sgi; sgi <= max_sgi; sgi++) { |
| u8 sources = *vgic_get_sgi_sources(dist, vcpu_id, sgi); |
| |
| reg |= ((u32)sources) << (8 * (sgi - min_sgi)); |
| } |
| |
| mmio_data_write(mmio, ~0, reg); |
| return false; |
| } |
| |
| static bool write_set_clear_sgi_pend_reg(struct kvm_vcpu *vcpu, |
| struct kvm_exit_mmio *mmio, |
| phys_addr_t offset, bool set) |
| { |
| struct vgic_dist *dist = &vcpu->kvm->arch.vgic; |
| int sgi; |
| int min_sgi = (offset & ~0x3); |
| int max_sgi = min_sgi + 3; |
| int vcpu_id = vcpu->vcpu_id; |
| u32 reg; |
| bool updated = false; |
| |
| reg = mmio_data_read(mmio, ~0); |
| |
| /* Clear pending SGIs on the distributor */ |
| for (sgi = min_sgi; sgi <= max_sgi; sgi++) { |
| u8 mask = reg >> (8 * (sgi - min_sgi)); |
| u8 *src = vgic_get_sgi_sources(dist, vcpu_id, sgi); |
| |
| if (set) { |
| if ((*src & mask) != mask) |
| updated = true; |
| *src |= mask; |
| } else { |
| if (*src & mask) |
| updated = true; |
| *src &= ~mask; |
| } |
| } |
| |
| if (updated) |
| vgic_update_state(vcpu->kvm); |
| |
| return updated; |
| } |
| |
| static bool handle_mmio_sgi_set(struct kvm_vcpu *vcpu, |
| struct kvm_exit_mmio *mmio, |
| phys_addr_t offset) |
| { |
| if (!mmio->is_write) |
| return read_set_clear_sgi_pend_reg(vcpu, mmio, offset); |
| else |
| return write_set_clear_sgi_pend_reg(vcpu, mmio, offset, true); |
| } |
| |
| static bool handle_mmio_sgi_clear(struct kvm_vcpu *vcpu, |
| struct kvm_exit_mmio *mmio, |
| phys_addr_t offset) |
| { |
| if (!mmio->is_write) |
| return read_set_clear_sgi_pend_reg(vcpu, mmio, offset); |
| else |
| return write_set_clear_sgi_pend_reg(vcpu, mmio, offset, false); |
| } |
| |
| static const struct vgic_io_range vgic_dist_ranges[] = { |
| { |
| .base = GIC_DIST_SOFTINT, |
| .len = 4, |
| .handle_mmio = handle_mmio_sgi_reg, |
| }, |
| { |
| .base = GIC_DIST_CTRL, |
| .len = 12, |
| .bits_per_irq = 0, |
| .handle_mmio = handle_mmio_misc, |
| }, |
| { |
| .base = GIC_DIST_IGROUP, |
| .len = VGIC_MAX_IRQS / 8, |
| .bits_per_irq = 1, |
| .handle_mmio = handle_mmio_raz_wi, |
| }, |
| { |
| .base = GIC_DIST_ENABLE_SET, |
| .len = VGIC_MAX_IRQS / 8, |
| .bits_per_irq = 1, |
| .handle_mmio = handle_mmio_set_enable_reg, |
| }, |
| { |
| .base = GIC_DIST_ENABLE_CLEAR, |
| .len = VGIC_MAX_IRQS / 8, |
| .bits_per_irq = 1, |
| .handle_mmio = handle_mmio_clear_enable_reg, |
| }, |
| { |
| .base = GIC_DIST_PENDING_SET, |
| .len = VGIC_MAX_IRQS / 8, |
| .bits_per_irq = 1, |
| .handle_mmio = handle_mmio_set_pending_reg, |
| }, |
| { |
| .base = GIC_DIST_PENDING_CLEAR, |
| .len = VGIC_MAX_IRQS / 8, |
| .bits_per_irq = 1, |
| .handle_mmio = handle_mmio_clear_pending_reg, |
| }, |
| { |
| .base = GIC_DIST_ACTIVE_SET, |
| .len = VGIC_MAX_IRQS / 8, |
| .bits_per_irq = 1, |
| .handle_mmio = handle_mmio_set_active_reg, |
| }, |
| { |
| .base = GIC_DIST_ACTIVE_CLEAR, |
| .len = VGIC_MAX_IRQS / 8, |
| .bits_per_irq = 1, |
| .handle_mmio = handle_mmio_clear_active_reg, |
| }, |
| { |
| .base = GIC_DIST_PRI, |
| .len = VGIC_MAX_IRQS, |
| .bits_per_irq = 8, |
| .handle_mmio = handle_mmio_priority_reg, |
| }, |
| { |
| .base = GIC_DIST_TARGET, |
| .len = VGIC_MAX_IRQS, |
| .bits_per_irq = 8, |
| .handle_mmio = handle_mmio_target_reg, |
| }, |
| { |
| .base = GIC_DIST_CONFIG, |
| .len = VGIC_MAX_IRQS / 4, |
| .bits_per_irq = 2, |
| .handle_mmio = handle_mmio_cfg_reg, |
| }, |
| { |
| .base = GIC_DIST_SGI_PENDING_CLEAR, |
| .len = VGIC_NR_SGIS, |
| .handle_mmio = handle_mmio_sgi_clear, |
| }, |
| { |
| .base = GIC_DIST_SGI_PENDING_SET, |
| .len = VGIC_NR_SGIS, |
| .handle_mmio = handle_mmio_sgi_set, |
| }, |
| {} |
| }; |
| |
| static void vgic_dispatch_sgi(struct kvm_vcpu *vcpu, u32 reg) |
| { |
| struct kvm *kvm = vcpu->kvm; |
| struct vgic_dist *dist = &kvm->arch.vgic; |
| int nrcpus = atomic_read(&kvm->online_vcpus); |
| u8 target_cpus; |
| int sgi, mode, c, vcpu_id; |
| |
| vcpu_id = vcpu->vcpu_id; |
| |
| sgi = reg & 0xf; |
| target_cpus = (reg >> 16) & 0xff; |
| mode = (reg >> 24) & 3; |
| |
| switch (mode) { |
| case 0: |
| if (!target_cpus) |
| return; |
| break; |
| |
| case 1: |
| target_cpus = ((1 << nrcpus) - 1) & ~(1 << vcpu_id) & 0xff; |
| break; |
| |
| case 2: |
| target_cpus = 1 << vcpu_id; |
| break; |
| } |
| |
| kvm_for_each_vcpu(c, vcpu, kvm) { |
| if (target_cpus & 1) { |
| /* Flag the SGI as pending */ |
| vgic_dist_irq_set_pending(vcpu, sgi); |
| *vgic_get_sgi_sources(dist, c, sgi) |= 1 << vcpu_id; |
| kvm_debug("SGI%d from CPU%d to CPU%d\n", |
| sgi, vcpu_id, c); |
| } |
| |
| target_cpus >>= 1; |
| } |
| } |
| |
| static bool vgic_v2_queue_sgi(struct kvm_vcpu *vcpu, int irq) |
| { |
| struct vgic_dist *dist = &vcpu->kvm->arch.vgic; |
| unsigned long sources; |
| int vcpu_id = vcpu->vcpu_id; |
| int c; |
| |
| sources = *vgic_get_sgi_sources(dist, vcpu_id, irq); |
| |
| for_each_set_bit(c, &sources, dist->nr_cpus) { |
| if (vgic_queue_irq(vcpu, c, irq)) |
| clear_bit(c, &sources); |
| } |
| |
| *vgic_get_sgi_sources(dist, vcpu_id, irq) = sources; |
| |
| /* |
| * If the sources bitmap has been cleared it means that we |
| * could queue all the SGIs onto link registers (see the |
| * clear_bit above), and therefore we are done with them in |
| * our emulated gic and can get rid of them. |
| */ |
| if (!sources) { |
| vgic_dist_irq_clear_pending(vcpu, irq); |
| vgic_cpu_irq_clear(vcpu, irq); |
| return true; |
| } |
| |
| return false; |
| } |
| |
| /** |
| * kvm_vgic_map_resources - Configure global VGIC state before running any VCPUs |
| * @kvm: pointer to the kvm struct |
| * |
| * Map the virtual CPU interface into the VM before running any VCPUs. We |
| * can't do this at creation time, because user space must first set the |
| * virtual CPU interface address in the guest physical address space. |
| */ |
| static int vgic_v2_map_resources(struct kvm *kvm, |
| const struct vgic_params *params) |
| { |
| struct vgic_dist *dist = &kvm->arch.vgic; |
| int ret = 0; |
| |
| if (!irqchip_in_kernel(kvm)) |
| return 0; |
| |
| mutex_lock(&kvm->lock); |
| |
| if (vgic_ready(kvm)) |
| goto out; |
| |
| if (IS_VGIC_ADDR_UNDEF(dist->vgic_dist_base) || |
| IS_VGIC_ADDR_UNDEF(dist->vgic_cpu_base)) { |
| kvm_err("Need to set vgic cpu and dist addresses first\n"); |
| ret = -ENXIO; |
| goto out; |
| } |
| |
| vgic_register_kvm_io_dev(kvm, dist->vgic_dist_base, |
| KVM_VGIC_V2_DIST_SIZE, |
| vgic_dist_ranges, -1, &dist->dist_iodev); |
| |
| /* |
| * Initialize the vgic if this hasn't already been done on demand by |
| * accessing the vgic state from userspace. |
| */ |
| ret = vgic_init(kvm); |
| if (ret) { |
| kvm_err("Unable to allocate maps\n"); |
| goto out_unregister; |
| } |
| |
| ret = kvm_phys_addr_ioremap(kvm, dist->vgic_cpu_base, |
| params->vcpu_base, KVM_VGIC_V2_CPU_SIZE, |
| true); |
| if (ret) { |
| kvm_err("Unable to remap VGIC CPU to VCPU\n"); |
| goto out_unregister; |
| } |
| |
| dist->ready = true; |
| goto out; |
| |
| out_unregister: |
| kvm_io_bus_unregister_dev(kvm, KVM_MMIO_BUS, &dist->dist_iodev.dev); |
| |
| out: |
| if (ret) |
| kvm_vgic_destroy(kvm); |
| mutex_unlock(&kvm->lock); |
| return ret; |
| } |
| |
| static void vgic_v2_add_sgi_source(struct kvm_vcpu *vcpu, int irq, int source) |
| { |
| struct vgic_dist *dist = &vcpu->kvm->arch.vgic; |
| |
| *vgic_get_sgi_sources(dist, vcpu->vcpu_id, irq) |= 1 << source; |
| } |
| |
| static int vgic_v2_init_model(struct kvm *kvm) |
| { |
| int i; |
| |
| for (i = VGIC_NR_PRIVATE_IRQS; i < kvm->arch.vgic.nr_irqs; i += 4) |
| vgic_set_target_reg(kvm, 0, i); |
| |
| return 0; |
| } |
| |
| void vgic_v2_init_emulation(struct kvm *kvm) |
| { |
| struct vgic_dist *dist = &kvm->arch.vgic; |
| |
| dist->vm_ops.queue_sgi = vgic_v2_queue_sgi; |
| dist->vm_ops.add_sgi_source = vgic_v2_add_sgi_source; |
| dist->vm_ops.init_model = vgic_v2_init_model; |
| dist->vm_ops.map_resources = vgic_v2_map_resources; |
| |
| kvm->arch.max_vcpus = VGIC_V2_MAX_CPUS; |
| } |
| |
| static bool handle_cpu_mmio_misc(struct kvm_vcpu *vcpu, |
| struct kvm_exit_mmio *mmio, phys_addr_t offset) |
| { |
| bool updated = false; |
| struct vgic_vmcr vmcr; |
| u32 *vmcr_field; |
| u32 reg; |
| |
| vgic_get_vmcr(vcpu, &vmcr); |
| |
| switch (offset & ~0x3) { |
| case GIC_CPU_CTRL: |
| vmcr_field = &vmcr.ctlr; |
| break; |
| case GIC_CPU_PRIMASK: |
| vmcr_field = &vmcr.pmr; |
| break; |
| case GIC_CPU_BINPOINT: |
| vmcr_field = &vmcr.bpr; |
| break; |
| case GIC_CPU_ALIAS_BINPOINT: |
| vmcr_field = &vmcr.abpr; |
| break; |
| default: |
| BUG(); |
| } |
| |
| if (!mmio->is_write) { |
| reg = *vmcr_field; |
| mmio_data_write(mmio, ~0, reg); |
| } else { |
| reg = mmio_data_read(mmio, ~0); |
| if (reg != *vmcr_field) { |
| *vmcr_field = reg; |
| vgic_set_vmcr(vcpu, &vmcr); |
| updated = true; |
| } |
| } |
| return updated; |
| } |
| |
| static bool handle_mmio_abpr(struct kvm_vcpu *vcpu, |
| struct kvm_exit_mmio *mmio, phys_addr_t offset) |
| { |
| return handle_cpu_mmio_misc(vcpu, mmio, GIC_CPU_ALIAS_BINPOINT); |
| } |
| |
| static bool handle_cpu_mmio_ident(struct kvm_vcpu *vcpu, |
| struct kvm_exit_mmio *mmio, |
| phys_addr_t offset) |
| { |
| u32 reg; |
| |
| if (mmio->is_write) |
| return false; |
| |
| /* GICC_IIDR */ |
| reg = (PRODUCT_ID_KVM << 20) | |
| (GICC_ARCH_VERSION_V2 << 16) | |
| (IMPLEMENTER_ARM << 0); |
| mmio_data_write(mmio, ~0, reg); |
| return false; |
| } |
| |
| /* |
| * CPU Interface Register accesses - these are not accessed by the VM, but by |
| * user space for saving and restoring VGIC state. |
| */ |
| static const struct vgic_io_range vgic_cpu_ranges[] = { |
| { |
| .base = GIC_CPU_CTRL, |
| .len = 12, |
| .handle_mmio = handle_cpu_mmio_misc, |
| }, |
| { |
| .base = GIC_CPU_ALIAS_BINPOINT, |
| .len = 4, |
| .handle_mmio = handle_mmio_abpr, |
| }, |
| { |
| .base = GIC_CPU_ACTIVEPRIO, |
| .len = 16, |
| .handle_mmio = handle_mmio_raz_wi, |
| }, |
| { |
| .base = GIC_CPU_IDENT, |
| .len = 4, |
| .handle_mmio = handle_cpu_mmio_ident, |
| }, |
| }; |
| |
| static int vgic_attr_regs_access(struct kvm_device *dev, |
| struct kvm_device_attr *attr, |
| u32 *reg, bool is_write) |
| { |
| const struct vgic_io_range *r = NULL, *ranges; |
| phys_addr_t offset; |
| int ret, cpuid, c; |
| struct kvm_vcpu *vcpu, *tmp_vcpu; |
| struct vgic_dist *vgic; |
| struct kvm_exit_mmio mmio; |
| u32 data; |
| |
| offset = attr->attr & KVM_DEV_ARM_VGIC_OFFSET_MASK; |
| cpuid = (attr->attr & KVM_DEV_ARM_VGIC_CPUID_MASK) >> |
| KVM_DEV_ARM_VGIC_CPUID_SHIFT; |
| |
| mutex_lock(&dev->kvm->lock); |
| |
| ret = vgic_init(dev->kvm); |
| if (ret) |
| goto out; |
| |
| if (cpuid >= atomic_read(&dev->kvm->online_vcpus)) { |
| ret = -EINVAL; |
| goto out; |
| } |
| |
| vcpu = kvm_get_vcpu(dev->kvm, cpuid); |
| vgic = &dev->kvm->arch.vgic; |
| |
| mmio.len = 4; |
| mmio.is_write = is_write; |
| mmio.data = &data; |
| if (is_write) |
| mmio_data_write(&mmio, ~0, *reg); |
| switch (attr->group) { |
| case KVM_DEV_ARM_VGIC_GRP_DIST_REGS: |
| mmio.phys_addr = vgic->vgic_dist_base + offset; |
| ranges = vgic_dist_ranges; |
| break; |
| case KVM_DEV_ARM_VGIC_GRP_CPU_REGS: |
| mmio.phys_addr = vgic->vgic_cpu_base + offset; |
| ranges = vgic_cpu_ranges; |
| break; |
| default: |
| BUG(); |
| } |
| r = vgic_find_range(ranges, 4, offset); |
| |
| if (unlikely(!r || !r->handle_mmio)) { |
| ret = -ENXIO; |
| goto out; |
| } |
| |
| |
| spin_lock(&vgic->lock); |
| |
| /* |
| * Ensure that no other VCPU is running by checking the vcpu->cpu |
| * field. If no other VPCUs are running we can safely access the VGIC |
| * state, because even if another VPU is run after this point, that |
| * VCPU will not touch the vgic state, because it will block on |
| * getting the vgic->lock in kvm_vgic_sync_hwstate(). |
| */ |
| kvm_for_each_vcpu(c, tmp_vcpu, dev->kvm) { |
| if (unlikely(tmp_vcpu->cpu != -1)) { |
| ret = -EBUSY; |
| goto out_vgic_unlock; |
| } |
| } |
| |
| /* |
| * Move all pending IRQs from the LRs on all VCPUs so the pending |
| * state can be properly represented in the register state accessible |
| * through this API. |
| */ |
| kvm_for_each_vcpu(c, tmp_vcpu, dev->kvm) |
| vgic_unqueue_irqs(tmp_vcpu); |
| |
| offset -= r->base; |
| r->handle_mmio(vcpu, &mmio, offset); |
| |
| if (!is_write) |
| *reg = mmio_data_read(&mmio, ~0); |
| |
| ret = 0; |
| out_vgic_unlock: |
| spin_unlock(&vgic->lock); |
| out: |
| mutex_unlock(&dev->kvm->lock); |
| return ret; |
| } |
| |
| static int vgic_v2_create(struct kvm_device *dev, u32 type) |
| { |
| return kvm_vgic_create(dev->kvm, type); |
| } |
| |
| static void vgic_v2_destroy(struct kvm_device *dev) |
| { |
| kfree(dev); |
| } |
| |
| static int vgic_v2_set_attr(struct kvm_device *dev, |
| struct kvm_device_attr *attr) |
| { |
| int ret; |
| |
| ret = vgic_set_common_attr(dev, attr); |
| if (ret != -ENXIO) |
| return ret; |
| |
| switch (attr->group) { |
| case KVM_DEV_ARM_VGIC_GRP_DIST_REGS: |
| case KVM_DEV_ARM_VGIC_GRP_CPU_REGS: { |
| u32 __user *uaddr = (u32 __user *)(long)attr->addr; |
| u32 reg; |
| |
| if (get_user(reg, uaddr)) |
| return -EFAULT; |
| |
| return vgic_attr_regs_access(dev, attr, ®, true); |
| } |
| |
| } |
| |
| return -ENXIO; |
| } |
| |
| static int vgic_v2_get_attr(struct kvm_device *dev, |
| struct kvm_device_attr *attr) |
| { |
| int ret; |
| |
| ret = vgic_get_common_attr(dev, attr); |
| if (ret != -ENXIO) |
| return ret; |
| |
| switch (attr->group) { |
| case KVM_DEV_ARM_VGIC_GRP_DIST_REGS: |
| case KVM_DEV_ARM_VGIC_GRP_CPU_REGS: { |
| u32 __user *uaddr = (u32 __user *)(long)attr->addr; |
| u32 reg = 0; |
| |
| ret = vgic_attr_regs_access(dev, attr, ®, false); |
| if (ret) |
| return ret; |
| return put_user(reg, uaddr); |
| } |
| |
| } |
| |
| return -ENXIO; |
| } |
| |
| static int vgic_v2_has_attr(struct kvm_device *dev, |
| struct kvm_device_attr *attr) |
| { |
| phys_addr_t offset; |
| |
| switch (attr->group) { |
| case KVM_DEV_ARM_VGIC_GRP_ADDR: |
| switch (attr->attr) { |
| case KVM_VGIC_V2_ADDR_TYPE_DIST: |
| case KVM_VGIC_V2_ADDR_TYPE_CPU: |
| return 0; |
| } |
| break; |
| case KVM_DEV_ARM_VGIC_GRP_DIST_REGS: |
| offset = attr->attr & KVM_DEV_ARM_VGIC_OFFSET_MASK; |
| return vgic_has_attr_regs(vgic_dist_ranges, offset); |
| case KVM_DEV_ARM_VGIC_GRP_CPU_REGS: |
| offset = attr->attr & KVM_DEV_ARM_VGIC_OFFSET_MASK; |
| return vgic_has_attr_regs(vgic_cpu_ranges, offset); |
| case KVM_DEV_ARM_VGIC_GRP_NR_IRQS: |
| return 0; |
| case KVM_DEV_ARM_VGIC_GRP_CTRL: |
| switch (attr->attr) { |
| case KVM_DEV_ARM_VGIC_CTRL_INIT: |
| return 0; |
| } |
| } |
| return -ENXIO; |
| } |
| |
| struct kvm_device_ops kvm_arm_vgic_v2_ops = { |
| .name = "kvm-arm-vgic-v2", |
| .create = vgic_v2_create, |
| .destroy = vgic_v2_destroy, |
| .set_attr = vgic_v2_set_attr, |
| .get_attr = vgic_v2_get_attr, |
| .has_attr = vgic_v2_has_attr, |
| }; |