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}