diff --git a/drivers/acpid/src/acpi.rs b/drivers/acpid/src/acpi.rs index 94a1eb17..c8919290 100644 --- a/drivers/acpid/src/acpi.rs +++ b/drivers/acpid/src/acpi.rs @@ -52,9 +52,7 @@ impl SdtHeader { } } pub fn length(&self) -> usize { - self.length - .try_into() - .expect("expected usize to be at least 32 bits") + self.length as usize } } @@ -132,6 +130,9 @@ impl Drop for PhysmapGuard { pub struct Sdt(Arc<[u8]>); impl Sdt { + // SDT validation is split between parser and caller policy: + // - this parser only decides whether a given byte slice is structurally valid, + // - callers decide whether rejection is fatal (root [R|X]SDT) or degradable (child tables). pub fn new(slice: Arc<[u8]>) -> Result { let header = match plain::from_bytes::(&slice) { Ok(header) => header, @@ -233,6 +234,177 @@ impl fmt::Debug for Sdt { pub struct Dsdt(Sdt); pub struct Ssdt(Sdt); +#[derive(Clone, Copy, Debug)] +pub enum AmlBootstrapMethod { + HwdEnv, + X86BiosFallback, +} +impl AmlBootstrapMethod { + fn as_str(self) -> &'static str { + match self { + Self::HwdEnv => "hwd RSDP_ADDR/RSDP_SIZE handoff", + Self::X86BiosFallback => "x86 BIOS fallback", + } + } +} + +#[derive(Clone, Debug)] +pub struct AmlBootstrap { + rsdp_addr: usize, + rsdp_size: Option, + method: AmlBootstrapMethod, +} +impl AmlBootstrap { + pub fn from_env() -> Result> { + let rsdp_addr = usize::from_str_radix(&std::env::var("RSDP_ADDR")?, 16)?; + let rsdp_size = match std::env::var("RSDP_SIZE") { + Ok(size) => Some(usize::from_str_radix(&size, 16)?), + Err(std::env::VarError::NotPresent) => None, + Err(err) => return Err(Box::new(err)), + }; + + Ok(Self { + rsdp_addr, + rsdp_size, + method: AmlBootstrapMethod::HwdEnv, + }) + } + + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + pub fn x86_bios_fallback() -> Result, Box> { + if let Some(rsdp_addr) = search_x86_bios_rsdp()? { + return Ok(Some(Self { + rsdp_addr, + rsdp_size: None, + method: AmlBootstrapMethod::X86BiosFallback, + })); + } + + Ok(None) + } + + #[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))] + pub fn x86_bios_fallback() -> Result, Box> { + Ok(None) + } + + pub fn log_bootstrap(&self) { + log::info!( + "acpid: AML bootstrap via {} (RSDP at {:#X})", + self.method.as_str(), + self.rsdp_addr + ); + + if let Some(rsdp_size) = self.rsdp_size { + log::debug!("acpid: AML bootstrap RSDP_SIZE={:#X}", rsdp_size); + } + } +} + +#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +const RSDP_SIGNATURE: &[u8; 8] = b"RSD PTR "; + +#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +fn search_x86_bios_rsdp() -> Result, Box> { + let ebda_segment = read_u16_physical(0x40E)?; + let ebda_addr = usize::from(ebda_segment) << 4; + + if ebda_addr != 0 { + if let Some(rsdp_addr) = search_rsdp_region(ebda_addr, 1024)? { + return Ok(Some(rsdp_addr)); + } + } + + search_rsdp_region(0xE0000, 0x20000).map_err(Into::into) +} + +#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +fn read_u16_physical(physaddr: usize) -> std::io::Result { + let start_page = physaddr / PAGE_SIZE * PAGE_SIZE; + let page_offset = physaddr % PAGE_SIZE; + let map = PhysmapGuard::map(start_page, 1)?; + let bytes = map + .get(page_offset..page_offset + mem::size_of::()) + .ok_or_else(|| std::io::Error::new(std::io::ErrorKind::UnexpectedEof, "short BIOS map"))?; + + Ok(u16::from_le_bytes([bytes[0], bytes[1]])) +} + +#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +fn search_rsdp_region(physaddr: usize, length: usize) -> std::io::Result> { + let start_page = physaddr / PAGE_SIZE * PAGE_SIZE; + let page_offset = physaddr % PAGE_SIZE; + let mapped_len = page_offset + length; + let page_count = mapped_len.div_ceil(PAGE_SIZE); + let map = PhysmapGuard::map(start_page, page_count)?; + let region = map.get(page_offset..page_offset + length).ok_or_else(|| { + std::io::Error::new(std::io::ErrorKind::UnexpectedEof, "short BIOS RSDP search window") + })?; + + for candidate_offset in (0..=length.saturating_sub(20)).step_by(16) { + if region + .get(candidate_offset..candidate_offset + RSDP_SIGNATURE.len()) + != Some(&RSDP_SIGNATURE[..]) + { + continue; + } + + if rsdp_candidate_valid(®ion[candidate_offset..]) { + return Ok(Some(physaddr + candidate_offset)); + } + } + + Ok(None) +} + +#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +fn rsdp_candidate_valid(candidate: &[u8]) -> bool { + if candidate.len() < 20 || &candidate[..RSDP_SIGNATURE.len()] != RSDP_SIGNATURE { + return false; + } + + if checksum_is_zero(&candidate[..20]).is_err() { + return false; + } + + let revision = candidate[15]; + if revision < 2 { + return true; + } + + if candidate.len() < 36 { + return false; + } + + let declared_length = u32::from_le_bytes([candidate[20], candidate[21], candidate[22], candidate[23]]) + as usize; + if declared_length < 36 || candidate.len() < declared_length { + return false; + } + + checksum_is_zero(&candidate[..declared_length]).is_ok() +} + +#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +fn checksum_is_zero(bytes: &[u8]) -> Result<(), ()> { + let checksum = bytes + .iter() + .copied() + .fold(0_u8, |current_sum, item| current_sum.wrapping_add(item)); + + if checksum == 0 { + Ok(()) + } else { + Err(()) + } +} + +#[derive(Clone, Copy, Debug)] +struct SleepTypeData { + slp_typa: u16, + slp_typb: u16, +} + // Current AML implementation builds the aml_context.namespace at startup, // but the cache for symbols is lazy-loaded when someone // reads from the acpi:/symbols scheme. @@ -245,15 +417,20 @@ pub struct AmlSymbols { symbol_cache: FxHashMap, page_cache: Arc>, aml_region_handlers: Vec<(RegionSpace, Box)>, + aml_bootstrap: Option, } impl AmlSymbols { - pub fn new(aml_region_handlers: Vec<(RegionSpace, Box)>) -> Self { + pub fn new( + aml_bootstrap: Option, + aml_region_handlers: Vec<(RegionSpace, Box)>, + ) -> Self { Self { aml_context: None, symbol_cache: FxHashMap::default(), page_cache: Arc::new(Mutex::new(AmlPageCache::default())), aml_region_handlers, + aml_bootstrap, } } @@ -264,9 +441,12 @@ impl AmlSymbols { let format_err = |err| format!("{:?}", err); let handler = AmlPhysMemHandler::new(pci_fd, Arc::clone(&self.page_cache)); //TODO: use these parsed tables for the rest of acpid - let rsdp_address = usize::from_str_radix(&std::env::var("RSDP_ADDR")?, 16)?; + let bootstrap = self + .aml_bootstrap + .as_ref() + .ok_or_else(|| std::io::Error::other("AML bootstrap unavailable"))?; let tables = - unsafe { AcpiTables::from_rsdp(handler.clone(), rsdp_address).map_err(format_err)? }; + unsafe { AcpiTables::from_rsdp(handler.clone(), bootstrap.rsdp_addr).map_err(format_err)? }; let platform = AcpiPlatform::new(tables, handler).map_err(format_err)?; let interpreter = Interpreter::new_from_platform(&platform).map_err(format_err)?; for (region, handler) in self.aml_region_handlers.drain(..) { @@ -316,7 +496,7 @@ impl AmlSymbols { .namespace .lock() .traverse(|level_aml_name, level| { - for (child_seg, handle) in level.values.iter() { + for (child_seg, _handle) in level.values.iter() { if let Ok(aml_name) = AmlName::from_name_seg(child_seg.to_owned()).resolve(level_aml_name) { @@ -379,6 +559,7 @@ pub struct AcpiContext { tables: Vec, dsdt: Option, fadt: Option, + shutdown_s5: RwLock>, aml_symbols: RwLock, @@ -426,27 +607,56 @@ impl AcpiContext { pub fn init( rxsdt_physaddrs: impl Iterator, + aml_bootstrap: Option, ec: Vec<(RegionSpace, Box)>, ) -> Self { - let tables = rxsdt_physaddrs - .map(|physaddr| { - let physaddr: usize = physaddr - .try_into() - .expect("expected ACPI addresses to be compatible with the current word size"); - - log::trace!("TABLE AT {:#>08X}", physaddr); - - Sdt::load_from_physical(physaddr).expect("failed to load physical SDT") - }) - .collect::>(); + // Child-table validation policy: + // - checksum/length failures are degradable: warn, skip the table, continue boot, + // - malformed FADT is handled separately as "raw-table-only" mode for ACPI control paths, + // - MADT subtable interpretation is delegated to consumers, which must skip unknown entry + // types instead of treating them as daemon-fatal. + let mut tables = Vec::new(); + for physaddr in rxsdt_physaddrs { + let physaddr: usize = match physaddr.try_into() { + Ok(physaddr) => physaddr, + Err(_) => { + log::warn!( + "acpid: skipping ACPI table at {:#X}: physical address out of range", + physaddr + ); + continue; + } + }; + + match Sdt::load_from_physical(physaddr) { + Ok(table) => { + log::debug!( + "acpid: accepted ACPI table {} at {:#X}", + String::from_utf8_lossy(&table.signature), + physaddr + ); + tables.push(table); + } + Err(TablePhysLoadError::Validity(InvalidSdtError::BadChecksum)) => { + log::warn!( + "acpid: skipping ACPI table at {:#X}: checksum validation failed", + physaddr + ); + } + Err(err) => { + log::warn!("acpid: skipping ACPI table at {:#X}: {}", physaddr, err); + } + } + } let mut this = Self { tables, dsdt: None, fadt: None, + shutdown_s5: RwLock::new(None), // Temporary values - aml_symbols: RwLock::new(AmlSymbols::new(ec)), + aml_symbols: RwLock::new(AmlSymbols::new(aml_bootstrap, ec)), next_ctx: RwLock::new(0), @@ -581,55 +791,26 @@ impl AcpiContext { let port = fadt.pm1a_control_block as u16; let mut val = 1 << 13; - let aml_symbols = self.aml_symbols.read(); - - let s5_aml_name = match acpi::aml::namespace::AmlName::from_str("\\_S5") { - Ok(aml_name) => aml_name, - Err(error) => { - log::error!("Could not build AmlName for \\_S5, {:?}", error); - return; - } - }; - - let s5 = match &aml_symbols.aml_context { - Some(aml_context) => match aml_context.namespace.lock().get(s5_aml_name) { - Ok(s5) => s5, - Err(error) => { - log::error!("Cannot set S-state, missing \\_S5, {:?}", error); - return; + if self.shutdown_s5.read().is_none() { + match self.cache_shutdown_s5_from_ready_aml("existing AML context") { + Ok(true) | Ok(false) => {} + Err(err) => { + log::warn!("acpid: _S5 was not ready at shutdown: {}", err); } - }, - None => { - log::error!("Cannot set S-state, AML context not initialized"); - return; } - }; - - let package = match s5.deref() { - acpi::aml::object::Object::Package(package) => package, - _ => { - log::error!("Cannot set S-state, \\_S5 is not a package"); - return; - } - }; + } - let slp_typa = match package[0].deref() { - acpi::aml::object::Object::Integer(i) => i.to_owned(), - _ => { - log::error!("typa is not an Integer"); - return; - } - }; - let slp_typb = match package[1].deref() { - acpi::aml::object::Object::Integer(i) => i.to_owned(), - _ => { - log::error!("typb is not an Integer"); - return; - } + let Some(sleep_types) = *self.shutdown_s5.read() else { + log::error!("Cannot set S-state, missing derived \\_S5 sleep types"); + return; }; - log::trace!("Shutdown SLP_TYPa {:X}, SLP_TYPb {:X}", slp_typa, slp_typb); - val |= slp_typa as u16; + log::trace!( + "Shutdown SLP_TYPa {:X}, SLP_TYPb {:X}", + sleep_types.slp_typa, + sleep_types.slp_typb + ); + val |= sleep_types.slp_typa; #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] { @@ -652,6 +833,86 @@ impl AcpiContext { core::hint::spin_loop(); } } + + pub fn prime_shutdown_s5(&self, pci_fd: Option<&libredox::Fd>, source: &'static str) { + match self.cache_shutdown_s5(pci_fd, source) { + Ok(()) => {} + Err(err) => { + log::warn!("acpid: unable to derive _S5 from {}: {}", source, err); + } + } + } + + fn cache_shutdown_s5( + &self, + pci_fd: Option<&libredox::Fd>, + source: &'static str, + ) -> Result<(), String> { + if self.shutdown_s5.read().is_some() { + return Ok(()); + } + + let mut aml_symbols = self.aml_symbols.write(); + let aml_context = aml_symbols + .aml_context_mut(pci_fd) + .map_err(|err| format!("AML not ready: {err}"))?; + let sleep_types = extract_s5_sleep_types(aml_context)?; + + *self.shutdown_s5.write() = Some(sleep_types); + log::info!("acpid: _S5 derived from {}", source); + Ok(()) + } + + fn cache_shutdown_s5_from_ready_aml(&self, source: &'static str) -> Result { + if self.shutdown_s5.read().is_some() { + return Ok(true); + } + + let aml_symbols = self.aml_symbols.read(); + let Some(aml_context) = aml_symbols.aml_context.as_ref() else { + return Ok(false); + }; + + let sleep_types = extract_s5_sleep_types(aml_context)?; + drop(aml_symbols); + + *self.shutdown_s5.write() = Some(sleep_types); + log::info!("acpid: _S5 derived from {}", source); + Ok(true) + } +} + +fn extract_s5_sleep_types( + aml_context: &Interpreter, +) -> Result { + let s5_aml_name = acpi::aml::namespace::AmlName::from_str("\\_S5") + .map_err(|error| format!("failed to build \\_S5 name: {error:?}"))?; + let s5 = aml_context + .namespace + .lock() + .get(s5_aml_name) + .map_err(|error| format!("missing \\_S5: {error:?}"))?; + let package = match s5.deref() { + acpi::aml::object::Object::Package(package) => package, + _ => return Err("\\_S5 is not a package".into()), + }; + + let slp_typa = extract_sleep_type(package.get(0), "SLP_TYPa")?; + let slp_typb = extract_sleep_type(package.get(1), "SLP_TYPb")?; + + Ok(SleepTypeData { slp_typa, slp_typb }) +} + +fn extract_sleep_type(value: Option<&WrappedObject>, label: &'static str) -> Result { + let Some(value) = value else { + return Err(format!("missing {label} in \\_S5 package")); + }; + + match value.deref() { + acpi::aml::object::Object::Integer(i) => u16::try_from(*i) + .map_err(|_| format!("{label} out of range for PM1 control register")), + _ => Err(format!("{label} is not an Integer")), + } } #[repr(C, packed)] @@ -760,45 +1021,66 @@ impl Deref for Fadt { type Target = FadtStruct; fn deref(&self) -> &Self::Target { - plain::from_bytes::(&self.0 .0) - .expect("expected FADT struct to already be validated in Deref impl") + match plain::from_bytes::(&self.0 .0) { + Ok(fadt) => fadt, + Err(plain::Error::TooShort) => unreachable!( + "Fadt::new validates the minimum FADT size before constructing Fadt" + ), + Err(plain::Error::BadAlignment) => unreachable!( + "plain::from_bytes reported bad alignment, but FadtStruct is #[repr(packed)]" + ), + } } } impl Fadt { pub fn new(sdt: Sdt) -> Option { - if sdt.signature != *b"FACP" || sdt.length() < mem::size_of::() { + if sdt.signature != *b"FACP" || sdt.length() < mem::size_of::() { return None; } Some(Fadt(sdt)) } pub fn init(context: &mut AcpiContext) { - let fadt_sdt = context - .take_single_sdt(*b"FACP") - .expect("expected ACPI to always have a FADT"); + // FADT policy: this table is mandatory for ACPI control services such as shutdown/reboot. + // If it is missing or malformed, acpid stays alive for diagnostics/raw tables but degrades + // into raw-table-only mode instead of crashing the boot. + let Some(fadt_sdt) = context.take_single_sdt(*b"FACP") else { + log::error!("acpid: missing FADT; booting without ACPI control services"); + return; + }; let fadt = match Fadt::new(fadt_sdt) { Some(fadt) => fadt, None => { - log::error!("Failed to find FADT"); + log::error!("acpid: corrupt FADT; booting without ACPI control services"); return; } }; let dsdt_ptr = match fadt.acpi_2_struct() { - Some(fadt2) => usize::try_from(fadt2.x_dsdt).unwrap_or_else(|_| { - usize::try_from(fadt.dsdt).expect("expected any given u32 to fit within usize") - }), - None => usize::try_from(fadt.dsdt).expect("expected any given u32 to fit within usize"), + Some(fadt2) if fadt2.x_dsdt != 0 => match usize::try_from(fadt2.x_dsdt) { + Ok(dsdt_ptr) => dsdt_ptr, + Err(_) => { + log::warn!( + "acpid: x_dsdt address out of range; falling back to 32-bit DSDT pointer" + ); + fadt.dsdt as usize + } + }, + _ => fadt.dsdt as usize, }; log::debug!("FACP at {:X}", { dsdt_ptr }); - let dsdt_sdt = match Sdt::load_from_physical(fadt.dsdt as usize) { + let dsdt_sdt = match Sdt::load_from_physical(dsdt_ptr) { Ok(dsdt) => dsdt, Err(error) => { - log::error!("Failed to load DSDT: {}", error); + log::error!( + "acpid: corrupt FADT/DSDT linkage (DSDT at {:#X}): booting without ACPI control services: {}", + dsdt_ptr, + error + ); return; } }; diff --git a/drivers/acpid/src/main.rs b/drivers/acpid/src/main.rs index 059254b3..25566553 100644 --- a/drivers/acpid/src/main.rs +++ b/drivers/acpid/src/main.rs @@ -3,6 +3,7 @@ use std::fs::File; use std::mem; use std::ops::ControlFlow; use std::os::unix::io::AsRawFd; +use std::process; use std::sync::Arc; use ::acpi::aml::op_region::{RegionHandler, RegionSpace}; @@ -28,94 +29,206 @@ fn daemon(daemon: daemon::Daemon) -> ! { log::info!("acpid start"); - let rxsdt_raw_data: Arc<[u8]> = std::fs::read("/scheme/kernel.acpi/rxsdt") - .expect("acpid: failed to read `/scheme/kernel.acpi/rxsdt`") - .into(); + let rxsdt_raw_data: Arc<[u8]> = match std::fs::read("/scheme/kernel.acpi/rxsdt") { + Ok(data) => data.into(), + Err(err) => { + log::error!("acpid: failed to read `/scheme/kernel.acpi/rxsdt`: {}", err); + process::exit(1); + } + }; if rxsdt_raw_data.is_empty() { log::info!("System doesn't use ACPI"); daemon.ready(); - std::process::exit(0); + process::exit(0); } - let sdt = self::acpi::Sdt::new(rxsdt_raw_data).expect("acpid: failed to parse [RX]SDT"); + // Root-table policy: if the kernel-provided [R|X]SDT is malformed, acpid cannot enumerate any + // firmware tables at all. That is fatal to this daemon, but it must fail with a logged exit + // rather than a panic on malformed firmware input. + let sdt = match self::acpi::Sdt::new(rxsdt_raw_data) { + Ok(sdt) => sdt, + Err(err) => { + log::error!("acpid: failed to parse kernel [R|X]SDT: {}", err); + process::exit(1); + } + }; + + // AML bootstrap contract: + // - preferred path: RSDP_ADDR[/RSDP_SIZE] inherited into acpid by the boot path, + // - x86 fallback: bounded BIOS RSDP search when that explicit handoff is absent or unusable. + let aml_bootstrap = match self::acpi::AmlBootstrap::from_env() { + Ok(bootstrap) => { + bootstrap.log_bootstrap(); + Some(bootstrap) + } + Err(err) => { + log::warn!( + "acpid: explicit AML bootstrap handoff unavailable ({}); trying x86 BIOS fallback", + err + ); - let mut thirty_two_bit; - let mut sixty_four_bit; + match self::acpi::AmlBootstrap::x86_bios_fallback() { + Ok(Some(bootstrap)) => { + bootstrap.log_bootstrap(); + Some(bootstrap) + } + Ok(None) => { + log::warn!( + "acpid: AML bootstrap unavailable; continuing without AML-backed ACPI services" + ); + None + } + Err(err) => { + log::warn!( + "acpid: x86 BIOS AML bootstrap fallback failed ({}); continuing without AML-backed ACPI services", + err + ); + None + } + } + } + }; - let physaddrs_iter = match &sdt.signature { + let physaddrs = match &sdt.signature { b"RSDT" => { - thirty_two_bit = sdt - .data() - .chunks(mem::size_of::()) - // TODO: With const generics, the compiler has some way of doing this for static sizes. - .map(|chunk| <[u8; mem::size_of::()]>::try_from(chunk).unwrap()) - .map(|chunk| u32::from_le_bytes(chunk)) - .map(u64::from); - - &mut thirty_two_bit as &mut dyn Iterator + let chunks = sdt.data().chunks_exact(mem::size_of::()); + if !chunks.remainder().is_empty() { + log::error!("acpid: malformed RSDT payload length {}", sdt.data().len()); + process::exit(1); + } + + chunks + .map(|chunk| { + let chunk = <[u8; mem::size_of::()]>::try_from(chunk) + .map_err(|_| "invalid 32-bit RSDT entry width")?; + Ok(u64::from(u32::from_le_bytes(chunk))) + }) + .collect::, &str>>() } b"XSDT" => { - sixty_four_bit = sdt - .data() - .chunks(mem::size_of::()) - .map(|chunk| <[u8; mem::size_of::()]>::try_from(chunk).unwrap()) - .map(|chunk| u64::from_le_bytes(chunk)); + let chunks = sdt.data().chunks_exact(mem::size_of::()); + if !chunks.remainder().is_empty() { + log::error!("acpid: malformed XSDT payload length {}", sdt.data().len()); + process::exit(1); + } - &mut sixty_four_bit as &mut dyn Iterator + chunks + .map(|chunk| { + let chunk = <[u8; mem::size_of::()]>::try_from(chunk) + .map_err(|_| "invalid 64-bit XSDT entry width")?; + Ok(u64::from_le_bytes(chunk)) + }) + .collect::, &str>>() + } + _ => { + log::error!( + "acpid: expected kernel root table to be RSDT or XSDT, got {}", + String::from_utf8_lossy(&sdt.signature) + ); + process::exit(1); + } + }; + let physaddrs = match physaddrs { + Ok(physaddrs) => physaddrs, + Err(err) => { + log::error!("acpid: failed to decode root table pointers: {}", err); + process::exit(1); } - _ => panic!("acpid: expected [RX]SDT from kernel to be either of those"), }; let region_handlers: Vec<(RegionSpace, Box)> = vec![ #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] (RegionSpace::EmbeddedControl, Box::new(ec::Ec::new())), ]; - let acpi_context = self::acpi::AcpiContext::init(physaddrs_iter, region_handlers); + let acpi_context = self::acpi::AcpiContext::init(physaddrs.into_iter(), aml_bootstrap, region_handlers); // TODO: I/O permission bitmap? #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] - common::acquire_port_io_rights().expect("acpid: failed to set I/O privilege level to Ring 3"); + if let Err(err) = common::acquire_port_io_rights() { + log::error!( + "acpid: failed to set I/O privilege level to Ring 3: {}", + err + ); + process::exit(1); + } - let shutdown_pipe = File::open("/scheme/kernel.acpi/kstop") - .expect("acpid: failed to open `/scheme/kernel.acpi/kstop`"); + let shutdown_pipe = match File::open("/scheme/kernel.acpi/kstop") { + Ok(file) => file, + Err(err) => { + log::error!("acpid: failed to open `/scheme/kernel.acpi/kstop`: {}", err); + process::exit(1); + } + }; - let mut event_queue = RawEventQueue::new().expect("acpid: failed to create event queue"); - let socket = Socket::nonblock().expect("acpid: failed to create disk scheme"); + let mut event_queue = match RawEventQueue::new() { + Ok(event_queue) => event_queue, + Err(err) => { + log::error!("acpid: failed to create event queue: {}", err); + process::exit(1); + } + }; + let socket = match Socket::nonblock() { + Ok(socket) => socket, + Err(err) => { + log::error!("acpid: failed to create acpi scheme socket: {}", err); + process::exit(1); + } + }; let mut scheme = self::scheme::AcpiScheme::new(&acpi_context, &socket); let mut handler = Blocking::new(&socket, 16); - event_queue - .subscribe(shutdown_pipe.as_raw_fd() as usize, 0, EventFlags::READ) - .expect("acpid: failed to register shutdown pipe for event queue"); - event_queue - .subscribe(socket.inner().raw(), 1, EventFlags::READ) - .expect("acpid: failed to register scheme socket for event queue"); + if let Err(err) = event_queue.subscribe(shutdown_pipe.as_raw_fd() as usize, 0, EventFlags::READ) + { + log::error!( + "acpid: failed to register shutdown pipe for event queue: {}", + err + ); + process::exit(1); + } + if let Err(err) = event_queue.subscribe(socket.inner().raw(), 1, EventFlags::READ) { + log::error!( + "acpid: failed to register scheme socket for event queue: {}", + err + ); + process::exit(1); + } - register_sync_scheme(&socket, "acpi", &mut scheme) - .expect("acpid: failed to register acpi scheme to namespace"); + if let Err(err) = register_sync_scheme(&socket, "acpi", &mut scheme) { + log::error!("acpid: failed to register acpi scheme to namespace: {}", err); + process::exit(1); + } daemon.ready(); - libredox::call::setrens(0, 0).expect("acpid: failed to enter null namespace"); + if let Err(err) = libredox::call::setrens(0, 0) { + log::error!("acpid: failed to enter null namespace: {}", err); + process::exit(1); + } let mut mounted = true; while mounted { - let Some(event) = event_queue - .next() - .transpose() - .expect("acpid: failed to read event file") - else { + let event = match event_queue.next().transpose() { + Ok(event) => event, + Err(err) => { + log::error!("acpid: failed to read event file: {}", err); + process::exit(1); + } + }; + let Some(event) = event else { break; }; if event.fd == socket.inner().raw() { loop { - match handler - .process_requests_nonblocking(&mut scheme) - .expect("acpid: failed to process requests") - { + match match handler.process_requests_nonblocking(&mut scheme) { + Ok(flow) => flow, + Err(err) => { + log::error!("acpid: failed to process requests: {}", err); + process::exit(1); + } + } { ControlFlow::Continue(()) => {} ControlFlow::Break(()) => break, } diff --git a/drivers/acpid/src/scheme.rs b/drivers/acpid/src/scheme.rs index 5a5040c3..6e57624a 100644 --- a/drivers/acpid/src/scheme.rs +++ b/drivers/acpid/src/scheme.rs @@ -474,6 +474,8 @@ impl SchemeSync for AcpiScheme<'_, '_> { return Err(Error::new(EINVAL)); } else { self.pci_fd = Some(new_fd); + self.ctx + .prime_shutdown_s5(self.pci_fd.as_ref(), "PCI-backed AML handoff"); } Ok(num_fds)