mirror of
https://github.com/ayufan/steam-deck-tools.git
synced 2026-01-26 10:14:21 +01:00
Match GPU memory regions via Device Manager framework instead of Ring 0
This commit is contained in:
parent
1cd98643bd
commit
4bb264802d
|
|
@ -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
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,86 +1,18 @@
|
|||
using CommonHelpers;
|
||||
using System.Diagnostics;
|
||||
using Device = System.Tuple<string, ulong, ulong, uint>;
|
||||
|
||||
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<UIntPtr, UIntPtr>(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;
|
||||
}
|
||||
|
|
|
|||
214
PowerControl/Helpers/DeviceManager.cs
Normal file
214
PowerControl/Helpers/DeviceManager.cs
Normal file
|
|
@ -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<Tuple<UIntPtr, UIntPtr>>? 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<Tuple<UIntPtr, UIntPtr>>();
|
||||
|
||||
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<MEM_RESOURCE>(logConf, out var memResource, 0))
|
||||
continue;
|
||||
|
||||
ranges.Add(new Tuple<UIntPtr, UIntPtr>(
|
||||
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<T>(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<T>();
|
||||
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<T>(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}");
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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).
|
||||
|
|
|
|||
Loading…
Reference in a new issue