From 6247098069db2db5f4fe5f928bc8d945d5b40f73 Mon Sep 17 00:00:00 2001
From: Julien Cretin <cretin@google.com>
Date: Wed, 29 Apr 2020 12:52:31 +0200
Subject: [PATCH] Do not use writeable flash regions for persistent storage

They don't play well with DFU.
---
 nrf52840dk_layout.ld                     |  6 ++-
 patches/tock/01-persistent-storage.patch | 54 +++---------------------
 src/ctap/storage.rs                      | 15 ++++---
 3 files changed, 19 insertions(+), 56 deletions(-)

diff --git a/nrf52840dk_layout.ld b/nrf52840dk_layout.ld
index 218b6bb..8292738 100644
--- a/nrf52840dk_layout.ld
+++ b/nrf52840dk_layout.ld
@@ -3,8 +3,10 @@
  */
 
 MEMORY {
-  /* The application region is 64 bytes (0x40) */
-  FLASH (rx) : ORIGIN = 0x00040040, LENGTH = 0x000BFFC0
+  /* The application region is 64 bytes (0x40) and we reserve 0x40000 at the end
+   * of the flash for the persistent storage.
+   */
+  FLASH (rx) : ORIGIN = 0x00040040, LENGTH = 0x0007FFC0
   SRAM (rwx) : ORIGIN = 0x20020000, LENGTH = 128K
 }
 
diff --git a/patches/tock/01-persistent-storage.patch b/patches/tock/01-persistent-storage.patch
index 57880c6..942e13d 100644
--- a/patches/tock/01-persistent-storage.patch
+++ b/patches/tock/01-persistent-storage.patch
@@ -106,7 +106,7 @@ index 5abd2d84..5a726fdb 100644
              let word: u32 = (data[i + 0] as u32) << 0
                  | (data[i + 1] as u32) << 8
                  | (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)
      }
  }
@@ -189,11 +189,7 @@ index 5abd2d84..5a726fdb 100644
 +    /// 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 a writeable flash region.
-+    fn write_slice(&self, appid: AppId, ptr: usize, slice: &[u8]) -> ReturnCode {
-+        if !appid.in_writeable_flash_region(ptr, slice.len()) {
-+            return ReturnCode::EINVAL;
-+        }
++    fn write_slice(&self, ptr: usize, slice: &[u8]) -> ReturnCode {
 +        if ptr & WORD_MASK != 0 || slice.len() & WORD_MASK != 0 {
 +            return ReturnCode::EINVAL;
 +        }
@@ -217,11 +213,7 @@ index 5abd2d84..5a726fdb 100644
 +    ///
 +    /// 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 a writeable flash region.
-+    fn erase_page(&self, appid: AppId, ptr: usize) -> ReturnCode {
-+        if !appid.in_writeable_flash_region(ptr, PAGE_SIZE) {
-+            return ReturnCode::EINVAL;
-+        }
++    fn erase_page(&self, ptr: usize) -> ReturnCode {
 +        if ptr & PAGE_MASK != 0 {
 +            return ReturnCode::EINVAL;
 +        }
@@ -257,11 +249,11 @@ index 5abd2d84..5a726fdb 100644
 +                        None => return ReturnCode::EINVAL,
 +                        Some(slice) => slice,
 +                    };
-+                    self.write_slice(appid, ptr, slice.as_ref())
++                    self.write_slice(ptr, slice.as_ref())
 +                })
 +                .unwrap_or_else(|err| err.into()),
 +
-+            (3, ptr) => self.erase_page(appid, ptr),
++            (3, ptr) => self.erase_page(ptr),
 +
 +            _ => 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.
diff --git a/src/ctap/storage.rs b/src/ctap/storage.rs
index f0a2ca4..3d0c4d2 100644
--- a/src/ctap/storage.rs
+++ b/src/ctap/storage.rs
@@ -138,12 +138,15 @@ const PAGE_SIZE: usize = 0x100;
 #[cfg(not(feature = "ram_storage"))]
 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;
 
-#[cfg(not(any(test, feature = "ram_storage")))]
-#[link_section = ".app_state"]
-static STORE: [u8; STORE_SIZE] = [0xff; STORE_SIZE];
-
 impl PersistentStore {
     /// Gives access to the persistent store.
     ///
@@ -164,9 +167,11 @@ impl PersistentStore {
 
     #[cfg(not(any(test, feature = "ram_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 {
             // 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 {
             // Safety: The store is in a writeable flash region.
-- 
GitLab