Browse Source

Merge pull request #73 from IsaacWoods/master

Initialize objects and support all _STAs and _INIs on QEMU tables
Isaac Woods 4 years ago
parent
commit
a322e83a92

+ 1 - 1
acpi/src/lib.rs

@@ -22,7 +22,7 @@
 //! gathered from the static tables, and can be queried to set up hardware etc.
 
 #![no_std]
-#![feature(const_generics)]
+#![feature(const_generics, unsafe_block_in_unsafe_fn)]
 
 extern crate alloc;
 #[cfg_attr(test, macro_use)]

+ 445 - 88
aml/src/lib.rs

@@ -60,8 +60,11 @@ pub use crate::{
     value::AmlValue,
 };
 
+use alloc::boxed::Box;
+use core::mem;
 use log::error;
 use misc::{ArgNum, LocalNum};
+use name_object::Target;
 use namespace::LevelType;
 use parser::Parser;
 use pkg_length::PkgLength;
@@ -86,12 +89,7 @@ pub enum DebugVerbosity {
     All,
 }
 
-#[derive(Debug)]
-pub struct AmlContext {
-    legacy_mode: bool,
-
-    pub namespace: Namespace,
-
+struct MethodContext {
     /*
      * AML local variables. These are used when we invoke a control method. A `None` value
      * represents a null AML object.
@@ -107,7 +105,33 @@ pub struct AmlContext {
 
     /// 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>,
+    args: Args,
+}
+
+impl MethodContext {
+    fn new(args: Args) -> MethodContext {
+        MethodContext {
+            local_0: None,
+            local_1: None,
+            local_2: None,
+            local_3: None,
+            local_4: None,
+            local_5: None,
+            local_6: None,
+            local_7: None,
+            args,
+        }
+    }
+}
+
+pub struct AmlContext {
+    /// The `Handler` passed from the library user. This is stored as a boxed trait object simply to avoid having
+    /// to add a lifetime and type parameter to `AmlContext`, as they would massively complicate the parser types.
+    handler: Box<dyn Handler>,
+    legacy_mode: bool,
+
+    pub namespace: Namespace,
+    method_context: Option<MethodContext>,
 
     /*
      * These track the state of the context while it's parsing an AML table.
@@ -128,19 +152,12 @@ impl AmlContext {
     ///     - Processors are expected to be defined with `DefProcessor`, instead of `DefDevice`
     ///     - 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 {
+    pub fn new(handler: Box<dyn Handler>, legacy_mode: bool, debug_verbosity: DebugVerbosity) -> AmlContext {
         let mut context = AmlContext {
+            handler,
             legacy_mode,
             namespace: Namespace::new(),
-            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,
+            method_context: None,
 
             current_scope: AmlName::root(),
             scope_indent: 0,
@@ -176,89 +193,417 @@ 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> {
-        if let AmlValue::Method { flags, code } = self.namespace.get_by_path(path)?.clone() {
-            /*
-             * First, set up the state we expect to enter the method with, but clearing local
-             * variables to "null" and setting the arguments.
-             */
-            self.current_scope = path.clone();
-            self.current_args = Some(args);
-            self.local_0 = None;
-            self.local_1 = None;
-            self.local_2 = None;
-            self.local_3 = None;
-            self.local_4 = None;
-            self.local_5 = None;
-            self.local_6 = None;
-            self.local_7 = None;
+        match self.namespace.get_by_path(path)?.clone() {
+            AmlValue::Method { flags, code } => {
+                /*
+                 * First, set up the state we expect to enter the method with, but clearing local
+                 * variables to "null" and setting the arguments. Save the current method state and scope, so if we're
+                 * already executing another control method, we resume into it correctly.
+                 */
+                let old_context = mem::replace(&mut self.method_context, Some(MethodContext::new(args)));
+                let old_scope = mem::replace(&mut self.current_scope, path.clone());
+
+                /*
+                 * Create a namespace level to store local objects created by the invocation.
+                 */
+                self.namespace.add_level(path.clone(), LevelType::MethodLocals)?;
+
+                let return_value =
+                    match term_list(PkgLength::from_raw_length(&code, code.len() as u32)).parse(&code, self) {
+                        // If the method doesn't return a value, we implicitly return `0`
+                        Ok(_) => Ok(AmlValue::Integer(0)),
+                        Err((_, _, AmlError::Return(result))) => Ok(result),
+                        Err((_, _, err)) => {
+                            error!("Failed to execute control method: {:?}", err);
+                            Err(err)
+                        }
+                    };
+
+                /*
+                 * 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())?;
+
+                /*
+                 * Restore the old state.
+                 */
+                self.method_context = old_context;
+                self.current_scope = old_scope;
+
+                return_value
+            }
 
             /*
-             * Create a namespace level to store local objects created by the invocation.
+             * AML can encode methods that don't require any computation simply as the value that would otherwise be
+             * returned (e.g. a `_STA` object simply being an `AmlValue::Integer`, instead of a method that just
+             * returns an integer).
              */
-            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) {
-                    // If the method doesn't return a value, we implicitly return `0`
-                    Ok(_) => Ok(AmlValue::Integer(0)),
-                    Err((_, _, AmlError::Return(result))) => Ok(result),
-                    Err((_, _, err)) => {
-                        error!("Failed to execute control method: {:?}", err);
-                        Err(err)
-                    }
-                };
+            value => Ok(value),
+        }
+    }
 
-            /*
-             * 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())?;
+    pub fn initialize_objects(&mut self) -> Result<(), AmlError> {
+        use name_object::NameSeg;
+        use namespace::NamespaceLevel;
+        use value::StatusObject;
 
-            /*
-             * Now clear the state.
-             */
-            self.current_args = None;
-            self.local_0 = None;
-            self.local_1 = None;
-            self.local_2 = None;
-            self.local_3 = None;
-            self.local_4 = None;
-            self.local_5 = None;
-            self.local_6 = None;
-            self.local_7 = None;
-
-            return_value
-        } else {
-            Err(AmlError::IncompatibleValueConversion)
+        /*
+         * If `\_SB._INI` exists, we unconditionally execute it at the beginning of device initialization.
+         */
+        match self.invoke_method(&AmlName::from_str("\\_SB._INI").unwrap(), Args::default()) {
+            Ok(_) => (),
+            Err(AmlError::ValueDoesNotExist(_)) => (),
+            Err(err) => return Err(err),
         }
+
+        /*
+         * Next, we traverse the namespace, looking for devices.
+         *
+         * XXX: we clone the namespace here, which obviously drives up heap burden quite a bit (not as much as you
+         * might first expect though - we're only duplicating the level data structure, not all the objects). The
+         * issue here is that we need to access the namespace during traversal (e.g. to invoke a method), which the
+         * borrow checker really doesn't like. A better solution could be a iterator-like traversal system that
+         * keeps track of the namespace without keeping it borrowed. This works for now.
+         */
+        self.namespace.clone().traverse(|path, level: &NamespaceLevel| match level.typ {
+            LevelType::Device => {
+                let status = if level.values.contains_key(&NameSeg::from_str("_STA").unwrap()) {
+                    self.invoke_method(&AmlName::from_str("_STA").unwrap().resolve(&path)?, Args::default())?
+                        .as_status()?
+                } else {
+                    StatusObject::default()
+                };
+
+                /*
+                 * If the device is present and has an `_INI` method, invoke it.
+                 */
+                if status.present && level.values.contains_key(&NameSeg::from_str("_INI").unwrap()) {
+                    log::info!("Invoking _INI at level: {}", path);
+                    self.invoke_method(&AmlName::from_str("_INI").unwrap().resolve(&path)?, Args::default())?;
+                }
+
+                /*
+                 * We traverse the children of this device if it's present, or isn't present but is functional.
+                 */
+                Ok(status.present || status.functional)
+            }
+
+            LevelType::Scope => Ok(true),
+
+            // TODO: can either of these contain devices?
+            LevelType::Processor => Ok(false),
+            LevelType::MethodLocals => Ok(false),
+        })?;
+
+        Ok(())
     }
 
+    /// Get the value of an argument by its argument number. Can only be executed from inside a control method.
     pub(crate) fn current_arg(&self, arg: ArgNum) -> Result<&AmlValue, AmlError> {
-        self.current_args.as_ref().ok_or(AmlError::InvalidArgumentAccess(0xff))?.arg(arg)
+        self.method_context.as_ref().ok_or(AmlError::NotExecutingControlMethod)?.args.arg(arg)
     }
 
