Many moons ago, I wrote a library to extract all sorts of info from the CPUID instruction, for both Intel and AMD.
This library can be used for determining things like cache size and configuration, the presence of various instructionset extensions such as MMX, SSE, EM64T and such...

It's still a work-in-progress, but I think most of the basic functionality has been covered. It can be a good alternative for something like CPU-Z, for developers... CPU-Z has a developer library, which is incredibly expensive.
I've decided to make my library opensource, under the BSD license. Hopefully other developers will find such a free library useful, and expand on the current functionality. We may get a full competitor to CPU-Z, at no cost.

Currently the code is Windows-only, but I tried to keep the code as OS-neutral as possible, so it should be easy to port to other OSes. I will try to port it to FreeBSD myself, then a linux version should be trivial.
The code is mostly portable ANSI C, with a bit of inline assembly where necessary. I want to keep the amount of assembly code to a minimum, so that the code remains easy to port to different compilers, OSes etc.
I will also try to add some OS-specific functions, like detecting CPU architecture, total/free memory etc.

I'm creating a Sourceforge account for the project. I'll try to clean up the code a bit, and upload it.
I will post the details for the Sourceforge project later.
Posted on 2009-07-06 08:23:10 by Scali
You kept the asm to a minimum, but you're only supporting x86 variants?
Posted on 2009-07-06 08:41:03 by Homer

You kept the asm to a minimum, but you're only supporting x86 variants?


Yea, for a few reasons:
1) Different compilers have different syntax. I wrote it for MS Visual C++, but for gcc you'd have to rewrite all inline asm. The less assembly you put in, the more portable it is.
2) In 64-bit, Visual C++ doesn't support asm at all. Luckily I don't need it there. There's an intrinsic for the CPUID instruction, and all other asm code simply isn't required in 64-bit mode (the code is mainly for legacy support, to detect CPUs that don't have a CPUID instruction yet. 64-bit x86 has CPUID by definition, so all that code is skipped).

The library will however be written with a plain C interface, so you can easily call it from asm, just like Win32 API functions.
I'll provide a MASM include file if you want.
Posted on 2009-07-06 08:48:03 by Scali
I've created the project here: http://cpuinfo.sourceforge.net

There should be a download of the first version as soon as I figure out how to make it downloadable :)
Posted on 2009-07-06 15:20:51 by Scali
Okay, I've just figured out how to add downloads to the page.
Currently I've put up a zip file which includes the .lib and the .h.
I will have to create a .inc file aswell, for MASM, and I'll create a second zip which contains the full sourcecode.
Posted on 2009-07-08 12:06:54 by Scali
It seems you can just browse the latest sourcecode here, or download it as a tarball:
http://cpuinfo.svn.sourceforge.net/viewvc/cpuinfo/

Doesn't seem to require registering an account or being part of the project team.

But I think I'll just put up a separate zip with the sourcecode anyway, for convenience's sake.

Okay, so my next steps will be:
- Create a .inc for MASM
- Expand the test application to become something like a simplified CPU-Z.
- Get the library running on FreeBSD.
- Add the 32/64-bit launcher application to the project.

Any other suggestions?
Posted on 2009-07-09 03:19:12 by Scali
in Speed.c , GetCmosTick() uses "out" and "in" instructions, which on NT are disabled - do you use a driver or something to make them work?
Posted on 2009-07-09 13:56:57 by Ultrano

in Speed.c , GetCmosTick() uses "out" and "in" instructions, which on NT are disabled - do you use a driver or something to make them work?


That isn't my code, it accidentally got checked in with the rest. As you can see from the comments, it's Intel's own code. Some of the asm routines I used to detect pre-CPUID processors, x87 support and such, comes from Intel's examples.
Posted on 2009-07-09 14:07:08 by Scali
I've updated the project code a bit.
Most notably the TestCPUInfo application now displays some basic info, more or less like CPU-Z. I hope it will give people an idea of what the library can do, and how to extract the information you want for use in your own application.
I've also started work on a Dependency Walker thingie, to check for imports and exports. I've added the code to this project, as it's more or less related, and at this point I don't want to maintain a separate project for it.
The project can be found at http://cpuinfo.sf.net
Attachments:
Posted on 2009-10-08 10:40:11 by Scali
The library can now also compile with GCC, and I've managed to run it on my Pentium II FreeBSD server:

Family: 6
FPU:  6
Model: 5
Stepping: 0
Type: 0
AMD Family: 0
AMD Model: 0
AMD Stepping: 0
Features: 0183F9FF
Extended Features: 00000000
Extended Intel Features: 00000000
Extended AMD Features: 0183F1FF
Brand index: 0
CLFLUSH Line Size: 0
Number of Logical Processors: 1
APIC ID: 0
Clockspeed: 350 MHz
Vendor ID: GenuineIntel
Processor Brand String:
Serial number: I64u
Maximum CPUID input value: 00000000
Maximum extended CPUID input value: 00000000
Cache descriptors:
01: Instruction TLB: 4K-Byte Pages, 4-way set associative, 32 entries
02: Instruction TLB: 4M-Byte Pages, 4-way set associative, 2 entries
03: Data TLB: 4K-Byte Pages, 4-way set associative, 64 entries
43: 2nd-level cache: 512K Bytes, 4-way set associative, 32 byte line size
08: 1st-level instruction cache: 16K Bytes, 4-way set associative, 32 byte line size
04: Data TLB: 4M-Byte Pages, 4-way set associative, 8 entries
0C: 1st-level data cache: 16K Bytes, 4-way set associative, 32 byte line size

