moel@236: /*
moel@236:  
moel@344:   This Source Code Form is subject to the terms of the Mozilla Public
moel@344:   License, v. 2.0. If a copy of the MPL was not distributed with this
moel@344:   file, You can obtain one at http://mozilla.org/MPL/2.0/.
moel@236:  
moel@344:   Copyright (C) 2010 Michael Möller <mmoeller@openhardwaremonitor.org>
moel@344: 	
moel@236: */
moel@236: 
moel@236: using System;
moel@236: using System.Runtime.InteropServices;
moel@239: using System.Reflection;
moel@236: 
moel@236: namespace OpenHardwareMonitor.Hardware {
moel@236:   internal static class Opcode {
moel@238:     
moel@236:     private static IntPtr codeBuffer;
moel@238:     private static ulong size;
moel@236: 
moel@238:     public static void Open() {  
moel@236:       int p = (int)Environment.OSVersion.Platform;
moel@238:             
moel@236:       byte[] rdtscCode;
moel@236:       byte[] cpuidCode;
moel@236:       if (IntPtr.Size == 4) {
moel@236:         rdtscCode = RDTSC_32;
moel@236:         cpuidCode = CPUID_32;
moel@236:       } else {
moel@236:         rdtscCode = RDTSC_64;
moel@238:         
moel@238:         if ((p == 4) || (p == 128)) { // Unix
moel@238:           cpuidCode = CPUID_64_LINUX;
moel@238:         } else { // Windows
moel@238:           cpuidCode = CPUID_64_WINDOWS;
moel@238:         }
moel@236:       }
moel@238:       
moel@238:       size = (ulong)(rdtscCode.Length + cpuidCode.Length);
moel@239: 
moel@239:       if ((p == 4) || (p == 128)) { // Unix   
moel@239:         Assembly assembly = 
moel@239:           Assembly.Load("Mono.Posix, Version=2.0.0.0, Culture=neutral, " +
moel@239:           "PublicKeyToken=0738eb9f132ed756");
moel@239: 
moel@239:         Type syscall = assembly.GetType("Mono.Unix.Native.Syscall");
moel@239:         MethodInfo mmap = syscall.GetMethod("mmap");
moel@239: 
moel@239:         Type mmapProts = assembly.GetType("Mono.Unix.Native.MmapProts");
moel@239:         object mmapProtsParam = Enum.ToObject(mmapProts,
moel@239:           (int)mmapProts.GetField("PROT_READ").GetValue(null) |
moel@239:           (int)mmapProts.GetField("PROT_WRITE").GetValue(null) |
moel@239:           (int)mmapProts.GetField("PROT_EXEC").GetValue(null));
moel@239: 
moel@239:         Type mmapFlags = assembly.GetType("Mono.Unix.Native.MmapFlags");
moel@239:         object mmapFlagsParam = Enum.ToObject(mmapFlags,
moel@239:           (int)mmapFlags.GetField("MAP_ANONYMOUS").GetValue(null) |
moel@239:           (int)mmapFlags.GetField("MAP_PRIVATE").GetValue(null));
moel@239:         
moel@239:         codeBuffer = (IntPtr)mmap.Invoke(null, new object[] { IntPtr.Zero, 
moel@239:           size, mmapProtsParam, mmapFlagsParam, -1, 0 });        
moel@238:       } else { // Windows
moel@238:         codeBuffer = NativeMethods.VirtualAlloc(IntPtr.Zero,
moel@238:           (UIntPtr)size, AllocationType.COMMIT | AllocationType.RESERVE, 
moel@238:           MemoryProtection.EXECUTE_READWRITE);
moel@238:       }
moel@236: 
moel@236:       Marshal.Copy(rdtscCode, 0, codeBuffer, rdtscCode.Length);
moel@236: 
moel@236:       Rdtsc = Marshal.GetDelegateForFunctionPointer(
moel@236:         codeBuffer, typeof(RdtscDelegate)) as RdtscDelegate;
moel@236: 
moel@236:       IntPtr cpuidAddress = (IntPtr)((long)codeBuffer + rdtscCode.Length);
moel@236:       Marshal.Copy(cpuidCode, 0, cpuidAddress, cpuidCode.Length);
moel@236: 
moel@236:       Cpuid = Marshal.GetDelegateForFunctionPointer(
moel@238:         cpuidAddress, typeof(CpuidDelegate)) as CpuidDelegate;         
moel@236:     }
moel@236: 
moel@236:     public static void Close() {
moel@236:       Rdtsc = null;
moel@236:       Cpuid = null;
moel@238:       
moel@238:       int p = (int)Environment.OSVersion.Platform;
moel@238:       if ((p == 4) || (p == 128)) { // Unix
moel@239:         Assembly assembly =
moel@239:           Assembly.Load("Mono.Posix, Version=2.0.0.0, Culture=neutral, " +
moel@239:           "PublicKeyToken=0738eb9f132ed756");
moel@239: 
moel@239:         Type syscall = assembly.GetType("Mono.Unix.Native.Syscall");
moel@239:         MethodInfo munmap = syscall.GetMethod("munmap");
moel@239:         munmap.Invoke(null, new object[] { codeBuffer, size });
moel@239: 
moel@238:       } else { // Windows
moel@238:         NativeMethods.VirtualFree(codeBuffer, UIntPtr.Zero, 
moel@238:           FreeType.RELEASE);        
moel@238:       }
moel@236:     }
moel@236: 
moel@236:     [UnmanagedFunctionPointer(CallingConvention.StdCall)]
moel@236:     public delegate ulong RdtscDelegate();
moel@236: 
moel@236:     public static RdtscDelegate Rdtsc;
moel@236: 
moel@236:     // unsigned __int64 __stdcall rdtsc() {
moel@236:     //   return __rdtsc();
moel@236:     // }
moel@236: 
moel@236:     private static readonly byte[] RDTSC_32 = new byte[] {
moel@236:       0x0F, 0x31,                     // rdtsc   
moel@236:       0xC3                            // ret  
moel@236:     };
moel@236: 
moel@236:     private static readonly byte[] RDTSC_64 = new byte[] {
moel@236:       0x0F, 0x31,                     // rdtsc  
moel@238:       0x48, 0xC1, 0xE2, 0x20,         // shl rdx, 20h  
moel@238:       0x48, 0x0B, 0xC2,               // or rax, rdx  
moel@236:       0xC3                            // ret  
moel@236:     };
moel@236:     
moel@236:     [UnmanagedFunctionPointer(CallingConvention.StdCall)]
moel@236:     public delegate bool CpuidDelegate(uint index, uint ecxValue,
moel@236:       out uint eax, out uint ebx, out uint ecx, out uint edx);
moel@236: 
moel@236:     public static CpuidDelegate Cpuid;
moel@236: 
moel@236: 
moel@236:     // void __stdcall cpuidex(unsigned int index, unsigned int ecxValue, 
moel@236:     //   unsigned int* eax, unsigned int* ebx, unsigned int* ecx, 
moel@236:     //   unsigned int* edx)
moel@236:     // {
moel@236:     //   int info[4];	
moel@236:     //   __cpuidex(info, index, ecxValue);
moel@236:     //   *eax = info[0];
moel@236:     //   *ebx = info[1];
moel@236:     //   *ecx = info[2];
moel@236:     //   *edx = info[3];
moel@236:     // }
moel@236: 
moel@236:     private static readonly byte[] CPUID_32 = new byte[] {
moel@236:       0x55,                           // push ebp  
moel@238:       0x8B, 0xEC,                     // mov ebp, esp  
moel@238:       0x83, 0xEC, 0x10,               // sub esp, 10h  
moel@238:       0x8B, 0x45, 0x08,               // mov eax, dword ptr [ebp+8]  
moel@238:       0x8B, 0x4D, 0x0C,               // mov ecx, dword ptr [ebp+0Ch]  
moel@236:       0x53,                           // push ebx  
moel@236:       0x0F, 0xA2,                     // cpuid  
moel@236:       0x56,                           // push esi  
moel@238:       0x8D, 0x75, 0xF0,               // lea esi, [info]  
moel@238:       0x89, 0x06,                     // mov dword ptr [esi],eax  
moel@238:       0x8B, 0x45, 0x10,               // mov eax, dword ptr [eax]  
moel@238:       0x89, 0x5E, 0x04,               // mov dword ptr [esi+4], ebx  
moel@238:       0x89, 0x4E, 0x08,               // mov dword ptr [esi+8], ecx  
moel@238:       0x89, 0x56, 0x0C,               // mov dword ptr [esi+0Ch], edx  
moel@238:       0x8B, 0x4D, 0xF0,               // mov ecx, dword ptr [info]  
moel@238:       0x89, 0x08,                     // mov dword ptr [eax], ecx  
moel@238:       0x8B, 0x45, 0x14,               // mov eax, dword ptr [ebx]  
moel@238:       0x8B, 0x4D, 0xF4,               // mov ecx, dword ptr [ebp-0Ch]  
moel@238:       0x89, 0x08,                     // mov dword ptr [eax], ecx  
moel@238:       0x8B, 0x45, 0x18,               // mov eax, dword ptr [ecx]  
moel@238:       0x8B, 0x4D, 0xF8,               // mov ecx, dword ptr [ebp-8]  
moel@238:       0x89, 0x08,                     // mov dword ptr [eax], ecx  
moel@238:       0x8B, 0x45, 0x1C,               // mov eax, dword ptr [edx]  
moel@238:       0x8B, 0x4D, 0xFC,               // mov ecx, dword ptr [ebp-4]  
moel@236:       0x5E,                           // pop esi  
moel@238:       0x89, 0x08,                     // mov dword ptr [eax], ecx  
moel@236:       0x5B,                           // pop ebx  
moel@236:       0xC9,                           // leave  
moel@236:       0xC2, 0x18, 0x00                // ret 18h  
moel@236:     };
moel@236:              
moel@238:     private static readonly byte[] CPUID_64_WINDOWS = new byte[] {
moel@238:       0x48, 0x89, 0x5C, 0x24, 0x08,   // mov qword ptr [rsp+8], rbx  
moel@238:       0x8B, 0xC1,                     // mov eax, ecx  
moel@238:       0x8B, 0xCA,                     // mov ecx, edx        
moel@238:       0x0F, 0xA2,                     // cpuid        
moel@238:       0x41, 0x89, 0x00,               // mov dword ptr [r8], eax        
moel@238:       0x48, 0x8B, 0x44, 0x24, 0x28,   // mov rax, qword ptr [rsp+28h]       
moel@238:       0x41, 0x89, 0x19,               // mov dword ptr [r9], ebx        
moel@238:       0x48, 0x8B, 0x5C, 0x24, 0x08,   // mov rbx, qword ptr [rsp+8]      
moel@238:       0x89, 0x08,                     // mov dword ptr [rax], ecx        
moel@238:       0x48, 0x8B, 0x44, 0x24, 0x30,   // mov rax, qword ptr [rsp+30h]  
moel@238:       0x89, 0x10,                     // mov dword ptr [rax], edx  
moel@236:       0xC3                            // ret  
moel@236:     };
moel@238:     
moel@238:     private static readonly byte[] CPUID_64_LINUX = new byte[] {
moel@238:       0x49, 0x89, 0xD2,               // mov r10, rdx
moel@238:       0x49, 0x89, 0xCB,               // mov r11, rcx
moel@238:       0x53,                           // push rbx
moel@238:       0x89, 0xF8,                     // mov eax, edi
moel@238:       0x89, 0xF1,                     // mov ecx, esi
moel@238:       0x0F, 0xA2,                     // cpuid
moel@238:       0x41, 0x89, 0x02,               // mov dword ptr [r10], eax
moel@238:       0x41, 0x89, 0x1B,               // mov dword ptr [r11], ebx
moel@238:       0x41, 0x89, 0x08,               // mov dword ptr [r8], ecx
moel@238:       0x41, 0x89, 0x11,               // mov dword ptr [r9], edx
moel@238:       0x5B,                           // pop rbx
moel@238:       0xC3,                           // ret
moel@238:     };
moel@236: 
moel@236:     public static bool CpuidTx(uint index, uint ecxValue, 
moel@236:       out uint eax, out uint ebx, out uint ecx, out uint edx, 
moel@238:       ulong threadAffinityMask) {
moel@238:       
moel@238:       ulong mask = ThreadAffinity.Set(threadAffinityMask);
moel@236: 
moel@238:       if (mask == 0) {
moel@236:         eax = ebx = ecx = edx = 0;
moel@236:         return false;
moel@238:       } 
moel@236: 
moel@236:       Cpuid(index, ecxValue, out eax, out ebx, out ecx, out edx);
moel@236: 
moel@238:       ThreadAffinity.Set(mask);      
moel@236:       return true;
moel@236:     }
moel@238:     
moel@236:     [Flags()]
moel@236:     public enum AllocationType : uint {
moel@236:       COMMIT = 0x1000,
moel@236:       RESERVE = 0x2000,
moel@236:       RESET = 0x80000,
moel@236:       LARGE_PAGES = 0x20000000,
moel@236:       PHYSICAL = 0x400000,
moel@236:       TOP_DOWN = 0x100000,
moel@236:       WRITE_WATCH = 0x200000
moel@236:     }
moel@236: 
moel@236:     [Flags()]
moel@236:     public enum MemoryProtection : uint {
moel@236:       EXECUTE = 0x10,
moel@236:       EXECUTE_READ = 0x20,
moel@236:       EXECUTE_READWRITE = 0x40,
moel@236:       EXECUTE_WRITECOPY = 0x80,
moel@236:       NOACCESS = 0x01,
moel@236:       READONLY = 0x02,
moel@236:       READWRITE = 0x04,
moel@236:       WRITECOPY = 0x08,
moel@236:       GUARD = 0x100,
moel@236:       NOCACHE = 0x200,
moel@236:       WRITECOMBINE = 0x400
moel@236:     }
moel@236: 
moel@236:     [Flags]
moel@239:     public enum FreeType {
moel@236:       DECOMMIT = 0x4000,
moel@236:       RELEASE = 0x8000
moel@236:     }
moel@236: 
moel@238:     private static class NativeMethods {      
moel@236:       private const string KERNEL = "kernel32.dll";
moel@236: 
moel@236:       [DllImport(KERNEL, CallingConvention = CallingConvention.Winapi)]
moel@236:       public static extern IntPtr VirtualAlloc(IntPtr lpAddress, UIntPtr dwSize,
moel@236:         AllocationType flAllocationType, MemoryProtection flProtect);
moel@236: 
moel@236:       [DllImport(KERNEL, CallingConvention = CallingConvention.Winapi)]
moel@236:       public static extern bool VirtualFree(IntPtr lpAddress, UIntPtr dwSize,
moel@238:         FreeType dwFreeType);                 
moel@236:     }
moel@236:   }
moel@236: }