CPUID: Difference between revisions
|Line 62:||Line 62:|
[dumbledore](0) $ </pre>
[dumbledore](0) $ </pre>
Revision as of 00:25, 3 November 2009
Loading the cpuid module creates the nodes /dev/cpu/*/cpuid. Good commands for breaking down cpuid data include cpuid and Dave Jones's x86info. A "cpuid_level" field can be found in /proc/cpuinfo (checked as of 2.6.25-rc3) for each processor in the system. There's Intel and AMD documents about using cpuid effectively, although they're given short shrift at this page.
Availability of cpuid
It's ironic that Intel claims that "any other approach may produce unpredictable results," since its algorithm is prone to failures that yield unpredictable results (as I'll demonstrate in this article). For more information on CPUID, see the text box "Pentium Detection," by Robert Moote (which accompanied the article "Processor-Detection Schemes," by Richard C. Leinecker, DDJ, June 1993).
The Intel algorithm relies on a series of PUSHF/POPF instructions to set and clear various FLAGs bits. Each generation of processor has a slightly different behavior which may be detected by this approach. This algorithm makes no attempt to detect the 80186/88 series of processors. In this regard, the algorithm is incomplete.
The 8086/88 is distinguished from the 80286 by attempting to clear bits 12 - 15 of the FLAGs register, The 8086/88 will always set these bits, regardless of what values are popped into them (see Listing One). The 286 treats these bits differently. In real mode, these bits are always cleared by the 286; in protected mode, they are used for IOPL (I/O Privilege Level) and NT (Nested Task). To continue the detection code, you need to set bits 12 - 15 in the FLAGs register, and see if they are cleared by the processor. If they are, then a 286 has been detected (see Listing Two).
If you gethis point in the algorithm, you know you have at least a 386. Therefore, it is safe to use 32-bit instructions, like PUSHFD. This will be necessary in detecting the difference between a 386 and 486. These processors are distinguished from each othmpting to set the AC flag in the EFLAGs register. This flag was introduced in the 486, The 386 never sets this bit, and always clhen it is set by POPFD. Therefore, to detect the difference between these processor generations, the algorithm attempts to set thiee if it is latched or cleared by the processor (see Listing Three).
At this point in the algorithm, you're almost home. To detect the difference between the 486 and the Pentium, you attempt to set another new EFLAG bit (bit-21) called the "ID flag." This flag has only one purpose - to indicate the presence of the CPUID instruction. This bit was first introduced on the Pentium, but later retrofitted into the 486. If the CPUID instruction exists on either processor, it may be executed to return the processor-identification information. 486s without the CPUID instruction will not be able to toggle this bit. Therefore, it is safe to execute a sequence of instructions on either processor that detects the processor's ability to toggle this bit (see Listing Four).
Once the algorithm gets to this point, you can execute the CPUID instruction to obtain the processor identification. This instruction can be run in any processor mode, at any privilege level. On the Pentium and 486, the CPUID instruction has two levels:
- Level 0 returns a vendor ID string in EBX:EDX:ECX, which says "GenuineIntel" when printed as ASCII text.
- Level 1 returns the processor identification signature - the same signature that appears in the EDX register after a processor RESET (see Listing Five).
The complete Intel algorithm is available in AP-485, or via anonymous FTP at ftp://ftp.intel.com/pub/IAL/tools_utils_demos/cpuid3.zip.
CPUID and multiple processors
Some CPUID attributes require multiple CPUID calls to retrieve (often EAX is first set to 0 to determine the highest attribute request supported, then called again with specific requests). Behavior is undefined if CPUID information from one processor is used to form requests for another (especially in a heterogeneous system). Furthermore, CPUID must be performed on each processor used -- results for one processor have no general ramifications for others (again, especially in a heterogeneous system). Thus, either:
- the process using CPUID must be bound to a single CPU by the system administrator,
- the process must be able to bind itself to a CPU (see sched_setpolicy() on Linux and cpuset_setpolicy on FreeBSD), or
- the operating system must provide a world-readable interface (ie, the process never calls CPUID directly)
On Linux, the /dev/cpuid(4) device provides an obsolete interface to CPUID. On modern systems, it is chmod'd 0440, and its use is discouraged. Instead, since at least 2.6.24 a sysfs interface has been supported; see /sys/devices/system/cpu/*/:
[dumbledore](0) $ for i in /sys/devices/system/cpu/cpu0/cache/*/* /sys/devices/system/cpu/cpu0/topology/* ; do echo -n "`basename $i`: " && cat $i ; done coherency_line_size: 64 level: 1 number_of_sets: 64 physical_line_partition: 1 shared_cpu_map: 00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000101 size: 32K type: Data ways_of_associativity: 8 coherency_line_size: 64 level: 1 number_of_sets: 128 physical_line_partition: 1 shared_cpu_map: 00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000101 size: 32K type: Instruction ways_of_associativity: 4 coherency_line_size: 64 level: 2 number_of_sets: 512 physical_line_partition: 1 shared_cpu_map: 00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000101 size: 256K type: Unified ways_of_associativity: 8 coherency_line_size: 64 level: 3 number_of_sets: 8192 physical_line_partition: 1 shared_cpu_map: 00000000,00000000,00000000,00000000,00000000,00000000,00000000,00005555 size: 8192K type: Unified ways_of_associativity: 16 core_id: 0 core_siblings: 00000000,00000000,00000000,00000000,00000000,00000000,00000000,00005555 physical_package_id: 1 thread_siblings: 00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000101 [dumbledore](0) $
CPUID and multiple threads
- The package must support SMT. Check via bit 28 (0x1c00) of the EDX register after CPUID function 0x00000001
- The processor must support SMT. Check via bits 23:16 of the EBX register after CPUID function 0x00000001. They must be > 1.
- Number of threads sharing a given cache can be determined via CPUID function 0x00000004, checking EAX bits 25:14 for each cache.
- Number of threads for a given level of topology can be determined via CPUID function 0x0000000B, checking ECX bits 15:8 for each level.