Type string: Original OEM Processor
Brand string: This processor does not support the brand identification feature
FPU: 1
MMX: 1
Extended MMX: 0
3DNow!: 0
Extended 3DNow!: 0
SSE: 0
SSE2: 0
SSE3: 0
SSSE3: 0
SSE4.1: 0
SSE4.2: 0
64-bit: 0
HyperThreading: 0
Serial number: 0
L1 code TLB (large) entries: 2
L1 code TLB (large) associativity: 4
L1 data TLB (large) entries: 8
L1 data TLB (large) associativity: 4
L1 code TLB entries: 32
L1 code TLB associativity: 4
L1 data TLB entries: 64
L1 data TLB associativity: 4
L2 data TLB (large) entries: 0
L2 data TLB (large) associativity: 0
L2 data TLB entries: 0
L2 data TLB associativity: 0
L1 code linesize: 32 bytes
L1 code lines per tag: 1
L1 code associativity: 4
L1 code size: 16 KB
L1 data linesize: 32 bytes
L1 data lines per tag: 1
L1 data associativity: 4
L1 data size: 16 KB
L2 data linesize: 32 bytes
L2 data lines per tag: 1
L2 data associativity: 4
L2 data size: 512 KB
L3 data linesize: 0 bytes
L3 data lines per tag: 0
L3 data associativity: 0
L3 data size: 0 KB
Clockspeed: 350 MHz
Posted on 2009-10-19 19:50:04 by Scali
Compiles + runs on 64bit gentoo linux.
Posted on 2009-10-20 05:51:05 by f0dder
Thanks for testing, f0dder.
I've also compiled it on my 64-bit FreeBSD box, seems to work there aswell:
Family: 6
FPU:  6
Model: 23
Stepping: 10
Type: 0
AMD Family: 0
AMD Model: 0
AMD Stepping: 0
Features: BFEBFBFF
Extended Features: 0400E39D
Extended Intel Features: 20100800
Extended AMD Features: 19E3F3FF
Brand index: 0
CLFLUSH Line Size: 8
Number of Logical Processors: 2
APIC ID: 0
Clockspeed: 2501 MHz
Vendor ID: GenuineIntel
Processor Brand String: Pentium(R) Dual-Core  CPU      E5200  @ 2.50GHz
Serial number: I64u
Maximum CPUID input value: 00000000
Maximum extended CPUID input value: 0000000D
Cache descriptors:
B1: Unknown cache descriptor
B0: Instruction TLB: 4M-Byte Pages, 4-way set associative, 128 entries
05: Data TLB: 4-MB Pages, 4-way set associative, 32 entries
F0: 64-byte Prefetching
57: L1 Data TLB: 4-KB pages, 4-way set associative, 16 entries
56: L1 Data TLB: 4-MB pages, 4-way set associative, 16 entries
7D: 2nd-level cache: 2MB, 8-way set associative, 64 byte line size, 128 byte sector size
30: 1st-level instruction cache: 32K Bytes, 8-way set associative, 64 byte line size
B4: Data TLB: 4-KB Pages, 4-way set associative, 256 entries
2C: 1st-level data cache: 32K Bytes, 8-way set associative, 64 byte line size

Type string: Original OEM Processor
Brand string: This processor does not support the brand identification feature
FPU: 1
MMX: 1
Extended MMX: 1
3DNow!: 0
Extended 3DNow!: 0
SSE: 1
SSE2: 1
SSE3: 1
SSSE3: 1
SSE4.1: 0
SSE4.2: 0
64-bit: 1
HyperThreading: 1
Serial number: 0
L1 code TLB (large) entries: 4
L1 code TLB (large) associativity: 4
L1 data TLB (large) entries: 32
L1 data TLB (large) associativity: 4
L1 code TLB entries: 128
L1 code TLB associativity: 4
L1 data TLB entries: 16
L1 data TLB associativity: 4
L2 data TLB (large) entries: 0
L2 data TLB (large) associativity: 0
L2 data TLB entries: 256
L2 data TLB associativity: 4
L1 code linesize: 64 bytes
L1 code lines per tag: 1
L1 code associativity: 8
L1 code size: 32 KB
L1 data linesize: 64 bytes
L1 data lines per tag: 1
L1 data associativity: 8
L1 data size: 32 KB
L2 data linesize: 64 bytes
L2 data lines per tag: 1
L2 data associativity: 8
L2 data size: 2048 KB
L3 data linesize: 0 bytes
L3 data lines per tag: 0
L3 data associativity: 0
L3 data size: 0 KB
Clockspeed: 2501 MHz
Posted on 2009-10-20 08:13:52 by Scali
I've cleaned up the code a bit... moving the Windows-only routines into a separate directory.
I've also added a README.txt with some explanation on what you can find where...
And for the *nix-people, there's now a simple makefile which will build the library and the test application.

