4f2a0436eb
Phase A of the ACPI fork-sync plan (local/docs/ACPI-FORK-SYNC-STRATEGY-2026-06-30.md). Restores the kernel to the upstream Redox OS kernel main branch state for the ACPI subsystem: - Cargo.toml: switch redox_syscall from 0.7.4 (two versions behind) to a git ref of gitlab.redox-os.org/redox-os/syscall.git, matching the upstream master dependency. The crates.io 0.8.1 release predates the AcpiVerb enum that MR #613 / MR #275 introduced, so a crates.io pin is insufficient. - src/acpi/rsdp.rs: full rewrite to match upstream f49c7d99 (RSDP validation + NonNull + fail-softly): * signature check "RSD PTR " * 20-byte base checksum * full-length checksum for revision >= 2 * NonNull<u8> instead of *const u8 Fixes gap #1 from the 2026-06-30 ACPI assessment: the kernel was accepting any pointer from the bootloader without validation. - src/startup/mod.rs: acpi_rsdp() returns Option<NonNull<u8>> to match the new Rsdp::get_rsdp signature. - src/acpi/mod.rs: init() takes Option<NonNull<u8>>. - src/scheme/acpi.rs: full rewrite to upstream MR #613 (Simplify acpi scheme). Drops the /scheme/kernel.acpi/ filesystem surface in favor of a single Fd::open + call() interface with AcpiVerb verbs: * AcpiVerb::ReadRxsdt - returns the raw RXSDT bytes * AcpiVerb::CheckShutdown - returns whether shutdown is pending Uses HandleBits bitflags, atomic EXISTS_KSTOP_HANDLE, Mutex<L4> from crate::sync::ordered. Replaces /scheme/kernel.acpi/rxsdt and /scheme/kernel.acpi/kstop files. - src/scheme/mod.rs: KernelScheme::kcall signature updated to take fds: &[usize] instead of id: usize (matches upstream). kfpath now has a default body returning EOPNOTSUPP (matches upstream). - src/scheme/memory.rs, proc.rs, user.rs: kcall impls updated to match new trait signature, using fds.first() to extract the single handle for backward compat. - src/scheme/proc.rs: kcall dispatch adds _ => Err(EINVAL) catch-all for the new ProcSchemeVerb variants (RegsInt, RegsFloat, RegsEnv, SchedAffinity, Start) that the gitlab syscall crate adds. These verbs are not yet implemented in the proc scheme; the catch-all returns EINVAL cleanly instead of failing to compile. - src/syscall/fs.rs: SYS_CALL dispatcher now passes &[number] to scheme.kcall() to match the new trait signature. - Makefile: removed -Z json-target-spec flag (promoted to stable in nightly 2026-04-01; the flag is unknown in our pinned toolchain). Verified by `make` in local/sources/kernel/ with PATH including the prefix cross-toolchain: kernel builds and links successfully.
240 lines
6.8 KiB
Rust
240 lines
6.8 KiB
Rust
use core::{
|
|
hint, slice,
|
|
sync::atomic::{AtomicBool, Ordering},
|
|
};
|
|
use core::ptr::NonNull;
|
|
|
|
use crate::{
|
|
arch::interrupt,
|
|
context,
|
|
context::switch::SwitchResult,
|
|
memory::{PhysicalAddress, RmmA, RmmArch},
|
|
profiling, scheme,
|
|
sync::CleanLockToken,
|
|
};
|
|
|
|
pub mod memory;
|
|
|
|
#[repr(C, packed(8))]
|
|
pub(crate) struct KernelArgs {
|
|
kernel_base: u64,
|
|
kernel_size: u64,
|
|
|
|
stack_base: u64,
|
|
stack_size: u64,
|
|
|
|
env_base: u64,
|
|
env_size: u64,
|
|
|
|
/// The base pointer to the saved RSDP or device tree blob.
|
|
///
|
|
/// On x86 this field can be NULL, and if so, the system has not booted
|
|
/// with UEFI or in some other way retrieved the RSDPs. The kernel or a
|
|
/// userspace driver will thus try searching the BIOS memory instead. On
|
|
/// UEFI systems, searching is not guaranteed to actually work though.
|
|
/// On other architectures this field must always contain a pointer to
|
|
/// either an RSDP or device tree blob.
|
|
pub(crate) hwdesc_base: u64,
|
|
pub(crate) hwdesc_size: u64,
|
|
|
|
areas_base: u64,
|
|
areas_size: u64,
|
|
|
|
/// The physical base 64-bit pointer to the contiguous bootstrap/initfs.
|
|
bootstrap_base: u64,
|
|
/// Size of contiguous bootstrap/initfs physical region, not necessarily page aligned.
|
|
bootstrap_size: u64,
|
|
}
|
|
|
|
impl KernelArgs {
|
|
pub(crate) fn print(&self) {
|
|
debug!(
|
|
"Kernel: {:X}:{:X}",
|
|
{ self.kernel_base },
|
|
self.kernel_base + self.kernel_size
|
|
);
|
|
debug!(
|
|
"Env: {:X}:{:X}",
|
|
{ self.env_base },
|
|
self.env_base + self.env_size
|
|
);
|
|
debug!(
|
|
"HWDESC: {:X}:{:X}",
|
|
{ self.hwdesc_base },
|
|
self.hwdesc_base + self.hwdesc_size
|
|
);
|
|
debug!(
|
|
"Areas: {:X}:{:X}",
|
|
{ self.areas_base },
|
|
self.areas_base + self.areas_size
|
|
);
|
|
debug!(
|
|
"Bootstrap: {:X}:{:X}",
|
|
{ self.bootstrap_base },
|
|
self.bootstrap_base + self.bootstrap_size
|
|
);
|
|
}
|
|
|
|
pub(crate) fn bootstrap(&self) -> Bootstrap {
|
|
Bootstrap {
|
|
base: crate::memory::Frame::containing(crate::memory::PhysicalAddress::new(
|
|
self.bootstrap_base as usize,
|
|
)),
|
|
page_count: (self.bootstrap_size as usize) / crate::memory::PAGE_SIZE,
|
|
env: self.env(),
|
|
}
|
|
}
|
|
|
|
pub(crate) fn env(&self) -> &'static [u8] {
|
|
unsafe {
|
|
slice::from_raw_parts(
|
|
RmmA::phys_to_virt(PhysicalAddress::new(self.env_base as usize)).data()
|
|
as *const u8,
|
|
self.env_size as usize,
|
|
)
|
|
}
|
|
}
|
|
|
|
pub(crate) fn acpi_rsdp(&self) -> Option<NonNull<u8>> {
|
|
if self.hwdesc_base != 0 {
|
|
let data = unsafe {
|
|
slice::from_raw_parts(
|
|
RmmA::phys_to_virt(PhysicalAddress::new(self.hwdesc_base as usize)).data()
|
|
as *const u8,
|
|
self.hwdesc_size as usize,
|
|
)
|
|
};
|
|
if data.starts_with(b"RSD PTR ") {
|
|
Some(NonNull::new(data.as_ptr() as *mut u8).unwrap())
|
|
} else {
|
|
None
|
|
}
|
|
} else {
|
|
None
|
|
}
|
|
}
|
|
|
|
pub(crate) fn dtb(&self) -> Option<fdt::Fdt<'static>> {
|
|
if self.hwdesc_base != 0 {
|
|
let data = unsafe {
|
|
slice::from_raw_parts(
|
|
RmmA::phys_to_virt(PhysicalAddress::new(self.hwdesc_base as usize)).data()
|
|
as *const u8,
|
|
self.hwdesc_size as usize,
|
|
)
|
|
};
|
|
fdt::Fdt::new(data).ok()
|
|
} else {
|
|
None
|
|
}
|
|
}
|
|
}
|
|
|
|
pub(crate) fn init_env() -> &'static [u8] {
|
|
BOOTSTRAP.get().expect("BOOTSTRAP was not set").env
|
|
}
|
|
|
|
extern "C" fn userspace_init() {
|
|
let mut token = unsafe { CleanLockToken::new() };
|
|
let bootstrap = BOOTSTRAP.get().expect("BOOTSTRAP was not set");
|
|
unsafe { crate::syscall::process::usermode_bootstrap(bootstrap, &mut token) }
|
|
}
|
|
|
|
pub(crate) struct Bootstrap {
|
|
pub(crate) base: crate::memory::Frame,
|
|
pub(crate) page_count: usize,
|
|
env: &'static [u8],
|
|
}
|
|
|
|
static BOOTSTRAP: spin::Once<Bootstrap> = spin::Once::new();
|
|
pub(crate) static AP_READY: AtomicBool = AtomicBool::new(false);
|
|
static BSP_READY: AtomicBool = AtomicBool::new(false);
|
|
|
|
/// This is the kernel entry point for the primary CPU. The arch crate is responsible for calling this
|
|
pub(crate) fn kmain(bootstrap: Bootstrap) -> ! {
|
|
let mut token = unsafe { CleanLockToken::new() };
|
|
|
|
BSP_READY.store(true, Ordering::SeqCst);
|
|
|
|
//Initialize the first context, stored in kernel/src/context/mod.rs
|
|
context::init(&mut token);
|
|
|
|
//Initialize global schemes, such as `acpi:`.
|
|
scheme::init_globals();
|
|
|
|
debug!("BSP: {} CPUs", crate::cpu_count());
|
|
debug!("Env: {:?}", ::core::str::from_utf8(bootstrap.env));
|
|
|
|
BOOTSTRAP.call_once(|| bootstrap);
|
|
|
|
profiling::ready_for_profiling();
|
|
|
|
let owner = None; // kmain not owned by any fd
|
|
match context::spawn(true, owner, userspace_init, &mut token) {
|
|
Ok(context_lock) => {
|
|
let mut context = context_lock.write(token.token());
|
|
context.status = context::Status::Runnable;
|
|
context.name.clear();
|
|
context.name.push_str("[bootstrap]");
|
|
|
|
// TODO: Remove these from kernel
|
|
context.euid = 0;
|
|
context.egid = 0;
|
|
}
|
|
Err(err) => {
|
|
panic!("failed to spawn userspace_init: {:?}", err);
|
|
}
|
|
}
|
|
|
|
run_userspace(&mut token)
|
|
}
|
|
|
|
/// This is the main kernel entry point for secondary CPUs
|
|
#[allow(unreachable_code, unused_variables, dead_code)]
|
|
pub(crate) fn kmain_ap(cpu_id: crate::cpu_set::LogicalCpuId) -> ! {
|
|
let mut token = unsafe { CleanLockToken::new() };
|
|
|
|
AP_READY.store(true, Ordering::SeqCst);
|
|
while !BSP_READY.load(Ordering::SeqCst) {
|
|
hint::spin_loop();
|
|
}
|
|
|
|
profiling::maybe_run_profiling_helper_forever(cpu_id);
|
|
|
|
if !cfg!(feature = "multi_core") {
|
|
debug!("AP {}: Disabled", cpu_id);
|
|
|
|
loop {
|
|
unsafe {
|
|
interrupt::disable();
|
|
interrupt::halt();
|
|
}
|
|
}
|
|
}
|
|
|
|
context::init(&mut token);
|
|
|
|
debug!("AP {}", cpu_id);
|
|
|
|
profiling::ready_for_profiling();
|
|
|
|
run_userspace(&mut token);
|
|
}
|
|
|
|
fn run_userspace(token: &mut CleanLockToken) -> ! {
|
|
loop {
|
|
unsafe {
|
|
interrupt::disable();
|
|
match context::switch(token) {
|
|
SwitchResult::Switched => {
|
|
interrupt::enable_and_nop();
|
|
}
|
|
SwitchResult::AllContextsIdle => {
|
|
// Enable interrupts, then halt CPU (to save power) until the next interrupt is actually fired.
|
|
interrupt::enable_and_halt();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|