here are cpuid with raw output

Programming, for all ages and all languages.
ggodw000
Member
Member
Posts: 396
Joined: Wed Nov 18, 2015 3:04 pm
Location: San Jose San Francisco Bay Area
Contact:

here are cpuid with raw output

Post by ggodw000 »

I made simply cpuid utility that can run from linux, after getting sick and tired of finding one. There are /proc/cpuinfo but not useful for automation and/or scripting utility and who know who how the sucker is decoding the latest CPU-s.
So I made the raw output cpuid utility that outputs the eax, ebx, ecx and edx:
for eax=0 through 3: simply run: cpuid
for other cpuid: run: cpuid <eax in value>
Any suggestion, comments or critisizm (but not trolling :) ) is welcomed.

Just drop it into vi editor and

Code: Select all

cc cpuid.c
cc and linux version info:

Code: Select all

(virtualenv) [root@localhost C]# cc --v
gcc version 4.4.7 20120313 (Red Hat 4.4.7-16) (GCC)
(virtualenv) [root@localhost C]# cat /etc/redhat-release
Red Hat Enterprise Linux Server release 6.7 (Santiago)
(virtualenv) [root@localhost C]#

cpuid.c

Code: Select all

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/param.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <sys/time.h>
#include <fcntl.h>

typedef u_int8_t u8;
typedef u_int16_t u16;
typedef u_int32_t u32;
typedef u_int64_t u64;

int
main(int argc, char *argv[])
{
    register int i;
    register int k;
    //int k;
    int j;
    int * cpuidInputs;
    int tmpList[] = {0, 1, 2, 3};
    int arrSize;

    if (argc != 2 && argc != 1) {
        printf("\nUsage: %s <cpuid initial eax value>\n", argv[0]);
        printf("\nUsage: %s (prints our CPUID EAX=0 through EAX=3)\n", argv[0]);
        exit(0);
    }

    if (argc == 2) {
        printf("cmdline mode.");
        int cpuidInput = strtoull(argv[1], NULL, 0);
        printf("\nInput being assigned to eax: %x", cpuidInput);

        cpuidInputs = (int*)malloc(sizeof(int));
        cpuidInputs[0] = cpuidInput;
        arrSize = 1;

        printf("\ncpuidInputs[0]: %x", cpuidInputs[0]);

    } else {
        printf("default mode.");
        cpuidInputs = (int*)calloc(sizeof(int), sizeof(tmpList)/sizeof(int));

        for (j = 0; j < sizeof(tmpList)/sizeof(int); j++) {
            printf("\nConstructing default array: %x...", tmpList[j]);
            cpuidInputs[j] = tmpList[j];
        }
        arrSize = 4;
    }

    for (j = 0; j < arrSize; j ++) {
        k = cpuidInputs[j];
        printf("\n----------------------------");
        printf("\nIssuing CPUID %x", k);

        asm("\t movl %0,%%eax" : "=r"(k));

        //asm("\t movl $0,%eax");

        //asm("\t movl $1,%eax");
        //asm("\t movl %%eax,%0" : "=r"(i));
        //printf("\neax: %x", i);
        //printf("\nChecking back eax...%x", i);

        asm("\t cpuid");

        asm("\t movl %%eax,%0" : "=r"(i));
        printf("\n--cpuid %04x, --eax: %08x", k, i);

        asm("\t movl %%ebx,%0" : "=r"(i));
        printf("\n--cpuid %04x, --ebx: %08x", k, i);

        asm("\t movl %%ecx,%0" : "=r"(i));
        printf("\n--cpuid %04x, --ecx: %08x", k, i);

        asm("\t movl %%edx,%0" : "=r"(i));
        printf("\n--cpuid %04x, --edx: %08x", k, i);
    }

    printf("\nDone.\n");
    exit(0);
}

output, friendly for automation, scripting and/or regexp/grep-ping:

Code: Select all

(virtualenv) [root@localhost C]# ./a.out
default mode.
Constructing default array: 0...
Constructing default array: 1...
Constructing default array: 2...
Constructing default array: 3...
----------------------------
Issuing CPUID 0
--cpuid 0000, --eax: 00000014
--cpuid 0000, --ebx: 00000014
--cpuid 0000, --ecx: 00400ac8
--cpuid 0000, --edx: fbf8fe10
----------------------------
Issuing CPUID 1
--cpuid 0001, --eax: 000406f1
--cpuid 0001, --ebx: 000406f1
--cpuid 0001, --ecx: 00400ac8
--cpuid 0001, --edx: fbf8fe10
----------------------------
Issuing CPUID 2
--cpuid 0002, --eax: 76036301
--cpuid 0002, --ebx: 76036301
--cpuid 0002, --ecx: 00400ac8
--cpuid 0002, --edx: fbf8fe10
----------------------------
Issuing CPUID 3
--cpuid 0003, --eax: 00000000
--cpuid 0003, --ebx: 00000000
--cpuid 0003, --ecx: 00400ac8
--cpuid 0003, --edx: fbf8fe10
Done.
(virtualenv) [root@localhost C]#
key takeaway after spending yrs on sw industry: big issue small because everyone jumps on it and fixes it. small issue is big since everyone ignores and it causes catastrophy later. #devilisinthedetails
Octocontrabass
Member
Member
Posts: 5513
Joined: Mon Mar 25, 2013 7:01 pm

Re: here are cpuid with raw output

Post by Octocontrabass »

ggodw000 wrote:Any suggestion, comments or critisizm (but not trolling :) ) is welcomed.
Your program doesn't work. Compare its output to Intel's or AMD's definition of CPUID and you'll see that it just doesn't make sense.

To make it work, you can either use cpuid.h (provided by GCC and Clang) or replace all of your inline assembly with one line like this:

Code: Select all

asm( "cpuid" : "=a"(eax_value), "=b"(ebx_value), "=c"(ecx_value), "=d"(edx_value) : "a"(leaf) );
User avatar
zaval
Member
Member
Posts: 656
Joined: Fri Feb 17, 2017 4:01 pm
Location: Ukraine, Bachmut
Contact:

Re: here are cpuid with raw output

Post by zaval »

just out of curiosity, why all this

Code: Select all

if (argc != 2 && argc != 1) {
...
exit(0);
}

 if (argc == 2) {
...
}else{
 ...
}
...
and not just

Code: Select all

switch(argc){
case 1:
 //default mode
 break;
case 2:
 //cmdline mode
 break;
default:
 //usage
 exit(0);
}
...
ANT - NT-like OS for x64 and arm64.
efify - UEFI for a couple of boards (mips and arm). suspended due to lost of all the target park boards (russians destroyed our town).
ggodw000
Member
Member
Posts: 396
Joined: Wed Nov 18, 2015 3:04 pm
Location: San Jose San Francisco Bay Area
Contact:

Re: here are cpuid with raw output

Post by ggodw000 »

zaval wrote:just out of curiosity, why all this

Code: Select all

if (argc != 2 && argc != 1) {
...
exit(0);
}

 if (argc == 2) {
...
}else{
 ...
}
...
and not just

Code: Select all

switch(argc){
case 1:
 //default mode
 break;
case 2:
 //cmdline mode
 break;
default:
 //usage
 exit(0);
}
...
Just got lazy, I know logic is pretty dumb but it is a small utility.
key takeaway after spending yrs on sw industry: big issue small because everyone jumps on it and fixes it. small issue is big since everyone ignores and it causes catastrophy later. #devilisinthedetails
ggodw000
Member
Member
Posts: 396
Joined: Wed Nov 18, 2015 3:04 pm
Location: San Jose San Francisco Bay Area
Contact:

Re: here are cpuid with raw output

Post by ggodw000 »

Octocontrabass wrote:
ggodw000 wrote:Any suggestion, comments or critisizm (but not trolling :) ) is welcomed.
Your program doesn't work. Compare its output to Intel's or AMD's definition of CPUID and you'll see that it just doesn't make sense.

To make it work, you can either use cpuid.h (provided by GCC and Clang) or replace all of your inline assembly with one line like this:

Code: Select all

asm( "cpuid" : "=a"(eax_value), "=b"(ebx_value), "=c"(ecx_value), "=d"(edx_value) : "a"(leaf) );
Can you give specifics on why it does not work or which part? I am not sure when I ll be able to look at cpuid.h. it is assuming that all cpuid instruction with input eax and output in ea,b,c,dx. In another word
Eax, ebx, ecx, edx=cpuid(eax). Everything else is called responsibility.
key takeaway after spending yrs on sw industry: big issue small because everyone jumps on it and fixes it. small issue is big since everyone ignores and it causes catastrophy later. #devilisinthedetails
davidv1992
Member
Member
Posts: 223
Joined: Thu Jul 05, 2007 8:58 am

Re: here are cpuid with raw output

Post by davidv1992 »

What specifically is going wrong is that your code does not tell gcc correctly which registers are used, leading to wrong data being moved into the variables.
ggodw000
Member
Member
Posts: 396
Joined: Wed Nov 18, 2015 3:04 pm
Location: San Jose San Francisco Bay Area
Contact:

Re: here are cpuid with raw output

Post by ggodw000 »

davidv1992 wrote:What specifically is going wrong is that your code does not tell gcc correctly which registers are used, leading to wrong data being moved into the variables.
which line(s)?
key takeaway after spending yrs on sw industry: big issue small because everyone jumps on it and fixes it. small issue is big since everyone ignores and it causes catastrophy later. #devilisinthedetails
davidv1992
Member
Member
Posts: 223
Joined: Thu Jul 05, 2007 8:58 am

Re: here are cpuid with raw output

Post by davidv1992 »

Code: Select all

asm("\t movl %0,%%eax" : "=r"(k));

asm("\t cpuid");

asm("\t movl %%eax,%0" : "=r"(i));
printf("\n--cpuid %04x, --eax: %08x", k, i);

asm("\t movl %%ebx,%0" : "=r"(i));
printf("\n--cpuid %04x, --ebx: %08x", k, i);

asm("\t movl %%ecx,%0" : "=r"(i));
printf("\n--cpuid %04x, --ecx: %08x", k, i);

asm("\t movl %%edx,%0" : "=r"(i));
printf("\n--cpuid %04x, --edx: %08x", k, i);
After each of the asm statement, gcc is free to use any registers as it likes. Especially with the function calls, all bets are of what gcc decides those can do to the registers. Hence, by the time you're reading edx, it is unlikely to contain the value put there by cpuid. Furthermore, you are not telling it that the cpuid is modifying any registers, so gcc might actually depend on the value of any of eax-edx being something that was changed by that instruction.
User avatar
Brendan
Member
Member
Posts: 8561
Joined: Sat Jan 15, 2005 12:00 am
Location: At his keyboard!
Contact:

Re: here are cpuid with raw output

Post by Brendan »

Hi,
ggodw000 wrote:I made simply cpuid utility that can run from linux, after getting sick and tired of finding one.
Some notes:
  • Some CPUID levels need to be done multiple times to get all of their information (e.g. "standard level 0x00000004, cache configuration descriptors" where ECX determines which cache you want information for)
  • There are more than just the standard levels. Specifically:
    • AMD defined a whole bunch starting at 0x800000000 (which Intel had to also adopt due to long mode support, etc)
    • Transmeta defined some starting at 0x808600000
    • Centaur defined some starting at 0xC00000000 (mostly for cryptography features)
    • Various hypervisor vendors agreed to use some starting at 0x40000000 (for hypervisor/virtual machine detection, etc)
    • Intel defined some starting at 0x20000000 (for features that only Xeon Phi provides)
  • For some CPUs (Cyrix and IBM blue lightning) you may need to enable the CPUID instruction (via. a CPU configuration IO port) before it will work
  • For some CPUs (NSC or NexGen - can't remember without looking it up) CPUID works but the "ID flag" in EFLAGS (that Intel tells people to use to determine if the CPUID instruction is supported or not) does not work
  • For some CPUs (early Intel Pentium) "standard level 0x00000000" uses a different format (there's no "max. supported level" and no "vendor ID string")
  • For a lot of CPUs (including AMD CPUs from the last 10+ years) there are MSRs that let you change/program the "brand string" and/or "vendor string" and/or feature flags and/or other things reported by CPUID. It's possible (but extremely messy) to detect when something has falsified the real information and report both (what CPUID says, and what CPUID should have said)
Also note that it might be fun to do a brute-force search in an attempt to find any unknown/undocumented levels - e.g. try level 0x00010000, then 0x00020000, then 0x00030000, ....; and if any of them work try doing everything with that prefix (e.g. if 0x12340000 exists, start trying 0x12340001, 0x12340002, ...).


Cheers,

Brendan
For all things; perfection is, and will always remain, impossible to achieve in practice. However; by striving for perfection we create things that are as perfect as practically possible. Let the pursuit of perfection be our guide.
Antti
Member
Member
Posts: 923
Joined: Thu Jul 05, 2012 5:12 am
Location: Finland

Re: here are cpuid with raw output

Post by Antti »

Brendan wrote:
  • For some CPUs (Cyrix and IBM blue lightning) you may need to enable the CPUID instruction (via. a CPU configuration IO port) before it will work
  • For some CPUs (NSC or NexGen - can't remember without looking it up) CPUID works but the "ID flag" in EFLAGS (that Intel tells people to use to determine if the CPUID instruction is supported or not) does not work
If using Intel methods for detecting the CPUID instruction, could it ever be falsely detected as available? In my code, I may accept that CPUID is reported as unavailable even though it might really be available. But not the other way around. Does the Cyrix CPU pass the Intel test (and crash when trying to use it after the test) before those CPU configurations?
ggodw000
Member
Member
Posts: 396
Joined: Wed Nov 18, 2015 3:04 pm
Location: San Jose San Francisco Bay Area
Contact:

Re: here are cpuid with raw output

Post by ggodw000 »

davidv1992 wrote:

Code: Select all

asm("\t movl %0,%%eax" : "=r"(k));

asm("\t cpuid");

asm("\t movl %%eax,%0" : "=r"(i));
printf("\n--cpuid %04x, --eax: %08x", k, i);

asm("\t movl %%ebx,%0" : "=r"(i));
printf("\n--cpuid %04x, --ebx: %08x", k, i);

asm("\t movl %%ecx,%0" : "=r"(i));
printf("\n--cpuid %04x, --ecx: %08x", k, i);

asm("\t movl %%edx,%0" : "=r"(i));
printf("\n--cpuid %04x, --edx: %08x", k, i);
After each of the asm statement, gcc is free to use any registers as it likes. Especially with the function calls, all bets are of what gcc decides those can do to the registers. Hence, by the time you're reading edx, it is unlikely to contain the value put there by cpuid. Furthermore, you are not telling it that the cpuid is modifying any registers, so gcc might actually depend on the value of any of eax-edx being something that was changed by that instruction.
So that means asm call itself could be thrashing any of registers, since there is no other line between the calls. I wondered if there is a way to fit all the series of instructions in one asm() call making it atomic.
key takeaway after spending yrs on sw industry: big issue small because everyone jumps on it and fixes it. small issue is big since everyone ignores and it causes catastrophy later. #devilisinthedetails
ggodw000
Member
Member
Posts: 396
Joined: Wed Nov 18, 2015 3:04 pm
Location: San Jose San Francisco Bay Area
Contact:

Re: here are cpuid with raw output

Post by ggodw000 »

Brendan wrote:Hi,
ggodw000 wrote:I made simply cpuid utility that can run from linux, after getting sick and tired of finding one.
Some notes:
  • Some CPUID levels need to be done multiple times to get all of their information (e.g. "standard level 0x00000004, cache configuration descriptors" where ECX determines which cache you want information for)
  • There are more than just the standard levels. Specifically:
    • AMD defined a whole bunch starting at 0x800000000 (which Intel had to also adopt due to long mode support, etc)
    • Transmeta defined some starting at 0x808600000
    • Centaur defined some starting at 0xC00000000 (mostly for cryptography features)
    • Various hypervisor vendors agreed to use some starting at 0x40000000 (for hypervisor/virtual machine detection, etc)
    • Intel defined some starting at 0x20000000 (for features that only Xeon Phi provides)
  • For some CPUs (Cyrix and IBM blue lightning) you may need to enable the CPUID instruction (via. a CPU configuration IO port) before it will work
  • For some CPUs (NSC or NexGen - can't remember without looking it up) CPUID works but the "ID flag" in EFLAGS (that Intel tells people to use to determine if the CPUID instruction is supported or not) does not work
  • For some CPUs (early Intel Pentium) "standard level 0x00000000" uses a different format (there's no "max. supported level" and no "vendor ID string")
  • For a lot of CPUs (including AMD CPUs from the last 10+ years) there are MSRs that let you change/program the "brand string" and/or "vendor string" and/or feature flags and/or other things reported by CPUID. It's possible (but extremely messy) to detect when something has falsified the real information and report both (what CPUID says, and what CPUID should have said)
Also note that it might be fun to do a brute-force search in an attempt to find any unknown/undocumented levels - e.g. try level 0x00010000, then 0x00020000, then 0x00030000, ....; and if any of them work try doing everything with that prefix (e.g. if 0x12340000 exists, start trying 0x12340001, 0x12340002, ...).


Cheers,

Brendan
Since i mostly work with Intel Xeon, I need to explicitly state in the program that other CPUs are not guaranteed.
But, let's remind that this is not supposed to be the full fledged program that can intelligently detect and identify CPU-s and act accordingly, rather, does perform cpuid instruction fast and raw. Anything vendor specifics are on the responsibility of the caller which very well include the list you aforementioned above. :)

Basically what it implements is:
- User can input the EAX value as command line argument
- Output of EAX, EBX, ECX and EDX are displayed in a parseable format to stdout.

But i have to say it needs workout in regards to that assembler mishap davidv1992 was mentioning about.
key takeaway after spending yrs on sw industry: big issue small because everyone jumps on it and fixes it. small issue is big since everyone ignores and it causes catastrophy later. #devilisinthedetails
Octocontrabass
Member
Member
Posts: 5513
Joined: Mon Mar 25, 2013 7:01 pm

Re: here are cpuid with raw output

Post by Octocontrabass »

ggodw000 wrote:I wondered if there is a way to fit all the series of instructions in one asm() call making it atomic.
Of course there is. You can put as many instructions in a single asm() call as you want.

But, for CPUID, you don't need more than one instruction. If you use the correct input and output constraints, GCC will automatically generate the instructions to load EAX before CPUID and read the outputs afterwards.

In fact, I already showed you one possible way to write it.

Code: Select all

asm( "cpuid" : "=a"(eax_value), "=b"(ebx_value), "=c"(ecx_value), "=d"(edx_value) : "a"(leaf) );
ggodw000
Member
Member
Posts: 396
Joined: Wed Nov 18, 2015 3:04 pm
Location: San Jose San Francisco Bay Area
Contact:

Re: here are cpuid with raw output

Post by ggodw000 »

thanks for pointers, I think I should find one hell of a book on that has extensive coverage of inline assembly discussed here.
key takeaway after spending yrs on sw industry: big issue small because everyone jumps on it and fixes it. small issue is big since everyone ignores and it causes catastrophy later. #devilisinthedetails
User avatar
iansjack
Member
Member
Posts: 4685
Joined: Sat Mar 31, 2012 3:07 am
Location: Chichester, UK

Re: here are cpuid with raw output

Post by iansjack »

I'm not sure if many books deal with this sort of detail about GCC, but the GNU documentation does:
https://gcc.gnu.org/onlinedocs/gcc/Usin ... ith-C.html
Post Reply