-    /// Get the current value of a local by its local number.
-    ///
-    /// ### Panics
-    /// Panics if an invalid local number is passed (valid local numbers are `0..=7`)
+    /// Get the current value of a local by its local number. Can only be executed from inside a control method.
     pub(crate) fn local(&self, local: LocalNum) -> Result<&AmlValue, AmlError> {
+        if let None = self.method_context {
+            return Err(AmlError::NotExecutingControlMethod);
+        }
+
         match local {
-            0 => self.local_0.as_ref().ok_or(AmlError::InvalidLocalAccess(local)),
-            1 => self.local_1.as_ref().ok_or(AmlError::InvalidLocalAccess(local)),
-            2 => self.local_2.as_ref().ok_or(AmlError::InvalidLocalAccess(local)),
-            3 => self.local_3.as_ref().ok_or(AmlError::InvalidLocalAccess(local)),
-            4 => self.local_4.as_ref().ok_or(AmlError::InvalidLocalAccess(local)),
-            5 => self.local_5.as_ref().ok_or(AmlError::InvalidLocalAccess(local)),
-            6 => self.local_6.as_ref().ok_or(AmlError::InvalidLocalAccess(local)),
-            7 => self.local_7.as_ref().ok_or(AmlError::InvalidLocalAccess(local)),
-            _ => panic!("Invalid local number: {}", local),
+            0 => self.method_context.as_ref().unwrap().local_0.as_ref().ok_or(AmlError::InvalidLocalAccess(local)),
+            1 => self.method_context.as_ref().unwrap().local_1.as_ref().ok_or(AmlError::InvalidLocalAccess(local)),
+            2 => self.method_context.as_ref().unwrap().local_2.as_ref().ok_or(AmlError::InvalidLocalAccess(local)),
+            3 => self.method_context.as_ref().unwrap().local_3.as_ref().ok_or(AmlError::InvalidLocalAccess(local)),
+            4 => self.method_context.as_ref().unwrap().local_4.as_ref().ok_or(AmlError::InvalidLocalAccess(local)),
+            5 => self.method_context.as_ref().unwrap().local_5.as_ref().ok_or(AmlError::InvalidLocalAccess(local)),
+            6 => self.method_context.as_ref().unwrap().local_6.as_ref().ok_or(AmlError::InvalidLocalAccess(local)),
+            7 => self.method_context.as_ref().unwrap().local_7.as_ref().ok_or(AmlError::InvalidLocalAccess(local)),
+            _ => Err(AmlError::InvalidLocalAccess(local)),
+        }
+    }
+
+    /// Perform a store into a `Target`. This returns a value read out of the target, if neccessary, as values can
+    /// be altered during a store in some circumstances. If the target is a `Name`, this also performs required
+    /// implicit conversions. Stores to other targets are semantically equivalent to a `CopyObject`.
+    pub(crate) fn store(&mut self, target: Target, value: AmlValue) -> Result<AmlValue, AmlError> {
+        use value::AmlType;
+
+        match target {
+            Target::Name(ref path) => {
+                let (_, handle) = self.namespace.search(path, &self.current_scope)?;
+                let converted_object = match self.namespace.get(handle).unwrap().type_of() {
+                    /*
+                     * We special-case FieldUnits here because we don't have the needed information to actually do
+                     * the write if we try and convert using `as_type`.
+                     */
+                    AmlType::FieldUnit => {
+                        let mut field = self.namespace.get(handle).unwrap().clone();
+                        field.write_field(value, self)?;
+                        field.read_field(self)?
+                    }
+                    typ => value.as_type(typ, self)?,
+                };
+
+                *self.namespace.get_mut(handle)? = converted_object;
+                Ok(self.namespace.get(handle)?.clone())
+            }
+
+            Target::Debug => {
+                // TODO
+                unimplemented!()
+            }
+
+            Target::Arg(arg_num) => {
+                if let None = self.method_context {
+                    return Err(AmlError::NotExecutingControlMethod);
+                }
+
+                match arg_num {
+                    1 => self.method_context.as_mut().unwrap().args.arg_1 = Some(value.clone()),
+                    2 => self.method_context.as_mut().unwrap().args.arg_2 = Some(value.clone()),
+                    3 => self.method_context.as_mut().unwrap().args.arg_3 = Some(value.clone()),
+                    4 => self.method_context.as_mut().unwrap().args.arg_4 = Some(value.clone()),
+                    5 => self.method_context.as_mut().unwrap().args.arg_5 = Some(value.clone()),
+                    6 => self.method_context.as_mut().unwrap().args.arg_6 = Some(value.clone()),
+                    _ => return Err(AmlError::InvalidArgAccess(arg_num)),
+                }
+                Ok(value)
+            }
+
+            Target::Local(local_num) => {
+                if let None = self.method_context {
+                    return Err(AmlError::NotExecutingControlMethod);
+                }
+
+                match local_num {
+                    0 => self.method_context.as_mut().unwrap().local_0 = Some(value.clone()),
+                    1 => self.method_context.as_mut().unwrap().local_1 = Some(value.clone()),
+                    2 => self.method_context.as_mut().unwrap().local_2 = Some(value.clone()),
+                    3 => self.method_context.as_mut().unwrap().local_3 = Some(value.clone()),
+                    4 => self.method_context.as_mut().unwrap().local_4 = Some(value.clone()),
+                    5 => self.method_context.as_mut().unwrap().local_5 = Some(value.clone()),
+                    6 => self.method_context.as_mut().unwrap().local_6 = Some(value.clone()),
+                    7 => self.method_context.as_mut().unwrap().local_7 = Some(value.clone()),
+                    _ => return Err(AmlError::InvalidLocalAccess(local_num)),
+                }
+                Ok(value)
+            }
+
+            Target::Null => Ok(value),
         }
     }
