Hardware/Opcode.cs
author moel.mich
Sun, 08 May 2011 22:10:13 +0000
changeset 279 6bce967ba1b5
parent 238 bddc6e01840a
child 344 3145aadca3d2
permissions -rw-r--r--
Fixed the bus and core clock reading on AMD family 10h model Ah CPUs. The new "Core Performance Boost" feature of these CPUs resulted in very low accuracy of the bus speed (and as a consequence also an inaccurate TSC multiplier). This fixed Issue 205.
moel@236
     1
/*
moel@236
     2
  
moel@236
     3
  Version: MPL 1.1/GPL 2.0/LGPL 2.1
moel@236
     4
moel@236
     5
  The contents of this file are subject to the Mozilla Public License Version
moel@236
     6
  1.1 (the "License"); you may not use this file except in compliance with
moel@236
     7
  the License. You may obtain a copy of the License at
moel@236
     8
 
moel@236
     9
  http://www.mozilla.org/MPL/
moel@236
    10
moel@236
    11
  Software distributed under the License is distributed on an "AS IS" basis,
moel@236
    12
  WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
moel@236
    13
  for the specific language governing rights and limitations under the License.
moel@236
    14
moel@236
    15
  The Original Code is the Open Hardware Monitor code.
moel@236
    16
moel@236
    17
  The Initial Developer of the Original Code is 
moel@236
    18
  Michael Möller <m.moeller@gmx.ch>.
moel@236
    19
  Portions created by the Initial Developer are Copyright (C) 2010
moel@236
    20
  the Initial Developer. All Rights Reserved.
moel@236
    21
moel@236
    22
  Contributor(s):
moel@236
    23
moel@236
    24
  Alternatively, the contents of this file may be used under the terms of
moel@236
    25
  either the GNU General Public License Version 2 or later (the "GPL"), or
moel@236
    26
  the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
moel@236
    27
  in which case the provisions of the GPL or the LGPL are applicable instead
moel@236
    28
  of those above. If you wish to allow use of your version of this file only
moel@236
    29
  under the terms of either the GPL or the LGPL, and not to allow others to
moel@236
    30
  use your version of this file under the terms of the MPL, indicate your
moel@236
    31
  decision by deleting the provisions above and replace them with the notice
moel@236
    32
  and other provisions required by the GPL or the LGPL. If you do not delete
moel@236
    33
  the provisions above, a recipient may use your version of this file under
moel@236
    34
  the terms of any one of the MPL, the GPL or the LGPL.
moel@236
    35
 
moel@236
    36
*/
moel@236
    37
moel@236
    38
using System;
moel@236
    39
using System.Runtime.InteropServices;
moel@239
    40
using System.Reflection;
moel@236
    41
moel@236
    42
