Skip to content

Commit

Permalink
uapi: start support for rustic events (DMA, IPC, Signals and IRQ) man…
Browse files Browse the repository at this point in the history
…ipulation (#107)

- [x] Define event header and event structure that correspond to a
kernel event
- [x] Support for Event reception from kernel
- [x] Support for Event data transmission when being and IPC

Note: Event reception/transmission are not syscall calls, but exchange
interactions, meaning that a typical usage would
be :

```rust
let mut my_event = Event {
    header = ExchangeHeader {
        event = EventType::None,
        length = 0,
        peer = 0,
        magic = 0,
    },
    data = &[u8; 128],
}

let _ = syscall::wait_for_event(EventType::Ipc.into() | EventType::Irq.into(), 0);
// get back event from kernel, including data if needed
copy_from_kernel(my_event)?;

match event.header.event {
   EventType::Ipc => (),
   EventType::Irq => (),
   /// and so on
}
```
  • Loading branch information
pthierry-ledger authored Dec 17, 2024
2 parents c053b3b + f8242ad commit 1ae1963
Show file tree
Hide file tree
Showing 2 changed files with 304 additions and 4 deletions.
189 changes: 188 additions & 1 deletion uapi/src/exchange.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
// SPDX-License-Identifier: Apache-2.0

use crate::systypes::shm::ShmInfo;
use crate::systypes::Status;
use crate::systypes::{ExchangeHeader, Status};
use core::ptr::*;

const EXCHANGE_AREA_LEN: usize = 128; // TODO: replace by CONFIG-defined value
Expand Down Expand Up @@ -76,6 +76,151 @@ impl SentryExchangeable for crate::systypes::shm::ShmInfo {
}
}

// from-exchange related capacity to Exchang header
impl ExchangeHeader {
unsafe fn from_addr(self, address: usize) -> &'static Self {
&*(address as *const Self)
}

#[cfg(test)]
unsafe fn from_addr_mut(self, address: usize) -> &'static mut Self {
&mut *(address as *mut Self)
}

pub unsafe fn from_exchange(self) -> &'static Self {
self.from_addr(EXCHANGE_AREA.as_ptr() as usize)
}

#[cfg(test)]
pub unsafe fn from_exchange_mut(self) -> &'static mut Self {
self.from_addr_mut(EXCHANGE_AREA.as_mut_ptr() as usize)
}
}

/// Event SentryExchangeable trait implementation
///
/// Events are objects that are used to hold event ifnormation that need to be delivered or
/// received from the kernel.
///
/// Events are received from the kernel and hold a header and an associated data bloc.
/// The SentryExchangeable trait only support, in nominal mode, the from_kernel() usage for any
/// event, and to_kernel when emitting IPC
///
/// This trait allows to easily receive or deliver properly formatted events, including the
/// event header forged by the kernel and associated data.
///
/// # Example
///
/// ```ignore
/// let mut my_event = uapi::systypes::Event {
/// header: uapi::systypes::ExchangeHeader {
/// peer: 0,
/// event: uapi::systypes::EventType::None.into(),
/// length: 0,
/// magic: 0,
/// },
/// data: &mut[0; 12],
/// };
/// // wait for kernel events of type IRQ or IPC
/// let _ = uapi::syscall::wait_for_event(
/// uapi::systypes::EventType::IRQ.into() | uapi::systypes::EventType::Ipc.into(),
/// 0;
/// );
/// // get back event data from kernel
/// let _ = my_event.from_kernel();
/// // handle event
/// if !my_event.header.is_valid() {
/// return Err(),
/// }
/// match my_event.header.event {
/// EventType::Irq => treat_irq(&my_event.data, my_event.length),
/// EventType::IPC => treat_ipc(&my_event.data, my_event.length),
/// any_other => Err(),
/// }
/// ```
///
impl SentryExchangeable for crate::systypes::Event<'_> {
#[allow(static_mut_refs)]
fn from_kernel(&mut self) -> Result<Status, Status> {
// declare exchange as header first
let k_header: &ExchangeHeader = unsafe { ExchangeHeader::from_exchange(self.header) };
if !k_header.is_valid() {
return Err(Status::Invalid);
}
self.header = *k_header;
let header_len = core::mem::size_of::<ExchangeHeader>() as usize;
// be sure we have enough size in exchange zone
if header_len + usize::from(self.header.length) > EXCHANGE_AREA_LEN {
return Err(Status::Invalid);
}
if usize::from(self.header.length) > EXCHANGE_AREA_LEN - header_len {
// the length field is set by the kernel and thus, should not be invalid
// yet we check that there is no overflow as we use an unsafe block to get
// back from the exchange area
return Err(Status::Invalid);
}
// copy the amount of data in data slice using the header length info.
// Note: here we do not do any semantic related content check (i.e. data length or content
// based on the exchange type) but we let the kernel ensuring the correlation instead.
unsafe {
let data_ptr: *const u8 = (EXCHANGE_AREA.as_ptr() as usize + header_len) as *const u8;
let data_slice = core::slice::from_raw_parts(data_ptr, self.header.length.into());
// the destination slice must have enough space to get back data from the exchange zone
if data_slice.iter().count() > self.data.len() {
return Err(Status::Invalid);
}
for (dst, src) in self.data.iter_mut().zip(data_slice.iter()) {
*dst = *src
}
}
Ok(Status::Ok)
}

