Skip to content

fralapo/Disable-BD-PROCHOT-on-LINUX

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

54 Commits
 
 
 
 
 
 
 
 

Repository files navigation

Disable BD PROCHOT on Linux

Stop BD PROCHOT from pinning your Intel CPU at ~800 MHz — at boot and after every resume from suspend.

License: MIT Issues Stars

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.

Table of Contents

Who this is for

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.

What's new (April 2026)

  • 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, and suspend-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.
  • msr module 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 0x1FC after 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/lockdown up front and offers an interactive mokutil --disable-validation path 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.

Prerequisites

msr-tools. The installer can fetch it for you; pick the right option when prompted.

Install

curl -LO https://raw.githubusercontent.com/fralapo/Disable-BD-PROCHOT-on-LINUX/main/Disable_BD_PROCHOT
sudo bash Disable_BD_PROCHOT

The 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)
  1. Install msr-tools with your package manager.

  2. Persist the msr module: echo msr | sudo tee /etc/modules-load.d/msr.conf.

  3. 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
  4. 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
  5. 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
  6. Enable both:

    sudo systemctl daemon-reload
    sudo systemctl enable --now disable_bd_prochot.service
    sudo systemctl enable disable_bd_prochot-resume.service

back to top

Verify it worked

sudo rdmsr -a 0x1FC

Look 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.

How it works

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")"
done

MSR 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.

back to top

Troubleshooting

CPU still stuck at ~800 MHz after resume

Check both services are enabled:

systemctl is-enabled disable_bd_prochot.service
systemctl is-enabled disable_bd_prochot-resume.service

The 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 -1

rdmsr: unknown command

msr-tools is not installed. Re-run the installer or install it manually for your distro.

modprobe: FATAL: Module msr not found

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.

wrmsr: pwrite: Operation not permitted

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 fail

Pick 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 → reboot

Shim 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=none on 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 -b

Write succeeds but the value reads back unchanged

wrmsr 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 --adaptive flag) and try blacklisting intel_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.

Legacy installation leftover

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.

back to top

Uninstall

curl -LO https://raw.githubusercontent.com/fralapo/Disable-BD-PROCHOT-on-LINUX/main/Uninstall_BD_PROCHOT
sudo bash Uninstall_BD_PROCHOT

This 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.

Cautions

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 sensors

If 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.

License

MIT. See LICENSE.

Acknowledgments

  • 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.

back to top

About

If your Intel laptop is stuck near 800 MHz because BD PROCHOT is misfiring, this installs a fix that re-clears the MSR bit at boot and after every resume. Works on regular distros and on immutable ones (Bazzite, Silverblue, Kinoite).

Topics

Resources

License

Stars

58 stars

Watchers

1 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages