CPUID implementations in C++
Asked Answered
D

3

32

I would like to know if somebody around here has some good examples of a C++ CPUID implementation that can be referenced from any of the managed .net languages.

Also, should this not be the case, should I be aware of certain implementation differences between X86 and X64?

I would like to use CPUID to get info on the machine my software is running on (crashreporting etc...) and I want to keep everything as widely compatible as possible.

Primary reason I ask is because I am a total noob when it comes to writing what will probably be all machine instructions though I have basic knowledge about CPU registers and so on...

Before people start telling me to Google: I found some examples online, but usually they were not meant to allow interaction from managed code and none of the examples were aimed at both X86 and X64. Most examples appeared to be X86 specific.

Dominga answered 3/11, 2009 at 9:1 Comment(0)
A
54

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.

Anonym answered 28/1, 2011 at 1:23 Comment(8)
Great answer. Just a note: you need to include the #include <intrin.h> on Windows to use the __cpuid() function.Softspoken
It's an extremely minor mishap, and an easy fix, but you also forgot to #include <string> ;3Inserted
@Inserted <string> is included by <iostream> on most systems but ok, I'll add it.Anonym
@Anonym Apparently not Visual C++ 2013. I get an error with the use of the ostream operator, without including <string>Inserted
with gcc you can use __get_cpuid, no need to write your ownAssignor
do you need admin rights to get the cpu id? (windows 10)Beckham
If I initialize the class with CPUID cpuID(1); and cout << &cpuID_features.ECX() << endl;, the value is different every time. Shouldn't it be the same? I'm looking for the Processor Info and Feature Bits, EAX=1. More specifically, I'm interested in ECX bit 17 (PCID support).Gainey
@CoDEmanX, looks to me like you are printing the address to a temporary. Why the &?Anonym
G
9

See this MSDN article about __cpuid.

There is a comprehensive sample that compiles with Visual Studio 2005 or better. For Visual Studio 6, you can use this instead of the compiler instrinsic __cpuid:

void __cpuid(int CPUInfo[4], int InfoType)
{
 __asm 
  {
     mov    esi, CPUInfo
     mov    eax, InfoType
     xor    ecx, ecx  
     cpuid  
     mov    dword ptr [esi +  0], eax
     mov    dword ptr [esi +  4], ebx  
     mov    dword ptr [esi +  8], ecx  
     mov    dword ptr [esi + 12], edx  
  }
}

For Visual Studio 2005, you can use this instead of the compiler instrinsic __cpuidex:

void __cpuidex(int CPUInfo[4], int InfoType, int ECXValue)
{
 __asm 
  {
     mov    esi, CPUInfo
     mov    eax, InfoType
     mov    ecx, ECXValue
     cpuid  
     mov    dword ptr [esi +  0], eax
     mov    dword ptr [esi +  4], ebx  
     mov    dword ptr [esi +  8], ecx  
     mov    dword ptr [esi + 12], edx  
  }
}
Garb answered 20/1, 2012 at 13:5 Comment(1)
Defining __cpuid and __cpuidex yourself results in undefined behaviour as the identifiers are reserved.Polak
S
2

Might not be exactly what you are looking for, but Intel have a good article and sample code for enumerating Intel 64 bit platform architectures (processor, cache, etc.) which also seems to cover 32 bit x86 processors.

Suppository answered 3/11, 2009 at 9:18 Comment(3)
I saw this one already, there are some more on intel.com as well as MSDN. But particularly for the one you referenced: for the life of me, I would not be able to figure out how to compile this properly into some native dll's that I could call from my C# code... I am busy trying to learn all things programming beyond managed environments, but the learning curves are steep :)Dominga
IA64 is actually Itanium, whereas that article refers to "Intel 64" which is more commonly called x86_64.Philomenaphiloo
Intel 64 is a correspond of AMD64, not IA64 which is ItaniumAssignor

© 2022 - 2024 — McMap. All rights reserved.