Hopefully without breaking the others. Summary of the changes: - Device descriptor reports the device is bus powered and requires 100mA max. - HID descriptor version bumped to 1.11 (was 1.10) - Added string index for Interface and HID descriptors (which seems to make OS X happy)
549 lines
21 KiB
Diff
549 lines
21 KiB
Diff
diff --git a/chips/nrf52/src/nvmc.rs b/chips/nrf52/src/nvmc.rs
|
|
index 61e94260e..e115e1851 100644
|
|
--- a/chips/nrf52/src/nvmc.rs
|
|
+++ b/chips/nrf52/src/nvmc.rs
|
|
@@ -5,7 +5,14 @@
|
|
use core::cell::Cell;
|
|
use core::ops::{Index, IndexMut};
|
|
use kernel::deferred_call::DeferredCall;
|
|
+use kernel::dynamic_deferred_call::{
|
|
+ DeferredCallHandle, DynamicDeferredCall, DynamicDeferredCallClient,
|
|
+};
|
|
+use kernel::grant::{AllowRoCount, AllowRwCount, Grant, UpcallCount};
|
|
use kernel::hil;
|
|
+use kernel::process::ProcessId;
|
|
+use kernel::processbuffer::ReadableProcessBuffer;
|
|
+use kernel::syscall::CommandReturn;
|
|
use kernel::utilities::cells::OptionalCell;
|
|
use kernel::utilities::cells::TakeCell;
|
|
use kernel::utilities::cells::VolatileCell;
|
|
@@ -142,7 +149,13 @@ register_bitfields! [u32,
|
|
static DEFERRED_CALL: DeferredCall<DeferredCallTask> =
|
|
unsafe { DeferredCall::new(DeferredCallTask::Nvmc) };
|
|
|
|
+type WORD = u32;
|
|
+const WORD_SIZE: usize = core::mem::size_of::<WORD>();
|
|
const PAGE_SIZE: usize = 4096;
|
|
+const MAX_WORD_WRITES: u32 = 2;
|
|
+const MAX_PAGE_ERASES: u32 = 10_000;
|
|
+const WORD_MASK: usize = WORD_SIZE - 1;
|
|
+const PAGE_MASK: usize = PAGE_SIZE - 1;
|
|
|
|
/// This is a wrapper around a u8 array that is sized to a single page for the
|
|
/// nrf. Users of this module must pass an object of this type to use the
|
|
@@ -157,13 +170,11 @@ const PAGE_SIZE: usize = 4096;
|
|
///
|
|
/// let pagebuffer = unsafe { static_init!(NrfPage, NrfPage::default()) };
|
|
/// ```
|
|
-pub struct NrfPage(pub [u8; PAGE_SIZE as usize]);
|
|
+pub struct NrfPage(pub [u8; PAGE_SIZE]);
|
|
|
|
impl Default for NrfPage {
|
|
fn default() -> Self {
|
|
- Self {
|
|
- 0: [0; PAGE_SIZE as usize],
|
|
- }
|
|
+ Self { 0: [0; PAGE_SIZE] }
|
|
}
|
|
}
|
|
impl NrfPage {
|
|
@@ -218,6 +229,10 @@ impl Nvmc {
|
|
}
|
|
}
|
|
|
|
+ pub fn configure_readonly(&self) {
|
|
+ self.registers.config.write(Configuration::WEN::Ren);
|
|
+ }
|
|
+
|
|
/// Configure the NVMC to allow writes to flash.
|
|
pub fn configure_writeable(&self) {
|
|
self.registers.config.write(Configuration::WEN::Wen);
|
|
@@ -233,6 +248,7 @@ impl Nvmc {
|
|
self.registers
|
|
.erasepage
|
|
.write(ErasePage::ERASEPAGE.val(0x10001000));
|
|
+ self.registers.eraseuicr.write(EraseUicr::ERASEUICR::ERASE);
|
|
while !self.is_ready() {}
|
|
}
|
|
|
|
@@ -320,7 +336,7 @@ impl Nvmc {
|
|
// Put the NVMC in write mode.
|
|
self.registers.config.write(Configuration::WEN::Wen);
|
|
|
|
- for i in (0..data.len()).step_by(4) {
|
|
+ for i in (0..data.len()).step_by(WORD_SIZE) {
|
|
let word: u32 = (data[i + 0] as u32) << 0
|
|
| (data[i + 1] as u32) << 8
|
|
| (data[i + 2] as u32) << 16
|
|
@@ -388,3 +404,228 @@ impl hil::flash::Flash for Nvmc {
|
|
self.erase_page(page_number)
|
|
}
|
|
}
|
|
+
|
|
+type NvmcDriverGrant = Grant<App, UpcallCount<1>, AllowRoCount<1>, AllowRwCount<0>>;
|
|
+
|
|
+/// Provides access to the writeable flash regions of the application.
|
|
+///
|
|
+/// The purpose of this driver is to provide low-level access to the embedded flash of nRF52 boards
|
|
+/// to allow applications to implement flash-aware (like wear-leveling) data-structures. The driver
|
|
+/// only permits applications to operate on their writeable flash regions. The API is blocking since
|
|
+/// the CPU is halted during write and erase operations.
|
|
+///
|
|
+/// Supported boards:
|
|
+/// - nRF52840 (tested)
|
|
+/// - nRF52833
|
|
+/// - nRF52811
|
|
+/// - nRF52810
|
|
+///
|
|
+/// The maximum number of writes for the nRF52832 board is not per word but per block (512 bytes)
|
|
+/// and as such doesn't exactly fit this API. However, it could be safely supported by returning
|
|
+/// either 1 for the maximum number of word writes (i.e. the flash can only be written once before
|
|
+/// being erased) or 8 for the word size (i.e. the write granularity is doubled). In both cases,
|
|
+/// only 128 writes per block are permitted while the flash supports 181.
|
|
+///
|
|
+/// # Syscalls
|
|
+///
|
|
+/// - SUBSCRIBE(0, done): The callback for COMMAND(2) and COMMAND(3).
|
|
+/// - COMMAND(0): Check the driver.
|
|
+/// - COMMAND(1, 0): Get the word size (always 4).
|
|
+/// - COMMAND(1, 1): Get the page size (always 4096).
|
|
+/// - COMMAND(1, 2): Get the maximum number of word writes between page erasures (always 2).
|
|
+/// - COMMAND(1, 3): Get the maximum number page erasures in the lifetime of the flash (always
|
|
+/// 10000).
|
|
+/// - COMMAND(2, ptr, len): Write the allow slice to the flash region starting at `ptr`.
|
|
+/// - `ptr` must be word-aligned.
|
|
+/// - The allow slice length must be word aligned.
|
|
+/// - The region starting at `ptr` of the same length as the allow slice must be in a writeable
|
|
+/// flash region.
|
|
+/// - COMMAND(3, ptr, len): Erase a page.
|
|
+/// - `ptr` must be page-aligned.
|
|
+/// - The page starting at `ptr` must be in a writeable flash region.
|
|
+/// - ALLOW(0): The allow slice for COMMAND(2).
|
|
+pub struct SyscallDriver {
|
|
+ nvmc: &'static Nvmc,
|
|
+ apps: NvmcDriverGrant,
|
|
+ waiting: OptionalCell<ProcessId>,
|
|
+ deferred_caller: &'static DynamicDeferredCall,
|
|
+ deferred_handle: OptionalCell<DeferredCallHandle>,
|
|
+ buffer: TakeCell<'static, [u8]>,
|
|
+}
|
|
+
|
|
+pub const DRIVER_NUM: usize = 0x50003;
|
|
+
|
|
+#[derive(Default)]
|
|
+pub struct App {}
|
|
+
|
|
+fn is_write_needed(old: u32, new: u32) -> bool {
|
|
+ // No need to write if it would not modify the current value.
|
|
+ old & new != old
|
|
+}
|
|
+
|
|
+impl SyscallDriver {
|
|
+ pub fn new(
|
|
+ nvmc: &'static Nvmc,
|
|
+ apps: NvmcDriverGrant,
|
|
+ deferred_caller: &'static DynamicDeferredCall,
|
|
+ buffer: &'static mut [u8],
|
|
+ ) -> SyscallDriver {
|
|
+ nvmc.configure_readonly();
|
|
+ SyscallDriver {
|
|
+ nvmc,
|
|
+ apps,
|
|
+ waiting: OptionalCell::empty(),
|
|
+ deferred_caller,
|
|
+ deferred_handle: OptionalCell::empty(),
|
|
+ buffer: TakeCell::new(buffer),
|
|
+ }
|
|
+ }
|
|
+
|
|
+ pub fn set_deferred_handle(&self, handle: DeferredCallHandle) {
|
|
+ self.deferred_handle.replace(handle);
|
|
+ }
|
|
+
|
|
+ /// Writes a word-aligned slice at a word-aligned address.
|
|
+ ///
|
|
+ /// Words are written only if necessary, i.e. if writing the new value would change the current
|
|
+ /// value. This can be used to simplify recovery operations (e.g. if power is lost during a
|
|
+ /// write operation). The application doesn't need to check which prefix has already been
|
|
+ /// written and may repeat the complete write that was interrupted.
|
|
+ ///
|
|
+ /// # Safety
|
|
+ ///
|
|
+ /// The words in this range must have been written less than `MAX_WORD_WRITES` since their last
|
|
+ /// page erasure.
|
|
+ ///
|
|
+ /// # Errors
|
|
+ ///
|
|
+ /// Fails with `EINVAL` if any of the following conditions does not hold:
|
|
+ /// - `ptr` must be word-aligned.
|
|
+ /// - `slice.len()` must be word-aligned.
|
|
+ /// - The slice starting at `ptr` of length `slice.len()` must fit in the storage.
|
|
+ fn write_slice(&self, ptr: usize, slice: &[u8]) -> CommandReturn {
|
|
+ if ptr & WORD_MASK != 0 || slice.len() & WORD_MASK != 0 {
|
|
+ return CommandReturn::failure(ErrorCode::INVAL);
|
|
+ }
|
|
+ self.nvmc.configure_writeable();
|
|
+ for (i, chunk) in slice.chunks(WORD_SIZE).enumerate() {
|
|
+ // `unwrap` cannot fail because `slice.len()` is word-aligned (see above).
|
|
+ let val = WORD::from_ne_bytes(<[u8; WORD_SIZE]>::try_from(chunk).unwrap());
|
|
+ let loc = unsafe { &*(ptr as *const VolatileCell<u32>).add(i) };
|
|
+ if is_write_needed(loc.get(), val) {
|
|
+ loc.set(val);
|
|
+ }
|
|
+ }
|
|
+ while !self.nvmc.is_ready() {}
|
|
+ self.nvmc.configure_readonly();
|
|
+ self.deferred_handle
|
|
+ .map(|handle| self.deferred_caller.set(*handle));
|
|
+ CommandReturn::success()
|
|
+ }
|
|
+
|
|
+ /// Erases a page at a page-aligned address.
|
|
+ ///
|
|
+ /// # Errors
|
|
+ ///
|
|
+ /// Fails with `EINVAL` if any of the following conditions does not hold:
|
|
+ /// - `ptr` must be page-aligned.
|
|
+ /// - The slice starting at `ptr` of length `PAGE_SIZE` must fit in the storage.
|
|
+ fn erase_page(&self, ptr: usize) -> CommandReturn {
|
|
+ if ptr & PAGE_MASK != 0 {
|
|
+ return CommandReturn::failure(ErrorCode::INVAL);
|
|
+ }
|
|
+ self.nvmc.erase_page_helper(ptr / PAGE_SIZE);
|
|
+ self.nvmc.configure_readonly();
|
|
+ self.deferred_handle
|
|
+ .map(|handle| self.deferred_caller.set(*handle));
|
|
+ CommandReturn::success()
|
|
+ }
|
|
+}
|
|
+
|
|
+impl DynamicDeferredCallClient for SyscallDriver {
|
|
+ fn call(&self, _handle: DeferredCallHandle) {
|
|
+ self.waiting.take().map(|process_id| {
|
|
+ self.apps.enter(process_id, |_, kernel_data| {
|
|
+ kernel_data.schedule_upcall(0, (0, 0, 0))
|
|
+ })
|
|
+ });
|
|
+ }
|
|
+}
|
|
+
|
|
+impl kernel::syscall::SyscallDriver for SyscallDriver {
|
|
+ fn allocate_grant(&self, process_id: ProcessId) -> Result<(), kernel::process::Error> {
|
|
+ self.apps.enter(process_id, |_, _| {})
|
|
+ }
|
|
+
|
|
+ fn command(
|
|
+ &self,
|
|
+ command_num: usize,
|
|
+ r2: usize,
|
|
+ r3: usize,
|
|
+ process_id: ProcessId,
|
|
+ ) -> CommandReturn {
|
|
+ match (command_num, r2, r3) {
|
|
+ (0, _, _) => CommandReturn::success(),
|
|
+
|
|
+ (1, 0, _) => CommandReturn::success_u32(WORD_SIZE.try_into().unwrap()),
|
|
+ (1, 1, _) => CommandReturn::success_u32(PAGE_SIZE.try_into().unwrap()),
|
|
+ (1, 2, _) => CommandReturn::success_u32(MAX_WORD_WRITES),
|
|
+ (1, 3, _) => CommandReturn::success_u32(MAX_PAGE_ERASES),
|
|
+ (1, _, _) => CommandReturn::failure(ErrorCode::INVAL),
|
|
+
|
|
+ (2, ptr, _len) => self
|
|
+ .apps
|
|
+ .enter(process_id, |_, kernel| {
|
|
+ kernel
|
|
+ .get_readonly_processbuffer(0)
|
|
+ .and_then(|processbuffer| {
|
|
+ processbuffer.enter(|app_buf| {
|
|
+ // Copy contents to the internal buffer first
|
|
+ self.buffer.take().map_or(
|
|
+ CommandReturn::failure(ErrorCode::RESERVE),
|
|
+ |buffer| {
|
|
+ // as the drivers buffer can be bigger than the app buffer,
|
|
+ // we choose the minimum to not copy anymore than we need
|
|
+ let len = core::cmp::min(buffer.len(), app_buf.len());
|
|
+
|
|
+ // safety check when the app buffer is too large
|
|
+ if app_buf.len() > buffer.len() {
|
|
+ return CommandReturn::failure(ErrorCode::INVAL);
|
|
+ }
|
|
+
|
|
+ let d = &app_buf[0..len];
|
|
+ for (i, v) in buffer.as_mut()[0..len].iter_mut().enumerate()
|
|
+ {
|
|
+ *v = d[i].get();
|
|
+ }
|
|
+
|
|
+ if self.waiting.is_some() {
|
|
+ return CommandReturn::failure(ErrorCode::BUSY);
|
|
+ }
|
|
+ self.waiting.set(process_id);
|
|
+ let result = self.write_slice(ptr, &buffer[0..len]);
|
|
+ self.buffer.replace(buffer);
|
|
+ result
|
|
+ },
|
|
+ )
|
|
+ })
|
|
+ })
|
|
+ .unwrap_or(CommandReturn::failure(ErrorCode::RESERVE))
|
|
+ })
|
|
+ .unwrap_or_else(|e| CommandReturn::failure(e.into())),
|
|
+
|
|
+ (3, ptr, len) => {
|
|
+ if len != PAGE_SIZE {
|
|
+ return CommandReturn::failure(ErrorCode::INVAL);
|
|
+ }
|
|
+ if self.waiting.is_some() {
|
|
+ return CommandReturn::failure(ErrorCode::BUSY);
|
|
+ }
|
|
+ self.waiting.set(process_id);
|
|
+ self.erase_page(ptr)
|
|
+ }
|
|
+
|
|
+ _ => CommandReturn::failure(ErrorCode::NOSUPPORT),
|
|
+ }
|
|
+ }
|
|
+}
|
|
diff --git a/kernel/src/kernel.rs b/kernel/src/kernel.rs
|
|
index a1d081ad7..583fbe8d6 100644
|
|
--- a/kernel/src/kernel.rs
|
|
+++ b/kernel/src/kernel.rs
|
|
@@ -38,6 +38,20 @@ use crate::utilities::cells::NumericCellExt;
|
|
/// is less than this threshold.
|
|
pub(crate) const MIN_QUANTA_THRESHOLD_US: u32 = 500;
|
|
|
|
+/// Represents the type of a storage slice.
|
|
+#[derive(Copy, Clone)]
|
|
+pub enum StorageType {
|
|
+ Store = 1,
|
|
+ Partition = 2,
|
|
+}
|
|
+
|
|
+/// Represents a storage location in flash.
|
|
+pub struct StorageLocation {
|
|
+ pub address: usize,
|
|
+ pub size: usize,
|
|
+ pub storage_type: StorageType,
|
|
+}
|
|
+
|
|
/// Main object for the kernel. Each board will need to create one.
|
|
pub struct Kernel {
|
|
/// How many "to-do" items exist at any given time. These include
|
|
@@ -47,6 +61,9 @@ pub struct Kernel {
|
|
/// This holds a pointer to the static array of Process pointers.
|
|
processes: &'static [Option<&'static dyn process::Process>],
|
|
|
|
+ /// List of storage locations.
|
|
+ storage_locations: &'static [StorageLocation],
|
|
+
|
|
/// A counter which keeps track of how many process identifiers have been
|
|
/// created. This is used to create new unique identifiers for processes.
|
|
process_identifier_max: Cell<usize>,
|
|
@@ -110,15 +127,26 @@ fn try_allocate_grant(driver: &dyn SyscallDriver, process: &dyn process::Process
|
|
|
|
impl Kernel {
|
|
pub fn new(processes: &'static [Option<&'static dyn process::Process>]) -> Kernel {
|
|
+ Kernel::new_with_storage(processes, &[])
|
|
+ }
|
|
+ pub fn new_with_storage(
|
|
+ processes: &'static [Option<&'static dyn process::Process>],
|
|
+ storage_locations: &'static [StorageLocation],
|
|
+ ) -> Kernel {
|
|
Kernel {
|
|
work: Cell::new(0),
|
|
processes,
|
|
+ storage_locations,
|
|
process_identifier_max: Cell::new(0),
|
|
grant_counter: Cell::new(0),
|
|
grants_finalized: Cell::new(false),
|
|
}
|
|
}
|
|
|
|
+ pub fn storage_locations(&self) -> &'static [StorageLocation] {
|
|
+ self.storage_locations
|
|
+ }
|
|
+
|
|
/// Something was scheduled for a process, so there is more work to do.
|
|
///
|
|
/// This is only exposed in the core kernel crate.
|
|
@@ -1374,3 +1402,14 @@ impl Kernel {
|
|
}
|
|
}
|
|
}
|
|
+
|
|
+impl TryFrom<StorageType> for u32 {
|
|
+ type Error = ();
|
|
+
|
|
+ fn try_from(value: StorageType) -> Result<Self, Self::Error> {
|
|
+ match value {
|
|
+ StorageType::Store => Ok(StorageType::Store as u32),
|
|
+ StorageType::Partition => Ok(StorageType::Partition as u32),
|
|
+ }
|
|
+ }
|
|
+}
|
|
diff --git a/kernel/src/lib.rs b/kernel/src/lib.rs
|
|
index 028f30220..8880bc000 100644
|
|
--- a/kernel/src/lib.rs
|
|
+++ b/kernel/src/lib.rs
|
|
@@ -125,5 +125,6 @@ mod syscall_driver;
|
|
// Core resources exposed as `kernel::Type`.
|
|
pub use crate::errorcode::ErrorCode;
|
|
pub use crate::kernel::Kernel;
|
|
+pub use crate::kernel::{StorageLocation, StorageType};
|
|
pub use crate::process::ProcessId;
|
|
pub use crate::scheduler::Scheduler;
|
|
diff --git a/kernel/src/memop.rs b/kernel/src/memop.rs
|
|
index 51d89f37c..c4f7cef92 100644
|
|
--- a/kernel/src/memop.rs
|
|
+++ b/kernel/src/memop.rs
|
|
@@ -107,6 +107,37 @@ pub(crate) fn memop(process: &dyn Process, op_type: usize, r1: usize) -> Syscall
|
|
SyscallReturn::Success
|
|
}
|
|
|
|
+ // 12 - 15 are required for the custom persistent store driver
|
|
+ // currently only implemented in the nvmc module of nrf52 series
|
|
+ // driver number: 0x50003
|
|
+
|
|
+ // Op Type 12: Number of storage locations
|
|
+ 12 => SyscallReturn::SuccessU32(process.number_storage_locations() as u32),
|
|
+
|
|
+ // Op Type 13: The start address of the storage location indexed by r1
|
|
+ 13 => {
|
|
+ match process.get_storage_location(r1) {
|
|
+ None => SyscallReturn::Failure(ErrorCode::FAIL),
|
|
+ Some(x) => SyscallReturn::SuccessU32(x.address as u32),
|
|
+ }
|
|
+ }
|
|
+
|
|
+ // Op Type 14: The size of the storage location indexed by r1.
|
|
+ 14 => {
|
|
+ match process.get_storage_location(r1) {
|
|
+ None => SyscallReturn::Failure(ErrorCode::FAIL),
|
|
+ Some(x) => SyscallReturn::SuccessU32(x.size as u32),
|
|
+ }
|
|
+ }
|
|
+
|
|
+ // Op Type 15: The type of the storage location indexed by r1.
|
|
+ 15 => {
|
|
+ match process.get_storage_location(r1) {
|
|
+ None => SyscallReturn::Failure(ErrorCode::FAIL),
|
|
+ Some(x) => SyscallReturn::SuccessU32(x.storage_type as u32)
|
|
+ }
|
|
+ }
|
|
+
|
|
_ => SyscallReturn::Failure(ErrorCode::NOSUPPORT),
|
|
}
|
|
}
|
|
diff --git a/kernel/src/process.rs b/kernel/src/process.rs
|
|
index c1794d9bf..4fbad3f0b 100644
|
|
--- a/kernel/src/process.rs
|
|
+++ b/kernel/src/process.rs
|
|
@@ -9,7 +9,7 @@ use core::str;
|
|
use crate::capabilities;
|
|
use crate::errorcode::ErrorCode;
|
|
use crate::ipc;
|
|
-use crate::kernel::Kernel;
|
|
+use crate::kernel::{Kernel, StorageLocation};
|
|
use crate::platform::mpu::{self};
|
|
use crate::processbuffer::{ReadOnlyProcessBuffer, ReadWriteProcessBuffer};
|
|
use crate::storage_permissions;
|
|
@@ -335,6 +335,15 @@ pub trait Process {
|
|
/// writeable flash region.
|
|
fn get_writeable_flash_region(&self, region_index: usize) -> (u32, u32);
|
|
|
|
+ /// How many storage locations are defined for this process.
|
|
+ fn number_storage_locations(&self) -> usize;
|
|
+
|
|
+ /// Get the i-th storage location.
|
|
+ fn get_storage_location(&self, index: usize) -> Option<&StorageLocation>;
|
|
+
|
|
+ /// Whether a slice fits in a storage location.
|
|
+ fn fits_in_storage_location(&self, ptr: usize, len: usize) -> bool;
|
|
+
|
|
/// Debug function to update the kernel on where the stack starts for this
|
|
/// process. Processes are not required to call this through the memop
|
|
/// system call, but it aids in debugging the process.
|
|
diff --git a/kernel/src/process_standard.rs b/kernel/src/process_standard.rs
|
|
index fa76e2c68..dd03e8f29 100644
|
|
--- a/kernel/src/process_standard.rs
|
|
+++ b/kernel/src/process_standard.rs
|
|
@@ -14,7 +14,7 @@ use crate::collections::ring_buffer::RingBuffer;
|
|
use crate::config;
|
|
use crate::debug;
|
|
use crate::errorcode::ErrorCode;
|
|
-use crate::kernel::Kernel;
|
|
+use crate::kernel::{Kernel, StorageLocation};
|
|
use crate::platform::chip::Chip;
|
|
use crate::platform::mpu::{self, MPU};
|
|
use crate::process::{Error, FunctionCall, FunctionCallSource, Process, State, Task};
|
|
@@ -258,6 +258,35 @@ impl<C: Chip> Process for ProcessStandard<'_, C> {
|
|
ret
|
|
}
|
|
|
|
+ fn number_storage_locations(&self) -> usize {
|
|
+ self.kernel.storage_locations().len()
|
|
+ }
|
|
+
|
|
+ fn get_storage_location(&self, index: usize) -> Option<&StorageLocation> {
|
|
+ self.kernel.storage_locations().get(index)
|
|
+ }
|
|
+
|
|
+ fn fits_in_storage_location(&self, ptr: usize, len: usize) -> bool {
|
|
+ self.kernel
|
|
+ .storage_locations()
|
|
+ .iter()
|
|
+ .any(|storage_location| {
|
|
+ let storage_ptr = storage_location.address;
|
|
+ let storage_len = storage_location.size;
|
|
+ // We want to check the 2 following inequalities:
|
|
+ // (1) `storage_ptr <= ptr`
|
|
+ // (2) `ptr + len <= storage_ptr + storage_len`
|
|
+ // However, the second one may overflow written as is. We introduce a third
|
|
+ // inequality to solve this issue:
|
|
+ // (3) `len <= storage_len`
|
|
+ // Using this third inequality, we can rewrite the second one as:
|
|
+ // (4) `ptr - storage_ptr <= storage_len - len`
|
|
+ // This fourth inequality is equivalent to the second one but doesn't overflow when
|
|
+ // the first and third inequalities hold.
|
|
+ storage_ptr <= ptr && len <= storage_len && ptr - storage_ptr <= storage_len - len
|
|
+ })
|
|
+ }
|
|
+
|
|
fn ready(&self) -> bool {
|
|
self.tasks.map_or(false, |ring_buf| ring_buf.has_elements())
|
|
|| self.state.get() == State::Running
|
|
@@ -1366,6 +1395,33 @@ impl<C: 'static + Chip> ProcessStandard<'_, C> {
|
|
return Err(ProcessLoadError::MpuInvalidFlashLength);
|
|
}
|
|
|
|
+ // Allocate MPU region for the storage locations. The storage locations are currently
|
|
+ // readable by all processes due to lack of stable app id.
|
|
+ for storage_location in kernel.storage_locations() {
|
|
+ if chip
|
|
+ .mpu()
|
|
+ .allocate_region(
|
|
+ storage_location.address as *const u8,
|
|
+ storage_location.size,
|
|
+ storage_location.size,
|
|
+ mpu::Permissions::ReadOnly,
|
|
+ &mut mpu_config,
|
|
+ )
|
|
+ .is_some()
|
|
+ {
|
|
+ continue;
|
|
+ }
|
|
+ if config::CONFIG.debug_load_processes {
|
|
+ debug!(
|
|
+ "[!] flash=[{:#010X}:{:#010X}] process={:?} - couldn't allocate flash region",
|
|
+ storage_location.address,
|
|
+ storage_location.address + storage_location.size,
|
|
+ process_name
|
|
+ );
|
|
+ }
|
|
+ return Ok((None, remaining_memory));
|
|
+ }
|
|
+
|
|
// Determine how much space we need in the application's memory space
|
|
// just for kernel and grant state. We need to make sure we allocate
|
|
// enough memory just for that.
|