zenmaster 1.2.0__tar.gz → 1.2.2__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-1.2.0/zenmaster.egg-info → zenmaster-1.2.2}/PKG-INFO +2 -2
- {zenmaster-1.2.0 → zenmaster-1.2.2}/README.md +1 -1
- {zenmaster-1.2.0 → zenmaster-1.2.2}/pyproject.toml +1 -1
- {zenmaster-1.2.0 → zenmaster-1.2.2}/zenmaster/RyzenSMU.bin +0 -0
- {zenmaster-1.2.0 → zenmaster-1.2.2}/zenmaster/__init__.py +1 -1
- {zenmaster-1.2.0 → zenmaster-1.2.2}/zenmaster/cli.py +9 -1
- {zenmaster-1.2.0 → zenmaster-1.2.2}/zenmaster/hardware.py +12 -1
- {zenmaster-1.2.0 → zenmaster-1.2.2}/zenmaster/linux.py +81 -26
- {zenmaster-1.2.0 → zenmaster-1.2.2}/zenmaster/macos.py +121 -18
- {zenmaster-1.2.0 → zenmaster-1.2.2}/zenmaster/mailbox.py +20 -11
- {zenmaster-1.2.0 → zenmaster-1.2.2}/zenmaster/smu.py +23 -10
- {zenmaster-1.2.0 → zenmaster-1.2.2}/zenmaster/windows.py +171 -40
- {zenmaster-1.2.0 → zenmaster-1.2.2/zenmaster.egg-info}/PKG-INFO +2 -2
- {zenmaster-1.2.0 → zenmaster-1.2.2}/LICENSE +0 -0
- {zenmaster-1.2.0 → zenmaster-1.2.2}/setup.cfg +0 -0
- {zenmaster-1.2.0 → zenmaster-1.2.2}/zenmaster/__main__.py +0 -0
- {zenmaster-1.2.0 → zenmaster-1.2.2}/zenmaster/apply.py +0 -0
- {zenmaster-1.2.0 → zenmaster-1.2.2}/zenmaster/directhw.py +0 -0
- {zenmaster-1.2.0 → zenmaster-1.2.2}/zenmaster/errors.py +0 -0
- {zenmaster-1.2.0 → zenmaster-1.2.2}/zenmaster/iokit.py +0 -0
- {zenmaster-1.2.0 → zenmaster-1.2.2}/zenmaster/iopci.py +0 -0
- {zenmaster-1.2.0 → zenmaster-1.2.2}/zenmaster/pmtable.py +0 -0
- {zenmaster-1.2.0 → zenmaster-1.2.2}/zenmaster/py.typed +0 -0
- {zenmaster-1.2.0 → zenmaster-1.2.2}/zenmaster/runner.py +0 -0
- {zenmaster-1.2.0 → zenmaster-1.2.2}/zenmaster/sensors.py +0 -0
- {zenmaster-1.2.0 → zenmaster-1.2.2}/zenmaster/update.py +0 -0
- {zenmaster-1.2.0 → zenmaster-1.2.2}/zenmaster.egg-info/SOURCES.txt +0 -0
- {zenmaster-1.2.0 → zenmaster-1.2.2}/zenmaster.egg-info/dependency_links.txt +0 -0
- {zenmaster-1.2.0 → zenmaster-1.2.2}/zenmaster.egg-info/entry_points.txt +0 -0
- {zenmaster-1.2.0 → zenmaster-1.2.2}/zenmaster.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: zenmaster
|
|
3
|
-
Version: 1.2.
|
|
3
|
+
Version: 1.2.2
|
|
4
4
|
Summary: Adjust power management settings for Ryzen CPUs on Linux, Windows, and macOS
|
|
5
5
|
Author-email: oxGorou <lekhanhbinh2901@gmail.com>
|
|
6
6
|
License-Expression: GPL-3.0-only
|
|
@@ -193,7 +193,7 @@ print(f"SMN 0x0005A380 = 0x{value:08X}")
|
|
|
193
193
|
|
|
194
194
|
## Technical Comparison: ZenMaster vs. RyzenAdj
|
|
195
195
|
|
|
196
|
-
| Feature | RyzenAdj | ZenMaster v1.2.
|
|
196
|
+
| Feature | RyzenAdj | ZenMaster v1.2.2 |
|
|
197
197
|
|---|---|---|
|
|
198
198
|
| **Installation** | Compilation from source (`cmake`, `make`, `libpci-dev`) | `pip install zenmaster` (pure Python package) |
|
|
199
199
|
| **Supported Families** | ~15 APU and desktop families | 44 families (Zen 1 to Zen 5/6, Desktop, Mobile, Threadripper, Server EPYC via HSMP) |
|
|
@@ -162,7 +162,7 @@ print(f"SMN 0x0005A380 = 0x{value:08X}")
|
|
|
162
162
|
|
|
163
163
|
## Technical Comparison: ZenMaster vs. RyzenAdj
|
|
164
164
|
|
|
165
|
-
| Feature | RyzenAdj | ZenMaster v1.2.
|
|
165
|
+
| Feature | RyzenAdj | ZenMaster v1.2.2 |
|
|
166
166
|
|---|---|---|
|
|
167
167
|
| **Installation** | Compilation from source (`cmake`, `make`, `libpci-dev`) | `pip install zenmaster` (pure Python package) |
|
|
168
168
|
| **Supported Families** | ~15 APU and desktop families | 44 families (Zen 1 to Zen 5/6, Desktop, Mobile, Threadripper, Server EPYC via HSMP) |
|
|
File without changes
|
|
@@ -26,7 +26,7 @@ from zenmaster.errors import (
|
|
|
26
26
|
try:
|
|
27
27
|
__version__ = version("zenmaster")
|
|
28
28
|
except PackageNotFoundError:
|
|
29
|
-
__version__ = "1.2.
|
|
29
|
+
__version__ = "1.2.2"
|
|
30
30
|
|
|
31
31
|
__all__ = [
|
|
32
32
|
"CpuInfo", "detect", "resolve", "apply", "ApplyResult", "runner", "smu", "sensors", "table",
|
|
@@ -490,9 +490,17 @@ _SENSOR_ROWS = [
|
|
|
490
490
|
|
|
491
491
|
|
|
492
492
|
def _show_sensors(json_out: bool, family: str = "") -> None:
|
|
493
|
+
if not smu.pm_table_supported(family):
|
|
494
|
+
msg = _pm_unavailable_msg(family)
|
|
495
|
+
if json_out:
|
|
496
|
+
print(json.dumps({"error": msg}))
|
|
497
|
+
else:
|
|
498
|
+
print(f"ZenMaster: {msg}", file=sys.stderr)
|
|
499
|
+
sys.exit(1)
|
|
500
|
+
|
|
493
501
|
sensors = smu.read_pm_sensors(family)
|
|
494
502
|
if sensors is None:
|
|
495
|
-
msg =
|
|
503
|
+
msg = "Failed to read PM table"
|
|
496
504
|
if json_out:
|
|
497
505
|
print(json.dumps({"error": msg}))
|
|
498
506
|
else:
|
|
@@ -97,7 +97,16 @@ def _libSystem() -> ctypes.CDLL | None:
|
|
|
97
97
|
global _libc
|
|
98
98
|
if _libc is None:
|
|
99
99
|
try:
|
|
100
|
-
|
|
100
|
+
libc = ctypes.CDLL("/usr/lib/libSystem.B.dylib")
|
|
101
|
+
libc.sysctlbyname.restype = ctypes.c_int
|
|
102
|
+
libc.sysctlbyname.argtypes = [
|
|
103
|
+
ctypes.c_char_p,
|
|
104
|
+
ctypes.c_void_p,
|
|
105
|
+
ctypes.POINTER(ctypes.c_size_t),
|
|
106
|
+
ctypes.c_void_p,
|
|
107
|
+
ctypes.c_size_t,
|
|
108
|
+
]
|
|
109
|
+
_libc = libc
|
|
101
110
|
except (OSError, Exception):
|
|
102
111
|
return None
|
|
103
112
|
return _libc
|
|
@@ -128,6 +137,8 @@ def _sysctl_int(name: str) -> int:
|
|
|
128
137
|
size = ctypes.c_size_t(ctypes.sizeof(val))
|
|
129
138
|
if libc.sysctlbyname(name.encode(), ctypes.byref(val), ctypes.byref(size), None, 0) != 0:
|
|
130
139
|
return 0
|
|
140
|
+
if size.value == 4:
|
|
141
|
+
return int(ctypes.c_int32(val.value & 0xFFFFFFFF).value)
|
|
131
142
|
return int(val.value)
|
|
132
143
|
except (OSError, Exception):
|
|
133
144
|
return 0
|
|
@@ -11,7 +11,10 @@ from typing import Any
|
|
|
11
11
|
|
|
12
12
|
from zenmaster.errors import BackendUnavailable, SMUNotInitialized
|
|
13
13
|
from zenmaster.pmtable import PM_TABLE_CMDS, TABLE_SIZES, DEFAULT_TABLE_SIZE
|
|
14
|
-
from zenmaster.mailbox import
|
|
14
|
+
from zenmaster.mailbox import (
|
|
15
|
+
MP1, MP1_DEFAULT, RSMU, RSMU_DEFAULT, HSMP, HSMP_DEFAULT, NARGS,
|
|
16
|
+
DEFAULT_TIMEOUT, poll_response, transfer_with_retry,
|
|
17
|
+
)
|
|
15
18
|
from zenmaster.smu import SMU_OK, SMU_FAILED, SMU_REJECTED_PREREQ, ModuleStatus
|
|
16
19
|
|
|
17
20
|
DRIVER_NAME = "ryzen_smu"
|
|
@@ -27,7 +30,7 @@ _PCI_MUTEX_FALLBACK: str = "/tmp/access_pci.lock"
|
|
|
27
30
|
_PCI_MUTEX_TIMEOUT_MS: int = 5000
|
|
28
31
|
|
|
29
32
|
|
|
30
|
-
def acquire_pci_mutex(timeout_ms: int = _PCI_MUTEX_TIMEOUT_MS) ->
|
|
33
|
+
def acquire_pci_mutex(timeout_ms: int = _PCI_MUTEX_TIMEOUT_MS) -> Any:
|
|
31
34
|
path = PCI_MUTEX_NAME
|
|
32
35
|
fd = None
|
|
33
36
|
try:
|
|
@@ -87,10 +90,11 @@ MIN_VERSION = (0, 1, 7)
|
|
|
87
90
|
|
|
88
91
|
_POLL_N = 100
|
|
89
92
|
_SLEEP_S = 0.0001
|
|
90
|
-
_DEADLINE =
|
|
93
|
+
_DEADLINE = DEFAULT_TIMEOUT
|
|
91
94
|
_lock = threading.Lock()
|
|
92
95
|
_backend: str | None = None
|
|
93
96
|
_fd: int | None = None
|
|
97
|
+
_cached_pm_ver: int | None = None
|
|
94
98
|
|
|
95
99
|
|
|
96
100
|
def version_str(v: tuple[int, ...]) -> str:
|
|
@@ -159,7 +163,7 @@ def secure_boot_enabled() -> bool:
|
|
|
159
163
|
try:
|
|
160
164
|
out = subprocess.run(
|
|
161
165
|
["mokutil", "--sb-state"],
|
|
162
|
-
capture_output=True, text=True, timeout=
|
|
166
|
+
capture_output=True, text=True, timeout=5,
|
|
163
167
|
).stdout.lower()
|
|
164
168
|
return "enabled" in out
|
|
165
169
|
except Exception:
|
|
@@ -185,6 +189,17 @@ def _pci_writable() -> bool:
|
|
|
185
189
|
return False
|
|
186
190
|
|
|
187
191
|
|
|
192
|
+
def _ryzen_smu_writable() -> bool:
|
|
193
|
+
if not (os.path.isdir(DRIVER_PATH) and module_version_ok() and os.path.exists(SMN_PATH)):
|
|
194
|
+
return False
|
|
195
|
+
try:
|
|
196
|
+
fd = os.open(SMN_PATH, os.O_RDWR)
|
|
197
|
+
os.close(fd)
|
|
198
|
+
return True
|
|
199
|
+
except OSError:
|
|
200
|
+
return False
|
|
201
|
+
|
|
202
|
+
|
|
188
203
|
def init() -> str:
|
|
189
204
|
global _backend
|
|
190
205
|
|
|
@@ -215,6 +230,10 @@ def init() -> str:
|
|
|
215
230
|
_backend = "pci"
|
|
216
231
|
return _backend
|
|
217
232
|
|
|
233
|
+
if _ryzen_smu_writable():
|
|
234
|
+
_backend = "ryzen_smu"
|
|
235
|
+
return _backend
|
|
236
|
+
|
|
218
237
|
if os.path.exists(PCI_CONFIG):
|
|
219
238
|
raise BackendUnavailable(
|
|
220
239
|
"Secure Boot is off, so ZenMaster uses PCI direct access, but the PCI "
|
|
@@ -256,7 +275,7 @@ def _pci_read(fd: int, addr: int) -> int:
|
|
|
256
275
|
|
|
257
276
|
def _pci_write(fd: int, addr: int, value: int) -> None:
|
|
258
277
|
os.lseek(fd, NB_ADDR, os.SEEK_SET)
|
|
259
|
-
os.write(fd, struct.pack("<I", addr))
|
|
278
|
+
os.write(fd, struct.pack("<I", addr & ~3))
|
|
260
279
|
os.lseek(fd, NB_DATA, os.SEEK_SET)
|
|
261
280
|
os.write(fd, struct.pack("<I", value))
|
|
262
281
|
|
|
@@ -267,17 +286,7 @@ def _send_seq(write: Any, read: Any, fd: int, msg: int, rsp: int, args: int, op:
|
|
|
267
286
|
for i in range(1, NARGS):
|
|
268
287
|
write(fd, args + i * 4, 0)
|
|
269
288
|
write(fd, msg, op)
|
|
270
|
-
|
|
271
|
-
r = read(fd, rsp)
|
|
272
|
-
if r:
|
|
273
|
-
return r
|
|
274
|
-
deadline = time.monotonic() + _DEADLINE
|
|
275
|
-
while time.monotonic() < deadline:
|
|
276
|
-
time.sleep(_SLEEP_S)
|
|
277
|
-
r = read(fd, rsp)
|
|
278
|
-
if r:
|
|
279
|
-
return r
|
|
280
|
-
return SMU_FAILED
|
|
289
|
+
return poll_response(lambda a: read(fd, a), rsp, _POLL_N, _SLEEP_S, _DEADLINE) or SMU_FAILED
|
|
281
290
|
|
|
282
291
|
|
|
283
292
|
def _require_init() -> None:
|
|
@@ -378,6 +387,8 @@ def smu_command(msg_id: int, arg: int = 0) -> int:
|
|
|
378
387
|
|
|
379
388
|
|
|
380
389
|
def pm_table_supported(family: str = "") -> bool:
|
|
390
|
+
if _cached_pm_ver:
|
|
391
|
+
return True
|
|
381
392
|
if os.path.exists(DRIVER_PATH + "/pm_table"):
|
|
382
393
|
return True
|
|
383
394
|
return family in PM_TABLE_CMDS
|
|
@@ -406,6 +417,9 @@ def _read_devmem(phys: int, size: int) -> bytes | None:
|
|
|
406
417
|
|
|
407
418
|
|
|
408
419
|
def _read_pm_table_pci(family: str) -> tuple[bytes, int] | None:
|
|
420
|
+
global _cached_pm_ver
|
|
421
|
+
if _backend is None:
|
|
422
|
+
return None
|
|
409
423
|
cmds = PM_TABLE_CMDS.get(family)
|
|
410
424
|
if cmds is None:
|
|
411
425
|
return None
|
|
@@ -414,7 +428,8 @@ def _read_pm_table_pci(family: str) -> tuple[bytes, int] | None:
|
|
|
414
428
|
status, out = query_rsmu(family, ver_op, 0)
|
|
415
429
|
if status != SMU_OK or not out[0]:
|
|
416
430
|
return None
|
|
417
|
-
ver = out[0]
|
|
431
|
+
ver = out[0] & 0xFFFFFFFF
|
|
432
|
+
_cached_pm_ver = ver
|
|
418
433
|
size = TABLE_SIZES.get(ver, DEFAULT_TABLE_SIZE)
|
|
419
434
|
|
|
420
435
|
status, out = query_rsmu(family, addr_op, extra)
|
|
@@ -424,10 +439,7 @@ def _read_pm_table_pci(family: str) -> tuple[bytes, int] | None:
|
|
|
424
439
|
if not phys:
|
|
425
440
|
return None
|
|
426
441
|
|
|
427
|
-
status
|
|
428
|
-
if status == SMU_REJECTED_PREREQ:
|
|
429
|
-
time.sleep(0.01)
|
|
430
|
-
status, _ = query_rsmu(family, transfer_op, extra)
|
|
442
|
+
status = transfer_with_retry(lambda: query_rsmu(family, transfer_op, extra)[0], (0.1, 0.5))
|
|
431
443
|
if status != SMU_OK:
|
|
432
444
|
return None
|
|
433
445
|
|
|
@@ -435,6 +447,19 @@ def _read_pm_table_pci(family: str) -> tuple[bytes, int] | None:
|
|
|
435
447
|
return (data, ver) if data is not None else None
|
|
436
448
|
|
|
437
449
|
|
|
450
|
+
def get_smu_version(family: str = "") -> int:
|
|
451
|
+
if _backend in ("ryzen_smu", None):
|
|
452
|
+
try:
|
|
453
|
+
with open(DRIVER_PATH + "/version", "r") as f:
|
|
454
|
+
return int(f.read().strip(), 16) & 0xFFFFFFFF
|
|
455
|
+
except (OSError, ValueError):
|
|
456
|
+
pass
|
|
457
|
+
if _backend is None:
|
|
458
|
+
return 0
|
|
459
|
+
status, out = query_mp1(family, 0x02, 1)
|
|
460
|
+
return (out[0] & 0xFFFFFFFF) if status == SMU_OK and out[0] else 0
|
|
461
|
+
|
|
462
|
+
|
|
438
463
|
def read_pm_table_full(family: str = "") -> tuple[bytes, int] | None:
|
|
439
464
|
if _backend == "pci":
|
|
440
465
|
return _read_pm_table_pci(family)
|
|
@@ -445,16 +470,45 @@ def read_pm_table_full(family: str = "") -> tuple[bytes, int] | None:
|
|
|
445
470
|
|
|
446
471
|
|
|
447
472
|
def read_pm_table_version(family: str = "") -> int:
|
|
473
|
+
global _cached_pm_ver
|
|
474
|
+
if _cached_pm_ver:
|
|
475
|
+
return _cached_pm_ver
|
|
476
|
+
|
|
448
477
|
if _backend == "pci":
|
|
478
|
+
if family in PM_TABLE_CMDS:
|
|
479
|
+
ver_op = PM_TABLE_CMDS[family][0]
|
|
480
|
+
status, out = query_rsmu(family, ver_op, 0)
|
|
481
|
+
if status == SMU_OK and out[0]:
|
|
482
|
+
_cached_pm_ver = out[0] & 0xFFFFFFFF
|
|
483
|
+
return _cached_pm_ver
|
|
449
484
|
r = _read_pm_table_pci(family)
|
|
450
|
-
|
|
485
|
+
if r:
|
|
486
|
+
_cached_pm_ver = r[1] & 0xFFFFFFFF
|
|
487
|
+
return _cached_pm_ver
|
|
488
|
+
return 0
|
|
489
|
+
|
|
451
490
|
try:
|
|
452
491
|
with open(DRIVER_PATH + "/pm_table_version", "rb") as f:
|
|
453
492
|
raw = f.read(4)
|
|
454
|
-
|
|
493
|
+
ver = struct.unpack("<I", raw)[0] & 0xFFFFFFFF if len(raw) >= 4 else 0
|
|
494
|
+
if ver:
|
|
495
|
+
_cached_pm_ver = ver
|
|
496
|
+
return _cached_pm_ver
|
|
455
497
|
except OSError:
|
|
456
|
-
|
|
457
|
-
|
|
498
|
+
pass
|
|
499
|
+
|
|
500
|
+
if _backend is not None and family in PM_TABLE_CMDS:
|
|
501
|
+
ver_op = PM_TABLE_CMDS[family][0]
|
|
502
|
+
status, out = query_rsmu(family, ver_op, 0)
|
|
503
|
+
if status == SMU_OK and out[0]:
|
|
504
|
+
_cached_pm_ver = out[0] & 0xFFFFFFFF
|
|
505
|
+
return _cached_pm_ver
|
|
506
|
+
|
|
507
|
+
r = _read_pm_table_pci(family)
|
|
508
|
+
if r:
|
|
509
|
+
_cached_pm_ver = r[1] & 0xFFFFFFFF
|
|
510
|
+
return _cached_pm_ver
|
|
511
|
+
return 0
|
|
458
512
|
|
|
459
513
|
|
|
460
514
|
def read_pm_table(family: str = "") -> bytes | None:
|
|
@@ -477,7 +531,7 @@ def read_pm_table(family: str = "") -> bytes | None:
|
|
|
477
531
|
|
|
478
532
|
|
|
479
533
|
def close() -> None:
|
|
480
|
-
global _backend, _fd
|
|
534
|
+
global _backend, _fd, _cached_pm_ver
|
|
481
535
|
if _fd is not None:
|
|
482
536
|
try:
|
|
483
537
|
os.close(_fd)
|
|
@@ -485,3 +539,4 @@ def close() -> None:
|
|
|
485
539
|
pass
|
|
486
540
|
_fd = None
|
|
487
541
|
_backend = None
|
|
542
|
+
_cached_pm_ver = None
|
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
|
+
from contextlib import contextmanager
|
|
3
|
+
import fcntl
|
|
4
|
+
import os
|
|
2
5
|
import threading
|
|
6
|
+
import time
|
|
7
|
+
from typing import Any
|
|
3
8
|
|
|
4
9
|
from zenmaster import directhw, iopci
|
|
5
10
|
from zenmaster.hardware import _sysctl_str
|
|
@@ -7,29 +12,84 @@ from zenmaster.errors import BackendUnavailable, SMUNotInitialized
|
|
|
7
12
|
from zenmaster.pmtable import PM_TABLE_CMDS, TABLE_SIZES, DEFAULT_TABLE_SIZE
|
|
8
13
|
from zenmaster.mailbox import (
|
|
9
14
|
MP1, MP1_DEFAULT, RSMU, RSMU_DEFAULT, HSMP, HSMP_DEFAULT,
|
|
10
|
-
mailbox_send, mailbox_query, transfer_with_retry,
|
|
15
|
+
DEFAULT_TIMEOUT, mailbox_send, mailbox_query, transfer_with_retry,
|
|
11
16
|
)
|
|
12
17
|
from zenmaster.smu import SMU_OK, ModuleStatus
|
|
13
18
|
|
|
14
19
|
DRIVER_NAME = "DirectHW"
|
|
20
|
+
PCI_MUTEX_NAME: str = "/tmp/access_pci.lock"
|
|
21
|
+
_PCI_MUTEX_TIMEOUT_MS: int = 5000
|
|
15
22
|
|
|
16
23
|
NB_ADDR = 0xB8
|
|
17
24
|
NB_DATA = 0xBC
|
|
18
25
|
PCI_ADDR = 0xCF8
|
|
19
26
|
PCI_DATA = 0xCFC
|
|
20
27
|
|
|
21
|
-
_POLL_N = 100
|
|
22
28
|
_FAST_POLL = 64
|
|
23
29
|
_POLL_SLEEP = 0.0005
|
|
30
|
+
_TIMEOUT = DEFAULT_TIMEOUT
|
|
24
31
|
_lock = threading.Lock()
|
|
25
32
|
_backend: str | None = None
|
|
26
33
|
_force: str | None = None
|
|
34
|
+
_cached_pm_ver: int | None = None
|
|
27
35
|
|
|
28
36
|
_KEXT_URL = "https://github.com/joevt/directhw"
|
|
29
37
|
|
|
30
38
|
_IOPCI_DEBUG_MSG = "The IOPCIBridge path requires the debug=0x144 boot-arg."
|
|
31
39
|
|
|
32
40
|
|
|
41
|
+
def acquire_pci_mutex(timeout_ms: int = _PCI_MUTEX_TIMEOUT_MS) -> Any:
|
|
42
|
+
path = PCI_MUTEX_NAME
|
|
43
|
+
try:
|
|
44
|
+
fd = os.open(path, os.O_CREAT | os.O_RDWR, 0o666)
|
|
45
|
+
except OSError:
|
|
46
|
+
return None
|
|
47
|
+
deadline = time.monotonic() + (timeout_ms / 1000.0)
|
|
48
|
+
while True:
|
|
49
|
+
try:
|
|
50
|
+
fcntl.flock(fd, fcntl.LOCK_EX | fcntl.LOCK_NB)
|
|
51
|
+
return fd
|
|
52
|
+
except (BlockingIOError, OSError):
|
|
53
|
+
if time.monotonic() >= deadline:
|
|
54
|
+
try:
|
|
55
|
+
os.close(fd)
|
|
56
|
+
except OSError:
|
|
57
|
+
pass
|
|
58
|
+
return None
|
|
59
|
+
time.sleep(0.005)
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def release_pci_mutex(handle: Any) -> bool:
|
|
63
|
+
if handle is None:
|
|
64
|
+
return False
|
|
65
|
+
try:
|
|
66
|
+
fcntl.flock(handle, fcntl.LOCK_UN)
|
|
67
|
+
return True
|
|
68
|
+
except OSError:
|
|
69
|
+
return False
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def close_pci_mutex(handle: Any) -> bool:
|
|
73
|
+
if handle is None:
|
|
74
|
+
return False
|
|
75
|
+
try:
|
|
76
|
+
os.close(handle)
|
|
77
|
+
return True
|
|
78
|
+
except OSError:
|
|
79
|
+
return False
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
@contextmanager
|
|
83
|
+
def pci_mutex_guard(timeout_ms: int = _PCI_MUTEX_TIMEOUT_MS) -> Any:
|
|
84
|
+
h = acquire_pci_mutex(timeout_ms)
|
|
85
|
+
try:
|
|
86
|
+
yield h
|
|
87
|
+
finally:
|
|
88
|
+
if h is not None:
|
|
89
|
+
release_pci_mutex(h)
|
|
90
|
+
close_pci_mutex(h)
|
|
91
|
+
|
|
92
|
+
|
|
33
93
|
def force_iopci() -> None:
|
|
34
94
|
global _force
|
|
35
95
|
_force = "iopci"
|
|
@@ -52,13 +112,17 @@ def module_version_ok() -> bool:
|
|
|
52
112
|
|
|
53
113
|
|
|
54
114
|
def module_status() -> ModuleStatus:
|
|
55
|
-
if
|
|
115
|
+
if _backend == "iopci":
|
|
116
|
+
return ModuleStatus(True, "loaded", "", None)
|
|
117
|
+
if not directhw.is_loaded():
|
|
118
|
+
if _debug_boot_arg_ok() and iopci.is_available():
|
|
119
|
+
return ModuleStatus(True, "loaded", "", None)
|
|
56
120
|
return ModuleStatus(False, "unknown", "", "not_loaded")
|
|
57
121
|
return ModuleStatus(True, "loaded", "", None)
|
|
58
122
|
|
|
59
123
|
|
|
60
124
|
def is_available() -> bool:
|
|
61
|
-
return directhw.is_loaded() or iopci.is_available()
|
|
125
|
+
return directhw.is_loaded() or (_debug_boot_arg_ok() and iopci.is_available())
|
|
62
126
|
|
|
63
127
|
|
|
64
128
|
def init() -> str:
|
|
@@ -140,12 +204,12 @@ def _smn_write(addr: int, value: int) -> None:
|
|
|
140
204
|
|
|
141
205
|
def _mailbox_send(msg: int, rsp: int, args_addr: int, op: int, arg0: int) -> int:
|
|
142
206
|
return mailbox_send(_smn_write, _smn_read, msg, rsp, args_addr, op, arg0,
|
|
143
|
-
|
|
207
|
+
_FAST_POLL, _POLL_SLEEP, _TIMEOUT)
|
|
144
208
|
|
|
145
209
|
|
|
146
210
|
def _mailbox_query(msg: int, rsp: int, args_base: int, op: int, arg0: int = 0) -> tuple[int, list[int]]:
|
|
147
211
|
return mailbox_query(_smn_write, _smn_read, msg, rsp, args_base, op, arg0,
|
|
148
|
-
|
|
212
|
+
_FAST_POLL, _POLL_SLEEP, _TIMEOUT)
|
|
149
213
|
|
|
150
214
|
|
|
151
215
|
def _require_init() -> None:
|
|
@@ -157,14 +221,16 @@ def _send(table: dict, default: tuple, family: str, op: int, arg0: int) -> int:
|
|
|
157
221
|
_require_init()
|
|
158
222
|
msg, rsp, args = table.get(family, default)
|
|
159
223
|
with _lock:
|
|
160
|
-
|
|
224
|
+
with pci_mutex_guard():
|
|
225
|
+
return _mailbox_send(msg, rsp, args, op, arg0)
|
|
161
226
|
|
|
162
227
|
|
|
163
228
|
def _query(table: dict, default: tuple, family: str, op: int, arg0: int) -> tuple[int, list[int]]:
|
|
164
229
|
_require_init()
|
|
165
230
|
msg, rsp, args = table.get(family, default)
|
|
166
231
|
with _lock:
|
|
167
|
-
|
|
232
|
+
with pci_mutex_guard():
|
|
233
|
+
return _mailbox_query(msg, rsp, args, op, arg0)
|
|
168
234
|
|
|
169
235
|
|
|
170
236
|
def send_mp1(family: str, op: int, arg0: int = 0) -> int:
|
|
@@ -194,49 +260,66 @@ def query_hsmp(family: str, op: int, arg0: int = 0) -> tuple[int, list[int]]:
|
|
|
194
260
|
def read_smn(addr: int) -> int:
|
|
195
261
|
_require_init()
|
|
196
262
|
with _lock:
|
|
197
|
-
|
|
263
|
+
with pci_mutex_guard():
|
|
264
|
+
return _smn_read(addr)
|
|
198
265
|
|
|
199
266
|
|
|
200
267
|
def write_smn(addr: int, value: int) -> None:
|
|
201
268
|
_require_init()
|
|
202
269
|
with _lock:
|
|
203
|
-
|
|
270
|
+
with pci_mutex_guard():
|
|
271
|
+
_smn_write(addr, value)
|
|
204
272
|
|
|
205
273
|
|
|
206
274
|
def smu_command(msg_id: int, arg: int = 0) -> int:
|
|
207
275
|
_require_init()
|
|
208
276
|
msg, rsp, args = MP1_DEFAULT
|
|
209
277
|
with _lock:
|
|
210
|
-
|
|
278
|
+
with pci_mutex_guard():
|
|
279
|
+
return _mailbox_send(msg, rsp, args, msg_id, arg)
|
|
280
|
+
|
|
281
|
+
|
|
282
|
+
def get_smu_version(family: str = "") -> int:
|
|
283
|
+
if _backend is None:
|
|
284
|
+
return 0
|
|
285
|
+
status, out = query_mp1(family, 0x02, 1)
|
|
286
|
+
return (out[0] & 0xFFFFFFFF) if status == SMU_OK and out[0] else 0
|
|
211
287
|
|
|
212
288
|
|
|
213
289
|
def pm_table_supported(family: str = "") -> bool:
|
|
290
|
+
if _cached_pm_ver:
|
|
291
|
+
return True
|
|
214
292
|
return _backend != "iopci" and family in PM_TABLE_CMDS
|
|
215
293
|
|
|
216
294
|
|
|
217
295
|
def _transfer_with_retry(msg: int, rsp: int, args_base: int, op: int, arg0: int = 0,
|
|
218
|
-
delays: tuple[float, ...] = (0.
|
|
296
|
+
delays: tuple[float, ...] = (0.1, 0.5)) -> int:
|
|
219
297
|
def once() -> int:
|
|
220
298
|
with _lock:
|
|
221
|
-
|
|
299
|
+
with pci_mutex_guard():
|
|
300
|
+
return _mailbox_send(msg, rsp, args_base, op, arg0)
|
|
222
301
|
return transfer_with_retry(once, delays)
|
|
223
302
|
|
|
224
303
|
|
|
225
304
|
def read_pm_table_full(family: str = "") -> tuple[bytes, int] | None:
|
|
305
|
+
global _cached_pm_ver
|
|
226
306
|
if _backend in (None, "iopci") or family not in PM_TABLE_CMDS:
|
|
227
307
|
return None
|
|
228
308
|
ver_op, addr_op, transfer_op, addr_64bit, extra = PM_TABLE_CMDS[family]
|
|
229
309
|
msg, rsp, args_base = RSMU.get(family, RSMU_DEFAULT)
|
|
230
310
|
|
|
231
311
|
with _lock:
|
|
232
|
-
|
|
312
|
+
with pci_mutex_guard():
|
|
313
|
+
status, out = _mailbox_query(msg, rsp, args_base, ver_op)
|
|
233
314
|
if status != SMU_OK or not out[0]:
|
|
234
315
|
return None
|
|
235
|
-
ver = out[0]
|
|
316
|
+
ver = out[0] & 0xFFFFFFFF
|
|
317
|
+
_cached_pm_ver = ver
|
|
236
318
|
size = TABLE_SIZES.get(ver, DEFAULT_TABLE_SIZE)
|
|
237
319
|
|
|
238
320
|
with _lock:
|
|
239
|
-
|
|
321
|
+
with pci_mutex_guard():
|
|
322
|
+
status, out = _mailbox_query(msg, rsp, args_base, addr_op, extra)
|
|
240
323
|
if status != SMU_OK:
|
|
241
324
|
return None
|
|
242
325
|
phys_addr = (out[1] << 32) | out[0] if addr_64bit else out[0]
|
|
@@ -251,8 +334,26 @@ def read_pm_table_full(family: str = "") -> tuple[bytes, int] | None:
|
|
|
251
334
|
|
|
252
335
|
|
|
253
336
|
def read_pm_table_version(family: str = "") -> int:
|
|
337
|
+
global _cached_pm_ver
|
|
338
|
+
if _backend in (None, "iopci"):
|
|
339
|
+
return 0
|
|
340
|
+
if _cached_pm_ver:
|
|
341
|
+
return _cached_pm_ver
|
|
342
|
+
|
|
343
|
+
if family in PM_TABLE_CMDS:
|
|
344
|
+
ver_op = PM_TABLE_CMDS[family][0]
|
|
345
|
+
msg, rsp, args_base = RSMU.get(family, RSMU_DEFAULT)
|
|
346
|
+
with _lock:
|
|
347
|
+
with pci_mutex_guard():
|
|
348
|
+
status, out = _mailbox_query(msg, rsp, args_base, ver_op)
|
|
349
|
+
if status == SMU_OK and out[0]:
|
|
350
|
+
_cached_pm_ver = out[0] & 0xFFFFFFFF
|
|
351
|
+
return _cached_pm_ver
|
|
254
352
|
r = read_pm_table_full(family)
|
|
255
|
-
|
|
353
|
+
if r:
|
|
354
|
+
_cached_pm_ver = r[1] & 0xFFFFFFFF
|
|
355
|
+
return _cached_pm_ver
|
|
356
|
+
return 0
|
|
256
357
|
|
|
257
358
|
|
|
258
359
|
def read_pm_table(family: str = "") -> bytes | None:
|
|
@@ -261,7 +362,9 @@ def read_pm_table(family: str = "") -> bytes | None:
|
|
|
261
362
|
|
|
262
363
|
|
|
263
364
|
def close() -> None:
|
|
264
|
-
global _backend
|
|
365
|
+
global _backend, _cached_pm_ver, DRIVER_NAME
|
|
265
366
|
directhw.close()
|
|
266
367
|
iopci.close()
|
|
368
|
+
DRIVER_NAME = "DirectHW"
|
|
267
369
|
_backend = None
|
|
370
|
+
_cached_pm_ver = None
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
import time
|
|
3
|
+
from typing import Any
|
|
3
4
|
|
|
4
5
|
from zenmaster.smu import SMU_FAILED, SMU_REJECTED_PREREQ
|
|
5
6
|
|
|
6
7
|
NARGS = 6
|
|
8
|
+
DEFAULT_TIMEOUT: float = 5.0
|
|
7
9
|
|
|
8
10
|
MP1: dict[str, tuple[int, int, int]] = {
|
|
9
11
|
"SummitRidge": (0x3B10528, 0x3B10564, 0x3B10598),
|
|
@@ -49,41 +51,48 @@ HSMP: dict[str, tuple[int, int, int]] = {
|
|
|
49
51
|
HSMP_DEFAULT = (0x3B10A8C, 0x3B10A90, 0x3B10A94)
|
|
50
52
|
|
|
51
53
|
|
|
52
|
-
def poll_response(smn_read, rsp: int,
|
|
53
|
-
|
|
54
|
+
def poll_response(smn_read: Any, rsp: int, fast_poll: int = 64, poll_sleep: float = 0.0005,
|
|
55
|
+
timeout: float = DEFAULT_TIMEOUT) -> int:
|
|
56
|
+
for _ in range(fast_poll):
|
|
57
|
+
r = smn_read(rsp)
|
|
58
|
+
if r:
|
|
59
|
+
return r
|
|
60
|
+
deadline = time.monotonic() + timeout
|
|
61
|
+
while time.monotonic() < deadline:
|
|
62
|
+
time.sleep(poll_sleep)
|
|
54
63
|
r = smn_read(rsp)
|
|
55
64
|
if r:
|
|
56
65
|
return r
|
|
57
|
-
if i >= fast_poll:
|
|
58
|
-
time.sleep(poll_sleep)
|
|
59
66
|
return 0
|
|
60
67
|
|
|
61
68
|
|
|
62
|
-
def mailbox_send(smn_write, smn_read, msg: int, rsp: int, args_addr: int, op: int, arg0: int,
|
|
63
|
-
|
|
69
|
+
def mailbox_send(smn_write: Any, smn_read: Any, msg: int, rsp: int, args_addr: int, op: int, arg0: int,
|
|
70
|
+
fast_poll: int = 64, poll_sleep: float = 0.0005,
|
|
71
|
+
timeout: float = DEFAULT_TIMEOUT) -> int:
|
|
64
72
|
smn_write(rsp, 0)
|
|
65
73
|
smn_write(args_addr, arg0)
|
|
66
74
|
for i in range(1, NARGS):
|
|
67
75
|
smn_write(args_addr + i * 4, 0)
|
|
68
76
|
smn_write(msg, op)
|
|
69
|
-
return poll_response(smn_read, rsp,
|
|
77
|
+
return poll_response(smn_read, rsp, fast_poll, poll_sleep, timeout) or SMU_FAILED
|
|
70
78
|
|
|
71
79
|
|
|
72
|
-
def mailbox_query(smn_write, smn_read, msg: int, rsp: int, args_base: int, op: int, arg0: int,
|
|
73
|
-
|
|
80
|
+
def mailbox_query(smn_write: Any, smn_read: Any, msg: int, rsp: int, args_base: int, op: int, arg0: int,
|
|
81
|
+
fast_poll: int = 64, poll_sleep: float = 0.0005,
|
|
82
|
+
timeout: float = DEFAULT_TIMEOUT) -> tuple[int, list[int]]:
|
|
74
83
|
smn_write(rsp, 0)
|
|
75
84
|
for i in range(NARGS):
|
|
76
85
|
smn_write(args_base + i * 4, 0)
|
|
77
86
|
if arg0:
|
|
78
87
|
smn_write(args_base, arg0)
|
|
79
88
|
smn_write(msg, op)
|
|
80
|
-
r = poll_response(smn_read, rsp,
|
|
89
|
+
r = poll_response(smn_read, rsp, fast_poll, poll_sleep, timeout)
|
|
81
90
|
if r:
|
|
82
91
|
return r, [smn_read(args_base + i * 4) for i in range(NARGS)]
|
|
83
92
|
return SMU_FAILED, [0] * NARGS
|
|
84
93
|
|
|
85
94
|
|
|
86
|
-
def transfer_with_retry(send_once, delays: tuple[float, ...] = (0.
|
|
95
|
+
def transfer_with_retry(send_once, delays: tuple[float, ...] = (0.1, 0.5)) -> int:
|
|
87
96
|
status = send_once()
|
|
88
97
|
for delay in delays:
|
|
89
98
|
if status != SMU_REJECTED_PREREQ:
|
|
@@ -58,10 +58,8 @@ def _backend() -> Any:
|
|
|
58
58
|
|
|
59
59
|
def smu_command(msg_id: int, arg: int = 0) -> int:
|
|
60
60
|
b = _backend() if callable(_backend) else _backend
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
return fn(msg_id, arg)
|
|
64
|
-
ensure_backend()
|
|
61
|
+
if getattr(b, "active_backend", None) is not None and not ensure_backend():
|
|
62
|
+
return SMU_FAILED
|
|
65
63
|
b = _backend() if callable(_backend) else _backend
|
|
66
64
|
fn = getattr(b, "smu_command", None)
|
|
67
65
|
if fn is not None:
|
|
@@ -116,18 +114,21 @@ def pm_table_supported(family: str = "") -> bool:
|
|
|
116
114
|
|
|
117
115
|
|
|
118
116
|
def read_pm_table(family: str = "") -> bytes | None:
|
|
117
|
+
ensure_backend()
|
|
119
118
|
b = _backend()
|
|
120
119
|
fn = getattr(b, "read_pm_table", None)
|
|
121
120
|
return fn(family) if fn else None
|
|
122
121
|
|
|
123
122
|
|
|
124
123
|
def read_pm_table_version(family: str = "") -> int:
|
|
124
|
+
ensure_backend()
|
|
125
125
|
b = _backend()
|
|
126
126
|
fn = getattr(b, "read_pm_table_version", None)
|
|
127
127
|
return fn(family) if fn else 0
|
|
128
128
|
|
|
129
129
|
|
|
130
130
|
def read_pm_table_full(family: str = "") -> tuple[bytes, int] | None:
|
|
131
|
+
ensure_backend()
|
|
131
132
|
b = _backend()
|
|
132
133
|
fn = getattr(b, "read_pm_table_full", None)
|
|
133
134
|
if fn:
|
|
@@ -191,7 +192,9 @@ def send_arg(family: str, name: str, value: int) -> list[tuple[str, int, int]]:
|
|
|
191
192
|
from zenmaster.errors import UnsupportedCPU
|
|
192
193
|
if not runner.is_supported(family):
|
|
193
194
|
raise UnsupportedCPU(f"'{family}' is not a supported CPU family")
|
|
194
|
-
|
|
195
|
+
b = _backend() if callable(_backend) else _backend
|
|
196
|
+
if getattr(b, "active_backend", None) is not None and active_backend() is None:
|
|
197
|
+
init()
|
|
195
198
|
out: list[tuple[str, int, int]] = []
|
|
196
199
|
use_hsmp = runner.is_hsmp(family)
|
|
197
200
|
for is_mp1, op in runner.lookup(family, name):
|
|
@@ -225,16 +228,26 @@ def unavailable_reason() -> str | None:
|
|
|
225
228
|
|
|
226
229
|
|
|
227
230
|
def get_bios_if_ver(family: str) -> int:
|
|
228
|
-
ensure_backend()
|
|
231
|
+
if not ensure_backend():
|
|
232
|
+
return 0
|
|
229
233
|
status, out = query_mp1(family, 0x03, 0)
|
|
230
234
|
return out[0] if status == SMU_OK else 0
|
|
231
235
|
|
|
232
236
|
|
|
233
237
|
def get_smu_version(family: str) -> int:
|
|
234
|
-
ensure_backend()
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
+
if ensure_backend():
|
|
239
|
+
b = _backend()
|
|
240
|
+
fn = getattr(b, "get_smu_version", None)
|
|
241
|
+
if fn is not None:
|
|
242
|
+
try:
|
|
243
|
+
ver = fn(family)
|
|
244
|
+
if ver:
|
|
245
|
+
return ver
|
|
246
|
+
except Exception:
|
|
247
|
+
pass
|
|
248
|
+
status, out = query_mp1(family, 0x02, 1)
|
|
249
|
+
if status == SMU_OK and out[0]:
|
|
250
|
+
return out[0]
|
|
238
251
|
if platform.system() == "Linux":
|
|
239
252
|
for path in ("/sys/kernel/ryzen_smu_drv/version", "/sys/kernel/ryzen_smu_drv/drv_version"):
|
|
240
253
|
try:
|
|
@@ -11,7 +11,7 @@ from zenmaster.errors import BackendUnavailable, SMUNotInitialized
|
|
|
11
11
|
from zenmaster.pmtable import PM_TABLE_CMDS, TABLE_SIZES, DEFAULT_TABLE_SIZE
|
|
12
12
|
from zenmaster.mailbox import (
|
|
13
13
|
MP1, MP1_DEFAULT, RSMU, RSMU_DEFAULT, HSMP, HSMP_DEFAULT,
|
|
14
|
-
mailbox_send, mailbox_query, transfer_with_retry,
|
|
14
|
+
DEFAULT_TIMEOUT, mailbox_send, mailbox_query, transfer_with_retry,
|
|
15
15
|
)
|
|
16
16
|
from zenmaster.smu import SMU_OK, ModuleStatus
|
|
17
17
|
|
|
@@ -24,9 +24,9 @@ _DEVICE_PATHS = [
|
|
|
24
24
|
]
|
|
25
25
|
_IOCTL_LOAD = 0xA1B22084
|
|
26
26
|
_IOCTL_EXEC = 0xA1B22104
|
|
27
|
-
_POLL_N = 8192
|
|
28
27
|
_FAST_POLL = 64
|
|
29
28
|
_POLL_SLEEP = 0.0005
|
|
29
|
+
_TIMEOUT = DEFAULT_TIMEOUT
|
|
30
30
|
_lock = threading.Lock()
|
|
31
31
|
_handle = None
|
|
32
32
|
_k32 = None
|
|
@@ -56,6 +56,8 @@ def _query_pawnio_info() -> str | None:
|
|
|
56
56
|
return winreg.QueryValueEx(key, "DisplayVersion")[0]
|
|
57
57
|
except OSError:
|
|
58
58
|
return ""
|
|
59
|
+
finally:
|
|
60
|
+
winreg.CloseKey(key)
|
|
59
61
|
except OSError:
|
|
60
62
|
return None
|
|
61
63
|
|
|
@@ -81,7 +83,7 @@ def module_status() -> ModuleStatus:
|
|
|
81
83
|
|
|
82
84
|
|
|
83
85
|
def is_available() -> bool:
|
|
84
|
-
return _pawnio_info() is not None
|
|
86
|
+
return _handle is not None or _pawnio_info() is not None
|
|
85
87
|
|
|
86
88
|
|
|
87
89
|
def acquire_pci_mutex(timeout_ms: int = _PCI_MUTEX_TIMEOUT_MS) -> Any:
|
|
@@ -186,16 +188,18 @@ def init() -> str:
|
|
|
186
188
|
raise BackendUnavailable(f"PawnIO module not found: {module_path}")
|
|
187
189
|
|
|
188
190
|
ver = _pawnio_info()
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
"After installation, reboot and try again."
|
|
194
|
-
)
|
|
195
|
-
|
|
196
|
-
k32 = _make_k32()
|
|
191
|
+
try:
|
|
192
|
+
k32 = _make_k32()
|
|
193
|
+
except AttributeError as e:
|
|
194
|
+
raise BackendUnavailable("PawnIO driver is only supported on Windows.") from e
|
|
197
195
|
handle = _open_device(k32)
|
|
198
196
|
if handle is None:
|
|
197
|
+
if ver is None:
|
|
198
|
+
raise BackendUnavailable(
|
|
199
|
+
"PawnIO driver is not installed.\n"
|
|
200
|
+
f"Download and run the installer: {_PAWNIO_INSTALLER_URL}\n"
|
|
201
|
+
"After installation, reboot and try again."
|
|
202
|
+
)
|
|
199
203
|
raise BackendUnavailable(
|
|
200
204
|
"PawnIO device not found. The driver may need a reboot to activate.\n"
|
|
201
205
|
f"If not installed: {_PAWNIO_INSTALLER_URL}"
|
|
@@ -240,6 +244,10 @@ def _require_init() -> None:
|
|
|
240
244
|
raise SMUNotInitialized("PawnIO not initialized, call smu.init() first")
|
|
241
245
|
|
|
242
246
|
|
|
247
|
+
_cached_pm_ver: int | None = None
|
|
248
|
+
_cached_pm_size: int | None = None
|
|
249
|
+
|
|
250
|
+
|
|
243
251
|
def _execute(fn_name: str, in_args: list[int], out_count: int) -> list[int]:
|
|
244
252
|
fn_bytes = fn_name.encode("ascii")[:31]
|
|
245
253
|
name_buf = struct.pack("32s", fn_bytes)
|
|
@@ -265,6 +273,27 @@ def _execute(fn_name: str, in_args: list[int], out_count: int) -> list[int]:
|
|
|
265
273
|
return list(struct.unpack(f"<{count}q", out_buf.raw[: count * 8]))
|
|
266
274
|
|
|
267
275
|
|
|
276
|
+
def _execute_cmd(fn_name: str, in_args: list[int] | None = None) -> bool:
|
|
277
|
+
if in_args is None:
|
|
278
|
+
in_args = []
|
|
279
|
+
fn_bytes = fn_name.encode("ascii")[:31]
|
|
280
|
+
name_buf = struct.pack("32s", fn_bytes)
|
|
281
|
+
args_buf = struct.pack(f"<{len(in_args)}q", *in_args) if in_args else b""
|
|
282
|
+
payload = name_buf + args_buf
|
|
283
|
+
in_buf = ctypes.create_string_buffer(payload)
|
|
284
|
+
ret = ctypes.wintypes.DWORD(0)
|
|
285
|
+
|
|
286
|
+
ok = _k32.DeviceIoControl(
|
|
287
|
+
ctypes.wintypes.HANDLE(_handle),
|
|
288
|
+
ctypes.wintypes.DWORD(_IOCTL_EXEC),
|
|
289
|
+
ctypes.cast(in_buf, ctypes.c_void_p),
|
|
290
|
+
ctypes.wintypes.DWORD(len(payload)),
|
|
291
|
+
None, 0,
|
|
292
|
+
ctypes.byref(ret), None,
|
|
293
|
+
)
|
|
294
|
+
return bool(ok)
|
|
295
|
+
|
|
296
|
+
|
|
268
297
|
def _smn_read(addr: int) -> int:
|
|
269
298
|
result = _execute("ioctl_read_smu_register", [addr], 1)
|
|
270
299
|
return result[0] & 0xFFFFFFFF if result else 0
|
|
@@ -276,24 +305,61 @@ def _smn_write(addr: int, value: int) -> None:
|
|
|
276
305
|
|
|
277
306
|
def _mailbox_send(msg: int, rsp: int, args_addr: int, op: int, arg0: int) -> int:
|
|
278
307
|
return mailbox_send(_smn_write, _smn_read, msg, rsp, args_addr, op, arg0,
|
|
279
|
-
|
|
308
|
+
_FAST_POLL, _POLL_SLEEP, _TIMEOUT)
|
|
280
309
|
|
|
281
310
|
|
|
282
311
|
def _mailbox_query(msg: int, rsp: int, args_base: int, op: int, arg0: int = 0) -> tuple[int, list[int]]:
|
|
283
312
|
return mailbox_query(_smn_write, _smn_read, msg, rsp, args_base, op, arg0,
|
|
284
|
-
|
|
313
|
+
_FAST_POLL, _POLL_SLEEP, _TIMEOUT)
|
|
314
|
+
|
|
315
|
+
|
|
316
|
+
def _resolve_pm_table() -> tuple[int, int] | None:
|
|
317
|
+
_require_init()
|
|
318
|
+
import time
|
|
319
|
+
for delay in (0.0, 0.1, 0.5):
|
|
320
|
+
if delay:
|
|
321
|
+
time.sleep(delay)
|
|
322
|
+
with _lock:
|
|
323
|
+
with pci_mutex_guard():
|
|
324
|
+
out = _execute("ioctl_resolve_pm_table", [], 2)
|
|
325
|
+
if len(out) == 2 and out[0] != 0:
|
|
326
|
+
return (out[0] & 0xFFFFFFFF), out[1]
|
|
327
|
+
return None
|
|
328
|
+
|
|
329
|
+
|
|
330
|
+
def _update_pm_table(family: str = "") -> bool:
|
|
331
|
+
_require_init()
|
|
332
|
+
import time
|
|
333
|
+
for delay in (0.0, 0.1, 0.5):
|
|
334
|
+
if delay:
|
|
335
|
+
time.sleep(delay)
|
|
336
|
+
with _lock:
|
|
337
|
+
with pci_mutex_guard():
|
|
338
|
+
ok = _execute_cmd("ioctl_update_pm_table")
|
|
339
|
+
if ok:
|
|
340
|
+
return True
|
|
341
|
+
if family in PM_TABLE_CMDS:
|
|
342
|
+
_ver_op, _addr_op, transfer_op, _addr_64bit, extra = PM_TABLE_CMDS[family]
|
|
343
|
+
msg, rsp, args_base = RSMU.get(family, RSMU_DEFAULT)
|
|
344
|
+
status = _transfer_with_retry(msg, rsp, args_base, transfer_op, extra)
|
|
345
|
+
return status == SMU_OK
|
|
346
|
+
return False
|
|
285
347
|
|
|
286
348
|
|
|
287
349
|
def _read_physical_memory(phys_addr: int, size: int) -> bytes | None:
|
|
288
|
-
n
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
350
|
+
n = min((size + 7) // 8, 512)
|
|
351
|
+
with _lock:
|
|
352
|
+
raw = _execute("ioctl_read_pm_table", [], n)
|
|
353
|
+
if raw:
|
|
354
|
+
data = struct.pack(f"<{len(raw)}q", *raw)[:size]
|
|
355
|
+
if len(data) < size:
|
|
356
|
+
data = data + b"\x00" * (size - len(data))
|
|
357
|
+
return data
|
|
292
358
|
return None
|
|
293
359
|
|
|
294
360
|
|
|
295
361
|
def _transfer_with_retry(msg: int, rsp: int, args_base: int, op: int, arg0: int = 0,
|
|
296
|
-
delays: tuple[float, ...] = (0.
|
|
362
|
+
delays: tuple[float, ...] = (0.1, 0.5)) -> int:
|
|
297
363
|
def once() -> int:
|
|
298
364
|
with _lock:
|
|
299
365
|
with pci_mutex_guard():
|
|
@@ -363,43 +429,105 @@ def smu_command(msg_id: int, arg: int = 0) -> int:
|
|
|
363
429
|
return _mailbox_send(msg, rsp, args, msg_id, arg)
|
|
364
430
|
|
|
365
431
|
|
|
432
|
+
def get_smu_version(family: str = "") -> int:
|
|
433
|
+
if _handle is None or _k32 is None:
|
|
434
|
+
return 0
|
|
435
|
+
with _lock:
|
|
436
|
+
with pci_mutex_guard():
|
|
437
|
+
out = _execute("ioctl_get_smu_version", [], 1)
|
|
438
|
+
if out and out[0]:
|
|
439
|
+
return out[0] & 0xFFFFFFFF
|
|
440
|
+
msg, rsp, args = MP1.get(family, MP1_DEFAULT)
|
|
441
|
+
with _lock:
|
|
442
|
+
with pci_mutex_guard():
|
|
443
|
+
status, res = _mailbox_query(msg, rsp, args, 0x02, 1)
|
|
444
|
+
return (res[0] & 0xFFFFFFFF) if status == SMU_OK and res[0] else 0
|
|
445
|
+
|
|
446
|
+
|
|
366
447
|
def pm_table_supported(family: str = "") -> bool:
|
|
448
|
+
if _cached_pm_ver:
|
|
449
|
+
return True
|
|
367
450
|
return family in PM_TABLE_CMDS
|
|
368
451
|
|
|
369
452
|
|
|
370
|
-
def
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
453
|
+
def _cache_pm_ver(ver: int) -> int:
|
|
454
|
+
global _cached_pm_ver, _cached_pm_size
|
|
455
|
+
_cached_pm_ver = ver & 0xFFFFFFFF
|
|
456
|
+
_cached_pm_size = TABLE_SIZES.get(_cached_pm_ver, DEFAULT_TABLE_SIZE)
|
|
457
|
+
return _cached_pm_ver
|
|
375
458
|
|
|
459
|
+
|
|
460
|
+
def _read_pm_ver_rsmu(family: str = "") -> int:
|
|
461
|
+
if _handle is None or _k32 is None or family not in PM_TABLE_CMDS:
|
|
462
|
+
return 0
|
|
463
|
+
ver_op = PM_TABLE_CMDS[family][0]
|
|
464
|
+
msg, rsp, args_base = RSMU.get(family, RSMU_DEFAULT)
|
|
376
465
|
with _lock:
|
|
377
466
|
with pci_mutex_guard():
|
|
378
467
|
status, out = _mailbox_query(msg, rsp, args_base, ver_op)
|
|
379
|
-
if status
|
|
380
|
-
return None
|
|
381
|
-
ver = out[0]
|
|
382
|
-
size = TABLE_SIZES.get(ver, DEFAULT_TABLE_SIZE)
|
|
468
|
+
return (out[0] & 0xFFFFFFFF) if status == SMU_OK and out[0] else 0
|
|
383
469
|
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
if status != SMU_OK:
|
|
470
|
+
|
|
471
|
+
def read_pm_table_full(family: str = "") -> tuple[bytes, int] | None:
|
|
472
|
+
if _handle is None or _k32 is None:
|
|
388
473
|
return None
|
|
389
|
-
|
|
390
|
-
|
|
474
|
+
|
|
475
|
+
global _cached_pm_ver, _cached_pm_size
|
|
476
|
+
|
|
477
|
+
if _cached_pm_ver is None:
|
|
478
|
+
resolved = _resolve_pm_table()
|
|
479
|
+
if resolved is not None and resolved[0]:
|
|
480
|
+
_cache_pm_ver(resolved[0])
|
|
481
|
+
elif family in PM_TABLE_CMDS:
|
|
482
|
+
ver = _read_pm_ver_rsmu(family)
|
|
483
|
+
if ver:
|
|
484
|
+
_cache_pm_ver(ver)
|
|
485
|
+
|
|
486
|
+
if _cached_pm_ver is None or not _cached_pm_ver:
|
|
391
487
|
return None
|
|
392
488
|
|
|
393
|
-
|
|
394
|
-
if
|
|
489
|
+
ver = _cached_pm_ver & 0xFFFFFFFF
|
|
490
|
+
if _cached_pm_size is None:
|
|
491
|
+
_cached_pm_size = TABLE_SIZES.get(ver, DEFAULT_TABLE_SIZE)
|
|
492
|
+
size = _cached_pm_size
|
|
493
|
+
|
|
494
|
+
_update_pm_table(family)
|
|
495
|
+
|
|
496
|
+
n = min((size + 7) // 8, 512)
|
|
497
|
+
with _lock:
|
|
498
|
+
raw = _execute("ioctl_read_pm_table", [], n)
|
|
499
|
+
|
|
500
|
+
if not raw:
|
|
501
|
+
resolved = _resolve_pm_table()
|
|
502
|
+
if resolved is not None and resolved[0]:
|
|
503
|
+
ver = _cache_pm_ver(resolved[0])
|
|
504
|
+
size = _cached_pm_size or DEFAULT_TABLE_SIZE
|
|
505
|
+
n = min((size + 7) // 8, 512)
|
|
506
|
+
_update_pm_table(family)
|
|
507
|
+
with _lock:
|
|
508
|
+
raw = _execute("ioctl_read_pm_table", [], n)
|
|
509
|
+
|
|
510
|
+
if not raw:
|
|
395
511
|
return None
|
|
396
|
-
|
|
397
|
-
|
|
512
|
+
|
|
513
|
+
data = struct.pack(f"<{len(raw)}q", *raw)[:size]
|
|
514
|
+
if len(data) < size:
|
|
515
|
+
data = data + b"\x00" * (size - len(data))
|
|
516
|
+
return (data, ver)
|
|
398
517
|
|
|
399
518
|
|
|
400
519
|
def read_pm_table_version(family: str = "") -> int:
|
|
401
|
-
|
|
402
|
-
|
|
520
|
+
if _cached_pm_ver:
|
|
521
|
+
return _cached_pm_ver
|
|
522
|
+
if _handle is None or _k32 is None:
|
|
523
|
+
return 0
|
|
524
|
+
resolved = _resolve_pm_table()
|
|
525
|
+
if resolved and resolved[0]:
|
|
526
|
+
return _cache_pm_ver(resolved[0])
|
|
527
|
+
ver = _read_pm_ver_rsmu(family)
|
|
528
|
+
if ver:
|
|
529
|
+
return _cache_pm_ver(ver)
|
|
530
|
+
return 0
|
|
403
531
|
|
|
404
532
|
|
|
405
533
|
def read_pm_table(family: str = "") -> bytes | None:
|
|
@@ -408,9 +536,12 @@ def read_pm_table(family: str = "") -> bytes | None:
|
|
|
408
536
|
|
|
409
537
|
|
|
410
538
|
def close() -> None:
|
|
411
|
-
global _handle, _k32, _pawnio_info_cache
|
|
539
|
+
global _handle, _k32, _pawnio_info_cache, _cached_pm_ver, _cached_pm_size
|
|
412
540
|
if _handle is not None and _k32 is not None:
|
|
413
541
|
_k32.CloseHandle(ctypes.wintypes.HANDLE(_handle))
|
|
414
542
|
_handle = None
|
|
415
543
|
_k32 = None
|
|
416
544
|
_pawnio_info_cache = _UNSET
|
|
545
|
+
_cached_pm_ver = None
|
|
546
|
+
_cached_pm_size = None
|
|
547
|
+
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: zenmaster
|
|
3
|
-
Version: 1.2.
|
|
3
|
+
Version: 1.2.2
|
|
4
4
|
Summary: Adjust power management settings for Ryzen CPUs on Linux, Windows, and macOS
|
|
5
5
|
Author-email: oxGorou <lekhanhbinh2901@gmail.com>
|
|
6
6
|
License-Expression: GPL-3.0-only
|
|
@@ -193,7 +193,7 @@ print(f"SMN 0x0005A380 = 0x{value:08X}")
|
|
|
193
193
|
|
|
194
194
|
## Technical Comparison: ZenMaster vs. RyzenAdj
|
|
195
195
|
|
|
196
|
-
| Feature | RyzenAdj | ZenMaster v1.2.
|
|
196
|
+
| Feature | RyzenAdj | ZenMaster v1.2.2 |
|
|
197
197
|
|---|---|---|
|
|
198
198
|
| **Installation** | Compilation from source (`cmake`, `make`, `libpci-dev`) | `pip install zenmaster` (pure Python package) |
|
|
199
199
|
| **Supported Families** | ~15 APU and desktop families | 44 families (Zen 1 to Zen 5/6, Desktop, Mobile, Threadripper, Server EPYC via HSMP) |
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|