|
|
/*++
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>
#if defined(_X86_)
typedef enum { CPU_NONE, CPU_INTEL, CPU_AMD, CPU_CYRIX, CPU_UNKNOWN } CPU_VENDORS;
PUCHAR FeatureBitDescription[] = { /* 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", };
PUCHAR BrandIndex[] = { "Intel Celeron", "Intel Pentium III", "Intel Pentium III XEON", "Reserved for future" "Reserved for future" "Reserved for future" "Reserved for future" "Reserved for future" "Intel Pentium 4" };
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 );
#endif
__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 %02x %s processor\n", Temp, Temp < (sizeof(BrandIndex) / sizeof(PUCHAR)) ? BrandIndex[Temp-1] : BrandIndex[(sizeof(BrandIndex) / sizeof(PUCHAR)) -1]); } 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.
//
Temp = Results[3]; 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(" %08x %s\n", Temp2, FeatureBitDescription[Bit]); } } 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.
//
Temp = Results[3]; 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(" %08x %s\n", Temp2, AMDExtendedFeatureBitDescription[Bit]); } } } 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; } } #endif
} return 0; }
|