first commit

This commit is contained in:
2025-04-07 07:44:27 -07:00
commit d6cde0c05e
512 changed files with 142392 additions and 0 deletions

View File

@@ -0,0 +1,161 @@
// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
// Copyright (C) LibreHardwareMonitor and Contributors.
// Partial Copyright (C) Michael Möller <mmoeller@openhardwaremonitor.org> and Contributors.
// All Rights Reserved.
using System;
using System.Globalization;
using System.Text;
using System.Threading;
namespace LibreHardwareMonitor.Hardware.Cpu;
internal sealed class Amd0FCpu : AmdCpu
{
private readonly Sensor _busClock;
private readonly Sensor[] _coreClocks;
private readonly Sensor[] _coreTemperatures;
private readonly uint _miscellaneousControlAddress;
private readonly byte _thermSenseCoreSelCPU0;
private readonly byte _thermSenseCoreSelCPU1;
public Amd0FCpu(int processorIndex, CpuId[][] cpuId, ISettings settings) : base(processorIndex, cpuId, settings)
{
float offset = -49.0f;
// AM2+ 65nm +21 offset
uint model = cpuId[0][0].Model;
if (model is >= 0x69 and not 0xc1 and not 0x6c and not 0x7c)
offset += 21;
if (model < 40)
{
// AMD Athlon 64 Processors
_thermSenseCoreSelCPU0 = 0x0;
_thermSenseCoreSelCPU1 = 0x4;
}
else
{
// AMD NPT Family 0Fh Revision F, G have the core selection swapped
_thermSenseCoreSelCPU0 = 0x4;
_thermSenseCoreSelCPU1 = 0x0;
}
// check if processor supports a digital thermal sensor
if (cpuId[0][0].ExtData.GetLength(0) > 7 && (cpuId[0][0].ExtData[7, 3] & 1) != 0)
{
_coreTemperatures = new Sensor[_coreCount];
for (int i = 0; i < _coreCount; i++)
{
_coreTemperatures[i] = new Sensor("Core #" + (i + 1),
i,
SensorType.Temperature,
this,
new[] { new ParameterDescription("Offset [°C]", "Temperature offset of the thermal sensor.\nTemperature = Value + Offset.", offset) },
settings);
}
}
else
{
_coreTemperatures = Array.Empty<Sensor>();
}
_miscellaneousControlAddress = GetPciAddress(MISCELLANEOUS_CONTROL_FUNCTION, MISCELLANEOUS_CONTROL_DEVICE_ID);
_busClock = new Sensor("Bus Speed", 0, SensorType.Clock, this, settings);
_coreClocks = new Sensor[_coreCount];
for (int i = 0; i < _coreClocks.Length; i++)
{
_coreClocks[i] = new Sensor(CoreString(i), i + 1, SensorType.Clock, this, settings);
if (HasTimeStampCounter)
ActivateSensor(_coreClocks[i]);
}
Update();
}
protected override uint[] GetMsrs()
{
return new[] { FIDVID_STATUS };
}
public override string GetReport()
{
StringBuilder r = new();
r.Append(base.GetReport());
r.Append("Miscellaneous Control Address: 0x");
r.AppendLine(_miscellaneousControlAddress.ToString("X", CultureInfo.InvariantCulture));
r.AppendLine();
return r.ToString();
}
public override void Update()
{
base.Update();
if (Mutexes.WaitPciBus(10))
{
if (_miscellaneousControlAddress != Interop.Ring0.INVALID_PCI_ADDRESS)
{
for (uint i = 0; i < _coreTemperatures.Length; i++)
{
if (Ring0.WritePciConfig(_miscellaneousControlAddress,
THERMTRIP_STATUS_REGISTER,
i > 0 ? _thermSenseCoreSelCPU1 : _thermSenseCoreSelCPU0))
{
if (Ring0.ReadPciConfig(_miscellaneousControlAddress, THERMTRIP_STATUS_REGISTER, out uint value))
{
_coreTemperatures[i].Value = ((value >> 16) & 0xFF) + _coreTemperatures[i].Parameters[0].Value;
ActivateSensor(_coreTemperatures[i]);
}
else
{
DeactivateSensor(_coreTemperatures[i]);
}
}
}
}
Mutexes.ReleasePciBus();
}
if (HasTimeStampCounter)
{
double newBusClock = 0;
for (int i = 0; i < _coreClocks.Length; i++)
{
Thread.Sleep(1);
if (Ring0.ReadMsr(FIDVID_STATUS, out uint eax, out uint _, _cpuId[i][0].Affinity))
{
// CurrFID can be found in eax bits 0-5, MaxFID in 16-21
// 8-13 hold StartFID, we don't use that here.
double curMp = 0.5 * ((eax & 0x3F) + 8);
double maxMp = 0.5 * ((eax >> 16 & 0x3F) + 8);
_coreClocks[i].Value = (float)(curMp * TimeStampCounterFrequency / maxMp);
newBusClock = (float)(TimeStampCounterFrequency / maxMp);
}
else
{
// Fail-safe value - if the code above fails, we'll use this instead
_coreClocks[i].Value = (float)TimeStampCounterFrequency;
}
}
if (newBusClock > 0)
{
_busClock.Value = (float)newBusClock;
ActivateSensor(_busClock);
}
}
}
// ReSharper disable InconsistentNaming
private const uint FIDVID_STATUS = 0xC0010042;
private const ushort MISCELLANEOUS_CONTROL_DEVICE_ID = 0x1103;
private const byte MISCELLANEOUS_CONTROL_FUNCTION = 3;
private const uint THERMTRIP_STATUS_REGISTER = 0xE4;
// ReSharper restore InconsistentNaming
}

View File

