Files
RedBear-OS/src/acpi/fadt.rs
T
vasilito ec8e88c364 0.3.0: kernel build fixes - syscall aliases and FADT length type cast
- Add SYS_OPENAT/SYS_DUP aliases in local syscall fork for upstream 0.9.0 renamed constants.

- Cast FADT_MIN_SIZE_ACPI_2_0 to u32 to match sdt.length() return type.

- Keep Cargo.lock based on upstream master with only redox_syscall sourced from local fork.
2026-07-06 18:57:41 +03:00

133 lines
5.6 KiB
Rust

//! ACPI Fixed ACPI Description Table (FADT) parser.
//!
//! Per ACPI 6.5 §5.2.9. The FADT contains the hardware register
//! addresses used by the kernel for ACPI sleep state entry (S3/S5)
//! and the SCI interrupt. This module parses the fields the
//! kernel needs (PM1a_CNT, PM1a_STS for the sleep entry path,
//! and x_firmware_ctrl / firmware_ctrl for the FACS address).
//!
//! Hardware-agnostic: the FADT layout is standardized by the ACPI
//! spec; only the field values vary per platform.
use core::sync::atomic::{AtomicU16, AtomicU32, AtomicU64};
use crate::acpi::sdt::Sdt;
/// Phase II: PM1a_CNT port. Read from the FADT at boot, written
/// by `enter_s3()` to enter S3 (SLP_TYP|SLP_EN bits). Also used
/// by S5 entry (set_global_s_state in acpid).
pub static PM1A_CONTROL_PORT: AtomicU16 = AtomicU16::new(0);
/// Phase II: PM1a_STS port. Used by `enter_s3()` to clear
/// WAK_STS (bit 15) before writing SLP_TYP|SLP_EN.
pub static PM1A_STATUS_PORT: AtomicU16 = AtomicU16::new(0);
/// Phase II.X.W: 32-bit FACS address (FADT offset 36,
/// `firmware_ctrl` field). Used as a fallback when
/// `x_firmware_ctrl` (offset 140, ACPI 2.0+) is not present
/// (i.e., for ACPI 1.0 systems).
pub static FIRMWARE_CTRL: AtomicU32 = AtomicU32::new(0);
/// Phase II.X.W: 64-bit FACS address (FADT offset 140,
/// `x_firmware_ctrl` field, ACPI 2.0+). The kernel's FACS
/// parser uses this to find the FACS for writing the
/// `xfirmware_waking_vector` on S3 entry.
pub static X_FIRMWARE_CTRL: AtomicU64 = AtomicU64::new(0);
/// FADT signature bytes ("FACP").
const FADT_SIGNATURE: [u8; 4] = *b"FACP";
/// FADT fixed offsets for the fields we read. These match
/// the ACPI 6.5 §5.2.9 Table 5.6 layout.
mod offsets {
/// PM1a_STS (PM1 Status Register) Block 0 Address.
/// 32-bit General-Purpose Event Register Block 0 Address.
pub const PM1A_STS: usize = 48;
/// PM1a_CNT (PM1 Control Register) Block 0 Address.
/// 32-bit General-Purpose Event Register Block 0 Address.
pub const PM1A_CNT: usize = 56;
/// `firmware_ctrl`: 32-bit Firmware ACPI Control
/// Structure address. ACPI 1.0+.
pub const FIRMWARE_CTRL_32: usize = 36;
/// `x_firmware_ctrl`: 64-bit Firmware ACPI Control
/// Structure address. ACPI 2.0+.
pub const X_FIRMWARE_CTRL_64: usize = 140;
/// FADT minimum size for ACPI 2.0+ (i.e., enough to
/// include `x_firmware_ctrl` at offset 140).
pub const FADT_MIN_SIZE_ACPI_2_0: usize = 148;
/// FADT minimum size for ACPI 1.0.
pub const FADT_MIN_SIZE_ACPI_1_0: usize = 76;
}
/// Parse the FADT from the given SDT bytes and extract the
/// PM1a_CNT, PM1a_STS, and FACS-address fields. Called once at
/// boot after the ACPI table discovery finds the FADT.
///
/// The FADT layout is variable (different sizes for ACPI 1.0 vs
/// 6.5+). We only need the first ~148 bytes which contain the
/// fixed-register addresses. Reference: ACPI 6.5 §5.2.9.
pub fn init(sdt: &Sdt) {
if &sdt.signature != &FADT_SIGNATURE {
return;
}
// SAFETY: We trust the ACPI table discovery code to have
// verified the FADT checksum. The FADT fields are at fixed
// offsets (per the ACPI spec); reading them as u32/u64 is
// safe because all of them are at 4-byte or 8-byte aligned
// offsets on x86_64.
let data = sdt.data_address() as *const u8;
unsafe {
// PM1a_CNT is at offset 56 in the FADT (ACPI 6.5 §5.2.9
// Table 5.6). 32-bit General-Purpose Event Register Block 0
// Address.
let pm1a_cnt = core::ptr::read_unaligned(data.add(offsets::PM1A_CNT) as *const u32);
// PM1a_STS is at offset 48 in the FADT.
let pm1a_sts = core::ptr::read_unaligned(data.add(offsets::PM1A_STS) as *const u32);
// Convert u32 to u16 (port numbers are 16-bit). The low
// 16 bits are the IO port; the high 16 bits are the
// address-space ID which we ignore (always IO on x86).
PM1A_CONTROL_PORT.store(
(pm1a_cnt & 0xFFFF) as u16,
core::sync::atomic::Ordering::Release,
);
PM1A_STATUS_PORT.store(
(pm1a_sts & 0xFFFF) as u16,
core::sync::atomic::Ordering::Release,
);
// Phase II.X.W: 32-bit FACS address (FADT offset 36,
// `firmware_ctrl` field). ACPI 1.0+.
let firmware_ctrl = core::ptr::read_unaligned(
data.add(offsets::FIRMWARE_CTRL_32) as *const u32,
);
FIRMWARE_CTRL.store(firmware_ctrl, core::sync::atomic::Ordering::Release);
// Phase II.X.W: 64-bit FACS address (FADT offset 140,
// `x_firmware_ctrl` field). ACPI 2.0+. We require the
// FADT to be at least 148 bytes to have this field
// (the field is at offset 140, which is 8 bytes for the
// u64, so the minimum FADT size is 148 bytes).
if sdt.length() >= offsets::FADT_MIN_SIZE_ACPI_2_0 as u32 {
let x_firmware_ctrl = core::ptr::read_unaligned(
data.add(offsets::X_FIRMWARE_CTRL_64) as *const u64,
);
X_FIRMWARE_CTRL.store(x_firmware_ctrl, core::sync::atomic::Ordering::Release);
}
}
}
/// Phase II.X.W: 32-bit FACS address (FADT offset 36,
/// `firmware_ctrl` field). Returns 0 if the FADT has not
/// been initialized.
pub fn firmware_ctrl() -> u32 {
FIRMWARE_CTRL.load(core::sync::atomic::Ordering::Acquire)
}
/// Phase II.X.W: 64-bit FACS address (FADT offset 140,
/// `x_firmware_ctrl` field). Returns 0 if the FADT has not
/// been initialized or the FADT is too short to have the
/// field.
pub fn x_firmware_ctrl() -> u64 {
X_FIRMWARE_CTRL.load(core::sync::atomic::Ordering::Acquire)
}