// 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) LibreHardwareMonitor and Contributors. // Partial Copyright (C) Michael Möller and Contributors. // All Rights Reserved. using System; using System.Diagnostics.CodeAnalysis; using System.Runtime.InteropServices; using Mono.Unix.Native; using Windows.Win32; using Windows.Win32.System.Memory; namespace LibreHardwareMonitor.Hardware; internal static class OpCode { public static CpuidDelegate CpuId; public static RdtscDelegate Rdtsc; private static IntPtr _codeBuffer; private static ulong _size; // 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[] CpuId32 = [ 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[] CpuId64Linux = [ 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 ]; private static readonly byte[] CpuId64Windows = [ 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 ]; // unsigned __int64 __stdcall rdtsc() { // return __rdtsc(); // } private static readonly byte[] Rdtsc32 = [ 0x0F, 0x31, // rdtsc 0xC3 // ret ]; private static readonly byte[] Rdtsc64 = [ 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); [UnmanagedFunctionPointer(CallingConvention.StdCall)] public delegate ulong RdtscDelegate(); #if NET [RequiresUnreferencedCode("Dynamic code generation for OpCode delegates")] #endif public static unsafe void Open() { byte[] rdTscCode; byte[] cpuidCode; if (IntPtr.Size == 4) { rdTscCode = Rdtsc32; cpuidCode = CpuId32; } else { rdTscCode = Rdtsc64; cpuidCode = Software.OperatingSystem.IsUnix ? CpuId64Linux : CpuId64Windows; } _size = (ulong)(rdTscCode.Length + cpuidCode.Length); if (Software.OperatingSystem.IsUnix) { _codeBuffer = Syscall.mmap(IntPtr.Zero, _size, MmapProts.PROT_READ | MmapProts.PROT_WRITE | MmapProts.PROT_EXEC, MmapFlags.MAP_ANONYMOUS | MmapFlags.MAP_PRIVATE, -1, 0); } else { _codeBuffer = (IntPtr)PInvoke.VirtualAlloc(null, (UIntPtr)_size, VIRTUAL_ALLOCATION_TYPE.MEM_COMMIT | VIRTUAL_ALLOCATION_TYPE.MEM_RESERVE, PAGE_PROTECTION_FLAGS.PAGE_EXECUTE_READWRITE); } Marshal.Copy(rdTscCode, 0, _codeBuffer, rdTscCode.Length); Rdtsc = Marshal.GetDelegateForFunctionPointer(_codeBuffer); IntPtr cpuidAddress = (IntPtr)((long)_codeBuffer + rdTscCode.Length); Marshal.Copy(cpuidCode, 0, cpuidAddress, cpuidCode.Length); CpuId = Marshal.GetDelegateForFunctionPointer(cpuidAddress); } public static unsafe void Close() { Rdtsc = null; CpuId = null; if (Software.OperatingSystem.IsUnix) { Syscall.munmap(_codeBuffer, _size); } else { PInvoke.VirtualFree((void*)_codeBuffer, UIntPtr.Zero, VIRTUAL_FREE_TYPE.MEM_RELEASE); } } }