From 50ea695c3a77667e93899e9ec3976e034e3707d2 Mon Sep 17 00:00:00 2001
From: Isaac Woods <isaacwoods.home@gmail.com>
Date: Wed, 26 Jun 2024 13:07:06 +0100
Subject: [PATCH 01/88] `acpi`: remove redundant import

---
 acpi/src/address.rs | 1 -
 1 file changed, 1 deletion(-)

diff --git a/acpi/src/address.rs b/acpi/src/address.rs
index 0d890b18..5854d987 100644
--- a/acpi/src/address.rs
+++ b/acpi/src/address.rs
@@ -2,7 +2,6 @@
 //! in a wide range of address spaces.
 
 use crate::AcpiError;
-use core::convert::TryFrom;
 
 /// This is the raw form of a Generic Address Structure, and follows the layout found in the ACPI tables. It does
 /// not form part of the public API, and should be turned into a `GenericAddress` for most use-cases.

From e2811fa314529f072bff9e0969c04fa8506eef85 Mon Sep 17 00:00:00 2001
From: Isaac Woods <isaacwoods.home@gmail.com>
Date: Wed, 26 Jun 2024 13:07:15 +0100
Subject: [PATCH 02/88] `aml`: fix fuzz target

---
 aml/fuzz/fuzz_targets/fuzz_target_1.rs | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/aml/fuzz/fuzz_targets/fuzz_target_1.rs b/aml/fuzz/fuzz_targets/fuzz_target_1.rs
index 280d9594..e2e452e8 100644
--- a/aml/fuzz/fuzz_targets/fuzz_target_1.rs
+++ b/aml/fuzz/fuzz_targets/fuzz_target_1.rs
@@ -11,7 +11,7 @@ fuzz_target!(|data: &[u8]| {
         simplelog::SimpleLogger::init(simplelog::LevelFilter::Trace, simplelog::Config::default()).unwrap();
     }
 
-    let mut context = aml::AmlContext::new(Box::new(Handler), false, aml::DebugVerbosity::None);
+    let mut context = aml::AmlContext::new(Box::new(Handler), aml::DebugVerbosity::None);
     let _ = context.parse_table(data);
 });
 
@@ -62,4 +62,7 @@ impl aml::Handler for Handler {
     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) {}
+
+    fn stall(&self, _microseconds: u64) {}
+    fn sleep(&self, _milliseconds: u64) {}
 }

From 598ad9c8bae711cf519951200f5e1ba617c135fb Mon Sep 17 00:00:00 2001
From: Isaac Woods <isaacwoods.home@gmail.com>
Date: Wed, 26 Jun 2024 13:12:50 +0100
Subject: [PATCH 03/88] `aml`: don't panic if AML tries to create a giant
 `Buffer`

---
 aml/src/expression.rs | 8 ++++++++
 aml/src/lib.rs        | 2 +-
 2 files changed, 9 insertions(+), 1 deletion(-)

diff --git a/aml/src/expression.rs b/aml/src/expression.rs
index 5dfa1fb6..f5babc22 100644
--- a/aml/src/expression.rs
+++ b/aml/src/expression.rs
@@ -198,6 +198,14 @@ where
                         return (Err(Propagate::Err(AmlError::MalformedBuffer)), context);
                     }
 
+                    /*
+                     * `Vec` has a maximum capacity of `isize::MAX` **bytes**. If pathological AML
+                     * does try to create a gigantic buffer, we don't want to crash.
+                     */
+                    if buffer_size > (isize::MAX as usize) {
+                        return (Err(Propagate::Err(AmlError::MalformedBuffer)), context);
+                    }
+
                     let mut buffer = vec![0; buffer_size];
                     buffer[0..bytes.len()].copy_from_slice(bytes);
                     (Ok(buffer), context)
diff --git a/aml/src/lib.rs b/aml/src/lib.rs
index 90027e3b..268011e9 100644
--- a/aml/src/lib.rs
+++ b/aml/src/lib.rs
@@ -35,7 +35,7 @@
 //! combinator patterns to express the parse.
 
 #![no_std]
-#![feature(decl_macro)]
+#![feature(decl_macro, try_with_capacity)]
 
 extern crate alloc;
 

From e495007cc34f6440cb1d4229234379a550e4751c Mon Sep 17 00:00:00 2001
From: Isaac Woods <isaacwoods.home@gmail.com>
Date: Sat, 18 Jan 2025 15:56:41 +0000
Subject: [PATCH 04/88] `acpi`: break out HPET fields

---
 acpi/src/hpet.rs | 34 +++++++++++-----------------------
 1 file changed, 11 insertions(+), 23 deletions(-)

diff --git a/acpi/src/hpet.rs b/acpi/src/hpet.rs
index 6360486c..ea314d1b 100644
--- a/acpi/src/hpet.rs
+++ b/acpi/src/hpet.rs
@@ -21,8 +21,11 @@ pub enum PageProtection {
 /// Information about the High Precision Event Timer (HPET)
 #[derive(Debug)]
 pub struct HpetInfo {
-    // TODO(3.0.0): unpack these fields directly, and get rid of methods
-    pub event_timer_block_id: u32,
+    pub hardware_rev: u8,
+    pub num_comparators: u8,
+    pub main_counter_is_64bits: bool,
+    pub legacy_irq_capable: bool,
+    pub pci_vendor_id: u16,
     pub base_address: usize,
     pub hpet_number: u8,
     /// The minimum number of clock ticks that can be set without losing interrupts (for timers in Periodic Mode)
@@ -40,8 +43,13 @@ impl HpetInfo {
         // Make sure the HPET is in system memory
         assert_eq!(hpet.base_address.address_space, 0);
 
+        let event_timer_block_id = hpet.event_timer_block_id;
         Ok(HpetInfo {
-            event_timer_block_id: hpet.event_timer_block_id,
+            hardware_rev: event_timer_block_id.get_bits(0..8) as u8,
+            num_comparators: event_timer_block_id.get_bits(8..13) as u8,
+            main_counter_is_64bits: event_timer_block_id.get_bit(13),
+            legacy_irq_capable: event_timer_block_id.get_bit(15),
+            pci_vendor_id: event_timer_block_id.get_bits(16..32) as u16,
             base_address: hpet.base_address.address as usize,
             hpet_number: hpet.hpet_number,
             clock_tick_unit: hpet.clock_tick_unit,
@@ -54,26 +62,6 @@ impl HpetInfo {
             },
         })
     }
-
-    pub fn hardware_rev(&self) -> u8 {
-        self.event_timer_block_id.get_bits(0..8) as u8
-    }
-
-    pub fn num_comparators(&self) -> u8 {
-        self.event_timer_block_id.get_bits(8..13) as u8 + 1
-    }
-
-    pub fn main_counter_is_64bits(&self) -> bool {
-        self.event_timer_block_id.get_bit(13)
-    }
-
-    pub fn legacy_irq_capable(&self) -> bool {
-        self.event_timer_block_id.get_bit(15)
-    }
-
-    pub fn pci_vendor_id(&self) -> u16 {
-        self.event_timer_block_id.get_bits(16..32) as u16
-    }
 }
 
 #[repr(C, packed)]

From 0308864819eb7f5c0d6d4d33d8953ddcf0fe3b1b Mon Sep 17 00:00:00 2001
From: Isaac Woods <isaacwoods.home@gmail.com>
Date: Sat, 18 Jan 2025 15:56:32 +0000
Subject: [PATCH 05/88] `acpi`: make all table fields public

---
 acpi/src/address.rs |  9 +++----
 acpi/src/bgrt.rs    | 10 +++----
 acpi/src/fadt.rs    | 66 ++++++++++++++++++++++-----------------------
 acpi/src/hpet.rs    | 14 +++++-----
 acpi/src/madt.rs    |  4 +--
 acpi/src/mcfg.rs    |  2 +-
 acpi/src/rsdp.rs    | 18 ++++++-------
 acpi/src/spcr.rs    | 34 +++++++++++------------
 8 files changed, 77 insertions(+), 80 deletions(-)

diff --git a/acpi/src/address.rs b/acpi/src/address.rs
index 5854d987..df95160b 100644
--- a/acpi/src/address.rs
+++ b/acpi/src/address.rs
@@ -1,13 +1,12 @@
 //! ACPI defines a Generic Address Structure (GAS), which provides a versatile way to describe register locations
 //! in a wide range of address spaces.
 
-use crate::AcpiError;
+use crate::{AcpiError, AcpiResult};
 
-/// This is the raw form of a Generic Address Structure, and follows the layout found in the ACPI tables. It does
-/// not form part of the public API, and should be turned into a `GenericAddress` for most use-cases.
+/// This is the raw form of a Generic Address Structure, and follows the layout found in the ACPI tables.
 #[derive(Clone, Copy, Debug)]
 #[repr(C, packed)]
-pub(crate) struct RawGenericAddress {
+pub struct RawGenericAddress {
     pub address_space: u8,
     pub bit_width: u8,
     pub bit_offset: u8,
@@ -84,7 +83,7 @@ pub struct GenericAddress {
 }
 
 impl GenericAddress {
-    pub(crate) fn from_raw(raw: RawGenericAddress) -> crate::AcpiResult<GenericAddress> {
+    pub fn from_raw(raw: RawGenericAddress) -> AcpiResult<GenericAddress> {
         let address_space = match raw.address_space {
             0x00 => AddressSpace::SystemMemory,
             0x01 => AddressSpace::SystemIo,
diff --git a/acpi/src/bgrt.rs b/acpi/src/bgrt.rs
index ab231511..4eeadc84 100644
--- a/acpi/src/bgrt.rs
+++ b/acpi/src/bgrt.rs
@@ -9,13 +9,13 @@ use bit_field::BitField;
 #[repr(C, packed)]
 #[derive(Debug, Clone, Copy)]
 pub struct Bgrt {
-    header: SdtHeader,
+    pub header: SdtHeader,
     pub version: u16,
-    status: u8,
-    image_type: u8,
+    pub status: u8,
+    pub image_type: u8,
     pub image_address: u64,
-    image_offset_x: u32,
-    image_offset_y: u32,
+    pub image_offset_x: u32,
+    pub image_offset_y: u32,
 }
 
 /// ### Safety: Implementation properly represents a valid BGRT.
diff --git a/acpi/src/fadt.rs b/acpi/src/fadt.rs
index f97c4e61..8f8793f6 100644
--- a/acpi/src/fadt.rs
+++ b/acpi/src/fadt.rs
@@ -30,15 +30,15 @@ pub enum PowerProfile {
 #[repr(C, packed)]
 #[derive(Debug, Clone, Copy)]
 pub struct Fadt {
-    header: SdtHeader,
+    pub header: SdtHeader,
 
-    firmware_ctrl: u32,
-    dsdt_address: u32,
+    pub firmware_ctrl: u32,
+    pub dsdt_address: u32,
 
     // Used in acpi 1.0; compatibility only, should be zero
     _reserved: u8,
 
-    preferred_pm_profile: u8,
+    pub preferred_pm_profile: u8,
     /// On systems with an i8259 PIC, this is the vector the System Control Interrupt (SCI) is wired to. On other systems, this is
     /// the Global System Interrupt (GSI) number of the SCI.
     ///
@@ -64,20 +64,20 @@ pub struct Fadt {
     pub acpi_disable: u8,
     pub s4bios_req: u8,
     pub pstate_control: u8,
-    pm1a_event_block: u32,
-    pm1b_event_block: u32,
-    pm1a_control_block: u32,
-    pm1b_control_block: u32,
-    pm2_control_block: u32,
-    pm_timer_block: u32,
-    gpe0_block: u32,
-    gpe1_block: u32,
-    pm1_event_length: u8,
-    pm1_control_length: u8,
-    pm2_control_length: u8,
-    pm_timer_length: u8,
-    gpe0_block_length: u8,
-    gpe1_block_length: u8,
+    pub pm1a_event_block: u32,
+    pub pm1b_event_block: u32,
+    pub pm1a_control_block: u32,
+    pub pm1b_control_block: u32,
+    pub pm2_control_block: u32,
+    pub pm_timer_block: u32,
+    pub gpe0_block: u32,
+    pub gpe1_block: u32,
+    pub pm1_event_length: u8,
+    pub pm1_control_length: u8,
+    pub pm2_control_length: u8,
+    pub pm_timer_length: u8,
+    pub gpe0_block_length: u8,
+    pub gpe1_block_length: u8,
     pub gpe1_base: u8,
     pub c_state_control: u8,
     /// The worst-case latency to enter and exit the C2 state, in microseconds. A value `>100` indicates that the
@@ -96,23 +96,23 @@ pub struct Fadt {
     pub iapc_boot_arch: IaPcBootArchFlags,
     _reserved2: u8, // must be 0
     pub flags: FixedFeatureFlags,
-    reset_reg: RawGenericAddress,
+    pub reset_reg: RawGenericAddress,
     pub reset_value: u8,
     pub arm_boot_arch: ArmBootArchFlags,
-    fadt_minor_version: u8,
-    x_firmware_ctrl: ExtendedField<u64, 2>,
-    x_dsdt_address: ExtendedField<u64, 2>,
-    x_pm1a_event_block: ExtendedField<RawGenericAddress, 2>,
-    x_pm1b_event_block: ExtendedField<RawGenericAddress, 2>,
-    x_pm1a_control_block: ExtendedField<RawGenericAddress, 2>,
-    x_pm1b_control_block: ExtendedField<RawGenericAddress, 2>,
-    x_pm2_control_block: ExtendedField<RawGenericAddress, 2>,
-    x_pm_timer_block: ExtendedField<RawGenericAddress, 2>,
-    x_gpe0_block: ExtendedField<RawGenericAddress, 2>,
-    x_gpe1_block: ExtendedField<RawGenericAddress, 2>,
-    sleep_control_reg: ExtendedField<RawGenericAddress, 2>,
-    sleep_status_reg: ExtendedField<RawGenericAddress, 2>,
-    hypervisor_vendor_id: ExtendedField<u64, 2>,
+    pub fadt_minor_version: u8,
+    pub x_firmware_ctrl: ExtendedField<u64, 2>,
+    pub x_dsdt_address: ExtendedField<u64, 2>,
+    pub x_pm1a_event_block: ExtendedField<RawGenericAddress, 2>,
+    pub x_pm1b_event_block: ExtendedField<RawGenericAddress, 2>,
+    pub x_pm1a_control_block: ExtendedField<RawGenericAddress, 2>,
+    pub x_pm1b_control_block: ExtendedField<RawGenericAddress, 2>,
+    pub x_pm2_control_block: ExtendedField<RawGenericAddress, 2>,
+    pub x_pm_timer_block: ExtendedField<RawGenericAddress, 2>,
+    pub x_gpe0_block: ExtendedField<RawGenericAddress, 2>,
+    pub x_gpe1_block: ExtendedField<RawGenericAddress, 2>,
+    pub sleep_control_reg: ExtendedField<RawGenericAddress, 2>,
+    pub sleep_status_reg: ExtendedField<RawGenericAddress, 2>,
+    pub hypervisor_vendor_id: ExtendedField<u64, 2>,
 }
 
 /// ### Safety: Implementation properly represents a valid FADT.
diff --git a/acpi/src/hpet.rs b/acpi/src/hpet.rs
index ea314d1b..02033515 100644
--- a/acpi/src/hpet.rs
+++ b/acpi/src/hpet.rs
@@ -67,17 +67,15 @@ impl HpetInfo {
 #[repr(C, packed)]
 #[derive(Debug, Clone, Copy)]
 pub struct HpetTable {
-    /// The contents of the HPET's 'General Capabilities and ID register'
-    header: SdtHeader,
-    event_timer_block_id: u32,
-    base_address: RawGenericAddress,
-    hpet_number: u8,
-    clock_tick_unit: u16,
+    pub header: SdtHeader,
+    pub event_timer_block_id: u32,
+    pub base_address: RawGenericAddress,
+    pub hpet_number: u8,
+    pub clock_tick_unit: u16,
     /// Bits `0..4` specify the page protection guarantee. Bits `4..8` are reserved for OEM attributes.
-    page_protection_and_oem: u8,
+    pub page_protection_and_oem: u8,
 }
 
-/// ### Safety: Implementation properly represents a valid HPET table.
 unsafe impl AcpiTable for HpetTable {
     const SIGNATURE: Signature = Signature::HPET;
 
diff --git a/acpi/src/madt.rs b/acpi/src/madt.rs
index 9f1427dd..4b080008 100644
--- a/acpi/src/madt.rs
+++ b/acpi/src/madt.rs
@@ -523,7 +523,7 @@ pub struct LocalSapicEntry {
     /// namespace when the `_UID` object is a string. It is a null-terminated ASCII string, and so
     /// this field will be `'\0'` if the string is not present, otherwise it extends from the
     /// address of this field.
-    processor_uid_string: u8,
+    pub processor_uid_string: u8,
 }
 
 #[derive(Clone, Copy, Debug)]
@@ -676,7 +676,7 @@ pub struct MultiprocessorWakeupMailbox {
     pub apic_id: u32,
     pub wakeup_vector: u64,
     pub reserved_for_os: [u64; 254],
-    reserved_for_firmware: [u64; 256],
+    pub reserved_for_firmware: [u64; 256],
 }
 
 #[cfg(feature = "allocator_api")]
diff --git a/acpi/src/mcfg.rs b/acpi/src/mcfg.rs
index c09d44f0..e125c87e 100644
--- a/acpi/src/mcfg.rs
+++ b/acpi/src/mcfg.rs
@@ -101,7 +101,7 @@ impl Iterator for PciConfigEntryIterator<'_> {
 
 #[repr(C, packed)]
 pub struct Mcfg {
-    header: SdtHeader,
+    pub header: SdtHeader,
     _reserved: u64,
     // Followed by `n` entries with format `McfgEntry`
 }
diff --git a/acpi/src/rsdp.rs b/acpi/src/rsdp.rs
index 3c33e9c0..0c2eae0f 100644
--- a/acpi/src/rsdp.rs
+++ b/acpi/src/rsdp.rs
@@ -22,19 +22,19 @@ const RSDP_V2_EXT_LENGTH: usize = mem::size_of::<Rsdp>() - RSDP_V1_LENGTH;
 #[derive(Clone, Copy, Debug)]
 #[repr(C, packed)]
 pub struct Rsdp {
-    signature: [u8; 8],
-    checksum: u8,
-    oem_id: [u8; 6],
-    revision: u8,
-    rsdt_address: u32,
+    pub signature: [u8; 8],
+    pub checksum: u8,
+    pub oem_id: [u8; 6],
+    pub revision: u8,
+    pub rsdt_address: u32,
 
     /*
      * These fields are only valid for ACPI Version 2.0 and greater
      */
-    length: u32,
-    xsdt_address: u64,
-    ext_checksum: u8,
-    reserved: [u8; 3],
+    pub length: u32,
+    pub xsdt_address: u64,
+    pub ext_checksum: u8,
+    _reserved: [u8; 3],
 }
 
 impl Rsdp {
diff --git a/acpi/src/spcr.rs b/acpi/src/spcr.rs
index 5649dde4..1e1820c2 100644
--- a/acpi/src/spcr.rs
+++ b/acpi/src/spcr.rs
@@ -24,33 +24,33 @@ use core::{
 #[derive(Debug)]
 pub struct Spcr {
     pub header: SdtHeader,
-    interface_type: u8,
+    pub interface_type: u8,
     _reserved: [u8; 3],
-    base_address: RawGenericAddress,
-    interrupt_type: u8,
-    irq: u8,
-    global_system_interrupt: u32,
+    pub base_address: RawGenericAddress,
+    pub interrupt_type: u8,
+    pub irq: u8,
+    pub global_system_interrupt: u32,
     /// The baud rate the BIOS used for redirection.
-    configured_baud_rate: u8,
+    pub configured_baud_rate: u8,
     pub parity: u8,
     pub stop_bits: u8,
-    flow_control: u8,
-    terminal_type: u8,
+    pub flow_control: u8,
+    pub terminal_type: u8,
     /// Language which the BIOS was redirecting. Must be 0.
     pub language: u8,
-    pci_device_id: u16,
-    pci_vendor_id: u16,
-    pci_bus_number: u8,
-    pci_device_number: u8,
-    pci_function_number: u8,
+    pub pci_device_id: u16,
+    pub pci_vendor_id: u16,
+    pub pci_bus_number: u8,
+    pub pci_device_number: u8,
+    pub pci_function_number: u8,
     pub pci_flags: u32,
     /// PCI segment number. systems with fewer than 255 PCI buses, this number
     /// will be 0.
     pub pci_segment: u8,
-    uart_clock_freq: u32,
-    precise_baud_rate: u32,
-    namespace_string_length: u16,
-    namespace_string_offset: u16,
+    pub uart_clock_freq: u32,
+    pub precise_baud_rate: u32,
+    pub namespace_string_length: u16,
+    pub namespace_string_offset: u16,
 }
 
 unsafe impl AcpiTable for Spcr {

From 65f08e2d116cf1713b73ae9b5885b5e8c6e8d35a Mon Sep 17 00:00:00 2001
From: Isaac Woods <isaacwoods.home@gmail.com>
Date: Sat, 18 Jan 2025 15:15:19 +0000
Subject: [PATCH 06/88] `acpi`: make `wakeup_aps` unsafe

---
 acpi/src/platform/mod.rs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/acpi/src/platform/mod.rs b/acpi/src/platform/mod.rs
index bf4097c3..2347aa44 100644
--- a/acpi/src/platform/mod.rs
+++ b/acpi/src/platform/mod.rs
@@ -142,7 +142,7 @@ where
 /// - Paging mode is enabled and physical memory for waking vector is identity mapped (virtual address equals physical address).
 /// - Waking vector must be contained within one physical page.
 /// - Selectors are set to flat and otherwise not used.
-pub fn wakeup_aps<H>(
+pub unsafe fn wakeup_aps<H>(
     tables: &AcpiTables<H>,
     handler: H,
     apic_id: u32,

From 3577b5a43c2dc0a9780a8e2157bacc32450d9e2e Mon Sep 17 00:00:00 2001
From: Isaac Woods <isaacwoods.home@gmail.com>
Date: Sat, 18 Jan 2025 15:14:56 +0000
Subject: [PATCH 07/88] `acpi`: remove lifetime from `ManagedSlice`

I don't really understand why this had a lifetime instead of constructing
the slice for the lifetime of `self`, but this cleans stuff up a fair bit
both in the library and for users.
---
 acpi/src/madt.rs               |  8 ++++++--
 acpi/src/managed_slice.rs      | 36 ++++++++++++++++------------------
 acpi/src/mcfg.rs               | 12 ++++++------
 acpi/src/platform/interrupt.rs | 24 +++++++++++------------
 acpi/src/platform/mod.rs       | 18 ++++++++---------
 5 files changed, 50 insertions(+), 48 deletions(-)

diff --git a/acpi/src/madt.rs b/acpi/src/madt.rs
index 4b080008..cf8c6509 100644
--- a/acpi/src/madt.rs
+++ b/acpi/src/madt.rs
@@ -71,10 +71,10 @@ impl Madt {
     }
 
     #[cfg(feature = "allocator_api")]
-    pub fn parse_interrupt_model_in<'a, A>(
+    pub fn parse_interrupt_model_in<A>(
         self: Pin<&Self>,
         allocator: A,
-    ) -> AcpiResult<(InterruptModel<'a, A>, Option<ProcessorInfo<'a, A>>)>
+    ) -> AcpiResult<(InterruptModel<A>, Option<ProcessorInfo<A>>)>
     where
         A: core::alloc::Allocator + Clone,
     {
@@ -116,10 +116,14 @@ impl Madt {
     }
 
     #[cfg(feature = "allocator_api")]
+<<<<<<< HEAD
     fn parse_apic_model_in<'a, A>(
         self: Pin<&Self>,
         allocator: A,
     ) -> AcpiResult<(InterruptModel<'a, A>, Option<ProcessorInfo<'a, A>>)>
+=======
+    fn parse_apic_model_in<A>(&self, allocator: A) -> AcpiResult<(InterruptModel<A>, Option<ProcessorInfo<A>>)>
+>>>>>>> 25e9814 (`acpi`: remove lifetime from `ManagedSlice`)
     where
         A: core::alloc::Allocator + Clone,
     {
diff --git a/acpi/src/managed_slice.rs b/acpi/src/managed_slice.rs
index d6a8a287..e035b747 100644
--- a/acpi/src/managed_slice.rs
+++ b/acpi/src/managed_slice.rs
@@ -1,22 +1,25 @@
 use crate::{AcpiError, AcpiResult};
 use core::{
     alloc::{Allocator, Layout},
-    mem,
     ptr::NonNull,
 };
 
 /// Thin wrapper around a regular slice, taking a reference to an allocator for automatic
 /// deallocation when the slice is dropped out of scope.
 #[derive(Debug)]
-pub struct ManagedSlice<'a, T, A>
+pub struct ManagedSlice<T, A>
 where
     A: Allocator,
 {
-    slice: &'a mut [T],
+    inner: *mut T,
+    len: usize,
     allocator: A,
 }
 
-impl<T, A> ManagedSlice<'_, T, A>
+unsafe impl<T: Send, A: Allocator> Send for ManagedSlice<T, A> {}
+unsafe impl<T: Sync, A: Allocator> Sync for ManagedSlice<T, A> {}
+
+impl<T, A> ManagedSlice<T, A>
 where
     A: Allocator,
 {
@@ -24,57 +27,52 @@ where
     pub fn new_in(len: usize, allocator: A) -> AcpiResult<Self> {
         let layout = Layout::array::<T>(len).map_err(|_| AcpiError::AllocError)?;
         match allocator.allocate(layout) {
-            Ok(mut ptr) => {
-                let slice = unsafe { core::slice::from_raw_parts_mut(ptr.as_mut().as_mut_ptr().cast(), len) };
-                Ok(ManagedSlice { slice, allocator })
-            }
+            Ok(mut ptr) => Ok(ManagedSlice { inner: unsafe { ptr.as_mut().as_mut_ptr().cast() }, len, allocator }),
             Err(_) => Err(AcpiError::AllocError),
         }
     }
 }
 
 #[cfg(feature = "alloc")]
-impl<T> ManagedSlice<'_, T, alloc::alloc::Global> {
+impl<T> ManagedSlice<T, alloc::alloc::Global> {
     pub fn new(len: usize) -> AcpiResult<Self> {
         Self::new_in(len, alloc::alloc::Global)
     }
 }
 
-impl<T, A> Drop for ManagedSlice<'_, T, A>
+impl<T, A> Drop for ManagedSlice<T, A>
 where
     A: Allocator,
 {
     fn drop(&mut self) {
         unsafe {
-            let slice_ptr = NonNull::new_unchecked(self.slice.as_ptr().cast_mut().cast::<u8>());
-            let slice_layout =
-                Layout::from_size_align_unchecked(mem::size_of_val(self.slice), mem::align_of_val(self.slice));
-            self.allocator.deallocate(slice_ptr, slice_layout);
+            let layout = Layout::array::<T>(self.len).unwrap();
+            self.allocator.deallocate(NonNull::new_unchecked(self.inner as *mut u8), layout);
         }
     }
 }
 
-impl<T, A> core::ops::Deref for ManagedSlice<'_, T, A>
+impl<T, A> core::ops::Deref for ManagedSlice<T, A>
 where
     A: Allocator,
 {
     type Target = [T];
 
     fn deref(&self) -> &Self::Target {
-        self.slice
+        unsafe { core::slice::from_raw_parts(self.inner, self.len) }
     }
 }
 
-impl<T, A> core::ops::DerefMut for ManagedSlice<'_, T, A>
+impl<T, A> core::ops::DerefMut for ManagedSlice<T, A>
 where
     A: Allocator,
 {
     fn deref_mut(&mut self) -> &mut Self::Target {
-        self.slice
+        unsafe { core::slice::from_raw_parts_mut(self.inner, self.len) }
     }
 }
 
-impl<T: Clone, A: Allocator + Clone> Clone for ManagedSlice<'_, T, A> {
+impl<T: Clone, A: Allocator + Clone> Clone for ManagedSlice<T, A> {
     fn clone(&self) -> Self {
         let mut new_managed_slice = ManagedSlice::new_in(self.len(), self.allocator.clone()).unwrap();
         new_managed_slice.clone_from_slice(self);
diff --git a/acpi/src/mcfg.rs b/acpi/src/mcfg.rs
index e125c87e..911521e5 100644
--- a/acpi/src/mcfg.rs
+++ b/acpi/src/mcfg.rs
@@ -10,16 +10,16 @@ use core::{mem, slice};
 /// address of the start of that device function's configuration space (each function has 4096
 /// bytes of configuration space in PCIe).
 #[cfg(feature = "allocator_api")]
-pub struct PciConfigRegions<'a, A>
+pub struct PciConfigRegions<A>
 where
     A: core::alloc::Allocator,
 {
-    regions: crate::ManagedSlice<'a, McfgEntry, A>,
+    regions: crate::ManagedSlice<McfgEntry, A>,
 }
 
 #[cfg(feature = "alloc")]
-impl<'a> PciConfigRegions<'a, alloc::alloc::Global> {
-    pub fn new<H>(tables: &crate::AcpiTables<H>) -> crate::AcpiResult<PciConfigRegions<'a, alloc::alloc::Global>>
+impl<'a> PciConfigRegions<alloc::alloc::Global> {
+    pub fn new<H>(tables: &crate::AcpiTables<H>) -> crate::AcpiResult<PciConfigRegions<alloc::alloc::Global>>
     where
         H: crate::AcpiHandler,
     {
@@ -28,11 +28,11 @@ impl<'a> PciConfigRegions<'a, alloc::alloc::Global> {
 }
 
 #[cfg(feature = "allocator_api")]
-impl<'a, A> PciConfigRegions<'a, A>
+impl<A> PciConfigRegions<A>
 where
     A: core::alloc::Allocator,
 {
-    pub fn new_in<H>(tables: &crate::AcpiTables<H>, allocator: A) -> crate::AcpiResult<PciConfigRegions<'a, A>>
+    pub fn new_in<H>(tables: &crate::AcpiTables<H>, allocator: A) -> crate::AcpiResult<PciConfigRegions<A>>
     where
         H: crate::AcpiHandler,
     {
diff --git a/acpi/src/platform/interrupt.rs b/acpi/src/platform/interrupt.rs
index a83fcefe..a1c2efe7 100644
--- a/acpi/src/platform/interrupt.rs
+++ b/acpi/src/platform/interrupt.rs
@@ -77,15 +77,15 @@ pub struct NmiSource {
 }
 
 #[derive(Debug, Clone)]
-pub struct Apic<'a, A>
+pub struct Apic<A>
 where
     A: Allocator,
 {
     pub local_apic_address: u64,
-    pub io_apics: ManagedSlice<'a, IoApic, A>,
-    pub local_apic_nmi_lines: ManagedSlice<'a, NmiLine, A>,
-    pub interrupt_source_overrides: ManagedSlice<'a, InterruptSourceOverride, A>,
-    pub nmi_sources: ManagedSlice<'a, NmiSource, A>,
+    pub io_apics: ManagedSlice<IoApic, A>,
+    pub local_apic_nmi_lines: ManagedSlice<NmiLine, A>,
+    pub interrupt_source_overrides: ManagedSlice<InterruptSourceOverride, A>,
+    pub nmi_sources: ManagedSlice<NmiSource, A>,
 
     /// If this field is set, you must remap and mask all the lines of the legacy PIC, even if
     /// you choose to use the APIC. It's recommended that you do this even if ACPI does not
@@ -93,16 +93,16 @@ where
     pub also_has_legacy_pics: bool,
 }
 
-impl<'a, A> Apic<'a, A>
+impl<A> Apic<A>
 where
     A: Allocator,
 {
     pub(crate) fn new(
         local_apic_address: u64,
-        io_apics: ManagedSlice<'a, IoApic, A>,
-        local_apic_nmi_lines: ManagedSlice<'a, NmiLine, A>,
-        interrupt_source_overrides: ManagedSlice<'a, InterruptSourceOverride, A>,
-        nmi_sources: ManagedSlice<'a, NmiSource, A>,
+        io_apics: ManagedSlice<IoApic, A>,
+        local_apic_nmi_lines: ManagedSlice<NmiLine, A>,
+        interrupt_source_overrides: ManagedSlice<InterruptSourceOverride, A>,
+        nmi_sources: ManagedSlice<NmiSource, A>,
         also_has_legacy_pics: bool,
     ) -> Self {
         Self {
@@ -118,7 +118,7 @@ where
 
 #[derive(Debug, Clone)]
 #[non_exhaustive]
-pub enum InterruptModel<'a, A>
+pub enum InterruptModel<A>
 where
     A: Allocator,
 {
@@ -129,5 +129,5 @@ where
     /// Describes an interrupt controller based around the Advanced Programmable Interrupt Controller (any of APIC,
     /// XAPIC, or X2APIC). These are likely to be found on x86 and x86_64 systems and are made up of a Local APIC
     /// for each core and one or more I/O APICs to handle external interrupts.
-    Apic(Apic<'a, A>),
+    Apic(Apic<A>),
 }
diff --git a/acpi/src/platform/mod.rs b/acpi/src/platform/mod.rs
index 2347aa44..ca43d583 100644
--- a/acpi/src/platform/mod.rs
+++ b/acpi/src/platform/mod.rs
@@ -46,20 +46,20 @@ pub struct Processor {
 }
 
 #[derive(Debug, Clone)]
-pub struct ProcessorInfo<'a, A>
+pub struct ProcessorInfo<A>
 where
     A: Allocator,
 {
     pub boot_processor: Processor,
     /// Application processors should be brought up in the order they're defined in this list.
-    pub application_processors: ManagedSlice<'a, Processor, A>,
+    pub application_processors: ManagedSlice<Processor, A>,
 }
 
-impl<'a, A> ProcessorInfo<'a, A>
+impl<A> ProcessorInfo<A>
 where
     A: Allocator,
 {
-    pub(crate) fn new(boot_processor: Processor, application_processors: ManagedSlice<'a, Processor, A>) -> Self {
+    pub(crate) fn new(boot_processor: Processor, application_processors: ManagedSlice<Processor, A>) -> Self {
         Self { boot_processor, application_processors }
     }
 }
@@ -86,15 +86,15 @@ impl PmTimer {
 /// tables in a nice way. It requires access to the `FADT` and `MADT`. It is the easiest way to get information
 /// about the processors and interrupt controllers on a platform.
 #[derive(Debug, Clone)]
-pub struct PlatformInfo<'a, A>
+pub struct PlatformInfo<A>
 where
     A: Allocator,
 {
     pub power_profile: PowerProfile,
-    pub interrupt_model: InterruptModel<'a, A>,
+    pub interrupt_model: InterruptModel<A>,
     /// On `x86_64` platforms that support the APIC, the processor topology must also be inferred from the
     /// interrupt model. That information is stored here, if present.
-    pub processor_info: Option<ProcessorInfo<'a, A>>,
+    pub processor_info: Option<ProcessorInfo<A>>,
     pub pm_timer: Option<PmTimer>,
     /*
      * TODO: we could provide a nice view of the hardware register blocks in the FADT here.
@@ -102,7 +102,7 @@ where
 }
 
 #[cfg(feature = "alloc")]
-impl PlatformInfo<'_, alloc::alloc::Global> {
+impl PlatformInfo<alloc::alloc::Global> {
     pub fn new<H>(tables: &AcpiTables<H>) -> AcpiResult<Self>
     where
         H: AcpiHandler,
@@ -111,7 +111,7 @@ impl PlatformInfo<'_, alloc::alloc::Global> {
     }
 }
 
-impl<A> PlatformInfo<'_, A>
+impl<A> PlatformInfo<A>
 where
     A: Allocator + Clone,
 {

From 041f5ea9ad4513b4a948d5ca0837c7813742cc11 Mon Sep 17 00:00:00 2001
From: Isaac Woods <isaacwoods.home@gmail.com>
Date: Fri, 8 Nov 2024 20:06:06 +0000
Subject: [PATCH 08/88] Add doc comment for `Namespace::get_level_for_path`

---
 aml/src/namespace.rs | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/aml/src/namespace.rs b/aml/src/namespace.rs
index 8f196fcc..8de69e7f 100644
--- a/aml/src/namespace.rs
+++ b/aml/src/namespace.rs
@@ -289,6 +289,8 @@ impl Namespace {
         }
     }
 
+    /// 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(&self, path: &AmlName) -> Result<(&NamespaceLevel, NameSeg), AmlError> {
         assert_ne!(*path, AmlName::root());
 

From 6a6daf874c3c18bb91408be2650a87921f11d84e Mon Sep 17 00:00:00 2001
From: Isaac Woods <isaacwoods.home@gmail.com>
Date: Sat, 7 Sep 2024 16:30:28 +0100
Subject: [PATCH 09/88] Fix crates compiled on CI

---
 .github/workflows/build.yml | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index d20a65fb..31d953dc 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -40,8 +40,8 @@ jobs:
         target: ${{ matrix.target }}
         components: llvm-tools-preview
         
-    - name: Build rsdp, acpi, and aml
-      run: cargo build -p rsdp -p acpi -p aml --target $TARGET
+    - name: Build crates
+      run: cargo build -p acpi -p aml --target $TARGET
 
   test:
     runs-on: ubuntu-latest

From 05531428098c235ff066b6fb47b64bf051bc3604 Mon Sep 17 00:00:00 2001
From: Isaac Woods <isaacwoods.home@gmail.com>
Date: Sat, 7 Sep 2024 16:29:09 +0100
Subject: [PATCH 10/88] Reverse `reset` option to `combined` in AML tester

---
 .github/workflows/build.yml |  2 +-
 aml_tester/src/main.rs      | 13 +++++++++----
 2 files changed, 10 insertions(+), 5 deletions(-)

diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 31d953dc..16bcf746 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -63,7 +63,7 @@ jobs:
       run: cargo test --all
 
     - name: Run AML test suite
-      run: cargo run --bin aml_tester -- -p tests --reset
+      run: cargo run_tests -p tests
 
   clippy:
     runs-on: ubuntu-latest
diff --git a/aml_tester/src/main.rs b/aml_tester/src/main.rs
index 50fab073..060a78dd 100644
--- a/aml_tester/src/main.rs
+++ b/aml_tester/src/main.rs
@@ -35,8 +35,13 @@ fn main() -> std::io::Result<()> {
         .version("v0.1.0")
         .author("Isaac Woods")
         .about("Compiles and tests ASL files")
-        .arg(Arg::new("no_compile").long("no-compile").action(ArgAction::SetTrue).help("Don't compile asl to aml"))
-        .arg(Arg::new("reset").long("reset").action(ArgAction::SetTrue).help("Clear namespace after each file"))
+        .arg(Arg::new("no_compile").long("no-compile").action(ArgAction::SetTrue).help("Don't compile ASL to AML"))
+        .arg(
+            Arg::new("combined")
+                .long("combined")
+                .action(ArgAction::SetTrue)
+                .help("Don't clear the namespace between tests"),
+        )
         .arg(Arg::new("path").short('p').long("path").required(false).action(ArgAction::Set).value_name("DIR"))
         .arg(Arg::new("files").action(ArgAction::Append).value_name("FILE.{asl,aml}"))
         .group(ArgGroup::new("files_list").args(["path", "files"]).required(true));
@@ -142,7 +147,7 @@ fn main() -> std::io::Result<()> {
             }
         });
 
-    let user_wants_reset = matches.get_flag("reset");
+    let combined_test = matches.get_flag("combined");
     let mut context = AmlContext::new(Box::new(Handler), DebugVerbosity::None);
 
     let (passed, failed) = aml_files.fold((0, 0), |(passed, failed), file_entry| {
@@ -155,7 +160,7 @@ fn main() -> std::io::Result<()> {
 
         const AML_TABLE_HEADER_LENGTH: usize = 36;
 
-        if user_wants_reset {
+        if !combined_test {
             context = AmlContext::new(Box::new(Handler), DebugVerbosity::None);
         }
 

From 44ab4156ca83b9fa2c6c60089050d4b68e9e7b82 Mon Sep 17 00:00:00 2001
From: Isaac Woods <isaacwoods.home@gmail.com>
Date: Sat, 7 Sep 2024 14:53:47 +0100
Subject: [PATCH 11/88] Improve layout of AML test runner output

---
 aml_tester/src/main.rs | 73 ++++++++++++++++++++++++++++++++++--------
 1 file changed, 60 insertions(+), 13 deletions(-)

diff --git a/aml_tester/src/main.rs b/aml_tester/src/main.rs
index 060a78dd..00646a1d 100644
--- a/aml_tester/src/main.rs
+++ b/aml_tester/src/main.rs
@@ -113,14 +113,35 @@ fn main() -> std::io::Result<()> {
             _ => (passed, failed),
         });
         if passed + failed > 0 {
-            println!("Compiled {} ASL files: {} passed, {} failed.", passed + failed, passed, failed);
+            println!(
+                "Compiled {} ASL files: {}{} passed{}, {}{} failed{}",
+                passed + failed,
+                termion::color::Fg(termion::color::Green),
+                passed,
+                termion::style::Reset,
+                termion::color::Fg(termion::color::Red),
+                failed,
+                termion::style::Reset
+            );
             println!();
         }
     }
 
+    #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
+    enum TestResult {
+        /// The test passed.
+        Pass,
+        /// The test ASL failed compilation by `iasl`.
+        CompileFail,
+        /// Our interpreter failed to parse the resulting AML.
+        ParseFail,
+        // TODO: should we do this??
+        NotCompiled,
+    }
+
     // Make a list of the files we have processed, and skip them if we see them again
     let mut dedup_list: HashSet<PathBuf> = HashSet::new();
-    let summaries: RefCell<HashSet<(PathBuf, &str)>> = RefCell::new(HashSet::new());
+    let mut summaries: HashSet<(PathBuf, TestResult)> = HashSet::new();
     // Filter down to the final list of AML files
     let aml_files = compiled_files
         .iter()
@@ -129,11 +150,11 @@ fn main() -> std::io::Result<()> {
             CompilationOutcome::Newer(path) => Some(path.clone()),
             CompilationOutcome::Succeeded(path) => Some(path.clone()),
             CompilationOutcome::Failed(path) => {
-                summaries.borrow_mut().insert((path.clone(), "COMPILE FAILED"));
+                summaries.insert((path.clone(), TestResult::CompileFail));
                 None
             }
             CompilationOutcome::NotCompiled(path) => {
-                summaries.borrow_mut().insert((path.clone(), "NotCompiled"));
+                summaries.insert((path.clone(), TestResult::NotCompiled));
                 None
             }
             CompilationOutcome::Ignored => None,
@@ -145,16 +166,20 @@ fn main() -> std::io::Result<()> {
                 dedup_list.insert(path.clone());
                 true
             }
-        });
+        })
+        .collect::<Vec<_>>();
 
     let combined_test = matches.get_flag("combined");
     let mut context = AmlContext::new(Box::new(Handler), DebugVerbosity::None);
 
-    let (passed, failed) = aml_files.fold((0, 0), |(passed, failed), file_entry| {
+    let (passed, failed) = aml_files.into_iter().fold((0, 0), |(passed, failed), file_entry| {
         print!("Testing AML file: {:?}... ", file_entry);
         std::io::stdout().flush().unwrap();
 
-        let mut file = File::open(&file_entry).unwrap();
+        let Ok(mut file) = File::open(&file_entry) else {
+            summaries.insert((file_entry, TestResult::CompileFail));
+            return (passed, failed + 1);
+        };
         let mut contents = Vec::new();
         file.read_to_end(&mut contents).unwrap();
 
@@ -168,14 +193,14 @@ fn main() -> std::io::Result<()> {
             Ok(()) => {
                 println!("{}OK{}", termion::color::Fg(termion::color::Green), termion::style::Reset);
                 println!("Namespace: {:#?}", context.namespace);
-                summaries.borrow_mut().insert((file_entry, "PASS"));
+                summaries.insert((file_entry, TestResult::Pass));
                 (passed + 1, failed)
             }
 
             Err(err) => {
                 println!("{}Failed ({:?}){}", termion::color::Fg(termion::color::Red), err, termion::style::Reset);
                 println!("Namespace: {:#?}", context.namespace);
-                summaries.borrow_mut().insert((file_entry, "PARSE FAIL"));
+                summaries.insert((file_entry, TestResult::ParseFail));
                 (passed, failed + 1)
             }
         }
@@ -183,10 +208,32 @@ fn main() -> std::io::Result<()> {
 
     // Print summaries
     println!("Summary:");
-    for (file, status) in summaries.borrow().iter() {
-        println!("{:<50}: {}", file.to_str().unwrap(), status);
-    }
-    println!("\nTest results:\n\tpassed:{}\n\tfailed:{}", passed, failed);
+    for (file, status) in summaries.iter() {
+        let status = match status {
+            TestResult::Pass => {
+                format!("{}OK{}", termion::color::Fg(termion::color::Green), termion::style::Reset)
+            }
+            TestResult::CompileFail => {
+                format!("{}COMPILE FAIL{}", termion::color::Fg(termion::color::Red), termion::style::Reset)
+            }
+            TestResult::ParseFail => {
+                format!("{}PARSE FAIL{}", termion::color::Fg(termion::color::Red), termion::style::Reset)
+            }
+            TestResult::NotCompiled => {
+                format!("{}NOT COMPILED{}", termion::color::Fg(termion::color::Red), termion::style::Reset)
+            }
+        };
+        println!("\t{:<50}: {}", file.to_str().unwrap(), status);
+    }
+    println!(
+        "\nTest results: {}{} passed{}, {}{} failed{}",
+        termion::color::Fg(termion::color::Green),
+        passed,
+        termion::style::Reset,
+        termion::color::Fg(termion::color::Red),
+        failed,
+        termion::style::Reset
+    );
     Ok(())
 }
 

From 1849d79190b88dc3c2f52116f3f013eb02c35ff0 Mon Sep 17 00:00:00 2001
From: Isaac Woods <isaacwoods.home@gmail.com>
Date: Sat, 7 Sep 2024 14:24:08 +0100
Subject: [PATCH 12/88] Move `aml_tester` and `acpi_dumper` into tools
 subdirectory

---
 .cargo/config.toml                             | 2 ++
 Cargo.toml                                     | 2 +-
 {acpi-dumper => tools/acpi_dumper}/Cargo.toml  | 2 +-
 {acpi-dumper => tools/acpi_dumper}/src/main.rs | 0
 {aml_tester => tools/aml_tester}/Cargo.toml    | 2 +-
 {aml_tester => tools/aml_tester}/src/main.rs   | 0
 6 files changed, 5 insertions(+), 3 deletions(-)
 create mode 100644 .cargo/config.toml
 rename {acpi-dumper => tools/acpi_dumper}/Cargo.toml (93%)
 rename {acpi-dumper => tools/acpi_dumper}/src/main.rs (100%)
 rename {aml_tester => tools/aml_tester}/Cargo.toml (83%)
 rename {aml_tester => tools/aml_tester}/src/main.rs (100%)

diff --git a/.cargo/config.toml b/.cargo/config.toml
new file mode 100644
index 00000000..59da1056
--- /dev/null
+++ b/.cargo/config.toml
@@ -0,0 +1,2 @@
+[alias]
+run_tests = "run --manifest-path tools/aml_tester/Cargo.toml --"
diff --git a/Cargo.toml b/Cargo.toml
index a1c9d3f4..49754db0 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,3 +1,3 @@
 [workspace]
-members = ["rsdp", "acpi", "aml", "acpi-dumper", "aml_tester"]
+members = ["acpi", "aml", "tools/aml_tester", "tools/acpi_dumper"]
 resolver = "2"
diff --git a/acpi-dumper/Cargo.toml b/tools/acpi_dumper/Cargo.toml
similarity index 93%
rename from acpi-dumper/Cargo.toml
rename to tools/acpi_dumper/Cargo.toml
index f6b260bb..dd4ce277 100644
--- a/acpi-dumper/Cargo.toml
+++ b/tools/acpi_dumper/Cargo.toml
@@ -1,5 +1,5 @@
 [package]
-name = "acpi-dumper"
+name = "acpi_dumper"
 version = "0.0.0"
 publish = false
 authors = ["Isaac Woods", "Matt Taylor"]
diff --git a/acpi-dumper/src/main.rs b/tools/acpi_dumper/src/main.rs
similarity index 100%
rename from acpi-dumper/src/main.rs
rename to tools/acpi_dumper/src/main.rs
diff --git a/aml_tester/Cargo.toml b/tools/aml_tester/Cargo.toml
similarity index 83%
rename from aml_tester/Cargo.toml
rename to tools/aml_tester/Cargo.toml
index a5a3fb17..f429cc75 100644
--- a/aml_tester/Cargo.toml
+++ b/tools/aml_tester/Cargo.toml
@@ -8,4 +8,4 @@ edition = "2018"
 clap = "4"
 termion = "1"
 log = "0.4"
-aml = { path = "../aml" }
+aml = { path = "../../aml" }
diff --git a/aml_tester/src/main.rs b/tools/aml_tester/src/main.rs
similarity index 100%
rename from aml_tester/src/main.rs
rename to tools/aml_tester/src/main.rs

From 691252bf7ef0bc08dd9a9fd6398f7cacbfdd3560 Mon Sep 17 00:00:00 2001
From: Isaac Woods <isaacwoods.home@gmail.com>
Date: Sat, 7 Sep 2024 14:22:52 +0100
Subject: [PATCH 13/88] Remove old `rsdp` crate

---
 rsdp/Cargo.toml     |  12 ---
 rsdp/README.md      |  43 --------
 rsdp/src/handler.rs | 128 -----------------------
 rsdp/src/lib.rs     | 243 --------------------------------------------
 4 files changed, 426 deletions(-)
 delete mode 100644 rsdp/Cargo.toml
 delete mode 100644 rsdp/README.md
 delete mode 100644 rsdp/src/handler.rs
 delete mode 100644 rsdp/src/lib.rs

diff --git a/rsdp/Cargo.toml b/rsdp/Cargo.toml
deleted file mode 100644
index e51e73f6..00000000
--- a/rsdp/Cargo.toml
+++ /dev/null
@@ -1,12 +0,0 @@
-[package]
-name = "rsdp"
-version = "2.0.1"
-authors = ["Isaac Woods", "Restioson"]
-repository = "https://github.com/rust-osdev/acpi"
-description = "Zero-allocation library for locating and parsing the RSDP, the first ACPI table"
-categories = ["hardware-support", "no-std"]
-license = "MIT/Apache-2.0"
-edition = "2021"
-
-[dependencies]
-log = "0.4"
diff --git a/rsdp/README.md b/rsdp/README.md
deleted file mode 100644
index bdf379e0..00000000
--- a/rsdp/README.md
+++ /dev/null
@@ -1,43 +0,0 @@
-# Acpi
-⚠️**WARNING: The `rsdp` crate was previously a component of the `acpi` ecosystem, but has been deprecated. Its
-functionality is now entirely supported by the `acpi` crate, including a subset of functionality that will work in
-an environment that does not have an allocator. This crate will likely not receive further updates**⚠️
-
-![Build Status](https://github.com/rust-osdev/acpi/actions/workflows/build.yml/badge.svg)
-[![Version](https://img.shields.io/crates/v/rsdp.svg?style=rounded-square)](https://crates.io/crates/rsdp/)
-[![Version](https://img.shields.io/crates/v/acpi.svg?style=rounded-square)](https://crates.io/crates/acpi/)
-[![Version](https://img.shields.io/crates/v/aml.svg?style=rounded-square)](https://crates.io/crates/aml/)
-
-### [Documentation (`rsdp`)](https://docs.rs/rsdp)
-### [Documentation (`acpi`)](https://docs.rs/acpi)
-### [Documentation (`aml`)](https://docs.rs/aml)
-
-A library to parse ACPI tables and AML, written in pure Rust. Designed to be easy to use from Rust bootloaders and kernels. The library is split into three crates:
-- `rsdp` parses the RSDP and can locate it on BIOS platforms. It does not depend on `alloc`, so is suitable to use from bootloaders without heap alloctors. All of its
-  functionality is reexported by `acpi`.
-- `acpi` parses the static tables (useful but not feature-complete). It can be used from environments that have allocators, and ones that don't (but with reduced functionality).
-- `aml` parses the AML tables (can be useful, far from feature-complete).
-
-There is also the `acpi-dumper` utility to easily dump a platform's ACPI tables (this currently only works on Linux).
-
-## Contributing
-Contributions are more than welcome! You can:
-- Write code - the ACPI spec is huge and there are bound to be things we don't support yet!
-- Improve our documentation!
-- Use the crates within your kernel and file bug reports and feature requests!
-
-Useful resources for contributing are:
-- [The ACPI specification](https://uefi.org/specifications)
-- [OSDev Wiki](https://wiki.osdev.org/ACPI)
-
-You can run the AML test suite with `cargo run --bin aml_tester -- -p tests`.
-You can run fuzz the AML parser with `cd aml && cargo fuzz run fuzz_target_1` (you may need to `cargo install cargo-fuzz`).
-
-## Licence
-This project is dual-licenced under:
-- Apache Licence, Version 2.0 ([LICENCE-APACHE](LICENCE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
-- MIT license ([LICENCE-MIT](LICENCE-MIT) or http://opensource.org/licenses/MIT)
-
-Unless you explicitly state otherwise, any contribution submitted for inclusion in this work by you,
-as defined in the Apache-2.0 licence, shall be dual licenced as above, without additional terms or
-conditions.
diff --git a/rsdp/src/handler.rs b/rsdp/src/handler.rs
deleted file mode 100644
index 6cc0cd05..00000000
--- a/rsdp/src/handler.rs
+++ /dev/null
@@ -1,128 +0,0 @@
-use core::{ops::Deref, ptr::NonNull};
-
-/// Describes a physical mapping created by `AcpiHandler::map_physical_region` and unmapped by
-/// `AcpiHandler::unmap_physical_region`. The region mapped must be at least `size_of::<T>()`
-/// bytes, but may be bigger.
-///
-/// See `PhysicalMapping::new` for the meaning of each field.
-#[derive(Debug)]
-pub struct PhysicalMapping<H, T>
-where
-    H: AcpiHandler,
-{
-    physical_start: usize,
-    virtual_start: NonNull<T>,
-    region_length: usize, // Can be equal or larger than size_of::<T>()
-    mapped_length: usize, // Differs from `region_length` if padding is added for alignment
-    handler: H,
-}
-
-impl<H, T> PhysicalMapping<H, T>
-where
-    H: AcpiHandler,
-{
-    /// Construct a new `PhysicalMapping`.
-    ///
-    /// - `physical_start` should be the physical address of the structure to be mapped.
-    /// - `virtual_start` should be the corresponding virtual address of that structure. It may differ from the
-    ///   start of the region mapped due to requirements of the paging system. It must be a valid, non-null
-    ///   pointer.
-    /// - `region_length` should be the number of bytes requested to be mapped. It must be equal to or larger than
-    ///   `size_of::<T>()`.
-    /// - `mapped_length` should be the number of bytes mapped to fulfill the request. It may be equal to or larger
-    ///   than `region_length`, due to requirements of the paging system or other reasoning.
-    /// - `handler` should be the same `AcpiHandler` that created the mapping. When the `PhysicalMapping` is
-    ///   dropped, it will be used to unmap the structure.
-    pub unsafe fn new(
-        physical_start: usize,
-        virtual_start: NonNull<T>,
-        region_length: usize,
-        mapped_length: usize,
-        handler: H,
-    ) -> Self {
-        Self { physical_start, virtual_start, region_length, mapped_length, handler }
-    }
-
-    pub fn physical_start(&self) -> usize {
-        self.physical_start
-    }
-
-    pub fn virtual_start(&self) -> NonNull<T> {
-        self.virtual_start
-    }
-
-    pub fn region_length(&self) -> usize {
-        self.region_length
-    }
-
-    pub fn mapped_length(&self) -> usize {
-        self.mapped_length
-    }
-
-    pub fn handler(&self) -> &H {
-        &self.handler
-    }
-}
-
-unsafe impl<H: AcpiHandler + Send, T: Send> Send for PhysicalMapping<H, T> {}
-
-impl<H, T> Deref for PhysicalMapping<H, T>
-where
-    H: AcpiHandler,
-{
-    type Target = T;
-
-    fn deref(&self) -> &T {
-        unsafe { self.virtual_start.as_ref() }
-    }
-}
-
-impl<H, T> Drop for PhysicalMapping<H, T>
-where
-    H: AcpiHandler,
-{
-    fn drop(&mut self) {
-        H::unmap_physical_region(self)
-    }
-}
-
-/// An implementation of this trait must be provided to allow `acpi` to access platform-specific
-/// functionality, such as mapping regions of physical memory. You are free to implement these
-/// however you please, as long as they conform to the documentation of each function. The handler is stored in
-/// every `PhysicalMapping` so it's able to unmap itself when dropped, so this type needs to be something you can
-/// clone/move about freely (e.g. a reference, wrapper over `Rc`, marker struct, etc.).
-pub trait AcpiHandler: Clone {
-    /// Given a physical address and a size, map a region of physical memory that contains `T` (note: the passed
-    /// size may be larger than `size_of::<T>()`). The address is not neccessarily page-aligned, so the
-    /// implementation may need to map more than `size` bytes. The virtual address the region is mapped to does not
-    /// matter, as long as it is accessible to `acpi`.
-    ///
-    /// See the documentation on `PhysicalMapping::new` for an explanation of each field on the `PhysicalMapping`
-    /// return type.
-    ///
-    /// ## Safety
-    ///
-    /// - `physical_address` must point to a valid `T` in physical memory.
-    /// - `size` must be at least `size_of::<T>()`.
-    unsafe fn map_physical_region<T>(&self, physical_address: usize, size: usize) -> PhysicalMapping<Self, T>;
-
-    /// Unmap the given physical mapping. This is called when a `PhysicalMapping` is dropped, you should **not** manually call this.
-    ///
-    /// Note: A reference to the handler used to construct `region` can be acquired by calling [`PhysicalMapping::handler`].
-    fn unmap_physical_region<T>(region: &PhysicalMapping<Self, T>);
-}
-
-#[cfg(test)]
-mod tests {
-    use super::*;
-
-    #[test]
-    #[allow(dead_code)]
-    fn test_send_sync() {
-        // verify that PhysicalMapping implements Send and Sync
-        fn test_send_sync<T: Send>() {}
-        fn caller<H: AcpiHandler + Send, T: Send>() {
-            test_send_sync::<PhysicalMapping<H, T>>();
-        }
-    }
-}
diff --git a/rsdp/src/lib.rs b/rsdp/src/lib.rs
deleted file mode 100644
index d2dffe04..00000000
--- a/rsdp/src/lib.rs
+++ /dev/null
@@ -1,243 +0,0 @@
-//! This crate provides types for representing the RSDP (the Root System Descriptor Table; the first ACPI table)
-//! and methods for searching for it on BIOS systems. Importantly, this crate (unlike `acpi`, which re-exports the
-//! contents of this crate) does not need `alloc`, and so can be used in environments that can't allocate. This is
-//! specifically meant to be used from bootloaders for finding the RSDP, so it can be passed to the payload. If you
-//! don't have this requirement, and want to do more than just find the RSDP, you can use `acpi` instead of this
-//! crate.
-//!
-//! To use this crate, you will need to provide an implementation of `AcpiHandler`. This is the same handler type
-//! used in the `acpi` crate.
-
-#![no_std]
-#![deny(unsafe_op_in_unsafe_fn)]
-
-#[cfg(test)]
-extern crate std;
-
-pub mod handler;
-
-use core::{mem, ops::Range, slice, str};
-use handler::{AcpiHandler, PhysicalMapping};
-use log::warn;
-
-#[derive(Clone, Copy, PartialEq, Eq, Debug)]
-pub enum RsdpError {
-    NoValidRsdp,
-    IncorrectSignature,
-    InvalidOemId,
-    InvalidChecksum,
-}
-
-/// The size in bytes of the ACPI 1.0 RSDP.
-const RSDP_V1_LENGTH: usize = 20;
-/// The total size in bytes of the RSDP fields introduced in ACPI 2.0.
-const RSDP_V2_EXT_LENGTH: usize = mem::size_of::<Rsdp>() - RSDP_V1_LENGTH;
-
-/// The first structure found in ACPI. It just tells us where the RSDT is.
-///
-/// On BIOS systems, it is either found in the first 1KB of the Extended Bios Data Area, or between
-/// 0x000E0000 and 0x000FFFFF. The signature is always on a 16 byte boundary. On (U)EFI, it may not
-/// be located in these locations, and so an address should be found in the EFI configuration table
-/// instead.
-///
-/// The recommended way of locating the RSDP is to let the bootloader do it - Multiboot2 can pass a
-/// tag with the physical address of it. If this is not possible, a manual scan can be done.
-///
-/// If `revision > 0`, (the hardware ACPI version is Version 2.0 or greater), the RSDP contains
-/// some new fields. For ACPI Version 1.0, these fields are not valid and should not be accessed.
-/// For ACPI Version 2.0+, `xsdt_address` should be used (truncated to `u32` on x86) instead of
-/// `rsdt_address`.
-#[derive(Clone, Copy, Debug)]
-#[repr(C, packed)]
-pub struct Rsdp {
-    signature: [u8; 8],
-    checksum: u8,
-    oem_id: [u8; 6],
-    revision: u8,
-    rsdt_address: u32,
-
-    /*
-     * These fields are only valid for ACPI Version 2.0 and greater
-     */
-    length: u32,
-    xsdt_address: u64,
-    ext_checksum: u8,
-    reserved: [u8; 3],
-}
-
-impl Rsdp {
-    /// This searches for a RSDP on BIOS systems.
-    ///
-    /// ### Safety
-    /// This function probes memory in three locations:
-    ///    - It reads a word from `40:0e` to locate the EBDA.
-    ///    - The first 1KiB of the EBDA (Extended BIOS Data Area).
-    ///    - The BIOS memory area at `0xe0000..=0xfffff`.
-    ///
-    /// This should be fine on all BIOS systems. However, UEFI platforms are free to put the RSDP wherever they
-    /// please, so this won't always find the RSDP. Further, prodding these memory locations may have unintended
-    /// side-effects. On UEFI systems, the RSDP should be found in the Configuration Table, using two GUIDs:
-    ///     - ACPI v1.0 structures use `eb9d2d30-2d88-11d3-9a16-0090273fc14d`.
-    ///     - ACPI v2.0 or later structures use `8868e871-e4f1-11d3-bc22-0080c73c8881`.
-    /// You should search the entire table for the v2.0 GUID before searching for the v1.0 one.
-    pub unsafe fn search_for_on_bios<H>(handler: H) -> Result<PhysicalMapping<H, Rsdp>, RsdpError>
-    where
-        H: AcpiHandler,
-    {
-        let rsdp_address = find_search_areas(handler.clone()).iter().find_map(|area| {
-            // Map the search area for the RSDP followed by `RSDP_V2_EXT_LENGTH` bytes so an ACPI 1.0 RSDP at the
-            // end of the area can be read as an `Rsdp` (which always has the size of an ACPI 2.0 RSDP)
-            let mapping = unsafe {
-                handler.map_physical_region::<u8>(area.start, area.end - area.start + RSDP_V2_EXT_LENGTH)
-            };
-
-            let extended_area_bytes =
-                unsafe { slice::from_raw_parts(mapping.virtual_start().as_ptr(), mapping.region_length()) };
-
-            // Search `Rsdp`-sized windows at 16-byte boundaries relative to the base of the area (which is also
-            // aligned to 16 bytes due to the implementation of `find_search_areas`)
-            extended_area_bytes.windows(mem::size_of::<Rsdp>()).step_by(16).find_map(|maybe_rsdp_bytes_slice| {
-                let maybe_rsdp_virt_ptr = maybe_rsdp_bytes_slice.as_ptr().cast::<Rsdp>();
-                let maybe_rsdp_phys_start = maybe_rsdp_virt_ptr as usize
-                    - mapping.virtual_start().as_ptr() as usize
-                    + mapping.physical_start();
-                // SAFETY: `maybe_rsdp_virt_ptr` points to an aligned, readable `Rsdp`-sized value, and the `Rsdp`
-                // struct's fields are always initialized.
-                let maybe_rsdp = unsafe { &*maybe_rsdp_virt_ptr };
-
-                match maybe_rsdp.validate() {
-                    Ok(()) => Some(maybe_rsdp_phys_start),
-                    Err(RsdpError::IncorrectSignature) => None,
-                    Err(e) => {
-                        warn!("Invalid RSDP found at {:#x}: {:?}", maybe_rsdp_phys_start, e);
-
-                        None
-                    }
-                }
-            })
-        });
-
-        match rsdp_address {
-            Some(address) => {
-                let rsdp_mapping = unsafe { handler.map_physical_region::<Rsdp>(address, mem::size_of::<Rsdp>()) };
-                Ok(rsdp_mapping)
-            }
-            None => Err(RsdpError::NoValidRsdp),
-        }
-    }
-
-    /// Checks that:
-    ///     1) The signature is correct
-    ///     2) The checksum is correct
-    ///     3) For Version 2.0+, that the extension checksum is correct
-    pub fn validate(&self) -> Result<(), RsdpError> {
-        // Check the signature
-        if self.signature != RSDP_SIGNATURE {
-            return Err(RsdpError::IncorrectSignature);
-        }
-
-        // Check the OEM id is valid UTF8 (allows use of unwrap)
-        if str::from_utf8(&self.oem_id).is_err() {
-            return Err(RsdpError::InvalidOemId);
-        }
-
-        /*
-         * `self.length` doesn't exist on ACPI version 1.0, so we mustn't rely on it. Instead,
-         * check for version 1.0 and use a hard-coded length instead.
-         */
-        let length = if self.revision > 0 {
-            // For Version 2.0+, include the number of bytes specified by `length`
-            self.length as usize
-        } else {
-            RSDP_V1_LENGTH
-        };
-
-        let bytes = unsafe { slice::from_raw_parts(self as *const Rsdp as *const u8, length) };
-        let sum = bytes.iter().fold(0u8, |sum, &byte| sum.wrapping_add(byte));
-
-        if sum != 0 {
-            return Err(RsdpError::InvalidChecksum);
-        }
-
-        Ok(())
-    }
-
-    pub fn signature(&self) -> [u8; 8] {
-        self.signature
-    }
-
-    pub fn checksum(&self) -> u8 {
-        self.checksum
-    }
-
-    pub fn oem_id(&self) -> &str {
-        str::from_utf8(&self.oem_id).unwrap()
-    }
-
-    pub fn revision(&self) -> u8 {
-        self.revision
-    }
-
-    pub fn rsdt_address(&self) -> u32 {
-        self.rsdt_address
-    }
-
-    pub fn length(&self) -> u32 {
-        assert!(self.revision > 0, "Tried to read extended RSDP field with ACPI Version 1.0");
-        self.length
-    }
-
-    pub fn xsdt_address(&self) -> u64 {
-        assert!(self.revision > 0, "Tried to read extended RSDP field with ACPI Version 1.0");
-        self.xsdt_address
-    }
-
-    pub fn ext_checksum(&self) -> u8 {
-        assert!(self.revision > 0, "Tried to read extended RSDP field with ACPI Version 1.0");
-        self.ext_checksum
-    }
-}
-
-/// Find the areas we should search for the RSDP in.
-pub fn find_search_areas<H>(handler: H) -> [Range<usize>; 2]
-where
-    H: AcpiHandler,
-{
-    /*
-     * Read the base address of the EBDA from its location in the BDA (BIOS Data Area). Not all BIOSs fill this out
-     * unfortunately, so we might not get a sensible result. We shift it left 4, as it's a segment address.
-     */
-    let ebda_start_mapping =
-        unsafe { handler.map_physical_region::<u16>(EBDA_START_SEGMENT_PTR, mem::size_of::<u16>()) };
-    let ebda_start = (*ebda_start_mapping as usize) << 4;
-
-    [
-        /*
-         * The main BIOS area below 1MiB. In practice, from my [Restioson's] testing, the RSDP is more often here
-         * than the EBDA. We also don't want to search the entire possibele EBDA range, if we've failed to find it
-         * from the BDA.
-         */
-        RSDP_BIOS_AREA_START..(RSDP_BIOS_AREA_END + 1),
-        // Check if base segment ptr is in valid range for EBDA base
-        if (EBDA_EARLIEST_START..EBDA_END).contains(&ebda_start) {
-            // First KiB of EBDA
-            ebda_start..ebda_start + 1024
-        } else {
-            // We don't know where the EBDA starts, so just search the largest possible EBDA
-            EBDA_EARLIEST_START..(EBDA_END + 1)
-        },
-    ]
-}
-
-/// This (usually!) contains the base address of the EBDA (Extended Bios Data Area), shifted right by 4
-const EBDA_START_SEGMENT_PTR: usize = 0x40e;
-/// The earliest (lowest) memory address an EBDA (Extended Bios Data Area) can start
-const EBDA_EARLIEST_START: usize = 0x80000;
-/// The end of the EBDA (Extended Bios Data Area)
-const EBDA_END: usize = 0x9ffff;
-/// The start of the main BIOS area below 1mb in which to search for the RSDP (Root System Description Pointer)
-const RSDP_BIOS_AREA_START: usize = 0xe0000;
-/// The end of the main BIOS area below 1mb in which to search for the RSDP (Root System Description Pointer)
-const RSDP_BIOS_AREA_END: usize = 0xfffff;
-/// The RSDP (Root System Description Pointer)'s signature, "RSD PTR " (note trailing space)
-const RSDP_SIGNATURE: [u8; 8] = *b"RSD PTR ";

From b337022dba80fa5a9d22f076c66ce8bcc1e01ed9 Mon Sep 17 00:00:00 2001
From: Isaac Woods <isaacwoods.home@gmail.com>
Date: Sat, 7 Sep 2024 14:21:52 +0100
Subject: [PATCH 14/88] Remove unmaintained changelog

---
 CHANGELOG.md | 4 ----
 1 file changed, 4 deletions(-)
 delete mode 100644 CHANGELOG.md

diff --git a/CHANGELOG.md b/CHANGELOG.md
deleted file mode 100644
index 0b2f174f..00000000
--- a/CHANGELOG.md
+++ /dev/null
@@ -1,4 +0,0 @@
-# `acpi v4.1.1` - 2022-08-01
-### Bug Fixes
-- Fix a bug with how the number of comparators the HPET provides is calculated
-    ([#121](https://github.com/rust-osdev/acpi/pull/121))

From 33057d7685c4666259f31fe86ea7661dc1199b5f Mon Sep 17 00:00:00 2001
From: Isaac Woods <isaacwoods.home@gmail.com>
Date: Thu, 30 Jan 2025 18:34:15 +0000
Subject: [PATCH 15/88] Clean-up a few straggling bits

---
 acpi/src/handler.rs          |  7 ++++++-
 acpi/src/madt.rs             | 11 +++--------
 tools/aml_tester/src/main.rs |  3 +--
 3 files changed, 10 insertions(+), 11 deletions(-)

diff --git a/acpi/src/handler.rs b/acpi/src/handler.rs
index 3a954511..85bbaecb 100644
--- a/acpi/src/handler.rs
+++ b/acpi/src/handler.rs
@@ -1,4 +1,9 @@
-use core::{fmt, ops::{Deref, DerefMut}, pin::Pin, ptr::NonNull};
+use core::{
+    fmt,
+    ops::{Deref, DerefMut},
+    pin::Pin,
+    ptr::NonNull,
+};
 
 /// Describes a physical mapping created by `AcpiHandler::map_physical_region` and unmapped by
 /// `AcpiHandler::unmap_physical_region`. The region mapped must be at least `size_of::<T>()`
diff --git a/acpi/src/madt.rs b/acpi/src/madt.rs
index cf8c6509..a7c6408d 100644
--- a/acpi/src/madt.rs
+++ b/acpi/src/madt.rs
@@ -51,7 +51,6 @@ pub struct Madt {
     _pinned: PhantomPinned,
 }
 
-/// ### Safety: Implementation properly represents a valid MADT.
 unsafe impl AcpiTable for Madt {
     const SIGNATURE: Signature = Signature::MADT;
 
@@ -116,14 +115,10 @@ impl Madt {
     }
 
     #[cfg(feature = "allocator_api")]
-<<<<<<< HEAD
-    fn parse_apic_model_in<'a, A>(
+    fn parse_apic_model_in<A>(
         self: Pin<&Self>,
         allocator: A,
-    ) -> AcpiResult<(InterruptModel<'a, A>, Option<ProcessorInfo<'a, A>>)>
-=======
-    fn parse_apic_model_in<A>(&self, allocator: A) -> AcpiResult<(InterruptModel<A>, Option<ProcessorInfo<A>>)>
->>>>>>> 25e9814 (`acpi`: remove lifetime from `ManagedSlice`)
+    ) -> AcpiResult<(InterruptModel<A>, Option<ProcessorInfo<A>>)>
     where
         A: core::alloc::Allocator + Clone,
     {
@@ -335,7 +330,7 @@ impl Madt {
     }
 
     pub fn supports_8259(&self) -> bool {
-        { self.flags }.get_bit(0)
+        self.flags.get_bit(0)
     }
 }
 
diff --git a/tools/aml_tester/src/main.rs b/tools/aml_tester/src/main.rs
index 00646a1d..1d8788a3 100644
--- a/tools/aml_tester/src/main.rs
+++ b/tools/aml_tester/src/main.rs
@@ -12,8 +12,7 @@
 use aml::{AmlContext, DebugVerbosity};
 use clap::{Arg, ArgAction, ArgGroup};
 use std::{
-    cell::RefCell,
-    collections::{HashMap, HashSet},
+    collections::HashSet,
     ffi::OsStr,
     fs::{self, File},
     io::{Read, Write},

From 04bbfa752b74167de591c7af416a5f54ae54b7ae Mon Sep 17 00:00:00 2001
From: Isaac Woods <isaacwoods.home@gmail.com>
Date: Thu, 6 Feb 2025 19:31:26 +0000
Subject: [PATCH 16/88] Start complete rewrite of AML interpreter

---
 aml/Cargo.toml                         |   11 +-
 aml/fuzz/.gitignore                    |    4 -
 aml/fuzz/Cargo.toml                    |   27 -
 aml/fuzz/fuzz_targets/fuzz_target_1.rs |   68 --
 aml/src/expression.rs                  |  919 ----------------
 aml/src/lib.rs                         | 1327 ++++++++++++++----------
 aml/src/misc.rs                        |   83 --
 aml/src/name_object.rs                 |  305 ------
 aml/src/namespace.rs                   |  708 +++----------
 aml/src/object.rs                      |   43 +
 aml/src/opcode.rs                      |  163 ---
 aml/src/opregion.rs                    |  301 ------
 aml/src/parser.rs                      |  535 ----------
 aml/src/pci_routing.rs                 |  186 ----
 aml/src/pkg_length.rs                  |  214 ----
 aml/src/resource.rs                    |  850 ---------------
 aml/src/statement.rs                   |  328 ------
 aml/src/term_object.rs                 | 1094 -------------------
 aml/src/test_utils.rs                  |  209 ----
 aml/src/value.rs                       |  601 -----------
 tools/aml_tester/src/main.rs           |   13 +-
 21 files changed, 964 insertions(+), 7025 deletions(-)
 delete mode 100644 aml/fuzz/.gitignore
 delete mode 100644 aml/fuzz/Cargo.toml
 delete mode 100644 aml/fuzz/fuzz_targets/fuzz_target_1.rs
 delete mode 100644 aml/src/expression.rs
 delete mode 100644 aml/src/misc.rs
 delete mode 100644 aml/src/name_object.rs
 create mode 100644 aml/src/object.rs
 delete mode 100644 aml/src/opcode.rs
 delete mode 100644 aml/src/opregion.rs
 delete mode 100644 aml/src/parser.rs
 delete mode 100644 aml/src/pci_routing.rs
 delete mode 100644 aml/src/pkg_length.rs
 delete mode 100644 aml/src/resource.rs
 delete mode 100644 aml/src/statement.rs
 delete mode 100644 aml/src/term_object.rs
 delete mode 100644 aml/src/test_utils.rs
 delete mode 100644 aml/src/value.rs

diff --git a/aml/Cargo.toml b/aml/Cargo.toml
index 2b79b3dc..eef7eae7 100644
--- a/aml/Cargo.toml
+++ b/aml/Cargo.toml
@@ -1,17 +1,14 @@
 [package]
 name = "aml"
-version = "0.16.4"
+version = "0.17.0"
+edition = "2024"
 authors = ["Isaac Woods"]
 repository = "https://github.com/rust-osdev/acpi"
 description = "Library for parsing AML"
 categories = ["hardware-support", "no-std"]
 readme = "../README.md"
 license = "MIT/Apache-2.0"
-edition = "2021"
 
 [dependencies]
-log = "0.4"
-bit_field = "0.10"
-byteorder = { version = "1", default-features = false }
-bitvec = { version = "1.0.1", default-features = false, features = ["alloc", "atomic"] }
-spinning_top = "0.2.4"
+spinning_top = "0.3.0"
+bit_field = "0.10.2"
diff --git a/aml/fuzz/.gitignore b/aml/fuzz/.gitignore
deleted file mode 100644
index 572e03bd..00000000
--- a/aml/fuzz/.gitignore
+++ /dev/null
@@ -1,4 +0,0 @@
-
-target
-corpus
-artifacts
diff --git a/aml/fuzz/Cargo.toml b/aml/fuzz/Cargo.toml
deleted file mode 100644
index dadf5eea..00000000
--- a/aml/fuzz/Cargo.toml
+++ /dev/null
@@ -1,27 +0,0 @@
-
-[package]
-name = "aml-fuzz"
-version = "0.0.0"
-authors = ["Automatically generated"]
-publish = false
-edition = "2018"
-
-[package.metadata]
-cargo-fuzz = true
-
-[dependencies]
-libfuzzer-sys = "0.3"
-simplelog = "0.8"
-
-[dependencies.aml]
-path = ".."
-
-# Prevent this from interfering with workspaces
-[workspace]
-members = ["."]
-
-[[bin]]
-name = "fuzz_target_1"
-path = "fuzz_targets/fuzz_target_1.rs"
-test = false
-doc = false
diff --git a/aml/fuzz/fuzz_targets/fuzz_target_1.rs b/aml/fuzz/fuzz_targets/fuzz_target_1.rs
deleted file mode 100644
index e2e452e8..00000000
--- a/aml/fuzz/fuzz_targets/fuzz_target_1.rs
+++ /dev/null
@@ -1,68 +0,0 @@
-#![no_main]
-use libfuzzer_sys::fuzz_target;
-extern crate aml;
-
-use std::sync::atomic::{AtomicBool, Ordering};
-
-static INITIALIZED: AtomicBool = AtomicBool::new(false);
-
-fuzz_target!(|data: &[u8]| {
-    if let Ok(false) = INITIALIZED.compare_exchange(false, true, Ordering::Relaxed, Ordering::Relaxed) {
-        simplelog::SimpleLogger::init(simplelog::LevelFilter::Trace, simplelog::Config::default()).unwrap();
-    }
-
-    let mut context = aml::AmlContext::new(Box::new(Handler), aml::DebugVerbosity::None);
-    let _ = context.parse_table(data);
-});
-
-struct Handler;
-
-impl aml::Handler for Handler {
-    fn read_u8(&self, _address: usize) -> u8 {
-        0
-    }
-    fn read_u16(&self, _address: usize) -> u16 {
-        0
-    }
-    fn read_u32(&self, _address: usize) -> u32 {
-        0
-    }
-    fn read_u64(&self, _address: usize) -> u64 {
-        0
-    }
-
-    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 {
-        0
-    }
-    fn read_io_u16(&self, _port: u16) -> u16 {
-        0
-    }
-    fn read_io_u32(&self, _port: u16) -> u32 {
-        0
-    }
-
-    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 {
-        0
-    }
-    fn read_pci_u16(&self, _segment: u16, _bus: u8, _device: u8, _function: u8, _offset: u16) -> u16 {
-        0
-    }
-    fn read_pci_u32(&self, _segment: u16, _bus: u8, _device: u8, _function: u8, _offset: u16) -> u32 {
-        0
-    }
-    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) {}
-
-    fn stall(&self, _microseconds: u64) {}
-    fn sleep(&self, _milliseconds: u64) {}
-}
diff --git a/aml/src/expression.rs b/aml/src/expression.rs
deleted file mode 100644
index f5babc22..00000000
--- a/aml/src/expression.rs
+++ /dev/null
@@ -1,919 +0,0 @@
-use crate::{
-    misc::debug_obj,
-    name_object::{name_string, simple_name, super_name, target},
-    opcode::{self, opcode},
-    parser::{choice, comment_scope, n_of, take, take_to_end_of_pkglength, try_with_context, Parser, Propagate},
-    pkg_length::pkg_length,
-    term_object::{data_ref_object, def_cond_ref_of, term_arg},
-    value::{AmlType, AmlValue, Args},
-    AmlError,
-    DebugVerbosity,
-};
-use alloc::{
-    string::{String, ToString},
-    sync::Arc,
-    vec,
-    vec::Vec,
-};
-use core::{cmp::Ordering, mem, ops::Deref};
-
-pub fn expression_opcode<'a, 'c>() -> impl Parser<'a, 'c, AmlValue>
-where
-    'c: 'a,
-{
-    /*
-     * ExpressionOpcode := DefAquire | DefAdd | DefAnd | DefBuffer | DefConcat | DefConcatRes |
-     *                     DefCondRefOf | DefCopyObject | DefDecrement | DefDerefOf | DefDivide |
-     *                     DefFindSetLeftBit | DefFindSetRightBit | DefFromBCD | DefIncrement | DefIndex |
-     *                     DefLAnd | DefLEqual | DefLGreater | DefLGreaterEqual | DefLLess | DefLLessEqual |
-     *                     DefMid | DefLNot | DefLNotEqual | DefLoad | DefLoadTable | DefLOr | DefMatch | DefMod |
-     *                     DefMultiply | DefNAnd | DefNOr | DefNot | DefObjectType | DefOr | DefPackage |
-     *                     DefVarPackage | DefRefOf | DefShiftLeft | DefShiftRight | DefSizeOf | DefStore |
-     *                     DefSubtract | DefTimer | DefToBCD | DefToBuffer | DefToDecimalString |
-     *                     DefToHexString | DefToInteger | DefToString | DefWait | DefXOr | MethodInvocation
-     */
-    comment_scope(
-        DebugVerbosity::AllScopes,
-        "ExpressionOpcode",
-        choice!(
-            def_add(),
-            def_subtract(),
-            def_and(),
-            def_or(),
-            def_buffer(),
-            def_concat(),
-            def_concat_res(),
-            def_increment(),
-            def_decrement(),
-            def_l_equal(),
-            def_l_greater(),
-            def_l_greater_equal(),
-            def_l_less(),
-            def_l_less_equal(),
-            def_l_not_equal(),
-            def_l_and(),
-            def_l_or(),
-            def_l_not(),
-            def_mid(),
-            def_object_type(),
-            def_package(),
-            def_shift_left(),
-            def_shift_right(),
-            def_store(),
-            def_to_integer(),
-            def_cond_ref_of(),
-            def_size_of(),
-            method_invocation() // XXX: this must always appear last. See how we have to parse it to see why.
-        ),
-    )
-}
-
-pub fn def_add<'a, 'c>() -> impl Parser<'a, 'c, AmlValue>
-where
-    'c: 'a,
-{
-    /*
-     * DefAdd := 0x72 Operand Operand Target
-     * Operand := TermArg => Integer
-     */
-    opcode(opcode::DEF_ADD_OP)
-        .then(comment_scope(
-            DebugVerbosity::AllScopes,
-            "DefAdd",
-            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));
-                    let result = AmlValue::Integer(left.wrapping_add(right));
-
-                    try_with_context!(context, context.store(target, result.clone()));
-                    (Ok(result), context)
-                },
-            ),
-        ))
-        .map(|((), result)| Ok(result))
-}
-
-pub fn def_subtract<'a, 'c>() -> impl Parser<'a, 'c, AmlValue>
-where
-    'c: 'a,
-{
-    /*
-     * DefSubtract := 0x74 Operand Operand Target
-     * Operand := TermArg => Integer
-     */
-    opcode(opcode::DEF_SUBTRACT_OP)
-        .then(comment_scope(
-            DebugVerbosity::AllScopes,
-            "DefSubtract",
-            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));
-                    let result = AmlValue::Integer(left.wrapping_sub(right));
-
-                    try_with_context!(context, context.store(target, result.clone()));
-                    (Ok(result), context)
-                },
-            ),
-        ))
-        .map(|((), result)| Ok(result))
-}
-
-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));
-                    let result = AmlValue::Integer(left & right);
-
-                    try_with_context!(context, context.store(target, result.clone()));
-                    (Ok(result), context)
-                },
-            ),
-        ))
-        .map(|((), result)| Ok(result))
-}
-
-pub fn def_or<'a, 'c>() -> impl Parser<'a, 'c, AmlValue>
-where
-    'c: 'a,
-{
-    /*
-     * DefOr := 0x7d Operand Operand Target
-     * Operand := TermArg => Integer
-     */
-    opcode(opcode::DEF_OR_OP)
-        .then(comment_scope(
-            DebugVerbosity::AllScopes,
-            "DefOr",
-            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));
-                    let result = AmlValue::Integer(left | right);
-
-                    try_with_context!(context, context.store(target, result.clone()));
-                    (Ok(result), context)
-                },
-            ),
-        ))
-        .map(|((), result)| Ok(result))
-}
-
-pub fn def_buffer<'a, 'c>() -> impl Parser<'a, 'c, AmlValue>
-where
-    'c: 'a,
-{
-    /*
-     * DefBuffer := 0x11 PkgLength BufferSize ByteList
-     * BufferSize := TermArg => Integer
-     *
-     * XXX: The spec says that zero-length buffers (e.g. the PkgLength is 0) are illegal, but
-     * we've encountered them in QEMU-generated tables, so we return an empty buffer in these
-     * cases.
-     *
-     * Uninitialized elements are initialized to zero.
-     */
-    opcode(opcode::DEF_BUFFER_OP)
-        .then(comment_scope(
-            DebugVerbosity::AllScopes,
-            "DefBuffer",
-            pkg_length().then(term_arg()).feed(|(pkg_length, buffer_size)| {
-                take_to_end_of_pkglength(pkg_length).map_with_context(move |bytes, context| {
-                    let buffer_size = try_with_context!(context, buffer_size.as_integer(context)) as usize;
-
-                    if buffer_size < bytes.len() {
-                        return (Err(Propagate::Err(AmlError::MalformedBuffer)), context);
-                    }
-
-                    /*
-                     * `Vec` has a maximum capacity of `isize::MAX` **bytes**. If pathological AML
-                     * does try to create a gigantic buffer, we don't want to crash.
-                     */
-                    if buffer_size > (isize::MAX as usize) {
-                        return (Err(Propagate::Err(AmlError::MalformedBuffer)), context);
-                    }
-
-                    let mut buffer = vec![0; buffer_size];
-                    buffer[0..bytes.len()].copy_from_slice(bytes);
-                    (Ok(buffer), context)
-                })
-            }),
-        ))
-        .map(|((), buffer)| Ok(AmlValue::Buffer(Arc::new(spinning_top::Spinlock::new(buffer)))))
-}
-
-pub fn def_concat<'a, 'c>() -> impl Parser<'a, 'c, AmlValue>
-where
-    'c: 'a,
-{
-    /*
-     * DefConcat := 0x73 Data Data Target
-     * Data := TermArg => ComputationalData
-     */
-    opcode(opcode::DEF_CONCAT_OP)
-        .then(comment_scope(
-            DebugVerbosity::AllScopes,
-            "DefConcat",
-            term_arg().then(term_arg()).then(target()).map_with_context(|((left, right), target), context| {
-                let result = match left.as_concat_type() {
-                    AmlValue::Integer(left) => {
-                        let right = try_with_context!(context, right.as_integer(context));
-
-                        let mut buffer = Vec::with_capacity(mem::size_of::<u64>() * 2);
-                        buffer.extend_from_slice(&left.to_le_bytes());
-                        buffer.extend_from_slice(&right.to_le_bytes());
-
-                        AmlValue::Buffer(Arc::new(spinning_top::Spinlock::new(buffer)))
-                    }
-                    AmlValue::Buffer(left) => {
-                        let mut new: Vec<u8> = left.lock().deref().clone();
-                        new.extend(try_with_context!(context, right.as_buffer(context)).lock().iter());
-                        AmlValue::Buffer(Arc::new(spinning_top::Spinlock::new(new)))
-                    }
-                    AmlValue::String(left) => {
-                        let right = match right.as_concat_type() {
-                            AmlValue::String(right) => right,
-                            AmlValue::Integer(_) => try_with_context!(context, right.as_string(context)),
-                            AmlValue::Buffer(_) => try_with_context!(context, right.as_string(context)),
-                            _ => panic!("Invalid type returned from `as_concat_type`"),
-                        };
-                        AmlValue::String(left + &right)
-                    }
-                    _ => panic!("Invalid type returned from `as_concat_type`"),
-                };
-
-                try_with_context!(context, context.store(target, result.clone()));
-                (Ok(result), context)
-            }),
-        ))
-        .map(|((), result)| Ok(result))
-}
-
-pub fn def_concat_res<'a, 'c>() -> impl Parser<'a, 'c, AmlValue>
-where
-    'c: 'a,
-{
-    /*
-     * DefConcatRes := 0x84 BufData BufData Target
-     * BufData := TermArg => Buffer
-     */
-    opcode(opcode::DEF_CONCAT_RES_OP)
-        .then(comment_scope(
-            DebugVerbosity::AllScopes,
-            "DefConcatRes",
-            term_arg().then(term_arg()).then(target()).map_with_context(|((left, right), target), context| {
-                let left = try_with_context!(context, left.as_buffer(context));
-                let right = try_with_context!(context, right.as_buffer(context));
-
-                let left_len = left.lock().len();
-                let right_len = right.lock().len();
-
-                if left_len == 1 || right_len == 1 {
-                    return (Err(Propagate::Err(AmlError::ResourceDescriptorTooShort)), context);
-                }
-
-                /*
-                 * `left` and `right` are buffers of resource descriptors, which we're trying to concatenate into a
-                 * new, single buffer containing all of the descriptors from the source buffers. We need to strip
-                 * off the end tags (2 bytes from each buffer), and then add our own end tag.
-                 *
-                 * XXX: either buffer may be empty (contains no tags), and so our arithmetic has to be careful.
-                 */
-                let result = {
-                    let mut result =
-                        Vec::with_capacity(left_len.saturating_sub(2) + right_len.saturating_sub(2) + 2);
-                    let left_contents = left.lock();
-                    let right_contents = right.lock();
-                    result.extend_from_slice(if left_len == 0 { &[] } else { &left_contents[..(left_len - 2)] });
-                    result.extend_from_slice(if right_len == 0 {
-                        &[]
-                    } else {
-                        &right_contents[..(right_len - 2)]
-                    });
-
-                    /*
-                     * Construct a new end tag, including a new checksum:
-                     *    | Bits        | Field             | Value                     |
-                     *    |-------------|-------------------|---------------------------|
-                     *    | 0-2         | Length - n bytes  | 1 (for checksum)          |
-                     *    | 3-6         | Small item type   | 0x0f = end tag descriptor |
-                     *    | 7           | 0 = small item    | 0                         |
-                     */
-                    result.push(0b01111001);
-                    result.push(
-                        result.iter().fold(0u8, |checksum, byte| checksum.wrapping_add(*byte)).wrapping_neg(),
-                    );
-
-                    AmlValue::Buffer(Arc::new(spinning_top::Spinlock::new(result)))
-                };
-
-                try_with_context!(context, context.store(target, result.clone()));
-                (Ok(result), context)
-            }),
-        ))
-        .map(|((), result)| Ok(result))
-}
-
-fn def_increment<'a, 'c>() -> impl Parser<'a, 'c, AmlValue>
-where
-    'c: 'a,
-{
-    /*
-     * DefIncrement := 0x75 SuperName
-     */
-    opcode(opcode::DEF_INCREMENT_OP)
-        .then(comment_scope(
-            DebugVerbosity::AllScopes,
-            "DefIncrement",
-            super_name().map_with_context(|addend, context| {
-                let value = try_with_context!(context, context.read_target(&addend)).clone();
-                let value = try_with_context!(context, value.as_integer(context));
-                let new_value = AmlValue::Integer(value + 1);
-                try_with_context!(context, context.store(addend, new_value.clone()));
-                (Ok(new_value), context)
-            }),
-        ))
-        .map(|((), result)| Ok(result))
-}
-
-fn def_decrement<'a, 'c>() -> impl Parser<'a, 'c, AmlValue>
-where
-    'c: 'a,
-{
-    /*
-     * DefDecrement := 0x76 SuperName
-     */
-    opcode(opcode::DEF_DECREMENT_OP)
-        .then(comment_scope(
-            DebugVerbosity::AllScopes,
-            "DefDecrement",
-            super_name().map_with_context(|minuend, context| {
-                let value = try_with_context!(context, context.read_target(&minuend)).clone();
-                let value = try_with_context!(context, value.as_integer(context));
-                let new_value = AmlValue::Integer(value - 1);
-                try_with_context!(context, context.store(minuend, new_value.clone()));
-                (Ok(new_value), context)
-            }),
-        ))
-        .map(|((), result)| Ok(result))
-}
-
-fn def_l_and<'a, 'c>() -> impl Parser<'a, 'c, AmlValue>
-where
-    'c: 'a,
-{
-    /*
-     * DefLAnd := 0x90 Operand Operand
-     * Operand := TermArg => Integer
-     */
-    opcode(opcode::DEF_L_AND_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(context));
-                let right = try_with_context!(context, right_arg.as_bool(context));
-                (Ok(AmlValue::Boolean(left && right)), context)
-            }),
-        ))
-        .map(|((), result)| Ok(result))
-}
-
-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(context));
-                let right = try_with_context!(context, right_arg.as_bool(context));
-                (Ok(AmlValue::Boolean(left || right)), context)
-            }),
-        ))
-        .map(|((), result)| Ok(result))
-}
-
-fn def_l_not<'a, 'c>() -> impl Parser<'a, 'c, AmlValue>
-where
-    'c: 'a,
-{
-    /*
-     * DefLNot := 0x92 Operand
-     * Operand := TermArg => Integer
-     */
-    opcode(opcode::DEF_L_NOT_OP)
-        .then(comment_scope(
-            DebugVerbosity::AllScopes,
-            "DefLNot",
-            term_arg().map_with_context(|arg, context| {
-                let operand = try_with_context!(context, arg.as_bool(context));
-                (Ok(AmlValue::Boolean(!operand)), context)
-            }),
-        ))
-        .map(|((), result)| Ok(result))
-}
-
-fn def_l_equal<'a, 'c>() -> impl Parser<'a, 'c, AmlValue>
-where
-    'c: 'a,
-{
-    /*
-     * DefLEqual := 0x93 Operand Operand
-     * Operand := TermArg => Integer
-     */
-    opcode(opcode::DEF_L_EQUAL_OP)
-        .then(comment_scope(
-            DebugVerbosity::AllScopes,
-            "DefLEqual",
-            term_arg().then(term_arg()).map_with_context(|(left_arg, right_arg), context| {
-                let ord = try_with_context!(context, left_arg.cmp(right_arg, context));
-                (Ok(AmlValue::Boolean(ord == Ordering::Equal)), context)
-            }),
-        ))
-        .map(|((), result)| Ok(result))
-}
-
-fn def_l_greater<'a, 'c>() -> impl Parser<'a, 'c, AmlValue>
-where
-    'c: 'a,
-{
-    /*
-     * DefLGreater := 0x94 Operand Operand
-     */
-    opcode(opcode::DEF_L_GREATER_OP)
-        .then(comment_scope(
-            DebugVerbosity::AllScopes,
-            "DefLGreater",
-            term_arg().then(term_arg()).map_with_context(|(left_arg, right_arg), context| {
-                let ord = try_with_context!(context, left_arg.cmp(right_arg, context));
-                (Ok(AmlValue::Boolean(ord == Ordering::Greater)), context)
-            }),
-        ))
-        .map(|((), result)| Ok(result))
-}
-
-fn def_l_greater_equal<'a, 'c>() -> impl Parser<'a, 'c, AmlValue>
-where
-    'c: 'a,
-{
-    /*
-     * DefLGreaterEqual := LNotOp(0x92) LLessOp(0x95) Operand Operand
-     */
-    opcode(opcode::DEF_L_NOT_OP)
-        .then(opcode(opcode::DEF_L_LESS_OP))
-        .then(comment_scope(
-            DebugVerbosity::AllScopes,
-            "DefLGreaterEqual",
-            term_arg().then(term_arg()).map_with_context(|(left_arg, right_arg), context| {
-                let ord = try_with_context!(context, left_arg.cmp(right_arg, context));
-                (Ok(AmlValue::Boolean(ord != Ordering::Less)), context)
-            }),
-        ))
-        .map(|(((), ()), result)| Ok(result))
-}
-
-fn def_l_less<'a, 'c>() -> impl Parser<'a, 'c, AmlValue>
-where
-    'c: 'a,
-{
-    /*
-     * DefLLess := 0x95 Operand Operand
-     */
-    opcode(opcode::DEF_L_LESS_OP)
-        .then(comment_scope(
-            DebugVerbosity::AllScopes,
-            "DefLLess",
-            term_arg().then(term_arg()).map_with_context(|(left_arg, right_arg), context| {
-                let ord = try_with_context!(context, left_arg.cmp(right_arg, context));
-                (Ok(AmlValue::Boolean(ord == Ordering::Less)), context)
-            }),
-        ))
-        .map(|((), result)| Ok(result))
-}
-
-fn def_l_less_equal<'a, 'c>() -> impl Parser<'a, 'c, AmlValue>
-where
-    'c: 'a,
-{
-    /*
-     * DefLLessEqual := LNotOp(0x92) LGreaterOp(0x94) Operand Operand
-     */
-    opcode(opcode::DEF_L_NOT_OP)
-        .then(opcode(opcode::DEF_L_GREATER_OP))
-        .then(comment_scope(
-            DebugVerbosity::AllScopes,
-            "DefLLessEqual",
-            term_arg().then(term_arg()).map_with_context(|(left_arg, right_arg), context| {
-                let ord = try_with_context!(context, left_arg.cmp(right_arg, context));
-                (Ok(AmlValue::Boolean(ord != Ordering::Greater)), context)
-            }),
-        ))
-        .map(|(((), ()), result)| Ok(result))
-}
-
-fn def_l_not_equal<'a, 'c>() -> impl Parser<'a, 'c, AmlValue>
-where
-    'c: 'a,
-{
-    /*
-     * DefLNotEqual := LNotOp(0x92) LEqualOp(0x93) Operand Operand
-     */
-    opcode(opcode::DEF_L_NOT_OP)
-        .then(opcode(opcode::DEF_L_EQUAL_OP))
-        .then(comment_scope(
-            DebugVerbosity::AllScopes,
-            "DefLNotEqual",
-            term_arg().then(term_arg()).map_with_context(|(left_arg, right_arg), context| {
-                let ord = try_with_context!(context, left_arg.cmp(right_arg, context));
-                (Ok(AmlValue::Boolean(ord != Ordering::Equal)), context)
-            }),
-        ))
-        .map(|(((), ()), result)| Ok(result))
-}
-
-fn def_mid<'a, 'c>() -> impl Parser<'a, 'c, AmlValue>
-where
-    'c: 'a,
-{
-    /*
-     * DefMid := 0x9e MidObj TermArg TermArg Target
-     * MidObj := TermArg => Buffer | String
-     */
-    opcode(opcode::DEF_MID_OP)
-        .then(comment_scope(
-            DebugVerbosity::AllScopes,
-            "DefMid",
-            term_arg().then(term_arg()).then(term_arg()).then(target()).map_with_context(
-                |(((source, index), length), target), context| {
-                    let index = try_with_context!(context, index.as_integer(context)) as usize;
-                    let length = try_with_context!(context, length.as_integer(context)) as usize;
-
-                    let result = try_with_context!(
-                        context,
-                        match source {
-                            AmlValue::Buffer(bytes) => {
-                                let guard = bytes.lock();
-                                if index >= guard.len() {
-                                    Ok(AmlValue::Buffer(Arc::new(spinning_top::Spinlock::new(vec![]))))
-                                } else if (index + length) >= guard.len() {
-                                    Ok(AmlValue::Buffer(Arc::new(spinning_top::Spinlock::new(
-                                        guard[index..].to_vec(),
-                                    ))))
-                                } else {
-                                    Ok(AmlValue::Buffer(Arc::new(spinning_top::Spinlock::new(
-                                        guard[index..(index + length)].to_vec(),
-                                    ))))
-                                }
-                            }
-                            /*
-                             * XXX: The spec conflates characters and bytes, so we effectively ignore unicode and do
-                             * this bytewise, to hopefully match other implementations.
-                             */
-                            AmlValue::String(string) => {
-                                if index >= string.len() {
-                                    Ok(AmlValue::String(String::new()))
-                                } else if (index + length) >= string.len() {
-                                    Ok(AmlValue::String(string[index..].to_string()))
-                                } else {
-                                    Ok(AmlValue::String(string[index..(index + length)].to_string()))
-                                }
-                            }
-                            _ => Err(AmlError::TypeCannotBeSliced(source.type_of())),
-                        }
-                    );
-
-                    try_with_context!(context, context.store(target, result.clone()));
-                    (Ok(result), context)
-                },
-            ),
-        ))
-        .map(|((), result)| Ok(result))
-}
-
-pub fn def_object_type<'a, 'c>() -> impl Parser<'a, 'c, AmlValue>
-where
-    'c: 'a,
-{
-    /*
-     * DefObjectType := 0x8e <SimpleName | DebugObj | DefRefOf | DefDerefOf | DefIndex>
-     *
-     * Returns an integer representing the type of an AML object. If executed on an object that is a reference to a
-     * value (e.g. produced by `Alias`, `RefOf`, or `Index`), the type of the base object is returned. For typeless
-     * objects, such as scopes, a type of `0 - Uninitialized` is returned.
-     *
-     *    0 = Uninitialized
-     *    1 = Integer
-     *    2 = String
-     *    3 = Buffer
-     *    4 = Package
-     *    5 = Field Unit
-     *    6 = Device
-     *    7 = Event
-     *    8 = Method
-     *    9 = Mutex
-     *    10 = Operation Region
-     *    11 = Power Resource
-     *    12 = Processor
-     *    13 = Thermal Zone
-     *    14 = Buffer Field
-     *    15 = Reserved
-     *    16 = Debug Object
-     *    >16 = *Reserved*
-     */
-    // TODO: this doesn't correctly handle taking the type of a namespace node (e.g. `\_SB`), but I'm not sure any
-    // other implementations do either?
-    opcode(opcode::DEF_OBJECT_TYPE_OP)
-        .then(comment_scope(
-            DebugVerbosity::AllScopes,
-            "DefObjectType",
-            choice!(
-                simple_name().map_with_context(|target, context| {
-                    let value = try_with_context!(context, context.read_target(&target));
-                    let typ = match value.type_of() {
-                        AmlType::Uninitialized => 0,
-                        AmlType::Integer => 1,
-                        AmlType::String => 2,
-                        AmlType::Buffer => 3,
-                        AmlType::RawDataBuffer => 3, // TODO: not sure if this is correct
-                        AmlType::Package => 4,
-                        AmlType::FieldUnit => 5,
-                        AmlType::Device => 6,
-                        AmlType::Event => 7,
-                        AmlType::Method => 8,
-                        AmlType::Mutex => 9,
-                        AmlType::OpRegion => 10,
-                        AmlType::PowerResource => 11,
-                        AmlType::Processor => 12,
-                        AmlType::ThermalZone => 13,
-                        AmlType::BufferField => 14,
-                        AmlType::DebugObject => 16,
-
-                        // TODO: what to do with this?
-                        AmlType::DdbHandle => 0,
-                        AmlType::ObjReference => todo!(),
-                    };
-
-                    (Ok(AmlValue::Integer(typ)), context)
-                }),
-                debug_obj().map(|()| Ok(AmlValue::Integer(16)))
-            ),
-        ))
-        .map(|((), result)| Ok(result))
-}
-
-pub fn def_package<'a, 'c>() -> impl Parser<'a, 'c, AmlValue>
-where
-    'c: 'a,
-{
-    /*
-     * DefPackage := 0x12 PkgLength NumElements PackageElementList
-     * NumElements := ByteData
-     * PackageElementList := Nothing | <PackageElement PackageElementList>
-     * PackageElement := DataRefObject | NameString
-     */
-    opcode(opcode::DEF_PACKAGE_OP)
-        .then(comment_scope(
-            DebugVerbosity::AllScopes,
-            "DefPackage",
-            pkg_length().then(take()).feed(|(pkg_length, num_elements)| {
-                move |mut input, mut context| {
-                    let mut package_contents = Vec::new();
-
-                    while pkg_length.still_parsing(input) {
-                        let (new_input, new_context, value) = package_element().parse(input, context)?;
-                        input = new_input;
-                        context = new_context;
-
-                        package_contents.push(value);
-                    }
-
-                    // ACPI6.2, §19.6.101 specifies that if NumElements is present and is greater
-                    // than the number of entries in the PackageList, the default entry of type
-                    // Uninitialized is used
-                    if package_contents.len() > num_elements as usize {
-                        return Err((input, context, Propagate::Err(AmlError::MalformedPackage)));
-                    }
-
-                    package_contents.resize(num_elements as usize, AmlValue::Uninitialized);
-
-                    Ok((input, context, AmlValue::Package(package_contents)))
-                }
-            }),
-        ))
-        .map(|((), package)| Ok(package))
-}
-
-pub fn package_element<'a, 'c>() -> impl Parser<'a, 'c, AmlValue>
-where
-    'c: 'a,
-{
-    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,
-{
-    /*
-     * DefStore := 0x70 TermArg SuperName
-     *
-     * Implicit conversion is only applied when the destination target is a `Name` - not when we
-     * are storing into a method local or argument (these stores are semantically identical to
-     * CopyObject). We must also make sure to return a copy of the data that is in the destination
-     * after the store (as opposed to the data we think we put into it), because some stores can
-     * alter the data during the store.
-     */
-    opcode(opcode::DEF_STORE_OP)
-        .then(comment_scope(DebugVerbosity::Scopes, "DefStore", term_arg().then(super_name())))
-        .map_with_context(|((), (value, target)), context| {
-            (Ok(try_with_context!(context, context.store(target, value))), context)
-        })
-}
-
-fn def_to_integer<'a, 'c>() -> impl Parser<'a, 'c, AmlValue>
-where
-    'c: 'a,
-{
-    /*
-     * DefToInteger := 0x99 Operand Target
-     * Operand := TermArg
-     */
-    opcode(opcode::DEF_TO_INTEGER_OP)
-        .then(comment_scope(DebugVerbosity::AllScopes, "DefToInteger", term_arg().then(target())))
-        .map_with_context(|((), (operand, target)), context| {
-            let result = match operand {
-                AmlValue::Integer(value) => AmlValue::Integer(value),
-                AmlValue::Buffer(data) => {
-                    AmlValue::Integer(try_with_context!(context, AmlValue::Buffer(data).as_integer(context)))
-                }
-                AmlValue::Field { .. } => {
-                    AmlValue::Integer(try_with_context!(context, operand.as_integer(context)))
-                }
-                AmlValue::String(string) => AmlValue::Integer(try_with_context!(
-                    context,
-                    if string.starts_with("0x") {
-                        u64::from_str_radix(string.trim_start_matches("0x"), 16)
-                    } else {
-                        string.parse::<u64>()
-                    }
-                    .map_err(|_| AmlError::IncompatibleValueConversion {
-                        current: AmlType::String,
-                        target: AmlType::Integer,
-                    })
-                )),
-                _ => {
-                    return (
-                        Err(Propagate::Err(AmlError::IncompatibleValueConversion {
-                            current: operand.type_of(),
-                            target: AmlType::Integer,
-                        })),
-                        context,
-                    )
-                }
-            };
-            try_with_context!(context, context.store(target, result.clone()));
-            (Ok(result), context)
-        })
-}
-
-fn def_size_of<'a, 'c>() -> impl Parser<'a, 'c, AmlValue>
-where
-    'c: 'a,
-{
-    /*
-     * SizeOf := 0x87 SuperName
-     */
-    opcode(opcode::DEF_SIZE_OF_OP)
-        .then(comment_scope(
-            DebugVerbosity::AllScopes,
-            "DefSizeOf",
-            super_name().map_with_context(|target, context| {
-                let value = try_with_context!(context, context.read_target(&target));
-                let size_of = try_with_context!(context, value.size_of());
-                (Ok(AmlValue::Integer(size_of)), context)
-            }),
-        ))
-        .map(|((), value)| Ok(value))
-}
-
-fn method_invocation<'a, 'c>() -> impl Parser<'a, 'c, AmlValue>
-where
-    'c: 'a,
-{
-    /*
-     * MethodInvocation := NameString TermArgList
-     *
-     * MethodInvocation is the worst of the AML structures, because you're meant to figure out how much you're
-     * meant to parse using the name of the method (by knowing from its definition how how many arguments it
-     * takes). However, the definition of a method can in theory appear after an invocation of that method, and
-     * so parsing them properly can be very difficult.
-     * NOTE: We don't support the case of the definition appearing after the invocation.
-     */
-    comment_scope(
-        DebugVerbosity::Scopes,
-        "MethodInvocation",
-        name_string()
-            .map_with_context(move |name, context| {
-                let (full_path, handle) =
-                    try_with_context!(context, context.namespace.search(&name, &context.current_scope)).clone();
-
-                /*
-                 * `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(|(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 args = try_with_context!(context, Args::from_list(arg_list));
-                        let result = context.invoke_method(&path, args);
-                        (Ok(try_with_context!(context, result)), context)
-                    } else {
-                        (Ok(try_with_context!(context, context.namespace.get_by_path(&path)).clone()), context)
-                    }
-                })
-            }),
-    )
-}
diff --git a/aml/src/lib.rs b/aml/src/lib.rs
index 268011e9..100da98d 100644
--- a/aml/src/lib.rs
+++ b/aml/src/lib.rs
@@ -1,521 +1,770 @@
-//! `aml` is a pure-Rust AML (ACPI Machine Language) parser, used for parsing the DSDT and
-//! SSDT tables from ACPI. This crate can be used by kernels to gather information about the
-//! hardware, and invoke control methods to query and change the state of devices in a
-//! hardware-independent way.
-//!
-//! ### Using the library
-//! To use the library, you should create an `AmlContext` using `AmlContext::new()`, and then pass it tables
-//! containing AML (probably from the `acpi` crate), which you've mapped into the virtual address space. This will
-//! parse the table, populating the namespace with objects encoded by the AML. After this, you may unmap the memory
-//! the table was mapped into - all the information needed will be extracted and allocated on the heap.
-//!
-//! You can then access specific objects by name like so: e.g.
-//! ```ignore
-//! let my_aml_value = aml_context.lookup(&AmlName::from_str("\\_SB.PCI0.S08._ADR").unwrap());
-//! ```
-//!
-//! And invoke control methods like this: e.g.
-//! ```ignore
-//! let result = aml_context.invoke_method(&AmlName::from_str("\\_SB.HPET._CRS").unwrap(), value::Args::EMPTY);
-//! ```
-//!
-//! ### About the parser
-//! The parser is written using a set of custom parser combinators - the code can be confusing on
-//! first reading, but provides an extensible and type-safe way to write parsers. For an easy
-//! introduction to parser combinators and the foundations used for this library, I suggest reading
-//! [Bodil's fantastic blog post](https://bodil.lol/parser-combinators/).
-//!
-//! The actual combinators can be found in `parser.rs`. Various tricks are used to provide a nice
-//! API and work around limitations in the type system, such as the concrete types like
-//! `MapWithContext`.
-//!
-//! The actual parsers are then grouped into categories based loosely on the AML grammar sections in
-//! the ACPI spec. Most are written in terms of combinators, but some have to be written in a more
-//! imperitive style, either because they're clearer, or because we haven't yet found good
-//! combinator patterns to express the parse.
-
 #![no_std]
-#![feature(decl_macro, try_with_capacity)]
-
-extern crate alloc;
+#![feature(let_chains, vec_pop_if)]
 
 #[cfg(test)]
 extern crate std;
 
-#[cfg(test)]
-mod test_utils;
-
-pub(crate) mod expression;
-pub(crate) mod misc;
-pub(crate) mod name_object;
-pub(crate) mod namespace;
-pub(crate) mod opcode;
-pub mod opregion;
-pub(crate) mod parser;
-pub mod pci_routing;
-pub(crate) mod pkg_length;
-pub mod resource;
-pub(crate) mod statement;
-pub(crate) mod term_object;
-pub mod value;
-
-pub use crate::{namespace::*, value::AmlValue};
-
-use alloc::{
-    boxed::Box,
-    format,
-    string::{String, ToString},
-};
-use core::{mem, str::FromStr};
-use log::{error, warn};
-use misc::{ArgNum, LocalNum};
-use name_object::Target;
-use parser::{Parser, Propagate};
-use pkg_length::PkgLength;
-use term_object::term_list;
-use value::{AmlType, 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.
-pub const AML_INTERPRETER_REVISION: u64 = 0;
-
-/// Describes how much debug information the parser should emit. Set the "maximum" expected verbosity in
-/// the context's `debug_verbosity` - everything will be printed that is less or equal in 'verbosity'.
-#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)]
-pub enum DebugVerbosity {
-    /// Print no debug information
-    None,
-    /// Print heads and tails when entering and leaving scopes of major objects, but not more minor ones.
-    Scopes,
-    /// Print heads and tails when entering and leaving scopes of all objects.
-    AllScopes,
-    /// Print heads and tails of all objects, and extra debug information as it's parsed.
-    All,
-}
-
-#[derive(Debug)]
-struct MethodContext {
-    /// AML local variables. These are used when we invoke a control method. A `None` value represents a null AML
-    /// object.
-    locals: [Option<AmlValue>; 8],
-    /// 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.
-    args: Args,
-}
+extern crate alloc;
 
-impl MethodContext {
-    fn new(args: Args) -> MethodContext {
-        // XXX: this is required because `Option<AmlValue>` is not `Copy`, so it can't be used to initialize an
-        // array, but consts can :(
-        const NONE_BUT_CONST: Option<AmlValue> = None;
+pub mod namespace;
+pub mod object;
 
-        MethodContext { locals: [NONE_BUT_CONST; 8], args }
-    }
-}
+use alloc::{boxed::Box, sync::Arc, vec, vec::Vec};
+use bit_field::BitField;
+use core::mem;
+use namespace::{AmlName, Namespace, NamespaceLevelKind};
+use object::Object;
+use spinning_top::Spinlock;
 
-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.
+pub struct Interpreter {
     handler: Box<dyn Handler>,
-
-    pub namespace: Namespace,
-    method_context: Option<MethodContext>,
-
-    /*
-     * These track the state of the context while it's parsing an AML table.
-     */
-    current_scope: AmlName,
-    scope_indent: usize,
-    debug_verbosity: DebugVerbosity,
+    pub namespace: Spinlock<Namespace>,
 }
 
-impl AmlContext {
-    /// Creates a new `AmlContext` - the central type in managing the AML tables. Only one of these should be
-    /// created, and it should be passed the DSDT and all SSDTs defined by the hardware.
-    pub fn new(handler: Box<dyn Handler>, debug_verbosity: DebugVerbosity) -> AmlContext {
-        let mut context = AmlContext {
-            handler,
-            namespace: Namespace::new(),
-            method_context: None,
-
-            current_scope: AmlName::root(),
-            scope_indent: 0,
-            debug_verbosity,
-        };
-
-        context.add_predefined_objects();
-        context
+impl Interpreter {
+    pub fn new<H>(handler: H) -> Interpreter
+    where
+        H: Handler + 'static,
+    {
+        Interpreter { namespace: Spinlock::new(Namespace::new()), handler: Box::new(handler) }
     }
 
-    pub fn parse_table(&mut self, stream: &[u8]) -> Result<(), AmlError> {
-        fn stream_context(stream: &[u8], err_buf: &[u8]) -> String {
-            const BEFORE_LEN: usize = 4;
-            const ABBREV_LEN: usize = 4;
-            let abbreviated = if err_buf.len() >= ABBREV_LEN { &err_buf[..ABBREV_LEN] } else { err_buf };
-
-            if let Some(position) = (err_buf.as_ptr() as usize).checked_sub(stream.as_ptr() as usize) {
-                if position <= stream.len() {
-                    let before = if position > BEFORE_LEN {
-                        &stream[position - BEFORE_LEN..position]
-                    } else {
-                        &stream[..position]
-                    };
-                    return format!(
-                        "position {:#X}: preceding {:X?}, buf {:X?}",
-                        position + 36,
-                        before,
-                        abbreviated
-                    );
-                }
-            }
-            format!("buf {:X?}", abbreviated)
-        }
+    pub fn load_table(&self, stream: &[u8]) -> Result<(), AmlError> {
+        // TODO: probs needs to do more stuff
+        self.execute_method(stream)
+    }
 
-        if stream.is_empty() {
-            return Err(AmlError::UnexpectedEndOfStream);
-        }
+    pub fn execute_method(&self, stream: &[u8]) -> Result<(), AmlError> {
+        let mut context = MethodContext::new(stream);
 
-        let table_length = PkgLength::from_raw_length(stream, stream.len() as u32).unwrap();
-        match term_object::term_list(table_length).parse(stream, self) {
-            Ok(_) => Ok(()),
-            Err((err_buf, _, Propagate::Err(err))) => {
-                error!("Failed to parse AML stream. Err = {:?}, {}", err, stream_context(stream, err_buf));
-                Err(err)
-            }
-            Err((_, _, other)) => {
-                error!("AML table evaluated to unexpected result: {:?}", other);
-                Err(AmlError::MalformedStream)
-            }
-        }
-    }
+        /*
+         * TODO
+         *
+         * This is the main loop that executes ops. Every op is handled at the top-level loop to
+         * prevent pathological stack depths.
+         *
+         * Worked example: AddOp TermArg TermArg Target
+         * - We go round the loop 4 times to interpret this.
+         * - We encounter the AddOp. We add a new in-flight operation with 3 expected arguments.
+         * - We go round again and find some sort of TermArg. This may create new in-flight ops
+         *   which we then go round again to complete. Once we're finished, we add this to the
+         *   AddOp's record. We then need to detect that all 3 arguments are ready and retire the
+         *   AddOp *before* moving on.
+         *
+         * My thoughts are that we can go round and round a loop with two big stages. First, we
+         * check if in-flight ops are ready to be executed (e.g. have collected all their
+         * arguments) and execute them. This can in turn complete the next op up, so this should
+         * complete as many in-flight ops as possible at a time.
+         *
+         * Once all possible in-flight ops have been executed, we then need to move forward in the
+         * stream. Depending on the next op, this could create a new in-flight op that is then
+         * pre-empted, or could parse an argument to an existing in-flight op, which may then be
+         * able to be completed. This stage should not do any executing in its own right, really.
+         * It's just about consuming the next however-many bytes of the stream and setting things
+         * up.
+         */
 
-    // TODO: docs
-    pub fn invoke_method(&mut self, path: &AmlName, args: Args) -> Result<AmlValue, AmlError> {
-        use value::MethodCode;
-
-        match self.namespace.get_by_path(path)?.clone() {
-            // TODO: respect the method's flags
-            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 code {
-                    MethodCode::Aml(ref code) => {
-                        match term_list(PkgLength::from_raw_length(code, code.len() as u32).unwrap())
-                            .parse(code, self)
+        'main_loop: loop {
+            /*
+             * First, see if we've gathered enough arguments to complete some in-flight operations.
+             */
+            while let Some(op) = context.in_flight.pop_if(|op| op.arguments.len() == op.expected_arguments) {
+                match op.op {
+                    Opcode::Add => {
+                        let [left, right, target] = &op.arguments[..] else { panic!() };
+                        if let Argument::Object(left) = left
+                            && let Argument::Object(right) = right
                         {
-                            // If the method doesn't return a value, we implicitly return `0`
-                            Ok(_) => Ok(AmlValue::Integer(0)),
-                            Err((_, _, Propagate::Return(result))) => Ok(result),
-                            Err((_, _, Propagate::Break)) => Err(AmlError::BreakInInvalidPosition),
-                            Err((_, _, Propagate::Continue)) => Err(AmlError::ContinueInInvalidPosition),
-                            Err((_, _, Propagate::Err(err))) => {
-                                error!("Failed to execute control method: {:?}", err);
-                                Err(err)
+                            let Object::Integer(left) = **left else { panic!() };
+                            let Object::Integer(right) = **right else { panic!() };
+
+                            if let Argument::Object(target) = target {
+                                *target.gain_mut() = Object::Integer(left + right);
+                            } else {
+                                panic!("Unexpected target type in AddOp");
+                            }
+                            // TODO: this is probs a slightly scuffed way of working out if the
+                            // prev op wants our result
+                            if let Some(prev_op) = context.in_flight.last_mut() {
+                                if prev_op.arguments.len() < prev_op.expected_arguments {
+                                    prev_op
+                                        .arguments
+                                        .push(Argument::Object(Arc::new(Object::Integer(left + right))));
+                                }
                             }
+                            // println!("Result: {}", left + right);
+                        } else {
+                            panic!("Bad operands");
                         }
                     }
-
-                    MethodCode::Native(ref method) => match (method)(self) {
-                        Ok(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
+                    Opcode::Name => {
+                        let [name, object] = &op.arguments[..] else { panic!() };
+                        let Argument::Namestring(name) = name else { panic!() };
+                        let Argument::Object(object) = object else { panic!() };
+                        // println!("Making Name with name {} and object: {:?}", name, object);
+
+                        let name = name.resolve(&context.current_scope)?;
+                        self.namespace.lock().insert(name, object.clone())?;
+                    }
+                    _ => panic!("Unexpected operation has created in-flight op!"),
+                }
             }
 
             /*
-             * 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).
+             * Now that we've retired as many in-flight operations as we have arguments for, move
+             * forward in the AML stream.
              */
-            value => Ok(value),
+            let opcode = loop {
+                match context.opcode() {
+                    Ok(opcode) => break opcode,
+                    Err(AmlError::RunOutOfStream) => {
+                        /*
+                         * We've reached the end of the current block. What we should do about this
+                         * depends on what type of block it was.
+                         */
+                        match context.current_block.kind {
+                            BlockKind::Normal => {
+                                break 'main_loop Ok(());
+                            }
+                            BlockKind::Scope { old_scope } => {
+                                assert!(context.block_stack.len() > 0);
+                                context.current_block = context.block_stack.pop().unwrap();
+                                context.current_scope = old_scope;
+                                // Go round the loop again to get the next opcode for the new block
+                            }
+                        }
+                    }
+                    Err(other_err) => return Err(other_err),
+                }
+            };
+            match opcode {
+                Opcode::Zero => {
+                    context.last_op()?.arguments.push(Argument::Object(Arc::new(Object::Integer(0))));
+                }
+                Opcode::One => {
+                    context.last_op()?.arguments.push(Argument::Object(Arc::new(Object::Integer(1))));
+                }
+                Opcode::Alias => todo!(),
+                Opcode::Name => {
+                    let name = context.namestring()?;
+                    context.start_in_flight_op(OpInFlight::new_with(
+                        Opcode::Name,
+                        vec![Argument::Namestring(name)],
+                        1,
+                    ));
+                }
+                Opcode::BytePrefix => {
+                    let value = context.next()?;
+                    context.last_op()?.arguments.push(Argument::Object(Arc::new(Object::Integer(value as u64))));
+                }
+                Opcode::WordPrefix => {
+                    let value = context.next_u16()?;
+                    context.last_op()?.arguments.push(Argument::Object(Arc::new(Object::Integer(value as u64))));
+                }
+                Opcode::DWordPrefix => {
+                    let value = context.next_u32()?;
+                    context.last_op()?.arguments.push(Argument::Object(Arc::new(Object::Integer(value as u64))));
+                }
+                Opcode::StringPrefix => todo!(),
+                Opcode::QWordPrefix => {
+                    let value = context.next_u64()?;
+                    context.last_op()?.arguments.push(Argument::Object(Arc::new(Object::Integer(value))));
+                }
+                Opcode::Scope => {
+                    let start_pc = context.current_block.pc;
+                    let pkg_length = context.pkglength()?;
+                    let name = context.namestring()?;
+
+                    let remaining_length = pkg_length - (context.current_block.pc - start_pc);
+
+                    let new_scope = name.resolve(&context.current_scope)?;
+                    self.namespace.lock().add_level(new_scope.clone(), NamespaceLevelKind::Scope)?;
+
+                    // println!("Scope. length = {}({}), name = {:?}", pkg_length, remaining_length, new_scope);
+                    let old_scope = mem::replace(&mut context.current_scope, new_scope);
+                    context.start_new_block(BlockKind::Scope { old_scope }, remaining_length);
+                }
+                Opcode::Buffer => todo!(),
+                Opcode::Package => todo!(),
+                Opcode::VarPackage => todo!(),
+                Opcode::Method => todo!(),
+                Opcode::External => todo!(),
+                Opcode::DualNamePrefix => todo!(),
+                Opcode::MultiNamePrefix => todo!(),
+                Opcode::Digit(_) => todo!(),
+                Opcode::NameChar(_) => todo!(),
+                Opcode::Mutex => todo!(),
+                Opcode::Event => todo!(),
+                Opcode::CondRefOf => todo!(),
+                Opcode::CreateField => todo!(),
+                Opcode::LoadTable => todo!(),
+                Opcode::Load => todo!(),
+                Opcode::Stall => todo!(),
+                Opcode::Sleep => todo!(),
+                Opcode::Acquire => todo!(),
+                Opcode::Signal => todo!(),
+                Opcode::Wait => todo!(),
+                Opcode::Reset => todo!(),
+                Opcode::Release => todo!(),
+                Opcode::FromBCD => todo!(),
+                Opcode::ToBCD => todo!(),
+                Opcode::Revision => todo!(),
+                Opcode::Debug => todo!(),
+                Opcode::Fatal => todo!(),
+                Opcode::Timer => todo!(),
+                Opcode::OpRegion => todo!(),
+                Opcode::Field => todo!(),
+                Opcode::Device => {
+                    let start_pc = context.current_block.pc;
+                    let pkg_length = context.pkglength()?;
+                    let name = context.namestring()?;
+
+                    let remaining_length = pkg_length - (context.current_block.pc - start_pc);
+
+                    let new_scope = name.resolve(&context.current_scope)?;
+                    self.namespace.lock().add_level(new_scope.clone(), NamespaceLevelKind::Device)?;
+
+                    // println!("Device. length = {}({}), name = {:?}", pkg_length, remaining_length, new_scope);
+                    let old_scope = mem::replace(&mut context.current_scope, new_scope);
+                    context.start_new_block(BlockKind::Scope { old_scope }, remaining_length);
+                }
+                Opcode::PowerRes => todo!(),
+                Opcode::ThermalZone => todo!(),
+                Opcode::IndexField => todo!(),
+                Opcode::BankField => todo!(),
+                Opcode::DataRegion => todo!(),
+                Opcode::RootChar => todo!(),
+                Opcode::ParentPrefixChar => todo!(),
+                Opcode::Local(local) => {
+                    let local = context.locals[local as usize].clone();
+                    context.last_op()?.arguments.push(Argument::Object(Arc::new(Object::Reference(local))));
+                }
+                Opcode::Arg(arg) => todo!(),
+                Opcode::Store => todo!(),
+                Opcode::RefOf => todo!(),
+                Opcode::Add => {
+                    context.start_in_flight_op(OpInFlight::new(Opcode::Add, 3));
+                }
+                Opcode::Concat => todo!(),
+                Opcode::Subtract => todo!(),
+                Opcode::Increment => todo!(),
+                Opcode::Decrement => todo!(),
+                Opcode::Multiply => todo!(),
+                Opcode::Divide => todo!(),
+                Opcode::ShiftLeft => todo!(),
+                Opcode::ShiftRight => todo!(),
+                Opcode::And => todo!(),
+                Opcode::Nand => todo!(),
+                Opcode::Or => todo!(),
+                Opcode::Nor => todo!(),
+                Opcode::Xor => todo!(),
+                Opcode::Not => todo!(),
+                Opcode::FindSetLeftBit => todo!(),
+                Opcode::FindSetRightBit => todo!(),
+                Opcode::DerefOf => todo!(),
+                Opcode::ConcatRes => todo!(),
+                Opcode::Mod => todo!(),
+                Opcode::Notify => todo!(),
+                Opcode::SizeOf => todo!(),
+                Opcode::Index => todo!(),
+                Opcode::Match => todo!(),
+                Opcode::CreateDWordField => todo!(),
+                Opcode::CreateWordField => todo!(),
+                Opcode::CreateByteField => todo!(),
+                Opcode::CreateBitField => todo!(),
+                Opcode::ObjectType => todo!(),
+                Opcode::CreateQWordField => todo!(),
+                Opcode::LAnd => todo!(),
+                Opcode::LOr => todo!(),
+                Opcode::LNot => todo!(),
+                Opcode::LNotEqual => todo!(),
+                Opcode::LLessEqual => todo!(),
+                Opcode::LGreaterEqual => todo!(),
+                Opcode::LEqual => todo!(),
+                Opcode::LGreater => todo!(),
+                Opcode::LLess => todo!(),
+                Opcode::ToBuffer => todo!(),
+                Opcode::ToDecimalString => todo!(),
+                Opcode::ToHexString => todo!(),
+                Opcode::ToInteger => todo!(),
+                Opcode::ToString => todo!(),
+                Opcode::CopyObject => todo!(),
+                Opcode::Mid => todo!(),
+                Opcode::Continue => todo!(),
+                Opcode::If => todo!(),
+                Opcode::Else => todo!(),
+                Opcode::While => todo!(),
+                Opcode::Noop => {}
+                Opcode::Return => todo!(),
+                Opcode::Break => todo!(),
+                Opcode::Breakpoint => todo!(),
+                Opcode::Ones => {
+                    context.last_op()?.arguments.push(Argument::Object(Arc::new(Object::Integer(u64::MAX))));
+                }
+            }
         }
     }
+}
 
-    // TODO: docs
-    pub fn initialize_objects(&mut self) -> Result<(), AmlError> {
-        use name_object::NameSeg;
-        use value::StatusObject;
+pub struct MethodContext<'a> {
+    current_block: Block<'a>,
+    block_stack: Vec<Block<'a>>,
+    in_flight: Vec<OpInFlight>,
+    locals: [Arc<Object>; 8],
+    current_scope: AmlName,
+}
 
-        /*
-         * 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),
-        }
+#[derive(Debug)]
+pub struct OpInFlight {
+    op: Opcode,
+    expected_arguments: usize,
+    arguments: Vec<Argument>,
+}
 
-        /*
-         * 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())?;
-                }
+#[derive(Debug)]
+pub enum Argument {
+    Object(Arc<Object>),
+    Namestring(AmlName),
+}
 
-                /*
-                 * We traverse the children of this device if it's present, or isn't present but is functional.
-                 */
-                Ok(status.present || status.functional)
-            }
+pub struct Block<'a> {
+    stream: &'a [u8],
+    pc: usize,
+    kind: BlockKind,
+}
 
-            LevelType::Scope => Ok(true),
+#[derive(Debug)]
+pub enum BlockKind {
+    Normal,
+    Scope { old_scope: AmlName },
+}
 
-            // TODO: can any of these contain devices?
-            LevelType::Processor => Ok(false),
-            LevelType::PowerResource => Ok(false),
-            LevelType::ThermalZone => Ok(false),
-            LevelType::MethodLocals => Ok(false),
-        })?;
+impl OpInFlight {
+    pub fn new(op: Opcode, expected_arguments: usize) -> OpInFlight {
+        OpInFlight { op, expected_arguments, arguments: Vec::new() }
+    }
 
-        Ok(())
+    pub fn new_with(op: Opcode, arguments: Vec<Argument>, more: usize) -> OpInFlight {
+        OpInFlight { op, expected_arguments: arguments.len() + more, arguments }
     }
+}
 
-    pub(crate) fn read_target(&self, target: &Target) -> Result<&AmlValue, AmlError> {
-        match target {
-            Target::Null => todo!(),
-            Target::Name(name) => {
-                let (_, handle) = self.namespace.search(name, &self.current_scope)?;
-                self.namespace.get(handle)
-            }
-            Target::Debug => todo!(),
-            Target::Arg(arg) => self.current_arg(*arg),
-            Target::Local(local) => self.local(*local),
+impl<'a> MethodContext<'a> {
+    pub fn new(stream: &'a [u8]) -> MethodContext<'a> {
+        let block = Block { stream, pc: 0, kind: BlockKind::Normal };
+        MethodContext {
+            current_block: block,
+            block_stack: Vec::new(),
+            in_flight: Vec::new(),
+            locals: core::array::from_fn(|_| Arc::new(Object::Uninitialized)),
+            current_scope: AmlName::root(),
         }
     }
 
-    /// 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.method_context.as_ref().ok_or(AmlError::NotExecutingControlMethod)?.args.arg(arg)
+    pub fn last_op(&mut self) -> Result<&mut OpInFlight, AmlError> {
+        match self.in_flight.last_mut() {
+            Some(op) => Ok(op),
+            None => Err(AmlError::NoCurrentOp),
+        }
     }
 
-    /// 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 self.method_context.is_none() {
-            return Err(AmlError::NotExecutingControlMethod);
-        }
-        if local > 7 {
-            return Err(AmlError::InvalidLocalAccess(local));
-        }
+    pub fn start_in_flight_op(&mut self, op: OpInFlight) {
+        // println!("Starting in-flight op of type: {:?}", op);
+        self.in_flight.push(op);
+    }
+
+    pub fn start_new_block(&mut self, kind: BlockKind, length: usize) {
+        // println!("Starting new block at pc={}, length={}, kind={:?}", self.current_block.pc, length, kind);
+        let block = Block {
+            stream: &self.current_block.stream[..(self.current_block.pc + length)],
+            pc: self.current_block.pc,
+            kind,
+        };
+        self.current_block.pc += length;
+        self.block_stack.push(mem::replace(&mut self.current_block, block));
+    }
+
+    pub fn opcode(&mut self) -> Result<Opcode, AmlError> {
+        let opcode: u16 = match self.next()? {
+            0x5b => {
+                let ext = self.next()?;
+                (0x5b << 8) as u16 | ext as u16
+            }
+            other => other as u16,
+        };
+
+        Ok(match opcode {
+            0x00 => Opcode::Zero,
+            0x01 => Opcode::One,
+            0x06 => Opcode::Alias,
+            0x08 => Opcode::Name,
+            0x0a => Opcode::BytePrefix,
+            0x0b => Opcode::WordPrefix,
+            0x0c => Opcode::DWordPrefix,
+            0x0d => Opcode::StringPrefix,
+            0x0e => Opcode::QWordPrefix,
+            0x10 => Opcode::Scope,
+            0x11 => Opcode::Buffer,
+            0x12 => Opcode::Package,
+            0x13 => Opcode::VarPackage,
+            0x14 => Opcode::Method,
+            0x15 => Opcode::External,
+            0x2e => Opcode::DualNamePrefix,
+            0x2f => Opcode::MultiNamePrefix,
+            0x30..=0x39 => Opcode::Digit(opcode as u8),    // b'0'..=b'9'
+            0x41..=0x5a => Opcode::NameChar(opcode as u8), // b'A'..=b'Z'
+            0x5b01 => Opcode::Mutex,
+            0x5b02 => Opcode::Event,
+            0x5b12 => Opcode::CondRefOf,
+            0x5b13 => Opcode::CreateField,
+            0x5b1f => Opcode::LoadTable,
+            0x5b20 => Opcode::Load,
+            0x5b21 => Opcode::Stall,
+            0x5b22 => Opcode::Sleep,
+            0x5b23 => Opcode::Acquire,
+            0x5b24 => Opcode::Signal,
+            0x5b25 => Opcode::Wait,
+            0x5b26 => Opcode::Reset,
+            0x5b27 => Opcode::Release,
+            0x5b28 => Opcode::FromBCD,
+            0x5b29 => Opcode::ToBCD,
+            0x5b30 => Opcode::Revision,
+            0x5b31 => Opcode::Debug,
+            0x5b32 => Opcode::Fatal,
+            0x5b33 => Opcode::Timer,
+            0x5b80 => Opcode::OpRegion,
+            0x5b81 => Opcode::Field,
+            0x5b82 => Opcode::Device,
+            0x5b84 => Opcode::PowerRes,
+            0x5b85 => Opcode::ThermalZone,
+            0x5b86 => Opcode::IndexField,
+            0x5b87 => Opcode::BankField,
+            0x5b88 => Opcode::DataRegion,
+            0x5c => Opcode::RootChar,
+            0x5e => Opcode::ParentPrefixChar,
+            0x5f => Opcode::NameChar(b'_'),
+            0x60..=0x67 => Opcode::Local(opcode as u8 - 0x60),
+            0x68..=0x6e => Opcode::Arg(opcode as u8 - 0x68),
+            0x70 => Opcode::Store,
+            0x71 => Opcode::RefOf,
+            0x72 => Opcode::Add,
+            0x73 => Opcode::Concat,
+            0x74 => Opcode::Subtract,
+            0x75 => Opcode::Increment,
+            0x76 => Opcode::Decrement,
+            0x77 => Opcode::Multiply,
+            0x78 => Opcode::Divide,
+            0x79 => Opcode::ShiftLeft,
+            0x7a => Opcode::ShiftRight,
+            0x7b => Opcode::And,
+            0x7c => Opcode::Nand,
+            0x7d => Opcode::Or,
+            0x7e => Opcode::Nor,
+            0x7f => Opcode::Xor,
+            0x80 => Opcode::Not,
+            0x81 => Opcode::FindSetLeftBit,
+            0x82 => Opcode::FindSetRightBit,
+            0x83 => Opcode::DerefOf,
+            0x84 => Opcode::ConcatRes,
+            0x85 => Opcode::Mod,
+            0x86 => Opcode::Notify,
+            0x87 => Opcode::SizeOf,
+            0x88 => Opcode::Index,
+            0x89 => Opcode::Match,
+            0x8a => Opcode::CreateDWordField,
+            0x8b => Opcode::CreateWordField,
+            0x8c => Opcode::CreateByteField,
+            0x8d => Opcode::CreateBitField,
+            0x8e => Opcode::ObjectType,
+            0x8f => Opcode::CreateQWordField,
+            0x90 => Opcode::LAnd,
+            0x91 => Opcode::LOr,
+            /*
+             * `0x92` is a bit strange. It can be an opcode in its own right (`LNotOp`), but when
+             * followed by `0x93..=0x95`, it instead serves as a negating prefix to encode
+             * `LNotEqualOp`, `LLessEqualOp`, and `LGreaterEqualOp`.
+             */
+            0x92 => match self.peek() {
+                Ok(0x93) => {
+                    self.current_block.pc += 1;
+                    Opcode::LNotEqual
+                }
+                Ok(0x94) => {
+                    self.current_block.pc += 1;
+                    Opcode::LLessEqual
+                }
+                Ok(0x95) => {
+                    self.current_block.pc += 1;
+                    Opcode::LGreaterEqual
+                }
+                _ => Opcode::LNot,
+            },
+            0x93 => Opcode::LEqual,
+            0x94 => Opcode::LGreater,
+            0x95 => Opcode::LLess,
+            0x96 => Opcode::ToBuffer,
+            0x97 => Opcode::ToDecimalString,
+            0x98 => Opcode::ToHexString,
+            0x99 => Opcode::ToInteger,
+            0x9c => Opcode::ToString,
+            0x9d => Opcode::CopyObject,
+            0x9e => Opcode::Mid,
+            0x9f => Opcode::Continue,
+            0xa0 => Opcode::If,
+            0xa1 => Opcode::Else,
+            0xa2 => Opcode::While,
+            0xa3 => Opcode::Noop,
+            0xa4 => Opcode::Return,
+            0xa5 => Opcode::Break,
+            0xcc => Opcode::Breakpoint,
+            0xff => Opcode::Ones,
+
+            _ => Err(AmlError::IllegalOpcode)?,
+        })
+    }
 
-        self.method_context.as_ref().unwrap().locals[local as usize]
-            .as_ref()
-            .ok_or(AmlError::InvalidLocalAccess(local))
+    pub fn pkglength(&mut self) -> Result<usize, AmlError> {
+        let lead_byte = self.next()?;
+        let byte_count = lead_byte.get_bits(6..8);
+        assert!(byte_count < 4);
+
+        if byte_count == 0 {
+            Ok(lead_byte.get_bits(0..6) as usize)
+        } else {
+            let mut length = lead_byte.get_bits(0..4) as usize;
+            for i in 0..byte_count {
+                length |= (self.next()? as usize) << (4 + i * 8);
+            }
+            Ok(length)
+        }
     }
 
-    /// Perform a store into a `Target`, according to the rules specified by §19.3.5.8. This returns a value read
-    /// out of the target, if neccessary, as values can be altered during a store in some circumstances.  When
-    /// required, this also performs required implicit conversions, otherwise stores are semantically equivalent to
-    /// a `CopyObject`.
-    pub(crate) fn store(&mut self, target: Target, value: AmlValue) -> Result<AmlValue, AmlError> {
-        match target {
-            Target::Name(ref path) => {
-                let (_, handle) = self.namespace.search(path, &self.current_scope)?;
-
-                match self.namespace.get(handle).unwrap().type_of() {
-                    AmlType::FieldUnit => {
-                        let mut field = self.namespace.get(handle).unwrap().clone();
-                        field.write_field(value, self)?;
-                        field.read_field(self)
+    pub fn namestring(&mut self) -> Result<AmlName, AmlError> {
+        use namespace::{NameComponent, NameSeg};
+
+        const NULL_NAME: u8 = 0x00;
+        const DUAL_NAME_PREFIX: u8 = 0x2e;
+        const MULTI_NAME_PREFIX: u8 = 0x2f;
+
+        let mut components = vec![];
+        loop {
+            let next = match self.next() {
+                Ok(next) => next,
+                Err(AmlError::RunOutOfStream) => break,
+                Err(other) => Err(other)?,
+            };
+            match next {
+                b'\\' => {
+                    if !components.is_empty() {
+                        return Err(AmlError::InvalidName(None));
+                    }
+                    components.push(NameComponent::Root);
+                }
+                b'^' => components.push(NameComponent::Prefix),
+                NULL_NAME => {}
+                DUAL_NAME_PREFIX => {
+                    for _ in 0..2 {
+                        let name_seg = [self.next()?, self.next()?, self.next()?, self.next()?];
+                        components.push(NameComponent::Segment(NameSeg::from_bytes(name_seg)?));
                     }
-                    AmlType::BufferField => {
-                        let mut buffer_field = self.namespace.get(handle).unwrap().clone();
-                        buffer_field.write_buffer_field(value.clone(), self)?;
-                        Ok(value)
+                }
+                MULTI_NAME_PREFIX => {
+                    let count = self.next()?;
+                    for _ in 0..count {
+                        let name_seg = [self.next()?, self.next()?, self.next()?, self.next()?];
+                        components.push(NameComponent::Segment(NameSeg::from_bytes(name_seg)?));
                     }
-                    typ => {
-                        *self.namespace.get_mut(handle)? = value.as_type(typ, self)?;
-                        Ok(self.namespace.get(handle)?.clone())
+                }
+                first_char => {
+                    if !namespace::is_lead_name_char(first_char) {
+                        self.current_block.pc -= 1;
+                        break;
                     }
+                    let name_seg = [first_char, self.next()?, self.next()?, self.next()?];
+                    components.push(namespace::NameComponent::Segment(namespace::NameSeg::from_bytes(name_seg)?));
                 }
             }
+        }
 
-            Target::Debug => {
-                // TODO
-                unimplemented!()
-            }
+        Ok(AmlName::from_components(components))
+    }
 
-            Target::Arg(arg_num) => {
-                if self.method_context.is_none() {
-                    return Err(AmlError::NotExecutingControlMethod);
-                }
+    pub fn next(&mut self) -> Result<u8, AmlError> {
+        if self.current_block.pc >= self.current_block.stream.len() {
+            return Err(AmlError::RunOutOfStream);
+        }
 
-                /*
-                 * Stores into `Arg` objects are simply copied with no conversion applied, unless the `Arg`
-                 * contains an Object Reference, in which case an automatic de-reference occurs and the object is
-                 * copied to the target of the Object Reference, instead of overwriting the `Arg.`
-                 */
-                // TODO: implement behaviour for object references
-                self.method_context.as_mut().unwrap().args.store_arg(arg_num, value.clone())?;
-                Ok(value)
-            }
+        let byte = self.current_block.stream[self.current_block.pc];
+        self.current_block.pc += 1;
 
-            Target::Local(local_num) => {
-                if self.method_context.is_none() {
-                    return Err(AmlError::NotExecutingControlMethod);
-                }
+        Ok(byte)
+    }
 
-                /*
-                 * Stores into `Local` objects are always simply copied into the destination with no conversion
-                 * applied, even if it contains an Object Reference.
-                 */
-                self.method_context.as_mut().unwrap().locals[local_num as usize] = Some(value.clone());
-                Ok(value)
-            }
+    pub fn next_u16(&mut self) -> Result<u16, AmlError> {
+        Ok(u16::from_le_bytes([self.next()?, self.next()?]))
+    }
+
+    pub fn next_u32(&mut self) -> Result<u32, AmlError> {
+        Ok(u32::from_le_bytes([self.next()?, self.next()?, self.next()?, self.next()?]))
+    }
 
-            Target::Null => Ok(value),
+    pub fn next_u64(&mut self) -> Result<u64, AmlError> {
+        Ok(u64::from_le_bytes([
+            self.next()?,
+            self.next()?,
+            self.next()?,
+            self.next()?,
+            self.next()?,
+            self.next()?,
+            self.next()?,
+            self.next()?,
+        ]))
+    }
+
+    pub fn peek(&self) -> Result<u8, AmlError> {
+        if self.current_block.pc >= self.current_block.stream.len() {
+            return Err(AmlError::RunOutOfStream);
         }
+
+        Ok(self.current_block.stream[self.current_block.pc])
     }
+}
 
-    fn add_predefined_objects(&mut self) {
-        /*
-         * These are the scopes predefined by the spec. Some tables will try to access them without defining them
-         * themselves, and so we have to pre-create them.
-         */
-        self.namespace.add_level(AmlName::from_str("\\_GPE").unwrap(), LevelType::Scope).unwrap();
-        self.namespace.add_level(AmlName::from_str("\\_SB").unwrap(), LevelType::Scope).unwrap();
-        self.namespace.add_level(AmlName::from_str("\\_SI").unwrap(), LevelType::Scope).unwrap();
-        self.namespace.add_level(AmlName::from_str("\\_PR").unwrap(), LevelType::Scope).unwrap();
-        self.namespace.add_level(AmlName::from_str("\\_TZ").unwrap(), LevelType::Scope).unwrap();
+#[derive(Clone, Copy, PartialEq, Debug)]
+pub enum Opcode {
+    Zero,
+    One,
+    Alias,
+    Name,
+    BytePrefix,
+    WordPrefix,
+    DWordPrefix,
+    StringPrefix,
+    QWordPrefix,
+    Scope,
+    Buffer,
+    Package,
+    VarPackage,
+    Method,
+    External,
+    DualNamePrefix,
+    MultiNamePrefix,
+    Digit(u8),
+    NameChar(u8),
+    Mutex,
+    Event,
+    CondRefOf,
+    CreateField,
+    LoadTable,
+    Load,
+    Stall,
+    Sleep,
+    Acquire,
+    Signal,
+    Wait,
+    Reset,
+    Release,
+    FromBCD,
+    ToBCD,
+    Revision,
+    Debug,
+    Fatal,
+    Timer,
+    OpRegion,
+    Field,
+    Device,
+    PowerRes,
+    ThermalZone,
+    IndexField,
+    BankField,
+    DataRegion,
+    RootChar,
+    ParentPrefixChar,
+    Local(u8),
+    Arg(u8),
+    Store,
+    RefOf,
+    Add,
+    Concat,
+    Subtract,
+    Increment,
+    Decrement,
+    Multiply,
+    Divide,
+    ShiftLeft,
+    ShiftRight,
+    And,
+    Nand,
+    Or,
+    Nor,
+    Xor,
+    Not,
+    FindSetLeftBit,
+    FindSetRightBit,
+    DerefOf,
+    ConcatRes,
+    Mod,
+    Notify,
+    SizeOf,
+    Index,
+    Match,
+    CreateDWordField,
+    CreateWordField,
+    CreateByteField,
+    CreateBitField,
+    ObjectType,
+    CreateQWordField,
+    LAnd,
+    LOr,
+    LNot,
+    LNotEqual,
+    LLessEqual,
+    LGreaterEqual,
+    LEqual,
+    LGreater,
+    LLess,
+    ToBuffer,
+    ToDecimalString,
+    ToHexString,
+    ToInteger,
+    ToString,
+    CopyObject,
+    Mid,
+    Continue,
+    If,
+    Else,
+    While,
+    Noop,
+    Return,
+    Break,
+    Breakpoint,
+    Ones,
+}
 
-        /*
-         * In the dark ages of ACPI 1.0, before `\_OSI`, `\_OS` was used to communicate to the firmware which OS
-         * was running. This was predictably not very good, and so was replaced in ACPI 3.0 with `_OSI`, which
-         * allows support for individual capabilities to be queried. `_OS` should not be used by modern firmwares,
-         * but to avoid problems we follow Linux in returning `"Microsoft Windows NT"`.
-         *
-         * See https://www.kernel.org/doc/html/latest/firmware-guide/acpi/osi.html for more information.
-         */
-        self.namespace
-            .add_value(AmlName::from_str("\\_OS").unwrap(), AmlValue::String("Microsoft Windows NT".to_string()))
-            .unwrap();
+/*
+ * TODO: not sure if we should use a better error reporting system or just keep a giant enum?
+ */
+#[derive(Clone, PartialEq, Debug)]
+pub enum AmlError {
+    RunOutOfStream,
+    IllegalOpcode,
 
-        /*
-         * `\_OSI` was introduced by ACPI 3.0 to improve the situation created by `\_OS`. Unfortunately, exactly
-         * the same problem was immediately repeated by introducing capabilities reflecting that an ACPI
-         * implementation is exactly the same as a particular version of Windows' (e.g. firmwares will call
-         * `\_OSI("Windows 2001")`).
-         *
-         * We basically follow suit with whatever Linux does, as this will hopefully minimise breakage:
-         *    - We always claim `Windows *` compatability
-         *    - We answer 'yes' to `_OSI("Darwin")
-         *    - We answer 'no' to `_OSI("Linux")`, and report that the tables are doing the wrong thing
-         */
-        self.namespace
-            .add_value(
-                AmlName::from_str("\\_OSI").unwrap(),
-                AmlValue::native_method(1, false, 0, |context| {
-                    let value = context.current_arg(0)?.clone();
-                    Ok(
-                        if match value.as_string(context)?.as_str() {
-                            "Windows 2000" => true,       // 2000
-                            "Windows 2001" => true,       // XP
-                            "Windows 2001 SP1" => true,   // XP SP1
-                            "Windows 2001 SP2" => true,   // XP SP2
-                            "Windows 2001.1" => true,     // Server 2003
-                            "Windows 2001.1 SP1" => true, // Server 2003 SP1
-                            "Windows 2006" => true,       // Vista
-                            "Windows 2006 SP1" => true,   // Vista SP1
-                            "Windows 2006 SP2" => true,   // Vista SP2
-                            "Windows 2006.1" => true,     // Server 2008
-                            "Windows 2009" => true,       // 7 and Server 2008 R2
-                            "Windows 2012" => true,       // 8 and Server 2012
-                            "Windows 2013" => true,       // 8.1 and Server 2012 R2
-                            "Windows 2015" => true,       // 10
-                            "Windows 2016" => true,       // 10 version 1607
-                            "Windows 2017" => true,       // 10 version 1703
-                            "Windows 2017.2" => true,     // 10 version 1709
-                            "Windows 2018" => true,       // 10 version 1803
-                            "Windows 2018.2" => true,     // 10 version 1809
-                            "Windows 2019" => true,       // 10 version 1903
-
-                            "Darwin" => true,
-
-                            "Linux" => {
-                                // TODO: should we allow users to specify that this should be true? Linux has a
-                                // command line option for this.
-                                warn!("ACPI evaluated `_OSI(\"Linux\")`. This is a bug. Reporting no support.");
-                                false
-                            }
+    InvalidName(Option<AmlName>),
 
-                            "Extended Address Space Descriptor" => true,
-                            // TODO: support module devices
-                            "Module Device" => false,
-                            "3.0 Thermal Model" => true,
-                            "3.0 _SCP Extensions" => true,
-                            // TODO: support processor aggregator devices
-                            "Processor Aggregator Device" => false,
-
-                            _ => false,
-                        } {
-                            AmlValue::ones()
-                        } else {
-                            AmlValue::zero()
-                        },
-                    )
-                }),
-            )
-            .unwrap();
+    InvalidNameSeg,
+    InvalidNormalizedName(AmlName),
+    RootHasNoParent,
+    EmptyNamesAreInvalid,
+    LevelDoesNotExist(AmlName),
+    NameCollision(AmlName),
+    ObjectDoesNotExist(AmlName),
 
-        /*
-         * `\_REV` evaluates to the version of the ACPI specification supported by this interpreter. Linux did this
-         * correctly until 2015, but firmwares misused this to detect Linux (as even modern versions of Windows
-         * return `2`), and so they switched to just returning `2` (as we'll also do). `_REV` should be considered
-         * useless and deprecated (this is mirrored in newer specs, which claim `2` means "ACPI 2 or greater").
-         */
-        self.namespace.add_value(AmlName::from_str("\\_REV").unwrap(), AmlValue::Integer(2)).unwrap();
-    }
+    NoCurrentOp,
 }
 
-/// Trait type used by [`AmlContext`] to handle reading and writing to various types of memory in the system.
+/// This trait represents the interface from the `Interpreter` to the hosting kernel, and allows
+/// AML to interact with the underlying hardware.
+// TODO: maybe use `pci_types::PciAddress` to simplify PCI address passing here
 pub trait Handler: Send + Sync {
     fn read_u8(&self, address: usize) -> u8;
     fn read_u16(&self, address: usize) -> u16;
@@ -553,131 +802,63 @@ pub trait Handler: Send + Sync {
     fn sleep(&self, milliseconds: u64);
 
     fn handle_fatal_error(&self, fatal_type: u8, fatal_code: u32, fatal_arg: u64) {
-        panic!("Fatal error while executing AML (encountered DefFatal op). fatal_type = {:?}, fatal_code = {:?}, fatal_arg = {:?}", fatal_type, fatal_code, fatal_arg);
+        panic!(
+            "Fatal error while executing AML (encountered DefFatalOp). fatal_type = {:?}, fatal_code = {:?}, fatal_arg = {:?}",
+            fatal_type, fatal_code, fatal_arg
+        );
     }
 }
 
-/// Used when an [`AmlContext`] encounters an error.
-#[derive(Clone, PartialEq, Eq, Debug)]
-pub enum AmlError {
-    /*
-     * Errors produced parsing the AML stream.
-     */
-    UnexpectedEndOfStream,
-    UnexpectedByte(u8),
-    /// Produced when the stream evaluates to something other than nothing or an error.
-    MalformedStream,
-    InvalidNameSeg,
-    InvalidPkgLength,
-    /// Invalid PkgLength relative to an OperationRegion
-    InvalidRegionPkgLength {
-        region_bit_length: u64,
-        raw_length: u32,
-    },
-    InvalidFieldFlags,
-    UnterminatedStringConstant,
-    InvalidStringConstant,
-    InvalidRegionSpace(u8),
-    /// Produced when a `DefPackage` contains a different number of elements to the package's length.
-    MalformedPackage,
-    /// Produced when a `DefBuffer` contains more bytes that its size.
-    MalformedBuffer,
-    /// Emitted by a parser when it's clear that the stream doesn't encode the object parsed by
-    /// that parser (e.g. the wrong opcode starts the stream). This is handled specially by some
-    /// parsers such as `or` and `choice!`.
-    WrongParser,
-    /// Returned when a `DefFatal` op is encountered. This is separately reported using [`Handler::handle_fatal_error`].
-    FatalError,
-
-    /*
-     * Errors produced manipulating AML names.
-     */
-    EmptyNamesAreInvalid,
-    /// Produced when trying to normalize a path that does not point to a valid level of the
-    /// namespace. E.g. `\_SB.^^PCI0` goes above the root of the namespace. The contained value is the name that
-    /// normalization was attempted upon.
-    InvalidNormalizedName(AmlName),
-    RootHasNoParent,
-
-    /*
-     * Errors produced working with the namespace.
-     */
-    /// Produced when a sub-level or value is added to a level that has not yet been added to the namespace. The
-    /// `AmlName` is the name of the entire sub-level/value.
-    LevelDoesNotExist(AmlName),
-    ValueDoesNotExist(AmlName),
-    /// Produced when two values with the same name are added to the namespace.
-    NameCollision(AmlName),
-    TriedToRemoveRootNamespace,
-
-    /*
-     * 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.
-    InvalidArgAccess(ArgNum),
-    /// Produced when a method accesses a local that it has not stored into.
-    InvalidLocalAccess(LocalNum),
-    /// Tried to invoke a method with too many arguments.
-    TooManyArgs,
-    /// A `DefBreak` operation was performed outside of a `DefWhile` or `DefSwitch`.
-    BreakInInvalidPosition,
-    /// A `DefContinue` operation was performed outside of a `DefWhile`.
-    ContinueInInvalidPosition,
-
-    /*
-     * Errors produced parsing the PCI routing tables (_PRT objects).
-     */
-    PrtInvalidAddress,
-    PrtInvalidPin,
-    PrtInvalidSource,
-    PrtInvalidGsi,
-    /// Produced when the PRT doesn't contain an entry for the requested address + pin
-    PrtNoEntry,
-
-    /*
-     * Errors produced parsing Resource Descriptors.
-     */
-    ReservedResourceType,
-    ResourceDescriptorTooShort,
-    ResourceDescriptorTooLong,
-    UnexpectedResourceType,
-
-    /*
-     * Errors produced working with AML values.
-     */
-    IncompatibleValueConversion {
-        current: AmlType,
-        target: AmlType,
-    },
-    InvalidStatusObject,
-    InvalidShiftLeft,
-    InvalidShiftRight,
-    FieldRegionIsNotOpRegion,
-    FieldInvalidAddress,
-    FieldInvalidAccessSize,
-    TypeCannotBeCompared(AmlType),
-    /// Produced when the `Mid` operator is applied to a value of a type other than `Buffer` or `String`.
-    TypeCannotBeSliced(AmlType),
-    TypeCannotBeWrittenToBufferField(AmlType),
-    BufferFieldIndexesOutOfBounds,
-    InvalidSizeOfApplication(AmlType),
-
-    /// Unimplemented functionality - return error rather than abort
-    Unimplemented,
-}
-
 #[cfg(test)]
 mod tests {
     use super::*;
+    use std::str::FromStr;
+
+    struct TestHandler;
+    #[rustfmt::skip]
+    impl Handler for TestHandler {
+        fn read_u8(&self, _address: usize) -> u8 {0}
+        fn read_u16(&self, _address: usize) -> u16 {0}
+        fn read_u32(&self, _address: usize) -> u32 {0}
+        fn read_u64(&self, _address: usize) -> u64 {0}
+        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 {0}
+        fn read_io_u16(&self, _port: u16) -> u16 {0}
+        fn read_io_u32(&self, _port: u16) -> u32 {0}
+        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 {0}
+        fn read_pci_u16(&self, _segment: u16, _bus: u8, _device: u8, _function: u8, _offset: u16) -> u16 {0}
+        fn read_pci_u32(&self, _segment: u16, _bus: u8, _device: u8, _function: u8, _offset: u16) -> u32 {0}
+        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) {}
+        fn stall(&self, _microseconds: u64) {}
+        fn sleep(&self, _milliseconds: u64) {}
+    }
+
+    #[test]
+    fn add_op() {
+        let interpreter = Interpreter::new(TestHandler);
+        // AddOp 0x0e 0x06 => Local2
+        interpreter.execute_method(&[0x72, 0x0b, 0x0e, 0x00, 0x0a, 0x06, 0x62]).unwrap();
+        // AddOp 0x0e (AddOp 0x01 0x03 => Local1) => Local1
+        interpreter.execute_method(&[0x72, 0x0a, 0x0e, 0x72, 0x0a, 0x01, 0x0a, 0x03, 0x61, 0x61]).unwrap();
+    }
 
     #[test]
-    fn test_send_sync() {
-        // verify that AmlContext implements Send and Sync
-        fn test_send_sync<T: Send + Sync>() {}
-        test_send_sync::<AmlContext>();
+    fn names() {
+        assert_eq!(
+            MethodContext::new(b"\\\x2eABC_DEF_").namestring(),
+            Ok(AmlName::from_str("\\ABC.DEF").unwrap())
+        );
+        assert_eq!(
+            MethodContext::new(b"\x2eABC_DEF_^_GHI").namestring(),
+            Ok(AmlName::from_str("ABC.DEF.^_GHI").unwrap())
+        );
     }
 }
diff --git a/aml/src/misc.rs b/aml/src/misc.rs
deleted file mode 100644
index 210c3afe..00000000
--- a/aml/src/misc.rs
+++ /dev/null
@@ -1,83 +0,0 @@
-use crate::{
-    opcode::{self, ext_opcode, opcode},
-    parser::{choice, comment_scope, id, Parser},
-    DebugVerbosity,
-};
-
-pub fn debug_obj<'a, 'c>() -> impl Parser<'a, 'c, ()>
-where
-    'c: 'a,
-{
-    /*
-     * DebugObj := ExtOpPrefix 0x31
-     */
-    ext_opcode(opcode::EXT_DEBUG_OP)
-}
-
-/// Takes a value between `0` and `7`, where 0 represents `Local0` etc.
-pub type LocalNum = u8;
-
-pub fn local_obj<'a, 'c>() -> impl Parser<'a, 'c, LocalNum>
-where
-    'c: 'a,
-{
-    /*
-     * LocalObj := Local0Op | Local1Op | Local2Op | Local3Op | Local4Op | Local5Op | Local6Op | Local7Op
-     * Local0Op := 0x60
-     * Local1Op := 0x61
-     * Local2Op := 0x62
-     * Local3Op := 0x63
-     * Local4Op := 0x64
-     * Local5Op := 0x65
-     * Local6Op := 0x66
-     * Local7Op := 0x67
-     */
-    let local_parser = |i, local_opcode| {
-        opcode(local_opcode)
-            .then(comment_scope(DebugVerbosity::AllScopes, "LocalObj", id()))
-            .map(move |((), _)| Ok(i))
-    };
-
-    choice!(
-        local_parser(0, opcode::LOCAL0_OP),
-        local_parser(1, opcode::LOCAL1_OP),
-        local_parser(2, opcode::LOCAL2_OP),
-        local_parser(3, opcode::LOCAL3_OP),
-        local_parser(4, opcode::LOCAL4_OP),
-        local_parser(5, opcode::LOCAL5_OP),
-        local_parser(6, opcode::LOCAL6_OP),
-        local_parser(7, opcode::LOCAL7_OP)
-    )
-}
-
-/// Takes a value between `0` and `6`, where 0 represents `Arg0` etc.
-pub type ArgNum = u8;
-
-pub fn arg_obj<'a, 'c>() -> impl Parser<'a, 'c, ArgNum>
-where
-    'c: 'a,
-{
-    /*
-     * ArgObj := Arg0Op | Arg1Op | Arg2Op | Arg3Op | Arg4Op | Arg5Op | Arg6Op
-     * Arg0Op = 0x68
-     * Arg1Op = 0x69
-     * Arg2Op = 0x6a
-     * Arg3Op = 0x6b
-     * Arg4Op = 0x6c
-     * Arg5Op = 0x6d
-     * Arg6Op = 0x6e
-     */
-    let arg_parser = |i, arg_opcode| {
-        opcode(arg_opcode).then(comment_scope(DebugVerbosity::AllScopes, "ArgObj", id())).map(move |((), _)| Ok(i))
-    };
-
-    choice!(
-        arg_parser(0, opcode::ARG0_OP),
-        arg_parser(1, opcode::ARG1_OP),
-        arg_parser(2, opcode::ARG2_OP),
-        arg_parser(3, opcode::ARG3_OP),
-        arg_parser(4, opcode::ARG4_OP),
-        arg_parser(5, opcode::ARG5_OP),
-        arg_parser(6, opcode::ARG6_OP)
-    )
-}
diff --git a/aml/src/name_object.rs b/aml/src/name_object.rs
deleted file mode 100644
index 4c51a496..00000000
--- a/aml/src/name_object.rs
+++ /dev/null
@@ -1,305 +0,0 @@
-use crate::{
-    misc::{arg_obj, debug_obj, local_obj, ArgNum, LocalNum},
-    namespace::{AmlName, NameComponent},
-    opcode::{opcode, DUAL_NAME_PREFIX, MULTI_NAME_PREFIX, NULL_NAME, PREFIX_CHAR, ROOT_CHAR},
-    parser::{choice, comment_scope, consume, n_of, take, take_while, Parser, Propagate},
-    AmlContext,
-    AmlError,
-    DebugVerbosity,
-};
-use alloc::vec::Vec;
-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,
-{
-    /*
-     * SuperName := SimpleName | DebugObj | ReferenceTypeOpcode
-     * TODO: this doesn't cover ReferenceTypeOpcode yet
-     */
-    comment_scope(
-        DebugVerbosity::AllScopes,
-        "SuperName",
-        choice!(debug_obj().map(|()| Ok(Target::Debug)), simple_name()),
-    )
-}
-
-pub fn simple_name<'a, 'c>() -> impl Parser<'a, 'c, Target>
-where
-    'c: 'a,
-{
-    /*
-     * SimpleName := NameString | ArgObj | LocalObj
-     */
-    comment_scope(
-        DebugVerbosity::AllScopes,
-        "SimpleName",
-        choice!(
-            arg_obj().map(|arg_num| Ok(Target::Arg(arg_num))),
-            local_obj().map(|local_num| Ok(Target::Local(local_num))),
-            name_string().map(move |name| Ok(Target::Name(name)))
-        ),
-    )
-}
-
-pub fn name_string<'a, 'c>() -> impl Parser<'a, 'c, AmlName>
-where
-    'c: 'a,
-{
-    /*
-     * NameString := <RootChar('\') NamePath> | <PrefixPath NamePath>
-     * PrefixPath := Nothing | <'^' PrefixPath>
-     */
-    let root_name_string = opcode(ROOT_CHAR).then(name_path()).map(|((), ref name_path)| {
-        let mut name = alloc::vec![NameComponent::Root];
-        name.extend_from_slice(name_path);
-        Ok(AmlName::from_components(name))
-    });
-
-    let prefix_path =
-        take_while(opcode(PREFIX_CHAR)).then(name_path()).map(|(num_prefix_chars, ref name_path)| {
-            let mut name = alloc::vec![NameComponent::Prefix; num_prefix_chars];
-            name.extend_from_slice(name_path);
-            Ok(AmlName::from_components(name))
-        });
-
-    // TODO: combinator to select a parser based on a peeked byte?
-    comment_scope(DebugVerbosity::AllScopes, "NameString", move |input: &'a [u8], context| {
-        let first_char = match input.first() {
-            Some(&c) => c,
-            None => return Err((input, context, Propagate::Err(AmlError::UnexpectedEndOfStream))),
-        };
-
-        match first_char {
-            ROOT_CHAR => root_name_string.parse(input, context),
-            PREFIX_CHAR => prefix_path.parse(input, context),
-            _ => name_path()
-                .map(|path| {
-                    if path.is_empty() {
-                        return Err(Propagate::Err(AmlError::EmptyNamesAreInvalid));
-                    }
-
-                    Ok(AmlName::from_components(path))
-                })
-                .parse(input, context),
-        }
-    })
-}
-
-pub fn name_path<'a, 'c>() -> impl Parser<'a, 'c, Vec<NameComponent>>
-where
-    'c: 'a,
-{
-    /*
-     * NamePath := NullName | DualNamePath | MultiNamePath | NameSeg
-     */
-    choice!(
-        null_name(),
-        dual_name_path(),
-        multi_name_path(),
-        name_seg().map(|seg| Ok(alloc::vec![NameComponent::Segment(seg)]))
-    )
-}
-
-pub fn null_name<'a, 'c>() -> impl Parser<'a, 'c, Vec<NameComponent>>
-where
-    'c: 'a,
-{
-    /*
-     * NullName := 0x00
-     */
-    opcode(NULL_NAME).map(|_| Ok(Vec::with_capacity(0)))
-}
-
-pub fn dual_name_path<'a, 'c>() -> impl Parser<'a, 'c, Vec<NameComponent>>
-where
-    'c: 'a,
-{
-    /*
-     * DualNamePath := 0x2e NameSeg NameSeg
-     */
-    opcode(DUAL_NAME_PREFIX).then(name_seg()).then(name_seg()).map(|(((), first), second)| {
-        Ok(alloc::vec![NameComponent::Segment(first), NameComponent::Segment(second)])
-    })
-}
-
-pub fn multi_name_path<'a, 'c>() -> impl Parser<'a, 'c, Vec<NameComponent>>
-where
-    'c: 'a,
-{
-    /*
-     * MultiNamePath := 0x2f ByteData{SegCount} NameSeg(SegCount)
-     */
-    move |input, context| {
-        let (new_input, context, ((), seg_count)) =
-            opcode(MULTI_NAME_PREFIX).then(take()).parse(input, context)?;
-        match n_of(name_seg(), usize::from(seg_count)).parse(new_input, context) {
-            Ok((new_input, context, name_segs)) => {
-                Ok((new_input, context, name_segs.iter().map(|&seg| NameComponent::Segment(seg)).collect()))
-            }
-            // Correct returned input to the one we haven't touched
-            Err((_, context, err)) => Err((input, context, err)),
-        }
-    }
-}
-
-#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
-pub struct NameSeg(pub(crate) [u8; 4]);
-
-impl NameSeg {
-    pub(crate) fn from_str(string: &str) -> Result<NameSeg, AmlError> {
-        // Each NameSeg can only have four chars, and must have at least one
-        if string.is_empty() || string.len() > 4 {
-            return Err(AmlError::InvalidNameSeg);
-        }
-
-        // We pre-fill the array with '_', so it will already be correct if the length is < 4
-        let mut seg = [b'_'; 4];
-        let bytes = string.as_bytes();
-
-        // Manually do the first one, because we have to check it's a LeadNameChar
-        if !is_lead_name_char(bytes[0]) {
-            return Err(AmlError::InvalidNameSeg);
-        }
-        seg[0] = bytes[0];
-
-        // Copy the rest of the chars, checking that they're NameChars
-        for i in 1..bytes.len() {
-            if !is_name_char(bytes[i]) {
-                return Err(AmlError::InvalidNameSeg);
-            }
-            seg[i] = bytes[i];
-        }
-
-        Ok(NameSeg(seg))
-    }
-
-    pub fn as_str(&self) -> &str {
-        /*
-         * This is safe, because we always check that all the bytes are valid ASCII, so every
-         * `NameSeg` will be valid UTF8.
-         */
-        unsafe { str::from_utf8_unchecked(&self.0) }
-    }
-}
-
-// A list of ASCII codes is pretty much never useful, so we always just show it as a string
-impl fmt::Debug for NameSeg {
-    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
-        write!(f, "{:?}", self.as_str())
-    }
-}
-
-pub fn name_seg<'a, 'c>() -> impl Parser<'a, 'c, NameSeg>
-where
-    'c: 'a,
-{
-    /*
-     * NameSeg := <LeadNameChar NameChar NameChar NameChar>
-     */
-    // TODO: can we write this better?
-    move |input, context: &'c mut AmlContext| {
-        let (input, context, char_1) = consume(is_lead_name_char).parse(input, context)?;
-        let (input, context, char_2) = consume(is_name_char).parse(input, context)?;
-        let (input, context, char_3) = consume(is_name_char).parse(input, context)?;
-        let (input, context, char_4) = consume(is_name_char).parse(input, context)?;
-        Ok((input, context, NameSeg([char_1, char_2, char_3, char_4])))
-    }
-}
-
-fn is_lead_name_char(byte: u8) -> bool {
-    byte.is_ascii_uppercase() || byte == b'_'
-}
-
-fn is_name_char(byte: u8) -> bool {
-    is_lead_name_char(byte) || byte.is_ascii_digit()
-}
-
-#[cfg(test)]
-mod tests {
-    use super::*;
-    use crate::{parser::Parser, test_utils::*, AmlError};
-    use core::str::FromStr;
-
-    #[test]
-    fn test_name_seg() {
-        let mut context = crate::test_utils::make_test_context();
-
-        check_ok!(
-            name_seg().parse(b"AF3Z", &mut context),
-            NameSeg([b'A', b'F', b'3', b'Z']),
-            &[]
-        );
-        check_ok!(
-            name_seg().parse(&[b'A', b'F', b'3', b'Z', 0xff], &mut context),
-            NameSeg([b'A', b'F', b'3', b'Z']),
-            &[0xff]
-        );
-        check_err!(
-            name_seg().parse(&[0xff, b'E', b'A', b'7'], &mut context),
-            AmlError::UnexpectedByte(0xff),
-            &[0xff, b'E', b'A', b'7']
-        );
-        check_err!(name_seg().parse(&[], &mut context), AmlError::UnexpectedEndOfStream, &[]);
-    }
-
-    #[test]
-    fn test_name_path() {
-        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![], &[]);
-        check_ok!(name_path().parse(&[0x00, 0x00], &mut context), alloc::vec![], &[0x00]);
-        check_err!(name_path().parse(&[0x2e, b'A'], &mut context), AmlError::UnexpectedEndOfStream, &[0x2e, b'A']);
-        check_ok!(
-            name_path().parse(&[0x2e, b'A', b'B', b'C', b'D', b'E', b'_', b'F', b'G'], &mut context),
-            alloc::vec![
-                NameComponent::Segment(NameSeg([b'A', b'B', b'C', b'D'])),
-                NameComponent::Segment(NameSeg([b'E', b'_', b'F', b'G']))
-            ],
-            &[]
-        );
-    }
-
-    #[test]
-    fn test_prefix_path() {
-        let mut context = crate::test_utils::make_test_context();
-
-        check_ok!(
-            name_string().parse(b"^ABCD", &mut context),
-            AmlName::from_str("^ABCD").unwrap(),
-            &[]
-        );
-        check_ok!(
-            name_string().parse(b"^^^ABCD", &mut context),
-            AmlName::from_str("^^^ABCD").unwrap(),
-            &[]
-        );
-    }
-}
diff --git a/aml/src/namespace.rs b/aml/src/namespace.rs
index 8de69e7f..2bcdcc10 100644
--- a/aml/src/namespace.rs
+++ b/aml/src/namespace.rs
@@ -1,107 +1,36 @@
-use crate::{name_object::NameSeg, value::AmlValue, AmlError};
+use crate::{AmlError, object::Object};
 use alloc::{
-    collections::BTreeMap,
+    collections::btree_map::BTreeMap,
     string::{String, ToString},
+    sync::Arc,
+    vec,
     vec::Vec,
 };
-use core::{fmt, str::FromStr};
-
-/// A handle is used to refer to an AML value without actually borrowing it until you need to
-/// access it (this makes borrowing situation much easier as you only have to consider who's
-/// borrowing the namespace). They can also be cached to avoid expensive namespace lookups.
-///
-/// Handles are never reused (the handle to a removed object will never be reused to point to a new
-/// object). This ensures handles cached by the library consumer will never point to an object they
-/// did not originally point to, but also means that, in theory, we can run out of handles on a
-/// very-long-running system (we are yet to see if this is a problem, practically).
-#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)]
-pub struct AmlHandle(u32);
-
-impl AmlHandle {
-    pub(self) fn increment(&mut self) {
-        self.0 += 1;
-    }
-}
-
-#[derive(Clone, Copy, PartialEq, Eq, Debug)]
-pub enum LevelType {
-    Scope,
-    Device,
-    /// A legacy `Processor` object's sub-objects are stored in a level of this type. Modern tables define
-    /// processors as `Device`s.
-    Processor,
-    /// A `PowerResource` object's sub-objects are stored in a level of this type.
-    PowerResource,
-    /// A `ThermalZone` object's sub-objects are stored in a level of this type.
-    ThermalZone,
-    /// 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,
-}
-
-#[derive(Clone, Debug)]
-pub struct NamespaceLevel {
-    pub typ: LevelType,
-    pub children: BTreeMap<NameSeg, NamespaceLevel>,
-    pub values: BTreeMap<NameSeg, AmlHandle>,
-}
+use core::{fmt, str, str::FromStr};
 
-impl NamespaceLevel {
-    pub(crate) fn new(typ: LevelType) -> NamespaceLevel {
-        NamespaceLevel { typ, children: BTreeMap::new(), values: BTreeMap::new() }
-    }
-}
-
-#[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
-    /// risking using the same id for two objects.
-    next_handle: AmlHandle,
-
-    /// This maps handles to actual values, and is used to access the actual AML values. When removing a value
-    /// from the object map, care must be taken to also remove references to its handle in the level data
-    /// structure, as invalid handles will cause panics.
-    object_map: BTreeMap<AmlHandle, AmlValue>,
-
-    /// Holds the first level of the namespace - containing items such as `\_SB`. Subsequent levels are held
-    /// recursively inside this structure. It holds handles to references, which need to be indexed into
-    /// `object_map` to acctually access the object.
     root: NamespaceLevel,
 }
 
-impl Default for Namespace {
-    fn default() -> Self {
-        Self::new()
-    }
-}
-
 impl Namespace {
+    /// Create a new AML namespace, with the expected pre-defined objects.
     pub fn new() -> Namespace {
-        Namespace {
-            next_handle: AmlHandle(0),
-            object_map: BTreeMap::new(),
-            root: NamespaceLevel::new(LevelType::Scope),
-        }
+        let mut namespace = Namespace { root: NamespaceLevel::new(NamespaceLevelKind::Scope) };
+
+        namespace.add_level(AmlName::from_str("\\_GPE").unwrap(), NamespaceLevelKind::Scope).unwrap();
+        namespace.add_level(AmlName::from_str("\\_SB").unwrap(), NamespaceLevelKind::Scope).unwrap();
+        namespace.add_level(AmlName::from_str("\\_SI").unwrap(), NamespaceLevelKind::Scope).unwrap();
+        namespace.add_level(AmlName::from_str("\\_PR").unwrap(), NamespaceLevelKind::Scope).unwrap();
+        namespace.add_level(AmlName::from_str("\\_TZ").unwrap(), NamespaceLevelKind::Scope).unwrap();
+
+        namespace
     }
 
-    /// Add a new level to the namespace. A "level" is named by a single `NameSeg`, and can contain values, and
-    /// also other further sub-levels. Once a level has been created, AML values can be added to it with
-    /// `add_value`.
-    ///
-    /// ### Note
-    /// At first glance, you might expect `DefDevice` to add a value of type `Device`. However, because all
-    /// `Devices` do is hold other values, we model them as namespace levels, and so they must be created
-    /// accordingly.
-    pub fn add_level(&mut self, path: AmlName, typ: LevelType) -> Result<(), AmlError> {
+    pub fn add_level(&mut self, path: AmlName, kind: NamespaceLevelKind) -> Result<(), AmlError> {
         assert!(path.is_absolute());
         let path = path.normalize()?;
 
-        /*
-         * We need to handle a special case here: if a `Scope(\) { ... }` appears in the AML, the parser will
-         * try and recreate the root scope. Instead of handling this specially in the parser, we just
-         * return nicely here.
-         */
+        // Don't try to recreate the root scope
         if path != AmlName::root() {
             let (level, last_seg) = self.get_level_for_path_mut(&path)?;
 
@@ -109,183 +38,31 @@ impl Namespace {
              * If the level has already been added, we don't need to add it again. The parser can try to add it
              * multiple times if the ASL contains multiple blocks that add to the same scope/device.
              */
-            level.children.entry(last_seg).or_insert_with(|| NamespaceLevel::new(typ));
+            level.children.entry(last_seg).or_insert_with(|| NamespaceLevel::new(kind));
         }
 
         Ok(())
     }
 
-    pub fn remove_level(&mut self, path: AmlName) -> Result<(), AmlError> {
+    pub fn insert(&mut self, path: AmlName, object: Arc<Object>) -> 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.
-    pub fn add_value(&mut self, path: AmlName, value: AmlValue) -> Result<AmlHandle, AmlError> {
-        assert!(path.is_absolute());
-        let path = path.normalize()?;
-
-        let handle = self.next_handle;
-        self.next_handle.increment();
-        self.object_map.insert(handle, value);
-
         let (level, last_seg) = self.get_level_for_path_mut(&path)?;
-        match level.values.insert(last_seg, handle) {
-            None => Ok(handle),
+        match level.values.insert(last_seg, object) {
+            None => Ok(()),
             Some(_) => Err(AmlError::NameCollision(path)),
         }
     }
 
-    /// Helper method for adding a value to the namespace at a path that is relative to the given
-    /// scope. This operation involves a lot of error handling in parts of the parser, so is
-    /// encapsulated here.
-    pub fn add_value_at_resolved_path(
-        &mut self,
-        path: AmlName,
-        scope: &AmlName,
-        value: AmlValue,
-    ) -> Result<AmlHandle, AmlError> {
-        self.add_value(path.resolve(scope)?, value)
-    }
-
-    /// Add an alias for an existing name. The alias will refer to the same value as the original,
-    /// and the fact that the alias exists is forgotten.
-    pub fn add_alias_at_resolved_path(
-        &mut self,
-        path: AmlName,
-        scope: &AmlName,
-        target: AmlName,
-    ) -> Result<AmlHandle, AmlError> {
-        let path = path.resolve(scope)?;
-        let target = target.resolve(scope)?;
-
-        let handle = self.get_handle(&target)?;
+    pub fn get(&mut self, path: AmlName) -> Result<Arc<Object>, AmlError> {
+        assert!(path.is_absolute());
+        let path = path.normalize()?;
 
         let (level, last_seg) = self.get_level_for_path_mut(&path)?;
-        match level.values.insert(last_seg, handle) {
-            None => Ok(handle),
-            Some(_) => Err(AmlError::NameCollision(path)),
-        }
-    }
-
-    pub fn get(&self, handle: AmlHandle) -> Result<&AmlValue, AmlError> {
-        Ok(self.object_map.get(&handle).unwrap())
-    }
-
-    pub fn get_mut(&mut self, handle: AmlHandle) -> Result<&mut AmlValue, AmlError> {
-        Ok(self.object_map.get_mut(&handle).unwrap())
-    }
-
-    pub fn get_handle(&self, path: &AmlName) -> Result<AmlHandle, AmlError> {
-        let (level, last_seg) = self.get_level_for_path(path)?;
-        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 handle = self.get_handle(path)?;
-        Ok(self.get_mut(handle).unwrap())
-    }
-
-    /// Search for an object at the given path of the namespace, applying the search rules described in §5.3 of the
-    /// ACPI specification, if they are applicable. Returns the resolved name, and the handle of the first valid
-    /// object, if found.
-    pub fn search(&self, path: &AmlName, starting_scope: &AmlName) -> Result<(AmlName, AmlHandle), AmlError> {
-        if path.search_rules_apply() {
-            /*
-             * If search rules apply, we need to recursively look through the namespace. If the
-             * given name does not occur in the current scope, we look at the parent scope, until
-             * we either find the name, or reach the root of the namespace.
-             */
-            let mut scope = starting_scope.clone();
-            assert!(scope.is_absolute());
-            loop {
-                // Search for the name at this namespace level. If we find it, we're done.
-                let name = path.resolve(&scope)?;
-                match self.get_level_for_path(&name) {
-                    Ok((level, last_seg)) => {
-                        if let Some(&handle) = level.values.get(&last_seg) {
-                            return Ok((name, handle));
-                        }
-                    }
-
-                    /*
-                     * This error is caught specially to avoid a case that seems bizzare but is quite useful - when
-                     * the passed starting scope doesn't exist. Certain methods return values that reference names
-                     * from the point of view of the method, so it makes sense for the starting scope to be inside
-                     * the method.  However, because we have destroyed all the objects created by the method
-                     * dynamically, the level no longer exists.
-                     *
-                     * To avoid erroring here, we simply continue to the parent scope. If the whole scope doesn't
-                     * exist, this will error when we get to the root, so this seems unlikely to introduce bugs.
-                     */
-                    Err(AmlError::LevelDoesNotExist(_)) => (),
-                    Err(err) => return Err(err),
-                }
-
-                // If we don't find it, go up a level in the namespace and search for it there recursively
-                match scope.parent() {
-                    Ok(parent) => scope = parent,
-                    // If we still haven't found the value and have run out of parents, return `None`.
-                    Err(AmlError::RootHasNoParent) => return Err(AmlError::ValueDoesNotExist(path.clone())),
-                    Err(err) => return Err(err),
-                }
-            }
-        } else {
-            // If search rules don't apply, simply resolve it against the starting scope
-            let name = path.resolve(starting_scope)?;
-            // TODO: the fuzzer crashes when path is `\` and the scope is also `\`. This means that name is `\`,
-            // which then trips up get_level_for_path. I don't know where to best solve this: we could check for
-            // specific things that crash `search`, or look for a more general solution.
-            let (level, last_seg) = self.get_level_for_path(&path.resolve(starting_scope)?)?;
-
-            if let Some(&handle) = level.values.get(&last_seg) {
-                Ok((name, handle))
-            } else {
-                Err(AmlError::ValueDoesNotExist(path.clone()))
-            }
-        }
-    }
-
-    pub fn search_for_level(&self, level_name: &AmlName, starting_scope: &AmlName) -> Result<AmlName, AmlError> {
-        if level_name.search_rules_apply() {
-            let mut scope = starting_scope.clone().normalize()?;
-            assert!(scope.is_absolute());
-
-            loop {
-                let name = level_name.resolve(&scope)?;
-                if let Ok((level, last_seg)) = self.get_level_for_path(&name) {
-                    if level.children.contains_key(&last_seg) {
-                        return Ok(name);
-                    }
-                }
-
-                // If we don't find it, move the scope up a level and search for it there recursively
-                match scope.parent() {
-                    Ok(parent) => scope = parent,
-                    Err(AmlError::RootHasNoParent) => return Err(AmlError::LevelDoesNotExist(level_name.clone())),
-                    Err(err) => return Err(err),
-                }
-            }
-        } else {
-            Ok(level_name.clone())
+        match level.values.get(&last_seg) {
+            Some(object) => Ok(object.clone()),
+            None => Err(AmlError::ObjectDoesNotExist(path.clone())),
         }
     }
 
@@ -295,7 +72,9 @@ impl Namespace {
         assert_ne!(*path, AmlName::root());
 
         let (last_seg, levels) = path.0[1..].split_last().unwrap();
-        let last_seg = last_seg.as_segment().unwrap();
+        let NameComponent::Segment(last_seg) = last_seg else {
+            panic!();
+        };
 
         // TODO: this helps with diagnostics, but requires a heap allocation just in case we need to error.
         let mut traversed_path = AmlName::root();
@@ -303,13 +82,15 @@ impl Namespace {
         let mut current_level = &self.root;
         for level in levels {
             traversed_path.0.push(*level);
-            current_level = current_level
-                .children
-                .get(&level.as_segment().unwrap())
-                .ok_or(AmlError::LevelDoesNotExist(traversed_path.clone()))?;
+
+            let NameComponent::Segment(segment) = level else {
+                panic!();
+            };
+            current_level =
+                current_level.children.get(&segment).ok_or(AmlError::LevelDoesNotExist(traversed_path.clone()))?;
         }
 
-        Ok((current_level, last_seg))
+        Ok((current_level, *last_seg))
     }
 
     /// Split an absolute path into a bunch of level segments (used to traverse the level data structure), and a
@@ -318,7 +99,9 @@ impl Namespace {
         assert_ne!(*path, AmlName::root());
 
         let (last_seg, levels) = path.0[1..].split_last().unwrap();
-        let last_seg = last_seg.as_segment().unwrap();
+        let NameComponent::Segment(last_seg) = last_seg else {
+            panic!();
+        };
 
         // TODO: this helps with diagnostics, but requires a heap allocation just in case we need to error. We can
         // improve this by changing the `levels` interation into an `enumerate()`, and then using the index to
@@ -328,45 +111,22 @@ impl Namespace {
         let mut current_level = &mut self.root;
         for level in levels {
             traversed_path.0.push(*level);
+
+            let NameComponent::Segment(segment) = level else {
+                panic!();
+            };
             current_level = current_level
                 .children
-                .get_mut(&level.as_segment().unwrap())
+                .get_mut(&segment)
                 .ok_or(AmlError::LevelDoesNotExist(traversed_path.clone()))?;
         }
 
-        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, 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(())
+        Ok((current_level, *last_seg))
     }
 }
 
+// TODO: this is fairly unreadable. We should group devices better and maybe use ASCII chars to
+// format the tree better (maybe that should be `Display` instead idk?)
 impl fmt::Debug for Namespace {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         const INDENT_PER_LEVEL: usize = 4;
@@ -380,15 +140,8 @@ impl fmt::Debug for Namespace {
         ) -> fmt::Result {
             writeln!(f, "{:indent$}{}:", "", level_name, indent = indent)?;
 
-            for (name, handle) in level.values.iter() {
-                writeln!(
-                    f,
-                    "{:indent$}{}: {:?}",
-                    "",
-                    name.as_str(),
-                    namespace.object_map.get(handle).unwrap(),
-                    indent = indent + INDENT_PER_LEVEL
-                )?;
+            for (name, object) in level.values.iter() {
+                writeln!(f, "{:indent$}{}: {:?}", "", name.as_str(), object, indent = indent + INDENT_PER_LEVEL)?;
             }
 
             for (name, sub_level) in level.children.iter() {
@@ -402,53 +155,48 @@ impl fmt::Debug for Namespace {
     }
 }
 
-impl FromStr for AmlName {
-    type Err = AmlError;
-
-    fn from_str(mut string: &str) -> Result<Self, Self::Err> {
-        if string.is_empty() {
-            return Err(AmlError::EmptyNamesAreInvalid);
-        }
-
-        let mut components = Vec::new();
-
-        // If it starts with a \, make it an absolute name
-        if string.starts_with('\\') {
-            components.push(NameComponent::Root);
-            string = &string[1..];
-        }
-
-        if !string.is_empty() {
-            // Divide the rest of it into segments, and parse those
-            for mut part in string.split('.') {
-                // Handle prefix chars
-                while part.starts_with('^') {
-                    components.push(NameComponent::Prefix);
-                    part = &part[1..];
-                }
+#[derive(Clone, Copy, PartialEq, Debug)]
+pub enum NamespaceLevelKind {
+    Scope,
+    Device,
+    Processor,
+    PowerResource,
+    ThermalZone,
+    //MethodLocals,
+}
 
-                components.push(NameComponent::Segment(NameSeg::from_str(part)?));
-            }
-        }
+pub struct NamespaceLevel {
+    pub kind: NamespaceLevelKind,
+    pub values: BTreeMap<NameSeg, Arc<Object>>,
+    pub children: BTreeMap<NameSeg, NamespaceLevel>,
+}
 
-        Ok(Self(components))
+impl NamespaceLevel {
+    pub fn new(kind: NamespaceLevelKind) -> NamespaceLevel {
+        NamespaceLevel { kind, values: BTreeMap::new(), children: BTreeMap::new() }
     }
 }
 
-#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Debug)]
+#[derive(Clone, PartialEq, Debug)]
 pub struct AmlName(Vec<NameComponent>);
 
+#[derive(Clone, Copy, PartialEq, Debug)]
+pub enum NameComponent {
+    Root,
+    Prefix,
+    Segment(NameSeg),
+}
+
 impl AmlName {
     pub fn root() -> AmlName {
-        AmlName(alloc::vec![NameComponent::Root])
+        AmlName(vec![NameComponent::Root])
     }
 
     pub fn from_name_seg(seg: NameSeg) -> AmlName {
-        AmlName(alloc::vec![NameComponent::Segment(seg)])
+        AmlName(vec![NameComponent::Segment(seg)])
     }
 
     pub fn from_components(components: Vec<NameComponent>) -> AmlName {
-        assert!(!components.is_empty());
         AmlName(components)
     }
 
@@ -548,252 +296,108 @@ impl AmlName {
     }
 }
 
-impl fmt::Display for AmlName {
-    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
-        write!(f, "{}", self.as_string())
-    }
-}
-
-#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)]
-pub enum NameComponent {
-    Root,
-    Prefix,
-    Segment(NameSeg),
-}
+impl FromStr for AmlName {
+    type Err = AmlError;
 
-impl NameComponent {
-    pub fn as_segment(self) -> Option<NameSeg> {
-        match self {
-            NameComponent::Segment(seg) => Some(seg),
-            NameComponent::Root | NameComponent::Prefix => None,
+    fn from_str(mut string: &str) -> Result<Self, Self::Err> {
+        if string.is_empty() {
+            return Err(AmlError::EmptyNamesAreInvalid);
         }
-    }
-}
 
-#[cfg(test)]
-mod tests {
-    use super::*;
-    use crate::test_utils::crudely_cmp_values;
-
-    #[test]
-    fn test_aml_name_from_str() {
-        assert_eq!(AmlName::from_str(""), Err(AmlError::EmptyNamesAreInvalid));
-        assert_eq!(AmlName::from_str("\\"), Ok(AmlName::root()));
-        assert_eq!(
-            AmlName::from_str("\\_SB.PCI0"),
-            Ok(AmlName(alloc::vec![
-                NameComponent::Root,
-                NameComponent::Segment(NameSeg([b'_', b'S', b'B', b'_'])),
-                NameComponent::Segment(NameSeg([b'P', b'C', b'I', b'0']))
-            ]))
-        );
-        assert_eq!(
-            AmlName::from_str("\\_SB.^^^PCI0"),
-            Ok(AmlName(alloc::vec![
-                NameComponent::Root,
-                NameComponent::Segment(NameSeg([b'_', b'S', b'B', b'_'])),
-                NameComponent::Prefix,
-                NameComponent::Prefix,
-                NameComponent::Prefix,
-                NameComponent::Segment(NameSeg([b'P', b'C', b'I', b'0']))
-            ]))
-        );
-    }
+        let mut components = Vec::new();
 
-    #[test]
-    fn test_is_normal() {
-        assert!(AmlName::root().is_normal());
-        assert!(AmlName::from_str("\\_SB.PCI0.VGA").unwrap().is_normal());
-        assert!(!AmlName::from_str("\\_SB.^PCI0.VGA").unwrap().is_normal());
-        assert!(!AmlName::from_str("\\^_SB.^^PCI0.VGA").unwrap().is_normal());
-        assert!(!AmlName::from_str("_SB.^^PCI0.VGA").unwrap().is_normal());
-        assert!(AmlName::from_str("_SB.PCI0.VGA").unwrap().is_normal());
-    }
+        // If it starts with a \, make it an absolute name
+        if string.starts_with('\\') {
+            components.push(NameComponent::Root);
+            string = &string[1..];
+        }
 
-    #[test]
-    fn test_normalization() {
-        assert_eq!(
-            AmlName::from_str("\\_SB.PCI0").unwrap().normalize(),
-            Ok(AmlName::from_str("\\_SB.PCI0").unwrap())
-        );
-        assert_eq!(
-            AmlName::from_str("\\_SB.^PCI0").unwrap().normalize(),
-            Ok(AmlName::from_str("\\PCI0").unwrap())
-        );
-        assert_eq!(
-            AmlName::from_str("\\_SB.PCI0.^^FOO").unwrap().normalize(),
-            Ok(AmlName::from_str("\\FOO").unwrap())
-        );
-        assert_eq!(
-            AmlName::from_str("_SB.PCI0.^FOO.BAR").unwrap().normalize(),
-            Ok(AmlName::from_str("_SB.FOO.BAR").unwrap())
-        );
-        assert_eq!(
-            AmlName::from_str("\\^_SB").unwrap().normalize(),
-            Err(AmlError::InvalidNormalizedName(AmlName::from_str("\\^_SB").unwrap()))
-        );
-        assert_eq!(
-            AmlName::from_str("\\_SB.PCI0.FOO.^^^^BAR").unwrap().normalize(),
-            Err(AmlError::InvalidNormalizedName(AmlName::from_str("\\_SB.PCI0.FOO.^^^^BAR").unwrap()))
-        );
-    }
+        if !string.is_empty() {
+            // Divide the rest of it into segments, and parse those
+            for mut part in string.split('.') {
+                // Handle prefix chars
+                while part.starts_with('^') {
+                    components.push(NameComponent::Prefix);
+                    part = &part[1..];
+                }
 
-    #[test]
-    fn test_is_absolute() {
-        assert!(AmlName::root().is_absolute());
-        assert!(AmlName::from_str("\\_SB.PCI0.VGA").unwrap().is_absolute());
-        assert!(AmlName::from_str("\\_SB.^PCI0.VGA").unwrap().is_absolute());
-        assert!(AmlName::from_str("\\^_SB.^^PCI0.VGA").unwrap().is_absolute());
-        assert!(!AmlName::from_str("_SB.^^PCI0.VGA").unwrap().is_absolute());
-        assert!(!AmlName::from_str("_SB.PCI0.VGA").unwrap().is_absolute());
-    }
+                components.push(NameComponent::Segment(NameSeg::from_str(part)?));
+            }
+        }
 
-    #[test]
-    fn test_search_rules_apply() {
-        assert!(!AmlName::root().search_rules_apply());
-        assert!(!AmlName::from_str("\\_SB").unwrap().search_rules_apply());
-        assert!(!AmlName::from_str("^VGA").unwrap().search_rules_apply());
-        assert!(!AmlName::from_str("_SB.PCI0.VGA").unwrap().search_rules_apply());
-        assert!(AmlName::from_str("VGA").unwrap().search_rules_apply());
-        assert!(AmlName::from_str("_SB").unwrap().search_rules_apply());
+        Ok(Self(components))
     }
+}
 
-    #[test]
-    fn test_aml_name_parent() {
-        assert_eq!(AmlName::from_str("\\").unwrap().parent(), Err(AmlError::RootHasNoParent));
-        assert_eq!(AmlName::from_str("\\_SB").unwrap().parent(), Ok(AmlName::root()));
-        assert_eq!(AmlName::from_str("\\_SB.PCI0").unwrap().parent(), Ok(AmlName::from_str("\\_SB").unwrap()));
-        assert_eq!(AmlName::from_str("\\_SB.PCI0").unwrap().parent().unwrap().parent(), Ok(AmlName::root()));
+impl fmt::Display for AmlName {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        write!(f, "{}", self.as_string())
     }
+}
 
-    #[test]
-    fn test_namespace() {
-        let mut namespace = Namespace::new();
-
-        /*
-         * This should succeed but do nothing.
-         */
-        assert_eq!(namespace.add_level(AmlName::from_str("\\").unwrap(), LevelType::Scope), Ok(()));
+#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
+pub struct NameSeg(pub(crate) [u8; 4]);
 
-        /*
-         * Add `\_SB`, also testing that adding a level twice succeeds.
-         */
-        assert_eq!(namespace.add_level(AmlName::from_str("\\_SB").unwrap(), LevelType::Scope), Ok(()));
-        assert_eq!(namespace.add_level(AmlName::from_str("\\_SB").unwrap(), LevelType::Scope), Ok(()));
+impl NameSeg {
+    pub fn from_str(string: &str) -> Result<NameSeg, AmlError> {
+        // Each NameSeg can only have four chars, and must have at least one
+        if string.is_empty() || string.len() > 4 {
+            return Err(AmlError::InvalidNameSeg);
+        }
 
-        /*
-         * Add a device under a level that already exists.
-         */
-        assert_eq!(namespace.add_level(AmlName::from_str("\\_SB.PCI0").unwrap(), LevelType::Device), Ok(()));
+        // We pre-fill the array with '_', so it will already be correct if the length is < 4
+        let mut seg = [b'_'; 4];
+        let bytes = string.as_bytes();
 
-        /*
-         * Add some deeper scopes.
-         */
-        assert_eq!(namespace.add_level(AmlName::from_str("\\FOO").unwrap(), LevelType::Scope), Ok(()));
-        assert_eq!(namespace.add_level(AmlName::from_str("\\FOO.BAR").unwrap(), LevelType::Scope), Ok(()));
-        assert_eq!(namespace.add_level(AmlName::from_str("\\FOO.BAR.BAZ").unwrap(), LevelType::Scope), Ok(()));
-        assert_eq!(namespace.add_level(AmlName::from_str("\\FOO.BAR.BAZ").unwrap(), LevelType::Scope), Ok(()));
-        assert_eq!(namespace.add_level(AmlName::from_str("\\FOO.BAR.BAZ.QUX").unwrap(), LevelType::Scope), Ok(()));
+        // Manually do the first one, because we have to check it's a LeadNameChar
+        if !is_lead_name_char(bytes[0]) {
+            return Err(AmlError::InvalidNameSeg);
+        }
+        seg[0] = bytes[0];
 
-        /*
-         * Add some things to the scopes to query later.
-         */
-        assert!(namespace.add_value(AmlName::from_str("\\MOO").unwrap(), AmlValue::Boolean(true)).is_ok());
-        assert!(namespace.add_value(AmlName::from_str("\\FOO.BAR.A").unwrap(), AmlValue::Integer(12345)).is_ok());
-        assert!(namespace.add_value(AmlName::from_str("\\FOO.BAR.B").unwrap(), AmlValue::Integer(6)).is_ok());
-        assert!(namespace
-            .add_value(AmlName::from_str("\\FOO.BAR.C").unwrap(), AmlValue::String(String::from("hello, world!")))
-            .is_ok());
+        // Copy the rest of the chars, checking that they're NameChars
+        for i in 1..bytes.len() {
+            if !is_name_char(bytes[i]) {
+                return Err(AmlError::InvalidNameSeg);
+            }
+            seg[i] = bytes[i];
+        }
 
-        /*
-         * Get objects using their absolute paths.
-         */
-        assert!(crudely_cmp_values(
-            namespace.get_by_path(&AmlName::from_str("\\MOO").unwrap()).unwrap(),
-            &AmlValue::Boolean(true)
-        ));
-        assert!(crudely_cmp_values(
-            namespace.get_by_path(&AmlName::from_str("\\FOO.BAR.A").unwrap()).unwrap(),
-            &AmlValue::Integer(12345)
-        ));
-        assert!(crudely_cmp_values(
-            namespace.get_by_path(&AmlName::from_str("\\FOO.BAR.B").unwrap()).unwrap(),
-            &AmlValue::Integer(6)
-        ));
-        assert!(crudely_cmp_values(
-            namespace.get_by_path(&AmlName::from_str("\\FOO.BAR.C").unwrap()).unwrap(),
-            &AmlValue::String(String::from("hello, world!"))
-        ));
+        Ok(NameSeg(seg))
+    }
 
-        /*
-         * Search for some objects that should use search rules.
-         */
-        {
-            let (name, _) = namespace
-                .search(&AmlName::from_str("MOO").unwrap(), &AmlName::from_str("\\FOO.BAR.BAZ").unwrap())
-                .unwrap();
-            assert_eq!(name, AmlName::from_str("\\MOO").unwrap());
+    pub fn from_bytes(bytes: [u8; 4]) -> Result<NameSeg, AmlError> {
+        if !is_lead_name_char(bytes[0]) {
+            return Err(AmlError::InvalidNameSeg);
         }
-        {
-            let (name, _) = namespace
-                .search(&AmlName::from_str("A").unwrap(), &AmlName::from_str("\\FOO.BAR").unwrap())
-                .unwrap();
-            assert_eq!(name, AmlName::from_str("\\FOO.BAR.A").unwrap());
+        if !is_name_char(bytes[1]) {
+            return Err(AmlError::InvalidNameSeg);
         }
-        {
-            let (name, _) = namespace
-                .search(&AmlName::from_str("A").unwrap(), &AmlName::from_str("\\FOO.BAR.BAZ.QUX").unwrap())
-                .unwrap();
-            assert_eq!(name, AmlName::from_str("\\FOO.BAR.A").unwrap());
+        if !is_name_char(bytes[2]) {
+            return Err(AmlError::InvalidNameSeg);
         }
+        if !is_name_char(bytes[3]) {
+            return Err(AmlError::InvalidNameSeg);
+        }
+        Ok(NameSeg(bytes))
     }
 
-    #[test]
-    fn test_alias() {
-        let mut namespace = Namespace::new();
-
-        assert_eq!(namespace.add_level((AmlName::from_str("\\FOO")).unwrap(), LevelType::Scope), Ok(()));
-
-        assert!(namespace
-            .add_value_at_resolved_path(
-                AmlName::from_str("BAR").unwrap(),
-                &AmlName::from_str("\\FOO").unwrap(),
-                AmlValue::Integer(100)
-            )
-            .is_ok());
-        assert!(namespace
-            .add_alias_at_resolved_path(
-                AmlName::from_str("BARA").unwrap(),
-                &AmlName::from_str("\\FOO").unwrap(),
-                AmlName::from_str("BAR").unwrap()
-            )
-            .is_ok());
-        assert!(namespace.get_by_path(&AmlName::from_str("\\FOO.BARA").unwrap()).is_ok());
-        assert_eq!(
-            namespace.get_handle(&AmlName::from_str("\\FOO.BARA").unwrap()),
-            namespace.get_handle(&AmlName::from_str("\\FOO.BAR").unwrap())
-        );
+    pub fn as_str(&self) -> &str {
+        // We should only construct valid ASCII name segments
+        unsafe { str::from_utf8_unchecked(&self.0) }
     }
+}
 
-    #[test]
-    fn test_get_level_for_path() {
-        let mut namespace = Namespace::new();
-
-        // Add some scopes
-        assert_eq!(namespace.add_level(AmlName::from_str("\\FOO").unwrap(), LevelType::Scope), Ok(()));
-        assert_eq!(namespace.add_level(AmlName::from_str("\\FOO.BAR").unwrap(), LevelType::Scope), Ok(()));
-        assert_eq!(namespace.add_level(AmlName::from_str("\\FOO.BAR.BAZ").unwrap(), LevelType::Scope), Ok(()));
-        assert_eq!(namespace.add_level(AmlName::from_str("\\FOO.BAR.BAZ").unwrap(), LevelType::Scope), Ok(()));
-        assert_eq!(namespace.add_level(AmlName::from_str("\\FOO.BAR.BAZ.QUX").unwrap(), LevelType::Scope), Ok(()));
-
-        {
-            let (_, last_seg) =
-                namespace.get_level_for_path(&AmlName::from_str("\\FOO.BAR.BAZ").unwrap()).unwrap();
-            assert_eq!(last_seg, NameSeg::from_str("BAZ").unwrap());
-        }
-        {
-            let (_, last_seg) = namespace.get_level_for_path(&AmlName::from_str("\\FOO").unwrap()).unwrap();
-            assert_eq!(last_seg, NameSeg::from_str("FOO").unwrap());
-        }
+pub fn is_lead_name_char(c: u8) -> bool {
+    c.is_ascii_uppercase() || c == b'_'
+}
+
+pub fn is_name_char(c: u8) -> bool {
+    is_lead_name_char(c) || c.is_ascii_digit()
+}
+
+impl fmt::Debug for NameSeg {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        write!(f, "{:?}", self.as_str())
     }
 }
diff --git a/aml/src/object.rs b/aml/src/object.rs
new file mode 100644
index 00000000..c40cfc87
--- /dev/null
+++ b/aml/src/object.rs
@@ -0,0 +1,43 @@
+use alloc::{sync::Arc, vec::Vec};
+
+#[derive(Debug)]
+pub enum Object {
+    Uninitialized,
+    Buffer(Vec<u8>),
+    BufferField,
+    Device,
+    Event,
+    FieldUnit,
+    Integer(u64),
+    Method,
+    Mutex,
+    Reference(Arc<Object>),
+    OpRegion,
+    Package,
+    PowerResource,
+    Processor,
+    RawDataBuffer,
+    String,
+    ThermalZone,
+}
+
+impl Object {
+    /*
+     * TODO XXX: this is a horrendous hack to emulate a clever locking solution for dynamically
+     * validating borrow checking for objects at A Later Date. It is trivially easy to produce
+     * undefined behaviour with this (and might be UB intrinsically).
+     *
+     * Options are:
+     *   - Put something like an AtomicRefCell around every single object. This is too slow I
+     *     think.
+     *   - Utilise a global lock on the namespace that gives us some sort of token we can then
+     *     magic up mutable references to objects through. Safety is ensured at type-level.
+     *   - Something else cleverer.
+     */
+    pub fn gain_mut(&self) -> &mut Self {
+        #[allow(invalid_reference_casting)]
+        unsafe {
+            &mut *(self as *const Self as *mut Self)
+        }
+    }
+}
diff --git a/aml/src/opcode.rs b/aml/src/opcode.rs
deleted file mode 100644
index f4312c06..00000000
--- a/aml/src/opcode.rs
+++ /dev/null
@@ -1,163 +0,0 @@
-use crate::{parser::*, AmlContext, AmlError};
-
-pub const NULL_NAME: u8 = 0x00;
-pub const DUAL_NAME_PREFIX: u8 = 0x2E;
-pub const MULTI_NAME_PREFIX: u8 = 0x2F;
-pub const ROOT_CHAR: u8 = b'\\';
-pub const PREFIX_CHAR: u8 = b'^';
-
-pub const RESERVED_FIELD: u8 = 0x00;
-// pub const ACCESS_FIELD: u8 = 0x01;
-// pub const CONNECT_FIELD: u8 = 0x02;
-// pub const EXTENDED_ACCESS_FIELD: u8 = 0x03;
-
-pub const ZERO_OP: u8 = 0x00;
-pub const ONE_OP: u8 = 0x01;
-pub const ONES_OP: u8 = 0xff;
-pub const BYTE_CONST: u8 = 0x0a;
-pub const WORD_CONST: u8 = 0x0b;
-pub const DWORD_CONST: u8 = 0x0c;
-pub const STRING_PREFIX: u8 = 0x0d;
-pub const QWORD_CONST: u8 = 0x0e;
-
-pub const DEF_ALIAS_OP: u8 = 0x06;
-pub const DEF_NAME_OP: u8 = 0x08;
-pub const DEF_SCOPE_OP: u8 = 0x10;
-pub const DEF_BUFFER_OP: u8 = 0x11;
-pub const DEF_PACKAGE_OP: u8 = 0x12;
-pub const DEF_METHOD_OP: u8 = 0x14;
-pub const DEF_EXTERNAL_OP: u8 = 0x15;
-pub const DEF_CREATE_DWORD_FIELD_OP: u8 = 0x8a;
-pub const DEF_CREATE_WORD_FIELD_OP: u8 = 0x8b;
-pub const DEF_CREATE_BYTE_FIELD_OP: u8 = 0x8c;
-pub const DEF_CREATE_BIT_FIELD_OP: u8 = 0x8d;
-pub const DEF_CREATE_QWORD_FIELD_OP: u8 = 0x8f;
-pub const EXT_DEF_MUTEX_OP: u8 = 0x01;
-pub const EXT_DEF_COND_REF_OF_OP: u8 = 0x12;
-pub const EXT_DEF_CREATE_FIELD_OP: u8 = 0x13;
-pub const EXT_REVISION_OP: u8 = 0x30;
-pub const EXT_DEF_FATAL_OP: u8 = 0x32;
-pub const EXT_DEF_OP_REGION_OP: u8 = 0x80;
-pub const EXT_DEF_FIELD_OP: u8 = 0x81;
-pub const EXT_DEF_DEVICE_OP: u8 = 0x82;
-pub const EXT_DEF_PROCESSOR_OP: u8 = 0x83;
-pub const EXT_DEF_POWER_RES_OP: u8 = 0x84;
-pub const EXT_DEF_THERMAL_ZONE_OP: u8 = 0x85;
-
-/*
- * Statement opcodes
- */
-pub const DEF_CONTINUE_OP: u8 = 0x9f;
-pub const DEF_IF_ELSE_OP: u8 = 0xa0;
-pub const DEF_ELSE_OP: u8 = 0xa1;
-pub const DEF_WHILE_OP: u8 = 0xa2;
-pub const DEF_NOOP_OP: u8 = 0xa3;
-pub const DEF_RETURN_OP: u8 = 0xa4;
-pub const DEF_BREAK_OP: u8 = 0xa5;
-pub const DEF_BREAKPOINT_OP: u8 = 0xcc;
-pub const EXT_DEF_STALL_OP: u8 = 0x21;
-pub const EXT_DEF_SLEEP_OP: u8 = 0x22;
-
-/*
- * Expression opcodes
- */
-pub const DEF_STORE_OP: u8 = 0x70;
-pub const DEF_ADD_OP: u8 = 0x72;
-pub const DEF_CONCAT_OP: u8 = 0x73;
-pub const DEF_SUBTRACT_OP: u8 = 0x74;
-pub const DEF_INCREMENT_OP: u8 = 0x75;
-pub const DEF_DECREMENT_OP: u8 = 0x76;
-pub const DEF_SHIFT_LEFT: u8 = 0x79;
-pub const DEF_SHIFT_RIGHT: u8 = 0x7a;
-pub const DEF_AND_OP: u8 = 0x7b;
-pub const DEF_OR_OP: u8 = 0x7d;
-pub const DEF_CONCAT_RES_OP: u8 = 0x84;
-pub const DEF_SIZE_OF_OP: u8 = 0x87;
-pub const DEF_OBJECT_TYPE_OP: u8 = 0x8e;
-pub const DEF_L_AND_OP: u8 = 0x90;
-pub const DEF_L_OR_OP: u8 = 0x91;
-pub const DEF_L_NOT_OP: u8 = 0x92;
-pub const DEF_L_EQUAL_OP: u8 = 0x93;
-pub const DEF_L_GREATER_OP: u8 = 0x94;
-pub const DEF_L_LESS_OP: u8 = 0x95;
-pub const DEF_TO_INTEGER_OP: u8 = 0x99;
-pub const DEF_MID_OP: u8 = 0x9e;
-
-/*
- * Miscellaneous objects
- */
-pub const EXT_DEBUG_OP: u8 = 0x31;
-pub const LOCAL0_OP: u8 = 0x60;
-pub const LOCAL1_OP: u8 = 0x61;
-pub const LOCAL2_OP: u8 = 0x62;
-pub const LOCAL3_OP: u8 = 0x63;
-pub const LOCAL4_OP: u8 = 0x64;
-pub const LOCAL5_OP: u8 = 0x65;
-pub const LOCAL6_OP: u8 = 0x66;
-pub const LOCAL7_OP: u8 = 0x67;
-pub const ARG0_OP: u8 = 0x68;
-pub const ARG1_OP: u8 = 0x69;
-pub const ARG2_OP: u8 = 0x6a;
-pub const ARG3_OP: u8 = 0x6b;
-pub const ARG4_OP: u8 = 0x6c;
-pub const ARG5_OP: u8 = 0x6d;
-pub const ARG6_OP: u8 = 0x6e;
-
-pub const EXT_OPCODE_PREFIX: u8 = 0x5b;
-
-pub(crate) fn opcode<'a, 'c>(opcode: u8) -> impl Parser<'a, 'c, ()>
-where
-    'c: 'a,
-{
-    move |input: &'a [u8], context: &'c mut AmlContext| match input.first() {
-        None => Err((input, context, Propagate::Err(AmlError::UnexpectedEndOfStream))),
-        Some(&byte) if byte == opcode => Ok((&input[1..], context, ())),
-        Some(_) => Err((input, context, Propagate::Err(AmlError::WrongParser))),
-    }
-}
-
-pub(crate) fn ext_opcode<'a, 'c>(ext_opcode: u8) -> impl Parser<'a, 'c, ()>
-where
-    'c: 'a,
-{
-    opcode(EXT_OPCODE_PREFIX).then(opcode(ext_opcode)).discard_result()
-}
-
-#[cfg(test)]
-mod tests {
-    use super::*;
-    use crate::{test_utils::*, AmlError};
-
-    #[test]
-    fn empty() {
-        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 = 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),
-            (),
-            &[0x31, 0x55, 0xf3]
-        );
-    }
-
-    #[test]
-    fn extended_opcodes() {
-        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,
-            &[EXT_DEF_FIELD_OP, EXT_DEF_FIELD_OP]
-        );
-        check_ok!(
-            ext_opcode(EXT_DEF_FIELD_OP).parse(&[EXT_OPCODE_PREFIX, EXT_DEF_FIELD_OP], &mut context),
-            (),
-            &[]
-        );
-    }
-}
diff --git a/aml/src/opregion.rs b/aml/src/opregion.rs
deleted file mode 100644
index 12898f92..00000000
--- a/aml/src/opregion.rs
+++ /dev/null
@@ -1,301 +0,0 @@
-use crate::{
-    value::{Args, FieldAccessType, FieldFlags, FieldUpdateRule},
-    AmlContext,
-    AmlError,
-    AmlName,
-    AmlValue,
-};
-use bit_field::BitField;
-use core::str::FromStr;
-
-#[derive(Clone, Debug)]
-pub struct OpRegion {
-    region: RegionSpace,
-    base: u64,
-    length: u64,
-    parent_device: Option<AmlName>,
-}
-
-impl OpRegion {
-    pub fn new(region: RegionSpace, base: u64, length: u64, parent_device: Option<AmlName>) -> OpRegion {
-        OpRegion { region, base, length, parent_device }
-    }
-
-    /// Get the length of this op-region, in **bits**.
-    pub fn length(&self) -> u64 {
-        self.length
-    }
-
-    /// Read a field from this op-region. This has looser requirements than `read`, and will
-    /// perform multiple standard-sized reads and mask the result as required.
-    pub fn read_field(
-        &self,
-        offset: u64,
-        length: u64,
-        flags: FieldFlags,
-        context: &mut AmlContext,
-    ) -> Result<AmlValue, AmlError> {
-        let _max_access_size = match self.region {
-            RegionSpace::SystemMemory => 64,
-            RegionSpace::SystemIo | RegionSpace::PciConfig => 32,
-            _ => unimplemented!(),
-        };
-        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
-         */
-        let value = self.read(offset, access_size, context)?.get_bits(0..(length as usize));
-        Ok(AmlValue::Integer(value))
-    }
-
-    pub fn write_field(
-        &self,
-        offset: u64,
-        length: u64,
-        flags: FieldFlags,
-        value: AmlValue,
-        context: &mut AmlContext,
-    ) -> Result<(), AmlError> {
-        /*
-         * If the field's update rule is `Preserve`, we need to read the initial value of the field, so we can
-         * overwrite the correct bits. We destructure the field to do the actual write, so we read from it if
-         * needed here, otherwise the borrow-checker doesn't understand.
-         */
-        let mut field_value = match flags.field_update_rule()? {
-            FieldUpdateRule::Preserve => self.read_field(offset, length, flags, context)?.as_integer(context)?,
-            FieldUpdateRule::WriteAsOnes => 0xffffffff_ffffffff,
-            FieldUpdateRule::WriteAsZeros => 0x0,
-        };
-
-        let _maximum_access_size = match self.region {
-            RegionSpace::SystemMemory => 64,
-            RegionSpace::SystemIo | RegionSpace::PciConfig => 32,
-            _ => unimplemented!(),
-        };
-        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());
-
-        field_value.set_bits(0..(length as usize), value.as_integer(context)?);
-        self.write(offset, access_size, field_value, context)
-    }
-
-    /// Perform a standard-size read from this op-region. `length` must be a supported power-of-2,
-    /// and `offset` correctly aligned for that `length`. `value` must be appropriately sized.
-    pub fn read(&self, offset: u64, length: u64, context: &mut AmlContext) -> Result<u64, AmlError> {
-        match self.region {
-            RegionSpace::SystemMemory => {
-                let address = (self.base + offset).try_into().map_err(|_| AmlError::FieldInvalidAddress)?;
-                match length {
-                    8 => Ok(context.handler.read_u8(address) as u64),
-                    16 => Ok(context.handler.read_u16(address) as u64),
-                    32 => Ok(context.handler.read_u32(address) as u64),
-                    64 => Ok(context.handler.read_u64(address)),
-                    _ => Err(AmlError::FieldInvalidAccessSize),
-                }
-            }
-
-            RegionSpace::SystemIo => {
-                let port = (self.base + offset).try_into().map_err(|_| AmlError::FieldInvalidAddress)?;
-                match length {
-                    8 => Ok(context.handler.read_io_u8(port) as u64),
-                    16 => Ok(context.handler.read_io_u16(port) as u64),
-                    32 => Ok(context.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 = self.parent_device.as_ref().unwrap();
-                let seg = match context.invoke_method(
-                    &AmlName::from_str("_SEG").unwrap().resolve(parent_device).unwrap(),
-                    Args::EMPTY,
-                ) {
-                    Ok(seg) => seg.as_integer(context)?.try_into().map_err(|_| AmlError::FieldInvalidAddress)?,
-                    Err(AmlError::ValueDoesNotExist(_)) => 0,
-                    Err(err) => return Err(err),
-                };
-                let bbn = match context.invoke_method(
-                    &AmlName::from_str("_BBN").unwrap().resolve(parent_device).unwrap(),
-                    Args::EMPTY,
-                ) {
-                    Ok(bbn) => bbn.as_integer(context)?.try_into().map_err(|_| AmlError::FieldInvalidAddress)?,
-                    Err(AmlError::ValueDoesNotExist(_)) => 0,
-                    Err(err) => return Err(err),
-                };
-                let adr = {
-                    let adr = context.invoke_method(
-                        &AmlName::from_str("_ADR").unwrap().resolve(parent_device).unwrap(),
-                        Args::EMPTY,
-                    )?;
-                    adr.as_integer(context)?
-                };
-
-                let device = adr.get_bits(16..24) as u8;
-                let function = adr.get_bits(0..8) as u8;
-                let offset = (self.base + offset).try_into().map_err(|_| AmlError::FieldInvalidAddress)?;
-
-                match length {
-                    8 => Ok(context.handler.read_pci_u8(seg, bbn, device, function, offset) as u64),
-                    16 => Ok(context.handler.read_pci_u16(seg, bbn, device, function, offset) as u64),
-                    32 => Ok(context.handler.read_pci_u32(seg, bbn, device, function, offset) as u64),
-                    _ => Err(AmlError::FieldInvalidAccessSize),
-                }
-            }
-
-            // TODO
-            _ => unimplemented!(),
-        }
-    }
-
-    /// Perform a standard-size write to this op-region. `length` must be a supported power-of-2,
-    /// and `offset` correctly aligned for that `length`. `value` must be appropriately sized.
-    pub fn write(&self, offset: u64, length: u64, value: u64, context: &mut AmlContext) -> Result<(), AmlError> {
-        match self.region {
-            RegionSpace::SystemMemory => {
-                let address = (self.base + offset).try_into().map_err(|_| AmlError::FieldInvalidAddress)?;
-                match length {
-                    8 => {
-                        context.handler.write_u8(address, value as u8);
-                        Ok(())
-                    }
-                    16 => {
-                        context.handler.write_u16(address, value as u16);
-                        Ok(())
-                    }
-                    32 => {
-                        context.handler.write_u32(address, value as u32);
-                        Ok(())
-                    }
-                    64 => {
-                        context.handler.write_u64(address, value);
-                        Ok(())
-                    }
-                    _ => Err(AmlError::FieldInvalidAccessSize),
-                }
-            }
-
-            RegionSpace::SystemIo => {
-                let port = (self.base + offset).try_into().map_err(|_| AmlError::FieldInvalidAddress)?;
-                match length {
-                    8 => {
-                        context.handler.write_io_u8(port, value as u8);
-                        Ok(())
-                    }
-                    16 => {
-                        context.handler.write_io_u16(port, value as u16);
-                        Ok(())
-                    }
-                    32 => {
-                        context.handler.write_io_u32(port, value as u32);
-                        Ok(())
-                    }
-                    _ => 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 = self.parent_device.as_ref().unwrap();
-                let seg = match context.invoke_method(
-                    &AmlName::from_str("_SEG").unwrap().resolve(parent_device).unwrap(),
-                    Args::EMPTY,
-                ) {
-                    Ok(seg) => seg.as_integer(context)?.try_into().map_err(|_| AmlError::FieldInvalidAddress)?,
-                    Err(AmlError::ValueDoesNotExist(_)) => 0,
-                    Err(err) => return Err(err),
-                };
-                let bbn = match context.invoke_method(
-                    &AmlName::from_str("_BBN").unwrap().resolve(parent_device).unwrap(),
-                    Args::EMPTY,
-                ) {
-                    Ok(bbn) => bbn.as_integer(context)?.try_into().map_err(|_| AmlError::FieldInvalidAddress)?,
-                    Err(AmlError::ValueDoesNotExist(_)) => 0,
-                    Err(err) => return Err(err),
-                };
-                let adr = {
-                    let adr = context.invoke_method(
-                        &AmlName::from_str("_ADR").unwrap().resolve(parent_device).unwrap(),
-                        Args::EMPTY,
-                    )?;
-                    adr.as_integer(context)?
-                };
-
-                let device = adr.get_bits(16..24) as u8;
-                let function = adr.get_bits(0..8) as u8;
-                let offset = (self.base + offset).try_into().map_err(|_| AmlError::FieldInvalidAddress)?;
-
-                match length {
-                    8 => {
-                        context.handler.write_pci_u8(seg, bbn, device, function, offset, value as u8);
-                        Ok(())
-                    }
-                    16 => {
-                        context.handler.write_pci_u16(seg, bbn, device, function, offset, value as u16);
-                        Ok(())
-                    }
-                    32 => {
-                        context.handler.write_pci_u32(seg, bbn, device, function, offset, value as u32);
-                        Ok(())
-                    }
-                    _ => Err(AmlError::FieldInvalidAccessSize),
-                }
-            }
-
-            // TODO
-            _ => unimplemented!(),
-        }
-    }
-}
-
-#[derive(Clone, Copy, PartialEq, Eq, Debug)]
-pub enum RegionSpace {
-    SystemMemory,
-    SystemIo,
-    PciConfig,
-    EmbeddedControl,
-    SMBus,
-    SystemCmos,
-    PciBarTarget,
-    IPMI,
-    GeneralPurposeIo,
-    GenericSerialBus,
-    OemDefined(u8),
-}
diff --git a/aml/src/parser.rs b/aml/src/parser.rs
deleted file mode 100644
index 1726a223..00000000
--- a/aml/src/parser.rs
+++ /dev/null
@@ -1,535 +0,0 @@
-use crate::{pkg_length::PkgLength, AmlContext, AmlError, AmlValue, DebugVerbosity};
-use alloc::vec::Vec;
-use core::marker::PhantomData;
-use log::trace;
-
-/// This is the number of spaces added to indent a scope when printing parser debug messages.
-pub const INDENT_PER_SCOPE: usize = 2;
-
-impl AmlContext {
-    /// This is used by the parser to provide debug comments about the current object, which are indented to the
-    /// correct level for the current object. We most often need to print these comments from `map_with_context`s,
-    /// so it's most convenient to have this method on `AmlContext`.
-    pub(crate) fn comment(&self, verbosity: DebugVerbosity, message: &str) {
-        if verbosity <= self.debug_verbosity {
-            log::trace!("{:indent$}{}", "", message, indent = self.scope_indent);
-        }
-    }
-}
-
-#[derive(Debug)]
-pub enum Propagate {
-    Err(AmlError),
-    Return(AmlValue),
-    Break,
-    Continue,
-}
-
-impl From<AmlError> for Propagate {
-    fn from(error: AmlError) -> Self {
-        Self::Err(error)
-    }
-}
-
-pub type ParseResult<'a, 'c, R> =
-    Result<(&'a [u8], &'c mut AmlContext, R), (&'a [u8], &'c mut AmlContext, Propagate)>;
-
-pub trait Parser<'a, 'c, R>: Sized
-where
-    'c: 'a,
-{
-    fn parse(&self, input: &'a [u8], context: &'c mut AmlContext) -> ParseResult<'a, 'c, R>;
-
-    fn map<F, A>(self, map_fn: F) -> Map<'a, 'c, Self, F, R, A>
-    where
-        F: Fn(R) -> Result<A, Propagate>,
-    {
-        Map { parser: self, map_fn, _phantom: PhantomData }
-    }
-
-    fn map_with_context<F, A>(self, map_fn: F) -> MapWithContext<'a, 'c, Self, F, R, A>
-    where
-        F: Fn(R, &'c mut AmlContext) -> (Result<A, Propagate>, &'c mut AmlContext),
-    {
-        MapWithContext { parser: self, map_fn, _phantom: PhantomData }
-    }
-
-    fn discard_result(self) -> DiscardResult<'a, 'c, Self, R> {
-        DiscardResult { parser: self, _phantom: PhantomData }
-    }
-
-    /// Try parsing with `self`. If it succeeds, return its result. If it returns `AmlError::WrongParser`, try
-    /// parsing with `other`, returning the result of that parser in all cases. Other errors from the first
-    /// parser are propagated without attempting the second parser. To chain more than two parsers using
-    /// `or`, see the `choice!` macro.
-    #[allow(unused)]
-    fn or<OtherParser>(self, other: OtherParser) -> Or<'a, 'c, Self, OtherParser, R>
-    where
-        OtherParser: Parser<'a, 'c, R>,
-    {
-        Or { p1: self, p2: other, _phantom: PhantomData }
-    }
-
-    fn then<NextParser, NextR>(self, next: NextParser) -> Then<'a, 'c, Self, NextParser, R, NextR>
-    where
-        NextParser: Parser<'a, 'c, NextR>,
-    {
-        Then { p1: self, p2: next, _phantom: PhantomData }
-    }
-
-    /// `feed` takes a function that takes the result of this parser (`self`) and creates another
-    /// parser, which is then used to parse the next part of the stream. This sounds convoluted,
-    /// but is useful for when the next parser's behaviour depends on a property of the result of
-    /// the first (e.g. the first parser might parse a length `n`, and the second parser then
-    /// consumes `n` bytes).
-    fn feed<F, P2, R2>(self, producer_fn: F) -> Feed<'a, 'c, Self, P2, F, R, R2>
-    where
-        P2: Parser<'a, 'c, R2>,
-        F: Fn(R) -> P2,
-    {
-        Feed { parser: self, producer_fn, _phantom: PhantomData }
-    }
-}
-
-impl<'a, 'c, F, R> Parser<'a, 'c, R> for F
-where
-    'c: 'a,
-    F: Fn(&'a [u8], &'c mut AmlContext) -> ParseResult<'a, 'c, R>,
-{
-    fn parse(&self, input: &'a [u8], context: &'c mut AmlContext) -> ParseResult<'a, 'c, R> {
-        self(input, context)
-    }
-}
-
-/// The identity parser - returns the stream and context unchanged. Useful for producing parsers
-/// that produce a result without parsing anything by doing: `id().map(|()| Ok(foo))`.
-pub fn id<'a, 'c>() -> impl Parser<'a, 'c, ()>
-where
-    'c: 'a,
-{
-    move |input: &'a [u8], context: &'c mut AmlContext| Ok((input, context, ()))
-}
-
-pub fn take<'a, 'c>() -> impl Parser<'a, 'c, u8>
-where
-    'c: 'a,
-{
-    move |input: &'a [u8], context: &'c mut AmlContext| match input.first() {
-        Some(&byte) => Ok((&input[1..], context, byte)),
-        None => Err((input, context, Propagate::Err(AmlError::UnexpectedEndOfStream))),
-    }
-}
-
-pub fn take_u16<'a, 'c>() -> impl Parser<'a, 'c, u16>
-where
-    'c: 'a,
-{
-    move |input: &'a [u8], context: &'c mut AmlContext| {
-        if input.len() < 2 {
-            return Err((input, context, Propagate::Err(AmlError::UnexpectedEndOfStream)));
-        }
-
-        Ok((&input[2..], context, u16::from_le_bytes(input[0..2].try_into().unwrap())))
-    }
-}
-
-pub fn take_u32<'a, 'c>() -> impl Parser<'a, 'c, u32>
-where
-    'c: 'a,
-{
-    move |input: &'a [u8], context: &'c mut AmlContext| {
-        if input.len() < 4 {
-            return Err((input, context, Propagate::Err(AmlError::UnexpectedEndOfStream)));
-        }
-
-        Ok((&input[4..], context, u32::from_le_bytes(input[0..4].try_into().unwrap())))
-    }
-}
-
-pub fn take_u64<'a, 'c>() -> impl Parser<'a, 'c, u64>
-where
-    'c: 'a,
-{
-    move |input: &'a [u8], context: &'c mut AmlContext| {
-        if input.len() < 8 {
-            return Err((input, context, Propagate::Err(AmlError::UnexpectedEndOfStream)));
-        }
-
-        Ok((&input[8..], context, u64::from_le_bytes(input[0..8].try_into().unwrap())))
-    }
-}
-
-pub fn take_n<'a, 'c>(n: u32) -> impl Parser<'a, 'c, &'a [u8]>
-where
-    'c: 'a,
-{
-    move |input: &'a [u8], context| {
-        if (input.len() as u32) < n {
-            return Err((input, context, Propagate::Err(AmlError::UnexpectedEndOfStream)));
-        }
-
-        let (result, new_input) = input.split_at(n as usize);
-        Ok((new_input, context, result))
-    }
-}
-
-pub fn take_to_end_of_pkglength<'a, 'c>(length: PkgLength) -> impl Parser<'a, 'c, &'a [u8]>
-where
-    'c: 'a,
-{
-    move |input: &'a [u8], context| {
-        /*
-         * TODO: fuzzing manages to find PkgLengths that correctly parse during construction, but later crash here.
-         * I would've thought we would pick up all invalid lengths there, so have a look at why this is needed.
-         */
-        let bytes_to_take = match (input.len() as u32).checked_sub(length.end_offset) {
-            Some(bytes_to_take) => bytes_to_take,
-            None => return Err((input, context, Propagate::Err(AmlError::InvalidPkgLength))),
-        };
-        take_n(bytes_to_take).parse(input, context)
-    }
-}
-
-pub fn n_of<'a, 'c, P, R>(parser: P, n: usize) -> impl Parser<'a, 'c, Vec<R>>
-where
-    'c: 'a,
-    P: Parser<'a, 'c, R>,
-{
-    // TODO: can we write this more nicely?
-    move |mut input, mut context| {
-        let mut results = Vec::with_capacity(n);
-
-        for _ in 0..n {
-            let (new_input, new_context, result) = match parser.parse(input, context) {
-                Ok((input, context, result)) => (input, context, result),
-                Err((_, context, propagate)) => return Err((input, context, propagate)),
-            };
-            results.push(result);
-            input = new_input;
-            context = new_context;
-        }
-
-        Ok((input, context, results))
-    }
-}
-
-pub fn take_while<'a, 'c, P, R>(parser: P) -> impl Parser<'a, 'c, usize>
-where
-    'c: 'a,
-    P: Parser<'a, 'c, R>,
-{
-    move |mut input: &'a [u8], mut context: &'c mut AmlContext| {
-        let mut num_passed = 0;
-        loop {
-            match parser.parse(input, context) {
-                Ok((new_input, new_context, _)) => {
-                    input = new_input;
-                    context = new_context;
-                    num_passed += 1;
-                }
-                Err((_, context, Propagate::Err(AmlError::WrongParser))) => {
-                    return Ok((input, context, num_passed))
-                }
-                Err((_, context, err)) => return Err((input, context, err)),
-            }
-        }
-    }
-}
-
-pub fn consume<'a, 'c, F>(condition: F) -> impl Parser<'a, 'c, u8>
-where
-    'c: 'a,
-    F: Fn(u8) -> bool,
-{
-    move |input: &'a [u8], context: &'c mut AmlContext| match input.first() {
-        Some(&byte) if condition(byte) => Ok((&input[1..], context, byte)),
-        Some(&byte) => Err((input, context, Propagate::Err(AmlError::UnexpectedByte(byte)))),
-        None => Err((input, context, Propagate::Err(AmlError::UnexpectedEndOfStream))),
-    }
-}
-
-pub fn comment_scope<'a, 'c, P, R>(
-    verbosity: DebugVerbosity,
-    scope_name: &'a str,
-    parser: P,
-) -> impl Parser<'a, 'c, R>
-where
-    'c: 'a,
-    R: core::fmt::Debug,
-    P: Parser<'a, 'c, R>,
-{
-    move |input, context: &'c mut AmlContext| {
-        if verbosity <= context.debug_verbosity {
-            trace!("{:indent$}--> {}", "", scope_name, indent = context.scope_indent);
-            context.scope_indent += INDENT_PER_SCOPE;
-        }
-
-        // Return if the parse fails, so we don't print the tail. Makes it easier to debug.
-        let (new_input, context, result) = parser.parse(input, context)?;
-
-        if verbosity <= context.debug_verbosity {
-            context.scope_indent -= INDENT_PER_SCOPE;
-            trace!("{:indent$}<-- {}", "", scope_name, indent = context.scope_indent);
-        }
-
-        Ok((new_input, context, result))
-    }
-}
-
-/// `extract` observes another parser consuming part of the stream, and returns the result of the parser, and the
-/// section of the stream that was parsed by the parser. This is useful for re-parsing that section of the stream,
-/// which allows the result of a piece of AML to be reevaluated with a new context, for example.
-///
-/// Note that reparsing the stream is not idempotent - the context is changed by this parse.
-pub fn extract<'a, 'c, P, R>(parser: P) -> impl Parser<'a, 'c, (R, &'a [u8])>
-where
-    'c: 'a,
-    P: Parser<'a, 'c, R>,
-{
-    move |input, context: &'c mut AmlContext| {
-        let before = input;
-        let (after, context, result) = parser.parse(input, context)?;
-        let bytes_parsed = before.len() - after.len();
-        let parsed = &before[..bytes_parsed];
-
-        Ok((after, context, (result, parsed)))
-    }
-}
-
-pub struct Or<'a, 'c, P1, P2, R>
-where
-    'c: 'a,
-    P1: Parser<'a, 'c, R>,
-    P2: Parser<'a, 'c, R>,
-{
-    p1: P1,
-    p2: P2,
-    _phantom: PhantomData<(&'a R, &'c ())>,
-}
-
-impl<'a, 'c, P1, P2, R> Parser<'a, 'c, R> for Or<'a, 'c, P1, P2, R>
-where
-    'c: 'a,
-    P1: Parser<'a, 'c, R>,
-    P2: Parser<'a, 'c, R>,
-{
-    fn parse(&self, input: &'a [u8], context: &'c mut AmlContext) -> ParseResult<'a, 'c, R> {
-        match self.p1.parse(input, context) {
-            Ok(parse_result) => Ok(parse_result),
-            Err((_, context, Propagate::Err(AmlError::WrongParser))) => self.p2.parse(input, context),
-            Err((_, context, err)) => Err((input, context, err)),
-        }
-    }
-}
-
-pub struct Map<'a, 'c, P, F, R, A>
-where
-    'c: 'a,
-    P: Parser<'a, 'c, R>,
-    F: Fn(R) -> Result<A, Propagate>,
-{
-    parser: P,
-    map_fn: F,
-    _phantom: PhantomData<(&'a (R, A), &'c ())>,
-}
-
-impl<'a, 'c, P, F, R, A> Parser<'a, 'c, A> for Map<'a, 'c, P, F, R, A>
-where
-    'c: 'a,
-    P: Parser<'a, 'c, R>,
-    F: Fn(R) -> Result<A, Propagate>,
-{
-    fn parse(&self, input: &'a [u8], context: &'c mut AmlContext) -> ParseResult<'a, 'c, A> {
-        match self.parser.parse(input, context) {
-            Ok((new_input, context, result)) => match (self.map_fn)(result) {
-                Ok(result_value) => Ok((new_input, context, result_value)),
-                Err(err) => Err((input, context, err)),
-            },
-            Err(result) => Err(result),
-        }
-    }
-}
-
-pub struct MapWithContext<'a, 'c, P, F, R, A>
-where
-    'c: 'a,
-    P: Parser<'a, 'c, R>,
-    F: Fn(R, &'c mut AmlContext) -> (Result<A, Propagate>, &'c mut AmlContext),
-{
-    parser: P,
-    map_fn: F,
-    _phantom: PhantomData<(&'a (R, A), &'c ())>,
-}
-
-impl<'a, 'c, P, F, R, A> Parser<'a, 'c, A> for MapWithContext<'a, 'c, P, F, R, A>
-where
-    'c: 'a,
-    P: Parser<'a, 'c, R>,
-    F: Fn(R, &'c mut AmlContext) -> (Result<A, Propagate>, &'c mut AmlContext),
-{
-    fn parse(&self, input: &'a [u8], context: &'c mut AmlContext) -> ParseResult<'a, 'c, A> {
-        match self.parser.parse(input, context) {
-            Ok((new_input, context, result)) => match (self.map_fn)(result, context) {
-                (Ok(result_value), context) => Ok((new_input, context, result_value)),
-                (Err(err), context) => Err((input, context, err)),
-            },
-            Err(result) => Err(result),
-        }
-    }
-}
-
-pub struct DiscardResult<'a, 'c, P, R>
-where
-    'c: 'a,
-    P: Parser<'a, 'c, R>,
-{
-    parser: P,
-    _phantom: PhantomData<(&'a R, &'c ())>,
-}
-
-impl<'a, 'c, P, R> Parser<'a, 'c, ()> for DiscardResult<'a, 'c, P, R>
-where
-    'c: 'a,
-    P: Parser<'a, 'c, R>,
-{
-    fn parse(&self, input: &'a [u8], context: &'c mut AmlContext) -> ParseResult<'a, 'c, ()> {
-        self.parser.parse(input, context).map(|(new_input, new_context, _)| (new_input, new_context, ()))
-    }
-}
-
-pub struct Then<'a, 'c, P1, P2, R1, R2>
-where
-    'c: 'a,
-    P1: Parser<'a, 'c, R1>,
-    P2: Parser<'a, 'c, R2>,
-{
-    p1: P1,
-    p2: P2,
-    _phantom: PhantomData<(&'a (R1, R2), &'c ())>,
-}
-
-impl<'a, 'c, P1, P2, R1, R2> Parser<'a, 'c, (R1, R2)> for Then<'a, 'c, P1, P2, R1, R2>
-where
-    'c: 'a,
-    P1: Parser<'a, 'c, R1>,
-    P2: Parser<'a, 'c, R2>,
-{
-    fn parse(&self, input: &'a [u8], context: &'c mut AmlContext) -> ParseResult<'a, 'c, (R1, R2)> {
-        self.p1.parse(input, context).and_then(|(next_input, context, result_a)| {
-            self.p2
-                .parse(next_input, context)
-                .map(|(final_input, context, result_b)| (final_input, context, (result_a, result_b)))
-        })
-    }
-}
-
-pub struct Feed<'a, 'c, P1, P2, F, R1, R2>
-where
-    'c: 'a,
-    P1: Parser<'a, 'c, R1>,
-    P2: Parser<'a, 'c, R2>,
-    F: Fn(R1) -> P2,
-{
-    parser: P1,
-    producer_fn: F,
-    _phantom: PhantomData<(&'a (R1, R2), &'c ())>,
-}
-
-impl<'a, 'c, P1, P2, F, R1, R2> Parser<'a, 'c, R2> for Feed<'a, 'c, P1, P2, F, R1, R2>
-where
-    'c: 'a,
-    P1: Parser<'a, 'c, R1>,
-    P2: Parser<'a, 'c, R2>,
-    F: Fn(R1) -> P2,
-{
-    fn parse(&self, input: &'a [u8], context: &'c mut AmlContext) -> ParseResult<'a, 'c, R2> {
-        let (input, context, first_result) = self.parser.parse(input, context)?;
-
-        // We can now produce the second parser, and parse using that.
-        let second_parser = (self.producer_fn)(first_result);
-        second_parser.parse(input, context)
-    }
-}
-
-/// Takes a number of parsers, and tries to apply each one to the input in order. Returns the
-/// result of the first one that succeeds, or fails if all of them fail.
-pub(crate) macro choice {
-    () => {
-        id().map(|()| Err(AmlError::WrongParser))
-    },
-
-    /*
-     * The nice way of writing this would be something like:
-     * ```
-     * $first_parser
-     * $(
-     *     .or($other_parser)
-     *  )*
-     * .or(id().map(|()| Err(AmlError::WrongParser)))
-     * ```
-     * This problem with this is that it generates enormous types that very easily break `rustc`'s type
-     * limit, so writing large parsers with choice required some gymnastics, which sucks for everyone involved.
-     *
-     * Instead, we manually call each parser sequentially, checking its result to see if we should return, or try
-     * the next parser. This generates worse code at the macro callsite, but is much easier for the compiler to
-     * type-check (and so reduces the cost of pulling us in as a dependency as well as improving ergonomics).
-     */
-    ($($parser: expr),+) => {
-        move |input, context| {
-            $(
-                let context = match ($parser).parse(input, context) {
-                    Ok(parse_result) => return Ok(parse_result),
-                    Err((_, new_context, Propagate::Err(AmlError::WrongParser))) => new_context,
-                    Err((_, context, propagate)) => return Err((input, context, propagate)),
-                };
-             )+
-            Err((input, context, Propagate::Err(AmlError::WrongParser)))
-        }
-    }
-}
-
-/// Helper macro for use within `map_with_context` as an alternative to "trying" an expression.
-///
-/// ### Example
-/// Problem: `expr?` won't work because the expected return type is `(Result<R, AmlError>, &mut AmlContext)`
-/// Solution: use `try_with_context!(context, expr)` instead.
-pub(crate) macro try_with_context($context: expr, $expr: expr) {
-    match $expr {
-        Ok(result) => result,
-        Err(err) => return (Err(Propagate::Err(err)), $context),
-    }
-}
-
-#[cfg(test)]
-mod tests {
-    use super::*;
-    use crate::test_utils::*;
-
-    #[test]
-    fn test_take_n() {
-        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]);
-
-        check_ok!(take_n(1).parse(&[0xff], &mut context), &[0xff], &[]);
-        check_ok!(take_n(1).parse(&[0xff, 0xf8], &mut context), &[0xff], &[0xf8]);
-        check_ok!(take_n(2).parse(&[0xff, 0xf8], &mut context), &[0xff, 0xf8], &[]);
-    }
-
-    #[test]
-    fn test_take_ux() {
-        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, &[]);
-
-        check_err!(take_u32().parse(&[0x34, 0x12], &mut context), AmlError::UnexpectedEndOfStream, &[0x34, 0x12]);
-        check_ok!(take_u32().parse(&[0x34, 0x12, 0xf4, 0xc3, 0x3e], &mut context), 0xc3f41234, &[0x3e]);
-
-        check_err!(take_u64().parse(&[0x34], &mut context), AmlError::UnexpectedEndOfStream, &[0x34]);
-        check_ok!(
-            take_u64().parse(&[0x34, 0x12, 0x35, 0x76, 0xd4, 0x43, 0xa3, 0xb6, 0xff, 0x00], &mut context),
-            0xb6a343d476351234,
-            &[0xff, 0x00]
-        );
-    }
-}
diff --git a/aml/src/pci_routing.rs b/aml/src/pci_routing.rs
deleted file mode 100644
index 7acf9264..00000000
--- a/aml/src/pci_routing.rs
+++ /dev/null
@@ -1,186 +0,0 @@
-use crate::{
-    namespace::AmlName,
-    resource::{self, InterruptPolarity, InterruptTrigger, Resource},
-    value::Args,
-    AmlContext,
-    AmlError,
-    AmlType,
-    AmlValue,
-};
-use alloc::vec::Vec;
-use bit_field::BitField;
-use core::str::FromStr;
-
-pub use crate::resource::IrqDescriptor;
-
-#[derive(Clone, Copy, PartialEq, Eq, Debug)]
-pub enum Pin {
-    IntA,
-    IntB,
-    IntC,
-    IntD,
-}
-
-#[derive(Debug)]
-pub enum PciRouteType {
-    /// The interrupt is hard-coded to a specific GSI
-    Gsi(u32),
-
-    /// The interrupt is linked to a link object. This object will have `_PRS`, `_CRS` fields and a `_SRS` method
-    /// that can be used to allocate the interrupt. Note that some platforms (e.g. QEMU's q35 chipset) use link
-    /// objects but do not support changing the interrupt that it's linked to (i.e. `_SRS` doesn't do anything).
-    /*
-     * The actual object itself will just be a `Device`, and we need paths to its children objects to do
-     * anything useful, so we just store the resolved name here.
-     */
-    LinkObject(AmlName),
-}
-
-#[derive(Debug)]
-pub struct PciRoute {
-    device: u16,
-    function: u16,
-    pin: Pin,
-    route_type: PciRouteType,
-}
-
-/// A `PciRoutingTable` is used to interpret the data in a `_PRT` object, which provides a mapping
-/// from PCI interrupt pins to the inputs of the interrupt controller. One of these objects must be
-/// present under each PCI root bridge, and consists of a package of packages, each of which describes the
-/// mapping of a single PCI interrupt pin.
-#[derive(Debug)]
-pub struct PciRoutingTable {
-    entries: Vec<PciRoute>,
-}
-
-impl PciRoutingTable {
-    /// Construct a `PciRoutingTable` from a path to a `_PRT` object. Returns
-    /// `AmlError::IncompatibleValueConversion` if the value passed is not a package, or if any of the values
-    /// within it are not packages. Returns the various `AmlError::Prt*` errors if the internal structure of the
-    /// entries is invalid.
-    pub fn from_prt_path(prt_path: &AmlName, context: &mut AmlContext) -> Result<PciRoutingTable, AmlError> {
-        let mut entries = Vec::new();
-
-        let prt = context.invoke_method(prt_path, Args::default())?;
-        if let AmlValue::Package(ref inner_values) = prt {
-            for value in inner_values {
-                if let AmlValue::Package(ref pin_package) = value {
-                    /*
-                     * Each inner package has the following structure:
-                     *   | Field      | Type      | Description                                               |
-                     *   | -----------|-----------|-----------------------------------------------------------|
-                     *   | Address    | Dword     | Address of the device. Same format as _ADR objects (high  |
-                     *   |            |           | word = #device, low word = #function)                     |
-                     *   | -----------|-----------|-----------------------------------------------------------|
-                     *   | Pin        | Byte      | The PCI pin (0 = INTA, 1 = INTB, 2 = INTC, 3 = INTD)      |
-                     *   | -----------|-----------|-----------------------------------------------------------|
-                     *   | Source     | Byte or   | Name of the device that allocates the interrupt to which  |
-                     *   |            | NamePath  | the above pin is connected. Can be fully qualified,       |
-                     *   |            |           | relative, or a simple NameSeg that utilizes namespace     |
-                     *   |            |           | search rules. Instead, if this is a byte value of 0, the  |
-                     *   |            |           | interrupt is allocated out of the GSI pool, and Source    |
-                     *   |            |           | Index should be utilised.                                 |
-                     *   | -----------|-----------|-----------------------------------------------------------|
-                     *   | Source     | Dword     | Index that indicates which resource descriptor in the     |
-                     *   | Index      |           | resource template of the device pointed to in the Source  |
-                     *   |            |           | field this interrupt is allocated from. If the Source     |
-                     *   |            |           | is zero, then this field is the GSI number to which the   |
-                     *   |            |           | pin is connected.                                         |
-                     *   | -----------|-----------|-----------------------------------------------------------|
-                     */
-                    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(context)? {
-                        0 => Pin::IntA,
-                        1 => Pin::IntB,
-                        2 => Pin::IntC,
-                        3 => Pin::IntD,
-                        _ => return Err(AmlError::PrtInvalidPin),
-                    };
-
-                    match pin_package[2] {
-                        AmlValue::Integer(0) => {
-                            /*
-                             * The Source Index field contains the GSI number that this interrupt is attached
-                             * to.
-                             */
-                            entries.push(PciRoute {
-                                device,
-                                function,
-                                pin,
-                                route_type: PciRouteType::Gsi(
-                                    pin_package[3]
-                                        .as_integer(context)?
-                                        .try_into()
-                                        .map_err(|_| AmlError::PrtInvalidGsi)?,
-                                ),
-                            });
-                        }
-                        AmlValue::String(ref name) => {
-                            let link_object_name =
-                                context.namespace.search_for_level(&AmlName::from_str(name)?, prt_path)?;
-                            entries.push(PciRoute {
-                                device,
-                                function,
-                                pin,
-                                route_type: PciRouteType::LinkObject(link_object_name),
-                            });
-                        }
-                        _ => return Err(AmlError::PrtInvalidSource),
-                    }
-                } else {
-                    return Err(AmlError::IncompatibleValueConversion {
-                        current: value.type_of(),
-                        target: AmlType::Package,
-                    });
-                }
-            }
-
-            Ok(PciRoutingTable { entries })
-        } else {
-            Err(AmlError::IncompatibleValueConversion { current: prt.type_of(), target: AmlType::Package })
-        }
-    }
-
-    /// Get the interrupt input that a given PCI interrupt pin is wired to. Returns `AmlError::PrtNoEntry` if the
-    /// PRT doesn't contain an entry for the given address + pin.
-    pub fn route(
-        &self,
-        device: u16,
-        function: u16,
-        pin: Pin,
-        context: &mut AmlContext,
-    ) -> Result<IrqDescriptor, AmlError> {
-        let entry = self
-            .entries
-            .iter()
-            .find(|entry| {
-                entry.device == device
-                    && (entry.function == 0xffff || entry.function == function)
-                    && entry.pin == pin
-            })
-            .ok_or(AmlError::PrtNoEntry)?;
-
-        match entry.route_type {
-            PciRouteType::Gsi(gsi) => Ok(IrqDescriptor {
-                is_consumer: true,
-                trigger: InterruptTrigger::Level,
-                polarity: InterruptPolarity::ActiveLow,
-                is_shared: true,
-                is_wake_capable: false,
-                irq: gsi,
-            }),
-            PciRouteType::LinkObject(ref name) => {
-                let path = AmlName::from_str("_CRS").unwrap().resolve(name)?;
-                let link_crs = context.invoke_method(&path, Args::EMPTY)?;
-
-                let resources = resource::resource_descriptor_list(&link_crs)?;
-                match resources.as_slice() {
-                    [Resource::Irq(descriptor)] => Ok(descriptor.clone()),
-                    _ => Err(AmlError::UnexpectedResourceType),
-                }
-            }
-        }
-    }
-}
diff --git a/aml/src/pkg_length.rs b/aml/src/pkg_length.rs
deleted file mode 100644
index f3b03a5d..00000000
--- a/aml/src/pkg_length.rs
+++ /dev/null
@@ -1,214 +0,0 @@
-use crate::{
-    parser::{take, take_n, Parser, Propagate},
-    AmlContext,
-    AmlError,
-    AmlHandle,
-    AmlValue,
-};
-use bit_field::BitField;
-
-/*
- * There are two types of PkgLength implemented: PkgLength and RegionPkgLength. The reason for this
- * is that while both are parsed as PkgLength op, they might have different meanings in different
- * contexts:
- *
- * - PkgLength refers to an offset within the AML input slice
- * - RegionPkgLength refers to an offset within an operation region (and is used this way in parsers
- *      like def_field())
- *
- * They both have identical fields, but the fields themselves have an entirely different meaning.
- */
-
-#[derive(Clone, Copy, PartialEq, Eq, Debug)]
-pub struct PkgLength {
-    pub raw_length: u32,
-    /// The offset in the structure's stream to stop parsing at - the "end" of the PkgLength. We need to track this
-    /// instead of the actual length encoded in the PkgLength as we often need to parse some stuff between the
-    /// PkgLength and the explicit-length structure.
-    pub end_offset: u32,
-}
-
-#[derive(Clone, Copy, PartialEq, Eq, Debug)]
-pub struct RegionPkgLength {
-    pub raw_length: u32,
-    pub end_offset: u32,
-}
-
-impl PkgLength {
-    pub fn from_raw_length(stream: &[u8], raw_length: u32) -> Result<PkgLength, AmlError> {
-        Ok(PkgLength {
-            raw_length,
-            end_offset: (stream.len() as u32).checked_sub(raw_length).ok_or(AmlError::InvalidPkgLength)?,
-        })
-    }
-
-    /// Returns `true` if the given stream is still within the structure this `PkgLength` refers
-    /// to.
-    pub fn still_parsing(&self, stream: &[u8]) -> bool {
-        stream.len() as u32 > self.end_offset
-    }
-}
-
-impl RegionPkgLength {
-    pub fn from_raw_length(region_bit_length: u64, raw_length: u32) -> Result<RegionPkgLength, AmlError> {
-        Ok(RegionPkgLength {
-            raw_length,
-            end_offset: (region_bit_length as u32)
-                .checked_sub(raw_length)
-                .ok_or(AmlError::InvalidRegionPkgLength { region_bit_length, raw_length })?,
-        })
-    }
-}
-
-pub fn region_pkg_length<'a, 'c>(region_handle: AmlHandle) -> impl Parser<'a, 'c, RegionPkgLength>
-where
-    'c: 'a,
-{
-    move |input: &'a [u8], context: &'c mut AmlContext| -> crate::parser::ParseResult<'a, 'c, RegionPkgLength> {
-        let region_value = match context.namespace.get(region_handle) {
-            Ok(value) => value,
-            Err(err) => return Err((input, context, Propagate::Err(err))),
-        };
-
-        /*
-         * OperationRegion length is in bytes, PkgLength is in bits, so conversion is needed
-         */
-        let region_bit_length = match region_value {
-            AmlValue::OpRegion(region) => region.length() * 8,
-            _ => return Err((input, context, Propagate::Err(AmlError::FieldRegionIsNotOpRegion))),
-        };
-
-        let (new_input, context, raw_length) = raw_pkg_length().parse(input, context)?;
-
-        /*
-         * NOTE: we use the original input here, because `raw_length` includes the length of the
-         * `PkgLength`.
-         */
-        match RegionPkgLength::from_raw_length(region_bit_length, raw_length) {
-            Ok(pkg_length) => Ok((new_input, context, pkg_length)),
-            Err(err) => Err((input, context, Propagate::Err(err))),
-        }
-    }
-}
-
-pub fn pkg_length<'a, 'c>() -> impl Parser<'a, 'c, PkgLength>
-where
-    'c: 'a,
-{
-    move |input: &'a [u8], context: &'c mut AmlContext| -> crate::parser::ParseResult<'a, 'c, PkgLength> {
-        let (new_input, context, raw_length) = raw_pkg_length().parse(input, context)?;
-
-        /*
-         * NOTE: we use the original input here, because `raw_length` includes the length of the
-         * `PkgLength`.
-         */
-        match PkgLength::from_raw_length(input, raw_length) {
-            Ok(pkg_length) => Ok((new_input, context, pkg_length)),
-            Err(err) => Err((input, context, Propagate::Err(err))),
-        }
-    }
-}
-
-/// Parses a `PkgLength` and returns the *raw length*. If you want an instance of `PkgLength`, use
-/// `pkg_length` instead.
-pub fn raw_pkg_length<'a, 'c>() -> impl Parser<'a, 'c, u32>
-where
-    'c: 'a,
-{
-    /*
-     * PkgLength := PkgLeadByte |
-     * <PkgLeadByte ByteData> |
-     * <PkgLeadByte ByteData ByteData> |
-     * <PkgLeadByte ByteData ByteData ByteData>
-     *
-     * The length encoded by the PkgLength includes the number of bytes used to encode it.
-     */
-    move |input: &'a [u8], context: &'c mut AmlContext| {
-        let (new_input, context, lead_byte) = take().parse(input, context)?;
-        let byte_count = lead_byte.get_bits(6..8);
-
-        if byte_count == 0 {
-            let length = u32::from(lead_byte.get_bits(0..6));
-            return Ok((new_input, context, length));
-        }
-
-        let (new_input, context, length): (&[u8], &mut AmlContext, u32) = match take_n(byte_count as u32)
-            .parse(new_input, context)
-        {
-            Ok((new_input, context, bytes)) => {
-                let initial_length = u32::from(lead_byte.get_bits(0..4));
-                (
-                    new_input,
-                    context,
-                    bytes
-                        .iter()
-                        .enumerate()
-                        .fold(initial_length, |length, (i, &byte)| length + (u32::from(byte) << (4 + i * 8))),
-                )
-            }
-
-            /*
-             * The stream was too short. We return an error, making sure to return the
-             * *original* stream (that we haven't consumed any of).
-             */
-            Err((_, context, _)) => return Err((input, context, Propagate::Err(AmlError::UnexpectedEndOfStream))),
-        };
-
-        Ok((new_input, context, length))
-    }
-}
-
-#[cfg(test)]
-mod tests {
-    use super::*;
-    use crate::{test_utils::*, AmlError};
-
-    fn test_correct_pkglength(stream: &[u8], expected_raw_length: u32, expected_leftover: &[u8]) {
-        let mut context = make_test_context();
-        check_ok!(
-            pkg_length().parse(stream, &mut context),
-            PkgLength::from_raw_length(stream, expected_raw_length).unwrap(),
-            &expected_leftover
-        );
-    }
-
-    #[test]
-    fn test_raw_pkg_length() {
-        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, &[]);
-    }
-
-    #[test]
-    fn test_pkg_length() {
-        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]);
-
-        check_ok!(
-            pkg_length()
-                .feed(crate::parser::take_to_end_of_pkglength)
-                .parse(&[0x05, 0x01, 0x02, 0x03, 0x04, 0xff, 0xff, 0xff], &mut context),
-            &[0x01, 0x02, 0x03, 0x04],
-            &[0xff, 0xff, 0xff]
-        );
-    }
-
-    #[test]
-    fn not_enough_pkglength() {
-        let mut context = make_test_context();
-        check_err!(
-            pkg_length().parse(&[0b11000000, 0xff, 0x4f], &mut context),
-            AmlError::UnexpectedEndOfStream,
-            &[0b11000000, 0xff, 0x4f]
-        );
-    }
-
-    #[test]
-    fn not_enough_stream() {
-        let mut context = make_test_context();
-        check_err!(pkg_length().parse(&[0x05, 0xf5], &mut context), AmlError::InvalidPkgLength, &[0x05, 0xf5]);
-    }
-}
diff --git a/aml/src/resource.rs b/aml/src/resource.rs
deleted file mode 100644
index abe907ae..00000000
--- a/aml/src/resource.rs
+++ /dev/null
@@ -1,850 +0,0 @@
-use core::mem;
-
-use crate::{
-    value::{AmlType, AmlValue},
-    AmlError,
-};
-use alloc::vec::Vec;
-use bit_field::BitField;
-use byteorder::{ByteOrder, LittleEndian};
-
-#[derive(Debug, PartialEq, Eq)]
-pub enum Resource {
-    Irq(IrqDescriptor),
-    AddressSpace(AddressSpaceDescriptor),
-    MemoryRange(MemoryRangeDescriptor),
-    IOPort(IOPortDescriptor),
-    Dma(DMADescriptor),
-}
-
-/// Parse a `ResourceDescriptor` into a list of resources. Returns `AmlError::IncompatibleValueConversion` if the passed value is not a
-/// `Buffer`.
-pub fn resource_descriptor_list(descriptor: &AmlValue) -> Result<Vec<Resource>, AmlError> {
-    if let AmlValue::Buffer(bytes) = descriptor {
-        let mut descriptors = Vec::new();
-        let buffer_data = bytes.lock();
-        let mut bytes = buffer_data.as_slice();
-
-        while !bytes.is_empty() {
-            let (descriptor, remaining_bytes) = resource_descriptor(bytes)?;
-
-            if let Some(descriptor) = descriptor {
-                descriptors.push(descriptor);
-                bytes = remaining_bytes;
-            } else {
-                break;
-            }
-        }
-
-        Ok(descriptors)
-    } else {
-        Err(AmlError::IncompatibleValueConversion { current: descriptor.type_of(), target: AmlType::Buffer })
-    }
-}
-
-/// Parse a `ResourceDescriptor`. Returns `AmlError::IncompatibleValueConversion` if the passed value is not a
-/// `Buffer`.
-fn resource_descriptor(bytes: &[u8]) -> Result<(Option<Resource>, &[u8]), AmlError> {
-    /*
-     * If bit 7 of Byte 0 is set, it's a large descriptor. If not, it's a small descriptor.
-     */
-    if bytes[0].get_bit(7) {
-        /*
-         * We're parsing a large item. The descriptor type is encoded in Bits 0-6 of Byte 0. Valid types:
-         *      0x00: Reserved
-         *      0x01: 24-bit Memory Range Descriptor
-         *      0x02: Generic Register Descriptor
-         *      0x03: Reserved
-         *      0x04: Vendor-defined Descriptor
-         *      0x05: 32-bit Memory Range Descriptor
-         *      0x06: 32-bit Fixed Memory Range Descriptor
-         *      0x07: Address Space Resource Descriptor
-         *      0x08: Word Address Space Descriptor
-         *      0x09: Extended Interrupt Descriptor
-         *      0x0a: QWord Address Space Descriptor
-         *      0x0b: Extended Address Space Descriptor
-         *      0x0c: GPIO Connection Descriptor
-         *      0x0d: Pin Function Descriptor
-         *      0x0e: GenericSerialBus Connection Descriptor
-         *      0x0f: Pin Configuration Descriptor
-         *      0x10: Pin Group Descriptor
-         *      0x11: Pin Group Function Descriptor
-         *      0x12: Pin Group Configuration Descriptor
-         *      0x13-0x7f: Reserved
-         *
-         * Byte 1 contains bits 0-7 of the length, and Byte 2 contains bits 8-15 of the length. Subsequent
-         * bytes contain the actual data items.
-         */
-        let descriptor_type = bytes[0].get_bits(0..7);
-        let length = LittleEndian::read_u16(&bytes[1..=2]) as usize;
-        let (descriptor_bytes, remaining_bytes) = bytes.split_at(length + 3);
-
-        let descriptor = match descriptor_type {
-            0x01 => unimplemented!("24-bit Memory Range Descriptor"),
-            0x02 => unimplemented!("Generic Register Descriptor"),
-            0x03 => unimplemented!("0x03 Reserved"),
-            0x04 => unimplemented!("Vendor-defined Descriptor"),
-            0x05 => unimplemented!("32-bit Memory Range Descriptor"),
-            0x06 => fixed_memory_descriptor(descriptor_bytes),
-            0x07 => address_space_descriptor::<u32>(descriptor_bytes),
-            0x08 => address_space_descriptor::<u16>(descriptor_bytes),
-            0x09 => extended_interrupt_descriptor(descriptor_bytes),
-            0x0a => address_space_descriptor::<u64>(descriptor_bytes),
-            0x0b => unimplemented!("Extended Address Space Descriptor"),
-            0x0c => unimplemented!("GPIO Connection Descriptor"),
-            0x0d => unimplemented!("Pin Function Descriptor"),
-            0x0e => unimplemented!("GenericSerialBus Connection Descriptor"),
-            0x0f => unimplemented!("Pin Configuration Descriptor"),
-            0x10 => unimplemented!("Pin Group Descriptor"),
-            0x11 => unimplemented!("Pin Group Function Descriptor"),
-            0x12 => unimplemented!("Pin Group Configuration Descriptor"),
-
-            0x00 | 0x13..=0x7f => Err(AmlError::ReservedResourceType),
-            0x80..=0xff => unreachable!(),
-        }?;
-
-        Ok((Some(descriptor), remaining_bytes))
-    } else {
-        /*
-         * We're parsing a small descriptor. Byte 0 has the format:
-         *    | Bits        | Field             |
-         *    |-------------|-------------------|
-         *    | 0-2         | Length - n bytes  |
-         *    | 3-6         | Small item type   |
-         *    | 7           | 0 = small item    |
-         *
-         * The valid types are:
-         *      0x00-0x03: Reserved
-         *      0x04: IRQ Format Descriptor
-         *      0x05: DMA Format Descriptor
-         *      0x06: Start Dependent Functions Descriptor
-         *      0x07: End Dependent Functions Descriptor
-         *      0x08: IO Port Descriptor
-         *      0x09: Fixed Location IO Port Descriptor
-         *      0x0A: Fixed DMA Descriptor
-         *      0x0B-0x0D: Reserved
-         *      0x0E: Vendor Defined Descriptor
-         *      0x0F: End Tag Descriptor
-         */
-        let descriptor_type = bytes[0].get_bits(3..=6);
-        let length: usize = bytes[0].get_bits(0..=2) as usize;
-        let (descriptor_bytes, remaining_bytes) = bytes.split_at(length + 1);
-
-        let descriptor = match descriptor_type {
-            0x00..=0x03 => Err(AmlError::ReservedResourceType),
-            0x04 => irq_format_descriptor(descriptor_bytes),
-            0x05 => dma_format_descriptor(descriptor_bytes),
-            0x06 => unimplemented!("Start Dependent Functions Descriptor"),
-            0x07 => unimplemented!("End Dependent Functions Descriptor"),
-            0x08 => io_port_descriptor(descriptor_bytes),
-            0x09 => unimplemented!("Fixed Location IO Port Descriptor"),
-            0x0A => unimplemented!("Fixed DMA Descriptor"),
-            0x0B..=0x0D => Err(AmlError::ReservedResourceType),
-            0x0E => unimplemented!("Vendor Defined Descriptor"),
-            0x0F => return Ok((None, &[])),
-            0x10..=0xFF => unreachable!(),
-        }?;
-
-        Ok((Some(descriptor), remaining_bytes))
-    }
-}
-
-#[derive(Debug, PartialEq, Eq, Copy, Clone)]
-pub enum InterruptTrigger {
-    Edge,
-    Level,
-}
-
-#[derive(Debug, PartialEq, Eq, Copy, Clone)]
-pub enum InterruptPolarity {
-    ActiveHigh,
-    ActiveLow,
-}
-
-#[derive(Debug, PartialEq, Eq, Copy, Clone)]
-pub enum AddressSpaceResourceType {
-    MemoryRange,
-    IORange,
-    BusNumberRange,
-}
-
-#[derive(Debug, PartialEq, Eq, Copy, Clone)]
-pub enum AddressSpaceDecodeType {
-    Additive,
-    Subtractive,
-}
-
-#[derive(Debug, PartialEq, Eq)]
-pub struct AddressSpaceDescriptor {
-    pub resource_type: AddressSpaceResourceType,
-    pub is_maximum_address_fixed: bool,
-    pub is_minimum_address_fixed: bool,
-    pub decode_type: AddressSpaceDecodeType,
-
-    pub granularity: u64,
-    pub address_range: (u64, u64),
-    pub translation_offset: u64,
-    pub length: u64,
-}
-
-#[derive(Debug, PartialEq, Eq)]
-pub enum MemoryRangeDescriptor {
-    FixedLocation { is_writable: bool, base_address: u32, range_length: u32 },
-}
-
-fn fixed_memory_descriptor(bytes: &[u8]) -> Result<Resource, AmlError> {
-    /*
-     * -- 32-bit Fixed Memory Descriptor ---
-     * Offset     Field Name                              Definition
-     * Byte 0     32-bit Fixed Memory Range Descriptor    Value = 0x86 (10000110B) – Type = 1, Large item name = 0x06
-     * Byte 1     Length, bits [7:0]                      Value = 0x09 (9)
-     * Byte 2     Length, bits [15:8]                     Value = 0x00
-     * Byte 3     Information                             This field provides extra information about this memory.
-     *                                                    Bit [7:1]   Ignored
-     *                                                    Bit [0]     Write status, _RW
-     *                                                        1  writeable (read/write)
-     *                                                        0  non-writeable (read-only)
-     * Byte 4     Range base address, _BAS bits [7:0]     Address bits [7:0] of the base memory address for which the card may be configured.
-     * Byte 5     Range base address, _BAS bits [15:8]    Address bits [15:8] of the base memory address for which the card may be configured.
-     * Byte 6     Range base address, _BAS bits [23:16]   Address bits [23:16] of the base memory address for which the card may be configured.
-     * Byte 7     Range base address, _BAS bits [31:24]   Address bits [31:24] of the base memory address for which the card may be configured.
-     * Byte 8     Range length, _LEN bits [7:0]           This field contains bits [7:0] of the memory range length. The range length provides the length of the memory range in 1-byte blocks.
-     * Byte 9     Range length, _LEN bits [15:8]          This field contains bits [15:8] of the memory range length. The range length provides the length of the memory range in 1-byte blocks.
-     * Byte 10    Range length, _LEN bits [23:16]         This field contains bits [23:16] of the memory range length. The range length provides the length of the memory range in 1-byte blocks.
-     * Byte 11    Range length, _LEN bits [31:24]         This field contains bits [31:24] of the memory range length. The range length provides the length of the memory range in 1-byte blocks.
-     */
-    if bytes.len() < 12 {
-        return Err(AmlError::ResourceDescriptorTooShort);
-    }
-
-    if bytes.len() > 12 {
-        return Err(AmlError::ResourceDescriptorTooLong);
-    }
-
-    let information = bytes[3];
-    let is_writable = information.get_bit(0);
-
-    let base_address = LittleEndian::read_u32(&bytes[4..=7]);
-    let range_length = LittleEndian::read_u32(&bytes[8..=11]);
-
-    Ok(Resource::MemoryRange(MemoryRangeDescriptor::FixedLocation { is_writable, base_address, range_length }))
-}
-
-fn address_space_descriptor<T>(bytes: &[u8]) -> Result<Resource, AmlError> {
-    /*
-     * WORD Address Space Descriptor Definition
-     * Note: The definitions for DWORD and QWORD are the same other than the width of the address fields.
-     *
-     * Offset  Field Name                                   Definition
-     * Byte 0  WORD Address Space Descriptor                Value = 0x88 (10001000B) – Type = 1, Large item name = 0x08
-     * Byte 1  Length, bits [7:0]                           Variable length, minimum value = 0x0D (13)
-     * Byte 2  Length, bits [15:8]                          Variable length, minimum value = 0x00
-     * Byte 3  Resource Type                                Indicates which type of resource this descriptor describes. Defined values are:
-     *                                                        0         Memory range
-     *                                                        1         I/O range
-     *                                                        2         Bus number range
-     *                                                        3–191     Reserved
-     *                                                        192-255   Hardware Vendor Defined
-     * Byte 4  General Flags                                Flags that are common to all resource types:
-     *                                                        Bits [7:4]   Reserved (must be 0)
-     *                                                        Bit [3]     Max Address Fixed, _MAF:
-     *                                                          1  The specified maximum address is fixed
-     *                                                          0  The specified maximum address is not fixed
-     *                                                             and can be changed
-     *                                                        Bit [2]      Min Address Fixed,_MIF:
-     *                                                          1   The specified minimum address is fixed
-     *                                                          0   The specified minimum address is not fixed
-     *                                                              and can be changed
-     *                                                        Bit [1]      Decode Type, _DEC:
-     *                                                          1   This bridge subtractively decodes this address          (top level bridges only)
-     *                                                          0   This bridge positively decodes this address
-     *                                                        Bit [0]      Ignored
-     * Byte 5  Type Specific Flags                           Flags that are specific to each resource type. The meaning of the flags in this field depends on the value of the Resource Type field (see above).
-     * Byte 6  Address space granularity, _GRA bits[7:0]     A set bit in this mask means that this bit is decoded. All bits less significant than the most significant set bit must be set. (In other words, the value of the full Address Space Granularity field (all 16 bits) must be a number (2n-1).
-     * Byte 7  Address space granularity, _GRA bits[15:8]
-     * Byte 8  Address range minimum, _MIN, bits [7:0]       For bridges that translate addresses, this is the address space on the secondary side of the bridge.
-     * Byte 9  Address range minimum, _MIN, bits [15:8]
-     * Byte 10 Address range maximum, _MAX, bits [7:0]       For bridges that translate addresses, this is the address space on the secondary side of the bridge.
-     * Byte 11 Address range maximum, _MAX, bits [15:8]
-     * Byte 12 Address Translation offset, _TRA, bits [7:0]  For bridges that translate addresses across the bridge, this is the offset that must be added to the address on the secondary side to obtain the address on the primary side. Non-bridge devices must list 0 for all Address Translation offset bits.
-     * Byte 13 Address Translation offset, _TRA, bits [15:8]
-     * Byte 14 Address Length, _LEN, bits [7:0]
-     * Byte 15 Address Length, _LEN, bits [15:8]
-     * Byte 16 Resource Source Index (Optional)              Only present if Resource Source (below) is present. This field gives an index to the specific resource descriptor that this device consumes from in the current resource template for the device object pointed to in Resource Source.
-     * String  Resource Source (Optional)                    If present, the device that uses this descriptor consumes its resources from the resources produced by the named device object. If not present, the device consumes its resources out of a global pool. If not present, the device consumes this resource from its hierarchical parent.
-     */
-    let size = mem::size_of::<T>();
-
-    if bytes.len() < 6 + size * 5 {
-        return Err(AmlError::ResourceDescriptorTooShort);
-    }
-
-    let resource_type = match bytes[3] {
-        0 => AddressSpaceResourceType::MemoryRange,
-        1 => AddressSpaceResourceType::IORange,
-        2 => AddressSpaceResourceType::BusNumberRange,
-        3..=191 => return Err(AmlError::ReservedResourceType),
-        192..=255 => unimplemented!(),
-    };
-
-    let general_flags = bytes[4];
-    let is_maximum_address_fixed = general_flags.get_bit(3);
-    let is_minimum_address_fixed = general_flags.get_bit(2);
-    let decode_type = if general_flags.get_bit(1) {
-        AddressSpaceDecodeType::Subtractive
-    } else {
-        AddressSpaceDecodeType::Additive
-    };
-
-    let mut address_fields = bytes[6..].chunks_exact(size);
-
-    // it's safe to unwrap because we check the length at the top
-    let granularity = LittleEndian::read_uint(address_fields.next().unwrap(), size);
-    let address_range_min = LittleEndian::read_uint(address_fields.next().unwrap(), size);
-    let address_range_max = LittleEndian::read_uint(address_fields.next().unwrap(), size);
-    let translation_offset = LittleEndian::read_uint(address_fields.next().unwrap(), size);
-    let length = LittleEndian::read_uint(address_fields.next().unwrap(), size);
-
-    Ok(Resource::AddressSpace(AddressSpaceDescriptor {
-        resource_type,
-        is_maximum_address_fixed,
-        is_minimum_address_fixed,
-        decode_type,
-        granularity,
-        address_range: (address_range_min, address_range_max),
-        translation_offset,
-        length,
-    }))
-}
-
-#[derive(Debug, PartialEq, Eq, Clone)]
-pub struct IrqDescriptor {
-    pub is_consumer: bool,
-    pub trigger: InterruptTrigger,
-    pub polarity: InterruptPolarity,
-    pub is_shared: bool,
-    pub is_wake_capable: bool,
-    /*
-     * NOTE: We currently only support the cases where a descriptor only contains a single interrupt
-     * number.
-     */
-    pub irq: u32,
-}
-
-fn irq_format_descriptor(bytes: &[u8]) -> Result<Resource, AmlError> {
-    /*
-     * IRQ Descriptor Definition
-     *
-     * Offset   Field Name
-     * Byte 0   Value = 0x22 or 0x23 (0010001nB)– Type = 0, Small item name = 0x4, Length = 2 or 3
-     * Byte 1   IRQ mask bits[7:0], _INT
-     *          Bit [0] represents IRQ0, bit[1] is IRQ1, and so on.
-     * Byte 2   IRQ mask bits[15:8], _INT
-     *          Bit [0] represents IRQ8, bit[1] is IRQ9, and so on.
-     * Byte 3   IRQ Information. Each bit, when set, indicates this device is capable of driving a certain type of interrupt.
-     *          (Optional—if not included then assume edge sensitive, high true interrupts.)
-     *          These bits can be used both for reporting and setting IRQ resources.
-     *          Note: This descriptor is meant for describing interrupts that are connected to PIC-compatible interrupt controllers, which can only be programmed for Active-High-Edge-Triggered or Active-Low-Level-Triggered interrupts. Any other combination is invalid. The Extended Interrupt Descriptor can be used to describe other combinations.
-     *            Bit [7:6]  Reserved (must be 0)
-     *            Bit [5]    Wake Capability, _WKC
-     *              0x0 = Not Wake Capable: This interrupt is not capable of waking the system.
-     *              0x1 = Wake Capable: This interrupt is capable of waking the system from a
-     *                    low-power idle state or a system sleep state.
-     *            Bit [4]    Interrupt Sharing, _SHR
-     *              0x0 = Exclusive: This interrupt is not shared with other devices.
-     *              0x1 = Shared: This interrupt is shared with other devices.
-     *            Bit [3]    Interrupt Polarity, _LL
-     *              0  Active-High – This interrupt is sampled when the signal is high, or true
-     *              1  Active-Low – This interrupt is sampled when the signal is low, or false.
-     *            Bit [2:1]  Ignored
-     *            Bit [0]    Interrupt Mode, _HE
-     *              0  Level-Triggered – Interrupt is triggered in response to signal in a low state.
-     *              1  Edge-Triggered – Interrupt is triggered in response to a change in signal state from low to high.
-     */
-
-    match bytes.len() {
-        0..=2 => Err(AmlError::ResourceDescriptorTooShort),
-        3 => {
-            // no IRQ information ("length 2" in spec)
-            let irq = LittleEndian::read_u16(&bytes[1..=2]);
-
-            Ok(Resource::Irq(IrqDescriptor {
-                irq: irq as u32,
-                is_wake_capable: false,
-                is_shared: false,
-                polarity: InterruptPolarity::ActiveHigh,
-                trigger: InterruptTrigger::Edge,
-
-                is_consumer: false, // assumed to be producer
-            }))
-        }
-        4 => {
-            // with IRQ information ("length 3" in spec)
-            let irq = LittleEndian::read_u16(&bytes[1..=2]);
-
-            let information = bytes[3];
-            let is_wake_capable = information.get_bit(5);
-            let is_shared = information.get_bit(4);
-            let polarity = match information.get_bit(3) {
-                false => InterruptPolarity::ActiveHigh,
-                true => InterruptPolarity::ActiveLow,
-            };
-            let trigger = match information.get_bit(0) {
-                false => InterruptTrigger::Level,
-                true => InterruptTrigger::Edge,
-            };
-
-            Ok(Resource::Irq(IrqDescriptor {
-                irq: irq as u32,
-                is_wake_capable,
-                is_shared,
-                polarity,
-                trigger,
-
-                is_consumer: false, // assumed to be producer
-            }))
-        }
-        _ => Err(AmlError::ResourceDescriptorTooLong),
-    }
-}
-
-#[derive(Debug, PartialEq, Eq)]
-pub enum DMASupportedSpeed {
-    CompatibilityMode,
-    TypeA, // as described by the EISA
-    TypeB,
-    TypeF,
-}
-
-#[derive(Debug, PartialEq, Eq)]
-pub enum DMATransferTypePreference {
-    _8BitOnly,
-    _8And16Bit,
-    _16Bit,
-}
-
-#[derive(Debug, PartialEq, Eq)]
-pub struct DMADescriptor {
-    pub channel_mask: u8,
-    pub supported_speeds: DMASupportedSpeed,
-    pub is_bus_master: bool,
-    pub transfer_type_preference: DMATransferTypePreference,
-}
-
-pub fn dma_format_descriptor(bytes: &[u8]) -> Result<Resource, AmlError> {
-    /*
-     * DMA Descriptor Definition
-     * Offset  Field Name
-     * Byte 0  Value = 0x2A (00101010B) – Type = 0, Small item name = 0x5, Length = 2
-     * Byte 1  DMA channel mask bits [7:0] (channels 0 – 7), _DMA
-     *         Bit [0] is channel 0, etc.
-     * Byte 2  Bit [7]           Reserved (must be 0)
-     *         Bits [6:5]        DMA channel speed supported, _TYP
-     *           00    Indicates compatibility mode
-     *           01    Indicates Type A DMA as described in the EISA
-     *           10    Indicates Type B DMA
-     *           11    Indicates Type F
-     *         Bits [4:3]        Ignored
-     *         Bit [2]           Logical device bus master status, _BM
-     *           0      Logical device is not a bus master
-     *           1      Logical device is a bus master
-     *         Bits [1:0]       DMA transfer type preference, _SIZ
-     *           00    8-bit only
-     *           01    8- and 16-bit
-     *           10    16-bit only
-     *           11    Reserved
-     */
-    if bytes.len() < 3 {
-        return Err(AmlError::ResourceDescriptorTooShort);
-    }
-
-    if bytes.len() > 3 {
-        return Err(AmlError::ResourceDescriptorTooLong);
-    }
-
-    let channel_mask = bytes[1];
-    let options = bytes[2];
-    let supported_speeds = match options.get_bits(5..=6) {
-        0 => DMASupportedSpeed::CompatibilityMode,
-        1 => DMASupportedSpeed::TypeA,
-        2 => DMASupportedSpeed::TypeB,
-        3 => DMASupportedSpeed::TypeF,
-        _ => unreachable!(),
-    };
-    let is_bus_master = options.get_bit(2);
-    let transfer_type_preference = match options.get_bits(0..=1) {
-        0 => DMATransferTypePreference::_8BitOnly,
-        1 => DMATransferTypePreference::_8And16Bit,
-        2 => DMATransferTypePreference::_16Bit,
-        3 => unimplemented!("Reserved DMA transfer type preference"),
-        _ => unreachable!(),
-    };
-
-    Ok(Resource::Dma(DMADescriptor { channel_mask, supported_speeds, is_bus_master, transfer_type_preference }))
-}
-
-#[derive(Debug, PartialEq, Eq)]
-pub struct IOPortDescriptor {
-    pub decodes_full_address: bool,
-    pub memory_range: (u16, u16),
-    pub base_alignment: u8,
-    pub range_length: u8,
-}
-
-fn io_port_descriptor(bytes: &[u8]) -> Result<Resource, AmlError> {
-    /*
-     * I/O Port Descriptor Definition
-     * Offset   Field Name                                  Definition
-     * Byte 0   I/O Port Descriptor                         Value = 0x47 (01000111B) –
-     *                                                      Type = 0, Small item name = 0x8, Length = 7
-     * Byte 1   Information                                 Bits [7:1]     Reserved and must be 0
-     *                                                      Bit [0]          (_DEC)
-     *                                                        1    The logical device decodes 16-bit addresses
-     *                                                        0    The logical device only decodes address bits[9:0]
-     * Byte 2   Range minimum base address, _MIN bits[7:0]  Address bits [7:0] of the minimum base I/O address that the card may be configured for.
-     * Byte 3   Range minimum base address, _MIN bits[15:8] Address bits [15:8] of the minimum base I/O address that the card may be configured for.
-     * Byte 4   Range maximum base address, _MAX bits[7:0]  Address bits [7:0] of the maximum base I/O address that the card may be configured for.
-     * Byte 5   Range maximum base address, _MAX bits[15:8] Address bits [15:8] of the maximum base I/O address that the card may be configured for.
-     * Byte 6   Base alignment, _ALN                        Alignment for minimum base address, increment in 1-byte blocks.
-     * Byte 7   Range length, _LEN                          The number of contiguous I/O ports requested.
-     */
-    if bytes.len() < 8 {
-        return Err(AmlError::ResourceDescriptorTooShort);
-    }
-
-    if bytes.len() > 8 {
-        return Err(AmlError::ResourceDescriptorTooLong);
-    }
-
-    let information = bytes[1];
-    let decodes_full_address = information.get_bit(0);
-
-    let memory_range_min = LittleEndian::read_u16(&bytes[2..=3]);
-    let memory_range_max = LittleEndian::read_u16(&bytes[4..=5]);
-    let memory_range = (memory_range_min, memory_range_max);
-
-    let base_alignment = bytes[6];
-    let range_length = bytes[7];
-
-    Ok(Resource::IOPort(IOPortDescriptor { decodes_full_address, memory_range, base_alignment, range_length }))
-}
-
-fn extended_interrupt_descriptor(bytes: &[u8]) -> Result<Resource, AmlError> {
-    /*
-     * --- Extended Interrupt Descriptor ---
-     * Byte 3 contains the Interrupt Vector Flags:
-     *      Bit 0: 1 if device consumes the resource, 0 if it produces it
-     *      Bit 1: 1 if edge-triggered, 0 if level-triggered
-     *      Bit 2: 1 = active-high, 0 = active-low
-     *      Bit 3: 1 if interrupt is shared with other devices
-     *      Bit 4: 1 if this interrupt is capable of waking the system, 0 if it is not
-     * Byte 4 contains the number of interrupt numbers that follow. When this descriptor is
-     * returned from `_CRS` or send to `_SRS`, this field must be 1.
-     *
-     * From Byte 5 onwards, there are `n` interrupt numbers, each of which is encoded as a
-     * 4-byte little-endian number.
-     *
-     * NOTE: We only support the case where there is a single interrupt number.
-     */
-    if bytes.len() < 9 {
-        return Err(AmlError::ResourceDescriptorTooShort);
-    }
-
-    let number_of_interrupts = bytes[4] as usize;
-    assert_eq!(number_of_interrupts, 1);
-    let irq = LittleEndian::read_u32(&[bytes[5], bytes[6], bytes[7], bytes[8]]);
-
-    Ok(Resource::Irq(IrqDescriptor {
-        is_consumer: bytes[3].get_bit(0),
-        trigger: if bytes[3].get_bit(1) { InterruptTrigger::Edge } else { InterruptTrigger::Level },
-        polarity: if bytes[3].get_bit(2) { InterruptPolarity::ActiveLow } else { InterruptPolarity::ActiveHigh },
-        is_shared: bytes[3].get_bit(3),
-        is_wake_capable: bytes[3].get_bit(4),
-        irq,
-    }))
-}
-
-#[cfg(test)]
-mod tests {
-    use super::*;
-    use std::sync::Arc;
-
-    #[test]
-    fn test_parses_keyboard_crs() {
-        let bytes: Vec<u8> = [
-            // Generated from `iasl -l pc-bios_acpi-dsdt.asl`
-            //
-            //         315:                   IO (Decode16,
-            //         316:                       0x0060,             // Range Minimum
-            //         317:                       0x0060,             // Range Maximum
-            //         318:                       0x01,               // Alignment
-            //         319:                       0x01,               // Length
-            //         320:                       )
-
-            //    0000040A:  47 01 60 00 60 00 01 01     "G.`.`..."
-            0x47, 0x01, 0x60, 0x00, 0x60, 0x00, 0x01, 0x01,
-            //         321:                   IO (Decode16,
-            //         322:                       0x0064,             // Range Minimum
-            //         323:                       0x0064,             // Range Maximum
-            //         324:                       0x01,               // Alignment
-            //         325:                       0x01,               // Length
-            //         326:                       )
-
-            //    00000412:  47 01 64 00 64 00 01 01     "G.d.d..."
-            0x47, 0x01, 0x64, 0x00, 0x64, 0x00, 0x01, 0x01,
-            //         327:                   IRQNoFlags ()
-            //         328:                       {1}
-
-            //    0000041A:  22 02 00 ...............    "".."
-            0x22, 0x02, 0x00, //    0000041D:  79 00 ..................    "y."
-            0x79, 0x00,
-        ]
-        .to_vec();
-
-        let value: AmlValue = AmlValue::Buffer(Arc::new(spinning_top::Spinlock::new(bytes)));
-        let resources = resource_descriptor_list(&value).unwrap();
-
-        assert_eq!(
-            resources,
-            Vec::from([
-                Resource::IOPort(IOPortDescriptor {
-                    decodes_full_address: true,
-                    memory_range: (0x60, 0x60),
-                    base_alignment: 1,
-                    range_length: 1
-                }),
-                Resource::IOPort(IOPortDescriptor {
-                    decodes_full_address: true,
-                    memory_range: (0x64, 0x64),
-                    base_alignment: 1,
-                    range_length: 1
-                }),
-                Resource::Irq(IrqDescriptor {
-                    is_consumer: false,
-                    trigger: InterruptTrigger::Edge,
-                    polarity: InterruptPolarity::ActiveHigh,
-                    is_shared: false,
-                    is_wake_capable: false,
-                    irq: (1 << 1)
-                })
-            ])
-        );
-    }
-
-    #[test]
-    fn test_pci_crs() {
-        let bytes: Vec<u8> = [
-            // Generated from `iasl -l pc-bios_acpi-dsdt.asl`
-            //
-            //      98:               WordBusNumber (ResourceProducer, MinFixed, MaxFixed, PosDecode,
-            //      99:                   0x0000,             // Granularity
-            //     100:                   0x0000,             // Range Minimum
-            //     101:                   0x00FF,             // Range Maximum
-            //     102:                   0x0000,             // Translation Offset
-            //     103:                   0x0100,             // Length
-            //     104:                   ,, )
-
-            // 000000F3:  88 0D 00 02 0C 00 00 00     "........"
-            // 000000FB:  00 00 FF 00 00 00 00 01     "........"
-            0x88, 0x0D, 0x00, 0x02, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x01,
-            //     105:               IO (Decode16,
-            //     106:                   0x0CF8,             // Range Minimum
-            //     107:                   0x0CF8,             // Range Maximum
-            //     108:                   0x01,               // Alignment
-            //     109:                   0x08,               // Length
-            //     110:                   )
-
-            // 00000103:  47 01 F8 0C F8 0C 01 08     "G......."
-            0x47, 0x01, 0xF8, 0x0C, 0xF8, 0x0C, 0x01, 0x08,
-            //     111:               WordIO (ResourceProducer, MinFixed, MaxFixed, PosDecode, EntireRange,
-            //     112:                   0x0000,             // Granularity
-            //     113:                   0x0000,             // Range Minimum
-            //     114:                   0x0CF7,             // Range Maximum
-            //     115:                   0x0000,             // Translation Offset
-            //     116:                   0x0CF8,             // Length
-            //     117:                   ,, , TypeStatic, DenseTranslation)
-
-            // 0000010B:  88 0D 00 01 0C 03 00 00     "........"
-            // 00000113:  00 00 F7 0C 00 00 F8 0C     "........"
-            0x88, 0x0D, 0x00, 0x01, 0x0C, 0x03, 0x00, 0x00, 0x00, 0x00, 0xF7, 0x0C, 0x00, 0x00, 0xF8, 0x0C,
-            //     118:               WordIO (ResourceProducer, MinFixed, MaxFixed, PosDecode, EntireRange,
-            //     119:                   0x0000,             // Granularity
-            //     120:                   0x0D00,             // Range Minimum
-            //     121:                   0xFFFF,             // Range Maximum
-            //     122:                   0x0000,             // Translation Offset
-            //     123:                   0xF300,             // Length
-            //     124:                   ,, , TypeStatic, DenseTranslation)
-
-            // 0000011B:  88 0D 00 01 0C 03 00 00     "........"
-            // 00000123:  00 0D FF FF 00 00 00 F3     "........"
-            0x88, 0x0D, 0x00, 0x01, 0x0C, 0x03, 0x00, 0x00, 0x00, 0x0D, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0xF3,
-            //     125:               DWordMemory (ResourceProducer, PosDecode, MinFixed, MaxFixed, Cacheable, ReadWrite,
-            //     126:                   0x00000000,         // Granularity
-            //     127:                   0x000A0000,         // Range Minimum
-            //     128:                   0x000BFFFF,         // Range Maximum
-            //     129:                   0x00000000,         // Translation Offset
-            //     130:                   0x00020000,         // Length
-            //     131:                   ,, , AddressRangeMemory, TypeStatic)
-
-            // 0000012B:  87 17 00 00 0C 03 00 00     "........"
-            // 00000133:  00 00 00 00 0A 00 FF FF     "........"
-            // 0000013B:  0B 00 00 00 00 00 00 00     "........"
-            // 00000143:  02 00 ..................    ".."
-            0x87, 0x17, 0x00, 0x00, 0x0C, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0A, 0x00, 0xFF, 0xFF, 0x0B,
-            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00,
-            //     132:               DWordMemory (ResourceProducer, PosDecode, MinFixed, MaxFixed, NonCacheable, ReadWrite,
-            //     133:                   0x00000000,         // Granularity
-            //     134:                   0xE0000000,         // Range Minimum
-            //     135:                   0xFEBFFFFF,         // Range Maximum
-            //     136:                   0x00000000,         // Translation Offset
-            //     137:                   0x1EC00000,         // Length
-            //     138:                   ,, _Y00, AddressRangeMemory, TypeStatic)
-
-            // 00000145:  87 17 00 00 0C 01 00 00     "........"
-            // 0000014D:  00 00 00 00 00 E0 FF FF     "........"
-            // 00000155:  BF FE 00 00 00 00 00 00     "........"
-            // 0000015D:  C0 1E ..................    ".."
-            0x87, 0x17, 0x00, 0x00, 0x0C, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0xFF, 0xFF, 0xBF,
-            0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x1E,
-            // 0000015F:  79 00 ..................    "y."
-            0x79, 0x00,
-        ]
-        .to_vec();
-
-        let value: AmlValue = AmlValue::Buffer(Arc::new(spinning_top::Spinlock::new(bytes)));
-        let resources = resource_descriptor_list(&value).unwrap();
-
-        assert_eq!(
-            resources,
-            Vec::from([
-                Resource::AddressSpace(AddressSpaceDescriptor {
-                    resource_type: AddressSpaceResourceType::BusNumberRange,
-                    is_maximum_address_fixed: true,
-                    is_minimum_address_fixed: true,
-                    decode_type: AddressSpaceDecodeType::Additive,
-                    granularity: 0,
-                    address_range: (0x00, 0xFF),
-                    translation_offset: 0,
-                    length: 0x100
-                }),
-                Resource::IOPort(IOPortDescriptor {
-                    decodes_full_address: true,
-                    memory_range: (0xCF8, 0xCF8),
-                    base_alignment: 1,
-                    range_length: 8
-                }),
-                Resource::AddressSpace(AddressSpaceDescriptor {
-                    resource_type: AddressSpaceResourceType::IORange,
-                    is_maximum_address_fixed: true,
-                    is_minimum_address_fixed: true,
-                    decode_type: AddressSpaceDecodeType::Additive,
-                    granularity: 0,
-                    address_range: (0x0000, 0x0CF7),
-                    translation_offset: 0,
-                    length: 0xCF8
-                }),
-                Resource::AddressSpace(AddressSpaceDescriptor {
-                    resource_type: AddressSpaceResourceType::IORange,
-                    is_maximum_address_fixed: true,
-                    is_minimum_address_fixed: true,
-                    decode_type: AddressSpaceDecodeType::Additive,
-                    granularity: 0,
-                    address_range: (0x0D00, 0xFFFF),
-                    translation_offset: 0,
-                    length: 0xF300
-                }),
-                Resource::AddressSpace(AddressSpaceDescriptor {
-                    resource_type: AddressSpaceResourceType::MemoryRange,
-                    is_maximum_address_fixed: true,
-                    is_minimum_address_fixed: true,
-                    decode_type: AddressSpaceDecodeType::Additive,
-                    granularity: 0,
-                    address_range: (0xA0000, 0xBFFFF),
-                    translation_offset: 0,
-                    length: 0x20000
-                }),
-                Resource::AddressSpace(AddressSpaceDescriptor {
-                    resource_type: AddressSpaceResourceType::MemoryRange,
-                    is_maximum_address_fixed: true,
-                    is_minimum_address_fixed: true,
-                    decode_type: AddressSpaceDecodeType::Additive,
-                    granularity: 0,
-                    address_range: (0xE0000000, 0xFEBFFFFF),
-                    translation_offset: 0,
-                    length: 0x1EC00000
-                }),
-            ])
-        );
-    }
-
-    #[test]
-    fn test_fdc_crs() {
-        let bytes: Vec<u8> = [
-            //         365:                   IO (Decode16,
-            //         366:                       0x03F2,             // Range Minimum
-            //         367:                       0x03F2,             // Range Maximum
-            //         368:                       0x00,               // Alignment
-            //         369:                       0x04,               // Length
-            //         370:                       )
-
-            //    0000047C:  47 01 F2 03 F2 03 00 04     "G......."
-            0x47, 0x01, 0xF2, 0x03, 0xF2, 0x03, 0x00, 0x04,
-            //         371:                   IO (Decode16,
-            //         372:                       0x03F7,             // Range Minimum
-            //         373:                       0x03F7,             // Range Maximum
-            //         374:                       0x00,               // Alignment
-            //         375:                       0x01,               // Length
-            //         376:                       )
-
-            //    00000484:  47 01 F7 03 F7 03 00 01     "G......."
-            0x47, 0x01, 0xF7, 0x03, 0xF7, 0x03, 0x00, 0x01,
-            //         377:                   IRQNoFlags ()
-            //         378:                       {6}
-
-            //    0000048C:  22 40 00 ...............    ""@."
-            0x22, 0x40, 0x00,
-            //         379:                   DMA (Compatibility, NotBusMaster, Transfer8, )
-            //         380:                       {2}
-
-            //    0000048F:  2A 04 00 ...............    "*.."
-            0x2A, 0x04, 0x00, //    00000492:  79 00 ..................    "y."
-            0x79, 0x00,
-        ]
-        .to_vec();
-
-        let value: AmlValue = AmlValue::Buffer(Arc::new(spinning_top::Spinlock::new(bytes)));
-        let resources = resource_descriptor_list(&value).unwrap();
-
-        assert_eq!(
-            resources,
-            Vec::from([
-                Resource::IOPort(IOPortDescriptor {
-                    decodes_full_address: true,
-                    memory_range: (0x03F2, 0x03F2),
-                    base_alignment: 0,
-                    range_length: 4
-                }),
-                Resource::IOPort(IOPortDescriptor {
-                    decodes_full_address: true,
-                    memory_range: (0x03F7, 0x03F7),
-                    base_alignment: 0,
-                    range_length: 1
-                }),
-                Resource::Irq(IrqDescriptor {
-                    is_consumer: false,
-                    trigger: InterruptTrigger::Edge,
-                    polarity: InterruptPolarity::ActiveHigh,
-                    is_shared: false,
-                    is_wake_capable: false,
-                    irq: (1 << 6)
-                }),
-                Resource::Dma(DMADescriptor {
-                    channel_mask: 1 << 2,
-                    supported_speeds: DMASupportedSpeed::CompatibilityMode,
-                    is_bus_master: false,
-                    transfer_type_preference: DMATransferTypePreference::_8BitOnly
-                })
-            ])
-        );
-    }
-}
diff --git a/aml/src/statement.rs b/aml/src/statement.rs
deleted file mode 100644
index 3687a686..00000000
--- a/aml/src/statement.rs
+++ /dev/null
@@ -1,328 +0,0 @@
-use crate::{
-    opcode::{self, ext_opcode, opcode},
-    parser::{
-        choice,
-        comment_scope,
-        extract,
-        id,
-        take,
-        take_to_end_of_pkglength,
-        take_u32,
-        try_with_context,
-        ParseResult,
-        Parser,
-        Propagate,
-    },
-    pkg_length::{pkg_length, PkgLength},
-    term_object::{term_arg, term_list},
-    AmlContext,
-    AmlError,
-    DebugVerbosity,
-};
-
-pub fn statement_opcode<'a, 'c>() -> impl Parser<'a, 'c, ()>
-where
-    'c: 'a,
-{
-    /*
-     * StatementOpcode := DefBreak | DefBreakPoint | DefContinue | DefFatal | DefIfElse | DefLoad | DefNoop |
-     *                    DefNotify | DefRelease | DefReset | DefReturn | DefSignal | DefSleep | DefStall | DefWhile
-     */
-    comment_scope(
-        DebugVerbosity::AllScopes,
-        "StatementOpcode",
-        choice!(
-            def_break(),
-            def_breakpoint(),
-            def_continue(),
-            def_fatal(),
-            def_if_else(),
-            def_noop(),
-            def_return(),
-            def_sleep(),
-            def_stall(),
-            def_while()
-        ),
-    )
-}
-
-fn def_break<'a, 'c>() -> impl Parser<'a, 'c, ()>
-where
-    'c: 'a,
-{
-    /*
-     * DefBreak := 0xa5
-     */
-    opcode(opcode::DEF_BREAK_OP)
-        .then(comment_scope(
-            DebugVerbosity::AllScopes,
-            "DefBreak",
-            id().map(|()| -> Result<(), Propagate> { Err(Propagate::Break) }),
-        ))
-        .discard_result()
-}
-
-fn def_breakpoint<'a, 'c>() -> impl Parser<'a, 'c, ()>
-where
-    'c: 'a,
-{
-    /*
-     * DefBreakPoint := 0xcc
-     * TODO: there is no debugger, so this doesn't do anything. If there was, this should stop execution and enter
-     * the AML debugger.
-     */
-    opcode(opcode::DEF_BREAKPOINT_OP)
-        .then(comment_scope(DebugVerbosity::AllScopes, "DefBreakPoint", id()))
-        .discard_result()
-}
-
-fn def_continue<'a, 'c>() -> impl Parser<'a, 'c, ()>
-where
-    'c: 'a,
-{
-    /*
-     * DefContinue := 0x9f
-     */
-    opcode(opcode::DEF_CONTINUE_OP)
-        .then(comment_scope(
-            DebugVerbosity::AllScopes,
-            "DefContinue",
-            id().map(|()| -> Result<(), Propagate> { Err(Propagate::Continue) }),
-        ))
-        .discard_result()
-}
-
-fn def_fatal<'a, 'c>() -> impl Parser<'a, 'c, ()>
-where
-    'c: 'a,
-{
-    /*
-     * DefFatal := ExtOpPrefix 0x32 FatalType FatalCode FatalArg
-     * FatalType := ByteData
-     * FatalCode := DWordData
-     * FatalArg := TermArg => Integer
-     */
-    ext_opcode(opcode::EXT_DEF_FATAL_OP)
-        .then(comment_scope(
-            DebugVerbosity::Scopes,
-            "DefFatal",
-            take().then(take_u32()).then(term_arg()).map_with_context(
-                |((fatal_type, fatal_code), fatal_arg), context| -> (Result<(), Propagate>, &'c mut AmlContext) {
-                    let fatal_arg = try_with_context!(context, fatal_arg.as_integer(context));
-                    context.handler.handle_fatal_error(fatal_type, fatal_code, fatal_arg);
-                    (Err(Propagate::Err(AmlError::FatalError)), context)
-                },
-            ),
-        ))
-        .discard_result()
-}
-
-fn def_if_else<'a, 'c>() -> impl Parser<'a, 'c, ()>
-where
-    'c: 'a,
-{
-    /*
-     * DefIfElse := 0xa0 PkgLength Predicate TermList DefElse
-     * Predicate := TermArg => Integer (0 = false, >0 = true)
-     * DefElse := Nothing | <0xa1 PkgLength TermList>
-     */
-
-    let maybe_else_opcode = |input, context| match opcode(opcode::DEF_ELSE_OP).parse(input, context) {
-        Err((x, y, Propagate::Err(AmlError::UnexpectedEndOfStream))) => {
-            Err((x, y, Propagate::Err(AmlError::WrongParser)))
-        }
-        r => r,
-    };
-
-    opcode(opcode::DEF_IF_ELSE_OP)
-        .then(comment_scope(
-            DebugVerbosity::Scopes,
-            "DefIfElse",
-            pkg_length()
-                .then(term_arg())
-                .feed(|(length, predicate_arg)| {
-                    take_to_end_of_pkglength(length).map_with_context(move |then_branch, context| {
-                        match predicate_arg.as_bool(context) {
-                            Ok(pred_val) => (Ok((pred_val, then_branch)), context),
-                            Err(e) => (Err(Propagate::Err(e)), context),
-                        }
-                    })
-                })
-                .then(choice!(
-                    maybe_else_opcode
-                        .then(comment_scope(
-                            DebugVerbosity::AllScopes,
-                            "DefElse",
-                            pkg_length().feed(take_to_end_of_pkglength),
-                        ))
-                        .map(|((), else_branch): ((), &[u8])| Ok(else_branch)),
-                    // TODO: can this be `id().map(&[])`?
-                    |input, context| -> ParseResult<'a, 'c, &[u8]> {
-                        /*
-                         * This path parses an DefIfElse that doesn't have an else branch. We simply
-                         * return an empty slice, so if the predicate is false, we don't execute
-                         * anything.
-                         */
-                        Ok((input, context, &[]))
-                    }
-                ))
-                .map_with_context(|((predicate, then_branch), else_branch), context| {
-                    let branch = if predicate { then_branch } else { else_branch };
-
-                    match term_list(PkgLength::from_raw_length(branch, branch.len() as u32).unwrap())
-                        .parse(branch, context)
-                    {
-                        Ok((_, context, result)) => (Ok(result), context),
-                        Err((_, context, err)) => (Err(err), context),
-                    }
-                }),
-        ))
-        .discard_result()
-}
-
-fn def_noop<'a, 'c>() -> impl Parser<'a, 'c, ()>
-where
-    'c: 'a,
-{
-    /*
-     * DefNoop := 0xa3
-     */
-    opcode(opcode::DEF_NOOP_OP).then(comment_scope(DebugVerbosity::AllScopes, "DefNoop", id())).discard_result()
-}
-
-fn def_return<'a, 'c>() -> impl Parser<'a, 'c, ()>
-where
-    'c: 'a,
-{
-    /*
-     * DefReturn := 0xa4 ArgObject
-     * ArgObject := TermArg => DataRefObject
-     */
-    opcode(opcode::DEF_RETURN_OP)
-        .then(comment_scope(
-            DebugVerbosity::Scopes,
-            "DefReturn",
-            term_arg().map(|return_arg| -> Result<(), Propagate> {
-                /*
-                 * To return a value, we want to halt execution of the method and propagate the
-                 * return value all the way up to the start of the method invocation. To do this,
-                 * we emit a special error that is intercepted during method invocation and turned
-                 * into a valid result.
-                 */
-                Err(Propagate::Return(return_arg))
-            }),
-        ))
-        .discard_result()
-}
-
-fn def_sleep<'a, 'c>() -> impl Parser<'a, 'c, ()>
-where
-    'c: 'a,
-{
-    /*
-     * DefSleep := ExtOpPrefix 0x22 MSecTime
-     * MSecTime := TermArg => Integer
-     */
-    ext_opcode(opcode::EXT_DEF_SLEEP_OP)
-        .then(comment_scope(
-            DebugVerbosity::Scopes,
-            "DefSleep",
-            term_arg().map_with_context(|milliseconds, context| {
-                let milliseconds = try_with_context!(context, milliseconds.as_integer(context));
-                context.handler.sleep(milliseconds);
-                (Ok(()), context)
-            }),
-        ))
-        .discard_result()
-}
-
-fn def_stall<'a, 'c>() -> impl Parser<'a, 'c, ()>
-where
-    'c: 'a,
-{
-    /*
-     * DefStall := ExtOpPrefix 0x21 USecTime
-     * USecTime := TermArg => Integer
-     */
-    ext_opcode(opcode::EXT_DEF_STALL_OP)
-        .then(comment_scope(
-            DebugVerbosity::Scopes,
-            "DefStall",
-            term_arg().map_with_context(|microseconds, context| {
-                let microseconds = try_with_context!(context, microseconds.as_integer(context));
-                context.handler.stall(microseconds);
-                (Ok(()), context)
-            }),
-        ))
-        .discard_result()
-}
-
-fn def_while<'a, 'c>() -> impl Parser<'a, 'c, ()>
-where
-    'c: 'a,
-{
-    /*
-     * DefWhile := 0xa2 PkgLength Predicate TermList
-     * Predicate := TermArg => Integer (0 = false, >0 = true)
-     *
-     * Parsing this does something a little unusual - it 'extracts' the predicate when it's first parsed, which
-     * allows us to reevaluate it to see if we should break out of the while yet. This is required, to make sure
-     * we're observing changes to the context between the iterations of the loop.
-     */
-    opcode(opcode::DEF_WHILE_OP)
-        .then(comment_scope(
-            DebugVerbosity::Scopes,
-            "DefWhile",
-            pkg_length()
-                .then(extract(term_arg()))
-                .feed(move |(length, (first_predicate, predicate_stream))| {
-                    take_to_end_of_pkglength(length)
-                        .map(move |body| Ok((first_predicate.clone(), predicate_stream, body)))
-                })
-                .map_with_context(|(first_predicate, predicate_stream, body), mut context| {
-                    if !try_with_context!(context, first_predicate.as_bool(context)) {
-                        return (Ok(()), context);
-                    }
-
-                    loop {
-                        match term_list(PkgLength::from_raw_length(body, body.len() as u32).unwrap())
-                            .parse(body, context)
-                        {
-                            Ok((_, new_context, _result)) => {
-                                context = new_context;
-                            }
-                            Err((_, new_context, Propagate::Break)) => {
-                                context = new_context;
-                                break;
-                            }
-                            Err((_, new_context, Propagate::Continue)) => {
-                                // We don't need to do anything special here - the `Propagate::Continue` bubbles
-                                // up, and then we can just move on to checking the predicate for the next
-                                // iteration.
-                                context = new_context;
-                            }
-                            Err((_, context, err)) => return (Err(err), context),
-                        }
-
-                        // Reevaluate the predicate to see if we should break out of the loop yet
-                        let predicate =
-                            match comment_scope(DebugVerbosity::AllScopes, "WhilePredicate", term_arg())
-                                .parse(predicate_stream, context)
-                            {
-                                Ok((_, new_context, result)) => {
-                                    context = new_context;
-                                    try_with_context!(context, result.as_bool(context))
-                                }
-                                Err((_, context, err)) => return (Err(err), context),
-                            };
-
-                        if !predicate {
-                            break;
-                        }
-                    }
-
-                    (Ok(()), context)
-                }),
-        ))
-        .discard_result()
-}
diff --git a/aml/src/term_object.rs b/aml/src/term_object.rs
deleted file mode 100644
index 4ddb14f0..00000000
--- a/aml/src/term_object.rs
+++ /dev/null
@@ -1,1094 +0,0 @@
-use crate::{
-    expression::{def_buffer, def_package, expression_opcode},
-    misc::{arg_obj, local_obj},
-    name_object::{name_seg, name_string, target, Target},
-    namespace::{AmlName, LevelType},
-    opcode::{self, ext_opcode, opcode},
-    opregion::{OpRegion, RegionSpace},
-    parser::{
-        choice,
-        comment_scope,
-        take,
-        take_to_end_of_pkglength,
-        take_u16,
-        take_u32,
-        take_u64,
-        try_with_context,
-        ParseResult,
-        Parser,
-        Propagate,
-    },
-    pkg_length::{pkg_length, region_pkg_length, PkgLength},
-    statement::statement_opcode,
-    value::{AmlValue, FieldFlags, MethodCode, MethodFlags},
-    AmlContext,
-    AmlError,
-    AmlHandle,
-    DebugVerbosity,
-};
-use alloc::{string::String, sync::Arc, vec::Vec};
-use core::str;
-
-/// `TermList`s are usually found within explicit-length objects (so they have a `PkgLength`
-/// elsewhere in the structure), so this takes a number of bytes to parse.
-pub fn term_list<'a, 'c>(list_length: PkgLength) -> impl Parser<'a, 'c, ()>
-where
-    'c: 'a,
-{
-    /*
-     * TermList := Nothing | <TermObj TermList>
-     */
-    // TODO: why does this use still_parsing, instead of just taking the whole thing and parsing it til it's empty?
-    move |mut input: &'a [u8], mut context: &'c mut AmlContext| {
-        while list_length.still_parsing(input) {
-            // TODO: currently, we ignore the value of the expression. We may need to propagate
-            // this.
-            let (new_input, new_context, _) = term_object().parse(input, context)?;
-            input = new_input;
-            context = new_context;
-        }
-
-        Ok((input, context, ()))
-    }
-}
-
-pub fn term_object<'a, 'c>() -> impl Parser<'a, 'c, Option<AmlValue>>
-where
-    'c: 'a,
-{
-    /*
-     * TermObj := NamespaceModifierObj | NamedObj | StatementOpcode | ExpressionOpcode
-     */
-    comment_scope(
-        DebugVerbosity::AllScopes,
-        "TermObj",
-        choice!(
-            namespace_modifier().map(|()| Ok(None)),
-            named_obj().map(|()| Ok(None)),
-            statement_opcode().map(|()| Ok(None)),
-            expression_opcode().map(|value| Ok(Some(value)))
-        ),
-    )
-}
-
-pub fn namespace_modifier<'a, 'c>() -> impl Parser<'a, 'c, ()>
-where
-    'c: 'a,
-{
-    /*
-     * NamespaceModifierObj := DefAlias | DefName | DefScope
-     */
-    choice!(def_alias(), def_name(), def_scope())
-}
-
-pub fn named_obj<'a, 'c>() -> impl Parser<'a, 'c, ()>
-where
-    'c: 'a,
-{
-    /*
-     * NamedObj := DefBankField | DefCreateBitField | DefCreateByteField | DefCreateWordField | DefCreateDWordField |
-     *             DefCreateQWordField | DefCreateField | DefDataRegion | DefExternal | DefOpRegion | DefPowerRes |
-     *             DefProcessor | DefThermalZone | DefMethod | DefMutex
-     *
-     * XXX: DefMethod and DefMutex (at least) are not included in any rule in the AML grammar,
-     * but are defined in the NamedObj section so we assume they're part of NamedObj
-     */
-    comment_scope(
-        DebugVerbosity::AllScopes,
-        "NamedObj",
-        choice!(
-            def_create_bit_field(),
-            def_create_byte_field(),
-            def_create_word_field(),
-            def_create_dword_field(),
-            def_create_qword_field(),
-            def_create_field(),
-            def_op_region(),
-            def_field(),
-            def_method(),
-            def_external(),
-            def_device(),
-            def_processor(),
-            def_power_res(),
-            def_thermal_zone(),
-            def_mutex()
-        ),
-    )
-}
-
-pub fn def_name<'a, 'c>() -> impl Parser<'a, 'c, ()>
-where
-    'c: 'a,
-{
-    /*
-     * DefName := 0x08 NameString DataRefObject
-     */
-    opcode(opcode::DEF_NAME_OP)
-        .then(comment_scope(
-            DebugVerbosity::Scopes,
-            "DefName",
-            name_string().then(data_ref_object()).map_with_context(|(name, data_ref_object), context| {
-                try_with_context!(
-                    context,
-                    context.namespace.add_value_at_resolved_path(name, &context.current_scope, data_ref_object)
-                );
-                (Ok(()), context)
-            }),
-        ))
-        .discard_result()
-}
-
-pub fn def_alias<'a, 'c>() -> impl Parser<'a, 'c, ()>
-where
-    'c: 'a,
-{
-    /*
-     * DefAlias := 0x06 NameString NameString
-     * The second name refers to the same object as the first
-     */
-    opcode(opcode::DEF_ALIAS_OP)
-        .then(comment_scope(
-            DebugVerbosity::Scopes,
-            "DefAlias",
-            name_string().then(name_string()).map_with_context(|(target, alias), context| {
-                try_with_context!(
-                    context,
-                    context.namespace.add_alias_at_resolved_path(alias, &context.current_scope, target)
-                );
-                (Ok(()), context)
-            }),
-        ))
-        .discard_result()
-}
-
-pub fn def_scope<'a, 'c>() -> impl Parser<'a, 'c, ()>
-where
-    'c: 'a,
-{
-    /*
-     * DefScope := 0x10 PkgLength NameString TermList
-     */
-    opcode(opcode::DEF_SCOPE_OP)
-        .then(comment_scope(
-            DebugVerbosity::Scopes,
-            "DefScope",
-            pkg_length()
-                .then(name_string())
-                .map_with_context(|(length, name), context| {
-                    let previous_scope = context.current_scope.clone();
-                    context.current_scope = try_with_context!(context, name.resolve(&context.current_scope));
-
-                    context.comment(
-                        DebugVerbosity::Scopes,
-                        &(String::from("Scope name: ") + &context.current_scope.as_string()),
-                    );
-
-                    try_with_context!(
-                        context,
-                        context.namespace.add_level(context.current_scope.clone(), LevelType::Scope)
-                    );
-
-                    (Ok((length, previous_scope)), context)
-                })
-                .feed(|(pkg_length, previous_scope)| {
-                    term_list(pkg_length).map(move |_| Ok(previous_scope.clone()))
-                })
-                .map_with_context(|previous_scope, context| {
-                    context.current_scope = previous_scope;
-                    (Ok(()), context)
-                }),
-        ))
-        .discard_result()
-}
-
-pub fn def_create_bit_field<'a, 'c>() -> impl Parser<'a, 'c, ()>
-where
-    'c: 'a,
-{
-    /*
-     * DefCreateBitField := 0x8d SourceBuf BitIndex NameString
-     * SourceBuf := TermArg => Buffer
-     * BitIndex := TermArg => Integer
-     */
-    opcode(opcode::DEF_CREATE_BIT_FIELD_OP)
-        .then(comment_scope(
-            DebugVerbosity::AllScopes,
-            "DefCreateBitField",
-            term_arg().then(term_arg()).then(name_string()).map_with_context(
-                |((source, index), name), context| {
-                    let source_data: Arc<spinning_top::Spinlock<Vec<u8>>> =
-                        try_with_context!(context, source.as_buffer(context)).clone();
-                    let index = try_with_context!(context, index.as_integer(context));
-
-                    try_with_context!(
-                        context,
-                        context.namespace.add_value_at_resolved_path(
-                            name,
-                            &context.current_scope,
-                            AmlValue::BufferField { buffer_data: source_data, offset: index, length: 1 }
-                        )
-                    );
-
-                    (Ok(()), context)
-                },
-            ),
-        ))
-        .discard_result()
-}
-
-pub fn def_create_byte_field<'a, 'c>() -> impl Parser<'a, 'c, ()>
-where
-    'c: 'a,
-{
-    /*
-     * DefCreateByteField := 0x8c SourceBuf ByteIndex NameString
-     * SourceBuf := TermArg => Buffer
-     * ByteIndex := TermArg => Integer
-     */
-    opcode(opcode::DEF_CREATE_BYTE_FIELD_OP)
-        .then(comment_scope(
-            DebugVerbosity::AllScopes,
-            "DefCreateByteField",
-            term_arg().then(term_arg()).then(name_string()).map_with_context(
-                |((source, index), name), context| {
-                    let source_data: Arc<spinning_top::Spinlock<Vec<u8>>> =
-                        try_with_context!(context, source.as_buffer(context)).clone();
-                    let index = try_with_context!(context, index.as_integer(context));
-
-                    try_with_context!(
-                        context,
-                        context.namespace.add_value_at_resolved_path(
-                            name,
-                            &context.current_scope,
-                            AmlValue::BufferField { buffer_data: source_data, offset: index * 8, length: 8 }
-                        )
-                    );
-
-                    (Ok(()), context)
-                },
-            ),
-        ))
-        .discard_result()
-}
-
-pub fn def_create_word_field<'a, 'c>() -> impl Parser<'a, 'c, ()>
-where
-    'c: 'a,
-{
-    /*
-     * DefCreateWordField := 0x8b SourceBuf ByteIndex NameString
-     * SourceBuf := TermArg => Buffer
-     * ByteIndex := TermArg => Integer
-     */
-    opcode(opcode::DEF_CREATE_WORD_FIELD_OP)
-        .then(comment_scope(
-            DebugVerbosity::AllScopes,
-            "DefCreateWordField",
-            term_arg().then(term_arg()).then(name_string()).map_with_context(
-                |((source, index), name), context| {
-                    let source_data: Arc<spinning_top::Spinlock<Vec<u8>>> =
-                        try_with_context!(context, source.as_buffer(context)).clone();
-                    let index = try_with_context!(context, index.as_integer(context));
-
-                    try_with_context!(
-                        context,
-                        context.namespace.add_value_at_resolved_path(
-                            name,
-                            &context.current_scope,
-                            AmlValue::BufferField { buffer_data: source_data, offset: index * 8, length: 16 }
-                        )
-                    );
-
-                    (Ok(()), context)
-                },
-            ),
-        ))
-        .discard_result()
-}
-
-pub fn def_create_dword_field<'a, 'c>() -> impl Parser<'a, 'c, ()>
-where
-    'c: 'a,
-{
-    /*
-     * DefCreateDWordField := 0x8a SourceBuf ByteIndex NameString
-     * SourceBuf := TermArg => Buffer
-     * ByteIndex := TermArg => Integer
-     */
-    opcode(opcode::DEF_CREATE_DWORD_FIELD_OP)
-        .then(comment_scope(
-            DebugVerbosity::AllScopes,
-            "DefCreateDWordField",
-            term_arg().then(term_arg()).then(name_string()).map_with_context(
-                |((source, index), name), context| {
-                    let source_data: Arc<spinning_top::Spinlock<Vec<u8>>> =
-                        try_with_context!(context, source.as_buffer(context)).clone();
-                    let index = try_with_context!(context, index.as_integer(context));
-
-                    try_with_context!(
-                        context,
-                        context.namespace.add_value_at_resolved_path(
-                            name,
-                            &context.current_scope,
-                            AmlValue::BufferField { buffer_data: source_data, offset: index * 8, length: 32 }
-                        )
-                    );
-
-                    (Ok(()), context)
-                },
-            ),
-        ))
-        .discard_result()
-}
-
-pub fn def_create_qword_field<'a, 'c>() -> impl Parser<'a, 'c, ()>
-where
-    'c: 'a,
-{
-    /*
-     * DefCreateQWordField := 0x8f SourceBuf ByteIndex NameString
-     * SourceBuf := TermArg => Buffer
-     * ByteIndex := TermArg => Integer
-     */
-    opcode(opcode::DEF_CREATE_QWORD_FIELD_OP)
-        .then(comment_scope(
-            DebugVerbosity::AllScopes,
-            "DefCreateQWordField",
-            term_arg().then(term_arg()).then(name_string()).map_with_context(
-                |((source, index), name), context| {
-                    let source_data: Arc<spinning_top::Spinlock<Vec<u8>>> =
-                        try_with_context!(context, source.as_buffer(context)).clone();
-                    let index = try_with_context!(context, index.as_integer(context));
-
-                    try_with_context!(
-                        context,
-                        context.namespace.add_value_at_resolved_path(
-                            name,
-                            &context.current_scope,
-                            AmlValue::BufferField { buffer_data: source_data, offset: index * 8, length: 64 }
-                        )
-                    );
-
-                    (Ok(()), context)
-                },
-            ),
-        ))
-        .discard_result()
-}
-
-pub fn def_create_field<'a, 'c>() -> impl Parser<'a, 'c, ()>
-where
-    'c: 'a,
-{
-    /*
-     * DefCreateField := ExtOpPrefix 0x13 SourceBuf BitIndex NumBits NameString
-     * SourceBuf := TermArg => Buffer
-     * BitIndex := TermArg => Integer
-     * NumBits := TermArg => Integer
-     */
-    ext_opcode(opcode::EXT_DEF_CREATE_FIELD_OP)
-        .then(comment_scope(
-            DebugVerbosity::Scopes,
-            "DefCreateField",
-            term_arg().then(term_arg()).then(term_arg()).then(name_string()).map_with_context(
-                |(((source, index), num_bits), name), context| {
-                    let source_data: Arc<spinning_top::Spinlock<Vec<u8>>> =
-                        try_with_context!(context, source.as_buffer(context)).clone();
-                    let index = try_with_context!(context, index.as_integer(context));
-                    let num_bits = try_with_context!(context, num_bits.as_integer(context));
-
-                    try_with_context!(
-                        context,
-                        context.namespace.add_value_at_resolved_path(
-                            name,
-                            &context.current_scope,
-                            AmlValue::BufferField { buffer_data: source_data, offset: index, length: num_bits }
-                        )
-                    );
-
-                    (Ok(()), context)
-                },
-            ),
-        ))
-        .discard_result()
-}
-
-pub fn def_op_region<'a, 'c>() -> impl Parser<'a, 'c, ()>
-where
-    'c: 'a,
-{
-    /*
-     * DefOpRegion := ExtOpPrefix 0x80 NameString RegionSpace RegionOffset RegionLen
-     * RegionSpace := ByteData (where 0x00      = SystemMemory
-     *                                0x01      = SystemIO
-     *                                0x02      = PciConfig
-     *                                0x03      = EmbeddedControl
-     *                                0x04      = SMBus
-     *                                0x05      = SystemCMOS
-     *                                0x06      = PciBarTarget
-     *                                0x07      = IPMI
-     *                                0x08      = GeneralPurposeIO
-     *                                0x09      = GenericSerialBus
-     *                                0x80-0xff = OEM Defined)
-     * ByteData := 0x00 - 0xff
-     * RegionOffset := TermArg => Integer
-     * RegionLen := TermArg => Integer
-     */
-    ext_opcode(opcode::EXT_DEF_OP_REGION_OP)
-        .then(comment_scope(
-            DebugVerbosity::Scopes,
-            "DefOpRegion",
-            name_string().then(take()).then(term_arg()).then(term_arg()).map_with_context(
-                |(((name, space), offset), length), context| {
-                    let region = match space {
-                        0x00 => RegionSpace::SystemMemory,
-                        0x01 => RegionSpace::SystemIo,
-                        0x02 => RegionSpace::PciConfig,
-                        0x03 => RegionSpace::EmbeddedControl,
-                        0x04 => RegionSpace::SMBus,
-                        0x05 => RegionSpace::SystemCmos,
-                        0x06 => RegionSpace::PciBarTarget,
-                        0x07 => RegionSpace::IPMI,
-                        0x08 => RegionSpace::GeneralPurposeIo,
-                        0x09 => RegionSpace::GenericSerialBus,
-                        space @ 0x80..=0xff => RegionSpace::OemDefined(space),
-                        byte => return (Err(Propagate::Err(AmlError::InvalidRegionSpace(byte))), context),
-                    };
-                    let offset = match offset.as_integer(context) {
-                        Ok(offset) => offset,
-                        Err(err) => return (Err(Propagate::Err(err)), context),
-                    };
-                    let length = match length.as_integer(context) {
-                        Ok(length) => length,
-                        Err(err) => return (Err(Propagate::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(OpRegion::new(region, offset, length, parent_device))
-                        )
-                    );
-                    (Ok(()), context)
-                },
-            ),
-        ))
-        .discard_result()
-}
-
-pub fn def_field<'a, 'c>() -> impl Parser<'a, 'c, ()>
-where
-    'c: 'a,
-{
-    /*
-     * 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(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>
-                     */
-                    // TODO: can this pattern be expressed as a combinator
-                    let mut current_offset = 0;
-                    while list_length.still_parsing(input) {
-                        let (new_input, new_context, field_length) =
-                            field_element(region_handle, FieldFlags::new(flags), current_offset)
-                                .parse(input, context)?;
-                        input = new_input;
-                        context = new_context;
-                        current_offset += field_length;
-                    }
-
-                    Ok((input, context, ()))
-                }
-            }),
-        ))
-        .discard_result()
-}
-
-/// 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_handle: AmlHandle,
-    flags: FieldFlags,
-    current_offset: u64,
-) -> impl Parser<'a, 'c, u64>
-where
-    'c: 'a,
-{
-    /*
-     * FieldElement := NamedField | ReservedField | AccessField | ExtendedAccessField |
-     *                 ConnectField
-     * NamedField := NameSeg PkgLength
-     * ReservedField := 0x00 PkgLength
-     * AccessField := 0x01 AccessType AccessAttrib
-     * ConnectField := <0x02 NameString> | <0x02 BufferData>
-     * ExtendedAccessField := 0x03 AccessType ExtendedAccessAttrib AccessLength
-     *
-     * AccessType := ByteData
-     * AccessAttrib := ByteData
-     *
-     * XXX: The spec says a ConnectField can be <0x02 BufferData>, but BufferData isn't an AML
-     * object (it seems to be defined in ASL). We treat BufferData as if it was encoded like
-     * DefBuffer, and this seems to work so far.
-     */
-    // TODO: parse ConnectField and ExtendedAccessField
-
-    /*
-     * Reserved fields shouldn't actually be added to the namespace; they seem to show gaps in
-     * the operation region that aren't used for anything.
-     */
-    let reserved_field = opcode(opcode::RESERVED_FIELD)
-        .then(region_pkg_length(region_handle))
-        .map(|((), length)| Ok(length.raw_length as u64));
-
-    // TODO: work out what to do with an access field
-    // let access_field = opcode(opcode::ACCESS_FIELD)
-    //     .then(take())
-    //     .then(take())
-    //     .map_with_context(|(((), access_type), access_attrib), context| (Ok(    , context));
-
-    // TODO: fields' start and end offsets need to be checked against their enclosing
-    //       OperationRegions to make sure they don't sit outside or cross the boundary.
-    //       This might not be a problem if a sane ASL compiler is used (which should check this
-    //       at compile-time), but it's better to be safe and validate that as well.
-
-    let named_field =
-        name_seg().then(region_pkg_length(region_handle)).map_with_context(move |(name_seg, length), context| {
-            try_with_context!(
-                context,
-                context.namespace.add_value_at_resolved_path(
-                    AmlName::from_name_seg(name_seg),
-                    &context.current_scope,
-                    AmlValue::Field {
-                        region: region_handle,
-                        flags,
-                        offset: current_offset,
-                        length: length.raw_length as u64,
-                    },
-                )
-            );
-
-            (Ok(length.raw_length as u64), context)
-        });
-
-    choice!(reserved_field, named_field)
-}
-
-pub fn def_method<'a, 'c>() -> impl Parser<'a, 'c, ()>
-where
-    'c: 'a,
-{
-    /*
-     * DefMethod := 0x14 PkgLength NameString MethodFlags TermList
-     * MethodFlags := ByteData (where bits 0-2: ArgCount (0 to 7)
-     *                                bit 3: SerializeFlag (0 = Not Serialized, 1 = Serialized)
-     *                                bits 4-7: SyncLevel (0x00 to 0x0f))
-     */
-    opcode(opcode::DEF_METHOD_OP)
-        .then(comment_scope(
-            DebugVerbosity::Scopes,
-            "DefMethod",
-            pkg_length()
-                .then(name_string())
-                .then(take())
-                .feed(|((length, name), flags)| {
-                    take_to_end_of_pkglength(length).map(move |code| Ok((name.clone(), flags, code)))
-                })
-                .map_with_context(|(name, flags, code), context| {
-                    try_with_context!(
-                        context,
-                        context.namespace.add_value_at_resolved_path(
-                            name,
-                            &context.current_scope,
-                            AmlValue::Method {
-                                flags: MethodFlags::from(flags),
-                                code: MethodCode::Aml(code.to_vec())
-                            },
-                        )
-                    );
-                    (Ok(()), context)
-                }),
-        ))
-        .discard_result()
-}
-
-pub fn def_external<'a, 'c>() -> impl Parser<'a, 'c, ()>
-where
-    'c: 'a,
-{
-    /*
-     * DefExternal = 0x15 NameString ObjectType ArgumentCount
-     * ObjectType := ByteData
-     * ArgumentCount := ByteData (0 to 7)
-     */
-    opcode(opcode::DEF_EXTERNAL_OP)
-        .then(comment_scope(DebugVerbosity::Scopes, "DefExternal", name_string().then(take()).then(take())))
-        .discard_result()
-}
-
-pub fn def_device<'a, 'c>() -> impl Parser<'a, 'c, ()>
-where
-    'c: 'a,
-{
-    /*
-     * DefDevice := ExtOpPrefix 0x82 PkgLength NameString TermList
-     */
-    ext_opcode(opcode::EXT_DEF_DEVICE_OP)
-        .then(comment_scope(
-            DebugVerbosity::Scopes,
-            "DefDevice",
-            pkg_length()
-                .then(name_string())
-                .map_with_context(|(length, name), context| {
-                    let resolved_name = try_with_context!(context, name.resolve(&context.current_scope));
-                    try_with_context!(
-                        context,
-                        context.namespace.add_value(resolved_name.clone(), AmlValue::Device)
-                    );
-                    try_with_context!(
-                        context,
-                        context.namespace.add_level(resolved_name.clone(), LevelType::Device)
-                    );
-
-                    let previous_scope = context.current_scope.clone();
-                    context.current_scope = resolved_name;
-
-                    (Ok((length, previous_scope)), context)
-                })
-                .feed(|(length, previous_scope)| term_list(length).map(move |_| Ok(previous_scope.clone())))
-                .map_with_context(|previous_scope, context| {
-                    context.current_scope = previous_scope;
-                    (Ok(()), context)
-                }),
-        ))
-        .discard_result()
-}
-
-pub fn def_processor<'a, 'c>() -> impl Parser<'a, 'c, ()>
-where
-    'c: 'a,
-{
-    /*
-     * DefProcessor := ExtOpPrefix 0x83 PkgLength NameString ProcID PblkAddress PblkLen TermList
-     * ProcID := ByteData
-     * PblkAddress := DWordData
-     * PblkLen := ByteData
-     */
-    ext_opcode(opcode::EXT_DEF_PROCESSOR_OP)
-        .then(comment_scope(
-            DebugVerbosity::Scopes,
-            "DefProcessor",
-            pkg_length()
-                .then(name_string())
-                .then(take())
-                .then(take_u32())
-                .then(take())
-                .map_with_context(|((((pkg_length, name), proc_id), pblk_address), pblk_len), context| {
-                    /*
-                     * Legacy `Processor` objects contain data within themselves, and can also have sub-objects,
-                     * so we add both a level for the sub-objects, and a value for the data.
-                     */
-                    let resolved_name = try_with_context!(context, name.resolve(&context.current_scope));
-                    try_with_context!(
-                        context,
-                        context.namespace.add_level(resolved_name.clone(), LevelType::Processor)
-                    );
-                    try_with_context!(
-                        context,
-                        context.namespace.add_value(
-                            resolved_name.clone(),
-                            AmlValue::Processor { id: proc_id, pblk_address, pblk_len }
-                        )
-                    );
-                    let previous_scope = context.current_scope.clone();
-                    context.current_scope = resolved_name;
-
-                    (Ok((previous_scope, pkg_length)), context)
-                })
-                .feed(move |(previous_scope, pkg_length)| {
-                    term_list(pkg_length).map(move |_| Ok(previous_scope.clone()))
-                })
-                .map_with_context(|previous_scope, context| {
-                    context.current_scope = previous_scope;
-                    (Ok(()), context)
-                }),
-        ))
-        .discard_result()
-}
-
-pub fn def_power_res<'a, 'c>() -> impl Parser<'a, 'c, ()>
-where
-    'c: 'a,
-{
-    /*
-     * DefPowerRes := ExtOpPrefix 0x84 PkgLength NameString SystemLevel ResourceOrder TermList
-     * SystemLevel := ByteData
-     * ResourceOrder := WordData
-     */
-    ext_opcode(opcode::EXT_DEF_POWER_RES_OP)
-        .then(comment_scope(
-            DebugVerbosity::Scopes,
-            "DefPowerRes",
-            pkg_length()
-                .then(name_string())
-                .then(take())
-                .then(take_u16())
-                .map_with_context(|(((pkg_length, name), system_level), resource_order), context| {
-                    /*
-                     * `PowerResource` objects contain data within themselves, and can also have sub-objects,
-                     * so we add both a level for the sub-objects, and a value for the data.
-                     */
-                    let resolved_name = try_with_context!(context, name.resolve(&context.current_scope));
-                    try_with_context!(
-                        context,
-                        context.namespace.add_level(resolved_name.clone(), LevelType::PowerResource)
-                    );
-                    try_with_context!(
-                        context,
-                        context.namespace.add_value(
-                            resolved_name.clone(),
-                            AmlValue::PowerResource { system_level, resource_order }
-                        )
-                    );
-                    let previous_scope = context.current_scope.clone();
-                    context.current_scope = resolved_name;
-
-                    (Ok((previous_scope, pkg_length)), context)
-                })
-                .feed(move |(previous_scope, pkg_length)| {
-                    term_list(pkg_length).map(move |_| Ok(previous_scope.clone()))
-                })
-                .map_with_context(|previous_scope, context| {
-                    context.current_scope = previous_scope;
-                    (Ok(()), context)
-                }),
-        ))
-        .discard_result()
-}
-
-pub fn def_thermal_zone<'a, 'c>() -> impl Parser<'a, 'c, ()>
-where
-    'c: 'a,
-{
-    /*
-     * DefThermalZone := ExtOpPrefix 0x85 PkgLength NameString TermList
-     * TODO: we use this pattern a lot (move into scope, parse a term_list, move back out). Could we simplify into
-     * just a `feed` by passing a scope into term_list?
-     */
-    ext_opcode(opcode::EXT_DEF_THERMAL_ZONE_OP)
-        .then(comment_scope(
-            DebugVerbosity::Scopes,
-            "DefThermalZone",
-            pkg_length()
-                .then(name_string())
-                .map_with_context(|(pkg_length, name), context| {
-                    let resolved_name = try_with_context!(context, name.resolve(&context.current_scope));
-                    try_with_context!(
-                        context,
-                        context.namespace.add_value(resolved_name.clone(), AmlValue::ThermalZone)
-                    );
-                    try_with_context!(
-                        context,
-                        context.namespace.add_level(resolved_name.clone(), LevelType::ThermalZone)
-                    );
-
-                    let previous_scope = context.current_scope.clone();
-                    context.current_scope = resolved_name;
-
-                    (Ok((pkg_length, previous_scope)), context)
-                })
-                .feed(|(length, previous_scope)| term_list(length).map(move |_| Ok(previous_scope.clone())))
-                .map_with_context(|previous_scope, context| {
-                    context.current_scope = previous_scope;
-                    (Ok(()), context)
-                }),
-        ))
-        .discard_result()
-}
-
-pub fn def_mutex<'a, 'c>() -> impl Parser<'a, 'c, ()>
-where
-    'c: 'a,
-{
-    /*
-     * DefMutex := ExtOpPrefix 0x01 NameString SyncFlags
-     * SyncFlags := ByteData (where bits 0-3: SyncLevel
-     *                              bits 4-7: Reserved)
-     */
-    ext_opcode(opcode::EXT_DEF_MUTEX_OP)
-        .then(comment_scope(
-            DebugVerbosity::Scopes,
-            "DefMutex",
-            name_string().then(take()).map_with_context(|(name, sync_level), context| {
-                try_with_context!(
-                    context,
-                    context.namespace.add_value_at_resolved_path(
-                        name,
-                        &context.current_scope,
-                        AmlValue::Mutex { sync_level }
-                    )
-                );
-                (Ok(()), context)
-            }),
-        ))
-        .discard_result()
-}
-
-pub fn def_cond_ref_of<'a, 'c>() -> impl Parser<'a, 'c, AmlValue>
-where
-    'c: 'a,
-{
-    /*
-     * DefCondRefOf := ExtOpPrefix 0x12 NameString Target => boolean
-     */
-    ext_opcode(opcode::EXT_DEF_COND_REF_OF_OP)
-        .then(comment_scope(
-            DebugVerbosity::Scopes,
-            "DefCondRefOf",
-            name_string().then(target()).map_with_context(|(source, target), context| {
-                let handle = context.namespace.search(&source, &context.current_scope);
-                let result = AmlValue::Boolean(handle.is_ok());
-                if let Ok((_name, _handle)) = handle {
-                    match target {
-                        Target::Null => { /* just return the result of the check */ }
-                        _ => return (Err(Propagate::Err(AmlError::Unimplemented)), context),
-                    }
-                }
-                (Ok(result), context)
-            }),
-        ))
-        .map(|((), result)| Ok(result))
-}
-
-pub fn term_arg<'a, 'c>() -> impl Parser<'a, 'c, AmlValue>
-where
-    'c: 'a,
-{
-    /*
-     * TermArg := ExpressionOpcode | DataObject | ArgObj | LocalObj
-     */
-    comment_scope(
-        DebugVerbosity::AllScopes,
-        "TermArg",
-        choice!(
-            data_object(),
-            arg_obj().map_with_context(|arg_num, context| {
-                (Ok(try_with_context!(context, context.current_arg(arg_num)).clone()), context)
-            }),
-            local_obj().map_with_context(|local_num, context| {
-                (Ok(try_with_context!(context, context.local(local_num)).clone()), context)
-            }),
-            expression_opcode()
-        ),
-    )
-}
-
-pub fn data_ref_object<'a, 'c>() -> impl Parser<'a, 'c, AmlValue>
-where
-    'c: 'a,
-{
-    /*
-     * DataRefObject := DataObject | ObjectReference | DDBHandle
-     */
-    comment_scope(DebugVerbosity::AllScopes, "DataRefObject", choice!(data_object()))
-}
-
-pub fn data_object<'a, 'c>() -> impl Parser<'a, 'c, AmlValue>
-where
-    'c: 'a,
-{
-    /*
-     * DataObject := DefPackage | DefVarPackage | ComputationalData
-     *
-     * The order of the parsers are important here, as DefPackage and DefVarPackage can be
-     * accidently parsed as ComputationalDatas.
-     */
-    // TODO: this doesn't yet parse DefVarPackage
-    comment_scope(DebugVerbosity::AllScopes, "DataObject", choice!(def_package(), computational_data()))
-}
-
-pub fn computational_data<'a, 'c>() -> impl Parser<'a, 'c, AmlValue>
-where
-    'c: 'a,
-{
-    /*
-     * ComputationalData := ByteConst | WordConst | DWordConst | QWordConst | String |
-     *                      ConstObj | RevisionOp | DefBuffer
-     * ByteConst := 0x0a ByteData
-     * WordConst := 0x0b WordData
-     * DWordConst := 0x0c DWordData
-     * QWordConst := 0x0e QWordData
-     * String := 0x0d AsciiCharList NullChar
-     * ConstObj := ZeroOp(0x00) | OneOp(0x01) | OnesOp(0xff)
-     * RevisionOp := ExtOpPrefix(0x5b) 0x30
-     */
-    let const_parser = |input: &'a [u8], context: &'c mut AmlContext| {
-        let string_parser = |input: &'a [u8], context| -> ParseResult<'a, 'c, AmlValue> {
-            /*
-             * Using `position` isn't very efficient here, but is probably fine because the
-             * strings are usually quite short.
-             */
-            let nul_position = match input.iter().position(|&c| c == b'\0') {
-                Some(position) => position,
-                None => return Err((input, context, Propagate::Err(AmlError::UnterminatedStringConstant))),
-            };
-
-            let string = String::from(match str::from_utf8(&input[0..nul_position]) {
-                Ok(string) => string,
-                Err(_) => return Err((input, context, Propagate::Err(AmlError::InvalidStringConstant))),
-            });
-
-            Ok((&input[(nul_position + 1)..], context, AmlValue::String(string)))
-        };
-
-        let (new_input, context, op) = take().parse(input, context)?;
-        match op {
-            opcode::BYTE_CONST => {
-                take().map(|value| Ok(AmlValue::Integer(value as u64))).parse(new_input, context)
-            }
-            opcode::WORD_CONST => {
-                take_u16().map(|value| Ok(AmlValue::Integer(value as u64))).parse(new_input, context)
-            }
-            opcode::DWORD_CONST => {
-                take_u32().map(|value| Ok(AmlValue::Integer(value as u64))).parse(new_input, context)
-            }
-            opcode::QWORD_CONST => take_u64().map(|value| Ok(AmlValue::Integer(value))).parse(new_input, context),
-            opcode::STRING_PREFIX => string_parser.parse(new_input, context),
-            opcode::ZERO_OP => Ok((new_input, context, AmlValue::zero())),
-            opcode::ONE_OP => Ok((new_input, context, AmlValue::one())),
-            opcode::ONES_OP => Ok((new_input, context, AmlValue::ones())),
-
-            _ => Err((input, context, Propagate::Err(AmlError::WrongParser))),
-        }
-    };
-
-    comment_scope(
-        DebugVerbosity::AllScopes,
-        "ComputationalData",
-        choice!(
-            ext_opcode(opcode::EXT_REVISION_OP).map(|_| Ok(AmlValue::Integer(crate::AML_INTERPRETER_REVISION))),
-            const_parser,
-            def_buffer()
-        ),
-    )
-}
-
-#[cfg(test)]
-mod test {
-    use super::*;
-    use crate::test_utils::*;
-
-    #[test]
-    fn test_package() {
-        let mut context = make_test_context();
-
-        // The tests also check that DefPackage consumes no more input than required
-        // Empty DefPackage
-        check_ok_value!(
-            def_package().parse(&[0x12, 0x02, 0x00, 0x12, 0x34], &mut context),
-            AmlValue::Package(Vec::new()),
-            &[0x12, 0x34]
-        );
-
-        // DefPackage where NumElements == package_content.len()
-        check_ok_value!(
-            def_package()
-                .parse(&[0x12, 0x09, 0x04, 0x01, 0x0A, 0x02, 0x0A, 0x03, 0x0A, 0x04, 0x12, 0x34], &mut context),
-            AmlValue::Package(alloc::vec![
-                AmlValue::Integer(1),
-                AmlValue::Integer(2),
-                AmlValue::Integer(3),
-                AmlValue::Integer(4)
-            ]),
-            &[0x12, 0x34]
-        );
-
-        // DefPackage where NumElements > package_content.len()
-        check_ok_value!(
-            def_package().parse(&[0x012, 0x05, 0x04, 0x01, 0x0A, 0x02, 0x12, 0x34], &mut context),
-            AmlValue::Package(alloc::vec![
-                AmlValue::Integer(1),
-                AmlValue::Integer(2),
-                AmlValue::Uninitialized,
-                AmlValue::Uninitialized,
-            ]),
-            &[0x12, 0x34]
-        );
-    }
-
-    #[test]
-    fn test_computational_data() {
-        let mut context = make_test_context();
-        check_ok_value!(
-            computational_data().parse(&[0x00, 0x34, 0x12], &mut context),
-            AmlValue::Integer(0),
-            &[0x34, 0x12]
-        );
-        check_ok_value!(
-            computational_data().parse(&[0x01, 0x18, 0xf3], &mut context),
-            AmlValue::Integer(1),
-            &[0x18, 0xf3]
-        );
-        check_ok_value!(
-            computational_data().parse(&[0xff, 0x98, 0xc3], &mut context),
-            AmlValue::Integer(u64::MAX),
-            &[0x98, 0xc3]
-        );
-        check_ok_value!(
-            computational_data().parse(&[0x5b, 0x30], &mut context),
-            AmlValue::Integer(crate::AML_INTERPRETER_REVISION),
-            &[]
-        );
-        check_ok_value!(
-            computational_data().parse(&[0x0a, 0xf3, 0x35], &mut context),
-            AmlValue::Integer(0xf3),
-            &[0x35]
-        );
-        check_ok_value!(
-            computational_data().parse(&[0x0b, 0xf3, 0x35], &mut context),
-            AmlValue::Integer(0x35f3),
-            &[]
-        );
-        check_ok_value!(
-            computational_data().parse(&[0x0c, 0xf3, 0x35, 0x12, 0x65, 0xff, 0x00], &mut context),
-            AmlValue::Integer(0x651235f3),
-            &[0xff, 0x00]
-        );
-        check_ok_value!(
-            computational_data()
-                .parse(&[0x0e, 0xf3, 0x35, 0x12, 0x65, 0xff, 0x00, 0x67, 0xde, 0x28], &mut context),
-            AmlValue::Integer(0xde6700ff651235f3),
-            &[0x28]
-        );
-        check_ok_value!(
-            computational_data().parse(&[0x0d, b'A', b'B', b'C', b'D', b'\0', 0xff, 0xf5], &mut context),
-            AmlValue::String(String::from("ABCD")),
-            &[0xff, 0xf5]
-        );
-    }
-}
diff --git a/aml/src/test_utils.rs b/aml/src/test_utils.rs
deleted file mode 100644
index 0664ca44..00000000
--- a/aml/src/test_utils.rs
+++ /dev/null
@@ -1,209 +0,0 @@
-use crate::{parser::Propagate, AmlContext, AmlValue, 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!()
-    }
-    fn stall(&self, _microseconds: u64) {
-        unimplemented!()
-    }
-    fn sleep(&self, _milliseconds: u64) {
-        unimplemented!()
-    }
-}
-
-pub(crate) fn make_test_context() -> AmlContext {
-    AmlContext::new(Box::new(TestHandler), crate::DebugVerbosity::None)
-}
-
-pub(crate) macro check_err($parse: expr, $error: pat, $remains: expr) {
-    match $parse {
-        Ok((remains, _, result)) => panic!("Expected Err, got {:#?}. Remaining = {:#x?}", result, remains),
-        Err((remains, _, Propagate::Err($error))) if *remains == *$remains => (),
-        Err((remains, _, Propagate::Err($error))) => {
-            panic!("Correct error, incorrect stream returned: {:#x?}", remains)
-        }
-        Err((_, _, err)) => panic!("Got wrong error: {:?}", err),
-    }
-}
-
-pub(crate) macro check_ok($parse: expr, $expected: expr, $remains: expr) {
-    match $parse {
-        Ok((remains, _, ref result)) if remains == *$remains && result == &$expected => (),
-        Ok((remains, _, ref result)) if result == &$expected => {
-            panic!("Correct result, incorrect slice returned: {:x?}", remains)
-        }
-        Ok((_, _, ref result)) => panic!("Successfully parsed Ok, but it was wrong: {:#?}", result),
-        Err((_, _, err)) => panic!("Expected Ok, got {:#?}", err),
-    }
-}
-
-pub(crate) macro check_ok_value($parse: expr, $expected: expr, $remains: expr) {
-    match $parse {
-        Ok((remains, _, ref result)) if remains == *$remains && crudely_cmp_values(result, &$expected) => (),
-        Ok((remains, _, ref result)) if crudely_cmp_values(result, &$expected) => {
-            panic!("Correct result, incorrect slice returned: {:x?}", remains)
-        }
-        Ok((_, _, ref result)) => panic!("Successfully parsed Ok, but it was wrong: {:#?}", result),
-        Err((_, _, err)) => panic!("Expected Ok, got {:#?}", err),
-    }
-}
-
-/// This is a bad (but good for testing) way of comparing `AmlValue`s, which tests that they're exactly the same if
-/// it can, and gives up if it can't. It's useful in tests to be able to see if you're getting the `AmlValue` that
-/// you're expecting.
-///
-/// NOTE: almost all of the `AmlValue` variants are `Eq`, and so for a long time, `AmlValue` implemented `Eq`.
-/// However, this is a footgun as, in the real interpreter, you rarely want to directly compare values, as you need
-/// to apply the AML value conversion rules to compare them correctly. This is therefore only useful for artificial
-/// testing scenarios.
-pub(crate) fn crudely_cmp_values(a: &AmlValue, b: &AmlValue) -> bool {
-    use crate::value::MethodCode;
-
-    match a {
-        AmlValue::Uninitialized => matches!(b, AmlValue::Uninitialized),
-        AmlValue::Boolean(a) => match b {
-            AmlValue::Boolean(b) => a == b,
-            _ => false,
-        },
-        AmlValue::Integer(a) => match b {
-            AmlValue::Integer(b) => a == b,
-            _ => false,
-        },
-        AmlValue::String(ref a) => match b {
-            AmlValue::String(ref b) => a == b,
-            _ => false,
-        },
-        AmlValue::OpRegion(_) => match b {
-            AmlValue::OpRegion(_) => panic!("Can't compare two op-regions"),
-            _ => false,
-        },
-        AmlValue::Field { region, flags, offset, length } => match b {
-            AmlValue::Field { region: b_region, flags: b_flags, offset: b_offset, length: b_length } => {
-                region == b_region && flags == b_flags && offset == b_offset && length == b_length
-            }
-            _ => false,
-        },
-        AmlValue::Device => matches!(b, AmlValue::Device),
-        AmlValue::Method { flags, code } => match b {
-            AmlValue::Method { flags: b_flags, code: b_code } => {
-                if flags != b_flags {
-                    return false;
-                }
-
-                match (code, b_code) {
-                    (MethodCode::Aml(a), MethodCode::Aml(b)) => a == b,
-                    (MethodCode::Aml(_), MethodCode::Native(_)) => false,
-                    (MethodCode::Native(_), MethodCode::Aml(_)) => false,
-                    (MethodCode::Native(_), MethodCode::Native(_)) => panic!("Can't compare two native methods"),
-                }
-            }
-            _ => false,
-        },
-        AmlValue::Buffer(a) => match b {
-            AmlValue::Buffer(b) => *a.lock() == *b.lock(),
-            _ => false,
-        },
-        AmlValue::BufferField { buffer_data, offset, length } => match b {
-            AmlValue::BufferField { buffer_data: b_buffer_data, offset: b_offset, length: b_length } => {
-                alloc::sync::Arc::as_ptr(buffer_data) == alloc::sync::Arc::as_ptr(b_buffer_data)
-                    && offset == b_offset
-                    && length == b_length
-            }
-            _ => false,
-        },
-        AmlValue::Processor { id, pblk_address, pblk_len } => match b {
-            AmlValue::Processor { id: b_id, pblk_address: b_pblk_address, pblk_len: b_pblk_len } => {
-                id == b_id && pblk_address == b_pblk_address && pblk_len == b_pblk_len
-            }
-            _ => false,
-        },
-        AmlValue::Mutex { sync_level } => match b {
-            AmlValue::Mutex { sync_level: b_sync_level } => sync_level == b_sync_level,
-            _ => false,
-        },
-        AmlValue::Package(a) => match b {
-            AmlValue::Package(b) => {
-                for (a, b) in a.iter().zip(b) {
-                    if !crudely_cmp_values(a, b) {
-                        return false;
-                    }
-                }
-
-                true
-            }
-            _ => false,
-        },
-        AmlValue::PowerResource { system_level, resource_order } => match b {
-            AmlValue::PowerResource { system_level: b_system_level, resource_order: b_resource_order } => {
-                system_level == b_system_level && resource_order == b_resource_order
-            }
-            _ => false,
-        },
-        AmlValue::ThermalZone => matches!(b, AmlValue::ThermalZone),
-    }
-}
diff --git a/aml/src/value.rs b/aml/src/value.rs
deleted file mode 100644
index 4da19842..00000000
--- a/aml/src/value.rs
+++ /dev/null
@@ -1,601 +0,0 @@
-use crate::{misc::ArgNum, opregion::OpRegion, AmlContext, AmlError, AmlHandle};
-use alloc::{
-    string::{String, ToString},
-    sync::Arc,
-    vec::Vec,
-};
-use bit_field::BitField;
-use core::{cmp, fmt, fmt::Debug};
-use spinning_top::Spinlock;
-
-#[derive(Clone, Copy, PartialEq, Eq, Debug)]
-pub enum FieldAccessType {
-    Any,
-    Byte,
-    Word,
-    DWord,
-    QWord,
-    Buffer,
-}
-
-#[derive(Clone, Copy, PartialEq, Eq, Debug)]
-pub enum FieldUpdateRule {
-    Preserve,
-    WriteAsOnes,
-    WriteAsZeros,
-}
-
-// TODO: custom debug impl
-#[derive(Clone, Copy, PartialEq, Eq, Debug)]
-pub struct FieldFlags(u8);
-
-impl FieldFlags {
-    pub fn new(value: u8) -> FieldFlags {
-        FieldFlags(value)
-    }
-
-    pub fn access_type(&self) -> Result<FieldAccessType, AmlError> {
-        match self.0.get_bits(0..4) {
-            0 => Ok(FieldAccessType::Any),
-            1 => Ok(FieldAccessType::Byte),
-            2 => Ok(FieldAccessType::Word),
-            3 => Ok(FieldAccessType::DWord),
-            4 => Ok(FieldAccessType::QWord),
-            5 => Ok(FieldAccessType::Buffer),
-            _ => Err(AmlError::InvalidFieldFlags),
-        }
-    }
-
-    pub fn lock_rule(&self) -> bool {
-        self.0.get_bit(4)
-    }
-
-    pub fn field_update_rule(&self) -> Result<FieldUpdateRule, AmlError> {
-        match self.0.get_bits(5..7) {
-            0 => Ok(FieldUpdateRule::Preserve),
-            1 => Ok(FieldUpdateRule::WriteAsOnes),
-            2 => Ok(FieldUpdateRule::WriteAsZeros),
-            _ => Err(AmlError::InvalidFieldFlags),
-        }
-    }
-}
-
-#[derive(Clone, Copy, PartialEq, Eq, Debug)]
-pub struct MethodFlags(u8);
-
-impl MethodFlags {
-    pub fn new(arg_count: u8, serialize: bool, sync_level: u8) -> MethodFlags {
-        assert!(arg_count <= 7);
-        assert!(sync_level <= 15);
-
-        let mut value = 0;
-        value.set_bits(0..3, arg_count);
-        value.set_bit(3, serialize);
-        value.set_bits(4..8, sync_level);
-        MethodFlags(value)
-    }
-
-    pub fn from(value: u8) -> MethodFlags {
-        MethodFlags(value)
-    }
-
-    pub fn arg_count(&self) -> u8 {
-        self.0.get_bits(0..3)
-    }
-
-    pub fn serialize(&self) -> bool {
-        self.0.get_bit(3)
-    }
-
-    pub fn sync_level(&self) -> u8 {
-        self.0.get_bits(4..8)
-    }
-}
-
-/// 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,
-    Buffer,
-    BufferField,
-    /// Handle to a definition block handle. Returned by the `Load` operator.
-    DdbHandle,
-    DebugObject,
-    Event,
-    FieldUnit,
-    Device,
-    Integer,
-    Method,
-    Mutex,
-    ObjReference,
-    OpRegion,
-    Package,
-    PowerResource,
-    Processor,
-    RawDataBuffer,
-    String,
-    ThermalZone,
-}
-
-type NativeMethod = Arc<dyn Fn(&mut AmlContext) -> Result<AmlValue, AmlError> + Send + Sync>;
-
-#[derive(Clone)]
-pub enum MethodCode {
-    Aml(Vec<u8>),
-    Native(NativeMethod),
-}
-
-impl fmt::Debug for MethodCode {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        match self {
-            MethodCode::Aml(ref code) => write!(f, "AML({:x?})", code),
-            MethodCode::Native(_) => write!(f, "(native method)"),
-        }
-    }
-}
-
-#[derive(Clone, Debug)]
-pub enum AmlValue {
-    Uninitialized,
-    Boolean(bool),
-    Integer(u64),
-    String(String),
-    /// 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(OpRegion),
-    /// Describes a field unit within an operation region.
-    Field {
-        region: AmlHandle,
-        flags: FieldFlags,
-        offset: u64,
-        length: u64,
-    },
-    Device,
-    Method {
-        flags: MethodFlags,
-        code: MethodCode,
-    },
-    Buffer(Arc<Spinlock<Vec<u8>>>),
-    BufferField {
-        buffer_data: Arc<Spinlock<Vec<u8>>>,
-        /// In bits.
-        offset: u64,
-        /// In bits.
-        length: u64,
-    },
-    Processor {
-        id: u8,
-        pblk_address: u32,
-        pblk_len: u8,
-    },
-    Mutex {
-        sync_level: u8,
-    },
-    // TODO: I think this will need to be `Arc`ed as well, as `Index` can be used on both Buffers and Packages
-    Package(Vec<AmlValue>),
-    PowerResource {
-        system_level: u8,
-        resource_order: u16,
-    },
-    ThermalZone,
-}
-
-impl AmlValue {
-    pub fn zero() -> AmlValue {
-        AmlValue::Integer(0)
-    }
-
-    pub fn one() -> AmlValue {
-        AmlValue::Integer(1)
-    }
-
-    pub fn ones() -> AmlValue {
-        AmlValue::Integer(u64::MAX)
-    }
-
-    pub fn native_method<F>(arg_count: u8, serialize: bool, sync_level: u8, f: F) -> AmlValue
-    where
-        F: (Fn(&mut AmlContext) -> Result<AmlValue, AmlError>) + 'static + Send + Sync,
-    {
-        let flags = MethodFlags::new(arg_count, serialize, sync_level);
-        AmlValue::Method { flags, code: MethodCode::Native(Arc::new(f)) }
-    }
-
-    pub fn type_of(&self) -> AmlType {
-        match self {
-            AmlValue::Uninitialized => AmlType::Uninitialized,
-            AmlValue::Boolean(_) => AmlType::Integer,
-            AmlValue::Integer(_) => AmlType::Integer,
-            AmlValue::String(_) => AmlType::String,
-            AmlValue::OpRegion { .. } => AmlType::OpRegion,
-            AmlValue::Field { .. } => AmlType::FieldUnit,
-            AmlValue::Device => AmlType::Device,
-            AmlValue::Method { .. } => AmlType::Method,
-            AmlValue::Buffer(_) => AmlType::Buffer,
-            AmlValue::BufferField { .. } => AmlType::BufferField,
-            AmlValue::Processor { .. } => AmlType::Processor,
-            AmlValue::Mutex { .. } => AmlType::Mutex,
-            AmlValue::Package(_) => AmlType::Package,
-            AmlValue::PowerResource { .. } => AmlType::PowerResource,
-            AmlValue::ThermalZone => AmlType::ThermalZone,
-        }
-    }
-
-    /// Returns the `SizeOf (x)` application result as specified in ACPI 6.2 §19.6.125
-    pub fn size_of(&self) -> Result<u64, AmlError> {
-        match self {
-            // For a buffer, returns the size in bytes of the data
-            AmlValue::Buffer(value) => Ok(value.lock().len() as u64),
-            // For a string, returns the size in bytes (without NULL)
-            AmlValue::String(value) => Ok(value.len() as u64),
-            // For a package, returns the number of elements
-            AmlValue::Package(value) => Ok(value.len() as u64),
-            // TODO: For an Object Reference, the size of the object is returned
-            // Other data types cause a fatal run-time error
-            _ => Err(AmlError::InvalidSizeOfApplication(self.type_of())),
-        }
-    }
-
-    pub fn as_bool(&self, context: &mut AmlContext) -> Result<bool, AmlError> {
-        match self {
-            AmlValue::Boolean(value) => Ok(*value),
-            AmlValue::Integer(value) => Ok(*value != 0),
-            AmlValue::Field { .. } => Ok(self.as_integer(context)? != 0),
-            _ => Err(AmlError::IncompatibleValueConversion { current: self.type_of(), target: AmlType::Integer }),
-        }
-    }
-
-    pub fn as_integer(&self, context: &mut AmlContext) -> Result<u64, AmlError> {
-        match self {
-            AmlValue::Integer(value) => Ok(*value),
-            AmlValue::Boolean(value) => Ok(if *value { u64::MAX } else { 0 }),
-            AmlValue::Buffer(ref bytes) => {
-                /*
-                 * "The first 8 bytes of the buffer are converted to an integer, taking the first
-                 * byte as the least significant byte of the integer. A zero-length buffer is
-                 * illegal." - §19.6.140
-                 *
-                 * XXX: Buffers with length `0` appear in real tables, so we return `0` for them.
-                 */
-                let bytes = bytes.lock();
-                let bytes = if bytes.len() > 8 { &bytes[0..8] } else { &bytes[..] };
-
-                Ok(bytes.iter().rev().fold(0u64, |mut i, &popped| {
-                    i <<= 8;
-                    i += popped as u64;
-                    i
-                }))
-            }
-            /*
-             * Read from a field or buffer field. These 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),
-            AmlValue::BufferField { .. } => self.read_buffer_field(context)?.as_integer(context),
-
-            _ => Err(AmlError::IncompatibleValueConversion { current: self.type_of(), target: AmlType::Integer }),
-        }
-    }
-
-    pub fn as_buffer(&self, context: &mut AmlContext) -> Result<Arc<Spinlock<Vec<u8>>>, AmlError> {
-        match self {
-            AmlValue::Buffer(ref bytes) => Ok(bytes.clone()),
-            // TODO: implement conversion of String and Integer to Buffer
-            AmlValue::Field { .. } => self.read_field(context)?.as_buffer(context),
-            AmlValue::BufferField { .. } => self.read_buffer_field(context)?.as_buffer(context),
-            _ => Err(AmlError::IncompatibleValueConversion { current: self.type_of(), target: AmlType::Buffer }),
-        }
-    }
-
-    pub fn as_string(&self, context: &mut AmlContext) -> Result<String, AmlError> {
-        match self {
-            AmlValue::String(ref string) => Ok(string.clone()),
-            // TODO: implement conversion of Buffer to String
-            AmlValue::Field { .. } => self.read_field(context)?.as_string(context),
-            _ => Err(AmlError::IncompatibleValueConversion { current: self.type_of(), target: AmlType::String }),
-        }
-    }
-
-    /// Converts an `AmlValue` to the representation that should be used when concatenating it with other values,
-    /// primarily by the `DefConcat` opcode. This will always produce a `AmlValue::Integer`, `AmlValue::String`, or
-    /// `AmlValue::Buffer`, with other types being converted to strings containing the name of their type.
-    pub fn as_concat_type(&self) -> AmlValue {
-        match self.type_of() {
-            AmlType::Integer => self.clone(),
-            AmlType::String => self.clone(),
-            AmlType::Buffer => self.clone(),
-
-            AmlType::Uninitialized => AmlValue::String("[Uninitialized]".to_string()),
-            AmlType::BufferField => AmlValue::String("[Buffer Field]".to_string()),
-            AmlType::DdbHandle => AmlValue::String("[Ddb Handle]".to_string()),
-            AmlType::DebugObject => AmlValue::String("[Debug Object]".to_string()),
-            AmlType::Event => AmlValue::String("[Event]".to_string()),
-            AmlType::FieldUnit => AmlValue::String("[Field]".to_string()),
-            AmlType::Device => AmlValue::String("[Device]".to_string()),
-            AmlType::Method => AmlValue::String("[Control Method]".to_string()),
-            AmlType::Mutex => AmlValue::String("[Mutex]".to_string()),
-            AmlType::ObjReference => AmlValue::String("[Obj Reference]".to_string()),
-            AmlType::OpRegion => AmlValue::String("[Operation Region]".to_string()),
-            AmlType::Package => AmlValue::String("[Package]".to_string()),
-            AmlType::Processor => AmlValue::String("[Processor]".to_string()),
-            AmlType::PowerResource => AmlValue::String("[Power Resource]".to_string()),
-            AmlType::RawDataBuffer => AmlValue::String("[Raw Data Buffer]".to_string()),
-            AmlType::ThermalZone => AmlValue::String("[Thermal Zone]".to_string()),
-        }
-    }
-
-    /// 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.
-    ///
-    /// The implicit conversions applied are:
-    ///     `Buffer` from: `Integer`, `String`, `Debug`
-    ///     `BufferField` from: `Integer`, `Buffer`, `String`, `Debug`
-    ///     `DdbHandle` from: `Integer`, `Debug`
-    ///     `FieldUnit` from: `Integer`,`Buffer`, `String`, `Debug`
-    ///     `Integer` from: `Buffer`, `BufferField`, `DdbHandle`, `FieldUnit`, `String`, `Debug`
-    ///     `Package` from: `Debug`
-    ///     `String` from: `Integer`, `Buffer`, `Debug`
-    pub fn as_type(&self, desired_type: AmlType, context: &mut AmlContext) -> Result<AmlValue, AmlError> {
-        // If the value is already of the correct type, just return it as is
-        if self.type_of() == desired_type {
-            return Ok(self.clone());
-        }
-
-        // TODO: implement all of the rules
-        match desired_type {
-            AmlType::Integer => self.as_integer(context).map(AmlValue::Integer),
-            AmlType::Buffer => self.as_buffer(context).map(AmlValue::Buffer),
-            AmlType::FieldUnit => panic!(
-                "Can't implicitly convert to FieldUnit. This must be special-cased by the caller for now :("
-            ),
-            _ => Err(AmlError::IncompatibleValueConversion { current: self.type_of(), target: desired_type }),
-        }
-    }
-
-    /// Reads from a field of an op-region, returning either a `AmlValue::Integer` or an `AmlValue::Buffer`,
-    /// depending on the size of the field.
-    pub fn read_field(&self, context: &mut AmlContext) -> Result<AmlValue, AmlError> {
-        if let AmlValue::Field { region, flags, offset, length } = self {
-            if let AmlValue::OpRegion(region) = context.namespace.get(*region)?.clone() {
-                region.read_field(*offset, *length, *flags, context)
-            } else {
-                Err(AmlError::FieldRegionIsNotOpRegion)
-            }
-        } else {
-            Err(AmlError::IncompatibleValueConversion { current: self.type_of(), target: AmlType::FieldUnit })
-        }
-    }
-
-    /// Write to a field of an op-region, from either a `AmlValue::Integer` or `AmlValue::Buffer`
-    /// as necessary.
-    pub fn write_field(&mut self, value: AmlValue, context: &mut AmlContext) -> Result<(), AmlError> {
-        if let AmlValue::Field { region, flags, offset, length } = self {
-            if let AmlValue::OpRegion(region) = context.namespace.get(*region)?.clone() {
-                region.write_field(*offset, *length, *flags, value, context)
-            } else {
-                Err(AmlError::FieldRegionIsNotOpRegion)
-            }
-        } else {
-            Err(AmlError::IncompatibleValueConversion { current: self.type_of(), target: AmlType::FieldUnit })
-        }
-    }
-
-    pub fn read_buffer_field(&self, _context: &AmlContext) -> Result<AmlValue, AmlError> {
-        use bitvec::view::BitView;
-
-        if let AmlValue::BufferField { buffer_data, offset, length } = self {
-            let offset = *offset as usize;
-            let length = *length as usize;
-            let inner_data = buffer_data.lock();
-
-            if (offset + length) > (inner_data.len() * 8) {
-                return Err(AmlError::BufferFieldIndexesOutOfBounds);
-            }
-
-            let bitslice = inner_data.view_bits::<bitvec::order::Lsb0>();
-            let bits = &bitslice[offset..(offset + length)];
-
-            if length > 64 {
-                let mut bitvec = bits.to_bitvec();
-                bitvec.set_uninitialized(false);
-                Ok(AmlValue::Buffer(Arc::new(spinning_top::Spinlock::new(bitvec.into_vec()))))
-            } else if length > 32 {
-                /*
-                 * TODO: this is a pretty gross hack to work around a weird limitation with the `bitvec` crate on
-                 * 32-bit platforms. For reasons beyond me right now, it can't operate on a `u64` on a 32-bit
-                 * platform, so we manually extract two `u32`s and stick them together. In the future, we should
-                 * definitely have a closer look at what `bitvec` is doing and see if we can fix this code, or
-                 * replace it with a different crate. This should hold everything vaguely together until we have
-                 * time to do that.
-                 */
-                let mut upper = 0u32;
-                let mut lower = 0u32;
-                lower.view_bits_mut::<bitvec::order::Lsb0>()[0..32].clone_from_bitslice(bits);
-                upper.view_bits_mut::<bitvec::order::Lsb0>()[0..(length - 32)].clone_from_bitslice(&bits[32..]);
-                Ok(AmlValue::Integer((upper as u64) << (32 + (lower as u64))))
-            } else {
-                let mut value = 0u32;
-                value.view_bits_mut::<bitvec::order::Lsb0>()[0..length].clone_from_bitslice(bits);
-                Ok(AmlValue::Integer(value as u64))
-            }
-        } else {
-            Err(AmlError::IncompatibleValueConversion { current: self.type_of(), target: AmlType::BufferField })
-        }
-    }
-
-    pub fn write_buffer_field(&mut self, value: AmlValue, _context: &mut AmlContext) -> Result<(), AmlError> {
-        use bitvec::view::BitView;
-
-        if let AmlValue::BufferField { buffer_data, offset, length } = self {
-            let offset = *offset as usize;
-            let length = *length as usize;
-            // TODO: check these against the size of the buffer to be written into
-            let mut inner_data = buffer_data.lock();
-            let bitslice = inner_data.view_bits_mut::<bitvec::order::Lsb0>();
-
-            match value {
-                AmlValue::Integer(value) => {
-                    /*
-                     * When an `Integer` is written into a `BufferField`, the entire contents are overwritten. If
-                     * it's smaller than the length of the buffer field, it's zero-extended. If it's larger, the
-                     * upper bits are truncated.
-                     */
-                    let bits_to_copy = cmp::min(length, 64);
-                    bitslice[offset..(offset + bits_to_copy)]
-                        .copy_from_bitslice(&value.to_le_bytes().view_bits()[..(bits_to_copy as usize)]);
-                    // Zero extend to the end of the buffer field
-                    bitslice[(offset + bits_to_copy)..(offset + length)].fill(false);
-                    Ok(())
-                }
-                AmlValue::Boolean(value) => {
-                    bitslice.set(offset, value);
-                    Ok(())
-                }
-                AmlValue::Buffer(value) => {
-                    /*
-                     * When a `Buffer` is written into a `BufferField`, the entire contents are copied into the
-                     * field. If the buffer is smaller than the size of the buffer field, it is zero extended. If
-                     * the buffer is larger, the upper bits are truncated.
-                     * XXX: this behaviour is only explicitly defined in ACPI 2.0+. While undefined in ACPI 1.0,
-                     * we produce the same behaviour there.
-                     */
-                    let value_data = value.lock();
-                    let bits_to_copy = cmp::min(length, value_data.len() * 8);
-                    bitslice[offset..(offset + bits_to_copy)]
-                        .copy_from_bitslice(&value_data.view_bits()[..(bits_to_copy as usize)]);
-                    // Zero extend to the end of the buffer field
-                    bitslice[(offset + bits_to_copy)..(offset + length)].fill(false);
-                    Ok(())
-                }
-                _ => Err(AmlError::TypeCannotBeWrittenToBufferField(value.type_of())),
-            }
-        } else {
-            Err(AmlError::IncompatibleValueConversion { current: self.type_of(), target: AmlType::BufferField })
-        }
-    }
-
-    /// Logically compare two `AmlValue`s, according to the rules that govern opcodes like `DefLEqual`, `DefLLess`,
-    /// etc. The type of `self` dictates the type that `other` will be converted to, and the method by which the
-    /// values will be compared:
-    ///    - `Integer`s are simply compared by numeric comparison
-    ///    - `String`s and `Buffer`s are compared lexicographically - `other` is compared byte-wise until a byte
-    ///      is discovered that is either less or greater than the corresponding byte of `self`. If the bytes are
-    ///      identical, the lengths are compared. Luckily, the Rust standard library implements lexicographic
-    ///      comparison of strings and `[u8]` for us already.
-    pub fn cmp(&self, other: AmlValue, context: &mut AmlContext) -> Result<cmp::Ordering, AmlError> {
-        let self_inner =
-            if self.type_of() == AmlType::FieldUnit { self.read_field(context)? } else { self.clone() };
-
-        match self_inner.type_of() {
-            AmlType::Integer => Ok(self.as_integer(context)?.cmp(&other.as_integer(context)?)),
-            AmlType::Buffer => Ok(self.as_buffer(context)?.lock().cmp(&other.as_buffer(context)?.lock())),
-            AmlType::String => Ok(self.as_string(context)?.cmp(&other.as_string(context)?)),
-            typ => Err(AmlError::TypeCannotBeCompared(typ)),
-        }
-    }
-}
-
-/// A control method can take up to 7 arguments, each of which is an `AmlValue`.
-#[derive(Clone, Default, Debug)]
-pub struct Args(pub [Option<AmlValue>; 7]);
-
-impl Args {
-    pub const EMPTY: Self = Self([None, None, None, None, None, None, None]);
-
-    pub fn from_list(list: Vec<AmlValue>) -> Result<Args, AmlError> {
-        if list.len() > 7 {
-            return Err(AmlError::TooManyArgs);
-        }
-
-        let mut args: Vec<Option<AmlValue>> = list.into_iter().map(Option::Some).collect();
-        args.extend(core::iter::repeat(None).take(7 - args.len()));
-        Ok(Args(args.try_into().unwrap()))
-    }
-
-    pub fn arg(&self, arg: ArgNum) -> Result<&AmlValue, AmlError> {
-        if arg > 6 {
-            return Err(AmlError::InvalidArgAccess(arg));
-        }
-
-        self.0[arg as usize].as_ref().ok_or(AmlError::InvalidArgAccess(arg))
-    }
-
-    pub fn store_arg(&mut self, arg: ArgNum, value: AmlValue) -> Result<(), AmlError> {
-        if arg > 6 {
-            return Err(AmlError::InvalidArgAccess(arg));
-        }
-
-        self.0[arg as usize] = Some(value);
-        Ok(())
-    }
-}
-
-#[cfg(test)]
-mod tests {
-    use super::*;
-    use crate::test_utils::*;
-    use core::cmp::Ordering;
-
-    #[test]
-    fn test_object_cmp() {
-        let mut context = make_test_context();
-
-        assert_eq!(AmlValue::Integer(76).cmp(AmlValue::Integer(89), &mut context), Ok(Ordering::Less));
-        assert_eq!(AmlValue::Integer(11).cmp(AmlValue::Integer(11), &mut context), Ok(Ordering::Equal));
-        assert_eq!(AmlValue::Integer(8362836690).cmp(AmlValue::Integer(1), &mut context), Ok(Ordering::Greater));
-
-        // TODO: test the other combinations too, as well as conversions to the correct types for the second operand
-    }
-}
diff --git a/tools/aml_tester/src/main.rs b/tools/aml_tester/src/main.rs
index 1d8788a3..b812279f 100644
--- a/tools/aml_tester/src/main.rs
+++ b/tools/aml_tester/src/main.rs
@@ -9,7 +9,7 @@
  *      - For failing tests, print out a nice summary of the errors for each file
  */
 
-use aml::{AmlContext, DebugVerbosity};
+use aml::Interpreter;
 use clap::{Arg, ArgAction, ArgGroup};
 use std::{
     collections::HashSet,
@@ -169,7 +169,8 @@ fn main() -> std::io::Result<()> {
         .collect::<Vec<_>>();
 
     let combined_test = matches.get_flag("combined");
-    let mut context = AmlContext::new(Box::new(Handler), DebugVerbosity::None);
+
+    let mut interpreter = Interpreter::new(Handler);
 
     let (passed, failed) = aml_files.into_iter().fold((0, 0), |(passed, failed), file_entry| {
         print!("Testing AML file: {:?}... ", file_entry);
@@ -185,20 +186,20 @@ fn main() -> std::io::Result<()> {
         const AML_TABLE_HEADER_LENGTH: usize = 36;
 
         if !combined_test {
-            context = AmlContext::new(Box::new(Handler), DebugVerbosity::None);
+            interpreter = Interpreter::new(Handler);
         }
 
-        match context.parse_table(&contents[AML_TABLE_HEADER_LENGTH..]) {
+        match interpreter.load_table(&contents[AML_TABLE_HEADER_LENGTH..]) {
             Ok(()) => {
                 println!("{}OK{}", termion::color::Fg(termion::color::Green), termion::style::Reset);
-                println!("Namespace: {:#?}", context.namespace);
+                println!("Namespace: {:#?}", interpreter.namespace.lock());
                 summaries.insert((file_entry, TestResult::Pass));
                 (passed + 1, failed)
             }
 
             Err(err) => {
                 println!("{}Failed ({:?}){}", termion::color::Fg(termion::color::Red), err, termion::style::Reset);
-                println!("Namespace: {:#?}", context.namespace);
+                println!("Namespace: {:#?}", interpreter.namespace.lock());
                 summaries.insert((file_entry, TestResult::ParseFail));
                 (passed, failed + 1)
             }

From c6de62f207c83d990e85602b05c3a7d68831d852 Mon Sep 17 00:00:00 2001
From: Isaac Woods <isaacwoods.home@gmail.com>
Date: Fri, 7 Feb 2025 15:02:50 +0000
Subject: [PATCH 17/88] `aml`: support `DefPackage` + strings + refactor block
 handling

---
 aml/src/lib.rs    | 103 ++++++++++++++++++++++++++++++++++++----------
 aml/src/object.rs |   4 +-
 tests/package.asl |  14 +++++++
 3 files changed, 98 insertions(+), 23 deletions(-)
 create mode 100644 tests/package.asl

diff --git a/aml/src/lib.rs b/aml/src/lib.rs
index 100da98d..66425955 100644
--- a/aml/src/lib.rs
+++ b/aml/src/lib.rs
@@ -11,7 +11,7 @@ pub mod object;
 
 use alloc::{boxed::Box, sync::Arc, vec, vec::Vec};
 use bit_field::BitField;
-use core::mem;
+use core::{mem, str};
 use namespace::{AmlName, Namespace, NamespaceLevelKind};
 use object::Object;
 use spinning_top::Spinlock;
@@ -64,7 +64,7 @@ impl Interpreter {
          * up.
          */
 
-        'main_loop: loop {
+        loop {
             /*
              * First, see if we've gathered enough arguments to complete some in-flight operations.
              */
@@ -106,6 +106,9 @@ impl Interpreter {
                         let name = name.resolve(&context.current_scope)?;
                         self.namespace.lock().insert(name, object.clone())?;
                     }
+                    Opcode::Package => {
+                        // Nothing to do here. The package is created by the block ending.
+                    }
                     _ => panic!("Unexpected operation has created in-flight op!"),
                 }
             }
@@ -114,28 +117,57 @@ impl Interpreter {
              * Now that we've retired as many in-flight operations as we have arguments for, move
              * forward in the AML stream.
              */
-            let opcode = loop {
-                match context.opcode() {
-                    Ok(opcode) => break opcode,
-                    Err(AmlError::RunOutOfStream) => {
-                        /*
-                         * We've reached the end of the current block. What we should do about this
-                         * depends on what type of block it was.
-                         */
-                        match context.current_block.kind {
-                            BlockKind::Normal => {
-                                break 'main_loop Ok(());
+            let opcode = match context.opcode() {
+                Ok(opcode) => opcode,
+                Err(AmlError::RunOutOfStream) => {
+                    /*
+                     * We've reached the end of the current block. What we should do about this
+                     * depends on what type of block it was.
+                     */
+                    match context.current_block.kind {
+                        BlockKind::Normal => {
+                            break Ok(());
+                        }
+                        BlockKind::Scope { old_scope } => {
+                            assert!(context.block_stack.len() > 0);
+                            context.current_block = context.block_stack.pop().unwrap();
+                            context.current_scope = old_scope;
+                            // Go round the loop again to get the next opcode for the new block
+                            continue;
+                        }
+                        BlockKind::Package => {
+                            assert!(context.block_stack.len() > 0);
+
+                            // Pop the in-flight operation off and make sure it's the package
+                            let package_op = context.in_flight.pop().unwrap();
+                            assert_eq!(package_op.op, Opcode::Package);
+
+                            let mut elements = Vec::with_capacity(package_op.expected_arguments);
+                            for arg in &package_op.arguments {
+                                let Argument::Object(object) = arg else { panic!() };
+                                elements.push(object.clone());
                             }
-                            BlockKind::Scope { old_scope } => {
-                                assert!(context.block_stack.len() > 0);
-                                context.current_block = context.block_stack.pop().unwrap();
-                                context.current_scope = old_scope;
-                                // Go round the loop again to get the next opcode for the new block
+                            for _ in package_op.arguments.len()..package_op.expected_arguments {
+                                // Each uninitialized element must be a distinct object
+                                elements.push(Arc::new(Object::Uninitialized));
                             }
+
+                            // Add the created package to the last in-flight op's arguments
+                            if let Some(prev_op) = context.in_flight.last_mut() {
+                                if prev_op.arguments.len() < prev_op.expected_arguments {
+                                    prev_op.arguments.push(Argument::Object(Arc::new(Object::Package(elements))));
+                                } else {
+                                    panic!("Random package floating around?");
+                                }
+                            }
+
+                            // End the block, go round the loop again
+                            context.current_block = context.block_stack.pop().unwrap();
+                            continue;
                         }
                     }
-                    Err(other_err) => return Err(other_err),
                 }
+                Err(other_err) => return Err(other_err),
             };
             match opcode {
                 Opcode::Zero => {
@@ -165,7 +197,18 @@ impl Interpreter {
                     let value = context.next_u32()?;
                     context.last_op()?.arguments.push(Argument::Object(Arc::new(Object::Integer(value as u64))));
                 }
-                Opcode::StringPrefix => todo!(),
+                Opcode::StringPrefix => {
+                    let str_start = context.current_block.pc;
+                    while context.next()? != b'\0' {}
+                    // TODO: handle err
+                    let str =
+                        str::from_utf8(&context.current_block.stream[str_start..(context.current_block.pc - 1)])
+                            .unwrap();
+                    context
+                        .last_op()?
+                        .arguments
+                        .push(Argument::Object(Arc::new(Object::String(String::from(str)))));
+                }
                 Opcode::QWordPrefix => {
                     let value = context.next_u64()?;
                     context.last_op()?.arguments.push(Argument::Object(Arc::new(Object::Integer(value))));
@@ -185,7 +228,24 @@ impl Interpreter {
                     context.start_new_block(BlockKind::Scope { old_scope }, remaining_length);
                 }
                 Opcode::Buffer => todo!(),
-                Opcode::Package => todo!(),
+                Opcode::Package => {
+                    let start_pc = context.current_block.pc;
+                    let pkg_length = context.pkglength()?;
+                    let num_elements = context.next()?;
+
+                    let remaining_length = pkg_length - (context.current_block.pc - start_pc);
+                    println!("Package. num elements = {}", num_elements);
+
+                    /*
+                     * We now need to interpret an arbitrary number of package elements, bounded by
+                     * the remaining pkglength. This may be less than `num_elements` - the
+                     * remaining elements of the package are uninitialized. We utilise a
+                     * combination of a block to manage the pkglength, plus an in-flight op to
+                     * store interpreted arguments.
+                     */
+                    context.start_in_flight_op(OpInFlight::new(Opcode::Package, num_elements as usize));
+                    context.start_new_block(BlockKind::Package, remaining_length);
+                }
                 Opcode::VarPackage => todo!(),
                 Opcode::Method => todo!(),
                 Opcode::External => todo!(),
@@ -337,6 +397,7 @@ pub struct Block<'a> {
 pub enum BlockKind {
     Normal,
     Scope { old_scope: AmlName },
+    Package,
 }
 
 impl OpInFlight {
diff --git a/aml/src/object.rs b/aml/src/object.rs
index c40cfc87..2a08b546 100644
--- a/aml/src/object.rs
+++ b/aml/src/object.rs
@@ -13,11 +13,11 @@ pub enum Object {
     Mutex,
     Reference(Arc<Object>),
     OpRegion,
-    Package,
+    Package(Vec<Arc<Object>>),
     PowerResource,
     Processor,
     RawDataBuffer,
-    String,
+    String(String),
     ThermalZone,
 }
 
diff --git a/tests/package.asl b/tests/package.asl
new file mode 100644
index 00000000..69a0e3b0
--- /dev/null
+++ b/tests/package.asl
@@ -0,0 +1,14 @@
+DefinitionBlock("package.aml", "DSDT", 1, "RSACPI", "PACKGE", 1) {
+    Name(FOO, Package (15) {
+        0x01,
+        0x02,
+        0x03,
+        "Hello, World!",
+        0x05,
+        0x06,
+        0x07,
+        0x08,
+        0x09,
+        0x0a,
+    })
+}

From 708dd48e82f3e0abdb06495cef02bc87c0b4c5b1 Mon Sep 17 00:00:00 2001
From: Isaac Woods <isaacwoods.home@gmail.com>
Date: Fri, 7 Feb 2025 15:05:22 +0000
Subject: [PATCH 18/88] `aml`: stub out support for `DefOpRegion`, `DefField`,
 `DefThermalZone`, `DefMethod`, `DefExternal`

---
 aml/src/lib.rs       | 175 ++++++++++++++++++++++++++++++++++---------
 aml/src/object.rs    |   8 +-
 aml/src/op_region.rs |  42 +++++++++++
 3 files changed, 186 insertions(+), 39 deletions(-)
 create mode 100644 aml/src/op_region.rs

diff --git a/aml/src/lib.rs b/aml/src/lib.rs
index 66425955..fbd87d2b 100644
--- a/aml/src/lib.rs
+++ b/aml/src/lib.rs
@@ -8,12 +8,14 @@ extern crate alloc;
 
 pub mod namespace;
 pub mod object;
+pub mod op_region;
 
 use alloc::{boxed::Box, sync::Arc, vec, vec::Vec};
 use bit_field::BitField;
 use core::{mem, str};
 use namespace::{AmlName, Namespace, NamespaceLevelKind};
-use object::Object;
+use object::{MethodFlags, Object};
+use op_region::RegionSpace;
 use spinning_top::Spinlock;
 
 pub struct Interpreter {
@@ -71,41 +73,53 @@ impl Interpreter {
             while let Some(op) = context.in_flight.pop_if(|op| op.arguments.len() == op.expected_arguments) {
                 match op.op {
                     Opcode::Add => {
-                        let [left, right, target] = &op.arguments[..] else { panic!() };
-                        if let Argument::Object(left) = left
-                            && let Argument::Object(right) = right
-                        {
-                            let Object::Integer(left) = **left else { panic!() };
-                            let Object::Integer(right) = **right else { panic!() };
-
-                            if let Argument::Object(target) = target {
-                                *target.gain_mut() = Object::Integer(left + right);
-                            } else {
-                                panic!("Unexpected target type in AddOp");
+                        let [Argument::Object(left), Argument::Object(right), Argument::Object(target)] =
+                            &op.arguments[..]
+                        else {
+                            panic!()
+                        };
+                        let Object::Integer(left) = **left else { panic!() };
+                        let Object::Integer(right) = **right else { panic!() };
+
+                        *target.gain_mut() = Object::Integer(left + right);
+
+                        // TODO: this is probs a slightly scuffed way of working out if the
+                        // prev op wants our result
+                        if let Some(prev_op) = context.in_flight.last_mut() {
+                            if prev_op.arguments.len() < prev_op.expected_arguments {
+                                prev_op.arguments.push(Argument::Object(Arc::new(Object::Integer(left + right))));
                             }
-                            // TODO: this is probs a slightly scuffed way of working out if the
-                            // prev op wants our result
-                            if let Some(prev_op) = context.in_flight.last_mut() {
-                                if prev_op.arguments.len() < prev_op.expected_arguments {
-                                    prev_op
-                                        .arguments
-                                        .push(Argument::Object(Arc::new(Object::Integer(left + right))));
-                                }
-                            }
-                            // println!("Result: {}", left + right);
-                        } else {
-                            panic!("Bad operands");
                         }
+                        println!("Result: {}", left + right);
                     }
                     Opcode::Name => {
-                        let [name, object] = &op.arguments[..] else { panic!() };
-                        let Argument::Namestring(name) = name else { panic!() };
-                        let Argument::Object(object) = object else { panic!() };
-                        // println!("Making Name with name {} and object: {:?}", name, object);
+                        let [Argument::Namestring(name), Argument::Object(object)] = &op.arguments[..] else {
+                            panic!()
+                        };
+                        println!("Making Name with name {} and object: {:?}", name, object);
 
                         let name = name.resolve(&context.current_scope)?;
                         self.namespace.lock().insert(name, object.clone())?;
                     }
+                    Opcode::OpRegion => {
+                        let [
+                            Argument::Namestring(name),
+                            Argument::ByteData(region_space),
+                            Argument::Object(region_offset),
+                            Argument::Object(region_length),
+                        ] = &op.arguments[..]
+                        else {
+                            panic!()
+                        };
+
+                        let region_space = RegionSpace::from(*region_space);
+                        println!(
+                            "OpRegion. name={}, space={:?}, offset={:?}, length={:?}",
+                            name, region_space, region_offset, region_length
+                        );
+                        // TODO: convert offset and length to integers
+                        // TODO: add to namespace
+                    }
                     Opcode::Package => {
                         // Nothing to do here. The package is created by the block ending.
                     }
@@ -223,7 +237,7 @@ impl Interpreter {
                     let new_scope = name.resolve(&context.current_scope)?;
                     self.namespace.lock().add_level(new_scope.clone(), NamespaceLevelKind::Scope)?;
 
-                    // println!("Scope. length = {}({}), name = {:?}", pkg_length, remaining_length, new_scope);
+                    println!("Scope. length = {}({}), name = {:?}", pkg_length, remaining_length, new_scope);
                     let old_scope = mem::replace(&mut context.current_scope, new_scope);
                     context.start_new_block(BlockKind::Scope { old_scope }, remaining_length);
                 }
@@ -247,8 +261,27 @@ impl Interpreter {
                     context.start_new_block(BlockKind::Package, remaining_length);
                 }
                 Opcode::VarPackage => todo!(),
-                Opcode::Method => todo!(),
-                Opcode::External => todo!(),
+                Opcode::Method => {
+                    let start_pc = context.current_block.pc;
+                    let pkg_length = context.pkglength()?;
+                    let name = context.namestring()?;
+                    let flags = MethodFlags(context.next()?);
+
+                    let code_len = pkg_length - (context.current_block.pc - start_pc);
+                    let code = context.current_block.stream[start_pc..(start_pc + code_len)].to_vec();
+                    context.current_block.pc += code_len;
+
+                    println!("Method. Name={:?}, len={}", name, code_len);
+
+                    let name = name.resolve(&context.current_scope)?;
+                    self.namespace.lock().insert(name, Arc::new(Object::Method { code, flags }))?;
+                }
+                Opcode::External => {
+                    let _name = context.namestring()?;
+                    let _object_type = context.next()?;
+                    let _arg_count = context.next()?;
+                    println!("External. Name = {}, typ={}, arg_count={}", _name, _object_type, _arg_count);
+                }
                 Opcode::DualNamePrefix => todo!(),
                 Opcode::MultiNamePrefix => todo!(),
                 Opcode::Digit(_) => todo!(),
@@ -272,8 +305,62 @@ impl Interpreter {
                 Opcode::Debug => todo!(),
                 Opcode::Fatal => todo!(),
                 Opcode::Timer => todo!(),
-                Opcode::OpRegion => todo!(),
-                Opcode::Field => todo!(),
+                Opcode::OpRegion => {
+                    let name = context.namestring()?;
+                    let region_space = context.next()?;
+                    context.start_in_flight_op(OpInFlight::new_with(
+                        Opcode::OpRegion,
+                        vec![Argument::Namestring(name), Argument::ByteData(region_space)],
+                        2,
+                    ));
+                }
+                Opcode::Field => {
+                    let start_pc = context.current_block.pc;
+                    let pkg_length = context.pkglength()?;
+                    let name = context.namestring()?;
+                    let flags = context.next()?;
+
+                    println!("Parsing field list. NAme = {:?}", name);
+
+                    const RESERVED_FIELD: u8 = 0x00;
+                    const ACCESS_FIELD: u8 = 0x01;
+                    const CONNECT_FIELD: u8 = 0x02;
+                    const EXTENDED_ACCESS_FIELD: u8 = 0x03;
+
+                    let mut field_offset = 0;
+
+                    while context.current_block.pc < (start_pc + pkg_length) {
+                        match context.next()? {
+                            RESERVED_FIELD => {
+                                let length = context.pkglength()?;
+                                field_offset += length;
+                                println!("Reserved field. len={}", length);
+                            }
+                            ACCESS_FIELD => {
+                                let access_type = context.next()?;
+                                let access_attrib = context.next()?;
+                                println!("Access field. typ={}, attrib={}", access_type, access_attrib);
+                                // TODO
+                            }
+                            CONNECT_FIELD => {
+                                // TODO: either consume a namestring or `BufferData` (it's not
+                                // clear what a buffer data acc is lmao)
+                                println!("Connect field :(");
+                            }
+                            EXTENDED_ACCESS_FIELD => {
+                                println!("Extended access field :(");
+                                todo!()
+                            }
+                            _ => {
+                                context.current_block.pc -= 1;
+                                // TODO: this should only ever be a nameseg really...
+                                let field_name = context.namestring()?;
+                                let field_length = context.pkglength()?;
+                                println!("NAmed field. NAme={:?}, len={}", field_name, field_length);
+                            }
+                        }
+                    }
+                }
                 Opcode::Device => {
                     let start_pc = context.current_block.pc;
                     let pkg_length = context.pkglength()?;
@@ -284,12 +371,25 @@ impl Interpreter {
                     let new_scope = name.resolve(&context.current_scope)?;
                     self.namespace.lock().add_level(new_scope.clone(), NamespaceLevelKind::Device)?;
 
-                    // println!("Device. length = {}({}), name = {:?}", pkg_length, remaining_length, new_scope);
+                    println!("Device. length = {}({}), name = {:?}", pkg_length, remaining_length, new_scope);
                     let old_scope = mem::replace(&mut context.current_scope, new_scope);
                     context.start_new_block(BlockKind::Scope { old_scope }, remaining_length);
                 }
                 Opcode::PowerRes => todo!(),
-                Opcode::ThermalZone => todo!(),
+                Opcode::ThermalZone => {
+                    let start_pc = context.current_block.pc;
+                    let pkg_length = context.pkglength()?;
+                    let name = context.namestring()?;
+
+                    let remaining_length = pkg_length - (context.current_block.pc - start_pc);
+
+                    let new_scope = name.resolve(&context.current_scope)?;
+                    self.namespace.lock().add_level(new_scope.clone(), NamespaceLevelKind::ThermalZone)?;
+
+                    println!("ThermalZone. length = {}({}), name = {:?}", pkg_length, remaining_length, new_scope);
+                    let old_scope = mem::replace(&mut context.current_scope, new_scope);
+                    context.start_new_block(BlockKind::Scope { old_scope }, remaining_length);
+                }
                 Opcode::IndexField => todo!(),
                 Opcode::BankField => todo!(),
                 Opcode::DataRegion => todo!(),
@@ -385,6 +485,7 @@ pub struct OpInFlight {
 pub enum Argument {
     Object(Arc<Object>),
     Namestring(AmlName),
+    ByteData(u8),
 }
 
 pub struct Block<'a> {
@@ -430,12 +531,12 @@ impl<'a> MethodContext<'a> {
     }
 
     pub fn start_in_flight_op(&mut self, op: OpInFlight) {
-        // println!("Starting in-flight op of type: {:?}", op);
+        println!("Starting in-flight op of type: {:?}", op);
         self.in_flight.push(op);
     }
 
     pub fn start_new_block(&mut self, kind: BlockKind, length: usize) {
-        // println!("Starting new block at pc={}, length={}, kind={:?}", self.current_block.pc, length, kind);
+        println!("Starting new block at pc={}, length={}, kind={:?}", self.current_block.pc, length, kind);
         let block = Block {
             stream: &self.current_block.stream[..(self.current_block.pc + length)],
             pc: self.current_block.pc,
diff --git a/aml/src/object.rs b/aml/src/object.rs
index 2a08b546..1b49b89c 100644
--- a/aml/src/object.rs
+++ b/aml/src/object.rs
@@ -1,3 +1,4 @@
+use crate::op_region::OpRegion;
 use alloc::{sync::Arc, vec::Vec};
 
 #[derive(Debug)]
@@ -9,10 +10,10 @@ pub enum Object {
     Event,
     FieldUnit,
     Integer(u64),
-    Method,
+    Method { code: Vec<u8>, flags: MethodFlags },
     Mutex,
     Reference(Arc<Object>),
-    OpRegion,
+    OpRegion(OpRegion),
     Package(Vec<Arc<Object>>),
     PowerResource,
     Processor,
@@ -41,3 +42,6 @@ impl Object {
         }
     }
 }
+
+#[derive(Clone, Copy, Debug)]
+pub struct MethodFlags(pub u8);
diff --git a/aml/src/op_region.rs b/aml/src/op_region.rs
new file mode 100644
index 00000000..7582c2dc
--- /dev/null
+++ b/aml/src/op_region.rs
@@ -0,0 +1,42 @@
+#[derive(Debug)]
+pub struct OpRegion {
+    space: RegionSpace,
+    base: u64,
+    limit: u64,
+    // parent_device
+}
+
+#[derive(Clone, Copy, PartialEq, Debug)]
+pub enum RegionSpace {
+    SystemMemory,
+    SystemIO,
+    PciConfig,
+    EmbeddedControl,
+    SmBus,
+    SystemCmos,
+    PciBarTarget,
+    Ipmi,
+    GeneralPurposeIo,
+    GenericSerialBus,
+    Pcc,
+    Oem(u8),
+}
+
+impl From<u8> for RegionSpace {
+    fn from(value: u8) -> Self {
+        match value {
+            0 => RegionSpace::SystemMemory,
+            1 => RegionSpace::SystemIO,
+            2 => RegionSpace::PciConfig,
+            3 => RegionSpace::EmbeddedControl,
+            4 => RegionSpace::SmBus,
+            5 => RegionSpace::SystemCmos,
+            6 => RegionSpace::PciBarTarget,
+            7 => RegionSpace::Ipmi,
+            8 => RegionSpace::GeneralPurposeIo,
+            9 => RegionSpace::GenericSerialBus,
+            10 => RegionSpace::Pcc,
+            _ => RegionSpace::Oem(value),
+        }
+    }
+}

From 4ae5c069cd753b632e01918b407c748d41882d21 Mon Sep 17 00:00:00 2001
From: Isaac Woods <isaacwoods.home@gmail.com>
Date: Fri, 7 Feb 2025 20:48:25 +0000
Subject: [PATCH 19/88] `aml`: fix parsing of `NameString`s

---
 aml/src/lib.rs | 78 +++++++++++++++++++++++++++++---------------------
 1 file changed, 45 insertions(+), 33 deletions(-)

diff --git a/aml/src/lib.rs b/aml/src/lib.rs
index fbd87d2b..6dc2726f 100644
--- a/aml/src/lib.rs
+++ b/aml/src/lib.rs
@@ -704,47 +704,59 @@ impl<'a> MethodContext<'a> {
     pub fn namestring(&mut self) -> Result<AmlName, AmlError> {
         use namespace::{NameComponent, NameSeg};
 
+        /*
+         * The NameString grammar is actually a little finicky and annoying.
+         *
+         * NameString := <RootChar NamePath> | <PrefixPath NamePath>
+         * PrefixPath := Nothing | <'^' PrefixPath>
+         * NamePath := NameSeg | DualNamePath | MultiNamePath | NullName
+         * DualNamePath := DualNamePrefix NameSeg NameSeg
+         * MultiNamePath := MultiNamePrefix SegCount NameSeg(SegCount)
+         */
         const NULL_NAME: u8 = 0x00;
         const DUAL_NAME_PREFIX: u8 = 0x2e;
         const MULTI_NAME_PREFIX: u8 = 0x2f;
 
         let mut components = vec![];
-        loop {
-            let next = match self.next() {
-                Ok(next) => next,
-                Err(AmlError::RunOutOfStream) => break,
-                Err(other) => Err(other)?,
-            };
-            match next {
-                b'\\' => {
-                    if !components.is_empty() {
-                        return Err(AmlError::InvalidName(None));
-                    }
-                    components.push(NameComponent::Root);
+
+        match self.peek()? {
+            b'\\' => {
+                self.next()?;
+                components.push(NameComponent::Root);
+            }
+            b'^' => {
+                components.push(NameComponent::Prefix);
+                self.next()?;
+                while self.peek()? == b'^' {
+                    self.next()?;
+                    components.push(NameComponent::Prefix);
                 }
-                b'^' => components.push(NameComponent::Prefix),
-                NULL_NAME => {}
-                DUAL_NAME_PREFIX => {
-                    for _ in 0..2 {
-                        let name_seg = [self.next()?, self.next()?, self.next()?, self.next()?];
-                        components.push(NameComponent::Segment(NameSeg::from_bytes(name_seg)?));
-                    }
+            }
+            _ => (),
+        }
+
+        let next = self.next()?;
+        match next {
+            NULL_NAME => {}
+            DUAL_NAME_PREFIX => {
+                for _ in 0..2 {
+                    let name_seg = [self.next()?, self.next()?, self.next()?, self.next()?];
+                    components.push(NameComponent::Segment(NameSeg::from_bytes(name_seg)?));
                 }
-                MULTI_NAME_PREFIX => {
-                    let count = self.next()?;
-                    for _ in 0..count {
-                        let name_seg = [self.next()?, self.next()?, self.next()?, self.next()?];
-                        components.push(NameComponent::Segment(NameSeg::from_bytes(name_seg)?));
-                    }
+            }
+            MULTI_NAME_PREFIX => {
+                let count = self.next()?;
+                for _ in 0..count {
+                    let name_seg = [self.next()?, self.next()?, self.next()?, self.next()?];
+                    components.push(NameComponent::Segment(NameSeg::from_bytes(name_seg)?));
                 }
-                first_char => {
-                    if !namespace::is_lead_name_char(first_char) {
-                        self.current_block.pc -= 1;
-                        break;
-                    }
-                    let name_seg = [first_char, self.next()?, self.next()?, self.next()?];
-                    components.push(namespace::NameComponent::Segment(namespace::NameSeg::from_bytes(name_seg)?));
+            }
+            first_char => {
+                if !namespace::is_lead_name_char(first_char) {
+                    self.current_block.pc -= 1;
                 }
+                let name_seg = [first_char, self.next()?, self.next()?, self.next()?];
+                components.push(namespace::NameComponent::Segment(namespace::NameSeg::from_bytes(name_seg)?));
             }
         }
 
@@ -1015,7 +1027,7 @@ mod tests {
     #[test]
     fn names() {
         assert_eq!(
-            MethodContext::new(b"\\\x2eABC_DEF_").namestring(),
+            MethodContext::new(b"\\\x2eABC_DEF_\0").namestring(),
             Ok(AmlName::from_str("\\ABC.DEF").unwrap())
         );
         assert_eq!(

From 2dd835f5dcdf41935772ffae921ed780b4c21636 Mon Sep 17 00:00:00 2001
From: Isaac Woods <isaacwoods.home@gmail.com>
Date: Fri, 7 Feb 2025 20:50:02 +0000
Subject: [PATCH 20/88] `aml`: handle `DefBuffer`

---
 aml/src/lib.rs | 37 ++++++++++++++++++++++++++++++++++++-
 1 file changed, 36 insertions(+), 1 deletion(-)

diff --git a/aml/src/lib.rs b/aml/src/lib.rs
index 6dc2726f..5cda6e5e 100644
--- a/aml/src/lib.rs
+++ b/aml/src/lib.rs
@@ -120,6 +120,31 @@ impl Interpreter {
                         // TODO: convert offset and length to integers
                         // TODO: add to namespace
                     }
+                    Opcode::Buffer => {
+                        let [
+                            Argument::TrackedPc(start_pc),
+                            Argument::PkgLength(pkg_length),
+                            Argument::Object(buffer_size),
+                        ] = &op.arguments[..]
+                        else {
+                            panic!()
+                        };
+                        let Object::Integer(buffer_size) = **buffer_size else { panic!() };
+
+                        let buffer_len = pkg_length - (context.current_block.pc - start_pc);
+                        let mut buffer = vec![0; buffer_size as usize];
+                        buffer[0..buffer_len].copy_from_slice(
+                            &context.current_block.stream
+                                [context.current_block.pc..(context.current_block.pc + buffer_len)],
+                        );
+                        context.current_block.pc += buffer_len;
+
+                        if let Some(prev_op) = context.in_flight.last_mut() {
+                            if prev_op.arguments.len() < prev_op.expected_arguments {
+                                prev_op.arguments.push(Argument::Object(Arc::new(Object::Buffer(buffer))));
+                            }
+                        }
+                    }
                     Opcode::Package => {
                         // Nothing to do here. The package is created by the block ending.
                     }
@@ -241,7 +266,15 @@ impl Interpreter {
                     let old_scope = mem::replace(&mut context.current_scope, new_scope);
                     context.start_new_block(BlockKind::Scope { old_scope }, remaining_length);
                 }
-                Opcode::Buffer => todo!(),
+                Opcode::Buffer => {
+                    let start_pc = context.current_block.pc;
+                    let pkg_length = context.pkglength()?;
+                    context.start_in_flight_op(OpInFlight::new_with(
+                        Opcode::Buffer,
+                        vec![Argument::TrackedPc(start_pc), Argument::PkgLength(pkg_length)],
+                        1,
+                    ));
+                }
                 Opcode::Package => {
                     let start_pc = context.current_block.pc;
                     let pkg_length = context.pkglength()?;
@@ -486,6 +519,8 @@ pub enum Argument {
     Object(Arc<Object>),
     Namestring(AmlName),
     ByteData(u8),
+    TrackedPc(usize),
+    PkgLength(usize),
 }
 
 pub struct Block<'a> {

From fa332bab842289bab2194880e1240d2300ef672e Mon Sep 17 00:00:00 2001
From: Isaac Woods <isaacwoods.home@gmail.com>
Date: Sun, 9 Feb 2025 23:37:34 +0000
Subject: [PATCH 21/88] `aml`: fix handling of package creation

---
 aml/src/lib.rs | 50 ++++++++++++++++++++++++++++----------------------
 1 file changed, 28 insertions(+), 22 deletions(-)

diff --git a/aml/src/lib.rs b/aml/src/lib.rs
index 5cda6e5e..e02fc065 100644
--- a/aml/src/lib.rs
+++ b/aml/src/lib.rs
@@ -146,7 +146,19 @@ impl Interpreter {
                         }
                     }
                     Opcode::Package => {
-                        // Nothing to do here. The package is created by the block ending.
+                        let mut elements = Vec::with_capacity(op.expected_arguments);
+                        for arg in &op.arguments {
+                            let Argument::Object(object) = arg else { panic!() };
+                            elements.push(object.clone());
+                        }
+
+                        if let Some(prev_op) = context.in_flight.last_mut() {
+                            if prev_op.arguments.len() < prev_op.expected_arguments {
+                                prev_op.arguments.push(Argument::Object(Arc::new(Object::Package(elements))));
+                            } else {
+                                panic!("Random package floating around?");
+                            }
+                        }
                     }
                     _ => panic!("Unexpected operation has created in-flight op!"),
                 }
@@ -175,32 +187,26 @@ impl Interpreter {
                             continue;
                         }
                         BlockKind::Package => {
+                            /*
+                             * We've reached the end of the package. The in-flight op may have
+                             * already been completed in the case of the package specifying all of
+                             * its elements, or reach the end of the block here if it does not.
+                             *
+                             * In the latter case, fill in the rest of the package with
+                             * *distinct* uninitialized objects, and go round again to complete the
+                             * in-flight op.
+                             */
                             assert!(context.block_stack.len() > 0);
 
-                            // Pop the in-flight operation off and make sure it's the package
-                            let package_op = context.in_flight.pop().unwrap();
-                            assert_eq!(package_op.op, Opcode::Package);
-
-                            let mut elements = Vec::with_capacity(package_op.expected_arguments);
-                            for arg in &package_op.arguments {
-                                let Argument::Object(object) = arg else { panic!() };
-                                elements.push(object.clone());
-                            }
-                            for _ in package_op.arguments.len()..package_op.expected_arguments {
-                                // Each uninitialized element must be a distinct object
-                                elements.push(Arc::new(Object::Uninitialized));
-                            }
-
-                            // Add the created package to the last in-flight op's arguments
-                            if let Some(prev_op) = context.in_flight.last_mut() {
-                                if prev_op.arguments.len() < prev_op.expected_arguments {
-                                    prev_op.arguments.push(Argument::Object(Arc::new(Object::Package(elements))));
-                                } else {
-                                    panic!("Random package floating around?");
+                            if let Some(package_op) = context.in_flight.last_mut()
+                                && package_op.op == Opcode::Package
+                            {
+                                let num_elements_left = package_op.expected_arguments - package_op.arguments.len();
+                                for _ in 0..num_elements_left {
+                                    package_op.arguments.push(Argument::Object(Arc::new(Object::Uninitialized)));
                                 }
                             }
 
-                            // End the block, go round the loop again
                             context.current_block = context.block_stack.pop().unwrap();
                             continue;
                         }

From 3944926df5e6d52c0665a44b66c43f5b7862cb04 Mon Sep 17 00:00:00 2001
From: Isaac Woods <isaacwoods.home@gmail.com>
Date: Sun, 9 Feb 2025 23:50:37 +0000
Subject: [PATCH 22/88] `aml`: support method calls by eliding lifetimes to AML
 streams

---
 aml/src/lib.rs    | 219 +++++++++++++++++++++++++++++++++++-----------
 aml/src/object.rs |  15 ++++
 tests/method.asl  |   8 ++
 3 files changed, 191 insertions(+), 51 deletions(-)
 create mode 100644 tests/method.asl

diff --git a/aml/src/lib.rs b/aml/src/lib.rs
index e02fc065..bc86cb8f 100644
--- a/aml/src/lib.rs
+++ b/aml/src/lib.rs
@@ -21,6 +21,7 @@ use spinning_top::Spinlock;
 pub struct Interpreter {
     handler: Box<dyn Handler>,
     pub namespace: Spinlock<Namespace>,
+    context_stack: Spinlock<Vec<MethodContext>>,
 }
 
 impl Interpreter {
@@ -28,7 +29,11 @@ impl Interpreter {
     where
         H: Handler + 'static,
     {
-        Interpreter { namespace: Spinlock::new(Namespace::new()), handler: Box::new(handler) }
+        Interpreter {
+            namespace: Spinlock::new(Namespace::new()),
+            handler: Box::new(handler),
+            context_stack: Spinlock::new(Vec::new()),
+        }
     }
 
     pub fn load_table(&self, stream: &[u8]) -> Result<(), AmlError> {
@@ -37,7 +42,7 @@ impl Interpreter {
     }
 
     pub fn execute_method(&self, stream: &[u8]) -> Result<(), AmlError> {
-        let mut context = MethodContext::new(stream);
+        let mut context = unsafe { MethodContext::new_from_table(stream) };
 
         /*
          * TODO
@@ -134,7 +139,7 @@ impl Interpreter {
                         let buffer_len = pkg_length - (context.current_block.pc - start_pc);
                         let mut buffer = vec![0; buffer_size as usize];
                         buffer[0..buffer_len].copy_from_slice(
-                            &context.current_block.stream
+                            &context.current_block.stream()
                                 [context.current_block.pc..(context.current_block.pc + buffer_len)],
                         );
                         context.current_block.pc += buffer_len;
@@ -160,6 +165,34 @@ impl Interpreter {
                             }
                         }
                     }
+                    Opcode::InternalMethodCall => {
+                        let Argument::Object(method) = &op.arguments[0] else { panic!() };
+
+                        let args = op.arguments[1..]
+                            .iter()
+                            .map(|arg| {
+                                if let Argument::Object(arg) = arg {
+                                    arg.clone()
+                                } else {
+                                    panic!();
+                                }
+                            })
+                            .collect();
+
+                        let new_context = MethodContext::new_from_method(method.clone(), args)?;
+                        let old_context = mem::replace(&mut context, new_context);
+                        self.context_stack.lock().push(old_context);
+                    }
+                    Opcode::Return => {
+                        let [Argument::Object(object)] = &op.arguments[..] else { panic!() };
+                        context = self.context_stack.lock().pop().unwrap();
+
+                        if let Some(prev_op) = context.in_flight.last_mut() {
+                            if prev_op.arguments.len() < prev_op.expected_arguments {
+                                prev_op.arguments.push(Argument::Object(object.clone()));
+                            }
+                        }
+                    }
                     _ => panic!("Unexpected operation has created in-flight op!"),
                 }
             }
@@ -176,9 +209,15 @@ impl Interpreter {
                      * depends on what type of block it was.
                      */
                     match context.current_block.kind {
-                        BlockKind::Normal => {
+                        BlockKind::Table => {
                             break Ok(());
                         }
+                        BlockKind::Method => {
+                            // TODO: not sure how to handle no explicit return. Result is undefined
+                            // but we might still need to handle sticking it in an in-flight op?
+                            context = self.context_stack.lock().pop().unwrap();
+                            continue;
+                        }
                         BlockKind::Scope { old_scope } => {
                             assert!(context.block_stack.len() > 0);
                             context.current_block = context.block_stack.pop().unwrap();
@@ -246,13 +285,11 @@ impl Interpreter {
                     let str_start = context.current_block.pc;
                     while context.next()? != b'\0' {}
                     // TODO: handle err
-                    let str =
-                        str::from_utf8(&context.current_block.stream[str_start..(context.current_block.pc - 1)])
-                            .unwrap();
-                    context
-                        .last_op()?
-                        .arguments
-                        .push(Argument::Object(Arc::new(Object::String(String::from(str)))));
+                    let str = String::from(
+                        str::from_utf8(&context.current_block.stream()[str_start..(context.current_block.pc - 1)])
+                            .unwrap(),
+                    );
+                    context.last_op()?.arguments.push(Argument::Object(Arc::new(Object::String(str))));
                 }
                 Opcode::QWordPrefix => {
                     let value = context.next_u64()?;
@@ -307,7 +344,9 @@ impl Interpreter {
                     let flags = MethodFlags(context.next()?);
 
                     let code_len = pkg_length - (context.current_block.pc - start_pc);
-                    let code = context.current_block.stream[start_pc..(start_pc + code_len)].to_vec();
+                    let code = context.current_block.stream()
+                        [context.current_block.pc..(context.current_block.pc + code_len)]
+                        .to_vec();
                     context.current_block.pc += code_len;
 
                     println!("Method. Name={:?}, len={}", name, code_len);
@@ -321,10 +360,6 @@ impl Interpreter {
                     let _arg_count = context.next()?;
                     println!("External. Name = {}, typ={}, arg_count={}", _name, _object_type, _arg_count);
                 }
-                Opcode::DualNamePrefix => todo!(),
-                Opcode::MultiNamePrefix => todo!(),
-                Opcode::Digit(_) => todo!(),
-                Opcode::NameChar(_) => todo!(),
                 Opcode::Mutex => todo!(),
                 Opcode::Event => todo!(),
                 Opcode::CondRefOf => todo!(),
@@ -432,15 +467,37 @@ impl Interpreter {
                 Opcode::IndexField => todo!(),
                 Opcode::BankField => todo!(),
                 Opcode::DataRegion => todo!(),
-                Opcode::RootChar => todo!(),
-                Opcode::ParentPrefixChar => todo!(),
                 Opcode::Local(local) => {
                     let local = context.locals[local as usize].clone();
                     context.last_op()?.arguments.push(Argument::Object(Arc::new(Object::Reference(local))));
                 }
-                Opcode::Arg(arg) => todo!(),
+                Opcode::Arg(arg) => {
+                    let arg = context.args[arg as usize].clone();
+                    context.last_op()?.arguments.push(Argument::Object(Arc::new(Object::Reference(arg))));
+                }
                 Opcode::Store => todo!(),
                 Opcode::RefOf => todo!(),
+
+                Opcode::DualNamePrefix
+                | Opcode::MultiNamePrefix
+                | Opcode::Digit(_)
+                | Opcode::NameChar(_)
+                | Opcode::RootChar
+                | Opcode::ParentPrefixChar => {
+                    context.current_block.pc -= 1;
+                    let name = context.namestring()?;
+
+                    let (_, object) = self.namespace.lock().search(&name, &context.current_scope)?;
+                    if let Object::Method { flags, .. } = *object {
+                        context.start_in_flight_op(OpInFlight::new_with(
+                            Opcode::InternalMethodCall,
+                            vec![Argument::Object(object)],
+                            flags.arg_count(),
+                        ))
+                    } else {
+                        context.last_op()?.arguments.push(Argument::Object(object));
+                    }
+                }
                 Opcode::Add => {
                     context.start_in_flight_op(OpInFlight::new(Opcode::Add, 3));
                 }
@@ -494,34 +551,49 @@ impl Interpreter {
                 Opcode::Else => todo!(),
                 Opcode::While => todo!(),
                 Opcode::Noop => {}
-                Opcode::Return => todo!(),
+                Opcode::Return => context.start_in_flight_op(OpInFlight::new(Opcode::Return, 1)),
                 Opcode::Break => todo!(),
                 Opcode::Breakpoint => todo!(),
                 Opcode::Ones => {
                     context.last_op()?.arguments.push(Argument::Object(Arc::new(Object::Integer(u64::MAX))));
                 }
+
+                Opcode::InternalMethodCall => panic!(),
             }
         }
     }
 }
 
-pub struct MethodContext<'a> {
-    current_block: Block<'a>,
-    block_stack: Vec<Block<'a>>,
+/// A `MethodContext` represents a piece of running AML code - either a real method, or the
+/// top-level of an AML table.
+///
+/// ### Safety
+/// `MethodContext` does not keep the lifetime of the underlying AML stream, which for tables is
+/// borrowed from the underlying physical mapping. This is because the interpreter needs to
+/// pre-empt method contexts that execute other methods, storing pre-empted contexts.
+///
+/// This is made safe in the case of methods by the context holding a reference to the method
+/// object, but must be handled manually for AML tables.
+struct MethodContext {
+    current_block: Block,
+    block_stack: Vec<Block>,
     in_flight: Vec<OpInFlight>,
+    args: [Arc<Object>; 8],
     locals: [Arc<Object>; 8],
     current_scope: AmlName,
+
+    _method: Option<Arc<Object>>,
 }
 
 #[derive(Debug)]
-pub struct OpInFlight {
+struct OpInFlight {
     op: Opcode,
     expected_arguments: usize,
     arguments: Vec<Argument>,
 }
 
 #[derive(Debug)]
-pub enum Argument {
+enum Argument {
     Object(Arc<Object>),
     Namestring(AmlName),
     ByteData(u8),
@@ -529,16 +601,27 @@ pub enum Argument {
     PkgLength(usize),
 }
 
-pub struct Block<'a> {
-    stream: &'a [u8],
+struct Block {
+    stream: *const [u8],
     pc: usize,
     kind: BlockKind,
 }
 
+// TODO: we might need to impl Send + Sync for Block?
+
+impl Block {
+    fn stream(&self) -> &[u8] {
+        unsafe { &*self.stream }
+    }
+}
+
 #[derive(Debug)]
 pub enum BlockKind {
-    Normal,
-    Scope { old_scope: AmlName },
+    Table,
+    Method,
+    Scope {
+        old_scope: AmlName,
+    },
     Package,
 }
 
@@ -552,34 +635,60 @@ impl OpInFlight {
     }
 }
 
-impl<'a> MethodContext<'a> {
-    pub fn new(stream: &'a [u8]) -> MethodContext<'a> {
-        let block = Block { stream, pc: 0, kind: BlockKind::Normal };
+impl MethodContext {
+    unsafe fn new_from_table(stream: &[u8]) -> MethodContext {
+        let block = Block { stream: stream as *const [u8], pc: 0, kind: BlockKind::Table };
         MethodContext {
             current_block: block,
             block_stack: Vec::new(),
             in_flight: Vec::new(),
+            args: core::array::from_fn(|_| Arc::new(Object::Uninitialized)),
             locals: core::array::from_fn(|_| Arc::new(Object::Uninitialized)),
             current_scope: AmlName::root(),
+            _method: None,
         }
     }
 
-    pub fn last_op(&mut self) -> Result<&mut OpInFlight, AmlError> {
+    fn new_from_method(method: Arc<Object>, args: Vec<Arc<Object>>) -> Result<MethodContext, AmlError> {
+        if let Object::Method { code, flags } = &*method {
+            if args.len() != flags.arg_count() {
+                return Err(AmlError::MethodArgCountIncorrect);
+            }
+            let block = Block { stream: code as &[u8] as *const [u8], pc: 0, kind: BlockKind::Method };
+            let args = core::array::from_fn(|i| {
+                if let Some(arg) = args.get(i) { arg.clone() } else { Arc::new(Object::Uninitialized) }
+            });
+            let context = MethodContext {
+                current_block: block,
+                block_stack: Vec::new(),
+                in_flight: Vec::new(),
+                args,
+                locals: core::array::from_fn(|_| Arc::new(Object::Uninitialized)),
+                current_scope: AmlName::root(),
+                _method: Some(method.clone()),
+            };
+            Ok(context)
+        } else {
+            panic!()
+        }
+    }
+
+    fn last_op(&mut self) -> Result<&mut OpInFlight, AmlError> {
         match self.in_flight.last_mut() {
             Some(op) => Ok(op),
             None => Err(AmlError::NoCurrentOp),
         }
     }
 
-    pub fn start_in_flight_op(&mut self, op: OpInFlight) {
+    fn start_in_flight_op(&mut self, op: OpInFlight) {
         println!("Starting in-flight op of type: {:?}", op);
         self.in_flight.push(op);
     }
 
-    pub fn start_new_block(&mut self, kind: BlockKind, length: usize) {
+    fn start_new_block(&mut self, kind: BlockKind, length: usize) {
         println!("Starting new block at pc={}, length={}, kind={:?}", self.current_block.pc, length, kind);
         let block = Block {
-            stream: &self.current_block.stream[..(self.current_block.pc + length)],
+            stream: &self.current_block.stream()[..(self.current_block.pc + length)] as *const [u8],
             pc: self.current_block.pc,
             kind,
         };
@@ -587,7 +696,7 @@ impl<'a> MethodContext<'a> {
         self.block_stack.push(mem::replace(&mut self.current_block, block));
     }
 
-    pub fn opcode(&mut self) -> Result<Opcode, AmlError> {
+    fn opcode(&mut self) -> Result<Opcode, AmlError> {
         let opcode: u16 = match self.next()? {
             0x5b => {
                 let ext = self.next()?;
@@ -722,11 +831,11 @@ impl<'a> MethodContext<'a> {
             0xcc => Opcode::Breakpoint,
             0xff => Opcode::Ones,
 
-            _ => Err(AmlError::IllegalOpcode)?,
+            _ => Err(AmlError::IllegalOpcode(opcode))?,
         })
     }
 
-    pub fn pkglength(&mut self) -> Result<usize, AmlError> {
+    fn pkglength(&mut self) -> Result<usize, AmlError> {
         let lead_byte = self.next()?;
         let byte_count = lead_byte.get_bits(6..8);
         assert!(byte_count < 4);
@@ -742,7 +851,7 @@ impl<'a> MethodContext<'a> {
         }
     }
 
-    pub fn namestring(&mut self) -> Result<AmlName, AmlError> {
+    fn namestring(&mut self) -> Result<AmlName, AmlError> {
         use namespace::{NameComponent, NameSeg};
 
         /*
@@ -804,26 +913,26 @@ impl<'a> MethodContext<'a> {
         Ok(AmlName::from_components(components))
     }
 
-    pub fn next(&mut self) -> Result<u8, AmlError> {
+    fn next(&mut self) -> Result<u8, AmlError> {
         if self.current_block.pc >= self.current_block.stream.len() {
             return Err(AmlError::RunOutOfStream);
         }
 
-        let byte = self.current_block.stream[self.current_block.pc];
+        let byte = self.current_block.stream()[self.current_block.pc];
         self.current_block.pc += 1;
 
         Ok(byte)
     }
 
-    pub fn next_u16(&mut self) -> Result<u16, AmlError> {
+    fn next_u16(&mut self) -> Result<u16, AmlError> {
         Ok(u16::from_le_bytes([self.next()?, self.next()?]))
     }
 
-    pub fn next_u32(&mut self) -> Result<u32, AmlError> {
+    fn next_u32(&mut self) -> Result<u32, AmlError> {
         Ok(u32::from_le_bytes([self.next()?, self.next()?, self.next()?, self.next()?]))
     }
 
-    pub fn next_u64(&mut self) -> Result<u64, AmlError> {
+    fn next_u64(&mut self) -> Result<u64, AmlError> {
         Ok(u64::from_le_bytes([
             self.next()?,
             self.next()?,
@@ -836,17 +945,17 @@ impl<'a> MethodContext<'a> {
         ]))
     }
 
-    pub fn peek(&self) -> Result<u8, AmlError> {
+    fn peek(&self) -> Result<u8, AmlError> {
         if self.current_block.pc >= self.current_block.stream.len() {
             return Err(AmlError::RunOutOfStream);
         }
 
-        Ok(self.current_block.stream[self.current_block.pc])
+        Ok(self.current_block.stream()[self.current_block.pc])
     }
 }
 
 #[derive(Clone, Copy, PartialEq, Debug)]
-pub enum Opcode {
+enum Opcode {
     Zero,
     One,
     Alias,
@@ -954,6 +1063,12 @@ pub enum Opcode {
     Break,
     Breakpoint,
     Ones,
+
+    /*
+     * Internal opcodes are not produced from the bytecode, but are used to track special in-flight
+     * ops etc.
+     */
+    InternalMethodCall,
 }
 
 /*
@@ -962,7 +1077,7 @@ pub enum Opcode {
 #[derive(Clone, PartialEq, Debug)]
 pub enum AmlError {
     RunOutOfStream,
-    IllegalOpcode,
+    IllegalOpcode(u16),
 
     InvalidName(Option<AmlName>),
 
@@ -975,6 +1090,8 @@ pub enum AmlError {
     ObjectDoesNotExist(AmlName),
 
     NoCurrentOp,
+
+    MethodArgCountIncorrect,
 }
 
 /// This trait represents the interface from the `Interpreter` to the hosting kernel, and allows
@@ -1068,11 +1185,11 @@ mod tests {
     #[test]
     fn names() {
         assert_eq!(
-            MethodContext::new(b"\\\x2eABC_DEF_\0").namestring(),
+            unsafe { MethodContext::new_from_table(b"\\\x2eABC_DEF_\0") }.namestring(),
             Ok(AmlName::from_str("\\ABC.DEF").unwrap())
         );
         assert_eq!(
-            MethodContext::new(b"\x2eABC_DEF_^_GHI").namestring(),
+            unsafe { MethodContext::new_from_table(b"\x2eABC_DEF_^_GHI") }.namestring(),
             Ok(AmlName::from_str("ABC.DEF.^_GHI").unwrap())
         );
     }
diff --git a/aml/src/object.rs b/aml/src/object.rs
index 1b49b89c..5c97d840 100644
--- a/aml/src/object.rs
+++ b/aml/src/object.rs
@@ -1,5 +1,6 @@
 use crate::op_region::OpRegion;
 use alloc::{sync::Arc, vec::Vec};
+use bit_field::BitField;
 
 #[derive(Debug)]
 pub enum Object {
@@ -45,3 +46,17 @@ impl Object {
 
 #[derive(Clone, Copy, Debug)]
 pub struct MethodFlags(pub u8);
+
+impl MethodFlags {
+    pub fn arg_count(&self) -> usize {
+        self.0.get_bits(0..3) as usize
+    }
+
+    pub fn serialize(&self) -> bool {
+        self.0.get_bit(3)
+    }
+
+    pub fn sync_level(&self) -> u8 {
+        self.0.get_bits(4..8)
+    }
+}
diff --git a/tests/method.asl b/tests/method.asl
new file mode 100644
index 00000000..9c208325
--- /dev/null
+++ b/tests/method.asl
@@ -0,0 +1,8 @@
+DefinitionBlock("method.aml", "DSDT", 1, "RSACPI", "METHOD", 1) {
+    Method(FOO, 0, NotSerialized) {
+        Return (0xff)
+    }
+
+    Name(X, 0)
+    X = FOO()
+}

From a1bccccb7144ed9dd6f7be9777a71442a47d4f95 Mon Sep 17 00:00:00 2001
From: Isaac Woods <isaacwoods.home@gmail.com>
Date: Sun, 9 Feb 2025 23:52:54 +0000
Subject: [PATCH 23/88] `aml`: support `DefIfElse`

---
 aml/src/lib.rs | 56 ++++++++++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 54 insertions(+), 2 deletions(-)

diff --git a/aml/src/lib.rs b/aml/src/lib.rs
index bc86cb8f..a6cdb64c 100644
--- a/aml/src/lib.rs
+++ b/aml/src/lib.rs
@@ -165,6 +165,31 @@ impl Interpreter {
                             }
                         }
                     }
+                    Opcode::If => {
+                        let [
+                            Argument::TrackedPc(start_pc),
+                            Argument::PkgLength(then_length),
+                            Argument::Object(predicate),
+                        ] = &op.arguments[..]
+                        else {
+                            panic!()
+                        };
+
+                        let Object::Integer(predicate) = **predicate else { panic!() };
+                        let remaining_then_length = then_length - (context.current_block.pc - start_pc);
+
+                        if predicate > 0 {
+                            context.start_new_block(BlockKind::IfThenBranch, remaining_then_length);
+                        } else {
+                            context.current_block.pc += remaining_then_length;
+                            // Skip over the prolog to the else branch if present
+                            const DEF_ELSE_OP: u8 = 0xa1;
+                            // TODO: maybe need to handle error here
+                            if context.peek()? == DEF_ELSE_OP {
+                                let _else_length = context.pkglength()?;
+                            }
+                        }
+                    }
                     Opcode::InternalMethodCall => {
                         let Argument::Object(method) = &op.arguments[0] else { panic!() };
 
@@ -249,6 +274,21 @@ impl Interpreter {
                             context.current_block = context.block_stack.pop().unwrap();
                             continue;
                         }
+                        BlockKind::IfThenBranch => {
+                            context.current_block = context.block_stack.pop().unwrap();
+
+                            // Check for an else-branch, and skip over it
+                            // TODO: if we run out of stream here, it might just be an IfOp at the
+                            // end I think?
+                            let start_pc = context.current_block.pc;
+                            const DEF_ELSE_OP: u8 = 0xa1;
+                            if context.peek()? == DEF_ELSE_OP {
+                                let else_length = context.pkglength()? - (context.current_block.pc - start_pc);
+                                context.current_block.pc += else_length;
+                            }
+
+                            continue;
+                        }
                     }
                 }
                 Err(other_err) => return Err(other_err),
@@ -547,8 +587,17 @@ impl Interpreter {
                 Opcode::CopyObject => todo!(),
                 Opcode::Mid => todo!(),
                 Opcode::Continue => todo!(),
-                Opcode::If => todo!(),
-                Opcode::Else => todo!(),
+                Opcode::If => {
+                    let start_pc = context.current_block.pc;
+                    let then_length = context.pkglength()?;
+                    context.start_in_flight_op(OpInFlight::new_with(
+                        Opcode::If,
+                        vec![Argument::TrackedPc(start_pc), Argument::PkgLength(then_length)],
+                        1,
+                    ));
+                }
+                // TODO: maybe should be a normal error instead
+                Opcode::Else => panic!("Unexpected DefElseOp without corresponding DefIfElseOp"),
                 Opcode::While => todo!(),
                 Opcode::Noop => {}
                 Opcode::Return => context.start_in_flight_op(OpInFlight::new(Opcode::Return, 1)),
@@ -623,6 +672,9 @@ pub enum BlockKind {
         old_scope: AmlName,
     },
     Package,
+    /// Used for executing the then-branch of an `DefIfElse`. After finishing, it will check for
+    /// and skip over an else-branch, if present.
+    IfThenBranch,
 }
 
 impl OpInFlight {

From 7c9931b11f523d1561d31e3fc72a1cc9e7cf82e4 Mon Sep 17 00:00:00 2001
From: Isaac Woods <isaacwoods.home@gmail.com>
Date: Sun, 9 Feb 2025 23:55:47 +0000
Subject: [PATCH 24/88] `aml`: support creation of buffer field objects

Still need to do a bunch of bit fiddling stuff to facilitate actually reading
and storing to them.
---
 aml/src/lib.rs    | 65 +++++++++++++++++++++++++++++++++++++++++++----
 aml/src/object.rs |  2 +-
 2 files changed, 61 insertions(+), 6 deletions(-)

diff --git a/aml/src/lib.rs b/aml/src/lib.rs
index a6cdb64c..1e75663f 100644
--- a/aml/src/lib.rs
+++ b/aml/src/lib.rs
@@ -190,6 +190,59 @@ impl Interpreter {
                             }
                         }
                     }
+                    opcode @ Opcode::CreateBitField
+                    | opcode @ Opcode::CreateByteField
+                    | opcode @ Opcode::CreateWordField
+                    | opcode @ Opcode::CreateDWordField
+                    | opcode @ Opcode::CreateQWordField => {
+                        let [Argument::Object(buffer), Argument::Object(index)] = &op.arguments[..] else {
+                            panic!()
+                        };
+                        let name = context.namestring()?;
+                        let Object::Integer(index) = **index else { panic!() };
+                        println!(
+                            "CreateBitField(or adjacent). buffer = {:?}, bit/byte_index={:?}, name={:?}",
+                            buffer, index, name
+                        );
+                        let (offset, length) = match opcode {
+                            Opcode::CreateBitField => (index, 1),
+                            Opcode::CreateByteField => (index * 8, 8),
+                            Opcode::CreateWordField => (index * 8, 16),
+                            Opcode::CreateDWordField => (index * 8, 32),
+                            Opcode::CreateQWordField => (index * 8, 64),
+                            _ => unreachable!(),
+                        };
+                        self.namespace.lock().insert(
+                            name.resolve(&context.current_scope)?,
+                            Arc::new(Object::BufferField {
+                                buffer: buffer.clone(),
+                                offset: offset as usize,
+                                length,
+                            }),
+                        )?;
+                    }
+                    Opcode::CreateField => {
+                        let [Argument::Object(buffer), Argument::Object(bit_index), Argument::Object(num_bits)] =
+                            &op.arguments[..]
+                        else {
+                            panic!()
+                        };
+                        let name = context.namestring()?;
+                        let Object::Integer(bit_index) = **bit_index else { panic!() };
+                        let Object::Integer(num_bits) = **num_bits else { panic!() };
+                        println!(
+                            "CreateBitField(or adjacent). buffer = {:?}, bit/byte_index={:?}, num_bits={:?}, name={:?}",
+                            buffer, bit_index, num_bits, name
+                        );
+                        self.namespace.lock().insert(
+                            name.resolve(&context.current_scope)?,
+                            Arc::new(Object::BufferField {
+                                buffer: buffer.clone(),
+                                offset: bit_index as usize,
+                                length: num_bits as usize,
+                            }),
+                        )?;
+                    }
                     Opcode::InternalMethodCall => {
                         let Argument::Object(method) = &op.arguments[0] else { panic!() };
 
@@ -403,7 +456,6 @@ impl Interpreter {
                 Opcode::Mutex => todo!(),
                 Opcode::Event => todo!(),
                 Opcode::CondRefOf => todo!(),
-                Opcode::CreateField => todo!(),
                 Opcode::LoadTable => todo!(),
                 Opcode::Load => todo!(),
                 Opcode::Stall => todo!(),
@@ -564,10 +616,13 @@ impl Interpreter {
                 Opcode::SizeOf => todo!(),
                 Opcode::Index => todo!(),
                 Opcode::Match => todo!(),
-                Opcode::CreateDWordField => todo!(),
-                Opcode::CreateWordField => todo!(),
-                Opcode::CreateByteField => todo!(),
-                Opcode::CreateBitField => todo!(),
+
+                Opcode::CreateBitField
+                | Opcode::CreateByteField
+                | Opcode::CreateWordField
+                | Opcode::CreateDWordField
+                | Opcode::CreateQWordField => context.start_in_flight_op(OpInFlight::new(opcode, 2)),
+                Opcode::CreateField => context.start_in_flight_op(OpInFlight::new(Opcode::CreateField, 3)),
                 Opcode::ObjectType => todo!(),
                 Opcode::CreateQWordField => todo!(),
                 Opcode::LAnd => todo!(),
diff --git a/aml/src/object.rs b/aml/src/object.rs
index 5c97d840..22741ebc 100644
--- a/aml/src/object.rs
+++ b/aml/src/object.rs
@@ -6,7 +6,7 @@ use bit_field::BitField;
 pub enum Object {
     Uninitialized,
     Buffer(Vec<u8>),
-    BufferField,
+    BufferField { buffer: Arc<Object>, offset: usize, length: usize },
     Device,
     Event,
     FieldUnit,

From b77079b47fa353adb227a5dc4dfa43a90137dec6 Mon Sep 17 00:00:00 2001
From: Isaac Woods <isaacwoods.home@gmail.com>
Date: Sun, 9 Feb 2025 23:58:16 +0000
Subject: [PATCH 25/88] `aml`: general parsing cleanup + little bits

---
 aml/src/lib.rs          | 193 ++++++++++++++++++++++++----------------
 aml/src/namespace.rs    |  59 ++++++++++--
 aml/src/object.rs       |   9 +-
 aml/src/op_region.rs    |   6 +-
 tests/buffer_fields.asl |  38 ++++----
 tests/power_res.asl     |  28 +++---
 tests/scopes.asl        |  22 ++---
 tests/thermal_zone.asl  |  72 +++++++--------
 8 files changed, 261 insertions(+), 166 deletions(-)

diff --git a/aml/src/lib.rs b/aml/src/lib.rs
index 1e75663f..b6ae3eb9 100644
--- a/aml/src/lib.rs
+++ b/aml/src/lib.rs
@@ -15,7 +15,7 @@ use bit_field::BitField;
 use core::{mem, str};
 use namespace::{AmlName, Namespace, NamespaceLevelKind};
 use object::{MethodFlags, Object};
-use op_region::RegionSpace;
+use op_region::{OpRegion, RegionSpace};
 use spinning_top::Spinlock;
 
 pub struct Interpreter {
@@ -95,13 +95,11 @@ impl Interpreter {
                                 prev_op.arguments.push(Argument::Object(Arc::new(Object::Integer(left + right))));
                             }
                         }
-                        println!("Result: {}", left + right);
                     }
                     Opcode::Name => {
                         let [Argument::Namestring(name), Argument::Object(object)] = &op.arguments[..] else {
                             panic!()
                         };
-                        println!("Making Name with name {} and object: {:?}", name, object);
 
                         let name = name.resolve(&context.current_scope)?;
                         self.namespace.lock().insert(name, object.clone())?;
@@ -116,14 +114,17 @@ impl Interpreter {
                         else {
                             panic!()
                         };
+                        let Object::Integer(region_offset) = **region_offset else { panic!() };
+                        let Object::Integer(region_length) = **region_length else { panic!() };
 
                         let region_space = RegionSpace::from(*region_space);
-                        println!(
-                            "OpRegion. name={}, space={:?}, offset={:?}, length={:?}",
-                            name, region_space, region_offset, region_length
-                        );
-                        // TODO: convert offset and length to integers
-                        // TODO: add to namespace
+
+                        let region = Object::OpRegion(OpRegion {
+                            space: region_space,
+                            base: region_offset,
+                            length: region_length,
+                        });
+                        self.namespace.lock().insert(name.resolve(&context.current_scope)?, Arc::new(region))?;
                     }
                     Opcode::Buffer => {
                         let [
@@ -200,10 +201,6 @@ impl Interpreter {
                         };
                         let name = context.namestring()?;
                         let Object::Integer(index) = **index else { panic!() };
-                        println!(
-                            "CreateBitField(or adjacent). buffer = {:?}, bit/byte_index={:?}, name={:?}",
-                            buffer, index, name
-                        );
                         let (offset, length) = match opcode {
                             Opcode::CreateBitField => (index, 1),
                             Opcode::CreateByteField => (index * 8, 8),
@@ -230,10 +227,6 @@ impl Interpreter {
                         let name = context.namestring()?;
                         let Object::Integer(bit_index) = **bit_index else { panic!() };
                         let Object::Integer(num_bits) = **num_bits else { panic!() };
-                        println!(
-                            "CreateBitField(or adjacent). buffer = {:?}, bit/byte_index={:?}, num_bits={:?}, name={:?}",
-                            buffer, bit_index, num_bits, name
-                        );
                         self.namespace.lock().insert(
                             name.resolve(&context.current_scope)?,
                             Arc::new(Object::BufferField {
@@ -243,6 +236,20 @@ impl Interpreter {
                             }),
                         )?;
                     }
+                    Opcode::Store => {
+                        let [Argument::Object(object), target] = &op.arguments[..] else { panic!() };
+                        self.do_store(&mut context, &target, object.clone())?;
+                    }
+                    Opcode::Sleep => {
+                        let [Argument::Object(msec)] = &op.arguments[..] else { panic!() };
+                        let Object::Integer(msec) = **msec else { panic!() };
+                        self.handler.sleep(msec);
+                    }
+                    Opcode::Stall => {
+                        let [Argument::Object(usec)] = &op.arguments[..] else { panic!() };
+                        let Object::Integer(usec) = **usec else { panic!() };
+                        self.handler.stall(usec);
+                    }
                     Opcode::InternalMethodCall => {
                         let Argument::Object(method) = &op.arguments[0] else { panic!() };
 
@@ -398,7 +405,6 @@ impl Interpreter {
                     let new_scope = name.resolve(&context.current_scope)?;
                     self.namespace.lock().add_level(new_scope.clone(), NamespaceLevelKind::Scope)?;
 
-                    println!("Scope. length = {}({}), name = {:?}", pkg_length, remaining_length, new_scope);
                     let old_scope = mem::replace(&mut context.current_scope, new_scope);
                     context.start_new_block(BlockKind::Scope { old_scope }, remaining_length);
                 }
@@ -417,7 +423,6 @@ impl Interpreter {
                     let num_elements = context.next()?;
 
                     let remaining_length = pkg_length - (context.current_block.pc - start_pc);
-                    println!("Package. num elements = {}", num_elements);
 
                     /*
                      * We now need to interpret an arbitrary number of package elements, bounded by
@@ -442,8 +447,6 @@ impl Interpreter {
                         .to_vec();
                     context.current_block.pc += code_len;
 
-                    println!("Method. Name={:?}, len={}", name, code_len);
-
                     let name = name.resolve(&context.current_scope)?;
                     self.namespace.lock().insert(name, Arc::new(Object::Method { code, flags }))?;
                 }
@@ -451,15 +454,14 @@ impl Interpreter {
                     let _name = context.namestring()?;
                     let _object_type = context.next()?;
                     let _arg_count = context.next()?;
-                    println!("External. Name = {}, typ={}, arg_count={}", _name, _object_type, _arg_count);
                 }
                 Opcode::Mutex => todo!(),
                 Opcode::Event => todo!(),
                 Opcode::CondRefOf => todo!(),
                 Opcode::LoadTable => todo!(),
                 Opcode::Load => todo!(),
-                Opcode::Stall => todo!(),
-                Opcode::Sleep => todo!(),
+                Opcode::Stall => context.start_in_flight_op(OpInFlight::new(Opcode::Stall, 1)),
+                Opcode::Sleep => context.start_in_flight_op(OpInFlight::new(Opcode::Sleep, 1)),
                 Opcode::Acquire => todo!(),
                 Opcode::Signal => todo!(),
                 Opcode::Wait => todo!(),
@@ -483,10 +485,10 @@ impl Interpreter {
                 Opcode::Field => {
                     let start_pc = context.current_block.pc;
                     let pkg_length = context.pkglength()?;
-                    let name = context.namestring()?;
+                    let region_name = context.namestring()?;
                     let flags = context.next()?;
 
-                    println!("Parsing field list. NAme = {:?}", name);
+                    let region = self.namespace.lock().get(region_name.resolve(&context.current_scope)?)?.clone();
 
                     const RESERVED_FIELD: u8 = 0x00;
                     const ACCESS_FIELD: u8 = 0x01;
@@ -500,34 +502,41 @@ impl Interpreter {
                             RESERVED_FIELD => {
                                 let length = context.pkglength()?;
                                 field_offset += length;
-                                println!("Reserved field. len={}", length);
                             }
                             ACCESS_FIELD => {
                                 let access_type = context.next()?;
                                 let access_attrib = context.next()?;
-                                println!("Access field. typ={}, attrib={}", access_type, access_attrib);
+                                todo!()
                                 // TODO
                             }
                             CONNECT_FIELD => {
                                 // TODO: either consume a namestring or `BufferData` (it's not
                                 // clear what a buffer data acc is lmao)
-                                println!("Connect field :(");
+                                todo!("Connect field :(");
                             }
                             EXTENDED_ACCESS_FIELD => {
-                                println!("Extended access field :(");
-                                todo!()
+                                todo!("Extended access field :(");
                             }
                             _ => {
                                 context.current_block.pc -= 1;
                                 // TODO: this should only ever be a nameseg really...
                                 let field_name = context.namestring()?;
                                 let field_length = context.pkglength()?;
-                                println!("NAmed field. NAme={:?}, len={}", field_name, field_length);
+                                // TODO: do something with the flags, and also detect when this
+                                // should be a bank or index field
+                                let field = Object::FieldUnit(object::FieldUnit::Normal {
+                                    region: region.clone(),
+                                    bit_index: field_offset,
+                                    bit_length: field_length,
+                                });
+                                self.namespace
+                                    .lock()
+                                    .insert(field_name.resolve(&context.current_scope)?, Arc::new(field))?;
                             }
                         }
                     }
                 }
-                Opcode::Device => {
+                Opcode::Device | Opcode::ThermalZone => {
                     let start_pc = context.current_block.pc;
                     let pkg_length = context.pkglength()?;
                     let name = context.namestring()?;
@@ -535,24 +544,32 @@ impl Interpreter {
                     let remaining_length = pkg_length - (context.current_block.pc - start_pc);
 
                     let new_scope = name.resolve(&context.current_scope)?;
-                    self.namespace.lock().add_level(new_scope.clone(), NamespaceLevelKind::Device)?;
+                    let (kind, object) = match opcode {
+                        Opcode::Device => (NamespaceLevelKind::Device, Object::Device),
+                        Opcode::ThermalZone => (NamespaceLevelKind::ThermalZone, Object::ThermalZone),
+                        _ => unreachable!(),
+                    };
+                    let mut namespace = self.namespace.lock();
+                    namespace.add_level(new_scope.clone(), kind)?;
+                    namespace.insert(new_scope.clone(), Arc::new(object))?;
 
-                    println!("Device. length = {}({}), name = {:?}", pkg_length, remaining_length, new_scope);
                     let old_scope = mem::replace(&mut context.current_scope, new_scope);
                     context.start_new_block(BlockKind::Scope { old_scope }, remaining_length);
                 }
-                Opcode::PowerRes => todo!(),
-                Opcode::ThermalZone => {
+                Opcode::PowerRes => {
                     let start_pc = context.current_block.pc;
                     let pkg_length = context.pkglength()?;
                     let name = context.namestring()?;
+                    let system_level = context.next()?;
+                    let resource_order = context.next_u16()?;
 
                     let remaining_length = pkg_length - (context.current_block.pc - start_pc);
 
                     let new_scope = name.resolve(&context.current_scope)?;
-                    self.namespace.lock().add_level(new_scope.clone(), NamespaceLevelKind::ThermalZone)?;
+                    let mut namespace = self.namespace.lock();
+                    namespace.add_level(new_scope.clone(), NamespaceLevelKind::PowerResource)?;
+                    namespace.insert(new_scope.clone(), Arc::new(Object::PowerResource))?;
 
-                    println!("ThermalZone. length = {}({}), name = {:?}", pkg_length, remaining_length, new_scope);
                     let old_scope = mem::replace(&mut context.current_scope, new_scope);
                     context.start_new_block(BlockKind::Scope { old_scope }, remaining_length);
                 }
@@ -567,7 +584,7 @@ impl Interpreter {
                     let arg = context.args[arg as usize].clone();
                     context.last_op()?.arguments.push(Argument::Object(Arc::new(Object::Reference(arg))));
                 }
-                Opcode::Store => todo!(),
+                Opcode::Store => context.start_in_flight_op(OpInFlight::new(Opcode::Store, 2)),
                 Opcode::RefOf => todo!(),
 
                 Opcode::DualNamePrefix
@@ -590,28 +607,30 @@ impl Interpreter {
                         context.last_op()?.arguments.push(Argument::Object(object));
                     }
                 }
-                Opcode::Add => {
-                    context.start_in_flight_op(OpInFlight::new(Opcode::Add, 3));
+
+                Opcode::Add
+                | Opcode::Subtract
+                | Opcode::Multiply
+                | Opcode::ShiftLeft
+                | Opcode::ShiftRight
+                | Opcode::Mod
+                | Opcode::Nand
+                | Opcode::And
+                | Opcode::Or
+                | Opcode::Nor
+                | Opcode::Xor
+                | Opcode::Concat => {
+                    context.start_in_flight_op(OpInFlight::new(opcode, 3));
+                }
+
+                Opcode::Divide => context.start_in_flight_op(OpInFlight::new(Opcode::Divide, 4)),
+                Opcode::Increment | Opcode::Decrement => context.start_in_flight_op(OpInFlight::new(opcode, 2)),
+                Opcode::Not => context.start_in_flight_op(OpInFlight::new(Opcode::Not, 2)),
+                Opcode::FindSetLeftBit | Opcode::FindSetRightBit => {
+                    context.start_in_flight_op(OpInFlight::new(opcode, 2))
                 }
-                Opcode::Concat => todo!(),
-                Opcode::Subtract => todo!(),
-                Opcode::Increment => todo!(),
-                Opcode::Decrement => todo!(),
-                Opcode::Multiply => todo!(),
-                Opcode::Divide => todo!(),
-                Opcode::ShiftLeft => todo!(),
-                Opcode::ShiftRight => todo!(),
-                Opcode::And => todo!(),
-                Opcode::Nand => todo!(),
-                Opcode::Or => todo!(),
-                Opcode::Nor => todo!(),
-                Opcode::Xor => todo!(),
-                Opcode::Not => todo!(),
-                Opcode::FindSetLeftBit => todo!(),
-                Opcode::FindSetRightBit => todo!(),
                 Opcode::DerefOf => todo!(),
                 Opcode::ConcatRes => todo!(),
-                Opcode::Mod => todo!(),
                 Opcode::Notify => todo!(),
                 Opcode::SizeOf => todo!(),
                 Opcode::Index => todo!(),
@@ -623,24 +642,28 @@ impl Interpreter {
                 | Opcode::CreateDWordField
                 | Opcode::CreateQWordField => context.start_in_flight_op(OpInFlight::new(opcode, 2)),
                 Opcode::CreateField => context.start_in_flight_op(OpInFlight::new(Opcode::CreateField, 3)),
+
+                Opcode::LAnd
+                | Opcode::LOr
+                | Opcode::LNot
+                | Opcode::LNotEqual
+                | Opcode::LLessEqual
+                | Opcode::LGreaterEqual
+                | Opcode::LEqual
+                | Opcode::LGreater
+                | Opcode::LLess => {
+                    context.start_in_flight_op(OpInFlight::new(opcode, 2));
+                }
+
+                Opcode::ToBuffer
+                | Opcode::ToDecimalString
+                | Opcode::ToHexString
+                | Opcode::ToInteger
+                | Opcode::ToString => context.start_in_flight_op(OpInFlight::new(opcode, 2)),
+
                 Opcode::ObjectType => todo!(),
-                Opcode::CreateQWordField => todo!(),
-                Opcode::LAnd => todo!(),
-                Opcode::LOr => todo!(),
-                Opcode::LNot => todo!(),
-                Opcode::LNotEqual => todo!(),
-                Opcode::LLessEqual => todo!(),
-                Opcode::LGreaterEqual => todo!(),
-                Opcode::LEqual => todo!(),
-                Opcode::LGreater => todo!(),
-                Opcode::LLess => todo!(),
-                Opcode::ToBuffer => todo!(),
-                Opcode::ToDecimalString => todo!(),
-                Opcode::ToHexString => todo!(),
-                Opcode::ToInteger => todo!(),
-                Opcode::ToString => todo!(),
                 Opcode::CopyObject => todo!(),
-                Opcode::Mid => todo!(),
+                Opcode::Mid => context.start_in_flight_op(OpInFlight::new(Opcode::Mid, 4)),
                 Opcode::Continue => todo!(),
                 Opcode::If => {
                     let start_pc = context.current_block.pc;
@@ -666,6 +689,24 @@ impl Interpreter {
             }
         }
     }
+    fn do_store(
+        &self,
+        context: &mut MethodContext,
+        target: &Argument,
+        object: Arc<Object>,
+    ) -> Result<(), AmlError> {
+        // TODO: find the destination (need to handle references, debug objects, etc.)
+        // TODO: convert object to be of the type of destination, in line with 19.3.5 of the spec
+        // TODO: write the object to the destination, including e.g. field writes that then lead to
+        // literally god knows what.
+        match target {
+            Argument::Object(_) => {}
+            Argument::Namestring(_) => {}
+
+            Argument::ByteData(_) | Argument::TrackedPc(_) | Argument::PkgLength(_) => panic!(),
+        }
+        Ok(())
+    }
 }
 
 /// A `MethodContext` represents a piece of running AML code - either a real method, or the
@@ -1188,7 +1229,7 @@ pub enum AmlError {
 
     InvalidName(Option<AmlName>),
 
-    InvalidNameSeg,
+    InvalidNameSeg([u8; 4]),
     InvalidNormalizedName(AmlName),
     RootHasNoParent,
     EmptyNamesAreInvalid,
diff --git a/aml/src/namespace.rs b/aml/src/namespace.rs
index 2bcdcc10..65584c04 100644
--- a/aml/src/namespace.rs
+++ b/aml/src/namespace.rs
@@ -66,6 +66,51 @@ impl Namespace {
         }
     }
 
+    /// Search for an object at the given path of the namespace, applying the search rules described in §5.3 of the
+    /// ACPI specification, if they are applicable. Returns the resolved name, and the handle of the first valid
+    /// object, if found.
+    pub fn search(&self, path: &AmlName, starting_scope: &AmlName) -> Result<(AmlName, Arc<Object>), AmlError> {
+        if path.search_rules_apply() {
+            /*
+             * If search rules apply, we need to recursively look through the namespace. If the
+             * given name does not occur in the current scope, we look at the parent scope, until
+             * we either find the name, or reach the root of the namespace.
+             */
+            let mut scope = starting_scope.clone();
+            assert!(scope.is_absolute());
+            loop {
+                // Search for the name at this namespace level. If we find it, we're done.
+                let name = path.resolve(&scope)?;
+                match self.get_level_for_path(&name) {
+                    Ok((level, last_seg)) => {
+                        if let Some(object) = level.values.get(&last_seg) {
+                            return Ok((name, object.clone()));
+                        }
+                    }
+
+                    Err(err) => return Err(err),
+                }
+
+                // If we don't find it, go up a level in the namespace and search for it there recursively
+                match scope.parent() {
+                    Ok(parent) => scope = parent,
+                    Err(AmlError::RootHasNoParent) => return Err(AmlError::ObjectDoesNotExist(path.clone())),
+                    Err(err) => return Err(err),
+                }
+            }
+        } else {
+            // If search rules don't apply, simply resolve it against the starting scope
+            let name = path.resolve(starting_scope)?;
+            let (level, last_seg) = self.get_level_for_path(&path.resolve(starting_scope)?)?;
+
+            if let Some(object) = level.values.get(&last_seg) {
+                Ok((name, object.clone()))
+            } else {
+                Err(AmlError::ObjectDoesNotExist(path.clone()))
+            }
+        }
+    }
+
     /// 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(&self, path: &AmlName) -> Result<(&NamespaceLevel, NameSeg), AmlError> {
@@ -342,7 +387,7 @@ impl NameSeg {
     pub fn from_str(string: &str) -> Result<NameSeg, AmlError> {
         // Each NameSeg can only have four chars, and must have at least one
         if string.is_empty() || string.len() > 4 {
-            return Err(AmlError::InvalidNameSeg);
+            return Err(AmlError::InvalidNameSeg([0xff, 0xff, 0xff, 0xff]));
         }
 
         // We pre-fill the array with '_', so it will already be correct if the length is < 4
@@ -351,14 +396,14 @@ impl NameSeg {
 
         // Manually do the first one, because we have to check it's a LeadNameChar
         if !is_lead_name_char(bytes[0]) {
-            return Err(AmlError::InvalidNameSeg);
+            return Err(AmlError::InvalidNameSeg([bytes[0], bytes[1], bytes[2], bytes[3]]));
         }
         seg[0] = bytes[0];
 
         // Copy the rest of the chars, checking that they're NameChars
         for i in 1..bytes.len() {
             if !is_name_char(bytes[i]) {
-                return Err(AmlError::InvalidNameSeg);
+                return Err(AmlError::InvalidNameSeg([bytes[0], bytes[1], bytes[2], bytes[3]]));
             }
             seg[i] = bytes[i];
         }
@@ -368,16 +413,16 @@ impl NameSeg {
 
     pub fn from_bytes(bytes: [u8; 4]) -> Result<NameSeg, AmlError> {
         if !is_lead_name_char(bytes[0]) {
-            return Err(AmlError::InvalidNameSeg);
+            return Err(AmlError::InvalidNameSeg(bytes));
         }
         if !is_name_char(bytes[1]) {
-            return Err(AmlError::InvalidNameSeg);
+            return Err(AmlError::InvalidNameSeg(bytes));
         }
         if !is_name_char(bytes[2]) {
-            return Err(AmlError::InvalidNameSeg);
+            return Err(AmlError::InvalidNameSeg(bytes));
         }
         if !is_name_char(bytes[3]) {
-            return Err(AmlError::InvalidNameSeg);
+            return Err(AmlError::InvalidNameSeg(bytes));
         }
         Ok(NameSeg(bytes))
     }
diff --git a/aml/src/object.rs b/aml/src/object.rs
index 22741ebc..395b8088 100644
--- a/aml/src/object.rs
+++ b/aml/src/object.rs
@@ -9,7 +9,7 @@ pub enum Object {
     BufferField { buffer: Arc<Object>, offset: usize, length: usize },
     Device,
     Event,
-    FieldUnit,
+    FieldUnit(FieldUnit),
     Integer(u64),
     Method { code: Vec<u8>, flags: MethodFlags },
     Mutex,
@@ -44,6 +44,13 @@ impl Object {
     }
 }
 
+#[derive(Debug)]
+pub enum FieldUnit {
+    Normal { region: Arc<Object>, bit_index: usize, bit_length: usize },
+    Bank,
+    Index,
+}
+
 #[derive(Clone, Copy, Debug)]
 pub struct MethodFlags(pub u8);
 
diff --git a/aml/src/op_region.rs b/aml/src/op_region.rs
index 7582c2dc..a2cce762 100644
--- a/aml/src/op_region.rs
+++ b/aml/src/op_region.rs
@@ -1,8 +1,8 @@
 #[derive(Debug)]
 pub struct OpRegion {
-    space: RegionSpace,
-    base: u64,
-    limit: u64,
+    pub space: RegionSpace,
+    pub base: u64,
+    pub length: u64,
     // parent_device
 }
 
diff --git a/tests/buffer_fields.asl b/tests/buffer_fields.asl
index 7b10daec..891b159d 100644
--- a/tests/buffer_fields.asl
+++ b/tests/buffer_fields.asl
@@ -1,23 +1,23 @@
 DefinitionBlock("buffer_fields.aml", "DSDT", 1, "RSACPI", "BUFFLD", 1) {
-	Name(X, Buffer (16) { 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff })
-	CreateBitField(X, 3, BIT3)
-	CreateField(X, 0, 3, BITS)
-	CreateByteField(X, 1, BYTE)
-	CreateWordField(X, 2, WORD)
-	CreateDWordField(X, 4, DWRD)
-	CreateQWordField(X, 8, QWRD)
+    Name(X, Buffer (16) { 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff })
+    CreateBitField(X, 3, BIT3)
+    CreateField(X, 0, 3, BITS)
+    CreateByteField(X, 1, BYTE)
+    CreateWordField(X, 2, WORD)
+    CreateDWordField(X, 4, DWRD)
+    CreateQWordField(X, 8, QWRD)
 
-	// `X` should end up as [0xff, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x00, 0x00]
-	BIT3 = 1
-	BITS = 7
-	BYTE = 0x01
-	WORD = 0x0302
-	DWRD = 0x07060504
-	// Last two bytes should be cleared because of zero-extension of this store
-	// We do this as a buffer store a) to test them b) because `iasl` doesn't support 64-bit integer constants...
-	QWRD = Buffer() { 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d }
+    // `X` should end up as [0xff, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x00, 0x00]
+    BIT3 = 1
+    BITS = 7
+    BYTE = 0x01
+    WORD = 0x0302
+    DWRD = 0x07060504
+    // Last two bytes should be cleared because of zero-extension of this store
+    // We do this as a buffer store a) to test them b) because `iasl` doesn't support 64-bit integer constants...
+    QWRD = Buffer() { 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d }
 
-	// `Y` should end up as `Integer(0x07060504)` (`Integer(117835012)` in decimal)
-	Name(Y, 4)
-	Y = DWRD
+    // `Y` should end up as `Integer(0x07060504)` (`Integer(117835012)` in decimal)
+    Name(Y, 4)
+    Y = DWRD
 }
diff --git a/tests/power_res.asl b/tests/power_res.asl
index 94aab519..fd89e5e2 100644
--- a/tests/power_res.asl
+++ b/tests/power_res.asl
@@ -1,16 +1,16 @@
 DefinitionBlock("power_res.aml", "DSDT", 1, "RSACPI", "PWRRES", 1) {
-	Scope(_SB) {
-		PowerResource(PIDE, 0, 1) {
-			Name(X, Zero)
-			Method(_STA) {
-				Return (X)
-			}
-			Method(_ON) {
-				Store(One, X)
-			}
-			Method(_OFF) {
-				Store(Zero, X)
-			}
-		}
-	}
+    Scope(_SB) {
+        PowerResource(PIDE, 0, 1) {
+            Name(X, Zero)
+            Method(_STA) {
+                Return (X)
+            }
+            Method(_ON) {
+                Store(One, X)
+            }
+            Method(_OFF) {
+                Store(Zero, X)
+            }
+        }
+    }
 }
diff --git a/tests/scopes.asl b/tests/scopes.asl
index 787ea4fd..1e5644b1 100644
--- a/tests/scopes.asl
+++ b/tests/scopes.asl
@@ -1,12 +1,14 @@
 DefinitionBlock("scopes.aml", "DSDT", 1, "RSACPI", "SCOPES", 1) {
-	Scope(_SB) {
-		Name(X, 320)
-		Device(PCI0) {
-			Name(Y, 15)
-			Name (_HID, EisaId ("PNP0A03"))
-			Scope(\) {
-				Name(Z, 413)
-			}
-		}
-	}
+    Scope(_SB) {
+        Name(X, 320)
+        Name(Y, Zero)
+
+        Device(PCI0) {
+            Name(Y, 15)
+            Name (_HID, EisaId ("PNP0A03"))
+            Scope(\) {
+                Name(Z, 413)
+            }
+        }
+    }
 }
diff --git a/tests/thermal_zone.asl b/tests/thermal_zone.asl
index 0f295829..08e33f80 100644
--- a/tests/thermal_zone.asl
+++ b/tests/thermal_zone.asl
@@ -1,40 +1,40 @@
 DefinitionBlock("thermal_zone.aml", "DSDT", 1, "RSACPI", "THERMZ", 1) {
-	Scope(_SB) {
-		Device(EC0) {
-			Name(_HID, EISAID("PNP0C09"))
-			OperationRegion(EC0, EmbeddedControl, 0, 0xFF)
-			Field(EC0, ByteAcc, Lock, Preserve) {
-				MODE, 1, // thermal policy (quiet/perform)
-				FAN, 1, // fan power (on/off)
-				, 6, // reserved
-				TMP, 16, // current temp
-				AC0, 16, // active cooling temp (fan high)
-				, 16, // reserved
-				PSV, 16, // passive cooling temp
-				HOT, 16, // critical S4 temp
-				CRT, 16 // critical temp
-			}
-		}
+    Scope(_SB) {
+        Device(EC0) {
+            Name(_HID, EISAID("PNP0C09"))
+            OperationRegion(EC0, EmbeddedControl, 0, 0xFF)
+            Field(EC0, ByteAcc, Lock, Preserve) {
+                MODE, 1, // thermal policy (quiet/perform)
+                FAN, 1, // fan power (on/off)
+                , 6, // reserved
+                TMP, 16, // current temp
+                AC0, 16, // active cooling temp (fan high)
+                , 16, // reserved
+                PSV, 16, // passive cooling temp
+                HOT, 16, // critical S4 temp
+                CRT, 16 // critical temp
+            }
+        }
 
-		Device(CPU0) {
-			Name(_HID, "ACPI0007")
-			Name(_UID, 1) // unique number for this processor
-		}
+        Device(CPU0) {
+            Name(_HID, "ACPI0007")
+            Name(_UID, 1) // unique number for this processor
+        }
 
-		ThermalZone(TZ0) {
-			Method(_TMP) { Return (\_SB.EC0.TMP )} // get current temp
-			Method(_AC0) { Return (\_SB.EC0.AC0) } // fan high temp
-			Name(_AL0, Package(){\_SB.EC0.FAN}) // fan is act cool dev
-			Method(_PSV) { Return (\_SB.EC0.PSV) } // passive cooling temp
-			Name(_PSL, Package (){\_SB.CPU0}) // passive cooling devices
-			Method(_HOT) { Return (\_SB.EC0.HOT) } // get critical S4 temp
-			Method(_CRT) { Return (\_SB.EC0.CRT) } // get critical temp
-			Method(_SCP, 1) { Store (Arg0, \_SB.EC0.MODE) } // set cooling mode
-			Name(_TC1, 4) // bogus example constant
-			Name(_TC2, 3) // bogus example constant
-			Name(_TSP, 150) // passive sampling = 15 sec
-			Name(_TZP, 0) // polling not required
-			Name (_STR, Unicode ("System thermal zone"))
-		}
-	}
+        ThermalZone(TZ0) {
+            Method(_TMP) { Return (\_SB.EC0.TMP )} // get current temp
+            Method(_AC0) { Return (\_SB.EC0.AC0) } // fan high temp
+            Name(_AL0, Package(){\_SB.EC0.FAN}) // fan is act cool dev
+            Method(_PSV) { Return (\_SB.EC0.PSV) } // passive cooling temp
+            Name(_PSL, Package (){\_SB.CPU0}) // passive cooling devices
+            Method(_HOT) { Return (\_SB.EC0.HOT) } // get critical S4 temp
+            Method(_CRT) { Return (\_SB.EC0.CRT) } // get critical temp
+            Method(_SCP, 1) { Store (Arg0, \_SB.EC0.MODE) } // set cooling mode
+            Name(_TC1, 4) // bogus example constant
+            Name(_TC2, 3) // bogus example constant
+            Name(_TSP, 150) // passive sampling = 15 sec
+            Name(_TZP, 0) // polling not required
+            Name (_STR, Unicode ("System thermal zone"))
+        }
+    }
 }

From dea87c4bf2dcf9c55c85c892ec587d26e5a7467c Mon Sep 17 00:00:00 2001
From: Isaac Woods <isaacwoods.home@gmail.com>
Date: Mon, 10 Feb 2025 20:26:27 +0000
Subject: [PATCH 26/88] `aml`: nicer `Display` impl for `Namespace`

---
 aml/src/namespace.rs         | 40 ++++++++++++++++++++++++------------
 tools/aml_tester/src/main.rs |  4 ++--
 2 files changed, 29 insertions(+), 15 deletions(-)

diff --git a/aml/src/namespace.rs b/aml/src/namespace.rs
index 65584c04..422956a4 100644
--- a/aml/src/namespace.rs
+++ b/aml/src/namespace.rs
@@ -170,33 +170,47 @@ impl Namespace {
     }
 }
 
-// TODO: this is fairly unreadable. We should group devices better and maybe use ASCII chars to
-// format the tree better (maybe that should be `Display` instead idk?)
-impl fmt::Debug for Namespace {
+impl fmt::Display for Namespace {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        const INDENT_PER_LEVEL: usize = 4;
+        const STEM: &str = "│   ";
+        const BRANCH: &str = "├── ";
+        const END: &str = "└── ";
 
         fn print_level(
             namespace: &Namespace,
             f: &mut fmt::Formatter<'_>,
-            level_name: &str,
             level: &NamespaceLevel,
-            indent: usize,
+            indent_stack: String,
         ) -> fmt::Result {
-            writeln!(f, "{:indent$}{}:", "", level_name, indent = indent)?;
-
-            for (name, object) in level.values.iter() {
-                writeln!(f, "{:indent$}{}: {:?}", "", name.as_str(), object, indent = indent + INDENT_PER_LEVEL)?;
+            for (i, (name, object)) in level.values.iter().enumerate() {
+                let end = (i == level.values.len() - 1)
+                    && level.children.iter().filter(|(_, l)| l.kind == NamespaceLevelKind::Scope).count() == 0;
+                writeln!(f, "{}{}{}: {:?}", &indent_stack, if end { END } else { BRANCH }, name.as_str(), object)?;
+
+                // If the object has a corresponding scope, print it here
+                if let Some(child_level) = level.children.get(&name) {
+                    print_level(
+                        namespace,
+                        f,
+                        child_level,
+                        if end { indent_stack.clone() + "    " } else { indent_stack.clone() + STEM },
+                    )?;
+                }
             }
 
-            for (name, sub_level) in level.children.iter() {
-                print_level(namespace, f, name.as_str(), sub_level, indent + INDENT_PER_LEVEL)?;
+            let remaining_scopes: Vec<_> =
+                level.children.iter().filter(|(_, l)| l.kind == NamespaceLevelKind::Scope).collect();
+            for (i, (name, sub_level)) in remaining_scopes.iter().enumerate() {
+                let end = i == remaining_scopes.len() - 1;
+                writeln!(f, "{}{}{}:", &indent_stack, if end { END } else { BRANCH }, name.as_str())?;
+                print_level(namespace, f, sub_level, indent_stack.clone() + STEM)?;
             }
 
             Ok(())
         }
 
-        print_level(self, f, "\\", &self.root, 0)
+        writeln!(f, "\n    \\:")?;
+        print_level(self, f, &self.root, String::from("    "))
     }
 }
 
diff --git a/tools/aml_tester/src/main.rs b/tools/aml_tester/src/main.rs
index b812279f..e5796093 100644
--- a/tools/aml_tester/src/main.rs
+++ b/tools/aml_tester/src/main.rs
@@ -192,14 +192,14 @@ fn main() -> std::io::Result<()> {
         match interpreter.load_table(&contents[AML_TABLE_HEADER_LENGTH..]) {
             Ok(()) => {
                 println!("{}OK{}", termion::color::Fg(termion::color::Green), termion::style::Reset);
-                println!("Namespace: {:#?}", interpreter.namespace.lock());
+                println!("Namespace: {}", interpreter.namespace.lock());
                 summaries.insert((file_entry, TestResult::Pass));
                 (passed + 1, failed)
             }
 
             Err(err) => {
                 println!("{}Failed ({:?}){}", termion::color::Fg(termion::color::Red), err, termion::style::Reset);
-                println!("Namespace: {:#?}", interpreter.namespace.lock());
+                println!("Namespace: {}", interpreter.namespace.lock());
                 summaries.insert((file_entry, TestResult::ParseFail));
                 (passed, failed + 1)
             }

From 33b41f67c5072e2cb01b0c364968e47530bc6ff1 Mon Sep 17 00:00:00 2001
From: Isaac Woods <isaacwoods.home@gmail.com>
Date: Wed, 12 Feb 2025 19:32:36 +0000
Subject: [PATCH 27/88] `aml`: support `DefIncrement`, `DefDecrement`

---
 aml/src/lib.rs                | 14 +++++++++++++-
 tests/external.asl            | 10 ----------
 tests/{inc.asl => incdec.asl} |  2 +-
 3 files changed, 14 insertions(+), 12 deletions(-)
 delete mode 100644 tests/external.asl
 rename tests/{inc.asl => incdec.asl} (52%)

diff --git a/aml/src/lib.rs b/aml/src/lib.rs
index b6ae3eb9..ef356a45 100644
--- a/aml/src/lib.rs
+++ b/aml/src/lib.rs
@@ -96,6 +96,18 @@ impl Interpreter {
                             }
                         }
                     }
+                    Opcode::Increment | Opcode::Decrement => {
+                        let [Argument::Object(operand)] = &op.arguments[..] else { panic!() };
+                        let Object::Integer(operand) = operand.gain_mut() else { panic!() };
+
+                        let new_value = match op.op {
+                            Opcode::Increment => operand.wrapping_add(1),
+                            Opcode::Decrement => operand.wrapping_sub(1),
+                            _ => unreachable!(),
+                        };
+
+                        *operand = new_value;
+                    }
                     Opcode::Name => {
                         let [Argument::Namestring(name), Argument::Object(object)] = &op.arguments[..] else {
                             panic!()
@@ -624,7 +636,7 @@ impl Interpreter {
                 }
 
                 Opcode::Divide => context.start_in_flight_op(OpInFlight::new(Opcode::Divide, 4)),
-                Opcode::Increment | Opcode::Decrement => context.start_in_flight_op(OpInFlight::new(opcode, 2)),
+                Opcode::Increment | Opcode::Decrement => context.start_in_flight_op(OpInFlight::new(opcode, 1)),
                 Opcode::Not => context.start_in_flight_op(OpInFlight::new(Opcode::Not, 2)),
                 Opcode::FindSetLeftBit | Opcode::FindSetRightBit => {
                     context.start_in_flight_op(OpInFlight::new(opcode, 2))
diff --git a/tests/external.asl b/tests/external.asl
deleted file mode 100644
index 4421477a..00000000
--- a/tests/external.asl
+++ /dev/null
@@ -1,10 +0,0 @@
-DefinitionBlock("external.aml", "DSDT", 1, "RSACPI", "EXTRL", 1) {
-	Scope(_SB) {
-		External(FOO, MethodObj, IntObj, {IntObj})
-	}
-
-	Name(X, Zero)
-	Method(BAR) {
-		Store(\_SB.FOO(4), X)
-	}
-}
diff --git a/tests/inc.asl b/tests/incdec.asl
similarity index 52%
rename from tests/inc.asl
rename to tests/incdec.asl
index 729231c7..4b584d51 100644
--- a/tests/inc.asl
+++ b/tests/incdec.asl
@@ -1,4 +1,4 @@
-DefinitionBlock("inc.aml", "DSDT", 1, "RSACPI", "INCDEC", 1) {
+DefinitionBlock("incdec.aml", "DSDT", 1, "RSACPI", "INCDEC", 1) {
     Name(X, 0)
     X++
     X++

From a56959edd3a697fbaf60496d46328cbee8c8f203 Mon Sep 17 00:00:00 2001
From: Isaac Woods <isaacwoods.home@gmail.com>
Date: Wed, 12 Feb 2025 19:34:07 +0000
Subject: [PATCH 28/88] `aml`: support `DefObjectType`

---
 aml/src/lib.rs              | 36 +++++++++++++++++++++++++++--
 aml/src/object.rs           | 45 +++++++++++++++++++++++++++++++++++++
 tests/object_type.asl       | 28 +++++++++++------------
 tests/pc-bios_acpi-dsdt.asl |  2 +-
 4 files changed, 94 insertions(+), 17 deletions(-)

diff --git a/aml/src/lib.rs b/aml/src/lib.rs
index ef356a45..83850cb7 100644
--- a/aml/src/lib.rs
+++ b/aml/src/lib.rs
@@ -14,7 +14,7 @@ use alloc::{boxed::Box, sync::Arc, vec, vec::Vec};
 use bit_field::BitField;
 use core::{mem, str};
 use namespace::{AmlName, Namespace, NamespaceLevelKind};
-use object::{MethodFlags, Object};
+use object::{MethodFlags, Object, ObjectType};
 use op_region::{OpRegion, RegionSpace};
 use spinning_top::Spinlock;
 
@@ -290,6 +290,38 @@ impl Interpreter {
                             }
                         }
                     }
+                    Opcode::ObjectType => {
+                        let [Argument::Object(object)] = &op.arguments[..] else { panic!() };
+                        // TODO: this should technically support scopes as well - this is less easy
+                        // (they should return `0`)
+                        // TODO: calling this on the debug object should should return `16`
+                        let typ = match object.typ() {
+                            ObjectType::Uninitialized => 0,
+                            ObjectType::Integer => 1,
+                            ObjectType::String => 2,
+                            ObjectType::Buffer => 3,
+                            ObjectType::Package => 4,
+                            ObjectType::FieldUnit => 5,
+                            ObjectType::Device => 6,
+                            ObjectType::Event => 7,
+                            ObjectType::Method => 8,
+                            ObjectType::Mutex => 9,
+                            ObjectType::OpRegion => 10,
+                            ObjectType::PowerResource => 11,
+                            ObjectType::Processor => 12,
+                            ObjectType::ThermalZone => 13,
+                            ObjectType::BufferField => 14,
+                            // XXX: 15 is reserved
+                            ObjectType::Reference => panic!(),
+                            ObjectType::RawDataBuffer => todo!(),
+                        };
+
+                        if let Some(prev_op) = context.in_flight.last_mut() {
+                            if prev_op.arguments.len() < prev_op.expected_arguments {
+                                prev_op.arguments.push(Argument::Object(Arc::new(Object::Integer(typ))));
+                            }
+                        }
+                    }
                     _ => panic!("Unexpected operation has created in-flight op!"),
                 }
             }
@@ -673,7 +705,7 @@ impl Interpreter {
                 | Opcode::ToInteger
                 | Opcode::ToString => context.start_in_flight_op(OpInFlight::new(opcode, 2)),
 
-                Opcode::ObjectType => todo!(),
+                Opcode::ObjectType => context.start_in_flight_op(OpInFlight::new(opcode, 1)),
                 Opcode::CopyObject => todo!(),
                 Opcode::Mid => context.start_in_flight_op(OpInFlight::new(Opcode::Mid, 4)),
                 Opcode::Continue => todo!(),
diff --git a/aml/src/object.rs b/aml/src/object.rs
index 395b8088..3a2bd477 100644
--- a/aml/src/object.rs
+++ b/aml/src/object.rs
@@ -42,6 +42,30 @@ impl Object {
             &mut *(self as *const Self as *mut Self)
         }
     }
+
+    /// Returns the `ObjectType` of this object. Returns the type of the referenced object in the
+    /// case of `Object::Reference`.
+    pub fn typ(&self) -> ObjectType {
+        match self {
+            Object::Uninitialized => ObjectType::Uninitialized,
+            Object::Buffer(_) => ObjectType::Buffer,
+            Object::BufferField { .. } => ObjectType::BufferField,
+            Object::Device => ObjectType::Device,
+            Object::Event => ObjectType::Event,
+            Object::FieldUnit(_) => ObjectType::FieldUnit,
+            Object::Integer(_) => ObjectType::Integer,
+            Object::Method { .. } => ObjectType::Method,
+            Object::Mutex => ObjectType::Mutex,
+            Object::Reference(object) => object.typ(),
+            Object::OpRegion(_) => ObjectType::OpRegion,
+            Object::Package(_) => ObjectType::Package,
+            Object::PowerResource => ObjectType::PowerResource,
+            Object::Processor => ObjectType::Processor,
+            Object::RawDataBuffer => ObjectType::RawDataBuffer,
+            Object::String(_) => ObjectType::String,
+            Object::ThermalZone => ObjectType::ThermalZone,
+        }
+    }
 }
 
 #[derive(Debug)]
@@ -67,3 +91,24 @@ impl MethodFlags {
         self.0.get_bits(4..8)
     }
 }
+
+#[derive(Clone, Copy, PartialEq, Debug)]
+pub enum ObjectType {
+    Uninitialized,
+    Buffer,
+    BufferField,
+    Device,
+    Event,
+    FieldUnit,
+    Integer,
+    Method,
+    Mutex,
+    Reference,
+    OpRegion,
+    Package,
+    PowerResource,
+    Processor,
+    RawDataBuffer,
+    String,
+    ThermalZone,
+}
diff --git a/tests/object_type.asl b/tests/object_type.asl
index d4abc90e..3d064504 100644
--- a/tests/object_type.asl
+++ b/tests/object_type.asl
@@ -1,19 +1,19 @@
 DefinitionBlock("object_type.aml", "DSDT", 1, "RSACPI", "OBJTYP", 1) {
-	Name(INT, 723)
-	Name(STR, "Hello, World!")
-	Name(BUFF, Buffer { 7, 2, 3, 5, 92, 6 })
-	// TODO: more types
+    Name(INT, 723)
+    Name(STR, "Hello, World!")
+    Name(BUFF, Buffer { 7, 2, 3, 5, 92, 6 })
+    // TODO: more types
 
-	Device(TYPS) {
-		// This is just so it compiles
-		Name (_HID, EisaId ("PNP0A03"))
+    Device(TYPS) {
+        // This is just so it compiles
+        Name (_HID, EisaId ("PNP0A03"))
 
-		Name(INT, 0)	// Should be `1`
-		Name(STR, 0)	// Should be `2`
-		Name(BUFF, 0)	// Should be `3`
+        Name(INT, 0)    // Should be `1`
+        Name(STR, 0)    // Should be `2`
+        Name(BUFF, 0)   // Should be `3`
 
-		INT = ObjectType(\INT)
-		STR = ObjectType(\STR)
-		BUFF = ObjectType(\BUFF)
-	}
+        INT = ObjectType(\INT)
+        STR = ObjectType(\STR)
+        BUFF = ObjectType(\BUFF)
+    }
 }
diff --git a/tests/pc-bios_acpi-dsdt.asl b/tests/pc-bios_acpi-dsdt.asl
index 28e96d89..3f45079e 100644
--- a/tests/pc-bios_acpi-dsdt.asl
+++ b/tests/pc-bios_acpi-dsdt.asl
@@ -1896,4 +1896,4 @@
      }
  }
  
- 
\ No newline at end of file
+ 

From db0362f1fe0cbbf164d20a9ff0d0ffd3b49b4d64 Mon Sep 17 00:00:00 2001
From: Isaac Woods <isaacwoods.home@gmail.com>
Date: Wed, 12 Feb 2025 19:37:09 +0000
Subject: [PATCH 29/88] `aml`: support reads from and writes to buffer fields

---
 aml/src/lib.rs    | 33 ++++++++++++++++++--
 aml/src/object.rs | 76 ++++++++++++++++++++++++++++++++++++++++++++++-
 2 files changed, 106 insertions(+), 3 deletions(-)

diff --git a/aml/src/lib.rs b/aml/src/lib.rs
index 83850cb7..2cdc55e8 100644
--- a/aml/src/lib.rs
+++ b/aml/src/lib.rs
@@ -744,8 +744,35 @@ impl Interpreter {
         // TODO: write the object to the destination, including e.g. field writes that then lead to
         // literally god knows what.
         match target {
-            Argument::Object(_) => {}
+            Argument::Object(target) => match target.gain_mut() {
+                Object::Integer(target) => match object.gain_mut() {
+                    Object::Integer(value) => {
+                        *target = *value;
+                    }
+                    Object::BufferField { .. } => {
+                        let mut buffer = [0u8; 8];
+                        object.gain_mut().read_buffer_field(&mut buffer)?;
+                        let value = u64::from_le_bytes(buffer);
+                        *target = value;
+                    }
+                    _ => panic!(),
+                },
+                Object::BufferField { .. } => match object.gain_mut() {
+                    Object::Integer(value) => {
+                        target.gain_mut().write_buffer_field(&value.to_le_bytes())?;
+                    }
+                    Object::Buffer(value) => {
+                        target.gain_mut().write_buffer_field(&value.as_slice())?;
+                    }
+                    _ => panic!(),
+                },
+                _ => panic!("Stores to objects like {:?} are not yet supported", target),
+            },
             Argument::Namestring(_) => {}
+            Argument::UnresolvedObjectPath(_) => {
+                // TODO: do we need to attempt to allow this somehow??
+                todo!("Is this allowed here?");
+            }
 
             Argument::ByteData(_) | Argument::TrackedPc(_) | Argument::PkgLength(_) => panic!(),
         }
@@ -804,7 +831,7 @@ impl Block {
     }
 }
 
-#[derive(Debug)]
+#[derive(PartialEq, Debug)]
 pub enum BlockKind {
     Table,
     Method,
@@ -1284,6 +1311,8 @@ pub enum AmlError {
     NoCurrentOp,
 
     MethodArgCountIncorrect,
+
+    InvalidOperationOnObject,
 }
 
 /// This trait represents the interface from the `Interpreter` to the hosting kernel, and allows
diff --git a/aml/src/object.rs b/aml/src/object.rs
index 3a2bd477..32136f6f 100644
--- a/aml/src/object.rs
+++ b/aml/src/object.rs
@@ -1,4 +1,4 @@
-use crate::op_region::OpRegion;
+use crate::{AmlError, op_region::OpRegion};
 use alloc::{sync::Arc, vec::Vec};
 use bit_field::BitField;
 
@@ -43,6 +43,27 @@ impl Object {
         }
     }
 
+    pub fn read_buffer_field(&self, dst: &mut [u8]) -> Result<(), AmlError> {
+        if let Self::BufferField { buffer, offset, length } = self {
+            let Object::Buffer(ref buffer) = **buffer else { panic!() };
+            // TODO: assert length of buffer is sufficient
+            copy_bits(buffer.as_slice(), *offset, dst, 0, *length);
+            Ok(())
+        } else {
+            Err(AmlError::InvalidOperationOnObject)
+        }
+    }
+
+    pub fn write_buffer_field(&mut self, value: &[u8]) -> Result<(), AmlError> {
+        if let Self::BufferField { buffer, offset, length } = self {
+            let Object::Buffer(buffer) = buffer.gain_mut() else { panic!() };
+            copy_bits(value, 0, buffer.as_mut_slice(), *offset, *length);
+            Ok(())
+        } else {
+            Err(AmlError::InvalidOperationOnObject)
+        }
+    }
+
     /// Returns the `ObjectType` of this object. Returns the type of the referenced object in the
     /// case of `Object::Reference`.
     pub fn typ(&self) -> ObjectType {
@@ -112,3 +133,56 @@ pub enum ObjectType {
     String,
     ThermalZone,
 }
+
+/// Copy an arbitrary bit range of `src` to an arbitrary bit range of `dst`. This is used for
+/// buffer fields. Data is zero-extended if `src` does not cover `length` bits, matching the
+/// expected behaviour for buffer fields.
+fn copy_bits(src: &[u8], mut src_index: usize, dst: &mut [u8], mut dst_index: usize, mut length: usize) {
+    while length > 0 {
+        let src_shift = src_index & 7;
+        let mut src_bits = src.get(src_index / 8).unwrap_or(&0x00) >> src_shift;
+        if src_shift > 0 && length > (8 - src_shift) {
+            src_bits |= src.get(src_index / 8 + 1).unwrap_or(&0x00) << (8 - src_shift);
+        }
+
+        if length < 8 {
+            src_bits &= (1 << length) - 1;
+        }
+
+        let dst_shift = dst_index & 7;
+        let mut dst_mask: u16 = if length < 8 { ((1 << length) - 1) as u16 } else { 0xff as u16 } << dst_shift;
+        dst[dst_index / 8] =
+            (dst[dst_index / 8] & !(dst_mask as u8)) | ((src_bits << dst_shift) & (dst_mask as u8));
+
+        if dst_shift > 0 && length > (8 - dst_shift) {
+            dst_mask >>= 8;
+            dst[dst_index / 8 + 1] &= !(dst_mask as u8);
+            dst[dst_index / 8 + 1] |= (src_bits >> (8 - dst_shift)) & (dst_mask as u8);
+        }
+
+        if length < 8 {
+            length = 0;
+        } else {
+            length -= 8;
+            src_index += 8;
+            dst_index += 8;
+        }
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+
+    #[test]
+    fn test_copy_bits() {
+        let src = [0b1011_1111, 0b1111_0111, 0b1111_1111, 0b1111_1111, 0b1111_1111];
+        let mut dst = [0b1110_0001, 0, 0, 0, 0];
+
+        copy_bits(&src, 0, &mut dst, 2, 15);
+        for i in 0..dst.len() {
+            print!("{:08b} ", dst[i]);
+        }
+        assert_eq!(dst, [0b1111_1101, 0b1101_1110, 0b0000_0001, 0b0000_0000, 0b0000_0000]);
+    }
+}

From 6d629b0020e93e952dbe11e3ba3c4191e1ad1ae5 Mon Sep 17 00:00:00 2001
From: Isaac Woods <isaacwoods.home@gmail.com>
Date: Sat, 15 Feb 2025 18:32:41 +0000
Subject: [PATCH 30/88] `aml_tester`: allow running a single test instead of a
 directory of them

---
 tools/aml_tester/src/main.rs | 26 ++++++++++++++++----------
 1 file changed, 16 insertions(+), 10 deletions(-)

diff --git a/tools/aml_tester/src/main.rs b/tools/aml_tester/src/main.rs
index e5796093..59251e16 100644
--- a/tools/aml_tester/src/main.rs
+++ b/tools/aml_tester/src/main.rs
@@ -56,16 +56,22 @@ fn main() -> std::io::Result<()> {
     // Get an initial list of files - may not work correctly on non-UTF8 OsString
     let files: Vec<String> = if matches.contains_id("path") {
         let dir_path = Path::new(matches.get_one::<String>("path").unwrap());
-        println!("Running tests in directory: {:?}", dir_path);
-        fs::read_dir(dir_path)?
-            .filter_map(|entry| {
-                if entry.is_ok() {
-                    Some(entry.unwrap().path().to_string_lossy().to_string())
-                } else {
-                    None
-                }
-            })
-            .collect()
+
+        if std::fs::metadata(&dir_path).unwrap().is_dir() {
+            println!("Running tests in directory: {:?}", dir_path);
+            fs::read_dir(dir_path)?
+                .filter_map(|entry| {
+                    if entry.is_ok() {
+                        Some(entry.unwrap().path().to_string_lossy().to_string())
+                    } else {
+                        None
+                    }
+                })
+                .collect()
+        } else {
+            println!("Running single test: {:?}", dir_path);
+            vec![dir_path.to_string_lossy().to_string()]
+        }
     } else {
         matches.get_many::<String>("files").unwrap_or_default().map(|name| name.to_string()).collect()
     };

From 5185140e7f66979995fdac0ae0b0dadf32a17150 Mon Sep 17 00:00:00 2001
From: Isaac Woods <isaacwoods.home@gmail.com>
Date: Sat, 15 Feb 2025 18:55:16 +0000
Subject: [PATCH 31/88] `aml`: fix package parsing (again)

---
 aml/src/lib.rs    | 18 +++++++++++++++++-
 tests/package.asl |  8 ++++++++
 2 files changed, 25 insertions(+), 1 deletion(-)

diff --git a/aml/src/lib.rs b/aml/src/lib.rs
index 2cdc55e8..04bb4752 100644
--- a/aml/src/lib.rs
+++ b/aml/src/lib.rs
@@ -170,6 +170,21 @@ impl Interpreter {
                             elements.push(object.clone());
                         }
 
+                        /*
+                         * We can end up completing a package's in-flight op in two circumstances:
+                         *    - If the correct number of elements are supplied, we end up here
+                         *      first, and then later in the block's finishing logic.
+                         *    - If less elements are supplied, we end up in the block's finishing
+                         *      logic to add some `Uninitialized`s, then go round again to complete
+                         *      the in-flight operation.
+                         *
+                         * To make these consistent, we always remove the block here, making sure
+                         * we've finished it as a sanity check.
+                         */
+                        assert_eq!(context.current_block.kind, BlockKind::Package);
+                        assert_eq!(context.peek(), Err(AmlError::RunOutOfStream));
+                        context.current_block = context.block_stack.pop().unwrap();
+
                         if let Some(prev_op) = context.in_flight.last_mut() {
                             if prev_op.arguments.len() < prev_op.expected_arguments {
                                 prev_op.arguments.push(Argument::Object(Arc::new(Object::Package(elements))));
@@ -375,7 +390,8 @@ impl Interpreter {
                                 }
                             }
 
-                            context.current_block = context.block_stack.pop().unwrap();
+                            // XXX: don't remove the package's block. Refer to completion of
+                            // package ops for rationale here.
                             continue;
                         }
                         BlockKind::IfThenBranch => {
diff --git a/tests/package.asl b/tests/package.asl
index 69a0e3b0..2edb838f 100644
--- a/tests/package.asl
+++ b/tests/package.asl
@@ -11,4 +11,12 @@ DefinitionBlock("package.aml", "DSDT", 1, "RSACPI", "PACKGE", 1) {
         0x09,
         0x0a,
     })
+
+    Name(BAR, Package (5) {
+        Package { 0x01, 0x02, 0x03 },
+        Package { 0x04, 0x05, 0x06 },
+        Package { 0x07, 0x08, 0x09 },
+        Package { 0x0a, 0x0b, 0x0c },
+        Package { 0x0d, 0x0e, 0x0f },
+    })
 }

From 4ceaa01aaf57f339ac4ffaaabebf081e35c7e913 Mon Sep 17 00:00:00 2001
From: Isaac Woods <isaacwoods.home@gmail.com>
Date: Sat, 15 Feb 2025 18:58:20 +0000
Subject: [PATCH 32/88] `aml`: handle legacy `DefProcessor` operations

---
 aml/src/lib.rs    | 24 +++++++++++++++++++++++-
 aml/src/object.rs | 10 +++++-----
 2 files changed, 28 insertions(+), 6 deletions(-)

diff --git a/aml/src/lib.rs b/aml/src/lib.rs
index 04bb4752..62a70eb6 100644
--- a/aml/src/lib.rs
+++ b/aml/src/lib.rs
@@ -616,6 +616,25 @@ impl Interpreter {
                     let old_scope = mem::replace(&mut context.current_scope, new_scope);
                     context.start_new_block(BlockKind::Scope { old_scope }, remaining_length);
                 }
+                Opcode::Processor => {
+                    let start_pc = context.current_block.pc;
+                    let pkg_length = context.pkglength()?;
+                    let name = context.namestring()?;
+                    let proc_id = context.next()?;
+                    let pblk_address = context.next_u32()?;
+                    let pblk_length = context.next()?;
+
+                    let remaining_length = pkg_length - (context.current_block.pc - start_pc);
+
+                    let new_scope = name.resolve(&context.current_scope)?;
+                    let object = Object::Processor { proc_id, pblk_address, pblk_length };
+                    let mut namespace = self.namespace.lock();
+                    namespace.add_level(new_scope.clone(), NamespaceLevelKind::Processor)?;
+                    namespace.insert(new_scope.clone(), Arc::new(object))?;
+
+                    let old_scope = mem::replace(&mut context.current_scope, new_scope);
+                    context.start_new_block(BlockKind::Scope { old_scope }, remaining_length);
+                }
                 Opcode::PowerRes => {
                     let start_pc = context.current_block.pc;
                     let pkg_length = context.pkglength()?;
@@ -626,9 +645,10 @@ impl Interpreter {
                     let remaining_length = pkg_length - (context.current_block.pc - start_pc);
 
                     let new_scope = name.resolve(&context.current_scope)?;
+                    let object = Object::PowerResource { system_level, resource_order };
                     let mut namespace = self.namespace.lock();
                     namespace.add_level(new_scope.clone(), NamespaceLevelKind::PowerResource)?;
-                    namespace.insert(new_scope.clone(), Arc::new(Object::PowerResource))?;
+                    namespace.insert(new_scope.clone(), Arc::new(object))?;
 
                     let old_scope = mem::replace(&mut context.current_scope, new_scope);
                     context.start_new_block(BlockKind::Scope { old_scope }, remaining_length);
@@ -982,6 +1002,7 @@ impl MethodContext {
             0x5b80 => Opcode::OpRegion,
             0x5b81 => Opcode::Field,
             0x5b82 => Opcode::Device,
+            0x5b83 => Opcode::Processor,
             0x5b84 => Opcode::PowerRes,
             0x5b85 => Opcode::ThermalZone,
             0x5b86 => Opcode::IndexField,
@@ -1232,6 +1253,7 @@ enum Opcode {
     OpRegion,
     Field,
     Device,
+    Processor,
     PowerRes,
     ThermalZone,
     IndexField,
diff --git a/aml/src/object.rs b/aml/src/object.rs
index 32136f6f..3f226fd0 100644
--- a/aml/src/object.rs
+++ b/aml/src/object.rs
@@ -1,5 +1,5 @@
 use crate::{AmlError, op_region::OpRegion};
-use alloc::{sync::Arc, vec::Vec};
+use alloc::{string::String, sync::Arc, vec::Vec};
 use bit_field::BitField;
 
 #[derive(Debug)]
@@ -16,8 +16,8 @@ pub enum Object {
     Reference(Arc<Object>),
     OpRegion(OpRegion),
     Package(Vec<Arc<Object>>),
-    PowerResource,
-    Processor,
+    PowerResource { system_level: u8, resource_order: u16 },
+    Processor { proc_id: u8, pblk_address: u32, pblk_length: u8 },
     RawDataBuffer,
     String(String),
     ThermalZone,
@@ -80,8 +80,8 @@ impl Object {
             Object::Reference(object) => object.typ(),
             Object::OpRegion(_) => ObjectType::OpRegion,
             Object::Package(_) => ObjectType::Package,
-            Object::PowerResource => ObjectType::PowerResource,
-            Object::Processor => ObjectType::Processor,
+            Object::PowerResource { .. } => ObjectType::PowerResource,
+            Object::Processor { .. } => ObjectType::Processor,
             Object::RawDataBuffer => ObjectType::RawDataBuffer,
             Object::String(_) => ObjectType::String,
             Object::ThermalZone => ObjectType::ThermalZone,

From b8749ce1985b09ca973786acc2bceab9a19581fd Mon Sep 17 00:00:00 2001
From: Isaac Woods <isaacwoods.home@gmail.com>
Date: Sun, 16 Feb 2025 15:51:53 +0000
Subject: [PATCH 33/88] `aml`: fix branching calculations for `DefIfElse`

---
 aml/src/lib.rs | 12 +++++++-----
 1 file changed, 7 insertions(+), 5 deletions(-)

diff --git a/aml/src/lib.rs b/aml/src/lib.rs
index 62a70eb6..835ca5a6 100644
--- a/aml/src/lib.rs
+++ b/aml/src/lib.rs
@@ -214,6 +214,7 @@ impl Interpreter {
                             const DEF_ELSE_OP: u8 = 0xa1;
                             // TODO: maybe need to handle error here
                             if context.peek()? == DEF_ELSE_OP {
+                                context.next()?;
                                 let _else_length = context.pkglength()?;
                             }
                         }
@@ -400,11 +401,12 @@ impl Interpreter {
                             // Check for an else-branch, and skip over it
                             // TODO: if we run out of stream here, it might just be an IfOp at the
                             // end I think?
-                            let start_pc = context.current_block.pc;
                             const DEF_ELSE_OP: u8 = 0xa1;
                             if context.peek()? == DEF_ELSE_OP {
-                                let else_length = context.pkglength()? - (context.current_block.pc - start_pc);
-                                context.current_block.pc += else_length;
+                                context.next()?;
+                                let start_pc = context.current_block.pc;
+                                let else_length = context.pkglength()?;
+                                context.current_block.pc += else_length - (context.current_block.pc - start_pc);
                             }
 
                             continue;
@@ -754,8 +756,7 @@ impl Interpreter {
                         1,
                     ));
                 }
-                // TODO: maybe should be a normal error instead
-                Opcode::Else => panic!("Unexpected DefElseOp without corresponding DefIfElseOp"),
+                Opcode::Else => return Err(AmlError::ElseFoundWithoutCorrespondingIf),
                 Opcode::While => todo!(),
                 Opcode::Noop => {}
                 Opcode::Return => context.start_in_flight_op(OpInFlight::new(Opcode::Return, 1)),
@@ -1347,6 +1348,7 @@ pub enum AmlError {
     ObjectDoesNotExist(AmlName),
 
     NoCurrentOp,
+    ElseFoundWithoutCorrespondingIf,
 
     MethodArgCountIncorrect,
 

From 5d4a75d91a5e04ddaccffda964bc967b55cce4ea Mon Sep 17 00:00:00 2001
From: Isaac Woods <isaacwoods.home@gmail.com>
Date: Sun, 16 Feb 2025 22:06:44 +0000
Subject: [PATCH 34/88] `aml`: stub out `DefMutex` and `DefEvent` support

---
 aml/src/lib.rs    | 20 +++++++++++++-------
 aml/src/object.rs |  4 ++--
 2 files changed, 15 insertions(+), 9 deletions(-)

diff --git a/aml/src/lib.rs b/aml/src/lib.rs
index 835ca5a6..83140ee6 100644
--- a/aml/src/lib.rs
+++ b/aml/src/lib.rs
@@ -517,8 +517,19 @@ impl Interpreter {
                     let _object_type = context.next()?;
                     let _arg_count = context.next()?;
                 }
-                Opcode::Mutex => todo!(),
-                Opcode::Event => todo!(),
+                Opcode::Mutex => {
+                    let name = context.namestring()?;
+                    let sync_level = context.next()?;
+
+                    let name = name.resolve(&context.current_scope)?;
+                    self.namespace.lock().insert(name, Arc::new(Object::Mutex { sync_level }))?;
+                }
+                Opcode::Event => {
+                    let name = context.namestring()?;
+
+                    let name = name.resolve(&context.current_scope)?;
+                    self.namespace.lock().insert(name, Arc::new(Object::Event))?;
+                }
                 Opcode::CondRefOf => todo!(),
                 Opcode::LoadTable => todo!(),
                 Opcode::Load => todo!(),
@@ -806,11 +817,6 @@ impl Interpreter {
                 _ => panic!("Stores to objects like {:?} are not yet supported", target),
             },
             Argument::Namestring(_) => {}
-            Argument::UnresolvedObjectPath(_) => {
-                // TODO: do we need to attempt to allow this somehow??
-                todo!("Is this allowed here?");
-            }
-
             Argument::ByteData(_) | Argument::TrackedPc(_) | Argument::PkgLength(_) => panic!(),
         }
         Ok(())
diff --git a/aml/src/object.rs b/aml/src/object.rs
index 3f226fd0..51679964 100644
--- a/aml/src/object.rs
+++ b/aml/src/object.rs
@@ -12,7 +12,7 @@ pub enum Object {
     FieldUnit(FieldUnit),
     Integer(u64),
     Method { code: Vec<u8>, flags: MethodFlags },
-    Mutex,
+    Mutex { sync_level: u8 },
     Reference(Arc<Object>),
     OpRegion(OpRegion),
     Package(Vec<Arc<Object>>),
@@ -76,7 +76,7 @@ impl Object {
             Object::FieldUnit(_) => ObjectType::FieldUnit,
             Object::Integer(_) => ObjectType::Integer,
             Object::Method { .. } => ObjectType::Method,
-            Object::Mutex => ObjectType::Mutex,
+            Object::Mutex { .. } => ObjectType::Mutex,
             Object::Reference(object) => object.typ(),
             Object::OpRegion(_) => ObjectType::OpRegion,
             Object::Package(_) => ObjectType::Package,

From 763bc744ec6508c0be450de5bc0394f557725d08 Mon Sep 17 00:00:00 2001
From: Isaac Woods <isaacwoods.home@gmail.com>
Date: Sun, 16 Feb 2025 22:33:36 +0000
Subject: [PATCH 35/88] `aml`: binary maths

---
 aml/src/lib.rs | 84 +++++++++++++++++++++++++++++++++++++++-----------
 1 file changed, 66 insertions(+), 18 deletions(-)

diff --git a/aml/src/lib.rs b/aml/src/lib.rs
index 83140ee6..ffef2533 100644
--- a/aml/src/lib.rs
+++ b/aml/src/lib.rs
@@ -77,24 +77,19 @@ impl Interpreter {
              */
             while let Some(op) = context.in_flight.pop_if(|op| op.arguments.len() == op.expected_arguments) {
                 match op.op {
-                    Opcode::Add => {
-                        let [Argument::Object(left), Argument::Object(right), Argument::Object(target)] =
-                            &op.arguments[..]
-                        else {
-                            panic!()
-                        };
-                        let Object::Integer(left) = **left else { panic!() };
-                        let Object::Integer(right) = **right else { panic!() };
-
-                        *target.gain_mut() = Object::Integer(left + right);
-
-                        // TODO: this is probs a slightly scuffed way of working out if the
-                        // prev op wants our result
-                        if let Some(prev_op) = context.in_flight.last_mut() {
-                            if prev_op.arguments.len() < prev_op.expected_arguments {
-                                prev_op.arguments.push(Argument::Object(Arc::new(Object::Integer(left + right))));
-                            }
-                        }
+                    Opcode::Add
+                    | Opcode::Subtract
+                    | Opcode::Multiply
+                    | Opcode::Divide
+                    | Opcode::ShiftLeft
+                    | Opcode::ShiftRight
+                    | Opcode::Mod
+                    | Opcode::Nand
+                    | Opcode::And
+                    | Opcode::Or
+                    | Opcode::Nor
+                    | Opcode::Xor => {
+                        self.do_binary_maths(&mut context, op)?;
                     }
                     Opcode::Increment | Opcode::Decrement => {
                         let [Argument::Object(operand)] = &op.arguments[..] else { panic!() };
@@ -781,6 +776,59 @@ impl Interpreter {
             }
         }
     }
+
+    fn do_binary_maths(&self, context: &mut MethodContext, op: OpInFlight) -> Result<(), AmlError> {
+        let [Argument::Object(left), Argument::Object(right), Argument::Object(target)] = &op.arguments[0..2]
+        else {
+            Err(AmlError::InvalidOperationOnObject)?
+        };
+        let target2 = if op.op == Opcode::Divide {
+            let Argument::Object(target2) = &op.arguments[3] else { panic!() };
+            Some(target2)
+        } else {
+            None
+        };
+
+        let Object::Integer(left) = *left.clone().unwrap_transparent_reference() else {
+            Err(AmlError::InvalidOperationOnObject)?
+        };
+        let Object::Integer(right) = *right.clone().unwrap_transparent_reference() else {
+            Err(AmlError::InvalidOperationOnObject)?
+        };
+
+        let value = match op.op {
+            Opcode::Add => left.wrapping_add(right),
+            Opcode::Subtract => left.wrapping_sub(right),
+            Opcode::Multiply => left.wrapping_mul(right),
+            Opcode::Divide => {
+                if let Some(remainder) = target2 {
+                    *remainder.gain_mut() = Object::Integer(left.wrapping_rem(right));
+                }
+                left.wrapping_div_euclid(right)
+            }
+            Opcode::ShiftLeft => left.wrapping_shl(right as u32),
+            Opcode::ShiftRight => left.wrapping_shr(right as u32),
+            Opcode::Mod => left.wrapping_rem(right),
+            Opcode::Nand => !(left & right),
+            Opcode::And => left & right,
+            Opcode::Or => left | right,
+            Opcode::Nor => !(left | right),
+            Opcode::Xor => left ^ right,
+            _ => panic!(),
+        };
+
+        *target.gain_mut() = Object::Integer(value);
+
+        // TODO: this is probs a slightly scuffed way of working out if the
+        // prev op wants our result
+        if let Some(prev_op) = context.in_flight.last_mut() {
+            if prev_op.arguments.len() < prev_op.expected_arguments {
+                prev_op.arguments.push(Argument::Object(Arc::new(Object::Integer(left + right))));
+            }
+        }
+
+        Ok(())
+    }
     fn do_store(
         &self,
         context: &mut MethodContext,

From 782e26b1896ff5ad1c988b14606fab267d75d6a0 Mon Sep 17 00:00:00 2001
From: Isaac Woods <isaacwoods.home@gmail.com>
Date: Mon, 17 Feb 2025 21:33:34 +0000
Subject: [PATCH 36/88] `aml`: handle `DefFindSetLeftBit` and
 `DefFindSetRightBit`

---
 aml/src/lib.rs | 38 ++++++++++++++++++++++++++++++++++++++
 1 file changed, 38 insertions(+)

diff --git a/aml/src/lib.rs b/aml/src/lib.rs
index ffef2533..29b1eec9 100644
--- a/aml/src/lib.rs
+++ b/aml/src/lib.rs
@@ -91,6 +91,9 @@ impl Interpreter {
                     | Opcode::Xor => {
                         self.do_binary_maths(&mut context, op)?;
                     }
+                    Opcode::FindSetLeftBit | Opcode::FindSetRightBit => {
+                        self.do_unary_maths(&mut context, op)?;
+                    }
                     Opcode::Increment | Opcode::Decrement => {
                         let [Argument::Object(operand)] = &op.arguments[..] else { panic!() };
                         let Object::Integer(operand) = operand.gain_mut() else { panic!() };
@@ -829,6 +832,41 @@ impl Interpreter {
 
         Ok(())
     }
+
+    fn do_unary_maths(&self, context: &mut MethodContext, op: OpInFlight) -> Result<(), AmlError> {
+        let [Argument::Object(operand)] = &op.arguments[..] else { Err(AmlError::InvalidOperationOnObject)? };
+        let Object::Integer(operand) = **operand else { Err(AmlError::InvalidOperationOnObject)? };
+
+        let result = match op.op {
+            Opcode::FindSetLeftBit => {
+                if operand == 0 {
+                    0
+                } else {
+                    /*
+                     * TODO: this is a particular instance where not respecting integers being
+                     * 32-bit on revision 1 tables does cause properly incorrect behaviour...
+                     */
+                    operand.leading_zeros() + 1
+                }
+            }
+            Opcode::FindSetRightBit => {
+                if operand == 0 {
+                    0
+                } else {
+                    operand.trailing_zeros() + 1
+                }
+            }
+            _ => panic!(),
+        };
+
+        if let Some(prev_op) = context.in_flight.last_mut() {
+            if prev_op.arguments.len() < prev_op.expected_arguments {
+                prev_op.arguments.push(Argument::Object(Arc::new(Object::Integer(result as u64))));
+            }
+        }
+
+        Ok(())
+    }
     fn do_store(
         &self,
         context: &mut MethodContext,

From 97861f8131832387f9af68fed9438835c203f805 Mon Sep 17 00:00:00 2001
From: Isaac Woods <isaacwoods.home@gmail.com>
Date: Mon, 17 Feb 2025 23:50:03 +0000
Subject: [PATCH 37/88] `aml`: support executing method objects

This also cleans up a few bugs, such as methods not being run in their correct
scopes, and creating a scope for their locally-defined names.
---
 aml/src/lib.rs       | 107 ++++++++++++++++++++++++++++++-------------
 aml/src/namespace.rs |  16 ++++++-
 tests/method.asl     |  13 ++++--
 3 files changed, 101 insertions(+), 35 deletions(-)

diff --git a/aml/src/lib.rs b/aml/src/lib.rs
index 29b1eec9..ad82104d 100644
--- a/aml/src/lib.rs
+++ b/aml/src/lib.rs
@@ -38,12 +38,29 @@ impl Interpreter {
 
     pub fn load_table(&self, stream: &[u8]) -> Result<(), AmlError> {
         // TODO: probs needs to do more stuff
-        self.execute_method(stream)
+        let context = unsafe { MethodContext::new_from_table(stream) };
+        self.do_execute_method(context)?;
+        Ok(())
     }
 
-    pub fn execute_method(&self, stream: &[u8]) -> Result<(), AmlError> {
-        let mut context = unsafe { MethodContext::new_from_table(stream) };
+    /// Invoke a method by its name, with the given set of arguments. If the referenced object is
+    /// not a method, the object will instead be returned - this is useful for objects that can
+    /// either be defined directly, or through a method (e.g. a `_CRS` object).
+    pub fn invoke_method(&self, path: AmlName, args: Vec<Arc<Object>>) -> Result<Arc<Object>, AmlError> {
+        info!("Invoking AML method: {}", path);
+
+        let object = self.namespace.lock().get(path.clone())?.clone();
+        match object.typ() {
+            ObjectType::Method => {
+                self.namespace.lock().add_level(path.clone(), NamespaceLevelKind::MethodLocals)?;
+                let context = MethodContext::new_from_method(object, args, path)?;
+                self.do_execute_method(context)
+            }
+            _ => Ok(object),
+        }
+    }
 
+    fn do_execute_method(&self, mut context: MethodContext) -> Result<Arc<Object>, AmlError> {
         /*
          * TODO
          *
@@ -277,9 +294,12 @@ impl Interpreter {
                         self.handler.stall(usec);
                     }
                     Opcode::InternalMethodCall => {
-                        let Argument::Object(method) = &op.arguments[0] else { panic!() };
+                        let [Argument::Object(method), Argument::Namestring(method_scope)] = &op.arguments[0..2]
+                        else {
+                            panic!()
+                        };
 
-                        let args = op.arguments[1..]
+                        let args = op.arguments[2..]
                             .iter()
                             .map(|arg| {
                                 if let Argument::Object(arg) = arg {
@@ -290,18 +310,30 @@ impl Interpreter {
                             })
                             .collect();
 
-                        let new_context = MethodContext::new_from_method(method.clone(), args)?;
+                        self.namespace.lock().add_level(method_scope.clone(), NamespaceLevelKind::MethodLocals)?;
+
+                        let new_context =
+                            MethodContext::new_from_method(method.clone(), args, method_scope.clone())?;
                         let old_context = mem::replace(&mut context, new_context);
                         self.context_stack.lock().push(old_context);
                     }
                     Opcode::Return => {
                         let [Argument::Object(object)] = &op.arguments[..] else { panic!() };
-                        context = self.context_stack.lock().pop().unwrap();
 
-                        if let Some(prev_op) = context.in_flight.last_mut() {
-                            if prev_op.arguments.len() < prev_op.expected_arguments {
-                                prev_op.arguments.push(Argument::Object(object.clone()));
+                        if let Some(last) = self.context_stack.lock().pop() {
+                            context = last;
+
+                            if let Some(prev_op) = context.in_flight.last_mut() {
+                                if prev_op.arguments.len() < prev_op.expected_arguments {
+                                    prev_op.arguments.push(Argument::Object(object.clone()));
+                                }
                             }
+                        } else {
+                            /*
+                             * If this is the top-most context, this is a `Return` from the actual
+                             * method.
+                             */
+                            return Ok(object.clone());
                         }
                     }
                     Opcode::ObjectType => {
@@ -353,13 +385,21 @@ impl Interpreter {
                      */
                     match context.current_block.kind {
                         BlockKind::Table => {
-                            break Ok(());
+                            break Ok(Arc::new(Object::Uninitialized));
                         }
-                        BlockKind::Method => {
-                            // TODO: not sure how to handle no explicit return. Result is undefined
-                            // but we might still need to handle sticking it in an in-flight op?
-                            context = self.context_stack.lock().pop().unwrap();
-                            continue;
+                        BlockKind::Method { method_scope } => {
+                            self.namespace.lock().remove_level(method_scope)?;
+
+                            if let Some(prev_context) = self.context_stack.lock().pop() {
+                                context = prev_context;
+                                continue;
+                            } else {
+                                /*
+                                 * If there is no explicit `Return` op, the result is undefined. We
+                                 * just return an uninitialized object.
+                                 */
+                                return Ok(Arc::new(Object::Uninitialized));
+                            }
                         }
                         BlockKind::Scope { old_scope } => {
                             assert!(context.block_stack.len() > 0);
@@ -691,11 +731,11 @@ impl Interpreter {
                     if let Object::Method { flags, .. } = *object {
                         context.start_in_flight_op(OpInFlight::new_with(
                             Opcode::InternalMethodCall,
-                            vec![Argument::Object(object)],
                             flags.arg_count(),
                         ))
                     } else {
                         context.last_op()?.arguments.push(Argument::Object(object));
+                                    vec![Argument::Object(object), Argument::Namestring(resolved_name)],
                     }
                 }
 
@@ -915,10 +955,9 @@ impl Interpreter {
 /// ### Safety
 /// `MethodContext` does not keep the lifetime of the underlying AML stream, which for tables is
 /// borrowed from the underlying physical mapping. This is because the interpreter needs to
-/// pre-empt method contexts that execute other methods, storing pre-empted contexts.
-///
-/// This is made safe in the case of methods by the context holding a reference to the method
-/// object, but must be handled manually for AML tables.
+/// preempt method contexts that execute other methods, and these contexts may have disparate
+/// lifetimes. This is made safe in the case of methods by the context holding a reference to the
+/// method object, but must be handled manually for AML tables.
 struct MethodContext {
     current_block: Block,
     block_stack: Vec<Block>,
@@ -963,7 +1002,9 @@ impl Block {
 #[derive(PartialEq, Debug)]
 pub enum BlockKind {
     Table,
-    Method,
+    Method {
+        method_scope: AmlName,
+    },
     Scope {
         old_scope: AmlName,
     },
@@ -997,12 +1038,20 @@ impl MethodContext {
         }
     }
 
-    fn new_from_method(method: Arc<Object>, args: Vec<Arc<Object>>) -> Result<MethodContext, AmlError> {
+    fn new_from_method(
+        method: Arc<Object>,
+        args: Vec<Arc<Object>>,
+        scope: AmlName,
+    ) -> Result<MethodContext, AmlError> {
         if let Object::Method { code, flags } = &*method {
             if args.len() != flags.arg_count() {
                 return Err(AmlError::MethodArgCountIncorrect);
             }
-            let block = Block { stream: code as &[u8] as *const [u8], pc: 0, kind: BlockKind::Method };
+            let block = Block {
+                stream: code as &[u8] as *const [u8],
+                pc: 0,
+                kind: BlockKind::Method { method_scope: scope.clone() },
+            };
             let args = core::array::from_fn(|i| {
                 if let Some(arg) = args.get(i) { arg.clone() } else { Arc::new(Object::Uninitialized) }
             });
@@ -1012,7 +1061,7 @@ impl MethodContext {
                 in_flight: Vec::new(),
                 args,
                 locals: core::array::from_fn(|_| Arc::new(Object::Uninitialized)),
-                current_scope: AmlName::root(),
+                current_scope: scope,
                 _method: Some(method.clone()),
             };
             Ok(context)
@@ -1530,9 +1579,9 @@ mod tests {
     fn add_op() {
         let interpreter = Interpreter::new(TestHandler);
         // AddOp 0x0e 0x06 => Local2
-        interpreter.execute_method(&[0x72, 0x0b, 0x0e, 0x00, 0x0a, 0x06, 0x62]).unwrap();
+        interpreter.load_table(&[0x72, 0x0b, 0x0e, 0x00, 0x0a, 0x06, 0x62]).unwrap();
         // AddOp 0x0e (AddOp 0x01 0x03 => Local1) => Local1
-        interpreter.execute_method(&[0x72, 0x0a, 0x0e, 0x72, 0x0a, 0x01, 0x0a, 0x03, 0x61, 0x61]).unwrap();
+        interpreter.load_table(&[0x72, 0x0a, 0x0e, 0x72, 0x0a, 0x01, 0x0a, 0x03, 0x61, 0x61]).unwrap();
     }
 
     #[test]
@@ -1541,9 +1590,5 @@ mod tests {
             unsafe { MethodContext::new_from_table(b"\\\x2eABC_DEF_\0") }.namestring(),
             Ok(AmlName::from_str("\\ABC.DEF").unwrap())
         );
-        assert_eq!(
-            unsafe { MethodContext::new_from_table(b"\x2eABC_DEF_^_GHI") }.namestring(),
-            Ok(AmlName::from_str("ABC.DEF.^_GHI").unwrap())
-        );
     }
 }
diff --git a/aml/src/namespace.rs b/aml/src/namespace.rs
index 422956a4..e7360419 100644
--- a/aml/src/namespace.rs
+++ b/aml/src/namespace.rs
@@ -44,6 +44,20 @@ impl Namespace {
         Ok(())
     }
 
+    pub fn remove_level(&mut self, path: AmlName) -> Result<(), AmlError> {
+        assert!(path.is_absolute());
+        let path = path.normalize()?;
+
+        // Don't try to remove the root scope
+        // TODO: we probably shouldn't be able to remove the pre-defined scopes either?
+        if path != AmlName::root() {
+            let (level, last_seg) = self.get_level_for_path_mut(&path)?;
+            level.children.remove(&last_seg);
+        }
+
+        Ok(())
+    }
+
     pub fn insert(&mut self, path: AmlName, object: Arc<Object>) -> Result<(), AmlError> {
         assert!(path.is_absolute());
         let path = path.normalize()?;
@@ -221,7 +235,7 @@ pub enum NamespaceLevelKind {
     Processor,
     PowerResource,
     ThermalZone,
-    //MethodLocals,
+    MethodLocals,
 }
 
 pub struct NamespaceLevel {
diff --git a/tests/method.asl b/tests/method.asl
index 9c208325..35cc9a1b 100644
--- a/tests/method.asl
+++ b/tests/method.asl
@@ -1,8 +1,15 @@
 DefinitionBlock("method.aml", "DSDT", 1, "RSACPI", "METHOD", 1) {
+    Name(X, 5)
+
     Method(FOO, 0, NotSerialized) {
-        Return (0xff)
+        If (X > 1) {
+            Noop
+        } Else {
+            Return (0x55)
+        }
+        Return (0x3f)
     }
 
-    Name(X, 0)
-    X = FOO()
+    Name(Y, 0)
+    Y = FOO()
 }

From 3f616f5787d8a7841a786f22f266be901e52ff13 Mon Sep 17 00:00:00 2001
From: Isaac Woods <isaacwoods.home@gmail.com>
Date: Tue, 18 Feb 2025 00:00:58 +0000
Subject: [PATCH 38/88] `aml`: general cleanup

---
 acpi/src/lib.rs   |  3 ++
 acpi/src/madt.rs  |  2 +-
 aml/src/lib.rs    | 77 ++++++++++++++++++-----------------------------
 aml/src/object.rs |  3 --
 4 files changed, 33 insertions(+), 52 deletions(-)

diff --git a/acpi/src/lib.rs b/acpi/src/lib.rs
index 0b254df1..30f3ad6c 100644
--- a/acpi/src/lib.rs
+++ b/acpi/src/lib.rs
@@ -193,6 +193,9 @@ pub struct AcpiTables<H: AcpiHandler> {
     handler: H,
 }
 
+unsafe impl<H> Send for AcpiTables<H> where H: AcpiHandler + Send {}
+unsafe impl<H> Sync for AcpiTables<H> where H: AcpiHandler + Send {}
+
 impl<H> AcpiTables<H>
 where
     H: AcpiHandler,
diff --git a/acpi/src/madt.rs b/acpi/src/madt.rs
index a7c6408d..ff759bff 100644
--- a/acpi/src/madt.rs
+++ b/acpi/src/madt.rs
@@ -330,7 +330,7 @@ impl Madt {
     }
 
     pub fn supports_8259(&self) -> bool {
-        self.flags.get_bit(0)
+        { self.flags }.get_bit(0)
     }
 }
 
diff --git a/aml/src/lib.rs b/aml/src/lib.rs
index ad82104d..a7a0856f 100644
--- a/aml/src/lib.rs
+++ b/aml/src/lib.rs
@@ -1,8 +1,6 @@
 #![no_std]
-#![feature(let_chains, vec_pop_if)]
+#![feature(let_chains, inherent_str_constructors)]
 
-#[cfg(test)]
-extern crate std;
 
 extern crate alloc;
 
@@ -10,11 +8,17 @@ pub mod namespace;
 pub mod object;
 pub mod op_region;
 
-use alloc::{boxed::Box, sync::Arc, vec, vec::Vec};
+use alloc::{
+    boxed::Box,
+    string::{String, ToString},
+    sync::Arc,
+    vec,
+    vec::Vec,
+};
 use bit_field::BitField;
-use core::{mem, str};
+use core::mem;
 use namespace::{AmlName, Namespace, NamespaceLevelKind};
-use object::{MethodFlags, Object, ObjectType};
+use object::{MethodFlags, Object, ObjectType, ReferenceKind};
 use op_region::{OpRegion, RegionSpace};
 use spinning_top::Spinlock;
 
@@ -24,6 +28,9 @@ pub struct Interpreter {
     context_stack: Spinlock<Vec<MethodContext>>,
 }
 
+unsafe impl Send for Interpreter {}
+unsafe impl Sync for Interpreter {}
+
 impl Interpreter {
     pub fn new<H>(handler: H) -> Interpreter
     where
@@ -172,11 +179,7 @@ impl Interpreter {
                         );
                         context.current_block.pc += buffer_len;
 
-                        if let Some(prev_op) = context.in_flight.last_mut() {
-                            if prev_op.arguments.len() < prev_op.expected_arguments {
-                                prev_op.arguments.push(Argument::Object(Arc::new(Object::Buffer(buffer))));
-                            }
-                        }
+                        context.contribute_arg(Argument::Object(Arc::new(Object::Buffer(buffer))));
                     }
                     Opcode::Package => {
                         let mut elements = Vec::with_capacity(op.expected_arguments);
@@ -199,14 +202,7 @@ impl Interpreter {
                         assert_eq!(context.current_block.kind, BlockKind::Package);
                         assert_eq!(context.peek(), Err(AmlError::RunOutOfStream));
                         context.current_block = context.block_stack.pop().unwrap();
-
-                        if let Some(prev_op) = context.in_flight.last_mut() {
-                            if prev_op.arguments.len() < prev_op.expected_arguments {
-                                prev_op.arguments.push(Argument::Object(Arc::new(Object::Package(elements))));
-                            } else {
-                                panic!("Random package floating around?");
-                            }
-                        }
+                        context.contribute_arg(Argument::Object(Arc::new(Object::Package(elements))));
                     }
                     Opcode::If => {
                         let [
@@ -322,12 +318,7 @@ impl Interpreter {
 
                         if let Some(last) = self.context_stack.lock().pop() {
                             context = last;
-
-                            if let Some(prev_op) = context.in_flight.last_mut() {
-                                if prev_op.arguments.len() < prev_op.expected_arguments {
-                                    prev_op.arguments.push(Argument::Object(object.clone()));
-                                }
-                            }
+                            context.contribute_arg(Argument::Object(object.clone()));
                         } else {
                             /*
                              * If this is the top-most context, this is a `Return` from the actual
@@ -362,11 +353,7 @@ impl Interpreter {
                             ObjectType::RawDataBuffer => todo!(),
                         };
 
-                        if let Some(prev_op) = context.in_flight.last_mut() {
-                            if prev_op.arguments.len() < prev_op.expected_arguments {
-                                prev_op.arguments.push(Argument::Object(Arc::new(Object::Integer(typ))));
-                            }
-                        }
+                        context.contribute_arg(Argument::Object(Arc::new(Object::Integer(typ))));
                     }
                     _ => panic!("Unexpected operation has created in-flight op!"),
                 }
@@ -861,15 +848,7 @@ impl Interpreter {
         };
 
         *target.gain_mut() = Object::Integer(value);
-
-        // TODO: this is probs a slightly scuffed way of working out if the
-        // prev op wants our result
-        if let Some(prev_op) = context.in_flight.last_mut() {
-            if prev_op.arguments.len() < prev_op.expected_arguments {
-                prev_op.arguments.push(Argument::Object(Arc::new(Object::Integer(left + right))));
-            }
-        }
-
+        context.contribute_arg(Argument::Object(Arc::new(Object::Integer(value))));
         Ok(())
     }
 
@@ -899,9 +878,7 @@ impl Interpreter {
             _ => panic!(),
         };
 
-        if let Some(prev_op) = context.in_flight.last_mut() {
-            if prev_op.arguments.len() < prev_op.expected_arguments {
-                prev_op.arguments.push(Argument::Object(Arc::new(Object::Integer(result as u64))));
+        context.contribute_arg(Argument::Object(Arc::new(Object::Integer(result as u64))));
             }
         }
 
@@ -929,7 +906,7 @@ impl Interpreter {
                         let value = u64::from_le_bytes(buffer);
                         *target = value;
                     }
-                    _ => panic!(),
+                    _ => panic!("Store to integer from unsupported object: {:?}", object),
                 },
                 Object::BufferField { .. } => match object.gain_mut() {
                     Object::Integer(value) => {
@@ -991,8 +968,6 @@ struct Block {
     kind: BlockKind,
 }
 
-// TODO: we might need to impl Send + Sync for Block?
-
 impl Block {
     fn stream(&self) -> &[u8] {
         unsafe { &*self.stream }
@@ -1077,13 +1052,19 @@ impl MethodContext {
         }
     }
 
+    fn contribute_arg(&mut self, arg: Argument) {
+        if let Some(in_flight) = self.in_flight.last_mut() {
+            if in_flight.arguments.len() < in_flight.expected_arguments {
+                in_flight.arguments.push(arg);
+            }
+        }
+    }
+
     fn start_in_flight_op(&mut self, op: OpInFlight) {
-        println!("Starting in-flight op of type: {:?}", op);
         self.in_flight.push(op);
     }
 
     fn start_new_block(&mut self, kind: BlockKind, length: usize) {
-        println!("Starting new block at pc={}, length={}, kind={:?}", self.current_block.pc, length, kind);
         let block = Block {
             stream: &self.current_block.stream()[..(self.current_block.pc + length)] as *const [u8],
             pc: self.current_block.pc,
@@ -1546,7 +1527,7 @@ pub trait Handler: Send + Sync {
 #[cfg(test)]
 mod tests {
     use super::*;
-    use std::str::FromStr;
+    use core::str::FromStr;
 
     struct TestHandler;
     #[rustfmt::skip]
diff --git a/aml/src/object.rs b/aml/src/object.rs
index 51679964..5a267d6b 100644
--- a/aml/src/object.rs
+++ b/aml/src/object.rs
@@ -180,9 +180,6 @@ mod tests {
         let mut dst = [0b1110_0001, 0, 0, 0, 0];
 
         copy_bits(&src, 0, &mut dst, 2, 15);
-        for i in 0..dst.len() {
-            print!("{:08b} ", dst[i]);
-        }
         assert_eq!(dst, [0b1111_1101, 0b1101_1110, 0b0000_0001, 0b0000_0000, 0b0000_0000]);
     }
 }

From d45879d2b91312f59ae1b39d61187da85026ef39 Mon Sep 17 00:00:00 2001
From: Isaac Woods <isaacwoods.home@gmail.com>
Date: Tue, 18 Feb 2025 00:02:16 +0000
Subject: [PATCH 39/88] `aml`: support `DefBreakpoint` + some logical
 operations

---
 aml/src/lib.rs | 56 +++++++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 55 insertions(+), 1 deletion(-)

diff --git a/aml/src/lib.rs b/aml/src/lib.rs
index a7a0856f..ddc094a2 100644
--- a/aml/src/lib.rs
+++ b/aml/src/lib.rs
@@ -130,6 +130,17 @@ impl Interpreter {
 
                         *operand = new_value;
                     }
+                    Opcode::LAnd
+                    | Opcode::LOr
+                    | Opcode::LNot
+                    | Opcode::LNotEqual
+                    | Opcode::LLessEqual
+                    | Opcode::LGreaterEqual
+                    | Opcode::LEqual
+                    | Opcode::LGreater
+                    | Opcode::LLess => {
+                        self.do_logical_op(&mut context, op)?;
+                    }
                     Opcode::Name => {
                         let [Argument::Namestring(name), Argument::Object(object)] = &op.arguments[..] else {
                             panic!()
@@ -797,7 +808,9 @@ impl Interpreter {
                 Opcode::Noop => {}
                 Opcode::Return => context.start_in_flight_op(OpInFlight::new(Opcode::Return, 1)),
                 Opcode::Break => todo!(),
-                Opcode::Breakpoint => todo!(),
+                Opcode::Breakpoint => {
+                    self.handler.breakpoint();
+                }
                 Opcode::Ones => {
                     context.last_op()?.arguments.push(Argument::Object(Arc::new(Object::Integer(u64::MAX))));
                 }
@@ -879,9 +892,48 @@ impl Interpreter {
         };
 
         context.contribute_arg(Argument::Object(Arc::new(Object::Integer(result as u64))));
+        Ok(())
+    }
+
+    fn do_logical_op(&self, context: &mut MethodContext, op: OpInFlight) -> Result<(), AmlError> {
+        if op.op == Opcode::LNot {
+            let [Argument::Object(operand)] = &op.arguments[..] else { Err(AmlError::InvalidOperationOnObject)? };
+            let Object::Integer(operand) = **operand else { Err(AmlError::InvalidOperationOnObject)? };
+            let result = if operand == 0 { u64::MAX } else { 0 };
+
+            if let Some(prev_op) = context.in_flight.last_mut() {
+                if prev_op.arguments.len() < prev_op.expected_arguments {
+                    prev_op.arguments.push(Argument::Object(Arc::new(Object::Integer(result))));
+                }
             }
+
+            return Ok(());
         }
 
+        let [Argument::Object(left), Argument::Object(right)] = &op.arguments[..] else {
+            Err(AmlError::InvalidOperationOnObject)?
+        };
+
+        // TODO: for some of the ops, strings and buffers are also allowed :(
+        // TODO: apparently when doing this conversion (^), NT's interpreter just takes the first 4
+        // bytes of the string/buffer and casts them to an integer lmao
+        let Object::Integer(left) = **left else { Err(AmlError::InvalidOperationOnObject)? };
+        let Object::Integer(right) = **right else { Err(AmlError::InvalidOperationOnObject)? };
+
+        let result = match op.op {
+            Opcode::LAnd => (left > 0) && (right > 0),
+            Opcode::LOr => (left > 0) || (right > 0),
+            Opcode::LNotEqual => left != right,
+            Opcode::LLessEqual => left <= right,
+            Opcode::LGreaterEqual => left >= right,
+            Opcode::LEqual => left == right,
+            Opcode::LGreater => left > right,
+            Opcode::LLess => left < right,
+            _ => panic!(),
+        };
+        let result = if result { Object::Integer(u64::MAX) } else { Object::Integer(0) };
+
+        context.contribute_arg(Argument::Object(Arc::new(result)));
         Ok(())
     }
     fn do_store(
@@ -1516,6 +1568,8 @@ pub trait Handler: Send + Sync {
     /// time supported, and should relinquish the processor.
     fn sleep(&self, milliseconds: u64);
 
+    fn breakpoint(&self) {}
+
     fn handle_fatal_error(&self, fatal_type: u8, fatal_code: u32, fatal_arg: u64) {
         panic!(
             "Fatal error while executing AML (encountered DefFatalOp). fatal_type = {:?}, fatal_code = {:?}, fatal_arg = {:?}",

From 4e13b653ffaabeaa7551313c4edbb43f18ffcc82 Mon Sep 17 00:00:00 2001
From: Isaac Woods <isaacwoods.home@gmail.com>
Date: Tue, 18 Feb 2025 00:03:44 +0000
Subject: [PATCH 40/88] `aml`: introduce 'transparent' reference types + handle
 undefined package elements

---
 aml/src/lib.rs    | 40 ++++++++++++++++++++++++++++++----------
 aml/src/object.rs | 39 +++++++++++++++++++++++++++++++++++++--
 2 files changed, 67 insertions(+), 12 deletions(-)

diff --git a/aml/src/lib.rs b/aml/src/lib.rs
index ddc094a2..599bedfe 100644
--- a/aml/src/lib.rs
+++ b/aml/src/lib.rs
@@ -707,11 +707,17 @@ impl Interpreter {
                 Opcode::DataRegion => todo!(),
                 Opcode::Local(local) => {
                     let local = context.locals[local as usize].clone();
-                    context.last_op()?.arguments.push(Argument::Object(Arc::new(Object::Reference(local))));
+                    context.last_op()?.arguments.push(Argument::Object(Arc::new(Object::Reference {
+                        kind: ReferenceKind::LocalOrArg,
+                        inner: local,
+                    })));
                 }
                 Opcode::Arg(arg) => {
                     let arg = context.args[arg as usize].clone();
-                    context.last_op()?.arguments.push(Argument::Object(Arc::new(Object::Reference(arg))));
+                    context.last_op()?.arguments.push(Argument::Object(Arc::new(Object::Reference {
+                        kind: ReferenceKind::LocalOrArg,
+                        inner: arg,
+                    })));
                 }
                 Opcode::Store => context.start_in_flight_op(OpInFlight::new(Opcode::Store, 2)),
                 Opcode::RefOf => todo!(),
@@ -725,15 +731,28 @@ impl Interpreter {
                     context.current_block.pc -= 1;
                     let name = context.namestring()?;
 
-                    let (_, object) = self.namespace.lock().search(&name, &context.current_scope)?;
-                    if let Object::Method { flags, .. } = *object {
-                        context.start_in_flight_op(OpInFlight::new_with(
-                            Opcode::InternalMethodCall,
-                            flags.arg_count(),
-                        ))
-                    } else {
-                        context.last_op()?.arguments.push(Argument::Object(object));
+                    match self.namespace.lock().search(&name, &context.current_scope) {
+                        Ok((resolved_name, object)) => {
+                            if let Object::Method { flags, .. } = *object {
+                                context.start_in_flight_op(OpInFlight::new_with(
+                                    Opcode::InternalMethodCall,
                                     vec![Argument::Object(object), Argument::Namestring(resolved_name)],
+                                    flags.arg_count(),
+                                ))
+                            } else {
+                                context.last_op()?.arguments.push(Argument::Object(object));
+                            }
+                        }
+                        Err(AmlError::ObjectDoesNotExist(_)) => {
+                            if context.current_block.kind == BlockKind::Package {
+                                let reference = Object::Reference {
+                                    kind: ReferenceKind::Unresolved,
+                                    inner: Arc::new(Object::String(name.to_string())),
+                                };
+                                context.last_op()?.arguments.push(Argument::Object(Arc::new(reference)));
+                            }
+                        }
+                        Err(other) => Err(other)?,
                     }
                 }
 
@@ -946,6 +965,7 @@ impl Interpreter {
         // TODO: convert object to be of the type of destination, in line with 19.3.5 of the spec
         // TODO: write the object to the destination, including e.g. field writes that then lead to
         // literally god knows what.
+        let object = object.unwrap_transparent_reference();
         match target {
             Argument::Object(target) => match target.gain_mut() {
                 Object::Integer(target) => match object.gain_mut() {
diff --git a/aml/src/object.rs b/aml/src/object.rs
index 5a267d6b..08404009 100644
--- a/aml/src/object.rs
+++ b/aml/src/object.rs
@@ -13,7 +13,7 @@ pub enum Object {
     Integer(u64),
     Method { code: Vec<u8>, flags: MethodFlags },
     Mutex { sync_level: u8 },
-    Reference(Arc<Object>),
+    Reference { kind: ReferenceKind, inner: Arc<Object> },
     OpRegion(OpRegion),
     Package(Vec<Arc<Object>>),
     PowerResource { system_level: u8, resource_order: u16 },
@@ -77,7 +77,7 @@ impl Object {
             Object::Integer(_) => ObjectType::Integer,
             Object::Method { .. } => ObjectType::Method,
             Object::Mutex { .. } => ObjectType::Mutex,
-            Object::Reference(object) => object.typ(),
+            Object::Reference { inner, .. } => inner.typ(),
             Object::OpRegion(_) => ObjectType::OpRegion,
             Object::Package(_) => ObjectType::Package,
             Object::PowerResource { .. } => ObjectType::PowerResource,
@@ -87,6 +87,35 @@ impl Object {
             Object::ThermalZone => ObjectType::ThermalZone,
         }
     }
+
+    pub fn unwrap_reference(self: Arc<Object>) -> Arc<Object> {
+        let mut object = self;
+        loop {
+            if let Object::Reference { ref inner, .. } = *object {
+                object = inner.clone();
+            } else {
+                return object.clone();
+            }
+        }
+    }
+
+    /// Unwraps 'transparent' references (e.g. locals, arguments, and internal usage of reference-type objects), but maintain 'real'
+    /// references deliberately created by AML.
+    pub fn unwrap_transparent_reference(self: Arc<Self>) -> Arc<Object> {
+        let mut object = self;
+        loop {
+            // TODO: what should this do with unresolved namestrings? It would need namespace
+            // access to resolve them (and then this would probs have to move to a method on
+            // `Interpreter`)?
+            if let Object::Reference { kind, ref inner } = *object
+                && kind == ReferenceKind::LocalOrArg
+            {
+                object = inner.clone();
+            } else {
+                return object.clone();
+            }
+        }
+    }
 }
 
 #[derive(Debug)]
@@ -113,6 +142,12 @@ impl MethodFlags {
     }
 }
 
+#[derive(Clone, Copy, PartialEq, Debug)]
+pub enum ReferenceKind {
+    LocalOrArg,
+    Unresolved,
+}
+
 #[derive(Clone, Copy, PartialEq, Debug)]
 pub enum ObjectType {
     Uninitialized,

From 462f92ec13e85476895adccb258f97d7b17f83c9 Mon Sep 17 00:00:00 2001
From: Isaac Woods <isaacwoods.home@gmail.com>
Date: Tue, 18 Feb 2025 00:11:45 +0000
Subject: [PATCH 41/88] `aml`: re-add old resource descriptor and `_PRT`
 parsing

---
 aml/Cargo.toml         |   1 +
 aml/src/lib.rs         |  11 +
 aml/src/namespace.rs   |  25 ++
 aml/src/pci_routing.rs | 185 +++++++++
 aml/src/resource.rs    | 833 +++++++++++++++++++++++++++++++++++++++++
 5 files changed, 1055 insertions(+)
 create mode 100644 aml/src/pci_routing.rs
 create mode 100644 aml/src/resource.rs

diff --git a/aml/Cargo.toml b/aml/Cargo.toml
index eef7eae7..afd1e859 100644
--- a/aml/Cargo.toml
+++ b/aml/Cargo.toml
@@ -12,3 +12,4 @@ license = "MIT/Apache-2.0"
 [dependencies]
 spinning_top = "0.3.0"
 bit_field = "0.10.2"
+byteorder = { version = "1.5.0", default-features = false }
diff --git a/aml/src/lib.rs b/aml/src/lib.rs
index 599bedfe..02e90d14 100644
--- a/aml/src/lib.rs
+++ b/aml/src/lib.rs
@@ -7,6 +7,8 @@ extern crate alloc;
 pub mod namespace;
 pub mod object;
 pub mod op_region;
+pub mod pci_routing;
+pub mod resource;
 
 use alloc::{
     boxed::Box,
@@ -1547,6 +1549,15 @@ pub enum AmlError {
     MethodArgCountIncorrect,
 
     InvalidOperationOnObject,
+
+    InvalidResourceDescriptor,
+    UnexpectedResourceType,
+
+    PrtInvalidAddress,
+    PrtInvalidPin,
+    PrtInvalidGsi,
+    PrtInvalidSource,
+    PrtNoEntry,
 }
 
 /// This trait represents the interface from the `Interpreter` to the hosting kernel, and allows
diff --git a/aml/src/namespace.rs b/aml/src/namespace.rs
index e7360419..49ba4199 100644
--- a/aml/src/namespace.rs
+++ b/aml/src/namespace.rs
@@ -125,6 +125,31 @@ impl Namespace {
         }
     }
 
+    pub fn search_for_level(&self, level_name: &AmlName, starting_scope: &AmlName) -> Result<AmlName, AmlError> {
+        if level_name.search_rules_apply() {
+            let mut scope = starting_scope.clone().normalize()?;
+            assert!(scope.is_absolute());
+
+            loop {
+                let name = level_name.resolve(&scope)?;
+                if let Ok((level, last_seg)) = self.get_level_for_path(&name) {
+                    if level.children.contains_key(&last_seg) {
+                        return Ok(name);
+                    }
+                }
+
+                // If we don't find it, move the scope up a level and search for it there recursively
+                match scope.parent() {
+                    Ok(parent) => scope = parent,
+                    Err(AmlError::RootHasNoParent) => return Err(AmlError::LevelDoesNotExist(level_name.clone())),
+                    Err(err) => return Err(err),
+                }
+            }
+        } else {
+            Ok(level_name.clone())
+        }
+    }
+
     /// 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(&self, path: &AmlName) -> Result<(&NamespaceLevel, NameSeg), AmlError> {
diff --git a/aml/src/pci_routing.rs b/aml/src/pci_routing.rs
new file mode 100644
index 00000000..b65a3b84
--- /dev/null
+++ b/aml/src/pci_routing.rs
@@ -0,0 +1,185 @@
+use crate::{
+    AmlError,
+    Interpreter,
+    namespace::AmlName,
+    object::{Object, ReferenceKind},
+    resource::{self, InterruptPolarity, InterruptTrigger, Resource},
+};
+use alloc::{vec, vec::Vec};
+use bit_field::BitField;
+use core::str::FromStr;
+
+pub use crate::resource::IrqDescriptor;
+
+#[derive(Clone, Copy, PartialEq, Eq, Debug)]
+pub enum Pin {
+    IntA,
+    IntB,
+    IntC,
+    IntD,
+}
+
+#[derive(Debug)]
+pub enum PciRouteType {
+    /// The interrupt is hard-coded to a specific GSI
+    Gsi(u32),
+
+    /// The interrupt is linked to a link object. This object will have `_PRS`, `_CRS` fields and a `_SRS` method
+    /// that can be used to allocate the interrupt. Note that some platforms (e.g. QEMU's q35 chipset) use link
+    /// objects but do not support changing the interrupt that it's linked to (i.e. `_SRS` doesn't do anything).
+    /*
+     * The actual object itself will just be a `Device`, and we need paths to its children objects to do
+     * anything useful, so we just store the resolved name here.
+     */
+    LinkObject(AmlName),
+}
+
+#[derive(Debug)]
+pub struct PciRoute {
+    device: u16,
+    function: u16,
+    pin: Pin,
+    route_type: PciRouteType,
+}
+
+/// A `PciRoutingTable` is used to interpret the data in a `_PRT` object, which provides a mapping
+/// from PCI interrupt pins to the inputs of the interrupt controller. One of these objects must be
+/// present under each PCI root bridge, and consists of a package of packages, each of which describes the
+/// mapping of a single PCI interrupt pin.
+#[derive(Debug)]
+pub struct PciRoutingTable {
+    entries: Vec<PciRoute>,
+}
+
+impl PciRoutingTable {
+    /// Construct a `PciRoutingTable` from a path to a `_PRT` object. Returns
+    /// `AmlError::InvalidOperationOnObject` if the value passed is not a package, or if any of the
+    /// values within it are not packages. Returns the various `AmlError::Prt*` errors if the
+    /// internal structure of the entries is invalid.
+    pub fn from_prt_path(prt_path: AmlName, interpreter: &Interpreter) -> Result<PciRoutingTable, AmlError> {
+        let mut entries = Vec::new();
+
+        let prt = interpreter.invoke_method(prt_path.clone(), vec![])?;
+
+        if let Object::Package(ref inner_values) = *prt {
+            for value in inner_values {
+                if let Object::Package(ref pin_package) = **value {
+                    /*
+                     * Each inner package has the following structure:
+                     *   | Field      | Type      | Description                                               |
+                     *   | -----------|-----------|-----------------------------------------------------------|
+                     *   | Address    | Dword     | Address of the device. Same format as _ADR objects (high  |
+                     *   |            |           | word = #device, low word = #function)                     |
+                     *   | -----------|-----------|-----------------------------------------------------------|
+                     *   | Pin        | Byte      | The PCI pin (0 = INTA, 1 = INTB, 2 = INTC, 3 = INTD)      |
+                     *   | -----------|-----------|-----------------------------------------------------------|
+                     *   | Source     | Byte or   | Name of the device that allocates the interrupt to which  |
+                     *   |            | NamePath  | the above pin is connected. Can be fully qualified,       |
+                     *   |            |           | relative, or a simple NameSeg that utilizes namespace     |
+                     *   |            |           | search rules. Instead, if this is a byte value of 0, the  |
+                     *   |            |           | interrupt is allocated out of the GSI pool, and Source    |
+                     *   |            |           | Index should be utilised.                                 |
+                     *   | -----------|-----------|-----------------------------------------------------------|
+                     *   | Source     | Dword     | Index that indicates which resource descriptor in the     |
+                     *   | Index      |           | resource template of the device pointed to in the Source  |
+                     *   |            |           | field this interrupt is allocated from. If the Source     |
+                     *   |            |           | is zero, then this field is the GSI number to which the   |
+                     *   |            |           | pin is connected.                                         |
+                     *   | -----------|-----------|-----------------------------------------------------------|
+                     */
+                    let Object::Integer(address) = *pin_package[0] else {
+                        return Err(AmlError::PrtInvalidAddress);
+                    };
+                    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] {
+                        Object::Integer(0) => Pin::IntA,
+                        Object::Integer(1) => Pin::IntB,
+                        Object::Integer(2) => Pin::IntC,
+                        Object::Integer(3) => Pin::IntD,
+                        _ => return Err(AmlError::PrtInvalidPin),
+                    };
+
+                    match *pin_package[2] {
+                        Object::Integer(0) => {
+                            /*
+                             * The Source Index field contains the GSI number that this interrupt is attached
+                             * to.
+                             */
+                            let Object::Integer(gsi) = *pin_package[3] else {
+                                return Err(AmlError::PrtInvalidGsi);
+                            };
+                            entries.push(PciRoute {
+                                device,
+                                function,
+                                pin,
+                                route_type: PciRouteType::Gsi(gsi as u32),
+                            });
+                        }
+                        Object::Reference { kind: ReferenceKind::Unresolved, ref inner } => {
+                            let Object::String(ref name) = **inner else { panic!() };
+                            let link_object_name = interpreter
+                                .namespace
+                                .lock()
+                                .search_for_level(&AmlName::from_str(&name)?, &prt_path)?;
+                            entries.push(PciRoute {
+                                device,
+                                function,
+                                pin,
+                                route_type: PciRouteType::LinkObject(link_object_name),
+                            });
+                        }
+                        _ => return Err(AmlError::PrtInvalidSource),
+                    }
+                } else {
+                    return Err(AmlError::InvalidOperationOnObject);
+                }
+            }
+
+            Ok(PciRoutingTable { entries })
+        } else {
+            Err(AmlError::InvalidOperationOnObject)
+        }
+    }
+
+    /// Get the interrupt input that a given PCI interrupt pin is wired to. Returns `AmlError::PrtNoEntry` if the
+    /// PRT doesn't contain an entry for the given address + pin.
+    pub fn route(
+        &self,
+        device: u16,
+        function: u16,
+        pin: Pin,
+        interpreter: &Interpreter,
+    ) -> Result<IrqDescriptor, AmlError> {
+        let entry = self
+            .entries
+            .iter()
+            .find(|entry| {
+                entry.device == device
+                    && (entry.function == 0xffff || entry.function == function)
+                    && entry.pin == pin
+            })
+            .ok_or(AmlError::PrtNoEntry)?;
+
+        match entry.route_type {
+            PciRouteType::Gsi(gsi) => Ok(IrqDescriptor {
+                is_consumer: true,
+                trigger: InterruptTrigger::Level,
+                polarity: InterruptPolarity::ActiveLow,
+                is_shared: true,
+                is_wake_capable: false,
+                irq: gsi,
+            }),
+            PciRouteType::LinkObject(ref name) => {
+                let path = AmlName::from_str("_CRS").unwrap().resolve(name)?;
+                let link_crs = interpreter.invoke_method(path, vec![])?;
+
+                let resources = resource::resource_descriptor_list(link_crs)?;
+                match resources.as_slice() {
+                    [Resource::Irq(descriptor)] => Ok(descriptor.clone()),
+                    _ => Err(AmlError::UnexpectedResourceType),
+                }
+            }
+        }
+    }
+}
diff --git a/aml/src/resource.rs b/aml/src/resource.rs
new file mode 100644
index 00000000..60e27def
--- /dev/null
+++ b/aml/src/resource.rs
@@ -0,0 +1,833 @@
+use crate::{AmlError, object::Object};
+use alloc::{sync::Arc, vec::Vec};
+use bit_field::BitField;
+use byteorder::{ByteOrder, LittleEndian};
+use core::mem;
+
+#[derive(Debug, PartialEq, Eq)]
+pub enum Resource {
+    Irq(IrqDescriptor),
+    AddressSpace(AddressSpaceDescriptor),
+    MemoryRange(MemoryRangeDescriptor),
+    IOPort(IOPortDescriptor),
+    Dma(DMADescriptor),
+}
+
+/// Parse a `ResourceDescriptor` buffer into a list of resources.
+pub fn resource_descriptor_list(descriptor: Arc<Object>) -> Result<Vec<Resource>, AmlError> {
+    if let Object::Buffer(ref bytes) = *descriptor {
+        let mut descriptors = Vec::new();
+        let mut bytes = bytes.as_slice();
+
+        while !bytes.is_empty() {
+            let (descriptor, remaining_bytes) = resource_descriptor(bytes)?;
+
+            if let Some(descriptor) = descriptor {
+                descriptors.push(descriptor);
+                bytes = remaining_bytes;
+            } else {
+                break;
+            }
+        }
+
+        Ok(descriptors)
+    } else {
+        Err(AmlError::InvalidOperationOnObject)
+    }
+}
+
+fn resource_descriptor(bytes: &[u8]) -> Result<(Option<Resource>, &[u8]), AmlError> {
+    /*
+     * If bit 7 of Byte 0 is set, it's a large descriptor. If not, it's a small descriptor.
+     */
+    if bytes[0].get_bit(7) {
+        /*
+         * We're parsing a large item. The descriptor type is encoded in Bits 0-6 of Byte 0. Valid types:
+         *      0x00: Reserved
+         *      0x01: 24-bit Memory Range Descriptor
+         *      0x02: Generic Register Descriptor
+         *      0x03: Reserved
+         *      0x04: Vendor-defined Descriptor
+         *      0x05: 32-bit Memory Range Descriptor
+         *      0x06: 32-bit Fixed Memory Range Descriptor
+         *      0x07: Address Space Resource Descriptor
+         *      0x08: Word Address Space Descriptor
+         *      0x09: Extended Interrupt Descriptor
+         *      0x0a: QWord Address Space Descriptor
+         *      0x0b: Extended Address Space Descriptor
+         *      0x0c: GPIO Connection Descriptor
+         *      0x0d: Pin Function Descriptor
+         *      0x0e: GenericSerialBus Connection Descriptor
+         *      0x0f: Pin Configuration Descriptor
+         *      0x10: Pin Group Descriptor
+         *      0x11: Pin Group Function Descriptor
+         *      0x12: Pin Group Configuration Descriptor
+         *      0x13-0x7f: Reserved
+         *
+         * Byte 1 contains bits 0-7 of the length, and Byte 2 contains bits 8-15 of the length. Subsequent
+         * bytes contain the actual data items.
+         */
+        let descriptor_type = bytes[0].get_bits(0..7);
+        let length = LittleEndian::read_u16(&bytes[1..=2]) as usize;
+        let (descriptor_bytes, remaining_bytes) = bytes.split_at(length + 3);
+
+        let descriptor = match descriptor_type {
+            0x01 => unimplemented!("24-bit Memory Range Descriptor"),
+            0x02 => unimplemented!("Generic Register Descriptor"),
+            0x03 => unimplemented!("0x03 Reserved"),
+            0x04 => unimplemented!("Vendor-defined Descriptor"),
+            0x05 => unimplemented!("32-bit Memory Range Descriptor"),
+            0x06 => fixed_memory_descriptor(descriptor_bytes),
+            0x07 => address_space_descriptor::<u32>(descriptor_bytes),
+            0x08 => address_space_descriptor::<u16>(descriptor_bytes),
+            0x09 => extended_interrupt_descriptor(descriptor_bytes),
+            0x0a => address_space_descriptor::<u64>(descriptor_bytes),
+            0x0b => unimplemented!("Extended Address Space Descriptor"),
+            0x0c => unimplemented!("GPIO Connection Descriptor"),
+            0x0d => unimplemented!("Pin Function Descriptor"),
+            0x0e => unimplemented!("GenericSerialBus Connection Descriptor"),
+            0x0f => unimplemented!("Pin Configuration Descriptor"),
+            0x10 => unimplemented!("Pin Group Descriptor"),
+            0x11 => unimplemented!("Pin Group Function Descriptor"),
+            0x12 => unimplemented!("Pin Group Configuration Descriptor"),
+
+            0x00 | 0x13..=0x7f => Err(AmlError::InvalidResourceDescriptor),
+            0x80..=0xff => unreachable!(),
+        }?;
+
+        Ok((Some(descriptor), remaining_bytes))
+    } else {
+        /*
+         * We're parsing a small descriptor. Byte 0 has the format:
+         *    | Bits        | Field             |
+         *    |-------------|-------------------|
+         *    | 0-2         | Length - n bytes  |
+         *    | 3-6         | Small item type   |
+         *    | 7           | 0 = small item    |
+         *
+         * The valid types are:
+         *      0x00-0x03: Reserved
+         *      0x04: IRQ Format Descriptor
+         *      0x05: DMA Format Descriptor
+         *      0x06: Start Dependent Functions Descriptor
+         *      0x07: End Dependent Functions Descriptor
+         *      0x08: IO Port Descriptor
+         *      0x09: Fixed Location IO Port Descriptor
+         *      0x0A: Fixed DMA Descriptor
+         *      0x0B-0x0D: Reserved
+         *      0x0E: Vendor Defined Descriptor
+         *      0x0F: End Tag Descriptor
+         */
+        let descriptor_type = bytes[0].get_bits(3..=6);
+        let length: usize = bytes[0].get_bits(0..=2) as usize;
+        let (descriptor_bytes, remaining_bytes) = bytes.split_at(length + 1);
+
+        let descriptor = match descriptor_type {
+            0x00..=0x03 => Err(AmlError::InvalidResourceDescriptor),
+            0x04 => irq_format_descriptor(descriptor_bytes),
+            0x05 => dma_format_descriptor(descriptor_bytes),
+            0x06 => unimplemented!("Start Dependent Functions Descriptor"),
+            0x07 => unimplemented!("End Dependent Functions Descriptor"),
+            0x08 => io_port_descriptor(descriptor_bytes),
+            0x09 => unimplemented!("Fixed Location IO Port Descriptor"),
+            0x0A => unimplemented!("Fixed DMA Descriptor"),
+            0x0B..=0x0D => Err(AmlError::InvalidResourceDescriptor),
+            0x0E => unimplemented!("Vendor Defined Descriptor"),
+            0x0F => return Ok((None, &[])),
+            0x10..=0xFF => unreachable!(),
+        }?;
+
+        Ok((Some(descriptor), remaining_bytes))
+    }
+}
+
+#[derive(Debug, PartialEq, Eq, Copy, Clone)]
+pub enum InterruptTrigger {
+    Edge,
+    Level,
+}
+
+#[derive(Debug, PartialEq, Eq, Copy, Clone)]
+pub enum InterruptPolarity {
+    ActiveHigh,
+    ActiveLow,
+}
+
+#[derive(Debug, PartialEq, Eq, Copy, Clone)]
+pub enum AddressSpaceResourceType {
+    MemoryRange,
+    IORange,
+    BusNumberRange,
+}
+
+#[derive(Debug, PartialEq, Eq, Copy, Clone)]
+pub enum AddressSpaceDecodeType {
+    Additive,
+    Subtractive,
+}
+
+#[derive(Debug, PartialEq, Eq)]
+pub struct AddressSpaceDescriptor {
+    pub resource_type: AddressSpaceResourceType,
+    pub is_maximum_address_fixed: bool,
+    pub is_minimum_address_fixed: bool,
+    pub decode_type: AddressSpaceDecodeType,
+
+    pub granularity: u64,
+    pub address_range: (u64, u64),
+    pub translation_offset: u64,
+    pub length: u64,
+}
+
+#[derive(Debug, PartialEq, Eq)]
+pub enum MemoryRangeDescriptor {
+    FixedLocation { is_writable: bool, base_address: u32, range_length: u32 },
+}
+
+fn fixed_memory_descriptor(bytes: &[u8]) -> Result<Resource, AmlError> {
+    /*
+     * -- 32-bit Fixed Memory Descriptor ---
+     * Offset     Field Name                              Definition
+     * Byte 0     32-bit Fixed Memory Range Descriptor    Value = 0x86 (10000110B) – Type = 1, Large item name = 0x06
+     * Byte 1     Length, bits [7:0]                      Value = 0x09 (9)
+     * Byte 2     Length, bits [15:8]                     Value = 0x00
+     * Byte 3     Information                             This field provides extra information about this memory.
+     *                                                    Bit [7:1]   Ignored
+     *                                                    Bit [0]     Write status, _RW
+     *                                                        1  writeable (read/write)
+     *                                                        0  non-writeable (read-only)
+     * Byte 4     Range base address, _BAS bits [7:0]     Address bits [7:0] of the base memory address for which the card may be configured.
+     * Byte 5     Range base address, _BAS bits [15:8]    Address bits [15:8] of the base memory address for which the card may be configured.
+     * Byte 6     Range base address, _BAS bits [23:16]   Address bits [23:16] of the base memory address for which the card may be configured.
+     * Byte 7     Range base address, _BAS bits [31:24]   Address bits [31:24] of the base memory address for which the card may be configured.
+     * Byte 8     Range length, _LEN bits [7:0]           This field contains bits [7:0] of the memory range length. The range length provides the length of the memory range in 1-byte blocks.
+     * Byte 9     Range length, _LEN bits [15:8]          This field contains bits [15:8] of the memory range length. The range length provides the length of the memory range in 1-byte blocks.
+     * Byte 10    Range length, _LEN bits [23:16]         This field contains bits [23:16] of the memory range length. The range length provides the length of the memory range in 1-byte blocks.
+     * Byte 11    Range length, _LEN bits [31:24]         This field contains bits [31:24] of the memory range length. The range length provides the length of the memory range in 1-byte blocks.
+     */
+    if bytes.len() != 12 {
+        return Err(AmlError::InvalidResourceDescriptor);
+    }
+
+    let information = bytes[3];
+    let is_writable = information.get_bit(0);
+
+    let base_address = LittleEndian::read_u32(&bytes[4..=7]);
+    let range_length = LittleEndian::read_u32(&bytes[8..=11]);
+
+    Ok(Resource::MemoryRange(MemoryRangeDescriptor::FixedLocation { is_writable, base_address, range_length }))
+}
+
+fn address_space_descriptor<T>(bytes: &[u8]) -> Result<Resource, AmlError> {
+    /*
+     * WORD Address Space Descriptor Definition
+     * Note: The definitions for DWORD and QWORD are the same other than the width of the address fields.
+     *
+     * Offset  Field Name                                   Definition
+     * Byte 0  WORD Address Space Descriptor                Value = 0x88 (10001000B) – Type = 1, Large item name = 0x08
+     * Byte 1  Length, bits [7:0]                           Variable length, minimum value = 0x0D (13)
+     * Byte 2  Length, bits [15:8]                          Variable length, minimum value = 0x00
+     * Byte 3  Resource Type                                Indicates which type of resource this descriptor describes. Defined values are:
+     *                                                        0         Memory range
+     *                                                        1         I/O range
+     *                                                        2         Bus number range
+     *                                                        3–191     Reserved
+     *                                                        192-255   Hardware Vendor Defined
+     * Byte 4  General Flags                                Flags that are common to all resource types:
+     *                                                        Bits [7:4]   Reserved (must be 0)
+     *                                                        Bit [3]     Max Address Fixed, _MAF:
+     *                                                          1  The specified maximum address is fixed
+     *                                                          0  The specified maximum address is not fixed
+     *                                                             and can be changed
+     *                                                        Bit [2]      Min Address Fixed,_MIF:
+     *                                                          1   The specified minimum address is fixed
+     *                                                          0   The specified minimum address is not fixed
+     *                                                              and can be changed
+     *                                                        Bit [1]      Decode Type, _DEC:
+     *                                                          1   This bridge subtractively decodes this address          (top level bridges only)
+     *                                                          0   This bridge positively decodes this address
+     *                                                        Bit [0]      Ignored
+     * Byte 5  Type Specific Flags                           Flags that are specific to each resource type. The meaning of the flags in this field depends on the value of the Resource Type field (see above).
+     * Byte 6  Address space granularity, _GRA bits[7:0]     A set bit in this mask means that this bit is decoded. All bits less significant than the most significant set bit must be set. (In other words, the value of the full Address Space Granularity field (all 16 bits) must be a number (2n-1).
+     * Byte 7  Address space granularity, _GRA bits[15:8]
+     * Byte 8  Address range minimum, _MIN, bits [7:0]       For bridges that translate addresses, this is the address space on the secondary side of the bridge.
+     * Byte 9  Address range minimum, _MIN, bits [15:8]
+     * Byte 10 Address range maximum, _MAX, bits [7:0]       For bridges that translate addresses, this is the address space on the secondary side of the bridge.
+     * Byte 11 Address range maximum, _MAX, bits [15:8]
+     * Byte 12 Address Translation offset, _TRA, bits [7:0]  For bridges that translate addresses across the bridge, this is the offset that must be added to the address on the secondary side to obtain the address on the primary side. Non-bridge devices must list 0 for all Address Translation offset bits.
+     * Byte 13 Address Translation offset, _TRA, bits [15:8]
+     * Byte 14 Address Length, _LEN, bits [7:0]
+     * Byte 15 Address Length, _LEN, bits [15:8]
+     * Byte 16 Resource Source Index (Optional)              Only present if Resource Source (below) is present. This field gives an index to the specific resource descriptor that this device consumes from in the current resource template for the device object pointed to in Resource Source.
+     * String  Resource Source (Optional)                    If present, the device that uses this descriptor consumes its resources from the resources produced by the named device object. If not present, the device consumes its resources out of a global pool. If not present, the device consumes this resource from its hierarchical parent.
+     */
+    let size = mem::size_of::<T>();
+
+    if bytes.len() < 6 + size * 5 {
+        return Err(AmlError::InvalidResourceDescriptor);
+    }
+
+    let resource_type = match bytes[3] {
+        0 => AddressSpaceResourceType::MemoryRange,
+        1 => AddressSpaceResourceType::IORange,
+        2 => AddressSpaceResourceType::BusNumberRange,
+        3..=191 => return Err(AmlError::InvalidResourceDescriptor),
+        192..=255 => unimplemented!(),
+    };
+
+    let general_flags = bytes[4];
+    let is_maximum_address_fixed = general_flags.get_bit(3);
+    let is_minimum_address_fixed = general_flags.get_bit(2);
+    let decode_type = if general_flags.get_bit(1) {
+        AddressSpaceDecodeType::Subtractive
+    } else {
+        AddressSpaceDecodeType::Additive
+    };
+
+    let mut address_fields = bytes[6..].chunks_exact(size);
+
+    // it's safe to unwrap because we check the length at the top
+    let granularity = LittleEndian::read_uint(address_fields.next().unwrap(), size);
+    let address_range_min = LittleEndian::read_uint(address_fields.next().unwrap(), size);
+    let address_range_max = LittleEndian::read_uint(address_fields.next().unwrap(), size);
+    let translation_offset = LittleEndian::read_uint(address_fields.next().unwrap(), size);
+    let length = LittleEndian::read_uint(address_fields.next().unwrap(), size);
+
+    Ok(Resource::AddressSpace(AddressSpaceDescriptor {
+        resource_type,
+        is_maximum_address_fixed,
+        is_minimum_address_fixed,
+        decode_type,
+        granularity,
+        address_range: (address_range_min, address_range_max),
+        translation_offset,
+        length,
+    }))
+}
+
+#[derive(Debug, PartialEq, Eq, Clone)]
+pub struct IrqDescriptor {
+    pub is_consumer: bool,
+    pub trigger: InterruptTrigger,
+    pub polarity: InterruptPolarity,
+    pub is_shared: bool,
+    pub is_wake_capable: bool,
+    /*
+     * NOTE: We currently only support the cases where a descriptor only contains a single interrupt
+     * number.
+     */
+    pub irq: u32,
+}
+
+fn irq_format_descriptor(bytes: &[u8]) -> Result<Resource, AmlError> {
+    /*
+     * IRQ Descriptor Definition
+     *
+     * Offset   Field Name
+     * Byte 0   Value = 0x22 or 0x23 (0010001nB)– Type = 0, Small item name = 0x4, Length = 2 or 3
+     * Byte 1   IRQ mask bits[7:0], _INT
+     *          Bit [0] represents IRQ0, bit[1] is IRQ1, and so on.
+     * Byte 2   IRQ mask bits[15:8], _INT
+     *          Bit [0] represents IRQ8, bit[1] is IRQ9, and so on.
+     * Byte 3   IRQ Information. Each bit, when set, indicates this device is capable of driving a certain type of interrupt.
+     *          (Optional—if not included then assume edge sensitive, high true interrupts.)
+     *          These bits can be used both for reporting and setting IRQ resources.
+     *          Note: This descriptor is meant for describing interrupts that are connected to PIC-compatible interrupt
+     *                controllers, which can only be programmed for Active-High-Edge-Triggered or Active-Low-Level-Triggered
+     *                interrupts. Any other combination is invalid. The Extended Interrupt Descriptor can be used to describe
+     *                other combinations.
+     *            Bit [7:6]  Reserved (must be 0)
+     *            Bit [5]    Wake Capability, _WKC
+     *              0x0 = Not Wake Capable: This interrupt is not capable of waking the system.
+     *              0x1 = Wake Capable: This interrupt is capable of waking the system from a
+     *                    low-power idle state or a system sleep state.
+     *            Bit [4]    Interrupt Sharing, _SHR
+     *              0x0 = Exclusive: This interrupt is not shared with other devices.
+     *              0x1 = Shared: This interrupt is shared with other devices.
+     *            Bit [3]    Interrupt Polarity, _LL
+     *              0  Active-High – This interrupt is sampled when the signal is high, or true
+     *              1  Active-Low – This interrupt is sampled when the signal is low, or false.
+     *            Bit [2:1]  Ignored
+     *            Bit [0]    Interrupt Mode, _HE
+     *              0  Level-Triggered – Interrupt is triggered in response to signal in a low state.
+     *              1  Edge-Triggered – Interrupt is triggered in response to a change in signal state from low to high.
+     */
+
+    match bytes.len() {
+        0..=2 => Err(AmlError::InvalidResourceDescriptor),
+        3 => {
+            // no IRQ information ("length 2" in spec)
+            let irq = LittleEndian::read_u16(&bytes[1..=2]);
+
+            Ok(Resource::Irq(IrqDescriptor {
+                irq: irq as u32,
+                is_wake_capable: false,
+                is_shared: false,
+                polarity: InterruptPolarity::ActiveHigh,
+                trigger: InterruptTrigger::Edge,
+
+                is_consumer: false, // assumed to be producer
+            }))
+        }
+        4 => {
+            // with IRQ information ("length 3" in spec)
+            let irq = LittleEndian::read_u16(&bytes[1..=2]);
+
+            let information = bytes[3];
+            let is_wake_capable = information.get_bit(5);
+            let is_shared = information.get_bit(4);
+            let polarity = match information.get_bit(3) {
+                false => InterruptPolarity::ActiveHigh,
+                true => InterruptPolarity::ActiveLow,
+            };
+            let trigger = match information.get_bit(0) {
+                false => InterruptTrigger::Level,
+                true => InterruptTrigger::Edge,
+            };
+
+            Ok(Resource::Irq(IrqDescriptor {
+                irq: irq as u32,
+                is_wake_capable,
+                is_shared,
+                polarity,
+                trigger,
+
+                is_consumer: false, // assumed to be producer
+            }))
+        }
+        _ => Err(AmlError::InvalidResourceDescriptor),
+    }
+}
+
+#[derive(Debug, PartialEq, Eq)]
+pub enum DMASupportedSpeed {
+    CompatibilityMode,
+    TypeA, // as described by the EISA
+    TypeB,
+    TypeF,
+}
+
+#[derive(Debug, PartialEq, Eq)]
+pub enum DMATransferTypePreference {
+    _8BitOnly,
+    _8And16Bit,
+    _16Bit,
+}
+
+#[derive(Debug, PartialEq, Eq)]
+pub struct DMADescriptor {
+    pub channel_mask: u8,
+    pub supported_speeds: DMASupportedSpeed,
+    pub is_bus_master: bool,
+    pub transfer_type_preference: DMATransferTypePreference,
+}
+
+pub fn dma_format_descriptor(bytes: &[u8]) -> Result<Resource, AmlError> {
+    /*
+     * DMA Descriptor Definition
+     * Offset  Field Name
+     * Byte 0  Value = 0x2A (00101010B) – Type = 0, Small item name = 0x5, Length = 2
+     * Byte 1  DMA channel mask bits [7:0] (channels 0 – 7), _DMA
+     *         Bit [0] is channel 0, etc.
+     * Byte 2  Bit [7]           Reserved (must be 0)
+     *         Bits [6:5]        DMA channel speed supported, _TYP
+     *           00    Indicates compatibility mode
+     *           01    Indicates Type A DMA as described in the EISA
+     *           10    Indicates Type B DMA
+     *           11    Indicates Type F
+     *         Bits [4:3]        Ignored
+     *         Bit [2]           Logical device bus master status, _BM
+     *           0      Logical device is not a bus master
+     *           1      Logical device is a bus master
+     *         Bits [1:0]       DMA transfer type preference, _SIZ
+     *           00    8-bit only
+     *           01    8- and 16-bit
+     *           10    16-bit only
+     *           11    Reserved
+     */
+    if bytes.len() != 3 {
+        return Err(AmlError::InvalidResourceDescriptor);
+    }
+
+    let channel_mask = bytes[1];
+    let options = bytes[2];
+    let supported_speeds = match options.get_bits(5..=6) {
+        0 => DMASupportedSpeed::CompatibilityMode,
+        1 => DMASupportedSpeed::TypeA,
+        2 => DMASupportedSpeed::TypeB,
+        3 => DMASupportedSpeed::TypeF,
+        _ => unreachable!(),
+    };
+    let is_bus_master = options.get_bit(2);
+    let transfer_type_preference = match options.get_bits(0..=1) {
+        0 => DMATransferTypePreference::_8BitOnly,
+        1 => DMATransferTypePreference::_8And16Bit,
+        2 => DMATransferTypePreference::_16Bit,
+        3 => unimplemented!("Reserved DMA transfer type preference"),
+        _ => unreachable!(),
+    };
+
+    Ok(Resource::Dma(DMADescriptor { channel_mask, supported_speeds, is_bus_master, transfer_type_preference }))
+}
+
+#[derive(Debug, PartialEq, Eq)]
+pub struct IOPortDescriptor {
+    pub decodes_full_address: bool,
+    pub memory_range: (u16, u16),
+    pub base_alignment: u8,
+    pub range_length: u8,
+}
+
+fn io_port_descriptor(bytes: &[u8]) -> Result<Resource, AmlError> {
+    /*
+     * I/O Port Descriptor Definition
+     * Offset   Field Name                                  Definition
+     * Byte 0   I/O Port Descriptor                         Value = 0x47 (01000111B) –
+     *                                                      Type = 0, Small item name = 0x8, Length = 7
+     * Byte 1   Information                                 Bits [7:1]     Reserved and must be 0
+     *                                                      Bit [0]          (_DEC)
+     *                                                        1    The logical device decodes 16-bit addresses
+     *                                                        0    The logical device only decodes address bits[9:0]
+     * Byte 2   Range minimum base address, _MIN bits[7:0]  Address bits [7:0] of the minimum base I/O address that the card may be configured for.
+     * Byte 3   Range minimum base address, _MIN bits[15:8] Address bits [15:8] of the minimum base I/O address that the card may be configured for.
+     * Byte 4   Range maximum base address, _MAX bits[7:0]  Address bits [7:0] of the maximum base I/O address that the card may be configured for.
+     * Byte 5   Range maximum base address, _MAX bits[15:8] Address bits [15:8] of the maximum base I/O address that the card may be configured for.
+     * Byte 6   Base alignment, _ALN                        Alignment for minimum base address, increment in 1-byte blocks.
+     * Byte 7   Range length, _LEN                          The number of contiguous I/O ports requested.
+     */
+    if bytes.len() != 8 {
+        return Err(AmlError::InvalidResourceDescriptor);
+    }
+
+    let information = bytes[1];
+    let decodes_full_address = information.get_bit(0);
+
+    let memory_range_min = LittleEndian::read_u16(&bytes[2..=3]);
+    let memory_range_max = LittleEndian::read_u16(&bytes[4..=5]);
+    let memory_range = (memory_range_min, memory_range_max);
+
+    let base_alignment = bytes[6];
+    let range_length = bytes[7];
+
+    Ok(Resource::IOPort(IOPortDescriptor { decodes_full_address, memory_range, base_alignment, range_length }))
+}
+
+fn extended_interrupt_descriptor(bytes: &[u8]) -> Result<Resource, AmlError> {
+    /*
+     * --- Extended Interrupt Descriptor ---
+     * Byte 3 contains the Interrupt Vector Flags:
+     *      Bit 0: 1 if device consumes the resource, 0 if it produces it
+     *      Bit 1: 1 if edge-triggered, 0 if level-triggered
+     *      Bit 2: 1 = active-high, 0 = active-low
+     *      Bit 3: 1 if interrupt is shared with other devices
+     *      Bit 4: 1 if this interrupt is capable of waking the system, 0 if it is not
+     * Byte 4 contains the number of interrupt numbers that follow. When this descriptor is
+     * returned from `_CRS` or send to `_SRS`, this field must be 1.
+     *
+     * From Byte 5 onwards, there are `n` interrupt numbers, each of which is encoded as a
+     * 4-byte little-endian number.
+     *
+     * NOTE: We only support the case where there is a single interrupt number.
+     */
+    if bytes.len() < 9 {
+        return Err(AmlError::InvalidResourceDescriptor);
+    }
+
+    let number_of_interrupts = bytes[4] as usize;
+    assert_eq!(number_of_interrupts, 1);
+    let irq = LittleEndian::read_u32(&[bytes[5], bytes[6], bytes[7], bytes[8]]);
+
+    Ok(Resource::Irq(IrqDescriptor {
+        is_consumer: bytes[3].get_bit(0),
+        trigger: if bytes[3].get_bit(1) { InterruptTrigger::Edge } else { InterruptTrigger::Level },
+        polarity: if bytes[3].get_bit(2) { InterruptPolarity::ActiveLow } else { InterruptPolarity::ActiveHigh },
+        is_shared: bytes[3].get_bit(3),
+        is_wake_capable: bytes[3].get_bit(4),
+        irq,
+    }))
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+    use alloc::sync::Arc;
+
+    #[test]
+    fn test_parses_keyboard_crs() {
+        let bytes: Vec<u8> = [
+            // Generated from `iasl -l pc-bios_acpi-dsdt.asl`
+            //
+            //         315:                   IO (Decode16,
+            //         316:                       0x0060,             // Range Minimum
+            //         317:                       0x0060,             // Range Maximum
+            //         318:                       0x01,               // Alignment
+            //         319:                       0x01,               // Length
+            //         320:                       )
+
+            //    0000040A:  47 01 60 00 60 00 01 01     "G.`.`..."
+            0x47, 0x01, 0x60, 0x00, 0x60, 0x00, 0x01, 0x01,
+            //         321:                   IO (Decode16,
+            //         322:                       0x0064,             // Range Minimum
+            //         323:                       0x0064,             // Range Maximum
+            //         324:                       0x01,               // Alignment
+            //         325:                       0x01,               // Length
+            //         326:                       )
+
+            //    00000412:  47 01 64 00 64 00 01 01     "G.d.d..."
+            0x47, 0x01, 0x64, 0x00, 0x64, 0x00, 0x01, 0x01,
+            //         327:                   IRQNoFlags ()
+            //         328:                       {1}
+
+            //    0000041A:  22 02 00 ...............    "".."
+            0x22, 0x02, 0x00, //    0000041D:  79 00 ..................    "y."
+            0x79, 0x00,
+        ]
+        .to_vec();
+
+        let value = Arc::new(Object::Buffer(bytes));
+        let resources = resource_descriptor_list(value).unwrap();
+
+        assert_eq!(
+            resources,
+            Vec::from([
+                Resource::IOPort(IOPortDescriptor {
+                    decodes_full_address: true,
+                    memory_range: (0x60, 0x60),
+                    base_alignment: 1,
+                    range_length: 1
+                }),
+                Resource::IOPort(IOPortDescriptor {
+                    decodes_full_address: true,
+                    memory_range: (0x64, 0x64),
+                    base_alignment: 1,
+                    range_length: 1
+                }),
+                Resource::Irq(IrqDescriptor {
+                    is_consumer: false,
+                    trigger: InterruptTrigger::Edge,
+                    polarity: InterruptPolarity::ActiveHigh,
+                    is_shared: false,
+                    is_wake_capable: false,
+                    irq: (1 << 1)
+                })
+            ])
+        );
+    }
+
+    #[test]
+    fn test_pci_crs() {
+        let bytes: Vec<u8> = [
+            // Generated from `iasl -l pc-bios_acpi-dsdt.asl`
+            //
+            //      98:               WordBusNumber (ResourceProducer, MinFixed, MaxFixed, PosDecode,
+            //      99:                   0x0000,             // Granularity
+            //     100:                   0x0000,             // Range Minimum
+            //     101:                   0x00FF,             // Range Maximum
+            //     102:                   0x0000,             // Translation Offset
+            //     103:                   0x0100,             // Length
+            //     104:                   ,, )
+
+            // 000000F3:  88 0D 00 02 0C 00 00 00     "........"
+            // 000000FB:  00 00 FF 00 00 00 00 01     "........"
+            0x88, 0x0D, 0x00, 0x02, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x01,
+            //     105:               IO (Decode16,
+            //     106:                   0x0CF8,             // Range Minimum
+            //     107:                   0x0CF8,             // Range Maximum
+            //     108:                   0x01,               // Alignment
+            //     109:                   0x08,               // Length
+            //     110:                   )
+
+            // 00000103:  47 01 F8 0C F8 0C 01 08     "G......."
+            0x47, 0x01, 0xF8, 0x0C, 0xF8, 0x0C, 0x01, 0x08,
+            //     111:               WordIO (ResourceProducer, MinFixed, MaxFixed, PosDecode, EntireRange,
+            //     112:                   0x0000,             // Granularity
+            //     113:                   0x0000,             // Range Minimum
+            //     114:                   0x0CF7,             // Range Maximum
+            //     115:                   0x0000,             // Translation Offset
+            //     116:                   0x0CF8,             // Length
+            //     117:                   ,, , TypeStatic, DenseTranslation)
+
+            // 0000010B:  88 0D 00 01 0C 03 00 00     "........"
+            // 00000113:  00 00 F7 0C 00 00 F8 0C     "........"
+            0x88, 0x0D, 0x00, 0x01, 0x0C, 0x03, 0x00, 0x00, 0x00, 0x00, 0xF7, 0x0C, 0x00, 0x00, 0xF8, 0x0C,
+            //     118:               WordIO (ResourceProducer, MinFixed, MaxFixed, PosDecode, EntireRange,
+            //     119:                   0x0000,             // Granularity
+            //     120:                   0x0D00,             // Range Minimum
+            //     121:                   0xFFFF,             // Range Maximum
+            //     122:                   0x0000,             // Translation Offset
+            //     123:                   0xF300,             // Length
+            //     124:                   ,, , TypeStatic, DenseTranslation)
+
+            // 0000011B:  88 0D 00 01 0C 03 00 00     "........"
+            // 00000123:  00 0D FF FF 00 00 00 F3     "........"
+            0x88, 0x0D, 0x00, 0x01, 0x0C, 0x03, 0x00, 0x00, 0x00, 0x0D, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0xF3,
+            //     125:               DWordMemory (ResourceProducer, PosDecode, MinFixed, MaxFixed, Cacheable, ReadWrite,
+            //     126:                   0x00000000,         // Granularity
+            //     127:                   0x000A0000,         // Range Minimum
+            //     128:                   0x000BFFFF,         // Range Maximum
+            //     129:                   0x00000000,         // Translation Offset
+            //     130:                   0x00020000,         // Length
+            //     131:                   ,, , AddressRangeMemory, TypeStatic)
+
+            // 0000012B:  87 17 00 00 0C 03 00 00     "........"
+            // 00000133:  00 00 00 00 0A 00 FF FF     "........"
+            // 0000013B:  0B 00 00 00 00 00 00 00     "........"
+            // 00000143:  02 00 ..................    ".."
+            0x87, 0x17, 0x00, 0x00, 0x0C, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0A, 0x00, 0xFF, 0xFF, 0x0B,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00,
+            //     132:               DWordMemory (ResourceProducer, PosDecode, MinFixed, MaxFixed, NonCacheable, ReadWrite,
+            //     133:                   0x00000000,         // Granularity
+            //     134:                   0xE0000000,         // Range Minimum
+            //     135:                   0xFEBFFFFF,         // Range Maximum
+            //     136:                   0x00000000,         // Translation Offset
+            //     137:                   0x1EC00000,         // Length
+            //     138:                   ,, _Y00, AddressRangeMemory, TypeStatic)
+
+            // 00000145:  87 17 00 00 0C 01 00 00     "........"
+            // 0000014D:  00 00 00 00 00 E0 FF FF     "........"
+            // 00000155:  BF FE 00 00 00 00 00 00     "........"
+            // 0000015D:  C0 1E ..................    ".."
+            0x87, 0x17, 0x00, 0x00, 0x0C, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0xFF, 0xFF, 0xBF,
+            0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x1E,
+            // 0000015F:  79 00 ..................    "y."
+            0x79, 0x00,
+        ]
+        .to_vec();
+
+        let value = Arc::new(Object::Buffer(bytes));
+        let resources = resource_descriptor_list(value).unwrap();
+
+        assert_eq!(
+            resources,
+            Vec::from([
+                Resource::AddressSpace(AddressSpaceDescriptor {
+                    resource_type: AddressSpaceResourceType::BusNumberRange,
+                    is_maximum_address_fixed: true,
+                    is_minimum_address_fixed: true,
+                    decode_type: AddressSpaceDecodeType::Additive,
+                    granularity: 0,
+                    address_range: (0x00, 0xFF),
+                    translation_offset: 0,
+                    length: 0x100
+                }),
+                Resource::IOPort(IOPortDescriptor {
+                    decodes_full_address: true,
+                    memory_range: (0xCF8, 0xCF8),
+                    base_alignment: 1,
+                    range_length: 8
+                }),
+                Resource::AddressSpace(AddressSpaceDescriptor {
+                    resource_type: AddressSpaceResourceType::IORange,
+                    is_maximum_address_fixed: true,
+                    is_minimum_address_fixed: true,
+                    decode_type: AddressSpaceDecodeType::Additive,
+                    granularity: 0,
+                    address_range: (0x0000, 0x0CF7),
+                    translation_offset: 0,
+                    length: 0xCF8
+                }),
+                Resource::AddressSpace(AddressSpaceDescriptor {
+                    resource_type: AddressSpaceResourceType::IORange,
+                    is_maximum_address_fixed: true,
+                    is_minimum_address_fixed: true,
+                    decode_type: AddressSpaceDecodeType::Additive,
+                    granularity: 0,
+                    address_range: (0x0D00, 0xFFFF),
+                    translation_offset: 0,
+                    length: 0xF300
+                }),
+                Resource::AddressSpace(AddressSpaceDescriptor {
+                    resource_type: AddressSpaceResourceType::MemoryRange,
+                    is_maximum_address_fixed: true,
+                    is_minimum_address_fixed: true,
+                    decode_type: AddressSpaceDecodeType::Additive,
+                    granularity: 0,
+                    address_range: (0xA0000, 0xBFFFF),
+                    translation_offset: 0,
+                    length: 0x20000
+                }),
+                Resource::AddressSpace(AddressSpaceDescriptor {
+                    resource_type: AddressSpaceResourceType::MemoryRange,
+                    is_maximum_address_fixed: true,
+                    is_minimum_address_fixed: true,
+                    decode_type: AddressSpaceDecodeType::Additive,
+                    granularity: 0,
+                    address_range: (0xE0000000, 0xFEBFFFFF),
+                    translation_offset: 0,
+                    length: 0x1EC00000
+                }),
+            ])
+        );
+    }
+
+    #[test]
+    fn test_fdc_crs() {
+        let bytes: Vec<u8> = [
+            //         365:                   IO (Decode16,
+            //         366:                       0x03F2,             // Range Minimum
+            //         367:                       0x03F2,             // Range Maximum
+            //         368:                       0x00,               // Alignment
+            //         369:                       0x04,               // Length
+            //         370:                       )
+
+            //    0000047C:  47 01 F2 03 F2 03 00 04     "G......."
+            0x47, 0x01, 0xF2, 0x03, 0xF2, 0x03, 0x00, 0x04,
+            //         371:                   IO (Decode16,
+            //         372:                       0x03F7,             // Range Minimum
+            //         373:                       0x03F7,             // Range Maximum
+            //         374:                       0x00,               // Alignment
+            //         375:                       0x01,               // Length
+            //         376:                       )
+
+            //    00000484:  47 01 F7 03 F7 03 00 01     "G......."
+            0x47, 0x01, 0xF7, 0x03, 0xF7, 0x03, 0x00, 0x01,
+            //         377:                   IRQNoFlags ()
+            //         378:                       {6}
+
+            //    0000048C:  22 40 00 ...............    ""@."
+            0x22, 0x40, 0x00,
+            //         379:                   DMA (Compatibility, NotBusMaster, Transfer8, )
+            //         380:                       {2}
+
+            //    0000048F:  2A 04 00 ...............    "*.."
+            0x2A, 0x04, 0x00, //    00000492:  79 00 ..................    "y."
+            0x79, 0x00,
+        ]
+        .to_vec();
+
+        let value = Arc::new(Object::Buffer(bytes));
+        let resources = resource_descriptor_list(value).unwrap();
+
+        assert_eq!(
+            resources,
+            Vec::from([
+                Resource::IOPort(IOPortDescriptor {
+                    decodes_full_address: true,
+                    memory_range: (0x03F2, 0x03F2),
+                    base_alignment: 0,
+                    range_length: 4
+                }),
+                Resource::IOPort(IOPortDescriptor {
+                    decodes_full_address: true,
+                    memory_range: (0x03F7, 0x03F7),
+                    base_alignment: 0,
+                    range_length: 1
+                }),
+                Resource::Irq(IrqDescriptor {
+                    is_consumer: false,
+                    trigger: InterruptTrigger::Edge,
+                    polarity: InterruptPolarity::ActiveHigh,
+                    is_shared: false,
+                    is_wake_capable: false,
+                    irq: (1 << 6)
+                }),
+                Resource::Dma(DMADescriptor {
+                    channel_mask: 1 << 2,
+                    supported_speeds: DMASupportedSpeed::CompatibilityMode,
+                    is_bus_master: false,
+                    transfer_type_preference: DMATransferTypePreference::_8BitOnly
+                })
+            ])
+        );
+    }
+}

From b8cb18fbb1b08579512f323592dc6accd2d81eb5 Mon Sep 17 00:00:00 2001
From: Isaac Woods <isaacwoods.home@gmail.com>
Date: Tue, 18 Feb 2025 00:39:06 +0000
Subject: [PATCH 42/88] `aml`: implement `DefSizeOf`

---
 aml/src/lib.rs | 18 +++++++++++++++++-
 1 file changed, 17 insertions(+), 1 deletion(-)

diff --git a/aml/src/lib.rs b/aml/src/lib.rs
index 02e90d14..5470e092 100644
--- a/aml/src/lib.rs
+++ b/aml/src/lib.rs
@@ -368,6 +368,7 @@ impl Interpreter {
 
                         context.contribute_arg(Argument::Object(Arc::new(Object::Integer(typ))));
                     }
+                    Opcode::SizeOf => self.do_size_of(&mut context, op)?,
                     _ => panic!("Unexpected operation has created in-flight op!"),
                 }
             }
@@ -782,7 +783,7 @@ impl Interpreter {
                 Opcode::DerefOf => todo!(),
                 Opcode::ConcatRes => todo!(),
                 Opcode::Notify => todo!(),
-                Opcode::SizeOf => todo!(),
+                Opcode::SizeOf => context.start_in_flight_op(OpInFlight::new(opcode, 1)),
                 Opcode::Index => todo!(),
                 Opcode::Match => todo!(),
 
@@ -957,6 +958,21 @@ impl Interpreter {
         context.contribute_arg(Argument::Object(Arc::new(result)));
         Ok(())
     }
+
+    fn do_size_of(&self, context: &mut MethodContext, op: OpInFlight) -> Result<(), AmlError> {
+        let [Argument::Object(object)] = &op.arguments[..] else { Err(AmlError::InvalidOperationOnObject)? };
+        let object = object.clone().unwrap_reference();
+
+        let result = match *object {
+            Object::Buffer(ref buffer) => buffer.len(),
+            Object::String(ref str) => str.len(),
+            Object::Package(ref package) => package.len(),
+            _ => Err(AmlError::InvalidOperationOnObject)?,
+        };
+
+        context.contribute_arg(Argument::Object(Arc::new(Object::Integer(result as u64))));
+        Ok(())
+    }
     fn do_store(
         &self,
         context: &mut MethodContext,

From 41edbacc0d7369cfc3371a1caf4b6423c034a726 Mon Sep 17 00:00:00 2001
From: Isaac Woods <isaacwoods.home@gmail.com>
Date: Tue, 18 Feb 2025 00:40:30 +0000
Subject: [PATCH 43/88] `aml`: monomorphise over `Handler` type

---
 aml/src/lib.rs         | 24 +++++++++++++-----------
 aml/src/pci_routing.rs |  8 ++++++--
 2 files changed, 19 insertions(+), 13 deletions(-)

diff --git a/aml/src/lib.rs b/aml/src/lib.rs
index 5470e092..07e5ebbf 100644
--- a/aml/src/lib.rs
+++ b/aml/src/lib.rs
@@ -11,7 +11,6 @@ pub mod pci_routing;
 pub mod resource;
 
 use alloc::{
-    boxed::Box,
     string::{String, ToString},
     sync::Arc,
     vec,
@@ -24,23 +23,26 @@ use object::{MethodFlags, Object, ObjectType, ReferenceKind};
 use op_region::{OpRegion, RegionSpace};
 use spinning_top::Spinlock;
 
-pub struct Interpreter {
-    handler: Box<dyn Handler>,
+pub struct Interpreter<H>
+where
+    H: Handler,
+{
+    handler: H,
     pub namespace: Spinlock<Namespace>,
     context_stack: Spinlock<Vec<MethodContext>>,
 }
 
-unsafe impl Send for Interpreter {}
-unsafe impl Sync for Interpreter {}
+unsafe impl<H> Send for Interpreter<H> where H: Handler + Send {}
+unsafe impl<H> Sync for Interpreter<H> where H: Handler + Send {}
 
-impl Interpreter {
-    pub fn new<H>(handler: H) -> Interpreter
-    where
-        H: Handler + 'static,
-    {
+impl<H> Interpreter<H>
+where
+    H: Handler,
+{
+    pub fn new(handler: H) -> Interpreter<H> {
         Interpreter {
+            handler,
             namespace: Spinlock::new(Namespace::new()),
-            handler: Box::new(handler),
             context_stack: Spinlock::new(Vec::new()),
         }
     }
diff --git a/aml/src/pci_routing.rs b/aml/src/pci_routing.rs
index b65a3b84..2302a9dd 100644
--- a/aml/src/pci_routing.rs
+++ b/aml/src/pci_routing.rs
@@ -1,5 +1,6 @@
 use crate::{
     AmlError,
+    Handler,
     Interpreter,
     namespace::AmlName,
     object::{Object, ReferenceKind},
@@ -56,7 +57,10 @@ impl PciRoutingTable {
     /// `AmlError::InvalidOperationOnObject` if the value passed is not a package, or if any of the
     /// values within it are not packages. Returns the various `AmlError::Prt*` errors if the
     /// internal structure of the entries is invalid.
-    pub fn from_prt_path(prt_path: AmlName, interpreter: &Interpreter) -> Result<PciRoutingTable, AmlError> {
+    pub fn from_prt_path(
+        prt_path: AmlName,
+        interpreter: &Interpreter<impl Handler>,
+    ) -> Result<PciRoutingTable, AmlError> {
         let mut entries = Vec::new();
 
         let prt = interpreter.invoke_method(prt_path.clone(), vec![])?;
@@ -149,7 +153,7 @@ impl PciRoutingTable {
         device: u16,
         function: u16,
         pin: Pin,
-        interpreter: &Interpreter,
+        interpreter: &Interpreter<impl Handler>,
     ) -> Result<IrqDescriptor, AmlError> {
         let entry = self
             .entries

From 62330d0e222bd42f21ca45f0dd91383498304676 Mon Sep 17 00:00:00 2001
From: Isaac Woods <isaacwoods.home@gmail.com>
Date: Wed, 19 Feb 2025 21:32:43 +0000
Subject: [PATCH 44/88] `aml`: implement `DefTimer`, `DefFatal`, `Debug`, and
 `Revision`

---
 aml/src/lib.rs               | 55 +++++++++++++++++++++++++++++++-----
 aml/src/object.rs            |  3 ++
 tools/aml_tester/src/main.rs |  4 +++
 3 files changed, 55 insertions(+), 7 deletions(-)

diff --git a/aml/src/lib.rs b/aml/src/lib.rs
index 07e5ebbf..0544b81e 100644
--- a/aml/src/lib.rs
+++ b/aml/src/lib.rs
@@ -35,6 +35,9 @@ where
 unsafe impl<H> Send for Interpreter<H> where H: Handler + Send {}
 unsafe impl<H> Sync for Interpreter<H> where H: Handler + Send {}
 
+/// The value returned by the `Revision` opcode.
+const INTERPRETER_REVISION: u64 = 1;
+
 impl<H> Interpreter<H>
 where
     H: Handler,
@@ -153,6 +156,15 @@ where
                         let name = name.resolve(&context.current_scope)?;
                         self.namespace.lock().insert(name, object.clone())?;
                     }
+                    Opcode::Fatal => {
+                        let [Argument::ByteData(typ), Argument::DWordData(code), Argument::Object(arg)] =
+                            &op.arguments[..]
+                        else {
+                            panic!()
+                        };
+                        let Object::Integer(arg) = **arg else { panic!() };
+                        self.handler.handle_fatal_error(*typ, *code, arg);
+                    }
                     Opcode::OpRegion => {
                         let [
                             Argument::Namestring(name),
@@ -346,7 +358,6 @@ where
                         let [Argument::Object(object)] = &op.arguments[..] else { panic!() };
                         // TODO: this should technically support scopes as well - this is less easy
                         // (they should return `0`)
-                        // TODO: calling this on the debug object should should return `16`
                         let typ = match object.typ() {
                             ObjectType::Uninitialized => 0,
                             ObjectType::Integer => 1,
@@ -364,6 +375,7 @@ where
                             ObjectType::ThermalZone => 13,
                             ObjectType::BufferField => 14,
                             // XXX: 15 is reserved
+                            ObjectType::Debug => 16,
                             ObjectType::Reference => panic!(),
                             ObjectType::RawDataBuffer => todo!(),
                         };
@@ -583,10 +595,26 @@ where
                 Opcode::Release => todo!(),
                 Opcode::FromBCD => todo!(),
                 Opcode::ToBCD => todo!(),
-                Opcode::Revision => todo!(),
-                Opcode::Debug => todo!(),
-                Opcode::Fatal => todo!(),
-                Opcode::Timer => todo!(),
+                Opcode::Revision => {
+                    context.contribute_arg(Argument::Object(Arc::new(Object::Integer(INTERPRETER_REVISION))));
+                }
+                Opcode::Debug => {
+                    context.contribute_arg(Argument::Object(Arc::new(Object::Debug)));
+                }
+                Opcode::Fatal => {
+                    let typ = context.next()?;
+                    let code = context.next_u32()?;
+                    context.start_in_flight_op(OpInFlight::new_with(
+                        Opcode::Fatal,
+                        vec![Argument::ByteData(typ), Argument::DWordData(code)],
+                        1,
+                    ));
+                }
+                Opcode::Timer => {
+                    // Time has to be monotonically-increasing, in 100ns units
+                    let time = self.handler.nanos_since_boot() / 100;
+                    context.contribute_arg(Argument::Object(Arc::new(Object::Integer(time))));
+                }
                 Opcode::OpRegion => {
                     let name = context.namestring()?;
                     let region_space = context.next()?;
@@ -1009,10 +1037,16 @@ where
                     }
                     _ => panic!(),
                 },
+                Object::Debug => {
+                    self.handler.handle_debug(&*object);
+                }
                 _ => panic!("Stores to objects like {:?} are not yet supported", target),
             },
+
             Argument::Namestring(_) => {}
-            Argument::ByteData(_) | Argument::TrackedPc(_) | Argument::PkgLength(_) => panic!(),
+            Argument::ByteData(_) | Argument::DWordData(_) | Argument::TrackedPc(_) | Argument::PkgLength(_) => {
+                panic!()
+            }
         }
         Ok(())
     }
@@ -1050,6 +1084,7 @@ enum Argument {
     Object(Arc<Object>),
     Namestring(AmlName),
     ByteData(u8),
+    DWordData(u32),
     TrackedPc(usize),
     PkgLength(usize),
 }
@@ -1608,6 +1643,9 @@ pub trait Handler: Send + Sync {
     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);
 
+    /// Returns a monotonically-increasing value of nanoseconds.
+    fn nanos_since_boot(&self) -> u64;
+
     /// Stall for at least the given number of **microseconds**. An implementation should not relinquish control of
     /// the processor during the stall, and for this reason, firmwares should not stall for periods of more than
     /// 100 microseconds.
@@ -1619,9 +1657,11 @@ pub trait Handler: Send + Sync {
 
     fn breakpoint(&self) {}
 
+    fn handle_debug(&self, _object: &Object) {}
+
     fn handle_fatal_error(&self, fatal_type: u8, fatal_code: u32, fatal_arg: u64) {
         panic!(
-            "Fatal error while executing AML (encountered DefFatalOp). fatal_type = {:?}, fatal_code = {:?}, fatal_arg = {:?}",
+            "Fatal error while executing AML (encountered DefFatalOp). fatal_type = {}, fatal_code = {}, fatal_arg = {}",
             fatal_type, fatal_code, fatal_arg
         );
     }
@@ -1655,6 +1695,7 @@ mod tests {
         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) {}
+        fn nanos_since_boot(&self) -> u64 {0}
         fn stall(&self, _microseconds: u64) {}
         fn sleep(&self, _milliseconds: u64) {}
     }
diff --git a/aml/src/object.rs b/aml/src/object.rs
index 08404009..6948beb5 100644
--- a/aml/src/object.rs
+++ b/aml/src/object.rs
@@ -21,6 +21,7 @@ pub enum Object {
     RawDataBuffer,
     String(String),
     ThermalZone,
+    Debug,
 }
 
 impl Object {
@@ -85,6 +86,7 @@ impl Object {
             Object::RawDataBuffer => ObjectType::RawDataBuffer,
             Object::String(_) => ObjectType::String,
             Object::ThermalZone => ObjectType::ThermalZone,
+            Object::Debug => ObjectType::Debug,
         }
     }
 
@@ -167,6 +169,7 @@ pub enum ObjectType {
     RawDataBuffer,
     String,
     ThermalZone,
+    Debug,
 }
 
 /// Copy an arbitrary bit range of `src` to an arbitrary bit range of `dst`. This is used for
diff --git a/tools/aml_tester/src/main.rs b/tools/aml_tester/src/main.rs
index 59251e16..221c3c7d 100644
--- a/tools/aml_tester/src/main.rs
+++ b/tools/aml_tester/src/main.rs
@@ -387,6 +387,10 @@ impl aml::Handler for Handler {
         println!("write_pci_u32 ({segment:#x}, {bus:#x}, {device:#x}, {function:#x})<-{value:#x}");
     }
 
+    fn nanos_since_boot(&self) -> u64 {
+        0
+    }
+
     fn stall(&self, microseconds: u64) {
         println!("Stalling for {}us", microseconds);
     }

From 86b47ebab1268f751238a908a2456a9ac4ecb86a Mon Sep 17 00:00:00 2001
From: Isaac Woods <isaacwoods.home@gmail.com>
Date: Wed, 19 Feb 2025 21:34:00 +0000
Subject: [PATCH 45/88] `aml`: implement `DefToBCD` and `DefFromBCD`

---
 aml/src/lib.rs | 38 +++++++++++++++++++++++++++++++++++---
 1 file changed, 35 insertions(+), 3 deletions(-)

diff --git a/aml/src/lib.rs b/aml/src/lib.rs
index 0544b81e..bc942f80 100644
--- a/aml/src/lib.rs
+++ b/aml/src/lib.rs
@@ -1,7 +1,6 @@
 #![no_std]
 #![feature(let_chains, inherent_str_constructors)]
 
-
 extern crate alloc;
 
 pub mod namespace;
@@ -148,6 +147,8 @@ where
                     | Opcode::LLess => {
                         self.do_logical_op(&mut context, op)?;
                     }
+                    Opcode::FromBCD => self.do_from_bcd(&mut context, op)?,
+                    Opcode::ToBCD => self.do_to_bcd(&mut context, op)?,
                     Opcode::Name => {
                         let [Argument::Namestring(name), Argument::Object(object)] = &op.arguments[..] else {
                             panic!()
@@ -593,8 +594,7 @@ where
                 Opcode::Wait => todo!(),
                 Opcode::Reset => todo!(),
                 Opcode::Release => todo!(),
-                Opcode::FromBCD => todo!(),
-                Opcode::ToBCD => todo!(),
+                Opcode::FromBCD | Opcode::ToBCD => context.start_in_flight_op(OpInFlight::new(opcode, 2)),
                 Opcode::Revision => {
                     context.contribute_arg(Argument::Object(Arc::new(Object::Integer(INTERPRETER_REVISION))));
                 }
@@ -989,6 +989,38 @@ where
         Ok(())
     }
 
+    fn do_from_bcd(&self, context: &mut MethodContext, op: OpInFlight) -> Result<(), AmlError> {
+        let [Argument::Object(value)] = &op.arguments[..] else { Err(AmlError::InvalidOperationOnObject)? };
+        let Object::Integer(mut value) = **value else { Err(AmlError::InvalidOperationOnObject)? };
+
+        let mut result = 0;
+        let mut i = 1;
+        while value > 0 {
+            result += (value & 0x0f) * i;
+            i *= 10;
+            value >>= 4;
+        }
+
+        context.contribute_arg(Argument::Object(Arc::new(Object::Integer(result))));
+        Ok(())
+    }
+
+    fn do_to_bcd(&self, context: &mut MethodContext, op: OpInFlight) -> Result<(), AmlError> {
+        let [Argument::Object(value)] = &op.arguments[..] else { Err(AmlError::InvalidOperationOnObject)? };
+        let Object::Integer(mut value) = **value else { Err(AmlError::InvalidOperationOnObject)? };
+
+        let mut result = 0;
+        let mut i = 0;
+        while value > 0 {
+            result |= (value % 10) << (4 * i);
+            value /= 10;
+            i += 1;
+        }
+
+        context.contribute_arg(Argument::Object(Arc::new(Object::Integer(result))));
+        Ok(())
+    }
+
     fn do_size_of(&self, context: &mut MethodContext, op: OpInFlight) -> Result<(), AmlError> {
         let [Argument::Object(object)] = &op.arguments[..] else { Err(AmlError::InvalidOperationOnObject)? };
         let object = object.clone().unwrap_reference();

From fe877bd5f473c98e394150eed5ad982babfface2 Mon Sep 17 00:00:00 2001
From: Isaac Woods <isaacwoods.home@gmail.com>
Date: Thu, 20 Feb 2025 01:17:53 +0000
Subject: [PATCH 46/88] `aml`: support parsing of bank and index fields

---
 aml/src/lib.rs    | 167 ++++++++++++++++++++++++++++++++--------------
 aml/src/object.rs |  19 ++++--
 tests/fields.asl  |  34 ++++++++++
 3 files changed, 167 insertions(+), 53 deletions(-)
 create mode 100644 tests/fields.asl

diff --git a/aml/src/lib.rs b/aml/src/lib.rs
index bc942f80..6dde540e 100644
--- a/aml/src/lib.rs
+++ b/aml/src/lib.rs
@@ -18,7 +18,7 @@ use alloc::{
 use bit_field::BitField;
 use core::mem;
 use namespace::{AmlName, Namespace, NamespaceLevelKind};
-use object::{MethodFlags, Object, ObjectType, ReferenceKind};
+use object::{FieldFlags, FieldUnit, FieldUnitKind, MethodFlags, Object, ObjectType, ReferenceKind};
 use op_region::{OpRegion, RegionSpace};
 use spinning_top::Spinlock;
 
@@ -384,6 +384,31 @@ where
                         context.contribute_arg(Argument::Object(Arc::new(Object::Integer(typ))));
                     }
                     Opcode::SizeOf => self.do_size_of(&mut context, op)?,
+                    Opcode::BankField => {
+                        let [
+                            Argument::TrackedPc(start_pc),
+                            Argument::PkgLength(pkg_length),
+                            Argument::Namestring(region_name),
+                            Argument::Namestring(bank_name),
+                            Argument::Object(bank_value),
+                        ] = &op.arguments[..]
+                        else {
+                            panic!()
+                        };
+                        let Object::Integer(bank_value) = **bank_value else { panic!() };
+
+                        let field_flags = context.next()?;
+
+                        let (region, bank) = {
+                            let namespace = self.namespace.lock();
+                            let (_, region) = namespace.search(region_name, &context.current_scope)?;
+                            let (_, bank) = namespace.search(bank_name, &context.current_scope)?;
+                            (region, bank)
+                        };
+
+                        let kind = FieldUnitKind::Bank { region, bank, bank_value };
+                        self.parse_field_list(&mut context, kind, *start_pc, *pkg_length, field_flags)?;
+                    }
                     _ => panic!("Unexpected operation has created in-flight op!"),
                 }
             }
@@ -628,55 +653,44 @@ where
                     let start_pc = context.current_block.pc;
                     let pkg_length = context.pkglength()?;
                     let region_name = context.namestring()?;
-                    let flags = context.next()?;
+                    let field_flags = context.next()?;
 
                     let region = self.namespace.lock().get(region_name.resolve(&context.current_scope)?)?.clone();
+                    let kind = FieldUnitKind::Normal { region };
+                    self.parse_field_list(&mut context, kind, start_pc, pkg_length, field_flags)?;
+                }
+                Opcode::BankField => {
+                    let start_pc = context.current_block.pc;
+                    let pkg_length = context.pkglength()?;
+                    let region_name = context.namestring()?;
+                    let bank_name = context.namestring()?;
 
-                    const RESERVED_FIELD: u8 = 0x00;
-                    const ACCESS_FIELD: u8 = 0x01;
-                    const CONNECT_FIELD: u8 = 0x02;
-                    const EXTENDED_ACCESS_FIELD: u8 = 0x03;
-
-                    let mut field_offset = 0;
-
-                    while context.current_block.pc < (start_pc + pkg_length) {
-                        match context.next()? {
-                            RESERVED_FIELD => {
-                                let length = context.pkglength()?;
-                                field_offset += length;
-                            }
-                            ACCESS_FIELD => {
-                                let access_type = context.next()?;
-                                let access_attrib = context.next()?;
-                                todo!()
-                                // TODO
-                            }
-                            CONNECT_FIELD => {
-                                // TODO: either consume a namestring or `BufferData` (it's not
-                                // clear what a buffer data acc is lmao)
-                                todo!("Connect field :(");
-                            }
-                            EXTENDED_ACCESS_FIELD => {
-                                todo!("Extended access field :(");
-                            }
-                            _ => {
-                                context.current_block.pc -= 1;
-                                // TODO: this should only ever be a nameseg really...
-                                let field_name = context.namestring()?;
-                                let field_length = context.pkglength()?;
-                                // TODO: do something with the flags, and also detect when this
-                                // should be a bank or index field
-                                let field = Object::FieldUnit(object::FieldUnit::Normal {
-                                    region: region.clone(),
-                                    bit_index: field_offset,
-                                    bit_length: field_length,
-                                });
-                                self.namespace
-                                    .lock()
-                                    .insert(field_name.resolve(&context.current_scope)?, Arc::new(field))?;
-                            }
-                        }
-                    }
+                    context.start_in_flight_op(OpInFlight::new_with(
+                        Opcode::BankField,
+                        vec![
+                            Argument::TrackedPc(start_pc),
+                            Argument::PkgLength(pkg_length),
+                            Argument::Namestring(region_name),
+                            Argument::Namestring(bank_name),
+                        ],
+                        1,
+                    ));
+                }
+                Opcode::IndexField => {
+                    let start_pc = context.current_block.pc;
+                    let pkg_length = context.pkglength()?;
+                    let index_name = context.namestring()?;
+                    let data_name = context.namestring()?;
+                    let field_flags = context.next()?;
+
+                    let (index, data) = {
+                        let namespace = self.namespace.lock();
+                        let (_, index) = namespace.search(&index_name, &context.current_scope)?;
+                        let (_, data) = namespace.search(&data_name, &context.current_scope)?;
+                        (index, data)
+                    };
+                    let kind = FieldUnitKind::Index { index, data };
+                    self.parse_field_list(&mut context, kind, start_pc, pkg_length, field_flags)?;
                 }
                 Opcode::Device | Opcode::ThermalZone => {
                     let start_pc = context.current_block.pc;
@@ -735,8 +749,6 @@ where
                     let old_scope = mem::replace(&mut context.current_scope, new_scope);
                     context.start_new_block(BlockKind::Scope { old_scope }, remaining_length);
                 }
-                Opcode::IndexField => todo!(),
-                Opcode::BankField => todo!(),
                 Opcode::DataRegion => todo!(),
                 Opcode::Local(local) => {
                     let local = context.locals[local as usize].clone();
@@ -872,6 +884,63 @@ where
         }
     }
 
+    fn parse_field_list(
+        &self,
+        context: &mut MethodContext,
+        kind: FieldUnitKind,
+        start_pc: usize,
+        pkg_length: usize,
+        flags: u8,
+    ) -> Result<(), AmlError> {
+        const RESERVED_FIELD: u8 = 0x00;
+        const ACCESS_FIELD: u8 = 0x01;
+        const CONNECT_FIELD: u8 = 0x02;
+        const EXTENDED_ACCESS_FIELD: u8 = 0x03;
+
+        let mut field_offset = 0;
+
+        while context.current_block.pc < (start_pc + pkg_length) {
+            match context.next()? {
+                RESERVED_FIELD => {
+                    let length = context.pkglength()?;
+                    field_offset += length;
+                }
+                ACCESS_FIELD => {
+                    let access_type = context.next()?;
+                    let access_attrib = context.next()?;
+                    todo!()
+                    // TODO
+                }
+                CONNECT_FIELD => {
+                    // TODO: either consume a namestring or `BufferData` (it's not
+                    // clear what a buffer data acc is lmao)
+                    todo!("Connect field :(");
+                }
+                EXTENDED_ACCESS_FIELD => {
+                    todo!("Extended access field :(");
+                }
+                _ => {
+                    context.current_block.pc -= 1;
+                    // TODO: this should only ever be a nameseg really...
+                    let field_name = context.namestring()?;
+                    let field_length = context.pkglength()?;
+
+                    let field = Object::FieldUnit(FieldUnit {
+                        kind: kind.clone(),
+                        bit_index: field_offset,
+                        bit_length: field_length,
+                        flags: FieldFlags(flags),
+                    });
+                    self.namespace.lock().insert(field_name.resolve(&context.current_scope)?, Arc::new(field))?;
+
+                    field_offset += field_length;
+                }
+            }
+        }
+
+        Ok(())
+    }
+
     fn do_binary_maths(&self, context: &mut MethodContext, op: OpInFlight) -> Result<(), AmlError> {
         let [Argument::Object(left), Argument::Object(right), Argument::Object(target)] = &op.arguments[0..2]
         else {
diff --git a/aml/src/object.rs b/aml/src/object.rs
index 6948beb5..340b1ef9 100644
--- a/aml/src/object.rs
+++ b/aml/src/object.rs
@@ -121,12 +121,23 @@ impl Object {
 }
 
 #[derive(Debug)]
-pub enum FieldUnit {
-    Normal { region: Arc<Object>, bit_index: usize, bit_length: usize },
-    Bank,
-    Index,
+pub struct FieldUnit {
+    pub kind: FieldUnitKind,
+    pub flags: FieldFlags,
+    pub bit_index: usize,
+    pub bit_length: usize,
 }
 
+#[derive(Clone, Debug)]
+pub enum FieldUnitKind {
+    Normal { region: Arc<Object> },
+    Bank { region: Arc<Object>, bank: Arc<Object>, bank_value: u64 },
+    Index { index: Arc<Object>, data: Arc<Object> },
+}
+
+#[derive(Clone, Copy, Debug)]
+pub struct FieldFlags(pub u8);
+
 #[derive(Clone, Copy, Debug)]
 pub struct MethodFlags(pub u8);
 
diff --git a/tests/fields.asl b/tests/fields.asl
new file mode 100644
index 00000000..a43c31db
--- /dev/null
+++ b/tests/fields.asl
@@ -0,0 +1,34 @@
+DefinitionBlock("fields.aml", "DSDT", 1, "RSACPI", "BUFFLD", 1) {
+    OperationRegion(GIO0, SystemIO, 0x125, 0x100)
+
+    Field(GIO0, ByteAcc, NoLock, Preserve) {
+        GLB1, 1,
+        GLB2, 1,
+        Offset(1),
+        BNK1, 4,
+
+        Offset(2),
+        IDX0, 8,
+        DAT0, 8
+    }
+
+    BankField(GIO0, BNK1, 0, ByteAcc, NoLock, Preserve) {
+        Offset(0x30),
+        FET0, 1,
+        FET1, 1
+    }
+
+    BankField(GIO0, BNK1, 1, ByteAcc, NoLock, Preserve) {
+        Offset(0x30),
+        BLVL, 7,
+        BAC, 1
+    }
+
+    IndexField(IDX0, DAT0, ByteAcc, NoLock, Preserve) {
+        FET2, 1,
+        FET3, 1,
+        Offset(0x2f),
+        , 7,
+        FET4, 1
+    }
+}

From 4c24b08ff2fabf708d2a9e78a87bb3fbd70cceb2 Mon Sep 17 00:00:00 2001
From: Isaac Woods <isaacwoods.home@gmail.com>
Date: Sat, 22 Feb 2025 00:09:04 +0000
Subject: [PATCH 47/88] `aml`: implement `DefIndex`

---
 aml/src/lib.rs    | 54 ++++++++++++++++++++++++++++++++++++++++++++++-
 aml/src/object.rs | 22 ++++++++++++++-----
 2 files changed, 70 insertions(+), 6 deletions(-)

diff --git a/aml/src/lib.rs b/aml/src/lib.rs
index 6dde540e..7af43d7c 100644
--- a/aml/src/lib.rs
+++ b/aml/src/lib.rs
@@ -384,6 +384,7 @@ where
                         context.contribute_arg(Argument::Object(Arc::new(Object::Integer(typ))));
                     }
                     Opcode::SizeOf => self.do_size_of(&mut context, op)?,
+                    Opcode::Index => self.do_index(&mut context, op)?,
                     Opcode::BankField => {
                         let [
                             Argument::TrackedPc(start_pc),
@@ -826,7 +827,7 @@ where
                 Opcode::ConcatRes => todo!(),
                 Opcode::Notify => todo!(),
                 Opcode::SizeOf => context.start_in_flight_op(OpInFlight::new(opcode, 1)),
-                Opcode::Index => todo!(),
+                Opcode::Index => context.start_in_flight_op(OpInFlight::new(opcode, 3)),
                 Opcode::Match => todo!(),
 
                 Opcode::CreateBitField
@@ -1104,6 +1105,55 @@ where
         context.contribute_arg(Argument::Object(Arc::new(Object::Integer(result as u64))));
         Ok(())
     }
+
+    fn do_index(&self, context: &mut MethodContext, op: OpInFlight) -> Result<(), AmlError> {
+        let [Argument::Object(object), Argument::Object(index_value), target] = &op.arguments[..] else {
+            panic!()
+        };
+        let Object::Integer(index_value) = **index_value else {
+            Err(AmlError::ObjectNotOfExpectedType { expected: ObjectType::Integer, got: index_value.typ() })?
+        };
+
+        let result = Arc::new(match **object {
+            Object::Buffer(ref buffer) => {
+                if index_value as usize >= buffer.len() {
+                    Err(AmlError::IndexOutOfBounds)?
+                }
+
+                Object::Reference {
+                    kind: ReferenceKind::RefOf,
+                    inner: Arc::new(Object::BufferField {
+                        buffer: object.clone(),
+                        offset: index_value as usize * 8,
+                        length: 8,
+                    }),
+                }
+            }
+            Object::String(ref string) => {
+                if index_value as usize >= string.len() {
+                    Err(AmlError::IndexOutOfBounds)?
+                }
+
+                Object::Reference {
+                    kind: ReferenceKind::RefOf,
+                    inner: Arc::new(Object::BufferField {
+                        buffer: object.clone(),
+                        offset: index_value as usize * 8,
+                        length: 8,
+                    }),
+                }
+            }
+            Object::Package(ref package) => {
+                let Some(element) = package.get(index_value as usize) else { Err(AmlError::IndexOutOfBounds)? };
+                Object::Reference { kind: ReferenceKind::RefOf, inner: element.clone() }
+            }
+            _ => Err(AmlError::IndexOutOfBounds)?,
+        });
+
+        self.do_store(context, target, result.clone())?;
+        context.contribute_arg(Argument::Object(result));
+        Ok(())
+    }
     fn do_store(
         &self,
         context: &mut MethodContext,
@@ -1703,6 +1753,8 @@ pub enum AmlError {
     MethodArgCountIncorrect,
 
     InvalidOperationOnObject,
+    IndexOutOfBounds,
+    ObjectNotOfExpectedType { expected: ObjectType, got: ObjectType },
 
     InvalidResourceDescriptor,
     UnexpectedResourceType,
diff --git a/aml/src/object.rs b/aml/src/object.rs
index 340b1ef9..7114bf9b 100644
--- a/aml/src/object.rs
+++ b/aml/src/object.rs
@@ -46,9 +46,13 @@ impl Object {
 
     pub fn read_buffer_field(&self, dst: &mut [u8]) -> Result<(), AmlError> {
         if let Self::BufferField { buffer, offset, length } = self {
-            let Object::Buffer(ref buffer) = **buffer else { panic!() };
-            // TODO: assert length of buffer is sufficient
-            copy_bits(buffer.as_slice(), *offset, dst, 0, *length);
+            let buffer = match **buffer {
+                Object::Buffer(ref buffer) => buffer.as_slice(),
+                Object::String(ref string) => string.as_bytes(),
+                _ => panic!(),
+            };
+            // TODO: bounds check the buffer first to avoid panicking
+            copy_bits(buffer, *offset, dst, 0, *length);
             Ok(())
         } else {
             Err(AmlError::InvalidOperationOnObject)
@@ -56,9 +60,16 @@ impl Object {
     }
 
     pub fn write_buffer_field(&mut self, value: &[u8]) -> Result<(), AmlError> {
+        // TODO: bounds check the buffer first to avoid panicking
         if let Self::BufferField { buffer, offset, length } = self {
-            let Object::Buffer(buffer) = buffer.gain_mut() else { panic!() };
-            copy_bits(value, 0, buffer.as_mut_slice(), *offset, *length);
+            let buffer = match buffer.gain_mut() {
+                Object::Buffer(buffer) => buffer.as_mut_slice(),
+                // XXX: this unfortunately requires us to trust AML to keep the string as valid
+                // UTF8... maybe there is a better way?
+                Object::String(string) => unsafe { string.as_bytes_mut() },
+                _ => panic!(),
+            };
+            copy_bits(value, 0, buffer, *offset, *length);
             Ok(())
         } else {
             Err(AmlError::InvalidOperationOnObject)
@@ -157,6 +168,7 @@ impl MethodFlags {
 
 #[derive(Clone, Copy, PartialEq, Debug)]
 pub enum ReferenceKind {
+    RefOf,
     LocalOrArg,
     Unresolved,
 }

From 8378db4b49ff7645f3cb3411278bb20f1e0346b5 Mon Sep 17 00:00:00 2001
From: Isaac Woods <isaacwoods.home@gmail.com>
Date: Sat, 22 Feb 2025 22:59:31 +0000
Subject: [PATCH 48/88] `aml`: better type mismatch errors + `DefNot` support

---
 aml/src/lib.rs    | 75 ++++++++++++++++++++++++-----------------------
 aml/src/object.rs |  8 +++++
 2 files changed, 47 insertions(+), 36 deletions(-)

diff --git a/aml/src/lib.rs b/aml/src/lib.rs
index 7af43d7c..9278fec7 100644
--- a/aml/src/lib.rs
+++ b/aml/src/lib.rs
@@ -121,12 +121,17 @@ where
                     | Opcode::Xor => {
                         self.do_binary_maths(&mut context, op)?;
                     }
-                    Opcode::FindSetLeftBit | Opcode::FindSetRightBit => {
+                    Opcode::Not | Opcode::FindSetLeftBit | Opcode::FindSetRightBit => {
                         self.do_unary_maths(&mut context, op)?;
                     }
                     Opcode::Increment | Opcode::Decrement => {
                         let [Argument::Object(operand)] = &op.arguments[..] else { panic!() };
-                        let Object::Integer(operand) = operand.gain_mut() else { panic!() };
+                        let Object::Integer(operand) = operand.gain_mut() else {
+                            Err(AmlError::ObjectNotOfExpectedType {
+                                expected: ObjectType::Integer,
+                                got: operand.typ(),
+                            })?
+                        };
 
                         let new_value = match op.op {
                             Opcode::Increment => operand.wrapping_add(1),
@@ -163,7 +168,7 @@ where
                         else {
                             panic!()
                         };
-                        let Object::Integer(arg) = **arg else { panic!() };
+                        let arg = arg.as_integer()?;
                         self.handler.handle_fatal_error(*typ, *code, arg);
                     }
                     Opcode::OpRegion => {
@@ -176,9 +181,8 @@ where
                         else {
                             panic!()
                         };
-                        let Object::Integer(region_offset) = **region_offset else { panic!() };
-                        let Object::Integer(region_length) = **region_length else { panic!() };
-
+                        let region_offset = region_offset.as_integer()?;
+                        let region_length = region_length.as_integer()?;
                         let region_space = RegionSpace::from(*region_space);
 
                         let region = Object::OpRegion(OpRegion {
@@ -197,7 +201,7 @@ where
                         else {
                             panic!()
                         };
-                        let Object::Integer(buffer_size) = **buffer_size else { panic!() };
+                        let buffer_size = buffer_size.as_integer()?;
 
                         let buffer_len = pkg_length - (context.current_block.pc - start_pc);
                         let mut buffer = vec![0; buffer_size as usize];
@@ -242,7 +246,7 @@ where
                             panic!()
                         };
 
-                        let Object::Integer(predicate) = **predicate else { panic!() };
+                        let predicate = predicate.as_integer()?;
                         let remaining_then_length = then_length - (context.current_block.pc - start_pc);
 
                         if predicate > 0 {
@@ -267,7 +271,7 @@ where
                             panic!()
                         };
                         let name = context.namestring()?;
-                        let Object::Integer(index) = **index else { panic!() };
+                        let index = index.as_integer()?;
                         let (offset, length) = match opcode {
                             Opcode::CreateBitField => (index, 1),
                             Opcode::CreateByteField => (index * 8, 8),
@@ -292,8 +296,9 @@ where
                             panic!()
                         };
                         let name = context.namestring()?;
-                        let Object::Integer(bit_index) = **bit_index else { panic!() };
-                        let Object::Integer(num_bits) = **num_bits else { panic!() };
+                        let bit_index = bit_index.as_integer()?;
+                        let num_bits = num_bits.as_integer()?;
+
                         self.namespace.lock().insert(
                             name.resolve(&context.current_scope)?,
                             Arc::new(Object::BufferField {
@@ -309,13 +314,11 @@ where
                     }
                     Opcode::Sleep => {
                         let [Argument::Object(msec)] = &op.arguments[..] else { panic!() };
-                        let Object::Integer(msec) = **msec else { panic!() };
-                        self.handler.sleep(msec);
+                        self.handler.sleep(msec.as_integer()?);
                     }
                     Opcode::Stall => {
                         let [Argument::Object(usec)] = &op.arguments[..] else { panic!() };
-                        let Object::Integer(usec) = **usec else { panic!() };
-                        self.handler.stall(usec);
+                        self.handler.stall(usec.as_integer()?);
                     }
                     Opcode::InternalMethodCall => {
                         let [Argument::Object(method), Argument::Namestring(method_scope)] = &op.arguments[0..2]
@@ -396,8 +399,7 @@ where
                         else {
                             panic!()
                         };
-                        let Object::Integer(bank_value) = **bank_value else { panic!() };
-
+                        let bank_value = bank_value.as_integer()?;
                         let field_flags = context.next()?;
 
                         let (region, bank) = {
@@ -954,12 +956,8 @@ where
             None
         };
 
-        let Object::Integer(left) = *left.clone().unwrap_transparent_reference() else {
-            Err(AmlError::InvalidOperationOnObject)?
-        };
-        let Object::Integer(right) = *right.clone().unwrap_transparent_reference() else {
-            Err(AmlError::InvalidOperationOnObject)?
-        };
+        let left = left.clone().unwrap_transparent_reference().as_integer()?;
+        let right = right.clone().unwrap_transparent_reference().as_integer()?;
 
         let value = match op.op {
             Opcode::Add => left.wrapping_add(right),
@@ -989,7 +987,7 @@ where
 
     fn do_unary_maths(&self, context: &mut MethodContext, op: OpInFlight) -> Result<(), AmlError> {
         let [Argument::Object(operand)] = &op.arguments[..] else { Err(AmlError::InvalidOperationOnObject)? };
-        let Object::Integer(operand) = **operand else { Err(AmlError::InvalidOperationOnObject)? };
+        let operand = operand.clone().unwrap_transparent_reference().as_integer()?;
 
         let result = match op.op {
             Opcode::FindSetLeftBit => {
@@ -1000,27 +998,34 @@ where
                      * TODO: this is a particular instance where not respecting integers being
                      * 32-bit on revision 1 tables does cause properly incorrect behaviour...
                      */
-                    operand.leading_zeros() + 1
+                    (operand.leading_zeros() + 1) as u64
                 }
             }
             Opcode::FindSetRightBit => {
                 if operand == 0 {
                     0
                 } else {
-                    operand.trailing_zeros() + 1
+                    (operand.trailing_zeros() + 1) as u64
+                }
+            }
+            Opcode::Not => {
+                if operand == 0 {
+                    u64::MAX
+                } else {
+                    0
                 }
             }
             _ => panic!(),
         };
 
-        context.contribute_arg(Argument::Object(Arc::new(Object::Integer(result as u64))));
+        context.contribute_arg(Argument::Object(Arc::new(Object::Integer(result))));
         Ok(())
     }
 
     fn do_logical_op(&self, context: &mut MethodContext, op: OpInFlight) -> Result<(), AmlError> {
         if op.op == Opcode::LNot {
             let [Argument::Object(operand)] = &op.arguments[..] else { Err(AmlError::InvalidOperationOnObject)? };
-            let Object::Integer(operand) = **operand else { Err(AmlError::InvalidOperationOnObject)? };
+            let operand = operand.clone().unwrap_transparent_reference().as_integer()?;
             let result = if operand == 0 { u64::MAX } else { 0 };
 
             if let Some(prev_op) = context.in_flight.last_mut() {
@@ -1039,8 +1044,8 @@ where
         // TODO: for some of the ops, strings and buffers are also allowed :(
         // TODO: apparently when doing this conversion (^), NT's interpreter just takes the first 4
         // bytes of the string/buffer and casts them to an integer lmao
-        let Object::Integer(left) = **left else { Err(AmlError::InvalidOperationOnObject)? };
-        let Object::Integer(right) = **right else { Err(AmlError::InvalidOperationOnObject)? };
+        let left = left.clone().unwrap_transparent_reference().as_integer()?;
+        let right = right.clone().unwrap_transparent_reference().as_integer()?;
 
         let result = match op.op {
             Opcode::LAnd => (left > 0) && (right > 0),
@@ -1061,7 +1066,7 @@ where
 
     fn do_from_bcd(&self, context: &mut MethodContext, op: OpInFlight) -> Result<(), AmlError> {
         let [Argument::Object(value)] = &op.arguments[..] else { Err(AmlError::InvalidOperationOnObject)? };
-        let Object::Integer(mut value) = **value else { Err(AmlError::InvalidOperationOnObject)? };
+        let mut value = value.clone().unwrap_transparent_reference().as_integer()?;
 
         let mut result = 0;
         let mut i = 1;
@@ -1077,7 +1082,7 @@ where
 
     fn do_to_bcd(&self, context: &mut MethodContext, op: OpInFlight) -> Result<(), AmlError> {
         let [Argument::Object(value)] = &op.arguments[..] else { Err(AmlError::InvalidOperationOnObject)? };
-        let Object::Integer(mut value) = **value else { Err(AmlError::InvalidOperationOnObject)? };
+        let mut value = value.clone().unwrap_transparent_reference().as_integer()?;
 
         let mut result = 0;
         let mut i = 0;
@@ -1110,9 +1115,7 @@ where
         let [Argument::Object(object), Argument::Object(index_value), target] = &op.arguments[..] else {
             panic!()
         };
-        let Object::Integer(index_value) = **index_value else {
-            Err(AmlError::ObjectNotOfExpectedType { expected: ObjectType::Integer, got: index_value.typ() })?
-        };
+        let index_value = index_value.as_integer()?;
 
         let result = Arc::new(match **object {
             Object::Buffer(ref buffer) => {
@@ -1319,7 +1322,7 @@ impl MethodContext {
             };
             Ok(context)
         } else {
-            panic!()
+            Err(AmlError::ObjectNotOfExpectedType { expected: ObjectType::Method, got: method.typ() })
         }
     }
 
diff --git a/aml/src/object.rs b/aml/src/object.rs
index 7114bf9b..a70f2021 100644
--- a/aml/src/object.rs
+++ b/aml/src/object.rs
@@ -44,6 +44,14 @@ impl Object {
         }
     }
 
+    pub fn as_integer(&self) -> Result<u64, AmlError> {
+        if let Object::Integer(value) = self {
+            Ok(*value)
+        } else {
+            Err(AmlError::ObjectNotOfExpectedType { expected: ObjectType::Integer, got: self.typ() })
+        }
+    }
+
     pub fn read_buffer_field(&self, dst: &mut [u8]) -> Result<(), AmlError> {
         if let Self::BufferField { buffer, offset, length } = self {
             let buffer = match **buffer {

From 9795a38ef97d4c6f22c88cb4922a72b25e6a0d8c Mon Sep 17 00:00:00 2001
From: Isaac Woods <isaacwoods.home@gmail.com>
Date: Wed, 5 Mar 2025 01:00:13 +0000
Subject: [PATCH 49/88] `aml`: support aliases

---
 aml/src/lib.rs       | 10 +++++-
 aml/src/namespace.rs | 78 ++++++++++++++++++++++++++++++++++++++++----
 tests/scopes.asl     |  5 +++
 3 files changed, 85 insertions(+), 8 deletions(-)

diff --git a/aml/src/lib.rs b/aml/src/lib.rs
index 9278fec7..80af3550 100644
--- a/aml/src/lib.rs
+++ b/aml/src/lib.rs
@@ -504,7 +504,15 @@ where
                 Opcode::One => {
                     context.last_op()?.arguments.push(Argument::Object(Arc::new(Object::Integer(1))));
                 }
-                Opcode::Alias => todo!(),
+                Opcode::Alias => {
+                    let source = context.namestring()?;
+                    let alias = context.namestring()?;
+
+                    let mut namespace = self.namespace.lock();
+                    let object = namespace.get(source.resolve(&context.current_scope)?)?.clone();
+                    let alias = alias.resolve(&context.current_scope)?;
+                    namespace.create_alias(alias, object)?;
+                }
                 Opcode::Name => {
                     let name = context.namestring()?;
                     context.start_in_flight_op(OpInFlight::new_with(
diff --git a/aml/src/namespace.rs b/aml/src/namespace.rs
index 49ba4199..155e6c26 100644
--- a/aml/src/namespace.rs
+++ b/aml/src/namespace.rs
@@ -6,6 +6,7 @@ use alloc::{
     vec,
     vec::Vec,
 };
+use bit_field::BitField;
 use core::{fmt, str, str::FromStr};
 
 pub struct Namespace {
@@ -63,7 +64,18 @@ impl Namespace {
         let path = path.normalize()?;
 
         let (level, last_seg) = self.get_level_for_path_mut(&path)?;
-        match level.values.insert(last_seg, object) {
+        match level.values.insert(last_seg, (ObjectFlags::new(false), object)) {
+            None => Ok(()),
+            Some(_) => Err(AmlError::NameCollision(path)),
+        }
+    }
+
+    pub fn create_alias(&mut self, path: AmlName, object: Arc<Object>) -> Result<(), AmlError> {
+        assert!(path.is_absolute());
+        let path = path.normalize()?;
+
+        let (level, last_seg) = self.get_level_for_path_mut(&path)?;
+        match level.values.insert(last_seg, (ObjectFlags::new(true), object)) {
             None => Ok(()),
             Some(_) => Err(AmlError::NameCollision(path)),
         }
@@ -75,7 +87,7 @@ impl Namespace {
 
         let (level, last_seg) = self.get_level_for_path_mut(&path)?;
         match level.values.get(&last_seg) {
-            Some(object) => Ok(object.clone()),
+            Some((_, object)) => Ok(object.clone()),
             None => Err(AmlError::ObjectDoesNotExist(path.clone())),
         }
     }
@@ -97,7 +109,7 @@ impl Namespace {
                 let name = path.resolve(&scope)?;
                 match self.get_level_for_path(&name) {
                     Ok((level, last_seg)) => {
-                        if let Some(object) = level.values.get(&last_seg) {
+                        if let Some((_, object)) = level.values.get(&last_seg) {
                             return Ok((name, object.clone()));
                         }
                     }
@@ -117,7 +129,7 @@ impl Namespace {
             let name = path.resolve(starting_scope)?;
             let (level, last_seg) = self.get_level_for_path(&path.resolve(starting_scope)?)?;
 
-            if let Some(object) = level.values.get(&last_seg) {
+            if let Some((_, object)) = level.values.get(&last_seg) {
                 Ok((name, object.clone()))
             } else {
                 Err(AmlError::ObjectDoesNotExist(path.clone()))
@@ -207,6 +219,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, 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::Display for Namespace {
@@ -221,10 +262,18 @@ impl fmt::Display for Namespace {
             level: &NamespaceLevel,
             indent_stack: String,
         ) -> fmt::Result {
-            for (i, (name, object)) in level.values.iter().enumerate() {
+            for (i, (name, (flags, object))) in level.values.iter().enumerate() {
                 let end = (i == level.values.len() - 1)
                     && level.children.iter().filter(|(_, l)| l.kind == NamespaceLevelKind::Scope).count() == 0;
-                writeln!(f, "{}{}{}: {:?}", &indent_stack, if end { END } else { BRANCH }, name.as_str(), object)?;
+                writeln!(
+                    f,
+                    "{}{}{}: {}{:?}",
+                    &indent_stack,
+                    if end { END } else { BRANCH },
+                    name.as_str(),
+                    if flags.is_alias() { "[A] " } else { "" },
+                    object
+                )?;
 
                 // If the object has a corresponding scope, print it here
                 if let Some(child_level) = level.children.get(&name) {
@@ -265,10 +314,25 @@ pub enum NamespaceLevelKind {
 
 pub struct NamespaceLevel {
     pub kind: NamespaceLevelKind,
-    pub values: BTreeMap<NameSeg, Arc<Object>>,
+    pub values: BTreeMap<NameSeg, (ObjectFlags, Arc<Object>)>,
     pub children: BTreeMap<NameSeg, NamespaceLevel>,
 }
 
+#[derive(Clone, Copy, Debug)]
+pub struct ObjectFlags(u8);
+
+impl ObjectFlags {
+    pub fn new(is_alias: bool) -> ObjectFlags {
+        let mut flags = 0;
+        flags.set_bit(0, is_alias);
+        ObjectFlags(flags)
+    }
+
+    pub fn is_alias(&self) -> bool {
+        self.0.get_bit(0)
+    }
+}
+
 impl NamespaceLevel {
     pub fn new(kind: NamespaceLevelKind) -> NamespaceLevel {
         NamespaceLevel { kind, values: BTreeMap::new(), children: BTreeMap::new() }
diff --git a/tests/scopes.asl b/tests/scopes.asl
index 1e5644b1..3ae726af 100644
--- a/tests/scopes.asl
+++ b/tests/scopes.asl
@@ -10,5 +10,10 @@ DefinitionBlock("scopes.aml", "DSDT", 1, "RSACPI", "SCOPES", 1) {
                 Name(Z, 413)
             }
         }
+
+        Device(FOO) {
+            Name (_HID, EisaId ("PNP0A03"))
+            Alias (\_SB.PCI0.Y, MY_Y)
+        }
     }
 }

From aee45b35b834942d82e4b0f242ebd0383b4ab5d9 Mon Sep 17 00:00:00 2001
From: Isaac Woods <isaacwoods.home@gmail.com>
Date: Wed, 5 Mar 2025 01:52:02 +0000
Subject: [PATCH 50/88] `aml`: implement `DefWhile`,`DefBreak`,`DefContinue`

---
 aml/src/lib.rs  | 67 ++++++++++++++++++++++++++++++++++++++++++++++---
 tests/while.asl | 40 ++++++++++++++---------------
 2 files changed, 83 insertions(+), 24 deletions(-)

diff --git a/aml/src/lib.rs b/aml/src/lib.rs
index 80af3550..9eac3719 100644
--- a/aml/src/lib.rs
+++ b/aml/src/lib.rs
@@ -412,6 +412,19 @@ where
                         let kind = FieldUnitKind::Bank { region, bank, bank_value };
                         self.parse_field_list(&mut context, kind, *start_pc, *pkg_length, field_flags)?;
                     }
+                    Opcode::While => {
+                        /*
+                         * We've just evaluated the predicate for an iteration of a while loop. If
+                         * false, skip over the rest of the loop, otherwise carry on.
+                         */
+                        let [Argument::Object(predicate)] = &op.arguments[..] else { panic!() };
+                        let predicate = predicate.as_integer()?;
+
+                        if predicate == 0 {
+                            // Exit from the while loop by skipping out of the current block
+                            context.current_block = context.block_stack.pop().unwrap();
+                        }
+                    }
                     _ => panic!("Unexpected operation has created in-flight op!"),
                 }
             }
@@ -493,6 +506,15 @@ where
 
                             continue;
                         }
+                        BlockKind::While { start_pc } => {
+                            /*
+                             * Go round again, and create a new in-flight op to have a look at the
+                             * predicate.
+                             */
+                            context.current_block.pc = start_pc;
+                            context.start_in_flight_op(OpInFlight::new(Opcode::While, 1));
+                            continue;
+                        }
                     }
                 }
                 Err(other_err) => return Err(other_err),
@@ -868,7 +890,6 @@ where
                 Opcode::ObjectType => context.start_in_flight_op(OpInFlight::new(opcode, 1)),
                 Opcode::CopyObject => todo!(),
                 Opcode::Mid => context.start_in_flight_op(OpInFlight::new(Opcode::Mid, 4)),
-                Opcode::Continue => todo!(),
                 Opcode::If => {
                     let start_pc = context.current_block.pc;
                     let then_length = context.pkglength()?;
@@ -879,10 +900,43 @@ where
                     ));
                 }
                 Opcode::Else => return Err(AmlError::ElseFoundWithoutCorrespondingIf),
-                Opcode::While => todo!(),
-                Opcode::Noop => {}
+                Opcode::While => {
+                    let start_pc = context.current_block.pc;
+                    let pkg_length = context.pkglength()?;
+                    let remaining_length = pkg_length - (context.current_block.pc - start_pc);
+                    context.start_new_block(
+                        BlockKind::While { start_pc: context.current_block.pc },
+                        remaining_length,
+                    );
+                    context.start_in_flight_op(OpInFlight::new(Opcode::While, 1));
+                }
+                Opcode::Continue => {
+                    let BlockKind::While { start_pc } = &context.current_block.kind else {
+                        Err(AmlError::ContinueOutsideOfWhile)?
+                    };
+                    context.current_block.pc = *start_pc;
+                    context.start_in_flight_op(OpInFlight::new(Opcode::While, 1));
+                }
+                Opcode::Break => {
+                    /*
+                     * Break out of the innermost `DefWhile`.
+                     */
+                    if let BlockKind::While { .. } = &context.current_block.kind {
+                        context.current_block = context.block_stack.pop().unwrap();
+                    } else {
+                        loop {
+                            let Some(block) = context.block_stack.pop() else {
+                                Err(AmlError::BreakOutsideOfWhile)?
+                            };
+                            if let BlockKind::While { .. } = block.kind {
+                                break;
+                            }
+                        }
+                        context.current_block = context.block_stack.pop().unwrap();
+                    }
+                }
                 Opcode::Return => context.start_in_flight_op(OpInFlight::new(Opcode::Return, 1)),
-                Opcode::Break => todo!(),
+                Opcode::Noop => {}
                 Opcode::Breakpoint => {
                     self.handler.breakpoint();
                 }
@@ -1276,6 +1330,9 @@ pub enum BlockKind {
     /// Used for executing the then-branch of an `DefIfElse`. After finishing, it will check for
     /// and skip over an else-branch, if present.
     IfThenBranch,
+    While {
+        start_pc: usize,
+    },
 }
 
 impl OpInFlight {
@@ -1760,6 +1817,8 @@ pub enum AmlError {
 
     NoCurrentOp,
     ElseFoundWithoutCorrespondingIf,
+    ContinueOutsideOfWhile,
+    BreakOutsideOfWhile,
 
     MethodArgCountIncorrect,
 
diff --git a/tests/while.asl b/tests/while.asl
index 517af2bc..d58d45d2 100644
--- a/tests/while.asl
+++ b/tests/while.asl
@@ -1,25 +1,25 @@
 DefinitionBlock("while.aml", "DSDT", 1, "RSACPI", "WHILE", 1) {
-	Name(X, 0)
-	While (X < 5) {
-		X++
-	}
+    Name(X, 0)
+    While (X < 5) {
+        X++
+    }
 
-	// Test `DefBreak` - Y should only make it to 5
-	Name(Y, 0)
-	While (Y < 10) {
-		If (Y >= 5) {
-			Break
-		}
+    // Test `DefBreak` - Y should only make it to 5
+    Name(Y, 0)
+    While (Y < 10) {
+        If (Y >= 5) {
+            Break
+        }
 
-		Y++
-	}
+        Y++
+    }
 
-	// Test `DefContinue` - Z should remain at zero
-	Name(CNT, 0)
-	Name(Z, 0)
-	While (CNT < 5) {
-		CNT++
-		Continue
-		Z++
-	}
+    // Test `DefContinue` - Z should remain at zero
+    Name(CNT, 0)
+    Name(Z, 0)
+    While (CNT < 5) {
+        CNT++
+        Continue
+        Z++
+    }
 }

From 7442604a69a0d8f42520fb81284f269a9939e6e6 Mon Sep 17 00:00:00 2001
From: Isaac Woods <isaacwoods.home@gmail.com>
Date: Wed, 5 Mar 2025 02:06:00 +0000
Subject: [PATCH 51/88] `aml`: implement `DefRefOf`, `DefCondRefOf`

---
 aml/src/lib.rs | 26 +++++++++++++++++++++++---
 1 file changed, 23 insertions(+), 3 deletions(-)

diff --git a/aml/src/lib.rs b/aml/src/lib.rs
index 9eac3719..ac0429cb 100644
--- a/aml/src/lib.rs
+++ b/aml/src/lib.rs
@@ -312,6 +312,24 @@ where
                         let [Argument::Object(object), target] = &op.arguments[..] else { panic!() };
                         self.do_store(&mut context, &target, object.clone())?;
                     }
+                    Opcode::RefOf => {
+                        let [Argument::Object(object)] = &op.arguments[..] else { panic!() };
+                        let reference =
+                            Arc::new(Object::Reference { kind: ReferenceKind::RefOf, inner: object.clone() });
+                        context.contribute_arg(Argument::Object(reference));
+                    }
+                    Opcode::CondRefOf => {
+                        let [Argument::Object(object), target] = &op.arguments[..] else { panic!() };
+                        let result = if let Object::Reference { kind: ReferenceKind::Unresolved, .. } = **object {
+                            Object::Integer(0)
+                        } else {
+                            let reference =
+                                Arc::new(Object::Reference { kind: ReferenceKind::RefOf, inner: object.clone() });
+                            self.do_store(&mut context, target, reference)?;
+                            Object::Integer(u64::MAX)
+                        };
+                        context.contribute_arg(Argument::Object(Arc::new(result)));
+                    }
                     Opcode::Sleep => {
                         let [Argument::Object(msec)] = &op.arguments[..] else { panic!() };
                         self.handler.sleep(msec.as_integer()?);
@@ -642,7 +660,6 @@ where
                     let name = name.resolve(&context.current_scope)?;
                     self.namespace.lock().insert(name, Arc::new(Object::Event))?;
                 }
-                Opcode::CondRefOf => todo!(),
                 Opcode::LoadTable => todo!(),
                 Opcode::Load => todo!(),
                 Opcode::Stall => context.start_in_flight_op(OpInFlight::new(Opcode::Stall, 1)),
@@ -798,7 +815,8 @@ where
                     })));
                 }
                 Opcode::Store => context.start_in_flight_op(OpInFlight::new(Opcode::Store, 2)),
-                Opcode::RefOf => todo!(),
+                Opcode::RefOf => context.start_in_flight_op(OpInFlight::new(Opcode::RefOf, 1)),
+                Opcode::CondRefOf => context.start_in_flight_op(OpInFlight::new(opcode, 2)),
 
                 Opcode::DualNamePrefix
                 | Opcode::MultiNamePrefix
@@ -822,7 +840,9 @@ where
                             }
                         }
                         Err(AmlError::ObjectDoesNotExist(_)) => {
-                            if context.current_block.kind == BlockKind::Package {
+                            let allow_unresolved = context.current_block.kind == BlockKind::Package
+                                || context.in_flight.last().map(|op| op.op == Opcode::CondRefOf).unwrap_or(false);
+                            if allow_unresolved {
                                 let reference = Object::Reference {
                                     kind: ReferenceKind::Unresolved,
                                     inner: Arc::new(Object::String(name.to_string())),

From eb47b1c4b056571f40d4590047252bbad476def5 Mon Sep 17 00:00:00 2001
From: Isaac Woods <isaacwoods.home@gmail.com>
Date: Wed, 5 Mar 2025 02:06:30 +0000
Subject: [PATCH 52/88] `aml`: implement `DefMid`

---
 aml/src/lib.rs | 38 ++++++++++++++++++++++++++++++++++++++
 1 file changed, 38 insertions(+)

diff --git a/aml/src/lib.rs b/aml/src/lib.rs
index ac0429cb..5f1aeef2 100644
--- a/aml/src/lib.rs
+++ b/aml/src/lib.rs
@@ -152,6 +152,7 @@ where
                     | Opcode::LLess => {
                         self.do_logical_op(&mut context, op)?;
                     }
+                    Opcode::Mid => self.do_mid(&mut context, op)?,
                     Opcode::FromBCD => self.do_from_bcd(&mut context, op)?,
                     Opcode::ToBCD => self.do_to_bcd(&mut context, op)?,
                     Opcode::Name => {
@@ -1146,6 +1147,43 @@ where
         Ok(())
     }
 
+    fn do_mid(&self, context: &mut MethodContext, op: OpInFlight) -> Result<(), AmlError> {
+        let [Argument::Object(source), Argument::Object(index), Argument::Object(length), target] =
+            &op.arguments[..]
+        else {
+            panic!()
+        };
+        let index = index.as_integer()? as usize;
+        let length = length.as_integer()? as usize;
+
+        let result = Arc::new(match **source {
+            Object::String(ref string) => {
+                if index >= string.len() {
+                    Object::String(String::new())
+                } else {
+                    let upper = usize::min(index + length, index + string.len());
+                    let chars = &string[index..upper];
+                    Object::String(String::from(chars))
+                }
+            }
+            Object::Buffer(ref buffer) => {
+                if index >= buffer.len() {
+                    Object::Buffer(vec![])
+                } else {
+                    let upper = usize::min(index + length, index + buffer.len());
+                    let bytes = &buffer[index..upper];
+                    Object::Buffer(bytes.to_vec())
+                }
+            }
+            _ => Err(AmlError::InvalidOperationOnObject)?,
+        });
+
+        self.do_store(context, target, result.clone())?;
+        context.contribute_arg(Argument::Object(result));
+
+        Ok(())
+    }
+
     fn do_from_bcd(&self, context: &mut MethodContext, op: OpInFlight) -> Result<(), AmlError> {
         let [Argument::Object(value)] = &op.arguments[..] else { Err(AmlError::InvalidOperationOnObject)? };
         let mut value = value.clone().unwrap_transparent_reference().as_integer()?;

From d38ca7307cd0ca4d970c867ff1c3365a8db92b44 Mon Sep 17 00:00:00 2001
From: Isaac Woods <isaacwoods.home@gmail.com>
Date: Wed, 5 Mar 2025 02:19:50 +0000
Subject: [PATCH 53/88] `aml`: fix handling of `DefContinue`

---
 aml/src/lib.rs | 22 ++++++++++++++--------
 1 file changed, 14 insertions(+), 8 deletions(-)

diff --git a/aml/src/lib.rs b/aml/src/lib.rs
index 5f1aeef2..fdd5da47 100644
--- a/aml/src/lib.rs
+++ b/aml/src/lib.rs
@@ -932,16 +932,22 @@ where
                     context.start_in_flight_op(OpInFlight::new(Opcode::While, 1));
                 }
                 Opcode::Continue => {
-                    let BlockKind::While { start_pc } = &context.current_block.kind else {
-                        Err(AmlError::ContinueOutsideOfWhile)?
-                    };
-                    context.current_block.pc = *start_pc;
+                    if let BlockKind::While { start_pc } = &context.current_block.kind {
+                        context.current_block.pc = *start_pc;
+                    } else {
+                        loop {
+                            let Some(block) = context.block_stack.pop() else {
+                                Err(AmlError::ContinueOutsideOfWhile)?
+                            };
+                            if let BlockKind::While { start_pc } = block.kind {
+                                context.current_block.pc = start_pc;
+                                break;
+                            }
+                        }
+                    }
                     context.start_in_flight_op(OpInFlight::new(Opcode::While, 1));
                 }
                 Opcode::Break => {
-                    /*
-                     * Break out of the innermost `DefWhile`.
-                     */
                     if let BlockKind::While { .. } = &context.current_block.kind {
                         context.current_block = context.block_stack.pop().unwrap();
                     } else {
@@ -950,10 +956,10 @@ where
                                 Err(AmlError::BreakOutsideOfWhile)?
                             };
                             if let BlockKind::While { .. } = block.kind {
+                                context.current_block = context.block_stack.pop().unwrap();
                                 break;
                             }
                         }
-                        context.current_block = context.block_stack.pop().unwrap();
                     }
                 }
                 Opcode::Return => context.start_in_flight_op(OpInFlight::new(Opcode::Return, 1)),

From 55989b3e5e401c3d40c44de2b5fedb004f056034 Mon Sep 17 00:00:00 2001
From: Isaac Woods <isaacwoods.home@gmail.com>
Date: Wed, 5 Mar 2025 02:33:50 +0000
Subject: [PATCH 54/88] `aml`: handle strings and buffers in logical ops

---
 aml/src/lib.rs | 46 +++++++++++++++++++++++++++++++++++++++++-----
 1 file changed, 41 insertions(+), 5 deletions(-)

diff --git a/aml/src/lib.rs b/aml/src/lib.rs
index fdd5da47..6a041c8a 100644
--- a/aml/src/lib.rs
+++ b/aml/src/lib.rs
@@ -1130,11 +1130,47 @@ where
             Err(AmlError::InvalidOperationOnObject)?
         };
 
-        // TODO: for some of the ops, strings and buffers are also allowed :(
-        // TODO: apparently when doing this conversion (^), NT's interpreter just takes the first 4
-        // bytes of the string/buffer and casts them to an integer lmao
-        let left = left.clone().unwrap_transparent_reference().as_integer()?;
-        let right = right.clone().unwrap_transparent_reference().as_integer()?;
+        /*
+         * Some of these operations allow strings and buffers to be used as operands. Apparently
+         * NT's interpreter just takes the first 4 bytes of the string/buffer and casts them as an
+         * integer...
+         */
+        let left = left.clone().unwrap_transparent_reference();
+        let right = right.clone().unwrap_transparent_reference();
+        let (left, right) = match *left {
+            Object::Integer(left) => (left, right.as_integer()?),
+            Object::String(ref left) => {
+                let left = {
+                    let mut bytes = [0u8; 4];
+                    let left_bytes = left.as_bytes();
+                    (bytes[0..left_bytes.len()]).copy_from_slice(left_bytes);
+                    u32::from_le_bytes(bytes) as u64
+                };
+                let right = {
+                    let mut bytes = [0u8; 4];
+                    let right = right.as_string()?;
+                    let right_bytes = right.as_bytes();
+                    (bytes[0..right_bytes.len()]).copy_from_slice(right_bytes);
+                    u32::from_le_bytes(bytes) as u64
+                };
+                (left, right)
+            }
+            Object::Buffer(ref left) => {
+                let Object::Buffer(ref right) = *right else { panic!() };
+                let left = {
+                    let mut bytes = [0u8; 4];
+                    (bytes[0..left.len()]).copy_from_slice(left);
+                    u32::from_le_bytes(bytes) as u64
+                };
+                let right = {
+                    let mut bytes = [0u8; 4];
+                    (bytes[0..right.len()]).copy_from_slice(right);
+                    u32::from_le_bytes(bytes) as u64
+                };
+                (left, right)
+            }
+            _ => panic!(),
+        };
 
         let result = match op.op {
             Opcode::LAnd => (left > 0) && (right > 0),

From b6b78ca99db1e0f84559026fd18d1aeb9c5a72bd Mon Sep 17 00:00:00 2001
From: Isaac Woods <isaacwoods.home@gmail.com>
Date: Sun, 9 Mar 2025 22:53:05 +0000
Subject: [PATCH 55/88] `aml`: lay groundwork for field reads + support system
 memory + IO reads

---
 aml/src/lib.rs               | 154 ++++++++++++++++++++++++++++++++++-
 aml/src/object.rs            |  86 ++++++++++++++++++-
 aml/src/op_region.rs         |   9 +-
 tests/fields.asl             |  19 +++++
 tools/aml_tester/Cargo.toml  |   2 +-
 tools/aml_tester/src/main.rs | 113 +++++++++++++------------
 6 files changed, 327 insertions(+), 56 deletions(-)

diff --git a/aml/src/lib.rs b/aml/src/lib.rs
index 6a041c8a..4ebb348c 100644
--- a/aml/src/lib.rs
+++ b/aml/src/lib.rs
@@ -10,6 +10,8 @@ pub mod pci_routing;
 pub mod resource;
 
 use alloc::{
+    boxed::Box,
+    collections::btree_map::BTreeMap,
     string::{String, ToString},
     sync::Arc,
     vec,
@@ -17,9 +19,10 @@ use alloc::{
 };
 use bit_field::BitField;
 use core::mem;
+use log::{info, trace};
 use namespace::{AmlName, Namespace, NamespaceLevelKind};
 use object::{FieldFlags, FieldUnit, FieldUnitKind, MethodFlags, Object, ObjectType, ReferenceKind};
-use op_region::{OpRegion, RegionSpace};
+use op_region::{OpRegion, RegionHandler, RegionSpace};
 use spinning_top::Spinlock;
 
 pub struct Interpreter<H>
@@ -29,6 +32,8 @@ where
     handler: H,
     pub namespace: Spinlock<Namespace>,
     context_stack: Spinlock<Vec<MethodContext>>,
+    dsdt_revision: u8,
+    region_handlers: Spinlock<BTreeMap<RegionSpace, Box<dyn RegionHandler>>>,
 }
 
 unsafe impl<H> Send for Interpreter<H> where H: Handler + Send {}
@@ -41,11 +46,15 @@ impl<H> Interpreter<H>
 where
     H: Handler,
 {
-    pub fn new(handler: H) -> Interpreter<H> {
+    pub fn new(handler: H, dsdt_revision: u8) -> Interpreter<H> {
+        info!("Initializing AML interpreter v{}", env!("CARGO_PKG_VERSION"));
         Interpreter {
             handler,
             namespace: Spinlock::new(Namespace::new()),
             context_stack: Spinlock::new(Vec::new()),
+            dsdt_revision,
+            ops_interpreted: AtomicUsize::new(0),
+            region_handlers: Spinlock::new(BTreeMap::new()),
         }
     }
 
@@ -73,6 +82,15 @@ where
         }
     }
 
+    pub fn install_region_handler<RH>(&self, space: RegionSpace, handler: RH)
+    where
+        RH: RegionHandler + 'static,
+    {
+        let mut handlers = self.region_handlers.lock();
+        assert!(handlers.get(&space).is_none(), "Tried to install handler for same space twice!");
+        handlers.insert(space, Box::new(handler));
+    }
+
     fn do_execute_method(&self, mut context: MethodContext) -> Result<Arc<Object>, AmlError> {
         /*
          * TODO
@@ -1342,6 +1360,10 @@ where
                         let value = u64::from_le_bytes(buffer);
                         *target = value;
                     }
+                    Object::FieldUnit(field) => {
+                        // TODO: not sure if we should convert buffers to integers if needed here?
+                        *target = self.do_field_read(field)?.as_integer()?;
+                    }
                     _ => panic!("Store to integer from unsupported object: {:?}", object),
                 },
                 Object::BufferField { .. } => match object.gain_mut() {
@@ -1366,6 +1388,131 @@ where
         }
         Ok(())
     }
+
+    /// Do a read from a field by performing one or more well-formed accesses to the underlying
+    /// operation regions, and then shifting and masking the resulting value as appropriate. Will
+    /// return either an `Integer` or `Buffer` as appropriate, guided by the size of the field
+    /// and expected integer size (as per the DSDT revision).
+    fn do_field_read(&self, field: &FieldUnit) -> Result<Arc<Object>, AmlError> {
+        let needs_buffer = if self.dsdt_revision >= 2 { field.bit_length > 64 } else { field.bit_length > 32 };
+        let access_width_bits = field.flags.access_type_bytes()? * 8;
+
+        trace!("AML field read. Field = {:?}", field);
+
+        // TODO: if the field needs to be locked, acquire/release a global mutex?
+
+        enum Output {
+            Integer([u8; 8]),
+            Buffer(Vec<u8>),
+        }
+        let mut output = if needs_buffer {
+            Output::Buffer(vec![0; field.bit_length.next_multiple_of(8)])
+        } else {
+            Output::Integer([0; 8])
+        };
+        let output_bytes = match &mut output {
+            Output::Buffer(bytes) => bytes.as_mut_slice(),
+            Output::Integer(value) => value,
+        };
+
+        match field.kind {
+            FieldUnitKind::Normal { ref region } => {
+                let Object::OpRegion(ref region) = **region else { panic!() };
+
+                /*
+                 * TODO: it might be worth having a fast path here for reads that don't do weird
+                 * unaligned accesses, which I'm guessing might be relatively common on real
+                 * hardware? Eg. single native read + mask
+                 */
+
+                /*
+                 * Break the field read into native reads that respect the region's access width.
+                 * Copy each potentially-unaligned part into the destination's bit range.
+                 */
+                let native_accesses_needed = (field.bit_length + (field.bit_index % access_width_bits))
+                    .next_multiple_of(access_width_bits)
+                    / access_width_bits;
+                let mut read_so_far = 0;
+                for i in 0..native_accesses_needed {
+                    let aligned_offset =
+                        object::align_down(field.bit_index + i * access_width_bits, access_width_bits);
+                    let raw = self.do_native_region_read(region, aligned_offset / 8, access_width_bits / 8)?;
+                    let src_index = if i == 0 { field.bit_index % access_width_bits } else { 0 };
+                    let remaining_length = field.bit_length - read_so_far;
+                    let length = if i == 0 {
+                        usize::min(remaining_length, access_width_bits - (field.bit_index % access_width_bits))
+                    } else {
+                        usize::min(remaining_length, access_width_bits)
+                    };
+
+                    trace!(
+                        "Extracting bits {}..{} from native read to bits {}..{}",
+                        src_index,
+                        src_index + length,
+                        read_so_far,
+                        read_so_far + length,
+                    );
+                    object::copy_bits(&raw.to_le_bytes(), src_index, output_bytes, read_so_far, length);
+
+                    read_so_far += length;
+                }
+
+                match output {
+                    Output::Buffer(bytes) => Ok(Arc::new(Object::Buffer(bytes))),
+                    Output::Integer(value) => Ok(Arc::new(Object::Integer(u64::from_le_bytes(value)))),
+                }
+            }
+            FieldUnitKind::Bank { ref region, ref bank, bank_value } => todo!(),
+            FieldUnitKind::Index { ref index, ref data } => todo!(),
+        }
+    }
+
+    /// Performs an actual read from an operation region. `offset` and `length` must respect the
+    /// access requirements of the field being read, and are supplied in **bytes**. This may call
+    /// AML methods if required, and may invoke user-supplied handlers.
+    fn do_native_region_read(&self, region: &OpRegion, offset: usize, length: usize) -> Result<u64, AmlError> {
+        trace!("Native field read. Region = {:?}, offset = {:#x}, length={:#x}", region, offset, length);
+
+        match region.space {
+            RegionSpace::SystemMemory => Ok({
+                let address = region.base as usize + offset;
+                match length {
+                    1 => self.handler.read_u8(address) as u64,
+                    2 => self.handler.read_u16(address) as u64,
+                    4 => self.handler.read_u32(address) as u64,
+                    8 => self.handler.read_u64(address) as u64,
+                    _ => panic!(),
+                }
+            }),
+            RegionSpace::SystemIO => Ok({
+                let address = region.base as u16 + offset as u16;
+                match length {
+                    1 => self.handler.read_io_u8(address) as u64,
+                    2 => self.handler.read_io_u16(address) as u64,
+                    4 => self.handler.read_io_u32(address) as u64,
+                    _ => panic!(),
+                }
+            }),
+            RegionSpace::PciConfig => todo!(),
+
+            RegionSpace::EmbeddedControl
+            | RegionSpace::SmBus
+            | RegionSpace::SystemCmos
+            | RegionSpace::PciBarTarget
+            | RegionSpace::Ipmi
+            | RegionSpace::GeneralPurposeIo
+            | RegionSpace::GenericSerialBus
+            | RegionSpace::Pcc
+            | RegionSpace::Oem(_) => {
+                if let Some(handler) = self.region_handlers.lock().get(&region.space) {
+                    todo!("Utilise handler");
+                } else {
+                    // TODO: panic or normal error here??
+                    panic!("Unhandled region space that needs handler!");
+                }
+            }
+        }
+    }
 }
 
 /// A `MethodContext` represents a piece of running AML code - either a real method, or the
@@ -1904,6 +2051,7 @@ enum Opcode {
 pub enum AmlError {
     RunOutOfStream,
     IllegalOpcode(u16),
+    InvalidFieldFlags,
 
     InvalidName(Option<AmlName>),
 
@@ -2025,7 +2173,7 @@ mod tests {
 
     #[test]
     fn add_op() {
-        let interpreter = Interpreter::new(TestHandler);
+        let interpreter = Interpreter::new(TestHandler, 2);
         // AddOp 0x0e 0x06 => Local2
         interpreter.load_table(&[0x72, 0x0b, 0x0e, 0x00, 0x0a, 0x06, 0x62]).unwrap();
         // AddOp 0x0e (AddOp 0x01 0x03 => Local1) => Local1
diff --git a/aml/src/object.rs b/aml/src/object.rs
index a70f2021..224aa60d 100644
--- a/aml/src/object.rs
+++ b/aml/src/object.rs
@@ -157,6 +157,64 @@ pub enum FieldUnitKind {
 #[derive(Clone, Copy, Debug)]
 pub struct FieldFlags(pub u8);
 
+#[derive(Clone, Copy, Debug)]
+pub enum FieldAccessType {
+    Any,
+    Byte,
+    Word,
+    DWord,
+    QWord,
+    Buffer,
+}
+
+#[derive(Clone, Copy, Debug)]
+pub enum FieldUpdateRule {
+    Preserve,
+    WriteAsOnes,
+    WriteAsZeros,
+}
+
+impl FieldFlags {
+    pub fn access_type(&self) -> Result<FieldAccessType, AmlError> {
+        match self.0.get_bits(0..4) {
+            0 => Ok(FieldAccessType::Any),
+            1 => Ok(FieldAccessType::Byte),
+            2 => Ok(FieldAccessType::Word),
+            3 => Ok(FieldAccessType::DWord),
+            4 => Ok(FieldAccessType::QWord),
+            5 => Ok(FieldAccessType::Buffer),
+            _ => Err(AmlError::InvalidFieldFlags),
+        }
+    }
+
+    pub fn access_type_bytes(&self) -> Result<usize, AmlError> {
+        match self.access_type()? {
+            FieldAccessType::Any => {
+                // TODO: given more info about the field, we might be able to make a more efficient
+                // read, since all are valid in this case
+                Ok(1)
+            }
+            FieldAccessType::Byte | FieldAccessType::Buffer => Ok(1),
+            FieldAccessType::Word => Ok(2),
+            FieldAccessType::DWord => Ok(4),
+            FieldAccessType::QWord => Ok(8),
+        }
+    }
+
+    pub fn lock_rule(&self) -> bool {
+        self.0.get_bit(4)
+    }
+
+    pub fn update_rule(&self) -> FieldUpdateRule {
+        match self.0.get_bits(5..7) {
+            0 => FieldUpdateRule::Preserve,
+            1 => FieldUpdateRule::WriteAsOnes,
+            2 => FieldUpdateRule::WriteAsZeros,
+            _ => unreachable!(),
+        }
+    }
+}
+
 #[derive(Clone, Copy, Debug)]
 pub struct MethodFlags(pub u8);
 
@@ -206,7 +264,13 @@ pub enum ObjectType {
 /// Copy an arbitrary bit range of `src` to an arbitrary bit range of `dst`. This is used for
 /// buffer fields. Data is zero-extended if `src` does not cover `length` bits, matching the
 /// expected behaviour for buffer fields.
-fn copy_bits(src: &[u8], mut src_index: usize, dst: &mut [u8], mut dst_index: usize, mut length: usize) {
+pub(crate) fn copy_bits(
+    src: &[u8],
+    mut src_index: usize,
+    dst: &mut [u8],
+    mut dst_index: usize,
+    mut length: usize,
+) {
     while length > 0 {
         let src_shift = src_index & 7;
         let mut src_bits = src.get(src_index / 8).unwrap_or(&0x00) >> src_shift;
@@ -239,6 +303,26 @@ fn copy_bits(src: &[u8], mut src_index: usize, dst: &mut [u8], mut dst_index: us
     }
 }
 
+#[inline]
+pub(crate) fn align_down(value: usize, align: usize) -> usize {
+    assert!(align == 0 || align.is_power_of_two());
+
+    if align == 0 {
+        value
+    } else {
+        /*
+         * Alignment must be a power of two.
+         *
+         * E.g.
+         * align       =   0b00001000
+         * align-1     =   0b00000111
+         * !(align-1)  =   0b11111000
+         * ^^^ Masks the value to the one below it with the correct align
+         */
+        value & !(align - 1)
+    }
+}
+
 #[cfg(test)]
 mod tests {
     use super::*;
diff --git a/aml/src/op_region.rs b/aml/src/op_region.rs
index a2cce762..5eefb40c 100644
--- a/aml/src/op_region.rs
+++ b/aml/src/op_region.rs
@@ -1,3 +1,5 @@
+use crate::AmlError;
+
 #[derive(Debug)]
 pub struct OpRegion {
     pub space: RegionSpace,
@@ -6,7 +8,12 @@ pub struct OpRegion {
     // parent_device
 }
 
-#[derive(Clone, Copy, PartialEq, Debug)]
+pub trait RegionHandler {
+    fn read(&self, region: &OpRegion) -> Result<(), AmlError>;
+    fn write(&self, region: &OpRegion) -> Result<(), AmlError>;
+}
+
+#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)]
 pub enum RegionSpace {
     SystemMemory,
     SystemIO,
diff --git a/tests/fields.asl b/tests/fields.asl
index a43c31db..3dd2f0ed 100644
--- a/tests/fields.asl
+++ b/tests/fields.asl
@@ -1,4 +1,14 @@
 DefinitionBlock("fields.aml", "DSDT", 1, "RSACPI", "BUFFLD", 1) {
+    OperationRegion(MEM, SystemMemory, 0x40000, 0x1000)
+    Field(MEM, WordAcc, NoLock, Preserve) {
+        , 7,
+        A, 15,
+        , 8,
+        B, 1,
+        , 1,
+        C, 35
+    }
+
     OperationRegion(GIO0, SystemIO, 0x125, 0x100)
 
     Field(GIO0, ByteAcc, NoLock, Preserve) {
@@ -31,4 +41,13 @@ DefinitionBlock("fields.aml", "DSDT", 1, "RSACPI", "BUFFLD", 1) {
         , 7,
         FET4, 1
     }
+
+    Name(RESA, 0)
+    Name(RESB, 0)
+    Name(RESC, 0)
+    Method(MAIN, 0, NotSerialized) {
+        RESA = A
+        RESB = B
+        RESC = C
+    }
 }
diff --git a/tools/aml_tester/Cargo.toml b/tools/aml_tester/Cargo.toml
index f429cc75..fa621460 100644
--- a/tools/aml_tester/Cargo.toml
+++ b/tools/aml_tester/Cargo.toml
@@ -5,7 +5,7 @@ authors = ["Isaac Woods"]
 edition = "2018"
 
 [dependencies]
+aml = { path = "../../aml" }
 clap = "4"
 termion = "1"
 log = "0.4"
-aml = { path = "../../aml" }
diff --git a/tools/aml_tester/src/main.rs b/tools/aml_tester/src/main.rs
index 221c3c7d..e6a11bfe 100644
--- a/tools/aml_tester/src/main.rs
+++ b/tools/aml_tester/src/main.rs
@@ -9,7 +9,7 @@
  *      - For failing tests, print out a nice summary of the errors for each file
  */
 
-use aml::Interpreter;
+use aml::{namespace::AmlName, AmlError, Interpreter};
 use clap::{Arg, ArgAction, ArgGroup};
 use std::{
     collections::HashSet,
@@ -18,6 +18,7 @@ use std::{
     io::{Read, Write},
     path::{Path, PathBuf},
     process::Command,
+    str::FromStr,
 };
 
 enum CompilationOutcome {
@@ -53,40 +54,6 @@ fn main() -> std::io::Result<()> {
 
     let matches = cmd.get_matches();
 
-    // Get an initial list of files - may not work correctly on non-UTF8 OsString
-    let files: Vec<String> = if matches.contains_id("path") {
-        let dir_path = Path::new(matches.get_one::<String>("path").unwrap());
-
-        if std::fs::metadata(&dir_path).unwrap().is_dir() {
-            println!("Running tests in directory: {:?}", dir_path);
-            fs::read_dir(dir_path)?
-                .filter_map(|entry| {
-                    if entry.is_ok() {
-                        Some(entry.unwrap().path().to_string_lossy().to_string())
-                    } else {
-                        None
-                    }
-                })
-                .collect()
-        } else {
-            println!("Running single test: {:?}", dir_path);
-            vec![dir_path.to_string_lossy().to_string()]
-        }
-    } else {
-        matches.get_many::<String>("files").unwrap_or_default().map(|name| name.to_string()).collect()
-    };
-
-    // Make sure all files exist, propagate error if it occurs
-    files.iter().fold(Ok(()), |result: std::io::Result<()>, file| {
-        let path = Path::new(file);
-        if !path.is_file() {
-            println!("Not a regular file: {}", file);
-            // Get the io error if there is one
-            path.metadata()?;
-        }
-        result
-    })?;
-
     // Make sure we have the ability to compile ASL -> AML, if user wants it
     let user_wants_compile = !matches.get_flag("no_compile");
     let can_compile = user_wants_compile &&
@@ -99,8 +66,9 @@ fn main() -> std::io::Result<()> {
             Err(_) => false,
     };
 
+    let tests = find_tests(&matches)?;
     let compiled_files: Vec<CompilationOutcome> =
-        files.iter().map(|name| resolve_and_compile(name, can_compile).unwrap()).collect();
+        tests.iter().map(|name| resolve_and_compile(name, can_compile).unwrap()).collect();
 
     // Check if compilation should have happened but did not
     if user_wants_compile
@@ -176,7 +144,7 @@ fn main() -> std::io::Result<()> {
 
     let combined_test = matches.get_flag("combined");
 
-    let mut interpreter = Interpreter::new(Handler);
+    let mut interpreter = Interpreter::new(Handler, 2);
 
     let (passed, failed) = aml_files.into_iter().fold((0, 0), |(passed, failed), file_entry| {
         print!("Testing AML file: {:?}... ", file_entry);
@@ -189,13 +157,14 @@ fn main() -> std::io::Result<()> {
         let mut contents = Vec::new();
         file.read_to_end(&mut contents).unwrap();
 
-        const AML_TABLE_HEADER_LENGTH: usize = 36;
-
         if !combined_test {
-            interpreter = Interpreter::new(Handler);
+            interpreter = Interpreter::new(Handler, 2);
         }
 
-        match interpreter.load_table(&contents[AML_TABLE_HEADER_LENGTH..]) {
+        const AML_TABLE_HEADER_LENGTH: usize = 36;
+        let stream = &contents[AML_TABLE_HEADER_LENGTH..];
+
+        match run_test(stream, &mut interpreter) {
             Ok(()) => {
                 println!("{}OK{}", termion::color::Fg(termion::color::Green), termion::style::Reset);
                 println!("Namespace: {}", interpreter.namespace.lock());
@@ -243,16 +212,60 @@ fn main() -> std::io::Result<()> {
     Ok(())
 }
 
+fn run_test(stream: &[u8], interpreter: &mut Interpreter<Handler>) -> Result<(), AmlError> {
+    interpreter.load_table(stream)?;
+
+    match interpreter.invoke_method(AmlName::from_str("\\MAIN").unwrap(), vec![]) {
+        Ok(_) => Ok(()),
+        Err(AmlError::ObjectDoesNotExist(name)) => {
+            if name == AmlName::from_str("\\MAIN").unwrap() {
+                Ok(())
+            } else {
+                Err(AmlError::ObjectDoesNotExist(name))
+            }
+        }
+        Err(other) => Err(other),
+    }
+}
+
+fn find_tests(matches: &clap::ArgMatches) -> std::io::Result<Vec<PathBuf>> {
+    // Get an initial list of files - may not work correctly on non-UTF8 OsString
+    let files: Vec<PathBuf> = if matches.contains_id("path") {
+        let dir_path = Path::new(matches.get_one::<String>("path").unwrap());
+
+        if std::fs::metadata(&dir_path).unwrap().is_dir() {
+            println!("Running tests in directory: {:?}", dir_path);
+            fs::read_dir(dir_path)?
+                .filter_map(|entry| if entry.is_ok() { Some(entry.unwrap().path().to_path_buf()) } else { None })
+                .collect()
+        } else {
+            println!("Running single test: {:?}", dir_path);
+            vec![dir_path.to_path_buf()]
+        }
+    } else {
+        matches.get_many::<PathBuf>("files").unwrap_or_default().cloned().collect()
+    };
+
+    // Make sure all files exist, propagate error if it occurs
+    files.iter().fold(Ok(()), |result: std::io::Result<()>, path| {
+        if !path.is_file() {
+            println!("Not a regular file: {}", path.display());
+            path.metadata()?;
+        }
+        result
+    })?;
+
+    Ok(files)
+}
+
 /// Determine what to do with this file - ignore, compile and parse, or just parse.
 /// If ".aml" does not exist, or if ".asl" is newer, compiles the file.
 /// If the ".aml" file is newer, indicate it is ready to parse.
-fn resolve_and_compile(name: &str, can_compile: bool) -> std::io::Result<CompilationOutcome> {
-    let path = PathBuf::from(name);
-
+fn resolve_and_compile(path: &PathBuf, can_compile: bool) -> std::io::Result<CompilationOutcome> {
     // If this file is aml and it exists, it's ready for parsing
     // metadata() will error if the file does not exist
     if path.extension() == Some(OsStr::new("aml")) && path.metadata()?.is_file() {
-        return Ok(CompilationOutcome::IsAml(path));
+        return Ok(CompilationOutcome::IsAml(path.clone()));
     }
 
     // If this file is not asl, it's not interesting. Error if the file does not exist.
@@ -273,20 +286,20 @@ fn resolve_and_compile(name: &str, can_compile: bool) -> std::io::Result<Compila
     }
 
     if !can_compile {
-        return Ok(CompilationOutcome::NotCompiled(path));
+        return Ok(CompilationOutcome::NotCompiled(path.clone()));
     }
 
     // Compile the ASL file using `iasl`
-    println!("Compiling file: {}", name);
-    let output = Command::new("iasl").arg(name).output()?;
+    println!("Compiling file: {}", path.display());
+    let output = Command::new("iasl").arg(path).output()?;
 
     if !output.status.success() {
         println!(
             "Failed to compile ASL file: {}. Output from iasl:\n {}",
-            name,
+            path.display(),
             String::from_utf8_lossy(&output.stderr)
         );
-        Ok(CompilationOutcome::Failed(path))
+        Ok(CompilationOutcome::Failed(path.clone()))
     } else {
         Ok(CompilationOutcome::Succeeded(aml_path))
     }

From 77d446e1652172c7e16f0a666a60967c2c32fc8a Mon Sep 17 00:00:00 2001
From: Isaac Woods <isaacwoods.home@gmail.com>
Date: Mon, 10 Mar 2025 03:27:55 +0000
Subject: [PATCH 56/88] `aml`: support PCI config space field reads

---
 aml/Cargo.toml               |   2 +
 aml/src/lib.rs               | 100 ++++++++++++++++++++++++++++-------
 aml/src/op_region.rs         |   4 +-
 tools/aml_tester/src/main.rs |  26 ++++-----
 4 files changed, 97 insertions(+), 35 deletions(-)

diff --git a/aml/Cargo.toml b/aml/Cargo.toml
index afd1e859..dedbe54f 100644
--- a/aml/Cargo.toml
+++ b/aml/Cargo.toml
@@ -13,3 +13,5 @@ license = "MIT/Apache-2.0"
 spinning_top = "0.3.0"
 bit_field = "0.10.2"
 byteorder = { version = "1.5.0", default-features = false }
+log = "0.4"
+pci_types = "0.10.0"
diff --git a/aml/src/lib.rs b/aml/src/lib.rs
index 4ebb348c..de176fc8 100644
--- a/aml/src/lib.rs
+++ b/aml/src/lib.rs
@@ -9,6 +9,8 @@ pub mod op_region;
 pub mod pci_routing;
 pub mod resource;
 
+pub use pci_types::PciAddress;
+
 use alloc::{
     boxed::Box,
     collections::btree_map::BTreeMap,
@@ -82,6 +84,24 @@ where
         }
     }
 
+    pub fn invoke_method_if_present(
+        &self,
+        path: AmlName,
+        args: Vec<Arc<Object>>,
+    ) -> Result<Option<Arc<Object>>, AmlError> {
+        match self.invoke_method(path.clone(), args) {
+            Ok(result) => Ok(Some(result)),
+            Err(AmlError::ObjectDoesNotExist(not_present)) => {
+                if path == not_present {
+                    Ok(None)
+                } else {
+                    Err(AmlError::ObjectDoesNotExist(not_present))
+                }
+            }
+            Err(other) => Err(other),
+        }
+    }
+
     pub fn install_region_handler<RH>(&self, space: RegionSpace, handler: RH)
     where
         RH: RegionHandler + 'static,
@@ -200,14 +220,13 @@ where
                         else {
                             panic!()
                         };
-                        let region_offset = region_offset.as_integer()?;
-                        let region_length = region_length.as_integer()?;
-                        let region_space = RegionSpace::from(*region_space);
 
                         let region = Object::OpRegion(OpRegion {
-                            space: region_space,
-                            base: region_offset,
-                            length: region_length,
+                            space: RegionSpace::from(*region_space),
+                            base: region_offset.as_integer()?,
+                            length: region_length.as_integer()?,
+                            parent_device_path: context.current_scope.clone(),
+                        });
                         });
                         self.namespace.lock().insert(name.resolve(&context.current_scope)?, Arc::new(region))?;
                     }
@@ -1493,7 +1512,45 @@ where
                     _ => panic!(),
                 }
             }),
-            RegionSpace::PciConfig => todo!(),
+            RegionSpace::PciConfig => {
+                /*
+                 * TODO: it's not ideal to do these reads for every native access. See if we can
+                 * cache them somewhere?
+                 */
+                let seg = match self.invoke_method_if_present(
+                    AmlName::from_str("_SEG").unwrap().resolve(&region.parent_device_path)?,
+                    vec![],
+                )? {
+                    Some(value) => value.as_integer()?,
+                    None => 0,
+                };
+                let bus = match self.invoke_method_if_present(
+                    AmlName::from_str("_BBR").unwrap().resolve(&region.parent_device_path)?,
+                    vec![],
+                )? {
+                    Some(value) => value.as_integer()?,
+                    None => 0,
+                };
+                let (device, function) = {
+                    let adr = self.invoke_method_if_present(
+                        AmlName::from_str("_ADR").unwrap().resolve(&region.parent_device_path)?,
+                        vec![],
+                    )?;
+                    let adr = match adr {
+                        Some(adr) => adr.as_integer()?,
+                        None => 0,
+                    };
+                    (adr.get_bits(16..32), adr.get_bits(0..16))
+                };
+
+                let address = PciAddress::new(seg as u16, bus as u8, device as u8, function as u8);
+                match length {
+                    1 => Ok(self.handler.read_pci_u8(address, offset as u16) as u64),
+                    2 => Ok(self.handler.read_pci_u16(address, offset as u16) as u64),
+                    4 => Ok(self.handler.read_pci_u32(address, offset as u16) as u64),
+                    _ => panic!(),
+                }
+            }
 
             RegionSpace::EmbeddedControl
             | RegionSpace::SmBus
@@ -2086,7 +2143,10 @@ pub enum AmlError {
 
 /// This trait represents the interface from the `Interpreter` to the hosting kernel, and allows
 /// AML to interact with the underlying hardware.
-// TODO: maybe use `pci_types::PciAddress` to simplify PCI address passing here
+///
+/// ### Implementation notes
+/// Reads and writes to PCI devices must succeed for devices that are not detected during
+/// enumeration of the PCI bus / do not exist.
 pub trait Handler: Send + Sync {
     fn read_u8(&self, address: usize) -> u8;
     fn read_u16(&self, address: usize) -> u16;
@@ -2106,13 +2166,13 @@ pub trait Handler: Send + Sync {
     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 read_pci_u8(&self, address: PciAddress, offset: u16) -> u8;
+    fn read_pci_u16(&self, address: PciAddress, offset: u16) -> u16;
+    fn read_pci_u32(&self, address: PciAddress, 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);
+    fn write_pci_u8(&self, address: PciAddress, offset: u16, value: u8);
+    fn write_pci_u16(&self, address: PciAddress, offset: u16, value: u16);
+    fn write_pci_u32(&self, address: PciAddress, offset: u16, value: u32);
 
     /// Returns a monotonically-increasing value of nanoseconds.
     fn nanos_since_boot(&self) -> u64;
@@ -2160,12 +2220,12 @@ mod tests {
         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 {0}
-        fn read_pci_u16(&self, _segment: u16, _bus: u8, _device: u8, _function: u8, _offset: u16) -> u16 {0}
-        fn read_pci_u32(&self, _segment: u16, _bus: u8, _device: u8, _function: u8, _offset: u16) -> u32 {0}
-        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) {}
+        fn read_pci_u8(&self, _address: PciAddress, _offset: u16) -> u8 {0}
+        fn read_pci_u16(&self, _address: PciAddress, _offset: u16) -> u16 {0}
+        fn read_pci_u32(&self, _address: PciAddress, _offset: u16) -> u32 {0}
+        fn write_pci_u8(&self, _address: PciAddress, _offset: u16, _value: u8) {}
+        fn write_pci_u16(&self, _address: PciAddress, _offset: u16, _value: u16) {}
+        fn write_pci_u32(&self, _address: PciAddress, _offset: u16, _value: u32) {}
         fn nanos_since_boot(&self) -> u64 {0}
         fn stall(&self, _microseconds: u64) {}
         fn sleep(&self, _milliseconds: u64) {}
diff --git a/aml/src/op_region.rs b/aml/src/op_region.rs
index 5eefb40c..14bb533f 100644
--- a/aml/src/op_region.rs
+++ b/aml/src/op_region.rs
@@ -1,11 +1,11 @@
-use crate::AmlError;
+use crate::{AmlError, namespace::AmlName};
 
 #[derive(Debug)]
 pub struct OpRegion {
     pub space: RegionSpace,
     pub base: u64,
     pub length: u64,
-    // parent_device
+    pub parent_device_path: AmlName,
 }
 
 pub trait RegionHandler {
diff --git a/tools/aml_tester/src/main.rs b/tools/aml_tester/src/main.rs
index e6a11bfe..1b62261a 100644
--- a/tools/aml_tester/src/main.rs
+++ b/tools/aml_tester/src/main.rs
@@ -9,7 +9,7 @@
  *      - For failing tests, print out a nice summary of the errors for each file
  */
 
-use aml::{namespace::AmlName, AmlError, Interpreter};
+use aml::{namespace::AmlName, AmlError, Interpreter, PciAddress};
 use clap::{Arg, ArgAction, ArgGroup};
 use std::{
     collections::HashSet,
@@ -377,27 +377,27 @@ impl aml::Handler for Handler {
         println!("write_io_u32 {port:#x}<-{value:#x}");
     }
 
-    fn read_pci_u8(&self, segment: u16, bus: u8, device: u8, function: u8, _offset: u16) -> u8 {
-        println!("read_pci_u8 ({segment:#x}, {bus:#x}, {device:#x}, {function:#x})");
+    fn read_pci_u8(&self, address: PciAddress, _offset: u16) -> u8 {
+        println!("read_pci_u8 ({address})");
         0
     }
-    fn read_pci_u16(&self, segment: u16, bus: u8, device: u8, function: u8, _offset: u16) -> u16 {
-        println!("read_pci_u16 ({segment:#x}, {bus:#x}, {device:#x}, {function:#x})");
+    fn read_pci_u16(&self, address: PciAddress, _offset: u16) -> u16 {
+        println!("read_pci_u16 ({address})");
         0
     }
-    fn read_pci_u32(&self, segment: u16, bus: u8, device: u8, function: u8, _offset: u16) -> u32 {
-        println!("read_pci_32 ({segment:#x}, {bus:#x}, {device:#x}, {function:#x})");
+    fn read_pci_u32(&self, address: PciAddress, _offset: u16) -> u32 {
+        println!("read_pci_u32 ({address})");
         0
     }
 
-    fn write_pci_u8(&self, segment: u16, bus: u8, device: u8, function: u8, _offset: u16, value: u8) {
-        println!("write_pci_u8 ({segment:#x}, {bus:#x}, {device:#x}, {function:#x})<-{value:#x}");
+    fn write_pci_u8(&self, address: PciAddress, _offset: u16, value: u8) {
+        println!("write_pci_u8 ({address})<-{value}");
     }
-    fn write_pci_u16(&self, segment: u16, bus: u8, device: u8, function: u8, _offset: u16, value: u16) {
-        println!("write_pci_u16 ({segment:#x}, {bus:#x}, {device:#x}, {function:#x})<-{value:#x}");
+    fn write_pci_u16(&self, address: PciAddress, _offset: u16, value: u16) {
+        println!("write_pci_u16 ({address})<-{value}");
     }
-    fn write_pci_u32(&self, segment: u16, bus: u8, device: u8, function: u8, _offset: u16, value: u32) {
-        println!("write_pci_u32 ({segment:#x}, {bus:#x}, {device:#x}, {function:#x})<-{value:#x}");
+    fn write_pci_u32(&self, address: PciAddress, _offset: u16, value: u32) {
+        println!("write_pci_u32 ({address})<-{value}");
     }
 
     fn nanos_since_boot(&self) -> u64 {

From df5778a0bb11b1c83ba1ce0c3c332e0a7ee430a9 Mon Sep 17 00:00:00 2001
From: Isaac Woods <isaacwoods.home@gmail.com>
Date: Mon, 10 Mar 2025 20:30:19 +0000
Subject: [PATCH 57/88] `aml`: implement `DefConcat` and `DefConcatRes`

---
 aml/src/lib.rs    | 79 ++++++++++++++++++++++++++++++++++++++++++++++-
 aml/src/object.rs | 46 ++++++++++++++++++++++++++-
 2 files changed, 123 insertions(+), 2 deletions(-)

diff --git a/aml/src/lib.rs b/aml/src/lib.rs
index de176fc8..3cb0f0e9 100644
--- a/aml/src/lib.rs
+++ b/aml/src/lib.rs
@@ -191,6 +191,27 @@ where
                         self.do_logical_op(&mut context, op)?;
                     }
                     Opcode::Mid => self.do_mid(&mut context, op)?,
+                    Opcode::Concat => self.do_concat(&mut context, op)?,
+                    Opcode::ConcatRes => {
+                        let [Argument::Object(source1), Argument::Object(source2), target] = &op.arguments[..]
+                        else {
+                            panic!()
+                        };
+                        let source1 = source1.as_buffer()?;
+                        let source2 = source2.as_buffer()?;
+                        let result = {
+                            let mut buffer = Vec::from(source1);
+                            buffer.extend_from_slice(source2);
+                            // Add a new end-tag
+                            buffer.push(0x78);
+                            // Don't calculate the new real checksum - just use 0
+                            buffer.push(0x00);
+                            Arc::new(Object::Buffer(buffer))
+                        };
+                        // TODO: use potentially-updated result for return value here
+                        self.do_store(&mut context, target, result.clone())?;
+                        context.contribute_arg(Argument::Object(result));
+                    }
                     Opcode::FromBCD => self.do_from_bcd(&mut context, op)?,
                     Opcode::ToBCD => self.do_to_bcd(&mut context, op)?,
                     Opcode::Name => {
@@ -914,8 +935,8 @@ where
                     context.start_in_flight_op(OpInFlight::new(opcode, 2))
                 }
                 Opcode::DerefOf => todo!(),
-                Opcode::ConcatRes => todo!(),
                 Opcode::Notify => todo!(),
+                Opcode::ConcatRes => context.start_in_flight_op(OpInFlight::new(opcode, 3)),
                 Opcode::SizeOf => context.start_in_flight_op(OpInFlight::new(opcode, 1)),
                 Opcode::Index => context.start_in_flight_op(OpInFlight::new(opcode, 3)),
                 Opcode::Match => todo!(),
@@ -1263,6 +1284,62 @@ where
         Ok(())
     }
 
+    fn do_concat(&self, context: &mut MethodContext, op: OpInFlight) -> Result<(), AmlError> {
+        let [Argument::Object(source1), Argument::Object(source2), target] = &op.arguments[..] else { panic!() };
+        fn resolve_as_string(obj: &Object) -> String {
+            match obj {
+                Object::Uninitialized => "[Uninitialized Object]".to_string(),
+                Object::Buffer(bytes) => String::from_utf8_lossy(&bytes).into_owned(),
+                Object::BufferField { .. } => "[Buffer Field]".to_string(),
+                Object::Device => "[Device]".to_string(),
+                Object::Event => "[Event]".to_string(),
+                Object::FieldUnit(_) => "[Field]".to_string(),
+                Object::Integer(value) => value.to_string(),
+                Object::Method { .. } => "[Control Method]".to_string(),
+                Object::Mutex { .. } => "[Mutex]".to_string(),
+                Object::Reference { inner, .. } => resolve_as_string(&*(inner.clone().unwrap_reference())),
+                Object::OpRegion(_) => "[Operation Region]".to_string(),
+                Object::Package(_) => "[Package]".to_string(),
+                Object::PowerResource { .. } => "[Power Resource]".to_string(),
+                Object::Processor { .. } => "[Processor]".to_string(),
+                // TODO: what even is one of these??
+                Object::RawDataBuffer => todo!(),
+                Object::String(value) => value.clone(),
+                Object::ThermalZone => "[Thermal Zone]".to_string(),
+                Object::Debug => "[Debug Object]".to_string(),
+            }
+        }
+        let result = match source1.typ() {
+            ObjectType::Integer => {
+                let source1 = source1.as_integer()?;
+                let source2 = source2.to_integer(if self.dsdt_revision >= 2 { 8 } else { 4 })?;
+                let mut buffer = Vec::new();
+                if self.dsdt_revision >= 2 {
+                    buffer.extend_from_slice(&source1.to_le_bytes());
+                    buffer.extend_from_slice(&source2.to_le_bytes());
+                } else {
+                    buffer.extend_from_slice(&(source1 as u32).to_le_bytes());
+                    buffer.extend_from_slice(&(source2 as u32).to_le_bytes());
+                }
+                Arc::new(Object::Buffer(buffer))
+            }
+            ObjectType::Buffer => {
+                let mut buffer = source1.as_buffer()?.to_vec();
+                buffer.extend(source2.to_buffer(if self.dsdt_revision >= 2 { 8 } else { 4 })?);
+                Arc::new(Object::Buffer(buffer))
+            }
+            ObjectType::String | _ => {
+                let source1 = resolve_as_string(&source1);
+                let source2 = resolve_as_string(&source2);
+                Arc::new(Object::String(source1 + &source2))
+            }
+        };
+        // TODO: use result of store
+        self.do_store(context, target, result.clone())?;
+        context.contribute_arg(Argument::Object(result));
+        Ok(())
+    }
+
     fn do_from_bcd(&self, context: &mut MethodContext, op: OpInFlight) -> Result<(), AmlError> {
         let [Argument::Object(value)] = &op.arguments[..] else { Err(AmlError::InvalidOperationOnObject)? };
         let mut value = value.clone().unwrap_transparent_reference().as_integer()?;
diff --git a/aml/src/object.rs b/aml/src/object.rs
index 224aa60d..3b2bac7f 100644
--- a/aml/src/object.rs
+++ b/aml/src/object.rs
@@ -1,5 +1,5 @@
 use crate::{AmlError, op_region::OpRegion};
-use alloc::{string::String, sync::Arc, vec::Vec};
+use alloc::{borrow::Cow, string::String, sync::Arc, vec::Vec};
 use bit_field::BitField;
 
 #[derive(Debug)]
@@ -52,6 +52,50 @@ impl Object {
         }
     }
 
+    pub fn as_string(&self) -> Result<Cow<str>, AmlError> {
+        if let Object::String(value) = self {
+            Ok(Cow::from(value))
+        } else {
+            Err(AmlError::ObjectNotOfExpectedType { expected: ObjectType::String, got: self.typ() })
+        }
+    }
+
+    pub fn as_buffer(&self) -> Result<&[u8], AmlError> {
+        if let Object::Buffer(bytes) = self {
+            Ok(bytes)
+        } else {
+            Err(AmlError::ObjectNotOfExpectedType { expected: ObjectType::Buffer, got: self.typ() })
+        }
+    }
+
+    pub fn to_integer(&self, allowed_bytes: usize) -> Result<u64, AmlError> {
+        match self {
+            Object::Integer(value) => Ok(*value),
+            Object::Buffer(value) => {
+                let length = usize::min(value.len(), allowed_bytes);
+                let mut bytes = [0u8; 8];
+                bytes[0..length].copy_from_slice(&value[0..length]);
+                Ok(u64::from_le_bytes(bytes))
+            }
+            // TODO: how should we handle invalid inputs? What does NT do here?
+            Object::String(value) => Ok(value.parse::<u64>().unwrap_or(0)),
+            _ => Ok(0),
+        }
+    }
+
+    pub fn to_buffer(&self, allowed_bytes: usize) -> Result<Vec<u8>, AmlError> {
+        match self {
+            Object::Buffer(bytes) => Ok(bytes.clone()),
+            Object::Integer(value) => match allowed_bytes {
+                4 => Ok((*value as u32).to_le_bytes().to_vec()),
+                8 => Ok(value.to_le_bytes().to_vec()),
+                _ => panic!(),
+            },
+            Object::String(value) => Ok(value.as_bytes().to_vec()),
+            _ => Err(AmlError::InvalidOperationOnObject),
+        }
+    }
+
     pub fn read_buffer_field(&self, dst: &mut [u8]) -> Result<(), AmlError> {
         if let Self::BufferField { buffer, offset, length } = self {
             let buffer = match **buffer {

From 5ef786004413d2776dd6f24fa3b028fb35300f0d Mon Sep 17 00:00:00 2001
From: Isaac Woods <isaacwoods.home@gmail.com>
Date: Thu, 13 Mar 2025 14:09:48 +0000
Subject: [PATCH 58/88] `aml`: fix behaviour of binary ops

Previous code did not correctly handle stores and had a bug in argument
extraction somehow.
---
 aml/src/lib.rs | 21 +++++++--------------
 1 file changed, 7 insertions(+), 14 deletions(-)

diff --git a/aml/src/lib.rs b/aml/src/lib.rs
index 3cb0f0e9..a42a50c4 100644
--- a/aml/src/lib.rs
+++ b/aml/src/lib.rs
@@ -1092,27 +1092,19 @@ where
     }
 
     fn do_binary_maths(&self, context: &mut MethodContext, op: OpInFlight) -> Result<(), AmlError> {
-        let [Argument::Object(left), Argument::Object(right), Argument::Object(target)] = &op.arguments[0..2]
-        else {
-            Err(AmlError::InvalidOperationOnObject)?
-        };
-        let target2 = if op.op == Opcode::Divide {
-            let Argument::Object(target2) = &op.arguments[3] else { panic!() };
-            Some(target2)
-        } else {
-            None
-        };
+        let [Argument::Object(left), Argument::Object(right), target] = &op.arguments[0..3] else { panic!() };
+        let target2 = if op.op == Opcode::Divide { Some(&op.arguments[3]) } else { None };
 
         let left = left.clone().unwrap_transparent_reference().as_integer()?;
         let right = right.clone().unwrap_transparent_reference().as_integer()?;
 
-        let value = match op.op {
+        let result = match op.op {
             Opcode::Add => left.wrapping_add(right),
             Opcode::Subtract => left.wrapping_sub(right),
             Opcode::Multiply => left.wrapping_mul(right),
             Opcode::Divide => {
                 if let Some(remainder) = target2 {
-                    *remainder.gain_mut() = Object::Integer(left.wrapping_rem(right));
+                    self.do_store(context, remainder, Arc::new(Object::Integer(left.wrapping_rem(right))))?;
                 }
                 left.wrapping_div_euclid(right)
             }
@@ -1127,8 +1119,9 @@ where
             _ => panic!(),
         };
 
-        *target.gain_mut() = Object::Integer(value);
-        context.contribute_arg(Argument::Object(Arc::new(Object::Integer(value))));
+        let result = Arc::new(Object::Integer(result));
+        self.do_store(context, target, result.clone())?;
+        context.contribute_arg(Argument::Object(result));
         Ok(())
     }
 

From bb3107a95efffcfb1500c73e47b65af4335bb51c Mon Sep 17 00:00:00 2001
From: Isaac Woods <isaacwoods.home@gmail.com>
Date: Thu, 13 Mar 2025 16:49:20 +0000
Subject: [PATCH 59/88] `aml`: make `InvalidOperationOnObject` error more
 useful

---
 aml/src/lib.rs         | 39 +++++++++++++++++++++++++++------------
 aml/src/object.rs      |  8 ++++----
 aml/src/pci_routing.rs |  5 +++--
 aml/src/resource.rs    |  4 ++--
 4 files changed, 36 insertions(+), 20 deletions(-)

diff --git a/aml/src/lib.rs b/aml/src/lib.rs
index a42a50c4..93aff257 100644
--- a/aml/src/lib.rs
+++ b/aml/src/lib.rs
@@ -1120,13 +1120,14 @@ where
         };
 
         let result = Arc::new(Object::Integer(result));
+        // TODO: use result for arg
         self.do_store(context, target, result.clone())?;
         context.contribute_arg(Argument::Object(result));
         Ok(())
     }
 
     fn do_unary_maths(&self, context: &mut MethodContext, op: OpInFlight) -> Result<(), AmlError> {
-        let [Argument::Object(operand)] = &op.arguments[..] else { Err(AmlError::InvalidOperationOnObject)? };
+        let [Argument::Object(operand)] = &op.arguments[..] else { panic!() };
         let operand = operand.clone().unwrap_transparent_reference().as_integer()?;
 
         let result = match op.op {
@@ -1137,6 +1138,7 @@ where
                     /*
                      * TODO: this is a particular instance where not respecting integers being
                      * 32-bit on revision 1 tables does cause properly incorrect behaviour...
+                     * TODO: we can fix this now we have the DSDT revision
                      */
                     (operand.leading_zeros() + 1) as u64
                 }
@@ -1164,7 +1166,7 @@ where
 
     fn do_logical_op(&self, context: &mut MethodContext, op: OpInFlight) -> Result<(), AmlError> {
         if op.op == Opcode::LNot {
-            let [Argument::Object(operand)] = &op.arguments[..] else { Err(AmlError::InvalidOperationOnObject)? };
+            let [Argument::Object(operand)] = &op.arguments[..] else { panic!() };
             let operand = operand.clone().unwrap_transparent_reference().as_integer()?;
             let result = if operand == 0 { u64::MAX } else { 0 };
 
@@ -1177,9 +1179,7 @@ where
             return Ok(());
         }
 
-        let [Argument::Object(left), Argument::Object(right)] = &op.arguments[..] else {
-            Err(AmlError::InvalidOperationOnObject)?
-        };
+        let [Argument::Object(left), Argument::Object(right)] = &op.arguments[..] else { panic!() };
 
         /*
          * Some of these operations allow strings and buffers to be used as operands. Apparently
@@ -1220,7 +1220,7 @@ where
                 };
                 (left, right)
             }
-            _ => panic!(),
+            _ => Err(AmlError::InvalidOperationOnObject { op: Operation::LogicalOp, typ: left.typ() })?,
         };
 
         let result = match op.op {
@@ -1268,7 +1268,7 @@ where
                     Object::Buffer(bytes.to_vec())
                 }
             }
-            _ => Err(AmlError::InvalidOperationOnObject)?,
+            _ => Err(AmlError::InvalidOperationOnObject { op: Operation::Mid, typ: source.typ() })?,
         });
 
         self.do_store(context, target, result.clone())?;
@@ -1334,7 +1334,7 @@ where
     }
 
     fn do_from_bcd(&self, context: &mut MethodContext, op: OpInFlight) -> Result<(), AmlError> {
-        let [Argument::Object(value)] = &op.arguments[..] else { Err(AmlError::InvalidOperationOnObject)? };
+        let [Argument::Object(value)] = &op.arguments[..] else { panic!() };
         let mut value = value.clone().unwrap_transparent_reference().as_integer()?;
 
         let mut result = 0;
@@ -1350,7 +1350,7 @@ where
     }
 
     fn do_to_bcd(&self, context: &mut MethodContext, op: OpInFlight) -> Result<(), AmlError> {
-        let [Argument::Object(value)] = &op.arguments[..] else { Err(AmlError::InvalidOperationOnObject)? };
+        let [Argument::Object(value)] = &op.arguments[..] else { panic!() };
         let mut value = value.clone().unwrap_transparent_reference().as_integer()?;
 
         let mut result = 0;
@@ -1366,14 +1366,14 @@ where
     }
 
     fn do_size_of(&self, context: &mut MethodContext, op: OpInFlight) -> Result<(), AmlError> {
-        let [Argument::Object(object)] = &op.arguments[..] else { Err(AmlError::InvalidOperationOnObject)? };
+        let [Argument::Object(object)] = &op.arguments[..] else { panic!() };
         let object = object.clone().unwrap_reference();
 
         let result = match *object {
             Object::Buffer(ref buffer) => buffer.len(),
             Object::String(ref str) => str.len(),
             Object::Package(ref package) => package.len(),
-            _ => Err(AmlError::InvalidOperationOnObject)?,
+            _ => Err(AmlError::InvalidOperationOnObject { op: Operation::SizeOf, typ: object.typ() })?,
         };
 
         context.contribute_arg(Argument::Object(Arc::new(Object::Integer(result as u64))));
@@ -2171,6 +2171,21 @@ enum Opcode {
     InternalMethodCall,
 }
 
+#[derive(Clone, Copy, PartialEq, Debug)]
+pub enum Operation {
+    Mid,
+    SizeOf,
+    Acquire,
+    Release,
+    ConvertToBuffer,
+
+    ReadBufferField,
+    WriteBufferField,
+    LogicalOp,
+    DecodePrt,
+    ParseResource,
+}
+
 /*
  * TODO: not sure if we should use a better error reporting system or just keep a giant enum?
  */
@@ -2197,7 +2212,7 @@ pub enum AmlError {
 
     MethodArgCountIncorrect,
 
-    InvalidOperationOnObject,
+    InvalidOperationOnObject { op: Operation, typ: ObjectType },
     IndexOutOfBounds,
     ObjectNotOfExpectedType { expected: ObjectType, got: ObjectType },
 
diff --git a/aml/src/object.rs b/aml/src/object.rs
index 3b2bac7f..0b25b629 100644
--- a/aml/src/object.rs
+++ b/aml/src/object.rs
@@ -1,4 +1,4 @@
-use crate::{AmlError, op_region::OpRegion};
+use crate::{AmlError, Handle, Operation, op_region::OpRegion};
 use alloc::{borrow::Cow, string::String, sync::Arc, vec::Vec};
 use bit_field::BitField;
 
@@ -92,7 +92,7 @@ impl Object {
                 _ => panic!(),
             },
             Object::String(value) => Ok(value.as_bytes().to_vec()),
-            _ => Err(AmlError::InvalidOperationOnObject),
+            _ => Err(AmlError::InvalidOperationOnObject { op: Operation::ConvertToBuffer, typ: self.typ() }),
         }
     }
 
@@ -107,7 +107,7 @@ impl Object {
             copy_bits(buffer, *offset, dst, 0, *length);
             Ok(())
         } else {
-            Err(AmlError::InvalidOperationOnObject)
+            Err(AmlError::InvalidOperationOnObject { op: Operation::ReadBufferField, typ: self.typ() })
         }
     }
 
@@ -124,7 +124,7 @@ impl Object {
             copy_bits(value, 0, buffer, *offset, *length);
             Ok(())
         } else {
-            Err(AmlError::InvalidOperationOnObject)
+            Err(AmlError::InvalidOperationOnObject { op: Operation::WriteBufferField, typ: self.typ() })
         }
     }
 
diff --git a/aml/src/pci_routing.rs b/aml/src/pci_routing.rs
index 2302a9dd..b0b52520 100644
--- a/aml/src/pci_routing.rs
+++ b/aml/src/pci_routing.rs
@@ -2,6 +2,7 @@ use crate::{
     AmlError,
     Handler,
     Interpreter,
+    Operation,
     namespace::AmlName,
     object::{Object, ReferenceKind},
     resource::{self, InterruptPolarity, InterruptTrigger, Resource},
@@ -136,13 +137,13 @@ impl PciRoutingTable {
                         _ => return Err(AmlError::PrtInvalidSource),
                     }
                 } else {
-                    return Err(AmlError::InvalidOperationOnObject);
+                    return Err(AmlError::InvalidOperationOnObject { op: Operation::DecodePrt, typ: value.typ() });
                 }
             }
 
             Ok(PciRoutingTable { entries })
         } else {
-            Err(AmlError::InvalidOperationOnObject)
+            return Err(AmlError::InvalidOperationOnObject { op: Operation::DecodePrt, typ: prt.typ() });
         }
     }
 
diff --git a/aml/src/resource.rs b/aml/src/resource.rs
index 60e27def..51f356a2 100644
--- a/aml/src/resource.rs
+++ b/aml/src/resource.rs
@@ -1,4 +1,4 @@
-use crate::{AmlError, object::Object};
+use crate::{AmlError, Operation, object::Object};
 use alloc::{sync::Arc, vec::Vec};
 use bit_field::BitField;
 use byteorder::{ByteOrder, LittleEndian};
@@ -32,7 +32,7 @@ pub fn resource_descriptor_list(descriptor: Arc<Object>) -> Result<Vec<Resource>
 
         Ok(descriptors)
     } else {
-        Err(AmlError::InvalidOperationOnObject)
+        Err(AmlError::InvalidOperationOnObject { op: Operation::ParseResource, typ: descriptor.typ() })
     }
 }
 

From b8e382d6fa94b204e2de27bb6caed59fc4dfa1c5 Mon Sep 17 00:00:00 2001
From: Isaac Woods <isaacwoods.home@gmail.com>
Date: Thu, 13 Mar 2025 16:54:27 +0000
Subject: [PATCH 60/88] `aml`: initial implementation of mutexes, `DefAcquire`,
 `DefRelease`

---
 aml/src/lib.rs               | 76 ++++++++++++++++++++++++++++++------
 aml/src/object.rs            |  2 +-
 tools/aml_tester/Cargo.toml  |  1 +
 tools/aml_tester/src/main.rs | 19 ++++++---
 4 files changed, 79 insertions(+), 19 deletions(-)

diff --git a/aml/src/lib.rs b/aml/src/lib.rs
index 93aff257..65097640 100644
--- a/aml/src/lib.rs
+++ b/aml/src/lib.rs
@@ -20,8 +20,11 @@ use alloc::{
     vec::Vec,
 };
 use bit_field::BitField;
-use core::mem;
-use log::{info, trace};
+use core::{
+    mem,
+    str::FromStr,
+};
+use log::{info, trace, warn};
 use namespace::{AmlName, Namespace, NamespaceLevelKind};
 use object::{FieldFlags, FieldUnit, FieldUnitKind, MethodFlags, Object, ObjectType, ReferenceKind};
 use op_region::{OpRegion, RegionHandler, RegionSpace};
@@ -397,6 +400,24 @@ where
                         let [Argument::Object(usec)] = &op.arguments[..] else { panic!() };
                         self.handler.stall(usec.as_integer()?);
                     }
+                    Opcode::Acquire => {
+                        let [Argument::Object(mutex)] = &op.arguments[..] else { panic!() };
+                        let Object::Mutex { mutex, sync_level } = **mutex else {
+                            Err(AmlError::InvalidOperationOnObject { op: Operation::Acquire, typ: mutex.typ() })?
+                        };
+                        let timeout = context.next_u16()?;
+
+                        // TODO: should we do something with the sync level??
+                        self.handler.acquire(mutex, timeout)?;
+                    }
+                    Opcode::Release => {
+                        let [Argument::Object(mutex)] = &op.arguments[..] else { panic!() };
+                        let Object::Mutex { mutex, sync_level } = **mutex else {
+                            Err(AmlError::InvalidOperationOnObject { op: Operation::Release, typ: mutex.typ() })?
+                        };
+                        // TODO: should we do something with the sync level??
+                        self.handler.release(mutex);
+                    }
                     Opcode::InternalMethodCall => {
                         let [Argument::Object(method), Argument::Namestring(method_scope)] = &op.arguments[0..2]
                         else {
@@ -711,7 +732,8 @@ where
                     let sync_level = context.next()?;
 
                     let name = name.resolve(&context.current_scope)?;
-                    self.namespace.lock().insert(name, Arc::new(Object::Mutex { sync_level }))?;
+                    let mutex = self.handler.create_mutex();
+                    self.namespace.lock().insert(name, Arc::new(Object::Mutex { mutex, sync_level }))?;
                 }
                 Opcode::Event => {
                     let name = context.namestring()?;
@@ -723,11 +745,11 @@ where
                 Opcode::Load => todo!(),
                 Opcode::Stall => context.start_in_flight_op(OpInFlight::new(Opcode::Stall, 1)),
                 Opcode::Sleep => context.start_in_flight_op(OpInFlight::new(Opcode::Sleep, 1)),
-                Opcode::Acquire => todo!(),
+                Opcode::Acquire => context.start_in_flight_op(OpInFlight::new(opcode, 1)),
+                Opcode::Release => context.start_in_flight_op(OpInFlight::new(opcode, 1)),
                 Opcode::Signal => todo!(),
                 Opcode::Wait => todo!(),
                 Opcode::Reset => todo!(),
-                Opcode::Release => todo!(),
                 Opcode::FromBCD | Opcode::ToBCD => context.start_in_flight_op(OpInFlight::new(opcode, 2)),
                 Opcode::Revision => {
                     context.contribute_arg(Argument::Object(Arc::new(Object::Integer(INTERPRETER_REVISION))));
@@ -2219,6 +2241,8 @@ pub enum AmlError {
     InvalidResourceDescriptor,
     UnexpectedResourceType,
 
+    MutexAquireTimeout,
+
     PrtInvalidAddress,
     PrtInvalidPin,
     PrtInvalidGsi,
@@ -2226,6 +2250,16 @@ pub enum AmlError {
     PrtNoEntry,
 }
 
+/// A `Handle` is an opaque reference to an object that is managed by the user of this library.
+/// They should be returned by the `create_*` methods on `Handler`, and are then used by methods to
+/// refer to a specific object.
+///
+/// The library will treat the value of a handle as entirely opaque. You may manage handles
+/// however you wish, and the same value can be used to refer to objects of different types, if
+/// desired.
+#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
+pub struct Handle(pub u32);
+
 /// This trait represents the interface from the `Interpreter` to the hosting kernel, and allows
 /// AML to interact with the underlying hardware.
 ///
@@ -2238,10 +2272,10 @@ pub trait Handler: Send + Sync {
     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 write_u8(&self, address: usize, value: u8);
+    fn write_u16(&self, address: usize, value: u16);
+    fn write_u32(&self, address: usize, value: u32);
+    fn write_u64(&self, address: usize, value: u64);
 
     fn read_io_u8(&self, port: u16) -> u8;
     fn read_io_u16(&self, port: u16) -> u16;
@@ -2271,6 +2305,19 @@ pub trait Handler: Send + Sync {
     /// time supported, and should relinquish the processor.
     fn sleep(&self, milliseconds: u64);
 
+    fn create_mutex(&self) -> Handle;
+
+    /// Acquire the mutex referred to by the given handle. `timeout` is a millisecond timeout value
+    /// with the following meaning:
+    ///    - `0` - try to acquire the mutex once, in a non-blocking manner. If the mutex cannot be
+    ///      acquired immediately, return `Err(AmlError::MutexAquireTimeout)`
+    ///    - `1-0xfffe` - try to acquire the mutex for at least `timeout` milliseconds.
+    ///    - `0xffff` - try to acquire the mutex indefinitely. Should not return `MutexAquireTimeout`.
+    ///
+    /// AML mutexes are **reentrant** - that is, a thread may acquire the same mutex more than once
+    /// without causing a deadlock.
+    fn acquire(&self, mutex: Handle, timeout: u16) -> Result<(), AmlError>;
+    fn release(&self, mutex: Handle);
     fn breakpoint(&self) {}
 
     fn handle_debug(&self, _object: &Object) {}
@@ -2295,10 +2342,10 @@ mod tests {
         fn read_u16(&self, _address: usize) -> u16 {0}
         fn read_u32(&self, _address: usize) -> u32 {0}
         fn read_u64(&self, _address: usize) -> u64 {0}
-        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 write_u8(&self, _address: usize, _value: u8) {}
+        fn write_u16(&self, _address: usize, _value: u16) {}
+        fn write_u32(&self, _address: usize, _value: u32) {}
+        fn write_u64(&self, _address: usize, _value: u64) {}
         fn read_io_u8(&self, _port: u16) -> u8 {0}
         fn read_io_u16(&self, _port: u16) -> u16 {0}
         fn read_io_u32(&self, _port: u16) -> u32 {0}
@@ -2314,6 +2361,9 @@ mod tests {
         fn nanos_since_boot(&self) -> u64 {0}
         fn stall(&self, _microseconds: u64) {}
         fn sleep(&self, _milliseconds: u64) {}
+        fn create_mutex(&self) -> Handle { Handle(0) }
+        fn acquire(&self, _mutex: Handle, _timeout: u16) -> Result<(), AmlError> { Ok(()) }
+        fn release(&self, _mutex: Handle) {}
     }
 
     #[test]
diff --git a/aml/src/object.rs b/aml/src/object.rs
index 0b25b629..9d7f3371 100644
--- a/aml/src/object.rs
+++ b/aml/src/object.rs
@@ -12,7 +12,7 @@ pub enum Object {
     FieldUnit(FieldUnit),
     Integer(u64),
     Method { code: Vec<u8>, flags: MethodFlags },
-    Mutex { sync_level: u8 },
+    Mutex { mutex: Handle, sync_level: u8 },
     Reference { kind: ReferenceKind, inner: Arc<Object> },
     OpRegion(OpRegion),
     Package(Vec<Arc<Object>>),
diff --git a/tools/aml_tester/Cargo.toml b/tools/aml_tester/Cargo.toml
index fa621460..517a288a 100644
--- a/tools/aml_tester/Cargo.toml
+++ b/tools/aml_tester/Cargo.toml
@@ -9,3 +9,4 @@ aml = { path = "../../aml" }
 clap = "4"
 termion = "1"
 log = "0.4"
+pci_types = "0.10"
diff --git a/tools/aml_tester/src/main.rs b/tools/aml_tester/src/main.rs
index 1b62261a..d2920fb2 100644
--- a/tools/aml_tester/src/main.rs
+++ b/tools/aml_tester/src/main.rs
@@ -9,8 +9,9 @@
  *      - For failing tests, print out a nice summary of the errors for each file
  */
 
-use aml::{namespace::AmlName, AmlError, Interpreter, PciAddress};
+use aml::{namespace::AmlName, AmlError, Handle, Interpreter};
 use clap::{Arg, ArgAction, ArgGroup};
+use pci_types::PciAddress;
 use std::{
     collections::HashSet,
     ffi::OsStr,
@@ -341,16 +342,16 @@ impl aml::Handler for Handler {
         0
     }
 
-    fn write_u8(&mut self, address: usize, value: u8) {
+    fn write_u8(&self, address: usize, value: u8) {
         println!("write_u8 {address:#x}<-{value:#x}");
     }
-    fn write_u16(&mut self, address: usize, value: u16) {
+    fn write_u16(&self, address: usize, value: u16) {
         println!("write_u16 {address:#x}<-{value:#x}");
     }
-    fn write_u32(&mut self, address: usize, value: u32) {
+    fn write_u32(&self, address: usize, value: u32) {
         println!("write_u32 {address:#x}<-{value:#x}");
     }
-    fn write_u64(&mut self, address: usize, value: u64) {
+    fn write_u64(&self, address: usize, value: u64) {
         println!("write_u64 {address:#x}<-{value:#x}");
     }
 
@@ -410,4 +411,12 @@ impl aml::Handler for Handler {
     fn sleep(&self, milliseconds: u64) {
         println!("Sleeping for {}ms", milliseconds);
     }
+
+    fn create_mutex(&self) -> Handle {
+        Handle(0)
+    }
+    fn acquire(&self, _mutex: Handle, _timeout: u16) -> Result<(), AmlError> {
+        Ok(())
+    }
+    fn release(&self, _mutex: Handle) {}
 }

From b5bb2a59677c5709c161215871b20312c6b0e63f Mon Sep 17 00:00:00 2001
From: Isaac Woods <isaacwoods.home@gmail.com>
Date: Thu, 13 Mar 2025 16:58:30 +0000
Subject: [PATCH 61/88] `aml`: initial implementation of field writing

---
 aml/src/lib.rs   | 288 ++++++++++++++++++++++++++++++++++-------------
 tests/fields.asl |   4 +
 2 files changed, 214 insertions(+), 78 deletions(-)

diff --git a/aml/src/lib.rs b/aml/src/lib.rs
index 65097640..2515714e 100644
--- a/aml/src/lib.rs
+++ b/aml/src/lib.rs
@@ -1486,6 +1486,7 @@ where
                     }
                     _ => panic!(),
                 },
+                Object::FieldUnit(field) => self.do_field_write(field, object)?,
                 Object::Debug => {
                     self.handler.handle_debug(&*object);
                 }
@@ -1526,56 +1527,129 @@ where
             Output::Integer(value) => value,
         };
 
-        match field.kind {
-            FieldUnitKind::Normal { ref region } => {
-                let Object::OpRegion(ref region) = **region else { panic!() };
-
-                /*
-                 * TODO: it might be worth having a fast path here for reads that don't do weird
-                 * unaligned accesses, which I'm guessing might be relatively common on real
-                 * hardware? Eg. single native read + mask
-                 */
-
-                /*
-                 * Break the field read into native reads that respect the region's access width.
-                 * Copy each potentially-unaligned part into the destination's bit range.
-                 */
-                let native_accesses_needed = (field.bit_length + (field.bit_index % access_width_bits))
-                    .next_multiple_of(access_width_bits)
-                    / access_width_bits;
-                let mut read_so_far = 0;
-                for i in 0..native_accesses_needed {
-                    let aligned_offset =
-                        object::align_down(field.bit_index + i * access_width_bits, access_width_bits);
-                    let raw = self.do_native_region_read(region, aligned_offset / 8, access_width_bits / 8)?;
-                    let src_index = if i == 0 { field.bit_index % access_width_bits } else { 0 };
-                    let remaining_length = field.bit_length - read_so_far;
-                    let length = if i == 0 {
-                        usize::min(remaining_length, access_width_bits - (field.bit_index % access_width_bits))
-                    } else {
-                        usize::min(remaining_length, access_width_bits)
-                    };
+        let read_region = match field.kind {
+            FieldUnitKind::Normal { ref region } => region,
+            FieldUnitKind::Bank { ref region, ref bank, bank_value } => {
+                // TODO: put the bank_value in the bank
+                todo!();
+                region
+            }
+            FieldUnitKind::Index { ref index, ref data } => {
+                // TODO: configure the correct index
+                todo!();
+                data
+            }
+        };
+        let Object::OpRegion(ref read_region) = **read_region else { panic!() };
 
-                    trace!(
-                        "Extracting bits {}..{} from native read to bits {}..{}",
-                        src_index,
-                        src_index + length,
-                        read_so_far,
-                        read_so_far + length,
-                    );
-                    object::copy_bits(&raw.to_le_bytes(), src_index, output_bytes, read_so_far, length);
+        /*
+         * TODO: it might be worth having a fast path here for reads that don't do weird
+         * unaligned accesses, which I'm guessing might be relatively common on real
+         * hardware? Eg. single native read + mask
+         */
 
-                    read_so_far += length;
-                }
+        /*
+         * Break the field read into native reads that respect the region's access width.
+         * Copy each potentially-unaligned part into the destination's bit range.
+         */
+        let native_accesses_needed = (field.bit_length + (field.bit_index % access_width_bits))
+            .next_multiple_of(access_width_bits)
+            / access_width_bits;
+        let mut read_so_far = 0;
+        for i in 0..native_accesses_needed {
+            let aligned_offset = object::align_down(field.bit_index + i * access_width_bits, access_width_bits);
+            let raw = self.do_native_region_read(read_region, aligned_offset / 8, access_width_bits / 8)?;
+            let src_index = if i == 0 { field.bit_index % access_width_bits } else { 0 };
+            let remaining_length = field.bit_length - read_so_far;
+            let length = if i == 0 {
+                usize::min(remaining_length, access_width_bits - (field.bit_index % access_width_bits))
+            } else {
+                usize::min(remaining_length, access_width_bits)
+            };
 
-                match output {
-                    Output::Buffer(bytes) => Ok(Arc::new(Object::Buffer(bytes))),
-                    Output::Integer(value) => Ok(Arc::new(Object::Integer(u64::from_le_bytes(value)))),
-                }
+            object::copy_bits(&raw.to_le_bytes(), src_index, output_bytes, read_so_far, length);
+            read_so_far += length;
+        }
+
+        match output {
+            Output::Buffer(bytes) => Ok(Arc::new(Object::Buffer(bytes))),
+            Output::Integer(value) => Ok(Arc::new(Object::Integer(u64::from_le_bytes(value)))),
+        }
+    }
+
+    fn do_field_write(&self, field: &FieldUnit, value: Arc<Object>) -> Result<(), AmlError> {
+        trace!("AML field write. Field = {:?}. Value = {:?}", field, value);
+
+        let value_bytes = match &*value {
+            Object::Integer(value) => &value.to_le_bytes() as &[u8],
+            Object::Buffer(bytes) => &bytes,
+            _ => Err(AmlError::ObjectNotOfExpectedType { expected: ObjectType::Integer, got: value.typ() })?,
+        };
+        let access_width_bits = field.flags.access_type_bytes()? * 8;
+
+        let write_region = match field.kind {
+            FieldUnitKind::Normal { ref region } => region,
+            FieldUnitKind::Bank { ref region, ref bank, bank_value } => {
+                // TODO: put the bank_value in the bank
+                todo!();
+                region
+            }
+            FieldUnitKind::Index { ref index, ref data } => {
+                // TODO: configure the correct index
+                todo!();
+                data
             }
-            FieldUnitKind::Bank { ref region, ref bank, bank_value } => todo!(),
-            FieldUnitKind::Index { ref index, ref data } => todo!(),
+        };
+        let Object::OpRegion(ref write_region) = **write_region else { panic!() };
+
+        // TODO: if the region wants locking, do that
+
+        // TODO: maybe also a fast path for writes
+
+        let native_accesses_needed = (field.bit_length + (field.bit_index % access_width_bits))
+            .next_multiple_of(access_width_bits)
+            / access_width_bits;
+        let mut written_so_far = 0;
+
+        for i in 0..native_accesses_needed {
+            let aligned_offset = object::align_down(field.bit_index + i * access_width_bits, access_width_bits);
+            let dst_index = if i == 0 { field.bit_index % access_width_bits } else { 0 };
+
+            /*
+             * If we're not going to write a whole native access, respect the field's
+             * update rule. If we're meant to preserve the surrounding bits, we need to do
+             * a read first.
+             */
+            let mut bytes = if dst_index > 0 || (field.bit_length - written_so_far) < access_width_bits {
+                match field.flags.update_rule() {
+                    FieldUpdateRule::Preserve => self
+                        .do_native_region_read(write_region, aligned_offset / 8, access_width_bits / 8)?
+                        .to_le_bytes(),
+                    FieldUpdateRule::WriteAsOnes => [0xff; 8],
+                    FieldUpdateRule::WriteAsZeros => [0; 8],
+                }
+            } else {
+                [0; 8]
+            };
+
+            let remaining_length = field.bit_length - written_so_far;
+            let length = if i == 0 {
+                usize::min(remaining_length, access_width_bits - (field.bit_index % access_width_bits))
+            } else {
+                usize::min(remaining_length, access_width_bits)
+            };
+
+            object::copy_bits(value_bytes, written_so_far, &mut bytes, dst_index, length);
+            self.do_native_region_write(
+                write_region,
+                aligned_offset / 8,
+                access_width_bits / 8,
+                u64::from_le_bytes(bytes),
+            )?;
+            written_so_far += length;
         }
+
+        Ok(())
     }
 
     /// Performs an actual read from an operation region. `offset` and `length` must respect the
@@ -1605,37 +1679,7 @@ where
                 }
             }),
             RegionSpace::PciConfig => {
-                /*
-                 * TODO: it's not ideal to do these reads for every native access. See if we can
-                 * cache them somewhere?
-                 */
-                let seg = match self.invoke_method_if_present(
-                    AmlName::from_str("_SEG").unwrap().resolve(&region.parent_device_path)?,
-                    vec![],
-                )? {
-                    Some(value) => value.as_integer()?,
-                    None => 0,
-                };
-                let bus = match self.invoke_method_if_present(
-                    AmlName::from_str("_BBR").unwrap().resolve(&region.parent_device_path)?,
-                    vec![],
-                )? {
-                    Some(value) => value.as_integer()?,
-                    None => 0,
-                };
-                let (device, function) = {
-                    let adr = self.invoke_method_if_present(
-                        AmlName::from_str("_ADR").unwrap().resolve(&region.parent_device_path)?,
-                        vec![],
-                    )?;
-                    let adr = match adr {
-                        Some(adr) => adr.as_integer()?,
-                        None => 0,
-                    };
-                    (adr.get_bits(16..32), adr.get_bits(0..16))
-                };
-
-                let address = PciAddress::new(seg as u16, bus as u8, device as u8, function as u8);
+                let address = self.pci_address_for_device(&region.parent_device_path)?;
                 match length {
                     1 => Ok(self.handler.read_pci_u8(address, offset as u16) as u64),
                     2 => Ok(self.handler.read_pci_u16(address, offset as u16) as u64),
@@ -1656,12 +1700,99 @@ where
                 if let Some(handler) = self.region_handlers.lock().get(&region.space) {
                     todo!("Utilise handler");
                 } else {
-                    // TODO: panic or normal error here??
-                    panic!("Unhandled region space that needs handler!");
+                    Err(AmlError::NoHandlerForRegionAccess(region.space))
+                }
+            }
+        }
+    }
+
+    /// Performs an actual write to an operation region. `offset` and `length` must respect the
+    /// access requirements of the field being read, and are supplied in **bytes**. This may call
+    /// AML methods if required, and may invoke user-supplied handlers.
+    fn do_native_region_write(
+        &self,
+        region: &OpRegion,
+        offset: usize,
+        length: usize,
+        value: u64,
+    ) -> Result<(), AmlError> {
+        trace!(
+            "Native field write. Region = {:?}, offset = {:#x}, length={:#x}, value={:#x}",
+            region, offset, length, value
+        );
+
+        match region.space {
+            RegionSpace::SystemMemory => Ok({
+                let address = region.base as usize + offset;
+                match length {
+                    1 => self.handler.write_u8(address, value as u8),
+                    2 => self.handler.write_u16(address, value as u16),
+                    4 => self.handler.write_u32(address, value as u32),
+                    8 => self.handler.write_u64(address, value),
+                    _ => panic!(),
+                }
+            }),
+            RegionSpace::SystemIO => Ok({
+                let address = region.base as u16 + offset as u16;
+                match length {
+                    1 => self.handler.write_io_u8(address, value as u8),
+                    2 => self.handler.write_io_u16(address, value as u16),
+                    4 => self.handler.write_io_u32(address, value as u32),
+                    _ => panic!(),
+                }
+            }),
+            RegionSpace::PciConfig => {
+                let address = self.pci_address_for_device(&region.parent_device_path)?;
+                match length {
+                    1 => self.handler.write_pci_u8(address, offset as u16, value as u8),
+                    2 => self.handler.write_pci_u16(address, offset as u16, value as u16),
+                    4 => self.handler.write_pci_u32(address, offset as u16, value as u32),
+                    _ => panic!(),
+                }
+                Ok(())
+            }
+
+            RegionSpace::EmbeddedControl
+            | RegionSpace::SmBus
+            | RegionSpace::SystemCmos
+            | RegionSpace::PciBarTarget
+            | RegionSpace::Ipmi
+            | RegionSpace::GeneralPurposeIo
+            | RegionSpace::GenericSerialBus
+            | RegionSpace::Pcc
+            | RegionSpace::Oem(_) => {
+                if let Some(handler) = self.region_handlers.lock().get(&region.space) {
+                    todo!("Utilise handler");
+                } else {
+                    Err(AmlError::NoHandlerForRegionAccess(region.space))
                 }
             }
         }
     }
+
+    fn pci_address_for_device(&self, path: &AmlName) -> Result<PciAddress, AmlError> {
+        /*
+         * TODO: it's not ideal to do these reads for every native access. See if we can
+         * cache them somewhere?
+         */
+        let seg = match self.invoke_method_if_present(AmlName::from_str("_SEG").unwrap().resolve(path)?, vec![])? {
+            Some(value) => value.as_integer()?,
+            None => 0,
+        };
+        let bus = match self.invoke_method_if_present(AmlName::from_str("_BBR").unwrap().resolve(path)?, vec![])? {
+            Some(value) => value.as_integer()?,
+            None => 0,
+        };
+        let (device, function) = {
+            let adr = self.invoke_method_if_present(AmlName::from_str("_ADR").unwrap().resolve(path)?, vec![])?;
+            let adr = match adr {
+                Some(adr) => adr.as_integer()?,
+                None => 0,
+            };
+            (adr.get_bits(16..32), adr.get_bits(0..16))
+        };
+        Ok(PciAddress::new(seg as u16, bus as u8, device as u8, function as u8))
+    }
 }
 
 /// A `MethodContext` represents a piece of running AML code - either a real method, or the
@@ -2241,6 +2372,7 @@ pub enum AmlError {
     InvalidResourceDescriptor,
     UnexpectedResourceType,
 
+    NoHandlerForRegionAccess(RegionSpace),
     MutexAquireTimeout,
 
     PrtInvalidAddress,
diff --git a/tests/fields.asl b/tests/fields.asl
index 3dd2f0ed..ac9c9e1d 100644
--- a/tests/fields.asl
+++ b/tests/fields.asl
@@ -49,5 +49,9 @@ DefinitionBlock("fields.aml", "DSDT", 1, "RSACPI", "BUFFLD", 1) {
         RESA = A
         RESB = B
         RESC = C
+
+        A = RESA
+        B = RESB
+        C = RESC
     }
 }

From 633174401ca3cf80142f94b8d4b58f699afef5e8 Mon Sep 17 00:00:00 2001
From: Isaac Woods <isaacwoods.home@gmail.com>
Date: Thu, 13 Mar 2025 18:43:45 +0000
Subject: [PATCH 62/88] `acpi`: don't gate `allocator_api` on a feature

---
 acpi/Cargo.toml                |  5 ++---
 acpi/src/lib.rs                | 41 ++++++++++++----------------------
 acpi/src/madt.rs               | 40 ++++++++++++++-------------------
 acpi/src/mcfg.rs               | 34 +++++++++++++++-------------
 acpi/src/platform/interrupt.rs |  2 +-
 acpi/src/platform/mod.rs       |  2 +-
 6 files changed, 53 insertions(+), 71 deletions(-)

diff --git a/acpi/Cargo.toml b/acpi/Cargo.toml
index deeb34ac..acc10ae5 100644
--- a/acpi/Cargo.toml
+++ b/acpi/Cargo.toml
@@ -15,6 +15,5 @@ bitflags = "2.5.0"
 log = "0.4.20"
 
 [features]
-default = ["allocator_api", "alloc"]
-allocator_api = []
-alloc = ["allocator_api"]
+default = ["alloc"]
+alloc = []
diff --git a/acpi/src/lib.rs b/acpi/src/lib.rs
index 30f3ad6c..fc3cd334 100644
--- a/acpi/src/lib.rs
+++ b/acpi/src/lib.rs
@@ -53,8 +53,7 @@
  */
 
 #![no_std]
-#![deny(unsafe_op_in_unsafe_fn)]
-#![cfg_attr(feature = "allocator_api", feature(allocator_api))]
+#![feature(allocator_api)]
 
 #[cfg_attr(test, macro_use)]
 #[cfg(test)]
@@ -69,35 +68,28 @@ pub mod fadt;
 pub mod handler;
 pub mod hpet;
 pub mod madt;
+pub mod managed_slice;
 pub mod mcfg;
+pub mod platform;
 pub mod rsdp;
 pub mod sdt;
 pub mod spcr;
 
-#[cfg(feature = "allocator_api")]
-mod managed_slice;
-#[cfg(feature = "allocator_api")]
-pub use managed_slice::*;
-
-#[cfg(feature = "allocator_api")]
-pub mod platform;
-#[cfg(feature = "allocator_api")]
-pub use crate::platform::{interrupt::InterruptModel, PlatformInfo};
-
-#[cfg(feature = "allocator_api")]
-pub use crate::mcfg::PciConfigRegions;
-
+pub use crate::{
+    mcfg::PciConfigRegions,
+    platform::{interrupt::InterruptModel, PlatformInfo},
+};
 pub use fadt::PowerProfile;
 pub use handler::{AcpiHandler, PhysicalMapping};
 pub use hpet::HpetInfo;
 pub use madt::MadtError;
 
 use crate::sdt::{SdtHeader, Signature};
-use core::mem;
+use core::{alloc::Allocator, mem};
 use rsdp::Rsdp;
 
 /// Result type used by error-returning functions.
-pub type AcpiResult<T> = core::result::Result<T, AcpiError>;
+pub type AcpiResult<T> = Result<T, AcpiError>;
 
 /// All types representing ACPI tables should implement this trait.
 ///
@@ -179,13 +171,9 @@ macro_rules! read_root_table {
     }};
 }
 
-/// Type capable of enumerating the existing ACPI tables on the system.
-///
-///
-/// ### Implementation Note
-///
-/// When using the `allocator_api`±`alloc` features, [`PlatformInfo::new()`] or [`PlatformInfo::new_in()`] provide
-/// a much cleaner API for enumerating ACPI structures once an `AcpiTables` has been constructed.
+/// `AcpiTables` represents all of the enumerable ACPI tables on the system, and is the main type
+/// to construct and use from the crate. `PlatformInfo` provides a higher-level interface to some
+/// of the data contained in the tables, and may be easier to use for common use-cases.
 #[derive(Debug)]
 pub struct AcpiTables<H: AcpiHandler> {
     mapping: PhysicalMapping<H, SdtHeader>,
@@ -376,12 +364,11 @@ where
         PlatformInfo::new(self)
     }
 
-    /// Convenience method for contructing a [`PlatformInfo`]. This is one of the first things you should usually do
+    /// Construct a [`PlatformInfo`]. This is one of the first things you should usually do
     /// with an `AcpiTables`, and allows to collect helpful information about the platform from the ACPI tables.
-    #[cfg(feature = "allocator_api")]
     pub fn platform_info_in<A>(&self, allocator: A) -> AcpiResult<PlatformInfo<A>>
     where
-        A: core::alloc::Allocator + Clone,
+        A: Allocator + Clone,
     {
         PlatformInfo::new_in(self, allocator)
     }
diff --git a/acpi/src/madt.rs b/acpi/src/madt.rs
index ff759bff..73b3729e 100644
--- a/acpi/src/madt.rs
+++ b/acpi/src/madt.rs
@@ -1,24 +1,22 @@
 use crate::{
+    managed_slice::ManagedSlice,
+    platform::{
+        interrupt::{InterruptModel, Polarity, TriggerMode},
+        ProcessorInfo,
+    },
     sdt::{ExtendedField, SdtHeader, Signature},
     AcpiError,
+    AcpiResult,
     AcpiTable,
 };
 use bit_field::BitField;
 use core::{
+    alloc::Allocator,
     marker::{PhantomData, PhantomPinned},
     mem,
     pin::Pin,
 };
 
-#[cfg(feature = "allocator_api")]
-use crate::{
-    platform::{
-        interrupt::{InterruptModel, Polarity, TriggerMode},
-        ProcessorInfo,
-    },
-    AcpiResult,
-};
-
 #[derive(Debug)]
 pub enum MadtError {
     UnexpectedEntry,
@@ -69,13 +67,12 @@ impl Madt {
         Err(AcpiError::InvalidMadt(MadtError::UnexpectedEntry))
     }
 
-    #[cfg(feature = "allocator_api")]
     pub fn parse_interrupt_model_in<A>(
         self: Pin<&Self>,
         allocator: A,
     ) -> AcpiResult<(InterruptModel<A>, Option<ProcessorInfo<A>>)>
     where
-        A: core::alloc::Allocator + Clone,
+        A: Allocator + Clone,
     {
         /*
          * We first do a pass through the MADT to determine which interrupt model is being used.
@@ -114,13 +111,12 @@ impl Madt {
         Ok((InterruptModel::Unknown, None))
     }
 
-    #[cfg(feature = "allocator_api")]
     fn parse_apic_model_in<A>(
         self: Pin<&Self>,
         allocator: A,
     ) -> AcpiResult<(InterruptModel<A>, Option<ProcessorInfo<A>>)>
     where
-        A: core::alloc::Allocator + Clone,
+        A: Allocator + Clone,
     {
         use crate::platform::{
             interrupt::{
@@ -157,12 +153,11 @@ impl Madt {
             }
         }
 
-        let mut io_apics = crate::ManagedSlice::new_in(io_apic_count, allocator.clone())?;
-        let mut interrupt_source_overrides = crate::ManagedSlice::new_in(iso_count, allocator.clone())?;
-        let mut nmi_sources = crate::ManagedSlice::new_in(nmi_source_count, allocator.clone())?;
-        let mut local_apic_nmi_lines = crate::ManagedSlice::new_in(local_nmi_line_count, allocator.clone())?;
-        let mut application_processors =
-            crate::ManagedSlice::new_in(processor_count.saturating_sub(1), allocator)?; // Subtract one for the BSP
+        let mut io_apics = ManagedSlice::new_in(io_apic_count, allocator.clone())?;
+        let mut interrupt_source_overrides = ManagedSlice::new_in(iso_count, allocator.clone())?;
+        let mut nmi_sources = ManagedSlice::new_in(nmi_source_count, allocator.clone())?;
+        let mut local_apic_nmi_lines = ManagedSlice::new_in(local_nmi_line_count, allocator.clone())?;
+        let mut application_processors = ManagedSlice::new_in(processor_count.saturating_sub(1), allocator)?; // Subtract one for the BSP
         let mut boot_processor = None;
 
         io_apic_count = 0;
@@ -678,20 +673,19 @@ pub struct MultiprocessorWakeupMailbox {
     pub reserved_for_firmware: [u64; 256],
 }
 
-#[cfg(feature = "allocator_api")]
-fn parse_mps_inti_flags(flags: u16) -> crate::AcpiResult<(Polarity, TriggerMode)> {
+fn parse_mps_inti_flags(flags: u16) -> AcpiResult<(Polarity, TriggerMode)> {
     let polarity = match flags.get_bits(0..2) {
         0b00 => Polarity::SameAsBus,
         0b01 => Polarity::ActiveHigh,
         0b11 => Polarity::ActiveLow,
-        _ => return Err(crate::AcpiError::InvalidMadt(MadtError::MpsIntiInvalidPolarity)),
+        _ => return Err(AcpiError::InvalidMadt(MadtError::MpsIntiInvalidPolarity)),
     };
 
     let trigger_mode = match flags.get_bits(2..4) {
         0b00 => TriggerMode::SameAsBus,
         0b01 => TriggerMode::Edge,
         0b11 => TriggerMode::Level,
-        _ => return Err(crate::AcpiError::InvalidMadt(MadtError::MpsIntiInvalidTriggerMode)),
+        _ => return Err(AcpiError::InvalidMadt(MadtError::MpsIntiInvalidTriggerMode)),
     };
 
     Ok((polarity, trigger_mode))
diff --git a/acpi/src/mcfg.rs b/acpi/src/mcfg.rs
index 911521e5..86d96816 100644
--- a/acpi/src/mcfg.rs
+++ b/acpi/src/mcfg.rs
@@ -1,45 +1,47 @@
 use crate::{
+    managed_slice::ManagedSlice,
     sdt::{SdtHeader, Signature},
+    AcpiHandler,
+    AcpiResult,
     AcpiTable,
+    AcpiTables,
 };
-use core::{mem, slice};
+use core::{alloc::Allocator, fmt, mem, ops::RangeInclusive, slice};
 
 /// Describes a set of regions of physical memory used to access the PCIe configuration space. A
 /// region is created for each entry in the MCFG. Given the segment group, bus, device number, and
 /// function of a PCIe device, the `physical_address` method on this will give you the physical
 /// address of the start of that device function's configuration space (each function has 4096
 /// bytes of configuration space in PCIe).
-#[cfg(feature = "allocator_api")]
 pub struct PciConfigRegions<A>
 where
-    A: core::alloc::Allocator,
+    A: Allocator,
 {
-    regions: crate::ManagedSlice<McfgEntry, A>,
+    regions: ManagedSlice<McfgEntry, A>,
 }
 
 #[cfg(feature = "alloc")]
 impl<'a> PciConfigRegions<alloc::alloc::Global> {
-    pub fn new<H>(tables: &crate::AcpiTables<H>) -> crate::AcpiResult<PciConfigRegions<alloc::alloc::Global>>
+    pub fn new<H>(tables: &AcpiTables<H>) -> AcpiResult<PciConfigRegions<alloc::alloc::Global>>
     where
-        H: crate::AcpiHandler,
+        H: AcpiHandler,
     {
         Self::new_in(tables, alloc::alloc::Global)
     }
 }
 
-#[cfg(feature = "allocator_api")]
 impl<A> PciConfigRegions<A>
 where
-    A: core::alloc::Allocator,
+    A: Allocator,
 {
-    pub fn new_in<H>(tables: &crate::AcpiTables<H>, allocator: A) -> crate::AcpiResult<PciConfigRegions<A>>
+    pub fn new_in<H>(tables: &AcpiTables<H>, allocator: A) -> AcpiResult<PciConfigRegions<A>>
     where
-        H: crate::AcpiHandler,
+        H: AcpiHandler,
     {
         let mcfg = tables.find_table::<Mcfg>()?;
         let mcfg_entries = mcfg.entries();
 
-        let mut regions = crate::ManagedSlice::new_in(mcfg_entries.len(), allocator)?;
+        let mut regions = ManagedSlice::new_in(mcfg_entries.len(), allocator)?;
         regions.copy_from_slice(mcfg_entries);
 
         Ok(Self { regions })
@@ -74,7 +76,7 @@ where
 /// Configuration entry describing a valid bus range for the given PCI segment group.
 pub struct PciConfigEntry {
     pub segment_group: u16,
-    pub bus_range: core::ops::RangeInclusive<u8>,
+    pub bus_range: RangeInclusive<u8>,
     pub physical_address: usize,
 }
 
@@ -121,8 +123,8 @@ impl Mcfg {
     pub fn entries(&self) -> &[McfgEntry] {
         let length = self.header.length as usize - mem::size_of::<Mcfg>();
 
-        // Intentionally round down in case length isn't an exact multiple of McfgEntry size
-        // (see rust-osdev/acpi#58)
+        // Intentionally round down in case length isn't an exact multiple of McfgEntry size - this
+        // has been observed on real hardware (see rust-osdev/acpi#58)
         let num_entries = length / mem::size_of::<McfgEntry>();
 
         unsafe {
@@ -132,8 +134,8 @@ impl Mcfg {
     }
 }
 
-impl core::fmt::Debug for Mcfg {
-    fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
+impl fmt::Debug for Mcfg {
+    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
         formatter.debug_struct("Mcfg").field("header", &self.header).field("entries", &self.entries()).finish()
     }
 }
diff --git a/acpi/src/platform/interrupt.rs b/acpi/src/platform/interrupt.rs
index a1c2efe7..944c16cd 100644
--- a/acpi/src/platform/interrupt.rs
+++ b/acpi/src/platform/interrupt.rs
@@ -1,4 +1,4 @@
-use crate::ManagedSlice;
+use crate::managed_slice::ManagedSlice;
 use core::alloc::Allocator;
 
 #[derive(Debug, Clone, Copy)]
diff --git a/acpi/src/platform/mod.rs b/acpi/src/platform/mod.rs
index ca43d583..5a033653 100644
--- a/acpi/src/platform/mod.rs
+++ b/acpi/src/platform/mod.rs
@@ -4,11 +4,11 @@ use crate::{
     address::GenericAddress,
     fadt::Fadt,
     madt::{Madt, MadtError, MpProtectedModeWakeupCommand, MultiprocessorWakeupMailbox},
+    managed_slice::ManagedSlice,
     AcpiError,
     AcpiHandler,
     AcpiResult,
     AcpiTables,
-    ManagedSlice,
     PowerProfile,
 };
 use core::{alloc::Allocator, mem, ptr};

From 184ddbff012f92433d841c671e947bf8025a3ed4 Mon Sep 17 00:00:00 2001
From: Isaac Woods <isaacwoods.home@gmail.com>
Date: Thu, 13 Mar 2025 19:32:05 +0000
Subject: [PATCH 63/88] `acpi`: include revision of AML tables in `AmlTable`

---
 acpi/src/lib.rs | 12 +++++++++---
 1 file changed, 9 insertions(+), 3 deletions(-)

diff --git a/acpi/src/lib.rs b/acpi/src/lib.rs
index fc3cd334..80d21f25 100644
--- a/acpi/src/lib.rs
+++ b/acpi/src/lib.rs
@@ -346,7 +346,7 @@ where
             let dsdt_address = fadt.dsdt_address()?;
             let dsdt = unsafe { read_table::<H, Dsdt>(self.handler.clone(), dsdt_address)? };
 
-            Ok(AmlTable::new(dsdt_address, dsdt.header().length))
+            Ok(AmlTable::new(dsdt_address, dsdt.header().length, dsdt.header().revision))
         })
     }
 
@@ -393,14 +393,16 @@ pub struct AmlTable {
     pub address: usize,
     /// Length (in bytes) of the AML stream.
     pub length: u32,
+    pub revision: u8,
 }
 
 impl AmlTable {
     /// Create an `AmlTable` from the address and length of the table **including the SDT header**.
-    pub(crate) fn new(address: usize, length: u32) -> AmlTable {
+    pub(crate) fn new(address: usize, length: u32, revision: u8) -> AmlTable {
         AmlTable {
             address: address + mem::size_of::<SdtHeader>(),
             length: length - mem::size_of::<SdtHeader>() as u32,
+            revision,
         }
     }
 }
@@ -458,7 +460,11 @@ where
         self.tables_phys_ptrs.find_map(|table_phys_ptr| {
             // SAFETY: Table guarantees its contained addresses to be valid.
             match unsafe { read_table::<_, Ssdt>(handler.clone(), table_phys_ptr as usize) } {
-                Ok(ssdt_mapping) => Some(AmlTable::new(ssdt_mapping.physical_start(), ssdt_mapping.header.length)),
+                Ok(ssdt_mapping) => Some(AmlTable::new(
+                    ssdt_mapping.physical_start(),
+                    ssdt_mapping.header.length,
+                    ssdt_mapping.header.revision,
+                )),
                 Err(AcpiError::SdtInvalidSignature(_)) => None,
                 Err(e) => {
                     log::warn!("Found invalid SSDT at physical address {:p}: {:?}", table_phys_ptr, e);

From 15b608d797c2d2cc405f668f2fe78802f813ffe4 Mon Sep 17 00:00:00 2001
From: Isaac Woods <isaacwoods.home@gmail.com>
Date: Mon, 24 Mar 2025 19:22:54 +0000
Subject: [PATCH 64/88] `acpi`: rewrite most of the library

- Correct a number of bugs in how we handle mapped regions of SDTs
- Simplify API on `AcpiTables`
- Move all `alloc`-usage into `platform`
- Move all SDT types under new `sdt` module
- Move from `ManagedSlice` back to just using `Vec`
- Improve documentation throughout the library
- Remove `AcpiResult` type in preference of normal `Result`
---
 acpi/Cargo.toml                 |   4 +-
 acpi/src/address.rs             |   4 +-
 acpi/src/handler.rs             |   3 +-
 acpi/src/lib.rs                 | 603 +++++++++-----------------------
 acpi/src/managed_slice.rs       |  81 -----
 acpi/src/mcfg.rs                | 151 --------
 acpi/src/platform/interrupt.rs  | 296 ++++++++++++++--
 acpi/src/platform/mod.rs        |  58 ++-
 acpi/src/platform/pci.rs        |  64 ++++
 acpi/src/rsdp.rs                |  12 +-
 acpi/src/{ => sdt}/bgrt.rs      |   7 +-
 acpi/src/{ => sdt}/fadt.rs      |   6 +-
 acpi/src/{ => sdt}/hpet.rs      |  12 +-
 acpi/src/{ => sdt}/madt.rs      | 284 +--------------
 acpi/src/sdt/mcfg.rs            |  54 +++
 acpi/src/{sdt.rs => sdt/mod.rs} | 125 +++----
 acpi/src/{ => sdt}/spcr.rs      |   8 +-
 17 files changed, 666 insertions(+), 1106 deletions(-)
 delete mode 100644 acpi/src/managed_slice.rs
 delete mode 100644 acpi/src/mcfg.rs
 create mode 100644 acpi/src/platform/pci.rs
 rename acpi/src/{ => sdt}/bgrt.rs (88%)
 rename acpi/src/{ => sdt}/fadt.rs (99%)
 rename acpi/src/{ => sdt}/hpet.rs (90%)
 rename acpi/src/{ => sdt}/madt.rs (56%)
 create mode 100644 acpi/src/sdt/mcfg.rs
 rename acpi/src/{sdt.rs => sdt/mod.rs} (73%)
 rename acpi/src/{ => sdt}/spcr.rs (98%)

diff --git a/acpi/Cargo.toml b/acpi/Cargo.toml
index acc10ae5..43fefaf7 100644
--- a/acpi/Cargo.toml
+++ b/acpi/Cargo.toml
@@ -3,11 +3,11 @@ name = "acpi"
 version = "5.2.0"
 authors = ["Isaac Woods"]
 repository = "https://github.com/rust-osdev/acpi"
-description = "A pure-Rust library for parsing ACPI tables"
+description = "A pure-Rust library for interacting with ACPI"
 categories = ["hardware-support", "no-std"]
 readme = "../README.md"
 license = "MIT/Apache-2.0"
-edition = "2021"
+edition = "2024"
 
 [dependencies]
 bit_field = "0.10.2"
diff --git a/acpi/src/address.rs b/acpi/src/address.rs
index df95160b..f8599b77 100644
--- a/acpi/src/address.rs
+++ b/acpi/src/address.rs
@@ -1,7 +1,7 @@
 //! ACPI defines a Generic Address Structure (GAS), which provides a versatile way to describe register locations
 //! in a wide range of address spaces.
 
-use crate::{AcpiError, AcpiResult};
+use crate::AcpiError;
 
 /// This is the raw form of a Generic Address Structure, and follows the layout found in the ACPI tables.
 #[derive(Clone, Copy, Debug)]
@@ -83,7 +83,7 @@ pub struct GenericAddress {
 }
 
 impl GenericAddress {
-    pub fn from_raw(raw: RawGenericAddress) -> AcpiResult<GenericAddress> {
+    pub fn from_raw(raw: RawGenericAddress) -> Result<GenericAddress, AcpiError> {
         let address_space = match raw.address_space {
             0x00 => AddressSpace::SystemMemory,
             0x01 => AddressSpace::SystemIo,
diff --git a/acpi/src/handler.rs b/acpi/src/handler.rs
index 85bbaecb..745ce2b1 100644
--- a/acpi/src/handler.rs
+++ b/acpi/src/handler.rs
@@ -54,7 +54,6 @@ where
     ///   dropped, it will be used to unmap the structure.
     ///
     /// ### Safety
-    ///
     /// The caller must ensure that the physical memory can be safely mapped.
     pub unsafe fn new(
         physical_start: usize,
@@ -128,7 +127,7 @@ where
 /// functionality, such as mapping regions of physical memory. You are free to implement these
 /// however you please, as long as they conform to the documentation of each function. The handler is stored in
 /// every `PhysicalMapping` so it's able to unmap itself when dropped, so this type needs to be something you can
-/// clone/move about freely (e.g. a reference, wrapper over `Rc`, marker struct, etc.).
+/// clone/move about freely (e.g. a reference, wrapper over `Arc`, marker struct, etc.).
 pub trait AcpiHandler: Clone {
     /// Given a physical address and a size, map a region of physical memory that contains `T` (note: the passed
     /// size may be larger than `size_of::<T>()`). The address is not neccessarily page-aligned, so the
diff --git a/acpi/src/lib.rs b/acpi/src/lib.rs
index 80d21f25..e8b61b07 100644
--- a/acpi/src/lib.rs
+++ b/acpi/src/lib.rs
@@ -1,56 +1,31 @@
-//! A library for parsing ACPI tables. This crate can be used by bootloaders and kernels for architectures that
-//! support ACPI. This crate is not feature-complete, but can parse lots of the more common tables. Parsing the
-//! ACPI tables is required for correctly setting up the APICs, HPET, and provides useful information about power
-//! management and many other platform capabilities.
+//! `acpi` is a Rust library for interacting with the Advanced Configuration and Power Interface, a
+//! complex framework for power management and device discovery and configuration. ACPI is used on
+//! modern x64, ARM, RISC-V, and other platforms. An operating system needs to interact with ACPI
+//! to correctly set up a platform's interrupt controllers, perform power management, and fully
+//! support many other platform capabilities.
 //!
-//! This crate is designed to find and parse the static tables ACPI provides. It should be used in conjunction with
-//! the `aml` crate, which is the (much less complete) AML parser used to parse the DSDT and SSDTs. These crates
-//! are separate because some kernels may want to detect the static tables, but delay AML parsing to a later stage.
-//!
-//! This crate can be used in three configurations, depending on the environment it's being used from:
-//!    - **Without allocator support** - this can be achieved by disabling the `allocator_api` and `alloc`
-//!      features. The core parts of the library will still be usable, but with generally reduced functionality
-//!      and ease-of-use.
-//!    - **With a custom allocator** - by disabling just the `alloc` feature, you can use the `new_in` functions to
-//!      access increased functionality with your own allocator. This allows `acpi` to be integrated more closely
-//!      with environments that already provide a custom allocator, for example to gracefully handle allocation
-//!      errors.
-//!    - **With the globally-set allocator** - the `alloc` feature provides `new` functions that simply use the
-//!      global allocator. This is the easiest option, and the one the majority of users will want. It is the
-//!      default configuration of the crate.
+//! This crate provides a limited API that can be used without an allocator, for example for use
+//! from a bootloader. This API will allow you to search for the RSDP, enumerate over the available
+//! tables, and interact with the tables using their raw structures. All other functionality is
+//! behind an `alloc` feature (enabled by default) and requires an allocator.
 //!
 //! ### Usage
-//! To use the library, you will need to provide an implementation of the [`AcpiHandler`] trait, which allows the
-//! library to make requests such as mapping a particular region of physical memory into the virtual address space.
+//! To use the library, you will need to provide an implementation of the [`AcpiHandler`] trait,
+//! which allows the library to make requests such as mapping a particular region of physical
+//! memory into the virtual address space.
+//!
+//! Next, you'll need to get the physical address of either the RSDP, or the RSDT/XSDT. The method
+//! for doing this depends on the platform you're running on and how you were booted. If you know
+//! the system was booted via the BIOS, you can use [`Rsdp::search_for_rsdp_bios`]. UEFI provides a
+//! separate mechanism for getting the address of the RSDP.
 //!
-//! You then need to construct an instance of [`AcpiTables`], which can be done in a few ways depending on how much
-//! information you have:
+//! You then need to construct an instance of [`AcpiTables`], which can be done in a few ways
+//! depending on how much information you have:
 //! * Use [`AcpiTables::from_rsdp`] if you have the physical address of the RSDP
 //! * Use [`AcpiTables::from_rsdt`] if you have the physical address of the RSDT/XSDT
-//! * Use [`AcpiTables::search_for_rsdp_bios`] if you don't have the address of either, but **you know you are
-//!   running on BIOS, not UEFI**
-//!
-//! `AcpiTables` stores the addresses of all of the tables detected on a platform. The SDTs are parsed by this
-//! library, or can be accessed directly with `from_sdt`, while the `DSDT` and any `SSDTs` should be parsed with
-//! `aml`.
 //!
-//! To gather information out of the static tables, a few of the types you should take a look at are:
-//!    - [`PlatformInfo`] parses the FADT and MADT to create a nice view of the processor topology and interrupt
-//!      controllers on `x86_64`, and the interrupt controllers on other platforms.
-//!      [`AcpiTables::platform_info`] is a convenience method for constructing a `PlatformInfo`.
-//!    - [`HpetInfo`] parses the HPET table and tells you how to configure the High Precision Event Timer.
-//!    - [`PciConfigRegions`] parses the MCFG and tells you how PCIe configuration space is mapped into physical
-//!      memory.
-
-/*
- * Contributing notes (you may find these useful if you're new to contributing to the library):
- *    - Accessing packed fields without UB: Lots of the structures defined by ACPI are defined with `repr(packed)`
- *      to prevent padding being introduced, which would make the structure's layout incorrect. In Rust, this
- *      creates a problem as references to these fields could be unaligned, which is undefined behaviour. For the
- *      majority of these fields, this problem can be easily avoided by telling the compiler to make a copy of the
- *      field's contents: this is the perhaps unfamiliar pattern of e.g. `!{ entry.flags }.get_bit(0)` we use
- *      around the codebase.
- */
+//! Once you have an `AcpiTables`, you can search for relevant tables, or use the higher-level
+//! interfaces, such as [`PlatformInfo`], [`PciConfigRegions`], or [`HpetInfo`].
 
 #![no_std]
 #![feature(allocator_api)]
@@ -63,121 +38,24 @@ extern crate std;
 extern crate alloc;
 
 pub mod address;
-pub mod bgrt;
-pub mod fadt;
 pub mod handler;
-pub mod hpet;
-pub mod madt;
-pub mod managed_slice;
-pub mod mcfg;
 pub mod platform;
 pub mod rsdp;
 pub mod sdt;
-pub mod spcr;
 
-pub use crate::{
-    mcfg::PciConfigRegions,
-    platform::{interrupt::InterruptModel, PlatformInfo},
-};
-pub use fadt::PowerProfile;
 pub use handler::{AcpiHandler, PhysicalMapping};
-pub use hpet::HpetInfo;
-pub use madt::MadtError;
+pub use sdt::{fadt::PowerProfile, hpet::HpetInfo, madt::MadtError};
 
 use crate::sdt::{SdtHeader, Signature};
-use core::{alloc::Allocator, mem};
+use core::mem;
+use log::warn;
 use rsdp::Rsdp;
 
-/// Result type used by error-returning functions.
-pub type AcpiResult<T> = Result<T, AcpiError>;
-
-/// All types representing ACPI tables should implement this trait.
-///
-/// ### Safety
-///
-/// The table's memory is naively interpreted, so you must be careful in providing a type that
-/// correctly represents the table's structure. Regardless of the provided type's size, the region mapped will
-/// be the size specified in the SDT's header. Providing a table impl that is larger than this, *may* lead to
-/// page-faults, aliasing references, or derefencing uninitialized memory (the latter two being UB).
-/// This isn't forbidden, however, because some tables rely on the impl being larger than a provided SDT in some
-/// versions of ACPI (the [`ExtendedField`](crate::sdt::ExtendedField) type will be useful if you need to do
-/// this. See our [`Fadt`](crate::fadt::Fadt) type for an example of this).
-pub unsafe trait AcpiTable {
-    const SIGNATURE: Signature;
-
-    fn header(&self) -> &sdt::SdtHeader;
-
-    fn validate(&self) -> AcpiResult<()> {
-        self.header().validate(Self::SIGNATURE)
-    }
-}
-
-/// Error type used by functions that return an `AcpiResult<T>`.
-#[derive(Debug)]
-pub enum AcpiError {
-    NoValidRsdp,
-    RsdpIncorrectSignature,
-    RsdpInvalidOemId,
-    RsdpInvalidChecksum,
-
-    SdtInvalidSignature(Signature),
-    SdtInvalidOemId(Signature),
-    SdtInvalidTableId(Signature),
-    SdtInvalidChecksum(Signature),
-
-    TableMissing(Signature),
-    InvalidFacsAddress,
-    InvalidDsdtAddress,
-    InvalidMadt(MadtError),
-    InvalidGenericAddress,
-
-    AllocError,
-}
-
-macro_rules! read_root_table {
-    ($signature_name:ident, $address:ident, $acpi_handler:ident) => {{
-        #[repr(transparent)]
-        struct RootTable {
-            header: SdtHeader,
-        }
-
-        unsafe impl AcpiTable for RootTable {
-            const SIGNATURE: Signature = Signature::$signature_name;
-
-            fn header(&self) -> &SdtHeader {
-                &self.header
-            }
-        }
-
-        // Map and validate root table
-        // SAFETY: Addresses from a validated RSDP are also guaranteed to be valid.
-        let table_mapping = unsafe { read_table::<_, RootTable>($acpi_handler.clone(), $address) }?;
-
-        // Convert `table_mapping` to header mapping for storage
-        // Avoid requesting table unmap twice (from both original and converted `table_mapping`s)
-        let table_mapping = mem::ManuallyDrop::new(table_mapping);
-        // SAFETY: `SdtHeader` is equivalent to `Sdt` memory-wise
-        let table_mapping = unsafe {
-            PhysicalMapping::new(
-                table_mapping.physical_start(),
-                table_mapping.virtual_start().cast::<SdtHeader>(),
-                table_mapping.region_length(),
-                table_mapping.mapped_length(),
-                $acpi_handler.clone(),
-            )
-        };
-
-        table_mapping
-    }};
-}
-
-/// `AcpiTables` represents all of the enumerable ACPI tables on the system, and is the main type
-/// to construct and use from the crate. `PlatformInfo` provides a higher-level interface to some
-/// of the data contained in the tables, and may be easier to use for common use-cases.
-#[derive(Debug)]
+/// `AcpiTables` should be constructed after finding the RSDP or RSDT/XSDT and allows enumeration
+/// of the system's ACPI tables.
 pub struct AcpiTables<H: AcpiHandler> {
-    mapping: PhysicalMapping<H, SdtHeader>,
-    revision: u8,
+    rsdt_mapping: PhysicalMapping<H, SdtHeader>,
+    pub rsdp_revision: u8,
     handler: H,
 }
 
@@ -188,322 +66,183 @@ impl<H> AcpiTables<H>
 where
     H: AcpiHandler,
 {
-    /// Create an `AcpiTables` if you have the physical address of the RSDP.
-    ///
-    /// ### Safety
-    ///
-    /// Caller must ensure the provided address is valid to read as an RSDP.
-    pub unsafe fn from_rsdp(handler: H, address: usize) -> AcpiResult<Self> {
-        let rsdp_mapping = unsafe { handler.map_physical_region::<Rsdp>(address, mem::size_of::<Rsdp>()) };
-        rsdp_mapping.validate()?;
-
-        // Safety: RSDP has been validated.
-        unsafe { Self::from_validated_rsdp(handler, rsdp_mapping) }
-    }
-
-    /// Search for the RSDP on a BIOS platform. This accesses BIOS-specific memory locations and will probably not
-    /// work on UEFI platforms. See [`Rsdp::search_for_on_bios`] for details.
-    /// details.
-    ///
-    /// ### Safety
-    ///
-    /// The caller must ensure that this function is called on BIOS platforms.
-    pub unsafe fn search_for_rsdp_bios(handler: H) -> AcpiResult<Self> {
-        let rsdp_mapping = unsafe { Rsdp::search_for_on_bios(handler.clone())? };
-        // Safety: RSDP has been validated from `Rsdp::search_for_on_bios`
-        unsafe { Self::from_validated_rsdp(handler, rsdp_mapping) }
-    }
-
-    /// Create an `AcpiTables` if you have a `PhysicalMapping` of the RSDP that you know is correct. This is called
-    /// from `from_rsdp` after validation, but can also be used if you've searched for the RSDP manually on a BIOS
-    /// system.
-    ///
-    /// ### Safety
-    ///
-    /// Caller must ensure that the provided mapping is a fully validated RSDP.
-    pub unsafe fn from_validated_rsdp(handler: H, rsdp_mapping: PhysicalMapping<H, Rsdp>) -> AcpiResult<Self> {
-        let revision = rsdp_mapping.revision();
-        let root_table_mapping = if revision == 0 {
-            /*
-             * We're running on ACPI Version 1.0. We should use the 32-bit RSDT address.
-             */
-            let table_phys_start = rsdp_mapping.rsdt_address() as usize;
-            drop(rsdp_mapping);
-            read_root_table!(RSDT, table_phys_start, handler)
-        } else {
-            /*
-             * We're running on ACPI Version 2.0+. We should use the 64-bit XSDT address, truncated
-             * to 32 bits on x86.
-             */
-            let table_phys_start = rsdp_mapping.xsdt_address() as usize;
-            drop(rsdp_mapping);
-            read_root_table!(XSDT, table_phys_start, handler)
-        };
-
-        Ok(Self { mapping: root_table_mapping, revision, handler })
-    }
-
-    /// Create an `AcpiTables` if you have the physical address of the RSDT/XSDT.
-    ///
-    /// ### Safety
-    ///
-    /// Caller must ensure the provided address is valid RSDT/XSDT address.
-    pub unsafe fn from_rsdt(handler: H, revision: u8, address: usize) -> AcpiResult<Self> {
-        let root_table_mapping = if revision == 0 {
-            /*
-             * We're running on ACPI Version 1.0. We should use the 32-bit RSDT address.
-             */
+    /// Construct an `AcpiTables` from the **physical** address of the RSDP.
+    pub unsafe fn from_rsdp(handler: H, rsdp_address: usize) -> Result<AcpiTables<H>, AcpiError> {
+        let rsdp_mapping = unsafe { handler.map_physical_region::<Rsdp>(rsdp_address, mem::size_of::<Rsdp>()) };
+
+        /*
+         * If the address given does not have a correct RSDP signature, the user has probably given
+         * us an invalid address, and we should not continue. We're more lenient with other errors
+         * as it's probably a real RSDP and the firmware developers are just lazy.
+         */
+        match rsdp_mapping.validate() {
+            Ok(()) => (),
+            Err(AcpiError::RsdpIncorrectSignature) => return Err(AcpiError::RsdpIncorrectSignature),
+            Err(AcpiError::RsdpInvalidOemId) | Err(AcpiError::RsdpInvalidChecksum) => {
+                warn!("RSDP has invalid checksum or OEM ID. Continuing.");
+            }
+            Err(_) => (),
+        }
 
-            read_root_table!(RSDT, address, handler)
+        let rsdp_revision = rsdp_mapping.revision();
+        let rsdt_address = if rsdp_revision == 0 {
+            // We're running on ACPI Version 1.0. We should use the 32-bit RSDT address.
+            rsdp_mapping.rsdt_address() as usize
         } else {
             /*
              * We're running on ACPI Version 2.0+. We should use the 64-bit XSDT address, truncated
              * to 32 bits on x86.
              */
-
-            read_root_table!(XSDT, address, handler)
+            rsdp_mapping.xsdt_address() as usize
         };
 
-        Ok(Self { mapping: root_table_mapping, revision, handler })
-    }
-
-    /// The ACPI revision of the tables enumerated by this structure.
-    #[inline]
-    pub const fn revision(&self) -> u8 {
-        self.revision
-    }
-
-    /// Constructs a [`TablesPhysPtrsIter`] over this table.
-    fn tables_phys_ptrs(&self) -> TablesPhysPtrsIter<'_> {
-        // SAFETY: The virtual address of the array of pointers follows the virtual address of the table in memory.
-        let ptrs_virt_start = unsafe { self.mapping.virtual_start().as_ptr().add(1).cast::<u8>() };
-        let ptrs_bytes_len = self.mapping.region_length() - mem::size_of::<SdtHeader>();
-        // SAFETY: `ptrs_virt_start` points to an array of `ptrs_bytes_len` bytes that lives as long as `self`.
-        let ptrs_bytes = unsafe { core::slice::from_raw_parts(ptrs_virt_start, ptrs_bytes_len) };
-        let ptr_size = if self.revision == 0 {
-            4 // RSDT entry size
-        } else {
-            8 // XSDT entry size
-        };
-
-        ptrs_bytes.chunks(ptr_size).map(|ptr_bytes_src| {
-            // Construct a native pointer using as many bytes as required from `ptr_bytes_src` (note that ACPI is
-            // little-endian)
-
-            let mut ptr_bytes_dst = [0; mem::size_of::<usize>()];
-            let common_ptr_size = usize::min(mem::size_of::<usize>(), ptr_bytes_src.len());
-            ptr_bytes_dst[..common_ptr_size].copy_from_slice(&ptr_bytes_src[..common_ptr_size]);
-
-            usize::from_le_bytes(ptr_bytes_dst) as *const SdtHeader
-        })
-    }
-
-    /// Searches through the ACPI table headers and attempts to locate the table with a matching `T::SIGNATURE`.
-    pub fn find_table<T: AcpiTable>(&self) -> AcpiResult<PhysicalMapping<H, T>> {
-        self.tables_phys_ptrs()
-            .find_map(|table_phys_ptr| {
-                // SAFETY: Table guarantees its contained addresses to be valid.
-                match unsafe { read_table(self.handler.clone(), table_phys_ptr as usize) } {
-                    Ok(table_mapping) => Some(table_mapping),
-                    Err(AcpiError::SdtInvalidSignature(_)) => None,
-                    Err(e) => {
-                        log::warn!(
-                            "Found invalid {} table at physical address {:p}: {:?}",
-                            T::SIGNATURE,
-                            table_phys_ptr,
-                            e
-                        );
-
-                        None
-                    }
-                }
-            })
-            .ok_or(AcpiError::TableMissing(T::SIGNATURE))
-    }
-
-    /// Iterates through all of the table headers.
-    pub fn headers(&self) -> SdtHeaderIterator<'_, H> {
-        SdtHeaderIterator { tables_phys_ptrs: self.tables_phys_ptrs(), handler: self.handler.clone() }
-    }
-
-    /// Finds and returns the DSDT AML table, if it exists.
-    pub fn dsdt(&self) -> AcpiResult<AmlTable> {
-        self.find_table::<fadt::Fadt>().and_then(|fadt| {
-            #[repr(transparent)]
-            struct Dsdt {
-                header: SdtHeader,
-            }
-
-            // Safety: Implementation properly represents a valid DSDT.
-            unsafe impl AcpiTable for Dsdt {
-                const SIGNATURE: Signature = Signature::DSDT;
-
-                fn header(&self) -> &SdtHeader {
-                    &self.header
+        unsafe { Self::from_rsdt(handler, rsdp_revision, rsdt_address) }
+    }
+
+    /// Construct an `AcpiTables` from the **physical** address of the RSDT/XSDT, and the revision
+    /// found in the RSDP.
+    pub unsafe fn from_rsdt(
+        handler: H,
+        rsdp_revision: u8,
+        rsdt_address: usize,
+    ) -> Result<AcpiTables<H>, AcpiError> {
+        let rsdt_mapping =
+            unsafe { handler.map_physical_region::<SdtHeader>(rsdt_address, mem::size_of::<SdtHeader>()) };
+        let rsdt_length = rsdt_mapping.length;
+        let rsdt_mapping = unsafe { handler.map_physical_region::<SdtHeader>(rsdt_address, rsdt_length as usize) };
+        Ok(Self { rsdt_mapping, rsdp_revision, handler })
+    }
+
+    /// Iterate over the **physical** addresses of the SDTs.
+    pub fn table_entries(&self) -> impl Iterator<Item = usize> {
+        let entry_size = if self.rsdp_revision == 0 { 4 } else { 8 };
+        let mut table_entries_ptr =
+            unsafe { self.rsdt_mapping.virtual_start().as_ptr().byte_add(mem::size_of::<SdtHeader>()) }
+                .cast::<u8>();
+        let mut num_entries = (self.rsdt_mapping.region_length() - mem::size_of::<SdtHeader>()) / entry_size;
+
+        core::iter::from_fn(move || {
+            if num_entries > 0 {
+                unsafe {
+                    let entry = if entry_size == 4 {
+                        *table_entries_ptr.cast::<u32>() as usize
+                    } else {
+                        *table_entries_ptr.cast::<u64>() as usize
+                    };
+                    table_entries_ptr = table_entries_ptr.byte_add(entry_size);
+                    num_entries -= 1;
+
+                    Some(entry)
                 }
+            } else {
+                None
             }
-
-            let dsdt_address = fadt.dsdt_address()?;
-            let dsdt = unsafe { read_table::<H, Dsdt>(self.handler.clone(), dsdt_address)? };
-
-            Ok(AmlTable::new(dsdt_address, dsdt.header().length, dsdt.header().revision))
         })
     }
 
-    /// Iterates through all of the SSDT tables.
-    pub fn ssdts(&self) -> SsdtIterator<H> {
-        SsdtIterator { tables_phys_ptrs: self.tables_phys_ptrs(), handler: self.handler.clone() }
+    /// Iterate over the headers of each SDT, along with their **physical** addresses.
+    pub fn table_headers(&self) -> impl Iterator<Item = (usize, SdtHeader)> {
+        self.table_entries().map(|table_phys_address| {
+            let mapping = unsafe {
+                self.handler.map_physical_region::<SdtHeader>(table_phys_address, mem::size_of::<SdtHeader>())
+            };
+            (table_phys_address, *mapping)
+        })
     }
 
-    /// Convenience method for contructing a [`PlatformInfo`]. This is one of the first things you should usually do
-    /// with an `AcpiTables`, and allows to collect helpful information about the platform from the ACPI tables.
-    ///
-    /// Like [`platform_info_in`](Self::platform_info_in), but uses the global allocator.
-    #[cfg(feature = "alloc")]
-    pub fn platform_info(&self) -> AcpiResult<PlatformInfo<alloc::alloc::Global>> {
-        PlatformInfo::new(self)
+    /// Find all tables with the signature `T::SIGNATURE`.
+    pub fn find_tables<T>(&self) -> impl Iterator<Item = PhysicalMapping<H, T>>
+    where
+        T: AcpiTable,
+    {
+        self.table_entries().filter_map(|table_phys_address| {
+            let header_mapping = unsafe {
+                self.handler.map_physical_region::<SdtHeader>(table_phys_address, mem::size_of::<SdtHeader>())
+            };
+            if header_mapping.signature == T::SIGNATURE {
+                // Extend the mapping to the entire table
+                let length = header_mapping.length;
+                drop(header_mapping);
+                Some(unsafe { self.handler.map_physical_region::<T>(table_phys_address, length as usize) })
+            } else {
+                None
+            }
+        })
     }
 
-    /// Construct a [`PlatformInfo`]. This is one of the first things you should usually do
-    /// with an `AcpiTables`, and allows to collect helpful information about the platform from the ACPI tables.
-    pub fn platform_info_in<A>(&self, allocator: A) -> AcpiResult<PlatformInfo<A>>
+    /// Find the first table with the signature `T::SIGNATURE`.
+    pub fn find_table<T>(&self) -> Option<PhysicalMapping<H, T>>
     where
-        A: Allocator + Clone,
+        T: AcpiTable,
     {
-        PlatformInfo::new_in(self, allocator)
+        self.find_tables().next()
     }
-}
 
-#[derive(Debug)]
-pub struct Sdt {
-    /// Physical address of the start of the SDT, including the header.
-    pub physical_address: usize,
-    /// Length of the table in bytes.
-    pub length: u32,
-    /// Whether this SDT has been validated. This is set to `true` the first time it is mapped and validated.
-    pub validated: bool,
+    pub fn dsdt(&self) -> Result<AmlTable, AcpiError> {
+        let Some(fadt) = self.find_table::<sdt::fadt::Fadt>() else {
+            Err(AcpiError::TableNotFound(Signature::FADT))?
+        };
+        let phys_address = fadt.dsdt_address()?;
+        let header =
+            unsafe { self.handler.map_physical_region::<SdtHeader>(phys_address, mem::size_of::<SdtHeader>()) };
+        Ok(AmlTable { phys_address, length: header.length, revision: header.revision })
+    }
+
+    pub fn ssdts(&self) -> impl Iterator<Item = AmlTable> {
+        self.table_headers().filter_map(|(phys_address, header)| {
+            if header.signature == Signature::SSDT {
+                Some(AmlTable { phys_address, length: header.length, revision: header.revision })
+            } else {
+                None
+            }
+        })
+    }
 }
 
-/// An iterator over the physical table addresses in an RSDT or XSDT.
-type TablesPhysPtrsIter<'t> = core::iter::Map<core::slice::Chunks<'t, u8>, fn(&[u8]) -> *const SdtHeader>;
-
-#[derive(Debug)]
+#[derive(Clone, Copy, Debug)]
 pub struct AmlTable {
-    /// Physical address of the start of the AML stream (excluding the table header).
-    pub address: usize,
-    /// Length (in bytes) of the AML stream.
+    /// The physical address of the start of the table. Add `mem::size_of::<SdtHeader>()` to this
+    /// to get the physical address of the start of the AML stream.
+    pub phys_address: usize,
+    /// The length of the table, including the header.
     pub length: u32,
     pub revision: u8,
 }
 
-impl AmlTable {
-    /// Create an `AmlTable` from the address and length of the table **including the SDT header**.
-    pub(crate) fn new(address: usize, length: u32, revision: u8) -> AmlTable {
-        AmlTable {
-            address: address + mem::size_of::<SdtHeader>(),
-            length: length - mem::size_of::<SdtHeader>() as u32,
-            revision,
-        }
-    }
-}
-
-/// ### Safety
+/// All types representing ACPI tables should implement this trait.
 ///
-/// Caller must ensure the provided address is valid for being read as an `SdtHeader`.
-unsafe fn read_table<H: AcpiHandler, T: AcpiTable>(
-    handler: H,
-    address: usize,
-) -> AcpiResult<PhysicalMapping<H, T>> {
-    // Attempt to peek at the SDT header to correctly enumerate the entire table.
-
-    // SAFETY: `address` needs to be valid for the size of `SdtHeader`, or the ACPI tables are malformed (not a
-    // software issue).
-    let header_mapping = unsafe { handler.map_physical_region::<SdtHeader>(address, mem::size_of::<SdtHeader>()) };
-
-    SdtHeader::validate_lazy(header_mapping, handler)
-}
-
-/// Iterator that steps through all of the tables, and returns only the SSDTs as `AmlTable`s.
-pub struct SsdtIterator<'t, H>
-where
-    H: AcpiHandler,
-{
-    tables_phys_ptrs: TablesPhysPtrsIter<'t>,
-    handler: H,
-}
-
-impl<H> Iterator for SsdtIterator<'_, H>
-where
-    H: AcpiHandler,
-{
-    type Item = AmlTable;
-
-    fn next(&mut self) -> Option<Self::Item> {
-        #[repr(transparent)]
-        struct Ssdt {
-            header: SdtHeader,
-        }
-
-        // SAFETY: Implementation properly represents a valid SSDT.
-        unsafe impl AcpiTable for Ssdt {
-            const SIGNATURE: Signature = Signature::SSDT;
-
-            fn header(&self) -> &SdtHeader {
-                &self.header
-            }
-        }
-
-        // Borrow single field for closure to avoid immutable reference to `self` that inhibits `find_map`
-        let handler = &self.handler;
+/// ### Safety
+/// The table's memory is naively interpreted, so you must be careful in providing a type that
+/// correctly represents the table's structure. Regardless of the provided type's size, the region mapped will
+/// be the size specified in the SDT's header. If a table's definition may be larger than a valid
+/// SDT's size, [`ExtendedField`](sdt::ExtendedField) should be used to define fields that may or
+/// may not exist.
+pub unsafe trait AcpiTable {
+    const SIGNATURE: Signature;
 
-        // Consume iterator until next valid SSDT and return the latter
-        self.tables_phys_ptrs.find_map(|table_phys_ptr| {
-            // SAFETY: Table guarantees its contained addresses to be valid.
-            match unsafe { read_table::<_, Ssdt>(handler.clone(), table_phys_ptr as usize) } {
-                Ok(ssdt_mapping) => Some(AmlTable::new(
-                    ssdt_mapping.physical_start(),
-                    ssdt_mapping.header.length,
-                    ssdt_mapping.header.revision,
-                )),
-                Err(AcpiError::SdtInvalidSignature(_)) => None,
-                Err(e) => {
-                    log::warn!("Found invalid SSDT at physical address {:p}: {:?}", table_phys_ptr, e);
+    fn header(&self) -> &SdtHeader;
 
-                    None
-                }
-            }
-        })
+    fn validate(&self) -> Result<(), AcpiError> {
+        unsafe { self.header().validate(Self::SIGNATURE) }
     }
 }
 
-pub struct SdtHeaderIterator<'t, H>
-where
-    H: AcpiHandler,
-{
-    tables_phys_ptrs: TablesPhysPtrsIter<'t>,
-    handler: H,
-}
+#[derive(Clone, Copy, Debug)]
+pub enum AcpiError {
+    NoValidRsdp,
+    RsdpIncorrectSignature,
+    RsdpInvalidOemId,
+    RsdpInvalidChecksum,
 
-impl<H> Iterator for SdtHeaderIterator<'_, H>
-where
-    H: AcpiHandler,
-{
-    type Item = SdtHeader;
+    SdtInvalidSignature(Signature),
+    SdtInvalidOemId(Signature),
+    SdtInvalidTableId(Signature),
+    SdtInvalidChecksum(Signature),
+    SdtInvalidCreatorId(Signature),
 
-    fn next(&mut self) -> Option<Self::Item> {
-        loop {
-            let table_phys_ptr = self.tables_phys_ptrs.next()?;
-            // SAFETY: `address` needs to be valid for the size of `SdtHeader`, or the ACPI tables are malformed (not a
-            // software issue).
-            let header_mapping = unsafe {
-                self.handler.map_physical_region::<SdtHeader>(table_phys_ptr as usize, mem::size_of::<SdtHeader>())
-            };
-            let r = header_mapping.validate(header_mapping.signature);
-            if r.is_err() {
-                log::warn!("Found invalid SDT at physical address {:p}: {:?}", table_phys_ptr, r);
-                continue;
-            }
-            return Some(*header_mapping);
-        }
-    }
+    TableNotFound(Signature),
+    InvalidFacsAddress,
+    InvalidDsdtAddress,
+    InvalidMadt(MadtError),
+    InvalidGenericAddress,
 }
diff --git a/acpi/src/managed_slice.rs b/acpi/src/managed_slice.rs
deleted file mode 100644
index e035b747..00000000
--- a/acpi/src/managed_slice.rs
+++ /dev/null
@@ -1,81 +0,0 @@
-use crate::{AcpiError, AcpiResult};
-use core::{
-    alloc::{Allocator, Layout},
-    ptr::NonNull,
-};
-
-/// Thin wrapper around a regular slice, taking a reference to an allocator for automatic
-/// deallocation when the slice is dropped out of scope.
-#[derive(Debug)]
-pub struct ManagedSlice<T, A>
-where
-    A: Allocator,
-{
-    inner: *mut T,
-    len: usize,
-    allocator: A,
-}
-
-unsafe impl<T: Send, A: Allocator> Send for ManagedSlice<T, A> {}
-unsafe impl<T: Sync, A: Allocator> Sync for ManagedSlice<T, A> {}
-
-impl<T, A> ManagedSlice<T, A>
-where
-    A: Allocator,
-{
-    /// Attempt to allocate a new `ManagedSlice` that holds `len` `T`s.
-    pub fn new_in(len: usize, allocator: A) -> AcpiResult<Self> {
-        let layout = Layout::array::<T>(len).map_err(|_| AcpiError::AllocError)?;
-        match allocator.allocate(layout) {
-            Ok(mut ptr) => Ok(ManagedSlice { inner: unsafe { ptr.as_mut().as_mut_ptr().cast() }, len, allocator }),
-            Err(_) => Err(AcpiError::AllocError),
-        }
-    }
-}
-
-#[cfg(feature = "alloc")]
-impl<T> ManagedSlice<T, alloc::alloc::Global> {
-    pub fn new(len: usize) -> AcpiResult<Self> {
-        Self::new_in(len, alloc::alloc::Global)
-    }
-}
-
-impl<T, A> Drop for ManagedSlice<T, A>
-where
-    A: Allocator,
-{
-    fn drop(&mut self) {
-        unsafe {
-            let layout = Layout::array::<T>(self.len).unwrap();
-            self.allocator.deallocate(NonNull::new_unchecked(self.inner as *mut u8), layout);
-        }
-    }
-}
-
-impl<T, A> core::ops::Deref for ManagedSlice<T, A>
-where
-    A: Allocator,
-{
-    type Target = [T];
-
-    fn deref(&self) -> &Self::Target {
-        unsafe { core::slice::from_raw_parts(self.inner, self.len) }
-    }
-}
-
-impl<T, A> core::ops::DerefMut for ManagedSlice<T, A>
-where
-    A: Allocator,
-{
-    fn deref_mut(&mut self) -> &mut Self::Target {
-        unsafe { core::slice::from_raw_parts_mut(self.inner, self.len) }
-    }
-}
-
-impl<T: Clone, A: Allocator + Clone> Clone for ManagedSlice<T, A> {
-    fn clone(&self) -> Self {
-        let mut new_managed_slice = ManagedSlice::new_in(self.len(), self.allocator.clone()).unwrap();
-        new_managed_slice.clone_from_slice(self);
-        new_managed_slice
-    }
-}
diff --git a/acpi/src/mcfg.rs b/acpi/src/mcfg.rs
deleted file mode 100644
index 86d96816..00000000
--- a/acpi/src/mcfg.rs
+++ /dev/null
@@ -1,151 +0,0 @@
-use crate::{
-    managed_slice::ManagedSlice,
-    sdt::{SdtHeader, Signature},
-    AcpiHandler,
-    AcpiResult,
-    AcpiTable,
-    AcpiTables,
-};
-use core::{alloc::Allocator, fmt, mem, ops::RangeInclusive, slice};
-
-/// Describes a set of regions of physical memory used to access the PCIe configuration space. A
-/// region is created for each entry in the MCFG. Given the segment group, bus, device number, and
-/// function of a PCIe device, the `physical_address` method on this will give you the physical
-/// address of the start of that device function's configuration space (each function has 4096
-/// bytes of configuration space in PCIe).
-pub struct PciConfigRegions<A>
-where
-    A: Allocator,
-{
-    regions: ManagedSlice<McfgEntry, A>,
-}
-
-#[cfg(feature = "alloc")]
-impl<'a> PciConfigRegions<alloc::alloc::Global> {
-    pub fn new<H>(tables: &AcpiTables<H>) -> AcpiResult<PciConfigRegions<alloc::alloc::Global>>
-    where
-        H: AcpiHandler,
-    {
-        Self::new_in(tables, alloc::alloc::Global)
-    }
-}
-
-impl<A> PciConfigRegions<A>
-where
-    A: Allocator,
-{
-    pub fn new_in<H>(tables: &AcpiTables<H>, allocator: A) -> AcpiResult<PciConfigRegions<A>>
-    where
-        H: AcpiHandler,
-    {
-        let mcfg = tables.find_table::<Mcfg>()?;
-        let mcfg_entries = mcfg.entries();
-
-        let mut regions = ManagedSlice::new_in(mcfg_entries.len(), allocator)?;
-        regions.copy_from_slice(mcfg_entries);
-
-        Ok(Self { regions })
-    }
-
-    /// Get the physical address of the start of the configuration space for a given PCIe device
-    /// function. Returns `None` if there isn't an entry in the MCFG that manages that device.
-    pub fn physical_address(&self, segment_group_no: u16, bus: u8, device: u8, function: u8) -> Option<u64> {
-        // First, find the memory region that handles this segment and bus. This method is fine
-        // because there should only be one region that handles each segment group + bus
-        // combination.
-        let region = self.regions.iter().find(|region| {
-            region.pci_segment_group == segment_group_no
-                && (region.bus_number_start..=region.bus_number_end).contains(&bus)
-        })?;
-
-        Some(
-            region.base_address
-                + ((u64::from(bus - region.bus_number_start) << 20)
-                    | (u64::from(device) << 15)
-                    | (u64::from(function) << 12)),
-        )
-    }
-
-    /// Returns an iterator providing information about the system's present PCI busses.
-    /// This is roughly equivalent to manually iterating the system's MCFG table.
-    pub fn iter(&self) -> PciConfigEntryIterator {
-        PciConfigEntryIterator { entries: &self.regions, index: 0 }
-    }
-}
-
-/// Configuration entry describing a valid bus range for the given PCI segment group.
-pub struct PciConfigEntry {
-    pub segment_group: u16,
-    pub bus_range: RangeInclusive<u8>,
-    pub physical_address: usize,
-}
-
-/// Iterator providing a [`PciConfigEntry`] for all of the valid bus ranges on the system.
-pub struct PciConfigEntryIterator<'a> {
-    entries: &'a [McfgEntry],
-    index: usize,
-}
-
-impl Iterator for PciConfigEntryIterator<'_> {
-    type Item = PciConfigEntry;
-
-    fn next(&mut self) -> Option<Self::Item> {
-        let entry = self.entries.get(self.index)?;
-        self.index += 1;
-
-        Some(PciConfigEntry {
-            segment_group: entry.pci_segment_group,
-            bus_range: entry.bus_number_start..=entry.bus_number_end,
-            physical_address: entry.base_address as usize,
-        })
-    }
-}
-
-#[repr(C, packed)]
-pub struct Mcfg {
-    pub header: SdtHeader,
-    _reserved: u64,
-    // Followed by `n` entries with format `McfgEntry`
-}
-
-/// ### Safety: Implementation properly represents a valid MCFG.
-unsafe impl AcpiTable for Mcfg {
-    const SIGNATURE: Signature = Signature::MCFG;
-
-    fn header(&self) -> &SdtHeader {
-        &self.header
-    }
-}
-
-impl Mcfg {
-    /// Returns a slice containing each of the entries in the MCFG table. Where possible, `PlatformInfo.interrupt_model` should
-    /// be enumerated instead.
-    pub fn entries(&self) -> &[McfgEntry] {
-        let length = self.header.length as usize - mem::size_of::<Mcfg>();
-
-        // Intentionally round down in case length isn't an exact multiple of McfgEntry size - this
-        // has been observed on real hardware (see rust-osdev/acpi#58)
-        let num_entries = length / mem::size_of::<McfgEntry>();
-
-        unsafe {
-            let pointer = (self as *const Mcfg as *const u8).add(mem::size_of::<Mcfg>()) as *const McfgEntry;
-            slice::from_raw_parts(pointer, num_entries)
-        }
-    }
-}
-
-impl fmt::Debug for Mcfg {
-    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
-        formatter.debug_struct("Mcfg").field("header", &self.header).field("entries", &self.entries()).finish()
-    }
-}
-
-#[derive(Clone, Copy, Debug)]
-#[repr(C, packed)]
-pub struct McfgEntry {
-    pub base_address: u64,
-    pub pci_segment_group: u16,
-    pub bus_number_start: u8,
-    pub bus_number_end: u8,
-    _reserved: u32,
-}
diff --git a/acpi/src/platform/interrupt.rs b/acpi/src/platform/interrupt.rs
index 944c16cd..b4d50ba3 100644
--- a/acpi/src/platform/interrupt.rs
+++ b/acpi/src/platform/interrupt.rs
@@ -1,5 +1,255 @@
-use crate::managed_slice::ManagedSlice;
-use core::alloc::Allocator;
+use super::{Processor, ProcessorInfo, ProcessorState};
+use crate::{
+    AcpiError,
+    AcpiHandler,
+    AcpiTables,
+    MadtError,
+    sdt::{
+        Signature,
+        madt::{Madt, MadtEntry, parse_mps_inti_flags},
+    },
+};
+use alloc::{alloc::Global, vec::Vec};
+use bit_field::BitField;
+use core::{alloc::Allocator, pin::Pin};
+
+#[derive(Debug, Clone)]
+#[non_exhaustive]
+pub enum InterruptModel<A: Allocator = Global> {
+    /// This model is only chosen when the MADT does not describe another interrupt model. On `x86_64` platforms,
+    /// this probably means only the legacy i8259 PIC is present.
+    Unknown,
+
+    /// Describes an interrupt controller based around the Advanced Programmable Interrupt Controller (any of APIC,
+    /// XAPIC, or X2APIC). These are likely to be found on x86 and x86_64 systems and are made up of a Local APIC
+    /// for each core and one or more I/O APICs to handle external interrupts.
+    Apic(Apic<A>),
+}
+
+impl InterruptModel<Global> {
+    pub fn new<H: AcpiHandler>(
+        tables: &AcpiTables<H>,
+    ) -> Result<(InterruptModel<Global>, Option<ProcessorInfo<Global>>), AcpiError> {
+        Self::new_in(tables, Global)
+    }
+}
+
+impl<A: Allocator + Clone> InterruptModel<A> {
+    pub fn new_in<H: AcpiHandler>(
+        tables: &AcpiTables<H>,
+        allocator: A,
+    ) -> Result<(InterruptModel<A>, Option<ProcessorInfo<A>>), AcpiError> {
+        let Some(madt) = tables.find_table::<Madt>() else { Err(AcpiError::TableNotFound(Signature::MADT))? };
+
+        /*
+         * We first do a pass through the MADT to determine which interrupt model is being used.
+         */
+        for entry in madt.get().entries() {
+            match entry {
+                MadtEntry::LocalApic(_)
+                | MadtEntry::LocalX2Apic(_)
+                | MadtEntry::IoApic(_)
+                | MadtEntry::InterruptSourceOverride(_)
+                | MadtEntry::LocalApicNmi(_)
+                | MadtEntry::X2ApicNmi(_)
+                | MadtEntry::LocalApicAddressOverride(_) => {
+                    return Self::from_apic_model_in(madt.get(), allocator);
+                }
+
+                MadtEntry::IoSapic(_) | MadtEntry::LocalSapic(_) | MadtEntry::PlatformInterruptSource(_) => {
+                    unimplemented!();
+                }
+
+                MadtEntry::Gicc(_)
+                | MadtEntry::Gicd(_)
+                | MadtEntry::GicMsiFrame(_)
+                | MadtEntry::GicRedistributor(_)
+                | MadtEntry::GicInterruptTranslationService(_) => {
+                    unimplemented!();
+                }
+
+                MadtEntry::NmiSource(_) => (),
+                MadtEntry::MultiprocessorWakeup(_) => (),
+            }
+        }
+
+        Ok((InterruptModel::Unknown, None))
+    }
+
+    fn from_apic_model_in(
+        madt: Pin<&Madt>,
+        allocator: A,
+    ) -> Result<(InterruptModel<A>, Option<ProcessorInfo<A>>), AcpiError> {
+        let mut local_apic_address = madt.local_apic_address as u64;
+        let mut io_apic_count = 0;
+        let mut iso_count = 0;
+        let mut nmi_source_count = 0;
+        let mut local_nmi_line_count = 0;
+        let mut processor_count = 0usize;
+
+        // Do a pass over the entries so we know how much space we should reserve in the vectors
+        for entry in madt.entries() {
+            match entry {
+                MadtEntry::IoApic(_) => io_apic_count += 1,
+                MadtEntry::InterruptSourceOverride(_) => iso_count += 1,
+                MadtEntry::NmiSource(_) => nmi_source_count += 1,
+                MadtEntry::LocalApicNmi(_) => local_nmi_line_count += 1,
+                MadtEntry::X2ApicNmi(_) => local_nmi_line_count += 1,
+                MadtEntry::LocalApic(_) => processor_count += 1,
+                MadtEntry::LocalX2Apic(_) => processor_count += 1,
+                _ => (),
+            }
+        }
+
+        let mut io_apics = Vec::with_capacity_in(io_apic_count, allocator.clone());
+        let mut interrupt_source_overrides = Vec::with_capacity_in(iso_count, allocator.clone());
+        let mut nmi_sources = Vec::with_capacity_in(nmi_source_count, allocator.clone());
+        let mut local_apic_nmi_lines = Vec::with_capacity_in(local_nmi_line_count, allocator.clone());
+        let mut application_processors = Vec::with_capacity_in(processor_count.saturating_sub(1), allocator); // Subtract one for the BSP
+        let mut boot_processor = None;
+
+        for entry in madt.entries() {
+            match entry {
+                MadtEntry::LocalApic(entry) => {
+                    /*
+                     * The first processor is the BSP. Subsequent ones are APs. If we haven't found
+                     * the BSP yet, this must be it.
+                     */
+                    let is_ap = boot_processor.is_some();
+                    let is_disabled = !{ entry.flags }.get_bit(0);
+
+                    let state = match (is_ap, is_disabled) {
+                        (_, true) => ProcessorState::Disabled,
+                        (true, false) => ProcessorState::WaitingForSipi,
+                        (false, false) => ProcessorState::Running,
+                    };
+
+                    let processor = Processor {
+                        processor_uid: entry.processor_id as u32,
+                        local_apic_id: entry.apic_id as u32,
+                        state,
+                        is_ap,
+                    };
+
+                    if is_ap {
+                        application_processors.push(processor);
+                    } else {
+                        boot_processor = Some(processor);
+                    }
+                }
+
+                MadtEntry::LocalX2Apic(entry) => {
+                    let is_ap = boot_processor.is_some();
+                    let is_disabled = !{ entry.flags }.get_bit(0);
+
+                    let state = match (is_ap, is_disabled) {
+                        (_, true) => ProcessorState::Disabled,
+                        (true, false) => ProcessorState::WaitingForSipi,
+                        (false, false) => ProcessorState::Running,
+                    };
+
+                    let processor = Processor {
+                        processor_uid: entry.processor_uid,
+                        local_apic_id: entry.x2apic_id,
+                        state,
+                        is_ap,
+                    };
+
+                    if is_ap {
+                        application_processors.push(processor);
+                    } else {
+                        boot_processor = Some(processor);
+                    }
+                }
+
+                MadtEntry::IoApic(entry) => {
+                    io_apics.push(IoApic {
+                        id: entry.io_apic_id,
+                        address: entry.io_apic_address,
+                        global_system_interrupt_base: entry.global_system_interrupt_base,
+                    });
+                }
+
+                MadtEntry::InterruptSourceOverride(entry) => {
+                    if entry.bus != 0 {
+                        return Err(AcpiError::InvalidMadt(MadtError::InterruptOverrideEntryHasInvalidBus));
+                    }
+
+                    let (polarity, trigger_mode) = parse_mps_inti_flags(entry.flags)?;
+
+                    interrupt_source_overrides.push(InterruptSourceOverride {
+                        isa_source: entry.irq,
+                        global_system_interrupt: entry.global_system_interrupt,
+                        polarity,
+                        trigger_mode,
+                    });
+                }
+
+                MadtEntry::NmiSource(entry) => {
+                    let (polarity, trigger_mode) = parse_mps_inti_flags(entry.flags)?;
+
+                    nmi_sources.push(NmiSource {
+                        global_system_interrupt: entry.global_system_interrupt,
+                        polarity,
+                        trigger_mode,
+                    });
+                }
+
+                MadtEntry::LocalApicNmi(entry) => {
+                    local_apic_nmi_lines.push(NmiLine {
+                        processor: if entry.processor_id == 0xff {
+                            NmiProcessor::All
+                        } else {
+                            NmiProcessor::ProcessorUid(entry.processor_id as u32)
+                        },
+                        line: match entry.nmi_line {
+                            0 => LocalInterruptLine::Lint0,
+                            1 => LocalInterruptLine::Lint1,
+                            _ => return Err(AcpiError::InvalidMadt(MadtError::InvalidLocalNmiLine)),
+                        },
+                    });
+                }
+
+                MadtEntry::X2ApicNmi(entry) => {
+                    local_apic_nmi_lines.push(NmiLine {
+                        processor: if entry.processor_uid == 0xffffffff {
+                            NmiProcessor::All
+                        } else {
+                            NmiProcessor::ProcessorUid(entry.processor_uid)
+                        },
+                        line: match entry.nmi_line {
+                            0 => LocalInterruptLine::Lint0,
+                            1 => LocalInterruptLine::Lint1,
+                            _ => return Err(AcpiError::InvalidMadt(MadtError::InvalidLocalNmiLine)),
+                        },
+                    });
+                }
+
+                MadtEntry::LocalApicAddressOverride(entry) => {
+                    local_apic_address = entry.local_apic_address;
+                }
+
+                MadtEntry::MultiprocessorWakeup(_) => {}
+
+                _ => {
+                    return Err(AcpiError::InvalidMadt(MadtError::UnexpectedEntry));
+                }
+            }
+        }
+
+        Ok((
+            InterruptModel::Apic(Apic::new(
+                local_apic_address,
+                io_apics,
+                local_apic_nmi_lines,
+                interrupt_source_overrides,
+                nmi_sources,
+                madt.supports_8259(),
+            )),
+            Some(ProcessorInfo::new_in(boot_processor.unwrap(), application_processors)),
+        ))
+    }
+}
 
 #[derive(Debug, Clone, Copy)]
 pub struct IoApic {
@@ -77,15 +327,12 @@ pub struct NmiSource {
 }
 
 #[derive(Debug, Clone)]
-pub struct Apic<A>
-where
-    A: Allocator,
-{
+pub struct Apic<A: Allocator = Global> {
     pub local_apic_address: u64,
-    pub io_apics: ManagedSlice<IoApic, A>,
-    pub local_apic_nmi_lines: ManagedSlice<NmiLine, A>,
-    pub interrupt_source_overrides: ManagedSlice<InterruptSourceOverride, A>,
-    pub nmi_sources: ManagedSlice<NmiSource, A>,
+    pub io_apics: Vec<IoApic, A>,
+    pub local_apic_nmi_lines: Vec<NmiLine, A>,
+    pub interrupt_source_overrides: Vec<InterruptSourceOverride, A>,
+    pub nmi_sources: Vec<NmiSource, A>,
 
     /// If this field is set, you must remap and mask all the lines of the legacy PIC, even if
     /// you choose to use the APIC. It's recommended that you do this even if ACPI does not
@@ -93,16 +340,13 @@ where
     pub also_has_legacy_pics: bool,
 }
 
-impl<A> Apic<A>
-where
-    A: Allocator,
-{
+impl<A: Allocator> Apic<A> {
     pub(crate) fn new(
         local_apic_address: u64,
-        io_apics: ManagedSlice<IoApic, A>,
-        local_apic_nmi_lines: ManagedSlice<NmiLine, A>,
-        interrupt_source_overrides: ManagedSlice<InterruptSourceOverride, A>,
-        nmi_sources: ManagedSlice<NmiSource, A>,
+        io_apics: Vec<IoApic, A>,
+        local_apic_nmi_lines: Vec<NmiLine, A>,
+        interrupt_source_overrides: Vec<InterruptSourceOverride, A>,
+        nmi_sources: Vec<NmiSource, A>,
         also_has_legacy_pics: bool,
     ) -> Self {
         Self {
@@ -115,19 +359,3 @@ where
         }
     }
 }
-
-#[derive(Debug, Clone)]
-#[non_exhaustive]
-pub enum InterruptModel<A>
-where
-    A: Allocator,
-{
-    /// This model is only chosen when the MADT does not describe another interrupt model. On `x86_64` platforms,
-    /// this probably means only the legacy i8259 PIC is present.
-    Unknown,
-
-    /// Describes an interrupt controller based around the Advanced Programmable Interrupt Controller (any of APIC,
-    /// XAPIC, or X2APIC). These are likely to be found on x86 and x86_64 systems and are made up of a Local APIC
-    /// for each core and one or more I/O APICs to handle external interrupts.
-    Apic(Apic<A>),
-}
diff --git a/acpi/src/platform/mod.rs b/acpi/src/platform/mod.rs
index 5a033653..537c95c7 100644
--- a/acpi/src/platform/mod.rs
+++ b/acpi/src/platform/mod.rs
@@ -1,18 +1,23 @@
 pub mod interrupt;
+pub mod pci;
+
+pub use interrupt::InterruptModel;
+pub use pci::PciConfigRegions;
 
 use crate::{
-    address::GenericAddress,
-    fadt::Fadt,
-    madt::{Madt, MadtError, MpProtectedModeWakeupCommand, MultiprocessorWakeupMailbox},
-    managed_slice::ManagedSlice,
     AcpiError,
     AcpiHandler,
-    AcpiResult,
     AcpiTables,
     PowerProfile,
+    address::GenericAddress,
+    sdt::{
+        Signature,
+        fadt::Fadt,
+        madt::{Madt, MadtError, MpProtectedModeWakeupCommand, MultiprocessorWakeupMailbox},
+    },
 };
+use alloc::{alloc::Global, vec::Vec};
 use core::{alloc::Allocator, mem, ptr};
-use interrupt::InterruptModel;
 
 #[derive(Clone, Copy, Debug, PartialEq, Eq)]
 pub enum ProcessorState {
@@ -46,20 +51,14 @@ pub struct Processor {
 }
 
 #[derive(Debug, Clone)]
-pub struct ProcessorInfo<A>
-where
-    A: Allocator,
-{
+pub struct ProcessorInfo<A: Allocator = Global> {
     pub boot_processor: Processor,
     /// Application processors should be brought up in the order they're defined in this list.
-    pub application_processors: ManagedSlice<Processor, A>,
+    pub application_processors: Vec<Processor, A>,
 }
 
-impl<A> ProcessorInfo<A>
-where
-    A: Allocator,
-{
-    pub(crate) fn new(boot_processor: Processor, application_processors: ManagedSlice<Processor, A>) -> Self {
+impl<A: Allocator> ProcessorInfo<A> {
+    pub(crate) fn new_in(boot_processor: Processor, application_processors: Vec<Processor, A>) -> Self {
         Self { boot_processor, application_processors }
     }
 }
@@ -86,10 +85,7 @@ impl PmTimer {
 /// tables in a nice way. It requires access to the `FADT` and `MADT`. It is the easiest way to get information
 /// about the processors and interrupt controllers on a platform.
 #[derive(Debug, Clone)]
-pub struct PlatformInfo<A>
-where
-    A: Allocator,
-{
+pub struct PlatformInfo<A: Allocator = Global> {
     pub power_profile: PowerProfile,
     pub interrupt_model: InterruptModel<A>,
     /// On `x86_64` platforms that support the APIC, the processor topology must also be inferred from the
@@ -101,9 +97,8 @@ where
      */
 }
 
-#[cfg(feature = "alloc")]
-impl PlatformInfo<alloc::alloc::Global> {
-    pub fn new<H>(tables: &AcpiTables<H>) -> AcpiResult<Self>
+impl PlatformInfo<Global> {
+    pub fn new<H>(tables: &AcpiTables<H>) -> Result<Self, AcpiError>
     where
         H: AcpiHandler,
     {
@@ -111,22 +106,15 @@ impl PlatformInfo<alloc::alloc::Global> {
     }
 }
 
-impl<A> PlatformInfo<A>
-where
-    A: Allocator + Clone,
-{
-    pub fn new_in<H>(tables: &AcpiTables<H>, allocator: A) -> AcpiResult<Self>
+impl<A: Allocator + Clone> PlatformInfo<A> {
+    pub fn new_in<H>(tables: &AcpiTables<H>, allocator: A) -> Result<Self, AcpiError>
     where
         H: AcpiHandler,
     {
-        let fadt = tables.find_table::<Fadt>()?;
+        let Some(fadt) = tables.find_table::<Fadt>() else { Err(AcpiError::TableNotFound(Signature::FADT))? };
         let power_profile = fadt.power_profile();
 
-        let madt = tables.find_table::<Madt>();
-        let (interrupt_model, processor_info) = match madt {
-            Ok(madt) => madt.get().parse_interrupt_model_in(allocator)?,
-            Err(_) => (InterruptModel::Unknown, None),
-        };
+        let (interrupt_model, processor_info) = InterruptModel::new_in(&tables, allocator)?;
         let pm_timer = PmTimer::new(&fadt)?;
 
         Ok(PlatformInfo { power_profile, interrupt_model, processor_info, pm_timer })
@@ -152,7 +140,7 @@ pub unsafe fn wakeup_aps<H>(
 where
     H: AcpiHandler,
 {
-    let madt = tables.find_table::<Madt>()?;
+    let Some(madt) = tables.find_table::<Madt>() else { Err(AcpiError::TableNotFound(Signature::MADT))? };
     let mailbox_addr = madt.get().get_mpwk_mailbox_addr()?;
     let mut mpwk_mapping = unsafe {
         handler.map_physical_region::<MultiprocessorWakeupMailbox>(
diff --git a/acpi/src/platform/pci.rs b/acpi/src/platform/pci.rs
new file mode 100644
index 00000000..7870938c
--- /dev/null
+++ b/acpi/src/platform/pci.rs
@@ -0,0 +1,64 @@
+use crate::{
+    AcpiError,
+    AcpiHandler,
+    AcpiTables,
+    sdt::{
+        Signature,
+        mcfg::{Mcfg, McfgEntry},
+    },
+};
+use alloc::{
+    alloc::{Allocator, Global},
+    vec::Vec,
+};
+
+/// Describes a set of regions of physical memory used to access the PCIe configuration space. A
+/// region is created for each entry in the MCFG. Given the segment group, bus, device number, and
+/// function of a PCIe device, [`PciConfigRegions::physical_address`] will give you the physical
+/// address of the start of that device function's configuration space (each function has 4096
+/// bytes of configuration space in PCIe).
+pub struct PciConfigRegions<A: Allocator> {
+    pub regions: Vec<McfgEntry, A>,
+}
+
+impl PciConfigRegions<Global> {
+    pub fn new<H>(tables: &AcpiTables<H>) -> Result<PciConfigRegions<Global>, AcpiError>
+    where
+        H: AcpiHandler,
+    {
+        Self::new_in(tables, Global)
+    }
+}
+
+impl<A: Allocator> PciConfigRegions<A> {
+    pub fn new_in<H>(tables: &AcpiTables<H>, allocator: A) -> Result<PciConfigRegions<A>, AcpiError>
+    where
+        H: AcpiHandler,
+    {
+        let Some(mcfg) = tables.find_table::<Mcfg>() else { Err(AcpiError::TableNotFound(Signature::MCFG))? };
+        let regions = mcfg.entries().to_vec_in(allocator);
+
+        Ok(Self { regions })
+    }
+
+    /// Get the **physical** address of the start of the configuration space for a given PCIe device
+    /// function. Returns `None` if there isn't an entry in the MCFG that manages that device.
+    pub fn physical_address(&self, segment_group_no: u16, bus: u8, device: u8, function: u8) -> Option<u64> {
+        /*
+         * First, find the memory region that handles this segment and bus. This method is fine
+         * because there should only be one region that handles each segment group + bus
+         * combination.
+         */
+        let region = self.regions.iter().find(|region| {
+            region.pci_segment_group == segment_group_no
+                && (region.bus_number_start..=region.bus_number_end).contains(&bus)
+        })?;
+
+        Some(
+            region.base_address
+                + ((u64::from(bus - region.bus_number_start) << 20)
+                    | (u64::from(device) << 15)
+                    | (u64::from(function) << 12)),
+        )
+    }
+}
diff --git a/acpi/src/rsdp.rs b/acpi/src/rsdp.rs
index 0c2eae0f..2879ee26 100644
--- a/acpi/src/rsdp.rs
+++ b/acpi/src/rsdp.rs
@@ -1,4 +1,4 @@
-use crate::{AcpiError, AcpiHandler, AcpiResult, PhysicalMapping};
+use crate::{AcpiError, AcpiHandler, PhysicalMapping};
 use core::{mem, ops::Range, slice, str};
 
 /// The size in bytes of the ACPI 1.0 RSDP.
@@ -52,7 +52,7 @@ impl Rsdp {
     ///     - ACPI v1.0 structures use `eb9d2d30-2d88-11d3-9a16-0090273fc14d`.
     ///     - ACPI v2.0 or later structures use `8868e871-e4f1-11d3-bc22-0080c73c8881`.
     /// You should search the entire table for the v2.0 GUID before searching for the v1.0 one.
-    pub unsafe fn search_for_on_bios<H>(handler: H) -> AcpiResult<PhysicalMapping<H, Rsdp>>
+    pub unsafe fn search_for_on_bios<H>(handler: H) -> Result<PhysicalMapping<H, Rsdp>, AcpiError>
     where
         H: AcpiHandler,
     {
@@ -101,13 +101,13 @@ impl Rsdp {
     ///     1) The signature is correct
     ///     2) The checksum is correct
     ///     3) For Version 2.0+, that the extension checksum is correct
-    pub fn validate(&self) -> AcpiResult<()> {
+    pub fn validate(&self) -> Result<(), AcpiError> {
         // Check the signature
         if self.signature != RSDP_SIGNATURE {
             return Err(AcpiError::RsdpIncorrectSignature);
         }
 
-        // Check the OEM id is valid UTF8 (allows use of unwrap)
+        // Check the OEM id is valid UTF-8
         if str::from_utf8(&self.oem_id).is_err() {
             return Err(AcpiError::RsdpInvalidOemId);
         }
@@ -141,8 +141,8 @@ impl Rsdp {
         self.checksum
     }
 
-    pub fn oem_id(&self) -> &str {
-        str::from_utf8(&self.oem_id).unwrap()
+    pub fn oem_id(&self) -> Result<&str, AcpiError> {
+        str::from_utf8(&self.oem_id).map_err(|_| AcpiError::RsdpInvalidOemId)
     }
 
     pub fn revision(&self) -> u8 {
diff --git a/acpi/src/bgrt.rs b/acpi/src/sdt/bgrt.rs
similarity index 88%
rename from acpi/src/bgrt.rs
rename to acpi/src/sdt/bgrt.rs
index 4eeadc84..2b82b8cd 100644
--- a/acpi/src/bgrt.rs
+++ b/acpi/src/sdt/bgrt.rs
@@ -1,6 +1,6 @@
 use crate::{
-    sdt::{SdtHeader, Signature},
     AcpiTable,
+    sdt::{SdtHeader, Signature},
 };
 use bit_field::BitField;
 
@@ -18,7 +18,6 @@ pub struct Bgrt {
     pub image_offset_y: u32,
 }
 
-/// ### Safety: Implementation properly represents a valid BGRT.
 unsafe impl AcpiTable for Bgrt {
     const SIGNATURE: Signature = Signature::BGRT;
 
@@ -37,7 +36,7 @@ impl Bgrt {
     }
 
     /// Gets the orientation offset of the image.
-    /// Degrees are clockwise from the images default orientation.
+    /// Degrees are clockwise from the image's default orientation.
     pub fn orientation_offset(&self) -> u16 {
         let status = self.status;
         match status.get_bits(1..3) {
@@ -45,7 +44,7 @@ impl Bgrt {
             1 => 90,
             2 => 180,
             3 => 270,
-            _ => unreachable!(), // will never happen
+            _ => unreachable!(),
         }
     }
 
diff --git a/acpi/src/fadt.rs b/acpi/src/sdt/fadt.rs
similarity index 99%
rename from acpi/src/fadt.rs
rename to acpi/src/sdt/fadt.rs
index 8f8793f6..c2619c67 100644
--- a/acpi/src/fadt.rs
+++ b/acpi/src/sdt/fadt.rs
@@ -1,8 +1,8 @@
 use crate::{
-    address::{AccessSize, AddressSpace, GenericAddress, RawGenericAddress},
-    sdt::{ExtendedField, SdtHeader, Signature},
     AcpiError,
     AcpiTable,
+    address::{AccessSize, AddressSpace, GenericAddress, RawGenericAddress},
+    sdt::{ExtendedField, SdtHeader, Signature},
 };
 use bit_field::BitField;
 
@@ -126,7 +126,7 @@ unsafe impl AcpiTable for Fadt {
 
 impl Fadt {
     pub fn validate(&self) -> Result<(), AcpiError> {
-        self.header.validate(crate::sdt::Signature::FADT)
+        unsafe { self.header.validate(crate::sdt::Signature::FADT) }
     }
 
     pub fn facs_address(&self) -> Result<usize, AcpiError> {
diff --git a/acpi/src/hpet.rs b/acpi/src/sdt/hpet.rs
similarity index 90%
rename from acpi/src/hpet.rs
rename to acpi/src/sdt/hpet.rs
index 02033515..a558d7fe 100644
--- a/acpi/src/hpet.rs
+++ b/acpi/src/sdt/hpet.rs
@@ -1,12 +1,13 @@
 use crate::{
-    address::RawGenericAddress,
-    sdt::{SdtHeader, Signature},
     AcpiError,
     AcpiHandler,
     AcpiTable,
     AcpiTables,
+    address::RawGenericAddress,
+    sdt::{SdtHeader, Signature},
 };
 use bit_field::BitField;
+use log::warn;
 
 #[derive(Debug)]
 pub enum PageProtection {
@@ -38,10 +39,11 @@ impl HpetInfo {
     where
         H: AcpiHandler,
     {
-        let hpet = tables.find_table::<HpetTable>()?;
+        let Some(hpet) = tables.find_table::<HpetTable>() else { Err(AcpiError::TableNotFound(Signature::HPET))? };
 
-        // Make sure the HPET is in system memory
-        assert_eq!(hpet.base_address.address_space, 0);
+        if hpet.base_address.address_space != 0 {
+            warn!("HPET reported as not in system memory; tables invalid?");
+        }
 
         let event_timer_block_id = hpet.event_timer_block_id;
         Ok(HpetInfo {
diff --git a/acpi/src/madt.rs b/acpi/src/sdt/madt.rs
similarity index 56%
rename from acpi/src/madt.rs
rename to acpi/src/sdt/madt.rs
index 73b3729e..2d69a7e1 100644
--- a/acpi/src/madt.rs
+++ b/acpi/src/sdt/madt.rs
@@ -1,23 +1,17 @@
 use crate::{
-    managed_slice::ManagedSlice,
-    platform::{
-        interrupt::{InterruptModel, Polarity, TriggerMode},
-        ProcessorInfo,
-    },
-    sdt::{ExtendedField, SdtHeader, Signature},
     AcpiError,
-    AcpiResult,
     AcpiTable,
+    platform::interrupt::{Polarity, TriggerMode},
+    sdt::{ExtendedField, SdtHeader, Signature},
 };
 use bit_field::BitField;
 use core::{
-    alloc::Allocator,
     marker::{PhantomData, PhantomPinned},
     mem,
     pin::Pin,
 };
 
-#[derive(Debug)]
+#[derive(Clone, Copy, Debug)]
 pub enum MadtError {
     UnexpectedEntry,
     InterruptOverrideEntryHasInvalidBus,
@@ -38,8 +32,8 @@ pub enum MadtError {
 ///
 /// The MADT is a variable-sized structure consisting of a static header and then a variable number of entries.
 /// This type only contains the static portion, and then uses pointer arithmetic to parse the following entries.
-/// To make this sound, this type is `!Unpin` - this prevents you from getting anything other than a `Pin<&Madt>`
-/// out of a `PhysicalMapping`, thereby preventing a `Madt` from being moved before [`Madt::entries`] is called.
+/// To make this sound, this type is `!Unpin` - this prevents `Madt` being moved, which would leave
+/// the entries behind.
 #[repr(C, packed)]
 #[derive(Debug)]
 pub struct Madt {
@@ -58,263 +52,6 @@ unsafe impl AcpiTable for Madt {
 }
 
 impl Madt {
-    pub fn get_mpwk_mailbox_addr(self: Pin<&Self>) -> Result<u64, AcpiError> {
-        for entry in self.entries() {
-            if let MadtEntry::MultiprocessorWakeup(entry) = entry {
-                return Ok(entry.mailbox_address);
-            }
-        }
-        Err(AcpiError::InvalidMadt(MadtError::UnexpectedEntry))
-    }
-
-    pub fn parse_interrupt_model_in<A>(
-        self: Pin<&Self>,
-        allocator: A,
-    ) -> AcpiResult<(InterruptModel<A>, Option<ProcessorInfo<A>>)>
-    where
-        A: Allocator + Clone,
-    {
-        /*
-         * We first do a pass through the MADT to determine which interrupt model is being used.
-         */
-        for entry in self.entries() {
-            match entry {
-                MadtEntry::LocalApic(_) |
-                MadtEntry::LocalX2Apic(_) |
-                MadtEntry::IoApic(_) |
-                MadtEntry::InterruptSourceOverride(_) |
-                MadtEntry::NmiSource(_) |   // TODO: is this one used by more than one model?
-                MadtEntry::LocalApicNmi(_) |
-                MadtEntry::X2ApicNmi(_) |
-                MadtEntry::LocalApicAddressOverride(_) => {
-                    return self.parse_apic_model_in(allocator);
-                }
-
-                MadtEntry::IoSapic(_) |
-                MadtEntry::LocalSapic(_) |
-                MadtEntry::PlatformInterruptSource(_) => {
-                    unimplemented!();
-                }
-
-                MadtEntry::Gicc(_) |
-                MadtEntry::Gicd(_) |
-                MadtEntry::GicMsiFrame(_) |
-                MadtEntry::GicRedistributor(_) |
-                MadtEntry::GicInterruptTranslationService(_) => {
-                    unimplemented!();
-                }
-
-                MadtEntry::MultiprocessorWakeup(_) => ()
-            }
-        }
-
-        Ok((InterruptModel::Unknown, None))
-    }
-
-    fn parse_apic_model_in<A>(
-        self: Pin<&Self>,
-        allocator: A,
-    ) -> AcpiResult<(InterruptModel<A>, Option<ProcessorInfo<A>>)>
-    where
-        A: Allocator + Clone,
-    {
-        use crate::platform::{
-            interrupt::{
-                Apic,
-                InterruptSourceOverride,
-                IoApic,
-                LocalInterruptLine,
-                NmiLine,
-                NmiProcessor,
-                NmiSource,
-            },
-            Processor,
-            ProcessorState,
-        };
-
-        let mut local_apic_address = self.local_apic_address as u64;
-        let mut io_apic_count = 0;
-        let mut iso_count = 0;
-        let mut nmi_source_count = 0;
-        let mut local_nmi_line_count = 0;
-        let mut processor_count = 0usize;
-
-        // Do a pass over the entries so we know how much space we should reserve in the vectors
-        for entry in self.entries() {
-            match entry {
-                MadtEntry::IoApic(_) => io_apic_count += 1,
-                MadtEntry::InterruptSourceOverride(_) => iso_count += 1,
-                MadtEntry::NmiSource(_) => nmi_source_count += 1,
-                MadtEntry::LocalApicNmi(_) => local_nmi_line_count += 1,
-                MadtEntry::X2ApicNmi(_) => local_nmi_line_count += 1,
-                MadtEntry::LocalApic(_) => processor_count += 1,
-                MadtEntry::LocalX2Apic(_) => processor_count += 1,
-                _ => (),
-            }
-        }
-
-        let mut io_apics = ManagedSlice::new_in(io_apic_count, allocator.clone())?;
-        let mut interrupt_source_overrides = ManagedSlice::new_in(iso_count, allocator.clone())?;
-        let mut nmi_sources = ManagedSlice::new_in(nmi_source_count, allocator.clone())?;
-        let mut local_apic_nmi_lines = ManagedSlice::new_in(local_nmi_line_count, allocator.clone())?;
-        let mut application_processors = ManagedSlice::new_in(processor_count.saturating_sub(1), allocator)?; // Subtract one for the BSP
-        let mut boot_processor = None;
-
-        io_apic_count = 0;
-        iso_count = 0;
-        nmi_source_count = 0;
-        local_nmi_line_count = 0;
-        processor_count = 0;
-
-        for entry in self.entries() {
-            match entry {
-                MadtEntry::LocalApic(entry) => {
-                    /*
-                     * The first processor is the BSP. Subsequent ones are APs. If we haven't found
-                     * the BSP yet, this must be it.
-                     */
-                    let is_ap = boot_processor.is_some();
-                    let is_disabled = !{ entry.flags }.get_bit(0);
-
-                    let state = match (is_ap, is_disabled) {
-                        (_, true) => ProcessorState::Disabled,
-                        (true, false) => ProcessorState::WaitingForSipi,
-                        (false, false) => ProcessorState::Running,
-                    };
-
-                    let processor = Processor {
-                        processor_uid: entry.processor_id as u32,
-                        local_apic_id: entry.apic_id as u32,
-                        state,
-                        is_ap,
-                    };
-
-                    if is_ap {
-                        application_processors[processor_count] = processor;
-                        processor_count += 1;
-                    } else {
-                        boot_processor = Some(processor);
-                    }
-                }
-
-                MadtEntry::LocalX2Apic(entry) => {
-                    let is_ap = boot_processor.is_some();
-                    let is_disabled = !{ entry.flags }.get_bit(0);
-
-                    let state = match (is_ap, is_disabled) {
-                        (_, true) => ProcessorState::Disabled,
-                        (true, false) => ProcessorState::WaitingForSipi,
-                        (false, false) => ProcessorState::Running,
-                    };
-
-                    let processor = Processor {
-                        processor_uid: entry.processor_uid,
-                        local_apic_id: entry.x2apic_id,
-                        state,
-                        is_ap,
-                    };
-
-                    if is_ap {
-                        application_processors[processor_count] = processor;
-                        processor_count += 1;
-                    } else {
-                        boot_processor = Some(processor);
-                    }
-                }
-
-                MadtEntry::IoApic(entry) => {
-                    io_apics[io_apic_count] = IoApic {
-                        id: entry.io_apic_id,
-                        address: entry.io_apic_address,
-                        global_system_interrupt_base: entry.global_system_interrupt_base,
-                    };
-                    io_apic_count += 1;
-                }
-
-                MadtEntry::InterruptSourceOverride(entry) => {
-                    if entry.bus != 0 {
-                        return Err(AcpiError::InvalidMadt(MadtError::InterruptOverrideEntryHasInvalidBus));
-                    }
-
-                    let (polarity, trigger_mode) = parse_mps_inti_flags(entry.flags)?;
-
-                    interrupt_source_overrides[iso_count] = InterruptSourceOverride {
-                        isa_source: entry.irq,
-                        global_system_interrupt: entry.global_system_interrupt,
-                        polarity,
-                        trigger_mode,
-                    };
-                    iso_count += 1;
-                }
-
-                MadtEntry::NmiSource(entry) => {
-                    let (polarity, trigger_mode) = parse_mps_inti_flags(entry.flags)?;
-
-                    nmi_sources[nmi_source_count] = NmiSource {
-                        global_system_interrupt: entry.global_system_interrupt,
-                        polarity,
-                        trigger_mode,
-                    };
-                    nmi_source_count += 1;
-                }
-
-                MadtEntry::LocalApicNmi(entry) => {
-                    local_apic_nmi_lines[local_nmi_line_count] = NmiLine {
-                        processor: if entry.processor_id == 0xff {
-                            NmiProcessor::All
-                        } else {
-                            NmiProcessor::ProcessorUid(entry.processor_id as u32)
-                        },
-                        line: match entry.nmi_line {
-                            0 => LocalInterruptLine::Lint0,
-                            1 => LocalInterruptLine::Lint1,
-                            _ => return Err(AcpiError::InvalidMadt(MadtError::InvalidLocalNmiLine)),
-                        },
-                    };
-                    local_nmi_line_count += 1;
-                }
-
-                MadtEntry::X2ApicNmi(entry) => {
-                    local_apic_nmi_lines[local_nmi_line_count] = NmiLine {
-                        processor: if entry.processor_uid == 0xffffffff {
-                            NmiProcessor::All
-                        } else {
-                            NmiProcessor::ProcessorUid(entry.processor_uid)
-                        },
-                        line: match entry.nmi_line {
-                            0 => LocalInterruptLine::Lint0,
-                            1 => LocalInterruptLine::Lint1,
-                            _ => return Err(AcpiError::InvalidMadt(MadtError::InvalidLocalNmiLine)),
-                        },
-                    };
-                    local_nmi_line_count += 1;
-                }
-
-                MadtEntry::LocalApicAddressOverride(entry) => {
-                    local_apic_address = entry.local_apic_address;
-                }
-
-                MadtEntry::MultiprocessorWakeup(_) => {}
-
-                _ => {
-                    return Err(AcpiError::InvalidMadt(MadtError::UnexpectedEntry));
-                }
-            }
-        }
-
-        Ok((
-            InterruptModel::Apic(Apic::new(
-                local_apic_address,
-                io_apics,
-                local_apic_nmi_lines,
-                interrupt_source_overrides,
-                nmi_sources,
-                self.supports_8259(),
-            )),
-            Some(ProcessorInfo::new(boot_processor.unwrap(), application_processors)),
-        ))
-    }
-
     pub fn entries(self: Pin<&Self>) -> MadtEntryIter<'_> {
         let ptr = unsafe { Pin::into_inner_unchecked(self) as *const Madt as *const u8 };
         MadtEntryIter {
@@ -327,6 +64,15 @@ impl Madt {
     pub fn supports_8259(&self) -> bool {
         { self.flags }.get_bit(0)
     }
+
+    pub fn get_mpwk_mailbox_addr(self: Pin<&Self>) -> Result<u64, AcpiError> {
+        for entry in self.entries() {
+            if let MadtEntry::MultiprocessorWakeup(entry) = entry {
+                return Ok(entry.mailbox_address);
+            }
+        }
+        Err(AcpiError::InvalidMadt(MadtError::UnexpectedEntry))
+    }
 }
 
 #[derive(Debug)]
@@ -673,7 +419,7 @@ pub struct MultiprocessorWakeupMailbox {
     pub reserved_for_firmware: [u64; 256],
 }
 
-fn parse_mps_inti_flags(flags: u16) -> AcpiResult<(Polarity, TriggerMode)> {
+pub fn parse_mps_inti_flags(flags: u16) -> Result<(Polarity, TriggerMode), AcpiError> {
     let polarity = match flags.get_bits(0..2) {
         0b00 => Polarity::SameAsBus,
         0b01 => Polarity::ActiveHigh,
diff --git a/acpi/src/sdt/mcfg.rs b/acpi/src/sdt/mcfg.rs
new file mode 100644
index 00000000..d14024f4
--- /dev/null
+++ b/acpi/src/sdt/mcfg.rs
@@ -0,0 +1,54 @@
+use crate::{
+    AcpiTable,
+    sdt::{SdtHeader, Signature},
+};
+use core::{fmt, mem, slice};
+
+#[repr(C, packed)]
+pub struct Mcfg {
+    pub header: SdtHeader,
+    _reserved: u64,
+    // Followed by `n` entries with format `McfgEntry`
+}
+
+/// ### Safety: Implementation properly represents a valid MCFG.
+unsafe impl AcpiTable for Mcfg {
+    const SIGNATURE: Signature = Signature::MCFG;
+
+    fn header(&self) -> &SdtHeader {
+        &self.header
+    }
+}
+
+impl Mcfg {
+    /// Returns a slice containing each of the entries in the MCFG table. Where possible, `PlatformInfo.interrupt_model` should
+    /// be enumerated instead.
+    pub fn entries(&self) -> &[McfgEntry] {
+        let length = self.header.length as usize - mem::size_of::<Mcfg>();
+
+        // Intentionally round down in case length isn't an exact multiple of McfgEntry size - this
+        // has been observed on real hardware (see rust-osdev/acpi#58)
+        let num_entries = length / mem::size_of::<McfgEntry>();
+
+        unsafe {
+            let pointer = (self as *const Mcfg as *const u8).add(mem::size_of::<Mcfg>()) as *const McfgEntry;
+            slice::from_raw_parts(pointer, num_entries)
+        }
+    }
+}
+
+impl fmt::Debug for Mcfg {
+    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
+        formatter.debug_struct("Mcfg").field("header", &self.header).field("entries", &self.entries()).finish()
+    }
+}
+
+#[derive(Clone, Copy, Debug)]
+#[repr(C, packed)]
+pub struct McfgEntry {
+    pub base_address: u64,
+    pub pci_segment_group: u16,
+    pub bus_number_start: u8,
+    pub bus_number_end: u8,
+    _reserved: u32,
+}
diff --git a/acpi/src/sdt.rs b/acpi/src/sdt/mod.rs
similarity index 73%
rename from acpi/src/sdt.rs
rename to acpi/src/sdt/mod.rs
index 9d92624a..ebd69c9d 100644
--- a/acpi/src/sdt.rs
+++ b/acpi/src/sdt/mod.rs
@@ -1,4 +1,11 @@
-use crate::{AcpiError, AcpiHandler, AcpiResult, AcpiTable, PhysicalMapping};
+pub mod bgrt;
+pub mod fadt;
+pub mod hpet;
+pub mod madt;
+pub mod mcfg;
+pub mod spcr;
+
+use crate::AcpiError;
 use core::{fmt, mem::MaybeUninit, str};
 
 /// Represents a field which may or may not be present within an ACPI structure, depending on the version of ACPI
@@ -13,11 +20,7 @@ impl<T: Copy, const MIN_REVISION: u8> ExtendedField<T, MIN_REVISION> {
     /// ### Safety
     /// If a bogus ACPI version is passed, this function may access uninitialised data.
     pub unsafe fn access(&self, revision: u8) -> Option<T> {
-        if revision >= MIN_REVISION {
-            Some(unsafe { self.0.assume_init() })
-        } else {
-            None
-        }
+        if revision >= MIN_REVISION { Some(unsafe { self.0.assume_init() }) } else { None }
     }
 }
 
@@ -56,7 +59,7 @@ impl<T: Copy, const MIN_REVISION: u8> ExtendedField<T, MIN_REVISION> {
 /// * SSDT - Secondary System Description Table
 /// * XSDT - Extended System Description Table
 ///
-/// Acpi reserves the following signatures and the specifications for them can be found [here](https://uefi.org/acpi):
+/// ACPI also reserves the following signatures and the specifications for them can be found [here](https://uefi.org/acpi):
 ///
 /// * AEST - ARM Error Source Table
 /// * BDAT - BIOS Data ACPI Table
@@ -105,13 +108,19 @@ pub struct SdtHeader {
     pub oem_id: [u8; 6],
     pub oem_table_id: [u8; 8],
     pub oem_revision: u32,
-    pub creator_id: u32,
+    pub creator_id: [u8; 4],
     pub creator_revision: u32,
 }
 
 impl SdtHeader {
-    /// Whether values of header fields are permitted.
-    fn validate_header_fields(&self, signature: Signature) -> AcpiResult<()> {
+    /// Checks that:
+    /// 1. The signature matches the one given.
+    /// 2. The values of various fields in the header are allowed.
+    /// 3. The checksum of the SDT is valid.
+    ///
+    /// ### Safety
+    /// The entire `length` bytes of the SDT must be mapped to compute the checksum.
+    pub unsafe fn validate(&self, signature: Signature) -> Result<(), AcpiError> {
         // Check the signature
         if self.signature != signature || str::from_utf8(&self.signature.0).is_err() {
             return Err(AcpiError::SdtInvalidSignature(signature));
@@ -127,89 +136,53 @@ impl SdtHeader {
             return Err(AcpiError::SdtInvalidTableId(signature));
         }
 
-        Ok(())
-    }
-
-    /// Whether table is valid according to checksum.
-    fn validate_checksum(&self, signature: Signature) -> AcpiResult<()> {
-        // SAFETY: Entire table is mapped.
+        // Check the checksum
         let table_bytes =
             unsafe { core::slice::from_raw_parts((self as *const SdtHeader).cast::<u8>(), self.length as usize) };
         let sum = table_bytes.iter().fold(0u8, |sum, &byte| sum.wrapping_add(byte));
-
-        if sum == 0 {
-            Ok(())
-        } else {
-            Err(AcpiError::SdtInvalidChecksum(signature))
+        if sum != 0 {
+            return Err(AcpiError::SdtInvalidChecksum(signature));
         }
-    }
-
-    /// Checks that:
-    ///
-    /// 1. The signature matches the one given.
-    /// 2. The values of various fields in the header are allowed.
-    /// 3. The checksum of the SDT is valid.
-    ///
-    /// This assumes that the whole SDT is mapped.
-    pub fn validate(&self, signature: Signature) -> AcpiResult<()> {
-        self.validate_header_fields(signature)?;
-        self.validate_checksum(signature)?;
 
         Ok(())
     }
 
-    /// Validates header, proceeding with checking entire table and returning a [`PhysicalMapping`] to it if
-    /// successful.
-    ///
-    /// The same checks are performed as [`SdtHeader::validate`], but `header_mapping` does not have to map the
-    /// entire table when calling. This is useful to avoid completely mapping a table that will be immediately
-    /// unmapped if it does not have a particular signature or has an invalid header.
-    pub(crate) fn validate_lazy<H: AcpiHandler, T: AcpiTable>(
-        header_mapping: PhysicalMapping<H, Self>,
-        handler: H,
-    ) -> AcpiResult<PhysicalMapping<H, T>> {
-        header_mapping.validate_header_fields(T::SIGNATURE)?;
+    #[inline]
+    pub fn length(&self) -> u32 {
+        self.length
+    }
 
-        // Reuse `header_mapping` to access the rest of the table if the latter is already mapped entirely
-        let table_length = header_mapping.length as usize;
-        let table_mapping = if header_mapping.mapped_length() >= table_length {
-            // Avoid requesting table unmap twice (from both `header_mapping` and `table_mapping`)
-            let header_mapping = core::mem::ManuallyDrop::new(header_mapping);
+    #[inline]
+    pub fn revision(&self) -> u8 {
+        self.revision
+    }
 
-            // SAFETY: `header_mapping` maps entire table.
-            unsafe {
-                PhysicalMapping::new(
-                    header_mapping.physical_start(),
-                    header_mapping.virtual_start().cast::<T>(),
-                    table_length,
-                    header_mapping.mapped_length(),
-                    handler,
-                )
-            }
-        } else {
-            // Unmap header as soon as possible
-            let table_phys_start = header_mapping.physical_start();
-            drop(header_mapping);
+    #[inline]
+    pub fn checksum(&self) -> u8 {
+        self.checksum
+    }
 
-            // SAFETY: `table_phys_start` is the physical address of the header and the rest of the table.
-            unsafe { handler.map_physical_region(table_phys_start, table_length) }
-        };
+    pub fn oem_id(&self) -> Result<&str, AcpiError> {
+        str::from_utf8(&self.oem_id).map_err(|_| AcpiError::SdtInvalidOemId(self.signature))
+    }
 
-        // This is usually redundant compared to simply calling `validate_checksum` but respects custom
-        // `AcpiTable::validate` implementations.
-        table_mapping.get().validate()?;
+    pub fn oem_table_id(&self) -> Result<&str, AcpiError> {
+        str::from_utf8(&self.oem_table_id).map_err(|_| AcpiError::SdtInvalidTableId(self.signature))
+    }
 
-        Ok(table_mapping)
+    #[inline]
+    pub fn oem_revision(&self) -> u32 {
+        self.oem_revision
     }
 
-    pub fn oem_id(&self) -> &str {
-        // Safe to unwrap because checked in `validate`
-        str::from_utf8(&self.oem_id).unwrap()
+    #[inline]
+    pub fn creator_id(&self) -> Result<&str, AcpiError> {
+        str::from_utf8(&self.creator_id).map_err(|_| AcpiError::SdtInvalidCreatorId(self.signature))
     }
 
-    pub fn oem_table_id(&self) -> &str {
-        // Safe to unwrap because checked in `validate`
-        str::from_utf8(&self.oem_table_id).unwrap()
+    #[inline]
+    pub fn creator_revision(&self) -> u32 {
+        self.creator_revision
     }
 }
 
diff --git a/acpi/src/spcr.rs b/acpi/src/sdt/spcr.rs
similarity index 98%
rename from acpi/src/spcr.rs
rename to acpi/src/sdt/spcr.rs
index 1e1820c2..d5645c37 100644
--- a/acpi/src/spcr.rs
+++ b/acpi/src/sdt/spcr.rs
@@ -1,12 +1,12 @@
 use crate::{
-    address::{GenericAddress, RawGenericAddress},
-    AcpiResult,
+    AcpiError,
     AcpiTable,
     SdtHeader,
     Signature,
+    address::{GenericAddress, RawGenericAddress},
 };
 use core::{
-    num::{NonZeroU32, NonZeroU8},
+    num::{NonZeroU8, NonZeroU32},
     ptr,
     slice,
     str::{self, Utf8Error},
@@ -69,7 +69,7 @@ impl Spcr {
 
     /// The base address of the Serial Port register set, if if console
     /// redirection is enabled.
-    pub fn base_address(&self) -> Option<AcpiResult<GenericAddress>> {
+    pub fn base_address(&self) -> Option<Result<GenericAddress, AcpiError>> {
         (!self.base_address.is_empty()).then(|| GenericAddress::from_raw(self.base_address))
     }
 

From c3efed10c9e00a08fad14147f80d7313b6f8a393 Mon Sep 17 00:00:00 2001
From: Isaac Woods <isaacwoods.home@gmail.com>
Date: Mon, 24 Mar 2025 23:56:11 +0000
Subject: [PATCH 65/88] `aml`: implement `DerefOf`

---
 aml/src/lib.rs | 16 ++++++++++++++++
 1 file changed, 16 insertions(+)

diff --git a/aml/src/lib.rs b/aml/src/lib.rs
index 2515714e..ac1378c4 100644
--- a/aml/src/lib.rs
+++ b/aml/src/lib.rs
@@ -392,6 +392,22 @@ where
                         };
                         context.contribute_arg(Argument::Object(Arc::new(result)));
                     }
+                    Opcode::DerefOf => {
+                        let [Argument::Object(object)] = &op.arguments[..] else { panic!() };
+                        let result = if object.typ() == ObjectType::Reference {
+                            object.clone().unwrap_reference()
+                        } else if object.typ() == ObjectType::String {
+                            let path = AmlName::from_str(&object.as_string().unwrap())?
+                                .resolve(&context.current_scope)?;
+                            self.namespace.lock().get(path)?.clone()
+                        } else {
+                            return Err(AmlError::ObjectNotOfExpectedType {
+                                expected: ObjectType::Reference,
+                                got: object.typ(),
+                            });
+                        };
+                        context.contribute_arg(Argument::Object(result));
+                    }
                     Opcode::Sleep => {
                         let [Argument::Object(msec)] = &op.arguments[..] else { panic!() };
                         self.handler.sleep(msec.as_integer()?);

From 12f8a336975273e70ef226f9293caaae747991d3 Mon Sep 17 00:00:00 2001
From: Isaac Woods <isaacwoods.home@gmail.com>
Date: Mon, 24 Mar 2025 23:56:24 +0000
Subject: [PATCH 66/88] `aml`: handle transparent references in return values

---
 aml/src/lib.rs | 1 +
 1 file changed, 1 insertion(+)

diff --git a/aml/src/lib.rs b/aml/src/lib.rs
index ac1378c4..2fb6b55f 100644
--- a/aml/src/lib.rs
+++ b/aml/src/lib.rs
@@ -460,6 +460,7 @@ where
                     }
                     Opcode::Return => {
                         let [Argument::Object(object)] = &op.arguments[..] else { panic!() };
+                        let object = object.clone().unwrap_transparent_reference();
 
                         if let Some(last) = self.context_stack.lock().pop() {
                             context = last;

From e2158d6c98e019c6f7488e73d292187f1aa02350 Mon Sep 17 00:00:00 2001
From: Isaac Woods <isaacwoods.home@gmail.com>
Date: Mon, 24 Mar 2025 23:58:59 +0000
Subject: [PATCH 67/88] `aml`: resolve fields at the top-level if required

This also makes the logic clearer about whether we're meant to resolve
a top-level name or not, depending on what we're interpreting.
---
 aml/src/lib.rs | 63 ++++++++++++++++++++++++++++++++------------------
 1 file changed, 40 insertions(+), 23 deletions(-)

diff --git a/aml/src/lib.rs b/aml/src/lib.rs
index 2fb6b55f..c3b14a53 100644
--- a/aml/src/lib.rs
+++ b/aml/src/lib.rs
@@ -20,10 +20,7 @@ use alloc::{
     vec::Vec,
 };
 use bit_field::BitField;
-use core::{
-    mem,
-    str::FromStr,
-};
+use core::{mem, str::FromStr};
 use log::{info, trace, warn};
 use namespace::{AmlName, Namespace, NamespaceLevelKind};
 use object::{FieldFlags, FieldUnit, FieldUnitKind, MethodFlags, Object, ObjectType, ReferenceKind};
@@ -58,13 +55,11 @@ where
             namespace: Spinlock::new(Namespace::new()),
             context_stack: Spinlock::new(Vec::new()),
             dsdt_revision,
-            ops_interpreted: AtomicUsize::new(0),
             region_handlers: Spinlock::new(BTreeMap::new()),
         }
     }
 
     pub fn load_table(&self, stream: &[u8]) -> Result<(), AmlError> {
-        // TODO: probs needs to do more stuff
         let context = unsafe { MethodContext::new_from_table(stream) };
         self.do_execute_method(context)?;
         Ok(())
@@ -925,30 +920,52 @@ where
                     context.current_block.pc -= 1;
                     let name = context.namestring()?;
 
-                    match self.namespace.lock().search(&name, &context.current_scope) {
-                        Ok((resolved_name, object)) => {
-                            if let Object::Method { flags, .. } = *object {
-                                context.start_in_flight_op(OpInFlight::new_with(
-                                    Opcode::InternalMethodCall,
-                                    vec![Argument::Object(object), Argument::Namestring(resolved_name)],
-                                    flags.arg_count(),
-                                ))
-                            } else {
-                                context.last_op()?.arguments.push(Argument::Object(object));
+                    /*
+                     * The desired behaviour when we encounter a name at the top-level differs
+                     * depending on the context we're in. There are certain places where we want to
+                     * evaluate things like methods and field units, and others where we simply
+                     * want to reference the name (such as inside package definitions). In the
+                     * latter case, we also allow undefined names to be used, and will resolve them
+                     * at the time of use.
+                     */
+                    let do_not_resolve = context.current_block.kind == BlockKind::Package
+                        || context.in_flight.last().map(|op| op.op == Opcode::CondRefOf).unwrap_or(false);
+                    if do_not_resolve {
+                        let object = self.namespace.lock().search(&name, &context.current_scope);
+                        match object {
+                            Ok((_, object)) => {
+                                let reference =
+                                    Object::Reference { kind: ReferenceKind::RefOf, inner: object.clone() };
+                                context.last_op()?.arguments.push(Argument::Object(Arc::new(reference)));
                             }
-                        }
-                        Err(AmlError::ObjectDoesNotExist(_)) => {
-                            let allow_unresolved = context.current_block.kind == BlockKind::Package
-                                || context.in_flight.last().map(|op| op.op == Opcode::CondRefOf).unwrap_or(false);
-                            if allow_unresolved {
+                            Err(AmlError::ObjectDoesNotExist(_)) => {
                                 let reference = Object::Reference {
                                     kind: ReferenceKind::Unresolved,
                                     inner: Arc::new(Object::String(name.to_string())),
                                 };
                                 context.last_op()?.arguments.push(Argument::Object(Arc::new(reference)));
                             }
+                            Err(other) => Err(other)?,
+                        }
+                    } else {
+                        let object = self.namespace.lock().search(&name, &context.current_scope);
+                        match object {
+                            Ok((resolved_name, object)) => {
+                                if let Object::Method { flags, .. } = *object {
+                                    context.start_in_flight_op(OpInFlight::new_with(
+                                        Opcode::InternalMethodCall,
+                                        vec![Argument::Object(object), Argument::Namestring(resolved_name)],
+                                        flags.arg_count(),
+                                    ))
+                                } else if let Object::FieldUnit(ref field) = *object {
+                                    let value = self.do_field_read(field)?;
+                                    context.last_op()?.arguments.push(Argument::Object(value));
+                                } else {
+                                    context.last_op()?.arguments.push(Argument::Object(object));
+                                }
+                            }
+                            Err(err) => Err(err)?,
                         }
-                        Err(other) => Err(other)?,
                     }
                 }
 
@@ -973,7 +990,7 @@ where
                 Opcode::FindSetLeftBit | Opcode::FindSetRightBit => {
                     context.start_in_flight_op(OpInFlight::new(opcode, 2))
                 }
-                Opcode::DerefOf => todo!(),
+                Opcode::DerefOf => context.start_in_flight_op(OpInFlight::new(opcode, 1)),
                 Opcode::Notify => todo!(),
                 Opcode::ConcatRes => context.start_in_flight_op(OpInFlight::new(opcode, 3)),
                 Opcode::SizeOf => context.start_in_flight_op(OpInFlight::new(opcode, 1)),

From 37afb695859e606e6d86bffaca846a83d1b9e94d Mon Sep 17 00:00:00 2001
From: Isaac Woods <isaacwoods.home@gmail.com>
Date: Tue, 25 Mar 2025 00:18:40 +0000
Subject: [PATCH 68/88] `aml`: don't fail on `DefDataRegion` ops

---
 aml/src/lib.rs | 35 ++++++++++++++++++++++++++++++++++-
 1 file changed, 34 insertions(+), 1 deletion(-)

diff --git a/aml/src/lib.rs b/aml/src/lib.rs
index c3b14a53..eb033d41 100644
--- a/aml/src/lib.rs
+++ b/aml/src/lib.rs
@@ -246,6 +246,32 @@ where
                             length: region_length.as_integer()?,
                             parent_device_path: context.current_scope.clone(),
                         });
+                        self.namespace.lock().insert(name.resolve(&context.current_scope)?, Arc::new(region))?;
+                    }
+                    Opcode::DataRegion => {
+                        let [
+                            Argument::Namestring(name),
+                            Argument::Object(signature),
+                            Argument::Object(oem_id),
+                            Argument::Object(oem_table_id),
+                        ] = &op.arguments[..]
+                        else {
+                            panic!()
+                        };
+                        let _signature = signature.as_string()?;
+                        let _oem_id = oem_id.as_string()?;
+                        let _oem_table_id = oem_table_id.as_string()?;
+
+                        // TODO: once this is integrated into the rest of the crate, load the table
+                        log::warn!(
+                            "DefDataRegion encountered in AML! We don't actually support these - produced region will be incorrect"
+                        );
+
+                        let region = Object::OpRegion(OpRegion {
+                            space: RegionSpace::SystemMemory,
+                            base: 0,
+                            length: 0,
+                            parent_device_path: context.current_scope.clone(),
                         });
                         self.namespace.lock().insert(name.resolve(&context.current_scope)?, Arc::new(region))?;
                     }
@@ -792,6 +818,14 @@ where
                         2,
                     ));
                 }
+                Opcode::DataRegion => {
+                    let name = context.namestring()?;
+                    context.start_in_flight_op(OpInFlight::new_with(
+                        Opcode::DataRegion,
+                        vec![Argument::Namestring(name)],
+                        3,
+                    ));
+                }
                 Opcode::Field => {
                     let start_pc = context.current_block.pc;
                     let pkg_length = context.pkglength()?;
@@ -892,7 +926,6 @@ where
                     let old_scope = mem::replace(&mut context.current_scope, new_scope);
                     context.start_new_block(BlockKind::Scope { old_scope }, remaining_length);
                 }
-                Opcode::DataRegion => todo!(),
                 Opcode::Local(local) => {
                     let local = context.locals[local as usize].clone();
                     context.last_op()?.arguments.push(Argument::Object(Arc::new(Object::Reference {

From 2561e5a727eecfd61f81bdc713cbecf643dad6e4 Mon Sep 17 00:00:00 2001
From: Isaac Woods <isaacwoods.home@gmail.com>
Date: Tue, 25 Mar 2025 00:19:31 +0000
Subject: [PATCH 69/88] `aml`: handle stores to locals/args

---
 aml/src/lib.rs | 16 ++++++++++++++++
 1 file changed, 16 insertions(+)

diff --git a/aml/src/lib.rs b/aml/src/lib.rs
index eb033d41..bc30d6cf 100644
--- a/aml/src/lib.rs
+++ b/aml/src/lib.rs
@@ -1554,6 +1554,22 @@ where
                     _ => panic!(),
                 },
                 Object::FieldUnit(field) => self.do_field_write(field, object)?,
+                Object::Reference { kind, inner } => {
+                    match kind {
+                        ReferenceKind::RefOf => todo!(),
+                        ReferenceKind::LocalOrArg => {
+                            if let Object::Reference { kind: inner_kind, inner: inner_inner } = &**inner {
+                                // TODO: this should store into the reference, potentially doing an
+                                // implicit cast
+                                todo!()
+                            } else {
+                                // Overwrite the value
+                                *inner.gain_mut() = object.gain_mut().clone();
+                            }
+                        }
+                        ReferenceKind::Unresolved => todo!(),
+                    }
+                }
                 Object::Debug => {
                     self.handler.handle_debug(&*object);
                 }

From de76c391f7e0e58178d60374fa43c2c2e5d2c80a Mon Sep 17 00:00:00 2001
From: Isaac Woods <isaacwoods.home@gmail.com>
Date: Tue, 25 Mar 2025 00:23:41 +0000
Subject: [PATCH 70/88] `aml`: improve interpreter docs + few bits and bobs

---
 aml/src/lib.rs | 76 ++++++++++++++++++++++++++------------------------
 1 file changed, 39 insertions(+), 37 deletions(-)

diff --git a/aml/src/lib.rs b/aml/src/lib.rs
index bc30d6cf..8203a1f0 100644
--- a/aml/src/lib.rs
+++ b/aml/src/lib.rs
@@ -111,32 +111,27 @@ where
 
     fn do_execute_method(&self, mut context: MethodContext) -> Result<Arc<Object>, AmlError> {
         /*
-         * TODO
+         * This is the main loop that executes operations. Every op is handled at the top-level of
+         * the loop to prevent pathological stack growth from nested operations.
          *
-         * This is the main loop that executes ops. Every op is handled at the top-level loop to
-         * prevent pathological stack depths.
+         * The loop has three main stages:
+         *   1) Check if any in-flight operations are ready to be executed (i.e. have collected all
+         *      their arguments). An operation completing may contribute the last required argument
+         *      of the one above, so this is repeated for as many operations as are ready to be
+         *      retired.
+         *   2) Look at the next opcode in the stream. If we've run out of opcodes in the current
+         *      block, run logic to determine where in the stream we should move to next. Special
+         *      logic at this level handles things like moving in/out of package definitions, and
+         *      performing control flow.
+         *   3) When the next opcode is determined, use it to interpret the next portion of the
+         *      stream. If that is data, the correct number of bytes can be consumed and
+         *      contributed to the current in-flight operation. If it's an opcode, a new in-flight
+         *      operation is started, and we go round the loop again.
          *
-         * Worked example: AddOp TermArg TermArg Target
-         * - We go round the loop 4 times to interpret this.
-         * - We encounter the AddOp. We add a new in-flight operation with 3 expected arguments.
-         * - We go round again and find some sort of TermArg. This may create new in-flight ops
-         *   which we then go round again to complete. Once we're finished, we add this to the
-         *   AddOp's record. We then need to detect that all 3 arguments are ready and retire the
-         *   AddOp *before* moving on.
-         *
-         * My thoughts are that we can go round and round a loop with two big stages. First, we
-         * check if in-flight ops are ready to be executed (e.g. have collected all their
-         * arguments) and execute them. This can in turn complete the next op up, so this should
-         * complete as many in-flight ops as possible at a time.
-         *
-         * Once all possible in-flight ops have been executed, we then need to move forward in the
-         * stream. Depending on the next op, this could create a new in-flight op that is then
-         * pre-empted, or could parse an argument to an existing in-flight op, which may then be
-         * able to be completed. This stage should not do any executing in its own right, really.
-         * It's just about consuming the next however-many bytes of the stream and setting things
-         * up.
+         * This scheme is what allows the interpreter to use a loop that somewhat resembles a
+         * traditional fast bytecode VM, but also provides enough flexibility to handle the
+         * quirkier parts of the AML grammar, particularly the left-to-right encoding of operands.
          */
-
         loop {
             /*
              * First, see if we've gathered enough arguments to complete some in-flight operations.
@@ -207,7 +202,7 @@ where
                             Arc::new(Object::Buffer(buffer))
                         };
                         // TODO: use potentially-updated result for return value here
-                        self.do_store(&mut context, target, result.clone())?;
+                        self.do_store(target, result.clone())?;
                         context.contribute_arg(Argument::Object(result));
                     }
                     Opcode::FromBCD => self.do_from_bcd(&mut context, op)?,
@@ -393,7 +388,7 @@ where
                     }
                     Opcode::Store => {
                         let [Argument::Object(object), target] = &op.arguments[..] else { panic!() };
-                        self.do_store(&mut context, &target, object.clone())?;
+                        self.do_store(&target, object.clone())?;
                     }
                     Opcode::RefOf => {
                         let [Argument::Object(object)] = &op.arguments[..] else { panic!() };
@@ -408,7 +403,7 @@ where
                         } else {
                             let reference =
                                 Arc::new(Object::Reference { kind: ReferenceKind::RefOf, inner: object.clone() });
-                            self.do_store(&mut context, target, reference)?;
+                            self.do_store(target, reference)?;
                             Object::Integer(u64::MAX)
                         };
                         context.contribute_arg(Argument::Object(Arc::new(result)));
@@ -1193,7 +1188,7 @@ where
             Opcode::Multiply => left.wrapping_mul(right),
             Opcode::Divide => {
                 if let Some(remainder) = target2 {
-                    self.do_store(context, remainder, Arc::new(Object::Integer(left.wrapping_rem(right))))?;
+                    self.do_store(remainder, Arc::new(Object::Integer(left.wrapping_rem(right))))?;
                 }
                 left.wrapping_div_euclid(right)
             }
@@ -1210,7 +1205,7 @@ where
 
         let result = Arc::new(Object::Integer(result));
         // TODO: use result for arg
-        self.do_store(context, target, result.clone())?;
+        self.do_store(target, result.clone())?;
         context.contribute_arg(Argument::Object(result));
         Ok(())
     }
@@ -1360,7 +1355,7 @@ where
             _ => Err(AmlError::InvalidOperationOnObject { op: Operation::Mid, typ: source.typ() })?,
         });
 
-        self.do_store(context, target, result.clone())?;
+        self.do_store(target, result.clone())?;
         context.contribute_arg(Argument::Object(result));
 
         Ok(())
@@ -1417,7 +1412,7 @@ where
             }
         };
         // TODO: use result of store
-        self.do_store(context, target, result.clone())?;
+        self.do_store(target, result.clone())?;
         context.contribute_arg(Argument::Object(result));
         Ok(())
     }
@@ -1511,21 +1506,22 @@ where
             _ => Err(AmlError::IndexOutOfBounds)?,
         });
 
-        self.do_store(context, target, result.clone())?;
+        self.do_store(target, result.clone())?;
         context.contribute_arg(Argument::Object(result));
         Ok(())
     }
-    fn do_store(
-        &self,
-        context: &mut MethodContext,
-        target: &Argument,
-        object: Arc<Object>,
-    ) -> Result<(), AmlError> {
+
+    // TODO: this might actually do weird stuff to your data if written to a field with BufferAcc
+    // access. I guess we need to return something here really and use it instead of the result
+    // when returning?? We need to look carefully at all use-sites to make sure it actually returns
+    // the result of the store, not the object it passed to us.
+    fn do_store(&self, target: &Argument, object: Arc<Object>) -> Result<(), AmlError> {
         // TODO: find the destination (need to handle references, debug objects, etc.)
         // TODO: convert object to be of the type of destination, in line with 19.3.5 of the spec
         // TODO: write the object to the destination, including e.g. field writes that then lead to
         // literally god knows what.
         let object = object.unwrap_transparent_reference();
+
         match target {
             Argument::Object(target) => match target.gain_mut() {
                 Object::Integer(target) => match object.gain_mut() {
@@ -2581,6 +2577,12 @@ mod tests {
         fn release(&self, _mutex: Handle) {}
     }
 
+    #[test]
+    fn verify_interpreter_send_sync() {
+        fn test_send_sync<T: Send + Sync>() {}
+        test_send_sync::<Interpreter<TestHandler>>();
+    }
+
     #[test]
     fn add_op() {
         let interpreter = Interpreter::new(TestHandler, 2);

From 9cf62936e1c922c8df830236f966162ae06aeb13 Mon Sep 17 00:00:00 2001
From: Isaac Woods <isaacwoods.home@gmail.com>
Date: Tue, 25 Mar 2025 00:25:18 +0000
Subject: [PATCH 71/88] `aml`: start process for initializing namespace -
 `_STA` and `_INI` objects

---
 aml/src/lib.rs       | 92 +++++++++++++++++++++++++++++++++++++++++++-
 aml/src/namespace.rs |  2 +
 aml/src/object.rs    | 31 ++++++++++++++-
 aml/src/op_region.rs |  2 +-
 4 files changed, 123 insertions(+), 4 deletions(-)

diff --git a/aml/src/lib.rs b/aml/src/lib.rs
index 8203a1f0..c1472be7 100644
--- a/aml/src/lib.rs
+++ b/aml/src/lib.rs
@@ -23,7 +23,17 @@ use bit_field::BitField;
 use core::{mem, str::FromStr};
 use log::{info, trace, warn};
 use namespace::{AmlName, Namespace, NamespaceLevelKind};
-use object::{FieldFlags, FieldUnit, FieldUnitKind, MethodFlags, Object, ObjectType, ReferenceKind};
+use object::{
+    DeviceStatus,
+    FieldFlags,
+    FieldUnit,
+    FieldUnitKind,
+    FieldUpdateRule,
+    MethodFlags,
+    Object,
+    ObjectType,
+    ReferenceKind,
+};
 use op_region::{OpRegion, RegionHandler, RegionSpace};
 use spinning_top::Spinlock;
 
@@ -109,6 +119,85 @@ where
         handlers.insert(space, Box::new(handler));
     }
 
+    /// 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.invoke_method_if_present(AmlName::from_str("\\_INI").unwrap(), vec![]) {
+            warn!("Invoking \\_INI failed: {:?}", err);
+        }
+        if let Err(err) = self.invoke_method_if_present(AmlName::from_str("\\_SB._INI").unwrap(), vec![]) {
+            warn!("Invoking \\_SB._INI failed: {:?}", err);
+        }
+
+        // TODO: run all _REGs for globally-installed handlers (this might need more bookkeeping)
+
+        /*
+         * 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.
+         *
+         * We then evaluate `_INI` for the device. This can dynamically populate objects such as
+         * `_ADR`, `_CID`, `_HID`, `_SUN`, and `_UID`, and so is necessary before further
+         * operation.
+         */
+        let mut num_devices_initialized = 0;
+        /*
+         * TODO
+         * We clone a copy of the namespace here to traverse while executing all the `_STA` and
+         * `_INI` objects. Avoiding this would be good, but is not easy, as we need
+         * potentially-mutable access while executing all of the methods.
+         */
+        let mut namespace = self.namespace.lock().clone();
+        let init_status = namespace.traverse(|path, level| {
+            match level.kind {
+                NamespaceLevelKind::Device
+                | NamespaceLevelKind::Processor
+                | NamespaceLevelKind::ThermalZone
+                | NamespaceLevelKind::PowerResource => {
+                    let should_initialize = match self
+                        .invoke_method_if_present(AmlName::from_str("_STA").unwrap().resolve(path)?, vec![])
+                    {
+                        Ok(Some(result)) => {
+                            let Object::Integer(result) = *result else { panic!() };
+                            let status = DeviceStatus(result);
+                            status.present() && status.functioning()
+                        }
+                        Ok(None) => true,
+                        Err(err) => {
+                            warn!("Failed to evaluate _STA for device {}: {:?}", path, err);
+                            false
+                        }
+                    };
+
+                    if should_initialize {
+                        num_devices_initialized += 1;
+                        if let Err(err) = self
+                            .invoke_method_if_present(AmlName::from_str("_INI").unwrap().resolve(path)?, vec![])
+                        {
+                            warn!("Failed to evaluate _INI for device {}: {:?}", path, err);
+                        }
+                        Ok(true)
+                    } else {
+                        /*
+                         * If this device should not be initialized, don't initialize it's children.
+                         */
+                        Ok(false)
+                    }
+                }
+                _ => Ok(true),
+            }
+        });
+        if let Err(err) = init_status {
+            warn!("Error while traversing namespace for devices: {:?}", err);
+        }
+
+        info!("Initialized {} devices", num_devices_initialized);
+    }
+
     fn do_execute_method(&self, mut context: MethodContext) -> Result<Arc<Object>, AmlError> {
         /*
          * This is the main loop that executes operations. Every op is handled at the top-level of
@@ -2529,6 +2618,7 @@ pub trait Handler: Send + Sync {
     /// without causing a deadlock.
     fn acquire(&self, mutex: Handle, timeout: u16) -> Result<(), AmlError>;
     fn release(&self, mutex: Handle);
+
     fn breakpoint(&self) {}
 
     fn handle_debug(&self, _object: &Object) {}
diff --git a/aml/src/namespace.rs b/aml/src/namespace.rs
index 155e6c26..cc5ca439 100644
--- a/aml/src/namespace.rs
+++ b/aml/src/namespace.rs
@@ -9,6 +9,7 @@ use alloc::{
 use bit_field::BitField;
 use core::{fmt, str, str::FromStr};
 
+#[derive(Clone)]
 pub struct Namespace {
     root: NamespaceLevel,
 }
@@ -312,6 +313,7 @@ pub enum NamespaceLevelKind {
     MethodLocals,
 }
 
+#[derive(Clone)]
 pub struct NamespaceLevel {
     pub kind: NamespaceLevelKind,
     pub values: BTreeMap<NameSeg, (ObjectFlags, Arc<Object>)>,
diff --git a/aml/src/object.rs b/aml/src/object.rs
index 9d7f3371..ae8f78bf 100644
--- a/aml/src/object.rs
+++ b/aml/src/object.rs
@@ -2,7 +2,7 @@ use crate::{AmlError, Handle, Operation, op_region::OpRegion};
 use alloc::{borrow::Cow, string::String, sync::Arc, vec::Vec};
 use bit_field::BitField;
 
-#[derive(Debug)]
+#[derive(Clone, Debug)]
 pub enum Object {
     Uninitialized,
     Buffer(Vec<u8>),
@@ -183,7 +183,7 @@ impl Object {
     }
 }
 
-#[derive(Debug)]
+#[derive(Clone, Debug)]
 pub struct FieldUnit {
     pub kind: FieldUnitKind,
     pub flags: FieldFlags,
@@ -305,6 +305,33 @@ pub enum ObjectType {
     Debug,
 }
 
+/// Helper type for decoding the result of `_STA` objects.
+pub struct DeviceStatus(pub u64);
+
+impl DeviceStatus {
+    pub fn present(&self) -> bool {
+        self.0.get_bit(0)
+    }
+
+    pub fn enabled(&self) -> bool {
+        self.0.get_bit(1)
+    }
+
+    pub fn show_in_ui(&self) -> bool {
+        self.0.get_bit(2)
+    }
+
+    pub fn functioning(&self) -> bool {
+        self.0.get_bit(3)
+    }
+
+    /// This flag is only used for Battery devices (PNP0C0A), and indicates if the battery is
+    /// present.
+    pub fn battery_present(&self) -> bool {
+        self.0.get_bit(4)
+    }
+}
+
 /// Copy an arbitrary bit range of `src` to an arbitrary bit range of `dst`. This is used for
 /// buffer fields. Data is zero-extended if `src` does not cover `length` bits, matching the
 /// expected behaviour for buffer fields.
diff --git a/aml/src/op_region.rs b/aml/src/op_region.rs
index 14bb533f..7242c81b 100644
--- a/aml/src/op_region.rs
+++ b/aml/src/op_region.rs
@@ -1,6 +1,6 @@
 use crate::{AmlError, namespace::AmlName};
 
-#[derive(Debug)]
+#[derive(Clone, Debug)]
 pub struct OpRegion {
     pub space: RegionSpace,
     pub base: u64,

From b266a6a59327d8f549f3e50fdb4b5a0adc7155a7 Mon Sep 17 00:00:00 2001
From: Isaac Woods <isaacwoods.home@gmail.com>
Date: Tue, 25 Mar 2025 16:37:42 +0000
Subject: [PATCH 72/88] `aml`: commit some comments/other bits in prep of
 merging crates

---
 aml/src/lib.rs       | 20 ++++++++++++++++++--
 aml/src/namespace.rs |  2 ++
 aml/src/op_region.rs | 11 +++++++++--
 3 files changed, 29 insertions(+), 4 deletions(-)

diff --git a/aml/src/lib.rs b/aml/src/lib.rs
index c1472be7..c3eb61bc 100644
--- a/aml/src/lib.rs
+++ b/aml/src/lib.rs
@@ -58,6 +58,8 @@ impl<H> Interpreter<H>
 where
     H: Handler,
 {
+    // TODO: new_from_tables helper that does `new` and then loads the DSDT + any SSDTs
+
     pub fn new(handler: H, dsdt_revision: u8) -> Interpreter<H> {
         info!("Initializing AML interpreter v{}", env!("CARGO_PKG_VERSION"));
         Interpreter {
@@ -272,6 +274,11 @@ where
                     | Opcode::LLess => {
                         self.do_logical_op(&mut context, op)?;
                     }
+                    Opcode::ToBuffer
+                    | Opcode::ToDecimalString
+                    | Opcode::ToHexString
+                    | Opcode::ToInteger
+                    | Opcode::ToString => todo!(),
                     Opcode::Mid => self.do_mid(&mut context, op)?,
                     Opcode::Concat => self.do_concat(&mut context, op)?,
                     Opcode::ConcatRes => {
@@ -412,7 +419,6 @@ where
                         else {
                             panic!()
                         };
-
                         let predicate = predicate.as_integer()?;
                         let remaining_then_length = then_length - (context.current_block.pc - start_pc);
 
@@ -697,6 +703,9 @@ where
                              */
                             assert!(context.block_stack.len() > 0);
 
+                            // TODO: I think we can handle VarPackage here as well because by the
+                            // time the block finishes, the first arg should be resolvable (the var
+                            // length) and so can be updated here...
                             if let Some(package_op) = context.in_flight.last_mut()
                                 && package_op.op == Opcode::Package
                             {
@@ -872,6 +881,7 @@ where
                 Opcode::Signal => todo!(),
                 Opcode::Wait => todo!(),
                 Opcode::Reset => todo!(),
+                Opcode::Notify => todo!(),
                 Opcode::FromBCD | Opcode::ToBCD => context.start_in_flight_op(OpInFlight::new(opcode, 2)),
                 Opcode::Revision => {
                     context.contribute_arg(Argument::Object(Arc::new(Object::Integer(INTERPRETER_REVISION))));
@@ -1108,10 +1118,16 @@ where
                     context.start_in_flight_op(OpInFlight::new(opcode, 2))
                 }
                 Opcode::DerefOf => context.start_in_flight_op(OpInFlight::new(opcode, 1)),
-                Opcode::Notify => todo!(),
                 Opcode::ConcatRes => context.start_in_flight_op(OpInFlight::new(opcode, 3)),
                 Opcode::SizeOf => context.start_in_flight_op(OpInFlight::new(opcode, 1)),
                 Opcode::Index => context.start_in_flight_op(OpInFlight::new(opcode, 3)),
+                /*
+                 * TODO
+                 * Match is a difficult opcode to parse, as it interleaves dynamic arguments and
+                 * random bytes that need to be extracted as you go. I think we'll need to use 1+
+                 * internal in-flight ops to parse the static bytedatas as we go, and then retire
+                 * the real op at the end.
+                 */
                 Opcode::Match => todo!(),
 
                 Opcode::CreateBitField
diff --git a/aml/src/namespace.rs b/aml/src/namespace.rs
index cc5ca439..16473331 100644
--- a/aml/src/namespace.rs
+++ b/aml/src/namespace.rs
@@ -25,6 +25,8 @@ impl Namespace {
         namespace.add_level(AmlName::from_str("\\_PR").unwrap(), NamespaceLevelKind::Scope).unwrap();
         namespace.add_level(AmlName::from_str("\\_TZ").unwrap(), NamespaceLevelKind::Scope).unwrap();
 
+        // TODO: add pre-defined objects as well - \GL, \OSI, etc.
+
         namespace
     }
 
diff --git a/aml/src/op_region.rs b/aml/src/op_region.rs
index 7242c81b..2feebd2f 100644
--- a/aml/src/op_region.rs
+++ b/aml/src/op_region.rs
@@ -9,8 +9,15 @@ pub struct OpRegion {
 }
 
 pub trait RegionHandler {
-    fn read(&self, region: &OpRegion) -> Result<(), AmlError>;
-    fn write(&self, region: &OpRegion) -> Result<(), AmlError>;
+    fn read_u8(&self, region: &OpRegion) -> Result<u8, AmlError>;
+    fn read_u16(&self, region: &OpRegion) -> Result<u16, AmlError>;
+    fn read_u32(&self, region: &OpRegion) -> Result<u32, AmlError>;
+    fn read_u64(&self, region: &OpRegion) -> Result<u64, AmlError>;
+
+    fn write_u8(&self, region: &OpRegion, value: u8) -> Result<(), AmlError>;
+    fn write_u16(&self, region: &OpRegion, value: u16) -> Result<(), AmlError>;
+    fn write_u32(&self, region: &OpRegion, value: u32) -> Result<(), AmlError>;
+    fn write_u64(&self, region: &OpRegion, value: u64) -> Result<(), AmlError>;
 }
 
 #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)]

From 3e73ac480ef6cc61a5844c7c12c23cb8aa16112e Mon Sep 17 00:00:00 2001
From: Isaac Woods <isaacwoods.home@gmail.com>
Date: Tue, 25 Mar 2025 17:11:04 +0000
Subject: [PATCH 73/88] Merge `acpi` and `aml` crates into one top-level crate

---
 Cargo.toml                              | 26 ++++++++++++++++++++++++-
 acpi/Cargo.toml                         | 19 ------------------
 aml/Cargo.toml                          | 17 ----------------
 {acpi/src => src}/address.rs            |  0
 aml/src/lib.rs => src/aml/mod.rs        | 20 +++++++++++++++----
 {aml/src => src/aml}/namespace.rs       |  2 +-
 {aml/src => src/aml}/object.rs          |  2 +-
 {aml/src => src/aml}/op_region.rs       |  2 +-
 {aml/src => src/aml}/pci_routing.rs     |  4 ++--
 {aml/src => src/aml}/resource.rs        |  2 +-
 {acpi/src => src}/handler.rs            |  0
 {acpi/src => src}/lib.rs                |  9 ++++++++-
 {acpi/src => src}/platform/interrupt.rs |  0
 {acpi/src => src}/platform/mod.rs       |  0
 {acpi/src => src}/platform/pci.rs       |  0
 {acpi/src => src}/rsdp.rs               |  0
 {acpi/src => src}/sdt/bgrt.rs           |  0
 {acpi/src => src}/sdt/fadt.rs           |  0
 {acpi/src => src}/sdt/hpet.rs           |  0
 {acpi/src => src}/sdt/madt.rs           |  0
 {acpi/src => src}/sdt/mcfg.rs           |  0
 {acpi/src => src}/sdt/mod.rs            |  0
 {acpi/src => src}/sdt/spcr.rs           |  0
 tools/aml_tester/Cargo.toml             |  2 +-
 tools/aml_tester/src/main.rs            |  4 ++--
 25 files changed, 58 insertions(+), 51 deletions(-)
 delete mode 100644 acpi/Cargo.toml
 delete mode 100644 aml/Cargo.toml
 rename {acpi/src => src}/address.rs (100%)
 rename aml/src/lib.rs => src/aml/mod.rs (99%)
 rename {aml/src => src/aml}/namespace.rs (99%)
 rename {aml/src => src/aml}/object.rs (99%)
 rename {aml/src => src/aml}/op_region.rs (97%)
 rename {aml/src => src/aml}/pci_routing.rs (99%)
 rename {aml/src => src/aml}/resource.rs (99%)
 rename {acpi/src => src}/handler.rs (100%)
 rename {acpi/src => src}/lib.rs (96%)
 rename {acpi/src => src}/platform/interrupt.rs (100%)
 rename {acpi/src => src}/platform/mod.rs (100%)
 rename {acpi/src => src}/platform/pci.rs (100%)
 rename {acpi/src => src}/rsdp.rs (100%)
 rename {acpi/src => src}/sdt/bgrt.rs (100%)
 rename {acpi/src => src}/sdt/fadt.rs (100%)
 rename {acpi/src => src}/sdt/hpet.rs (100%)
 rename {acpi/src => src}/sdt/madt.rs (100%)
 rename {acpi/src => src}/sdt/mcfg.rs (100%)
 rename {acpi/src => src}/sdt/mod.rs (100%)
 rename {acpi/src => src}/sdt/spcr.rs (100%)

diff --git a/Cargo.toml b/Cargo.toml
index 49754db0..a4d6270e 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,3 +1,27 @@
 [workspace]
-members = ["acpi", "aml", "tools/aml_tester", "tools/acpi_dumper"]
+members = ["tools/aml_tester", "tools/acpi_dumper"]
 resolver = "2"
+
+[package]
+name = "acpi"
+version = "5.1.0"
+authors = ["Isaac Woods"]
+repository = "https://github.com/rust-osdev/acpi"
+description = "A pure-Rust library for interacting with ACPI"
+categories = ["hardware-support", "no-std"]
+readme = "../README.md"
+license = "MIT/Apache-2.0"
+edition = "2024"
+
+[dependencies]
+bit_field = "0.10.2"
+bitflags = "2.5.0"
+log = "0.4.20"
+spinning_top = "0.3.0"
+pci_types = { version = "0.10.0", public = true, optional = true }
+byteorder = { version = "1.5.0", default-features = false }
+
+[features]
+default = ["alloc", "aml"]
+alloc = []
+aml = ["alloc", "pci_types"]
diff --git a/acpi/Cargo.toml b/acpi/Cargo.toml
deleted file mode 100644
index 43fefaf7..00000000
--- a/acpi/Cargo.toml
+++ /dev/null
@@ -1,19 +0,0 @@
-[package]
-name = "acpi"
-version = "5.2.0"
-authors = ["Isaac Woods"]
-repository = "https://github.com/rust-osdev/acpi"
-description = "A pure-Rust library for interacting with ACPI"
-categories = ["hardware-support", "no-std"]
-readme = "../README.md"
-license = "MIT/Apache-2.0"
-edition = "2024"
-
-[dependencies]
-bit_field = "0.10.2"
-bitflags = "2.5.0"
-log = "0.4.20"
-
-[features]
-default = ["alloc"]
-alloc = []
diff --git a/aml/Cargo.toml b/aml/Cargo.toml
deleted file mode 100644
index dedbe54f..00000000
--- a/aml/Cargo.toml
+++ /dev/null
@@ -1,17 +0,0 @@
-[package]
-name = "aml"
-version = "0.17.0"
-edition = "2024"
-authors = ["Isaac Woods"]
-repository = "https://github.com/rust-osdev/acpi"
-description = "Library for parsing AML"
-categories = ["hardware-support", "no-std"]
-readme = "../README.md"
-license = "MIT/Apache-2.0"
-
-[dependencies]
-spinning_top = "0.3.0"
-bit_field = "0.10.2"
-byteorder = { version = "1.5.0", default-features = false }
-log = "0.4"
-pci_types = "0.10.0"
diff --git a/acpi/src/address.rs b/src/address.rs
similarity index 100%
rename from acpi/src/address.rs
rename to src/address.rs
diff --git a/aml/src/lib.rs b/src/aml/mod.rs
similarity index 99%
rename from aml/src/lib.rs
rename to src/aml/mod.rs
index c3eb61bc..02ab0fc2 100644
--- a/aml/src/lib.rs
+++ b/src/aml/mod.rs
@@ -1,7 +1,19 @@
-#![no_std]
-#![feature(let_chains, inherent_str_constructors)]
-
-extern crate alloc;
+/*
+ * TODO:
+ *  - Field reads supporting custom handlers
+ *  - Run `_REG` on supported op region handlers
+ *  - Sort out situation with `gain_mut` omg - thinking we should have a weird mutex thingy and
+ *    gain a 'token' to give us access to objects. Objects themselves should probs be in like an
+ *    `UnsafeCell` or something.
+ *  - Count operations performed and time
+ *  - Do stores properly :(
+ *  - Load and LoadTable
+ *  - Entire Event subsystem and opcodes for them
+ *
+ *  - Method recursion depth?
+ *  - Loop timeouts
+ *  - Fuzz the shit out of it I guess?
+ */
 
 pub mod namespace;
 pub mod object;
diff --git a/aml/src/namespace.rs b/src/aml/namespace.rs
similarity index 99%
rename from aml/src/namespace.rs
rename to src/aml/namespace.rs
index 16473331..b44164cd 100644
--- a/aml/src/namespace.rs
+++ b/src/aml/namespace.rs
@@ -1,4 +1,4 @@
-use crate::{AmlError, object::Object};
+use crate::aml::{AmlError, object::Object};
 use alloc::{
     collections::btree_map::BTreeMap,
     string::{String, ToString},
diff --git a/aml/src/object.rs b/src/aml/object.rs
similarity index 99%
rename from aml/src/object.rs
rename to src/aml/object.rs
index ae8f78bf..6e311140 100644
--- a/aml/src/object.rs
+++ b/src/aml/object.rs
@@ -1,4 +1,4 @@
-use crate::{AmlError, Handle, Operation, op_region::OpRegion};
+use crate::aml::{AmlError, Handle, Operation, op_region::OpRegion};
 use alloc::{borrow::Cow, string::String, sync::Arc, vec::Vec};
 use bit_field::BitField;
 
diff --git a/aml/src/op_region.rs b/src/aml/op_region.rs
similarity index 97%
rename from aml/src/op_region.rs
rename to src/aml/op_region.rs
index 2feebd2f..e2e73d64 100644
--- a/aml/src/op_region.rs
+++ b/src/aml/op_region.rs
@@ -1,4 +1,4 @@
-use crate::{AmlError, namespace::AmlName};
+use crate::aml::{AmlError, namespace::AmlName};
 
 #[derive(Clone, Debug)]
 pub struct OpRegion {
diff --git a/aml/src/pci_routing.rs b/src/aml/pci_routing.rs
similarity index 99%
rename from aml/src/pci_routing.rs
rename to src/aml/pci_routing.rs
index b0b52520..cea969b8 100644
--- a/aml/src/pci_routing.rs
+++ b/src/aml/pci_routing.rs
@@ -1,4 +1,4 @@
-use crate::{
+use crate::aml::{
     AmlError,
     Handler,
     Interpreter,
@@ -11,7 +11,7 @@ use alloc::{vec, vec::Vec};
 use bit_field::BitField;
 use core::str::FromStr;
 
-pub use crate::resource::IrqDescriptor;
+pub use crate::aml::resource::IrqDescriptor;
 
 #[derive(Clone, Copy, PartialEq, Eq, Debug)]
 pub enum Pin {
diff --git a/aml/src/resource.rs b/src/aml/resource.rs
similarity index 99%
rename from aml/src/resource.rs
rename to src/aml/resource.rs
index 51f356a2..738545e2 100644
--- a/aml/src/resource.rs
+++ b/src/aml/resource.rs
@@ -1,4 +1,4 @@
-use crate::{AmlError, Operation, object::Object};
+use crate::aml::{AmlError, Operation, object::Object};
 use alloc::{sync::Arc, vec::Vec};
 use bit_field::BitField;
 use byteorder::{ByteOrder, LittleEndian};
diff --git a/acpi/src/handler.rs b/src/handler.rs
similarity index 100%
rename from acpi/src/handler.rs
rename to src/handler.rs
diff --git a/acpi/src/lib.rs b/src/lib.rs
similarity index 96%
rename from acpi/src/lib.rs
rename to src/lib.rs
index e8b61b07..ede4544c 100644
--- a/acpi/src/lib.rs
+++ b/src/lib.rs
@@ -9,6 +9,10 @@
 //! tables, and interact with the tables using their raw structures. All other functionality is
 //! behind an `alloc` feature (enabled by default) and requires an allocator.
 //!
+//! With an allocator, this crate also provides higher-level interfaces to the static tables, as
+//! well as a dynamic interpreter for AML - the bytecode format encoded in the DSDT and SSDT
+//! tables.
+//!
 //! ### Usage
 //! To use the library, you will need to provide an implementation of the [`AcpiHandler`] trait,
 //! which allows the library to make requests such as mapping a particular region of physical
@@ -28,7 +32,7 @@
 //! interfaces, such as [`PlatformInfo`], [`PciConfigRegions`], or [`HpetInfo`].
 
 #![no_std]
-#![feature(allocator_api)]
+#![feature(allocator_api, let_chains, inherent_str_constructors)]
 
 #[cfg_attr(test, macro_use)]
 #[cfg(test)]
@@ -38,7 +42,10 @@ extern crate std;
 extern crate alloc;
 
 pub mod address;
+#[cfg(feature = "aml")]
+pub mod aml;
 pub mod handler;
+#[cfg(feature = "alloc")]
 pub mod platform;
 pub mod rsdp;
 pub mod sdt;
diff --git a/acpi/src/platform/interrupt.rs b/src/platform/interrupt.rs
similarity index 100%
rename from acpi/src/platform/interrupt.rs
rename to src/platform/interrupt.rs
diff --git a/acpi/src/platform/mod.rs b/src/platform/mod.rs
similarity index 100%
rename from acpi/src/platform/mod.rs
rename to src/platform/mod.rs
diff --git a/acpi/src/platform/pci.rs b/src/platform/pci.rs
similarity index 100%
rename from acpi/src/platform/pci.rs
rename to src/platform/pci.rs
diff --git a/acpi/src/rsdp.rs b/src/rsdp.rs
similarity index 100%
rename from acpi/src/rsdp.rs
rename to src/rsdp.rs
diff --git a/acpi/src/sdt/bgrt.rs b/src/sdt/bgrt.rs
similarity index 100%
rename from acpi/src/sdt/bgrt.rs
rename to src/sdt/bgrt.rs
diff --git a/acpi/src/sdt/fadt.rs b/src/sdt/fadt.rs
similarity index 100%
rename from acpi/src/sdt/fadt.rs
rename to src/sdt/fadt.rs
diff --git a/acpi/src/sdt/hpet.rs b/src/sdt/hpet.rs
similarity index 100%
rename from acpi/src/sdt/hpet.rs
rename to src/sdt/hpet.rs
diff --git a/acpi/src/sdt/madt.rs b/src/sdt/madt.rs
similarity index 100%
rename from acpi/src/sdt/madt.rs
rename to src/sdt/madt.rs
diff --git a/acpi/src/sdt/mcfg.rs b/src/sdt/mcfg.rs
similarity index 100%
rename from acpi/src/sdt/mcfg.rs
rename to src/sdt/mcfg.rs
diff --git a/acpi/src/sdt/mod.rs b/src/sdt/mod.rs
similarity index 100%
rename from acpi/src/sdt/mod.rs
rename to src/sdt/mod.rs
diff --git a/acpi/src/sdt/spcr.rs b/src/sdt/spcr.rs
similarity index 100%
rename from acpi/src/sdt/spcr.rs
rename to src/sdt/spcr.rs
diff --git a/tools/aml_tester/Cargo.toml b/tools/aml_tester/Cargo.toml
index 517a288a..14dcf618 100644
--- a/tools/aml_tester/Cargo.toml
+++ b/tools/aml_tester/Cargo.toml
@@ -5,7 +5,7 @@ authors = ["Isaac Woods"]
 edition = "2018"
 
 [dependencies]
-aml = { path = "../../aml" }
+acpi = { path = "../.." }
 clap = "4"
 termion = "1"
 log = "0.4"
diff --git a/tools/aml_tester/src/main.rs b/tools/aml_tester/src/main.rs
index d2920fb2..c7b0b025 100644
--- a/tools/aml_tester/src/main.rs
+++ b/tools/aml_tester/src/main.rs
@@ -9,7 +9,7 @@
  *      - For failing tests, print out a nice summary of the errors for each file
  */
 
-use aml::{namespace::AmlName, AmlError, Handle, Interpreter};
+use acpi::aml::{namespace::AmlName, AmlError, Handle, Interpreter};
 use clap::{Arg, ArgAction, ArgGroup};
 use pci_types::PciAddress;
 use std::{
@@ -324,7 +324,7 @@ impl log::Log for Logger {
 
 struct Handler;
 
-impl aml::Handler for Handler {
+impl acpi::aml::Handler for Handler {
     fn read_u8(&self, address: usize) -> u8 {
         println!("read_u8 {address:#x}");
         0

From fe31c72a27b0fdc0a248ab356f4978cc92d3274b Mon Sep 17 00:00:00 2001
From: Isaac Woods <isaacwoods.home@gmail.com>
Date: Tue, 25 Mar 2025 18:39:03 +0000
Subject: [PATCH 74/88] Add `Interpreter::new_from_tables` helper method

---
 src/aml/mod.rs | 49 +++++++++++++++++++++++++++++++++++++++++++++----
 src/lib.rs     |  5 ++++-
 2 files changed, 49 insertions(+), 5 deletions(-)

diff --git a/src/aml/mod.rs b/src/aml/mod.rs
index 02ab0fc2..307f8a96 100644
--- a/src/aml/mod.rs
+++ b/src/aml/mod.rs
@@ -23,6 +23,7 @@ pub mod resource;
 
 pub use pci_types::PciAddress;
 
+use crate::{AcpiError, AcpiHandler, AcpiTables, AmlTable, sdt::SdtHeader};
 use alloc::{
     boxed::Box,
     collections::btree_map::BTreeMap,
@@ -32,7 +33,7 @@ use alloc::{
     vec::Vec,
 };
 use bit_field::BitField;
-use core::{mem, str::FromStr};
+use core::{mem, slice, str::FromStr};
 use log::{info, trace, warn};
 use namespace::{AmlName, Namespace, NamespaceLevelKind};
 use object::{
@@ -70,8 +71,8 @@ impl<H> Interpreter<H>
 where
     H: Handler,
 {
-    // TODO: new_from_tables helper that does `new` and then loads the DSDT + any SSDTs
-
+    /// Construct a new `Interpreter`. This does not load any tables - if you have an `AcpiTables`
+    /// already, use [`Interpreter::new_from_tables`] instead.
     pub fn new(handler: H, dsdt_revision: u8) -> Interpreter<H> {
         info!("Initializing AML interpreter v{}", env!("CARGO_PKG_VERSION"));
         Interpreter {
@@ -83,6 +84,46 @@ where
         }
     }
 
+    /// Construct a new `Interpreter` with the given set of ACPI tables. This will automatically
+    /// load the DSDT and any SSDTs in the supplied [`AcpiTables`].
+    // TODO: maybe merge handler types? Maybe make one a supertrait of the other?
+    pub fn new_from_tables<AH: AcpiHandler>(
+        acpi_handler: AH,
+        aml_handler: H,
+        tables: &AcpiTables<AH>,
+    ) -> Result<Interpreter<H>, AcpiError> {
+        fn load_table<H: Handler, AH: AcpiHandler>(
+            interpreter: &Interpreter<H>,
+            acpi_handler: &AH,
+            table: AmlTable,
+        ) -> Result<(), AcpiError> {
+            let mapping = unsafe {
+                acpi_handler.map_physical_region::<SdtHeader>(table.phys_address, table.length as usize)
+            };
+            let stream = unsafe {
+                slice::from_raw_parts(
+                    mapping.virtual_start().as_ptr().byte_add(mem::size_of::<SdtHeader>()) as *const u8,
+                    table.length as usize - mem::size_of::<SdtHeader>(),
+                )
+            };
+            interpreter.load_table(stream).map_err(|err| AcpiError::Aml(err))?;
+            Ok(())
+        }
+
+        let dsdt = tables.dsdt()?;
+        let interpreter = Interpreter::new(aml_handler, dsdt.revision);
+        load_table(&interpreter, &acpi_handler, dsdt)?;
+
+        for ssdt in tables.ssdts() {
+            load_table(&interpreter, &acpi_handler, ssdt)?;
+        }
+
+        Ok(interpreter)
+    }
+
+    /// Load the supplied byte stream as an AML table. This should be only the encoded AML stream -
+    /// not the header at the start of a table. If you've used [`Interpreter::new_from_tables`],
+    /// you'll likely not need to load any tables manually.
     pub fn load_table(&self, stream: &[u8]) -> Result<(), AmlError> {
         let context = unsafe { MethodContext::new_from_table(stream) };
         self.do_execute_method(context)?;
@@ -93,7 +134,7 @@ where
     /// not a method, the object will instead be returned - this is useful for objects that can
     /// either be defined directly, or through a method (e.g. a `_CRS` object).
     pub fn invoke_method(&self, path: AmlName, args: Vec<Arc<Object>>) -> Result<Arc<Object>, AmlError> {
-        info!("Invoking AML method: {}", path);
+        trace!("Invoking AML method: {}", path);
 
         let object = self.namespace.lock().get(path.clone())?.clone();
         match object.typ() {
diff --git a/src/lib.rs b/src/lib.rs
index ede4544c..00ad7cde 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -234,7 +234,7 @@ pub unsafe trait AcpiTable {
     }
 }
 
-#[derive(Clone, Copy, Debug)]
+#[derive(Clone, Debug)]
 pub enum AcpiError {
     NoValidRsdp,
     RsdpIncorrectSignature,
@@ -252,4 +252,7 @@ pub enum AcpiError {
     InvalidDsdtAddress,
     InvalidMadt(MadtError),
     InvalidGenericAddress,
+
+    #[cfg(feature = "alloc")]
+    Aml(aml::AmlError),
 }

From 35ccb3bd9ab6345c8d985fca335270af82dc7e70 Mon Sep 17 00:00:00 2001
From: Isaac Woods <isaacwoods.home@gmail.com>
Date: Fri, 28 Mar 2025 15:02:15 +0000
Subject: [PATCH 75/88] AML: respect the table revision in `DefFindSetLeftBit`

---
 src/aml/mod.rs | 11 +++++++----
 1 file changed, 7 insertions(+), 4 deletions(-)

diff --git a/src/aml/mod.rs b/src/aml/mod.rs
index 307f8a96..7798a776 100644
--- a/src/aml/mod.rs
+++ b/src/aml/mod.rs
@@ -1378,11 +1378,14 @@ where
                     0
                 } else {
                     /*
-                     * TODO: this is a particular instance where not respecting integers being
-                     * 32-bit on revision 1 tables does cause properly incorrect behaviour...
-                     * TODO: we can fix this now we have the DSDT revision
+                     * This is a particularly important place to respect the integer width as set
+                     * by the DSDT revision.
                      */
-                    (operand.leading_zeros() + 1) as u64
+                    if self.dsdt_revision >= 2 {
+                        (operand.leading_zeros() + 1) as u64
+                    } else {
+                        ((operand as u32).leading_zeros() + 1) as u64
+                    }
                 }
             }
             Opcode::FindSetRightBit => {

From 88fd1346399e155090acfbf01461f7cc19b82f84 Mon Sep 17 00:00:00 2001
From: Isaac Woods <isaacwoods.home@gmail.com>
Date: Sun, 30 Mar 2025 17:48:19 +0100
Subject: [PATCH 76/88] Fix CI

---
 .github/workflows/build.yml | 18 ++++++------------
 1 file changed, 6 insertions(+), 12 deletions(-)

diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 16bcf746..bcce705f 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -41,7 +41,7 @@ jobs:
         components: llvm-tools-preview
         
     - name: Build crates
-      run: cargo build -p acpi -p aml --target $TARGET
+      run: cargo build --target $TARGET
 
   test:
     runs-on: ubuntu-latest
@@ -60,7 +60,7 @@ jobs:
       run: sudo apt-get install -y acpica-tools
 
     - name: Run tests
-      run: cargo test --all
+      run: cargo test
 
     - name: Run AML test suite
       run: cargo run_tests -p tests
@@ -79,14 +79,8 @@ jobs:
         profile: minimal
         components: clippy
 
-    - name: Run clippy (ACPI)
-      run: cargo clippy -p acpi
+    - name: Run clippy
+      run: cargo clippy
 
-    - name: Run clippy (ACPI tests)
-      run: cargo clippy -p acpi --tests
-
-    - name: Run clippy (AML)
-      run: cargo clippy -p aml
-
-    - name: Run clippy (AML tests)
-      run: cargo clippy -p aml --tests
+    - name: Run clippy (tests)
+      run: cargo clippy --tests

From 3956699875b6c881ef4e1b25e353d34bfe120675 Mon Sep 17 00:00:00 2001
From: Isaac Woods <isaacwoods.home@gmail.com>
Date: Wed, 2 Apr 2025 19:24:32 +0100
Subject: [PATCH 77/88] Make error enums `non_exhaustive`

This will allow us to add variants without them being breaking changes, and
I don't think you should be exhaustively matching our errors regardless.
---
 src/aml/mod.rs | 4 +---
 src/lib.rs     | 1 +
 2 files changed, 2 insertions(+), 3 deletions(-)

diff --git a/src/aml/mod.rs b/src/aml/mod.rs
index 7798a776..ed0588ea 100644
--- a/src/aml/mod.rs
+++ b/src/aml/mod.rs
@@ -2579,10 +2579,8 @@ pub enum Operation {
     ParseResource,
 }
 
-/*
- * TODO: not sure if we should use a better error reporting system or just keep a giant enum?
- */
 #[derive(Clone, PartialEq, Debug)]
+#[non_exhaustive]
 pub enum AmlError {
     RunOutOfStream,
     IllegalOpcode(u16),
diff --git a/src/lib.rs b/src/lib.rs
index 00ad7cde..1284d577 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -235,6 +235,7 @@ pub unsafe trait AcpiTable {
 }
 
 #[derive(Clone, Debug)]
+#[non_exhaustive]
 pub enum AcpiError {
     NoValidRsdp,
     RsdpIncorrectSignature,

From 4ce1dad65b138035e54d7c1c835dc8a80eae7f68 Mon Sep 17 00:00:00 2001
From: Isaac Woods <isaacwoods.home@gmail.com>
Date: Wed, 2 Apr 2025 19:26:37 +0100
Subject: [PATCH 78/88] AML: implement `ToString`, `ToBuffer`,
 `ToDecimalString`, `ToHexString`, and `ToInteger`

---
 src/aml/mod.rs               | 159 ++++++++++++++++++++++++++++++++---
 tools/aml_tester/src/main.rs |   5 ++
 2 files changed, 154 insertions(+), 10 deletions(-)

diff --git a/src/aml/mod.rs b/src/aml/mod.rs
index ed0588ea..7f7f6941 100644
--- a/src/aml/mod.rs
+++ b/src/aml/mod.rs
@@ -327,11 +327,12 @@ where
                     | Opcode::LLess => {
                         self.do_logical_op(&mut context, op)?;
                     }
-                    Opcode::ToBuffer
-                    | Opcode::ToDecimalString
-                    | Opcode::ToHexString
-                    | Opcode::ToInteger
-                    | Opcode::ToString => todo!(),
+                    Opcode::ToBuffer => self.do_to_buffer(&mut context, op)?,
+                    Opcode::ToInteger => self.do_to_integer(&mut context, op)?,
+                    Opcode::ToString => self.do_to_string(&mut context, op)?,
+                    Opcode::ToDecimalString | Opcode::ToHexString => {
+                        self.do_to_dec_hex_string(&mut context, op)?
+                    }
                     Opcode::Mid => self.do_mid(&mut context, op)?,
                     Opcode::Concat => self.do_concat(&mut context, op)?,
                     Opcode::ConcatRes => {
@@ -1202,11 +1203,10 @@ where
                     context.start_in_flight_op(OpInFlight::new(opcode, 2));
                 }
 
-                Opcode::ToBuffer
-                | Opcode::ToDecimalString
-                | Opcode::ToHexString
-                | Opcode::ToInteger
-                | Opcode::ToString => context.start_in_flight_op(OpInFlight::new(opcode, 2)),
+                Opcode::ToBuffer | Opcode::ToDecimalString | Opcode::ToHexString | Opcode::ToInteger => {
+                    context.start_in_flight_op(OpInFlight::new(opcode, 2))
+                }
+                Opcode::ToString => context.start_in_flight_op(OpInFlight::new(opcode, 3)),
 
                 Opcode::ObjectType => context.start_in_flight_op(OpInFlight::new(opcode, 1)),
                 Opcode::CopyObject => todo!(),
@@ -1485,6 +1485,140 @@ where
         Ok(())
     }
 
+    fn do_to_buffer(&self, context: &mut MethodContext, op: OpInFlight) -> Result<(), AmlError> {
+        let [Argument::Object(operand), target] = &op.arguments[..] else { panic!() };
+
+        let result = Arc::new(match **operand {
+            Object::Buffer(ref bytes) => Object::Buffer(bytes.clone()),
+            Object::Integer(value) => {
+                if self.dsdt_revision >= 2 {
+                    Object::Buffer(value.to_le_bytes().to_vec())
+                } else {
+                    Object::Buffer((value as u32).to_le_bytes().to_vec())
+                }
+            }
+            Object::String(ref value) => {
+                // XXX: an empty string is converted to an empty buffer, *without* the null-terminator
+                if value.is_empty() {
+                    Object::Buffer(vec![])
+                } else {
+                    let mut bytes = value.as_bytes().to_vec();
+                    bytes.push(b'\0');
+                    Object::Buffer(bytes)
+                }
+            }
+            _ => Err(AmlError::InvalidOperationOnObject { op: Operation::ToBuffer, typ: operand.typ() })?,
+        });
+
+        // TODO: use result of store
+        self.do_store(target, result.clone())?;
+        context.contribute_arg(Argument::Object(result));
+        Ok(())
+    }
+
+    fn do_to_integer(&self, context: &mut MethodContext, op: OpInFlight) -> Result<(), AmlError> {
+        let [Argument::Object(operand), target] = &op.arguments[..] else { panic!() };
+
+        let result = Arc::new(match **operand {
+            Object::Integer(value) => Object::Integer(value),
+            Object::Buffer(ref bytes) => {
+                /*
+                 * The spec says this should respect the revision of the current definition block.
+                 * Apparently, the NT interpreter always uses the first 8 bytes of the buffer.
+                 */
+                let mut to_interpret = [0u8; 8];
+                (to_interpret[0..usize::min(bytes.len(), 8)]).copy_from_slice(&bytes);
+                Object::Integer(u64::from_le_bytes(to_interpret))
+            }
+            Object::String(ref value) => {
+                if let Some(value) = value.strip_prefix("0x") {
+                    let parsed = u64::from_str_radix(value, 16).map_err(|_| {
+                        AmlError::InvalidOperationOnObject { op: Operation::ToInteger, typ: ObjectType::String }
+                    })?;
+                    Object::Integer(parsed)
+                } else {
+                    let parsed = u64::from_str_radix(value, 10).map_err(|_| {
+                        AmlError::InvalidOperationOnObject { op: Operation::ToInteger, typ: ObjectType::String }
+                    })?;
+                    Object::Integer(parsed)
+                }
+            }
+            _ => Err(AmlError::InvalidOperationOnObject { op: Operation::ToBuffer, typ: operand.typ() })?,
+        });
+
+        // TODO: use result of store
+        self.do_store(target, result.clone())?;
+        context.contribute_arg(Argument::Object(result));
+        Ok(())
+    }
+
+    fn do_to_string(&self, context: &mut MethodContext, op: OpInFlight) -> Result<(), AmlError> {
+        let [Argument::Object(source), Argument::Object(length), target] = &op.arguments[..] else { panic!() };
+        let source = source.as_buffer()?;
+        let length = length.as_integer()? as usize;
+
+        let result = Arc::new(if source.is_empty() {
+            Object::String(String::new())
+        } else {
+            let mut buffer = source.split_inclusive(|b| *b == b'\0').next().unwrap();
+            if length < usize::MAX {
+                buffer = &buffer[0..usize::min(length, buffer.len())];
+            }
+            let string = str::from_utf8(buffer).map_err(|_| AmlError::InvalidOperationOnObject {
+                op: Operation::ToString,
+                typ: ObjectType::Buffer,
+            })?;
+            Object::String(string.to_string())
+        });
+
+        // TODO: use result of store
+        self.do_store(target, result.clone())?;
+        context.contribute_arg(Argument::Object(result));
+        Ok(())
+    }
+
+    /// Perform a `ToDecimalString` or `ToHexString` operation
+    fn do_to_dec_hex_string(&self, context: &mut MethodContext, op: OpInFlight) -> Result<(), AmlError> {
+        let [Argument::Object(operand), target] = &op.arguments[..] else { panic!() };
+        let operand = operand.clone().unwrap_transparent_reference();
+
+        let result = Arc::new(match *operand {
+            Object::String(ref value) => Object::String(value.clone()),
+            Object::Integer(value) => match op.op {
+                Opcode::ToDecimalString => Object::String(value.to_string()),
+                Opcode::ToHexString => Object::String(alloc::format!("{:#x}", value)),
+                _ => panic!(),
+            },
+            Object::Buffer(ref bytes) => {
+                if bytes.is_empty() {
+                    Object::String(String::new())
+                } else {
+                    // TODO: there has GOT to be a better way to format directly into a string...
+                    let mut string = String::new();
+                    for byte in bytes {
+                        let as_str = match op.op {
+                            Opcode::ToDecimalString => alloc::format!("{},", byte),
+                            Opcode::ToHexString => alloc::format!("{:?},", byte),
+                            _ => panic!(),
+                        };
+                        string.push_str(&as_str);
+                    }
+                    // Remove last comma, if present
+                    if !string.is_empty() {
+                        string.pop();
+                    }
+                    Object::String(string)
+                }
+            }
+            _ => Err(AmlError::InvalidOperationOnObject { op: Operation::ToDecOrHexString, typ: operand.typ() })?,
+        });
+
+        // TODO: use result of store
+        self.do_store(target, result.clone())?;
+        context.contribute_arg(Argument::Object(result));
+        Ok(())
+    }
+
     fn do_mid(&self, context: &mut MethodContext, op: OpInFlight) -> Result<(), AmlError> {
         let [Argument::Object(source), Argument::Object(index), Argument::Object(length), target] =
             &op.arguments[..]
@@ -2572,6 +2706,11 @@ pub enum Operation {
     Release,
     ConvertToBuffer,
 
+    ToBuffer,
+    ToInteger,
+    ToString,
+    ToDecOrHexString,
+
     ReadBufferField,
     WriteBufferField,
     LogicalOp,
diff --git a/tools/aml_tester/src/main.rs b/tools/aml_tester/src/main.rs
index c7b0b025..02af7bab 100644
--- a/tools/aml_tester/src/main.rs
+++ b/tools/aml_tester/src/main.rs
@@ -11,6 +11,7 @@
 
 use acpi::aml::{namespace::AmlName, AmlError, Handle, Interpreter};
 use clap::{Arg, ArgAction, ArgGroup};
+use log::info;
 use pci_types::PciAddress;
 use std::{
     collections::HashSet,
@@ -401,6 +402,10 @@ impl acpi::aml::Handler for Handler {
         println!("write_pci_u32 ({address})<-{value}");
     }
 
+    fn handle_debug(&self, object: &acpi::aml::object::Object) {
+        info!("Debug store: {:?}", object);
+    }
+
     fn nanos_since_boot(&self) -> u64 {
         0
     }

From abb054b1bba08a3b965bbd08392175d2f0b2921b Mon Sep 17 00:00:00 2001
From: Isaac Woods <isaacwoods.home@gmail.com>
Date: Wed, 2 Apr 2025 19:27:43 +0100
Subject: [PATCH 79/88] AML: attempt implicit integer conversion on stores

---
 src/aml/mod.rs | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/src/aml/mod.rs b/src/aml/mod.rs
index 7f7f6941..fa4b1e94 100644
--- a/src/aml/mod.rs
+++ b/src/aml/mod.rs
@@ -1833,7 +1833,10 @@ where
                         // TODO: not sure if we should convert buffers to integers if needed here?
                         *target = self.do_field_read(field)?.as_integer()?;
                     }
-                    _ => panic!("Store to integer from unsupported object: {:?}", object),
+                    _ => {
+                        let as_integer = object.to_integer(if self.dsdt_revision >= 2 { 8 } else { 4 })?;
+                        *target = as_integer;
+                    }
                 },
                 Object::BufferField { .. } => match object.gain_mut() {
                     Object::Integer(value) => {

From 6e8148995ce3053a6cdf4939deaf60efbbf1f2ca Mon Sep 17 00:00:00 2001
From: Isaac Woods <isaacwoods.home@gmail.com>
Date: Wed, 2 Apr 2025 19:44:15 +0100
Subject: [PATCH 80/88] AML: fix handling of `If` ops at the end of blocks

---
 src/aml/mod.rs | 45 +++++++++++++++++++++++++++++++--------------
 1 file changed, 31 insertions(+), 14 deletions(-)

diff --git a/src/aml/mod.rs b/src/aml/mod.rs
index fa4b1e94..7d559f1b 100644
--- a/src/aml/mod.rs
+++ b/src/aml/mod.rs
@@ -480,12 +480,21 @@ where
                             context.start_new_block(BlockKind::IfThenBranch, remaining_then_length);
                         } else {
                             context.current_block.pc += remaining_then_length;
-                            // Skip over the prolog to the else branch if present
+
+                            /*
+                             * Skip over the prolog to the else branch if present. Also handle if
+                             * there are no more bytes to peek - the `If` op could be the last op
+                             * in a block.
+                             */
                             const DEF_ELSE_OP: u8 = 0xa1;
-                            // TODO: maybe need to handle error here
-                            if context.peek()? == DEF_ELSE_OP {
-                                context.next()?;
-                                let _else_length = context.pkglength()?;
+                            match context.peek() {
+                                Ok(DEF_ELSE_OP) => {
+                                    context.next()?;
+                                    let _else_length = context.pkglength()?;
+                                }
+                                Ok(_) => (),
+                                Err(AmlError::RunOutOfStream) => (),
+                                Err(other) => Err(other)?,
                             }
                         }
                     }
@@ -776,16 +785,24 @@ where
                         BlockKind::IfThenBranch => {
                             context.current_block = context.block_stack.pop().unwrap();
 
-                            // Check for an else-branch, and skip over it
-                            // TODO: if we run out of stream here, it might just be an IfOp at the
-                            // end I think?
+                            /*
+                             * Check for an else-branch, and skip over it. We need to handle the
+                             * case here where there isn't a next byte - that just means the `If`
+                             * is the last op in a block.
+                             */
                             const DEF_ELSE_OP: u8 = 0xa1;
-                            if context.peek()? == DEF_ELSE_OP {
-                                context.next()?;
-                                let start_pc = context.current_block.pc;
-                                let else_length = context.pkglength()?;
-                                context.current_block.pc += else_length - (context.current_block.pc - start_pc);
-                            }
+                            match context.peek() {
+                                Ok(DEF_ELSE_OP) => {
+                                    context.next()?;
+                                    let start_pc = context.current_block.pc;
+                                    let else_length = context.pkglength()?;
+                                    context.current_block.pc +=
+                                        else_length - (context.current_block.pc - start_pc);
+                                }
+                                Ok(_) => (),
+                                Err(AmlError::RunOutOfStream) => (),
+                                Err(other) => Err(other)?,
+                            };
 
                             continue;
                         }

From 4d66363b9aa4e0d83068458d7af7e462bd0dea6d Mon Sep 17 00:00:00 2001
From: Isaac Woods <isaacwoods.home@gmail.com>
Date: Wed, 2 Apr 2025 20:13:04 +0100
Subject: [PATCH 81/88] Add `ToInteger` test case from uACPI

---
 src/aml/mod.rs       |   6 +++
 tests/to_integer.asl | 109 +++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 115 insertions(+)
 create mode 100644 tests/to_integer.asl

diff --git a/src/aml/mod.rs b/src/aml/mod.rs
index 7d559f1b..6a5d091f 100644
--- a/src/aml/mod.rs
+++ b/src/aml/mod.rs
@@ -1548,6 +1548,12 @@ where
                 Object::Integer(u64::from_le_bytes(to_interpret))
             }
             Object::String(ref value) => {
+                /*
+                 * This is about the same level of effort as ACPICA puts in. The uACPI test suite
+                 * has tests that this fails - namely because of support for octal, signs, strings
+                 * that won't fit in a `u64` etc. We probably need to write a more robust parser
+                 * 'real' parser to handle those cases.
+                 */
                 if let Some(value) = value.strip_prefix("0x") {
                     let parsed = u64::from_str_radix(value, 16).map_err(|_| {
                         AmlError::InvalidOperationOnObject { op: Operation::ToInteger, typ: ObjectType::String }
diff --git a/tests/to_integer.asl b/tests/to_integer.asl
new file mode 100644
index 00000000..8de15d7c
--- /dev/null
+++ b/tests/to_integer.asl
@@ -0,0 +1,109 @@
+DefinitionBlock ("", "SSDT", 2, "uTEST", "TESTTABL", 0xF0F0F0F0)
+{
+    Name(FCNT, 0)
+
+    Method (CHEK, 3)
+    {
+        If (Arg0 != Arg1) {
+            FCNT++
+            Printf("On line %o: invalid number %o, expected %o", ToDecimalString(Arg2), ToHexString(Arg0), ToHexString(Arg1))
+        }
+    }
+
+    Method (MAIN, 0, NotSerialized)
+    {
+        Local0 = ToInteger(123)
+        Local1 = 123
+        CHEK(Local0, Local1, __LINE__)
+
+        Local0 = ToInteger("123")
+        Local1 = 123
+        CHEK(Local0, Local1, __LINE__)
+
+        Local0 = ToInteger("       \t\t\t\v       123")
+        Local1 = 123
+        CHEK(Local0, Local1, __LINE__)
+
+        Local0 = ToInteger("123abcd")
+        Local1 = 123
+        CHEK(Local0, Local1, __LINE__)
+
+        Local0 = ToInteger("0x123abcd")
+        Local1 = 0x123abcd
+        CHEK(Local0, Local1, __LINE__)
+
+        Local0 = ToInteger("")
+        Local1 = 0
+        CHEK(Local0, Local1, __LINE__)
+
+        Local0 = ToInteger("0X")
+        Local1 = 0
+        CHEK(Local0, Local1, __LINE__)
+
+        Local0 = ToInteger("0x")
+        Local1 = 0
+        CHEK(Local0, Local1, __LINE__)
+
+        Local0 = ToInteger("0")
+        Local1 = 0
+        CHEK(Local0, Local1, __LINE__)
+
+        Local0 = ToInteger("0xDeAdBeeF")
+        Local1 = 0xDEADBEEF
+        CHEK(Local0, Local1, __LINE__)
+
+        Local0 = ToInteger("0XDeAdBeeFCafeBabeHelloWorld")
+        Local1 = 0xDEADBEEFCAFEBABE
+        CHEK(Local0, Local1, __LINE__)
+
+        Local0 = ToInteger(Buffer { 0xDE, 0xAD, 0xBE, 0xEF })
+        Local1 = 0xEFBEADDE
+        CHEK(Local0, Local1, __LINE__)
+
+        Local0 = ToInteger(Buffer { 1 })
+        Local1 = 1
+        CHEK(Local0, Local1, __LINE__)
+
+        Local0 = ToInteger(Buffer { 0 })
+        Local1 = 0
+        CHEK(Local0, Local1, __LINE__)
+
+        Local0 = ToInteger(Buffer { 0xDE, 0xAD, 0xBE, 0xEF, 0xCA, 0xFE, 0xBA, 0xBE })
+        Local1 = 0xBEBAFECAEFBEADDE
+        CHEK(Local0, Local1, __LINE__)
+
+        // These are incompatible with ACPICA, skip if it's detected
+        // TODO: these currently fail on our interpreter too. Can fix later.
+        /*If (ToHexString(0xF) == "0xF") {
+            Local0 = ToInteger("99999999999999999999999999999999999999999999999")
+            Local1 = 0xFFFFFFFFFFFFFFFF
+            CHEK(Local0, Local1, __LINE__)
+
+            Local0 = ToInteger("0xDEADBEEFCAFEBABE1")
+            Local1 = 0xFFFFFFFFFFFFFFFF
+            CHEK(Local0, Local1, __LINE__)
+
+            Local0 = ToInteger("+123")
+            Local1 = 123
+            CHEK(Local0, Local1, __LINE__)
+
+            Local0 = ToInteger("-123")
+            Local1 = 0xFFFFFFFFFFFFFF85
+            CHEK(Local0, Local1, __LINE__)
+
+            Local0 = ToInteger("-0xDEADBEF HELLOWORLD")
+            Local1 = 0xFFFFFFFFF2152411
+            CHEK(Local0, Local1, __LINE__)
+
+            Local0 = ToInteger("+0XC0D\t123")
+            Local1 = 0xC0D
+            CHEK(Local0, Local1, __LINE__)
+
+            Local0 = ToInteger("0123")
+            Local1 = 83
+            CHEK(Local0, Local1, __LINE__)
+        }*/
+
+        Return (FCNT)
+    }
+}

From ccde4c7bd7dc4763dc54eb7b74b2dba2f9f54eee Mon Sep 17 00:00:00 2001
From: Isaac Woods <isaacwoods.home@gmail.com>
Date: Thu, 3 Apr 2025 02:04:15 +0100
Subject: [PATCH 82/88] AML: support `DefVarPackage`

This necessitated also unwrapping more references for package and buffer
lengths. This may need to be done elsewhere.
---
 src/aml/mod.rs            | 44 ++++++++++++++++++++-----
 tests/complex_package.asl | 68 +++++++++++++++++++++++++++++++++++++++
 tests/package.asl         |  7 ++++
 3 files changed, 111 insertions(+), 8 deletions(-)
 create mode 100644 tests/complex_package.asl

diff --git a/src/aml/mod.rs b/src/aml/mod.rs
index 6a5d091f..f36af4b8 100644
--- a/src/aml/mod.rs
+++ b/src/aml/mod.rs
@@ -429,7 +429,7 @@ where
                         else {
                             panic!()
                         };
-                        let buffer_size = buffer_size.as_integer()?;
+                        let buffer_size = buffer_size.clone().unwrap_reference().as_integer()?;
 
                         let buffer_len = pkg_length - (context.current_block.pc - start_pc);
                         let mut buffer = vec![0; buffer_size as usize];
@@ -441,7 +441,7 @@ where
 
                         context.contribute_arg(Argument::Object(Arc::new(Object::Buffer(buffer))));
                     }
-                    Opcode::Package => {
+                    Opcode::Package | Opcode::VarPackage => {
                         let mut elements = Vec::with_capacity(op.expected_arguments);
                         for arg in &op.arguments {
                             let Argument::Object(object) = arg else { panic!() };
@@ -766,13 +766,28 @@ where
                              */
                             assert!(context.block_stack.len() > 0);
 
-                            // TODO: I think we can handle VarPackage here as well because by the
-                            // time the block finishes, the first arg should be resolvable (the var
-                            // length) and so can be updated here...
                             if let Some(package_op) = context.in_flight.last_mut()
-                                && package_op.op == Opcode::Package
+                                && (package_op.op == Opcode::Package || package_op.op == Opcode::VarPackage)
                             {
-                                let num_elements_left = package_op.expected_arguments - package_op.arguments.len();
+                                let num_elements_left = match package_op.op {
+                                    Opcode::Package => package_op.expected_arguments - package_op.arguments.len(),
+                                    Opcode::VarPackage => {
+                                        let Argument::Object(total_elements) = &package_op.arguments[0] else {
+                                            panic!()
+                                        };
+                                        let total_elements =
+                                            total_elements.clone().unwrap_reference().as_integer()? as usize;
+
+                                        // Update the expected number of arguments to terminate the in-flight op
+                                        package_op.expected_arguments = total_elements;
+
+                                        total_elements - package_op.arguments.len()
+                                    }
+                                    _ => panic!(
+                                        "Current in-flight op is not a `Package` or `VarPackage` when finished parsing package block"
+                                    ),
+                                };
+
                                 for _ in 0..num_elements_left {
                                     package_op.arguments.push(Argument::Object(Arc::new(Object::Uninitialized)));
                                 }
@@ -908,7 +923,20 @@ where
                     context.start_in_flight_op(OpInFlight::new(Opcode::Package, num_elements as usize));
                     context.start_new_block(BlockKind::Package, remaining_length);
                 }
-                Opcode::VarPackage => todo!(),
+                Opcode::VarPackage => {
+                    let start_pc = context.current_block.pc;
+                    let pkg_length = context.pkglength()?;
+                    let remaining_length = pkg_length - (context.current_block.pc - start_pc);
+
+                    /*
+                     * For variable packages, we're first going to parse a `TermArg` that encodes,
+                     * dynamically, how many elements the package will have. We then accept as many
+                     * elements as remain in the block, and we'll sort out how many are supposed to
+                     * be in the package later.
+                     */
+                    context.start_in_flight_op(OpInFlight::new(Opcode::VarPackage, usize::MAX));
+                    context.start_new_block(BlockKind::Package, remaining_length);
+                }
                 Opcode::Method => {
                     let start_pc = context.current_block.pc;
                     let pkg_length = context.pkglength()?;
diff --git a/tests/complex_package.asl b/tests/complex_package.asl
new file mode 100644
index 00000000..944b75f0
--- /dev/null
+++ b/tests/complex_package.asl
@@ -0,0 +1,68 @@
+DefinitionBlock ("", "DSDT", 2, "uTEST", "TESTTABL", 0xF0F0F0F0)
+{
+    Method(GPKG) {
+        Local1 = 10
+        Local0 = Package (Local1) {
+            0x123,
+            0x321,
+            Package {
+                0x321,
+                "123",
+                Package {
+                    0x321,
+                    Package {
+                        0x321,
+                        "123",
+                        Package {
+                            0x321,
+                            "123",
+                                Package {
+                                    0x321,
+                                    Package {
+                                        0x321,
+                                        "123",
+                                        Package (Local1) {
+                                            0x321,
+                                            "123",
+                                            999,
+                                        },
+                                        999,
+                                    },
+                                "123",
+                                999,
+                            },
+                            999,
+                        },
+                        999,
+                    },
+                    "123",
+                    999,
+                },
+                999,
+            },
+            "Hello world",
+            Package {
+                0x321,
+                "Hello",
+            },
+            Package {
+                0x321,
+                "World",
+            },
+            Package {
+                Buffer (Local1) { 0xFF },
+                0xDEADBEEF,
+            },
+            Buffer { 1, 2, 3 }
+        }
+
+        Return (Local0)
+    }
+
+    Method (MAIN) {
+        Local0 = GPKG()
+        Debug = Local0
+        Local0 = 1
+        Return (Local0)
+    }
+}
diff --git a/tests/package.asl b/tests/package.asl
index 2edb838f..cce92ba1 100644
--- a/tests/package.asl
+++ b/tests/package.asl
@@ -19,4 +19,11 @@ DefinitionBlock("package.aml", "DSDT", 1, "RSACPI", "PACKGE", 1) {
         Package { 0x0a, 0x0b, 0x0c },
         Package { 0x0d, 0x0e, 0x0f },
     })
+
+    Name(LEN, 10)
+    Name(BAZ, Package (LEN) {
+        4,
+        11,
+        16,
+    })
 }

From a880918c875748fe4fd922fbd345023beadfae8d Mon Sep 17 00:00:00 2001
From: Isaac Woods <isaacwoods.home@gmail.com>
Date: Fri, 4 Apr 2025 22:03:07 +0100
Subject: [PATCH 83/88] AML: improve safety of mutable object handling

---
 src/aml/mod.rs       | 220 +++++++++++++++++++++++--------------------
 src/aml/namespace.rs |  15 +--
 src/aml/object.rs    | 187 +++++++++++++++++++++++++-----------
 src/aml/resource.rs  |   5 +-
 4 files changed, 260 insertions(+), 167 deletions(-)

diff --git a/src/aml/mod.rs b/src/aml/mod.rs
index f36af4b8..b1430ca7 100644
--- a/src/aml/mod.rs
+++ b/src/aml/mod.rs
@@ -28,7 +28,6 @@ use alloc::{
     boxed::Box,
     collections::btree_map::BTreeMap,
     string::{String, ToString},
-    sync::Arc,
     vec,
     vec::Vec,
 };
@@ -44,8 +43,10 @@ use object::{
     FieldUpdateRule,
     MethodFlags,
     Object,
+    ObjectToken,
     ObjectType,
     ReferenceKind,
+    WrappedObject,
 };
 use op_region::{OpRegion, RegionHandler, RegionSpace};
 use spinning_top::Spinlock;
@@ -56,6 +57,7 @@ where
 {
     handler: H,
     pub namespace: Spinlock<Namespace>,
+    pub object_token: Spinlock<ObjectToken>,
     context_stack: Spinlock<Vec<MethodContext>>,
     dsdt_revision: u8,
     region_handlers: Spinlock<BTreeMap<RegionSpace, Box<dyn RegionHandler>>>,
@@ -78,6 +80,7 @@ where
         Interpreter {
             handler,
             namespace: Spinlock::new(Namespace::new()),
+            object_token: Spinlock::new(unsafe { ObjectToken::create_interpreter_token() }),
             context_stack: Spinlock::new(Vec::new()),
             dsdt_revision,
             region_handlers: Spinlock::new(BTreeMap::new()),
@@ -133,7 +136,7 @@ where
     /// Invoke a method by its name, with the given set of arguments. If the referenced object is
     /// not a method, the object will instead be returned - this is useful for objects that can
     /// either be defined directly, or through a method (e.g. a `_CRS` object).
-    pub fn invoke_method(&self, path: AmlName, args: Vec<Arc<Object>>) -> Result<Arc<Object>, AmlError> {
+    pub fn invoke_method(&self, path: AmlName, args: Vec<WrappedObject>) -> Result<WrappedObject, AmlError> {
         trace!("Invoking AML method: {}", path);
 
         let object = self.namespace.lock().get(path.clone())?.clone();
@@ -150,8 +153,8 @@ where
     pub fn invoke_method_if_present(
         &self,
         path: AmlName,
-        args: Vec<Arc<Object>>,
-    ) -> Result<Option<Arc<Object>>, AmlError> {
+        args: Vec<WrappedObject>,
+    ) -> Result<Option<WrappedObject>, AmlError> {
         match self.invoke_method(path.clone(), args) {
             Ok(result) => Ok(Some(result)),
             Err(AmlError::ObjectDoesNotExist(not_present)) => {
@@ -253,7 +256,7 @@ where
         info!("Initialized {} devices", num_devices_initialized);
     }
 
-    fn do_execute_method(&self, mut context: MethodContext) -> Result<Arc<Object>, AmlError> {
+    fn do_execute_method(&self, mut context: MethodContext) -> Result<WrappedObject, AmlError> {
         /*
          * This is the main loop that executes operations. Every op is handled at the top-level of
          * the loop to prevent pathological stack growth from nested operations.
@@ -301,7 +304,8 @@ where
                     }
                     Opcode::Increment | Opcode::Decrement => {
                         let [Argument::Object(operand)] = &op.arguments[..] else { panic!() };
-                        let Object::Integer(operand) = operand.gain_mut() else {
+                        let token = self.object_token.lock();
+                        let Object::Integer(operand) = (unsafe { operand.gain_mut(&*token) }) else {
                             Err(AmlError::ObjectNotOfExpectedType {
                                 expected: ObjectType::Integer,
                                 got: operand.typ(),
@@ -349,7 +353,7 @@ where
                             buffer.push(0x78);
                             // Don't calculate the new real checksum - just use 0
                             buffer.push(0x00);
-                            Arc::new(Object::Buffer(buffer))
+                            Object::Buffer(buffer).wrap()
                         };
                         // TODO: use potentially-updated result for return value here
                         self.do_store(target, result.clone())?;
@@ -391,7 +395,7 @@ where
                             length: region_length.as_integer()?,
                             parent_device_path: context.current_scope.clone(),
                         });
-                        self.namespace.lock().insert(name.resolve(&context.current_scope)?, Arc::new(region))?;
+                        self.namespace.lock().insert(name.resolve(&context.current_scope)?, region.wrap())?;
                     }
                     Opcode::DataRegion => {
                         let [
@@ -418,7 +422,7 @@ where
                             length: 0,
                             parent_device_path: context.current_scope.clone(),
                         });
-                        self.namespace.lock().insert(name.resolve(&context.current_scope)?, Arc::new(region))?;
+                        self.namespace.lock().insert(name.resolve(&context.current_scope)?, region.wrap())?;
                     }
                     Opcode::Buffer => {
                         let [
@@ -439,7 +443,7 @@ where
                         );
                         context.current_block.pc += buffer_len;
 
-                        context.contribute_arg(Argument::Object(Arc::new(Object::Buffer(buffer))));
+                        context.contribute_arg(Argument::Object(Object::Buffer(buffer).wrap()));
                     }
                     Opcode::Package | Opcode::VarPackage => {
                         let mut elements = Vec::with_capacity(op.expected_arguments);
@@ -462,7 +466,7 @@ where
                         assert_eq!(context.current_block.kind, BlockKind::Package);
                         assert_eq!(context.peek(), Err(AmlError::RunOutOfStream));
                         context.current_block = context.block_stack.pop().unwrap();
-                        context.contribute_arg(Argument::Object(Arc::new(Object::Package(elements))));
+                        context.contribute_arg(Argument::Object(Object::Package(elements).wrap()));
                     }
                     Opcode::If => {
                         let [
@@ -518,11 +522,7 @@ where
                         };
                         self.namespace.lock().insert(
                             name.resolve(&context.current_scope)?,
-                            Arc::new(Object::BufferField {
-                                buffer: buffer.clone(),
-                                offset: offset as usize,
-                                length,
-                            }),
+                            Object::BufferField { buffer: buffer.clone(), offset: offset as usize, length }.wrap(),
                         )?;
                     }
                     Opcode::CreateField => {
@@ -537,11 +537,12 @@ where
 
                         self.namespace.lock().insert(
                             name.resolve(&context.current_scope)?,
-                            Arc::new(Object::BufferField {
+                            Object::BufferField {
                                 buffer: buffer.clone(),
                                 offset: bit_index as usize,
                                 length: num_bits as usize,
-                            }),
+                            }
+                            .wrap(),
                         )?;
                     }
                     Opcode::Store => {
@@ -551,7 +552,7 @@ where
                     Opcode::RefOf => {
                         let [Argument::Object(object)] = &op.arguments[..] else { panic!() };
                         let reference =
-                            Arc::new(Object::Reference { kind: ReferenceKind::RefOf, inner: object.clone() });
+                            Object::Reference { kind: ReferenceKind::RefOf, inner: object.clone() }.wrap();
                         context.contribute_arg(Argument::Object(reference));
                     }
                     Opcode::CondRefOf => {
@@ -560,11 +561,11 @@ where
                             Object::Integer(0)
                         } else {
                             let reference =
-                                Arc::new(Object::Reference { kind: ReferenceKind::RefOf, inner: object.clone() });
+                                Object::Reference { kind: ReferenceKind::RefOf, inner: object.clone() }.wrap();
                             self.do_store(target, reference)?;
                             Object::Integer(u64::MAX)
                         };
-                        context.contribute_arg(Argument::Object(Arc::new(result)));
+                        context.contribute_arg(Argument::Object(result.wrap()));
                     }
                     Opcode::DerefOf => {
                         let [Argument::Object(object)] = &op.arguments[..] else { panic!() };
@@ -673,7 +674,7 @@ where
                             ObjectType::RawDataBuffer => todo!(),
                         };
 
-                        context.contribute_arg(Argument::Object(Arc::new(Object::Integer(typ))));
+                        context.contribute_arg(Argument::Object(Object::Integer(typ).wrap()));
                     }
                     Opcode::SizeOf => self.do_size_of(&mut context, op)?,
                     Opcode::Index => self.do_index(&mut context, op)?,
@@ -731,7 +732,7 @@ where
                      */
                     match context.current_block.kind {
                         BlockKind::Table => {
-                            break Ok(Arc::new(Object::Uninitialized));
+                            break Ok(Object::Uninitialized.wrap());
                         }
                         BlockKind::Method { method_scope } => {
                             self.namespace.lock().remove_level(method_scope)?;
@@ -744,7 +745,7 @@ where
                                  * If there is no explicit `Return` op, the result is undefined. We
                                  * just return an uninitialized object.
                                  */
-                                return Ok(Arc::new(Object::Uninitialized));
+                                return Ok(Object::Uninitialized.wrap());
                             }
                         }
                         BlockKind::Scope { old_scope } => {
@@ -789,7 +790,7 @@ where
                                 };
 
                                 for _ in 0..num_elements_left {
-                                    package_op.arguments.push(Argument::Object(Arc::new(Object::Uninitialized)));
+                                    package_op.arguments.push(Argument::Object(Object::Uninitialized.wrap()));
                                 }
                             }
 
@@ -836,10 +837,10 @@ where
             };
             match opcode {
                 Opcode::Zero => {
-                    context.last_op()?.arguments.push(Argument::Object(Arc::new(Object::Integer(0))));
+                    context.last_op()?.arguments.push(Argument::Object(Object::Integer(0).wrap()));
                 }
                 Opcode::One => {
-                    context.last_op()?.arguments.push(Argument::Object(Arc::new(Object::Integer(1))));
+                    context.last_op()?.arguments.push(Argument::Object(Object::Integer(1).wrap()));
                 }
                 Opcode::Alias => {
                     let source = context.namestring()?;
@@ -860,15 +861,15 @@ where
                 }
                 Opcode::BytePrefix => {
                     let value = context.next()?;
-                    context.last_op()?.arguments.push(Argument::Object(Arc::new(Object::Integer(value as u64))));
+                    context.last_op()?.arguments.push(Argument::Object(Object::Integer(value as u64).wrap()));
                 }
                 Opcode::WordPrefix => {
                     let value = context.next_u16()?;
-                    context.last_op()?.arguments.push(Argument::Object(Arc::new(Object::Integer(value as u64))));
+                    context.last_op()?.arguments.push(Argument::Object(Object::Integer(value as u64).wrap()));
                 }
                 Opcode::DWordPrefix => {
                     let value = context.next_u32()?;
-                    context.last_op()?.arguments.push(Argument::Object(Arc::new(Object::Integer(value as u64))));
+                    context.last_op()?.arguments.push(Argument::Object(Object::Integer(value as u64).wrap()));
                 }
                 Opcode::StringPrefix => {
                     let str_start = context.current_block.pc;
@@ -878,11 +879,11 @@ where
                         str::from_utf8(&context.current_block.stream()[str_start..(context.current_block.pc - 1)])
                             .unwrap(),
                     );
-                    context.last_op()?.arguments.push(Argument::Object(Arc::new(Object::String(str))));
+                    context.last_op()?.arguments.push(Argument::Object(Object::String(str).wrap()));
                 }
                 Opcode::QWordPrefix => {
                     let value = context.next_u64()?;
-                    context.last_op()?.arguments.push(Argument::Object(Arc::new(Object::Integer(value))));
+                    context.last_op()?.arguments.push(Argument::Object(Object::Integer(value).wrap()));
                 }
                 Opcode::Scope => {
                     let start_pc = context.current_block.pc;
@@ -950,7 +951,7 @@ where
                     context.current_block.pc += code_len;
 
                     let name = name.resolve(&context.current_scope)?;
-                    self.namespace.lock().insert(name, Arc::new(Object::Method { code, flags }))?;
+                    self.namespace.lock().insert(name, Object::Method { code, flags }.wrap())?;
                 }
                 Opcode::External => {
                     let _name = context.namestring()?;
@@ -963,13 +964,13 @@ where
 
                     let name = name.resolve(&context.current_scope)?;
                     let mutex = self.handler.create_mutex();
-                    self.namespace.lock().insert(name, Arc::new(Object::Mutex { mutex, sync_level }))?;
+                    self.namespace.lock().insert(name, Object::Mutex { mutex, sync_level }.wrap())?;
                 }
                 Opcode::Event => {
                     let name = context.namestring()?;
 
                     let name = name.resolve(&context.current_scope)?;
-                    self.namespace.lock().insert(name, Arc::new(Object::Event))?;
+                    self.namespace.lock().insert(name, Object::Event.wrap())?;
                 }
                 Opcode::LoadTable => todo!(),
                 Opcode::Load => todo!(),
@@ -983,10 +984,10 @@ where
                 Opcode::Notify => todo!(),
                 Opcode::FromBCD | Opcode::ToBCD => context.start_in_flight_op(OpInFlight::new(opcode, 2)),
                 Opcode::Revision => {
-                    context.contribute_arg(Argument::Object(Arc::new(Object::Integer(INTERPRETER_REVISION))));
+                    context.contribute_arg(Argument::Object(Object::Integer(INTERPRETER_REVISION).wrap()));
                 }
                 Opcode::Debug => {
-                    context.contribute_arg(Argument::Object(Arc::new(Object::Debug)));
+                    context.contribute_arg(Argument::Object(Object::Debug.wrap()));
                 }
                 Opcode::Fatal => {
                     let typ = context.next()?;
@@ -1000,7 +1001,7 @@ where
                 Opcode::Timer => {
                     // Time has to be monotonically-increasing, in 100ns units
                     let time = self.handler.nanos_since_boot() / 100;
-                    context.contribute_arg(Argument::Object(Arc::new(Object::Integer(time))));
+                    context.contribute_arg(Argument::Object(Object::Integer(time).wrap()));
                 }
                 Opcode::OpRegion => {
                     let name = context.namestring()?;
@@ -1077,7 +1078,7 @@ where
                     };
                     let mut namespace = self.namespace.lock();
                     namespace.add_level(new_scope.clone(), kind)?;
-                    namespace.insert(new_scope.clone(), Arc::new(object))?;
+                    namespace.insert(new_scope.clone(), object.wrap())?;
 
                     let old_scope = mem::replace(&mut context.current_scope, new_scope);
                     context.start_new_block(BlockKind::Scope { old_scope }, remaining_length);
@@ -1096,7 +1097,7 @@ where
                     let object = Object::Processor { proc_id, pblk_address, pblk_length };
                     let mut namespace = self.namespace.lock();
                     namespace.add_level(new_scope.clone(), NamespaceLevelKind::Processor)?;
-                    namespace.insert(new_scope.clone(), Arc::new(object))?;
+                    namespace.insert(new_scope.clone(), object.wrap())?;
 
                     let old_scope = mem::replace(&mut context.current_scope, new_scope);
                     context.start_new_block(BlockKind::Scope { old_scope }, remaining_length);
@@ -1114,24 +1115,22 @@ where
                     let object = Object::PowerResource { system_level, resource_order };
                     let mut namespace = self.namespace.lock();
                     namespace.add_level(new_scope.clone(), NamespaceLevelKind::PowerResource)?;
-                    namespace.insert(new_scope.clone(), Arc::new(object))?;
+                    namespace.insert(new_scope.clone(), object.wrap())?;
 
                     let old_scope = mem::replace(&mut context.current_scope, new_scope);
                     context.start_new_block(BlockKind::Scope { old_scope }, remaining_length);
                 }
                 Opcode::Local(local) => {
                     let local = context.locals[local as usize].clone();
-                    context.last_op()?.arguments.push(Argument::Object(Arc::new(Object::Reference {
-                        kind: ReferenceKind::LocalOrArg,
-                        inner: local,
-                    })));
+                    context.last_op()?.arguments.push(Argument::Object(
+                        Object::Reference { kind: ReferenceKind::LocalOrArg, inner: local }.wrap(),
+                    ));
                 }
                 Opcode::Arg(arg) => {
                     let arg = context.args[arg as usize].clone();
-                    context.last_op()?.arguments.push(Argument::Object(Arc::new(Object::Reference {
-                        kind: ReferenceKind::LocalOrArg,
-                        inner: arg,
-                    })));
+                    context.last_op()?.arguments.push(Argument::Object(
+                        Object::Reference { kind: ReferenceKind::LocalOrArg, inner: arg }.wrap(),
+                    ));
                 }
                 Opcode::Store => context.start_in_flight_op(OpInFlight::new(Opcode::Store, 2)),
                 Opcode::RefOf => context.start_in_flight_op(OpInFlight::new(Opcode::RefOf, 1)),
@@ -1162,14 +1161,14 @@ where
                             Ok((_, object)) => {
                                 let reference =
                                     Object::Reference { kind: ReferenceKind::RefOf, inner: object.clone() };
-                                context.last_op()?.arguments.push(Argument::Object(Arc::new(reference)));
+                                context.last_op()?.arguments.push(Argument::Object(reference.wrap()));
                             }
                             Err(AmlError::ObjectDoesNotExist(_)) => {
                                 let reference = Object::Reference {
                                     kind: ReferenceKind::Unresolved,
-                                    inner: Arc::new(Object::String(name.to_string())),
+                                    inner: Object::String(name.to_string()).wrap(),
                                 };
-                                context.last_op()?.arguments.push(Argument::Object(Arc::new(reference)));
+                                context.last_op()?.arguments.push(Argument::Object(reference.wrap()));
                             }
                             Err(other) => Err(other)?,
                         }
@@ -1313,7 +1312,7 @@ where
                     self.handler.breakpoint();
                 }
                 Opcode::Ones => {
-                    context.last_op()?.arguments.push(Argument::Object(Arc::new(Object::Integer(u64::MAX))));
+                    context.last_op()?.arguments.push(Argument::Object(Object::Integer(u64::MAX).wrap()));
                 }
 
                 Opcode::InternalMethodCall => panic!(),
@@ -1368,7 +1367,7 @@ where
                         bit_length: field_length,
                         flags: FieldFlags(flags),
                     });
-                    self.namespace.lock().insert(field_name.resolve(&context.current_scope)?, Arc::new(field))?;
+                    self.namespace.lock().insert(field_name.resolve(&context.current_scope)?, field.wrap())?;
 
                     field_offset += field_length;
                 }
@@ -1391,7 +1390,7 @@ where
             Opcode::Multiply => left.wrapping_mul(right),
             Opcode::Divide => {
                 if let Some(remainder) = target2 {
-                    self.do_store(remainder, Arc::new(Object::Integer(left.wrapping_rem(right))))?;
+                    self.do_store(remainder, Object::Integer(left.wrapping_rem(right)).wrap())?;
                 }
                 left.wrapping_div_euclid(right)
             }
@@ -1406,7 +1405,7 @@ where
             _ => panic!(),
         };
 
-        let result = Arc::new(Object::Integer(result));
+        let result = Object::Integer(result).wrap();
         // TODO: use result for arg
         self.do_store(target, result.clone())?;
         context.contribute_arg(Argument::Object(result));
@@ -1450,7 +1449,7 @@ where
             _ => panic!(),
         };
 
-        context.contribute_arg(Argument::Object(Arc::new(Object::Integer(result))));
+        context.contribute_arg(Argument::Object(Object::Integer(result).wrap()));
         Ok(())
     }
 
@@ -1462,7 +1461,7 @@ where
 
             if let Some(prev_op) = context.in_flight.last_mut() {
                 if prev_op.arguments.len() < prev_op.expected_arguments {
-                    prev_op.arguments.push(Argument::Object(Arc::new(Object::Integer(result))));
+                    prev_op.arguments.push(Argument::Object(Object::Integer(result).wrap()));
                 }
             }
 
@@ -1526,14 +1525,14 @@ where
         };
         let result = if result { Object::Integer(u64::MAX) } else { Object::Integer(0) };
 
-        context.contribute_arg(Argument::Object(Arc::new(result)));
+        context.contribute_arg(Argument::Object(result.wrap()));
         Ok(())
     }
 
     fn do_to_buffer(&self, context: &mut MethodContext, op: OpInFlight) -> Result<(), AmlError> {
         let [Argument::Object(operand), target] = &op.arguments[..] else { panic!() };
 
-        let result = Arc::new(match **operand {
+        let result = match **operand {
             Object::Buffer(ref bytes) => Object::Buffer(bytes.clone()),
             Object::Integer(value) => {
                 if self.dsdt_revision >= 2 {
@@ -1553,7 +1552,8 @@ where
                 }
             }
             _ => Err(AmlError::InvalidOperationOnObject { op: Operation::ToBuffer, typ: operand.typ() })?,
-        });
+        }
+        .wrap();
 
         // TODO: use result of store
         self.do_store(target, result.clone())?;
@@ -1564,7 +1564,7 @@ where
     fn do_to_integer(&self, context: &mut MethodContext, op: OpInFlight) -> Result<(), AmlError> {
         let [Argument::Object(operand), target] = &op.arguments[..] else { panic!() };
 
-        let result = Arc::new(match **operand {
+        let result = match **operand {
             Object::Integer(value) => Object::Integer(value),
             Object::Buffer(ref bytes) => {
                 /*
@@ -1595,7 +1595,8 @@ where
                 }
             }
             _ => Err(AmlError::InvalidOperationOnObject { op: Operation::ToBuffer, typ: operand.typ() })?,
-        });
+        }
+        .wrap();
 
         // TODO: use result of store
         self.do_store(target, result.clone())?;
@@ -1608,7 +1609,7 @@ where
         let source = source.as_buffer()?;
         let length = length.as_integer()? as usize;
 
-        let result = Arc::new(if source.is_empty() {
+        let result = if source.is_empty() {
             Object::String(String::new())
         } else {
             let mut buffer = source.split_inclusive(|b| *b == b'\0').next().unwrap();
@@ -1620,7 +1621,8 @@ where
                 typ: ObjectType::Buffer,
             })?;
             Object::String(string.to_string())
-        });
+        }
+        .wrap();
 
         // TODO: use result of store
         self.do_store(target, result.clone())?;
@@ -1633,7 +1635,7 @@ where
         let [Argument::Object(operand), target] = &op.arguments[..] else { panic!() };
         let operand = operand.clone().unwrap_transparent_reference();
 
-        let result = Arc::new(match *operand {
+        let result = match *operand {
             Object::String(ref value) => Object::String(value.clone()),
             Object::Integer(value) => match op.op {
                 Opcode::ToDecimalString => Object::String(value.to_string()),
@@ -1662,7 +1664,8 @@ where
                 }
             }
             _ => Err(AmlError::InvalidOperationOnObject { op: Operation::ToDecOrHexString, typ: operand.typ() })?,
-        });
+        }
+        .wrap();
 
         // TODO: use result of store
         self.do_store(target, result.clone())?;
@@ -1679,7 +1682,7 @@ where
         let index = index.as_integer()? as usize;
         let length = length.as_integer()? as usize;
 
-        let result = Arc::new(match **source {
+        let result = match **source {
             Object::String(ref string) => {
                 if index >= string.len() {
                     Object::String(String::new())
@@ -1699,7 +1702,8 @@ where
                 }
             }
             _ => Err(AmlError::InvalidOperationOnObject { op: Operation::Mid, typ: source.typ() })?,
-        });
+        }
+        .wrap();
 
         self.do_store(target, result.clone())?;
         context.contribute_arg(Argument::Object(result));
@@ -1744,17 +1748,17 @@ where
                     buffer.extend_from_slice(&(source1 as u32).to_le_bytes());
                     buffer.extend_from_slice(&(source2 as u32).to_le_bytes());
                 }
-                Arc::new(Object::Buffer(buffer))
+                Object::Buffer(buffer).wrap()
             }
             ObjectType::Buffer => {
                 let mut buffer = source1.as_buffer()?.to_vec();
                 buffer.extend(source2.to_buffer(if self.dsdt_revision >= 2 { 8 } else { 4 })?);
-                Arc::new(Object::Buffer(buffer))
+                Object::Buffer(buffer).wrap()
             }
             ObjectType::String | _ => {
                 let source1 = resolve_as_string(&source1);
                 let source2 = resolve_as_string(&source2);
-                Arc::new(Object::String(source1 + &source2))
+                Object::String(source1 + &source2).wrap()
             }
         };
         // TODO: use result of store
@@ -1775,7 +1779,7 @@ where
             value >>= 4;
         }
 
-        context.contribute_arg(Argument::Object(Arc::new(Object::Integer(result))));
+        context.contribute_arg(Argument::Object(Object::Integer(result).wrap()));
         Ok(())
     }
 
@@ -1791,7 +1795,7 @@ where
             i += 1;
         }
 
-        context.contribute_arg(Argument::Object(Arc::new(Object::Integer(result))));
+        context.contribute_arg(Argument::Object(Object::Integer(result).wrap()));
         Ok(())
     }
 
@@ -1806,7 +1810,7 @@ where
             _ => Err(AmlError::InvalidOperationOnObject { op: Operation::SizeOf, typ: object.typ() })?,
         };
 
-        context.contribute_arg(Argument::Object(Arc::new(Object::Integer(result as u64))));
+        context.contribute_arg(Argument::Object(Object::Integer(result as u64).wrap()));
         Ok(())
     }
 
@@ -1816,7 +1820,7 @@ where
         };
         let index_value = index_value.as_integer()?;
 
-        let result = Arc::new(match **object {
+        let result = match **object {
             Object::Buffer(ref buffer) => {
                 if index_value as usize >= buffer.len() {
                     Err(AmlError::IndexOutOfBounds)?
@@ -1824,11 +1828,12 @@ where
 
                 Object::Reference {
                     kind: ReferenceKind::RefOf,
-                    inner: Arc::new(Object::BufferField {
+                    inner: Object::BufferField {
                         buffer: object.clone(),
                         offset: index_value as usize * 8,
                         length: 8,
-                    }),
+                    }
+                    .wrap(),
                 }
             }
             Object::String(ref string) => {
@@ -1838,11 +1843,12 @@ where
 
                 Object::Reference {
                     kind: ReferenceKind::RefOf,
-                    inner: Arc::new(Object::BufferField {
+                    inner: Object::BufferField {
                         buffer: object.clone(),
                         offset: index_value as usize * 8,
                         length: 8,
-                    }),
+                    }
+                    .wrap(),
                 }
             }
             Object::Package(ref package) => {
@@ -1850,7 +1856,8 @@ where
                 Object::Reference { kind: ReferenceKind::RefOf, inner: element.clone() }
             }
             _ => Err(AmlError::IndexOutOfBounds)?,
-        });
+        }
+        .wrap();
 
         self.do_store(target, result.clone())?;
         context.contribute_arg(Argument::Object(result));
@@ -1861,22 +1868,23 @@ where
     // access. I guess we need to return something here really and use it instead of the result
     // when returning?? We need to look carefully at all use-sites to make sure it actually returns
     // the result of the store, not the object it passed to us.
-    fn do_store(&self, target: &Argument, object: Arc<Object>) -> Result<(), AmlError> {
+    fn do_store(&self, target: &Argument, object: WrappedObject) -> Result<(), AmlError> {
         // TODO: find the destination (need to handle references, debug objects, etc.)
         // TODO: convert object to be of the type of destination, in line with 19.3.5 of the spec
         // TODO: write the object to the destination, including e.g. field writes that then lead to
         // literally god knows what.
         let object = object.unwrap_transparent_reference();
+        let token = self.object_token.lock();
 
         match target {
-            Argument::Object(target) => match target.gain_mut() {
-                Object::Integer(target) => match object.gain_mut() {
+            Argument::Object(target) => match unsafe { target.gain_mut(&*token) } {
+                Object::Integer(target) => match unsafe { object.gain_mut(&*token) } {
                     Object::Integer(value) => {
                         *target = *value;
                     }
                     Object::BufferField { .. } => {
                         let mut buffer = [0u8; 8];
-                        object.gain_mut().read_buffer_field(&mut buffer)?;
+                        unsafe { object.gain_mut(&*token) }.read_buffer_field(&mut buffer)?;
                         let value = u64::from_le_bytes(buffer);
                         *target = value;
                     }
@@ -1889,12 +1897,12 @@ where
                         *target = as_integer;
                     }
                 },
-                Object::BufferField { .. } => match object.gain_mut() {
+                Object::BufferField { .. } => match unsafe { object.gain_mut(&*token) } {
                     Object::Integer(value) => {
-                        target.gain_mut().write_buffer_field(&value.to_le_bytes())?;
+                        unsafe { target.gain_mut(&*token) }.write_buffer_field(&value.to_le_bytes(), &*token)?;
                     }
                     Object::Buffer(value) => {
-                        target.gain_mut().write_buffer_field(&value.as_slice())?;
+                        unsafe { target.gain_mut(&*token) }.write_buffer_field(&value.as_slice(), &*token)?;
                     }
                     _ => panic!(),
                 },
@@ -1906,10 +1914,14 @@ where
                             if let Object::Reference { kind: inner_kind, inner: inner_inner } = &**inner {
                                 // TODO: this should store into the reference, potentially doing an
                                 // implicit cast
-                                todo!()
+                                unsafe {
+                                    *inner_inner.gain_mut(&*token) = object.gain_mut(&*token).clone();
+                                }
                             } else {
                                 // Overwrite the value
-                                *inner.gain_mut() = object.gain_mut().clone();
+                                unsafe {
+                                    *inner.gain_mut(&*token) = object.gain_mut(&*token).clone();
+                                }
                             }
                         }
                         ReferenceKind::Unresolved => todo!(),
@@ -1933,7 +1945,7 @@ where
     /// operation regions, and then shifting and masking the resulting value as appropriate. Will
     /// return either an `Integer` or `Buffer` as appropriate, guided by the size of the field
     /// and expected integer size (as per the DSDT revision).
-    fn do_field_read(&self, field: &FieldUnit) -> Result<Arc<Object>, AmlError> {
+    fn do_field_read(&self, field: &FieldUnit) -> Result<WrappedObject, AmlError> {
         let needs_buffer = if self.dsdt_revision >= 2 { field.bit_length > 64 } else { field.bit_length > 32 };
         let access_width_bits = field.flags.access_type_bytes()? * 8;
 
@@ -2000,12 +2012,12 @@ where
         }
 
         match output {
-            Output::Buffer(bytes) => Ok(Arc::new(Object::Buffer(bytes))),
-            Output::Integer(value) => Ok(Arc::new(Object::Integer(u64::from_le_bytes(value)))),
+            Output::Buffer(bytes) => Ok(Object::Buffer(bytes).wrap()),
+            Output::Integer(value) => Ok(Object::Integer(u64::from_le_bytes(value)).wrap()),
         }
     }
 
-    fn do_field_write(&self, field: &FieldUnit, value: Arc<Object>) -> Result<(), AmlError> {
+    fn do_field_write(&self, field: &FieldUnit, value: WrappedObject) -> Result<(), AmlError> {
         trace!("AML field write. Field = {:?}. Value = {:?}", field, value);
 
         let value_bytes = match &*value {
@@ -2236,11 +2248,11 @@ struct MethodContext {
     current_block: Block,
     block_stack: Vec<Block>,
     in_flight: Vec<OpInFlight>,
-    args: [Arc<Object>; 8],
-    locals: [Arc<Object>; 8],
+    args: [WrappedObject; 8],
+    locals: [WrappedObject; 8],
     current_scope: AmlName,
 
-    _method: Option<Arc<Object>>,
+    _method: Option<WrappedObject>,
 }
 
 #[derive(Debug)]
@@ -2252,7 +2264,7 @@ struct OpInFlight {
 
 #[derive(Debug)]
 enum Argument {
-    Object(Arc<Object>),
+    Object(WrappedObject),
     Namestring(AmlName),
     ByteData(u8),
     DWordData(u32),
@@ -2307,16 +2319,16 @@ impl MethodContext {
             current_block: block,
             block_stack: Vec::new(),
             in_flight: Vec::new(),
-            args: core::array::from_fn(|_| Arc::new(Object::Uninitialized)),
-            locals: core::array::from_fn(|_| Arc::new(Object::Uninitialized)),
+            args: core::array::from_fn(|_| Object::Uninitialized.wrap()),
+            locals: core::array::from_fn(|_| Object::Uninitialized.wrap()),
             current_scope: AmlName::root(),
             _method: None,
         }
     }
 
     fn new_from_method(
-        method: Arc<Object>,
-        args: Vec<Arc<Object>>,
+        method: WrappedObject,
+        args: Vec<WrappedObject>,
         scope: AmlName,
     ) -> Result<MethodContext, AmlError> {
         if let Object::Method { code, flags } = &*method {
@@ -2329,14 +2341,14 @@ impl MethodContext {
                 kind: BlockKind::Method { method_scope: scope.clone() },
             };
             let args = core::array::from_fn(|i| {
-                if let Some(arg) = args.get(i) { arg.clone() } else { Arc::new(Object::Uninitialized) }
+                if let Some(arg) = args.get(i) { arg.clone() } else { Object::Uninitialized.wrap() }
             });
             let context = MethodContext {
                 current_block: block,
                 block_stack: Vec::new(),
                 in_flight: Vec::new(),
                 args,
-                locals: core::array::from_fn(|_| Arc::new(Object::Uninitialized)),
+                locals: core::array::from_fn(|_| Object::Uninitialized.wrap()),
                 current_scope: scope,
                 _method: Some(method.clone()),
             };
diff --git a/src/aml/namespace.rs b/src/aml/namespace.rs
index b44164cd..9300d9f0 100644
--- a/src/aml/namespace.rs
+++ b/src/aml/namespace.rs
@@ -1,3 +1,4 @@
+use super::object::WrappedObject;
 use crate::aml::{AmlError, object::Object};
 use alloc::{
     collections::btree_map::BTreeMap,
@@ -62,7 +63,7 @@ impl Namespace {
         Ok(())
     }
 
-    pub fn insert(&mut self, path: AmlName, object: Arc<Object>) -> Result<(), AmlError> {
+    pub fn insert(&mut self, path: AmlName, object: WrappedObject) -> Result<(), AmlError> {
         assert!(path.is_absolute());
         let path = path.normalize()?;
 
@@ -73,7 +74,7 @@ impl Namespace {
         }
     }
 
-    pub fn create_alias(&mut self, path: AmlName, object: Arc<Object>) -> Result<(), AmlError> {
+    pub fn create_alias(&mut self, path: AmlName, object: WrappedObject) -> Result<(), AmlError> {
         assert!(path.is_absolute());
         let path = path.normalize()?;
 
@@ -84,7 +85,7 @@ impl Namespace {
         }
     }
 
-    pub fn get(&mut self, path: AmlName) -> Result<Arc<Object>, AmlError> {
+    pub fn get(&mut self, path: AmlName) -> Result<WrappedObject, AmlError> {
         assert!(path.is_absolute());
         let path = path.normalize()?;
 
@@ -98,7 +99,7 @@ impl Namespace {
     /// Search for an object at the given path of the namespace, applying the search rules described in §5.3 of the
     /// ACPI specification, if they are applicable. Returns the resolved name, and the handle of the first valid
     /// object, if found.
-    pub fn search(&self, path: &AmlName, starting_scope: &AmlName) -> Result<(AmlName, Arc<Object>), AmlError> {
+    pub fn search(&self, path: &AmlName, starting_scope: &AmlName) -> Result<(AmlName, WrappedObject), AmlError> {
         if path.search_rules_apply() {
             /*
              * If search rules apply, we need to recursively look through the namespace. If the
@@ -270,12 +271,12 @@ impl fmt::Display for Namespace {
                     && level.children.iter().filter(|(_, l)| l.kind == NamespaceLevelKind::Scope).count() == 0;
                 writeln!(
                     f,
-                    "{}{}{}: {}{:?}",
+                    "{}{}{}: {}{}",
                     &indent_stack,
                     if end { END } else { BRANCH },
                     name.as_str(),
                     if flags.is_alias() { "[A] " } else { "" },
-                    object
+                    **object
                 )?;
 
                 // If the object has a corresponding scope, print it here
@@ -318,7 +319,7 @@ pub enum NamespaceLevelKind {
 #[derive(Clone)]
 pub struct NamespaceLevel {
     pub kind: NamespaceLevelKind,
-    pub values: BTreeMap<NameSeg, (ObjectFlags, Arc<Object>)>,
+    pub values: BTreeMap<NameSeg, (ObjectFlags, WrappedObject)>,
     pub children: BTreeMap<NameSeg, NamespaceLevel>,
 }
 
diff --git a/src/aml/object.rs b/src/aml/object.rs
index 6e311140..df35619b 100644
--- a/src/aml/object.rs
+++ b/src/aml/object.rs
@@ -1,21 +1,22 @@
 use crate::aml::{AmlError, Handle, Operation, op_region::OpRegion};
 use alloc::{borrow::Cow, string::String, sync::Arc, vec::Vec};
 use bit_field::BitField;
+use core::{cell::UnsafeCell, fmt, ops};
 
 #[derive(Clone, Debug)]
 pub enum Object {
     Uninitialized,
     Buffer(Vec<u8>),
-    BufferField { buffer: Arc<Object>, offset: usize, length: usize },
+    BufferField { buffer: WrappedObject, offset: usize, length: usize },
     Device,
     Event,
     FieldUnit(FieldUnit),
     Integer(u64),
     Method { code: Vec<u8>, flags: MethodFlags },
     Mutex { mutex: Handle, sync_level: u8 },
-    Reference { kind: ReferenceKind, inner: Arc<Object> },
+    Reference { kind: ReferenceKind, inner: WrappedObject },
     OpRegion(OpRegion),
-    Package(Vec<Arc<Object>>),
+    Package(Vec<WrappedObject>),
     PowerResource { system_level: u8, resource_order: u16 },
     Processor { proc_id: u8, pblk_address: u32, pblk_length: u8 },
     RawDataBuffer,
@@ -24,26 +25,133 @@ pub enum Object {
     Debug,
 }
 
-impl Object {
-    /*
-     * TODO XXX: this is a horrendous hack to emulate a clever locking solution for dynamically
-     * validating borrow checking for objects at A Later Date. It is trivially easy to produce
-     * undefined behaviour with this (and might be UB intrinsically).
-     *
-     * Options are:
-     *   - Put something like an AtomicRefCell around every single object. This is too slow I
-     *     think.
-     *   - Utilise a global lock on the namespace that gives us some sort of token we can then
-     *     magic up mutable references to objects through. Safety is ensured at type-level.
-     *   - Something else cleverer.
-     */
-    pub fn gain_mut(&self) -> &mut Self {
-        #[allow(invalid_reference_casting)]
-        unsafe {
-            &mut *(self as *const Self as *mut Self)
+impl fmt::Display for Object {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        match self {
+            Object::Uninitialized => write!(f, "[Uninitialized]"),
+            Object::Buffer(bytes) => write!(f, "Buffer({:x?})", bytes),
+            // TODO: include fields here
+            Object::BufferField { buffer, offset, length } => write!(f, "BufferField {{ .. }}"),
+            Object::Device => write!(f, "Device"),
+            Object::Event => write!(f, "Event"),
+            // TODO: include fields
+            Object::FieldUnit(_) => write!(f, "FieldUnit"),
+            Object::Integer(value) => write!(f, "Integer({})", value),
+            // TODO: decode flags here
+            Object::Method { code, flags } => write!(f, "Method"),
+            Object::Mutex { mutex, sync_level } => write!(f, "Mutex"),
+            Object::Reference { kind, inner } => write!(f, "Reference({:?} -> {})", kind, **inner),
+            Object::OpRegion(region) => write!(f, "{:?}", region),
+            Object::Package(elements) => {
+                write!(f, "Package {{ ")?;
+                for (i, element) in elements.iter().enumerate() {
+                    if i == elements.len() - 1 {
+                        write!(f, "{}", **element)?;
+                    } else {
+                        write!(f, "{}, ", **element)?;
+                    }
+                }
+                write!(f, " }}")?;
+                Ok(())
+            }
+            // TODO: include fields
+            Object::PowerResource { system_level, resource_order } => write!(f, "PowerResource"),
+            // TODO: include fields
+            Object::Processor { proc_id, pblk_address, pblk_length } => write!(f, "Processor"),
+            Object::RawDataBuffer => write!(f, "RawDataBuffer"),
+            Object::String(value) => write!(f, "String({:?})", value),
+            Object::ThermalZone => write!(f, "ThermalZone"),
+            Object::Debug => write!(f, "Debug"),
+        }
+    }
+}
+
+/// `ObjectToken` is used to mediate mutable access to objects from a [`WrappedObject`]. It must be
+/// acquired by locking the single token provided by [`super::Interpreter`].
+#[non_exhaustive]
+pub struct ObjectToken {
+    _dont_construct_me: (),
+}
+
+impl ObjectToken {
+    /// Create an [`ObjectToken`]. This should **only** be done **once** by the main interpreter,
+    /// as contructing your own token allows invalid mutable access to objects.
+    pub(super) unsafe fn create_interpreter_token() -> ObjectToken {
+        ObjectToken { _dont_construct_me: () }
+    }
+}
+
+#[derive(Clone, Debug)]
+pub struct WrappedObject(Arc<UnsafeCell<Object>>);
+
+impl WrappedObject {
+    pub fn new(object: Object) -> WrappedObject {
+        WrappedObject(Arc::new(UnsafeCell::new(object)))
+    }
+
+    /// Gain a mutable reference to an [`Object`] from this [`WrappedObject`]. This requires an
+    /// [`ObjectToken`] which is protected by a lock on [`super::Interpreter`], which prevents
+    /// mutable access to objects from multiple contexts. It does not, however, prevent the same
+    /// object, referenced from multiple [`WrappedObject`]s, having multiple mutable (and therefore
+    /// aliasing) references being made to it, and therefore care must be taken in the interpreter
+    /// to prevent this.
+    pub unsafe fn gain_mut<'r, 'a, 't>(&'a self, _token: &'t ObjectToken) -> &'r mut Object
+    where
+        't: 'r,
+        'a: 'r,
+    {
+        unsafe { &mut *(self.0.get()) }
+    }
+
+    pub fn unwrap_reference(self) -> WrappedObject {
+        let mut object = self;
+        loop {
+            if let Object::Reference { ref inner, .. } = *object {
+                object = inner.clone();
+            } else {
+                return object.clone();
+            }
         }
     }
 
+    /// Unwraps 'transparent' references (e.g. locals, arguments, and internal usage of reference-type objects), but maintain 'real'
+    /// references deliberately created by AML.
+    pub fn unwrap_transparent_reference(self) -> WrappedObject {
+        let mut object = self;
+        loop {
+            // TODO: what should this do with unresolved namestrings? It would need namespace
+            // access to resolve them (and then this would probs have to move to a method on
+            // `Interpreter`)?
+            if let Object::Reference { kind, ref inner } = *object
+                && kind == ReferenceKind::LocalOrArg
+            {
+                object = inner.clone();
+            } else {
+                return object.clone();
+            }
+        }
+    }
+}
+
+impl ops::Deref for WrappedObject {
+    type Target = Object;
+
+    fn deref(&self) -> &Self::Target {
+        /*
+         * SAFETY: elided lifetime ensures reference cannot outlive at least one reference-counted
+         * instance of the object. `WrappedObject::gain_mut` is unsafe, and so it is the user's
+         * responsibility to ensure shared references from `Deref` do not co-exist with an
+         * exclusive reference.
+         */
+        unsafe { &*self.0.get() }
+    }
+}
+
+impl Object {
+    pub fn wrap(self) -> WrappedObject {
+        WrappedObject::new(self)
+    }
+
     pub fn as_integer(&self) -> Result<u64, AmlError> {
         if let Object::Integer(value) = self {
             Ok(*value)
@@ -111,10 +219,10 @@ impl Object {
         }
     }
 
-    pub fn write_buffer_field(&mut self, value: &[u8]) -> Result<(), AmlError> {
+    pub fn write_buffer_field(&mut self, value: &[u8], token: &ObjectToken) -> Result<(), AmlError> {
         // TODO: bounds check the buffer first to avoid panicking
         if let Self::BufferField { buffer, offset, length } = self {
-            let buffer = match buffer.gain_mut() {
+            let buffer = match unsafe { buffer.gain_mut(token) } {
                 Object::Buffer(buffer) => buffer.as_mut_slice(),
                 // XXX: this unfortunately requires us to trust AML to keep the string as valid
                 // UTF8... maybe there is a better way?
@@ -152,35 +260,6 @@ impl Object {
             Object::Debug => ObjectType::Debug,
         }
     }
-
-    pub fn unwrap_reference(self: Arc<Object>) -> Arc<Object> {
-        let mut object = self;
-        loop {
-            if let Object::Reference { ref inner, .. } = *object {
-                object = inner.clone();
-            } else {
-                return object.clone();
-            }
-        }
-    }
-
-    /// Unwraps 'transparent' references (e.g. locals, arguments, and internal usage of reference-type objects), but maintain 'real'
-    /// references deliberately created by AML.
-    pub fn unwrap_transparent_reference(self: Arc<Self>) -> Arc<Object> {
-        let mut object = self;
-        loop {
-            // TODO: what should this do with unresolved namestrings? It would need namespace
-            // access to resolve them (and then this would probs have to move to a method on
-            // `Interpreter`)?
-            if let Object::Reference { kind, ref inner } = *object
-                && kind == ReferenceKind::LocalOrArg
-            {
-                object = inner.clone();
-            } else {
-                return object.clone();
-            }
-        }
-    }
 }
 
 #[derive(Clone, Debug)]
@@ -193,9 +272,9 @@ pub struct FieldUnit {
 
 #[derive(Clone, Debug)]
 pub enum FieldUnitKind {
-    Normal { region: Arc<Object> },
-    Bank { region: Arc<Object>, bank: Arc<Object>, bank_value: u64 },
-    Index { index: Arc<Object>, data: Arc<Object> },
+    Normal { region: WrappedObject },
+    Bank { region: WrappedObject, bank: WrappedObject, bank_value: u64 },
+    Index { index: WrappedObject, data: WrappedObject },
 }
 
 #[derive(Clone, Copy, Debug)]
diff --git a/src/aml/resource.rs b/src/aml/resource.rs
index 738545e2..bb0d1289 100644
--- a/src/aml/resource.rs
+++ b/src/aml/resource.rs
@@ -1,5 +1,6 @@
+use super::object::WrappedObject;
 use crate::aml::{AmlError, Operation, object::Object};
-use alloc::{sync::Arc, vec::Vec};
+use alloc::vec::Vec;
 use bit_field::BitField;
 use byteorder::{ByteOrder, LittleEndian};
 use core::mem;
@@ -14,7 +15,7 @@ pub enum Resource {
 }
 
 /// Parse a `ResourceDescriptor` buffer into a list of resources.
-pub fn resource_descriptor_list(descriptor: Arc<Object>) -> Result<Vec<Resource>, AmlError> {
+pub fn resource_descriptor_list(descriptor: WrappedObject) -> Result<Vec<Resource>, AmlError> {
     if let Object::Buffer(ref bytes) = *descriptor {
         let mut descriptors = Vec::new();
         let mut bytes = bytes.as_slice();

From 22867c6811c8d4d8c28de76adfedf19a24c333b3 Mon Sep 17 00:00:00 2001
From: Isaac Woods <isaacwoods.home@gmail.com>
Date: Mon, 7 Apr 2025 18:38:14 +0100
Subject: [PATCH 84/88] AML: add rudimentary operation tracing for debugging

---
 src/aml/mod.rs | 56 ++++++++++++++++++++++++++++++++++++++++++++------
 1 file changed, 50 insertions(+), 6 deletions(-)

diff --git a/src/aml/mod.rs b/src/aml/mod.rs
index b1430ca7..96654880 100644
--- a/src/aml/mod.rs
+++ b/src/aml/mod.rs
@@ -319,6 +319,7 @@ where
                         };
 
                         *operand = new_value;
+                        context.retire_op(op);
                     }
                     Opcode::LAnd
                     | Opcode::LOr
@@ -358,6 +359,7 @@ where
                         // TODO: use potentially-updated result for return value here
                         self.do_store(target, result.clone())?;
                         context.contribute_arg(Argument::Object(result));
+                        context.retire_op(op);
                     }
                     Opcode::FromBCD => self.do_from_bcd(&mut context, op)?,
                     Opcode::ToBCD => self.do_to_bcd(&mut context, op)?,
@@ -368,6 +370,7 @@ where
 
                         let name = name.resolve(&context.current_scope)?;
                         self.namespace.lock().insert(name, object.clone())?;
+                        context.retire_op(op);
                     }
                     Opcode::Fatal => {
                         let [Argument::ByteData(typ), Argument::DWordData(code), Argument::Object(arg)] =
@@ -377,6 +380,7 @@ where
                         };
                         let arg = arg.as_integer()?;
                         self.handler.handle_fatal_error(*typ, *code, arg);
+                        context.retire_op(op);
                     }
                     Opcode::OpRegion => {
                         let [
@@ -396,6 +400,7 @@ where
                             parent_device_path: context.current_scope.clone(),
                         });
                         self.namespace.lock().insert(name.resolve(&context.current_scope)?, region.wrap())?;
+                        context.retire_op(op);
                     }
                     Opcode::DataRegion => {
                         let [
@@ -423,6 +428,7 @@ where
                             parent_device_path: context.current_scope.clone(),
                         });
                         self.namespace.lock().insert(name.resolve(&context.current_scope)?, region.wrap())?;
+                        context.retire_op(op);
                     }
                     Opcode::Buffer => {
                         let [
@@ -444,6 +450,7 @@ where
                         context.current_block.pc += buffer_len;
 
                         context.contribute_arg(Argument::Object(Object::Buffer(buffer).wrap()));
+                        context.retire_op(op);
                     }
                     Opcode::Package | Opcode::VarPackage => {
                         let mut elements = Vec::with_capacity(op.expected_arguments);
@@ -467,6 +474,7 @@ where
                         assert_eq!(context.peek(), Err(AmlError::RunOutOfStream));
                         context.current_block = context.block_stack.pop().unwrap();
                         context.contribute_arg(Argument::Object(Object::Package(elements).wrap()));
+                        context.retire_op(op);
                     }
                     Opcode::If => {
                         let [
@@ -501,6 +509,7 @@ where
                                 Err(other) => Err(other)?,
                             }
                         }
+                        context.retire_op(op);
                     }
                     opcode @ Opcode::CreateBitField
                     | opcode @ Opcode::CreateByteField
@@ -524,6 +533,7 @@ where
                             name.resolve(&context.current_scope)?,
                             Object::BufferField { buffer: buffer.clone(), offset: offset as usize, length }.wrap(),
                         )?;
+                        context.retire_op(op);
                     }
                     Opcode::CreateField => {
                         let [Argument::Object(buffer), Argument::Object(bit_index), Argument::Object(num_bits)] =
@@ -544,16 +554,19 @@ where
                             }
                             .wrap(),
                         )?;
+                        context.retire_op(op);
                     }
                     Opcode::Store => {
                         let [Argument::Object(object), target] = &op.arguments[..] else { panic!() };
                         self.do_store(&target, object.clone())?;
+                        context.retire_op(op);
                     }
                     Opcode::RefOf => {
                         let [Argument::Object(object)] = &op.arguments[..] else { panic!() };
                         let reference =
                             Object::Reference { kind: ReferenceKind::RefOf, inner: object.clone() }.wrap();
                         context.contribute_arg(Argument::Object(reference));
+                        context.retire_op(op);
                     }
                     Opcode::CondRefOf => {
                         let [Argument::Object(object), target] = &op.arguments[..] else { panic!() };
@@ -566,6 +579,7 @@ where
                             Object::Integer(u64::MAX)
                         };
                         context.contribute_arg(Argument::Object(result.wrap()));
+                        context.retire_op(op);
                     }
                     Opcode::DerefOf => {
                         let [Argument::Object(object)] = &op.arguments[..] else { panic!() };
@@ -582,14 +596,17 @@ where
                             });
                         };
                         context.contribute_arg(Argument::Object(result));
+                        context.retire_op(op);
                     }
                     Opcode::Sleep => {
                         let [Argument::Object(msec)] = &op.arguments[..] else { panic!() };
                         self.handler.sleep(msec.as_integer()?);
+                        context.retire_op(op);
                     }
                     Opcode::Stall => {
                         let [Argument::Object(usec)] = &op.arguments[..] else { panic!() };
                         self.handler.stall(usec.as_integer()?);
+                        context.retire_op(op);
                     }
                     Opcode::Acquire => {
                         let [Argument::Object(mutex)] = &op.arguments[..] else { panic!() };
@@ -600,6 +617,7 @@ where
 
                         // TODO: should we do something with the sync level??
                         self.handler.acquire(mutex, timeout)?;
+                        context.retire_op(op);
                     }
                     Opcode::Release => {
                         let [Argument::Object(mutex)] = &op.arguments[..] else { panic!() };
@@ -608,6 +626,7 @@ where
                         };
                         // TODO: should we do something with the sync level??
                         self.handler.release(mutex);
+                        context.retire_op(op);
                     }
                     Opcode::InternalMethodCall => {
                         let [Argument::Object(method), Argument::Namestring(method_scope)] = &op.arguments[0..2]
@@ -632,6 +651,7 @@ where
                             MethodContext::new_from_method(method.clone(), args, method_scope.clone())?;
                         let old_context = mem::replace(&mut context, new_context);
                         self.context_stack.lock().push(old_context);
+                        context.retire_op(op);
                     }
                     Opcode::Return => {
                         let [Argument::Object(object)] = &op.arguments[..] else { panic!() };
@@ -640,6 +660,7 @@ where
                         if let Some(last) = self.context_stack.lock().pop() {
                             context = last;
                             context.contribute_arg(Argument::Object(object.clone()));
+                            context.retire_op(op);
                         } else {
                             /*
                              * If this is the top-most context, this is a `Return` from the actual
@@ -675,6 +696,7 @@ where
                         };
 
                         context.contribute_arg(Argument::Object(Object::Integer(typ).wrap()));
+                        context.retire_op(op);
                     }
                     Opcode::SizeOf => self.do_size_of(&mut context, op)?,
                     Opcode::Index => self.do_index(&mut context, op)?,
@@ -701,6 +723,7 @@ where
 
                         let kind = FieldUnitKind::Bank { region, bank, bank_value };
                         self.parse_field_list(&mut context, kind, *start_pc, *pkg_length, field_flags)?;
+                        context.retire_op(op);
                     }
                     Opcode::While => {
                         /*
@@ -713,6 +736,7 @@ where
                         if predicate == 0 {
                             // Exit from the while loop by skipping out of the current block
                             context.current_block = context.block_stack.pop().unwrap();
+                            context.retire_op(op);
                         }
                     }
                     _ => panic!("Unexpected operation has created in-flight op!"),
@@ -1409,6 +1433,7 @@ where
         // TODO: use result for arg
         self.do_store(target, result.clone())?;
         context.contribute_arg(Argument::Object(result));
+        context.retire_op(op);
         Ok(())
     }
 
@@ -1450,6 +1475,7 @@ where
         };
 
         context.contribute_arg(Argument::Object(Object::Integer(result).wrap()));
+        context.retire_op(op);
         Ok(())
     }
 
@@ -1459,12 +1485,8 @@ where
             let operand = operand.clone().unwrap_transparent_reference().as_integer()?;
             let result = if operand == 0 { u64::MAX } else { 0 };
 
-            if let Some(prev_op) = context.in_flight.last_mut() {
-                if prev_op.arguments.len() < prev_op.expected_arguments {
-                    prev_op.arguments.push(Argument::Object(Object::Integer(result).wrap()));
-                }
-            }
-
+            context.contribute_arg(Argument::Object(Object::Integer(result).wrap()));
+            context.retire_op(op);
             return Ok(());
         }
 
@@ -1526,6 +1548,7 @@ where
         let result = if result { Object::Integer(u64::MAX) } else { Object::Integer(0) };
 
         context.contribute_arg(Argument::Object(result.wrap()));
+        context.retire_op(op);
         Ok(())
     }
 
@@ -1558,6 +1581,7 @@ where
         // TODO: use result of store
         self.do_store(target, result.clone())?;
         context.contribute_arg(Argument::Object(result));
+        context.retire_op(op);
         Ok(())
     }
 
@@ -1601,6 +1625,7 @@ where
         // TODO: use result of store
         self.do_store(target, result.clone())?;
         context.contribute_arg(Argument::Object(result));
+        context.retire_op(op);
         Ok(())
     }
 
@@ -1627,6 +1652,7 @@ where
         // TODO: use result of store
         self.do_store(target, result.clone())?;
         context.contribute_arg(Argument::Object(result));
+        context.retire_op(op);
         Ok(())
     }
 
@@ -1670,6 +1696,7 @@ where
         // TODO: use result of store
         self.do_store(target, result.clone())?;
         context.contribute_arg(Argument::Object(result));
+        context.retire_op(op);
         Ok(())
     }
 
@@ -1707,6 +1734,7 @@ where
 
         self.do_store(target, result.clone())?;
         context.contribute_arg(Argument::Object(result));
+        context.retire_op(op);
 
         Ok(())
     }
@@ -1736,6 +1764,7 @@ where
                 Object::Debug => "[Debug Object]".to_string(),
             }
         }
+
         let result = match source1.typ() {
             ObjectType::Integer => {
                 let source1 = source1.as_integer()?;
@@ -1764,6 +1793,7 @@ where
         // TODO: use result of store
         self.do_store(target, result.clone())?;
         context.contribute_arg(Argument::Object(result));
+        context.retire_op(op);
         Ok(())
     }
 
@@ -1780,6 +1810,7 @@ where
         }
 
         context.contribute_arg(Argument::Object(Object::Integer(result).wrap()));
+        context.retire_op(op);
         Ok(())
     }
 
@@ -1796,6 +1827,7 @@ where
         }
 
         context.contribute_arg(Argument::Object(Object::Integer(result).wrap()));
+        context.retire_op(op);
         Ok(())
     }
 
@@ -1811,6 +1843,7 @@ where
         };
 
         context.contribute_arg(Argument::Object(Object::Integer(result as u64).wrap()));
+        context.retire_op(op);
         Ok(())
     }
 
@@ -1861,6 +1894,7 @@ where
 
         self.do_store(target, result.clone())?;
         context.contribute_arg(Argument::Object(result));
+        context.retire_op(op);
         Ok(())
     }
 
@@ -2374,9 +2408,19 @@ impl MethodContext {
     }
 
     fn start_in_flight_op(&mut self, op: OpInFlight) {
+        trace!(
+            "START OP: {:?}, args: {:?}, with {} more needed",
+            op.op,
+            op.arguments,
+            op.expected_arguments - op.arguments.len()
+        );
         self.in_flight.push(op);
     }
 
+    fn retire_op(&mut self, op: OpInFlight) {
+        trace!("RETIRE OP: {:?}, args: {:?}", op.op, op.arguments);
+    }
+
     fn start_new_block(&mut self, kind: BlockKind, length: usize) {
         let block = Block {
             stream: &self.current_block.stream()[..(self.current_block.pc + length)] as *const [u8],

From cf4306747784ae9ddab3c29c57f38f3287d0404b Mon Sep 17 00:00:00 2001
From: Isaac Woods <isaacwoods.home@gmail.com>
Date: Mon, 7 Apr 2025 18:39:32 +0100
Subject: [PATCH 85/88] AML: fix bug in `ToHexString`, add test for `To*`
 opcodes from uACPI

---
 src/aml/mod.rs |  2 +-
 tests/to_x.asl | 96 ++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 97 insertions(+), 1 deletion(-)
 create mode 100644 tests/to_x.asl

diff --git a/src/aml/mod.rs b/src/aml/mod.rs
index 96654880..b27532d5 100644
--- a/src/aml/mod.rs
+++ b/src/aml/mod.rs
@@ -1677,7 +1677,7 @@ where
                     for byte in bytes {
                         let as_str = match op.op {
                             Opcode::ToDecimalString => alloc::format!("{},", byte),
-                            Opcode::ToHexString => alloc::format!("{:?},", byte),
+                            Opcode::ToHexString => alloc::format!("{:#04X},", byte),
                             _ => panic!(),
                         };
                         string.push_str(&as_str);
diff --git a/tests/to_x.asl b/tests/to_x.asl
new file mode 100644
index 00000000..be110816
--- /dev/null
+++ b/tests/to_x.asl
@@ -0,0 +1,96 @@
+DefinitionBlock ("", "SSDT", 2, "uTEST", "TESTTABL", 0xF0F0F0F0)
+{
+    Name(FCNT, 0)
+
+    Method (CHEK, 2)
+    {
+        If (ObjectType(Arg0) == 3) {
+            Arg0 = ToHexString(Arg0)
+        }
+
+        If (ObjectType(Arg1) == 3) {
+            Arg1 = ToHexString(Arg1)
+        }
+
+        If (Arg0 != Arg1) {
+            FCNT++
+            Printf("Invalid string %o, expected %o", Arg0, Arg1)
+        }
+    }
+
+    Method (MAIN, 0, NotSerialized)
+    {
+        // Dec string
+        Local0 = ToDecimalString(123)
+        Local1 = "123"
+        CHEK(Local0, Local1)
+
+        Local0 = ToDecimalString(Buffer { 1, 2, 222, 33, 45, 192, 3, 255 })
+        Local1 = "1,2,222,33,45,192,3,255"
+        CHEK(Local0, Local1)
+
+        Local0 = ToDecimalString("")
+        Local1 = ""
+        CHEK(Local0, Local1)
+
+        Local0 = ToDecimalString("123")
+        Local1 = "123"
+        CHEK(Local0, Local1)
+
+        Local0 = ToDecimalString(0xFFFFFFFFFFFFFFFF)
+        Local1 = "18446744073709551615"
+        CHEK(Local0, Local1)
+
+        // Hex string
+        Local0 = ToHexString(123)
+        Local1 = "0x7B"
+        CHEK(Local0, Local1)
+
+        Local0 = ToHexString(Buffer { 1, 2, 222, 33, 45, 192, 3, 255 })
+        Local1 = "0x01,0x02,0xDE,0x21,0x2D,0xC0,0x03,0xFF"
+        CHEK(Local0, Local1)
+
+        Local0 = ToHexString("")
+        Local1 = ""
+        CHEK(Local0, Local1)
+
+        Local0 = ToHexString("123")
+        Local1 = "123"
+        CHEK(Local0, Local1)
+
+        Local0 = ToHexString(0xF)
+        Local1 = "0xF"
+        CHEK(Local0, Local1)
+
+        Local0 = ToHexString(0xFF)
+        Local1 = "0xFF"
+        CHEK(Local0, Local1)
+
+        Local0 = ToHexString(0xFFF)
+        Local1 = "0xFFF"
+        CHEK(Local0, Local1)
+
+        Local0 = ToHexString(0xFFFFF)
+        Local1 = "0xFFFFF"
+        CHEK(Local0, Local1)
+
+        Local0 = ToHexString(0xFFFFFFFFFFFFFFFF)
+        Local1 = "0xFFFFFFFFFFFFFFFF"
+        CHEK(Local0, Local1)
+
+        // Buffer
+        Local0 = ToBuffer(Buffer { 1, 2, 3 })
+        Local1 = Buffer { 1, 2, 3 }
+        CHEK(Local0, Local1)
+
+        Local0 = ToBuffer("Hello")
+        Local1 = Buffer { 0x48, 0x65, 0x6C, 0x6C, 0x6F, 0x00 }
+        CHEK(Local0, Local1)
+
+        Local0 = ToBuffer(0xDEADBEEFCAFEBABE)
+        Local1 = Buffer { 0xBE, 0xBA, 0xFE, 0xCA, 0xEF, 0xBE, 0xAD, 0xDE }
+        CHEK(Local0, Local1)
+
+        Return (FCNT)
+    }
+}

From 91e0c0f6ab209fa9f0bb238f2ec3cc30719ad609 Mon Sep 17 00:00:00 2001
From: Isaac Woods <isaacwoods.home@gmail.com>
Date: Mon, 7 Apr 2025 18:40:17 +0100
Subject: [PATCH 86/88] AML: fix bug in string comparisons with strings of less
 than 4 bytes

---
 src/aml/mod.rs | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/src/aml/mod.rs b/src/aml/mod.rs
index b27532d5..487537f9 100644
--- a/src/aml/mod.rs
+++ b/src/aml/mod.rs
@@ -1505,14 +1505,16 @@ where
                 let left = {
                     let mut bytes = [0u8; 4];
                     let left_bytes = left.as_bytes();
-                    (bytes[0..left_bytes.len()]).copy_from_slice(left_bytes);
+                    let bytes_to_use = usize::min(4, left_bytes.len());
+                    (bytes[0..bytes_to_use]).copy_from_slice(&left_bytes[0..bytes_to_use]);
                     u32::from_le_bytes(bytes) as u64
                 };
                 let right = {
                     let mut bytes = [0u8; 4];
                     let right = right.as_string()?;
                     let right_bytes = right.as_bytes();
-                    (bytes[0..right_bytes.len()]).copy_from_slice(right_bytes);
+                    let bytes_to_use = usize::min(4, right_bytes.len());
+                    (bytes[0..bytes_to_use]).copy_from_slice(&right_bytes[0..bytes_to_use]);
                     u32::from_le_bytes(bytes) as u64
                 };
                 (left, right)

From e986ac76e770acda5fceb176f7d85b3feb2e2502 Mon Sep 17 00:00:00 2001
From: Isaac Woods <isaacwoods.home@gmail.com>
Date: Mon, 7 Apr 2025 19:13:02 +0100
Subject: [PATCH 87/88] AML: don't error on name collisions

---
 src/aml/namespace.rs | 10 +++++++++-
 1 file changed, 9 insertions(+), 1 deletion(-)

diff --git a/src/aml/namespace.rs b/src/aml/namespace.rs
index 9300d9f0..9f3bac0b 100644
--- a/src/aml/namespace.rs
+++ b/src/aml/namespace.rs
@@ -9,6 +9,7 @@ use alloc::{
 };
 use bit_field::BitField;
 use core::{fmt, str, str::FromStr};
+use log::trace;
 
 #[derive(Clone)]
 pub struct Namespace {
@@ -70,7 +71,14 @@ impl Namespace {
         let (level, last_seg) = self.get_level_for_path_mut(&path)?;
         match level.values.insert(last_seg, (ObjectFlags::new(false), object)) {
             None => Ok(()),
-            Some(_) => Err(AmlError::NameCollision(path)),
+            Some(_) => {
+                /*
+                 * Real AML often has name collisions, and so we can't afford to be too strict
+                 * about it. We do warn the user as it does have the potential to break stuff.
+                 */
+                trace!("AML name collision: {}. Replacing object.", path);
+                Ok(())
+            }
         }
     }
 

From beb29508805b7e7d94ad9fcf012a6583bf3ef82c Mon Sep 17 00:00:00 2001
From: Isaac Woods <isaacwoods.home@gmail.com>
Date: Mon, 7 Apr 2025 19:15:53 +0100
Subject: [PATCH 88/88] AML: allow use of `Local`/`Arg` transparent references
 in `OpRegion`s

This fixes issues in real tables.
---
 src/aml/mod.rs | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/src/aml/mod.rs b/src/aml/mod.rs
index 487537f9..a45e9e11 100644
--- a/src/aml/mod.rs
+++ b/src/aml/mod.rs
@@ -393,6 +393,9 @@ where
                             panic!()
                         };
 
+                        let region_offset = region_offset.clone().unwrap_transparent_reference();
+                        let region_length = region_length.clone().unwrap_transparent_reference();
+
                         let region = Object::OpRegion(OpRegion {
                             space: RegionSpace::from(*region_space),
                             base: region_offset.as_integer()?,