namespace OpenHardwareMonitor.Hardware {
moel@236
    43
  internal static class Opcode {
moel@238
    44
    
moel@236
    45
    private static IntPtr codeBuffer;
moel@238
    46
    private static ulong size;
moel@236
    47
moel@238
    48
    public static void Open() {  
moel@236
    49
      int p = (int)Environment.OSVersion.Platform;
moel@238
    50
            
moel@236
    51
      byte[] rdtscCode;
moel@236
    52
      byte[] cpuidCode;
moel@236
    53
      if (IntPtr.Size == 4) {
moel@236
    54
        rdtscCode = RDTSC_32;
moel@236
    55
        cpuidCode = CPUID_32;
moel@236
    56
      } else {
moel@236
    57
        rdtscCode = RDTSC_64;
moel@238
    58
        
moel@238
    59
        if ((p == 4) || (p == 128)) { // Unix
moel@238
    60
          cpuidCode = CPUID_64_LINUX;
moel@238
    61
        } else { // Windows
moel@238
    62
          cpuidCode = CPUID_64_WINDOWS;
moel@238
    63
        }
moel@236
    64
      }
moel@238
    65
      
moel@238
    66
      size = (ulong)(rdtscCode.Length + cpuidCode.Length);
moel@239
    67
moel@239
    68
      if ((p == 4) || (p == 128)) { // Unix   
moel@239
    69
        Assembly assembly = 
moel@239
    70
          Assembly.Load("Mono.Posix, Version=2.0.0.0, Culture=neutral, " +
moel@239
    71
          "PublicKeyToken=0738eb9f132ed756");
moel@239
    72
moel@239
    73
        Type syscall = assembly.GetType("Mono.Unix.Native.Syscall");
moel@239
    74
        MethodInfo mmap = syscall.GetMethod("mmap");
moel@239
    75
moel@239
    76
        Type mmapProts = assembly.GetType("Mono.Unix.Native.MmapProts");
moel@239
    77
        object mmapProtsParam = Enum.ToObject(mmapProts,
moel@239
    78
          (int)mmapProts.GetField("PROT_READ").GetValue(null) |
moel@239
    79
          (int)mmapProts.GetField("PROT_WRITE").GetValue(null) |
moel@239
    80
          (int)mmapProts.GetField("PROT_EXEC").GetValue(null));
moel@239
    81
moel@239
    82
        Type mmapFlags = assembly.GetType("Mono.Unix.Native.MmapFlags");
moel@239
    83
        object mmapFlagsParam = Enum.ToObject(mmapFlags,
moel@239
    84
          (int)mmapFlags.GetField("MAP_ANONYMOUS").GetValue(null) |
moel@239
    85
          (int)mmapFlags.GetField("MAP_PRIVATE").GetValue(null));
moel@239
    86
        
moel@239
    87
        codeBuffer = (IntPtr)mmap.Invoke(null, new object[] { IntPtr.Zero, 
moel@239
    88
          size, mmapProtsParam, mmapFlagsParam, -1, 0 });        
moel@238
    89
      } else { // Windows
moel@238
    90
        codeBuffer = NativeMethods.VirtualAlloc(IntPtr.Zero,
moel@238
    91
          (UIntPtr)size, AllocationType.COMMIT | AllocationType.RESERVE, 
moel@238
    92
          MemoryProtection.EXECUTE_READWRITE);
moel@238
    93
      }
moel@236
    94
moel@236
    95
      Marshal.Copy(rdtscCode, 0, codeBuffer, rdtscCode.Length);
moel@236
    96
moel@236
    97
      Rdtsc = Marshal.GetDelegateForFunctionPointer(
moel@236
    98
        codeBuffer, typeof(RdtscDelegate)) as RdtscDelegate;
moel@236
    99
moel@236
   100
      IntPtr cpuidAddress = (IntPtr)((long)codeBuffer + rdtscCode.Length);
moel@236
   101
      Marshal.Copy(cpuidCode, 0, cpuidAddress, cpuidCode.Length);
moel@236
   102
moel@236
   103
      Cpuid = Marshal.GetDelegateForFunctionPointer(
moel@238
   104
        cpuidAddress, typeof(CpuidDelegate)) as CpuidDelegate;         
moel@236
   105
    }
moel@236
   106
moel@236
   107
    public static void Close() {
moel@236
   108
      Rdtsc = null;
moel@236
   109
      Cpuid = null;
moel@238
   110
      
moel@238
   111
      int p = (int)Environment.OSVersion.Platform;
moel@238
   112
      if ((p == 4) || (p == 128)) { // Unix
moel@239
   113
        Assembly assembly =
moel@239
   114
          Assembly.Load("Mono.Posix, Version=2.0.0.0, Culture=neutral, " +
moel@239
   115
          "PublicKeyToken=0738eb9f132ed756");
moel@239
   116
moel@239
   117
        Type syscall = assembly.GetType("Mono.Unix.Native.Syscall");
moel@239
   118
        MethodInfo munmap = syscall.GetMethod("munmap");
moel@239
   119
        munmap.Invoke(null, new object[] { codeBuffer, size });
moel@239
   120
moel@238
   121
      } else { // Windows
moel@238
   122
        NativeMethods.VirtualFree(codeBuffer, UIntPtr.Zero, 
moel@238
   123
          FreeType.RELEASE);        
moel@238
   124
      }
moel@236
   125
    }
moel@236
   126
moel@236
   127
    [UnmanagedFunctionPointer(CallingConvention.StdCall)]
moel@236
   128
    public delegate ulong RdtscDelegate();
moel@236
   129
moel@236
   130
    public static RdtscDelegate Rdtsc;
moel@236
   131
moel@236
   132
    // unsigned __int64 __stdcall rdtsc() {
moel@236
   133
    //   return __rdtsc();
moel@236
   134
    // }
moel@236
   135
moel@236
   136
    private static readonly byte[] RDTSC_32 = new byte[] {
moel@236
   137
      0x0F, 0x31,                     // rdtsc   
moel@236
   138
      0xC3                            // ret  
moel@236
   139
    };
moel@236
   140
moel@236
   141
    private static readonly byte[] RDTSC_64 = new byte[] {
moel@236
   142
      0x0F, 0x31,                     // rdtsc  
moel@238
   143
      0x48, 0xC1, 0xE2, 0x20,         // shl rdx, 20h  
moel@238
   144
      0x48, 0x0B, 0xC2,               // or rax, rdx  
moel@236
   145
      0xC3                            // ret  
moel@236
   146
    };
moel@236
   147
    
moel@236
   148
    [UnmanagedFunctionPointer(CallingConvention.StdCall)]
moel@236
   149
    public delegate bool CpuidDelegate(uint index, uint ecxValue,
moel@236
   150
      out uint eax, out uint ebx, out uint ecx, out uint edx);
moel@236
   151
moel@236
   152
    public static CpuidDelegate Cpuid;
moel@236
   153
moel@236
   154
moel@236
   155
    // void __stdcall cpuidex(unsigned int index, unsigned int ecxValue, 
moel@236
   156
    //   unsigned int* eax, unsigned int* ebx, unsigned int* ecx, 
moel@236
   157
    //   unsigned int* edx)
moel@236
   158
    // {
moel@236
   159
    //   int info[4];	
moel@236
   160
    //   __cpuidex(info, index, ecxValue);
moel@236
   161
    //   *eax = info[0];
moel@236
   162
    //   *ebx = info[1];
moel@236
   163
    //   *ecx = info[2];
moel@236
   164
    //   *edx = info[3];
moel@236
   165
    // }
moel@236
   166
moel@236
   167
    private static readonly byte[] CPUID_32 = new byte[] {
moel@236
   168
      0x55,                           // push ebp  
moel@238
   169
      0x8B, 0xEC,                     // mov ebp, esp  
moel@238
   170
      0x83, 0xEC, 0x10,               // sub esp, 10h  
moel@238
   171
      0x8B, 0x45, 0x08,               // mov eax, dword ptr [ebp+8]  
moel@238
   172
      0x8B, 0x4D, 0x0C,               // mov ecx, dword ptr [ebp+0Ch]  
moel@236
   173
      0x53,                           // push ebx  
moel@236
   174
      0x0F, 0xA2,                     // cpuid  
moel@236
   175
      0x56,                           // push esi  
moel@238
   176
      0x8D, 0x75, 0xF0,               // lea esi, [info]  
moel@238
   177
      0x89, 0x06,                     // mov dword ptr [esi],eax  
moel@238
   178
      0x8B, 0x45, 0x10,               // mov eax, dword ptr [eax]  
moel@238
   179
      0x89, 0x5E, 0x04,               // mov dword ptr [esi+4], ebx  
moel@238
   180
      0x89, 0x4E, 0x08,               // mov dword ptr [esi+8], ecx  
moel@238
   181
      0x89, 0x56, 0x0C,               // mov dword ptr [esi+0Ch], edx  
moel@238
   182
      0x8B, 0x4D, 0xF0,               // mov ecx, dword ptr [info]  
moel@238
   183
      0x89, 0x08,                     // mov dword ptr [eax], ecx  
moel@238
   184
      0x8B, 0x45, 0x14,               // mov eax, dword ptr [ebx]  
moel@238
   185
      0x8B, 0x4D, 0xF4,               // mov ecx, dword ptr [ebp-0Ch]  
moel@238
   186
      0x89, 0x08,                     // mov dword ptr [eax], ecx  
moel@238
   187
      0x8B, 0x45, 0x18,               // mov eax, dword ptr [ecx]  
moel@238
   188
      0x8B, 0x4D, 0xF8,               // mov ecx, dword ptr [ebp-8]  
moel@238
   189
      0x89, 0x08,                     // mov dword ptr [eax], ecx  
moel@238
   190
      0x8B, 0x45, 0x1C,               // mov eax, dword ptr [edx]  
moel@238
   191
      0x8B, 0x4D, 0xFC,               // mov ecx, dword ptr [ebp-4]  
moel@236
   192
      0x5E,                           // pop esi  
moel@238
   193
      0x89, 0x08,                     // mov dword ptr [eax], ecx  
moel@236
   194
      0x5B,                           // pop ebx  
moel@236
   195
      0xC9,                           // leave  
moel@236
   196
      0xC2, 0x18, 0x00                // ret 18h  
moel@236
   197
    };
moel@236
   198
             
moel@238
   199
    private static readonly byte[] CPUID_64_WINDOWS = new byte[] {
moel@238
   200
      0x48, 0x89, 0x5C, 0x24, 0x08,   // mov qword ptr [rsp+8], rbx  
moel@238
   201
      0x8B, 0xC1,                     // mov eax, ecx  
moel@238
   202
      0x8B, 0xCA,                     // mov ecx, edx        
moel@238
   203
      0x0F, 0xA2,                     // cpuid        
moel@238
   204
      0x41, 0x89, 0x00,               // mov dword ptr [r8], eax        
moel@238
   205
      0x48, 0x8B, 0x44, 0x24, 0x28,   // mov rax, qword ptr [rsp+28h]       
moel@238
   206
      0x41, 0x89, 0x19,               // mov dword ptr [r9], ebx        
moel@238
   207
      0x48, 0x8B, 0x5C, 0x24, 0x08,   // mov rbx, qword ptr [rsp+8]      
moel@238
   208
      0x89, 0x08,                     // mov dword ptr [rax], ecx        
moel@238
   209
      0x48, 0x8B, 0x44, 0x24, 0x30,   // mov rax, qword ptr [rsp+30h]  
moel@238
   210
      0x89, 0x10,                     // mov dword ptr [rax], edx  
moel@236
   211
      0xC3                            // ret  
moel@236
   212
    };
moel@238
   213
    
moel@238
   214
    private static readonly byte[] CPUID_64_LINUX = new byte[] {
moel@238
   215
      0x49, 0x89, 0xD2,               // mov r10, rdx
moel@238
   216
      0x49, 0x89, 0xCB,               // mov r11, rcx
moel@238
   217
      0x53,                           // push rbx
moel@238
   218
      0x89, 0xF8,                     // mov eax, edi
moel@238
   219
      0x89, 0xF1,                     // mov ecx, esi
moel@238
   220
      0x0F, 0xA2,                     // cpuid
moel@238
   221
      0x41, 0x89, 0x02,               // mov dword ptr [r10], eax
moel@238
   222
      0x41, 0x89, 0x1B,               // mov dword ptr [r11], ebx
moel@238
   223
      0x41, 0x89, 0x08,               // mov dword ptr [r8], ecx
moel@238
   224
      0x41, 0x89, 0x11,               // mov dword ptr [r9], edx
moel@238
   225
      0x5B,                           // pop rbx
moel@238
   226
      0xC3,                           // ret
moel@238
   227
    };
moel@236
   228
moel@236
   229
    public static bool CpuidTx(uint index, uint ecxValue, 
moel@236
   230
      out uint eax, out uint ebx, out uint ecx, out uint edx, 
moel@238
   231
      ulong threadAffinityMask) {
moel@238
   232
      
moel@238
   233
      ulong mask = ThreadAffinity.Set(threadAffinityMask);
moel@236
   234
moel@238
   235
      if (mask == 0) {
moel@236
   236
        eax = ebx = ecx = edx = 0;
moel@236
   237
        return false;
moel@238
   238
      } 
moel@236
   239
moel@236
   240
      Cpuid(index, ecxValue, out eax, out ebx, out ecx, out edx);
moel@236
   241
moel@238
   242
      ThreadAffinity.Set(mask);      
moel@236
   243
      return true;
moel@236
   244
    }
moel@238
   245
    
moel@236
   246
    [Flags()]
moel@236
   247
    public enum AllocationType : uint {
moel@236
   248
      COMMIT = 0x1000,
moel@236
   249
      RESERVE = 0x2000,
moel@236
   250
      RESET = 0x80000,
moel@236
   251
      LARGE_PAGES = 0x20000000,
moel@236
   252
      PHYSICAL = 0x400000,
moel@236
   253
      TOP_DOWN = 0x100000,
moel@236
   254
      WRITE_WATCH = 0x200000
moel@236
   255
    }
moel@236
   256
moel@236
   257
    [Flags()]
moel@236
   258
    public enum MemoryProtection : uint {
moel@236
   259
      EXECUTE = 0x10,
moel@236
   260
      EXECUTE_READ = 0x20,
moel@236
   261
      EXECUTE_READWRITE = 0x40,
moel@236
   262
      EXECUTE_WRITECOPY = 0x80,
moel@236
   263
      NOACCESS = 0x01,
moel@236
   264
      READONLY = 0x02,
moel@236
   265
      READWRITE = 0x04,
moel@236
   266
      WRITECOPY = 0x08,
moel@236
   267
      GUARD = 0x100,
moel@236
   268
      NOCACHE = 0x200,
moel@236
   269
      WRITECOMBINE = 0x400
moel@236
   270
    }
moel@236
   271
moel@236
   272
    [Flags]
moel@239
   273
    public enum FreeType {
moel@236
   274
      DECOMMIT = 0x4000,
moel@236
   275
      RELEASE = 0x8000
moel@236
   276
    }
moel@236
   277
moel@238
   278
    private static class NativeMethods {      
moel@236
   279
      private const string KERNEL = "kernel32.dll";
moel@236
   280
moel@236
   281
      [DllImport(KERNEL, CallingConvention = CallingConvention.Winapi)]
moel@236
   282
      public static extern IntPtr VirtualAlloc(IntPtr lpAddress, UIntPtr dwSize,
moel@236
   283
        AllocationType flAllocationType, MemoryProtection flProtect);
moel@236
   284
moel@236
   285
      [DllImport(KERNEL, CallingConvention = CallingConvention.Winapi)]
moel@236
   286
      public static extern bool VirtualFree(IntPtr lpAddress, UIntPtr dwSize,
moel@238
   287
        FreeType dwFreeType);                 
moel@236
   288
    }
moel@236
   289
  }
moel@236
   290
}