/* * cpuid.c * * This program tries to determine CPU type and other CPU data * from user-level. It displays information similar to linux' * /proc/cpuinfo for a single processor along with some explanation * as found in Intel and/or AMD manuals. * * Changes: * 05/Jan/00 V1.2 Updated Tables according to current Intel Instruction Set * Reference. Fixed typos. Most changes made by Jean-Pierre * Panziera - Thanks Jean-Pierre! * 15/Sep/98 V1.1 Added AMD Support and cpu_identifier_map - gjh. * 14/Sep/98 V1.0 Initial version - Intel Processors only - gjh. * * Copyright by Gerald J. Heim and the * University of Tübingen, Germany. All rights reserved. * This software is provided under the terms of the GNU General Public License. * It is provided as it is. No guarantee on functionality or absence of bugs * is given. Please send bug reports back to the author. * * ( Only for the EXE program, we Compile with: gcc -Wall -O2 -o cpuid tstcpuid.c ) /-------------------------------------------------------------------/ /-------------------------------------------------------------------/ Modification to make DLL for use with InstallAware with Call DLL Plug-In: February, 2006 Jon D Richards jon_d_r@msn.com NOTE: Any problems in the code as presented here are probably due to the modifications, not the original author(s). InstallAware ALTERNATIVE INSTALLATION with Call DLL Example Problem: We want to provide alternative installations depending on the CPU capabilities of the target computer. Our compiler can optimize code at several different levels, depending on the target CPU. It does not produce code that is backward compatible to lower level CPUs. There is a significant difference in runtimes for the codes, which can run for hours or days. We want to provide the user with the best code that her machine can run. For the Installation of our program MyProgram, we want to distinguish among these CPU targets: MyProgram Compiler Level Options Explanation ------- -------- ------------------------------------------------- 1 -tp Code optimized for Intel Pentium or Pentium MMX processors, or their generic counterparts. 2 -tpp Code optimized for Intel Pentium Pro, Pentium II, Pentium III, or their generic counterparts. 3 -tp4 Code optimized for Intel Pentium 4 processors. 4 -sse2 Code optimized using SSE2 instructions (in addition to Pentium 4) We will set these values in the C code to a MyProgram_level variable and return these values as long int from the DLL to the "Call DLL" Plug-In for InstallAware. These values are are returned to the script variable MYPROGRAMLEVEL. We can then make alternate executable installations, tuned to the most optimized code on the target machine. Our development environment for the DLL is a Windows XP computer with MinGW - Minimalist GNU for Windows installed. http://www.mingw.org/ To make a DLL named "tst.dll", Compile with: gcc -c -DBUILD_DLL tstcpuid.c gcc -shared -o tst.dll -Wl,--out-implib,libtstdll.a tstcpuid.o ^-- This makes the DLL. The DLL runs on Windows computers without MinGW or cygwin installed. NOTE the way the callable function is declared in the code below, in order to allow it to be callable. __declspec(dllexport) int tstcpuid(void) /-------------------------------------------------------------------/ Inside the InstallAware Script: Within the Main Install, where we want to do the First Time or Maintenance Installation: NOTE: Remove the MessageBox's in the final, tested product. ... // the script above this part is the same as before [DEFINE REGION: Perform First Time or Maintenance Installation] Set Variable MYPROGRAMLEVEL to 0 Call DLL Function $SUPPORTDIR$\tst.dll->tstcpuid (get result into variable MYPROGRAMLEVEL) // Install/Re-Install product Create Shortcut $SHORTCUTFOLDER$\Uninstall MyProgram to $UNINSTALLLINK$ [OFFLINE CONTENT] if Variable MYPROGRAMLEVEL Equals 0 MessageBox: MYPROGRAM LEVEL 0, MyProgram Level is $MYPROGRAMLEVEL$ ... MyProgram probably can't run on this computer because the CPU is not seen as a Pentium or above. We will install the most generic version of MyProgram and hope for the best. Install Files C:\MyProgram95\install\bin1\MyProgram.exe to $TARGETDIR$\bin else if Variable MYPROGRAMLEVEL Equals 1 MessageBox: MYPROGRAM LEVEL 1, MyProgram Level is $MYPROGRAMLEVEL$ ... CPU seen as Pentium (or equivalent). Install Files C:\MyProgram95\install\bin1\MyProgram.exe to $TARGETDIR$\bin else if Variable MYPROGRAMLEVEL Equals 2 MessageBox: MYPROGRAM LEVEL 2, MyProgram Level is $MYPROGRAMLEVEL$ ... CPU appears to be a Pentium (or equivalent) with support for MMX instructions Install Files C:\MyProgram95\install\bin2\MyProgram.exe to $TARGETDIR$\bin else if Variable MYPROGRAMLEVEL Equals 3 MessageBox: MYPROGRAM LEVEL 3, MyProgram Level is $MYPROGRAMLEVEL$ ... CPU appears to be at least a Pentium 4 (or equivalent) Install Files C:\MyProgram95\install\bin3\MyProgram.exe to $TARGETDIR$\bin else if Variable MYPROGRAMLEVEL Equals 4 MessageBox: MYPROGRAM LEVEL 4, MyProgram Level is $MYPROGRAMLEVEL$ ... CPU appears to be at least Pentium 4 (or equivalent) and support SSE2 instructions. Install Files C:\MyProgram95\install\bin\MyProgram.exe to $TARGETDIR$\bin else MessageBox: MYPROGRAM LEVEL ..., MyProgram Level is $MYPROGRAMLEVEL$ ... The CPU is either a 386 or 486 equivalent ... OR ... there was an Error trying to find the CPU type. MyProgram may not run on this computer. We will install the most generic MyProgram in any case and hope for the best. Install Files C:\MyProgram95\install\bin1\MyProgram.exe to $TARGETDIR$\bin end end end end end Create Shortcut $SHORTCUTFOLDER$\MyProgram to $TARGETDIR$\bin\MyProgram.exe Install Files C:\MyProgram95\install\bin\wintax.dll to $TARGETDIR$\bin Install Files C:\MyProgram95\install\docs\MYPROGRAM.pdf to $TARGETDIR$\docs Create Shortcut $SHORTCUTFOLDER$\MyProgram User Manual to $TARGETDIR$\docs\MYPROGRAM.pdf Install Files C:\MyProgram95\install\help\MyProgram.chm to $TARGETDIR$\help Create Shortcut $SHORTCUTFOLDER$\MyProgram Help to $TARGETDIR$\help\MyProgram.chm // and so on for the rest of script which is the same ... NOTE: In case we *did* do something wrong here, document this and include it in the README of the installation. We might also want to provide for setup command line options to override the level installed, in the case the user wants to try another optimization level of the program on their computer. This might be useful for non-Intel targets where we don't know about what might work best. /-------------------------------------------------------------------/ */ #include #include #include /* * asm-CPUID result structure. */ struct cpuid_type { unsigned int eax; unsigned int ebx; unsigned int ecx; unsigned int edx; }; typedef struct cpuid_type cpuid_t; /* * Table of available Processor descriptions. * (Source: Intel/AMD online manuals.) */ struct cpu_ident { long int MyProgram_level_a; unsigned int family; unsigned int model; char manufacturerid[12]; /* char expln[64]; */ /* comment out expln for our DLL */ }; typedef struct cpu_ident cpu_identifier; static cpu_identifier cpu_identifier_map[] = { /* MyProgram level, Family, Model, Manufacturer, (Explanation) */ { 0, 4, 0, "GenuineIntel" }, /* " Intel 486 DX" }, */ { 0, 4, 1, "GenuineIntel" }, /* " Intel 486 DX" }, */ { 0, 4, 2, "GenuineIntel" }, /* " Intel 486 SX" }, */ { 0, 4, 3, "GenuineIntel" }, /* " Intel 487, 486 DX2 or DX2 OverDrive" }, */ { 0, 4, 4, "GenuineIntel" }, /* " Intel 486 SL" }, */ { 0, 4, 5, "GenuineIntel" }, /* " Intel 486 SX2" }, */ { 0, 4, 7, "GenuineIntel" }, /* " Intel 486 DX2 w/ writeback" }, */ { 0, 4, 8, "GenuineIntel" }, /* " Intel 486 DX4 or DX4 OverDrive" }, */ { 1, 5, 1, "GenuineIntel" }, /* " Intel Pentium 60/66" }, */ /* first level for MyProgram */ { 1, 5, 2, "GenuineIntel" }, /* " Intel Pentium 75/90/100/120/133/150/166/200" }, */ { 1, 5, 3, "GenuineIntel" }, /* " Intel Pentium OverDrive for 486 Systems" }, */ { 1, 5, 4, "GenuineIntel" }, /* " Intel Pentium w/ MMX 166/200/233" }, */ { 2, 6, 1, "GenuineIntel" }, /* " Intel Pentium Pro" }, */ /* second level for MyProgram */ { 2, 6, 3, "GenuineIntel" }, /* " Intel Pentium II, model 3" }, */ { 2, 6, 5, "GenuineIntel" }, /* " Intel Pentium II, model 5" }, */ { 2, 6, 6, "GenuineIntel" }, /* " Intel Celeron model 6" }, */ { 2, 6, 7, "GenuineIntel" }, /* " Intel Pentium III, model 7" }, */ { 2, 6, 8, "GenuineIntel" }, /* " Intel Pentium III, model 8" }, */ { 2, 6, 9, "GenuineIntel" }, /* " Intel Pentium M, model 9" }, */ { 2, 6, 10, "GenuineIntel" }, /* " Intel Pentium III Xeon, model A" }, */ { 2, 6, 11, "GenuineIntel" }, /* " Intel Pentium III Xeon, model B" }, */ { 2, 6, 13, "GenuineIntel" }, /* " Intel Pentium M, Model D" }, */ { 3, 15, 0, "GenuineIntel" }, /* " Intel Pentium 4 or Intel Xeon" }, */ /* third level for MyProgram (level 4 if SSE2 */ { 3, 15, 1, "GenuineIntel" }, /* " Intel Pentium 4 or Intel Xeon, model 1" }, */ { 3, 15, 2, "GenuineIntel" }, /* " Intel Pentium 4 or Intel Xeon, model 2" }, */ { 3, 15, 3, "GenuineIntel" }, /* " Intel Pentium 4 or Intel Xeon, model 3" }, */ { 3, 15, 4, "GenuineIntel" }, /* " Intel Pentium 4 or Intel Xeon, model 4" }, */ { 0, 4, 0, "AuthenticAMD" }, /* " AM486 and AM5x86" }, */ /* Level 0 for LF95 */ { 1, 5, 0, "AuthenticAMD" }, /* " AMD-K5-PR75/90/100" }, */ /* all 1st level (Pentium) */ { 1, 5, 1, "AuthenticAMD" }, /* " AMD-K5-PR120/133" }, */ { 1, 5, 2, "AuthenticAMD" }, /* " AMD-K5-PR150/166" }, */ { 1, 5, 3, "AuthenticAMD" }, /* " AMD-K5-PR200" }, */ { 1, 5, 6, "AuthenticAMD" }, /* " AMD-K6 Model 6 (2.9 / 3.2 V Types)" }, */ { 1, 5, 7, "AuthenticAMD" }, /* " AMD-K6 Model 7 (2.2 V Types)" }, */ { 1, 5, 8, "AuthenticAMD" }, /* " AMD-K6-2 3D Model 8" }, */ { 1, 5, 9, "AuthenticAMD" }, /* " AMD-K6-III Model 9" }, */ { 1, 6, 1, "AuthenticAMD" }, /* " AMD Athlon Model 1" }, */ { 1, 6, 2, "AuthenticAMD" }, /* " AMD Athlon Model 2" }, */ { 1, 6, 3, "AuthenticAMD" }, /* " AMD Duron Model 3, Mobile AMD Duron Model 4" }, */ { 1, 6, 4, "AuthenticAMD" }, /* " AMD Athlon Model 4" }, */ { 1, 6, 6, "AuthenticAMD" }, /* " AMD Athlon MP, XP, Mobile Athlon, Duron Model 6" }, */ { 1, 6, 7, "AuthenticAMD" }, /* " AMD Duron, Mobil Duron Model 7" }, */ { 1, 6, 8, "AuthenticAMD" }, /* " AMD Athlon XP, MP, Sempron Model 8" }, */ { 1, 6, 10, "AuthenticAMD" }, /* " Mobil AMD Athlon XP-M, Athlon XP Model 10" }, */ { 1, 1, 1, "UMC UMC UMC " }, /* " UMC processor" }, */ { 1, 1, 1, "CyrixInstead" }, /* " Cyrix processor" }, */ { 1, 1, 1, "NexGenDriven" }, /* " NexGen processor" }, */ { 1, 1, 1, "CentaurHauls" }, /* " Centaur processor" }, */ { 1, 1, 1, "RiseRiseRise" }, /* " Rise Technology processor" }, */ { 1, 1, 1, "SiS SiS SiS " }, /* " SiS processor" }, */ { 1, 1, 1, "GenuineTMx86" }, /* " Transmeta processor" }, */ { 1, 1, 1, "Geode by NSC" }, /* " National Semiconductor processor" }, */ { 0, 0, 0, "" } /*, ""} /* End marker, don't remove! */ }; /* * CPU features as returned by CPUID 1 * Intel (CPUID 1) and AMD (CPUID 0x80000001) maps differ slightly. */ struct cpu_ftr { long int MyProgram_level_b; unsigned int bit; /* char name[8]; */ /* comment this out for DLL */ /* char expln[64]; */ /* comment this out for DLL */ }; typedef struct cpu_ftr cpu_feature; /* This is according to Intel's manuals. */ static cpu_feature intel_cpu_feature_map[] = /* additional MyProgram level, feature bit, (name), (explanation) */ { { 0, 0 }, /* "FPU", "Floating-point unit on-Chip" }, */ { 0, 1 }, /* "VME", "Virtual Mode Extension" }, */ { 0, 2 }, /* "DE", "Debugging Extension" }, */ { 0, 3 }, /* "PSE", "Page Size Extension" }, */ { 0, 4 }, /* "TSC", "Time-Stamp Counter" }, */ { 0, 5 }, /* "MSR", "Model Specific Registers with RDMSR/WRMSR Support" }, */ { 0, 6 }, /* "PAE", "Physical Adress Extension" }, */ { 0, 7 }, /* "MCE", "Machine Check Exception" }, */ { 0, 8 }, /* "CXS", "CMPXCHG8 Instruction Supported" }, */ { 0, 9 }, /* "APIC", "On-chip APIC Hardware Supported" }, */ { 0, 11 }, /* "SEP", "Fast System Call" }, */ { 0, 12 }, /* "MTRR", "Memory Type Range Registers" }, */ { 0, 13 }, /* "PGE", "Page Global Enable" }, */ { 0, 14 }, /* "MCA", "Machine Check Architecture" }, */ { 0, 15 }, /* "CMOV", "Conditional Move Instruction Supported." }, */ { 0, 16 }, /* "FGPAT", "Page Attribute Table" }, */ { 0, 17 }, /* "PSE-36", "36-bit Page Size Extension" }, */ { 0, 18 }, /* "PSN", "Processor Serial Number (present & enabled)" }, */ { 0, 19 }, /* "CLFSH", "CLFSH Instruction supported" }, */ { 0, 20 }, /* "---", "Reserved" }, */ { 0, 21 }, /* "DS", "Debug Store" }, */ { 0, 22 }, /* "ACPI", "Thermal Monitor & S/W Controlled Clock Facilities supported" }, */ { 0, 23 }, /* "MMX", "MMX Extension" }, */ { 0, 24 }, /* "FXSR", "Fast floating point Save and Restore" }, */ { 0, 25 }, /* "SSE", "Streaming SIMD Extensions" }, */ { 1, 26 }, /* "SSE2", "-[B4]- Streaming SIMD Extensions 2" }, */ /* fourth level for LF95 */ { 0, 27 }, /* "SS", "Self-Snoop" }, */ { 0, 28 }, /* "HTT", "Hyper-Threading Technology" }, */ { 0, 29 }, /* "TM", "Thermal Monitor supported" }, */ { 0, 30 }, /* "---", "Reserved" }, */ { 0, 27 }, /* "PBE", "Pending Break Enable" }, */ { 0,255 } /* "", "" } */ /* End marker, don't remove! */ }; /* These are AMD specific features detected with CPUID 0x80000001. */ static cpu_feature amd_cpu_feature_map[] = /* additional MyProgram level, feature bit, (name), (explanation) */ { { 0, 0}, /* "FPU", "Floating-point unit on-Chip" }, */ { 0, 1}, /* "VME", "Virtual Mode Extension" }, */ { 0, 2}, /* "DE", "Debugging Extension" }, */ { 0, 3}, /* "PSE", "Page Size Extension" }, */ { 0, 4}, /* "TSC", "Time-Stamp Counter" }, */ { 0, 5}, /* "MSR", "Model Specific Registers with RDMSR/WRMSR Support" }, */ { 0, 6}, /* "PAE", "Physical Address Extension" }, */ { 0, 7}, /* "MCE", "Machine Check Exception" }, */ { 0, 8}, /* "CXS", "CMPXCHG8 Instruction Supported" }, */ { 0, 9}, /* "APIC", "On-chip APIC Hardware Supported" }, */ { 0, 10}, /* "---", "Reserved" }, */ { 0, 11}, /* "SCSR", "SysCallSysRet Instruction" }, */ { 0, 12}, /* "MTRR", "Memory Tape Range Registers" }, */ { 0, 13}, /* "PGE", "Page Global Extension" }, */ { 0, 14}, /* "MCA", "Machine Check Architecture" }, */ { 0, 15}, /* "CMOV", "Conditional Move Instruction Supported" }, */ { 0, 16}, /* "PAT", "Page Attribute Table" }, */ { 0, 17}, /* "PSE36", "36-bit Page Size Extension" }, */ { 0, 18}, /* "---", "Reserved" }, */ { 0, 19}, /* "---", "Reserved" }, */ { 0, 20}, /* "NX", "No-eXecute Page Protection" }, */ { 0, 21}, /* "---", "Reserved" }, */ { 0, 22}, /* "MmxExt", "AMD Extenstions to MMX" }, */ { 0, 23}, /* "MMX", "MMX Extension" }, */ { 0, 24}, /* "FXSR", "Fast floating point Save and Restore" }, */ { 0, 25}, /* "FFXSR", "FFXSR Instruction Optimization" }, */ { 0, 26}, /* "---", "Reserved" }, */ { 0, 27}, /* "RDTSCP", "RDTSCP Instruction" }, */ { 0, 28}, /* "---", "Reserved" }, */ { 0, 29}, /* "LM", "Long Mode" }, */ { 0, 30}, /* "3DNowEx", "3DNow AMD Extensions" }, */ { 0, 31}, /* "3DNow", "3DNow Instructions" }, */ { 0,255} /* "", "" } */ /* End marker, don't remove! */ }; /* * This table describes the possible cache and TLB configurations * as documented by Intel. For now AMD doesn't use this but gives * exact cache layout data on CPUID 0x8000000x. * * MAX_CACHE_FEATURES_ITERATIONS limits the possible cache information * to 80 bytes (of which 16 bytes are used in generic Pentii2). * With 80 possible caches we are on the safe side for one or two years. * * Strange enough no BHT, BTB or return stack data is given this way... */ #define MAX_CACHE_FEATURES_ITERATIONS 8 /* WE will not use the feature table for the DLL */ /* The following feature table is good for EXE program. */ /* Keep it around for reference and when we want to go back to EXE */ /* struct cache_ftr { unsigned char label; char expln[90]; }; typedef struct cache_ftr cache_feature; static cache_feature intel_cache_feature_map[] = { { 0x01 , "Instruction TLB: 4-KBPages, 4-way set associative, 32 entries" }, { 0x02 , "Instruction TLB: 4-MB Pages, fully associative, 2 entries" }, { 0x03 , "Data TLB: 4-KB Pages, 4-way set associative, 64 entries" }, { 0x04 , "Data TLB: 4-MB Pages, 4-way set associative, 8 entries" }, { 0x06 , "1st-level instruction cache: 8-KB, 4-way set associative, 32-byte line size" }, { 0x08 , "1st-level instruction cache: 16-KB, 4-way set associative, 32-byte line size" }, { 0x0a , "1st-level data cache: 8-KB, 2-way set associative, 32-byte line size" }, { 0x0c , "1st-level data cache: 16-KB, 4-way set associative, 32-byte line size" }, { 0x22 , "3rd-level cache: 512 KB, 4-way set associative, sectored cache, 64-byte line size" }, { 0x23 , "3rd-level cache: 1-MB, 8-way set associative, sectored cache, 64-byte line size" }, { 0x25 , "3rd-level cache: 2-MB, 8-way set associative, sectored cache, 64-byte line size" }, { 0x29 , "3rd-level cache: 4-MB, 8-way set associative, sectored cache, 64-byte line size" }, { 0x2C , "1st-level data cache: 32-KB, 8-way set associative, 64-byte line size" }, { 0x30 , "1st-level instruction cache: 32-KB, 8-way set associative, 64-byte line size" }, { 0x39 , "2nd-level cache: 128-KB, 4-way set associative, sectored cache, 64-byte line size" }, { 0x3B , "2nd-level cache: 128-KB, 2-way set associative, sectored cache, 64-byte line size" }, { 0x3C , "2nd-level cache: 256-KB, 4-way set associative, sectored cache, 64-byte line size" }, { 0x40 , "No 2nd-level cache or, if processor contains a valid 2nd-level cache, no 3rd-level cache" }, { 0x41 , "2nd-level cache: 128-KB, 4-way set associative, 32-byte line size" }, { 0x42 , "2nd-level cache: 256-KB, 4-way set associative, 32-byte line size" }, { 0x43 , "2nd-level cache: 512-KB, 4-way set associative, 32-byte line size" }, { 0x44 , "2nd-level cache: 1-MB, 4-way set associative, 32-byte line size" }, { 0x45 , "2nd-level cache: 2-MB, 4-way set associative, 32-byte line size" }, { 0x46 , "3rd-level cache: 4MB, 4-way set associative, 64-byte line size" }, { 0x47 , "3rd-level cache: 8MB, 8-way set associative, 64-byte line size" }, { 0x50 , "Instruction TLB: 4-KB, 2-MB or 4-MB pages, fully associative, 64 ent" }, { 0x51 , "Instruction TLB: 4-KB, 2-MB or 4-MB pages, fully associative, 128 entries" }, { 0x52 , "Instruction TLB: 4-KB, 2-MB or 4-MB pages, fully associative, 256 entries" }, { 0x5B , "Data TLB: 4-KB or 4-MB pages, fully associative, 64 entries" }, { 0x5C , "Data TLB: 4-KB or 4-MB pages, fully associative, 128 entries" }, { 0x5D , "Data TLB: 4-KB or 4-MB pages, fully associative, 256 entries" }, { 0x60 , "1st-level data cache: 16-KB, 8-way set associative, sectored cache, 64-byte line size" }, { 0x66 , "1st-level data cache: 8-KB, 4-way set associative, sectored cache, 64-byte line size" }, { 0x67 , "1st-level data cache: 16-KB, 4-way set associative, sectored cache, 64-byte line size" }, { 0x68 , "1st-level data cache: 32-KB, 4 way set associative, sectored cache, 64-byte line size" }, { 0x70 , "Trace cache: 12K-uops, 8-way set associative" }, { 0x71 , "Trace cache: 16K-uops, 8-way set associative" }, { 0x72 , "Trace cache: 32K-uops, 8-way set associative" }, { 0x78 , "2nd-level cache: 1-MB, 4-way set associative, 64-byte line size" }, { 0x79 , "2nd-level cache: 128-KB, 8-way set associative, sectored cache, 64-byte line size" }, { 0x7A , "2nd-level cache: 256-KB, 8-way set associative, sectored cache, 64-byte line size" }, { 0x7B , "2nd-level cache: 512-KB, 8-way set associative, sectored cache, 64-byte line size" }, { 0x7C , "2nd-level cache: 1-MB, 8-way set associative, sectored cache, 64-byte line size" }, { 0x7D , "2nd-level cache: 2-MB, 8-way set associative, 64-byte line size" }, { 0x7F , "2nd-level cache: 512-KB, 2-way set associative, 64-byte line size" }, { 0x82 , "2nd-level cache: 256-KB, 8-way set associative, 32-byte line size" }, { 0x83 , "2nd-level cache: 512-KB, 8-way set associative, 32-byte line size" }, { 0x84 , "2nd-level cache: 1-MB, 8-way set associative, 32-byte line size" }, { 0x85 , "2nd-level cache: 2-MB, 8-way set associative, 32-byte line size" }, { 0x86 , "2nd-level cache: 512-KB, 4-way set associative, 64-byte line size" }, { 0x87 , "2nd-level cache: 1-MB, 8-way set associative, 64-byte line size" }, { 0xB0 , "Instruction TLB: 4-KB Pages, 4-way set associative, 128 entries" }, { 0xB3 , "Data TLB: 4-KB Pages, 4-way set associative, 128 entries" }, { 0xF0 , "64-byte Prefetching" }, { 0xF1 , "128-byte Prefetching" }, { 0x00 , "NULL" } }; */ /* * cpuid calls the processor's internal CPUID instruction and returns the * 16-byte result. The type of CPUID to return is determined by the * argument number. No range check is performed on number as all * tested processors return 0s on illegal CPUID input values. * However a call to CPUID 0 gives the maximum reasonable value to * call CPUID with, so the caller may check this. * */ cpuid_t cpuid(unsigned int number) { cpuid_t result; /* structure to return */ /* Store number in %eax, call CPUID, save %eax, %ebx, %ecx, %edx in variables. As output constraint "m=" has been used as this keeps gcc's optimizer from overwriting %eax, %ebx, %ecx, %edx by accident */ asm("movl %4, %%eax; cpuid; movl %%eax, %0; movl %%ebx, %1; movl %%ecx, %2; movl %%edx, %3;" : "m=" (result.eax), "m=" (result.ebx), "m=" (result.ecx), "m=" (result.edx) /* output */ : "r" (number) /* input */ : "eax", "ebx", "ecx", "edx" /* no changed registers except output registers */ ); /* comment out the outputs for DLL */ /* printf("CPUID(0x%.8x) result: eax=0x%.8x ebx=0x%.8x ecx=0x%.8x edx=0x%.8x\n", */ /* number, result.eax, result.ebx, result.ecx, result.edx); */ return result; } /* * identify_amd gives AMD specific details for a processor _known_ to be an AMD. * This fuction is not called on processors not identified as AMD. * AMD Processor identification is similar to Intel but uses 0x8000000x as * CPUID argument. Cache and TLB identification is quite a bit different as on * Intel - seems more straightforward... */ int identify_amd(void) { cpuid_t cpuid_registers; /* results as returned from cpuid */ unsigned int largest_function_value; unsigned int amd_cpu_family, amd_cpu_model, amd_cpu_stepping; /* brief cpu description */ /* don't need processor description for DLL */ /* char processor_description_text[3*4*sizeof(unsigned long)]; */ int i, j; /* Level 2 is Pentium Pro now */ /* AMD -> at least some 486 thingy */ amd_cpu_family = 4; /* First determine "Largest Extended Function Value" */ cpuid_registers = cpuid(0x80000000); largest_function_value = cpuid_registers.eax; if(largest_function_value >= 0x80000001) { cpuid_registers = cpuid(0x80000001); amd_cpu_family = 0xf&(cpuid_registers.eax>>8); amd_cpu_model = 0xf&(cpuid_registers.eax>>4); amd_cpu_stepping = 0xf&cpuid_registers.eax; /* No stdout for DLL */ /* printf("CPUID (AMD - 1): CPU is a %u86, Model %u, Stepping %u\n", */ /* amd_cpu_family, amd_cpu_model, amd_cpu_stepping); */ /* printf(" AMD CPU features are:\n"); */ i = 0; while(cpuid_registers.edx > 0) { if(cpuid_registers.edx&0x1) { /* LSB set? */ /* Find description */ j = 0; while((amd_cpu_feature_map[j].bit!=i) && (amd_cpu_feature_map[j].bit!=255)) { j++; } if(amd_cpu_feature_map[j].bit!=255) { /* We found a description */ /* No stdout for DLL */ /* printf(" %10s - %s\n", amd_cpu_feature_map[j].name, amd_cpu_feature_map[j].expln ); */ } else { /* No description available */ /* No stdout for DLL */ /* printf(" Feature %.2d - No description available\n", i); */ } } i++; cpuid_registers.edx = cpuid_registers.edx >> 1; /* Shift 1 Bit */ } } /* Get textual AMD processor description */ /* First part of description text */ if(largest_function_value >= 0x80000002) { cpuid_registers = cpuid(0x80000002); /* strncpy(&processor_description_text[0], (char *) &cpuid_registers, 4*sizeof(unsigned long)); */ } /* 2nd part of description text */ if(largest_function_value >= 0x80000003) { cpuid_registers = cpuid(0x80000003); /* strncpy(&processor_description_text[4*sizeof(unsigned long)], (char *) &cpuid_registers, 4*sizeof(unsigned long)); */ } /* 3rd part of description text */ if(largest_function_value >= 0x80000004) { cpuid_registers = cpuid(0x80000004); /* strncpy(&processor_description_text[2*4*sizeof(unsigned long)], (char *) &cpuid_registers, 4*sizeof(unsigned long)); */ } /* No stdout for DLL */ /* printf("CPUID(AMD - x) Textual processor description is \"%s\"\n", processor_description_text); */ /* L1 Caches and TLB descriptions */ if(largest_function_value >= 0x80000005) { cpuid_registers = cpuid(0x80000005); /* No stdout for DLL */ /* printf("CPUID(AMD - 5) : Data TLB with %3d entries, %d-way set associative\n", (cpuid_registers.ebx>>16)&0xff, (cpuid_registers.ebx>>24)&0xff); printf("CPUID(AMD - 5) : Instruction TLB with %3d entries, %d-way set associative\n", cpuid_registers.ebx&0xff, (cpuid_registers.ebx>>8)&0xff); printf("CPUID(AMD - 5) : L1 Data Cache is %3dk (%d lines per tag, %d-byte lines, %d-way set associative\n", (cpuid_registers.ecx>>24)&0xff, (cpuid_registers.ecx>>8)&0xff, cpuid_registers.ecx&0xff, (cpuid_registers.ecx>>16)&0xff); printf("CPUID(AMD - 5) : L1 Instruction Cache is %3dk (%d lines per tag, %d-byte lines, %d-way set associative\n", (cpuid_registers.edx>>24)&0xff, (cpuid_registers.edx>>8)&0xff, cpuid_registers.edx&0xff, (cpuid_registers.edx>>16)&0xff); */ } /* L2 Cache description (K6-2 3D+ (and higher?)) - untested at this time! */ if(largest_function_value >= 0x80000006) { cpuid_registers = cpuid(0x80000006); /* No stdout for DLL */ /* printf("CPUID(AMD - 6) : WARNING! This has not been tested!\n"); printf("CPUID(AMD - 6) : L2 Unified Cache is %3dk (%d lines per tag, %d-byte lines, %d-way set associative\n", (cpuid_registers.ecx>>24)&0xff, (cpuid_registers.ecx>>8)&0xff, cpuid_registers.ecx&0xff, (cpuid_registers.ecx>>16)&0xff); */ } /* Level 2 is Pentium Pro now */ return amd_cpu_family; } /* our EXE program will detect the CPU type and return 3 on 386, 4 on 486, ... */ /* parts of this routine are copied from linux' head.S */ /* NOTE: the __declspec(dllexport) before the function allows the function tstcpuid to be stdcall callable from external programs, including the "Call DLL" Plug-In for InstallAware. */ /* int main(void) */ /* <-- Use this for EXE */ __declspec(dllexport) int tstcpuid(void) /* <-- Use this for DLL */ { unsigned long flags_before, flags_after; /* temp to save flags */ unsigned int max_cpuid_index, cpu_family, cpu_model, cpu_stepping; /* brief cpu description */ long int MyProgram_level; char manufacturerid[3*sizeof(unsigned int)]; /* CPUID returns 12 chars in 4 ints */ cpuid_t cpuid_registers; /* results as returned from cpuid */ int i,j; char cachesizes[MAX_CACHE_FEATURES_ITERATIONS*4*sizeof(unsigned int)]; MyProgram_level = 1; /* * Check for 386 */ /* Read Flags;; Save Flags; Flip AC in Flags; Write Flags;; Read Flags;; Save new Flags; Restore old Flags */ asm("pushfl; popl %%eax; movl %%eax, %0; xorl $0x40000, %%eax; pushl %%eax; popfl; pushfl; popl %%eax; movl %%eax, %1; pushl %0; popfl " : "r=" (flags_before), "r=" (flags_after) /* output */ : /* input */ : "eax" /* changed registers */ ); flags_before &= 0x40000; flags_after &= 0x40000; if(flags_before == flags_after) { /* We have found a 386 */ /* No stdout for DLL */ /* printf("386 CPU found.\n"); */ MyProgram_level = 386; /* MyProgram level 0, but report CPU */ return MyProgram_level; } /* * Check for 486 w/o working CPUID */ /* Read Flags;; Save Flags; Flip AC in Flags; Write Flags;; Read Flags;; Save new Flags; Restore old Flags */ asm("pushfl; popl %%eax; movl %%eax, %0; xorl $0x200000, %%eax; pushl %%eax; popfl; pushfl; popl %%eax; movl %%eax, %1; pushl %0; popfl " : "r=" (flags_before), "r=" (flags_after) /* output */ : /* input */ : "eax" /* changed registers */ ); flags_before &= 0x200000; flags_after &= 0x200000; if(flags_before == flags_after) { /* We have found an old 486 */ /* No stdout for DLL */ /* printf("486 CPU w/o working CPUID found.\n"); */ MyProgram_level = 486; /* MyProgram level 0, but report CPU */ return MyProgram_level; } /* * All processors > 486 and some newer 486 do have CPUID, so we will use that. */ /* First, detect maximum index for CPUID along with CPU manufacturer */ cpu_family = 4; /* At least it is a 486 */ cpu_model = 0; /* With CPUID 0 we get the maximum reasonable argument to CPUID and the 12-char string identifying the manufacturer */ cpuid_registers = cpuid(0); max_cpuid_index = cpuid_registers.eax; sprintf(manufacturerid, "%.4s%.4s%.4s", (char *) &cpuid_registers.ebx, (char *) &cpuid_registers.edx, (char *) &cpuid_registers.ecx); /* No stdout for DLL */ /* printf("CPUID (0): maximum CPUID index is %d, Manufacturer says \"%.12s\"\n", max_cpuid_index, manufacturerid); */ /* With CPUID 1 we get the exact chip revision (eax) and (not nearly) all the features that distinguish this chip from an 80386 (edx) */ if(max_cpuid_index >= 1) { cpuid_registers = cpuid(1); cpu_family = 0xf&(cpuid_registers.eax>>8); cpu_model = 0xf&(cpuid_registers.eax>>4); cpu_stepping = 0xf&cpuid_registers.eax; /* No stdout for DLL */ /* printf("CPUID (1): CPU is a %u86, Model %u, Stepping %u\n", cpu_family, cpu_model, cpu_stepping); */ /* Search for a match in cpu_identifier_map */ i = 0; while((cpu_identifier_map[i].family != 0) && ((cpu_identifier_map[i].family != cpu_family) || (cpu_identifier_map[i].model != cpu_model ) || strncasecmp(cpu_identifier_map[i].manufacturerid, manufacturerid, 12))) i++; if(cpu_identifier_map[i].family != 0) { /* No stdout for DLL */ /* printf(" CPU seems to be a %s.\n", cpu_identifier_map[i].expln ); */ MyProgram_level = cpu_identifier_map[i].MyProgram_level_a; } else { /* No stdout for DLL */ /* printf(" No description available for this CPU family/model/vendor."); */ } /* No stdout for DLL */ /* printf(" CPU features are:\n"); */ i = 0; while(cpuid_registers.edx > 0) { if(cpuid_registers.edx&0x1) { /* LSB set? */ /* Find description */ j = 0; while((intel_cpu_feature_map[j].bit != i) && (intel_cpu_feature_map[j].bit != 255)) { j++; } if(intel_cpu_feature_map[j].bit != 255) { /* We found a description */ /* No stdout for DLL */ /* printf(" %10s - %s\n", intel_cpu_feature_map[j].name, intel_cpu_feature_map[j].expln); */ if(intel_cpu_feature_map[j].MyProgram_level_b != 0) { /* For now, increment only for Pentium 4 or later */ if ( MyProgram_level == 3 ) { MyProgram_level = 4; /* MyProgram_level + intel_cpu_feature_map[j].MyProgram_level_b; */ } } } else { /* No description available */ /* No stdout for DLL */ /* printf(" Feature %.2d - No description available\n", i); */ } } i++; cpuid_registers.edx = cpuid_registers.edx >> 1; /* Shift 1 Bit */ } } /* * Special treatment on AMD processors as those K6 try to behave like * a Intel Pentium but might have somewhat more to reveal */ if((!strncasecmp("AuthenticAMD", manufacturerid, 12)) && (cpu_model > 0)) { MyProgram_level = identify_amd(); /* returns the AMD CPU Family */ /* Level 2 is Pentium Pro now */ if(MyProgram_level == 4) { /* AMD equiv to 486 */ MyProgram_level = 0; } if(MyProgram_level == 5) { /* AMD equiv to Pentium */ MyProgram_level = 1; } return MyProgram_level; } /* * For other CPU makers, we don't know anything, so assume MyProgram Level 1, * which is a generic Pentium (I) * Later, If we can find out more about these chips, e.g, SSE2 * instructions, then we can increase MyProgram Level. */ if(strncasecmp("UMC UMC UMC ", manufacturerid, 12) == 0) { /* Old Fortran coder ;^) */ MyProgram_level = 1; } if(strncasecmp("CyrixInstead", manufacturerid, 12) == 0) { MyProgram_level = 1; } if(strncasecmp("NexGenDriven", manufacturerid, 12) == 0) { MyProgram_level = 1; } if(strncasecmp("CentaurHauls", manufacturerid, 12) == 0) { MyProgram_level = 1; } if(strncasecmp("RiseRiseRise", manufacturerid, 12) == 0) { MyProgram_level = 1; } if(strncasecmp("SiS SiS SiS ", manufacturerid, 12) == 0) { MyProgram_level = 1; } if(strncasecmp("GenuineTMx86", manufacturerid, 12) == 0) { MyProgram_level = 1; } if(strncasecmp("Geode by NSC", manufacturerid, 12) == 0) { MyProgram_level = 1; } /* We don't need this section in our DLL but it is cool for the EXE */ // /* CPUID 2 returns chars describing the cache features. More than 16 cache // Features are possible by requiring to call CPUID 2 more than once! // As one has to look up those chars in a table, this feature seems to be // somewhat ugly but allows for future extensions (that are not found in the // lookup table anyways...) */ // if(max_cpuid_index >= 2) { // // /* First, clear result array */ // for(i=0; i < 4*4*MAX_CACHE_FEATURES_ITERATIONS; i++) cachesizes[i] = 0; // // /* Read CPUID 2 for the first time; al (in eax) delivers how often to read */ // cpuid_registers = cpuid(2); // memcpy(&cachesizes[ 0], &cpuid_registers, sizeof(cpuid_registers)); // cachesizes[0]--; /* Counter how often to call CPUID 2. Already called once. */ // i=1; // while(cachesizes[0] && ( i < MAX_CACHE_FEATURES_ITERATIONS ) ) { // /* Untested, as PentrumII only needs one call to CPUID 2 */ // /* printf("WARNING! This Program was not tested on CPUs > PentiumII!\n"); */ // cpuid_registers = cpuid(2); // memcpy(&cachesizes[i*16 + 0], &cpuid_registers, sizeof(cpuid_registers)); // i++; // cachesizes[0]--; // } // /* printf("CPUID (2): Cache and TLB Sizes are:\n"); */ // for(i=1; i < 4*4*MAX_CACHE_FEATURES_ITERATIONS; i++) { // /* ignore cachesizes[0] - is the counter */ // /* printf("0x%.2x ", cachesizes[i]); */ // if(cachesizes[i]) { /* 0x00 is ignored - NULL value */ // j = 0; // while((intel_cache_feature_map[j].label!=0x00) && // (intel_cache_feature_map[j].label!=cachesizes[i])) j++; // if(intel_cache_feature_map[j].label==cachesizes[i]) { // /* printf(" %s\n", intel_cache_feature_map[j].expln); */ // } else { // /* printf(" Cache Feature 0x%.2x - No Interpretation available.\n", */ // /* cachesizes[i]); */ // } // } // } // } else { // /* printf("CPUID (2): No Cache or TLB information available.\n"); */ // } /* EXE: Quit with an exit value describing x in a processor nomenclatura of 80x86 */ /* DLL: quit and return MyProgram level */ /* return cpu_family; */ /* this is return for EXE */ return MyProgram_level; /* this is return for DLL */ }