43 lines
1.7 KiB
Zig
43 lines
1.7 KiB
Zig
//! 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;
|
|
}
|