diff --git a/arch/arm64/include/asm/kvm_hyp.h b/arch/arm64/include/asm/kvm_hyp.h index c20023a81a74..d12b208d67f3 100644 --- a/arch/arm64/include/asm/kvm_hyp.h +++ b/arch/arm64/include/asm/kvm_hyp.h @@ -134,4 +134,10 @@ extern bool kvm_nvhe_sym(smccc_trng_available); extern bool kvm_nvhe_sym(__pkvm_modules_enabled); +struct kvm_nvhe_clock_data { + u32 mult; + u32 shift; + u64 epoch_ns; + u64 epoch_cyc; +}; #endif /* __ARM64_KVM_HYP_H__ */ diff --git a/arch/arm64/kvm/hyp/include/nvhe/clock.h b/arch/arm64/kvm/hyp/include/nvhe/clock.h new file mode 100644 index 000000000000..7e5c2d2b1886 --- /dev/null +++ b/arch/arm64/kvm/hyp/include/nvhe/clock.h @@ -0,0 +1,15 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __ARM64_KVM_HYP_NVHE_CLOCK_H +#define __ARM64_KVM_HYP_NVHE_CLOCK_H +#include + +#include + +#ifdef CONFIG_TRACING +void trace_clock_update(struct kvm_nvhe_clock_data *data); +u64 trace_clock(void); +#else +static inline void trace_clock_update(struct kvm_nvhe_clock_data *data) { } +static inline u64 trace_clock(void) { return 0; } +#endif +#endif diff --git a/arch/arm64/kvm/hyp/nvhe/Makefile b/arch/arm64/kvm/hyp/nvhe/Makefile index 2e5b26ebfddf..9a3b3eb6be0e 100644 --- a/arch/arm64/kvm/hyp/nvhe/Makefile +++ b/arch/arm64/kvm/hyp/nvhe/Makefile @@ -9,6 +9,7 @@ hyp-obj-y := timer-sr.o sysreg-sr.o debug-sr.o switch.o tlb.o hyp-init.o host.o serial.o hyp-obj-y += ../vgic-v3-sr.o ../aarch32.o ../vgic-v2-cpuif-proxy.o ../entry.o \ ../fpsimd.o ../hyp-entry.o ../exception.o ../pgtable.o +hyp-obj-$(CONFIG_TRACING) += clock.o hyp-obj-$(CONFIG_DEBUG_LIST) += list_debug.o hyp-obj-$(CONFIG_MODULES) += modules.o hyp-obj-y += $(lib-objs) diff --git a/arch/arm64/kvm/hyp/nvhe/clock.c b/arch/arm64/kvm/hyp/nvhe/clock.c new file mode 100644 index 000000000000..4ff87e86787c --- /dev/null +++ b/arch/arm64/kvm/hyp/nvhe/clock.c @@ -0,0 +1,42 @@ +// SPDX-License-Identifier: GPL-2.0 +#include + +#include +#include + +static struct kvm_nvhe_clock_data trace_clock_data; + +/* + * Update without any locks! This is fine because tracing, the sole user of this + * clock is ordering the memory and protects from races between read and + * updates. + */ +void trace_clock_update(struct kvm_nvhe_clock_data *data) +{ + trace_clock_data.mult = data->mult; + trace_clock_data.shift = data->shift; + trace_clock_data.epoch_ns = data->epoch_ns; + trace_clock_data.epoch_cyc = data->epoch_cyc; +} + +/* + * This clock is relying on host provided slope and epoch values to return + * something synchronized with the host. The downside is we can't trust the + * output which must not be used for anything else than debugging. + */ +u64 trace_clock(void) +{ + u64 cyc = __arch_counter_get_cntpct() - trace_clock_data.epoch_cyc; + __uint128_t ns; + + /* + * The host kernel can avoid the 64-bits overflow of the multiplication + * by updating the epoch value with a timer (see + * kernel/time/clocksource.c). The hypervisor doesn't have that option, + * so let's do a more costly 128-bits mult here. + */ + ns = (__uint128_t)cyc * trace_clock_data.mult; + ns >>= trace_clock_data.shift; + + return (u64)ns + trace_clock_data.epoch_ns; +}