Browse Source

Create and remove levels during method invocations

Isaac Woods 4 years ago
parent
commit
1496401d49
2 changed files with 36 additions and 2 deletions
  1. 15 2
      aml/src/lib.rs
  2. 21 0
      aml/src/namespace.rs

+ 15 - 2
aml/src/lib.rs

@@ -62,6 +62,7 @@ pub use crate::{
 
 use log::error;
 use misc::{ArgNum, LocalNum};
+use namespace::LevelType;
 use parser::Parser;
 use pkg_length::PkgLength;
 use term_object::term_list;
@@ -128,8 +129,6 @@ impl AmlContext {
     ///     - Processors are expected to be found in `\_PR`, instead of `\_SB`
     ///     - Thermal zones are expected to be found in `\_TZ`, instead of `\_SB`
     pub fn new(legacy_mode: bool, debug_verbosity: DebugVerbosity) -> AmlContext {
-        use namespace::LevelType;
-
         let mut context = AmlContext {
             legacy_mode,
             namespace: Namespace::new(),
@@ -195,6 +194,11 @@ impl AmlContext {
             self.local_6 = None;
             self.local_7 = None;
 
+            /*
+             * Create a namespace level to store local objects created by the invocation.
+             */
+            self.namespace.add_level(path.clone(), LevelType::MethodLocals)?;
+
             log::trace!("Invoking method with {} arguments, code: {:x?}", flags.arg_count(), code);
             let return_value =
                 match term_list(PkgLength::from_raw_length(&code, code.len() as u32)).parse(&code, self) {
@@ -207,6 +211,14 @@ impl AmlContext {
                     }
                 };
 
+            /*
+             * Locally-created objects should be destroyed on method exit (see §5.5.2.3 of the ACPI spec). We do
+             * this by simply removing the method's local object layer.
+             */
+            // TODO: this should also remove objects created by the method outside the method's scope, if they
+            // weren't statically created. This is harder.
+            self.namespace.remove_level(path.clone())?;
+
             /*
              * Now clear the state.
              */
@@ -286,6 +298,7 @@ pub enum AmlError {
     ValueDoesNotExist(AmlName),
     /// Produced when two values with the same name are added to the namespace.
     NameCollision(AmlName),
+    TriedToRemoveRootNamespace,
 
     /*
      * Errors produced executing control methods.

+ 21 - 0
aml/src/namespace.rs

@@ -30,6 +30,9 @@ pub enum LevelType {
     /// A legacy `Processor` object's sub-objects are stored in a level of this type. Modern tables define
     /// processors as `Device`s.
     Processor,
+    /// A level of this type is created at the same path as the name of a method when it is invoked. It can be
+    /// used by the method to store local variables.
+    MethodLocals,
 }
 
 pub struct NamespaceLevel {
@@ -102,6 +105,22 @@ impl Namespace {
         Ok(())
     }
 
+    pub fn remove_level(&mut self, path: AmlName) -> Result<(), AmlError> {
+        assert!(path.is_absolute());
+        let path = path.normalize()?;
+
+        if path != AmlName::root() {
+            let (level, last_seg) = self.get_level_for_path_mut(&path)?;
+
+            match level.children.remove(&last_seg) {
+                Some(_) => Ok(()),
+                None => Err(AmlError::LevelDoesNotExist(path)),
+            }
+        } else {
+            Err(AmlError::TriedToRemoveRootNamespace)
+        }
+    }
+
     /// Add a value to the namespace at the given path, which must be a normalized, absolute AML
     /// name. If you want to add at a path relative to a given scope, use `add_at_resolved_path`
     /// instead.
@@ -213,6 +232,8 @@ impl Namespace {
         Ok((current_level, last_seg))
     }
 
+    /// Split an absolute path into a bunch of level segments (used to traverse the level data structure), and a
+    /// last segment to index into that level. This must not be called on `\\`.
     fn get_level_for_path_mut(&mut self, path: &AmlName) -> Result<(&mut NamespaceLevel, NameSeg), AmlError> {
         let (last_seg, levels) = path.0[1..].split_last().unwrap();
         let last_seg = last_seg.as_segment().unwrap();