[
  {
    "path": ".cargo/config.toml",
    "content": "[alias]\nrun_tests = \"run --manifest-path tools/aml_tester/Cargo.toml --\"\n"
  },
  {
    "path": ".github/FUNDING.yml",
    "content": "# These are supported funding model platforms\n\ngithub: [IsaacWoods]\npatreon: # Replace with a single Patreon username\nopen_collective: # Replace with a single Open Collective username\nko_fi: # Replace with a single Ko-fi username\ntidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel\ncommunity_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry\nliberapay: # Replace with a single Liberapay username\nissuehunt: # Replace with a single IssueHunt username\notechie: # Replace with a single Otechie username\ncustom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']\n"
  },
  {
    "path": ".github/workflows/build.yml",
    "content": "name: Build\n\non:\n  push:\n    branches: [ main ]\n  pull_request:\n    branches: [ main ]\n\nenv:\n  CARGO_TERM_COLOR: always\n\njobs:\n  build:\n    strategy:\n      fail-fast: false\n      matrix:\n        include:\n          - TARGET: x86_64-unknown-linux-gnu\n            OS: ubuntu-latest\n          - TARGET: aarch64-unknown-linux-gnu\n            OS: ubuntu-latest\n          - TARGET: i686-unknown-linux-gnu\n            OS: ubuntu-latest\n\n    runs-on: ${{ matrix.OS }}\n    env:\n      TARGET: ${{ matrix.TARGET }}\n\n    steps:\n    - uses: actions/checkout@v2\n      with:\n        submodules: 'recursive'\n      \n    - name: Install Rust\n      uses: actions-rs/toolchain@v1\n      with:\n        toolchain: nightly\n        default: true\n        profile: minimal\n        target: ${{ matrix.target }}\n        components: llvm-tools-preview\n        \n    - name: Build crates\n      run: cargo build --target $TARGET\n\n  test:\n    runs-on: ubuntu-latest\n    steps:\n    - uses: actions/checkout@v2\n      with:\n        submodules: 'recursive'\n    - name: Install Rust\n      uses: actions-rs/toolchain@v1\n      with:\n        toolchain: nightly\n        default: true\n        profile: minimal\n\n    - name: Install dependencies\n      run: sudo apt-get install -y acpica-tools\n\n    - name: Run tests\n      run: cargo test\n\n    - name: Run AML test suite\n      run: cargo run_tests -p tests\n\n  clippy:\n    runs-on: ubuntu-latest\n    steps:\n    - uses: actions/checkout@v2\n      with:\n        submodules: 'recursive'\n    - name: Install Rust\n      uses: actions-rs/toolchain@v1\n      with:\n        toolchain: nightly\n        default: true\n        profile: minimal\n        components: clippy\n\n    - name: Run clippy\n      run: cargo clippy\n\n    - name: Run clippy (tests)\n      run: cargo clippy --tests\n"
  },
  {
    "path": ".gitignore",
    "content": "/target\n**/*.rs.bk\nCargo.lock\ntests/*.aml\ntests/*.lst\ntests/*.txt\ndumps/\n"
  },
  {
    "path": "Cargo.toml",
    "content": "[workspace]\nmembers = [\"tools/aml_tester\", \"tools/acpi_dumper\", \"tools/aml_test_tools\", \"tools/uacpi_test_adapter\"]\nresolver = \"2\"\n\n[package]\nname = \"acpi\"\nversion = \"6.1.1\"\nauthors = [\"Isaac Woods\"]\nrepository = \"https://github.com/rust-osdev/acpi\"\ndescription = \"A pure-Rust library for interacting with ACPI\"\ncategories = [\"hardware-support\", \"no-std\"]\nlicense = \"MIT/Apache-2.0\"\nedition = \"2024\"\n\n[dependencies]\nbit_field = \"0.10.2\"\nbitflags = \"2.5.0\"\nlog = \"0.4.20\"\nspinning_top = \"0.3.0\"\npci_types = { version = \"0.10.0\", public = true }\nbyteorder = { version = \"1.5.0\", default-features = false }\n\n[dev-dependencies]\naml_test_tools = { path = \"tools/aml_test_tools\" }\npretty_env_logger = \"0.5.0\"\n\n[features]\ndefault = [\"alloc\", \"aml\"]\nalloc = []\naml = [\"alloc\"]\n"
  },
  {
    "path": "LICENCE-APACHE",
    "content": "                                 Apache License\n                           Version 2.0, January 2004\n                        http://www.apache.org/licenses/\n\n   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n   1. Definitions.\n\n      \"License\" shall mean the terms and conditions for use, reproduction,\n      and distribution as defined by Sections 1 through 9 of this document.\n\n      \"Licensor\" shall mean the copyright owner or entity authorized by\n      the copyright owner that is granting the License.\n\n      \"Legal Entity\" shall mean the union of the acting entity and all\n      other entities that control, are controlled by, or are under common\n      control with that entity. For the purposes of this definition,\n      \"control\" means (i) the power, direct or indirect, to cause the\n      direction or management of such entity, whether by contract or\n      otherwise, or (ii) ownership of fifty percent (50%) or more of the\n      outstanding shares, or (iii) beneficial ownership of such entity.\n\n      \"You\" (or \"Your\") shall mean an individual or Legal Entity\n      exercising permissions granted by this License.\n\n      \"Source\" form shall mean the preferred form for making modifications,\n      including but not limited to software source code, documentation\n      source, and configuration files.\n\n      \"Object\" form shall mean any form resulting from mechanical\n      transformation or translation of a Source form, including but\n      not limited to compiled object code, generated documentation,\n      and conversions to other media types.\n\n      \"Work\" shall mean the work of authorship, whether in Source or\n      Object form, made available under the License, as indicated by a\n      copyright notice that is included in or attached to the work\n      (an example is provided in the Appendix below).\n\n      \"Derivative Works\" shall mean any work, whether in Source or Object\n      form, that is based on (or derived from) the Work and for which the\n      editorial revisions, annotations, elaborations, or other modifications\n      represent, as a whole, an original work of authorship. For the purposes\n      of this License, Derivative Works shall not include works that remain\n      separable from, or merely link (or bind by name) to the interfaces of,\n      the Work and Derivative Works thereof.\n\n      \"Contribution\" shall mean any work of authorship, including\n      the original version of the Work and any modifications or additions\n      to that Work or Derivative Works thereof, that is intentionally\n      submitted to Licensor for inclusion in the Work by the copyright owner\n      or by an individual or Legal Entity authorized to submit on behalf of\n      the copyright owner. For the purposes of this definition, \"submitted\"\n      means any form of electronic, verbal, or written communication sent\n      to the Licensor or its representatives, including but not limited to\n      communication on electronic mailing lists, source code control systems,\n      and issue tracking systems that are managed by, or on behalf of, the\n      Licensor for the purpose of discussing and improving the Work, but\n      excluding communication that is conspicuously marked or otherwise\n      designated in writing by the copyright owner as \"Not a Contribution.\"\n\n      \"Contributor\" shall mean Licensor and any individual or Legal Entity\n      on behalf of whom a Contribution has been received by Licensor and\n      subsequently incorporated within the Work.\n\n   2. Grant of Copyright License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      copyright license to reproduce, prepare Derivative Works of,\n      publicly display, publicly perform, sublicense, and distribute the\n      Work and such Derivative Works in Source or Object form.\n\n   3. Grant of Patent License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      (except as stated in this section) patent license to make, have made,\n      use, offer to sell, sell, import, and otherwise transfer the Work,\n      where such license applies only to those patent claims licensable\n      by such Contributor that are necessarily infringed by their\n      Contribution(s) alone or by combination of their Contribution(s)\n      with the Work to which such Contribution(s) was submitted. If You\n      institute patent litigation against any entity (including a\n      cross-claim or counterclaim in a lawsuit) alleging that the Work\n      or a Contribution incorporated within the Work constitutes direct\n      or contributory patent infringement, then any patent licenses\n      granted to You under this License for that Work shall terminate\n      as of the date such litigation is filed.\n\n   4. Redistribution. You may reproduce and distribute copies of the\n      Work or Derivative Works thereof in any medium, with or without\n      modifications, and in Source or Object form, provided that You\n      meet the following conditions:\n\n      (a) You must give any other recipients of the Work or\n          Derivative Works a copy of this License; and\n\n      (b) You must cause any modified files to carry prominent notices\n          stating that You changed the files; and\n\n      (c) You must retain, in the Source form of any Derivative Works\n          that You distribute, all copyright, patent, trademark, and\n          attribution notices from the Source form of the Work,\n          excluding those notices that do not pertain to any part of\n          the Derivative Works; and\n\n      (d) If the Work includes a \"NOTICE\" text file as part of its\n          distribution, then any Derivative Works that You distribute must\n          include a readable copy of the attribution notices contained\n          within such NOTICE file, excluding those notices that do not\n          pertain to any part of the Derivative Works, in at least one\n          of the following places: within a NOTICE text file distributed\n          as part of the Derivative Works; within the Source form or\n          documentation, if provided along with the Derivative Works; or,\n          within a display generated by the Derivative Works, if and\n          wherever such third-party notices normally appear. The contents\n          of the NOTICE file are for informational purposes only and\n          do not modify the License. You may add Your own attribution\n          notices within Derivative Works that You distribute, alongside\n          or as an addendum to the NOTICE text from the Work, provided\n          that such additional attribution notices cannot be construed\n          as modifying the License.\n\n      You may add Your own copyright statement to Your modifications and\n      may provide additional or different license terms and conditions\n      for use, reproduction, or distribution of Your modifications, or\n      for any such Derivative Works as a whole, provided Your use,\n      reproduction, and distribution of the Work otherwise complies with\n      the conditions stated in this License.\n\n   5. Submission of Contributions. Unless You explicitly state otherwise,\n      any Contribution intentionally submitted for inclusion in the Work\n      by You to the Licensor shall be under the terms and conditions of\n      this License, without any additional terms or conditions.\n      Notwithstanding the above, nothing herein shall supersede or modify\n      the terms of any separate license agreement you may have executed\n      with Licensor regarding such Contributions.\n\n   6. Trademarks. This License does not grant permission to use the trade\n      names, trademarks, service marks, or product names of the Licensor,\n      except as required for reasonable and customary use in describing the\n      origin of the Work and reproducing the content of the NOTICE file.\n\n   7. Disclaimer of Warranty. Unless required by applicable law or\n      agreed to in writing, Licensor provides the Work (and each\n      Contributor provides its Contributions) on an \"AS IS\" BASIS,\n      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n      implied, including, without limitation, any warranties or conditions\n      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n      PARTICULAR PURPOSE. You are solely responsible for determining the\n      appropriateness of using or redistributing the Work and assume any\n      risks associated with Your exercise of permissions under this License.\n\n   8. Limitation of Liability. In no event and under no legal theory,\n      whether in tort (including negligence), contract, or otherwise,\n      unless required by applicable law (such as deliberate and grossly\n      negligent acts) or agreed to in writing, shall any Contributor be\n      liable to You for damages, including any direct, indirect, special,\n      incidental, or consequential damages of any character arising as a\n      result of this License or out of the use or inability to use the\n      Work (including but not limited to damages for loss of goodwill,\n      work stoppage, computer failure or malfunction, or any and all\n      other commercial damages or losses), even if such Contributor\n      has been advised of the possibility of such damages.\n\n   9. Accepting Warranty or Additional Liability. While redistributing\n      the Work or Derivative Works thereof, You may choose to offer,\n      and charge a fee for, acceptance of support, warranty, indemnity,\n      or other liability obligations and/or rights consistent with this\n      License. However, in accepting such obligations, You may act only\n      on Your own behalf and on Your sole responsibility, not on behalf\n      of any other Contributor, and only if You agree to indemnify,\n      defend, and hold each Contributor harmless for any liability\n      incurred by, or claims asserted against, such Contributor by reason\n      of your accepting any such warranty or additional liability.\n\n   END OF TERMS AND CONDITIONS\n\n   APPENDIX: How to apply the Apache License to your work.\n\n      To apply the Apache License to your work, attach the following\n      boilerplate notice, with the fields enclosed by brackets \"{}\"\n      replaced with your own identifying information. (Don't include\n      the brackets!)  The text should be enclosed in the appropriate\n      comment syntax for the file format. We also recommend that a\n      file or class name and description of purpose be included on the\n      same \"printed page\" as the copyright notice for easier\n      identification within third-party archives.\n\n   Copyright {yyyy} {name of copyright owner}\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n   You may obtain a copy of the License at\n\n       http://www.apache.org/licenses/LICENSE-2.0\n\n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License.\n"
  },
  {
    "path": "LICENCE-MIT",
    "content": "The MIT License (MIT)\n\nCopyright (c) 2018 Isaac Woods\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "# Acpi\n![Build Status](https://github.com/rust-osdev/acpi/actions/workflows/build.yml/badge.svg)\n[![Version](https://img.shields.io/crates/v/acpi.svg?style=rounded-square)](https://crates.io/crates/acpi/)\n\n### [Documentation](https://docs.rs/acpi)\n\n`acpi` is a Rust library for interacting with the Advanced Configuration and Power Interface, a\ncomplex framework for power management and device discovery and configuration. ACPI is used on\nmodern x64, as well as some ARM and RISC-V platforms. An operating system needs to interact with\nACPI to correctly set up a platform's interrupt controllers, perform power management, and fully\nsupport many other platform capabilities.\n\nThis crate provides a limited API that can be used without an allocator, for example for use\nfrom a bootloader. This API will allow you to search for the RSDP, enumerate over the available\ntables, and interact with the tables using their raw structures. All other functionality is\nbehind an `alloc` feature (enabled by default) and requires an allocator.\n\nWith an allocator, this crate provides a richer higher-level interfaces to the static tables, as\nwell as a dynamic interpreter for AML - the bytecode format encoded in the DSDT and SSDT tables.\n\nSee the library documentation for example usage. You will almost certainly need to read portions\nof the [ACPI Specification](https://uefi.org/specifications) too (however, be aware that firmware often\nships with ACPI tables that are not spec-compliant).\n\n## Licence\nThis project is dual-licenced under:\n- Apache Licence, Version 2.0 ([LICENCE-APACHE](LICENCE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)\n- MIT license ([LICENCE-MIT](LICENCE-MIT) or http://opensource.org/licenses/MIT)\n\nUnless you explicitly state otherwise, any contribution submitted for inclusion in this work by you,\nas defined in the Apache-2.0 licence, shall be dual licenced as above, without additional terms or\nconditions.\n"
  },
  {
    "path": "rust-toolchain.toml",
    "content": "[toolchain]\nchannel = \"nightly\"\n"
  },
  {
    "path": "rustfmt.toml",
    "content": "unstable_features = true\nedition = \"2021\"\n\nimports_granularity='Crate'\nimports_layout = \"HorizontalVertical\"\nuse_field_init_shorthand = true\nuse_try_shorthand = true\nformat_code_in_doc_comments = true\nmax_width = 115\nuse_small_heuristics = \"Max\"\n"
  },
  {
    "path": "src/address.rs",
    "content": "//! ACPI defines a Generic Address Structure (GAS), which provides a versatile way to describe register locations\n//! in a wide range of address spaces.\n\nuse crate::{AcpiError, Handler, PhysicalMapping};\nuse core::ptr;\nuse log::warn;\n\n/// This is the raw form of a Generic Address Structure, and follows the layout found in the ACPI tables.\n#[derive(Clone, Copy, Debug)]\n#[repr(C, packed)]\npub struct RawGenericAddress {\n    pub address_space: u8,\n    pub bit_width: u8,\n    pub bit_offset: u8,\n    pub access_size: u8,\n    pub address: u64,\n}\n\nimpl RawGenericAddress {\n    pub(crate) const fn is_empty(&self) -> bool {\n        self.address_space == 0\n            && self.bit_width == 0\n            && self.bit_offset == 0\n            && self.access_size == 0\n            && self.address == 0\n    }\n}\n\n#[derive(PartialEq, Eq, Clone, Copy, Debug)]\npub enum AddressSpace {\n    SystemMemory,\n    SystemIo,\n    /// Describes a register in the configuration space of a PCI device in segment `0`, on bus `0`.\n    /// The `address` field is of the format:\n    /// ```ignore\n    /// 64              48              32              16               0\n    ///  +---------------+---------------+---------------+---------------+\n    ///  |  reserved (0) |    device     |   function    |    offset     |\n    ///  +---------------+---------------+---------------+---------------+\n    /// ```\n    PciConfigSpace,\n    EmbeddedController,\n    SMBus,\n    SystemCmos,\n    PciBarTarget,\n    Ipmi,\n    GeneralIo,\n    GenericSerialBus,\n    PlatformCommunicationsChannel,\n    FunctionalFixedHardware,\n    OemDefined(u8),\n}\n\n/// Specifies a standard access size. The access size of a GAS can be non-standard, and is defined\n/// by the Address Space ID in such cases.\n#[derive(PartialEq, Eq, Clone, Copy, Debug)]\npub enum StandardAccessSize {\n    Undefined,\n    ByteAccess,\n    WordAccess,\n    DWordAccess,\n    QWordAccess,\n}\n\nimpl TryFrom<u8> for StandardAccessSize {\n    type Error = AcpiError;\n\n    fn try_from(size: u8) -> Result<Self, Self::Error> {\n        match size {\n            0 => Ok(StandardAccessSize::Undefined),\n            1 => Ok(StandardAccessSize::ByteAccess),\n            2 => Ok(StandardAccessSize::WordAccess),\n            3 => Ok(StandardAccessSize::DWordAccess),\n            4 => Ok(StandardAccessSize::QWordAccess),\n            _ => Err(AcpiError::InvalidGenericAddress),\n        }\n    }\n}\n\n#[derive(PartialEq, Eq, Clone, Copy, Debug)]\npub struct GenericAddress {\n    pub address_space: AddressSpace,\n    pub bit_width: u8,\n    pub bit_offset: u8,\n    pub access_size: u8,\n    pub address: u64,\n}\n\nimpl GenericAddress {\n    pub fn from_raw(raw: RawGenericAddress) -> Result<GenericAddress, AcpiError> {\n        let address_space = match raw.address_space {\n            0x00 => AddressSpace::SystemMemory,\n            0x01 => AddressSpace::SystemIo,\n            0x02 => AddressSpace::PciConfigSpace,\n            0x03 => AddressSpace::EmbeddedController,\n            0x04 => AddressSpace::SMBus,\n            0x05 => AddressSpace::SystemCmos,\n            0x06 => AddressSpace::PciBarTarget,\n            0x07 => AddressSpace::Ipmi,\n            0x08 => AddressSpace::GeneralIo,\n            0x09 => AddressSpace::GenericSerialBus,\n            0x0a => AddressSpace::PlatformCommunicationsChannel,\n            0x0b..=0x7e => return Err(AcpiError::InvalidGenericAddress),\n            0x7f => AddressSpace::FunctionalFixedHardware,\n            0x80..=0xbf => return Err(AcpiError::InvalidGenericAddress),\n            0xc0..=0xff => AddressSpace::OemDefined(raw.address_space),\n        };\n\n        Ok(GenericAddress {\n            address_space,\n            bit_width: raw.bit_width,\n            bit_offset: raw.bit_offset,\n            access_size: raw.access_size,\n            address: raw.address,\n        })\n    }\n\n    pub fn standard_access_size(&self) -> Result<StandardAccessSize, AcpiError> {\n        StandardAccessSize::try_from(self.access_size)\n    }\n}\n\npub struct MappedGas<H: Handler> {\n    gas: GenericAddress,\n    handler: H,\n    mapping: Option<PhysicalMapping<H, u8>>,\n}\n\nimpl<H> MappedGas<H>\nwhere\n    H: Handler,\n{\n    /// Map the given `GenericAddress`, giving a `MappedGas` that can be read from and written to.\n    ///\n    /// ### Safety\n    /// The supplied `GenericAddress` must be a valid GAS and all subsequent reads and writes must\n    /// be valid.\n    pub unsafe fn map_gas(gas: GenericAddress, handler: &H) -> Result<MappedGas<H>, AcpiError> {\n        match gas.address_space {\n            AddressSpace::SystemMemory => {\n                // TODO: how to know total size needed?\n                let mapping = unsafe { handler.map_physical_region(gas.address as usize, 0x1000) };\n                Ok(MappedGas { gas, handler: handler.clone(), mapping: Some(mapping) })\n            }\n            AddressSpace::SystemIo => Ok(MappedGas { gas, handler: handler.clone(), mapping: None }),\n            other => {\n                warn!(\"Mapping a GAS in address space {:?} is not supported!\", other);\n                Err(AcpiError::LibUnimplemented)\n            }\n        }\n    }\n\n    pub fn read(&self) -> Result<u64, AcpiError> {\n        /*\n         * TODO: this is only correct for basic GASs that require a single access. Extend it to\n         * support bit offsets and multiple reads etc.\n         */\n        let access_size_bits = gas_decode_access_bit_width(self.gas)?;\n        match self.gas.address_space {\n            AddressSpace::SystemMemory => {\n                let mapping = self.mapping.as_ref().unwrap();\n                match access_size_bits {\n                    8 => Ok(unsafe { ptr::read_volatile(mapping.virtual_start.as_ptr() as *const u8) as u64 }),\n                    16 => Ok(unsafe { ptr::read_volatile(mapping.virtual_start.as_ptr() as *const u16) as u64 }),\n                    32 => Ok(unsafe { ptr::read_volatile(mapping.virtual_start.as_ptr() as *const u32) as u64 }),\n                    64 => Ok(unsafe { ptr::read_volatile(mapping.virtual_start.as_ptr() as *const u64) }),\n                    _ => Err(AcpiError::InvalidGenericAddress),\n                }\n            }\n            AddressSpace::SystemIo => match access_size_bits {\n                8 => Ok(self.handler.read_io_u8(self.gas.address as u16) as u64),\n                16 => Ok(self.handler.read_io_u16(self.gas.address as u16) as u64),\n                32 => Ok(self.handler.read_io_u32(self.gas.address as u16) as u64),\n                _ => Err(AcpiError::InvalidGenericAddress),\n            },\n            _ => {\n                warn!(\"Read from GAS with address space {:?} is not supported. Ignored.\", self.gas.address_space);\n                Err(AcpiError::LibUnimplemented)\n            }\n        }\n    }\n\n    pub fn write(&self, value: u64) -> Result<(), AcpiError> {\n        // TODO: see above\n        let access_size_bits = gas_decode_access_bit_width(self.gas)?;\n        match self.gas.address_space {\n            AddressSpace::SystemMemory => {\n                let mapping = self.mapping.as_ref().unwrap();\n                match access_size_bits {\n                    8 => unsafe {\n                        ptr::write_volatile(mapping.virtual_start.as_ptr(), value as u8);\n                    },\n                    16 => unsafe {\n                        ptr::write_volatile(mapping.virtual_start.as_ptr() as *mut u16, value as u16);\n                    },\n                    32 => unsafe {\n                        ptr::write_volatile(mapping.virtual_start.as_ptr() as *mut u32, value as u32);\n                    },\n                    64 => unsafe { ptr::write_volatile(mapping.virtual_start.as_ptr() as *mut u64, value) },\n                    _ => return Err(AcpiError::InvalidGenericAddress),\n                }\n                Ok(())\n            }\n            AddressSpace::SystemIo => {\n                match access_size_bits {\n                    8 => self.handler.write_io_u8(self.gas.address as u16, value as u8),\n                    16 => self.handler.write_io_u16(self.gas.address as u16, value as u16),\n                    32 => self.handler.write_io_u32(self.gas.address as u16, value as u32),\n                    _ => return Err(AcpiError::InvalidGenericAddress),\n                }\n                Ok(())\n            }\n            _ => {\n                warn!(\"Write to GAS with address space {:?} is not supported. Ignored.\", self.gas.address_space);\n                Err(AcpiError::LibUnimplemented)\n            }\n        }\n    }\n}\n\n/// Returns the access size that should be made for a given `GenericAddress`, in bits.\nfn gas_decode_access_bit_width(gas: GenericAddress) -> Result<u8, AcpiError> {\n    /*\n     * This is more complex than it should be - we follow ACPICA to try and work with quirky\n     * firmwares.\n     *\n     * We should actually ignore the access sizes for normal registers (they tend to be unspecified\n     * in my experience anyway) and base our accesses on the width of the register. Only if a\n     * register has a bit width that cannot be accessed as a single native access do we look at the\n     * access size.\n     *\n     * We use a third method, based on the alignment of the address, for registers that have\n     * non-zero bit offsets. These are not typically encountered in normal registers - they very\n     * often mean the GAS has come from APEI (ACPI Platform Error Interface), and so needs special\n     * handling.\n     */\n    if gas.bit_offset == 0 && [8, 16, 32, 64].contains(&gas.bit_width) {\n        Ok(gas.bit_width)\n    } else if gas.access_size != 0 {\n        match gas.access_size {\n            1 => Ok(8),\n            2 => Ok(16),\n            3 => Ok(32),\n            4 => Ok(64),\n            _ => Err(AcpiError::InvalidGenericAddress),\n        }\n    } else {\n        /*\n         * Work out the access size based on the alignment of the address. We round up the total\n         * width (`bit_offset + bit_width`) to the next power-of-two, up to 64.\n         */\n        let total_width = gas.bit_offset + gas.bit_width;\n        Ok(if total_width <= 8 {\n            8\n        } else if total_width <= 16 {\n            16\n        } else if total_width <= 32 {\n            32\n        } else {\n            64\n        })\n    }\n}\n"
  },
  {
    "path": "src/aml/mod.rs",
    "content": "/*\n * TODO:\n *  - Field reads supporting custom handlers\n *  - Locked fields\n *  - Bank and index fields\n *  - Run `_REG` on supported op region handlers\n *  - Count operations performed and time\n *  - Correct DefStore / DefCopyObject behaviour\n *  - Load and LoadTable\n *  - DefDataRegion\n *  - Notify\n *  - DefMatch\n *\n *  - Method recursion depth?\n *  - Loop timeouts\n *  - Fuzzing and guarantee panic-free interpretation\n */\n\npub mod namespace;\npub mod object;\npub mod op_region;\npub mod pci_routing;\npub mod resource;\n\nuse crate::{\n    AcpiError,\n    AmlTable,\n    Handle,\n    Handler,\n    PhysicalMapping,\n    platform::AcpiPlatform,\n    registers::{FixedRegisters, Pm1ControlBit},\n    sdt::{SdtHeader, facs::Facs, fadt::Fadt},\n};\nuse alloc::{\n    boxed::Box,\n    collections::btree_map::BTreeMap,\n    string::{String, ToString},\n    sync::Arc,\n    vec,\n    vec::Vec,\n};\nuse bit_field::BitField;\nuse core::{\n    mem,\n    slice,\n    str::FromStr,\n    sync::atomic::{AtomicU64, Ordering},\n};\nuse log::{error, info, trace, warn};\nuse namespace::{AmlName, Namespace, NamespaceLevelKind};\nuse object::{\n    DeviceStatus,\n    FieldFlags,\n    FieldUnit,\n    FieldUnitKind,\n    FieldUpdateRule,\n    MethodFlags,\n    Object,\n    ObjectToken,\n    ObjectType,\n    ReferenceKind,\n    WrappedObject,\n};\nuse op_region::{OpRegion, RegionHandler, RegionSpace};\nuse pci_types::PciAddress;\nuse spinning_top::Spinlock;\n\n/// Helper macro to extract an expected set of [`Argument`]s from the given [`OpInFlight`]. Use\n/// like:\n/// ``` ignore,rust\n/// extract_args!(op => [Argument::Object(source), Argument::Object(target)]);\n/// extract_args!(op[0..2] => [Argument::Object(source), Argument::Namespace(name)]);\n/// ```\nmacro_rules! extract_args {\n    ($op:ident => $args:tt) => {\n        let $args = &$op.arguments[..] else {\n            return Err(AmlError::InternalError(alloc::format!(\n                \"Operation has invalid argument types: {}, in {}:{}\",\n                stringify!($args),\n                file!(),\n                line!(),\n            )));\n        };\n    };\n    ($op:ident[$x:expr] => $args:tt) => {\n        let $args = &$op.arguments[$x] else {\n            return Err(AmlError::InternalError(alloc::format!(\n                \"Operation has invalid argument types: {}, in {}:{}\",\n                stringify!($args),\n                file!(),\n                line!(),\n            )));\n        };\n    };\n}\n\n/// `Interpreter` implements a virtual machine for the dynamic AML bytecode. It can be used by a\n/// host operating system to load tables containing AML bytecode (generally the DSDT and SSDTs) and\n/// will then manage the AML namespace and all objects created during the life of the system.\npub struct Interpreter<H>\nwhere\n    H: Handler,\n{\n    handler: H,\n    pub namespace: Spinlock<Namespace>,\n    pub object_token: Spinlock<ObjectToken>,\n    context_stack: Spinlock<Vec<MethodContext>>,\n    dsdt_revision: u8,\n    region_handlers: Spinlock<BTreeMap<RegionSpace, Box<dyn RegionHandler>>>,\n\n    global_lock_mutex: Handle,\n    registers: Arc<FixedRegisters<H>>,\n    facs: Option<PhysicalMapping<H, Facs>>,\n}\n\nunsafe impl<H> Send for Interpreter<H> where H: Handler + Send {}\nunsafe impl<H> Sync for Interpreter<H> where H: Handler + Send {}\n\n/// The value returned by the `Revision` opcode.\nconst INTERPRETER_REVISION: u64 = 1;\n\nimpl<H> Interpreter<H>\nwhere\n    H: Handler,\n{\n    /// Construct a new [`Interpreter`]. This does not load any tables - if you have an\n    /// [`crate::AcpiTables`] already, construct an [`AcpiPlatform`] first and then use\n    /// [`Interpreter::new_from_platform`]\n    pub fn new(\n        handler: H,\n        dsdt_revision: u8,\n        registers: Arc<FixedRegisters<H>>,\n        facs: Option<PhysicalMapping<H, Facs>>,\n    ) -> Interpreter<H> {\n        info!(\"Initializing AML interpreter v{}\", env!(\"CARGO_PKG_VERSION\"));\n\n        let global_lock_mutex = handler.create_mutex();\n\n        Interpreter {\n            handler,\n            namespace: Spinlock::new(Namespace::new(global_lock_mutex)),\n            object_token: Spinlock::new(unsafe { ObjectToken::create_interpreter_token() }),\n            context_stack: Spinlock::new(Vec::new()),\n            dsdt_revision,\n            region_handlers: Spinlock::new(BTreeMap::new()),\n            global_lock_mutex,\n            registers,\n            facs,\n        }\n    }\n\n    /// Construct a new [`Interpreter`] with the given [`AcpiPlatform`].\n    pub fn new_from_platform(platform: &AcpiPlatform<H>) -> Result<Interpreter<H>, AcpiError> {\n        fn load_table(interpreter: &Interpreter<impl Handler>, table: AmlTable) -> Result<(), AcpiError> {\n            let mapping = unsafe {\n                interpreter.handler.map_physical_region::<SdtHeader>(table.phys_address, table.length as usize)\n            };\n            let stream = unsafe {\n                slice::from_raw_parts(\n                    mapping.virtual_start.as_ptr().byte_add(mem::size_of::<SdtHeader>()) as *const u8,\n                    table.length as usize - mem::size_of::<SdtHeader>(),\n                )\n            };\n            interpreter.load_table(stream).map_err(AcpiError::Aml)?;\n            Ok(())\n        }\n\n        let registers = platform.registers.clone();\n        let facs = {\n            platform.tables.find_table::<Fadt>().and_then(|fadt| fadt.facs_address().ok()).map(\n                |facs_address| unsafe {\n                    platform.handler.map_physical_region(facs_address, mem::size_of::<Facs>())\n                },\n            )\n        };\n\n        let dsdt = platform.tables.dsdt()?;\n        let interpreter = Interpreter::new(platform.handler.clone(), dsdt.revision, registers, facs);\n\n        if let Err(err) = load_table(&interpreter, dsdt) {\n            error!(\"Error while loading DSDT: {:?}. Continuing; this may cause downstream errors.\", err);\n        }\n\n        for (i, ssdt) in platform.tables.ssdts().enumerate() {\n            if let Err(err) = load_table(&interpreter, ssdt) {\n                error!(\"Error while loading SSDT{}: {:?}. Continuing.\", i, err);\n            }\n        }\n\n        Ok(interpreter)\n    }\n\n    /// Load the supplied byte stream as an AML table. This should be only the encoded AML stream -\n    /// not the header at the start of a table. If you've used [`Interpreter::new_from_platform`],\n    /// you'll likely not need to load any tables manually.\n    pub fn load_table(&self, stream: &[u8]) -> Result<(), AmlError> {\n        let context = unsafe { MethodContext::new_from_table(stream) };\n        self.do_execute_method(context)?;\n        Ok(())\n    }\n\n    /// Evaluate an object at the given path in the namespace. If the object is a method, this\n    /// invokes the method with the given set of arguments.\n    pub fn evaluate(&self, path: AmlName, args: Vec<WrappedObject>) -> Result<WrappedObject, AmlError> {\n        trace!(\"Invoking AML method: {}\", path);\n\n        let object = self.namespace.lock().get(path.clone())?.clone();\n        match &*object {\n            Object::Method { .. } => {\n                self.namespace.lock().add_level(path.clone(), NamespaceLevelKind::MethodLocals)?;\n                let context = MethodContext::new_from_method(object, args, path)?;\n                self.do_execute_method(context)\n            }\n            Object::NativeMethod { f, .. } => f(&args),\n            _ => Ok(object),\n        }\n    }\n\n    pub fn evaluate_if_present(\n        &self,\n        path: AmlName,\n        args: Vec<WrappedObject>,\n    ) -> Result<Option<WrappedObject>, AmlError> {\n        match self.evaluate(path.clone(), args) {\n            Ok(result) => Ok(Some(result)),\n            Err(AmlError::ObjectDoesNotExist(not_present)) => {\n                if path == not_present {\n                    Ok(None)\n                } else {\n                    Err(AmlError::ObjectDoesNotExist(not_present))\n                }\n            }\n            Err(other) => Err(other),\n        }\n    }\n\n    pub fn install_region_handler<RH>(&self, space: RegionSpace, handler: RH)\n    where\n        RH: RegionHandler + 'static,\n    {\n        let mut handlers = self.region_handlers.lock();\n        assert!(handlers.get(&space).is_none(), \"Tried to install handler for same space twice!\");\n        handlers.insert(space, Box::new(handler));\n    }\n\n    /// Initialize the namespace - this should be called after all tables have been loaded and\n    /// operation region handlers registered. Specifically, it will call relevant `_STA`, `_INI`,\n    /// and `_REG` methods.\n    pub fn initialize_namespace(&self) {\n        /*\n         * This should match the initialization order of ACPICA and uACPI.\n         */\n        if let Err(err) = self.evaluate_if_present(AmlName::from_str(\"\\\\_INI\").unwrap(), vec![]) {\n            warn!(\"Invoking \\\\_INI failed: {:?}\", err);\n        }\n        if let Err(err) = self.evaluate_if_present(AmlName::from_str(\"\\\\_SB._INI\").unwrap(), vec![]) {\n            warn!(\"Invoking \\\\_SB._INI failed: {:?}\", err);\n        }\n\n        // TODO: run all _REGs for globally-installed handlers (this might need more bookkeeping)\n\n        /*\n         * We can now initialize each device in the namespace. For each device, we evaluate `_STA`,\n         * which indicates if the device is present and functional. If this method does not exist,\n         * we assume the device should be initialized.\n         *\n         * We then evaluate `_INI` for the device. This can dynamically populate objects such as\n         * `_ADR`, `_CID`, `_HID`, `_SUN`, and `_UID`, and so is necessary before further\n         * operation.\n         */\n        let mut num_devices_initialized = 0;\n        /*\n         * TODO\n         * We clone a copy of the namespace here to traverse while executing all the `_STA` and\n         * `_INI` objects. Avoiding this would be good, but is not easy, as we need\n         * potentially-mutable access while executing all of the methods.\n         */\n        let mut namespace = self.namespace.lock().clone();\n        let init_status = namespace.traverse(|path, level| {\n            match level.kind {\n                NamespaceLevelKind::Device\n                | NamespaceLevelKind::Processor\n                | NamespaceLevelKind::ThermalZone\n                | NamespaceLevelKind::PowerResource => {\n                    let should_initialize = match self\n                        .evaluate_if_present(AmlName::from_str(\"_STA\").unwrap().resolve(path)?, vec![])\n                    {\n                        Ok(Some(result)) => {\n                            let Object::Integer(result) = *result else { panic!() };\n                            let status = DeviceStatus(result);\n                            status.present() && status.functioning()\n                        }\n                        Ok(None) => true,\n                        Err(err) => {\n                            warn!(\"Failed to evaluate _STA for device {}: {:?}\", path, err);\n                            false\n                        }\n                    };\n\n                    if should_initialize {\n                        num_devices_initialized += 1;\n                        if let Err(err) =\n                            self.evaluate_if_present(AmlName::from_str(\"_INI\").unwrap().resolve(path)?, vec![])\n                        {\n                            warn!(\"Failed to evaluate _INI for device {}: {:?}\", path, err);\n                        }\n                        Ok(true)\n                    } else {\n                        /*\n                         * If this device should not be initialized, don't initialize it's children.\n                         */\n                        Ok(false)\n                    }\n                }\n                _ => Ok(true),\n            }\n        });\n        if let Err(err) = init_status {\n            warn!(\"Error while traversing namespace for devices: {:?}\", err);\n        }\n\n        info!(\"Initialized {} devices\", num_devices_initialized);\n    }\n\n    pub fn acquire_global_lock(&self, timeout: u16) -> Result<(), AmlError> {\n        self.handler.acquire(self.global_lock_mutex, timeout)?;\n\n        // Now we've acquired the AML-side mutex, acquire the hardware side\n        // TODO: count the number of times we have to go round this loop / enforce a timeout?\n        loop {\n            if self.try_do_acquire_firmware_lock() {\n                break Ok(());\n            } else {\n                /*\n                 * The lock is owned by the firmware. We have set the pending bit - we now need to\n                 * wait for the firmware to signal it has released the lock.\n                 *\n                 * TODO: this should wait for an interrupt from the firmware. That needs more infra\n                 * so for now let's just spin round and try and acquire it again...\n                 */\n                self.handler.release(self.global_lock_mutex);\n                continue;\n            }\n        }\n    }\n\n    /// Attempt to acquire the firmware lock, setting the owned bit if the lock is free. If the\n    /// lock is not free, sets the pending bit to instruct the firmware to alert us when we can\n    /// attempt to take ownership of the lock again. Returns `true` if we now have ownership of the\n    /// lock, and `false` if we need to wait for firmware to release it.\n    fn try_do_acquire_firmware_lock(&self) -> bool {\n        let Some(facs) = &self.facs else { return true };\n        loop {\n            let global_lock = facs.global_lock.load(Ordering::Relaxed);\n            let is_owned = global_lock.get_bit(1);\n\n            /*\n             * Compute the new value: either the lock is already owned, and we need to set the\n             * pending bit and wait, or we can acquire ownership of the lock now. Either way, we\n             * unconditionally set the owned bit and set the pending bit if the lock is already\n             * owned.\n             */\n            let mut new_value = global_lock;\n            new_value.set_bit(0, is_owned);\n            new_value.set_bit(1, true);\n\n            if facs\n                .global_lock\n                .compare_exchange(global_lock, new_value, Ordering::AcqRel, Ordering::Acquire)\n                .is_ok()\n            {\n                break !is_owned;\n            }\n        }\n    }\n\n    pub fn release_global_lock(&self) -> Result<(), AmlError> {\n        let is_pending = self.do_release_firmware_lock();\n        if is_pending {\n            self.registers.pm1_control_registers.set_bit(Pm1ControlBit::GlobalLockRelease, true).unwrap();\n        }\n        Ok(())\n    }\n\n    /// Atomically release the owned and pending bits of the global lock. Returns whether the\n    /// pending bit was set (this means the firmware is waiting to acquire the lock, and should be\n    /// informed we're finished with it).\n    fn do_release_firmware_lock(&self) -> bool {\n        let Some(facs) = &self.facs else { return false };\n        loop {\n            let global_lock = facs.global_lock.load(Ordering::Relaxed);\n            let is_pending = global_lock.get_bit(0);\n            let mut new_value = global_lock;\n            new_value.set_bit(0, false);\n            new_value.set_bit(1, false);\n\n            if facs\n                .global_lock\n                .compare_exchange(global_lock, new_value, Ordering::AcqRel, Ordering::Acquire)\n                .is_ok()\n            {\n                break is_pending;\n            }\n        }\n    }\n\n    /// Returns the size of an integer (in bytes) for the set of tables parsed so far. This depends\n    /// on the revision of the initial DSDT.\n    pub fn integer_size(&self) -> usize {\n        if self.dsdt_revision >= 2 { 8 } else { 4 }\n    }\n\n    fn do_execute_method(&self, mut context: MethodContext) -> Result<WrappedObject, AmlError> {\n        /*\n         * This is the main loop that executes operations. Every op is handled at the top-level of\n         * the loop to prevent pathological stack growth from nested operations.\n         *\n         * The loop has three main stages:\n         *   1) Check if any in-flight operations are ready to be executed (i.e. have collected all\n         *      their arguments). An operation completing may contribute the last required argument\n         *      of the one above, so this is repeated for as many operations as are ready to be\n         *      retired.\n         *   2) Look at the next opcode in the stream. If we've run out of opcodes in the current\n         *      block, run logic to determine where in the stream we should move to next. Special\n         *      logic at this level handles things like moving in/out of package definitions, and\n         *      performing control flow.\n         *   3) When the next opcode is determined, use it to interpret the next portion of the\n         *      stream. If that is data, the correct number of bytes can be consumed and\n         *      contributed to the current in-flight operation. If it's an opcode, a new in-flight\n         *      operation is started, and we go round the loop again.\n         *\n         * This scheme is what allows the interpreter to use a loop that somewhat resembles a\n         * traditional fast bytecode VM, but also provides enough flexibility to handle the\n         * quirkier parts of the AML grammar, particularly the left-to-right encoding of operands.\n         */\n        loop {\n            /*\n             * First, see if we've gathered enough arguments to complete some in-flight operations.\n             */\n            while let Some(op) = context.in_flight.pop_if(|op| op.arguments.len() == op.expected_arguments) {\n                match op.op {\n                    Opcode::Add\n                    | Opcode::Subtract\n                    | Opcode::Multiply\n                    | Opcode::Divide\n                    | Opcode::ShiftLeft\n                    | Opcode::ShiftRight\n                    | Opcode::Mod\n                    | Opcode::Nand\n                    | Opcode::And\n                    | Opcode::Or\n                    | Opcode::Nor\n                    | Opcode::Xor => self.do_binary_maths(&mut context, op)?,\n                    Opcode::Not | Opcode::FindSetLeftBit | Opcode::FindSetRightBit => {\n                        self.do_unary_maths(&mut context, op)?;\n                    }\n                    Opcode::Increment | Opcode::Decrement => {\n                        let [Argument::Object(operand)] = &op.arguments[..] else { panic!() };\n                        let operand = operand.clone().unwrap_transparent_reference();\n                        let token = self.object_token.lock();\n\n                        let Object::Integer(operand) = (unsafe { operand.gain_mut(&token) }) else {\n                            Err(AmlError::ObjectNotOfExpectedType {\n                                expected: ObjectType::Integer,\n                                got: operand.typ(),\n                            })?\n                        };\n\n                        let new_value = match op.op {\n                            Opcode::Increment => operand.wrapping_add(1),\n                            Opcode::Decrement => operand.wrapping_sub(1),\n                            _ => unreachable!(),\n                        };\n\n                        *operand = new_value;\n                        context.contribute_arg(Argument::Object(Object::Integer(new_value).wrap()));\n                        context.retire_op(op);\n                    }\n                    Opcode::LAnd\n                    | Opcode::LOr\n                    | Opcode::LNot\n                    | Opcode::LNotEqual\n                    | Opcode::LLessEqual\n                    | Opcode::LGreaterEqual\n                    | Opcode::LEqual\n                    | Opcode::LGreater\n                    | Opcode::LLess => self.do_logical_op(&mut context, op)?,\n                    Opcode::ToBuffer => self.do_to_buffer(&mut context, op)?,\n                    Opcode::ToInteger => self.do_to_integer(&mut context, op)?,\n                    Opcode::ToString => self.do_to_string(&mut context, op)?,\n                    Opcode::ToDecimalString | Opcode::ToHexString => {\n                        self.do_to_dec_hex_string(&mut context, op)?\n                    }\n                    Opcode::Mid => self.do_mid(&mut context, op)?,\n                    Opcode::Concat => self.do_concat(&mut context, op)?,\n                    Opcode::ConcatRes => {\n                        extract_args!(op => [\n                            Argument::Object(source1),\n                            Argument::Object(source2),\n                            Argument::Object(target)\n                        ]);\n                        let source1 = source1.as_buffer()?;\n                        let source2 = source2.as_buffer()?;\n                        let result = {\n                            let mut buffer = Vec::from(source1);\n                            buffer.extend_from_slice(source2);\n                            // Add a new end-tag\n                            buffer.push(0x78);\n                            // Don't calculate the new real checksum - just use 0\n                            buffer.push(0x00);\n                            Object::Buffer(buffer).wrap()\n                        };\n                        // TODO: use potentially-updated result for return value here\n                        self.do_store(target.clone(), result.clone())?;\n                        context.contribute_arg(Argument::Object(result));\n                        context.retire_op(op);\n                    }\n                    Opcode::Reset => {\n                        extract_args!(op => [Argument::Object(sync_object)]);\n                        let sync_object = sync_object.clone().unwrap_reference();\n\n                        if let Object::Event(ref counter) = *sync_object {\n                            counter.store(0, Ordering::Release);\n                        } else {\n                            return Err(AmlError::InvalidOperationOnObject {\n                                op: Operation::ResetEvent,\n                                typ: sync_object.typ(),\n                            });\n                        }\n                    }\n                    Opcode::Signal => {\n                        extract_args!(op => [Argument::Object(sync_object)]);\n                        let sync_object = sync_object.clone().unwrap_reference();\n\n                        if let Object::Event(ref counter) = *sync_object {\n                            counter.fetch_add(1, Ordering::AcqRel);\n                        } else {\n                            return Err(AmlError::InvalidOperationOnObject {\n                                op: Operation::SignalEvent,\n                                typ: sync_object.typ(),\n                            });\n                        }\n                    }\n                    Opcode::Wait => {\n                        extract_args!(op => [Argument::Object(sync_object), Argument::Object(timeout)]);\n                        let sync_object = sync_object.clone().unwrap_reference();\n                        let timeout = u64::min(timeout.as_integer()?, 0xffff);\n\n                        if let Object::Event(ref counter) = *sync_object {\n                            /*\n                             * `Wait` returns a non-zero value if a timeout occurs and the event\n                             * was not signaled, and zero if it was. Timeout is specified in\n                             * milliseconds, should relinquish processor control (we use\n                             * `Handler::sleep` to do so) and a value of `0xffff` specifies that\n                             * the operation should wait indefinitely.\n                             */\n                            let mut remaining_sleep = timeout;\n                            let mut timed_out = true;\n\n                            'signaled: while remaining_sleep > 0 {\n                                loop {\n                                    /*\n                                     * Try to decrement the counter. If it's zero after a load, we\n                                     * haven't been signalled and should wait for a bit. If it's\n                                     * non-zero, we were signalled and should stop waiting.\n                                     */\n                                    let value = counter.load(Ordering::Acquire);\n                                    if value == 0 {\n                                        break;\n                                    }\n                                    if counter\n                                        .compare_exchange(value, value - 1, Ordering::AcqRel, Ordering::Acquire)\n                                        .is_ok()\n                                    {\n                                        timed_out = false;\n                                        break 'signaled;\n                                    }\n                                }\n\n                                let to_sleep = u64::min(timeout, 10);\n                                if timeout < 0xffff {\n                                    remaining_sleep = remaining_sleep.saturating_sub(to_sleep);\n                                }\n                                self.handler.sleep(to_sleep);\n                            }\n\n                            context.contribute_arg(Argument::Object(\n                                Object::Integer(if timed_out { u64::MAX } else { 0 }).wrap(),\n                            ));\n                        } else {\n                            return Err(AmlError::InvalidOperationOnObject {\n                                op: Operation::WaitEvent,\n                                typ: sync_object.typ(),\n                            });\n                        }\n                    }\n                    Opcode::Notify => {\n                        // TODO: may need special handling on the node to get path?\n                        extract_args!(op => [Argument::Namestring(name), Argument::Object(value)]);\n                        let value = value.as_integer()?;\n\n                        info!(\"Notify {:?} with value {}\", name, value);\n                        // TODO: support\n                        return Err(AmlError::LibUnimplemented);\n                    }\n                    Opcode::FromBCD => self.do_from_bcd(&mut context, op)?,\n                    Opcode::ToBCD => self.do_to_bcd(&mut context, op)?,\n                    Opcode::Name => {\n                        extract_args!(op => [Argument::Namestring(name), Argument::Object(object)]);\n                        let name = name.resolve(&context.current_scope)?;\n                        self.namespace.lock().insert(name, object.clone())?;\n                        context.retire_op(op);\n                    }\n                    Opcode::Fatal => {\n                        extract_args!(op => [Argument::ByteData(typ), Argument::DWordData(code), Argument::Object(arg)]);\n                        let arg = arg.as_integer()?;\n                        self.handler.handle_fatal_error(*typ, *code, arg);\n                        context.retire_op(op);\n                        return Err(AmlError::FatalErrorEncountered);\n                    }\n                    Opcode::OpRegion => {\n                        extract_args!(op => [\n                            Argument::Namestring(name),\n                            Argument::ByteData(region_space),\n                            Argument::Object(region_offset),\n                            Argument::Object(region_length),\n                        ]);\n                        let region_offset = region_offset.clone().unwrap_transparent_reference();\n                        let region_length = region_length.clone().unwrap_transparent_reference();\n\n                        let region = Object::OpRegion(OpRegion {\n                            space: RegionSpace::from(*region_space),\n                            base: region_offset.as_integer()?,\n                            length: region_length.as_integer()?,\n                            parent_device_path: context.current_scope.clone(),\n                        });\n                        self.namespace.lock().insert(name.resolve(&context.current_scope)?, region.wrap())?;\n                        context.retire_op(op);\n                    }\n                    Opcode::DataRegion => {\n                        extract_args!(op => [\n                            Argument::Namestring(name),\n                            Argument::Object(signature),\n                            Argument::Object(oem_id),\n                            Argument::Object(oem_table_id),\n                        ]);\n                        let _signature = signature.as_string()?;\n                        let _oem_id = oem_id.as_string()?;\n                        let _oem_table_id = oem_table_id.as_string()?;\n\n                        // TODO: once this is integrated into the rest of the crate, load the table\n                        log::warn!(\n                            \"DefDataRegion encountered in AML! We don't actually support these - produced region will be incorrect\"\n                        );\n\n                        let region = Object::OpRegion(OpRegion {\n                            space: RegionSpace::SystemMemory,\n                            base: 0,\n                            length: 0,\n                            parent_device_path: context.current_scope.clone(),\n                        });\n                        self.namespace.lock().insert(name.resolve(&context.current_scope)?, region.wrap())?;\n                        context.retire_op(op);\n                    }\n                    Opcode::Buffer => {\n                        extract_args!(op => [\n                            Argument::TrackedPc(start_pc),\n                            Argument::PkgLength(pkg_length),\n                            Argument::Object(buffer_size),\n                        ]);\n                        let buffer_size = buffer_size.clone().unwrap_transparent_reference().as_integer()?;\n\n                        let buffer_len = pkg_length - (context.current_block.pc - start_pc);\n                        let mut buffer = vec![0; buffer_size as usize];\n                        buffer[0..buffer_len].copy_from_slice(\n                            &context.current_block.stream()\n                                [context.current_block.pc..(context.current_block.pc + buffer_len)],\n                        );\n                        context.current_block.pc += buffer_len;\n\n                        context.contribute_arg(Argument::Object(Object::Buffer(buffer).wrap()));\n                        context.retire_op(op);\n                    }\n                    Opcode::Package => {\n                        let mut elements = Vec::with_capacity(op.expected_arguments);\n                        for arg in &op.arguments {\n                            let Argument::Object(object) = arg else {\n                                return Err(AmlError::InternalError(\n                                    \"Invalid argument type produced for package element\".to_string(),\n                                ));\n                            };\n                            elements.push(object.clone());\n                        }\n\n                        /*\n                         * We can end up completing a package's in-flight op in two circumstances:\n                         *    - If the correct number of elements are supplied, we end up here\n                         *      first, and then later in the block's finishing logic.\n                         *    - If less elements are supplied, we end up in the block's finishing\n                         *      logic to add some `Uninitialized`s, then go round again to complete\n                         *      the in-flight operation.\n                         *\n                         * To make these consistent, we always remove the block here, making sure\n                         * we've finished it as a sanity check.\n                         */\n                        assert_eq!(context.current_block.kind, BlockKind::Package);\n                        assert_eq!(context.peek(), Err(AmlError::RunOutOfStream));\n                        context.current_block = context.block_stack.pop().unwrap();\n                        context.contribute_arg(Argument::Object(Object::Package(elements).wrap()));\n                        context.retire_op(op);\n                    }\n                    Opcode::VarPackage => {\n                        extract_args!(op[0..1] => [Argument::Object(total_elements)]);\n                        let total_elements =\n                            total_elements.clone().unwrap_transparent_reference().as_integer()? as usize;\n\n                        let mut elements = Vec::with_capacity(total_elements);\n                        for arg in &op.arguments[1..] {\n                            let Argument::Object(object) = arg else {\n                                return Err(AmlError::InternalError(\n                                    \"Invalid argument type produced for package element\".to_string(),\n                                ));\n                            };\n                            elements.push(object.clone());\n                        }\n\n                        /*\n                         * As above, we always remove the block here after the in-flight op has\n                         * been retired.\n                         */\n                        assert_eq!(context.current_block.kind, BlockKind::VarPackage);\n                        assert_eq!(context.peek(), Err(AmlError::RunOutOfStream));\n                        context.current_block = context.block_stack.pop().unwrap();\n                        context.contribute_arg(Argument::Object(Object::Package(elements).wrap()));\n                        context.retire_op(op);\n                    }\n                    Opcode::If => {\n                        extract_args!(op => [\n                            Argument::TrackedPc(start_pc),\n                            Argument::PkgLength(then_length),\n                            Argument::Object(predicate),\n                        ]);\n                        let predicate = predicate.as_integer()?;\n                        let remaining_then_length = then_length - (context.current_block.pc - start_pc);\n\n                        if predicate > 0 {\n                            context.start_new_block(BlockKind::IfThenBranch, remaining_then_length);\n                        } else {\n                            context.current_block.pc += remaining_then_length;\n\n                            /*\n                             * Skip over the prolog to the else branch if present. Also handle if\n                             * there are no more bytes to peek - the `If` op could be the last op\n                             * in a block.\n                             */\n                            const DEF_ELSE_OP: u8 = 0xa1;\n                            match context.peek() {\n                                Ok(DEF_ELSE_OP) => {\n                                    context.next()?;\n                                    let _else_length = context.pkglength()?;\n                                }\n                                Ok(_) => (),\n                                Err(AmlError::RunOutOfStream) => (),\n                                Err(other) => Err(other)?,\n                            }\n                        }\n                        context.retire_op(op);\n                    }\n                    opcode @ Opcode::CreateBitField\n                    | opcode @ Opcode::CreateByteField\n                    | opcode @ Opcode::CreateWordField\n                    | opcode @ Opcode::CreateDWordField\n                    | opcode @ Opcode::CreateQWordField => {\n                        extract_args!(op => [Argument::Object(buffer), Argument::Object(index)]);\n                        let name = context.namestring()?;\n                        let index = index.as_integer()?;\n                        let (offset, length) = match opcode {\n                            Opcode::CreateBitField => (index, 1),\n                            Opcode::CreateByteField => (index * 8, 8),\n                            Opcode::CreateWordField => (index * 8, 16),\n                            Opcode::CreateDWordField => (index * 8, 32),\n                            Opcode::CreateQWordField => (index * 8, 64),\n                            _ => unreachable!(),\n                        };\n                        self.namespace.lock().insert(\n                            name.resolve(&context.current_scope)?,\n                            Object::BufferField { buffer: buffer.clone(), offset: offset as usize, length }.wrap(),\n                        )?;\n                        context.retire_op(op);\n                    }\n                    Opcode::CreateField => {\n                        extract_args!(op => [Argument::Object(buffer), Argument::Object(bit_index), Argument::Object(num_bits)]);\n                        let name = context.namestring()?;\n                        let bit_index = bit_index.as_integer()?;\n                        let num_bits = num_bits.as_integer()?;\n\n                        self.namespace.lock().insert(\n                            name.resolve(&context.current_scope)?,\n                            Object::BufferField {\n                                buffer: buffer.clone(),\n                                offset: bit_index as usize,\n                                length: num_bits as usize,\n                            }\n                            .wrap(),\n                        )?;\n                        context.retire_op(op);\n                    }\n                    Opcode::CopyObject => {\n                        extract_args!(op => [Argument::Object(object), Argument::Object(target)]);\n                        self.do_copy_object(target.clone(), object.clone())?;\n                        context.retire_op(op);\n                    }\n                    Opcode::Store => {\n                        extract_args!(op => [Argument::Object(object), Argument::Object(target)]);\n                        self.do_store(target.clone(), object.clone())?;\n                        context.retire_op(op);\n                    }\n                    Opcode::RefOf => {\n                        extract_args!(op => [Argument::Object(object)]);\n                        let reference =\n                            Object::Reference { kind: ReferenceKind::RefOf, inner: object.clone() }.wrap();\n                        context.contribute_arg(Argument::Object(reference));\n                        context.retire_op(op);\n                    }\n                    Opcode::CondRefOf => {\n                        extract_args!(op => [Argument::Object(object), Argument::Object(target)]);\n                        let result = if let Object::Reference { kind: ReferenceKind::Unresolved, .. } = **object {\n                            Object::Integer(0)\n                        } else {\n                            let reference =\n                                Object::Reference { kind: ReferenceKind::RefOf, inner: object.clone() }.wrap();\n                            self.do_store(target.clone(), reference)?;\n                            Object::Integer(u64::MAX)\n                        };\n                        context.contribute_arg(Argument::Object(result.wrap()));\n                        context.retire_op(op);\n                    }\n                    Opcode::DerefOf => {\n                        extract_args!(op => [Argument::Object(object)]);\n                        let result = if object.typ() == ObjectType::Reference {\n                            object.clone().unwrap_reference()\n                        } else if object.typ() == ObjectType::String {\n                            let path = AmlName::from_str(&object.as_string().unwrap())?;\n                            let (_, object) = self.namespace.lock().search(&path, &context.current_scope)?;\n                            object.clone()\n                        } else {\n                            return Err(AmlError::ObjectNotOfExpectedType {\n                                expected: ObjectType::Reference,\n                                got: object.typ(),\n                            });\n                        };\n                        context.contribute_arg(Argument::Object(result));\n                        context.retire_op(op);\n                    }\n                    Opcode::Load => {\n                        extract_args!(op => [Argument::Namestring(object), Argument::Object(result)]);\n                        // TODO: read the AML from the object and load it\n                        warn!(\"Ignoring unsupported DefLoad operation (object={}, result = {})\", object, result);\n                        context.retire_op(op);\n                        return Err(AmlError::LibUnimplemented);\n                    }\n                    Opcode::LoadTable => {\n                        extract_args!(op => [\n                            Argument::Object(signature),\n                            Argument::Object(oem_id),\n                            Argument::Object(oem_table_id),\n                            Argument::Object(root_path),\n                            Argument::Object(parameter_path),\n                            Argument::Object(parameter_data),\n                        ]);\n                        // TODO: search for the table in the RSDT/XSDT and load the contained AML\n                        warn!(\n                            \"Ignoring unsupported DefLoadTable operation (signature = {}, oem_id = {}, oem_table_id = {}, root_path = {}, parameter_path = {}, parameter_data = {})\",\n                            signature, oem_id, oem_table_id, root_path, parameter_path, parameter_data\n                        );\n                        context.retire_op(op);\n                        return Err(AmlError::LibUnimplemented);\n                    }\n                    Opcode::Sleep => {\n                        extract_args!(op => [Argument::Object(msec)]);\n                        self.handler.sleep(msec.as_integer()?);\n                        context.retire_op(op);\n                    }\n                    Opcode::Stall => {\n                        extract_args!(op => [Argument::Object(usec)]);\n                        self.handler.stall(usec.as_integer()?);\n                        context.retire_op(op);\n                    }\n                    Opcode::Acquire => {\n                        extract_args!(op => [Argument::Object(mutex)]);\n                        let Object::Mutex { mutex, sync_level: _ } = **mutex else {\n                            Err(AmlError::InvalidOperationOnObject { op: Operation::Acquire, typ: mutex.typ() })?\n                        };\n                        let timeout = context.next_u16()?;\n\n                        // TODO: should we do something with the sync level??\n                        if mutex == self.global_lock_mutex {\n                            self.acquire_global_lock(timeout)?;\n                        } else {\n                            self.handler.acquire(mutex, timeout)?;\n                        }\n\n                        context.retire_op(op);\n                    }\n                    Opcode::Release => {\n                        extract_args!(op => [Argument::Object(mutex)]);\n                        let Object::Mutex { mutex, sync_level: _ } = **mutex else {\n                            Err(AmlError::InvalidOperationOnObject { op: Operation::Release, typ: mutex.typ() })?\n                        };\n\n                        // TODO: should we do something with the sync level??\n                        if mutex == self.global_lock_mutex {\n                            self.release_global_lock()?;\n                        } else {\n                            self.handler.release(mutex);\n                        }\n\n                        context.retire_op(op);\n                    }\n                    Opcode::InternalMethodCall => {\n                        extract_args!(op[0..2] => [Argument::Object(method), Argument::Namestring(method_scope)]);\n                        let args = op.arguments[2..]\n                            .iter()\n                            .map(|arg| {\n                                if let Argument::Object(arg) = arg {\n                                    arg.clone()\n                                } else {\n                                    panic!();\n                                }\n                            })\n                            .collect();\n\n                        if let Object::Method { .. } = **method {\n                            self.namespace\n                                .lock()\n                                .add_level(method_scope.clone(), NamespaceLevelKind::MethodLocals)?;\n\n                            let new_context =\n                                MethodContext::new_from_method(method.clone(), args, method_scope.clone())?;\n                            let old_context = mem::replace(&mut context, new_context);\n                            self.context_stack.lock().push(old_context);\n                            context.retire_op(op);\n                        } else if let Object::NativeMethod { ref f, .. } = **method {\n                            let result = f(&args)?;\n                            context.contribute_arg(Argument::Object(result));\n                        } else {\n                            panic!();\n                        }\n                    }\n                    Opcode::Return => {\n                        extract_args!(op => [Argument::Object(object)]);\n                        let object = object.clone().unwrap_transparent_reference();\n\n                        if let Some(last) = self.context_stack.lock().pop() {\n                            context = last;\n                            context.contribute_arg(Argument::Object(object.clone()));\n                            context.retire_op(op);\n                        } else {\n                            /*\n                             * If this is the top-most context, this is a `Return` from the actual\n                             * method.\n                             */\n                            return Ok(object.clone());\n                        }\n                    }\n                    Opcode::ObjectType => {\n                        extract_args!(op => [Argument::Object(object)]);\n\n                        // TODO: this should technically support scopes as well - this is less easy\n                        // (they should return `0`)\n                        fn object_type(object: &Object) -> u64 {\n                            if let Object::Reference { kind: _, inner } = object {\n                                object_type(&inner)\n                            } else {\n                                match object.typ() {\n                                    ObjectType::Uninitialized => 0,\n                                    ObjectType::Integer => 1,\n                                    ObjectType::String => 2,\n                                    ObjectType::Buffer => 3,\n                                    ObjectType::Package => 4,\n                                    ObjectType::FieldUnit => 5,\n                                    ObjectType::Device => 6,\n                                    ObjectType::Event => 7,\n                                    ObjectType::Method => 8,\n                                    ObjectType::Mutex => 9,\n                                    ObjectType::OpRegion => 10,\n                                    ObjectType::PowerResource => 11,\n                                    ObjectType::Processor => 12,\n                                    ObjectType::ThermalZone => 13,\n                                    ObjectType::BufferField => 14,\n                                    // XXX: 15 is reserved\n                                    ObjectType::Debug => 16,\n                                    ObjectType::RawDataBuffer => 17,\n                                    ObjectType::Reference => unreachable!(),\n                                }\n                            }\n                        }\n\n                        context.contribute_arg(Argument::Object(Object::Integer(object_type(&object)).wrap()));\n                        context.retire_op(op);\n                    }\n                    Opcode::SizeOf => self.do_size_of(&mut context, op)?,\n                    Opcode::Index => self.do_index(&mut context, op)?,\n                    Opcode::BankField => {\n                        extract_args!(op => [\n                            Argument::TrackedPc(start_pc),\n                            Argument::PkgLength(pkg_length),\n                            Argument::Namestring(region_name),\n                            Argument::Namestring(bank_name),\n                            Argument::Object(bank_value),\n                        ]);\n                        let bank_value = bank_value.as_integer()?;\n                        let field_flags = context.next()?;\n\n                        let (region, bank) = {\n                            let namespace = self.namespace.lock();\n                            let (_, region) = namespace.search(region_name, &context.current_scope)?;\n                            let (_, bank) = namespace.search(bank_name, &context.current_scope)?;\n                            (region, bank)\n                        };\n\n                        let kind = FieldUnitKind::Bank { region, bank, bank_value };\n                        self.parse_field_list(&mut context, kind, *start_pc, *pkg_length, field_flags)?;\n                        context.retire_op(op);\n                    }\n                    Opcode::While => {\n                        /*\n                         * We've just evaluated the predicate for an iteration of a while loop. If\n                         * false, skip over the rest of the loop, otherwise carry on.\n                         */\n                        extract_args!(op => [Argument::Object(predicate)]);\n                        let predicate = predicate.clone().unwrap_transparent_reference().as_integer()?;\n\n                        if predicate == 0 {\n                            // Exit from the while loop by skipping out of the current block\n                            context.current_block = context.block_stack.pop().unwrap();\n                            context.retire_op(op);\n                        }\n                    }\n                    _ => panic!(\"Unexpected operation has created in-flight op!\"),\n                }\n            }\n\n            /*\n             * Now that we've retired as many in-flight operations as we have arguments for, move\n             * forward in the AML stream.\n             */\n            let opcode = match context.opcode() {\n                Ok(opcode) => opcode,\n                Err(AmlError::RunOutOfStream) => {\n                    /*\n                     * We've reached the end of the current block. What we should do about this\n                     * depends on what type of block it was.\n                     */\n                    match context.current_block.kind {\n                        BlockKind::Table => {\n                            break Ok(Object::Uninitialized.wrap());\n                        }\n                        BlockKind::Method { method_scope } => {\n                            self.namespace.lock().remove_level(method_scope)?;\n\n                            if let Some(prev_context) = self.context_stack.lock().pop() {\n                                context = prev_context;\n                                continue;\n                            } else {\n                                /*\n                                 * If there is no explicit `Return` op, the result is undefined. We\n                                 * just return an uninitialized object.\n                                 */\n                                return Ok(Object::Uninitialized.wrap());\n                            }\n                        }\n                        BlockKind::Scope { old_scope } => {\n                            assert!(!context.block_stack.is_empty());\n                            context.current_block = context.block_stack.pop().unwrap();\n                            context.current_scope = old_scope;\n                            // Go round the loop again to get the next opcode for the new block\n                            continue;\n                        }\n                        BlockKind::Package => {\n                            /*\n                             * We've reached the end of the package. The in-flight op may have\n                             * already been completed in the case of the package specifying all of\n                             * its elements, or reach the end of the block here if it does not.\n                             *\n                             * In the latter case, fill in the rest of the package with\n                             * *distinct* uninitialized objects, and go round again to complete the\n                             * in-flight op.\n                             */\n                            assert!(!context.block_stack.is_empty());\n\n                            if let Some(package_op) = context.in_flight.last_mut()\n                                && package_op.op == Opcode::Package\n                            {\n                                let num_elements_left = package_op.expected_arguments - package_op.arguments.len();\n                                for _ in 0..num_elements_left {\n                                    package_op.arguments.push(Argument::Object(Object::Uninitialized.wrap()));\n                                }\n                            }\n\n                            // XXX: don't remove the package's block. Refer to completion of\n                            // package ops for rationale here.\n                            continue;\n                        }\n                        BlockKind::VarPackage => {\n                            assert!(!context.block_stack.is_empty());\n\n                            if let Some(package_op) = context.in_flight.last_mut()\n                                && package_op.op == Opcode::VarPackage\n                            {\n                                let num_elements_left = {\n                                    let Argument::Object(total_elements) = &package_op.arguments[0] else {\n                                        panic!()\n                                    };\n                                    let total_elements =\n                                        total_elements.clone().unwrap_transparent_reference().as_integer()?\n                                            as usize;\n\n                                    // Update the expected number of arguments to terminate the in-flight op\n                                    package_op.expected_arguments = package_op.arguments.len();\n                                    total_elements - (package_op.arguments.len() - 1)\n                                };\n\n                                for _ in 0..num_elements_left {\n                                    package_op.arguments.push(Argument::Object(Object::Uninitialized.wrap()));\n                                }\n                            }\n\n                            // As above, leave the package's block.\n                            continue;\n                        }\n                        BlockKind::IfThenBranch => {\n                            context.current_block = context.block_stack.pop().unwrap();\n\n                            /*\n                             * Check for an else-branch, and skip over it. We need to handle the\n                             * case here where there isn't a next byte - that just means the `If`\n                             * is the last op in a block.\n                             */\n                            const DEF_ELSE_OP: u8 = 0xa1;\n                            match context.peek() {\n                                Ok(DEF_ELSE_OP) => {\n                                    context.next()?;\n                                    let start_pc = context.current_block.pc;\n                                    let else_length = context.pkglength()?;\n                                    context.current_block.pc +=\n                                        else_length - (context.current_block.pc - start_pc);\n                                }\n                                Ok(_) => (),\n                                Err(AmlError::RunOutOfStream) => (),\n                                Err(other) => Err(other)?,\n                            };\n\n                            continue;\n                        }\n                        BlockKind::While { start_pc } => {\n                            /*\n                             * Go round again, and create a new in-flight op to have a look at the\n                             * predicate.\n                             */\n                            context.current_block.pc = start_pc;\n                            context.start(OpInFlight::new(Opcode::While, &[ResolveBehaviour::TermArg]));\n                            continue;\n                        }\n                    }\n                }\n                Err(other_err) => return Err(other_err),\n            };\n            match opcode {\n                Opcode::Zero => {\n                    /*\n                     * This represents a `Zero` operand that should create an `Integer` operand in\n                     * most places, but could also encode a `NullName` if we are expecting a\n                     * `Target`. We handle the latter in logic for stores to targets.\n                     */\n                    context.last_op()?.arguments.push(Argument::Object(Object::Integer(0).wrap()));\n                }\n                Opcode::One => {\n                    context.last_op()?.arguments.push(Argument::Object(Object::Integer(1).wrap()));\n                }\n                Opcode::Alias => {\n                    let source = context.namestring()?;\n                    let alias = context.namestring()?;\n\n                    let mut namespace = self.namespace.lock();\n                    let object = namespace.get(source.resolve(&context.current_scope)?)?.clone();\n                    let alias = alias.resolve(&context.current_scope)?;\n                    namespace.create_alias(alias, object)?;\n                }\n                Opcode::Name => {\n                    let name = context.namestring()?;\n                    context.start(OpInFlight::new_with(\n                        Opcode::Name,\n                        vec![Argument::Namestring(name)],\n                        &[ResolveBehaviour::Placeholder, ResolveBehaviour::TermArg],\n                    ));\n                }\n                Opcode::BytePrefix => {\n                    let value = context.next()?;\n                    context.last_op()?.arguments.push(Argument::Object(Object::Integer(value as u64).wrap()));\n                }\n                Opcode::WordPrefix => {\n                    let value = context.next_u16()?;\n                    context.last_op()?.arguments.push(Argument::Object(Object::Integer(value as u64).wrap()));\n                }\n                Opcode::DWordPrefix => {\n                    let value = context.next_u32()?;\n                    context.last_op()?.arguments.push(Argument::Object(Object::Integer(value as u64).wrap()));\n                }\n                Opcode::StringPrefix => {\n                    let str_start = context.current_block.pc;\n                    while context.next()? != b'\\0' {}\n                    // TODO: handle err\n                    let str = String::from(\n                        str::from_utf8(&context.current_block.stream()[str_start..(context.current_block.pc - 1)])\n                            .unwrap(),\n                    );\n                    context.last_op()?.arguments.push(Argument::Object(Object::String(str).wrap()));\n                }\n                Opcode::QWordPrefix => {\n                    let value = context.next_u64()?;\n                    context.last_op()?.arguments.push(Argument::Object(Object::Integer(value).wrap()));\n                }\n                Opcode::Scope => {\n                    let start_pc = context.current_block.pc;\n                    let pkg_length = context.pkglength()?;\n                    let name = context.namestring()?;\n\n                    let remaining_length = pkg_length - (context.current_block.pc - start_pc);\n\n                    let new_scope = name.resolve(&context.current_scope)?;\n                    self.namespace.lock().add_level(new_scope.clone(), NamespaceLevelKind::Scope)?;\n\n                    let old_scope = mem::replace(&mut context.current_scope, new_scope);\n                    context.start_new_block(BlockKind::Scope { old_scope }, remaining_length);\n                }\n                Opcode::Buffer => {\n                    let start_pc = context.current_block.pc;\n                    let pkg_length = context.pkglength()?;\n                    context.start(OpInFlight::new_with(\n                        Opcode::Buffer,\n                        vec![Argument::TrackedPc(start_pc), Argument::PkgLength(pkg_length)],\n                        &[ResolveBehaviour::Placeholder, ResolveBehaviour::Placeholder, ResolveBehaviour::TermArg],\n                    ));\n                }\n                Opcode::Package => {\n                    let start_pc = context.current_block.pc;\n                    let pkg_length = context.pkglength()?;\n                    let num_elements = context.next()?;\n\n                    let remaining_length = pkg_length - (context.current_block.pc - start_pc);\n\n                    /*\n                     * We now need to interpret an arbitrary number of package elements, bounded by\n                     * the remaining pkglength. This may be less than `num_elements` - the\n                     * remaining elements of the package are uninitialized. We utilise a\n                     * combination of a block to manage the pkglength, plus an in-flight op to\n                     * store interpreted arguments.\n                     */\n                    context.start(OpInFlight::new_dynamic(\n                        Opcode::Package,\n                        num_elements as usize,\n                        &[ResolveBehaviour::AsPackageElements],\n                    ));\n                    context.start_new_block(BlockKind::Package, remaining_length);\n                }\n                Opcode::VarPackage => {\n                    let start_pc = context.current_block.pc;\n                    let pkg_length = context.pkglength()?;\n                    let remaining_length = pkg_length - (context.current_block.pc - start_pc);\n\n                    /*\n                     * For variable packages, we're first going to parse a `TermArg` that encodes,\n                     * dynamically, how many elements the package will have. We then accept as many\n                     * elements as remain in the block, and we'll sort out how many are supposed to\n                     * be in the package later.\n                     */\n                    context.start(OpInFlight::new_dynamic(\n                        Opcode::VarPackage,\n                        usize::MAX,\n                        &[ResolveBehaviour::TermArg, ResolveBehaviour::AsPackageElements],\n                    ));\n                    context.start_new_block(BlockKind::VarPackage, remaining_length);\n                }\n                Opcode::Method => {\n                    let start_pc = context.current_block.pc;\n                    let pkg_length = context.pkglength()?;\n                    let name = context.namestring()?;\n                    let flags = MethodFlags(context.next()?);\n\n                    let code_len = pkg_length - (context.current_block.pc - start_pc);\n                    let code = context.current_block.stream()\n                        [context.current_block.pc..(context.current_block.pc + code_len)]\n                        .to_vec();\n                    context.current_block.pc += code_len;\n\n                    let name = name.resolve(&context.current_scope)?;\n                    self.namespace.lock().insert(name, Object::Method { code, flags }.wrap())?;\n                }\n                Opcode::External => {\n                    let _name = context.namestring()?;\n                    let _object_type = context.next()?;\n                    let _arg_count = context.next()?;\n                }\n                Opcode::Mutex => {\n                    let name = context.namestring()?;\n                    let sync_level = context.next()?;\n\n                    let name = name.resolve(&context.current_scope)?;\n                    let mutex = self.handler.create_mutex();\n                    self.namespace.lock().insert(name, Object::Mutex { mutex, sync_level }.wrap())?;\n                }\n                Opcode::Event => {\n                    let name = context.namestring()?;\n\n                    let name = name.resolve(&context.current_scope)?;\n                    self.namespace.lock().insert(name, Object::Event(Arc::new(AtomicU64::new(0))).wrap())?;\n                }\n                Opcode::LoadTable => {\n                    context.start(OpInFlight::new(Opcode::LoadTable, &[ResolveBehaviour::TermArg; 6]));\n                }\n                Opcode::Load => {\n                    let name = context.namestring()?;\n                    context.start(OpInFlight::new_with(\n                        Opcode::Load,\n                        vec![Argument::Namestring(name)],\n                        &[ResolveBehaviour::Target],\n                    ));\n                }\n                Opcode::Stall => context.start(OpInFlight::new(Opcode::Stall, &[ResolveBehaviour::TermArg])),\n                Opcode::Sleep => context.start(OpInFlight::new(Opcode::Sleep, &[ResolveBehaviour::TermArg])),\n                Opcode::Acquire => context.start(OpInFlight::new(opcode, &[ResolveBehaviour::SuperName])),\n                Opcode::Release => context.start(OpInFlight::new(opcode, &[ResolveBehaviour::SuperName])),\n                Opcode::Signal => context.start(OpInFlight::new(opcode, &[ResolveBehaviour::SuperName])),\n                Opcode::Wait => context\n                    .start(OpInFlight::new(opcode, &[ResolveBehaviour::SuperName, ResolveBehaviour::TermArg])),\n                Opcode::Reset => context.start(OpInFlight::new(opcode, &[ResolveBehaviour::SuperName])),\n                Opcode::Notify => context\n                    .start(OpInFlight::new(opcode, &[ResolveBehaviour::SuperName, ResolveBehaviour::TermArg])),\n                Opcode::FromBCD | Opcode::ToBCD => {\n                    context.start(OpInFlight::new(opcode, &[ResolveBehaviour::TermArg, ResolveBehaviour::Target]))\n                }\n                Opcode::Revision => {\n                    context.contribute_arg(Argument::Object(Object::Integer(INTERPRETER_REVISION).wrap()));\n                }\n                Opcode::Debug => context.contribute_arg(Argument::Object(Object::Debug.wrap())),\n                Opcode::Fatal => {\n                    let typ = context.next()?;\n                    let code = context.next_u32()?;\n                    context.start(OpInFlight::new_with(\n                        Opcode::Fatal,\n                        vec![Argument::ByteData(typ), Argument::DWordData(code)],\n                        &[ResolveBehaviour::Placeholder, ResolveBehaviour::Placeholder, ResolveBehaviour::TermArg],\n                    ));\n                }\n                Opcode::Timer => {\n                    // Time has to be monotonically-increasing, in 100ns units\n                    let time = self.handler.nanos_since_boot() / 100;\n                    context.contribute_arg(Argument::Object(Object::Integer(time).wrap()));\n                }\n                Opcode::OpRegion => {\n                    let name = context.namestring()?;\n                    let region_space = context.next()?;\n                    context.start(OpInFlight::new_with(\n                        Opcode::OpRegion,\n                        vec![Argument::Namestring(name), Argument::ByteData(region_space)],\n                        &[\n                            ResolveBehaviour::Placeholder,\n                            ResolveBehaviour::Placeholder,\n                            ResolveBehaviour::TermArg,\n                            ResolveBehaviour::TermArg,\n                        ],\n                    ));\n                }\n                Opcode::DataRegion => {\n                    let name = context.namestring()?;\n                    context.start(OpInFlight::new_with(\n                        Opcode::DataRegion,\n                        vec![Argument::Namestring(name)],\n                        &[\n                            ResolveBehaviour::Placeholder,\n                            ResolveBehaviour::TermArg,\n                            ResolveBehaviour::TermArg,\n                            ResolveBehaviour::TermArg,\n                        ],\n                    ));\n                }\n                Opcode::Field => {\n                    let start_pc = context.current_block.pc;\n                    let pkg_length = context.pkglength()?;\n                    let region_name = context.namestring()?;\n                    let field_flags = context.next()?;\n\n                    let (_, region) = self.namespace.lock().search(&region_name, &context.current_scope)?.clone();\n                    let kind = FieldUnitKind::Normal { region };\n                    self.parse_field_list(&mut context, kind, start_pc, pkg_length, field_flags)?;\n                }\n                Opcode::BankField => {\n                    let start_pc = context.current_block.pc;\n                    let pkg_length = context.pkglength()?;\n                    let region_name = context.namestring()?;\n                    let bank_name = context.namestring()?;\n\n                    context.start(OpInFlight::new_with(\n                        Opcode::BankField,\n                        vec![\n                            Argument::TrackedPc(start_pc),\n                            Argument::PkgLength(pkg_length),\n                            Argument::Namestring(region_name),\n                            Argument::Namestring(bank_name),\n                        ],\n                        &[\n                            ResolveBehaviour::Placeholder,\n                            ResolveBehaviour::Placeholder,\n                            ResolveBehaviour::Placeholder,\n                            ResolveBehaviour::Placeholder,\n                            ResolveBehaviour::TermArg,\n                        ],\n                    ));\n                }\n                Opcode::IndexField => {\n                    let start_pc = context.current_block.pc;\n                    let pkg_length = context.pkglength()?;\n                    let index_name = context.namestring()?;\n                    let data_name = context.namestring()?;\n                    let field_flags = context.next()?;\n\n                    let (index, data) = {\n                        let namespace = self.namespace.lock();\n                        let (_, index) = namespace.search(&index_name, &context.current_scope)?;\n                        let (_, data) = namespace.search(&data_name, &context.current_scope)?;\n                        (index, data)\n                    };\n\n                    if let Object::FieldUnit(ref data_fu) = *data {\n                        if data_fu.flags.access_type_bytes()? < FieldFlags(field_flags).access_type_bytes()? {\n                            // On ACPICA this causes reads/writes to be truncated to the width of\n                            // the data register.\n                            //\n                            // The issue comes from this part of the spec:\n                            // \"The value written to the IndexName register is defined to be a byte\n                            // offset that is aligned on an AccessType boundary.\"\n                            //\n                            // Consider the case where the index field is WordAcc but the data\n                            // register is ByteAcc. Only even numbers could be written to the index\n                            // register - multiples of width of word (2 bytes). But to access the\n                            // high byte of the word for the field, we'd need to write an odd number\n                            // to the index register. Which is not compatible with the spec.\n                            //\n                            // The ASL writer shouldn't have allowed this. And it seems that most\n                            // uses of IndexField are ByteAcc all around. So whatever strange\n                            // behaviour we allow is probably OK.\n                            //\n                            // But warn the user, just in case.\n                            warn!(\"Data field access width is smaller than normal field width at {:?}\", start_pc);\n                        }\n                    } else {\n                        warn!(\"Wrong data field type in IndexField: {:?}\", data);\n                    };\n\n                    let kind = FieldUnitKind::Index { index, data };\n                    self.parse_field_list(&mut context, kind, start_pc, pkg_length, field_flags)?;\n                }\n                Opcode::Device | Opcode::ThermalZone => {\n                    let start_pc = context.current_block.pc;\n                    let pkg_length = context.pkglength()?;\n                    let name = context.namestring()?;\n\n                    let remaining_length = pkg_length - (context.current_block.pc - start_pc);\n\n                    let new_scope = name.resolve(&context.current_scope)?;\n                    let (kind, object) = match opcode {\n                        Opcode::Device => (NamespaceLevelKind::Device, Object::Device),\n                        Opcode::ThermalZone => (NamespaceLevelKind::ThermalZone, Object::ThermalZone),\n                        _ => unreachable!(),\n                    };\n                    let mut namespace = self.namespace.lock();\n                    namespace.add_level(new_scope.clone(), kind)?;\n                    namespace.insert(new_scope.clone(), object.wrap())?;\n\n                    let old_scope = mem::replace(&mut context.current_scope, new_scope);\n                    context.start_new_block(BlockKind::Scope { old_scope }, remaining_length);\n                }\n                Opcode::Processor => {\n                    let start_pc = context.current_block.pc;\n                    let pkg_length = context.pkglength()?;\n                    let name = context.namestring()?;\n                    let proc_id = context.next()?;\n                    let pblk_address = context.next_u32()?;\n                    let pblk_length = context.next()?;\n\n                    let remaining_length = pkg_length - (context.current_block.pc - start_pc);\n\n                    let new_scope = name.resolve(&context.current_scope)?;\n                    let object = Object::Processor { proc_id, pblk_address, pblk_length };\n                    let mut namespace = self.namespace.lock();\n                    namespace.add_level(new_scope.clone(), NamespaceLevelKind::Processor)?;\n                    namespace.insert(new_scope.clone(), object.wrap())?;\n\n                    let old_scope = mem::replace(&mut context.current_scope, new_scope);\n                    context.start_new_block(BlockKind::Scope { old_scope }, remaining_length);\n                }\n                Opcode::PowerRes => {\n                    let start_pc = context.current_block.pc;\n                    let pkg_length = context.pkglength()?;\n                    let name = context.namestring()?;\n                    let system_level = context.next()?;\n                    let resource_order = context.next_u16()?;\n\n                    let remaining_length = pkg_length - (context.current_block.pc - start_pc);\n\n                    let new_scope = name.resolve(&context.current_scope)?;\n                    let object = Object::PowerResource { system_level, resource_order };\n                    let mut namespace = self.namespace.lock();\n                    namespace.add_level(new_scope.clone(), NamespaceLevelKind::PowerResource)?;\n                    namespace.insert(new_scope.clone(), object.wrap())?;\n\n                    let old_scope = mem::replace(&mut context.current_scope, new_scope);\n                    context.start_new_block(BlockKind::Scope { old_scope }, remaining_length);\n                }\n                Opcode::Local(local) => {\n                    let local = context.locals[local as usize].clone();\n                    context.last_op()?.arguments.push(Argument::Object(\n                        Object::Reference { kind: ReferenceKind::Local, inner: local }.wrap(),\n                    ));\n                }\n                Opcode::Arg(arg) => {\n                    let arg = context.args[arg as usize].clone();\n                    context\n                        .last_op()?\n                        .arguments\n                        .push(Argument::Object(Object::Reference { kind: ReferenceKind::Arg, inner: arg }.wrap()));\n                }\n                Opcode::Store => context.start(OpInFlight::new(\n                    Opcode::Store,\n                    &[ResolveBehaviour::TermArg, ResolveBehaviour::SuperName],\n                )),\n                Opcode::CopyObject => context.start(OpInFlight::new(\n                    Opcode::CopyObject,\n                    &[ResolveBehaviour::TermArg, ResolveBehaviour::SimpleName],\n                )),\n                Opcode::RefOf => context.start(OpInFlight::new(Opcode::RefOf, &[ResolveBehaviour::SuperName])),\n                Opcode::CondRefOf => context.start(OpInFlight::new(\n                    opcode,\n                    &[ResolveBehaviour::SuperNameIfExists, ResolveBehaviour::Target],\n                )),\n\n                Opcode::DualNamePrefix\n                | Opcode::MultiNamePrefix\n                | Opcode::Digit(_)\n                | Opcode::NameChar(_)\n                | Opcode::RootChar\n                | Opcode::ParentPrefixChar => {\n                    context.current_block.pc -= 1;\n                    let name = context.namestring()?;\n\n                    let behaviour = context\n                        .in_flight\n                        .last()\n                        .map(|op| op.resolve_behaviour())\n                        .unwrap_or(ResolveBehaviour::TermArg);\n                    match behaviour {\n                        // XXX: `NullName` is handled separately given its ambiguity with `Zero`\n                        ResolveBehaviour::SimpleName | ResolveBehaviour::SuperName | ResolveBehaviour::Target => {\n                            let object = self.namespace.lock().search(&name, &context.current_scope);\n                            match object {\n                                Ok((_resolved_name, object)) => {\n                                    context.last_op()?.arguments.push(Argument::Object(\n                                        Object::Reference { kind: ReferenceKind::Named, inner: object }.wrap(),\n                                    ));\n                                }\n                                Err(err) => Err(err)?,\n                            }\n                        }\n                        ResolveBehaviour::SuperNameIfExists => {\n                            let object = self.namespace.lock().search(&name, &context.current_scope);\n                            match object {\n                                Ok((_resolved_name, object)) => {\n                                    context.last_op()?.arguments.push(Argument::Object(object));\n                                }\n                                Err(AmlError::ObjectDoesNotExist(_)) => {\n                                    let reference = Object::Reference {\n                                        kind: ReferenceKind::Unresolved,\n                                        inner: Object::String(name.to_string()).wrap(),\n                                    };\n                                    context.last_op()?.arguments.push(Argument::Object(reference.wrap()));\n                                }\n                                Err(err) => Err(err)?,\n                            }\n                        }\n                        ResolveBehaviour::TermArg => {\n                            let object = self.namespace.lock().search(&name, &context.current_scope);\n                            match object {\n                                Ok((resolved_name, object)) => {\n                                    if let Object::Method { flags, .. } | Object::NativeMethod { flags, .. } =\n                                        *object\n                                    {\n                                        context.start(OpInFlight::new_with_dynamic(\n                                            Opcode::InternalMethodCall,\n                                            vec![Argument::Object(object), Argument::Namestring(resolved_name)],\n                                            flags.arg_count(),\n                                            &[\n                                                ResolveBehaviour::Placeholder,\n                                                ResolveBehaviour::Placeholder,\n                                                ResolveBehaviour::TermArg,\n                                                ResolveBehaviour::TermArg,\n                                                ResolveBehaviour::TermArg,\n                                                ResolveBehaviour::TermArg,\n                                                ResolveBehaviour::TermArg,\n                                                ResolveBehaviour::TermArg,\n                                                ResolveBehaviour::TermArg,\n                                                ResolveBehaviour::TermArg,\n                                            ],\n                                        ))\n                                    } else if let Object::FieldUnit(ref field) = *object {\n                                        let value = self.do_field_read(field)?;\n                                        context.last_op()?.arguments.push(Argument::Object(value));\n                                    } else if let Object::BufferField { .. } = *object {\n                                        let value = object.read_buffer_field(self.integer_size())?;\n                                        context.last_op()?.arguments.push(Argument::Object(value.wrap()));\n                                    } else {\n                                        context.last_op()?.arguments.push(Argument::Object(object));\n                                    }\n                                }\n                                Err(err) => Err(err)?,\n                            }\n                        }\n                        ResolveBehaviour::AsPackageElements => {\n                            context\n                                .last_op()?\n                                .arguments\n                                .push(Argument::Object(Object::String(name.to_string()).wrap()));\n                        }\n                        ResolveBehaviour::Placeholder => {\n                            panic!(\"Invalid resolve behaviour for name to be resolved!\")\n                        }\n                    }\n                }\n\n                Opcode::Add\n                | Opcode::Subtract\n                | Opcode::Multiply\n                | Opcode::ShiftLeft\n                | Opcode::ShiftRight\n                | Opcode::Mod\n                | Opcode::Nand\n                | Opcode::And\n                | Opcode::Or\n                | Opcode::Nor\n                | Opcode::Xor\n                | Opcode::Concat => {\n                    context.start(OpInFlight::new(\n                        opcode,\n                        &[ResolveBehaviour::TermArg, ResolveBehaviour::TermArg, ResolveBehaviour::Target],\n                    ));\n                }\n\n                Opcode::Divide => context.start(OpInFlight::new(\n                    Opcode::Divide,\n                    &[\n                        ResolveBehaviour::TermArg,\n                        ResolveBehaviour::TermArg,\n                        ResolveBehaviour::Target,\n                        ResolveBehaviour::Target,\n                    ],\n                )),\n                Opcode::Increment | Opcode::Decrement => {\n                    context.start(OpInFlight::new(opcode, &[ResolveBehaviour::SuperName]))\n                }\n                Opcode::Not => context\n                    .start(OpInFlight::new(Opcode::Not, &[ResolveBehaviour::TermArg, ResolveBehaviour::Target])),\n                Opcode::FindSetLeftBit | Opcode::FindSetRightBit => {\n                    context.start(OpInFlight::new(opcode, &[ResolveBehaviour::TermArg, ResolveBehaviour::Target]))\n                }\n                Opcode::DerefOf => context.start(OpInFlight::new(opcode, &[ResolveBehaviour::TermArg])),\n                Opcode::ConcatRes => context.start(OpInFlight::new(\n                    opcode,\n                    &[ResolveBehaviour::TermArg, ResolveBehaviour::TermArg, ResolveBehaviour::Target],\n                )),\n                Opcode::SizeOf => context.start(OpInFlight::new(opcode, &[ResolveBehaviour::SuperName])),\n                Opcode::Index => context.start(OpInFlight::new(\n                    opcode,\n                    &[ResolveBehaviour::TermArg, ResolveBehaviour::TermArg, ResolveBehaviour::Target],\n                )),\n                /*\n                 * TODO\n                 * Match is a difficult opcode to parse, as it interleaves dynamic arguments and\n                 * random bytes that need to be extracted as you go. I think we'll need to use 1+\n                 * internal in-flight ops to parse the static bytedatas as we go, and then retire\n                 * the real op at the end.\n                 */\n                Opcode::Match => todo!(),\n\n                Opcode::CreateBitField\n                | Opcode::CreateByteField\n                | Opcode::CreateWordField\n                | Opcode::CreateDWordField\n                | Opcode::CreateQWordField => {\n                    context.start(OpInFlight::new(opcode, &[ResolveBehaviour::TermArg; 2]))\n                }\n                Opcode::CreateField => {\n                    context.start(OpInFlight::new(Opcode::CreateField, &[ResolveBehaviour::TermArg; 3]))\n                }\n\n                Opcode::LAnd\n                | Opcode::LOr\n                | Opcode::LNot\n                | Opcode::LNotEqual\n                | Opcode::LLessEqual\n                | Opcode::LGreaterEqual\n                | Opcode::LEqual\n                | Opcode::LGreater\n                | Opcode::LLess => {\n                    context.start(OpInFlight::new(opcode, &[ResolveBehaviour::TermArg; 2]));\n                }\n\n                Opcode::ToBuffer | Opcode::ToDecimalString | Opcode::ToHexString | Opcode::ToInteger => {\n                    context.start(OpInFlight::new(opcode, &[ResolveBehaviour::TermArg, ResolveBehaviour::Target]))\n                }\n                Opcode::ToString => context.start(OpInFlight::new(\n                    opcode,\n                    &[ResolveBehaviour::TermArg, ResolveBehaviour::TermArg, ResolveBehaviour::Target],\n                )),\n\n                Opcode::ObjectType => context.start(OpInFlight::new(opcode, &[ResolveBehaviour::SuperName])),\n                Opcode::Mid => context.start(OpInFlight::new(\n                    Opcode::Mid,\n                    &[\n                        ResolveBehaviour::TermArg,\n                        ResolveBehaviour::TermArg,\n                        ResolveBehaviour::TermArg,\n                        ResolveBehaviour::Target,\n                    ],\n                )),\n                Opcode::If => {\n                    let start_pc = context.current_block.pc;\n                    let then_length = context.pkglength()?;\n                    context.start(OpInFlight::new_with(\n                        Opcode::If,\n                        vec![Argument::TrackedPc(start_pc), Argument::PkgLength(then_length)],\n                        &[ResolveBehaviour::Placeholder, ResolveBehaviour::Placeholder, ResolveBehaviour::TermArg],\n                    ));\n                }\n                Opcode::Else => return Err(AmlError::ElseFoundWithoutCorrespondingIf),\n                Opcode::While => {\n                    let start_pc = context.current_block.pc;\n                    let pkg_length = context.pkglength()?;\n                    let remaining_length = pkg_length - (context.current_block.pc - start_pc);\n                    context.start_new_block(\n                        BlockKind::While { start_pc: context.current_block.pc },\n                        remaining_length,\n                    );\n                    context.start(OpInFlight::new(Opcode::While, &[ResolveBehaviour::TermArg]));\n                }\n                Opcode::Continue => {\n                    if let BlockKind::While { start_pc } = &context.current_block.kind {\n                        context.current_block.pc = *start_pc;\n                    } else {\n                        loop {\n                            let Some(block) = context.block_stack.pop() else {\n                                Err(AmlError::ContinueOutsideOfWhile)?\n                            };\n                            if let BlockKind::While { start_pc } = block.kind {\n                                context.current_block.pc = start_pc;\n                                break;\n                            }\n                        }\n                    }\n                    context.start(OpInFlight::new(Opcode::While, &[ResolveBehaviour::TermArg]));\n                }\n                Opcode::Break => {\n                    if let BlockKind::While { .. } = &context.current_block.kind {\n                        context.current_block = context.block_stack.pop().unwrap();\n                    } else {\n                        loop {\n                            let Some(block) = context.block_stack.pop() else {\n                                Err(AmlError::BreakOutsideOfWhile)?\n                            };\n                            if let BlockKind::While { .. } = block.kind {\n                                context.current_block = context.block_stack.pop().unwrap();\n                                break;\n                            }\n                        }\n                    }\n                }\n                Opcode::Return => context.start(OpInFlight::new(Opcode::Return, &[ResolveBehaviour::TermArg])),\n                Opcode::Noop => {}\n                Opcode::Breakpoint => {\n                    self.handler.breakpoint();\n                }\n                Opcode::Ones => {\n                    context.last_op()?.arguments.push(Argument::Object(Object::Integer(u64::MAX).wrap()));\n                }\n\n                Opcode::InternalMethodCall => panic!(),\n            }\n        }\n    }\n\n    fn parse_field_list(\n        &self,\n        context: &mut MethodContext,\n        kind: FieldUnitKind,\n        start_pc: usize,\n        pkg_length: usize,\n        mut flags: u8,\n    ) -> Result<(), AmlError> {\n        const RESERVED_FIELD: u8 = 0x00;\n        const ACCESS_FIELD: u8 = 0x01;\n        const CONNECT_FIELD: u8 = 0x02;\n        const EXTENDED_ACCESS_FIELD: u8 = 0x03;\n\n        let mut field_offset = 0;\n\n        while context.current_block.pc < (start_pc + pkg_length) {\n            match context.next()? {\n                RESERVED_FIELD => {\n                    let length = context.pkglength()?;\n                    field_offset += length;\n                }\n                ACCESS_FIELD => {\n                    /*\n                     * These aren't actually fields themselves, but are created by `AccessAs` AML\n                     * elements. They change the access type and attributes for remaining fields in\n                     * the list.\n                     */\n                    let access_type = context.next()?;\n                    let _access_attrib = context.next()?;\n                    flags.set_bits(0..4, access_type);\n                }\n                EXTENDED_ACCESS_FIELD => {\n                    let access_type = context.next()?;\n                    let _extended_access_attrib = context.next()?;\n                    let _access_length = context.next()?;\n                    flags.set_bits(0..4, access_type);\n                    warn!(\"Ignoring extended attributes and length in ExtendedAccessField\");\n                }\n                CONNECT_FIELD => {\n                    // TODO: either consume a namestring or `BufferData` (it's not\n                    // clear what a buffer data acc is lmao)\n                    todo!(\"Connect field :(\");\n                }\n                _ => {\n                    context.current_block.pc -= 1;\n                    // TODO: this should only ever be a nameseg really...\n                    let field_name = context.namestring()?;\n                    let field_length = context.pkglength()?;\n\n                    let field = Object::FieldUnit(FieldUnit {\n                        kind: kind.clone(),\n                        bit_index: field_offset,\n                        bit_length: field_length,\n                        flags: FieldFlags(flags),\n                    });\n                    self.namespace.lock().insert(field_name.resolve(&context.current_scope)?, field.wrap())?;\n\n                    field_offset += field_length;\n                }\n            }\n        }\n\n        Ok(())\n    }\n\n    fn do_binary_maths(&self, context: &mut MethodContext, op: OpInFlight) -> Result<(), AmlError> {\n        extract_args!(op[0..3] => [Argument::Object(left), Argument::Object(right), Argument::Object(target)]);\n        let target2 = if op.op == Opcode::Divide { Some(&op.arguments[3]) } else { None };\n\n        let left = left.clone().unwrap_transparent_reference().as_integer()?;\n        let right = right.clone().unwrap_transparent_reference().as_integer()?;\n\n        let result = match op.op {\n            Opcode::Add => left.wrapping_add(right),\n            Opcode::Subtract => left.wrapping_sub(right),\n            Opcode::Multiply => left.wrapping_mul(right),\n            Opcode::Divide => {\n                if let Some(Argument::Object(remainder)) = target2 {\n                    self.do_store(remainder.clone(), Object::Integer(left.wrapping_rem(right)).wrap())?;\n                }\n                left.wrapping_div_euclid(right)\n            }\n            Opcode::ShiftLeft => left.wrapping_shl(right as u32),\n            Opcode::ShiftRight => left.wrapping_shr(right as u32),\n            Opcode::Mod => left.wrapping_rem(right),\n            Opcode::Nand => !(left & right),\n            Opcode::And => left & right,\n            Opcode::Or => left | right,\n            Opcode::Nor => !(left | right),\n            Opcode::Xor => left ^ right,\n            _ => panic!(),\n        };\n\n        let result = Object::Integer(result).wrap();\n        let result = self.do_store(target.clone(), result)?;\n        context.contribute_arg(Argument::Object(result));\n        context.retire_op(op);\n        Ok(())\n    }\n\n    fn do_unary_maths(&self, context: &mut MethodContext, op: OpInFlight) -> Result<(), AmlError> {\n        extract_args!(op => [Argument::Object(operand)]);\n        let operand = operand.clone().unwrap_transparent_reference().as_integer()?;\n\n        let result = match op.op {\n            Opcode::FindSetLeftBit => {\n                if operand == 0 {\n                    0\n                } else {\n                    /*\n                     * This is a particularly important place to respect the integer width as set\n                     * by the DSDT revision.\n                     */\n                    match self.integer_size() {\n                        4 => ((operand as u32).leading_zeros() + 1) as u64,\n                        8 => (operand.leading_zeros() + 1) as u64,\n                        _ => unreachable!(),\n                    }\n                }\n            }\n            Opcode::FindSetRightBit => {\n                if operand == 0 {\n                    0\n                } else {\n                    (operand.trailing_zeros() + 1) as u64\n                }\n            }\n            Opcode::Not => {\n                if operand == 0 {\n                    u64::MAX\n                } else {\n                    0\n                }\n            }\n            _ => panic!(),\n        };\n\n        context.contribute_arg(Argument::Object(Object::Integer(result).wrap()));\n        context.retire_op(op);\n        Ok(())\n    }\n\n    fn do_logical_op(&self, context: &mut MethodContext, op: OpInFlight) -> Result<(), AmlError> {\n        if op.op == Opcode::LNot {\n            extract_args!(op => [Argument::Object(operand)]);\n            let operand = operand.clone().unwrap_transparent_reference().as_integer()?;\n            let result = if operand == 0 { u64::MAX } else { 0 };\n\n            context.contribute_arg(Argument::Object(Object::Integer(result).wrap()));\n            context.retire_op(op);\n            return Ok(());\n        }\n\n        extract_args!(op => [Argument::Object(left), Argument::Object(right)]);\n        let left = left.clone().unwrap_transparent_reference();\n        let right = right.clone().unwrap_transparent_reference();\n\n        /*\n         * Some of these operations allow strings and buffers to be used as operands. Apparently\n         * NT's interpreter just takes the first 4 bytes of the string/buffer and casts them as an\n         * integer...\n         */\n        let (left, right) = match *left {\n            Object::Integer(left) => (left, right.as_integer()?),\n            Object::String(ref left) => {\n                let left = {\n                    let mut bytes = [0u8; 4];\n                    let left_bytes = left.as_bytes();\n                    let bytes_to_use = usize::min(4, left_bytes.len());\n                    (bytes[0..bytes_to_use]).copy_from_slice(&left_bytes[0..bytes_to_use]);\n                    u32::from_le_bytes(bytes) as u64\n                };\n                let right = {\n                    let mut bytes = [0u8; 4];\n                    let right = right.as_string()?;\n                    let right_bytes = right.as_bytes();\n                    let bytes_to_use = usize::min(4, right_bytes.len());\n                    (bytes[0..bytes_to_use]).copy_from_slice(&right_bytes[0..bytes_to_use]);\n                    u32::from_le_bytes(bytes) as u64\n                };\n                (left, right)\n            }\n            Object::Buffer(ref left) => {\n                let Object::Buffer(ref right) = *right else { panic!() };\n                let left = {\n                    let mut bytes = [0u8; 4];\n                    (bytes[0..left.len()]).copy_from_slice(left);\n                    u32::from_le_bytes(bytes) as u64\n                };\n                let right = {\n                    let mut bytes = [0u8; 4];\n                    (bytes[0..right.len()]).copy_from_slice(right);\n                    u32::from_le_bytes(bytes) as u64\n                };\n                (left, right)\n            }\n            _ => Err(AmlError::InvalidOperationOnObject { op: Operation::LogicalOp, typ: left.typ() })?,\n        };\n\n        let result = match op.op {\n            Opcode::LAnd => (left > 0) && (right > 0),\n            Opcode::LOr => (left > 0) || (right > 0),\n            Opcode::LNotEqual => left != right,\n            Opcode::LLessEqual => left <= right,\n            Opcode::LGreaterEqual => left >= right,\n            Opcode::LEqual => left == right,\n            Opcode::LGreater => left > right,\n            Opcode::LLess => left < right,\n            _ => panic!(),\n        };\n        let result = if result { Object::Integer(u64::MAX) } else { Object::Integer(0) };\n\n        context.contribute_arg(Argument::Object(result.wrap()));\n        context.retire_op(op);\n        Ok(())\n    }\n\n    fn do_to_buffer(&self, context: &mut MethodContext, op: OpInFlight) -> Result<(), AmlError> {\n        extract_args!(op => [Argument::Object(operand), Argument::Object(target)]);\n        let operand = operand.clone().unwrap_transparent_reference();\n\n        let result = match *operand {\n            Object::Buffer(ref bytes) => Object::Buffer(bytes.clone()),\n            Object::Integer(value) => {\n                if self.integer_size() == 8 {\n                    Object::Buffer(value.to_le_bytes().to_vec())\n                } else {\n                    Object::Buffer((value as u32).to_le_bytes().to_vec())\n                }\n            }\n            Object::String(ref value) => {\n                // XXX: an empty string is converted to an empty buffer, *without* the null-terminator\n                if value.is_empty() {\n                    Object::Buffer(vec![])\n                } else {\n                    let mut bytes = value.as_bytes().to_vec();\n                    bytes.push(b'\\0');\n                    Object::Buffer(bytes)\n                }\n            }\n            _ => Err(AmlError::InvalidOperationOnObject { op: Operation::ToBuffer, typ: operand.typ() })?,\n        }\n        .wrap();\n\n        let result = self.do_store(target.clone(), result)?;\n        context.contribute_arg(Argument::Object(result));\n        context.retire_op(op);\n        Ok(())\n    }\n\n    fn do_to_integer(&self, context: &mut MethodContext, op: OpInFlight) -> Result<(), AmlError> {\n        extract_args!(op => [Argument::Object(operand), Argument::Object(target)]);\n        let operand = operand.clone().unwrap_transparent_reference();\n\n        let result = match *operand {\n            Object::Integer(value) => Object::Integer(value),\n            Object::Buffer(ref bytes) => {\n                /*\n                 * The spec says this should respect the revision of the current definition block.\n                 * Apparently, the NT interpreter always uses the first 8 bytes of the buffer.\n                 */\n                let mut to_interpret = [0u8; 8];\n                (to_interpret[0..usize::min(bytes.len(), 8)]).copy_from_slice(bytes);\n                Object::Integer(u64::from_le_bytes(to_interpret))\n            }\n            Object::String(ref value) => {\n                /*\n                 * TODO:\n                 * This is about the same level of effort as ACPICA puts in. The uACPI test suite\n                 * has tests that this fails - namely because of support for octal, signs, strings\n                 * that won't fit in a `u64` etc. We probably need to write a more robust parser\n                 * 'real' parser to handle those cases.\n                 */\n                let value = value.trim();\n                let value = value.to_ascii_lowercase();\n                let (value, radix): (&str, u32) = match value.strip_prefix(\"0x\") {\n                    Some(value) => (value.split(|c: char| !c.is_ascii_hexdigit()).next().unwrap_or(\"\"), 16),\n                    None => (value.split(|c: char| !c.is_ascii_digit()).next().unwrap_or(\"\"), 10),\n                };\n                match value.len() {\n                    0 => Object::Integer(0),\n                    _ => Object::Integer(u64::from_str_radix(value, radix).map_err(|_| {\n                        AmlError::InvalidOperationOnObject { op: Operation::ToInteger, typ: ObjectType::String }\n                    })?),\n                }\n            }\n            _ => Err(AmlError::InvalidOperationOnObject { op: Operation::ToBuffer, typ: operand.typ() })?,\n        }\n        .wrap();\n\n        let result = self.do_store(target.clone(), result)?;\n        context.contribute_arg(Argument::Object(result));\n        context.retire_op(op);\n        Ok(())\n    }\n\n    fn do_to_string(&self, context: &mut MethodContext, op: OpInFlight) -> Result<(), AmlError> {\n        extract_args!(op => [Argument::Object(source), Argument::Object(length), Argument::Object(target)]);\n        let source = source.clone().unwrap_transparent_reference();\n        let source = source.as_buffer()?;\n        let length = length.clone().unwrap_transparent_reference().as_integer()? as usize;\n\n        let result = if source.is_empty() {\n            Object::String(String::new())\n        } else {\n            let mut buffer = source.split_inclusive(|b| *b == b'\\0').next().unwrap();\n            if length < usize::MAX {\n                buffer = &buffer[0..usize::min(length, buffer.len())];\n            }\n            let string = str::from_utf8(buffer).map_err(|_| AmlError::InvalidOperationOnObject {\n                op: Operation::ToString,\n                typ: ObjectType::Buffer,\n            })?;\n            Object::String(string.to_string())\n        }\n        .wrap();\n\n        let result = self.do_store(target.clone(), result)?;\n        context.contribute_arg(Argument::Object(result));\n        context.retire_op(op);\n        Ok(())\n    }\n\n    /// Perform a `ToDecimalString` or `ToHexString` operation\n    fn do_to_dec_hex_string(&self, context: &mut MethodContext, op: OpInFlight) -> Result<(), AmlError> {\n        extract_args!(op => [Argument::Object(operand), Argument::Object(target)]);\n        let operand = operand.clone().unwrap_transparent_reference();\n\n        let result = match *operand {\n            Object::String(ref value) => Object::String(value.clone()),\n            Object::Integer(value) => match op.op {\n                Opcode::ToDecimalString => Object::String(value.to_string()),\n                Opcode::ToHexString => Object::String(alloc::format!(\"{value:#X}\")),\n                _ => panic!(),\n            },\n            Object::Buffer(ref bytes) => {\n                if bytes.is_empty() {\n                    Object::String(String::new())\n                } else {\n                    let mut string = String::new();\n                    for byte in bytes {\n                        let as_str = match op.op {\n                            Opcode::ToDecimalString => alloc::format!(\"{byte},\"),\n                            Opcode::ToHexString => alloc::format!(\"{byte:#04X},\"),\n                            _ => panic!(),\n                        };\n                        string.push_str(&as_str);\n                    }\n                    // Remove last comma, if present\n                    if !string.is_empty() {\n                        string.pop();\n                    }\n                    Object::String(string)\n                }\n            }\n            _ => Err(AmlError::InvalidOperationOnObject { op: Operation::ToDecOrHexString, typ: operand.typ() })?,\n        }\n        .wrap();\n\n        let result = self.do_store(target.clone(), result)?;\n        context.contribute_arg(Argument::Object(result));\n        context.retire_op(op);\n        Ok(())\n    }\n\n    fn do_mid(&self, context: &mut MethodContext, op: OpInFlight) -> Result<(), AmlError> {\n        extract_args!(op => [Argument::Object(source), Argument::Object(index), Argument::Object(length), Argument::Object(target)]);\n        let index = index.clone().unwrap_transparent_reference().as_integer()? as usize;\n        let length = length.clone().unwrap_transparent_reference().as_integer()? as usize;\n\n        let result = match **source {\n            Object::String(ref string) => {\n                if index >= string.len() {\n                    Object::String(String::new())\n                } else {\n                    let upper = usize::min(index + length, index + string.len());\n                    let chars = &string[index..upper];\n                    Object::String(String::from(chars))\n                }\n            }\n            Object::Buffer(ref buffer) => {\n                if index >= buffer.len() {\n                    Object::Buffer(vec![])\n                } else {\n                    let upper = usize::min(index + length, index + buffer.len());\n                    let bytes = &buffer[index..upper];\n                    Object::Buffer(bytes.to_vec())\n                }\n            }\n            _ => Err(AmlError::InvalidOperationOnObject { op: Operation::Mid, typ: source.typ() })?,\n        }\n        .wrap();\n\n        self.do_store(target.clone(), result.clone())?;\n        context.contribute_arg(Argument::Object(result));\n        context.retire_op(op);\n        Ok(())\n    }\n\n    fn do_concat(&self, context: &mut MethodContext, op: OpInFlight) -> Result<(), AmlError> {\n        extract_args!(op => [Argument::Object(source1), Argument::Object(source2), Argument::Object(target)]);\n        let source1 = source1.clone().unwrap_transparent_reference();\n        let source2 = source2.clone().unwrap_transparent_reference();\n\n        fn resolve_as_string(obj: &Object) -> String {\n            match obj {\n                Object::Uninitialized => \"[Uninitialized Object]\".to_string(),\n                Object::Buffer(bytes) => String::from_utf8_lossy(bytes).into_owned(),\n                Object::BufferField { .. } => \"[Buffer Field]\".to_string(),\n                Object::Device => \"[Device]\".to_string(),\n                Object::Event(_) => \"[Event]\".to_string(),\n                Object::FieldUnit(_) => \"[Field]\".to_string(),\n                Object::Integer(value) => value.to_string(),\n                Object::Method { .. } | Object::NativeMethod { .. } => \"[Control Method]\".to_string(),\n                Object::Mutex { .. } => \"[Mutex]\".to_string(),\n                Object::Reference { inner, .. } => resolve_as_string(&(inner.clone().unwrap_reference())),\n                Object::OpRegion(_) => \"[Operation Region]\".to_string(),\n                Object::Package(_) => \"[Package]\".to_string(),\n                Object::PowerResource { .. } => \"[Power Resource]\".to_string(),\n                Object::Processor { .. } => \"[Processor]\".to_string(),\n                Object::RawDataBuffer => \"[Raw Data Buffer]\".to_string(),\n                Object::String(value) => value.clone(),\n                Object::ThermalZone => \"[Thermal Zone]\".to_string(),\n                Object::Debug => \"[Debug Object]\".to_string(),\n            }\n        }\n\n        let result = match source1.typ() {\n            ObjectType::Integer => {\n                let source1 = source1.as_integer()?;\n                let source2 = source2.to_integer(self.integer_size())?;\n                let mut buffer = Vec::new();\n                if self.integer_size() == 8 {\n                    buffer.extend_from_slice(&source1.to_le_bytes());\n                    buffer.extend_from_slice(&source2.to_le_bytes());\n                } else {\n                    buffer.extend_from_slice(&(source1 as u32).to_le_bytes());\n                    buffer.extend_from_slice(&(source2 as u32).to_le_bytes());\n                }\n                Object::Buffer(buffer).wrap()\n            }\n            ObjectType::Buffer => {\n                let mut buffer = source1.as_buffer()?.to_vec();\n                buffer.extend(source2.to_buffer(self.integer_size())?);\n                Object::Buffer(buffer).wrap()\n            }\n            _ => {\n                let source1 = resolve_as_string(&source1);\n                let source2 = resolve_as_string(&source2);\n                Object::String(source1 + &source2).wrap()\n            }\n        };\n\n        let result = self.do_store(target.clone(), result)?;\n        context.contribute_arg(Argument::Object(result));\n        context.retire_op(op);\n        Ok(())\n    }\n\n    fn do_from_bcd(&self, context: &mut MethodContext, op: OpInFlight) -> Result<(), AmlError> {\n        extract_args!(op => [Argument::Object(value)]);\n        let mut value = value.clone().unwrap_transparent_reference().as_integer()?;\n\n        let mut result = 0;\n        let mut i = 1;\n        while value > 0 {\n            result += (value & 0x0f) * i;\n            i *= 10;\n            value >>= 4;\n        }\n\n        context.contribute_arg(Argument::Object(Object::Integer(result).wrap()));\n        context.retire_op(op);\n        Ok(())\n    }\n\n    fn do_to_bcd(&self, context: &mut MethodContext, op: OpInFlight) -> Result<(), AmlError> {\n        extract_args!(op => [Argument::Object(value)]);\n        let mut value = value.clone().unwrap_transparent_reference().as_integer()?;\n\n        let mut result = 0;\n        let mut i = 0;\n        while value > 0 {\n            result |= (value % 10) << (4 * i);\n            value /= 10;\n            i += 1;\n        }\n\n        context.contribute_arg(Argument::Object(Object::Integer(result).wrap()));\n        context.retire_op(op);\n        Ok(())\n    }\n\n    fn do_size_of(&self, context: &mut MethodContext, op: OpInFlight) -> Result<(), AmlError> {\n        extract_args!(op => [Argument::Object(object)]);\n        let object = object.clone().unwrap_transparent_reference();\n\n        let result = match *object {\n            Object::Buffer(ref buffer) => buffer.len(),\n            Object::String(ref str) => str.len(),\n            Object::Package(ref package) => package.len(),\n            _ => Err(AmlError::InvalidOperationOnObject { op: Operation::SizeOf, typ: object.typ() })?,\n        };\n\n        context.contribute_arg(Argument::Object(Object::Integer(result as u64).wrap()));\n        context.retire_op(op);\n        Ok(())\n    }\n\n    fn do_index(&self, context: &mut MethodContext, op: OpInFlight) -> Result<(), AmlError> {\n        extract_args!(op => [Argument::Object(object), Argument::Object(index_value), Argument::Object(target)]);\n        let object = object.clone().unwrap_transparent_reference();\n        let index_value = index_value.clone().unwrap_transparent_reference().as_integer()?;\n\n        let result = match *object {\n            Object::Buffer(ref buffer) => {\n                if index_value as usize >= buffer.len() {\n                    Err(AmlError::IndexOutOfBounds)?\n                }\n\n                Object::Reference {\n                    kind: ReferenceKind::RefOf,\n                    inner: Object::BufferField {\n                        buffer: object.clone(),\n                        offset: index_value as usize * 8,\n                        length: 8,\n                    }\n                    .wrap(),\n                }\n            }\n            Object::String(ref string) => {\n                if index_value as usize >= string.len() {\n                    Err(AmlError::IndexOutOfBounds)?\n                }\n\n                Object::Reference {\n                    kind: ReferenceKind::RefOf,\n                    inner: Object::BufferField {\n                        buffer: object.clone(),\n                        offset: index_value as usize * 8,\n                        length: 8,\n                    }\n                    .wrap(),\n                }\n            }\n            Object::Package(ref package) => {\n                let Some(element) = package.get(index_value as usize) else { Err(AmlError::IndexOutOfBounds)? };\n                Object::Reference { kind: ReferenceKind::RefOf, inner: element.clone() }\n            }\n            _ => Err(AmlError::IndexOutOfBounds)?,\n        }\n        .wrap();\n\n        self.do_store(target.clone(), result.clone())?;\n        context.contribute_arg(Argument::Object(result));\n        context.retire_op(op);\n        Ok(())\n    }\n\n    /// Perform a store of `object` into `target`, matching the expected behaviour of `DefStore`,\n    /// which depends on the target:\n    ///    - Locals are overwritten, unless they contain a reference, in which case a store is\n    ///      performed to the referenced object with implicit casting\n    ///    - Args are overwritten, unless they contain a reference, in which case the referenced\n    ///      object is overwritten\n    ///    - Index references behave the same as locals\n    ///    - Named objects are stored into, with implicit casting\n    fn do_store(&self, target: WrappedObject, object: WrappedObject) -> Result<WrappedObject, AmlError> {\n        let object = object.unwrap_transparent_reference();\n        let token = self.object_token.lock();\n\n        match unsafe { target.gain_mut(&token) } {\n            Object::Reference { kind, inner } => {\n                let (target_object, overwrite) = match kind {\n                    ReferenceKind::Named => (inner.clone().unwrap_reference(), false),\n                    ReferenceKind::Local | ReferenceKind::Index => {\n                        if let Object::Reference { kind: _, inner: ref inner_inner } = **inner {\n                            (inner_inner.clone(), false)\n                        } else {\n                            (inner.clone().unwrap_transparent_reference(), true)\n                        }\n                    }\n                    ReferenceKind::Arg => {\n                        if let Object::Reference { kind: _, inner: ref inner_inner } = **inner {\n                            (inner_inner.clone(), true)\n                        } else {\n                            (inner.clone().unwrap_transparent_reference(), true)\n                        }\n                    }\n                    ReferenceKind::RefOf | ReferenceKind::Unresolved => {\n                        return Err(AmlError::StoreToInvalidReferenceType);\n                    }\n                };\n\n                if overwrite {\n                    unsafe {\n                        *target_object.gain_mut(&token) = (*object).clone();\n                    }\n                } else {\n                    match &*target_object {\n                        Object::Integer(_) | Object::String(_) | Object::Buffer(_) => {\n                            let target_object = unsafe { target_object.gain_mut(&token) };\n                            target_object.replace_with_implicit_casting((*object).clone())?;\n                        }\n                        Object::BufferField { .. } => {\n                            let target_object = unsafe { target_object.gain_mut(&token) };\n                            match unsafe { object.gain_mut(&token) } {\n                                Object::Integer(value) => {\n                                    target_object.write_buffer_field(&value.to_le_bytes(), &token)?\n                                }\n                                Object::Buffer(value) => {\n                                    target_object.write_buffer_field(value.as_slice(), &token)?\n                                }\n                                _ => panic!(),\n                            }\n                        }\n\n                        /*\n                         * TODO: stores to fields with BufferAcc can return a different value to the\n                         * object stored. This is used for complex field types with a\n                         * write-then-read pattern. We should perform a read in those cases and\n                         * return that instead.\n                         */\n                        Object::FieldUnit(field_unit) => self.do_field_write(field_unit, object.clone())?,\n\n                        _ => {\n                            return Err(AmlError::InvalidOperationOnObject {\n                                op: Operation::Store,\n                                typ: target_object.typ(),\n                            });\n                        }\n                    }\n                }\n            }\n            Object::Debug => self.handler.handle_debug(&object),\n            Object::Integer(0) => {} // Store to NullName\n            _ => return Err(AmlError::InvalidOperationOnObject { op: Operation::Store, typ: target.typ() }),\n        }\n\n        Ok(object)\n    }\n\n    /// Copy `object` into `target`, matching the expected behaviour of `DefCopyObject`, which\n    /// depends on the object referenced:\n    ///    - Locals are overwritten\n    ///    - Args are overwritten, unless they are references, in which case the referenced object is overwritten\n    ///    - Objects referenced by name are overwritten\n    ///    - Index references cause the object at the index to be overwritten\n    ///    - Other reference operations are not allowed\n    fn do_copy_object(&self, target: WrappedObject, object: WrappedObject) -> Result<(), AmlError> {\n        let Object::Reference { kind, ref inner } = *target else {\n            return Err(AmlError::InternalError(\"Target of CopyObject must be a reference\".to_string()));\n        };\n        let object = object.clone().unwrap_transparent_reference();\n        let token = self.object_token.lock();\n\n        let dst = match kind {\n            ReferenceKind::Named | ReferenceKind::Local => inner.clone().unwrap_transparent_reference(),\n            ReferenceKind::Arg => {\n                if let Object::Reference { kind: _, inner: ref inner_inner } = **inner {\n                    inner_inner.clone()\n                } else {\n                    inner.clone().unwrap_transparent_reference()\n                }\n            }\n            ReferenceKind::Index => todo!(),\n            ReferenceKind::RefOf | ReferenceKind::Unresolved => return Err(AmlError::StoreToInvalidReferenceType),\n        };\n\n        unsafe {\n            *dst.gain_mut(&token) = (*object).clone();\n        }\n\n        Ok(())\n    }\n\n    /// Do a read from a field by performing one or more well-formed accesses to the underlying\n    /// operation regions, and then shifting and masking the resulting value as appropriate. Will\n    /// return either an `Integer` or `Buffer` as appropriate, guided by the size of the field\n    /// and expected integer size (as per the DSDT revision).\n    fn do_field_read(&self, field: &FieldUnit) -> Result<WrappedObject, AmlError> {\n        let needs_buffer = field.bit_length > (self.integer_size() * 8);\n        let access_width_bits = field.flags.access_type_bytes()? * 8;\n\n        trace!(\"AML field read. Field = {:?}\", field);\n\n        // TODO: if the field needs to be locked, acquire/release a global mutex?\n\n        enum Output {\n            Integer([u8; 8]),\n            Buffer(Vec<u8>),\n        }\n        let mut output = if needs_buffer {\n            Output::Buffer(vec![0; field.bit_length.next_multiple_of(8)])\n        } else {\n            Output::Integer([0; 8])\n        };\n        let output_bytes = match &mut output {\n            Output::Buffer(bytes) => bytes.as_mut_slice(),\n            Output::Integer(value) => value,\n        };\n\n        let (read_region, index_field_idx) = match field.kind {\n            FieldUnitKind::Normal { ref region } => (region, 0),\n            FieldUnitKind::Bank { ref region, ref bank, bank_value } => {\n                let Object::FieldUnit(ref bank) = **bank else { panic!() };\n                assert!(matches!(bank.kind, FieldUnitKind::Normal { .. }));\n                self.do_field_write(bank, Object::Integer(bank_value).wrap())?;\n                (region, 0)\n            }\n            FieldUnitKind::Index { index: _, ref data } => {\n                let Object::FieldUnit(ref data) = **data else { panic!() };\n                let FieldUnitKind::Normal { region } = &data.kind else { panic!() };\n                let reg_idx = field.bit_index / 8;\n                (region, reg_idx)\n            }\n        };\n        let Object::OpRegion(ref read_region) = **read_region else { panic!() };\n\n        /*\n         * TODO: it might be worth having a fast path here for reads that don't do weird\n         * unaligned accesses, which I'm guessing might be relatively common on real\n         * hardware? Eg. single native read + mask\n         */\n\n        /*\n         * Break the field read into native reads that respect the region's access width.\n         * Copy each potentially-unaligned part into the destination's bit range.\n         */\n        let native_accesses_needed = (field.bit_length + (field.bit_index % access_width_bits))\n            .next_multiple_of(access_width_bits)\n            / access_width_bits;\n        let mut read_so_far = 0;\n        for i in 0..native_accesses_needed {\n            // Advance the read pointer. For Index fields, this also means updating the Index\n            // register.\n            let aligned_offset = match field.kind {\n                FieldUnitKind::Normal { .. } | FieldUnitKind::Bank { .. } => {\n                    object::align_down(field.bit_index + i * access_width_bits, access_width_bits)\n                }\n                FieldUnitKind::Index { ref index, ref data } => {\n                    // Update index register\n                    let Object::FieldUnit(ref index) = **index else { panic!() };\n                    let Object::FieldUnit(ref data) = **data else { panic!() };\n                    self.do_field_write(\n                        index,\n                        Object::Integer((index_field_idx + i * (access_width_bits / 8)) as u64).wrap(),\n                    )?;\n\n                    // The offset is always that of the data register, as we always read from the\n                    // base of the data register.\n                    data.bit_index\n                }\n            };\n\n            let raw = self.do_native_region_read(read_region, aligned_offset / 8, access_width_bits / 8)?;\n            let src_index = if i == 0 { field.bit_index % access_width_bits } else { 0 };\n            let remaining_length = field.bit_length - read_so_far;\n            let length = if i == 0 {\n                usize::min(remaining_length, access_width_bits - (field.bit_index % access_width_bits))\n            } else {\n                usize::min(remaining_length, access_width_bits)\n            };\n\n            object::copy_bits(&raw.to_le_bytes(), src_index, output_bytes, read_so_far, length);\n            read_so_far += length;\n        }\n\n        match output {\n            Output::Buffer(bytes) => Ok(Object::Buffer(bytes).wrap()),\n            Output::Integer(value) => Ok(Object::Integer(u64::from_le_bytes(value)).wrap()),\n        }\n    }\n\n    fn do_field_write(&self, field: &FieldUnit, value: WrappedObject) -> Result<(), AmlError> {\n        trace!(\"AML field write. Field = {:?}. Value = {}\", field, value);\n\n        let value_bytes = match &*value {\n            Object::Integer(value) => &value.to_le_bytes() as &[u8],\n            Object::Buffer(bytes) => bytes,\n            _ => Err(AmlError::ObjectNotOfExpectedType { expected: ObjectType::Integer, got: value.typ() })?,\n        };\n        let access_width_bits = field.flags.access_type_bytes()? * 8;\n\n        // In this tuple:\n        // - write_region is the region that the data will be written to.\n        // - index_field_idx is the initial index to write into the Index register of an index\n        //   field. For all other field types it is unused and set to zero.\n        let (write_region, index_field_idx) = match field.kind {\n            FieldUnitKind::Normal { ref region } => (region, 0),\n            FieldUnitKind::Bank { ref region, ref bank, bank_value } => {\n                let Object::FieldUnit(ref bank) = **bank else { panic!() };\n                assert!(matches!(bank.kind, FieldUnitKind::Normal { .. }));\n                self.do_field_write(bank, Object::Integer(bank_value).wrap())?;\n                (region, 0)\n            }\n            FieldUnitKind::Index { index: _, ref data } => {\n                let Object::FieldUnit(ref data) = **data else { panic!() };\n                let FieldUnitKind::Normal { region: data_region } = &data.kind else { panic!() };\n                let reg_idx = field.bit_index / 8;\n                (data_region, reg_idx)\n            }\n        };\n        let Object::OpRegion(ref write_region) = **write_region else { panic!() };\n\n        // TODO: if the region wants locking, do that\n\n        // TODO: maybe also a fast path for writes\n\n        let native_accesses_needed = (field.bit_length + (field.bit_index % access_width_bits))\n            .next_multiple_of(access_width_bits)\n            / access_width_bits;\n        let mut written_so_far = 0;\n\n        for i in 0..native_accesses_needed {\n            // Advance the write pointer... For normal and bank fields this is straightforward. For\n            // Index fields, this involves updating the index register.\n            let aligned_offset = match field.kind {\n                FieldUnitKind::Normal { .. } | FieldUnitKind::Bank { .. } => {\n                    object::align_down(field.bit_index + i * access_width_bits, access_width_bits)\n                }\n                FieldUnitKind::Index { ref index, ref data } => {\n                    // Update index register\n                    let Object::FieldUnit(ref index) = **index else { panic!() };\n                    let Object::FieldUnit(ref data) = **data else { panic!() };\n                    self.do_field_write(\n                        index,\n                        Object::Integer((index_field_idx + i * (access_width_bits / 8)) as u64).wrap(),\n                    )?;\n\n                    // The offset is always that of the data register, as we always read from the\n                    // base of the data register.\n                    data.bit_index\n                }\n            };\n            let dst_index = if i == 0 { field.bit_index % access_width_bits } else { 0 };\n\n            /*\n             * If we're not going to write a whole native access, respect the field's\n             * update rule. If we're meant to preserve the surrounding bits, we need to do\n             * a read first.\n             */\n            let mut bytes = if dst_index > 0 || (field.bit_length - written_so_far) < access_width_bits {\n                match field.flags.update_rule() {\n                    FieldUpdateRule::Preserve => self\n                        .do_native_region_read(write_region, aligned_offset / 8, access_width_bits / 8)?\n                        .to_le_bytes(),\n                    FieldUpdateRule::WriteAsOnes => [0xff; 8],\n                    FieldUpdateRule::WriteAsZeros => [0; 8],\n                }\n            } else {\n                [0; 8]\n            };\n\n            let remaining_length = field.bit_length - written_so_far;\n            let length = if i == 0 {\n                usize::min(remaining_length, access_width_bits - (field.bit_index % access_width_bits))\n            } else {\n                usize::min(remaining_length, access_width_bits)\n            };\n\n            object::copy_bits(value_bytes, written_so_far, &mut bytes, dst_index, length);\n            self.do_native_region_write(\n                write_region,\n                aligned_offset / 8,\n                access_width_bits / 8,\n                u64::from_le_bytes(bytes),\n            )?;\n            written_so_far += length;\n        }\n\n        Ok(())\n    }\n\n    /// Performs an actual read from an operation region. `offset` and `length` must respect the\n    /// access requirements of the field being read, and are supplied in **bytes**. This may call\n    /// AML methods if required, and may invoke user-supplied handlers.\n    fn do_native_region_read(&self, region: &OpRegion, offset: usize, length: usize) -> Result<u64, AmlError> {\n        trace!(\"Native field read. Region = {:?}, offset = {:#x}, length={:#x}\", region, offset, length);\n\n        match region.space {\n            RegionSpace::SystemMemory => Ok({\n                let address = region.base as usize + offset;\n                match length {\n                    1 => self.handler.read_u8(address) as u64,\n                    2 => self.handler.read_u16(address) as u64,\n                    4 => self.handler.read_u32(address) as u64,\n                    8 => self.handler.read_u64(address),\n                    _ => panic!(),\n                }\n            }),\n            RegionSpace::SystemIO => Ok({\n                let address = region.base as u16 + offset as u16;\n                match length {\n                    1 => self.handler.read_io_u8(address) as u64,\n                    2 => self.handler.read_io_u16(address) as u64,\n                    4 => self.handler.read_io_u32(address) as u64,\n                    _ => panic!(),\n                }\n            }),\n            RegionSpace::PciConfig => {\n                let address = self.pci_address_for_device(&region.parent_device_path)?;\n                let offset = region.base as u16 + offset as u16;\n                match length {\n                    1 => Ok(self.handler.read_pci_u8(address, offset) as u64),\n                    2 => Ok(self.handler.read_pci_u16(address, offset) as u64),\n                    4 => Ok(self.handler.read_pci_u32(address, offset) as u64),\n                    _ => panic!(),\n                }\n            }\n\n            RegionSpace::EmbeddedControl\n            | RegionSpace::SmBus\n            | RegionSpace::SystemCmos\n            | RegionSpace::PciBarTarget\n            | RegionSpace::Ipmi\n            | RegionSpace::GeneralPurposeIo\n            | RegionSpace::GenericSerialBus\n            | RegionSpace::Pcc\n            | RegionSpace::Oem(_) => {\n                if let Some(_handler) = self.region_handlers.lock().get(&region.space) {\n                    warn!(\"Custom region handlers aren't actually supported yet.\");\n                    Err(AmlError::LibUnimplemented)\n                } else {\n                    Err(AmlError::NoHandlerForRegionAccess(region.space))\n                }\n            }\n        }\n    }\n\n    /// Performs an actual write to an operation region. `offset` and `length` must respect the\n    /// access requirements of the field being read, and are supplied in **bytes**. This may call\n    /// AML methods if required, and may invoke user-supplied handlers.\n    fn do_native_region_write(\n        &self,\n        region: &OpRegion,\n        offset: usize,\n        length: usize,\n        value: u64,\n    ) -> Result<(), AmlError> {\n        trace!(\n            \"Native field write. Region = {:?}, offset = {:#x}, length={:#x}, value={:#x}\",\n            region, offset, length, value\n        );\n\n        match region.space {\n            RegionSpace::SystemMemory => {\n                let address = region.base as usize + offset;\n                match length {\n                    1 => self.handler.write_u8(address, value as u8),\n                    2 => self.handler.write_u16(address, value as u16),\n                    4 => self.handler.write_u32(address, value as u32),\n                    8 => self.handler.write_u64(address, value),\n                    _ => panic!(),\n                }\n                Ok(())\n            }\n            RegionSpace::SystemIO => {\n                let address = region.base as u16 + offset as u16;\n                match length {\n                    1 => self.handler.write_io_u8(address, value as u8),\n                    2 => self.handler.write_io_u16(address, value as u16),\n                    4 => self.handler.write_io_u32(address, value as u32),\n                    _ => panic!(),\n                }\n                Ok(())\n            }\n            RegionSpace::PciConfig => {\n                let address = self.pci_address_for_device(&region.parent_device_path)?;\n                let offset = region.base as u16 + offset as u16;\n                match length {\n                    1 => self.handler.write_pci_u8(address, offset, value as u8),\n                    2 => self.handler.write_pci_u16(address, offset, value as u16),\n                    4 => self.handler.write_pci_u32(address, offset, value as u32),\n                    _ => panic!(),\n                }\n                Ok(())\n            }\n\n            RegionSpace::EmbeddedControl\n            | RegionSpace::SmBus\n            | RegionSpace::SystemCmos\n            | RegionSpace::PciBarTarget\n            | RegionSpace::Ipmi\n            | RegionSpace::GeneralPurposeIo\n            | RegionSpace::GenericSerialBus\n            | RegionSpace::Pcc\n            | RegionSpace::Oem(_) => {\n                if let Some(_handler) = self.region_handlers.lock().get(&region.space) {\n                    warn!(\"Custom region handlers aren't actually supported yet.\");\n                    Err(AmlError::LibUnimplemented)\n                } else {\n                    Err(AmlError::NoHandlerForRegionAccess(region.space))\n                }\n            }\n        }\n    }\n\n    fn pci_address_for_device(&self, path: &AmlName) -> Result<PciAddress, AmlError> {\n        /*\n         * TODO: it's not ideal to do these reads for every native access. See if we can\n         * cache them somewhere?\n         */\n        let seg = match self.evaluate_if_present(AmlName::from_str(\"_SEG\").unwrap().resolve(path)?, vec![])? {\n            Some(value) => value.as_integer()?,\n            None => 0,\n        };\n        let bus = match self.evaluate_if_present(AmlName::from_str(\"_BBN\").unwrap().resolve(path)?, vec![])? {\n            Some(value) => value.as_integer()?,\n            None => 0,\n        };\n        let (device, function) = {\n            let adr = self.evaluate_if_present(AmlName::from_str(\"_ADR\").unwrap().resolve(path)?, vec![])?;\n            let adr = match adr {\n                Some(adr) => adr.as_integer()?,\n                None => 0,\n            };\n            (adr.get_bits(16..32), adr.get_bits(0..16))\n        };\n        Ok(PciAddress::new(seg as u16, bus as u8, device as u8, function as u8))\n    }\n}\n\n/// A `MethodContext` represents a piece of running AML code - either a real method, or the\n/// top-level of an AML table.\n///\n/// ### Safety\n/// `MethodContext` does not keep the lifetime of the underlying AML stream, which for tables is\n/// borrowed from the underlying physical mapping. This is because the interpreter needs to\n/// preempt method contexts that execute other methods, and these contexts may have disparate\n/// lifetimes. This is made safe in the case of methods by the context holding a reference to the\n/// method object, but must be handled manually for AML tables.\nstruct MethodContext {\n    current_block: Block,\n    block_stack: Vec<Block>,\n    in_flight: Vec<OpInFlight>,\n    args: [WrappedObject; 8],\n    locals: [WrappedObject; 8],\n    current_scope: AmlName,\n\n    _method: Option<WrappedObject>,\n}\n\nstruct Block {\n    stream: *const [u8],\n    pc: usize,\n    kind: BlockKind,\n}\n\nimpl Block {\n    fn stream(&self) -> &[u8] {\n        unsafe { &*self.stream }\n    }\n}\n\n#[derive(PartialEq, Debug)]\npub enum BlockKind {\n    Table,\n    Method {\n        method_scope: AmlName,\n    },\n    Scope {\n        old_scope: AmlName,\n    },\n    Package,\n    VarPackage,\n    /// Used for executing the then-branch of an `DefIfElse`. After finishing, it will check for\n    /// and skip over an else-branch, if present.\n    IfThenBranch,\n    While {\n        start_pc: usize,\n    },\n}\n\n/// A `ResolveBehaviour` describes how a name at the top-level should be resolved as part of an\n/// operation.\n#[derive(Clone, Copy, PartialEq, Debug)]\nenum ResolveBehaviour {\n    /// Attempt to resolve the name to an object that has already been defined. There are generally\n    /// no forward definitions in AML, so this is the usual resolution behaviour for most operands.\n    TermArg,\n    /// Resolve a name to reference an object. This is used when an operation needs to operate on\n    /// the object itself, rather than evaluate it to a value (for example, given a `FieldUnit`, `TermArg` would read a\n    /// value from the field, while this would resolve to the `FieldUnit` itself. Can be a name,\n    /// argument, or local.\n    SimpleName,\n    /// Like a `SimpleName`, but can also be the `Debug` object or an operation that produces a\n    /// reference.\n    SuperName,\n    /// Behaves the same as `SuperName` if the object exists, but resolves successfully to an\n    /// unresolved reference if the object does not exist. Used by `DefCondRefOf`.\n    SuperNameIfExists,\n    /// `SuperName`, but can also be `NullName`\n    Target,\n    /// Surrogate argument, used by `DefPackage` and `DefVarPackage`. Only one of these is emitted,\n    /// but represents parsing of potentially many package elements. Names in packages should be\n    /// resolved into `String` objects - this is not well defined by the specification, but matches\n    /// expected behaviour of other interpreters.\n    AsPackageElements,\n    /// Used with [`OpInFlight::new_with`] to represent arguments that have already been resolved\n    /// when an operation enters flight.\n    Placeholder,\n}\n\n#[derive(Debug)]\nstruct OpInFlight {\n    op: Opcode,\n    expected_arguments: usize,\n    arguments: Vec<Argument>,\n    resolve_behaviour: &'static [ResolveBehaviour],\n}\n\n#[derive(Debug)]\nenum Argument {\n    Object(WrappedObject),\n    Namestring(AmlName),\n    ByteData(u8),\n    DWordData(u32),\n    TrackedPc(usize),\n    PkgLength(usize),\n}\n\nimpl OpInFlight {\n    /// Creates a new `OpInFlight`. The number of expected arguments is inferred from the number of\n    /// `ResolveBehaviour`s passed.\n    pub fn new(op: Opcode, resolve_behaviour: &'static [ResolveBehaviour]) -> OpInFlight {\n        OpInFlight { op, expected_arguments: resolve_behaviour.len(), arguments: Vec::new(), resolve_behaviour }\n    }\n\n    /// Creates a new `OpInFlight` with the given number of expected arguments. This should be used\n    /// when the correct number of expected arguments differs from the number of\n    /// `ResolveBehaviour`s passed.\n    pub fn new_dynamic(\n        op: Opcode,\n        expected_arguments: usize,\n        resolve_behaviour: &'static [ResolveBehaviour],\n    ) -> OpInFlight {\n        OpInFlight { op, expected_arguments, arguments: Vec::new(), resolve_behaviour }\n    }\n\n    /// Creates a new `OpInFlight` with a number of arguments that have already been interpreted,\n    /// and is expecting some `more` arguments.\n    pub fn new_with_dynamic(\n        op: Opcode,\n        arguments: Vec<Argument>,\n        more: usize,\n        resolve_behaviour: &'static [ResolveBehaviour],\n    ) -> OpInFlight {\n        OpInFlight { op, expected_arguments: arguments.len() + more, arguments, resolve_behaviour }\n    }\n\n    /// Creates a new `OpInFlight` with a number of arguments that have already been interpreted,\n    /// and is expecting more - the number of remaining arguments is inferred from the number of\n    /// `ResolveBehaviour`s passed (with existing arguments marked as\n    /// `ResolveBehaviour::Placeholder`).\n    pub fn new_with(\n        op: Opcode,\n        arguments: Vec<Argument>,\n        resolve_behaviour: &'static [ResolveBehaviour],\n    ) -> OpInFlight {\n        OpInFlight { op, expected_arguments: resolve_behaviour.len(), arguments, resolve_behaviour }\n    }\n\n    /// Get the desired `ResolveBehaviour` for the argument currently being interpreted\n    fn resolve_behaviour(&self) -> ResolveBehaviour {\n        if let Some(behaviour) = self.resolve_behaviour.get(self.arguments.len()) {\n            *behaviour\n        } else if self.op == Opcode::Package || (self.op == Opcode::VarPackage && self.arguments.len() > 0) {\n            ResolveBehaviour::AsPackageElements\n        } else {\n            panic!(\"Tried to get resolving behaviour for unexpected argument for operation of type {:?}\", self.op);\n        }\n    }\n}\n\nimpl MethodContext {\n    unsafe fn new_from_table(stream: &[u8]) -> MethodContext {\n        let block = Block { stream: stream as *const [u8], pc: 0, kind: BlockKind::Table };\n        MethodContext {\n            current_block: block,\n            block_stack: Vec::new(),\n            in_flight: Vec::new(),\n            args: core::array::from_fn(|_| Object::Uninitialized.wrap()),\n            locals: core::array::from_fn(|_| Object::Uninitialized.wrap()),\n            current_scope: AmlName::root(),\n            _method: None,\n        }\n    }\n\n    fn new_from_method(\n        method: WrappedObject,\n        args: Vec<WrappedObject>,\n        scope: AmlName,\n    ) -> Result<MethodContext, AmlError> {\n        if let Object::Method { code, flags } = &*method {\n            if args.len() != flags.arg_count() {\n                return Err(AmlError::MethodArgCountIncorrect);\n            }\n            let block = Block {\n                stream: code as &[u8] as *const [u8],\n                pc: 0,\n                kind: BlockKind::Method { method_scope: scope.clone() },\n            };\n            let args = core::array::from_fn(|i| {\n                if let Some(arg) = args.get(i) { arg.clone() } else { Object::Uninitialized.wrap() }\n            });\n            let context = MethodContext {\n                current_block: block,\n                block_stack: Vec::new(),\n                in_flight: Vec::new(),\n                args,\n                locals: core::array::from_fn(|_| Object::Uninitialized.wrap()),\n                current_scope: scope,\n                _method: Some(method.clone()),\n            };\n            Ok(context)\n        } else {\n            Err(AmlError::ObjectNotOfExpectedType { expected: ObjectType::Method, got: method.typ() })\n        }\n    }\n\n    fn last_op(&mut self) -> Result<&mut OpInFlight, AmlError> {\n        match self.in_flight.last_mut() {\n            Some(op) => Ok(op),\n            None => Err(AmlError::NoCurrentOp),\n        }\n    }\n\n    fn contribute_arg(&mut self, arg: Argument) {\n        if let Some(in_flight) = self.in_flight.last_mut()\n            && in_flight.arguments.len() < in_flight.expected_arguments\n        {\n            in_flight.arguments.push(arg);\n        }\n    }\n\n    /// Start a new `InFlightOp`.\n    fn start(&mut self, op: OpInFlight) {\n        trace!(\n            \"START OP: {:?}, args: {:?}, with {} more needed ({:?})\",\n            op.op,\n            op.arguments,\n            op.expected_arguments - op.arguments.len(),\n            op.resolve_behaviour\n        );\n        self.in_flight.push(op);\n    }\n\n    fn retire_op(&mut self, op: OpInFlight) {\n        trace!(\"RETIRE OP: {:?}, args: {:?}\", op.op, op.arguments);\n    }\n\n    fn start_new_block(&mut self, kind: BlockKind, length: usize) {\n        let block = Block {\n            stream: &self.current_block.stream()[..(self.current_block.pc + length)] as *const [u8],\n            pc: self.current_block.pc,\n            kind,\n        };\n        self.current_block.pc += length;\n        self.block_stack.push(mem::replace(&mut self.current_block, block));\n    }\n\n    fn opcode(&mut self) -> Result<Opcode, AmlError> {\n        let opcode: u16 = match self.next()? {\n            0x5b => {\n                let ext = self.next()?;\n                (0x5b << 8) as u16 | ext as u16\n            }\n            other => other as u16,\n        };\n\n        Ok(match opcode {\n            0x00 => Opcode::Zero,\n            0x01 => Opcode::One,\n            0x06 => Opcode::Alias,\n            0x08 => Opcode::Name,\n            0x0a => Opcode::BytePrefix,\n            0x0b => Opcode::WordPrefix,\n            0x0c => Opcode::DWordPrefix,\n            0x0d => Opcode::StringPrefix,\n            0x0e => Opcode::QWordPrefix,\n            0x10 => Opcode::Scope,\n            0x11 => Opcode::Buffer,\n            0x12 => Opcode::Package,\n            0x13 => Opcode::VarPackage,\n            0x14 => Opcode::Method,\n            0x15 => Opcode::External,\n            0x2e => Opcode::DualNamePrefix,\n            0x2f => Opcode::MultiNamePrefix,\n            0x30..=0x39 => Opcode::Digit(opcode as u8),    // b'0'..=b'9'\n            0x41..=0x5a => Opcode::NameChar(opcode as u8), // b'A'..=b'Z'\n            0x5b01 => Opcode::Mutex,\n            0x5b02 => Opcode::Event,\n            0x5b12 => Opcode::CondRefOf,\n            0x5b13 => Opcode::CreateField,\n            0x5b1f => Opcode::LoadTable,\n            0x5b20 => Opcode::Load,\n            0x5b21 => Opcode::Stall,\n            0x5b22 => Opcode::Sleep,\n            0x5b23 => Opcode::Acquire,\n            0x5b24 => Opcode::Signal,\n            0x5b25 => Opcode::Wait,\n            0x5b26 => Opcode::Reset,\n            0x5b27 => Opcode::Release,\n            0x5b28 => Opcode::FromBCD,\n            0x5b29 => Opcode::ToBCD,\n            0x5b30 => Opcode::Revision,\n            0x5b31 => Opcode::Debug,\n            0x5b32 => Opcode::Fatal,\n            0x5b33 => Opcode::Timer,\n            0x5b80 => Opcode::OpRegion,\n            0x5b81 => Opcode::Field,\n            0x5b82 => Opcode::Device,\n            0x5b83 => Opcode::Processor,\n            0x5b84 => Opcode::PowerRes,\n            0x5b85 => Opcode::ThermalZone,\n            0x5b86 => Opcode::IndexField,\n            0x5b87 => Opcode::BankField,\n            0x5b88 => Opcode::DataRegion,\n            0x5c => Opcode::RootChar,\n            0x5e => Opcode::ParentPrefixChar,\n            0x5f => Opcode::NameChar(b'_'),\n            0x60..=0x67 => Opcode::Local(opcode as u8 - 0x60),\n            0x68..=0x6e => Opcode::Arg(opcode as u8 - 0x68),\n            0x70 => Opcode::Store,\n            0x71 => Opcode::RefOf,\n            0x72 => Opcode::Add,\n            0x73 => Opcode::Concat,\n            0x74 => Opcode::Subtract,\n            0x75 => Opcode::Increment,\n            0x76 => Opcode::Decrement,\n            0x77 => Opcode::Multiply,\n            0x78 => Opcode::Divide,\n            0x79 => Opcode::ShiftLeft,\n            0x7a => Opcode::ShiftRight,\n            0x7b => Opcode::And,\n            0x7c => Opcode::Nand,\n            0x7d => Opcode::Or,\n            0x7e => Opcode::Nor,\n            0x7f => Opcode::Xor,\n            0x80 => Opcode::Not,\n            0x81 => Opcode::FindSetLeftBit,\n            0x82 => Opcode::FindSetRightBit,\n            0x83 => Opcode::DerefOf,\n            0x84 => Opcode::ConcatRes,\n            0x85 => Opcode::Mod,\n            0x86 => Opcode::Notify,\n            0x87 => Opcode::SizeOf,\n            0x88 => Opcode::Index,\n            0x89 => Opcode::Match,\n            0x8a => Opcode::CreateDWordField,\n            0x8b => Opcode::CreateWordField,\n            0x8c => Opcode::CreateByteField,\n            0x8d => Opcode::CreateBitField,\n            0x8e => Opcode::ObjectType,\n            0x8f => Opcode::CreateQWordField,\n            0x90 => Opcode::LAnd,\n            0x91 => Opcode::LOr,\n            /*\n             * `0x92` is a bit strange. It can be an opcode in its own right (`LNotOp`), but when\n             * followed by `0x93..=0x95`, it instead serves as a negating prefix to encode\n             * `LNotEqualOp`, `LLessEqualOp`, and `LGreaterEqualOp`.\n             */\n            0x92 => match self.peek() {\n                Ok(0x93) => {\n                    self.current_block.pc += 1;\n                    Opcode::LNotEqual\n                }\n                Ok(0x94) => {\n                    self.current_block.pc += 1;\n                    Opcode::LLessEqual\n                }\n                Ok(0x95) => {\n                    self.current_block.pc += 1;\n                    Opcode::LGreaterEqual\n                }\n                _ => Opcode::LNot,\n            },\n            0x93 => Opcode::LEqual,\n            0x94 => Opcode::LGreater,\n            0x95 => Opcode::LLess,\n            0x96 => Opcode::ToBuffer,\n            0x97 => Opcode::ToDecimalString,\n            0x98 => Opcode::ToHexString,\n            0x99 => Opcode::ToInteger,\n            0x9c => Opcode::ToString,\n            0x9d => Opcode::CopyObject,\n            0x9e => Opcode::Mid,\n            0x9f => Opcode::Continue,\n            0xa0 => Opcode::If,\n            0xa1 => Opcode::Else,\n            0xa2 => Opcode::While,\n            0xa3 => Opcode::Noop,\n            0xa4 => Opcode::Return,\n            0xa5 => Opcode::Break,\n            0xcc => Opcode::Breakpoint,\n            0xff => Opcode::Ones,\n\n            _ => Err(AmlError::IllegalOpcode(opcode))?,\n        })\n    }\n\n    fn pkglength(&mut self) -> Result<usize, AmlError> {\n        let lead_byte = self.next()?;\n        let byte_count = lead_byte.get_bits(6..8);\n        assert!(byte_count < 4);\n\n        if byte_count == 0 {\n            Ok(lead_byte.get_bits(0..6) as usize)\n        } else {\n            let mut length = lead_byte.get_bits(0..4) as usize;\n            for i in 0..byte_count {\n                length |= (self.next()? as usize) << (4 + i * 8);\n            }\n            Ok(length)\n        }\n    }\n\n    fn namestring(&mut self) -> Result<AmlName, AmlError> {\n        use namespace::{NameComponent, NameSeg};\n\n        /*\n         * The NameString grammar is actually a little finicky and annoying.\n         *\n         * NameString := <RootChar NamePath> | <PrefixPath NamePath>\n         * PrefixPath := Nothing | <'^' PrefixPath>\n         * NamePath := NameSeg | DualNamePath | MultiNamePath | NullName\n         * DualNamePath := DualNamePrefix NameSeg NameSeg\n         * MultiNamePath := MultiNamePrefix SegCount NameSeg(SegCount)\n         */\n        const NULL_NAME: u8 = 0x00;\n        const DUAL_NAME_PREFIX: u8 = 0x2e;\n        const MULTI_NAME_PREFIX: u8 = 0x2f;\n\n        let mut components = vec![];\n\n        match self.peek()? {\n            b'\\\\' => {\n                self.next()?;\n                components.push(NameComponent::Root);\n            }\n            b'^' => {\n                components.push(NameComponent::Prefix);\n                self.next()?;\n                while self.peek()? == b'^' {\n                    self.next()?;\n                    components.push(NameComponent::Prefix);\n                }\n            }\n            _ => (),\n        }\n\n        let next = self.next()?;\n        match next {\n            NULL_NAME => {}\n            DUAL_NAME_PREFIX => {\n                for _ in 0..2 {\n                    let name_seg = [self.next()?, self.next()?, self.next()?, self.next()?];\n                    components.push(NameComponent::Segment(NameSeg::from_bytes(name_seg)?));\n                }\n            }\n            MULTI_NAME_PREFIX => {\n                let count = self.next()?;\n                for _ in 0..count {\n                    let name_seg = [self.next()?, self.next()?, self.next()?, self.next()?];\n                    components.push(NameComponent::Segment(NameSeg::from_bytes(name_seg)?));\n                }\n            }\n            first_char => {\n                if !namespace::is_lead_name_char(first_char) {\n                    self.current_block.pc -= 1;\n                }\n                let name_seg = [first_char, self.next()?, self.next()?, self.next()?];\n                components.push(namespace::NameComponent::Segment(namespace::NameSeg::from_bytes(name_seg)?));\n            }\n        }\n\n        Ok(AmlName::from_components(components))\n    }\n\n    fn next(&mut self) -> Result<u8, AmlError> {\n        if self.current_block.pc >= self.current_block.stream.len() {\n            return Err(AmlError::RunOutOfStream);\n        }\n\n        let byte = self.current_block.stream()[self.current_block.pc];\n        self.current_block.pc += 1;\n\n        Ok(byte)\n    }\n\n    fn next_u16(&mut self) -> Result<u16, AmlError> {\n        Ok(u16::from_le_bytes([self.next()?, self.next()?]))\n    }\n\n    fn next_u32(&mut self) -> Result<u32, AmlError> {\n        Ok(u32::from_le_bytes([self.next()?, self.next()?, self.next()?, self.next()?]))\n    }\n\n    fn next_u64(&mut self) -> Result<u64, AmlError> {\n        Ok(u64::from_le_bytes([\n            self.next()?,\n            self.next()?,\n            self.next()?,\n            self.next()?,\n            self.next()?,\n            self.next()?,\n            self.next()?,\n            self.next()?,\n        ]))\n    }\n\n    fn peek(&self) -> Result<u8, AmlError> {\n        if self.current_block.pc >= self.current_block.stream.len() {\n            return Err(AmlError::RunOutOfStream);\n        }\n\n        Ok(self.current_block.stream()[self.current_block.pc])\n    }\n}\n\n#[derive(Clone, Copy, PartialEq, Debug)]\nenum Opcode {\n    Zero,\n    One,\n    Alias,\n    Name,\n    BytePrefix,\n    WordPrefix,\n    DWordPrefix,\n    StringPrefix,\n    QWordPrefix,\n    Scope,\n    Buffer,\n    Package,\n    VarPackage,\n    Method,\n    External,\n    DualNamePrefix,\n    MultiNamePrefix,\n    Digit(u8),\n    NameChar(u8),\n    Mutex,\n    Event,\n    CondRefOf,\n    CreateField,\n    LoadTable,\n    Load,\n    Stall,\n    Sleep,\n    Acquire,\n    Signal,\n    Wait,\n    Reset,\n    Release,\n    FromBCD,\n    ToBCD,\n    Revision,\n    Debug,\n    Fatal,\n    Timer,\n    OpRegion,\n    Field,\n    Device,\n    Processor,\n    PowerRes,\n    ThermalZone,\n    IndexField,\n    BankField,\n    DataRegion,\n    RootChar,\n    ParentPrefixChar,\n    Local(u8),\n    Arg(u8),\n    Store,\n    RefOf,\n    Add,\n    Concat,\n    Subtract,\n    Increment,\n    Decrement,\n    Multiply,\n    Divide,\n    ShiftLeft,\n    ShiftRight,\n    And,\n    Nand,\n    Or,\n    Nor,\n    Xor,\n    Not,\n    FindSetLeftBit,\n    FindSetRightBit,\n    DerefOf,\n    ConcatRes,\n    Mod,\n    Notify,\n    SizeOf,\n    Index,\n    Match,\n    CreateDWordField,\n    CreateWordField,\n    CreateByteField,\n    CreateBitField,\n    ObjectType,\n    CreateQWordField,\n    LAnd,\n    LOr,\n    LNot,\n    LNotEqual,\n    LLessEqual,\n    LGreaterEqual,\n    LEqual,\n    LGreater,\n    LLess,\n    ToBuffer,\n    ToDecimalString,\n    ToHexString,\n    ToInteger,\n    ToString,\n    CopyObject,\n    Mid,\n    Continue,\n    If,\n    Else,\n    While,\n    Noop,\n    Return,\n    Break,\n    Breakpoint,\n    Ones,\n\n    /*\n     * Internal opcodes are not produced from the bytecode, but are used to track special in-flight\n     * ops etc.\n     */\n    InternalMethodCall,\n}\n\n#[derive(Clone, Copy, PartialEq, Debug)]\npub enum Operation {\n    Mid,\n    SizeOf,\n    Acquire,\n    Release,\n    ConvertToBuffer,\n\n    ToBuffer,\n    ToInteger,\n    ToString,\n    ToDecOrHexString,\n\n    ReadBufferField,\n    WriteBufferField,\n    LogicalOp,\n    DecodePrt,\n    ParseResource,\n\n    Store,\n\n    ResetEvent,\n    SignalEvent,\n    WaitEvent,\n}\n\n#[derive(Clone, PartialEq, Debug)]\n#[non_exhaustive]\npub enum AmlError {\n    RunOutOfStream,\n    IllegalOpcode(u16),\n    InvalidFieldFlags,\n\n    InvalidName(Option<AmlName>),\n\n    InvalidNameSeg([u8; 4]),\n    InvalidNormalizedName(AmlName),\n    RootHasNoParent,\n    EmptyNamesAreInvalid,\n    LevelDoesNotExist(AmlName),\n    NameCollision(AmlName),\n    ObjectDoesNotExist(AmlName),\n\n    NoCurrentOp,\n    ElseFoundWithoutCorrespondingIf,\n    ContinueOutsideOfWhile,\n    BreakOutsideOfWhile,\n\n    MethodArgCountIncorrect,\n\n    InvalidOperationOnObject {\n        op: Operation,\n        typ: ObjectType,\n    },\n    IndexOutOfBounds,\n    ObjectNotOfExpectedType {\n        expected: ObjectType,\n        got: ObjectType,\n    },\n    InvalidImplicitCast {\n        from: ObjectType,\n        to: ObjectType,\n    },\n    StoreToInvalidReferenceType,\n\n    InvalidResourceDescriptor,\n    UnexpectedResourceType,\n\n    NoHandlerForRegionAccess(RegionSpace),\n    MutexAcquireTimeout,\n\n    PrtInvalidAddress,\n    PrtInvalidPin,\n    PrtInvalidGsi,\n    PrtInvalidSource,\n    PrtNoEntry,\n\n    /// An OEM-defined fatal error has occured. The specification states a host should log this\n    /// fatal error and then shutdown in a timely fashion.\n    FatalErrorEncountered,\n\n    /// This is emitted to signal that the library does not support the requested behaviour. This\n    /// should eventually never be emitted.\n    LibUnimplemented,\n\n    /// The library has given a response the host does not understand, or the host is otherwise\n    /// unable to continue operating the library correctly. The specific reason is given in the\n    /// contained String.\n    ///\n    /// This variant is set by the host, not by the library, and can be used when it is convenient\n    /// not to construct a more complex error type around [`AmlError`].\n    HostError(String),\n\n    /// An internal interpreter error has occured, and the interpreter has been left in an unknown\n    /// state. More information may be given in the contained value.\n    InternalError(String),\n}\n"
  },
  {
    "path": "src/aml/namespace.rs",
    "content": "use super::{\n    AmlError,\n    Handle,\n    object::{Object, ObjectType, WrappedObject},\n};\nuse alloc::{\n    collections::btree_map::BTreeMap,\n    string::{String, ToString},\n    vec,\n    vec::Vec,\n};\nuse bit_field::BitField;\nuse core::{\n    fmt,\n    str::{self, FromStr},\n};\nuse log::{trace, warn};\n\n#[derive(Clone)]\npub struct Namespace {\n    root: NamespaceLevel,\n}\n\nimpl Namespace {\n    /// Create a new AML namespace, with the expected pre-defined objects.\n    pub fn new(global_lock_mutex: Handle) -> Namespace {\n        let mut namespace = Namespace { root: NamespaceLevel::new(NamespaceLevelKind::Scope) };\n\n        namespace.add_level(AmlName::from_str(\"\\\\_GPE\").unwrap(), NamespaceLevelKind::Scope).unwrap();\n        namespace.add_level(AmlName::from_str(\"\\\\_SB\").unwrap(), NamespaceLevelKind::Scope).unwrap();\n        namespace.add_level(AmlName::from_str(\"\\\\_SI\").unwrap(), NamespaceLevelKind::Scope).unwrap();\n        namespace.add_level(AmlName::from_str(\"\\\\_PR\").unwrap(), NamespaceLevelKind::Scope).unwrap();\n        namespace.add_level(AmlName::from_str(\"\\\\_TZ\").unwrap(), NamespaceLevelKind::Scope).unwrap();\n\n        namespace\n            .insert(\n                AmlName::from_str(\"\\\\_GL\").unwrap(),\n                Object::Mutex { mutex: global_lock_mutex, sync_level: 0 }.wrap(),\n            )\n            .unwrap();\n\n        /*\n         * In the dark ages of ACPI 1.0, before `\\_OSI`, `\\_OS` was used to communicate to the firmware which OS\n         * was running. This was predictably not very good, and so was replaced in ACPI 3.0 with `_OSI`, which\n         * allows support for individual capabilities to be queried. `_OS` should not be used by modern firmwares;\n         * we follow the NT interpreter and ACPICA by calling ourselves `Microsoft Windows NT`.\n         *\n         * See https://www.kernel.org/doc/html/latest/firmware-guide/acpi/osi.html for more information.\n         */\n        namespace\n            .insert(AmlName::from_str(\"\\\\_OS\").unwrap(), Object::String(\"Microsoft Windows NT\".to_string()).wrap())\n            .unwrap();\n\n        /*\n         * `\\_OSI` was introduced by ACPI 3.0 to improve the situation created by `\\_OS`. Unfortunately, exactly\n         * the same problem was immediately repeated by introducing capabilities reflecting that an ACPI\n         * implementation is exactly the same as a particular version of Windows' (e.g. firmwares will call\n         * `\\_OSI(\"Windows 2001\")`).\n         *\n         * We basically follow suit with whatever Linux does, as this will hopefully minimise breakage:\n         *    - We always claim `Windows *` compatability\n         *    - We answer 'yes' to `_OSI(\"Darwin\")\n         *    - We answer 'no' to `_OSI(\"Linux\")`, and report that the tables are doing the wrong thing\n         */\n        namespace\n            .insert(\n                AmlName::from_str(\"\\\\_OSI\").unwrap(),\n                Object::native_method(1, |args| {\n                    if args.len() != 1 {\n                        return Err(AmlError::MethodArgCountIncorrect);\n                    }\n                    let Object::String(ref feature) = *args[0] else {\n                        return Err(AmlError::ObjectNotOfExpectedType {\n                            expected: ObjectType::String,\n                            got: args[0].typ(),\n                        });\n                    };\n\n                    let is_supported = match feature.as_str() {\n                        \"Windows 2000\" => true,       // 2000\n                        \"Windows 2001\" => true,       // XP\n                        \"Windows 2001 SP1\" => true,   // XP SP1\n                        \"Windows 2001 SP2\" => true,   // XP SP2\n                        \"Windows 2001.1\" => true,     // Server 2003\n                        \"Windows 2001.1 SP1\" => true, // Server 2003 SP1\n                        \"Windows 2006\" => true,       // Vista\n                        \"Windows 2006 SP1\" => true,   // Vista SP1\n                        \"Windows 2006 SP2\" => true,   // Vista SP2\n                        \"Windows 2006.1\" => true,     // Server 2008\n                        \"Windows 2009\" => true,       // 7 and Server 2008 R2\n                        \"Windows 2012\" => true,       // 8 and Server 2012\n                        \"Windows 2013\" => true,       // 8.1 and Server 2012 R2\n                        \"Windows 2015\" => true,       // 10\n                        \"Windows 2016\" => true,       // 10 version 1607\n                        \"Windows 2017\" => true,       // 10 version 1703\n                        \"Windows 2017.2\" => true,     // 10 version 1709\n                        \"Windows 2018\" => true,       // 10 version 1803\n                        \"Windows 2018.2\" => true,     // 10 version 1809\n                        \"Windows 2019\" => true,       // 10 version 1903\n                        \"Windows 2020\" => true,       // 10 version 20H1\n                        \"Windows 2021\" => true,       // 11\n                        \"Windows 2022\" => true,       // 11 version 22H2\n\n                        // TODO: Linux answers yes to this, NT answers no. Maybe make configurable\n                        \"Darwin\" => false,\n\n                        \"Linux\" => {\n                            // TODO: should we allow users to specify that this should be true? Linux has a\n                            // command line option for this.\n                            warn!(\"ACPI evaluated `_OSI(\\\"Linux\\\")`. This is a bug. Reporting no support.\");\n                            false\n                        }\n\n                        \"Extended Address Space Descriptor\" => true,\n                        \"Module Device\" => true,\n                        \"3.0 Thermal Model\" => true,\n                        \"3.0 _SCP Extensions\" => true,\n                        \"Processor Aggregator Device\" => true,\n                        _ => false,\n                    };\n\n                    Ok(Object::Integer(if is_supported { u64::MAX } else { 0 }).wrap())\n                })\n                .wrap(),\n            )\n            .unwrap();\n\n        /*\n         * `\\_REV` evaluates to the version of the ACPI specification supported by this interpreter. Linux did this\n         * correctly until 2015, but firmwares misused this to detect Linux (as even modern versions of Windows\n         * return `2`), and so they switched to just returning `2` (as we'll also do). `_REV` should be considered\n         * useless and deprecated (this is mirrored in newer specs, which claim `2` means \"ACPI 2 or greater\").\n         */\n        namespace.insert(AmlName::from_str(\"\\\\_REV\").unwrap(), Object::Integer(2).wrap()).unwrap();\n\n        namespace\n    }\n\n    pub fn add_level(&mut self, path: AmlName, kind: NamespaceLevelKind) -> Result<(), AmlError> {\n        assert!(path.is_absolute());\n        let path = path.normalize()?;\n\n        // Don't try to recreate the root scope\n        if path != AmlName::root() {\n            let (level, last_seg) = self.get_level_for_path_mut(&path)?;\n\n            /*\n             * If the level has already been added, we don't need to add it again. The parser can try to add it\n             * multiple times if the ASL contains multiple blocks that add to the same scope/device.\n             */\n            level.children.entry(last_seg).or_insert_with(|| NamespaceLevel::new(kind));\n        }\n\n        Ok(())\n    }\n\n    pub fn remove_level(&mut self, path: AmlName) -> Result<(), AmlError> {\n        assert!(path.is_absolute());\n        let path = path.normalize()?;\n\n        // Don't try to remove the root scope\n        // TODO: we probably shouldn't be able to remove the pre-defined scopes either?\n        if path != AmlName::root() {\n            let (level, last_seg) = self.get_level_for_path_mut(&path)?;\n            level.children.remove(&last_seg);\n        }\n\n        Ok(())\n    }\n\n    pub fn insert(&mut self, path: AmlName, object: WrappedObject) -> Result<(), AmlError> {\n        assert!(path.is_absolute());\n        let path = path.normalize()?;\n\n        let (level, last_seg) = self.get_level_for_path_mut(&path)?;\n        match level.values.insert(last_seg, (ObjectFlags::new(false), object)) {\n            None => Ok(()),\n            Some(_) => {\n                /*\n                 * Real AML often has name collisions, and so we can't afford to be too strict\n                 * about it. We do warn the user as it does have the potential to break stuff.\n                 */\n                trace!(\"AML name collision: {}. Replacing object.\", path);\n                Ok(())\n            }\n        }\n    }\n\n    pub fn create_alias(&mut self, path: AmlName, object: WrappedObject) -> Result<(), AmlError> {\n        assert!(path.is_absolute());\n        let path = path.normalize()?;\n\n        let (level, last_seg) = self.get_level_for_path_mut(&path)?;\n        match level.values.insert(last_seg, (ObjectFlags::new(true), object)) {\n            None => Ok(()),\n            Some(_) => Err(AmlError::NameCollision(path)),\n        }\n    }\n\n    pub fn get(&mut self, path: AmlName) -> Result<WrappedObject, AmlError> {\n        assert!(path.is_absolute());\n        let path = path.normalize()?;\n\n        let (level, last_seg) = self.get_level_for_path_mut(&path)?;\n        match level.values.get(&last_seg) {\n            Some((_, object)) => Ok(object.clone()),\n            None => Err(AmlError::ObjectDoesNotExist(path.clone())),\n        }\n    }\n\n    /// Search for an object at the given path of the namespace, applying the search rules described in §5.3 of the\n    /// ACPI specification, if they are applicable. Returns the resolved name, and the handle of the first valid\n    /// object, if found.\n    pub fn search(&self, path: &AmlName, starting_scope: &AmlName) -> Result<(AmlName, WrappedObject), AmlError> {\n        if path.search_rules_apply() {\n            /*\n             * If search rules apply, we need to recursively look through the namespace. If the\n             * given name does not occur in the current scope, we look at the parent scope, until\n             * we either find the name, or reach the root of the namespace.\n             */\n            let mut scope = starting_scope.clone();\n            assert!(scope.is_absolute());\n            loop {\n                // Search for the name at this namespace level. If we find it, we're done.\n                let name = path.resolve(&scope)?;\n                match self.get_level_for_path(&name) {\n                    Ok((level, last_seg)) => {\n                        if let Some((_, object)) = level.values.get(&last_seg) {\n                            return Ok((name, object.clone()));\n                        }\n                    }\n\n                    Err(err) => return Err(err),\n                }\n\n                // If we don't find it, go up a level in the namespace and search for it there recursively\n                match scope.parent() {\n                    Ok(parent) => scope = parent,\n                    Err(AmlError::RootHasNoParent) => return Err(AmlError::ObjectDoesNotExist(path.clone())),\n                    Err(err) => return Err(err),\n                }\n            }\n        } else {\n            // If search rules don't apply, simply resolve it against the starting scope\n            let name = path.resolve(starting_scope)?;\n            let (level, last_seg) = self.get_level_for_path(&path.resolve(starting_scope)?)?;\n\n            if let Some((_, object)) = level.values.get(&last_seg) {\n                Ok((name, object.clone()))\n            } else {\n                Err(AmlError::ObjectDoesNotExist(path.clone()))\n            }\n        }\n    }\n\n    pub fn search_for_level(&self, level_name: &AmlName, starting_scope: &AmlName) -> Result<AmlName, AmlError> {\n        if level_name.search_rules_apply() {\n            let mut scope = starting_scope.clone().normalize()?;\n            assert!(scope.is_absolute());\n\n            loop {\n                let name = level_name.resolve(&scope)?;\n                if let Ok((level, last_seg)) = self.get_level_for_path(&name)\n                    && level.children.contains_key(&last_seg)\n                {\n                    return Ok(name);\n                }\n\n                // If we don't find it, move the scope up a level and search for it there recursively\n                match scope.parent() {\n                    Ok(parent) => scope = parent,\n                    Err(AmlError::RootHasNoParent) => return Err(AmlError::LevelDoesNotExist(level_name.clone())),\n                    Err(err) => return Err(err),\n                }\n            }\n        } else {\n            Ok(level_name.clone())\n        }\n    }\n\n    /// Split an absolute path into a bunch of level segments (used to traverse the level data structure), and a\n    /// last segment to index into that level. This must not be called on `\\\\`.\n    fn get_level_for_path(&self, path: &AmlName) -> Result<(&NamespaceLevel, NameSeg), AmlError> {\n        assert_ne!(*path, AmlName::root());\n\n        let (last_seg, levels) = path.0[1..].split_last().unwrap();\n        let NameComponent::Segment(last_seg) = last_seg else {\n            panic!();\n        };\n\n        // TODO: this helps with diagnostics, but requires a heap allocation just in case we need to error.\n        let mut traversed_path = AmlName::root();\n\n        let mut current_level = &self.root;\n        for level in levels {\n            traversed_path.0.push(*level);\n\n            let NameComponent::Segment(segment) = level else {\n                panic!();\n            };\n            current_level =\n                current_level.children.get(segment).ok_or(AmlError::LevelDoesNotExist(traversed_path.clone()))?;\n        }\n\n        Ok((current_level, *last_seg))\n    }\n\n    /// Split an absolute path into a bunch of level segments (used to traverse the level data structure), and a\n    /// last segment to index into that level. This must not be called on `\\\\`.\n    fn get_level_for_path_mut(&mut self, path: &AmlName) -> Result<(&mut NamespaceLevel, NameSeg), AmlError> {\n        assert_ne!(*path, AmlName::root());\n\n        let (last_seg, levels) = path.0[1..].split_last().unwrap();\n        let NameComponent::Segment(last_seg) = last_seg else {\n            panic!();\n        };\n\n        // TODO: this helps with diagnostics, but requires a heap allocation just in case we need to error. We can\n        // improve this by changing the `levels` interation into an `enumerate()`, and then using the index to\n        // create the correct path on the error path\n        let mut traversed_path = AmlName::root();\n\n        let mut current_level = &mut self.root;\n        for level in levels {\n            traversed_path.0.push(*level);\n\n            let NameComponent::Segment(segment) = level else {\n                panic!();\n            };\n            current_level = current_level\n                .children\n                .get_mut(segment)\n                .ok_or(AmlError::LevelDoesNotExist(traversed_path.clone()))?;\n        }\n\n        Ok((current_level, *last_seg))\n    }\n\n    /// Traverse the namespace, calling `f` on each namespace level. `f` returns a `Result<bool, AmlError>` -\n    /// errors terminate the traversal and are propagated, and the `bool` on the successful path marks whether the\n    /// children of the level should also be traversed.\n    pub fn traverse<F>(&mut self, mut f: F) -> Result<(), AmlError>\n    where\n        F: FnMut(&AmlName, &NamespaceLevel) -> Result<bool, AmlError>,\n    {\n        fn traverse_level<F>(level: &NamespaceLevel, scope: &AmlName, f: &mut F) -> Result<(), AmlError>\n        where\n            F: FnMut(&AmlName, &NamespaceLevel) -> Result<bool, AmlError>,\n        {\n            for (name, child) in level.children.iter() {\n                let name = AmlName::from_name_seg(*name).resolve(scope)?;\n\n                if f(&name, child)? {\n                    traverse_level(child, &name, f)?;\n                }\n            }\n\n            Ok(())\n        }\n\n        if f(&AmlName::root(), &self.root)? {\n            traverse_level(&self.root, &AmlName::root(), &mut f)?;\n        }\n\n        Ok(())\n    }\n}\n\nimpl fmt::Display for Namespace {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        const STEM: &str = \"│   \";\n        const BRANCH: &str = \"├── \";\n        const END: &str = \"└── \";\n\n        fn print_level(f: &mut fmt::Formatter<'_>, level: &NamespaceLevel, indent_stack: String) -> fmt::Result {\n            for (i, (name, (flags, object))) in level.values.iter().enumerate() {\n                let end = (i == level.values.len() - 1)\n                    && level.children.iter().filter(|(_, l)| l.kind == NamespaceLevelKind::Scope).count() == 0;\n                writeln!(\n                    f,\n                    \"{}{}{}: {}{}\",\n                    &indent_stack,\n                    if end { END } else { BRANCH },\n                    name.as_str(),\n                    if flags.is_alias() { \"[A] \" } else { \"\" },\n                    **object\n                )?;\n\n                // If the object has a corresponding scope, print it here\n                if let Some(child_level) = level.children.get(name) {\n                    print_level(\n                        f,\n                        child_level,\n                        if end { indent_stack.clone() + \"    \" } else { indent_stack.clone() + STEM },\n                    )?;\n                }\n            }\n\n            let remaining_scopes: Vec<_> =\n                level.children.iter().filter(|(_, l)| l.kind == NamespaceLevelKind::Scope).collect();\n            for (i, (name, sub_level)) in remaining_scopes.iter().enumerate() {\n                let end = i == remaining_scopes.len() - 1;\n                writeln!(f, \"{}{}{}:\", &indent_stack, if end { END } else { BRANCH }, name.as_str())?;\n                print_level(f, sub_level, indent_stack.clone() + STEM)?;\n            }\n\n            Ok(())\n        }\n\n        writeln!(f, \"\\n    \\\\:\")?;\n        print_level(f, &self.root, String::from(\"    \"))\n    }\n}\n\n#[derive(Clone, Copy, PartialEq, Debug)]\npub enum NamespaceLevelKind {\n    Scope,\n    Device,\n    Processor,\n    PowerResource,\n    ThermalZone,\n    MethodLocals,\n}\n\n#[derive(Clone)]\npub struct NamespaceLevel {\n    pub kind: NamespaceLevelKind,\n    pub values: BTreeMap<NameSeg, (ObjectFlags, WrappedObject)>,\n    pub children: BTreeMap<NameSeg, NamespaceLevel>,\n}\n\n#[derive(Clone, Copy, Debug)]\npub struct ObjectFlags(u8);\n\nimpl ObjectFlags {\n    pub fn new(is_alias: bool) -> ObjectFlags {\n        let mut flags = 0;\n        flags.set_bit(0, is_alias);\n        ObjectFlags(flags)\n    }\n\n    pub fn is_alias(&self) -> bool {\n        self.0.get_bit(0)\n    }\n}\n\nimpl NamespaceLevel {\n    pub fn new(kind: NamespaceLevelKind) -> NamespaceLevel {\n        NamespaceLevel { kind, values: BTreeMap::new(), children: BTreeMap::new() }\n    }\n}\n\n#[derive(Clone, PartialEq, Debug)]\npub struct AmlName(Vec<NameComponent>);\n\n#[derive(Clone, Copy, PartialEq, Debug)]\npub enum NameComponent {\n    Root,\n    Prefix,\n    Segment(NameSeg),\n}\n\nimpl AmlName {\n    pub fn root() -> AmlName {\n        AmlName(vec![NameComponent::Root])\n    }\n\n    pub fn from_name_seg(seg: NameSeg) -> AmlName {\n        AmlName(vec![NameComponent::Segment(seg)])\n    }\n\n    pub fn from_components(components: Vec<NameComponent>) -> AmlName {\n        AmlName(components)\n    }\n\n    pub fn as_string(&self) -> String {\n        self.0\n            .iter()\n            .fold(String::new(), |name, component| match component {\n                NameComponent::Root => name + \"\\\\\",\n                NameComponent::Prefix => name + \"^\",\n                NameComponent::Segment(seg) => name + seg.as_str() + \".\",\n            })\n            .trim_end_matches('.')\n            .to_string()\n    }\n\n    /// An AML path is normal if it does not contain any prefix elements (\"^\" characters, when\n    /// expressed as a string).\n    pub fn is_normal(&self) -> bool {\n        !self.0.contains(&NameComponent::Prefix)\n    }\n\n    pub fn is_absolute(&self) -> bool {\n        self.0.first() == Some(&NameComponent::Root)\n    }\n\n    /// Special rules apply when searching for certain paths (specifically, those that are made up\n    /// of a single name segment). Returns `true` if those rules apply.\n    pub fn search_rules_apply(&self) -> bool {\n        if self.0.len() != 1 {\n            return false;\n        }\n\n        matches!(self.0[0], NameComponent::Segment(_))\n    }\n\n    /// Normalize an AML path, resolving prefix chars. Returns `AmlError::InvalidNormalizedName` if the path\n    /// normalizes to an invalid path (e.g. `\\^_FOO`)\n    pub fn normalize(self) -> Result<AmlName, AmlError> {\n        /*\n         * If the path is already normal, just return it as-is. This avoids an unneccessary heap allocation and\n         * free.\n         */\n        if self.is_normal() {\n            return Ok(self);\n        }\n\n        Ok(AmlName(self.0.iter().try_fold(Vec::new(), |mut name, &component| match component {\n            seg @ NameComponent::Segment(_) => {\n                name.push(seg);\n                Ok(name)\n            }\n\n            NameComponent::Root => {\n                name.push(NameComponent::Root);\n                Ok(name)\n            }\n\n            NameComponent::Prefix => {\n                if let Some(NameComponent::Segment(_)) = name.iter().last() {\n                    name.pop().unwrap();\n                    Ok(name)\n                } else {\n                    Err(AmlError::InvalidNormalizedName(self.clone()))\n                }\n            }\n        })?))\n    }\n\n    /// Get the parent of this `AmlName`. For example, the parent of `\\_SB.PCI0._PRT` is `\\_SB.PCI0`. The root\n    /// path has no parent, and so returns `None`.\n    pub fn parent(&self) -> Result<AmlName, AmlError> {\n        // Firstly, normalize the path so we don't have to deal with prefix chars\n        let mut normalized_self = self.clone().normalize()?;\n\n        match normalized_self.0.last() {\n            None | Some(NameComponent::Root) => Err(AmlError::RootHasNoParent),\n            Some(NameComponent::Segment(_)) => {\n                normalized_self.0.pop();\n                Ok(normalized_self)\n            }\n            Some(NameComponent::Prefix) => unreachable!(), // Prefix chars are removed by normalization\n        }\n    }\n\n    /// Resolve this path against a given scope, making it absolute. If the path is absolute, it is\n    /// returned directly. The path is also normalized.\n    pub fn resolve(&self, scope: &AmlName) -> Result<AmlName, AmlError> {\n        assert!(scope.is_absolute());\n\n        if self.is_absolute() {\n            return Ok(self.clone());\n        }\n\n        let mut resolved_path = scope.clone();\n        resolved_path.0.extend_from_slice(&(self.0));\n        resolved_path.normalize()\n    }\n}\n\nimpl FromStr for AmlName {\n    type Err = AmlError;\n\n    fn from_str(mut string: &str) -> Result<Self, Self::Err> {\n        if string.is_empty() {\n            return Err(AmlError::EmptyNamesAreInvalid);\n        }\n\n        let mut components = Vec::new();\n\n        // If it starts with a \\, make it an absolute name\n        if string.starts_with('\\\\') {\n            components.push(NameComponent::Root);\n            string = &string[1..];\n        }\n\n        if !string.is_empty() {\n            // Divide the rest of it into segments, and parse those\n            for mut part in string.split('.') {\n                // Handle prefix chars\n                while part.starts_with('^') {\n                    components.push(NameComponent::Prefix);\n                    part = &part[1..];\n                }\n\n                components.push(NameComponent::Segment(NameSeg::from_str(part)?));\n            }\n        }\n\n        Ok(Self(components))\n    }\n}\n\nimpl fmt::Display for AmlName {\n    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {\n        write!(f, \"{}\", self.as_string())\n    }\n}\n\n#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]\npub struct NameSeg(pub(crate) [u8; 4]);\n\nimpl NameSeg {\n    pub fn from_bytes(bytes: [u8; 4]) -> Result<NameSeg, AmlError> {\n        if !is_lead_name_char(bytes[0]) {\n            return Err(AmlError::InvalidNameSeg(bytes));\n        }\n        if !is_name_char(bytes[1]) {\n            return Err(AmlError::InvalidNameSeg(bytes));\n        }\n        if !is_name_char(bytes[2]) {\n            return Err(AmlError::InvalidNameSeg(bytes));\n        }\n        if !is_name_char(bytes[3]) {\n            return Err(AmlError::InvalidNameSeg(bytes));\n        }\n        Ok(NameSeg(bytes))\n    }\n\n    pub fn as_str(&self) -> &str {\n        // We should only construct valid ASCII name segments\n        unsafe { str::from_utf8_unchecked(&self.0) }\n    }\n}\n\nimpl FromStr for NameSeg {\n    type Err = AmlError;\n\n    fn from_str(s: &str) -> Result<Self, Self::Err> {\n        // Each NameSeg can only have four chars, and must have at least one\n        if s.is_empty() || s.len() > 4 {\n            return Err(AmlError::InvalidNameSeg([0xff, 0xff, 0xff, 0xff]));\n        }\n\n        // We pre-fill the array with '_', so it will already be correct if the length is < 4\n        let mut seg = [b'_'; 4];\n        let bytes = s.as_bytes();\n\n        // Manually do the first one, because we have to check it's a LeadNameChar\n        if !is_lead_name_char(bytes[0]) {\n            return Err(AmlError::InvalidNameSeg([bytes[0], bytes[1], bytes[2], bytes[3]]));\n        }\n        seg[0] = bytes[0];\n\n        // Copy the rest of the chars, checking that they're NameChars\n        for i in 1..bytes.len() {\n            if !is_name_char(bytes[i]) {\n                return Err(AmlError::InvalidNameSeg([bytes[0], bytes[1], bytes[2], bytes[3]]));\n            }\n            seg[i] = bytes[i];\n        }\n\n        Ok(NameSeg(seg))\n    }\n}\n\npub fn is_lead_name_char(c: u8) -> bool {\n    c.is_ascii_uppercase() || c == b'_'\n}\n\npub fn is_name_char(c: u8) -> bool {\n    is_lead_name_char(c) || c.is_ascii_digit()\n}\n\nimpl fmt::Debug for NameSeg {\n    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {\n        write!(f, \"{:?}\", self.as_str())\n    }\n}\n"
  },
  {
    "path": "src/aml/object.rs",
    "content": "use crate::aml::{AmlError, Handle, Operation, op_region::OpRegion};\nuse alloc::{\n    borrow::Cow,\n    string::{String, ToString},\n    sync::Arc,\n    vec::Vec,\n};\nuse bit_field::BitField;\nuse core::{cell::UnsafeCell, fmt, ops, sync::atomic::AtomicU64};\n\ntype NativeMethod = dyn Fn(&[WrappedObject]) -> Result<WrappedObject, AmlError>;\n\n#[derive(Clone)]\npub enum Object {\n    Uninitialized,\n    Buffer(Vec<u8>),\n    BufferField { buffer: WrappedObject, offset: usize, length: usize },\n    Device,\n    Event(Arc<AtomicU64>),\n    FieldUnit(FieldUnit),\n    Integer(u64),\n    Method { code: Vec<u8>, flags: MethodFlags },\n    NativeMethod { f: Arc<NativeMethod>, flags: MethodFlags },\n    Mutex { mutex: Handle, sync_level: u8 },\n    Reference { kind: ReferenceKind, inner: WrappedObject },\n    OpRegion(OpRegion),\n    Package(Vec<WrappedObject>),\n    PowerResource { system_level: u8, resource_order: u16 },\n    Processor { proc_id: u8, pblk_address: u32, pblk_length: u8 },\n    RawDataBuffer,\n    String(String),\n    ThermalZone,\n    Debug,\n}\n\nimpl Object {\n    pub fn native_method<F>(num_args: u8, f: F) -> Object\n    where\n        F: Fn(&[WrappedObject]) -> Result<WrappedObject, AmlError> + 'static,\n    {\n        let mut flags = 0;\n        flags.set_bits(0..3, num_args);\n        Object::NativeMethod { f: Arc::new(f), flags: MethodFlags(flags) }\n    }\n}\n\nimpl fmt::Display for Object {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        match self {\n            Object::Uninitialized => write!(f, \"[Uninitialized]\"),\n            Object::Buffer(bytes) => write!(f, \"Buffer({bytes:x?})\"),\n            Object::BufferField { offset, length, .. } => {\n                write!(f, \"BufferField {{ offset: {offset}, length: {length} }}\")\n            }\n            Object::Device => write!(f, \"Device\"),\n            Object::Event(counter) => write!(f, \"Event({counter:?})\"),\n            // TODO: include fields\n            Object::FieldUnit(_) => write!(f, \"FieldUnit\"),\n            Object::Integer(value) => write!(f, \"Integer({value})\"),\n            // TODO: decode flags here\n            Object::Method { .. } => write!(f, \"Method\"),\n            Object::NativeMethod { .. } => write!(f, \"NativeMethod\"),\n            Object::Mutex { .. } => write!(f, \"Mutex\"),\n            Object::Reference { kind, inner } => write!(f, \"Reference({:?} -> {})\", kind, **inner),\n            Object::OpRegion(region) => write!(f, \"{region:?}\"),\n            Object::Package(elements) => {\n                write!(f, \"Package {{ \")?;\n                for (i, element) in elements.iter().enumerate() {\n                    if i == elements.len() - 1 {\n                        write!(f, \"{}\", **element)?;\n                    } else {\n                        write!(f, \"{}, \", **element)?;\n                    }\n                }\n                write!(f, \" }}\")?;\n                Ok(())\n            }\n            // TODO: include fields\n            Object::PowerResource { .. } => write!(f, \"PowerResource\"),\n            // TODO: include fields\n            Object::Processor { .. } => write!(f, \"Processor\"),\n            Object::RawDataBuffer => write!(f, \"RawDataBuffer\"),\n            Object::String(value) => write!(f, \"String({value:?})\"),\n            Object::ThermalZone => write!(f, \"ThermalZone\"),\n            Object::Debug => write!(f, \"Debug\"),\n        }\n    }\n}\n\n/// `ObjectToken` is used to mediate mutable access to objects from a [`WrappedObject`]. It must be\n/// acquired by locking the single token provided by [`super::Interpreter`].\n#[non_exhaustive]\npub struct ObjectToken {\n    _dont_construct_me: (),\n}\n\nimpl ObjectToken {\n    /// Create an [`ObjectToken`]. This should **only** be done **once** by the main interpreter,\n    /// as contructing your own token allows invalid mutable access to objects.\n    pub(super) unsafe fn create_interpreter_token() -> ObjectToken {\n        ObjectToken { _dont_construct_me: () }\n    }\n}\n\n#[derive(Clone, Debug)]\npub struct WrappedObject(Arc<UnsafeCell<Object>>);\n\nimpl WrappedObject {\n    pub fn new(object: Object) -> WrappedObject {\n        #[allow(clippy::arc_with_non_send_sync)]\n        WrappedObject(Arc::new(UnsafeCell::new(object)))\n    }\n\n    /// Gain a mutable reference to an [`Object`] from this [`WrappedObject`].\n    ///\n    /// # Safety\n    /// This requires an [`ObjectToken`] which is protected by a lock on [`super::Interpreter`],\n    /// which prevents mutable access to objects from multiple contexts. It does not, however,\n    /// prevent the same object, referenced from multiple [`WrappedObject`]s, having multiple\n    /// mutable (and therefore aliasing) references being made to it, and therefore care must be\n    /// taken in the interpreter to prevent this.\n    pub unsafe fn gain_mut<'r, 'a, 't>(&'a self, _token: &'t ObjectToken) -> &'r mut Object\n    where\n        't: 'r,\n        'a: 'r,\n    {\n        unsafe { &mut *(self.0.get()) }\n    }\n\n    pub fn unwrap_reference(self) -> WrappedObject {\n        let mut object = self;\n        loop {\n            if let Object::Reference { ref inner, .. } = *object {\n                object = inner.clone();\n            } else {\n                return object.clone();\n            }\n        }\n    }\n\n    /// Unwraps 'transparent' references (e.g. locals, arguments, and internal usage of reference-type objects), but maintain 'real'\n    /// references deliberately created by AML.\n    pub fn unwrap_transparent_reference(self) -> WrappedObject {\n        let mut object = self;\n        loop {\n            if let Object::Reference { kind, ref inner } = *object\n                && (kind == ReferenceKind::Local || kind == ReferenceKind::Arg || kind == ReferenceKind::Named)\n            {\n                object = inner.clone();\n            } else {\n                return object.clone();\n            }\n        }\n    }\n}\n\nimpl ops::Deref for WrappedObject {\n    type Target = Object;\n\n    fn deref(&self) -> &Self::Target {\n        /*\n         * SAFETY: elided lifetime ensures reference cannot outlive at least one reference-counted\n         * instance of the object. `WrappedObject::gain_mut` is unsafe, and so it is the user's\n         * responsibility to ensure shared references from `Deref` do not co-exist with an\n         * exclusive reference.\n         */\n        unsafe { &*self.0.get() }\n    }\n}\n\nimpl fmt::Display for WrappedObject {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        write!(f, \"Wrapped({})\", unsafe { &*self.0.get() })\n    }\n}\n\nimpl Object {\n    pub fn wrap(self) -> WrappedObject {\n        WrappedObject::new(self)\n    }\n\n    pub fn as_integer(&self) -> Result<u64, AmlError> {\n        if let Object::Integer(value) = self {\n            Ok(*value)\n        } else {\n            Err(AmlError::ObjectNotOfExpectedType { expected: ObjectType::Integer, got: self.typ() })\n        }\n    }\n\n    pub fn as_string(&self) -> Result<Cow<'_, str>, AmlError> {\n        if let Object::String(value) = self {\n            Ok(Cow::from(value))\n        } else {\n            Err(AmlError::ObjectNotOfExpectedType { expected: ObjectType::String, got: self.typ() })\n        }\n    }\n\n    pub fn as_buffer(&self) -> Result<&[u8], AmlError> {\n        if let Object::Buffer(bytes) = self {\n            Ok(bytes)\n        } else {\n            Err(AmlError::ObjectNotOfExpectedType { expected: ObjectType::Buffer, got: self.typ() })\n        }\n    }\n\n    pub fn to_integer(&self, allowed_bytes: usize) -> Result<u64, AmlError> {\n        match self {\n            Object::Integer(value) => Ok(*value),\n            Object::Buffer(value) => {\n                let length = usize::min(value.len(), allowed_bytes);\n                let mut bytes = [0u8; 8];\n                bytes[0..length].copy_from_slice(&value[0..length]);\n                Ok(u64::from_le_bytes(bytes))\n            }\n            // TODO: how should we handle invalid inputs? What does NT do here?\n            Object::String(value) => Ok(value.parse::<u64>().unwrap_or(0)),\n            _ => Ok(0),\n        }\n    }\n\n    pub fn to_buffer(&self, allowed_bytes: usize) -> Result<Vec<u8>, AmlError> {\n        match self {\n            Object::Buffer(bytes) => Ok(bytes.clone()),\n            Object::Integer(value) => match allowed_bytes {\n                4 => Ok((*value as u32).to_le_bytes().to_vec()),\n                8 => Ok(value.to_le_bytes().to_vec()),\n                _ => panic!(),\n            },\n            Object::String(value) => Ok(value.as_bytes().to_vec()),\n            _ => Err(AmlError::InvalidOperationOnObject { op: Operation::ConvertToBuffer, typ: self.typ() }),\n        }\n    }\n\n    pub fn read_buffer_field(&self, integer_size: usize) -> Result<Object, AmlError> {\n        if let Self::BufferField { buffer, offset, length } = self {\n            let buffer = match **buffer {\n                Object::Buffer(ref buffer) => buffer.as_slice(),\n                Object::String(ref string) => string.as_bytes(),\n                _ => panic!(),\n            };\n            if *length <= integer_size {\n                let mut dst = [0u8; 8];\n                copy_bits(buffer, *offset, &mut dst, 0, *length);\n                Ok(Object::Integer(u64::from_le_bytes(dst)))\n            } else {\n                let mut dst = alloc::vec![0u8; length.div_ceil(8)];\n                copy_bits(buffer, *offset, &mut dst, 0, *length);\n                Ok(Object::Buffer(dst))\n            }\n        } else {\n            Err(AmlError::InvalidOperationOnObject { op: Operation::ReadBufferField, typ: self.typ() })\n        }\n    }\n\n    pub fn write_buffer_field(&mut self, value: &[u8], token: &ObjectToken) -> Result<(), AmlError> {\n        // TODO: bounds check the buffer first to avoid panicking\n        if let Self::BufferField { buffer, offset, length } = self {\n            let buffer = match unsafe { buffer.gain_mut(token) } {\n                Object::Buffer(buffer) => buffer.as_mut_slice(),\n                // XXX: this unfortunately requires us to trust AML to keep the string as valid\n                // UTF8... maybe there is a better way?\n                Object::String(string) => unsafe { string.as_bytes_mut() },\n                _ => panic!(),\n            };\n            copy_bits(value, 0, buffer, *offset, *length);\n            Ok(())\n        } else {\n            Err(AmlError::InvalidOperationOnObject { op: Operation::WriteBufferField, typ: self.typ() })\n        }\n    }\n\n    /// Replace this object's contents with that of a `new` object, applying implicit casting rules\n    /// as needed. This follows the NT interpreter's creative interpretation of implicit casts, which is\n    /// effectively a byte-wise transmutation.\n    pub fn replace_with_implicit_casting(&mut self, new: Object) -> Result<(), AmlError> {\n        let new_bytes = match new {\n            Object::Integer(value) => &value.to_le_bytes(),\n            Object::String(ref value) => value.as_bytes(),\n            Object::Buffer(ref value) => &value.clone(),\n            _ => return Err(AmlError::InvalidImplicitCast { from: self.typ(), to: new.typ() }),\n        };\n\n        match self {\n            Object::Integer(value) => {\n                let bytes_to_copy = core::cmp::min(new_bytes.len(), 8);\n                let mut bytes = [0u8; 8];\n                bytes[0..bytes_to_copy].copy_from_slice(&new_bytes[0..bytes_to_copy]);\n                *value = u64::from_le_bytes(bytes);\n            }\n            Object::String(value) => {\n                *value = String::from_utf8_lossy(&new_bytes).to_string();\n            }\n            Object::Buffer(value) => {\n                *value = new_bytes.to_vec();\n            }\n            _ => return Err(AmlError::InvalidImplicitCast { from: self.typ(), to: new.typ() }),\n        }\n\n        Ok(())\n    }\n\n    /// Returns the `ObjectType` of this object. Returns the type of the referenced object in the\n    /// case of `Object::Reference`.\n    pub fn typ(&self) -> ObjectType {\n        match self {\n            Object::Uninitialized => ObjectType::Uninitialized,\n            Object::Buffer(_) => ObjectType::Buffer,\n            Object::BufferField { .. } => ObjectType::BufferField,\n            Object::Device => ObjectType::Device,\n            Object::Event(_) => ObjectType::Event,\n            Object::FieldUnit(_) => ObjectType::FieldUnit,\n            Object::Integer(_) => ObjectType::Integer,\n            Object::Method { .. } => ObjectType::Method,\n            Object::NativeMethod { .. } => ObjectType::Method,\n            Object::Mutex { .. } => ObjectType::Mutex,\n            // Object::Reference { inner, .. } => inner.typ(),\n            Object::Reference { .. } => ObjectType::Reference, // TODO: maybe this should\n            // differentiate internal/real\n            // references?\n            Object::OpRegion(_) => ObjectType::OpRegion,\n            Object::Package(_) => ObjectType::Package,\n            Object::PowerResource { .. } => ObjectType::PowerResource,\n            Object::Processor { .. } => ObjectType::Processor,\n            Object::RawDataBuffer => ObjectType::RawDataBuffer,\n            Object::String(_) => ObjectType::String,\n            Object::ThermalZone => ObjectType::ThermalZone,\n            Object::Debug => ObjectType::Debug,\n        }\n    }\n}\n\n#[derive(Clone, Debug)]\npub struct FieldUnit {\n    pub kind: FieldUnitKind,\n    pub flags: FieldFlags,\n    pub bit_index: usize,\n    pub bit_length: usize,\n}\n\n#[derive(Clone, Debug)]\npub enum FieldUnitKind {\n    Normal { region: WrappedObject },\n    Bank { region: WrappedObject, bank: WrappedObject, bank_value: u64 },\n    Index { index: WrappedObject, data: WrappedObject },\n}\n\n#[derive(Clone, Copy, Debug)]\npub struct FieldFlags(pub u8);\n\n#[derive(Clone, Copy, Debug)]\npub enum FieldAccessType {\n    Any,\n    Byte,\n    Word,\n    DWord,\n    QWord,\n    Buffer,\n}\n\n#[derive(Clone, Copy, Debug)]\npub enum FieldUpdateRule {\n    Preserve,\n    WriteAsOnes,\n    WriteAsZeros,\n}\n\nimpl FieldFlags {\n    pub fn access_type(&self) -> Result<FieldAccessType, AmlError> {\n        match self.0.get_bits(0..4) {\n            0 => Ok(FieldAccessType::Any),\n            1 => Ok(FieldAccessType::Byte),\n            2 => Ok(FieldAccessType::Word),\n            3 => Ok(FieldAccessType::DWord),\n            4 => Ok(FieldAccessType::QWord),\n            5 => Ok(FieldAccessType::Buffer),\n            _ => Err(AmlError::InvalidFieldFlags),\n        }\n    }\n\n    pub fn access_type_bytes(&self) -> Result<usize, AmlError> {\n        match self.access_type()? {\n            FieldAccessType::Any => {\n                // TODO: given more info about the field, we might be able to make a more efficient\n                // read, since all are valid in this case\n                Ok(1)\n            }\n            FieldAccessType::Byte | FieldAccessType::Buffer => Ok(1),\n            FieldAccessType::Word => Ok(2),\n            FieldAccessType::DWord => Ok(4),\n            FieldAccessType::QWord => Ok(8),\n        }\n    }\n\n    pub fn lock_rule(&self) -> bool {\n        self.0.get_bit(4)\n    }\n\n    pub fn update_rule(&self) -> FieldUpdateRule {\n        match self.0.get_bits(5..7) {\n            0 => FieldUpdateRule::Preserve,\n            1 => FieldUpdateRule::WriteAsOnes,\n            2 => FieldUpdateRule::WriteAsZeros,\n            _ => unreachable!(),\n        }\n    }\n}\n\n#[derive(Clone, Copy, Debug)]\npub struct MethodFlags(pub u8);\n\nimpl MethodFlags {\n    pub fn arg_count(&self) -> usize {\n        self.0.get_bits(0..3) as usize\n    }\n\n    pub fn serialize(&self) -> bool {\n        self.0.get_bit(3)\n    }\n\n    pub fn sync_level(&self) -> u8 {\n        self.0.get_bits(4..8)\n    }\n}\n\n#[derive(Clone, Copy, PartialEq, Debug)]\npub enum ReferenceKind {\n    Named,\n    RefOf,\n    Local,\n    Arg,\n    Index,\n    Unresolved,\n}\n\n#[derive(Clone, Copy, PartialEq, Debug)]\npub enum ObjectType {\n    Uninitialized,\n    Buffer,\n    BufferField,\n    Device,\n    Event,\n    FieldUnit,\n    Integer,\n    Method,\n    Mutex,\n    Reference,\n    OpRegion,\n    Package,\n    PowerResource,\n    Processor,\n    RawDataBuffer,\n    String,\n    ThermalZone,\n    Debug,\n}\n\n/// Helper type for decoding the result of `_STA` objects.\npub struct DeviceStatus(pub u64);\n\nimpl DeviceStatus {\n    pub fn present(&self) -> bool {\n        self.0.get_bit(0)\n    }\n\n    pub fn enabled(&self) -> bool {\n        self.0.get_bit(1)\n    }\n\n    pub fn show_in_ui(&self) -> bool {\n        self.0.get_bit(2)\n    }\n\n    pub fn functioning(&self) -> bool {\n        self.0.get_bit(3)\n    }\n\n    /// This flag is only used for Battery devices (PNP0C0A), and indicates if the battery is\n    /// present.\n    pub fn battery_present(&self) -> bool {\n        self.0.get_bit(4)\n    }\n}\n\n/// Copy an arbitrary bit range of `src` to an arbitrary bit range of `dst`. This is used for\n/// buffer fields. Data is zero-extended if `src` does not cover `length` bits, matching the\n/// expected behaviour for buffer fields.\npub(crate) fn copy_bits(\n    src: &[u8],\n    mut src_index: usize,\n    dst: &mut [u8],\n    mut dst_index: usize,\n    mut length: usize,\n) {\n    while length > 0 {\n        let src_shift = src_index & 7;\n        let mut src_bits = src.get(src_index / 8).unwrap_or(&0x00) >> src_shift;\n        if src_shift > 0 && length > (8 - src_shift) {\n            src_bits |= src.get(src_index / 8 + 1).unwrap_or(&0x00) << (8 - src_shift);\n        }\n\n        if length < 8 {\n            src_bits &= (1 << length) - 1;\n        }\n\n        let dst_shift = dst_index & 7;\n        let mut dst_mask: u16 = if length < 8 { ((1 << length) - 1) as u16 } else { 0xff_u16 } << dst_shift;\n        dst[dst_index / 8] =\n            (dst[dst_index / 8] & !(dst_mask as u8)) | ((src_bits << dst_shift) & (dst_mask as u8));\n\n        if dst_shift > 0 && length > (8 - dst_shift) {\n            dst_mask >>= 8;\n            dst[dst_index / 8 + 1] &= !(dst_mask as u8);\n            dst[dst_index / 8 + 1] |= (src_bits >> (8 - dst_shift)) & (dst_mask as u8);\n        }\n\n        if length < 8 {\n            length = 0;\n        } else {\n            length -= 8;\n            src_index += 8;\n            dst_index += 8;\n        }\n    }\n}\n\n#[inline]\npub(crate) fn align_down(value: usize, align: usize) -> usize {\n    assert!(align == 0 || align.is_power_of_two());\n\n    if align == 0 {\n        value\n    } else {\n        /*\n         * Alignment must be a power of two.\n         *\n         * E.g.\n         * align       =   0b00001000\n         * align-1     =   0b00000111\n         * !(align-1)  =   0b11111000\n         * ^^^ Masks the value to the one below it with the correct align\n         */\n        value & !(align - 1)\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    #[test]\n    fn test_copy_bits() {\n        let src = [0b1011_1111, 0b1111_0111, 0b1111_1111, 0b1111_1111, 0b1111_1111];\n        let mut dst = [0b1110_0001, 0, 0, 0, 0];\n\n        copy_bits(&src, 0, &mut dst, 2, 15);\n        assert_eq!(dst, [0b1111_1101, 0b1101_1110, 0b0000_0001, 0b0000_0000, 0b0000_0000]);\n    }\n}\n"
  },
  {
    "path": "src/aml/op_region.rs",
    "content": "use crate::aml::{AmlError, namespace::AmlName};\n\n#[derive(Clone, Debug)]\npub struct OpRegion {\n    pub space: RegionSpace,\n    pub base: u64,\n    pub length: u64,\n    pub parent_device_path: AmlName,\n}\n\npub trait RegionHandler {\n    fn read_u8(&self, region: &OpRegion) -> Result<u8, AmlError>;\n    fn read_u16(&self, region: &OpRegion) -> Result<u16, AmlError>;\n    fn read_u32(&self, region: &OpRegion) -> Result<u32, AmlError>;\n    fn read_u64(&self, region: &OpRegion) -> Result<u64, AmlError>;\n\n    fn write_u8(&self, region: &OpRegion, value: u8) -> Result<(), AmlError>;\n    fn write_u16(&self, region: &OpRegion, value: u16) -> Result<(), AmlError>;\n    fn write_u32(&self, region: &OpRegion, value: u32) -> Result<(), AmlError>;\n    fn write_u64(&self, region: &OpRegion, value: u64) -> Result<(), AmlError>;\n}\n\n#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)]\npub enum RegionSpace {\n    SystemMemory,\n    SystemIO,\n    PciConfig,\n    EmbeddedControl,\n    SmBus,\n    SystemCmos,\n    PciBarTarget,\n    Ipmi,\n    GeneralPurposeIo,\n    GenericSerialBus,\n    Pcc,\n    Oem(u8),\n}\n\nimpl From<u8> for RegionSpace {\n    fn from(value: u8) -> Self {\n        match value {\n            0 => RegionSpace::SystemMemory,\n            1 => RegionSpace::SystemIO,\n            2 => RegionSpace::PciConfig,\n            3 => RegionSpace::EmbeddedControl,\n            4 => RegionSpace::SmBus,\n            5 => RegionSpace::SystemCmos,\n            6 => RegionSpace::PciBarTarget,\n            7 => RegionSpace::Ipmi,\n            8 => RegionSpace::GeneralPurposeIo,\n            9 => RegionSpace::GenericSerialBus,\n            10 => RegionSpace::Pcc,\n            _ => RegionSpace::Oem(value),\n        }\n    }\n}\n"
  },
  {
    "path": "src/aml/pci_routing.rs",
    "content": "use crate::aml::{\n    AmlError,\n    Handler,\n    Interpreter,\n    Operation,\n    namespace::AmlName,\n    object::Object,\n    resource::{self, InterruptPolarity, InterruptTrigger, Resource},\n};\nuse alloc::{vec, vec::Vec};\nuse bit_field::BitField;\nuse core::str::FromStr;\n\npub use crate::aml::resource::IrqDescriptor;\n\n#[derive(Clone, Copy, PartialEq, Eq, Debug)]\npub enum Pin {\n    IntA,\n    IntB,\n    IntC,\n    IntD,\n}\n\n#[derive(Debug)]\npub enum PciRouteType {\n    /// The interrupt is hard-coded to a specific GSI\n    Gsi(u32),\n\n    /// The interrupt is linked to a link object. This object will have `_PRS`, `_CRS` fields and a `_SRS` method\n    /// that can be used to allocate the interrupt. Note that some platforms (e.g. QEMU's q35 chipset) use link\n    /// objects but do not support changing the interrupt that it's linked to (i.e. `_SRS` doesn't do anything).\n    /*\n     * The actual object itself will just be a `Device`, and we need paths to its children objects to do\n     * anything useful, so we just store the resolved name here.\n     */\n    LinkObject(AmlName),\n}\n\n#[derive(Debug)]\npub struct PciRoute {\n    device: u16,\n    function: u16,\n    pin: Pin,\n    route_type: PciRouteType,\n}\n\n/// A `PciRoutingTable` is used to interpret the data in a `_PRT` object, which provides a mapping\n/// from PCI interrupt pins to the inputs of the interrupt controller. One of these objects must be\n/// present under each PCI root bridge, and consists of a package of packages, each of which describes the\n/// mapping of a single PCI interrupt pin.\n#[derive(Debug)]\npub struct PciRoutingTable {\n    entries: Vec<PciRoute>,\n}\n\nimpl PciRoutingTable {\n    /// Construct a `PciRoutingTable` from a path to a `_PRT` object. Returns\n    /// `AmlError::InvalidOperationOnObject` if the value passed is not a package, or if any of the\n    /// values within it are not packages. Returns the various `AmlError::Prt*` errors if the\n    /// internal structure of the entries is invalid.\n    pub fn from_prt_path(\n        prt_path: AmlName,\n        interpreter: &Interpreter<impl Handler>,\n    ) -> Result<PciRoutingTable, AmlError> {\n        let mut entries = Vec::new();\n\n        let prt = interpreter.evaluate(prt_path.clone(), vec![])?;\n\n        if let Object::Package(ref inner_values) = *prt {\n            for value in inner_values {\n                if let Object::Package(ref pin_package) = **value {\n                    /*\n                     * Each inner package has the following structure:\n                     *   | Field      | Type      | Description                                               |\n                     *   | -----------|-----------|-----------------------------------------------------------|\n                     *   | Address    | Dword     | Address of the device. Same format as _ADR objects (high  |\n                     *   |            |           | word = #device, low word = #function)                     |\n                     *   | -----------|-----------|-----------------------------------------------------------|\n                     *   | Pin        | Byte      | The PCI pin (0 = INTA, 1 = INTB, 2 = INTC, 3 = INTD)      |\n                     *   | -----------|-----------|-----------------------------------------------------------|\n                     *   | Source     | Byte or   | Name of the device that allocates the interrupt to which  |\n                     *   |            | NamePath  | the above pin is connected. Can be fully qualified,       |\n                     *   |            |           | relative, or a simple NameSeg that utilizes namespace     |\n                     *   |            |           | search rules. Instead, if this is a byte value of 0, the  |\n                     *   |            |           | interrupt is allocated out of the GSI pool, and Source    |\n                     *   |            |           | Index should be utilised.                                 |\n                     *   | -----------|-----------|-----------------------------------------------------------|\n                     *   | Source     | Dword     | Index that indicates which resource descriptor in the     |\n                     *   | Index      |           | resource template of the device pointed to in the Source  |\n                     *   |            |           | field this interrupt is allocated from. If the Source     |\n                     *   |            |           | is zero, then this field is the GSI number to which the   |\n                     *   |            |           | pin is connected.                                         |\n                     *   | -----------|-----------|-----------------------------------------------------------|\n                     */\n                    let Object::Integer(address) = *pin_package[0] else {\n                        return Err(AmlError::PrtInvalidAddress);\n                    };\n                    let device = address.get_bits(16..32).try_into().map_err(|_| AmlError::PrtInvalidAddress)?;\n                    let function = address.get_bits(0..16).try_into().map_err(|_| AmlError::PrtInvalidAddress)?;\n                    let pin = match *pin_package[1] {\n                        Object::Integer(0) => Pin::IntA,\n                        Object::Integer(1) => Pin::IntB,\n                        Object::Integer(2) => Pin::IntC,\n                        Object::Integer(3) => Pin::IntD,\n                        _ => return Err(AmlError::PrtInvalidPin),\n                    };\n\n                    match *pin_package[2] {\n                        Object::Integer(0) => {\n                            /*\n                             * The Source Index field contains the GSI number that this interrupt is attached\n                             * to.\n                             */\n                            let Object::Integer(gsi) = *pin_package[3] else {\n                                return Err(AmlError::PrtInvalidGsi);\n                            };\n                            entries.push(PciRoute {\n                                device,\n                                function,\n                                pin,\n                                route_type: PciRouteType::Gsi(gsi as u32),\n                            });\n                        }\n                        Object::String(ref name) => {\n                            let link_object_name = interpreter\n                                .namespace\n                                .lock()\n                                .search_for_level(&AmlName::from_str(name)?, &prt_path)?;\n                            entries.push(PciRoute {\n                                device,\n                                function,\n                                pin,\n                                route_type: PciRouteType::LinkObject(link_object_name),\n                            });\n                        }\n                        _ => return Err(AmlError::PrtInvalidSource),\n                    }\n                } else {\n                    return Err(AmlError::InvalidOperationOnObject { op: Operation::DecodePrt, typ: value.typ() });\n                }\n            }\n\n            Ok(PciRoutingTable { entries })\n        } else {\n            Err(AmlError::InvalidOperationOnObject { op: Operation::DecodePrt, typ: prt.typ() })\n        }\n    }\n\n    /// Get the interrupt input that a given PCI interrupt pin is wired to. Returns `AmlError::PrtNoEntry` if the\n    /// PRT doesn't contain an entry for the given address + pin.\n    pub fn route(\n        &self,\n        device: u16,\n        function: u16,\n        pin: Pin,\n        interpreter: &Interpreter<impl Handler>,\n    ) -> Result<IrqDescriptor, AmlError> {\n        let entry = self\n            .entries\n            .iter()\n            .find(|entry| {\n                entry.device == device\n                    && (entry.function == 0xffff || entry.function == function)\n                    && entry.pin == pin\n            })\n            .ok_or(AmlError::PrtNoEntry)?;\n\n        match entry.route_type {\n            PciRouteType::Gsi(gsi) => Ok(IrqDescriptor {\n                is_consumer: true,\n                trigger: InterruptTrigger::Level,\n                polarity: InterruptPolarity::ActiveLow,\n                is_shared: true,\n                is_wake_capable: false,\n                irq: gsi,\n            }),\n            PciRouteType::LinkObject(ref name) => {\n                let path = AmlName::from_str(\"_CRS\").unwrap().resolve(name)?;\n                let link_crs = interpreter.evaluate(path, vec![])?;\n\n                let resources = resource::resource_descriptor_list(link_crs)?;\n                match resources.as_slice() {\n                    [Resource::Irq(descriptor)] => Ok(descriptor.clone()),\n                    _ => Err(AmlError::UnexpectedResourceType),\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/aml/resource.rs",
    "content": "use super::object::WrappedObject;\nuse crate::aml::{AmlError, Operation, object::Object};\nuse alloc::vec::Vec;\nuse bit_field::BitField;\nuse byteorder::{ByteOrder, LittleEndian};\nuse core::mem;\n\n#[derive(Debug, PartialEq, Eq)]\npub enum Resource {\n    Irq(IrqDescriptor),\n    AddressSpace(AddressSpaceDescriptor),\n    MemoryRange(MemoryRangeDescriptor),\n    IOPort(IOPortDescriptor),\n    Dma(DMADescriptor),\n}\n\n/// Parse a `ResourceDescriptor` buffer into a list of resources.\npub fn resource_descriptor_list(descriptor: WrappedObject) -> Result<Vec<Resource>, AmlError> {\n    if let Object::Buffer(ref bytes) = *descriptor {\n        let mut descriptors = Vec::new();\n        let mut bytes = bytes.as_slice();\n\n        while !bytes.is_empty() {\n            let (descriptor, remaining_bytes) = resource_descriptor(bytes)?;\n\n            if let Some(descriptor) = descriptor {\n                descriptors.push(descriptor);\n                bytes = remaining_bytes;\n            } else {\n                break;\n            }\n        }\n\n        Ok(descriptors)\n    } else {\n        Err(AmlError::InvalidOperationOnObject { op: Operation::ParseResource, typ: descriptor.typ() })\n    }\n}\n\nfn resource_descriptor(bytes: &[u8]) -> Result<(Option<Resource>, &[u8]), AmlError> {\n    /*\n     * If bit 7 of Byte 0 is set, it's a large descriptor. If not, it's a small descriptor.\n     */\n    if bytes[0].get_bit(7) {\n        /*\n         * We're parsing a large item. The descriptor type is encoded in Bits 0-6 of Byte 0. Valid types:\n         *      0x00: Reserved\n         *      0x01: 24-bit Memory Range Descriptor\n         *      0x02: Generic Register Descriptor\n         *      0x03: Reserved\n         *      0x04: Vendor-defined Descriptor\n         *      0x05: 32-bit Memory Range Descriptor\n         *      0x06: 32-bit Fixed Memory Range Descriptor\n         *      0x07: Address Space Resource Descriptor\n         *      0x08: Word Address Space Descriptor\n         *      0x09: Extended Interrupt Descriptor\n         *      0x0a: QWord Address Space Descriptor\n         *      0x0b: Extended Address Space Descriptor\n         *      0x0c: GPIO Connection Descriptor\n         *      0x0d: Pin Function Descriptor\n         *      0x0e: GenericSerialBus Connection Descriptor\n         *      0x0f: Pin Configuration Descriptor\n         *      0x10: Pin Group Descriptor\n         *      0x11: Pin Group Function Descriptor\n         *      0x12: Pin Group Configuration Descriptor\n         *      0x13-0x7f: Reserved\n         *\n         * Byte 1 contains bits 0-7 of the length, and Byte 2 contains bits 8-15 of the length. Subsequent\n         * bytes contain the actual data items.\n         */\n        let descriptor_type = bytes[0].get_bits(0..7);\n        let length = LittleEndian::read_u16(&bytes[1..=2]) as usize;\n        let (descriptor_bytes, remaining_bytes) = bytes.split_at(length + 3);\n\n        let descriptor = match descriptor_type {\n            0x01 => unimplemented!(\"24-bit Memory Range Descriptor\"),\n            0x02 => unimplemented!(\"Generic Register Descriptor\"),\n            0x03 => unimplemented!(\"0x03 Reserved\"),\n            0x04 => unimplemented!(\"Vendor-defined Descriptor\"),\n            0x05 => unimplemented!(\"32-bit Memory Range Descriptor\"),\n            0x06 => fixed_memory_descriptor(descriptor_bytes),\n            0x07 => address_space_descriptor::<u32>(descriptor_bytes),\n            0x08 => address_space_descriptor::<u16>(descriptor_bytes),\n            0x09 => extended_interrupt_descriptor(descriptor_bytes),\n            0x0a => address_space_descriptor::<u64>(descriptor_bytes),\n            0x0b => unimplemented!(\"Extended Address Space Descriptor\"),\n            0x0c => unimplemented!(\"GPIO Connection Descriptor\"),\n            0x0d => unimplemented!(\"Pin Function Descriptor\"),\n            0x0e => unimplemented!(\"GenericSerialBus Connection Descriptor\"),\n            0x0f => unimplemented!(\"Pin Configuration Descriptor\"),\n            0x10 => unimplemented!(\"Pin Group Descriptor\"),\n            0x11 => unimplemented!(\"Pin Group Function Descriptor\"),\n            0x12 => unimplemented!(\"Pin Group Configuration Descriptor\"),\n\n            0x00 | 0x13..=0x7f => Err(AmlError::InvalidResourceDescriptor),\n            0x80..=0xff => unreachable!(),\n        }?;\n\n        Ok((Some(descriptor), remaining_bytes))\n    } else {\n        /*\n         * We're parsing a small descriptor. Byte 0 has the format:\n         *    | Bits        | Field             |\n         *    |-------------|-------------------|\n         *    | 0-2         | Length - n bytes  |\n         *    | 3-6         | Small item type   |\n         *    | 7           | 0 = small item    |\n         *\n         * The valid types are:\n         *      0x00-0x03: Reserved\n         *      0x04: IRQ Format Descriptor\n         *      0x05: DMA Format Descriptor\n         *      0x06: Start Dependent Functions Descriptor\n         *      0x07: End Dependent Functions Descriptor\n         *      0x08: IO Port Descriptor\n         *      0x09: Fixed Location IO Port Descriptor\n         *      0x0A: Fixed DMA Descriptor\n         *      0x0B-0x0D: Reserved\n         *      0x0E: Vendor Defined Descriptor\n         *      0x0F: End Tag Descriptor\n         */\n        let descriptor_type = bytes[0].get_bits(3..=6);\n        let length: usize = bytes[0].get_bits(0..=2) as usize;\n        let (descriptor_bytes, remaining_bytes) = bytes.split_at(length + 1);\n\n        let descriptor = match descriptor_type {\n            0x00..=0x03 => Err(AmlError::InvalidResourceDescriptor),\n            0x04 => irq_format_descriptor(descriptor_bytes),\n            0x05 => dma_format_descriptor(descriptor_bytes),\n            0x06 => unimplemented!(\"Start Dependent Functions Descriptor\"),\n            0x07 => unimplemented!(\"End Dependent Functions Descriptor\"),\n            0x08 => io_port_descriptor(descriptor_bytes),\n            0x09 => unimplemented!(\"Fixed Location IO Port Descriptor\"),\n            0x0A => unimplemented!(\"Fixed DMA Descriptor\"),\n            0x0B..=0x0D => Err(AmlError::InvalidResourceDescriptor),\n            0x0E => unimplemented!(\"Vendor Defined Descriptor\"),\n            0x0F => return Ok((None, &[])),\n            0x10..=0xFF => unreachable!(),\n        }?;\n\n        Ok((Some(descriptor), remaining_bytes))\n    }\n}\n\n#[derive(Debug, PartialEq, Eq, Copy, Clone)]\npub enum InterruptTrigger {\n    Edge,\n    Level,\n}\n\n#[derive(Debug, PartialEq, Eq, Copy, Clone)]\npub enum InterruptPolarity {\n    ActiveHigh,\n    ActiveLow,\n}\n\n#[derive(Debug, PartialEq, Eq, Copy, Clone)]\npub enum AddressSpaceResourceType {\n    MemoryRange,\n    IORange,\n    BusNumberRange,\n}\n\n#[derive(Debug, PartialEq, Eq, Copy, Clone)]\npub enum AddressSpaceDecodeType {\n    Additive,\n    Subtractive,\n}\n\n#[derive(Debug, PartialEq, Eq)]\npub struct AddressSpaceDescriptor {\n    pub resource_type: AddressSpaceResourceType,\n    pub is_maximum_address_fixed: bool,\n    pub is_minimum_address_fixed: bool,\n    pub decode_type: AddressSpaceDecodeType,\n\n    pub granularity: u64,\n    pub address_range: (u64, u64),\n    pub translation_offset: u64,\n    pub length: u64,\n}\n\n#[derive(Debug, PartialEq, Eq)]\npub enum MemoryRangeDescriptor {\n    FixedLocation { is_writable: bool, base_address: u32, range_length: u32 },\n}\n\nfn fixed_memory_descriptor(bytes: &[u8]) -> Result<Resource, AmlError> {\n    /*\n     * -- 32-bit Fixed Memory Descriptor ---\n     * Offset     Field Name                              Definition\n     * Byte 0     32-bit Fixed Memory Range Descriptor    Value = 0x86 (10000110B) – Type = 1, Large item name = 0x06\n     * Byte 1     Length, bits [7:0]                      Value = 0x09 (9)\n     * Byte 2     Length, bits [15:8]                     Value = 0x00\n     * Byte 3     Information                             This field provides extra information about this memory.\n     *                                                    Bit [7:1]   Ignored\n     *                                                    Bit [0]     Write status, _RW\n     *                                                        1  writeable (read/write)\n     *                                                        0  non-writeable (read-only)\n     * 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.\n     * 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.\n     * 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.\n     * 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.\n     * 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.\n     * 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.\n     * 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.\n     * 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.\n     */\n    if bytes.len() != 12 {\n        return Err(AmlError::InvalidResourceDescriptor);\n    }\n\n    let information = bytes[3];\n    let is_writable = information.get_bit(0);\n\n    let base_address = LittleEndian::read_u32(&bytes[4..=7]);\n    let range_length = LittleEndian::read_u32(&bytes[8..=11]);\n\n    Ok(Resource::MemoryRange(MemoryRangeDescriptor::FixedLocation { is_writable, base_address, range_length }))\n}\n\nfn address_space_descriptor<T>(bytes: &[u8]) -> Result<Resource, AmlError> {\n    /*\n     * WORD Address Space Descriptor Definition\n     * Note: The definitions for DWORD and QWORD are the same other than the width of the address fields.\n     *\n     * Offset  Field Name                                   Definition\n     * Byte 0  WORD Address Space Descriptor                Value = 0x88 (10001000B) – Type = 1, Large item name = 0x08\n     * Byte 1  Length, bits [7:0]                           Variable length, minimum value = 0x0D (13)\n     * Byte 2  Length, bits [15:8]                          Variable length, minimum value = 0x00\n     * Byte 3  Resource Type                                Indicates which type of resource this descriptor describes. Defined values are:\n     *                                                        0         Memory range\n     *                                                        1         I/O range\n     *                                                        2         Bus number range\n     *                                                        3–191     Reserved\n     *                                                        192-255   Hardware Vendor Defined\n     * Byte 4  General Flags                                Flags that are common to all resource types:\n     *                                                        Bits [7:4]   Reserved (must be 0)\n     *                                                        Bit [3]     Max Address Fixed, _MAF:\n     *                                                          1  The specified maximum address is fixed\n     *                                                          0  The specified maximum address is not fixed\n     *                                                             and can be changed\n     *                                                        Bit [2]      Min Address Fixed,_MIF:\n     *                                                          1   The specified minimum address is fixed\n     *                                                          0   The specified minimum address is not fixed\n     *                                                              and can be changed\n     *                                                        Bit [1]      Decode Type, _DEC:\n     *                                                          1   This bridge subtractively decodes this address          (top level bridges only)\n     *                                                          0   This bridge positively decodes this address\n     *                                                        Bit [0]      Ignored\n     * 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).\n     * 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).\n     * Byte 7  Address space granularity, _GRA bits[15:8]\n     * 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.\n     * Byte 9  Address range minimum, _MIN, bits [15:8]\n     * 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.\n     * Byte 11 Address range maximum, _MAX, bits [15:8]\n     * 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.\n     * Byte 13 Address Translation offset, _TRA, bits [15:8]\n     * Byte 14 Address Length, _LEN, bits [7:0]\n     * Byte 15 Address Length, _LEN, bits [15:8]\n     * 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.\n     * 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.\n     */\n    let size = mem::size_of::<T>();\n\n    if bytes.len() < 6 + size * 5 {\n        return Err(AmlError::InvalidResourceDescriptor);\n    }\n\n    let resource_type = match bytes[3] {\n        0 => AddressSpaceResourceType::MemoryRange,\n        1 => AddressSpaceResourceType::IORange,\n        2 => AddressSpaceResourceType::BusNumberRange,\n        3..=191 => return Err(AmlError::InvalidResourceDescriptor),\n        192..=255 => unimplemented!(),\n    };\n\n    let general_flags = bytes[4];\n    let is_maximum_address_fixed = general_flags.get_bit(3);\n    let is_minimum_address_fixed = general_flags.get_bit(2);\n    let decode_type = if general_flags.get_bit(1) {\n        AddressSpaceDecodeType::Subtractive\n    } else {\n        AddressSpaceDecodeType::Additive\n    };\n\n    let mut address_fields = bytes[6..].chunks_exact(size);\n\n    // it's safe to unwrap because we check the length at the top\n    let granularity = LittleEndian::read_uint(address_fields.next().unwrap(), size);\n    let address_range_min = LittleEndian::read_uint(address_fields.next().unwrap(), size);\n    let address_range_max = LittleEndian::read_uint(address_fields.next().unwrap(), size);\n    let translation_offset = LittleEndian::read_uint(address_fields.next().unwrap(), size);\n    let length = LittleEndian::read_uint(address_fields.next().unwrap(), size);\n\n    Ok(Resource::AddressSpace(AddressSpaceDescriptor {\n        resource_type,\n        is_maximum_address_fixed,\n        is_minimum_address_fixed,\n        decode_type,\n        granularity,\n        address_range: (address_range_min, address_range_max),\n        translation_offset,\n        length,\n    }))\n}\n\n#[derive(Debug, PartialEq, Eq, Clone)]\npub struct IrqDescriptor {\n    pub is_consumer: bool,\n    pub trigger: InterruptTrigger,\n    pub polarity: InterruptPolarity,\n    pub is_shared: bool,\n    pub is_wake_capable: bool,\n    /*\n     * NOTE: We currently only support the cases where a descriptor only contains a single interrupt\n     * number.\n     */\n    pub irq: u32,\n}\n\nfn irq_format_descriptor(bytes: &[u8]) -> Result<Resource, AmlError> {\n    /*\n     * IRQ Descriptor Definition\n     *\n     * Offset   Field Name\n     * Byte 0   Value = 0x22 or 0x23 (0010001nB)– Type = 0, Small item name = 0x4, Length = 2 or 3\n     * Byte 1   IRQ mask bits[7:0], _INT\n     *          Bit [0] represents IRQ0, bit[1] is IRQ1, and so on.\n     * Byte 2   IRQ mask bits[15:8], _INT\n     *          Bit [0] represents IRQ8, bit[1] is IRQ9, and so on.\n     * Byte 3   IRQ Information. Each bit, when set, indicates this device is capable of driving a certain type of interrupt.\n     *          (Optional—if not included then assume edge sensitive, high true interrupts.)\n     *          These bits can be used both for reporting and setting IRQ resources.\n     *          Note: This descriptor is meant for describing interrupts that are connected to PIC-compatible interrupt\n     *                controllers, which can only be programmed for Active-High-Edge-Triggered or Active-Low-Level-Triggered\n     *                interrupts. Any other combination is invalid. The Extended Interrupt Descriptor can be used to describe\n     *                other combinations.\n     *            Bit [7:6]  Reserved (must be 0)\n     *            Bit [5]    Wake Capability, _WKC\n     *              0x0 = Not Wake Capable: This interrupt is not capable of waking the system.\n     *              0x1 = Wake Capable: This interrupt is capable of waking the system from a\n     *                    low-power idle state or a system sleep state.\n     *            Bit [4]    Interrupt Sharing, _SHR\n     *              0x0 = Exclusive: This interrupt is not shared with other devices.\n     *              0x1 = Shared: This interrupt is shared with other devices.\n     *            Bit [3]    Interrupt Polarity, _LL\n     *              0  Active-High – This interrupt is sampled when the signal is high, or true\n     *              1  Active-Low – This interrupt is sampled when the signal is low, or false.\n     *            Bit [2:1]  Ignored\n     *            Bit [0]    Interrupt Mode, _HE\n     *              0  Level-Triggered – Interrupt is triggered in response to signal in a low state.\n     *              1  Edge-Triggered – Interrupt is triggered in response to a change in signal state from low to high.\n     */\n\n    match bytes.len() {\n        0..=2 => Err(AmlError::InvalidResourceDescriptor),\n        3 => {\n            // no IRQ information (\"length 2\" in spec)\n            let irq = LittleEndian::read_u16(&bytes[1..=2]);\n\n            Ok(Resource::Irq(IrqDescriptor {\n                irq: irq as u32,\n                is_wake_capable: false,\n                is_shared: false,\n                polarity: InterruptPolarity::ActiveHigh,\n                trigger: InterruptTrigger::Edge,\n\n                is_consumer: false, // assumed to be producer\n            }))\n        }\n        4 => {\n            // with IRQ information (\"length 3\" in spec)\n            let irq = LittleEndian::read_u16(&bytes[1..=2]);\n\n            let information = bytes[3];\n            let is_wake_capable = information.get_bit(5);\n            let is_shared = information.get_bit(4);\n            let polarity = match information.get_bit(3) {\n                false => InterruptPolarity::ActiveHigh,\n                true => InterruptPolarity::ActiveLow,\n            };\n            let trigger = match information.get_bit(0) {\n                false => InterruptTrigger::Level,\n                true => InterruptTrigger::Edge,\n            };\n\n            Ok(Resource::Irq(IrqDescriptor {\n                irq: irq as u32,\n                is_wake_capable,\n                is_shared,\n                polarity,\n                trigger,\n\n                is_consumer: false, // assumed to be producer\n            }))\n        }\n        _ => Err(AmlError::InvalidResourceDescriptor),\n    }\n}\n\n#[derive(Debug, PartialEq, Eq)]\npub enum DMASupportedSpeed {\n    CompatibilityMode,\n    TypeA, // as described by the EISA\n    TypeB,\n    TypeF,\n}\n\n#[derive(Debug, PartialEq, Eq)]\npub enum DMATransferTypePreference {\n    _8BitOnly,\n    _8And16Bit,\n    _16Bit,\n}\n\n#[derive(Debug, PartialEq, Eq)]\npub struct DMADescriptor {\n    pub channel_mask: u8,\n    pub supported_speeds: DMASupportedSpeed,\n    pub is_bus_master: bool,\n    pub transfer_type_preference: DMATransferTypePreference,\n}\n\npub fn dma_format_descriptor(bytes: &[u8]) -> Result<Resource, AmlError> {\n    /*\n     * DMA Descriptor Definition\n     * Offset  Field Name\n     * Byte 0  Value = 0x2A (00101010B) – Type = 0, Small item name = 0x5, Length = 2\n     * Byte 1  DMA channel mask bits [7:0] (channels 0 – 7), _DMA\n     *         Bit [0] is channel 0, etc.\n     * Byte 2  Bit [7]           Reserved (must be 0)\n     *         Bits [6:5]        DMA channel speed supported, _TYP\n     *           00    Indicates compatibility mode\n     *           01    Indicates Type A DMA as described in the EISA\n     *           10    Indicates Type B DMA\n     *           11    Indicates Type F\n     *         Bits [4:3]        Ignored\n     *         Bit [2]           Logical device bus master status, _BM\n     *           0      Logical device is not a bus master\n     *           1      Logical device is a bus master\n     *         Bits [1:0]       DMA transfer type preference, _SIZ\n     *           00    8-bit only\n     *           01    8- and 16-bit\n     *           10    16-bit only\n     *           11    Reserved\n     */\n    if bytes.len() != 3 {\n        return Err(AmlError::InvalidResourceDescriptor);\n    }\n\n    let channel_mask = bytes[1];\n    let options = bytes[2];\n    let supported_speeds = match options.get_bits(5..=6) {\n        0 => DMASupportedSpeed::CompatibilityMode,\n        1 => DMASupportedSpeed::TypeA,\n        2 => DMASupportedSpeed::TypeB,\n        3 => DMASupportedSpeed::TypeF,\n        _ => unreachable!(),\n    };\n    let is_bus_master = options.get_bit(2);\n    let transfer_type_preference = match options.get_bits(0..=1) {\n        0 => DMATransferTypePreference::_8BitOnly,\n        1 => DMATransferTypePreference::_8And16Bit,\n        2 => DMATransferTypePreference::_16Bit,\n        3 => unimplemented!(\"Reserved DMA transfer type preference\"),\n        _ => unreachable!(),\n    };\n\n    Ok(Resource::Dma(DMADescriptor { channel_mask, supported_speeds, is_bus_master, transfer_type_preference }))\n}\n\n#[derive(Debug, PartialEq, Eq)]\npub struct IOPortDescriptor {\n    pub decodes_full_address: bool,\n    pub memory_range: (u16, u16),\n    pub base_alignment: u8,\n    pub range_length: u8,\n}\n\nfn io_port_descriptor(bytes: &[u8]) -> Result<Resource, AmlError> {\n    /*\n     * I/O Port Descriptor Definition\n     * Offset   Field Name                                  Definition\n     * Byte 0   I/O Port Descriptor                         Value = 0x47 (01000111B) –\n     *                                                      Type = 0, Small item name = 0x8, Length = 7\n     * Byte 1   Information                                 Bits [7:1]     Reserved and must be 0\n     *                                                      Bit [0]          (_DEC)\n     *                                                        1    The logical device decodes 16-bit addresses\n     *                                                        0    The logical device only decodes address bits[9:0]\n     * 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.\n     * 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.\n     * 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.\n     * 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.\n     * Byte 6   Base alignment, _ALN                        Alignment for minimum base address, increment in 1-byte blocks.\n     * Byte 7   Range length, _LEN                          The number of contiguous I/O ports requested.\n     */\n    if bytes.len() != 8 {\n        return Err(AmlError::InvalidResourceDescriptor);\n    }\n\n    let information = bytes[1];\n    let decodes_full_address = information.get_bit(0);\n\n    let memory_range_min = LittleEndian::read_u16(&bytes[2..=3]);\n    let memory_range_max = LittleEndian::read_u16(&bytes[4..=5]);\n    let memory_range = (memory_range_min, memory_range_max);\n\n    let base_alignment = bytes[6];\n    let range_length = bytes[7];\n\n    Ok(Resource::IOPort(IOPortDescriptor { decodes_full_address, memory_range, base_alignment, range_length }))\n}\n\nfn extended_interrupt_descriptor(bytes: &[u8]) -> Result<Resource, AmlError> {\n    /*\n     * --- Extended Interrupt Descriptor ---\n     * Byte 3 contains the Interrupt Vector Flags:\n     *      Bit 0: 1 if device consumes the resource, 0 if it produces it\n     *      Bit 1: 1 if edge-triggered, 0 if level-triggered\n     *      Bit 2: 1 = active-high, 0 = active-low\n     *      Bit 3: 1 if interrupt is shared with other devices\n     *      Bit 4: 1 if this interrupt is capable of waking the system, 0 if it is not\n     * Byte 4 contains the number of interrupt numbers that follow. When this descriptor is\n     * returned from `_CRS` or send to `_SRS`, this field must be 1.\n     *\n     * From Byte 5 onwards, there are `n` interrupt numbers, each of which is encoded as a\n     * 4-byte little-endian number.\n     *\n     * NOTE: We only support the case where there is a single interrupt number.\n     */\n    if bytes.len() < 9 {\n        return Err(AmlError::InvalidResourceDescriptor);\n    }\n\n    let number_of_interrupts = bytes[4] as usize;\n    assert_eq!(number_of_interrupts, 1);\n    let irq = LittleEndian::read_u32(&[bytes[5], bytes[6], bytes[7], bytes[8]]);\n\n    Ok(Resource::Irq(IrqDescriptor {\n        is_consumer: bytes[3].get_bit(0),\n        trigger: if bytes[3].get_bit(1) { InterruptTrigger::Edge } else { InterruptTrigger::Level },\n        polarity: if bytes[3].get_bit(2) { InterruptPolarity::ActiveLow } else { InterruptPolarity::ActiveHigh },\n        is_shared: bytes[3].get_bit(3),\n        is_wake_capable: bytes[3].get_bit(4),\n        irq,\n    }))\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n    use alloc::sync::Arc;\n\n    #[test]\n    fn test_parses_keyboard_crs() {\n        let bytes: Vec<u8> = [\n            // Generated from `iasl -l pc-bios_acpi-dsdt.asl`\n            //\n            //         315:                   IO (Decode16,\n            //         316:                       0x0060,             // Range Minimum\n            //         317:                       0x0060,             // Range Maximum\n            //         318:                       0x01,               // Alignment\n            //         319:                       0x01,               // Length\n            //         320:                       )\n\n            //    0000040A:  47 01 60 00 60 00 01 01     \"G.`.`...\"\n            0x47, 0x01, 0x60, 0x00, 0x60, 0x00, 0x01, 0x01,\n            //         321:                   IO (Decode16,\n            //         322:                       0x0064,             // Range Minimum\n            //         323:                       0x0064,             // Range Maximum\n            //         324:                       0x01,               // Alignment\n            //         325:                       0x01,               // Length\n            //         326:                       )\n\n            //    00000412:  47 01 64 00 64 00 01 01     \"G.d.d...\"\n            0x47, 0x01, 0x64, 0x00, 0x64, 0x00, 0x01, 0x01,\n            //         327:                   IRQNoFlags ()\n            //         328:                       {1}\n\n            //    0000041A:  22 02 00 ...............    \"\"..\"\n            0x22, 0x02, 0x00, //    0000041D:  79 00 ..................    \"y.\"\n            0x79, 0x00,\n        ]\n        .to_vec();\n\n        let value = Object::Buffer(bytes).wrap();\n        let resources = resource_descriptor_list(value).unwrap();\n\n        assert_eq!(\n            resources,\n            Vec::from([\n                Resource::IOPort(IOPortDescriptor {\n                    decodes_full_address: true,\n                    memory_range: (0x60, 0x60),\n                    base_alignment: 1,\n                    range_length: 1\n                }),\n                Resource::IOPort(IOPortDescriptor {\n                    decodes_full_address: true,\n                    memory_range: (0x64, 0x64),\n                    base_alignment: 1,\n                    range_length: 1\n                }),\n                Resource::Irq(IrqDescriptor {\n                    is_consumer: false,\n                    trigger: InterruptTrigger::Edge,\n                    polarity: InterruptPolarity::ActiveHigh,\n                    is_shared: false,\n                    is_wake_capable: false,\n                    irq: (1 << 1)\n                })\n            ])\n        );\n    }\n\n    #[test]\n    fn test_pci_crs() {\n        let bytes: Vec<u8> = [\n            // Generated from `iasl -l pc-bios_acpi-dsdt.asl`\n            //\n            //      98:               WordBusNumber (ResourceProducer, MinFixed, MaxFixed, PosDecode,\n            //      99:                   0x0000,             // Granularity\n            //     100:                   0x0000,             // Range Minimum\n            //     101:                   0x00FF,             // Range Maximum\n            //     102:                   0x0000,             // Translation Offset\n            //     103:                   0x0100,             // Length\n            //     104:                   ,, )\n\n            // 000000F3:  88 0D 00 02 0C 00 00 00     \"........\"\n            // 000000FB:  00 00 FF 00 00 00 00 01     \"........\"\n            0x88, 0x0D, 0x00, 0x02, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x01,\n            //     105:               IO (Decode16,\n            //     106:                   0x0CF8,             // Range Minimum\n            //     107:                   0x0CF8,             // Range Maximum\n            //     108:                   0x01,               // Alignment\n            //     109:                   0x08,               // Length\n            //     110:                   )\n\n            // 00000103:  47 01 F8 0C F8 0C 01 08     \"G.......\"\n            0x47, 0x01, 0xF8, 0x0C, 0xF8, 0x0C, 0x01, 0x08,\n            //     111:               WordIO (ResourceProducer, MinFixed, MaxFixed, PosDecode, EntireRange,\n            //     112:                   0x0000,             // Granularity\n            //     113:                   0x0000,             // Range Minimum\n            //     114:                   0x0CF7,             // Range Maximum\n            //     115:                   0x0000,             // Translation Offset\n            //     116:                   0x0CF8,             // Length\n            //     117:                   ,, , TypeStatic, DenseTranslation)\n\n            // 0000010B:  88 0D 00 01 0C 03 00 00     \"........\"\n            // 00000113:  00 00 F7 0C 00 00 F8 0C     \"........\"\n            0x88, 0x0D, 0x00, 0x01, 0x0C, 0x03, 0x00, 0x00, 0x00, 0x00, 0xF7, 0x0C, 0x00, 0x00, 0xF8, 0x0C,\n            //     118:               WordIO (ResourceProducer, MinFixed, MaxFixed, PosDecode, EntireRange,\n            //     119:                   0x0000,             // Granularity\n            //     120:                   0x0D00,             // Range Minimum\n            //     121:                   0xFFFF,             // Range Maximum\n            //     122:                   0x0000,             // Translation Offset\n            //     123:                   0xF300,             // Length\n            //     124:                   ,, , TypeStatic, DenseTranslation)\n\n            // 0000011B:  88 0D 00 01 0C 03 00 00     \"........\"\n            // 00000123:  00 0D FF FF 00 00 00 F3     \"........\"\n            0x88, 0x0D, 0x00, 0x01, 0x0C, 0x03, 0x00, 0x00, 0x00, 0x0D, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0xF3,\n            //     125:               DWordMemory (ResourceProducer, PosDecode, MinFixed, MaxFixed, Cacheable, ReadWrite,\n            //     126:                   0x00000000,         // Granularity\n            //     127:                   0x000A0000,         // Range Minimum\n            //     128:                   0x000BFFFF,         // Range Maximum\n            //     129:                   0x00000000,         // Translation Offset\n            //     130:                   0x00020000,         // Length\n            //     131:                   ,, , AddressRangeMemory, TypeStatic)\n\n            // 0000012B:  87 17 00 00 0C 03 00 00     \"........\"\n            // 00000133:  00 00 00 00 0A 00 FF FF     \"........\"\n            // 0000013B:  0B 00 00 00 00 00 00 00     \"........\"\n            // 00000143:  02 00 ..................    \"..\"\n            0x87, 0x17, 0x00, 0x00, 0x0C, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0A, 0x00, 0xFF, 0xFF, 0x0B,\n            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00,\n            //     132:               DWordMemory (ResourceProducer, PosDecode, MinFixed, MaxFixed, NonCacheable, ReadWrite,\n            //     133:                   0x00000000,         // Granularity\n            //     134:                   0xE0000000,         // Range Minimum\n            //     135:                   0xFEBFFFFF,         // Range Maximum\n            //     136:                   0x00000000,         // Translation Offset\n            //     137:                   0x1EC00000,         // Length\n            //     138:                   ,, _Y00, AddressRangeMemory, TypeStatic)\n\n            // 00000145:  87 17 00 00 0C 01 00 00     \"........\"\n            // 0000014D:  00 00 00 00 00 E0 FF FF     \"........\"\n            // 00000155:  BF FE 00 00 00 00 00 00     \"........\"\n            // 0000015D:  C0 1E ..................    \"..\"\n            0x87, 0x17, 0x00, 0x00, 0x0C, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0xFF, 0xFF, 0xBF,\n            0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x1E,\n            // 0000015F:  79 00 ..................    \"y.\"\n            0x79, 0x00,\n        ]\n        .to_vec();\n\n        let value = Object::Buffer(bytes).wrap();\n        let resources = resource_descriptor_list(value).unwrap();\n\n        assert_eq!(\n            resources,\n            Vec::from([\n                Resource::AddressSpace(AddressSpaceDescriptor {\n                    resource_type: AddressSpaceResourceType::BusNumberRange,\n                    is_maximum_address_fixed: true,\n                    is_minimum_address_fixed: true,\n                    decode_type: AddressSpaceDecodeType::Additive,\n                    granularity: 0,\n                    address_range: (0x00, 0xFF),\n                    translation_offset: 0,\n                    length: 0x100\n                }),\n                Resource::IOPort(IOPortDescriptor {\n                    decodes_full_address: true,\n                    memory_range: (0xCF8, 0xCF8),\n                    base_alignment: 1,\n                    range_length: 8\n                }),\n                Resource::AddressSpace(AddressSpaceDescriptor {\n                    resource_type: AddressSpaceResourceType::IORange,\n                    is_maximum_address_fixed: true,\n                    is_minimum_address_fixed: true,\n                    decode_type: AddressSpaceDecodeType::Additive,\n                    granularity: 0,\n                    address_range: (0x0000, 0x0CF7),\n                    translation_offset: 0,\n                    length: 0xCF8\n                }),\n                Resource::AddressSpace(AddressSpaceDescriptor {\n                    resource_type: AddressSpaceResourceType::IORange,\n                    is_maximum_address_fixed: true,\n                    is_minimum_address_fixed: true,\n                    decode_type: AddressSpaceDecodeType::Additive,\n                    granularity: 0,\n                    address_range: (0x0D00, 0xFFFF),\n                    translation_offset: 0,\n                    length: 0xF300\n                }),\n                Resource::AddressSpace(AddressSpaceDescriptor {\n                    resource_type: AddressSpaceResourceType::MemoryRange,\n                    is_maximum_address_fixed: true,\n                    is_minimum_address_fixed: true,\n                    decode_type: AddressSpaceDecodeType::Additive,\n                    granularity: 0,\n                    address_range: (0xA0000, 0xBFFFF),\n                    translation_offset: 0,\n                    length: 0x20000\n                }),\n                Resource::AddressSpace(AddressSpaceDescriptor {\n                    resource_type: AddressSpaceResourceType::MemoryRange,\n                    is_maximum_address_fixed: true,\n                    is_minimum_address_fixed: true,\n                    decode_type: AddressSpaceDecodeType::Additive,\n                    granularity: 0,\n                    address_range: (0xE0000000, 0xFEBFFFFF),\n                    translation_offset: 0,\n                    length: 0x1EC00000\n                }),\n            ])\n        );\n    }\n\n    #[test]\n    fn test_fdc_crs() {\n        let bytes: Vec<u8> = [\n            //         365:                   IO (Decode16,\n            //         366:                       0x03F2,             // Range Minimum\n            //         367:                       0x03F2,             // Range Maximum\n            //         368:                       0x00,               // Alignment\n            //         369:                       0x04,               // Length\n            //         370:                       )\n\n            //    0000047C:  47 01 F2 03 F2 03 00 04     \"G.......\"\n            0x47, 0x01, 0xF2, 0x03, 0xF2, 0x03, 0x00, 0x04,\n            //         371:                   IO (Decode16,\n            //         372:                       0x03F7,             // Range Minimum\n            //         373:                       0x03F7,             // Range Maximum\n            //         374:                       0x00,               // Alignment\n            //         375:                       0x01,               // Length\n            //         376:                       )\n\n            //    00000484:  47 01 F7 03 F7 03 00 01     \"G.......\"\n            0x47, 0x01, 0xF7, 0x03, 0xF7, 0x03, 0x00, 0x01,\n            //         377:                   IRQNoFlags ()\n            //         378:                       {6}\n\n            //    0000048C:  22 40 00 ...............    \"\"@.\"\n            0x22, 0x40, 0x00,\n            //         379:                   DMA (Compatibility, NotBusMaster, Transfer8, )\n            //         380:                       {2}\n\n            //    0000048F:  2A 04 00 ...............    \"*..\"\n            0x2A, 0x04, 0x00, //    00000492:  79 00 ..................    \"y.\"\n            0x79, 0x00,\n        ]\n        .to_vec();\n\n        let value = Object::Buffer(bytes).wrap();\n        let resources = resource_descriptor_list(value).unwrap();\n\n        assert_eq!(\n            resources,\n            Vec::from([\n                Resource::IOPort(IOPortDescriptor {\n                    decodes_full_address: true,\n                    memory_range: (0x03F2, 0x03F2),\n                    base_alignment: 0,\n                    range_length: 4\n                }),\n                Resource::IOPort(IOPortDescriptor {\n                    decodes_full_address: true,\n                    memory_range: (0x03F7, 0x03F7),\n                    base_alignment: 0,\n                    range_length: 1\n                }),\n                Resource::Irq(IrqDescriptor {\n                    is_consumer: false,\n                    trigger: InterruptTrigger::Edge,\n                    polarity: InterruptPolarity::ActiveHigh,\n                    is_shared: false,\n                    is_wake_capable: false,\n                    irq: (1 << 6)\n                }),\n                Resource::Dma(DMADescriptor {\n                    channel_mask: 1 << 2,\n                    supported_speeds: DMASupportedSpeed::CompatibilityMode,\n                    is_bus_master: false,\n                    transfer_type_preference: DMATransferTypePreference::_8BitOnly\n                })\n            ])\n        );\n    }\n}\n"
  },
  {
    "path": "src/lib.rs",
    "content": "//! `acpi` is a Rust library for interacting with the Advanced Configuration and Power Interface, a\n//! complex framework for power management and device discovery and configuration. ACPI is used on\n//! modern x64, as well as some ARM and RISC-V platforms. An operating system needs to interact with\n//! ACPI to correctly set up a platform's interrupt controllers, perform power management, and fully\n//! support many other platform capabilities.\n//!\n//! This crate provides a limited API that can be used without an allocator, for example for use\n//! from a bootloader. This API will allow you to search for the RSDP, enumerate over the available\n//! tables, and interact with the tables using their raw structures. All other functionality is\n//! behind an `alloc` feature (enabled by default) and requires an allocator.\n//!\n//! With an allocator, this crate also provides higher-level interfaces to the static tables, as\n//! well as a dynamic interpreter for AML - the bytecode format encoded in the DSDT and SSDT\n//! tables.\n//!\n//! ### Usage\n//! To use the library, you will need to provide an implementation of the [`Handler`] trait,\n//! which allows the library to make requests such as mapping a particular region of physical\n//! memory into the virtual address space.\n//!\n//! Next, you'll need to get the physical address of either the RSDP, or the RSDT/XSDT. The method\n//! for doing this depends on the platform you're running on and how you were booted. If you know\n//! the system was booted via the BIOS, you can use [`Rsdp::search_for_on_bios`]. UEFI provides a\n//! separate mechanism for getting the address of the RSDP.\n//!\n//! You then need to construct an instance of [`AcpiTables`], which can be done in a few ways\n//! depending on how much information you have:\n//! * Use [`AcpiTables::from_rsdp`] if you have the physical address of the RSDP\n//! * Use [`AcpiTables::from_rsdt`] if you have the physical address of the RSDT/XSDT\n//!\n//! Once you have an [`AcpiTables`], you can search for relevant tables, or use the higher-level\n//! interfaces, such as [`PowerProfile`], or [`HpetInfo`].\n//!\n//! If you have the `aml` feature enabled then you can construct an\n//! [AML interpreter](`crate::aml::Interpreter`) by first constructing an\n//! [`AcpiPlatform`](platform::AcpiPlatform) and then the interpreter:\n//!\n//! ```ignore,rust\n//! let mut acpi_handler = YourHandler::new(/*args*/);\n//! let acpi_tables = unsafe { AcpiTables::from_rsdp(acpi_handler.clone(), rsdp_addr) }.unwrap();\n//! let platform = AcpiPlatform::new(acpi_tables, acpi_handler).unwrap();\n//! let interpreter = Interpreter::new_from_platform(&platform).unwrap();\n//! ```\n\n#![no_std]\n#![feature(allocator_api)]\n\n#[cfg_attr(test, macro_use)]\n#[cfg(test)]\nextern crate std;\n\n#[cfg(feature = \"alloc\")]\nextern crate alloc;\n\npub mod address;\n#[cfg(feature = \"aml\")]\npub mod aml;\n#[cfg(feature = \"alloc\")]\npub mod platform;\npub mod registers;\npub mod rsdp;\npub mod sdt;\n\npub use pci_types::PciAddress;\npub use sdt::{fadt::PowerProfile, hpet::HpetInfo, madt::MadtError};\n\nuse crate::sdt::{SdtHeader, Signature};\nuse core::{\n    fmt,\n    mem,\n    ops::{Deref, DerefMut},\n    pin::Pin,\n    ptr::NonNull,\n};\nuse log::{error, warn};\nuse rsdp::Rsdp;\n\n/// `AcpiTables` should be constructed after finding the RSDP or RSDT/XSDT and allows enumeration\n/// of the system's ACPI tables.\npub struct AcpiTables<H: Handler> {\n    rsdt_mapping: PhysicalMapping<H, SdtHeader>,\n    pub rsdp_revision: u8,\n    handler: H,\n}\n\nunsafe impl<H> Send for AcpiTables<H> where H: Handler + Send {}\nunsafe impl<H> Sync for AcpiTables<H> where H: Handler + Send {}\n\nimpl<H> AcpiTables<H>\nwhere\n    H: Handler,\n{\n    /// Construct an `AcpiTables` from the **physical** address of the RSDP.\n    ///\n    /// # Safety\n    /// The address of the RSDP must be valid.\n    pub unsafe fn from_rsdp(handler: H, rsdp_address: usize) -> Result<AcpiTables<H>, AcpiError> {\n        let rsdp_mapping = unsafe { handler.map_physical_region::<Rsdp>(rsdp_address, mem::size_of::<Rsdp>()) };\n\n        /*\n         * If the address given does not have a correct RSDP signature, the user has probably given\n         * us an invalid address, and we should not continue. We're more lenient with other errors\n         * as it's probably a real RSDP and the firmware developers are just lazy.\n         */\n        match rsdp_mapping.validate() {\n            Ok(()) => (),\n            Err(AcpiError::RsdpIncorrectSignature) => return Err(AcpiError::RsdpIncorrectSignature),\n            Err(AcpiError::RsdpInvalidOemId) | Err(AcpiError::RsdpInvalidChecksum) => {\n                warn!(\"RSDP has invalid checksum or OEM ID. Continuing.\");\n            }\n            Err(_) => (),\n        }\n\n        let rsdp_revision = rsdp_mapping.revision();\n        let rsdt_address = if rsdp_revision == 0 {\n            // We're running on ACPI Version 1.0. We should use the 32-bit RSDT address.\n            rsdp_mapping.rsdt_address() as usize\n        } else {\n            /*\n             * We're running on ACPI Version 2.0+. We should use the 64-bit XSDT address, truncated\n             * to 32 bits on x86.\n             */\n            rsdp_mapping.xsdt_address() as usize\n        };\n\n        unsafe { Self::from_rsdt(handler, rsdp_revision, rsdt_address) }\n    }\n\n    /// Construct an `AcpiTables` from the **physical** address of the RSDT/XSDT, and the revision\n    /// found in the RSDP.\n    ///\n    /// # Safety\n    /// The address of the RSDT must be valid.\n    pub unsafe fn from_rsdt(\n        handler: H,\n        rsdp_revision: u8,\n        rsdt_address: usize,\n    ) -> Result<AcpiTables<H>, AcpiError> {\n        let rsdt_mapping =\n            unsafe { handler.map_physical_region::<SdtHeader>(rsdt_address, mem::size_of::<SdtHeader>()) };\n        let rsdt_length = rsdt_mapping.length;\n        let rsdt_mapping = unsafe { handler.map_physical_region::<SdtHeader>(rsdt_address, rsdt_length as usize) };\n        Ok(Self { rsdt_mapping, rsdp_revision, handler })\n    }\n\n    /// Iterate over the **physical** addresses of the SDTs.\n    pub fn table_entries(&self) -> impl Iterator<Item = usize> {\n        let entry_size = if self.rsdp_revision == 0 { 4 } else { 8 };\n        let mut table_entries_ptr =\n            unsafe { self.rsdt_mapping.virtual_start.as_ptr().byte_add(mem::size_of::<SdtHeader>()) }.cast::<u8>();\n        let mut num_entries = (self.rsdt_mapping.region_length - mem::size_of::<SdtHeader>()) / entry_size;\n\n        core::iter::from_fn(move || {\n            if num_entries > 0 {\n                unsafe {\n                    let entry = if entry_size == 4 {\n                        *table_entries_ptr.cast::<u32>() as usize\n                    } else {\n                        *table_entries_ptr.cast::<u64>() as usize\n                    };\n                    table_entries_ptr = table_entries_ptr.byte_add(entry_size);\n                    num_entries -= 1;\n\n                    Some(entry)\n                }\n            } else {\n                None\n            }\n        })\n    }\n\n    /// Iterate over the headers of each SDT, along with their **physical** addresses.\n    pub fn table_headers(&self) -> impl Iterator<Item = (usize, SdtHeader)> {\n        self.table_entries().map(|table_phys_address| {\n            let mapping = unsafe {\n                self.handler.map_physical_region::<SdtHeader>(table_phys_address, mem::size_of::<SdtHeader>())\n            };\n            (table_phys_address, *mapping)\n        })\n    }\n\n    /// Find all tables with the signature `T::SIGNATURE`.\n    pub fn find_tables<T>(&self) -> impl Iterator<Item = PhysicalMapping<H, T>>\n    where\n        T: AcpiTable,\n    {\n        self.table_entries().filter_map(|table_phys_address| {\n            let header_mapping = unsafe {\n                self.handler.map_physical_region::<SdtHeader>(table_phys_address, mem::size_of::<SdtHeader>())\n            };\n            if header_mapping.signature == T::SIGNATURE {\n                // Extend the mapping to the entire table\n                let length = header_mapping.length;\n                drop(header_mapping);\n                Some(unsafe { self.handler.map_physical_region::<T>(table_phys_address, length as usize) })\n            } else {\n                None\n            }\n        })\n    }\n\n    /// Find the first table with the signature `T::SIGNATURE`.\n    pub fn find_table<T>(&self) -> Option<PhysicalMapping<H, T>>\n    where\n        T: AcpiTable,\n    {\n        self.find_tables().next()\n    }\n\n    pub fn dsdt(&self) -> Result<AmlTable, AcpiError> {\n        let Some(fadt) = self.find_table::<sdt::fadt::Fadt>() else {\n            Err(AcpiError::TableNotFound(Signature::FADT))?\n        };\n        let phys_address = fadt.dsdt_address()?;\n        let header =\n            unsafe { self.handler.map_physical_region::<SdtHeader>(phys_address, mem::size_of::<SdtHeader>()) };\n        Ok(AmlTable { phys_address, length: header.length, revision: header.revision })\n    }\n\n    pub fn ssdts(&self) -> impl Iterator<Item = AmlTable> {\n        self.table_headers().filter_map(|(phys_address, header)| {\n            if header.signature == Signature::SSDT {\n                Some(AmlTable { phys_address, length: header.length, revision: header.revision })\n            } else {\n                None\n            }\n        })\n    }\n}\n\n#[derive(Clone, Copy, Debug)]\npub struct AmlTable {\n    /// The physical address of the start of the table. Add `mem::size_of::<SdtHeader>()` to this\n    /// to get the physical address of the start of the AML stream.\n    pub phys_address: usize,\n    /// The length of the table, including the header.\n    pub length: u32,\n    pub revision: u8,\n}\n\n/// All types representing ACPI tables should implement this trait.\n///\n/// ### Safety\n/// The table's memory is naively interpreted, so you must be careful in providing a type that\n/// correctly represents the table's structure. Regardless of the provided type's size, the region mapped will\n/// be the size specified in the SDT's header. If a table's definition may be larger than a valid\n/// SDT's size, [`ExtendedField`](sdt::ExtendedField) should be used to define fields that may or\n/// may not exist.\npub unsafe trait AcpiTable {\n    const SIGNATURE: Signature;\n\n    fn header(&self) -> &SdtHeader;\n\n    fn validate(&self) -> Result<(), AcpiError> {\n        unsafe { self.header().validate(Self::SIGNATURE) }\n    }\n}\n\n#[derive(Clone, Debug)]\n#[non_exhaustive]\npub enum AcpiError {\n    NoValidRsdp,\n    RsdpIncorrectSignature,\n    RsdpInvalidOemId,\n    RsdpInvalidChecksum,\n\n    SdtInvalidSignature(Signature),\n    SdtInvalidOemId(Signature),\n    SdtInvalidTableId(Signature),\n    SdtInvalidChecksum(Signature),\n    SdtInvalidCreatorId(Signature),\n\n    TableNotFound(Signature),\n    InvalidFacsAddress,\n    InvalidDsdtAddress,\n    InvalidMadt(MadtError),\n    InvalidGenericAddress,\n\n    Timeout,\n\n    #[cfg(feature = \"alloc\")]\n    Aml(aml::AmlError),\n\n    /// This is emitted to signal that the library does not support the requested behaviour. This\n    /// should eventually never be emitted.\n    LibUnimplemented,\n\n    /// This can be returned by the host (user of the library) to signal that required behaviour\n    /// has not been implemented. This will cause the error to be propagated back to the host if an\n    /// operation that requires that behaviour is performed.\n    HostUnimplemented,\n}\n\n/// Describes a physical mapping created by [`Handler::map_physical_region`] and unmapped by\n/// [`Handler::unmap_physical_region`]. The region mapped must be at least `size_of::<T>()`\n/// bytes, but may be bigger.\npub struct PhysicalMapping<H, T>\nwhere\n    H: Handler,\n{\n    /// The physical address of the mapped structure. The actual mapping may start at a lower address\n    /// if the requested physical address is not well-aligned.\n    pub physical_start: usize,\n    /// The virtual address of the mapped structure. It must be a valid, non-null pointer to the\n    /// start of the requested structure. The actual virtual mapping may start at a lower address\n    /// if the requested address is not well-aligned.\n    pub virtual_start: NonNull<T>,\n    /// The size of the requested region, in bytes. Can be equal or larger to `size_of::<T>()`. If a\n    /// larger region has been mapped, this should still be the requested size.\n    pub region_length: usize,\n    /// The total size of the produced mapping. This may be the same as `region_length`, or larger to\n    /// meet requirements of the mapping implementation.\n    pub mapped_length: usize,\n    /// The [`Handler`] that was used to produce the mapping. When this mapping is dropped, this\n    /// handler will be used to unmap the region.\n    pub handler: H,\n}\n\nimpl<H, T> PhysicalMapping<H, T>\nwhere\n    H: Handler,\n{\n    /// Get a pinned reference to the inner `T`. This is generally only useful if `T` is `!Unpin`,\n    /// otherwise the mapping can simply be dereferenced to access the inner type.\n    pub fn get(&self) -> Pin<&T> {\n        unsafe { Pin::new_unchecked(self.virtual_start.as_ref()) }\n    }\n}\n\nimpl<H, T> fmt::Debug for PhysicalMapping<H, T>\nwhere\n    H: Handler,\n{\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        f.debug_struct(\"PhysicalMapping\")\n            .field(\"physical_start\", &self.physical_start)\n            .field(\"virtual_start\", &self.virtual_start)\n            .field(\"region_length\", &self.region_length)\n            .field(\"mapped_length\", &self.mapped_length)\n            .field(\"handler\", &())\n            .finish()\n    }\n}\n\nunsafe impl<H: Handler + Send, T: Send> Send for PhysicalMapping<H, T> {}\n\nimpl<H, T> Deref for PhysicalMapping<H, T>\nwhere\n    T: Unpin,\n    H: Handler,\n{\n    type Target = T;\n\n    fn deref(&self) -> &T {\n        unsafe { self.virtual_start.as_ref() }\n    }\n}\n\nimpl<H, T> DerefMut for PhysicalMapping<H, T>\nwhere\n    T: Unpin,\n    H: Handler,\n{\n    fn deref_mut(&mut self) -> &mut T {\n        unsafe { self.virtual_start.as_mut() }\n    }\n}\n\nimpl<H, T> Drop for PhysicalMapping<H, T>\nwhere\n    H: Handler,\n{\n    fn drop(&mut self) {\n        H::unmap_physical_region(self)\n    }\n}\n\n/// A `Handle` is an opaque reference to an object that is managed by the host on behalf of this\n/// library.\n///\n/// The library will treat the value of a handle as entirely opaque. You may manage handles\n/// however you wish, and the same value can be used to refer to objects of different types, if\n/// desired.\n#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]\npub struct Handle(pub u32);\n\n/// An implementation of this trait must be provided to allow `acpi` to perform operations that\n/// interface with the underlying hardware and other systems in your host implementation. This\n/// interface is designed to be flexible to allow usage of the library from a variety of settings.\n///\n/// Depending on your usage of this library, not all functionality may be required. If you do not\n/// provide certain functionality, you should return [`AcpiError::HostUnimplemented`]. The library\n/// will attempt to propagate this error back to the host if an operation cannot be performed\n/// without that functionality.\n///\n/// The `Handler` must be cheaply clonable (e.g. a reference, `Arc`, marker struct, etc.) as a copy\n/// of the handler is stored in various structures, such as in each [`PhysicalMapping`] to\n/// facilitate unmapping.\npub trait Handler: Clone {\n    /// Given a physical address and a size, map a region of physical memory that contains `T` (note: the passed\n    /// size may be larger than `size_of::<T>()`). The address is not neccessarily page-aligned, so the\n    /// implementation may need to map more than `size` bytes. The virtual address the region is mapped to does not\n    /// matter, as long as it is accessible to `acpi`. Refer to the fields on [`PhysicalMapping`] to understand how\n    /// to produce one properly.\n    ///\n    /// ## Safety\n    ///\n    /// - `physical_address` must point to a valid `T` in physical memory.\n    /// - `size` must be at least `size_of::<T>()`.\n    unsafe fn map_physical_region<T>(&self, physical_address: usize, size: usize) -> PhysicalMapping<Self, T>;\n\n    /// Unmap the given physical mapping. This is called when a [`PhysicalMapping`] is dropped, you should **not** manually call this.\n    ///\n    /// Note: A reference to the [`Handler`] used to construct `region` can be acquired from [`PhysicalMapping::handler`].\n    fn unmap_physical_region<T>(region: &PhysicalMapping<Self, T>);\n\n    // TODO: maybe we should map stuff ourselves in the AML interpreter and do this internally?\n    // Maybe provide a hook for tracing the IO / emit trace events ourselves if we do do that?\n    fn read_u8(&self, address: usize) -> u8;\n    fn read_u16(&self, address: usize) -> u16;\n    fn read_u32(&self, address: usize) -> u32;\n    fn read_u64(&self, address: usize) -> u64;\n\n    fn write_u8(&self, address: usize, value: u8);\n    fn write_u16(&self, address: usize, value: u16);\n    fn write_u32(&self, address: usize, value: u32);\n    fn write_u64(&self, address: usize, value: u64);\n\n    // TODO: would be nice to provide defaults that just do the actual port IO on x86?\n    fn read_io_u8(&self, port: u16) -> u8;\n    fn read_io_u16(&self, port: u16) -> u16;\n    fn read_io_u32(&self, port: u16) -> u32;\n\n    fn write_io_u8(&self, port: u16, value: u8);\n    fn write_io_u16(&self, port: u16, value: u16);\n    fn write_io_u32(&self, port: u16, value: u32);\n\n    fn read_pci_u8(&self, address: PciAddress, offset: u16) -> u8;\n    fn read_pci_u16(&self, address: PciAddress, offset: u16) -> u16;\n    fn read_pci_u32(&self, address: PciAddress, offset: u16) -> u32;\n\n    fn write_pci_u8(&self, address: PciAddress, offset: u16, value: u8);\n    fn write_pci_u16(&self, address: PciAddress, offset: u16, value: u16);\n    fn write_pci_u32(&self, address: PciAddress, offset: u16, value: u32);\n\n    /// Returns a monotonically-increasing value of nanoseconds.\n    fn nanos_since_boot(&self) -> u64;\n\n    /// Stall for at least the given number of **microseconds**. An implementation should not relinquish control of\n    /// the processor during the stall, and for this reason, firmwares should not stall for periods of more than\n    /// 100 microseconds.\n    fn stall(&self, microseconds: u64);\n\n    /// Sleep for at least the given number of **milliseconds**. An implementation may round to the closest sleep\n    /// time supported, and should relinquish the processor.\n    fn sleep(&self, milliseconds: u64);\n\n    #[cfg(feature = \"aml\")]\n    fn create_mutex(&self) -> Handle;\n\n    /// Acquire the mutex referred to by the given handle. `timeout` is a millisecond timeout value\n    /// with the following meaning:\n    ///    - `0` - try to acquire the mutex once, in a non-blocking manner. If the mutex cannot be\n    ///      acquired immediately, return `Err(AmlError::MutexAcquireTimeout)`\n    ///    - `1-0xfffe` - try to acquire the mutex for at least `timeout` milliseconds.\n    ///    - `0xffff` - try to acquire the mutex indefinitely. Should not return `MutexAcquireTimeout`.\n    ///\n    /// AML mutexes are **reentrant** - that is, a thread may acquire the same mutex more than once\n    /// without causing a deadlock.\n    #[cfg(feature = \"aml\")]\n    fn acquire(&self, mutex: Handle, timeout: u16) -> Result<(), aml::AmlError>;\n    #[cfg(feature = \"aml\")]\n    fn release(&self, mutex: Handle);\n\n    #[cfg(feature = \"aml\")]\n    fn breakpoint(&self) {}\n\n    #[cfg(feature = \"aml\")]\n    fn handle_debug(&self, _object: &aml::object::Object) {}\n\n    #[cfg(feature = \"aml\")]\n    fn handle_fatal_error(&self, fatal_type: u8, fatal_code: u32, fatal_arg: u64) {\n        error!(\n            \"Fatal error while executing AML (encountered DefFatalOp). fatal_type = {}, fatal_code = {}, fatal_arg = {}\",\n            fatal_type, fatal_code, fatal_arg\n        );\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    #[test]\n    #[allow(dead_code)]\n    fn test_physical_mapping_send_sync() {\n        fn test_send_sync<T: Send>() {}\n        fn caller<H: Handler + Send, T: Send>() {\n            test_send_sync::<PhysicalMapping<H, T>>();\n        }\n    }\n}\n"
  },
  {
    "path": "src/platform/interrupt.rs",
    "content": "use super::{Processor, ProcessorInfo, ProcessorState};\nuse crate::{\n    AcpiError,\n    AcpiTables,\n    Handler,\n    MadtError,\n    sdt::{\n        Signature,\n        madt::{Madt, MadtEntry, parse_mps_inti_flags},\n    },\n};\nuse alloc::{alloc::Global, vec::Vec};\nuse bit_field::BitField;\nuse core::{alloc::Allocator, pin::Pin};\n\npub use crate::sdt::madt::{Polarity, TriggerMode};\n\n#[derive(Debug, Clone)]\n#[non_exhaustive]\npub enum InterruptModel<A: Allocator = Global> {\n    /// This model is only chosen when the MADT does not describe another interrupt model. On `x86_64` platforms,\n    /// this probably means only the legacy i8259 PIC is present.\n    Unknown,\n\n    /// Describes an interrupt controller based around the Advanced Programmable Interrupt Controller (any of APIC,\n    /// XAPIC, or X2APIC). These are likely to be found on x86 and x86_64 systems and are made up of a Local APIC\n    /// for each core and one or more I/O APICs to handle external interrupts.\n    Apic(Apic<A>),\n}\n\nimpl InterruptModel<Global> {\n    pub fn new<H: Handler>(\n        tables: &AcpiTables<H>,\n    ) -> Result<(InterruptModel<Global>, Option<ProcessorInfo<Global>>), AcpiError> {\n        Self::new_in(tables, Global)\n    }\n}\n\nimpl<A: Allocator + Clone> InterruptModel<A> {\n    pub fn new_in<H: Handler>(\n        tables: &AcpiTables<H>,\n        allocator: A,\n    ) -> Result<(InterruptModel<A>, Option<ProcessorInfo<A>>), AcpiError> {\n        let Some(madt) = tables.find_table::<Madt>() else { Err(AcpiError::TableNotFound(Signature::MADT))? };\n\n        /*\n         * We first do a pass through the MADT to determine which interrupt model is being used.\n         */\n        for entry in madt.get().entries() {\n            match entry {\n                MadtEntry::LocalApic(_)\n                | MadtEntry::LocalX2Apic(_)\n                | MadtEntry::IoApic(_)\n                | MadtEntry::InterruptSourceOverride(_)\n                | MadtEntry::LocalApicNmi(_)\n                | MadtEntry::X2ApicNmi(_)\n                | MadtEntry::LocalApicAddressOverride(_) => {\n                    return Self::from_apic_model_in(madt.get(), allocator);\n                }\n\n                MadtEntry::IoSapic(_) | MadtEntry::LocalSapic(_) | MadtEntry::PlatformInterruptSource(_) => {}\n\n                MadtEntry::Gicc(_)\n                | MadtEntry::Gicd(_)\n                | MadtEntry::GicMsiFrame(_)\n                | MadtEntry::GicRedistributor(_)\n                | MadtEntry::GicInterruptTranslationService(_) => {}\n\n                MadtEntry::NmiSource(_) => (),\n                MadtEntry::MultiprocessorWakeup(_) => (),\n            }\n        }\n\n        Ok((InterruptModel::Unknown, None))\n    }\n\n    fn from_apic_model_in(\n        madt: Pin<&Madt>,\n        allocator: A,\n    ) -> Result<(InterruptModel<A>, Option<ProcessorInfo<A>>), AcpiError> {\n        let mut local_apic_address = madt.local_apic_address as u64;\n        let mut io_apic_count = 0;\n        let mut iso_count = 0;\n        let mut nmi_source_count = 0;\n        let mut local_nmi_line_count = 0;\n        let mut processor_count = 0usize;\n\n        // Do a pass over the entries so we know how much space we should reserve in the vectors\n        for entry in madt.entries() {\n            match entry {\n                MadtEntry::IoApic(_) => io_apic_count += 1,\n                MadtEntry::InterruptSourceOverride(_) => iso_count += 1,\n                MadtEntry::NmiSource(_) => nmi_source_count += 1,\n                MadtEntry::LocalApicNmi(_) => local_nmi_line_count += 1,\n                MadtEntry::X2ApicNmi(_) => local_nmi_line_count += 1,\n                MadtEntry::LocalApic(_) => processor_count += 1,\n                MadtEntry::LocalX2Apic(_) => processor_count += 1,\n                _ => (),\n            }\n        }\n\n        let mut io_apics = Vec::with_capacity_in(io_apic_count, allocator.clone());\n        let mut interrupt_source_overrides = Vec::with_capacity_in(iso_count, allocator.clone());\n        let mut nmi_sources = Vec::with_capacity_in(nmi_source_count, allocator.clone());\n        let mut local_apic_nmi_lines = Vec::with_capacity_in(local_nmi_line_count, allocator.clone());\n        let mut application_processors = Vec::with_capacity_in(processor_count.saturating_sub(1), allocator); // Subtract one for the BSP\n        let mut boot_processor = None;\n\n        for entry in madt.entries() {\n            match entry {\n                MadtEntry::LocalApic(entry) => {\n                    /*\n                     * The first processor is the BSP. Subsequent ones are APs. If we haven't found\n                     * the BSP yet, this must be it.\n                     */\n                    let is_ap = boot_processor.is_some();\n                    let is_disabled = !{ entry.flags }.get_bit(0);\n\n                    let state = match (is_ap, is_disabled) {\n                        (_, true) => ProcessorState::Disabled,\n                        (true, false) => ProcessorState::WaitingForSipi,\n                        (false, false) => ProcessorState::Running,\n                    };\n\n                    let processor = Processor {\n                        processor_uid: entry.processor_id as u32,\n                        local_apic_id: entry.apic_id as u32,\n                        state,\n                        is_ap,\n                    };\n\n                    if is_ap {\n                        application_processors.push(processor);\n                    } else {\n                        boot_processor = Some(processor);\n                    }\n                }\n\n                MadtEntry::LocalX2Apic(entry) => {\n                    let is_ap = boot_processor.is_some();\n                    let is_disabled = !{ entry.flags }.get_bit(0);\n\n                    let state = match (is_ap, is_disabled) {\n                        (_, true) => ProcessorState::Disabled,\n                        (true, false) => ProcessorState::WaitingForSipi,\n                        (false, false) => ProcessorState::Running,\n                    };\n\n                    let processor = Processor {\n                        processor_uid: entry.processor_uid,\n                        local_apic_id: entry.x2apic_id,\n                        state,\n                        is_ap,\n                    };\n\n                    if is_ap {\n                        application_processors.push(processor);\n                    } else {\n                        boot_processor = Some(processor);\n                    }\n                }\n\n                MadtEntry::IoApic(entry) => {\n                    io_apics.push(IoApic {\n                        id: entry.io_apic_id,\n                        address: entry.io_apic_address,\n                        global_system_interrupt_base: entry.global_system_interrupt_base,\n                    });\n                }\n\n                MadtEntry::InterruptSourceOverride(entry) => {\n                    if entry.bus != 0 {\n                        return Err(AcpiError::InvalidMadt(MadtError::InterruptOverrideEntryHasInvalidBus));\n                    }\n\n                    let (polarity, trigger_mode) = parse_mps_inti_flags(entry.flags)?;\n\n                    interrupt_source_overrides.push(InterruptSourceOverride {\n                        isa_source: entry.irq,\n                        global_system_interrupt: entry.global_system_interrupt,\n                        polarity,\n                        trigger_mode,\n                    });\n                }\n\n                MadtEntry::NmiSource(entry) => {\n                    let (polarity, trigger_mode) = parse_mps_inti_flags(entry.flags)?;\n\n                    nmi_sources.push(NmiSource {\n                        global_system_interrupt: entry.global_system_interrupt,\n                        polarity,\n                        trigger_mode,\n                    });\n                }\n\n                MadtEntry::LocalApicNmi(entry) => {\n                    local_apic_nmi_lines.push(NmiLine {\n                        processor: if entry.processor_id == 0xff {\n                            NmiProcessor::All\n                        } else {\n                            NmiProcessor::ProcessorUid(entry.processor_id as u32)\n                        },\n                        line: match entry.nmi_line {\n                            0 => LocalInterruptLine::Lint0,\n                            1 => LocalInterruptLine::Lint1,\n                            _ => return Err(AcpiError::InvalidMadt(MadtError::InvalidLocalNmiLine)),\n                        },\n                    });\n                }\n\n                MadtEntry::X2ApicNmi(entry) => {\n                    local_apic_nmi_lines.push(NmiLine {\n                        processor: if entry.processor_uid == 0xffffffff {\n                            NmiProcessor::All\n                        } else {\n                            NmiProcessor::ProcessorUid(entry.processor_uid)\n                        },\n                        line: match entry.nmi_line {\n                            0 => LocalInterruptLine::Lint0,\n                            1 => LocalInterruptLine::Lint1,\n                            _ => return Err(AcpiError::InvalidMadt(MadtError::InvalidLocalNmiLine)),\n                        },\n                    });\n                }\n\n                MadtEntry::LocalApicAddressOverride(entry) => {\n                    local_apic_address = entry.local_apic_address;\n                }\n\n                MadtEntry::MultiprocessorWakeup(_) => {}\n\n                _ => {\n                    return Err(AcpiError::InvalidMadt(MadtError::UnexpectedEntry));\n                }\n            }\n        }\n\n        Ok((\n            InterruptModel::Apic(Apic::new(\n                local_apic_address,\n                io_apics,\n                local_apic_nmi_lines,\n                interrupt_source_overrides,\n                nmi_sources,\n                madt.supports_8259(),\n            )),\n            Some(ProcessorInfo::new_in(boot_processor.unwrap(), application_processors)),\n        ))\n    }\n}\n\n#[derive(Debug, Clone, Copy)]\npub struct IoApic {\n    pub id: u8,\n    /// The physical address at which to access this I/O APIC.\n    pub address: u32,\n    /// The global system interrupt number where this I/O APIC's inputs start.\n    pub global_system_interrupt_base: u32,\n}\n\n#[derive(Debug, Clone, Copy)]\npub struct NmiLine {\n    pub processor: NmiProcessor,\n    pub line: LocalInterruptLine,\n}\n\n/// Indicates which local interrupt line will be utilized by an external interrupt. Specifically,\n/// these lines directly correspond to their requisite LVT entries in a processor's APIC.\n#[derive(Debug, Clone, Copy, PartialEq, Eq)]\npub enum LocalInterruptLine {\n    Lint0,\n    Lint1,\n}\n\n#[derive(Debug, Clone, Copy, PartialEq, Eq)]\npub enum NmiProcessor {\n    All,\n    ProcessorUid(u32),\n}\n\n/// Describes a difference in the mapping of an ISA interrupt to how it's mapped in other interrupt\n/// models. For example, if a device is connected to ISA IRQ 0 and IOAPIC input 2, an override will\n/// appear mapping source 0 to GSI 2. Currently these will only be created for ISA interrupt\n/// sources.\n#[derive(Debug, Clone, Copy)]\npub struct InterruptSourceOverride {\n    pub isa_source: u8,\n    pub global_system_interrupt: u32,\n    pub polarity: Polarity,\n    pub trigger_mode: TriggerMode,\n}\n\n/// Describes a Global System Interrupt that should be enabled as non-maskable. Any source that is\n/// non-maskable can not be used by devices.\n#[derive(Debug, Clone, Copy)]\npub struct NmiSource {\n    pub global_system_interrupt: u32,\n    pub polarity: Polarity,\n    pub trigger_mode: TriggerMode,\n}\n\n#[derive(Debug, Clone)]\npub struct Apic<A: Allocator = Global> {\n    pub local_apic_address: u64,\n    pub io_apics: Vec<IoApic, A>,\n    pub local_apic_nmi_lines: Vec<NmiLine, A>,\n    pub interrupt_source_overrides: Vec<InterruptSourceOverride, A>,\n    pub nmi_sources: Vec<NmiSource, A>,\n\n    /// If this field is set, you must remap and mask all the lines of the legacy PIC, even if\n    /// you choose to use the APIC. It's recommended that you do this even if ACPI does not\n    /// require you to.\n    pub also_has_legacy_pics: bool,\n}\n\nimpl<A: Allocator> Apic<A> {\n    pub(crate) fn new(\n        local_apic_address: u64,\n        io_apics: Vec<IoApic, A>,\n        local_apic_nmi_lines: Vec<NmiLine, A>,\n        interrupt_source_overrides: Vec<InterruptSourceOverride, A>,\n        nmi_sources: Vec<NmiSource, A>,\n        also_has_legacy_pics: bool,\n    ) -> Self {\n        Self {\n            local_apic_address,\n            io_apics,\n            local_apic_nmi_lines,\n            interrupt_source_overrides,\n            nmi_sources,\n            also_has_legacy_pics,\n        }\n    }\n}\n"
  },
  {
    "path": "src/platform/mod.rs",
    "content": "pub mod interrupt;\npub mod numa;\npub mod pci;\n\npub use interrupt::InterruptModel;\npub use pci::PciConfigRegions;\n\nuse crate::{\n    AcpiError,\n    AcpiTables,\n    Handler,\n    PowerProfile,\n    address::GenericAddress,\n    registers::{FixedRegisters, Pm1ControlBit, Pm1Event},\n    sdt::{\n        Signature,\n        fadt::Fadt,\n        madt::{Madt, MadtError, MpProtectedModeWakeupCommand, MultiprocessorWakeupMailbox},\n    },\n};\nuse alloc::{alloc::Global, sync::Arc, vec::Vec};\nuse core::{alloc::Allocator, mem, ptr};\n\n/// `AcpiPlatform` is a higher-level view of the ACPI tables that makes it easier to perform common\n/// tasks with ACPI. It requires allocator support.\npub struct AcpiPlatform<H: Handler, A: Allocator = Global> {\n    pub handler: H,\n    pub tables: AcpiTables<H>,\n    pub power_profile: PowerProfile,\n    pub interrupt_model: InterruptModel<A>,\n    /// The interrupt vector that the System Control Interrupt (SCI) is wired to. On x86 systems with\n    /// an 8259, this is the interrupt vector. On other systems, this is the GSI of the SCI\n    /// interrupt. The interrupt should be treated as a shareable, level, active-low interrupt.\n    pub sci_interrupt: u16,\n    /// On `x86_64` platforms that support the APIC, the processor topology must also be inferred from the\n    /// interrupt model. That information is stored here, if present.\n    pub processor_info: Option<ProcessorInfo<A>>,\n    pub pm_timer: Option<PmTimer>,\n    pub registers: Arc<FixedRegisters<H>>,\n}\n\nunsafe impl<H, A> Send for AcpiPlatform<H, A>\nwhere\n    H: Handler,\n    A: Allocator,\n{\n}\nunsafe impl<H, A> Sync for AcpiPlatform<H, A>\nwhere\n    H: Handler,\n    A: Allocator,\n{\n}\n\nimpl<H: Handler> AcpiPlatform<H, Global> {\n    pub fn new(tables: AcpiTables<H>, handler: H) -> Result<Self, AcpiError> {\n        Self::new_in(tables, handler, alloc::alloc::Global)\n    }\n}\n\nimpl<H: Handler, A: Allocator + Clone> AcpiPlatform<H, A> {\n    pub fn new_in(tables: AcpiTables<H>, handler: H, allocator: A) -> Result<Self, AcpiError> {\n        let Some(fadt) = tables.find_table::<Fadt>() else { Err(AcpiError::TableNotFound(Signature::FADT))? };\n        let power_profile = fadt.power_profile();\n\n        let (interrupt_model, processor_info) = InterruptModel::new_in(&tables, allocator)?;\n        let pm_timer = PmTimer::new(&fadt)?;\n        let registers = Arc::new(FixedRegisters::new(&fadt, handler.clone())?);\n\n        Ok(AcpiPlatform {\n            handler: handler.clone(),\n            tables,\n            power_profile,\n            interrupt_model,\n            sci_interrupt: fadt.sci_interrupt,\n            processor_info,\n            pm_timer,\n            registers,\n        })\n    }\n\n    /// Initializes the event registers, masking all events to start.\n    pub fn initialize_events(&self) -> Result<(), AcpiError> {\n        /*\n         * Disable all fixed events to start.\n         */\n        self.registers.pm1_event_registers.set_event_enabled(Pm1Event::Timer, false)?;\n        self.registers.pm1_event_registers.set_event_enabled(Pm1Event::GlobalLock, false)?;\n        self.registers.pm1_event_registers.set_event_enabled(Pm1Event::PowerButton, false)?;\n        self.registers.pm1_event_registers.set_event_enabled(Pm1Event::SleepButton, false)?;\n        self.registers.pm1_event_registers.set_event_enabled(Pm1Event::Rtc, false)?;\n        self.registers.pm1_event_registers.set_event_enabled(Pm1Event::PciEWake, false)?;\n        self.registers.pm1_event_registers.set_event_enabled(Pm1Event::Wake, false)?;\n\n        // TODO: deal with GPEs\n\n        Ok(())\n    }\n\n    pub fn read_mode(&self) -> Result<AcpiMode, AcpiError> {\n        if self.registers.pm1_control_registers.read_bit(Pm1ControlBit::SciEnable)? {\n            Ok(AcpiMode::Acpi)\n        } else {\n            Ok(AcpiMode::Legacy)\n        }\n    }\n\n    /// Move the platform into ACPI mode, if it is not already in it. This means platform power\n    /// management events will be routed to the kernel via the SCI interrupt, instead of to the\n    /// firmware's SMI handler.\n    ///\n    /// ### Warning\n    /// This can be a bad idea on real hardware if you are not able to handle platform events\n    /// properly. Entering ACPI mode means you are responsible for dealing with events like the\n    /// power button and thermal events instead of the firmware - if you do not handle these, it\n    /// may be difficult to recover the platform. Hardware damage is unlikely as firmware usually\n    /// has safeguards for critical events, but like with all things concerning firmware, you may\n    /// not wish to rely on these.\n    pub fn enter_acpi_mode(&self) -> Result<(), AcpiError> {\n        if self.read_mode()? == AcpiMode::Acpi {\n            return Ok(());\n        }\n\n        let Some(fadt) = self.tables.find_table::<Fadt>() else { Err(AcpiError::TableNotFound(Signature::FADT))? };\n        self.handler.write_io_u8(fadt.smi_cmd_port as u16, fadt.acpi_enable);\n\n        /*\n         * We now have to spin and wait for the firmware to yield control. We'll wait up to 3\n         * seconds.\n         */\n        let mut spinning = 3 * 1000 * 1000; // Microseconds\n        while spinning > 0 {\n            if self.read_mode()? == AcpiMode::Acpi {\n                return Ok(());\n            }\n\n            spinning -= 100;\n            self.handler.stall(100);\n        }\n\n        Err(AcpiError::Timeout)\n    }\n\n    /// Wake up all Application Processors (APs) using the Multiprocessor Wakeup Mailbox Mechanism.\n    /// This may not be available on the platform you're running on.\n    ///\n    /// On Intel processors, this will start the AP in long-mode, with interrupts disabled and a\n    /// single page with the supplied waking vector identity-mapped (it is therefore advisable to\n    /// align your waking vector to start at a page boundary and fit within one page).\n    ///\n    /// # Safety\n    /// An appropriate environment must exist for the AP to boot into at the given address, or the\n    /// AP could fault or cause unexpected behaviour.\n    pub unsafe fn wake_aps(&self, apic_id: u32, wakeup_vector: u64, timeout_loops: u64) -> Result<(), AcpiError> {\n        let Some(madt) = self.tables.find_table::<Madt>() else { Err(AcpiError::TableNotFound(Signature::MADT))? };\n        let mailbox_addr = madt.get().get_mpwk_mailbox_addr()?;\n        let mut mpwk_mapping = unsafe {\n            self.handler.map_physical_region::<MultiprocessorWakeupMailbox>(\n                mailbox_addr as usize,\n                mem::size_of::<MultiprocessorWakeupMailbox>(),\n            )\n        };\n\n        // Reset command\n        unsafe {\n            ptr::write_volatile(&mut mpwk_mapping.command, MpProtectedModeWakeupCommand::Noop as u16);\n        }\n\n        // Fill the mailbox\n        mpwk_mapping.apic_id = apic_id;\n        mpwk_mapping.wakeup_vector = wakeup_vector;\n        unsafe {\n            ptr::write_volatile(&mut mpwk_mapping.command, MpProtectedModeWakeupCommand::Wakeup as u16);\n        }\n\n        // Wait to join\n        // TODO: if we merge the handlers into one, we could use `stall` here.\n        let mut loops = 0;\n        let mut command = MpProtectedModeWakeupCommand::Wakeup;\n        while command != MpProtectedModeWakeupCommand::Noop {\n            if loops >= timeout_loops {\n                return Err(AcpiError::InvalidMadt(MadtError::WakeupApsTimeout));\n            }\n            // SAFETY: The caller must ensure that the provided `handler` correctly handles these\n            // operations and that the specified `mailbox_addr` is valid.\n            unsafe {\n                command = ptr::read_volatile(&mpwk_mapping.command).into();\n            }\n            core::hint::spin_loop();\n            loops += 1;\n        }\n        drop(mpwk_mapping);\n\n        Ok(())\n    }\n}\n\n#[derive(Clone, Copy, Debug, PartialEq, Eq)]\npub enum ProcessorState {\n    /// A processor in this state is unusable, and you must not attempt to bring it up.\n    Disabled,\n\n    /// A processor waiting for a SIPI (Startup Inter-processor Interrupt) is currently not active,\n    /// but may be brought up.\n    WaitingForSipi,\n\n    /// A Running processor is currently brought up and running code.\n    Running,\n}\n\n#[derive(Clone, Copy, Debug, PartialEq, Eq)]\npub struct Processor {\n    /// Corresponds to the `_UID` object of the processor's `Device`, or the `ProcessorId` field of the `Processor`\n    /// object, in AML.\n    pub processor_uid: u32,\n    /// The ID of the local APIC of the processor. Will be less than `256` if the APIC is being used, but can be\n    /// greater than this if the X2APIC is being used.\n    pub local_apic_id: u32,\n\n    /// The state of this processor. Check that the processor is not `Disabled` before attempting to bring it up!\n    pub state: ProcessorState,\n\n    /// Whether this processor is the Bootstrap Processor (BSP), or an Application Processor (AP).\n    /// When the bootloader is entered, the BSP is the only processor running code. To run code on\n    /// more than one processor, you need to \"bring up\" the APs.\n    pub is_ap: bool,\n}\n\n#[derive(Debug, Clone)]\npub struct ProcessorInfo<A: Allocator = Global> {\n    pub boot_processor: Processor,\n    /// Application processors should be brought up in the order they're defined in this list.\n    pub application_processors: Vec<Processor, A>,\n}\n\nimpl<A: Allocator> ProcessorInfo<A> {\n    pub(crate) fn new_in(boot_processor: Processor, application_processors: Vec<Processor, A>) -> Self {\n        Self { boot_processor, application_processors }\n    }\n}\n\n/// Information about the ACPI Power Management Timer (ACPI PM Timer).\n#[derive(Debug, Clone)]\npub struct PmTimer {\n    /// A generic address to the register block of ACPI PM Timer.\n    pub base: GenericAddress,\n    /// This field is `true` if the hardware supports 32-bit timer, and `false` if the hardware supports 24-bit timer.\n    pub supports_32bit: bool,\n}\n\nimpl PmTimer {\n    pub fn new(fadt: &Fadt) -> Result<Option<PmTimer>, AcpiError> {\n        match fadt.pm_timer_block()? {\n            Some(base) => Ok(Some(PmTimer { base, supports_32bit: { fadt.flags }.pm_timer_is_32_bit() })),\n            None => Ok(None),\n        }\n    }\n}\n\n#[derive(Clone, Copy, PartialEq, Debug)]\npub enum AcpiMode {\n    Legacy,\n    Acpi,\n}\n"
  },
  {
    "path": "src/platform/numa.rs",
    "content": "use crate::{\n    AcpiTables,\n    Handler,\n    sdt::{\n        slit::{DistanceMatrix, Slit},\n        srat::{LocalApicAffinityFlags, MemoryAffinityFlags, Srat, SratEntry},\n    },\n};\nuse alloc::{alloc::Global, vec::Vec};\nuse core::alloc::Allocator;\n\n/// Information about the setup of NUMA (Non-Uniform Memory Architecture) resources within the\n/// sytem.\n#[derive(Clone, Debug)]\npub struct NumaInfo<A: Allocator = Global> {\n    pub processor_affinity: Vec<ProcessorAffinity, A>,\n    pub memory_affinity: Vec<MemoryAffinity, A>,\n    pub num_proximity_domains: usize,\n    pub distance_matrix: Vec<u8, A>,\n}\n\nimpl NumaInfo<Global> {\n    pub fn new(tables: AcpiTables<impl Handler>) -> NumaInfo<Global> {\n        Self::new_in(tables, Global)\n    }\n}\n\nimpl<A: Allocator + Clone> NumaInfo<A> {\n    pub fn new_in(tables: AcpiTables<impl Handler>, allocator: A) -> NumaInfo<A> {\n        let mut processor_affinity = Vec::new_in(allocator.clone());\n        let mut memory_affinity = Vec::new_in(allocator.clone());\n\n        if let Some(srat) = tables.find_table::<Srat>() {\n            for entry in srat.get().entries() {\n                match entry {\n                    SratEntry::LocalApicAffinity(entry) => processor_affinity.push(ProcessorAffinity {\n                        local_apic_id: entry.apic_id as u32,\n                        proximity_domain: entry.proximity_domain(),\n                        is_enabled: { entry.flags }.contains(LocalApicAffinityFlags::ENABLED),\n                    }),\n                    SratEntry::LocalApicX2Affinity(entry) => processor_affinity.push(ProcessorAffinity {\n                        local_apic_id: entry.x2apic_id,\n                        proximity_domain: entry.proximity_domain,\n                        is_enabled: { entry.flags }.contains(LocalApicAffinityFlags::ENABLED),\n                    }),\n                    SratEntry::MemoryAffinity(entry) => memory_affinity.push(MemoryAffinity {\n                        base_address: entry.base_address(),\n                        length: entry.length(),\n                        proximity_domain: entry.proximity_domain,\n                        is_enabled: { entry.flags }.contains(MemoryAffinityFlags::ENABLED),\n                        is_hot_pluggable: { entry.flags }.contains(MemoryAffinityFlags::HOT_PLUGGABLE),\n                        is_non_volatile: { entry.flags }.contains(MemoryAffinityFlags::NON_VOLATILE),\n                    }),\n                    _ => (),\n                }\n            }\n        }\n\n        let (num_proximity_domains, distance_matrix) = if let Some(slit) = tables.find_table::<Slit>() {\n            (slit.get().num_proximity_domains as usize, slit.get().matrix_raw().to_vec_in(allocator.clone()))\n        } else {\n            (0, Vec::new_in(allocator.clone()))\n        };\n\n        NumaInfo { processor_affinity, memory_affinity, num_proximity_domains, distance_matrix }\n    }\n\n    pub fn distance_matrix(&self) -> DistanceMatrix<'_> {\n        DistanceMatrix { num_proximity_domains: self.num_proximity_domains as u64, matrix: &self.distance_matrix }\n    }\n}\n\n#[derive(Clone, Debug)]\npub struct ProcessorAffinity {\n    pub local_apic_id: u32,\n    pub proximity_domain: u32,\n    pub is_enabled: bool,\n}\n\n#[derive(Clone, Debug)]\npub struct MemoryAffinity {\n    pub base_address: u64,\n    pub length: u64,\n    pub proximity_domain: u32,\n    pub is_enabled: bool,\n    pub is_hot_pluggable: bool,\n    pub is_non_volatile: bool,\n}\n"
  },
  {
    "path": "src/platform/pci.rs",
    "content": "use crate::{\n    AcpiError,\n    AcpiTables,\n    Handler,\n    sdt::{\n        Signature,\n        mcfg::{Mcfg, McfgEntry},\n    },\n};\nuse alloc::{\n    alloc::{Allocator, Global},\n    vec::Vec,\n};\n\n/// Describes a set of regions of physical memory used to access the PCIe configuration space. A\n/// region is created for each entry in the MCFG. Given the segment group, bus, device number, and\n/// function of a PCIe device, [`PciConfigRegions::physical_address`] will give you the physical\n/// address of the start of that device function's configuration space (each function has 4096\n/// bytes of configuration space in PCIe).\n#[derive(Clone, Debug)]\npub struct PciConfigRegions<A: Allocator = Global> {\n    pub regions: Vec<McfgEntry, A>,\n}\n\nimpl PciConfigRegions<Global> {\n    pub fn new<H>(tables: &AcpiTables<H>) -> Result<PciConfigRegions<Global>, AcpiError>\n    where\n        H: Handler,\n    {\n        Self::new_in(tables, Global)\n    }\n}\n\nimpl<A: Allocator> PciConfigRegions<A> {\n    pub fn new_in<H>(tables: &AcpiTables<H>, allocator: A) -> Result<PciConfigRegions<A>, AcpiError>\n    where\n        H: Handler,\n    {\n        let Some(mcfg) = tables.find_table::<Mcfg>() else { Err(AcpiError::TableNotFound(Signature::MCFG))? };\n        let regions = mcfg.entries().to_vec_in(allocator);\n\n        Ok(Self { regions })\n    }\n\n    /// Get the **physical** address of the start of the configuration space for a given PCIe device\n    /// function. Returns `None` if there isn't an entry in the MCFG that manages that device.\n    pub fn physical_address(&self, segment_group_no: u16, bus: u8, device: u8, function: u8) -> Option<u64> {\n        /*\n         * First, find the memory region that handles this segment and bus. This method is fine\n         * because there should only be one region that handles each segment group + bus\n         * combination.\n         */\n        let region = self.regions.iter().find(|region| {\n            region.pci_segment_group == segment_group_no\n                && (region.bus_number_start..=region.bus_number_end).contains(&bus)\n        })?;\n\n        Some(\n            region.base_address\n                + ((u64::from(bus - region.bus_number_start) << 20)\n                    | (u64::from(device) << 15)\n                    | (u64::from(function) << 12)),\n        )\n    }\n}\n"
  },
  {
    "path": "src/registers.rs",
    "content": "use crate::{AcpiError, Handler, address::MappedGas, sdt::fadt::Fadt};\nuse bit_field::BitField;\n\npub struct FixedRegisters<H: Handler> {\n    pub pm1_event_registers: Pm1EventRegisterBlock<H>,\n    pub pm1_control_registers: Pm1ControlRegisterBlock<H>,\n}\n\nimpl<H> FixedRegisters<H>\nwhere\n    H: Handler,\n{\n    pub fn new(fadt: &Fadt, handler: H) -> Result<FixedRegisters<H>, AcpiError> {\n        let pm1_event_registers = {\n            let pm1a = unsafe { MappedGas::map_gas(fadt.pm1a_event_block()?, &handler)? };\n            let pm1b = match fadt.pm1b_event_block()? {\n                Some(gas) => Some(unsafe { MappedGas::map_gas(gas, &handler)? }),\n                None => None,\n            };\n            Pm1EventRegisterBlock { pm1_event_length: fadt.pm1_event_length as usize, pm1a, pm1b }\n        };\n        let pm1_control_registers = {\n            let pm1a = unsafe { MappedGas::map_gas(fadt.pm1a_control_block()?, &handler)? };\n            let pm1b = match fadt.pm1b_control_block()? {\n                Some(gas) => Some(unsafe { MappedGas::map_gas(gas, &handler)? }),\n                None => None,\n            };\n            Pm1ControlRegisterBlock { pm1a, pm1b }\n        };\n\n        Ok(FixedRegisters { pm1_event_registers, pm1_control_registers })\n    }\n}\n\n/// The PM1 register grouping contains two register blocks that control fixed events. It is split\n/// into two to allow its functionality to be split between two hardware components. `PM1a` and\n/// `PM1b` are effectively mirrors of each other - reads are made from both of them and logically\n/// ORed, and writes are made to both of them.\n///\n/// The register grouping contains two registers - a `STS` status register that can be read to\n/// determine if an event has fired (and written to clear), and an `EN` enabling register to\n/// control whether an event should fire.\npub struct Pm1EventRegisterBlock<H: Handler> {\n    pub pm1_event_length: usize,\n    pub pm1a: MappedGas<H>,\n    pub pm1b: Option<MappedGas<H>>,\n}\n\n#[derive(Clone, Copy, PartialEq, Eq, Debug)]\n#[repr(u8)]\npub enum Pm1Event {\n    Timer = 0,\n    GlobalLock = 5,\n    PowerButton = 8,\n    SleepButton = 9,\n    Rtc = 10,\n    PciEWake = 14,\n    Wake = 15,\n}\n\nimpl<H> Pm1EventRegisterBlock<H>\nwhere\n    H: Handler,\n{\n    pub fn set_event_enabled(&self, event: Pm1Event, enabled: bool) -> Result<(), AcpiError> {\n        let enable_offset = self.pm1_event_length * 8 / 2;\n        let event_bit = match event {\n            Pm1Event::Timer => 0,\n            Pm1Event::GlobalLock => 5,\n            Pm1Event::PowerButton => 8,\n            Pm1Event::SleepButton => 9,\n            Pm1Event::Rtc => 10,\n            Pm1Event::PciEWake => 14,\n            Pm1Event::Wake => 15,\n        };\n\n        let mut pm1a = self.pm1a.read()?;\n        pm1a.set_bit(enable_offset + event_bit, enabled);\n        self.pm1a.write(pm1a)?;\n\n        if let Some(pm1b) = &self.pm1b {\n            let mut value = pm1b.read()?;\n            value.set_bit(enable_offset + event_bit, enabled);\n            pm1b.write(value)?;\n        }\n\n        Ok(())\n    }\n\n    pub fn read(&self) -> Result<u64, AcpiError> {\n        let pm1_len = self.pm1_event_length * 8;\n\n        let pm1a = self.pm1a.read()?.get_bits(0..pm1_len);\n        let pm1b = if let Some(pm1b) = &self.pm1b { pm1b.read()?.get_bits(0..pm1_len) } else { 0 };\n\n        Ok(pm1a | pm1b)\n    }\n}\n\npub struct Pm1ControlRegisterBlock<H: Handler> {\n    pub pm1a: MappedGas<H>,\n    pub pm1b: Option<MappedGas<H>>,\n}\n\n#[derive(Clone, Copy, PartialEq, Debug)]\npub enum Pm1ControlBit {\n    /// Determines whether the power management event produces an SCI or SMI interrupt. This is\n    /// controlled by the firmware - OSPM should always preserve this bit.\n    SciEnable = 0,\n    /// When this bit is set, bus master requests can cause any processor in the C3 state to\n    /// transistion to C0.\n    BusMasterWake = 1,\n    /// A write to this bit generates an SMI, passing control to the platform runtime firmware. It\n    /// should be written when the global lock is released and the pending bit in the FACS is set.\n    GlobalLockRelease = 2,\n    /*\n     * Bits 3..10 are reserved. Bits 10..13 are SLP_TYPx - this field is set separately and\n     * contains the desired hardware sleep state the system enters when `SleepEnable` is set.\n     */\n    SleepEnable = 13,\n}\n\nimpl<H> Pm1ControlRegisterBlock<H>\nwhere\n    H: Handler,\n{\n    pub fn read_bit(&self, bit: Pm1ControlBit) -> Result<bool, AcpiError> {\n        let control_bit = match bit {\n            Pm1ControlBit::SciEnable => 0,\n            Pm1ControlBit::BusMasterWake => 1,\n            Pm1ControlBit::GlobalLockRelease => 2,\n            Pm1ControlBit::SleepEnable => 13,\n        };\n\n        let pm1a = self.pm1a.read()?;\n        let pm1b = if let Some(ref pm1b) = self.pm1b { pm1b.read()? } else { 0 };\n        let pm1 = pm1a | pm1b;\n        Ok(pm1.get_bit(control_bit))\n    }\n\n    pub fn set_bit(&self, bit: Pm1ControlBit, set: bool) -> Result<(), AcpiError> {\n        let control_bit = match bit {\n            Pm1ControlBit::SciEnable => 0,\n            Pm1ControlBit::BusMasterWake => 1,\n            Pm1ControlBit::GlobalLockRelease => 2,\n            Pm1ControlBit::SleepEnable => 13,\n        };\n\n        let mut pm1a = self.pm1a.read()?;\n        pm1a.set_bit(control_bit, set);\n        self.pm1a.write(pm1a)?;\n\n        if let Some(pm1b) = &self.pm1b {\n            let mut value = pm1b.read()?;\n            value.set_bit(control_bit, set);\n            pm1b.write(value)?;\n        }\n\n        Ok(())\n    }\n\n    pub fn set_sleep_typ(&self, value: u8) -> Result<(), AcpiError> {\n        let mut pm1a = self.pm1a.read()?;\n        pm1a.set_bits(10..13, value as u64);\n        self.pm1a.write(pm1a)?;\n\n        if let Some(pm1b) = &self.pm1b {\n            let mut pm1b_value = pm1b.read()?;\n            pm1b_value.set_bits(10..13, value as u64);\n            pm1b.write(pm1b_value)?;\n        }\n\n        Ok(())\n    }\n}\n"
  },
  {
    "path": "src/rsdp.rs",
    "content": "use crate::{AcpiError, Handler, PhysicalMapping};\nuse core::{mem, ops::Range, slice, str};\n\n/// The size in bytes of the ACPI 1.0 RSDP.\nconst RSDP_V1_LENGTH: usize = 20;\n/// The total size in bytes of the RSDP fields introduced in ACPI 2.0.\nconst RSDP_V2_EXT_LENGTH: usize = mem::size_of::<Rsdp>() - RSDP_V1_LENGTH;\n\n/// The first structure found in ACPI. It just tells us where the RSDT is.\n///\n/// On BIOS systems, it is either found in the first 1KiB of the Extended Bios Data Area, or between `0x000e0000`\n/// and `0x000fffff`. The signature is always on a 16 byte boundary. On (U)EFI, it may not be located in these\n/// locations, and so an address should be found in the EFI configuration table instead.\n///\n/// The recommended way of locating the RSDP is to let the bootloader do it - Multiboot2 can pass a\n/// tag with the physical address of it. If this is not possible, a manual scan can be done.\n///\n/// If `revision > 0`, (the hardware ACPI version is Version 2.0 or greater), the RSDP contains\n/// some new fields. For ACPI Version 1.0, these fields are not valid and should not be accessed.\n/// For ACPI Version 2.0+, `xsdt_address` should be used (truncated to `u32` on x86) instead of\n/// `rsdt_address`.\n#[derive(Clone, Copy, Debug)]\n#[repr(C, packed)]\npub struct Rsdp {\n    pub signature: [u8; 8],\n    pub checksum: u8,\n    pub oem_id: [u8; 6],\n    pub revision: u8,\n    pub rsdt_address: u32,\n\n    /*\n     * These fields are only valid for ACPI Version 2.0 and greater\n     */\n    pub length: u32,\n    pub xsdt_address: u64,\n    pub ext_checksum: u8,\n    _reserved: [u8; 3],\n}\n\nimpl Rsdp {\n    /// This searches for a RSDP on BIOS systems.\n    ///\n    /// ### Safety\n    /// This function probes memory in three locations:\n    ///    - It reads a word from `40:0e` to locate the EBDA.\n    ///    - The first 1KiB of the EBDA (Extended BIOS Data Area).\n    ///    - The BIOS memory area at `0xe0000..=0xfffff`.\n    ///\n    /// This should be fine on all BIOS systems. However, UEFI platforms are free to put the RSDP wherever they\n    /// please, so this won't always find the RSDP. Further, prodding these memory locations may have unintended\n    /// side-effects. On UEFI systems, the RSDP should be found in the Configuration Table, using two GUIDs:\n    ///     - ACPI v1.0 structures use `eb9d2d30-2d88-11d3-9a16-0090273fc14d`.\n    ///     - ACPI v2.0 or later structures use `8868e871-e4f1-11d3-bc22-0080c73c8881`.\n    /// You should search the entire table for the v2.0 GUID before searching for the v1.0 one.\n    pub unsafe fn search_for_on_bios<H>(handler: H) -> Result<PhysicalMapping<H, Rsdp>, AcpiError>\n    where\n        H: Handler,\n    {\n        let rsdp_address = find_search_areas(handler.clone()).iter().find_map(|area| {\n            // Map the search area for the RSDP followed by `RSDP_V2_EXT_LENGTH` bytes so an ACPI 1.0 RSDP at the\n            // end of the area can be read as an `Rsdp` (which always has the size of an ACPI 2.0 RSDP)\n            let mapping = unsafe {\n                handler.map_physical_region::<u8>(area.start, area.end - area.start + RSDP_V2_EXT_LENGTH)\n            };\n\n            let extended_area_bytes =\n                unsafe { slice::from_raw_parts(mapping.virtual_start.as_ptr(), mapping.region_length) };\n\n            // Search `Rsdp`-sized windows at 16-byte boundaries relative to the base of the area (which is also\n            // aligned to 16 bytes due to the implementation of `find_search_areas`)\n            extended_area_bytes.windows(mem::size_of::<Rsdp>()).step_by(16).find_map(|maybe_rsdp_bytes_slice| {\n                let maybe_rsdp_virt_ptr = maybe_rsdp_bytes_slice.as_ptr().cast::<Rsdp>();\n                let maybe_rsdp_phys_start = maybe_rsdp_virt_ptr as usize - mapping.virtual_start.as_ptr() as usize\n                    + mapping.physical_start;\n                // SAFETY: `maybe_rsdp_virt_ptr` points to an aligned, readable `Rsdp`-sized value, and the `Rsdp`\n                // struct's fields are always initialized.\n                let maybe_rsdp = unsafe { &*maybe_rsdp_virt_ptr };\n\n                match maybe_rsdp.validate() {\n                    Ok(()) => Some(maybe_rsdp_phys_start),\n                    Err(AcpiError::RsdpIncorrectSignature) => None,\n                    Err(err) => {\n                        log::warn!(\"Invalid RSDP found at {:#x}: {:?}\", maybe_rsdp_phys_start, err);\n                        None\n                    }\n                }\n            })\n        });\n\n        match rsdp_address {\n            Some(address) => {\n                let rsdp_mapping = unsafe { handler.map_physical_region::<Rsdp>(address, mem::size_of::<Rsdp>()) };\n                Ok(rsdp_mapping)\n            }\n            None => Err(AcpiError::NoValidRsdp),\n        }\n    }\n\n    /// Checks that:\n    ///     1) The signature is correct\n    ///     2) The checksum is correct\n    ///     3) For Version 2.0+, that the extension checksum is correct\n    pub fn validate(&self) -> Result<(), AcpiError> {\n        // Check the signature\n        if self.signature != RSDP_SIGNATURE {\n            return Err(AcpiError::RsdpIncorrectSignature);\n        }\n\n        // Check the OEM id is valid UTF-8\n        if str::from_utf8(&self.oem_id).is_err() {\n            return Err(AcpiError::RsdpInvalidOemId);\n        }\n\n        /*\n         * `self.length` doesn't exist on ACPI version 1.0, so we mustn't rely on it. Instead,\n         * check for version 1.0 and use a hard-coded length instead.\n         */\n        let length = if self.revision > 0 {\n            // For Version 2.0+, include the number of bytes specified by `length`\n            self.length as usize\n        } else {\n            RSDP_V1_LENGTH\n        };\n\n        let bytes = unsafe { slice::from_raw_parts(self as *const Rsdp as *const u8, length) };\n        let sum = bytes.iter().fold(0u8, |sum, &byte| sum.wrapping_add(byte));\n\n        if sum != 0 {\n            return Err(AcpiError::RsdpInvalidChecksum);\n        }\n\n        Ok(())\n    }\n\n    pub fn signature(&self) -> [u8; 8] {\n        self.signature\n    }\n\n    pub fn checksum(&self) -> u8 {\n        self.checksum\n    }\n\n    pub fn oem_id(&self) -> Result<&str, AcpiError> {\n        str::from_utf8(&self.oem_id).map_err(|_| AcpiError::RsdpInvalidOemId)\n    }\n\n    pub fn revision(&self) -> u8 {\n        self.revision\n    }\n\n    pub fn rsdt_address(&self) -> u32 {\n        self.rsdt_address\n    }\n\n    pub fn length(&self) -> u32 {\n        assert!(self.revision > 0, \"Tried to read extended RSDP field with ACPI Version 1.0\");\n        self.length\n    }\n\n    pub fn xsdt_address(&self) -> u64 {\n        assert!(self.revision > 0, \"Tried to read extended RSDP field with ACPI Version 1.0\");\n        self.xsdt_address\n    }\n\n    pub fn ext_checksum(&self) -> u8 {\n        assert!(self.revision > 0, \"Tried to read extended RSDP field with ACPI Version 1.0\");\n        self.ext_checksum\n    }\n}\n\n/// Find the areas we should search for the RSDP in.\nfn find_search_areas<H>(handler: H) -> [Range<usize>; 2]\nwhere\n    H: Handler,\n{\n    /*\n     * Read the base address of the EBDA from its location in the BDA (BIOS Data Area). Not all BIOSs fill this out\n     * unfortunately, so we might not get a sensible result. We shift it left 4, as it's a segment address.\n     */\n    let ebda_start_mapping =\n        unsafe { handler.map_physical_region::<u16>(EBDA_START_SEGMENT_PTR, mem::size_of::<u16>()) };\n    let ebda_start = (*ebda_start_mapping as usize) << 4;\n\n    [\n        /*\n         * The main BIOS area below 1MiB. In practice, from my [Restioson's] testing, the RSDP is more often here\n         * than the EBDA. We also don't want to search the entire possibele EBDA range, if we've failed to find it\n         * from the BDA.\n         */\n        RSDP_BIOS_AREA_START..(RSDP_BIOS_AREA_END + 1),\n        // Check if base segment ptr is in valid range for EBDA base\n        if (EBDA_EARLIEST_START..EBDA_END).contains(&ebda_start) {\n            // First KiB of EBDA\n            ebda_start..ebda_start + 1024\n        } else {\n            // We don't know where the EBDA starts, so just search the largest possible EBDA\n            EBDA_EARLIEST_START..(EBDA_END + 1)\n        },\n    ]\n}\n\n/// This (usually!) contains the base address of the EBDA (Extended Bios Data Area), shifted right by 4\nconst EBDA_START_SEGMENT_PTR: usize = 0x40e;\n/// The earliest (lowest) memory address an EBDA (Extended Bios Data Area) can start\nconst EBDA_EARLIEST_START: usize = 0x80000;\n/// The end of the EBDA (Extended Bios Data Area)\nconst EBDA_END: usize = 0x9ffff;\n/// The start of the main BIOS area below 1MiB in which to search for the RSDP (Root System Description Pointer)\nconst RSDP_BIOS_AREA_START: usize = 0xe0000;\n/// The end of the main BIOS area below 1MiB in which to search for the RSDP (Root System Description Pointer)\nconst RSDP_BIOS_AREA_END: usize = 0xfffff;\n/// The RSDP (Root System Description Pointer)'s signature, \"RSD PTR \" (note trailing space)\nconst RSDP_SIGNATURE: [u8; 8] = *b\"RSD PTR \";\n"
  },
  {
    "path": "src/sdt/bgrt.rs",
    "content": "use crate::{\n    AcpiTable,\n    sdt::{SdtHeader, Signature},\n};\nuse bit_field::BitField;\n\n/// The BGRT table contains information about a boot graphic that was displayed\n/// by firmware.\n#[repr(C, packed)]\n#[derive(Debug, Clone, Copy)]\npub struct Bgrt {\n    pub header: SdtHeader,\n    pub version: u16,\n    pub status: u8,\n    pub image_type: u8,\n    pub image_address: u64,\n    pub image_offset_x: u32,\n    pub image_offset_y: u32,\n}\n\nunsafe impl AcpiTable for Bgrt {\n    const SIGNATURE: Signature = Signature::BGRT;\n\n    fn header(&self) -> &SdtHeader {\n        &self.header\n    }\n}\n\nimpl Bgrt {\n    pub fn image_type(&self) -> ImageType {\n        let img_type = self.image_type;\n        match img_type {\n            0 => ImageType::Bitmap,\n            _ => ImageType::Reserved,\n        }\n    }\n\n    /// Gets the orientation offset of the image.\n    /// Degrees are clockwise from the image's default orientation.\n    pub fn orientation_offset(&self) -> u16 {\n        let status = self.status;\n        match status.get_bits(1..3) {\n            0 => 0,\n            1 => 90,\n            2 => 180,\n            3 => 270,\n            _ => unreachable!(),\n        }\n    }\n\n    pub fn was_displayed(&self) -> bool {\n        let status = self.status;\n        status.get_bit(0)\n    }\n\n    pub fn image_offset(&self) -> (u32, u32) {\n        let x = self.image_offset_x;\n        let y = self.image_offset_y;\n        (x, y)\n    }\n}\n\n#[repr(u8)]\n#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]\npub enum ImageType {\n    Bitmap,\n    Reserved,\n}\n"
  },
  {
    "path": "src/sdt/facs.rs",
    "content": "use crate::sdt::Signature;\nuse core::sync::atomic::AtomicU32;\n\n#[repr(C)]\npub struct Facs {\n    pub signature: Signature,\n    pub length: u32,\n    pub hardware_signature: u32,\n    pub firmware_waking_vector: u32,\n    pub global_lock: AtomicU32,\n    pub flags: u32,\n    pub x_firmware_waking_vector: u64,\n    pub version: u8,\n    pub _reserved0: [u8; 3],\n    pub ospm_flags: u32,\n    pub reserved1: [u8; 24],\n}\n"
  },
  {
    "path": "src/sdt/fadt.rs",
    "content": "use crate::{\n    AcpiError,\n    AcpiTable,\n    address::{AddressSpace, GenericAddress, RawGenericAddress},\n    sdt::{ExtendedField, SdtHeader, Signature},\n};\nuse bit_field::BitField;\n\n#[derive(Debug, Clone, Copy, PartialEq, Eq)]\npub enum PowerProfile {\n    Unspecified,\n    Desktop,\n    Mobile,\n    Workstation,\n    EnterpriseServer,\n    SohoServer,\n    AppliancePc,\n    PerformanceServer,\n    Tablet,\n    Reserved(u8),\n}\n\n/// Represents the Fixed ACPI Description Table (FADT). This table contains various fixed hardware\n/// details, such as the addresses of the hardware register blocks. It also contains a pointer to\n/// the Differentiated Definition Block (DSDT).\n///\n/// In cases where the FADT contains both a 32-bit and 64-bit field for the same address, we should\n/// always prefer the 64-bit one. Only if it's zero or the CPU will not allow us to access that\n/// address should the 32-bit one be used.\n#[repr(C, packed)]\n#[derive(Debug, Clone, Copy)]\npub struct Fadt {\n    pub header: SdtHeader,\n\n    pub firmware_ctrl: u32,\n    pub dsdt_address: u32,\n\n    // Used in acpi 1.0; compatibility only, should be zero\n    _reserved: u8,\n\n    pub preferred_pm_profile: u8,\n    /// On systems with an i8259 PIC, this is the vector the System Control Interrupt (SCI) is wired to. On other systems, this is\n    /// the Global System Interrupt (GSI) number of the SCI.\n    ///\n    /// The SCI should be treated as a sharable, level, active-low interrupt.\n    pub sci_interrupt: u16,\n    /// The system port address of the SMI Command Port. This port should only be accessed from the boot processor.\n    /// A value of `0` indicates that System Management Mode is not supported.\n    ///\n    ///    - Writing the value in `acpi_enable` to this port will transfer control of the ACPI hardware registers\n    ///      from the firmware to the OS. You must synchronously wait for the transfer to complete, indicated by the\n    ///      setting of `SCI_EN`.\n    ///    - Writing the value in `acpi_disable` will relinquish ownership of the hardware registers to the\n    ///      firmware. This should only be done if you've previously acquired ownership. Before writing this value,\n    ///      the OS should mask all SCI interrupts and clear the `SCI_EN` bit.\n    ///    - Writing the value in `s4bios_req` requests that the firmware enter the S4 state through the S4BIOS\n    ///      feature. This is only supported if the `S4BIOS_F` flag in the FACS is set.\n    ///    - Writing the value in `pstate_control` yields control of the processor performance state to the OS.\n    ///      If this field is `0`, this feature is not supported.\n    ///    - Writing the value in `c_state_control` tells the firmware that the OS supports `_CST` AML objects and\n    ///      notifications of C State changes.\n    pub smi_cmd_port: u32,\n    pub acpi_enable: u8,\n    pub acpi_disable: u8,\n    pub s4bios_req: u8,\n    pub pstate_control: u8,\n    pub pm1a_event_block: u32,\n    pub pm1b_event_block: u32,\n    pub pm1a_control_block: u32,\n    pub pm1b_control_block: u32,\n    pub pm2_control_block: u32,\n    pub pm_timer_block: u32,\n    pub gpe0_block: u32,\n    pub gpe1_block: u32,\n    pub pm1_event_length: u8,\n    pub pm1_control_length: u8,\n    pub pm2_control_length: u8,\n    pub pm_timer_length: u8,\n    pub gpe0_block_length: u8,\n    pub gpe1_block_length: u8,\n    pub gpe1_base: u8,\n    pub c_state_control: u8,\n    /// The worst-case latency to enter and exit the C2 state, in microseconds. A value `>100` indicates that the\n    /// system does not support the C2 state.\n    pub worst_c2_latency: u16,\n    /// The worst-case latency to enter and exit the C3 state, in microseconds. A value `>1000` indicates that the\n    /// system does not support the C3 state.\n    pub worst_c3_latency: u16,\n    pub flush_size: u16,\n    pub flush_stride: u16,\n    pub duty_offset: u8,\n    pub duty_width: u8,\n    pub day_alarm: u8,\n    pub month_alarm: u8,\n    pub century: u8,\n    pub iapc_boot_arch: IaPcBootArchFlags,\n    _reserved2: u8, // must be 0\n    pub flags: FixedFeatureFlags,\n    pub reset_reg: RawGenericAddress,\n    pub reset_value: u8,\n    pub arm_boot_arch: ArmBootArchFlags,\n    pub fadt_minor_version: u8,\n    pub x_firmware_ctrl: ExtendedField<u64, 2>,\n    pub x_dsdt_address: ExtendedField<u64, 2>,\n    pub x_pm1a_event_block: ExtendedField<RawGenericAddress, 2>,\n    pub x_pm1b_event_block: ExtendedField<RawGenericAddress, 2>,\n    pub x_pm1a_control_block: ExtendedField<RawGenericAddress, 2>,\n    pub x_pm1b_control_block: ExtendedField<RawGenericAddress, 2>,\n    pub x_pm2_control_block: ExtendedField<RawGenericAddress, 2>,\n    pub x_pm_timer_block: ExtendedField<RawGenericAddress, 2>,\n    pub x_gpe0_block: ExtendedField<RawGenericAddress, 2>,\n    pub x_gpe1_block: ExtendedField<RawGenericAddress, 2>,\n    pub sleep_control_reg: ExtendedField<RawGenericAddress, 2>,\n    pub sleep_status_reg: ExtendedField<RawGenericAddress, 2>,\n    pub hypervisor_vendor_id: ExtendedField<u64, 2>,\n}\n\n/// ## Safety\n/// Implementation properly represents a valid FADT.\nunsafe impl AcpiTable for Fadt {\n    const SIGNATURE: Signature = Signature::FADT;\n\n    fn header(&self) -> &SdtHeader {\n        &self.header\n    }\n}\n\nimpl Fadt {\n    pub fn validate(&self) -> Result<(), AcpiError> {\n        unsafe { self.header.validate(crate::sdt::Signature::FADT) }\n    }\n\n    pub fn facs_address(&self) -> Result<usize, AcpiError> {\n        unsafe {\n            { self.x_firmware_ctrl }\n                .access(self.header.revision)\n                .filter(|&p| p != 0)\n                .or(Some(self.firmware_ctrl as u64))\n                .filter(|&p| p != 0)\n                .map(|p| p as usize)\n                .ok_or(AcpiError::InvalidFacsAddress)\n        }\n    }\n\n    pub fn dsdt_address(&self) -> Result<usize, AcpiError> {\n        unsafe {\n            { self.x_dsdt_address }\n                .access(self.header.revision)\n                .filter(|&p| p != 0)\n                .or(Some(self.dsdt_address as u64))\n                .filter(|&p| p != 0)\n                .map(|p| p as usize)\n                .ok_or(AcpiError::InvalidDsdtAddress)\n        }\n    }\n\n    pub fn power_profile(&self) -> PowerProfile {\n        match self.preferred_pm_profile {\n            0 => PowerProfile::Unspecified,\n            1 => PowerProfile::Desktop,\n            2 => PowerProfile::Mobile,\n            3 => PowerProfile::Workstation,\n            4 => PowerProfile::EnterpriseServer,\n            5 => PowerProfile::SohoServer,\n            6 => PowerProfile::AppliancePc,\n            7 => PowerProfile::PerformanceServer,\n            8 => PowerProfile::Tablet,\n            other => PowerProfile::Reserved(other),\n        }\n    }\n\n    pub fn pm1a_event_block(&self) -> Result<GenericAddress, AcpiError> {\n        if let Some(raw) = unsafe { self.x_pm1a_event_block.access(self.header().revision) }\n            && raw.address != 0x0\n        {\n            return GenericAddress::from_raw(raw);\n        }\n\n        Ok(GenericAddress {\n            address_space: AddressSpace::SystemIo,\n            bit_width: self.pm1_event_length * 8,\n            bit_offset: 0,\n            access_size: 0,\n            address: self.pm1a_event_block.into(),\n        })\n    }\n\n    pub fn pm1b_event_block(&self) -> Result<Option<GenericAddress>, AcpiError> {\n        if let Some(raw) = unsafe { self.x_pm1b_event_block.access(self.header().revision) }\n            && raw.address != 0x0\n        {\n            return Ok(Some(GenericAddress::from_raw(raw)?));\n        }\n\n        if self.pm1b_event_block != 0 {\n            Ok(Some(GenericAddress {\n                address_space: AddressSpace::SystemIo,\n                bit_width: self.pm1_event_length * 8,\n                bit_offset: 0,\n                access_size: 0,\n                address: self.pm1b_event_block.into(),\n            }))\n        } else {\n            Ok(None)\n        }\n    }\n\n    pub fn pm1a_control_block(&self) -> Result<GenericAddress, AcpiError> {\n        if let Some(raw) = unsafe { self.x_pm1a_control_block.access(self.header().revision) }\n            && raw.address != 0x0\n        {\n            return GenericAddress::from_raw(raw);\n        }\n\n        Ok(GenericAddress {\n            address_space: AddressSpace::SystemIo,\n            bit_width: self.pm1_control_length * 8,\n            bit_offset: 0,\n            access_size: 0,\n            address: self.pm1a_control_block.into(),\n        })\n    }\n\n    pub fn pm1b_control_block(&self) -> Result<Option<GenericAddress>, AcpiError> {\n        if let Some(raw) = unsafe { self.x_pm1b_control_block.access(self.header().revision) }\n            && raw.address != 0x0\n        {\n            return Ok(Some(GenericAddress::from_raw(raw)?));\n        }\n\n        if self.pm1b_control_block != 0 {\n            Ok(Some(GenericAddress {\n                address_space: AddressSpace::SystemIo,\n                bit_width: self.pm1_control_length * 8,\n                bit_offset: 0,\n                access_size: 0,\n                address: self.pm1b_control_block.into(),\n            }))\n        } else {\n            Ok(None)\n        }\n    }\n\n    pub fn pm2_control_block(&self) -> Result<Option<GenericAddress>, AcpiError> {\n        if let Some(raw) = unsafe { self.x_pm2_control_block.access(self.header().revision) }\n            && raw.address != 0x0\n        {\n            return Ok(Some(GenericAddress::from_raw(raw)?));\n        }\n\n        if self.pm2_control_block != 0 {\n            Ok(Some(GenericAddress {\n                address_space: AddressSpace::SystemIo,\n                bit_width: self.pm2_control_length * 8,\n                bit_offset: 0,\n                access_size: 0,\n                address: self.pm2_control_block.into(),\n            }))\n        } else {\n            Ok(None)\n        }\n    }\n\n    /// Attempts to parse the FADT's PWM timer blocks, first returning the extended block, and falling back to\n    /// parsing the legacy block into a `GenericAddress`.\n    pub fn pm_timer_block(&self) -> Result<Option<GenericAddress>, AcpiError> {\n        // ACPI spec indicates `PM_TMR_LEN` should be 4, or otherwise the PM_TMR is not supported.\n        if self.pm_timer_length != 4 {\n            return Ok(None);\n        }\n\n        if let Some(raw) = unsafe { self.x_pm_timer_block.access(self.header().revision) }\n            && raw.address != 0x0\n        {\n            return Ok(Some(GenericAddress::from_raw(raw)?));\n        }\n\n        if self.pm_timer_block != 0 {\n            Ok(Some(GenericAddress {\n                address_space: AddressSpace::SystemIo,\n                bit_width: 32,\n                bit_offset: 0,\n                access_size: 0,\n                address: self.pm_timer_block.into(),\n            }))\n        } else {\n            Ok(None)\n        }\n    }\n\n    pub fn gpe0_block(&self) -> Result<Option<GenericAddress>, AcpiError> {\n        if let Some(raw) = unsafe { self.x_gpe0_block.access(self.header().revision) }\n            && raw.address != 0x0\n        {\n            return Ok(Some(GenericAddress::from_raw(raw)?));\n        }\n\n        if self.gpe0_block != 0 {\n            Ok(Some(GenericAddress {\n                address_space: AddressSpace::SystemIo,\n                bit_width: self.gpe0_block_length * 8,\n                bit_offset: 0,\n                access_size: 0,\n                address: self.gpe0_block.into(),\n            }))\n        } else {\n            Ok(None)\n        }\n    }\n\n    pub fn gpe1_block(&self) -> Result<Option<GenericAddress>, AcpiError> {\n        if let Some(raw) = unsafe { self.x_gpe1_block.access(self.header().revision) }\n            && raw.address != 0x0\n        {\n            return Ok(Some(GenericAddress::from_raw(raw)?));\n        }\n\n        if self.gpe1_block != 0 {\n            Ok(Some(GenericAddress {\n                address_space: AddressSpace::SystemIo,\n                bit_width: self.gpe1_block_length * 8,\n                bit_offset: 0,\n                access_size: 0,\n                address: self.gpe1_block.into(),\n            }))\n        } else {\n            Ok(None)\n        }\n    }\n\n    pub fn reset_register(&self) -> Result<GenericAddress, AcpiError> {\n        GenericAddress::from_raw(self.reset_reg)\n    }\n\n    pub fn sleep_control_register(&self) -> Result<Option<GenericAddress>, AcpiError> {\n        if let Some(raw) = unsafe { self.sleep_control_reg.access(self.header().revision) } {\n            Ok(Some(GenericAddress::from_raw(raw)?))\n        } else {\n            Ok(None)\n        }\n    }\n\n    pub fn sleep_status_register(&self) -> Result<Option<GenericAddress>, AcpiError> {\n        if let Some(raw) = unsafe { self.sleep_status_reg.access(self.header().revision) } {\n            Ok(Some(GenericAddress::from_raw(raw)?))\n        } else {\n            Ok(None)\n        }\n    }\n}\n\n#[derive(Clone, Copy, Debug)]\npub struct FixedFeatureFlags(u32);\n\nimpl FixedFeatureFlags {\n    /// If true, an equivalent to the x86 [WBINVD](https://www.felixcloutier.com/x86/wbinvd) instruction is supported.\n    /// All caches will be flushed and invalidated upon completion of this instruction,\n    /// and memory coherency is properly maintained. The cache *SHALL* only contain what OSPM references or allows to be cached.\n    pub fn supports_equivalent_to_wbinvd(&self) -> bool {\n        self.0.get_bit(0)\n    }\n\n    /// If true, [WBINVD](https://www.felixcloutier.com/x86/wbinvd) properly flushes all caches and  memory coherency is maintained, but caches may not be invalidated.\n    pub fn wbinvd_flushes_all_caches(&self) -> bool {\n        self.0.get_bit(1)\n    }\n\n    /// If true, all processors implement the C1 power state.\n    pub fn all_procs_support_c1_power_state(&self) -> bool {\n        self.0.get_bit(2)\n    }\n\n    /// If true, the C2 power state is configured to work on a uniprocessor and multiprocessor system.\n    pub fn c2_configured_for_mp_system(&self) -> bool {\n        self.0.get_bit(3)\n    }\n\n    /// If true, the power button is handled as a control method device.\n    /// If false, the power button is handled as a fixed-feature programming model.\n    pub fn power_button_is_control_method(&self) -> bool {\n        self.0.get_bit(4)\n    }\n\n    /// If true, the sleep button is handled as a control method device.\n    /// If false, the sleep button is handled as a fixed-feature programming model.\n    pub fn sleep_button_is_control_method(&self) -> bool {\n        self.0.get_bit(5)\n    }\n\n    /// If true, the RTC wake status is not supported in fixed register space.\n    pub fn no_rtc_wake_in_fixed_register_space(&self) -> bool {\n        self.0.get_bit(6)\n    }\n\n    /// If true, the RTC alarm function can wake the system from an S4 sleep state.\n    pub fn rtc_wakes_system_from_s4(&self) -> bool {\n        self.0.get_bit(7)\n    }\n\n    /// If true, indicates that the PM timer is a 32-bit value.\n    /// If false, the PM timer is a 24-bit value and the remaining 8 bits are clear.\n    pub fn pm_timer_is_32_bit(&self) -> bool {\n        self.0.get_bit(8)\n    }\n\n    /// If true, the system supports docking.\n    pub fn supports_docking(&self) -> bool {\n        self.0.get_bit(9)\n    }\n\n    /// If true, the system supports system reset via the reset_reg field of the FADT.\n    pub fn supports_system_reset_via_fadt(&self) -> bool {\n        self.0.get_bit(10)\n    }\n\n    /// If true, the system supports no expansion capabilities and the case is sealed.\n    pub fn case_is_sealed(&self) -> bool {\n        self.0.get_bit(11)\n    }\n\n    /// If true, the system cannot detect the monitor or keyboard/mouse devices.\n    pub fn system_is_headless(&self) -> bool {\n        self.0.get_bit(12)\n    }\n\n    /// If true, OSPM must use a processor instruction after writing to the SLP_TYPx register.\n    pub fn use_instr_after_write_to_slp_typx(&self) -> bool {\n        self.0.get_bit(13)\n    }\n\n    /// If set, the platform supports the `PCIEXP_WAKE_STS` and `PCIEXP_WAKE_EN` bits in the PM1 status and enable registers.\n    pub fn supports_pciexp_wake_in_pm1(&self) -> bool {\n        self.0.get_bit(14)\n    }\n\n    /// If true, OSPM should use the ACPI power management timer or HPET for monotonically-decreasing timers.\n    pub fn use_pm_or_hpet_for_monotonically_decreasing_timers(&self) -> bool {\n        self.0.get_bit(15)\n    }\n\n    /// If true, the contents of the `RTC_STS` register are valid after wakeup from S4.\n    pub fn rtc_sts_is_valid_after_wakeup_from_s4(&self) -> bool {\n        self.0.get_bit(16)\n    }\n\n    /// If true, the platform supports OSPM leaving GPE wake events armed prior to an S5 transition.\n    pub fn ospm_may_leave_gpe_wake_events_armed_before_s5(&self) -> bool {\n        self.0.get_bit(17)\n    }\n\n    /// If true, all LAPICs must be configured using the cluster destination model when delivering interrupts in logical mode.\n    pub fn lapics_must_use_cluster_model_for_logical_mode(&self) -> bool {\n        self.0.get_bit(18)\n    }\n\n    /// If true, all LXAPICs must be configured using physical destination mode.\n    pub fn local_xapics_must_use_physical_destination_mode(&self) -> bool {\n        self.0.get_bit(19)\n    }\n\n    /// If true, this system is a hardware-reduced ACPI platform, and software methods are used for fixed-feature functions defined in chapter 4 of the ACPI specification.\n    pub fn system_is_hw_reduced_acpi(&self) -> bool {\n        self.0.get_bit(20)\n    }\n\n    /// If true, the system can achieve equal or better power savings in an S0 power state, making an S3 transition useless.\n    pub fn no_benefit_to_s3(&self) -> bool {\n        self.0.get_bit(21)\n    }\n}\n\n#[derive(Clone, Copy, Debug)]\npub struct IaPcBootArchFlags(u16);\n\nimpl IaPcBootArchFlags {\n    /// If true, legacy user-accessible devices are available on the LPC and/or ISA buses.\n    pub fn legacy_devices_are_accessible(&self) -> bool {\n        self.0.get_bit(0)\n    }\n\n    /// If true, the motherboard exposes an IO port 60/64 keyboard controller, typically implemented as an 8042 microcontroller.\n    pub fn motherboard_implements_8042(&self) -> bool {\n        self.0.get_bit(1)\n    }\n\n    /// If true, OSPM *must not* blindly probe VGA hardware.\n    /// VGA hardware is at MMIO addresses A0000h-BFFFFh and IO ports 3B0h-3BBh and 3C0h-3DFh.\n    pub fn dont_probe_vga(&self) -> bool {\n        self.0.get_bit(2)\n    }\n\n    /// If true, OSPM *must not* enable message-signaled interrupts.\n    pub fn dont_enable_msi(&self) -> bool {\n        self.0.get_bit(3)\n    }\n\n    /// If true, OSPM *must not* enable PCIe ASPM control.\n    pub fn dont_enable_pcie_aspm(&self) -> bool {\n        self.0.get_bit(4)\n    }\n\n    /// If true, OSPM *must not* use the RTC via its IO ports, either because it isn't implemented or is at other addresses;\n    /// instead, OSPM *MUST* use the time and alarm namespace device control method.\n    pub fn use_time_and_alarm_namespace_for_rtc(&self) -> bool {\n        self.0.get_bit(5)\n    }\n}\n\n#[derive(Clone, Copy, Debug)]\npub struct ArmBootArchFlags(u16);\n\nimpl ArmBootArchFlags {\n    /// If true, the system implements PSCI.\n    pub fn implements_psci(&self) -> bool {\n        self.0.get_bit(0)\n    }\n\n    /// If true, OSPM must use HVC instead of SMC as the PSCI conduit.\n    pub fn use_hvc_as_psci_conduit(&self) -> bool {\n        self.0.get_bit(1)\n    }\n}\n"
  },
  {
    "path": "src/sdt/hpet.rs",
    "content": "use crate::{\n    AcpiError,\n    AcpiTable,\n    AcpiTables,\n    Handler,\n    address::RawGenericAddress,\n    sdt::{SdtHeader, Signature},\n};\nuse bit_field::BitField;\nuse log::warn;\n\n#[derive(Debug)]\npub enum PageProtection {\n    None,\n    /// Access to the rest of the 4KiB, relative to the base address, will not generate a fault.\n    Protected4K,\n    /// Access to the rest of the 64KiB, relative to the base address, will not generate a fault.\n    Protected64K,\n    Other,\n}\n\n/// Information about the High Precision Event Timer (HPET)\n#[derive(Debug)]\npub struct HpetInfo {\n    pub hardware_rev: u8,\n    pub num_comparators: u8,\n    pub main_counter_is_64bits: bool,\n    pub legacy_irq_capable: bool,\n    pub pci_vendor_id: u16,\n    pub base_address: usize,\n    pub hpet_number: u8,\n    /// The minimum number of clock ticks that can be set without losing interrupts (for timers in Periodic Mode)\n    pub clock_tick_unit: u16,\n    pub page_protection: PageProtection,\n}\n\nimpl HpetInfo {\n    pub fn new<H>(tables: &AcpiTables<H>) -> Result<HpetInfo, AcpiError>\n    where\n        H: Handler,\n    {\n        let Some(hpet) = tables.find_table::<HpetTable>() else { Err(AcpiError::TableNotFound(Signature::HPET))? };\n\n        if hpet.base_address.address_space != 0 {\n            warn!(\"HPET reported as not in system memory; tables invalid?\");\n        }\n\n        let event_timer_block_id = hpet.event_timer_block_id;\n        Ok(HpetInfo {\n            hardware_rev: event_timer_block_id.get_bits(0..8) as u8,\n            num_comparators: event_timer_block_id.get_bits(8..13) as u8,\n            main_counter_is_64bits: event_timer_block_id.get_bit(13),\n            legacy_irq_capable: event_timer_block_id.get_bit(15),\n            pci_vendor_id: event_timer_block_id.get_bits(16..32) as u16,\n            base_address: hpet.base_address.address as usize,\n            hpet_number: hpet.hpet_number,\n            clock_tick_unit: hpet.clock_tick_unit,\n            page_protection: match hpet.page_protection_and_oem.get_bits(0..4) {\n                0 => PageProtection::None,\n                1 => PageProtection::Protected4K,\n                2 => PageProtection::Protected64K,\n                3..=15 => PageProtection::Other,\n                _ => unreachable!(),\n            },\n        })\n    }\n}\n\n#[repr(C, packed)]\n#[derive(Debug, Clone, Copy)]\npub struct HpetTable {\n    pub header: SdtHeader,\n    pub event_timer_block_id: u32,\n    pub base_address: RawGenericAddress,\n    pub hpet_number: u8,\n    pub clock_tick_unit: u16,\n    /// Bits `0..4` specify the page protection guarantee. Bits `4..8` are reserved for OEM attributes.\n    pub page_protection_and_oem: u8,\n}\n\nunsafe impl AcpiTable for HpetTable {\n    const SIGNATURE: Signature = Signature::HPET;\n\n    fn header(&self) -> &SdtHeader {\n        &self.header\n    }\n}\n"
  },
  {
    "path": "src/sdt/madt.rs",
    "content": "use crate::{\n    AcpiError,\n    AcpiTable,\n    sdt::{ExtendedField, SdtHeader, Signature},\n};\nuse bit_field::BitField;\nuse core::{\n    marker::{PhantomData, PhantomPinned},\n    mem,\n    pin::Pin,\n};\nuse log::warn;\n\n#[derive(Clone, Copy, Debug)]\npub enum MadtError {\n    UnexpectedEntry,\n    InterruptOverrideEntryHasInvalidBus,\n    InvalidLocalNmiLine,\n    MpsIntiInvalidPolarity,\n    MpsIntiInvalidTriggerMode,\n    WakeupApsTimeout,\n}\n\n/// Represents the MADT - this contains the MADT header fields. You can then iterate over a `Madt`\n/// to read each entry from it.\n///\n/// In modern versions of ACPI, the MADT can detail one of four interrupt models:\n/// - The ancient dual-i8259 legacy PIC model\n/// - The Advanced Programmable Interrupt Controller (APIC) model\n/// - The Streamlined Advanced Programmable Interrupt Controller (SAPIC) model (for Itanium systems)\n/// - The Generic Interrupt Controller (GIC) model (for ARM systems)\n///\n/// The MADT is a variable-sized structure consisting of a static header and then a variable number of entries.\n/// This type only contains the static portion, and then uses pointer arithmetic to parse the following entries.\n/// To make this sound, this type is `!Unpin` - this prevents `Madt` being moved, which would leave\n/// the entries behind.\n#[repr(C, packed)]\n#[derive(Debug)]\npub struct Madt {\n    pub header: SdtHeader,\n    pub local_apic_address: u32,\n    pub flags: u32,\n    _pinned: PhantomPinned,\n}\n\nunsafe impl AcpiTable for Madt {\n    const SIGNATURE: Signature = Signature::MADT;\n\n    fn header(&self) -> &SdtHeader {\n        &self.header\n    }\n}\n\nimpl Madt {\n    pub fn entries(self: Pin<&Self>) -> MadtEntryIter<'_> {\n        let ptr = unsafe { Pin::into_inner_unchecked(self) as *const Madt as *const u8 };\n        MadtEntryIter {\n            pointer: unsafe { ptr.add(mem::size_of::<Madt>()) },\n            remaining_length: self.header.length - mem::size_of::<Madt>() as u32,\n            _phantom: PhantomData,\n        }\n    }\n\n    pub fn supports_8259(&self) -> bool {\n        { self.flags }.get_bit(0)\n    }\n\n    pub fn get_mpwk_mailbox_addr(self: Pin<&Self>) -> Result<u64, AcpiError> {\n        for entry in self.entries() {\n            if let MadtEntry::MultiprocessorWakeup(entry) = entry {\n                return Ok(entry.mailbox_address);\n            }\n        }\n        Err(AcpiError::InvalidMadt(MadtError::UnexpectedEntry))\n    }\n}\n\n#[derive(Debug)]\npub struct MadtEntryIter<'a> {\n    pointer: *const u8,\n    /*\n     * The iterator can only have at most `u32::MAX` remaining bytes, because the length of the\n     * whole SDT can only be at most `u32::MAX`.\n     */\n    remaining_length: u32,\n    _phantom: PhantomData<&'a ()>,\n}\n\n#[derive(Debug)]\npub enum MadtEntry<'a> {\n    LocalApic(&'a LocalApicEntry),\n    IoApic(&'a IoApicEntry),\n    InterruptSourceOverride(&'a InterruptSourceOverrideEntry),\n    NmiSource(&'a NmiSourceEntry),\n    LocalApicNmi(&'a LocalApicNmiEntry),\n    LocalApicAddressOverride(&'a LocalApicAddressOverrideEntry),\n    IoSapic(&'a IoSapicEntry),\n    LocalSapic(&'a LocalSapicEntry),\n    PlatformInterruptSource(&'a PlatformInterruptSourceEntry),\n    LocalX2Apic(&'a LocalX2ApicEntry),\n    X2ApicNmi(&'a X2ApicNmiEntry),\n    Gicc(&'a GiccEntry),\n    Gicd(&'a GicdEntry),\n    GicMsiFrame(&'a GicMsiFrameEntry),\n    GicRedistributor(&'a GicRedistributorEntry),\n    GicInterruptTranslationService(&'a GicInterruptTranslationServiceEntry),\n    MultiprocessorWakeup(&'a MultiprocessorWakeupEntry),\n}\n\nimpl<'a> Iterator for MadtEntryIter<'a> {\n    type Item = MadtEntry<'a>;\n\n    fn next(&mut self) -> Option<Self::Item> {\n        while self.remaining_length > 0 {\n            let entry_pointer = self.pointer;\n            let header = unsafe { *(self.pointer as *const EntryHeader) };\n\n            if header.length as u32 > self.remaining_length {\n                warn!(\n                    \"Invalid entry of type {} in MADT - extending past length of table. Ignoring\",\n                    header.entry_type\n                );\n                return None;\n            }\n\n            self.pointer = unsafe { self.pointer.byte_offset(header.length as isize) };\n            self.remaining_length = self.remaining_length.saturating_sub(header.length as u32);\n\n            macro_rules! construct_entry {\n                ($entry_type:expr,\n                 $entry_pointer:expr,\n                 $(($value:expr => $variant:path as $type:ty)),*\n                ) => {\n                    match $entry_type {\n                        $(\n                            $value => {\n                                return Some($variant(unsafe {\n                                    &*($entry_pointer as *const $type)\n                                }))\n                            }\n                         )*\n\n                        /*\n                         * These entry types are reserved by the ACPI standard. We should skip them\n                         * if they appear in a real MADT.\n                         */\n                        0x11..=0x7f => {}\n\n                        /*\n                         * These entry types are reserved for OEM use. Atm, we just skip them too.\n                         * TODO: work out if we should ever do anything else here\n                         */\n                        0x80..=0xff => {}\n                    }\n                }\n            }\n\n            #[rustfmt::skip]\n            construct_entry!(\n                header.entry_type,\n                entry_pointer,\n                (0x0 => MadtEntry::LocalApic as LocalApicEntry),\n                (0x1 => MadtEntry::IoApic as IoApicEntry),\n                (0x2 => MadtEntry::InterruptSourceOverride as InterruptSourceOverrideEntry),\n                (0x3 => MadtEntry::NmiSource as NmiSourceEntry),\n                (0x4 => MadtEntry::LocalApicNmi as LocalApicNmiEntry),\n                (0x5 => MadtEntry::LocalApicAddressOverride as LocalApicAddressOverrideEntry),\n                (0x6 => MadtEntry::IoSapic as IoSapicEntry),\n                (0x7 => MadtEntry::LocalSapic as LocalSapicEntry),\n                (0x8 => MadtEntry::PlatformInterruptSource as PlatformInterruptSourceEntry),\n                (0x9 => MadtEntry::LocalX2Apic as LocalX2ApicEntry),\n                (0xa => MadtEntry::X2ApicNmi as X2ApicNmiEntry),\n                (0xb => MadtEntry::Gicc as GiccEntry),\n                (0xc => MadtEntry::Gicd as GicdEntry),\n                (0xd => MadtEntry::GicMsiFrame as GicMsiFrameEntry),\n                (0xe => MadtEntry::GicRedistributor as GicRedistributorEntry),\n                (0xf => MadtEntry::GicInterruptTranslationService as GicInterruptTranslationServiceEntry),\n                (0x10 => MadtEntry::MultiprocessorWakeup as MultiprocessorWakeupEntry)\n            );\n        }\n\n        None\n    }\n}\n\n#[derive(Clone, Copy, Debug)]\n#[repr(C, packed)]\npub struct EntryHeader {\n    pub entry_type: u8,\n    pub length: u8,\n}\n\n#[derive(Clone, Copy, Debug)]\n#[repr(C, packed)]\npub struct LocalApicEntry {\n    pub header: EntryHeader,\n    pub processor_id: u8,\n    pub apic_id: u8,\n    pub flags: u32,\n}\n\n#[derive(Clone, Copy, Debug)]\n#[repr(C, packed)]\npub struct IoApicEntry {\n    pub header: EntryHeader,\n    pub io_apic_id: u8,\n    _reserved: u8,\n    pub io_apic_address: u32,\n    pub global_system_interrupt_base: u32,\n}\n\n#[derive(Clone, Copy, Debug)]\n#[repr(C, packed)]\npub struct InterruptSourceOverrideEntry {\n    pub header: EntryHeader,\n    pub bus: u8, // 0 - ISA bus\n    pub irq: u8, // This is bus-relative\n    pub global_system_interrupt: u32,\n    pub flags: u16,\n}\n\n#[derive(Clone, Copy, Debug)]\n#[repr(C, packed)]\npub struct NmiSourceEntry {\n    pub header: EntryHeader,\n    pub flags: u16,\n    pub global_system_interrupt: u32,\n}\n\n#[derive(Clone, Copy, Debug)]\n#[repr(C, packed)]\npub struct LocalApicNmiEntry {\n    pub header: EntryHeader,\n    pub processor_id: u8,\n    pub flags: u16,\n    pub nmi_line: u8, // Describes which LINTn is the NMI connected to\n}\n\n#[derive(Clone, Copy, Debug)]\n#[repr(C, packed)]\npub struct LocalApicAddressOverrideEntry {\n    pub header: EntryHeader,\n    _reserved: u16,\n    pub local_apic_address: u64,\n}\n\n/// If this entry is present, the system has an I/O SAPIC, which must be used instead of the I/O\n/// APIC.\n#[derive(Clone, Copy, Debug)]\n#[repr(C, packed)]\npub struct IoSapicEntry {\n    pub header: EntryHeader,\n    pub io_apic_id: u8,\n    _reserved: u8,\n    pub global_system_interrupt_base: u32,\n    pub io_sapic_address: u64,\n}\n\n#[derive(Clone, Copy, Debug)]\n#[repr(C, packed)]\npub struct LocalSapicEntry {\n    pub header: EntryHeader,\n    pub processor_id: u8,\n    pub local_sapic_id: u8,\n    pub local_sapic_eid: u8,\n    _reserved: [u8; 3],\n    pub flags: u32,\n    pub processor_uid: u32,\n\n    /// This string can be used to associate this local SAPIC to a processor defined in the\n    /// namespace when the `_UID` object is a string. It is a null-terminated ASCII string, and so\n    /// this field will be `'\\0'` if the string is not present, otherwise it extends from the\n    /// address of this field.\n    pub processor_uid_string: u8,\n}\n\n#[derive(Clone, Copy, Debug)]\n#[repr(C, packed)]\npub struct PlatformInterruptSourceEntry {\n    pub header: EntryHeader,\n    pub flags: u16,\n    pub interrupt_type: u8,\n    pub processor_id: u8,\n    pub processor_eid: u8,\n    pub io_sapic_vector: u8,\n    pub global_system_interrupt: u32,\n    pub platform_interrupt_source_flags: u32,\n}\n\n#[derive(Clone, Copy, Debug)]\n#[repr(C, packed)]\npub struct LocalX2ApicEntry {\n    pub header: EntryHeader,\n    _reserved: u16,\n    pub x2apic_id: u32,\n    pub flags: u32,\n    pub processor_uid: u32,\n}\n\n#[derive(Clone, Copy, Debug)]\n#[repr(C, packed)]\npub struct X2ApicNmiEntry {\n    pub header: EntryHeader,\n    pub flags: u16,\n    pub processor_uid: u32,\n    pub nmi_line: u8,\n    _reserved: [u8; 3],\n}\n\n/// This field will appear for ARM processors that support ACPI and use the Generic Interrupt\n/// Controller. In the GICC interrupt model, each logical process has a Processor Device object in\n/// the namespace, and uses this structure to convey its GIC information.\n#[derive(Clone, Copy, Debug)]\n#[repr(C, packed)]\npub struct GiccEntry {\n    pub header: EntryHeader,\n    _reserved1: u16,\n    pub cpu_interface_number: u32,\n    pub processor_uid: u32,\n    pub flags: u32,\n    pub parking_protocol_version: u32,\n    pub performance_interrupt_gsiv: u32,\n    pub parked_address: u64,\n    pub gic_registers_address: u64,\n    pub gic_virtual_registers_address: u64,\n    pub gic_hypervisor_registers_address: u64,\n    pub vgic_maintenance_interrupt: u32,\n    pub gicr_base_address: u64,\n    pub mpidr: u64,\n    pub processor_power_efficiency_class: u8,\n    _reserved2: u8,\n    /// SPE overflow Interrupt.\n    ///\n    /// ACPI 6.3 defined this field. It is zero in prior versions or\n    /// if this processor does not support SPE.\n    pub spe_overflow_interrupt: u16,\n    pub trbe_interrupt: ExtendedField<u16, 6>,\n}\n\n#[derive(Clone, Copy, Debug)]\n#[repr(C, packed)]\npub struct GicdEntry {\n    pub header: EntryHeader,\n    _reserved1: u16,\n    pub gic_id: u32,\n    pub physical_base_address: u64,\n    pub system_vector_base: u32,\n\n    /// The GIC version\n    ///     0x00: Fall back to hardware discovery\n    ///     0x01: GICv1\n    ///     0x02: GICv2\n    ///     0x03: GICv3\n    ///     0x04: GICv4\n    ///     0x05-0xff: Reserved for future use\n    pub gic_version: u8,\n    _reserved2: [u8; 3],\n}\n\n#[derive(Clone, Copy, Debug)]\n#[repr(C, packed)]\npub struct GicMsiFrameEntry {\n    pub header: EntryHeader,\n    _reserved: u16,\n    pub frame_id: u32,\n    pub physical_base_address: u64,\n    pub flags: u32,\n    pub spi_count: u16,\n    pub spi_base: u16,\n}\n\n#[derive(Clone, Copy, Debug)]\n#[repr(C, packed)]\npub struct GicRedistributorEntry {\n    pub header: EntryHeader,\n    _reserved: u16,\n    pub discovery_range_base_address: u64,\n    pub discovery_range_length: u32,\n}\n\n#[derive(Clone, Copy, Debug)]\n#[repr(C, packed)]\npub struct GicInterruptTranslationServiceEntry {\n    pub header: EntryHeader,\n    _reserved1: u16,\n    pub id: u32,\n    pub physical_base_address: u64,\n    _reserved2: u32,\n}\n\n#[derive(Clone, Copy, Debug)]\n#[repr(C, packed)]\npub struct MultiprocessorWakeupEntry {\n    pub header: EntryHeader,\n    pub mailbox_version: u16,\n    _reserved: u32,\n    pub mailbox_address: u64,\n}\n\n#[derive(Debug, PartialEq, Eq)]\npub enum MpProtectedModeWakeupCommand {\n    Noop = 0,\n    Wakeup = 1,\n    Sleep = 2,\n    AcceptPages = 3,\n}\n\nimpl From<u16> for MpProtectedModeWakeupCommand {\n    fn from(value: u16) -> Self {\n        match value {\n            0 => MpProtectedModeWakeupCommand::Noop,\n            1 => MpProtectedModeWakeupCommand::Wakeup,\n            2 => MpProtectedModeWakeupCommand::Sleep,\n            3 => MpProtectedModeWakeupCommand::AcceptPages,\n            _ => panic!(\"Invalid value for MpProtectedModeWakeupCommand\"),\n        }\n    }\n}\n\n#[repr(C)]\npub struct MultiprocessorWakeupMailbox {\n    pub command: u16,\n    _reserved: u16,\n    pub apic_id: u32,\n    pub wakeup_vector: u64,\n    pub reserved_for_os: [u64; 254],\n    pub reserved_for_firmware: [u64; 256],\n}\n\n/// Polarity indicates what signal mode the interrupt line needs to be in to be considered 'active'.\n#[derive(Debug, Clone, Copy, PartialEq, Eq)]\npub enum Polarity {\n    SameAsBus,\n    ActiveHigh,\n    ActiveLow,\n}\n\n/// Trigger mode of an interrupt, describing how the interrupt is triggered.\n///\n/// When an interrupt is `Edge` triggered, it is triggered exactly once, when the interrupt\n/// signal goes from its opposite polarity to its active polarity.\n///\n/// For `Level` triggered interrupts, a continuous signal is emitted so long as the interrupt\n/// is in its active polarity.\n///\n/// `SameAsBus`-triggered interrupts will utilize the same interrupt triggering as the system bus\n/// they communicate across.\n#[derive(Debug, Clone, Copy, PartialEq, Eq)]\npub enum TriggerMode {\n    SameAsBus,\n    Edge,\n    Level,\n}\n\npub fn parse_mps_inti_flags(flags: u16) -> Result<(Polarity, TriggerMode), AcpiError> {\n    let polarity = match flags.get_bits(0..2) {\n        0b00 => Polarity::SameAsBus,\n        0b01 => Polarity::ActiveHigh,\n        0b11 => Polarity::ActiveLow,\n        _ => return Err(AcpiError::InvalidMadt(MadtError::MpsIntiInvalidPolarity)),\n    };\n\n    let trigger_mode = match flags.get_bits(2..4) {\n        0b00 => TriggerMode::SameAsBus,\n        0b01 => TriggerMode::Edge,\n        0b11 => TriggerMode::Level,\n        _ => return Err(AcpiError::InvalidMadt(MadtError::MpsIntiInvalidTriggerMode)),\n    };\n\n    Ok((polarity, trigger_mode))\n}\n"
  },
  {
    "path": "src/sdt/mcfg.rs",
    "content": "use crate::{\n    AcpiTable,\n    sdt::{SdtHeader, Signature},\n};\nuse core::{fmt, mem, slice};\n\n#[repr(C, packed)]\npub struct Mcfg {\n    pub header: SdtHeader,\n    _reserved: u64,\n    // Followed by `n` entries with format `McfgEntry`\n}\n\n/// ### Safety: Implementation properly represents a valid MCFG.\nunsafe impl AcpiTable for Mcfg {\n    const SIGNATURE: Signature = Signature::MCFG;\n\n    fn header(&self) -> &SdtHeader {\n        &self.header\n    }\n}\n\nimpl Mcfg {\n    /// Returns a slice containing each of the entries in the MCFG table. Where possible, `PlatformInfo.interrupt_model` should\n    /// be enumerated instead.\n    pub fn entries(&self) -> &[McfgEntry] {\n        let length = self.header.length as usize - mem::size_of::<Mcfg>();\n\n        // Intentionally round down in case length isn't an exact multiple of McfgEntry size - this\n        // has been observed on real hardware (see rust-osdev/acpi#58)\n        let num_entries = length / mem::size_of::<McfgEntry>();\n\n        unsafe {\n            let pointer = (self as *const Mcfg as *const u8).add(mem::size_of::<Mcfg>()) as *const McfgEntry;\n            slice::from_raw_parts(pointer, num_entries)\n        }\n    }\n}\n\nimpl fmt::Debug for Mcfg {\n    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {\n        formatter.debug_struct(\"Mcfg\").field(\"header\", &self.header).field(\"entries\", &self.entries()).finish()\n    }\n}\n\n#[derive(Clone, Copy, Debug)]\n#[repr(C, packed)]\npub struct McfgEntry {\n    pub base_address: u64,\n    pub pci_segment_group: u16,\n    pub bus_number_start: u8,\n    pub bus_number_end: u8,\n    _reserved: u32,\n}\n"
  },
  {
    "path": "src/sdt/mod.rs",
    "content": "pub mod bgrt;\npub mod facs;\npub mod fadt;\npub mod hpet;\npub mod madt;\npub mod mcfg;\npub mod slit;\npub mod spcr;\npub mod srat;\n\nuse crate::AcpiError;\nuse core::{fmt, mem::MaybeUninit, str};\n\n/// Represents a field which may or may not be present within an ACPI structure, depending on the version of ACPI\n/// that a system supports. If the field is not present, it is not safe to treat the data as initialised.\n#[derive(Debug, Clone, Copy)]\n#[repr(transparent)]\npub struct ExtendedField<T: Copy, const MIN_REVISION: u8>(MaybeUninit<T>);\n\nimpl<T: Copy, const MIN_REVISION: u8> ExtendedField<T, MIN_REVISION> {\n    /// Access the field if it's present for the given revision of the table.\n    ///\n    /// ### Safety\n    /// If a bogus ACPI version is passed, this function may access uninitialised data.\n    pub unsafe fn access(&self, revision: u8) -> Option<T> {\n        if revision >= MIN_REVISION { Some(unsafe { self.0.assume_init() }) } else { None }\n    }\n}\n\n/// All SDTs share the same header, and are `length` bytes long. The signature tells us which SDT\n/// this is.\n///\n/// The ACPI Spec (Version 6.4) defines the following SDT signatures:\n///\n/// * APIC - Multiple APIC Description Table (MADT)\n/// * BERT - Boot Error Record Table\n/// * BGRT - Boot Graphics Resource Table\n/// * CPEP - Corrected Platform Error Polling Table\n/// * DSDT - Differentiated System Description Table (DSDT)\n/// * ECDT - Embedded Controller Boot Resources Table\n/// * EINJ - Error Injection Table\n/// * ERST - Error Record Serialization Table\n/// * FACP - Fixed ACPI Description Table (FADT)\n/// * FACS - Firmware ACPI Control Structure\n/// * FPDT - Firmware Performance Data Table\n/// * GTDT - Generic Timer Description Table\n/// * HEST - Hardware Error Source Table\n/// * MSCT - Maximum System Characteristics Table\n/// * MPST - Memory Power StateTable\n/// * NFIT - NVDIMM Firmware Interface Table\n/// * OEMx - OEM Specific Information Tables\n/// * PCCT - Platform Communications Channel Table\n/// * PHAT - Platform Health Assessment Table\n/// * PMTT - Platform Memory Topology Table\n/// * PSDT - Persistent System Description Table\n/// * RASF - ACPI RAS Feature Table\n/// * RSDT - Root System Description Table\n/// * SBST - Smart Battery Specification Table\n/// * SDEV - Secure DEVices Table\n/// * SLIT - System Locality Distance Information Table\n/// * SRAT - System Resource Affinity Table\n/// * SSDT - Secondary System Description Table\n/// * XSDT - Extended System Description Table\n///\n/// ACPI also reserves the following signatures and the specifications for them can be found [here](https://uefi.org/acpi):\n///\n/// * AEST - ARM Error Source Table\n/// * BDAT - BIOS Data ACPI Table\n/// * CDIT - Component Distance Information Table\n/// * CEDT - CXL Early Discovery Table\n/// * CRAT - Component Resource Attribute Table\n/// * CSRT - Core System Resource Table\n/// * DBGP - Debug Port Table\n/// * DBG2 - Debug Port Table 2 (note: ACPI 6.4 defines this as \"DBPG2\" but this is incorrect)\n/// * DMAR - DMA Remapping Table\n/// * DRTM -Dynamic Root of Trust for Measurement Table\n/// * ETDT - Event Timer Description Table (obsolete, superseeded by HPET)\n/// * HPET - IA-PC High Precision Event Timer Table\n/// * IBFT - iSCSI Boot Firmware Table\n/// * IORT - I/O Remapping Table\n/// * IVRS - I/O Virtualization Reporting Structure\n/// * LPIT - Low Power Idle Table\n/// * MCFG - PCI Express Memory-mapped Configuration Space base address description table\n/// * MCHI - Management Controller Host Interface table\n/// * MPAM - ARM Memory Partitioning And Monitoring table\n/// * MSDM - Microsoft Data Management Table\n/// * PRMT - Platform Runtime Mechanism Table\n/// * RGRT - Regulatory Graphics Resource Table\n/// * SDEI - Software Delegated Exceptions Interface table\n/// * SLIC - Microsoft Software Licensing table\n/// * SPCR - Microsoft Serial Port Console Redirection table\n/// * SPMI - Server Platform Management Interface table\n/// * STAO - _STA Override table\n/// * SVKL - Storage Volume Key Data table (Intel TDX only)\n/// * TCPA - Trusted Computing Platform Alliance Capabilities Table\n/// * TPM2 - Trusted Platform Module 2 Table\n/// * UEFI - Unified Extensible Firmware Interface Specification table\n/// * WAET - Windows ACPI Emulated Devices Table\n/// * WDAT - Watch Dog Action Table\n/// * WDRT - Watchdog Resource Table\n/// * WPBT - Windows Platform Binary Table\n/// * WSMT - Windows Security Mitigations Table\n/// * XENV - Xen Project\n#[derive(Debug, Clone, Copy)]\n#[repr(C, packed)]\npub struct SdtHeader {\n    pub signature: Signature,\n    // TODO: Make sure this and other fields are interpreted as little-endian on big-endian machine.\n    pub length: u32,\n    pub revision: u8,\n    pub checksum: u8,\n    pub oem_id: [u8; 6],\n    pub oem_table_id: [u8; 8],\n    pub oem_revision: u32,\n    pub creator_id: [u8; 4],\n    pub creator_revision: u32,\n}\n\nimpl SdtHeader {\n    /// Checks that:\n    /// 1. The signature matches the one given.\n    /// 2. The values of various fields in the header are allowed.\n    /// 3. The checksum of the SDT is valid.\n    ///\n    /// ### Safety\n    /// The entire `length` bytes of the SDT must be mapped to compute the checksum.\n    pub unsafe fn validate(&self, signature: Signature) -> Result<(), AcpiError> {\n        // Check the signature\n        if self.signature != signature || str::from_utf8(&self.signature.0).is_err() {\n            return Err(AcpiError::SdtInvalidSignature(signature));\n        }\n\n        // Check the OEM id\n        if str::from_utf8(&self.oem_id).is_err() {\n            return Err(AcpiError::SdtInvalidOemId(signature));\n        }\n\n        // Check the OEM table id\n        if str::from_utf8(&self.oem_table_id).is_err() {\n            return Err(AcpiError::SdtInvalidTableId(signature));\n        }\n\n        // Check the checksum\n        let table_bytes =\n            unsafe { core::slice::from_raw_parts((self as *const SdtHeader).cast::<u8>(), self.length as usize) };\n        let sum = table_bytes.iter().fold(0u8, |sum, &byte| sum.wrapping_add(byte));\n        if sum != 0 {\n            return Err(AcpiError::SdtInvalidChecksum(signature));\n        }\n\n        Ok(())\n    }\n\n    #[inline]\n    pub fn length(&self) -> u32 {\n        self.length\n    }\n\n    #[inline]\n    pub fn revision(&self) -> u8 {\n        self.revision\n    }\n\n    #[inline]\n    pub fn checksum(&self) -> u8 {\n        self.checksum\n    }\n\n    pub fn oem_id(&self) -> Result<&str, AcpiError> {\n        str::from_utf8(&self.oem_id).map_err(|_| AcpiError::SdtInvalidOemId(self.signature))\n    }\n\n    pub fn oem_table_id(&self) -> Result<&str, AcpiError> {\n        str::from_utf8(&self.oem_table_id).map_err(|_| AcpiError::SdtInvalidTableId(self.signature))\n    }\n\n    #[inline]\n    pub fn oem_revision(&self) -> u32 {\n        self.oem_revision\n    }\n\n    #[inline]\n    pub fn creator_id(&self) -> Result<&str, AcpiError> {\n        str::from_utf8(&self.creator_id).map_err(|_| AcpiError::SdtInvalidCreatorId(self.signature))\n    }\n\n    #[inline]\n    pub fn creator_revision(&self) -> u32 {\n        self.creator_revision\n    }\n}\n\n#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]\n#[repr(transparent)]\npub struct Signature([u8; 4]);\n\nimpl Signature {\n    pub const RSDT: Signature = Signature(*b\"RSDT\");\n    pub const XSDT: Signature = Signature(*b\"XSDT\");\n    pub const FADT: Signature = Signature(*b\"FACP\");\n    pub const HPET: Signature = Signature(*b\"HPET\");\n    pub const MADT: Signature = Signature(*b\"APIC\");\n    pub const MCFG: Signature = Signature(*b\"MCFG\");\n    pub const SSDT: Signature = Signature(*b\"SSDT\");\n    pub const BERT: Signature = Signature(*b\"BERT\");\n    pub const BGRT: Signature = Signature(*b\"BGRT\");\n    pub const CPEP: Signature = Signature(*b\"CPEP\");\n    pub const DSDT: Signature = Signature(*b\"DSDT\");\n    pub const ECDT: Signature = Signature(*b\"ECDT\");\n    pub const EINJ: Signature = Signature(*b\"EINJ\");\n    pub const ERST: Signature = Signature(*b\"ERST\");\n    pub const FACS: Signature = Signature(*b\"FACS\");\n    pub const FPDT: Signature = Signature(*b\"FPDT\");\n    pub const GTDT: Signature = Signature(*b\"GTDT\");\n    pub const HEST: Signature = Signature(*b\"HEST\");\n    pub const MSCT: Signature = Signature(*b\"MSCT\");\n    pub const MPST: Signature = Signature(*b\"MPST\");\n    pub const NFIT: Signature = Signature(*b\"NFIT\");\n    pub const PCCT: Signature = Signature(*b\"PCCT\");\n    pub const PHAT: Signature = Signature(*b\"PHAT\");\n    pub const PMTT: Signature = Signature(*b\"PMTT\");\n    pub const PSDT: Signature = Signature(*b\"PSDT\");\n    pub const RASF: Signature = Signature(*b\"RASF\");\n    pub const SBST: Signature = Signature(*b\"SBST\");\n    pub const SDEV: Signature = Signature(*b\"SDEV\");\n    pub const SLIT: Signature = Signature(*b\"SLIT\");\n    pub const SRAT: Signature = Signature(*b\"SRAT\");\n    pub const AEST: Signature = Signature(*b\"AEST\");\n    pub const BDAT: Signature = Signature(*b\"BDAT\");\n    pub const CDIT: Signature = Signature(*b\"CDIT\");\n    pub const CEDT: Signature = Signature(*b\"CEDT\");\n    pub const CRAT: Signature = Signature(*b\"CRAT\");\n    pub const CSRT: Signature = Signature(*b\"CSRT\");\n    pub const DBGP: Signature = Signature(*b\"DBGP\");\n    pub const DBG2: Signature = Signature(*b\"DBG2\");\n    pub const DMAR: Signature = Signature(*b\"DMAR\");\n    pub const DRTM: Signature = Signature(*b\"DRTM\");\n    pub const ETDT: Signature = Signature(*b\"ETDT\");\n    pub const IBFT: Signature = Signature(*b\"IBFT\");\n    pub const IORT: Signature = Signature(*b\"IORT\");\n    pub const IVRS: Signature = Signature(*b\"IVRS\");\n    pub const LPIT: Signature = Signature(*b\"LPIT\");\n    pub const MCHI: Signature = Signature(*b\"MCHI\");\n    pub const MPAM: Signature = Signature(*b\"MPAM\");\n    pub const MSDM: Signature = Signature(*b\"MSDM\");\n    pub const PRMT: Signature = Signature(*b\"PRMT\");\n    pub const RGRT: Signature = Signature(*b\"RGRT\");\n    pub const SDEI: Signature = Signature(*b\"SDEI\");\n    pub const SLIC: Signature = Signature(*b\"SLIC\");\n    pub const SPCR: Signature = Signature(*b\"SPCR\");\n    pub const SPMI: Signature = Signature(*b\"SPMI\");\n    pub const STAO: Signature = Signature(*b\"STAO\");\n    pub const SVKL: Signature = Signature(*b\"SVKL\");\n    pub const TCPA: Signature = Signature(*b\"TCPA\");\n    pub const TPM2: Signature = Signature(*b\"TPM2\");\n    pub const UEFI: Signature = Signature(*b\"UEFI\");\n    pub const WAET: Signature = Signature(*b\"WAET\");\n    pub const WDAT: Signature = Signature(*b\"WDAT\");\n    pub const WDRT: Signature = Signature(*b\"WDRT\");\n    pub const WPBT: Signature = Signature(*b\"WPBT\");\n    pub const WSMT: Signature = Signature(*b\"WSMT\");\n    pub const XENV: Signature = Signature(*b\"XENV\");\n\n    pub fn as_str(&self) -> &str {\n        str::from_utf8(&self.0).unwrap()\n    }\n}\n\nimpl fmt::Display for Signature {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        write!(f, \"{}\", self.as_str())\n    }\n}\n\nimpl fmt::Debug for Signature {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        write!(f, \"\\\"{}\\\"\", self.as_str())\n    }\n}\n"
  },
  {
    "path": "src/sdt/slit.rs",
    "content": "use crate::{\n    AcpiTable,\n    sdt::{SdtHeader, Signature},\n};\nuse core::{marker::PhantomPinned, mem, pin::Pin, slice};\nuse log::warn;\n\n/// The SLIT (System Locality Information Table) provides a matrix of relative distances between\n/// all proximity domains. It encodes a list of N*N entries, where each entry is `u8` and the\n/// relative distance between proximity domains `(i, j)` is the entry at `i*N+j`. The distance\n/// between a proximity domain and itself is normalised to a value of `10`.\n#[derive(Debug)]\n#[repr(C, packed)]\npub struct Slit {\n    pub header: SdtHeader,\n    pub num_proximity_domains: u64,\n    _pinned: PhantomPinned,\n}\n\nunsafe impl AcpiTable for Slit {\n    const SIGNATURE: Signature = Signature::SLIT;\n\n    fn header(&self) -> &SdtHeader {\n        &self.header\n    }\n}\n\nimpl Slit {\n    pub fn matrix(self: Pin<&Self>) -> DistanceMatrix<'_> {\n        DistanceMatrix { num_proximity_domains: self.num_proximity_domains, matrix: self.matrix_raw() }\n    }\n\n    pub fn matrix_raw(self: Pin<&Self>) -> &[u8] {\n        let mut num_entries = self.num_proximity_domains * self.num_proximity_domains;\n        if (mem::size_of::<Slit>() + num_entries as usize * num_entries as usize * mem::size_of::<u8>())\n            > self.header.length as usize\n        {\n            warn!(\"SLIT too short for given number of proximity domains! Returning empty matrix\");\n            num_entries = 0;\n        }\n\n        unsafe {\n            let ptr = Pin::into_inner_unchecked(self) as *const Slit as *const u8;\n            slice::from_raw_parts(ptr.byte_add(mem::size_of::<Slit>()), num_entries as usize)\n        }\n    }\n}\n\npub struct DistanceMatrix<'a> {\n    pub num_proximity_domains: u64,\n    pub matrix: &'a [u8],\n}\n\nimpl DistanceMatrix<'_> {\n    pub fn distance(&self, i: u32, j: u32) -> Option<u8> {\n        if i as u64 <= self.num_proximity_domains && j as u64 <= self.num_proximity_domains {\n            Some(self.matrix[i as usize + j as usize * self.num_proximity_domains as usize])\n        } else {\n            None\n        }\n    }\n}\n"
  },
  {
    "path": "src/sdt/spcr.rs",
    "content": "use crate::{\n    AcpiError,\n    AcpiTable,\n    SdtHeader,\n    Signature,\n    address::{GenericAddress, RawGenericAddress},\n};\nuse core::{\n    num::{NonZeroU8, NonZeroU32},\n    ptr,\n    slice,\n    str::{self, Utf8Error},\n};\n\n/// Serial Port Console Redirection (SPCR) Table.\n///\n/// The table provides information about the configuration and use of the\n/// serial port or non-legacy UART interface. On a system where the BIOS or\n/// system firmware uses the serial port for console input/output, this table\n/// should be used to convey information about the settings.\n///\n/// For more information, see [the official documentation](https://learn.microsoft.com/en-us/windows-hardware/drivers/serports/serial-port-console-redirection-table).\n#[repr(C, packed)]\n#[derive(Debug)]\npub struct Spcr {\n    pub header: SdtHeader,\n    pub interface_type: u8,\n    _reserved: [u8; 3],\n    pub base_address: RawGenericAddress,\n    pub interrupt_type: u8,\n    pub irq: u8,\n    pub global_system_interrupt: u32,\n    /// The baud rate the BIOS used for redirection.\n    pub configured_baud_rate: u8,\n    pub parity: u8,\n    pub stop_bits: u8,\n    pub flow_control: u8,\n    pub terminal_type: u8,\n    /// Language which the BIOS was redirecting. Must be 0.\n    pub language: u8,\n    pub pci_device_id: u16,\n    pub pci_vendor_id: u16,\n    pub pci_bus_number: u8,\n    pub pci_device_number: u8,\n    pub pci_function_number: u8,\n    pub pci_flags: u32,\n    /// PCI segment number. systems with fewer than 255 PCI buses, this number\n    /// will be 0.\n    pub pci_segment: u8,\n    pub uart_clock_freq: u32,\n    pub precise_baud_rate: u32,\n    pub namespace_string_length: u16,\n    pub namespace_string_offset: u16,\n}\n\nunsafe impl AcpiTable for Spcr {\n    const SIGNATURE: Signature = Signature::SPCR;\n\n    fn header(&self) -> &SdtHeader {\n        &self.header\n    }\n}\n\nimpl Spcr {\n    /// Gets the type of the register interface.\n    pub fn interface_type(&self) -> SpcrInterfaceType {\n        SpcrInterfaceType::from(self.interface_type)\n    }\n\n    /// The base address of the Serial Port register set, if if console\n    /// redirection is enabled.\n    pub fn base_address(&self) -> Option<Result<GenericAddress, AcpiError>> {\n        (!self.base_address.is_empty()).then(|| GenericAddress::from_raw(self.base_address))\n    }\n\n    fn configured_baud_rate(&self) -> Option<NonZeroU32> {\n        match self.configured_baud_rate {\n            3 => unsafe { Some(NonZeroU32::new_unchecked(9600)) },\n            4 => unsafe { Some(NonZeroU32::new_unchecked(19200)) },\n            6 => unsafe { Some(NonZeroU32::new_unchecked(57600)) },\n            7 => unsafe { Some(NonZeroU32::new_unchecked(115200)) },\n            _ => None,\n        }\n    }\n\n    /// The baud rate the BIOS used for redirection, if configured.\n    pub fn baud_rate(&self) -> Option<NonZeroU32> {\n        NonZeroU32::new(self.precise_baud_rate).or_else(|| self.configured_baud_rate())\n    }\n\n    /// Flow control flags for the UART.\n    pub fn flow_control(&self) -> SpcrFlowControl {\n        SpcrFlowControl::from_bits_truncate(self.flow_control)\n    }\n\n    /// Interrupt type(s) used by the UART.\n    pub fn interrupt_type(&self) -> SpcrInterruptType {\n        SpcrInterruptType::from_bits_truncate(self.interrupt_type)\n    }\n\n    /// The PC-AT-compatible IRQ used by the UART, if the UART supports it.\n    /// Support is indicated by the [`interrupt_type`](Self::interrupt_type).\n    pub fn irq(&self) -> Option<u8> {\n        self.interrupt_type().contains(SpcrInterruptType::DUAL_8259).then_some(self.irq)\n    }\n\n    /// The Global System Interrupt (GSIV) used by the UART, if the UART\n    /// supports it. Support is indicated by the\n    /// [`interrupt_type`](Self::interrupt_type).\n    pub fn global_system_interrupt(&self) -> Option<u32> {\n        if self.interrupt_type().difference(SpcrInterruptType::DUAL_8259).is_empty() {\n            return None;\n        }\n        Some(self.global_system_interrupt)\n    }\n\n    /// The terminal protocol the BIOS was using for console redirection.\n    pub fn terminal_type(&self) -> SpcrTerminalType {\n        SpcrTerminalType::from_bits_truncate(self.terminal_type)\n    }\n\n    /// If the UART is a PCI device, returns its Device ID.\n    pub fn pci_device_id(&self) -> Option<u16> {\n        (self.pci_device_id != 0xffff).then_some(self.pci_device_id)\n    }\n\n    /// If the UART is a PCI device, returns its Vendor ID.\n    pub fn pci_vendor_id(&self) -> Option<u16> {\n        (self.pci_vendor_id != 0xffff).then_some(self.pci_vendor_id)\n    }\n\n    /// If the UART is a PCI device, returns its bus number.\n    pub fn pci_bus_number(&self) -> Option<NonZeroU8> {\n        NonZeroU8::new(self.pci_bus_number)\n    }\n\n    /// If the UART is a PCI device, returns its device number.\n    pub fn pci_device_number(&self) -> Option<NonZeroU8> {\n        NonZeroU8::new(self.pci_device_number)\n    }\n\n    /// If the UART is a PCI device, returns its function number.\n    pub fn pci_function_number(&self) -> Option<NonZeroU8> {\n        NonZeroU8::new(self.pci_function_number)\n    }\n\n    /// The UART clock frequency in Hz, if it can be determined.\n    pub const fn uart_clock_frequency(&self) -> Option<NonZeroU32> {\n        if self.header.revision <= 2 {\n            return None;\n        }\n        NonZeroU32::new(self.uart_clock_freq)\n    }\n\n    /// An ASCII string to uniquely identify this device. This string consists\n    /// of a fully qualified reference to the object that represents this\n    /// device in the ACPI namespace. If no namespace device exists,\n    /// the namespace string must only contain a single '.'.\n    pub fn namespace_string(&self) -> Result<&str, Utf8Error> {\n        let start = ptr::from_ref(self).cast::<u8>();\n        let bytes = unsafe {\n            let str_start = start.add(self.namespace_string_offset as usize);\n            slice::from_raw_parts(str_start, self.namespace_string_length as usize)\n        };\n        str::from_utf8(bytes)\n    }\n}\n\nbitflags::bitflags! {\n    /// Interrupt type(s) used by an UART.\n    #[derive(Clone, Copy, Debug)]\n    pub struct SpcrInterruptType: u8 {\n        /// PC-AT-compatible dual-8259 IRQ interrupt.\n        const DUAL_8259 = 1 << 0;\n        /// I/O APIC interrupt (Global System Interrupt).\n        const IO_APIC = 1 << 1;\n        /// I/O SAPIC interrupt (Global System Interrupt).\n        const IO_SAPIC = 1 << 2;\n        /// ARMH GIC interrupt (Global System Interrupt).\n        const ARMH_GIC = 1 << 3;\n        /// RISC-V PLIC/APLIC interrupt (Global System Interrupt).\n        const RISCV_PLIC = 1 << 4;\n    }\n}\n\nbitflags::bitflags! {\n    /// The terminal protocol the BIOS uses for console redirection.\n    #[derive(Clone, Copy, Debug)]\n    pub struct SpcrTerminalType: u8 {\n        const VT1000 = 1 << 0;\n        const EXTENDED_VT1000 = 1 << 1;\n        const VT_UTF8 = 1 << 2;\n        const ANSI = 1 << 3;\n    }\n}\n\nbitflags::bitflags! {\n    /// Flow control flags for the UART.\n    #[derive(Clone, Copy, Debug)]\n    pub struct SpcrFlowControl: u8 {\n        /// DCD required for transmit\n        const DCD = 1 << 0;\n        /// RTS/CTS hardware flow control\n        const RTS_CTS = 1 << 1;\n        /// XON/XOFF software control\n        const XON_XOFF = 1 << 2;\n    }\n}\n\n#[repr(u8)]\n#[derive(Clone, Copy, Debug)]\npub enum SpcrInterfaceType {\n    /// Full 16550 interface\n    Full16550,\n    /// Full 16450 interface (must also accept writing to the 16550 FCR register).\n    Full16450,\n    /// MAX311xE SPI UART\n    MAX311xE,\n    /// Arm PL011 UART\n    ArmPL011,\n    /// MSM8x60 (e.g. 8960)\n    MSM8x60,\n    /// Nvidia 16550\n    Nvidia16550,\n    /// TI OMAP\n    TiOmap,\n    /// APM88xxxx\n    APM88xxxx,\n    /// MSM8974\n    Msm8974,\n    /// SAM5250\n    Sam5250,\n    /// Intel USIF\n    IntelUSIF,\n    /// i.MX 6\n    Imx6,\n    /// (deprecated) Arm SBSA (2.x only) Generic UART supporting only 32-bit accesses\n    ArmSBSAGeneric32bit,\n    /// Arm SBSA Generic UART\n    ArmSBSAGeneric,\n    /// Arm DCC\n    ArmDCC,\n    /// VCM2835\n    Bcm2835,\n    /// SDM845 with clock rate of 1.8432 MHz\n    Sdm845_18432,\n    /// 16550-compatible with parameters defined in Generic Address Structure\n    Generic16550,\n    /// SDM845 with clock rate of 7.372 MHz\n    Sdm845_7372,\n    /// Intel LPSS\n    IntelLPSS,\n    /// RISC-V SBI console (any supported SBI mechanism)\n    RiscVSbi,\n    /// Unknown interface\n    Unknown(u8),\n}\n\nimpl From<u8> for SpcrInterfaceType {\n    fn from(val: u8) -> Self {\n        match val {\n            0x00 => Self::Full16550,\n            0x01 => Self::Full16450,\n            0x02 => Self::MAX311xE,\n            0x03 => Self::ArmPL011,\n            0x04 => Self::MSM8x60,\n            0x05 => Self::Nvidia16550,\n            0x06 => Self::TiOmap,\n            0x08 => Self::APM88xxxx,\n            0x09 => Self::Msm8974,\n            0x0A => Self::Sam5250,\n            0x0B => Self::IntelUSIF,\n            0x0C => Self::Imx6,\n            0x0D => Self::ArmSBSAGeneric32bit,\n            0x0E => Self::ArmSBSAGeneric,\n            0x0F => Self::ArmDCC,\n            0x10 => Self::Bcm2835,\n            0x11 => Self::Sdm845_18432,\n            0x12 => Self::Generic16550,\n            0x13 => Self::Sdm845_7372,\n            0x14 => Self::IntelLPSS,\n            0x15 => Self::RiscVSbi,\n            _ => Self::Unknown(val),\n        }\n    }\n}\n"
  },
  {
    "path": "src/sdt/srat.rs",
    "content": "use crate::{\n    AcpiTable,\n    sdt::{SdtHeader, Signature},\n};\nuse bit_field::BitField;\nuse core::{\n    marker::{PhantomData, PhantomPinned},\n    mem,\n    pin::Pin,\n};\nuse log::warn;\n\n/// Represents the SRAT (System Resource Affinity Table). This is a variable length table that\n/// allows devices (processors, memory ranges, and generic 'initiators') to be associated with\n/// system locality / proximity domains and clock domains.\n#[derive(Debug)]\n#[repr(C, packed)]\npub struct Srat {\n    pub header: SdtHeader,\n    _reserved0: u32,\n    _reserved1: u64,\n    _pinned: PhantomPinned,\n}\n\nunsafe impl AcpiTable for Srat {\n    const SIGNATURE: Signature = Signature::SRAT;\n\n    fn header(&self) -> &SdtHeader {\n        &self.header\n    }\n}\n\nimpl Srat {\n    pub fn entries(self: Pin<&Self>) -> SratEntryIter<'_> {\n        let ptr = unsafe { Pin::into_inner_unchecked(self) as *const Srat as *const u8 };\n        SratEntryIter {\n            pointer: unsafe { ptr.add(mem::size_of::<Srat>()) },\n            remaining_length: self.header.length - mem::size_of::<Srat>() as u32,\n            _phantom: PhantomData,\n        }\n    }\n}\n\n#[derive(Debug)]\npub struct SratEntryIter<'a> {\n    pointer: *const u8,\n    /*\n     * The iterator can only have at most `u32::MAX` remaining bytes, because the length of the\n     * whole SDT can only be at most `u32::MAX`.\n     */\n    remaining_length: u32,\n    _phantom: PhantomData<&'a ()>,\n}\n\n#[derive(Debug)]\npub enum SratEntry<'a> {\n    LocalApicAffinity(&'a LocalApicAffinity),\n    MemoryAffinity(&'a MemoryAffinity),\n    LocalApicX2Affinity(&'a LocalApicX2Affinity),\n    GiccAffinity(&'a GiccAffinity),\n    GicItsAffinity(&'a GicItsAffinity),\n    GicInitiatorAffinity(&'a GicInitiatorAffinity),\n}\n\nimpl<'a> Iterator for SratEntryIter<'a> {\n    type Item = SratEntry<'a>;\n\n    fn next(&mut self) -> Option<Self::Item> {\n        while self.remaining_length > 0 {\n            let entry_pointer = self.pointer;\n            let header = unsafe { *(self.pointer as *const EntryHeader) };\n\n            if header.length as u32 > self.remaining_length {\n                warn!(\"Invalid entry of type {} in SRAT - extending past length of table. Ignoring\", header.typ);\n                return None;\n            }\n\n            self.pointer = unsafe { self.pointer.byte_offset(header.length as isize) };\n            self.remaining_length = self.remaining_length.saturating_sub(header.length as u32);\n\n            match header.typ {\n                0 => {\n                    return Some(SratEntry::LocalApicAffinity(unsafe {\n                        &*(entry_pointer as *const LocalApicAffinity)\n                    }));\n                }\n                1 => {\n                    return Some(SratEntry::MemoryAffinity(unsafe { &*(entry_pointer as *const MemoryAffinity) }));\n                }\n                2 => {\n                    return Some(SratEntry::LocalApicX2Affinity(unsafe {\n                        &*(entry_pointer as *const LocalApicX2Affinity)\n                    }));\n                }\n                3 => return Some(SratEntry::GiccAffinity(unsafe { &*(entry_pointer as *const GiccAffinity) })),\n                4 => {\n                    return Some(SratEntry::GicItsAffinity(unsafe { &*(entry_pointer as *const GicItsAffinity) }));\n                }\n                5 => {\n                    return Some(SratEntry::GicInitiatorAffinity(unsafe {\n                        &*(entry_pointer as *const GicInitiatorAffinity)\n                    }));\n                }\n                other => warn!(\"Unrecognised entry in SRAT of type {}\", other),\n            }\n        }\n\n        None\n    }\n}\n\n#[derive(Clone, Copy, Debug)]\n#[repr(C, packed)]\npub struct EntryHeader {\n    pub typ: u8,\n    pub length: u16,\n}\n\n#[derive(Clone, Copy, Debug)]\n#[repr(C, packed)]\npub struct LocalApicAffinity {\n    pub header: EntryHeader,\n    pub proximity_domain_low: u8,\n    pub apic_id: u8,\n    pub flags: LocalApicAffinityFlags,\n    pub local_sapic_eid: u8,\n    pub proximity_domain_high: [u8; 3],\n    pub clock_domain: u32,\n}\n\nbitflags::bitflags! {\n    #[derive(Clone, Copy, Debug)]\n    pub struct LocalApicAffinityFlags: u32 {\n        const ENABLED = 1;\n    }\n}\n\nimpl LocalApicAffinity {\n    pub fn proximity_domain(&self) -> u32 {\n        u32::from_le_bytes([\n            self.proximity_domain_low,\n            self.proximity_domain_high[0],\n            self.proximity_domain_high[1],\n            self.proximity_domain_high[2],\n        ])\n    }\n}\n\n#[derive(Clone, Copy, Debug)]\n#[repr(C, packed)]\npub struct MemoryAffinity {\n    pub header: EntryHeader,\n    pub proximity_domain: u32,\n    _reserved0: u16,\n    pub base_address_low: u32,\n    pub base_address_high: u32,\n    pub length_low: u32,\n    pub length_high: u32,\n    _reserved1: u32,\n    pub flags: MemoryAffinityFlags,\n    _reserved2: u64,\n}\n\nbitflags::bitflags! {\n    #[derive(Clone, Copy, Debug)]\n    pub struct MemoryAffinityFlags: u32 {\n        const ENABLED = 1;\n        const HOT_PLUGGABLE = 1 << 1;\n        const NON_VOLATILE = 1 << 2;\n    }\n}\n\nimpl MemoryAffinity {\n    pub fn base_address(&self) -> u64 {\n        let mut address = self.base_address_low as u64;\n        address.set_bits(32..64, self.base_address_high as u64);\n        address\n    }\n\n    pub fn length(&self) -> u64 {\n        let mut length = self.length_low as u64;\n        length.set_bits(32..64, self.base_address_high as u64);\n        length\n    }\n}\n\n#[derive(Clone, Copy, Debug)]\n#[repr(C, packed)]\npub struct LocalApicX2Affinity {\n    pub header: EntryHeader,\n    _reserved0: u16,\n    pub proximity_domain: u32,\n    pub x2apic_id: u32,\n    pub flags: LocalApicAffinityFlags,\n    pub clock_domain: u32,\n    _reserved1: u32,\n}\n\n#[derive(Clone, Copy, Debug)]\n#[repr(C, packed)]\npub struct GiccAffinity {\n    pub header: EntryHeader,\n    pub proximity_domain: u32,\n    pub acpi_processor_uid: u32,\n    pub flags: u32,\n    pub clock_domain: u32,\n}\n\n#[derive(Clone, Copy, Debug)]\n#[repr(C, packed)]\npub struct GicItsAffinity {\n    pub header: EntryHeader,\n    pub proximity_domain: u32,\n    _reserved0: u16,\n    pub its_id: u32,\n}\n\n#[derive(Clone, Copy, Debug)]\n#[repr(C, packed)]\npub struct GicInitiatorAffinity {\n    pub header: EntryHeader,\n    _reserved0: u8,\n    pub device_handle_type: u8,\n    pub proximity_domain: u32,\n    pub device_handle: [u8; 16],\n    pub flags: u32,\n    _reserved1: u32,\n}\n"
  },
  {
    "path": "tests/README.md",
    "content": "# Tests in the ACPI crate.\n\nThere are 4 categories of tests:\n\n1. Unit tests in the main crate source.\n2. Integration tests in the `tests` directory - these all have the extension `.rs`.\n3. Raw ASL files that can be checked using `aml_tester`\n4. Support for running tests from the `uacpi` project.\n\n## Unit tests\n\nThese are standard Rust unit tests. Run them using `cargo test` (which will also run the integration tests). There are\nrelatively few unit tests at present - feel free to send PRs with more!\n\n## Integration tests\n\nThese are the `.rs` files in the `tests` directory. They largely consist of snippets of ASL code that are parsed and\nthen executed using the `aml_test_tools` sub-crate. Most of the tests specify the expected output from the provided\n`Handler`, which provides reasonable confidence the parser is working correctly.\n\nThe `aml_test_tools` sub-crate provides various utilities for writing this style of test.\n\nLike the unit tests, they can be run using `cargo test`.\n\n## Raw ASL files\n\nVarious ASL files are provided in the `tests` directory. They can be parsed and executed by running \n`cargo run_tests -p tests` from the root of the repository. This cargo alias runs the `aml_tester` tool.\n\nThe `aml_tester` tool provides a command-line way to run ASL files - similar to `acpiexec`.\n\n## Running tests from the `uacpi` project\n\n> See the documentation for `uacpi_test_adapter` for more detailed information. Note that a large proportion of the\n> tests in the `uacpi` project do not yet pass in this crate.\n \nThe `uacpi` project has a fairly extensive test suite. It makes sense for us to be able to check against their test\nsuite as well as our own. This can be done by:\n\n1. Checking out the [`uacpi` repository.](https://github.com/uACPI/uACPI)\n2. Navigating to the root of that repo.\n3. Running something like: \n   ```shell\n   python3 tests/run_tests.py --test-runner ../acpi/target/debug/uacpi_test_adapter\n   ```\n   Adjusting the paths as necessary. See the `uacpi_test_adapter` documentation for details on how it finds\n   `aml_tester`.\n\nNote that, at present, several of the uACPI tests run indefinitely. You may want to skip these! The easiest way to do so\nis probably just to delete them... The relevant tests are:\n\n* `global-lock.asl`\n* `hanging-while.asl`\n* `infinite-recursion.asl`\n* `mutex-1.asl`\n* `mutex-2.asl`\n* `mutex-3.asl`\n"
  },
  {
    "path": "tests/bank_fields.rs",
    "content": "// Test operations on \"Bank\" fields\n\nuse aml_test_tools::handlers::std_test_handler::{\n    Command,\n    construct_std_handler,\n    create_mutex,\n    read_u16,\n    write_u8,\n    write_u16,\n};\n\nmod test_infra;\n\n#[test]\nfn test_basic_bank_store_and_load() {\n    // This is a straightforward test of banked fields.\n    // Internally: Apart from setting the bank index beforehand, the field read/write is identical\n    // to normal fields. So this test is probably sufficient testing of banked fields.\n    const AML: &str = r#\"DefinitionBlock(\"\", \"DSDT\", 1, \"RSACPI\", \"BNKFLD\", 1) {\n    OperationRegion(MEM, SystemMemory, 0x40000, 0x1000)\n    Field(MEM, ByteAcc, NoLock, Preserve) {\n        INDX, 8\n    }\n\n    BankField (MEM, INDX, 0, WordAcc, NoLock, Preserve) {\n        OFFSET (0x02), // Prevent aliasing INDX\n        A, 16,\n        B, 16,\n        C, 16\n    }\n\n    BankField (MEM, INDX, 1, WordAcc, NoLock, Preserve) {\n        OFFSET (0x02),\n        D, 16,\n        E, 16,\n        F, 16\n    }\n\n    Method(MAIN, 0, NotSerialized) {\n        C = 0xA55A\n        D = C\n        E = 0x5AA5\n        A = E\n        Return (0)\n    }\n}\n\"#;\n\n    const EXPECTED_COMMANDS: &[Command] = &[\n        create_mutex(),\n        // C = 0xA55A\n        write_u8(0x40000, 0),       // Select bank 0.\n        write_u16(0x40006, 0xA55A), // Write the value to C.\n        // D = C\n        write_u8(0x40000, 0),       // Select bank 0.\n        read_u16(0x40006, 0xA55A),  // Read the value from C.\n        write_u8(0x40000, 1),       // Select bank 1.\n        write_u16(0x40002, 0xA55A), // Write the value to D.\n        // E = 0x5AA5\n        write_u8(0x40000, 1),       // Select bank 1.\n        write_u16(0x40004, 0x5AA5), // Write the value to E.\n        // A = E\n        write_u8(0x40000, 1),       // Select bank 1.\n        read_u16(0x40004, 0x5AA5),  // Read from E\n        write_u8(0x40000, 0),       // Select bank 0.\n        write_u16(0x40002, 0x5AA5), // Write the value to A.\n    ];\n\n    let handler = construct_std_handler(EXPECTED_COMMANDS.to_vec());\n    test_infra::run_aml_test(AML, handler);\n}\n"
  },
  {
    "path": "tests/buffer_fields.asl",
    "content": "DefinitionBlock(\"buffer_fields.aml\", \"DSDT\", 1, \"RSACPI\", \"BUFFLD\", 1) {\n    Name(X, Buffer (16) { 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff })\n    CreateBitField(X, 3, BIT3)\n    CreateField(X, 0, 3, BITS)\n    CreateByteField(X, 1, BYTE)\n    CreateWordField(X, 2, WORD)\n    CreateDWordField(X, 4, DWRD)\n    CreateQWordField(X, 8, QWRD)\n\n    // `X` should end up as [0xff, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x00, 0x00]\n    BIT3 = 1\n    BITS = 7\n    BYTE = 0x01\n    WORD = 0x0302\n    DWRD = 0x07060504\n    // Last two bytes should be cleared because of zero-extension of this store\n    // We do this as a buffer store a) to test them b) because `iasl` doesn't support 64-bit integer constants...\n    QWRD = Buffer() { 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d }\n\n    // `Y` should end up as `Integer(0x07060504)` (`Integer(117835012)` in decimal)\n    Name(Y, 4)\n    Y = DWRD\n}\n"
  },
  {
    "path": "tests/complex_package.asl",
    "content": "DefinitionBlock (\"\", \"DSDT\", 2, \"uTEST\", \"TESTTABL\", 0xF0F0F0F0)\n{\n    Method(GPKG) {\n        Local1 = 10\n        Local0 = Package (Local1) {\n            0x123,\n            0x321,\n            Package {\n                0x321,\n                \"123\",\n                Package {\n                    0x321,\n                    Package {\n                        0x321,\n                        \"123\",\n                        Package {\n                            0x321,\n                            \"123\",\n                                Package {\n                                    0x321,\n                                    Package {\n                                        0x321,\n                                        \"123\",\n                                        Package (Local1) {\n                                            0x321,\n                                            \"123\",\n                                            999,\n                                        },\n                                        999,\n                                    },\n                                \"123\",\n                                999,\n                            },\n                            999,\n                        },\n                        999,\n                    },\n                    \"123\",\n                    999,\n                },\n                999,\n            },\n            \"Hello world\",\n            Package {\n                0x321,\n                \"Hello\",\n            },\n            Package {\n                0x321,\n                \"World\",\n            },\n            Package {\n                Buffer (Local1) { 0xFF },\n                0xDEADBEEF,\n            },\n            Buffer { 1, 2, 3 }\n        }\n\n        Return (Local0)\n    }\n\n    Method (MAIN) {\n        Local0 = GPKG()\n        Debug = Local0\n        Return (0)\n    }\n}\n"
  },
  {
    "path": "tests/fields.asl",
    "content": "DefinitionBlock(\"fields.aml\", \"DSDT\", 1, \"RSACPI\", \"BUFFLD\", 1) {\n    OperationRegion(MEM, SystemMemory, 0x40000, 0x1000)\n    Field(MEM, WordAcc, NoLock, Preserve) {\n        , 7,\n        A, 15,\n        , 8,\n        B, 1,\n        , 1,\n        C, 35\n    }\n\n    OperationRegion(GIO0, SystemIO, 0x125, 0x100)\n\n    Field(GIO0, ByteAcc, NoLock, Preserve) {\n        GLB1, 1,\n        GLB2, 1,\n        Offset(1),\n        BNK1, 4,\n\n        Offset(2),\n        IDX0, 8,\n        DAT0, 8\n    }\n\n    BankField(GIO0, BNK1, 0, ByteAcc, NoLock, Preserve) {\n        Offset(0x30),\n        FET0, 1,\n        FET1, 1\n    }\n\n    BankField(GIO0, BNK1, 1, ByteAcc, NoLock, Preserve) {\n        Offset(0x30),\n        BLVL, 7,\n        BAC, 1\n    }\n\n    IndexField(IDX0, DAT0, ByteAcc, NoLock, Preserve) {\n        FET2, 1,\n        FET3, 1,\n        Offset(0x2f),\n        , 7,\n        FET4, 1\n    }\n\n    Name(RESA, 0)\n    Name(RESB, 0)\n    Name(RESC, 0)\n    Method(MAIN, 0, NotSerialized) {\n        RESA = A\n        RESB = B\n        RESC = C\n\n        A = RESA\n        B = RESB\n        C = RESC\n\n        Return (0)\n    }\n}\n"
  },
  {
    "path": "tests/incdec.asl",
    "content": "DefinitionBlock(\"incdec.aml\", \"DSDT\", 1, \"RSACPI\", \"INCDEC\", 1) {\n    Name(X, 0)\n    X++\n    X++\n    X++\n    Name(Y, 0)\n    Y = X\n    X--\n}\n"
  },
  {
    "path": "tests/index_fields.rs",
    "content": "// Test operations on \"Index\" fields\n\nuse aml_test_tools::handlers::std_test_handler::{\n    Command,\n    construct_std_handler,\n    create_mutex,\n    read_u8,\n    read_u16,\n    write_u8,\n    write_u16,\n};\n\nmod test_infra;\n\n#[test]\nfn test_basic_index_store_and_load_8_bit() {\n    // In this test, the data register has the same width as the fields and all fields are correctly\n    // aligned. We should see single reads and writes for each store operation.\n    const AML: &str = r#\"DefinitionBlock(\"\", \"DSDT\", 1, \"RSACPI\", \"IDXFLD\", 1) {\n    OperationRegion(MEM, SystemMemory, 0x40000, 0x1000)\n    Field(MEM, ByteAcc, NoLock, Preserve) {\n        OFFSET(0x10),\n        INDX, 8,\n        DATA, 8\n    }\n\n    IndexField (INDX, DATA, ByteAcc, NoLock, Preserve) {\n        A, 8,\n        B, 8,\n        C, 8\n    }\n\n    Method(MAIN, 0, NotSerialized) {\n        C = 0xA5\n        A = 0x5A\n        B = A\n        C = A\n        Return (0)\n    }\n}\n\"#;\n\n    const EXPECTED_COMMANDS: &[Command] = &[\n        create_mutex(),\n        // C = 0xA5\n        write_u8(0x40010, 0x02), // Set index to point at C\n        write_u8(0x40011, 0xA5), // Set C = 0xA5\n        // A = 0x5A\n        write_u8(0x40010, 0x00), // Set index to point at A\n        write_u8(0x40011, 0x5A), // Set A = 0x5A\n        // Read A\n        write_u8(0x40010, 0x00), // etc.\n        read_u8(0x40011, 0x5A),\n        // B = A\n        write_u8(0x40010, 0x01),\n        write_u8(0x40011, 0x5A),\n        // Read A\n        write_u8(0x40010, 0x00),\n        read_u8(0x40011, 0x5A),\n        // C = A\n        write_u8(0x40010, 0x02),\n        write_u8(0x40011, 0x5A),\n    ];\n\n    let handler = construct_std_handler(EXPECTED_COMMANDS.to_vec());\n    test_infra::run_aml_test(AML, handler);\n}\n\n#[test]\nfn test_basic_index_store_and_load_16_bit() {\n    // In this test, the data register has the same width as the fields and all fields are correctly\n    // aligned. We should see single reads and writes for each store operation.\n    const AML: &str = r#\"DefinitionBlock(\"\", \"DSDT\", 1, \"RSACPI\", \"IDXFLD\", 1) {\n    OperationRegion(MEM, SystemMemory, 0x40000, 0x1000)\n    Field(MEM, WordAcc, NoLock, Preserve) {\n        OFFSET(0x20),\n        INDX, 16,\n        DATA, 16\n    }\n\n    IndexField (INDX, DATA, WordAcc, NoLock, Preserve) {\n        A, 16,\n        B, 16,\n        C, 16\n    }\n\n    Method(MAIN, 0, NotSerialized) {\n        C = 0xA5B6\n        A = 0x5A6B\n        B = A\n        C = A\n        Return (0)\n    }\n}\n\"#;\n\n    const EXPECTED_COMMANDS: &[Command] = &[\n        create_mutex(),\n        // C = 0xA5B6\n        write_u16(0x40020, 0x04),   // Set index to point at C\n        write_u16(0x40022, 0xA5B6), // Set C = 0xA5B6\n        // A = 0x5A6B\n        write_u16(0x40020, 0x00),   // Set index to point at A\n        write_u16(0x40022, 0x5A6B), // Set A = 0x5A6B\n        // Read A\n        write_u16(0x40020, 0x00), // etc.\n        read_u16(0x40022, 0x5A6B),\n        // B = A\n        write_u16(0x40020, 0x02),\n        write_u16(0x40022, 0x5A6B),\n        // Read A\n        write_u16(0x40020, 0x00),\n        read_u16(0x40022, 0x5A6B),\n        // C = A\n        write_u16(0x40020, 0x04),\n        write_u16(0x40022, 0x5A6B),\n    ];\n\n    let handler = construct_std_handler(EXPECTED_COMMANDS.to_vec());\n    test_infra::run_aml_test(AML, handler);\n}\n\n#[test]\nfn test_index_multiple_aligned_reads() {\n    // In this test, the data register is narrower than the fields, so multiple data register\n    // reads are needed to access each field.\n    const AML: &str = r#\"DefinitionBlock(\"\", \"DSDT\", 1, \"RSACPI\", \"IDXFLD\", 1) {\n    OperationRegion(MEM, SystemMemory, 0x40000, 0x1000)\n    Field(MEM, ByteAcc, NoLock, Preserve) {\n        OFFSET(0x04),\n        INDX, 8,\n        DATA, 8\n    }\n\n    IndexField (INDX, DATA, ByteAcc, NoLock, Preserve) {\n        A, 16,\n        B, 16,\n        C, 16\n    }\n\n    Method(MAIN, 0, NotSerialized) {\n        C = 0xA5B6\n        B = C\n        Return (0)\n    }\n}\n\"#;\n\n    const EXPECTED_COMMANDS: &[Command] = &[\n        create_mutex(),\n        // C = 0xA5B6\n        write_u8(0x40004, 0x04), // Set index to point at C (low byte)\n        write_u8(0x40005, 0xB6), // Set low C = 0xB6\n        write_u8(0x40004, 0x05), // Set index to point at C (high byte)\n        write_u8(0x40005, 0xA5), // Set low C = 0xA5\n        // B = C. Read C\n        write_u8(0x40004, 0x04),\n        read_u8(0x40005, 0xB6),\n        write_u8(0x40004, 0x05),\n        read_u8(0x40005, 0xA5),\n        // Write B\n        write_u8(0x40004, 0x02),\n        write_u8(0x40005, 0xB6),\n        write_u8(0x40004, 0x03),\n        write_u8(0x40005, 0xA5),\n    ];\n\n    let handler = construct_std_handler(EXPECTED_COMMANDS.to_vec());\n    test_infra::run_aml_test(AML, handler);\n}\n\n#[test]\nfn test_index_narrower_than_data() {\n    // In this test, the access width of the index field is smaller than the data register. Even\n    // though it looks as though individual 16-bit reads/writes would be OK, actually multiple\n    // 8-bit reads/writes are needed to access each field. (Not intuitive, but matches ACPICA)\n    const AML: &str = r#\"DefinitionBlock(\"\", \"DSDT\", 1, \"RSACPI\", \"IDXFLD\", 1) {\n    OperationRegion(MEM, SystemMemory, 0x40000, 0x1000)\n    Field(MEM, WordAcc, NoLock, Preserve) {\n        OFFSET(0x06),\n        INDX, 16,\n        DATA, 16\n    }\n\n    IndexField (INDX, DATA, ByteAcc, NoLock, Preserve) {\n        A, 16,\n        B, 16,\n        C, 16\n    }\n\n    Method(MAIN, 0, NotSerialized) {\n        C = 0xA5B6\n        B = C\n        Return (0)\n    }\n}\n\"#;\n\n    const EXPECTED_COMMANDS: &[Command] = &[\n        create_mutex(),\n        // C = 0xA5B6\n        write_u16(0x40006, 0x04), // Set index to point at C, low byte\n        write_u8(0x40008, 0xB6),  // Set C (low) = 0xB6\n        write_u16(0x40006, 0x05), // Set index to point at C, high byte\n        write_u8(0x40008, 0xA5),  // Set C (high) = 0xA5\n        // Read C\n        write_u16(0x40006, 0x04), // etc.\n        read_u8(0x40008, 0xB6),\n        write_u16(0x40006, 0x05),\n        read_u8(0x40008, 0xA5),\n        // B = C\n        write_u16(0x40006, 0x02),\n        write_u8(0x40008, 0xB6),\n        write_u16(0x40006, 0x03),\n        write_u8(0x40008, 0xA5),\n    ];\n\n    //let handler = NullHandler;\n    let handler = construct_std_handler(EXPECTED_COMMANDS.to_vec());\n    test_infra::run_aml_test(AML, handler);\n}\n\n#[test]\nfn test_index_misaligned_field() {\n    // Check that we can successfully update non-aligned index fields.\n    const AML: &str = r#\"DefinitionBlock(\"\", \"DSDT\", 1, \"RSACPI\", \"IDXFLD\", 1) {\n    OperationRegion(MEM, SystemMemory, 0x40000, 0x1000)\n    Field(MEM, ByteAcc, NoLock, Preserve) {\n        OFFSET(0x08),\n        INDX, 8,\n        DATA, 8\n    }\n\n    IndexField (INDX, DATA, ByteAcc, NoLock, Preserve) {\n        SKIP, 4, // Nybbles make it easier to model mentally!\n        A, 8,\n        B, 8\n    }\n\n    Method(MAIN, 0, NotSerialized) {\n        B = 0xB6\n        A = B\n        Return (0)\n    }\n}\n\"#;\n\n    const EXPECTED_COMMANDS: &[Command] = &[\n        create_mutex(),\n        // B = 0xB6\n        // Set index to point at B, low nybble. Which is actually the byte containing A's high\n        // nybble as well.\n        write_u8(0x40008, 0x01),\n        // Read that byte so as to be able to preserve A's high nybble. Pretend that A's high nybble\n        // is 0xA.\n        read_u8(0x40009, 0x0A),\n        // Write back A's high nybble and B's low nybble. This is as much as we test Preserve\n        // behaviour in the index field tests - it's assumed that if it works for normal fields, it\n        // works for index fields as well.\n        write_u8(0x40009, 0x6A),\n        // Set the index to point at B, high nybble.\n        write_u8(0x40008, 0x02),\n        // Read that byte, which also contains an unused nybble. (Which we've just set to zero)\n        read_u8(0x40009, 0x00),\n        // Write B's high nybble, preserving the unused zero nybble.\n        write_u8(0x40009, 0x0B),\n        // A = B. Start by reading B\n        write_u8(0x40008, 0x01),\n        read_u8(0x40009, 0x60),\n        write_u8(0x40008, 0x02),\n        read_u8(0x40009, 0x0B),\n        // Set A, remembering that there are some reads needed to preserve the other nybbles.\n        write_u8(0x40008, 0x00),\n        read_u8(0x40009, 0x00),\n        write_u8(0x40009, 0x60),\n        write_u8(0x40008, 0x01),\n        read_u8(0x40009, 0x60),\n        write_u8(0x40009, 0x6B),\n    ];\n\n    let handler = construct_std_handler(EXPECTED_COMMANDS.to_vec());\n    test_infra::run_aml_test(AML, handler);\n}\n"
  },
  {
    "path": "tests/method.asl",
    "content": "DefinitionBlock(\"method.aml\", \"DSDT\", 1, \"RSACPI\", \"METHOD\", 1) {\n    Name(X, 5)\n\n    Method(FOO, 0, NotSerialized) {\n        If (X > 1) {\n            Noop\n        } Else {\n            Return (0x55)\n        }\n        Return (0x3f)\n    }\n\n    Name(Y, 0)\n    Y = FOO()\n}\n"
  },
  {
    "path": "tests/multi_table.asl",
    "content": "/* This file is a test for `aml_tester` rather than of the parser itself.\n * Can `aml_tester` cope with multiple tables?\n */\nDefinitionBlock(\"\", \"DSDT\", 1, \"RSACPI\", \"BUFFLD\", 1) {\n    OperationRegion(MEM, SystemMemory, 0x40000, 0x1000)\n    Field(MEM, ByteAcc, NoLock, Preserve) {\n        A, 8\n    }\n}\nDefinitionBlock(\"\", \"SSDT\", 1, \"RSACPI\", \"BUFFLD\", 1) {\n    OperationRegion(MEMB, SystemMemory, 0x50000, 0x1000)\n    Field(MEMB, ByteAcc, NoLock, Preserve) {\n        B, 8\n    }\n}\n"
  },
  {
    "path": "tests/name_search.rs",
    "content": "use aml_test_tools::handlers::null_handler::NullHandler;\n\nmod test_infra;\n\n#[test]\nfn test_name_search_1() {\n    const AML: &str = r#\"\nDefinitionBlock(\"\", \"DSDT\", 1, \"RSACPI\", \"NAMING\", 1) {\n    Scope (\\) {\n        Device (AMW0) {\n            Name (_HID, EisaId (\"PNP0C14\"))\n\n            OperationRegion (\\RAMX, SystemMemory, 0x1000, 0x0100)\n            Field (RAMX, ByteAcc, NoLock, Preserve)\n            {\n                WFUN,   32,\n            }\n        }\n    }\n}\n\"#;\n\n    let handler = NullHandler {};\n    test_infra::run_aml_test(AML, handler);\n}\n\n#[test]\nfn test_name_search_2() {\n    const AML: &str = r#\"\nDefinitionBlock(\"\", \"DSDT\", 1, \"RSACPI\", \"NAMING\", 1) {\n    Scope (\\) {\n        OperationRegion (RAMX, SystemMemory, 0x1000, 0x0100)\n        Device (AMW0) {\n            Name (_HID, EisaId (\"PNP0C14\"))\n\n            Field (\\RAMX, ByteAcc, NoLock, Preserve)\n            {\n                WFUN,   32,\n            }\n        }\n    }\n}\n\"#;\n\n    let handler = NullHandler {};\n    test_infra::run_aml_test(AML, handler);\n}\n"
  },
  {
    "path": "tests/normal_fields.rs",
    "content": "// Test operations on \"normal\" fields - those that are not Index or Bank fields.\n\nuse aml_test_tools::handlers::std_test_handler::{\n    Command,\n    construct_std_handler,\n    create_mutex,\n    read_io_u8,\n    read_io_u16,\n    read_u16,\n    write_io_u8,\n    write_io_u16,\n    write_u16,\n};\n\nmod test_infra;\n\n#[test]\nfn test_basic_store_and_load() {\n    const AML: &str = r#\"DefinitionBlock(\"\", \"DSDT\", 1, \"RSACPI\", \"BUFFLD\", 1) {\n    OperationRegion(MEM, SystemMemory, 0x40000, 0x1000)\n    Field(MEM, WordAcc, NoLock, Preserve) {\n        A, 16,\n        B, 16\n    }\n\n    Method(MAIN, 0, NotSerialized) {\n        A = 0xA5A5\n        B = A\n        Return (0)\n    }\n}\n\"#;\n\n    const EXPECTED_COMMANDS: &[Command] = &[\n        create_mutex(),\n        // A = 0xA5A5\n        write_u16(0x40000, 0xA5A5),\n        // B = A\n        read_u16(0x40000, 0xA5A5),\n        write_u16(0x40002, 0xA5A5),\n    ];\n\n    let handler = construct_std_handler(EXPECTED_COMMANDS.to_vec());\n    test_infra::run_aml_test(AML, handler);\n}\n\n#[test]\nfn test_narrow_access_store_and_load() {\n    const AML: &str = r#\"DefinitionBlock(\"\", \"DSDT\", 1, \"RSACPI\", \"BUFFLD\", 1) {\n    OperationRegion(MEM, SystemIO, 0x40, 0x10)\n    Field(MEM, ByteAcc, NoLock, Preserve) {\n        A, 16,\n        B, 16\n    }\n\n    Method(MAIN, 0, NotSerialized) {\n        A = 0xA55A\n        B = A\n        Return (0)\n    }\n}\n\"#;\n\n    const EXPECTED_COMMANDS: &[Command] = &[\n        create_mutex(),\n        // A = 0xA55A\n        write_io_u8(0x40, 0x5A),\n        write_io_u8(0x41, 0xA5),\n        // B = A\n        read_io_u8(0x40, 0x5A),\n        read_io_u8(0x41, 0xA5),\n        write_io_u8(0x42, 0x5A),\n        write_io_u8(0x43, 0xA5),\n    ];\n\n    let handler = construct_std_handler(EXPECTED_COMMANDS.to_vec());\n    test_infra::run_aml_test(AML, handler);\n}\n\n#[test]\nfn test_unaligned_field_store() {\n    const AML: &str = r#\"DefinitionBlock(\"\", \"DSDT\", 1, \"RSACPI\", \"BUFFLD\", 1) {\n    OperationRegion(MEM, SystemIO, 0x40, 0x10)\n    Field(MEM, WordAcc, NoLock, Preserve) {\n        A, 7,\n        B, 8\n    }\n\n    Method(MAIN, 0, NotSerialized) {\n        A = 4\n        B = A\n\n        Return (0)\n    }\n}\n\"#;\n\n    const EXPECTED_COMMANDS: &[Command] = &[\n        create_mutex(),\n        read_io_u16(0x40, 0),\n        write_io_u16(0x40, 0x04),\n        read_io_u16(0x40, 4),\n        read_io_u16(0x40, 4),\n        write_io_u16(0x40, 0x204),\n    ];\n\n    let handler = construct_std_handler(EXPECTED_COMMANDS.to_vec());\n    test_infra::run_aml_test(AML, handler);\n}\n"
  },
  {
    "path": "tests/object_type.asl",
    "content": "DefinitionBlock(\"object_type.aml\", \"DSDT\", 1, \"RSACPI\", \"OBJTYP\", 1) {\n    Name(INT, 723)\n    Name(STR, \"Hello, World!\")\n    Name(BUFF, Buffer { 7, 2, 3, 5, 92, 6 })\n    // TODO: more types\n\n    Device(TYPS) {\n        // This is just so it compiles\n        Name (_HID, EisaId (\"PNP0A03\"))\n\n        Name(INT, 0)    // Should be `1`\n        Name(STR, 0)    // Should be `2`\n        Name(BUFF, 0)   // Should be `3`\n\n        INT = ObjectType(\\INT)\n        STR = ObjectType(\\STR)\n        BUFF = ObjectType(\\BUFF)\n    }\n}\n"
  },
  {
    "path": "tests/operation_region.rs",
    "content": "use aml_test_tools::handlers::std_test_handler::{Command, construct_std_handler, create_mutex, read_u8, write_pci_u8, write_u8, read_pci_u8};\nuse pci_types::PciAddress;\n\nmod test_infra;\n\n#[test]\nfn test_region_in_top_level() {\n    const AML: &str = r#\"DefinitionBlock(\"\", \"DSDT\", 1, \"RSACPI\", \"OPREG\", 1) {\n    OperationRegion(MEM, SystemMemory, 0x40000, 0x1000)\n    Field(MEM, ByteAcc, NoLock, Preserve) {\n        DATA, 8\n    }\n\n    Method(RDWR, 0, NotSerialized) {\n        DATA = 0xA5\n        Return (DATA)\n    }\n\n    Method(MAIN, 0, NotSerialized) {\n        Local0 = RDWR()\n        // Remember that returning zero indicates success.\n        Return (Local0 != 0xA5)\n    }\n}\n\"#;\n\n    const EXPECTED_COMMANDS: &[Command] = &[\n        create_mutex(),\n        // DATA = 0xA5\n        write_u8(0x40000, 0xA5),\n        read_u8(0x40000, 0xA5),\n    ];\n\n    let handler = construct_std_handler(EXPECTED_COMMANDS.to_vec());\n    test_infra::run_aml_test(AML, handler);\n}\n\n#[test]\nfn test_region_in_device() {\n    const AML: &str = r#\"DefinitionBlock(\"\", \"DSDT\", 1, \"RSACPI\", \"OPREG\", 1) {\n    Device(TEST) {\n        Name (_HID, EisaId (\"PNP0C01\")) // Arbitrary choice.\n\n        OperationRegion(MEM, SystemMemory, 0x40000, 0x1000)\n        Field(MEM, ByteAcc, NoLock, Preserve) {\n            DATA, 8\n        }\n\n        Method(RDWR, 0, NotSerialized) {\n            DATA = 0xA5\n            Return (DATA)\n        }\n    }\n\n    Method(MAIN, 0, NotSerialized) {\n        Local0 = ^TEST.RDWR()\n        // Remember that returning zero indicates success.\n        Return (Local0 != 0xA5)\n    }\n}\n\"#;\n\n    const EXPECTED_COMMANDS: &[Command] = &[\n        create_mutex(),\n        // DATA = 0xA5\n        write_u8(0x40000, 0xA5),\n        read_u8(0x40000, 0xA5),\n    ];\n\n    let handler = construct_std_handler(EXPECTED_COMMANDS.to_vec());\n    test_infra::run_aml_test(AML, handler);\n}\n\n#[test]\nfn test_region_in_method() {\n    const AML: &str = r#\"DefinitionBlock(\"\", \"DSDT\", 1, \"RSACPI\", \"OPREG\", 1) {\n\n    // `iasl` is not a fan of this function - it generates a bunch of warnings, which for testing\n    // purposes can be ignored.\n    Method(RDWR, 1, NotSerialized) {\n        OperationRegion(MEM, SystemMemory, Arg0, 0x1000)\n        Field(MEM, ByteAcc, NoLock, Preserve) {\n            DATA, 8\n        }\n\n        DATA = 0xA5\n        Return (DATA)\n    }\n\n    Method(MAIN, 0, NotSerialized) {\n        Local0 = RDWR(0x40000)\n        // Remember that returning zero indicates success.\n        if (Local0 != 0xA5) {\n            Return (One)\n        }\n\n        Local0 = RDWR(0x50000)\n        Return (Local0 != 0xA5)\n    }\n}\n\"#;\n\n    const EXPECTED_COMMANDS: &[Command] = &[\n        create_mutex(),\n        // Base set to 0x40000\n        write_u8(0x40000, 0xA5),\n        read_u8(0x40000, 0xA5),\n        // Base set to 0x50000\n        write_u8(0x50000, 0xA5),\n        read_u8(0x50000, 0xA5),\n    ];\n\n    let handler = construct_std_handler(EXPECTED_COMMANDS.to_vec());\n    test_infra::run_aml_test(AML, handler);\n}\n\n#[test]\n#[ignore]\nfn test_buffer_field_implicit_conv() {\n    // This test covers https://github.com/rust-osdev/acpi/issues/273\n    const AML: &str = r#\"DefinitionBlock (\"\", \"SSDT\", 2, \"AMD\", \"AmdTable\", 0x00000002)\n{\n    Name (ADAT, Buffer (0x0010)\n    {\n        /* 0000 */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,  // ........\n        /* 0008 */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,  // ........\n\n    })\n    OperationRegion (A053, SystemMemory, (DerefOf (ADAT [(0x04)])), 0x1000) // <--- this line failed\n    Field (A053, ByteAcc, NoLock, Preserve)\n    {\n        Offset (0x18),\n        A, 8\n    }\n}\"#;\n\n    const EXPECTED_COMMANDS: &[Command] = &[create_mutex()];\n\n    let handler = construct_std_handler(EXPECTED_COMMANDS.to_vec());\n    test_infra::run_aml_test(AML, handler);\n}\n\n#[test]\nfn test_region_in_pci_device() {\n    const AML: &str = r#\"DefinitionBlock(\"\", \"DSDT\", 1, \"RSACPI\", \"OPREG\", 1) {\n    Device(TEST) {\n        Name (_ADR, 0x00020001) // Arbitrary choice.\n        Name (_BBN, 3)\n        Name (_SEG, 4)\n\n        OperationRegion(MEM, PCI_Config, 0x40, 0x20)\n        Field(MEM, ByteAcc, NoLock, Preserve) {\n        Offset (0x10),\n            DATA, 8\n        }\n\n        Method(RDWR, 0, NotSerialized) {\n            DATA = 0xA5\n            Return (DATA)\n        }\n    }\n\n    Method(MAIN, 0, NotSerialized) {\n        Local0 = ^TEST.RDWR()\n        // Remember that returning zero indicates success.\n        Return (Local0 != 0xA5)\n    }\n}\n\"#;\n\n    let address: PciAddress = PciAddress::new(4, 3, 2, 1);\n    let expected_commands: &[Command] = &[\n        create_mutex(),\n        // DATA = 0xA5\n        write_pci_u8(address, 0x50, 0xA5),\n        read_pci_u8(address, 0x50, 0xA5),\n    ];\n\n    let handler = construct_std_handler(expected_commands.to_vec());\n    test_infra::run_aml_test(AML, handler);\n}\n"
  },
  {
    "path": "tests/package.asl",
    "content": "DefinitionBlock(\"package.aml\", \"DSDT\", 1, \"RSACPI\", \"PACKGE\", 1) {\n    Name(FOO, Package (15) {\n        0x01,\n        0x02,\n        0x03,\n        \"Hello, World!\",\n        0x05,\n        0x06,\n        0x07,\n        0x08,\n        0x09,\n        0x0a,\n    })\n\n    Name(BAR, Package (5) {\n        Package { 0x01, 0x02, 0x03 },\n        Package { 0x04, 0x05, 0x06 },\n        Package { 0x07, 0x08, 0x09 },\n        Package { 0x0a, 0x0b, 0x0c },\n        Package { 0x0d, 0x0e, 0x0f },\n    })\n\n    Name(LEN, 5)\n    Name(BAL, Package(LEN) {\n        1,\n        2,\n        3,\n        4,\n    })\n\n    LEN = 10\n    Name(BAZ, Package (LEN) {\n        4,\n        11,\n        16,\n    })\n}\n"
  },
  {
    "path": "tests/pc-bios_acpi-dsdt.asl",
    "content": "/*\n * Intel ACPI Component Architecture\n * AML/ASL+ Disassembler version 20180427 (64-bit version)(RM)\n * Copyright (c) 2000 - 2018 Intel Corporation\n * \n * Disassembling to symbolic ASL+ operators\n *\n * Disassembly of pc-bios_acpi-dsdt.aml, Wed Sep 23 20:26:45 2020\n *\n * Original Table Header:\n *     Signature        \"DSDT\"\n *     Length           0x00001137 (4407)\n *     Revision         0x01 **** 32-bit table (V1), no 64-bit math support\n *     Checksum         0xF0\n *     OEM ID           \"BXPC\"\n *     OEM Table ID     \"BXDSDT\"\n *     OEM Revision     0x00000001 (1)\n *     Compiler ID      \"INTL\"\n *     Compiler Version 0x20120913 (538052883)\n */\n DefinitionBlock (\"\", \"DSDT\", 1, \"BXPC\", \"BXDSDT\", 0x00000001)\n {\n     /*\n      * iASL Warning: There were 2 external control methods found during\n      * disassembly, but only 0 were resolved (2 unresolved). Additional\n      * ACPI tables may be required to properly disassemble the code. This\n      * resulting disassembler output file may not compile because the\n      * disassembler did not know how many arguments to assign to the\n      * unresolved methods. Note: SSDTs can be dynamically loaded at\n      * runtime and may or may not be available via the host OS.\n      *\n      * To specify the tables needed to resolve external control method\n      * references, the -e option can be used to specify the filenames.\n      * Example iASL invocations:\n      *     iasl -e ssdt1.aml ssdt2.aml ssdt3.aml -d dsdt.aml\n      *     iasl -e dsdt.aml ssdt2.aml -d ssdt1.aml\n      *     iasl -e ssdt*.aml -d dsdt.aml\n      *\n      * In addition, the -fe option can be used to specify a file containing\n      * control method external declarations with the associated method\n      * argument counts. Each line of the file must be of the form:\n      *     External (<method pathname>, MethodObj, <argument count>)\n      * Invocation:\n      *     iasl -fe refs.txt -d dsdt.aml\n      *\n      * The following methods were unresolved and many not compile properly\n      * because the disassembler had to guess at the number of arguments\n      * required for each:\n      */\n     External (CPON, UnknownObj)    // Warning: Unknown object\n     External (NTFY, MethodObj)    // Warning: Unknown method, guessing 2 arguments\n     External (P0E_, IntObj)    // Warning: Unknown object\n     External (P0S_, IntObj)    // Warning: Unknown object\n     External (P1E_, IntObj)    // Warning: Unknown object\n     External (P1L_, IntObj)    // Warning: Unknown object\n     External (P1S_, IntObj)    // Warning: Unknown object\n     External (P1V_, UnknownObj)    // Warning: Unknown object\n     External (PCNT, MethodObj)    // Warning: Unknown method, guessing 2 arguments\n \n     Scope (\\)\n     {\n         OperationRegion (DBG, SystemIO, 0x0402, One)\n         Field (DBG, ByteAcc, NoLock, Preserve)\n         {\n             DBGB,   8\n         }\n \n         Method (DBUG, 1, NotSerialized)\n         {\n             ToHexString (Arg0, Local0)\n             ToBuffer (Local0, Local0)\n             Local1 = (SizeOf (Local0) - One)\n             Local2 = Zero\n             While ((Local2 < Local1))\n             {\n                 DBGB = DerefOf (Local0 [Local2])\n                 Local2++\n             }\n \n             DBGB = 0x0A\n         }\n     }\n \n     Scope (_SB)\n     {\n         Device (PCI0)\n         {\n             Name (_HID, EisaId (\"PNP0A03\"))  // _HID: Hardware ID\n             Name (_ADR, Zero)  // _ADR: Address\n             Name (_UID, One)  // _UID: Unique ID\n         }\n     }\n \n     Scope (_SB.PCI0)\n     {\n         Name (CRES, ResourceTemplate ()\n         {\n             WordBusNumber (ResourceProducer, MinFixed, MaxFixed, PosDecode,\n                 0x0000,             // Granularity\n                 0x0000,             // Range Minimum\n                 0x00FF,             // Range Maximum\n                 0x0000,             // Translation Offset\n                 0x0100,             // Length\n                 ,, )\n             IO (Decode16,\n                 0x0CF8,             // Range Minimum\n                 0x0CF8,             // Range Maximum\n                 0x01,               // Alignment\n                 0x08,               // Length\n                 )\n             WordIO (ResourceProducer, MinFixed, MaxFixed, PosDecode, EntireRange,\n                 0x0000,             // Granularity\n                 0x0000,             // Range Minimum\n                 0x0CF7,             // Range Maximum\n                 0x0000,             // Translation Offset\n                 0x0CF8,             // Length\n                 ,, , TypeStatic, DenseTranslation)\n             WordIO (ResourceProducer, MinFixed, MaxFixed, PosDecode, EntireRange,\n                 0x0000,             // Granularity\n                 0x0D00,             // Range Minimum\n                 0xFFFF,             // Range Maximum\n                 0x0000,             // Translation Offset\n                 0xF300,             // Length\n                 ,, , TypeStatic, DenseTranslation)\n             DWordMemory (ResourceProducer, PosDecode, MinFixed, MaxFixed, Cacheable, ReadWrite,\n                 0x00000000,         // Granularity\n                 0x000A0000,         // Range Minimum\n                 0x000BFFFF,         // Range Maximum\n                 0x00000000,         // Translation Offset\n                 0x00020000,         // Length\n                 ,, , AddressRangeMemory, TypeStatic)\n             DWordMemory (ResourceProducer, PosDecode, MinFixed, MaxFixed, NonCacheable, ReadWrite,\n                 0x00000000,         // Granularity\n                 0xE0000000,         // Range Minimum\n                 0xFEBFFFFF,         // Range Maximum\n                 0x00000000,         // Translation Offset\n                 0x1EC00000,         // Length\n                 ,, _Y00, AddressRangeMemory, TypeStatic)\n         })\n         Name (CR64, ResourceTemplate ()\n         {\n             QWordMemory (ResourceProducer, PosDecode, MinFixed, MaxFixed, Cacheable, ReadWrite,\n                 0x0000000000000000, // Granularity\n                 0x0000008000000000, // Range Minimum\n                 0x000000FFFFFFFFFF, // Range Maximum\n                 0x0000000000000000, // Translation Offset\n                 0x0000008000000000, // Length\n                 ,, _Y01, AddressRangeMemory, TypeStatic)\n         })\n         Method (_CRS, 0, NotSerialized)  // _CRS: Current Resource Settings\n         {\n             CreateDWordField (CRES, \\_SB.PCI0._Y00._MIN, PS32)  // _MIN: Minimum Base Address\n             CreateDWordField (CRES, \\_SB.PCI0._Y00._MAX, PE32)  // _MAX: Maximum Base Address\n             CreateDWordField (CRES, \\_SB.PCI0._Y00._LEN, PL32)  // _LEN: Length\n             PS32 = P0S\n             PE32 = P0E\n             PL32 = ((P0E - P0S) + One)\n             If ((P1V == Zero))\n             {\n                 Return (CRES)\n             }\n \n             CreateQWordField (CR64, \\_SB.PCI0._Y01._MIN, PS64)  // _MIN: Minimum Base Address\n             CreateQWordField (CR64, \\_SB.PCI0._Y01._MAX, PE64)  // _MAX: Maximum Base Address\n             CreateQWordField (CR64, \\_SB.PCI0._Y01._LEN, PL64)  // _LEN: Length\n             PS64 = P1S\n             PE64 = P1E\n             PL64 = P1L\n             ConcatenateResTemplate (CRES, CR64, Local0)\n             Return (Local0)\n         }\n     }\n \n     Scope (_SB)\n     {\n         Device (HPET)\n         {\n             Name (_HID, EisaId (\"PNP0103\"))  // _HID: Hardware ID\n             Name (_UID, Zero)  // _UID: Unique ID\n             OperationRegion (HPTM, SystemMemory, 0xFED00000, 0x0400)\n             Field (HPTM, DWordAcc, Lock, Preserve)\n             {\n                 VEND,   32, \n                 PRD,    32\n             }\n \n             Method (_STA, 0, NotSerialized)  // _STA: Status\n             {\n                 Local0 = VEND\n                 Local1 = PRD\n                 Local0 >>= 0x10\n                 If (((Local0 == Zero) || (Local0 == 0xFFFF)))\n                 {\n                     Return (Zero)\n                 }\n \n                 If (((Local1 == Zero) || (Local1 > 0x05F5E100)))\n                 {\n                     Return (Zero)\n                 }\n \n                 Return (0x0F)\n             }\n \n             Name (_CRS, ResourceTemplate ()  // _CRS: Current Resource Settings\n             {\n                 Memory32Fixed (ReadOnly,\n                     0xFED00000,         // Address Base\n                     0x00000400,         // Address Length\n                     )\n             })\n         }\n     }\n \n     Scope (_SB.PCI0)\n     {\n         Device (VGA)\n         {\n             Name (_ADR, 0x00020000)  // _ADR: Address\n             OperationRegion (PCIC, PCI_Config, Zero, 0x04)\n             Field (PCIC, DWordAcc, NoLock, Preserve)\n             {\n                 VEND,   32\n             }\n \n             Method (_S1D, 0, NotSerialized)  // _S1D: S1 Device State\n             {\n                 Return (Zero)\n             }\n \n             Method (_S2D, 0, NotSerialized)  // _S2D: S2 Device State\n             {\n                 Return (Zero)\n             }\n \n             Method (_S3D, 0, NotSerialized)  // _S3D: S3 Device State\n             {\n                 If ((VEND == 0x01001B36))\n                 {\n                     Return (0x03)\n                 }\n                 Else\n                 {\n                     Return (Zero)\n                 }\n             }\n         }\n     }\n \n     Scope (_SB.PCI0)\n     {\n         Device (PX13)\n         {\n             Name (_ADR, 0x00010003)  // _ADR: Address\n             OperationRegion (P13C, PCI_Config, Zero, 0xFF)\n         }\n     }\n \n     Scope (_SB.PCI0)\n     {\n         Device (ISA)\n         {\n             Name (_ADR, 0x00010000)  // _ADR: Address\n             OperationRegion (P40C, PCI_Config, 0x60, 0x04)\n             Field (^PX13.P13C, AnyAcc, NoLock, Preserve)\n             {\n                 Offset (0x5F), \n                     ,   7, \n                 LPEN,   1, \n                 Offset (0x67), \n                     ,   3, \n                 CAEN,   1, \n                     ,   3, \n                 CBEN,   1\n             }\n \n             Name (FDEN, One)\n         }\n     }\n \n     Scope (_SB.PCI0.ISA)\n     {\n         Device (RTC)\n         {\n             Name (_HID, EisaId (\"PNP0B00\"))  // _HID: Hardware ID\n             Name (_CRS, ResourceTemplate ()  // _CRS: Current Resource Settings\n             {\n                 IO (Decode16,\n                     0x0070,             // Range Minimum\n                     0x0070,             // Range Maximum\n                     0x10,               // Alignment\n                     0x02,               // Length\n                     )\n                 IRQNoFlags ()\n                     {8}\n                 IO (Decode16,\n                     0x0072,             // Range Minimum\n                     0x0072,             // Range Maximum\n                     0x02,               // Alignment\n                     0x06,               // Length\n                     )\n             })\n         }\n \n         Device (KBD)\n         {\n             Name (_HID, EisaId (\"PNP0303\"))  // _HID: Hardware ID\n             Method (_STA, 0, NotSerialized)  // _STA: Status\n             {\n                 Return (0x0F)\n             }\n \n             Name (_CRS, ResourceTemplate ()  // _CRS: Current Resource Settings\n             {\n                 IO (Decode16,\n                     0x0060,             // Range Minimum\n                     0x0060,             // Range Maximum\n                     0x01,               // Alignment\n                     0x01,               // Length\n                     )\n                 IO (Decode16,\n                     0x0064,             // Range Minimum\n                     0x0064,             // Range Maximum\n                     0x01,               // Alignment\n                     0x01,               // Length\n                     )\n                 IRQNoFlags ()\n                     {1}\n             })\n         }\n \n         Device (MOU)\n         {\n             Name (_HID, EisaId (\"PNP0F13\"))  // _HID: Hardware ID\n             Method (_STA, 0, NotSerialized)  // _STA: Status\n             {\n                 Return (0x0F)\n             }\n \n             Name (_CRS, ResourceTemplate ()  // _CRS: Current Resource Settings\n             {\n                 IRQNoFlags ()\n                     {12}\n             })\n         }\n \n         Device (FDC0)\n         {\n             Name (_HID, EisaId (\"PNP0700\"))  // _HID: Hardware ID\n             Method (_STA, 0, NotSerialized)  // _STA: Status\n             {\n                 Local0 = FDEN\n                 If ((Local0 == Zero))\n                 {\n                     Return (Zero)\n                 }\n                 Else\n                 {\n                     Return (0x0F)\n                 }\n             }\n \n             Name (_CRS, ResourceTemplate ()  // _CRS: Current Resource Settings\n             {\n                 IO (Decode16,\n                     0x03F2,             // Range Minimum\n                     0x03F2,             // Range Maximum\n                     0x00,               // Alignment\n                     0x04,               // Length\n                     )\n                 IO (Decode16,\n                     0x03F7,             // Range Minimum\n                     0x03F7,             // Range Maximum\n                     0x00,               // Alignment\n                     0x01,               // Length\n                     )\n                 IRQNoFlags ()\n                     {6}\n                 DMA (Compatibility, NotBusMaster, Transfer8, )\n                     {2}\n             })\n         }\n \n         Device (LPT)\n         {\n             Name (_HID, EisaId (\"PNP0400\"))  // _HID: Hardware ID\n             Method (_STA, 0, NotSerialized)  // _STA: Status\n             {\n                 Local0 = LPEN\n                 If ((Local0 == Zero))\n                 {\n                     Return (Zero)\n                 }\n                 Else\n                 {\n                     Return (0x0F)\n                 }\n             }\n \n             Name (_CRS, ResourceTemplate ()  // _CRS: Current Resource Settings\n             {\n                 IO (Decode16,\n                     0x0378,             // Range Minimum\n                     0x0378,             // Range Maximum\n                     0x08,               // Alignment\n                     0x08,               // Length\n                     )\n                 IRQNoFlags ()\n                     {7}\n             })\n         }\n \n         Device (COM1)\n         {\n             Name (_HID, EisaId (\"PNP0501\"))  // _HID: Hardware ID\n             Name (_UID, One)  // _UID: Unique ID\n             Method (_STA, 0, NotSerialized)  // _STA: Status\n             {\n                 Local0 = CAEN\n                 If ((Local0 == Zero))\n                 {\n                     Return (Zero)\n                 }\n                 Else\n                 {\n                     Return (0x0F)\n                 }\n             }\n \n             Name (_CRS, ResourceTemplate ()  // _CRS: Current Resource Settings\n             {\n                 IO (Decode16,\n                     0x03F8,             // Range Minimum\n                     0x03F8,             // Range Maximum\n                     0x00,               // Alignment\n                     0x08,               // Length\n                     )\n                 IRQNoFlags ()\n                     {4}\n             })\n         }\n \n         Device (COM2)\n         {\n             Name (_HID, EisaId (\"PNP0501\"))  // _HID: Hardware ID\n             Name (_UID, 0x02)  // _UID: Unique ID\n             Method (_STA, 0, NotSerialized)  // _STA: Status\n             {\n                 Local0 = CBEN\n                 If ((Local0 == Zero))\n                 {\n                     Return (Zero)\n                 }\n                 Else\n                 {\n                     Return (0x0F)\n                 }\n             }\n \n             Name (_CRS, ResourceTemplate ()  // _CRS: Current Resource Settings\n             {\n                 IO (Decode16,\n                     0x02F8,             // Range Minimum\n                     0x02F8,             // Range Maximum\n                     0x00,               // Alignment\n                     0x08,               // Length\n                     )\n                 IRQNoFlags ()\n                     {3}\n             })\n         }\n     }\n \n     Scope (_SB.PCI0)\n     {\n         OperationRegion (PCST, SystemIO, 0xAE00, 0x08)\n         Field (PCST, DWordAcc, NoLock, WriteAsZeros)\n         {\n             PCIU,   32, \n             PCID,   32\n         }\n \n         OperationRegion (SEJ, SystemIO, 0xAE08, 0x04)\n         Field (SEJ, DWordAcc, NoLock, WriteAsZeros)\n         {\n             B0EJ,   32\n         }\n \n         Method (PCEJ, 1, NotSerialized)\n         {\n             B0EJ = (One << Arg0)\n             Return (Zero)\n         }\n \n         Method (PCNF, 0, NotSerialized)\n         {\n             Local0 = Zero\n             While ((Local0 < 0x1F))\n             {\n                 Local0++\n                 If ((PCIU & (One << Local0)))\n                 {\n                     PCNT (Local0, One)\n                 }\n \n                 If ((PCID & (One << Local0)))\n                 {\n                     PCNT (Local0, 0x03)\n                 }\n             }\n         }\n     }\n \n     Scope (_SB)\n     {\n         Scope (PCI0)\n         {\n             Name (_PRT, Package (0x80)  // _PRT: PCI Routing Table\n             {\n                 Package (0x04)\n                 {\n                     0xFFFF, \n                     Zero, \n                     LNKD, \n                     Zero\n                 }, \n \n                 Package (0x04)\n                 {\n                     0xFFFF, \n                     One, \n                     LNKA, \n                     Zero\n                 }, \n \n                 Package (0x04)\n                 {\n                     0xFFFF, \n                     0x02, \n                     LNKB, \n                     Zero\n                 }, \n \n                 Package (0x04)\n                 {\n                     0xFFFF, \n                     0x03, \n                     LNKC, \n                     Zero\n                 }, \n \n                 Package (0x04)\n                 {\n                     0x0001FFFF, \n                     Zero, \n                     LNKS, \n                     Zero\n                 }, \n \n                 Package (0x04)\n                 {\n                     0x0001FFFF, \n                     One, \n                     LNKB, \n                     Zero\n                 }, \n \n                 Package (0x04)\n                 {\n                     0x0001FFFF, \n                     0x02, \n                     LNKC, \n                     Zero\n                 }, \n \n                 Package (0x04)\n                 {\n                     0x0001FFFF, \n                     0x03, \n                     LNKD, \n                     Zero\n                 }, \n \n                 Package (0x04)\n                 {\n                     0x0002FFFF, \n                     Zero, \n                     LNKB, \n                     Zero\n                 }, \n \n                 Package (0x04)\n                 {\n                     0x0002FFFF, \n                     One, \n                     LNKC, \n                     Zero\n                 }, \n \n                 Package (0x04)\n                 {\n                     0x0002FFFF, \n                     0x02, \n                     LNKD, \n                     Zero\n                 }, \n \n                 Package (0x04)\n                 {\n                     0x0002FFFF, \n                     0x03, \n                     LNKA, \n                     Zero\n                 }, \n \n                 Package (0x04)\n                 {\n                     0x0003FFFF, \n                     Zero, \n                     LNKC, \n                     Zero\n                 }, \n \n                 Package (0x04)\n                 {\n                     0x0003FFFF, \n                     One, \n                     LNKD, \n                     Zero\n                 }, \n \n                 Package (0x04)\n                 {\n                     0x0003FFFF, \n                     0x02, \n                     LNKA, \n                     Zero\n                 }, \n \n                 Package (0x04)\n                 {\n                     0x0003FFFF, \n                     0x03, \n                     LNKB, \n                     Zero\n                 }, \n \n                 Package (0x04)\n                 {\n                     0x0004FFFF, \n                     Zero, \n                     LNKD, \n                     Zero\n                 }, \n \n                 Package (0x04)\n                 {\n                     0x0004FFFF, \n                     One, \n                     LNKA, \n                     Zero\n                 }, \n \n                 Package (0x04)\n                 {\n                     0x0004FFFF, \n                     0x02, \n                     LNKB, \n                     Zero\n                 }, \n \n                 Package (0x04)\n                 {\n                     0x0004FFFF, \n                     0x03, \n                     LNKC, \n                     Zero\n                 }, \n \n                 Package (0x04)\n                 {\n                     0x0005FFFF, \n                     Zero, \n                     LNKA, \n                     Zero\n                 }, \n \n                 Package (0x04)\n                 {\n                     0x0005FFFF, \n                     One, \n                     LNKB, \n                     Zero\n                 }, \n \n                 Package (0x04)\n                 {\n                     0x0005FFFF, \n                     0x02, \n                     LNKC, \n                     Zero\n                 }, \n \n                 Package (0x04)\n                 {\n                     0x0005FFFF, \n                     0x03, \n                     LNKD, \n                     Zero\n                 }, \n \n                 Package (0x04)\n                 {\n                     0x0006FFFF, \n                     Zero, \n                     LNKB, \n                     Zero\n                 }, \n \n                 Package (0x04)\n                 {\n                     0x0006FFFF, \n                     One, \n                     LNKC, \n                     Zero\n                 }, \n \n                 Package (0x04)\n                 {\n                     0x0006FFFF, \n                     0x02, \n                     LNKD, \n                     Zero\n                 }, \n \n                 Package (0x04)\n                 {\n                     0x0006FFFF, \n                     0x03, \n                     LNKA, \n                     Zero\n                 }, \n \n                 Package (0x04)\n                 {\n                     0x0007FFFF, \n                     Zero, \n                     LNKC, \n                     Zero\n                 }, \n \n                 Package (0x04)\n                 {\n                     0x0007FFFF, \n                     One, \n                     LNKD, \n                     Zero\n                 }, \n \n                 Package (0x04)\n                 {\n                     0x0007FFFF, \n                     0x02, \n                     LNKA, \n                     Zero\n                 }, \n \n                 Package (0x04)\n                 {\n                     0x0007FFFF, \n                     0x03, \n                     LNKB, \n                     Zero\n                 }, \n \n                 Package (0x04)\n                 {\n                     0x0008FFFF, \n                     Zero, \n                     LNKD, \n                     Zero\n                 }, \n \n                 Package (0x04)\n                 {\n                     0x0008FFFF, \n                     One, \n                     LNKA, \n                     Zero\n                 }, \n \n                 Package (0x04)\n                 {\n                     0x0008FFFF, \n                     0x02, \n                     LNKB, \n                     Zero\n                 }, \n \n                 Package (0x04)\n                 {\n                     0x0008FFFF, \n                     0x03, \n                     LNKC, \n                     Zero\n                 }, \n \n                 Package (0x04)\n                 {\n                     0x0009FFFF, \n                     Zero, \n                     LNKA, \n                     Zero\n                 }, \n \n                 Package (0x04)\n                 {\n                     0x0009FFFF, \n                     One, \n                     LNKB, \n                     Zero\n                 }, \n \n                 Package (0x04)\n                 {\n                     0x0009FFFF, \n                     0x02, \n                     LNKC, \n                     Zero\n                 }, \n \n                 Package (0x04)\n                 {\n                     0x0009FFFF, \n                     0x03, \n                     LNKD, \n                     Zero\n                 }, \n \n                 Package (0x04)\n                 {\n                     0x000AFFFF, \n                     Zero, \n                     LNKB, \n                     Zero\n                 }, \n \n                 Package (0x04)\n                 {\n                     0x000AFFFF, \n                     One, \n                     LNKC, \n                     Zero\n                 }, \n \n                 Package (0x04)\n                 {\n                     0x000AFFFF, \n                     0x02, \n                     LNKD, \n                     Zero\n                 }, \n \n                 Package (0x04)\n                 {\n                     0x000AFFFF, \n                     0x03, \n                     LNKA, \n                     Zero\n                 }, \n \n                 Package (0x04)\n                 {\n                     0x000BFFFF, \n                     Zero, \n                     LNKC, \n                     Zero\n                 }, \n \n                 Package (0x04)\n                 {\n                     0x000BFFFF, \n                     One, \n                     LNKD, \n                     Zero\n                 }, \n \n                 Package (0x04)\n                 {\n                     0x000BFFFF, \n                     0x02, \n                     LNKA, \n                     Zero\n                 }, \n \n                 Package (0x04)\n                 {\n                     0x000BFFFF, \n                     0x03, \n                     LNKB, \n                     Zero\n                 }, \n \n                 Package (0x04)\n                 {\n                     0x000CFFFF, \n                     Zero, \n                     LNKD, \n                     Zero\n                 }, \n \n                 Package (0x04)\n                 {\n                     0x000CFFFF, \n                     One, \n                     LNKA, \n                     Zero\n                 }, \n \n                 Package (0x04)\n                 {\n                     0x000CFFFF, \n                     0x02, \n                     LNKB, \n                     Zero\n                 }, \n \n                 Package (0x04)\n                 {\n                     0x000CFFFF, \n                     0x03, \n                     LNKC, \n                     Zero\n                 }, \n \n                 Package (0x04)\n                 {\n                     0x000DFFFF, \n                     Zero, \n                     LNKA, \n                     Zero\n                 }, \n \n                 Package (0x04)\n                 {\n                     0x000DFFFF, \n                     One, \n                     LNKB, \n                     Zero\n                 }, \n \n                 Package (0x04)\n                 {\n                     0x000DFFFF, \n                     0x02, \n                     LNKC, \n                     Zero\n                 }, \n \n                 Package (0x04)\n                 {\n                     0x000DFFFF, \n                     0x03, \n                     LNKD, \n                     Zero\n                 }, \n \n                 Package (0x04)\n                 {\n                     0x000EFFFF, \n                     Zero, \n                     LNKB, \n                     Zero\n                 }, \n \n                 Package (0x04)\n                 {\n                     0x000EFFFF, \n                     One, \n                     LNKC, \n                     Zero\n                 }, \n \n                 Package (0x04)\n                 {\n                     0x000EFFFF, \n                     0x02, \n                     LNKD, \n                     Zero\n                 }, \n \n                 Package (0x04)\n                 {\n                     0x000EFFFF, \n                     0x03, \n                     LNKA, \n                     Zero\n                 }, \n \n                 Package (0x04)\n                 {\n                     0x000FFFFF, \n                     Zero, \n                     LNKC, \n                     Zero\n                 }, \n \n                 Package (0x04)\n                 {\n                     0x000FFFFF, \n                     One, \n                     LNKD, \n                     Zero\n                 }, \n \n                 Package (0x04)\n                 {\n                     0x000FFFFF, \n                     0x02, \n                     LNKA, \n                     Zero\n                 }, \n \n                 Package (0x04)\n                 {\n                     0x000FFFFF, \n                     0x03, \n                     LNKB, \n                     Zero\n                 }, \n \n                 Package (0x04)\n                 {\n                     0x0010FFFF, \n                     Zero, \n                     LNKD, \n                     Zero\n                 }, \n \n                 Package (0x04)\n                 {\n                     0x0010FFFF, \n                     One, \n                     LNKA, \n                     Zero\n                 }, \n \n                 Package (0x04)\n                 {\n                     0x0010FFFF, \n                     0x02, \n                     LNKB, \n                     Zero\n                 }, \n \n                 Package (0x04)\n                 {\n                     0x0010FFFF, \n                     0x03, \n                     LNKC, \n                     Zero\n                 }, \n \n                 Package (0x04)\n                 {\n                     0x0011FFFF, \n                     Zero, \n                     LNKA, \n                     Zero\n                 }, \n \n                 Package (0x04)\n                 {\n                     0x0011FFFF, \n                     One, \n                     LNKB, \n                     Zero\n                 }, \n \n                 Package (0x04)\n                 {\n                     0x0011FFFF, \n                     0x02, \n                     LNKC, \n                     Zero\n                 }, \n \n                 Package (0x04)\n                 {\n                     0x0011FFFF, \n                     0x03, \n                     LNKD, \n                     Zero\n                 }, \n \n                 Package (0x04)\n                 {\n                     0x0012FFFF, \n                     Zero, \n                     LNKB, \n                     Zero\n                 }, \n \n                 Package (0x04)\n                 {\n                     0x0012FFFF, \n                     One, \n                     LNKC, \n                     Zero\n                 }, \n \n                 Package (0x04)\n                 {\n                     0x0012FFFF, \n                     0x02, \n                     LNKD, \n                     Zero\n                 }, \n \n                 Package (0x04)\n                 {\n                     0x0012FFFF, \n                     0x03, \n                     LNKA, \n                     Zero\n                 }, \n \n                 Package (0x04)\n                 {\n                     0x0013FFFF, \n                     Zero, \n                     LNKC, \n                     Zero\n                 }, \n \n                 Package (0x04)\n                 {\n                     0x0013FFFF, \n                     One, \n                     LNKD, \n                     Zero\n                 }, \n \n                 Package (0x04)\n                 {\n                     0x0013FFFF, \n                     0x02, \n                     LNKA, \n                     Zero\n                 }, \n \n                 Package (0x04)\n                 {\n                     0x0013FFFF, \n                     0x03, \n                     LNKB, \n                     Zero\n                 }, \n \n                 Package (0x04)\n                 {\n                     0x0014FFFF, \n                     Zero, \n                     LNKD, \n                     Zero\n                 }, \n \n                 Package (0x04)\n                 {\n                     0x0014FFFF, \n                     One, \n                     LNKA, \n                     Zero\n                 }, \n \n                 Package (0x04)\n                 {\n                     0x0014FFFF, \n                     0x02, \n                     LNKB, \n                     Zero\n                 }, \n \n                 Package (0x04)\n                 {\n                     0x0014FFFF, \n                     0x03, \n                     LNKC, \n                     Zero\n                 }, \n \n                 Package (0x04)\n                 {\n                     0x0015FFFF, \n                     Zero, \n                     LNKA, \n                     Zero\n                 }, \n \n                 Package (0x04)\n                 {\n                     0x0015FFFF, \n                     One, \n                     LNKB, \n                     Zero\n                 }, \n \n                 Package (0x04)\n                 {\n                     0x0015FFFF, \n                     0x02, \n                     LNKC, \n                     Zero\n                 }, \n \n                 Package (0x04)\n                 {\n                     0x0015FFFF, \n                     0x03, \n                     LNKD, \n                     Zero\n                 }, \n \n                 Package (0x04)\n                 {\n                     0x0016FFFF, \n                     Zero, \n                     LNKB, \n                     Zero\n                 }, \n \n                 Package (0x04)\n                 {\n                     0x0016FFFF, \n                     One, \n                     LNKC, \n                     Zero\n                 }, \n \n                 Package (0x04)\n                 {\n                     0x0016FFFF, \n                     0x02, \n                     LNKD, \n                     Zero\n                 }, \n \n                 Package (0x04)\n                 {\n                     0x0016FFFF, \n                     0x03, \n                     LNKA, \n                     Zero\n                 }, \n \n                 Package (0x04)\n                 {\n                     0x0017FFFF, \n                     Zero, \n                     LNKC, \n                     Zero\n                 }, \n \n                 Package (0x04)\n                 {\n                     0x0017FFFF, \n                     One, \n                     LNKD, \n                     Zero\n                 }, \n \n                 Package (0x04)\n                 {\n                     0x0017FFFF, \n                     0x02, \n                     LNKA, \n                     Zero\n                 }, \n \n                 Package (0x04)\n                 {\n                     0x0017FFFF, \n                     0x03, \n                     LNKB, \n                     Zero\n                 }, \n \n                 Package (0x04)\n                 {\n                     0x0018FFFF, \n                     Zero, \n                     LNKD, \n                     Zero\n                 }, \n \n                 Package (0x04)\n                 {\n                     0x0018FFFF, \n                     One, \n                     LNKA, \n                     Zero\n                 }, \n \n                 Package (0x04)\n                 {\n                     0x0018FFFF, \n                     0x02, \n                     LNKB, \n                     Zero\n                 }, \n \n                 Package (0x04)\n                 {\n                     0x0018FFFF, \n                     0x03, \n                     LNKC, \n                     Zero\n                 }, \n \n                 Package (0x04)\n                 {\n                     0x0019FFFF, \n                     Zero, \n                     LNKA, \n                     Zero\n                 }, \n \n                 Package (0x04)\n                 {\n                     0x0019FFFF, \n                     One, \n                     LNKB, \n                     Zero\n                 }, \n \n                 Package (0x04)\n                 {\n                     0x0019FFFF, \n                     0x02, \n                     LNKC, \n                     Zero\n                 }, \n \n                 Package (0x04)\n                 {\n                     0x0019FFFF, \n                     0x03, \n                     LNKD, \n                     Zero\n                 }, \n \n                 Package (0x04)\n                 {\n                     0x001AFFFF, \n                     Zero, \n                     LNKB, \n                     Zero\n                 }, \n \n                 Package (0x04)\n                 {\n                     0x001AFFFF, \n                     One, \n                     LNKC, \n                     Zero\n                 }, \n \n                 Package (0x04)\n                 {\n                     0x001AFFFF, \n                     0x02, \n                     LNKD, \n                     Zero\n                 }, \n \n                 Package (0x04)\n                 {\n                     0x001AFFFF, \n                     0x03, \n                     LNKA, \n                     Zero\n                 }, \n \n                 Package (0x04)\n                 {\n                     0x001BFFFF, \n                     Zero, \n                     LNKC, \n                     Zero\n                 }, \n \n                 Package (0x04)\n                 {\n                     0x001BFFFF, \n                     One, \n                     LNKD, \n                     Zero\n                 }, \n \n                 Package (0x04)\n                 {\n                     0x001BFFFF, \n                     0x02, \n                     LNKA, \n                     Zero\n                 }, \n \n                 Package (0x04)\n                 {\n                     0x001BFFFF, \n                     0x03, \n                     LNKB, \n                     Zero\n                 }, \n \n                 Package (0x04)\n                 {\n                     0x001CFFFF, \n                     Zero, \n                     LNKD, \n                     Zero\n                 }, \n \n                 Package (0x04)\n                 {\n                     0x001CFFFF, \n                     One, \n                     LNKA, \n                     Zero\n                 }, \n \n                 Package (0x04)\n                 {\n                     0x001CFFFF, \n                     0x02, \n                     LNKB, \n                     Zero\n                 }, \n \n                 Package (0x04)\n                 {\n                     0x001CFFFF, \n                     0x03, \n                     LNKC, \n                     Zero\n                 }, \n \n                 Package (0x04)\n                 {\n                     0x001DFFFF, \n                     Zero, \n                     LNKA, \n                     Zero\n                 }, \n \n                 Package (0x04)\n                 {\n                     0x001DFFFF, \n                     One, \n                     LNKB, \n                     Zero\n                 }, \n \n                 Package (0x04)\n                 {\n                     0x001DFFFF, \n                     0x02, \n                     LNKC, \n                     Zero\n                 }, \n \n                 Package (0x04)\n                 {\n                     0x001DFFFF, \n                     0x03, \n                     LNKD, \n                     Zero\n                 }, \n \n                 Package (0x04)\n                 {\n                     0x001EFFFF, \n                     Zero, \n                     LNKB, \n                     Zero\n                 }, \n \n                 Package (0x04)\n                 {\n                     0x001EFFFF, \n                     One, \n                     LNKC, \n                     Zero\n                 }, \n \n                 Package (0x04)\n                 {\n                     0x001EFFFF, \n                     0x02, \n                     LNKD, \n                     Zero\n                 }, \n \n                 Package (0x04)\n                 {\n                     0x001EFFFF, \n                     0x03, \n                     LNKA, \n                     Zero\n                 }, \n \n                 Package (0x04)\n                 {\n                     0x001FFFFF, \n                     Zero, \n                     LNKC, \n                     Zero\n                 }, \n \n                 Package (0x04)\n                 {\n                     0x001FFFFF, \n                     One, \n                     LNKD, \n                     Zero\n                 }, \n \n                 Package (0x04)\n                 {\n                     0x001FFFFF, \n                     0x02, \n                     LNKA, \n                     Zero\n                 }, \n \n                 Package (0x04)\n                 {\n                     0x001FFFFF, \n                     0x03, \n                     LNKB, \n                     Zero\n                 }\n             })\n         }\n \n         Field (PCI0.ISA.P40C, ByteAcc, NoLock, Preserve)\n         {\n             PRQ0,   8, \n             PRQ1,   8, \n             PRQ2,   8, \n             PRQ3,   8\n         }\n \n         Method (IQST, 1, NotSerialized)\n         {\n             If ((0x80 & Arg0))\n             {\n                 Return (0x09)\n             }\n \n             Return (0x0B)\n         }\n \n         Method (IQCR, 1, NotSerialized)\n         {\n             Name (PRR0, ResourceTemplate ()\n             {\n                 Interrupt (ResourceConsumer, Level, ActiveHigh, Shared, ,, _Y02)\n                 {\n                     0x00000000,\n                 }\n             })\n             CreateDWordField (PRR0, \\_SB.IQCR._Y02._INT, PRRI)  // _INT: Interrupts\n             If ((Arg0 < 0x80))\n             {\n                 PRRI = Arg0\n             }\n \n             Return (PRR0)\n         }\n \n         Device (LNKA)\n         {\n             Name (_HID, EisaId (\"PNP0C0F\"))  // _HID: Hardware ID\n             Name (_UID, Zero)  // _UID: Unique ID\n             Name (_PRS, ResourceTemplate ()  // _PRS: Possible Resource Settings\n             {\n                 Interrupt (ResourceConsumer, Level, ActiveHigh, Shared, ,, )\n                 {\n                     0x00000005,\n                     0x0000000A,\n                     0x0000000B,\n                 }\n             })\n             Method (_STA, 0, NotSerialized)  // _STA: Status\n             {\n                 Return (IQST (PRQ0))\n             }\n \n             Method (_DIS, 0, NotSerialized)  // _DIS: Disable Device\n             {\n                 PRQ0 |= 0x80\n             }\n \n             Method (_CRS, 0, NotSerialized)  // _CRS: Current Resource Settings\n             {\n                 Return (IQCR (PRQ0))\n             }\n \n             Method (_SRS, 1, NotSerialized)  // _SRS: Set Resource Settings\n             {\n                 CreateDWordField (Arg0, 0x05, PRRI)\n                 PRQ0 = PRRI\n             }\n         }\n \n         Device (LNKB)\n         {\n             Name (_HID, EisaId (\"PNP0C0F\"))  // _HID: Hardware ID\n             Name (_UID, One)  // _UID: Unique ID\n             Name (_PRS, ResourceTemplate ()  // _PRS: Possible Resource Settings\n             {\n                 Interrupt (ResourceConsumer, Level, ActiveHigh, Shared, ,, )\n                 {\n                     0x00000005,\n                     0x0000000A,\n                     0x0000000B,\n                 }\n             })\n             Method (_STA, 0, NotSerialized)  // _STA: Status\n             {\n                 Return (IQST (PRQ1))\n             }\n \n             Method (_DIS, 0, NotSerialized)  // _DIS: Disable Device\n             {\n                 PRQ1 |= 0x80\n             }\n \n             Method (_CRS, 0, NotSerialized)  // _CRS: Current Resource Settings\n             {\n                 Return (IQCR (PRQ1))\n             }\n \n             Method (_SRS, 1, NotSerialized)  // _SRS: Set Resource Settings\n             {\n                 CreateDWordField (Arg0, 0x05, PRRI)\n                 PRQ1 = PRRI\n             }\n         }\n \n         Device (LNKC)\n         {\n             Name (_HID, EisaId (\"PNP0C0F\"))  // _HID: Hardware ID\n             Name (_UID, 0x02)  // _UID: Unique ID\n             Name (_PRS, ResourceTemplate ()  // _PRS: Possible Resource Settings\n             {\n                 Interrupt (ResourceConsumer, Level, ActiveHigh, Shared, ,, )\n                 {\n                     0x00000005,\n                     0x0000000A,\n                     0x0000000B,\n                 }\n             })\n             Method (_STA, 0, NotSerialized)  // _STA: Status\n             {\n                 Return (IQST (PRQ2))\n             }\n \n             Method (_DIS, 0, NotSerialized)  // _DIS: Disable Device\n             {\n                 PRQ2 |= 0x80\n             }\n \n             Method (_CRS, 0, NotSerialized)  // _CRS: Current Resource Settings\n             {\n                 Return (IQCR (PRQ2))\n             }\n \n             Method (_SRS, 1, NotSerialized)  // _SRS: Set Resource Settings\n             {\n                 CreateDWordField (Arg0, 0x05, PRRI)\n                 PRQ2 = PRRI\n             }\n         }\n \n         Device (LNKD)\n         {\n             Name (_HID, EisaId (\"PNP0C0F\"))  // _HID: Hardware ID\n             Name (_UID, 0x03)  // _UID: Unique ID\n             Name (_PRS, ResourceTemplate ()  // _PRS: Possible Resource Settings\n             {\n                 Interrupt (ResourceConsumer, Level, ActiveHigh, Shared, ,, )\n                 {\n                     0x00000005,\n                     0x0000000A,\n                     0x0000000B,\n                 }\n             })\n             Method (_STA, 0, NotSerialized)  // _STA: Status\n             {\n                 Return (IQST (PRQ3))\n             }\n \n             Method (_DIS, 0, NotSerialized)  // _DIS: Disable Device\n             {\n                 PRQ3 |= 0x80\n             }\n \n             Method (_CRS, 0, NotSerialized)  // _CRS: Current Resource Settings\n             {\n                 Return (IQCR (PRQ3))\n             }\n \n             Method (_SRS, 1, NotSerialized)  // _SRS: Set Resource Settings\n             {\n                 CreateDWordField (Arg0, 0x05, PRRI)\n                 PRQ3 = PRRI\n             }\n         }\n \n         Device (LNKS)\n         {\n             Name (_HID, EisaId (\"PNP0C0F\"))  // _HID: Hardware ID\n             Name (_UID, 0x04)  // _UID: Unique ID\n             Name (_PRS, ResourceTemplate ()  // _PRS: Possible Resource Settings\n             {\n                 Interrupt (ResourceConsumer, Level, ActiveHigh, Shared, ,, )\n                 {\n                     0x00000009,\n                 }\n             })\n             Method (_STA, 0, NotSerialized)  // _STA: Status\n             {\n                 Return (0x0B)\n             }\n \n             Method (_DIS, 0, NotSerialized)  // _DIS: Disable Device\n             {\n             }\n \n             Method (_CRS, 0, NotSerialized)  // _CRS: Current Resource Settings\n             {\n                 Return (_PRS)\n             }\n \n             Method (_SRS, 1, NotSerialized)  // _SRS: Set Resource Settings\n             {\n             }\n         }\n     }\n \n     Scope (_SB)\n     {\n         Method (CPMA, 1, NotSerialized)\n         {\n             Local0 = DerefOf (CPON [Arg0])\n             Local1 = Buffer (0x08)\n                 {\n                      0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 \n                 }\n             Local1 [0x02] = Arg0\n             Local1 [0x03] = Arg0\n             Local1 [0x04] = Local0\n             Return (Local1)\n         }\n \n         Method (CPST, 1, NotSerialized)\n         {\n             Local0 = DerefOf (CPON [Arg0])\n             If (Local0)\n             {\n                 Return (0x0F)\n             }\n             Else\n             {\n                 Return (Zero)\n             }\n         }\n \n         Method (CPEJ, 2, NotSerialized)\n         {\n             Sleep (0xC8)\n         }\n \n         OperationRegion (PRST, SystemIO, 0xAF00, 0x20)\n         Field (PRST, ByteAcc, NoLock, Preserve)\n         {\n             PRS,    256\n         }\n \n         Method (PRSC, 0, NotSerialized)\n         {\n             Local5 = PRS\n             Local2 = Zero\n             Local0 = Zero\n             While ((Local0 < SizeOf (CPON)))\n             {\n                 Local1 = DerefOf (CPON [Local0])\n                 If ((Local0 & 0x07))\n                 {\n                     Local2 >>= One\n                 }\n                 Else\n                 {\n                     Local2 = DerefOf (Local5 [(Local0 >> 0x03)])\n                 }\n \n                 Local3 = (Local2 & One)\n                 If ((Local1 != Local3))\n                 {\n                     CPON [Local0] = Local3\n                     If ((Local3 == One))\n                     {\n                         NTFY (Local0, One)\n                     }\n                     Else\n                     {\n                         NTFY (Local0, 0x03)\n                     }\n                 }\n \n                 Local0++\n             }\n         }\n     }\n \n     Scope (_GPE)\n     {\n         Name (_HID, \"ACPI0006\")  // _HID: Hardware ID\n         Method (_L00, 0, NotSerialized)  // _Lxx: Level-Triggered GPE\n         {\n         }\n \n         Method (_E01, 0, NotSerialized)  // _Exx: Edge-Triggered GPE\n         {\n             \\_SB.PCI0.PCNF ()\n         }\n \n         Method (_E02, 0, NotSerialized)  // _Exx: Edge-Triggered GPE\n         {\n             \\_SB.PRSC ()\n         }\n \n         Method (_L03, 0, NotSerialized)  // _Lxx: Level-Triggered GPE\n         {\n         }\n \n         Method (_L04, 0, NotSerialized)  // _Lxx: Level-Triggered GPE\n         {\n         }\n \n         Method (_L05, 0, NotSerialized)  // _Lxx: Level-Triggered GPE\n         {\n         }\n \n         Method (_L06, 0, NotSerialized)  // _Lxx: Level-Triggered GPE\n         {\n         }\n \n         Method (_L07, 0, NotSerialized)  // _Lxx: Level-Triggered GPE\n         {\n         }\n \n         Method (_L08, 0, NotSerialized)  // _Lxx: Level-Triggered GPE\n         {\n         }\n \n         Method (_L09, 0, NotSerialized)  // _Lxx: Level-Triggered GPE\n         {\n         }\n \n         Method (_L0A, 0, NotSerialized)  // _Lxx: Level-Triggered GPE\n         {\n         }\n \n         Method (_L0B, 0, NotSerialized)  // _Lxx: Level-Triggered GPE\n         {\n         }\n \n         Method (_L0C, 0, NotSerialized)  // _Lxx: Level-Triggered GPE\n         {\n         }\n \n         Method (_L0D, 0, NotSerialized)  // _Lxx: Level-Triggered GPE\n         {\n         }\n \n         Method (_L0E, 0, NotSerialized)  // _Lxx: Level-Triggered GPE\n         {\n         }\n \n         Method (_L0F, 0, NotSerialized)  // _Lxx: Level-Triggered GPE\n         {\n         }\n     }\n }\n \n \n"
  },
  {
    "path": "tests/power_res.asl",
    "content": "DefinitionBlock(\"power_res.aml\", \"DSDT\", 1, \"RSACPI\", \"PWRRES\", 1) {\n    Scope(_SB) {\n        PowerResource(PIDE, 0, 1) {\n            Name(X, Zero)\n            Method(_STA) {\n                Return (X)\n            }\n            Method(_ON) {\n                Store(One, X)\n            }\n            Method(_OFF) {\n                Store(Zero, X)\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "tests/scopes.asl",
    "content": "DefinitionBlock(\"scopes.aml\", \"DSDT\", 1, \"RSACPI\", \"SCOPES\", 1) {\n    Scope(_SB) {\n        Name(X, 320)\n        Name(Y, Zero)\n\n        Device(PCI0) {\n            Name(Y, 15)\n            Name (_HID, EisaId (\"PNP0A03\"))\n            Scope(\\) {\n                Name(Z, 413)\n            }\n        }\n\n        Device(FOO) {\n            Name (_HID, EisaId (\"PNP0A03\"))\n            Alias (\\_SB.PCI0.Y, MY_Y)\n        }\n    }\n}\n"
  },
  {
    "path": "tests/test_infra/mod.rs",
    "content": "use acpi::Handler;\nuse aml_test_tools::{\n    RunTestResult,\n    TestResult,\n    handlers::logging_handler::LoggingHandler,\n    new_interpreter,\n    run_test_for_string,\n};\n\npub fn run_aml_test(asl: &'static str, handler: impl Handler) {\n    // Tests calling `run_aml_test` don't do much else, and we usually want logging, so initialize it here.\n    let _ = pretty_env_logger::try_init();\n\n    let logged_handler = LoggingHandler::new(handler);\n    let interpreter = new_interpreter(logged_handler);\n\n    let result = run_test_for_string(asl, interpreter, &None);\n    assert!(matches!(result, RunTestResult::Pass(_)), \"Test failed with: {:?}\", TestResult::from(&result));\n}\n"
  },
  {
    "path": "tests/thermal_zone.asl",
    "content": "DefinitionBlock(\"thermal_zone.aml\", \"DSDT\", 1, \"RSACPI\", \"THERMZ\", 1) {\n    Scope(_SB) {\n        Device(EC0) {\n            Name(_HID, EISAID(\"PNP0C09\"))\n            OperationRegion(EC0, EmbeddedControl, 0, 0xFF)\n            Field(EC0, ByteAcc, Lock, Preserve) {\n                MODE, 1, // thermal policy (quiet/perform)\n                FAN, 1, // fan power (on/off)\n                , 6, // reserved\n                TMP, 16, // current temp\n                AC0, 16, // active cooling temp (fan high)\n                , 16, // reserved\n                PSV, 16, // passive cooling temp\n                HOT, 16, // critical S4 temp\n                CRT, 16 // critical temp\n            }\n        }\n\n        Device(CPU0) {\n            Name(_HID, \"ACPI0007\")\n            Name(_UID, 1) // unique number for this processor\n        }\n\n        ThermalZone(TZ0) {\n            Method(_TMP) { Return (\\_SB.EC0.TMP )} // get current temp\n            Method(_AC0) { Return (\\_SB.EC0.AC0) } // fan high temp\n            Name(_AL0, Package(){\\_SB.EC0.FAN}) // fan is act cool dev\n            Method(_PSV) { Return (\\_SB.EC0.PSV) } // passive cooling temp\n            Name(_PSL, Package (){\\_SB.CPU0}) // passive cooling devices\n            Method(_HOT) { Return (\\_SB.EC0.HOT) } // get critical S4 temp\n            Method(_CRT) { Return (\\_SB.EC0.CRT) } // get critical temp\n            Method(_SCP, 1) { Store (Arg0, \\_SB.EC0.MODE) } // set cooling mode\n            Name(_TC1, 4) // bogus example constant\n            Name(_TC2, 3) // bogus example constant\n            Name(_TSP, 150) // passive sampling = 15 sec\n            Name(_TZP, 0) // polling not required\n            Name (_STR, Unicode (\"System thermal zone\"))\n        }\n    }\n}\n"
  },
  {
    "path": "tests/to_integer.asl",
    "content": "DefinitionBlock (\"\", \"SSDT\", 2, \"uTEST\", \"TESTTABL\", 0xF0F0F0F0)\n{\n    Name(FCNT, 0)\n\n    Method (CHEK, 3)\n    {\n        If (Arg0 != Arg1) {\n            FCNT++\n            Printf(\"On line %o: invalid number %o, expected %o\", ToDecimalString(Arg2), ToHexString(Arg0), ToHexString(Arg1))\n        }\n    }\n\n    Method (MAIN, 0, NotSerialized)\n    {\n        Local0 = ToInteger(123)\n        Local1 = 123\n        CHEK(Local0, Local1, __LINE__)\n\n        Local0 = ToInteger(\"123\")\n        Local1 = 123\n        CHEK(Local0, Local1, __LINE__)\n\n        Local0 = ToInteger(\"       \\t\\t\\t\\v       123\")\n        Local1 = 123\n        CHEK(Local0, Local1, __LINE__)\n\n        Local0 = ToInteger(\"123abcd\")\n        Local1 = 123\n        CHEK(Local0, Local1, __LINE__)\n\n        Local0 = ToInteger(\"0x123abcd\")\n        Local1 = 0x123abcd\n        CHEK(Local0, Local1, __LINE__)\n\n        Local0 = ToInteger(\"\")\n        Local1 = 0\n        CHEK(Local0, Local1, __LINE__)\n\n        Local0 = ToInteger(\"0X\")\n        Local1 = 0\n        CHEK(Local0, Local1, __LINE__)\n\n        Local0 = ToInteger(\"0x\")\n        Local1 = 0\n        CHEK(Local0, Local1, __LINE__)\n\n        Local0 = ToInteger(\"0\")\n        Local1 = 0\n        CHEK(Local0, Local1, __LINE__)\n\n        Local0 = ToInteger(\"0xDeAdBeeF\")\n        Local1 = 0xDEADBEEF\n        CHEK(Local0, Local1, __LINE__)\n\n        Local0 = ToInteger(\"0XDeAdBeeFCafeBabeHelloWorld\")\n        Local1 = 0xDEADBEEFCAFEBABE\n        CHEK(Local0, Local1, __LINE__)\n\n        Local0 = ToInteger(Buffer { 0xDE, 0xAD, 0xBE, 0xEF })\n        Local1 = 0xEFBEADDE\n        CHEK(Local0, Local1, __LINE__)\n\n        Local0 = ToInteger(Buffer { 1 })\n        Local1 = 1\n        CHEK(Local0, Local1, __LINE__)\n\n        Local0 = ToInteger(Buffer { 0 })\n        Local1 = 0\n        CHEK(Local0, Local1, __LINE__)\n\n        Local0 = ToInteger(Buffer { 0xDE, 0xAD, 0xBE, 0xEF, 0xCA, 0xFE, 0xBA, 0xBE })\n        Local1 = 0xBEBAFECAEFBEADDE\n        CHEK(Local0, Local1, __LINE__)\n\n        // These are incompatible with ACPICA, skip if it's detected\n        // TODO: these currently fail on our interpreter too. Can fix later.\n        /*If (ToHexString(0xF) == \"0xF\") {\n            Local0 = ToInteger(\"99999999999999999999999999999999999999999999999\")\n            Local1 = 0xFFFFFFFFFFFFFFFF\n            CHEK(Local0, Local1, __LINE__)\n\n            Local0 = ToInteger(\"0xDEADBEEFCAFEBABE1\")\n            Local1 = 0xFFFFFFFFFFFFFFFF\n            CHEK(Local0, Local1, __LINE__)\n\n            Local0 = ToInteger(\"+123\")\n            Local1 = 123\n            CHEK(Local0, Local1, __LINE__)\n\n            Local0 = ToInteger(\"-123\")\n            Local1 = 0xFFFFFFFFFFFFFF85\n            CHEK(Local0, Local1, __LINE__)\n\n            Local0 = ToInteger(\"-0xDEADBEF HELLOWORLD\")\n            Local1 = 0xFFFFFFFFF2152411\n            CHEK(Local0, Local1, __LINE__)\n\n            Local0 = ToInteger(\"+0XC0D\\t123\")\n            Local1 = 0xC0D\n            CHEK(Local0, Local1, __LINE__)\n\n            Local0 = ToInteger(\"0123\")\n            Local1 = 83\n            CHEK(Local0, Local1, __LINE__)\n        }*/\n\n        Return (FCNT)\n    }\n}\n"
  },
  {
    "path": "tests/to_x.asl",
    "content": "DefinitionBlock (\"\", \"SSDT\", 2, \"uTEST\", \"TESTTABL\", 0xF0F0F0F0)\n{\n    Name(FCNT, 0)\n\n    Method (CHEK, 2)\n    {\n        If (ObjectType(Arg0) == 3) {\n            Arg0 = ToHexString(Arg0)\n        }\n\n        If (ObjectType(Arg1) == 3) {\n            Arg1 = ToHexString(Arg1)\n        }\n\n        If (Arg0 != Arg1) {\n            FCNT++\n            Printf(\"Invalid string %o, expected %o\", Arg0, Arg1)\n        }\n    }\n\n    Method (MAIN, 0, NotSerialized)\n    {\n        // Dec string\n        Local0 = ToDecimalString(123)\n        Local1 = \"123\"\n        CHEK(Local0, Local1)\n\n        Local0 = ToDecimalString(Buffer { 1, 2, 222, 33, 45, 192, 3, 255 })\n        Local1 = \"1,2,222,33,45,192,3,255\"\n        CHEK(Local0, Local1)\n\n        Local0 = ToDecimalString(\"\")\n        Local1 = \"\"\n        CHEK(Local0, Local1)\n\n        Local0 = ToDecimalString(\"123\")\n        Local1 = \"123\"\n        CHEK(Local0, Local1)\n\n        Local0 = ToDecimalString(0xFFFFFFFFFFFFFFFF)\n        Local1 = \"18446744073709551615\"\n        CHEK(Local0, Local1)\n\n        // Hex string\n        Local0 = ToHexString(123)\n        Local1 = \"0x7B\"\n        CHEK(Local0, Local1)\n\n        Local0 = ToHexString(Buffer { 1, 2, 222, 33, 45, 192, 3, 255 })\n        Local1 = \"0x01,0x02,0xDE,0x21,0x2D,0xC0,0x03,0xFF\"\n        CHEK(Local0, Local1)\n\n        Local0 = ToHexString(\"\")\n        Local1 = \"\"\n        CHEK(Local0, Local1)\n\n        Local0 = ToHexString(\"123\")\n        Local1 = \"123\"\n        CHEK(Local0, Local1)\n\n        Local0 = ToHexString(0xF)\n        Local1 = \"0xF\"\n        CHEK(Local0, Local1)\n\n        Local0 = ToHexString(0xFF)\n        Local1 = \"0xFF\"\n        CHEK(Local0, Local1)\n\n        Local0 = ToHexString(0xFFF)\n        Local1 = \"0xFFF\"\n        CHEK(Local0, Local1)\n\n        Local0 = ToHexString(0xFFFFF)\n        Local1 = \"0xFFFFF\"\n        CHEK(Local0, Local1)\n\n        Local0 = ToHexString(0xFFFFFFFFFFFFFFFF)\n        Local1 = \"0xFFFFFFFFFFFFFFFF\"\n        CHEK(Local0, Local1)\n\n        // Buffer\n        Local0 = ToBuffer(Buffer { 1, 2, 3 })\n        Local1 = Buffer { 1, 2, 3 }\n        CHEK(Local0, Local1)\n\n        Local0 = ToBuffer(\"Hello\")\n        Local1 = Buffer { 0x48, 0x65, 0x6C, 0x6C, 0x6F, 0x00 }\n        CHEK(Local0, Local1)\n\n        Local0 = ToBuffer(0xDEADBEEFCAFEBABE)\n        Local1 = Buffer { 0xBE, 0xBA, 0xFE, 0xCA, 0xEF, 0xBE, 0xAD, 0xDE }\n        CHEK(Local0, Local1)\n\n        Return (FCNT)\n    }\n}\n"
  },
  {
    "path": "tests/uacpi_examples.rs",
    "content": "//! These examples have been adapted from the Readme of the [uACPI](https://github.com/uACPI/uACPI)\n//! project at commit 1ca45f3.\n//!\n//! At present most of these tests probably won't work, but it'll help to guide us towards better\n//! compatibility.\n//!\n//! The comments demonstrate some of the differences between the NT \"real world\" interpreter and\n//! the ACPI reference standard.\n\nmod test_infra;\n\nuse aml_test_tools::{handlers::null_handler::NullHandler};\n\n#[test]\n#[ignore] // Fails with ObjectNotOfExpectedType { expected: Reference, got: Integer }\nfn expressions_with_package() {\n    const ASL: &str = r#\"\nDefinitionBlock(\"\", \"DSDT\", 1, \"RSACPI\", \"UACPI\", 1) {\n    Method (TEST) {\n        Local0 = 10\n        Local1 = Package { Local0 * 5 }\n        Return (DerefOf(Local1[0]))\n    }\n\n    // ACPICA: AE_SUPPORT, Expressions within package elements are not supported\n    // Windows, uACPI: Local0 = 50\n    Local0 = TEST()\n}\n    \"#;\n\n    let handler = NullHandler;\n    test_infra::run_aml_test(ASL, handler);\n}\n\n#[test]\nfn package_outside_of_control_method() {\n    const ASL: &str = r#\"\nDefinitionBlock(\"\", \"DSDT\", 1, \"RSACPI\", \"UACPI\", 1) {\n    // ACPICA: internal error\n    // Windows, uACPI: ok\n    Local0 = Package { 1 }\n\n    // I don't have a good way of testing this, but if it completes without errors it's probably OK.\n    Debug = Local0[0]\n}\"#;\n\n    let handler = NullHandler;\n    test_infra::run_aml_test(ASL, handler);\n}\n\n#[test]\n#[ignore] // Local0 is set to 123 - we follow the ACPICA way, not the Windows way.\nfn reference_rebind_semantics() {\n    const ASL: &str = r#\"\nDefinitionBlock(\"\", \"DSDT\", 1, \"RSACPI\", \"UACPI\", 1) {\n    Method (MAIN, 0, NotSerialized) {\n        Local0 = 123\n        Local1 = RefOf(Local0)\n\n        // ACPICA: Local1 = 321, Local0 = 123\n        // Windows, uACPI: Local1 = reference->Local0, Local0 = 321\n        Local1 = 321\n\n        Return (Local0 == 123)\n    }\n}\"#;\n\n    let handler = NullHandler;\n    test_infra::run_aml_test(ASL, handler);\n}\n\n#[test]\n#[ignore] // ParseFail(ObjectNotOfExpectedType { expected: Integer, got: Integer } (a referencing failure)\nfn increment_decrement() {\n    const ASL: &str = r#\"\nDefinitionBlock(\"\", \"DSDT\", 1, \"RSACPI\", \"UACPI\", 1) {\n    Local0 = 123\n    Local1 = RefOf(Local0)\n\n    // ACPICA: error\n    // Windows, uACPI: Local0 = 124\n    Local1++\n}\"#;\n\n    let handler = NullHandler;\n    test_infra::run_aml_test(ASL, handler);\n}\n\n#[test]\n#[ignore] // ParseFail(ObjectNotOfExpectedType { expected: Reference, got: Integer })\nfn multilevel_references() {\n    const ASL: &str = r#\"\nDefinitionBlock(\"\", \"DSDT\", 1, \"RSACPI\", \"UACPI\", 1) {\n    Local0 = 123\n    Local1 = RefOf(Local0)\n    Local2 = RefOf(Local1)\n\n    // ACPICA: Local3 = reference->Local0\n    // Windows, uACPI: Local3 = 123\n    Local3 = DerefOf(Local2)\n}\"#;\n\n    let handler = NullHandler;\n    test_infra::run_aml_test(ASL, handler);\n}\n\n#[test]\n#[ignore] // \"Stores to objects like WrappedObject(UnsafeCell { .. }) are not yet supported\"\nfn implicit_case_semantics() {\n    const ASL: &str = r#\"\nDefinitionBlock(\"\", \"DSDT\", 1, \"RSACPI\", \"UACPI\", 1) {\n    Name (TEST, \"BAR\")\n\n    // ACPICA: TEST = \"00000000004F4F46\"\n    // Windows, uACPI: TEST = \"FOO\"\n    TEST = 0x4F4F46\n}\"#;\n\n    let handler = NullHandler;\n    test_infra::run_aml_test(ASL, handler);\n}\n\n#[test]\n#[ignore] // \"Stores to objects like WrappedObject(UnsafeCell { .. }) are not yet supported\"\nfn buffer_size_mutability() {\n    const ASL: &str = r#\"\nDefinitionBlock(\"\", \"DSDT\", 1, \"RSACPI\", \"UACPI\", 1) {\n    Name (TEST, \"XXXX\")\n    Name (VAL, \"\")\n\n    // ACPICA: TEST = \"LONGSTRING\"\n    // Windows, UACPI: TEST = \"LONG\"\n    TEST = \"LONGSTRING\"\n\n    // ACPICA: VAL = \"FOO\"\n    // Windows, UACPI: VAL = \"\"\n    VAL = \"FOO\"\n}\"#;\n\n    let handler = NullHandler;\n    test_infra::run_aml_test(ASL, handler);\n}\n\n#[test]\nfn ref_to_local() {\n    const ASL: &str = r#\"\nDefinitionBlock(\"\", \"DSDT\", 1, \"RSACPI\", \"UACPI\", 1) {\n    Method (TEST) {\n        Local0 = 123\n\n        // Use-after-free in ACPICA, perfectly fine in uACPI\n        Return (RefOf(Local0))\n    }\n\n    Method (FOO) {\n        Name (TEST, 123)\n\n        // Use-after-free in ACPICA, object lifetime prolonged in uACPI (node is still removed from the namespace)\n        Return (RefOf(TEST))\n    }\n\n    Method (MAIN) {\n        FOO ()\n        Return (0)\n    }\n}\"#;\n\n    let handler = NullHandler;\n    test_infra::run_aml_test(ASL, handler);\n}\n\n#[test]\n#[ignore] // CopyObject not yet implemented\nfn copy_object_to_self() {\n    const ASL: &str = r#\"\nDefinitionBlock(\"\", \"DSDT\", 1, \"RSACPI\", \"UACPI\", 1) {\n    Method (TEST) {\n        CopyObject(123, TEST)\n        Return (1)\n    }\n\n    // Segfault in ACPICA, prints 1 in uACPI\n    Debug = TEST()\n\n    // Unreachable in ACPICA, prints 123 in uACPI\n    Debug = TEST\n}\"#;\n\n    let handler = NullHandler;\n    test_infra::run_aml_test(ASL, handler);\n}\n"
  },
  {
    "path": "tests/while.asl",
    "content": "DefinitionBlock(\"while.aml\", \"DSDT\", 1, \"RSACPI\", \"WHILE\", 1) {\n    Name(X, 0)\n    While (X < 5) {\n        X++\n    }\n\n    // Test `DefBreak` - Y should only make it to 5\n    Name(Y, 0)\n    While (Y < 10) {\n        If (Y >= 5) {\n            Break\n        }\n\n        Y++\n    }\n\n    // Test `DefContinue` - Z should remain at zero\n    Name(CNT, 0)\n    Name(Z, 0)\n    While (CNT < 5) {\n        CNT++\n        Continue\n        Z++\n    }\n\n    // Test `Decrement` in the predicate - common pattern\n    Local0 = 5\n    While (Local0--) {\n        Continue\n    }\n}\n"
  },
  {
    "path": "tools/acpi_dumper/Cargo.toml",
    "content": "[package]\nname = \"acpi_dumper\"\nversion = \"0.0.0\"\npublish = false\nauthors = [\"Isaac Woods\", \"Matt Taylor\"]\nrepository = \"https://github.com/rust-osdev/acpi\"\ndescription = \"ACPI table dumper\"\ncategories = [\"hardware-support\"]\nreadme = \"../README.md\"\nlicense = \"MIT/Apache-2.0\"\nedition = \"2018\"\n\n[dependencies]\npico-args = \"0.2.0\"\nlibc = \"0.2.61\"\n"
  },
  {
    "path": "tools/acpi_dumper/src/main.rs",
    "content": "#[cfg(target_os = \"linux\")]\nfn main() {\n    use pico_args::Arguments;\n    use std::{\n        fs::{self, Permissions},\n        os::unix::fs::PermissionsExt,\n        path::PathBuf,\n        process::{self, Command},\n    };\n\n    // Find output folder from command line arguments\n    let mut args = Arguments::from_env();\n    let name: String = match args.value_from_str(\"--name\") {\n        Ok(Some(name)) => {\n            println!(\"output directory is: dumps/{}\", name);\n            name\n        }\n        _ => {\n            eprintln!(\n                \"usage: acpi-dumper --name [NAME]\\n\\\n                 the acpi tables will be dumped to the dumps/[name] directory\"\n            );\n            process::exit(-1);\n        }\n    };\n\n    // Check lshw is installed\n    match Command::new(\"lshw\").arg(\"-version\").output() {\n        Ok(output) => {\n            println!(\n                \"found lshw version: {}\",\n                String::from_utf8(output.stderr)\n                    .expect(\"lshw returned invalid utf-8\")\n                    .rsplit_terminator(' ')\n                    .next()\n                    .unwrap_or(\"unknown\")\n                    .trim_end()\n            );\n        }\n        Err(e) => {\n            eprintln!(\"error: lshw failed to execute ({}) - please check that it is installed\", e);\n            process::exit(-1);\n        }\n    };\n\n    if unsafe { libc::geteuid() } != 0 {\n        eprintln!(\"error: please re-run with root privileges\");\n        process::exit(-1);\n    }\n\n    // Create output folder\n    let mut out_path = PathBuf::from(\"./dumps\");\n    out_path.push(name);\n    fs::create_dir(&out_path).expect(\"failed to create output directory\");\n    fs::set_permissions(&out_path, Permissions::from_mode(0o777))\n        .expect(\"failed to set permissions for output directory\");\n\n    println!(\"dumping acpi tables...\");\n\n    // Loop over the ACPI tables, dump their contents\n    for file in fs::read_dir(\"/sys/firmware/acpi/tables\")\n        .expect(\"failed to access acpi tables, check permissions\")\n        .filter_map(|entry| entry.ok()) // Ignore failures\n        .filter(|entry| entry.metadata().unwrap().is_file())\n    {\n        let table_signature = file.file_name().into_string().expect(\"invalid utf-8 file name in acpi tables\");\n\n        // All tables have a 4 letter signature (apart from SSDTs)\n        if table_signature.len() == 4 || &table_signature[..4] != \"SSDT\" {\n            // Read table\n            let data = fs::read(file.path()).expect(\"failed to read acpi table\");\n            out_path.push(table_signature + \".bin\");\n\n            // Dump table to disk\n            fs::write(&out_path, data).expect(\"failed to write acpi table\");\n\n            // Allow anyone to read or write the file\n            fs::set_permissions(&out_path, Permissions::from_mode(0o666))\n                .expect(\"failed to set permissions of dumped acpi table\");\n            out_path.pop();\n        }\n    }\n\n    println!(\"done!\");\n\n    println!(\"capturing lshw output...\");\n    let lshw_info = Command::new(\"lshw\").output().expect(\"failed to get hardware information from lshw\");\n\n    // Write lshw output to disk\n    let lshw_info = String::from_utf8(lshw_info.stdout).expect(\"lshw returned invalid utf-8\");\n    out_path.push(\"lshw.txt\");\n    fs::write(&out_path, lshw_info).expect(\"failed to write lshw dump\");\n    fs::set_permissions(&out_path, Permissions::from_mode(0o666))\n        .expect(\"failed to set permissions of dumped acpi table\");\n    out_path.pop();\n    println!(\"done!\");\n}\n\n#[cfg(not(target_os = \"linux\"))]\nfn main() {\n    std::compile_error!(\"acpi-dumper currently only supports linux\");\n}\n"
  },
  {
    "path": "tools/aml_test_tools/Cargo.toml",
    "content": "[package]\nname = \"aml_test_tools\"\nversion = \"0.1.0\"\nauthors = [\"Isaac Woods\", \"Martin Hughes\"]\nedition = \"2024\"\npublish = false\n\n[dependencies]\nacpi = { path = \"../..\" }\nlog = \"0.4\"\npci_types = \"0.10.0\"\ntempfile = \"3.26.0\"\n"
  },
  {
    "path": "tools/aml_test_tools/src/handlers/check_cmd_handler.rs",
    "content": "//! A wrapper around another [`Handler`] that checks for the correct sequence of commands in a test.\n\nuse acpi::{Handle, Handler, PhysicalMapping, aml::AmlError};\nuse pci_types::PciAddress;\nuse std::{\n    mem::ManuallyDrop,\n    sync::{\n        Arc,\n        atomic::{AtomicUsize, Ordering::Relaxed},\n    },\n};\n\n/// The commands that may be received by an ACPI [`handler`](Handler).\n///\n/// They are written as an enum to allow a list of commands to be stored in a [`Vec`] or similar.\n/// A `Vec` of these is used by [`CheckCommandHandler`]\n#[derive(Clone, Copy, Debug, PartialEq)]\npub enum AcpiCommands {\n    MapPhysicalRegion(usize, usize),\n    UnmapPhysicalRegion(usize),\n    ReadU8(usize),\n    ReadU16(usize),\n    ReadU32(usize),\n    ReadU64(usize),\n    WriteU8(usize, u8),\n    WriteU16(usize, u16),\n    WriteU32(usize, u32),\n    WriteU64(usize, u64),\n    ReadIoU8(u16),\n    ReadIoU16(u16),\n    ReadIoU32(u16),\n    WriteIoU8(u16, u8),\n    WriteIoU16(u16, u16),\n    WriteIoU32(u16, u32),\n    ReadPciU8(PciAddress, u16),\n    ReadPciU16(PciAddress, u16),\n    ReadPciU32(PciAddress, u16),\n    WritePciU8(PciAddress, u16, u8),\n    WritePciU16(PciAddress, u16, u16),\n    WritePciU32(PciAddress, u16, u32),\n    NanosSinceBoot,\n    Stall(u64),\n    Sleep(u64),\n    CreateMutex,\n    Acquire(Handle, u16),\n    Release(Handle),\n}\n\n/// A wrapper around another [`Handler`] that checks the correct sequence of commands are being\n/// generated, and that they have the expected parameters.\n#[derive(Clone, Debug)]\npub struct CheckCommandHandler<H>\nwhere\n    H: Handler + Clone,\n{\n    commands: Vec<AcpiCommands>,\n    next_command_idx: Arc<AtomicUsize>,\n    next_handler: H,\n}\n\nimpl<H> CheckCommandHandler<H>\nwhere\n    H: Handler + Clone,\n{\n    pub fn new(commands: Vec<AcpiCommands>, next_handler: H) -> Self {\n        Self { commands, next_command_idx: Arc::new(AtomicUsize::new(0)), next_handler }\n    }\n\n    fn check_command(&self, command: AcpiCommands) {\n        let next_command_idx = self.next_command_idx.fetch_add(1, Relaxed);\n        let next_command = self.commands.get(next_command_idx);\n\n        if next_command.is_none() {\n            panic!(\"More commands attempted than expected\");\n        };\n\n        assert_eq!(*next_command.unwrap(), command);\n    }\n}\n\nimpl<H> Drop for CheckCommandHandler<H>\nwhere\n    H: Handler + Clone,\n{\n    fn drop(&mut self) {\n        // Don't do this if the test has already failed, to avoid a double-panic.\n        if !std::thread::panicking() {\n            assert_eq!(\n                self.next_command_idx.load(std::sync::atomic::Ordering::Relaxed),\n                self.commands.len(),\n                \"Not all commands were executed\"\n            );\n        }\n    }\n}\n\nimpl<H> Handler for CheckCommandHandler<H>\nwhere\n    H: Handler + Clone,\n{\n    unsafe fn map_physical_region<T>(&self, physical_address: usize, size: usize) -> PhysicalMapping<Self, T> {\n        self.check_command(AcpiCommands::MapPhysicalRegion(physical_address, size));\n\n        let inner_mapping = unsafe { self.next_handler.map_physical_region::<T>(physical_address, size) };\n        let inner_mapping = ManuallyDrop::new(inner_mapping);\n\n        PhysicalMapping {\n            physical_start: inner_mapping.physical_start,\n            virtual_start: inner_mapping.virtual_start,\n            region_length: inner_mapping.region_length,\n            mapped_length: inner_mapping.mapped_length,\n            handler: self.clone(),\n        }\n    }\n\n    fn unmap_physical_region<T>(region: &PhysicalMapping<Self, T>) {\n        // This function can be called during a panic, and it's pretty unlikely this command will\n        // be in the expected commands list...\n        //\n        // Also stop checking if we're at or past the end of the command list. This stops any\n        // confusion about whether we're in Drop or not.\n        if !std::thread::panicking()\n            && region.handler.commands.len() > region.handler.next_command_idx.load(Relaxed)\n        {\n            region.handler.check_command(AcpiCommands::UnmapPhysicalRegion(region.physical_start));\n        }\n\n        // Convert `PhysicalMapping<LoggingHandler<H>, T>` -> `PhysicalMapping<H, T>` and delegate.\n        // Prevent the temporary mapping from being dropped (and thus calling `H::unmap_physical_region` twice).\n        let inner_region = ManuallyDrop::new(PhysicalMapping::<H, T> {\n            physical_start: region.physical_start,\n            virtual_start: region.virtual_start,\n            region_length: region.region_length,\n            mapped_length: region.mapped_length,\n            handler: region.handler.next_handler.clone(),\n        });\n\n        H::unmap_physical_region(&inner_region);\n    }\n\n    fn read_u8(&self, address: usize) -> u8 {\n        self.check_command(AcpiCommands::ReadU8(address));\n        self.next_handler.read_u8(address)\n    }\n\n    fn read_u16(&self, address: usize) -> u16 {\n        self.check_command(AcpiCommands::ReadU16(address));\n        self.next_handler.read_u16(address)\n    }\n\n    fn read_u32(&self, address: usize) -> u32 {\n        self.check_command(AcpiCommands::ReadU32(address));\n        self.next_handler.read_u32(address)\n    }\n\n    fn read_u64(&self, address: usize) -> u64 {\n        self.check_command(AcpiCommands::ReadU64(address));\n        self.next_handler.read_u64(address)\n    }\n\n    fn write_u8(&self, address: usize, value: u8) {\n        self.check_command(AcpiCommands::WriteU8(address, value));\n        self.next_handler.write_u8(address, value);\n    }\n\n    fn write_u16(&self, address: usize, value: u16) {\n        self.check_command(AcpiCommands::WriteU16(address, value));\n        self.next_handler.write_u16(address, value);\n    }\n\n    fn write_u32(&self, address: usize, value: u32) {\n        self.check_command(AcpiCommands::WriteU32(address, value));\n        self.next_handler.write_u32(address, value);\n    }\n\n    fn write_u64(&self, address: usize, value: u64) {\n        self.check_command(AcpiCommands::WriteU64(address, value));\n        self.next_handler.write_u64(address, value);\n    }\n\n    fn read_io_u8(&self, port: u16) -> u8 {\n        self.check_command(AcpiCommands::ReadIoU8(port));\n        self.next_handler.read_io_u8(port)\n    }\n\n    fn read_io_u16(&self, port: u16) -> u16 {\n        self.check_command(AcpiCommands::ReadIoU16(port));\n        self.next_handler.read_io_u16(port)\n    }\n\n    fn read_io_u32(&self, port: u16) -> u32 {\n        self.check_command(AcpiCommands::ReadIoU32(port));\n        self.next_handler.read_io_u32(port)\n    }\n\n    fn write_io_u8(&self, port: u16, value: u8) {\n        self.check_command(AcpiCommands::WriteIoU8(port, value));\n        self.next_handler.write_io_u8(port, value);\n    }\n\n    fn write_io_u16(&self, port: u16, value: u16) {\n        self.check_command(AcpiCommands::WriteIoU16(port, value));\n        self.next_handler.write_io_u16(port, value);\n    }\n\n    fn write_io_u32(&self, port: u16, value: u32) {\n        self.check_command(AcpiCommands::WriteIoU32(port, value));\n        self.next_handler.write_io_u32(port, value);\n    }\n\n    fn read_pci_u8(&self, address: PciAddress, offset: u16) -> u8 {\n        self.check_command(AcpiCommands::ReadPciU8(address, offset));\n        self.next_handler.read_pci_u8(address, offset)\n    }\n\n    fn read_pci_u16(&self, address: PciAddress, offset: u16) -> u16 {\n        self.check_command(AcpiCommands::ReadPciU16(address, offset));\n        self.next_handler.read_pci_u16(address, offset)\n    }\n\n    fn read_pci_u32(&self, address: PciAddress, offset: u16) -> u32 {\n        self.check_command(AcpiCommands::ReadPciU32(address, offset));\n        self.next_handler.read_pci_u32(address, offset)\n    }\n\n    fn write_pci_u8(&self, address: PciAddress, offset: u16, value: u8) {\n        self.check_command(AcpiCommands::WritePciU8(address, offset, value));\n        self.next_handler.write_pci_u8(address, offset, value);\n    }\n\n    fn write_pci_u16(&self, address: PciAddress, offset: u16, value: u16) {\n        self.check_command(AcpiCommands::WritePciU16(address, offset, value));\n        self.next_handler.write_pci_u16(address, offset, value);\n    }\n\n    fn write_pci_u32(&self, address: PciAddress, offset: u16, value: u32) {\n        self.check_command(AcpiCommands::WritePciU32(address, offset, value));\n        self.next_handler.write_pci_u32(address, offset, value);\n    }\n\n    fn nanos_since_boot(&self) -> u64 {\n        self.check_command(AcpiCommands::NanosSinceBoot);\n        self.next_handler.nanos_since_boot()\n    }\n\n    fn stall(&self, microseconds: u64) {\n        self.check_command(AcpiCommands::Stall(microseconds));\n        self.next_handler.stall(microseconds);\n    }\n\n    fn sleep(&self, milliseconds: u64) {\n        self.check_command(AcpiCommands::Sleep(milliseconds));\n        self.next_handler.sleep(milliseconds);\n    }\n\n    fn create_mutex(&self) -> Handle {\n        self.check_command(AcpiCommands::CreateMutex);\n        self.next_handler.create_mutex()\n    }\n\n    fn acquire(&self, mutex: Handle, timeout: u16) -> Result<(), AmlError> {\n        self.check_command(AcpiCommands::Acquire(mutex, timeout));\n        self.next_handler.acquire(mutex, timeout)\n    }\n\n    fn release(&self, mutex: Handle) {\n        self.check_command(AcpiCommands::Release(mutex));\n        self.next_handler.release(mutex);\n    }\n}\n\n#[cfg(test)]\nmod test {\n    use crate::handlers::null_handler::NullHandler;\n    use super::*;\n\n    #[test]\n    fn handler_basic_functions() {\n        let test_commands = vec![AcpiCommands::ReadIoU8(2), AcpiCommands::WriteIoU16(3, 4)];\n\n        let handler = CheckCommandHandler::new(test_commands, NullHandler {});\n        handler.read_io_u8(2);\n        handler.write_io_u16(3, 4);\n    }\n\n    #[test]\n    #[should_panic]\n    fn handler_fails_for_wrong_command() {\n        let test_commands = vec![AcpiCommands::ReadIoU8(2), AcpiCommands::WriteIoU16(3, 4)];\n\n        let handler = CheckCommandHandler::new(test_commands, NullHandler {});\n        handler.read_io_u8(3);\n        // We shouldn't actually make it to this command, but it makes sure the handler doesn't panic for having too few\n        // commands sent to it.\n        handler.write_io_u16(3, 4);\n    }\n\n    #[test]\n    #[should_panic]\n    fn handler_fails_for_too_many_commands() {\n        let test_commands = vec![AcpiCommands::ReadIoU8(2), AcpiCommands::WriteIoU16(3, 4)];\n\n        let handler = CheckCommandHandler::new(test_commands, NullHandler {});\n        handler.read_io_u8(2);\n        handler.write_io_u16(3, 4);\n        handler.read_io_u8(5);\n    }\n\n    #[test]\n    #[should_panic]\n    fn handler_fails_for_too_few_commands() {\n        let test_commands = vec![AcpiCommands::ReadIoU8(2), AcpiCommands::WriteIoU16(3, 4)];\n\n        let handler = CheckCommandHandler::new(test_commands, NullHandler {});\n        handler.read_io_u8(2);\n    }\n\n    #[test]\n    #[should_panic]\n    fn check_handler_fails_gracefully_for_no_commands() {\n        let test_commands: Vec<AcpiCommands> = vec![];\n        let handler = CheckCommandHandler::new(test_commands, NullHandler {});\n        handler.read_io_u8(2);\n    }\n}"
  },
  {
    "path": "tools/aml_test_tools/src/handlers/listed_response_handler.rs",
    "content": "//! A basic [`Handler`] that returns an expected result from a provided sequence of commands.\n\nuse acpi::{Handle, Handler, PhysicalMapping, aml::AmlError};\nuse pci_types::PciAddress;\nuse std::sync::{Arc, atomic::AtomicUsize};\n\n/// Commands that may be received by a [handler](Handler) which return a value from the handler.\n///\n/// Commands that do not return a value are represented by [`Skip`](AcpiCommands::Skip).\n///\n/// A [`Vec`] of these is used by [`ListedResponseHandler`]\n// Some variants are unused for the time being, until more tests are written.\n#[allow(unused)]\n#[derive(Clone, Copy, Debug, PartialEq)]\npub enum AcpiCommands {\n    /// A stand-in for all commands that don't return a value.\n    Skip(),\n    ReadU8(u8),\n    ReadU16(u16),\n    ReadU32(u32),\n    ReadU64(u64),\n    ReadIoU8(u8),\n    ReadIoU16(u16),\n    ReadIoU32(u32),\n    ReadPciU8(u8),\n    ReadPciU16(u16),\n    ReadPciU32(u32),\n    NanosSinceBoot(u64),\n}\n\n/// A basic [`Handler`] that returns an expected result from a provided sequence of commands.\n///\n/// If the command is unexpected, this handler will panic.\n#[derive(Clone, Debug)]\npub struct ListedResponseHandler {\n    commands: Vec<AcpiCommands>,\n    next_command_idx: Arc<AtomicUsize>,\n}\n\nimpl ListedResponseHandler {\n    pub fn new(commands: Vec<AcpiCommands>) -> Self {\n        Self { commands, next_command_idx: Arc::new(AtomicUsize::new(0)) }\n    }\n\n    fn get_next_command(&self) -> AcpiCommands {\n        let next_command_idx = self.next_command_idx.fetch_add(1, std::sync::atomic::Ordering::Relaxed);\n        let next_command = self.commands.get(next_command_idx);\n\n        if next_command.is_none() {\n            panic!(\"More commands attempted than expected\");\n        };\n\n        *next_command.unwrap()\n    }\n}\n\nmacro_rules! check_and_get_result {\n    ($self:ident, $expected_cmd:ident) => {\n        match $self.get_next_command() {\n            AcpiCommands::$expected_cmd(x) => x,\n            _ => panic!(\"Unexpected command\"),\n        }\n    };\n}\n\nmacro_rules! check_is_skipped {\n    ($self:ident) => {\n        match $self.get_next_command() {\n            AcpiCommands::Skip() => (),\n            _ => panic!(\"Unexpected command\"),\n        }\n    };\n}\nimpl Drop for ListedResponseHandler {\n    fn drop(&mut self) {\n        // Don't do this if the test has already failed, to avoid a double-panic.\n        if !std::thread::panicking() {\n            assert_eq!(\n                self.next_command_idx.load(std::sync::atomic::Ordering::Relaxed),\n                self.commands.len(),\n                \"Not all commands were executed\"\n            );\n        }\n    }\n}\n\nimpl Handler for ListedResponseHandler {\n    unsafe fn map_physical_region<T>(&self, _physical_address: usize, _size: usize) -> PhysicalMapping<Self, T> {\n        // This isn't implemented in `aml_tester` either\n        todo!()\n    }\n\n    fn unmap_physical_region<T>(_region: &PhysicalMapping<Self, T>) {}\n\n    fn read_u8(&self, _address: usize) -> u8 {\n        check_and_get_result!(self, ReadU8)\n    }\n\n    fn read_u16(&self, _address: usize) -> u16 {\n        check_and_get_result!(self, ReadU16)\n    }\n\n    fn read_u32(&self, _address: usize) -> u32 {\n        check_and_get_result!(self, ReadU32)\n    }\n\n    fn read_u64(&self, _address: usize) -> u64 {\n        check_and_get_result!(self, ReadU64)\n    }\n\n    fn write_u8(&self, _address: usize, _value: u8) {\n        check_is_skipped!(self)\n    }\n\n    fn write_u16(&self, _address: usize, _value: u16) {\n        check_is_skipped!(self)\n    }\n\n    fn write_u32(&self, _address: usize, _value: u32) {\n        check_is_skipped!(self)\n    }\n\n    fn write_u64(&self, _address: usize, _value: u64) {\n        check_is_skipped!(self)\n    }\n\n    fn read_io_u8(&self, _port: u16) -> u8 {\n        check_and_get_result!(self, ReadIoU8)\n    }\n\n    fn read_io_u16(&self, _port: u16) -> u16 {\n        check_and_get_result!(self, ReadIoU16)\n    }\n\n    fn read_io_u32(&self, _port: u16) -> u32 {\n        check_and_get_result!(self, ReadIoU32)\n    }\n\n    fn write_io_u8(&self, _port: u16, _value: u8) {\n        check_is_skipped!(self)\n    }\n\n    fn write_io_u16(&self, _port: u16, _value: u16) {\n        check_is_skipped!(self)\n    }\n\n    fn write_io_u32(&self, _port: u16, _value: u32) {\n        check_is_skipped!(self)\n    }\n\n    fn read_pci_u8(&self, _address: PciAddress, _offset: u16) -> u8 {\n        check_and_get_result!(self, ReadPciU8)\n    }\n\n    fn read_pci_u16(&self, _address: PciAddress, _offset: u16) -> u16 {\n        check_and_get_result!(self, ReadPciU16)\n    }\n\n    fn read_pci_u32(&self, _address: PciAddress, _offset: u16) -> u32 {\n        check_and_get_result!(self, ReadPciU32)\n    }\n\n    fn write_pci_u8(&self, _address: PciAddress, _offset: u16, _value: u8) {\n        check_is_skipped!(self)\n    }\n\n    fn write_pci_u16(&self, _address: PciAddress, _offset: u16, _value: u16) {\n        check_is_skipped!(self)\n    }\n\n    fn write_pci_u32(&self, _address: PciAddress, _offset: u16, _value: u32) {\n        check_is_skipped!(self)\n    }\n\n    fn nanos_since_boot(&self) -> u64 {\n        check_and_get_result!(self, NanosSinceBoot)\n    }\n\n    fn stall(&self, _microseconds: u64) {\n        check_is_skipped!(self)\n    }\n\n    fn sleep(&self, _milliseconds: u64) {\n        check_is_skipped!(self)\n    }\n\n    fn create_mutex(&self) -> Handle {\n        check_is_skipped!(self);\n        Handle(1)\n    }\n\n    fn acquire(&self, _mutex: Handle, _timeout: u16) -> Result<(), AmlError> {\n        check_is_skipped!(self);\n        Ok(())\n    }\n\n    fn release(&self, _mutex: Handle) {\n        check_is_skipped!(self)\n    }\n}\n\n#[cfg(test)]\nmod test {\n    use super::*;\n\n    #[test]\n    fn handler_basic_functions() {\n        let test_commands = vec![AcpiCommands::ReadIoU8(2), AcpiCommands::Skip()];\n\n        let handler = ListedResponseHandler::new(test_commands);\n        handler.read_io_u8(2);\n        handler.write_io_u16(3, 4);\n    }\n\n    #[test]\n    #[should_panic]\n    fn handler_fails_for_wrong_command() {\n        let test_commands = vec![AcpiCommands::ReadIoU8(2), AcpiCommands::ReadIoU8(3)];\n\n        let handler = ListedResponseHandler::new(test_commands);\n        handler.read_io_u8(3);\n        // We shouldn't actually make it to this command, but it makes sure the handler doesn't panic for having too few\n        // commands sent to it.\n        handler.write_io_u16(3, 4);\n    }\n\n    #[test]\n    #[should_panic]\n    fn handler_fails_for_too_many_commands() {\n        let test_commands = vec![AcpiCommands::ReadIoU8(2), AcpiCommands::Skip()];\n\n        let handler = ListedResponseHandler::new(test_commands);\n        handler.read_io_u8(2);\n        handler.write_io_u16(3, 4);\n        handler.read_io_u8(5);\n    }\n\n    #[test]\n    #[should_panic]\n    fn handler_fails_for_too_few_commands() {\n        let test_commands = vec![AcpiCommands::ReadIoU8(2), AcpiCommands::Skip()];\n\n        let handler = ListedResponseHandler::new(test_commands);\n        handler.read_io_u8(2);\n    }\n\n    #[test]\n    #[should_panic]\n    fn check_handler_fails_gracefully_for_no_commands() {\n        let test_commands: Vec<AcpiCommands> = vec![];\n        let handler = ListedResponseHandler::new(test_commands);\n        handler.read_io_u8(2);\n    }\n}\n"
  },
  {
    "path": "tools/aml_test_tools/src/handlers/logging_handler.rs",
    "content": "//! A [`Handler`] that logs all calls, then forwards them to an inner handler.\n\nuse acpi::{Handle, Handler, PhysicalMapping, aml::object::Object};\nuse core::mem::ManuallyDrop;\nuse log::info;\nuse pci_types::PciAddress;\n\n/// A [`Handler`] wrapper that logs every call to `info!` and then forwards it to an inner handler.\n///\n/// Use this Handler to have a logging style consistent with the [`acpi`] crate's tests.\n#[derive(Clone)]\npub struct LoggingHandler<H> {\n    next_handler: H,\n}\n\nimpl<H> LoggingHandler<H>\nwhere\n    H: Handler,\n{\n    pub fn new(next_handler: H) -> Self {\n        Self { next_handler }\n    }\n}\n\nimpl<H> Handler for LoggingHandler<H>\nwhere\n    H: Handler,\n{\n    unsafe fn map_physical_region<T>(&self, physical_address: usize, size: usize) -> PhysicalMapping<Self, T> {\n        info!(\"map_physical_region(physical_address={:#x}, size={:#x})\", physical_address, size);\n\n        let inner_mapping = unsafe { self.next_handler.map_physical_region::<T>(physical_address, size) };\n        let inner_mapping = ManuallyDrop::new(inner_mapping);\n\n        PhysicalMapping {\n            physical_start: inner_mapping.physical_start,\n            virtual_start: inner_mapping.virtual_start,\n            region_length: inner_mapping.region_length,\n            mapped_length: inner_mapping.mapped_length,\n            handler: self.clone(),\n        }\n    }\n\n    fn unmap_physical_region<T>(region: &PhysicalMapping<Self, T>) {\n        info!(\"unmap_physical_region(physical_start={:#x})\", region.physical_start);\n\n        // Convert `PhysicalMapping<LoggingHandler<H>, T>` -> `PhysicalMapping<H, T>` and delegate.\n        // Prevent the temporary mapping from being dropped (and thus calling `H::unmap_physical_region` twice).\n        let inner_region = ManuallyDrop::new(PhysicalMapping::<H, T> {\n            physical_start: region.physical_start,\n            virtual_start: region.virtual_start,\n            region_length: region.region_length,\n            mapped_length: region.mapped_length,\n            handler: region.handler.next_handler.clone(),\n        });\n\n        H::unmap_physical_region(&inner_region);\n    }\n\n    fn read_u8(&self, address: usize) -> u8 {\n        let value = self.next_handler.read_u8(address);\n        info!(\"read_u8(address={:#x}) -> {:#x}\", address, value);\n        value\n    }\n\n    fn read_u16(&self, address: usize) -> u16 {\n        let value = self.next_handler.read_u16(address);\n        info!(\"read_u16(address={:#x}) -> {:#x}\", address, value);\n        value\n    }\n\n    fn read_u32(&self, address: usize) -> u32 {\n        let value = self.next_handler.read_u32(address);\n        info!(\"read_u32(address={:#x}) -> {:#x}\", address, value);\n        value\n    }\n\n    fn read_u64(&self, address: usize) -> u64 {\n        let value = self.next_handler.read_u64(address);\n        info!(\"read_u64(address={:#x}) -> {:#x}\", address, value);\n        value\n    }\n\n    fn write_u8(&self, address: usize, value: u8) {\n        info!(\"write_u8(address={:#x}, value={:#x})\", address, value);\n        self.next_handler.write_u8(address, value);\n    }\n\n    fn write_u16(&self, address: usize, value: u16) {\n        info!(\"write_u16(address={:#x}, value={:#x})\", address, value);\n        self.next_handler.write_u16(address, value);\n    }\n\n    fn write_u32(&self, address: usize, value: u32) {\n        info!(\"write_u32(address={:#x}, value={:#x})\", address, value);\n        self.next_handler.write_u32(address, value);\n    }\n\n    fn write_u64(&self, address: usize, value: u64) {\n        info!(\"write_u64(address={:#x}, value={:#x})\", address, value);\n        self.next_handler.write_u64(address, value);\n    }\n\n    fn read_io_u8(&self, port: u16) -> u8 {\n        let value = self.next_handler.read_io_u8(port);\n        info!(\"read_io_u8(port={:#x}) -> {:#x}\", port, value);\n        value\n    }\n\n    fn read_io_u16(&self, port: u16) -> u16 {\n        let value = self.next_handler.read_io_u16(port);\n        info!(\"read_io_u16(port={:#x}) -> {:#x}\", port, value);\n        value\n    }\n\n    fn read_io_u32(&self, port: u16) -> u32 {\n        let value = self.next_handler.read_io_u32(port);\n        info!(\"read_io_u32(port={:#x}) -> {:#x}\", port, value);\n        value\n    }\n\n    fn write_io_u8(&self, port: u16, value: u8) {\n        info!(\"write_io_u8(port={:#x}, value={:#x})\", port, value);\n        self.next_handler.write_io_u8(port, value);\n    }\n\n    fn write_io_u16(&self, port: u16, value: u16) {\n        info!(\"write_io_u16(port={:#x}, value={:#x})\", port, value);\n        self.next_handler.write_io_u16(port, value);\n    }\n\n    fn write_io_u32(&self, port: u16, value: u32) {\n        info!(\"write_io_u32(port={:#x}, value={:#x})\", port, value);\n        self.next_handler.write_io_u32(port, value);\n    }\n\n    fn read_pci_u8(&self, address: PciAddress, offset: u16) -> u8 {\n        let value = self.next_handler.read_pci_u8(address, offset);\n        info!(\"read_pci_u8(address={:?}, offset={:#x}) -> {:#x}\", address, offset, value);\n        value\n    }\n\n    fn read_pci_u16(&self, address: PciAddress, offset: u16) -> u16 {\n        let value = self.next_handler.read_pci_u16(address, offset);\n        info!(\"read_pci_u16(address={:?}, offset={:#x}) -> {:#x}\", address, offset, value);\n        value\n    }\n\n    fn read_pci_u32(&self, address: PciAddress, offset: u16) -> u32 {\n        let value = self.next_handler.read_pci_u32(address, offset);\n        info!(\"read_pci_u32(address={:?}, offset={:#x}) -> {:#x}\", address, offset, value);\n        value\n    }\n\n    fn write_pci_u8(&self, address: PciAddress, offset: u16, value: u8) {\n        info!(\"write_pci_u8(address={:?}, offset={:#x}, value={:#x})\", address, offset, value);\n        self.next_handler.write_pci_u8(address, offset, value);\n    }\n\n    fn write_pci_u16(&self, address: PciAddress, offset: u16, value: u16) {\n        info!(\"write_pci_u16(address={:?}, offset={:#x}, value={:#x})\", address, offset, value);\n        self.next_handler.write_pci_u16(address, offset, value);\n    }\n\n    fn write_pci_u32(&self, address: PciAddress, offset: u16, value: u32) {\n        info!(\"write_pci_u32(address={:?}, offset={:#x}, value={:#x})\", address, offset, value);\n        self.next_handler.write_pci_u32(address, offset, value);\n    }\n\n    fn nanos_since_boot(&self) -> u64 {\n        info!(\"nanos_since_boot()\");\n        self.next_handler.nanos_since_boot()\n    }\n\n    fn stall(&self, microseconds: u64) {\n        info!(\"stall(microseconds={})\", microseconds);\n        self.next_handler.stall(microseconds);\n    }\n\n    fn sleep(&self, milliseconds: u64) {\n        info!(\"sleep(milliseconds={})\", milliseconds);\n        self.next_handler.sleep(milliseconds);\n    }\n\n    fn create_mutex(&self) -> Handle {\n        info!(\"create_mutex()\");\n        self.next_handler.create_mutex()\n    }\n\n    fn acquire(&self, mutex: Handle, timeout: u16) -> Result<(), acpi::aml::AmlError> {\n        info!(\"acquire(mutex={:?}, timeout={})\", mutex, timeout);\n        self.next_handler.acquire(mutex, timeout)\n    }\n\n    fn release(&self, mutex: Handle) {\n        info!(\"release(mutex={:?})\", mutex);\n        self.next_handler.release(mutex);\n    }\n\n    fn handle_debug(&self, object: &Object) {\n        info!(\"Debug store: {}\", object);\n        self.next_handler.handle_debug(object);\n    }\n}\n"
  },
  {
    "path": "tools/aml_test_tools/src/handlers/mod.rs",
    "content": "//! A collection of [`handlers`](acpi::Handler) that might be useful when testing AML.\n//!\n//! These are all used by the [`acpi`] integration tests, or by `aml_tester`.\n\npub mod check_cmd_handler;\npub mod listed_response_handler;\npub mod logging_handler;\npub mod null_handler;\npub mod std_test_handler;\n"
  },
  {
    "path": "tools/aml_test_tools/src/handlers/null_handler.rs",
    "content": "//! A [`Handler`] that does nothing useful.\n\nuse acpi::{Handle, Handler, PhysicalMapping, aml::AmlError};\nuse pci_types::PciAddress;\n\n#[derive(Clone)]\npub struct NullHandler;\n\n/// A [`Handler`] that does nothing. If required, it will generally return zero.\n///\n/// This is useful as a placeholder if you really don't care what values the core [`acpi`] parser\n/// receives.\nimpl Handler for NullHandler {\n    unsafe fn map_physical_region<T>(&self, _physical_address: usize, _size: usize) -> PhysicalMapping<Self, T> {\n        // This isn't implemented in `aml_tester` either\n        todo!()\n    }\n\n    fn unmap_physical_region<T>(_region: &PhysicalMapping<Self, T>) {}\n\n    fn read_u8(&self, _address: usize) -> u8 {\n        0\n    }\n\n    fn read_u16(&self, _address: usize) -> u16 {\n        0\n    }\n\n    fn read_u32(&self, _address: usize) -> u32 {\n        0\n    }\n\n    fn read_u64(&self, _address: usize) -> u64 {\n        0\n    }\n\n    fn write_u8(&self, _address: usize, _value: u8) {}\n\n    fn write_u16(&self, _address: usize, _value: u16) {}\n\n    fn write_u32(&self, _address: usize, _value: u32) {}\n\n    fn write_u64(&self, _address: usize, _value: u64) {}\n\n    fn read_io_u8(&self, _port: u16) -> u8 {\n        0\n    }\n\n    fn read_io_u16(&self, _port: u16) -> u16 {\n        0\n    }\n\n    fn read_io_u32(&self, _port: u16) -> u32 {\n        0\n    }\n\n    fn write_io_u8(&self, _port: u16, _value: u8) {}\n\n    fn write_io_u16(&self, _port: u16, _value: u16) {}\n\n    fn write_io_u32(&self, _port: u16, _value: u32) {}\n\n    fn read_pci_u8(&self, _address: PciAddress, _offset: u16) -> u8 {\n        0\n    }\n\n    fn read_pci_u16(&self, _address: PciAddress, _offset: u16) -> u16 {\n        0\n    }\n\n    fn read_pci_u32(&self, _address: PciAddress, _offset: u16) -> u32 {\n        0\n    }\n\n    fn write_pci_u8(&self, _address: PciAddress, _offset: u16, _value: u8) {}\n\n    fn write_pci_u16(&self, _address: PciAddress, _offset: u16, _value: u16) {}\n\n    fn write_pci_u32(&self, _address: PciAddress, _offset: u16, _value: u32) {}\n\n    fn nanos_since_boot(&self) -> u64 {\n        1000\n    }\n\n    fn stall(&self, _microseconds: u64) {}\n\n    fn sleep(&self, _milliseconds: u64) {}\n\n    fn create_mutex(&self) -> Handle {\n        Handle(0)\n    }\n\n    fn acquire(&self, _mutex: Handle, _timeout: u16) -> Result<(), AmlError> {\n        Ok(())\n    }\n\n    fn release(&self, _mutex: Handle) {}\n}\n"
  },
  {
    "path": "tools/aml_test_tools/src/handlers/std_test_handler.rs",
    "content": "//! Rather than defining a [`Handler`], this module defines useful functions to streamline the most-\n//! used case of a [`CheckCommandHandler`] wrapping a [`ListedResponseHandler`].\n\nuse pci_types::PciAddress;\nuse crate::handlers::{\n    check_cmd_handler::{AcpiCommands as Check, CheckCommandHandler},\n    listed_response_handler::{AcpiCommands as Response, ListedResponseHandler},\n};\nuse acpi::{Handle, Handler};\n\n/// Simplifies the construction of a standard test [`Handler`].\n///\n/// By combining the commands and responses into a single tuple, it hopefully makes test files\n/// easier to read and write.\npub type Command = (Check, Response);\n\n/// Construct a \"standard handler\".\n///\n/// This is the handler that I expect to be used most often in tests. (A [`CheckCommandHandler`]\n/// wrapping a [`ListedResponseHandler`]).\npub fn construct_std_handler(commands: Vec<Command>) -> impl Handler {\n    let (c, r): (Vec<Check>, Vec<Response>) = commands.into_iter().unzip();\n\n    CheckCommandHandler::new(c, ListedResponseHandler::new(r))\n}\n\n/// A simple helper to generate a [`Command`] for [`Handler::map_physical_region`].\npub const fn map_physical_region(physical_address: usize, size: usize) -> Command {\n    (Check::MapPhysicalRegion(physical_address, size), Response::Skip())\n}\n\n/// A simple helper to generate a [`Command`] for [`Handler::unmap_physical_region`].\npub const fn unmap_physical_region(region: usize) -> Command {\n    (Check::UnmapPhysicalRegion(region), Response::Skip())\n}\n\n/// A simple helper to generate a [`Command`] for [`Handler::read_u8`].\npub const fn read_u8(address: usize, response: u8) -> Command {\n    (Check::ReadU8(address), Response::ReadU8(response))\n}\n\n/// A simple helper to generate a [`Command`] for [`Handler::read_u16`].\npub const fn read_u16(address: usize, response: u16) -> Command {\n    (Check::ReadU16(address), Response::ReadU16(response))\n}\n\n/// A simple helper to generate a [`Command`] for [`Handler::read_u32`].\npub const fn read_u32(address: usize, response: u32) -> Command {\n    (Check::ReadU32(address), Response::ReadU32(response))\n}\n\n/// A simple helper to generate a [`Command`] for [`Handler::read_u64`].\npub const fn read_u64(address: usize, response: u64) -> Command {\n    (Check::ReadU64(address), Response::ReadU64(response))\n}\n\n/// A simple helper to generate a [`Command`] for [`Handler::write_u8`].\npub const fn write_u8(address: usize, value: u8) -> Command {\n    (Check::WriteU8(address, value), Response::Skip())\n}\n\n/// A simple helper to generate a [`Command`] for [`Handler::write_u16`].\npub const fn write_u16(address: usize, value: u16) -> Command {\n    (Check::WriteU16(address, value), Response::Skip())\n}\n\n/// A simple helper to generate a [`Command`] for [`Handler::write_u32`].\npub const fn write_u32(address: usize, value: u32) -> Command {\n    (Check::WriteU32(address, value), Response::Skip())\n}\n\n/// A simple helper to generate a [`Command`] for [`Handler::write_u64`].\npub const fn write_u64(address: usize, value: u64) -> Command {\n    (Check::WriteU64(address, value), Response::Skip())\n}\n\n/// A simple helper to generate a [`Command`] for [`Handler::read_io_u8`].\npub const fn read_io_u8(port: u16, response: u8) -> Command {\n    (Check::ReadIoU8(port), Response::ReadIoU8(response))\n}\n\n/// A simple helper to generate a [`Command`] for [`Handler::read_io_u16`].\npub const fn read_io_u16(port: u16, response: u16) -> Command {\n    (Check::ReadIoU16(port), Response::ReadIoU16(response))\n}\n\n/// A simple helper to generate a [`Command`] for [`Handler::read_io_u32`].\npub const fn read_io_u32(port: u16, response: u32) -> Command {\n    (Check::ReadIoU32(port), Response::ReadIoU32(response))\n}\n\n/// A simple helper to generate a [`Command`] for [`Handler::write_io_u8`].\npub const fn write_io_u8(port: u16, value: u8) -> Command {\n    (Check::WriteIoU8(port, value), Response::Skip())\n}\n\n/// A simple helper to generate a [`Command`] for [`Handler::write_io_u16`].\npub const fn write_io_u16(port: u16, value: u16) -> Command {\n    (Check::WriteIoU16(port, value), Response::Skip())\n}\n\n/// A simple helper to generate a [`Command`] for [`Handler::write_io_u32`].\npub const fn write_io_u32(port: u16, value: u32) -> Command {\n    (Check::WriteIoU32(port, value), Response::Skip())\n}\n\n/// A simple helper to generate a [`Command`] for [`Handler::read_pci_u8`].\npub const fn read_pci_u8(address: PciAddress, offset: u16, response: u8) -> Command {\n    (Check::ReadPciU8(address, offset), Response::ReadPciU8(response))\n}\n\n/// A simple helper to generate a [`Command`] for [`Handler::read_pci_u16`].\npub const fn read_pci_u16(address: PciAddress, offset: u16, response: u16) -> Command {\n    (Check::ReadPciU16(address, offset), Response::ReadPciU16(response))\n}\n\n/// A simple helper to generate a [`Command`] for [`Handler::read_pci_u32`].\npub const fn read_pci_u32(address: PciAddress, offset: u16, response: u32) -> Command {\n    (Check::ReadPciU32(address, offset), Response::ReadPciU32(response))\n}\n\n/// A simple helper to generate a [`Command`] for [`Handler::write_pci_u8`].\npub const fn write_pci_u8(address: PciAddress, offset: u16, value: u8) -> Command {\n    (Check::WritePciU8(address, offset, value), Response::Skip())\n}\n\n/// A simple helper to generate a [`Command`] for [`Handler::write_pci_u16`].\npub const fn write_pci_u16(address: PciAddress, offset: u16, value: u16) -> Command {\n    (Check::WritePciU16(address, offset, value), Response::Skip())\n}\n\n/// A simple helper to generate a [`Command`] for [`Handler::write_pci_u32`].\npub const fn write_pci_u32(address: PciAddress, offset: u16, value: u32) -> Command {\n    (Check::WritePciU32(address, offset, value), Response::Skip())\n}\n\n/// A simple helper to generate a [`Command`] for [`Handler::nanos_since_boot`].\npub const fn nanos_since_boot(response: u64) -> Command {\n    (Check::NanosSinceBoot, Response::NanosSinceBoot(response))\n}\n\n/// A simple helper to generate a [`Command`] for [`Handler::stall`].\npub const fn stall(microseconds: u64) -> Command {\n    (Check::Stall(microseconds), Response::Skip())\n}\n\n/// A simple helper to generate a [`Command`] for [`Handler::sleep`].\npub const fn sleep(milliseconds: u64) -> Command {\n    (Check::Sleep(milliseconds), Response::Skip())\n}\n\n/// A simple helper to generate a [`Command`] for [`Handler::create_mutex`].\npub const fn create_mutex() -> Command {\n    (Check::CreateMutex, Response::Skip())\n}\n\n/// A simple helper to generate a [`Command`] for [`Handler::acquire`].\npub const fn acquire(mutex: Handle, timeout: u16) -> Command {\n    (Check::Acquire(mutex, timeout), Response::Skip())\n}\n\n/// A simple helper to generate a [`Command`] for [`Handler::release`].\npub const fn release(mutex: Handle) -> Command {\n    (Check::Release(mutex), Response::Skip())\n}"
  },
  {
    "path": "tools/aml_test_tools/src/lib.rs",
    "content": "#![feature(sync_unsafe_cell)]\n//! A collection of helper utilities for testing AML using the [`acpi`] crate.\n//!\n//! These utilities are very heavily based on the way the [`acpi`] crate has used them historically.\n//! As always, feel free to offer PRs for improvements.\n\npub mod handlers;\npub mod result;\npub mod tables;\n\nuse crate::{\n    result::{ExpectedResult, result_matches},\n    tables::{TestAcpiTable, bytes_to_tables},\n};\nuse acpi::{\n    Handler,\n    PhysicalMapping,\n    address::MappedGas,\n    aml::{AmlError, Interpreter, namespace::AmlName},\n    sdt::{Signature, facs::Facs},\n};\nuse log::{error, trace};\nuse std::{\n    cell::SyncUnsafeCell,\n    ffi::OsStr,\n    fmt::Debug,\n    fs::File,\n    io::{Read, Write},\n    panic::{AssertUnwindSafe, catch_unwind},\n    path::PathBuf,\n    process::Command,\n    ptr::NonNull,\n    str::FromStr,\n    sync::{Arc, atomic::AtomicU32},\n};\nuse tempfile::{NamedTempFile, TempDir, tempdir};\n\n/// The result of a call to [`run_test`]. This is a bit of a combination - it contains the actual\n/// result of the test, but also the interpreter - if it is still valid.\npub enum RunTestResult<T>\nwhere\n    T: Handler,\n{\n    /// The test passed, and the interpreter is still valid.\n    Pass(Interpreter<T>),\n\n    /// The test failed, but the interpreter is still valid. A failure reason is also provided.\n    Failed(Interpreter<T>, TestFailureReason),\n\n    /// The test failed, and the interpreter is no longer valid.\n    Panicked,\n}\n\n/// The result of a test\n#[derive(Debug, PartialEq)]\npub enum TestResult {\n    /// The test passed.\n    Pass,\n\n    /// The test failed.\n    Failed(TestFailureReason),\n\n    /// The test caused a panic, probably in the interpreter. This is separated out from the failed\n    /// case for two reasons:\n    ///\n    /// 1. We want to highlight panics as they are in some ways worse than a failure - some AML\n    ///    might cause the problems for the interpreter, but the system as a whole shouldn't crash\n    /// 2. The interpreter that was being used for testing is no longer valid - a new one is needed.\n    Panicked,\n}\n\nimpl<T> From<&RunTestResult<T>> for TestResult\nwhere\n    T: Handler,\n{\n    fn from(result: &RunTestResult<T>) -> Self {\n        match result {\n            RunTestResult::Pass(_) => TestResult::Pass,\n            RunTestResult::Failed(_, reason) => TestResult::Failed(reason.clone()),\n            RunTestResult::Panicked => TestResult::Panicked,\n        }\n    }\n}\n\n/// Possible results of [`resolve_and_compile`].\npub enum CompilationOutcome {\n    /// Not a valid ASL or AML file.\n    Ignored,\n    /// A filesystem error occurred.\n    FilesystemErr(PathBuf),\n    /// This file is already an AML file, does not need recompiling.\n    IsAml(PathBuf),\n    /// Both .asl and .aml files exist, and the .aml file is newer - it does not need recompiling.\n    Newer(PathBuf),\n    /// There's a .asl file only, but [`resolve_and_compile`] was called with `can_compile=false`.\n    NotCompiled(PathBuf),\n    /// Compilation was attempted but failed. The compilation output has been printed to stdout.\n    Failed(PathBuf),\n    /// An ASL file was found and compiled successfully. This variant contains the path to the\n    /// compiled AML file.\n    Succeeded(PathBuf),\n}\n\n/// Possible causes of a test failure.\n#[derive(Clone, Debug, /*Eq, Hash,*/ PartialEq)]\npub enum TestFailureReason {\n    /// The test ASL failed compilation by `iasl`.\n    CompileFail,\n    /// Some error occurred attempting to read or write the test file.\n    FilesystemErr,\n    /// There was a problem interpreting the basic structure of the tables in the AML file.\n    TablesErr,\n    /// Our interpreter failed to parse or execute the resulting AML.\n    ParseFail(AmlError),\n}\n\n/// An internal-only struct that helps keep track of temporary files generated by\n/// [`run_test_for_string`]\nstruct TempScriptFile {\n    // This appears to be unused, but since it deletes the temporary directory when it's dropped, we\n    // need to keep it.\n    #[allow(unused)]\n    dir: TempDir,\n    asl_file: NamedTempFile,\n}\n\n/// Determine what to do with this file - ignore, compile and parse, or just parse.\n/// If \".aml\" does not exist, or if \".asl\" is newer, compiles the file.\n/// If the \".aml\" file is newer, indicate it is ready to parse.\n///\n/// Arguments:\n/// - `path`: The filename of an ASL file to (possibly) compile.\n/// - `can_compile`: Whether compilation is allowed for this file. If not, any existing AML file\n///   with the same name (but different extension) will be used.\npub fn resolve_and_compile(path: &PathBuf, can_compile: bool) -> CompilationOutcome {\n    let Ok(meta) = path.metadata() else {\n        return CompilationOutcome::FilesystemErr(path.clone());\n    };\n\n    // If this file is aml and it exists, it's ready for parsing\n    // metadata() will error if the file does not exist\n    if path.extension() == Some(OsStr::new(\"aml\")) && meta.is_file() {\n        return CompilationOutcome::IsAml(path.clone());\n    }\n\n    // If this file is not asl, it's not interesting. Error if the file does not exist.\n    if path.extension() != Some(OsStr::new(\"asl\")) || !meta.is_file() {\n        return CompilationOutcome::Ignored;\n    }\n\n    let aml_path = path.with_extension(\"aml\");\n    if aml_path.is_file() {\n        let Ok(asl_last_modified) = meta.modified() else {\n            return CompilationOutcome::FilesystemErr(path.clone());\n        };\n        let Ok(aml_last_modified) = aml_path.metadata().and_then(|m| m.modified()) else {\n            return CompilationOutcome::FilesystemErr(path.clone());\n        };\n        // If the aml is more recent than the asl, use the existing aml\n        // Otherwise continue to compilation\n        if asl_last_modified <= aml_last_modified {\n            return CompilationOutcome::Newer(aml_path);\n        }\n    }\n\n    if !can_compile {\n        return CompilationOutcome::NotCompiled(path.clone());\n    }\n\n    // Compile the ASL file using `iasl`\n    println!(\"Compiling file: {}\", path.display());\n    let output = Command::new(\"iasl\").arg(\"-oa\").arg(path).output();\n\n    match output {\n        Ok(output) => {\n            if !output.status.success() {\n                println!(\n                    \"Failed to compile ASL file: {}. Output from iasl:\\n {}\",\n                    path.display(),\n                    String::from_utf8_lossy(&output.stderr)\n                );\n                CompilationOutcome::Failed(path.clone())\n            } else {\n                CompilationOutcome::Succeeded(aml_path)\n            }\n        }\n        Err(e) => {\n            println!(\"Failed to compile ASL file: {}. Error: {}\", path.display(), e);\n            CompilationOutcome::Failed(path.clone())\n        }\n    }\n}\n\n/// Construct a new [interpreter](Interpreter) to use for testing.\n///\n/// This interpreter uses some simple fake registers and FACS to facilitate testing. It is not\n/// mandatory to use this interpreter for testing when using other functions in this crate.\n///\n/// Arguments:\n///\n/// * `handler`: The Handler to be called by the interpreter when needed. This crate includes some\n///   example [handlers].\n///\n/// Thread safety:\n///\n/// This function uses a single, static, FACS for all tests. If tests are run in parallel, this\n/// means they will share a single global lock.\npub fn new_interpreter<T>(handler: T) -> Interpreter<T>\nwhere\n    T: Handler + Clone,\n{\n    let fake_registers = Arc::new(acpi::registers::FixedRegisters {\n        pm1_event_registers: acpi::registers::Pm1EventRegisterBlock {\n            pm1_event_length: 8,\n            pm1a: unsafe {\n                MappedGas::map_gas(\n                    acpi::address::GenericAddress {\n                        address_space: acpi::address::AddressSpace::SystemIo,\n                        bit_width: 32,\n                        bit_offset: 0,\n                        access_size: 1,\n                        address: 0x400,\n                    },\n                    &handler,\n                )\n                .unwrap()\n            },\n            pm1b: None,\n        },\n        pm1_control_registers: acpi::registers::Pm1ControlRegisterBlock {\n            pm1a: unsafe {\n                MappedGas::map_gas(\n                    acpi::address::GenericAddress {\n                        address_space: acpi::address::AddressSpace::SystemIo,\n                        bit_width: 32,\n                        bit_offset: 0,\n                        access_size: 1,\n                        address: 0x600,\n                    },\n                    &handler,\n                )\n                .unwrap()\n            },\n            pm1b: None,\n        },\n    });\n\n    // As noted in the doc-comment, this Facs is shared between all tests - so if tests are run in\n    // parallel, they will share a single global lock.\n    //\n    // This construct is needed because the FACS in a real system is effectively 'static, and the\n    // interpreter relies on that.\n    static FAKE_FACS: SyncUnsafeCell<Facs> = SyncUnsafeCell::new(Facs {\n        signature: Signature::FACS,\n        length: size_of::<Facs>() as u32,\n        hardware_signature: 0,\n        firmware_waking_vector: 0,\n        global_lock: AtomicU32::new(0),\n        flags: 0,\n        x_firmware_waking_vector: 0,\n        version: 2,\n        _reserved0: [0; 3],\n        ospm_flags: 0,\n        reserved1: [0; 24],\n    });\n\n    let fake_facs_ptr = FAKE_FACS.get();\n\n    // This PhysicalMapping is dropped when the interpreter is dropped, and if you use logging in\n    // the handler object you'll see a call to Handler::unmap_physical_region without any\n    // corresponding call to Interpreter::map_physical_region.\n    let fake_facs_mapping = PhysicalMapping {\n        physical_start: 0x0,\n        virtual_start: NonNull::new(fake_facs_ptr).unwrap(),\n        region_length: 32,\n        mapped_length: 32,\n        handler: handler.clone(),\n    };\n    Interpreter::new(handler, 2, fake_registers, Some(fake_facs_mapping))\n}\n\n/// Test an ASL script given as a string, using [`run_test`].\n///\n/// Arguments:\n/// * `asl`: A string slice containing an ASL script. This will be compiled to AML using `iasl` and\n///   then tested using [`run_test`]\n/// * `interpreter`: The interpreter to use for testing.\npub fn run_test_for_string<T>(\n    asl: &'static str,\n    interpreter: Interpreter<T>,\n    expected_result: &Option<ExpectedResult>,\n) -> RunTestResult<T>\nwhere\n    T: Handler,\n{\n    let script = create_script_file(asl);\n    match resolve_and_compile(&script.asl_file.path().to_path_buf(), true) {\n        CompilationOutcome::Succeeded(aml_path) | CompilationOutcome::IsAml(aml_path) => {\n            run_test_for_file(&aml_path, interpreter, expected_result)\n        }\n        _ => RunTestResult::Failed(interpreter, TestFailureReason::CompileFail),\n    }\n}\n\n/// Test an AML file using [`run_test`]\n///\n/// Arguments:\n///\n/// * `file`: The path to the AML file to test. This must be an AML file otherwise the test will\n///   fail very quickly.\n/// * `interpreter`: The interpreter to use for testing.\npub fn run_test_for_file<T>(\n    file: &PathBuf,\n    interpreter: Interpreter<T>,\n    expected_result: &Option<ExpectedResult>,\n) -> RunTestResult<T>\nwhere\n    T: Handler,\n{\n    let Ok(mut file) = File::open(file) else {\n        return RunTestResult::Failed(interpreter, TestFailureReason::FilesystemErr);\n    };\n    let mut contents = Vec::new();\n    file.read_to_end(&mut contents).unwrap();\n\n    let Ok(tables) = bytes_to_tables(&contents) else {\n        return RunTestResult::Failed(interpreter, TestFailureReason::TablesErr);\n    };\n\n    run_test(tables, interpreter, expected_result)\n}\n\n/// Internal function to create a temporary script file from an ASL string, plus to calculate the\n/// name of the post-compilation AML file.\nfn create_script_file(asl: &'static str) -> TempScriptFile {\n    let dir = tempdir().unwrap();\n    trace!(\"Created temp dir: {:?}\", dir.path());\n    let mut script_file = NamedTempFile::with_suffix_in(\".asl\", &dir).unwrap();\n    trace!(\"Created temp file: {:?}\", script_file.path());\n\n    script_file.write_all(asl.as_bytes()).unwrap();\n    script_file.flush().unwrap();\n\n    TempScriptFile { dir, asl_file: script_file }\n}\n\n/// Run a test on an AML stream.\n///\n/// In this context, \"a test\" means:\n///\n/// * Attempting to parse the AML\n/// * If the AML contains a \"MAIN\" method, attempting to execute it.\n///\n/// Arguments:\n///\n/// * `tables`: A Vec of tables to test. The DSDT will be loaded first, if found. Other tables will\n///   be loaded in the order they appear in the Vec.\n/// * `interpreter`: The interpreter to test with. The interpreter is consumed to maintain unwind\n///   safety - if the interpreter panics, the caller should not be able to see the interpreter in\n///   an inconsistent state.\npub fn run_test<T>(\n    tables: Vec<TestAcpiTable>,\n    interpreter: Interpreter<T>,\n    expected_result: &Option<ExpectedResult>,\n) -> RunTestResult<T>\nwhere\n    T: Handler,\n{\n    // Without `AssertUnwindSafe`, the following code will not build as the Interpreter is not\n    // unwind safe. To avoid the caller being able to see an inconsistent Interpreter, if a panic\n    // occurs we drop the Interpreter, forcing the caller to create a new one.\n    let result = catch_unwind(AssertUnwindSafe(|| -> Result<(), AmlError> {\n        // Load the DSDT table first, if there is one.\n        if let Some(dsdt) = tables.iter().find(|t| t.header().signature == Signature::DSDT) {\n            trace!(\"Loading table: DSDT\");\n            interpreter.load_table(dsdt.content())?;\n        }\n        let others = tables.iter().filter(|t| t.header().signature != Signature::DSDT);\n\n        for t in others {\n            let h = t.header();\n            trace!(\n                \"Loading table: {:?}/{:?}/{:?}\",\n                h.signature,\n                str::from_utf8(&h.oem_id).unwrap_or(\"Unknown\"),\n                str::from_utf8(&h.oem_table_id).unwrap_or(\"Unknown\")\n            );\n            interpreter.load_table(t.content())?;\n        }\n\n        trace!(\"All tables loaded\");\n\n        if let Some(result) = interpreter.evaluate_if_present(AmlName::from_str(\"\\\\MAIN\").unwrap(), vec![])? {\n            let expected_result = expected_result.as_ref().unwrap_or(&ExpectedResult::Integer(0));\n            if result_matches(expected_result, &result) {\n                Ok(())\n            } else {\n                let e = format!(\"Unexpected MAIN result: {}, expected: {:?}\", *result, expected_result);\n                error!(\"{}\", e);\n                Err(AmlError::HostError(e))\n            }\n        } else {\n            Ok(())\n        }\n    }));\n\n    match result {\n        Ok(inner) => match inner {\n            Ok(()) => RunTestResult::Pass(interpreter),\n            Err(e) => RunTestResult::Failed(interpreter, TestFailureReason::ParseFail(e)),\n        },\n        Err(_e) => RunTestResult::Panicked,\n    }\n}\n"
  },
  {
    "path": "tools/aml_test_tools/src/result.rs",
    "content": "use acpi::aml::object::Object;\n\n#[derive(Clone, Debug)]\npub enum ExpectedResult {\n    Integer(u64),\n    String(String),\n}\n\npub fn result_matches(expected: &ExpectedResult, actual: &Object) -> bool {\n    match (expected, actual) {\n        (ExpectedResult::Integer(expected), Object::Integer(actual)) => expected == actual,\n        (ExpectedResult::String(expected), Object::String(actual)) => expected == actual,\n        _ => false,\n    }\n}\n"
  },
  {
    "path": "tools/aml_test_tools/src/tables.rs",
    "content": "//! A basic interpreter to extract ACPI tables from byte-slices.\n\nuse acpi::sdt::SdtHeader;\nuse std::mem::transmute;\n\nconst AML_TABLE_HEADER_LENGTH: usize = 36;\n\n/// An ACPI table separated into header and content.\n///\n/// This is not provided in the main crate as it is unnecessary - but it is useful for testing.\npub struct TestAcpiTable {\n    header: SdtHeader,\n    content: Vec<u8>,\n}\n\nimpl TryFrom<&[u8]> for TestAcpiTable {\n    type Error = &'static str;\n\n    fn try_from(bytes: &[u8]) -> Result<Self, Self::Error> {\n        if bytes.len() < AML_TABLE_HEADER_LENGTH {\n            return Err(\"Buffer shorter than table header\");\n        }\n\n        let mut header_bytes: [u8; AML_TABLE_HEADER_LENGTH] = [0; AML_TABLE_HEADER_LENGTH];\n        header_bytes.copy_from_slice(&bytes[..AML_TABLE_HEADER_LENGTH]);\n        let header: SdtHeader = unsafe { transmute(header_bytes) };\n\n        if header.length < AML_TABLE_HEADER_LENGTH as u32 {\n            return Err(\"AML table header reported length too short\");\n        }\n\n        let content = bytes[AML_TABLE_HEADER_LENGTH..header.length as usize].to_vec();\n\n        Ok(Self { header, content })\n    }\n}\n\n/// Provide accessor functions for the two fields - don't provide write access so that `content`\n/// and `header` can't get out of sync with each other.\nimpl TestAcpiTable {\n    pub fn content(&self) -> &[u8] {\n        &self.content\n    }\n\n    pub fn header(&self) -> &SdtHeader {\n        &self.header\n    }\n}\n\n/// Construct a Vec of AML tables from a slice of bytes.\npub fn bytes_to_tables(bytes: &[u8]) -> Result<Vec<TestAcpiTable>, &'static str> {\n    let mut tables = Vec::new();\n    let mut offset = 0;\n\n    while offset < bytes.len() {\n        let table = TestAcpiTable::try_from(&bytes[offset..])?;\n        offset += table.header.length as usize;\n        tables.push(table);\n    }\n\n    Ok(tables)\n}\n"
  },
  {
    "path": "tools/aml_tester/Cargo.toml",
    "content": "[package]\nname = \"aml_tester\"\nversion = \"0.1.0\"\nauthors = [\"Isaac Woods\"]\nedition = \"2018\"\n\n[dependencies]\nacpi = { path = \"../..\" }\naml_test_tools = { path = \"../aml_test_tools\" }\nclap = \"4\"\ncolored = \"3.1.1\"\nlog = \"0.4\"\nparse_int = \"0.9.0\"\npretty_env_logger = \"0.5.0\"\n"
  },
  {
    "path": "tools/aml_tester/src/main.rs",
    "content": "/*\n * This is a small program that is meant for testing the AML parser on artificial\n * AML. We want to:\n *      - scan a directory for ASL files\n *      - compile them using `iasl` into AML files (these should be gitignored), but only if the ASL file has a\n *        newer timestamp than the AML file (or just compile if there isn't a corresponding AML file)\n *      - Run the AML parser on each AML file, printing test output like `cargo test` does in a nice table for\n *        each AML file\n *      - For failing tests, print out a nice summary of the errors for each file\n *\n * `aml_tester` can be used with `uacpi_test_adapter` to run the uACPI test suite (except for the\n * resource tests). See the `uacpi_test_adapter` documentation for more information.\n */\n\nuse acpi::Handler;\nuse aml_test_tools::{\n    handlers::{logging_handler::LoggingHandler, null_handler::NullHandler},\n    new_interpreter,\n    resolve_and_compile,\n    result::ExpectedResult,\n    CompilationOutcome,\n    RunTestResult,\n    TestFailureReason,\n    TestResult,\n};\nuse clap::{Arg, ArgAction, ArgGroup};\nuse colored::Colorize;\nuse parse_int::parse;\nuse std::{\n    collections::HashSet,\n    fs::{self},\n    io::Write,\n    path::{Path, PathBuf},\n    process::{Command, ExitCode},\n};\n\n/// The result of a test, with all other information (error codes etc.) stripped away. This value\n/// can then be stored in a Set - the test results with more info cannot.\n#[derive(Eq, Hash, PartialEq)]\nenum FinalTestResult {\n    Passed,\n    Failed,\n    NotCompiled,\n    CompileFailed,\n}\n\nimpl From<TestResult> for FinalTestResult {\n    fn from(outcome: TestResult) -> Self {\n        match outcome {\n            TestResult::Pass => FinalTestResult::Passed,\n            TestResult::Failed(e) => match e {\n                TestFailureReason::CompileFail | TestFailureReason::FilesystemErr => {\n                    FinalTestResult::CompileFailed\n                }\n                _ => FinalTestResult::Failed,\n            },\n            TestResult::Panicked => FinalTestResult::Failed,\n        }\n    }\n}\n\nfn main() -> ExitCode {\n    pretty_env_logger::init();\n\n    let mut cmd = clap::Command::new(\"aml_tester\")\n        .version(\"v0.1.0\")\n        .author(\"Isaac Woods\")\n        .about(\n            \"Compiles ASL files and checks that they can be parsed by the ACPI crate.\nIf the ASL contains a MAIN method, it will be executed.\",\n        )\n        .arg(Arg::new(\"no_compile\").long(\"no-compile\").action(ArgAction::SetTrue).help(\"Don't compile ASL to AML\"))\n        .arg(\n            Arg::new(\"combined\")\n                .long(\"combined\")\n                .action(ArgAction::SetTrue)\n                .help(\"Don't clear the namespace between tests\"),\n        )\n        .arg(Arg::new(\"path\").short('p').long(\"path\").required(false).action(ArgAction::Set).value_name(\"DIR\"))\n        // The --expect argument is defined to exactly match the parameters sent by the uACPI test\n        // runner. This makes it much easier to test our crate against their tests.\n        .arg(\n            Arg::new(\"expect\")\n                .long(\"expect\")\n                .value_names([\"TYPE\", \"VALUE\"])\n                .num_args(2)\n                .required(false)\n                .help(\"Expect MAIN to return a value: --expect int <unsigned> or --expect str <text>\"),\n        )\n        .arg(Arg::new(\"files\").action(ArgAction::Append).value_name(\"FILE.{asl,aml}\"))\n        .group(ArgGroup::new(\"files_list\").args([\"path\", \"files\"]).required(true));\n    if std::env::args().count() <= 1 {\n        cmd.print_help().unwrap();\n        return ExitCode::SUCCESS;\n    }\n\n    let matches = cmd.get_matches();\n\n    let Ok(expected_result) = matches\n        .get_many::<String>(\"expect\")\n        .map(|mut values| {\n            let expect_type = values.next().unwrap();\n            let expect_value = values.next().unwrap();\n\n            match expect_type.as_str() {\n                \"int\" => {\n                    let parsed = parse::<u64>(expect_value).map_err(|_| {\n                        println!(\"Invalid --expect value for type int: expected an unsigned integer\");\n                    })?;\n                    Ok(ExpectedResult::Integer(parsed))\n                }\n                \"str\" => Ok(ExpectedResult::String(expect_value.clone())),\n                _ => {\n                    println!(\"Invalid --expect type: expected `int` or `str`\");\n                    Err(())\n                }\n            }\n        })\n        .transpose()\n    else {\n        return ExitCode::FAILURE;\n    };\n\n    // Make sure we have the ability to compile ASL -> AML, if user wants it\n    let user_wants_compile = !matches.get_flag(\"no_compile\");\n    let can_compile = user_wants_compile &&\n        // Test if `iasl` is installed, so we can give a good error later if it's not\n        match Command::new(\"iasl\").arg(\"-v\").status() {\n            Ok(exit_status) if exit_status.success() => true,\n            Ok(exit_status) => {\n                panic!(\"`iasl` exited with unsuccessful status: {:?}\", exit_status);\n            },\n            Err(_) => false,\n    };\n\n    let tests = find_tests(&matches).unwrap();\n    let compiled_files: Vec<CompilationOutcome> =\n        tests.iter().map(|name| resolve_and_compile(name, can_compile)).collect();\n\n    // Check if compilation should have happened but did not\n    if user_wants_compile\n        && compiled_files.iter().any(|outcome| matches!(outcome, CompilationOutcome::NotCompiled(_)))\n    {\n        panic!(\n            \"`iasl` is not installed, but we want to compile some ASL files! Pass --no-compile, or install `iasl`\"\n        );\n    }\n    // Report compilation results\n    if user_wants_compile {\n        let (passed, failed) = compiled_files.iter().fold((0, 0), |(passed, failed), outcome| match outcome {\n            CompilationOutcome::Succeeded(_) => (passed + 1, failed),\n            CompilationOutcome::Failed(_) => (passed, failed + 1),\n            _ => (passed, failed),\n        });\n        if passed + failed > 0 {\n            println!(\n                \"Compiled {} ASL files: {}, {}\",\n                passed + failed,\n                format!(\"{} passed\", passed).green(),\n                format!(\"{} failed\", failed).red(),\n            );\n            println!();\n        }\n    }\n\n    let mut passed = 0u32;\n    let mut failed = 0u32;\n\n    // Make a list of the files we have processed, and skip them if we see them again\n    let mut dedup_list: HashSet<PathBuf> = HashSet::new();\n    let mut summaries: HashSet<(PathBuf, FinalTestResult)> = HashSet::new();\n    // Filter down to the final list of AML files\n    let aml_files = compiled_files\n        .iter()\n        .filter_map(|outcome| match outcome {\n            CompilationOutcome::IsAml(path)\n            | CompilationOutcome::Newer(path)\n            | CompilationOutcome::Succeeded(path) => Some(path.clone()),\n            CompilationOutcome::Failed(path)\n            | CompilationOutcome::NotCompiled(path)\n            | CompilationOutcome::FilesystemErr(path) => {\n                summaries.insert((path.clone(), FinalTestResult::NotCompiled));\n                failed += 1;\n                None\n            }\n            CompilationOutcome::Ignored => None,\n        })\n        .filter(|path| {\n            if dedup_list.contains(path) {\n                false\n            } else {\n                dedup_list.insert(path.clone());\n                true\n            }\n        })\n        .collect::<Vec<_>>();\n\n    let combined_test = matches.get_flag(\"combined\");\n    let mut interpreter = new_interpreter(new_handler());\n\n    for file_entry in aml_files {\n        print!(\"Testing AML file: {:?}... \", file_entry);\n        std::io::stdout().flush().unwrap();\n\n        let result = aml_test_tools::run_test_for_file(&file_entry, interpreter, &expected_result);\n        let simple_result: FinalTestResult = TestResult::from(&result).into();\n\n        let interpreter_returned = match result {\n            RunTestResult::Pass(i) => {\n                println!(\"{}\", \"OK\".green());\n                passed += 1;\n                Some(i)\n            }\n            RunTestResult::Failed(i, e) => {\n                println!(\"{}\", format!(\"Failed ({:?})\", e).red());\n                failed += 1;\n                Some(i)\n            }\n            RunTestResult::Panicked => {\n                println!(\"{}\", \"Panicked\".red());\n                failed += 1;\n                if combined_test {\n                    // We can't continue to use the old Interpreter, and the user specifically wants\n                    // to. Creating a new one most likely invalidates the test they were trying to\n                    // perform.\n                    panic!(\"Interpreter panicked during combined test, unable to continue.\");\n                }\n                None\n            }\n        };\n\n        if let Some(ref interpreter) = interpreter_returned {\n            println!(\"Namespace: {}\", interpreter.namespace.lock());\n        }\n\n        interpreter = match interpreter_returned {\n            _ if !combined_test => new_interpreter(new_handler()),\n            None => new_interpreter(new_handler()),\n            Some(i) => i,\n        };\n\n        summaries.insert((file_entry, simple_result));\n    }\n\n    // Print summaries\n    println!(\"Summary:\");\n    for (file, status) in summaries.iter() {\n        let status = match status {\n            FinalTestResult::Passed => {\n                format!(\"{}\", \"OK\".green())\n            }\n            FinalTestResult::CompileFailed => {\n                format!(\"{}\", \"COMPILE FAIL\".red())\n            }\n            FinalTestResult::Failed => {\n                format!(\"{}\", \"PARSE FAIL\".red())\n            }\n            FinalTestResult::NotCompiled => {\n                format!(\"{}\", \"NOT COMPILED\".red())\n            }\n        };\n        println!(\"\\t{:<50}: {}\", file.to_str().unwrap(), status);\n    }\n    println!(\"\\nTest results: {}, {}\", format!(\"{} passed\", passed).green(), format!(\"{} failed\", failed).red());\n    if failed == 0 {\n        ExitCode::SUCCESS\n    } else {\n        ExitCode::FAILURE\n    }\n}\n\nfn find_tests(matches: &clap::ArgMatches) -> std::io::Result<Vec<PathBuf>> {\n    // Get an initial list of files - may not work correctly on non-UTF8 OsString\n    let files: Vec<PathBuf> = if matches.contains_id(\"path\") {\n        let dir_path = Path::new(matches.get_one::<String>(\"path\").unwrap());\n\n        if fs::metadata(dir_path)?.is_dir() {\n            println!(\"Running tests in directory: {:?}\", dir_path);\n            fs::read_dir(dir_path)?\n                .filter_map(|entry| if let Ok(entry) = entry { Some(entry.path().to_path_buf()) } else { None })\n                .collect()\n        } else {\n            println!(\"Running single test: {:?}\", dir_path);\n            vec![dir_path.to_path_buf()]\n        }\n    } else {\n        matches.get_many::<String>(\"files\").unwrap_or_default().map(PathBuf::from).collect()\n    };\n\n    // Make sure all files exist, propagate error if it occurs\n    files.iter().fold(Ok(()), |result: std::io::Result<()>, path| {\n        if !path.is_file() {\n            println!(\"Not a regular file: {}\", path.display());\n            path.metadata()?;\n        }\n        result\n    })?;\n\n    Ok(files)\n}\n\nfn new_handler() -> impl Handler {\n    LoggingHandler::new(NullHandler {})\n}\n"
  },
  {
    "path": "tools/uacpi_test_adapter/Cargo.toml",
    "content": "[package]\nname = \"uacpi_test_adapter\"\nversion = \"0.0.0\"\npublish = false\nauthors = [\"Martin Hughes\"]\nrepository = \"https://github.com/rust-osdev/acpi\"\ndescription = \"uACPI to aml_tester adapter\"\ncategories = [\"hardware-support\"]\nreadme = \"../../README.md\"\nlicense = \"MIT/Apache-2.0\"\nedition = \"2024\"\n\n[dependencies]\nwhich = \"8.0.2\"\n"
  },
  {
    "path": "tools/uacpi_test_adapter/src/main.rs",
    "content": "//! A wrapper around the `aml_tester` crate that allows it to run the uACPI test suite.\n//!\n//! Simply forwards all arguments sent from the uACPI test runner to `aml_tester`, *except* for when\n//! the resource-tests are requested (which we don't support).\n//!\n//! Why make an adapter instead of just running `aml_tester` directly?\n//! * It allows uACPI to change their testing system without affecting users of `aml_tester`.\n//! * We can write a separate binary or other tool for running \"resource-tests\" without needing to\n//!   complicate `aml_tester`.\n//!\n//! Usage:\n//! * Make sure `aml_tester` can be found. The following methods are tried, in this order of\n//!   precedence:\n//!   * Set the environment variable `AML_TESTER_PATH` to the path and filename of the `aml_tester`\n//!     binary, or\n//!   * Make sure `aml_tester` is in the system PATH, or\n//!   * Ensure `aml_tester` is in the same folder as `uacpi_test_adapter`.\n//! * Run the uACPI test suite but setting `uacpi_test_adapter` to the test runner.\n//!\n//! e.g.: from the uACPI root directory:\n//! ```sh\n//! AML_TESTER_PATH=../acpi/target/debug/aml_tester python3 tests/run_tests.py --test-runner ../acpi/target/debug/uacpi_test_adapter\n//! ```\n//! > Adjust the paths as needed!\n//!\n//! Notes:\n//!\n//! You may prefer to manually run aml_tester with individual ASL files from the uACPI test suite,\n//! as you'll get better formatting. However, that would require you to manually enter the expected\n//! results.\nuse std::{\n    env,\n    ffi::OsString,\n    path::PathBuf,\n    process::{Command, ExitCode},\n};\nuse which::which;\n\nfn main() -> ExitCode {\n    // We don't support the resource tests, so just claim success to allow the main test suite to\n    // run.\n    if env::args_os().skip(1).any(|arg| arg == \"resource-tests\") {\n        return ExitCode::SUCCESS;\n    }\n\n    let Some(tester_path) = get_aml_tester_path() else {\n        eprintln!(\"aml_tester not found. Try setting AML_TESTER_PATH to the path of the aml_tester binary.\");\n        return ExitCode::FAILURE;\n    };\n\n    let status = match Command::new(tester_path).args(env::args_os().skip(1)).status() {\n        Ok(status) => status,\n        Err(err) => {\n            eprintln!(\"Failed to execute aml_tester: {err}\");\n            return ExitCode::FAILURE;\n        }\n    };\n\n    match status.code() {\n        Some(code) => ExitCode::from(code as u8),\n        None => ExitCode::FAILURE,\n    }\n}\n\n/// Find the path to the `aml_tester` binary.\n///\n/// Uses the search order given in this executable's main documentation.\nfn get_aml_tester_path() -> Option<OsString> {\n    [get_aml_tester_from_env, get_aml_tester_from_path_env, get_aml_tester_from_binary_path]\n        .iter()\n        .find_map(|f| f())\n}\n\n/// If the environment variable `AML_TESTER_PATH` is set, use that. Assume it is correct without\n/// checking.\nfn get_aml_tester_from_env() -> Option<OsString> {\n    env::var_os(\"AML_TESTER_PATH\")\n}\n\n/// If `aml_tester` is in the system PATH, use that.\nfn get_aml_tester_from_path_env() -> Option<OsString> {\n    which(\"aml_tester\").ok().map(|path| path.into())\n}\n\n/// If `aml_tester` exists alongside this executable, use that.\nfn get_aml_tester_from_binary_path() -> Option<OsString> {\n    // This says: \"change the name of the current executable to `aml_tester` and see if that exists.\"\n    env::current_exe().ok().map(change_exec_name).and_then(|path| which(path).ok()).map(|path| path.into())\n}\n\n/// Replace the filename of the given path with `aml_tester`. Preserve the extension so that\n/// Windows won't have problems.\nfn change_exec_name(mut path: PathBuf) -> PathBuf {\n    // The final `unwrap` on the following line is reasonable because if the UTF-8 conversion fails,\n    // the filename is probably invalid, so we don't really want to keep trying to use it!\n    let extension = String::from(path.extension().unwrap_or_default().to_str().unwrap());\n    path.set_file_name(\"aml_tester\");\n    path.set_extension(extension);\n    path\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    #[test]\n    fn test_filename_replacement() {\n        const PATH: &str = \"/a/b/c/uacpi_test_adapter.ext\";\n        let original_path = PathBuf::from(PATH);\n        let expected_path = PathBuf::from(\"/a/b/c/aml_tester.ext\");\n        let result_path = change_exec_name(original_path);\n\n        assert_eq!(result_path, expected_path);\n    }\n}\n"
  }
]