Stop BD PROCHOT from pinning your Intel CPU at ~800 MHz — at boot and after every resume from suspend.
BD PROCHOT (Bi-Directional PROCHOT) is an Intel feature that lets external chips (VRMs, chipset, thermal sensors) signal the CPU to throttle hard, even when the CPU itself is cool. On some laptops a dying battery or a misreporting sensor can pin the cores at 800 MHz indefinitely. This installer clears bit 0 of MSR 0x1FC so the CPU ignores those external signals, and it keeps clearing it after every sleep state.
- Who this is for
- What's new
- Prerequisites
- Install
- Verify it worked
- How it works
- Troubleshooting
- Uninstall
- Cautions
- License
Intel laptops (Sandy Bridge and newer) stuck at a low frequency because of a broken sensor or an overzealous EC, on:
- Arch Linux
- Ubuntu, Debian and derivatives
- Fedora, CentOS, RHEL
- Bazzite, Silverblue, Kinoite and other rpm-ostree immutable distros
AMD CPUs don't expose MSR 0x1FC the same way, so this project is Intel-only.
- Works on immutable distros. All unit files now live under
/etc/systemd/system/(writable everywhere) instead of/usr/lib/systemd/system-sleep/(read-only on Bazzite / Silverblue / Kinoite). Fixes #1. - Every sleep state is covered. Resume runs after
suspend,hibernate,hybrid-sleep, andsuspend-then-hibernate. - Correct MSR bit clearing. The worker reads
MSR 0x1FC, clears bit 0 with a bitmask, and writes the result back. No hardcoded hex that could be wrong on some CPUs. msrmodule is persisted via/etc/modules-load.d/msr.conf, so the service has everything it needs at early boot.- Writes are verified. The worker re-reads
MSR 0x1FCafter each write. If bit 0 reads back set — as it does on Dell laptops where the EC re-asserts BD PROCHOT — it logs that to the journal instead of reporting a silent success. Addresses #5. - Kernel lockdown detection. The installer checks
/sys/kernel/security/lockdownup front and offers an interactivemokutil --disable-validationpath when Secure Boot is blocking MSR writes. The worker logs the exact failure to the journal so kernel lockdown stops being a silent failure. Addresses #4.
msr-tools. The installer can fetch it for you; pick the right option when prompted.
curl -LO https://raw.githubusercontent.com/fralapo/Disable-BD-PROCHOT-on-LINUX/main/Disable_BD_PROCHOT
sudo bash Disable_BD_PROCHOTThe installer asks which package manager to use, then writes:
| Path | Purpose |
|---|---|
/usr/local/bin/disable_bd_prochot.sh |
worker that clears bit 0 of MSR 0x1FC |
/etc/systemd/system/disable_bd_prochot.service |
runs the worker at boot |
/etc/systemd/system/disable_bd_prochot-resume.service |
runs the worker on every resume |
/etc/modules-load.d/msr.conf |
loads the msr kernel module at boot |
On Bazzite and other rpm-ostree systems, msr-tools is layered into the next deployment; a reboot is required before the services can run. The installer warns you when this is the case.
Manual install (without running the installer)
-
Install
msr-toolswith your package manager. -
Persist the
msrmodule:echo msr | sudo tee /etc/modules-load.d/msr.conf. -
Create
/usr/local/bin/disable_bd_prochot.sh(chmod 0755):#!/bin/bash modprobe msr 2>/dev/null || true shopt -s nullglob cpus=(/dev/cpu/[0-9]*) [ ${#cpus[@]} -eq 0 ] && { logger -t disable_bd_prochot "no /dev/cpu/*/msr"; exit 1; } fail=0 for cpu in "${cpus[@]}"; do cpu_id="${cpu##*/cpu/}"; cpu_id="${cpu_id%%/*}" cur=$(rdmsr -p "$cpu_id" 0x1FC 2>/dev/null) || { logger -t disable_bd_prochot "rdmsr failed on cpu $cpu_id"; fail=1; continue; } (( 16#$cur & 1 )) || continue # bit 0 already clear new=$(( 16#$cur & ~1 )) wrmsr -p "$cpu_id" 0x1FC "$(printf '0x%x' "$new")" 2>/dev/null || { logger -t disable_bd_prochot "wrmsr failed on cpu $cpu_id (kernel lockdown?)"; fail=1; continue; } chk=$(rdmsr -p "$cpu_id" 0x1FC 2>/dev/null) (( 16#$chk & 1 )) && { logger -t disable_bd_prochot "wrmsr on cpu $cpu_id did not persist (firmware/EC re-asserting)"; fail=1; } done exit $fail
-
Create
/etc/systemd/system/disable_bd_prochot.service:[Unit] Description=Disable BD PROCHOT at boot After=multi-user.target ConditionPathExists=/usr/local/bin/disable_bd_prochot.sh [Service] Type=oneshot ExecStart=/usr/local/bin/disable_bd_prochot.sh RemainAfterExit=yes [Install] WantedBy=multi-user.target
-
Create
/etc/systemd/system/disable_bd_prochot-resume.service:[Unit] Description=Disable BD PROCHOT on resume from suspend/hibernate After=suspend.target hibernate.target hybrid-sleep.target suspend-then-hibernate.target ConditionPathExists=/usr/local/bin/disable_bd_prochot.sh [Service] Type=oneshot ExecStart=/usr/local/bin/disable_bd_prochot.sh [Install] WantedBy=suspend.target hibernate.target hybrid-sleep.target suspend-then-hibernate.target
-
Enable both:
sudo systemctl daemon-reload sudo systemctl enable --now disable_bd_prochot.service sudo systemctl enable disable_bd_prochot-resume.service
sudo rdmsr -a 0x1FCLook at the last hex digit of every line:
- even (
0 2 4 6 8 a c e) → BD PROCHOT is disabled - odd (
1 3 5 7 9 b d f) → still active
Quick example:
2c005c # last digit 'c', even → disabled
2c005d # last digit 'd', odd → active
Check the cores aren't pinned low:
watch -n 1 'grep MHz /proc/cpuinfo'You should see frequencies moving freely up to the turbo ceiling, not stuck near 800 MHz.
Two systemd oneshots share one worker script.
At boot, disable_bd_prochot.service runs after multi-user.target. After every sleep state, disable_bd_prochot-resume.service runs the same worker. The resume unit is wired to suspend.target, hibernate.target, hybrid-sleep.target, and suspend-then-hibernate.target with both After= and WantedBy=. That combination is what the Arch wiki Power management page documents as the reliable pattern for "run on resume".
The worker itself:
for cpu in /dev/cpu/[0-9]*; do
cpu_id="${cpu##*/cpu/}"
cpu_id="${cpu_id%%/*}"
current=$(rdmsr -p "$cpu_id" 0x1FC)
new=$(( 16#$current & ~1 )) # clear bit 0
wrmsr -p "$cpu_id" 0x1FC "$(printf '0x%x' "$new")"
doneMSR 0x1FC is MSR_POWER_CTL on Intel. Bit 0 is the BD PROCHOT enable bit: 1 means the CPU reacts to external PROCHOT assertions, 0 means it ignores them. The worker reads the current value, clears bit 0 with a bitmask, and writes the result back, so it stays correct regardless of what the other bits happen to be on your CPU.
Check both services are enabled:
systemctl is-enabled disable_bd_prochot.service
systemctl is-enabled disable_bd_prochot-resume.serviceThe resume unit shows inactive (dead) between resumes. That is expected for a oneshot. What matters is is-enabled returning enabled.
Check the last run:
journalctl -u disable_bd_prochot-resume.service -b -1msr-tools is not installed. Re-run the installer or install it manually for your distro.
Some stripped kernels omit the MSR module. Confirm with grep CONFIG_X86_MSR /boot/config-$(uname -r) — you want =y or =m. If it is missing, you need a different kernel.
Kernel lockdown is blocking the MSR write. On distro kernels with Secure Boot enabled (Ubuntu, Linux Mint, Debian, Fedora) the lockdown LSM auto-engages in integrity mode and refuses wrmsr even as root.
Diagnose:
cat /sys/kernel/security/lockdown
# [none] integrity confidentiality → not blocked
# none [integrity] confidentiality → blocked, wrmsr will failPick one fix:
A — Disable Secure Boot (cleanest, recommended).
Reboot, enter UEFI firmware, set Secure Boot to Disabled, save and reboot. Re-run the installer. lockdown will be [none]. The installer's pre-check detects this automatically.
B — mokutil --disable-validation (no firmware access required).
The installer offers this interactively when it detects active lockdown. Manual equivalent:
sudo apt install mokutil # or dnf / pacman
sudo mokutil --disable-validation
# pick an 8–16 char one-time password, reboot,
# at the blue MOK Management screen choose:
# Change Secure Boot state → enter the same password → rebootShim will then boot in insecure mode; the kernel sees Secure Boot as disabled and never engages lockdown. Reversible with sudo mokutil --enable-validation. Security impact is equivalent to disabling Secure Boot at the firmware level.
C — Remove lockdown from the LSM cmdline (keeps firmware Secure Boot on).
Works on some Debian / Ubuntu builds, not all — newer distro kernels re-trigger lockdown from the Secure Boot path even when lockdown is missing from lsm=. Try it; if /sys/kernel/security/lockdown still shows [integrity] after reboot, fall back to A or B.
cat /sys/kernel/security/lsm
# e.g. capability,lockdown,landlock,yama,...Edit /etc/default/grub, append the LSM list without lockdown to GRUB_CMDLINE_LINUX_DEFAULT:
GRUB_CMDLINE_LINUX_DEFAULT="... lsm=capability,landlock,yama,..."
Then:
sudo update-grub # Debian/Ubuntu/Mint
# or: sudo grub2-mkconfig -o /boot/grub2/grub.cfg # Fedora
sudo reboot
cat /sys/kernel/security/lockdown # should show [none]
lockdown=noneon its own is not enough when Secure Boot is enabled: the kernel ignores it by design.
After any of A/B/C, the installed services start working from the next boot or resume. Tail the journal to confirm:
journalctl -t disable_bd_prochot -bwrmsr returns no error, yet rdmsr 0x1FC still shows the old value with bit 0 set:
$ sudo wrmsr -a 0x1FC 0xec005a
$ sudo rdmsr 0x1FC
ec005b # bit 0 back to 1
If cat /sys/kernel/security/lockdown reads [none] and /sys/module/msr/parameters/allow_writes is on, this is not a permissions problem. Something outside Linux is putting the bit back.
On most Dell laptops, the Latitude line especially, the embedded controller and SMM rewrite MSR_POWER_CTL to its default within milliseconds. A one-shot write never holds, which is also why the boot-and-resume approach this project uses is not enough on those machines: by the time the next instruction runs, the EC has already reverted it.
There is a second catch. On 11th and 12th gen Latitudes the cap you actually hit, say 1.8 GHz on the P-cores and 1.3 GHz on the E-cores once the package reaches ~60 °C, usually is not BD PROCHOT at all. It is a firmware power limit applied through MMIO (MCHBAR) together with Intel DPTF and thermald. Clearing bit 0 of 0x1FC does nothing about those, so the cores stay pinned even when the write does hold.
Work out which one you have:
sudo wrmsr -a 0x1FC 0xec005a
for i in $(seq 5); do sudo rdmsr 0x1FC; sleep 1; done- Flips back to an odd last digit within a second or two: the EC/SMM is re-asserting it.
- Stays even but the cores are still capped: the throttle is the power limit / DPTF, not BD PROCHOT.
For either case the one-shot model here is the wrong tool. What does work on these machines:
- erpalma/throttled runs as a daemon and re-applies the MSR and the MMIO power limits every few seconds, so it out-paces the EC. Best fit for Dell Latitudes.
- For the DPTF cap specifically, mask
thermald(or drop its--adaptiveflag) and try blacklistingintel_rapl_msr. See intel/thermal_daemon#341 and the Dell community thread. - Update to the latest BIOS, and check the laptop isn't throttling because it thinks the charger is non-Dell.
Since the worker now verifies each write, this case lands in the journal (bit 0 still set ... firmware/EC likely re-asserting), so journalctl -t disable_bd_prochot -b tells you when a write didn't hold instead of reporting a silent success.
Older versions of the installer wrote a sleep hook to /usr/lib/systemd/system-sleep/disable_bd_prochot. The current installer cleans that up automatically. If anything odd persists, run Uninstall_BD_PROCHOT and reinstall.
curl -LO https://raw.githubusercontent.com/fralapo/Disable-BD-PROCHOT-on-LINUX/main/Uninstall_BD_PROCHOT
sudo bash Uninstall_BD_PROCHOTThis removes every file listed in the install table, plus the legacy sleep hook if it still exists. BD PROCHOT is re-enabled at the next reboot.
Disabling BD PROCHOT removes one layer of thermal defense. If the external signal was real (actual overheating rather than a bad sensor), you now have no hardware-level brake. Keep an eye on temperatures:
watch -n 1 sensorsIf the fans can't keep up, reverse the change by running Uninstall_BD_PROCHOT. This project targets the specific case where the throttle trigger is a faulty external signal, not a CPU that is actually overheating.
MIT. See LICENSE.
- ThrottleStop for the Windows-side prior art.
- Intel MSR documentation for
MSR_POWER_CTL(MSR 0x1FC). - Contributors who reported the post-suspend issue on immutable distros in #1.