+
+    /// Read from an operation-region, performing only standard-sized reads (supported powers-of-2 only. If a field
+    /// is not one of these sizes, it may need to be masked, or multiple reads may need to be performed).
+    pub(crate) fn read_region(&self, region_handle: AmlHandle, offset: u64, length: u64) -> Result<u64, AmlError> {
+        use bit_field::BitField;
+        use core::convert::TryInto;
+        use value::RegionSpace;
+
+        let (region_space, region_base, region_length, parent_device) = {
+            if let AmlValue::OpRegion { region, offset, length, parent_device } =
+                self.namespace.get(region_handle)?
+            {
+                (region, offset, length, parent_device)
+            } else {
+                return Err(AmlError::FieldRegionIsNotOpRegion);
+            }
+        };
+
+        match region_space {
+            RegionSpace::SystemMemory => {
+                let address = (region_base + offset).try_into().map_err(|_| AmlError::FieldInvalidAddress)?;
+                match length {
+                    8 => Ok(self.handler.read_u8(address) as u64),
+                    16 => Ok(self.handler.read_u16(address) as u64),
+                    32 => Ok(self.handler.read_u32(address) as u64),
+                    64 => Ok(self.handler.read_u64(address)),
+                    _ => Err(AmlError::FieldInvalidAccessSize),
+                }
+            }
+
+            RegionSpace::SystemIo => {
+                let port = (region_base + offset).try_into().map_err(|_| AmlError::FieldInvalidAddress)?;
+                match length {
+                    8 => Ok(self.handler.read_io_u8(port) as u64),
+                    16 => Ok(self.handler.read_io_u16(port) as u64),
+                    32 => Ok(self.handler.read_io_u32(port) as u64),
+                    _ => Err(AmlError::FieldInvalidAccessSize),
+                }
+            }
+
+            RegionSpace::PciConfig => {
+                /*
+                 * First, we need to get some extra information out of objects in the parent object. Both
+                 * `_SEG` and `_BBN` seem optional, with defaults that line up with legacy PCI implementations
+                 * (e.g. systems with a single segment group and a single root, respectively).
+                 */
+                let parent_device = parent_device.as_ref().unwrap();
+                let seg = match self.namespace.search(&AmlName::from_str("_SEG").unwrap(), parent_device) {
+                    Ok((_, handle)) => self
+                        .namespace
+                        .get(handle)?
+                        .as_integer(self)?
+                        .try_into()
+                        .map_err(|_| AmlError::FieldInvalidAddress)?,
+                    Err(AmlError::ValueDoesNotExist(_)) => 0,
+                    Err(err) => return Err(err),
+                };
+                let bbn = match self.namespace.search(&AmlName::from_str("_BBN").unwrap(), parent_device) {
+                    Ok((_, handle)) => self
+                        .namespace
+                        .get(handle)?
+                        .as_integer(self)?
+                        .try_into()
+                        .map_err(|_| AmlError::FieldInvalidAddress)?,
+                    Err(AmlError::ValueDoesNotExist(_)) => 0,
+                    Err(err) => return Err(err),
+                };
+                let adr = {
+                    let (_, handle) = self.namespace.search(&AmlName::from_str("_ADR").unwrap(), parent_device)?;
+                    self.namespace.get(handle)?.as_integer(self)?
+                };
+
+                let device = adr.get_bits(16..24) as u8;
+                let function = adr.get_bits(0..8) as u8;
+                let offset = (region_base + offset).try_into().map_err(|_| AmlError::FieldInvalidAddress)?;
+
+                match length {
+                    8 => Ok(self.handler.read_pci_u8(seg, bbn, device, function, offset) as u64),
+                    16 => Ok(self.handler.read_pci_u16(seg, bbn, device, function, offset) as u64),
+                    32 => Ok(self.handler.read_pci_u32(seg, bbn, device, function, offset) as u64),
+                    _ => Err(AmlError::FieldInvalidAccessSize),
+                }
+            }
+
+            // TODO
+            _ => unimplemented!(),
+        }
+    }
+
+    pub(crate) fn write_region(
+        &mut self,
+        region_handle: AmlHandle,
+        offset: u64,
+        length: u64,
+        value: u64,
+    ) -> Result<(), AmlError> {
+        use bit_field::BitField;
+        use core::convert::TryInto;
+        use value::RegionSpace;
+
+        let (region_space, region_base, region_length, parent_device) = {
+            if let AmlValue::OpRegion { region, offset, length, parent_device } =
+                self.namespace.get(region_handle)?
+            {
+                (region, offset, length, parent_device)
+            } else {
+                return Err(AmlError::FieldRegionIsNotOpRegion);
+            }
+        };
+
+        match region_space {
+            RegionSpace::SystemMemory => {
+                let address = (region_base + offset).try_into().map_err(|_| AmlError::FieldInvalidAddress)?;
+                match length {
+                    8 => Ok(self.handler.write_u8(address, value as u8)),
+                    16 => Ok(self.handler.write_u16(address, value as u16)),
+                    32 => Ok(self.handler.write_u32(address, value as u32)),
+                    64 => Ok(self.handler.write_u64(address, value)),
+                    _ => Err(AmlError::FieldInvalidAccessSize),
+                }
+            }
+
+            RegionSpace::SystemIo => {
+                let port = (region_base + offset).try_into().map_err(|_| AmlError::FieldInvalidAddress)?;
+                match length {
+                    8 => Ok(self.handler.write_io_u8(port, value as u8)),
+                    16 => Ok(self.handler.write_io_u16(port, value as u16)),
+                    32 => Ok(self.handler.write_io_u32(port, value as u32)),
+                    _ => Err(AmlError::FieldInvalidAccessSize),
+                }
+            }
+
+            RegionSpace::PciConfig => {
+                /*
+                 * First, we need to get some extra information out of objects in the parent object. Both
+                 * `_SEG` and `_BBN` seem optional, with defaults that line up with legacy PCI implementations
+                 * (e.g. systems with a single segment group and a single root, respectively).
+                 */
+                let parent_device = parent_device.as_ref().unwrap();
+                let seg = match self.namespace.search(&AmlName::from_str("_SEG").unwrap(), parent_device) {
+                    Ok((_, handle)) => self
+                        .namespace
+                        .get(handle)?
+                        .as_integer(self)?
+                        .try_into()
+                        .map_err(|_| AmlError::FieldInvalidAddress)?,
+                    Err(AmlError::ValueDoesNotExist(_)) => 0,
+                    Err(err) => return Err(err),
+                };
+                let bbn = match self.namespace.search(&AmlName::from_str("_BBN").unwrap(), parent_device) {
+                    Ok((_, handle)) => self
+                        .namespace
+                        .get(handle)?
+                        .as_integer(self)?
+                        .try_into()
+                        .map_err(|_| AmlError::FieldInvalidAddress)?,
+                    Err(AmlError::ValueDoesNotExist(_)) => 0,
+                    Err(err) => return Err(err),
+                };
+                let adr = {
+                    let (_, handle) = self.namespace.search(&AmlName::from_str("_ADR").unwrap(), parent_device)?;
+                    self.namespace.get(handle)?.as_integer(self)?
+                };
+
+                let device = adr.get_bits(16..24) as u8;
+                let function = adr.get_bits(0..8) as u8;
+                let offset = (region_base + offset).try_into().map_err(|_| AmlError::FieldInvalidAddress)?;
+
+                match length {
+                    8 => Ok(self.handler.write_pci_u8(seg, bbn, device, function, offset, value as u8)),
+                    16 => Ok(self.handler.write_pci_u16(seg, bbn, device, function, offset, value as u16)),
+                    32 => Ok(self.handler.write_pci_u32(seg, bbn, device, function, offset, value as u32)),
+                    _ => Err(AmlError::FieldInvalidAccessSize),
+                }
+            }
+
+            // TODO
+            _ => unimplemented!(),
+        }
+    }
+}
+
+pub trait Handler {
+    fn read_u8(&self, address: usize) -> u8;
+    fn read_u16(&self, address: usize) -> u16;
+    fn read_u32(&self, address: usize) -> u32;
+    fn read_u64(&self, address: usize) -> u64;
+
+    fn write_u8(&mut self, address: usize, value: u8);
+    fn write_u16(&mut self, address: usize, value: u16);
+    fn write_u32(&mut self, address: usize, value: u32);
+    fn write_u64(&mut self, address: usize, value: u64);
+
+    fn read_io_u8(&self, port: u16) -> u8;
+    fn read_io_u16(&self, port: u16) -> u16;
+    fn read_io_u32(&self, port: u16) -> u32;
+
+    fn write_io_u8(&self, port: u16, value: u8);
+    fn write_io_u16(&self, port: u16, value: u16);
+    fn write_io_u32(&self, port: u16, value: u32);
+
+    fn read_pci_u8(&self, segment: u16, bus: u8, device: u8, function: u8, offset: u16) -> u8;
+    fn read_pci_u16(&self, segment: u16, bus: u8, device: u8, function: u8, offset: u16) -> u16;
+    fn read_pci_u32(&self, segment: u16, bus: u8, device: u8, function: u8, offset: u16) -> u32;
+
+    fn write_pci_u8(&self, segment: u16, bus: u8, device: u8, function: u8, offset: u16, value: u8);
+    fn write_pci_u16(&self, segment: u16, bus: u8, device: u8, function: u8, offset: u16, value: u16);
+    fn write_pci_u32(&self, segment: u16, bus: u8, device: u8, function: u8, offset: u16, value: u32);
 }
 
 #[derive(Clone, Debug, PartialEq, Eq)]
@@ -303,11 +648,13 @@ pub enum AmlError {
     /*
      * Errors produced executing control methods.
      */
+    /// Produced when AML tries to do something only possible in a control method (e.g. read from an argument)
+    /// when there's no control method executing.
+    NotExecutingControlMethod,
     /// Produced when a method accesses an argument it does not have (e.g. a method that takes 2
-    /// arguments accesses `Arg4`). The inner value is the number of the argument accessed. If any
-    /// arguments are accessed when a method is not being executed, this error is produced with an
-    /// argument number of `0xff`.
-    InvalidArgumentAccess(ArgNum),
+    /// arguments accesses `Arg4`). The inner value is the number of the argument accessed.
+    InvalidArgAccess(ArgNum),
+    /// Produced when a method accesses a local that it has not stored into.
     InvalidLocalAccess(LocalNum),
     /// This is not a real error, but is used to propagate return values from within the deep
     /// parsing call-stack. It should only be emitted when parsing a `DefReturn`. We use the
@@ -330,4 +677,14 @@ pub enum AmlError {
      */
     ReservedResourceType,
     ResourceDescriptorTooShort,
+
+    /*
+     * Errors produced working with AML values.
+     */
+    InvalidStatusObject,
+    InvalidShiftLeft,
+    InvalidShiftRight,
+    FieldRegionIsNotOpRegion,
+    FieldInvalidAddress,
+    FieldInvalidAccessSize,
 }

+ 22 - 6
aml/src/name_object.rs

@@ -13,12 +13,28 @@ use core::{fmt, str};
 /// Produced by the `Target`, `SimpleName`, and `SuperName` parsers
 #[derive(Clone, Debug)]
 pub enum Target {
+    Null,
     Name(AmlName),
     Debug,
     Arg(ArgNum),
     Local(LocalNum),
 }
 
+pub fn target<'a, 'c>() -> impl Parser<'a, 'c, Target>
+where
+    'c: 'a,
+{
+    /*
+     * Target := SuperName | NullName
+     * NullName := 0x00
+     */
+    comment_scope(
+        DebugVerbosity::AllScopes,
+        "Target",
+        choice!(null_name().map(|_| Ok(Target::Null)), super_name()),
+    )
+}
+
 pub fn super_name<'a, 'c>() -> impl Parser<'a, 'c, Target>
 where
     'c: 'a,
@@ -45,9 +61,9 @@ where
         DebugVerbosity::AllScopes,
         "SimpleName",
         choice!(
-            name_string().map(move |name| Ok(Target::Name(name))),
             arg_obj().map(|arg_num| Ok(Target::Arg(arg_num))),
-            local_obj().map(|local_num| Ok(Target::Local(local_num)))
+            local_obj().map(|local_num| Ok(Target::Local(local_num))),
+            name_string().map(move |name| Ok(Target::Name(name)))
         ),
     )
 }
