linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH] x86/tsc: Read AMD CPU frequency from Core::X86::Msr::PStateDef
@ 2025-08-13 11:23 Stephen Horvath
  2025-08-14  6:36 ` kernel test robot
  2025-08-14 15:25 ` Borislav Petkov
  0 siblings, 2 replies; 5+ messages in thread
From: Stephen Horvath @ 2025-08-13 11:23 UTC (permalink / raw)
  To: Thomas Gleixner, Ingo Molnar, Borislav Petkov, Dave Hansen,
	H. Peter Anvin, x86@kernel.org, linux-kernel@vger.kernel.org
  Cc: Stephen Horvath

AMD's Zen CPUs (17h and newer) have an MSR that provides the CPU/TSC
frequency directly, instead of calibrating it against the PIT.

My understanding of the PIT calibration code is that it loops between
two and three times and takes 10ms (or 50ms) each loop, taking at least
20ms total to calibrate. This patch skips that calibration time.

Through experimentation, this patch seems to save approximately 30ms on
boot time for Zen 4 (19h) CPUs (Ryzen 7 7800X3D & Ryzen 7 7840U).

This has also been tested to not interfere with KVM guests running a
custom TSC frequency.

Signed-off-by: Stephen Horvath <s.horvath@outlook.com.au>
---
 arch/x86/include/asm/tsc.h |  1 +
 arch/x86/kernel/tsc.c      |  2 +
 arch/x86/kernel/tsc_msr.c  | 98 ++++++++++++++++++++++++++++++++++++++
 3 files changed, 101 insertions(+)

diff --git a/arch/x86/include/asm/tsc.h b/arch/x86/include/asm/tsc.h
index 4f7f09f50552..a7e2710aa7f9 100644
--- a/arch/x86/include/asm/tsc.h
+++ b/arch/x86/include/asm/tsc.h
@@ -119,5 +119,6 @@ extern void tsc_save_sched_clock_state(void);
 extern void tsc_restore_sched_clock_state(void);
 
 unsigned long cpu_khz_from_msr(void);
+unsigned long cpu_khz_from_msr_amd(void);
 
 #endif /* _ASM_X86_TSC_H */
