sentry_uapi/
exchange.rs

1// SPDX-FileCopyrightText: 2023 Ledger SAS
2// SPDX-FileCopyrightText: 2025 ANSSI
3// SPDX-License-Identifier: Apache-2.0
4
5#![allow(unused_imports)]
6#![allow(static_mut_refs)]
7#![allow(clippy::wrong_self_convention)]
8use crate::systypes::shm::ShmInfo;
9use crate::systypes::{ExchangeHeader, Status};
10use core::mem;
11use core::ptr::*;
12
13const EXCHANGE_AREA_LEN: usize = 128; // TODO: replace by CONFIG-defined value
14
15/// The effective kernelspace/userspace exchange zone, set in a dedicated section
16///
17#[unsafe(link_section = ".svcexchange")]
18static mut EXCHANGE_AREA: [u8; EXCHANGE_AREA_LEN] = [0u8; EXCHANGE_AREA_LEN];
19
20/// Trait of kernel-user exchangeable objects
21///
22/// This trait written in order to support the notion of "kernel-exchangeable"
23/// type. Any type that do support this trait can be delivered to (and/or received
24/// from) the kernel.
25///
26/// The effective implementation of this trait public functions are kept private,
27/// and can't be implemented out of this very crate in order to ensure that only
28/// this crate's declared types are exchangeable.
29/// To ensure such a restriction, this trait is hosted by the current, crate-private,
30/// module.
31///
32/// As a consequence, don't try to implement that trait in any of the upper layers.
33///
34pub trait SentryExchangeable {
35    /// Copy the current object to the kernel exchagne zone
36    fn to_kernel(&self) -> Result<Status, Status>;
37
38    /// set the current object using the data delivered by the kernel in the exchange zone
39    fn from_kernel(&mut self) -> Result<Status, Status>;
40}
41
42/// SentryExchangeable trait implementation for ShmInfo.
43/// Shminfo is a typical structure which is returned by the kernel to the
44/// userspace in order to delivers various SHM-related information to a given
45/// task that is using the corresponding SHM.
46///
47/// In test mode only, this structure can be written back to the Exchange Area.
48/// In production mode, the application can't write such a content to the exchange
49/// as the kernel as strictly no use of it.
50///
51impl SentryExchangeable for crate::systypes::shm::ShmInfo {
52    #[allow(static_mut_refs)]
53    fn from_kernel(&mut self) -> Result<Status, Status> {
54        unsafe {
55            core::ptr::copy_nonoverlapping(
56                EXCHANGE_AREA.as_ptr(),
57                addr_of_mut!(*self) as *mut u8,
58                core::mem::size_of::<ShmInfo>().min(EXCHANGE_AREA_LEN),
59            );
60        }
61        Ok(Status::Ok)
62    }
63
64    #[cfg(test)]
65    #[allow(static_mut_refs)]
66    fn to_kernel(&self) -> Result<Status, Status> {
67        unsafe {
68            core::ptr::copy_nonoverlapping(
69                addr_of!(*self) as *const u8,
70                EXCHANGE_AREA.as_mut_ptr(),
71                core::mem::size_of::<ShmInfo>().min(EXCHANGE_AREA_LEN),
72            );
73        }
74        Ok(Status::Ok)
75    }
76
77    #[cfg(not(test))]
78    #[allow(static_mut_refs)]
79    fn to_kernel(&self) -> Result<Status, Status> {
80        Err(Status::Invalid)
81    }
82}
83
84/// SentryExchangeable trait implementation for dma::GpdmaStreamConfig.
85/// dma::GpdmaStreamConfig is a typical structure which is returned by the kernel to the
86/// userspace in order to delivers various DMA stream-related information to a given
87/// task that is using the corresponding DMA handle.
88///
89/// In test mode only, this structure can be written back to the Exchange Area.
90/// In production mode, the application can't write such a content to the exchange
91/// as the kernel as strictly no use of it.
92///
93impl SentryExchangeable for crate::systypes::dma::GpdmaStreamConfig {
94    #[allow(static_mut_refs)]
95    fn from_kernel(&mut self) -> Result<Status, Status> {
96        unsafe {
97            core::ptr::copy_nonoverlapping(
98                EXCHANGE_AREA.as_ptr(),
99                addr_of_mut!(*self) as *mut u8,
100                core::mem::size_of::<crate::systypes::dma::GpdmaStreamConfig>()
101                    .min(EXCHANGE_AREA_LEN),
102            );
103        }
104        Ok(Status::Ok)
105    }
106
107    #[cfg(test)]
108    #[allow(static_mut_refs)]
109    fn to_kernel(&self) -> Result<Status, Status> {
110        unsafe {
111            core::ptr::copy_nonoverlapping(
112                addr_of!(*self) as *const u8,
113                EXCHANGE_AREA.as_mut_ptr(),
114                core::mem::size_of::<crate::systypes::dma::GpdmaStreamConfig>()
115                    .min(EXCHANGE_AREA_LEN),
116            );
117        }
118        Ok(Status::Ok)
119    }
120
121    #[cfg(not(test))]
122    #[allow(static_mut_refs)]
123    fn to_kernel(&self) -> Result<Status, Status> {
124        Err(Status::Invalid)
125    }
126}
127
128/// SentryExchangeable trait implementation for Scalar types.
129///
130macro_rules! impl_exchangeable {
131    ($($t:ty),*) => {
132        $(
133            impl SentryExchangeable for $t {
134                fn from_kernel(&mut self) -> Result<Status, Status> {
135                    let (_, aligned, _) = unsafe { EXCHANGE_AREA.align_to_mut::<$t>() };
136                    let slot = aligned.first_mut().ok_or(Status::Invalid)?;
137                    *self = *slot;
138                    Ok(Status::Ok)
139                }
140
141                #[cfg(test)]
142                fn to_kernel(&self) -> Result<Status, Status> {
143                    let (_, aligned, _) = unsafe { EXCHANGE_AREA.align_to_mut::<$t>() };
144                    let slot = aligned.first_mut().ok_or(Status::Invalid)?;
145                    *slot = *self;
146                    Ok(Status::Ok)
147                }
148
149                #[cfg(not(test))]
150                fn to_kernel(&self) -> Result<Status, Status> {
151                    Err(Status::Invalid)
152                }
153            }
154        )*
155    };
156}
157
158impl_exchangeable!(u8, u16, u32, u64, usize);
159
160// from-exchange related capacity to Exchange header
161impl ExchangeHeader {
162    /// # Safety
163    /// We check that the address is aligned to the size of the type
164    unsafe fn from_addr() -> Option<&'static Self> {
165        let (_, aligned, _) = unsafe { EXCHANGE_AREA.align_to::<Self>() };
166        aligned.first()
167    }
168
169    #[cfg(test)]
170    unsafe fn from_addr_mut(self) -> &'static mut Self {
171        let (_, aligned, _) = unsafe { EXCHANGE_AREA.align_to_mut::<Self>() };
172        // .expect gives a explicit context if the exchange area is too small or misaligned
173        aligned
174            .first_mut()
175            .expect("Exchange area too small or misaligned")
176    }
177
178    //pub unsafe fn from_exchange(self) -> &'static Self {
179    //    unsafe { self.from_addr(EXCHANGE_AREA.as_ptr() as usize) }
180    /// # Safety
181    /// - `EXCHANGE_AREA` must be correctly aligned for `ExchangeHeader`.
182    /// - `EXCHANGE_AREA` must be large enough to contain a full `ExchangeHeader`.
183    /// - The contents of `EXCHANGE_AREA` must be properly initialized for reading as an `ExchangeHeader`.
184    ///
185    /// Violating any of these conditions may lead to undefined behavior.
186    pub unsafe fn from_exchange(self) -> Option<&'static Self> {
187        unsafe { Self::from_addr() }
188    }
189
190    #[cfg(test)]
191    pub unsafe fn from_exchange_mut(self) -> &'static mut Self {
192        unsafe { self.from_addr_mut() }
193    }
194}
195
196/// Event SentryExchangeable trait implementation
197///
198/// Events are objects that are used to hold event ifnormation that need to be delivered or
199/// received from the kernel.
200///
201/// Events are received from the kernel and hold a header and an associated data bloc.
202/// The SentryExchangeable trait only support, in nominal mode, the from_kernel() usage for any
203/// event, and to_kernel when emitting IPC
204///
205/// This trait allows to easily receive or deliver properly formatted events, including the
206/// event header forged by the kernel and associated data.
207///
208/// # Example
209///
210/// ```ignore
211/// let mut my_event = uapi::systypes::Event {
212///     header: uapi::systypes::ExchangeHeader {
213///         peer: 0,
214///         event: uapi::systypes::EventType::None.into(),
215///         length: 0,
216///         magic: 0,
217///     },
218///     data: &mut[0; 12],
219/// };
220/// // wait for kernel events of type IRQ or IPC
221/// let _ = uapi::syscall::wait_for_event(
222///             uapi::systypes::EventType::IRQ.into() | uapi::systypes::EventType::Ipc.into(),
223///             0;
224///         );
225/// // get back event data from kernel
226/// let _ = my_event.from_kernel();
227/// // handle event
228/// if !my_event.header.is_valid() {
229///     return Err(),
230/// }
231/// match my_event.header.event {
232///     EventType::Irq => treat_irq(&my_event.data, my_event.length),
233///     EventType::IPC => treat_ipc(&my_event.data,  my_event.length),
234///     any_other      => Err(),
235/// }
236/// ```
237///
238impl SentryExchangeable for crate::systypes::Event<'_> {
239    #[allow(static_mut_refs)]
240    fn from_kernel(&mut self) -> Result<Status, Status> {
241        use core::ptr;
242
243        unsafe {
244            // Read the header from the exchange area
245            // If the header is not aligned, it will be a bit slower
246            // but it will not crash anyway.
247            let header = ptr::read_unaligned(EXCHANGE_AREA.as_ptr() as *const ExchangeHeader);
248            self.header = header;
249        }
250
251        if !self.header.is_valid() {
252            return Err(Status::Invalid);
253        }
254
255        let header_len = core::mem::size_of::<ExchangeHeader>();
256        if header_len + usize::from(self.header.length) > EXCHANGE_AREA_LEN {
257            return Err(Status::Invalid);
258        }
259
260        unsafe {
261            let data_ptr = EXCHANGE_AREA.as_ptr().add(header_len);
262            let data_slice = core::slice::from_raw_parts(data_ptr, self.header.length.into());
263            if data_slice.len() > self.data.len() {
264                return Err(Status::Invalid);
265            }
266            for (dst, src) in self.data.iter_mut().zip(data_slice.iter()) {
267                *dst = *src;
268            }
269        }
270
271        Ok(Status::Ok)
272    }
273
274    /// Event can be used as source for to_kernel() when being an IPC
275    ///
276    /// Events not being and IPC do not need to emit any data in the
277    /// kernel exchange zone, leading to Err(Status::Invalid) return code.
278    ///
279    #[cfg(not(test))]
280    fn to_kernel(&self) -> Result<Status, Status> {
281        use crate::systypes::EventType;
282
283        if self.header.event == EventType::Ipc.into() {
284            self.data.to_kernel()
285        } else {
286            Err(Status::Invalid)
287        }
288    }
289
290    #[cfg(test)]
291    #[allow(static_mut_refs)]
292    fn to_kernel(&self) -> Result<Status, Status> {
293        use core::ptr;
294
295        if usize::from(self.header.length) > self.data.len() {
296            return Err(Status::Invalid);
297        }
298
299        unsafe {
300            // Writing the header
301            ptr::write_unaligned(
302                EXCHANGE_AREA.as_mut_ptr() as *mut ExchangeHeader,
303                self.header,
304            );
305
306            // Then writing the data
307            let header_len = core::mem::size_of::<ExchangeHeader>();
308            let data_addr = EXCHANGE_AREA.as_mut_ptr().add(header_len);
309            let dst_slice = core::slice::from_raw_parts_mut(data_addr, self.header.length.into());
310            dst_slice.copy_from_slice(&self.data[..self.header.length.into()]);
311        }
312
313        Ok(Status::Ok)
314    }
315}
316
317impl SentryExchangeable for &mut [u8] {
318    #[allow(static_mut_refs)]
319    fn from_kernel(&mut self) -> Result<Status, Status> {
320        unsafe {
321            core::ptr::copy_nonoverlapping(
322                EXCHANGE_AREA.as_ptr(),
323                self.as_mut_ptr(),
324                self.len().min(EXCHANGE_AREA_LEN),
325            );
326        }
327        Ok(Status::Ok)
328    }
329
330    #[allow(static_mut_refs)]
331    fn to_kernel(&self) -> Result<Status, Status> {
332        unsafe {
333            core::ptr::copy_nonoverlapping(
334                self.as_ptr(),
335                EXCHANGE_AREA.as_mut_ptr(),
336                self.len().min(EXCHANGE_AREA_LEN),
337            );
338        }
339        Ok(Status::Ok)
340    }
341}
342
343impl SentryExchangeable for &[u8] {
344    #[allow(static_mut_refs)]
345    fn from_kernel(&mut self) -> Result<Status, Status> {
346        Err(Status::Invalid)
347    }
348
349    #[allow(static_mut_refs)]
350    fn to_kernel(&self) -> Result<Status, Status> {
351        unsafe {
352            core::ptr::copy_nonoverlapping(
353                self.as_ptr(),
354                EXCHANGE_AREA.as_mut_ptr(),
355                self.len().min(EXCHANGE_AREA_LEN),
356            );
357        }
358        Ok(Status::Ok)
359    }
360}
361
362pub const fn length() -> usize {
363    EXCHANGE_AREA_LEN
364}
365
366/// copy to kernel generic implementation
367///
368/// This API is a generic implementation in order to allow userspace to kernelspace
369/// exchange for any type that do implement the SentryExchangeable trait.
370///
371/// # Example
372///
373/// A basic usage would look like the following:
374///
375/// ```ignore
376/// let my_info: &[u8] = &[1,2,3,4];
377/// copy_to_kernel(my_info);
378/// ```
379///
380pub fn copy_to_kernel<T>(from: &T) -> Result<Status, Status>
381where
382    T: SentryExchangeable + ?Sized,
383{
384    from.to_kernel()
385}
386
387pub fn copy_from_kernel<T>(to: &mut T) -> Result<Status, Status>
388where
389    T: SentryExchangeable + ?Sized,
390{
391    to.from_kernel()
392}
393
394#[cfg(test)]
395mod tests {
396    #[allow(dead_code)]
397    #[repr(align(8))]
398    pub struct ExchangeAligned(pub [u8; 128]);
399    #[unsafe(no_mangle)]
400    pub static mut EXCHANGE_AREA_TEST: ExchangeAligned = ExchangeAligned([0u8; 128]);
401
402    use crate::systypes::{EventType, ShmHandle};
403
404    use super::*;
405
406    #[test]
407    fn back_to_back_shminfo() {
408        let src = ShmInfo {
409            handle: 2,
410            label: 42,
411            base: 0x123456,
412            len: 64,
413            perms: 0x1,
414        };
415        let mut dst = ShmInfo {
416            handle: 0,
417            label: 0,
418            base: 0,
419            len: 0,
420            perms: 0,
421        };
422        let _ = src.to_kernel();
423        let _ = dst.from_kernel();
424        assert_eq!(src, dst);
425    }
426
427    #[test]
428    fn back_to_back_shmhandle() {
429        let src: ShmHandle = 33;
430        let mut dst: ShmHandle = 0;
431        // If not test to_kernel() must return Err(Status::Invalid)
432        let res1 = src.to_kernel();
433        let res2 = dst.from_kernel();
434        assert_eq!(res1, Ok(Status::Ok));
435        assert_eq!(res2, Ok(Status::Ok));
436        assert_eq!(src, dst);
437    }
438
439    #[test]
440    fn back_to_back_event() {
441        let src = crate::systypes::Event {
442            header: ExchangeHeader {
443                peer: 0x42,
444                event: EventType::Irq.into(),
445                length: 12,
446                magic: 0x4242,
447            },
448            data: &mut [0x42; 12],
449        };
450        let mut dst = crate::systypes::Event {
451            header: ExchangeHeader {
452                peer: 0,
453                event: EventType::None.into(),
454                length: 0,
455                magic: 0,
456            },
457            data: &mut [0; 12],
458        };
459
460        assert_eq!(src.to_kernel(), Ok(Status::Ok));
461        assert_eq!(dst.from_kernel(), Ok(Status::Ok));
462        assert_eq!(src.header, dst.header);
463        assert_eq!(
464            src.data[..src.header.length.into()],
465            dst.data[..src.header.length.into()]
466        );
467        let mut shorter_dst = crate::systypes::Event {
468            header: ExchangeHeader {
469                peer: 0,
470                event: EventType::None.into(),
471                length: 0,
472                magic: 0,
473            },
474            data: &mut [0; 8],
475        };
476        // dest that are not able to hold overall data must not generate panic
477        assert_eq!(shorter_dst.from_kernel(), Err(Status::Invalid));
478    }
479
480    #[test]
481    fn back_to_back_c_string() {
482        let src: &[u8] = &[42, 1, 3, 5, 12];
483        let mut dst: &mut [u8] = &mut [0, 0, 0, 0, 0];
484        assert_eq!(src.to_kernel(), Ok(Status::Ok));
485        assert_eq!(dst.from_kernel(), Ok(Status::Ok));
486        assert_eq!(src, dst);
487    }
488
489    #[test]
490    fn back_to_back_u8() {
491        let src: u8 = 42;
492        let mut dst = 0;
493        assert_eq!(src.to_kernel(), Ok(Status::Ok));
494        assert_eq!(dst.from_kernel(), Ok(Status::Ok));
495        assert_eq!(src, dst);
496    }
497
498    #[test]
499    fn back_to_back_u16() {
500        let src: u16 = 42;
501        let mut dst = 0;
502        assert_eq!(src.to_kernel(), Ok(Status::Ok));
503        assert_eq!(dst.from_kernel(), Ok(Status::Ok));
504        assert_eq!(src, dst);
505    }
506
507    #[test]
508    fn back_to_back_u32() {
509        let src: u32 = 42;
510        let mut dst = 0;
511        assert_eq!(src.to_kernel(), Ok(Status::Ok));
512        assert_eq!(dst.from_kernel(), Ok(Status::Ok));
513        assert_eq!(src, dst);
514    }
515
516    #[test]
517    fn back_to_back_u64() {
518        let src: u64 = 42;
519        let mut dst = 0;
520        assert_eq!(src.to_kernel(), Ok(Status::Ok));
521        assert_eq!(dst.from_kernel(), Ok(Status::Ok));
522        assert_eq!(src, dst);
523    }
524}