1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197
//! [](https://crates.io/crates/persistent-buff) [](https://docs.rs/persistent-buff)
//!
//! A buffer that persists between boot.
//! Inspired by [panic-persist](https://github.com/jamesmunns/panic-persist)
//!
//! A region in RAM is reseved for this buffer.
//! Your linker script should make sure the start and end of the buffer are outside of other sections
//!
//! ## Usage
//!
//! ### Linker script
//! You need to create a new reserved section for the buffer and make sure it's
//! outside of other sections to avoid zero initializations.
//!
//! #### Example
//! `memory.x` file before modification:
//!
//! ```text
//! MEMORY
//! {
//! /* NOTE 1 K = 1 KiBi = 1024 bytes */
//! FLASH : ORIGIN = 0x00000000, LENGTH = 1024K
//! RAM : ORIGIN = 0x20000000, LENGTH = 128K
//! }
//! ```
//!
//! `memory.x` file after modification to hold a 1K region:
//! ```text
//! MEMORY
//! {
//! /* NOTE 1 K = 1 KiBi = 1024 bytes */
//! FLASH : ORIGIN = 0x00000000, LENGTH = 1024K
//! RAM : ORIGIN = 0x20000000, LENGTH = 128K - 1K
//! PERSISTENT_BUFF: ORIGIN = ORIGIN(RAM) + LENGTH(RAM), LENGTH = 1K
//! }
//! _persistent_buff_start = ORIGIN(PERSISTENT_BUFF);
//! _persistent_buff_end = ORIGIN(PERSISTENT_BUFF) + LENGTH(PERSISTENT_BUFF);
//! ```
//!
//! ### Program
//!
//! ```ignore
//! #![no_std]
//!
//! #[entry]
//! fn main() -> ! {
//! let mut pbuff = persistent_buff::PersistentBuff::take_managed().unwrap();
//!
//! // Trivial way to initialize is to fill it with 0
//! let buff = pbuff.validate(|b| b.fill(0));
//!
//! buff[0] = (buff[0] % 255) + 1;
//! info!("Value is now {}", buff[0]);
//! }
//! ```
//!
//! ## License
//! Licensed under either of
//! - Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or
//! <http://www.apache.org/licenses/LICENSE-2.0>)
//!
//! - MIT license ([LICENSE-MIT](LICENSE-MIT) or <http://opensource.org/licenses/MIT>)
//!
//! at your option.
//!
//! ## Contribution
//! Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.
#![no_std]
#![no_main]
#![deny(missing_docs)]
use core::sync::atomic::{AtomicBool, Ordering};
const MAGIC_NUMBER: u32 = 0xFAB42069;
static mut PERSISTENT_BUFF_TAKEN: AtomicBool = AtomicBool::new(false);
/// Strut to request the persistent buff and manage it `safely`.
/// When acquiring the buffer you need to validate/init it to a known sate.
pub struct PersistentBuff {
magic: &'static mut [u8],
buff: &'static mut [u8],
}
impl PersistentBuff {
/// Take a managed version fo the persistent buff.
/// Allow to check if the buffer is valid or not before usage.
/// Note that vs the [Self::take] function, you will lose some bytes for storage of the marker.
pub fn take_managed() -> Option<Self> {
Self::take().map(|b| {
let (magic, buff) = b.split_at_mut(core::mem::size_of::<u32>());
Self { magic, buff }
})
}
/// Steal a managed version for the persistent buff without check.
/// See [Self::take_managed]
///
/// # Safety
/// Calling this function could allow to have two mutable reference to the same buffer.
/// Make sure to only have one reference at a time to avoid multiple mutable reference.
pub unsafe fn steal_managed() -> Self {
let b = Self::steal();
let (magic, buff) = b.split_at_mut(core::mem::size_of::<u32>());
Self { magic, buff }
}
/// Get the raw persistent slice.
pub fn take() -> Option<&'static mut [u8]> {
unsafe {
if PERSISTENT_BUFF_TAKEN.swap(true, Ordering::Relaxed) {
None
} else {
Some(Self::steal())
}
}
}
/// Steal the raw persistent slice.
/// Ignore if it was already taken or not.
///
/// # Safety
/// Calling this function could allow to have two mutable reference to the same buffer.
/// Make sure to only have one reference at a time to avoid multiple mutable reference.
pub unsafe fn steal() -> &'static mut [u8] {
PERSISTENT_BUFF_TAKEN.store(true, Ordering::SeqCst);
extern "C" {
static mut _persistent_buff_start: u8;
static mut _persistent_buff_end: u8;
}
let start = &mut _persistent_buff_start as *mut u8;
let end = &mut _persistent_buff_end as *mut u8;
let len = end as usize - start as usize;
core::slice::from_raw_parts_mut(start, len)
}
/// Mark the persistent buffer with valid data in it.
fn mark(&mut self) {
unsafe {
self.magic
.as_mut_ptr()
.cast::<u32>()
.write_unaligned(MAGIC_NUMBER);
}
}
/// Unmark the persistent buffer with valid data in it.
fn unmark(&mut self) {
unsafe {
self.magic.as_mut_ptr().cast::<u32>().write_unaligned(0);
}
}
/// Verify if the persistent buffer has valid data in it.
pub fn valid(&self) -> bool {
unsafe { self.magic.as_ptr().cast::<u32>().read_unaligned() == MAGIC_NUMBER }
}
/// Get the buffer if the data is valid, if not, return None
pub fn get(&mut self) -> Option<&mut [u8]> {
if self.valid() {
return Some(self.buff);
} else {
return None;
}
}
/// Force reset the buffer to a known state via the closure, mark as valid and return the buffer
pub fn reset<F>(&mut self, f: F) -> &mut [u8]
where
F: FnOnce(&mut [u8]),
{
f(self.buff);
self.mark();
self.buff
}
/// Check if the buffer is valid, if not call the provided closure.
/// Then mark the buffer as valid and initialize it to a known state.
/// This is to make sure the data in it is always "valid" and not garbage after a powerloss.
pub fn validate<F>(&mut self, f: F) -> &mut [u8]
where
F: FnOnce(&mut [u8]),
{
if !self.valid() {
f(self.buff)
}
self.mark();
self.buff
}
/// Mark the buffer as invalid
pub fn invalidate(&mut self) {
self.unmark();
}
}