CPUID: Difference between revisions

From OSDev.wiki
Jump to navigation Jump to search
[unchecked revision][unchecked revision]
Content added Content deleted
m (Correction to cpuid_string. It was returning value of uninitialized local variable.)
m (Correction to inline asm; input constraint for EAX input was "0" instead of "a")
Line 157: Line 157:
*/
*/
static inline void cpuid(int code, dword *a, dword *d) {
static inline void cpuid(int code, dword *a, dword *d) {
asm volatile("cpuid":"=a"(*a),"=d"(*d):"0"(code):"ecx","ebx");
asm volatile("cpuid":"=a"(*a),"=d"(*d):"a"(code):"ecx","ebx");
}
}
Line 164: Line 164:
static inline int cpuid_string(int code, dword where[4]) {
static inline int cpuid_string(int code, dword where[4]) {
asm volatile("cpuid":"=a"(*where),"=b"(*(where+1)),
asm volatile("cpuid":"=a"(*where),"=b"(*(where+1)),
"=c"(*(where+2)),"=d"(*(where+3)):"0"(code));
"=c"(*(where+2)),"=d"(*(where+3)):"a"(code));
return (int)where[0];
return (int)where[0];
}
}

Revision as of 20:45, 28 April 2012

The CPUID instruction can be used to retrieve various amount of information about your cpu, like its vendor string and model number, the size of internal caches and (more interesting), the list of CPU features supported.

How to use CPUID

