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 UNINITALIZED_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: &'a mut 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(
169 flash: &'a mut FlashRegion<'a, F>,
170 ota_partition_count: usize,
171 ) -> Result<Ota<'a, F>, Error> {
172 if ota_partition_count == 0 || ota_partition_count > 16 {
173 return Err(Error::InvalidArgument);
174 }
175
176 if flash.capacity() != 0x2000
177 || flash.raw.partition_type() != PartitionType::Data(DataPartitionSubType::Ota)
178 {
179 return Err(Error::InvalidPartition {
180 expected_size: 0x2000,
181 expected_type: PartitionType::Data(DataPartitionSubType::Ota),
182 });
183 }
184
185 Ok(Ota {
186 flash,
187 ota_partition_count,
188 })
189 }
190
191 pub fn current_app_partition(&mut self) -> Result<AppPartitionSubType, Error> {
199 let (seq0, seq1) = self.get_slot_seq()?;
200
201 let slot = if seq0 == UNINITALIZED_SEQUENCE && seq1 == UNINITALIZED_SEQUENCE {
202 AppPartitionSubType::Factory
203 } else if seq0 == UNINITALIZED_SEQUENCE {
204 AppPartitionSubType::from_ota_app_number(
205 ((seq1 - 1) % self.ota_partition_count as u32) as u8,
206 )?
207 } else if seq1 == UNINITALIZED_SEQUENCE || seq0 > seq1 {
208 AppPartitionSubType::from_ota_app_number(
209 ((seq0 - 1) % self.ota_partition_count as u32) as u8,
210 )?
211 } else {
212 let counter = u32::max(seq0, seq1) - 1;
213 AppPartitionSubType::from_ota_app_number(
214 (counter % self.ota_partition_count as u32) as u8,
215 )?
216 };
217
218 Ok(slot)
219 }
220
221 fn get_slot_seq(&mut self) -> Result<(u32, u32), Error> {
222 let mut buffer1 = OtaSelectEntry::default();
223 let mut buffer2 = OtaSelectEntry::default();
224 self.flash.read(SLOT0_DATA_OFFSET, buffer1.as_bytes_mut())?;
225 self.flash.read(SLOT1_DATA_OFFSET, buffer2.as_bytes_mut())?;
226 let seq0 = buffer1.ota_seq;
227 let seq1 = buffer2.ota_seq;
228 Ok((seq0, seq1))
229 }
230
231 pub fn set_current_app_partition(&mut self, app: AppPartitionSubType) -> Result<(), Error> {
240 if app == AppPartitionSubType::Factory {
241 self.flash.write(SLOT0_DATA_OFFSET, &[0xffu8; 0x20])?;
242 self.flash.write(SLOT1_DATA_OFFSET, &[0xffu8; 0x20])?;
243 return Ok(());
244 }
245
246 if app == AppPartitionSubType::Test {
247 return Err(Error::InvalidArgument);
250 }
251
252 let ota_app_index = app.ota_app_number();
253 if ota_app_index >= self.ota_partition_count as u8 {
254 return Err(Error::InvalidArgument);
255 }
256
257 let current = self.current_app_partition()?;
258
259 if current != app {
261 let inc = if current == AppPartitionSubType::Factory {
270 (((app.ota_app_number()) as i32 + 1) + (self.ota_partition_count as i32)) as u32
271 % self.ota_partition_count as u32
272 } else {
273 ((((app.ota_app_number()) as i32) - ((current.ota_app_number()) as i32))
274 + (self.ota_partition_count as i32)) as u32
275 % self.ota_partition_count as u32
276 };
277
278 let slot = self.current_slot()?.next();
280
281 let (seq0, seq1) = self.get_slot_seq()?;
282 let new_seq = {
283 if seq0 == UNINITALIZED_SEQUENCE && seq1 == UNINITALIZED_SEQUENCE {
284 inc
286 } else if seq0 == UNINITALIZED_SEQUENCE {
287 seq1 + inc
289 } else if seq1 == UNINITALIZED_SEQUENCE {
290 seq0 + inc
292 } else {
293 u32::max(seq0, seq1) + inc
294 }
295 };
296
297 let crc = crate::crypto::Crc32::new();
298 let checksum = crc.crc(&new_seq.to_le_bytes());
299
300 let mut buffer = OtaSelectEntry::default();
301 self.flash.read(slot.offset(), buffer.as_bytes_mut())?;
302 buffer.ota_seq = new_seq;
303 buffer.crc = checksum;
304 self.flash.write(slot.offset(), buffer.as_bytes_mut())?;
305 }
306
307 Ok(())
308 }
309
310 fn current_slot(&mut self) -> Result<OtaDataSlot, Error> {
312 let (seq0, seq1) = self.get_slot_seq()?;
313
314 let slot = if seq0 == UNINITALIZED_SEQUENCE && seq1 == UNINITALIZED_SEQUENCE {
315 OtaDataSlot::None
316 } else if seq0 == UNINITALIZED_SEQUENCE {
317 OtaDataSlot::Slot1
318 } else if seq1 == UNINITALIZED_SEQUENCE || seq0 > seq1 {
319 OtaDataSlot::Slot0
320 } else {
321 OtaDataSlot::Slot1
322 };
323 Ok(slot)
324 }
325
326 pub fn set_current_ota_state(&mut self, state: OtaImageState) -> Result<(), Error> {
331 if let (UNINITALIZED_SEQUENCE, UNINITALIZED_SEQUENCE) = self.get_slot_seq()? {
332 Err(Error::InvalidState)
333 } else {
334 let offset = self.current_slot()?.offset();
335 let mut buffer = OtaSelectEntry::default();
336 self.flash.read(offset, buffer.as_bytes_mut())?;
337 buffer.ota_state = state;
338 self.flash.write(offset, buffer.as_bytes_mut())?;
339 Ok(())
340 }
341 }
342
343 pub fn current_ota_state(&mut self) -> Result<OtaImageState, Error> {
348 if let (UNINITALIZED_SEQUENCE, UNINITALIZED_SEQUENCE) = self.get_slot_seq()? {
349 Err(Error::InvalidState)
350 } else {
351 let offset = self.current_slot()?.offset();
352 let mut buffer = OtaSelectEntry::default();
353 self.flash.read(offset, buffer.as_bytes_mut())?;
354 Ok(buffer.ota_state)
355 }
356 }
357}
358
359#[cfg(test)]
360mod tests {
361 use super::*;
362 use crate::partitions::PartitionEntry;
363
364 struct MockFlash {
365 data: [u8; 0x2000],
366 }
367
368 impl embedded_storage::Storage for MockFlash {
369 fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> {
370 self.data[offset as usize..][..bytes.len()].copy_from_slice(bytes);
371 Ok(())
372 }
373 }
374
375 impl embedded_storage::ReadStorage for MockFlash {
376 type Error = crate::partitions::Error;
377 fn read(&mut self, offset: u32, buffer: &mut [u8]) -> Result<(), Self::Error> {
378 let l = buffer.len();
379 buffer[..l].copy_from_slice(&self.data[offset as usize..][..l]);
380 Ok(())
381 }
382
383 fn capacity(&self) -> usize {
384 unimplemented!()
385 }
386 }
387
388 const PARTITION_RAW: [u8; 32] = [
389 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, ];
397
398 const SLOT_INITIAL: &[u8] = &[
399 255u8, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
400 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
401 ];
402
403 const SLOT_COUNT_1_UNDEFINED: &[u8] = &[
404 1u8, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
405 255, 255, 255, 255, 255, 255, 255, 255, 255, 154, 152, 67, 71,
406 ];
407
408 const SLOT_COUNT_1_VALID: &[u8] = &[
409 1u8, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
410 255, 255, 255, 255, 255, 2, 0, 0, 0, 154, 152, 67, 71,
411 ];
412
413 const SLOT_COUNT_2_NEW: &[u8] = &[
414 2, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
415 255, 255, 255, 255, 0, 0, 0, 0, 116, 55, 246, 85,
416 ];
417
418 const SLOT_COUNT_3_PENDING: &[u8] = &[
419 3, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
420 255, 255, 255, 255, 1, 0, 0, 0, 17, 80, 74, 237,
421 ];
422
423 #[test]
424 fn test_initial_state_and_next_slot() {
425 let mut binary = PARTITION_RAW;
426
427 let mock_entry = PartitionEntry {
428 binary: &mut binary,
429 };
430
431 let mut mock_flash = MockFlash {
432 data: [0xff; 0x2000],
433 };
434
435 let mut mock_region = FlashRegion {
436 raw: &mock_entry,
437 flash: &mut mock_flash,
438 };
439
440 let mut sut = Ota::new(&mut mock_region, 2).unwrap();
441 assert_eq!(
442 sut.current_app_partition().unwrap(),
443 AppPartitionSubType::Factory
444 );
445 assert_eq!(
446 sut.current_ota_state(),
447 Err(crate::partitions::Error::InvalidState)
448 );
449 assert_eq!(
450 sut.set_current_ota_state(OtaImageState::New),
451 Err(crate::partitions::Error::InvalidState)
452 );
453 assert_eq!(
454 sut.current_ota_state(),
455 Err(crate::partitions::Error::InvalidState)
456 );
457
458 sut.set_current_app_partition(AppPartitionSubType::Ota0)
459 .unwrap();
460 assert_eq!(
461 sut.current_app_partition().unwrap(),
462 AppPartitionSubType::Ota0
463 );
464 assert_eq!(sut.current_ota_state(), Ok(OtaImageState::Undefined));
465
466 assert_eq!(SLOT_COUNT_1_UNDEFINED, &mock_flash.data[0x0000..][..0x20],);
467 assert_eq!(SLOT_INITIAL, &mock_flash.data[0x1000..][..0x20],);
468 }
469
470 #[test]
471 fn test_slot0_valid_next_slot() {
472 let mut binary = PARTITION_RAW;
473
474 let mock_entry = PartitionEntry {
475 binary: &mut binary,
476 };
477
478 let mut mock_flash = MockFlash {
479 data: [0xff; 0x2000],
480 };
481
482 mock_flash.data[0x0000..][..0x20].copy_from_slice(SLOT_COUNT_1_VALID);
483 mock_flash.data[0x1000..][..0x20].copy_from_slice(SLOT_INITIAL);
484
485 let mut mock_region = FlashRegion {
486 raw: &mock_entry,
487 flash: &mut mock_flash,
488 };
489
490 let mut sut = Ota::new(&mut mock_region, 2).unwrap();
491 assert_eq!(
492 sut.current_app_partition().unwrap(),
493 AppPartitionSubType::Ota0
494 );
495 assert_eq!(sut.current_ota_state(), Ok(OtaImageState::Valid));
496
497 sut.set_current_app_partition(AppPartitionSubType::Ota1)
498 .unwrap();
499 sut.set_current_ota_state(OtaImageState::New).unwrap();
500 assert_eq!(
501 sut.current_app_partition().unwrap(),
502 AppPartitionSubType::Ota1
503 );
504 assert_eq!(sut.current_ota_state(), Ok(OtaImageState::New));
505
506 assert_eq!(SLOT_COUNT_1_VALID, &mock_flash.data[0x0000..][..0x20],);
507 assert_eq!(SLOT_COUNT_2_NEW, &mock_flash.data[0x1000..][..0x20],);
508 }
509
510 #[test]
511 fn test_slot1_new_next_slot() {
512 let mut binary = PARTITION_RAW;
513
514 let mock_entry = PartitionEntry {
515 binary: &mut binary,
516 };
517
518 let mut mock_flash = MockFlash {
519 data: [0xff; 0x2000],
520 };
521
522 mock_flash.data[0x0000..][..0x20].copy_from_slice(SLOT_COUNT_1_VALID);
523 mock_flash.data[0x1000..][..0x20].copy_from_slice(SLOT_COUNT_2_NEW);
524
525 let mut mock_region = FlashRegion {
526 raw: &mock_entry,
527 flash: &mut mock_flash,
528 };
529
530 let mut sut = Ota::new(&mut mock_region, 2).unwrap();
531 assert_eq!(
532 sut.current_app_partition().unwrap(),
533 AppPartitionSubType::Ota1
534 );
535 assert_eq!(sut.current_ota_state(), Ok(OtaImageState::New));
536
537 sut.set_current_app_partition(AppPartitionSubType::Ota0)
538 .unwrap();
539 sut.set_current_ota_state(OtaImageState::PendingVerify)
540 .unwrap();
541 assert_eq!(
542 sut.current_app_partition().unwrap(),
543 AppPartitionSubType::Ota0
544 );
545 assert_eq!(sut.current_ota_state(), Ok(OtaImageState::PendingVerify));
546
547 assert_eq!(SLOT_COUNT_3_PENDING, &mock_flash.data[0x0000..][..0x20],);
548 assert_eq!(SLOT_COUNT_2_NEW, &mock_flash.data[0x1000..][..0x20],);
549 }
550
551 #[test]
552 fn test_multi_updates() {
553 let mut binary = PARTITION_RAW;
554
555 let mock_entry = PartitionEntry {
556 binary: &mut binary,
557 };
558
559 let mut mock_flash = MockFlash {
560 data: [0xff; 0x2000],
561 };
562
563 let mut mock_region = FlashRegion {
564 raw: &mock_entry,
565 flash: &mut mock_flash,
566 };
567
568 let mut sut = Ota::new(&mut mock_region, 2).unwrap();
569 assert_eq!(
570 sut.current_app_partition().unwrap(),
571 AppPartitionSubType::Factory
572 );
573 assert_eq!(
574 sut.current_ota_state(),
575 Err(crate::partitions::Error::InvalidState)
576 );
577
578 sut.set_current_app_partition(AppPartitionSubType::Ota0)
579 .unwrap();
580 sut.set_current_ota_state(OtaImageState::PendingVerify)
581 .unwrap();
582 assert_eq!(
583 sut.current_app_partition().unwrap(),
584 AppPartitionSubType::Ota0
585 );
586 assert_eq!(sut.current_ota_state(), Ok(OtaImageState::PendingVerify));
587
588 sut.set_current_app_partition(AppPartitionSubType::Ota1)
589 .unwrap();
590 sut.set_current_ota_state(OtaImageState::New).unwrap();
591 assert_eq!(
592 sut.current_app_partition().unwrap(),
593 AppPartitionSubType::Ota1
594 );
595 assert_eq!(sut.current_ota_state(), Ok(OtaImageState::New));
596
597 sut.set_current_app_partition(AppPartitionSubType::Ota0)
598 .unwrap();
599 sut.set_current_ota_state(OtaImageState::Aborted).unwrap();
600 assert_eq!(
601 sut.current_app_partition().unwrap(),
602 AppPartitionSubType::Ota0
603 );
604 assert_eq!(sut.current_ota_state(), Ok(OtaImageState::Aborted));
605
606 sut.set_current_app_partition(AppPartitionSubType::Ota0)
608 .unwrap();
609 sut.set_current_ota_state(OtaImageState::Valid).unwrap();
610 assert_eq!(
611 sut.current_app_partition().unwrap(),
612 AppPartitionSubType::Ota0
613 );
614 assert_eq!(sut.current_ota_state(), Ok(OtaImageState::Valid));
615 }
616
617 #[test]
618 fn test_multi_updates_4_apps() {
619 let mut binary = PARTITION_RAW;
620
621 let mock_entry = PartitionEntry {
622 binary: &mut binary,
623 };
624
625 let mut mock_flash = MockFlash {
626 data: [0xff; 0x2000],
627 };
628
629 let mut mock_region = FlashRegion {
630 raw: &mock_entry,
631 flash: &mut mock_flash,
632 };
633
634 let mut sut = Ota::new(&mut mock_region, 4).unwrap();
635 assert_eq!(
636 sut.current_app_partition().unwrap(),
637 AppPartitionSubType::Factory
638 );
639 assert_eq!(
640 sut.current_ota_state(),
641 Err(crate::partitions::Error::InvalidState)
642 );
643
644 sut.set_current_app_partition(AppPartitionSubType::Ota0)
645 .unwrap();
646 sut.set_current_ota_state(OtaImageState::PendingVerify)
647 .unwrap();
648 assert_eq!(
649 sut.current_app_partition().unwrap(),
650 AppPartitionSubType::Ota0
651 );
652 assert_eq!(sut.current_ota_state(), Ok(OtaImageState::PendingVerify));
653
654 sut.set_current_app_partition(AppPartitionSubType::Ota1)
655 .unwrap();
656 sut.set_current_ota_state(OtaImageState::New).unwrap();
657 assert_eq!(
658 sut.current_app_partition().unwrap(),
659 AppPartitionSubType::Ota1
660 );
661 assert_eq!(sut.current_ota_state(), Ok(OtaImageState::New));
662
663 sut.set_current_app_partition(AppPartitionSubType::Ota2)
664 .unwrap();
665 sut.set_current_ota_state(OtaImageState::Aborted).unwrap();
666 assert_eq!(
667 sut.current_app_partition().unwrap(),
668 AppPartitionSubType::Ota2
669 );
670 assert_eq!(sut.current_ota_state(), Ok(OtaImageState::Aborted));
671
672 sut.set_current_app_partition(AppPartitionSubType::Ota3)
673 .unwrap();
674 sut.set_current_ota_state(OtaImageState::Valid).unwrap();
675 assert_eq!(
676 sut.current_app_partition().unwrap(),
677 AppPartitionSubType::Ota3
678 );
679 assert_eq!(sut.current_ota_state(), Ok(OtaImageState::Valid));
680
681 sut.set_current_app_partition(AppPartitionSubType::Ota2)
683 .unwrap();
684 sut.set_current_ota_state(OtaImageState::Invalid).unwrap();
685 assert_eq!(
686 sut.current_app_partition().unwrap(),
687 AppPartitionSubType::Ota2
688 );
689 assert_eq!(sut.current_ota_state(), Ok(OtaImageState::Invalid));
690
691 assert_eq!(
692 sut.set_current_app_partition(AppPartitionSubType::Ota5),
693 Err(crate::partitions::Error::InvalidArgument)
694 );
695
696 assert_eq!(
697 sut.set_current_app_partition(AppPartitionSubType::Test),
698 Err(crate::partitions::Error::InvalidArgument)
699 );
700 }
701
702 #[test]
703 fn test_multi_updates_skip_parts() {
704 let mut binary = PARTITION_RAW;
705
706 let mock_entry = PartitionEntry {
707 binary: &mut binary,
708 };
709
710 let mut mock_flash = MockFlash {
711 data: [0xff; 0x2000],
712 };
713
714 let mut mock_region = FlashRegion {
715 raw: &mock_entry,
716 flash: &mut mock_flash,
717 };
718
719 let mut sut = Ota::new(&mut mock_region, 16).unwrap();
720 assert_eq!(
721 sut.current_app_partition().unwrap(),
722 AppPartitionSubType::Factory
723 );
724 assert_eq!(
725 sut.current_ota_state(),
726 Err(crate::partitions::Error::InvalidState)
727 );
728
729 sut.set_current_app_partition(AppPartitionSubType::Ota10)
730 .unwrap();
731 assert_eq!(
732 sut.current_app_partition().unwrap(),
733 AppPartitionSubType::Ota10
734 );
735 assert_eq!(sut.current_ota_state(), Ok(OtaImageState::Undefined));
736
737 sut.set_current_app_partition(AppPartitionSubType::Ota14)
738 .unwrap();
739 assert_eq!(
740 sut.current_app_partition().unwrap(),
741 AppPartitionSubType::Ota14
742 );
743 assert_eq!(sut.current_ota_state(), Ok(OtaImageState::Undefined));
744
745 sut.set_current_app_partition(AppPartitionSubType::Ota5)
746 .unwrap();
747 assert_eq!(
748 sut.current_app_partition().unwrap(),
749 AppPartitionSubType::Ota5
750 );
751 assert_eq!(sut.current_ota_state(), Ok(OtaImageState::Undefined));
752 }
753
754 #[test]
755 fn test_ota_slot_next() {
756 assert_eq!(OtaDataSlot::None.next(), OtaDataSlot::Slot0);
757 assert_eq!(OtaDataSlot::Slot0.next(), OtaDataSlot::Slot1);
758 assert_eq!(OtaDataSlot::Slot1.next(), OtaDataSlot::Slot0);
759 }
760}