├── icon.png ├── ZenStates for Linux v1.0_006.png ├── togglecode.py ├── LICENSE ├── .gitignore ├── README.md ├── cpuid.py └── zenstates.py /icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/irusanov/ZenStates-Linux/HEAD/icon.png -------------------------------------------------------------------------------- /ZenStates for Linux v1.0_006.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/irusanov/ZenStates-Linux/HEAD/ZenStates for Linux v1.0_006.png -------------------------------------------------------------------------------- /togglecode.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | import portio 3 | 4 | portio.iopl(3) 5 | portio.ioperm(0x2E, 2, 1) 6 | 7 | portio.outb_p(0x87, 0x2E) 8 | portio.outb_p(0x01, 0x2E) 9 | portio.outb_p(0x55, 0x2E) 10 | portio.outb_p(0x55, 0x2E) 11 | portio.outb_p(0x07, 0x2E) 12 | portio.outb_p(0x03, 0x2F) 13 | portio.outb_p(0xF0, 0x2E) 14 | f = portio.inb_p(0x2F) 15 | f ^= 0x08 16 | portio.outb_p(f, 0x2F) 17 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Thiago Ramon Gonçalves Montoya 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | env/ 12 | build/ 13 | develop-eggs/ 14 | dist/ 15 | downloads/ 16 | eggs/ 17 | .eggs/ 18 | lib/ 19 | lib64/ 20 | parts/ 21 | sdist/ 22 | var/ 23 | *.egg-info/ 24 | .installed.cfg 25 | *.egg 26 | 27 | # PyInstaller 28 | # Usually these files are written by a python script from a template 29 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 30 | *.manifest 31 | *.spec 32 | 33 | # Installer logs 34 | pip-log.txt 35 | pip-delete-this-directory.txt 36 | 37 | # Unit test / coverage reports 38 | htmlcov/ 39 | .tox/ 40 | .coverage 41 | .coverage.* 42 | .cache 43 | nosetests.xml 44 | coverage.xml 45 | *,cover 46 | .hypothesis/ 47 | 48 | # Translations 49 | *.mo 50 | *.pot 51 | 52 | # Django stuff: 53 | *.log 54 | local_settings.py 55 | 56 | # Flask stuff: 57 | instance/ 58 | .webassets-cache 59 | 60 | # Scrapy stuff: 61 | .scrapy 62 | 63 | # Sphinx documentation 64 | docs/_build/ 65 | 66 | # PyBuilder 67 | target/ 68 | 69 | # IPython Notebook 70 | .ipynb_checkpoints 71 | 72 | # pyenv 73 | .python-version 74 | 75 | # celery beat schedule file 76 | celerybeat-schedule 77 | 78 | # dotenv 79 | .env 80 | 81 | # virtualenv 82 | venv/ 83 | ENV/ 84 | 85 | # Spyder project settings 86 | .spyderproject 87 | 88 | # Rope project settings 89 | .ropeproject 90 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ZenStates-Linux 2 | Collection of utilities for Ryzen processors and motherboards 3 | 4 | ## zenstates.py 5 | Dynamically edit AMD Ryzen processor parameters. 6 | Current version supports Zen2-based CPUs only. 7 | 8 | Requires root access and the msr kernel module loaded (just run "modprobe msr" as root). 9 | ```console 10 | $ sudo modprobe msr 11 | ``` 12 | 13 | The utility is based on [r4m0n's ZenStates-Linux](https://github.com/r4m0n/ZenStates-Linux). 14 | 15 | GUI is based on [PySimpleGUI](https://pypi.org/project/PySimpleGUI/). 16 | 17 | CPUID module used: [flababah's cpuid.py](https://github.com/flababah/cpuid.py). 18 | 19 | ## CLI 20 | ```console 21 | $ sudo ./zenstates.py --no-gui [args...] 22 | ``` 23 | 24 | usage: zenstates.py [-h] [--no-gui] [-l] [-p {0,1,2,3,4,5,6,7}] [--enable] [--disable] [-f FID] [-d DID] [-v VID] 25 | [--smu-test-message] 26 | 27 | Sets parameters of Ryzen processors 28 | 29 | required arguments: 30 | --no-gui Run in CLI without GUI 31 | 32 | optional arguments: 33 | -h, --help Show this help message and exit 34 | -l, --list List all P-States 35 | -p {0,1,2,3,4,5,6,7}, --pstate {0,1,2,3,4,5,6,7} 36 | P-State to set 37 | --enable Enable P-State 38 | --disable Disable P-State 39 | -f FID, --fid FID FID to set (in hex) 40 | -d DID, --did DID DID to set (in hex) 41 | -v VID, --vid VID VID to set (in hex) 42 | --c6-enable Enable C-State C6 43 | --c6-disable Disable C-State C6 44 | --smu-test-message Send test message to the SMU (response 1 means 'success') 45 | --oc-frequency Set Overclock frequency (in MHz) 46 | --oc-vid Set Overclock VID (in hex) 47 | --ppt Set PPT limit (in W) 48 | --tdc Set TDC limit (in A) 49 | --edc Set EDC limit (in A) 50 | 51 | ## GUI 52 | ![Screenshot](ZenStates%20for%20Linux%20v1.0_006.png?raw=true "ZenStates for Linux screenshot") 53 | 54 | To run the GUI, additional packages are needed: 55 | ```console 56 | $ sudo apt install pip3 python3-tk wheel 57 | $ pip3 install pysimplegui 58 | ``` 59 | 60 | Then run: 61 | ```console 62 | $ sudo python3 zenstates.py 63 | ``` 64 | 65 | ## togglecode.py 66 | Turns on/off the Q-Code display on ASUS Crosshair VI Hero motherboards (and other boards with a compatible Super I/O chip) 67 | 68 | Requires root access and the portio python module. 69 | To install run: 70 | ```console 71 | $ pip install wheel portio 72 | ``` 73 | -------------------------------------------------------------------------------- /cpuid.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # Copyright (c) 2018 Anders Høst 4 | # 5 | 6 | from __future__ import print_function 7 | 8 | import platform 9 | import os 10 | import ctypes 11 | from ctypes import c_uint32, c_int, c_long, c_ulong, c_size_t, c_void_p, POINTER, CFUNCTYPE 12 | 13 | # Posix x86_64: 14 | # Three first call registers : RDI, RSI, RDX 15 | # Volatile registers : RAX, RCX, RDX, RSI, RDI, R8-11 16 | 17 | # Windows x86_64: 18 | # Three first call registers : RCX, RDX, R8 19 | # Volatile registers : RAX, RCX, RDX, R8-11 20 | 21 | # cdecl 32 bit: 22 | # Three first call registers : Stack (%esp) 23 | # Volatile registers : EAX, ECX, EDX 24 | 25 | _POSIX_64_OPC = [ 26 | 0x53, # push %rbx 27 | 0x89, 0xf0, # mov %esi,%eax 28 | 0x89, 0xd1, # mov %edx,%ecx 29 | 0x0f, 0xa2, # cpuid 30 | 0x89, 0x07, # mov %eax,(%rdi) 31 | 0x89, 0x5f, 0x04, # mov %ebx,0x4(%rdi) 32 | 0x89, 0x4f, 0x08, # mov %ecx,0x8(%rdi) 33 | 0x89, 0x57, 0x0c, # mov %edx,0xc(%rdi) 34 | 0x5b, # pop %rbx 35 | 0xc3 # retq 36 | ] 37 | 38 | _WINDOWS_64_OPC = [ 39 | 0x53, # push %rbx 40 | 0x89, 0xd0, # mov %edx,%eax 41 | 0x49, 0x89, 0xc9, # mov %rcx,%r9 42 | 0x44, 0x89, 0xc1, # mov %r8d,%ecx 43 | 0x0f, 0xa2, # cpuid 44 | 0x41, 0x89, 0x01, # mov %eax,(%r9) 45 | 0x41, 0x89, 0x59, 0x04, # mov %ebx,0x4(%r9) 46 | 0x41, 0x89, 0x49, 0x08, # mov %ecx,0x8(%r9) 47 | 0x41, 0x89, 0x51, 0x0c, # mov %edx,0xc(%r9) 48 | 0x5b, # pop %rbx 49 | 0xc3 # retq 50 | ] 51 | 52 | _CDECL_32_OPC = [ 53 | 0x53, # push %ebx 54 | 0x57, # push %edi 55 | 0x8b, 0x7c, 0x24, 0x0c, # mov 0xc(%esp),%edi 56 | 0x8b, 0x44, 0x24, 0x10, # mov 0x10(%esp),%eax 57 | 0x8b, 0x4c, 0x24, 0x14, # mov 0x14(%esp),%ecx 58 | 0x0f, 0xa2, # cpuid 59 | 0x89, 0x07, # mov %eax,(%edi) 60 | 0x89, 0x5f, 0x04, # mov %ebx,0x4(%edi) 61 | 0x89, 0x4f, 0x08, # mov %ecx,0x8(%edi) 62 | 0x89, 0x57, 0x0c, # mov %edx,0xc(%edi) 63 | 0x5f, # pop %edi 64 | 0x5b, # pop %ebx 65 | 0xc3 # ret 66 | ] 67 | 68 | is_windows = os.name == "nt" 69 | is_64bit = ctypes.sizeof(ctypes.c_voidp) == 8 70 | 71 | class CPUID_struct(ctypes.Structure): 72 | _fields_ = [(r, c_uint32) for r in ("eax", "ebx", "ecx", "edx")] 73 | 74 | class CPUID(object): 75 | def __init__(self): 76 | if platform.machine() not in ("AMD64", "x86_64", "x86", "i686"): 77 | raise SystemError("Only available for x86") 78 | 79 | if is_windows: 80 | if is_64bit: 81 | # VirtualAlloc seems to fail under some weird 82 | # circumstances when ctypes.windll.kernel32 is 83 | # used under 64 bit Python. CDLL fixes this. 84 | self.win = ctypes.CDLL("kernel32.dll") 85 | opc = _WINDOWS_64_OPC 86 | else: 87 | # Here ctypes.windll.kernel32 is needed to get the 88 | # right DLL. Otherwise it will fail when running 89 | # 32 bit Python on 64 bit Windows. 90 | self.win = ctypes.windll.kernel32 91 | opc = _CDECL_32_OPC 92 | else: 93 | opc = _POSIX_64_OPC if is_64bit else _CDECL_32_OPC 94 | 95 | size = len(opc) 96 | code = (ctypes.c_ubyte * size)(*opc) 97 | 98 | if is_windows: 99 | self.win.VirtualAlloc.restype = c_void_p 100 | self.win.VirtualAlloc.argtypes = [ctypes.c_void_p, ctypes.c_size_t, ctypes.c_ulong, ctypes.c_ulong] 101 | self.addr = self.win.VirtualAlloc(None, size, 0x1000, 0x40) 102 | if not self.addr: 103 | raise MemoryError("Could not allocate RWX memory") 104 | else: 105 | self.libc = ctypes.cdll.LoadLibrary(None) 106 | self.libc.valloc.restype = ctypes.c_void_p 107 | self.libc.valloc.argtypes = [ctypes.c_size_t] 108 | self.addr = self.libc.valloc(size) 109 | if not self.addr: 110 | raise MemoryError("Could not allocate memory") 111 | 112 | self.libc.mprotect.restype = c_int 113 | self.libc.mprotect.argtypes = [c_void_p, c_size_t, c_int] 114 | ret = self.libc.mprotect(self.addr, size, 1 | 2 | 4) 115 | if ret != 0: 116 | raise OSError("Failed to set RWX") 117 | 118 | 119 | ctypes.memmove(self.addr, code, size) 120 | 121 | func_type = CFUNCTYPE(None, POINTER(CPUID_struct), c_uint32, c_uint32) 122 | self.func_ptr = func_type(self.addr) 123 | 124 | def __call__(self, eax, ecx=0): 125 | struct = CPUID_struct() 126 | self.func_ptr(struct, eax, ecx) 127 | return struct.eax, struct.ebx, struct.ecx, struct.edx 128 | 129 | def __del__(self): 130 | if is_windows: 131 | self.win.VirtualFree.restype = c_long 132 | self.win.VirtualFree.argtypes = [c_void_p, c_size_t, c_ulong] 133 | self.win.VirtualFree(self.addr, 0, 0x8000) 134 | elif self.libc: 135 | # Seems to throw exception when the program ends and 136 | # libc is cleaned up before the object? 137 | self.libc.free.restype = None 138 | self.libc.free.argtypes = [c_void_p] 139 | self.libc.free(self.addr) 140 | 141 | if __name__ == "__main__": 142 | def valid_inputs(): 143 | cpuid = CPUID() 144 | for eax in (0x0, 0x80000000): 145 | highest, _, _, _ = cpuid(eax) 146 | while eax <= highest: 147 | regs = cpuid(eax) 148 | yield (eax, regs) 149 | eax += 1 150 | 151 | print(" ".join(x.ljust(8) for x in ("CPUID", "A", "B", "C", "D")).strip()) 152 | for eax, regs in valid_inputs(): 153 | print("%08x" % eax, " ".join("%08x" % reg for reg in regs)) 154 | 155 | -------------------------------------------------------------------------------- /zenstates.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import struct 3 | import os 4 | import glob 5 | import argparse 6 | import cpuid 7 | import ctypes 8 | 9 | APP_NAME = 'ZenStates for Linux' 10 | APP_VERSION = 1.5 11 | 12 | FID_MAX = 0xFF 13 | FID_MIN = 0x10 14 | DID_MIN = 0x02 15 | DID_MAX = 0x0E 16 | VID_MAX = 0xC8 # 0.300V 17 | VID_MIN = 0x00 18 | 19 | PSTATES = range(0xC0010064, 0xC001006C) 20 | 21 | MSR_PMGT_MISC = 0xC0010292 # [32] PC6En 22 | MSR_CSTATE_CONFIG = 0xC0010296 # [22] CCR2_CC6EN [14] CCR1_CC6EN [6] CCR0_CC6EN 23 | MSR_HWCR = 0xC0010015 24 | SMU_CMD_ADDR = 0 25 | SMU_RSP_ADDR = 0 26 | SMU_ARG_ADDR = 0 27 | SMU_CMD_OC_ENABLE = 0 28 | SMU_CMD_OC_DISABLE = 0 29 | SMU_CMD_OC_FREQ_ALL_CORES = 0 30 | SMU_CMD_OC_VID = 0 31 | SMU_CMD_GET_PBO_SCALAR = 0 32 | 33 | isOcFreqSupported = False 34 | cpu_sockets = int(os.popen('cat /proc/cpuinfo | grep "physical id" | sort -u | wc -l').read()) 35 | 36 | def writesmureg(reg, value=0): 37 | os.popen('setpci -v -s 0:0.0 b8.l={:08X}'.format(reg)).read() 38 | os.popen('setpci -v -s 0:0.0 bc.l={:08X}'.format(value)).read() 39 | 40 | if cpu_sockets == 2: 41 | os.popen('setpci -v -s A0:0.0 b8.l={:08X}'.format(reg)).read() 42 | os.popen('setpci -v -s A0:0.0 bc.l={:08X}'.format(value)).read() 43 | 44 | 45 | def readsmureg(reg): 46 | os.popen('setpci -v -s 0:0.0 b8.l={:08X}'.format(reg)).read() 47 | output = os.popen('setpci -v -s 0:0.0 bc.l').read() 48 | 49 | if cpu_sockets == 2: 50 | os.popen('setpci -v -s A0:0.0 b8.l={:08X}'.format(reg)).read() 51 | os.popen('setpci -v -s A0:0.0 bc.l').read() 52 | 53 | return hex(output[-9:][0:8]) 54 | 55 | 56 | def writesmu(cmd, value=0): 57 | res = False 58 | # clear the response register 59 | writesmureg(SMU_RSP_ADDR, 0) 60 | # write the value 61 | writesmureg(SMU_ARG_ADDR, value) 62 | writesmureg(SMU_ARG_ADDR + 4, 0) 63 | # send the command 64 | writesmureg(SMU_CMD_ADDR, cmd) 65 | res = smuwaitdone() 66 | if res: 67 | return readsmureg(SMU_RSP_ADDR) 68 | else: 69 | return 0 70 | 71 | 72 | def readsmu(cmd): 73 | writesmureg(SMU_RSP_ADDR, 0) 74 | writesmureg(SMU_CMD_ADDR, cmd) 75 | return readsmureg(SMU_ARG_ADDR) 76 | 77 | def smuwaitdone(): 78 | res = False 79 | timeout = 1000 80 | data = 0 81 | while ((not res or data != 1) and timeout > 0): 82 | timeout-=1 83 | data = readsmureg(SMU_RSP_ADDR) 84 | if data == 1: 85 | res = True 86 | if (timeout == 0 or data != 1): 87 | res = False 88 | return res 89 | 90 | def writemsr(msr, val, cpu=-1): 91 | try: 92 | if cpu == -1: 93 | for c in glob.glob('/dev/cpu/[0-9]*/msr'): 94 | f = os.open(c, os.O_WRONLY) 95 | os.lseek(f, msr, os.SEEK_SET) 96 | os.write(f, struct.pack('Q', val)) 97 | os.close(f) 98 | else: 99 | f = os.open('/dev/cpu/%d/msr' % (cpu), os.O_WRONLY) 100 | os.lseek(f, msr, os.SEEK_SET) 101 | os.write(f, struct.pack('Q', val)) 102 | os.close(f) 103 | except: 104 | raise OSError("msr module not loaded (run modprobe msr)") 105 | 106 | 107 | def readmsr(msr, cpu=0): 108 | try: 109 | f = os.open('/dev/cpu/%d/msr' % cpu, os.O_RDONLY) 110 | os.lseek(f, msr, os.SEEK_SET) 111 | val = struct.unpack('Q', os.read(f, 8))[0] 112 | os.close(f) 113 | return val 114 | except: 115 | raise OSError("msr module not loaded (run modprobe msr)") 116 | 117 | 118 | def pstate2str(val): 119 | if val & (1 << 63): 120 | fid = val & 0xff 121 | did = val >> 8 & 0x3f 122 | vid = val >> 14 & 0xff 123 | ratio = 25*fid/(12.5 * did) 124 | vcore = vidToVolts(vid) 125 | return "Enabled - FID = %X - DID = %X - VID = %X - Ratio = %.2f - vCore = %.5f" % (fid, did, vid, ratio, vcore) 126 | else: 127 | return "Disabled" 128 | 129 | 130 | def pstateToGuiString(fid, did, vid): 131 | ratio = 25 * int(fid)/(12.5 * int(did)) 132 | vcore = vidToVolts(vid) 133 | return "Ratio = %.2f - vCore = %.5f" % (ratio, vcore) 134 | 135 | 136 | def getPstateFid(index): 137 | return readmsr(PSTATES[index]) & 0xff 138 | 139 | 140 | def getPstateDid(index): 141 | return readmsr(PSTATES[index]) >> 8 & 0x3f 142 | 143 | 144 | def getPstateVid(index): 145 | return readmsr(PSTATES[index]) >> 14 & 0xff 146 | 147 | 148 | def getCurrentVid(): 149 | return readmsr(0xC0010293) >> 14 & 0xff 150 | 151 | 152 | def getPstateDetails(val): 153 | fid = val & 0xff 154 | did = val >> 8 & 0x3f 155 | vid = val >> 14 & 0xff 156 | return [fid, did, vid] 157 | 158 | 159 | def getRatio(msr): 160 | val = readmsr(msr) 161 | fid = val & 0xff 162 | did = val >> 8 & 0x3f 163 | return 25*fid/(12.5 * did) 164 | 165 | 166 | def calculateFrequencyFromFid(msr, fid): 167 | did = readmsr(msr) >> 8 & 0x3f 168 | if fid not in range(FID_MIN, FID_MAX): 169 | fid = 0x88 # 1.00V 170 | return int(25*fid/(12.5 * did) * 100) 171 | 172 | 173 | def vidToVolts(vid): 174 | return 1.55 - vid * 0.00625 175 | 176 | 177 | def voltsToVid(volts): 178 | return (1.55 - volts) / 0.00625 179 | 180 | 181 | def getCpuidRegs(fn): 182 | if args.libcpuid: 183 | regs = (ctypes.c_uint32 * 4)(fn, 0, 0, 0) 184 | cpuid(regs) 185 | else: 186 | regs = cpuid.CPUID()(fn) 187 | return regs 188 | 189 | def getCpuid(): 190 | eax, ebx, ecx, edx = getCpuidRegs(0x00000001) 191 | print("CPUID: %08X" % eax) 192 | return eax 193 | 194 | 195 | def getPkgType(): 196 | eax, ebx, ecx, edx = getCpuidRegs(0x80000001) 197 | type = ebx >> 28 198 | print("Package Type: %01d" % type) 199 | return type 200 | 201 | 202 | def getOcMode(): 203 | return readsmu(SMU_CMD_GET_PBO_SCALAR) == 0 204 | 205 | 206 | def getC6core(): 207 | return readmsr(MSR_CSTATE_CONFIG) & ((1 << 22) | (1 << 14) | (1 << 6)) == ((1 << 22) | (1 << 14) | (1 << 6)) 208 | 209 | 210 | def getC6package(): 211 | return readmsr(MSR_PMGT_MISC) & (1 << 32) != 0 212 | 213 | 214 | def setC6Core(enable): 215 | if not getC6core() and enable: 216 | writemsr(MSR_CSTATE_CONFIG, readmsr(MSR_CSTATE_CONFIG) | ((1 << 22) | (1 << 14) | (1 << 6))) 217 | print('GUI: Set C6-Core: %s' % str(enable)) 218 | elif getC6core() and not enable: 219 | writemsr(MSR_CSTATE_CONFIG, readmsr(MSR_CSTATE_CONFIG) & ~ ((1 << 22) | (1 << 14) | (1 << 6))) 220 | print('GUI: Set C6-Core: %s' % str(enable)) 221 | 222 | 223 | def setC6Package(enable): 224 | if not getC6package() and enable: 225 | writemsr(MSR_PMGT_MISC, readmsr(MSR_PMGT_MISC) | (1 << 32)) 226 | print('GUI: Set C6-Package: %s' % str(enable)) 227 | elif getC6package() and not enable: 228 | writemsr(MSR_PMGT_MISC, readmsr(MSR_PMGT_MISC) & ~(1 << 32)) 229 | print('GUI: Set C6-Package: %s' % str(enable)) 230 | 231 | 232 | def setPPT(val): 233 | if int(val) > -1: writesmu(0x53, int(val) * 1000) 234 | 235 | 236 | def setTDC(val): 237 | if int(val) > -1: writesmu(0x54, int(val) * 1000) 238 | 239 | 240 | def setEDC(val): 241 | if int(val) > -1: writesmu(0x55, int(val) * 1000) 242 | 243 | 244 | # Not supported yet 245 | def setScalar(val): 246 | if int(val) > 0 and int(val) <= 10: print('PBO Scalar') 247 | 248 | 249 | def setPboLimits(ppt, tdc, edc, scalar): 250 | setPPT(ppt) 251 | setTDC(tdc) 252 | setEDC(edc) 253 | setScalar(scalar) 254 | 255 | 256 | def setbits(val, base, length, new): 257 | return (val ^ (val & ((2 ** length - 1) << base))) + (new << base) 258 | 259 | 260 | def setfid(val, new): 261 | return setbits(val, 0, 8, new) 262 | 263 | 264 | def setdid(val, new): 265 | return setbits(val, 8, 6, new) 266 | 267 | 268 | def setvid(val, new): 269 | return setbits(val, 14, 8, new) 270 | 271 | 272 | def hex(x): 273 | return int(x, 16) 274 | 275 | 276 | def setPstateGui(index, fid, did, vid): 277 | new = old = readmsr(PSTATES[index]) 278 | if fid in range(FID_MIN, FID_MAX): 279 | new = setfid(new, fid) 280 | if did in range(DID_MIN, DID_MAX): 281 | new = setdid(new, did) 282 | if vid in range(VID_MIN, VID_MAX): 283 | new = setvid(new, vid) 284 | if new != old: 285 | if not (readmsr(MSR_HWCR) & (1 << 21)): 286 | print('GUI: Locking TSC frequency') 287 | for c in range(len(glob.glob('/dev/cpu/[0-9]*/msr'))): 288 | writemsr(MSR_HWCR, readmsr(MSR_HWCR, c) | (1 << 21), c) 289 | print('GUI: Set Pstate%s: %s' % (index, getPstateDetails(new))) 290 | writemsr(PSTATES[index], new) 291 | 292 | parser = argparse.ArgumentParser(description='Dynamically edit AMD Ryzen processor parameters') 293 | parser.add_argument('-l', '--list', action='store_true', help='List all P-States') 294 | parser.add_argument('--no-gui', action='store_true', help='Run in CLI without GUI') 295 | parser.add_argument('--libcpuid', action='store_true', help='Use libcpuid instead of cpuid.py') 296 | parser.add_argument('-p', '--pstate', default=-1, type=int, choices=range(8), help='P-State to set') 297 | parser.add_argument('--enable', action='store_true', help='Enable P-State') 298 | parser.add_argument('--disable', action='store_true', help='Disable P-State') 299 | parser.add_argument('-f', '--fid', default=-1, type=hex, help='FID to set (in hex)') 300 | parser.add_argument('-d', '--did', default=-1, type=hex, help='DID to set (in hex)') 301 | parser.add_argument('-v', '--vid', default=-1, type=hex, help='VID to set (in hex)') 302 | parser.add_argument('--c6-enable', action='store_true', help='Enable C-State C6') 303 | parser.add_argument('--c6-disable', action='store_true', help='Disable C-State C6') 304 | parser.add_argument('--smu-test-message', action='store_true', help='Send test message to the SMU (response 1 means "success")') 305 | parser.add_argument('--oc-enable', action='store_true', help='Enable OC') 306 | parser.add_argument('--oc-disable', action='store_true', help='Disable OC') 307 | parser.add_argument('--oc-frequency', default=550, type=int, help='Set overclock frequency (in MHz)') 308 | parser.add_argument('--oc-vid', default=-1, type=hex, help='Set overclock VID') 309 | parser.add_argument('--ppt', default=-1, type=int, help='Set PPT limit (in W)') 310 | parser.add_argument('--tdc', default=-1, type=int, help='Set TDC limit (in A)') 311 | parser.add_argument('--edc', default=-1, type=int, help='Set EDC limit (in A)') 312 | 313 | args = parser.parse_args() 314 | 315 | if (not args.list 316 | and args.pstate == -1 317 | and not args.c6_enable 318 | and not args.c6_disable 319 | and not args.smu_test_message 320 | and not args.oc_disable 321 | and not args.oc_enable 322 | and args.no_gui 323 | and args.edc == -1 324 | and args.ppt == -1 325 | and args.tdc == -1): 326 | parser.print_help() 327 | exit() 328 | 329 | if args.libcpuid: 330 | cpuid = ctypes.CDLL("libcpuid.so").cpu_exec_cpuid_ext 331 | cpuid.restype = None 332 | cpuid.argtypes = [ctypes.POINTER(ctypes.c_uint32)] 333 | 334 | print('CPUs: %d' % cpu_sockets) 335 | 336 | _cpuid = getCpuid() 337 | _pkgtype = getPkgType() 338 | 339 | # Zen | Summit Ridge, Threadripper 340 | if _cpuid in [0x00800F11, 0x00800F00]: 341 | SMU_CMD_ADDR = 0x03B10528 342 | SMU_RSP_ADDR = 0x03B10564 343 | SMU_ARG_ADDR = 0x03B10598 344 | SMU_CMD_OC_ENABLE = 0x23 345 | SMU_CMD_OC_DISABLE = 0x24 346 | SMU_CMD_OC_FREQ_ALL_CORES = 0x26 347 | SMU_CMD_OC_VID = 0x28 348 | # depends on SMU version. Need to find which version disables the manual OC 349 | # turn it off for now 350 | isOcFreqSupported = False 351 | 352 | # Zen | Naples - P-States only 353 | elif _cpuid == 0x00800F12: 354 | SMU_CMD_ADDR = 0x03B10528 355 | SMU_RSP_ADDR = 0x03B10564 356 | SMU_ARG_ADDR = 0x03B10598 357 | isOcFreqSupported = False 358 | 359 | # Zen+ | Pinnacle Ridge, Colfax 360 | elif _cpuid == 0x00800F82: 361 | SMU_CMD_ADDR = 0x03B1051C 362 | SMU_RSP_ADDR = 0x03B10568 363 | SMU_ARG_ADDR = 0x03B10590 364 | # SMU_CMD_OC_ENABLE = 0x63 365 | # SMU_CMD_OC_DISABLE = 0x64 366 | isOcFreqSupported = True 367 | 368 | if _pkgtype == 7: # Colfax 369 | SMU_CMD_OC_ENABLE = 0x67 # based on assumption 370 | SMU_CMD_OC_FREQ_ALL_CORES = 0x68 371 | SMU_CMD_OC_VID = 0x6A 372 | SMU_CMD_GET_PBO_SCALAR = 0x70 373 | else: 374 | SMU_CMD_OC_ENABLE = 0x6B 375 | SMU_CMD_OC_FREQ_ALL_CORES = 0x6C 376 | SMU_CMD_OC_VID = 0x6E 377 | SMU_CMD_GET_PBO_SCALAR = 0x6F 378 | 379 | # Zen 2 | Matisse, Rome, Castle Peak 380 | # Zen 3 | Vermeer 381 | elif _cpuid in [0x00870F10, 0x00870F00, 0x00830F00, 0x00830F10, 0x00A20F00, 0x00A20F10]: 382 | SMU_CMD_ADDR = 0x03B10524 383 | SMU_RSP_ADDR = 0x03B10570 384 | SMU_ARG_ADDR = 0x03B10A40 385 | isOcFreqSupported = True 386 | 387 | if _pkgtype == 7: # Rome ES 388 | SMU_CMD_OC_FREQ_ALL_CORES = 0x18 389 | SMU_CMD_OC_VID = 0x12 390 | else: 391 | SMU_CMD_OC_ENABLE = 0x5A 392 | SMU_CMD_OC_DISABLE = 0x5B 393 | SMU_CMD_OC_FREQ_ALL_CORES = 0x5C 394 | SMU_CMD_OC_VID = 0x61 395 | SMU_CMD_GET_PBO_SCALAR = 0x6C 396 | 397 | # RavenRidge, RavenRidge2 398 | elif _cpuid in [0x00810F00, 0x00810F10, 0x00820F00]: 399 | SMU_CMD_ADDR = 0x03B10528 400 | SMU_RSP_ADDR = 0x03B10564 401 | SMU_ARG_ADDR = 0x03B10998 402 | isOcFreqSupported = False 403 | 404 | # Picasso, Fenghuang 405 | elif _cpuid in [0x00810F81, 0x00850F00]: 406 | SMU_CMD_ADDR = 0x03B10A20 407 | SMU_RSP_ADDR = 0x03B10A80 408 | SMU_ARG_ADDR = 0x03B10A88 409 | SMU_CMD_OC_ENABLE = 0x69 410 | SMU_CMD_OC_DISABLE = 0x6A 411 | SMU_CMD_OC_FREQ_ALL_CORES = 0x7D 412 | SMU_CMD_OC_VID = 0x7F 413 | SMU_CMD_GET_PBO_SCALAR = 0x62 414 | isOcFreqSupported = True 415 | 416 | # Renoir 417 | elif _cpuid in [0x00860F01]: 418 | SMU_CMD_ADDR = 0x03B10A20 419 | SMU_RSP_ADDR = 0x03B10A80 420 | SMU_ARG_ADDR = 0x03B10A88 421 | SMU_CMD_GET_PBO_SCALAR = 0xF 422 | isOcFreqSupported = False 423 | 424 | else: 425 | exit('CPU not supported!') 426 | 427 | if args.list: 428 | for p in range(len(PSTATES)): 429 | print('P' + str(p) + " - " + pstate2str(readmsr(PSTATES[p]))) 430 | print('C6 State - Package - ' + 431 | ('Enabled' if getC6package() else 'Disabled')) 432 | print('C6 State - Core - ' + ('Enabled' if getC6core() else 'Disabled')) 433 | 434 | if args.pstate >= 0: 435 | new = old = readmsr(PSTATES[args.pstate]) 436 | print('Current P' + str(args.pstate) + ': ' + pstate2str(old)) 437 | if args.enable: 438 | new = setbits(new, 63, 1, 1) 439 | print('Enabling state') 440 | if args.disable: 441 | new = setbits(new, 63, 1, 0) 442 | print('Disabling state') 443 | if args.fid in range(FID_MIN, FID_MAX): 444 | new = setfid(new, args.fid) 445 | print('Setting FID to %X' % args.fid) 446 | if args.did >= 0: 447 | new = setdid(new, args.did) 448 | print('Setting DID to %X' % args.did) 449 | if args.vid in range(VID_MIN, VID_MAX): 450 | new = setvid(new, args.vid) 451 | print('Setting VID to %X' % args.vid) 452 | if new != old: 453 | if not (readmsr(MSR_HWCR) & (1 << 21)): 454 | print('Locking TSC frequency') 455 | for c in range(len(glob.glob('/dev/cpu/[0-9]*/msr'))): 456 | writemsr(MSR_HWCR, readmsr(MSR_HWCR, c) | (1 << 21), c) 457 | print('New P' + str(args.pstate) + ': ' + pstate2str(new)) 458 | writemsr(PSTATES[args.pstate], new) 459 | 460 | if args.c6_enable: 461 | setC6Package(True) 462 | setC6Core(True) 463 | print('Enabling C6 state') 464 | 465 | if args.c6_disable: 466 | setC6Package(False) 467 | setC6Core(False) 468 | print('Disabling C6 state') 469 | 470 | if args.smu_test_message: 471 | print('Sending test SMU message') 472 | print('SMU response: %X' % writesmu(0x1)) 473 | 474 | if args.oc_enable: 475 | if isOcFreqSupported: 476 | writesmu(SMU_CMD_OC_ENABLE) 477 | print('Enable OC Mode') 478 | else: 479 | print('OC Mode not supported') 480 | 481 | if args.oc_disable: 482 | if isOcFreqSupported: 483 | writesmu(SMU_CMD_OC_DISABLE) 484 | print('Disabling OC Mode') 485 | else: 486 | print('OC Mode not supported') 487 | 488 | if args.oc_vid >= 0: 489 | writesmu(SMU_CMD_OC_VID, args.oc_vid) 490 | print('Set OC VID to %X' % args.oc_vid) 491 | 492 | if args.oc_frequency > 550: 493 | writesmu(SMU_CMD_OC_FREQ_ALL_CORES, args.oc_frequency) 494 | print('Set OC frequency to %sMHz' % args.oc_frequency) 495 | 496 | if args.ppt > -1: 497 | setPPT(args.ppt) 498 | print('Set PPT to %sW' % args.ppt) 499 | 500 | if args.tdc > -1: 501 | setTDC(args.tdc) 502 | print('Set TDC to %sA' % args.tdc) 503 | 504 | if args.edc > -1: 505 | setEDC(args.edc) 506 | print('Set EDC to %sA' % args.edc) 507 | 508 | 509 | ############################### 510 | # GUI 511 | if not args.no_gui: 512 | import PySimpleGUI as sg 513 | 514 | _oc_mode = getOcMode() 515 | if _oc_mode: 516 | _default_vid = getCurrentVid() 517 | _ratio = getRatio(0xC0010293) 518 | else: 519 | _default_vid = getPstateVid(0) 520 | _ratio = getRatio(PSTATES[0]) 521 | 522 | _current_freq = int(_ratio * 100) 523 | 524 | #sg.theme('Dark Teal 9') 525 | sg.set_options(icon='icon.png', element_padding=(5, 5), margins=(1, 1), border_width=0) 526 | 527 | # The tab 1, 2, 3 layouts - what goes inside the tab 528 | tab1_layout = [ 529 | [sg.CBox('OC Mode', default=_oc_mode, key='ocMode', enable_events=True)], 530 | [ 531 | sg.Text(' All Core Frequency', size=(18, 1)), 532 | sg.Spin( 533 | values=[x for x in range(550, 7000, 25)], 534 | initial_value=_current_freq, 535 | enable_events=True, 536 | disabled=not _oc_mode, 537 | size=(5, 1), 538 | key='cpuOcFrequency'), 539 | sg.Text('MHz'), 540 | ], 541 | [ 542 | sg.Text(' Overclock VID', size=(18, 1)), 543 | sg.Spin( 544 | values=[x for x in range(VID_MAX, VID_MIN, -1)], 545 | initial_value=_default_vid, 546 | enable_events=True, 547 | disabled=not _oc_mode, 548 | size=(5, 1), 549 | key='cpuOcVid'), 550 | sg.Text("%.5f V" % vidToVolts(_default_vid), key='cpuOcVoltageText'), 551 | ], 552 | ] 553 | 554 | tab2_layout = [ 555 | [ 556 | sg.Text('', size=(8, 1)), 557 | sg.Text('FID', size=(6, 1)), 558 | sg.Text('DID', size=(6, 1)), 559 | sg.Text('VID', size=(6, 1)) 560 | ] 561 | ] 562 | for p in range(0, 3): 563 | state = readmsr(PSTATES[p]) 564 | d = getPstateDetails(state) 565 | tab2_layout.append([ 566 | sg.Text(' P-State%s' % str(p), size=(8, 1)), 567 | sg.Spin( 568 | values=[x for x in range(FID_MIN, FID_MAX, 1)], 569 | initial_value=d[0], 570 | enable_events=True, 571 | size=(5, 1), 572 | key='pstate%sFid' % str(p) 573 | ), 574 | sg.Spin( 575 | values=[x for x in range(DID_MAX, DID_MIN - 1, -2)], 576 | initial_value=d[1], 577 | enable_events=True, 578 | size=(5, 1), 579 | key='pstate%sDid' % str(p) 580 | ), 581 | sg.Spin( 582 | values=[x for x in range(VID_MAX, VID_MIN - 1, -1)], 583 | initial_value=d[2], 584 | enable_events=True, 585 | size=(5, 1), 586 | key='pstate%sVid' % str(p) 587 | ), 588 | sg.Text(pstateToGuiString(d[0], d[1], d[2]), key='pstateDetails%s' % str(p)) 589 | ]) 590 | 591 | tab3_layout = [ 592 | [sg.Text('C6 States')], 593 | [sg.CBox( 594 | 'C6-State Package', 595 | default=getC6package(), 596 | enable_events=True, 597 | key='c6StatePackage') 598 | ], 599 | [sg.CBox( 600 | 'C6-State Core', 601 | default=getC6core(), 602 | enable_events=True, 603 | key='c6StateCore') 604 | ], 605 | [sg.Text('Experimental')], 606 | [ 607 | sg.Text(' PPT', size=(6, 1)), 608 | sg.Spin( 609 | values=[x for x in range(-1, 1000, 1)], 610 | initial_value=-1, 611 | enable_events=True, 612 | disabled=False, 613 | size=(5, 1), 614 | key='ppt'), 615 | sg.Text('W', size=(4, 1)), 616 | sg.Text(' TDC', size=(6, 1)), 617 | sg.Spin( 618 | values=[x for x in range(-1, 1000, 1)], 619 | initial_value=-1, 620 | enable_events=True, 621 | disabled=False, 622 | size=(5, 1), 623 | key='tdc'), 624 | sg.Text('A', size=(4, 1)), 625 | ], 626 | [ 627 | sg.Text(' EDC', size=(6, 1)), 628 | sg.Spin( 629 | values=[x for x in range(-1, 1000, 1)], 630 | initial_value=-1, 631 | enable_events=True, 632 | disabled=False, 633 | size=(5, 1), 634 | key='edc'), 635 | sg.Text('A', size=(4, 1)), 636 | sg.Text(' Scalar', size=(6, 1)), 637 | sg.Spin( 638 | values=[x for x in range(0, 10, 1)], 639 | initial_value=0, 640 | enable_events=True, 641 | disabled=True, 642 | size=(5, 1), 643 | key='scalar') 644 | ], 645 | [sg.Text(' * -1 = Auto / No change')] 646 | ] 647 | 648 | # The TabgGroup layout - it must contain only Tabs 649 | if isOcFreqSupported: 650 | tab_group_layout = [ 651 | [ 652 | sg.Tab('CPU', tab1_layout, key='-TAB1-'), 653 | sg.Tab('P-States', tab2_layout, key='-TAB2-'), 654 | sg.Tab('Power', tab3_layout, key='-TAB3-') 655 | ] 656 | ] 657 | else: 658 | tab_group_layout = [ 659 | [ 660 | sg.Tab('P-States', tab2_layout, key='-TAB2-'), 661 | sg.Tab('Power', tab3_layout, key='-TAB3-') 662 | ] 663 | ] 664 | 665 | # The window layout - defines the entire window 666 | layout = [ 667 | [sg.TabGroup(tab_group_layout, 668 | # selected_title_color='blue', 669 | # selected_background_color='red', 670 | # tab_background_color='green', 671 | enable_events=True, 672 | # font='Courier 18', 673 | key='-TABGROUP-')], 674 | [sg.Button('Apply', key='applyBtn'), sg.Button('Cancel')] 675 | ] 676 | 677 | def applyCpuSettings(): 678 | if values['ocMode']: 679 | writesmu(SMU_CMD_OC_ENABLE) 680 | writesmu(SMU_CMD_OC_FREQ_ALL_CORES, values['cpuOcFrequency']) 681 | writesmu(SMU_CMD_OC_VID, values['cpuOcVid']) 682 | else: 683 | writesmu(SMU_CMD_OC_DISABLE) 684 | 685 | 686 | def applyPstatesSettings(): 687 | for p in range(0, 3): 688 | setPstateGui(p, values['pstate%sFid' % str(p)], values['pstate%sDid' % str(p)], values['pstate%sVid' % str(p)]) 689 | 690 | 691 | def applyPowerSettings(): 692 | setC6Core(values['c6StateCore']) 693 | setC6Package(values['c6StatePackage']) 694 | setPboLimits(values['ppt'], values['tdc'], values['edc'], values['scalar']) 695 | 696 | 697 | window_title = "%s v%s" % (APP_NAME, APP_VERSION) 698 | window = sg.Window(window_title, layout) 699 | print('GUI: %s initialized' % window_title) 700 | 701 | while True: # Event Loop 702 | event, values = window.read() 703 | # print(event) 704 | # print(values) 705 | 706 | # Cancel or close event 707 | if event in (None, 'Cancel'): 708 | break 709 | 710 | # Apply button events 711 | if event == 'applyBtn' and values['-TABGROUP-'] == '-TAB1-': 712 | if isOcFreqSupported: applyCpuSettings() 713 | if event == 'applyBtn' and values['-TABGROUP-'] == '-TAB2-': 714 | applyPstatesSettings() 715 | if event == 'applyBtn' and values['-TABGROUP-'] == '-TAB3-': 716 | applyPowerSettings() 717 | 718 | # UI elements state change 719 | if event == 'ocMode': 720 | window['cpuOcFrequency'].update(disabled=(not values['ocMode'])) 721 | window['cpuOcVid'].update(disabled=(not values['ocMode'])) 722 | if event == 'cpuOcVid': 723 | window['cpuOcVoltageText'].update("%.5f V" % vidToVolts(values['cpuOcVid'])) 724 | 725 | for p in range(0, 3): 726 | if event in ['pstate%sFid' % str(p), 'pstate%sDid' % str(p), 'pstate%sVid' % str(p)]: 727 | window['pstateDetails%s' % str(p)].update( 728 | pstateToGuiString( 729 | values['pstate%sFid' % str(p)], 730 | values['pstate%sDid' % str(p)], 731 | values['pstate%sVid' % str(p)] 732 | ) 733 | ) 734 | window.close() 735 | --------------------------------------------------------------------------------