* [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).