@@ -0,0 +1,504 @@
// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
// Copyright (C) LibreHardwareMonitor and Contributors.
// Partial Copyright (C) Michael Möller <mmoeller@openhardwaremonitor.org> and Contributors.
// All Rights Reserved.
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Text;
using System.Threading;
namespace LibreHardwareMonitor.Hardware.Cpu;
internal sealed class Amd10Cpu : AmdCpu
{
private readonly Sensor _busClock;
private readonly Sensor[] _coreClocks;
private readonly Sensor _coreTemperature;
private readonly Sensor _coreVoltage;
private readonly byte _cStatesIoOffset;
private readonly Sensor[] _cStatesResidency;
private readonly bool _hasSmuTemperatureRegister;
private readonly bool _isSvi2;
private readonly uint _miscellaneousControlAddress;
private readonly Sensor _northbridgeVoltage;
private readonly FileStream _temperatureStream;
private readonly double _timeStampCounterMultiplier;
public Amd10Cpu(int processorIndex, CpuId[][] cpuId, ISettings settings) : base(processorIndex, cpuId, settings)
{
// AMD family 1Xh processors support only one temperature sensor
ushort miscellaneousControlDeviceId;
_coreTemperature = new Sensor("CPU Cores", 0, SensorType.Temperature, this, new[] { new ParameterDescription("Offset [°C]", "Temperature offset.", 0) }, settings);
_coreVoltage = new Sensor("CPU Cores", 0, SensorType.Voltage, this, settings);
ActivateSensor(_coreVoltage);
_northbridgeVoltage = new Sensor("Northbridge", 0, SensorType.Voltage, this, settings);
ActivateSensor(_northbridgeVoltage);
_isSvi2 = (_family == 0x15 && _model >= 0x10) || _family == 0x16;
switch (_family)
{
case 0x10:
miscellaneousControlDeviceId = FAMILY_10H_MISCELLANEOUS_CONTROL_DEVICE_ID;
break;
case 0x11:
miscellaneousControlDeviceId = FAMILY_11H_MISCELLANEOUS_CONTROL_DEVICE_ID;
break;
case 0x12:
miscellaneousControlDeviceId = FAMILY_12H_MISCELLANEOUS_CONTROL_DEVICE_ID;
break;
case 0x14:
miscellaneousControlDeviceId = FAMILY_14H_MISCELLANEOUS_CONTROL_DEVICE_ID;
break;
case 0x15:
switch (_model & 0xF0)
{
case 0x00:
miscellaneousControlDeviceId = FAMILY_15H_MODEL_00_MISC_CONTROL_DEVICE_ID;
break;
case 0x10:
miscellaneousControlDeviceId = FAMILY_15H_MODEL_10_MISC_CONTROL_DEVICE_ID;
break;
case 0x30:
miscellaneousControlDeviceId = FAMILY_15H_MODEL_30_MISC_CONTROL_DEVICE_ID;
break;
case 0x70:
miscellaneousControlDeviceId = FAMILY_15H_MODEL_70_MISC_CONTROL_DEVICE_ID;
_hasSmuTemperatureRegister = true;
break;
case 0x60:
miscellaneousControlDeviceId = FAMILY_15H_MODEL_60_MISC_CONTROL_DEVICE_ID;
_hasSmuTemperatureRegister = true;
break;
default:
miscellaneousControlDeviceId = 0;
break;
}
break;
case 0x16:
miscellaneousControlDeviceId = (_model & 0xF0) switch
{
0x00 => FAMILY_16H_MODEL_00_MISC_CONTROL_DEVICE_ID,
0x30 => FAMILY_16H_MODEL_30_MISC_CONTROL_DEVICE_ID,
_ => 0
};
break;
default:
miscellaneousControlDeviceId = 0;
break;
}
// get the pci address for the Miscellaneous Control registers
_miscellaneousControlAddress = GetPciAddress(MISCELLANEOUS_CONTROL_FUNCTION, miscellaneousControlDeviceId);
_busClock = new Sensor("Bus Speed", 0, SensorType.Clock, this, settings);
_coreClocks = new Sensor[_coreCount];
for (int i = 0; i < _coreClocks.Length; i++)
{
_coreClocks[i] = new Sensor(CoreString(i), i + 1, SensorType.Clock, this, settings);
if (HasTimeStampCounter)
ActivateSensor(_coreClocks[i]);
}
bool corePerformanceBoostSupport = (cpuId[0][0].ExtData[7, 3] & (1 << 9)) > 0;
// set affinity to the first thread for all frequency estimations
GroupAffinity previousAffinity = ThreadAffinity.Set(cpuId[0][0].Affinity);
// disable core performance boost
Ring0.ReadMsr(HWCR, out uint hwcrEax, out uint hwcrEdx);
if (corePerformanceBoostSupport)
Ring0.WriteMsr(HWCR, hwcrEax | (1 << 25), hwcrEdx);
Ring0.ReadMsr(PERF_CTL_0, out uint ctlEax, out uint ctlEdx);
Ring0.ReadMsr(PERF_CTR_0, out uint ctrEax, out uint ctrEdx);
_timeStampCounterMultiplier = EstimateTimeStampCounterMultiplier();
// restore the performance counter registers
Ring0.WriteMsr(PERF_CTL_0, ctlEax, ctlEdx);
Ring0.WriteMsr(PERF_CTR_0, ctrEax, ctrEdx);
// restore core performance boost
if (corePerformanceBoostSupport)
Ring0.WriteMsr(HWCR, hwcrEax, hwcrEdx);
// restore the thread affinity.
ThreadAffinity.Set(previousAffinity);
// the file reader for lm-sensors support on Linux
_temperatureStream = null;
if (Software.OperatingSystem.IsUnix)
{
foreach (string path in Directory.GetDirectories("/sys/class/hwmon/"))
{
string name = null;
try
{
using StreamReader reader = new(path + "/device/name");
name = reader.ReadLine();
}
catch (IOException)
{ }
_temperatureStream = name switch
{
"k10temp" => new FileStream(path + "/device/temp1_input", FileMode.Open, FileAccess.Read, FileShare.ReadWrite),
_ => _temperatureStream
};
}
}
uint addr = Ring0.GetPciAddress(0, 20, 0);
if (Ring0.ReadPciConfig(addr, 0, out uint dev))
{
Ring0.ReadPciConfig(addr, 8, out uint rev);
_cStatesIoOffset = dev switch
{
0x43851002 => (byte)((rev & 0xFF) < 0x40 ? 0xB3 : 0x9C),
0x780B1022 or 0x790B1022 => 0x9C,
_ => _cStatesIoOffset
};
}
if (_cStatesIoOffset != 0)
{
_cStatesResidency = new[] { new Sensor("CPU Package C2", 0, SensorType.Level, this, settings), new Sensor("CPU Package C3", 1, SensorType.Level, this, settings) };
ActivateSensor(_cStatesResidency[0]);
ActivateSensor(_cStatesResidency[1]);
}
Update();
}
private double EstimateTimeStampCounterMultiplier()
{
// preload the function
EstimateTimeStampCounterMultiplier(0);
EstimateTimeStampCounterMultiplier(0);
// estimate the multiplier
List<double> estimate = new(3);
for (int i = 0; i < 3; i++)
estimate.Add(EstimateTimeStampCounterMultiplier(0.025));
estimate.Sort();
return estimate[1];
}
private double EstimateTimeStampCounterMultiplier(double timeWindow)
{
// select event "076h CPU Clocks not Halted" and enable the counter
Ring0.WriteMsr(PERF_CTL_0,
(1 << 22) | // enable performance counter
(1 << 17) | // count events in user mode
(1 << 16) | // count events in operating-system mode
0x76,
0x00000000);
// set the counter to 0
Ring0.WriteMsr(PERF_CTR_0, 0, 0);
long ticks = (long)(timeWindow * Stopwatch.Frequency);
long timeBegin = Stopwatch.GetTimestamp() +
(long)Math.Ceiling(0.001 * ticks);
long timeEnd = timeBegin + ticks;
while (Stopwatch.GetTimestamp() < timeBegin)
{ }
Ring0.ReadMsr(PERF_CTR_0, out uint lsbBegin, out uint msbBegin);
while (Stopwatch.GetTimestamp() < timeEnd)
{ }
Ring0.ReadMsr(PERF_CTR_0, out uint lsbEnd, out uint msbEnd);
Ring0.ReadMsr(COFVID_STATUS, out uint eax, out uint _);
double coreMultiplier = GetCoreMultiplier(eax);
ulong countBegin = ((ulong)msbBegin << 32) | lsbBegin;
ulong countEnd = ((ulong)msbEnd << 32) | lsbEnd;
double coreFrequency = 1e-6 * ((double)(countEnd - countBegin) * Stopwatch.Frequency) / (timeEnd - timeBegin);
double busFrequency = coreFrequency / coreMultiplier;
return 0.25 * Math.Round(4 * TimeStampCounterFrequency / busFrequency);
}
protected override uint[] GetMsrs()
{
return new[] { PERF_CTL_0, PERF_CTR_0, HWCR, P_STATE_0, COFVID_STATUS };
}
public override string GetReport()
{
StringBuilder r = new();
r.Append(base.GetReport());
r.Append("Miscellaneous Control Address: 0x");
r.AppendLine(_miscellaneousControlAddress.ToString("X", CultureInfo.InvariantCulture));
r.Append("Time Stamp Counter Multiplier: ");
r.AppendLine(_timeStampCounterMultiplier.ToString(CultureInfo.InvariantCulture));
if (_family == 0x14)
{
Ring0.ReadPciConfig(_miscellaneousControlAddress, CLOCK_POWER_TIMING_CONTROL_0_REGISTER, out uint value);
r.Append("PCI Register D18F3xD4: ");
r.AppendLine(value.ToString("X8", CultureInfo.InvariantCulture));
}
r.AppendLine();
return r.ToString();
}
private double GetCoreMultiplier(uint cofVidEax)
{
uint cpuDid;
uint cpuFid;
switch (_family)
{
case 0x10:
case 0x11:
case 0x15:
case 0x16:
// 8:6 CpuDid: current core divisor ID
// 5:0 CpuFid: current core frequency ID
cpuDid = (cofVidEax >> 6) & 7;
cpuFid = cofVidEax & 0x1F;
return 0.5 * (cpuFid + 0x10) / (1 << (int)cpuDid);
case 0x12:
// 8:4 CpuFid: current CPU core frequency ID
// 3:0 CpuDid: current CPU core divisor ID
cpuFid = (cofVidEax >> 4) & 0x1F;
cpuDid = cofVidEax & 0xF;
double divisor = cpuDid switch
{
0 => 1,
1 => 1.5,
2 => 2,
3 => 3,
4 => 4,
5 => 6,
6 => 8,
7 => 12,
8 => 16,
_ => 1
};
return (cpuFid + 0x10) / divisor;
case 0x14:
// 8:4: current CPU core divisor ID most significant digit
// 3:0: current CPU core divisor ID least significant digit
uint divisorIdMsd = (cofVidEax >> 4) & 0x1F;
uint divisorIdLsd = cofVidEax & 0xF;
Ring0.ReadPciConfig(_miscellaneousControlAddress, CLOCK_POWER_TIMING_CONTROL_0_REGISTER, out uint value);
uint frequencyId = value & 0x1F;
return (frequencyId + 0x10) / (divisorIdMsd + (divisorIdLsd * 0.25) + 1);
default:
return 1;
}
}
private static string ReadFirstLine(Stream stream)
{
StringBuilder stringBuilder = new();
try
{
stream.Seek(0, SeekOrigin.Begin);
int b = stream.ReadByte();
while (b is not -1 and not 10)
{
stringBuilder.Append((char)b);
b = stream.ReadByte();
}
}
catch
{ }
return stringBuilder.ToString();
}
public override void Update()
{
base.Update();
if (_temperatureStream == null)
{
if (_miscellaneousControlAddress != Interop.Ring0.INVALID_PCI_ADDRESS)
{
bool isValueValid = _hasSmuTemperatureRegister
? ReadSmuRegister(SMU_REPORTED_TEMP_CTRL_OFFSET, out uint value)
: Ring0.ReadPciConfig(_miscellaneousControlAddress, REPORTED_TEMPERATURE_CONTROL_REGISTER, out value);
if (isValueValid)
{
if ((_family == 0x15 || _family == 0x16) && (value & 0x30000) == 0x3000)
{
if (_family == 0x15 && (_model & 0xF0) == 0x00)
{
_coreTemperature.Value = (((value >> 21) & 0x7FC) / 8.0f) + _coreTemperature.Parameters[0].Value - 49;
}
else
{
_coreTemperature.Value = (((value >> 21) & 0x7FF) / 8.0f) + _coreTemperature.Parameters[0].Value - 49;
}
}
else
{
_coreTemperature.Value = (((value >> 21) & 0x7FF) / 8.0f) + _coreTemperature.Parameters[0].Value;
}
ActivateSensor(_coreTemperature);
}
else
{
DeactivateSensor(_coreTemperature);
}
}
else
{
DeactivateSensor(_coreTemperature);
}
}
else
{
string s = ReadFirstLine(_temperatureStream);
try
{
_coreTemperature.Value = 0.001f * long.Parse(s, CultureInfo.InvariantCulture);
ActivateSensor(_coreTemperature);
}
catch
{
DeactivateSensor(_coreTemperature);
}
}
if (HasTimeStampCounter)
{
double newBusClock = 0;
float maxCoreVoltage = 0, maxNbVoltage = 0;
for (int i = 0; i < _coreClocks.Length; i++)
{
Thread.Sleep(1);
if (Ring0.ReadMsr(COFVID_STATUS, out uint curEax, out uint _, _cpuId[i][0].Affinity))
{
double multiplier = GetCoreMultiplier(curEax);
_coreClocks[i].Value = (float)(multiplier * TimeStampCounterFrequency / _timeStampCounterMultiplier);
newBusClock = (float)(TimeStampCounterFrequency / _timeStampCounterMultiplier);
}
else
{
_coreClocks[i].Value = (float)TimeStampCounterFrequency;
}
float SVI2Volt(uint vid) => vid < 0b1111_1000 ? 1.5500f - (0.00625f * vid) : 0;
float SVI1Volt(uint vid) => vid < 0x7C ? 1.550f - (0.0125f * vid) : 0;
float newCoreVoltage, newNbVoltage;
uint coreVid60 = (curEax >> 9) & 0x7F;
if (_isSvi2)
{
newCoreVoltage = SVI2Volt((curEax >> 13 & 0x80) | coreVid60);
newNbVoltage = SVI2Volt(curEax >> 24);
}
else
{
newCoreVoltage = SVI1Volt(coreVid60);
newNbVoltage = SVI1Volt(curEax >> 25);
}
if (newCoreVoltage > maxCoreVoltage)
maxCoreVoltage = newCoreVoltage;
if (newNbVoltage > maxNbVoltage)
maxNbVoltage = newNbVoltage;
}
_coreVoltage.Value = maxCoreVoltage;
_northbridgeVoltage.Value = maxNbVoltage;
if (newBusClock > 0)
{
_busClock.Value = (float)newBusClock;
ActivateSensor(_busClock);
}
}
if (_cStatesResidency != null)
{
for (int i = 0; i < _cStatesResidency.Length; i++)
{
Ring0.WriteIoPort(CSTATES_IO_PORT, (byte)(_cStatesIoOffset + i));
_cStatesResidency[i].Value = Ring0.ReadIoPort(CSTATES_IO_PORT + 1) / 256f * 100;
}
}
}
private static bool ReadSmuRegister(uint address, out uint value)
{
if (Mutexes.WaitPciBus(10))
{
if (!Ring0.WritePciConfig(0, 0xB8, address))
{
value = 0;
Mutexes.ReleasePciBus();
return false;
}
bool result = Ring0.ReadPciConfig(0, 0xBC, out value);
Mutexes.ReleasePciBus();
return result;
}
value = 0;
return false;
}
public override void Close()
{
_temperatureStream?.Close();
base.Close();
}
// ReSharper disable InconsistentNaming
private const uint CLOCK_POWER_TIMING_CONTROL_0_REGISTER = 0xD4;
private const uint COFVID_STATUS = 0xC0010071;
private const uint CSTATES_IO_PORT = 0xCD6;
private const uint SMU_REPORTED_TEMP_CTRL_OFFSET = 0xD8200CA4;
private const uint HWCR = 0xC0010015;
private const byte MISCELLANEOUS_CONTROL_FUNCTION = 3;
private const uint P_STATE_0 = 0xC0010064;
private const uint PERF_CTL_0 = 0xC0010000;
private const uint PERF_CTR_0 = 0xC0010004;
private const uint REPORTED_TEMPERATURE_CONTROL_REGISTER = 0xA4;
private const ushort FAMILY_10H_MISCELLANEOUS_CONTROL_DEVICE_ID = 0x1203;
private const ushort FAMILY_11H_MISCELLANEOUS_CONTROL_DEVICE_ID = 0x1303;
private const ushort FAMILY_12H_MISCELLANEOUS_CONTROL_DEVICE_ID = 0x1703;
private const ushort FAMILY_14H_MISCELLANEOUS_CONTROL_DEVICE_ID = 0x1703;
private const ushort FAMILY_15H_MODEL_00_MISC_CONTROL_DEVICE_ID = 0x1603;
private const ushort FAMILY_15H_MODEL_10_MISC_CONTROL_DEVICE_ID = 0x1403;
private const ushort FAMILY_15H_MODEL_30_MISC_CONTROL_DEVICE_ID = 0x141D;
private const ushort FAMILY_15H_MODEL_60_MISC_CONTROL_DEVICE_ID = 0x1573;
private const ushort FAMILY_15H_MODEL_70_MISC_CONTROL_DEVICE_ID = 0x15B3;
private const ushort FAMILY_16H_MODEL_00_MISC_CONTROL_DEVICE_ID = 0x1533;
private const ushort FAMILY_16H_MODEL_30_MISC_CONTROL_DEVICE_ID = 0x1583;
// ReSharper restore InconsistentNaming
}

View File

