KVM: arm/arm64: The GIC is dead, long live the GIC
I don't think any single piece of the KVM/ARM code ever generated as much hatred as the GIC emulation. It was written by someone who had zero experience in modeling hardware (me), was riddled with design flaws, should have been scrapped and rewritten from scratch long before having a remote chance of reaching mainline, and yet we supported it for a good three years. No need to mention the names of those who suffered, the git log is singing their praises. Thankfully, we now have a much more maintainable implementation, and we can safely put the grumpy old GIC to rest. Fellow hackers, please raise your glass in memory of the GIC: The GIC is dead, long live the GIC! Signed-off-by: Marc Zyngier <marc.zyngier@arm.com> Signed-off-by: Christoffer Dall <christoffer.dall@linaro.org>
This commit is contained in:
parent
0996353f8e
commit
50926d82fa
@ -46,13 +46,6 @@ config KVM_ARM_HOST
|
||||
---help---
|
||||
Provides host support for ARM processors.
|
||||
|
||||
config KVM_NEW_VGIC
|
||||
bool "New VGIC implementation"
|
||||
depends on KVM
|
||||
default y
|
||||
---help---
|
||||
uses the new VGIC implementation
|
||||
|
||||
source drivers/vhost/Kconfig
|
||||
|
||||
endif # VIRTUALIZATION
|
||||
|
@ -22,7 +22,6 @@ obj-y += kvm-arm.o init.o interrupts.o
|
||||
obj-y += arm.o handle_exit.o guest.o mmu.o emulate.o reset.o
|
||||
obj-y += coproc.o coproc_a15.o coproc_a7.o mmio.o psci.o perf.o
|
||||
|
||||
ifeq ($(CONFIG_KVM_NEW_VGIC),y)
|
||||
obj-y += $(KVM)/arm/vgic/vgic.o
|
||||
obj-y += $(KVM)/arm/vgic/vgic-init.o
|
||||
obj-y += $(KVM)/arm/vgic/vgic-irqfd.o
|
||||
@ -30,9 +29,4 @@ obj-y += $(KVM)/arm/vgic/vgic-v2.o
|
||||
obj-y += $(KVM)/arm/vgic/vgic-mmio.o
|
||||
obj-y += $(KVM)/arm/vgic/vgic-mmio-v2.o
|
||||
obj-y += $(KVM)/arm/vgic/vgic-kvm-device.o
|
||||
else
|
||||
obj-y += $(KVM)/arm/vgic.o
|
||||
obj-y += $(KVM)/arm/vgic-v2.o
|
||||
obj-y += $(KVM)/arm/vgic-v2-emul.o
|
||||
endif
|
||||
obj-y += $(KVM)/arm/arch_timer.o
|
||||
|
@ -54,13 +54,6 @@ config KVM_ARM_PMU
|
||||
Adds support for a virtual Performance Monitoring Unit (PMU) in
|
||||
virtual machines.
|
||||
|
||||
config KVM_NEW_VGIC
|
||||
bool "New VGIC implementation"
|
||||
depends on KVM
|
||||
default y
|
||||
---help---
|
||||
uses the new VGIC implementation
|
||||
|
||||
source drivers/vhost/Kconfig
|
||||
|
||||
endif # VIRTUALIZATION
|
||||
|
@ -20,7 +20,6 @@ kvm-$(CONFIG_KVM_ARM_HOST) += emulate.o inject_fault.o regmap.o
|
||||
kvm-$(CONFIG_KVM_ARM_HOST) += hyp.o hyp-init.o handle_exit.o
|
||||
kvm-$(CONFIG_KVM_ARM_HOST) += guest.o debug.o reset.o sys_regs.o sys_regs_generic_v8.o
|
||||
|
||||
ifeq ($(CONFIG_KVM_NEW_VGIC),y)
|
||||
kvm-$(CONFIG_KVM_ARM_HOST) += $(KVM)/arm/vgic/vgic.o
|
||||
kvm-$(CONFIG_KVM_ARM_HOST) += $(KVM)/arm/vgic/vgic-init.o
|
||||
kvm-$(CONFIG_KVM_ARM_HOST) += $(KVM)/arm/vgic/vgic-irqfd.o
|
||||
@ -30,12 +29,5 @@ kvm-$(CONFIG_KVM_ARM_HOST) += $(KVM)/arm/vgic/vgic-mmio.o
|
||||
kvm-$(CONFIG_KVM_ARM_HOST) += $(KVM)/arm/vgic/vgic-mmio-v2.o
|
||||
kvm-$(CONFIG_KVM_ARM_HOST) += $(KVM)/arm/vgic/vgic-mmio-v3.o
|
||||
kvm-$(CONFIG_KVM_ARM_HOST) += $(KVM)/arm/vgic/vgic-kvm-device.o
|
||||
else
|
||||
kvm-$(CONFIG_KVM_ARM_HOST) += $(KVM)/arm/vgic.o
|
||||
kvm-$(CONFIG_KVM_ARM_HOST) += $(KVM)/arm/vgic-v2.o
|
||||
kvm-$(CONFIG_KVM_ARM_HOST) += $(KVM)/arm/vgic-v2-emul.o
|
||||
kvm-$(CONFIG_KVM_ARM_HOST) += $(KVM)/arm/vgic-v3.o
|
||||
kvm-$(CONFIG_KVM_ARM_HOST) += $(KVM)/arm/vgic-v3-emul.o
|
||||
endif
|
||||
kvm-$(CONFIG_KVM_ARM_HOST) += $(KVM)/arm/arch_timer.o
|
||||
kvm-$(CONFIG_KVM_ARM_PMU) += $(KVM)/arm/pmu.o
|
||||
|
@ -1,6 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2012 ARM Ltd.
|
||||
* Author: Marc Zyngier <marc.zyngier@arm.com>
|
||||
* Copyright (C) 2015, 2016 ARM Ltd.
|
||||
*
|
||||
* 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
|
||||
@ -12,16 +11,10 @@
|
||||
* 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, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef __ASM_ARM_KVM_VGIC_H
|
||||
#define __ASM_ARM_KVM_VGIC_H
|
||||
|
||||
#ifdef CONFIG_KVM_NEW_VGIC
|
||||
#include <kvm/vgic/vgic.h>
|
||||
#else
|
||||
#ifndef __KVM_ARM_VGIC_H
|
||||
#define __KVM_ARM_VGIC_H
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/kvm.h>
|
||||
@ -29,248 +22,130 @@
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/types.h>
|
||||
#include <kvm/iodev.h>
|
||||
#include <linux/irqchip/arm-gic-common.h>
|
||||
|
||||
#define VGIC_NR_IRQS_LEGACY 256
|
||||
#define VGIC_V3_MAX_CPUS 255
|
||||
#define VGIC_V2_MAX_CPUS 8
|
||||
#define VGIC_NR_IRQS_LEGACY 256
|
||||
#define VGIC_NR_SGIS 16
|
||||
#define VGIC_NR_PPIS 16
|
||||
#define VGIC_NR_PRIVATE_IRQS (VGIC_NR_SGIS + VGIC_NR_PPIS)
|
||||
|
||||
#define VGIC_V2_MAX_LRS (1 << 6)
|
||||
#define VGIC_V3_MAX_LRS 16
|
||||
#define VGIC_MAX_IRQS 1024
|
||||
#define VGIC_V2_MAX_CPUS 8
|
||||
#define VGIC_V3_MAX_CPUS 255
|
||||
|
||||
#if (VGIC_NR_IRQS_LEGACY & 31)
|
||||
#error "VGIC_NR_IRQS must be a multiple of 32"
|
||||
#endif
|
||||
|
||||
#if (VGIC_NR_IRQS_LEGACY > VGIC_MAX_IRQS)
|
||||
#error "VGIC_NR_IRQS must be <= 1024"
|
||||
#endif
|
||||
|
||||
/*
|
||||
* The GIC distributor registers describing interrupts have two parts:
|
||||
* - 32 per-CPU interrupts (SGI + PPI)
|
||||
* - a bunch of shared interrupts (SPI)
|
||||
*/
|
||||
struct vgic_bitmap {
|
||||
/*
|
||||
* - One UL per VCPU for private interrupts (assumes UL is at
|
||||
* least 32 bits)
|
||||
* - As many UL as necessary for shared interrupts.
|
||||
*
|
||||
* The private interrupts are accessed via the "private"
|
||||
* field, one UL per vcpu (the state for vcpu n is in
|
||||
* private[n]). The shared interrupts are accessed via the
|
||||
* "shared" pointer (IRQn state is at bit n-32 in the bitmap).
|
||||
*/
|
||||
unsigned long *private;
|
||||
unsigned long *shared;
|
||||
};
|
||||
|
||||
struct vgic_bytemap {
|
||||
/*
|
||||
* - 8 u32 per VCPU for private interrupts
|
||||
* - As many u32 as necessary for shared interrupts.
|
||||
*
|
||||
* The private interrupts are accessed via the "private"
|
||||
* field, (the state for vcpu n is in private[n*8] to
|
||||
* private[n*8 + 7]). The shared interrupts are accessed via
|
||||
* the "shared" pointer (IRQn state is at byte (n-32)%4 of the
|
||||
* shared[(n-32)/4] word).
|
||||
*/
|
||||
u32 *private;
|
||||
u32 *shared;
|
||||
};
|
||||
|
||||
struct kvm_vcpu;
|
||||
#define VGIC_MAX_PRIVATE (VGIC_NR_PRIVATE_IRQS - 1)
|
||||
#define VGIC_MAX_SPI 1019
|
||||
#define VGIC_MAX_RESERVED 1023
|
||||
#define VGIC_MIN_LPI 8192
|
||||
|
||||
enum vgic_type {
|
||||
VGIC_V2, /* Good ol' GICv2 */
|
||||
VGIC_V3, /* New fancy GICv3 */
|
||||
};
|
||||
|
||||
#define LR_STATE_PENDING (1 << 0)
|
||||
#define LR_STATE_ACTIVE (1 << 1)
|
||||
#define LR_STATE_MASK (3 << 0)
|
||||
#define LR_EOI_INT (1 << 2)
|
||||
#define LR_HW (1 << 3)
|
||||
/* same for all guests, as depending only on the _host's_ GIC model */
|
||||
struct vgic_global {
|
||||
/* type of the host GIC */
|
||||
enum vgic_type type;
|
||||
|
||||
struct vgic_lr {
|
||||
unsigned irq:10;
|
||||
union {
|
||||
unsigned hwirq:10;
|
||||
unsigned source:3;
|
||||
};
|
||||
unsigned state:4;
|
||||
};
|
||||
|
||||
struct vgic_vmcr {
|
||||
u32 ctlr;
|
||||
u32 abpr;
|
||||
u32 bpr;
|
||||
u32 pmr;
|
||||
};
|
||||
|
||||
struct vgic_ops {
|
||||
struct vgic_lr (*get_lr)(const struct kvm_vcpu *, int);
|
||||
void (*set_lr)(struct kvm_vcpu *, int, struct vgic_lr);
|
||||
u64 (*get_elrsr)(const struct kvm_vcpu *vcpu);
|
||||
u64 (*get_eisr)(const struct kvm_vcpu *vcpu);
|
||||
void (*clear_eisr)(struct kvm_vcpu *vcpu);
|
||||
u32 (*get_interrupt_status)(const struct kvm_vcpu *vcpu);
|
||||
void (*enable_underflow)(struct kvm_vcpu *vcpu);
|
||||
void (*disable_underflow)(struct kvm_vcpu *vcpu);
|
||||
void (*get_vmcr)(struct kvm_vcpu *vcpu, struct vgic_vmcr *vmcr);
|
||||
void (*set_vmcr)(struct kvm_vcpu *vcpu, struct vgic_vmcr *vmcr);
|
||||
void (*enable)(struct kvm_vcpu *vcpu);
|
||||
};
|
||||
|
||||
struct vgic_params {
|
||||
/* vgic type */
|
||||
enum vgic_type type;
|
||||
/* Physical address of vgic virtual cpu interface */
|
||||
phys_addr_t vcpu_base;
|
||||
/* Number of list registers */
|
||||
u32 nr_lr;
|
||||
/* Interrupt number */
|
||||
unsigned int maint_irq;
|
||||
/* Virtual control interface base address */
|
||||
void __iomem *vctrl_base;
|
||||
int max_gic_vcpus;
|
||||
phys_addr_t vcpu_base;
|
||||
|
||||
/* virtual control interface mapping */
|
||||
void __iomem *vctrl_base;
|
||||
|
||||
/* Number of implemented list registers */
|
||||
int nr_lr;
|
||||
|
||||
/* Maintenance IRQ number */
|
||||
unsigned int maint_irq;
|
||||
|
||||
/* maximum number of VCPUs allowed (GICv2 limits us to 8) */
|
||||
int max_gic_vcpus;
|
||||
|
||||
/* Only needed for the legacy KVM_CREATE_IRQCHIP */
|
||||
bool can_emulate_gicv2;
|
||||
bool can_emulate_gicv2;
|
||||
};
|
||||
|
||||
struct vgic_vm_ops {
|
||||
bool (*queue_sgi)(struct kvm_vcpu *, int irq);
|
||||
void (*add_sgi_source)(struct kvm_vcpu *, int irq, int source);
|
||||
int (*init_model)(struct kvm *);
|
||||
int (*map_resources)(struct kvm *, const struct vgic_params *);
|
||||
extern struct vgic_global kvm_vgic_global_state;
|
||||
|
||||
#define VGIC_V2_MAX_LRS (1 << 6)
|
||||
#define VGIC_V3_MAX_LRS 16
|
||||
#define VGIC_V3_LR_INDEX(lr) (VGIC_V3_MAX_LRS - 1 - lr)
|
||||
|
||||
enum vgic_irq_config {
|
||||
VGIC_CONFIG_EDGE = 0,
|
||||
VGIC_CONFIG_LEVEL
|
||||
};
|
||||
|
||||
struct vgic_irq {
|
||||
spinlock_t irq_lock; /* Protects the content of the struct */
|
||||
struct list_head ap_list;
|
||||
|
||||
struct kvm_vcpu *vcpu; /* SGIs and PPIs: The VCPU
|
||||
* SPIs and LPIs: The VCPU whose ap_list
|
||||
* this is queued on.
|
||||
*/
|
||||
|
||||
struct kvm_vcpu *target_vcpu; /* The VCPU that this interrupt should
|
||||
* be sent to, as a result of the
|
||||
* targets reg (v2) or the
|
||||
* affinity reg (v3).
|
||||
*/
|
||||
|
||||
u32 intid; /* Guest visible INTID */
|
||||
bool pending;
|
||||
bool line_level; /* Level only */
|
||||
bool soft_pending; /* Level only */
|
||||
bool active; /* not used for LPIs */
|
||||
bool enabled;
|
||||
bool hw; /* Tied to HW IRQ */
|
||||
u32 hwintid; /* HW INTID number */
|
||||
union {
|
||||
u8 targets; /* GICv2 target VCPUs mask */
|
||||
u32 mpidr; /* GICv3 target VCPU */
|
||||
};
|
||||
u8 source; /* GICv2 SGIs only */
|
||||
u8 priority;
|
||||
enum vgic_irq_config config; /* Level or edge */
|
||||
};
|
||||
|
||||
struct vgic_register_region;
|
||||
|
||||
struct vgic_io_device {
|
||||
gpa_t addr;
|
||||
int len;
|
||||
const struct vgic_io_range *reg_ranges;
|
||||
gpa_t base_addr;
|
||||
struct kvm_vcpu *redist_vcpu;
|
||||
const struct vgic_register_region *regions;
|
||||
int nr_regions;
|
||||
struct kvm_io_device dev;
|
||||
};
|
||||
|
||||
struct irq_phys_map {
|
||||
u32 virt_irq;
|
||||
u32 phys_irq;
|
||||
};
|
||||
|
||||
struct irq_phys_map_entry {
|
||||
struct list_head entry;
|
||||
struct rcu_head rcu;
|
||||
struct irq_phys_map map;
|
||||
};
|
||||
|
||||
struct vgic_dist {
|
||||
spinlock_t lock;
|
||||
bool in_kernel;
|
||||
bool ready;
|
||||
bool initialized;
|
||||
|
||||
/* vGIC model the kernel emulates for the guest (GICv2 or GICv3) */
|
||||
u32 vgic_model;
|
||||
|
||||
int nr_cpus;
|
||||
int nr_irqs;
|
||||
int nr_spis;
|
||||
|
||||
/* TODO: Consider moving to global state */
|
||||
/* Virtual control interface mapping */
|
||||
void __iomem *vctrl_base;
|
||||
|
||||
/* Distributor and vcpu interface mapping in the guest */
|
||||
phys_addr_t vgic_dist_base;
|
||||
/* GICv2 and GICv3 use different mapped register blocks */
|
||||
/* base addresses in guest physical address space: */
|
||||
gpa_t vgic_dist_base; /* distributor */
|
||||
union {
|
||||
phys_addr_t vgic_cpu_base;
|
||||
phys_addr_t vgic_redist_base;
|
||||
/* either a GICv2 CPU interface */
|
||||
gpa_t vgic_cpu_base;
|
||||
/* or a number of GICv3 redistributor regions */
|
||||
gpa_t vgic_redist_base;
|
||||
};
|
||||
|
||||
/* Distributor enabled */
|
||||
u32 enabled;
|
||||
/* distributor enabled */
|
||||
bool enabled;
|
||||
|
||||
/* Interrupt enabled (one bit per IRQ) */
|
||||
struct vgic_bitmap irq_enabled;
|
||||
struct vgic_irq *spis;
|
||||
|
||||
/* Level-triggered interrupt external input is asserted */
|
||||
struct vgic_bitmap irq_level;
|
||||
|
||||
/*
|
||||
* Interrupt state is pending on the distributor
|
||||
*/
|
||||
struct vgic_bitmap irq_pending;
|
||||
|
||||
/*
|
||||
* Tracks writes to GICD_ISPENDRn and GICD_ICPENDRn for level-triggered
|
||||
* interrupts. Essentially holds the state of the flip-flop in
|
||||
* Figure 4-10 on page 4-101 in ARM IHI 0048B.b.
|
||||
* Once set, it is only cleared for level-triggered interrupts on
|
||||
* guest ACKs (when we queue it) or writes to GICD_ICPENDRn.
|
||||
*/
|
||||
struct vgic_bitmap irq_soft_pend;
|
||||
|
||||
/* Level-triggered interrupt queued on VCPU interface */
|
||||
struct vgic_bitmap irq_queued;
|
||||
|
||||
/* Interrupt was active when unqueue from VCPU interface */
|
||||
struct vgic_bitmap irq_active;
|
||||
|
||||
/* Interrupt priority. Not used yet. */
|
||||
struct vgic_bytemap irq_priority;
|
||||
|
||||
/* Level/edge triggered */
|
||||
struct vgic_bitmap irq_cfg;
|
||||
|
||||
/*
|
||||
* Source CPU per SGI and target CPU:
|
||||
*
|
||||
* Each byte represent a SGI observable on a VCPU, each bit of
|
||||
* this byte indicating if the corresponding VCPU has
|
||||
* generated this interrupt. This is a GICv2 feature only.
|
||||
*
|
||||
* For VCPUn (n < 8), irq_sgi_sources[n*16] to [n*16 + 15] are
|
||||
* the SGIs observable on VCPUn.
|
||||
*/
|
||||
u8 *irq_sgi_sources;
|
||||
|
||||
/*
|
||||
* Target CPU for each SPI:
|
||||
*
|
||||
* Array of available SPI, each byte indicating the target
|
||||
* VCPU for SPI. IRQn (n >=32) is at irq_spi_cpu[n-32].
|
||||
*/
|
||||
u8 *irq_spi_cpu;
|
||||
|
||||
/*
|
||||
* Reverse lookup of irq_spi_cpu for faster compute pending:
|
||||
*
|
||||
* Array of bitmaps, one per VCPU, describing if IRQn is
|
||||
* routed to a particular VCPU.
|
||||
*/
|
||||
struct vgic_bitmap *irq_spi_target;
|
||||
|
||||
/* Target MPIDR for each IRQ (needed for GICv3 IROUTERn) only */
|
||||
u32 *irq_spi_mpidr;
|
||||
|
||||
/* Bitmap indicating which CPU has something pending */
|
||||
unsigned long *irq_pending_on_cpu;
|
||||
|
||||
/* Bitmap indicating which CPU has active IRQs */
|
||||
unsigned long *irq_active_on_cpu;
|
||||
|
||||
struct vgic_vm_ops vm_ops;
|
||||
struct vgic_io_device dist_iodev;
|
||||
struct vgic_io_device *redist_iodevs;
|
||||
|
||||
/* Virtual irq to hwirq mapping */
|
||||
spinlock_t irq_phys_map_lock;
|
||||
struct list_head irq_phys_map_list;
|
||||
};
|
||||
|
||||
struct vgic_v2_cpu_if {
|
||||
@ -298,78 +173,74 @@ struct vgic_v3_cpu_if {
|
||||
};
|
||||
|
||||
struct vgic_cpu {
|
||||
/* Pending/active/both interrupts on this VCPU */
|
||||
DECLARE_BITMAP(pending_percpu, VGIC_NR_PRIVATE_IRQS);
|
||||
DECLARE_BITMAP(active_percpu, VGIC_NR_PRIVATE_IRQS);
|
||||
DECLARE_BITMAP(pend_act_percpu, VGIC_NR_PRIVATE_IRQS);
|
||||
|
||||
/* Pending/active/both shared interrupts, dynamically sized */
|
||||
unsigned long *pending_shared;
|
||||
unsigned long *active_shared;
|
||||
unsigned long *pend_act_shared;
|
||||
|
||||
/* CPU vif control registers for world switch */
|
||||
union {
|
||||
struct vgic_v2_cpu_if vgic_v2;
|
||||
struct vgic_v3_cpu_if vgic_v3;
|
||||
};
|
||||
|
||||
/* Protected by the distributor's irq_phys_map_lock */
|
||||
struct list_head irq_phys_map_list;
|
||||
unsigned int used_lrs;
|
||||
struct vgic_irq private_irqs[VGIC_NR_PRIVATE_IRQS];
|
||||
|
||||
u64 live_lrs;
|
||||
spinlock_t ap_list_lock; /* Protects the ap_list */
|
||||
|
||||
/*
|
||||
* List of IRQs that this VCPU should consider because they are either
|
||||
* Active or Pending (hence the name; AP list), or because they recently
|
||||
* were one of the two and need to be migrated off this list to another
|
||||
* VCPU.
|
||||
*/
|
||||
struct list_head ap_list_head;
|
||||
|
||||
u64 live_lrs;
|
||||
};
|
||||
|
||||
#define LR_EMPTY 0xff
|
||||
|
||||
#define INT_STATUS_EOI (1 << 0)
|
||||
#define INT_STATUS_UNDERFLOW (1 << 1)
|
||||
|
||||
struct kvm;
|
||||
struct kvm_vcpu;
|
||||
|
||||
int kvm_vgic_addr(struct kvm *kvm, unsigned long type, u64 *addr, bool write);
|
||||
int kvm_vgic_hyp_init(void);
|
||||
int kvm_vgic_map_resources(struct kvm *kvm);
|
||||
int kvm_vgic_get_max_vcpus(void);
|
||||
void kvm_vgic_early_init(struct kvm *kvm);
|
||||
int kvm_vgic_create(struct kvm *kvm, u32 type);
|
||||
void kvm_vgic_destroy(struct kvm *kvm);
|
||||
void kvm_vgic_vcpu_early_init(struct kvm_vcpu *vcpu);
|
||||
void kvm_vgic_vcpu_destroy(struct kvm_vcpu *vcpu);
|
||||
void kvm_vgic_flush_hwstate(struct kvm_vcpu *vcpu);
|
||||
void kvm_vgic_sync_hwstate(struct kvm_vcpu *vcpu);
|
||||
int kvm_vgic_inject_irq(struct kvm *kvm, int cpuid, unsigned int irq_num,
|
||||
int kvm_vgic_map_resources(struct kvm *kvm);
|
||||
int kvm_vgic_hyp_init(void);
|
||||
|
||||
int kvm_vgic_inject_irq(struct kvm *kvm, int cpuid, unsigned int intid,
|
||||
bool level);
|
||||
int kvm_vgic_inject_mapped_irq(struct kvm *kvm, int cpuid,
|
||||
unsigned int virt_irq, bool level);
|
||||
void vgic_v3_dispatch_sgi(struct kvm_vcpu *vcpu, u64 reg);
|
||||
int kvm_vgic_vcpu_pending_irq(struct kvm_vcpu *vcpu);
|
||||
int kvm_vgic_map_phys_irq(struct kvm_vcpu *vcpu, int virt_irq, int phys_irq);
|
||||
int kvm_vgic_inject_mapped_irq(struct kvm *kvm, int cpuid, unsigned int intid,
|
||||
bool level);
|
||||
int kvm_vgic_map_phys_irq(struct kvm_vcpu *vcpu, u32 virt_irq, u32 phys_irq);
|
||||
int kvm_vgic_unmap_phys_irq(struct kvm_vcpu *vcpu, unsigned int virt_irq);
|
||||
bool kvm_vgic_map_is_active(struct kvm_vcpu *vcpu, unsigned int virt_irq);
|
||||
|
||||
int kvm_vgic_vcpu_pending_irq(struct kvm_vcpu *vcpu);
|
||||
|
||||
#define irqchip_in_kernel(k) (!!((k)->arch.vgic.in_kernel))
|
||||
#define vgic_initialized(k) (!!((k)->arch.vgic.nr_cpus))
|
||||
#define vgic_initialized(k) ((k)->arch.vgic.initialized)
|
||||
#define vgic_ready(k) ((k)->arch.vgic.ready)
|
||||
#define vgic_valid_spi(k, i) (((i) >= VGIC_NR_PRIVATE_IRQS) && \
|
||||
((i) < (k)->arch.vgic.nr_irqs))
|
||||
((i) < (k)->arch.vgic.nr_spis + VGIC_NR_PRIVATE_IRQS))
|
||||
|
||||
bool kvm_vcpu_has_pending_irqs(struct kvm_vcpu *vcpu);
|
||||
void kvm_vgic_sync_hwstate(struct kvm_vcpu *vcpu);
|
||||
void kvm_vgic_flush_hwstate(struct kvm_vcpu *vcpu);
|
||||
|
||||
int vgic_v2_probe(const struct gic_kvm_info *gic_kvm_info,
|
||||
const struct vgic_ops **ops,
|
||||
const struct vgic_params **params);
|
||||
#ifdef CONFIG_KVM_ARM_VGIC_V3
|
||||
int vgic_v3_probe(const struct gic_kvm_info *gic_kvm_info,
|
||||
const struct vgic_ops **ops,
|
||||
const struct vgic_params **params);
|
||||
void vgic_v3_dispatch_sgi(struct kvm_vcpu *vcpu, u64 reg);
|
||||
#else
|
||||
static inline int vgic_v3_probe(const struct gic_kvm_info *gic_kvm_info,
|
||||
const struct vgic_ops **ops,
|
||||
const struct vgic_params **params)
|
||||
static inline void vgic_v3_dispatch_sgi(struct kvm_vcpu *vcpu, u64 reg)
|
||||
{
|
||||
return -ENODEV;
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* old VGIC include */
|
||||
#endif
|
||||
/**
|
||||
* kvm_vgic_get_max_vcpus - Get the maximum number of VCPUs allowed by HW
|
||||
*
|
||||
* The host's GIC naturally limits the maximum amount of VCPUs a guest
|
||||
* can use.
|
||||
*/
|
||||
static inline int kvm_vgic_get_max_vcpus(void)
|
||||
{
|
||||
return kvm_vgic_global_state.max_gic_vcpus;
|
||||
}
|
||||
|
||||
#endif /* __KVM_ARM_VGIC_H */
|
||||
|
@ -1,246 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2015, 2016 ARM Ltd.
|
||||
*
|
||||
* 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/>.
|
||||
*/
|
||||
#ifndef __ASM_ARM_KVM_VGIC_VGIC_H
|
||||
#define __ASM_ARM_KVM_VGIC_VGIC_H
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/kvm.h>
|
||||
#include <linux/irqreturn.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/types.h>
|
||||
#include <kvm/iodev.h>
|
||||
|
||||
#define VGIC_V3_MAX_CPUS 255
|
||||
#define VGIC_V2_MAX_CPUS 8
|
||||
#define VGIC_NR_IRQS_LEGACY 256
|
||||
#define VGIC_NR_SGIS 16
|
||||
#define VGIC_NR_PPIS 16
|
||||
#define VGIC_NR_PRIVATE_IRQS (VGIC_NR_SGIS + VGIC_NR_PPIS)
|
||||
#define VGIC_MAX_PRIVATE (VGIC_NR_PRIVATE_IRQS - 1)
|
||||
#define VGIC_MAX_SPI 1019
|
||||
#define VGIC_MAX_RESERVED 1023
|
||||
#define VGIC_MIN_LPI 8192
|
||||
|
||||
enum vgic_type {
|
||||
VGIC_V2, /* Good ol' GICv2 */
|
||||
VGIC_V3, /* New fancy GICv3 */
|
||||
};
|
||||
|
||||
/* same for all guests, as depending only on the _host's_ GIC model */
|
||||
struct vgic_global {
|
||||
/* type of the host GIC */
|
||||
enum vgic_type type;
|
||||
|
||||
/* Physical address of vgic virtual cpu interface */
|
||||
phys_addr_t vcpu_base;
|
||||
|
||||
/* virtual control interface mapping */
|
||||
void __iomem *vctrl_base;
|
||||
|
||||
/* Number of implemented list registers */
|
||||
int nr_lr;
|
||||
|
||||
/* Maintenance IRQ number */
|
||||
unsigned int maint_irq;
|
||||
|
||||
/* maximum number of VCPUs allowed (GICv2 limits us to 8) */
|
||||
int max_gic_vcpus;
|
||||
|
||||
/* Only needed for the legacy KVM_CREATE_IRQCHIP */
|
||||
bool can_emulate_gicv2;
|
||||
};
|
||||
|
||||
extern struct vgic_global kvm_vgic_global_state;
|
||||
|
||||
#define VGIC_V2_MAX_LRS (1 << 6)
|
||||
#define VGIC_V3_MAX_LRS 16
|
||||
#define VGIC_V3_LR_INDEX(lr) (VGIC_V3_MAX_LRS - 1 - lr)
|
||||
|
||||
enum vgic_irq_config {
|
||||
VGIC_CONFIG_EDGE = 0,
|
||||
VGIC_CONFIG_LEVEL
|
||||
};
|
||||
|
||||
struct vgic_irq {
|
||||
spinlock_t irq_lock; /* Protects the content of the struct */
|
||||
struct list_head ap_list;
|
||||
|
||||
struct kvm_vcpu *vcpu; /* SGIs and PPIs: The VCPU
|
||||
* SPIs and LPIs: The VCPU whose ap_list
|
||||
* this is queued on.
|
||||
*/
|
||||
|
||||
struct kvm_vcpu *target_vcpu; /* The VCPU that this interrupt should
|
||||
* be sent to, as a result of the
|
||||
* targets reg (v2) or the
|
||||
* affinity reg (v3).
|
||||
*/
|
||||
|
||||
u32 intid; /* Guest visible INTID */
|
||||
bool pending;
|
||||
bool line_level; /* Level only */
|
||||
bool soft_pending; /* Level only */
|
||||
bool active; /* not used for LPIs */
|
||||
bool enabled;
|
||||
bool hw; /* Tied to HW IRQ */
|
||||
u32 hwintid; /* HW INTID number */
|
||||
union {
|
||||
u8 targets; /* GICv2 target VCPUs mask */
|
||||
u32 mpidr; /* GICv3 target VCPU */
|
||||
};
|
||||
u8 source; /* GICv2 SGIs only */
|
||||
u8 priority;
|
||||
enum vgic_irq_config config; /* Level or edge */
|
||||
};
|
||||
|
||||
struct vgic_register_region;
|
||||
|
||||
struct vgic_io_device {
|
||||
gpa_t base_addr;
|
||||
struct kvm_vcpu *redist_vcpu;
|
||||
const struct vgic_register_region *regions;
|
||||
int nr_regions;
|
||||
struct kvm_io_device dev;
|
||||
};
|
||||
|
||||
struct vgic_dist {
|
||||
bool in_kernel;
|
||||
bool ready;
|
||||
bool initialized;
|
||||
|
||||
/* vGIC model the kernel emulates for the guest (GICv2 or GICv3) */
|
||||
u32 vgic_model;
|
||||
|
||||
int nr_spis;
|
||||
|
||||
/* TODO: Consider moving to global state */
|
||||
/* Virtual control interface mapping */
|
||||
void __iomem *vctrl_base;
|
||||
|
||||
/* base addresses in guest physical address space: */
|
||||
gpa_t vgic_dist_base; /* distributor */
|
||||
union {
|
||||
/* either a GICv2 CPU interface */
|
||||
gpa_t vgic_cpu_base;
|
||||
/* or a number of GICv3 redistributor regions */
|
||||
gpa_t vgic_redist_base;
|
||||
};
|
||||
|
||||
/* distributor enabled */
|
||||
bool enabled;
|
||||
|
||||
struct vgic_irq *spis;
|
||||
|
||||
struct vgic_io_device dist_iodev;
|
||||
struct vgic_io_device *redist_iodevs;
|
||||
};
|
||||
|
||||
struct vgic_v2_cpu_if {
|
||||
u32 vgic_hcr;
|
||||
u32 vgic_vmcr;
|
||||
u32 vgic_misr; /* Saved only */
|
||||
u64 vgic_eisr; /* Saved only */
|
||||
u64 vgic_elrsr; /* Saved only */
|
||||
u32 vgic_apr;
|
||||
u32 vgic_lr[VGIC_V2_MAX_LRS];
|
||||
};
|
||||
|
||||
struct vgic_v3_cpu_if {
|
||||
#ifdef CONFIG_KVM_ARM_VGIC_V3
|
||||
u32 vgic_hcr;
|
||||
u32 vgic_vmcr;
|
||||
u32 vgic_sre; /* Restored only, change ignored */
|
||||
u32 vgic_misr; /* Saved only */
|
||||
u32 vgic_eisr; /* Saved only */
|
||||
u32 vgic_elrsr; /* Saved only */
|
||||
u32 vgic_ap0r[4];
|
||||
u32 vgic_ap1r[4];
|
||||
u64 vgic_lr[VGIC_V3_MAX_LRS];
|
||||
#endif
|
||||
};
|
||||
|
||||
struct vgic_cpu {
|
||||
/* CPU vif control registers for world switch */
|
||||
union {
|
||||
struct vgic_v2_cpu_if vgic_v2;
|
||||
struct vgic_v3_cpu_if vgic_v3;
|
||||
};
|
||||
|
||||
unsigned int used_lrs;
|
||||
struct vgic_irq private_irqs[VGIC_NR_PRIVATE_IRQS];
|
||||
|
||||
spinlock_t ap_list_lock; /* Protects the ap_list */
|
||||
|
||||
/*
|
||||
* List of IRQs that this VCPU should consider because they are either
|
||||
* Active or Pending (hence the name; AP list), or because they recently
|
||||
* were one of the two and need to be migrated off this list to another
|
||||
* VCPU.
|
||||
*/
|
||||
struct list_head ap_list_head;
|
||||
|
||||
u64 live_lrs;
|
||||
};
|
||||
|
||||
int kvm_vgic_addr(struct kvm *kvm, unsigned long type, u64 *addr, bool write);
|
||||
void kvm_vgic_early_init(struct kvm *kvm);
|
||||
int kvm_vgic_create(struct kvm *kvm, u32 type);
|
||||
void kvm_vgic_destroy(struct kvm *kvm);
|
||||
void kvm_vgic_vcpu_early_init(struct kvm_vcpu *vcpu);
|
||||
void kvm_vgic_vcpu_destroy(struct kvm_vcpu *vcpu);
|
||||
int kvm_vgic_map_resources(struct kvm *kvm);
|
||||
int kvm_vgic_hyp_init(void);
|
||||
|
||||
int kvm_vgic_inject_irq(struct kvm *kvm, int cpuid, unsigned int intid,
|
||||
bool level);
|
||||
int kvm_vgic_inject_mapped_irq(struct kvm *kvm, int cpuid, unsigned int intid,
|
||||
bool level);
|
||||
int kvm_vgic_map_phys_irq(struct kvm_vcpu *vcpu, u32 virt_irq, u32 phys_irq);
|
||||
int kvm_vgic_unmap_phys_irq(struct kvm_vcpu *vcpu, unsigned int virt_irq);
|
||||
bool kvm_vgic_map_is_active(struct kvm_vcpu *vcpu, unsigned int virt_irq);
|
||||
|
||||
int kvm_vgic_vcpu_pending_irq(struct kvm_vcpu *vcpu);
|
||||
|
||||
#define irqchip_in_kernel(k) (!!((k)->arch.vgic.in_kernel))
|
||||
#define vgic_initialized(k) ((k)->arch.vgic.initialized)
|
||||
#define vgic_ready(k) ((k)->arch.vgic.ready)
|
||||
#define vgic_valid_spi(k, i) (((i) >= VGIC_NR_PRIVATE_IRQS) && \
|
||||
((i) < (k)->arch.vgic.nr_spis + VGIC_NR_PRIVATE_IRQS))
|
||||
|
||||
bool kvm_vcpu_has_pending_irqs(struct kvm_vcpu *vcpu);
|
||||
void kvm_vgic_sync_hwstate(struct kvm_vcpu *vcpu);
|
||||
void kvm_vgic_flush_hwstate(struct kvm_vcpu *vcpu);
|
||||
|
||||
#ifdef CONFIG_KVM_ARM_VGIC_V3
|
||||
void vgic_v3_dispatch_sgi(struct kvm_vcpu *vcpu, u64 reg);
|
||||
#else
|
||||
static inline void vgic_v3_dispatch_sgi(struct kvm_vcpu *vcpu, u64 reg)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* kvm_vgic_get_max_vcpus - Get the maximum number of VCPUs allowed by HW
|
||||
*
|
||||
* The host's GIC naturally limits the maximum amount of VCPUs a guest
|
||||
* can use.
|
||||
*/
|
||||
static inline int kvm_vgic_get_max_vcpus(void)
|
||||
{
|
||||
return kvm_vgic_global_state.max_gic_vcpus;
|
||||
}
|
||||
|
||||
#endif /* __ASM_ARM_KVM_VGIC_VGIC_H */
|
@ -21,18 +21,11 @@
|
||||
|
||||
#include <asm/kvm_hyp.h>
|
||||
|
||||
#ifdef CONFIG_KVM_NEW_VGIC
|
||||
extern struct vgic_global kvm_vgic_global_state;
|
||||
#define vgic_v2_params kvm_vgic_global_state
|
||||
#else
|
||||
extern struct vgic_params vgic_v2_params;
|
||||
#endif
|
||||
|
||||
static void __hyp_text save_maint_int_state(struct kvm_vcpu *vcpu,
|
||||
void __iomem *base)
|
||||
{
|
||||
struct vgic_v2_cpu_if *cpu_if = &vcpu->arch.vgic_cpu.vgic_v2;
|
||||
int nr_lr = (kern_hyp_va(&vgic_v2_params))->nr_lr;
|
||||
int nr_lr = (kern_hyp_va(&kvm_vgic_global_state))->nr_lr;
|
||||
u32 eisr0, eisr1;
|
||||
int i;
|
||||
bool expect_mi;
|
||||
@ -74,7 +67,7 @@ static void __hyp_text save_maint_int_state(struct kvm_vcpu *vcpu,
|
||||
static void __hyp_text save_elrsr(struct kvm_vcpu *vcpu, void __iomem *base)
|
||||
{
|
||||
struct vgic_v2_cpu_if *cpu_if = &vcpu->arch.vgic_cpu.vgic_v2;
|
||||
int nr_lr = (kern_hyp_va(&vgic_v2_params))->nr_lr;
|
||||
int nr_lr = (kern_hyp_va(&kvm_vgic_global_state))->nr_lr;
|
||||
u32 elrsr0, elrsr1;
|
||||
|
||||
elrsr0 = readl_relaxed(base + GICH_ELRSR0);
|
||||
@ -93,7 +86,7 @@ static void __hyp_text save_elrsr(struct kvm_vcpu *vcpu, void __iomem *base)
|
||||
static void __hyp_text save_lrs(struct kvm_vcpu *vcpu, void __iomem *base)
|
||||
{
|
||||
struct vgic_v2_cpu_if *cpu_if = &vcpu->arch.vgic_cpu.vgic_v2;
|
||||
int nr_lr = (kern_hyp_va(&vgic_v2_params))->nr_lr;
|
||||
int nr_lr = (kern_hyp_va(&kvm_vgic_global_state))->nr_lr;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < nr_lr; i++) {
|
||||
@ -147,7 +140,7 @@ void __hyp_text __vgic_v2_restore_state(struct kvm_vcpu *vcpu)
|
||||
struct vgic_v2_cpu_if *cpu_if = &vcpu->arch.vgic_cpu.vgic_v2;
|
||||
struct vgic_dist *vgic = &kvm->arch.vgic;
|
||||
void __iomem *base = kern_hyp_va(vgic->vctrl_base);
|
||||
int nr_lr = (kern_hyp_va(&vgic_v2_params))->nr_lr;
|
||||
int nr_lr = (kern_hyp_va(&kvm_vgic_global_state))->nr_lr;
|
||||
int i;
|
||||
u64 live_lrs = 0;
|
||||
|
||||
|
@ -1,856 +0,0 @@
|
||||
/*
|
||||
* 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,
|
||||
};
|
@ -1,274 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2012,2013 ARM Limited, All Rights Reserved.
|
||||
* 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/irqchip/arm-gic.h>
|
||||
|
||||
#include <asm/kvm_emulate.h>
|
||||
#include <asm/kvm_arm.h>
|
||||
#include <asm/kvm_mmu.h>
|
||||
|
||||
static struct vgic_lr vgic_v2_get_lr(const struct kvm_vcpu *vcpu, int lr)
|
||||
{
|
||||
struct vgic_lr lr_desc;
|
||||
u32 val = vcpu->arch.vgic_cpu.vgic_v2.vgic_lr[lr];
|
||||
|
||||
lr_desc.irq = val & GICH_LR_VIRTUALID;
|
||||
if (lr_desc.irq <= 15)
|
||||
lr_desc.source = (val >> GICH_LR_PHYSID_CPUID_SHIFT) & 0x7;
|
||||
else
|
||||
lr_desc.source = 0;
|
||||
lr_desc.state = 0;
|
||||
|
||||
if (val & GICH_LR_PENDING_BIT)
|
||||
lr_desc.state |= LR_STATE_PENDING;
|
||||
if (val & GICH_LR_ACTIVE_BIT)
|
||||
lr_desc.state |= LR_STATE_ACTIVE;
|
||||
if (val & GICH_LR_EOI)
|
||||
lr_desc.state |= LR_EOI_INT;
|
||||
if (val & GICH_LR_HW) {
|
||||
lr_desc.state |= LR_HW;
|
||||
lr_desc.hwirq = (val & GICH_LR_PHYSID_CPUID) >> GICH_LR_PHYSID_CPUID_SHIFT;
|
||||
}
|
||||
|
||||
return lr_desc;
|
||||
}
|
||||
|
||||
static void vgic_v2_set_lr(struct kvm_vcpu *vcpu, int lr,
|
||||
struct vgic_lr lr_desc)
|
||||
{
|
||||
u32 lr_val;
|
||||
|
||||
lr_val = lr_desc.irq;
|
||||
|
||||
if (lr_desc.state & LR_STATE_PENDING)
|
||||
lr_val |= GICH_LR_PENDING_BIT;
|
||||
if (lr_desc.state & LR_STATE_ACTIVE)
|
||||
lr_val |= GICH_LR_ACTIVE_BIT;
|
||||
if (lr_desc.state & LR_EOI_INT)
|
||||
lr_val |= GICH_LR_EOI;
|
||||
|
||||
if (lr_desc.state & LR_HW) {
|
||||
lr_val |= GICH_LR_HW;
|
||||
lr_val |= (u32)lr_desc.hwirq << GICH_LR_PHYSID_CPUID_SHIFT;
|
||||
}
|
||||
|
||||
if (lr_desc.irq < VGIC_NR_SGIS)
|
||||
lr_val |= (lr_desc.source << GICH_LR_PHYSID_CPUID_SHIFT);
|
||||
|
||||
vcpu->arch.vgic_cpu.vgic_v2.vgic_lr[lr] = lr_val;
|
||||
|
||||
if (!(lr_desc.state & LR_STATE_MASK))
|
||||
vcpu->arch.vgic_cpu.vgic_v2.vgic_elrsr |= (1ULL << lr);
|
||||
else
|
||||
vcpu->arch.vgic_cpu.vgic_v2.vgic_elrsr &= ~(1ULL << lr);
|
||||
}
|
||||
|
||||
static u64 vgic_v2_get_elrsr(const struct kvm_vcpu *vcpu)
|
||||
{
|
||||
return vcpu->arch.vgic_cpu.vgic_v2.vgic_elrsr;
|
||||
}
|
||||
|
||||
static u64 vgic_v2_get_eisr(const struct kvm_vcpu *vcpu)
|
||||
{
|
||||
return vcpu->arch.vgic_cpu.vgic_v2.vgic_eisr;
|
||||
}
|
||||
|
||||
static void vgic_v2_clear_eisr(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
vcpu->arch.vgic_cpu.vgic_v2.vgic_eisr = 0;
|
||||
}
|
||||
|
||||
static u32 vgic_v2_get_interrupt_status(const struct kvm_vcpu *vcpu)
|
||||
{
|
||||
u32 misr = vcpu->arch.vgic_cpu.vgic_v2.vgic_misr;
|
||||
u32 ret = 0;
|
||||
|
||||
if (misr & GICH_MISR_EOI)
|
||||
ret |= INT_STATUS_EOI;
|
||||
if (misr & GICH_MISR_U)
|
||||
ret |= INT_STATUS_UNDERFLOW;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void vgic_v2_enable_underflow(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
vcpu->arch.vgic_cpu.vgic_v2.vgic_hcr |= GICH_HCR_UIE;
|
||||
}
|
||||
|
||||
static void vgic_v2_disable_underflow(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
vcpu->arch.vgic_cpu.vgic_v2.vgic_hcr &= ~GICH_HCR_UIE;
|
||||
}
|
||||
|
||||
static void vgic_v2_get_vmcr(struct kvm_vcpu *vcpu, struct vgic_vmcr *vmcrp)
|
||||
{
|
||||
u32 vmcr = vcpu->arch.vgic_cpu.vgic_v2.vgic_vmcr;
|
||||
|
||||
vmcrp->ctlr = (vmcr & GICH_VMCR_CTRL_MASK) >> GICH_VMCR_CTRL_SHIFT;
|
||||
vmcrp->abpr = (vmcr & GICH_VMCR_ALIAS_BINPOINT_MASK) >> GICH_VMCR_ALIAS_BINPOINT_SHIFT;
|
||||
vmcrp->bpr = (vmcr & GICH_VMCR_BINPOINT_MASK) >> GICH_VMCR_BINPOINT_SHIFT;
|
||||
vmcrp->pmr = (vmcr & GICH_VMCR_PRIMASK_MASK) >> GICH_VMCR_PRIMASK_SHIFT;
|
||||
}
|
||||
|
||||
static void vgic_v2_set_vmcr(struct kvm_vcpu *vcpu, struct vgic_vmcr *vmcrp)
|
||||
{
|
||||
u32 vmcr;
|
||||
|
||||
vmcr = (vmcrp->ctlr << GICH_VMCR_CTRL_SHIFT) & GICH_VMCR_CTRL_MASK;
|
||||
vmcr |= (vmcrp->abpr << GICH_VMCR_ALIAS_BINPOINT_SHIFT) & GICH_VMCR_ALIAS_BINPOINT_MASK;
|
||||
vmcr |= (vmcrp->bpr << GICH_VMCR_BINPOINT_SHIFT) & GICH_VMCR_BINPOINT_MASK;
|
||||
vmcr |= (vmcrp->pmr << GICH_VMCR_PRIMASK_SHIFT) & GICH_VMCR_PRIMASK_MASK;
|
||||
|
||||
vcpu->arch.vgic_cpu.vgic_v2.vgic_vmcr = vmcr;
|
||||
}
|
||||
|
||||
static void vgic_v2_enable(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
/*
|
||||
* By forcing VMCR to zero, the GIC will restore the binary
|
||||
* points to their reset values. Anything else resets to zero
|
||||
* anyway.
|
||||
*/
|
||||
vcpu->arch.vgic_cpu.vgic_v2.vgic_vmcr = 0;
|
||||
vcpu->arch.vgic_cpu.vgic_v2.vgic_elrsr = ~0;
|
||||
|
||||
/* Get the show on the road... */
|
||||
vcpu->arch.vgic_cpu.vgic_v2.vgic_hcr = GICH_HCR_EN;
|
||||
}
|
||||
|
||||
static const struct vgic_ops vgic_v2_ops = {
|
||||
.get_lr = vgic_v2_get_lr,
|
||||
.set_lr = vgic_v2_set_lr,
|
||||
.get_elrsr = vgic_v2_get_elrsr,
|
||||
.get_eisr = vgic_v2_get_eisr,
|
||||
.clear_eisr = vgic_v2_clear_eisr,
|
||||
.get_interrupt_status = vgic_v2_get_interrupt_status,
|
||||
.enable_underflow = vgic_v2_enable_underflow,
|
||||
.disable_underflow = vgic_v2_disable_underflow,
|
||||
.get_vmcr = vgic_v2_get_vmcr,
|
||||
.set_vmcr = vgic_v2_set_vmcr,
|
||||
.enable = vgic_v2_enable,
|
||||
};
|
||||
|
||||
struct vgic_params __section(.hyp.text) vgic_v2_params;
|
||||
|
||||
static void vgic_cpu_init_lrs(void *params)
|
||||
{
|
||||
struct vgic_params *vgic = params;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < vgic->nr_lr; i++)
|
||||
writel_relaxed(0, vgic->vctrl_base + GICH_LR0 + (i * 4));
|
||||
}
|
||||
|
||||
/**
|
||||
* vgic_v2_probe - probe for a GICv2 compatible interrupt controller
|
||||
* @gic_kvm_info: pointer to the GIC description
|
||||
* @ops: address of a pointer to the GICv2 operations
|
||||
* @params: address of a pointer to HW-specific parameters
|
||||
*
|
||||
* Returns 0 if a GICv2 has been found, with the low level operations
|
||||
* in *ops and the HW parameters in *params. Returns an error code
|
||||
* otherwise.
|
||||
*/
|
||||
int vgic_v2_probe(const struct gic_kvm_info *gic_kvm_info,
|
||||
const struct vgic_ops **ops,
|
||||
const struct vgic_params **params)
|
||||
{
|
||||
int ret;
|
||||
struct vgic_params *vgic = &vgic_v2_params;
|
||||
const struct resource *vctrl_res = &gic_kvm_info->vctrl;
|
||||
const struct resource *vcpu_res = &gic_kvm_info->vcpu;
|
||||
|
||||
memset(vgic, 0, sizeof(*vgic));
|
||||
|
||||
if (!gic_kvm_info->maint_irq) {
|
||||
kvm_err("error getting vgic maintenance irq\n");
|
||||
ret = -ENXIO;
|
||||
goto out;
|
||||
}
|
||||
vgic->maint_irq = gic_kvm_info->maint_irq;
|
||||
|
||||
if (!gic_kvm_info->vctrl.start) {
|
||||
kvm_err("GICH not present in the firmware table\n");
|
||||
ret = -ENXIO;
|
||||
goto out;
|
||||
}
|
||||
|
||||
vgic->vctrl_base = ioremap(gic_kvm_info->vctrl.start,
|
||||
resource_size(&gic_kvm_info->vctrl));
|
||||
if (!vgic->vctrl_base) {
|
||||
kvm_err("Cannot ioremap GICH\n");
|
||||
ret = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
vgic->nr_lr = readl_relaxed(vgic->vctrl_base + GICH_VTR);
|
||||
vgic->nr_lr = (vgic->nr_lr & 0x3f) + 1;
|
||||
|
||||
ret = create_hyp_io_mappings(vgic->vctrl_base,
|
||||
vgic->vctrl_base + resource_size(vctrl_res),
|
||||
vctrl_res->start);
|
||||
if (ret) {
|
||||
kvm_err("Cannot map VCTRL into hyp\n");
|
||||
goto out_unmap;
|
||||
}
|
||||
|
||||
if (!PAGE_ALIGNED(vcpu_res->start)) {
|
||||
kvm_err("GICV physical address 0x%llx not page aligned\n",
|
||||
(unsigned long long)vcpu_res->start);
|
||||
ret = -ENXIO;
|
||||
goto out_unmap;
|
||||
}
|
||||
|
||||
if (!PAGE_ALIGNED(resource_size(vcpu_res))) {
|
||||
kvm_err("GICV size 0x%llx not a multiple of page size 0x%lx\n",
|
||||
(unsigned long long)resource_size(vcpu_res),
|
||||
PAGE_SIZE);
|
||||
ret = -ENXIO;
|
||||
goto out_unmap;
|
||||
}
|
||||
|
||||
vgic->can_emulate_gicv2 = true;
|
||||
kvm_register_device_ops(&kvm_arm_vgic_v2_ops, KVM_DEV_TYPE_ARM_VGIC_V2);
|
||||
|
||||
vgic->vcpu_base = vcpu_res->start;
|
||||
|
||||
kvm_info("GICH base=0x%llx, GICV base=0x%llx, IRQ=%d\n",
|
||||
gic_kvm_info->vctrl.start, vgic->vcpu_base, vgic->maint_irq);
|
||||
|
||||
vgic->type = VGIC_V2;
|
||||
vgic->max_gic_vcpus = VGIC_V2_MAX_CPUS;
|
||||
|
||||
on_each_cpu(vgic_cpu_init_lrs, vgic, 1);
|
||||
|
||||
*ops = &vgic_v2_ops;
|
||||
*params = vgic;
|
||||
goto out;
|
||||
|
||||
out_unmap:
|
||||
iounmap(vgic->vctrl_base);
|
||||
out:
|
||||
return ret;
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -1,279 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2013 ARM Limited, All Rights Reserved.
|
||||
* 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/irqchip/arm-gic-v3.h>
|
||||
#include <linux/irqchip/arm-gic-common.h>
|
||||
|
||||
#include <asm/kvm_emulate.h>
|
||||
#include <asm/kvm_arm.h>
|
||||
#include <asm/kvm_asm.h>
|
||||
#include <asm/kvm_mmu.h>
|
||||
|
||||
static u32 ich_vtr_el2;
|
||||
|
||||
static struct vgic_lr vgic_v3_get_lr(const struct kvm_vcpu *vcpu, int lr)
|
||||
{
|
||||
struct vgic_lr lr_desc;
|
||||
u64 val = vcpu->arch.vgic_cpu.vgic_v3.vgic_lr[lr];
|
||||
|
||||
if (vcpu->kvm->arch.vgic.vgic_model == KVM_DEV_TYPE_ARM_VGIC_V3)
|
||||
lr_desc.irq = val & ICH_LR_VIRTUAL_ID_MASK;
|
||||
else
|
||||
lr_desc.irq = val & GICH_LR_VIRTUALID;
|
||||
|
||||
lr_desc.source = 0;
|
||||
if (lr_desc.irq <= 15 &&
|
||||
vcpu->kvm->arch.vgic.vgic_model == KVM_DEV_TYPE_ARM_VGIC_V2)
|
||||
lr_desc.source = (val >> GICH_LR_PHYSID_CPUID_SHIFT) & 0x7;
|
||||
|
||||
lr_desc.state = 0;
|
||||
|
||||
if (val & ICH_LR_PENDING_BIT)
|
||||
lr_desc.state |= LR_STATE_PENDING;
|
||||
if (val & ICH_LR_ACTIVE_BIT)
|
||||
lr_desc.state |= LR_STATE_ACTIVE;
|
||||
if (val & ICH_LR_EOI)
|
||||
lr_desc.state |= LR_EOI_INT;
|
||||
if (val & ICH_LR_HW) {
|
||||
lr_desc.state |= LR_HW;
|
||||
lr_desc.hwirq = (val >> ICH_LR_PHYS_ID_SHIFT) & GENMASK(9, 0);
|
||||
}
|
||||
|
||||
return lr_desc;
|
||||
}
|
||||
|
||||
static void vgic_v3_set_lr(struct kvm_vcpu *vcpu, int lr,
|
||||
struct vgic_lr lr_desc)
|
||||
{
|
||||
u64 lr_val;
|
||||
|
||||
lr_val = lr_desc.irq;
|
||||
|
||||
/*
|
||||
* Currently all guest IRQs are Group1, as Group0 would result
|
||||
* in a FIQ in the guest, which it wouldn't expect.
|
||||
* Eventually we want to make this configurable, so we may revisit
|
||||
* this in the future.
|
||||
*/
|
||||
switch (vcpu->kvm->arch.vgic.vgic_model) {
|
||||
case KVM_DEV_TYPE_ARM_VGIC_V3:
|
||||
lr_val |= ICH_LR_GROUP;
|
||||
break;
|
||||
case KVM_DEV_TYPE_ARM_VGIC_V2:
|
||||
if (lr_desc.irq < VGIC_NR_SGIS)
|
||||
lr_val |= (u32)lr_desc.source << GICH_LR_PHYSID_CPUID_SHIFT;
|
||||
break;
|
||||
default:
|
||||
BUG();
|
||||
}
|
||||
|
||||
if (lr_desc.state & LR_STATE_PENDING)
|
||||
lr_val |= ICH_LR_PENDING_BIT;
|
||||
if (lr_desc.state & LR_STATE_ACTIVE)
|
||||
lr_val |= ICH_LR_ACTIVE_BIT;
|
||||
if (lr_desc.state & LR_EOI_INT)
|
||||
lr_val |= ICH_LR_EOI;
|
||||
if (lr_desc.state & LR_HW) {
|
||||
lr_val |= ICH_LR_HW;
|
||||
lr_val |= ((u64)lr_desc.hwirq) << ICH_LR_PHYS_ID_SHIFT;
|
||||
}
|
||||
|
||||
vcpu->arch.vgic_cpu.vgic_v3.vgic_lr[lr] = lr_val;
|
||||
|
||||
if (!(lr_desc.state & LR_STATE_MASK))
|
||||
vcpu->arch.vgic_cpu.vgic_v3.vgic_elrsr |= (1U << lr);
|
||||
else
|
||||
vcpu->arch.vgic_cpu.vgic_v3.vgic_elrsr &= ~(1U << lr);
|
||||
}
|
||||
|
||||
static u64 vgic_v3_get_elrsr(const struct kvm_vcpu *vcpu)
|
||||
{
|
||||
return vcpu->arch.vgic_cpu.vgic_v3.vgic_elrsr;
|
||||
}
|
||||
|
||||
static u64 vgic_v3_get_eisr(const struct kvm_vcpu *vcpu)
|
||||
{
|
||||
return vcpu->arch.vgic_cpu.vgic_v3.vgic_eisr;
|
||||
}
|
||||
|
||||
static void vgic_v3_clear_eisr(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
vcpu->arch.vgic_cpu.vgic_v3.vgic_eisr = 0;
|
||||
}
|
||||
|
||||
static u32 vgic_v3_get_interrupt_status(const struct kvm_vcpu *vcpu)
|
||||
{
|
||||
u32 misr = vcpu->arch.vgic_cpu.vgic_v3.vgic_misr;
|
||||
u32 ret = 0;
|
||||
|
||||
if (misr & ICH_MISR_EOI)
|
||||
ret |= INT_STATUS_EOI;
|
||||
if (misr & ICH_MISR_U)
|
||||
ret |= INT_STATUS_UNDERFLOW;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void vgic_v3_get_vmcr(struct kvm_vcpu *vcpu, struct vgic_vmcr *vmcrp)
|
||||
{
|
||||
u32 vmcr = vcpu->arch.vgic_cpu.vgic_v3.vgic_vmcr;
|
||||
|
||||
vmcrp->ctlr = (vmcr & ICH_VMCR_CTLR_MASK) >> ICH_VMCR_CTLR_SHIFT;
|
||||
vmcrp->abpr = (vmcr & ICH_VMCR_BPR1_MASK) >> ICH_VMCR_BPR1_SHIFT;
|
||||
vmcrp->bpr = (vmcr & ICH_VMCR_BPR0_MASK) >> ICH_VMCR_BPR0_SHIFT;
|
||||
vmcrp->pmr = (vmcr & ICH_VMCR_PMR_MASK) >> ICH_VMCR_PMR_SHIFT;
|
||||
}
|
||||
|
||||
static void vgic_v3_enable_underflow(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
vcpu->arch.vgic_cpu.vgic_v3.vgic_hcr |= ICH_HCR_UIE;
|
||||
}
|
||||
|
||||
static void vgic_v3_disable_underflow(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
vcpu->arch.vgic_cpu.vgic_v3.vgic_hcr &= ~ICH_HCR_UIE;
|
||||
}
|
||||
|
||||
static void vgic_v3_set_vmcr(struct kvm_vcpu *vcpu, struct vgic_vmcr *vmcrp)
|
||||
{
|
||||
u32 vmcr;
|
||||
|
||||
vmcr = (vmcrp->ctlr << ICH_VMCR_CTLR_SHIFT) & ICH_VMCR_CTLR_MASK;
|
||||
vmcr |= (vmcrp->abpr << ICH_VMCR_BPR1_SHIFT) & ICH_VMCR_BPR1_MASK;
|
||||
vmcr |= (vmcrp->bpr << ICH_VMCR_BPR0_SHIFT) & ICH_VMCR_BPR0_MASK;
|
||||
vmcr |= (vmcrp->pmr << ICH_VMCR_PMR_SHIFT) & ICH_VMCR_PMR_MASK;
|
||||
|
||||
vcpu->arch.vgic_cpu.vgic_v3.vgic_vmcr = vmcr;
|
||||
}
|
||||
|
||||
static void vgic_v3_enable(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
struct vgic_v3_cpu_if *vgic_v3 = &vcpu->arch.vgic_cpu.vgic_v3;
|
||||
|
||||
/*
|
||||
* By forcing VMCR to zero, the GIC will restore the binary
|
||||
* points to their reset values. Anything else resets to zero
|
||||
* anyway.
|
||||
*/
|
||||
vgic_v3->vgic_vmcr = 0;
|
||||
vgic_v3->vgic_elrsr = ~0;
|
||||
|
||||
/*
|
||||
* If we are emulating a GICv3, we do it in an non-GICv2-compatible
|
||||
* way, so we force SRE to 1 to demonstrate this to the guest.
|
||||
* This goes with the spec allowing the value to be RAO/WI.
|
||||
*/
|
||||
if (vcpu->kvm->arch.vgic.vgic_model == KVM_DEV_TYPE_ARM_VGIC_V3)
|
||||
vgic_v3->vgic_sre = ICC_SRE_EL1_SRE;
|
||||
else
|
||||
vgic_v3->vgic_sre = 0;
|
||||
|
||||
/* Get the show on the road... */
|
||||
vgic_v3->vgic_hcr = ICH_HCR_EN;
|
||||
}
|
||||
|
||||
static const struct vgic_ops vgic_v3_ops = {
|
||||
.get_lr = vgic_v3_get_lr,
|
||||
.set_lr = vgic_v3_set_lr,
|
||||
.get_elrsr = vgic_v3_get_elrsr,
|
||||
.get_eisr = vgic_v3_get_eisr,
|
||||
.clear_eisr = vgic_v3_clear_eisr,
|
||||
.get_interrupt_status = vgic_v3_get_interrupt_status,
|
||||
.enable_underflow = vgic_v3_enable_underflow,
|
||||
.disable_underflow = vgic_v3_disable_underflow,
|
||||
.get_vmcr = vgic_v3_get_vmcr,
|
||||
.set_vmcr = vgic_v3_set_vmcr,
|
||||
.enable = vgic_v3_enable,
|
||||
};
|
||||
|
||||
static struct vgic_params vgic_v3_params;
|
||||
|
||||
static void vgic_cpu_init_lrs(void *params)
|
||||
{
|
||||
kvm_call_hyp(__vgic_v3_init_lrs);
|
||||
}
|
||||
|
||||
/**
|
||||
* vgic_v3_probe - probe for a GICv3 compatible interrupt controller
|
||||
* @gic_kvm_info: pointer to the GIC description
|
||||
* @ops: address of a pointer to the GICv3 operations
|
||||
* @params: address of a pointer to HW-specific parameters
|
||||
*
|
||||
* Returns 0 if a GICv3 has been found, with the low level operations
|
||||
* in *ops and the HW parameters in *params. Returns an error code
|
||||
* otherwise.
|
||||
*/
|
||||
int vgic_v3_probe(const struct gic_kvm_info *gic_kvm_info,
|
||||
const struct vgic_ops **ops,
|
||||
const struct vgic_params **params)
|
||||
{
|
||||
int ret = 0;
|
||||
struct vgic_params *vgic = &vgic_v3_params;
|
||||
const struct resource *vcpu_res = &gic_kvm_info->vcpu;
|
||||
|
||||
vgic->maint_irq = gic_kvm_info->maint_irq;
|
||||
|
||||
ich_vtr_el2 = kvm_call_hyp(__vgic_v3_get_ich_vtr_el2);
|
||||
|
||||
/*
|
||||
* The ListRegs field is 5 bits, but there is a architectural
|
||||
* maximum of 16 list registers. Just ignore bit 4...
|
||||
*/
|
||||
vgic->nr_lr = (ich_vtr_el2 & 0xf) + 1;
|
||||
vgic->can_emulate_gicv2 = false;
|
||||
|
||||
if (!vcpu_res->start) {
|
||||
kvm_info("GICv3: no GICV resource entry\n");
|
||||
vgic->vcpu_base = 0;
|
||||
} else if (!PAGE_ALIGNED(vcpu_res->start)) {
|
||||
pr_warn("GICV physical address 0x%llx not page aligned\n",
|
||||
(unsigned long long)vcpu_res->start);
|
||||
vgic->vcpu_base = 0;
|
||||
} else if (!PAGE_ALIGNED(resource_size(vcpu_res))) {
|
||||
pr_warn("GICV size 0x%llx not a multiple of page size 0x%lx\n",
|
||||
(unsigned long long)resource_size(vcpu_res),
|
||||
PAGE_SIZE);
|
||||
} else {
|
||||
vgic->vcpu_base = vcpu_res->start;
|
||||
vgic->can_emulate_gicv2 = true;
|
||||
kvm_register_device_ops(&kvm_arm_vgic_v2_ops,
|
||||
KVM_DEV_TYPE_ARM_VGIC_V2);
|
||||
}
|
||||
if (vgic->vcpu_base == 0)
|
||||
kvm_info("disabling GICv2 emulation\n");
|
||||
kvm_register_device_ops(&kvm_arm_vgic_v3_ops, KVM_DEV_TYPE_ARM_VGIC_V3);
|
||||
|
||||
vgic->vctrl_base = NULL;
|
||||
vgic->type = VGIC_V3;
|
||||
vgic->max_gic_vcpus = VGIC_V3_MAX_CPUS;
|
||||
|
||||
kvm_info("GICV base=0x%llx, IRQ=%d\n",
|
||||
vgic->vcpu_base, vgic->maint_irq);
|
||||
|
||||
on_each_cpu(vgic_cpu_init_lrs, vgic, 1);
|
||||
|
||||
*ops = &vgic_v3_ops;
|
||||
*params = vgic;
|
||||
|
||||
return ret;
|
||||
}
|
2440
virt/kvm/arm/vgic.c
2440
virt/kvm/arm/vgic.c
File diff suppressed because it is too large
Load Diff
@ -1,140 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2012-2014 ARM Ltd.
|
||||
* Author: Marc Zyngier <marc.zyngier@arm.com>
|
||||
*
|
||||
* Derived from virt/kvm/arm/vgic.c
|
||||
*
|
||||
* 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/>.
|
||||
*/
|
||||
|
||||
#ifndef __KVM_VGIC_H__
|
||||
#define __KVM_VGIC_H__
|
||||
|
||||
#include <kvm/iodev.h>
|
||||
|
||||
#define VGIC_ADDR_UNDEF (-1)
|
||||
#define IS_VGIC_ADDR_UNDEF(_x) ((_x) == VGIC_ADDR_UNDEF)
|
||||
|
||||
#define PRODUCT_ID_KVM 0x4b /* ASCII code K */
|
||||
#define IMPLEMENTER_ARM 0x43b
|
||||
|
||||
#define ACCESS_READ_VALUE (1 << 0)
|
||||
#define ACCESS_READ_RAZ (0 << 0)
|
||||
#define ACCESS_READ_MASK(x) ((x) & (1 << 0))
|
||||
#define ACCESS_WRITE_IGNORED (0 << 1)
|
||||
#define ACCESS_WRITE_SETBIT (1 << 1)
|
||||
#define ACCESS_WRITE_CLEARBIT (2 << 1)
|
||||
#define ACCESS_WRITE_VALUE (3 << 1)
|
||||
#define ACCESS_WRITE_MASK(x) ((x) & (3 << 1))
|
||||
|
||||
#define VCPU_NOT_ALLOCATED ((u8)-1)
|
||||
|
||||
unsigned long *vgic_bitmap_get_shared_map(struct vgic_bitmap *x);
|
||||
|
||||
void vgic_update_state(struct kvm *kvm);
|
||||
int vgic_init_common_maps(struct kvm *kvm);
|
||||
|
||||
u32 *vgic_bitmap_get_reg(struct vgic_bitmap *x, int cpuid, u32 offset);
|
||||
u32 *vgic_bytemap_get_reg(struct vgic_bytemap *x, int cpuid, u32 offset);
|
||||
|
||||
void vgic_dist_irq_set_pending(struct kvm_vcpu *vcpu, int irq);
|
||||
void vgic_dist_irq_clear_pending(struct kvm_vcpu *vcpu, int irq);
|
||||
void vgic_cpu_irq_clear(struct kvm_vcpu *vcpu, int irq);
|
||||
void vgic_bitmap_set_irq_val(struct vgic_bitmap *x, int cpuid,
|
||||
int irq, int val);
|
||||
|
||||
void vgic_get_vmcr(struct kvm_vcpu *vcpu, struct vgic_vmcr *vmcr);
|
||||
void vgic_set_vmcr(struct kvm_vcpu *vcpu, struct vgic_vmcr *vmcr);
|
||||
|
||||
bool vgic_queue_irq(struct kvm_vcpu *vcpu, u8 sgi_source_id, int irq);
|
||||
void vgic_unqueue_irqs(struct kvm_vcpu *vcpu);
|
||||
|
||||
struct kvm_exit_mmio {
|
||||
phys_addr_t phys_addr;
|
||||
void *data;
|
||||
u32 len;
|
||||
bool is_write;
|
||||
void *private;
|
||||
};
|
||||
|
||||
void vgic_reg_access(struct kvm_exit_mmio *mmio, u32 *reg,
|
||||
phys_addr_t offset, int mode);
|
||||
bool handle_mmio_raz_wi(struct kvm_vcpu *vcpu, struct kvm_exit_mmio *mmio,
|
||||
phys_addr_t offset);
|
||||
|
||||
static inline
|
||||
u32 mmio_data_read(struct kvm_exit_mmio *mmio, u32 mask)
|
||||
{
|
||||
return le32_to_cpu(*((u32 *)mmio->data)) & mask;
|
||||
}
|
||||
|
||||
static inline
|
||||
void mmio_data_write(struct kvm_exit_mmio *mmio, u32 mask, u32 value)
|
||||
{
|
||||
*((u32 *)mmio->data) = cpu_to_le32(value) & mask;
|
||||
}
|
||||
|
||||
struct vgic_io_range {
|
||||
phys_addr_t base;
|
||||
unsigned long len;
|
||||
int bits_per_irq;
|
||||
bool (*handle_mmio)(struct kvm_vcpu *vcpu, struct kvm_exit_mmio *mmio,
|
||||
phys_addr_t offset);
|
||||
};
|
||||
|
||||
int vgic_register_kvm_io_dev(struct kvm *kvm, gpa_t base, int len,
|
||||
const struct vgic_io_range *ranges,
|
||||
int redist_id,
|
||||
struct vgic_io_device *iodev);
|
||||
|
||||
static inline bool is_in_range(phys_addr_t addr, unsigned long len,
|
||||
phys_addr_t baseaddr, unsigned long size)
|
||||
{
|
||||
return (addr >= baseaddr) && (addr + len <= baseaddr + size);
|
||||
}
|
||||
|
||||
const
|
||||
struct vgic_io_range *vgic_find_range(const struct vgic_io_range *ranges,
|
||||
int len, gpa_t offset);
|
||||
|
||||
bool vgic_handle_enable_reg(struct kvm *kvm, struct kvm_exit_mmio *mmio,
|
||||
phys_addr_t offset, int vcpu_id, int access);
|
||||
|
||||
bool vgic_handle_set_pending_reg(struct kvm *kvm, struct kvm_exit_mmio *mmio,
|
||||
phys_addr_t offset, int vcpu_id);
|
||||
|
||||
bool vgic_handle_clear_pending_reg(struct kvm *kvm, struct kvm_exit_mmio *mmio,
|
||||
phys_addr_t offset, int vcpu_id);
|
||||
|
||||
bool vgic_handle_set_active_reg(struct kvm *kvm,
|
||||
struct kvm_exit_mmio *mmio,
|
||||
phys_addr_t offset, int vcpu_id);
|
||||
|
||||
bool vgic_handle_clear_active_reg(struct kvm *kvm,
|
||||
struct kvm_exit_mmio *mmio,
|
||||
phys_addr_t offset, int vcpu_id);
|
||||
|
||||
bool vgic_handle_cfg_reg(u32 *reg, struct kvm_exit_mmio *mmio,
|
||||
phys_addr_t offset);
|
||||
|
||||
void vgic_kick_vcpus(struct kvm *kvm);
|
||||
|
||||
int vgic_has_attr_regs(const struct vgic_io_range *ranges, phys_addr_t offset);
|
||||
int vgic_set_common_attr(struct kvm_device *dev, struct kvm_device_attr *attr);
|
||||
int vgic_get_common_attr(struct kvm_device *dev, struct kvm_device_attr *attr);
|
||||
|
||||
int vgic_init(struct kvm *kvm);
|
||||
void vgic_v2_init_emulation(struct kvm *kvm);
|
||||
void vgic_v3_init_emulation(struct kvm *kvm);
|
||||
|
||||
#endif
|
Loading…
Reference in New Issue
Block a user