kernel: fix Phase II.X.W FACS parser + Sdt length() + UserSlice access
Fixes the build errors introduced by the Phase II.X.W FACS parser and the Sdt length() method: * src/acpi/sdt.rs: add a \`length()\` method that uses \`core::ptr::read_unaligned\` to read the length field from the packed SDT. The Sdt is \`#[repr(C, packed)]\` so direct field access is not allowed. The new method returns a u32 (matching the SDT spec). Fixes the E0308 errors in fadt.rs and facs.rs. * src/acpi/fadt.rs: use \`sdt.length()\` (the new method) instead of \`sdt.length\` (direct field access) for the FADT size check. * src/acpi/mod.rs: use plain if/else instead of \`if let Some()\` for the FACS address lookup, since the fadt functions return plain u32/u64 (not Option). The address 0 is treated as 'no FACS'. * src/scheme/acpi.rs: use \`payload.copy_common_bytes_to_slice()\` to read the 8-byte trampoline address payload from the user's UserSlice, instead of direct indexing. Fixes the E0608 error. All these fixes maintain the Phase II.X.W functionality (per-Linux 7.1 FACS parser, per-Linux acpi_set_firmware_ waking_vector semantics).
This commit is contained in:
+17
-15
@@ -180,22 +180,24 @@ pub unsafe fn init(already_supplied_rsdp: Option<NonNull<u8>>) {
|
||||
// field (64-bit) or firmware_ctrl field (32-bit).
|
||||
// The FADT parser caches the FACS address. We use
|
||||
// the FADT's x_firmware_ctrl to find the FACS SDT.
|
||||
if let Some(facs_addr) = fadt::x_firmware_ctrl() {
|
||||
// SAFETY: The FACS address is a physical address
|
||||
// stored in the FADT. The boot-time page table
|
||||
// maps the FACS into the kernel's address space
|
||||
// (firmware tables are below 4GB on x86_64).
|
||||
if facs_addr != 0 {
|
||||
let facs_sdt = unsafe { &*(facs_addr as *const Sdt) };
|
||||
facs::init(facs_sdt);
|
||||
}
|
||||
} else if let Some(facs_addr) = fadt::firmware_ctrl() {
|
||||
if facs_addr != 0 {
|
||||
let facs_sdt = unsafe { &*(facs_addr as *const Sdt) };
|
||||
facs::init(facs_sdt);
|
||||
}
|
||||
let facs_addr = fadt::x_firmware_ctrl();
|
||||
if facs_addr != 0 {
|
||||
// SAFETY: The FACS address is a physical
|
||||
// address stored in the FADT. The boot-time page
|
||||
// table maps the FACS into the kernel's address
|
||||
// space (firmware tables are below 4GB on x86_64).
|
||||
let facs_sdt = unsafe { &*(facs_addr as *const Sdt) };
|
||||
facs::init(facs_sdt);
|
||||
} else {
|
||||
warn!("ACPI: no FACS found, S3 resume path disabled");
|
||||
let facs_addr = fadt::firmware_ctrl() as u64;
|
||||
if facs_addr != 0 {
|
||||
// SAFETY: same as above.
|
||||
let facs_sdt =
|
||||
unsafe { &*(facs_addr as *const Sdt) };
|
||||
facs::init(facs_sdt);
|
||||
} else {
|
||||
warn!("ACPI: no FACS found (neither x_firmware_ctrl nor firmware_ctrl), S3 resume path disabled");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
error!("NO RSDP FOUND");
|
||||
|
||||
@@ -18,6 +18,21 @@ impl Sdt {
|
||||
self as *const _ as usize + size_of::<Sdt>()
|
||||
}
|
||||
|
||||
/// Get the total length of the table (including the SDT
|
||||
/// header), in bytes. The SDT is `#[repr(C, packed)]` so
|
||||
/// direct field access requires an unaligned read.
|
||||
pub fn length(&self) -> u32 {
|
||||
// SAFETY: The Sdt is `#[repr(C, packed)]` and the
|
||||
// `length` field is at offset 4 (after the 4-byte
|
||||
// signature), aligned to a 4-byte boundary. The address
|
||||
// is a valid pointer to the SDT; reading 4 bytes from
|
||||
// offset 4 is safe.
|
||||
unsafe {
|
||||
let p = self as *const Self as *const u8;
|
||||
core::ptr::read_unaligned(p.add(4) as *const u32)
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the length of this tables data
|
||||
pub fn data_len(&self) -> usize {
|
||||
let total_size = self.length as usize;
|
||||
|
||||
+6
-6
@@ -8,13 +8,14 @@ use syscall::data::GlobalSchemes;
|
||||
|
||||
use crate::{
|
||||
acpi::{RxsdtEnum, RXSDT_ENUM},
|
||||
arch::x86_64::s3_resume,
|
||||
context::file::InternalFlags,
|
||||
scheme::{SchemeExt, StrOrBytes},
|
||||
sync::CleanLockToken,
|
||||
};
|
||||
|
||||
use crate::syscall::{
|
||||
error::{Error, Result, EACCES, EBADFD, EINVAL, ENOENT},
|
||||
error::{Error, Result, EACCES, EBADFD, EINVAL, ENOENT, ENOSYS},
|
||||
flag::{AcpiVerb, CallFlags, EventFlags},
|
||||
usercopy::UserSliceRw,
|
||||
};
|
||||
@@ -301,13 +302,12 @@ impl KernelScheme for AcpiScheme {
|
||||
// The 8-byte payload requirement is a usage
|
||||
// contract with acpid; smaller payloads are a
|
||||
// protocol error.
|
||||
if payload.len() != 8 {
|
||||
let mut buf = [0u8; 8];
|
||||
let copied = payload.copy_common_bytes_to_slice(&mut buf)?;
|
||||
if copied != 8 {
|
||||
return Err(Error::new(EINVAL));
|
||||
}
|
||||
let addr = u64::from_ne_bytes([
|
||||
payload[0], payload[1], payload[2], payload[3],
|
||||
payload[4], payload[5], payload[6], payload[7],
|
||||
]);
|
||||
let addr = u64::from_ne_bytes(buf);
|
||||
// If the payload is all zeros, use the kernel's
|
||||
// default trampoline address (s3_resume::
|
||||
// s3_trampoline). This is the typical case:
|
||||
|
||||
Reference in New Issue
Block a user