diff --git a/drivers/acpi-rs/src/aml/mod.rs b/drivers/acpi-rs/src/aml/mod.rs index d499baff82..73efe79748 100644 --- a/drivers/acpi-rs/src/aml/mod.rs +++ b/drivers/acpi-rs/src/aml/mod.rs @@ -241,10 +241,79 @@ where handlers.insert(space, handler); } + /// `_REG` opregion connect (ACPICA evrgnini.c): `\_SB._REG(space, 1)` per + /// installed handler + `._REG(space, 1)` per device holding an + /// OpRegion of that space. Firmware gates EC access behind this. Idempotent. + pub fn connect_op_regions(&self) { + let installed_spaces: Vec = + self.region_handlers.lock().keys().copied().collect(); + + for space in &installed_spaces { + let space_id = u8::from(*space) as u64; + if let Err(err) = self.evaluate_if_present( + AmlName::from_str("\\_SB._REG").unwrap(), + vec![Object::Integer(space_id).wrap(), Object::Integer(1).wrap()], + ) { + warn!("\\_SB._REG({:?}, connect) failed: {:?}", space, err); + } + } + + let mut reg_namespace = self.namespace.lock().clone(); + let _ = reg_namespace.traverse(|path, level| { + match level.kind { + NamespaceLevelKind::Device + | NamespaceLevelKind::Processor + | NamespaceLevelKind::ThermalZone + | NamespaceLevelKind::PowerResource => { + let mut device_spaces: Vec = level + .values + .values() + .filter_map(|(_, obj)| match &**obj { + Object::OpRegion(region) + if installed_spaces.contains(®ion.space) => + { + Some(region.space) + } + _ => None, + }) + .collect(); + device_spaces.sort(); + device_spaces.dedup(); + for space in device_spaces { + let space_id = u8::from(space) as u64; + if let Ok(reg_path) = + AmlName::from_str("_REG").unwrap().resolve(path) + { + let _ = self.evaluate_if_present( + reg_path, + vec![Object::Integer(space_id).wrap(), Object::Integer(1).wrap()], + ); + } + } + Ok(true) + } + _ => Ok(true), + } + }); + } + /// Initialize the namespace - this should be called after all tables have been loaded and /// operation region handlers registered. Specifically, it will call relevant `_STA`, `_INI`, /// and `_REG` methods. pub fn initialize_namespace(&self) { + /* + * This should match the initialization order of ACPICA and uACPI. + */ + if let Err(err) = self.evaluate_if_present(AmlName::from_str("\\_INI").unwrap(), vec![]) { + warn!("Invoking \\_INI failed: {:?}", err); + } + if let Err(err) = self.evaluate_if_present(AmlName::from_str("\\_SB._INI").unwrap(), vec![]) { + warn!("Invoking \\_SB._INI failed: {:?}", err); + } + + self.connect_op_regions(); + + /* * We can now initialize each device in the namespace. For each device, we evaluate `_STA`, * which indicates if the device is present and functional. If this method does not exist, * we assume the device should be initialized.