diff --git a/LibreHardwareMonitorLib/Hardware/Memory/DimmMemory.cs b/LibreHardwareMonitorLib/Hardware/Memory/DimmMemory.cs new file mode 100644 index 0000000..f7f0e8c --- /dev/null +++ b/LibreHardwareMonitorLib/Hardware/Memory/DimmMemory.cs @@ -0,0 +1,64 @@ +// 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 LibreHardwareMonitor.Hardware.Memory.Sensors; +using RAMSPDToolkit.SPD; +using RAMSPDToolkit.SPD.Enums; +using RAMSPDToolkit.SPD.Interfaces; +using RAMSPDToolkit.SPD.Interop.Shared; + +namespace LibreHardwareMonitor.Hardware.Memory; + +internal sealed class DimmMemory : Hardware +{ + private readonly SpdThermalSensor _thermalSensor; + + public DimmMemory(SPDAccessor accessor, string name, Identifier identifier, ISettings settings) + : base(name, identifier, settings) + { + //Check which kind of RAM we have + switch (accessor.MemoryType()) + { + case SPDMemoryType.SPD_DDR4_SDRAM: + case SPDMemoryType.SPD_DDR4E_SDRAM: + case SPDMemoryType.SPD_LPDDR4_SDRAM: + case SPDMemoryType.SPD_LPDDR4X_SDRAM: + _thermalSensor = new SpdThermalSensor($"DIMM #{accessor.Index}", + accessor.Index, + SensorType.Temperature, + this, + settings, + accessor as IThermalSensor); + + break; + case SPDMemoryType.SPD_DDR5_SDRAM: + case SPDMemoryType.SPD_LPDDR5_SDRAM: + //Check if we are on correct page or if write protection is not enabled + if (accessor.PageData.HasFlag(PageData.ThermalData) || !accessor.HasSPDWriteProtection) + { + _thermalSensor = new SpdThermalSensor($"DIMM #{accessor.Index}", + accessor.Index, + SensorType.Temperature, + this, + settings, + accessor as IThermalSensor); + } + + break; + } + + //Add thermal sensor + if (_thermalSensor != null) + ActivateSensor(_thermalSensor); + } + + public override HardwareType HardwareType => HardwareType.Memory; + + public override void Update() + { + _thermalSensor?.UpdateSensor(); + } +} diff --git a/LibreHardwareMonitorLib/Hardware/Memory/GenericLinuxMemory.cs b/LibreHardwareMonitorLib/Hardware/Memory/GenericLinuxMemory.cs deleted file mode 100644 index 6c597f5..0000000 --- a/LibreHardwareMonitorLib/Hardware/Memory/GenericLinuxMemory.cs +++ /dev/null @@ -1,92 +0,0 @@ -// 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.IO; -using System.Linq; - -namespace LibreHardwareMonitor.Hardware.Memory; - -internal sealed class GenericLinuxMemory : Hardware -{ - private readonly Sensor _physicalMemoryAvailable; - private readonly Sensor _physicalMemoryLoad; - private readonly Sensor _physicalMemoryUsed; - private readonly Sensor _virtualMemoryAvailable; - private readonly Sensor _virtualMemoryLoad; - private readonly Sensor _virtualMemoryUsed; - - public override HardwareType HardwareType => HardwareType.Memory; - - public GenericLinuxMemory(string name, ISettings settings) : base(name, new Identifier("ram"), settings) - { - _physicalMemoryUsed = new Sensor("Memory Used", 0, SensorType.Data, this, settings); - ActivateSensor(_physicalMemoryUsed); - - _physicalMemoryAvailable = new Sensor("Memory Available", 1, SensorType.Data, this, settings); - ActivateSensor(_physicalMemoryAvailable); - - _physicalMemoryLoad = new Sensor("Memory", 0, SensorType.Load, this, settings); - ActivateSensor(_physicalMemoryLoad); - - _virtualMemoryUsed = new Sensor("Virtual Memory Used", 2, SensorType.Data, this, settings); - ActivateSensor(_virtualMemoryUsed); - - _virtualMemoryAvailable = new Sensor("Virtual Memory Available", 3, SensorType.Data, this, settings); - ActivateSensor(_virtualMemoryAvailable); - - _virtualMemoryLoad = new Sensor("Virtual Memory", 1, SensorType.Load, this, settings); - ActivateSensor(_virtualMemoryLoad); - } - - public override void Update() - { - try - { - string[] memoryInfo = File.ReadAllLines("/proc/meminfo"); - - { - float totalMemory_GB = GetMemInfoValue(memoryInfo.First(entry => entry.StartsWith("MemTotal:"))) / 1024.0f / 1024.0f; - float freeMemory_GB = GetMemInfoValue(memoryInfo.First(entry => entry.StartsWith("MemFree:"))) / 1024.0f / 1024.0f; - float cachedMemory_GB = GetMemInfoValue(memoryInfo.First(entry => entry.StartsWith("Cached:"))) / 1024.0f / 1024.0f; - - float usedMemory_GB = totalMemory_GB - freeMemory_GB - cachedMemory_GB; - - _physicalMemoryUsed.Value = usedMemory_GB; - _physicalMemoryAvailable.Value = totalMemory_GB; - _physicalMemoryLoad.Value = 100.0f * (usedMemory_GB / totalMemory_GB); - } - { - float totalSwapMemory_GB = GetMemInfoValue(memoryInfo.First(entry => entry.StartsWith("SwapTotal"))) / 1024.0f / 1024.0f; - float freeSwapMemory_GB = GetMemInfoValue(memoryInfo.First(entry => entry.StartsWith("SwapFree"))) / 1024.0f / 1024.0f; - float usedSwapMemory_GB = totalSwapMemory_GB - freeSwapMemory_GB; - - _virtualMemoryUsed.Value = usedSwapMemory_GB; - _virtualMemoryAvailable.Value = totalSwapMemory_GB; - _virtualMemoryLoad.Value = 100.0f * (usedSwapMemory_GB / totalSwapMemory_GB); - } - } - catch - { - _physicalMemoryUsed.Value = null; - _physicalMemoryAvailable.Value = null; - _physicalMemoryLoad.Value = null; - - _virtualMemoryUsed.Value = null; - _virtualMemoryAvailable.Value = null; - _virtualMemoryLoad.Value = null; - } - } - - private static long GetMemInfoValue(string line) - { - // Example: "MemTotal: 32849676 kB" - - string valueWithUnit = line.Split(':').Skip(1).First().Trim(); - string valueAsString = valueWithUnit.Split(' ').First(); - - return long.Parse(valueAsString); - } -} diff --git a/LibreHardwareMonitorLib/Hardware/Memory/GenericWindowsMemory.cs b/LibreHardwareMonitorLib/Hardware/Memory/GenericWindowsMemory.cs deleted file mode 100644 index 0d41066..0000000 --- a/LibreHardwareMonitorLib/Hardware/Memory/GenericWindowsMemory.cs +++ /dev/null @@ -1,62 +0,0 @@ -// 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.Runtime.InteropServices; -using LibreHardwareMonitor.Interop; - -namespace LibreHardwareMonitor.Hardware.Memory; - -internal sealed class GenericWindowsMemory : Hardware -{ - private readonly Sensor _physicalMemoryAvailable; - private readonly Sensor _physicalMemoryLoad; - private readonly Sensor _physicalMemoryUsed; - private readonly Sensor _virtualMemoryAvailable; - private readonly Sensor _virtualMemoryLoad; - private readonly Sensor _virtualMemoryUsed; - - public GenericWindowsMemory(string name, ISettings settings) : base(name, new Identifier("ram"), settings) - { - _physicalMemoryUsed = new Sensor("Memory Used", 0, SensorType.Data, this, settings); - ActivateSensor(_physicalMemoryUsed); - - _physicalMemoryAvailable = new Sensor("Memory Available", 1, SensorType.Data, this, settings); - ActivateSensor(_physicalMemoryAvailable); - - _physicalMemoryLoad = new Sensor("Memory", 0, SensorType.Load, this, settings); - ActivateSensor(_physicalMemoryLoad); - - _virtualMemoryUsed = new Sensor("Virtual Memory Used", 2, SensorType.Data, this, settings); - ActivateSensor(_virtualMemoryUsed); - - _virtualMemoryAvailable = new Sensor("Virtual Memory Available", 3, SensorType.Data, this, settings); - ActivateSensor(_virtualMemoryAvailable); - - _virtualMemoryLoad = new Sensor("Virtual Memory", 1, SensorType.Load, this, settings); - ActivateSensor(_virtualMemoryLoad); - } - - public override HardwareType HardwareType - { - get { return HardwareType.Memory; } - } - - public override void Update() - { - Kernel32.MEMORYSTATUSEX status = new() { dwLength = (uint)Marshal.SizeOf() }; - - if (!Kernel32.GlobalMemoryStatusEx(ref status)) - return; - - _physicalMemoryUsed.Value = (float)(status.ullTotalPhys - status.ullAvailPhys) / (1024 * 1024 * 1024); - _physicalMemoryAvailable.Value = (float)status.ullAvailPhys / (1024 * 1024 * 1024); - _physicalMemoryLoad.Value = 100.0f - ((100.0f * status.ullAvailPhys) / status.ullTotalPhys); - - _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); - } -} \ No newline at end of file diff --git a/LibreHardwareMonitorLib/Hardware/Memory/MemoryGroup.cs b/LibreHardwareMonitorLib/Hardware/Memory/MemoryGroup.cs index c437635..9d25cd3 100644 --- a/LibreHardwareMonitorLib/Hardware/Memory/MemoryGroup.cs +++ b/LibreHardwareMonitorLib/Hardware/Memory/MemoryGroup.cs @@ -5,28 +5,137 @@ // All Rights Reserved. using System.Collections.Generic; +using System.Timers; +using RAMSPDToolkit.I2CSMBus; +using RAMSPDToolkit.SPD; +using RAMSPDToolkit.SPD.Enums; +using RAMSPDToolkit.SPD.Interfaces; +using RAMSPDToolkit.SPD.Interop.Shared; +using RAMSPDToolkit.Windows.Driver; namespace LibreHardwareMonitor.Hardware.Memory; internal class MemoryGroup : IGroup { - private readonly Hardware[] _hardware; + //Retry 12x + private const int RetryCount = 12; + + //Retry every 2.5 seconds + private const double RetryTime = 2500; + private static readonly object _lock = new(); + + private readonly List _hardware = []; + private int _elapsedCounter; + + private Timer _timer; + + static MemoryGroup() + { + if (Ring0.IsOpen) + { + //Assign implementation of IDriver + DriverManager.Driver = new RAMSPDToolkitDriver(Ring0.KernelDriver); + SMBusManager.UseWMI = false; + } + } public MemoryGroup(ISettings settings) { - _hardware = new Hardware[] { Software.OperatingSystem.IsUnix ? new GenericLinuxMemory("Generic Memory", settings) : new GenericWindowsMemory("Generic Memory", settings) }; + _hardware.Add(new VirtualMemory(settings)); + _hardware.Add(new TotalMemory(settings)); + + //No RAM detected + if (!DetectThermalSensors(out List accessors)) + { + //Retry a couple of times + //SMBus might not be detected right after boot + _timer = new Timer(RetryTime); + + _timer.Elapsed += (_, _) => + { + if (_elapsedCounter++ >= RetryCount || DetectThermalSensors(out accessors)) + { + _timer.Stop(); + _timer = null; + + if (accessors != null) + AddDimms(accessors, settings); + } + }; + + _timer.Start(); + } + else + { + AddDimms(accessors, settings); + } } + public IReadOnlyList Hardware => _hardware; + public string GetReport() { return null; } - public IReadOnlyList Hardware => _hardware; - public void Close() { foreach (Hardware ram in _hardware) ram.Close(); } + + private static bool DetectThermalSensors(out List accessors) + { + lock (_lock) + { + var list = new List(); + + bool ramDetected = false; + + SMBusManager.DetectSMBuses(); + + //Go through detected SMBuses + foreach (var smbus in SMBusManager.RegisteredSMBuses) + { + //Go through possible RAM slots + for (byte i = SPDConstants.SPD_BEGIN; i <= SPDConstants.SPD_END; ++i) + { + //Detect type of RAM, if available + var detector = new SPDDetector(smbus, i); + + //RAM available and detected + if (detector.Accessor != null) + { + //We are only interested in modules with thermal sensor + if (detector.Accessor is IThermalSensor { HasThermalSensor: true }) + list.Add(detector.Accessor); + + ramDetected = true; + } + } + } + + accessors = list.Count > 0 ? list : []; + return ramDetected; + } + } + + private void AddDimms(List accessors, ISettings settings) + { + foreach (var ram in accessors) + { + //Default value + string name = $"DIMM #{ram.Index}"; + + //Check if we can switch to the correct page + if (ram.ChangePage(PageData.ModulePartNumber)) + { + name = $"{ram.GetModuleManufacturerString()} - {ram.ModulePartNumber()} (#{ram.Index})"; + } + + var memory = new DimmMemory(ram, name, new Identifier("ram"), settings); + + _hardware.Add(memory); + } + } } diff --git a/LibreHardwareMonitorLib/Hardware/Memory/MemoryLinux.cs b/LibreHardwareMonitorLib/Hardware/Memory/MemoryLinux.cs new file mode 100644 index 0000000..4309b42 --- /dev/null +++ b/LibreHardwareMonitorLib/Hardware/Memory/MemoryLinux.cs @@ -0,0 +1,73 @@ +// 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.IO; +using System.Linq; + +namespace LibreHardwareMonitor.Hardware.Memory; + +internal static class MemoryLinux +{ + public static void Update(TotalMemory memory) + { + try + { + string[] memoryInfo = File.ReadAllLines("/proc/meminfo"); + + { + float totalMemoryGb = GetMemInfoValue(memoryInfo.First(entry => entry.StartsWith("MemTotal:"))) / 1024.0f / 1024.0f; + float freeMemoryGb = GetMemInfoValue(memoryInfo.First(entry => entry.StartsWith("MemFree:"))) / 1024.0f / 1024.0f; + float cachedMemoryGb = GetMemInfoValue(memoryInfo.First(entry => entry.StartsWith("Cached:"))) / 1024.0f / 1024.0f; + + float usedMemoryGb = totalMemoryGb - freeMemoryGb - cachedMemoryGb; + + memory.PhysicalMemoryUsed.Value = usedMemoryGb; + memory.PhysicalMemoryAvailable.Value = totalMemoryGb; + memory.PhysicalMemoryLoad.Value = 100.0f * (usedMemoryGb / totalMemoryGb); + } + } + catch + { + memory.PhysicalMemoryUsed.Value = null; + memory.PhysicalMemoryAvailable.Value = null; + memory.PhysicalMemoryLoad.Value = null; + } + } + + public static void Update(VirtualMemory memory) + { + try + { + string[] memoryInfo = File.ReadAllLines("/proc/meminfo"); + + { + float totalSwapMemoryGb = GetMemInfoValue(memoryInfo.First(entry => entry.StartsWith("SwapTotal"))) / 1024.0f / 1024.0f; + float freeSwapMemoryGb = GetMemInfoValue(memoryInfo.First(entry => entry.StartsWith("SwapFree"))) / 1024.0f / 1024.0f; + float usedSwapMemoryGb = totalSwapMemoryGb - freeSwapMemoryGb; + + memory.VirtualMemoryUsed.Value = usedSwapMemoryGb; + memory.VirtualMemoryAvailable.Value = totalSwapMemoryGb; + memory.VirtualMemoryLoad.Value = 100.0f * (usedSwapMemoryGb / totalSwapMemoryGb); + } + } + catch + { + memory.VirtualMemoryUsed.Value = null; + memory.VirtualMemoryAvailable.Value = null; + memory.VirtualMemoryLoad.Value = null; + } + } + + private static long GetMemInfoValue(string line) + { + // Example: "MemTotal: 32849676 kB" + + string valueWithUnit = line.Split(':').Skip(1).First().Trim(); + string valueAsString = valueWithUnit.Split(' ').First(); + + return long.Parse(valueAsString); + } +} diff --git a/LibreHardwareMonitorLib/Hardware/Memory/MemoryWindows.cs b/LibreHardwareMonitorLib/Hardware/Memory/MemoryWindows.cs new file mode 100644 index 0000000..15b9998 --- /dev/null +++ b/LibreHardwareMonitorLib/Hardware/Memory/MemoryWindows.cs @@ -0,0 +1,37 @@ +// 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.Runtime.InteropServices; +using LibreHardwareMonitor.Interop; + +namespace LibreHardwareMonitor.Hardware.Memory; + +internal static class MemoryWindows +{ + public static void Update(TotalMemory memory) + { + Kernel32.MEMORYSTATUSEX status = new() { dwLength = (uint)Marshal.SizeOf() }; + + if (!Kernel32.GlobalMemoryStatusEx(ref status)) + return; + + memory.PhysicalMemoryUsed.Value = (float)(status.ullTotalPhys - status.ullAvailPhys) / (1024 * 1024 * 1024); + memory.PhysicalMemoryAvailable.Value = (float)status.ullAvailPhys / (1024 * 1024 * 1024); + memory.PhysicalMemoryLoad.Value = 100.0f - ((100.0f * status.ullAvailPhys) / status.ullTotalPhys); + } + + public static void Update(VirtualMemory memory) + { + Kernel32.MEMORYSTATUSEX status = new() { dwLength = (uint)Marshal.SizeOf() }; + + if (!Kernel32.GlobalMemoryStatusEx(ref status)) + return; + + memory.VirtualMemoryUsed.Value = (float)(status.ullTotalPageFile - status.ullAvailPageFile) / (1024 * 1024 * 1024); + memory.VirtualMemoryAvailable.Value = (float)status.ullAvailPageFile / (1024 * 1024 * 1024); + memory.VirtualMemoryLoad.Value = 100.0f - ((100.0f * status.ullAvailPageFile) / status.ullTotalPageFile); + } +} diff --git a/LibreHardwareMonitorLib/Hardware/Memory/Sensors/SpdThermalSensor.cs b/LibreHardwareMonitorLib/Hardware/Memory/Sensors/SpdThermalSensor.cs new file mode 100644 index 0000000..e2add21 --- /dev/null +++ b/LibreHardwareMonitorLib/Hardware/Memory/Sensors/SpdThermalSensor.cs @@ -0,0 +1,23 @@ +// 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 RAMSPDToolkit.SPD.Interfaces; + +namespace LibreHardwareMonitor.Hardware.Memory.Sensors; + +internal class SpdThermalSensor(string name, int index, SensorType sensorType, Hardware hardware, ISettings settings, IThermalSensor thermalSensor) + : Sensor(name, index, sensorType, hardware, settings) +{ + public bool UpdateSensor() + { + if (!thermalSensor.UpdateTemperature()) + return false; + + Value = thermalSensor.Temperature; + + return true; + } +} diff --git a/LibreHardwareMonitorLib/Hardware/Memory/TotalMemory.cs b/LibreHardwareMonitorLib/Hardware/Memory/TotalMemory.cs new file mode 100644 index 0000000..ecce1d7 --- /dev/null +++ b/LibreHardwareMonitorLib/Hardware/Memory/TotalMemory.cs @@ -0,0 +1,43 @@ +// 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. + +namespace LibreHardwareMonitor.Hardware.Memory; + +internal sealed class TotalMemory : Hardware +{ + public TotalMemory(ISettings settings) + : base("Total Memory", new Identifier("ram"), settings) + { + PhysicalMemoryUsed = new Sensor("Memory Used", 0, SensorType.Data, this, settings); + ActivateSensor(PhysicalMemoryUsed); + + PhysicalMemoryAvailable = new Sensor("Memory Available", 1, SensorType.Data, this, settings); + ActivateSensor(PhysicalMemoryAvailable); + + PhysicalMemoryLoad = new Sensor("Memory", 0, SensorType.Load, this, settings); + ActivateSensor(PhysicalMemoryLoad); + } + + public override HardwareType HardwareType => HardwareType.Memory; + + internal Sensor PhysicalMemoryAvailable { get; } + + internal Sensor PhysicalMemoryLoad { get; } + + internal Sensor PhysicalMemoryUsed { get; } + + public override void Update() + { + if (Software.OperatingSystem.IsUnix) + { + MemoryLinux.Update(this); + } + else + { + MemoryWindows.Update(this); + } + } +} diff --git a/LibreHardwareMonitorLib/Hardware/Memory/VirtualMemory.cs b/LibreHardwareMonitorLib/Hardware/Memory/VirtualMemory.cs new file mode 100644 index 0000000..673da52 --- /dev/null +++ b/LibreHardwareMonitorLib/Hardware/Memory/VirtualMemory.cs @@ -0,0 +1,43 @@ +// 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. + +namespace LibreHardwareMonitor.Hardware.Memory; + +internal sealed class VirtualMemory : Hardware +{ + public VirtualMemory(ISettings settings) + : base("Virtual Memory", new Identifier("ram"), settings) + { + VirtualMemoryUsed = new Sensor("Memory Used", 2, SensorType.Data, this, settings); + ActivateSensor(VirtualMemoryUsed); + + VirtualMemoryAvailable = new Sensor("Memory Available", 3, SensorType.Data, this, settings); + ActivateSensor(VirtualMemoryAvailable); + + VirtualMemoryLoad = new Sensor("Memory", 1, SensorType.Load, this, settings); + ActivateSensor(VirtualMemoryLoad); + } + + public override HardwareType HardwareType => HardwareType.Memory; + + internal Sensor VirtualMemoryAvailable { get; } + + internal Sensor VirtualMemoryLoad { get; } + + internal Sensor VirtualMemoryUsed { get; } + + public override void Update() + { + if (Software.OperatingSystem.IsUnix) + { + MemoryLinux.Update(this); + } + else + { + MemoryWindows.Update(this); + } + } +} diff --git a/LibreHardwareMonitorLib/Hardware/RAMSPDToolkitDriver.cs b/LibreHardwareMonitorLib/Hardware/RAMSPDToolkitDriver.cs new file mode 100644 index 0000000..a85846d --- /dev/null +++ b/LibreHardwareMonitorLib/Hardware/RAMSPDToolkitDriver.cs @@ -0,0 +1,414 @@ +// 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.Runtime.InteropServices; +using RAMSPDToolkit.Windows.Driver; + +using IOCC = LibreHardwareMonitor.Interop.Ring0; + +namespace LibreHardwareMonitor.Hardware +{ + /// + /// Implementation of interface for RAMSPDToolkit. + /// + internal class RAMSPDToolkitDriver : IDriver + { + private KernelDriver _kernelDriver; + + private const byte PCI_MAX_NUMBER_OF_BUS = 255; + private const byte PCI_NUMBER_OF_DEVICE = 32; + private const byte PCI_NUMBER_OF_FUNCTION = 8; + + public bool IsOpen => _kernelDriver != null; + + public RAMSPDToolkitDriver(KernelDriver kernelDriver) + { + _kernelDriver = kernelDriver; + } + + //Driver is being managed internally by other classes, so nothing to do in Load + Unload + + public bool Load() + { + return true; + } + + public void Unload() + { + } + + public byte ReadIoPortByte(ushort port) + { + byte value = 0; + + ReadIoPortByteEx(port, ref value); + + return value; + } + + public ushort ReadIoPortWord(ushort port) + { + ushort value = 0; + + ReadIoPortWordEx(port, ref value); + + return value; + } + + public uint ReadIoPortDword(ushort port) + { + uint value = 0; + + ReadIoPortDwordEx(port, ref value); + + return value; + } + + public bool ReadIoPortByteEx(ushort port, ref byte value) + { + if (!IsOpen) + return false; + + return _kernelDriver.DeviceIOControl(IOCC.IOCTL_OLS_READ_IO_PORT_BYTE, port, ref value); + } + + public bool ReadIoPortWordEx(ushort port, ref ushort value) + { + if (!IsOpen) + return false; + + return _kernelDriver.DeviceIOControl(IOCC.IOCTL_OLS_READ_IO_PORT_WORD, port, ref value); + } + + public bool ReadIoPortDwordEx(ushort port, ref uint value) + { + if (!IsOpen) + return false; + + return _kernelDriver.DeviceIOControl(IOCC.IOCTL_OLS_READ_IO_PORT_DWORD, port, ref value); + } + + public void WriteIoPortByte(ushort port, byte value) + { + WriteIoPortByteEx(port, value); + } + + public void WriteIoPortWord(ushort port, ushort value) + { + WriteIoPortWordEx(port, value); + } + + public void WriteIoPortDword(ushort port, uint value) + { + WriteIoPortDwordEx(port, value); + } + + public bool WriteIoPortByteEx(ushort port, byte value) + { + if (!IsOpen) + return false; + + var input = new WriteIoPortInputByte { PortNumber = port, Value = value }; + return _kernelDriver.DeviceIOControl(IOCC.IOCTL_OLS_WRITE_IO_PORT_BYTE, input); + } + + public bool WriteIoPortWordEx(ushort port, ushort value) + { + if (!IsOpen) + return false; + + var input = new WriteIoPortInputWord { PortNumber = port, Value = value }; + return _kernelDriver.DeviceIOControl(IOCC.IOCTL_OLS_WRITE_IO_PORT_WORD, input); + } + + public bool WriteIoPortDwordEx(ushort port, uint value) + { + if (!IsOpen) + return false; + + var input = new WriteIoPortInputDword { PortNumber = port, Value = value }; + return _kernelDriver.DeviceIOControl(IOCC.IOCTL_OLS_WRITE_IO_PORT_DWORD, input); + } + + public uint FindPciDeviceById(ushort vendorId, ushort deviceId, byte index) + { + if (!IsOpen) + return uint.MaxValue; + + if (vendorId == ushort.MaxValue) + { + return uint.MaxValue; + } + + uint count = 0; + + for (byte bus = 0; bus < PCI_MAX_NUMBER_OF_BUS; ++bus) + { + for (byte dev = 0; dev < PCI_NUMBER_OF_DEVICE; ++dev) + { + bool multiFuncFlag = false; + + for (byte func = 0; func < PCI_NUMBER_OF_FUNCTION; ++func) + { + if (!multiFuncFlag && func > 0) + { + break; + } + + uint pciAddress = Ring0.GetPciAddress(bus, dev, func); + + uint id = 0; + + if (PciConfigRead(pciAddress, 0, ref id)) + { + if (func == 0) //Is Multi Function Device + { + byte type = 0; + + if (PciConfigRead(pciAddress, 0x0E, ref type)) + { + if ((type & 0x80) != 0) + { + multiFuncFlag = true; + } + } + } + + if (id == (vendorId | ((uint)deviceId << 16))) + { + if (count == index) + { + return pciAddress; + } + + ++count; + continue; + } + } + } + } + } + + return uint.MaxValue; + } + + public uint FindPciDeviceByClass(byte baseClass, byte subClass, byte programIf, byte index) + { + if (!IsOpen) + return uint.MaxValue; + + uint count = 0; + + for (byte bus = 0; bus < PCI_MAX_NUMBER_OF_BUS; ++bus) + { + for (byte dev = 0; dev < PCI_NUMBER_OF_DEVICE; ++dev) + { + bool multiFuncFlag = false; + + for (byte func = 0; func < PCI_NUMBER_OF_FUNCTION; ++func) + { + if (!multiFuncFlag && func > 0) + { + break; + } + + uint pciAddress = Ring0.GetPciAddress(bus, dev, func); + + var conf = new Conf(); + + if (PciConfigRead(pciAddress, 0, ref conf)) + { + if (func == 0) //Is Multi Function Device + { + byte type = 0; + + if (PciConfigRead(pciAddress, 0x0E, ref type)) + { + if ((type & 0x80) != 0) + { + multiFuncFlag = true; + } + } + } + + var temp = (uint)baseClass << 24 | (uint)subClass << 16 | (uint)programIf << 8; + + if ((conf.C & 0xFFFFFF00) == temp) + { + if (count == index) + { + return pciAddress; + } + + ++count; + continue; + } + } + } + } + } + + return uint.MaxValue; + } + + public byte ReadPciConfigByte(uint pciAddress, byte regAddress) + { + byte value = 0; + + if (ReadPciConfigByteEx(pciAddress, regAddress, ref value)) + { + return value; + } + else + { + return byte.MaxValue; + } + } + + public ushort ReadPciConfigWord(uint pciAddress, byte regAddress) + { + ushort value = 0; + + if (ReadPciConfigWordEx(pciAddress, regAddress, ref value)) + { + return value; + } + else + { + return ushort.MaxValue; + } + } + + public uint ReadPciConfigDword(uint pciAddress, byte regAddress) + { + uint value = 0; + + if (ReadPciConfigDwordEx(pciAddress, regAddress, ref value)) + { + return value; + } + else + { + return uint.MaxValue; + } + } + + public bool ReadPciConfigByteEx(uint pciAddress, uint regAddress, ref byte value) + { + return PciConfigRead(pciAddress, regAddress, ref value); + } + + public bool ReadPciConfigWordEx(uint pciAddress, uint regAddress, ref ushort value) + { + return PciConfigRead(pciAddress, regAddress, ref value); + } + + public bool ReadPciConfigDwordEx(uint pciAddress, uint regAddress, ref uint value) + { + return PciConfigRead(pciAddress, regAddress, ref value); + } + + public void WritePciConfigByte(uint pciAddress, byte regAddress, byte value) + { + WritePciConfigByteEx(pciAddress, regAddress, value); + } + + public void WritePciConfigWord(uint pciAddress, byte regAddress, ushort value) + { + WritePciConfigWordEx(pciAddress, regAddress, value); + } + + public void WritePciConfigDword(uint pciAddress, byte regAddress, uint value) + { + WritePciConfigDwordEx(pciAddress, regAddress, value); + } + + public bool WritePciConfigByteEx(uint pciAddress, uint regAddress, byte value) + { + var input = new WritePciConfigInputByte { PciAddress = pciAddress, RegAddress = regAddress, Value = value }; + return _kernelDriver.DeviceIOControl(IOCC.IOCTL_OLS_WRITE_PCI_CONFIG, input); + } + + public bool WritePciConfigWordEx(uint pciAddress, uint regAddress, ushort value) + { + var input = new WritePciConfigInputWord { PciAddress = pciAddress, RegAddress = regAddress, Value = value }; + return _kernelDriver.DeviceIOControl(IOCC.IOCTL_OLS_WRITE_PCI_CONFIG, input); + } + + public bool WritePciConfigDwordEx(uint pciAddress, uint regAddress, uint value) + { + var input = new WritePciConfigInputDword { PciAddress = pciAddress, RegAddress = regAddress, Value = value }; + return _kernelDriver.DeviceIOControl(IOCC.IOCTL_OLS_WRITE_PCI_CONFIG, input); + } + + private bool PciConfigRead(uint pciAddress, uint regAddress, ref TValue value) + where TValue : struct + { + var input = new ReadPciConfigInput { PciAddress = pciAddress, RegAddress = regAddress }; + return _kernelDriver.DeviceIOControl(IOCC.IOCTL_OLS_READ_PCI_CONFIG, input, ref value); + } + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + private struct WriteIoPortInputByte + { + public uint PortNumber; + public byte Value; + } + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + private struct WriteIoPortInputWord + { + public uint PortNumber; + public ushort Value; + } + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + private struct WriteIoPortInputDword + { + public uint PortNumber; + public uint Value; + } + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + private struct ReadPciConfigInput + { + public uint PciAddress; + public uint RegAddress; + } + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + private struct WritePciConfigInputByte + { + public uint PciAddress; + public uint RegAddress; + public byte Value; + } + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + private struct WritePciConfigInputWord + { + public uint PciAddress; + public uint RegAddress; + public ushort Value; + } + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + private struct WritePciConfigInputDword + { + public uint PciAddress; + public uint RegAddress; + public uint Value; + } + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + private struct Conf + { + public uint A; + public uint B; + public uint C; + } + } +} diff --git a/LibreHardwareMonitorLib/Hardware/Ring0.cs b/LibreHardwareMonitorLib/Hardware/Ring0.cs index 680eec0..6dcbef9 100644 --- a/LibreHardwareMonitorLib/Hardware/Ring0.cs +++ b/LibreHardwareMonitorLib/Hardware/Ring0.cs @@ -23,6 +23,8 @@ internal static class Ring0 public static bool IsOpen => _driver != null; + internal static KernelDriver KernelDriver => _driver; + public static void Open() { // no implementation for unix systems diff --git a/LibreHardwareMonitorLib/Interop/Ring0.cs b/LibreHardwareMonitorLib/Interop/Ring0.cs index 1cdafe9..f3b25a2 100644 --- a/LibreHardwareMonitorLib/Interop/Ring0.cs +++ b/LibreHardwareMonitorLib/Interop/Ring0.cs @@ -20,9 +20,16 @@ internal static class Ring0 public static readonly Kernel32.IOControlCode IOCTL_OLS_GET_REFCOUNT = new(OLS_TYPE, 0x801, Kernel32.IOControlCode.Access.Any); public static readonly Kernel32.IOControlCode IOCTL_OLS_READ_MSR = new(OLS_TYPE, 0x821, Kernel32.IOControlCode.Access.Any); public static readonly Kernel32.IOControlCode IOCTL_OLS_WRITE_MSR = new(OLS_TYPE, 0x822, Kernel32.IOControlCode.Access.Any); - public static readonly Kernel32.IOControlCode IOCTL_OLS_READ_IO_PORT_BYTE = new(OLS_TYPE, 0x833, Kernel32.IOControlCode.Access.Read); - public static readonly Kernel32.IOControlCode IOCTL_OLS_WRITE_IO_PORT_BYTE = new(OLS_TYPE, 0x836, Kernel32.IOControlCode.Access.Write); - public static readonly Kernel32.IOControlCode IOCTL_OLS_READ_PCI_CONFIG = new(OLS_TYPE, 0x851, Kernel32.IOControlCode.Access.Read); - public static readonly Kernel32.IOControlCode IOCTL_OLS_WRITE_PCI_CONFIG = new(OLS_TYPE, 0x852, Kernel32.IOControlCode.Access.Write); public static readonly Kernel32.IOControlCode IOCTL_OLS_READ_MEMORY = new(OLS_TYPE, 0x841, Kernel32.IOControlCode.Access.Read); -} \ No newline at end of file + + public static readonly Kernel32.IOControlCode IOCTL_OLS_READ_IO_PORT_BYTE = new(OLS_TYPE, 0x833, Kernel32.IOControlCode.Access.Read); + public static readonly Kernel32.IOControlCode IOCTL_OLS_READ_IO_PORT_WORD = new(OLS_TYPE, 0x834, Kernel32.IOControlCode.Access.Read); + public static readonly Kernel32.IOControlCode IOCTL_OLS_READ_IO_PORT_DWORD = new(OLS_TYPE, 0x835, Kernel32.IOControlCode.Access.Read); + + public static readonly Kernel32.IOControlCode IOCTL_OLS_WRITE_IO_PORT_BYTE = new(OLS_TYPE, 0x836, Kernel32.IOControlCode.Access.Write); + public static readonly Kernel32.IOControlCode IOCTL_OLS_WRITE_IO_PORT_WORD = new(OLS_TYPE, 0x837, Kernel32.IOControlCode.Access.Write); + public static readonly Kernel32.IOControlCode IOCTL_OLS_WRITE_IO_PORT_DWORD = new(OLS_TYPE, 0x838, Kernel32.IOControlCode.Access.Write); + + public static readonly Kernel32.IOControlCode IOCTL_OLS_READ_PCI_CONFIG = new(OLS_TYPE, 0x851, Kernel32.IOControlCode.Access.Read); + public static readonly Kernel32.IOControlCode IOCTL_OLS_WRITE_PCI_CONFIG = new(OLS_TYPE, 0x852, Kernel32.IOControlCode.Access.Write); +} diff --git a/LibreHardwareMonitorLib/LibreHardwareMonitorLib.csproj b/LibreHardwareMonitorLib/LibreHardwareMonitorLib.csproj index 17d667b..995b53c 100644 --- a/LibreHardwareMonitorLib/LibreHardwareMonitorLib.csproj +++ b/LibreHardwareMonitorLib/LibreHardwareMonitorLib.csproj @@ -41,6 +41,7 @@ +