Do not use writeable flash regions for persistent storage
They don't play well with DFU.
This commit is contained in:
@@ -3,8 +3,10 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
MEMORY {
|
MEMORY {
|
||||||
/* The application region is 64 bytes (0x40) */
|
/* The application region is 64 bytes (0x40) and we reserve 0x40000 at the end
|
||||||
FLASH (rx) : ORIGIN = 0x00040040, LENGTH = 0x000BFFC0
|
* of the flash for the persistent storage.
|
||||||
|
*/
|
||||||
|
FLASH (rx) : ORIGIN = 0x00040040, LENGTH = 0x0007FFC0
|
||||||
SRAM (rwx) : ORIGIN = 0x20020000, LENGTH = 128K
|
SRAM (rwx) : ORIGIN = 0x20020000, LENGTH = 128K
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -106,7 +106,7 @@ index 5abd2d84..5a726fdb 100644
|
|||||||
let word: u32 = (data[i + 0] as u32) << 0
|
let word: u32 = (data[i + 0] as u32) << 0
|
||||||
| (data[i + 1] as u32) << 8
|
| (data[i + 1] as u32) << 8
|
||||||
| (data[i + 2] as u32) << 16
|
| (data[i + 2] as u32) << 16
|
||||||
@@ -374,3 +386,178 @@ impl hil::flash::Flash for Nvmc {
|
@@ -374,3 +386,170 @@ impl hil::flash::Flash for Nvmc {
|
||||||
self.erase_page(page_number)
|
self.erase_page(page_number)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -189,11 +189,7 @@ index 5abd2d84..5a726fdb 100644
|
|||||||
+ /// Fails with `EINVAL` if any of the following conditions does not hold:
|
+ /// Fails with `EINVAL` if any of the following conditions does not hold:
|
||||||
+ /// - `ptr` must be word-aligned.
|
+ /// - `ptr` must be word-aligned.
|
||||||
+ /// - `slice.len()` must be word-aligned.
|
+ /// - `slice.len()` must be word-aligned.
|
||||||
+ /// - The slice starting at `ptr` of length `slice.len()` must fit in a writeable flash region.
|
+ fn write_slice(&self, ptr: usize, slice: &[u8]) -> ReturnCode {
|
||||||
+ fn write_slice(&self, appid: AppId, ptr: usize, slice: &[u8]) -> ReturnCode {
|
|
||||||
+ if !appid.in_writeable_flash_region(ptr, slice.len()) {
|
|
||||||
+ return ReturnCode::EINVAL;
|
|
||||||
+ }
|
|
||||||
+ if ptr & WORD_MASK != 0 || slice.len() & WORD_MASK != 0 {
|
+ if ptr & WORD_MASK != 0 || slice.len() & WORD_MASK != 0 {
|
||||||
+ return ReturnCode::EINVAL;
|
+ return ReturnCode::EINVAL;
|
||||||
+ }
|
+ }
|
||||||
@@ -217,11 +213,7 @@ index 5abd2d84..5a726fdb 100644
|
|||||||
+ ///
|
+ ///
|
||||||
+ /// Fails with `EINVAL` if any of the following conditions does not hold:
|
+ /// Fails with `EINVAL` if any of the following conditions does not hold:
|
||||||
+ /// - `ptr` must be page-aligned.
|
+ /// - `ptr` must be page-aligned.
|
||||||
+ /// - The slice starting at `ptr` of length `PAGE_SIZE` must fit in a writeable flash region.
|
+ fn erase_page(&self, ptr: usize) -> ReturnCode {
|
||||||
+ fn erase_page(&self, appid: AppId, ptr: usize) -> ReturnCode {
|
|
||||||
+ if !appid.in_writeable_flash_region(ptr, PAGE_SIZE) {
|
|
||||||
+ return ReturnCode::EINVAL;
|
|
||||||
+ }
|
|
||||||
+ if ptr & PAGE_MASK != 0 {
|
+ if ptr & PAGE_MASK != 0 {
|
||||||
+ return ReturnCode::EINVAL;
|
+ return ReturnCode::EINVAL;
|
||||||
+ }
|
+ }
|
||||||
@@ -257,11 +249,11 @@ index 5abd2d84..5a726fdb 100644
|
|||||||
+ None => return ReturnCode::EINVAL,
|
+ None => return ReturnCode::EINVAL,
|
||||||
+ Some(slice) => slice,
|
+ Some(slice) => slice,
|
||||||
+ };
|
+ };
|
||||||
+ self.write_slice(appid, ptr, slice.as_ref())
|
+ self.write_slice(ptr, slice.as_ref())
|
||||||
+ })
|
+ })
|
||||||
+ .unwrap_or_else(|err| err.into()),
|
+ .unwrap_or_else(|err| err.into()),
|
||||||
+
|
+
|
||||||
+ (3, ptr) => self.erase_page(appid, ptr),
|
+ (3, ptr) => self.erase_page(ptr),
|
||||||
+
|
+
|
||||||
+ _ => ReturnCode::ENOSUPPORT,
|
+ _ => ReturnCode::ENOSUPPORT,
|
||||||
+ }
|
+ }
|
||||||
@@ -285,39 +277,3 @@ index 5abd2d84..5a726fdb 100644
|
|||||||
+ }
|
+ }
|
||||||
+ }
|
+ }
|
||||||
+}
|
+}
|
||||||
diff --git a/kernel/src/callback.rs b/kernel/src/callback.rs
|
|
||||||
index c812e0bf..bd1613b3 100644
|
|
||||||
--- a/kernel/src/callback.rs
|
|
||||||
+++ b/kernel/src/callback.rs
|
|
||||||
@@ -130,6 +130,31 @@ impl AppId {
|
|
||||||
(start, end)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
+
|
|
||||||
+ pub fn in_writeable_flash_region(&self, ptr: usize, len: usize) -> bool {
|
|
||||||
+ self.kernel.process_map_or(false, *self, |process| {
|
|
||||||
+ let ptr = match ptr.checked_sub(process.flash_start() as usize) {
|
|
||||||
+ None => return false,
|
|
||||||
+ Some(ptr) => ptr,
|
|
||||||
+ };
|
|
||||||
+ (0..process.number_writeable_flash_regions()).any(|i| {
|
|
||||||
+ let (region_ptr, region_len) = process.get_writeable_flash_region(i);
|
|
||||||
+ let region_ptr = region_ptr as usize;
|
|
||||||
+ let region_len = region_len as usize;
|
|
||||||
+ // We want to check the 2 following inequalities:
|
|
||||||
+ // (1) `region_ptr <= ptr`
|
|
||||||
+ // (2) `ptr + len <= region_ptr + region_len`
|
|
||||||
+ // However, the second one may overflow written as is. We introduce a third
|
|
||||||
+ // inequality to solve this issue:
|
|
||||||
+ // (3) `len <= region_len`
|
|
||||||
+ // Using this third inequality, we can rewrite the second one as:
|
|
||||||
+ // (4) `ptr - region_ptr <= region_len - len`
|
|
||||||
+ // This fourth inequality is equivalent to the second one but doesn't overflow when
|
|
||||||
+ // the first and third inequalities hold.
|
|
||||||
+ region_ptr <= ptr && len <= region_len && ptr - region_ptr <= region_len - len
|
|
||||||
+ })
|
|
||||||
+ })
|
|
||||||
+ }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Type to uniquely identify a callback subscription across all drivers.
|
|
||||||
|
|||||||
@@ -138,12 +138,15 @@ const PAGE_SIZE: usize = 0x100;
|
|||||||
#[cfg(not(feature = "ram_storage"))]
|
#[cfg(not(feature = "ram_storage"))]
|
||||||
const PAGE_SIZE: usize = 0x1000;
|
const PAGE_SIZE: usize = 0x1000;
|
||||||
|
|
||||||
|
// We have the following layout:
|
||||||
|
// 0x00000-0x2ffff: Tock
|
||||||
|
// 0x30000-0x3ffff: Padding
|
||||||
|
// 0x40000-0xbffff: App
|
||||||
|
// 0xc0000-0xfffff: Store
|
||||||
|
const STORE_ADDR: usize = 0xC0000;
|
||||||
|
const STORE_SIZE_LIMIT: usize = 0x40000;
|
||||||
const STORE_SIZE: usize = NUM_PAGES * PAGE_SIZE;
|
const STORE_SIZE: usize = NUM_PAGES * PAGE_SIZE;
|
||||||
|
|
||||||
#[cfg(not(any(test, feature = "ram_storage")))]
|
|
||||||
#[link_section = ".app_state"]
|
|
||||||
static STORE: [u8; STORE_SIZE] = [0xff; STORE_SIZE];
|
|
||||||
|
|
||||||
impl PersistentStore {
|
impl PersistentStore {
|
||||||
/// Gives access to the persistent store.
|
/// Gives access to the persistent store.
|
||||||
///
|
///
|
||||||
@@ -164,9 +167,11 @@ impl PersistentStore {
|
|||||||
|
|
||||||
#[cfg(not(any(test, feature = "ram_storage")))]
|
#[cfg(not(any(test, feature = "ram_storage")))]
|
||||||
fn new_prod_storage() -> Storage {
|
fn new_prod_storage() -> Storage {
|
||||||
|
// This should ideally be a compile-time assert, but Rust doesn't have native support.
|
||||||
|
assert!(STORE_SIZE <= STORE_SIZE_LIMIT);
|
||||||
let store = unsafe {
|
let store = unsafe {
|
||||||
// Safety: The store cannot alias because this function is called only once.
|
// Safety: The store cannot alias because this function is called only once.
|
||||||
core::slice::from_raw_parts_mut(STORE.as_ptr() as *mut u8, STORE_SIZE)
|
core::slice::from_raw_parts_mut(STORE_ADDR as *mut u8, STORE_SIZE)
|
||||||
};
|
};
|
||||||
unsafe {
|
unsafe {
|
||||||
// Safety: The store is in a writeable flash region.
|
// Safety: The store is in a writeable flash region.
|
||||||
|
|||||||
Reference in New Issue
Block a user