Merge bootprocess branch overlay into 0.2.0
Restore all bootprocess branch files that were overwritten by later 0.2.0 commits. This overlay brings back the complete boot infrastructure: - Configs: redbear-full, redbear-mini, redbear-device-services, driver .d files - Kernel: IRQ affinity, x2APIC, C-states, NUMA (SLIT/SRAT), MCS locks, cpuidle - Base patches: P0-P55 + new P6 (lived block_size=512) + P57 (fbbootlogd graceful init) - Driver infra: driver-manager, udev-shim, thermald, cpufreqd, iommu, redox-driver-sys/core - GPU: redox-drm with improved connector handling - System: redbear-info, redbear-hwutils phase-timer-check - Build system: fetch.rs improvements, build-iso.sh, run_full.sh - Kernel source: new ACPI (SLIT, SRAT), cpuidle, cstate, MCS lock modules 83 files changed, +3966/-1248 lines
This commit is contained in:
@@ -0,0 +1,186 @@
|
||||
use core::cell::SyncUnsafeCell;
|
||||
use core::sync::atomic::{AtomicUsize, Ordering};
|
||||
|
||||
use crate::arch::cpuid::cpuid;
|
||||
use crate::syscall::error::{Error, Result, EINVAL};
|
||||
|
||||
#[repr(align(64))]
|
||||
struct MonitorTarget {
|
||||
value: AtomicUsize,
|
||||
}
|
||||
|
||||
static MONITOR_TARGET: MonitorTarget = MonitorTarget {
|
||||
value: AtomicUsize::new(0),
|
||||
};
|
||||
|
||||
bitflags::bitflags! {
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub struct CStateFlags: u32 {
|
||||
const NEEDS_MONITOR = 1;
|
||||
const NEEDS_WBINVD = 2;
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct CState {
|
||||
pub name: &'static str,
|
||||
pub typ: u32,
|
||||
pub latency: u32,
|
||||
pub power: u32,
|
||||
pub mwait_hint: u32,
|
||||
pub flags: CStateFlags,
|
||||
}
|
||||
|
||||
const MAX_CSTATES: usize = 8;
|
||||
static CPUIDLE_STATES: SyncUnsafeCell<[Option<CState>; MAX_CSTATES]> =
|
||||
SyncUnsafeCell::new([None; MAX_CSTATES]);
|
||||
static NUM_CPUIDLE_STATES: AtomicUsize = AtomicUsize::new(0);
|
||||
|
||||
static CSTATE_POLICY_MAX: AtomicUsize = AtomicUsize::new(0);
|
||||
|
||||
fn has_mwait() -> bool {
|
||||
cpuid().get_feature_info().map_or(false, |info| info.has_monitor_mwait())
|
||||
}
|
||||
|
||||
fn add_state(index: usize, state: CState) {
|
||||
unsafe {
|
||||
(*CPUIDLE_STATES.get())[index] = Some(state);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn init() {
|
||||
add_state(0, CState {
|
||||
name: "C1",
|
||||
typ: 1,
|
||||
latency: 1,
|
||||
power: 1000,
|
||||
mwait_hint: 0x00,
|
||||
flags: CStateFlags::empty(),
|
||||
});
|
||||
let mut count = 1;
|
||||
|
||||
if has_mwait() {
|
||||
add_state(count, CState {
|
||||
name: "C1E",
|
||||
typ: 1,
|
||||
latency: 2,
|
||||
power: 800,
|
||||
mwait_hint: 0x01,
|
||||
flags: CStateFlags::NEEDS_MONITOR,
|
||||
});
|
||||
count += 1;
|
||||
|
||||
add_state(count, CState {
|
||||
name: "C2",
|
||||
typ: 2,
|
||||
latency: 10,
|
||||
power: 500,
|
||||
mwait_hint: 0x10,
|
||||
flags: CStateFlags::NEEDS_MONITOR,
|
||||
});
|
||||
count += 1;
|
||||
|
||||
add_state(count, CState {
|
||||
name: "C3",
|
||||
typ: 3,
|
||||
latency: 50,
|
||||
power: 100,
|
||||
mwait_hint: 0x20,
|
||||
flags: CStateFlags::NEEDS_MONITOR | CStateFlags::NEEDS_WBINVD,
|
||||
});
|
||||
count += 1;
|
||||
|
||||
add_state(count, CState {
|
||||
name: "C6",
|
||||
typ: 6,
|
||||
latency: 100,
|
||||
power: 50,
|
||||
mwait_hint: 0x50,
|
||||
flags: CStateFlags::NEEDS_MONITOR | CStateFlags::NEEDS_WBINVD,
|
||||
});
|
||||
count += 1;
|
||||
|
||||
add_state(count, CState {
|
||||
name: "C7",
|
||||
typ: 7,
|
||||
latency: 200,
|
||||
power: 30,
|
||||
mwait_hint: 0x60,
|
||||
flags: CStateFlags::NEEDS_MONITOR | CStateFlags::NEEDS_WBINVD,
|
||||
});
|
||||
count += 1;
|
||||
}
|
||||
|
||||
NUM_CPUIDLE_STATES.store(count, Ordering::SeqCst);
|
||||
log::info!("cpuidle: initialized {} states (mwait={})", count, has_mwait());
|
||||
}
|
||||
|
||||
pub fn policy_read() -> usize {
|
||||
CSTATE_POLICY_MAX.load(Ordering::Relaxed)
|
||||
}
|
||||
|
||||
pub fn policy_write(buf: &[u8]) -> Result<usize> {
|
||||
let s = core::str::from_utf8(buf).map_err(|_| Error::new(EINVAL))?;
|
||||
let s = s.trim();
|
||||
let val: usize = s.parse().map_err(|_| Error::new(EINVAL))?;
|
||||
let num_states = NUM_CPUIDLE_STATES.load(Ordering::Relaxed);
|
||||
if val >= num_states {
|
||||
return Err(Error::new(EINVAL));
|
||||
}
|
||||
CSTATE_POLICY_MAX.store(val, Ordering::Relaxed);
|
||||
log::info!("cpuidle: policy set to max state {}", val);
|
||||
Ok(s.len())
|
||||
}
|
||||
|
||||
pub fn resource() -> Result<alloc::vec::Vec<u8>> {
|
||||
let mut output = alloc::string::String::new();
|
||||
let num_states = NUM_CPUIDLE_STATES.load(Ordering::Relaxed);
|
||||
let policy = CSTATE_POLICY_MAX.load(Ordering::Relaxed);
|
||||
output.push_str(&format!("policy_max: {}\n", policy));
|
||||
output.push_str(&format!("num_states: {}\n", num_states));
|
||||
for i in 0..num_states {
|
||||
if let Some(state) = unsafe { (*CPUIDLE_STATES.get())[i] } {
|
||||
output.push_str(&format!(
|
||||
"state{}: name={} type={} latency={}us power={} hint={:#x} flags={:?}\n",
|
||||
i, state.name, state.typ, state.latency, state.power, state.mwait_hint, state.flags
|
||||
));
|
||||
}
|
||||
}
|
||||
Ok(output.into_bytes())
|
||||
}
|
||||
|
||||
pub unsafe fn enter_idle() {
|
||||
let policy_max = CSTATE_POLICY_MAX.load(Ordering::Relaxed);
|
||||
let num_states = NUM_CPUIDLE_STATES.load(Ordering::Relaxed);
|
||||
let target_index = if num_states == 0 {
|
||||
0
|
||||
} else {
|
||||
core::cmp::min(policy_max, num_states - 1)
|
||||
};
|
||||
|
||||
if target_index == 0 {
|
||||
unsafe { crate::arch::interrupt::enable_and_halt(); }
|
||||
return;
|
||||
}
|
||||
|
||||
let state = match unsafe { (*CPUIDLE_STATES.get())[target_index] } {
|
||||
Some(s) => s,
|
||||
None => {
|
||||
unsafe { crate::arch::interrupt::enable_and_halt(); }
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
if state.flags.contains(CStateFlags::NEEDS_MONITOR) {
|
||||
let addr = &MONITOR_TARGET.value as *const AtomicUsize as *const u8;
|
||||
unsafe { crate::arch::interrupt::monitor(addr, 0, 0); }
|
||||
}
|
||||
|
||||
if state.flags.contains(CStateFlags::NEEDS_WBINVD) {
|
||||
unsafe { core::arch::asm!("wbinvd", options(nostack)); }
|
||||
}
|
||||
|
||||
unsafe {
|
||||
crate::arch::interrupt::enable_and_mwait(state.mwait_hint, 0);
|
||||
}
|
||||
}
|
||||
@@ -120,6 +120,21 @@ impl IoApic {
|
||||
reg |= u64::from(mask) << 16;
|
||||
let _ = guard.write_ioredtbl(idx, reg);
|
||||
}
|
||||
/// Change the destination APIC for a GSI by reprogramming the redirection table entry.
|
||||
/// Preserves all other fields (vector, polarity, trigger mode, delivery mode, mask).
|
||||
/// Returns true if the entry was successfully updated.
|
||||
pub fn set_irq_affinity(&self, gsi: u32, dest: ApicId) -> bool {
|
||||
let idx = (gsi - self.gsi_start) as u8;
|
||||
let mut guard = self.regs.lock();
|
||||
let Some(mut entry) = guard.read_ioredtbl(idx) else {
|
||||
return false;
|
||||
};
|
||||
// Clear destination field (bits 63:56 for xAPIC physical mode)
|
||||
// and set new destination APIC ID
|
||||
entry &= !(0xFF_u64 << 56);
|
||||
entry |= u64::from(dest.get()) << 56;
|
||||
guard.write_ioredtbl(idx, entry)
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(u8)]
|
||||
@@ -474,3 +489,14 @@ pub unsafe fn unmask(irq: u8) {
|
||||
};
|
||||
apic.set_mask(gsi, false);
|
||||
}
|
||||
|
||||
/// Change the destination CPU for an IRQ by reprogramming the IOAPIC redirection entry.
|
||||
/// Resolves the legacy IRQ to its GSI, finds the owning IOAPIC, and updates the destination
|
||||
/// APIC ID in the redirection table while preserving all other fields.
|
||||
pub unsafe fn set_affinity(irq: u8, dest: ApicId) -> bool {
|
||||
let gsi = resolve(irq);
|
||||
match find_ioapic(gsi) {
|
||||
Some(apic) => apic.set_irq_affinity(gsi, dest),
|
||||
None => false,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,10 +59,10 @@ impl LocalApic {
|
||||
.is_some_and(|feature_info| feature_info.has_x2apic());
|
||||
|
||||
if !self.x2 {
|
||||
debug!("Detected xAPIC at {:#x}", physaddr.data());
|
||||
info!("Detected xAPIC at {:#x}", physaddr.data());
|
||||
self.address = map_device_memory(physaddr, 4096).data();
|
||||
} else {
|
||||
debug!("Detected x2APIC");
|
||||
info!("Detected x2APIC");
|
||||
}
|
||||
|
||||
self.init_ap();
|
||||
|
||||
@@ -110,6 +110,8 @@ pub fn set_reserved(cpu_id: LogicalCpuId, index: u8, reserved: bool) {
|
||||
}
|
||||
|
||||
pub fn available_irqs_iter(cpu_id: LogicalCpuId) -> impl Iterator<Item = u8> + 'static {
|
||||
let count = (32..=254).filter(|&index| !is_reserved(cpu_id, index)).count();
|
||||
info!("available_irqs_iter: cpu_id={} count={}", cpu_id.get(), count);
|
||||
(32..=254).filter(move |&index| !is_reserved(cpu_id, index))
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user