Initial version

Add some common FF-A functionality. Supported features (roughly):
  - define FF-A function IDs and error codes
  - create boot info descriptor
  - create partition info descriptor
  - create and parse memory transaction descriptor
  - parse memory relinquish descriptor
  - parse console log message

Limitations apply, code quality is not production ready.

Signed-off-by: Balint Dobszay <balint.dobszay@arm.com>
Change-Id: Ibb3fde9d4c0d0d9b8823bb84cdcf23eb3f9d087a
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..ea8c4bf
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1 @@
+/target
diff --git a/.vscode/settings.json b/.vscode/settings.json
new file mode 100644
index 0000000..7b798fd
--- /dev/null
+++ b/.vscode/settings.json
@@ -0,0 +1,6 @@
+{
+        "rust-analyzer.check.allTargets": false,
+        "rust-analyzer.check.command": "clippy",
+        "rust-analyzer.cargo.target": "aarch64-unknown-none-softfloat",
+        "editor.formatOnSave": true,
+}
\ No newline at end of file
diff --git a/Cargo.lock b/Cargo.lock
new file mode 100644
index 0000000..60e2f03
--- /dev/null
+++ b/Cargo.lock
@@ -0,0 +1,72 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 3
+
+[[package]]
+name = "arm-ffa"
+version = "0.1.0"
+dependencies = [
+ "num_enum",
+ "uuid",
+]
+
+[[package]]
+name = "num_enum"
+version = "0.7.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4e613fc340b2220f734a8595782c551f1250e969d87d3be1ae0579e8d4065179"
+dependencies = [
+ "num_enum_derive",
+]
+
+[[package]]
+name = "num_enum_derive"
+version = "0.7.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "af1844ef2428cc3e1cb900be36181049ef3d3193c63e43026cfe202983b27a56"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "proc-macro2"
+version = "1.0.89"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f139b0662de085916d1fb67d2b4169d1addddda1919e696f3252b740b629986e"
+dependencies = [
+ "unicode-ident",
+]
+
+[[package]]
+name = "quote"
+version = "1.0.37"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af"
+dependencies = [
+ "proc-macro2",
+]
+
+[[package]]
+name = "syn"
+version = "2.0.85"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5023162dfcd14ef8f32034d8bcd4cc5ddc61ef7a247c024a33e24e1f24d21b56"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
+name = "unicode-ident"
+version = "1.0.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe"
+
+[[package]]
+name = "uuid"
+version = "1.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f8c5f0a0af699448548ad1a2fbf920fb4bee257eae39953ba95cb84891a0446a"
diff --git a/Cargo.toml b/Cargo.toml
new file mode 100644
index 0000000..90ed22a
--- /dev/null
+++ b/Cargo.toml
@@ -0,0 +1,19 @@
+# SPDX-FileCopyrightText: Copyright 2023 Arm Limited and/or its affiliates <open-source-office@arm.com>
+# SPDX-License-Identifier: MIT OR Apache-2.0
+
+[package]
+name = "arm-ffa"
+version = "0.1.0"
+authors = [
+    "Balint Dobszay <balint.dobszay@arm.com>",
+    "Imre Kis <imre.kis@arm.com>",
+]
+description = "Arm Firmware Framework for Arm A-profile support library"
+edition = "2021"
+license = "MIT OR Apache-2.0"
+repository = "https://git.trustedfirmware.org/rust-spmc/rust-spmc.git"
+keywords = ["arm", "aarch64", "spmc", "ff-a", "firmware"]
+
+[dependencies]
+num_enum = { version = "0.7", default-features = false }
+uuid = { version = "1.10", default-features = false }
diff --git a/LICENSE-APACHE b/LICENSE-APACHE
new file mode 100644
index 0000000..1b5ec8b
--- /dev/null
+++ b/LICENSE-APACHE
@@ -0,0 +1,176 @@
+                              Apache License
+                        Version 2.0, January 2004
+                     http://www.apache.org/licenses/
+
+TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+1. Definitions.
+
+   "License" shall mean the terms and conditions for use, reproduction,
+   and distribution as defined by Sections 1 through 9 of this document.
+
+   "Licensor" shall mean the copyright owner or entity authorized by
+   the copyright owner that is granting the License.
+
+   "Legal Entity" shall mean the union of the acting entity and all
+   other entities that control, are controlled by, or are under common
+   control with that entity. For the purposes of this definition,
+   "control" means (i) the power, direct or indirect, to cause the
+   direction or management of such entity, whether by contract or
+   otherwise, or (ii) ownership of fifty percent (50%) or more of the
+   outstanding shares, or (iii) beneficial ownership of such entity.
+
+   "You" (or "Your") shall mean an individual or Legal Entity
+   exercising permissions granted by this License.
+
+   "Source" form shall mean the preferred form for making modifications,
+   including but not limited to software source code, documentation
+   source, and configuration files.
+
+   "Object" form shall mean any form resulting from mechanical
+   transformation or translation of a Source form, including but
+   not limited to compiled object code, generated documentation,
+   and conversions to other media types.
+
+   "Work" shall mean the work of authorship, whether in Source or
+   Object form, made available under the License, as indicated by a
+   copyright notice that is included in or attached to the work
+   (an example is provided in the Appendix below).
+
+   "Derivative Works" shall mean any work, whether in Source or Object
+   form, that is based on (or derived from) the Work and for which the
+   editorial revisions, annotations, elaborations, or other modifications
+   represent, as a whole, an original work of authorship. For the purposes
+   of this License, Derivative Works shall not include works that remain
+   separable from, or merely link (or bind by name) to the interfaces of,
+   the Work and Derivative Works thereof.
+
+   "Contribution" shall mean any work of authorship, including
+   the original version of the Work and any modifications or additions
+   to that Work or Derivative Works thereof, that is intentionally
+   submitted to Licensor for inclusion in the Work by the copyright owner
+   or by an individual or Legal Entity authorized to submit on behalf of
+   the copyright owner. For the purposes of this definition, "submitted"
+   means any form of electronic, verbal, or written communication sent
+   to the Licensor or its representatives, including but not limited to
+   communication on electronic mailing lists, source code control systems,
+   and issue tracking systems that are managed by, or on behalf of, the
+   Licensor for the purpose of discussing and improving the Work, but
+   excluding communication that is conspicuously marked or otherwise
+   designated in writing by the copyright owner as "Not a Contribution."
+
+   "Contributor" shall mean Licensor and any individual or Legal Entity
+   on behalf of whom a Contribution has been received by Licensor and
+   subsequently incorporated within the Work.
+
+2. Grant of Copyright License. Subject to the terms and conditions of
+   this License, each Contributor hereby grants to You a perpetual,
+   worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+   copyright license to reproduce, prepare Derivative Works of,
+   publicly display, publicly perform, sublicense, and distribute the
+   Work and such Derivative Works in Source or Object form.
+
+3. Grant of Patent License. Subject to the terms and conditions of
+   this License, each Contributor hereby grants to You a perpetual,
+   worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+   (except as stated in this section) patent license to make, have made,
+   use, offer to sell, sell, import, and otherwise transfer the Work,
+   where such license applies only to those patent claims licensable
+   by such Contributor that are necessarily infringed by their
+   Contribution(s) alone or by combination of their Contribution(s)
+   with the Work to which such Contribution(s) was submitted. If You
+   institute patent litigation against any entity (including a
+   cross-claim or counterclaim in a lawsuit) alleging that the Work
+   or a Contribution incorporated within the Work constitutes direct
+   or contributory patent infringement, then any patent licenses
+   granted to You under this License for that Work shall terminate
+   as of the date such litigation is filed.
+
+4. Redistribution. You may reproduce and distribute copies of the
+   Work or Derivative Works thereof in any medium, with or without
+   modifications, and in Source or Object form, provided that You
+   meet the following conditions:
+
+   (a) You must give any other recipients of the Work or
+       Derivative Works a copy of this License; and
+
+   (b) You must cause any modified files to carry prominent notices
+       stating that You changed the files; and
+
+   (c) You must retain, in the Source form of any Derivative Works
+       that You distribute, all copyright, patent, trademark, and
+       attribution notices from the Source form of the Work,
+       excluding those notices that do not pertain to any part of
+       the Derivative Works; and
+
+   (d) If the Work includes a "NOTICE" text file as part of its
+       distribution, then any Derivative Works that You distribute must
+       include a readable copy of the attribution notices contained
+       within such NOTICE file, excluding those notices that do not
+       pertain to any part of the Derivative Works, in at least one
+       of the following places: within a NOTICE text file distributed
+       as part of the Derivative Works; within the Source form or
+       documentation, if provided along with the Derivative Works; or,
+       within a display generated by the Derivative Works, if and
+       wherever such third-party notices normally appear. The contents
+       of the NOTICE file are for informational purposes only and
+       do not modify the License. You may add Your own attribution
+       notices within Derivative Works that You distribute, alongside
+       or as an addendum to the NOTICE text from the Work, provided
+       that such additional attribution notices cannot be construed
+       as modifying the License.
+
+   You may add Your own copyright statement to Your modifications and
+   may provide additional or different license terms and conditions
+   for use, reproduction, or distribution of Your modifications, or
+   for any such Derivative Works as a whole, provided Your use,
+   reproduction, and distribution of the Work otherwise complies with
+   the conditions stated in this License.
+
+5. Submission of Contributions. Unless You explicitly state otherwise,
+   any Contribution intentionally submitted for inclusion in the Work
+   by You to the Licensor shall be under the terms and conditions of
+   this License, without any additional terms or conditions.
+   Notwithstanding the above, nothing herein shall supersede or modify
+   the terms of any separate license agreement you may have executed
+   with Licensor regarding such Contributions.
+
+6. Trademarks. This License does not grant permission to use the trade
+   names, trademarks, service marks, or product names of the Licensor,
+   except as required for reasonable and customary use in describing the
+   origin of the Work and reproducing the content of the NOTICE file.
+
+7. Disclaimer of Warranty. Unless required by applicable law or
+   agreed to in writing, Licensor provides the Work (and each
+   Contributor provides its Contributions) on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+   implied, including, without limitation, any warranties or conditions
+   of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+   PARTICULAR PURPOSE. You are solely responsible for determining the
+   appropriateness of using or redistributing the Work and assume any
+   risks associated with Your exercise of permissions under this License.
+
+8. Limitation of Liability. In no event and under no legal theory,
+   whether in tort (including negligence), contract, or otherwise,
+   unless required by applicable law (such as deliberate and grossly
+   negligent acts) or agreed to in writing, shall any Contributor be
+   liable to You for damages, including any direct, indirect, special,
+   incidental, or consequential damages of any character arising as a
+   result of this License or out of the use or inability to use the
+   Work (including but not limited to damages for loss of goodwill,
+   work stoppage, computer failure or malfunction, or any and all
+   other commercial damages or losses), even if such Contributor
+   has been advised of the possibility of such damages.
+
+9. Accepting Warranty or Additional Liability. While redistributing
+   the Work or Derivative Works thereof, You may choose to offer,
+   and charge a fee for, acceptance of support, warranty, indemnity,
+   or other liability obligations and/or rights consistent with this
+   License. However, in accepting such obligations, You may act only
+   on Your own behalf and on Your sole responsibility, not on behalf
+   of any other Contributor, and only if You agree to indemnify,
+   defend, and hold each Contributor harmless for any liability
+   incurred by, or claims asserted against, such Contributor by reason
+   of your accepting any such warranty or additional liability.
+
+END OF TERMS AND CONDITIONS
diff --git a/LICENSE-MIT b/LICENSE-MIT
new file mode 100644
index 0000000..31aa793
--- /dev/null
+++ b/LICENSE-MIT
@@ -0,0 +1,23 @@
+Permission is hereby granted, free of charge, to any
+person obtaining a copy of this software and associated
+documentation files (the "Software"), to deal in the
+Software without restriction, including without
+limitation the rights to use, copy, modify, merge,
+publish, distribute, sublicense, and/or sell copies of
+the Software, and to permit persons to whom the Software
+is furnished to do so, subject to the following
+conditions:
+
+The above copyright notice and this permission notice
+shall be included in all copies or substantial portions
+of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
+ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
+TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
+PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
+SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
+IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
diff --git a/dco.txt b/dco.txt
new file mode 100644
index 0000000..d950df0
--- /dev/null
+++ b/dco.txt
@@ -0,0 +1,34 @@
+Developer Certificate of Origin
+Version 1.1
+
+Copyright (C) 2004, 2006 The Linux Foundation and its contributors.
+
+Everyone is permitted to copy and distribute verbatim copies of this
+license document, but changing it is not allowed.
+
+
+Developer's Certificate of Origin 1.1
+
+By making a contribution to this project, I certify that:
+
+(a) The contribution was created in whole or in part by me and I
+    have the right to submit it under the open source license
+    indicated in the file; or
+
+(b) The contribution is based upon previous work that, to the best
+    of my knowledge, is covered under an appropriate open source
+    license and I have the right under that license to submit that
+    work with modifications, whether created in whole or in part
+    by me, under the same open source license (unless I am
+    permitted to submit under a different license), as indicated
+    in the file; or
+
+(c) The contribution was provided directly to me by some other
+    person who certified (a), (b) or (c) and I have not modified
+    it.
+
+(d) I understand and agree that this project and the contribution
+    are public and that a record of the contribution (including all
+    personal information I submit with it, including my sign-off) is
+    maintained indefinitely and may be redistributed consistent with
+    this project or the open source license(s) involved.
\ No newline at end of file
diff --git a/src/boot_info.rs b/src/boot_info.rs
new file mode 100644
index 0000000..553c7c5
--- /dev/null
+++ b/src/boot_info.rs
@@ -0,0 +1,182 @@
+// SPDX-FileCopyrightText: Copyright 2023 Arm Limited and/or its affiliates <open-source-office@arm.com>
+// SPDX-License-Identifier: MIT OR Apache-2.0
+
+use uuid::Uuid;
+
+pub enum BootInfoName {
+    NullTermString(&'static str),
+    Uuid(Uuid),
+}
+
+#[derive(Clone, Copy)]
+pub enum BootInfoStdType {
+    Fdt = 0,
+    Hob = 1,
+}
+
+pub enum BootInfoType {
+    Std(BootInfoStdType),
+    Impdef(u8),
+}
+
+pub enum BootInfoContents {
+    Address(usize),
+    Value(u64),
+}
+
+pub struct BootInfoDescriptor {
+    /// Name of boot information passed to the consumer
+    pub name: BootInfoName,
+
+    /// Type of boot information passed to the consumer
+    pub typ: BootInfoType,
+
+    /// Size (in bytes) of boot information identified by the Name and Type fields
+    pub size: u32,
+
+    pub contents: BootInfoContents,
+}
+
+impl BootInfoDescriptor {
+    pub fn create(descriptors: &[BootInfoDescriptor], buf: &mut [u8]) {
+        // Offset from the base of the header to the first element in the boot info descriptor array
+        // Must be 8 byte aligned
+        const DESC_ARRAY_OFFSET: usize = 32;
+
+        /// In FF-A v1.1, Table 5.8: Boot information descriptor is 32 bytes long
+        const DESC_SIZE: usize = 32;
+
+        // assert!(descriptors.len() <= u32::MAX as usize);
+        let desc_cnt = descriptors.len();
+
+        // Add the already known fields, later we have to add the sizes referenced by the individual descriptors
+        let mut total_size = 0usize;
+        total_size = total_size.checked_add(DESC_ARRAY_OFFSET).unwrap();
+        total_size = total_size
+            .checked_add(desc_cnt.checked_mul(DESC_SIZE).unwrap())
+            .unwrap();
+
+        // Create the boot info header starting at offset 0 in the buffer
+        // Offset 0, length 4: Hexadecimal value 0x0FFA to identify the header
+        buf[0..4].copy_from_slice(&0x0ffa_u32.to_le_bytes());
+
+        // Offset 4, length 4: Version of the boot information blob encoded as in FFA_VERSION_GET
+        buf[4..8].copy_from_slice(&0x0001_0001_u32.to_le_bytes());
+
+        // Offset 12, length 4: Size of each boot information descriptor in the array
+        buf[12..16].copy_from_slice(&(DESC_SIZE as u32).to_le_bytes());
+
+        // Offset 16, length 4: Count of boot information descriptors in the array
+        buf[16..20].copy_from_slice(&(desc_cnt as u32).to_le_bytes());
+
+        // Offset 20, length 4: Offset to array of boot information descriptors
+        buf[20..24].copy_from_slice(&(DESC_ARRAY_OFFSET as u32).to_le_bytes());
+
+        // Offset 24, length 8: Reserved (MBZ)
+        buf[24..32].fill(0);
+
+        // Fill the boot info descriptor array, all offset based from DESC_ARRAY_OFFSET
+        let mut offset = DESC_ARRAY_OFFSET;
+        for desc in descriptors {
+            // Offset 0, length 16: Name of boot information passed to the consumer
+            match &desc.name {
+                BootInfoName::NullTermString(name) => {
+                    assert!(name.is_ascii());
+                    let name_len = name.len().min(15);
+                    buf[offset..offset + name_len].copy_from_slice(&name.as_bytes()[..name_len]);
+                    buf[offset + name_len..offset + 16].fill(0); // Make sure it's null terminated
+                }
+                BootInfoName::Uuid(uuid) => {
+                    buf[offset..offset + 16].copy_from_slice(&uuid.to_bytes_le());
+                }
+            }
+
+            // Offset 16, length 1: Type of boot information passed to the consumer
+            let info_type = match desc.typ {
+                BootInfoType::Std(std_type) => (std_type as u8) & 0b0111_1111,
+                BootInfoType::Impdef(typ) => (0b1 << 7) | typ,
+            };
+            buf[offset + 16] = info_type;
+
+            // Offset 17, length 1: Reserved (MBZ)
+            buf[offset + 17] = 0;
+
+            // Offset 18, length 2: Flags to describe properties of boot information associated with this descriptor
+            let mut flags = 0u16;
+            if let BootInfoName::Uuid(_) = &desc.name {
+                flags |= 0b1;
+            }
+            if let BootInfoContents::Value(_) = desc.contents {
+                flags |= 0b1 << 2;
+            }
+            buf[offset + 18..offset + 20].copy_from_slice(&flags.to_le_bytes());
+
+            // Offset 20, length 4: Size (in bytes) of boot information identified by the Name and Type fields.
+            match desc.contents {
+                BootInfoContents::Address(_) => {
+                    total_size = total_size.checked_add(desc.size as usize).unwrap();
+                }
+                BootInfoContents::Value(_) => {
+                    assert!((1..=8).contains(&desc.size));
+                }
+            }
+            buf[offset + 20..offset + 24].copy_from_slice(&desc.size.to_le_bytes());
+
+            // Offset 24, length 8: Value or address of boot information identified by the Name and Type fields.
+            // Value or address of boot information identified by the Name and Type fields.
+            //
+            // If in the Flags field, bit\[3:2\] = b'0,
+            // * The address has the same attributes as the boot information blob address described in
+            //   5.4.3 Boot information address.
+            // * Size field contains the length (in bytes) of boot information at the specified address.
+            //
+            // If in the Flags field, bit\[3:2\] = b’1,
+            // * Size field contains the exact size of the value specified in this field.
+            // * Size is >=1 bytes and <= 8 bytes.
+            let content = match desc.contents {
+                BootInfoContents::Address(addr) => addr as u64,
+                BootInfoContents::Value(val) => val,
+            };
+            buf[offset + 24..offset + 32].copy_from_slice(&content.to_le_bytes());
+
+            offset += DESC_SIZE;
+        }
+
+        // TODO: add padding size between boot information referenced by the descriptors
+
+        // Offset 8, length 4: Size of boot information blob spanning contiguous memory
+        assert!(buf.len() <= u32::MAX as usize);
+        assert!(total_size <= buf.len());
+        buf[8..12].copy_from_slice(&(total_size as u32).to_le_bytes());
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+
+    use uuid::uuid;
+
+    #[test]
+    fn boot_info() {
+        let desc1 = BootInfoDescriptor {
+            name: BootInfoName::NullTermString(&"test1234test1234"),
+            typ: BootInfoType::Impdef(0xab),
+            size: 4,
+            contents: BootInfoContents::Value(0xbeef),
+        };
+
+        let fdt = [0u8; 0xff];
+        let desc2 = BootInfoDescriptor {
+            name: BootInfoName::Uuid(uuid!("12345678-1234-1234-1234-123456789abc")),
+            typ: BootInfoType::Std(BootInfoStdType::Fdt),
+            size: 0xff,
+            contents: BootInfoContents::Address(&fdt as *const u8 as usize),
+        };
+
+        let mut buf = [0u8; 0x1ff];
+        BootInfoDescriptor::create(&[desc1, desc2], &mut buf);
+
+        println!("{:#x?}", &buf[0..0x0f]);
+    }
+}
diff --git a/src/lib.rs b/src/lib.rs
new file mode 100644
index 0000000..ca1190e
--- /dev/null
+++ b/src/lib.rs
@@ -0,0 +1,863 @@
+// SPDX-FileCopyrightText: Copyright 2023 Arm Limited and/or its affiliates <open-source-office@arm.com>
+// SPDX-License-Identifier: MIT OR Apache-2.0
+
+#![cfg_attr(not(test), no_std)]
+
+extern crate alloc;
+
+use alloc::string::String;
+use num_enum::TryFromPrimitive;
+use uuid::Uuid;
+
+pub mod boot_info;
+pub mod memory_management;
+pub mod partition_info;
+
+pub const FFA_PAGE_SIZE: usize = 4096;
+
+#[derive(PartialEq, Clone, Copy)]
+pub enum Instance {
+    SecurePhysical,
+    SecureVirtual(u16),
+}
+
+/// FF-A v1.1, Table 12.2: Error status codes
+#[derive(Debug, Eq, PartialEq, Clone, Copy, TryFromPrimitive)]
+#[repr(i32)]
+pub enum Error {
+    NotSupported = -1,
+    InvalidParameters = -2,
+    NoMemory = -3,
+    Busy = -4,
+    Interrupted = -5,
+    Denied = -6,
+    Retry = -7,
+    Aborted = -8,
+    NoData = -9,
+}
+
+/// FF-A v1.1: Function IDs
+#[derive(Debug, Eq, PartialEq, TryFromPrimitive)]
+#[repr(u32)]
+pub enum FuncId {
+    Error = 0x84000060,
+    Success32 = 0x84000061,
+    Success64 = 0xc4000061,
+    Interrupt = 0x84000062,
+    Version = 0x84000063,
+    Features = 0x84000064,
+    RxAcquire = 0x84000084,
+    RxRelease = 0x84000065,
+    RxTxMap32 = 0x84000066,
+    RxTxMap64 = 0xc4000066,
+    RxTxUnmap = 0x84000067,
+    PartitionInfoGet = 0x84000068,
+    IdGet = 0x84000069,
+    SpmIdGet = 0x84000085,
+    MsgWait = 0x8400006b,
+    Yield = 0x8400006c,
+    Run = 0x8400006d,
+    NormalWorldResume = 0x8400007c,
+    MsgSend2 = 0x84000086,
+    MsgSendDirectReq32 = 0x8400006f,
+    MsgSendDirectReq64 = 0xc400006f,
+    MsgSendDirectResp32 = 0x84000070,
+    MsgSendDirectResp64 = 0xc4000070,
+    MemDonate32 = 0x84000071,
+    MemDonate64 = 0xc4000071,
+    MemLend32 = 0x84000072,
+    MemLend64 = 0xc4000072,
+    MemShare32 = 0x84000073,
+    MemShare64 = 0xc4000073,
+    MemRetrieveReq32 = 0x84000074,
+    MemRetrieveReq64 = 0xc4000074,
+    MemRetrieveResp = 0x84000075,
+    MemRelinquish = 0x84000076,
+    MemReclaim = 0x84000077,
+    MemPermGet32 = 0x84000088,
+    MemPermGet64 = 0xc4000088,
+    MemPermSet32 = 0x84000089,
+    MemPermSet64 = 0xc4000089,
+    ConsoleLog32 = 0x8400008a,
+    ConsoleLog64 = 0xc400008a,
+}
+
+#[derive(Debug, Eq, PartialEq, Clone, Copy)]
+pub enum Interface {
+    Error {
+        target_info: u32,
+        error_code: Error,
+    },
+    Success {
+        target_info: u32,
+        result_regs: [u64; 6],
+        is_32bit: bool,
+    },
+    Interrupt {
+        endpoint_id: u32,
+        interrupt_id: u32,
+    },
+    Version {
+        input_version: u32,
+    },
+    VersionOut {
+        output_version: u32,
+    },
+    Features {
+        feat_id: u32,
+        input_properties: u32,
+    },
+    RxAcquire {
+        vm_id: u32,
+    },
+    RxRelease {
+        vm_id: u32,
+    },
+    RxTxMap {
+        tx_addr: u64,
+        rx_addr: u64,
+        page_cnt: u32,
+        is_32bit: bool,
+    },
+    RxTxUnmap {
+        id: u32,
+    },
+    PartitionInfoGet {
+        uuid: Uuid,
+        flags: u32,
+    },
+    IdGet,
+    SpmIdGet,
+    MsgWait,
+    Yield,
+    Run {
+        target_info: u32,
+    },
+    NormalWorldResume,
+    MsgSend2 {
+        sender_vm_id: u32,
+        flags: u32,
+    },
+    MsgSendDirectReq {
+        src_id: u16,
+        dst_id: u16,
+        flags: u32,
+        args: [u64; 5],
+        is_32bit: bool,
+    },
+    MsgSendDirectResp {
+        src_id: u16,
+        dst_id: u16,
+        flags: u32,
+        args: [u64; 5],
+        is_32bit: bool,
+    },
+    MemDonate {
+        total_len: u32,
+        frag_len: u32,
+        address: u64,
+        page_cnt: u32,
+        is_32bit: bool,
+    },
+    MemLend {
+        total_len: u32,
+        frag_len: u32,
+        address: u64,
+        page_cnt: u32,
+        is_32bit: bool,
+    },
+    MemShare {
+        total_len: u32,
+        frag_len: u32,
+        address: u64,
+        page_cnt: u32,
+        is_32bit: bool,
+    },
+    MemRetrieveReq {
+        total_len: u32,
+        frag_len: u32,
+        address: u64,
+        page_cnt: u32,
+        is_32bit: bool,
+    },
+    MemRetrieveResp {
+        total_len: u32,
+        frag_len: u32,
+    },
+    MemRelinquish,
+    MemReclaim {
+        handle: memory_management::Handle,
+        flags: u32,
+    },
+    MemPermGet {
+        base_addr: u64,
+        is_32bit: bool,
+    },
+    MemPermSet {
+        base_addr: u64,
+        page_cnt: u32,
+        mem_perm: u32,
+        is_32bit: bool,
+    },
+    ConsoleLog {
+        char_cnt: u32,
+        char_lists: [u64; 6],
+        is_32bit: bool,
+    },
+}
+
+impl TryFrom<[u64; 8]> for Interface {
+    type Error = ();
+
+    fn try_from(regs: [u64; 8]) -> Result<Self, ()> {
+        let fid = FuncId::try_from(regs[0] as u32).unwrap();
+
+        let msg = match fid {
+            FuncId::Error => Self::Error {
+                target_info: regs[1] as u32,
+                error_code: Error::try_from(regs[2] as i32).unwrap(),
+            },
+            FuncId::Success32 | FuncId::Success64 => {
+                let target_info = regs[1] as u32;
+                let mut result_regs = [regs[2], regs[3], regs[4], regs[5], regs[6], regs[7]];
+                let mut is_32bit = false;
+
+                if fid == FuncId::Success32 {
+                    result_regs[0] &= u32::MAX as u64;
+                    result_regs[1] &= u32::MAX as u64;
+                    result_regs[2] &= u32::MAX as u64;
+                    result_regs[3] &= u32::MAX as u64;
+                    result_regs[4] &= u32::MAX as u64;
+                    result_regs[5] &= u32::MAX as u64;
+                    is_32bit = true;
+                }
+
+                Self::Success {
+                    target_info,
+                    result_regs,
+                    is_32bit,
+                }
+            }
+            FuncId::Interrupt => Self::Interrupt {
+                endpoint_id: regs[1] as u32,
+                interrupt_id: regs[2] as u32,
+            },
+            FuncId::Version => Self::Version {
+                input_version: regs[1] as u32,
+            },
+            FuncId::Features => Self::Features {
+                feat_id: regs[1] as u32,
+                input_properties: regs[2] as u32,
+            },
+            FuncId::RxAcquire => Self::RxAcquire {
+                vm_id: regs[1] as u32,
+            },
+            FuncId::RxRelease => Self::RxRelease {
+                vm_id: regs[1] as u32,
+            },
+            FuncId::RxTxMap32 | FuncId::RxTxMap64 => {
+                let mut tx_addr = regs[1];
+                let mut rx_addr = regs[2];
+                let page_cnt = regs[3] as u32;
+                let mut is_32bit = false;
+
+                if fid == FuncId::RxTxMap32 {
+                    tx_addr &= u32::MAX as u64;
+                    rx_addr &= u32::MAX as u64;
+                    is_32bit = true;
+                }
+
+                Self::RxTxMap {
+                    tx_addr,
+                    rx_addr,
+                    page_cnt,
+                    is_32bit,
+                }
+            }
+            FuncId::RxTxUnmap => Self::RxTxUnmap { id: regs[1] as u32 },
+            FuncId::PartitionInfoGet => {
+                let uuid_words = [
+                    regs[1] as u32,
+                    regs[2] as u32,
+                    regs[3] as u32,
+                    regs[4] as u32,
+                ];
+                let mut bytes: [u8; 16] = [0; 16];
+                for (i, b) in uuid_words.iter().flat_map(|w| w.to_le_bytes()).enumerate() {
+                    bytes[i] = b;
+                }
+                Self::PartitionInfoGet {
+                    uuid: Uuid::from_bytes(bytes),
+                    flags: regs[5] as u32,
+                }
+            }
+            FuncId::IdGet => Self::IdGet,
+            FuncId::SpmIdGet => Self::SpmIdGet,
+            FuncId::MsgWait => Self::MsgWait,
+            FuncId::Yield => Self::Yield,
+            FuncId::Run => Self::Run {
+                target_info: regs[1] as u32,
+            },
+            FuncId::NormalWorldResume => Self::NormalWorldResume,
+            FuncId::MsgSend2 => Self::MsgSend2 {
+                sender_vm_id: regs[1] as u32,
+                flags: regs[2] as u32,
+            },
+            FuncId::MsgSendDirectReq32 | FuncId::MsgSendDirectReq64 => {
+                let src_id = (regs[1] >> 16) as u16;
+                let dst_id = regs[1] as u16;
+                let flags = regs[2] as u32;
+                let mut args = [regs[3], regs[4], regs[5], regs[6], regs[7]];
+                let mut is_32bit = false;
+
+                if fid == FuncId::MsgSendDirectReq32 {
+                    args[0] &= u32::MAX as u64;
+                    args[1] &= u32::MAX as u64;
+                    args[2] &= u32::MAX as u64;
+                    args[3] &= u32::MAX as u64;
+                    args[4] &= u32::MAX as u64;
+                    is_32bit = true;
+                }
+
+                Self::MsgSendDirectReq {
+                    src_id,
+                    dst_id,
+                    flags,
+                    args,
+                    is_32bit,
+                }
+            }
+            FuncId::MsgSendDirectResp32 | FuncId::MsgSendDirectResp64 => {
+                let src_id = (regs[1] >> 16) as u16;
+                let dst_id = regs[1] as u16;
+                let flags = regs[2] as u32;
+                let mut args = [regs[3], regs[4], regs[5], regs[6], regs[7]];
+                let mut is_32bit = false;
+
+                if fid == FuncId::MsgSendDirectResp32 {
+                    args[0] &= u32::MAX as u64;
+                    args[1] &= u32::MAX as u64;
+                    args[2] &= u32::MAX as u64;
+                    args[3] &= u32::MAX as u64;
+                    args[4] &= u32::MAX as u64;
+                    is_32bit = true;
+                }
+
+                Self::MsgSendDirectResp {
+                    src_id,
+                    dst_id,
+                    flags,
+                    args,
+                    is_32bit,
+                }
+            }
+            FuncId::MemDonate32 | FuncId::MemDonate64 => {
+                let total_len = regs[1] as u32;
+                let frag_len = regs[2] as u32;
+                let mut address = regs[3];
+                let page_cnt = regs[4] as u32;
+                let mut is_32bit = false;
+
+                if fid == FuncId::MemDonate32 {
+                    address &= u32::MAX as u64;
+                    is_32bit = true;
+                }
+
+                Self::MemDonate {
+                    total_len,
+                    frag_len,
+                    address,
+                    page_cnt,
+                    is_32bit,
+                }
+            }
+            FuncId::MemLend32 | FuncId::MemLend64 => {
+                let total_len = regs[1] as u32;
+                let frag_len = regs[2] as u32;
+                let mut address = regs[3];
+                let page_cnt = regs[4] as u32;
+                let mut is_32bit = false;
+
+                if fid == FuncId::MemLend32 {
+                    address &= u32::MAX as u64;
+                    is_32bit = true;
+                }
+
+                Self::MemLend {
+                    total_len,
+                    frag_len,
+                    address,
+                    page_cnt,
+                    is_32bit,
+                }
+            }
+            FuncId::MemShare32 | FuncId::MemShare64 => {
+                let total_len = regs[1] as u32;
+                let frag_len = regs[2] as u32;
+                let mut address = regs[3];
+                let page_cnt = regs[4] as u32;
+                let mut is_32bit = false;
+
+                if fid == FuncId::MemShare32 {
+                    address &= u32::MAX as u64;
+                    is_32bit = true;
+                }
+
+                Self::MemShare {
+                    total_len,
+                    frag_len,
+                    address,
+                    page_cnt,
+                    is_32bit,
+                }
+            }
+            FuncId::MemRetrieveReq32 | FuncId::MemRetrieveReq64 => {
+                let total_len = regs[1] as u32;
+                let frag_len = regs[2] as u32;
+                let mut address = regs[3];
+                let page_cnt = regs[4] as u32;
+                let mut is_32bit = false;
+
+                if fid == FuncId::MemRetrieveReq32 {
+                    address &= u32::MAX as u64;
+                    is_32bit = true;
+                }
+
+                Self::MemRetrieveReq {
+                    total_len,
+                    frag_len,
+                    address,
+                    page_cnt,
+                    is_32bit,
+                }
+            }
+            FuncId::MemRetrieveResp => Self::MemRetrieveResp {
+                total_len: regs[1] as u32,
+                frag_len: regs[2] as u32,
+            },
+            FuncId::MemRelinquish => Self::MemRelinquish,
+            FuncId::MemReclaim => Self::MemReclaim {
+                handle: memory_management::Handle::from([regs[1] as u32, regs[2] as u32]),
+                flags: regs[3] as u32,
+            },
+            FuncId::MemPermGet32 | FuncId::MemPermGet64 => {
+                let mut base_addr = regs[1];
+                let mut is_32bit = false;
+
+                if fid == FuncId::MemPermGet32 {
+                    base_addr &= u32::MAX as u64;
+                    is_32bit = true;
+                }
+
+                Self::MemPermGet {
+                    base_addr,
+                    is_32bit,
+                }
+            }
+            FuncId::MemPermSet32 | FuncId::MemPermSet64 => {
+                let mut base_addr = regs[1];
+                let page_cnt = regs[2] as u32;
+                let mem_perm = regs[3] as u32;
+                let mut is_32bit = false;
+
+                if fid == FuncId::MemPermSet32 {
+                    base_addr &= u32::MAX as u64;
+                    is_32bit = true;
+                }
+
+                Self::MemPermSet {
+                    base_addr,
+                    page_cnt,
+                    mem_perm,
+                    is_32bit,
+                }
+            }
+            FuncId::ConsoleLog32 | FuncId::ConsoleLog64 => {
+                let char_cnt = regs[1] as u32;
+                let mut char_lists = [regs[2], regs[3], regs[4], regs[5], regs[6], regs[7]];
+                let mut is_32bit = false;
+
+                if fid == FuncId::ConsoleLog32 {
+                    char_lists[0] &= u32::MAX as u64;
+                    char_lists[1] &= u32::MAX as u64;
+                    char_lists[2] &= u32::MAX as u64;
+                    char_lists[3] &= u32::MAX as u64;
+                    char_lists[4] &= u32::MAX as u64;
+                    char_lists[5] &= u32::MAX as u64;
+                    is_32bit = true;
+                }
+
+                Self::ConsoleLog {
+                    char_cnt,
+                    char_lists,
+                    is_32bit,
+                }
+            }
+        };
+
+        Ok(msg)
+    }
+}
+
+impl Interface {
+    pub fn copy_to_array(&self, a: &mut [u64; 8]) {
+        a.fill(0);
+
+        match *self {
+            Interface::Error {
+                target_info,
+                error_code,
+            } => {
+                a[0] = FuncId::Error as u64;
+                a[1] = target_info as u64;
+                a[2] = error_code as u32 as u64;
+            }
+            Interface::Success {
+                target_info,
+                result_regs,
+                is_32bit,
+            } => {
+                a[1] = target_info as u64;
+                if is_32bit {
+                    a[0] = FuncId::Success32 as u64;
+                    a[2] = result_regs[0] & u32::MAX as u64;
+                    a[3] = result_regs[1] & u32::MAX as u64;
+                    a[4] = result_regs[2] & u32::MAX as u64;
+                    a[5] = result_regs[3] & u32::MAX as u64;
+                    a[6] = result_regs[4] & u32::MAX as u64;
+                    a[7] = result_regs[5] & u32::MAX as u64;
+                } else {
+                    a[0] = FuncId::Success64 as u64;
+                    a[2] = result_regs[0];
+                    a[3] = result_regs[1];
+                    a[4] = result_regs[2];
+                    a[5] = result_regs[3];
+                    a[6] = result_regs[4];
+                    a[7] = result_regs[5];
+                }
+            }
+            Interface::Interrupt {
+                endpoint_id,
+                interrupt_id,
+            } => {
+                a[0] = FuncId::Interrupt as u64;
+                a[1] = endpoint_id as u64;
+                a[2] = interrupt_id as u64;
+            }
+            Interface::Version { input_version } => {
+                a[0] = FuncId::Version as u64;
+                a[1] = input_version as u64;
+            }
+            Interface::VersionOut { output_version } => {
+                a[0] = output_version as u64;
+            }
+            Interface::Features {
+                feat_id,
+                input_properties,
+            } => {
+                a[0] = FuncId::Features as u64;
+                a[1] = feat_id as u64;
+                a[2] = input_properties as u64;
+            }
+            Interface::RxAcquire { vm_id } => {
+                a[0] = FuncId::RxAcquire as u64;
+                a[1] = vm_id as u64;
+            }
+            Interface::RxRelease { vm_id } => {
+                a[0] = FuncId::RxRelease as u64;
+                a[1] = vm_id as u64;
+            }
+            Interface::RxTxMap {
+                tx_addr,
+                rx_addr,
+                page_cnt,
+                is_32bit,
+            } => {
+                a[3] = page_cnt as u64;
+                if is_32bit {
+                    a[0] = FuncId::RxTxMap32 as u64;
+                    a[1] = tx_addr & u32::MAX as u64;
+                    a[2] = rx_addr & u32::MAX as u64;
+                } else {
+                    a[0] = FuncId::RxTxMap64 as u64;
+                    a[1] = tx_addr;
+                    a[2] = rx_addr;
+                }
+            }
+            Interface::RxTxUnmap { id } => {
+                a[0] = FuncId::RxTxUnmap as u64;
+                a[1] = id as u64;
+            }
+            Interface::PartitionInfoGet { uuid, flags } => {
+                let bytes = uuid.to_bytes_le();
+                a[0] = FuncId::PartitionInfoGet as u64;
+                a[1] = u32::from_le_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]) as u64;
+                a[2] = u32::from_le_bytes([bytes[4], bytes[5], bytes[6], bytes[7]]) as u64;
+                a[3] = u32::from_le_bytes([bytes[8], bytes[9], bytes[10], bytes[11]]) as u64;
+                a[4] = u32::from_le_bytes([bytes[12], bytes[13], bytes[14], bytes[15]]) as u64;
+                a[5] = flags as u64;
+            }
+            Interface::IdGet => a[0] = FuncId::IdGet as u64,
+            Interface::SpmIdGet => a[0] = FuncId::SpmIdGet as u64,
+            Interface::MsgWait => a[0] = FuncId::MsgWait as u64,
+            Interface::Yield => a[0] = FuncId::Yield as u64,
+            Interface::Run { target_info } => {
+                a[0] = FuncId::Run as u64;
+                a[1] = target_info as u64;
+            }
+            Interface::NormalWorldResume => a[0] = FuncId::NormalWorldResume as u64,
+            Interface::MsgSend2 {
+                sender_vm_id,
+                flags,
+            } => {
+                a[0] = FuncId::MsgSend2 as u64;
+                a[1] = sender_vm_id as u64;
+                a[2] = flags as u64;
+            }
+            Interface::MsgSendDirectReq {
+                src_id,
+                dst_id,
+                flags,
+                args,
+                is_32bit,
+            } => {
+                a[1] = (src_id as u64) << 16 | dst_id as u64;
+                a[2] = flags as u64;
+                if is_32bit {
+                    a[0] = FuncId::MsgSendDirectReq32 as u64;
+                    a[3] = args[0] & u32::MAX as u64;
+                    a[4] = args[1] & u32::MAX as u64;
+                    a[5] = args[2] & u32::MAX as u64;
+                    a[6] = args[3] & u32::MAX as u64;
+                    a[7] = args[4] & u32::MAX as u64;
+                } else {
+                    a[0] = FuncId::MsgSendDirectReq64 as u64;
+                    a[3] = args[0];
+                    a[4] = args[1];
+                    a[5] = args[2];
+                    a[6] = args[3];
+                    a[7] = args[4];
+                }
+            }
+            Interface::MsgSendDirectResp {
+                src_id,
+                dst_id,
+                flags,
+                args,
+                is_32bit,
+            } => {
+                a[1] = (src_id as u64) << 16 | dst_id as u64;
+                a[2] = flags as u64;
+                if is_32bit {
+                    a[0] = FuncId::MsgSendDirectResp32 as u64;
+                    a[3] = args[0] & u32::MAX as u64;
+                    a[4] = args[1] & u32::MAX as u64;
+                    a[5] = args[2] & u32::MAX as u64;
+                    a[6] = args[3] & u32::MAX as u64;
+                    a[7] = args[4] & u32::MAX as u64;
+                } else {
+                    a[0] = FuncId::MsgSendDirectResp64 as u64;
+                    a[3] = args[0];
+                    a[4] = args[1];
+                    a[5] = args[2];
+                    a[6] = args[3];
+                    a[7] = args[4];
+                }
+            }
+            Interface::MemDonate {
+                total_len,
+                frag_len,
+                address,
+                page_cnt,
+                is_32bit,
+            } => {
+                a[1] = total_len as u64;
+                a[2] = frag_len as u64;
+                a[4] = page_cnt as u64;
+                if is_32bit {
+                    a[0] = FuncId::MemDonate32 as u64;
+                    a[3] = address & u32::MAX as u64;
+                } else {
+                    a[0] = FuncId::MemDonate64 as u64;
+                    a[3] = address;
+                }
+            }
+            Interface::MemLend {
+                total_len,
+                frag_len,
+                address,
+                page_cnt,
+                is_32bit,
+            } => {
+                a[1] = total_len as u64;
+                a[2] = frag_len as u64;
+                a[4] = page_cnt as u64;
+                if is_32bit {
+                    a[0] = FuncId::MemLend32 as u64;
+                    a[3] = address & u32::MAX as u64;
+                } else {
+                    a[0] = FuncId::MemLend64 as u64;
+                    a[3] = address;
+                }
+            }
+            Interface::MemShare {
+                total_len,
+                frag_len,
+                address,
+                page_cnt,
+                is_32bit,
+            } => {
+                a[1] = total_len as u64;
+                a[2] = frag_len as u64;
+                a[4] = page_cnt as u64;
+                if is_32bit {
+                    a[0] = FuncId::MemShare32 as u64;
+                    a[3] = address & u32::MAX as u64;
+                } else {
+                    a[0] = FuncId::MemShare64 as u64;
+                    a[3] = address;
+                }
+            }
+            Interface::MemRetrieveReq {
+                total_len,
+                frag_len,
+                address,
+                page_cnt,
+                is_32bit,
+            } => {
+                a[1] = total_len as u64;
+                a[2] = frag_len as u64;
+                a[4] = page_cnt as u64;
+                if is_32bit {
+                    a[0] = FuncId::MemRetrieveReq32 as u64;
+                    a[3] = address & u32::MAX as u64;
+                } else {
+                    a[0] = FuncId::MemRetrieveReq64 as u64;
+                    a[3] = address;
+                }
+            }
+            Interface::MemRetrieveResp {
+                total_len,
+                frag_len,
+            } => {
+                a[0] = FuncId::MemRetrieveResp as u64;
+                a[1] = total_len as u64;
+                a[2] = frag_len as u64;
+            }
+            Interface::MemRelinquish => a[0] = FuncId::MemRelinquish as u64,
+            Interface::MemReclaim { handle, flags } => {
+                let handle_regs: [u32; 2] = handle.into();
+                a[0] = FuncId::MemReclaim as u64;
+                a[1] = handle_regs[0] as u64;
+                a[2] = handle_regs[1] as u64;
+                a[3] = flags as u64;
+            }
+            Interface::MemPermGet {
+                base_addr,
+                is_32bit,
+            } => {
+                if is_32bit {
+                    a[0] = FuncId::MemPermGet32 as u64;
+                    a[1] = base_addr & u32::MAX as u64;
+                } else {
+                    a[0] = FuncId::MemPermGet64 as u64;
+                    a[1] = base_addr;
+                }
+            }
+            Interface::MemPermSet {
+                base_addr,
+                page_cnt,
+                mem_perm,
+                is_32bit,
+            } => {
+                a[2] = page_cnt as u64;
+                a[3] = mem_perm as u64;
+
+                if is_32bit {
+                    a[0] = FuncId::MemPermSet32 as u64;
+                    a[1] = base_addr & u32::MAX as u64;
+                } else {
+                    a[0] = FuncId::MemPermSet64 as u64;
+                    a[1] = base_addr;
+                }
+            }
+            Interface::ConsoleLog {
+                char_cnt,
+                char_lists,
+                is_32bit,
+            } => {
+                a[1] = char_cnt as u64;
+                if is_32bit {
+                    a[0] = FuncId::ConsoleLog32 as u64;
+                    a[2] = char_lists[0] & u32::MAX as u64;
+                    a[3] = char_lists[1] & u32::MAX as u64;
+                    a[4] = char_lists[2] & u32::MAX as u64;
+                    a[5] = char_lists[3] & u32::MAX as u64;
+                    a[6] = char_lists[4] & u32::MAX as u64;
+                    a[7] = char_lists[5] & u32::MAX as u64;
+                } else {
+                    a[0] = FuncId::ConsoleLog64 as u64;
+                    a[2] = char_lists[0];
+                    a[3] = char_lists[1];
+                    a[4] = char_lists[2];
+                    a[5] = char_lists[3];
+                    a[6] = char_lists[4];
+                    a[7] = char_lists[5];
+                }
+            }
+        }
+    }
+
+    /// Helper function to create an FFA_SUCCESS interface without any arguments
+    pub fn success32_noargs() -> Self {
+        Self::Success {
+            target_info: 0,
+            result_regs: [0, 0, 0, 0, 0, 0],
+            is_32bit: true,
+        }
+    }
+}
+
+#[derive(Clone, Copy, Debug, PartialEq)]
+pub struct Version(pub u16, pub u16);
+
+impl From<u32> for Version {
+    fn from(val: u32) -> Self {
+        Self((val >> 16) as u16, val as u16)
+    }
+}
+
+impl From<Version> for u32 {
+    fn from(v: Version) -> Self {
+        (v.0 as u32) << 16 | v.1 as u32
+    }
+}
+
+pub fn parse_console_log(
+    char_cnt: u32,
+    char_lists: &[u64; 6],
+    is_32bit: bool,
+) -> Result<String, Error> {
+    const CHAR_COUNT_MASK: u32 = 0xff;
+    const LOG_32_MAX_MSG_LEN: usize = 24;
+    const LOG_64_MAX_MSG_LEN: usize = 48;
+
+    let mut msg_bytes = [0u8; LOG_64_MAX_MSG_LEN + 1];
+    let char_count = (char_cnt & CHAR_COUNT_MASK) as usize;
+    let (max_length, reg_size) = if is_32bit {
+        (LOG_32_MAX_MSG_LEN, 4)
+    } else {
+        (LOG_64_MAX_MSG_LEN, 8)
+    };
+
+    if char_count < 1 || char_count > max_length {
+        return Err(Error::InvalidParameters);
+    }
+
+    for i in 0..=5 {
+        msg_bytes[reg_size * i..reg_size * (i + 1)]
+            .copy_from_slice(&char_lists[i].to_le_bytes()[0..reg_size]);
+    }
+
+    String::from_utf8(msg_bytes.to_vec()).map_err(|_| Error::InvalidParameters)
+}
diff --git a/src/memory_management.rs b/src/memory_management.rs
new file mode 100644
index 0000000..f0da51f
--- /dev/null
+++ b/src/memory_management.rs
@@ -0,0 +1,635 @@
+// SPDX-FileCopyrightText: Copyright 2023 Arm Limited and/or its affiliates <open-source-office@arm.com>
+// SPDX-License-Identifier: MIT OR Apache-2.0
+
+use alloc::vec::Vec;
+
+#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
+pub struct Handle(pub u64);
+
+impl From<[u32; 2]> for Handle {
+    fn from(value: [u32; 2]) -> Self {
+        Self((value[1] as u64) << 32 | value[0] as u64)
+    }
+}
+
+impl From<Handle> for [u32; 2] {
+    fn from(value: Handle) -> Self {
+        [value.0 as u32, (value.0 >> 32) as u32]
+    }
+}
+
+impl Handle {
+    pub const INVALID: u64 = 0xffff_ffff_ffff_ffff;
+}
+
+#[derive(Debug, Default, Clone, Copy, PartialEq)]
+#[repr(u16)]
+pub enum Cacheability {
+    #[default]
+    NonCacheable = Self::NON_CACHEABLE << Self::SHIFT,
+    WriteBack = Self::WRITE_BACK << Self::SHIFT,
+}
+
+impl TryFrom<u16> for Cacheability {
+    type Error = ();
+
+    fn try_from(value: u16) -> Result<Self, Self::Error> {
+        match (value >> Self::SHIFT) & Self::MASK {
+            Self::NON_CACHEABLE => Ok(Cacheability::NonCacheable),
+            Self::WRITE_BACK => Ok(Cacheability::WriteBack),
+            _ => Err(()),
+        }
+    }
+}
+
+impl Cacheability {
+    const SHIFT: usize = 2;
+    const MASK: u16 = 0b11;
+    const NON_CACHEABLE: u16 = 0b01;
+    const WRITE_BACK: u16 = 0b11;
+}
+
+#[derive(Debug, Default, Clone, Copy, PartialEq)]
+#[repr(u16)]
+pub enum Shareability {
+    #[default]
+    NonShareable = Self::NON_SHAREABLE << Self::SHIFT,
+    Outer = Self::OUTER << Self::SHIFT,
+    Inner = Self::INNER << Self::SHIFT,
+}
+
+impl TryFrom<u16> for Shareability {
+    type Error = ();
+
+    fn try_from(value: u16) -> Result<Self, Self::Error> {
+        match (value >> Self::SHIFT) & Self::MASK {
+            Self::NON_SHAREABLE => Ok(Self::NonShareable),
+            Self::OUTER => Ok(Self::Outer),
+            Self::INNER => Ok(Self::Inner),
+            _ => Err(()),
+        }
+    }
+}
+
+impl Shareability {
+    const SHIFT: usize = 0;
+    const MASK: u16 = 0b11;
+    const NON_SHAREABLE: u16 = 0b00;
+    const OUTER: u16 = 0b10;
+    const INNER: u16 = 0b11;
+}
+
+#[derive(Debug, Default, Clone, Copy)]
+#[repr(u16)]
+pub enum DeviceMemAttributes {
+    #[default]
+    DevnGnRnE = Self::DEV_NGNRNE << Self::SHIFT,
+    DevnGnRE = Self::DEV_NGNRE << Self::SHIFT,
+    DevnGRE = Self::DEV_NGRE << Self::SHIFT,
+    DevGRE = Self::DEV_GRE << Self::SHIFT,
+}
+
+impl TryFrom<u16> for DeviceMemAttributes {
+    type Error = ();
+
+    fn try_from(value: u16) -> Result<Self, Self::Error> {
+        // TODO: sanity check if it's device memory
+        match (value >> Self::SHIFT) & Self::MASK {
+            Self::DEV_NGNRNE => Ok(Self::DevnGnRnE),
+            Self::DEV_NGNRE => Ok(Self::DevnGnRE),
+            Self::DEV_NGRE => Ok(Self::DevnGRE),
+            Self::DEV_GRE => Ok(Self::DevGRE),
+            _ => Err(()),
+        }
+    }
+}
+
+impl DeviceMemAttributes {
+    const SHIFT: usize = 2;
+    const MASK: u16 = 0b11;
+    const DEV_NGNRNE: u16 = 0b00;
+    const DEV_NGNRE: u16 = 0b01;
+    const DEV_NGRE: u16 = 0b10;
+    const DEV_GRE: u16 = 0b11;
+}
+
+#[derive(Debug, Default, Clone, Copy)]
+pub enum MemType {
+    #[default]
+    NotSpecified,
+    Device(DeviceMemAttributes),
+    Normal {
+        cacheability: Cacheability,
+        shareability: Shareability,
+    },
+}
+
+impl TryFrom<u16> for MemType {
+    type Error = ();
+
+    fn try_from(value: u16) -> Result<Self, Self::Error> {
+        match (value >> Self::SHIFT) & Self::MASK {
+            Self::NOT_SPECIFIED => Ok(Self::NotSpecified),
+            Self::DEVICE => Ok(Self::Device(DeviceMemAttributes::try_from(value)?)),
+            Self::NORMAL => Ok(Self::Normal {
+                cacheability: Cacheability::try_from(value)?,
+                shareability: Shareability::try_from(value)?,
+            }),
+            _ => Err(()),
+        }
+    }
+}
+
+impl From<MemType> for u16 {
+    fn from(value: MemType) -> Self {
+        match value {
+            MemType::NotSpecified => MemType::NOT_SPECIFIED << MemType::SHIFT,
+            MemType::Device(attr) => attr as u16 | MemType::DEVICE << MemType::SHIFT,
+            MemType::Normal {
+                cacheability,
+                shareability,
+            } => cacheability as u16 | shareability as u16 | MemType::NORMAL << MemType::SHIFT,
+        }
+    }
+}
+
+impl MemType {
+    const SHIFT: usize = 4;
+    const MASK: u16 = 0b11;
+    const NOT_SPECIFIED: u16 = 0b00;
+    const DEVICE: u16 = 0b01;
+    const NORMAL: u16 = 0b10;
+}
+
+#[derive(Debug, Default, Clone, Copy, PartialEq)]
+#[repr(u16)]
+pub enum MemRegionSecurity {
+    #[default]
+    Secure = Self::SECURE << Self::SHIFT,
+    NonSecure = Self::NON_SECURE << Self::SHIFT,
+}
+
+impl TryFrom<u16> for MemRegionSecurity {
+    type Error = ();
+
+    fn try_from(value: u16) -> Result<Self, Self::Error> {
+        match (value >> Self::SHIFT) & Self::MASK {
+            Self::SECURE => Ok(Self::Secure),
+            Self::NON_SECURE => Ok(Self::NonSecure),
+            _ => Err(()),
+        }
+    }
+}
+
+impl MemRegionSecurity {
+    const SHIFT: usize = 6;
+    const MASK: u16 = 0b1;
+    const SECURE: u16 = 0b0;
+    const NON_SECURE: u16 = 0b1;
+}
+
+/// FF-A v1.1: Table 10.18: Memory region attributes descriptor
+#[derive(Debug, Default, Clone, Copy)]
+pub struct MemRegionAttributes {
+    pub security: MemRegionSecurity,
+    pub mem_type: MemType,
+}
+
+impl TryFrom<u16> for MemRegionAttributes {
+    type Error = ();
+
+    fn try_from(value: u16) -> Result<Self, Self::Error> {
+        // bits[15:7]: Reserved (MBZ)
+        assert_eq!(value >> 7, 0);
+        Ok(Self {
+            security: MemRegionSecurity::try_from(value)?,
+            mem_type: MemType::try_from(value)?,
+        })
+    }
+}
+
+impl From<MemRegionAttributes> for u16 {
+    fn from(value: MemRegionAttributes) -> Self {
+        value.security as u16 | u16::from(value.mem_type)
+    }
+}
+
+#[derive(Debug, Default, Clone, Copy)]
+#[repr(u8)]
+pub enum InstuctionAccessPerm {
+    #[default]
+    NotSpecified = Self::NOT_SPECIFIED << Self::SHIFT,
+    NotExecutable = Self::NOT_EXECUTABLE << Self::SHIFT,
+    Executable = Self::EXECUTABLE << Self::SHIFT,
+}
+
+impl TryFrom<u8> for InstuctionAccessPerm {
+    type Error = ();
+
+    fn try_from(value: u8) -> Result<Self, Self::Error> {
+        match (value >> Self::SHIFT) & Self::MASK {
+            Self::NOT_SPECIFIED => Ok(Self::NotSpecified),
+            Self::NOT_EXECUTABLE => Ok(Self::NotExecutable),
+            Self::EXECUTABLE => Ok(Self::Executable),
+            _ => Err(()),
+        }
+    }
+}
+
+impl InstuctionAccessPerm {
+    const SHIFT: usize = 2;
+    const MASK: u8 = 0b11;
+    const NOT_SPECIFIED: u8 = 0b00;
+    const NOT_EXECUTABLE: u8 = 0b01;
+    const EXECUTABLE: u8 = 0b10;
+}
+
+#[derive(Debug, Default, Clone, Copy)]
+#[repr(u8)]
+pub enum DataAccessPerm {
+    #[default]
+    NotSpecified = Self::NOT_SPECIFIED << Self::SHIFT,
+    ReadOnly = Self::READ_ONLY << Self::SHIFT,
+    ReadWrite = Self::READ_WRITE << Self::SHIFT,
+}
+
+impl TryFrom<u8> for DataAccessPerm {
+    type Error = ();
+
+    fn try_from(value: u8) -> Result<Self, Self::Error> {
+        match (value >> Self::SHIFT) & Self::MASK {
+            Self::NOT_SPECIFIED => Ok(Self::NotSpecified),
+            Self::READ_ONLY => Ok(Self::ReadOnly),
+            Self::READ_WRITE => Ok(Self::ReadWrite),
+            _ => Err(()),
+        }
+    }
+}
+
+impl DataAccessPerm {
+    const SHIFT: usize = 0;
+    const MASK: u8 = 0b11;
+    const NOT_SPECIFIED: u8 = 0b00;
+    const READ_ONLY: u8 = 0b01;
+    const READ_WRITE: u8 = 0b10;
+}
+
+/// FF-A v1.1: Table 10.15: Memory access permissions descriptor
+#[derive(Debug, Default, Clone, Copy)]
+pub struct MemAccessPermDesc {
+    pub endpoint_id: u16,
+    pub instr_access: InstuctionAccessPerm,
+    pub data_access: DataAccessPerm,
+    pub flags: u8, // TODO
+}
+
+/// FF-A v1.1 Table 10.16: Endpoint memory access descriptor
+#[derive(Debug, Default, Clone, Copy)]
+pub struct EndpointMemAccessDesc {
+    pub mem_access_perm: MemAccessPermDesc,
+    pub composite_offset: u32,
+}
+
+impl EndpointMemAccessDesc {
+    const SIZE: usize = 16;
+}
+
+/// FF-A v1.1 Table 10.21: Flags usage in FFA_MEM_DONATE, FFA_MEM_LEND and FFA_MEM_SHARE ABIs
+/// FF-A v1.1 Table 10.22: Flags usage in FFA_MEM_RETRIEVE_REQ ABI
+/// FF-A v1.1 Table 10.23: Flags usage in FFA_MEM_RETRIEVE_RESP ABI
+#[derive(Debug, Default, Clone, Copy)]
+pub struct MemTransactionFlags(pub u32); // TODO: use bitflags?
+
+#[allow(dead_code)]
+impl MemTransactionFlags {
+    const MEM_SHARE_MASK: u32 = 0b11;
+    const MEM_RETRIEVE_REQ_MASK: u32 = 0b11_1111_1111;
+    const MEM_RETRIEVE_RESP_MASK: u32 = 0b1_1111;
+    const ZERO_MEMORY: u32 = 0b1;
+    const TIME_SLICING: u32 = 0b1 << 1;
+    const ZERO_AFTER_RELINQ: u32 = 0b1 << 2;
+    pub const TYPE_SHARE: u32 = 0b01 << 3;
+    const TYPE_LEND: u32 = 0b10 << 3;
+    const TYPE_DONATE: u32 = 0b11 << 3;
+    const ALIGN_HINT_MASK: u32 = 0b1111 << 5;
+    const HINT_VALID: u32 = 0b1 << 9;
+}
+
+/// FF-A v1.1: Table 10.20: Memory transaction descriptor
+#[derive(Debug, Default)]
+pub struct MemTransactionDesc {
+    pub sender_id: u16,
+    pub mem_region_attr: MemRegionAttributes,
+    pub flags: MemTransactionFlags,
+    pub handle: Handle,
+    pub tag: u64, // TODO
+    pub ep_access_descs: Vec<EndpointMemAccessDesc>,
+}
+
+/// FF-A v1.1 Table 10.13: Composite memory region descriptor
+#[derive(Debug, Default)]
+pub struct CompositeMemRegionDesc {
+    pub total_page_cnt: u32,
+    pub constituents: Vec<ConstituentMemRegionDesc>,
+}
+
+impl CompositeMemRegionDesc {
+    const CONSTITUENT_ARRAY_OFFSET: usize = 16;
+}
+
+/// FF-A v1.1 Table 10.14: Constituent memory region descriptor
+#[derive(Debug, Default, Clone, Copy)]
+pub struct ConstituentMemRegionDesc {
+    pub address: u64,
+    pub page_cnt: u32,
+}
+
+impl ConstituentMemRegionDesc {
+    const SIZE: usize = 16;
+}
+
+impl MemTransactionDesc {
+    // Must be 16 byte aligned
+    const ENDPOINT_MEM_ACCESS_DESC_OFFSET: usize = 48;
+
+    pub fn create(&self, composite_desc: &CompositeMemRegionDesc, buf: &mut [u8]) -> usize {
+        let mem_access_desc_cnt = self.ep_access_descs.len();
+        let composite_offset = (Self::ENDPOINT_MEM_ACCESS_DESC_OFFSET
+            + mem_access_desc_cnt * EndpointMemAccessDesc::SIZE)
+            .next_multiple_of(8);
+
+        // Offset 0, length 2: ID of the Owner endpoint.
+        buf[0..2].copy_from_slice(&self.sender_id.to_le_bytes());
+
+        // Offset 2, length 2: Memory region attributes
+        let mem_reg_attr = u16::from(self.mem_region_attr);
+        buf[2..4].copy_from_slice(&mem_reg_attr.to_le_bytes());
+
+        // Offset 4, length 4: Flags
+        buf[4..8].copy_from_slice(&self.flags.0.to_le_bytes());
+
+        // Offset 8, length 8: Handle
+        buf[8..16].copy_from_slice(&self.handle.0.to_le_bytes());
+
+        // Offset 16, length 8: Tag
+        buf[16..24].copy_from_slice(&self.tag.to_le_bytes());
+
+        // Offset 24, length 4: Size of each endpoint memory access descriptor in the array.
+        buf[24..28].copy_from_slice(&(EndpointMemAccessDesc::SIZE as u32).to_le_bytes());
+
+        // Offset 28, length 4: Count of endpoint memory access descriptors.
+        buf[28..32].copy_from_slice(&(mem_access_desc_cnt as u32).to_le_bytes());
+
+        // Offset 32, length 4: 16-byte aligned offset from the base address of this descriptor to the first element of the Endpoint memory access descriptor array.
+        buf[32..36].copy_from_slice(&(Self::ENDPOINT_MEM_ACCESS_DESC_OFFSET as u32).to_le_bytes());
+
+        let mut offset = Self::ENDPOINT_MEM_ACCESS_DESC_OFFSET;
+        for desc in &self.ep_access_descs {
+            // Offset 0, length 4: Memory access permissions descriptor
+            // Offset 0, length 2: 16-bit ID of endpoint to which the memory access permissions apply
+            buf[offset..offset + 2]
+                .copy_from_slice(&desc.mem_access_perm.endpoint_id.to_le_bytes());
+
+            // Offset 2, length 1: Permissions used to access a memory region.
+            buf[offset + 2] =
+                desc.mem_access_perm.data_access as u8 | desc.mem_access_perm.instr_access as u8;
+
+            // Offset 3, length 1: ABI specific flags
+            buf[offset + 2] = desc.mem_access_perm.flags;
+
+            // Offset 4, length 4: Offset to the composite memory region descriptor to which the endpoint access permissions apply
+            buf[offset + 4..offset + 8].copy_from_slice(&(composite_offset as u32).to_le_bytes());
+
+            // Offset 8, length 8: Reserved (MBZ)
+            buf[offset + 8..offset + 16].fill(0);
+
+            offset += EndpointMemAccessDesc::SIZE;
+        }
+
+        offset = composite_offset;
+        // Offset 0, length 4: Size of the memory region described as the count of 4K pages
+        buf[offset..offset + 4].copy_from_slice(&composite_desc.total_page_cnt.to_le_bytes());
+
+        // Offset 4, length 4: Count of address ranges specified using constituent memory region descriptors
+        let addr_range_cnt = composite_desc.constituents.len() as u32;
+        buf[offset + 4..offset + 8].copy_from_slice(&addr_range_cnt.to_le_bytes());
+
+        // Offset 8, length 8: Reserved (MBZ)
+        buf[offset + 8..offset + 16].fill(0);
+
+        offset = composite_offset + CompositeMemRegionDesc::CONSTITUENT_ARRAY_OFFSET;
+        for constituent in &composite_desc.constituents {
+            // Offset 0, length 8: Base VA, PA or IPA of constituent memory region aligned to the page size (4K) granularity.
+            buf[offset..offset + 8].copy_from_slice(&constituent.address.to_le_bytes());
+
+            // Offset 8, length 4: Number of 4K pages in constituent memory region
+            buf[offset + 8..offset + 12].copy_from_slice(&constituent.page_cnt.to_le_bytes());
+
+            // Offset 12, length 4: Reserved (MBZ)
+            buf[offset + 12..offset + 16].fill(0);
+
+            offset += ConstituentMemRegionDesc::SIZE;
+        }
+
+        offset
+    }
+
+    pub fn parse(
+        &mut self,
+        composite_desc: &mut CompositeMemRegionDesc,
+        buf: &[u8],
+    ) -> Result<(), ()> {
+        // Offset 0, length 2: ID of the Owner endpoint.
+        self.sender_id = u16::from_le_bytes(buf[0..2].try_into().unwrap());
+
+        // Offset 2, length 2: Memory region attributes
+        let mem_attr = u16::from_le_bytes(buf[2..4].try_into().unwrap());
+        self.mem_region_attr = MemRegionAttributes::try_from(mem_attr)?;
+
+        // Offset 4, length 4: Flags
+        self.flags.0 = u32::from_le_bytes(buf[4..8].try_into().unwrap()); // TODO: validate
+
+        // Offset 8, length 8: Handle
+        self.handle.0 = u64::from_le_bytes(buf[8..16].try_into().unwrap());
+
+        // Offset 16, length 8: Tag
+        self.tag = u64::from_le_bytes(buf[16..24].try_into().unwrap());
+
+        // Offset 24, length 4: Size of each endpoint memory access descriptor in the array.
+        let endpoint_mem_access_desc_size = u32::from_le_bytes(buf[24..28].try_into().unwrap());
+        assert_eq!(
+            EndpointMemAccessDesc::SIZE,
+            endpoint_mem_access_desc_size as usize
+        );
+
+        // Offset 28, length 4: Count of endpoint memory access descriptors.
+        let endpoint_mem_access_desc_cnt = u32::from_le_bytes(buf[28..32].try_into().unwrap());
+
+        // Offset 32, length 4: 16-byte aligned offset from the base address of this descriptor to
+        // the first element of the Endpoint memory access descriptor array.
+        let endpoint_mem_access_desc_offset = u32::from_le_bytes(buf[32..36].try_into().unwrap());
+
+        assert!(
+            endpoint_mem_access_desc_offset
+                + endpoint_mem_access_desc_cnt * endpoint_mem_access_desc_size
+                <= buf.len() as u32
+        );
+
+        let mut composite_offset = 0;
+        let mut offset = endpoint_mem_access_desc_offset as usize;
+        for _ in 0..endpoint_mem_access_desc_cnt {
+            let mut desc = EndpointMemAccessDesc::default();
+            desc.mem_access_perm.endpoint_id =
+                u16::from_le_bytes(buf[offset..offset + 2].try_into().unwrap());
+
+            desc.mem_access_perm.instr_access = InstuctionAccessPerm::try_from(buf[offset + 2])?;
+            desc.mem_access_perm.data_access = DataAccessPerm::try_from(buf[offset + 2])?;
+            desc.mem_access_perm.flags = buf[offset + 3];
+            desc.composite_offset =
+                u32::from_le_bytes(buf[offset + 4..offset + 8].try_into().unwrap());
+            // TODO: different composite offsets?
+            composite_offset = desc.composite_offset as usize;
+
+            self.ep_access_descs.push(desc);
+
+            offset += endpoint_mem_access_desc_size as usize;
+        }
+
+        if self.handle != Handle(0) || composite_offset == 0 {
+            return Ok(());
+        }
+
+        composite_desc.total_page_cnt = u32::from_le_bytes(
+            buf[composite_offset..composite_offset + 4]
+                .try_into()
+                .unwrap(),
+        );
+
+        let addr_range_cnt = u32::from_le_bytes(
+            buf[composite_offset + 4..composite_offset + 8]
+                .try_into()
+                .unwrap(),
+        );
+
+        offset = composite_offset + CompositeMemRegionDesc::CONSTITUENT_ARRAY_OFFSET;
+        let mut total_page_cnt = 0;
+        for _ in 0..addr_range_cnt {
+            let desc = ConstituentMemRegionDesc {
+                address: u64::from_le_bytes(buf[offset..offset + 8].try_into().unwrap()),
+                page_cnt: u32::from_le_bytes(buf[offset + 8..offset + 12].try_into().unwrap()),
+            };
+            total_page_cnt += desc.page_cnt;
+
+            composite_desc.constituents.push(desc);
+
+            offset += ConstituentMemRegionDesc::SIZE;
+        }
+
+        assert_eq!(total_page_cnt, composite_desc.total_page_cnt);
+
+        Ok(())
+    }
+}
+
+/// FF-A v1.1 Table 16.25: Descriptor to relinquish a memory region
+#[derive(Debug, Default)]
+pub struct MemRelinquishDesc {
+    pub handle: Handle,
+    pub flags: u32,
+    pub endpoints: Vec<u16>,
+}
+
+impl MemRelinquishDesc {
+    const ENDPOINT_ARRAY_OFFSET: usize = 16;
+
+    pub fn parse(&mut self, buf: &[u8]) -> Result<(), ()> {
+        // Offset 0, length 8: Handle
+        self.handle.0 = u64::from_le_bytes(buf[0..8].try_into().unwrap());
+
+        // Offset 8, length 4: Flags
+        self.flags = u32::from_le_bytes(buf[8..12].try_into().unwrap()); // TODO: validate
+
+        // Offset 12, length 4: Count of endpoint ID entries in the Endpoint array
+        let endpoint_cnt = u32::from_le_bytes(buf[12..16].try_into().unwrap());
+
+        let mut offset = MemRelinquishDesc::ENDPOINT_ARRAY_OFFSET;
+        for _ in 0..endpoint_cnt as usize {
+            let endpoint = u16::from_le_bytes(buf[offset..offset + 2].try_into().unwrap());
+            self.endpoints.push(endpoint);
+            offset += 2;
+        }
+
+        Ok(())
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+
+    #[allow(dead_code)]
+    const MEM_SHARE_FROM_SP1: &[u8] = &[
+        0x05, 0x80, 0x2f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x01, 0x00,
+        0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x03, 0x80, 0x02, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x10, 0x40, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    ];
+
+    #[allow(dead_code)]
+    const MEM_SHARE_FROM_SP2: &[u8] = &[
+        0x06, 0x80, 0x2f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x01, 0x00,
+        0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x05, 0x80, 0x02, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x07, 0x40, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    ];
+
+    #[allow(dead_code)]
+    const MEM_RETRIEVE_REQ_FROM_SP1: &[u8] = &[
+        0x05, 0x80, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x01, 0x00,
+        0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x03, 0x80, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00,
+    ];
+
+    #[allow(dead_code)]
+    const MEM_RETRIEVE_REQ_FROM_SP2: &[u8] = &[
+        0x06, 0x80, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x01, 0x00,
+        0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x05, 0x80, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00,
+    ];
+
+    #[allow(dead_code)]
+    const MEM_SHARE_FROM_NWD: &[u8] = &[
+        0x00, 0x00, 0x2f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x01, 0x00,
+        0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x03, 0x80, 0x02, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x22, 0x80, 0x08, 0x00, 0x00, 0x00, 0x02, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    ];
+
+    #[test]
+    fn mem_share() {
+        let mut transaction_desc = MemTransactionDesc::default();
+        let mut composite_desc = CompositeMemRegionDesc::default();
+
+        transaction_desc
+            .parse(&mut composite_desc, MEM_RETRIEVE_REQ_FROM_SP1)
+            .unwrap();
+
+        println!("transaction desc: {:#x?}", transaction_desc);
+        println!("endpont desc: {:#x?}", transaction_desc.ep_access_descs);
+        println!("composite desc: {:#x?}", composite_desc);
+        println!("constituent desc: {:#x?}", composite_desc.constituents);
+    }
+}
diff --git a/src/partition_info.rs b/src/partition_info.rs
new file mode 100644
index 0000000..5301cb7
--- /dev/null
+++ b/src/partition_info.rs
@@ -0,0 +1,121 @@
+// SPDX-FileCopyrightText: Copyright 2023 Arm Limited and/or its affiliates <open-source-office@arm.com>
+// SPDX-License-Identifier: MIT OR Apache-2.0
+
+use uuid::Uuid;
+
+pub enum PartitionIdType {
+    PeEndpoint(u16),
+    SepidIndep,
+    SepidDep(u16),
+    Aux,
+}
+
+pub struct PartInfoDesc {
+    pub partition_id: u16,
+    pub uuid: Uuid,
+    pub id_type: PartitionIdType,
+    pub support_direct_req_rec: bool,
+    pub support_direct_req_send: bool,
+    pub support_indirect_msg: bool,
+    pub support_notif_rec: bool,
+    pub subscribe_vm_created: bool,
+    pub subscribe_vm_destroyed: bool,
+    pub is_aarch64: bool,
+}
+
+impl PartInfoDesc {
+    pub const SIZE: usize = 24;
+}
+
+pub fn create_partition_info(buf: &mut [u8], descriptors: &[PartInfoDesc], fill_uuid: bool) {
+    let mut offset = 0;
+
+    for desc in descriptors {
+        // Offset 0, length 2: 16-bit ID of the partition, stream or auxiliary endpoint.
+        buf[offset..offset + 2].copy_from_slice(&desc.partition_id.to_le_bytes());
+
+        // Offset 2, length 2: Execution context count or Proxy partition ID
+        match desc.id_type {
+            PartitionIdType::PeEndpoint(exec_ctx_cnt) => {
+                buf[offset + 2..offset + 4].copy_from_slice(&exec_ctx_cnt.to_le_bytes())
+            }
+            PartitionIdType::SepidDep(id) => {
+                buf[offset + 2..offset + 4].copy_from_slice(&id.to_le_bytes())
+            }
+            _ => buf[offset + 2..offset + 4].fill(0),
+        }
+
+        // Offset 4, length 4: Flags to determine partition properties.
+        let mut props = 0u32;
+        match desc.id_type {
+            PartitionIdType::PeEndpoint(_) => {
+                if desc.support_direct_req_rec {
+                    props |= 0b1;
+                    if desc.subscribe_vm_created {
+                        // TODO: check NS phys instance
+                        props |= 0b1 << 6
+                    }
+                    if desc.subscribe_vm_destroyed {
+                        // TODO: check NS phys instance
+                        props |= 0b1 << 7
+                    }
+                }
+                if desc.support_direct_req_send {
+                    props |= 0b1 << 1
+                }
+                if desc.support_indirect_msg {
+                    props |= 0b1 << 2
+                }
+                if desc.support_notif_rec {
+                    props |= 0b1 << 3
+                }
+            }
+            PartitionIdType::SepidIndep => props |= 0b01 << 4,
+            PartitionIdType::SepidDep(_) => props |= 0b10 << 4,
+            PartitionIdType::Aux => props |= 0b11 << 4,
+        }
+        if desc.is_aarch64 {
+            props |= 0b1 << 8
+        }
+        buf[offset + 4..offset + 8].copy_from_slice(&props.to_le_bytes());
+
+        // Offset 8, length 16: Partition UUID if the Nil UUID was specified. Reserved (MBZ) otherwise
+        if fill_uuid {
+            buf[offset + 8..offset + 24].copy_from_slice(desc.uuid.as_bytes());
+        } else {
+            buf[offset + 8..offset + 24].fill(0);
+        }
+
+        offset += PartInfoDesc::SIZE;
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+
+    use uuid::uuid;
+
+    #[test]
+    fn part_info() {
+        let desc = PartInfoDesc {
+            partition_id: 0x8001,
+            uuid: uuid!("12345678-1234-1234-1234-123456789abc"),
+            id_type: PartitionIdType::PeEndpoint(1),
+            support_direct_req_rec: true,
+            support_direct_req_send: true,
+            support_indirect_msg: false,
+            support_notif_rec: false,
+            subscribe_vm_created: true,
+            subscribe_vm_destroyed: true,
+            is_aarch64: true,
+        };
+
+        let mut buf = [0u8; 0xff];
+        create_partition_info(&mut buf, &[desc], true);
+
+        println!("{:#x?}", &buf[0..0x0f]);
+
+        assert_eq!(0x8001_u16, u16::from_le_bytes([buf[0], buf[1]]));
+    }
+}