/// Event can be used as source for to_kernel() when being an IPC
///
/// Events not being and IPC do not need to emit any data in the
/// kernel exchange zone, leading to Err(Status::Invalid) return code.
///
#[cfg(not(test))]
fn to_kernel(&self) -> Result<Status, Status> {
use crate::systypes::EventType;

if self.header.event == EventType::Ipc.into() {
self.data.to_kernel()
} else {
Err(Status::Invalid)
}
}

#[cfg(test)]
#[allow(static_mut_refs)]
fn to_kernel(&self) -> Result<Status, Status> {
// copy exchange header to exhcange zone
let k_header: &mut ExchangeHeader =
unsafe { ExchangeHeader::from_exchange_mut(self.header) };
let header_len = core::mem::size_of::<ExchangeHeader>() as usize;
k_header.peer = self.header.peer;
k_header.magic = self.header.magic;
k_header.length = self.header.length;
k_header.event = self.header.event;
// now append data to header in exchange zone
if usize::from(self.header.length) > self.data.len() {
return Err(Status::Invalid);
}
unsafe {
let data_addr = EXCHANGE_AREA.as_ptr() as usize + header_len;
let data_ptr =
data_addr as *mut [u8; EXCHANGE_AREA_LEN - core::mem::size_of::<ExchangeHeader>()];
core::ptr::copy_nonoverlapping(
self.data.as_ptr(),
data_ptr as *mut u8,
self.header.length.into(),
);
}
Ok(Status::Ok)
}
}

