ec8e88c364
- 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.
133 lines
5.6 KiB
Rust
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)
|
|
}
|