capsules_extra/
nonvolatile_storage_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 provides kernel and userspace access to nonvolatile memory.
6//!
7//! This is an initial implementation that does not provide safety for
8//! individual userland applications. Each application has full access to
9//! the entire memory space that has been provided to userland. Future revisions
10//! should update this to limit applications to only their allocated regions.
11//!
12//! However, the kernel accessible memory does not have to be the same range
13//! as the userspace accessible address space. The kernel memory can overlap
14//! if desired, or can be a completely separate range.
15//!
16//! Here is a diagram of the expected stack with this capsule:
17//! Boxes are components and between the boxes are the traits that are the
18//! interfaces between components. This capsule provides both a kernel and
19//! userspace interface.
20//!
21//! ```text
22//! +--------------------------------------------+     +--------------+
23//! |                                            |     |              |
24//! |                  kernel                    |     |  userspace   |
25//! |                                            |     |              |
26//! +--------------------------------------------+     +--------------+
27//!  hil::nonvolatile_storage::NonvolatileStorage       kernel::Driver
28//! +-----------------------------------------------------------------+
29//! |                                                                 |
30//! | capsules::nonvolatile_storage_driver::NonvolatileStorage (this) |
31//! |                                                                 |
32//! +-----------------------------------------------------------------+
33//!            hil::nonvolatile_storage::NonvolatileStorage
34//! +-----------------------------------------------------------------+
35//! |                                                                 |
36//! |               Physical nonvolatile storage driver               |
37//! |                                                                 |
38//! +-----------------------------------------------------------------+
39//! ```
40//!
41//! Example instantiation:
42//!
43//! ```rust,ignore
44//! # use kernel::static_init;
45//!
46//! let nonvolatile_storage = static_init!(
47//!     capsules::nonvolatile_storage_driver::NonvolatileStorage<'static>,
48//!     capsules::nonvolatile_storage_driver::NonvolatileStorage::new(
49//!         fm25cl,                      // The underlying storage driver.
50//!         board_kernel.create_grant(&grant_cap),     // Storage for app-specific state.
51//!         3000,                        // The byte start address for the userspace
52//!                                      // accessible memory region.
53//!         2000,                        // The length of the userspace region.
54//!         0,                           // The byte start address of the region
55//!                                      // that is accessible by the kernel.
56//!         3000,                        // The length of the kernel region.
57//!         &mut capsules::nonvolatile_storage_driver::BUFFER));
58//! hil::nonvolatile_storage::NonvolatileStorage::set_client(fm25cl, nonvolatile_storage);
59//! ```
60
61use core::cell::Cell;
62use core::cmp;
63
64use kernel::grant::{AllowRoCount, AllowRwCount, Grant, UpcallCount};
65use kernel::hil;
66use kernel::processbuffer::{ReadableProcessBuffer, WriteableProcessBuffer};
67use kernel::syscall::{CommandReturn, SyscallDriver};
68use kernel::utilities::cells::{OptionalCell, TakeCell};
69use kernel::{ErrorCode, ProcessId};
70
71/// Syscall driver number.
72use capsules_core::driver;
73pub const DRIVER_NUM: usize = driver::NUM::NvmStorage as usize;
74
75/// IDs for subscribed upcalls.
76mod upcall {
77    /// Read done callback.
78    pub const READ_DONE: usize = 0;
79    /// Write done callback.
80    pub const WRITE_DONE: usize = 1;
81    /// Number of upcalls.
82    pub const COUNT: u8 = 2;
83}
84
85/// Ids for read-only allow buffers
86mod ro_allow {
87    /// Setup a buffer to write bytes to the nonvolatile storage.
88    pub const WRITE: usize = 0;
89    /// The number of allow buffers the kernel stores for this grant
90    pub const COUNT: u8 = 1;
91}
92
93/// Ids for read-write allow buffers
94mod rw_allow {
95    /// Setup a buffer to read from the nonvolatile storage into.
96    pub const READ: usize = 0;
97    /// The number of allow buffers the kernel stores for this grant
98    pub const COUNT: u8 = 1;
99}
100
101pub const BUF_LEN: usize = 512;
102
103#[derive(Clone, Copy, PartialEq)]
104pub enum NonvolatileCommand {
105    UserspaceRead,
106    UserspaceWrite,
107    KernelRead,
108    KernelWrite,
109}
110
111#[derive(Clone, Copy)]
112pub enum NonvolatileUser {
113    App { processid: ProcessId },
114    Kernel,
115}
116
117pub struct App {
118    pending_command: bool,
119    command: NonvolatileCommand,
120    offset: usize,
121    length: usize,
122}
123
124impl Default for App {
125    fn default() -> App {
126        App {
127            pending_command: false,
128            command: NonvolatileCommand::UserspaceRead,
129            offset: 0,
130            length: 0,
131        }
132    }
133}
134
135pub struct NonvolatileStorage<'a> {
136    // The underlying physical storage device.
137    driver: &'a dyn hil::nonvolatile_storage::NonvolatileStorage<'a>,
138    // Per-app state.
139    apps: Grant<
140        App,
141        UpcallCount<{ upcall::COUNT }>,
142        AllowRoCount<{ ro_allow::COUNT }>,
143        AllowRwCount<{ rw_allow::COUNT }>,
144    >,
145
146    // Internal buffer for copying appslices into.
147    buffer: TakeCell<'static, [u8]>,
148    // What issued the currently executing call. This can be an app or the kernel.
149    current_user: OptionalCell<NonvolatileUser>,
150
151    // The first byte that is accessible from userspace.
152    userspace_start_address: usize,
153    // How many bytes allocated to userspace.
154    userspace_length: usize,
155    // The first byte that is accessible from the kernel.
156    kernel_start_address: usize,
157    // How many bytes allocated to kernel.
158    kernel_length: usize,
159
160    // Optional client for the kernel. Only needed if the kernel intends to use
161    // this nonvolatile storage.
162    kernel_client: OptionalCell<&'a dyn hil::nonvolatile_storage::NonvolatileStorageClient>,
163    // Whether the kernel is waiting for a read/write.
164    kernel_pending_command: Cell<bool>,
165    // Whether the kernel wanted a read/write.
166    kernel_command: Cell<NonvolatileCommand>,
167    // Holder for the buffer passed from the kernel in case we need to wait.
168    kernel_buffer: TakeCell<'static, [u8]>,
169    // How many bytes to read/write from the kernel buffer.
170    kernel_readwrite_length: Cell<usize>,
171    // Where to read/write from the kernel request.
172    kernel_readwrite_address: Cell<usize>,
173}
174
175impl<'a> NonvolatileStorage<'a> {
176    pub fn new(
177        driver: &'a dyn hil::nonvolatile_storage::NonvolatileStorage<'a>,
178        grant: Grant<
179            App,
180            UpcallCount<{ upcall::COUNT }>,
181            AllowRoCount<{ ro_allow::COUNT }>,
182            AllowRwCount<{ rw_allow::COUNT }>,
183        >,
184        userspace_start_address: usize,
185        userspace_length: usize,
186        kernel_start_address: usize,
187        kernel_length: usize,
188        buffer: &'static mut [u8],
189    ) -> NonvolatileStorage<'a> {
190        NonvolatileStorage {
191            driver,
192            apps: grant,
193            buffer: TakeCell::new(buffer),
194            current_user: OptionalCell::empty(),
195            userspace_start_address,
196            userspace_length,
197            kernel_start_address,
198            kernel_length,
199            kernel_client: OptionalCell::empty(),
200            kernel_pending_command: Cell::new(false),
201            kernel_command: Cell::new(NonvolatileCommand::KernelRead),
202            kernel_buffer: TakeCell::empty(),
203            kernel_readwrite_length: Cell::new(0),
204            kernel_readwrite_address: Cell::new(0),
205        }
206    }
207
208    // Check so see if we are doing something. If not, go ahead and do this
209    // command. If so, this is queued and will be run when the pending
210    // command completes.
211    fn enqueue_command(
212        &self,
213        command: NonvolatileCommand,
214        offset: usize,
215        length: usize,
216        processid: Option<ProcessId>,
217    ) -> Result<(), ErrorCode> {
218        // Do bounds check.
219        match command {
220            NonvolatileCommand::UserspaceRead | NonvolatileCommand::UserspaceWrite => {
221                // Userspace sees memory that starts at address 0 even if it
222                // is offset in the physical memory.
223                if offset >= self.userspace_length
224                    || length > self.userspace_length
225                    || offset + length > self.userspace_length
226                {
227                    return Err(ErrorCode::INVAL);
228                }
229            }
230            NonvolatileCommand::KernelRead | NonvolatileCommand::KernelWrite => {
231                // Because the kernel uses the NonvolatileStorage interface,
232                // its calls are absolute addresses.
233                if offset < self.kernel_start_address
234                    || offset >= self.kernel_start_address + self.kernel_length
235                    || length > self.kernel_length
236                    || offset + length > self.kernel_start_address + self.kernel_length
237                {
238                    return Err(ErrorCode::INVAL);
239                }
240            }
241        }
242
243        // Do very different actions if this is a call from userspace
244        // or from the kernel.
245        match command {
246            NonvolatileCommand::UserspaceRead | NonvolatileCommand::UserspaceWrite => {
247                processid.map_or(Err(ErrorCode::FAIL), |processid| {
248                    self.apps
249                        .enter(processid, |app, kernel_data| {
250                            // Get the length of the correct allowed buffer.
251                            let allow_buf_len = match command {
252                                NonvolatileCommand::UserspaceRead => kernel_data
253                                    .get_readwrite_processbuffer(rw_allow::READ)
254                                    .map_or(0, |read| read.len()),
255                                NonvolatileCommand::UserspaceWrite => kernel_data
256                                    .get_readonly_processbuffer(ro_allow::WRITE)
257                                    .map_or(0, |read| read.len()),
258                                _ => 0,
259                            };
260
261                            // Check that it exists.
262                            if allow_buf_len == 0 || self.buffer.is_none() {
263                                return Err(ErrorCode::RESERVE);
264                            }
265
266                            // Shorten the length if the application gave us nowhere to
267                            // put it.
268                            let active_len = cmp::min(length, allow_buf_len);
269
270                            // First need to determine if we can execute this or must
271                            // queue it.
272                            if self.current_user.is_none() {
273                                // No app is currently using the underlying storage.
274                                // Mark this app as active, and then execute the command.
275                                self.current_user.set(NonvolatileUser::App { processid });
276
277                                // Need to copy bytes if this is a write!
278                                if command == NonvolatileCommand::UserspaceWrite {
279                                    let _ = kernel_data
280                                        .get_readonly_processbuffer(ro_allow::WRITE)
281                                        .and_then(|write| {
282                                            write.enter(|app_buffer| {
283                                                self.buffer.map(|kernel_buffer| {
284                                                    // Check that the internal buffer and the buffer that was
285                                                    // allowed are long enough.
286                                                    let write_len =
287                                                        cmp::min(active_len, kernel_buffer.len());
288
289                                                    let d = &app_buffer[0..write_len];
290                                                    for (i, c) in kernel_buffer[0..write_len]
291                                                        .iter_mut()
292                                                        .enumerate()
293                                                    {
294                                                        *c = d[i].get();
295                                                    }
296                                                });
297                                            })
298                                        });
299                                }
300
301                                self.userspace_call_driver(command, offset, active_len)
302                            } else {
303                                // Some app is using the storage, we must wait.
304                                if app.pending_command {
305                                    // No more room in the queue, nowhere to store this
306                                    // request.
307                                    Err(ErrorCode::NOMEM)
308                                } else {
309                                    // We can store this, so lets do it.
310                                    app.pending_command = true;
311                                    app.command = command;
312                                    app.offset = offset;
313                                    app.length = active_len;
314                                    Ok(())
315                                }
316                            }
317                        })
318                        .unwrap_or_else(|err| Err(err.into()))
319                })
320            }
321            NonvolatileCommand::KernelRead | NonvolatileCommand::KernelWrite => {
322                self.kernel_buffer
323                    .take()
324                    .map_or(Err(ErrorCode::NOMEM), |kernel_buffer| {
325                        let active_len = cmp::min(length, kernel_buffer.len());
326
327                        // Check if there is something going on.
328                        if self.current_user.is_none() {
329                            // Nothing is using this, lets go!
330                            self.current_user.set(NonvolatileUser::Kernel);
331
332                            match command {
333                                NonvolatileCommand::KernelRead => {
334                                    self.driver.read(kernel_buffer, offset, active_len)
335                                }
336                                NonvolatileCommand::KernelWrite => {
337                                    self.driver.write(kernel_buffer, offset, active_len)
338                                }
339                                _ => Err(ErrorCode::FAIL),
340                            }
341                        } else {
342                            if self.kernel_pending_command.get() {
343                                Err(ErrorCode::NOMEM)
344                            } else {
345                                self.kernel_pending_command.set(true);
346                                self.kernel_command.set(command);
347                                self.kernel_readwrite_length.set(active_len);
348                                self.kernel_readwrite_address.set(offset);
349                                self.kernel_buffer.replace(kernel_buffer);
350                                Ok(())
351                            }
352                        }
353                    })
354            }
355        }
356    }
357
358    fn userspace_call_driver(
359        &self,
360        command: NonvolatileCommand,
361        offset: usize,
362        length: usize,
363    ) -> Result<(), ErrorCode> {
364        // Calculate where we want to actually read from in the physical
365        // storage.
366        let physical_address = offset + self.userspace_start_address;
367
368        self.buffer
369            .take()
370            .map_or(Err(ErrorCode::RESERVE), |buffer| {
371                // Check that the internal buffer and the buffer that was
372                // allowed are long enough.
373                let active_len = cmp::min(length, buffer.len());
374
375                // self.current_app.set(Some(processid));
376                match command {
377                    NonvolatileCommand::UserspaceRead => {
378                        self.driver.read(buffer, physical_address, active_len)
379                    }
380                    NonvolatileCommand::UserspaceWrite => {
381                        self.driver.write(buffer, physical_address, active_len)
382                    }
383                    _ => Err(ErrorCode::FAIL),
384                }
385            })
386    }
387
388    fn check_queue(&self) {
389        // Check if there are any pending events.
390        if self.kernel_pending_command.get() {
391            self.kernel_buffer.take().map(|kernel_buffer| {
392                self.kernel_pending_command.set(false);
393                self.current_user.set(NonvolatileUser::Kernel);
394
395                match self.kernel_command.get() {
396                    NonvolatileCommand::KernelRead => self.driver.read(
397                        kernel_buffer,
398                        self.kernel_readwrite_address.get(),
399                        self.kernel_readwrite_length.get(),
400                    ),
401                    NonvolatileCommand::KernelWrite => self.driver.write(
402                        kernel_buffer,
403                        self.kernel_readwrite_address.get(),
404                        self.kernel_readwrite_length.get(),
405                    ),
406                    _ => Err(ErrorCode::FAIL),
407                }
408            });
409        } else {
410            // If the kernel is not requesting anything, check all of the apps.
411            for cntr in self.apps.iter() {
412                let processid = cntr.processid();
413                let started_command = cntr.enter(|app, _| {
414                    if app.pending_command {
415                        app.pending_command = false;
416                        self.current_user.set(NonvolatileUser::App { processid });
417                        if let Ok(()) =
418                            self.userspace_call_driver(app.command, app.offset, app.length)
419                        {
420                            true
421                        } else {
422                            false
423                        }
424                    } else {
425                        false
426                    }
427                });
428                if started_command {
429                    break;
430                }
431            }
432        }
433    }
434}
435
436/// This is the callback client for the underlying physical storage driver.
437impl hil::nonvolatile_storage::NonvolatileStorageClient for NonvolatileStorage<'_> {
438    fn read_done(&self, buffer: &'static mut [u8], length: usize) {
439        // Switch on which user of this capsule generated this callback.
440        self.current_user.take().map(|user| {
441            match user {
442                NonvolatileUser::Kernel => {
443                    self.kernel_client.map(move |client| {
444                        client.read_done(buffer, length);
445                    });
446                }
447                NonvolatileUser::App { processid } => {
448                    let _ = self.apps.enter(processid, move |_, kernel_data| {
449                        // Need to copy in the contents of the buffer
450                        let _ = kernel_data
451                            .get_readwrite_processbuffer(rw_allow::READ)
452                            .and_then(|read| {
453                                read.mut_enter(|app_buffer| {
454                                    let read_len = cmp::min(app_buffer.len(), length);
455
456                                    let d = &app_buffer[0..read_len];
457                                    for (i, c) in buffer[0..read_len].iter().enumerate() {
458                                        d[i].set(*c);
459                                    }
460                                })
461                            });
462
463                        // Replace the buffer we used to do this read.
464                        self.buffer.replace(buffer);
465
466                        // And then signal the app.
467                        kernel_data
468                            .schedule_upcall(upcall::READ_DONE, (length, 0, 0))
469                            .ok();
470                    });
471                }
472            }
473        });
474
475        self.check_queue();
476    }
477
478    fn write_done(&self, buffer: &'static mut [u8], length: usize) {
479        // Switch on which user of this capsule generated this callback.
480        self.current_user.take().map(|user| {
481            match user {
482                NonvolatileUser::Kernel => {
483                    self.kernel_client.map(move |client| {
484                        client.write_done(buffer, length);
485                    });
486                }
487                NonvolatileUser::App { processid } => {
488                    let _ = self.apps.enter(processid, move |_app, kernel_data| {
489                        // Replace the buffer we used to do this write.
490                        self.buffer.replace(buffer);
491
492                        // And then signal the app.
493                        kernel_data
494                            .schedule_upcall(upcall::WRITE_DONE, (length, 0, 0))
495                            .ok();
496                    });
497                }
498            }
499        });
500
501        self.check_queue();
502    }
503}
504
505/// Provide an interface for the kernel.
506impl<'a> hil::nonvolatile_storage::NonvolatileStorage<'a> for NonvolatileStorage<'a> {
507    fn set_client(&self, client: &'a dyn hil::nonvolatile_storage::NonvolatileStorageClient) {
508        self.kernel_client.set(client);
509    }
510
511    fn read(
512        &self,
513        buffer: &'static mut [u8],
514        address: usize,
515        length: usize,
516    ) -> Result<(), ErrorCode> {
517        self.kernel_buffer.replace(buffer);
518        self.enqueue_command(NonvolatileCommand::KernelRead, address, length, None)
519    }
520
521    fn write(
522        &self,
523        buffer: &'static mut [u8],
524        address: usize,
525        length: usize,
526    ) -> Result<(), ErrorCode> {
527        self.kernel_buffer.replace(buffer);
528        self.enqueue_command(NonvolatileCommand::KernelWrite, address, length, None)
529    }
530}
531
532/// Provide an interface for userland.
533impl SyscallDriver for NonvolatileStorage<'_> {
534    /// Command interface.
535    ///
536    /// Commands are selected by the lowest 8 bits of the first argument.
537    ///
538    /// ### `command_num`
539    ///
540    /// - `0`: Return Ok(()) if this driver is included on the platform.
541    /// - `1`: Return the number of bytes available to userspace.
542    /// - `2`: Start a read from the nonvolatile storage.
543    /// - `3`: Start a write to the nonvolatile_storage.
544    fn command(
545        &self,
546        command_num: usize,
547        offset: usize,
548        length: usize,
549        processid: ProcessId,
550    ) -> CommandReturn {
551        match command_num {
552            0 => CommandReturn::success(),
553
554            1 => {
555                // How many bytes are accessible from userspace
556                // TODO: Would break on 64-bit platforms
557                CommandReturn::success_u32(self.userspace_length as u32)
558            }
559
560            2 => {
561                // Issue a read command
562                let res = self.enqueue_command(
563                    NonvolatileCommand::UserspaceRead,
564                    offset,
565                    length,
566                    Some(processid),
567                );
568
569                match res {
570                    Ok(()) => CommandReturn::success(),
571                    Err(e) => CommandReturn::failure(e),
572                }
573            }
574
575            3 => {
576                // Issue a write command
577                let res = self.enqueue_command(
578                    NonvolatileCommand::UserspaceWrite,
579                    offset,
580                    length,
581                    Some(processid),
582                );
583
584                match res {
585                    Ok(()) => CommandReturn::success(),
586                    Err(e) => CommandReturn::failure(e),
587                }
588            }
589
590            _ => CommandReturn::failure(ErrorCode::NOSUPPORT),
591        }
592    }
593
594    fn allocate_grant(&self, processid: ProcessId) -> Result<(), kernel::process::Error> {
595        self.apps.enter(processid, |_, _| {})
596    }
597}