@@ -228,11 +244,11 @@ fn is_name_char(byte: u8) -> bool {
 #[cfg(test)]
 mod tests {
     use super::*;
-    use crate::{parser::Parser, test_utils::*, AmlContext, AmlError, DebugVerbosity};
+    use crate::{parser::Parser, test_utils::*, AmlError};
 
     #[test]
     fn test_name_seg() {
-        let mut context = AmlContext::new(false, DebugVerbosity::None);
+        let mut context = crate::test_utils::make_test_context();
 
         check_ok!(
             name_seg().parse(&[b'A', b'F', b'3', b'Z'], &mut context),
@@ -254,7 +270,7 @@ mod tests {
 
     #[test]
     fn test_name_path() {
-        let mut context = AmlContext::new(false, DebugVerbosity::None);
+        let mut context = crate::test_utils::make_test_context();
 
         check_err!(name_path().parse(&[], &mut context), AmlError::UnexpectedEndOfStream, &[]);
         check_ok!(name_path().parse(&[0x00], &mut context), alloc::vec![], &[]);
@@ -272,7 +288,7 @@ mod tests {
 
     #[test]
     fn test_prefix_path() {
-        let mut context = AmlContext::new(false, DebugVerbosity::None);
+        let mut context = crate::test_utils::make_test_context();
 
         check_ok!(
             name_string().parse(&[b'^', b'A', b'B', b'C', b'D'], &mut context),

+ 41 - 7
aml/src/namespace.rs

@@ -35,10 +35,11 @@ pub enum LevelType {
     MethodLocals,
 }
 
+#[derive(Clone, Debug)]
 pub struct NamespaceLevel {
-    typ: LevelType,
-    children: BTreeMap<NameSeg, NamespaceLevel>,
-    values: BTreeMap<NameSeg, AmlHandle>,
+    pub typ: LevelType,
+    pub children: BTreeMap<NameSeg, NamespaceLevel>,
+    pub values: BTreeMap<NameSeg, AmlHandle>,
 }
 
 impl NamespaceLevel {
@@ -47,6 +48,7 @@ impl NamespaceLevel {
     }
 }
 
+#[derive(Clone)]
 pub struct Namespace {
     /// This is a running count of ids, which are never reused. This is incremented every time we
     /// add a new object to the namespace. We can then remove objects, freeing their memory, without
@@ -159,15 +161,18 @@ impl Namespace {
         Ok(self.object_map.get_mut(&handle).unwrap())
     }
 
-    pub fn get_by_path(&self, path: &AmlName) -> Result<&AmlValue, AmlError> {
+    pub fn get_handle(&self, path: &AmlName) -> Result<AmlHandle, AmlError> {
         let (level, last_seg) = self.get_level_for_path(path)?;
-        let &handle = level.values.get(&last_seg).ok_or(AmlError::ValueDoesNotExist(path.clone()))?;
+        Ok(*level.values.get(&last_seg).ok_or(AmlError::ValueDoesNotExist(path.clone()))?)
+    }
+
+    pub fn get_by_path(&self, path: &AmlName) -> Result<&AmlValue, AmlError> {
+        let handle = self.get_handle(path)?;
         Ok(self.get(handle).unwrap())
     }
 
     pub fn get_by_path_mut(&mut self, path: &AmlName) -> Result<&mut AmlValue, AmlError> {
-        let (level, last_seg) = self.get_level_for_path(path)?;
-        let &handle = level.values.get(&last_seg).ok_or(AmlError::ValueDoesNotExist(path.clone()))?;
+        let handle = self.get_handle(path)?;
         Ok(self.get_mut(handle).unwrap())
     }
 
@@ -294,6 +299,35 @@ impl Namespace {
 
         Ok((current_level, last_seg))
     }
+
+    /// Traverse the namespace, calling `f` on each namespace level. `f` returns a `Result<bool, AmlError>` -
+    /// errors terminate the traversal and are propagated, and the `bool` on the successful path marks whether the
+    /// children of the level should also be traversed.
+    pub fn traverse<F>(&mut self, mut f: F) -> Result<(), AmlError>
+    where
+        F: FnMut(&AmlName, &NamespaceLevel) -> Result<bool, AmlError>,
+    {
+        fn traverse_level<F>(level: &NamespaceLevel, scope: &AmlName, f: &mut F) -> Result<(), AmlError>
+        where
+            F: FnMut(&AmlName, &NamespaceLevel) -> Result<bool, AmlError>,
+        {
+            for (name, ref child) in level.children.iter() {
+                let name = AmlName::from_name_seg(*name).resolve(scope)?;
+
+                if f(&name, child)? {
+                    traverse_level(child, &name, f)?;
+                }
+            }
+
+            Ok(())
+        }
+
+        if f(&AmlName::root(), &self.root)? {
+            traverse_level(&self.root, &AmlName::root(), &mut f)?;
+        }
+
+        Ok(())
+    }
 }
 
 impl fmt::Debug for Namespace {

+ 9 - 5
aml/src/opcode.rs

@@ -42,8 +42,12 @@ pub const DEF_RETURN_OP: u8 = 0xa4;
 /*
  * Type 2 opcodes
  */
-pub const DEF_L_EQUAL_OP: u8 = 0x93;
 pub const DEF_STORE_OP: u8 = 0x70;
+pub const DEF_SHIFT_LEFT: u8 = 0x79;
+pub const DEF_SHIFT_RIGHT: u8 = 0x7a;
+pub const DEF_AND_OP: u8 = 0x7b;
+pub const DEF_L_OR_OP: u8 = 0x91;
+pub const DEF_L_EQUAL_OP: u8 = 0x93;
 
 /*
  * Miscellaneous objects
@@ -88,18 +92,18 @@ where
 #[cfg(test)]
 mod tests {
     use super::*;
-    use crate::{test_utils::*, AmlError, DebugVerbosity};
+    use crate::{test_utils::*, AmlError};
 
     #[test]
     fn empty() {
-        let mut context = AmlContext::new(false, DebugVerbosity::None);
+        let mut context = crate::test_utils::make_test_context();
         check_err!(opcode(NULL_NAME).parse(&[], &mut context), AmlError::UnexpectedEndOfStream, &[]);
         check_err!(ext_opcode(EXT_DEF_FIELD_OP).parse(&[], &mut context), AmlError::UnexpectedEndOfStream, &[]);
     }
 
     #[test]
     fn simple_opcodes() {
-        let mut context = AmlContext::new(false, DebugVerbosity::None);
+        let mut context = crate::test_utils::make_test_context();
         check_ok!(opcode(DEF_SCOPE_OP).parse(&[DEF_SCOPE_OP], &mut context), (), &[]);
         check_ok!(
             opcode(DEF_NAME_OP).parse(&[DEF_NAME_OP, 0x31, 0x55, 0xf3], &mut context),
@@ -110,7 +114,7 @@ mod tests {
 
     #[test]
     fn extended_opcodes() {
-        let mut context = AmlContext::new(false, DebugVerbosity::None);
+        let mut context = crate::test_utils::make_test_context();
         check_err!(
             ext_opcode(EXT_DEF_FIELD_OP).parse(&[EXT_DEF_FIELD_OP, EXT_DEF_FIELD_OP], &mut context),
             AmlError::WrongParser,

+ 3 - 3
aml/src/parser.rs

@@ -472,11 +472,11 @@ pub(crate) macro try_with_context($context: expr, $expr: expr) {
 #[cfg(test)]
 mod tests {
     use super::*;
-    use crate::{test_utils::*, DebugVerbosity};
+    use crate::test_utils::*;
 
     #[test]
     fn test_take_n() {
-        let mut context = AmlContext::new(false, DebugVerbosity::None);
+        let mut context = make_test_context();
         check_err!(take_n(1).parse(&[], &mut context), AmlError::UnexpectedEndOfStream, &[]);
         check_err!(take_n(2).parse(&[0xf5], &mut context), AmlError::UnexpectedEndOfStream, &[0xf5]);
 
@@ -487,7 +487,7 @@ mod tests {
 
     #[test]
     fn test_take_ux() {
-        let mut context = AmlContext::new(false, DebugVerbosity::None);
+        let mut context = make_test_context();
         check_err!(take_u16().parse(&[0x34], &mut context), AmlError::UnexpectedEndOfStream, &[0x34]);
         check_ok!(take_u16().parse(&[0x34, 0x12], &mut context), 0x1234, &[]);
 

+ 3 - 3
aml/src/pci_routing.rs

@@ -87,10 +87,10 @@ impl PciRoutingTable {
                      *   |            |           | pin is connected.                                         |
                      *   | -----------|-----------|-----------------------------------------------------------|
                      */
-                    let address = pin_package[0].as_integer()?;
+                    let address = pin_package[0].as_integer(context)?;
                     let device = address.get_bits(16..32).try_into().map_err(|_| AmlError::PrtInvalidAddress)?;
                     let function = address.get_bits(0..16).try_into().map_err(|_| AmlError::PrtInvalidAddress)?;
-                    let pin = match pin_package[1].as_integer()? {
+                    let pin = match pin_package[1].as_integer(context)? {
                         0 => Pin::IntA,
                         1 => Pin::IntB,
                         2 => Pin::IntC,
@@ -110,7 +110,7 @@ impl PciRoutingTable {
                                 pin,
                                 route_type: PciRouteType::Gsi(
                                     pin_package[3]
-                                        .as_integer()?
+                                        .as_integer(context)?
                                         .try_into()
                                         .map_err(|_| AmlError::PrtInvalidGsi)?,
                                 ),

+ 4 - 4
aml/src/pkg_length.rs

@@ -91,10 +91,10 @@ where
 #[cfg(test)]
 mod tests {
     use super::*;
-    use crate::{test_utils::*, AmlError, DebugVerbosity};
+    use crate::{test_utils::*, AmlError};
 
     fn test_correct_pkglength(stream: &[u8], expected_raw_length: u32, expected_leftover: &[u8]) {
-        let mut context = AmlContext::new(false, DebugVerbosity::None);
+        let mut context = make_test_context();
         check_ok!(
             pkg_length().parse(stream, &mut context),
             PkgLength::from_raw_length(stream, expected_raw_length),
@@ -104,7 +104,7 @@ mod tests {
 
     #[test]
     fn test_raw_pkg_length() {
-        let mut context = AmlContext::new(false, DebugVerbosity::None);
+        let mut context = make_test_context();
         check_ok!(raw_pkg_length().parse(&[0b01000101, 0x14], &mut context), 325, &[]);
         check_ok!(raw_pkg_length().parse(&[0b01000111, 0x14, 0x46], &mut context), 327, &[0x46]);
         check_ok!(raw_pkg_length().parse(&[0b10000111, 0x14, 0x46], &mut context), 287047, &[]);
@@ -112,7 +112,7 @@ mod tests {
 
     #[test]
     fn test_pkg_length() {
-        let mut context = AmlContext::new(false, DebugVerbosity::None);
+        let mut context = make_test_context();
         check_err!(pkg_length().parse(&[], &mut context), AmlError::UnexpectedEndOfStream, &[]);
         test_correct_pkglength(&[0x00], 0, &[]);
         test_correct_pkglength(&[0x05, 0xf5, 0x7f, 0x3e, 0x54, 0x03], 5, &[0xf5, 0x7f, 0x3e, 0x54, 0x03]);

+ 27 - 9
aml/src/term_object.rs

@@ -22,6 +22,7 @@ use crate::{
     value::{AmlValue, FieldFlags, MethodFlags, RegionSpace},
     AmlContext,
     AmlError,
+    AmlHandle,
     DebugVerbosity,
 };
 use alloc::string::String;
@@ -201,21 +202,28 @@ where
                         space @ 0x80..=0xff => RegionSpace::OemDefined(space),
                         byte => return (Err(AmlError::InvalidRegionSpace(byte)), context),
                     };
-                    let offset = match offset.as_integer() {
+                    let offset = match offset.as_integer(context) {
                         Ok(offset) => offset,
                         Err(err) => return (Err(err), context),
                     };
-                    let length = match length.as_integer() {
+                    let length = match length.as_integer(context) {
                         Ok(length) => length,
                         Err(err) => return (Err(err), context),
                     };
+                    let parent_device = match region {
+                        RegionSpace::PciConfig | RegionSpace::IPMI | RegionSpace::GenericSerialBus => {
+                            let resolved_path = try_with_context!(context, name.resolve(&context.current_scope));
+                            Some(try_with_context!(context, resolved_path.parent()))
+                        }
+                        _ => None,
+                    };
 
                     try_with_context!(
                         context,
                         context.namespace.add_value_at_resolved_path(
                             name,
                             &context.current_scope,
-                            AmlValue::OpRegion { region, offset, length }
+                            AmlValue::OpRegion { region, offset, length, parent_device }
                         )
                     );
                     (Ok(()), context)
@@ -233,11 +241,21 @@ where
      * DefField = ExtOpPrefix 0x81 PkgLength NameString FieldFlags FieldList
      * FieldFlags := ByteData
      */
+    let opregion_as_handle = name_string().map_with_context(|region_name, context| {
+        /*
+         * We search for the opregion that this field is referencing here as we already have the correct starting
+         * scope. If we leave this to later, it becomes much harder as we also need to know the field's scope.
+         */
+        let (_, handle) =
+            try_with_context!(context, context.namespace.search(&region_name, &context.current_scope));
+        (Ok(handle), context)
+    });
+
     ext_opcode(opcode::EXT_DEF_FIELD_OP)
         .then(comment_scope(
             DebugVerbosity::Scopes,
             "DefField",
-            pkg_length().then(name_string()).then(take()).feed(|((list_length, region_name), flags)| {
+            pkg_length().then(opregion_as_handle).then(take()).feed(|((list_length, region_handle), flags)| {
                 move |mut input: &'a [u8], mut context: &'c mut AmlContext| -> ParseResult<'a, 'c, ()> {
                     /*
                      * FieldList := Nothing | <FieldElement FieldList>
@@ -246,7 +264,7 @@ where
                     let mut current_offset = 0;
                     while list_length.still_parsing(input) {
                         let (new_input, new_context, field_length) =
-                            field_element(region_name.clone(), FieldFlags::new(flags), current_offset)
+                            field_element(region_handle, FieldFlags::new(flags), current_offset)
                                 .parse(input, context)?;
                         input = new_input;
                         context = new_context;
@@ -263,7 +281,7 @@ where
 /// Parses a `FieldElement`. Takes the current offset within the field list, and returns the length
 /// of the field element parsed.
 pub fn field_element<'a, 'c>(
-    region_name: AmlName,
+    region_handle: AmlHandle,
     flags: FieldFlags,
     current_offset: u64,
 ) -> impl Parser<'a, 'c, u64>
@@ -308,7 +326,7 @@ where
                 AmlName::from_name_seg(name_seg),
                 &context.current_scope,
                 AmlValue::Field {
-                    region: region_name.clone(),
+                    region: region_handle,
                     flags,
                     offset: current_offset,
                     length: length.raw_length as u64,
@@ -587,11 +605,11 @@ where
 #[cfg(test)]
 mod test {
     use super::*;
-    use crate::{test_utils::*, AmlContext, DebugVerbosity};
+    use crate::test_utils::*;
 
     #[test]
     fn test_computational_data() {
-        let mut context = AmlContext::new(false, DebugVerbosity::None);
+        let mut context = make_test_context();
         check_ok!(
             computational_data().parse(&[0x00, 0x34, 0x12], &mut context),
             AmlValue::Integer(0),

+ 79 - 3
aml/src/test_utils.rs

@@ -1,8 +1,84 @@
+use crate::{AmlContext, Handler};
+use alloc::boxed::Box;
+
+struct TestHandler;
+
+impl Handler for TestHandler {
+    fn read_u8(&self, _address: usize) -> u8 {
+        unimplemented!()
+    }
+    fn read_u16(&self, _address: usize) -> u16 {
+        unimplemented!()
+    }
+    fn read_u32(&self, _address: usize) -> u32 {
+        unimplemented!()
+    }
+    fn read_u64(&self, _address: usize) -> u64 {
+        unimplemented!()
+    }
+
+    fn write_u8(&mut self, _address: usize, _value: u8) {
+        unimplemented!()
+    }
+    fn write_u16(&mut self, _address: usize, _value: u16) {
+        unimplemented!()
+    }
+    fn write_u32(&mut self, _address: usize, _value: u32) {
+        unimplemented!()
+    }
+    fn write_u64(&mut self, _address: usize, _value: u64) {
+        unimplemented!()
+    }
+
+    fn read_io_u8(&self, _port: u16) -> u8 {
+        unimplemented!()
+    }
+    fn read_io_u16(&self, _port: u16) -> u16 {
+        unimplemented!()
+    }
+    fn read_io_u32(&self, _port: u16) -> u32 {
+        unimplemented!()
+    }
+
+    fn write_io_u8(&self, _port: u16, _value: u8) {
+        unimplemented!()
+    }
+    fn write_io_u16(&self, _port: u16, _value: u16) {
+        unimplemented!()
+    }
+    fn write_io_u32(&self, _port: u16, _value: u32) {
+        unimplemented!()
+    }
+
+    fn read_pci_u8(&self, _segment: u16, _bus: u8, device: u8, _function: u8, _offset: u16) -> u8 {
+        unimplemented!()
+    }
+    fn read_pci_u16(&self, _segment: u16, _bus: u8, device: u8, _function: u8, _offset: u16) -> u16 {
+        unimplemented!()
+    }
+    fn read_pci_u32(&self, _segment: u16, _bus: u8, device: u8, _function: u8, _offset: u16) -> u32 {
+        unimplemented!()
+    }
+    fn write_pci_u8(&self, _segment: u16, _bus: u8, device: u8, _function: u8, _offset: u16, _value: u8) {
+        unimplemented!()
+    }
+    fn write_pci_u16(&self, _segment: u16, _bus: u8, device: u8, _function: u8, _offset: u16, _value: u16) {
+        unimplemented!()
+    }
+    fn write_pci_u32(&self, _segment: u16, _bus: u8, device: u8, _function: u8, _offset: u16, _value: u32) {
+        unimplemented!()
+    }
+}
+
+pub(crate) fn make_test_context() -> AmlContext {
+    AmlContext::new(Box::new(TestHandler), false, crate::DebugVerbosity::None)
+}
+
 pub(crate) macro check_err($parse: expr, $error: pat, $remains: expr) {
     match $parse {
-        Ok(result) => panic!("Expected Err, got {:#?}", result),
+        Ok((remains, _, result)) => panic!("Expected Err, got {:#?}. Remaining = {:#x?}", result, remains),
         Err((remains, _, $error)) if *remains == *$remains => (),
-        Err((remains, _, $error)) => panic!("Correct error, incorrect stream returned: {:x?}", remains),
+        Err((remains, _, $error)) => panic!("Correct error, incorrect stream returned: {:#x?}", remains),
         Err((_, _, err)) => panic!("Got wrong error: {:?}", err),
     }
 }
@@ -13,7 +89,7 @@ pub(crate) macro check_ok($parse: expr, $expected: expr, $remains: expr) {
         Ok((remains, _, ref result)) if result == &$expected => {
             panic!("Correct result, incorrect slice returned: {:x?}", remains)
         }
-        Ok(result) => panic!("Successfully parsed Ok, but it was wrong: {:#?}", result),
+        Ok((_, _, ref result)) => panic!("Successfully parsed Ok, but it was wrong: {:#?}", result),
         Err((_, _, err)) => panic!("Expected Ok, got {:#?}", err),
     }
 }

+ 161 - 50
aml/src/type2.rs

@@ -1,13 +1,24 @@
 use crate::{
-    name_object::{name_string, super_name, Target},
+    name_object::{name_string, super_name, target},
     opcode::{self, opcode},
-    parser::{choice, comment_scope, id, take, take_to_end_of_pkglength, try_with_context, Parser},
+    parser::{
+        choice,
+        comment_scope,
+        make_parser_concrete,
+        n_of,
+        take,
+        take_to_end_of_pkglength,
+        try_with_context,
+        Parser,
+    },
     pkg_length::pkg_length,
     term_object::{data_ref_object, term_arg},
-    value::AmlValue,
+    value::{AmlValue, Args},
+    AmlError,
     DebugVerbosity,
 };
 use alloc::vec::Vec;
+use core::convert::TryInto;
 
 /// Type 2 opcodes return a value and so can be used in expressions.
 pub fn type2_opcode<'a, 'c>() -> impl Parser<'a, 'c, AmlValue>
@@ -24,12 +35,47 @@ where
      *                DefVarPackage | DefRefOf | DefShiftLeft | DefShitRight | DefSizeOf | DefStore |
      *                DefSubtract | DefTimer | DefToBCD | DefToBuffer | DefToDecimalString |
      *                DefToHexString | DefToInteger | DefToString | DefWait | DefXOr | MethodInvocation
+     *
+     * NOTE: MethodInvocation should always appear last in the choice.
      */
-    comment_scope(
+    make_parser_concrete!(comment_scope(
         DebugVerbosity::AllScopes,
         "Type2Opcode",
-        choice!(def_buffer(), def_l_equal(), def_package(), def_store(), method_invocation()),
-    )
+        choice!(
+            def_and(),
+            def_buffer(),
+            def_l_equal(),
+            def_l_or(),
+            def_package(),
+            def_shift_left(),
+            def_shift_right(),
+            def_store(),
+            method_invocation()
+        ),
+    ))
+}
+
+pub fn def_and<'a, 'c>() -> impl Parser<'a, 'c, AmlValue>
+where
+    'c: 'a,
+{
+    /*
+     * DefAnd := 0x7b Operand Operand Target
+     * Operand := TermArg => Integer
+     */
+    opcode(opcode::DEF_AND_OP)
+        .then(comment_scope(
+            DebugVerbosity::AllScopes,
+            "DefAnd",
+            term_arg().then(term_arg()).then(target()).map_with_context(
+                |((left_arg, right_arg), target), context| {
+                    let left = try_with_context!(context, left_arg.as_integer(context));
+                    let right = try_with_context!(context, right_arg.as_integer(context));
+                    (Ok(AmlValue::Integer(left & right)), context)
+                },
+            ),
+        ))
+        .map(|((), result)| Ok(result))
 }
 
 pub fn def_buffer<'a, 'c>() -> impl Parser<'a, 'c, AmlValue>
@@ -49,13 +95,36 @@ where
             DebugVerbosity::AllScopes,
             "DefBuffer",
             pkg_length().then(term_arg()).feed(|(pkg_length, buffer_size)| {
-                take_to_end_of_pkglength(pkg_length)
-                    .map(move |bytes| Ok((bytes.to_vec(), buffer_size.as_integer()?)))
+                take_to_end_of_pkglength(pkg_length).map_with_context(move |bytes, context| {
+                    let length = try_with_context!(context, buffer_size.as_integer(context));
+                    (Ok((bytes.to_vec(), length)), context)
+                })
             }),
         ))
         .map(|((), (bytes, buffer_size))| Ok(AmlValue::Buffer { bytes, size: buffer_size }))
 }
 
+fn def_l_or<'a, 'c>() -> impl Parser<'a, 'c, AmlValue>
+where
+    'c: 'a,
+{
+    /*
+     * DefLOr := 0x91 Operand Operand
+     * Operand := TermArg => Integer
+     */
+    opcode(opcode::DEF_L_OR_OP)
+        .then(comment_scope(
+            DebugVerbosity::AllScopes,
+            "DefLOr",
+            term_arg().then(term_arg()).map_with_context(|(left_arg, right_arg), context| {
+                let left = try_with_context!(context, left_arg.as_bool());
+                let right = try_with_context!(context, right_arg.as_bool());
+                (Ok(AmlValue::Boolean(left || right)), context)
+            }),
+        ))
+        .map(|((), result)| Ok(result))
+}
+
 fn def_l_equal<'a, 'c>() -> impl Parser<'a, 'c, AmlValue>
 where
     'c: 'a,
@@ -68,8 +137,15 @@ where
         .then(comment_scope(
             DebugVerbosity::AllScopes,
             "DefLEqual",
-            term_arg().then(term_arg()).map(|(left_arg, right_arg)| {
-                Ok(AmlValue::Boolean(left_arg.as_integer()? == right_arg.as_integer()?))
+            term_arg().then(term_arg()).map_with_context(|(left_arg, right_arg), context| {
+                /*
+                 * TODO: we should also be able to compare strings and buffers. `left_arg` decides the type that we
+                 * need to use - we have to try and convert `right_arg` into that type and then compare them in the
+                 * correct way.
+                 */
+                let left = try_with_context!(context, left_arg.as_integer(context));
+                let right = try_with_context!(context, right_arg.as_integer(context));
+                (Ok(AmlValue::Boolean(left == right)), context)
             }),
         ))
         .map(|((), result)| Ok(result))
@@ -116,6 +192,60 @@ where
     choice!(data_ref_object(), name_string().map(|string| Ok(AmlValue::String(string.as_string()))))
 }
 
+fn def_shift_left<'a, 'c>() -> impl Parser<'a, 'c, AmlValue>
+where
+    'c: 'a,
+{
+    /*
+     * DefShiftLeft := 0x79 Operand ShiftCount Target
+     * Operand := TermArg => Integer
+     * ShiftCount := TermArg => Integer
+     */
+    opcode(opcode::DEF_SHIFT_LEFT)
+        .then(comment_scope(DebugVerbosity::Scopes, "DefShiftLeft", term_arg().then(term_arg()).then(target())))
+        .map_with_context(|((), ((operand, shift_count), target)), context| {
+            let operand = try_with_context!(context, operand.as_integer(context));
+            let shift_count = try_with_context!(context, shift_count.as_integer(context));
+            let shift_count =
+                try_with_context!(context, shift_count.try_into().map_err(|_| AmlError::InvalidShiftLeft));
+
+            let result = AmlValue::Integer(try_with_context!(
+                context,
+                operand.checked_shl(shift_count).ok_or(AmlError::InvalidShiftLeft)
+            ));
+
+            try_with_context!(context, context.store(target, result.clone()));
+            (Ok(result), context)
+        })
+}
+
+fn def_shift_right<'a, 'c>() -> impl Parser<'a, 'c, AmlValue>
+where
+    'c: 'a,
+{
+    /*
+     * DefShiftRight := 0x7a Operand ShiftCount Target
+     * Operand := TermArg => Integer
+     * ShiftCount := TermArg => Integer
+     */
+    opcode(opcode::DEF_SHIFT_RIGHT)
+        .then(comment_scope(DebugVerbosity::Scopes, "DefShiftRight", term_arg().then(term_arg()).then(target())))
+        .map_with_context(|((), ((operand, shift_count), target)), context| {
+            let operand = try_with_context!(context, operand.as_integer(context));
+            let shift_count = try_with_context!(context, shift_count.as_integer(context));
+            let shift_count =
+                try_with_context!(context, shift_count.try_into().map_err(|_| AmlError::InvalidShiftRight));
+
+            let result = AmlValue::Integer(try_with_context!(
+                context,
+                operand.checked_shr(shift_count).ok_or(AmlError::InvalidShiftRight)
+            ));
+
+            try_with_context!(context, context.store(target, result.clone()));
+            (Ok(result), context)
+        })
+}
+
 fn def_store<'a, 'c>() -> impl Parser<'a, 'c, AmlValue>
 where
     'c: 'a,
@@ -132,32 +262,7 @@ where
     opcode(opcode::DEF_STORE_OP)
         .then(comment_scope(DebugVerbosity::Scopes, "DefStore", term_arg().then(super_name())))
         .map_with_context(|((), (value, target)), context| {
-            match target {
-                Target::Name(ref path) => {
-                    let (_, handle) =
-                        try_with_context!(context, context.namespace.search(path, &context.current_scope));
-                    let desired_type = context.namespace.get(handle).unwrap().type_of();
-                    let converted_object = try_with_context!(context, value.as_type(desired_type));
-
-                    *try_with_context!(context, context.namespace.get_mut(handle)) = converted_object;
-                    (Ok(context.namespace.get(handle).unwrap().clone()), context)
-                }
-
-                Target::Debug => {
-                    // TODO
-                    unimplemented!()
-                }
-
-                Target::Arg(arg_num) => {
-                    // TODO
-                    unimplemented!()
-                }
-
-                Target::Local(local_num) => {
-                    // TODO
-                    unimplemented!()
-                }
-            }
+            (Ok(try_with_context!(context, context.store(target, value))), context)
         })
 }
 
@@ -179,23 +284,29 @@ where
         "MethodInvocation",
         name_string()
             .map_with_context(move |name, context| {
-                let (_, handle) =
+                let (full_path, handle) =
                     try_with_context!(context, context.namespace.search(&name, &context.current_scope)).clone();
-                (Ok(handle), context)
+
+                /*
+                 * `None` if the path is not a method and so doesn't have arguments, or `Some(the number of
+                 * arguments to parse)` if it's a method.
+                 */
+                let num_args = if let AmlValue::Method { flags, .. } =
+                    try_with_context!(context, context.namespace.get(handle))
+                {
+                    Some(flags.arg_count())
+                } else {
+                    None
+                };
+                (Ok((full_path, num_args)), context)
             })
-            .feed(|handle| {
-                id().map_with_context(move |(), context| {
-                    let object = try_with_context!(context, context.namespace.get(handle));
-                    if let AmlValue::Method { ref code, .. } = object {
-                        // TODO: we need to allow a method to be invoked from inside another method before we can
-                        // implement this (basically a stack of contexts) then implement this
-                        unimplemented!()
+            .feed(|(path, num_args)| {
+                n_of(term_arg(), num_args.unwrap_or(0) as usize).map_with_context(move |arg_list, context| {
+                    if num_args.is_some() {
+                        let result = context.invoke_method(&path, Args::from_list(arg_list));
+                        (Ok(try_with_context!(context, result)), context)
                     } else {
-                        // We appear to be seeing AML where a MethodInvocation actually doesn't point to a method
-                        // at all, which isn't mentioned in the spec afaict.  However, if we treat it as an
-                        // "invocation" with 0 arguments and simply return the object, the AML seems to do sensible
-                        // things.
-                        (Ok(object.clone()), context)
+                        (Ok(try_with_context!(context, context.namespace.get_by_path(&path)).clone()), context)
                     }
                 })
             }),

+ 213 - 23
aml/src/value.rs

@@ -1,4 +1,4 @@
-use crate::{misc::ArgNum, namespace::AmlName, AmlError};
+use crate::{misc::ArgNum, AmlContext, AmlError, AmlHandle, AmlName};
 use alloc::{string::String, vec::Vec};
 use bit_field::BitField;
 
@@ -25,7 +25,6 @@ pub enum FieldAccessType {
     DWord,
     QWord,
     Buffer,
-    Reserved,
 }
 
 #[derive(Clone, Copy, PartialEq, Eq, Debug)]
@@ -91,6 +90,32 @@ impl MethodFlags {
     }
 }
 
+/// Representation of the return value of a `_STA` method, which represents the status of an object. It must be
+/// evaluated, if present, before evaluating the `_INI` method for an device.
+///
+/// The `Default` implementation of this type is the correct value to use if a device doesn't have a `_STA` object
+/// to evaluate.
+#[derive(Clone, Copy, PartialEq, Eq, Debug)]
+pub struct StatusObject {
+    /// Whether the device is physically present. If this is `false`, `enabled` should also be `false` (i.e. a
+    /// device that is not present can't be enabled). However, this is not enforced here if the firmware is doing
+    /// something wrong.
+    pub present: bool,
+    /// Whether the device is enabled. Both `present` and `enabled` must be `true` for the device to decode its
+    /// hardware resources.
+    pub enabled: bool,
+    pub show_in_ui: bool,
+    pub functional: bool,
+    /// Only applicable for Control Method Battery Devices (`PNP0C0A`). For all other devices, ignore this value.
+    pub battery_present: bool,
+}
+
+impl Default for StatusObject {
+    fn default() -> Self {
+        StatusObject { present: true, enabled: true, show_in_ui: true, functional: true, battery_present: true }
+    }
+}
+
 #[derive(Clone, Copy, PartialEq, Eq, Debug)]
 pub enum AmlType {
     Uninitialized,
@@ -119,12 +144,38 @@ pub enum AmlValue {
     Boolean(bool),
     Integer(u64),
     String(String),
-    OpRegion { region: RegionSpace, offset: u64, length: u64 },
-    Field { region: AmlName, flags: FieldFlags, offset: u64, length: u64 },
-    Method { flags: MethodFlags, code: Vec<u8> },
-    Buffer { bytes: Vec<u8>, size: u64 },
-    Processor { id: u8, pblk_address: u32, pblk_len: u8 },
-    Mutex { sync_level: u8 },
+    /// Describes an operation region. Some regions require other objects to be declared under their parent device
+    /// (e.g. an `_ADR` object for a `PciConfig` region), in which case an absolute path to the object is stored in
+    /// `parent_device`.
+    OpRegion {
+        region: RegionSpace,
+        offset: u64,
+        length: u64,
+        parent_device: Option<AmlName>,
+    },
+    /// Describes a field unit within an operation region.
+    Field {
+        region: AmlHandle,
+        flags: FieldFlags,
+        offset: u64,
+        length: u64,
+    },
+    Method {
+        flags: MethodFlags,
+        code: Vec<u8>,
+    },
+    Buffer {
+        bytes: Vec<u8>,
+        size: u64,
+    },
+    Processor {
+        id: u8,
+        pblk_address: u32,
+        pblk_len: u8,
+    },
+    Mutex {
+        sync_level: u8,
+    },
     Package(Vec<AmlValue>),
 }
 
@@ -148,11 +199,12 @@ impl AmlValue {
     pub fn as_bool(&self) -> Result<bool, AmlError> {
         match self {
             AmlValue::Boolean(value) => Ok(*value),
+            AmlValue::Integer(value) => Ok(*value != 0),
             _ => Err(AmlError::IncompatibleValueConversion),
         }
     }
 
-    pub fn as_integer(&self) -> Result<u64, AmlError> {
+    pub fn as_integer(&self, context: &AmlContext) -> Result<u64, AmlError> {
         match self {
             AmlValue::Integer(value) => Ok(*value),
 
@@ -174,10 +226,42 @@ impl AmlValue {
                 }))
             }
 
+            /*
+             * Read from a field. This can return either a `Buffer` or an `Integer`, so we make sure to call
+             * `as_integer` on the result.
+             */
+            AmlValue::Field { .. } => self.read_field(context)?.as_integer(context),
+
             _ => Err(AmlError::IncompatibleValueConversion),
         }
     }
 
+    /// Turns an `AmlValue` returned from a `_STA` method into a `StatusObject`. Should only be called for values
+    /// returned from `_STA`. If you need a `StatusObject`, but the device does not have a `_STA` method, use
+    /// `StatusObject::default()`.
+    pub fn as_status(&self) -> Result<StatusObject, AmlError> {
+        match self {
+            AmlValue::Integer(value) => {
+                /*
+                 * Bits 5+ are reserved and are expected to be cleared.
+                 */
+                if value.get_bits(5..64) != 0 {
+                    return Err(AmlError::InvalidStatusObject);
+                }
+
+                Ok(StatusObject {
+                    present: value.get_bit(0),
+                    enabled: value.get_bit(1),
+                    show_in_ui: value.get_bit(2),
+                    functional: value.get_bit(3),
+                    battery_present: value.get_bit(4),
+                })
+            }
+
+            _ => Err(AmlError::InvalidStatusObject),
+        }
+    }
+
     /// Convert this value to a value of the same data, but with the given AML type, if possible,
     /// by converting the implicit conversions described in §19.3.5 of the spec.
     ///
@@ -189,21 +273,114 @@ impl AmlValue {
     ///     `Integer` from: `Buffer`, `BufferField`, `DdbHandle`, `FieldUnit`, `String`, `Debug`
     ///     `Package` from: `Debug`
     ///     `String` from: `Integer`, `Buffer`, `Debug`
-    pub fn as_type(&self, desired_type: AmlType) -> Result<AmlValue, AmlError> {
-        // Cache the type of this object
-        let our_type = self.type_of();
-
+    pub fn as_type(&self, desired_type: AmlType, context: &AmlContext) -> Result<AmlValue, AmlError> {
         // If the value is already of the correct type, just return it as is
-        if our_type == desired_type {
+        if self.type_of() == desired_type {
             return Ok(self.clone());
         }
 
         // TODO: implement all of the rules
         match desired_type {
-            AmlType::Integer => self.as_integer().map(|value| AmlValue::Integer(value)),
+            AmlType::Integer => self.as_integer(context).map(|value| AmlValue::Integer(value)),
+            AmlType::FieldUnit => panic!(
+                "Can't implicitly convert to FieldUnit. This must be special-cased by the caller for now :("
+            ),
             _ => Err(AmlError::IncompatibleValueConversion),
         }
     }
+
+    /// Reads from a field of an opregion, returning either a `AmlValue::Integer` or an `AmlValue::Buffer`,
+    /// depending on the size of the field.
+    pub fn read_field(&self, context: &AmlContext) -> Result<AmlValue, AmlError> {
+        if let AmlValue::Field { region, flags, offset, length } = self {
+            let maximum_access_size = {
+                if let AmlValue::OpRegion { region, .. } = context.namespace.get(*region)? {
+                    match region {
+                        RegionSpace::SystemMemory => 64,
+                        RegionSpace::SystemIo | RegionSpace::PciConfig => 32,
+                        _ => unimplemented!(),
+                    }
+                } else {
+                    return Err(AmlError::FieldRegionIsNotOpRegion);
+                }
+            };
+            let minimum_access_size = match flags.access_type()? {
+                FieldAccessType::Any => 8,
+                FieldAccessType::Byte => 8,
+                FieldAccessType::Word => 16,
+                FieldAccessType::DWord => 32,
+                FieldAccessType::QWord => 64,
+                FieldAccessType::Buffer => 8, // TODO
+            };
+
+            /*
+             * Find the access size, as either the minimum access size allowed by the region, or the field length
+             * rounded up to the next power-of-2, whichever is larger.
+             */
+            let access_size = u64::max(minimum_access_size, length.next_power_of_two());
+
+            /*
+             * TODO: we need to decide properly how to read from the region itself. Complications:
+             *    - if the region has a minimum access size greater than the desired length, we need to read the
+             *      minimum and mask it (reading a byte from a WordAcc region)
+             *    - if the desired length is larger than we can read, we need to do multiple reads
+             */
+            Ok(AmlValue::Integer(
+                context.read_region(*region, *offset, access_size)?.get_bits(0..(*length as usize)),
+            ))
+        } else {
+            Err(AmlError::IncompatibleValueConversion)
+        }
+    }
+
+    pub fn write_field(&mut self, value: AmlValue, context: &mut AmlContext) -> Result<(), AmlError> {
+        /*
+         * TODO:
+         * If we need to preserve the field's value, we'll need the contents of the field before we write it. To
+         * appease the borrow-checker, this is done before we destructure the field for now, but it would be more
+         * efficient if we could only do this if the field's update rule is Preserve.
+         */
+        let field_value = self.read_field(context)?.as_integer(context)?;
+
+        if let AmlValue::Field { region, flags, offset, length } = self {
+            let maximum_access_size = {
+                if let AmlValue::OpRegion { region, .. } = context.namespace.get(*region)? {
+                    match region {
+                        RegionSpace::SystemMemory => 64,
+                        RegionSpace::SystemIo | RegionSpace::PciConfig => 32,
+                        _ => unimplemented!(),
+                    }
+                } else {
+                    return Err(AmlError::FieldRegionIsNotOpRegion);
+                }
+            };
+            let minimum_access_size = match flags.access_type()? {
+                FieldAccessType::Any => 8,
+                FieldAccessType::Byte => 8,
+                FieldAccessType::Word => 16,
+                FieldAccessType::DWord => 32,
+                FieldAccessType::QWord => 64,
+                FieldAccessType::Buffer => 8, // TODO
+            };
+
+            /*
+             * Find the access size, as either the minimum access size allowed by the region, or the field length
+             * rounded up to the next power-of-2, whichever is larger.
+             */
+            let access_size = u64::max(minimum_access_size, length.next_power_of_two());
+
+            let mut value_to_write = match flags.field_update_rule()? {
+                FieldUpdateRule::Preserve => field_value,
+                FieldUpdateRule::WriteAsOnes => 0xffffffff_ffffffff,
+                FieldUpdateRule::WriteAsZeros => 0x0,
+            };
+            value_to_write.set_bits(0..(*length as usize), value.as_integer(context)?);
+
+            context.write_region(*region, *offset, access_size, value_to_write)
+        } else {
+            Err(AmlError::IncompatibleValueConversion)
+        }
+    }
 }
 
 /// A control method can take up to 7 arguments, each of which can be an `AmlValue`.
@@ -219,20 +396,33 @@ pub struct Args {
 }
 
 impl Args {
+    pub fn from_list(mut list: Vec<AmlValue>) -> Args {
+        assert!(list.len() <= 7);
+        list.reverse();
+        Args {
+            arg_0: list.pop(),
+            arg_1: list.pop(),
+            arg_2: list.pop(),
+            arg_3: list.pop(),
+            arg_4: list.pop(),
+            arg_5: list.pop(),
+            arg_6: list.pop(),
+        }
+    }
     /// Get an argument by its `ArgNum`.
     ///
     /// ### Panics
     /// Panics if passed an invalid argument number (valid argument numbers are `0..=6`)
     pub fn arg(&self, num: ArgNum) -> Result<&AmlValue, AmlError> {
         match num {
-            0 => self.arg_0.as_ref().ok_or(AmlError::InvalidArgumentAccess(num)),
-            1 => self.arg_1.as_ref().ok_or(AmlError::InvalidArgumentAccess(num)),
-            2 => self.arg_2.as_ref().ok_or(AmlError::InvalidArgumentAccess(num)),
-            3 => self.arg_3.as_ref().ok_or(AmlError::InvalidArgumentAccess(num)),
-            4 => self.arg_4.as_ref().ok_or(AmlError::InvalidArgumentAccess(num)),
-            5 => self.arg_5.as_ref().ok_or(AmlError::InvalidArgumentAccess(num)),
-            6 => self.arg_6.as_ref().ok_or(AmlError::InvalidArgumentAccess(num)),
-            _ => panic!("Invalid argument number: {}", num),
+            0 => self.arg_0.as_ref().ok_or(AmlError::InvalidArgAccess(num)),
+            1 => self.arg_1.as_ref().ok_or(AmlError::InvalidArgAccess(num)),
+            2 => self.arg_2.as_ref().ok_or(AmlError::InvalidArgAccess(num)),
+            3 => self.arg_3.as_ref().ok_or(AmlError::InvalidArgAccess(num)),
+            4 => self.arg_4.as_ref().ok_or(AmlError::InvalidArgAccess(num)),
+            5 => self.arg_5.as_ref().ok_or(AmlError::InvalidArgAccess(num)),
+            6 => self.arg_6.as_ref().ok_or(AmlError::InvalidArgAccess(num)),
+            _ => Err(AmlError::InvalidArgAccess(num)),
         }
     }
 }

+ 70 - 1
aml_tester/src/main.rs

@@ -56,7 +56,7 @@ fn main() -> std::io::Result<()> {
         file.read_to_end(&mut contents).unwrap();
 
         const AML_TABLE_HEADER_LENGTH: usize = 36;
-        let mut context = AmlContext::new(false, DebugVerbosity::None);
+        let mut context = AmlContext::new(Box::new(Handler), false, DebugVerbosity::None);
 
         match context.parse_table(&contents[AML_TABLE_HEADER_LENGTH..]) {
             Ok(()) => {
@@ -137,3 +137,72 @@ impl log::Log for Logger {
         std::io::stdout().flush().unwrap();
     }
 }
+
+struct Handler;
+
+impl aml::Handler for Handler {
+    fn read_u8(&self, _address: usize) -> u8 {
+        unimplemented!()
+    }
+    fn read_u16(&self, _address: usize) -> u16 {
+        unimplemented!()
+    }
+    fn read_u32(&self, _address: usize) -> u32 {
+        unimplemented!()
+    }
+    fn read_u64(&self, _address: usize) -> u64 {
+        unimplemented!()
+    }
+
+    fn write_u8(&mut self, _address: usize, _value: u8) {
+        unimplemented!()
+    }
+    fn write_u16(&mut self, _address: usize, _value: u16) {
+        unimplemented!()
+    }
+    fn write_u32(&mut self, _address: usize, _value: u32) {
+        unimplemented!()
+    }
+    fn write_u64(&mut self, _address: usize, _value: u64) {
+        unimplemented!()
+    }
+
+    fn read_io_u8(&self, _port: u16) -> u8 {
+        unimplemented!()
+    }
+    fn read_io_u16(&self, _port: u16) -> u16 {
+        unimplemented!()
+    }
+    fn read_io_u32(&self, _port: u16) -> u32 {
+        unimplemented!()
+    }
+
+    fn write_io_u8(&self, _port: u16, _value: u8) {
+        unimplemented!()
+    }
+    fn write_io_u16(&self, _port: u16, _value: u16) {
+        unimplemented!()
+    }
+    fn write_io_u32(&self, _port: u16, _value: u32) {
+        unimplemented!()
+    }
+
+    fn read_pci_u8(&self, _segment: u16, _bus: u8, device: u8, _function: u8, _offset: u16) -> u8 {
+        unimplemented!()
+    }
+    fn read_pci_u16(&self, _segment: u16, _bus: u8, device: u8, _function: u8, _offset: u16) -> u16 {
+        unimplemented!()
+    }
+    fn read_pci_u32(&self, _segment: u16, _bus: u8, device: u8, _function: u8, _offset: u16) -> u32 {
+        unimplemented!()
+    }
+    fn write_pci_u8(&self, _segment: u16, _bus: u8, device: u8, _function: u8, _offset: u16, _value: u8) {
+        unimplemented!()
+    }
+    fn write_pci_u16(&self, _segment: u16, _bus: u8, device: u8, _function: u8, _offset: u16, _value: u16) {
+        unimplemented!()
+    }
+    fn write_pci_u32(&self, _segment: u16, _bus: u8, device: u8, _function: u8, _offset: u16, _value: u32) {
+        unimplemented!()
+    }
+}