[PATCH v3 19/32] arm64: KVM: Plug the VGIC
Christoffer Dall
cdall at cs.columbia.edu
Tue Apr 23 19:00:12 EDT 2013
On Mon, Apr 08, 2013 at 05:17:21PM +0100, Marc Zyngier wrote:
> Add support for the in-kernel GIC emulation. The include file
> is a complete duplicate of the 32bit one - something to fix
> at one point.
seems like something that should be fixed sooner as opposed to later...
Is it hard?
>
> Reviewed-by: Christopher Covington <cov at codeaurora.org>
> Signed-off-by: Marc Zyngier <marc.zyngier at arm.com>
> ---
> arch/arm64/include/asm/kvm_vgic.h | 156 ++++++++++++++++++++++++++++++++++++++
> arch/arm64/kvm/hyp.S | 88 +++++++++++++++++++++
> 2 files changed, 244 insertions(+)
> create mode 100644 arch/arm64/include/asm/kvm_vgic.h
>
> diff --git a/arch/arm64/include/asm/kvm_vgic.h b/arch/arm64/include/asm/kvm_vgic.h
> new file mode 100644
> index 0000000..f353f22
> --- /dev/null
> +++ b/arch/arm64/include/asm/kvm_vgic.h
> @@ -0,0 +1,156 @@
> +/*
> + * Copyright (C) 2012 ARM Ltd.
> + * Author: Marc Zyngier <marc.zyngier at 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/>.
> + */
> +
> +#ifndef __ARM64_KVM_VGIC_H
> +#define __ARM64_KVM_VGIC_H
> +
> +#include <linux/kernel.h>
> +#include <linux/kvm.h>
> +#include <linux/irqreturn.h>
> +#include <linux/spinlock.h>
> +#include <linux/types.h>
> +#include <linux/irqchip/arm-gic.h>
> +
> +#define VGIC_NR_IRQS 128
> +#define VGIC_NR_SGIS 16
> +#define VGIC_NR_PPIS 16
> +#define VGIC_NR_PRIVATE_IRQS (VGIC_NR_SGIS + VGIC_NR_PPIS)
> +#define VGIC_NR_SHARED_IRQS (VGIC_NR_IRQS - VGIC_NR_PRIVATE_IRQS)
> +#define VGIC_MAX_CPUS KVM_MAX_VCPUS
> +
> +/* Sanity checks... */
> +#if (VGIC_MAX_CPUS > 8)
> +#error Invalid number of CPU interfaces
> +#endif
> +
> +#if (VGIC_NR_IRQS & 31)
> +#error "VGIC_NR_IRQS must be a multiple of 32"
> +#endif
> +
> +#if (VGIC_NR_IRQS > 1024)
> +#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 {
> + union {
> + u32 reg[VGIC_NR_PRIVATE_IRQS / 32];
> + DECLARE_BITMAP(reg_ul, VGIC_NR_PRIVATE_IRQS);
> + } percpu[VGIC_MAX_CPUS];
> + union {
> + u32 reg[VGIC_NR_SHARED_IRQS / 32];
> + DECLARE_BITMAP(reg_ul, VGIC_NR_SHARED_IRQS);
> + } shared;
> +};
> +
> +struct vgic_bytemap {
> + u32 percpu[VGIC_MAX_CPUS][VGIC_NR_PRIVATE_IRQS / 4];
> + u32 shared[VGIC_NR_SHARED_IRQS / 4];
> +};
> +
> +struct vgic_dist {
> + spinlock_t lock;
> + bool ready;
> +
> + /* Virtual control interface mapping */
> + void __iomem *vctrl_base;
> +
> + /* Distributor and vcpu interface mapping in the guest */
> + phys_addr_t vgic_dist_base;
> + phys_addr_t vgic_cpu_base;
> +
> + /* Distributor enabled */
> + u32 enabled;
> +
> + /* Interrupt enabled (one bit per IRQ) */
> + struct vgic_bitmap irq_enabled;
> +
> + /* Interrupt 'pin' level */
> + struct vgic_bitmap irq_state;
> +
> + /* Level-triggered interrupt in progress */
> + 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 */
> + u8 irq_sgi_sources[VGIC_MAX_CPUS][16];
> +
> + /* Target CPU for each IRQ */
> + u8 irq_spi_cpu[VGIC_NR_SHARED_IRQS];
> + struct vgic_bitmap irq_spi_target[VGIC_MAX_CPUS];
> +
> + /* Bitmap indicating which CPU has something pending */
> + unsigned long irq_pending_on_cpu;
> +};
> +
> +struct vgic_cpu {
> + /* per IRQ to LR mapping */
> + u8 vgic_irq_lr_map[VGIC_NR_IRQS];
> +
> + /* Pending interrupts on this VCPU */
> + DECLARE_BITMAP( pending_percpu, VGIC_NR_PRIVATE_IRQS);
> + DECLARE_BITMAP( pending_shared, VGIC_NR_SHARED_IRQS);
> +
> + /* Bitmap of used/free list registers */
> + DECLARE_BITMAP( lr_used, 64);
> +
> + /* Number of list registers on this CPU */
> + int nr_lr;
> +
> + /* CPU vif control registers for world switch */
> + u32 vgic_hcr;
> + u32 vgic_vmcr;
> + u32 vgic_misr; /* Saved only */
> + u32 vgic_eisr[2]; /* Saved only */
> + u32 vgic_elrsr[2]; /* Saved only */
> + u32 vgic_apr;
> + u32 vgic_lr[64]; /* A15 has only 4... */
> +};
> +
> +#define LR_EMPTY 0xff
> +
> +struct kvm;
> +struct kvm_vcpu;
> +struct kvm_run;
> +struct kvm_exit_mmio;
> +
> +int kvm_vgic_set_addr(struct kvm *kvm, unsigned long type, u64 addr);
> +int kvm_vgic_hyp_init(void);
> +int kvm_vgic_init(struct kvm *kvm);
> +int kvm_vgic_create(struct kvm *kvm);
> +int kvm_vgic_vcpu_init(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,
> + bool level);
> +int kvm_vgic_vcpu_pending_irq(struct kvm_vcpu *vcpu);
> +bool vgic_handle_mmio(struct kvm_vcpu *vcpu, struct kvm_run *run,
> + struct kvm_exit_mmio *mmio);
> +
> +#define irqchip_in_kernel(k) (!!((k)->arch.vgic.vctrl_base))
> +#define vgic_initialized(k) ((k)->arch.vgic.ready)
> +
> +#endif
> diff --git a/arch/arm64/kvm/hyp.S b/arch/arm64/kvm/hyp.S
> index c745d20..cc3192e 100644
> --- a/arch/arm64/kvm/hyp.S
> +++ b/arch/arm64/kvm/hyp.S
> @@ -306,6 +306,90 @@ __kvm_hyp_code_start:
> msr vttbr_el2, xzr
> .endm
>
> +/*
> + * Save the VGIC CPU state into memory
> + * x0: Register pointing to VCPU struct
> + * Do not corrupt x1!!!
> + */
> +.macro save_vgic_state
> + /* Get VGIC VCTRL base into x2 */
> + ldr x2, [x0, #VCPU_KVM]
> + kern_hyp_va x2
> + ldr x2, [x2, #KVM_VGIC_VCTRL]
> + kern_hyp_va x2
> + cbz x2, 2f // disabled
> +
> + /* Compute the address of struct vgic_cpu */
> + add x3, x0, #VCPU_VGIC_CPU
> +
> + /* Save all interesting registers */
> + ldr w4, [x2, #GICH_HCR]
> + ldr w5, [x2, #GICH_VMCR]
> + ldr w6, [x2, #GICH_MISR]
> + ldr w7, [x2, #GICH_EISR0]
> + ldr w8, [x2, #GICH_EISR1]
> + ldr w9, [x2, #GICH_ELRSR0]
> + ldr w10, [x2, #GICH_ELRSR1]
> + ldr w11, [x2, #GICH_APR]
> +
> + str w4, [x3, #VGIC_CPU_HCR]
> + str w5, [x3, #VGIC_CPU_VMCR]
> + str w6, [x3, #VGIC_CPU_MISR]
> + str w7, [x3, #VGIC_CPU_EISR]
> + str w8, [x3, #(VGIC_CPU_EISR + 4)]
> + str w9, [x3, #VGIC_CPU_ELRSR]
> + str w10, [x3, #(VGIC_CPU_ELRSR + 4)]
> + str w11, [x3, #VGIC_CPU_APR]
> +
> + /* Clear GICH_HCR */
> + str wzr, [x2, #GICH_HCR]
> +
> + /* Save list registers */
> + add x2, x2, #GICH_LR0
> + ldr w4, [x3, #VGIC_CPU_NR_LR]
> + add x3, x3, #VGIC_CPU_LR
> +1: ldr w5, [x2], #4
> + str w5, [x3], #4
> + sub w4, w4, #1
> + cbnz w4, 1b
> +2:
> +.endm
> +
> +/*
> + * Restore the VGIC CPU state from memory
> + * x0: Register pointing to VCPU struct
> + */
> +.macro restore_vgic_state
> + /* Get VGIC VCTRL base into x2 */
> + ldr x2, [x0, #VCPU_KVM]
> + kern_hyp_va x2
> + ldr x2, [x2, #KVM_VGIC_VCTRL]
> + kern_hyp_va x2
> + cbz x2, 2f // disabled
> +
> + /* Compute the address of struct vgic_cpu */
> + add x3, x0, #VCPU_VGIC_CPU
> +
> + /* We only restore a minimal set of registers */
> + ldr w4, [x3, #VGIC_CPU_HCR]
> + ldr w5, [x3, #VGIC_CPU_VMCR]
> + ldr w6, [x3, #VGIC_CPU_APR]
> +
> + str w4, [x2, #GICH_HCR]
> + str w5, [x2, #GICH_VMCR]
> + str w6, [x2, #GICH_APR]
> +
> + /* Restore list registers */
> + add x2, x2, #GICH_LR0
> + ldr w4, [x3, #VGIC_CPU_NR_LR]
> + add x3, x3, #VGIC_CPU_LR
> +1: ldr w5, [x3], #4
> + str w5, [x2], #4
> + sub w4, w4, #1
> + cbnz w4, 1b
> +2:
> +.endm
> +
> __save_sysregs:
> save_sysregs
> ret
> @@ -348,6 +432,8 @@ ENTRY(__kvm_vcpu_run)
> activate_traps
> activate_vm
>
> + restore_vgic_state
> +
> // Guest context
> add x2, x0, #VCPU_CONTEXT
>
> @@ -370,6 +456,8 @@ __kvm_vcpu_return:
> bl __save_fpsimd
> bl __save_sysregs
>
> + save_vgic_state
> +
> deactivate_traps
> deactivate_vm
>
> --
> 1.8.1.4
>
>
>
> _______________________________________________
> kvmarm mailing list
> kvmarm at lists.cs.columbia.edu
> https://lists.cs.columbia.edu/cucslists/listinfo/kvmarm
More information about the linux-arm-kernel
mailing list