1use embedded_storage::{ReadStorage, Storage};
28
29use crate::partitions::{
30 AppPartitionSubType,
31 DataPartitionSubType,
32 Error,
33 FlashRegion,
34 PartitionType,
35};
36
37const SLOT0_DATA_OFFSET: u32 = 0x0000;
38const SLOT1_DATA_OFFSET: u32 = 0x1000;
39
40const UNINITIALIZED_SEQUENCE: u32 = 0xffffffff;
41
42#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, strum::FromRepr)]
44#[cfg_attr(feature = "defmt", derive(defmt::Format))]
45enum OtaDataSlot {
46 None,
48 Slot0,
50 Slot1,
52}
53
54impl OtaDataSlot {
55 fn next(&self) -> OtaDataSlot {
57 match self {
58 OtaDataSlot::None => OtaDataSlot::Slot0,
59 OtaDataSlot::Slot0 => OtaDataSlot::Slot1,
60 OtaDataSlot::Slot1 => OtaDataSlot::Slot0,
61 }
62 }
63
64 fn offset(&self) -> u32 {
65 match self {
66 OtaDataSlot::None => SLOT0_DATA_OFFSET,
67 OtaDataSlot::Slot0 => SLOT0_DATA_OFFSET,
68 OtaDataSlot::Slot1 => SLOT1_DATA_OFFSET,
69 }
70 }
71}
72
73#[derive(Debug, PartialEq, Eq, Clone, Copy, Default, Hash, strum::FromRepr)]
75#[cfg_attr(feature = "defmt", derive(defmt::Format))]
76#[repr(u32)]
77pub enum OtaImageState {
78 New = 0x0,
83
84 PendingVerify = 0x1,
88
89 Valid = 0x2,
92
93 Invalid = 0x3,
98
99 Aborted = 0x4,
103
104 #[default]
107 Undefined = 0xFFFFFFFF,
108}
109
110impl TryFrom<u32> for OtaImageState {
111 type Error = Error;
112
113 fn try_from(value: u32) -> Result<Self, Self::Error> {
114 OtaImageState::from_repr(value).ok_or(Error::Invalid)
115 }
116}
117
118#[derive(Debug, Clone, Copy, Default)]
121#[cfg_attr(feature = "defmt", derive(defmt::Format))]
122#[repr(C)]
123struct OtaSelectEntry {
124 pub ota_seq: u32,
126 pub seq_label: [u8; 20],
128 pub ota_state: OtaImageState,
130 pub crc: u32,
132}
133
134impl OtaSelectEntry {
135 fn as_bytes_mut(&mut self) -> &mut [u8; 0x20] {
136 debug_assert!(core::mem::size_of::<Self>() == 32);
137 unwrap!(
138 unsafe { core::slice::from_raw_parts_mut(self as *mut _ as *mut u8, 0x20) }.try_into()
139 )
140 }
141}
142
143#[derive(Debug)]
147#[cfg_attr(feature = "defmt", derive(defmt::Format))]
148pub struct Ota<'a, F>
149where
150 F: embedded_storage::Storage,
151{
152 flash: FlashRegion<'a, F>,
153 ota_partition_count: usize,
154}
155
156impl<'a, F> Ota<'a, F>
157where
158 F: embedded_storage::Storage,
159{
160 pub fn new(flash: FlashRegion<'a, F>, ota_partition_count: usize) -> Result<Ota<'a, F>, Error> {
169 if ota_partition_count == 0 || ota_partition_count > 16 {
170 return Err(Error::InvalidArgument);
171 }
172
173 if flash.capacity() != 0x2000
174 || flash.raw.partition_type() != PartitionType::Data(DataPartitionSubType::Ota)
175 {
176 return Err(Error::InvalidPartition {
177 expected_size: 0x2000,
178 expected_type: PartitionType::Data(DataPartitionSubType::Ota),
179 });
180 }
181
182 Ok(Ota {
183 flash,
184 ota_partition_count,
185 })
186 }
187
188 pub fn current_app_partition(&mut self) -> Result<AppPartitionSubType, Error> {
196 let (seq0, seq1) = self.get_slot_seq()?;
197
198 let slot = if seq0 == UNINITIALIZED_SEQUENCE && seq1 == UNINITIALIZED_SEQUENCE {
199 AppPartitionSubType::Factory
200 } else if seq0 == UNINITIALIZED_SEQUENCE {
201 AppPartitionSubType::from_ota_app_number(
202 ((seq1 - 1) % self.ota_partition_count as u32) as u8,
203 )?
204 } else if seq1 == UNINITIALIZED_SEQUENCE || seq0 > seq1 {
205 AppPartitionSubType::from_ota_app_number(
206 ((seq0 - 1) % self.ota_partition_count as u32) as u8,
207 )?
208 } else {
209 let counter = u32::max(seq0, seq1) - 1;
210 AppPartitionSubType::from_ota_app_number(
211 (counter % self.ota_partition_count as u32) as u8,
212 )?
213 };
214
215 Ok(slot)
216 }
217
218 fn get_slot_seq(&mut self) -> Result<(u32, u32), Error> {
219 let mut buffer1 = OtaSelectEntry::default();
220 let mut buffer2 = OtaSelectEntry::default();
221 self.flash.read(SLOT0_DATA_OFFSET, buffer1.as_bytes_mut())?;
222 self.flash.read(SLOT1_DATA_OFFSET, buffer2.as_bytes_mut())?;
223 let seq0 = buffer1.ota_seq;
224 let seq1 = buffer2.ota_seq;
225 Ok((seq0, seq1))
226 }
227
228 pub fn set_current_app_partition(&mut self, app: AppPartitionSubType) -> Result<(), Error> {
237 if app == AppPartitionSubType::Factory {
238 self.flash.write(SLOT0_DATA_OFFSET, &[0xffu8; 0x20])?;
239 self.flash.write(SLOT1_DATA_OFFSET, &[0xffu8; 0x20])?;
240 return Ok(());
241 }
242
243 if app == AppPartitionSubType::Test {
244 return Err(Error::InvalidArgument);
247 }
248
249 let ota_app_index = app.ota_app_number();
250 if ota_app_index >= self.ota_partition_count as u8 {
251 return Err(Error::InvalidArgument);
252 }
253
254 let current = self.current_app_partition()?;
255
256 if current != app {
258 let inc = if current == AppPartitionSubType::Factory {
267 (((app.ota_app_number()) as i32 + 1) + (self.ota_partition_count as i32)) as u32
268 % self.ota_partition_count as u32
269 } else {
270 ((((app.ota_app_number()) as i32) - ((current.ota_app_number()) as i32))
271 + (self.ota_partition_count as i32)) as u32
272 % self.ota_partition_count as u32
273 };
274
275 let slot = self.current_slot()?.next();
277
278 let (seq0, seq1) = self.get_slot_seq()?;
279 let new_seq = {
280 if seq0 == UNINITIALIZED_SEQUENCE && seq1 == UNINITIALIZED_SEQUENCE {
281 inc
283 } else if seq0 == UNINITIALIZED_SEQUENCE {
284 seq1 + inc
286 } else if seq1 == UNINITIALIZED_SEQUENCE {
287 seq0 + inc
289 } else {
290 u32::max(seq0, seq1) + inc
291 }
292 };
293
294 let crc = crate::crypto::Crc32::new();
295 let checksum = crc.crc(&new_seq.to_le_bytes());
296
297 let mut buffer = OtaSelectEntry::default();
298 self.flash.read(slot.offset(), buffer.as_bytes_mut())?;
299 buffer.ota_seq = new_seq;
300 buffer.crc = checksum;
301 self.flash.write(slot.offset(), buffer.as_bytes_mut())?;
302 }
303
304 Ok(())
305 }
306
307 fn current_slot(&mut self) -> Result<OtaDataSlot, Error> {
309 let (seq0, seq1) = self.get_slot_seq()?;
310
311 let slot = if seq0 == UNINITIALIZED_SEQUENCE && seq1 == UNINITIALIZED_SEQUENCE {
312 OtaDataSlot::None
313 } else if seq0 == UNINITIALIZED_SEQUENCE {
314 OtaDataSlot::Slot1
315 } else if seq1 == UNINITIALIZED_SEQUENCE || seq0 > seq1 {
316 OtaDataSlot::Slot0
317 } else {
318 OtaDataSlot::Slot1
319 };
320 Ok(slot)
321 }
322
323 pub fn set_current_ota_state(&mut self, state: OtaImageState) -> Result<(), Error> {
328 if let (UNINITIALIZED_SEQUENCE, UNINITIALIZED_SEQUENCE) = self.get_slot_seq()? {
329 Err(Error::InvalidState)
330 } else {
331 let offset = self.current_slot()?.offset();
332 let mut buffer = OtaSelectEntry::default();
333 self.flash.read(offset, buffer.as_bytes_mut())?;
334 buffer.ota_state = state;
335 self.flash.write(offset, buffer.as_bytes_mut())?;
336 Ok(())
337 }
338 }
339
340 pub fn current_ota_state(&mut self) -> Result<OtaImageState, Error> {
345 if let (UNINITIALIZED_SEQUENCE, UNINITIALIZED_SEQUENCE) = self.get_slot_seq()? {
346 Err(Error::InvalidState)
347 } else {
348 let offset = self.current_slot()?.offset();
349 let mut buffer = OtaSelectEntry::default();
350 self.flash.read(offset, buffer.as_bytes_mut())?;
351 Ok(buffer.ota_state)
352 }
353 }
354}
355
356#[cfg(test)]
357mod tests {
358 use super::*;
359 use crate::partitions::PartitionEntry;
360
361 struct MockFlash {
362 data: [u8; 0x2000],
363 }
364
365 impl embedded_storage::Storage for MockFlash {
366 fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> {
367 self.data[offset as usize..][..bytes.len()].copy_from_slice(bytes);
368 Ok(())
369 }
370 }
371
372 impl embedded_storage::ReadStorage for MockFlash {
373 type Error = crate::partitions::Error;
374 fn read(&mut self, offset: u32, buffer: &mut [u8]) -> Result<(), Self::Error> {
375 let l = buffer.len();
376 buffer[..l].copy_from_slice(&self.data[offset as usize..][..l]);
377 Ok(())
378 }
379
380 fn capacity(&self) -> usize {
381 unimplemented!()
382 }
383 }
384
385 const PARTITION_RAW: [u8; 32] = [
386 0xaa, 0x50, 1, 0, 0, 0, 0, 0, 0, 0x20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ];
394
395 const SLOT_INITIAL: &[u8] = &[
396 255u8, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
397 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
398 ];
399
400 const SLOT_COUNT_1_UNDEFINED: &[u8] = &[
401 1u8, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
402 255, 255, 255, 255, 255, 255, 255, 255, 255, 154, 152, 67, 71,
403 ];
404
405 const SLOT_COUNT_1_VALID: &[u8] = &[
406 1u8, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
407 255, 255, 255, 255, 255, 2, 0, 0, 0, 154, 152, 67, 71,
408 ];
409
410 const SLOT_COUNT_2_NEW: &[u8] = &[
411 2, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
412 255, 255, 255, 255, 0, 0, 0, 0, 116, 55, 246, 85,
413 ];
414
415 const SLOT_COUNT_3_PENDING: &[u8] = &[
416 3, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
417 255, 255, 255, 255, 1, 0, 0, 0, 17, 80, 74, 237,
418 ];
419
420 #[test]
421 fn test_initial_state_and_next_slot() {
422 let mut binary = PARTITION_RAW;
423
424 let mock_entry = PartitionEntry {
425 binary: &mut binary,
426 };
427
428 let mut mock_flash = MockFlash {
429 data: [0xff; 0x2000],
430 };
431
432 let mock_region = FlashRegion {
433 raw: mock_entry,
434 flash: &mut mock_flash,
435 };
436
437 let mut sut = Ota::new(mock_region, 2).unwrap();
438 assert_eq!(
439 sut.current_app_partition().unwrap(),
440 AppPartitionSubType::Factory
441 );
442 assert_eq!(
443 sut.current_ota_state(),
444 Err(crate::partitions::Error::InvalidState)
445 );
446 assert_eq!(
447 sut.set_current_ota_state(OtaImageState::New),
448 Err(crate::partitions::Error::InvalidState)
449 );
450 assert_eq!(
451 sut.current_ota_state(),
452 Err(crate::partitions::Error::InvalidState)
453 );
454
455 sut.set_current_app_partition(AppPartitionSubType::Ota0)
456 .unwrap();
457 assert_eq!(
458 sut.current_app_partition().unwrap(),
459 AppPartitionSubType::Ota0
460 );
461 assert_eq!(sut.current_ota_state(), Ok(OtaImageState::Undefined));
462
463 assert_eq!(SLOT_COUNT_1_UNDEFINED, &mock_flash.data[0x0000..][..0x20],);
464 assert_eq!(SLOT_INITIAL, &mock_flash.data[0x1000..][..0x20],);
465 }
466
467 #[test]
468 fn test_slot0_valid_next_slot() {
469 let mut binary = PARTITION_RAW;
470
471 let mock_entry = PartitionEntry {
472 binary: &mut binary,
473 };
474
475 let mut mock_flash = MockFlash {
476 data: [0xff; 0x2000],
477 };
478
479 mock_flash.data[0x0000..][..0x20].copy_from_slice(SLOT_COUNT_1_VALID);
480 mock_flash.data[0x1000..][..0x20].copy_from_slice(SLOT_INITIAL);
481
482 let mock_region = FlashRegion {
483 raw: mock_entry,
484 flash: &mut mock_flash,
485 };
486
487 let mut sut = Ota::new(mock_region, 2).unwrap();
488 assert_eq!(
489 sut.current_app_partition().unwrap(),
490 AppPartitionSubType::Ota0
491 );
492 assert_eq!(sut.current_ota_state(), Ok(OtaImageState::Valid));
493
494 sut.set_current_app_partition(AppPartitionSubType::Ota1)
495 .unwrap();
496 sut.set_current_ota_state(OtaImageState::New).unwrap();
497 assert_eq!(
498 sut.current_app_partition().unwrap(),
499 AppPartitionSubType::Ota1
500 );
501 assert_eq!(sut.current_ota_state(), Ok(OtaImageState::New));
502
503 assert_eq!(SLOT_COUNT_1_VALID, &mock_flash.data[0x0000..][..0x20],);
504 assert_eq!(SLOT_COUNT_2_NEW, &mock_flash.data[0x1000..][..0x20],);
505 }
506
507 #[test]
508 fn test_slot1_new_next_slot() {
509 let mut binary = PARTITION_RAW;
510
511 let mock_entry = PartitionEntry {
512 binary: &mut binary,
513 };
514
515 let mut mock_flash = MockFlash {
516 data: [0xff; 0x2000],
517 };
518
519 mock_flash.data[0x0000..][..0x20].copy_from_slice(SLOT_COUNT_1_VALID);
520 mock_flash.data[0x1000..][..0x20].copy_from_slice(SLOT_COUNT_2_NEW);
521
522 let mock_region = FlashRegion {
523 raw: mock_entry,
524 flash: &mut mock_flash,
525 };
526
527 let mut sut = Ota::new(mock_region, 2).unwrap();
528 assert_eq!(
529 sut.current_app_partition().unwrap(),
530 AppPartitionSubType::Ota1
531 );
532 assert_eq!(sut.current_ota_state(), Ok(OtaImageState::New));
533
534 sut.set_current_app_partition(AppPartitionSubType::Ota0)
535 .unwrap();
536 sut.set_current_ota_state(OtaImageState::PendingVerify)
537 .unwrap();
538 assert_eq!(
539 sut.current_app_partition().unwrap(),
540 AppPartitionSubType::Ota0
541 );
542 assert_eq!(sut.current_ota_state(), Ok(OtaImageState::PendingVerify));
543
544 assert_eq!(SLOT_COUNT_3_PENDING, &mock_flash.data[0x0000..][..0x20],);
545 assert_eq!(SLOT_COUNT_2_NEW, &mock_flash.data[0x1000..][..0x20],);
546 }
547
548 #[test]
549 fn test_multi_updates() {
550 let mut binary = PARTITION_RAW;
551
552 let mock_entry = PartitionEntry {
553 binary: &mut binary,
554 };
555
556 let mut mock_flash = MockFlash {
557 data: [0xff; 0x2000],
558 };
559
560 let mock_region = FlashRegion {
561 raw: mock_entry,
562 flash: &mut mock_flash,
563 };
564
565 let mut sut = Ota::new(mock_region, 2).unwrap();
566 assert_eq!(
567 sut.current_app_partition().unwrap(),
568 AppPartitionSubType::Factory
569 );
570 assert_eq!(
571 sut.current_ota_state(),
572 Err(crate::partitions::Error::InvalidState)
573 );
574
575 sut.set_current_app_partition(AppPartitionSubType::Ota0)
576 .unwrap();
577 sut.set_current_ota_state(OtaImageState::PendingVerify)
578 .unwrap();
579 assert_eq!(
580 sut.current_app_partition().unwrap(),
581 AppPartitionSubType::Ota0
582 );
583 assert_eq!(sut.current_ota_state(), Ok(OtaImageState::PendingVerify));
584
585 sut.set_current_app_partition(AppPartitionSubType::Ota1)
586 .unwrap();
587 sut.set_current_ota_state(OtaImageState::New).unwrap();
588 assert_eq!(
589 sut.current_app_partition().unwrap(),
590 AppPartitionSubType::Ota1
591 );
592 assert_eq!(sut.current_ota_state(), Ok(OtaImageState::New));
593
594 sut.set_current_app_partition(AppPartitionSubType::Ota0)
595 .unwrap();
596 sut.set_current_ota_state(OtaImageState::Aborted).unwrap();
597 assert_eq!(
598 sut.current_app_partition().unwrap(),
599 AppPartitionSubType::Ota0
600 );
601 assert_eq!(sut.current_ota_state(), Ok(OtaImageState::Aborted));
602
603 sut.set_current_app_partition(AppPartitionSubType::Ota0)
605 .unwrap();
606 sut.set_current_ota_state(OtaImageState::Valid).unwrap();
607 assert_eq!(
608 sut.current_app_partition().unwrap(),
609 AppPartitionSubType::Ota0
610 );
611 assert_eq!(sut.current_ota_state(), Ok(OtaImageState::Valid));
612 }
613
614 #[test]
615 fn test_multi_updates_4_apps() {
616 let mut binary = PARTITION_RAW;
617
618 let mock_entry = PartitionEntry {
619 binary: &mut binary,
620 };
621
622 let mut mock_flash = MockFlash {
623 data: [0xff; 0x2000],
624 };
625
626 let mock_region = FlashRegion {
627 raw: mock_entry,
628 flash: &mut mock_flash,
629 };
630
631 let mut sut = Ota::new(mock_region, 4).unwrap();
632 assert_eq!(
633 sut.current_app_partition().unwrap(),
634 AppPartitionSubType::Factory
635 );
636 assert_eq!(
637 sut.current_ota_state(),
638 Err(crate::partitions::Error::InvalidState)
639 );
640
641 sut.set_current_app_partition(AppPartitionSubType::Ota0)
642 .unwrap();
643 sut.set_current_ota_state(OtaImageState::PendingVerify)
644 .unwrap();
645 assert_eq!(
646 sut.current_app_partition().unwrap(),
647 AppPartitionSubType::Ota0
648 );
649 assert_eq!(sut.current_ota_state(), Ok(OtaImageState::PendingVerify));
650
651 sut.set_current_app_partition(AppPartitionSubType::Ota1)
652 .unwrap();
653 sut.set_current_ota_state(OtaImageState::New).unwrap();
654 assert_eq!(
655 sut.current_app_partition().unwrap(),
656 AppPartitionSubType::Ota1
657 );
658 assert_eq!(sut.current_ota_state(), Ok(OtaImageState::New));
659
660 sut.set_current_app_partition(AppPartitionSubType::Ota2)
661 .unwrap();
662 sut.set_current_ota_state(OtaImageState::Aborted).unwrap();
663 assert_eq!(
664 sut.current_app_partition().unwrap(),
665 AppPartitionSubType::Ota2
666 );
667 assert_eq!(sut.current_ota_state(), Ok(OtaImageState::Aborted));
668
669 sut.set_current_app_partition(AppPartitionSubType::Ota3)
670 .unwrap();
671 sut.set_current_ota_state(OtaImageState::Valid).unwrap();
672 assert_eq!(
673 sut.current_app_partition().unwrap(),
674 AppPartitionSubType::Ota3
675 );
676 assert_eq!(sut.current_ota_state(), Ok(OtaImageState::Valid));
677
678 sut.set_current_app_partition(AppPartitionSubType::Ota2)
680 .unwrap();
681 sut.set_current_ota_state(OtaImageState::Invalid).unwrap();
682 assert_eq!(
683 sut.current_app_partition().unwrap(),
684 AppPartitionSubType::Ota2
685 );
686 assert_eq!(sut.current_ota_state(), Ok(OtaImageState::Invalid));
687
688 assert_eq!(
689 sut.set_current_app_partition(AppPartitionSubType::Ota5),
690 Err(crate::partitions::Error::InvalidArgument)
691 );
692
693 assert_eq!(
694 sut.set_current_app_partition(AppPartitionSubType::Test),
695 Err(crate::partitions::Error::InvalidArgument)
696 );
697 }
698
699 #[test]
700 fn test_multi_updates_skip_parts() {
701 let mut binary = PARTITION_RAW;
702
703 let mock_entry = PartitionEntry {
704 binary: &mut binary,
705 };
706
707 let mut mock_flash = MockFlash {
708 data: [0xff; 0x2000],
709 };
710
711 let mock_region = FlashRegion {
712 raw: mock_entry,
713 flash: &mut mock_flash,
714 };
715
716 let mut sut = Ota::new(mock_region, 16).unwrap();
717 assert_eq!(
718 sut.current_app_partition().unwrap(),
719 AppPartitionSubType::Factory
720 );
721 assert_eq!(
722 sut.current_ota_state(),
723 Err(crate::partitions::Error::InvalidState)
724 );
725
726 sut.set_current_app_partition(AppPartitionSubType::Ota10)
727 .unwrap();
728 assert_eq!(
729 sut.current_app_partition().unwrap(),
730 AppPartitionSubType::Ota10
731 );
732 assert_eq!(sut.current_ota_state(), Ok(OtaImageState::Undefined));
733
734 sut.set_current_app_partition(AppPartitionSubType::Ota14)
735 .unwrap();
736 assert_eq!(
737 sut.current_app_partition().unwrap(),
738 AppPartitionSubType::Ota14
739 );
740 assert_eq!(sut.current_ota_state(), Ok(OtaImageState::Undefined));
741
742 sut.set_current_app_partition(AppPartitionSubType::Ota5)
743 .unwrap();
744 assert_eq!(
745 sut.current_app_partition().unwrap(),
746 AppPartitionSubType::Ota5
747 );
748 assert_eq!(sut.current_ota_state(), Ok(OtaImageState::Undefined));
749 }
750
751 #[test]
752 fn test_ota_slot_next() {
753 assert_eq!(OtaDataSlot::None.next(), OtaDataSlot::Slot0);
754 assert_eq!(OtaDataSlot::Slot0.next(), OtaDataSlot::Slot1);
755 assert_eq!(OtaDataSlot::Slot1.next(), OtaDataSlot::Slot0);
756 }
757}