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_emulate.h | 9 +++++++ arch/arm/include/asm/kvm_host.h | 8 ++++++ arch/arm/include/asm/kvm_vgic.h | 47 ++++++++++++++++++++++++++++++++++++ arch/arm/kvm/arm.c | 18 ++++++++++++++ arch/arm/kvm/emulate.c | 2 +- arch/arm/kvm/interrupts.S | 18 ++++++++++++++ arch/arm/kvm/mmu.c | 3 +++ virt/kvm/kvm_main.c | 5 ++-- 8 files changed, 107 insertions(+), 3 deletions(-) create mode 100644 arch/arm/include/asm/kvm_vgic.h diff --git a/arch/arm/include/asm/kvm_emulate.h b/arch/arm/include/asm/kvm_emulate.h index c41537b..7774ec4 100644 --- a/arch/arm/include/asm/kvm_emulate.h +++ b/arch/arm/include/asm/kvm_emulate.h @@ -22,6 +22,15 @@ #include <linux/kvm_host.h> #include <asm/kvm_asm.h> +/* + * The in-kernel MMIO emulation code wants to use a copy of run->mmio, + * which is an anonymous type. In order to avoid keeping the two types + * in sync, use the below horror. Yes, this is beyond ugly. + */ +struct kvm_exit_mmio { + typeof(((struct kvm_run *)0)->mmio) mmio; +}; + u32 *vcpu_reg_mode(struct kvm_vcpu *vcpu, u8 reg_num, enum vcpu_mode mode); static inline u8 __vcpu_mode(u32 cpsr) diff --git a/arch/arm/include/asm/kvm_host.h b/arch/arm/include/asm/kvm_host.h index 91be029..3b289b8 100644 --- a/arch/arm/include/asm/kvm_host.h +++ b/arch/arm/include/asm/kvm_host.h @@ -24,6 +24,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 @@ -45,6 +47,9 @@ struct kvm_arch { /* VTTBR value associated with above pgd and vmid */ u64 vttbr; + + /* Interrupt controller */ + struct vgic_dist vgic; }; #define EXCEPTION_NONE 0 @@ -125,6 +130,9 @@ struct kvm_vcpu_arch { /* Hyp exception information */ u32 hyp_pc; /* PC when exception was taken from Hyp mode */ + + /* VGIC state */ + 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..8e8bd6d --- /dev/null +++ b/arch/arm/include/asm/kvm_vgic.h @@ -0,0 +1,47 @@ +#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; +struct kvm_exit_mmio; + +#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, + struct kvm_exit_mmio *mmio) +{ + 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 8bb53cb..bdf0f86 100644 --- a/arch/arm/kvm/arm.c +++ b/arch/arm/kvm/arm.c @@ -177,6 +177,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; @@ -289,6 +292,9 @@ int kvm_arch_vcpu_init(struct kvm_vcpu *vcpu) if (ret < 0) return ret; + /* Set up VGIC */ + kvm_vgic_vcpu_init(vcpu); + return 0; } @@ -343,6 +349,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; } @@ -578,6 +585,8 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu, struct kvm_run *run) update_vttbr(vcpu->kvm); + kvm_vgic_sync_to_cpu(vcpu); + /* * Make sure preemption is disabled while calling handle_exit * as exit handling touches CPU-specific resources, such as @@ -626,6 +635,8 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu, struct kvm_run *run) } else { exit_reason = ret; /* 0 == KVM_EXIT_UNKNOWN */ } + + kvm_vgic_sync_from_cpu(vcpu); } if (vcpu->sigset_active) @@ -808,6 +819,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 564add2..4580c1f4 100644 --- a/arch/arm/kvm/emulate.c +++ b/arch/arm/kvm/emulate.c @@ -518,7 +518,7 @@ int kvm_handle_wfi(struct kvm_vcpu *vcpu, struct kvm_run *run) { trace_kvm_wfi(vcpu->arch.regs.pc); vcpu->stat.wfi_exits++; - 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 fd7331c..f3e4c3c 100644 --- a/arch/arm/kvm/interrupts.S +++ b/arch/arm/kvm/interrupts.S @@ -267,6 +267,20 @@ ENDPROC(__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 @@ -324,6 +338,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 @@ -412,6 +428,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 ca76be0..4955a51 100644 --- a/arch/arm/kvm/mmu.c +++ b/arch/arm/kvm/mmu.c @@ -598,6 +598,7 @@ static int io_mem_abort(struct kvm_vcpu *vcpu, struct kvm_run *run, { unsigned long rd, len, instr_len; bool is_write, sign_extend; + struct kvm_exit_mmio mmio; if (!(vcpu->arch.hsr & HSR_ISV)) return invalid_io_mem_abort(vcpu, fault_ipa); @@ -654,6 +655,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 1d33877..d19c0f0 100644 --- a/virt/kvm/kvm_main.c +++ b/virt/kvm/kvm_main.c @@ -1756,12 +1756,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.10.3