200 lines
7.4 KiB
C#
200 lines
7.4 KiB
C#
// 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.Runtime.InteropServices;
|
|
using System.Threading;
|
|
using LibreHardwareMonitor.Hardware.Cpu;
|
|
|
|
namespace LibreHardwareMonitor.Hardware.Motherboard.Lpc;
|
|
|
|
/// <summary>
|
|
/// This is a controller present on some Gigabyte motherboards for both Intel and AMD, that is in custom firmware
|
|
/// loaded onto the 2nd ITE EC.
|
|
/// It can be accessed by using memory mapped IO, mapping its internal RAM onto main RAM via the ISA Bridge.
|
|
/// This class can disable it so that the regular IT87XX code can drive the fans.
|
|
/// </summary>
|
|
internal class IsaBridgeGigabyteController : IGigabyteController
|
|
{
|
|
private const uint ControllerAddressRange = 0xFF;
|
|
private const int ControllerEnableRegister = 0x47;
|
|
private const uint ControllerFanControlArea = 0x900;
|
|
|
|
/// <summary>
|
|
/// Base address in PCI RAM that maps to the EC's RAM
|
|
/// </summary>
|
|
private readonly uint _controllerBaseAddress;
|
|
|
|
private readonly Vendor _vendor;
|
|
|
|
private bool? _initialState;
|
|
|
|
public IsaBridgeGigabyteController(uint address, Vendor vendor)
|
|
{
|
|
_controllerBaseAddress = address;
|
|
_vendor = vendor;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Enable/Disable Fan Control
|
|
/// </summary>
|
|
/// <param name="enabled"></param>
|
|
/// <returns>true on success</returns>
|
|
public bool Enable(bool enabled)
|
|
{
|
|
return _vendor switch
|
|
{
|
|
Vendor.Intel => IntelEnable(enabled),
|
|
Vendor.AMD => AmdEnable(enabled),
|
|
_ => false
|
|
};
|
|
}
|
|
|
|
private bool IntelEnable(bool enabled)
|
|
{
|
|
if (!Mutexes.WaitPciBus(10))
|
|
return false;
|
|
|
|
bool result = false;
|
|
|
|
uint intelIsaBridgeAddress = Ring0.GetPciAddress(0x0, 0x1F, 0x0);
|
|
|
|
const uint ioOrMemoryPortDecodeEnableRegister = 0xD8;
|
|
const uint romAddressRange2Register = 0x98;
|
|
|
|
uint controllerFanControlAddress = _controllerBaseAddress + ControllerFanControlArea;
|
|
|
|
Ring0.ReadPciConfig(intelIsaBridgeAddress, ioOrMemoryPortDecodeEnableRegister, out uint originalDecodeEnableRegister);
|
|
Ring0.ReadPciConfig(intelIsaBridgeAddress, romAddressRange2Register, out uint originalRomAddressRegister);
|
|
|
|
bool originalMmIoEnabled = false;
|
|
if (!enabled)
|
|
{
|
|
originalMmIoEnabled = ((int)originalDecodeEnableRegister & 1) == 0 || ((int)originalRomAddressRegister & 1) == 1;
|
|
}
|
|
else
|
|
{
|
|
originalMmIoEnabled = ((int)originalDecodeEnableRegister & 1) == 0 && ((int)originalRomAddressRegister & 1) == 1;
|
|
}
|
|
|
|
if (enabled == originalMmIoEnabled)
|
|
{
|
|
result = Enable(enabled, new IntPtr(controllerFanControlAddress));
|
|
Mutexes.ReleasePciBus();
|
|
return result;
|
|
}
|
|
|
|
uint lpcBiosDecodeEnable;
|
|
uint lpcMemoryRange;
|
|
if (enabled)
|
|
{
|
|
lpcBiosDecodeEnable = ioOrMemoryPortDecodeEnableRegister & ~(uint)(1 << 0);
|
|
lpcMemoryRange = romAddressRange2Register | (uint)(1 << 0);
|
|
}
|
|
else
|
|
{
|
|
lpcBiosDecodeEnable = Convert.ToUInt32(ioOrMemoryPortDecodeEnableRegister | (uint)(1 << 0));
|
|
lpcMemoryRange = Convert.ToUInt32(romAddressRange2Register & ~(uint)(1 << 0));
|
|
}
|
|
|
|
Ring0.WritePciConfig(intelIsaBridgeAddress, ioOrMemoryPortDecodeEnableRegister, lpcBiosDecodeEnable);
|
|
Ring0.WritePciConfig(intelIsaBridgeAddress, romAddressRange2Register, lpcMemoryRange);
|
|
|
|
result = Enable(enabled, new IntPtr(controllerFanControlAddress));
|
|
|
|
Mutexes.ReleasePciBus();
|
|
|
|
return result;
|
|
}
|
|
|
|
private bool AmdEnable(bool enabled)
|
|
{
|
|
if (!Mutexes.WaitPciBus(10))
|
|
return false;
|
|
|
|
// see D14F3x https://www.amd.com/system/files/TechDocs/55072_AMD_Family_15h_Models_70h-7Fh_BKDG.pdf
|
|
uint amdIsaBridgeAddress = Ring0.GetPciAddress(0x0, 0x14, 0x3);
|
|
|
|
const uint ioOrMemoryPortDecodeEnableRegister = 0x48;
|
|
const uint memoryRangePortEnableMask = 0x1 << 5;
|
|
const uint pciMemoryAddressForLpcTargetCyclesRegister = 0x60;
|
|
const uint romAddressRange2Register = 0x6C;
|
|
|
|
uint controllerFanControlAddress = _controllerBaseAddress + ControllerFanControlArea;
|
|
|
|
uint pciAddressStart = _controllerBaseAddress >> 0x10;
|
|
uint pciAddressEnd = pciAddressStart + 1;
|
|
|
|
uint enabledPciMemoryAddressRegister = pciAddressEnd << 0x10 | pciAddressStart;
|
|
uint enabledRomAddressRegister = 0xFFFFU << 0x10 | pciAddressEnd;
|
|
|
|
Ring0.ReadPciConfig(amdIsaBridgeAddress, ioOrMemoryPortDecodeEnableRegister, out uint originalDecodeEnableRegister);
|
|
Ring0.ReadPciConfig(amdIsaBridgeAddress, pciMemoryAddressForLpcTargetCyclesRegister, out uint originalPciMemoryAddressRegister);
|
|
Ring0.ReadPciConfig(amdIsaBridgeAddress, romAddressRange2Register, out uint originalRomAddressRegister);
|
|
|
|
bool originalMmIoEnabled = (originalDecodeEnableRegister & memoryRangePortEnableMask) != 0 &&
|
|
originalPciMemoryAddressRegister == enabledPciMemoryAddressRegister &&
|
|
originalRomAddressRegister == enabledRomAddressRegister;
|
|
|
|
if (!originalMmIoEnabled)
|
|
{
|
|
Ring0.WritePciConfig(amdIsaBridgeAddress, ioOrMemoryPortDecodeEnableRegister, originalDecodeEnableRegister | memoryRangePortEnableMask);
|
|
Ring0.WritePciConfig(amdIsaBridgeAddress, pciMemoryAddressForLpcTargetCyclesRegister, enabledPciMemoryAddressRegister);
|
|
Ring0.WritePciConfig(amdIsaBridgeAddress, romAddressRange2Register, enabledRomAddressRegister);
|
|
}
|
|
|
|
bool result = Enable(enabled, new IntPtr(controllerFanControlAddress));
|
|
|
|
// Restore previous values
|
|
if (!originalMmIoEnabled)
|
|
{
|
|
Ring0.WritePciConfig(amdIsaBridgeAddress, ioOrMemoryPortDecodeEnableRegister, originalDecodeEnableRegister);
|
|
Ring0.WritePciConfig(amdIsaBridgeAddress, pciMemoryAddressForLpcTargetCyclesRegister, originalPciMemoryAddressRegister);
|
|
Ring0.WritePciConfig(amdIsaBridgeAddress, romAddressRange2Register, originalRomAddressRegister);
|
|
}
|
|
|
|
Mutexes.ReleasePciBus();
|
|
|
|
return result;
|
|
}
|
|
|
|
private bool Enable(bool enabled, IntPtr pciMmIoBaseAddress)
|
|
{
|
|
// Map PCI memory to this process memory
|
|
if (!InpOut.Open())
|
|
return false;
|
|
|
|
IntPtr mapped = InpOut.MapMemory(pciMmIoBaseAddress, ControllerAddressRange, out IntPtr handle);
|
|
|
|
if (mapped == IntPtr.Zero)
|
|
return false;
|
|
|
|
bool current = Convert.ToBoolean(Marshal.ReadByte(mapped, ControllerEnableRegister));
|
|
|
|
_initialState ??= current;
|
|
|
|
// Update Controller State
|
|
if (current != enabled)
|
|
{
|
|
Marshal.WriteByte(mapped, ControllerEnableRegister, Convert.ToByte(enabled));
|
|
// Give it some time to see the change
|
|
Thread.Sleep(500);
|
|
}
|
|
|
|
InpOut.UnmapMemory(handle, mapped);
|
|
return true;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Restore settings back to initial values
|
|
/// </summary>
|
|
public void Restore()
|
|
{
|
|
if (_initialState.HasValue)
|
|
Enable(_initialState.Value);
|
|
}
|
|
}
|