@@ -0,0 +1,604 @@
// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
// Copyright (C) LibreHardwareMonitor and Contributors.
// All Rights Reserved.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace LibreHardwareMonitor.Hardware.Cpu;
internal sealed class Amd17Cpu : AmdCpu
{
private readonly Processor _processor;
private readonly Dictionary<SensorType, int> _sensorTypeIndex;
private readonly RyzenSMU _smu;
public Amd17Cpu(int processorIndex, CpuId[][] cpuId, ISettings settings) : base(processorIndex, cpuId, settings)
{
_sensorTypeIndex = new Dictionary<SensorType, int>();
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);
// Add all numa nodes.
int coreId = 0;
int lastCoreId = -1; // Invalid id.
// Ryzen 3000's skip some core ids.
// So start at 1 and count upwards when the read core changes.
foreach (CpuId[] cpu in cpuId.OrderBy(x => x[0].ExtData[0x1e, 1] & 0xFF))
{
CpuId thread = cpu[0];
// CPUID_Fn8000001E_EBX, Register ..1E_1, [7:0]
// threads per core = CPUID_Fn8000001E_EBX[15:8] + 1
// CoreId: core ID = CPUID_Fn8000001E_EBX[7:0]
int coreIdRead = (int)(thread.ExtData[0x1e, 1] & 0xff);
// CPUID_Fn8000001E_ECX, Node Identifiers, Register ..1E_2
// NodesPerProcessor = CPUID_Fn8000001E_ECX[10:8]
// nodeID = CPUID_Fn8000001E_ECX[7:0]
int nodeId = (int)(thread.ExtData[0x1e, 2] & 0xff);
if (coreIdRead != lastCoreId)
{
coreId++;
}
lastCoreId = coreIdRead;
_processor.AppendThread(thread, nodeId, coreId);
}
Update();
}
protected override uint[] GetMsrs()
{
return new[] { PERF_CTL_0, PERF_CTR_0, HWCR, MSR_PSTATE_0, COFVID_STATUS };
}
public override string GetReport()
{
StringBuilder r = new();
r.Append(base.GetReport());
r.Append(_smu.GetReport());
return r.ToString();
}
public override void Update()
{
base.Update();
_processor.UpdateSensors();
foreach (NumaNode node in _processor.Nodes)
{
NumaNode.UpdateSensors();
foreach (Core c in node.Cores)
{
c.UpdateSensors();
}
}
}
private class Processor
{
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<KeyValuePair<uint, RyzenSMU.SmuSensorType>, Sensor> _smuSensors = new();
private readonly Sensor _socVoltage;
private Sensor _ccdsAverageTemperature;
private Sensor _ccdsMaxTemperature;
private DateTime _lastPwrTime = new(0);
private uint _lastPwrValue;
public Processor(Hardware hardware)
{
_cpu = (Amd17Cpu)hardware;
_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._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<uint, RyzenSMU.SmuSensorType> 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<NumaNode> Nodes { get; } = new();
public void UpdateSensors()
{
NumaNode node = Nodes[0];
Core core = node?.Cores[0];
CpuId cpuId = core?.Threads[0];
if (cpuId == null)
return;
GroupAffinity previousAffinity = ThreadAffinity.Set(cpuId.Affinity);
// MSRC001_0299
// TU [19:16]
// ESU [12:8] -> Unit 15.3 micro Joule per increment
// PU [3:0]
Ring0.ReadMsr(MSR_PWR_UNIT, out uint _, out uint _);
// MSRC001_029B
// total_energy [31:0]
DateTime sampleTime = DateTime.Now;
Ring0.ReadMsr(MSR_PKG_ENERGY_STAT, out uint eax, out _);
uint totalEnergy = eax;
uint smuSvi0Tfn = 0;
uint smuSvi0TelPlane0 = 0;
uint smuSvi0TelPlane1 = 0;
if (Mutexes.WaitPciBus(10))
{
// THM_TCON_CUR_TMP
// CUR_TEMP [31:21]
Ring0.WritePciConfig(0x00, FAMILY_17H_PCI_CONTROL_REGISTER, F17H_M01H_THM_TCON_CUR_TMP);
Ring0.ReadPciConfig(0x00, FAMILY_17H_PCI_CONTROL_REGISTER + 4, out uint temperature);
// SVI0_TFN_PLANE0 [0]
// SVI0_TFN_PLANE1 [1]
Ring0.WritePciConfig(0x00, FAMILY_17H_PCI_CONTROL_REGISTER, F17H_M01H_SVI + 0x8);
Ring0.ReadPciConfig(0x00, FAMILY_17H_PCI_CONTROL_REGISTER + 4, out smuSvi0Tfn);
bool supportsPerCcdTemperatures = false;
// TODO: find a better way because these will probably keep changing in the future.
uint sviPlane0Offset;
uint sviPlane1Offset;
switch (cpuId.Model)
{
case 0x31: // Threadripper 3000.
sviPlane0Offset = F17H_M01H_SVI + 0x14;
sviPlane1Offset = F17H_M01H_SVI + 0x10;
supportsPerCcdTemperatures = true;
break;
case 0x71: // Zen 2.
case 0x21: // Zen 3.
sviPlane0Offset = F17H_M01H_SVI + 0x10;
sviPlane1Offset = F17H_M01H_SVI + 0xC;
supportsPerCcdTemperatures = true;
break;
case 0x61: //Zen 4
case 0x44: //Zen 5
sviPlane0Offset = F17H_M01H_SVI + 0x10;
sviPlane1Offset = F17H_M01H_SVI + 0xC;
supportsPerCcdTemperatures = true;
break;
default: // Zen and Zen+.
sviPlane0Offset = F17H_M01H_SVI + 0xC;
sviPlane1Offset = F17H_M01H_SVI + 0x10;
break;
}
// SVI0_PLANE0_VDDCOR [24:16]
// SVI0_PLANE0_IDDCOR [7:0]
Ring0.WritePciConfig(0x00, FAMILY_17H_PCI_CONTROL_REGISTER, sviPlane0Offset);
Ring0.ReadPciConfig(0x00, FAMILY_17H_PCI_CONTROL_REGISTER + 4, out smuSvi0TelPlane0);
// SVI0_PLANE1_VDDCOR [24:16]
// SVI0_PLANE1_IDDCOR [7:0]
Ring0.WritePciConfig(0x00, FAMILY_17H_PCI_CONTROL_REGISTER, sviPlane1Offset);
Ring0.ReadPciConfig(0x00, FAMILY_17H_PCI_CONTROL_REGISTER + 4, out smuSvi0TelPlane1);
ThreadAffinity.Set(previousAffinity);
// power consumption
// power.Value = (float) ((double)pu * 0.125);
// esu = 15.3 micro Joule per increment
if (_lastPwrTime.Ticks == 0)
{
_lastPwrTime = sampleTime;
_lastPwrValue = totalEnergy;
}
// ticks diff
TimeSpan time = sampleTime - _lastPwrTime;
long pwr;
if (_lastPwrValue <= totalEnergy)
pwr = totalEnergy - _lastPwrValue;
else
pwr = (0xffffffff - _lastPwrValue) + totalEnergy;
// update for next sample
_lastPwrTime = sampleTime;
_lastPwrValue = totalEnergy;
double energy = 15.3e-6 * pwr;
energy /= time.TotalSeconds;
if (!double.IsNaN(energy))
_packagePower.Value = (float)energy;
// current temp Bit [31:21]
// If bit 19 of the Temperature Control register is set, there is an additional offset of 49 degrees C.
bool tempOffsetFlag = (temperature & F17H_TEMP_OFFSET_FLAG) != 0;
temperature = (temperature >> 21) * 125;
float offset = 0.0f;
// Offset table: https://github.com/torvalds/linux/blob/master/drivers/hwmon/k10temp.c#L78
if (string.IsNullOrWhiteSpace(cpuId.Name))
offset = 0;
else if (cpuId.Name.Contains("1600X") || cpuId.Name.Contains("1700X") || cpuId.Name.Contains("1800X"))
offset = -20.0f;
else if (cpuId.Name.Contains("Threadripper 19") || cpuId.Name.Contains("Threadripper 29"))
offset = -27.0f;
else if (cpuId.Name.Contains("2700X"))
offset = -10.0f;
float t = temperature * 0.001f;
if (tempOffsetFlag)
t += -49.0f;
if (offset < 0)
{
_coreTemperatureTctl.Value = t;
_coreTemperatureTdie.Value = t + offset;
_cpu.ActivateSensor(_coreTemperatureTctl);
_cpu.ActivateSensor(_coreTemperatureTdie);
}
else
{
// Zen 2 doesn't have an offset so Tdie and Tctl are the same.
_coreTemperatureTctlTdie.Value = t;
_cpu.ActivateSensor(_coreTemperatureTctlTdie);
}
// Tested only on R5 3600 & Threadripper 3960X, 5900X, 7900X
if (supportsPerCcdTemperatures)
{
for (uint i = 0; i < _ccdTemperatures.Length; i++)
{
if (cpuId.Model is 0x61 or 0x44) // Raphael or GraniteRidge
Ring0.WritePciConfig(0x00, FAMILY_17H_PCI_CONTROL_REGISTER, F17H_M61H_CCD1_TEMP + (i * 0x4));
else
Ring0.WritePciConfig(0x00, FAMILY_17H_PCI_CONTROL_REGISTER, F17H_M70H_CCD1_TEMP + (i * 0x4));
Ring0.ReadPciConfig(0x00, FAMILY_17H_PCI_CONTROL_REGISTER + 4, out uint ccdRawTemp);
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 (_ccdTemperatures[i] == null)
{
_cpu.ActivateSensor(_ccdTemperatures[i] = new Sensor($"CCD{i + 1} (Tdie)",
_cpu._sensorTypeIndex[SensorType.Temperature]++,
SensorType.Temperature,
_cpu,
_cpu._settings));
}
_ccdTemperatures[i].Value = ccdTemp;
}
}
Sensor[] activeCcds = _ccdTemperatures.Where(x => x != null).ToArray();
if (activeCcds.Length > 1)
{
// No need to get the max / average ccds temp if there is only one CCD.
if (_ccdsMaxTemperature == null)
{
_cpu.ActivateSensor(_ccdsMaxTemperature = new Sensor("CCDs Max (Tdie)",
_cpu._sensorTypeIndex[SensorType.Temperature]++,
SensorType.Temperature,
_cpu,
_cpu._settings));
}
if (_ccdsAverageTemperature == null)
{
_cpu.ActivateSensor(_ccdsAverageTemperature = new Sensor("CCDs Average (Tdie)",
_cpu._sensorTypeIndex[SensorType.Temperature]++,
SensorType.Temperature,
_cpu,
_cpu._settings));
}
_ccdsMaxTemperature.Value = activeCcds.Max(x => x.Value);
_ccdsAverageTemperature.Value = activeCcds.Average(x => x.Value);
}
}
Mutexes.ReleasePciBus();
}
// voltage
const double vidStep = 0.00625;
double vcc;
uint svi0PlaneXVddCor;
if (cpuId.Model is 0x61 or 0x44) // Readout not working for Ryzen 7000/9000.
smuSvi0Tfn |= 0x01 | 0x02;
// Core (0x01).
if ((smuSvi0Tfn & 0x01) == 0)
{
svi0PlaneXVddCor = (smuSvi0TelPlane0 >> 16) & 0xff;
vcc = 1.550 - (vidStep * svi0PlaneXVddCor);
_coreVoltage.Value = (float)vcc;
_cpu.ActivateSensor(_coreVoltage);
}
// SoC (0x02), not every Zen cpu has this voltage.
if (cpuId.Model is 0x11 or 0x21 or 0x71 or 0x31 || (smuSvi0Tfn & 0x02) == 0)
{
svi0PlaneXVddCor = (smuSvi0TelPlane1 >> 16) & 0xff;
vcc = 1.550 - (vidStep * svi0PlaneXVddCor);
_socVoltage.Value = (float)vcc;
_cpu.ActivateSensor(_socVoltage);
}
double timeStampCounterMultiplier = GetTimeStampCounterMultiplier();
if (timeStampCounterMultiplier > 0)
{
_busClock.Value = (float)(_cpu.TimeStampCounterFrequency / timeStampCounterMultiplier);
_cpu.ActivateSensor(_busClock);
}
if (_cpu._smu.IsPmTableLayoutDefined())
{
float[] smuData = _cpu._smu.GetPmTable();
foreach (KeyValuePair<KeyValuePair<uint, RyzenSMU.SmuSensorType>, Sensor> 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()
{
Ring0.ReadMsr(MSR_PSTATE_0, out uint eax, out _);
uint cpuDfsId = (eax >> 8) & 0x3f;
uint cpuFid = eax & 0xff;
return 2.0 * cpuFid / cpuDfsId;
}
public void AppendThread(CpuId thread, int numaId, int coreId)
{
NumaNode node = null;
foreach (NumaNode n in Nodes)
{
if (n.NodeId == numaId)
{
node = n;
break;
}
}
if (node == null)
{
node = new NumaNode(_cpu, numaId);
Nodes.Add(node);
}
if (thread != null)
node.AppendThread(thread, coreId);
}
}
private class NumaNode
{
private readonly Amd17Cpu _cpu;
public NumaNode(Amd17Cpu cpu, int id)
{
Cores = new List<Core>();
NodeId = id;
_cpu = cpu;
}
public List<Core> Cores { get; }
public int NodeId { get; }
public void AppendThread(CpuId thread, int coreId)
{
Core core = null;
foreach (Core c in Cores)
{
if (c.CoreId == coreId)
core = c;
}
if (core == null)
{
core = new Core(_cpu, coreId);
Cores.Add(core);
}
if (thread != null)
core.Threads.Add(thread);
}
public static void UpdateSensors()
{ }
}
private class Core
{
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(0);
private uint _lastPwrValue;
public Core(Amd17Cpu cpu, int id)
{
_cpu = cpu;
Threads = new List<CpuId>();
CoreId = id;
_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);
cpu.ActivateSensor(_power);
cpu.ActivateSensor(_vcore);
}
public int CoreId { get; }
public List<CpuId> Threads { get; }
public void UpdateSensors()
{
// CPUID cpu = threads.FirstOrDefault();
CpuId cpu = Threads[0];
if (cpu == null)
return;
GroupAffinity previousAffinity = ThreadAffinity.Set(cpu.Affinity);
// MSRC001_0299
// TU [19:16]
// ESU [12:8] -> Unit 15.3 micro Joule per increment
// PU [3:0]
Ring0.ReadMsr(MSR_PWR_UNIT, out _, out _);
// MSRC001_029A
// total_energy [31:0]
DateTime sampleTime = DateTime.Now;
Ring0.ReadMsr(MSR_CORE_ENERGY_STAT, out uint eax, out _);
uint totalEnergy = eax;
// MSRC001_0293
// CurHwPstate [24:22]
// CurCpuVid [21:14]
// CurCpuDfsId [13:8]
// CurCpuFid [7:0]
Ring0.ReadMsr(MSR_HARDWARE_PSTATE_STATUS, out eax, out _);
int curCpuVid = (int)((eax >> 14) & 0xff);
int curCpuDfsId = (int)((eax >> 8) & 0x3f);
int curCpuFid = (int)(eax & 0xff);
// MSRC001_0064 + x
// IddDiv [31:30]
// IddValue [29:22]
// CpuVid [21:14]
// CpuDfsId [13:8]
// CpuFid [7:0]
// Ring0.ReadMsr(MSR_PSTATE_0 + (uint)CurHwPstate, out eax, out edx);
// int IddDiv = (int)((eax >> 30) & 0x03);
// int IddValue = (int)((eax >> 22) & 0xff);
// int CpuVid = (int)((eax >> 14) & 0xff);
ThreadAffinity.Set(previousAffinity);
// clock
// CoreCOF is (Core::X86::Msr::PStateDef[CpuFid[7:0]] / Core::X86::Msr::PStateDef[CpuDfsId]) * 200
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.Value = (float)(curCpuFid / (double)curCpuDfsId * clock);
// multiplier
_multiplier.Value = (float)(curCpuFid / (double)curCpuDfsId * 2.0);
// Voltage
const double vidStep = 0.00625;
double vcc = 1.550 - (vidStep * curCpuVid);
_vcore.Value = (float)vcc;
// power consumption
// power.Value = (float) ((double)pu * 0.125);
// esu = 15.3 micro Joule per increment
if (_lastPwrTime.Ticks == 0)
{
_lastPwrTime = sampleTime;
_lastPwrValue = totalEnergy;
}
// ticks diff
TimeSpan time = sampleTime - _lastPwrTime;
long pwr;
if (_lastPwrValue <= totalEnergy)
pwr = totalEnergy - _lastPwrValue;
else
pwr = (0xffffffff - _lastPwrValue) + totalEnergy;
// update for next sample
_lastPwrTime = sampleTime;
_lastPwrValue = totalEnergy;
double energy = 15.3e-6 * pwr;
energy /= time.TotalSeconds;
if (!double.IsNaN(energy))
_power.Value = (float)energy;
}
}
// ReSharper disable InconsistentNaming
private const uint COFVID_STATUS = 0xC0010071;
private const uint F17H_M01H_SVI = 0x0005A000;
private const uint F17H_M01H_THM_TCON_CUR_TMP = 0x00059800;
private const uint F17H_M70H_CCD1_TEMP = 0x00059954;
private const uint F17H_M61H_CCD1_TEMP = 0x00059b08;
private const uint F17H_TEMP_OFFSET_FLAG = 0x80000;
private const uint FAMILY_17H_PCI_CONTROL_REGISTER = 0x60;
private const uint HWCR = 0xC0010015;
private const uint MSR_CORE_ENERGY_STAT = 0xC001029A;
private const uint MSR_HARDWARE_PSTATE_STATUS = 0xC0010293;
private const uint MSR_PKG_ENERGY_STAT = 0xC001029B;
private const uint MSR_PSTATE_0 = 0xC0010064;
private const uint MSR_PWR_UNIT = 0xC0010299;
private const uint PERF_CTL_0 = 0xC0010000;
private const uint PERF_CTR_0 = 0xC0010004;
// ReSharper restore InconsistentNaming
}

View File

@@ -0,0 +1,36 @@
// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
// Copyright (C) LibreHardwareMonitor and Contributors.
// Partial Copyright (C) Michael Möller <mmoeller@openhardwaremonitor.org> and Contributors.
// All Rights Reserved.
namespace LibreHardwareMonitor.Hardware.Cpu;
internal abstract class AmdCpu : GenericCpu
{
protected AmdCpu(int processorIndex, CpuId[][] cpuId, ISettings settings) : base(processorIndex, cpuId, settings)
{ }
protected uint GetPciAddress(byte function, ushort deviceId)
{
// assemble the pci address
uint address = Ring0.GetPciAddress(PCI_BUS, (byte)(PCI_BASE_DEVICE + Index), function);
// verify that we have the correct bus, device and function
if (!Ring0.ReadPciConfig(address, DEVICE_VENDOR_ID_REGISTER, out uint deviceVendor))
return Interop.Ring0.INVALID_PCI_ADDRESS;
if (deviceVendor != (deviceId << 16 | AMD_VENDOR_ID))
return Interop.Ring0.INVALID_PCI_ADDRESS;
return address;
}
// ReSharper disable InconsistentNaming
private const ushort AMD_VENDOR_ID = 0x1022;
private const byte DEVICE_VENDOR_ID_REGISTER = 0;
private const byte PCI_BASE_DEVICE = 0x18;
private const byte PCI_BUS = 0;
// ReSharper restore InconsistentNaming
}

View File

@@ -0,0 +1,211 @@
// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
// Copyright (C) LibreHardwareMonitor and Contributors.
// Partial Copyright (C) Michael Möller <mmoeller@openhardwaremonitor.org> and Contributors.
// All Rights Reserved.
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Text;
namespace LibreHardwareMonitor.Hardware.Cpu;
internal class CpuGroup : IGroup
{
private readonly List<GenericCpu> _hardware = new();
private readonly CpuId[][][] _threads;
public CpuGroup(ISettings settings)
{
CpuId[][] processorThreads = GetProcessorThreads();
_threads = new CpuId[processorThreads.Length][][];
int index = 0;
foreach (CpuId[] threads in processorThreads)
{
if (threads.Length == 0)
continue;
CpuId[][] coreThreads = GroupThreadsByCore(threads);
_threads[index] = coreThreads;
switch (threads[0].Vendor)
{
case Vendor.Intel:
_hardware.Add(new IntelCpu(index, coreThreads, settings));
break;
case Vendor.AMD:
switch (threads[0].Family)
{
case 0x0F:
_hardware.Add(new Amd0FCpu(index, coreThreads, settings));
break;
case 0x10:
case 0x11:
case 0x12:
case 0x14:
case 0x15:
case 0x16:
_hardware.Add(new Amd10Cpu(index, coreThreads, settings));
break;
case 0x17:
case 0x19:
case 0x1A:
_hardware.Add(new Amd17Cpu(index, coreThreads, settings));
break;
default:
_hardware.Add(new GenericCpu(index, coreThreads, settings));
break;
}
break;
default:
_hardware.Add(new GenericCpu(index, coreThreads, settings));
break;
}
index++;
}
}
public IReadOnlyList<IHardware> Hardware => _hardware;
public string GetReport()
{
if (_threads == null)
return null;
StringBuilder r = new();
r.AppendLine("CPUID");
r.AppendLine();
for (int i = 0; i < _threads.Length; i++)
{
r.AppendLine("Processor " + i);
r.AppendLine();
r.AppendFormat("Processor Vendor: {0}{1}", _threads[i][0][0].Vendor, Environment.NewLine);
r.AppendFormat("Processor Brand: {0}{1}", _threads[i][0][0].BrandString, Environment.NewLine);
r.AppendFormat("Family: 0x{0}{1}", _threads[i][0][0].Family.ToString("X", CultureInfo.InvariantCulture), Environment.NewLine);
r.AppendFormat("Model: 0x{0}{1}", _threads[i][0][0].Model.ToString("X", CultureInfo.InvariantCulture), Environment.NewLine);
r.AppendFormat("Stepping: 0x{0}{1}", _threads[i][0][0].Stepping.ToString("X", CultureInfo.InvariantCulture), Environment.NewLine);
r.AppendLine();
r.AppendLine("CPUID Return Values");
r.AppendLine();
for (int j = 0; j < _threads[i].Length; j++)
{
for (int k = 0; k < _threads[i][j].Length; k++)
{
r.AppendLine(" CPU Group: " + _threads[i][j][k].Group);
r.AppendLine(" CPU Thread: " + _threads[i][j][k].Thread);
r.AppendLine(" APIC ID: " + _threads[i][j][k].ApicId);
r.AppendLine(" Processor ID: " + _threads[i][j][k].ProcessorId);
r.AppendLine(" Core ID: " + _threads[i][j][k].CoreId);
r.AppendLine(" Thread ID: " + _threads[i][j][k].ThreadId);
r.AppendLine();
r.AppendLine(" Function EAX EBX ECX EDX");
AppendCpuidData(r, _threads[i][j][k].Data, CpuId.CPUID_0);
AppendCpuidData(r, _threads[i][j][k].ExtData, CpuId.CPUID_EXT);
r.AppendLine();
}
}
}
return r.ToString();
}
public void Close()
{
foreach (GenericCpu cpu in _hardware)
{
cpu.Close();
}
}
private static CpuId[][] GetProcessorThreads()
{
List<CpuId> threads = new();
for (int i = 0; i < ThreadAffinity.ProcessorGroupCount; i++)
{
for (int j = 0; j < 192; j++)
{
try
{
var cpuid = CpuId.Get(i, j);
if (cpuid != null)
threads.Add(cpuid);
}
catch (ArgumentOutOfRangeException)
{
// Continue...
}
}
}
SortedDictionary<uint, List<CpuId>> processors = new();
foreach (CpuId thread in threads)
{
processors.TryGetValue(thread.ProcessorId, out List<CpuId> list);
if (list == null)
{
list = new List<CpuId>();
processors.Add(thread.ProcessorId, list);
}
list.Add(thread);
}
CpuId[][] processorThreads = new CpuId[processors.Count][];
int index = 0;
foreach (List<CpuId> list in processors.Values)
{
processorThreads[index] = list.ToArray();
index++;
}
return processorThreads;
}
private static CpuId[][] GroupThreadsByCore(IEnumerable<CpuId> threads)
{
SortedDictionary<uint, List<CpuId>> cores = new();
foreach (CpuId thread in threads)
{
cores.TryGetValue(thread.CoreId, out List<CpuId> coreList);
if (coreList == null)
{
coreList = new List<CpuId>();
cores.Add(thread.CoreId, coreList);
}
coreList.Add(thread);
}
CpuId[][] coreThreads = new CpuId[cores.Count][];
int index = 0;
foreach (List<CpuId> list in cores.Values)
{
coreThreads[index] = list.ToArray();
index++;
}
return coreThreads;
}
private static void AppendCpuidData(StringBuilder r, uint[,] data, uint offset)
{
for (int i = 0; i < data.GetLength(0); i++)
{
r.Append(" ");
r.Append((i + offset).ToString("X8", CultureInfo.InvariantCulture));
for (int j = 0; j < 4; j++)
{
r.Append(" ");
r.Append(data[i, j].ToString("X8", CultureInfo.InvariantCulture));
}
r.AppendLine();
}
}
}

View File

@@ -0,0 +1,274 @@
// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
// Copyright (C) LibreHardwareMonitor and Contributors.
// Partial Copyright (C) Michael Möller <mmoeller@openhardwaremonitor.org> and Contributors.
// All Rights Reserved.
using System;
using System.Text;
namespace LibreHardwareMonitor.Hardware.Cpu;
public enum Vendor
{
Unknown,
Intel,
AMD
}
public class CpuId
{
/// <summary>
/// Initializes a new instance of the <see cref="CpuId" /> class.
/// </summary>
/// <param name="group">The group.</param>
/// <param name="thread">The thread.</param>
/// <param name="affinity">The affinity.</param>
private CpuId(int group, int thread, GroupAffinity affinity)
{
Thread = thread;
Group = group;
Affinity = affinity;
uint threadMaskWith;
uint coreMaskWith;
uint maxCpuidExt;
if (thread >= 64)
throw new ArgumentOutOfRangeException(nameof(thread));
uint maxCpuid;
if (OpCode.CpuId(CPUID_0, 0, out uint eax, out uint ebx, out uint ecx, out uint edx))
{
if (eax > 0)
maxCpuid = eax;
else
return;
StringBuilder vendorBuilder = new();
AppendRegister(vendorBuilder, ebx);
AppendRegister(vendorBuilder, edx);
AppendRegister(vendorBuilder, ecx);
Vendor = vendorBuilder.ToString() switch
{
"GenuineIntel" => Vendor.Intel,
"AuthenticAMD" => Vendor.AMD,
_ => Vendor.Unknown
};
if (OpCode.CpuId(CPUID_EXT, 0, out eax, out _, out _, out _))
{
if (eax > CPUID_EXT)
maxCpuidExt = eax - CPUID_EXT;
else
return;
}
else
{
throw new ArgumentOutOfRangeException(nameof(thread));
}
}
else
{
throw new ArgumentOutOfRangeException(nameof(thread));
}
maxCpuid = Math.Min(maxCpuid, 1024);
maxCpuidExt = Math.Min(maxCpuidExt, 1024);
Data = new uint[maxCpuid + 1, 4];
for (uint i = 0; i < maxCpuid + 1; i++)
{
OpCode.CpuId(CPUID_0 + i, 0, out Data[i, 0], out Data[i, 1], out Data[i, 2], out Data[i, 3]);
}
ExtData = new uint[maxCpuidExt + 1, 4];
for (uint i = 0; i < maxCpuidExt + 1; i++)
{
OpCode.CpuId(CPUID_EXT + i, 0, out ExtData[i, 0], out ExtData[i, 1], out ExtData[i, 2], out ExtData[i, 3]);
}
StringBuilder nameBuilder = new();
for (uint i = 2; i <= 4; i++)
{
if (OpCode.CpuId(CPUID_EXT + i, 0, out eax, out ebx, out ecx, out edx))
{
AppendRegister(nameBuilder, eax);
AppendRegister(nameBuilder, ebx);
AppendRegister(nameBuilder, ecx);
AppendRegister(nameBuilder, edx);
}
}
nameBuilder.Replace('\0', ' ');
BrandString = nameBuilder.ToString().Trim();
nameBuilder.Replace("(R)", string.Empty);
nameBuilder.Replace("(TM)", string.Empty);
nameBuilder.Replace("(tm)", string.Empty);
nameBuilder.Replace("CPU", string.Empty);
nameBuilder.Replace("Dual-Core Processor", string.Empty);
nameBuilder.Replace("Triple-Core Processor", string.Empty);
nameBuilder.Replace("Quad-Core Processor", string.Empty);
nameBuilder.Replace("Six-Core Processor", string.Empty);
nameBuilder.Replace("Eight-Core Processor", string.Empty);
nameBuilder.Replace("64-Core Processor", string.Empty);
nameBuilder.Replace("32-Core Processor", string.Empty);
nameBuilder.Replace("24-Core Processor", string.Empty);
nameBuilder.Replace("16-Core Processor", string.Empty);
nameBuilder.Replace("12-Core Processor", string.Empty);
nameBuilder.Replace("8-Core Processor", string.Empty);
nameBuilder.Replace("6-Core Processor", string.Empty);
for (int i = 0; i < 10; i++)
nameBuilder.Replace(" ", " ");
Name = nameBuilder.ToString();
if (Name.Contains("@"))
Name = Name.Remove(Name.LastIndexOf('@'));
Name = Name.Trim();
Family = ((Data[1, 0] & 0x0FF00000) >> 20) + ((Data[1, 0] & 0x0F00) >> 8);
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)
{
case Vendor.Intel:
uint maxCoreAndThreadIdPerPackage = (Data[1, 1] >> 16) & 0xFF;
uint maxCoreIdPerPackage;
if (maxCpuid >= 4)
maxCoreIdPerPackage = ((Data[4, 0] >> 26) & 0x3F) + 1;
else
maxCoreIdPerPackage = 1;
threadMaskWith = NextLog2(maxCoreAndThreadIdPerPackage / maxCoreIdPerPackage);
coreMaskWith = NextLog2(maxCoreIdPerPackage);
break;
case Vendor.AMD:
uint corePerPackage;
if (maxCpuidExt >= 8)
corePerPackage = (ExtData[8, 2] & 0xFF) + 1;
else
corePerPackage = 1;
threadMaskWith = 0;
coreMaskWith = NextLog2(corePerPackage);
if (Family is 0x17 or 0x19)
{
// ApicIdCoreIdSize: APIC ID size.
// cores per DIE
// we need this for Ryzen 5 (4 cores, 8 threads) ans Ryzen 6 (6 cores, 12 threads)
// Ryzen 5: [core0][core1][dummy][dummy][core2][core3] (Core0 EBX = 00080800, Core2 EBX = 08080800)
coreMaskWith = ((ExtData[8, 2] >> 12) & 0xF) switch
{
0x04 => NextLog2(16), // Ryzen
0x05 => NextLog2(32), // Threadripper
0x06 => NextLog2(64), // Epic
_ => coreMaskWith
};
}
break;
default:
threadMaskWith = 0;
coreMaskWith = 0;
break;
}
ProcessorId = ApicId >> (int)(coreMaskWith + threadMaskWith);
CoreId = (ApicId >> (int)threadMaskWith) - (ProcessorId << (int)coreMaskWith);
ThreadId = ApicId - (ProcessorId << (int)(coreMaskWith + threadMaskWith)) - (CoreId << (int)threadMaskWith);
}
public GroupAffinity Affinity { get; }
public uint ApicId { get; }
public string BrandString { get; } = string.Empty;
public uint CoreId { get; }
public uint[,] Data { get; } = new uint[0, 0];
public uint[,] ExtData { get; } = new uint[0, 0];
public uint Family { get; }
public int Group { get; }
public uint Model { get; }
public string Name { get; } = string.Empty;
public uint PkgType { get; }
public uint ProcessorId { get; }
public uint Stepping { get; }
public int Thread { get; }
public uint ThreadId { get; }
public Vendor Vendor { get; } = Vendor.Unknown;
/// <summary>
/// Gets the specified <see cref="CpuId" />.
/// </summary>
/// <param name="group">The group.</param>
/// <param name="thread">The thread.</param>
/// <returns><see cref="CpuId" />.</returns>
public static CpuId Get(int group, int thread)
{
if (thread >= 64)
return null;
var affinity = GroupAffinity.Single((ushort)group, thread);
GroupAffinity previousAffinity = ThreadAffinity.Set(affinity);
if (previousAffinity == GroupAffinity.Undefined)
return null;
try
{
return new CpuId(group, thread, affinity);
}
finally
{
ThreadAffinity.Set(previousAffinity);
}
}
private static void AppendRegister(StringBuilder b, uint value)
{
b.Append((char)(value & 0xff));
b.Append((char)((value >> 8) & 0xff));
b.Append((char)((value >> 16) & 0xff));
b.Append((char)((value >> 24) & 0xff));
}
private static uint NextLog2(long x)
{
if (x <= 0)
return 0;
x--;
uint count = 0;
while (x > 0)
{
x >>= 1;
count++;
}
return count;
}
// ReSharper disable InconsistentNaming
public const uint CPUID_0 = 0;
public const uint CPUID_EXT = 0x80000000;
// ReSharper restore InconsistentNaming
}

