| /* SPDX-License-Identifier: GPL-2.0-only */ | |
| /* | |
| * Copyright (C) 2012 ARM Ltd. | |
| * Author: Marc Zyngier <marc.zyngier@arm.com> | |
| */ | |
| #ifndef __ASM_ARM_KVM_ARCH_TIMER_H | |
| #define __ASM_ARM_KVM_ARCH_TIMER_H | |
| #include <linux/clocksource.h> | |
| #include <linux/hrtimer.h> | |
| #include <linux/irqchip/arm-gic-v5.h> | |
| enum kvm_arch_timers { | |
| TIMER_PTIMER, | |
| TIMER_VTIMER, | |
| NR_KVM_EL0_TIMERS, | |
| TIMER_HVTIMER = NR_KVM_EL0_TIMERS, | |
| TIMER_HPTIMER, | |
| NR_KVM_TIMERS | |
| }; | |
| enum kvm_arch_timer_regs { | |
| TIMER_REG_CNT, | |
| TIMER_REG_CVAL, | |
| TIMER_REG_TVAL, | |
| TIMER_REG_CTL, | |
| TIMER_REG_VOFF, | |
| }; | |
| struct arch_timer_offset { | |
| /* | |
| * If set, pointer to one of the offsets in the kvm's offset | |
| * structure. If NULL, assume a zero offset. | |
| */ | |
| u64 *vm_offset; | |
| /* | |
| * If set, pointer to one of the offsets in the vcpu's sysreg | |
| * array. If NULL, assume a zero offset. | |
| */ | |
| u64 *vcpu_offset; | |
| }; | |
| struct arch_timer_vm_data { | |
| /* Offset applied to the virtual timer/counter */ | |
| u64 voffset; | |
| /* Offset applied to the physical timer/counter */ | |
| u64 poffset; | |
| /* The PPI for each timer, global to the VM */ | |
| u32 ppi[NR_KVM_TIMERS]; | |
| }; | |
| struct arch_timer_context { | |
| /* Emulated Timer (may be unused) */ | |
| struct hrtimer hrtimer; | |
| u64 ns_frac; | |
| /* Offset for this counter/timer */ | |
| struct arch_timer_offset offset; | |
| /* | |
| * We have multiple paths which can save/restore the timer state onto | |
| * the hardware, so we need some way of keeping track of where the | |
| * latest state is. | |
| */ | |
| bool loaded; | |
| /* Who am I? */ | |
| enum kvm_arch_timers timer_id; | |
| /* Duplicated state from arch_timer.c for convenience */ | |
| u32 host_timer_irq; | |
| }; | |
| struct timer_map { | |
| struct arch_timer_context *direct_vtimer; | |
| struct arch_timer_context *direct_ptimer; | |
| struct arch_timer_context *emul_vtimer; | |
| struct arch_timer_context *emul_ptimer; | |
| }; | |
| void get_timer_map(struct kvm_vcpu *vcpu, struct timer_map *map); | |
| struct arch_timer_cpu { | |
| struct arch_timer_context timers[NR_KVM_TIMERS]; | |
| /* Background timer used when the guest is not running */ | |
| struct hrtimer bg_timer; | |
| /* Is the timer enabled */ | |
| bool enabled; | |
| }; | |
| int __init kvm_timer_hyp_init(bool has_gic); | |
| int kvm_timer_enable(struct kvm_vcpu *vcpu); | |
| void kvm_timer_vcpu_reset(struct kvm_vcpu *vcpu); | |
| void kvm_timer_vcpu_init(struct kvm_vcpu *vcpu); | |
| void kvm_timer_sync_nested(struct kvm_vcpu *vcpu); | |
| void kvm_timer_sync_user(struct kvm_vcpu *vcpu); | |
| bool kvm_timer_should_notify_user(struct kvm_vcpu *vcpu); | |
| bool kvm_timer_update_run(struct kvm_vcpu *vcpu); | |
| void kvm_timer_vcpu_terminate(struct kvm_vcpu *vcpu); | |
| void kvm_timer_init_vm(struct kvm *kvm); | |
| int kvm_arm_timer_set_attr(struct kvm_vcpu *vcpu, struct kvm_device_attr *attr); | |
| int kvm_arm_timer_get_attr(struct kvm_vcpu *vcpu, struct kvm_device_attr *attr); | |
| int kvm_arm_timer_has_attr(struct kvm_vcpu *vcpu, struct kvm_device_attr *attr); | |
| u64 kvm_phys_timer_read(void); | |
| void kvm_timer_vcpu_load(struct kvm_vcpu *vcpu); | |
| void kvm_timer_vcpu_put(struct kvm_vcpu *vcpu); | |
| void kvm_timer_init_vhe(void); | |
| #define vcpu_timer(v) (&(v)->arch.timer_cpu) | |
| #define vcpu_get_timer(v,t) (&vcpu_timer(v)->timers[(t)]) | |
| #define vcpu_vtimer(v) (&(v)->arch.timer_cpu.timers[TIMER_VTIMER]) | |
| #define vcpu_ptimer(v) (&(v)->arch.timer_cpu.timers[TIMER_PTIMER]) | |
| #define vcpu_hvtimer(v) (&(v)->arch.timer_cpu.timers[TIMER_HVTIMER]) | |
| #define vcpu_hptimer(v) (&(v)->arch.timer_cpu.timers[TIMER_HPTIMER]) | |
| #define arch_timer_ctx_index(ctx) ((ctx)->timer_id) | |
| #define timer_context_to_vcpu(ctx) container_of((ctx), struct kvm_vcpu, arch.timer_cpu.timers[(ctx)->timer_id]) | |
| #define timer_vm_data(ctx) (&(timer_context_to_vcpu(ctx)->kvm->arch.timer_data)) | |
| #define timer_irq(ctx) (timer_vm_data(ctx)->ppi[arch_timer_ctx_index(ctx)]) | |
| #define get_vgic_ppi(k, i) (((k)->arch.vgic.vgic_model != KVM_DEV_TYPE_ARM_VGIC_V5) ? \ | |
| (i) : (FIELD_PREP(GICV5_HWIRQ_ID, i) | \ | |
| FIELD_PREP(GICV5_HWIRQ_TYPE, GICV5_HWIRQ_TYPE_PPI))) | |
| u64 kvm_arm_timer_read_sysreg(struct kvm_vcpu *vcpu, | |
| enum kvm_arch_timers tmr, | |
| enum kvm_arch_timer_regs treg); | |
| void kvm_arm_timer_write_sysreg(struct kvm_vcpu *vcpu, | |
| enum kvm_arch_timers tmr, | |
| enum kvm_arch_timer_regs treg, | |
| u64 val); | |
| /* Needed for tracing */ | |
| u32 timer_get_ctl(struct arch_timer_context *ctxt); | |
| u64 timer_get_cval(struct arch_timer_context *ctxt); | |
| /* CPU HP callbacks */ | |
| void kvm_timer_cpu_up(void); | |
| void kvm_timer_cpu_down(void); | |
| /* CNTKCTL_EL1 valid bits as of DDI0487J.a */ | |
| #define CNTKCTL_VALID_BITS (BIT(17) | GENMASK_ULL(9, 0)) | |
| DECLARE_STATIC_KEY_FALSE(broken_cntvoff_key); | |
| static inline bool has_broken_cntvoff(void) | |
| { | |
| return static_branch_unlikely(&broken_cntvoff_key); | |
| } | |
| static inline bool has_cntpoff(void) | |
| { | |
| return (has_vhe() && cpus_have_final_cap(ARM64_HAS_ECV_CNTPOFF)); | |
| } | |
| static inline u64 timer_get_offset(struct arch_timer_context *ctxt) | |
| { | |
| u64 offset = 0; | |
| if (!ctxt) | |
| return 0; | |
| if (ctxt->offset.vm_offset) | |
| offset += *ctxt->offset.vm_offset; | |
| if (ctxt->offset.vcpu_offset) | |
| offset += *ctxt->offset.vcpu_offset; | |
| return offset; | |
| } | |
| static inline void timer_set_offset(struct arch_timer_context *ctxt, u64 offset) | |
| { | |
| if (!ctxt->offset.vm_offset) { | |
| WARN(offset, "timer %d\n", arch_timer_ctx_index(ctxt)); | |
| return; | |
| } | |
| WRITE_ONCE(*ctxt->offset.vm_offset, offset); | |
| } | |
| #endif |