|
|
/*++
Copyright (c) 1999 Microsoft Corporation
Module Name:
cpuid.c
Abstract:
Originally written to test the fix to an OS bug but indended to be expanded as time allows to dump out as much useful stuff as we can.
Author:
Peter L Johnston (peterj) July 14, 1999.
Revision History:
Notes:
--*/
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <winbase.h>
VOID PrintFeatures( ULONG_PTR Result, PUCHAR FeatureBitDescription[] );
#if defined(_X86_)
typedef enum { CPU_NONE, CPU_INTEL, CPU_AMD, CPU_CYRIX, CPU_UNKNOWN } CPU_VENDORS;
PUCHAR X86FeatureBitDescription[] = { /* 0 */ "FPU 387 (Floating Point) instructions", /* 1 */ "VME Virtual 8086 Mode Enhancements", /* 2 */ "DE Debugging Extensions", /* 3 */ "PSE Page Size Extensions (4MB pages)", /* 4 */ "TSC Time Stamp Counter", /* 5 */ "MSR Model Specific Registers (RDMSR/WRMSR)", /* 6 */ "PAE Physical Address Extension (> 32 bit physical addressing)", /* 7 */ "MCE Machine Check Exception", /* 8 */ "CX8 CMPXCHG8B (compare and exchange 8 byte)", /* 9 */ "APIC Advanced Programmable Interrupt Controller", /* 10 */ "Reserved", /* 11 */ "SEP Fast System Call (SYSENTER/SYSEXIT)", /* 12 */ "MTRR Memory Type Range Registers", /* 13 */ "PGE PTE Global Flag", /* 14 */ "MCA Machine Check Architecture", /* 15 */ "CMOV Conditional Move and Compare", /* 16 */ "PAT Page Attribute Table", /* 17 */ "PSE36 4MB pages can have 36 bit physical addresses", /* 18 */ "PN 96 bit Processor Number", /* 19 */ "CLFLSH Cache Line Flush", /* 20 */ "Reserved", /* 21 */ "DTS Debug Trace Store", /* 22 */ "ACPI ACPI Thermal Throttle Registers", /* 23 */ "MMX Multi Media eXtensions", /* 24 */ "FXSR Fast Save/Restore (FXSAVE/FXRSTOR)", /* 25 */ "XMM Streaming Simd Extensions", /* 26 */ "WNI Willamette New Instructions", /* 27 */ "SLFSNP Self Snoop", /* 28 */ "JT Jackson Technology (SMT)", /* 29 */ "ATHROT Automatic Thermal Throttle", /* 30 */ "IA64 64 Bit Intel Architecture", /* 31 */ "Reserved" };
PUCHAR AMDExtendedFeatureBitDescription[] = { /* 0 */ "FPU 387 (Floating Point) instructions", /* 1 */ "VME Virtual 8086 Mode Enhancements", /* 2 */ "DE Debugging Extensions", /* 3 */ "PSE Page Size Extensions (4MB pages)", /* 4 */ "TSC Time Stamp Counter", /* 5 */ "MSR Model Specific Registers (RDMSR/WRMSR)", /* 6 */ "PAE Physical Address Extension (> 32 bit physical addressing)", /* 7 */ "MCE Machine Check Exception", /* 8 */ "CX8 CMPXCHG8B (compare and exchange 8 byte)", /* 9 */ "APIC Advanced Programmable Interrupt Controller", /* 10 */ "Reserved", /* 11 */ " SYSCALL and SYSRET Instructions", /* 12 */ "MTRR Memory Type Range Registers", /* 13 */ "PGE PTE Global Flag", /* 14 */ "MCA Machine Check Architecture", /* 15 */ "CMOV Conditional Move and Compare", /* 16 */ "PAT Page Attribute Table", /* 17 */ "PSE36 4MB pages can have 36 bit physical addresses", /* 18 */ "Reserved", /* 19 */ "Reserved", /* 20 */ "Reserved", /* 21 */ "Reserved", /* 22 */ " AMD MMX Instruction Extensions", /* 23 */ "MMX Multi Media eXtensions", /* 24 */ "FXSR Fast Save/Restore (FXSAVE/FXRSTOR)", /* 25 */ "Reserved", /* 26 */ "Reserved", /* 27 */ "Reserved", /* 28 */ "Reserved", /* 29 */ "Reserved", /* 30 */ " AMD 3DNow! Instruction Extensions", /* 31 */ " 3DNow! Instructions", };
// From Intel AP-485, Order Number: 241618-021, May 2002
PUCHAR BrandIndex[] = { "Intel Celeron", // 0x01
"Intel Pentium III", // 0x02
"Intel Pentium III Xeon", // 0x03
"Intel Pentium III", // 0x04
"Reserved", // 0x05
"Mobile Intel Pentium IIIM", // 0x06
"Mobile Intel Celeron", // 0x07
"Intel Pentium 4", // 0x08
"Intel Pentium 4", // 0x09
"Intel Celeron", // 0x0a
"Intel Xeon", // 0x0b
"Intel Xeon MP", // 0x0c
"Reserved", // 0x0d
"Mobile Intel Pentium 4M", // 0x0e
"Mobile Intel Celeron", // 0x0f
};
VOID ExecuteCpuidFunction( IN ULONG Function, OUT PULONG Results );
BOOLEAN IsCpuidPresent( VOID );
PUCHAR AMD_Associativity( ULONG Descriptor ) { switch (Descriptor) { case 0x0: return"L2 Off"; case 0x1: return"Direct"; case 0x2: return" 2 way"; case 0x4: return" 4 way"; case 0x6: return" 8 way"; case 0x8: return"16 way"; case 0xff: return" Full"; default: return"Reserved"; } } VOID AMD_DI_TLB( ULONG Format, ULONG TLBDesc ) { UCHAR Which = 'D'; ULONG AssocIndex; ULONG Entries;
if ((TLBDesc >> 16) == 0) {
//
// Unified.
//
TLBDesc <<= 16; Which = ' '; } do { if (Format == 1) { AssocIndex = TLBDesc >> 24; Entries = (TLBDesc >> 16) & 0xff; } else { AssocIndex = TLBDesc >> 28; Entries = (TLBDesc >> 16) & 0xfff; } printf(" %8s %4d entry %cTLB", AMD_Associativity(AssocIndex), Entries, Which );
//
// Repeat for lower half of descriptor.
//
TLBDesc <<= 16; Which = 'I'; } while (TLBDesc); printf("\n"); } VOID AMD_Cache( ULONG Format, ULONG CacheDesc ) { ULONG Size; ULONG AssocIndex; ULONG LinesPerTag; ULONG LineSize;
if (Format == 1) { Size = CacheDesc >> 24; AssocIndex = (CacheDesc >> 16) & 0xff; LinesPerTag = (CacheDesc >> 8) & 0xff; LineSize = CacheDesc & 0xff; } else { Size = CacheDesc >> 16; AssocIndex = (CacheDesc >> 12) & 0xf; LinesPerTag = (CacheDesc >> 8) & 0xf; LineSize = CacheDesc & 0xff; } printf(" %8s %5dKB (%d L/T)%3d bytes per line.\n", AMD_Associativity(AssocIndex), Size, LinesPerTag, LineSize ); }
#endif
#if defined(_IA64_)
ULONGLONG ia64CPUID( ULONGLONG Index );
PUCHAR IA64FeatureBitDescription[] = { /* 0 */ "BRL Long Branch instructions", /* 1 */ "Reserved-01", /* 2 */ "Reserved-02", /* 3 */ "Reserved-03", /* 4 */ "Reserved-04", /* 5 */ "Reserved-05", /* 6 */ "Reserved-06", /* 7 */ "Reserved-07", /* 8 */ "Reserved-08", /* 9 */ "Reserved-09", /* 10 */ "Reserved-10", /* 11 */ "Reserved-11", /* 12 */ "Reserved-12", /* 13 */ "Reserved-13", /* 14 */ "Reserved-14", /* 15 */ "Reserved-15", /* 16 */ "Reserved-16", /* 17 */ "Reserved-17", /* 18 */ "Reserved-18", /* 19 */ "Reserved-19", /* 20 */ "Reserved-20", /* 21 */ "Reserved-21", /* 22 */ "Reserved-22", /* 23 */ "Reserved-23", /* 24 */ "Reserved-24", /* 25 */ "Reserved-25", /* 26 */ "Reserved-26", /* 27 */ "Reserved-27", /* 28 */ "Reserved-28", /* 29 */ "Reserved-29", /* 30 */ "Reserved-30", /* 31 */ "Reserved-31", /* 32 */ "Reserved-32", /* 33 */ "Reserved-33", /* 34 */ "Reserved-34", /* 35 */ "Reserved-35", /* 36 */ "Reserved-36", /* 37 */ "Reserved-37", /* 38 */ "Reserved-38", /* 39 */ "Reserved-39", /* 40 */ "Reserved-40", /* 41 */ "Reserved-41", /* 42 */ "Reserved-42", /* 43 */ "Reserved-43", /* 44 */ "Reserved-44", /* 45 */ "Reserved-45", /* 46 */ "Reserved-46", /* 47 */ "Reserved-47", /* 48 */ "Reserved-48", /* 49 */ "Reserved-49", /* 50 */ "Reserved-50", /* 51 */ "Reserved-51", /* 52 */ "Reserved-52", /* 53 */ "Reserved-53", /* 54 */ "Reserved-54", /* 55 */ "Reserved-55", /* 56 */ "Reserved-56", /* 57 */ "Reserved-57", /* 58 */ "Reserved-58", /* 59 */ "Reserved-59", /* 60 */ "Reserved-60", /* 61 */ "Reserved-61", /* 62 */ "Reserved-62", /* 63 */ "Reserved-63" };
#endif
char *FeatureBitDescription32BitFormat = " %08x %s\n"; char *FeatureBitDescription64BitFormat = " %016I64x %s\n";
VOID PrintFeatures( ULONG_PTR Result, PUCHAR FeatureBitDescription[] ) { ULONG_PTR temp, temp2, bit; char *format = (sizeof(ULONG_PTR) == 8) ? FeatureBitDescription64BitFormat : FeatureBitDescription32BitFormat; //
// Feature bits.
//
temp = Result; if (temp) { printf(" Features\n"); for (bit = 0, temp2 = 1; temp; bit++, temp2 <<= 1) { if ((temp2 & temp) == 0) { //
// Feature bit not set.
//
continue; } temp ^= temp2; printf( format, temp2, FeatureBitDescription[bit]); } } return;
} // PrintFeatures()
__cdecl main( LONG Argc, PUCHAR *Argv ) { ULONG Processor; ULONG Function; ULONG MaxFunction; ULONG Temp; ULONG Temp2, Bit; HANDLE ProcessHandle; HANDLE ThreadHandle;
#if defined(_X86_)
ULONG Results[5]; ULONG Family = 0; ULONG Model = 0; ULONG Stepping = 0; ULONG Generation = 0; BOOLEAN CpuidPresent; CPU_VENDORS Vendor = CPU_NONE; ULONG ThreadAffinity; ULONG SystemAffinity; ULONG ProcessAffinity;
#endif
#if defined(_IA64_)
ULONGLONG Result; ULONGLONG ThreadAffinity; ULONGLONG SystemAffinity; ULONGLONG ProcessAffinity; ULONGLONG VendorInformation[3];
#endif
//
// Make sure this process is set to run on any processor in
// the system.
//
ProcessHandle = GetCurrentProcess(); ThreadHandle = GetCurrentThread();
if (!GetProcessAffinityMask(ProcessHandle, &ProcessAffinity, &SystemAffinity)) { printf("Fatal error: Unable to determine process affinity.\n"); exit(1); }
if (ProcessAffinity != SystemAffinity) {
if (!SetProcessAffinityMask(ProcessHandle, SystemAffinity)) { printf("Warning: Unable to run on all processors\n"); printf(" System Affinity %08x\n", SystemAffinity); printf(" - Process Affinity %08x\n", ProcessAffinity); printf(" Will Try %08x\n", SystemAffinity & ProcessAffinity); SystemAffinity &= ProcessAffinity; } ProcessAffinity = SystemAffinity; } #if defined(_X86_)
//
// Cpuid returns 4 DWORDs of data. In some cases this is string
// data in which case it needs to be NULL terminated.
//
Results[4] = 0;
#endif
//
// For each CPU in the system, determine the availability of
// the CPUID instruction and dump out anything useful it might
// have to say.
//
for (ThreadAffinity = 1, Processor = 0; ThreadAffinity; ThreadAffinity <<= 1, Processor++) { if (!(ThreadAffinity & ProcessAffinity)) {
//
// Can't test this processor.
//
if (ThreadAffinity > ProcessAffinity) {
//
// Tested all the processors there are, we're done.
//
break; }
continue; }
//
// Set affinity so this thread can only run on the processor
// being tested.
//
if (!SetThreadAffinityMask(ThreadHandle, ThreadAffinity)) {
printf( "** Could not set affinity %08x for processor %d, skipping.\n", ThreadAffinity, Processor); continue; }
#if defined(_X86_)
CpuidPresent = IsCpuidPresent(); if (CpuidPresent) { printf("++ Processor %d\n", Processor); } else { printf("-- No CPUID support, processor %d\n", Processor); continue; }
//
// CPUID is present, examine basic functions.
//
ExecuteCpuidFunction(0, Results);
MaxFunction = Results[0];
//
// For reasons unclear to anyone, the Vendor ID string comes
// back in the order EBX, EDX, ECX,... so switch the last two
// results before printing it.
//
Temp = Results[3]; Results[3] = Results[2]; Results[2] = Temp;
if (strcmp((PVOID)&Results[1], "GenuineIntel") == 0) { Vendor = CPU_INTEL; } else if (strcmp((PVOID)&Results[1], "AuthenticAMD") == 0) { Vendor = CPU_AMD; } else if (strcmp((PVOID)&Results[1], "CyrixInstead") == 0) { Vendor = CPU_CYRIX; } else { Vendor = CPU_UNKNOWN; }
printf(" Vendor ID '%s', Maximum Supported Function %d.\n", (PUCHAR)(&Results[1]), MaxFunction);
for (Function = 0; Function <= MaxFunction; Function++) { ExecuteCpuidFunction(Function, Results); printf(" F %d raw = %08x %08x %08x %08x\n", Function, Results[0], Results[1], Results[2], Results[3]); //
// Do some interpretation on the ones we know how to
// deal with.
//
switch(Function) { case 0:
//
// Already handled as the main header (gave max func
// and Vendor ID.
//
break;
case 1:
//
// EAX = Type, Family, Model, Stepping.
// EBX = Family != 0xf ?
// Yes = Reserved,
// No = 0xAABBCCDD where
// AA = APIC ID
// BB = LP per PP
// CC = CLFLUSH line size (8 = 64 bytes)
// DD = Brand Index
// ECX = Reserved
// EDX = Feature Bits
//
//
// Family Model Stepping
//
Temp = Results[0]; Family = (Temp >> 8) & 0xf; Model = (Temp >> 4) & 0xf; Stepping = Temp & 0xf; printf(" Type = %d, Family = %d, Model = %d, Stepping = %d\n", (Temp >> 12) & 0x3, Family, Model, Stepping);
//
// Willamette stuff
//
if ((Temp & 0xf00) == 0xf00) { Temp = Results[1] & 0xff; if (Temp) { //
// Indexes are a DISGUSTING way to get this info!!
//
printf(" Brand Index %02Xh %s processor\n", Temp, Temp <= (sizeof(BrandIndex) / sizeof(PUCHAR)) ? BrandIndex[Temp-1] : "Unknown"); } Temp = (Results[1] >> 8) & 0xff; printf(" CLFLUSH line size (%x) = %d bytes\n", Temp, Temp << 3); // ?? plj - nobasis
Temp = Results[1] >> 16; printf(" LP per PP %d\n", Temp & 0xff); printf(" APIC Id %02x\n", Temp >> 8); }
//
// Feature bits.
//
PrintFeatures( Results[3], X86FeatureBitDescription ); break;
case 2:
//
// Get number of times we have to do function 2 again.
// (Then replace iteration count with a NULL descr).
//
Temp = Results[0] & 0xff;
if (Temp == 0) {
//
// If the count is 0, this processor doesn't do
// function 2, get out.
//
break; } Results[0] &= 0xffffff00;
do { ULONG i;
for (i = 0; i < 4; i++) {
Temp2 = Results[i];
if (Temp2 & 0x80000000) {
//
// Not valid, skip.
//
continue; }
while (Temp2) {
UCHAR Descriptor = (UCHAR)(Temp2 & 0xff); ULONG K, Way, Line, Level; PUCHAR IorD = "";
Temp2 >>= 8;
if (((Descriptor > 0x40) && (Descriptor <= 0x47)) || ((Descriptor > 0x78) && (Descriptor <= 0x7c)) || ((Descriptor > 0x80) && (Descriptor <= 0x87))) {
//
// It's an L2 Descriptor. (The following
// is peterj's wacky formula,... not
// guaranteed forever but the nice people
// at Intel better pray I'm dead before
// they break it or I'll hunt them down).
//
Level = 2; Way = Descriptor >= 0x79 ? 8 : 4; K = 0x40 << (Descriptor & 0x7); Line = 32; if ((Descriptor & 0xf8) == 0x78) { Line = 128; } } else if ((Descriptor >= 0x50) && (Descriptor <= 0x5d)) { if (Descriptor & 0x8) { IorD = "D"; K = 0x40 << (Descriptor - 0x5b); } else { IorD = "I"; K = 0x40 << (Descriptor - 0x50); } printf(" %02xH %sTLB %d entry\n", Descriptor, IorD, K); continue; } else { PUCHAR s = NULL; switch (Descriptor) { case 0x00: continue; case 0x01: s = "ITLB 4KB pages, 4 way, 32 entry"; break; case 0x02: s = "ITLB 4MB pages, fully assoc, 2 entry"; break; case 0x03: s = "DTLB 4KB pages, 4 way, 64 entry"; break; case 0x04: s = "DTLB 4MB pages, 4 way, 8 entry"; break; case 0x06: s = "I-Cache 8KB, 4 way, 32B line"; break; case 0x08: s = "I-Cache 16KB, 4 way, 32B line"; break; case 0x0a: s = "D-Cache 8KB, 2 way, 32B line"; break; case 0x0c: s = "D-Cache 16KB, 2 or 4 way, 32B line"; break; case 0x22: K = 512; Level = 3; Way = 4; Line = 128; break; case 0x23: K = 1024; Level = 3; Way = 8; Line = 128; break; case 0x25: K = 2048; Level = 3; Way = 8; Line = 128; break; case 0x29: K = 4096; Level = 3; Way = 8; Line = 128; break; case 0x40: s = "No L3 Cache"; break; case 0x66: K = 8; Level = 1; Way = 4; Line = 64; IorD = "D"; break; case 0x67: K = 16; Level = 1; Way = 4; Line = 64; IorD = "D"; break; case 0x68: K = 32; Level = 1; Way = 4; Line = 64; IorD = "D"; break; case 0x70: K = 12; Level = 1; Way = 8; Line = 64; IorD = "I"; break; case 0x71: K = 16; Level = 1; Way = 8; Line = 64; IorD = "I"; break; case 0x72: K = 32; Level = 1; Way = 8; Line = 64; IorD = "I"; break; case 0x80: s = "No L2 Cache"; break; default: s = "Unknown Descriptor"; } if (s) { printf(" %02xH %s.\n", Descriptor, s); continue; } } printf(" %02xH L%d %sCache %dKB, %d way, %dB line\n", Descriptor, Level, IorD, K, Way, Line); } // while more bytes in this register
} // for each register
//
// If more iterations,...
//
if (--Temp == 0) { break; }
ExecuteCpuidFunction(2, Results); printf(" F %d raw = %08x %08x %08x %08x\n", 2, Results[0], Results[1], Results[2], Results[3]); } while (TRUE); break; } }
//
// Examine extended functions.
//
ExecuteCpuidFunction(0x80000000, Results);
MaxFunction = Results[0];
//
// Ok, function numbers > MaxFunction (the basic one) by
// definition return undefined results. But, we are told
// that if extended functions are not supported, the return
// value for 0x80000000 will never have the top bit set.
//
if ((MaxFunction & 0x80000000) == 0) { printf(" This processor does not support Extended CPUID functions.\n"); continue; }
printf(" Maximum Supported Extended Function 0x%x.\n", MaxFunction);
for (Function = 0x80000000; Function <= MaxFunction; Function++) { ExecuteCpuidFunction(Function, Results); printf(" F 0x%08x raw = %08x %08x %08x %08x\n", Function, Results[0], Results[1], Results[2], Results[3]); switch (Function) { case 0x80000000: break;
case 0x80000001:
if (Vendor == CPU_AMD) { //
// EAX = Generation, Model, Stepping.
// EBX = Reserved
// ECX = Reserved
// EDX = Feature Bits
//
//
// Generation Model Stepping
//
Temp = Results[0]; Generation = (Temp >> 8) & 0xf; Model = (Temp >> 4) & 0xf; Stepping = Temp & 0xf; printf(" Generation = %d, Model = %d, Stepping = %d\n", Generation, Model, Stepping);
//
// Feature bits.
//
PrintFeatures( Results[3], AMDExtendedFeatureBitDescription ); } break;
case 0x80000002:
Temp2 = 1;
case 0x80000003:
Temp2++;
case 0x80000004:
Temp2++;
printf(" Processor Name[%2d-%2d] = '%s'\n", 49 - (Temp2 * 16), 64 - (Temp2 * 16), Results); Temp2 = 0; break;
case 0x80000005:
if (Vendor == CPU_AMD) {
if (Family == 6) {
//
// Athlon.
//
printf(" Large Page TLBs :"); AMD_DI_TLB(1, Results[0]);
} else if (Family > 6) { printf(" Family %d is a new AMD family which this program doesn't know about.\n"); break; }
//
// Common to K5, K6 and Athlon
//
printf(" 4KB Page TLBs :"); AMD_DI_TLB(1, Results[1]); printf(" L1 D-Cache :"); AMD_Cache(1, Results[2]); printf(" L1 I-Cache :"); AMD_Cache(1, Results[3]); } break;
case 0x80000006:
if (Vendor == CPU_AMD) {
if (Family == 6) {
//
// Athlon.
//
if (Results[0]) { printf(" Large Page L2 TLB :"); AMD_DI_TLB(2, Results[0]); } if (Results[1]) { printf(" 4KB Page L2 TLB :"); AMD_DI_TLB(2, Results[1]); } if ((Model == 3) && (Stepping == 0)) { Results[2] &= 0xffff; Results[2] |= 0x400000; } } else if (Family > 6) { break; }
//
// Common to K5, K6 and Athlon
//
printf(" L2 Cache :"); AMD_Cache(2, Results[2]); } break; } }
#endif
#if defined(_IA64_)
printf("++ Processor %d\n", Processor);
//
// On IA64, cpuid is implemented as a set of 64 bit registers.
// Registers
// 0 and 1 contain the Vendor Information.
// 2 contains 0.
// 3 most significant 24 bits are reserved, the low 5 bytes
// contain-
// 39-32 archrev
// 31-24 family
// 23-16 model
// 15-08 revision
// 07-00 number index of largest implemented register
// 4 features
//
//
// Until we have read register 3, set 3 as the maximum number.
//
MaxFunction = 3;
for (Function = 0; Function <= MaxFunction; Function++) {
Result = ia64CPUID(Function);
printf(" F %d raw = %016I64x\n", Function, Result);
//
// Do some interpretation on the ones we know how to
// deal with.
//
switch(Function) { case 0: VendorInformation[0] = Result; break; case 1: VendorInformation[1] = Result; VendorInformation[2] = 0; printf(" \"%s\"\n", (PUCHAR)VendorInformation); break; case 3: printf(" Architecture Revision = %d, Family = %d, Model = %d, Revision = %d\n", (Result >> 32) & 0xff, (Result >> 24) & 0xff, (Result >> 16) & 0xff, (Result >> 8) & 0xff); MaxFunction = (ULONG)Result & 0xff; printf(" Maximum Supported Function %d.\n", MaxFunction); break; case 4: PrintFeatures( Result, IA64FeatureBitDescription ); break; } } #endif
} return 0; }
|