Refactoring console log handling
Add ConsoleLogChars32 and ConsoleLogChars64 types to store character
payloads along with their lengths. These types offer utilities for
constructing and parsing console log payloads.
Signed-off-by: Imre Kis <imre.kis@arm.com>
Change-Id: Iaa9d777285a27dbdd0ae46485901ead4a83366f1
diff --git a/src/lib.rs b/src/lib.rs
index 502b7f1..2f4fc71 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -10,7 +10,7 @@
use num_enum::{IntoPrimitive, TryFromPrimitive};
use thiserror::Error;
pub use uuid::Uuid;
-use zerocopy::{transmute, IntoBytes};
+use zerocopy::{transmute, FromBytes, Immutable, IntoBytes};
pub mod boot_info;
mod ffa_v1_1;
@@ -58,6 +58,8 @@
InvalidPartitionInfoGetRegsResponse,
#[error("Invalid FF-A version {0} for function ID {1:?}")]
InvalidVersionForFunctionId(Version, FuncId),
+ #[error("Invalid character count {0}")]
+ InvalidCharacterCount(u8),
}
impl From<Error> for FfaError {
@@ -78,7 +80,8 @@
| Error::InvalidPartitionInfoGetFlag(_)
| Error::InvalidSuccessArgsVariant
| Error::InvalidNotificationCount
- | Error::InvalidPartitionInfoGetRegsResponse => Self::InvalidParameters,
+ | Error::InvalidPartitionInfoGetRegsResponse
+ | Error::InvalidCharacterCount(_) => Self::InvalidParameters,
}
}
}
@@ -700,10 +703,59 @@
/// Argument for the `FFA_CONSOLE_LOG` interface.
#[derive(Debug, Eq, PartialEq, Clone, Copy)]
pub enum ConsoleLogChars {
- Reg32([u32; 6]),
- Reg64([u64; 16]),
+ Chars32(ConsoleLogChars32),
+ Chars64(ConsoleLogChars64),
}
+/// Generic type for storing `FFA_CONSOLE_LOG` character payload and its length in bytes.
+#[derive(Debug, Default, Eq, PartialEq, Clone, Copy)]
+pub struct LogChars<T>
+where
+ T: IntoBytes + FromBytes + Immutable,
+{
+ char_cnt: u8,
+ char_lists: T,
+}
+
+impl<T> LogChars<T>
+where
+ T: IntoBytes + FromBytes + Immutable,
+{
+ const MAX_LENGTH: u8 = core::mem::size_of::<T>() as u8;
+
+ /// Returns true if there are no characters in the structure.
+ pub fn empty(&self) -> bool {
+ self.char_cnt == 0
+ }
+
+ /// Returns true if the structure is full.
+ pub fn full(&self) -> bool {
+ self.char_cnt as usize >= core::mem::size_of::<T>()
+ }
+
+ /// Returns the payload bytes.
+ pub fn bytes(&self) -> &[u8] {
+ &self.char_lists.as_bytes()[..self.char_cnt as usize]
+ }
+
+ /// Append byte slice to the end of the characters.
+ pub fn push(&mut self, source: &[u8]) -> usize {
+ let empty_area = &mut self.char_lists.as_mut_bytes()[self.char_cnt.into()..];
+ let len = empty_area.len().min(source.len());
+
+ empty_area[..len].copy_from_slice(&source[..len]);
+ self.char_cnt += len as u8;
+
+ len
+ }
+}
+
+/// Specialized type for 32-bit `FFA_CONSOLE_LOG` payload.
+pub type ConsoleLogChars32 = LogChars<[u32; 6]>;
+
+/// Specialized type for 64-bit `FFA_CONSOLE_LOG` payload.
+pub type ConsoleLogChars64 = LogChars<[u64; 16]>;
+
#[derive(Debug, Eq, PartialEq, Clone, Copy)]
pub struct NotificationBindFlags {
per_vcpu_notification: bool,
@@ -1229,8 +1281,7 @@
mem_perm: u32,
},
ConsoleLog {
- char_cnt: u8,
- char_lists: ConsoleLogChars,
+ chars: ConsoleLogChars,
},
NotificationBitmapCreate {
vm_id: u16,
@@ -1349,9 +1400,9 @@
MemAddr::Addr32(_) => Some(FuncId::MemPermSet32),
MemAddr::Addr64(_) => Some(FuncId::MemPermSet64),
},
- Interface::ConsoleLog { char_lists, .. } => match char_lists {
- ConsoleLogChars::Reg32(_) => Some(FuncId::ConsoleLog32),
- ConsoleLogChars::Reg64(_) => Some(FuncId::ConsoleLog64),
+ Interface::ConsoleLog { chars, .. } => match chars {
+ ConsoleLogChars::Chars32(_) => Some(FuncId::ConsoleLog32),
+ ConsoleLogChars::Chars64(_) => Some(FuncId::ConsoleLog64),
},
Interface::NotificationBitmapCreate { .. } => Some(FuncId::NotificationBitmapCreate),
Interface::NotificationBitmapDestroy { .. } => Some(FuncId::NotificationBitmapDestroy),
@@ -1756,17 +1807,26 @@
page_cnt: regs[2] as u32,
mem_perm: regs[3] as u32,
},
- FuncId::ConsoleLog32 => Self::ConsoleLog {
- char_cnt: regs[1] as u8,
- char_lists: ConsoleLogChars::Reg32([
- regs[2] as u32,
- regs[3] as u32,
- regs[4] as u32,
- regs[5] as u32,
- regs[6] as u32,
- regs[7] as u32,
- ]),
- },
+ FuncId::ConsoleLog32 => {
+ let char_cnt = regs[1] as u8;
+ if char_cnt > ConsoleLogChars32::MAX_LENGTH {
+ return Err(Error::InvalidCharacterCount(char_cnt));
+ }
+
+ Self::ConsoleLog {
+ chars: ConsoleLogChars::Chars32(ConsoleLogChars32 {
+ char_cnt,
+ char_lists: [
+ regs[2] as u32,
+ regs[3] as u32,
+ regs[4] as u32,
+ regs[5] as u32,
+ regs[6] as u32,
+ regs[7] as u32,
+ ],
+ }),
+ }
+ }
FuncId::NotificationBitmapCreate => {
let tentative_vm_id = regs[1] as u32;
if (tentative_vm_id >> 16) != 0 {
@@ -1838,10 +1898,19 @@
dst_id: regs[1] as u16,
args: DirectMsg2Args(regs[4..18].try_into().unwrap()),
},
- FuncId::ConsoleLog64 => Self::ConsoleLog {
- char_cnt: regs[1] as u8,
- char_lists: ConsoleLogChars::Reg64(regs[2..18].try_into().unwrap()),
- },
+ FuncId::ConsoleLog64 => {
+ let char_cnt = regs[1] as u8;
+ if char_cnt > ConsoleLogChars64::MAX_LENGTH {
+ return Err(Error::InvalidCharacterCount(char_cnt));
+ }
+
+ Self::ConsoleLog {
+ chars: ConsoleLogChars::Chars64(ConsoleLogChars64 {
+ char_cnt,
+ char_lists: regs[2..18].try_into().unwrap(),
+ }),
+ }
+ }
FuncId::PartitionInfoGetRegs => {
// Bits[15:0]: Start index
let start_index = (regs[3] & 0xffff) as u16;
@@ -1881,7 +1950,7 @@
match self {
Interface::ConsoleLog {
- char_lists: ConsoleLogChars::Reg64(_),
+ chars: ConsoleLogChars::Chars64(_),
..
}
| Interface::Success {
@@ -2205,23 +2274,21 @@
a[2] = page_cnt.into();
a[3] = mem_perm.into();
}
- Interface::ConsoleLog {
- char_cnt,
- char_lists,
- } => {
- a[1] = char_cnt.into();
- match char_lists {
- ConsoleLogChars::Reg32(regs) => {
- a[2] = regs[0].into();
- a[3] = regs[1].into();
- a[4] = regs[2].into();
- a[5] = regs[3].into();
- a[6] = regs[4].into();
- a[7] = regs[5].into();
- }
- _ => panic!("{:#x?} requires 18 registers", char_lists),
+ Interface::ConsoleLog { chars } => match chars {
+ ConsoleLogChars::Chars32(ConsoleLogChars32 {
+ char_cnt,
+ char_lists,
+ }) => {
+ a[1] = char_cnt.into();
+ a[2] = char_lists[0].into();
+ a[3] = char_lists[1].into();
+ a[4] = char_lists[2].into();
+ a[5] = char_lists[3].into();
+ a[6] = char_lists[4].into();
+ a[7] = char_lists[5].into();
}
- }
+ _ => panic!("{:#x?} requires 18 registers", chars),
+ },
Interface::NotificationBitmapCreate { vm_id, vcpu_cnt } => {
a[1] = vm_id.into();
a[2] = vcpu_cnt.into();
@@ -2310,16 +2377,16 @@
a[3] = 0;
a[4..18].copy_from_slice(&args.0[..14]);
}
- Interface::ConsoleLog {
- char_cnt,
- char_lists,
- } => {
- a[1] = char_cnt.into();
- match char_lists {
- ConsoleLogChars::Reg64(regs) => a[2..18].copy_from_slice(®s[..16]),
- _ => panic!("{:#x?} requires 8 registers", char_lists),
+ Interface::ConsoleLog { chars: char_lists } => match char_lists {
+ ConsoleLogChars::Chars64(ConsoleLogChars64 {
+ char_cnt,
+ char_lists,
+ }) => {
+ a[1] = char_cnt.into();
+ a[2..18].copy_from_slice(&char_lists[..16])
}
- }
+ _ => panic!("{:#x?} requires 8 registers", char_lists),
+ },
Interface::PartitionInfoGetRegs {
uuid,
start_index,
@@ -2357,36 +2424,6 @@
}
}
-/// Maximum number of characters transmitted in a single `FFA_CONSOLE_LOG32` message.
-pub const CONSOLE_LOG_32_MAX_CHAR_CNT: u8 = 24;
-/// Maximum number of characters transmitted in a single `FFA_CONSOLE_LOG64` message.
-pub const CONSOLE_LOG_64_MAX_CHAR_CNT: u8 = 128;
-
-/// Helper function to convert the "Tightly packed list of characters" format used by the
-/// `FFA_CONSOLE_LOG` interface into a byte slice.
-pub fn parse_console_log(
- char_cnt: u8,
- char_lists: &ConsoleLogChars,
- log_bytes: &mut [u8],
-) -> Result<(), FfaError> {
- match char_lists {
- ConsoleLogChars::Reg32(regs) => {
- if !(1..=CONSOLE_LOG_32_MAX_CHAR_CNT).contains(&char_cnt) {
- return Err(FfaError::InvalidParameters);
- }
- log_bytes[..char_cnt.into()].copy_from_slice(®s.as_bytes()[0..char_cnt.into()]);
- }
- ConsoleLogChars::Reg64(regs) => {
- if !(1..=CONSOLE_LOG_64_MAX_CHAR_CNT).contains(&char_cnt) {
- return Err(FfaError::InvalidParameters);
- }
- log_bytes[..char_cnt.into()].copy_from_slice(®s.as_bytes()[0..char_cnt.into()]);
- }
- }
-
- Ok(())
-}
-
#[cfg(test)]
mod tests {
use super::*;