diff --git a/.editorconfig b/.editorconfig index 3eba5d1..73f2470 100644 --- a/.editorconfig +++ b/.editorconfig @@ -28,8 +28,8 @@ csharp_new_line_between_query_expression_clauses = true # Indentation preferences csharp_indent_block_contents = true csharp_indent_braces = false -csharp_indent_case_contents = true -csharp_indent_case_contents_when_block = true +csharp_indent_case_contents = false +csharp_indent_case_contents_when_block = false csharp_indent_switch_labels = true csharp_indent_labels = one_less_than_current diff --git a/LibreHardwareMonitor/Resources/current.png b/LibreHardwareMonitor/Resources/current.png new file mode 100644 index 0000000..3721f55 Binary files /dev/null and b/LibreHardwareMonitor/Resources/current.png differ diff --git a/LibreHardwareMonitorLib/Hardware/Cpu/Amd17Cpu.cs b/LibreHardwareMonitorLib/Hardware/Cpu/Amd17Cpu.cs index d322791..72d3dbf 100644 --- a/LibreHardwareMonitorLib/Hardware/Cpu/Amd17Cpu.cs +++ b/LibreHardwareMonitorLib/Hardware/Cpu/Amd17Cpu.cs @@ -13,16 +13,21 @@ namespace LibreHardwareMonitor.Hardware.CPU internal sealed class Amd17Cpu : AmdCpu { private readonly Processor _processor; - private int _sensorClock; - private int _sensorMulti; - private int _sensorPower; - - // counter, to create sensor index values - private int _sensorTemperatures; - private int _sensorVoltage; + private readonly Dictionary _sensorTypeIndex; + private readonly RyzenSMU _smu; public Amd17Cpu(int processorIndex, CpuId[][] cpuId, ISettings settings) : base(processorIndex, cpuId, settings) { + _sensorTypeIndex = new Dictionary(); + foreach (SensorType type in Enum.GetValues(typeof(SensorType))) + { + _sensorTypeIndex.Add(type, 0); + } + + _sensorTypeIndex[SensorType.Load] = _active.Count(x => x.SensorType == SensorType.Load); + + _smu = new RyzenSMU(_family, _model, _packageType); + // Add all numa nodes. // Register ..1E_2, [10:8] + 1 _processor = new Processor(this); @@ -51,6 +56,7 @@ namespace LibreHardwareMonitor.Hardware.CPU { coreId++; } + lastCoreId = coreIdRead; _processor.AppendThread(thread, nodeId, coreId); @@ -66,9 +72,9 @@ namespace LibreHardwareMonitor.Hardware.CPU public override string GetReport() { - StringBuilder r = new StringBuilder(); + StringBuilder r = new(); r.Append(base.GetReport()); - r.Append("Ryzen"); + r.Append(_smu.GetReport()); return r.ToString(); } @@ -77,6 +83,7 @@ namespace LibreHardwareMonitor.Hardware.CPU base.Update(); _processor.UpdateSensors(); + foreach (NumaNode node in _processor.Nodes) { NumaNode.UpdateSensors(); @@ -90,37 +97,44 @@ namespace LibreHardwareMonitor.Hardware.CPU private class Processor { - private readonly Amd17Cpu _cpu; private readonly Sensor _busClock; private readonly Sensor[] _ccdTemperatures; private readonly Sensor _coreTemperatureTctl; private readonly Sensor _coreTemperatureTctlTdie; private readonly Sensor _coreTemperatureTdie; private readonly Sensor _coreVoltage; + private readonly Amd17Cpu _cpu; private readonly Sensor _packagePower; + private readonly Dictionary, Sensor> _smuSensors = new(); private readonly Sensor _socVoltage; + private Sensor _ccdsAverageTemperature; private Sensor _ccdsMaxTemperature; - private DateTime _lastPwrTime = new DateTime(0); + private DateTime _lastPwrTime = new(0); private uint _lastPwrValue; public Processor(Hardware hardware) { _cpu = (Amd17Cpu)hardware; - _packagePower = new Sensor("Package Power", _cpu._sensorPower++, SensorType.Power, _cpu, _cpu._settings); - _coreTemperatureTctl = new Sensor("Core (Tctl)", _cpu._sensorTemperatures++, SensorType.Temperature, _cpu, _cpu._settings); - _coreTemperatureTdie = new Sensor("Core (Tdie)", _cpu._sensorTemperatures++, SensorType.Temperature, _cpu, _cpu._settings); - _coreTemperatureTctlTdie = new Sensor("Core (Tctl/Tdie)", _cpu._sensorTemperatures++, SensorType.Temperature, _cpu, _cpu._settings); + _packagePower = new Sensor("Package", _cpu._sensorTypeIndex[SensorType.Power]++, SensorType.Power, _cpu, _cpu._settings); + _coreTemperatureTctl = new Sensor("Core (Tctl)", _cpu._sensorTypeIndex[SensorType.Temperature]++, SensorType.Temperature, _cpu, _cpu._settings); + _coreTemperatureTdie = new Sensor("Core (Tdie)", _cpu._sensorTypeIndex[SensorType.Temperature]++, SensorType.Temperature, _cpu, _cpu._settings); + _coreTemperatureTctlTdie = new Sensor("Core (Tctl/Tdie)", _cpu._sensorTypeIndex[SensorType.Temperature]++, SensorType.Temperature, _cpu, _cpu._settings); _ccdTemperatures = new Sensor[8]; // Hardcoded until there's a way to get max CCDs. - _coreVoltage = new Sensor("Core (SVI2 TFN)", _cpu._sensorVoltage++, SensorType.Voltage, _cpu, _cpu._settings); - _socVoltage = new Sensor("SoC (SVI2 TFN)", _cpu._sensorVoltage++, SensorType.Voltage, _cpu, _cpu._settings); - _busClock = new Sensor("Bus Speed", _cpu._sensorClock++, SensorType.Clock, _cpu, _cpu._settings); + _coreVoltage = new Sensor("Core (SVI2 TFN)", _cpu._sensorTypeIndex[SensorType.Voltage]++, SensorType.Voltage, _cpu, _cpu._settings); + _socVoltage = new Sensor("SoC (SVI2 TFN)", _cpu._sensorTypeIndex[SensorType.Voltage]++, SensorType.Voltage, _cpu, _cpu._settings); + _busClock = new Sensor("Bus Speed", _cpu._sensorTypeIndex[SensorType.Clock]++, SensorType.Clock, _cpu, _cpu._settings); _cpu.ActivateSensor(_packagePower); + + foreach (KeyValuePair sensor in _cpu._smu.GetPmTableStructure()) + { + _smuSensors.Add(sensor, new Sensor(sensor.Value.Name, _cpu._sensorTypeIndex[sensor.Value.Type]++, sensor.Value.Type, _cpu, _cpu._settings)); + } } - public List Nodes { get; } = new List(); + public List Nodes { get; } = new(); public void UpdateSensors() { @@ -279,16 +293,17 @@ namespace LibreHardwareMonitor.Hardware.CPU ccdRawTemp &= 0xFFF; float ccdTemp = ((ccdRawTemp * 125) - 305000) * 0.001f; - if (ccdRawTemp > 0 && ccdTemp < 125) // Zen 2 reports 95 degrees C max, but it might exceed that. + if (ccdRawTemp > 0 && ccdTemp < 125) // Zen 2 reports 95 degrees C max, but it might exceed that. { if (_ccdTemperatures[i] == null) { _cpu.ActivateSensor(_ccdTemperatures[i] = new Sensor($"CCD{i + 1} (Tdie)", - _cpu._sensorTemperatures++, - SensorType.Temperature, - _cpu, - _cpu._settings)); + _cpu._sensorTypeIndex[SensorType.Temperature]++, + SensorType.Temperature, + _cpu, + _cpu._settings)); } + _ccdTemperatures[i].Value = ccdTemp; } } @@ -301,25 +316,26 @@ namespace LibreHardwareMonitor.Hardware.CPU if (_ccdsMaxTemperature == null) { _cpu.ActivateSensor(_ccdsMaxTemperature = new Sensor("CCDs Max (Tdie)", - _cpu._sensorTemperatures++, - SensorType.Temperature, - _cpu, - _cpu._settings)); + _cpu._sensorTypeIndex[SensorType.Temperature]++, + SensorType.Temperature, + _cpu, + _cpu._settings)); } if (_ccdsAverageTemperature == null) { _cpu.ActivateSensor(_ccdsAverageTemperature = new Sensor("CCDs Average (Tdie)", - _cpu._sensorTemperatures++, - SensorType.Temperature, - _cpu, - _cpu._settings)); + _cpu._sensorTypeIndex[SensorType.Temperature]++, + SensorType.Temperature, + _cpu, + _cpu._settings)); } _ccdsMaxTemperature.Value = activeCcds.Max(x => x.Value); _ccdsAverageTemperature.Value = activeCcds.Average(x => x.Value); } } + Ring0.ReleasePciBusMutex(); } @@ -354,6 +370,21 @@ namespace LibreHardwareMonitor.Hardware.CPU _busClock.Value = (float)(_cpu.TimeStampCounterFrequency / timeStampCounterMultiplier); _cpu.ActivateSensor(_busClock); } + + if (_cpu._smu.IsPmTableLayoutDefined()) + { + float[] smuData = _cpu._smu.GetPmTable(); + + foreach (var sensor in _smuSensors) + { + if (smuData.Length > sensor.Key.Key) + { + sensor.Value.Value = smuData[sensor.Key.Key] * sensor.Key.Value.Scale; + if (sensor.Value.Value != 0) + _cpu.ActivateSensor(sensor.Value); + } + } + } } private double GetTimeStampCounterMultiplier() @@ -427,13 +458,13 @@ namespace LibreHardwareMonitor.Hardware.CPU private class Core { - private readonly Amd17Cpu _cpu; private readonly Sensor _clock; + private readonly Amd17Cpu _cpu; private readonly Sensor _multiplier; private readonly Sensor _power; private readonly Sensor _vcore; private ISensor _busSpeed; - private DateTime _lastPwrTime = new DateTime(0); + private DateTime _lastPwrTime = new(0); private uint _lastPwrValue; public Core(Amd17Cpu cpu, int id) @@ -441,10 +472,10 @@ namespace LibreHardwareMonitor.Hardware.CPU _cpu = cpu; Threads = new List(); CoreId = id; - _clock = new Sensor("Core #" + CoreId, cpu._sensorClock++, SensorType.Clock, cpu, cpu._settings); - _multiplier = new Sensor("Core #" + CoreId, cpu._sensorMulti++, SensorType.Factor, cpu, cpu._settings); - _power = new Sensor("Core #" + CoreId + " (SMU)", cpu._sensorPower++, SensorType.Power, cpu, cpu._settings); - _vcore = new Sensor("Core #" + CoreId + " VID", cpu._sensorVoltage++, SensorType.Voltage, cpu, cpu._settings); + _clock = new Sensor("Core #" + CoreId, _cpu._sensorTypeIndex[SensorType.Clock]++, SensorType.Clock, cpu, cpu._settings); + _multiplier = new Sensor("Core #" + CoreId, cpu._sensorTypeIndex[SensorType.Factor]++, SensorType.Factor, cpu, cpu._settings); + _power = new Sensor("Core #" + CoreId + " (SMU)", cpu._sensorTypeIndex[SensorType.Power]++, SensorType.Power, cpu, cpu._settings); + _vcore = new Sensor("Core #" + CoreId + " VID", cpu._sensorTypeIndex[SensorType.Voltage]++, SensorType.Voltage, cpu, cpu._settings); cpu.ActivateSensor(_clock); cpu.ActivateSensor(_multiplier); @@ -506,7 +537,7 @@ namespace LibreHardwareMonitor.Hardware.CPU double clock = 200.0; _busSpeed ??= _cpu.Sensors.FirstOrDefault(x => x.Name == "Bus Speed"); if (_busSpeed?.Value.HasValue == true && _busSpeed.Value > 0) - clock = (double) (_busSpeed.Value * 2); + clock = (double)(_busSpeed.Value * 2); _clock.Value = (float)(curCpuFid / (double)curCpuDfsId * clock); diff --git a/LibreHardwareMonitorLib/Hardware/Cpu/CpuId.cs b/LibreHardwareMonitorLib/Hardware/Cpu/CpuId.cs index e4b5d34..e75917c 100644 --- a/LibreHardwareMonitorLib/Hardware/Cpu/CpuId.cs +++ b/LibreHardwareMonitorLib/Hardware/Cpu/CpuId.cs @@ -141,6 +141,7 @@ namespace LibreHardwareMonitor.Hardware.CPU Model = ((Data[1, 0] & 0x0F0000) >> 12) + ((Data[1, 0] & 0xF0) >> 4); Stepping = Data[1, 0] & 0x0F; ApicId = (Data[1, 1] >> 24) & 0xFF; + PkgType = (ExtData[1, 1] >> 28) & 0xFF; switch (Vendor) { @@ -240,6 +241,8 @@ namespace LibreHardwareMonitor.Hardware.CPU public Vendor Vendor { get; } = Vendor.Unknown; + public uint PkgType { get; } + /// /// Gets the specified . /// diff --git a/LibreHardwareMonitorLib/Hardware/Cpu/GenericCpu.cs b/LibreHardwareMonitorLib/Hardware/Cpu/GenericCpu.cs index 417ce8c..a691141 100644 --- a/LibreHardwareMonitorLib/Hardware/Cpu/GenericCpu.cs +++ b/LibreHardwareMonitorLib/Hardware/Cpu/GenericCpu.cs @@ -20,6 +20,7 @@ namespace LibreHardwareMonitor.Hardware.CPU protected readonly uint _model; protected readonly int _processorIndex; protected readonly uint _stepping; + protected readonly uint _packageType; private readonly Sensor[] _coreLoads; private readonly CpuLoad _cpuLoad; @@ -39,6 +40,7 @@ namespace LibreHardwareMonitor.Hardware.CPU _family = cpuId[0][0].Family; _model = cpuId[0][0].Model; _stepping = cpuId[0][0].Stepping; + _packageType = cpuId[0][0].PkgType; _processorIndex = processorIndex; _coreCount = cpuId.Length; @@ -71,10 +73,14 @@ namespace LibreHardwareMonitor.Hardware.CPU if (_cpuLoad.IsAvailable) { foreach (Sensor sensor in _coreLoads) + { ActivateSensor(sensor); + } if (_totalLoad != null) + { ActivateSensor(_totalLoad); + } } if (HasTimeStampCounter) diff --git a/LibreHardwareMonitorLib/Hardware/ISensor.cs b/LibreHardwareMonitorLib/Hardware/ISensor.cs index 48b903a..750c685 100644 --- a/LibreHardwareMonitorLib/Hardware/ISensor.cs +++ b/LibreHardwareMonitorLib/Hardware/ISensor.cs @@ -16,6 +16,7 @@ namespace LibreHardwareMonitor.Hardware { Voltage, // V Current, // A + Power, // W Clock, // MHz Temperature, // °C Load, // % @@ -25,7 +26,6 @@ namespace LibreHardwareMonitor.Hardware Control, // % Level, // % Factor, // 1 - Power, // W Data, // GB = 2^30 Bytes SmallData, // MB = 2^20 Bytes Throughput // B/s diff --git a/LibreHardwareMonitorLib/Hardware/KernelDriver.cs b/LibreHardwareMonitorLib/Hardware/KernelDriver.cs index 62d954c..8070ecf 100644 --- a/LibreHardwareMonitorLib/Hardware/KernelDriver.cs +++ b/LibreHardwareMonitorLib/Hardware/KernelDriver.cs @@ -137,6 +137,26 @@ namespace LibreHardwareMonitor.Hardware return b; } + public bool DeviceIOControl(Kernel32.IOControlCode ioControlCode, object inBuffer, ref T[] outBuffer) + { + if (_device == null) + return false; + + + object boxedOutBuffer = outBuffer; + bool b = Kernel32.DeviceIoControl(_device, + ioControlCode, + inBuffer, + inBuffer == null ? 0 : (uint)Marshal.SizeOf(inBuffer), + boxedOutBuffer, + (uint)(Marshal.SizeOf(typeof(T)) * outBuffer.Length), + out uint _, + IntPtr.Zero); + + outBuffer = (T[])boxedOutBuffer; + return b; + } + public void Close() { if (_device != null) diff --git a/LibreHardwareMonitorLib/Hardware/Ring0.cs b/LibreHardwareMonitorLib/Hardware/Ring0.cs index 263d29f..334068b 100644 --- a/LibreHardwareMonitorLib/Hardware/Ring0.cs +++ b/LibreHardwareMonitorLib/Hardware/Ring0.cs @@ -466,6 +466,15 @@ namespace LibreHardwareMonitor.Hardware return _driver.DeviceIOControl(Interop.Ring0.IOCTL_OLS_READ_MEMORY, input, ref buffer); } + public static bool ReadMemory(ulong address, ref T[] buffer) + { + if (_driver == null) + return false; + + ReadMemoryInput input = new ReadMemoryInput { Address = address, UnitSize = (uint)Marshal.SizeOf(typeof(T)), Count = (uint)buffer.Length }; + return _driver.DeviceIOControl(Interop.Ring0.IOCTL_OLS_READ_MEMORY, input, ref buffer); + } + [StructLayout(LayoutKind.Sequential, Pack = 1)] private struct WriteMsrInput { diff --git a/LibreHardwareMonitorLib/Hardware/RyzenSMU.cs b/LibreHardwareMonitorLib/Hardware/RyzenSMU.cs new file mode 100644 index 0000000..b0746b9 --- /dev/null +++ b/LibreHardwareMonitorLib/Hardware/RyzenSMU.cs @@ -0,0 +1,819 @@ +// ported from: https://gitlab.com/leogx9r/ryzen_smu +// and: https://github.com/irusanov/SMUDebugTool + +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading; + +// ReSharper disable InconsistentNaming + +namespace LibreHardwareMonitor.Hardware +{ + internal class RyzenSMU + { + private const byte SMU_PCI_ADDR_REG = 0xC4; + private const byte SMU_PCI_DATA_REG = 0xC8; + private const uint SMU_REQ_MAX_ARGS = 6; + private const uint SMU_RETRIES_MAX = 8096; + + private readonly CpuCodeName _cpuCodeName; + private readonly Mutex _mutex = new(); + private readonly bool _supportedCPU; + + private readonly Dictionary> _supportedPmTableVersions = new() + { + { + // Zen Raven Ridge APU. + 0x001E0004, new Dictionary + { + { 28, new SmuSensorType { Name = "TDC", Type = SensorType.Current, Scale = 1}}, + { 44, new SmuSensorType { Name = "EDC", Type = SensorType.Current, Scale = 1}}, + //{ 61, new SmuSensorType { Name = "Core", Type = SensorType.Voltage } }, + //{ 62, new SmuSensorType { Name = "Core", Type = SensorType.Current, Scale = 1} }, + //{ 63, new SmuSensorType { Name = "Core", Type = SensorType.Power, Scale = 1 } }, + //{ 65, new SmuSensorType { Name = "SoC", Type = SensorType.Voltage } }, + { 66, new SmuSensorType { Name = "SoC", Type = SensorType.Current, Scale = 1 } }, + { 67, new SmuSensorType { Name = "SoC", Type = SensorType.Power, Scale = 1 } }, + //{ 96, new SmuSensorType { Name = "Core #1", Type = SensorType.Power } }, + //{ 97, new SmuSensorType { Name = "Core #2", Type = SensorType.Power } }, + //{ 98, new SmuSensorType { Name = "Core #3", Type = SensorType.Power } }, + //{ 99, new SmuSensorType { Name = "Core #4", Type = SensorType.Power } }, + { 108, new SmuSensorType { Name = "Core #1", Type = SensorType.Temperature, Scale = 1 } }, + { 109, new SmuSensorType { Name = "Core #2", Type = SensorType.Temperature, Scale = 1 } }, + { 110, new SmuSensorType { Name = "Core #3", Type = SensorType.Temperature, Scale = 1 } }, + { 111, new SmuSensorType { Name = "Core #4", Type = SensorType.Temperature, Scale = 1 } }, + { 150, new SmuSensorType { Name = "GFX", Type = SensorType.Voltage, Scale = 1 } }, + { 151, new SmuSensorType { Name = "GFX", Type = SensorType.Temperature, Scale = 1 } }, + { 154, new SmuSensorType { Name = "GFX", Type = SensorType.Clock, Scale = 1 } }, + { 156, new SmuSensorType { Name = "GFX", Type = SensorType.Load, Scale = 1 } }, + { 166, new SmuSensorType { Name = "Fabric", Type = SensorType.Clock, Scale = 1 } }, + { 177, new SmuSensorType { Name = "Uncore", Type = SensorType.Clock, Scale = 1 } }, + { 178, new SmuSensorType { Name = "Memory", Type = SensorType.Clock, Scale = 1 } }, + { 342, new SmuSensorType { Name = "Displays", Type = SensorType.Factor, Scale = 1 } }, + } + }, + { + // Zen 2. + 0x00240903, new Dictionary + { + { 15, new SmuSensorType { Name = "TDC", Type = SensorType.Current, Scale = 1}}, + { 21, new SmuSensorType { Name = "EDC", Type = SensorType.Current, Scale = 1}}, + { 48, new SmuSensorType { Name = "Fabric", Type = SensorType.Clock, Scale = 1} }, + { 50, new SmuSensorType { Name = "Uncore", Type = SensorType.Clock, Scale = 1} }, + { 51, new SmuSensorType { Name = "Memory", Type = SensorType.Clock, Scale = 1} }, + { 115, new SmuSensorType { Name = "SoC", Type = SensorType.Temperature, Scale = 1} }, + //{ 66, new SmuSensorType { Name = "Bus Speed", Type = SensorType.Clock, Scale = 1 } }, + //{ 188, new SmuSensorType { Name = "Core #1", Type = SensorType.Clock, Scale = 1000 } }, + //{ 189, new SmuSensorType { Name = "Core #2", Type = SensorType.Clock, Scale = 1000 } }, + //{ 190, new SmuSensorType { Name = "Core #3", Type = SensorType.Clock, Scale = 1000 } }, + //{ 191, new SmuSensorType { Name = "Core #4", Type = SensorType.Clock, Scale = 1000 } }, + //{ 192, new SmuSensorType { Name = "Core #5", Type = SensorType.Clock, Scale = 1000 } }, + //{ 193, new SmuSensorType { Name = "Core #6", Type = SensorType.Clock, Scale = 1000 } }, + } + } + }; + + private uint _argsAddr; + private uint _cmdAddr; + private uint _dramBaseAddr; + private uint _pmTableSize; + private uint _pmTableSizeAlt; + private uint _pmTableVersion; + private uint _rspAddr; + + public RyzenSMU(uint family, uint model, uint packageType) + { + _cpuCodeName = GetCpuCodeName(family, model, packageType); + + _supportedCPU = SetAddresses(_cpuCodeName); + + SetupPmTableAddrAndSize(); + } + + private static CpuCodeName GetCpuCodeName(uint family, uint model, uint packageType) + { + if (family == 0x17) + { + switch (model) + { + case 0x01: + { + return packageType == 7 ? CpuCodeName.Threadripper : CpuCodeName.SummitRidge; + } + case 0x08: + { + return packageType == 7 ? CpuCodeName.Colfax : CpuCodeName.PinnacleRidge; + } + case 0x11: + { + return CpuCodeName.RavenRidge; + } + case 0x18: + { + return packageType == 2 ? CpuCodeName.RavenRidge2 : CpuCodeName.Picasso; + } + case 0x20: + { + return CpuCodeName.Dali; + } + case 0x31: + { + return CpuCodeName.CastlePeak; + } + case 0x60: + { + return CpuCodeName.Renoir; + } + case 0x71: + { + return CpuCodeName.Matisse; + } + case 0x90: + { + return CpuCodeName.Vangogh; + } + default: + { + return CpuCodeName.Undefined; + } + } + } + + if (family == 0x19) + { + switch (model) + { + case 0x00: + { + return CpuCodeName.Milan; + } + case 0x20: + case 0x21: + { + return CpuCodeName.Vermeer; + } + case 0x40: + { + return CpuCodeName.Rembrandt; + } + case 0x50: + { + return CpuCodeName.Cezanne; + } + default: + { + return CpuCodeName.Undefined; + } + } + } + + return CpuCodeName.Undefined; + } + + public string GetReport() + { + StringBuilder r = new(); + + r.AppendLine("Ryzen SMU"); + r.AppendLine(); + r.AppendLine($" PM table version: 0x{_pmTableVersion:X8}"); + r.AppendLine($" PM table supported: {_supportedCPU}"); + r.AppendLine($" PM table layout defined: {IsPmTableLayoutDefined()}"); + + if (_supportedCPU) + { + r.AppendLine($" PM table size: 0x{_pmTableSize:X3}"); + r.AppendLine($" PM table start address: 0x{_dramBaseAddr:X8}"); + r.AppendLine(); + r.AppendLine(" PM table dump:"); + r.AppendLine(" Idx Offset Value"); + r.AppendLine(" ------------------------"); + + float[] pm_values = GetPmTable(); + for (int i = 0; i < pm_values.Length; i++) + { + r.AppendLine($" {i,4} 0x{i * 4:X3} {pm_values[i]}"); + } + } + + return r.ToString(); + } + + private bool SetAddresses(CpuCodeName codeName) + { + switch (codeName) + { + case CpuCodeName.CastlePeak: + case CpuCodeName.Matisse: + case CpuCodeName.Vermeer: + { + _cmdAddr = 0x3B10524; + _rspAddr = 0x3B10570; + _argsAddr = 0x3B10A40; + + return true; + } + case CpuCodeName.Colfax: + case CpuCodeName.SummitRidge: + case CpuCodeName.Threadripper: + case CpuCodeName.PinnacleRidge: + { + _cmdAddr = 0x3B1051C; + _rspAddr = 0x3B10568; + _argsAddr = 0x3B10590; + + return true; + } + case CpuCodeName.Renoir: + case CpuCodeName.Picasso: + case CpuCodeName.RavenRidge: + case CpuCodeName.RavenRidge2: + case CpuCodeName.Dali: + { + _cmdAddr = 0x3B10A20; + _rspAddr = 0x3B10A80; + _argsAddr = 0x3B10A88; + + return true; + } + default: + { + return false; + } + } + } + + public uint GetSmuVersion() + { + uint[] args = { 1 }; + + if (SendCommand(0x02, ref args)) + { + return args[0]; + } + + return 0; + } + + public Dictionary GetPmTableStructure() + { + if (!IsPmTableLayoutDefined()) + return new Dictionary(); + + + return _supportedPmTableVersions[_pmTableVersion]; + } + + public bool IsPmTableLayoutDefined() + { + return _supportedPmTableVersions.ContainsKey(_pmTableVersion); + } + + public float[] GetPmTable() + { + if (!_supportedCPU) + return new float[] { 0 }; + + if (!SetupPmTableAddrAndSize()) + return new float[] { 0 }; + + if (!TransferTableToDram()) + return new float[] { 0 }; + + + float[] table = ReadDramToArray(); + if (table[0] == 0) /* Fix for Zen+ empty values at first call bug */ + { + Thread.Sleep(10); + TransferTableToDram(); + table = ReadDramToArray(); + } + + return table; + } + + private float[] ReadDramToArray() + { + float[] table = new float[_pmTableSize / 4]; + + Ring0.ReadMemory(_dramBaseAddr, ref table); + + return table; + } + + private bool SetupPmTableAddrAndSize() + { + if (_pmTableSize == 0) + { + SetupPmTableSize(); + } + + if (_dramBaseAddr == 0) + { + SetupDramBaseAddr(); + } + + return _dramBaseAddr != 0 && _pmTableSize != 0; + } + + private void SetupPmTableSize() + { + if (!GetPmTableVersion(ref _pmTableVersion)) + return; + + + switch (_cpuCodeName) + { + case CpuCodeName.Matisse: + { + switch (_pmTableVersion) + { + case 0x240902: + { + _pmTableSize = 0x514; + break; + } + case 0x240903: + { + _pmTableSize = 0x518; + break; + } + case 0x240802: + { + _pmTableSize = 0x7E0; + break; + } + case 0x240803: + { + _pmTableSize = 0x7E4; + break; + } + default: + { + return; + } + } + + break; + } + case CpuCodeName.Vermeer: + { + switch (_pmTableVersion) + { + case 0x2D0903: + { + _pmTableSize = 0x594; + break; + } + case 0x380904: + { + _pmTableSize = 0x5A4; + break; + } + case 0x380905: + { + _pmTableSize = 0x5D0; + break; + } + case 0x2D0803: + { + _pmTableSize = 0x894; + break; + } + case 0x380804: + { + _pmTableSize = 0x8A4; + break; + } + case 0x380805: + { + _pmTableSize = 0x8F0; + break; + } + default: + { + return; + } + } + + break; + } + case CpuCodeName.Renoir: + { + switch (_pmTableVersion) + { + case 0x370000: + { + _pmTableSize = 0x794; + break; + } + case 0x370001: + { + _pmTableSize = 0x884; + break; + } + case 0x370002: + case 0x370003: + { + _pmTableSize = 0x88C; + break; + } + case 0x370004: + { + _pmTableSize = 0x8AC; + break; + } + case 0x370005: + { + _pmTableSize = 0x8C8; + break; + } + default: + { + return; + } + } + + break; + } + case CpuCodeName.Cezanne: + { + switch (_pmTableVersion) + { + case 0x400005: + { + _pmTableSize = 0x944; + break; + } + default: + { + return; + } + } + + break; + } + case CpuCodeName.Picasso: + case CpuCodeName.RavenRidge: + case CpuCodeName.RavenRidge2: + { + _pmTableSizeAlt = 0xA4; + _pmTableSize = 0x608 + _pmTableSizeAlt; + break; + } + default: + { + return; + } + } + } + + private bool GetPmTableVersion(ref uint version) + { + uint[] args = { 0 }; + uint fn; + + switch (_cpuCodeName) + { + case CpuCodeName.RavenRidge: + case CpuCodeName.Picasso: + { + fn = 0x0c; + break; + } + case CpuCodeName.Matisse: + case CpuCodeName.Vermeer: + { + fn = 0x08; + break; + } + case CpuCodeName.Renoir: + { + fn = 0x06; + break; + } + default: + { + return false; + } + } + + bool ret = SendCommand(fn, ref args); + version = args[0]; + + return ret; + } + + private void SetupAddrClass1(uint[] fn) + { + uint[] args = { 1, 1 }; + + bool command = SendCommand(fn[0], ref args); + if (!command) + return; + + + _dramBaseAddr = args[0] | (args[1] << 32); + } + + private void SetupAddrClass2(uint[] fn) + { + uint[] args = { 0, 0, 0, 0, 0, 0 }; + + bool command = SendCommand(fn[0], ref args); + if (!command) + return; + + + args = new uint[] { 0 }; + command = SendCommand(fn[1], ref args); + if (!command) + return; + + + _dramBaseAddr = args[0]; + } + + private void SetupAddrClass3(uint[] fn) + { + uint[] parts = { 0, 0 }; + + // == Part 1 == + uint[] args = { 3 }; + bool command = SendCommand(fn[0], ref args); + if (!command) + return; + + + args = new uint[] { 3 }; + command = SendCommand(fn[2], ref args); + if (!command) + return; + + + // 1st Base. + parts[0] = args[0]; + // == Part 1 End == + + // == Part 2 == + args = new uint[] { 3 }; + command = SendCommand(fn[1], ref args); + if (!command) + return; + + + args = new uint[] { 5 }; + command = SendCommand(fn[0], ref args); + if (!command) + return; + + + args = new uint[] { 5 }; + command = SendCommand(fn[2], ref args); + if (!command) + return; + + + // 2nd base. + parts[1] = args[0]; + // == Part 2 End == + + _dramBaseAddr = parts[0] & 0xFFFFFFFF; + } + + private void SetupDramBaseAddr() + { + uint[] fn = { 0, 0, 0 }; + + switch (_cpuCodeName) + { + case CpuCodeName.Vermeer: + case CpuCodeName.Matisse: + case CpuCodeName.CastlePeak: + { + fn[0] = 0x06; + SetupAddrClass1(fn); + + return; + } + case CpuCodeName.Renoir: + { + fn[0] = 0x66; + SetupAddrClass1(fn); + + return; + } + case CpuCodeName.Colfax: + case CpuCodeName.PinnacleRidge: + { + fn[0] = 0x0b; + fn[1] = 0x0c; + SetupAddrClass2(fn); + + return; + } + case CpuCodeName.Dali: + case CpuCodeName.Picasso: + case CpuCodeName.RavenRidge: + case CpuCodeName.RavenRidge2: + { + fn[0] = 0x0a; + fn[1] = 0x3d; + fn[2] = 0x0b; + SetupAddrClass3(fn); + + return; + } + default: + { + return; + } + } + } + + public bool TransferTableToDram() + { + uint[] args = { 0 }; + uint fn; + + switch (_cpuCodeName) + { + case CpuCodeName.Matisse: + case CpuCodeName.Vermeer: + { + fn = 0x05; + break; + } + case CpuCodeName.Renoir: + { + args[0] = 3; + fn = 0x65; + break; + } + case CpuCodeName.Picasso: + case CpuCodeName.RavenRidge: + case CpuCodeName.RavenRidge2: + { + args[0] = 3; + fn = 0x3d; + break; + } + default: + { + return false; + } + } + + return SendCommand(fn, ref args); + } + + private bool SendCommand(uint msg, ref uint[] args) + { + uint[] cmdArgs = new uint[SMU_REQ_MAX_ARGS]; + int argsLength = Math.Min(args.Length, cmdArgs.Length); + + for (int i = 0; i < argsLength; ++i) + cmdArgs[i] = args[i]; + + uint tmp = 0; + if (_mutex.WaitOne(5000)) + { + // Step 1: Wait until the RSP register is non-zero. + + tmp = 0; + uint retries = SMU_RETRIES_MAX; + do + { + if (!ReadReg(_rspAddr, ref tmp)) + { + _mutex.ReleaseMutex(); + return false; + } + } + while (tmp == 0 && 0 != retries--); + + // Step 1.b: A command is still being processed meaning a new command cannot be issued. + + if (retries == 0 && tmp == 0) + { + _mutex.ReleaseMutex(); + return false; + } + + // Step 2: Write zero (0) to the RSP register + WriteReg(_rspAddr, 0); + + // Step 3: Write the argument(s) into the argument register(s) + for (int i = 0; i < cmdArgs.Length; ++i) + WriteReg(_argsAddr + (uint)(i * 4), cmdArgs[i]); + + // Step 4: Write the message Id into the Message ID register + WriteReg(_cmdAddr, msg); + + // Step 5: Wait until the Response register is non-zero. + tmp = 0; + retries = SMU_RETRIES_MAX; + do + { + if (!ReadReg(_rspAddr, ref tmp)) + { + _mutex.ReleaseMutex(); + return false; + } + } + while (tmp == 0 && 0 != retries--); + + if (retries == 0 && tmp != (uint)Status.OK) + { + _mutex.ReleaseMutex(); + return false; + } + + // Step 6: If the Response register contains OK, then SMU has finished processing the message. + + args = new uint[SMU_REQ_MAX_ARGS]; + for (byte i = 0; i < SMU_REQ_MAX_ARGS; i++) + { + if (!ReadReg(_argsAddr + (uint)(i * 4), ref args[i])) + { + _mutex.ReleaseMutex(); + return false; + } + } + + ReadReg(_rspAddr, ref tmp); + _mutex.ReleaseMutex(); + } + + return tmp == (uint)Status.OK; + } + + private void WriteReg(uint addr, uint data) + { + if (Ring0.WaitPciBusMutex(10)) + { + if (Ring0.WritePciConfig(0x00, SMU_PCI_ADDR_REG, addr)) + { + Ring0.WritePciConfig(0x00, SMU_PCI_DATA_REG, data); + } + + Ring0.ReleasePciBusMutex(); + } + } + + private bool ReadReg(uint addr, ref uint data) + { + bool read = false; + + if (Ring0.WaitPciBusMutex(10)) + { + if (Ring0.WritePciConfig(0x00, SMU_PCI_ADDR_REG, addr)) + { + read = Ring0.ReadPciConfig(0x00, SMU_PCI_DATA_REG, out data); + } + + Ring0.ReleasePciBusMutex(); + } + + return read; + } + + public struct SmuSensorType + { + public string Name; + public SensorType Type; + public float Scale; + } + + private enum Status : uint + { + OK = 0x01, + Failed = 0xFF, + UnknownCmd = 0xFE, + CmdRejectedPrereq = 0xFD, + CmdRejectedBusy = 0xFC + } + + private enum CpuCodeName + { + Undefined, + Colfax, + Renoir, + Picasso, + Matisse, + Threadripper, + CastlePeak, + RavenRidge, + RavenRidge2, + SummitRidge, + PinnacleRidge, + Rembrandt, + Vermeer, + Vangogh, + Cezanne, + Milan, + Dali + } + } +} diff --git a/LibreHardwareMonitorLib/Hardware/WinRing0.sys b/LibreHardwareMonitorLib/Hardware/WinRing0.sys index feaea41..4657dfb 100644 Binary files a/LibreHardwareMonitorLib/Hardware/WinRing0.sys and b/LibreHardwareMonitorLib/Hardware/WinRing0.sys differ diff --git a/LibreHardwareMonitorLib/Hardware/WinRing0x64.sys b/LibreHardwareMonitorLib/Hardware/WinRing0x64.sys index 197c255..c3daa88 100644 Binary files a/LibreHardwareMonitorLib/Hardware/WinRing0x64.sys and b/LibreHardwareMonitorLib/Hardware/WinRing0x64.sys differ