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.
158 lines
4.7 KiB
Rust
158 lines
4.7 KiB
Rust
use alloc::boxed::Box;
|
|
use core::sync::atomic::{AtomicBool, Ordering};
|
|
|
|
use crate::sync::ordered::{Mutex, L4};
|
|
use spin::Once;
|
|
|
|
use syscall::data::GlobalSchemes;
|
|
|
|
use crate::{
|
|
acpi::{RxsdtEnum, RXSDT_ENUM},
|
|
context::file::InternalFlags,
|
|
scheme::{SchemeExt, StrOrBytes},
|
|
sync::CleanLockToken,
|
|
};
|
|
|
|
use crate::syscall::{
|
|
error::{Error, Result, EACCES, EBADFD, EINVAL, ENOENT},
|
|
flag::{AcpiVerb, CallFlags, EventFlags},
|
|
usercopy::UserSliceRw,
|
|
};
|
|
|
|
use super::{CallerCtx, KernelScheme, OpenResult};
|
|
|
|
/// A scheme used to access the RSDT or XSDT, and listen for shutdown, which is needed for e.g. `acpid` to function.
|
|
pub struct AcpiScheme;
|
|
|
|
bitflags! {
|
|
#[derive(PartialEq)]
|
|
struct HandleBits: usize {
|
|
const CAN_READ_RXSDT = 1;
|
|
const CAN_REGISTER_KSTOP = 2;
|
|
|
|
// mutually exclusive with the other flags
|
|
const KSTOP_HANDLE = 4;
|
|
}
|
|
}
|
|
|
|
static RXSDT_DATA: Once<Box<[u8]>> = Once::new();
|
|
|
|
static KSTOP_FLAG: Mutex<L4, bool> = Mutex::new(false);
|
|
static EXISTS_KSTOP_HANDLE: AtomicBool = AtomicBool::new(false);
|
|
|
|
pub fn register_kstop(token: &mut CleanLockToken) -> bool {
|
|
*KSTOP_FLAG.lock(token.token()) = true;
|
|
|
|
if !EXISTS_KSTOP_HANDLE.load(Ordering::Relaxed) {
|
|
error!("No userspace ACPI handler was notified when trying to shutdown. This is bad.");
|
|
// Let the kernel shutdown without ACPI.
|
|
return false;
|
|
}
|
|
crate::event::trigger(
|
|
GlobalSchemes::Acpi.scheme_id(),
|
|
HandleBits::KSTOP_HANDLE.bits(),
|
|
EventFlags::EVENT_READ,
|
|
token,
|
|
);
|
|
|
|
// TODO: Context switch directly to the waiting context, to avoid annoying timeouts.
|
|
true
|
|
}
|
|
|
|
impl AcpiScheme {
|
|
pub fn init() {
|
|
// NOTE: This __must__ be called from the main kernel context, while initializing all
|
|
// schemes. If it is called by any other context, then all ACPI data will probably not even
|
|
// be mapped.
|
|
|
|
let mut data_init = false;
|
|
|
|
RXSDT_DATA.call_once(|| {
|
|
data_init = true;
|
|
|
|
let table = match RXSDT_ENUM.get() {
|
|
Some(RxsdtEnum::Rsdt(rsdt)) => rsdt.as_slice(),
|
|
Some(RxsdtEnum::Xsdt(xsdt)) => xsdt.as_slice(),
|
|
None => {
|
|
warn!("expected RXSDT_ENUM to be initialized before AcpiScheme, is ACPI available?");
|
|
&[]
|
|
}
|
|
};
|
|
|
|
Box::from(table)
|
|
});
|
|
|
|
if !data_init {
|
|
error!("AcpiScheme::init called multiple times");
|
|
}
|
|
}
|
|
}
|
|
|
|
impl KernelScheme for AcpiScheme {
|
|
fn scheme_root(&self, _token: &mut CleanLockToken) -> Result<usize> {
|
|
Ok((HandleBits::CAN_READ_RXSDT | HandleBits::CAN_REGISTER_KSTOP).bits())
|
|
}
|
|
fn kopenat(
|
|
&self,
|
|
id: usize,
|
|
path: StrOrBytes,
|
|
_flags: usize,
|
|
_fcntl_flags: u32,
|
|
caller: CallerCtx,
|
|
_token: &mut CleanLockToken,
|
|
) -> Result<OpenResult> {
|
|
let bits = HandleBits::from_bits_retain(id);
|
|
|
|
let new_bits = match path.as_bytes() {
|
|
b"" | b"/" => bits,
|
|
b"kstop" | b"/kstop" => {
|
|
// TODO: can the uid check be removed?
|
|
if caller.uid != 0 || !bits.contains(HandleBits::CAN_REGISTER_KSTOP) {
|
|
return Err(Error::new(EACCES));
|
|
}
|
|
EXISTS_KSTOP_HANDLE.store(true, Ordering::Relaxed);
|
|
HandleBits::KSTOP_HANDLE
|
|
}
|
|
_ => return Err(Error::new(ENOENT)),
|
|
};
|
|
Ok(OpenResult::SchemeLocal(
|
|
new_bits.bits(),
|
|
InternalFlags::empty(),
|
|
))
|
|
}
|
|
fn kcall(
|
|
&self,
|
|
fds: &[usize],
|
|
payload: UserSliceRw,
|
|
flags: CallFlags,
|
|
metadata: &[u64],
|
|
token: &mut CleanLockToken,
|
|
) -> Result<usize> {
|
|
let [handle] = <&[usize; 1]>::try_from(fds)
|
|
.map_err(|_| Error::new(EINVAL))?
|
|
.map(HandleBits::from_bits_retain);
|
|
let verb = metadata
|
|
.get(0)
|
|
.copied()
|
|
.and_then(AcpiVerb::try_from_raw)
|
|
.ok_or(Error::new(EINVAL))?;
|
|
|
|
match verb {
|
|
AcpiVerb::ReadRxsdt => {
|
|
if !handle.contains(HandleBits::CAN_READ_RXSDT) || !flags.contains(CallFlags::READ)
|
|
{
|
|
return Err(Error::new(EINVAL));
|
|
}
|
|
let src = RXSDT_DATA.get().ok_or(Error::new(EBADFD))?;
|
|
payload.copy_common_bytes_from_slice(src)?;
|
|
Ok(src.len())
|
|
}
|
|
AcpiVerb::CheckShutdown => {
|
|
if handle != HandleBits::KSTOP_HANDLE {
|
|
return Err(Error::new(EINVAL));
|
|
}
|
|
Ok(usize::from(*KSTOP_FLAG.lock(token.token())))
|
|
}
|
|
}
|
|
}
|
|
} |