Add DIMM Temperature to LHM. (#1743)

* Add DIMM Temperature to LHM.

* Remove RAMSPDToolkit as submodule and use nuget package instead.
Upgrade System.Management assembly (required for nuget package).
Implement retry mechanism for RAM detection due to possible issue at system boot.

* Change to static field.

* Driver now back in LHM.
Ring0 replaced with OLS implementation.

* Update package version.

* Undo most of changes.
Implement IDriver interface.

* Change package to new one.

* Review changes.
Explicitly set UseWMI (current default is also false).

* PR comments:
Now having one DimmMemory instance per RAM stick.
Separated memory sensors into two new classes.

* PR feedback

---------

Co-authored-by: PhyxionNL <7643972+PhyxionNL@users.noreply.github.com>
This commit is contained in:
Blacktempel
2025-06-23 11:31:17 +02:00
committed by GitHub
co-authored by PhyxionNL
parent 9cefa1c93a
commit 5027bd9d57
13 changed files with 825 additions and 163 deletions
@@ -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 <mmoeller@openhardwaremonitor.org> 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();
}
}
@@ -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 <mmoeller@openhardwaremonitor.org> 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);
}
}
@@ -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 <mmoeller@openhardwaremonitor.org> 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<Kernel32.MEMORYSTATUSEX>() };
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);
}
}
@@ -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> _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<SPDAccessor> 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<IHardware> Hardware => _hardware;
public string GetReport()
{
return null;
}
public IReadOnlyList<IHardware> Hardware => _hardware;
public void Close()
{
foreach (Hardware ram in _hardware)
ram.Close();
}
private static bool DetectThermalSensors(out List<SPDAccessor> accessors)
{
lock (_lock)
{
var list = new List<SPDAccessor>();
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<SPDAccessor> 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);
}
}
}
@@ -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 <mmoeller@openhardwaremonitor.org> 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);
}
}
@@ -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 <mmoeller@openhardwaremonitor.org> 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<Kernel32.MEMORYSTATUSEX>() };
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<Kernel32.MEMORYSTATUSEX>() };
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);
}
}
@@ -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 <mmoeller@openhardwaremonitor.org> 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;
}
}
@@ -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 <mmoeller@openhardwaremonitor.org> 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);
}
}
}
@@ -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 <mmoeller@openhardwaremonitor.org> 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);
}
}
}
@@ -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 <mmoeller@openhardwaremonitor.org> and Contributors.
// All Rights Reserved.
using System.Runtime.InteropServices;
using RAMSPDToolkit.Windows.Driver;
using IOCC = LibreHardwareMonitor.Interop.Ring0;
namespace LibreHardwareMonitor.Hardware
{
/// <summary>
/// Implementation of <see cref="IDriver"/> interface for RAMSPDToolkit.
/// </summary>
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<TValue>(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;
}
}
}
@@ -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
+12 -5
View File
@@ -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);
}
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);
}
@@ -41,6 +41,7 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="HidSharp" Version="2.1.0" />
<PackageReference Include="RAMSPDToolkit-NDD" Version="1.1.4" />
<PackageReference Include="System.Management" Version="9.0.6" />
</ItemGroup>
<ItemGroup>