TheBlueCat
TheBlueCat

Reputation: 1195

How do I call "cpuid" in Linux?

While writing new code for Windows, I stumbled upon _cpuinfo() from the Windows API. As I am mainly dealing with a Linux environment (GCC) I want to have access to the CPUInfo.

I have tried the following:

#include <iostream>
 
int main()
{
  int a, b;
 
  for (a = 0; a < 5; a++)
  {
    __asm ( "mov %1, %%eax; "            // a into eax
          "cpuid;"
          "mov %%eax, %0;"             // eax into b
          :"=r"(b)                     // output
          :"r"(a)                      // input
          :"%eax","%ebx","%ecx","%edx" // clobbered register
         );
    std::cout << "The CPUID level " << a << " gives EAX= " << b << '\n';
  }
 
  return 0;
}

This use assembly but I don't want to re-invent the wheel. Is there any other way to implement CPUInfo without assembly?

Upvotes: 10

Views: 25998

Answers (3)

famzah
famzah

Reputation: 1706

@UpAndAdam asked for an example a decade ago... I worked on this today, so here you are :)

#include <cpuid.h>
#include <stdio.h>
#include <stdlib.h>
#include <err.h>
#include <string.h>

// https://en.wikipedia.org/wiki/CPUID
// https://github.com/gcc-mirror/gcc/blob/master/gcc/config/i386/cpuid.h

void print_as_ASCII(unsigned int value) {
    for (int i = 0; i < 4; i++) {
        char byte = (value >> (i * 8)) & 0xFF; // Extract byte
        printf("%c", byte); // Print as ASCII character
    }
}

void call_cpuid(unsigned int EAX_input, unsigned int *eax, unsigned int *ebx, unsigned int *ecx, unsigned int *edx) {
        unsigned int ret;

        ret = __get_cpuid(EAX_input, eax, ebx, ecx, edx);
        if (ret != 1) {
                errx(EXIT_FAILURE, "Failed to call CPUID with EAX=%d", EAX_input);
        }
}

int main() {
        unsigned int eax, ebx, ecx, edx;

        // EAX=0: Highest Function Parameter and Manufacturer ID
        call_cpuid(0, &eax, &ebx, &ecx, &edx);

        printf("CPU Manufacturer ID: ");
        print_as_ASCII(ebx);
        print_as_ASCII(edx);
        print_as_ASCII(ecx);
        printf("\n");

        return 0;
}

On Linux this call does not require "root" privileges:

$ gcc -Wall cpuid-example.c -o cpuid-example && ./cpuid-example
CPU Manufacturer ID: GenuineIntel

Upvotes: 2

David Heffernan
David Heffernan

Reputation: 613003

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.

Upvotes: 39

user9876
user9876

Reputation: 11102

for (a =0; a < 5; ++a;)

There should only be two semicolons there. You've got three.

This is basic C/C++ syntax; the CPUID is a red herring.

Upvotes: 8

Related Questions