diff --git a/PowerControl/Helpers/AMD/RyzenSMU.cs b/PowerControl/Helpers/AMD/RyzenSMU.cs index eaa39a6..2bc94dc 100644 --- a/PowerControl/Helpers/AMD/RyzenSMU.cs +++ b/PowerControl/Helpers/AMD/RyzenSMU.cs @@ -7,7 +7,7 @@ using System.Linq; using System.Text; using System.Threading.Tasks; -namespace PowerControl.Helpers.GPU +namespace PowerControl.Helpers.AMD { public class RyzenSMU : IDisposable { diff --git a/PowerControl/Helpers/AMD/VangoghGPU.cs b/PowerControl/Helpers/AMD/VangoghGPU.cs index a0fe57a..05f3141 100644 --- a/PowerControl/Helpers/AMD/VangoghGPU.cs +++ b/PowerControl/Helpers/AMD/VangoghGPU.cs @@ -1,86 +1,18 @@ using CommonHelpers; using System.Diagnostics; +using Device = System.Tuple; -namespace PowerControl.Helpers.GPU +namespace PowerControl.Helpers.AMD { internal class VangoghGPU : IDisposable { - public struct SupportedDevice - { - public ushort VendorID, DeviceID; - public IntPtr MMIOAddress; - public uint MMIOSize; - public uint SMUVersion; - - public SupportedDevice(ushort VendorID, ushort DeviceID, uint MMIOAddress, uint MMIOSize, uint SMUVersion) - { - this.VendorID = VendorID; - this.DeviceID = DeviceID; - this.MMIOAddress = new IntPtr(MMIOAddress); - this.MMIOSize = MMIOSize; - this.SMUVersion = SMUVersion; - } - - private void Log(string format, params object?[]? arg) - { - Trace.WriteLine(string.Format("GPU: [{0:X4}:{1:X4}] ", VendorID, DeviceID) + string.Format(format, arg)); - } - - public bool Found() - { - // Strong validate device that it has our "memory layout" - var pciAddress = WinRing0.FindPciDeviceById(VendorID, DeviceID, 0); - if (pciAddress == WinRing0.NO_DEVICE) - { - Log("PCI: not found"); - return false; - } - - Log("PCI: [{0:X8}]", pciAddress); - - var barAddr = WinRing0.ReadPciConfigDword(pciAddress, 0x24); // BAR6 - if (MMIOAddress != new IntPtr(barAddr)) - { - Log("PCI: [{0:X8}] => BAR: {1:X8} vs {2:X8} => mismatch", - pciAddress, MMIOAddress, barAddr); - return false; - } - - Log("PCI: [{0:X8}] => BAR: {1:X8} => OK", - pciAddress, barAddr); - return true; - } - - public VangoghGPU? Open(bool validateSMU = true) - { - var gpu = VangoghGPU.OpenMMIO(MMIOAddress, MMIOSize); - if (gpu == null) - return null; - - if (validateSMU) - { - // Check supported SMU version - var smuVersion = gpu.SMUVersion; - if (smuVersion != SMUVersion) - { - Log("SMU: {0:X8} => not supported", smuVersion); - return null; - } - - Log("SMU: {0:X8} => detected", smuVersion); - } - - return gpu; - } - }; - - public static readonly SupportedDevice[] SupportedDevices = new SupportedDevice[] + public static readonly Device[] SupportedDevices = { // SteamDeck - new SupportedDevice(0x1002, 0x163F, 0x80300000, 0x80380000 - 0x80300000, 0x43F3900) + new Device("AMD Custom GPU 0405", 0x80300000, 0x8037ffff, 0x43F3900) }; - private static SupportedDevice? DetectedDevice; + private static Device? DetectedDevice; public static bool IsSupported { @@ -89,26 +21,70 @@ namespace PowerControl.Helpers.GPU public static VangoghGPU? Open() { - return DetectedDevice?.Open(false); + if (DetectedDevice is null) + return null; + + return Open(DetectedDevice); + } + + public static VangoghGPU? Open(Device device) + { + if (device is null) + return null; + + return OpenMMIO(new IntPtr((long)device.Item2), (uint)(device.Item3 - device.Item2 + 1)); } public static bool Detect() { + var discoveredDevices = DeviceManager.GetDevices(DeviceManager.GUID_DISPLAY).ToDictionary((pnp) => + { + return DeviceManager.GetDeviceDesc(pnp) ?? ""; + }); + foreach (var device in SupportedDevices) { - if (!device.Found()) - continue; + var deviceName = device.Item1; - using (var gpu = device.Open()) + if (!discoveredDevices.ContainsKey(deviceName)) { - if (gpu is not null) + TraceLine("GPU: {0}: Not matched.", deviceName); + continue; + } + + var devicePNP = discoveredDevices[deviceName]; + var ranges = DeviceManager.GetDeviceMemResources(devicePNP); + if (ranges is null) + { + TraceLine("GPU: {0}: {1}: No memory ranges", deviceName, devicePNP); + continue; + } + if (!ranges.Contains(new Tuple(new UIntPtr(device.Item2), new UIntPtr(device.Item3)))) + { + TraceLine("GPU: {0}: {1}: Memory range not found", deviceName, devicePNP); + continue; + } + + using (var gpu = Open(device)) + { + if (gpu is null) { - DetectedDevice = device; - return true; + TraceLine("GPU: {0}: {1}: Failed to open.", deviceName, devicePNP); + continue; } + + var smuVersion = gpu.SMUVersion; + if (smuVersion != device.Item4) + { + TraceLine("GPU: {0}: {1}: SMU not supported: {2:X8}", deviceName, devicePNP, smuVersion); + continue; + } + + TraceLine("GPU: {0}: Matched!", deviceName); + DetectedDevice = device; + return true; } } - DetectedDevice = null; return false; } diff --git a/PowerControl/Helpers/DeviceManager.cs b/PowerControl/Helpers/DeviceManager.cs new file mode 100644 index 0000000..d1b22b6 --- /dev/null +++ b/PowerControl/Helpers/DeviceManager.cs @@ -0,0 +1,214 @@ +using PowerControl.External; +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading.Tasks; + +namespace PowerControl.Helpers +{ + internal class DeviceManager + { + public static string[]? GetDevices(Guid? classGuid) + { + string? filter = null; + int flags = CM_GETIDLIST_FILTER_PRESENT; + + if (classGuid is not null) + { + filter = classGuid?.ToString("B").ToUpper(); + flags |= CM_GETIDLIST_FILTER_CLASS; + } + + var res = CM_Get_Device_ID_List_Size(out var size, filter, flags); + if (res != CR_SUCCESS) + return null; + + char[] data = new char[size]; + res = CM_Get_Device_ID_List(filter, data, size, flags); + if (res != CR_SUCCESS) + return null; + + var result = new string(data); + var devices = result.Split('\0', StringSplitOptions.RemoveEmptyEntries); + return devices.ToArray(); + } + + public static string? GetDeviceDesc(String PNPString) + { + if (CM_Locate_DevNode(out var devInst, PNPString, 0) != 0) + return null; + + if (!CM_Get_DevNode_Property(devInst, DEVPKEY_Device_DeviceDesc, out var deviceDesc, 0)) + return null; + + return deviceDesc; + } + + public static IList>? GetDeviceMemResources(string PNPString) + { + int res = CM_Locate_DevNode(out var devInst, PNPString, 0); + if (res != CR_SUCCESS) + return null; + + res = CM_Get_First_Log_Conf(out var logConf, devInst, ALLOC_LOG_CONF); + if (res != CR_SUCCESS) + res = CM_Get_First_Log_Conf(out logConf, devInst, BOOT_LOG_CONF); + if (res != CR_SUCCESS) + return null; + + var ranges = new List>(); + + while (CM_Get_Next_Res_Des(out var newResDes, logConf, ResType_Mem, out _, 0) == 0) + { + CM_Free_Res_Des_Handle(logConf); + logConf = newResDes; + + if (!CM_Get_Res_Des_Data(logConf, out var memResource, 0)) + continue; + + ranges.Add(new Tuple( + memResource.MEM_Header.MD_Alloc_Base, memResource.MEM_Header.MD_Alloc_End)); + } + + CM_Free_Res_Des_Handle(logConf); + return ranges; + } + + static bool CM_Get_DevNode_Property(IntPtr devInst, DEVPROPKEY propertyKey, out string result, int flags) + { + result = default; + + // int length = 0; + // int res = CM_Get_DevNode_Property(devInst, ref propertyKey, out var propertyType, null, ref length, flags); + // if (res != CR_SUCCESS && res != CR_BUFFER_TOO_SMALL) + // return false; + + char[] buffer = new char[2048]; + int length = buffer.Length; + int res = CM_Get_DevNode_Property(devInst, ref propertyKey, out var propertyType, buffer, ref length, flags); + if (res != CR_SUCCESS) + return false; + if (propertyType != DEVPROP_TYPE_STRING) + return false; + + result = new String(buffer, 0, length).Split('\0').First(); + return true; + } + + static bool CM_Get_Res_Des_Data(IntPtr rdResDes, out T buffer, int ulFlags) where T : struct + { + buffer = default; + + int res = CM_Get_Res_Des_Data_Size(out var size, rdResDes, ulFlags); + if (res != CR_SUCCESS) + return false; + + int sizeOf = Marshal.SizeOf(); + if (sizeOf < size) + return false; + + var addr = Marshal.AllocHGlobal(sizeOf); + try + { + res = CM_Get_Res_Des_Data(rdResDes, addr, size, 0); + if (res != CR_SUCCESS) + return false; + + buffer = Marshal.PtrToStructure(addr); + return true; + } + finally + { + Marshal.FreeHGlobal(addr); + } + } + + [DllImport("setupapi.dll", CharSet = CharSet.Auto)] + static extern int CM_Locate_DevNode(out IntPtr pdnDevInst, string pDeviceID, int ulFlags); + + [DllImport("setupapi.dll", CharSet = CharSet.Unicode)] + static extern int CM_Get_Device_ID_List_Size(out int idListlen, string? filter, int ulFlags); + + [DllImport("setupapi.dll", CharSet = CharSet.Unicode)] + static extern int CM_Get_Device_ID_List(string? filter, char[] bffr, int bffrLen, int ulFlags); + + [DllImport("CfgMgr32.dll", CharSet = CharSet.Unicode)] + static extern int CM_Get_DevNode_Property(IntPtr devInst, ref DEVPROPKEY propertyKey, out int propertyType, char[]? bffr, ref int bffrLen, int flags); + + [DllImport("setupapi.dll")] + static extern int CM_Free_Res_Des_Handle(IntPtr rdResDes); + + [DllImport("setupapi.dll")] + static extern int CM_Get_First_Log_Conf(out IntPtr rdResDes, IntPtr pdnDevInst, int ulFlags); + + [DllImport("setupapi.dll")] + static extern int CM_Get_Next_Res_Des(out IntPtr newResDes, IntPtr rdResDes, int resType, out int resourceID, int ulFlags); + + [DllImport("setupapi.dll")] + static extern int CM_Get_Res_Des_Data_Size(out int size, IntPtr rdResDes, int ulFlags); + + [DllImport("setupapi.dll")] + static extern int CM_Get_Res_Des_Data(IntPtr rdResDes, IntPtr buffer, int size, int ulFlags); + + [StructLayout(LayoutKind.Sequential)] + struct MEM_DES + { + internal uint MD_Count; + internal uint MD_Type; + internal UIntPtr MD_Alloc_Base; + internal UIntPtr MD_Alloc_End; + internal uint MD_Flags; + internal uint MD_Reserved; + }; + + [StructLayout(LayoutKind.Sequential, Pack = 4)] + struct MEM_RANGE + { + internal UIntPtr MR_Align; // specifies mask for base alignment + internal uint MR_nBytes; // specifies number of bytes required + internal UIntPtr MR_Min; // specifies minimum address of the range + internal UIntPtr MR_Max; // specifies maximum address of the range + internal uint MR_Flags; // specifies flags describing range (fMD flags) + internal uint MR_Reserved; + }; + + [StructLayout(LayoutKind.Sequential)] + struct MEM_RESOURCE + { + internal MEM_DES MEM_Header; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 20)] + internal MEM_RANGE[] MEM_Data; + }; + + [StructLayout(LayoutKind.Sequential)] + struct DEVPROPKEY + { + public Guid Guid; + public uint Pid; + + public DEVPROPKEY(String guid, uint pid) + { + this.Guid = new Guid(guid); + this.Pid = pid; + } + }; + + const int ALLOC_LOG_CONF = 0x00000002; // Specifies the Alloc Element. + const int BOOT_LOG_CONF = 0x00000003; // Specifies the RM Alloc Element. + const int ResType_Mem = (0x00000001); // Physical address resource + + const int CM_GETIDLIST_FILTER_PRESENT = 0x00000100; + const int CM_GETIDLIST_FILTER_CLASS = 0x00000200; + const int CR_SUCCESS = 0x0; + const int CR_BUFFER_TOO_SMALL = 0x1A; + + const int DEVPROP_TYPE_STRING = 0x00000012; + + static readonly DEVPROPKEY DEVPKEY_Device_DeviceDesc = new DEVPROPKEY("a45c254e-df1c-4efd-8020-67d146a850e0", 2); + + internal static readonly Guid GUID_DISPLAY = new Guid("{4d36e968-e325-11ce-bfc1-08002be10318}"); + } +} diff --git a/PowerControl/MenuStack.cs b/PowerControl/MenuStack.cs index 2b3abcc..259b08e 100644 --- a/PowerControl/MenuStack.cs +++ b/PowerControl/MenuStack.cs @@ -1,7 +1,6 @@ using CommonHelpers; using PowerControl.Helpers; using PowerControl.Helpers.AMD; -using PowerControl.Helpers.GPU; using System.Diagnostics; using static PowerControl.Helpers.AMD.DCE; diff --git a/PowerControl/Program.cs b/PowerControl/Program.cs index 1865e82..ea63f2a 100644 --- a/PowerControl/Program.cs +++ b/PowerControl/Program.cs @@ -1,6 +1,8 @@ using CommonHelpers; -using PowerControl.Helpers.GPU; +using PowerControl.Helpers; +using PowerControl.Helpers.AMD; using System.Diagnostics; +using System.Runtime.InteropServices; namespace PowerControl { @@ -18,8 +20,6 @@ namespace PowerControl if (Settings.Default.EnableExperimentalFeatures) { - Trace.WriteLine("WinRing0 initialized=" + WinRing0.InitializeOls().ToString()); - Instance.WithGlobalMutex(1000, () => VangoghGPU.Detect()); } diff --git a/RELEASE.md b/RELEASE.md index f2df0e7..779e80a 100644 --- a/RELEASE.md +++ b/RELEASE.md @@ -9,5 +9,6 @@ - Highly risky: Allow to change CPU and GPU frequency (enable `EnableExperimentalFeatures` in `PowerControl.dll.config`) - Show CPU/GPU frequency in Full overlay - Allow to control GPU Scaling and Display Color Correction +- Do not use WinRing0 for GPU detection to control CPU/GPU frequency If you found it useful buy me [Ko-fi](https://ko-fi.com/ayufan).