├── win32 ├── inpoutx64.dll ├── inpoutx64.lib ├── WinRing0x64.dll ├── WinRing0x64.exp ├── WinRing0x64.lib ├── WinRing0x64.sys ├── demo.bat ├── uninstallServiceTask.bat ├── installServiceTask.bat ├── RyzenAdjServiceTask.xml.template └── readjustService.ps1 ├── lib ├── linux │ ├── osdep_linux_mem.h │ ├── osdep_linux_smu_kernel_module.h │ ├── osdep_linux.c │ ├── osdep_linux_mem.c │ └── osdep_linux_smu_kernel_module.c ├── ryzenadj_priv.h ├── nb_smu_ops.h ├── win32 │ ├── OlsDef.h │ ├── osdep_win32.cpp │ └── OlsApi.h ├── cpuid.c ├── nb_smu_ops.c └── ryzenadj.h ├── CMakeLists.txt ├── examples ├── pmtable-example.py └── readjust.py ├── argparse.h ├── .github └── workflows │ └── build.yml ├── .gitignore ├── LICENSE ├── README.md ├── argparse.c └── main.c /win32/inpoutx64.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ReimuNotMoe/RyzenAdj/master/win32/inpoutx64.dll -------------------------------------------------------------------------------- /win32/inpoutx64.lib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ReimuNotMoe/RyzenAdj/master/win32/inpoutx64.lib -------------------------------------------------------------------------------- /win32/WinRing0x64.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ReimuNotMoe/RyzenAdj/master/win32/WinRing0x64.dll -------------------------------------------------------------------------------- /win32/WinRing0x64.exp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ReimuNotMoe/RyzenAdj/master/win32/WinRing0x64.exp -------------------------------------------------------------------------------- /win32/WinRing0x64.lib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ReimuNotMoe/RyzenAdj/master/win32/WinRing0x64.lib -------------------------------------------------------------------------------- /win32/WinRing0x64.sys: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ReimuNotMoe/RyzenAdj/master/win32/WinRing0x64.sys -------------------------------------------------------------------------------- /win32/demo.bat: -------------------------------------------------------------------------------- 1 | %~dp0\ryzenadj.exe --stapm-limit=40000 --fast-limit=45000 --slow-limit=45000 --tctl-temp=90 2 | pause 3 | -------------------------------------------------------------------------------- /lib/linux/osdep_linux_mem.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "../nb_smu_ops.h" 4 | 5 | os_access_obj_t *init_os_access_obj_mem(); 6 | int init_mem_obj_mem(os_access_obj_t *os_access, uintptr_t physAddr); 7 | int copy_pm_table_mem(const os_access_obj_t *obj, void *buffer, size_t size); 8 | int compare_pm_table_mem(const void *buffer, size_t size); 9 | void free_os_access_obj_mem(os_access_obj_t *obj); 10 | 11 | uint32_t smn_reg_read_mem(const os_access_obj_t *obj, uint32_t addr); 12 | void smn_reg_write_mem(const os_access_obj_t *obj, uint32_t addr, uint32_t data); 13 | -------------------------------------------------------------------------------- /lib/linux/osdep_linux_smu_kernel_module.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "../nb_smu_ops.h" 4 | 5 | os_access_obj_t *init_os_access_obj_kmod(); 6 | int init_mem_obj_kmod(os_access_obj_t *os_access, uintptr_t physAddr); 7 | int copy_pm_table_kmod(const os_access_obj_t *obj, void *buffer, size_t size); 8 | int compare_pm_table_kmod(const void *buffer, size_t size); 9 | void free_os_access_obj_kmod(os_access_obj_t *obj); 10 | 11 | uint32_t smn_reg_read_kmod(const os_access_obj_t *obj, uint32_t addr); 12 | void smn_reg_write_kmod(const os_access_obj_t *obj, uint32_t addr, uint32_t data); 13 | -------------------------------------------------------------------------------- /win32/uninstallServiceTask.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | NET FILE 1>NUL 2>NUL 3 | if %errorlevel% NEQ 0 ( 4 | echo Deinstallation need to be run as Administrator to delete your Scheduled Task 5 | pause 6 | exit /B 0 7 | ) 8 | 9 | reg query HKCU\Software\HWiNFO64\Sensors\Custom\RyzenAdj 2>NUL 10 | if %errorlevel% EQU 0 reg delete HKCU\Software\HWiNFO64\Sensors\Custom\RyzenAdj 11 | 12 | SCHTASKS /query /TN "AMD\RyzenAdj" 2>NUL 13 | 14 | if %errorlevel% NEQ 0 ( 15 | echo RyzenAdj Service Task is not installed 16 | pause 17 | exit /B 0 18 | ) 19 | 20 | SCHTASKS /delete /TN "AMD\RyzenAdj" 2>NUL 21 | 22 | pause 23 | exit /B 0 24 | -------------------------------------------------------------------------------- /lib/ryzenadj_priv.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: LGPL */ 2 | /* Copyright (C) 2020 Jiaxun Yang */ 3 | /* RyzenAdj Private stuff */ 4 | /* Do not include this file! */ 5 | 6 | #ifndef RYZENADJ_PRIV_H 7 | #define RYZENADJ_PRIV_H 8 | 9 | #include 10 | 11 | #include "nb_smu_ops.h" 12 | 13 | struct _ryzen_access { 14 | os_access_obj_t *os_access; 15 | smu_t mp1_smu; 16 | smu_t psmu; 17 | enum ryzen_family family; 18 | int bios_if_ver; 19 | uintptr_t table_addr; 20 | uint32_t table_ver; 21 | size_t table_size; 22 | float *table_values; 23 | }; 24 | 25 | enum ryzen_family cpuid_get_family(); 26 | 27 | #endif 28 | -------------------------------------------------------------------------------- /win32/installServiceTask.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | NET FILE 1>NUL 2>NUL 3 | if %errorlevel% NEQ 0 ( 4 | echo Installation need be run as Administrator to install a Task 5 | pause 6 | exit /B 0 7 | ) 8 | 9 | cd /D "%~dp0" 10 | choice /C YN /M "Do you want to install Service based on directory %~dp0? It can not be changed after installation." 11 | if %ERRORLEVEL% NEQ 1 exit /B 1 12 | 13 | for %%f in (RyzenAdjServiceTask.xml.template readjustService.ps1 libryzenadj.dll WinRing0x64.dll WinRing0x64.sys inpoutx64.dll) do ( 14 | if not exist %%f echo %%f is missing && goto failed 15 | ) 16 | 17 | echo Please configure RyzenAdjService by adding your prefered values in the top section of the powershell script. 18 | timeout /t 2 > NUL 19 | notepad "%~dp0\readjustService.ps1" 20 | 21 | powershell -Command "(gc '%~dp0RyzenAdjServiceTask.xml.template') -replace '###SCRIPTPATH###', '%~dp0readjustService.ps1' | Out-File -encoding ASCII '%~dp0RyzenAdjServiceTask.xml'" 22 | 23 | SCHTASKS /Create /TN "AMD\RyzenAdj" /XML "%~dp0RyzenAdjServiceTask.xml" /F || goto failed 24 | 25 | SCHTASKS /run /TN "AMD\RyzenAdj" || goto failed 26 | 27 | timeout /t 2 > NUL 28 | 29 | SCHTASKS /query /TN "AMD\RyzenAdj" || goto failed 30 | 31 | echo. 32 | echo Installation successfull 33 | pause 34 | exit /B 0 35 | 36 | :FAILED 37 | echo Installation failed 38 | pause 39 | exit /B 1 40 | -------------------------------------------------------------------------------- /win32/RyzenAdjServiceTask.xml.template: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | true 6 | 7 | 8 | 9 | 10 | InteractiveToken 11 | HighestAvailable 12 | 13 | 14 | 15 | IgnoreNew 16 | false 17 | false 18 | true 19 | false 20 | false 21 | 22 | false 23 | false 24 | 25 | true 26 | true 27 | false 28 | false 29 | false 30 | PT0S 31 | 7 32 | 33 | PT60M 34 | 10 35 | 36 | 37 | 38 | 39 | powershell 40 | -WindowStyle hidden -ExecutionPolicy Bypass -file "###SCRIPTPATH###" 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | #cmake version 2 | CMAKE_MINIMUM_REQUIRED(VERSION 3.9) 3 | 4 | #define project name 5 | PROJECT(ryzenadj) 6 | 7 | set(CMAKE_C_VISIBILITY_PRESET hidden) 8 | 9 | set(CMAKE_CXX_STANDARD 11) 10 | set(CMAKE_CXX_VISIBILITY_PRESET hidden) 11 | 12 | message(STATUS "Build type: ${CMAKE_BUILD_TYPE}") 13 | 14 | #Enable LTO 15 | include(CheckIPOSupported) 16 | check_ipo_supported(RESULT supported OUTPUT error) 17 | if( supported ) 18 | message(STATUS "IPO / LTO enabled") 19 | set(CMAKE_INTERPROCEDURAL_OPTIMIZATION TRUE) 20 | endif() 21 | 22 | AUX_SOURCE_DIRECTORY(./ SRC_DIR) 23 | 24 | if(WIN32) 25 | set(OS_SOURCE lib/win32/osdep_win32.cpp) 26 | set(OS_LINK_LIBRARY WinRing0x64) 27 | set(OS_LINK_DIR ./win32) 28 | else() 29 | set(OS_SOURCE lib/linux/osdep_linux.c lib/linux/osdep_linux_mem.c lib/linux/osdep_linux_smu_kernel_module.c) 30 | #if (CMAKE_BUILD_TYPE STREQUAL "Release") 31 | #Static link libpci in release build 32 | #set(OS_LINK_LIBRARY libpci.a) 33 | #else() 34 | set(OS_LINK_LIBRARY pci) 35 | #endif() 36 | endif() 37 | 38 | LINK_DIRECTORIES(${OS_LINK_DIR}) 39 | 40 | set(COMMON_SOURCES lib/nb_smu_ops.c lib/api.c lib/cpuid.c) 41 | add_definitions(-D_LIBRYZENADJ_INTERNAL) 42 | 43 | ADD_EXECUTABLE(${PROJECT_NAME} ${OS_SOURCE} ${COMMON_SOURCES} argparse.c main.c) 44 | target_link_libraries(${PROJECT_NAME} ${OS_LINK_LIBRARY}) 45 | #SET_TARGET_PROPERTIES(${PROJECT_NAME} PROPERTIES LINKER_LANGUAGE C) 46 | option(BUILD_SHARED_LIBS "Build using shared libraries" ON) 47 | ADD_LIBRARY (libryzenadj ${OS_SOURCE} ${COMMON_SOURCES}) 48 | set_target_properties(libryzenadj PROPERTIES PREFIX "") 49 | target_link_libraries(libryzenadj ${OS_LINK_LIBRARY}) 50 | #SET_TARGET_PROPERTIES(libryzenadj PROPERTIES LINKER_LANGUAGE C) 51 | install(TARGETS ${PROJECT_NAME} DESTINATION ${CMAKE_INSTALL_BINDIR}) 52 | -------------------------------------------------------------------------------- /lib/nb_smu_ops.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: LGPL */ 2 | /* Copyright (C) 2018-2019 Jiaxun Yang */ 3 | /* Ryzen NB SMU Service Request Opreations */ 4 | 5 | #pragma once 6 | 7 | #ifdef _WIN32 8 | #define WIN32_LEAN_AND_MEAN 9 | #include 10 | #endif 11 | #include 12 | #include 13 | #include 14 | 15 | #ifdef NDEBUG 16 | #define DBG(...) 17 | #else 18 | #define DBG(...) fprintf(stderr, __VA_ARGS__) 19 | #endif 20 | 21 | enum SMU_TYPE{ 22 | TYPE_MP1, 23 | TYPE_PSMU, 24 | TYPE_COUNT, 25 | }; 26 | 27 | #define NB_PCI_REG_ADDR_ADDR 0xB8 28 | #define NB_PCI_REG_DATA_ADDR 0xBC 29 | 30 | #define REP_MSG_OK 0x1 31 | #define REP_MSG_Failed 0xFF 32 | #define REP_MSG_UnknownCmd 0xFE 33 | #define REP_MSG_CmdRejectedPrereq 0xFD 34 | #define REP_MSG_CmdRejectedBusy 0xFC 35 | 36 | typedef struct _smu_service_args_t { 37 | uint32_t arg0; 38 | uint32_t arg1; 39 | uint32_t arg2; 40 | uint32_t arg3; 41 | uint32_t arg4; 42 | uint32_t arg5; 43 | } smu_service_args_t; 44 | 45 | typedef struct { 46 | #ifdef _WIN32 47 | uint32_t pci_address; 48 | HINSTANCE inpoutDll; 49 | #else 50 | union { 51 | struct { 52 | struct pci_access *pci_acc; 53 | struct pci_dev *pci_dev; 54 | } mem; 55 | struct { 56 | int smn_fd; 57 | int pm_table_fd; 58 | size_t pm_table_size; 59 | } kmod; 60 | } access; 61 | #endif 62 | } os_access_obj_t; 63 | 64 | typedef struct _smu_t { 65 | os_access_obj_t *os_access; 66 | uint32_t msg; 67 | uint32_t rep; 68 | uint32_t arg_base; 69 | } *smu_t; 70 | 71 | os_access_obj_t *init_os_access_obj(); 72 | int init_mem_obj(os_access_obj_t *os_access, uintptr_t physAddr); 73 | int copy_pm_table(const os_access_obj_t *obj, void *buffer, size_t size); 74 | int compare_pm_table(const void *buffer, size_t size); 75 | void free_os_access_obj(os_access_obj_t *obj); 76 | 77 | uint32_t smn_reg_read(const os_access_obj_t *obj, uint32_t addr); 78 | void smn_reg_write(const os_access_obj_t *obj, uint32_t addr, uint32_t data); 79 | bool is_using_smu_driver(); 80 | 81 | smu_t get_smu(os_access_obj_t *obj, int smu_type); 82 | uint32_t smu_service_req(smu_t smu, uint32_t id, smu_service_args_t *args); 83 | -------------------------------------------------------------------------------- /examples/pmtable-example.py: -------------------------------------------------------------------------------- 1 | import os, sys, time 2 | from ctypes import * 3 | from shutil import copyfile 4 | 5 | lib_path = os.path.dirname(os.path.abspath(__file__)) 6 | os.chdir(lib_path) 7 | 8 | if sys.platform == 'win32' or sys.platform == 'cygwin': 9 | try: 10 | os.add_dll_directory(lib_path) 11 | except AttributeError: 12 | pass #not needed for old python version 13 | 14 | winring0_driver_file_path = os.path.join(os.path.dirname(os.path.abspath(sys.executable)), 'WinRing0x64.sys') 15 | if not os.path.isfile(winring0_driver_file_path): 16 | copyfile(os.path.join(lib_path, 'WinRing0x64.sys'), winring0_driver_file_path) 17 | 18 | lib = cdll.LoadLibrary('libryzenadj') 19 | else: 20 | lib = cdll.LoadLibrary('libryzenadj.so') 21 | 22 | # define ctype mappings for types which can not be mapped automatically 23 | lib.init_ryzenadj.restype = c_void_p 24 | lib.get_table_ver.argtypes = [c_void_p] 25 | lib.get_table_size.argtypes = [c_void_p] 26 | lib.get_table_values.restype = POINTER(c_float) 27 | lib.get_table_values.argtypes = [c_void_p] 28 | lib.refresh_table.argtypes = [c_void_p] 29 | 30 | ry = lib.init_ryzenadj() 31 | 32 | if not ry: 33 | sys.exit("RyzenAdj could not get initialized") 34 | 35 | print("pmtable version: {:x}".format(lib.get_table_ver(ry))) 36 | 37 | input("Press any key to show all pmtable values...") 38 | 39 | pmtable_size = lib.get_table_size(ry) // 4 40 | pmtable = lib.get_table_values(ry) 41 | 42 | while True: 43 | lib.refresh_table(ry) 44 | columns, lines = os.get_terminal_size() 45 | table_columns = columns // 16 # 16 chars per table entry 46 | os.system('cls' if sys.platform == 'win32' else 'clear') 47 | table_rows = 0 48 | for index in (range(pmtable_size)): 49 | sys.stdout.write("{:3d}:{:8.2f}\t".format(index, pmtable[index])) 50 | if index % table_columns == table_columns - 1: 51 | sys.stdout.write('\n') 52 | table_rows += 1 53 | if table_rows >= lines - 1: 54 | sys.stdout.write('{:d} More entries ...'.format(pmtable_size - 1 - index)) 55 | break 56 | 57 | if index % table_columns != table_columns - 1: sys.stdout.write('\n') 58 | sys.stdout.flush() 59 | time.sleep(1) 60 | -------------------------------------------------------------------------------- /lib/win32/OlsDef.h: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------------------- 2 | // Author : hiyohiyo 3 | // Mail : hiyohiyo@crystalmark.info 4 | // Web : http://openlibsys.org/ 5 | // License : The modified BSD license 6 | // 7 | // Copyright 2007 OpenLibSys.org. All rights reserved. 8 | //----------------------------------------------------------------------------- 9 | 10 | #pragma once 11 | 12 | //----------------------------------------------------------------------------- 13 | // 14 | // DLL Status Code 15 | // 16 | //----------------------------------------------------------------------------- 17 | 18 | #define OLS_DLL_NO_ERROR 0 19 | #define OLS_DLL_UNSUPPORTED_PLATFORM 1 20 | #define OLS_DLL_DRIVER_NOT_LOADED 2 21 | #define OLS_DLL_DRIVER_NOT_FOUND 3 22 | #define OLS_DLL_DRIVER_UNLOADED 4 23 | #define OLS_DLL_DRIVER_NOT_LOADED_ON_NETWORK 5 24 | #define OLS_DLL_UNKNOWN_ERROR 9 25 | 26 | //----------------------------------------------------------------------------- 27 | // 28 | // Driver Type 29 | // 30 | //----------------------------------------------------------------------------- 31 | 32 | #define OLS_DRIVER_TYPE_UNKNOWN 0 33 | #define OLS_DRIVER_TYPE_WIN_9X 1 34 | #define OLS_DRIVER_TYPE_WIN_NT 2 35 | #define OLS_DRIVER_TYPE_WIN_NT4 3 // Obsolete 36 | #define OLS_DRIVER_TYPE_WIN_NT_X64 4 37 | #define OLS_DRIVER_TYPE_WIN_NT_IA64 5 // Reseved 38 | 39 | //----------------------------------------------------------------------------- 40 | // 41 | // PCI Error Code 42 | // 43 | //----------------------------------------------------------------------------- 44 | 45 | #define OLS_ERROR_PCI_BUS_NOT_EXIST (0xE0000001L) 46 | #define OLS_ERROR_PCI_NO_DEVICE (0xE0000002L) 47 | #define OLS_ERROR_PCI_WRITE_CONFIG (0xE0000003L) 48 | #define OLS_ERROR_PCI_READ_CONFIG (0xE0000004L) 49 | 50 | //----------------------------------------------------------------------------- 51 | // 52 | // Support Macros 53 | // 54 | //----------------------------------------------------------------------------- 55 | 56 | // Bus Number, Device Number and Function Number to PCI Device Address 57 | #define PciBusDevFunc(Bus, Dev, Func) ((Bus&0xFF)<<8) | ((Dev&0x1F)<<3) | (Func&7) 58 | // PCI Device Address to Bus Number 59 | #define PciGetBus(address) ((address>>8) & 0xFF) 60 | // PCI Device Address to Device Number 61 | #define PciGetDev(address) ((address>>3) & 0x1F) 62 | // PCI Device Address to Function Number 63 | #define PciGetFunc(address) (address&7) 64 | -------------------------------------------------------------------------------- /examples/readjust.py: -------------------------------------------------------------------------------- 1 | import os, sys, time 2 | from ctypes import * 3 | from shutil import copyfile 4 | 5 | lib_path = os.path.dirname(os.path.abspath(__file__)) 6 | os.chdir(lib_path) 7 | 8 | if sys.platform == 'win32' or sys.platform == 'cygwin': 9 | try: 10 | os.add_dll_directory(lib_path) 11 | except AttributeError: 12 | pass #not needed for old python version 13 | 14 | winring0_driver_file_path = os.path.join(os.path.dirname(os.path.abspath(sys.executable)), 'WinRing0x64.sys') 15 | if not os.path.isfile(winring0_driver_file_path): 16 | copyfile(os.path.join(lib_path, 'WinRing0x64.sys'), winring0_driver_file_path) 17 | 18 | lib = cdll.LoadLibrary('libryzenadj') 19 | else: 20 | lib = cdll.LoadLibrary('libryzenadj.so') 21 | 22 | # define ctype mappings for types which can not be mapped automatically 23 | lib.init_ryzenadj.restype = c_void_p 24 | lib.refresh_table.argtypes = [c_void_p] 25 | lib.get_fast_limit.restype = c_float 26 | lib.get_fast_limit.argtypes = [c_void_p] 27 | 28 | ry = lib.init_ryzenadj() 29 | 30 | if not ry: 31 | sys.exit("RyzenAdj could not get initialized") 32 | 33 | error_messages = { 34 | -1: "{:s} is not supported on this family\n", 35 | -3: "{:s} is not supported on this SMU\n", 36 | -4: "{:s} is rejected by SMU\n" 37 | } 38 | 39 | def adjust(field, value): 40 | function_name = "set_" + field 41 | adjust_func = lib.__getattr__(function_name) 42 | adjust_func.argtypes = [c_void_p, c_ulong] 43 | res = adjust_func(ry, value) 44 | if res: 45 | error = error_messages.get(res, "{:s} did fail with {:d}\n") 46 | sys.stderr.write(error.format(function_name, res)); 47 | 48 | def enable(field): 49 | function_name = "set_" + field 50 | adjust_func = lib.__getattr__(function_name) 51 | adjust_func.argtypes = [c_void_p] 52 | res = adjust_func(ry) 53 | if res: 54 | error = error_messages.get(res, "{:s} did fail with {:d}\n") 55 | sys.stderr.write(error.format(function_name, res)); 56 | 57 | print("Monitor if fast limit is not 35W") 58 | while True: 59 | lib.refresh_table(ry) 60 | limit = round(lib.get_fast_limit(ry)) 61 | if limit != 35: 62 | print("reapply limits, because old limit was {:d}".format(limit)) 63 | adjust("fast_limit", 35000) 64 | adjust("slow_limit", 22000) 65 | adjust("slow_time", 30) 66 | adjust("tctl_temp", 97) 67 | adjust("apu_skin_temp_limit", 50) 68 | adjust("vrmmax_current", 100000) 69 | enable("max_performance") 70 | time.sleep(3) 71 | -------------------------------------------------------------------------------- /lib/linux/osdep_linux.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: LGPL 2 | /* Copyright (C) 2018-2019 Jiaxun Yang 3 | */ 4 | #include 5 | 6 | #include "osdep_linux_mem.h" 7 | #include "osdep_linux_smu_kernel_module.h" 8 | 9 | bool is_smu = false; 10 | 11 | static bool is_ryzen_smu_driver_compatible() { 12 | FILE *drv_ver = fopen("/sys/kernel/ryzen_smu_drv/drv_version", "r"); 13 | int major, minor, patch, ret; 14 | 15 | if (drv_ver == NULL) { 16 | DBG("failed to open drv_version"); 17 | return false; 18 | } 19 | 20 | ret = fscanf(drv_ver, "%d.%d.%d", &major, &minor, &patch); 21 | if (ret == EOF || ret < 3) { 22 | DBG("failed to parse ryzen_smu version string\n"); 23 | fclose(drv_ver); 24 | return false; 25 | } 26 | 27 | if (major != 0 || minor != 1 || patch < 7) { 28 | fclose(drv_ver); 29 | return false; 30 | } 31 | 32 | fclose(drv_ver); 33 | return true; 34 | } 35 | 36 | os_access_obj_t *init_os_access_obj() { 37 | struct stat stats; 38 | 39 | if (lstat("/sys/kernel/ryzen_smu_drv", &stats) == 0 && is_ryzen_smu_driver_compatible()) { 40 | fprintf(stderr, "detected compatible ryzen_smu kernel module\n"); 41 | is_smu = true; 42 | return init_os_access_obj_kmod(); 43 | } 44 | 45 | fprintf(stderr, "no compatible ryzen_smu kernel module found, fallback to /dev/mem\n"); 46 | return init_os_access_obj_mem(); 47 | } 48 | 49 | int init_mem_obj(os_access_obj_t *os_access, const uintptr_t physAddr) { 50 | if (is_smu) 51 | return init_mem_obj_kmod(os_access, physAddr); 52 | 53 | return init_mem_obj_mem(os_access, physAddr); 54 | } 55 | 56 | void free_os_access_obj(os_access_obj_t *obj) { 57 | if (is_smu) 58 | free_os_access_obj_kmod(obj); 59 | else 60 | free_os_access_obj_mem(obj); 61 | 62 | is_smu = false; 63 | } 64 | 65 | uint32_t smn_reg_read(const os_access_obj_t *obj, const uint32_t addr) { 66 | if (is_smu) 67 | return smn_reg_read_kmod(obj, addr); 68 | 69 | return smn_reg_read_mem(obj, addr); 70 | } 71 | 72 | void smn_reg_write(const os_access_obj_t *obj, const uint32_t addr, const uint32_t data) { 73 | if (is_smu) 74 | smn_reg_write_kmod(obj, addr, data); 75 | else 76 | smn_reg_write_mem(obj, addr, data); 77 | } 78 | 79 | int copy_pm_table(const os_access_obj_t *obj, void *buffer, const size_t size) { 80 | if (is_smu) 81 | return copy_pm_table_kmod(obj, buffer, size); 82 | 83 | return copy_pm_table_mem(obj, buffer, size); 84 | } 85 | 86 | int compare_pm_table(const void *buffer, const size_t size) { 87 | if (is_smu) 88 | return compare_pm_table_kmod(buffer, size); 89 | 90 | return compare_pm_table_mem(buffer, size); 91 | } 92 | 93 | bool is_using_smu_driver() { 94 | return is_smu; 95 | } 96 | -------------------------------------------------------------------------------- /lib/linux/osdep_linux_mem.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: LGPL 2 | /* Copyright (C) 2018-2019 Jiaxun Yang 3 | * 2025 kylon - 0.20 4 | */ 5 | /* Access PCI Config Space - libpci */ 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #include "osdep_linux_mem.h" 15 | 16 | void *phy_map = MAP_FAILED; 17 | 18 | os_access_obj_t *init_os_access_obj_mem() { 19 | os_access_obj_t *obj = malloc(sizeof(os_access_obj_t)); 20 | 21 | if (obj == NULL) 22 | return NULL; 23 | 24 | memset(obj, 0, sizeof(os_access_obj_t)); 25 | 26 | obj->access.mem.pci_acc = pci_alloc(); 27 | if (!obj->access.mem.pci_acc) { 28 | fprintf(stderr, "pci_alloc failed\n"); 29 | goto err_exit; 30 | } 31 | 32 | pci_init(obj->access.mem.pci_acc); 33 | 34 | obj->access.mem.pci_dev = pci_get_dev(obj->access.mem.pci_acc, 0, 0, 0, 0); 35 | if (!obj->access.mem.pci_dev) { 36 | fprintf(stderr, "Unable to get pci device\n"); 37 | pci_cleanup(obj->access.mem.pci_acc); 38 | goto err_exit; 39 | } 40 | 41 | pci_fill_info(obj->access.mem.pci_dev, PCI_FILL_IDENT | PCI_FILL_BASES | PCI_FILL_CLASS); 42 | return obj; 43 | 44 | err_exit: 45 | free(obj); 46 | return NULL; 47 | } 48 | 49 | int init_mem_obj_mem([[maybe_unused]] os_access_obj_t *os_access, const uintptr_t physAddr) { 50 | const int dev_mem_fd = open("/dev/mem", O_RDONLY); 51 | 52 | // It is too complicated to check PAT, CONFIG_NONPROMISC_DEVMEM, CONFIG_STRICT_DEVMEM or other dependencies, just try to open /dev/mem 53 | if (dev_mem_fd > 0) { 54 | phy_map = mmap(NULL, 0x1000, PROT_READ, MAP_SHARED, dev_mem_fd, (long)physAddr); 55 | close(dev_mem_fd); 56 | } 57 | 58 | return phy_map == MAP_FAILED ? -1 : 0; 59 | } 60 | 61 | void free_os_access_obj_mem(os_access_obj_t *obj) { 62 | if (obj == NULL) 63 | return; 64 | 65 | if (obj->access.mem.pci_dev) 66 | pci_free_dev(obj->access.mem.pci_dev); 67 | 68 | if (obj->access.mem.pci_acc) 69 | pci_cleanup(obj->access.mem.pci_acc); 70 | 71 | if (phy_map != MAP_FAILED) { 72 | munmap(phy_map, 0x1000); 73 | phy_map = MAP_FAILED; 74 | } 75 | 76 | free(obj); 77 | } 78 | 79 | uint32_t smn_reg_read_mem(const os_access_obj_t *obj, const uint32_t addr) { 80 | pci_write_long(obj->access.mem.pci_dev, NB_PCI_REG_ADDR_ADDR, addr & (~0x3)); 81 | return pci_read_long(obj->access.mem.pci_dev, NB_PCI_REG_DATA_ADDR); 82 | } 83 | 84 | void smn_reg_write_mem(const os_access_obj_t *obj, const uint32_t addr, const uint32_t data) { 85 | pci_write_long(obj->access.mem.pci_dev, NB_PCI_REG_ADDR_ADDR, addr); 86 | pci_write_long(obj->access.mem.pci_dev, NB_PCI_REG_DATA_ADDR, data); 87 | } 88 | 89 | int copy_pm_table_mem([[maybe_unused]] const os_access_obj_t *obj, void *buffer, const size_t size) { 90 | if (phy_map != MAP_FAILED) { 91 | memcpy(buffer, phy_map, size); 92 | return 0; 93 | } 94 | 95 | DBG("failed to get pm_table from /dev/mem\n"); 96 | return -1; 97 | } 98 | 99 | int compare_pm_table_mem(const void *buffer, const size_t size) { 100 | return memcmp(buffer, phy_map, size); 101 | } 102 | -------------------------------------------------------------------------------- /lib/linux/osdep_linux_smu_kernel_module.c: -------------------------------------------------------------------------------- 1 | /* Backend which uses the sysfs interface provided by the ryzen_smu kernel module. */ 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "osdep_linux_smu_kernel_module.h" 9 | 10 | static uint32_t get_pm_table_size() { 11 | const int fd = open("/sys/kernel/ryzen_smu_drv/pm_table_size", O_RDONLY); 12 | uint32_t table_sz = 0; 13 | 14 | if (fd == -1) 15 | return -1; 16 | 17 | if (read(fd, &table_sz, sizeof(table_sz)) == -1) { 18 | DBG("failed to retrieve PM table size: %s\n", strerror(errno)); 19 | close(fd); 20 | return -1; 21 | } 22 | 23 | close(fd); 24 | return table_sz; 25 | } 26 | 27 | os_access_obj_t *init_os_access_obj_kmod() { 28 | os_access_obj_t *obj = malloc(sizeof(os_access_obj_t)); 29 | 30 | if (obj == NULL) 31 | return NULL; 32 | 33 | memset(obj, 0, sizeof(os_access_obj_t)); 34 | 35 | obj->access.kmod.pm_table_size = get_pm_table_size(); 36 | if (obj->access.kmod.pm_table_size == -1) 37 | goto err_exit; 38 | 39 | obj->access.kmod.smn_fd = open("/sys/kernel/ryzen_smu_drv/smn", O_RDWR); 40 | if (obj->access.kmod.smn_fd == -1) { 41 | DBG("failed to open smn fd: %s\n", strerror(errno)); 42 | goto err_exit; 43 | } 44 | 45 | obj->access.kmod.pm_table_fd = open("/sys/kernel/ryzen_smu_drv/pm_table", O_RDONLY); 46 | if (obj->access.kmod.pm_table_fd == -1) { 47 | DBG("failed to open pm_table fd: %s\n", strerror(errno)); 48 | close(obj->access.kmod.smn_fd); 49 | goto err_exit; 50 | } 51 | 52 | return obj; 53 | 54 | err_exit: 55 | free(obj); 56 | return NULL; 57 | } 58 | 59 | int init_mem_obj_kmod([[maybe_unused]] os_access_obj_t *os_access, [[maybe_unused]] const uintptr_t physAddr) { 60 | return 0; 61 | } 62 | 63 | void free_os_access_obj_kmod(os_access_obj_t *obj) { 64 | close(obj->access.kmod.smn_fd); 65 | close(obj->access.kmod.pm_table_fd); 66 | free(obj); 67 | } 68 | 69 | uint32_t smn_reg_read_kmod(const os_access_obj_t *obj, const uint32_t addr) { 70 | uint32_t result = 0; 71 | 72 | lseek(obj->access.kmod.smn_fd, 0, SEEK_SET); 73 | 74 | if (write(obj->access.kmod.smn_fd, &addr, sizeof(addr)) == -1) { 75 | DBG("%s: write error: %s\n", __func__, strerror(errno)); 76 | return 0; 77 | } 78 | 79 | lseek(obj->access.kmod.smn_fd, 0, SEEK_SET); 80 | 81 | if (read(obj->access.kmod.smn_fd, &result, sizeof(result)) == -1) { 82 | DBG("%s: read error: %s\n", __func__, strerror(errno)); 83 | return 0; 84 | } 85 | 86 | return result; 87 | } 88 | 89 | void smn_reg_write_kmod(const os_access_obj_t *obj, const uint32_t addr, const uint32_t data) { 90 | const uint32_t write_buffer[2] = { addr, data }; 91 | 92 | lseek(obj->access.kmod.smn_fd, 0, SEEK_SET); 93 | 94 | if (write(obj->access.kmod.smn_fd, &write_buffer, sizeof(write_buffer)) == -1) 95 | DBG("%s: error: %s\n", __func__, strerror(errno)); 96 | } 97 | 98 | int copy_pm_table_kmod(const os_access_obj_t *obj, void *buffer, const size_t size) { 99 | if (obj->access.kmod.pm_table_size != size) { 100 | DBG("PM table size mismatch: ryzenadj (%zd) | ryzen_smu (%zd)\n", size, obj->access.kmod.pm_table_size); 101 | return -1; 102 | } 103 | 104 | lseek(obj->access.kmod.pm_table_fd, 0, SEEK_SET); 105 | 106 | if (read(obj->access.kmod.pm_table_fd, buffer, size) == -1) { 107 | DBG("%s: error: %s\n", __func__, strerror(errno)); 108 | return -1; 109 | } 110 | 111 | return 0; 112 | } 113 | 114 | int compare_pm_table_kmod([[maybe_unused]] const void *buffer, [[maybe_unused]] size_t size) { 115 | DBG("internal error: compare_pm_table() should never be called if ryzen_smu is loaded\n"); 116 | return -1; 117 | } 118 | -------------------------------------------------------------------------------- /lib/cpuid.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: LGPL 2 | /* Copyright (C) 2020 Jiaxun Yang */ 3 | /* RyzenAdj CPU ID stuff */ 4 | 5 | #include "ryzenadj.h" 6 | 7 | #include 8 | 9 | #if defined(__GNUC__) 10 | #include 11 | #elif defined (_WIN32) 12 | #include 13 | #endif 14 | 15 | #define CPUID_VENDOR_AMD "AuthenticAMD" 16 | 17 | static void getcpuid(unsigned int CPUInfo[4], unsigned int InfoType) 18 | { 19 | #if defined(__GNUC__) 20 | __cpuid(InfoType, CPUInfo[0],CPUInfo[1],CPUInfo[2],CPUInfo[3]); 21 | #elif defined(_WIN32) 22 | __cpuid((int*)(void*)CPUInfo, (int)InfoType); 23 | #endif 24 | } 25 | 26 | static enum ryzen_family cpuid_family = WAIT_FOR_LOAD; 27 | 28 | static enum ryzen_family cpuid_load_family() 29 | { 30 | uint32_t regs[4]; 31 | int family, model; 32 | char vendor[4 * 4]; 33 | 34 | getcpuid(regs, 0); 35 | 36 | /* Hack Alert! Put into str buffer */ 37 | *(uint32_t *) &vendor[0] = regs[1]; 38 | *(uint32_t *) &vendor[4] = regs[3]; 39 | *(uint32_t *) &vendor[8] = regs[2]; 40 | 41 | if (strncmp(vendor, CPUID_VENDOR_AMD, strlen(CPUID_VENDOR_AMD))) { 42 | printf("Not AMD processor, must be kidding\n"); 43 | return FAM_UNKNOWN; 44 | } 45 | 46 | getcpuid(regs, 1); 47 | 48 | family = ((regs[0] >> 8) & 0xf) + ((regs[0] >> 20) & 0xff); 49 | model = ((regs[0] >> 4) & 0xf) | ((regs[0] >> 12) & 0xf0); 50 | 51 | switch (family) { 52 | case 0x17: /* Zen, Zen+, Zen2 */ 53 | switch (model) { 54 | case 17: 55 | return FAM_RAVEN; 56 | case 24: 57 | return FAM_PICASSO; 58 | case 32: 59 | return FAM_DALI; 60 | case 96: 61 | return FAM_RENOIR; 62 | case 104: 63 | return FAM_LUCIENNE; 64 | case 144: 65 | case 145: 66 | return FAM_VANGOGH; 67 | case 160: 68 | return FAM_MENDOCINO; 69 | default: 70 | printf("Fam%xh: unsupported model %d\n", family, model); 71 | break; 72 | }; 73 | break; 74 | 75 | case 0x19: /* Zen3, Zen4 */ 76 | switch (model) { 77 | case 80: 78 | return FAM_CEZANNE; 79 | case 64: 80 | case 68: 81 | return FAM_REMBRANDT; 82 | case 97: 83 | return FAM_DRAGONRANGE; 84 | case 116: 85 | case 120: 86 | return FAM_PHOENIX; 87 | case 117: 88 | return FAM_HAWKPOINT; 89 | default: 90 | printf("Fam%xh: unsupported model %d\n", family, model); 91 | break; 92 | }; 93 | break; 94 | case 0x1A: /* Zen5, Zen6 */ 95 | switch (model) { 96 | case 32: 97 | case 36: 98 | return FAM_STRIXPOINT; 99 | case 68: 100 | return FAM_FIRERANGE; 101 | case 96: 102 | return FAM_KRACKANPOINT; 103 | case 112: 104 | return FAM_STRIXHALO; 105 | default: 106 | printf("Fam%xh: unsupported model %d\n", family, model); 107 | break; 108 | } 109 | 110 | default: 111 | printf("Unsupported family: %xh\n", family); 112 | break; 113 | } 114 | 115 | printf("Only Ryzen Mobile Series are supported\n"); 116 | return FAM_UNKNOWN; 117 | } 118 | 119 | /* 120 | * The function cpuid_load_family() processes a lot of 121 | * information. This information will be used several 122 | * times by acquiring the same data. Avoid this wasted 123 | * computation by entering cpuid_get_family(). This function 124 | * guarantees the load is done only 1 time. 125 | */ 126 | enum ryzen_family cpuid_get_family() { 127 | if (cpuid_family == WAIT_FOR_LOAD) 128 | cpuid_family = cpuid_load_family(); 129 | 130 | return cpuid_family; 131 | } 132 | -------------------------------------------------------------------------------- /argparse.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2012-2015 Yecheng Fu 3 | * All rights reserved. 4 | * 5 | * Use of this source code is governed by a MIT-style license that can be found 6 | * in the LICENSE file. 7 | */ 8 | #ifndef ARGPARSE_H 9 | #define ARGPARSE_H 10 | 11 | /* For c++ compatibility */ 12 | #ifdef __cplusplus 13 | extern "C" { 14 | #endif 15 | 16 | #include 17 | 18 | struct argparse; 19 | struct argparse_option; 20 | 21 | typedef int argparse_callback (struct argparse *self, 22 | const struct argparse_option *option); 23 | 24 | enum argparse_flag { 25 | ARGPARSE_STOP_AT_NON_OPTION = 1, 26 | ARGPARSE_NON_OPTION_IS_INVALID = 2, 27 | }; 28 | 29 | enum argparse_option_type { 30 | /* special */ 31 | ARGPARSE_OPT_END, 32 | ARGPARSE_OPT_GROUP, 33 | /* options with no arguments */ 34 | ARGPARSE_OPT_BOOLEAN, 35 | ARGPARSE_OPT_BIT, 36 | /* options with arguments (optional or required) */ 37 | ARGPARSE_OPT_INTEGER, 38 | ARGPARSE_OPT_U32, 39 | ARGPARSE_OPT_FLOAT, 40 | ARGPARSE_OPT_STRING, 41 | }; 42 | 43 | enum argparse_option_flags { 44 | OPT_NONEG = 1, /* disable negation */ 45 | }; 46 | 47 | /** 48 | * argparse option 49 | * 50 | * `type`: 51 | * holds the type of the option, you must have an ARGPARSE_OPT_END last in your 52 | * array. 53 | * 54 | * `short_name`: 55 | * the character to use as a short option name, '\0' if none. 56 | * 57 | * `long_name`: 58 | * the long option name, without the leading dash, NULL if none. 59 | * 60 | * `value`: 61 | * stores pointer to the value to be filled. 62 | * 63 | * `help`: 64 | * the short help message associated to what the option does. 65 | * Must never be NULL (except for ARGPARSE_OPT_END). 66 | * 67 | * `callback`: 68 | * function is called when corresponding argument is parsed. 69 | * 70 | * `data`: 71 | * associated data. Callbacks can use it like they want. 72 | * 73 | * `flags`: 74 | * option flags. 75 | */ 76 | struct argparse_option { 77 | enum argparse_option_type type; 78 | const char short_name; 79 | const char *long_name; 80 | void *value; 81 | const char *help; 82 | argparse_callback *callback; 83 | intptr_t data; 84 | int flags; 85 | }; 86 | 87 | /** 88 | * argpparse 89 | */ 90 | struct argparse { 91 | // user supplied 92 | const struct argparse_option *options; 93 | const char *const *usages; 94 | int flags; 95 | const char *description; // a description after usage 96 | const char *epilog; // a description at the end 97 | // internal context 98 | int argc; 99 | const char **argv; 100 | const char **out; 101 | int cpidx; 102 | const char *optvalue; // current option value 103 | }; 104 | 105 | // built-in callbacks 106 | int argparse_help_cb(struct argparse *self, 107 | const struct argparse_option *option); 108 | 109 | // built-in option macros 110 | #define OPT_END() { ARGPARSE_OPT_END, 0, NULL, NULL, 0, NULL, 0, 0 } 111 | #define OPT_BOOLEAN(...) { ARGPARSE_OPT_BOOLEAN, __VA_ARGS__ } 112 | #define OPT_BIT(...) { ARGPARSE_OPT_BIT, __VA_ARGS__ } 113 | #define OPT_INTEGER(...) { ARGPARSE_OPT_INTEGER, __VA_ARGS__ } 114 | #define OPT_U32(...) { ARGPARSE_OPT_U32, __VA_ARGS__ } 115 | #define OPT_FLOAT(...) { ARGPARSE_OPT_FLOAT, __VA_ARGS__ } 116 | #define OPT_STRING(...) { ARGPARSE_OPT_STRING, __VA_ARGS__ } 117 | #define OPT_GROUP(h) { ARGPARSE_OPT_GROUP, 0, NULL, NULL, h, NULL, 0, 0 } 118 | #define OPT_HELP() OPT_BOOLEAN('h', "help", NULL, \ 119 | "show this help message and exit", \ 120 | argparse_help_cb, 0, OPT_NONEG) 121 | 122 | int argparse_init(struct argparse *self, struct argparse_option *options, 123 | const char *const *usages, int flags); 124 | void argparse_describe(struct argparse *self, const char *description, 125 | const char *epilog); 126 | int argparse_parse(struct argparse *self, int argc, const char **argv); 127 | void argparse_usage(struct argparse *self); 128 | 129 | #ifdef __cplusplus 130 | } 131 | #endif 132 | 133 | #endif 134 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Build 2 | 3 | on: 4 | push: 5 | pull_request: 6 | 7 | jobs: 8 | Windows: 9 | runs-on: windows-latest 10 | 11 | steps: 12 | # Checks-out 13 | - uses: actions/checkout@v2 14 | 15 | - uses: ilammy/msvc-dev-cmd@v1 16 | 17 | #don't use run-cmake for windows because only one build should add warnings to pull request 18 | - name: Build Debug 19 | run: | 20 | mkdir debug 21 | cd debug 22 | cmake -G "NMake Makefiles" -DCMAKE_BUILD_TYPE=Debug .. 23 | nmake 24 | 25 | - name: Upload ryzenadj debug 26 | uses: actions/upload-artifact@v4 27 | with: 28 | name: ryzenadj-win64-debug 29 | path: | 30 | debug/ryzenadj.exe 31 | debug/libryzenadj.dll 32 | 33 | - name: Build Release 34 | run: | 35 | mkdir build 36 | cd build 37 | cmake -G "NMake Makefiles" -DCMAKE_BUILD_TYPE=Release .. 38 | nmake 39 | 40 | - name: Prepair Release Folder 41 | run: | 42 | mkdir release 43 | copy build/ryzenadj.exe release/ 44 | copy build/libryzenadj.dll release/ 45 | copy win32/* release/ 46 | copy examples/* release/ 47 | 48 | # - name: Test Scripts 49 | # shell: pwsh 50 | # run: | 51 | # cd release 52 | # $expectedOutput = "Not AMD processor, must be kidding" 53 | # $cmdDemoOutput = '' | cmd.exe /c .\demo.bat 54 | # if ($cmdDemoOutput -notcontains $expectedOutput){ 55 | # Write-Error "$cmdDemoOutput" 56 | # exit 1 57 | # } 58 | # $psServiceOutput = Powershell .\readjustService.ps1 -noGUI 59 | # if ($psServiceOutput -ne $expectedOutput){ 60 | # Write-Error "$psServiceOutput" 61 | # exit 1 62 | # } 63 | # $pythonOutput = python pmtable-example.py 64 | # if ($pythonOutput -notcontains $expectedOutput){ 65 | # Write-Error "$pythonOutput" 66 | # exit 1 67 | # } 68 | # $pythonOutput = python readjust.py 69 | # if ($pythonOutput -notcontains $expectedOutput){ 70 | # Write-Error "$pythonOutput" 71 | # exit 1 72 | # } 73 | # exit 0 74 | 75 | - name: Upload ryzenadj 76 | uses: actions/upload-artifact@v4 77 | with: 78 | name: ryzenadj-win64 79 | path: | 80 | release/ 81 | !release/*.lib 82 | !release/*.exp 83 | 84 | - name: Upload libryzenadj 85 | uses: actions/upload-artifact@v4 86 | with: 87 | name: libryzenadj-win64 88 | path: | 89 | ./build/libryzenadj.dll 90 | ./build/libryzenadj.lib 91 | ./lib/ryzenadj.h 92 | ./win32/inpoutx64.dll 93 | ./win32/WinRing0x64.dll 94 | ./win32/WinRing0x64.sys 95 | 96 | Linux: 97 | runs-on: ubuntu-latest 98 | 99 | steps: 100 | - uses: actions/checkout@v2 101 | 102 | - name: Install Dependencies 103 | run: | 104 | sudo apt-get update 105 | sudo apt-get install libpci-dev 106 | 107 | - name: run-cmake #with support for inline error reporting 108 | uses: lukka/run-cmake@v3.3 109 | 110 | - name: Test make like readme 111 | run: | 112 | mkdir build && cd build 113 | cmake DCMAKE_BUILD_TYPE=Release .. 114 | make 115 | 116 | # - name: Test Scripts 117 | # shell: bash 118 | # run: | 119 | # cd examples 120 | # export LD_LIBRARY_PATH="${{ github.workspace }}/build/" 121 | # expectedOutput="Not AMD processor, must be kidding" 122 | # pythonOutput=$(python pmtable-example.py 2>&1) || true 123 | # if [[ ! "$pythonOutput" =~ "$expectedOutput" ]]; then 124 | # echo "pmtable-example.py output: $pythonOutput" 125 | # exit 1 126 | # fi 127 | # pythonOutput=$(python readjust.py 2>&1) || true 128 | # if [[ ! "$pythonOutput" =~ "$expectedOutput" ]]; then 129 | # echo "readjust.py output: $pythonOutput" 130 | # exit 1 131 | # fi 132 | # exit 0 133 | 134 | -------------------------------------------------------------------------------- /lib/win32/osdep_win32.cpp: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: LGPL 2 | /* Copyright (C) 2018-2019 Jiaxun Yang */ 3 | /* Access PCI Config Space - winring0 */ 4 | extern "C" { 5 | #include "../nb_smu_ops.h" 6 | } 7 | #include 8 | 9 | #include "OlsApi.h" 10 | #include "OlsDef.h" 11 | 12 | typedef BOOL (__stdcall *lpIsInpOutDriverOpen)(void); 13 | typedef PBYTE (__stdcall *lpMapPhysToLin)(uintptr_t pbPhysAddr, size_t dwPhysSize, HANDLE *pPhysicalMemoryHandle); 14 | typedef BOOL (__stdcall *lpUnmapPhysicalMemory)(HANDLE PhysicalMemoryHandle, uintptr_t pbLinAddr); 15 | typedef BOOL (__stdcall *lpGetPhysLong)(uintptr_t pbPhysAddr, uint32_t *pdwPhysVal); 16 | lpIsInpOutDriverOpen gfpIsInpOutDriverOpen; 17 | lpGetPhysLong gfpGetPhysLong; 18 | lpMapPhysToLin gfpMapPhysToLin; 19 | lpUnmapPhysicalMemory gfpUnmapPhysicalMemory; 20 | uint32_t *pdwLinAddr; 21 | HANDLE physicalMemoryHandle; 22 | 23 | #ifdef __cplusplus 24 | extern "C" { 25 | #endif 26 | 27 | os_access_obj_t *init_os_access_obj() { 28 | InitializeOls(); 29 | 30 | const DWORD dllStatus = GetDllStatus(); 31 | 32 | switch (dllStatus) { 33 | case OLS_DLL_NO_ERROR: { 34 | os_access_obj_t *obj = static_cast(std::malloc(sizeof(os_access_obj_t))); 35 | 36 | if (obj == NULL) 37 | return NULL; 38 | 39 | memset(obj, 0, sizeof(os_access_obj_t)); 40 | return obj; 41 | } 42 | case OLS_DLL_UNSUPPORTED_PLATFORM: 43 | DBG("WinRing0 Err: Unsupported Plattform\n"); 44 | break; 45 | case OLS_DLL_DRIVER_NOT_LOADED: 46 | DBG("WinRing0 Err: Driver not loaded\n"); 47 | break; 48 | case OLS_DLL_DRIVER_NOT_FOUND: 49 | DBG("WinRing0 Err: Driver not found\n"); 50 | break; 51 | case OLS_DLL_DRIVER_UNLOADED: 52 | DBG("WinRing0 Err: Driver unloaded\n"); 53 | break; 54 | case OLS_DLL_DRIVER_NOT_LOADED_ON_NETWORK: 55 | DBG("WinRing0 Err: Driver not loaded on network\n"); 56 | break; 57 | case OLS_DLL_UNKNOWN_ERROR: 58 | DBG("WinRing0 Err: unknown error\n"); 59 | break; 60 | default: 61 | DBG("WinRing0 Err: unknown status code %lu\n", dllStatus); 62 | break; 63 | } 64 | 65 | return NULL; 66 | } 67 | 68 | void free_os_access_obj(os_access_obj_t *obj) { 69 | if (obj == NULL) 70 | return; 71 | 72 | DeinitializeOls(); 73 | gfpUnmapPhysicalMemory(physicalMemoryHandle, *pdwLinAddr); 74 | FreeLibrary(obj->inpoutDll); 75 | free(obj); 76 | } 77 | 78 | uint32_t smn_reg_read(const os_access_obj_t *obj, uint32_t addr) { 79 | WritePciConfigDword(obj->pci_address, NB_PCI_REG_ADDR_ADDR, addr & (~0x3)); 80 | return ReadPciConfigDword(obj->pci_address, NB_PCI_REG_DATA_ADDR); 81 | } 82 | 83 | void smn_reg_write(const os_access_obj_t *obj, uint32_t addr, uint32_t data) { 84 | WritePciConfigDword(obj->pci_address, NB_PCI_REG_ADDR_ADDR, addr); 85 | WritePciConfigDword(obj->pci_address, NB_PCI_REG_DATA_ADDR, data); 86 | } 87 | 88 | int init_mem_obj(os_access_obj_t *os_access, uintptr_t physAddr) { 89 | HINSTANCE hInpOutDll = LoadLibrary ("inpoutx64.DLL"); 90 | 91 | if (hInpOutDll == NULL) { 92 | DBG("Could not load inpoutx64.DLL\n"); 93 | return -1; 94 | } 95 | 96 | gfpIsInpOutDriverOpen = (lpIsInpOutDriverOpen)GetProcAddress(hInpOutDll, "IsInpOutDriverOpen"); 97 | gfpGetPhysLong = (lpGetPhysLong)GetProcAddress(hInpOutDll, "GetPhysLong"); 98 | gfpMapPhysToLin = (lpMapPhysToLin)GetProcAddress(hInpOutDll, "MapPhysToLin"); 99 | gfpUnmapPhysicalMemory = (lpUnmapPhysicalMemory)GetProcAddress(hInpOutDll, "UnmapPhysicalMemory"); 100 | 101 | if (!gfpIsInpOutDriverOpen()) { 102 | DBG("Could not open inpoutx64 driver\n"); 103 | return -1; 104 | } 105 | 106 | pdwLinAddr = (uint32_t*)gfpMapPhysToLin(physAddr, 0x1000, &physicalMemoryHandle); 107 | if (pdwLinAddr == NULL) { 108 | DBG("failed to map memory\n"); 109 | return -1; 110 | } 111 | 112 | os_access->inpoutDll = hInpOutDll; 113 | 114 | return 0; 115 | } 116 | 117 | int copy_pm_table(const os_access_obj_t *obj, void *buffer, const size_t size) { 118 | memcpy(buffer, pdwLinAddr, size); 119 | return 0; 120 | } 121 | 122 | int compare_pm_table(const void *buffer, const size_t size) { 123 | return memcmp(buffer, pdwLinAddr, size); 124 | } 125 | 126 | bool is_using_smu_driver() { 127 | return false; 128 | } 129 | 130 | #ifdef __cplusplus 131 | } 132 | #endif 133 | -------------------------------------------------------------------------------- /lib/nb_smu_ops.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: LGPL 2 | /* Copyright (C) 2018-2019 Jiaxun Yang */ 3 | /* Ryzen NB SMU Service Request Opreations */ 4 | #include 5 | 6 | #include "ryzenadj.h" 7 | 8 | #define MP1_C2PMSG_MESSAGE_ADDR_1 0x3B10528 9 | #define MP1_C2PMSG_RESPONSE_ADDR_1 0x3B10564 10 | #define MP1_C2PMSG_ARG_BASE_1 0x3B10998 11 | 12 | /* For Vangogh and Rembrandt */ 13 | #define MP1_C2PMSG_MESSAGE_ADDR_2 0x3B10528 14 | #define MP1_C2PMSG_RESPONSE_ADDR_2 0x3B10578 15 | #define MP1_C2PMSG_ARG_BASE_2 0x3B10998 16 | 17 | /* For Strix Point */ 18 | #define MP1_C2PMSG_MESSAGE_ADDR_3 0x3b10928 19 | #define MP1_C2PMSG_RESPONSE_ADDR_3 0x3b10978 20 | #define MP1_C2PMSG_ARG_BASE_3 0x3b10998 21 | 22 | #define PSMU_C2PMSG_MESSAGE_ADDR_1 0x3B10a20 23 | #define PSMU_C2PMSG_RESPONSE_ADDR_1 0x3B10a80 24 | #define PSMU_C2PMSG_ARG_BASE_1 0x3B10a88 25 | 26 | /* For Dragon and Fire Range */ 27 | #define MP1_C2PMSG_MESSAGE_ADDR_4 0x3B10530 28 | #define MP1_C2PMSG_RESPONSE_ADDR_4 0x3B1057C 29 | #define MP1_C2PMSG_ARG_BASE_4 0x3B109C4 30 | 31 | #define PSMU_C2PMSG_MESSAGE_ADDR_2 0x03B10524 32 | #define PSMU_C2PMSG_RESPONSE_ADDR_2 0x03B10570 33 | #define PSMU_C2PMSG_ARG_BASE_2 0x03B10A40 34 | 35 | /* 36 | * All the SMU have the same TestMessage as for now 37 | * Correct me if they don't 38 | */ 39 | #define SMU_TEST_MSG 0x1 40 | 41 | static uint32_t c2pmsg_argX_addr(const uint32_t base, const uint32_t offt) { 42 | return base + 4 * offt; 43 | } 44 | 45 | uint32_t smu_service_req(smu_t smu, const uint32_t id, smu_service_args_t *args) { 46 | uint32_t response = 0x0; 47 | DBG("SMU_SERVICE REQ_ID:0x%x\n", id); 48 | DBG("SMU_SERVICE REQ: arg0: 0x%x, arg1:0x%x, arg2:0x%x, arg3:0x%x, arg4: 0x%x, arg5: 0x%x\n", \ 49 | args->arg0, args->arg1, args->arg2, args->arg3, args->arg4, args->arg5); 50 | 51 | /* Clear the response */ 52 | smn_reg_write(smu->os_access, smu->rep, 0x0); 53 | /* Pass arguments */ 54 | smn_reg_write(smu->os_access, c2pmsg_argX_addr(smu->arg_base, 0), args->arg0); 55 | smn_reg_write(smu->os_access, c2pmsg_argX_addr(smu->arg_base, 1), args->arg1); 56 | smn_reg_write(smu->os_access, c2pmsg_argX_addr(smu->arg_base, 2), args->arg2); 57 | smn_reg_write(smu->os_access, c2pmsg_argX_addr(smu->arg_base, 3), args->arg3); 58 | smn_reg_write(smu->os_access, c2pmsg_argX_addr(smu->arg_base, 4), args->arg4); 59 | smn_reg_write(smu->os_access, c2pmsg_argX_addr(smu->arg_base, 5), args->arg5); 60 | /* Send message ID */ 61 | smn_reg_write(smu->os_access, smu->msg, id); 62 | /* Wait until reponse changed */ 63 | while(response == 0x0) { 64 | response = smn_reg_read(smu->os_access, smu->rep); 65 | } 66 | /* Read back arguments */ 67 | args->arg0 = smn_reg_read(smu->os_access, c2pmsg_argX_addr(smu->arg_base, 0)); 68 | args->arg1 = smn_reg_read(smu->os_access, c2pmsg_argX_addr(smu->arg_base, 1)); 69 | args->arg2 = smn_reg_read(smu->os_access, c2pmsg_argX_addr(smu->arg_base, 2)); 70 | args->arg3 = smn_reg_read(smu->os_access, c2pmsg_argX_addr(smu->arg_base, 3)); 71 | args->arg4 = smn_reg_read(smu->os_access, c2pmsg_argX_addr(smu->arg_base, 4)); 72 | args->arg5 = smn_reg_read(smu->os_access, c2pmsg_argX_addr(smu->arg_base, 5)); 73 | 74 | DBG("SMU_SERVICE REP: REP: 0x%x, arg0: 0x%x, arg1:0x%x, arg2:0x%x, arg3:0x%x, arg4: 0x%x, arg5: 0x%x\n", \ 75 | response, args->arg0, args->arg1, args->arg2, args->arg3, args->arg4, args->arg5); 76 | 77 | return response; 78 | } 79 | 80 | static int smu_service_test(smu_t smu) 81 | { 82 | uint32_t response = 0x0; 83 | 84 | /* Clear the response */ 85 | smn_reg_write(smu->os_access, smu->rep, 0x0); 86 | /* Test message with unique argument */ 87 | smn_reg_write(smu->os_access, smu->arg_base, 0x47); 88 | if(smn_reg_read(smu->os_access, smu->arg_base) != 0x47){ 89 | printf("PCI Bus is not writeable, check secure boot\n"); 90 | return 0; 91 | } 92 | 93 | /* Send message ID */ 94 | smn_reg_write(smu->os_access, smu->msg, SMU_TEST_MSG); 95 | /* Wait until reponse changed */ 96 | while(response == 0x0) { 97 | response = smn_reg_read(smu->os_access, smu->rep); 98 | } 99 | 100 | return response == REP_MSG_OK; 101 | } 102 | 103 | smu_t get_smu(os_access_obj_t *obj, const int smu_type) { 104 | smu_t smu = malloc(sizeof(*smu)); 105 | 106 | if (smu == NULL) 107 | return NULL; 108 | 109 | smu->os_access = obj; 110 | 111 | /* Fill SMU information */ 112 | switch(smu_type){ 113 | case TYPE_MP1: 114 | switch (cpuid_get_family()) { 115 | case FAM_REMBRANDT: 116 | case FAM_VANGOGH: 117 | case FAM_MENDOCINO: 118 | case FAM_PHOENIX: 119 | case FAM_HAWKPOINT: 120 | smu->msg = MP1_C2PMSG_MESSAGE_ADDR_2; 121 | smu->rep = MP1_C2PMSG_RESPONSE_ADDR_2; 122 | smu->arg_base = MP1_C2PMSG_ARG_BASE_2; 123 | break; 124 | case FAM_KRACKANPOINT: 125 | case FAM_STRIXPOINT: 126 | case FAM_STRIXHALO: 127 | smu->msg = MP1_C2PMSG_MESSAGE_ADDR_3; 128 | smu->rep = MP1_C2PMSG_RESPONSE_ADDR_3; 129 | smu->arg_base = MP1_C2PMSG_ARG_BASE_3; 130 | break; 131 | case FAM_DRAGONRANGE: 132 | case FAM_FIRERANGE: 133 | smu->msg = MP1_C2PMSG_MESSAGE_ADDR_4; 134 | smu->rep = MP1_C2PMSG_RESPONSE_ADDR_4; 135 | smu->arg_base = MP1_C2PMSG_ARG_BASE_4; 136 | break; 137 | default: 138 | smu->msg = MP1_C2PMSG_MESSAGE_ADDR_1; 139 | smu->rep = MP1_C2PMSG_RESPONSE_ADDR_1; 140 | smu->arg_base = MP1_C2PMSG_ARG_BASE_1; 141 | break; 142 | } 143 | break; 144 | case TYPE_PSMU: 145 | switch (cpuid_get_family()) { 146 | case FAM_DRAGONRANGE: 147 | case FAM_FIRERANGE: 148 | smu->msg = PSMU_C2PMSG_MESSAGE_ADDR_2; 149 | smu->rep = PSMU_C2PMSG_RESPONSE_ADDR_2; 150 | smu->arg_base = PSMU_C2PMSG_ARG_BASE_2; 151 | break; 152 | default: 153 | smu->msg = PSMU_C2PMSG_MESSAGE_ADDR_1; 154 | smu->rep = PSMU_C2PMSG_RESPONSE_ADDR_1; 155 | smu->arg_base = PSMU_C2PMSG_ARG_BASE_1; 156 | break; 157 | } 158 | break; 159 | default: 160 | DBG("Failed to get SMU, unknown SMU_TYPE: %i\n", smu_type); 161 | goto err; 162 | break; 163 | } 164 | 165 | if(smu_service_test(smu)){ 166 | return smu; 167 | } else { 168 | DBG("Faild to get SMU, SMU_TYPE: %i\n", smu_type); 169 | goto err; 170 | } 171 | err: 172 | free(smu); 173 | return NULL; 174 | } 175 | -------------------------------------------------------------------------------- /lib/ryzenadj.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: LGPL */ 2 | /* Copyright (C) 2019 Jiaxun Yang */ 3 | /* RyzenAdj API */ 4 | 5 | #ifndef RYZENADJ_H 6 | #define RYZENADJ_H 7 | #ifdef __cplusplus 8 | extern "C" { 9 | #endif 10 | 11 | #define RYZENADJ_REVISION_VER 0 12 | #define RYZENADJ_MAJOR_VER 17 13 | #define RYZENADJ_MINIOR_VER 0 14 | 15 | enum ryzen_family { 16 | WAIT_FOR_LOAD = -2, 17 | FAM_UNKNOWN = -1, 18 | FAM_RAVEN = 0, 19 | FAM_PICASSO, 20 | FAM_RENOIR, 21 | FAM_CEZANNE, 22 | FAM_DALI, 23 | FAM_LUCIENNE, 24 | FAM_VANGOGH, 25 | FAM_REMBRANDT, 26 | FAM_MENDOCINO, 27 | FAM_PHOENIX, 28 | FAM_HAWKPOINT, 29 | FAM_DRAGONRANGE, 30 | FAM_KRACKANPOINT, 31 | FAM_STRIXPOINT, 32 | FAM_STRIXHALO, 33 | FAM_FIRERANGE, 34 | FAM_END 35 | }; 36 | 37 | #ifdef _LIBRYZENADJ_INTERNAL 38 | #include "ryzenadj_priv.h" 39 | 40 | #ifdef _WIN32 41 | #define EXP __declspec(dllexport) 42 | #define CALL __stdcall 43 | #else 44 | #define EXP __attribute__((visibility("default"))) 45 | #define CALL 46 | #endif 47 | 48 | #else 49 | 50 | #ifdef _WIN32 51 | #define EXP __declspec(dllimport) 52 | #define CALL __stdcall 53 | #else 54 | #define EXP 55 | #define CALL 56 | #endif 57 | struct _ryzen_access; 58 | 59 | #endif 60 | 61 | #define ADJ_ERR_FAM_UNSUPPORTED -1 62 | #define ADJ_ERR_SMU_TIMEOUT -2 63 | #define ADJ_ERR_SMU_UNSUPPORTED -3 64 | #define ADJ_ERR_SMU_REJECTED -4 65 | #define ADJ_ERR_MEMORY_ACCESS -5 66 | 67 | typedef struct _ryzen_access *ryzen_access; 68 | 69 | EXP ryzen_access CALL init_ryzenadj(); 70 | 71 | EXP void CALL cleanup_ryzenadj(ryzen_access ry); 72 | 73 | EXP enum ryzen_family get_cpu_family(ryzen_access ry); 74 | EXP int get_bios_if_ver(ryzen_access ry); 75 | 76 | EXP int CALL init_table(ryzen_access ry); 77 | EXP uint32_t CALL get_table_ver(ryzen_access ry); 78 | EXP size_t CALL get_table_size(ryzen_access ry); 79 | EXP float* CALL get_table_values(ryzen_access ry); 80 | EXP int CALL refresh_table(ryzen_access ry); 81 | 82 | EXP int CALL set_stapm_limit(ryzen_access, uint32_t value); 83 | EXP int CALL set_fast_limit(ryzen_access, uint32_t value); 84 | EXP int CALL set_slow_limit(ryzen_access, uint32_t value); 85 | EXP int CALL set_slow_time(ryzen_access, uint32_t value); 86 | EXP int CALL set_stapm_time(ryzen_access, uint32_t value); 87 | EXP int CALL set_tctl_temp(ryzen_access, uint32_t value); 88 | EXP int CALL set_vrm_current(ryzen_access, uint32_t value); 89 | EXP int CALL set_vrmsoc_current(ryzen_access, uint32_t value); 90 | EXP int CALL set_vrmgfx_current(ryzen_access, uint32_t value); 91 | EXP int CALL set_vrmcvip_current(ryzen_access, uint32_t value); 92 | EXP int CALL set_vrmmax_current(ryzen_access, uint32_t value); 93 | EXP int CALL set_vrmgfxmax_current(ryzen_access, uint32_t value); 94 | EXP int CALL set_vrmsocmax_current(ryzen_access, uint32_t value); 95 | EXP int CALL set_psi0_current(ryzen_access, uint32_t value); 96 | EXP int CALL set_psi3cpu_current(ryzen_access, uint32_t value); 97 | EXP int CALL set_psi0soc_current(ryzen_access, uint32_t value); 98 | EXP int CALL set_psi3gfx_current(ryzen_access, uint32_t value); 99 | EXP int CALL set_max_gfxclk_freq(ryzen_access, uint32_t value); 100 | EXP int CALL set_min_gfxclk_freq(ryzen_access, uint32_t value); 101 | EXP int CALL set_max_socclk_freq(ryzen_access, uint32_t value); 102 | EXP int CALL set_min_socclk_freq(ryzen_access, uint32_t value); 103 | EXP int CALL set_max_fclk_freq(ryzen_access, uint32_t value); 104 | EXP int CALL set_min_fclk_freq(ryzen_access, uint32_t value); 105 | EXP int CALL set_max_vcn(ryzen_access, uint32_t value); 106 | EXP int CALL set_min_vcn(ryzen_access, uint32_t value); 107 | EXP int CALL set_max_lclk(ryzen_access, uint32_t value); 108 | EXP int CALL set_min_lclk(ryzen_access, uint32_t value); 109 | EXP int CALL set_prochot_deassertion_ramp(ryzen_access ry, uint32_t value); 110 | EXP int CALL set_apu_skin_temp_limit(ryzen_access, uint32_t value); 111 | EXP int CALL set_dgpu_skin_temp_limit(ryzen_access, uint32_t value); 112 | EXP int CALL set_apu_slow_limit(ryzen_access, uint32_t value); 113 | EXP int CALL set_skin_temp_power_limit(ryzen_access ry, uint32_t value); 114 | EXP int CALL set_gfx_clk(ryzen_access ry, uint32_t value); 115 | EXP int CALL set_oc_clk(ryzen_access ry, uint32_t value); 116 | EXP int CALL set_per_core_oc_clk(ryzen_access ry, uint32_t value); 117 | EXP int CALL set_oc_volt(ryzen_access ry, uint32_t value); 118 | EXP int CALL set_disable_oc(ryzen_access ry); 119 | EXP int CALL set_enable_oc(ryzen_access ry); 120 | EXP int CALL set_power_saving(ryzen_access ry); 121 | EXP int CALL set_max_performance(ryzen_access ry); 122 | EXP int CALL set_coall(ryzen_access ry, uint32_t value); 123 | EXP int CALL set_coper(ryzen_access ry, uint32_t value); 124 | EXP int CALL set_cogfx(ryzen_access ry, uint32_t value); 125 | 126 | EXP float CALL get_stapm_limit(ryzen_access ry); 127 | EXP float CALL get_stapm_value(ryzen_access ry); 128 | EXP float CALL get_fast_limit(ryzen_access ry); 129 | EXP float CALL get_fast_value(ryzen_access ry); 130 | EXP float CALL get_slow_limit(ryzen_access ry); 131 | EXP float CALL get_slow_value(ryzen_access ry); 132 | EXP float CALL get_apu_slow_limit(ryzen_access ry); 133 | EXP float CALL get_apu_slow_value(ryzen_access ry); 134 | EXP float CALL get_vrm_current(ryzen_access ry); 135 | EXP float CALL get_vrm_current_value(ryzen_access ry); 136 | EXP float CALL get_vrmsoc_current(ryzen_access ry); 137 | EXP float CALL get_vrmsoc_current_value(ryzen_access ry); 138 | EXP float CALL get_vrmmax_current(ryzen_access ry); 139 | EXP float CALL get_vrmmax_current_value(ryzen_access ry); 140 | EXP float CALL get_vrmsocmax_current(ryzen_access ry); 141 | EXP float CALL get_vrmsocmax_current_value(ryzen_access ry); 142 | EXP float CALL get_tctl_temp(ryzen_access ry); 143 | EXP float CALL get_tctl_temp_value(ryzen_access ry); 144 | EXP float CALL get_apu_skin_temp_limit(ryzen_access ry); 145 | EXP float CALL get_apu_skin_temp_value(ryzen_access ry); 146 | EXP float CALL get_dgpu_skin_temp_limit(ryzen_access ry); 147 | EXP float CALL get_dgpu_skin_temp_value(ryzen_access ry); 148 | EXP float CALL get_psi0_current(ryzen_access ry); 149 | EXP float CALL get_psi0soc_current(ryzen_access ry); 150 | EXP float CALL get_stapm_time(ryzen_access ry); 151 | EXP float CALL get_slow_time(ryzen_access ry); 152 | EXP float CALL get_cclk_setpoint(ryzen_access ry); 153 | EXP float CALL get_cclk_busy_value(ryzen_access ry); 154 | 155 | EXP float CALL get_core_clk(ryzen_access ry, uint32_t value); 156 | EXP float CALL get_core_volt(ryzen_access ry, uint32_t value); 157 | EXP float CALL get_core_power(ryzen_access ry, uint32_t value); 158 | EXP float CALL get_core_temp(ryzen_access ry, uint32_t value); 159 | 160 | EXP float CALL get_l3_clk(ryzen_access ry); 161 | EXP float CALL get_l3_logic(ryzen_access ry); 162 | EXP float CALL get_l3_vddm(ryzen_access ry); 163 | EXP float CALL get_l3_temp(ryzen_access ry); 164 | 165 | EXP float CALL get_gfx_clk(ryzen_access ry); 166 | EXP float CALL get_gfx_temp(ryzen_access ry); 167 | EXP float CALL get_gfx_volt(ryzen_access ry); 168 | 169 | EXP float CALL get_mem_clk(ryzen_access ry); 170 | EXP float CALL get_fclk(ryzen_access ry); 171 | 172 | EXP float CALL get_soc_power(ryzen_access ry); 173 | EXP float CALL get_soc_volt(ryzen_access ry); 174 | 175 | EXP float CALL get_socket_power(ryzen_access ry); 176 | 177 | #ifdef __cplusplus 178 | } 179 | #endif 180 | #endif 181 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | CMakeCache.txt 2 | CMakeFiles 3 | CMakeScripts 4 | Testing 5 | Makefile 6 | cmake_install.cmake 7 | install_manifest.txt 8 | compile_commands.json 9 | CTestTestfile.cmake 10 | build/ 11 | 12 | # User-specific files 13 | *.rsuser 14 | *.suo 15 | *.user 16 | *.userosscache 17 | *.sln.docstates 18 | 19 | # User-specific files (MonoDevelop/Xamarin Studio) 20 | *.userprefs 21 | 22 | # Build results 23 | [Dd]ebug/ 24 | [Dd]ebugPublic/ 25 | [Rr]elease/ 26 | [Rr]eleases/ 27 | x64/ 28 | x86/ 29 | [Aa][Rr][Mm]/ 30 | [Aa][Rr][Mm]64/ 31 | bld/ 32 | [Bb]in/ 33 | [Oo]bj/ 34 | [Ll]og/ 35 | 36 | # Visual Studio 2015/2017 cache/options directory 37 | .vs/ 38 | # Uncomment if you have tasks that create the project's static files in wwwroot 39 | #wwwroot/ 40 | 41 | # Visual Studio 2017 auto generated files 42 | Generated\ Files/ 43 | 44 | # MSTest test Results 45 | [Tt]est[Rr]esult*/ 46 | [Bb]uild[Ll]og.* 47 | 48 | # NUNIT 49 | *.VisualState.xml 50 | TestResult.xml 51 | 52 | # Build Results of an ATL Project 53 | [Dd]ebugPS/ 54 | [Rr]eleasePS/ 55 | dlldata.c 56 | 57 | # Benchmark Results 58 | BenchmarkDotNet.Artifacts/ 59 | 60 | # .NET Core 61 | project.lock.json 62 | project.fragment.lock.json 63 | artifacts/ 64 | 65 | # StyleCop 66 | StyleCopReport.xml 67 | 68 | # Files built by Visual Studio 69 | *_i.c 70 | *_p.c 71 | *_h.h 72 | *.ilk 73 | *.meta 74 | *.obj 75 | *.iobj 76 | *.pch 77 | *.pdb 78 | *.ipdb 79 | *.pgc 80 | *.pgd 81 | *.rsp 82 | *.sbr 83 | *.tlb 84 | *.tli 85 | *.tlh 86 | *.tmp 87 | *.tmp_proj 88 | *_wpftmp.csproj 89 | *.log 90 | *.vspscc 91 | *.vssscc 92 | .builds 93 | *.pidb 94 | *.svclog 95 | *.scc 96 | 97 | # Chutzpah Test files 98 | _Chutzpah* 99 | 100 | # Visual C++ cache files 101 | ipch/ 102 | *.aps 103 | *.ncb 104 | *.opendb 105 | *.opensdf 106 | *.sdf 107 | *.cachefile 108 | *.VC.db 109 | *.VC.VC.opendb 110 | 111 | # Visual Studio profiler 112 | *.psess 113 | *.vsp 114 | *.vspx 115 | *.sap 116 | 117 | # Visual Studio Trace Files 118 | *.e2e 119 | 120 | # TFS 2012 Local Workspace 121 | $tf/ 122 | 123 | # Guidance Automation Toolkit 124 | *.gpState 125 | 126 | # ReSharper is a .NET coding add-in 127 | _ReSharper*/ 128 | *.[Rr]e[Ss]harper 129 | *.DotSettings.user 130 | 131 | # JustCode is a .NET coding add-in 132 | .JustCode 133 | 134 | # TeamCity is a build add-in 135 | _TeamCity* 136 | 137 | # DotCover is a Code Coverage Tool 138 | *.dotCover 139 | 140 | # AxoCover is a Code Coverage Tool 141 | .axoCover/* 142 | !.axoCover/settings.json 143 | 144 | # Visual Studio code coverage results 145 | *.coverage 146 | *.coveragexml 147 | 148 | # NCrunch 149 | _NCrunch_* 150 | .*crunch*.local.xml 151 | nCrunchTemp_* 152 | 153 | # MightyMoose 154 | *.mm.* 155 | AutoTest.Net/ 156 | 157 | # Web workbench (sass) 158 | .sass-cache/ 159 | 160 | # Installshield output folder 161 | [Ee]xpress/ 162 | 163 | # DocProject is a documentation generator add-in 164 | DocProject/buildhelp/ 165 | DocProject/Help/*.HxT 166 | DocProject/Help/*.HxC 167 | DocProject/Help/*.hhc 168 | DocProject/Help/*.hhk 169 | DocProject/Help/*.hhp 170 | DocProject/Help/Html2 171 | DocProject/Help/html 172 | 173 | # Click-Once directory 174 | publish/ 175 | 176 | # Publish Web Output 177 | *.[Pp]ublish.xml 178 | *.azurePubxml 179 | # Note: Comment the next line if you want to checkin your web deploy settings, 180 | # but database connection strings (with potential passwords) will be unencrypted 181 | *.pubxml 182 | *.publishproj 183 | 184 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 185 | # checkin your Azure Web App publish settings, but sensitive information contained 186 | # in these scripts will be unencrypted 187 | PublishScripts/ 188 | 189 | # NuGet Packages 190 | *.nupkg 191 | # The packages folder can be ignored because of Package Restore 192 | **/[Pp]ackages/* 193 | # except build/, which is used as an MSBuild target. 194 | !**/[Pp]ackages/build/ 195 | # Uncomment if necessary however generally it will be regenerated when needed 196 | #!**/[Pp]ackages/repositories.config 197 | # NuGet v3's project.json files produces more ignorable files 198 | *.nuget.props 199 | *.nuget.targets 200 | 201 | # Microsoft Azure Build Output 202 | csx/ 203 | *.build.csdef 204 | 205 | # Microsoft Azure Emulator 206 | ecf/ 207 | rcf/ 208 | 209 | # Windows Store app package directories and files 210 | AppPackages/ 211 | BundleArtifacts/ 212 | Package.StoreAssociation.xml 213 | _pkginfo.txt 214 | *.appx 215 | 216 | # Visual Studio cache files 217 | # files ending in .cache can be ignored 218 | *.[Cc]ache 219 | # but keep track of directories ending in .cache 220 | !?*.[Cc]ache/ 221 | 222 | # Others 223 | ClientBin/ 224 | ~$* 225 | *~ 226 | *.dbmdl 227 | *.dbproj.schemaview 228 | *.jfm 229 | *.pfx 230 | *.publishsettings 231 | orleans.codegen.cs 232 | 233 | # Including strong name files can present a security risk 234 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 235 | #*.snk 236 | 237 | # Since there are multiple workflows, uncomment next line to ignore bower_components 238 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 239 | #bower_components/ 240 | # ASP.NET Core default setup: bower directory is configured as wwwroot/lib/ and bower restore is true 241 | **/wwwroot/lib/ 242 | 243 | # RIA/Silverlight projects 244 | Generated_Code/ 245 | 246 | # Backup & report files from converting an old project file 247 | # to a newer Visual Studio version. Backup files are not needed, 248 | # because we have git ;-) 249 | _UpgradeReport_Files/ 250 | Backup*/ 251 | UpgradeLog*.XML 252 | UpgradeLog*.htm 253 | ServiceFabricBackup/ 254 | *.rptproj.bak 255 | 256 | # SQL Server files 257 | *.mdf 258 | *.ldf 259 | *.ndf 260 | 261 | # Business Intelligence projects 262 | *.rdl.data 263 | *.bim.layout 264 | *.bim_*.settings 265 | *.rptproj.rsuser 266 | *- Backup*.rdl 267 | 268 | # Microsoft Fakes 269 | FakesAssemblies/ 270 | 271 | # GhostDoc plugin setting file 272 | *.GhostDoc.xml 273 | 274 | # Node.js Tools for Visual Studio 275 | .ntvs_analysis.dat 276 | node_modules/ 277 | 278 | # Visual Studio 6 build log 279 | *.plg 280 | 281 | # Visual Studio 6 workspace options file 282 | *.opt 283 | 284 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 285 | *.vbw 286 | 287 | # Visual Studio LightSwitch build output 288 | **/*.HTMLClient/GeneratedArtifacts 289 | **/*.DesktopClient/GeneratedArtifacts 290 | **/*.DesktopClient/ModelManifest.xml 291 | **/*.Server/GeneratedArtifacts 292 | **/*.Server/ModelManifest.xml 293 | _Pvt_Extensions 294 | 295 | # Paket dependency manager 296 | .paket/paket.exe 297 | paket-files/ 298 | 299 | # FAKE - F# Make 300 | .fake/ 301 | 302 | # JetBrains Rider 303 | .idea/ 304 | *.sln.iml 305 | 306 | # CodeRush personal settings 307 | .cr/personal 308 | 309 | # Python Tools for Visual Studio (PTVS) 310 | __pycache__/ 311 | *.pyc 312 | 313 | # Cake - Uncomment if you are using it 314 | # tools/** 315 | # !tools/packages.config 316 | 317 | # Tabs Studio 318 | *.tss 319 | 320 | # Telerik's JustMock configuration file 321 | *.jmconfig 322 | 323 | # BizTalk build output 324 | *.btp.cs 325 | *.btm.cs 326 | *.odx.cs 327 | *.xsd.cs 328 | 329 | # OpenCover UI analysis results 330 | OpenCover/ 331 | 332 | # Azure Stream Analytics local run output 333 | ASALocalRun/ 334 | 335 | # MSBuild Binary and Structured Log 336 | *.binlog 337 | 338 | # NVidia Nsight GPU debugger configuration file 339 | *.nvuser 340 | 341 | # MFractors (Xamarin productivity tool) working folder 342 | .mfractor/ 343 | 344 | # Local History for Visual Studio 345 | .localhistory/ 346 | 347 | # BeatPulse healthcheck temp database 348 | healthchecksdb 349 | 350 | # End of https://www.gitignore.io/api/visualstudio 351 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU LESSER GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | 9 | This version of the GNU Lesser General Public License incorporates 10 | the terms and conditions of version 3 of the GNU General Public 11 | License, supplemented by the additional permissions listed below. 12 | 13 | 0. Additional Definitions. 14 | 15 | As used herein, "this License" refers to version 3 of the GNU Lesser 16 | General Public License, and the "GNU GPL" refers to version 3 of the GNU 17 | General Public License. 18 | 19 | "The Library" refers to a covered work governed by this License, 20 | other than an Application or a Combined Work as defined below. 21 | 22 | An "Application" is any work that makes use of an interface provided 23 | by the Library, but which is not otherwise based on the Library. 24 | Defining a subclass of a class defined by the Library is deemed a mode 25 | of using an interface provided by the Library. 26 | 27 | A "Combined Work" is a work produced by combining or linking an 28 | Application with the Library. The particular version of the Library 29 | with which the Combined Work was made is also called the "Linked 30 | Version". 31 | 32 | The "Minimal Corresponding Source" for a Combined Work means the 33 | Corresponding Source for the Combined Work, excluding any source code 34 | for portions of the Combined Work that, considered in isolation, are 35 | based on the Application, and not on the Linked Version. 36 | 37 | The "Corresponding Application Code" for a Combined Work means the 38 | object code and/or source code for the Application, including any data 39 | and utility programs needed for reproducing the Combined Work from the 40 | Application, but excluding the System Libraries of the Combined Work. 41 | 42 | 1. Exception to Section 3 of the GNU GPL. 43 | 44 | You may convey a covered work under sections 3 and 4 of this License 45 | without being bound by section 3 of the GNU GPL. 46 | 47 | 2. Conveying Modified Versions. 48 | 49 | If you modify a copy of the Library, and, in your modifications, a 50 | facility refers to a function or data to be supplied by an Application 51 | that uses the facility (other than as an argument passed when the 52 | facility is invoked), then you may convey a copy of the modified 53 | version: 54 | 55 | a) under this License, provided that you make a good faith effort to 56 | ensure that, in the event an Application does not supply the 57 | function or data, the facility still operates, and performs 58 | whatever part of its purpose remains meaningful, or 59 | 60 | b) under the GNU GPL, with none of the additional permissions of 61 | this License applicable to that copy. 62 | 63 | 3. Object Code Incorporating Material from Library Header Files. 64 | 65 | The object code form of an Application may incorporate material from 66 | a header file that is part of the Library. You may convey such object 67 | code under terms of your choice, provided that, if the incorporated 68 | material is not limited to numerical parameters, data structure 69 | layouts and accessors, or small macros, inline functions and templates 70 | (ten or fewer lines in length), you do both of the following: 71 | 72 | a) Give prominent notice with each copy of the object code that the 73 | Library is used in it and that the Library and its use are 74 | covered by this License. 75 | 76 | b) Accompany the object code with a copy of the GNU GPL and this license 77 | document. 78 | 79 | 4. Combined Works. 80 | 81 | You may convey a Combined Work under terms of your choice that, 82 | taken together, effectively do not restrict modification of the 83 | portions of the Library contained in the Combined Work and reverse 84 | engineering for debugging such modifications, if you also do each of 85 | the following: 86 | 87 | a) Give prominent notice with each copy of the Combined Work that 88 | the Library is used in it and that the Library and its use are 89 | covered by this License. 90 | 91 | b) Accompany the Combined Work with a copy of the GNU GPL and this license 92 | document. 93 | 94 | c) For a Combined Work that displays copyright notices during 95 | execution, include the copyright notice for the Library among 96 | these notices, as well as a reference directing the user to the 97 | copies of the GNU GPL and this license document. 98 | 99 | d) Do one of the following: 100 | 101 | 0) Convey the Minimal Corresponding Source under the terms of this 102 | License, and the Corresponding Application Code in a form 103 | suitable for, and under terms that permit, the user to 104 | recombine or relink the Application with a modified version of 105 | the Linked Version to produce a modified Combined Work, in the 106 | manner specified by section 6 of the GNU GPL for conveying 107 | Corresponding Source. 108 | 109 | 1) Use a suitable shared library mechanism for linking with the 110 | Library. A suitable mechanism is one that (a) uses at run time 111 | a copy of the Library already present on the user's computer 112 | system, and (b) will operate properly with a modified version 113 | of the Library that is interface-compatible with the Linked 114 | Version. 115 | 116 | e) Provide Installation Information, but only if you would otherwise 117 | be required to provide such information under section 6 of the 118 | GNU GPL, and only to the extent that such information is 119 | necessary to install and execute a modified version of the 120 | Combined Work produced by recombining or relinking the 121 | Application with a modified version of the Linked Version. (If 122 | you use option 4d0, the Installation Information must accompany 123 | the Minimal Corresponding Source and Corresponding Application 124 | Code. If you use option 4d1, you must provide the Installation 125 | Information in the manner specified by section 6 of the GNU GPL 126 | for conveying Corresponding Source.) 127 | 128 | 5. Combined Libraries. 129 | 130 | You may place library facilities that are a work based on the 131 | Library side by side in a single library together with other library 132 | facilities that are not Applications and are not covered by this 133 | License, and convey such a combined library under terms of your 134 | choice, if you do both of the following: 135 | 136 | a) Accompany the combined library with a copy of the same work based 137 | on the Library, uncombined with any other library facilities, 138 | conveyed under the terms of this License. 139 | 140 | b) Give prominent notice with the combined library that part of it 141 | is a work based on the Library, and explaining where to find the 142 | accompanying uncombined form of the same work. 143 | 144 | 6. Revised Versions of the GNU Lesser General Public License. 145 | 146 | The Free Software Foundation may publish revised and/or new versions 147 | of the GNU Lesser General Public License from time to time. Such new 148 | versions will be similar in spirit to the present version, but may 149 | differ in detail to address new problems or concerns. 150 | 151 | Each version is given a distinguishing version number. If the 152 | Library as you received it specifies that a certain numbered version 153 | of the GNU Lesser General Public License "or any later version" 154 | applies to it, you have the option of following the terms and 155 | conditions either of that published version or of any later version 156 | published by the Free Software Foundation. If the Library as you 157 | received it does not specify a version number of the GNU Lesser 158 | General Public License, you may choose any version of the GNU Lesser 159 | General Public License ever published by the Free Software Foundation. 160 | 161 | If the Library as you received it specifies that a proxy can decide 162 | whether future versions of the GNU Lesser General Public License shall 163 | apply, that proxy's public statement of acceptance of any version is 164 | permanent authorization for you to choose that version for the 165 | Library. 166 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # RyzenAdj 2 | Adjust power management settings for Ryzen Mobile Processors. 3 | 4 | [![Build Status](https://travis-ci.org/FlyGoat/RyzenAdj.svg?branch=master)](https://travis-ci.org/FlyGoat/RyzenAdj) 5 | 6 | Based on: [FlyGoat/ryzen_nb_smu](https://github.com/flygoat/ryzen_nb_smu) 7 | 8 | RyzenAdjUI_WPF by "JustSkill" is no longer maintained, for GUI please see [Universal x86 Tuning Utility](https://github.com/JamesCJ60/Universal-x86-Tuning-Utility) or [ryzen-controller-team/ryzen-controller](https://gitlab.com/ryzen-controller-team/ryzen-controller/). 9 | 10 | ## Usage 11 | The command line interface is identical on both Windows and Unix-Like OS. 12 | 13 | You should run it with Administrator on Windows or root on Linux. 14 | 15 | You can write a shell script or bat to do it automatically. 16 | 17 | ``` 18 | $./ryzenadj -h 19 | Usage: ryzenadj [options] 20 | 21 | Ryzen Power Management adjust tool. 22 | 23 | -h, --help show this help message and exit 24 | 25 | Options 26 | -i, --info Show information and most important power metrics after adjustment 27 | --dump-table Show whole power metric table before and after adjustment 28 | 29 | Settings 30 | -a, --stapm-limit= Sustained Power Limit - STAPM LIMIT (mW) 31 | -b, --fast-limit= Actual Power Limit - PPT LIMIT FAST (mW) 32 | -c, --slow-limit= Average Power Limit - PPT LIMIT SLOW (mW) 33 | -d, --slow-time= Slow PPT Constant Time (s) 34 | -e, --stapm-time= STAPM constant time (s) 35 | -f, --tctl-temp= Tctl Temperature Limit (degree C) 36 | -g, --vrm-current= VRM Current Limit - TDC LIMIT VDD (mA) 37 | -j, --vrmsoc-current= VRM SoC Current Limit - TDC LIMIT SoC (mA) 38 | -k, --vrmmax-current= VRM Maximum Current Limit - EDC LIMIT VDD (mA) 39 | -l, --vrmsocmax-current= VRM SoC Maximum Current Limit - EDC LIMIT SoC (mA) 40 | -m, --psi0-current= PSI0 VDD Current Limit (mA) 41 | -n, --psi0soc-current= PSI0 SoC Current Limit (mA) 42 | -o, --max-socclk-frequency= Maximum SoC Clock Frequency (MHz) 43 | -p, --min-socclk-frequency= Minimum SoC Clock Frequency (MHz) 44 | -q, --max-fclk-frequency= Maximum Transmission (CPU-GPU) Frequency (MHz) 45 | -r, --min-fclk-frequency= Minimum Transmission (CPU-GPU) Frequency (MHz) 46 | -s, --max-vcn= Maximum Video Core Next (VCE - Video Coding Engine) (MHz) 47 | -t, --min-vcn= Minimum Video Core Next (VCE - Video Coding Engine) (MHz) 48 | -u, --max-lclk= Maximum Data Launch Clock (MHz) 49 | -v, --min-lclk= Minimum Data Launch Clock (MHz) 50 | -w, --max-gfxclk= Maximum GFX Clock (MHz) 51 | -x, --min-gfxclk= Minimum GFX Clock (MHz) 52 | -y, --prochot-deassertion-ramp= Ramp Time After Prochot is Deasserted: limit power based on value, higher values does apply tighter limits after prochot is over 53 | --apu-skin-temp= APU Skin Temperature Limit - STT LIMIT APU (degree C) 54 | --dgpu-skin-temp= dGPU Skin Temperature Limit - STT LIMIT dGPU (degree C) 55 | --apu-slow-limit= APU PPT Slow Power limit for A+A dGPU platform - PPT LIMIT APU (mW) 56 | --skin-temp-limit= Skin Temperature Power Limit (mW) 57 | --power-saving Hidden options to improve power efficiency (is set when AC unplugged): behavior depends on CPU generation, Device and Manufacture 58 | --max-performance Hidden options to improve performance (is set when AC plugged in): behavior depends on CPU generation, Device and Manufacture 59 | ``` 60 | 61 | ### Demo 62 | If I'm going to set all the Power Limit to 45W, and Tctl to 90 °C, 63 | then the command line should be: 64 | 65 | ./ryzenadj --stapm-limit=45000 --fast-limit=45000 --slow-limit=45000 --tctl-temp=90 66 | 67 | ### Documentation 68 | - [Supported Models](https://github.com/FlyGoat/RyzenAdj/wiki/Supported-Models) 69 | - [Renoir Tuning Guide](https://github.com/FlyGoat/RyzenAdj/wiki/Renoir-Tuning-Guide) 70 | - [Options](https://github.com/FlyGoat/RyzenAdj/wiki/Options) 71 | - [FAQ](https://github.com/FlyGoat/RyzenAdj/wiki/FAQ) 72 | 73 | ## Installation 74 | 75 | You don't need to install RyzenAdj because it does not need configuration, everything is set via arguments 76 | However, some settings could get overwritten by power management features of your device, and you need to regularly set your values again. 77 | 78 | We did provide some examples for automation. And these require configuration during installation. 79 | 80 | ### Linux Installation 81 | 82 | Because it is very easy to build the latest version of RyzenAdj on Linux, we don't provide precompiled packages for distributions. 83 | Just follow the build instructions below and you are ready to use it. 84 | 85 | ### Windows Installation 86 | 87 | Before you start installing anything, it is highly recommended getting familiar with RyzenAdj to find out what can be done on your device. 88 | Use the CLI `ryzenadj.exe` to test the support of your device and to benchmark the effects of each setting. 89 | If your values don't stay persistent you may want to consider installing our example script for automation. 90 | 91 | 1. Prepare your favorite RyzenAdj arguments 92 | 1. Copy the content of your RyzenAdj folder to the final destination 93 | 1. Put your configuration into `readjustService.ps1` and test it as administrator until everything works as expected 94 | 1. Install `readjustService.ps1` as Task for Windows Task Scheduler by running `installServiceTask.bat` 95 | 96 | Deinstallation of the Task can be done via `uninstallServiceTask.bat` 97 | 98 | Over Windows Task Scheduler you can check if it is running. It is called `RyzenAdj` below `AMD` folder. 99 | Or just run 100 | 101 | SCHTASKS /query /TN "AMD\RyzenAdj" 102 | 103 | ## Build 104 | 105 | ### Build Requirements 106 | 107 | Building this tool requires C & C++ compilers as well as **cmake**. 108 | 109 | ### Linux 110 | 111 | RyzenAdj needs elevated access to the NB config space. This can be achieved by using either one of 112 | these two methods: 113 | 114 | * Using libpci and exposing `/dev/mem` 115 | * Using the ryzen\_smu kernel module 116 | 117 | RyzenAdj will try ryzen\_smu first, and then fallback to /dev/mem, if no compatible smu driver is found. 118 | The minimum supported version of ryzen_smu is 0.1.7 119 | If no backend is available, RyzenAdj will fail initialization. 120 | 121 | _**Please note that `/dev/mem` access may be restricted, for security reasons, in your kernel config**_ 122 | 123 | Please make sure that you have libpci dependency before compiling. 124 | 125 | On Debian-based distros this is covered by installing **pcilib-dev** package: 126 | 127 | sudo apt install build-essential cmake libpci-dev 128 | 129 | On Fedora: 130 | 131 | sudo dnf install cmake gcc-c++ pciutils-devel 132 | 133 | On Arch: 134 | 135 | sudo pacman -S base-devel pciutils cmake 136 | 137 | 138 | On OpenSUSE Tumbleweed: 139 | 140 | sudo zypper in cmake gcc14-c++ pciutils-devel 141 | 142 | You may need to add the `iomem=relaxed` param to your kernel params on Tumbleweed, or [you may run into errors at runtime](https://github.com/FlyGoat/RyzenAdj/issues/241). 143 | 144 | If your Distribution is not supported, try finding the packages or use [Distrobox](https://github.com/89luca89/distrobox) or [Toolbox](https://docs.fedoraproject.org/en-US/fedora-silverblue/toolbox/) instead. 145 | 146 | The simplest way to build it: 147 | 148 | git clone https://github.com/FlyGoat/RyzenAdj.git 149 | cd RyzenAdj 150 | rm -r win32 151 | mkdir build && cd build 152 | cmake -DCMAKE_BUILD_TYPE=Release .. 153 | make 154 | if [ -d ~/.local/bin ]; then ln -s $(readlink -f ryzenadj) ~/.local/bin/ryzenadj && echo "symlinked to ~/.local/bin/ryzenadj"; fi 155 | if [ -d ~/.bin ]; then ln -s $(readlink -f ryzenadj) ~/.bin/ryzenadj && echo "symlinked to ~/.bin/ryzenadj"; fi 156 | 157 | #### Ryzen\_smu 158 | 159 | To let RyzenAdj use ryzen\_smu module, you have to install it first, it is not part of the linux kernel. 160 | 161 | On Fedora: 162 | 163 | ```sh 164 | sudo dnf install cmake gcc gcc-c++ dkms openssl 165 | ``` 166 | 167 | Clone and install ryzen\_smu: 168 | 169 | ```sh 170 | git clone https://github.com/amkillam/ryzen_smu # Active fork of the original module 171 | (cd ryzen_smu/ && sudo make dkms-install) 172 | ``` 173 | 174 | If you are using secure boot, you have to enroll the UEFI keys which dkms has generated on its first 175 | run. These have to be added to your machines UEFI key database. This can be done with following 176 | command, which will ask you to set a password. This password is only needed _one single time_ later 177 | in the MOK manager. 178 | 179 | ```sh 180 | sudo mokutil --import /var/lib/dkms/mok.pub 181 | ``` 182 | 183 | Restart your system. This will boot into the MOK manager. Choose `Enroll MOK`, enter your password 184 | and then reboot. 185 | [Here](https://github.com/dell/dkms/blob/f7f526c145ecc01fb4ac4eab3009b1879b14ced4/README.md#secure-boot) 186 | are some screenshots describing the process. 187 | 188 | The module is now loaded and visible via dmesg. It will show a message about the kernel being 189 | tainted, but this just means it loaded a (potentially proprietary) binary blob. 190 | 191 | Build and install RyzenAdj: 192 | 193 | ```sh 194 | git clone https://github.com/FlyGoat/RyzenAdj 195 | cd RyzenAdj 196 | cmake -B build -DCMAKE_BUILD_TYPE=Release 197 | make -C build -j"$(nproc)" 198 | sudo cp -v build/ryzenadj /usr/local/bin/ 199 | ``` 200 | 201 | ### Windows 202 | 203 | It can be built by Visual Studio + MSVC automaticaly, or Clang + Nmake in command line. 204 | However, as for now, MingW-gcc can't be used to compile for some reason. 205 | 206 | Required dll is included in ./win32 of source tree. Please put the dll 207 | library and sys driver in the same folder with ryzenadj.exe. 208 | 209 | We don't recommend you to build by yourself on Windows since the environment configuarion 210 | is very complicated. If you would like to use ryzenadj functions in your program, see libryzenadj. 211 | -------------------------------------------------------------------------------- /argparse.c: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2012-2015 Yecheng Fu 3 | * All rights reserved. 4 | * 5 | * Use of this source code is governed by a MIT-style license that can be found 6 | * in the LICENSE file. 7 | */ 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include "argparse.h" 14 | 15 | #define OPT_UNSET 1 16 | #define OPT_LONG (1 << 1) 17 | 18 | static const char * 19 | prefix_skip(const char *str, const char *prefix) 20 | { 21 | size_t len = strlen(prefix); 22 | return strncmp(str, prefix, len) ? NULL : str + len; 23 | } 24 | 25 | static int 26 | prefix_cmp(const char *str, const char *prefix) 27 | { 28 | for (;; str++, prefix++) 29 | if (!*prefix) { 30 | return 0; 31 | } else if (*str != *prefix) { 32 | return (unsigned char)*prefix - (unsigned char)*str; 33 | } 34 | } 35 | 36 | static void 37 | argparse_error(struct argparse *self, const struct argparse_option *opt, 38 | const char *reason, int flags) 39 | { 40 | (void)self; 41 | if (flags & OPT_LONG) { 42 | fprintf(stderr, "error: option `--%s` %s\n", opt->long_name, reason); 43 | } else { 44 | fprintf(stderr, "error: option `-%c` %s\n", opt->short_name, reason); 45 | } 46 | exit(1); 47 | } 48 | 49 | static int 50 | argparse_getvalue(struct argparse *self, const struct argparse_option *opt, 51 | int flags) 52 | { 53 | const char *s = NULL; 54 | char buf[256]; 55 | buf[0] = 0; 56 | char *pbuf = buf; 57 | 58 | if (!opt->value) 59 | goto skipped; 60 | switch (opt->type) { 61 | case ARGPARSE_OPT_BOOLEAN: 62 | if (flags & OPT_UNSET) { 63 | *(int *)opt->value = *(int *)opt->value - 1; 64 | } else { 65 | *(int *)opt->value = *(int *)opt->value + 1; 66 | } 67 | if (*(int *)opt->value < 0) { 68 | *(int *)opt->value = 0; 69 | } 70 | break; 71 | case ARGPARSE_OPT_BIT: 72 | if (flags & OPT_UNSET) { 73 | *(int *)opt->value &= ~opt->data; 74 | } else { 75 | *(int *)opt->value |= opt->data; 76 | } 77 | break; 78 | case ARGPARSE_OPT_STRING: 79 | if (self->optvalue) { 80 | *(const char **)opt->value = self->optvalue; 81 | self->optvalue = NULL; 82 | } else if (self->argc > 1) { 83 | self->argc--; 84 | *(const char **)opt->value = *++self->argv; 85 | } else { 86 | argparse_error(self, opt, "requires a value", flags); 87 | } 88 | break; 89 | case ARGPARSE_OPT_INTEGER: 90 | errno = 0; 91 | if (self->optvalue) { 92 | *(int *)opt->value = strtol(self->optvalue, (char **)&s, 0); 93 | self->optvalue = NULL; 94 | } else if (self->argc > 1) { 95 | self->argc--; 96 | *(int *)opt->value = strtol(*++self->argv, (char **)&s, 0); 97 | } else { 98 | argparse_error(self, opt, "requires a value", flags); 99 | } 100 | if (errno){ 101 | #ifdef _WIN32 102 | strerror_s(buf, sizeof(buf), errno); 103 | #else 104 | strerror_r(errno, buf, sizeof(buf)); 105 | #endif 106 | argparse_error(self, opt, pbuf, flags); 107 | } 108 | if (s[0] != '\0') 109 | argparse_error(self, opt, "expects an integer value", flags); 110 | break; 111 | case ARGPARSE_OPT_U32: 112 | errno = 0; 113 | if (self->optvalue) { 114 | *(uint32_t *)opt->value = strtoul(self->optvalue, (char **)&s, 0); 115 | self->optvalue = NULL; 116 | } else if (self->argc > 1) { 117 | self->argc--; 118 | *(uint32_t *)opt->value = strtoul(*++self->argv, (char **)&s, 0); 119 | } else { 120 | argparse_error(self, opt, "requires a value", flags); 121 | } 122 | if (errno){ 123 | #ifdef _WIN32 124 | strerror_s(buf, sizeof(buf), errno); 125 | #else 126 | strerror_r(errno, buf, sizeof(buf)); 127 | #endif 128 | argparse_error(self, opt, pbuf, flags); 129 | } 130 | if (s[0] != '\0') 131 | argparse_error(self, opt, "expects an unsigned 32-bit integer value", flags); 132 | break; 133 | case ARGPARSE_OPT_FLOAT: 134 | errno = 0; 135 | if (self->optvalue) { 136 | *(float *)opt->value = strtof(self->optvalue, (char **)&s); 137 | self->optvalue = NULL; 138 | } else if (self->argc > 1) { 139 | self->argc--; 140 | *(float *)opt->value = strtof(*++self->argv, (char **)&s); 141 | } else { 142 | argparse_error(self, opt, "requires a value", flags); 143 | } 144 | if (errno){ 145 | #ifdef _WIN32 146 | strerror_s(buf, sizeof(buf), errno); 147 | #else 148 | strerror_r(errno, buf, sizeof(buf)); 149 | #endif 150 | argparse_error(self, opt, pbuf, flags); 151 | } 152 | if (s[0] != '\0') 153 | argparse_error(self, opt, "expects a numerical value", flags); 154 | break; 155 | default: 156 | assert(0); 157 | } 158 | 159 | skipped: 160 | if (opt->callback) { 161 | return opt->callback(self, opt); 162 | } 163 | 164 | return 0; 165 | } 166 | 167 | static void 168 | argparse_options_check(const struct argparse_option *options) 169 | { 170 | for (; options->type != ARGPARSE_OPT_END; options++) { 171 | switch (options->type) { 172 | case ARGPARSE_OPT_END: 173 | case ARGPARSE_OPT_BOOLEAN: 174 | case ARGPARSE_OPT_BIT: 175 | case ARGPARSE_OPT_INTEGER: 176 | case ARGPARSE_OPT_U32: 177 | case ARGPARSE_OPT_FLOAT: 178 | case ARGPARSE_OPT_STRING: 179 | case ARGPARSE_OPT_GROUP: 180 | continue; 181 | default: 182 | fprintf(stderr, "wrong option type: %d", options->type); 183 | break; 184 | } 185 | } 186 | } 187 | 188 | static int 189 | argparse_short_opt(struct argparse *self, const struct argparse_option *options) 190 | { 191 | for (; options->type != ARGPARSE_OPT_END; options++) { 192 | if (options->short_name == *self->optvalue) { 193 | self->optvalue = self->optvalue[1] ? self->optvalue + 1 : NULL; 194 | return argparse_getvalue(self, options, 0); 195 | } 196 | } 197 | return -2; 198 | } 199 | 200 | static int 201 | argparse_long_opt(struct argparse *self, const struct argparse_option *options) 202 | { 203 | for (; options->type != ARGPARSE_OPT_END; options++) { 204 | const char *rest; 205 | int opt_flags = 0; 206 | if (!options->long_name) 207 | continue; 208 | 209 | rest = prefix_skip(self->argv[0] + 2, options->long_name); 210 | if (!rest) { 211 | // negation disabled? 212 | if (options->flags & OPT_NONEG) { 213 | continue; 214 | } 215 | // only OPT_BOOLEAN/OPT_BIT supports negation 216 | if (options->type != ARGPARSE_OPT_BOOLEAN && options->type != 217 | ARGPARSE_OPT_BIT) { 218 | continue; 219 | } 220 | 221 | if (prefix_cmp(self->argv[0] + 2, "no-")) { 222 | continue; 223 | } 224 | rest = prefix_skip(self->argv[0] + 2 + 3, options->long_name); 225 | if (!rest) 226 | continue; 227 | opt_flags |= OPT_UNSET; 228 | } 229 | if (*rest) { 230 | if (*rest != '=') 231 | continue; 232 | self->optvalue = rest + 1; 233 | } 234 | return argparse_getvalue(self, options, opt_flags | OPT_LONG); 235 | } 236 | return -2; 237 | } 238 | 239 | int 240 | argparse_init(struct argparse *self, struct argparse_option *options, 241 | const char *const *usages, int flags) 242 | { 243 | memset(self, 0, sizeof(*self)); 244 | self->options = options; 245 | self->usages = usages; 246 | self->flags = flags; 247 | self->description = NULL; 248 | self->epilog = NULL; 249 | return 0; 250 | } 251 | 252 | void 253 | argparse_describe(struct argparse *self, const char *description, 254 | const char *epilog) 255 | { 256 | self->description = description; 257 | self->epilog = epilog; 258 | } 259 | 260 | int 261 | argparse_parse(struct argparse *self, int argc, const char **argv) 262 | { 263 | self->argc = argc - 1; 264 | self->argv = argv + 1; 265 | self->out = argv; 266 | 267 | argparse_options_check(self->options); 268 | if(!self->argc) { 269 | argparse_usage(self); 270 | exit(1); 271 | } 272 | 273 | for (; self->argc; self->argc--, self->argv++) { 274 | const char *arg = self->argv[0]; 275 | if (arg[0] != '-' || !arg[1]) { 276 | if (self->flags & ARGPARSE_STOP_AT_NON_OPTION) { 277 | goto end; 278 | } 279 | if (self->flags & ARGPARSE_NON_OPTION_IS_INVALID) { 280 | goto unknown; 281 | } 282 | // if it's not option or is a single char '-', copy verbatim 283 | self->out[self->cpidx++] = self->argv[0]; 284 | continue; 285 | } 286 | // short option 287 | if (arg[1] != '-') { 288 | self->optvalue = arg + 1; 289 | switch (argparse_short_opt(self, self->options)) { 290 | case -1: 291 | break; 292 | case -2: 293 | goto unknown; 294 | } 295 | while (self->optvalue) { 296 | switch (argparse_short_opt(self, self->options)) { 297 | case -1: 298 | break; 299 | case -2: 300 | goto unknown; 301 | } 302 | } 303 | continue; 304 | } 305 | // if '--' presents 306 | if (!arg[2]) { 307 | self->argc--; 308 | self->argv++; 309 | break; 310 | } 311 | // long option 312 | switch (argparse_long_opt(self, self->options)) { 313 | case -1: 314 | break; 315 | case -2: 316 | goto unknown; 317 | } 318 | continue; 319 | 320 | unknown: 321 | fprintf(stderr, "error: unknown option `%s`\n", self->argv[0]); 322 | argparse_usage(self); 323 | exit(1); 324 | } 325 | 326 | end: 327 | memmove(self->out + self->cpidx, self->argv, 328 | self->argc * sizeof(*self->out)); 329 | self->out[self->cpidx + self->argc] = NULL; 330 | 331 | return self->cpidx + self->argc; 332 | } 333 | 334 | void 335 | argparse_usage(struct argparse *self) 336 | { 337 | if (self->usages) { 338 | fprintf(stdout, "Usage: %s\n", *self->usages++); 339 | while (*self->usages && **self->usages) 340 | fprintf(stdout, " or: %s\n", *self->usages++); 341 | } else { 342 | fprintf(stdout, "Usage:\n"); 343 | } 344 | 345 | // print description 346 | if (self->description) 347 | fprintf(stdout, "%s\n", self->description); 348 | 349 | fputc('\n', stdout); 350 | 351 | const struct argparse_option *options; 352 | 353 | // figure out best width 354 | size_t usage_opts_width = 0; 355 | size_t len; 356 | options = self->options; 357 | for (; options->type != ARGPARSE_OPT_END; options++) { 358 | len = 0; 359 | if ((options)->short_name) { 360 | len += 2; 361 | } 362 | if ((options)->short_name && (options)->long_name) { 363 | len += 2; // separator ", " 364 | } 365 | if ((options)->long_name) { 366 | len += strlen((options)->long_name) + 2; 367 | } 368 | if (options->type == ARGPARSE_OPT_INTEGER) { 369 | len += strlen("="); 370 | } 371 | if (options->type == ARGPARSE_OPT_U32) { 372 | len += strlen("="); 373 | } 374 | if (options->type == ARGPARSE_OPT_FLOAT) { 375 | len += strlen("="); 376 | } else if (options->type == ARGPARSE_OPT_STRING) { 377 | len += strlen("="); 378 | } 379 | len = (len + 3) - ((len + 3) & 3); 380 | if (usage_opts_width < len) { 381 | usage_opts_width = len; 382 | } 383 | } 384 | usage_opts_width += 4; // 4 spaces prefix 385 | 386 | options = self->options; 387 | for (; options->type != ARGPARSE_OPT_END; options++) { 388 | size_t pos = 0; 389 | size_t pad = 0; 390 | if (options->type == ARGPARSE_OPT_GROUP) { 391 | fputc('\n', stdout); 392 | fprintf(stdout, "%s", options->help); 393 | fputc('\n', stdout); 394 | continue; 395 | } 396 | pos = fprintf(stdout, " "); 397 | if (options->short_name) { 398 | pos += fprintf(stdout, "-%c", options->short_name); 399 | } 400 | if (options->long_name && options->short_name) { 401 | pos += fprintf(stdout, ", "); 402 | } 403 | if (options->long_name) { 404 | pos += fprintf(stdout, "--%s", options->long_name); 405 | } 406 | if (options->type == ARGPARSE_OPT_INTEGER) { 407 | pos += fprintf(stdout, "="); 408 | } else if (options->type == ARGPARSE_OPT_U32) { 409 | pos += fprintf(stdout, "="); 410 | } else if (options->type == ARGPARSE_OPT_FLOAT) { 411 | pos += fprintf(stdout, "="); 412 | } else if (options->type == ARGPARSE_OPT_STRING) { 413 | pos += fprintf(stdout, "="); 414 | } 415 | if (pos <= usage_opts_width) { 416 | pad = usage_opts_width - pos; 417 | } else { 418 | fputc('\n', stdout); 419 | pad = usage_opts_width; 420 | } 421 | fprintf(stdout, "%*s%s\n", (int)pad + 2, "", options->help); 422 | } 423 | 424 | // print epilog 425 | if (self->epilog) 426 | fprintf(stdout, "%s\n", self->epilog); 427 | } 428 | 429 | int 430 | argparse_help_cb(struct argparse *self, const struct argparse_option *option) 431 | { 432 | (void)option; 433 | argparse_usage(self); 434 | exit(0); 435 | } 436 | -------------------------------------------------------------------------------- /main.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0 2 | /* Copyright (C) 2018-2019 Jiaxun Yang */ 3 | /* Ryzen NB SMU Service Request Tool */ 4 | 5 | #include 6 | #include 7 | 8 | #ifdef _WIN32 9 | #include 10 | #else 11 | #include 12 | #endif 13 | 14 | #include "lib/ryzenadj.h" 15 | #include "argparse.h" 16 | 17 | #define STRINGIFY2(X) #X 18 | #define STRINGIFY(X) STRINGIFY2(X) 19 | 20 | #define _do_adjust(ARG) \ 21 | do { \ 22 | /* ignore max unsigned integer values */ \ 23 | if (ARG != -1) { \ 24 | int adjerr = set_##ARG(ry, ARG); \ 25 | if (!adjerr){ \ 26 | any_adjust_applied = 1; \ 27 | printf("Sucessfully set " STRINGIFY(ARG) " to %u\n", ARG); \ 28 | } else if (adjerr == ADJ_ERR_FAM_UNSUPPORTED) { \ 29 | printf("set_" STRINGIFY(ARG) " is not supported on this family\n"); \ 30 | err = -1; \ 31 | } else if (adjerr == ADJ_ERR_SMU_UNSUPPORTED) { \ 32 | printf("set_" STRINGIFY(ARG) " is not supported on this SMU\n"); \ 33 | err = -1; \ 34 | } else if (adjerr == ADJ_ERR_SMU_REJECTED) { \ 35 | printf("set_" STRINGIFY(ARG) " is rejected by SMU\n"); \ 36 | err = -1; \ 37 | } else { \ 38 | printf("Failed to set" STRINGIFY(ARG) " \n"); \ 39 | err = -1; \ 40 | } \ 41 | } \ 42 | } while(0); 43 | 44 | #define _do_enable(ARG) \ 45 | do { \ 46 | if (ARG) { \ 47 | int adjerr = set_##ARG(ry); \ 48 | if (!adjerr){ \ 49 | any_adjust_applied = 1; \ 50 | printf("Sucessfully enable " STRINGIFY(ARG) "\n"); \ 51 | } else if (adjerr == ADJ_ERR_FAM_UNSUPPORTED) { \ 52 | printf("set_" STRINGIFY(ARG) " is not supported on this family\n"); \ 53 | err = -1; \ 54 | } else if (adjerr == ADJ_ERR_SMU_UNSUPPORTED) { \ 55 | printf("set_" STRINGIFY(ARG) " is not supported on this SMU\n"); \ 56 | err = -1; \ 57 | } else if (adjerr == ADJ_ERR_SMU_REJECTED) { \ 58 | printf("set_" STRINGIFY(ARG) " is rejected by SMU\n"); \ 59 | err = -1; \ 60 | } else { \ 61 | printf("Failed to set" STRINGIFY(ARG) " \n"); \ 62 | err = -1; \ 63 | } \ 64 | } \ 65 | } while(0); 66 | 67 | static const char *const usage[] = { 68 | "ryzenadj [options]", 69 | NULL, 70 | }; 71 | 72 | static const char *family_name(enum ryzen_family fam) 73 | { 74 | switch (fam) 75 | { 76 | case FAM_RAVEN: return "Raven"; 77 | case FAM_PICASSO: return "Picasso"; 78 | case FAM_RENOIR: return "Renoir"; 79 | case FAM_CEZANNE: return "Cezanne"; 80 | case FAM_DALI: return "Dali"; 81 | case FAM_LUCIENNE: return "Lucienne"; 82 | case FAM_VANGOGH: return "Vangogh"; 83 | case FAM_REMBRANDT: return "Rembrandt"; 84 | case FAM_PHOENIX: return "Phoenix Point"; 85 | case FAM_HAWKPOINT: return "Hawk Point"; 86 | case FAM_KRACKANPOINT: return "Krackan Point"; 87 | case FAM_STRIXPOINT: return "Strix Point"; 88 | case FAM_STRIXHALO: return "Strix Halo"; 89 | default: 90 | break; 91 | } 92 | 93 | return "Unknown"; 94 | } 95 | 96 | static void show_info_header(ryzen_access ry) 97 | { 98 | printf("CPU Family: %s\n", family_name(get_cpu_family(ry))); 99 | printf("SMU BIOS Interface Version: %d\n", get_bios_if_ver(ry)); 100 | printf("Version: v" STRINGIFY(RYZENADJ_REVISION_VER) "." STRINGIFY(RYZENADJ_MAJOR_VER) "." STRINGIFY(RYZENADJ_MINIOR_VER) " \n"); 101 | } 102 | 103 | static void show_info_table(ryzen_access ry) 104 | { 105 | printf("PM Table Version: %x\n", get_table_ver(ry)); 106 | 107 | //get refresh table after adjust 108 | int errorcode = refresh_table(ry); 109 | if(errorcode){ 110 | printf("Unable to refresh power metric table: %d\n", errorcode); 111 | return; 112 | } 113 | 114 | //print table in github markdown 115 | printf("| Name | Value | Parameter |\n"); 116 | printf("|---------------------|-----------|--------------------|\n"); 117 | char tableFormat[] = "| %-19s | %9.3lf | %-18s |\n"; 118 | printf(tableFormat, "STAPM LIMIT", get_stapm_limit(ry), "stapm-limit"); 119 | printf(tableFormat, "STAPM VALUE", get_stapm_value(ry), ""); 120 | printf(tableFormat, "PPT LIMIT FAST", get_fast_limit(ry), "fast-limit"); 121 | printf(tableFormat, "PPT VALUE FAST", get_fast_value(ry), ""); 122 | printf(tableFormat, "PPT LIMIT SLOW", get_slow_limit(ry), "slow-limit"); 123 | printf(tableFormat, "PPT VALUE SLOW", get_slow_value(ry), ""); 124 | printf(tableFormat, "StapmTimeConst", get_stapm_time(ry), "stapm-time"); 125 | printf(tableFormat, "SlowPPTTimeConst", get_slow_time(ry), "slow-time"); 126 | printf(tableFormat, "PPT LIMIT APU", get_apu_slow_limit(ry), "apu-slow-limit"); 127 | printf(tableFormat, "PPT VALUE APU", get_apu_slow_value(ry), ""); 128 | printf(tableFormat, "TDC LIMIT VDD", get_vrm_current(ry), "vrm-current"); 129 | printf(tableFormat, "TDC VALUE VDD", get_vrm_current_value(ry), ""); 130 | printf(tableFormat, "TDC LIMIT SOC", get_vrmsoc_current(ry), "vrmsoc-current"); 131 | printf(tableFormat, "TDC VALUE SOC", get_vrmsoc_current_value(ry), ""); 132 | printf(tableFormat, "EDC LIMIT VDD", get_vrmmax_current(ry), "vrmmax-current"); 133 | printf(tableFormat, "EDC VALUE VDD", get_vrmmax_current_value(ry), ""); 134 | printf(tableFormat, "EDC LIMIT SOC", get_vrmsocmax_current(ry), "vrmsocmax-current"); 135 | printf(tableFormat, "EDC VALUE SOC", get_vrmsocmax_current_value(ry), ""); 136 | printf(tableFormat, "THM LIMIT CORE", get_tctl_temp(ry), "tctl-temp"); 137 | printf(tableFormat, "THM VALUE CORE", get_tctl_temp_value(ry), ""); 138 | printf(tableFormat, "STT LIMIT APU", get_apu_skin_temp_limit(ry), "apu-skin-temp"); 139 | printf(tableFormat, "STT VALUE APU", get_apu_skin_temp_value(ry), ""); 140 | printf(tableFormat, "STT LIMIT dGPU", get_dgpu_skin_temp_limit(ry), "dgpu-skin-temp"); 141 | printf(tableFormat, "STT VALUE dGPU", get_dgpu_skin_temp_value(ry), ""); 142 | printf(tableFormat, "CCLK Boost SETPOINT", get_cclk_setpoint(ry), "power-saving /"); 143 | printf(tableFormat, "CCLK BUSY VALUE", get_cclk_busy_value(ry), "max-performance"); 144 | } 145 | 146 | static void show_table_dump(ryzen_access ry, int any_adjust_applied) 147 | { 148 | size_t index, table_size; 149 | uint32_t *table_data_copy; 150 | float *current_table_values, *old_table_values; 151 | 152 | printf("PM Table Dump of Version: %x\n", get_table_ver(ry)); 153 | table_size = get_table_size(ry); 154 | 155 | current_table_values = get_table_values(ry); 156 | table_data_copy = malloc(table_size); 157 | memcpy(table_data_copy, current_table_values, table_size); 158 | 159 | if(any_adjust_applied) 160 | { 161 | //copy old values before refresh 162 | old_table_values = malloc(table_size); 163 | memcpy(old_table_values, table_data_copy, table_size); 164 | 165 | int errorcode = refresh_table(ry); 166 | if(errorcode){ 167 | printf("Unable to refresh power metric table: %d\n", errorcode); 168 | } 169 | 170 | //print table in github markdown 171 | printf("| Offset | Data | Value | After Adjust |\n"); 172 | printf("|--------|------------|-----------|--------------|\n"); 173 | char tableFormat[] = "| 0x%04X | 0x%08X | %9.3lf | %12.3lf |\n"; 174 | for(index = 0; index < table_size / 4; index++) 175 | { 176 | printf(tableFormat, index * 4, table_data_copy[index], old_table_values[index], current_table_values[index]); 177 | } 178 | 179 | free(old_table_values); 180 | } 181 | else 182 | { 183 | //print table in github markdown 184 | printf("| Offset | Data | Value |\n"); 185 | printf("|--------|------------|-----------|\n"); 186 | char tableFormat[] = "| 0x%04X | 0x%08X | %9.3lf |\n"; 187 | for(index = 0; index < table_size / 4; index++) 188 | { 189 | printf(tableFormat, index * 4, table_data_copy[index], current_table_values[index]); 190 | } 191 | } 192 | 193 | free(table_data_copy); 194 | //don't free current_table_values because this would deinitialize our table 195 | } 196 | 197 | 198 | int main(int argc, const char **argv) 199 | { 200 | ryzen_access ry; 201 | int err = 0; 202 | 203 | int info = 0, dump_table = 0, any_adjust_applied = 0; 204 | uint32_t persist_interval = 0; 205 | int power_saving = 0, max_performance = 0, enable_oc = 0x0, disable_oc = 0x0; 206 | //init unsigned types with max value because we treat max value as unset 207 | uint32_t stapm_limit = -1, fast_limit = -1, slow_limit = -1, slow_time = -1, stapm_time = -1, tctl_temp = -1; 208 | uint32_t vrm_current = -1, vrmsoc_current = -1, vrmmax_current = -1, vrmsocmax_current = -1, psi0_current = -1, psi0soc_current = -1; 209 | uint32_t vrmgfx_current = -1, vrmcvip_current = -1, vrmgfxmax_current = -1, psi3cpu_current = -1, psi3gfx_current = -1; 210 | uint32_t max_socclk_freq = -1, min_socclk_freq = -1, max_fclk_freq = -1, min_fclk_freq = -1, max_vcn = -1, min_vcn = -1, max_lclk = -1, min_lclk = -1; 211 | uint32_t max_gfxclk_freq = -1, min_gfxclk_freq = -1, prochot_deassertion_ramp = -1, apu_skin_temp_limit = -1, dgpu_skin_temp_limit = -1, apu_slow_limit = -1; 212 | uint32_t skin_temp_power_limit = -1; 213 | uint32_t gfx_clk = -1, oc_clk = -1, oc_volt = -1, coall = -1, coper = -1, cogfx = -1; 214 | 215 | //create structure for parseing 216 | struct argparse_option options[] = { 217 | OPT_HELP(), 218 | OPT_GROUP("Options"), 219 | OPT_BOOLEAN('i', "info", &info, "Show information and most important power metrics after adjustment"), 220 | OPT_BOOLEAN('\0', "dump-table", &dump_table, "Show whole power metric table before and after adjustment"), 221 | OPT_U32('\0', "persist-interval", &persist_interval, "Re-apply settings every x milliseconds"), 222 | OPT_GROUP("Settings"), 223 | OPT_U32('a', "stapm-limit", &stapm_limit, "Sustained Power Limit - STAPM LIMIT (mW)"), 224 | OPT_U32('b', "fast-limit", &fast_limit, "Actual Power Limit - PPT LIMIT FAST (mW)"), 225 | OPT_U32('c', "slow-limit", &slow_limit, "Average Power Limit - PPT LIMIT SLOW (mW)"), 226 | OPT_U32('d', "slow-time", &slow_time, "Slow PPT Constant Time (s)"), 227 | OPT_U32('e', "stapm-time", &stapm_time, "STAPM constant time (s)"), 228 | OPT_U32('f', "tctl-temp", &tctl_temp, "Tctl Temperature Limit (degree C)"), 229 | OPT_U32('g', "vrm-current", &vrm_current, "VRM Current Limit - TDC LIMIT VDD (mA)"), 230 | OPT_U32('j', "vrmsoc-current", &vrmsoc_current, "VRM SoC Current Limit - TDC LIMIT SoC (mA)"), 231 | OPT_U32('\0', "vrmgfx-current", &vrmgfx_current, "VRM GFX Current Limit - TDC LIMIT GFX (mA)"), 232 | OPT_U32('\0', "vrmcvip-current", &vrmcvip_current, "VRM CVIP Current Limit - TDC LIMIT CVIP (mA)"), 233 | OPT_U32('k', "vrmmax-current", &vrmmax_current, "VRM Maximum Current Limit - EDC LIMIT VDD (mA)"), 234 | OPT_U32('l', "vrmsocmax-current", &vrmsocmax_current, "VRM SoC Maximum Current Limit - EDC LIMIT SoC (mA)"), 235 | OPT_U32('\0', "vrmgfxmax_current", &vrmgfxmax_current, "VRM GFX Maximum Current Limit - EDC LIMIT GFX (mA)"), 236 | OPT_U32('m', "psi0-current", &psi0_current, "PSI0 VDD Current Limit (mA)"), 237 | OPT_U32('\0', "psi3cpu_current", &psi3cpu_current, "PSI3 CPU Current Limit (mA)"), 238 | OPT_U32('n', "psi0soc-current", &psi0soc_current, "PSI0 SoC Current Limit (mA)"), 239 | OPT_U32('\0', "psi3gfx_current", &psi3gfx_current, "PSI3 GFX Current Limit (mA)"), 240 | OPT_U32('o', "max-socclk-frequency", &max_socclk_freq, "Maximum SoC Clock Frequency (MHz)"), 241 | OPT_U32('p', "min-socclk-frequency", &min_socclk_freq, "Minimum SoC Clock Frequency (MHz)"), 242 | OPT_U32('q', "max-fclk-frequency", &max_fclk_freq, "Maximum Transmission (CPU-GPU) Frequency (MHz)"), 243 | OPT_U32('r', "min-fclk-frequency", &min_fclk_freq, "Minimum Transmission (CPU-GPU) Frequency (MHz)"), 244 | OPT_U32('s', "max-vcn", &max_vcn, "Maximum Video Core Next (VCE - Video Coding Engine) (MHz)"), 245 | OPT_U32('t', "min-vcn", &min_vcn, "Minimum Video Core Next (VCE - Video Coding Engine) (MHz)"), 246 | OPT_U32('u', "max-lclk", &max_lclk, "Maximum Data Launch Clock (MHz)"), 247 | OPT_U32('v', "min-lclk", &min_lclk, "Minimum Data Launch Clock (MHz)"), 248 | OPT_U32('w', "max-gfxclk", &max_gfxclk_freq, "Maximum GFX Clock (MHz)"), 249 | OPT_U32('x', "min-gfxclk", &min_gfxclk_freq, "Minimum GFX Clock (MHz)"), 250 | OPT_U32('y', "prochot-deassertion-ramp", &prochot_deassertion_ramp, "Ramp Time After Prochot is Deasserted: limit power based on value, higher values does apply tighter limits after prochot is over"), 251 | OPT_U32('\0', "apu-skin-temp", &apu_skin_temp_limit, "APU Skin Temperature Limit - STT LIMIT APU (degree C)"), 252 | OPT_U32('\0', "dgpu-skin-temp", &dgpu_skin_temp_limit, "dGPU Skin Temperature Limit - STT LIMIT dGPU (degree C)"), 253 | OPT_U32('\0', "apu-slow-limit", &apu_slow_limit, "APU PPT Slow Power limit for A+A dGPU platform - PPT LIMIT APU (mW)"), 254 | OPT_U32('\0', "skin-temp-limit", &skin_temp_power_limit, "Skin Temperature Power Limit (mW)"), 255 | OPT_U32('\0', "gfx-clk", &gfx_clk, "Forced Clock Speed MHz (Renoir Only)"), 256 | OPT_U32('\0', "oc-clk", &oc_clk, "Forced Core Clock Speed MHz (Renoir and up Only)"), 257 | OPT_U32('\0', "oc-volt", &oc_volt, "Forced Core VID: Must follow this calcuation (1.55 - [VID you want to set e.g. 1.25 for 1.25v]) / 0.00625 (Renoir and up Only)"), 258 | OPT_BOOLEAN('\0', "enable-oc", &enable_oc, "Enable OC (Renoir and up Only)"), 259 | OPT_BOOLEAN('\0', "disable-oc", &disable_oc, "Disable OC (Renoir and up Only)"), 260 | OPT_U32('\0', "set-coall", &coall, "All core Curve Optimiser"), 261 | OPT_U32('\0', "set-coper", &coper, "Per core Curve Optimiser"), 262 | OPT_U32('\0', "set-cogfx", &cogfx, "iGPU Curve Optimiser"), 263 | OPT_BOOLEAN('\0', "power-saving", &power_saving, "Hidden options to improve power efficiency (is set when AC unplugged): behavior depends on CPU generation, Device and Manufacture"), 264 | OPT_BOOLEAN('\0', "max-performance", &max_performance, "Hidden options to improve performance (is set when AC plugged in): behavior depends on CPU generation, Device and Manufacture"), 265 | OPT_GROUP("P-State Functions"), 266 | OPT_END(), 267 | }; 268 | 269 | 270 | struct argparse argparse; 271 | argparse_init(&argparse, options, usage, ARGPARSE_NON_OPTION_IS_INVALID); 272 | argparse_describe(&argparse, "\n Ryzen Power Management adjust tool.", "\nWARNING: Use at your own risk!\nBy Jiaxun Yang , Under LGPL.\nVersion: v" STRINGIFY(RYZENADJ_REVISION_VER) "." STRINGIFY(RYZENADJ_MAJOR_VER) "." STRINGIFY(RYZENADJ_MINIOR_VER)); 273 | argc = argparse_parse(&argparse, argc, argv); 274 | 275 | 276 | //init RyzenAdj and validate that it was able to 277 | ry = init_ryzenadj(); 278 | if(!ry){ 279 | printf("Unable to init ryzenadj\n"); 280 | return -1; 281 | } 282 | 283 | //shows info header before init_table 284 | if (info) { 285 | show_info_header(ry); 286 | } 287 | 288 | if (info || dump_table) { 289 | //init before adjustment to get the default values 290 | err = init_table(ry); 291 | if (err) { 292 | printf("Unable to init power metric table: %d, this does not affect adjustments because it is only needed for monitoring.\n", err); 293 | } 294 | } 295 | 296 | #ifndef _WIN32 297 | persist_interval *= 1000; 298 | #endif 299 | 300 | //adjust all the arguments sent to RyzenAdj.exe 301 | adjust_begin: 302 | _do_adjust(stapm_limit); 303 | _do_adjust(fast_limit); 304 | _do_adjust(slow_limit); 305 | _do_adjust(slow_time); 306 | _do_adjust(stapm_time); 307 | _do_adjust(tctl_temp); 308 | _do_adjust(vrm_current); 309 | _do_adjust(vrmsoc_current); 310 | _do_adjust(vrmgfx_current); 311 | _do_adjust(vrmcvip_current); 312 | _do_adjust(vrmmax_current); 313 | _do_adjust(vrmsocmax_current); 314 | _do_adjust(vrmgfxmax_current); 315 | _do_adjust(psi0_current); 316 | _do_adjust(psi3cpu_current); 317 | _do_adjust(psi0soc_current); 318 | _do_adjust(psi3gfx_current); 319 | _do_adjust(max_socclk_freq); 320 | _do_adjust(min_socclk_freq); 321 | _do_adjust(max_fclk_freq); 322 | _do_adjust(min_fclk_freq); 323 | _do_adjust(max_vcn); 324 | _do_adjust(min_vcn); 325 | _do_adjust(max_lclk); 326 | _do_adjust(min_lclk); 327 | _do_adjust(max_gfxclk_freq); 328 | _do_adjust(min_gfxclk_freq); 329 | _do_adjust(prochot_deassertion_ramp); 330 | _do_adjust(apu_skin_temp_limit); 331 | _do_adjust(dgpu_skin_temp_limit); 332 | _do_adjust(apu_slow_limit); 333 | _do_adjust(skin_temp_power_limit); 334 | _do_adjust(gfx_clk); 335 | _do_adjust(oc_clk); 336 | _do_adjust(oc_volt); 337 | _do_enable(power_saving); 338 | _do_enable(max_performance); 339 | _do_enable(enable_oc) 340 | _do_enable(disable_oc); 341 | _do_adjust(coall); 342 | _do_adjust(coper); 343 | _do_adjust(cogfx); 344 | 345 | if (persist_interval) { 346 | #ifdef _WIN32 347 | Sleep(persist_interval); 348 | #else 349 | usleep(persist_interval); 350 | #endif 351 | goto adjust_begin; 352 | } 353 | 354 | if (!err) { 355 | //call show table dump before anybody did call table refresh, because we want to copy the old values first 356 | if (dump_table) { 357 | show_table_dump(ry, any_adjust_applied); 358 | } 359 | //show power table after apply settings 360 | if (info) { 361 | show_info_table(ry); 362 | } 363 | } 364 | 365 | cleanup_ryzenadj(ry); 366 | 367 | return err; 368 | } 369 | -------------------------------------------------------------------------------- /lib/win32/OlsApi.h: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------------------- 2 | // Author : hiyohiyo 3 | // Mail : hiyohiyo@crystalmark.info 4 | // Web : http://openlibsys.org/ 5 | // License : The modified BSD license 6 | // 7 | // Copyright 2007-2009 OpenLibSys.org. All rights reserved. 8 | //----------------------------------------------------------------------------- 9 | // for WinRing0 1.3.x 10 | 11 | #pragma once 12 | 13 | /****************************************************************************** 14 | ** 15 | ** DLL Information 16 | ** 17 | ******************************************************************************/ 18 | 19 | //----------------------------------------------------------------------------- 20 | // GetDllStatus 21 | //----------------------------------------------------------------------------- 22 | DWORD // DLL Status, defined OLS_DLL_**** 23 | WINAPI GetDllStatus(); 24 | 25 | //----------------------------------------------------------------------------- 26 | // GetDllVersion 27 | //----------------------------------------------------------------------------- 28 | DWORD // DLL Version, defined OLS_VERSION 29 | WINAPI GetDllVersion( 30 | PBYTE major, // major version 31 | PBYTE minor, // minor version 32 | PBYTE revision, // revision 33 | PBYTE release // release/build 34 | ); 35 | 36 | //----------------------------------------------------------------------------- 37 | // GetDriverVersion 38 | //----------------------------------------------------------------------------- 39 | DWORD // Device Driver Version, defined OLS_DRIVER_VERSION 40 | WINAPI GetDriverVersion( 41 | PBYTE major, // major version 42 | PBYTE minor, // minor version 43 | PBYTE revision, // revision 44 | PBYTE release // release/build 45 | ); 46 | 47 | //----------------------------------------------------------------------------- 48 | // GetDriverType 49 | //----------------------------------------------------------------------------- 50 | DWORD // Device Driver Type, defined OLS_DRIVER_TYPE_**** 51 | WINAPI GetDriverType(); 52 | 53 | //----------------------------------------------------------------------------- 54 | // InitializeOls 55 | //----------------------------------------------------------------------------- 56 | BOOL // TRUE: success, FALSE: failure 57 | WINAPI InitializeOls(); 58 | 59 | //----------------------------------------------------------------------------- 60 | // DeinitializeOls 61 | //----------------------------------------------------------------------------- 62 | VOID WINAPI DeinitializeOls(); 63 | 64 | /****************************************************************************** 65 | ** 66 | ** CPU 67 | ** 68 | ******************************************************************************/ 69 | 70 | //----------------------------------------------------------------------------- 71 | // IsCpuid 72 | //----------------------------------------------------------------------------- 73 | BOOL // TRUE: support CPUID instruction, FALSE: not support CPUID instruction 74 | WINAPI IsCpuid(); 75 | 76 | //----------------------------------------------------------------------------- 77 | // IsMsr 78 | //----------------------------------------------------------------------------- 79 | BOOL // TRUE: support MSR(Model-Specific Register), FALSE: not support MSR 80 | WINAPI IsMsr(); 81 | 82 | //----------------------------------------------------------------------------- 83 | // IsTsc 84 | //----------------------------------------------------------------------------- 85 | BOOL // TRUE: support TSC(Time Stamp Counter), FALSE: not support TSC 86 | WINAPI IsTsc(); 87 | 88 | //----------------------------------------------------------------------------- 89 | // Rdmsr 90 | //----------------------------------------------------------------------------- 91 | BOOL // TRUE: success, FALSE: failure 92 | WINAPI Rdmsr( 93 | DWORD index, // MSR index 94 | PDWORD eax, // bit 0-31 95 | PDWORD edx // bit 32-63 96 | ); 97 | 98 | //----------------------------------------------------------------------------- 99 | // RdmsrTx 100 | //----------------------------------------------------------------------------- 101 | BOOL // TRUE: success, FALSE: failure 102 | WINAPI RdmsrTx( 103 | DWORD index, // MSR index 104 | PDWORD eax, // bit 0-31 105 | PDWORD edx, // bit 32-63 106 | DWORD_PTR threadAffinityMask 107 | ); 108 | 109 | //----------------------------------------------------------------------------- 110 | // RdmsrPx 111 | //----------------------------------------------------------------------------- 112 | BOOL // TRUE: success, FALSE: failure 113 | WINAPI RdmsrPx( 114 | DWORD index, // MSR index 115 | PDWORD eax, // bit 0-31 116 | PDWORD edx, // bit 32-63 117 | DWORD_PTR processAffinityMask 118 | ); 119 | 120 | //----------------------------------------------------------------------------- 121 | // Wrmsr 122 | //----------------------------------------------------------------------------- 123 | BOOL // TRUE: success, FALSE: failure 124 | WINAPI Wrmsr( 125 | DWORD index, // MSR index 126 | DWORD eax, // bit 0-31 127 | DWORD edx // bit 32-63 128 | ); 129 | 130 | //----------------------------------------------------------------------------- 131 | // WrmsrTx 132 | //----------------------------------------------------------------------------- 133 | BOOL // TRUE: success, FALSE: failure 134 | WINAPI WrmsrTx( 135 | DWORD index, // MSR index 136 | DWORD eax, // bit 0-31 137 | DWORD edx, // bit 32-63 138 | DWORD_PTR threadAffinityMask 139 | ); 140 | 141 | //----------------------------------------------------------------------------- 142 | // WrmsrPx 143 | //----------------------------------------------------------------------------- 144 | BOOL // TRUE: success, FALSE: failure 145 | WINAPI WrmsrPx( 146 | DWORD index, // MSR index 147 | DWORD eax, // bit 0-31 148 | DWORD edx, // bit 32-63 149 | DWORD_PTR processAffinityMask 150 | ); 151 | 152 | //----------------------------------------------------------------------------- 153 | // Rdpmc 154 | //----------------------------------------------------------------------------- 155 | BOOL // TRUE: success, FALSE: failure 156 | WINAPI Rdpmc( 157 | DWORD index, // PMC index 158 | PDWORD eax, // bit 0-31 159 | PDWORD edx // bit 32-63 160 | ); 161 | 162 | //----------------------------------------------------------------------------- 163 | // RdmsrTx 164 | //----------------------------------------------------------------------------- 165 | BOOL // TRUE: success, FALSE: failure 166 | WINAPI RdpmcTx( 167 | DWORD index, // PMC index 168 | PDWORD eax, // bit 0-31 169 | PDWORD edx, // bit 32-63 170 | DWORD_PTR threadAffinityMask 171 | ); 172 | 173 | //----------------------------------------------------------------------------- 174 | // RdmsrPx 175 | //----------------------------------------------------------------------------- 176 | BOOL // TRUE: success, FALSE: failure 177 | WINAPI RdpmcPx( 178 | DWORD index, // PMC index 179 | PDWORD eax, // bit 0-31 180 | PDWORD edx, // bit 32-63 181 | DWORD_PTR processAffinityMask 182 | ); 183 | 184 | //----------------------------------------------------------------------------- 185 | // Cpuid 186 | //----------------------------------------------------------------------------- 187 | BOOL // TRUE: success, FALSE: failure 188 | WINAPI Cpuid( 189 | DWORD index, // CPUID index 190 | PDWORD eax, 191 | PDWORD ebx, 192 | PDWORD ecx, 193 | PDWORD edx 194 | ); 195 | 196 | //----------------------------------------------------------------------------- 197 | // CpuidTx 198 | //----------------------------------------------------------------------------- 199 | BOOL // TRUE: success, FALSE: failure 200 | WINAPI CpuidTx( 201 | DWORD index, // CPUID index 202 | PDWORD eax, 203 | PDWORD ebx, 204 | PDWORD ecx, 205 | PDWORD edx, 206 | DWORD_PTR threadAffinityMask 207 | ); 208 | 209 | //----------------------------------------------------------------------------- 210 | // CpuidPx 211 | //----------------------------------------------------------------------------- 212 | BOOL // TRUE: success, FALSE: failure 213 | WINAPI CpuidPx( 214 | DWORD index, // CPUID index 215 | PDWORD eax, 216 | PDWORD ebx, 217 | PDWORD ecx, 218 | PDWORD edx, 219 | DWORD_PTR processAffinityMask 220 | ); 221 | 222 | //----------------------------------------------------------------------------- 223 | // Rdtsc 224 | //----------------------------------------------------------------------------- 225 | BOOL // TRUE: success, FALSE: failure 226 | WINAPI Rdtsc( 227 | PDWORD eax, // bit 0-31 228 | PDWORD edx // bit 32-63 229 | ); 230 | 231 | //----------------------------------------------------------------------------- 232 | // RdmsrTx 233 | //----------------------------------------------------------------------------- 234 | BOOL // TRUE: success, FALSE: failure 235 | WINAPI RdtscTx( 236 | PDWORD eax, // bit 0-31 237 | PDWORD edx, // bit 32-63 238 | DWORD_PTR threadAffinityMask 239 | ); 240 | 241 | //----------------------------------------------------------------------------- 242 | // RdmsrPx 243 | //----------------------------------------------------------------------------- 244 | BOOL // TRUE: success, FALSE: failure 245 | WINAPI RdtscPx( 246 | PDWORD eax, // bit 0-31 247 | PDWORD edx, // bit 32-63 248 | DWORD_PTR processAffinityMask 249 | ); 250 | 251 | //----------------------------------------------------------------------------- 252 | // Hlt 253 | //----------------------------------------------------------------------------- 254 | BOOL // TRUE: success, FALSE: failure 255 | WINAPI Hlt(); 256 | 257 | //----------------------------------------------------------------------------- 258 | // HltTx 259 | //----------------------------------------------------------------------------- 260 | BOOL // TRUE: success, FALSE: failure 261 | WINAPI HltTx( 262 | DWORD_PTR threadAffinityMask 263 | ); 264 | 265 | //----------------------------------------------------------------------------- 266 | // HltPx 267 | //----------------------------------------------------------------------------- 268 | BOOL // TRUE: success, FALSE: failure 269 | WINAPI HltTx( 270 | DWORD_PTR processAffinityMask 271 | ); 272 | 273 | /****************************************************************************** 274 | ** 275 | ** I/O 276 | ** 277 | ******************************************************************************/ 278 | 279 | //----------------------------------------------------------------------------- 280 | // ReadIoPortByte 281 | //----------------------------------------------------------------------------- 282 | BYTE // Read Value 283 | WINAPI ReadIoPortByte( 284 | WORD port // I/O port address 285 | ); 286 | 287 | //----------------------------------------------------------------------------- 288 | // ReadIoPortWord 289 | //----------------------------------------------------------------------------- 290 | WORD // Read Value 291 | WINAPI ReadIoPortWord( 292 | WORD port // I/O port address 293 | ); 294 | 295 | //----------------------------------------------------------------------------- 296 | // ReadIoPortDword 297 | //----------------------------------------------------------------------------- 298 | DWORD // Read Value 299 | WINAPI ReadIoPortDword( 300 | WORD port // I/O port address 301 | ); 302 | 303 | //----------------------------------------------------------------------------- 304 | // ReadIoPortByteEx 305 | //----------------------------------------------------------------------------- 306 | BOOL // TRUE: success, FALSE: failure 307 | WINAPI ReadIoPortByteEx( 308 | WORD port, // I/O port address 309 | PBYTE value // Read Value 310 | ); 311 | //----------------------------------------------------------------------------- 312 | // ReadIoPortWordEx 313 | //----------------------------------------------------------------------------- 314 | BOOL // TRUE: success, FALSE: failure 315 | WINAPI ReadIoPortWordEx( 316 | WORD port, // I/O port address 317 | PWORD value // Read Value 318 | ); 319 | //----------------------------------------------------------------------------- 320 | // ReadIoPortDwordEx 321 | //----------------------------------------------------------------------------- 322 | BOOL // TRUE: success, FALSE: failure 323 | WINAPI ReadIoPortDwordEx( 324 | WORD port, // I/O port address 325 | PDWORD value // Read Value 326 | ); 327 | 328 | //----------------------------------------------------------------------------- 329 | // WriteIoPortByte 330 | //----------------------------------------------------------------------------- 331 | VOID 332 | WINAPI WriteIoPortByte( 333 | WORD port, // I/O port address 334 | BYTE value // Write Value 335 | ); 336 | 337 | //----------------------------------------------------------------------------- 338 | // WriteIoPortDword 339 | //----------------------------------------------------------------------------- 340 | VOID 341 | WINAPI WriteIoPortDword( 342 | WORD port, // I/O port address 343 | DWORD value // Write Value 344 | ); 345 | 346 | 347 | //----------------------------------------------------------------------------- 348 | // WriteIoPortWord 349 | //----------------------------------------------------------------------------- 350 | VOID 351 | WINAPI WriteIoPortWord( 352 | WORD port, // I/O port address 353 | WORD value // Write Value 354 | ); 355 | 356 | //----------------------------------------------------------------------------- 357 | // WriteIoPortByteEx 358 | //----------------------------------------------------------------------------- 359 | BOOL // TRUE: success, FALSE: failure 360 | WINAPI WriteIoPortByteEx( 361 | WORD port, // I/O port address 362 | BYTE value // Write Value 363 | ); 364 | 365 | //----------------------------------------------------------------------------- 366 | // WriteIoPortWordEx 367 | //----------------------------------------------------------------------------- 368 | BOOL // TRUE: success, FALSE: failure 369 | WINAPI WriteIoPortWordEx( 370 | WORD port, // I/O port address 371 | WORD value // Write Value 372 | ); 373 | 374 | 375 | //----------------------------------------------------------------------------- 376 | // WriteIoPortDwordEx 377 | //----------------------------------------------------------------------------- 378 | BOOL // TRUE: success, FALSE: failure 379 | WINAPI WriteIoPortDwordEx( 380 | WORD port, // I/O port address 381 | DWORD value // Write Value 382 | ); 383 | 384 | /****************************************************************************** 385 | ** 386 | ** PCI 387 | ** 388 | ******************************************************************************/ 389 | // pciAddress 390 | // 0- 2: Function Number 391 | // 3- 7: Device Number 392 | // 8-15: PCI Bus Number 393 | // 16-31: Reserved 394 | // 0xFFFFFFFF : Error 395 | 396 | //----------------------------------------------------------------------------- 397 | // SetPciMaxBusNo 398 | //----------------------------------------------------------------------------- 399 | VOID 400 | WINAPI SetPciMaxBusIndex( 401 | BYTE max // Max PCI Bus to Scan 402 | ); 403 | 404 | //----------------------------------------------------------------------------- 405 | // ReadPciConfigByte 406 | //----------------------------------------------------------------------------- 407 | BYTE // Read Value 408 | WINAPI ReadPciConfigByte( 409 | DWORD pciAddress, // PCI Device Address 410 | BYTE regAddress // Configuration Address 0-255 411 | ); 412 | 413 | //----------------------------------------------------------------------------- 414 | // ReadPciConfigWord 415 | //----------------------------------------------------------------------------- 416 | WORD // Read Value 417 | WINAPI ReadPciConfigWord( 418 | DWORD pciAddress, // PCI Device Address 419 | BYTE regAddress // Configuration Address 0-255 420 | ); 421 | 422 | //----------------------------------------------------------------------------- 423 | // ReadPciConfigDword 424 | //----------------------------------------------------------------------------- 425 | DWORD // Read Value 426 | WINAPI ReadPciConfigDword( 427 | DWORD pciAddress, // PCI Device Address 428 | BYTE regAddress // Configuration Address 0-255 429 | ); 430 | 431 | //----------------------------------------------------------------------------- 432 | // ReadPciConfigByteEx 433 | //----------------------------------------------------------------------------- 434 | BOOL // TRUE: success, FALSE: failure 435 | WINAPI ReadPciConfigByteEx( 436 | DWORD pciAddress, // PCI Device Address 437 | DWORD regAddress, // Configuration Address 0-whatever 438 | PBYTE value // Read Value 439 | ); 440 | 441 | //----------------------------------------------------------------------------- 442 | // ReadPciConfigWordEx 443 | //----------------------------------------------------------------------------- 444 | BOOL // TRUE: success, FALSE: failure 445 | WINAPI ReadPciConfigWordEx( 446 | DWORD pciAddress, // PCI Device Address 447 | DWORD regAddress, // Configuration Address 0-whatever 448 | PWORD value // Read Value 449 | ); 450 | 451 | //----------------------------------------------------------------------------- 452 | // ReadPciConfigDwordEx 453 | //----------------------------------------------------------------------------- 454 | BOOL // TRUE: success, FALSE: failure 455 | WINAPI ReadPciConfigDwordEx( 456 | DWORD pciAddress, // PCI Device Address 457 | DWORD regAddress, // Configuration Address 0-whatever 458 | PDWORD value // Read Value 459 | ); 460 | 461 | //----------------------------------------------------------------------------- 462 | // WritePciConfigByte 463 | //----------------------------------------------------------------------------- 464 | VOID 465 | WINAPI WritePciConfigByte( 466 | DWORD pciAddress, // PCI Device Address 467 | BYTE regAddress, // Configuration Address 0-255 468 | BYTE value // Write Value 469 | ); 470 | 471 | //----------------------------------------------------------------------------- 472 | // WritePciConfigWord 473 | //----------------------------------------------------------------------------- 474 | VOID 475 | WINAPI WritePciConfigWord( 476 | DWORD pciAddress, // PCI Device Address 477 | BYTE regAddress, // Configuration Address 0-255 478 | WORD value // Write Value 479 | ); 480 | 481 | //----------------------------------------------------------------------------- 482 | // WritePciConfigDword 483 | //----------------------------------------------------------------------------- 484 | VOID 485 | WINAPI WritePciConfigDword( 486 | DWORD pciAddress, // PCI Device Address 487 | BYTE regAddress, // Configuration Address 0-255 488 | DWORD value // Write Value 489 | ); 490 | 491 | //----------------------------------------------------------------------------- 492 | // WritePciConfigByteEx 493 | //----------------------------------------------------------------------------- 494 | BOOL // TRUE: success, FALSE: failure 495 | WINAPI WritePciConfigByteEx( 496 | DWORD pciAddress, // PCI Device Address 497 | DWORD regAddress, // Configuration Address 0-whatever 498 | BYTE value // Write Value 499 | ); 500 | 501 | //----------------------------------------------------------------------------- 502 | // WritePciConfigWordEx 503 | //----------------------------------------------------------------------------- 504 | BOOL // TRUE: success, FALSE: failure 505 | WINAPI WritePciConfigWordEx( 506 | DWORD pciAddress, // PCI Device Address 507 | DWORD regAddress, // Configuration Address 0-whatever 508 | WORD value // Write Value 509 | ); 510 | 511 | //----------------------------------------------------------------------------- 512 | // WritePciConfigDwordEx 513 | //----------------------------------------------------------------------------- 514 | BOOL // TRUE: success, FALSE: failure 515 | WINAPI WritePciConfigDwordEx( 516 | DWORD pciAddress, // PCI Device Address 517 | DWORD regAddress, // Configuration Address 0-whatever 518 | DWORD value // Write Value 519 | ); 520 | 521 | //----------------------------------------------------------------------------- 522 | // FindPciDeviceById 523 | //----------------------------------------------------------------------------- 524 | DWORD // pciAddress, 0xFFFFFFFF: failure 525 | WINAPI FindPciDeviceById( 526 | WORD vendorId, // Vendor ID 527 | WORD deviceId, // Device ID 528 | BYTE index // Index 529 | ); 530 | 531 | //----------------------------------------------------------------------------- 532 | // FindPciDeviceByClass 533 | //----------------------------------------------------------------------------- 534 | DWORD // pciAddress, 0xFFFFFFFF: failure 535 | WINAPI FindPciDeviceByClass( 536 | BYTE baseClass, // Base Class 537 | BYTE subClass, // Sub Class 538 | BYTE programIf, // Program Interface 539 | BYTE index // Index 540 | ); 541 | 542 | /****************************************************************************** 543 | ** 544 | ** Memory (Special API) 545 | ** 546 | ******************************************************************************/ 547 | 548 | #ifdef _PHYSICAL_MEMORY_SUPPORT 549 | //----------------------------------------------------------------------------- 550 | // ReadDmiMemory 551 | //----------------------------------------------------------------------------- 552 | DWORD // Read size(byte), 0: failure 553 | WINAPI ReadDmiMemory( 554 | PBYTE buffer, // Buffer 555 | DWORD count, // Count 556 | DWORD unitSize // Unit Size (BYTE, WORD, DWORD) 557 | ); 558 | 559 | //----------------------------------------------------------------------------- 560 | // ReadPhysicalMemory 561 | //----------------------------------------------------------------------------- 562 | DWORD // Read size(byte), 0: failure 563 | WINAPI ReadPhysicalMemory( 564 | DWORD_PTR address, // Physical Memory Address 565 | PBYTE buffer, // Buffer 566 | DWORD count, // Count 567 | DWORD unitSize // Unit Size (BYTE, WORD, DWORD) 568 | ); 569 | 570 | //----------------------------------------------------------------------------- 571 | // WritePhysicalMemory 572 | //----------------------------------------------------------------------------- 573 | DWORD // Write size(byte), 0: failure 574 | WINAPI WritePhysicalMemory( 575 | DWORD_PTR address, // Physical Memory Address 576 | PBYTE buffer, // Buffer 577 | DWORD count, // Count 578 | DWORD unitSize // Unit Size (BYTE, WORD, DWORD) 579 | ); 580 | #endif -------------------------------------------------------------------------------- /win32/readjustService.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | Automates ryzenAdj calls based on custom conditions 4 | .DESCRIPTION 5 | This script is designed to provide maximum flexibility to the user. For that reason it does not use parameters. 6 | Instead of parameters, you need to populate the functions in the configuration section with your custom adjustments and additional custom code. 7 | .NOTES 8 | SPDX-License-Identifier: LGPL 9 | Falco Schaffrath 10 | #> 11 | 12 | Param([Parameter(Mandatory=$false)][switch]$noGUI) 13 | $Error.Clear() 14 | ################################################################################ 15 | #### Configuration Start 16 | ################################################################################ 17 | # WARNING: Use at your own risk! 18 | 19 | $pathToRyzenAdjDlls = Split-Path -Parent $PSCommandPath #script path is DLL path, needs to be absolut path if you define something else 20 | 21 | $showErrorPopupsDuringInit = $true 22 | # debug mode prints adjust success messages too instead of errorss only 23 | $debugMode = $false 24 | # if monitorField is set, this script does only adjust values if something did revert your monitored value. Clear monitorField String to disable monitoring 25 | # This needs to be an value which actually gets overwritten by your device firmware/software if no changes get detected, your settings will not reapplied 26 | $monitorField = "fast_limit" 27 | # Does reapply adjustments if power slider did change position, check $Script:acSlider or $Script:dcSlider to apply slider specific values 28 | $monitorPowerSlider = $true 29 | # Does reapply adjustments if AC line status did change when AC power cable is plugged or unplugged 30 | $monitorACLineStatus = $true 31 | # HWiNFO needs to be restartet after this script did run the first time with this option 32 | $updateHWINFOSensors = $false 33 | # some Zen3 devices have a locked STAPM limit, this workarround resets the stapm timer to have unlimited stapm. Use max stapm_limit and stapm_time (usually 500) to triger as less resets as possible 34 | $resetSTAPMUsage = $false 35 | 36 | function doAdjust_ACmode { 37 | $Script:repeatWaitTimeSeconds = 1 #only use values below 5s if you are using $monitorField 38 | adjust "fast_limit" 46000 39 | adjust "slow_limit" 25000 40 | #adjust "slow_time" 30 41 | #adjust "tctl_temp" 93 42 | #adjust "apu_skin_temp_limit" 50 43 | #adjust "vrmmax_current" 100000 44 | #replace any_other_field with additional adjustments. Name is equal to RyzenAdj options but it uses _ instead of - 45 | #adjust "any_other_field" 1234 46 | 47 | #custom code, for example set fan controll back to auto 48 | #values (WriteRegister: 47, FanSpeedResetValue:128) extracted from similar devices at https://github.com/hirschmann/nbfc/blob/master/Configs/ 49 | #Start-Process -NoNewWindow -Wait -filePath "C:\Program Files (x86)\NoteBook FanControl\ec-probe.exe" -ArgumentList("write", "47", "128") 50 | 51 | if($Script:acSlider -eq $Script:betterBattery){ 52 | #put adjustments for energie saving slider position here: 53 | enable "power_saving" #add 10s boost delay for usage on cable to reduce idle power consumtion 54 | } 55 | } 56 | 57 | function doAdjust_BatteryMode { 58 | $Script:repeatWaitTimeSeconds = 10 #do less reapplies and less HWiNFO updates to save power 59 | adjust "fast_limit" 26000 60 | adjust "slow_limit" 10000 61 | #adjust "any_other_field" 1234 62 | 63 | if($Script:dcSlider -eq $Script:betterBattery){ 64 | #put adjustments for energie saving slider position here: for example disable fan to save power 65 | #Start-Process -NoNewWindow -Wait -filePath "C:\Program Files (x86)\NoteBook FanControl\ec-probe.exe" -ArgumentList("write", "47", "0") 66 | } 67 | 68 | if($Script:dcSlider -eq $Script:bestPerformance){ 69 | #put adjustments for highest performance slider position here: 70 | enable "max_performance" #removes 10s boost delay on battery 71 | doAdjust_ACmode #set limits from cable mode on battery 72 | } 73 | } 74 | ################################################################################ 75 | #### Configuration End 76 | ################################################################################ 77 | 78 | $env:PATH += ";$pathToRyzenAdjDlls" 79 | $NL = $([System.Environment]::NewLine); 80 | 81 | if($noGUI){ $showErrorPopupsDuringInit = $false } 82 | 83 | $apiHeader = @' 84 | [DllImport("libryzenadj.dll")] public static extern IntPtr init_ryzenadj(); 85 | [DllImport("libryzenadj.dll")] public static extern int set_stapm_limit(IntPtr ry, [In]uint value); 86 | [DllImport("libryzenadj.dll")] public static extern int set_fast_limit(IntPtr ry, [In]uint value); 87 | [DllImport("libryzenadj.dll")] public static extern int set_slow_limit(IntPtr ry, [In]uint value); 88 | [DllImport("libryzenadj.dll")] public static extern int set_slow_time(IntPtr ry, [In]uint value); 89 | [DllImport("libryzenadj.dll")] public static extern int set_stapm_time(IntPtr ry, [In]uint value); 90 | [DllImport("libryzenadj.dll")] public static extern int set_tctl_temp(IntPtr ry, [In]uint value); 91 | [DllImport("libryzenadj.dll")] public static extern int set_vrm_current(IntPtr ry, [In]uint value); 92 | [DllImport("libryzenadj.dll")] public static extern int set_vrmsoc_current(IntPtr ry, [In]uint value); 93 | [DllImport("libryzenadj.dll")] public static extern int set_vrmmax_current(IntPtr ry, [In]uint value); 94 | [DllImport("libryzenadj.dll")] public static extern int set_vrmsocmax_current(IntPtr ry, [In]uint value); 95 | [DllImport("libryzenadj.dll")] public static extern int set_psi0_current(IntPtr ry, [In]uint value); 96 | [DllImport("libryzenadj.dll")] public static extern int set_psi0soc_current(IntPtr ry, [In]uint value); 97 | [DllImport("libryzenadj.dll")] public static extern int set_max_gfxclk_freq(IntPtr ry, [In]uint value); 98 | [DllImport("libryzenadj.dll")] public static extern int set_min_gfxclk_freq(IntPtr ry, [In]uint value); 99 | [DllImport("libryzenadj.dll")] public static extern int set_max_socclk_freq(IntPtr ry, [In]uint value); 100 | [DllImport("libryzenadj.dll")] public static extern int set_min_socclk_freq(IntPtr ry, [In]uint value); 101 | [DllImport("libryzenadj.dll")] public static extern int set_max_fclk_freq(IntPtr ry, [In]uint value); 102 | [DllImport("libryzenadj.dll")] public static extern int set_min_fclk_freq(IntPtr ry, [In]uint value); 103 | [DllImport("libryzenadj.dll")] public static extern int set_max_vcn(IntPtr ry, [In]uint value); 104 | [DllImport("libryzenadj.dll")] public static extern int set_min_vcn(IntPtr ry, [In]uint value); 105 | [DllImport("libryzenadj.dll")] public static extern int set_max_lclk(IntPtr ry, [In]uint value); 106 | [DllImport("libryzenadj.dll")] public static extern int set_min_lclk(IntPtr ry, [In]uint value); 107 | [DllImport("libryzenadj.dll")] public static extern int set_prochot_deassertion_ramp(IntPtr ry, [In]uint value); 108 | [DllImport("libryzenadj.dll")] public static extern int set_apu_skin_temp_limit(IntPtr ry, [In]uint value); 109 | [DllImport("libryzenadj.dll")] public static extern int set_dgpu_skin_temp_limit(IntPtr ry, [In]uint value); 110 | [DllImport("libryzenadj.dll")] public static extern int set_apu_slow_limit(IntPtr ry, [In]uint value); 111 | [DllImport("libryzenadj.dll")] public static extern int set_power_saving(IntPtr ry); 112 | [DllImport("libryzenadj.dll")] public static extern int set_max_performance(IntPtr ry); 113 | 114 | [DllImport("libryzenadj.dll")] public static extern int refresh_table(IntPtr ry); 115 | [DllImport("libryzenadj.dll")] public static extern IntPtr get_table_values(IntPtr ry); 116 | [DllImport("libryzenadj.dll")] public static extern float get_stapm_limit(IntPtr ry); 117 | [DllImport("libryzenadj.dll")] public static extern float get_stapm_value(IntPtr ry); 118 | [DllImport("libryzenadj.dll")] public static extern float get_stapm_time(IntPtr ry); 119 | [DllImport("libryzenadj.dll")] public static extern float get_fast_limit(IntPtr ry); 120 | [DllImport("libryzenadj.dll")] public static extern float get_fast_value(IntPtr ry); 121 | [DllImport("libryzenadj.dll")] public static extern float get_slow_limit(IntPtr ry); 122 | [DllImport("libryzenadj.dll")] public static extern float get_slow_value(IntPtr ry); 123 | [DllImport("libryzenadj.dll")] public static extern float get_apu_slow_limit(IntPtr ry); 124 | [DllImport("libryzenadj.dll")] public static extern float get_apu_slow_value(IntPtr ry); 125 | [DllImport("libryzenadj.dll")] public static extern float get_vrm_current(IntPtr ry); 126 | [DllImport("libryzenadj.dll")] public static extern float get_vrm_current_value(IntPtr ry); 127 | [DllImport("libryzenadj.dll")] public static extern float get_vrmsoc_current(IntPtr ry); 128 | [DllImport("libryzenadj.dll")] public static extern float get_vrmsoc_current_value(IntPtr ry); 129 | [DllImport("libryzenadj.dll")] public static extern float get_vrmmax_current(IntPtr ry); 130 | [DllImport("libryzenadj.dll")] public static extern float get_vrmmax_current_value(IntPtr ry); 131 | [DllImport("libryzenadj.dll")] public static extern float get_vrmsocmax_current(IntPtr ry); 132 | [DllImport("libryzenadj.dll")] public static extern float get_vrmsocmax_current_value(IntPtr ry); 133 | [DllImport("libryzenadj.dll")] public static extern float get_tctl_temp(IntPtr ry); 134 | [DllImport("libryzenadj.dll")] public static extern float get_tctl_temp_value(IntPtr ry); 135 | [DllImport("libryzenadj.dll")] public static extern float get_apu_skin_temp_limit(IntPtr ry); 136 | [DllImport("libryzenadj.dll")] public static extern float get_apu_skin_temp_value(IntPtr ry); 137 | [DllImport("libryzenadj.dll")] public static extern float get_dgpu_skin_temp_limit(IntPtr ry); 138 | [DllImport("libryzenadj.dll")] public static extern float get_dgpu_skin_temp_value(IntPtr ry); 139 | 140 | [DllImport("kernel32.dll")] public static extern uint GetModuleFileName(IntPtr hModule, [Out]StringBuilder lpFilename, [In]int nSize); 141 | [DllImport("kernel32.dll")] public static extern Boolean GetSystemPowerStatus(out SystemPowerStatus sps); 142 | public struct SystemPowerStatus { 143 | public Byte ACLineStatus; 144 | public Byte BatteryFlag; 145 | public Byte BatteryLifePercent; 146 | public Byte Reserved1; 147 | public Int32 BatteryLifeTime; 148 | public Int32 BatteryFullLifeTime; 149 | } 150 | 151 | public static String getExpectedWinRing0DriverFilepath(){ 152 | StringBuilder fileName = new StringBuilder(255); 153 | GetModuleFileName(IntPtr.Zero, fileName, fileName.Capacity); 154 | return Path.GetDirectoryName(fileName.ToString()) + "\\WinRing0x64.sys"; 155 | } 156 | 157 | public static String getDllImportErrors(){ 158 | try { 159 | Marshal.PrelinkAll(typeof(adj)); 160 | } catch (Exception e) { 161 | return e.Message; 162 | } 163 | return ""; 164 | } 165 | '@ 166 | 167 | if(-not ([System.Management.Automation.PSTypeName]'ryzen.adj').Type){ 168 | Add-Type -MemberDefinition $apiHeader -Namespace 'ryzen' -Name 'adj' -UsingNamespace ('System.Text', 'System.IO') 169 | } 170 | 171 | Add-Type -AssemblyName System.Windows.Forms 172 | function showErrorMsg ([String] $msg){ 173 | if($showErrorPopupsDuringInit){ 174 | [void][System.Windows.Forms.MessageBox]::Show($msg, $PSCommandPath, 175 | [System.Windows.Forms.MessageBoxButtons]::OK, 176 | [System.Windows.Forms.MessageBoxIcon]::Error) 177 | } 178 | } 179 | 180 | $dllImportErrors = [ryzen.adj]::getDllImportErrors(); 181 | if($dllImportErrors -or $Error){ 182 | Write-Error $dllImportErrors 183 | showErrorMsg "Problem with using libryzenadj.dll$NL$NL$($Error -join $NL)" 184 | exit 1 185 | } 186 | 187 | $winring0DriverFilepath = [ryzen.adj]::getExpectedWinRing0DriverFilepath() 188 | if(!(Test-Path $winring0DriverFilepath)) { Copy-Item -Path $pathToRyzenAdjDlls\WinRing0x64.sys -Destination $winring0DriverFilepath } 189 | 190 | $ry = [ryzen.adj]::init_ryzenadj() 191 | if($ry -eq 0){ 192 | $msg = "RyzenAdj could not get initialized.$($NL)Reason can be found inside Powershell$($NL)" 193 | if($psISE) { $msg += "It is not possible to see the error reason inside ISE, you need to test it in PowerShell Console" } 194 | showErrorMsg "$msg$NL$NL$($Error -join $NL)" 195 | exit 1 196 | } 197 | 198 | function adjust ([String] $fieldName, [uInt32] $value) { 199 | if($fieldName -eq $Script:monitorField) { 200 | # Adjust target value to the unit of measurement of return value of RyzenAdj 201 | $multiplier = if($value -gt 2000) { 0.001 } else { 1 } 202 | $newTargetValue = [math]::round($value * $multiplier, 3, 0) 203 | if($Script:monitorFieldAdjTarget -ne $newTargetValue){ 204 | $Script:monitorFieldAdjTarget = $newTargetValue 205 | Write-Host "set new monitoring target $fieldName to $newTargetValue" 206 | } 207 | } 208 | $res = Invoke-Expression "[ryzen.adj]::set_$fieldName($ry, $value)" 209 | switch ($res) { 210 | 0 { 211 | if($debugMode) { Write-Host "set $fieldName to $value" } 212 | return 213 | } 214 | -1 { Write-Error "set_$fieldName is not supported on this family"} 215 | -3 { Write-Error "set_$fieldName is not supported on this SMU"} 216 | -4 { Write-Error "set_$fieldName is rejected by SMU"} 217 | default { Write-Error "set_$fieldName did fail with $res"} 218 | } 219 | } 220 | 221 | function enable ([String] $fieldName) { 222 | $res = Invoke-Expression "[ryzen.adj]::set_$fieldName($ry)" 223 | switch ($res) { 224 | 0 { 225 | if($debugMode) { Write-Host "enable $fieldName"} 226 | return 227 | } 228 | -1 { Write-Error "set_$fieldName is not supported on this family"} 229 | -3 { Write-Error "set_$fieldName is not supported on this SMU"} 230 | -4 { Write-Error "set_$fieldName is rejected by SMU"} 231 | default { Write-Error "set_$fieldName did fail with $res"} 232 | } 233 | } 234 | 235 | function testMonitorField { 236 | if($monitorField -and $Script:monitorFieldAdjTarget -eq 0){ 237 | Write-Error ("You forgot to set $monitorField in your profile.$NL$NL" + 238 | "If you ignore it, the script will apply values unnessasary often.$NL") 239 | } 240 | } 241 | 242 | function updateMonitorFieldAdjResult { 243 | if($monitorField){ 244 | [void][ryzen.adj]::refresh_table($ry) 245 | $Script:monitorFieldAdjResult = [math]::round((getMonitorValue), 3, 0) 246 | if($Script:monitorFieldAdjTarget -ne $Script:monitorFieldAdjResult){ 247 | Write-Host ("Warning - $monitorField adjust result $Script:monitorFieldAdjResult does not match target value $Script:monitorFieldAdjTarget. Value $Script:monitorFieldAdjResult will be used for monitoring") 248 | } 249 | } 250 | } 251 | 252 | function testConfiguration { 253 | Write-Host "Test Adjustments" 254 | if($Script:systemPowerStatus.ACLineStatus){ 255 | doAdjust_BatteryMode 256 | testMonitorField 257 | $Script:monitorFieldAdjTarget = 0 258 | doAdjust_ACmode 259 | } else { 260 | doAdjust_ACmode 261 | testMonitorField 262 | $Script:monitorFieldAdjTarget = 0 263 | doAdjust_BatteryMode 264 | } 265 | testMonitorField 266 | updateMonitorFieldAdjResult 267 | 268 | if($resetSTAPMUsage -and [ryzen.adj]::get_stapm_time($ry) -eq 1) { 269 | Write-Error "resetSTAPMUsage function does only work on devices with active STAPM control and don't do anything on devices with enabled STT control." 270 | } 271 | 272 | if($Error -and $showErrorPopupsDuringInit){ 273 | $answer = [System.Windows.Forms.MessageBox]::Show("Your Adjustment configuration did not work.$NL$NL$($Error -join $NL)", $PSCommandPath, 274 | [System.Windows.Forms.MessageBoxButtons]::AbortRetryIgnore, 275 | [System.Windows.Forms.MessageBoxIcon]::Warning) 276 | $Error.Clear() 277 | if($answer -eq "Abort"){ exit 1 } 278 | if($answer -eq "Retry"){ testConfiguration } 279 | } 280 | } 281 | 282 | function getMonitorValue { 283 | if($monitorField){ 284 | return Invoke-Expression "[ryzen.adj]::get_$monitorField($ry)" 285 | } 286 | return 0 287 | } 288 | 289 | function createOrDeleteHWINFOSensors { 290 | if($updateHWINFOSensors){ 291 | New-Item -Path HKCU:\Software\HWiNFO64\Sensors\Custom -Name RyzenAdj -Force > $null 292 | 'Key,Name,Value 293 | Power0,STAPM Limit 294 | Power1,STAPM 295 | Power2,PPT FAST Limit 296 | Power3,PPT FAST 297 | Power4,PPT SLOW Limit 298 | Power5,PPT SLOW 299 | Power6,APU SLOW Limit 300 | Power7,APU SLOW 301 | Current0,TDC VRM Limit 302 | Current1,TDC VRM 303 | Current2,TDC VRM SoC Limit 304 | Current3,TDC VRM SoC 305 | Current4,EDC VRM Max Limit 306 | Current5,EDC VRM Max 307 | Current6,EDC VRM SoC Max Limit 308 | Current7,EDC VRM SoC Max 309 | Temp0,TCTL Temp Limit 310 | Temp1,TCTL Temp 311 | Temp2,SST APU Skin Temp Limit 312 | Temp3,SST APU Skin Temp 313 | Temp4,SST dGPU Skin Temp Limit 314 | Temp5,SST dGPU Skin Temp 315 | Usage0,STAPM Limit Usage, ("STAPM" / "STAPM Limit") * 100 316 | Usage1,PPT FAST Limit Usage, ("PPT FAST" / "PPT FAST Limit") * 100 317 | Usage2,PPT SLOW Limit Usage, ("PPT SLOW" / "PPT SLOW Limit") * 100 318 | Usage3,APU SLOW Limit Usage, ("APU SLOW" / "APU SLOW Limit") * 100 319 | Usage4,TDC VRM Limit Usage, ("TDC VRM" / "TDC VRM Limit") * 100 320 | Usage5,TDC VRM SoC Limit Usage, ("TDC VRM SoC" / "TDC VRM SoC Limit") * 100 321 | Usage6,EDC VRM Max Limit Usage, ("EDC VRM Max" / "EDC VRM Max Limit") * 100 322 | Usage7,EDC VRM SoC Max Limit Usage, ("EDC VRM SoC Max" / "EDC VRM SoC Max Limit") * 100 323 | Usage8,TCTL Temp Limit Usage, ("TCTL Temp" / "TCTL Temp Limit") * 100 324 | Usage9,SST APU Skin Temp Limit Usage, ("SST APU Skin Temp" / "SST APU Skin Temp Limit") * 100 325 | Usage10,SST dGPU Skin Temp Limit Usage, ("SST dGPU Skin Temp" / "SST dGPU Skin Temp Limit") * 100' | ConvertFrom-Csv -outvariable hwinfo_keys > $null 326 | $hwinfo_keys | ForEach-Object {New-item -Path HKCU:\Software\HWiNFO64\Sensors\Custom\RyzenAdj -Name $_.Key -Force} > $null 327 | $hwinfo_keys | ForEach-Object {[Microsoft.Win32.Registry]::SetValue("HKEY_CURRENT_USER\Software\HWiNFO64\Sensors\Custom\RyzenAdj\" + $_.Key,"Name",$_.Name)} > $null 328 | $hwinfo_keys | Where Value -ne $null | ForEach-Object {[Microsoft.Win32.Registry]::SetValue("HKEY_CURRENT_USER\Software\HWiNFO64\Sensors\Custom\RyzenAdj\" + $_.Key,"Value",$_.Value)} > $null 329 | } else { 330 | Remove-Item HKCU:\Software\HWiNFO64\Sensors\Custom\RyzenAdj -Recurse -ErrorAction:Ignore 331 | } 332 | } 333 | 334 | function setHWINFOValue ([String] $name, [float] $value) { 335 | if(![float]::IsNaN($value)){ [Microsoft.Win32.Registry]::SetValue("HKEY_CURRENT_USER\Software\HWiNFO64\Sensors\Custom\RyzenAdj\" + $name,"Value",[String]$value) } 336 | } 337 | 338 | function updateHWINFOSensors { 339 | setHWINFOValue Power0 ([ryzen.adj]::get_stapm_limit($ry)) 340 | setHWINFOValue Power1 ([ryzen.adj]::get_stapm_value($ry)) 341 | setHWINFOValue Power2 ([ryzen.adj]::get_fast_limit($ry)) 342 | setHWINFOValue Power3 ([ryzen.adj]::get_fast_value($ry)) 343 | setHWINFOValue Power4 ([ryzen.adj]::get_slow_limit($ry)) 344 | setHWINFOValue Power5 ([ryzen.adj]::get_slow_value($ry)) 345 | setHWINFOValue Power6 ([ryzen.adj]::get_apu_slow_limit($ry)) 346 | setHWINFOValue Power7 ([ryzen.adj]::get_apu_slow_value($ry)) 347 | setHWINFOValue Current0 ([ryzen.adj]::get_vrm_current($ry)) 348 | setHWINFOValue Current1 ([ryzen.adj]::get_vrm_current_value($ry)) 349 | setHWINFOValue Current2 ([ryzen.adj]::get_vrmsoc_current($ry)) 350 | setHWINFOValue Current3 ([ryzen.adj]::get_vrmsoc_current_value($ry)) 351 | setHWINFOValue Current4 ([ryzen.adj]::get_vrmmax_current($ry)) 352 | setHWINFOValue Current5 ([ryzen.adj]::get_vrmmax_current_value($ry)) 353 | setHWINFOValue Current6 ([ryzen.adj]::get_vrmsocmax_current($ry)) 354 | setHWINFOValue Current7 ([ryzen.adj]::get_vrmsocmax_current_value($ry)) 355 | setHWINFOValue Temp0 ([ryzen.adj]::get_tctl_temp($ry)) 356 | setHWINFOValue Temp1 ([ryzen.adj]::get_tctl_temp_value($ry)) 357 | setHWINFOValue Temp2 ([ryzen.adj]::get_apu_skin_temp_limit($ry)) 358 | setHWINFOValue Temp3 ([ryzen.adj]::get_apu_skin_temp_value($ry)) 359 | setHWINFOValue Temp4 ([ryzen.adj]::get_dgpu_skin_temp_limit($ry)) 360 | setHWINFOValue Temp5 ([ryzen.adj]::get_dgpu_skin_temp_value($ry)) 361 | #setHWINFOValue Usage11 $pmTable[546] 362 | } 363 | 364 | function resetSTAPMIfNeeded { 365 | $stapm_limit = [ryzen.adj]::get_stapm_limit($ry) 366 | $stapm_value = [ryzen.adj]::get_stapm_value($ry) 367 | $stapm_hysteresis = 1 #Throttling starts arround ~0.9W before limit 368 | 369 | if ($stapm_value -gt ($stapm_limit - $stapm_hysteresis)) { 370 | $stapm_time = [ryzen.adj]::get_stapm_time($ry) 371 | $reduced_stapm_limit = ($stapm_limit - 5) #reduce stapm by 5W 372 | Write-Host "[STAPM_RESET] stapm_value ($stapm_value) nearing stapm_limit ($stapm_limit), resetting..." 373 | [void][ryzen.adj]::set_stapm_limit($ry, ($reduced_stapm_limit) * 1000) 374 | [void][ryzen.adj]::set_stapm_time($ry, 0) 375 | [Threading.Thread]::Sleep(10) #10ms is usually enough time 376 | [void][ryzen.adj]::set_stapm_time($ry, $stapm_time) 377 | [void][ryzen.adj]::set_stapm_limit($ry, $stapm_limit * 1250) # add 25% STAPM limit in case we are at battery saving mode where applied values get reduced by 10% or 20% 378 | } 379 | } 380 | 381 | if(-not $Script:repeatWaitTimeSeconds) { $Script:repeatWaitTimeSeconds = 5 } 382 | $Script:monitorFieldAdjResult = 0; #adjust result will be used for monitoring because SMU may only set 90% and 80% of your value 383 | $Script:monitorFieldAdjTarget = 0; 384 | $powerkey = [Microsoft.Win32.Registry]::LocalMachine.OpenSubKey("SYSTEM\ControlSet001\Control\Power\User\PowerSchemes\") 385 | $Script:betterBattery = "961cc777-2547-4f9d-8174-7d86181b8a7a" 386 | $Script:betterPerformance = "00000000-0000-0000-0000-000000000000" 387 | $Script:bestPerformance = "ded574b5-45a0-4f42-8737-46345c09c238" 388 | $Script:acSlider = $powerkey.GetValue("ActiveOverlayACPowerScheme") 389 | $Script:dcSlider = $powerkey.GetValue("ActiveOverlayDCPowerScheme") 390 | 391 | $systemPowerStatus = New-Object ryzen.adj+SystemPowerStatus 392 | [void][ryzen.adj]::GetSystemPowerStatus([ref]$systemPowerStatus) 393 | $Script:acLineStatus = $systemPowerStatus.ACLineStatus 394 | 395 | testConfiguration 396 | 397 | <# Example how to get 560 lines of ptable 398 | $pmTable = [float[]]::new(560) 399 | $tablePtr = [ryzen.adj]::get_table_values($ry); 400 | [System.Runtime.InteropServices.Marshal]::Copy($tablePtr, $pmTable, 0, 560); 401 | #> 402 | createOrDeleteHWINFOSensors 403 | 404 | $mtxtArray = @() 405 | if($monitorField){$mtxtArray += "$monitorField changes"} 406 | if($monitorPowerSlider){$mtxtArray += "PowerSlider changes"} 407 | if($monitorACLineStatus){$mtxtArray += "ACLineStatus changes"} 408 | if($mtxtArray.Length){ 409 | $processType = "Monitor " + ($mtxtArray -join " and ") 410 | } else { 411 | $processType = "Apply Settings" 412 | } 413 | Write-Host "$processType every $Script:repeatWaitTimeSeconds seconds..." 414 | while($true) { 415 | $doAdjust = !$monitorField -and !$monitorPowerSlider -and !$monitorACLineStatus 416 | if($monitorField -or $updateHWINFOSensors -or $resetSTAPMUsage) { 417 | [void][ryzen.adj]::refresh_table($ry) 418 | #[System.Runtime.InteropServices.Marshal]::Copy($tablePtr, $pmTable, 0, 560); 419 | } 420 | 421 | if($updateHWINFOSensors){ 422 | updateHWINFOSensors 423 | } 424 | 425 | if($monitorPowerSlider -and ($Script:acSlider -ne $powerkey.GetValue("ActiveOverlayACPowerScheme") -or $Script:dcSlider -ne $powerkey.GetValue("ActiveOverlayDCPowerScheme"))){ 426 | Write-Host "Power Slider changed" 427 | $Script:acSlider = $powerkey.GetValue("ActiveOverlayACPowerScheme") 428 | $Script:dcSlider = $powerkey.GetValue("ActiveOverlayDCPowerScheme") 429 | $doAdjust = $true 430 | } 431 | if($monitorField){ 432 | $monitorValue = getMonitorValue 433 | if($Script:monitorFieldAdjResult -ne [math]::round($monitorValue, 3, 0)){ 434 | Write-Host "$monitorField value unexpectedly changed from $Script:monitorFieldAdjResult to $monitorValue" 435 | $doAdjust = $true 436 | } 437 | } 438 | if($monitorACLineStatus){ 439 | [void][ryzen.adj]::GetSystemPowerStatus([ref]$systemPowerStatus) 440 | if($Script:acLineStatus -ne $systemPowerStatus.ACLineStatus){ 441 | Write-Host "AC Line Status changed" 442 | $Script:acLineStatus = $systemPowerStatus.ACLineStatus 443 | $doAdjust = $true 444 | } 445 | } 446 | 447 | if($resetSTAPMUsage){ 448 | resetSTAPMIfNeeded 449 | } 450 | 451 | if($doAdjust){ 452 | [void][ryzen.adj]::GetSystemPowerStatus([ref]$systemPowerStatus) 453 | $oldWait = $Script:repeatWaitTimeSeconds 454 | if($systemPowerStatus.ACLineStatus){ 455 | doAdjust_ACmode 456 | } else { 457 | doAdjust_BatteryMode 458 | } 459 | updateMonitorFieldAdjResult 460 | if($oldWait -ne $Script:repeatWaitTimeSeconds ) { Write-Host "$processType every $Script:repeatWaitTimeSeconds seconds..." } 461 | } 462 | 463 | sleep $Script:repeatWaitTimeSeconds 464 | } 465 | --------------------------------------------------------------------------------