Reputation: 1195
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
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
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
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