1use 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#[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#[derive(Debug, Clone)]
54pub struct FirmwareHeader {
55 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 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
84impl FirmwareHeader {
86 #[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 New = 0xFF,
124 CommitPending = 0xFE,
126 Valid = 0xFC,
128 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
143pub 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 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#[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#[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}