Releases: HorizonUnix/ZenMaster
Releases · HorizonUnix/ZenMaster
Release list
v0.6.0
Added
CpuInfo.cpu_stepping_int(defaults to0).resolve()takes it as an optional arg anddetect()fills it in on all three platformszenmaster.init()re-exported at top level.smu.read_pm_table_full(family). Every backend was running the full mailbox sequence twice perread_pm_sensors()call: once forread_pm_table(), once again forread_pm_table_version(), because they were written as two separate functions that each redid the same version/address/transfer round trip. Now there's one fetch, and both older functions call into it.- Linux
ModuleStatus.reasoncan benot_installedorunsignednow, not justnot_loaded. Previously "never installed," "installed but unsigned," and "installed but idle" all collapsed into the same reason, so anyone consuming this had to shell out tomodinfothemselves to tell them apart. smu.close()(also at top level aszenmaster.close()) releases whatever the active backend is holding: the Windows PawnIO handle, macOS's DirectHW/IOKit connection, or the Linux SMU file descriptor. Nothing closed these before; a long-running process (a daemon that wants to drop and reacquire access) had no way to let go of them.- More of
smu's surface is re-exported at the top level:active_backend,pm_table_supported,send_mp1,send_rsmu,query_mp1,query_rsmu,read_pm_table,read_pm_table_version,read_pm_table_full. The CLI already needed all of these; nowimport zenmastergets you the same surface without dropping down tozenmaster.smu. BackendUnavailablenow appends a link to the wiki's Installation guide to whatever message it's raised with. Every "PawnIO isn't installed" / "ryzen_smu is too old" / "DirectHW isn't loaded" error now points somewhere with the actual setup steps, instead of leaving you to go find them.
Removed
AMDFamily0F.bin,AMDFamily10.bin,AMDFamily17.bin,AMDReset.bin.
Changed
send_arg()raisesUnsupportedCPUfor a family with no SMU support.send_arg()no longer swallows every exception as a hardware rejection. OnlyOSError,struct.error, andctypes.ArgumentErrorcount as SMU_FAILED now. A real bug in a backend used to look identical to the SMU just saying no.- CLI shows help instead of doing nothing when you run it with no action flag and no tuning args.
zenmaster --jsonby itself used to just exit 0 silently. hardware.detect()caches its result for the life of the process. It used to re-parse/proc/cpuinfo,PROCESSOR_IDENTIFIER, orsysctland recompute the codename on every single call, which is why callers (U4L included) kept building their own memoization around it.resolve()is untouched and still takes whatever you pass it every time, since that's the one meant for feeding in values you didn't read off the real CPU.- The mailbox send/query/poll/retry logic that macOS and Windows each had their own near-identical copy of now lives once in
zenmaster/mailbox.py. Linux already shared its two backends this way; the other two platforms didn't. - All three backends now check "has
init()run yet" the same way: a bare_require_init()guard called at the top ofsend_mp1/send_rsmu/query_mp1/query_rsmu, raisingSMUNotInitializedbefore any hardware I/O happens.
Fixed
- Windows: the PawnIO registry lookup (
_pawnio_info) was a separate registry read formodule_version(),module_version_ok(),module_status(), andis_available()each. It's read once and cached now. - Windows:
read_pm_table_fulldidn't check for a zero table version, unlike Linux and macOS, so a rejected version query could fall through and read from a garbage address instead of returningNone. apply()now counts an unparseable value (--stapm-limit=notanumber) as a rejection. It already returned an error result for this case, buthad_rejectionstayedFalse, so a caller only checking that flag would think the preset applied cleanly.
v0.5.0
Added
- macOS backend (AMD Hackintosh): CPU detection via
sysctl, and SMU access through DirectHW.kext, with a kext-free fallback over IOPCIBridge (tuning only, no PM table) - CLI
--iopci(macOS only) forces the IOPCIBridge path instead of DirectHW zenmaster.hardware.detect()readsmachdep.cpu.family/machdep.cpu.model/machdep.cpu.brand_stringon macOS instead of/proc/cpuinfo
Changed
--info(text and--json) omits theBackend/Driverfields entirely when the SMU hasn't been initialized, instead of printing "not initialized"--table,--dump-table, and--sensorspoint to DirectHW.kext specifically when they fail on the IOPCIBridge path, instead of a generic "not available" message
v0.4.0
Added
- CLI
--sensors(and--sensors --json): compact live readout of temp, load, socket power, iGPU clock/temp, and memory clock - CLI
--infonow shows aDriverline with the driver name, version, and status (orPCI direct access); same data under--info --json smu.read_pm_sensors(family)reads and decodes the PM table in one call, returning aPmSensors(orNone).table.read_sensors(data, ver)decodes raw bytes you already havesmu.module_status()returns oneModuleStatusverdict for the driver (ok,version,min_version,reason). Helpers:module_version(),module_version_ok(),is_available(),secure_boot_enabled(),driver_name()smu.ensure_backend(): likeinit()but returns the backend orNoneinstead of raisingsmu.unavailable_reason(): the message explaining why the SMU can't be used, orNonewhen it cansmu.send_arg(family, name, value)sends one value to every mailbox the arg maps to, returning[(mailbox, opcode, status), ...]resolve(name, cpu_family, cpu_model)builds aCpuInfofrom explicit values without reading/proc/cpuinforunner.is_supported(family)- All of the above are re-exported at the top level, so apps don't import from
zenmaster.linux
Changed
- Linux now checks the
ryzen_smuversion (drv_version) and requires>= 0.1.7, with the installed version in the error - Backend selection follows Secure Boot: off uses PCI direct access, on uses
ryzen_smu. A loaded module is no longer preferred over PCI when Secure Boot is off - On Windows, the driver is PawnIO with no version requirement, and Secure Boot does not apply
v0.3.0
Added
get-*query commands now return the value the SMU reports, shown in the CLI and in--jsonas areturnedfield;smu.query_mp1()/smu.query_rsmu()expose the raw read-back for library users--versionflag, which prints the installed version and checks PyPI for a newer release;zenmaster.check_update()exposes the same check to library users- Exception hierarchy for library users:
ZenMasterError(base) withBackendUnavailable,SMUNotInitialized,UnsupportedCPU, all still subclassRuntimeError, so existingexcept RuntimeErrorkeeps working smu.SmuStatusIntEnum for SMU status codes (SMU_OKetc. now alias it)ApplyResultTypedDict documenting theapply()result contractpy.typedmarker so downstream type-checkers use the bundled type hints- PM-table support for the Raven/Picasso-era APUs (RavenRidge, Picasso, Dali, Pollock), which use a distinct table opcode set
- Linux can now read the PM table over PCI direct access (via
/dev/mem) whenryzen_smuis not loaded
Changed
tdc-limitandedc-limitnow have descriptions and appear under "VRM & Currents" in--helpinstead of "Unknown command"- Moved the MP1/RSMU mailbox tables, SMU status codes, and PM-table metadata into shared modules (
mailbox.py,pmtable.py) - Unified the Linux register send/poll path into one routine shared by the
ryzen_smuand PCI backends; simplifiedapply()token parsing - Removed unused
runnerhelpers (get_socket_short,has_smu_support,get_commands)
Fixed
- Windows
--table/--dump-tablenow work on VanGogh (Steam Deck) and Mendocino, which were missing from the PM-table family list
v0.2.0
Added
- Public library API:
from zenmaster import detect, apply, runner, smu, CpuInfo runner.is_flag_arg()to query args that take no valuevrmcvip-current(VanGogh) for full RyzenAdj arg parity
Changed
- Linux PCI backend now probes writability with a
0x47register round-trip
(like UXTU4Linux) instead of only checking that the config file exists, so a
non-writable bus (no root, lockdown) fails at init with a clear message - Unified project description to "Adjust power management settings for Ryzen
CPUs on Linux and Windows" - Rewrote README with a fuller overview, compatibility table, and an accurate
RyzenAdj comparison - Args that require a value now report an error (or show help on the CLI)
when passed without=value, instead of silently sending 0 apply()results always include a consistent set of keys (erroris
Noneon success), making the return shape stable for consumers- Argument names are normalised to lowercase/hyphen form in
apply()results - Modernised packaging to PEP 639 SPDX license metadata
Fixed
- Windows PM table read now aborts if the final table-transfer retry is still
rejected, instead of reading stale/garbage physical memory - Windows SMU polling sleeps briefly after a fast initial spin, avoiding a busy
loop that pinned a core while waiting for the mailbox --skin-temp-limit(a power limit in mW) is no longer multiplied by 256;
only the temperature argsapu-skin-tempanddgpu-skin-tempare scaled,
matching RyzenAdj- Negative values (e.g. Curve Optimiser
--set-coall=-20) now wrap to
unsigned 32-bit like RyzenAdj, instead of being clamped to 0 - Flag args (
--max-performance,--enable-oc, …) always send 0, ignoring any
value passed, matching RyzenAdj's boolean handling smu.send_*now raise a clear error if called beforesmu.init()smu.init()is idempotent and no longer leaks the Windows PawnIO handle- CLI shows help for a malformed argument before requiring root
apply()handles malformed preset strings (unclosed quotes) gracefully--tableno longer shows duplicate TDC values in EDC rows on Zen 5 tables
v0.1.1
Fixed
- Workflow and packaging fixes for initial PyPI release