zenmaster 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- zenmaster-0.1.0/PKG-INFO +261 -0
- zenmaster-0.1.0/README.md +235 -0
- zenmaster-0.1.0/pyproject.toml +45 -0
- zenmaster-0.1.0/setup.cfg +4 -0
- zenmaster-0.1.0/tests/test_apply.py +62 -0
- zenmaster-0.1.0/tests/test_hardware.py +97 -0
- zenmaster-0.1.0/tests/test_runner.py +63 -0
- zenmaster-0.1.0/zenmaster/__init__.py +5 -0
- zenmaster-0.1.0/zenmaster/__main__.py +3 -0
- zenmaster-0.1.0/zenmaster/apply.py +58 -0
- zenmaster-0.1.0/zenmaster/assets/AMD/PawnIO/AMDFamily0F.bin +0 -0
- zenmaster-0.1.0/zenmaster/assets/AMD/PawnIO/AMDFamily10.bin +0 -0
- zenmaster-0.1.0/zenmaster/assets/AMD/PawnIO/AMDFamily17.bin +0 -0
- zenmaster-0.1.0/zenmaster/assets/AMD/PawnIO/AMDReset.bin +0 -0
- zenmaster-0.1.0/zenmaster/assets/AMD/PawnIO/RyzenSMU.bin +0 -0
- zenmaster-0.1.0/zenmaster/cli.py +397 -0
- zenmaster-0.1.0/zenmaster/hardware.py +145 -0
- zenmaster-0.1.0/zenmaster/linux.py +241 -0
- zenmaster-0.1.0/zenmaster/runner.py +466 -0
- zenmaster-0.1.0/zenmaster/smu.py +66 -0
- zenmaster-0.1.0/zenmaster/table.py +151 -0
- zenmaster-0.1.0/zenmaster/windows.py +320 -0
- zenmaster-0.1.0/zenmaster.egg-info/PKG-INFO +261 -0
- zenmaster-0.1.0/zenmaster.egg-info/SOURCES.txt +26 -0
- zenmaster-0.1.0/zenmaster.egg-info/dependency_links.txt +1 -0
- zenmaster-0.1.0/zenmaster.egg-info/entry_points.txt +2 -0
- zenmaster-0.1.0/zenmaster.egg-info/requires.txt +3 -0
- zenmaster-0.1.0/zenmaster.egg-info/top_level.txt +1 -0
zenmaster-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: zenmaster
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Pure-Python AMD Ryzen SMU power management for Linux and Windows
|
|
5
|
+
License: GPL-3.0-only
|
|
6
|
+
Keywords: amd,ryzen,ryzenadj,smu,power-management,tuning
|
|
7
|
+
Classifier: Development Status :: 4 - Beta
|
|
8
|
+
Classifier: Environment :: Console
|
|
9
|
+
Classifier: Intended Audience :: End Users/Desktop
|
|
10
|
+
Classifier: Intended Audience :: Developers
|
|
11
|
+
Classifier: License :: OSI Approved :: GNU General Public License v3 (GPLv3)
|
|
12
|
+
Classifier: Operating System :: POSIX :: Linux
|
|
13
|
+
Classifier: Operating System :: Microsoft :: Windows
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
20
|
+
Classifier: Topic :: System :: Hardware
|
|
21
|
+
Classifier: Topic :: Utilities
|
|
22
|
+
Requires-Python: >=3.10
|
|
23
|
+
Description-Content-Type: text/markdown
|
|
24
|
+
Provides-Extra: dev
|
|
25
|
+
Requires-Dist: pytest>=7; extra == "dev"
|
|
26
|
+
|
|
27
|
+
# ZenMaster
|
|
28
|
+
|
|
29
|
+
[](https://pypi.org/project/zenmaster/)
|
|
30
|
+
[](https://pypi.org/project/zenmaster/)
|
|
31
|
+
[](https://www.gnu.org/licenses/gpl-3.0)
|
|
32
|
+
[](https://pypi.org/project/zenmaster/)
|
|
33
|
+
|
|
34
|
+
Pure-Python AMD Ryzen power management via SMU. Works on Linux and Windows. No compiler, no build chain, no dependencies.
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
pip install zenmaster
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
---
|
|
41
|
+
|
|
42
|
+
## Features
|
|
43
|
+
|
|
44
|
+
- Set and read power limits, thermal limits, clock ranges, voltages, and PBO/Curve Optimiser offsets
|
|
45
|
+
- Live PM table — labeled power, thermal, and current sensor data
|
|
46
|
+
- Dynamic `--help` that shows only what your CPU supports
|
|
47
|
+
- JSON output for scripting and integration
|
|
48
|
+
- `--reapply=N` to continuously re-apply a tuning preset
|
|
49
|
+
- Embeddable Python library — use it as a backend in your own tuning tools
|
|
50
|
+
- **No WinRing0** — Windows support uses [PawnIO](https://github.com/namazso/PawnIO), a modern signed driver
|
|
51
|
+
|
|
52
|
+
---
|
|
53
|
+
|
|
54
|
+
## Why not RyzenAdj
|
|
55
|
+
|
|
56
|
+
| | RyzenAdj | ZenMaster |
|
|
57
|
+
|---|---|---|
|
|
58
|
+
| Install | Build from source | `pip install zenmaster` |
|
|
59
|
+
| Windows | WinRing0 ⚠️ | PawnIO ✅ |
|
|
60
|
+
| `--help` | Static, shows all args | Dynamic — CPU-specific only |
|
|
61
|
+
| Output | Plain text | Plain text or `--json` |
|
|
62
|
+
| PM table | Raw floats | Labeled fields with units |
|
|
63
|
+
| Embed / script | Shell out to binary | `import zenmaster` |
|
|
64
|
+
| Build deps | cmake, make, libpci | None |
|
|
65
|
+
|
|
66
|
+
### On WinRing0
|
|
67
|
+
|
|
68
|
+
RyzenAdj's Windows backend uses WinRing0, a driver with well-documented security vulnerabilities ([CVE-2020-14979](https://nvd.nist.gov/vuln/detail/CVE-2020-14979), [CVE-2021-41285](https://nvd.nist.gov/vuln/detail/CVE-2021-41285)). It grants any unprivileged process full read/write access to physical memory, PCI config space, and I/O ports. Several AV vendors flag it as malicious outright.
|
|
69
|
+
|
|
70
|
+
ZenMaster uses [PawnIO](https://github.com/namazso/PawnIO) instead — a purpose-built, Microsoft-signed kernel driver that exposes a controlled IOCTL interface. No raw memory access, no known CVEs.
|
|
71
|
+
|
|
72
|
+
---
|
|
73
|
+
|
|
74
|
+
## Installation
|
|
75
|
+
|
|
76
|
+
### Linux
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
pip install zenmaster
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
Requires root and either the `ryzen_smu` kernel module (recommended) or direct PCI access.
|
|
83
|
+
|
|
84
|
+
**Install ryzen_smu module:**
|
|
85
|
+
|
|
86
|
+
```bash
|
|
87
|
+
git clone https://github.com/amkillam/ryzen_smu
|
|
88
|
+
cd ryzen_smu && make && sudo make install
|
|
89
|
+
sudo modprobe ryzen_smu
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
**Apply a tuning preset:**
|
|
93
|
+
|
|
94
|
+
```bash
|
|
95
|
+
sudo zenmaster --stapm-limit=15000 --fast-limit=20000 --tctl-temp=90
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
**Re-apply every 30 seconds:**
|
|
99
|
+
|
|
100
|
+
```bash
|
|
101
|
+
sudo zenmaster --stapm-limit=15000 --reapply=30
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
### Windows
|
|
105
|
+
|
|
106
|
+
1. Install [PawnIO](https://github.com/namazso/PawnIO.Setup/releases/latest/download/PawnIO_setup.exe) and reboot.
|
|
107
|
+
2. Open an **Administrator** command prompt or terminal.
|
|
108
|
+
|
|
109
|
+
```bat
|
|
110
|
+
pip install zenmaster
|
|
111
|
+
zenmaster --stapm-limit=15000 --fast-limit=20000 --tctl-temp=90
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
---
|
|
115
|
+
|
|
116
|
+
## CLI
|
|
117
|
+
|
|
118
|
+
```
|
|
119
|
+
zenmaster [OPTIONS] [TUNING ARGS...]
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
| Option | Description |
|
|
123
|
+
|---|---|
|
|
124
|
+
| `--help` | Show supported tuning args for your CPU |
|
|
125
|
+
| `--info` | Detected CPU name, family, socket, backend |
|
|
126
|
+
| `--table` | Live PM table with labeled values |
|
|
127
|
+
| `--dump-table` | Raw PM table floats with hex offsets |
|
|
128
|
+
| `--json` | Machine-readable JSON output |
|
|
129
|
+
| `--reapply=N` | Re-apply settings every N seconds |
|
|
130
|
+
|
|
131
|
+
**Example — check what your CPU supports:**
|
|
132
|
+
|
|
133
|
+
```
|
|
134
|
+
$ zenmaster --help
|
|
135
|
+
|
|
136
|
+
ZenMaster — Ryzen Power Management Tool
|
|
137
|
+
|
|
138
|
+
Usage: zenmaster [OPTIONS] [TUNING ARGS...]
|
|
139
|
+
|
|
140
|
+
Tuning arguments for AMD Ryzen 9 7950X (Raphael, AM5_V1):
|
|
141
|
+
|
|
142
|
+
Power limits:
|
|
143
|
+
--stapm-limit=<mW> Sustained Power Limit — STAPM LIMIT
|
|
144
|
+
--fast-limit=<mW> Actual Power Limit — PPT LIMIT FAST
|
|
145
|
+
--slow-limit=<mW> Average Power Limit — PPT LIMIT SLOW
|
|
146
|
+
--stapm-time=<s> STAPM constant time
|
|
147
|
+
--slow-time=<s> Slow PPT constant time
|
|
148
|
+
|
|
149
|
+
Thermal:
|
|
150
|
+
--tctl-temp=<°C> Tctl Temperature Limit — THM LIMIT CORE
|
|
151
|
+
...
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
**Example — live PM table (APU/mobile):**
|
|
155
|
+
|
|
156
|
+
```
|
|
157
|
+
$ sudo zenmaster --table
|
|
158
|
+
|
|
159
|
+
PM Table Version: 0x00450005
|
|
160
|
+
+-------------------------+-----------+------------------------+
|
|
161
|
+
| STAPM LIMIT | 15.000 | stapm-limit |
|
|
162
|
+
| STAPM VALUE | 12.441 | |
|
|
163
|
+
| PPT LIMIT FAST | 20.000 | fast-limit |
|
|
164
|
+
| PPT VALUE FAST | 18.203 | |
|
|
165
|
+
| THM LIMIT CORE | 90.000 | tctl-temp |
|
|
166
|
+
| THM VALUE CORE | 67.125 | |
|
|
167
|
+
+-------------------------+-----------+------------------------+
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
---
|
|
171
|
+
|
|
172
|
+
## Library usage
|
|
173
|
+
|
|
174
|
+
ZenMaster is designed to be embedded in tuning utilities, dashboards, and automation tools.
|
|
175
|
+
|
|
176
|
+
```python
|
|
177
|
+
from zenmaster.hardware import detect
|
|
178
|
+
from zenmaster import smu
|
|
179
|
+
from zenmaster.apply import apply
|
|
180
|
+
|
|
181
|
+
# Detect CPU (no privileges needed)
|
|
182
|
+
info = detect()
|
|
183
|
+
print(info.name) # "AMD Ryzen 9 7950X"
|
|
184
|
+
print(info.family) # "Raphael"
|
|
185
|
+
|
|
186
|
+
# Initialise SMU backend — requires root/admin and a working driver.
|
|
187
|
+
# Always raises RuntimeError with a clear message if something is missing.
|
|
188
|
+
#
|
|
189
|
+
# Linux failure cases:
|
|
190
|
+
# - ryzen_smu not loaded + Secure Boot on → raises (lockdown blocks PCI too)
|
|
191
|
+
# - ryzen_smu not loaded + no PCI config → raises
|
|
192
|
+
# - ryzen_smu loaded but /smn missing → raises (module too old)
|
|
193
|
+
#
|
|
194
|
+
# Windows failure cases:
|
|
195
|
+
# - PawnIO not installed → raises with installer URL
|
|
196
|
+
# - PawnIO installed but not rebooted yet → raises, says to reboot
|
|
197
|
+
# - Not running as Administrator → raises
|
|
198
|
+
try:
|
|
199
|
+
backend = smu.init() # "ryzen_smu", "pci", or "pawnio"
|
|
200
|
+
except RuntimeError as e:
|
|
201
|
+
print(f"SMU unavailable: {e}")
|
|
202
|
+
raise SystemExit(1)
|
|
203
|
+
|
|
204
|
+
# Apply tuning args — returns per-arg results + a rejection flag
|
|
205
|
+
results, rejected = apply("--stapm-limit=15000 --tctl-temp=90", info.family)
|
|
206
|
+
for r in results:
|
|
207
|
+
print(r["arg"], r["status"]) # 0x01 = SMU_OK
|
|
208
|
+
|
|
209
|
+
# Read PM table (APU / mobile only)
|
|
210
|
+
if smu.pm_table_supported(info.family):
|
|
211
|
+
data = smu.read_pm_table(info.family)
|
|
212
|
+
ver = smu.read_pm_table_version(info.family)
|
|
213
|
+
|
|
214
|
+
# Send raw SMU commands directly
|
|
215
|
+
smu.send_mp1(info.family, opcode=0x05, arg0=15000)
|
|
216
|
+
smu.send_rsmu(info.family, opcode=0x53, arg0=90)
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
**Look up supported args for a CPU:**
|
|
220
|
+
|
|
221
|
+
```python
|
|
222
|
+
from zenmaster import runner
|
|
223
|
+
|
|
224
|
+
args = runner.get_supported_args("Raphael")
|
|
225
|
+
# ["stapm-limit", "fast-limit", "slow-limit", "tctl-temp", ...]
|
|
226
|
+
|
|
227
|
+
opcodes = runner.lookup("Raphael", "stapm-limit")
|
|
228
|
+
# [(True, 0x14), (False, 0x53)] — (is_mp1, opcode)
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
**Install with dev dependencies:**
|
|
232
|
+
|
|
233
|
+
```bash
|
|
234
|
+
pip install "zenmaster[dev]"
|
|
235
|
+
pytest
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
---
|
|
239
|
+
|
|
240
|
+
## Supported CPUs
|
|
241
|
+
|
|
242
|
+
Covers first-gen Ryzen (Summit Ridge / Zen 1) through Ryzen 9000 and Strix Halo. Run `zenmaster --info` to confirm detection and socket mapping.
|
|
243
|
+
|
|
244
|
+
PM table support (`--table`): Renoir, Lucienne, Cezanne/Barcelo, Rembrandt, Phoenix Point, Hawk Point, Strix Point, Krackan Point, Strix Halo.
|
|
245
|
+
|
|
246
|
+
---
|
|
247
|
+
|
|
248
|
+
## Requirements
|
|
249
|
+
|
|
250
|
+
| | Linux | Windows |
|
|
251
|
+
|---|---|---|
|
|
252
|
+
| Python | 3.10+ | 3.10+ |
|
|
253
|
+
| Privileges | root | Administrator |
|
|
254
|
+
| Driver | `ryzen_smu` module or PCI direct | [PawnIO](https://github.com/namazso/PawnIO.Setup) |
|
|
255
|
+
| Extra deps | None | None |
|
|
256
|
+
|
|
257
|
+
---
|
|
258
|
+
|
|
259
|
+
## License
|
|
260
|
+
|
|
261
|
+
GPL-3.0. SMU opcode tables from [UXTU4Linux](https://github.com/JamesCJ60/Universal-x86-Tuning-Utility-Handheld) (GPL-3.0). PawnIO kernel interface from [namazso/PawnIO](https://github.com/namazso/PawnIO) (MIT).
|
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
# ZenMaster
|
|
2
|
+
|
|
3
|
+
[](https://pypi.org/project/zenmaster/)
|
|
4
|
+
[](https://pypi.org/project/zenmaster/)
|
|
5
|
+
[](https://www.gnu.org/licenses/gpl-3.0)
|
|
6
|
+
[](https://pypi.org/project/zenmaster/)
|
|
7
|
+
|
|
8
|
+
Pure-Python AMD Ryzen power management via SMU. Works on Linux and Windows. No compiler, no build chain, no dependencies.
|
|
9
|
+
|
|
10
|
+
```bash
|
|
11
|
+
pip install zenmaster
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
## Features
|
|
17
|
+
|
|
18
|
+
- Set and read power limits, thermal limits, clock ranges, voltages, and PBO/Curve Optimiser offsets
|
|
19
|
+
- Live PM table — labeled power, thermal, and current sensor data
|
|
20
|
+
- Dynamic `--help` that shows only what your CPU supports
|
|
21
|
+
- JSON output for scripting and integration
|
|
22
|
+
- `--reapply=N` to continuously re-apply a tuning preset
|
|
23
|
+
- Embeddable Python library — use it as a backend in your own tuning tools
|
|
24
|
+
- **No WinRing0** — Windows support uses [PawnIO](https://github.com/namazso/PawnIO), a modern signed driver
|
|
25
|
+
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
## Why not RyzenAdj
|
|
29
|
+
|
|
30
|
+
| | RyzenAdj | ZenMaster |
|
|
31
|
+
|---|---|---|
|
|
32
|
+
| Install | Build from source | `pip install zenmaster` |
|
|
33
|
+
| Windows | WinRing0 ⚠️ | PawnIO ✅ |
|
|
34
|
+
| `--help` | Static, shows all args | Dynamic — CPU-specific only |
|
|
35
|
+
| Output | Plain text | Plain text or `--json` |
|
|
36
|
+
| PM table | Raw floats | Labeled fields with units |
|
|
37
|
+
| Embed / script | Shell out to binary | `import zenmaster` |
|
|
38
|
+
| Build deps | cmake, make, libpci | None |
|
|
39
|
+
|
|
40
|
+
### On WinRing0
|
|
41
|
+
|
|
42
|
+
RyzenAdj's Windows backend uses WinRing0, a driver with well-documented security vulnerabilities ([CVE-2020-14979](https://nvd.nist.gov/vuln/detail/CVE-2020-14979), [CVE-2021-41285](https://nvd.nist.gov/vuln/detail/CVE-2021-41285)). It grants any unprivileged process full read/write access to physical memory, PCI config space, and I/O ports. Several AV vendors flag it as malicious outright.
|
|
43
|
+
|
|
44
|
+
ZenMaster uses [PawnIO](https://github.com/namazso/PawnIO) instead — a purpose-built, Microsoft-signed kernel driver that exposes a controlled IOCTL interface. No raw memory access, no known CVEs.
|
|
45
|
+
|
|
46
|
+
---
|
|
47
|
+
|
|
48
|
+
## Installation
|
|
49
|
+
|
|
50
|
+
### Linux
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
pip install zenmaster
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
Requires root and either the `ryzen_smu` kernel module (recommended) or direct PCI access.
|
|
57
|
+
|
|
58
|
+
**Install ryzen_smu module:**
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
git clone https://github.com/amkillam/ryzen_smu
|
|
62
|
+
cd ryzen_smu && make && sudo make install
|
|
63
|
+
sudo modprobe ryzen_smu
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
**Apply a tuning preset:**
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
sudo zenmaster --stapm-limit=15000 --fast-limit=20000 --tctl-temp=90
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
**Re-apply every 30 seconds:**
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
sudo zenmaster --stapm-limit=15000 --reapply=30
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
### Windows
|
|
79
|
+
|
|
80
|
+
1. Install [PawnIO](https://github.com/namazso/PawnIO.Setup/releases/latest/download/PawnIO_setup.exe) and reboot.
|
|
81
|
+
2. Open an **Administrator** command prompt or terminal.
|
|
82
|
+
|
|
83
|
+
```bat
|
|
84
|
+
pip install zenmaster
|
|
85
|
+
zenmaster --stapm-limit=15000 --fast-limit=20000 --tctl-temp=90
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
---
|
|
89
|
+
|
|
90
|
+
## CLI
|
|
91
|
+
|
|
92
|
+
```
|
|
93
|
+
zenmaster [OPTIONS] [TUNING ARGS...]
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
| Option | Description |
|
|
97
|
+
|---|---|
|
|
98
|
+
| `--help` | Show supported tuning args for your CPU |
|
|
99
|
+
| `--info` | Detected CPU name, family, socket, backend |
|
|
100
|
+
| `--table` | Live PM table with labeled values |
|
|
101
|
+
| `--dump-table` | Raw PM table floats with hex offsets |
|
|
102
|
+
| `--json` | Machine-readable JSON output |
|
|
103
|
+
| `--reapply=N` | Re-apply settings every N seconds |
|
|
104
|
+
|
|
105
|
+
**Example — check what your CPU supports:**
|
|
106
|
+
|
|
107
|
+
```
|
|
108
|
+
$ zenmaster --help
|
|
109
|
+
|
|
110
|
+
ZenMaster — Ryzen Power Management Tool
|
|
111
|
+
|
|
112
|
+
Usage: zenmaster [OPTIONS] [TUNING ARGS...]
|
|
113
|
+
|
|
114
|
+
Tuning arguments for AMD Ryzen 9 7950X (Raphael, AM5_V1):
|
|
115
|
+
|
|
116
|
+
Power limits:
|
|
117
|
+
--stapm-limit=<mW> Sustained Power Limit — STAPM LIMIT
|
|
118
|
+
--fast-limit=<mW> Actual Power Limit — PPT LIMIT FAST
|
|
119
|
+
--slow-limit=<mW> Average Power Limit — PPT LIMIT SLOW
|
|
120
|
+
--stapm-time=<s> STAPM constant time
|
|
121
|
+
--slow-time=<s> Slow PPT constant time
|
|
122
|
+
|
|
123
|
+
Thermal:
|
|
124
|
+
--tctl-temp=<°C> Tctl Temperature Limit — THM LIMIT CORE
|
|
125
|
+
...
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
**Example — live PM table (APU/mobile):**
|
|
129
|
+
|
|
130
|
+
```
|
|
131
|
+
$ sudo zenmaster --table
|
|
132
|
+
|
|
133
|
+
PM Table Version: 0x00450005
|
|
134
|
+
+-------------------------+-----------+------------------------+
|
|
135
|
+
| STAPM LIMIT | 15.000 | stapm-limit |
|
|
136
|
+
| STAPM VALUE | 12.441 | |
|
|
137
|
+
| PPT LIMIT FAST | 20.000 | fast-limit |
|
|
138
|
+
| PPT VALUE FAST | 18.203 | |
|
|
139
|
+
| THM LIMIT CORE | 90.000 | tctl-temp |
|
|
140
|
+
| THM VALUE CORE | 67.125 | |
|
|
141
|
+
+-------------------------+-----------+------------------------+
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
---
|
|
145
|
+
|
|
146
|
+
## Library usage
|
|
147
|
+
|
|
148
|
+
ZenMaster is designed to be embedded in tuning utilities, dashboards, and automation tools.
|
|
149
|
+
|
|
150
|
+
```python
|
|
151
|
+
from zenmaster.hardware import detect
|
|
152
|
+
from zenmaster import smu
|
|
153
|
+
from zenmaster.apply import apply
|
|
154
|
+
|
|
155
|
+
# Detect CPU (no privileges needed)
|
|
156
|
+
info = detect()
|
|
157
|
+
print(info.name) # "AMD Ryzen 9 7950X"
|
|
158
|
+
print(info.family) # "Raphael"
|
|
159
|
+
|
|
160
|
+
# Initialise SMU backend — requires root/admin and a working driver.
|
|
161
|
+
# Always raises RuntimeError with a clear message if something is missing.
|
|
162
|
+
#
|
|
163
|
+
# Linux failure cases:
|
|
164
|
+
# - ryzen_smu not loaded + Secure Boot on → raises (lockdown blocks PCI too)
|
|
165
|
+
# - ryzen_smu not loaded + no PCI config → raises
|
|
166
|
+
# - ryzen_smu loaded but /smn missing → raises (module too old)
|
|
167
|
+
#
|
|
168
|
+
# Windows failure cases:
|
|
169
|
+
# - PawnIO not installed → raises with installer URL
|
|
170
|
+
# - PawnIO installed but not rebooted yet → raises, says to reboot
|
|
171
|
+
# - Not running as Administrator → raises
|
|
172
|
+
try:
|
|
173
|
+
backend = smu.init() # "ryzen_smu", "pci", or "pawnio"
|
|
174
|
+
except RuntimeError as e:
|
|
175
|
+
print(f"SMU unavailable: {e}")
|
|
176
|
+
raise SystemExit(1)
|
|
177
|
+
|
|
178
|
+
# Apply tuning args — returns per-arg results + a rejection flag
|
|
179
|
+
results, rejected = apply("--stapm-limit=15000 --tctl-temp=90", info.family)
|
|
180
|
+
for r in results:
|
|
181
|
+
print(r["arg"], r["status"]) # 0x01 = SMU_OK
|
|
182
|
+
|
|
183
|
+
# Read PM table (APU / mobile only)
|
|
184
|
+
if smu.pm_table_supported(info.family):
|
|
185
|
+
data = smu.read_pm_table(info.family)
|
|
186
|
+
ver = smu.read_pm_table_version(info.family)
|
|
187
|
+
|
|
188
|
+
# Send raw SMU commands directly
|
|
189
|
+
smu.send_mp1(info.family, opcode=0x05, arg0=15000)
|
|
190
|
+
smu.send_rsmu(info.family, opcode=0x53, arg0=90)
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
**Look up supported args for a CPU:**
|
|
194
|
+
|
|
195
|
+
```python
|
|
196
|
+
from zenmaster import runner
|
|
197
|
+
|
|
198
|
+
args = runner.get_supported_args("Raphael")
|
|
199
|
+
# ["stapm-limit", "fast-limit", "slow-limit", "tctl-temp", ...]
|
|
200
|
+
|
|
201
|
+
opcodes = runner.lookup("Raphael", "stapm-limit")
|
|
202
|
+
# [(True, 0x14), (False, 0x53)] — (is_mp1, opcode)
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
**Install with dev dependencies:**
|
|
206
|
+
|
|
207
|
+
```bash
|
|
208
|
+
pip install "zenmaster[dev]"
|
|
209
|
+
pytest
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
---
|
|
213
|
+
|
|
214
|
+
## Supported CPUs
|
|
215
|
+
|
|
216
|
+
Covers first-gen Ryzen (Summit Ridge / Zen 1) through Ryzen 9000 and Strix Halo. Run `zenmaster --info` to confirm detection and socket mapping.
|
|
217
|
+
|
|
218
|
+
PM table support (`--table`): Renoir, Lucienne, Cezanne/Barcelo, Rembrandt, Phoenix Point, Hawk Point, Strix Point, Krackan Point, Strix Halo.
|
|
219
|
+
|
|
220
|
+
---
|
|
221
|
+
|
|
222
|
+
## Requirements
|
|
223
|
+
|
|
224
|
+
| | Linux | Windows |
|
|
225
|
+
|---|---|---|
|
|
226
|
+
| Python | 3.10+ | 3.10+ |
|
|
227
|
+
| Privileges | root | Administrator |
|
|
228
|
+
| Driver | `ryzen_smu` module or PCI direct | [PawnIO](https://github.com/namazso/PawnIO.Setup) |
|
|
229
|
+
| Extra deps | None | None |
|
|
230
|
+
|
|
231
|
+
---
|
|
232
|
+
|
|
233
|
+
## License
|
|
234
|
+
|
|
235
|
+
GPL-3.0. SMU opcode tables from [UXTU4Linux](https://github.com/JamesCJ60/Universal-x86-Tuning-Utility-Handheld) (GPL-3.0). PawnIO kernel interface from [namazso/PawnIO](https://github.com/namazso/PawnIO) (MIT).
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=64"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "zenmaster"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
requires-python = ">=3.10"
|
|
9
|
+
description = "Pure-Python AMD Ryzen SMU power management for Linux and Windows"
|
|
10
|
+
readme = "README.md"
|
|
11
|
+
license = { text = "GPL-3.0-only" }
|
|
12
|
+
keywords = ["amd", "ryzen", "ryzenadj", "smu", "power-management", "tuning"]
|
|
13
|
+
classifiers = [
|
|
14
|
+
"Development Status :: 4 - Beta",
|
|
15
|
+
"Environment :: Console",
|
|
16
|
+
"Intended Audience :: End Users/Desktop",
|
|
17
|
+
"Intended Audience :: Developers",
|
|
18
|
+
"License :: OSI Approved :: GNU General Public License v3 (GPLv3)",
|
|
19
|
+
"Operating System :: POSIX :: Linux",
|
|
20
|
+
"Operating System :: Microsoft :: Windows",
|
|
21
|
+
"Programming Language :: Python :: 3",
|
|
22
|
+
"Programming Language :: Python :: 3.10",
|
|
23
|
+
"Programming Language :: Python :: 3.11",
|
|
24
|
+
"Programming Language :: Python :: 3.12",
|
|
25
|
+
"Programming Language :: Python :: 3.13",
|
|
26
|
+
"Programming Language :: Python :: 3.14",
|
|
27
|
+
"Topic :: System :: Hardware",
|
|
28
|
+
"Topic :: Utilities",
|
|
29
|
+
]
|
|
30
|
+
|
|
31
|
+
[project.scripts]
|
|
32
|
+
zenmaster = "zenmaster.cli:main"
|
|
33
|
+
|
|
34
|
+
[project.optional-dependencies]
|
|
35
|
+
dev = ["pytest>=7"]
|
|
36
|
+
|
|
37
|
+
[tool.pytest.ini_options]
|
|
38
|
+
testpaths = ["tests"]
|
|
39
|
+
|
|
40
|
+
[tool.setuptools.packages.find]
|
|
41
|
+
where = ["."]
|
|
42
|
+
include = ["zenmaster*"]
|
|
43
|
+
|
|
44
|
+
[tool.setuptools.package-data]
|
|
45
|
+
zenmaster = ["assets/AMD/PawnIO/*.bin"]
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
from unittest.mock import patch
|
|
2
|
+
from zenmaster.apply import apply
|
|
3
|
+
from zenmaster.smu import SMU_OK, SMU_FAILED
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def test_apply_single_arg_ok():
|
|
7
|
+
with patch("zenmaster.smu.send_mp1", return_value=SMU_OK), \
|
|
8
|
+
patch("zenmaster.smu.send_rsmu", return_value=SMU_OK):
|
|
9
|
+
results, rejected = apply("--stapm-limit=15000", "Rembrandt")
|
|
10
|
+
assert not rejected
|
|
11
|
+
assert any(r["arg"] == "stapm-limit" and r["status"] == SMU_OK for r in results)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def test_apply_unsupported_arg():
|
|
15
|
+
results, rejected = apply("--nonexistent-arg=1", "Rembrandt")
|
|
16
|
+
assert any(r["arg"] == "nonexistent-arg" and r["status"] == 0 for r in results)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def test_apply_unknown_family():
|
|
20
|
+
results, rejected = apply("--stapm-limit=15000", "UnknownCPU")
|
|
21
|
+
assert any(r["arg"] == "stapm-limit" and r["status"] == 0 for r in results)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def test_apply_rejected_counts_as_rejection():
|
|
25
|
+
with patch("zenmaster.smu.send_mp1", return_value=SMU_FAILED), \
|
|
26
|
+
patch("zenmaster.smu.send_rsmu", return_value=SMU_FAILED):
|
|
27
|
+
results, rejected = apply("--stapm-limit=15000", "Rembrandt")
|
|
28
|
+
assert rejected
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def test_apply_skin_temp_scale():
|
|
32
|
+
captured = []
|
|
33
|
+
def fake_send_mp1(family, op, arg0):
|
|
34
|
+
captured.append(arg0)
|
|
35
|
+
return SMU_OK
|
|
36
|
+
with patch("zenmaster.smu.send_mp1", side_effect=fake_send_mp1), \
|
|
37
|
+
patch("zenmaster.smu.send_rsmu", return_value=SMU_OK):
|
|
38
|
+
apply("--apu-skin-temp=45", "Rembrandt")
|
|
39
|
+
assert any(v == 45 * 256 for v in captured)
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def test_apply_multiple_args():
|
|
43
|
+
with patch("zenmaster.smu.send_mp1", return_value=SMU_OK), \
|
|
44
|
+
patch("zenmaster.smu.send_rsmu", return_value=SMU_OK):
|
|
45
|
+
results, rejected = apply("--stapm-limit=15000 --tctl-temp=85", "Rembrandt")
|
|
46
|
+
arg_names = [r["arg"] for r in results]
|
|
47
|
+
assert "stapm-limit" in arg_names
|
|
48
|
+
assert "tctl-temp" in arg_names
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def test_apply_empty_string():
|
|
52
|
+
results, rejected = apply("", "Rembrandt")
|
|
53
|
+
assert results == []
|
|
54
|
+
assert not rejected
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def test_apply_strips_leading_dashes():
|
|
58
|
+
with patch("zenmaster.smu.send_mp1", return_value=SMU_OK), \
|
|
59
|
+
patch("zenmaster.smu.send_rsmu", return_value=SMU_OK):
|
|
60
|
+
r1, _ = apply("--stapm-limit=15000", "Rembrandt")
|
|
61
|
+
r2, _ = apply("stapm-limit=15000", "Rembrandt")
|
|
62
|
+
assert len(r1) == len(r2)
|