View File

@@ -0,0 +1,184 @@
// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
// Copyright (C) LibreHardwareMonitor and Contributors.
// Partial Copyright (C) Michael Möller <mmoeller@openhardwaremonitor.org> and Contributors.
// All Rights Reserved.
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
namespace LibreHardwareMonitor.Hardware.Cpu;
internal class CpuLoad
{
private readonly float[] _threadLoads;
private long[] _idleTimes;
private float _totalLoad;
private long[] _totalTimes;
public CpuLoad(CpuId[][] cpuid)
{
_threadLoads = new float[cpuid.Sum(x => x.Length)];
_totalLoad = 0;
try
{
GetTimes(out _idleTimes, out _totalTimes);
}
catch (Exception)
{
_idleTimes = null;
_totalTimes = null;
}
if (_idleTimes != null)
IsAvailable = true;
}
public bool IsAvailable { get; }
private static bool GetTimes(out long[] idle, out long[] total)
{
return !Software.OperatingSystem.IsUnix ? GetWindowsTimes(out idle, out total) : GetUnixTimes(out idle, out total);
}
private static bool GetWindowsTimes(out long[] idle, out long[] total)
{
idle = null;
total = null;
//Query processor idle information
Interop.NtDll.SYSTEM_PROCESSOR_IDLE_INFORMATION[] idleInformation = new Interop.NtDll.SYSTEM_PROCESSOR_IDLE_INFORMATION[64];
int idleSize = Marshal.SizeOf(typeof(Interop.NtDll.SYSTEM_PROCESSOR_IDLE_INFORMATION));
if (Interop.NtDll.NtQuerySystemInformation(Interop.NtDll.SYSTEM_INFORMATION_CLASS.SystemProcessorIdleInformation, idleInformation, idleInformation.Length * idleSize, out int idleReturn) != 0)
return false;
//Query processor performance information
Interop.NtDll.SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION[] perfInformation = new Interop.NtDll.SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION[64];
int perfSize = Marshal.SizeOf(typeof(Interop.NtDll.SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION));
if (Interop.NtDll.NtQuerySystemInformation(Interop.NtDll.SYSTEM_INFORMATION_CLASS.SystemProcessorPerformanceInformation,
perfInformation,
perfInformation.Length * perfSize,
out int perfReturn) != 0)
{
return false;
}
idle = new long[idleReturn / idleSize];
for (int i = 0; i < idle.Length; i++)
idle[i] = idleInformation[i].IdleTime;
total = new long[perfReturn / perfSize];
for (int i = 0; i < total.Length; i++)
total[i] = perfInformation[i].KernelTime + perfInformation[i].UserTime;
return true;
}
private static bool GetUnixTimes(out long[] idle, out long[] total)
{
idle = null;
total = null;
List<long> idleList = new();
List<long> totalList = new();
if (!File.Exists("/proc/stat"))
return false;
string[] cpuInfos = File.ReadAllLines("/proc/stat");
// currently parse the OverAll CPU info
// cpu 1583083 737 452845 36226266 723316 63685 31896 0 0 0
// cpu0 397468 189 109728 9040007 191429 16939 14954 0 0 0
// 0=cpu 1=user 2=nice 3=system 4=idle 5=iowait 6=irq 7=softirq 8=steal 9=guest 10=guest_nice
foreach (string cpuInfo in cpuInfos.Where(s => s.StartsWith("cpu") && s.Length > 3 && s[3] != ' '))
{
string[] overall = cpuInfo.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
try
{
// Parse idle information.
idleList.Add(long.Parse(overall[4]));
}
catch
{
// Ignored.
}
// Parse total information = user + nice + system + idle + iowait + irq + softirq + steal + guest + guest_nice.
long currentTotal = 0;
foreach (string item in overall.Skip(1))
{
try
{
currentTotal += long.Parse(item);
}
catch
{
// Ignored.
}
}
totalList.Add(currentTotal);
}
idle = idleList.ToArray();
total = totalList.ToArray();
return true;
}
public float GetTotalLoad()
{
return _totalLoad;
}
public float GetThreadLoad(int thread)
{
return _threadLoads[thread];
}
public void Update()
{
if (_idleTimes == null || !GetTimes(out long[] newIdleTimes, out long[] newTotalTimes))
return;
int minDiff = Software.OperatingSystem.IsUnix ? 100 : 100000;
for (int i = 0; i < Math.Min(newTotalTimes.Length, _totalTimes.Length); i++)
{
if (newTotalTimes[i] - _totalTimes[i] < minDiff)
return;
}
if (newIdleTimes == null)
return;
float total = 0;
int count = 0;
for (int i = 0; i < _threadLoads.Length && i < _idleTimes.Length && i < newIdleTimes.Length; i++)
{
float idle = (newIdleTimes[i] - _idleTimes[i]) / (float)(newTotalTimes[i] - _totalTimes[i]);
_threadLoads[i] = 100f * (1.0f - Math.Min(idle, 1.0f));
total += idle;
count++;
}
if (count > 0)
{
total = 1.0f - (total / count);
total = total < 0 ? 0 : total;
}
else
{
total = 0;
}
_totalLoad = total * 100;
_totalTimes = newTotalTimes;
_idleTimes = newIdleTimes;
}
}

