This is an automated email from the ASF dual-hosted git repository. raiden00 pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/nuttx.git
The following commit(s) were added to refs/heads/master by this push: new 1ea3c01062 arch/x86_64: Implement TSC frequency query via CPUID 0x40000010 1ea3c01062 is described below commit 1ea3c010627f68141caccf5f11992c6da1a464da Author: ouyangxiangzhen <ouyangxiangz...@xiaomi.com> AuthorDate: Fri Feb 21 18:00:37 2025 +0800 arch/x86_64: Implement TSC frequency query via CPUID 0x40000010 This commit introduces support for querying TSC frequency using CPUID 0x40000010. This function can be tested with the following command: `sudo qemu-system-x86_64 -enable-kvm -cpu host,+invtsc,+vmware-cpuid-freq -m 2G -kernel nuttx -nographic -serial mon:stdio -s` Signed-off-by: ouyangxiangzhen <ouyangxiangz...@xiaomi.com> --- arch/x86_64/include/intel64/arch.h | 1 + arch/x86_64/src/common/x86_64_internal.h | 10 ++++ arch/x86_64/src/intel64/Kconfig | 7 +++ arch/x86_64/src/intel64/intel64_freq.c | 95 +++++++++++++++++++++++++------- 4 files changed, 94 insertions(+), 19 deletions(-) diff --git a/arch/x86_64/include/intel64/arch.h b/arch/x86_64/include/intel64/arch.h index 94d8334700..90c0bb2bfa 100644 --- a/arch/x86_64/include/intel64/arch.h +++ b/arch/x86_64/include/intel64/arch.h @@ -240,6 +240,7 @@ # define X86_64_CPUID_07_AVX512VL (1 << 31) #define X86_64_CPUID_XSAVE 0x0d #define X86_64_CPUID_TSC 0x15 +#define X86_64_CPUID_TSC_VMWARE 0x40000010 #define X86_64_CPUID_EXTINFO 0x80000001 # define X86_64_CPUID_EXTINFO_RDTSCP (1 << 27) diff --git a/arch/x86_64/src/common/x86_64_internal.h b/arch/x86_64/src/common/x86_64_internal.h index 04830cd09a..a4b79fde51 100644 --- a/arch/x86_64/src/common/x86_64_internal.h +++ b/arch/x86_64/src/common/x86_64_internal.h @@ -193,6 +193,16 @@ extern uint8_t _etbss[]; /* End+1 of .tbss */ * Inline Functions ****************************************************************************/ +static inline void x86_64_cpuid(uint32_t leaf, uint32_t subleaf, + uint32_t *eax, uint32_t *ebx, + uint32_t *ecx, uint32_t *edx) +{ + __asm__ volatile("cpuid" + : "=a"(*eax), "=b"(*ebx), "=c"(*ecx), "=d"(*edx) + : "a" (leaf), "c" (subleaf) + : "memory"); +} + #ifdef CONFIG_ARCH_KERNEL_STACK static inline_function uint64_t *x86_64_get_ktopstk(void) { diff --git a/arch/x86_64/src/intel64/Kconfig b/arch/x86_64/src/intel64/Kconfig index 5c213f0117..b89dcb6c1a 100644 --- a/arch/x86_64/src/intel64/Kconfig +++ b/arch/x86_64/src/intel64/Kconfig @@ -88,6 +88,13 @@ config ARCH_INTEL64_CORE_FREQ_KHZ to set the TSC deadline timer frequency. If set to 0 we try to get frequency from CPUID. +config ARCH_INTEL64_TSC_FREQ_VMWARE + bool "Use CPUID 0x40000010 to get CPU Core frequency" + default n + depends on ARCH_INTEL64_CORE_FREQ_KHZ = 0 + ---help--- + Use CPUID 0x40000010 defined by VMware to get CPU Core frequency + in virtualized environments. endif if ARCH_INTEL64_TSC diff --git a/arch/x86_64/src/intel64/intel64_freq.c b/arch/x86_64/src/intel64/intel64_freq.c index 3747d206d0..c2775fe6c6 100644 --- a/arch/x86_64/src/intel64/intel64_freq.c +++ b/arch/x86_64/src/intel64/intel64_freq.c @@ -36,12 +36,74 @@ #include <nuttx/board.h> #include <arch/board/board.h> +#include "x86_64_internal.h" + /**************************************************************************** * Public Data ****************************************************************************/ unsigned long g_x86_64_timer_freq; +/**************************************************************************** + * Inline Functions + ****************************************************************************/ + +static inline uint64_t x86_64_timer_tsc_freq_vmware(void) +{ + uint32_t eax_tsc; + uint32_t ebx_apic; + uint32_t ecx; + uint32_t edx; + + /* CPUID Leaf 0x40000010, Timing Information. + * Timing information leaf first defined by VMware. + * It is also adopted by many hypervisors such as ACRN Hypervisor. + * The leaf returns the TSC frequency and APIC frequency. + * EAX - TSC frequency in kHz. + * EBX - APIC frequency in kHz. + */ + + x86_64_cpuid(X86_64_CPUID_TSC_VMWARE, 0x0, + &eax_tsc, &ebx_apic, &ecx, &edx); + + /* Suppress the warning. */ + + UNUSED(ecx); + UNUSED(edx); + UNUSED(ebx_apic); + + return 1000ul * eax_tsc; +} + +static inline uint64_t x86_64_timer_tsc_freq_15h(void) +{ + uint32_t crystal_freq; + uint32_t numerator; + uint32_t denominator; + uint32_t edx; + + /* CPUID Leaf 0x15h, TSC frequency properties. + * The leaf returns the TSC frequency properties. + * EAX - Denominator. + * EBX - Numerator. + * ECX - Crystal Frequency. + */ + + x86_64_cpuid(X86_64_CPUID_TSC, 0x0, + &denominator, &numerator, &crystal_freq, &edx); + + /* Suppress the warning. */ + + UNUSED(edx); + + if (numerator == 0 || denominator == 0 || crystal_freq == 0) + { + return 0; + } + + return crystal_freq / denominator * numerator; +} + /**************************************************************************** * Public Functions ****************************************************************************/ @@ -74,29 +136,24 @@ unsigned long g_x86_64_timer_freq; void x86_64_timer_calibrate_freq(void) { #ifdef CONFIG_ARCH_INTEL64_TSC_DEADLINE -# if CONFIG_ARCH_INTEL64_CORE_FREQ_KHZ == 0 - unsigned long crystal_freq; - unsigned long numerator; - unsigned long denominator; - - __asm__ volatile("cpuid" - : "=c" (crystal_freq), "=b" (numerator), - "=a" (denominator) - : "a" (X86_64_CPUID_TSC) - : "rdx", "memory"); + g_x86_64_timer_freq = CONFIG_ARCH_INTEL64_CORE_FREQ_KHZ * 1000ul; - if (numerator == 0 || denominator == 0 || crystal_freq == 0) - { - PANIC(); - } - else + if (CONFIG_ARCH_INTEL64_CORE_FREQ_KHZ == 0) { - g_x86_64_timer_freq = crystal_freq / denominator * numerator; - } +# ifndef CONFIG_ARCH_INTEL64_TSC_FREQ_VMWARE + g_x86_64_timer_freq = x86_64_timer_tsc_freq_15h(); # else - g_x86_64_timer_freq = CONFIG_ARCH_INTEL64_CORE_FREQ_KHZ * 1000L; + g_x86_64_timer_freq = x86_64_timer_tsc_freq_vmware(); # endif + } #elif defined(CONFIG_ARCH_INTEL64_TSC) - g_x86_64_timer_freq = CONFIG_ARCH_INTEL64_APIC_FREQ_KHZ * 1000L; + g_x86_64_timer_freq = CONFIG_ARCH_INTEL64_APIC_FREQ_KHZ * 1000ul; #endif + + if (g_x86_64_timer_freq == 0) + { + /* The TSC frequency is not available */ + + PANIC(); + } }