diff --git a/arch/x86/kernel/tsc.c b/arch/x86/kernel/tsc.c
index 87e749106dda..9acb7d13719d 100644
--- a/arch/x86/kernel/tsc.c
+++ b/arch/x86/kernel/tsc.c
@@ -911,6 +911,8 @@ unsigned long native_calibrate_cpu_early(void)
 {
 	unsigned long flags, fast_calibrate = cpu_khz_from_cpuid();
 
+	if (!fast_calibrate)
+		fast_calibrate = cpu_khz_from_msr_amd();
 	if (!fast_calibrate)
 		fast_calibrate = cpu_khz_from_msr();
 	if (!fast_calibrate) {
diff --git a/arch/x86/kernel/tsc_msr.c b/arch/x86/kernel/tsc_msr.c
index 48e6cc1cb017..bea62f8f1eb1 100644
--- a/arch/x86/kernel/tsc_msr.c
+++ b/arch/x86/kernel/tsc_msr.c
@@ -234,3 +234,101 @@ unsigned long cpu_khz_from_msr(void)
 
 	return res;
 }
+
+/*
+ * MSR-based CPU/TSC frequency discovery for AMD Zen CPUs.
+ *
+ * Return processor base frequency in KHz, or 0 on failure.
+ */
+unsigned long cpu_khz_from_msr_amd(void)
+{
+	u64 hwcr, pstatedef;
+	unsigned long cpufid, cpudfsid, p0_freq;
+
+	if (boot_cpu_data.x86_vendor != X86_VENDOR_AMD)
+		return 0;
+
+	/*
+	 * This register mapping is only valid for Zen and later CPUs.
+	 * X86_FEATURE_ZEN is not set yet, so we just check the cpuid.
+	 */
+	if (boot_cpu_data.x86 < 0x17)
+		return 0;
+
+	/*
+	 * PPR states for MSR0000_0010:
+	 * The TSC increments at the P0 frequency. The TSC counts at the
+	 * same rate in all P-states, all C states, S0, or S1.
+	 */
+
+	/* Read the Hardware Configuration MSR (MSRC001_0015) */
+	if (rdmsrq_safe(MSR_K7_HWCR, &hwcr))
+		return 0;
+
+	/*
+	 * Check TscFreqSel (bit 24) is set.
+	 * This verifies the TSC does actually increment at P0 frequency.
+	 * E.g. VMs may be configured to increment at a different rate.
+	 */
+	if (!(hwcr & BIT_64(24)))
+		return 0;
+
+	/* Read the zeroth PStateDef MSR (MSRC001_0064) */
+	if (rdmsrq_safe(MSR_AMD_PSTATE_DEF_BASE, &pstatedef))
+		return 0;
+
+	/* Check PstateEn is set (bit 63) */
+	if (!(pstatedef & BIT_64(63)))
+		return 0;
+
+	/* CpuFid is the first 8 bits (7:0) */
+	cpufid = pstatedef & 0xff;
+
+	/* Values between 0Fh-00h are reserved */
+	if (cpufid < 0x0F)
+		return 0;
+
+	/* The PPR defines the core multiplier as CpuFid * 25MHz */
+	p0_freq = cpufid * 25;
+
+	/* Convert from MHz to KHz before dividing */
+	p0_freq *= 1000;
+
+	/* CpuDfsId is the next 6 bits (13:8) */
+	cpudfsid = (pstatedef >> 8) & 0x3f;
+
+	/* Calculate the core divisor */
+	switch (cpudfsid) {
+	case 0x08:
+		/* VCO/1 */
+		break;
+	case 0x09:
+		/* VCO/1.125 */
+		p0_freq = (unsigned long)(p0_freq * 1125ull / 1000);
+		break;
+	case 0x0A ... 0x1A:
+	case 0x1C:
+	case 0x1E:
+	case 0x20:
+	case 0x22:
+	case 0x24:
+	case 0x26:
+	case 0x28:
+	case 0x2A:
+	case 0x2C:
+		/* VCO/<Value/8> */
+		p0_freq /= cpudfsid / 8;
+		break;
+	default:
+		/* Reserved */
+		return 0;
+	}
+
+	/*
+	 * TSC frequency determined by MSR is always considered "known"
+	 * because it is reported by HW.
+	 */
+	setup_force_cpu_cap(X86_FEATURE_TSC_KNOWN_FREQ);
+
+	return p0_freq;
+}
-- 
2.47.2


^ permalink raw reply related	[flat|nested] 5+ messages in thread

* Re: [PATCH] x86/tsc: Read AMD CPU frequency from Core::X86::Msr::PStateDef
  2025-08-13 11:23 [PATCH] x86/tsc: Read AMD CPU frequency from Core::X86::Msr::PStateDef Stephen Horvath
@ 2025-08-14  6:36 ` kernel test robot
  2025-08-14  7:40   ` Stephen Horvath
  2025-08-14 15:25 ` Borislav Petkov
  1 sibling, 1 reply; 5+ messages in thread
From: kernel test robot @ 2025-08-14  6:36 UTC (permalink / raw)
  To: Stephen Horvath, Thomas Gleixner, Ingo Molnar, Borislav Petkov,
	Dave Hansen, H. Peter Anvin, x86@kernel.org,
	linux-kernel@vger.kernel.org
  Cc: oe-kbuild-all, Stephen Horvath

Hi Stephen,

kernel test robot noticed the following build errors:

[auto build test ERROR on tip/x86/core]
[also build test ERROR on tip/master linus/master v6.17-rc1 next-20250814]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url:    https://github.com/intel-lab-lkp/linux/commits/Stephen-Horvath/x86-tsc-Read-AMD-CPU-frequency-from-Core-X86-Msr-PStateDef/20250813-192644
base:   tip/x86/core
patch link:    https://lore.kernel.org/r/20250813112020.345622-1-s.horvath%40outlook.com.au
patch subject: [PATCH] x86/tsc: Read AMD CPU frequency from Core::X86::Msr::PStateDef
config: i386-randconfig-007-20250814 (https://download.01.org/0day-ci/archive/20250814/202508141439.JO8YA6fq-lkp@intel.com/config)
compiler: gcc-12 (Debian 12.2.0-14+deb12u1) 12.2.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20250814/202508141439.JO8YA6fq-lkp@intel.com/reproduce)

If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202508141439.JO8YA6fq-lkp@intel.com/

All errors (new ones prefixed by >>):

   ld: arch/x86/kernel/tsc_msr.o: in function `cpu_khz_from_msr_amd':
>> arch/x86/kernel/tsc_msr.c:307: undefined reference to `__udivdi3'


vim +307 arch/x86/kernel/tsc_msr.c

   237	
   238	/*
   239	 * MSR-based CPU/TSC frequency discovery for AMD Zen CPUs.
   240	 *
   241	 * Return processor base frequency in KHz, or 0 on failure.
   242	 */
   243	unsigned long cpu_khz_from_msr_amd(void)
   244	{
   245		u64 hwcr, pstatedef;
   246		unsigned long cpufid, cpudfsid, p0_freq;
   247	
   248		if (boot_cpu_data.x86_vendor != X86_VENDOR_AMD)
   249			return 0;
   250	
   251		/*
   252		 * This register mapping is only valid for Zen and later CPUs.
   253		 * X86_FEATURE_ZEN is not set yet, so we just check the cpuid.
   254		 */
   255		if (boot_cpu_data.x86 < 0x17)
   256			return 0;
   257	
   258		/*
   259		 * PPR states for MSR0000_0010:
   260		 * The TSC increments at the P0 frequency. The TSC counts at the
   261		 * same rate in all P-states, all C states, S0, or S1.
   262		 */
   263	
   264		/* Read the Hardware Configuration MSR (MSRC001_0015) */
   265		if (rdmsrq_safe(MSR_K7_HWCR, &hwcr))
   266			return 0;
   267	
   268		/*
   269		 * Check TscFreqSel (bit 24) is set.
   270		 * This verifies the TSC does actually increment at P0 frequency.
   271		 * E.g. VMs may be configured to increment at a different rate.
   272		 */
   273		if (!(hwcr & BIT_64(24)))
   274			return 0;
   275	
   276		/* Read the zeroth PStateDef MSR (MSRC001_0064) */
   277		if (rdmsrq_safe(MSR_AMD_PSTATE_DEF_BASE, &pstatedef))
   278			return 0;
   279	
   280		/* Check PstateEn is set (bit 63) */
   281		if (!(pstatedef & BIT_64(63)))
   282			return 0;
   283	
   284		/* CpuFid is the first 8 bits (7:0) */
   285		cpufid = pstatedef & 0xff;
   286	
   287		/* Values between 0Fh-00h are reserved */
   288		if (cpufid < 0x0F)
   289			return 0;
   290	
   291		/* The PPR defines the core multiplier as CpuFid * 25MHz */
   292		p0_freq = cpufid * 25;
   293	
   294		/* Convert from MHz to KHz before dividing */
   295		p0_freq *= 1000;
   296	
   297		/* CpuDfsId is the next 6 bits (13:8) */
   298		cpudfsid = (pstatedef >> 8) & 0x3f;
   299	
   300		/* Calculate the core divisor */
   301		switch (cpudfsid) {
   302		case 0x08:
   303			/* VCO/1 */
   304			break;
   305		case 0x09:
   306			/* VCO/1.125 */
 > 307			p0_freq = (unsigned long)(p0_freq * 1125ull / 1000);

-- 
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [PATCH] x86/tsc: Read AMD CPU frequency from Core::X86::Msr::PStateDef
  2025-08-14  6:36 ` kernel test robot
@ 2025-08-14  7:40   ` Stephen Horvath
  0 siblings, 0 replies; 5+ messages in thread
From: Stephen Horvath @ 2025-08-14  7:40 UTC (permalink / raw)
  To: kernel test robot, Thomas Gleixner, Ingo Molnar, Borislav Petkov,
	Dave Hansen, H. Peter Anvin, x86@kernel.org,
	linux-kernel@vger.kernel.org
  Cc: oe-kbuild-all

On 14/8/25 16:36, kernel test robot wrote:
> Hi Stephen,
> 
> kernel test robot noticed the following build errors:
> 
> All errors (new ones prefixed by >>):
> 
>    ld: arch/x86/kernel/tsc_msr.o: in function `cpu_khz_from_msr_amd':
>>> arch/x86/kernel/tsc_msr.c:307: undefined reference to `__udivdi3'
> 
>  > 307			p0_freq = (unsigned long)(p0_freq * 1125ull / 1000);

Thanks! I can replace this with `mul_u64_u32_div(p0_freq, 1125, 1000)`
in the next revision.

Thanks,
Steve

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [PATCH] x86/tsc: Read AMD CPU frequency from Core::X86::Msr::PStateDef
  2025-08-13 11:23 [PATCH] x86/tsc: Read AMD CPU frequency from Core::X86::Msr::PStateDef Stephen Horvath
  2025-08-14  6:36 ` kernel test robot
@ 2025-08-14 15:25 ` Borislav Petkov
  2025-08-15  1:53   ` Stephen Horvath
  1 sibling, 1 reply; 5+ messages in thread
From: Borislav Petkov @ 2025-08-14 15:25 UTC (permalink / raw)
  To: Stephen Horvath
  Cc: Thomas Gleixner, Ingo Molnar, Dave Hansen, H. Peter Anvin,
	x86@kernel.org, linux-kernel@vger.kernel.org

On Wed, Aug 13, 2025 at 11:23:38AM +0000, Stephen Horvath wrote:
> +	/* The PPR defines the core multiplier as CpuFid * 25MHz */
> +	p0_freq = cpufid * 25;

As someone already pointed out:

PPR Vol 1 for AMD Family 1Ah Model 02h C1

...

MSRC001_006[4...B] [P-state [7:0]] (Core::X86::Msr::PStateDef)

...

CpuFid[11:0]: core frequency ID.

FFFh- <Value>*5
010h

So we need to do per-family checks here.

Not sure if that is worth it, frankly.

-- 
Regards/Gruss,
    Boris.

https://people.kernel.org/tglx/notes-about-netiquette

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [PATCH] x86/tsc: Read AMD CPU frequency from Core::X86::Msr::PStateDef
  2025-08-14 15:25 ` Borislav Petkov
@ 2025-08-15  1:53   ` Stephen Horvath
  0 siblings, 0 replies; 5+ messages in thread
From: Stephen Horvath @ 2025-08-15  1:53 UTC (permalink / raw)
  To: Borislav Petkov
  Cc: Thomas Gleixner, Ingo Molnar, Dave Hansen, H. Peter Anvin,
	x86@kernel.org, linux-kernel@vger.kernel.org

Hi Borislav, Thank you very much for reviewing my patch!

On 15/8/25 01:25, Borislav Petkov wrote:
> On Wed, Aug 13, 2025 at 11:23:38AM +0000, Stephen Horvath wrote:
>> +	/* The PPR defines the core multiplier as CpuFid * 25MHz */
>> +	p0_freq = cpufid * 25;
> 
> As someone already pointed out:

Was this pointed out publicly, or something internally? I couldn't find
any relevant TSC patches for AMD on lore when I looked previously (other
than the Secure TSC which didn't seem applicable).

> 
> PPR Vol 1 for AMD Family 1Ah Model 02h C1
> 
> ...
> 
> MSRC001_006[4...B] [P-state [7:0]] (Core::X86::Msr::PStateDef)
> 
> ...
> 
> CpuFid[11:0]: core frequency ID.
> 
> FFFh- <Value>*5
> 010h
> 
> So we need to do per-family checks here.

Ah, good catch! I did check through the 17h and 19h PPRs, but overlooked
1Ah. At least 1Ah is simpler since there's no divisor.

> 
> Not sure if that is worth it, frankly.
> 

Yeah that's fair enough. I might submit a v2 with some changes next week
and leave it at that, depending on feedback from others.

Thank you again for your time reviewing this!
Steve.

^ permalink raw reply	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2025-08-15  1:53 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-08-13 11:23 [PATCH] x86/tsc: Read AMD CPU frequency from Core::X86::Msr::PStateDef Stephen Horvath
2025-08-14  6:36 ` kernel test robot
2025-08-14  7:40   ` Stephen Horvath
2025-08-14 15:25 ` Borislav Petkov
2025-08-15  1:53   ` Stephen Horvath

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).