steam-deck-tools/PowerControl/Helpers/AMD/RyzenSMU.cs
Kamil Trzciński 9d9a9a4196 Allow to disable usage of Kernel Drivers (to allow work with Anti-Cheat detection)
This allows to use all tools with Anti-Cheat detection.
2022-11-26 20:49:36 +01:00

219 lines
5.5 KiB
C#

using CommonHelpers;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace PowerControl.Helpers.AMD
{
public class RyzenSMU : IDisposable
{
public IntPtr MMIO_ADDR;
public uint MMIO_SIZE;
public uint RES_ADDR, MSG_ADDR, PARAM_ADDR;
public uint INDEX_ADDR, DATA_ADDR;
IntPtr mappedAddress;
IntPtr physicalHandle;
InpOut? inpOut;
public RyzenSMU()
{
}
~RyzenSMU()
{
Dispose();
}
public bool Opened
{
get { return physicalHandle != IntPtr.Zero && mappedAddress != IntPtr.Zero; }
}
public bool Open()
{
if (physicalHandle != IntPtr.Zero)
return true;
if (MMIO_ADDR == IntPtr.Zero || MMIO_SIZE == 0)
return false;
try
{
inpOut = new InpOut();
mappedAddress = inpOut.MapPhysToLin(MMIO_ADDR, MMIO_SIZE, out physicalHandle);
}
catch (Exception e)
{
TraceLine("RyzenSMU: {0}", e);
return false;
}
return Opened;
}
public void Dispose()
{
if (physicalHandle == IntPtr.Zero)
return;
GC.SuppressFinalize(this);
inpOut?.UnmapPhysicalMemory(physicalHandle, mappedAddress);
mappedAddress = IntPtr.Zero;
physicalHandle = IntPtr.Zero;
using (inpOut) { }
inpOut = null;
}
private uint RregRaw(uint reg)
{
return (uint)Marshal.ReadInt32(mappedAddress, (int)reg);
}
private void WregRaw(uint reg, uint value)
{
Marshal.WriteInt32(mappedAddress, (int)reg, (int)value);
}
private bool WregCheckedRaw(uint reg, uint value)
{
WregRaw(reg, value);
return RregRaw(reg) == value;
}
private bool Wreg(uint reg, uint value)
{
if (!Opened)
return false;
bool success = false;
try
{
if (reg < MMIO_SIZE)
{
return success = WregCheckedRaw(reg, value);
}
else
{
if (!WregCheckedRaw(INDEX_ADDR, reg))
return false;
if (!WregCheckedRaw(DATA_ADDR, value))
return false;
}
return success = true;
}
finally
{
TraceLine("Wreg: reg={0:X}, value={1:X} => success={2}",
reg, value, success);
}
}
private bool Rreg(uint reg, out uint value)
{
value = default;
if (!Opened)
return false;
bool success = false;
try
{
if (reg < MMIO_SIZE)
{
value = RregRaw(reg);
}
else
{
if (!WregCheckedRaw(INDEX_ADDR, reg))
return false;
value = RregRaw(DATA_ADDR);
}
return success = true;
}
finally
{
TraceLine("Rreg: reg={0:X} => read={1}/{1:X}, success={2}",
reg, value, success);
}
}
private uint WaitForResponse()
{
const int timeout = 20;
for (int i = 0; i < timeout; i++)
{
uint value;
if (!Rreg(RES_ADDR, out value))
return 0;
if (value != 0)
return value;
Thread.SpinWait(100);
}
return 0;
}
public bool SendMsg(ushort msg, uint param)
{
return SendMsg(msg, param, out _);
}
public bool SendMsg(ushort msg, uint param, out uint arg)
{
bool success = false;
arg = 0;
try
{
var res = WaitForResponse();
if (res != 0x1)
{
// Reset SMU state
if (res != 0)
Wreg(RES_ADDR, 1);
return false;
}
Wreg(RES_ADDR, 0);
Wreg(PARAM_ADDR, param);
Wreg(MSG_ADDR, msg);
res = WaitForResponse();
if (res != 0x1)
return false;
success = Rreg(PARAM_ADDR, out arg);
return success;
}
finally
{
TraceLine(">> SendMsg: msg={0:X}, param={1:X} => arg={2}/{2:X}, success={3}",
msg, param, arg, success);
}
}
public bool SendMsg<T>(T msg, uint param)
{
return SendMsg((ushort)(object)msg, param, out _);
}
public bool SendMsg<T>(T msg, uint param, out uint arg) where T : unmanaged
{
return SendMsg((ushort)(object)msg, param, out arg);
}
private static void TraceLine(string format, params object?[]? arg)
{
Trace.WriteLine(string.Format(format, arg));
}
}
}