View File

@@ -0,0 +1,321 @@
// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
// Copyright (C) LibreHardwareMonitor and Contributors.
// Partial Copyright (C) Michael Möller <mmoeller@openhardwaremonitor.org> and Contributors.
// All Rights Reserved.
using System;
using System.Diagnostics;
using System.Globalization;
using System.Linq;
using System.Text;
namespace LibreHardwareMonitor.Hardware.Cpu;
public class GenericCpu : Hardware
{
protected readonly int _coreCount;
protected readonly CpuId[][] _cpuId;
protected readonly uint _family;
protected readonly uint _model;
protected readonly uint _packageType;
protected readonly uint _stepping;
protected readonly int _threadCount;
private readonly CpuLoad _cpuLoad;
private readonly double _estimatedTimeStampCounterFrequency;
private readonly double _estimatedTimeStampCounterFrequencyError;
private readonly bool _isInvariantTimeStampCounter;
private readonly Sensor[] _threadLoads;
private readonly Sensor _totalLoad;
private readonly Sensor _maxLoad;
private readonly Vendor _vendor;
private long _lastTime;
private ulong _lastTimeStampCount;
public GenericCpu(int processorIndex, CpuId[][] cpuId, ISettings settings) : base(cpuId[0][0].Name, CreateIdentifier(cpuId[0][0].Vendor, processorIndex), settings)
{
_cpuId = cpuId;
_vendor = cpuId[0][0].Vendor;
_family = cpuId[0][0].Family;
_model = cpuId[0][0].Model;
_stepping = cpuId[0][0].Stepping;
_packageType = cpuId[0][0].PkgType;
Index = processorIndex;
_coreCount = cpuId.Length;
_threadCount = cpuId.Sum(x => x.Length);
// Check if processor has MSRs.
HasModelSpecificRegisters = cpuId[0][0].Data.GetLength(0) > 1 && (cpuId[0][0].Data[1, 3] & 0x20) != 0;
// Check if processor has a TSC.
HasTimeStampCounter = cpuId[0][0].Data.GetLength(0) > 1 && (cpuId[0][0].Data[1, 3] & 0x10) != 0;
// Check if processor supports an invariant TSC.
_isInvariantTimeStampCounter = cpuId[0][0].ExtData.GetLength(0) > 7 && (cpuId[0][0].ExtData[7, 3] & 0x100) != 0;
_totalLoad = _coreCount > 1 ? new Sensor("CPU Total", 0, SensorType.Load, this, settings) : null;
_maxLoad = _coreCount > 1 ? new Sensor("CPU Core Max", 1, SensorType.Load, this, settings) : null;
_cpuLoad = new CpuLoad(cpuId);
if (_cpuLoad.IsAvailable)
{
_threadLoads = new Sensor[_threadCount];
for (int coreIdx = 0; coreIdx < cpuId.Length; coreIdx++)
{
for (int threadIdx = 0; threadIdx < cpuId[coreIdx].Length; threadIdx++)
{
int thread = cpuId[coreIdx][threadIdx].Thread;
if (thread < _threadLoads.Length)
{
// Some cores may have 2 threads while others have only one (e.g. P-cores vs E-cores on Intel 12th gen).
string sensorName = CoreString(coreIdx) + (cpuId[coreIdx].Length > 1 ? $" Thread #{threadIdx + 1}" : string.Empty);
_threadLoads[thread] = new Sensor(sensorName, thread + 2, SensorType.Load, this, settings);
ActivateSensor(_threadLoads[thread]);
}
}
}
if (_totalLoad != null)
{
ActivateSensor(_totalLoad);
}
if (_maxLoad != null)
{
ActivateSensor(_maxLoad);
}
}
if (HasTimeStampCounter)
{
GroupAffinity previousAffinity = ThreadAffinity.Set(cpuId[0][0].Affinity);
EstimateTimeStampCounterFrequency(out _estimatedTimeStampCounterFrequency, out _estimatedTimeStampCounterFrequencyError);
ThreadAffinity.Set(previousAffinity);
}
else
{
_estimatedTimeStampCounterFrequency = 0;
}
TimeStampCounterFrequency = _estimatedTimeStampCounterFrequency;
}
/// <summary>
/// Gets the CPUID.
/// </summary>
public CpuId[][] CpuId => _cpuId;
public override HardwareType HardwareType => HardwareType.Cpu;
public bool HasModelSpecificRegisters { get; }
public bool HasTimeStampCounter { get; }
/// <summary>
/// Gets the CPU index.
/// </summary>
public int Index { get; }
public double TimeStampCounterFrequency { get; private set; }
protected string CoreString(int i)
{
if (_coreCount == 1)
return "CPU Core";
return "CPU Core #" + (i + 1);
}
private static Identifier CreateIdentifier(Vendor vendor, int processorIndex)
{
string s = vendor switch
{
Vendor.AMD => "amdcpu",
Vendor.Intel => "intelcpu",
_ => "genericcpu"
};
return new Identifier(s, processorIndex.ToString(CultureInfo.InvariantCulture));
}
private static void EstimateTimeStampCounterFrequency(out double frequency, out double error)
{
// preload the function
EstimateTimeStampCounterFrequency(0, out double f, out double e);
EstimateTimeStampCounterFrequency(0, out f, out e);
// estimate the frequency
error = double.MaxValue;
frequency = 0;
for (int i = 0; i < 5; i++)
{
EstimateTimeStampCounterFrequency(0.025, out f, out e);
if (e < error)
{
error = e;
frequency = f;
}
if (error < 1e-4)
break;
}
}
private static void EstimateTimeStampCounterFrequency(double timeWindow, out double frequency, out double error)
{
long ticks = (long)(timeWindow * Stopwatch.Frequency);
long timeBegin = Stopwatch.GetTimestamp() + (long)Math.Ceiling(0.001 * ticks);
long timeEnd = timeBegin + ticks;
while (Stopwatch.GetTimestamp() < timeBegin)
{ }
ulong countBegin = OpCode.Rdtsc();
long afterBegin = Stopwatch.GetTimestamp();
while (Stopwatch.GetTimestamp() < timeEnd)
{ }
ulong countEnd = OpCode.Rdtsc();
long afterEnd = Stopwatch.GetTimestamp();
double delta = timeEnd - timeBegin;
frequency = 1e-6 * ((double)(countEnd - countBegin) * Stopwatch.Frequency) / delta;
double beginError = (afterBegin - timeBegin) / delta;
double endError = (afterEnd - timeEnd) / delta;
error = beginError + endError;
}
private static void AppendMsrData(StringBuilder r, uint msr, GroupAffinity affinity)
{
if (Ring0.ReadMsr(msr, out uint eax, out uint edx, affinity))
{
r.Append(" ");
r.Append(msr.ToString("X8", CultureInfo.InvariantCulture));
r.Append(" ");
r.Append(edx.ToString("X8", CultureInfo.InvariantCulture));
r.Append(" ");
r.Append(eax.ToString("X8", CultureInfo.InvariantCulture));
r.AppendLine();
}
}
protected virtual uint[] GetMsrs()
{
return null;
}
public override string GetReport()
{
StringBuilder r = new();
switch (_vendor)
{
case Vendor.AMD:
r.AppendLine("AMD CPU");
break;
case Vendor.Intel:
r.AppendLine("Intel CPU");
break;
default:
r.AppendLine("Generic CPU");
break;
}
r.AppendLine();
r.AppendFormat("Name: {0}{1}", _name, Environment.NewLine);
r.AppendFormat("Number of Cores: {0}{1}", _coreCount, Environment.NewLine);
r.AppendFormat("Threads per Core: {0}{1}", _cpuId[0].Length, Environment.NewLine);
r.AppendLine(string.Format(CultureInfo.InvariantCulture, "Timer Frequency: {0} MHz", Stopwatch.Frequency * 1e-6));
r.AppendLine("Time Stamp Counter: " + (HasTimeStampCounter ? _isInvariantTimeStampCounter ? "Invariant" : "Not Invariant" : "None"));
r.AppendLine(string.Format(CultureInfo.InvariantCulture, "Estimated Time Stamp Counter Frequency: {0} MHz", Math.Round(_estimatedTimeStampCounterFrequency * 100) * 0.01));
r.AppendLine(string.Format(CultureInfo.InvariantCulture,
"Estimated Time Stamp Counter Frequency Error: {0} Mhz",
Math.Round(_estimatedTimeStampCounterFrequency * _estimatedTimeStampCounterFrequencyError * 1e5) * 1e-5));
r.AppendLine(string.Format(CultureInfo.InvariantCulture, "Time Stamp Counter Frequency: {0} MHz", Math.Round(TimeStampCounterFrequency * 100) * 0.01));
r.AppendLine();
uint[] msrArray = GetMsrs();
if (msrArray is { Length: > 0 })
{
for (int i = 0; i < _cpuId.Length; i++)
{
r.AppendLine("MSR Core #" + (i + 1));
r.AppendLine();
r.AppendLine(" MSR EDX EAX");
foreach (uint msr in msrArray)
AppendMsrData(r, msr, _cpuId[i][0].Affinity);
r.AppendLine();
}
}
return r.ToString();
}
public override void Update()
{
if (HasTimeStampCounter && _isInvariantTimeStampCounter)
{
// make sure always the same thread is used
GroupAffinity previousAffinity = ThreadAffinity.Set(_cpuId[0][0].Affinity);
// read time before and after getting the TSC to estimate the error
long firstTime = Stopwatch.GetTimestamp();
ulong timeStampCount = OpCode.Rdtsc();
long time = Stopwatch.GetTimestamp();
// restore the thread affinity mask
ThreadAffinity.Set(previousAffinity);
double delta = (double)(time - _lastTime) / Stopwatch.Frequency;
double error = (double)(time - firstTime) / Stopwatch.Frequency;
// only use data if they are measured accurate enough (max 0.1ms delay)
if (error < 0.0001)
{
// ignore the first reading because there are no initial values
// ignore readings with too large or too small time window
if (_lastTime != 0 && delta is > 0.5 and < 2)
{
// update the TSC frequency with the new value
TimeStampCounterFrequency = (timeStampCount - _lastTimeStampCount) / (1e6 * delta);
}
_lastTimeStampCount = timeStampCount;
_lastTime = time;
}
}
if (_cpuLoad.IsAvailable)
{
_cpuLoad.Update();
float maxLoad = 0;
if (_threadLoads != null)
{
for (int i = 0; i < _threadLoads.Length; i++)
{
if (_threadLoads[i] != null)
{
_threadLoads[i].Value = _cpuLoad.GetThreadLoad(i);
maxLoad = Math.Max(maxLoad, _threadLoads[i].Value ?? 0);
}
}
}
if (_totalLoad != null)
_totalLoad.Value = _cpuLoad.GetTotalLoad();
if (_maxLoad != null)
_maxLoad.Value = maxLoad;
}
}
}

