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
//! [![crates.io](https://img.shields.io/crates/v/persistent-buff)](https://crates.io/crates/persistent-buff) [![documentation](https://docs.rs/persistent-buff/badge.svg)](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();
    }
}