CPUID (CPU Identification)

CPUID (CPU Identification) is a set of instructions that is used to fetch information about the Processor such as Processor Topology, Cache Size and much more. It is supported in x86 architectures and was introduced in 1993 by Intel. Today, CPUID (CPU Identification) is supported in many CPU processors including Intel and AMD.

Table of contents:

  1. Basics of CPUID
  2. Calling CPUID instruction in Assembly
  3. Calling CPUID in C and C++
  4. Highest input in CPUID
  5. Platform specific code
  6. What information does CPUID give?

Let us get started with CPUID (CPU Identification).

Basics of CPUID

There are two types of CPUID:

  • Basic: returns basic information about processor
  • Extended: returns information about extended processor

CPUID instructions deal with 4 registers: EAX, EBX, ECX, EDX

Input determine the information returned by the CPUID instruction. CPUID instructions take two inputs:

  • EAX: known as leaf.
  • ECX: known as sub-leaf; Optional input; Needed only for specific functions.

CPUID instructions provide 4 outputs stored in 4 different registers:

  • EAX
  • EBX
  • ECX
  • EDX

To know for which input, what information is provided, you need to consult the CPUID Specification document for the Processor you are using. For example:

Calling CPUID instruction in Assembly

The opcode of CPUID is 0Fh, A2h (= A20Fh). In assembly programming language, CPUID can be used directly as it will use the value stored in EAX and ECX. Therefore, before calling CPUID, the correct input value should be set in EAX and ECX.

Following is a valid assembly code calling CPUID for leaf value 0:

MOV EAX, 00h
CPUID

The above code sets the value of 0 to EAX as input and then, calls the instruction CPUID.

Calling CPUID in C and C++

CPUID can be used in C and C++ as well. Compilers like GCC have a header file cpuid.h which has an utility function __get_cpuid_count to call CPUID instruction for a given input.

The functions __get_cpuid_count and __cpuid_count are defined as follows in the header files:

static __inline int
__get_cpuid_count (unsigned int __leaf, unsigned int __subleaf,
		   unsigned int *__eax, unsigned int *__ebx,
		   unsigned int *__ecx, unsigned int *__edx)
{
  unsigned int __ext = __leaf & 0x80000000;
  unsigned int __maxlevel = __get_cpuid_max (__ext, 0);

  if (__maxlevel == 0 || __maxlevel < __leaf)
    return 0;

  __cpuid_count (__leaf, __subleaf, *__eax, *__ebx, *__ecx, *__edx);
  return 1;
}

#define __cpuid_count(level, count, a, b, c, d)				\
  __asm__ __volatile__ ("cpuid\n\t"					\
			: "=a" (a), "=b" (b), "=c" (c), "=d" (d)	\
			: "0" (level), "2" (count))

There are two main functions:

  • __get_cpuid_count: takes two inputs that is leaf ("eax") and sub-leaf ("ecx")
  • __get_cpuid: takes one input that is leaf ("eax")

When we need to specify the sub-leaf, we have to use __get_cpuid_count. In other cases, one can use anyone of the two functions. If sub-leaf is not needed, then it should be set to 0 in case of __get_cpuid_count.

As a precaution, it is suggested to set EBX and EDX to 0 before calling CPUID. Of ECX is not an input, it should be set to 0 as well. This is not necessary but used as a precautionary measure.

Following is a C code example using __get_cpuid to get cache information:

// Code to get cache information using CPUID
// Works on Intel CPUs
#include "cpuid.h"

int main() {
    int eax = 0, ebx = 0, ecx = 0, edx = 0;
    int leaf = 0x4;

    __get_cpuid(leaf, eax, ebx, ecx, edx);
    // The output is stored in variables: eax, ebx, ecx, edx
    // Use it get the information you need about L2 cache
}

Following is a C code example using __get_cpuid_count to get cache information:

// Code to get L2 cache information using CPUID
// Works on AMD CPUs
#include "cpuid.h"

int main() {
    int eax = 0, ebx = 0, ecx = 0, edx = 0;
    int leaf = 0x80000001D;
    int sub_leaf = 2;

    __get_cpuid_count(leaf, sub_leaf, eax, ebx, ecx, edx);
    // The output is stored in variables: eax, ebx, ecx, edx
    // Use it get the information you need about L2 cache
}

Highest input in CPUID

To get the value of the highest value into CPUID basic instructions, we need to pass 0 in EAX to CPUID instruction. The output in EAX is the value of the highest value into CPUID.

MOV EAX, 00h
CPUID

To get the value of the highest value into CPUID extended instructions, we need to pass 80000000h in EAX to CPUID instruction. The output in EAX is the value of the highest value into CPUID extended instruction.

MOV EAX, 80000000h
CPUID

The output is EBX, ECX and EDX forms the Processor Vendor name.

In short,

Processor Vendor name = Reverse(EBX) + Reverse(EDX) + Reverse(ECX)

Some examples of Processor Vendor names are:

  • GenuineIntel
  • AuthenticAMD
  • AMDisbetter!
  • Microsoft Hv

and many more.

Platform specific code

Code dealing with CPUID can be platform specific code that is it will be working as expected in one platform while it is not working on another platform.

This is because the leaf value for a specific information vary across platforms.

Example: To get cache topology, following is the leaf value for different platforms:

  • 0x4: For Intel CPUs
  • 0x80000005h and 0x80000006h: For Old AMD CPUs

What information does CPUID give?

Any information that is related to Processor including the design of the Processor can be fetched using CPUID instructions. For example, we can get the following information using CPUID using different leaf values:

  • Processor Vendor name
  • Processor name, Serial number
  • Number of Cores
  • Features supported like AVX512
  • L1, L2 and L3 Cache Size
  • Cache Topology
  • Thermal and Power Management

With this article at OpenGenus, you must have the complete idea of what is CPUID and how to use it.