impl SentryExchangeable for &mut [u8] {
#[allow(static_mut_refs)]
fn from_kernel(&mut self) -> Result<Status, Status> {
Expand Down Expand Up @@ -155,6 +300,8 @@ where

#[cfg(test)]
mod tests {
use crate::systypes::EventType;

use super::*;

#[test]
Expand All @@ -178,6 +325,46 @@ mod tests {
assert_eq!(src, dst);
}

#[test]
fn back_to_back_event() {
let src = crate::systypes::Event {
header: ExchangeHeader {
peer: 0x42,
event: EventType::Irq.into(),
length: 12,
magic: 0x4242,
},
data: &mut [0x42; 12],
};
let mut dst = crate::systypes::Event {
header: ExchangeHeader {
peer: 0,
event: EventType::None.into(),
length: 0,
magic: 0,
},
data: &mut [0; 12],
};
assert_eq!(src.to_kernel(), Ok(Status::Ok));
assert_eq!(dst.from_kernel(), Ok(Status::Ok));
assert_eq!(src.header, dst.header);
assert_eq!(
src.data[..src.header.length.into()],
dst.data[..src.header.length.into()]
);
let mut shorter_dst = crate::systypes::Event {
header: ExchangeHeader {
peer: 0,
event: EventType::None.into(),
length: 0,
magic: 0,
},
data: &mut [0; 8],
};
// dest that are not able to hold overall data must not generate panic
assert_eq!(shorter_dst.from_kernel(), Err(Status::Invalid));
}

#[test]
fn back_to_back_c_string() {
let src: &[u8] = &[42, 1, 3, 5, 12];
Expand Down
119 changes: 116 additions & 3 deletions uapi/src/systypes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,7 @@ pub type StreamHandle = u32;
/// As multiple events can be set at once, event field is using a
/// bitfield model to keep C+Rust usage easy
#[repr(C)]
#[derive(Clone, PartialEq, Copy, Debug)]
pub enum EventType {
/// No event
None = 0,
Expand All @@ -260,9 +261,17 @@ pub enum EventType {
All = 15,
}

/// Event Type to register (u32) converter
impl From<EventType> for u32 {
fn from(event: EventType) -> u32 {
/// list of potential Event types, encoded as u8 to exchange with kernel
///
/// This value corresponds to a set of one or multiple EventType value, so
/// that the job can wait for multiple events at a time.
/// Events priority when returning from the kernel is described in the events
/// chapter of the Sentry documentation.
///
pub type EventList = u8;

impl From<EventType> for u8 {
fn from(event: EventType) -> u8 {
match event {
EventType::None => 0,
EventType::Ipc => 1,
Expand All @@ -274,6 +283,19 @@ impl From<EventType> for u32 {
}
}

impl From<u8> for EventType {
fn from(event: u8) -> EventType {
match event {
0 => EventType::None,
1 => EventType::Ipc,
2 => EventType::Signal,
4 => EventType::Irq,
8 => EventType::Dma,
15 => EventType::All,
_ => EventType::None,
}
}
}
/// Erase type that can be used to clear the SVC_Exchange.
///
/// TODO: to be moved to svc_exchange module as used exclusively by
Expand Down Expand Up @@ -593,6 +615,97 @@ mirror_enum! {
}
}

/// Header received from the kernel when waiting for at one event type
///
/// Received when returning from [`crate::syscall::wait_for_event`] syscall with
/// a [`Status::Ok`] value.
/// The kernel return data for a single event type at a time, store in event field,
/// while wait_for_event() allows waiting for multiple event at a time.
//#[derive(Debug, Copy, Clone, PartialEq)]
#[repr(C)]
#[derive(Debug, Copy, Clone, PartialEq)]
pub struct ExchangeHeader {
pub event: u8,
pub length: u8,
pub magic: u16,
pub peer: u32,
}

#[test]
fn test_layout_exchange_header() {
const UNINIT: ::std::mem::MaybeUninit<ExchangeHeader> = ::std::mem::MaybeUninit::uninit();
let ptr = UNINIT.as_ptr();
assert_eq!(
::std::mem::size_of::<ExchangeHeader>(),
8usize,
concat!("Size of: ", stringify!(ExchangeHeader))
);
assert_eq!(
::std::mem::align_of::<ExchangeHeader>(),
4usize,
concat!("Alignment of ", stringify!(ExchangeHeader))
);
assert_eq!(
unsafe { ::std::ptr::addr_of!((*ptr).event) as usize - ptr as usize },
0usize,
concat!(
"Offset of field: ",
stringify!(ExchangeHeader),
"::",
stringify!(event)
)
);
assert_eq!(
unsafe { ::std::ptr::addr_of!((*ptr).length) as usize - ptr as usize },
1usize,
concat!(
"Offset of field: ",
stringify!(ExchangeHeader),
"::",
stringify!(length)
)
);
assert_eq!(
unsafe { ::std::ptr::addr_of!((*ptr).magic) as usize - ptr as usize },
2usize,
concat!(
"Offset of field: ",
stringify!(ExchangeHeader),
"::",
stringify!(magic)
)
);
assert_eq!(
unsafe { ::std::ptr::addr_of!((*ptr).peer) as usize - ptr as usize },
4usize,
concat!(
"Offset of field: ",
stringify!(ExchangeHeader),
"::",
stringify!(peer)
)
);
}

impl ExchangeHeader {
pub fn is_valid(self) -> bool {
// TODO: hard-coded by now
self.magic == 0x4242
}
}

/// Typical Event received by the kernel using the copy_from_kernel(my_event)
///
/// Event is SentryExchangeable, see [`crate::exchange::SentryExchangeable`] and
/// the corresponding implementation for this struct for more information.
///
#[derive(Debug)]
pub struct Event<'a> {
pub header: ExchangeHeader,
pub data: &'a mut [u8],
}

/// Device related types definitions
pub mod dev {

Expand Down

0 comments on commit 1ae1963

Please sign in to comment.