View File

@@ -0,0 +1,731 @@
// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
// Copyright (C) LibreHardwareMonitor and Contributors.
// Partial Copyright (C) Michael Möller <mmoeller@openhardwaremonitor.org> and Contributors.
// All Rights Reserved.
using System;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.Text;
namespace LibreHardwareMonitor.Hardware.Cpu;
internal sealed class IntelCpu : GenericCpu
{
private readonly Sensor _busClock;
private readonly Sensor _coreAvg;
private readonly Sensor[] _coreClocks;
private readonly Sensor _coreMax;
private readonly Sensor[] _coreTemperatures;
private readonly Sensor[] _coreVIDs;
private readonly Sensor _coreVoltage;
private readonly Sensor[] _distToTjMaxTemperatures;
private readonly uint[] _energyStatusMsrs = { MSR_PKG_ENERGY_STATUS, MSR_PP0_ENERGY_STATUS, MSR_PP1_ENERGY_STATUS, MSR_DRAM_ENERGY_STATUS, MSR_PLATFORM_ENERGY_STATUS };
private readonly uint[] _lastEnergyConsumed;
private readonly DateTime[] _lastEnergyTime;
private readonly MicroArchitecture _microArchitecture;
private readonly Sensor _packageTemperature;
private readonly Sensor[] _powerSensors;
private readonly double _timeStampCounterMultiplier;
public IntelCpu(int processorIndex, CpuId[][] cpuId, ISettings settings) : base(processorIndex, cpuId, settings)
{
uint eax;
// set tjMax
float[] tjMax;
switch (_family)
{
case 0x06:
{
switch (_model)
{
case 0x0F: // Intel Core 2 (65nm)
_microArchitecture = MicroArchitecture.Core;
tjMax = _stepping switch
{
// B2
0x06 => _coreCount switch
{
2 => Floats(80 + 10),
4 => Floats(90 + 10),
_ => Floats(85 + 10)
},
// G0
0x0B => Floats(90 + 10),
// M0
0x0D => Floats(85 + 10),
_ => Floats(85 + 10)
};
break;
case 0x17: // Intel Core 2 (45nm)
_microArchitecture = MicroArchitecture.Core;
tjMax = Floats(100);
break;
case 0x1C: // Intel Atom (45nm)
_microArchitecture = MicroArchitecture.Atom;
tjMax = _stepping switch
{
// C0
0x02 => Floats(90),
// A0, B0
0x0A => Floats(100),
_ => Floats(90)
};
break;
case 0x1A: // Intel Core i7 LGA1366 (45nm)
case 0x1E: // Intel Core i5, i7 LGA1156 (45nm)
case 0x1F: // Intel Core i5, i7
case 0x25: // Intel Core i3, i5, i7 LGA1156 (32nm)
case 0x2C: // Intel Core i7 LGA1366 (32nm) 6 Core
case 0x2E: // Intel Xeon Processor 7500 series (45nm)
case 0x2F: // Intel Xeon Processor (32nm)
_microArchitecture = MicroArchitecture.Nehalem;
tjMax = GetTjMaxFromMsr();
break;
case 0x2A: // Intel Core i5, i7 2xxx LGA1155 (32nm)
case 0x2D: // Next Generation Intel Xeon, i7 3xxx LGA2011 (32nm)
_microArchitecture = MicroArchitecture.SandyBridge;
tjMax = GetTjMaxFromMsr();
break;
case 0x3A: // Intel Core i5, i7 3xxx LGA1155 (22nm)
case 0x3E: // Intel Core i7 4xxx LGA2011 (22nm)
_microArchitecture = MicroArchitecture.IvyBridge;
tjMax = GetTjMaxFromMsr();
break;
case 0x3C: // Intel Core i5, i7 4xxx LGA1150 (22nm)
case 0x3F: // Intel Xeon E5-2600/1600 v3, Core i7-59xx
// LGA2011-v3, Haswell-E (22nm)
case 0x45: // Intel Core i5, i7 4xxxU (22nm)
case 0x46:
_microArchitecture = MicroArchitecture.Haswell;
tjMax = GetTjMaxFromMsr();
break;
case 0x3D: // Intel Core M-5xxx (14nm)
case 0x47: // Intel i5, i7 5xxx, Xeon E3-1200 v4 (14nm)
case 0x4F: // Intel Xeon E5-26xx v4
case 0x56: // Intel Xeon D-15xx
_microArchitecture = MicroArchitecture.Broadwell;
tjMax = GetTjMaxFromMsr();
break;
case 0x36: // Intel Atom S1xxx, D2xxx, N2xxx (32nm)
_microArchitecture = MicroArchitecture.Atom;
tjMax = GetTjMaxFromMsr();
break;
case 0x37: // Intel Atom E3xxx, Z3xxx (22nm)
case 0x4A:
case 0x4D: // Intel Atom C2xxx (22nm)
case 0x5A:
case 0x5D:
_microArchitecture = MicroArchitecture.Silvermont;
tjMax = GetTjMaxFromMsr();
break;
case 0x4E:
case 0x5E: // Intel Core i5, i7 6xxxx LGA1151 (14nm)
case 0x55: // Intel Core X i7, i9 7xxx LGA2066 (14nm)
_microArchitecture = MicroArchitecture.Skylake;
tjMax = GetTjMaxFromMsr();
break;
case 0x4C: // Intel Airmont (Cherry Trail, Braswell)
_microArchitecture = MicroArchitecture.Airmont;
tjMax = GetTjMaxFromMsr();
break;
case 0x8E: // Intel Core i5, i7 7xxxx (14nm) (Kaby Lake) and 8xxxx (14nm++) (Coffee Lake)
case 0x9E:
_microArchitecture = MicroArchitecture.KabyLake;
tjMax = GetTjMaxFromMsr();
break;
case 0x5C: // Goldmont (Apollo Lake)
case 0x5F: // (Denverton)
_microArchitecture = MicroArchitecture.Goldmont;
tjMax = GetTjMaxFromMsr();
break;
case 0x7A: // Goldmont plus (Gemini Lake)
_microArchitecture = MicroArchitecture.GoldmontPlus;
tjMax = GetTjMaxFromMsr();
break;
case 0x66: // Intel Core i3 8xxx (10nm) (Cannon Lake)
_microArchitecture = MicroArchitecture.CannonLake;
tjMax = GetTjMaxFromMsr();
break;
case 0x7D: // Intel Core i3, i5, i7 10xxx (10nm) (Ice Lake)
case 0x7E:
case 0x6A: // Ice Lake server
case 0x6C:
_microArchitecture = MicroArchitecture.IceLake;
tjMax = GetTjMaxFromMsr();
break;
case 0xA5:
case 0xA6: // Intel Core i3, i5, i7 10xxxU (14nm)
_microArchitecture = MicroArchitecture.CometLake;
tjMax = GetTjMaxFromMsr();
break;
case 0x86: // Tremont (10nm) (Elkhart Lake, Skyhawk Lake)
_microArchitecture = MicroArchitecture.Tremont;
tjMax = GetTjMaxFromMsr();
break;
case 0x8C: // Tiger Lake (Intel 10 nm SuperFin, Gen. 11)
case 0x8D:
_microArchitecture = MicroArchitecture.TigerLake;
tjMax = GetTjMaxFromMsr();
break;
case 0x97: // Alder Lake (Intel 7 (10ESF), Gen. 12)
case 0x9A: // Alder Lake-L (Intel 7 (10ESF), Gen. 12)
case 0xBE: // Alder Lake-N (Intel 7 (10ESF), Gen. 12)
_microArchitecture = MicroArchitecture.AlderLake;
tjMax = GetTjMaxFromMsr();
break;
case 0xB7: // Raptor Lake (Intel 7 (10ESF), Gen. 13)
case 0xBA: // Raptor Lake-P (Intel 7 (10ESF), Gen. 13)
case 0xBF: // Raptor Lake-N (Intel 7 (10ESF), Gen. 13)
_microArchitecture = MicroArchitecture.RaptorLake;
tjMax = GetTjMaxFromMsr();
break;
case 0xAC: // Meteor Lake (Intel 4, TSMC N5/N6, Gen. 14)
case 0xAA: // Meteor Lake-L (Intel 4, TSMC N5/N6, Gen. 14)
_microArchitecture = MicroArchitecture.MeteorLake;
tjMax = GetTjMaxFromMsr();
break;
case 0x9C: // Jasper Lake (10nm)
_microArchitecture = MicroArchitecture.JasperLake;
tjMax = GetTjMaxFromMsr();
break;
case 0xA7: // Intel Core i5, i6, i7 11xxx (14nm) (Rocket Lake)
_microArchitecture = MicroArchitecture.RocketLake;
tjMax = GetTjMaxFromMsr();
break;
case 0xC6: // Intel Core Ultra 7 200 Series ArrowLake
_microArchitecture = MicroArchitecture.ArrowLake;
tjMax = GetTjMaxFromMsr();
break;
case 0xBD: // Intel Core Ultra 5/7 200 Series LunarLake
_microArchitecture = MicroArchitecture.LunarLake;
tjMax = GetTjMaxFromMsr();
break;
default:
_microArchitecture = MicroArchitecture.Unknown;
tjMax = Floats(100);
break;
}
}
break;
case 0x0F:
switch (_model)
{
case 0x00: // Pentium 4 (180nm)
case 0x01: // Pentium 4 (130nm)
case 0x02: // Pentium 4 (130nm)
case 0x03: // Pentium 4, Celeron D (90nm)
case 0x04: // Pentium 4, Pentium D, Celeron D (90nm)
case 0x06: // Pentium 4, Pentium D, Celeron D (65nm)
_microArchitecture = MicroArchitecture.NetBurst;
tjMax = Floats(100);
break;
default:
_microArchitecture = MicroArchitecture.Unknown;
tjMax = Floats(100);
break;
}
break;
default:
_microArchitecture = MicroArchitecture.Unknown;
tjMax = Floats(100);
break;
}
// set timeStampCounterMultiplier
switch (_microArchitecture)
{
case MicroArchitecture.Atom:
case MicroArchitecture.Core:
case MicroArchitecture.NetBurst:
if (Ring0.ReadMsr(IA32_PERF_STATUS, out uint _, out uint edx))
_timeStampCounterMultiplier = ((edx >> 8) & 0x1f) + (0.5 * ((edx >> 14) & 1));
break;
case MicroArchitecture.Airmont:
case MicroArchitecture.AlderLake:
case MicroArchitecture.ArrowLake:
case MicroArchitecture.Broadwell:
case MicroArchitecture.CannonLake:
case MicroArchitecture.CometLake:
case MicroArchitecture.Goldmont:
case MicroArchitecture.GoldmontPlus:
case MicroArchitecture.Haswell:
case MicroArchitecture.IceLake:
case MicroArchitecture.IvyBridge:
case MicroArchitecture.JasperLake:
case MicroArchitecture.KabyLake:
case MicroArchitecture.LunarLake:
case MicroArchitecture.Nehalem:
case MicroArchitecture.MeteorLake:
case MicroArchitecture.RaptorLake:
case MicroArchitecture.RocketLake:
case MicroArchitecture.SandyBridge:
case MicroArchitecture.Silvermont:
case MicroArchitecture.Skylake:
case MicroArchitecture.TigerLake:
case MicroArchitecture.Tremont:
if (Ring0.ReadMsr(MSR_PLATFORM_INFO, out eax, out uint _))
_timeStampCounterMultiplier = (eax >> 8) & 0xff;
break;
default:
_timeStampCounterMultiplier = 0;
break;
}
int coreSensorId = 0;
//core temp avg and max value
//is only available when the cpu has more than 1 core
if (cpuId[0][0].Data.GetLength(0) > 6 && (cpuId[0][0].Data[6, 0] & 0x40) != 0 && _microArchitecture != MicroArchitecture.Unknown && _coreCount > 1)
{
_coreMax = new Sensor("Core Max", coreSensorId, SensorType.Temperature, this, settings);
ActivateSensor(_coreMax);
coreSensorId++;
_coreAvg = new Sensor("Core Average", coreSensorId, SensorType.Temperature, this, settings);
ActivateSensor(_coreAvg);
coreSensorId++;
}
else
{
_coreMax = null;
_coreAvg = null;
}
// check if processor supports a digital thermal sensor at core level
if (cpuId[0][0].Data.GetLength(0) > 6 && (cpuId[0][0].Data[6, 0] & 1) != 0 && _microArchitecture != MicroArchitecture.Unknown)
{
_coreTemperatures = new Sensor[_coreCount];
for (int i = 0; i < _coreTemperatures.Length; i++)
{
_coreTemperatures[i] = new Sensor(CoreString(i),
coreSensorId,
SensorType.Temperature,
this,
new[]
{
new ParameterDescription("TjMax [°C]", "TjMax temperature of the core sensor.\n" + "Temperature = TjMax - TSlope * Value.", tjMax[i]),
new ParameterDescription("TSlope [°C]", "Temperature slope of the digital thermal sensor.\n" + "Temperature = TjMax - TSlope * Value.", 1)
},
settings);
ActivateSensor(_coreTemperatures[i]);
coreSensorId++;
}
}
else
_coreTemperatures = Array.Empty<Sensor>();
// check if processor supports a digital thermal sensor at package level
if (cpuId[0][0].Data.GetLength(0) > 6 && (cpuId[0][0].Data[6, 0] & 0x40) != 0 && _microArchitecture != MicroArchitecture.Unknown)
{
_packageTemperature = new Sensor("CPU Package",
coreSensorId,
SensorType.Temperature,
this,
new[]
{
new ParameterDescription("TjMax [°C]", "TjMax temperature of the package sensor.\n" + "Temperature = TjMax - TSlope * Value.", tjMax[0]),
new ParameterDescription("TSlope [°C]", "Temperature slope of the digital thermal sensor.\n" + "Temperature = TjMax - TSlope * Value.", 1)
},
settings);
ActivateSensor(_packageTemperature);
coreSensorId++;
}
// dist to tjmax sensor
if (cpuId[0][0].Data.GetLength(0) > 6 && (cpuId[0][0].Data[6, 0] & 1) != 0 && _microArchitecture != MicroArchitecture.Unknown)
{
_distToTjMaxTemperatures = new Sensor[_coreCount];
for (int i = 0; i < _distToTjMaxTemperatures.Length; i++)
{
_distToTjMaxTemperatures[i] = new Sensor(CoreString(i) + " Distance to TjMax", coreSensorId, SensorType.Temperature, this, settings);
ActivateSensor(_distToTjMaxTemperatures[i]);
coreSensorId++;
}
}
else
_distToTjMaxTemperatures = Array.Empty<Sensor>();
_busClock = new Sensor("Bus Speed", 0, SensorType.Clock, this, settings);
_coreClocks = new Sensor[_coreCount];
for (int i = 0; i < _coreClocks.Length; i++)
{
_coreClocks[i] = new Sensor(CoreString(i), i + 1, SensorType.Clock, this, settings);
if (HasTimeStampCounter && _microArchitecture != MicroArchitecture.Unknown)
ActivateSensor(_coreClocks[i]);
}
if (_microArchitecture is MicroArchitecture.Airmont or
MicroArchitecture.AlderLake or
MicroArchitecture.ArrowLake or
MicroArchitecture.Broadwell or
MicroArchitecture.CannonLake or
MicroArchitecture.CometLake or
MicroArchitecture.Goldmont or
MicroArchitecture.GoldmontPlus or
MicroArchitecture.Haswell or
MicroArchitecture.IceLake or
MicroArchitecture.IvyBridge or
MicroArchitecture.JasperLake or
MicroArchitecture.KabyLake or
MicroArchitecture.LunarLake or
MicroArchitecture.MeteorLake or
MicroArchitecture.RaptorLake or
MicroArchitecture.RocketLake or
MicroArchitecture.SandyBridge or
MicroArchitecture.Silvermont or
MicroArchitecture.Skylake or
MicroArchitecture.TigerLake or
MicroArchitecture.Tremont)
{
_powerSensors = new Sensor[_energyStatusMsrs.Length];
_lastEnergyTime = new DateTime[_energyStatusMsrs.Length];
_lastEnergyConsumed = new uint[_energyStatusMsrs.Length];
if (Ring0.ReadMsr(MSR_RAPL_POWER_UNIT, out eax, out uint _))
{
EnergyUnitsMultiplier = _microArchitecture switch
{
MicroArchitecture.Silvermont or MicroArchitecture.Airmont => 1.0e-6f * (1 << (int)((eax >> 8) & 0x1F)),
_ => 1.0f / (1 << (int)((eax >> 8) & 0x1F))
};
}
if (EnergyUnitsMultiplier != 0)
{
string[] powerSensorLabels = { "CPU Package", "CPU Cores", "CPU Graphics", "CPU Memory", "CPU Platform" };
for (int i = 0; i < _energyStatusMsrs.Length; i++)
{
if (!Ring0.ReadMsr(_energyStatusMsrs[i], out eax, out uint _))
continue;
// Don't show the "GPU Graphics" sensor on windows, it will show up under the GPU instead.
if (i == 2 && !Software.OperatingSystem.IsUnix)
continue;
_lastEnergyTime[i] = DateTime.UtcNow;
_lastEnergyConsumed[i] = eax;
_powerSensors[i] = new Sensor(powerSensorLabels[i],
i,
SensorType.Power,
this,
settings);
ActivateSensor(_powerSensors[i]);
}
}
}
if (Ring0.ReadMsr(IA32_PERF_STATUS, out eax, out uint _) && ((eax >> 32) & 0xFFFF) > 0)
{
_coreVoltage = new Sensor("CPU Core", 0, SensorType.Voltage, this, settings);
ActivateSensor(_coreVoltage);
}
_coreVIDs = new Sensor[_coreCount];
for (int i = 0; i < _coreVIDs.Length; i++)
{
_coreVIDs[i] = new Sensor(CoreString(i), i + 1, SensorType.Voltage, this, settings);
ActivateSensor(_coreVIDs[i]);
}
Update();
}
public float EnergyUnitsMultiplier { get; }
private float[] Floats(float f)
{
float[] result = new float[_coreCount];
for (int i = 0; i < _coreCount; i++)
result[i] = f;
return result;
}
private float[] GetTjMaxFromMsr()
{
float[] result = new float[_coreCount];
for (int i = 0; i < _coreCount; i++)
{
if (Ring0.ReadMsr(IA32_TEMPERATURE_TARGET, out uint eax, out uint _, _cpuId[i][0].Affinity))
result[i] = (eax >> 16) & 0xFF;
else
result[i] = 100;
}
return result;
}
protected override uint[] GetMsrs()
{
return new[]
{
MSR_PLATFORM_INFO,
IA32_PERF_STATUS,
IA32_THERM_STATUS_MSR,
IA32_TEMPERATURE_TARGET,
IA32_PACKAGE_THERM_STATUS,
MSR_RAPL_POWER_UNIT,
MSR_PKG_ENERGY_STATUS,
MSR_DRAM_ENERGY_STATUS,
MSR_PP0_ENERGY_STATUS,
MSR_PP1_ENERGY_STATUS,
MSR_PLATFORM_ENERGY_STATUS,
};
}
public override string GetReport()
{
StringBuilder r = new();
r.Append(base.GetReport());
r.Append("MicroArchitecture: ");
r.AppendLine(_microArchitecture.ToString());
r.Append("Time Stamp Counter Multiplier: ");
r.AppendLine(_timeStampCounterMultiplier.ToString(CultureInfo.InvariantCulture));
r.AppendLine();
return r.ToString();
}
public override void Update()
{
base.Update();
float coreMax = float.MinValue;
float coreAvg = 0;
uint eax;
for (int i = 0; i < _coreTemperatures.Length; i++)
{
// if reading is valid
if (Ring0.ReadMsr(IA32_THERM_STATUS_MSR, out eax, out _, _cpuId[i][0].Affinity) && (eax & 0x80000000) != 0)
{
// get the dist from tjMax from bits 22:16
float deltaT = (eax & 0x007F0000) >> 16;
float tjMax = _coreTemperatures[i].Parameters[0].Value;
float tSlope = _coreTemperatures[i].Parameters[1].Value;
_coreTemperatures[i].Value = tjMax - (tSlope * deltaT);
coreAvg += (float)_coreTemperatures[i].Value;
if (coreMax < _coreTemperatures[i].Value)
coreMax = (float)_coreTemperatures[i].Value;
_distToTjMaxTemperatures[i].Value = deltaT;
}
else
{
_coreTemperatures[i].Value = null;
_distToTjMaxTemperatures[i].Value = null;
}
}
//calculate average cpu temperature over all cores
if (_coreMax != null && coreMax != float.MinValue)
{
_coreMax.Value = coreMax;
coreAvg /= _coreTemperatures.Length;
_coreAvg.Value = coreAvg;
}
if (_packageTemperature != null)
{
// if reading is valid
if (Ring0.ReadMsr(IA32_PACKAGE_THERM_STATUS, out eax, out _, _cpuId[0][0].Affinity) && (eax & 0x80000000) != 0)
{
// get the dist from tjMax from bits 22:16
float deltaT = (eax & 0x007F0000) >> 16;
float tjMax = _packageTemperature.Parameters[0].Value;
float tSlope = _packageTemperature.Parameters[1].Value;
_packageTemperature.Value = tjMax - (tSlope * deltaT);
}
else
{
_packageTemperature.Value = null;
}
}
if (HasTimeStampCounter && _timeStampCounterMultiplier > 0)
{
double newBusClock = 0;
for (int i = 0; i < _coreClocks.Length; i++)
{
System.Threading.Thread.Sleep(1);
if (Ring0.ReadMsr(IA32_PERF_STATUS, out eax, out _, _cpuId[i][0].Affinity))
{
newBusClock = TimeStampCounterFrequency / _timeStampCounterMultiplier;
switch (_microArchitecture)
{
case MicroArchitecture.Nehalem:
_coreClocks[i].Value = (float)((eax & 0xff) * newBusClock);
break;
case MicroArchitecture.Airmont:
case MicroArchitecture.AlderLake:
case MicroArchitecture.ArrowLake:
case MicroArchitecture.Broadwell:
case MicroArchitecture.CannonLake:
case MicroArchitecture.CometLake:
case MicroArchitecture.Goldmont:
case MicroArchitecture.GoldmontPlus:
case MicroArchitecture.Haswell:
case MicroArchitecture.IceLake:
case MicroArchitecture.IvyBridge:
case MicroArchitecture.JasperLake:
case MicroArchitecture.KabyLake:
case MicroArchitecture.LunarLake:
case MicroArchitecture.MeteorLake:
case MicroArchitecture.RaptorLake:
case MicroArchitecture.RocketLake:
case MicroArchitecture.SandyBridge:
case MicroArchitecture.Silvermont:
case MicroArchitecture.Skylake:
case MicroArchitecture.TigerLake:
case MicroArchitecture.Tremont:
_coreClocks[i].Value = (float)(((eax >> 8) & 0xff) * newBusClock);
break;
default:
_coreClocks[i].Value = (float)((((eax >> 8) & 0x1f) + (0.5 * ((eax >> 14) & 1))) * newBusClock);
break;
}
}
else
{
// if IA32_PERF_STATUS is not available, assume TSC frequency
_coreClocks[i].Value = (float)TimeStampCounterFrequency;
}
}
if (newBusClock > 0)
{
_busClock.Value = (float)newBusClock;
ActivateSensor(_busClock);
}
}
if (_powerSensors != null)
{
foreach (Sensor sensor in _powerSensors)
{
if (sensor == null)
continue;
if (!Ring0.ReadMsr(_energyStatusMsrs[sensor.Index], out eax, out _))
continue;
DateTime time = DateTime.UtcNow;
uint energyConsumed = eax;
float deltaTime = (float)(time - _lastEnergyTime[sensor.Index]).TotalSeconds;
if (deltaTime < 0.01)
continue;
sensor.Value = EnergyUnitsMultiplier * unchecked(energyConsumed - _lastEnergyConsumed[sensor.Index]) / deltaTime;
_lastEnergyTime[sensor.Index] = time;
_lastEnergyConsumed[sensor.Index] = energyConsumed;
}
}
if (_coreVoltage != null && Ring0.ReadMsr(IA32_PERF_STATUS, out _, out uint edx))
{
_coreVoltage.Value = ((edx >> 32) & 0xFFFF) / (float)(1 << 13);
}
for (int i = 0; i < _coreVIDs.Length; i++)
{
if (Ring0.ReadMsr(IA32_PERF_STATUS, out _, out edx, _cpuId[i][0].Affinity) && ((edx >> 32) & 0xFFFF) > 0)
{
_coreVIDs[i].Value = ((edx >> 32) & 0xFFFF) / (float)(1 << 13);
ActivateSensor(_coreVIDs[i]);
}
else
{
DeactivateSensor(_coreVIDs[i]);
}
}
}
[SuppressMessage("ReSharper", "IdentifierTypo")]
private enum MicroArchitecture
{
Airmont,
AlderLake,
Atom,
ArrowLake, // Gen. 15 (0xC6, -H = 0xC5)
Broadwell,
CannonLake,
CometLake,
Core,
Goldmont,
GoldmontPlus,
Haswell,
IceLake,
IvyBridge,
JasperLake,
KabyLake,
LunarLake,
Nehalem,
NetBurst,
MeteorLake,
RocketLake,
SandyBridge,
Silvermont,
Skylake,
TigerLake,
Tremont,
RaptorLake,
Unknown
}
// ReSharper disable InconsistentNaming
private const uint IA32_PACKAGE_THERM_STATUS = 0x1B1;
private const uint IA32_PERF_STATUS = 0x0198;
private const uint IA32_TEMPERATURE_TARGET = 0x01A2;
private const uint IA32_THERM_STATUS_MSR = 0x019C;
private const uint MSR_DRAM_ENERGY_STATUS = 0x619;
private const uint MSR_PKG_ENERGY_STATUS = 0x611;
private const uint MSR_PLATFORM_INFO = 0xCE;
private const uint MSR_PP0_ENERGY_STATUS = 0x639;
private const uint MSR_PP1_ENERGY_STATUS = 0x641;
private const uint MSR_PLATFORM_ENERGY_STATUS = 0x64D;
private const uint MSR_RAPL_POWER_UNIT = 0x606;
// ReSharper restore InconsistentNaming
}