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}