earlgrey_cw310/
otbn.rs

1// Licensed under the Apache License, Version 2.0 or the MIT License.
2// SPDX-License-Identifier: Apache-2.0 OR MIT
3// Copyright Tock Contributors 2022.
4
5//! Components for collections of Hardware Accelerators.
6//!
7//! Usage
8//! -----
9//! ```rust
10//!     let _mux_otbn = crate::otbn::AccelMuxComponent::new(&peripherals.otbn)
11//!         .finalize(otbn_mux_component_static!());
12//!
13//!     peripherals.otbn.initialise(
14//!         dynamic_deferred_caller
15//!             .register(&peripherals.otbn)
16//!             .unwrap(), // Unwrap fail = dynamic deferred caller out of slots
17//!     );
18//! ```
19
20use core::mem::MaybeUninit;
21use kernel::component::Component;
22use lowrisc::otbn::Otbn;
23use lowrisc::virtual_otbn::{MuxAccel, VirtualMuxAccel};
24
25#[macro_export]
26macro_rules! otbn_mux_component_static {
27    () => {{
28        kernel::static_buf!(lowrisc::virtual_otbn::MuxAccel<'static>)
29    }};
30}
31
32#[macro_export]
33macro_rules! otbn_component_static {
34    () => {{
35        kernel::static_buf!(lowrisc::virtual_otbn::VirtualMuxAccel<'static>)
36    }};
37}
38
39pub struct AccelMuxComponent {
40    otbn: &'static Otbn<'static>,
41}
42
43impl AccelMuxComponent {
44    pub fn new(otbn: &'static Otbn<'static>) -> AccelMuxComponent {
45        AccelMuxComponent { otbn }
46    }
47}
48
49impl Component for AccelMuxComponent {
50    type StaticInput = &'static mut MaybeUninit<MuxAccel<'static>>;
51    type Output = &'static MuxAccel<'static>;
52
53    fn finalize(self, s: Self::StaticInput) -> Self::Output {
54        s.write(MuxAccel::new(self.otbn))
55    }
56}
57
58pub struct OtbnComponent {
59    mux_otbn: &'static MuxAccel<'static>,
60}
61
62impl OtbnComponent {
63    pub fn new(mux_otbn: &'static MuxAccel<'static>) -> OtbnComponent {
64        OtbnComponent { mux_otbn }
65    }
66}
67
68impl Component for OtbnComponent {
69    type StaticInput = &'static mut MaybeUninit<VirtualMuxAccel<'static>>;
70
71    type Output = &'static VirtualMuxAccel<'static>;
72
73    fn finalize(self, s: Self::StaticInput) -> Self::Output {
74        let virtual_otbn_user = s.write(VirtualMuxAccel::new(self.mux_otbn));
75
76        virtual_otbn_user
77    }
78}
79
80/// Find the OTBN app in the Tock process list
81///
82/// This will iterate through the app list inside the `app_flash` looking
83/// for a disabled app with the same name as `name`.
84/// On success this function will return the following information:
85///    * OTBN imem start address
86///    * OTBN imem size
87///    * OTBN dmem start address
88///    * OTBN dmem size
89///
90/// This function is based on the Tock process loading code
91#[allow(dead_code)]
92pub fn find_app(name: &str, app_flash: &'static [u8]) -> Result<(usize, usize, usize, usize), ()> {
93    let mut remaining_flash = app_flash;
94
95    loop {
96        // Get the first eight bytes of flash to check if there is another
97        // app.
98        let test_header_slice = match remaining_flash.get(0..8) {
99            Some(s) => s,
100            None => {
101                // Not enough flash to test for another app. This just means
102                // we are at the end of flash, and there are no more apps to
103                // load.
104                return Err(());
105            }
106        };
107
108        // Pass the first eight bytes to tbfheader to parse out the length of
109        // the tbf header and app. We then use those values to see if we have
110        // enough flash remaining to parse the remainder of the header.
111        let (version, header_length, entry_length) = match tock_tbf::parse::parse_tbf_header_lengths(
112            test_header_slice.try_into().or(Err(()))?,
113        ) {
114            Ok((v, hl, el)) => (v, hl, el),
115            Err(tock_tbf::types::InitialTbfParseError::InvalidHeader(entry_length)) => {
116                // If we could not parse the header, then we want to skip over
117                // this app and look for the next one.
118                (0, 0, entry_length)
119            }
120            Err(tock_tbf::types::InitialTbfParseError::UnableToParse) => {
121                // Since Tock apps use a linked list, it is very possible the
122                // header we started to parse is intentionally invalid to signal
123                // the end of apps. This is ok and just means we have finished
124                // loading apps.
125                return Err(());
126            }
127        };
128
129        // Now we can get a slice which only encompasses the length of flash
130        // described by this tbf header.  We will either parse this as an actual
131        // app, or skip over this region.
132        let entry_flash = remaining_flash.get(0..entry_length as usize).ok_or(())?;
133
134        // Advance the flash slice for process discovery beyond this last entry.
135        // This will be the start of where we look for a new process since Tock
136        // processes are allocated back-to-back in flash.
137        remaining_flash = remaining_flash.get(entry_flash.len()..).ok_or(())?;
138
139        if header_length > 0 {
140            // If we found an actual app header, try to create a `Process`
141            // object. We also need to shrink the amount of remaining memory
142            // based on whatever is assigned to the new process if one is
143            // created.
144
145            // Get a slice for just the app header.
146            let header_flash = entry_flash.get(0..header_length as usize).ok_or(())?;
147
148            // Parse the full TBF header to see if this is a valid app. If the
149            // header can't parse, we will error right here.
150            if let Ok(tbf_header) = tock_tbf::parse::parse_tbf_header(header_flash, version) {
151                let process_name = tbf_header.get_package_name().unwrap();
152
153                // If the app is enabled, it's a real app and not what we are looking for.
154                if tbf_header.enabled() {
155                    continue;
156                }
157
158                if name != process_name {
159                    continue;
160                }
161
162                let dmem_length = tbf_header.get_minimum_app_ram_size();
163
164                let imem_start =
165                    unsafe { entry_flash.as_ptr().offset(header_length as isize) as usize };
166                let imem_length = entry_length - dmem_length - header_length as u32 - 4;
167
168                let dmem_start = unsafe {
169                    entry_flash
170                        .as_ptr()
171                        .offset(header_length as isize + imem_length as isize)
172                        as usize
173                };
174
175                return Ok((
176                    imem_start,
177                    imem_length as usize,
178                    dmem_start,
179                    dmem_length as usize,
180                ));
181            }
182        }
183    }
184}