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/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)
+}