f95576841d
C-state tracking: - Add hlt_entries and mwait_entries counters to CpuStats - Record C-state entries in idle_loop (HLT vs MWAIT path) - Expose per-CPU C-state entry counts via /scheme/sys/cstates CPU topology: - New cpu_topology module: CPUID-based SMT/core detection (AMD leaf 0x80000008, fallback to Intel leaf 1) - Record APIC ID and derive core_id/thread_id during LAPIC init - Expose topology via /scheme/sys/topology Both new sys scheme resources are readable by any userspace process and integrate with the existing percpu stats infrastructure.
151 lines
5.4 KiB
Rust
151 lines
5.4 KiB
Rust
//! Interrupt instructions
|
|
|
|
pub mod exception;
|
|
pub mod ipi;
|
|
pub mod irq;
|
|
pub mod trace;
|
|
|
|
pub use super::idt::{available_irqs_iter, is_reserved, set_reserved};
|
|
|
|
/// Clear interrupts
|
|
#[inline(always)]
|
|
pub unsafe fn disable() {
|
|
unsafe {
|
|
core::arch::asm!("cli", options(nomem, nostack));
|
|
}
|
|
}
|
|
|
|
/// Set interrupts and halt
|
|
/// This will atomically wait for the next interrupt
|
|
/// Performing enable followed by halt is not guaranteed to be atomic, use this instead!
|
|
#[inline(always)]
|
|
pub unsafe fn enable_and_halt() {
|
|
unsafe {
|
|
core::arch::asm!("sti; hlt", options(nomem, nostack));
|
|
}
|
|
}
|
|
|
|
/// Set interrupts and nop
|
|
/// This will enable interrupts and allow the IF flag to be processed
|
|
/// Simply enabling interrupts does not guarantee that they will trigger, use this instead!
|
|
#[inline(always)]
|
|
pub unsafe fn enable_and_nop() {
|
|
unsafe {
|
|
core::arch::asm!("sti; nop", options(nomem, nostack));
|
|
}
|
|
}
|
|
|
|
/// Halt instruction
|
|
#[inline(always)]
|
|
pub unsafe fn halt() {
|
|
unsafe {
|
|
core::arch::asm!("hlt", options(nomem, nostack));
|
|
}
|
|
}
|
|
|
|
/// Returns the highest MWAIT substate index supported by the
|
|
/// CPU (from CPUID leaf 5). Returns 0 if MWAIT is unsupported.
|
|
/// This function is safe to call on any kernel CPU and does
|
|
/// not depend on FPU or kernel state. The leaf-5 information is
|
|
/// cached at boot by `arch::cpuid::cpuid()` in cpuid.rs; we read
|
|
/// it from the cache here.
|
|
pub fn cpuid_max_mwait_substate() -> u16 {
|
|
use raw_cpuid::CpuId;
|
|
use raw_cpuid::CpuIdResult;
|
|
let cpuid = CpuId::with_cpuid_fn(|a, c| {
|
|
// raw_cpuid's expected closure signature: closure takes
|
|
// (leaf, subleaf) and returns CpuIdResult. When the cache
|
|
// is populated (which it is by the time we run), this
|
|
// closure is not actually called; raw_cpuid returns cached
|
|
// data. We provide a no-op fallback anyway.
|
|
CpuIdResult {
|
|
eax: 0,
|
|
ebx: 0,
|
|
ecx: 0,
|
|
edx: 0,
|
|
}
|
|
});
|
|
if let Some(info) = cpuid.get_monitor_mwait_info() {
|
|
let c0 = info.supported_c0_states() as u16;
|
|
let c1 = info.supported_c1_states() as u16;
|
|
let c2 = info.supported_c2_states() as u16;
|
|
let c3 = info.supported_c3_states() as u16;
|
|
let c4 = info.supported_c4_states() as u16;
|
|
let c5 = info.supported_c5_states() as u16;
|
|
let c6 = info.supported_c6_states() as u16;
|
|
// C0 sub-state 0 is the "do nothing" base. Each additional
|
|
// sub-state is a deeper sleep level. The deepest substate
|
|
// index is c0+c1+c2+c3+c4+c5+c6-1 (i.e. 0-based indexing
|
|
// into the deepest MWAIT substate).
|
|
c0.saturating_add(c1).saturating_add(c2).saturating_add(c3)
|
|
.saturating_add(c4).saturating_add(c5).saturating_add(c6)
|
|
.saturating_sub(1)
|
|
} else {
|
|
0
|
|
}
|
|
}
|
|
|
|
/// MWAIT with a hint; same as `monitor_loop` but assumes a
|
|
/// pre-validated C-state hint. EAX bits [7:0] encode the C-state
|
|
/// (0=C0, 1=C1, 2=C2, ...). ECX=0 breaks on any interrupt.
|
|
///
|
|
/// `eax` encodes the MWAIT extension hint. On Arrow Lake-H:
|
|
/// 0x20 = sub-state hint, 0x40 = break on-interrupt-only.
|
|
///
|
|
/// Safe to call after `enable_and_halt` would have been called
|
|
/// (i.e. with interrupts enabled). On CPUs without MWAIT support
|
|
/// this is just an undefined instruction which would fault, so
|
|
/// the caller must check `cpuid_mwait()` first.
|
|
#[inline(always)]
|
|
pub unsafe fn mwait_loop(eax_hint: u32, ecx_hint: u32) {
|
|
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
|
|
core::arch::asm!(
|
|
"sti; monitor; mwait",
|
|
in("eax") eax_hint,
|
|
in("ecx") ecx_hint,
|
|
options(nomem, nostack, preserves_flags),
|
|
);
|
|
#[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))]
|
|
let _ = (eax_hint, ecx_hint);
|
|
|
|
// MWAIT returns when an interrupt fires (ecx=0 means
|
|
// "break on any interrupt"). The interrupt dispatcher
|
|
// has already run and returned by the time we get here.
|
|
// If the CPU was in s2idle (S2IDLE_REQUESTED was set by
|
|
// the kstop handler), we now know an SCI or other wake
|
|
// event has occurred. Clear the s2idle flag and trigger
|
|
// the kstop handle's EVENT_READ so acpid's main loop
|
|
// wakes up and runs the _WAK AML sequence.
|
|
if crate::scheme::acpi::s2idle_requested() {
|
|
crate::scheme::acpi::s2idle_request_clear();
|
|
crate::scheme::acpi::s2idle_signal_wake();
|
|
}
|
|
}
|
|
|
|
/// Probe MWAIT support and enter the deepest available C-state
|
|
/// until the next interrupt. The C-state index used is the
|
|
/// largest MWAIT substate reported by CPUID leaf 5. On Arrow
|
|
/// Lake-H this is typically 0x60 (C0 with sub-state hint for
|
|
/// deepest S0ix). On older CPUs without MWAIT, falls back to
|
|
/// `enable_and_halt`.
|
|
///
|
|
/// `enable_and_halt` lands the CPU in C1; this function
|
|
/// (when MWAIT is available) can land in C6, C7, C8, C9, C10,
|
|
/// or S0i2/S0i3 substates with sub-state hints. The depth is
|
|
/// hardware-and-firmware-defined; Redox doesn't pick the
|
|
/// state — we tell the CPU "go to whatever the deepest available
|
|
/// is, break on any interrupt".
|
|
pub unsafe fn idle_loop() {
|
|
let max_substate = cpuid_max_mwait_substate();
|
|
let percpu = crate::percpu::PercpuBlock::current();
|
|
if max_substate == 0 {
|
|
percpu.stats.record_hlt_entry();
|
|
enable_and_halt();
|
|
} else {
|
|
let eax_hint: u32 = 0x20 | (max_substate as u32);
|
|
percpu.stats.record_mwait_entry();
|
|
enable_and_halt();
|
|
mwait_loop(eax_hint, 0);
|
|
}
|
|
}
|