OSDev.org

The Place to Start for Operating System Developers
It is currently Mon Nov 20, 2017 7:55 am

All times are UTC - 6 hours




Post new topic Reply to topic  [ 15 posts ] 
Author Message
 Post subject: here are cpuid with raw output
PostPosted: Wed Oct 18, 2017 7:07 pm 
Offline
Member
Member

Joined: Wed Nov 18, 2015 3:04 pm
Posts: 285
Location: San Jose San Francisco Bay Area
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]#

_________________
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


Top
 Profile  
 
 Post subject: Re: here are cpuid with raw output
PostPosted: Thu Oct 19, 2017 9:39 am 
Offline
Member
Member

Joined: Mon Mar 25, 2013 7:01 pm
Posts: 1179
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) );


Top
 Profile  
 
 Post subject: Re: here are cpuid with raw output
PostPosted: Thu Oct 19, 2017 2:11 pm 
Offline
Member
Member
User avatar

Joined: Fri Feb 17, 2017 4:01 pm
Posts: 209
Location: Ukraine, Bachmut
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);
}
...

_________________
future big goal: ANT - NT-like OS for mips, arm and x86 (and, possibly, ppc and itanium, hehehe).
current smaller goal: efify - UEFI for a couple of boards (mips and arm).


Top
 Profile  
 
 Post subject: Re: here are cpuid with raw output
PostPosted: Thu Oct 19, 2017 2:23 pm 
Offline
Member
Member

Joined: Wed Nov 18, 2015 3:04 pm
Posts: 285
Location: San Jose San Francisco Bay Area
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.

_________________
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


Top
 Profile  
 
 Post subject: Re: here are cpuid with raw output
PostPosted: Thu Oct 19, 2017 2:34 pm 
Offline
Member
Member

Joined: Wed Nov 18, 2015 3:04 pm
Posts: 285
Location: San Jose San Francisco Bay Area
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.

_________________
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


Top
 Profile  
 
 Post subject: Re: here are cpuid with raw output
PostPosted: Thu Oct 19, 2017 2:56 pm 
Offline
Member
Member

Joined: Thu Jul 05, 2007 8:58 am
Posts: 170
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.


Top
 Profile  
 
 Post subject: Re: here are cpuid with raw output
PostPosted: Thu Oct 19, 2017 6:05 pm 
Offline
Member
Member

Joined: Wed Nov 18, 2015 3:04 pm
Posts: 285
Location: San Jose San Francisco Bay Area
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


Top
 Profile  
 
 Post subject: Re: here are cpuid with raw output
PostPosted: Thu Oct 19, 2017 10:12 pm 
Offline
Member
Member

Joined: Thu Jul 05, 2007 8:58 am
Posts: 170
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.


Top
 Profile  
 
 Post subject: Re: here are cpuid with raw output
PostPosted: Fri Oct 20, 2017 1:26 am 
Offline
Member
Member
User avatar

Joined: Sat Jan 15, 2005 12:00 am
Posts: 8148
Location: At his keyboard!
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.


Top
 Profile  
 
 Post subject: Re: here are cpuid with raw output
PostPosted: Fri Oct 20, 2017 2:34 am 
Offline
Member
Member

Joined: Thu Jul 05, 2012 5:12 am
Posts: 870
Location: Finland
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?

_________________
Undefined behavior since 2012


Top
 Profile  
 
 Post subject: Re: here are cpuid with raw output
PostPosted: Fri Oct 20, 2017 12:40 pm 
Offline
Member
Member

Joined: Wed Nov 18, 2015 3:04 pm
Posts: 285
Location: San Jose San Francisco Bay Area
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.

_________________
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


Top
 Profile  
 
 Post subject: Re: here are cpuid with raw output
PostPosted: Fri Oct 20, 2017 12:45 pm 
Offline
Member
Member

Joined: Wed Nov 18, 2015 3:04 pm
Posts: 285
Location: San Jose San Francisco Bay Area
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


Top
 Profile  
 
 Post subject: Re: here are cpuid with raw output
PostPosted: Sun Oct 22, 2017 9:52 am 
Offline
Member
Member

Joined: Mon Mar 25, 2013 7:01 pm
Posts: 1179
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) );


Top
 Profile  
 
 Post subject: Re: here are cpuid with raw output
PostPosted: Fri Oct 27, 2017 7:43 pm 
Offline
Member
Member

Joined: Wed Nov 18, 2015 3:04 pm
Posts: 285
Location: San Jose San Francisco Bay Area
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


Top
 Profile  
 
 Post subject: Re: here are cpuid with raw output
PostPosted: Sat Oct 28, 2017 8:34 am 
Offline
Member
Member
User avatar

Joined: Sat Mar 31, 2012 3:07 am
Posts: 3008
Location: Chichester, UK
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


Top
 Profile  
 
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 15 posts ] 

All times are UTC - 6 hours


Who is online

Users browsing this forum: No registered users and 4 guests


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Jump to:  
Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group