Fix 'TSC warp between CPUs' on AMD machines before Linux boots: a UEFI tool that syncs core TSCs from GRUB so Linux keeps the TSC clocksource instead of HPET
C
0
8 commits
updated Sep 28, 2026
Fixes "TSC warp between CPUs" on AMD machines before Linux boots, so the kernel keeps the fast TSC clocksource instead of falling back to HPET.
tsc: Marking TSC unstable due to check_tsc_sync_source failed
clocksource: Switched to clocksource hpet
If your kernel log shows those lines on every boot, your firmware (BIOS) is handing over with the CPU cores' Time Stamp Counters out of sync. tscsync is a small UEFI program that runs from the boot loader (GRUB or systemd-boot) just before Linux, measures every core's TSC, moves the lagging ones forward until they agree, and then checks the result with a test modelled on the kernel's own.
On a Legion Pro 5 16ADR10 this turned a 5.2-billion-cycle warp (about 2 s) on every boot into cores that agree to within about ±10 cycles, and Linux kept the TSC on every cold boot, reboot and resume from sleep.
Check the current boot:
journalctl -k -b | grep -iE 'TSC warp|TSC unstable'
cat /sys/devices/system/clocksource/clocksource0/current_clocksource
You are affected if the first command prints Measured N cycles TSC warp between CPUs and the second prints hpet. tscsync is designed for:
IA32_TSC_ADJUST, so the kernel cannot repair the offset itself.You do not need it on Intel CPUs with TSC_ADJUST (the kernel fixes
those itself), and it cannot help if the warp is caused by something other
than the boot-time offset.
With HPET, every clock read goes to a slow timer chip (about 1.2 µs instead of tens of nanoseconds). The kernel and desktop read the clock tens of thousands of times a second, which costs latency and power. On the Legion, idle CPU temperature dropped from 66–72 °C to 59–63 °C after the fix.
Forcing the TSC with tsc=reliable does not work: the cores really are out of
sync, which causes audio and video stutter. The out-of-tree tsc=directsync
kernel patch tries to fix it in the kernel but was rejected upstream. tscsync
fixes the offset before the kernel starts, so the kernel's own safety checks
stay on and confirm the result.
tscsync.efi before showing its menu; systemd-boot loads the
same code as a driver (EFI/systemd/drivers/tscsyncx64.efi) before its
menu.check_tsc_warp(), stores a report in RAM, and returns to the boot loader.The whole run takes about 0.6 s. Details and measurements: docs/DESIGN.md.
EFI/tscsync/ on the EFI system partition with systemd-boot.install.sh starts in read-only measure mode. You switch to sync only after
you've seen the numbers.sudo scripts/set-mode.sh sync to turn it back on.TSC_ADJUST, without
an invariant TSC, or outside the supported list) unless you opt in.This is unofficial, low-level software. Read the code and use it at your own risk; the MIT license applies.
EFI/tscsync/ on the EFI system partitiongcc, make, binutils, curlsbsigntools and an enrolled MOK, or your own sbctl
keys
(docs/SECURE-BOOT.md)git clone https://github.com/Zanasin/tscsync.git
cd tscsync
make # downloads and verifies gnu-efi, builds the GRUB and systemd-boot binaries
sudo scripts/install.sh # installs in read-only measure mode
Shut down fully, power on, then:
tscsync-status
If the report shows cores out of sync (BAD lines in the survey), switch to
sync mode and reboot:
sudo scripts/set-mode.sh sync
Success looks like this in tscsync-status:
current clocksource : tsc
...
RESULT: all APs in sync (Linux should keep the TSC)
A core marked UNSURE passed the warp test, but its offset reading was taken
over a slower round trip than usual, so tscsync cannot tell whether it is in
sync. The kernel's own check decides: if current clocksource is tsc, it
passed.
| Command | What it does |
|---|---|
sudo scripts/install.sh | Detects GRUB or systemd-boot, the EFI partition and a signing key, installs in measure mode |
sudo scripts/set-mode.sh measure|sync|off | Switches mode for the next boot |
tscsync-status | Clocksource, kernel TSC messages, and this boot's tscsync report |
sudo scripts/uninstall.sh | Removes everything |
build/tscprobe | Live per-core TSC offsets from Linux (read-only) |
sudo tools/idle-bench.sh 300 | Idle temperature, sleep states and CPU power, for before/after comparisons |
vmtest/run.sh | Runs the test suite in QEMU/OVMF VMs via podman |
sudo scripts/set-mode.sh off or
sudo scripts/uninstall.sh.custom.cfg in
GRUB's config directory. systemd-boot: delete
EFI/systemd/drivers/tscsyncx64.efi on the EFI system partition.Your vendor may fix the firmware. Run sudo scripts/set-mode.sh measure and
reboot: if every core reports OK without sync mode, you can uninstall
tscsync.
Whether it works or not, please open an issue with your model, BIOS version,
CPU and the output of tscsync-status. That builds the list in
docs/HARDWARE.md and shows vendors how widespread the bug
is.
docs/report/tsc-firmware-report.pdf covers 80 boots of boot history from the Legion Pro 5. It explains why this BIOS leaves the TSCs out of sync when the hardware can keep them in sync, and makes the case for open firmware with safe, vendor-signed updates. The data and scripts behind it are in docs/report/.
MIT, see LICENSE. Firmware vendors are welcome to use the approach
or the code in their own firmware, which is where this should be fixed. The
report in docs/report/ is under CC BY 4.0.
gnu-efi, downloaded at build time, is under its own BSD-style license.
C
57.1%
Shell
38.8%
Makefile
3.8%
Fix 'TSC warp between CPUs' on AMD machines before Linux boots: a UEFI tool that syncs core TSCs from GRUB so Linux keeps the TSC clocksource instead of HPET
C
0
8 commits
updated Sep 28, 2026
Fixes "TSC warp between CPUs" on AMD machines before Linux boots, so the kernel keeps the fast TSC clocksource instead of falling back to HPET.
tsc: Marking TSC unstable due to check_tsc_sync_source failed
clocksource: Switched to clocksource hpet
If your kernel log shows those lines on every boot, your firmware (BIOS) is handing over with the CPU cores' Time Stamp Counters out of sync. tscsync is a small UEFI program that runs from the boot loader (GRUB or systemd-boot) just before Linux, measures every core's TSC, moves the lagging ones forward until they agree, and then checks the result with a test modelled on the kernel's own.
On a Legion Pro 5 16ADR10 this turned a 5.2-billion-cycle warp (about 2 s) on every boot into cores that agree to within about ±10 cycles, and Linux kept the TSC on every cold boot, reboot and resume from sleep.
Check the current boot:
journalctl -k -b | grep -iE 'TSC warp|TSC unstable'
cat /sys/devices/system/clocksource/clocksource0/current_clocksource
You are affected if the first command prints Measured N cycles TSC warp between CPUs and the second prints hpet. tscsync is designed for:
IA32_TSC_ADJUST, so the kernel cannot repair the offset itself.You do not need it on Intel CPUs with TSC_ADJUST (the kernel fixes
those itself), and it cannot help if the warp is caused by something other
than the boot-time offset.
With HPET, every clock read goes to a slow timer chip (about 1.2 µs instead of tens of nanoseconds). The kernel and desktop read the clock tens of thousands of times a second, which costs latency and power. On the Legion, idle CPU temperature dropped from 66–72 °C to 59–63 °C after the fix.
Forcing the TSC with tsc=reliable does not work: the cores really are out of
sync, which causes audio and video stutter. The out-of-tree tsc=directsync
kernel patch tries to fix it in the kernel but was rejected upstream. tscsync
fixes the offset before the kernel starts, so the kernel's own safety checks
stay on and confirm the result.
tscsync.efi before showing its menu; systemd-boot loads the
same code as a driver (EFI/systemd/drivers/tscsyncx64.efi) before its
menu.check_tsc_warp(), stores a report in RAM, and returns to the boot loader.The whole run takes about 0.6 s. Details and measurements: docs/DESIGN.md.
EFI/tscsync/ on the EFI system partition with systemd-boot.install.sh starts in read-only measure mode. You switch to sync only after
you've seen the numbers.sudo scripts/set-mode.sh sync to turn it back on.TSC_ADJUST, without
an invariant TSC, or outside the supported list) unless you opt in.This is unofficial, low-level software. Read the code and use it at your own risk; the MIT license applies.
EFI/tscsync/ on the EFI system partitiongcc, make, binutils, curlsbsigntools and an enrolled MOK, or your own sbctl
keys
(docs/SECURE-BOOT.md)git clone https://github.com/Zanasin/tscsync.git
cd tscsync
make # downloads and verifies gnu-efi, builds the GRUB and systemd-boot binaries
sudo scripts/install.sh # installs in read-only measure mode
Shut down fully, power on, then:
tscsync-status
If the report shows cores out of sync (BAD lines in the survey), switch to
sync mode and reboot:
sudo scripts/set-mode.sh sync
Success looks like this in tscsync-status:
current clocksource : tsc
...
RESULT: all APs in sync (Linux should keep the TSC)
A core marked UNSURE passed the warp test, but its offset reading was taken
over a slower round trip than usual, so tscsync cannot tell whether it is in
sync. The kernel's own check decides: if current clocksource is tsc, it
passed.
| Command | What it does |
|---|---|
sudo scripts/install.sh | Detects GRUB or systemd-boot, the EFI partition and a signing key, installs in measure mode |
sudo scripts/set-mode.sh measure|sync|off | Switches mode for the next boot |
tscsync-status | Clocksource, kernel TSC messages, and this boot's tscsync report |
sudo scripts/uninstall.sh | Removes everything |
build/tscprobe | Live per-core TSC offsets from Linux (read-only) |
sudo tools/idle-bench.sh 300 | Idle temperature, sleep states and CPU power, for before/after comparisons |
vmtest/run.sh | Runs the test suite in QEMU/OVMF VMs via podman |
sudo scripts/set-mode.sh off or
sudo scripts/uninstall.sh.custom.cfg in
GRUB's config directory. systemd-boot: delete
EFI/systemd/drivers/tscsyncx64.efi on the EFI system partition.Your vendor may fix the firmware. Run sudo scripts/set-mode.sh measure and
reboot: if every core reports OK without sync mode, you can uninstall
tscsync.
Whether it works or not, please open an issue with your model, BIOS version,
CPU and the output of tscsync-status. That builds the list in
docs/HARDWARE.md and shows vendors how widespread the bug
is.
docs/report/tsc-firmware-report.pdf covers 80 boots of boot history from the Legion Pro 5. It explains why this BIOS leaves the TSCs out of sync when the hardware can keep them in sync, and makes the case for open firmware with safe, vendor-signed updates. The data and scripts behind it are in docs/report/.
MIT, see LICENSE. Firmware vendors are welcome to use the approach
or the code in their own firmware, which is where this should be fixed. The
report in docs/report/ is under CC BY 4.0.
gnu-efi, downloaded at build time, is under its own BSD-style license.
C
57.1%
Shell
38.8%
Makefile
3.8%