Ver código fonte

Lay groundwork for method invocation

Isaac Woods 5 anos atrás
pai
commit
849ba03136
2 arquivos alterados com 112 adições e 2 exclusões
  1. 51 1
      aml_parser/src/lib.rs
  2. 61 1
      aml_parser/src/value.rs

+ 51 - 1
aml_parser/src/lib.rs

@@ -55,6 +55,7 @@ use alloc::collections::BTreeMap;
 use log::error;
 use parser::Parser;
 use pkg_length::PkgLength;
+use value::Args;
 
 /// AML has a `RevisionOp` operator that returns the "AML interpreter revision". It's not clear
 /// what this is actually used for, but this is ours.
@@ -62,6 +63,9 @@ pub const AML_INTERPRETER_REVISION: u64 = 0;
 
 #[derive(Clone, Copy, Debug, PartialEq, Eq)]
 pub enum AmlError {
+    /*
+     * Errors produced parsing the AML stream.
+     */
     UnexpectedEndOfStream,
     UnexpectedByte(u8),
     InvalidNameSeg([u8; 4]),
@@ -73,17 +77,52 @@ pub enum AmlError {
     /// Error produced when none of the parsers in a `choice!` could parse the next part of the
     /// stream.
     NoParsersCouldParse,
+
+    /*
+     * Errors produced querying the namespace.
+     */
+    /// Produced when a path is given that does not point to an object in the AML namespace.
+    ObjectDoesNotExist,
 }
 
 #[derive(Debug)]
 pub struct AmlContext {
     pub namespace: BTreeMap<AmlName, AmlValue>,
     current_scope: AmlName,
+
+    /*
+     * AML local variables. These are used when we invoke a control method. A `None` value
+     * represents a null AML object.
+     */
+    local_0: Option<AmlValue>,
+    local_1: Option<AmlValue>,
+    local_2: Option<AmlValue>,
+    local_3: Option<AmlValue>,
+    local_4: Option<AmlValue>,
+    local_5: Option<AmlValue>,
+    local_6: Option<AmlValue>,
+    local_7: Option<AmlValue>,
+
+    /// If we're currently invoking a control method, this stores the arguments that were passed to
+    /// it. It's `None` if we aren't invoking a method.
+    current_args: Option<Args>,
 }
 
 impl AmlContext {
     pub fn new() -> AmlContext {
-        AmlContext { namespace: BTreeMap::new(), current_scope: AmlName::root() }
+        AmlContext {
+            namespace: BTreeMap::new(),
+            current_scope: AmlName::root(),
+            local_0: None,
+            local_1: None,
+            local_2: None,
+            local_3: None,
+            local_4: None,
+            local_5: None,
+            local_6: None,
+            local_7: None,
+            current_args: None,
+        }
     }
 
     pub fn parse_table(&mut self, stream: &[u8]) -> Result<(), AmlError> {
@@ -101,6 +140,17 @@ impl AmlContext {
         }
     }
 
+    /// Invoke a method referred to by its path in the namespace, with the given arguments.
+    pub fn invoke_method(&mut self, path: &AmlName, args: Args) -> Result<AmlValue, AmlError> {
+        let method = match self.lookup(path) {
+            // Unfortunately, we have to clone the method object to end the borrow on the context.
+            Some(object) => object.clone(),
+            None => return Err(AmlError::ObjectDoesNotExist),
+        };
+
+        method.invoke(self, args)
+    }
+
     /// Resolves a given path relative to the current scope (if the given path is not absolute).
     /// The returned path can be used to index the namespace.
     pub fn resolve_path(&self, path: &AmlName) -> AmlName {

+ 61 - 1
aml_parser/src/value.rs

@@ -1,6 +1,14 @@
-use crate::{name_object::AmlName, AmlError};
+use crate::{
+    name_object::AmlName,
+    parser::Parser,
+    pkg_length::PkgLength,
+    term_object::term_list,
+    AmlContext,
+    AmlError,
+};
 use alloc::{boxed::Box, string::String, vec::Vec};
 use bit_field::BitField;
+use log::info;
 
 #[derive(Clone, Copy, PartialEq, Eq, Debug)]
 pub enum RegionSpace {
@@ -132,4 +140,56 @@ impl AmlValue {
             _ => Err(AmlError::IncompatibleValueConversion),
         }
     }
+
+    /// If this value is a control method, invoke it. Returns `AmlError::IncompatibleValueConversion` if this
+    /// is not a method.
+    pub fn invoke(&self, context: &mut AmlContext, args: Args) -> Result<AmlValue, AmlError> {
+        if let AmlValue::Method { flags, ref code } = self {
+            /*
+             * First, set up the state we expect to enter the method with, but clearing local
+             * variables to "null" and setting the arguments.
+             */
+            context.current_args = Some(args);
+            context.local_0 = None;
+            context.local_1 = None;
+            context.local_2 = None;
+            context.local_3 = None;
+            context.local_4 = None;
+            context.local_5 = None;
+            context.local_6 = None;
+            context.local_7 = None;
+
+            let result = term_list(PkgLength::from_raw_length(code, code.len() as u32)).parse(code, context);
+
+            /*
+             * Now clear the state.
+             */
+            context.current_args = None;
+            context.local_0 = None;
+            context.local_1 = None;
+            context.local_2 = None;
+            context.local_3 = None;
+            context.local_4 = None;
+            context.local_5 = None;
+            context.local_6 = None;
+            context.local_7 = None;
+
+            // TODO: return the real return value
+            Ok(AmlValue::Integer(0))
+        } else {
+            Err(AmlError::IncompatibleValueConversion)
+        }
+    }
+}
+
+/// A control method can take up to 7 arguments, each of which can be an `AmlValue`.
+#[derive(Clone, Debug, Default)]
+pub struct Args {
+    arg_0: Option<AmlValue>,
+    arg_1: Option<AmlValue>,
+    arg_2: Option<AmlValue>,
+    arg_3: Option<AmlValue>,
+    arg_4: Option<AmlValue>,
+    arg_5: Option<AmlValue>,
+    arg_6: Option<AmlValue>,
 }