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
//! [![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: *mut u32,
    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| Self {
            magic: b.as_mut_ptr().cast::<u32>(),
            buff: &mut b[core::mem::size_of::<u32>()..],
        })
    }

    /// 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();
        Self {
            magic: b.as_mut_ptr().cast::<u32>(),
            buff: &mut b[core::mem::size_of::<u32>()..],
        }
    }

    /// 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 = MAGIC_NUMBER;
        }
    }

    /// Verify if the persistent buffer has valid data in it.
    fn check(&self) -> bool {
        unsafe { *self.magic == MAGIC_NUMBER }
    }

    /// 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.check() {
            f(self.buff)
        }
        self.mark();
        self.buff
    }
}