Files

286 lines
5.5 KiB
ObjectPascal
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// **************************************************************************************************
// CPUID for Delphi.
// Unit CPUID
// https://github.com/MahdiSafsafi/DDetours
//
// 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
// https://mozilla.org/MPL/2.0/.
// **************************************************************************************************
unit CPUID;
{$IFDEF FPC}
{$MODE DELPHI}
{$WARN 4055 OFF}
{$WARN 4082 OFF}
{$WARN 5057 OFF}
{$ENDIF FPC}
interface
{$I DDetoursDefs.inc}
uses
SysUtils
{$IFNDEF FPC}, LegacyTypes{$ENDIF FPC}
;
type
{ Do not change registers order ! }
TCPUIDStruct = packed record
rEAX: Cardinal; { EAX Register }
rEBX: Cardinal; { EBX Register }
rEDX: Cardinal; { EDX Register }
rECX: Cardinal; { ECX Register }
end;
PCPUIDStruct = ^TCPUIDStruct;
procedure CallCPUID(ID: NativeUInt; var CPUIDStruct: TCPUIDStruct);
function IsCPUIDSupported(): Boolean;
type
TCPUVendor = (vUnknown, vIntel, vAMD, vNextGen);
TCPUEncoding = set of (REX, VEX, EVEX);
TCPUInstructions = set of (iMultiNop);
var
CPUVendor: TCPUVendor;
CPUEncoding: TCPUEncoding;
CPUInsts: TCPUInstructions;
implementation
var
CPUIDSupported: Boolean = False;
function ___IsCPUIDSupported: Boolean;
asm
{$IFDEF CPUX64}
PUSH RCX
MOV RCX,RCX
PUSHFQ
POP RAX
MOV RCX, RAX
XOR RAX, $200000
PUSH RAX
POPFQ
PUSHFQ
POP RAX
XOR RAX, RCX
SHR RAX, 21
AND RAX, 1
PUSH RCX
POPFQ
POP RCX
{$ELSE !CPUX64}
PUSH ECX
PUSHFD
POP EAX { EAX = EFLAGS }
MOV ECX, EAX { Save the original EFLAGS value . }
{
CPUID is supported only if we can modify
bit 21 of EFLAGS register !
}
XOR EAX, $200000
PUSH EAX
POPFD { Set the new EFLAGS value }
PUSHFD
POP EAX { Read EFLAGS }
{
Check if the 21 bit was modified !
If so ==> Return True .
else ==> Return False.
}
XOR EAX, ECX
SHR EAX, 21
AND EAX, 1
PUSH ECX
POPFD { Restore original EFLAGS value . }
POP ECX
{$ENDIF CPUX64}
end;
procedure ___CallCPUID(const ID: NativeInt; var CPUIDStruct);
asm
{
ALL REGISTERS (rDX,rCX,rBX) MUST BE SAVED BEFORE
EXECUTING CPUID INSTRUCTION !
}
{$IFDEF CPUX64}
PUSH R9
PUSH RBX
PUSH RDX
MOV RAX,RCX
MOV R9,RDX
CPUID
{$IFNDEF FPC}
MOV R9.TCPUIDStruct.rEAX,EAX
MOV R9.TCPUIDStruct.rEBX,EBX
MOV R9.TCPUIDStruct.rECX,ECX
MOV R9.TCPUIDStruct.rEDX,EDX
{$ELSE FPC}
MOV [R9].TCPUIDStruct.rEAX,EAX
MOV [R9].TCPUIDStruct.rEBX,EBX
MOV [R9].TCPUIDStruct.rECX,ECX
MOV [R9].TCPUIDStruct.rEDX,EDX
{$ENDIF !FPC}
POP RDX
POP RBX
POP R9
{$ELSE !CPUX64}
PUSH EDI
PUSH ECX
PUSH EBX
MOV EDI,EDX
CPUID
{$IFNDEF FPC}
MOV EDI.TCPUIDStruct.rEAX,EAX
MOV EDI.TCPUIDStruct.rEBX,EBX
MOV EDI.TCPUIDStruct.rECX,ECX
MOV EDI.TCPUIDStruct.rEDX,EDX
{$ELSE FPC}
MOV [EDI].TCPUIDStruct.rEAX,EAX
MOV [EDI].TCPUIDStruct.rEBX,EBX
MOV [EDI].TCPUIDStruct.rECX,ECX
MOV [EDI].TCPUIDStruct.rEDX,EDX
{$ENDIF !FPC}
POP EBX
POP ECX
POP EDI
{$ENDIF CPUX64}
end;
function ___IsAVXSupported: Boolean;
asm
{
Checking for AVX support requires 3 steps:
1) Detect CPUID.1:ECX.OSXSAVE[bit 27] = 1
=> XGETBV enabled for application use
2) Detect CPUID.1:ECX.AVX[bit 28] = 1
=> AVX instructions supported.
3) Issue XGETBV and verify that XCR0[2:1] = 11b
=> XMM state and YMM state are enabled by OS.
}
{ Steps : 1 and 2 }
{$IFDEF CPUX64}
MOV RAX, 1
PUSH RCX
PUSH RBX
PUSH RDX
{$ELSE !CPUX64}
MOV EAX, 1
PUSH ECX
PUSH EBX
PUSH EDX
{$ENDIF CPUX64}
CPUID
AND ECX, $018000000
CMP ECX, $018000000
JNE @@NOT_SUPPORTED
XOR ECX,ECX
{
Delphi does not support XGETBV !
=> We need to use the XGETBV opcodes !
}
DB $0F DB $01 DB $D0 // XGETBV
{ Step :3 }
AND EAX, $06
CMP EAX, $06
JNE @@NOT_SUPPORTED
MOV EAX, 1
JMP @@END
@@NOT_SUPPORTED:
XOR EAX,EAX
@@END:
{$IFDEF CPUX64}
POP RDX
POP RBX
POP RCX
{$ELSE !CPUX64}
POP EDX
POP EBX
POP ECX
{$ENDIF CPUX64}
end;
procedure CallCPUID(ID: NativeUInt; var CPUIDStruct: TCPUIDStruct);
begin
FillChar(CPUIDStruct, SizeOf(TCPUIDStruct), #0);
if not CPUIDSupported then
raise Exception.Create('CPUID instruction not supported.')
else
___CallCPUID(ID, CPUIDStruct);
end;
function IsCPUIDSupported: Boolean;
begin
Result := CPUIDSupported;
end;
type
TVendorName = array [0 .. 12] of AnsiChar;
function GetVendorName(): TVendorName;
var
Info: PCPUIDStruct;
P: PByte;
begin
Result := '';
if not IsCPUIDSupported then
Exit;
Info := GetMemory(SizeOf(TCPUIDStruct));
CallCPUID(0, Info^);
P := PByte(NativeInt(Info) + 4); // Skip EAX !
Move(P^, PByte(@Result[0])^, 12);
FreeMemory(Info);
end;
procedure __Init__;
var
vn: TVendorName;
Info: TCPUIDStruct;
r: Cardinal;
begin
CPUVendor := vUnknown;
{$IFDEF CPUX64}
CPUEncoding := [REX];
{$ELSE !CPUX64}
CPUEncoding := [];
{$ENDIF CPUX64}
CPUInsts := [];
if IsCPUIDSupported then
begin
vn := GetVendorName();
if vn = 'GenuineIntel' then
CPUVendor := vIntel
else if vn = 'AuthenticAMD' then
CPUVendor := vAMD
else if vn = 'NexGenDriven' then
CPUVendor := vNextGen;
CallCPUID(1, Info);
r := Info.rEAX and $F00;
case r of
$F00, $600:
Include(CPUInsts, iMultiNop);
end;
if ___IsAVXSupported then
Include(CPUEncoding, VEX);
end;
end;
initialization
CPUIDSupported := ___IsCPUIDSupported;
__Init__;
end.