Wire the basic framework code for VGIC support. Nothing to enable yet. Signed-off-by: Marc Zyngier <marc.zyngier at arm.com> --- arch/arm/include/asm/kvm_host.h | 6 +++++ arch/arm/include/asm/kvm_vgic.h | 45 +++++++++++++++++++++++++++++++++++++++ arch/arm/kvm/arm.c | 17 ++++++++++++++ arch/arm/kvm/emulate.c | 2 +- arch/arm/kvm/interrupts.S | 18 +++++++++++++++ arch/arm/kvm/mmu.c | 3 +- virt/kvm/kvm_main.c | 5 ++- 7 files changed, 92 insertions(+), 4 deletions(-) create mode 100644 arch/arm/include/asm/kvm_vgic.h diff --git a/arch/arm/include/asm/kvm_host.h b/arch/arm/include/asm/kvm_host.h index 44abdc8..4b55fae 100644 --- a/arch/arm/include/asm/kvm_host.h +++ b/arch/arm/include/asm/kvm_host.h @@ -22,6 +22,8 @@ #define KVM_PRIVATE_MEM_SLOTS 4 #define KVM_COALESCED_MMIO_PAGE_OFFSET 1 +#include <asm/kvm_vgic.h> + /* We don't currently support large pages. */ #define KVM_HPAGE_GFN_SHIFT(x) 0 #define KVM_NR_PAGE_SIZES 1 @@ -40,6 +42,9 @@ struct kvm_arch { /* VTTBR value associated with above pgd and vmid */ u64 vttbr; + + /* Interrupt controller */ + struct vgic_dist vgic; }; #define EXCEPTION_NONE 0 @@ -105,6 +110,7 @@ struct kvm_vcpu_arch { /* Interrupt related fields */ u32 irq_lines; /* IRQ and FIQ levels */ u32 wait_for_interrupts; + struct vgic_cpu vgic_cpu; }; struct kvm_vm_stat { diff --git a/arch/arm/include/asm/kvm_vgic.h b/arch/arm/include/asm/kvm_vgic.h new file mode 100644 index 0000000..222812e --- /dev/null +++ b/arch/arm/include/asm/kvm_vgic.h @@ -0,0 +1,45 @@ +#ifndef __ASM_ARM_KVM_VGIC_H +#define __ASM_ARM_KVM_VGIC_H + +struct vgic_dist { +}; + +struct vgic_cpu { +}; + +struct kvm; +struct kvm_vcpu; +struct kvm_run; + +#ifndef CONFIG_KVM_ARM_VGIC +static inline int kvm_vgic_hyp_init(void) +{ + return 0; +} + +static inline int kvm_vgic_init(struct kvm *kvm) +{ + return 0; +} + +static inline void kvm_vgic_vcpu_init(struct kvm_vcpu *vcpu) {} +static inline void kvm_vgic_sync_to_cpu(struct kvm_vcpu *vcpu) {} +static inline void kvm_vgic_sync_from_cpu(struct kvm_vcpu *vcpu) {} + +static inline int kvm_vgic_vcpu_pending_irq(struct kvm_vcpu *vcpu) +{ + return 0; +} + +static inline int vgic_handle_mmio(struct kvm_vcpu *vcpu, struct kvm_run *run) +{ + return KVM_EXIT_MMIO; +} + +static inline int irqchip_in_kernel(struct kvm *kvm) +{ + return 0; +} +#endif + +#endif diff --git a/arch/arm/kvm/arm.c b/arch/arm/kvm/arm.c index a73c613..a573f16 100644 --- a/arch/arm/kvm/arm.c +++ b/arch/arm/kvm/arm.c @@ -147,6 +147,9 @@ int kvm_dev_ioctl_check_extension(long ext) { int r; switch (ext) { +#ifdef CONFIG_KVM_ARM_VGIC + case KVM_CAP_IRQCHIP: +#endif case KVM_CAP_USER_MEMORY: case KVM_CAP_DESTROY_MEMORY_REGION_WORKS: r = 1; @@ -256,6 +259,10 @@ int kvm_arch_vcpu_init(struct kvm_vcpu *vcpu) /* Compute guest MPIDR */ vcpu->arch.cp15[c0_MPIDR] = (read_cpuid_mpidr() & ~0xff) | vcpu->vcpu_id; + + /* Set up VGIC */ + kvm_vgic_vcpu_init(vcpu); + return 0; } @@ -301,6 +308,7 @@ int kvm_arch_vcpu_ioctl_set_mpstate(struct kvm_vcpu *vcpu, int kvm_arch_vcpu_runnable(struct kvm_vcpu *v) { return !!v->arch.irq_lines || + kvm_vgic_vcpu_pending_irq(v) || !v->arch.wait_for_interrupts; } @@ -500,9 +508,11 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu, struct kvm_run *run) local_irq_disable(); kvm_guest_enter(); vcpu->mode = IN_GUEST_MODE; + kvm_vgic_sync_to_cpu(vcpu); ret = __kvm_vcpu_run(vcpu); + kvm_vgic_sync_from_cpu(vcpu); vcpu->mode = OUTSIDE_GUEST_MODE; kvm_guest_exit(); local_irq_enable(); @@ -729,6 +739,13 @@ static int init_hyp_mode(void) } /* + * Init HYP view of VGIC + */ + err = kvm_vgic_hyp_init(); + if (err) + goto out_free_mappings; + + /* * Set the HVBAR to the virtual kernel address */ for_each_online_cpu(cpu) diff --git a/arch/arm/kvm/emulate.c b/arch/arm/kvm/emulate.c index 6eaa97d..d810c30 100644 --- a/arch/arm/kvm/emulate.c +++ b/arch/arm/kvm/emulate.c @@ -443,7 +443,7 @@ int kvm_handle_cp15_32(struct kvm_vcpu *vcpu, struct kvm_run *run) int kvm_handle_wfi(struct kvm_vcpu *vcpu, struct kvm_run *run) { trace_kvm_wfi(vcpu->arch.regs.pc); - if (!vcpu->arch.irq_lines) + if (!vcpu->arch.irq_lines && !kvm_vgic_vcpu_pending_irq(vcpu)) vcpu->arch.wait_for_interrupts = 1; return 0; } diff --git a/arch/arm/kvm/interrupts.S b/arch/arm/kvm/interrupts.S index 2c32d22..2e19b34 100644 --- a/arch/arm/kvm/interrupts.S +++ b/arch/arm/kvm/interrupts.S @@ -236,6 +236,20 @@ ENTRY(__kvm_flush_vm_context) mcr p15, 0, r11, c10, c2, 1 @ NMRR .endm +/* + * Save the VGIC CPU state into memory + * @vcpup: Register pointing to VCPU struct + */ +.macro save_vgic_state vcpup +.endm + +/* + * Restore the VGIC CPU state from memory + * @vcpup: Register pointing to VCPU struct + */ +.macro restore_vgic_state vcpup +.endm + /* Configures the HSTR (Hyp System Trap Register) on entry/return * (hardware reset value is 0) */ .macro set_hstr entry @@ -280,6 +294,8 @@ ENTRY(__kvm_vcpu_run) store_mode_state sp, irq store_mode_state sp, fiq + restore_vgic_state r0 + @ Store hardware CP15 state and load guest state read_cp15_state write_cp15_state 1, r0 @@ -365,6 +381,8 @@ __kvm_vcpu_return: read_cp15_state 1, r1 write_cp15_state + save_vgic_state r1 + load_mode_state sp, fiq load_mode_state sp, irq load_mode_state sp, und diff --git a/arch/arm/kvm/mmu.c b/arch/arm/kvm/mmu.c index 8d8530f..2d4fc68 100644 --- a/arch/arm/kvm/mmu.c +++ b/arch/arm/kvm/mmu.c @@ -590,7 +590,6 @@ static int io_mem_abort(struct kvm_vcpu *vcpu, struct kvm_run *run, instr_len = ((vcpu->arch.hsr >> 25) & 1) ? 4 : 2; /* Export MMIO operations to user space */ - run->exit_reason = KVM_EXIT_MMIO; run->mmio.is_write = is_write; run->mmio.phys_addr = fault_ipa; run->mmio.len = len; @@ -604,6 +603,8 @@ static int io_mem_abort(struct kvm_vcpu *vcpu, struct kvm_run *run, if (is_write) memcpy(run->mmio.data, vcpu_reg(vcpu, rd), len); + run->exit_reason = vgic_handle_mmio(vcpu, run); + /* * The MMIO instruction is emulated and should not be re-executed * in the guest. diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c index 0837346..ecfe649 100644 --- a/virt/kvm/kvm_main.c +++ b/virt/kvm/kvm_main.c @@ -1744,12 +1744,13 @@ static long kvm_vcpu_ioctl(struct file *filp, if (vcpu->kvm->mm != current->mm) return -EIO; -#if defined(CONFIG_S390) || defined(CONFIG_PPC) +#if defined(CONFIG_S390) || defined(CONFIG_PPC) || defined(CONFIG_ARM) /* * Special cases: vcpu ioctls that are asynchronous to vcpu execution, * so vcpu_load() would break it. */ - if (ioctl == KVM_S390_INTERRUPT || ioctl == KVM_INTERRUPT) + if (ioctl == KVM_S390_INTERRUPT || ioctl == KVM_INTERRUPT || + ioctl == KVM_IRQ_LINE) return kvm_arch_vcpu_ioctl(filp, ioctl, arg); #endif -- 1.7.7.1