diff --git a/.github/workflows/pull requests.yml b/.github/workflows/pull requests.yml index 1b96c68e4..730707bc4 100644 --- a/.github/workflows/pull requests.yml +++ b/.github/workflows/pull requests.yml @@ -2,7 +2,7 @@ name: pull requests on: pull_request: - branches: [ master ] + branches: [ 0.8.9 ] jobs: build: @@ -36,26 +36,26 @@ jobs: run: msbuild LibreHardwareMonitor.sln -p:Configuration=Release -m - name: Publish net452 - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v4 with: name: LibreHardwareMonitor-net452 path: | bin/Release/net452 - name: Publish netstandard20 - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v4 with: name: LibreHardwareMonitorLib-netstandard20 path: | bin/Release/netstandard2.0 - name: Publish net50 - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v4 with: name: LibreHardwareMonitorLib-net50 path: | bin/Release/net5.0 - name: Publish nupkg - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v4 with: name: LibreHardwareMonitorLib-nupkg path: | diff --git a/LibreHardwareMonitorLib/Hardware/Memory/AmdDimmSensor.cs b/LibreHardwareMonitorLib/Hardware/Memory/AmdDimmSensor.cs new file mode 100644 index 000000000..ffdea5469 --- /dev/null +++ b/LibreHardwareMonitorLib/Hardware/Memory/AmdDimmSensor.cs @@ -0,0 +1,122 @@ +// 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 and Contributors. +// All Rights Reserved. + +using System; +using System.Collections.Generic; +using System.Runtime.InteropServices; + +namespace LibreHardwareMonitor.Hardware.Memory +{ + internal sealed class AmdDimmSensor : DimmSensor + { + public AmdDimmSensor(string name, int index, Hardware hardware, ISettings settings, byte address) : base(name, index, hardware, settings, address) + { + + } + + public override void UpdateSensor() + { + try + { + ushort data = GetWord(_address, 0x05); + var temp = BitConverter.GetBytes(data); + Array.Reverse(temp); + + temp[1] = (byte)(temp[1] & 0x0F); + ushort count = BitConverter.ToUInt16(temp, 0); + double value = count * 0.0625f; + if (value > 0) + { + Value = (float)value; + } + } + catch { } + } + + public static byte SmbDetect(byte addr) + { + Ring0.WriteSmbus(SMB_HSTADD, (addr << 1) | SMB_WRITE); + Ring0.WriteSmbus(SMB_HSTCNT, 0); + if (Transaction() == true) + { + return addr; + } + return 0x00; + } + + private static ushort GetWord(byte addr, byte command) + { + Ring0.WriteSmbus(SMB_HSTADD, (addr << 1) | SMB_READ); + Ring0.WriteSmbus(SMB_HSTCMD, command); + + Ring0.WriteSmbus(SMB_HSTCNT, 0x0C); + + Transaction(); + + ushort temp = (ushort)(Ring0.ReadSmbus(SMB_HSTDAT0) + (Ring0.ReadSmbus(SMB_HSTDAT1) << 8)); + return temp; + } + + private static bool Transaction() + { + byte temp = (byte)Ring0.ReadSmbus(SMB_HSTSTS); + + if (temp != 0x00) + { + Ring0.WriteSmbus(SMB_HSTSTS, temp); + + temp = (byte)Ring0.ReadSmbus(SMB_HSTSTS); + + if (temp != 0x00) + { + return false; + } + } + + temp = (byte)Ring0.ReadSmbus(SMB_HSTCNT); + Ring0.WriteSmbus(SMB_HSTCNT, (byte)(temp | 0x040)); + + temp = 0; + int timeout = 0; + int MAX_TIMEOUT = 5000; + while ((++timeout < MAX_TIMEOUT) && temp <= 1) + { + temp = (byte)Ring0.ReadSmbus(SMB_HSTSTS); + } + + if (timeout == MAX_TIMEOUT || (temp & 0x10) > 0 || (temp & 0x08) > 0 || (temp & 0x04) > 0) + { + return false; + } + + temp = (byte)Ring0.ReadSmbus(SMB_HSTSTS); + if (temp != 0x00) + { + Ring0.WriteSmbus(SMB_HSTSTS, temp); + } + + return true; + } + + private const byte SMB_READ = 0x01; + private const byte SMB_WRITE = 0x00; + + private const ushort SMB_ADDRESS = 0x0B00; + + private const ushort SMB_HSTSTS = (0 + SMB_ADDRESS); + //private const ushort SMB_HSLVSTS = (1 + SMB_ADDRESS); + private const ushort SMB_HSTCNT = (2 + SMB_ADDRESS); + private const ushort SMB_HSTCMD = (3 + SMB_ADDRESS); + private const ushort SMB_HSTADD = (4 + SMB_ADDRESS); + private const ushort SMB_HSTDAT0 = (5 + SMB_ADDRESS); + private const ushort SMB_HSTDAT1 = (6 + SMB_ADDRESS); + //private const ushort SMB_BLKDAT = (7 + SMB_ADDRESS); + //private const ushort SMB_SLVCNT = (8 + SMB_ADDRESS); + //private const ushort SMB_SHDWCMD = (9 + SMB_ADDRESS); + //private const ushort SMB_SLVEVT = (0xA + SMB_ADDRESS); + //private const ushort SMB_SLVDAT = (0xC + SMB_ADDRESS); + } +} diff --git a/LibreHardwareMonitorLib/Hardware/Memory/DimmSensor.cs b/LibreHardwareMonitorLib/Hardware/Memory/DimmSensor.cs new file mode 100644 index 000000000..c1528a780 --- /dev/null +++ b/LibreHardwareMonitorLib/Hardware/Memory/DimmSensor.cs @@ -0,0 +1,28 @@ +// 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 and Contributors. +// All Rights Reserved. + +using System; +using System.Collections.Generic; +using System.Runtime.InteropServices; +using System.Threading; + +namespace LibreHardwareMonitor.Hardware.Memory +{ + internal class DimmSensor : Sensor + { + protected byte _address; + + public DimmSensor(string name, int index, Hardware hardware, ISettings settings, byte address) : base(name, index, SensorType.Temperature, hardware, settings) + { + _address = address; + } + + public virtual void UpdateSensor() + { + + } + } +} \ No newline at end of file diff --git a/LibreHardwareMonitorLib/Hardware/Memory/GenericMemory.cs b/LibreHardwareMonitorLib/Hardware/Memory/GenericMemory.cs index 42063ec44..1a0c113fa 100644 --- a/LibreHardwareMonitorLib/Hardware/Memory/GenericMemory.cs +++ b/LibreHardwareMonitorLib/Hardware/Memory/GenericMemory.cs @@ -4,7 +4,10 @@ // Partial Copyright (C) Michael Möller and Contributors. // All Rights Reserved. +using System.Collections.Generic; +using System.Management; using System.Runtime.InteropServices; +using System.Threading; using LibreHardwareMonitor.Interop; namespace LibreHardwareMonitor.Hardware.Memory @@ -18,6 +21,8 @@ internal sealed class GenericMemory : Hardware private readonly Sensor _virtualMemoryLoad; private readonly Sensor _virtualMemoryUsed; + private readonly List _dimmSensorList = new List(); + public GenericMemory(string name, ISettings settings) : base(name, new Identifier("ram"), settings) { _physicalMemoryUsed = new Sensor("Memory Used", 0, SensorType.Data, this, settings); @@ -37,6 +42,93 @@ internal sealed class GenericMemory : Hardware _virtualMemoryLoad = new Sensor("Virtual Memory", 1, SensorType.Load, this, settings); ActivateSensor(_virtualMemoryLoad); + + + AddDimm(settings); + } + + private void AddDimm(ISettings settings) + { + try + { + string wmiQuery = "SELECT * FROM Win32_PnPSignedDriver WHERE Description LIKE '%%SMBUS%%' OR Description LIKE '%%SM BUS%%'"; + var searcher = new ManagementObjectSearcher(wmiQuery); + var collection = searcher.Get(); + string manufacturer = ""; + foreach (var obj in collection) + { + manufacturer = obj["Manufacturer"].ToString().ToUpper(); + if (manufacturer.Equals("INTEL") == true) + { + wmiQuery = "SELECT * FROM Win32_PnPAllocatedResource"; + string deviceID = obj["DeviceID"].ToString().Substring(4, 33); + + var searcher2 = new ManagementObjectSearcher(wmiQuery); + var collection2 = searcher2.Get(); + foreach (var obj2 in collection2) + { + string dependent = obj2["Dependent"].ToString(); + string antecedent = obj2["Antecedent"].ToString(); + + if (dependent.IndexOf(deviceID) >= 0 && antecedent.IndexOf("Port") >= 0) + { + var antecedentArray = antecedent.Split('='); + if (antecedentArray.Length >= 2) + { + string addressString = antecedentArray[1].Replace("\"", ""); + if (addressString.Length > 0) + { + ushort startAddress = ushort.Parse(addressString); + IntelDimmSensor.SetSMBAddress(startAddress); + + if (Ring0.WaitsmBusMutex(100)) + { + int index = 0; + for (byte addr = 0x18; addr <= 0x20; addr++) + { + var data = IntelDimmSensor.SmbDetect(addr); + if (data == addr) + { + var sensor = new IntelDimmSensor("DIMM #" + index, index, this, settings, addr); + _dimmSensorList.Add(sensor); + ActivateSensor(sensor); + } + Thread.Sleep(10); + index++; + } + Ring0.ReleasesmBusMutex(); + return; + } + } + } + } + } + } + + else if (manufacturer.Equals("ADVANCED MICRO DEVICES, INC") == true) + { + if (Ring0.WaitsmBusMutex(100)) + { + int index = 0; + for (byte addr = 0x18; addr <= 0x20; addr++) + { + var data = AmdDimmSensor.SmbDetect(addr); + if (data == addr) + { + var sensor = new AmdDimmSensor("DIMM #" + index, index, this, settings, addr); + _dimmSensorList.Add(sensor); + ActivateSensor(sensor); + } + Thread.Sleep(10); + index++; + } + Ring0.ReleasesmBusMutex(); + return; + } + } + } + } + catch { } } public override HardwareType HardwareType @@ -59,6 +151,19 @@ public override void Update() _virtualMemoryUsed.Value = (float)(status.ullTotalPageFile - status.ullAvailPageFile) / (1024 * 1024 * 1024); _virtualMemoryAvailable.Value = (float)status.ullAvailPageFile / (1024 * 1024 * 1024); _virtualMemoryLoad.Value = 100.0f - (100.0f * status.ullAvailPageFile) / status.ullTotalPageFile; + + + if (!Ring0.WaitsmBusMutex(10)) + return; + + for (int i = 0; i < _dimmSensorList.Count; i++) + { + _dimmSensorList[i].UpdateSensor(); + Thread.Sleep(10); + } + + Ring0.ReleasesmBusMutex(); + } } } diff --git a/LibreHardwareMonitorLib/Hardware/Memory/IntelDimmSensor.cs b/LibreHardwareMonitorLib/Hardware/Memory/IntelDimmSensor.cs new file mode 100644 index 000000000..f4c7ee1e1 --- /dev/null +++ b/LibreHardwareMonitorLib/Hardware/Memory/IntelDimmSensor.cs @@ -0,0 +1,209 @@ +// 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 and Contributors. +// All Rights Reserved. + +using System; +using System.Collections.Generic; +using System.Runtime.InteropServices; +using System.Threading; + +namespace LibreHardwareMonitor.Hardware.Memory +{ + internal sealed class IntelDimmSensor : DimmSensor + { + public IntelDimmSensor(string name, int index, Hardware hardware, ISettings settings, byte address) : base(name, index, hardware, settings, address) + { + + } + + public override void UpdateSensor() + { + try + { + ushort data = GetWord(_address, 0x05); + var temp = BitConverter.GetBytes(data); + Array.Reverse(temp); + + temp[1] = (byte)(temp[1] & 0x0F); + ushort count = BitConverter.ToUInt16(temp, 0); + double value = count * 0.0625f; + if (value > 0) + { + Value = (float)value; + } + } + catch { } + } + + public static byte SmbDetect(byte addr) + { + Ring0.WriteSmbus(SMBHSTADD, ((addr & 0x7f) << 1) | SMB_WRITE); + Ring0.WriteSmbus(SMBAUXCTL, Ring0.ReadSmbus(SMBAUXCTL) & (~SMBAUXCTL_CRC)); + + int res = Transaction(0x00); + + Ring0.WriteSmbus(SMBAUXCTL, Ring0.ReadSmbus(SMBAUXCTL) & ~(SMBAUXCTL_CRC | SMBAUXCTL_E32B)); + + return (byte)((res < 0) ? 0x00 : addr); + } + + private static ushort GetWord(byte addr, byte command) + { + Ring0.WriteSmbus(SMBHSTADD, ((addr & 0x7f) << 1) | SMB_READ); + Ring0.WriteSmbus(SMBHSTCMD, command); + + Ring0.WriteSmbus(SMBAUXCTL, Ring0.ReadSmbus(SMBAUXCTL) & (~SMBAUXCTL_CRC)); + + Transaction(0x0C); + + Ring0.WriteSmbus(SMBAUXCTL, Ring0.ReadSmbus(SMBAUXCTL) & ~(SMBAUXCTL_CRC | SMBAUXCTL_E32B)); + + ushort wordData = (ushort)(Ring0.ReadSmbus(SMBHSTDAT0) + (Ring0.ReadSmbus(SMBHSTDAT1) << 8)); + return wordData; + } + + private static int Transaction(byte xact) + { + int result = CheckPre(); + if (result < 0) + return result; + + Ring0.WriteSmbus(SMBHSTCNT, Ring0.ReadSmbus(SMBHSTCNT) & ~SMBHSTCNT_INTREN); + Ring0.WriteSmbus(SMBHSTCNT, xact | SMBHSTCNT_START); + + int status = WaitIntr(); + return CheckPost(status); + } + + private static int CheckPre() + { + ushort status = Ring0.ReadSmbus(SMBHSTSTS); + if ((status & SMBHSTSTS_HOST_BUSY) > 0) + { + return -1; + } + + status &= STATUS_FLAGS; + if (status > 0) + { + Ring0.WriteSmbus(SMBHSTSTS, status); + status = (ushort)(Ring0.ReadSmbus(SMBHSTSTS) & STATUS_FLAGS); + if (status > 0) + { + return -1; + } + } + return 0; + } + + private static int WaitIntr() + { + int maxCount = 400; + int timeout = 0; + ushort status; + bool val = false; + bool val2 = false; + + do + { + status = Ring0.ReadSmbus(SMBHSTSTS); + val = (status & SMBHSTSTS_HOST_BUSY) > 0; + val2 = (status & (STATUS_ERROR_FLAGS | SMBHSTSTS_INTR)) > 0; + + } while ((val || !val2) && timeout++ < maxCount); + + if (timeout > maxCount) + { + return -1; + } + return status & (STATUS_ERROR_FLAGS | SMBHSTSTS_INTR); + } + + private static int CheckPost(int status) + { + int result = 0; + if (status < 0) + { + Ring0.WriteSmbus(SMBHSTCNT, Ring0.ReadSmbus(SMBHSTCNT) | SMBHSTCNT_KILL); + Thread.Sleep(1); + Ring0.WriteSmbus(SMBHSTCNT, Ring0.ReadSmbus(SMBHSTCNT) & (~SMBHSTCNT_KILL)); + + Ring0.WriteSmbus(SMBHSTSTS, STATUS_FLAGS); + return -1; + } + + if ((status & SMBHSTSTS_FAILED) > 0 || (status & SMBHSTSTS_DEV_ERR) > 0 || (status & SMBHSTSTS_BUS_ERR) > 0) + { + result = -1; + } + + Ring0.WriteSmbus(SMBHSTSTS, status); + + return result; + } + + private const byte SMB_READ = 0x01; + private const byte SMB_WRITE = 0x00; + + private static ushort SMB_ADDRESS = 0; + + public static void SetSMBAddress(ushort address) + { + SMB_ADDRESS = address; + SMBHSTSTS = (ushort)(0 + SMB_ADDRESS); + SMBHSTCNT = (ushort)(2 + SMB_ADDRESS); + SMBHSTCMD = (ushort)(3 + SMB_ADDRESS); + SMBHSTADD = (ushort)(4 + SMB_ADDRESS); + SMBHSTDAT0 = (ushort)(5 + SMB_ADDRESS); + SMBHSTDAT1 = (ushort)(6 + SMB_ADDRESS); + //SMBBLKDAT = (ushort)(7 + SMB_ADDRESS); + //SMBPEC = (ushort)(8 + SMB_ADDRESS); + //SMBAUXSTS = (ushort)(12 + SMB_ADDRESS); + SMBAUXCTL = (ushort)(13 + SMB_ADDRESS); + //SMBSLVSTS = (ushort)(16 + SMB_ADDRESS); + //SMBSLVCMD = (ushort)(17 + SMB_ADDRESS); + //SMBNTFDADD = (ushort)(20 + SMB_ADDRESS); + } + + private static ushort SMBHSTSTS = 0; + private static ushort SMBHSTCNT = 0; + private static ushort SMBHSTCMD = 0; + private static ushort SMBHSTADD = 0; + private static ushort SMBHSTDAT0 = 0; + private static ushort SMBHSTDAT1 = 0; + //private static ushort SMBBLKDAT = 0; + //private static ushort SMBPEC = 0; + //private static ushort SMBAUXSTS = 0; + private static ushort SMBAUXCTL = 0; + //private static ushort SMBSLVSTS = 0; + //private static ushort SMBSLVCMD = 0; + //private static ushort SMBNTFDADD = 0; + + private static ushort SMBAUXCTL_CRC = (1 << 0); + private static ushort SMBAUXCTL_E32B = (1 << 1); + + private static ushort SMBHSTCNT_INTREN = (1 << 0); + private static ushort SMBHSTCNT_KILL = (1 << 1); + //private static ushort SMBHSTCNT_LAST_BYTE = (1 << 5); + private static ushort SMBHSTCNT_START = (1 << 6); + //private static ushort SMBHSTCNT_PEC_EN = (1 << 7); + + private static ushort SMBHSTSTS_BYTE_DONE = (1 << 7); + //private static ushort SMBHSTSTS_INUSE_STS = (1 << 6); + //private static ushort SMBHSTSTS_SMBALERT_STS = (1 << 5); + private static ushort SMBHSTSTS_FAILED = (1 << 4); + private static ushort SMBHSTSTS_BUS_ERR = (1 << 3); + private static ushort SMBHSTSTS_DEV_ERR = (1 << 2); + private static ushort SMBHSTSTS_INTR = (1 << 1); + private static ushort SMBHSTSTS_HOST_BUSY = (1 << 0); + + //private static ushort SMBSLVSTS_HST_NTFY_STS = (1 << 0); + + //private static ushort SMBSLVCMD_HST_NTFY_INTREN = (1 << 0); + + private static ushort STATUS_ERROR_FLAGS = (ushort)(SMBHSTSTS_FAILED | SMBHSTSTS_BUS_ERR | SMBHSTSTS_DEV_ERR); + private static ushort STATUS_FLAGS = (ushort)(SMBHSTSTS_BYTE_DONE | SMBHSTSTS_INTR | STATUS_ERROR_FLAGS); + } +} \ No newline at end of file diff --git a/LibreHardwareMonitorLib/Hardware/Ring0.cs b/LibreHardwareMonitorLib/Hardware/Ring0.cs index ef5bbc939..90ef2db9f 100644 --- a/LibreHardwareMonitorLib/Hardware/Ring0.cs +++ b/LibreHardwareMonitorLib/Hardware/Ring0.cs @@ -23,6 +23,8 @@ internal static class Ring0 private static Mutex _isaBusMutex; private static Mutex _pciBusMutex; + private static Mutex _smBusMutex; + private static readonly StringBuilder _report = new(); public static bool IsOpen => _driver != null; @@ -137,6 +139,26 @@ public static void Open() _pciBusMutex = Mutex.OpenExisting(pciMutexName, MutexRights.Synchronize); #else _pciBusMutex = Mutex.OpenExisting(pciMutexName); +#endif + } + catch + { } + } + + const string smbusMutexName = "Global\\Access_SMBUS.HTP.Method"; + + try + { + _smBusMutex = new Mutex(false, smbusMutexName); + } + catch (UnauthorizedAccessException) + { + try + { +#if NETFRAMEWORK + _smBusMutex = Mutex.OpenExisting(smbusMutexName, MutexRights.Synchronize); +#else + _smBusMutex = Mutex.OpenExisting(smbusMutexName); #endif } catch @@ -355,6 +377,12 @@ public static void Close() _pciBusMutex = null; } + if (_smBusMutex != null) + { + _smBusMutex.Close(); + _smBusMutex = null; + } + // try to delete temporary driver file again if failed during open DeleteDriver(); } @@ -424,6 +452,31 @@ public static void ReleasePciBusMutex() _pciBusMutex?.ReleaseMutex(); } + public static bool WaitsmBusMutex(int millisecondsTimeout) + { + if (_smBusMutex == null) + return true; + + + try + { + return _smBusMutex.WaitOne(millisecondsTimeout, false); + } + catch (AbandonedMutexException) + { + return true; + } + catch (InvalidOperationException) + { + return false; + } + } + + public static void ReleasesmBusMutex() + { + _smBusMutex?.ReleaseMutex(); + } + public static bool ReadMsr(uint index, out uint eax, out uint edx) { if (_driver == null) @@ -479,6 +532,26 @@ public static void WriteIoPort(uint port, byte value) _driver.DeviceIOControl(Interop.Ring0.IOCTL_OLS_WRITE_IO_PORT_BYTE, input); } + public static ushort ReadSmbus(ushort port) + { + if (_driver == null) + return 0; + + uint value = 0; + _driver.DeviceIOControl(Interop.Ring0.IOCTL_OLS_READ_IO_PORT_BYTE, port, ref value); + ushort retVal = (ushort)(value & 0xff); + return retVal; + } + + public static void WriteSmbus(ushort port, int value) + { + if (_driver == null) + return; + + WriteIoPortInput input = new WriteIoPortInput { PortNumber = port, Value = (byte)(value & 0xff) }; + _driver.DeviceIOControl(Interop.Ring0.IOCTL_OLS_WRITE_IO_PORT_BYTE, input); + } + public static uint GetPciAddress(byte bus, byte device, byte function) { return (uint)(((bus & 0xFF) << 8) | ((device & 0x1F) << 3) | (function & 7));