Skip to content

CLI Usage

Le Khanh Binh edited this page Jul 2, 2026 · 1 revision

CLI Usage

zenmaster [OPTIONS] [TUNING ARGS...]

Tuning arguments use the same --name=value form as RyzenAdj. Options and tuning arguments can be combined in one call. SMU writes require root (Linux, macOS) or Administrator (Windows); the read-only options below do not.

Options

Option Root needed Description
--help, -h No Show the tuning arguments your CPU supports, then exit
--info No* Detected CPU name, family, type, socket, active backend, and driver status
--table Yes Live PM table with labeled values (APU / mobile)
--sensors Yes Key live sensors only: temp, load, power, clocks (compact; structured under --json)
--dump-table Yes Raw PM table floats with hex offsets
--json none Machine-readable JSON output (modifier for the others)
--reapply=N Yes Re-apply the tuning args every N seconds (foreground loop)
--version No Show the installed version and check PyPI for a newer release
--iopci Yes macOS only: force the kext-free IOPCIBridge path (tuning only, no PM table)

* --info works without root, but only shows the Backend:/Driver: lines at all if it can initialize the SMU (which needs root). Without privileges, those two lines are simply left out rather than shown as some placeholder.

Running zenmaster with no arguments prints the same dynamic help as --help.


Tuning arguments

Tuning arguments are passed straight through, e.g.:

sudo zenmaster --stapm-limit=15000 --fast-limit=20000 --slow-limit=18000 --tctl-temp=90
  • Value arguments take =value (integers: milliwatts, milliamps, MHz, °C, and so on). See Tuning Arguments for every argument and its unit.
  • Flag arguments take no value and are passed bare: --enable-oc, --power-saving, --max-performance, and all --get-* queries. A value passed to a flag is ignored (matching RyzenAdj).
  • Negative values (e.g. Curve Optimizer --set-coall=-20) wrap to unsigned 32-bit, exactly like RyzenAdj.

If you pass an argument your CPU does not support, or a value argument without =value, ZenMaster shows the help screen instead of running.

Apply output

$ sudo zenmaster --stapm-limit=15000 --tctl-temp=90
stapm-limit [MP1 0x14] = 15000 -> OK
tctl-temp [MP1 0x19] = 90 -> OK

Each line shows the argument, the mailbox and opcode used (MP1 or RSMU), the value sent, and the SMU status. Some arguments map to more than one mailbox/opcode; ZenMaster tries each and prints a line per attempt.

Query (get-*) output

get-* arguments read a value back instead of writing:

$ sudo zenmaster --get-pbo-scalar
get-pbo-scalar [RSMU 0x6D] -> OK = 42 (0x0000002A)

Exit codes

Code Meaning
0 All tuning args applied successfully
1 At least one argument was rejected, or a fatal error (no root, backend unavailable)

Dynamic --help

--help detects your CPU and lists only the arguments its family supports, grouped by category, with units and descriptions:

$ zenmaster --help

ZenMaster — Ryzen Power Management Tool

Usage: zenmaster [OPTIONS] [TUNING ARGS...]

Options:
  --info           Show CPU and backend info
  --json           Machine-readable JSON output
  --reapply=N      Re-apply settings every N seconds (foreground)
  --version        Show version and check PyPI for a newer release
  --table          Show labeled power metrics table
  --dump-table     Dump raw PM table floats with hex offsets

Tuning arguments for AMD Ryzen 9 7950X (Raphael, AM5_V1):

  Power limits:
    --stapm-limit=<mW>                 Sustained Power Limit — STAPM LIMIT
    --fast-limit=<mW>                  Actual Power Limit — PPT LIMIT FAST
    --slow-limit=<mW>                  Average Power Limit — PPT LIMIT SLOW
    ...

WARNING: Use at your own risk!
Version: 0.6.0 by HorizonUnix under GPL-3.0 License

The --table / --dump-table lines only appear if your family has a PM table. On macOS, an extra --iopci line appears too, forcing the kext-free IOPCIBridge path (see Installation).


Reapply loop

Some firmware and vendor software periodically reset SMU limits. --reapply=N re-applies your tuning args every N seconds in the foreground:

sudo zenmaster --stapm-limit=15000 --reapply=30

It runs until you stop it with Ctrl-C. Minimum interval is 1 second. For a background service, wrap this in a systemd unit (Linux), a scheduled task (Windows), or a launchd agent (macOS).


--info

$ sudo zenmaster --info
Name   : AMD Ryzen 9 7950X
Family : Raphael  (Zen 3 - Zen 4)
Type   : Amd_Desktop_Cpu
Socket : AM5_V1
Backend: pci
Driver : PCI direct access

The Driver line reports the SMU driver backing the active backend: PCI direct access on Secure-Boot-off Linux, otherwise the module and its status, for example ryzen_smu 0.1.7 (OK), ryzen_smu 0.1.5 — too old (need 0.1.7) on Linux, PawnIO 3.0 (OK) on Windows, or DirectHW loaded (OK)/IOKit PCI loaded (OK) on macOS.


--sensors

A compact live readout of the key sensors (fields with no value on your family are omitted):

$ sudo zenmaster --sensors
CPU Temp    :     64.0 °C
CPU Load    :     38.5 %
Socket Power:     41.2 W
iGPU Clock  :   2400.0 MHz
Mem Clock   :   2400.0 MHz

With --json it emits the full structured sensor object, the same shape monitoring tools consume, so there's no label parsing:

sudo zenmaster --sensors --json
{
  "stapm_limit": null,
  "stapm_value": 30.1,
  "tctl_temp": 64.0,
  "cclk_busy": 38.5,
  "socket_power": 41.2,
  "gfx_clk": 2400.0,
  "gfx_temp": 58.0,
  "mem_clk": 2400.0
}

Use --table instead when you want the full RyzenAdj-style limit/value table; --sensors is the focused monitoring view.


JSON output

Add --json to make any output machine-readable. This is the supported way to drive ZenMaster from non-Python apps.

zenmaster --info --json
sudo zenmaster --stapm-limit=15000 --tctl-temp=90 --json
sudo zenmaster --table --json
zenmaster --version --json

Apply, with --json:

{
  "cpu": "AMD Ryzen 9 7950X",
  "family": "Raphael",
  "socket": "AM5_V1",
  "backend": "pci",
  "results": [
    {
      "arg": "stapm-limit",
      "value": 15000,
      "mailbox": "MP1",
      "opcode": "0x14",
      "status": "OK",
      "returned": null
    }
  ],
  "rejected": false
}

For a get-* query, returned holds the value read back. On error, status carries the error message and opcode is empty.


Examples

# Detect only (no root needed)
zenmaster --info

# Apply a mobile power preset
sudo zenmaster --stapm-limit=15000 --fast-limit=25000 --slow-limit=20000 --tctl-temp=95

# Undervolt the cores by 20 (all-core Curve Optimizer)
sudo zenmaster --set-coall=-20

# Hold a preset against vendor software that resets it
sudo zenmaster --stapm-limit=15000 --reapply=15

# Read the live sensor table
sudo zenmaster --table

# Scriptable: get current PBO scalar as JSON
sudo zenmaster --get-pbo-scalar --json

Clone this wiki locally