KVM/ARM Architected Timers support
Subsystems (2)
v2 cover KVM/ARM Architected Timers support
The following series implements support for the architected generic timers for KVM/ARM.
This is an unmodified repost of the previously submitted series.
This patch series can also be pulled from:
git://github.com/virtualopensystems/linux-kvm-arm.git
branch: kvm-arm-v12-vgic-timers
---
Marc Zyngier (5):
ARM: arch_timers: switch to physical timers if HYP mode is available
ARM: KVM: arch_timers: Add minimal infrastructure
ARM: KVM: arch_timers: Add guest timer core support
ARM: KVM: arch_timers: Add timer world switch
ARM: KVM: arch_timers: Wire the init code and config option
arch/arm/include/asm/kvm_arch_timer.h | 95 +++++++++++++++ arch/arm/include/asm/kvm_host.h | 5 + arch/arm/kernel/arch_timer.c | 7 + arch/arm/kernel/asm-offsets.c | 8 + arch/arm/kvm/Kconfig | 7 + arch/arm/kvm/Makefile | 1 arch/arm/kvm/arm.c | 14 ++ arch/arm/kvm/interrupts.S | 2 arch/arm/kvm/interrupts_head.S | 60 ++++++++++ arch/arm/kvm/reset.c | 9 + arch/arm/kvm/timer.c | 204 +++++++++++++++++++++++++++++++++ arch/arm/kvm/vgic.c | 4 + 12 files changed, 415 insertions(+), 1 deletion(-) create mode 100644 arch/arm/include/asm/kvm_arch_timer.h create mode 100644 arch/arm/kvm/timer.c
PATCH v2 1/5 ARM: arch_timers: switch to physical timers if HYP mode is available
From: Marc Zyngier <[email protected]>
If we're booted in HYP mode, it is possible that we'll run some kind of virtualized environment. In this case, it is a better to switch to the physical timers, and leave the virtual timers to guests.
--- arch/arm/kernel/arch_timer.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-)
arch/arm/kernel/arch_timer.c +6 -1
@@ -25,6 +25,7 @@#include <asm/arch_timer.h> #include <asm/system_info.h> #include <asm/sched_clock.h> +#include <asm/virt.h> static unsigned long arch_timer_rate;
@@ -490,10 +491,14 @@ int __init arch_timer_of_register(void) arch_timer_ppi[i] =irq_of_parse_and_map (np, i);/* +* If HYP mode is available, we know that the physical timer +* has been configured to be accessible from PL1. Use it, so +* that a guest can use the virtual timer instead. +* * If no interrupt provided for virtual timer, we'll have to * stick to the physical timer. It'd better be accessible... */ -if (!arch_timer_ppi[VIRT_PPI ]) { +if (is_hyp_mode_available () || !arch_timer_ppi[VIRT_PPI ]) { arch_timer_use_virtual = false; if (!arch_timer_ppi[PHYS_SECURE_PPI] ||
PATCH v2 2/5 ARM: KVM: arch_timers: Add minimal infrastructure
In reply to Christoffer DallFrom: Marc Zyngier <[email protected]>
Add some very minimal architected timer related infrastructure. For the moment, we just provide empty structures, and enable/disable access to the physical timer across world switch.
--- arch/arm/include/asm/kvm_arch_timer.h | 45 +++++++++++++++++++++++++++++++++ arch/arm/include/asm/kvm_host.h | 5 ++++ arch/arm/kvm/interrupts.S | 2 + arch/arm/kvm/interrupts_head.S | 19 ++++++++++++++ 4 files changed, 71 insertions(+) create mode 100644 arch/arm/include/asm/kvm_arch_timer.h
arch/arm/include/asm/kvm_arch_timer.h new file +45 -0
@@ -0,0 +1,45 @@ +/* +* Copyright (C) 2012 ARM Ltd. +* Author: Marc Zyngier <[email protected]> +* +* 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, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifndef __ASM_ARM_KVM_ARCH_TIMER_H +#define __ASM_ARM_KVM_ARCH_TIMER_H + +struct arch_timer_kvm { +}; + +struct arch_timer_cpu { +}; + +#ifndef CONFIG_KVM_ARM_TIMER +static inline int kvm_timer_hyp_init (void ) +{ +return 0 ; +}; + +static inline int kvm_timer_init (struct kvm *kvm) +{ +return 0 ; +} + +static inline void kvm_timer_vcpu_init (struct kvm_vcpu *vcpu) {} +static inline void kvm_timer_sync_to_cpu (struct kvm_vcpu *vcpu) {} +static inline void kvm_timer_sync_from_cpu (struct kvm_vcpu *vcpu) {} +static inline void kvm_timer_vcpu_terminate (struct kvm_vcpu *vcpu) {} +#endif + +#endif
arch/arm/include/asm/kvm_host.h +5 -0
@@ -23,6 +23,7 @@#include <asm/kvm_asm.h> #include <asm/fpstate.h> #include <asm/kvm_vgic.h> +#include <asm/kvm_arch_timer.h> #define KVM_MAX_VCPUS NR_CPUS#define KVM_MEMORY_SLOTS 32
@@ -56,6 +57,9 @@ struct kvm_arch {/* Interrupt controller */ struct vgic_dist vgic; + +/* Timer */ +struct arch_timer_kvm timer; };#define KVM_NR_MEM_OBJS 40
@@ -93,6 +97,7 @@ struct kvm_vcpu_arch {/* VGIC state */ struct vgic_cpu vgic_cpu; +struct arch_timer_cpu timer_cpu;/* * Anything that is not used directly from assembly code goes
arch/arm/kvm/interrupts.S +2 -0
@@ -105,6 +105,7 @@ ENTRY(__kvm_vcpu_run) store_mode_state sp, fiq restore_vgic_state r0 + restore_timer_state r0 @ Store hardware CP15 state and load guest state read_cp15_state
@@ -223,6 +224,7 @@ after_vfp_restore: read_cp15_state 1, r1 write_cp15_state + save_timer_state r1 save_vgic_state r1 load_mode_state sp, fiq
arch/arm/kvm/interrupts_head.S +19 -0
@@ -302,6 +302,25 @@ #endif .endm +#define CNTHCTL_PL1PCTEN (1 << 0) +#define CNTHCTL_PL1PCEN (1 << 1) + +.macro save_timer_state vcpup + @ Allow physical timer/counter access for the host + mrc p15, 4, r2, c14, c1, 0 @ CNTHCTL + orr r2, r2, #(CNTHCTL_PL1PCEN | CNTHCTL_PL1PCTEN) + mcr p15, 4, r2, c14, c1, 0 @ CNTHCTL +.endm + +.macro restore_timer_state vcpup + @ Disallow physical timer access for the guest + @ Physical counter access is allowed + mrc p15, 4, r2, c14, c1, 0 @ CNTHCTL + orr r2, r2, #CNTHCTL_PL1PCTEN + bic r2, r2, #CNTHCTL_PL1PCEN + mcr p15, 4, r2, c14, c1, 0 @ CNTHCTL +.endm + /* Configures the HSTR (Hyp System Trap Register) on entry/return * (hardware reset value is 0) */ .macro set_hstr entry
PATCH v2 3/5 ARM: KVM: arch_timers: Add guest timer core support
In reply to Christoffer DallFrom: Marc Zyngier <[email protected]>
We can inject a timer interrupt into the guest as a result of three possible events:
- The virtual timer interrupt has fired while we were still executing the guest
- The timer interrupt hasn't fired, but it expired while we were doing the world switch
- A hrtimer we programmed earlier has fired
--- arch/arm/include/asm/kvm_arch_timer.h | 52 ++++++++ arch/arm/kvm/reset.c | 9 + arch/arm/kvm/timer.c | 204 +++++++++++++++++++++++++++++++++ 3 files changed, 264 insertions(+), 1 deletion(-) create mode 100644 arch/arm/kvm/timer.c
arch/arm/include/asm/kvm_arch_timer.h +51 -1
@@ -19,13 +19,63 @@ #ifndef __ASM_ARM_KVM_ARCH_TIMER_H #define __ASM_ARM_KVM_ARCH_TIMER_H +#include <linux/clocksource.h> +#include <linux/hrtimer.h> +#include <linux/workqueue.h> + struct arch_timer_kvm { +#ifdef CONFIG_KVM_ARM_TIMER + /* Is the timer enabled */ + bool enabled; + + /* + * Virtual offset (kernel access it through cntvoff, HYP code + * access it as two 32bit values). + */ + union { + cycle_t cntvoff; + struct { + u32 low; /* Restored only */ + u32 high; /* Restored only */ + } cntvoff32; + }; +#endif }; struct arch_timer_cpu { +#ifdef CONFIG_KVM_ARM_TIMER + /* Background timer used when the guest is not running */ + struct hrtimer timer; + + /* Work queued with the above timer expires */ + struct work_struct expired; + + /* Background timer active */ + bool armed; + + /* Timer IRQ */ + const struct kvm_irq_level *irq; + + /* Registers: control register, timer value */ + u32 cntv_ctl; /* Saved/restored */ + union { + cycle_t cntv_cval; + struct { + u32 low; /* Saved/restored */ + u32 high; /* Saved/restored */ + } cntv_cval32; + }; +#endif }; -#ifndef CONFIG_KVM_ARM_TIMER +#ifdef CONFIG_KVM_ARM_TIMER +int kvm_timer_hyp_init(void); +int kvm_timer_init(struct kvm *kvm); +void kvm_timer_vcpu_init(struct kvm_vcpu *vcpu); +void kvm_timer_sync_to_cpu(struct kvm_vcpu *vcpu); +void kvm_timer_sync_from_cpu(struct kvm_vcpu *vcpu); +void kvm_timer_vcpu_terminate(struct kvm_vcpu *vcpu); +#else static inline int kvm_timer_hyp_init(void) { return 0;
arch/arm/kvm/reset.c +9 -0
@@ -37,6 +37,12 @@ static struct kvm_regs a15_regs_reset = { .cpsr = SVC_MODE | PSR_A_BIT | PSR_I_BIT | PSR_F_BIT, }; +#ifdef CONFIG_KVM_ARM_TIMER +static const struct kvm_irq_level a15_virt_timer_ppi = { + .irq =27 ,/* A7/A15 specific */ + .level =1 , +}; +#endif /******************************************************************************* * Exported reset function
@@ -59,6 +65,9 @@ int kvm_reset_vcpu(struct kvm_vcpu *vcpu)return -EINVAL ; cpu_reset = &a15_regs_reset; vcpu->arch.midr =read_cpuid_id (); +#ifdef CONFIG_KVM_ARM_TIMER + vcpu->arch.timer_cpu.irq = &a15_virt_timer_ppi; +#endif break ;default :return -ENODEV ;
arch/arm/kvm/timer.c new file +204 -0
204 lines not on this page · raw message
PATCH v2 4/5 ARM: KVM: arch_timers: Add timer world switch
In reply to Christoffer DallFrom: Marc Zyngier <[email protected]>
Do the necessary save/restore dance for the timers in the world switch code. In the process, allow the guest to read the physical counter, which is useful for its own clock_event_device.
--- arch/arm/kernel/asm-offsets.c | 8 ++++++++ arch/arm/kvm/arm.c | 3 +++ arch/arm/kvm/interrupts_head.S | 41 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 52 insertions(+)
arch/arm/kernel/asm-offsets.c +8 -0
@@ -176,6 +176,14 @@ int main(void)DEFINE (VGIC_CPU_APR , offsetof(struct vgic_cpu , vgic_apr));DEFINE (VGIC_CPU_LR , offsetof(struct vgic_cpu , vgic_lr));DEFINE (VGIC_CPU_NR_LR , offsetof(struct vgic_cpu , nr_lr)); +#ifdef CONFIG_KVM_ARM_TIMER + DEFINE(VCPU_TIMER_CNTV_CTL, offsetof(struct kvm_vcpu, arch.timer_cpu.cntv_ctl)); + DEFINE(VCPU_TIMER_CNTV_CVALH, offsetof(struct kvm_vcpu, arch.timer_cpu.cntv_cval32.high)); + DEFINE(VCPU_TIMER_CNTV_CVALL, offsetof(struct kvm_vcpu, arch.timer_cpu.cntv_cval32.low)); + DEFINE(KVM_TIMER_CNTVOFF_H, offsetof(struct kvm, arch.timer.cntvoff32.high)); + DEFINE(KVM_TIMER_CNTVOFF_L, offsetof(struct kvm, arch.timer.cntvoff32.low)); + DEFINE(KVM_TIMER_ENABLED, offsetof(struct kvm, arch.timer.enabled)); +#endif DEFINE(KVM_VGIC_VCTRL, offsetof(struct kvm, arch.vgic.vctrl_base));#endif DEFINE(KVM_VTTBR, offsetof(struct kvm, arch.vttbr));
arch/arm/kvm/arm.c +3 -0
@@ -649,6 +649,7 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu, struct kvm_run *run)update_vttbr (vcpu->kvm);kvm_vgic_sync_to_cpu (vcpu); +kvm_timer_sync_to_cpu (vcpu);local_irq_disable ();
@@ -662,6 +663,7 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu, struct kvm_run *run)if (ret <=0 ||need_new_vmid_gen (vcpu->kvm)) {local_irq_enable (); +kvm_timer_sync_from_cpu (vcpu);kvm_vgic_sync_from_cpu (vcpu);continue ; }
@@ -701,6 +703,7 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu, struct kvm_run *run)* Back from guest *************************************************************/ +kvm_timer_sync_from_cpu (vcpu);kvm_vgic_sync_from_cpu (vcpu); ret =handle_exit (vcpu, run, ret);
arch/arm/kvm/interrupts_head.S +41 -0
53 lines not on this page · raw message
PATCH v2 5/5 ARM: KVM: arch_timers: Wire the init code and config option
In reply to Christoffer DallFrom: Marc Zyngier <[email protected]>
It is now possible to select CONFIG_KVM_ARM_TIMER to enable the KVM architected timer support.
--- arch/arm/kvm/Kconfig | 7 +++++++ arch/arm/kvm/Makefile | 1 + arch/arm/kvm/arm.c | 11 +++++++++++ arch/arm/kvm/vgic.c | 4 ++++ 4 files changed, 23 insertions(+)
arch/arm/kvm/Kconfig +7 -0
@@ -47,6 +47,13 @@ config KVM_ARM_VGIC ---help--- Adds support for a hardware assisted, in-kernel GIC emulation. +config KVM_ARM_TIMER + bool "KVM support for Architected Timers" + depends on KVM_ARM_VGIC && ARM_ARCH_TIMER + select HAVE_KVM_IRQCHIP + ---help--- + Adds support for the Architected Timers in virtual machines + source drivers/virtio/Kconfig endif # VIRTUALIZATION
arch/arm/kvm/Makefile +1 -0
@@ -21,3 +21,4 @@ obj-$(CONFIG_KVM_ARM_HOST) += $(addprefix ../../../virt/kvm/, kvm_main.o coalesc obj-$(CONFIG_KVM_ARM_HOST) += arm.o guest.o mmu.o emulate.o reset.o obj-$(CONFIG_KVM_ARM_HOST) += coproc.o coproc_a15.o obj-$(CONFIG_KVM_ARM_VGIC) += vgic.o +obj-$(CONFIG_KVM_ARM_TIMER) += timer.o
arch/arm/kvm/arm.c +11 -0
@@ -283,6 +283,7 @@ out:void kvm_arch_vcpu_free (struct kvm_vcpu *vcpu) {kvm_mmu_free_memory_caches (vcpu); +kvm_timer_vcpu_terminate (vcpu);kmem_cache_free (kvm_vcpu_cache, vcpu); }
@@ -320,6 +321,9 @@ int kvm_arch_vcpu_init(struct kvm_vcpu *vcpu)/* Set up VGIC */ kvm_vgic_vcpu_init (vcpu); +/* Set up the timer */ +kvm_timer_vcpu_init (vcpu); +return 0 ; }
@@ -1010,6 +1014,13 @@ static int init_hyp_mode(void)if (!err) vgic_present = true; +/* +* Init HYP architected timer support +*/ + err =kvm_timer_hyp_init (); +if (err) + goto out_free_mappings; +return 0 ; out_free_vfp:free_percpu (kvm_host_vfp_state);
arch/arm/kvm/vgic.c +4 -0
@@ -1111,5 +1111,9 @@ int kvm_vgic_init(struct kvm *kvm) out:mutex_unlock (&kvm->lock); + +if (!ret) +kvm_timer_init (kvm); +return ret; }