/* 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) 2010 Michael Möller */ using System; using System.Runtime.InteropServices; using System.Reflection; namespace OpenHardwareMonitor.Hardware { internal static class Opcode { private static IntPtr codeBuffer; private static ulong size; public static void Open() { byte[] rdtscCode; byte[] cpuidCode; if (IntPtr.Size == 4) { rdtscCode = RDTSC_32; cpuidCode = CPUID_32; } else { rdtscCode = RDTSC_64; if (OperatingSystem.IsUnix) { // Unix cpuidCode = CPUID_64_LINUX; } else { // Windows cpuidCode = CPUID_64_WINDOWS; } } size = (ulong)(rdtscCode.Length + cpuidCode.Length); if (OperatingSystem.IsUnix) { // Unix Assembly assembly = Assembly.Load("Mono.Posix, Version=2.0.0.0, Culture=neutral, " + "PublicKeyToken=0738eb9f132ed756"); Type syscall = assembly.GetType("Mono.Unix.Native.Syscall"); MethodInfo mmap = syscall.GetMethod("mmap"); Type mmapProts = assembly.GetType("Mono.Unix.Native.MmapProts"); object mmapProtsParam = Enum.ToObject(mmapProts, (int)mmapProts.GetField("PROT_READ").GetValue(null) | (int)mmapProts.GetField("PROT_WRITE").GetValue(null) | (int)mmapProts.GetField("PROT_EXEC").GetValue(null)); Type mmapFlags = assembly.GetType("Mono.Unix.Native.MmapFlags"); object mmapFlagsParam = Enum.ToObject(mmapFlags, (int)mmapFlags.GetField("MAP_ANONYMOUS").GetValue(null) | (int)mmapFlags.GetField("MAP_PRIVATE").GetValue(null)); codeBuffer = (IntPtr)mmap.Invoke(null, new object[] { IntPtr.Zero, size, mmapProtsParam, mmapFlagsParam, -1, 0 }); } else { // Windows codeBuffer = NativeMethods.VirtualAlloc(IntPtr.Zero, (UIntPtr)size, 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; if (OperatingSystem.IsUnix) { // Unix Assembly assembly = Assembly.Load("Mono.Posix, Version=2.0.0.0, Culture=neutral, " + "PublicKeyToken=0738eb9f132ed756"); Type syscall = assembly.GetType("Mono.Unix.Native.Syscall"); MethodInfo munmap = syscall.GetMethod("munmap"); munmap.Invoke(null, new object[] { codeBuffer, size }); } else { // Windows 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_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, GroupAffinity affinity) { var previousAffinity = ThreadAffinity.Set(affinity); if (previousAffinity == GroupAffinity.Undefined) { eax = ebx = ecx = edx = 0; return false; } Cpuid(index, ecxValue, out eax, out ebx, out ecx, out edx); ThreadAffinity.Set(previousAffinity); 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] public 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); } } }