Note that prior to use the CPUID instruction, you should also make sure the processor supports it by testing the 'ID' bit (0x200000) in eflags (modifiable only when the CPUID instruction is supported. For systems that don't support CPUID, writing a '1' at that place will have no effect). Here is an assembly routine that checks if CPUID is supported:

; returns 1 if CPUID is supported, 0 otherwise (ZF is also set accordingly)
pushfd ; get
pop eax
mov ecx, eax ; save 
xor eax, 0x200000 ; flip
push eax ; set
popfd
pushfd ; and test
pop eax
xor eax, ecx ; mask changed bits
shr eax, 21 ; move bit 21 to bit 0
and eax, 1 ; and mask others
push ecx
popfd ; restore original flags
ret

The idea of the CPUID instruction is that you can call it with different values in EAX, and it will return different information about the processor. For example, if we want the Vendor ID String (see below), we should code something like that:

mov eax, 0x0
cpuid

There are differences between AMD and Intel. According to the Intel CPUID application note, we should first check the Vendor ID String for "GenuineIntel" before taking out information, such as the Processor Signature, Processor Feature Flags, etc.

CPU Vendor ID String

When called with EAX = 0, CPUID returns the vendor ID string in EBX, EDX and ECX. Writing these to memory in this order results in a 12-character string. These can be tested against known Vendor ID strings:

/* Vendor-strings. */
#define CPUID_VENDOR_OLDAMD       "AMDisbetter!" //early engineering samples of AMD K5 processor
#define CPUID_VENDOR_AMD          "AuthenticAMD"
#define CPUID_VENDOR_INTEL        "GenuineIntel"
#define CPUID_VENDOR_VIA          "CentaurHauls"
#define CPUID_VENDOR_OLDTRANSMETA "TransmetaCPU"
#define CPUID_VENDOR_TRANSMETA    "GenuineTMx86"
#define CPUID_VENDOR_CYRIX        "CyrixInstead"
#define CPUID_VENDOR_CENTAUR      "CentaurHauls"
#define CPUID_VENDOR_NEXGEN       "NexGenDriven"
#define CPUID_VENDOR_UMC          "UMC UMC UMC "
#define CPUID_VENDOR_SIS          "SiS SiS SiS "
#define CPUID_VENDOR_NSC          "Geode by NSC"
#define CPUID_VENDOR_RISE         "RiseRiseRise"

You already know that the Vendor ID String is returned in EBX, ECX, EDX. Let us take an Intel processor. It should return "GenuineIntel". Look at the following text to see how the string is placed in the registers:

      MSB         LSB
EBX = 'u' 'n' 'e' 'G'
EDX = 'I' 'e' 'n' 'i'
ECX = 'l' 'e' 't' 'n' 
 
 Where, MSB stands for "Most Significant Byte" and LSB is the "Least Significant Byte".

Also, EAX is set to the maximum EAX value supported for CPUID calls, as not all queries are supported on all processors.

CPU Features

When called with EAX = 1 (CPUID_GETFEATURES), CPUID returns a bit field in EDX containing the following values. Note that different brands of CPUs may have given different meanings to these. Recent processors also use ECX for features (which form a different set), with which you should be very careful as some old CPUs return bogus information in this register.

enum {
    CPUID_FEAT_ECX_SSE3         = 1 << 0, 
    CPUID_FEAT_ECX_PCLMUL       = 1 << 1,
    CPUID_FEAT_ECX_DTES64       = 1 << 2,
    CPUID_FEAT_ECX_MONITOR      = 1 << 3,  
    CPUID_FEAT_ECX_DS_CPL       = 1 << 4,  
    CPUID_FEAT_ECX_VMX          = 1 << 5,  
    CPUID_FEAT_ECX_SMX          = 1 << 6,  
    CPUID_FEAT_ECX_EST          = 1 << 7,  
    CPUID_FEAT_ECX_TM2          = 1 << 8,  
    CPUID_FEAT_ECX_SSSE3        = 1 << 9,  
    CPUID_FEAT_ECX_CID          = 1 << 10,
    CPUID_FEAT_ECX_FMA          = 1 << 12,
    CPUID_FEAT_ECX_CX16         = 1 << 13, 
    CPUID_FEAT_ECX_ETPRD        = 1 << 14, 
    CPUID_FEAT_ECX_PDCM         = 1 << 15, 
    CPUID_FEAT_ECX_DCA          = 1 << 18, 
    CPUID_FEAT_ECX_SSE4_1       = 1 << 19, 
    CPUID_FEAT_ECX_SSE4_2       = 1 << 20, 
    CPUID_FEAT_ECX_x2APIC       = 1 << 21, 
    CPUID_FEAT_ECX_MOVBE        = 1 << 22, 
    CPUID_FEAT_ECX_POPCNT       = 1 << 23, 
    CPUID_FEAT_ECX_AES          = 1 << 25, 
    CPUID_FEAT_ECX_XSAVE        = 1 << 26, 
    CPUID_FEAT_ECX_OSXSAVE      = 1 << 27, 
    CPUID_FEAT_ECX_AVX          = 1 << 28,

    CPUID_FEAT_EDX_FPU          = 1 << 0,  
    CPUID_FEAT_EDX_VME          = 1 << 1,  
    CPUID_FEAT_EDX_DE           = 1 << 2,  
    CPUID_FEAT_EDX_PSE          = 1 << 3,  
    CPUID_FEAT_EDX_TSC          = 1 << 4,  
    CPUID_FEAT_EDX_MSR          = 1 << 5,  
    CPUID_FEAT_EDX_PAE          = 1 << 6,  
    CPUID_FEAT_EDX_MCE          = 1 << 7,  
    CPUID_FEAT_EDX_CX8          = 1 << 8,  
    CPUID_FEAT_EDX_APIC         = 1 << 9,  
    CPUID_FEAT_EDX_SEP          = 1 << 11, 
    CPUID_FEAT_EDX_MTRR         = 1 << 12, 
    CPUID_FEAT_EDX_PGE          = 1 << 13, 
    CPUID_FEAT_EDX_MCA          = 1 << 14, 
    CPUID_FEAT_EDX_CMOV         = 1 << 15, 
    CPUID_FEAT_EDX_PAT          = 1 << 16, 
    CPUID_FEAT_EDX_PSE36        = 1 << 17, 
    CPUID_FEAT_EDX_PSN          = 1 << 18, 
    CPUID_FEAT_EDX_CLF          = 1 << 19, 
    CPUID_FEAT_EDX_DTES         = 1 << 21, 
    CPUID_FEAT_EDX_ACPI         = 1 << 22, 
    CPUID_FEAT_EDX_MMX          = 1 << 23, 
    CPUID_FEAT_EDX_FXSR         = 1 << 24, 
    CPUID_FEAT_EDX_SSE          = 1 << 25, 
    CPUID_FEAT_EDX_SSE2         = 1 << 26, 
    CPUID_FEAT_EDX_SS           = 1 << 27, 
    CPUID_FEAT_EDX_HTT          = 1 << 28, 
    CPUID_FEAT_EDX_TM1          = 1 << 29, 
    CPUID_FEAT_EDX_IA64         = 1 << 30,
    CPUID_FEAT_EDX_PBE          = 1 << 31
};

Using CPUID from GCC

CPUID can be invoked with various request codes (in eax) and will return values in general registers (much as a built-in service interrupt). The following code is made Public Domain out of Clicker's x86/cpu.h

enum cpuid_requests {
  CPUID_GETVENDORSTRING,
  CPUID_GETFEATURES,
  CPUID_GETTLB,
  CPUID_GETSERIAL,
  
  CPUID_INTELEXTENDED=0x80000000,
  CPUID_INTELFEATURES,
  CPUID_INTELBRANDSTRING,
  CPUID_INTELBRANDSTRINGMORE,
  CPUID_INTELBRANDSTRINGEND,
};
    
/** issue a single request to CPUID. Fits 'intel features', for instance
 *  note that even if only "eax" and "edx" are of interest, other registers
 *  will be modified by the operation, so we need to tell the compiler about it.
 */
static inline void cpuid(int code, dword *a, dword *d) {
  asm volatile("cpuid":"=a"(*a),"=d"(*d):"a"(code):"ecx","ebx");
}
    
/** issue a complete request, storing general registers output as a string
 */
static inline int cpuid_string(int code, dword where[4]) {
  asm volatile("cpuid":"=a"(*where),"=b"(*(where+1)),
               "=c"(*(where+2)),"=d"(*(where+3)):"a"(code));
  return (int)where[0];
}

See Also

Articles

Threads

External Links