capsules_extra/
app_flash_driver.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//! This allows multiple apps to write their own flash region.
6//!
7//! All write requests from userland are checked to ensure that they are only
8//! trying to write their own flash space, and not the TBF header either.
9//!
10//! This driver can handle non page aligned writes.
11//!
12//! Userland apps should allocate buffers in flash when they are compiled to
13//! ensure that there is room to write to. This should be accomplished by
14//! declaring `const` buffers.
15//!
16//! Usage
17//! -----
18//!
19//! ```rust,ignore
20//! # use kernel::static_init;
21//!
22//! let app_flash_buffer = static_init!([u8; 512], [0; 512]);
23//! let app_flash = static_init!(
24//!     capsules::app_flash_driver::AppFlash<'static>,
25//!     capsules::app_flash_driver::AppFlash::new(nv_to_page,
26//!         board_kernel.create_grant(&grant_cap), app_flash_buffer));
27//! ```
28
29use core::cmp;
30
31use kernel::grant::{AllowRoCount, AllowRwCount, Grant, UpcallCount};
32use kernel::hil;
33use kernel::processbuffer::ReadableProcessBuffer;
34use kernel::syscall::{CommandReturn, SyscallDriver};
35use kernel::utilities::cells::{OptionalCell, TakeCell};
36use kernel::{ErrorCode, ProcessId};
37
38/// Syscall driver number.
39use capsules_core::driver;
40pub const DRIVER_NUM: usize = driver::NUM::AppFlash as usize;
41
42/// IDs for subscribed upcalls.
43mod upcall {
44    /// `write_done` callback.
45    pub const WRITE_DONE: usize = 0;
46    /// Number of upcalls.
47    pub const COUNT: u8 = 1;
48}
49
50/// Ids for read-only allow buffers
51mod ro_allow {
52    /// Set write buffer. This entire buffer will be written to flash.
53    pub const BUFFER: usize = 0;
54    /// The number of allow buffers the kernel stores for this grant
55    pub const COUNT: u8 = 1;
56}
57
58#[derive(Default)]
59pub struct App {
60    pending_command: bool,
61    flash_address: usize,
62}
63
64pub struct AppFlash<'a> {
65    driver: &'a dyn hil::nonvolatile_storage::NonvolatileStorage<'a>,
66    apps: Grant<
67        App,
68        UpcallCount<{ upcall::COUNT }>,
69        AllowRoCount<{ ro_allow::COUNT }>,
70        AllowRwCount<0>,
71    >,
72    current_app: OptionalCell<ProcessId>,
73    buffer: TakeCell<'static, [u8]>,
74}
75
76impl<'a> AppFlash<'a> {
77    pub fn new(
78        driver: &'a dyn hil::nonvolatile_storage::NonvolatileStorage<'a>,
79        grant: Grant<
80            App,
81            UpcallCount<{ upcall::COUNT }>,
82            AllowRoCount<{ ro_allow::COUNT }>,
83            AllowRwCount<0>,
84        >,
85        buffer: &'static mut [u8],
86    ) -> AppFlash<'a> {
87        AppFlash {
88            driver,
89            apps: grant,
90            current_app: OptionalCell::empty(),
91            buffer: TakeCell::new(buffer),
92        }
93    }
94
95    // Check to see if we are doing something. If not, go ahead and do this
96    // command. If so, this is queued and will be run when the pending command
97    // completes.
98    fn enqueue_write(&self, flash_address: usize, processid: ProcessId) -> Result<(), ErrorCode> {
99        self.apps
100            .enter(processid, |app, kernel_data| {
101                // Check that this is a valid range in the app's flash.
102                let flash_length = kernel_data
103                    .get_readonly_processbuffer(ro_allow::BUFFER)
104                    .map_or(0, |buffer| buffer.len());
105                let (app_flash_start, app_flash_end) = processid.get_editable_flash_range();
106                if flash_address < app_flash_start
107                    || flash_address >= app_flash_end
108                    || flash_address + flash_length >= app_flash_end
109                {
110                    return Err(ErrorCode::INVAL);
111                }
112
113                if self.current_app.is_none() {
114                    self.current_app.set(processid);
115
116                    kernel_data
117                        .get_readonly_processbuffer(ro_allow::BUFFER)
118                        .and_then(|buffer| {
119                            buffer.enter(|app_buffer| {
120                                // Copy contents to internal buffer and write it.
121                                self.buffer
122                                    .take()
123                                    .map_or(Err(ErrorCode::RESERVE), |buffer| {
124                                        let length = cmp::min(buffer.len(), app_buffer.len());
125                                        let d = &app_buffer[0..length];
126                                        for (i, c) in buffer[0..length].iter_mut().enumerate() {
127                                            *c = d[i].get();
128                                        }
129
130                                        self.driver.write(buffer, flash_address, length)
131                                    })
132                            })
133                        })
134                        .unwrap_or(Err(ErrorCode::RESERVE))
135                } else {
136                    // Queue this request for later.
137                    if app.pending_command {
138                        Err(ErrorCode::NOMEM)
139                    } else {
140                        app.pending_command = true;
141                        app.flash_address = flash_address;
142                        Ok(())
143                    }
144                }
145            })
146            .unwrap_or_else(|err| Err(err.into()))
147    }
148}
149
150impl hil::nonvolatile_storage::NonvolatileStorageClient for AppFlash<'_> {
151    fn read_done(&self, _buffer: &'static mut [u8], _length: usize) {}
152
153    fn write_done(&self, buffer: &'static mut [u8], _length: usize) {
154        // Put our write buffer back.
155        self.buffer.replace(buffer);
156
157        // Notify the current application that the command finished.
158        self.current_app.take().map(|processid| {
159            let _ = self.apps.enter(processid, |_app, upcalls| {
160                upcalls.schedule_upcall(upcall::WRITE_DONE, (0, 0, 0)).ok();
161            });
162        });
163
164        // Check if there are any pending events.
165        for cntr in self.apps.iter() {
166            let processid = cntr.processid();
167            let started_command = cntr.enter(|app, kernel_data| {
168                if app.pending_command {
169                    app.pending_command = false;
170                    self.current_app.set(processid);
171                    let flash_address = app.flash_address;
172
173                    kernel_data
174                        .get_readonly_processbuffer(ro_allow::BUFFER)
175                        .and_then(|buffer| {
176                            buffer.enter(|app_buffer| {
177                                self.buffer.take().is_some_and(|buffer| {
178                                    if app_buffer.len() != 512 {
179                                        false
180                                    } else {
181                                        // Copy contents to internal buffer and write it.
182                                        let length = cmp::min(buffer.len(), app_buffer.len());
183                                        let d = &app_buffer[0..length];
184                                        for (i, c) in buffer[0..length].iter_mut().enumerate() {
185                                            *c = d[i].get();
186                                        }
187
188                                        if let Ok(()) =
189                                            self.driver.write(buffer, flash_address, length)
190                                        {
191                                            true
192                                        } else {
193                                            false
194                                        }
195                                    }
196                                })
197                            })
198                        })
199                        .unwrap_or(false)
200                } else {
201                    false
202                }
203            });
204            if started_command {
205                break;
206            }
207        }
208    }
209}
210
211impl SyscallDriver for AppFlash<'_> {
212    /// App flash control.
213    ///
214    /// ### `command_num`
215    ///
216    /// - `0`: Driver existence check.
217    /// - `1`: Write the memory from the `allow` buffer to the address in flash.
218    fn command(
219        &self,
220        command_num: usize,
221        arg1: usize,
222        _: usize,
223        processid: ProcessId,
224    ) -> CommandReturn {
225        match command_num {
226            0 => CommandReturn::success(),
227
228            1 => {
229                // Write to flash from the allowed buffer
230                let flash_address = arg1;
231
232                let res = self.enqueue_write(flash_address, processid);
233
234                match res {
235                    Ok(()) => CommandReturn::success(),
236                    Err(e) => CommandReturn::failure(e),
237                }
238            }
239
240            _ => CommandReturn::failure(ErrorCode::NOSUPPORT),
241        }
242    }
243
244    fn allocate_grant(&self, processid: ProcessId) -> Result<(), kernel::process::Error> {
245        self.apps.enter(processid, |_, _| {})
246    }
247}