diff --git a/src/acpi/mod.rs b/src/acpi/mod.rs index 75af916c5a..2ab2e17168 100644 --- a/src/acpi/mod.rs +++ b/src/acpi/mod.rs @@ -180,22 +180,24 @@ pub unsafe fn init(already_supplied_rsdp: Option>) { // 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"); diff --git a/src/acpi/sdt.rs b/src/acpi/sdt.rs index 83ff67dac1..8d40c10df6 100644 --- a/src/acpi/sdt.rs +++ b/src/acpi/sdt.rs @@ -18,6 +18,21 @@ impl Sdt { self as *const _ as usize + size_of::() } + /// 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; diff --git a/src/scheme/acpi.rs b/src/scheme/acpi.rs index f03ef41fab..9997b7e0e6 100644 --- a/src/scheme/acpi.rs +++ b/src/scheme/acpi.rs @@ -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: