How to Call "Cpuid" in Linux

How do I call cpuid in Linux?

Since you are compiling with GCC then you can include cpuid.h which declares these functions:

/* Return highest supported input value for cpuid instruction.  ext can
be either 0x0 or 0x8000000 to return highest supported value for
basic or extended cpuid information. Function returns 0 if cpuid
is not supported or whatever cpuid returns in eax register. If sig
pointer is non-null, then first four bytes of the signature
(as found in ebx register) are returned in location pointed by sig. */
unsigned int __get_cpuid_max (unsigned int __ext, unsigned int *__sig)

/* Return cpuid data for requested cpuid level, as found in returned
eax, ebx, ecx and edx registers. The function checks if cpuid is
supported and returns 1 for valid cpuid information or 0 for
unsupported cpuid level. All pointers are required to be non-null. */
int __get_cpuid (unsigned int __level,
unsigned int *__eax, unsigned int *__ebx,
unsigned int *__ecx, unsigned int *__edx)

You don't need to, and should not, re-implement this functionality.

Getting the machine serial number and CPU ID using C/C++ in Linux

Here is what the Linux kernel seems to use:

static inline void native_cpuid(unsigned int *eax, unsigned int *ebx,
unsigned int *ecx, unsigned int *edx)
{
/* ecx is often an input as well as an output. */
asm volatile("cpuid"
: "=a" (*eax),
"=b" (*ebx),
"=c" (*ecx),
"=d" (*edx)
: "0" (*eax), "2" (*ecx));
}

which one then can use as e.g.:

#include <stdio.h>

int main(int argc, char **argv)
{
unsigned eax, ebx, ecx, edx;

eax = 1; /* processor info and feature bits */
native_cpuid(&eax, &ebx, &ecx, &edx);

printf("stepping %d\n", eax & 0xF);
printf("model %d\n", (eax >> 4) & 0xF);
printf("family %d\n", (eax >> 8) & 0xF);
printf("processor type %d\n", (eax >> 12) & 0x3);
printf("extended model %d\n", (eax >> 16) & 0xF);
printf("extended family %d\n", (eax >> 20) & 0xFF);

/* EDIT */
eax = 3; /* processor serial number */
native_cpuid(&eax, &ebx, &ecx, &edx);

/** see the CPUID Wikipedia article on which models return the serial
number in which registers. The example here is for
Pentium III */
printf("serial number 0x%08x%08x\n", edx, ecx);

}

Where a good reference on how to use the CPUID instruction is in this Wikipedia article.

EDIT The Wikipedia article says that the serial number was introduced with the Pentium III but was not anymore implemented in later models due to privacy concerns. On a Linux system you can check for the presence of this feature (PSN bit) by doing:

grep -i --color psn /proc/cpuinfo

if this does not show anything, your system does not support a processor serial number.

Differing CPUID usage from high-level languages

As Jester says, in GNU C the cpuid.h wrapper intrinsic is probably your best bet.


There's also __builtin_cpu_supports("popcnt") or "avx" or whatever, which works after you call __builtin_cpu_init(). Only the really major feature-bits are supported, though. For example, the docs don't mention the feature-bit for rdrand, so __builtin_cpu_supports("rdrand") probably doesn't work.



Custom inline-assembly versions:

The implementation from Linux can inline with no wasted instructions, and it looks well-written, so there's no reason to use anything else. It's remotely possible that you might get a complaint about not being able to satisfy the "=b" constraint; if so see below for what clang's cpuid.h does. (But I think that's never necessary and the result of a documentation mistake).

It doesn't actually need volatile, though, if you're using it for the values produced rather than the serializing effect on the pipeline: Running CPUID with the same inputs will give the same result, so we can let the optimizer move it around or hoist it out of loops. (So it runs fewer times). This is probably not helpful because normal code won't use it in a loop in the first place, though.


The source for clang's implementation of cpuid.h does some weird stuff, like preserving %rbx because apparently some x86-64 environments might not be able to satisfy a constraint that uses %rbx as an output operand? The comment is /* x86-64 uses %rbx as the base register, so preserve it. */, but I have no idea what they're talking about. If anything x86-32 PIC code in the SysV ABI uses %ebx for a fixed purpose (as a pointer to the GOT), but I don't know about anything like that for x86-64. Perhaps that code is motivated by a mistake in the ABI documentation? See HJ Lu's mailing list post about it.


Most importantly, the first version in the question (inside main()) is broken because it clobbers the red-zone with push.

To fix it, just tell the compiler the result will be in ebx (with "=b"), and let it worry about saving/restoring ebx/rbx at the start/end of the function.

CPUID implementations in C++

Accessing raw CPUID information is actually very easy, here is a C++ class for that which works in Windows, Linux and OSX:

#ifndef CPUID_H
#define CPUID_H

#ifdef _WIN32
#include <limits.h>
#include <intrin.h>
typedef unsigned __int32 uint32_t;

#else
#include <stdint.h>
#endif

class CPUID {
uint32_t regs[4];

public:
explicit CPUID(unsigned i) {
#ifdef _WIN32
__cpuid((int *)regs, (int)i);

#else
asm volatile
("cpuid" : "=a" (regs[0]), "=b" (regs[1]), "=c" (regs[2]), "=d" (regs[3])
: "a" (i), "c" (0));
// ECX is set to zero for CPUID function 4
#endif
}

const uint32_t &EAX() const {return regs[0];}
const uint32_t &EBX() const {return regs[1];}
const uint32_t &ECX() const {return regs[2];}
const uint32_t &EDX() const {return regs[3];}
};

#endif // CPUID_H

To use it just instantiate an instance of the class, load the CPUID instruction you are interested in and examine the registers. For example:

#include "CPUID.h"

#include <iostream>
#include <string>

using namespace std;

int main(int argc, char *argv[]) {
CPUID cpuID(0); // Get CPU vendor

string vendor;
vendor += string((const char *)&cpuID.EBX(), 4);
vendor += string((const char *)&cpuID.EDX(), 4);
vendor += string((const char *)&cpuID.ECX(), 4);

cout << "CPU vendor = " << vendor << endl;

return 0;
}

This Wikipedia page tells you how to use CPUID: http://en.wikipedia.org/wiki/CPUID

EDIT: Added #include <intrin.h> for Windows, per comments.



Related Topics



Leave a reply



Submit