// 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.Runtime.InteropServices; using System.Threading; using LibreHardwareMonitor.Hardware.Cpu; namespace LibreHardwareMonitor.Hardware.Motherboard.Lpc; /// /// 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. /// internal class IsaBridgeGigabyteController : IGigabyteController { private const uint ControllerAddressRange = 0xFF; private const int ControllerEnableRegister = 0x47; private const uint ControllerFanControlArea = 0x900; /// /// Base address in PCI RAM that maps to the EC's RAM /// private readonly uint _controllerBaseAddress; private readonly Vendor _vendor; private bool? _initialState; public IsaBridgeGigabyteController(uint address, Vendor vendor) { _controllerBaseAddress = address; _vendor = vendor; } /// /// Enable/Disable Fan Control /// /// /// true on success 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; } /// /// Restore settings back to initial values /// public void Restore() { if (_initialState.HasValue) Enable(_initialState.Value); } }