//! CPUID — the CPU describing itself. //! //! One helper for the whole architecture layer (the timer's TSC leaves, the //! supervisor-hardening feature bits), rather than a private copy per module. //! `leaf` always executes with ECX = 0, which is what every leaf danos reads //! wants: leaf 7's feature words live in sub-leaf 0, and leaves that ignore ECX //! don't care. A sub-leaf-taking caller would add its own entry point here. //! //! **Always gate on `supports` first.** CPUID does not fault on an out-of-range //! leaf — it returns the data of the highest supported leaf instead, which would //! be read as a feature bit that isn't there. The maximum lives in leaf 0 (basic //! range) and leaf 0x80000000 (extended range). pub const Registers = struct { eax: u32, ebx: u32, ecx: u32, edx: u32 }; /// Execute CPUID for `number` at sub-leaf 0. pub fn leaf(number: u32) Registers { var a: u32 = undefined; var b: u32 = undefined; var c: u32 = undefined; var d: u32 = undefined; asm volatile ("cpuid" : [a] "={eax}" (a), [b] "={ebx}" (b), [c] "={ecx}" (c), [d] "={edx}" (d), : [leaf] "{eax}" (number), [sub] "{ecx}" (@as(u32, 0)), ); return .{ .eax = a, .ebx = b, .ecx = c, .edx = d }; } /// Whether `number` is inside the range this CPU actually enumerates — the basic /// range for a leaf below 0x80000000, the extended range above it. Every read of /// a leaf beyond 0 or 0x80000000 must pass through here first (see the module doc). pub fn supports(number: u32) bool { const maximum = if (number >= 0x8000_0000) leaf(0x8000_0000).eax else leaf(0).eax; return maximum >= number; }