From f238f35eff8034e6f81ffb6a239fbed4f5f1de44 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michael=20M=C3=B6ller?= Date: Sun, 31 Oct 2010 22:08:47 +0000 Subject: [PATCH] Replaced the non-kernel code of WinRing0 with a managed implementation. The new implementation should fix Issue 32 and simplify further work on Issue 46. --- External/WinRing0.dll | Bin 55296 -> 0 bytes External/WinRing0x64.dll | Bin 57856 -> 0 bytes Hardware/CPU/AMD0FCPU.cs | 8 +- Hardware/CPU/AMD10CPU.cs | 24 +- Hardware/CPU/AMDCPU.cs | 8 +- Hardware/CPU/CPUGroup.cs | 5 +- Hardware/CPU/CPUID.cs | 10 +- Hardware/CPU/GenericCPU.cs | 51 ++++- Hardware/CPU/IntelCPU.cs | 10 +- Hardware/Computer.cs | 7 +- Hardware/IOControlCode.cs | 70 ++++++ Hardware/KernelDriver.cs | 300 +++++++++++++++++++++++++ Hardware/LPC/F718XX.cs | 12 +- Hardware/LPC/IT87XX.cs | 18 +- Hardware/LPC/LPCIO.cs | 36 +-- Hardware/LPC/W836XX.cs | 24 +- Hardware/Opcode.cs | 244 ++++++++++++++++++++ Hardware/Ring0.cs | 291 ++++++++++++++++++++++++ Hardware/WinRing0.cs | 179 --------------- {External => Hardware}/WinRing0.sys | Bin {External => Hardware}/WinRing0x64.sys | Bin OpenHardwareMonitorLib.csproj | 10 +- Program.cs | 11 - 23 files changed, 1034 insertions(+), 284 deletions(-) delete mode 100644 External/WinRing0.dll delete mode 100644 External/WinRing0x64.dll create mode 100644 Hardware/IOControlCode.cs create mode 100644 Hardware/KernelDriver.cs create mode 100644 Hardware/Opcode.cs create mode 100644 Hardware/Ring0.cs delete mode 100644 Hardware/WinRing0.cs rename {External => Hardware}/WinRing0.sys (100%) rename {External => Hardware}/WinRing0x64.sys (100%) diff --git a/External/WinRing0.dll b/External/WinRing0.dll deleted file mode 100644 index e0ed0cbacd601695c2f7374a03ebb75fc4bf490a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 55296 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 diff --git a/External/WinRing0x64.dll b/External/WinRing0x64.dll deleted file mode 100644 index e5b7e44130bab26741bc13bc06c94c854b5beb18..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 57856 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 z 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 J$1Gqh@ZUq8m9GE* diff --git a/Hardware/CPU/AMD0FCPU.cs b/Hardware/CPU/AMD0FCPU.cs index 2a3b17e..287c0c7 100644 --- a/Hardware/CPU/AMD0FCPU.cs +++ b/Hardware/CPU/AMD0FCPU.cs @@ -118,13 +118,13 @@ namespace OpenHardwareMonitor.Hardware.CPU { public override void Update() { base.Update(); - if (miscellaneousControlAddress != WinRing0.InvalidPciAddress) { + if (miscellaneousControlAddress != Ring0.InvalidPciAddress) { for (uint i = 0; i < coreTemperatures.Length; i++) { - if (WinRing0.WritePciConfigDwordEx( + if (Ring0.WritePciConfig( miscellaneousControlAddress, THERMTRIP_STATUS_REGISTER, i > 0 ? THERM_SENSE_CORE_SEL_CPU1 : THERM_SENSE_CORE_SEL_CPU0)) { uint value; - if (WinRing0.ReadPciConfigDwordEx( + if (Ring0.ReadPciConfig( miscellaneousControlAddress, THERMTRIP_STATUS_REGISTER, out value)) { @@ -145,7 +145,7 @@ namespace OpenHardwareMonitor.Hardware.CPU { Thread.Sleep(1); uint eax, edx; - if (WinRing0.RdmsrTx(FIDVID_STATUS, out eax, out edx, + if (Ring0.RdmsrTx(FIDVID_STATUS, out eax, out edx, (UIntPtr)(1L << cpuid[i][0].Thread))) { // CurrFID can be found in eax bits 0-5, MaxFID in 16-21 // 8-13 hold StartFID, we don't use that here. diff --git a/Hardware/CPU/AMD10CPU.cs b/Hardware/CPU/AMD10CPU.cs index 875bf8f..71e3e9a 100644 --- a/Hardware/CPU/AMD10CPU.cs +++ b/Hardware/CPU/AMD10CPU.cs @@ -93,15 +93,15 @@ namespace OpenHardwareMonitor.Hardware.CPU { (UIntPtr)(1L << cpuid[0][0].Thread)); uint ctlEax, ctlEdx; - WinRing0.Rdmsr(PERF_CTL_0, out ctlEax, out ctlEdx); + Ring0.Rdmsr(PERF_CTL_0, out ctlEax, out ctlEdx); uint ctrEax, ctrEdx; - WinRing0.Rdmsr(PERF_CTR_0, out ctrEax, out ctrEdx); + Ring0.Rdmsr(PERF_CTR_0, out ctrEax, out ctrEdx); timeStampCounterMultiplier = estimateTimeStampCounterMultiplier(); // restore the performance counter registers - WinRing0.Wrmsr(PERF_CTL_0, ctlEax, ctlEdx); - WinRing0.Wrmsr(PERF_CTR_0, ctrEax, ctrEdx); + Ring0.Wrmsr(PERF_CTL_0, ctlEax, ctlEdx); + Ring0.Wrmsr(PERF_CTR_0, ctrEax, ctrEdx); // restore the thread affinity. NativeMethods.SetThreadAffinityMask(thread, mask); @@ -126,14 +126,14 @@ namespace OpenHardwareMonitor.Hardware.CPU { uint eax, edx; // select event "076h CPU Clocks not Halted" and enable the counter - WinRing0.Wrmsr(PERF_CTL_0, + Ring0.Wrmsr(PERF_CTL_0, (1 << 22) | // enable performance counter (1 << 17) | // count events in user mode (1 << 16) | // count events in operating-system mode 0x76, 0x00000000); // set the counter to 0 - WinRing0.Wrmsr(PERF_CTR_0, 0, 0); + Ring0.Wrmsr(PERF_CTR_0, 0, 0); long ticks = (long)(timeWindow * Stopwatch.Frequency); uint lsbBegin, msbBegin, lsbEnd, msbEnd; @@ -142,11 +142,11 @@ namespace OpenHardwareMonitor.Hardware.CPU { (long)Math.Ceiling(0.001 * ticks); long timeEnd = timeBegin + ticks; while (Stopwatch.GetTimestamp() < timeBegin) { } - WinRing0.Rdmsr(PERF_CTR_0, out lsbBegin, out msbBegin); + Ring0.Rdmsr(PERF_CTR_0, out lsbBegin, out msbBegin); while (Stopwatch.GetTimestamp() < timeEnd) { } - WinRing0.Rdmsr(PERF_CTR_0, out lsbEnd, out msbEnd); + Ring0.Rdmsr(PERF_CTR_0, out lsbEnd, out msbEnd); - WinRing0.Rdmsr(COFVID_STATUS, out eax, out edx); + Ring0.Rdmsr(COFVID_STATUS, out eax, out edx); uint cpuDid = (eax >> 6) & 7; uint cpuFid = eax & 0x1F; double coreMultiplier = MultiplierFromIDs(cpuDid, cpuFid); @@ -188,9 +188,9 @@ namespace OpenHardwareMonitor.Hardware.CPU { public override void Update() { base.Update(); - if (miscellaneousControlAddress != WinRing0.InvalidPciAddress) { + if (miscellaneousControlAddress != Ring0.InvalidPciAddress) { uint value; - if (WinRing0.ReadPciConfigDwordEx(miscellaneousControlAddress, + if (Ring0.ReadPciConfig(miscellaneousControlAddress, REPORTED_TEMPERATURE_CONTROL_REGISTER, out value)) { coreTemperature.Value = ((value >> 21) & 0x7FF) / 8.0f + coreTemperature.Parameters[0].Value; @@ -207,7 +207,7 @@ namespace OpenHardwareMonitor.Hardware.CPU { Thread.Sleep(1); uint curEax, curEdx; - if (WinRing0.RdmsrTx(COFVID_STATUS, out curEax, out curEdx, + if (Ring0.RdmsrTx(COFVID_STATUS, out curEax, out curEdx, (UIntPtr)(1L << cpuid[i][0].Thread))) { // 8:6 CpuDid: current core divisor ID diff --git a/Hardware/CPU/AMDCPU.cs b/Hardware/CPU/AMDCPU.cs index 8c8ba2a..3dcca5b 100644 --- a/Hardware/CPU/AMDCPU.cs +++ b/Hardware/CPU/AMDCPU.cs @@ -50,17 +50,17 @@ namespace OpenHardwareMonitor.Hardware.CPU { protected uint GetPciAddress(byte function, ushort deviceId) { // assemble the pci address - uint address = WinRing0.GetPciAddress(PCI_BUS, + uint address = Ring0.GetPciAddress(PCI_BUS, (byte)(PCI_BASE_DEVICE + processorIndex), function); // verify that we have the correct bus, device and function uint deviceVendor; - if (!WinRing0.ReadPciConfigDwordEx( + if (!Ring0.ReadPciConfig( address, DEVICE_VENDOR_ID_REGISTER, out deviceVendor)) - return WinRing0.InvalidPciAddress; + return Ring0.InvalidPciAddress; if (deviceVendor != (deviceId << 16 | AMD_VENDOR_ID)) - return WinRing0.InvalidPciAddress; + return Ring0.InvalidPciAddress; return address; } diff --git a/Hardware/CPU/CPUGroup.cs b/Hardware/CPU/CPUGroup.cs index c9edce1..74a1101 100644 --- a/Hardware/CPU/CPUGroup.cs +++ b/Hardware/CPU/CPUGroup.cs @@ -104,10 +104,7 @@ namespace OpenHardwareMonitor.Hardware.CPU { // No implementation for cpuid on Unix systems int p = (int)Environment.OSVersion.Platform; if ((p == 4) || (p == 128)) - return; - - if (!WinRing0.IsCpuid()) - return; + return; CPUID[][] processorThreads = GetProcessorThreads(); this.threads = new CPUID[processorThreads.Length][][]; diff --git a/Hardware/CPU/CPUID.cs b/Hardware/CPU/CPUID.cs index 7008562..373ba7f 100644 --- a/Hardware/CPU/CPUID.cs +++ b/Hardware/CPU/CPUID.cs @@ -107,7 +107,7 @@ namespace OpenHardwareMonitor.Hardware.CPU { throw new ArgumentOutOfRangeException("thread"); UIntPtr mask = (UIntPtr)(1L << thread); - if (WinRing0.CpuidTx(CPUID_0, 0, + if (Opcode.CpuidTx(CPUID_0, 0, out eax, out ebx, out ecx, out edx, mask)) { if (eax > 0) maxCpuid = eax; @@ -131,7 +131,7 @@ namespace OpenHardwareMonitor.Hardware.CPU { break; } eax = ebx = ecx = edx = 0; - if (WinRing0.CpuidTx(CPUID_EXT, 0, + if (Opcode.CpuidTx(CPUID_EXT, 0, out eax, out ebx, out ecx, out edx, mask)) { if (eax > CPUID_EXT) maxCpuidExt = eax - CPUID_EXT; @@ -149,19 +149,19 @@ namespace OpenHardwareMonitor.Hardware.CPU { cpuidData = new uint[maxCpuid + 1, 4]; for (uint i = 0; i < (maxCpuid + 1); i++) - WinRing0.CpuidTx(CPUID_0 + i, 0, + Opcode.CpuidTx(CPUID_0 + i, 0, out cpuidData[i, 0], out cpuidData[i, 1], out cpuidData[i, 2], out cpuidData[i, 3], mask); cpuidExtData = new uint[maxCpuidExt + 1, 4]; for (uint i = 0; i < (maxCpuidExt + 1); i++) - WinRing0.CpuidTx(CPUID_EXT + i, 0, + Opcode.CpuidTx(CPUID_EXT + i, 0, out cpuidExtData[i, 0], out cpuidExtData[i, 1], out cpuidExtData[i, 2], out cpuidExtData[i, 3], mask); StringBuilder nameBuilder = new StringBuilder(); for (uint i = 2; i <= 4; i++) { - if (WinRing0.CpuidTx(CPUID_EXT + i, 0, + if (Opcode.CpuidTx(CPUID_EXT + i, 0, out eax, out ebx, out ecx, out edx, mask)) { AppendRegister(nameBuilder, eax); diff --git a/Hardware/CPU/GenericCPU.cs b/Hardware/CPU/GenericCPU.cs index 6238ca0..d653531 100644 --- a/Hardware/CPU/GenericCPU.cs +++ b/Hardware/CPU/GenericCPU.cs @@ -39,6 +39,7 @@ using System; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; +using System.Runtime.InteropServices; using System.Text; using System.Threading; @@ -55,6 +56,8 @@ namespace OpenHardwareMonitor.Hardware.CPU { protected readonly int coreCount; protected readonly string name; + private readonly bool hasModelSpecificRegisters; + private readonly bool hasTimeStampCounter; private readonly bool isInvariantTimeStampCounter; private readonly double estimatedTimeStampCounterFrequency; @@ -87,7 +90,14 @@ namespace OpenHardwareMonitor.Hardware.CPU { this.processorIndex = processorIndex; this.coreCount = cpuid.Length; - this.name = cpuid[0][0].Name; + this.name = cpuid[0][0].Name; + + // check if processor has MSRs + if (cpuid[0][0].Data.GetLength(0) > 1 + && (cpuid[0][0].Data[1, 3] & 0x20) != 0) + hasModelSpecificRegisters = true; + else + hasModelSpecificRegisters = false; // check if processor has a TSC if (cpuid[0][0].Data.GetLength(0) > 1 @@ -144,28 +154,26 @@ namespace OpenHardwareMonitor.Hardware.CPU { private static double EstimateTimeStampCounterFrequency(double timeWindow) { long ticks = (long)(timeWindow * Stopwatch.Frequency); - uint lsbBegin, msbBegin, lsbEnd, msbEnd; + ulong countBegin, countEnd; Thread.BeginThreadAffinity(); long timeBegin = Stopwatch.GetTimestamp() + (long)Math.Ceiling(0.001 * ticks); long timeEnd = timeBegin + ticks; while (Stopwatch.GetTimestamp() < timeBegin) { } - WinRing0.Rdtsc(out lsbBegin, out msbBegin); + countBegin = Opcode.Rdtsc(); while (Stopwatch.GetTimestamp() < timeEnd) { } - WinRing0.Rdtsc(out lsbEnd, out msbEnd); + countEnd = Opcode.Rdtsc(); Thread.EndThreadAffinity(); - ulong countBegin = ((ulong)msbBegin << 32) | lsbBegin; - ulong countEnd = ((ulong)msbEnd << 32) | lsbEnd; - return (((double)(countEnd - countBegin)) * Stopwatch.Frequency) / (timeEnd - timeBegin); } + private static void AppendMSRData(StringBuilder r, uint msr, int thread) { uint eax, edx; - if (WinRing0.RdmsrTx(msr, out eax, out edx, (UIntPtr)(1L << thread))) { + if (Ring0.RdmsrTx(msr, out eax, out edx, (UIntPtr)(1L << thread))) { r.Append(" "); r.Append((msr).ToString("X8", CultureInfo.InvariantCulture)); r.Append(" "); @@ -240,6 +248,10 @@ namespace OpenHardwareMonitor.Hardware.CPU { get { return HardwareType.CPU; } } + public bool HasModelSpecificRegisters { + get { return hasModelSpecificRegisters; } + } + public bool HasTimeStampCounter { get { return hasTimeStampCounter; } } @@ -250,14 +262,20 @@ namespace OpenHardwareMonitor.Hardware.CPU { public override void Update() { if (hasTimeStampCounter && isInvariantTimeStampCounter) { - uint lsb, msb; + + // make sure always the same thread is used + IntPtr thread = NativeMethods.GetCurrentThread(); + UIntPtr mask = NativeMethods.SetThreadAffinityMask(thread, + (UIntPtr)(1L << cpuid[0][0].Thread)); // read time before and after getting the TSC to estimate the error long firstTime = Stopwatch.GetTimestamp(); - WinRing0.RdtscTx(out lsb, out msb, (UIntPtr)1); + ulong timeStampCount = Opcode.Rdtsc(); long time = Stopwatch.GetTimestamp(); - ulong timeStampCount = ((ulong)msb << 32) | lsb; + // restore the thread affinity mask + NativeMethods.SetThreadAffinityMask(thread, mask); + double delta = ((double)(time - lastTime)) / Stopwatch.Frequency; double error = ((double)(time - firstTime)) / Stopwatch.Frequency; @@ -286,5 +304,16 @@ namespace OpenHardwareMonitor.Hardware.CPU { totalLoad.Value = cpuLoad.GetTotalLoad(); } } + + private static class NativeMethods { + private const string KERNEL = "kernel32.dll"; + + [DllImport(KERNEL, CallingConvention = CallingConvention.Winapi)] + public static extern UIntPtr + SetThreadAffinityMask(IntPtr handle, UIntPtr mask); + + [DllImport(KERNEL, CallingConvention = CallingConvention.Winapi)] + public static extern IntPtr GetCurrentThread(); + } } } diff --git a/Hardware/CPU/IntelCPU.cs b/Hardware/CPU/IntelCPU.cs index a65d0ec..6503d41 100644 --- a/Hardware/CPU/IntelCPU.cs +++ b/Hardware/CPU/IntelCPU.cs @@ -117,7 +117,7 @@ namespace OpenHardwareMonitor.Hardware.CPU { uint eax, edx; tjMax = new float[coreCount]; for (int i = 0; i < coreCount; i++) { - if (WinRing0.RdmsrTx(IA32_TEMPERATURE_TARGET, out eax, + if (Ring0.RdmsrTx(IA32_TEMPERATURE_TARGET, out eax, out edx, (UIntPtr)(1L << cpuid[i][0].Thread))) { tjMax[i] = (eax >> 16) & 0xFF; } else { @@ -142,14 +142,14 @@ namespace OpenHardwareMonitor.Hardware.CPU { case Microarchitecture.Atom: case Microarchitecture.Core: { uint eax, edx; - if (WinRing0.Rdmsr(IA32_PERF_STATUS, out eax, out edx)) { + if (Ring0.Rdmsr(IA32_PERF_STATUS, out eax, out edx)) { timeStampCounterMultiplier = ((edx >> 8) & 0x1f) + 0.5 * ((edx >> 14) & 1); } } break; case Microarchitecture.Nehalem: { uint eax, edx; - if (WinRing0.Rdmsr(MSR_PLATFORM_INFO, out eax, out edx)) { + if (Ring0.Rdmsr(MSR_PLATFORM_INFO, out eax, out edx)) { timeStampCounterMultiplier = (eax >> 8) & 0xff; } } break; @@ -215,7 +215,7 @@ namespace OpenHardwareMonitor.Hardware.CPU { for (int i = 0; i < coreTemperatures.Length; i++) { uint eax, edx; - if (WinRing0.RdmsrTx( + if (Ring0.RdmsrTx( IA32_THERM_STATUS_MSR, out eax, out edx, (UIntPtr)(1L << cpuid[i][0].Thread))) { // if reading is valid @@ -236,7 +236,7 @@ namespace OpenHardwareMonitor.Hardware.CPU { uint eax, edx; for (int i = 0; i < coreClocks.Length; i++) { System.Threading.Thread.Sleep(1); - if (WinRing0.RdmsrTx(IA32_PERF_STATUS, out eax, out edx, + if (Ring0.RdmsrTx(IA32_PERF_STATUS, out eax, out edx, (UIntPtr)(1L << cpuid[i][0].Thread))) { newBusClock = diff --git a/Hardware/Computer.cs b/Hardware/Computer.cs index 0f97e10..a7be257 100644 --- a/Hardware/Computer.cs +++ b/Hardware/Computer.cs @@ -40,6 +40,7 @@ using System.Collections.Generic; using System.Globalization; using System.IO; using System.Security.Permissions; +using System.Reflection; namespace OpenHardwareMonitor.Hardware { @@ -86,7 +87,8 @@ namespace OpenHardwareMonitor.Hardware { if (open) return; - WinRing0.Open(); + Ring0.Open(); + Opcode.Open(); Add(new Mainboard.MainboardGroup(settings)); Add(new CPU.CPUGroup(settings)); @@ -262,7 +264,8 @@ namespace OpenHardwareMonitor.Hardware { group.Close(); groups.Clear(); - WinRing0.Close(); + Opcode.Close(); + Ring0.Close(); open = false; } diff --git a/Hardware/IOControlCode.cs b/Hardware/IOControlCode.cs new file mode 100644 index 0000000..b2ff08d --- /dev/null +++ b/Hardware/IOControlCode.cs @@ -0,0 +1,70 @@ +/* + + Version: MPL 1.1/GPL 2.0/LGPL 2.1 + + The contents of this file are subject to the Mozilla Public License Version + 1.1 (the "License"); you may not use this file except in compliance with + the License. You may obtain a copy of the License at + + http://www.mozilla.org/MPL/ + + Software distributed under the License is distributed on an "AS IS" basis, + WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + for the specific language governing rights and limitations under the License. + + The Original Code is the Open Hardware Monitor code. + + The Initial Developer of the Original Code is + Michael Möller . + Portions created by the Initial Developer are Copyright (C) 2010 + the Initial Developer. All Rights Reserved. + + Contributor(s): + + Alternatively, the contents of this file may be used under the terms of + either the GNU General Public License Version 2 or later (the "GPL"), or + the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + in which case the provisions of the GPL or the LGPL are applicable instead + of those above. If you wish to allow use of your version of this file only + under the terms of either the GPL or the LGPL, and not to allow others to + use your version of this file under the terms of the MPL, indicate your + decision by deleting the provisions above and replace them with the notice + and other provisions required by the GPL or the LGPL. If you do not delete + the provisions above, a recipient may use your version of this file under + the terms of any one of the MPL, the GPL or the LGPL. + +*/ + +using System; +using System.Runtime.InteropServices; + +namespace OpenHardwareMonitor.Hardware { + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + internal struct IOControlCode { + private uint code; + + public IOControlCode(uint deviceType, uint function, Access access) : + this(deviceType, function, Method.Buffered, access) { } + + public IOControlCode(uint deviceType, uint function, Method method, + Access access) + { + code = (deviceType << 16) | + ((uint)access << 14) | (function << 2) | (uint)method; + } + + public enum Method : uint { + Buffered = 0, + InDirect = 1, + OutDirect = 2, + Neither = 3 + } + + public enum Access : uint { + Any = 0, + Read = 1, + Write = 2 + } + } +} diff --git a/Hardware/KernelDriver.cs b/Hardware/KernelDriver.cs new file mode 100644 index 0000000..c5fd4e5 --- /dev/null +++ b/Hardware/KernelDriver.cs @@ -0,0 +1,300 @@ +/* + + Version: MPL 1.1/GPL 2.0/LGPL 2.1 + + The contents of this file are subject to the Mozilla Public License Version + 1.1 (the "License"); you may not use this file except in compliance with + the License. You may obtain a copy of the License at + + http://www.mozilla.org/MPL/ + + Software distributed under the License is distributed on an "AS IS" basis, + WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + for the specific language governing rights and limitations under the License. + + The Original Code is the Open Hardware Monitor code. + + The Initial Developer of the Original Code is + Michael Möller . + Portions created by the Initial Developer are Copyright (C) 2010 + the Initial Developer. All Rights Reserved. + + Contributor(s): + + Alternatively, the contents of this file may be used under the terms of + either the GNU General Public License Version 2 or later (the "GPL"), or + the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + in which case the provisions of the GPL or the LGPL are applicable instead + of those above. If you wish to allow use of your version of this file only + under the terms of either the GPL or the LGPL, and not to allow others to + use your version of this file under the terms of the MPL, indicate your + decision by deleting the provisions above and replace them with the notice + and other provisions required by the GPL or the LGPL. If you do not delete + the provisions above, a recipient may use your version of this file under + the terms of any one of the MPL, the GPL or the LGPL. + +*/ + +using System; +using System.Runtime.InteropServices; +using Microsoft.Win32.SafeHandles; + +namespace OpenHardwareMonitor.Hardware { + internal class KernelDriver { + + private string id; + + private SafeFileHandle device; + + public KernelDriver(string id) { + this.id = id; + } + + public bool Install(string path) { + IntPtr manager = NativeMethods.OpenSCManager(null, null, + ServiceControlManagerAccessRights.SC_MANAGER_ALL_ACCESS); + + if (manager == IntPtr.Zero) + return false; + + IntPtr service = NativeMethods.CreateService(manager, id, id, + ServiceAccessRights.SERVICE_ALL_ACCESS, + ServiceType.SERVICE_KERNEL_DRIVER, StartType.SERVICE_DEMAND_START, + ErrorControl.SERVICE_ERROR_NORMAL, path, null, null, null, null, + null); + + if (service == IntPtr.Zero) { + if (Marshal.GetHRForLastWin32Error() == ERROR_SERVICE_EXISTS) + service = NativeMethods.OpenService(manager, id, + ServiceAccessRights.SERVICE_ALL_ACCESS); + else + return false; + } + + if (!NativeMethods.StartService(service, 0, null)) { + if (Marshal.GetHRForLastWin32Error() != ERROR_SERVICE_ALREADY_RUNNING) + return false; + } + + NativeMethods.CloseServiceHandle(service); + NativeMethods.CloseServiceHandle(manager); + + return true; + } + + public bool Open() { + device = new SafeFileHandle(NativeMethods.CreateFile(@"\\.\" + id, + FileAccess.GENERIC_READ | FileAccess.GENERIC_WRITE, 0, IntPtr.Zero, + CreationDisposition.OPEN_EXISTING, FileAttributes.FILE_ATTRIBUTE_NORMAL, + IntPtr.Zero), true); + + if (device.IsInvalid) { + device.Close(); + device.Dispose(); + device = null; + } + + return device != null; + } + + public bool IsOpen { + get { return device != null; } + } + + public bool DeviceIOControl(IOControlCode ioControlCode, object inBuffer) { + if (device == null) + return false; + + uint bytesReturned; + bool b = NativeMethods.DeviceIoControl(device, ioControlCode, + inBuffer, inBuffer == null ? 0 : (uint)Marshal.SizeOf(inBuffer), + null, 0, out bytesReturned, IntPtr.Zero); + return b; + } + + public bool DeviceIOControl(IOControlCode ioControlCode, object inBuffer, + ref T outBuffer) + { + if (device == null) + return false; + + object boxedOutBuffer = outBuffer; + uint bytesReturned; + bool b = NativeMethods.DeviceIoControl(device, ioControlCode, + inBuffer, inBuffer == null ? 0 : (uint)Marshal.SizeOf(inBuffer), + boxedOutBuffer, (uint)Marshal.SizeOf(boxedOutBuffer), + out bytesReturned, IntPtr.Zero); + outBuffer = (T)boxedOutBuffer; + return b; + } + + public void Close() { + if (device != null) { + device.Close(); + device.Dispose(); + device = null; + } + } + + public bool Delete() { + IntPtr manager = NativeMethods.OpenSCManager(null, null, + ServiceControlManagerAccessRights.SC_MANAGER_ALL_ACCESS); + + if (manager == IntPtr.Zero) + return false; + + IntPtr service = NativeMethods.OpenService(manager, id, + ServiceAccessRights.SERVICE_ALL_ACCESS); + + if (service == IntPtr.Zero) + return true; + + ServiceStatus status = new ServiceStatus(); + NativeMethods.ControlService(service, ServiceControl.SERVICE_CONTROL_STOP, + ref status); + + NativeMethods.DeleteService(service); + + NativeMethods.CloseServiceHandle(service); + NativeMethods.CloseServiceHandle(manager); + + return true; + } + + private enum ServiceAccessRights : uint { + SERVICE_ALL_ACCESS = 0xF01FF + } + + private enum ServiceControlManagerAccessRights : uint { + SC_MANAGER_ALL_ACCESS = 0xF003F + } + + private enum ServiceType : uint { + SERVICE_KERNEL_DRIVER = 1, + SERVICE_FILE_SYSTEM_DRIVER = 2 + } + + private enum StartType : uint { + SERVICE_BOOT_START = 0, + SERVICE_SYSTEM_START = 1, + SERVICE_AUTO_START = 2, + SERVICE_DEMAND_START = 3, + SERVICE_DISABLED = 4 + } + + private enum ErrorControl : uint { + SERVICE_ERROR_IGNORE = 0, + SERVICE_ERROR_NORMAL = 1, + SERVICE_ERROR_SEVERE = 2, + SERVICE_ERROR_CRITICAL = 3 + } + + private enum ServiceControl : uint { + SERVICE_CONTROL_STOP = 1, + SERVICE_CONTROL_PAUSE = 2, + SERVICE_CONTROL_CONTINUE = 3, + SERVICE_CONTROL_INTERROGATE = 4, + SERVICE_CONTROL_SHUTDOWN = 5, + SERVICE_CONTROL_PARAMCHANGE = 6, + SERVICE_CONTROL_NETBINDADD = 7, + SERVICE_CONTROL_NETBINDREMOVE = 8, + SERVICE_CONTROL_NETBINDENABLE = 9, + SERVICE_CONTROL_NETBINDDISABLE = 10, + SERVICE_CONTROL_DEVICEEVENT = 11, + SERVICE_CONTROL_HARDWAREPROFILECHANGE = 12, + SERVICE_CONTROL_POWEREVENT = 13, + SERVICE_CONTROL_SESSIONCHANGE = 14 + } + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + private struct ServiceStatus { + public uint dwServiceType; + public uint dwCurrentState; + public uint dwControlsAccepted; + public uint dwWin32ExitCode; + public uint dwServiceSpecificExitCode; + public uint dwCheckPoint; + public uint dwWaitHint; + } + + private enum FileAccess : uint { + GENERIC_READ = 0x80000000, + GENERIC_WRITE = 0x40000000 + } + + private enum CreationDisposition : uint { + CREATE_NEW = 1, + CREATE_ALWAYS = 2, + OPEN_EXISTING = 3, + OPEN_ALWAYS = 4, + TRUNCATE_EXISTING = 5 + } + + private enum FileAttributes : uint { + FILE_ATTRIBUTE_NORMAL = 0x80 + } + + private const int + ERROR_SERVICE_EXISTS = unchecked((int)0x80070431), + ERROR_SERVICE_ALREADY_RUNNING = unchecked((int)0x80070420); + + private static class NativeMethods { + private const string ADVAPI = "advapi32.dll"; + private const string KERNEL = "kernel32.dll"; + + [DllImport(ADVAPI, CallingConvention = CallingConvention.Winapi)] + public static extern IntPtr OpenSCManager(string machineName, + string databaseName, ServiceControlManagerAccessRights dwAccess); + + [DllImport(ADVAPI, CallingConvention = CallingConvention.Winapi)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool CloseServiceHandle(IntPtr hSCObject); + + [DllImport(ADVAPI, CallingConvention = CallingConvention.Winapi, + SetLastError = true)] + public static extern IntPtr CreateService(IntPtr hSCManager, + string lpServiceName, string lpDisplayName, + ServiceAccessRights dwDesiredAccess, ServiceType dwServiceType, + StartType dwStartType, ErrorControl dwErrorControl, + string lpBinaryPathName, string lpLoadOrderGroup, string lpdwTagId, + string lpDependencies, string lpServiceStartName, string lpPassword); + + [DllImport(ADVAPI, CallingConvention = CallingConvention.Winapi, + SetLastError = true)] + public static extern IntPtr OpenService(IntPtr hSCManager, + string lpServiceName, ServiceAccessRights dwDesiredAccess); + + [DllImport(ADVAPI, CallingConvention = CallingConvention.Winapi, + SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool DeleteService(IntPtr hService); + + [DllImport(ADVAPI, CallingConvention = CallingConvention.Winapi, + SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool StartService(IntPtr hService, + uint dwNumServiceArgs, string[] lpServiceArgVectors); + + [DllImport(ADVAPI, CallingConvention = CallingConvention.Winapi, + SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool ControlService(IntPtr hService, + ServiceControl dwControl, ref ServiceStatus lpServiceStatus); + + [DllImport(KERNEL, CallingConvention = CallingConvention.Winapi)] + public static extern bool DeviceIoControl(SafeFileHandle device, + IOControlCode ioControlCode, + [MarshalAs(UnmanagedType.AsAny)] [In] object inBuffer, + uint inBufferSize, + [MarshalAs(UnmanagedType.AsAny)] [Out] object outBuffer, + uint nOutBufferSize, out uint bytesReturned, IntPtr overlapped); + + [DllImport(KERNEL, CallingConvention = CallingConvention.Winapi, + SetLastError = true)] + public static extern IntPtr CreateFile(string lpFileName, + FileAccess dwDesiredAccess, uint dwShareMode, + IntPtr lpSecurityAttributes, CreationDisposition dwCreationDisposition, + FileAttributes dwFlagsAndAttributes, IntPtr hTemplateFile); + } + } +} diff --git a/Hardware/LPC/F718XX.cs b/Hardware/LPC/F718XX.cs index 6236f10..cc1b59e 100644 --- a/Hardware/LPC/F718XX.cs +++ b/Hardware/LPC/F718XX.cs @@ -60,9 +60,9 @@ namespace OpenHardwareMonitor.Hardware.LPC { new byte[] { 0xA0, 0xB0, 0xC0, 0xD0 }; private byte ReadByte(byte register) { - WinRing0.WriteIoPortByte( + Ring0.WriteIoPort( (ushort)(address + ADDRESS_REGISTER_OFFSET), register); - return WinRing0.ReadIoPortByte((ushort)(address + DATA_REGISTER_OFFSET)); + return Ring0.ReadIoPort((ushort)(address + DATA_REGISTER_OFFSET)); } public byte? ReadGPIO(int index) { @@ -94,7 +94,7 @@ namespace OpenHardwareMonitor.Hardware.LPC { r.AppendLine(address.ToString("X4", CultureInfo.InvariantCulture)); r.AppendLine(); - if (!WinRing0.WaitIsaBusMutex(100)) + if (!Ring0.WaitIsaBusMutex(100)) return r.ToString(); r.AppendLine("Hardware Monitor Registers"); @@ -114,13 +114,13 @@ namespace OpenHardwareMonitor.Hardware.LPC { } r.AppendLine(); - WinRing0.ReleaseIsaBusMutex(); + Ring0.ReleaseIsaBusMutex(); return r.ToString(); } public void Update() { - if (!WinRing0.WaitIsaBusMutex(10)) + if (!Ring0.WaitIsaBusMutex(10)) return; for (int i = 0; i < voltages.Length; i++) { @@ -173,7 +173,7 @@ namespace OpenHardwareMonitor.Hardware.LPC { fans[i] = null; } - WinRing0.ReleaseIsaBusMutex(); + Ring0.ReleaseIsaBusMutex(); } } } diff --git a/Hardware/LPC/IT87XX.cs b/Hardware/LPC/IT87XX.cs index 5fabd51..1871892 100644 --- a/Hardware/LPC/IT87XX.cs +++ b/Hardware/LPC/IT87XX.cs @@ -76,9 +76,9 @@ namespace OpenHardwareMonitor.Hardware.LPC { private const byte VOLTAGE_BASE_REG = 0x20; private byte ReadByte(byte register, out bool valid) { - WinRing0.WriteIoPortByte(addressReg, register); - byte value = WinRing0.ReadIoPortByte(dataReg); - valid = register == WinRing0.ReadIoPortByte(addressReg); + Ring0.WriteIoPort(addressReg, register); + byte value = Ring0.ReadIoPort(dataReg); + valid = register == Ring0.ReadIoPort(addressReg); return value; } @@ -86,14 +86,14 @@ namespace OpenHardwareMonitor.Hardware.LPC { if (index >= gpioCount) return null; - return WinRing0.ReadIoPortByte((ushort)(gpioAddress + index)); + return Ring0.ReadIoPort((ushort)(gpioAddress + index)); } public void WriteGPIO(int index, byte value) { if (index >= gpioCount) return; - WinRing0.WriteIoPortByte((ushort)(gpioAddress + index), value); + Ring0.WriteIoPort((ushort)(gpioAddress + index), value); } public IT87XX(Chip chip, ushort address, ushort gpioAddress, byte version) { @@ -162,7 +162,7 @@ namespace OpenHardwareMonitor.Hardware.LPC { gpioAddress.ToString("X4", CultureInfo.InvariantCulture)); r.AppendLine(); - if (!WinRing0.WaitIsaBusMutex(100)) + if (!Ring0.WaitIsaBusMutex(100)) return r.ToString(); r.AppendLine("Environment Controller Registers"); @@ -194,13 +194,13 @@ namespace OpenHardwareMonitor.Hardware.LPC { r.AppendLine(); r.AppendLine(); - WinRing0.ReleaseIsaBusMutex(); + Ring0.ReleaseIsaBusMutex(); return r.ToString(); } public void Update() { - if (!WinRing0.WaitIsaBusMutex(10)) + if (!Ring0.WaitIsaBusMutex(10)) return; for (int i = 0; i < voltages.Length; i++) { @@ -246,7 +246,7 @@ namespace OpenHardwareMonitor.Hardware.LPC { } } - WinRing0.ReleaseIsaBusMutex(); + Ring0.ReleaseIsaBusMutex(); } } } diff --git a/Hardware/LPC/LPCIO.cs b/Hardware/LPC/LPCIO.cs index e051cbc..23914e3 100644 --- a/Hardware/LPC/LPCIO.cs +++ b/Hardware/LPC/LPCIO.cs @@ -62,8 +62,8 @@ namespace OpenHardwareMonitor.Hardware.LPC { private const byte BASE_ADDRESS_REGISTER = 0x60; private byte ReadByte(byte register) { - WinRing0.WriteIoPortByte(registerPort, register); - return WinRing0.ReadIoPortByte(valuePort); + Ring0.WriteIoPort(registerPort, register); + return Ring0.ReadIoPort(valuePort); } private ushort ReadWord(byte register) { @@ -72,8 +72,8 @@ namespace OpenHardwareMonitor.Hardware.LPC { } private void Select(byte logicalDeviceNumber) { - WinRing0.WriteIoPortByte(registerPort, DEVCIE_SELECT_REGISTER); - WinRing0.WriteIoPortByte(valuePort, logicalDeviceNumber); + Ring0.WriteIoPort(registerPort, DEVCIE_SELECT_REGISTER); + Ring0.WriteIoPort(valuePort, logicalDeviceNumber); } private void ReportUnknownChip(string type, int chip) { @@ -99,12 +99,12 @@ namespace OpenHardwareMonitor.Hardware.LPC { private const byte FINTEK_HARDWARE_MONITOR_LDN = 0x04; private void WinbondFintekEnter() { - WinRing0.WriteIoPortByte(registerPort, 0x87); - WinRing0.WriteIoPortByte(registerPort, 0x87); + Ring0.WriteIoPort(registerPort, 0x87); + Ring0.WriteIoPort(registerPort, 0x87); } private void WinbondFintekExit() { - WinRing0.WriteIoPortByte(registerPort, 0xAA); + Ring0.WriteIoPort(registerPort, 0xAA); } private bool DetectWinbondFintek() { @@ -308,15 +308,15 @@ namespace OpenHardwareMonitor.Hardware.LPC { private const byte IT87_CHIP_VERSION_REGISTER = 0x22; private void IT87Enter() { - WinRing0.WriteIoPortByte(registerPort, 0x87); - WinRing0.WriteIoPortByte(registerPort, 0x01); - WinRing0.WriteIoPortByte(registerPort, 0x55); - WinRing0.WriteIoPortByte(registerPort, 0x55); + Ring0.WriteIoPort(registerPort, 0x87); + Ring0.WriteIoPort(registerPort, 0x01); + Ring0.WriteIoPort(registerPort, 0x55); + Ring0.WriteIoPort(registerPort, 0x55); } private void IT87Exit() { - WinRing0.WriteIoPortByte(registerPort, CONFIGURATION_CONTROL_REGISTER); - WinRing0.WriteIoPortByte(valuePort, 0x02); + Ring0.WriteIoPort(registerPort, CONFIGURATION_CONTROL_REGISTER); + Ring0.WriteIoPort(valuePort, 0x02); } private bool DetectIT87() { @@ -392,11 +392,11 @@ namespace OpenHardwareMonitor.Hardware.LPC { #region SMSC private void SMSCEnter() { - WinRing0.WriteIoPortByte(registerPort, 0x55); + Ring0.WriteIoPort(registerPort, 0x55); } private void SMSCExit() { - WinRing0.WriteIoPortByte(registerPort, 0xAA); + Ring0.WriteIoPort(registerPort, 0xAA); } private bool DetectSMSC() { @@ -438,15 +438,15 @@ namespace OpenHardwareMonitor.Hardware.LPC { } public LPCIO() { - if (!WinRing0.IsAvailable) + if (!Ring0.IsOpen) return; - if (!WinRing0.WaitIsaBusMutex(100)) + if (!Ring0.WaitIsaBusMutex(100)) return; Detect(); - WinRing0.ReleaseIsaBusMutex(); + Ring0.ReleaseIsaBusMutex(); } public ISuperIO[] SuperIO { diff --git a/Hardware/LPC/W836XX.cs b/Hardware/LPC/W836XX.cs index 97fc0e4..2efa2d8 100644 --- a/Hardware/LPC/W836XX.cs +++ b/Hardware/LPC/W836XX.cs @@ -84,24 +84,24 @@ namespace OpenHardwareMonitor.Hardware.LPC { private readonly byte[] FAN_DIV_BIT2 = new byte[] { 5, 6, 7, 23, 15 }; private byte ReadByte(byte bank, byte register) { - WinRing0.WriteIoPortByte( + Ring0.WriteIoPort( (ushort)(address + ADDRESS_REGISTER_OFFSET), BANK_SELECT_REGISTER); - WinRing0.WriteIoPortByte( + Ring0.WriteIoPort( (ushort)(address + DATA_REGISTER_OFFSET), bank); - WinRing0.WriteIoPortByte( + Ring0.WriteIoPort( (ushort)(address + ADDRESS_REGISTER_OFFSET), register); - return WinRing0.ReadIoPortByte( + return Ring0.ReadIoPort( (ushort)(address + DATA_REGISTER_OFFSET)); } private void WriteByte(byte bank, byte register, byte value) { - WinRing0.WriteIoPortByte( + Ring0.WriteIoPort( (ushort)(address + ADDRESS_REGISTER_OFFSET), BANK_SELECT_REGISTER); - WinRing0.WriteIoPortByte( + Ring0.WriteIoPort( (ushort)(address + DATA_REGISTER_OFFSET), bank); - WinRing0.WriteIoPortByte( + Ring0.WriteIoPort( (ushort)(address + ADDRESS_REGISTER_OFFSET), register); - WinRing0.WriteIoPortByte( + Ring0.WriteIoPort( (ushort)(address + DATA_REGISTER_OFFSET), value); } @@ -203,7 +203,7 @@ namespace OpenHardwareMonitor.Hardware.LPC { public float?[] Fans { get { return fans; } } public void Update() { - if (!WinRing0.WaitIsaBusMutex(10)) + if (!Ring0.WaitIsaBusMutex(10)) return; for (int i = 0; i < voltages.Length; i++) { @@ -291,7 +291,7 @@ namespace OpenHardwareMonitor.Hardware.LPC { WriteByte(0, FAN_BIT_REG[i], newByte); } - WinRing0.ReleaseIsaBusMutex(); + Ring0.ReleaseIsaBusMutex(); } public string GetReport() { @@ -306,7 +306,7 @@ namespace OpenHardwareMonitor.Hardware.LPC { r.AppendLine(address.ToString("X4", CultureInfo.InvariantCulture)); r.AppendLine(); - if (!WinRing0.WaitIsaBusMutex(100)) + if (!Ring0.WaitIsaBusMutex(100)) return r.ToString(); r.AppendLine("Hardware Monitor Registers"); @@ -340,7 +340,7 @@ namespace OpenHardwareMonitor.Hardware.LPC { } r.AppendLine(); - WinRing0.ReleaseIsaBusMutex(); + Ring0.ReleaseIsaBusMutex(); return r.ToString(); } diff --git a/Hardware/Opcode.cs b/Hardware/Opcode.cs new file mode 100644 index 0000000..0805f5a --- /dev/null +++ b/Hardware/Opcode.cs @@ -0,0 +1,244 @@ +/* + + Version: MPL 1.1/GPL 2.0/LGPL 2.1 + + The contents of this file are subject to the Mozilla Public License Version + 1.1 (the "License"); you may not use this file except in compliance with + the License. You may obtain a copy of the License at + + http://www.mozilla.org/MPL/ + + Software distributed under the License is distributed on an "AS IS" basis, + WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + for the specific language governing rights and limitations under the License. + + The Original Code is the Open Hardware Monitor code. + + The Initial Developer of the Original Code is + Michael Möller . + Portions created by the Initial Developer are Copyright (C) 2010 + the Initial Developer. All Rights Reserved. + + Contributor(s): + + Alternatively, the contents of this file may be used under the terms of + either the GNU General Public License Version 2 or later (the "GPL"), or + the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + in which case the provisions of the GPL or the LGPL are applicable instead + of those above. If you wish to allow use of your version of this file only + under the terms of either the GPL or the LGPL, and not to allow others to + use your version of this file under the terms of the MPL, indicate your + decision by deleting the provisions above and replace them with the notice + and other provisions required by the GPL or the LGPL. If you do not delete + the provisions above, a recipient may use your version of this file under + the terms of any one of the MPL, the GPL or the LGPL. + +*/ + +using System; +using System.Runtime.InteropServices; + +namespace OpenHardwareMonitor.Hardware { + internal static class Opcode { + private static IntPtr codeBuffer; + + public static void Open() { + // No implementation for Unix systems + int p = (int)Environment.OSVersion.Platform; + if ((p == 4) || (p == 128)) + return; + + byte[] rdtscCode; + byte[] cpuidCode; + if (IntPtr.Size == 4) { + rdtscCode = RDTSC_32; + cpuidCode = CPUID_32; + } else { + rdtscCode = RDTSC_64; + cpuidCode = CPUID_64; + } + + codeBuffer = NativeMethods.VirtualAlloc(IntPtr.Zero, + (UIntPtr)(rdtscCode.Length + cpuidCode.Length), + AllocationType.COMMIT | AllocationType.RESERVE, + MemoryProtection.EXECUTE_READWRITE); + + Marshal.Copy(rdtscCode, 0, codeBuffer, rdtscCode.Length); + + Rdtsc = Marshal.GetDelegateForFunctionPointer( + codeBuffer, typeof(RdtscDelegate)) as RdtscDelegate; + + IntPtr cpuidAddress = (IntPtr)((long)codeBuffer + rdtscCode.Length); + Marshal.Copy(cpuidCode, 0, cpuidAddress, cpuidCode.Length); + + Cpuid = Marshal.GetDelegateForFunctionPointer( + cpuidAddress, typeof(CpuidDelegate)) as CpuidDelegate; + } + + public static void Close() { + Rdtsc = null; + Cpuid = null; + + NativeMethods.VirtualFree(codeBuffer, UIntPtr.Zero, + FreeType.RELEASE); + } + + [UnmanagedFunctionPointer(CallingConvention.StdCall)] + public delegate ulong RdtscDelegate(); + + public static RdtscDelegate Rdtsc; + + // unsigned __int64 __stdcall rdtsc() { + // return __rdtsc(); + // } + + private static readonly byte[] RDTSC_32 = new byte[] { + 0x0F, 0x31, // rdtsc + 0xC3 // ret + }; + + private static readonly byte[] RDTSC_64 = new byte[] { + 0x0F, 0x31, // rdtsc + 0x48, 0xC1, 0xE2, 0x20, // shl rdx,20h + 0x48, 0x0B, 0xC2, // or rax,rdx + 0xC3 // ret + }; + + [UnmanagedFunctionPointer(CallingConvention.StdCall)] + public delegate bool CpuidDelegate(uint index, uint ecxValue, + out uint eax, out uint ebx, out uint ecx, out uint edx); + + public static CpuidDelegate Cpuid; + + + // void __stdcall cpuidex(unsigned int index, unsigned int ecxValue, + // unsigned int* eax, unsigned int* ebx, unsigned int* ecx, + // unsigned int* edx) + // { + // int info[4]; + // __cpuidex(info, index, ecxValue); + // *eax = info[0]; + // *ebx = info[1]; + // *ecx = info[2]; + // *edx = info[3]; + // } + + private static readonly byte[] CPUID_32 = new byte[] { + 0x55, // push ebp + 0x8B, 0xEC, // mov ebp,esp + 0x83, 0xEC, 0x10, // sub esp,10h + 0x8B, 0x45, 0x08, // mov eax,dword ptr [ebp+8] + 0x8B, 0x4D, 0x0C, // mov ecx,dword ptr [ebp+0Ch] + 0x53, // push ebx + 0x0F, 0xA2, // cpuid + 0x56, // push esi + 0x8D, 0x75, 0xF0, // lea esi,[info] + 0x89, 0x06, // mov dword ptr [esi],eax + 0x8B, 0x45, 0x10, // mov eax,dword ptr [eax] + 0x89, 0x5E, 0x04, // mov dword ptr [esi+4],ebx + 0x89, 0x4E, 0x08, // mov dword ptr [esi+8],ecx + 0x89, 0x56, 0x0C, // mov dword ptr [esi+0Ch],edx + 0x8B, 0x4D, 0xF0, // mov ecx,dword ptr [info] + 0x89, 0x08, // mov dword ptr [eax],ecx + 0x8B, 0x45, 0x14, // mov eax,dword ptr [ebx] + 0x8B, 0x4D, 0xF4, // mov ecx,dword ptr [ebp-0Ch] + 0x89, 0x08, // mov dword ptr [eax],ecx + 0x8B, 0x45, 0x18, // mov eax,dword ptr [ecx] + 0x8B, 0x4D, 0xF8, // mov ecx,dword ptr [ebp-8] + 0x89, 0x08, // mov dword ptr [eax],ecx + 0x8B, 0x45, 0x1C, // mov eax,dword ptr [edx] + 0x8B, 0x4D, 0xFC, // mov ecx,dword ptr [ebp-4] + 0x5E, // pop esi + 0x89, 0x08, // mov dword ptr [eax],ecx + 0x5B, // pop ebx + 0xC9, // leave + 0xC2, 0x18, 0x00 // ret 18h + }; + + private static readonly byte[] CPUID_64 = new byte[] { + 0x48, 0x89, 0x5C, 0x24, 0x08, // mov qword ptr [rsp+8],rbx + 0x8B, 0xC1, // mov eax,ecx + 0x8B, 0xCA, // mov ecx,edx + 0x0F, 0xA2, // cpuid + 0x41, 0x89, 0x00, // mov dword ptr [r8],eax + 0x48, 0x8B, 0x44, 0x24, 0x28, // mov rax,qword ptr [ecx] + 0x41, 0x89, 0x19, // mov dword ptr [r9],ebx + 0x48, 0x8B, 0x5C, 0x24, 0x08, // mov rbx,qword ptr [rsp+8] + 0x89, 0x08, // mov dword ptr [rax],ecx + 0x48, 0x8B, 0x44, 0x24, 0x30, // mov rax,qword ptr [rsp+30h] + 0x89, 0x10, // mov dword ptr [rax],edx + 0xC3 // ret + }; + + public static bool CpuidTx(uint index, uint ecxValue, + out uint eax, out uint ebx, out uint ecx, out uint edx, + UIntPtr threadAffinityMask) { + + IntPtr thread = NativeMethods.GetCurrentThread(); + UIntPtr mask = NativeMethods.SetThreadAffinityMask(thread, + threadAffinityMask); + + if (mask == UIntPtr.Zero) { + eax = ebx = ecx = edx = 0; + return false; + } + + Cpuid(index, ecxValue, out eax, out ebx, out ecx, out edx); + + NativeMethods.SetThreadAffinityMask(thread, mask); + + return true; + } + + [Flags()] + public enum AllocationType : uint { + COMMIT = 0x1000, + RESERVE = 0x2000, + RESET = 0x80000, + LARGE_PAGES = 0x20000000, + PHYSICAL = 0x400000, + TOP_DOWN = 0x100000, + WRITE_WATCH = 0x200000 + } + + [Flags()] + public enum MemoryProtection : uint { + EXECUTE = 0x10, + EXECUTE_READ = 0x20, + EXECUTE_READWRITE = 0x40, + EXECUTE_WRITECOPY = 0x80, + NOACCESS = 0x01, + READONLY = 0x02, + READWRITE = 0x04, + WRITECOPY = 0x08, + GUARD = 0x100, + NOCACHE = 0x200, + WRITECOMBINE = 0x400 + } + + [Flags] + enum FreeType { + DECOMMIT = 0x4000, + RELEASE = 0x8000 + } + + private static class NativeMethods { + private const string KERNEL = "kernel32.dll"; + + [DllImport(KERNEL, CallingConvention = CallingConvention.Winapi)] + public static extern IntPtr VirtualAlloc(IntPtr lpAddress, UIntPtr dwSize, + AllocationType flAllocationType, MemoryProtection flProtect); + + [DllImport(KERNEL, CallingConvention = CallingConvention.Winapi)] + public static extern bool VirtualFree(IntPtr lpAddress, UIntPtr dwSize, + FreeType dwFreeType); + + [DllImport(KERNEL, CallingConvention = CallingConvention.Winapi)] + public static extern UIntPtr + SetThreadAffinityMask(IntPtr handle, UIntPtr mask); + + [DllImport(KERNEL, CallingConvention = CallingConvention.Winapi)] + public static extern IntPtr GetCurrentThread(); + } + } +} diff --git a/Hardware/Ring0.cs b/Hardware/Ring0.cs new file mode 100644 index 0000000..e5fe3a2 --- /dev/null +++ b/Hardware/Ring0.cs @@ -0,0 +1,291 @@ +/* + + Version: MPL 1.1/GPL 2.0/LGPL 2.1 + + The contents of this file are subject to the Mozilla Public License Version + 1.1 (the "License"); you may not use this file except in compliance with + the License. You may obtain a copy of the License at + + http://www.mozilla.org/MPL/ + + Software distributed under the License is distributed on an "AS IS" basis, + WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + for the specific language governing rights and limitations under the License. + + The Original Code is the Open Hardware Monitor code. + + The Initial Developer of the Original Code is + Michael Möller . + Portions created by the Initial Developer are Copyright (C) 2010 + the Initial Developer. All Rights Reserved. + + Contributor(s): + + Alternatively, the contents of this file may be used under the terms of + either the GNU General Public License Version 2 or later (the "GPL"), or + the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + in which case the provisions of the GPL or the LGPL are applicable instead + of those above. If you wish to allow use of your version of this file only + under the terms of either the GPL or the LGPL, and not to allow others to + use your version of this file under the terms of the MPL, indicate your + decision by deleting the provisions above and replace them with the notice + and other provisions required by the GPL or the LGPL. If you do not delete + the provisions above, a recipient may use your version of this file under + the terms of any one of the MPL, the GPL or the LGPL. + +*/ + +using System; +using System.IO; +using System.Reflection; +using System.Runtime.InteropServices; +using System.Threading; + +namespace OpenHardwareMonitor.Hardware { + internal static class Ring0 { + + private static KernelDriver driver; + private static Mutex isaBusMutex; + + private const uint OLS_TYPE = 40000; + private static IOControlCode + IOCTL_OLS_GET_REFCOUNT = new IOControlCode(OLS_TYPE, 0x801, + IOControlCode.Access.Any), + IOCTL_OLS_GET_DRIVER_VERSION = new IOControlCode(OLS_TYPE, 0x800, + IOControlCode.Access.Any), + IOCTL_OLS_READ_MSR = new IOControlCode(OLS_TYPE, 0x821, + IOControlCode.Access.Any), + IOCTL_OLS_WRITE_MSR = new IOControlCode(OLS_TYPE, 0x822, + IOControlCode.Access.Any), + IOCTL_OLS_READ_IO_PORT_BYTE = new IOControlCode(OLS_TYPE, 0x833, + IOControlCode.Access.Read), + IOCTL_OLS_WRITE_IO_PORT_BYTE = new IOControlCode(OLS_TYPE, 0x836, + IOControlCode.Access.Write), + IOCTL_OLS_READ_PCI_CONFIG = new IOControlCode(OLS_TYPE, 0x851, + IOControlCode.Access.Read), + IOCTL_OLS_WRITE_PCI_CONFIG = new IOControlCode(OLS_TYPE, 0x852, + IOControlCode.Access.Write); + + private static bool ExtractDriver(string fileName) { + string resourceName = "OpenHardwareMonitor.Hardware." + + (IntPtr.Size == 4 ? "WinRing0.sys" : "WinRing0x64.sys"); + + string[] names = + Assembly.GetExecutingAssembly().GetManifestResourceNames(); + byte[] buffer = null; + for (int i = 0; i < names.Length; i++) { + if (names[i].Replace('\\', '.') == resourceName) { + using (Stream stream = Assembly.GetExecutingAssembly(). + GetManifestResourceStream(names[i])) + { + buffer = new byte[stream.Length]; + stream.Read(buffer, 0, buffer.Length); + } + } + } + + if (buffer == null) + return false; + + using (FileStream target = new FileStream(fileName, FileMode.Create)) { + target.Write(buffer, 0, buffer.Length); + } + + return true; + } + + public static void Open() { + if (driver != null) + return; + + driver = new KernelDriver("WinRing0_1_2_0"); + driver.Open(); + + if (!driver.IsOpen) { + string fileName = Path.GetTempFileName(); + if (ExtractDriver(fileName)) { + + driver.Install(fileName); + File.Delete(fileName); + + driver.Open(); + + if (!driver.IsOpen) + driver.Delete(); + } + } + + if (!driver.IsOpen) + driver = null; + + isaBusMutex = new Mutex(false, "Access_ISABUS.HTP.Method"); + } + + public static bool IsOpen { + get { return driver != null; } + } + + public static void Close() { + if (driver == null) + return; + + uint refCount = 0; + driver.DeviceIOControl(IOCTL_OLS_GET_REFCOUNT, null, ref refCount); + + driver.Close(); + + if (refCount <= 1) + driver.Delete(); + + driver = null; + + isaBusMutex.Close(); + } + + public static bool WaitIsaBusMutex(int millisecondsTimeout) { + try { + return isaBusMutex.WaitOne(millisecondsTimeout, false); + } catch (AbandonedMutexException) { return false; } + catch (InvalidOperationException) { return false; } + } + + public static void ReleaseIsaBusMutex() { + isaBusMutex.ReleaseMutex(); + } + + public static bool Rdmsr(uint index, out uint eax, out uint edx) { + if (driver == null) { + eax = 0; + edx = 0; + return false; + } + + ulong buffer = 0; + bool result = driver.DeviceIOControl(IOCTL_OLS_READ_MSR, index, + ref buffer); + + edx = (uint)((buffer >> 32) & 0xFFFFFFFF); + eax = (uint)(buffer & 0xFFFFFFFF); + return result; + } + + public static bool RdmsrTx(uint index, out uint eax, out uint edx, + UIntPtr threadAffinityMask) + { + IntPtr thread = NativeMethods.GetCurrentThread(); + UIntPtr mask = NativeMethods.SetThreadAffinityMask(thread, + threadAffinityMask); + + bool result = Rdmsr(index, out eax, out edx); + + NativeMethods.SetThreadAffinityMask(thread, mask); + return result; + } + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + private struct WrmsrInput { + public uint Register; + public ulong Value; + } + + public static bool Wrmsr(uint index, uint eax, uint edx) { + if (driver == null) + return false; + + WrmsrInput input = new WrmsrInput(); + input.Register = index; + input.Value = ((ulong)edx << 32) | eax; + + return driver.DeviceIOControl(IOCTL_OLS_WRITE_MSR, input); + } + + public static byte ReadIoPort(uint port) { + if (driver == null) + return 0; + + uint value = 0; + driver.DeviceIOControl(IOCTL_OLS_READ_IO_PORT_BYTE, port, ref value); + + return (byte)(value & 0xFF); + } + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + private struct WriteIoPortInput { + public uint PortNumber; + public byte Value; + } + + public static void WriteIoPort(uint port, byte value) { + if (driver == null) + return; + + WriteIoPortInput input = new WriteIoPortInput(); + input.PortNumber = port; + input.Value = value; + + driver.DeviceIOControl(IOCTL_OLS_WRITE_IO_PORT_BYTE, input); + } + + public const uint InvalidPciAddress = 0xFFFFFFFF; + + public static uint GetPciAddress(byte bus, byte device, byte function) { + return + (uint)(((bus & 0xFF) << 8) | ((device & 0x1F) << 3) | (function & 7)); + } + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + private struct ReadPciConfigInput { + public uint PciAddress; + public uint RegAddress; + } + + public static bool ReadPciConfig(uint pciAddress, uint regAddress, + out uint value) + { + if (driver == null || (regAddress & 3) != 0) { + value = 0; + return false; + } + + ReadPciConfigInput input = new ReadPciConfigInput(); + input.PciAddress = pciAddress; + input.RegAddress = regAddress; + + value = 0; + return driver.DeviceIOControl(IOCTL_OLS_READ_PCI_CONFIG, input, + ref value); + } + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + private struct WritePciConfigInput { + public uint PciAddress; + public uint RegAddress; + public uint Value; + } + + public static bool WritePciConfig(uint pciAddress, uint regAddress, + uint value) + { + if (driver == null || (regAddress & 3) != 0) + return false; + + WritePciConfigInput input = new WritePciConfigInput(); + input.PciAddress = pciAddress; + input.RegAddress = regAddress; + input.Value = value; + + return driver.DeviceIOControl(IOCTL_OLS_WRITE_PCI_CONFIG, input); + } + + private static class NativeMethods { + private const string KERNEL = "kernel32.dll"; + + [DllImport(KERNEL, CallingConvention = CallingConvention.Winapi)] + public static extern UIntPtr + SetThreadAffinityMask(IntPtr handle, UIntPtr mask); + + [DllImport(KERNEL, CallingConvention = CallingConvention.Winapi)] + public static extern IntPtr GetCurrentThread(); + } + } +} diff --git a/Hardware/WinRing0.cs b/Hardware/WinRing0.cs deleted file mode 100644 index 1d97f2a..0000000 --- a/Hardware/WinRing0.cs +++ /dev/null @@ -1,179 +0,0 @@ -/* - - Version: MPL 1.1/GPL 2.0/LGPL 2.1 - - The contents of this file are subject to the Mozilla Public License Version - 1.1 (the "License"); you may not use this file except in compliance with - the License. You may obtain a copy of the License at - - http://www.mozilla.org/MPL/ - - Software distributed under the License is distributed on an "AS IS" basis, - WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - for the specific language governing rights and limitations under the License. - - The Original Code is the Open Hardware Monitor code. - - The Initial Developer of the Original Code is - Michael Möller . - Portions created by the Initial Developer are Copyright (C) 2009-2010 - the Initial Developer. All Rights Reserved. - - Contributor(s): - - Alternatively, the contents of this file may be used under the terms of - either the GNU General Public License Version 2 or later (the "GPL"), or - the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - in which case the provisions of the GPL or the LGPL are applicable instead - of those above. If you wish to allow use of your version of this file only - under the terms of either the GPL or the LGPL, and not to allow others to - use your version of this file under the terms of the MPL, indicate your - decision by deleting the provisions above and replace them with the notice - and other provisions required by the GPL or the LGPL. If you do not delete - the provisions above, a recipient may use your version of this file under - the terms of any one of the MPL, the GPL or the LGPL. - -*/ - -using System; -using System.Runtime.InteropServices; -using System.Threading; - -namespace OpenHardwareMonitor.Hardware { - - internal class WinRing0 { - - private WinRing0() { } - - public enum OlsDllStatus{ - OLS_DLL_NO_ERROR = 0, - OLS_DLL_UNSUPPORTED_PLATFORM = 1, - OLS_DLL_DRIVER_NOT_LOADED = 2, - OLS_DLL_DRIVER_NOT_FOUND = 3, - OLS_DLL_DRIVER_UNLOADED = 4, - OLS_DLL_DRIVER_NOT_LOADED_ON_NETWORK = 5, - OLS_DLL_UNKNOWN_ERROR = 9 - } - - private static bool available; - private static Mutex isaBusMutex; - - private static string GetDllName() { - int p = (int)Environment.OSVersion.Platform; - if ((p == 4) || (p == 128)) { - if (IntPtr.Size == 4) { - return "libring0.so"; - } else { - return "libring0x64.so"; - } - } else { - if (IntPtr.Size == 4) { - return "WinRing0.dll"; - } else { - return "WinRing0x64.dll"; - } - } - } - - private delegate bool InitializeOlsDelegate(); - private delegate void DeinitializeOlsDelegate(); - - public delegate bool IsCpuidDelegate(); - public delegate bool CpuidTxDelegate(uint index, uint ecxValue, - out uint eax, out uint ebx, out uint ecx, out uint edx, - UIntPtr threadAffinityMask); - public delegate bool RdmsrDelegate(uint index, out uint eax, out uint edx); - public delegate bool RdmsrTxDelegate(uint index, out uint eax, out uint edx, - UIntPtr threadAffinityMask); - public delegate byte ReadIoPortByteDelegate(ushort port); - public delegate void WriteIoPortByteDelegate(ushort port, byte value); - public delegate bool ReadPciConfigDwordExDelegate(uint pciAddress, - uint regAddress, out uint value); - public delegate bool WritePciConfigDwordExDelegate(uint pciAddress, - uint regAddress, uint value); - public delegate bool RdtscDelegate(out uint eax, out uint edx); - public delegate bool RdtscTxDelegate(out uint eax, out uint edx, - UIntPtr threadAffinityMask); - public delegate bool WrmsrDelegate(uint index, uint eax, uint edx); - public delegate bool WrmsrTxDelegate(uint index, uint eax, uint edx, - UIntPtr threadAffinityMask); - - private static readonly InitializeOlsDelegate InitializeOls = - CreateDelegate("InitializeOls"); - private static readonly DeinitializeOlsDelegate DeinitializeOls = - CreateDelegate("DeinitializeOls"); - - public static readonly IsCpuidDelegate IsCpuid = - CreateDelegate("IsCpuid"); - public static readonly CpuidTxDelegate CpuidTx = - CreateDelegate("CpuidTx"); - public static readonly RdmsrDelegate Rdmsr = - CreateDelegate("Rdmsr"); - public static readonly RdmsrTxDelegate RdmsrTx = - CreateDelegate("RdmsrTx"); - public static readonly ReadIoPortByteDelegate ReadIoPortByte = - CreateDelegate("ReadIoPortByte"); - public static readonly WriteIoPortByteDelegate WriteIoPortByte = - CreateDelegate("WriteIoPortByte"); - public static readonly ReadPciConfigDwordExDelegate ReadPciConfigDwordEx = - CreateDelegate("ReadPciConfigDwordEx"); - public static readonly WritePciConfigDwordExDelegate WritePciConfigDwordEx = - CreateDelegate("WritePciConfigDwordEx"); - public static readonly RdtscDelegate Rdtsc = - CreateDelegate("Rdtsc"); - public static readonly RdtscTxDelegate RdtscTx = - CreateDelegate("RdtscTx"); - public static readonly WrmsrDelegate Wrmsr = - CreateDelegate("Wrmsr"); - public static readonly WrmsrTxDelegate WrmsrTx = - CreateDelegate("WrmsrTx"); - - private static T CreateDelegate(string entryPoint) where T : class { - DllImportAttribute attribute = new DllImportAttribute(GetDllName()); - attribute.CallingConvention = CallingConvention.Winapi; - attribute.PreserveSig = true; - attribute.EntryPoint = entryPoint; - attribute.CharSet = CharSet.Auto; - T result; - PInvokeDelegateFactory.CreateDelegate(attribute, out result); - return result; - } - - public static void Open() { - try { - if (InitializeOls != null && InitializeOls()) - available = true; - } catch (DllNotFoundException) { } - - isaBusMutex = new Mutex(false, "Access_ISABUS.HTP.Method"); - } - - public static bool IsAvailable { - get { return available; } - } - - public static void Close() { - if (available) - DeinitializeOls(); - isaBusMutex.Close(); - } - - public static bool WaitIsaBusMutex(int millisecondsTimeout) { - try { - return isaBusMutex.WaitOne(millisecondsTimeout, false); - } catch (AbandonedMutexException) { return false; } - catch (InvalidOperationException) { return false; } - } - - public static void ReleaseIsaBusMutex() { - isaBusMutex.ReleaseMutex(); - } - - public const uint InvalidPciAddress = 0xFFFFFFFF; - - public static uint GetPciAddress(byte bus, byte device, byte function) { - return - (uint)(((bus & 0xFF) << 8) | ((device & 0x1F) << 3) | (function & 7)); - } - } -} diff --git a/External/WinRing0.sys b/Hardware/WinRing0.sys similarity index 100% rename from External/WinRing0.sys rename to Hardware/WinRing0.sys diff --git a/External/WinRing0x64.sys b/Hardware/WinRing0x64.sys similarity index 100% rename from External/WinRing0x64.sys rename to Hardware/WinRing0x64.sys diff --git a/OpenHardwareMonitorLib.csproj b/OpenHardwareMonitorLib.csproj index 4367518..f321e78 100644 --- a/OpenHardwareMonitorLib.csproj +++ b/OpenHardwareMonitorLib.csproj @@ -60,6 +60,7 @@ + @@ -69,6 +70,9 @@ + + + @@ -105,7 +109,6 @@ - @@ -132,7 +135,10 @@ true - + + + +