KVM/ARM Architected Timers support
Subsystems (2)
v6 cover KVM/ARM Architected Timers support
The following series implements support for the architected generic timers for KVM/ARM.
This patch series can also be pulled from:
git://github.com/virtualopensystems/linux-kvm-arm.git
branch: kvm-arm-v16-vgic-timers
Changes since v5:
- Renamed sync_{to,from} to {flush,sync}_hwstate
- Removed ISB's in world-switch code
- Avoid add/sub on vcpu pointer in world-switch
Changes since v1-v4:
- Get virtual IRQ number from DT
- Simplify access to cntvoff and cntv_cval
- Remove extraneous bit clearing
- Abstract timer arming/disarming to improve code readability
- Context switch CNTKCTL across world-switches
- Add CPU hotplug notifier
---
Marc Zyngier (4):
ARM: arch_timers: switch to physical timers if HYP mode is available
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 | 85 ++++++++++ arch/arm/include/asm/kvm_asm.h | 3 arch/arm/include/asm/kvm_host.h | 5 + arch/arm/kernel/arch_timer.c | 7 + arch/arm/kernel/asm-offsets.c | 6 + arch/arm/kvm/Kconfig | 8 + arch/arm/kvm/Makefile | 1 arch/arm/kvm/arch_timer.c | 271 +++++++++++++++++++++++++++++++++ arch/arm/kvm/arm.c | 14 ++ arch/arm/kvm/coproc.c | 4 arch/arm/kvm/interrupts.S | 2 arch/arm/kvm/interrupts_head.S | 90 +++++++++++ arch/arm/kvm/vgic.c | 1 13 files changed, 495 insertions(+), 2 deletions(-) create mode 100644 arch/arm/include/asm/kvm_arch_timer.h create mode 100644 arch/arm/kvm/arch_timer.c
PATCH v6 1/4 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
@@ -26,6 +26,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;
@@ -489,10 +490,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 v6 2/4 ARM: KVM: arch_timers: Add guest timer core support
In reply to Christoffer DallFrom: Marc Zyngier <[email protected]>
Add some the architected timer related infrastructure, and support timer interrupt injection, which can happen as a resultof 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 | 85 ++++++++++ arch/arm/include/asm/kvm_host.h | 5 + arch/arm/kvm/arch_timer.c | 271 +++++++++++++++++++++++++++++++++ arch/arm/kvm/interrupts.S | 2 arch/arm/kvm/interrupts_head.S | 31 ++++ 5 files changed, 394 insertions(+) create mode 100644 arch/arm/include/asm/kvm_arch_timer.h create mode 100644 arch/arm/kvm/arch_timer.c
arch/arm/include/asm/kvm_arch_timer.h new file +85 -0
@@ -0,0 +1,85 @@ +/* +* 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 + +#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 */ +cycle_t cntvoff; +#endif +}; + +struct arch_timer_cpu { +#ifdef CONFIG_KVM_ARM_TIMER +/* Registers: control register, timer value */ +u32 cntv_ctl;/* Saved/restored */ +cycle_t cntv_cval;/* Saved/restored */ + +/* +* Anything that is not used directly from assembly code goes +* here. +*/ + +/* 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; +#endif +}; + +#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_flush_hwstate (struct kvm_vcpu *vcpu); +void kvm_timer_sync_hwstate (struct kvm_vcpu *vcpu); +void kvm_timer_vcpu_terminate (struct kvm_vcpu *vcpu); +#else +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_flush_hwstate (struct kvm_vcpu *vcpu) {} +static inline void kvm_timer_sync_hwstate (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/kvm_mmio.h> #include <asm/fpstate.h> +#include <asm/kvm_arch_timer.h> #define KVM_MAX_VCPUS CONFIG_KVM_ARM_MAX_VCPUS#define KVM_MEMORY_SLOTS 32
@@ -49,6 +50,9 @@ struct kvm_arch {/* VTTBR value associated with below pgd and vmid */ u64 vttbr; +/* Timer */ +struct arch_timer_kvm timer; +/* * Anything that is not used directly from assembly code goes * here.
@@ -99,6 +103,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/arch_timer.c new file +271 -0
271 lines not on this page · raw message
arch/arm/kvm/interrupts.S +2 -0
@@ -95,6 +95,7 @@ ENTRY(__kvm_vcpu_run) save_host_regs restore_vgic_state + restore_timer_state @ Store hardware CP15 state and load guest state read_cp15_state store_to_vcpu = 0
@@ -189,6 +190,7 @@ after_vfp_restore: read_cp15_state store_to_vcpu = 1 write_cp15_state read_from_vcpu = 0 + save_timer_state save_vgic_state restore_host_regs
arch/arm/kvm/interrupts_head.S +31 -0
@@ -453,6 +453,37 @@ vcpu .req r0 @ vcpu pointer always in r0 #endif .endm +#define CNTHCTL_PL1PCTEN (1 << 0) +#define CNTHCTL_PL1PCEN (1 << 1) + +/* + * Save the timer state onto the VCPU and allow physical timer/counter access + * for the host. + * + * Assumes vcpu pointer in vcpu reg + */ +.macro save_timer_state + @ 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 + +/* + * Load the timer state from the VCPU and deny physical timer/counter access + * for the host. + * + * Assumes vcpu pointer in vcpu reg + */ +.macro restore_timer_state + @ 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 + .equ vmentry, 0 .equ vmexit, 1
PATCH v6 3/4 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/include/asm/kvm_asm.h | 3 +- arch/arm/kernel/asm-offsets.c | 6 ++++ arch/arm/kvm/arm.c | 3 ++ arch/arm/kvm/coproc.c | 4 +++ arch/arm/kvm/interrupts_head.S | 59 ++++++++++++++++++++++++++++++++++++++++ 5 files changed, 74 insertions(+), 1 deletion(-)
arch/arm/include/asm/kvm_asm.h +2 -1
@@ -45,7 +45,8 @@#define c13_TID_URW 23/* Thread ID, User R/W */ #define c13_TID_URO 24/* Thread ID, User R/O */ #define c13_TID_PRIV 25/* Thread ID, Privileged */ -#define NR_CP15_REGS 26/* Number of regs (incl. invalid) */ +#define c14_CNTKCTL 26/* Timer Control Register (PL1) */ +#define NR_CP15_REGS 27/* Number of regs (incl. invalid) */ #define ARM_EXCEPTION_RESET 0#define ARM_EXCEPTION_UNDEFINED 1
arch/arm/kernel/asm-offsets.c +6 -0
@@ -179,6 +179,12 @@ 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_CVAL, offsetof(struct kvm_vcpu, arch.timer_cpu.cntv_cval)); + DEFINE(KVM_TIMER_CNTVOFF, offsetof(struct kvm, arch.timer.cntvoff)); + 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
@@ -707,6 +707,7 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu, struct kvm_run *run)update_vttbr (vcpu->kvm);kvm_vgic_flush_hwstate (vcpu); +kvm_timer_flush_hwstate (vcpu);local_irq_disable ();
@@ -720,6 +721,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_hwstate (vcpu);kvm_vgic_sync_hwstate (vcpu);continue ; }
@@ -759,6 +761,7 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu, struct kvm_run *run)* Back from guest *************************************************************/ +kvm_timer_sync_hwstate (vcpu);kvm_vgic_sync_hwstate (vcpu); ret =handle_exit (vcpu, run, ret);
arch/arm/kvm/coproc.c +4 -0
@@ -222,6 +222,10 @@ static const struct coproc_reg cp15_regs[] = { NULL, reset_unknown, c13_TID_URO }, {CRn (13 ),CRm (0 ),Op1 (0 ),Op2 (4 ), is32, NULL, reset_unknown, c13_TID_PRIV }, + +/* CNTKCTL: swapped by interrupt.S. */ + { CRn(14), CRm( 1), Op1( 0), Op2( 0), is32, + NULL, reset_val, c14_CNTKCTL, 0x00000000 }, };/* Target specific emulation tables */
arch/arm/kvm/interrupts_head.S +59 -0
91 lines not on this page · raw message
PATCH v6 4/4 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 | 8 ++++++++ arch/arm/kvm/Makefile | 1 + arch/arm/kvm/arm.c | 11 +++++++++++ arch/arm/kvm/vgic.c | 1 + 4 files changed, 21 insertions(+)
arch/arm/kvm/Kconfig +8 -0
@@ -59,6 +59,14 @@ 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 + default y + ---help--- + Adds support for the Architected Timers in virtual machines + source drivers/virtio/Kconfig endif # VIRTUALIZATION
arch/arm/kvm/Makefile +1 -0
@@ -20,3 +20,4 @@ obj-y += kvm-arm.o init.o interrupts.o obj-y += arm.o guest.o mmu.o emulate.o reset.o obj-y += coproc.o coproc_a15.o mmio.o psci.o obj-$(CONFIG_KVM_ARM_VGIC) += vgic.o +obj-$(CONFIG_KVM_ARM_TIMER) += arch_timer.o
arch/arm/kvm/arm.c +11 -0
@@ -289,6 +289,7 @@ int kvm_arch_vcpu_postcreate(struct kvm_vcpu *vcpu)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); }
@@ -330,6 +331,9 @@ int kvm_arch_vcpu_init(struct kvm_vcpu *vcpu)if (ret)return ret; +/* Set up the timer */ +kvm_timer_vcpu_init (vcpu); +return 0 ; }
@@ -1096,6 +1100,13 @@ static int init_hyp_mode(void) vgic_present = true;#endif +/* +* Init HYP architected timer support +*/ + err =kvm_timer_hyp_init (); +if (err) + goto out_free_mappings; +kvm_info ("Hyp mode initialized successfully\n" );return 0 ; out_free_vfp:
arch/arm/kvm/vgic.c +1 -0
@@ -1417,6 +1417,7 @@ int kvm_vgic_init(struct kvm *kvm)for (i =VGIC_NR_PRIVATE_IRQS ; i <VGIC_NR_IRQS ; i +=4 )vgic_set_target_reg (kvm,0 , i); +kvm_timer_init (kvm); kvm->arch.vgic.ready = true; out:mutex_unlock (&kvm->lock);