Thermal and power profiles for selected HP EliteBook and ProBook AMD Ryzen laptops.
The profile values are field-tested on an HP EliteBook 845 G8 with the AMD Ryzen 7 PRO 5850U. Other HP Cezanne models are accepted by the hardware guard only when they match the validated CPU family listed below. Rembrandt models are recognized but intentionally blocked by default until their SMU behavior is validated.
The goal is not to hard-cap the CPU. The profiles keep boost enabled and leave the maximum CPU frequency uncapped for normal work, then tune the sustained SMU power limits and the AMD P-State energy preference so the laptop can still burst hard without sitting at the stock 100 C thermal edge during sustained workloads.
Idle efficiency is handled separately by a staged idle overlay. The overlay leaves the selected profile intact, moves to a soft idle hint after a few quiet seconds, and only enters a deeper capped state after sustained quiet. Any real CPU load clears the overlay and restores the active profile.
Tested on:
- HP EliteBook 845 G8
- AMD Ryzen 7 PRO 5850U
- Fedora Linux 44
- GNOME Shell 50
- Linux 6.19 series and 7.0.4-200.fc44.x86_64
This is intentionally hardware-scoped. The main script refuses to run on unrecognized hardware unless ELITEBOOK_THERMAL_FORCE=1 is set.
Fedora status: Fedora 44 x86_64 is the primary supported distribution path.
The public COPR publishes elitebook-thermal-profile,
gnome-shell-extension-elitebook-thermal-profile, and upstream RyzenAdj
v0.17.0. Package installation is intentionally passive: it installs files only
and does not start hardware tuning until you enable the services below.
For Fedora 44 users with a supported HP business Cezanne laptop, the COPR RPM is the lowest-friction install path:
sudo dnf copr enable matteo407/elitebook-thermal-profile
sudo dnf install elitebook-thermal-profileThe same COPR also packages RyzenAdj v0.17.0; with default DNF weak
dependencies, installing elitebook-thermal-profile pulls it in automatically.
If your system disables weak dependencies, install it explicitly:
sudo dnf install elitebook-thermal-profile ryzenadjThe RPM deliberately installs files only; it does not start hardware tuning during package installation. After reviewing the supported hardware table:
sudo systemctl enable --now elitebook-thermal-profile.service
sudo systemctl enable --now elitebook-idle-watcher.service
sudo systemctl enable --now elitebook-steam-game-watcher.service
sudo systemctl enable --now elitebook-power-guard.timer
sudo systemctl start elitebook-power-guard.serviceIf ryzenadj is unavailable on another distribution, the source installer can
build a pinned, SHA256-verified RyzenAdj release with --build-ryzenadj.
Without RyzenAdj, the system degrades to sysfs controls for EPP, boost, and CPU
frequency where the kernel exposes them. See Supported Hardware
before enabling services on any machine. Other distributions are not packaged
yet; see Related Work and the roadmap.
Validated by default:
| DMI product family | CPU family | Status |
|---|---|---|
| HP EliteBook 835 G7/G8/G9 | Ryzen 5/7 PRO 5650U, 5750U, 5850U | Cezanne profiles enabled |
| HP EliteBook 845 G7/G8/G9 | Ryzen 5/7 PRO 5650U, 5750U, 5850U | Cezanne profiles enabled |
| HP ProBook 445 G8/G9 | Ryzen 5/7 PRO 5650U, 5750U, 5850U | Cezanne profiles enabled |
| HP ProBook 455 G8/G9 | Ryzen 5/7 PRO 5650U, 5750U, 5850U | Cezanne profiles enabled |
Known but not enabled by default:
| DMI product family | CPU family | Status |
|---|---|---|
| HP EliteBook 835/845 G9, HP ProBook 445/455 G9 | Ryzen 5/7 PRO 6650U, 6850U | Rembrandt detected, requires ELITEBOOK_THERMAL_FORCE=1 after manual review |
The project does not change SMU numbers per model yet. Every enabled model uses the same conservative Cezanne limits shown in the profiles table.
If you run this on a supported HP business Cezanne variant other than the 845 G8 (for example 835 G7/G8/G9, 845 G7/G9, or ProBook 445/455 G8/G9) and it behaves correctly, please open a Hardware Report. Confirmed reports help grow the validated whitelist with real-world data instead of one machine.
| Profile | Use case | Boost | CPU max freq | EPP | Fast limit | Sustained CPU/APU | Tctl |
|---|---|---|---|---|---|---|---|
ac |
Plugged in daily work | on | uncapped | balance_power |
30 W | 18 W | 90 C |
performance |
Manual plugged-in performance | on | uncapped | balance_performance |
30 W | 18 W | 90 C |
battery |
Unplugged work | on | uncapped | balance_power |
30 W | 15 W | 88 C |
battery-saver |
Automatic low-battery guard | off | 1.8 GHz | power |
15 W | 8 W | 80 C |
gaming |
Steam game process detected | on | uncapped | balance_performance |
30 W | 23 W | 92 C |
cool |
Quiet/cool fallback | on | uncapped | power |
22 W | 12 W | 85 C |
There is no stock profile. On the tested unit, the stock firmware/SMU behavior allowed sustained 100-102 C operation under real development workloads, which was exactly what this project was built to avoid.
elitebook-thermal-profile: appliesauto,ac,performance,battery,battery-saver,gaming, orcool- systemd oneshot service: reapplies
autoat boot - udev rules: reapply
autowhen AC power or battery state changes - system sleep hook: reapplies
autoafter resume - idle overlay watcher: applies soft/deep idle hints with near-zero polling overhead
- Steam game watcher: switches to
gamingonly while a real Steam game process is detected - update guard timer: remasks conflicting GNOME power profile backends, keeps the custom units enabled, and reapplies
autoafter package/kernel changes without pinning updates - optional btrfs swapfile hibernate preflight (
--with-hibernate-preflight): validates swap layout, resume offset, kernel lockdown, and SELinux state before systemd enters hibernate - optional GNOME Shell panel indicator: three human actions (
Auto,Game,Quiet) while automaticac,battery,battery-saver, and idle states remain status-only
Battery capacity changes also emit power_supply udev events, so on battery the dispatcher re-runs roughly once per battery percent. This is intentional: it is what detects the low-battery threshold crossing without a polling daemon, and the periodic reapply heals SMU limits that firmware events may have reset. Each run is an idempotent oneshot serialized by a dispatcher lock.
The idle watcher is intentionally cheap. It samples aggregate /proc/stat and /proc/loadavg once per second, does not scan processes, preserves stricter base frequency caps such as battery-saver, and calls RyzenAdj only when entering or leaving deep idle. The Steam watcher scans /proc only on long intervals and is constrained with low scheduling priority, CPUQuota=5%, and MemoryMax=64M.
- Linux with systemd and udev
- AMD P-State or cpufreq sysfs support
ryzenadjinstalled as/usr/local/sbin/ryzenadj,/usr/local/bin/ryzenadj, or/usr/bin/ryzenadjfor full SMU power-limit controltunedrecommended on Fedorapkexeconly if using the GNOME Shell switcherflockfrom util-linux, normally installed by default on Fedora- Kernel lockdown set to
nonefor full RyzenAdj SMU control through/dev/mem
Secure Boot commonly enables kernel lockdown. When lockdown is active, RyzenAdj may not be able to write SMU limits through /dev/mem; the installer warns and asks for confirmation before continuing. EPP, CPU max frequency, and boost sysfs controls still degrade cleanly.
This repository does not vendor RyzenAdj. Fedora users can install RyzenAdj from the same COPR:
sudo dnf copr enable matteo407/elitebook-thermal-profile
sudo dnf install ryzenadjThe COPR package builds upstream FlyGoat RyzenAdj v0.17.0 from source and
verifies the same SHA256 used by the source installer. Users on other
distributions can install RyzenAdj from their package source, upstream, or let
the Fedora/source installer build the pinned release:
sudo ./scripts/install-fedora.sh --build-ryzenadjThe source-build path pins RyzenAdj v0.17.0 at commit 67aa960e71bf4cdd140b47d42c0c62c4cded68d1 and verifies SHA256 848ac9d86ff65d30f5e2c8600aac2613f0f10003b0d6f0e516a54761d7345d44 before compiling.
When RyzenAdj works, profiles apply both kernel-level policy and SMU limits: EPP, boost, CPU max frequency, sustained package power, STAPM/fast/slow limits, and thermal targets. When RyzenAdj is missing or blocked by kernel lockdown, the runtime fallback still applies the kernel-level controls: EPP, boost, and CPU max frequency. That fallback is intentionally conservative; it is not a full replacement for SMU tuning.
One SMU value is special: on platforms with Skin Temperature Tracking (STT) enabled (the EliteBook 845 G8 itself included), the firmware owns the STAPM limit and rewrites it within about a second of any write, so the profiles' STAPM stage only takes effect where STT is disabled. Sustained power is governed by the slow limit either way. See docs/troubleshooting.md for the details.
sudo dnf copr enable matteo407/elitebook-thermal-profile
sudo dnf install elitebook-thermal-profileIf DNF weak dependencies are disabled:
sudo dnf install elitebook-thermal-profile ryzenadjOptional GNOME Shell indicator:
sudo dnf install gnome-shell-extension-elitebook-thermal-profile
gnome-extensions enable elitebook-thermal-profile@matteopasseri.github.ioAfter installing the RPM, enable the runtime services explicitly:
sudo systemctl enable --now elitebook-thermal-profile.service
sudo systemctl enable --now elitebook-idle-watcher.service
sudo systemctl enable --now elitebook-steam-game-watcher.service
sudo systemctl enable --now elitebook-power-guard.timer
sudo systemctl start elitebook-power-guard.servicesudo dnf install tuned python3 polkit
git clone https://github.com/matteopasseri407/hp-elitebook-845-g8-ryzen-tuning.git
cd hp-elitebook-845-g8-ryzen-tuning
sudo ./scripts/install-fedora.shThe installer checks for python3, tuned-adm and RyzenAdj, then
prints an explicit dnf install hint if anything is missing. pkexec
is required only when installing the optional GNOME Shell indicator.
To skip the Steam game watcher:
sudo ./scripts/install-fedora.sh --without-steam-watcherTo skip the idle overlay watcher:
sudo ./scripts/install-fedora.sh --without-idle-watcherTo skip the update guard timer:
sudo ./scripts/install-fedora.sh --without-power-guardTo install on a different laptop after reviewing the profile values:
sudo ./scripts/install-fedora.sh --forceTo build a pinned RyzenAdj from source when no ryzenadj binary is installed:
sudo dnf install cmake gcc-c++ make pciutils-devel curl tar
sudo ./scripts/install-fedora.sh --build-ryzenadjOptional btrfs swapfile hibernate preflight:
sudo ./scripts/install-fedora.sh --with-hibernate-preflightSee the Hibernate Preflight section below for the required one-time configuration.
Optional GNOME Shell indicator:
sudo ./scripts/install-fedora.sh --with-gnome-extensionAfter installing the GNOME extension, enable it from the Extensions app or with:
gnome-extensions enable elitebook-thermal-profile@matteopasseri.github.ioOn Wayland, a logout/login may be needed after installing a local extension.
The GNOME indicator intentionally exposes only Auto, Game, and Quiet. performance, battery, and battery-saver remain technical profiles available to automation or the CLI, not primary daily UI choices.
sudo elitebook-thermal-profile auto
sudo elitebook-thermal-profile ac
sudo elitebook-thermal-profile performance
sudo elitebook-thermal-profile battery
sudo elitebook-thermal-profile battery-saver
sudo elitebook-thermal-profile gaming
sudo elitebook-thermal-profile coolauto maps to ac when plugged in, battery when unplugged, and battery-saver when unplugged at or below the low-battery threshold. The default threshold is 20% and can be changed with ELITEBOOK_LOW_BATTERY_THRESHOLD.
Manual profiles intentionally win over system automation. Running sudo elitebook-thermal-profile auto clears the manual override and returns control to AC/battery automation. The Steam watcher also respects manual overrides.
Low-battery protection is the exception: when the systemd/udev automation sees the battery at or below the threshold, it may override an active manual or Steam profile and apply battery-saver. This protects unattended systems from continuing a high-power workload until firmware cutoff.
Current state is exposed at:
cat /run/elitebook-thermal-profile/current
cat /run/elitebook-thermal-profile/idle-watcher
cat /run/elitebook-thermal-profile/guardelitebook-power-guard.timer runs after boot and then periodically. It is deliberately conservative: it does not versionlock Fedora packages, pin kernels, or block upgrades. Instead it repairs the invariants this project owns:
tuned-ppd.serviceandpower-profiles-daemon.servicestay masked so GNOME's stock Power Mode backend cannot take over EPP policyelitebook-thermal-profile, idle watcher, and Steam watcher stay enabled- udev power-supply rules are reloaded
- hardware, RyzenAdj presence, and
amd-pstate-eppsysfs shape are checked autois reapplied withELITEBOOK_PROFILE_SOURCE=system-auto, so manual overrides are preserved except for the low-batterybattery-saverguard
If the full RyzenAdj profile cannot be applied after an update, the guard writes /run/elitebook-thermal-profile/fallback and applies a direct sysfs fallback: conservative EPP/frequency on battery, or a moderate 3.0 GHz cap on AC. That fallback is meant to preserve thermals and battery until the RyzenAdj/kernel issue is fixed, not as a normal operating mode.
Hibernating to a btrfs swapfile is fragile by default: a regenerated swapfile
silently changes its physical offset, SELinux relabeling can break swap
activation, and kernel lockdown blocks the resume path. A stale
resume_offset produces a machine that fails to resume or, worse, resumes
from a corrupted image.
elitebook-hibernate-preflight fails closed instead. It runs as
ExecStartPre= of systemd-hibernate.service and
systemd-suspend-then-hibernate.service and aborts the hibernate unless
everything matches the configuration in /etc/elitebook-hibernate.conf:
- the configured swapfile is active swap and large enough for the image plus 2 GiB
- its SELinux type is
swapfile_twhen SELinux is enforcing - its current btrfs physical offset still matches the configured
RESUME_OFFSET - kernel lockdown is
none - both the default boot entry and the running kernel carry the matching
resume=UUID=andresume_offset=arguments
On success it programs /sys/power/resume, /sys/power/resume_offset, and
/sys/power/image_size before systemd continues.
One-time setup after --with-hibernate-preflight:
sudoedit /etc/elitebook-hibernate.conf # fill in the values for your machine
sudo elitebook-hibernate-preflight check-configThe configuration template documents how to derive each value
(findmnt -no UUID -T /swap, btrfs inspect-internal map-swapfile -r ...).
The preflight intentionally targets Fedora-style setups: it uses grubby to
inspect the default boot entry and btrfs-progs for the swapfile offset.
sudo ./scripts/install-fedora.sh --uninstallThe compatibility wrapper still works:
sudo ./scripts/uninstall.shTo keep a RyzenAdj binary installed by --build-ryzenadj:
sudo ./scripts/install-fedora.sh --uninstall --keep-ryzenadjTo remove the optional GNOME extension as well:
sudo ./scripts/install-fedora.sh --uninstall --gnome-extensionUninstall stops and disables the elitebook-* units, removes installed binaries, removes the udev rule and sleep hook, unmasks Fedora's stock power profile services, and restores tuned-adm profile balanced.
This project does not invent the category of Linux Ryzen power tuning. It builds on existing tools and adds the integration layers needed to run them as a daily driver on an HP business Cezanne laptop.
Prior art and dependencies:
- RyzenAdj by FlyGoat. The CLI used to write SMU power limits on AMD Ryzen mobile parts. Pinned, SHA256-verified, and optionally built from source by the installer. Not vendored, not forked. Every SMU limit applied by the profiles in this repository goes through RyzenAdj.
ryzen-ppdby xsmile. A Python daemon that switches RyzenAdj profiles on AC/battery transitions. This repository covers the same need with a systemd/udev approach, plus the additional layers below.ryzenadj-controlby h4us0x. A PyQt6 GUI for sending RyzenAdj commands manually. Complementary scope: this project is service-driven, not a manual cockpit.SimpleDeckyTDP. A Decky plugin focused on the Steam Deck. Same SMU mechanism, different ecosystem and target hardware.auto-power-profile. Switchespower-profiles-daemonprofiles based on CPU usage. Operates one layer above SMU; this project's update guard intentionally remaskspower-profiles-daemonso the two do not fight over EPP policy.
What this repository adds on top of those building blocks, integrated for HP business Cezanne laptops as a daily driver:
- udev-driven AC/battery profile switching, not a polling daemon
- a two-stage idle overlay watcher that respects stricter base profiles such as
battery-saver - a Steam game watcher that scans
/procrarely and exits cleanly when no game is running - an update guard timer that reapplies profile invariants after
dnf upgrade, including remaskingtuned-ppdandpower-profiles-daemon - an opt-in hibernate preflight check that validates swap layout, lockdown, and SELinux state before allowing hibernation
- a native GNOME Shell extension exposing only three meaningful daily actions (
Auto,Game,Quiet) - hardened systemd sandboxes with
CapabilityBoundingSet,SystemCallFilter,MemoryDenyWriteExecute,PrivateNetwork,ProtectSystem=strict, and related directives applied to every long-running unit
Framing notes:
- This is userland integration on top of RyzenAdj and AMD P-State. It is not a kernel project and does not modify Ryzen firmware behavior.
- Where another project already owns a layer (RyzenAdj for SMU writes, AMD P-State for EPP, GNOME for power UI), this repository delegates to it.
- Development was AI-assisted using Codex and Claude. Profile values, the hardware whitelist, and runtime behavior were validated on real hardware on Fedora.
This project does:
- Apply local thermal, EPP, boost, frequency, and SMU power-limit policy for supported HP AMD laptops
- Reapply that policy after boot, AC/battery changes, resume, Steam game detection, idle transitions, and Fedora package updates
- Fail closed on unknown hardware unless
ELITEBOOK_THERMAL_FORCE=1is set - Provide an uninstall path that returns Fedora power management to the stock balanced baseline
This project does not:
- Bypass firmware thermal safety limits
- Guarantee support for every Ryzen APU generation or OEM BIOS
- Auto-update itself or download code in the background
- Collect telemetry, analytics, hardware inventory, or usage data
The main trust boundary is root execution on the local machine. Installer and runtime scripts write to systemd, udev, /run, CPU sysfs, and RyzenAdj-accessible SMU controls. Review docs/safety.md and the profile values before forcing unsupported hardware.
This changes Ryzen SMU limits through RyzenAdj and writes CPU policy settings through sysfs. It is provided as a field-tested configuration for one laptop model, not as a universal Ryzen tuning recipe.
Read docs/safety.md before adapting it to other machines.
- Consider renaming the public repository to
cezanne-thermal-profileorhp-amd-thermal-tunerbefore a broader release. The current repository name is accurate for the original test laptop but too narrow for the validated Cezanne hardware table above. - RPM spec cleanup for
/usr/libexecand packaged systemd units - More test reports across BIOS versions
- Optional AUR and
.debinstall paths after Fedora usage reports justify the maintenance cost