factbird_common/
memory_regions.rs

1//! # `memory_regions`
2//!
3//! This module automatically takes linker flags and makes functions which return slices mapping to
4//! the memory region.
5
6use core::convert::TryInto;
7use core::slice;
8use core::{cmp::Ordering, fmt::Debug};
9
10use crate::HardwareRevision;
11
12#[cfg(not(feature = "firmware"))]
13include!(concat!(env!("OUT_DIR"), "/memory_map.rs"));
14
15/// Contains the version of a firmware
16#[derive(Debug, Copy, Clone, PartialEq)]
17pub struct FirmwareVersion {
18    pub semver_major: u8,
19    pub semver_minor: u8,
20    pub semver_patch: u8,
21}
22
23#[cfg(feature = "defmt")]
24impl defmt::Format for FirmwareVersion {
25    fn format(&self, fmt: defmt::Formatter) {
26        defmt::write!(
27            fmt,
28            "{=u8}.{=u8}.{=u8}",
29            self.semver_major,
30            self.semver_minor,
31            self.semver_patch
32        )
33    }
34}
35
36impl PartialOrd for FirmwareVersion {
37    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
38        match self.semver_major.cmp(&other.semver_major) {
39            Ordering::Equal => {}
40            ord => return Some(ord),
41        }
42
43        match self.semver_minor.cmp(&other.semver_minor) {
44            Ordering::Equal => {}
45            ord => return Some(ord),
46        }
47
48        Some(self.semver_patch.cmp(&other.semver_patch))
49    }
50}
51
52/// Contains the metadata of a firmware
53#[derive(Debug, Clone)]
54pub struct FirmwareHeader {
55    /// The CRC is an CRC32-IEEE
56    pub crc: u32,
57    pub firmware_size: u32,
58    pub version: FirmwareVersion,
59    pub hw_version: HardwareRevision,
60    pub git_sha: heapless::String<20>,
61}
62
63impl FirmwareVersion {
64    #[must_use]
65    pub const fn new(major: u8, minor: u8, patch: u8) -> Self {
66        // This might become more advanced in the future
67        Self {
68            semver_major: major,
69            semver_minor: minor,
70            semver_patch: patch,
71        }
72    }
73
74    #[must_use]
75    pub const fn as_bytes(self) -> [u8; 3] {
76        [
77            self.semver_patch as u8,
78            self.semver_minor as u8,
79            self.semver_major as u8,
80        ]
81    }
82}
83
84/// Layout of the header: | CRC32 (4b) | `FIRMWARE_SIZE` (4b) | VERSION (4b) | GIT_SHA (20b) | `HEADER_SIZE` - 32 0s |
85impl FirmwareHeader {
86    /// Creates a firmware header from a section of memory
87    #[must_use]
88    pub fn new_from_slice(s: &[u8]) -> Self {
89        let sha_len = s[15] as usize;
90        let git_sha = if sha_len < 20 {
91            heapless::String::from(core::str::from_utf8(&s[16..16 + sha_len]).unwrap_or(""))
92        } else {
93            heapless::String::new()
94        };
95
96        Self {
97            crc: u32::from_le_bytes(s[0..4].try_into().unwrap()),
98            firmware_size: u32::from_le_bytes(s[4..8].try_into().unwrap()),
99            hw_version: HardwareRevision::from(u32::from_le_bytes(s[8..12].try_into().unwrap())),
100            version: FirmwareVersion::new(s[14], s[13], s[12]),
101            git_sha,
102        }
103    }
104
105    #[must_use]
106    pub fn as_bytes(&self) -> [u8; 32] {
107        let mut ret = [0_u8; 32];
108        let sha = self.git_sha.as_bytes();
109        ret[0..4].copy_from_slice(&self.crc.to_le_bytes());
110        ret[4..8].copy_from_slice(&self.firmware_size.to_le_bytes());
111        ret[8..12].copy_from_slice(&(self.hw_version as u32).to_le_bytes());
112        ret[12..15].copy_from_slice(&self.version.as_bytes());
113        ret[15] = sha.len() as u8;
114        ret[16..16 + sha.len()].copy_from_slice(&sha);
115        ret
116    }
117}
118
119#[derive(Debug, Clone, Copy, Eq, PartialEq)]
120#[repr(C)]
121pub enum ImageFlag {
122    /// If the application image is running for the first time and never executed before.
123    New = 0xFF,
124    /// The application image is marked to execute for test boot.
125    CommitPending = 0xFE,
126    /// The application image is marked valid and committed.
127    Valid = 0xFC,
128    /// The application image is marked invalid.
129    Invalid = 0xF8,
130}
131
132impl From<u8> for ImageFlag {
133    fn from(v: u8) -> Self {
134        match v {
135            0xFF => Self::New,
136            0xFE => Self::CommitPending,
137            0xFC => Self::Valid,
138            _ => Self::Invalid,
139        }
140    }
141}
142
143/// Contains run-time metadata of the firmware, generated by OTA
144pub struct ImageDescriptor {
145    pub magic: [u8; 7],
146    pub image_flags: ImageFlag,
147    pub sequence_nr: u32,
148    pub signature_size: usize,
149    pub signature: [u8; 256],
150}
151
152impl Debug for ImageDescriptor {
153    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
154        if self.magic_valid() {
155            write!(
156                f,
157                "ImageDescriptor {{ image_flags: {:?}, sequence_nr: {}, signature_size: {} }}",
158                self.image_flags, self.sequence_nr, self.signature_size
159            )
160        } else {
161            write!(f, "ImageDescriptor {{ Invalid }}",)
162        }
163    }
164}
165
166const MAGIC: [u8; 7] = *b"@FB-DUO";
167
168impl ImageDescriptor {
169    pub fn new(sequence_nr: u32, signature_size: usize, signature: [u8; 256]) -> Self {
170        Self {
171            magic: MAGIC,
172            image_flags: ImageFlag::New,
173            sequence_nr,
174            signature_size,
175            signature,
176        }
177    }
178
179    /// Creates a firmware header from a section of memory
180    pub fn new_from_slice(s: &[u8]) -> Self {
181        Self {
182            magic: s[0..7].try_into().unwrap(),
183            image_flags: s[7].into(),
184            sequence_nr: u32::from_le_bytes(s[8..12].try_into().unwrap()),
185            signature_size: u32::from_le_bytes(s[12..16].try_into().unwrap()) as usize,
186            signature: s[16..272].try_into().unwrap(),
187        }
188    }
189
190    pub fn magic_valid(&self) -> bool {
191        self.magic == MAGIC
192    }
193
194    pub fn as_bytes(&self) -> [u8; 512] {
195        let mut ret = [0u8; 512];
196        ret[0..7].copy_from_slice(&self.magic);
197        ret[7] = self.image_flags as u8;
198        ret[8..12].copy_from_slice(&self.sequence_nr.to_le_bytes());
199        ret[12..16].copy_from_slice(&(self.signature_size as u32).to_le_bytes());
200        ret[16..272].copy_from_slice(&self.signature);
201        ret
202    }
203}
204
205/// The available flash regions in the chip
206#[derive(Debug, Copy, Clone)]
207pub enum FlashRegion {
208    Firmware,
209    UpdateFirmware,
210    Swap,
211}
212
213impl FlashRegion {
214    pub fn as_slice(&self) -> &'static [u8] {
215        unsafe {
216            slice::from_raw_parts(
217                self.start_addr() as *const u32 as *const u8,
218                self.len() as *const u32 as usize,
219            )
220        }
221    }
222
223    pub fn header(&self) -> FirmwareHeader {
224        FirmwareHeader::new_from_slice(unsafe {
225            slice::from_raw_parts((self.start_addr() + 0x200) as *const u8, header_len())
226        })
227    }
228
229    pub fn image_descriptor(&self) -> ImageDescriptor {
230        ImageDescriptor::new_from_slice(unsafe {
231            slice::from_raw_parts(self.start_addr() as *const u8, image_descriptor_len())
232        })
233    }
234
235    pub fn header_start_offset(&self) -> u32 {
236        self.start_offset() + image_descriptor_len() as u32
237    }
238
239    pub fn start_offset(&self) -> u32 {
240        self.start_addr() - 0x0800_0000
241    }
242
243    pub fn start_addr(&self) -> u32 {
244        #[cfg(feature = "firmware")]
245        {
246            extern "C" {
247                static __firmware_start__: u32;
248                static __fwupdate_start__: u32;
249                static __fwswap_start__: u32;
250            }
251            return match self {
252                Self::Firmware => unsafe { &__firmware_start__ as *const u32 as u32 },
253                Self::UpdateFirmware => unsafe { &__fwupdate_start__ as *const u32 as u32 },
254                Self::Swap => unsafe { &__fwswap_start__ as *const u32 as u32 },
255            };
256        }
257        #[cfg(not(feature = "firmware"))]
258        match self {
259            Self::Firmware => __firmware_start__,
260            Self::UpdateFirmware => __fwupdate_start__,
261            Self::Swap => __fwswap_start__,
262        }
263    }
264
265    pub fn end_offset(&self) -> u32 {
266        self.end_addr() - 0x08000000
267    }
268
269    pub fn end_addr(&self) -> u32 {
270        #[cfg(feature = "firmware")]
271        {
272            extern "C" {
273                static __firmware_end__: u32;
274                static __fwupdate_end__: u32;
275                static __fwswap_end__: u32;
276            }
277            return match self {
278                Self::Firmware => unsafe { &__firmware_end__ as *const u32 as u32 },
279                Self::UpdateFirmware => unsafe { &__fwupdate_end__ as *const u32 as u32 },
280                Self::Swap => unsafe { &__fwswap_end__ as *const u32 as u32 },
281            };
282        }
283
284        #[cfg(not(feature = "firmware"))]
285        match self {
286            Self::Firmware => __firmware_end__,
287            Self::UpdateFirmware => __fwupdate_end__,
288            Self::Swap => __fwswap_end__,
289        }
290    }
291
292    pub fn len(&self) -> usize {
293        (self.end_addr() - self.start_addr()) as usize
294    }
295
296    pub fn is_empty(&self) -> bool {
297        self.len() == 0
298    }
299}
300
301/// Get the size of the header
302#[inline(always)]
303pub fn full_header_len() -> usize {
304    #[cfg(feature = "firmware")]
305    {
306        extern "C" {
307            static __header_start__: u32;
308            static __header_end__: u32;
309        }
310
311        return unsafe {
312            &__header_end__ as *const u32 as usize - &__header_start__ as *const u32 as usize
313        };
314    }
315
316    #[cfg(not(feature = "firmware"))]
317    return (__header_end__ - __header_start__) as usize;
318}
319
320pub fn header_len() -> usize {
321    full_header_len() - image_descriptor_len()
322}
323
324pub fn image_descriptor_len() -> usize {
325    0x200
326}