Files
RedBear-OS/src/arch/x86_shared/interrupt/mod.rs
T
vasilito f95576841d kernel: Tier 3 — C-state tracking and CPU topology
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.
2026-07-02 23:27:16 +03:00

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);
}
}