CPUID
CPUID is the instruction that reports what a given CPU actually supports: which optional instruction sets it implements, how many address bits it exposes, and dozens of other details a kernel would otherwise have to guess at. Software loads a leaf number into EAX (and, for some leaves, a subleaf into ECX), executes CPUID, and reads the answer back out of EAX, EBX, ECX, and EDX, whose meaning depends entirely on which leaf was requested. Code paths throughout a kernel, from deciding whether long mode can even be attempted to reading the TSC’s invariance in the timer code, ultimately trace back to a CPUID check made once during boot.
Leaf 0 and the vendor string
Section titled “Leaf 0 and the vendor string”Leaf 0 (EAX = 0) is the one call every implementation is guaranteed to answer: it returns the highest leaf number the CPU supports in EAX, and a twelve-character vendor identification string packed into EBX, EDX, and ECX, in that order, four ASCII bytes per register. "GenuineIntel" and "AuthenticAMD" are the two strings a kernel is most likely to see on real hardware, though a hypervisor can present any string it wants here, which is part of why the vendor string alone is a weak signal for anything beyond a coarse first guess at CPU identity. The maximum-leaf value matters more in practice: a kernel that queries a leaf beyond what the CPU actually implements doesn’t fault, it silently gets back the result of the highest leaf that is implemented, so checking this value first is what keeps a later out-of-range query from being misread as a real answer.
Feature bits at leaf 1
Section titled “Feature bits at leaf 1”Leaf 1 returns the processor’s family, model, and stepping numbers in EAX, and, more usefully for a kernel deciding what it can safely enable, two 32-bit bitfields of feature flags in ECX and EDX. EDX carries the older, more fundamental set: bit 0 for an onboard FPU, bit 5 for the MSR/RDMSR/WRMSR instructions, bit 6 for PAE, bit 9 for a local APIC. ECX carries newer additions layered on afterward: bit 0 for SSE3, bit 13 for CMPXCHG16B, and bit 31, unusually, not a hardware feature at all but a flag a hypervisor sets to announce that the code is running inside a virtual machine rather than on physical hardware.
int cpu_has_feature(uint32_t leaf, uint32_t edx_bit) { uint32_t eax, ebx, ecx, edx; __asm__ volatile("cpuid" : "=a"(eax), "=b"(ebx), "=c"(ecx), "=d"(edx) : "a"(leaf)); return (edx >> edx_bit) & 1;}
// e.g. cpu_has_feature(1, 6) checks PAE before enabling long modeA handful of leaves need more than just EAX to disambiguate a request: leaf 7 packs its feature bits (AVX2, SMEP, SMAP among them) across multiple subleaves selected through ECX, and leaf 4, which describes the cache hierarchy, returns a different cache level’s data on each successive subleaf until the CPU signals there are no more by returning a cache type of zero.
Extended leaves and the brand string
Section titled “Extended leaves and the brand string”A separate range of leaves, starting at EAX = 0x80000000, exists alongside the standard range and has to be probed the same way: querying 0x80000000 itself returns the highest extended leaf supported, which on some older CPUs is simply 0x80000000 again, meaning no extended leaves exist at all beyond confirming their own absence. Where extended leaves are present, 0x80000001 carries a second set of feature bits distinct from leaf 1’s, most notably bit 29 of EDX (the LM bit), which is the specific flag a kernel checks before attempting the long-mode transition at all. Leaves 0x80000002 through 0x80000004 together spell out the CPU’s human-readable brand string (“Intel(R) Core(TM)…” or similar) as 48 bytes of ASCII split across three calls’ worth of EAX/EBX/ECX/EDX, purely descriptive text with no bearing on what the CPU can actually do.
Why software checks first
Section titled “Why software checks first”Executing an instruction the CPU doesn’t implement raises a #UD (invalid opcode) exception rather than doing something approximately right, and enabling a control-register bit tied to an absent feature is just as likely to fault or produce genuinely undefined behavior. Querying the relevant CPUID leaf first and branching on the result costs a handful of cycles and turns what would otherwise be a fault, often one occurring early enough in boot that there’s no working exception handler yet to report it cleanly, into an ordinary conditional. This matters even under emulation and virtualization, arguably more than on physical hardware: QEMU’s default CPU model and a minimally configured VM can both legitimately lack instruction sets a kernel developer’s own physical machine has, so code that never checks CPUID and only ever runs on the developer’s own machine can pass every test there and still fault the moment it runs somewhere else.
Implementation notes
Section titled “Implementation notes”CPUID clobbers all four of EAX, EBX, ECX, and EDX, and EBX in particular is a callee-saved register under the System V calling convention, so inline assembly issuing CPUID from C on x86-64 needs to explicitly save and restore RBX around the call (or route the query through a small wrapper written entirely in assembly) rather than letting the compiler assume it survives untouched. A kernel targeting virtualized development environments commonly layers a second check on top of the ordinary feature bits: leaf 1’s hypervisor-present bit, followed by leaf 0x40000000, whose EBX/ECX/EDX registers carry a hypervisor-specific vendor string (“KVMKVMKVM” among them) the same way leaf 0 does for the physical CPU vendor, useful for a kernel that wants to take a different code path (skipping a slow hardware probe, for instance) when it knows it’s running under a hypervisor rather than on bare metal.
References
Section titled “References”- ^ Intel, Intel 64 and IA-32 Architectures Software Developer’s Manual, Volume 2A, Chapter 3.3: the full CPUID instruction reference, including every standard and extended leaf.
- ^ AMD, AMD64 Architecture Programmer’s Manual, Volume 3, Appendix E: the AMD-side leaf listing, including the extended feature leaves this article covers.
See also
Section titled “See also”- Long Mode: the transition whose own availability is checked via the extended-leaf
LMbit described above. - Timers: the invariant-TSC feature bit already mentioned there as a concrete example of a leaf-1 flag.
- Model-Specific Registers: a second, complementary way to check feature support, used once CPUID has confirmed the underlying mechanism exists.
- The System V ABI: the calling convention behind the callee-saved
RBXrestriction noted above. - Hardware Random Number Generation: RDRAND/RDSEED, two instructions whose own availability is checked the same way.