I've put up a new 0.3a release at http://cpuinfo.sf.net
This time it includes not only the compiled Windows libraries, but also the full codebase as of release time.
I've also added the Launch3264 thing to the precompiled Windows binaries, so you now automatically get the 64-bit test program on a 64-bit system and all that (as discussed earlier in the 'abstracting 64-bit' thread).
Posted on 2009-10-23 13:51:26 by Scali
I've updated the 0.3a release. The Makefile accidentally had Windows-style line-endings, which for some reason didn't matter for one of my FreeBSD boxes (the one I initally tested on ofcourse), but it did matter on the other, it wouldn't make.
I've also generalized the 64-bit #ifdef routines. Instead of using WIN64, I now check for _M_X64 (MSVC) or __x86_64__ (gcc). So it's now strictly tied to the architecture, using a constant defined by the compiler itself during compile-time, rather than a project setting.

You can again find it here: http://sourceforge.net/projects/cpuinfo/files/
Posted on 2009-10-25 08:43:25 by Scali
I see there's an OS X section on the board now... If anyone has OS X with an x86 processor, could they please download the latest sources with svn (svn co https://cpuinfo.svn.sourceforge.net/svnroot/cpuinfo cpuinfo), make it, and see if TestCPUInfo works?
I believe OS X also uses gcc, so it probably works just as well as on FreeBSD and linux. But I'd like to be sure, and fix any problems if they occur.
Posted on 2009-11-03 02:56:42 by Scali

I see there's an OS X section on the board now... If anyone has OS X with an x86 processor, could they please download the latest sources with svn (svn co https://cpuinfo.svn.sourceforge.net/svnroot/cpuinfo cpuinfo), make it, and see if TestCPUInfo works?
I believe OS X also uses gcc, so it probably works just as well as on FreeBSD and linux. But I'd like to be sure, and fix any problems if they occur.


I have 10.5 on a Core 2 Duo, I'll test it some time this week.
Posted on 2009-11-03 12:49:40 by SpooK
On another note... someone told me that ESET NOD32 antivirus says that my Launch3264 executable is a worm.
Must be a false positive. Unfortunate though, it may give the wrong impression when people download CPUInfo or my 3d engine stuff.
Posted on 2009-11-03 15:40:21 by Scali
Using the supplied Makefile, it didn't compile with XCode 3:


cc -c -o CPUInfo.o CPUInfo.c
CPUInfo.c: In function ‘CPUID’:
CPUInfo.c:404: error: can't find a register in class ‘BREG’ while reloading ‘asm’
make: *** Error 1


I read up on the error and it has to do with using EBX in position independent code. From what I remember about Mach-O, it uses a technique similar to ELF's GOT for PIC, so an issue with using EBX sounds feasible.

I had to change the following in CPUInfo.c around line 404:


__asm ("cpuid"
: "=a" (pRegs->eax), "=b" (pRegs->ebx), "=c" (pRegs->ecx), "=d" (pRegs->edx)
: "a" (index)
);


... to this...


__asm ("cpuid"
: "=a" (pRegs->eax), "=S" (pRegs->ebx), "=c" (pRegs->ecx), "=d" (pRegs->edx)
: "a" (index)
);


... and then it compiled fine.

I ran TestCPUInfo and attached the results to this thread.
Attachments:
Posted on 2009-11-03 22:51:54 by SpooK
Hum, that is a problem... I can't stop cpuid from using the ebx register.
As you can clearly see from the processor brand string, the 4 characters that should come from ebx are now garbled (they are read from esi instead of ebx).
Perhaps the answer is to add ebx to the clobber list of the __asm statement... Weird that it didn't complain about that on linux/FreeBSD though... they all use gcc, and technically ebx is a non-volatile register. Then again, a perfectly valid __asm block in a .cpp file suddenly caused errors when I moved it to a .c file, so who knows what logic or lack thereof is in gcc and its handling of __asm blocks :)

Thanks for testing anyway.
It looks like the clockspeed is also wrong, by the way.

Edit: Could you try to put the =b back, and surround the code with a push %%ebx and pop %%ebx? That might make gcc see that the register is preserved.
Posted on 2009-11-04 03:52:52 by Scali
It appears that the problem is caused by the -fPIC flag of gcc. PIC stands for Position Independent Code. It uses ebx to make the code position-independent (only in 32-bit mode, 64-bit mode probably uses RIP-relative addressing).
Apparently the Apple environment has -fPIC enabled by default.
I've added a workaround for the cpuid instruction. It should now compile right away, and also report the proper string and all that.
Posted on 2009-11-04 14:50:38 by Scali