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 <ouyangxiangzhen@xiaomi.com>
This commit is contained in:
parent
c009c386cc
commit
1ea3c01062
4 changed files with 96 additions and 21 deletions
|
|
@ -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)
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
g_x86_64_timer_freq = CONFIG_ARCH_INTEL64_CORE_FREQ_KHZ * 1000ul;
|
||||
|
||||
__asm__ volatile("cpuid"
|
||||
: "=c" (crystal_freq), "=b" (numerator),
|
||||
"=a" (denominator)
|
||||
: "a" (X86_64_CPUID_TSC)
|
||||
: "rdx", "memory");
|
||||
|
||||
if (numerator == 0 || denominator == 0 || crystal_freq == 0)
|
||||
if (CONFIG_ARCH_INTEL64_CORE_FREQ_KHZ == 0)
|
||||
{
|
||||
# ifndef CONFIG_ARCH_INTEL64_TSC_FREQ_VMWARE
|
||||
g_x86_64_timer_freq = x86_64_timer_tsc_freq_15h();
|
||||
# else
|
||||
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 * 1000ul;
|
||||
#endif
|
||||
|
||||
if (g_x86_64_timer_freq == 0)
|
||||
{
|
||||
/* The TSC frequency is not available */
|
||||
|
||||
PANIC();
|
||||
}
|
||||
else
|
||||
{
|
||||
g_x86_64_timer_freq = crystal_freq / denominator * numerator;
|
||||
}
|
||||
# else
|
||||
g_x86_64_timer_freq = CONFIG_ARCH_INTEL64_CORE_FREQ_KHZ * 1000L;
|
||||
# endif
|
||||
#elif defined(CONFIG_ARCH_INTEL64_TSC)
|
||||
g_x86_64_timer_freq = CONFIG_ARCH_INTEL64_APIC_FREQ_KHZ * 1000L;
|
||||
#endif
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue