OSDev.org
https://forum.osdev.org/

here are cpuid with raw output
https://forum.osdev.org/viewtopic.php?f=13&t=32503
Page 1 of 2

Author:  ggodw000 [ Wed Oct 18, 2017 7:07 pm ]
Post subject:  here are cpuid with raw output

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:
cc cpuid.c


cc and linux version info:
Code:
(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:
#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:
(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]#

Author:  Octocontrabass [ Thu Oct 19, 2017 9:39 am ]
Post subject:  Re: here are cpuid with raw output

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:
asm( "cpuid" : "=a"(eax_value), "=b"(ebx_value), "=c"(ecx_value), "=d"(edx_value) : "a"(leaf) );

Author:  zaval [ Thu Oct 19, 2017 2:11 pm ]
Post subject:  Re: here are cpuid with raw output

just out of curiosity, why all this
Code:
if (argc != 2 && argc != 1) {
...
exit(0);
}

if (argc == 2) {
...
}else{
...
}
...


and not just
Code:
switch(argc){
case 1:
//default mode
break;
case 2:
//cmdline mode
break;
default:
//usage
exit(0);
}
...

Author:  ggodw000 [ Thu Oct 19, 2017 2:23 pm ]
Post subject:  Re: here are cpuid with raw output

zaval wrote:
just out of curiosity, why all this
Code:
if (argc != 2 && argc != 1) {
...
exit(0);
}

if (argc == 2) {
...
}else{
...
}
...


and not just
Code:
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.

Author:  ggodw000 [ Thu Oct 19, 2017 2:34 pm ]
Post subject:  Re: here are cpuid with raw output

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:
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.

Author:  davidv1992 [ Thu Oct 19, 2017 2:56 pm ]
Post subject:  Re: here are cpuid with raw output

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.

Author:  ggodw000 [ Thu Oct 19, 2017 6:05 pm ]
Post subject:  Re: here are cpuid with raw output

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)?

Author:  davidv1992 [ Thu Oct 19, 2017 10:12 pm ]
Post subject:  Re: here are cpuid with raw output

Code:
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.

Author:  Brendan [ Fri Oct 20, 2017 1:26 am ]
Post subject:  Re: here are cpuid with raw output

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

Author:  Antti [ Fri Oct 20, 2017 2:34 am ]
Post subject:  Re: here are cpuid with raw output

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?

Author:  ggodw000 [ Fri Oct 20, 2017 12:40 pm ]
Post subject:  Re: here are cpuid with raw output

davidv1992 wrote:
Code:
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.

Author:  ggodw000 [ Fri Oct 20, 2017 12:45 pm ]
Post subject:  Re: here are cpuid with raw output

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.

Author:  Octocontrabass [ Sun Oct 22, 2017 9:52 am ]
Post subject:  Re: here are cpuid with raw output

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:
asm( "cpuid" : "=a"(eax_value), "=b"(ebx_value), "=c"(ecx_value), "=d"(edx_value) : "a"(leaf) );

Author:  ggodw000 [ Fri Oct 27, 2017 7:43 pm ]
Post subject:  Re: here are cpuid with raw output

thanks for pointers, I think I should find one hell of a book on that has extensive coverage of inline assembly discussed here.

Author:  iansjack [ Sat Oct 28, 2017 8:34 am ]
Post subject:  Re: here are cpuid with raw output

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

Page 1 of 2 All times are UTC - 6 hours
Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group
http://www.phpbb.com/