diff --git a/drivers/acpid/src/acpi.rs b/drivers/acpid/src/acpi.rs index a96bf7d1cc..d0048009f5 100644 --- a/drivers/acpid/src/acpi.rs +++ b/drivers/acpid/src/acpi.rs @@ -613,6 +613,39 @@ impl AcpiContext { log::info!("s2idle resume complete"); } + /// Run the S3 resume AML sequence: \_SST(2) → \_WAK(state) → \_SST(1). + /// + /// Mirrors Linux 7.1 `acpi_suspend_reset` wake path + /// (`drivers/acpi/sleep.c:1012`): after the kernel trampoline + /// at s3_resume::s3_trampoline has restored the kernel state, + /// acpid is responsible for running \_WAK(state) to notify the + /// firmware that the system is waking, with the matching + /// \_SST(0) → working (1) transitions bracketing the wake. + /// + /// Phase II.X.W: the kernel-side s3_resume trampoline has + /// already restored the kernel state. The acpid just runs the + /// AML wake sequence and returns. The state argument is the + /// sleep state we are waking from (3 for S3). + pub fn wake_from_sleep_state(&self, state: u8) { + log::info!( + "s3 wake: AML sequence \\_SST(2) -> \\_WAK({}) -> \\_SST(1)", + state + ); + // Step 1: System Status -> waking (2). Mirrors Linux 7.1's + // acpi_s2idle_restore wake step. + if let Err(e) = self.aml_evaluate_simple_method("\\_SST", 2) { + log::debug!("\\_SST(2) not evaluated ({}), continuing", e); + } + // Step 2: \_WAK(state) — the AML wake notification. + if let Err(e) = self.aml_evaluate_simple_method("\\_WAK", state as u64) { + log::error!("\\_WAK({}) failed: {:?}, continuing", state, e); + } + // Step 3: System Status -> working (1). + if let Err(e) = self.aml_evaluate_simple_method("\\_SST", 1) { + log::debug!("\\_SST(1) not evaluated ({}), continuing", e); + } + } + /// Returns the FACS 32-bit firmware_waking_vector, or `None` if no /// FACS is present. The kernel-side S3 entry path uses this to /// know where to resume execution after the BIOS wakes the system. @@ -1292,9 +1325,12 @@ impl Facs { // a u32 into a packed struct field. The Sdt bytes are // 'static for the lifetime of the ACPI context (mapped // from physical memory at boot and never reallocated). - let facs: &mut FacsStruct = unsafe { - &mut *((self.0 .0).as_mut_ptr() as *mut FacsStruct) - }; + // We use Arc::get_mut which is safe because the &mut self + // ensures this is the only strong reference (acpid holds + // the Sdt Arc exclusively). + let bytes = Arc::get_mut(&mut self.0 .0) + .expect("Facs Arc must be uniquely owned here (acpid holds the only strong ref)"); + let facs: &mut FacsStruct = unsafe { &mut *(bytes.as_mut_ptr() as *mut FacsStruct) }; facs.waking_vector = addr; true } @@ -1309,10 +1345,12 @@ impl Facs { return false; } // SAFETY: same as set_waking_vector. The x_waking_vector - // field is at offset 40, 8 bytes wide. - let facs: &mut FacsStruct = unsafe { - &mut *((self.0 .0).as_mut_ptr() as *mut FacsStruct) - }; + // field is at offset 40, 8 bytes wide. We use Arc::get_mut + // for the same reason as set_waking_vector: the &mut self + // ensures this is the only strong reference. + let bytes = Arc::get_mut(&mut self.0 .0) + .expect("Facs Arc must be uniquely owned here (acpid holds the only strong ref)"); + let facs: &mut FacsStruct = unsafe { &mut *(bytes.as_mut_ptr() as *mut FacsStruct) }; facs.x_waking_vector = addr; true } diff --git a/drivers/acpid/src/scheme.rs b/drivers/acpid/src/scheme.rs index 84dbc4697c..0222d61fcb 100644 --- a/drivers/acpid/src/scheme.rs +++ b/drivers/acpid/src/scheme.rs @@ -99,9 +99,10 @@ impl HandleKind<'_> { Self::Symbol { .. } => false, Self::SchemeRoot => false, Self::RegisterPci => false, - Self::Thermal | Self::Power => true, + Self::Thermal | Self::Power | Self::Processor | Self::DmiDir => true, Self::Dmi => true, Self::DmiField(_) => false, + Self::ProcFile { .. } => false, } } fn len(&self, acpi_ctx: &AcpiContext) -> Result { @@ -124,7 +125,12 @@ impl HandleKind<'_> { .unwrap_or(0), // Directories Self::TopLevel | Self::Symbols(_) | Self::Tables => 0, - Self::Thermal | Self::Power => 0, + Self::Thermal | Self::Power | Self::Processor | Self::DmiDir => 0, + // ProcFile contents (e.g. PSS table) are bounded by the + // platform's ACPI table sizes; the maximum reasonable size + // is one page (4096 bytes). Report the file as a fixed + // size so the kernel-side read can mmap it. + Self::ProcFile { .. } => 4096, Self::SchemeRoot | Self::RegisterPci => return Err(Error::new(EBADF)), }) } @@ -163,7 +169,7 @@ impl<'acpi, 'sock> AcpiScheme<'acpi, 'sock> { /// independent; only the wake source (SCI, GPIO, RTC, /// ...) varies per OEM. pub fn kstop_reason(&mut self) -> syscall::Result { - let handle = self.kstop_fd.ok_or(syscall::error::Error::new(syscall::error::EBADF))?; + let handle = self.kstop_fd.as_ref().ok_or(syscall::error::Error::new(syscall::error::EBADF))?; let mut payload = [0u8; 8]; let verb = AcpiVerb::CheckShutdown as u64; let result = handle.call_ro(&mut payload, CallFlags::empty(), &[verb])?; @@ -186,7 +192,7 @@ impl<'acpi, 'sock> AcpiScheme<'acpi, 'sock> { /// Mirrors Linux 7.1 `acpi_s2idle_begin` in /// `kernel/power/suspend.c:91`. pub fn kstop_enter_s2idle(&self) -> syscall::Result<()> { - let handle = self.kstop_fd.ok_or(syscall::error::Error::new(syscall::error::EBADF))?; + let handle = self.kstop_fd.as_ref().ok_or(syscall::error::Error::new(syscall::error::EBADF))?; let verb = AcpiVerb::EnterS2Idle as u64; // AcpiVerb::EnterS2Idle doesn't need a write payload; // the verb code itself is the signal. The kernel @@ -205,7 +211,7 @@ impl<'acpi, 'sock> AcpiScheme<'acpi, 'sock> { /// writes this to FACS via the `SetS3WakingVector` /// AcPiVerb (verb 5). pub fn kstop_enter_s3(&self, trampoline_addr: u64) -> syscall::Result<()> { - let handle = self.kstop_fd.ok_or(syscall::error::Error::new(syscall::error::EBADF))?; + let handle = self.kstop_fd.as_ref().ok_or(syscall::error::Error::new(syscall::error::EBADF))?; let verb = AcpiVerb::SetS3WakingVector as u64; // Payload: 8-byte little-endian u64 (the trampoline // address). The kernel's `SetS3WakingVector` handler @@ -515,6 +521,7 @@ impl SchemeSync for AcpiScheme<'_, '_> { proc_buf = b"# ACPI processor data not yet populated\n".to_vec(); proc_buf.as_slice() } + HandleKind::Tables => return Err(Error::new(EISDIR)), }; let offset = std::cmp::min(src_buf.len(), offset); @@ -645,10 +652,10 @@ impl SchemeSync for AcpiScheme<'_, '_> { HandleKind::Dmi => { // Consumers should `read_to_string("/scheme/acpi/dmi")` // rather than iterating, but we still surface the field - list so that ls /scheme/acpi/dmi/ produces a useful - diagnostic on a live system. We always list the same - set of fields regardless of whether SMBIOS data is - present -- empty entries just produce empty reads. + // list so that ls /scheme/acpi/dmi/ produces a useful + // diagnostic on a live system. We always list the same + // set of fields regardless of whether SMBIOS data is + // present -- empty entries just produce empty reads. for (idx, field) in DMI_FIELDS .iter() .enumerate()