Added CPUID support for Linux.

This commit is contained in:
Michael Möller
2010-11-03 22:07:46 +00:00
parent 0e435cafc6
commit 1804e2308b
10 changed files with 208 additions and 133 deletions
+1 -1
View File
@@ -146,7 +146,7 @@ namespace OpenHardwareMonitor.Hardware.CPU {
uint eax, edx;
if (Ring0.RdmsrTx(FIDVID_STATUS, out eax, out edx,
(UIntPtr)(1L << cpuid[i][0].Thread))) {
1UL << 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.
double curMP = 0.5 * ((eax & 0x3F) + 8);
+4 -17
View File
@@ -87,10 +87,8 @@ namespace OpenHardwareMonitor.Hardware.CPU {
ActivateSensor(coreClocks[i]);
}
// set affinity to the first thread for all frequency estimations
IntPtr thread = NativeMethods.GetCurrentThread();
UIntPtr mask = NativeMethods.SetThreadAffinityMask(thread,
(UIntPtr)(1L << cpuid[0][0].Thread));
// set affinity to the first thread for all frequency estimations
ulong mask = ThreadAffinity.Set(1UL << cpuid[0][0].Thread);
uint ctlEax, ctlEdx;
Ring0.Rdmsr(PERF_CTL_0, out ctlEax, out ctlEdx);
@@ -104,7 +102,7 @@ namespace OpenHardwareMonitor.Hardware.CPU {
Ring0.Wrmsr(PERF_CTR_0, ctrEax, ctrEdx);
// restore the thread affinity.
NativeMethods.SetThreadAffinityMask(thread, mask);
ThreadAffinity.Set(mask);
Update();
}
@@ -208,7 +206,7 @@ namespace OpenHardwareMonitor.Hardware.CPU {
uint curEax, curEdx;
if (Ring0.RdmsrTx(COFVID_STATUS, out curEax, out curEdx,
(UIntPtr)(1L << cpuid[i][0].Thread)))
1UL << cpuid[i][0].Thread))
{
// 8:6 CpuDid: current core divisor ID
// 5:0 CpuFid: current core frequency ID
@@ -232,16 +230,5 @@ namespace OpenHardwareMonitor.Hardware.CPU {
}
}
}
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();
}
}
}
-4
View File
@@ -101,10 +101,6 @@ namespace OpenHardwareMonitor.Hardware.CPU {
}
public CPUGroup(ISettings settings) {
// No implementation for cpuid on Unix systems
int p = (int)Environment.OSVersion.Platform;
if ((p == 4) || (p == 128))
return;
CPUID[][] processorThreads = GetProcessorThreads();
this.threads = new CPUID[processorThreads.Length][][];
+1 -1
View File
@@ -105,7 +105,7 @@ namespace OpenHardwareMonitor.Hardware.CPU {
if (thread >= 32)
throw new ArgumentOutOfRangeException("thread");
UIntPtr mask = (UIntPtr)(1L << thread);
ulong mask = 1UL << thread;
if (Opcode.CpuidTx(CPUID_0, 0,
out eax, out ebx, out ecx, out edx, mask)) {
+10 -20
View File
@@ -130,8 +130,12 @@ namespace OpenHardwareMonitor.Hardware.CPU {
}
if (hasTimeStampCounter) {
ulong mask = ThreadAffinity.Set(1UL << cpuid[0][0].Thread);
estimatedTimeStampCounterFrequency =
EstimateTimeStampCounterFrequency();
EstimateTimeStampCounterFrequency();
ThreadAffinity.Set(mask);
} else {
estimatedTimeStampCounterFrequency = 0;
}
@@ -139,7 +143,7 @@ namespace OpenHardwareMonitor.Hardware.CPU {
timeStampCounterFrequency = estimatedTimeStampCounterFrequency;
}
private static double EstimateTimeStampCounterFrequency() {
private static double EstimateTimeStampCounterFrequency() {
// preload the function
EstimateTimeStampCounterFrequency(0);
EstimateTimeStampCounterFrequency(0);
@@ -148,6 +152,7 @@ namespace OpenHardwareMonitor.Hardware.CPU {
List<double> estimatedFrequency = new List<double>(3);
for (int i = 0; i < 3; i++)
estimatedFrequency.Add(1e-6 * EstimateTimeStampCounterFrequency(0.025));
estimatedFrequency.Sort();
return estimatedFrequency[1];
}
@@ -156,7 +161,6 @@ namespace OpenHardwareMonitor.Hardware.CPU {
long ticks = (long)(timeWindow * Stopwatch.Frequency);
ulong countBegin, countEnd;
Thread.BeginThreadAffinity();
long timeBegin = Stopwatch.GetTimestamp() +
(long)Math.Ceiling(0.001 * ticks);
long timeEnd = timeBegin + ticks;
@@ -164,7 +168,6 @@ namespace OpenHardwareMonitor.Hardware.CPU {
countBegin = Opcode.Rdtsc();
while (Stopwatch.GetTimestamp() < timeEnd) { }
countEnd = Opcode.Rdtsc();
Thread.EndThreadAffinity();
return (((double)(countEnd - countBegin)) * Stopwatch.Frequency) /
(timeEnd - timeBegin);
@@ -173,7 +176,7 @@ namespace OpenHardwareMonitor.Hardware.CPU {
private static void AppendMSRData(StringBuilder r, uint msr, int thread) {
uint eax, edx;
if (Ring0.RdmsrTx(msr, out eax, out edx, (UIntPtr)(1L << thread))) {
if (Ring0.RdmsrTx(msr, out eax, out edx, 1UL << thread)) {
r.Append(" ");
r.Append((msr).ToString("X8", CultureInfo.InvariantCulture));
r.Append(" ");
@@ -264,9 +267,7 @@ namespace OpenHardwareMonitor.Hardware.CPU {
if (hasTimeStampCounter && isInvariantTimeStampCounter) {
// make sure always the same thread is used
IntPtr thread = NativeMethods.GetCurrentThread();
UIntPtr mask = NativeMethods.SetThreadAffinityMask(thread,
(UIntPtr)(1L << cpuid[0][0].Thread));
ulong mask = ThreadAffinity.Set(1UL << cpuid[0][0].Thread);
// read time before and after getting the TSC to estimate the error
long firstTime = Stopwatch.GetTimestamp();
@@ -274,7 +275,7 @@ namespace OpenHardwareMonitor.Hardware.CPU {
long time = Stopwatch.GetTimestamp();
// restore the thread affinity mask
NativeMethods.SetThreadAffinityMask(thread, mask);
ThreadAffinity.Set(mask);
double delta = ((double)(time - lastTime)) / Stopwatch.Frequency;
double error = ((double)(time - firstTime)) / Stopwatch.Frequency;
@@ -304,16 +305,5 @@ 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();
}
}
}
+3 -3
View File
@@ -118,7 +118,7 @@ namespace OpenHardwareMonitor.Hardware.CPU {
tjMax = new float[coreCount];
for (int i = 0; i < coreCount; i++) {
if (Ring0.RdmsrTx(IA32_TEMPERATURE_TARGET, out eax,
out edx, (UIntPtr)(1L << cpuid[i][0].Thread))) {
out edx, 1UL << cpuid[i][0].Thread)) {
tjMax[i] = (eax >> 16) & 0xFF;
} else {
tjMax[i] = 100;
@@ -217,7 +217,7 @@ namespace OpenHardwareMonitor.Hardware.CPU {
uint eax, edx;
if (Ring0.RdmsrTx(
IA32_THERM_STATUS_MSR, out eax, out edx,
(UIntPtr)(1L << cpuid[i][0].Thread))) {
1UL << cpuid[i][0].Thread)) {
// if reading is valid
if ((eax & 0x80000000) != 0) {
// get the dist from tjMax from bits 22:16
@@ -237,7 +237,7 @@ namespace OpenHardwareMonitor.Hardware.CPU {
for (int i = 0; i < coreClocks.Length; i++) {
System.Threading.Thread.Sleep(1);
if (Ring0.RdmsrTx(IA32_PERF_STATUS, out eax, out edx,
(UIntPtr)(1L << cpuid[i][0].Thread)))
1UL << cpuid[i][0].Thread))
{
newBusClock =
TimeStampCounterFrequency / timeStampCounterMultiplier;
+91 -69
View File
@@ -37,17 +37,17 @@
using System;
using System.Runtime.InteropServices;
using Mono.Unix.Native;
namespace OpenHardwareMonitor.Hardware {
internal static class Opcode {
private static IntPtr codeBuffer;
private static ulong size;
public static void Open() {
// No implementation for Unix systems
public static void Open() {
int p = (int)Environment.OSVersion.Platform;
if ((p == 4) || (p == 128))
return;
byte[] rdtscCode;
byte[] cpuidCode;
if (IntPtr.Size == 4) {
@@ -55,13 +55,25 @@ namespace OpenHardwareMonitor.Hardware {
cpuidCode = CPUID_32;
} else {
rdtscCode = RDTSC_64;
cpuidCode = CPUID_64;
if ((p == 4) || (p == 128)) { // Unix
cpuidCode = CPUID_64_LINUX;
} else { // Windows
cpuidCode = CPUID_64_WINDOWS;
}
}
size = (ulong)(rdtscCode.Length + cpuidCode.Length);
if ((p == 4) || (p == 128)) { // Unix
codeBuffer = Syscall.mmap(IntPtr.Zero, size,
MmapProts.PROT_READ | MmapProts.PROT_WRITE | MmapProts.PROT_EXEC,
MmapFlags.MAP_ANONYMOUS | MmapFlags.MAP_PRIVATE, -1, 0);
} else { // Windows
codeBuffer = NativeMethods.VirtualAlloc(IntPtr.Zero,
(UIntPtr)size, AllocationType.COMMIT | AllocationType.RESERVE,
MemoryProtection.EXECUTE_READWRITE);
}
codeBuffer = NativeMethods.VirtualAlloc(IntPtr.Zero,
(UIntPtr)(rdtscCode.Length + cpuidCode.Length),
AllocationType.COMMIT | AllocationType.RESERVE,
MemoryProtection.EXECUTE_READWRITE);
Marshal.Copy(rdtscCode, 0, codeBuffer, rdtscCode.Length);
@@ -72,15 +84,20 @@ namespace OpenHardwareMonitor.Hardware {
Marshal.Copy(cpuidCode, 0, cpuidAddress, cpuidCode.Length);
Cpuid = Marshal.GetDelegateForFunctionPointer(
cpuidAddress, typeof(CpuidDelegate)) as CpuidDelegate;
cpuidAddress, typeof(CpuidDelegate)) as CpuidDelegate;
}
public static void Close() {
Rdtsc = null;
Cpuid = null;
NativeMethods.VirtualFree(codeBuffer, UIntPtr.Zero,
FreeType.RELEASE);
int p = (int)Environment.OSVersion.Platform;
if ((p == 4) || (p == 128)) { // Unix
Syscall.munmap(codeBuffer, size);
} else { // Windows
NativeMethods.VirtualFree(codeBuffer, UIntPtr.Zero,
FreeType.RELEASE);
}
}
[UnmanagedFunctionPointer(CallingConvention.StdCall)]
@@ -99,8 +116,8 @@ namespace OpenHardwareMonitor.Hardware {
private static readonly byte[] RDTSC_64 = new byte[] {
0x0F, 0x31, // rdtsc
0x48, 0xC1, 0xE2, 0x20, // shl rdx,20h
0x48, 0x0B, 0xC2, // or rax,rdx
0x48, 0xC1, 0xE2, 0x20, // shl rdx, 20h
0x48, 0x0B, 0xC2, // or rax, rdx
0xC3 // ret
};
@@ -125,71 +142,83 @@ namespace OpenHardwareMonitor.Hardware {
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]
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]
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
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
private static readonly byte[] CPUID_64_WINDOWS = 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 [rsp+28h]
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
};
private static readonly byte[] CPUID_64_LINUX = new byte[] {
0x49, 0x89, 0xD2, // mov r10, rdx
0x49, 0x89, 0xCB, // mov r11, rcx
0x53, // push rbx
0x89, 0xF8, // mov eax, edi
0x89, 0xF1, // mov ecx, esi
0x0F, 0xA2, // cpuid
0x41, 0x89, 0x02, // mov dword ptr [r10], eax
0x41, 0x89, 0x1B, // mov dword ptr [r11], ebx
0x41, 0x89, 0x08, // mov dword ptr [r8], ecx
0x41, 0x89, 0x11, // mov dword ptr [r9], edx
0x5B, // pop rbx
0xC3, // ret
};
public static bool CpuidTx(uint index, uint ecxValue,
out uint eax, out uint ebx, out uint ecx, out uint edx,
UIntPtr threadAffinityMask) {
ulong threadAffinityMask) {
ulong mask = ThreadAffinity.Set(threadAffinityMask);
IntPtr thread = NativeMethods.GetCurrentThread();
UIntPtr mask = NativeMethods.SetThreadAffinityMask(thread,
threadAffinityMask);
if (mask == UIntPtr.Zero) {
if (mask == 0) {
eax = ebx = ecx = edx = 0;
return false;
}
}
Cpuid(index, ecxValue, out eax, out ebx, out ecx, out edx);
NativeMethods.SetThreadAffinityMask(thread, mask);
ThreadAffinity.Set(mask);
return true;
}
[Flags()]
public enum AllocationType : uint {
COMMIT = 0x1000,
@@ -222,7 +251,7 @@ namespace OpenHardwareMonitor.Hardware {
RELEASE = 0x8000
}
private static class NativeMethods {
private static class NativeMethods {
private const string KERNEL = "kernel32.dll";
[DllImport(KERNEL, CallingConvention = CallingConvention.Winapi)]
@@ -231,14 +260,7 @@ namespace OpenHardwareMonitor.Hardware {
[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();
FreeType dwFreeType);
}
}
}
+8 -16
View File
@@ -95,6 +95,11 @@ namespace OpenHardwareMonitor.Hardware {
}
public static void Open() {
// No implementation for Unix systems
int p = (int)Environment.OSVersion.Platform;
if ((p == 4) || (p == 128))
return;
if (driver != null)
return;
@@ -170,15 +175,13 @@ namespace OpenHardwareMonitor.Hardware {
}
public static bool RdmsrTx(uint index, out uint eax, out uint edx,
UIntPtr threadAffinityMask)
ulong threadAffinityMask)
{
IntPtr thread = NativeMethods.GetCurrentThread();
UIntPtr mask = NativeMethods.SetThreadAffinityMask(thread,
threadAffinityMask);
ulong mask = ThreadAffinity.Set(threadAffinityMask);
bool result = Rdmsr(index, out eax, out edx);
NativeMethods.SetThreadAffinityMask(thread, mask);
ThreadAffinity.Set(mask);
return result;
}
@@ -276,16 +279,5 @@ namespace OpenHardwareMonitor.Hardware {
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();
}
}
}
+87
View File
@@ -0,0 +1,87 @@
/*
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 <m.moeller@gmx.ch>.
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 ThreadAffinity {
public static ulong Set(ulong mask) {
if (mask == 0)
return 0;
int p = (int)Environment.OSVersion.Platform;
if ((p == 4) || (p == 128)) { // Unix
ulong result = 0;
if (NativeMethods.sched_getaffinity(0, (IntPtr)Marshal.SizeOf(result),
ref result) != 0)
return 0;
if (NativeMethods.sched_setaffinity(0, (IntPtr)Marshal.SizeOf(mask),
ref mask) != 0)
return 0;
return result;
} else { // Windows
return (ulong)NativeMethods.SetThreadAffinityMask(
NativeMethods.GetCurrentThread(), (UIntPtr)mask);
}
}
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();
private const string LIBC = "libc";
[DllImport(LIBC)]
public static extern int sched_getaffinity(int pid, IntPtr maskSize,
ref ulong mask);
[DllImport(LIBC)]
public static extern int sched_setaffinity(int pid, IntPtr maskSize,
ref ulong mask);
}
}
}