diff --git a/esp-bootloader-esp-idf/src/ota.rs b/esp-bootloader-esp-idf/src/ota.rs index 325bd8800c7..71d17cd6865 100644 --- a/esp-bootloader-esp-idf/src/ota.rs +++ b/esp-bootloader-esp-idf/src/ota.rs @@ -206,7 +206,7 @@ impl<'a, 'd> Ota<'a, 'd> { } if flash.capacity() != 0x2000 - || flash.raw.partition_type() != PartitionType::Data(DataPartitionSubType::Ota) + || flash.partition_type != PartitionType::Data(DataPartitionSubType::Ota) { return Err(Error::InvalidPartition { expected_size: 0x2000, @@ -425,12 +425,9 @@ mod tests { fn ota_region<'a>( flash: &'a mut FlashStorage<'static>, - binary: &'a mut [u8; 32], + binary: [u8; 32], ) -> FlashRegion<'a, 'static> { - FlashRegion { - raw: PartitionEntry { binary }, - flash, - } + PartitionEntry { binary }.as_flash_region(flash) } fn init_ota_flash(flash: &mut FlashStorage<'static>) { @@ -445,12 +442,10 @@ mod tests { #[test] fn test_initial_state_and_next_slot() { - let mut binary = PARTITION_RAW; - let mut flash = FlashStorage::new(); init_ota_flash(&mut flash); - let mock_region = ota_region(&mut flash, &mut binary); + let mock_region = ota_region(&mut flash, PARTITION_RAW); let mut sut = Ota::new(mock_region, 2).unwrap(); assert_eq!( @@ -484,14 +479,12 @@ mod tests { #[test] fn test_slot0_valid_next_slot() { - let mut binary = PARTITION_RAW; - let mut flash = FlashStorage::new(); init_ota_flash(&mut flash); flash.write(0x0000, SLOT_COUNT_1_VALID).unwrap(); flash.write(0x1000, SLOT_INITIAL).unwrap(); - let mock_region = ota_region(&mut flash, &mut binary); + let mock_region = ota_region(&mut flash, PARTITION_RAW); let mut sut = Ota::new(mock_region, 2).unwrap(); assert_eq!( @@ -515,14 +508,12 @@ mod tests { #[test] fn test_slot1_new_next_slot() { - let mut binary = PARTITION_RAW; - let mut flash = FlashStorage::new(); init_ota_flash(&mut flash); flash.write(0x0000, SLOT_COUNT_1_VALID).unwrap(); flash.write(0x1000, SLOT_COUNT_2_NEW).unwrap(); - let mock_region = ota_region(&mut flash, &mut binary); + let mock_region = ota_region(&mut flash, PARTITION_RAW); let mut sut = Ota::new(mock_region, 2).unwrap(); assert_eq!( @@ -547,12 +538,10 @@ mod tests { #[test] fn test_multi_updates() { - let mut binary = PARTITION_RAW; - let mut flash = FlashStorage::new(); init_ota_flash(&mut flash); - let mock_region = ota_region(&mut flash, &mut binary); + let mock_region = ota_region(&mut flash, PARTITION_RAW); let mut sut = Ota::new(mock_region, 2).unwrap(); assert_eq!( @@ -605,12 +594,10 @@ mod tests { #[test] fn test_multi_updates_4_apps() { - let mut binary = PARTITION_RAW; - let mut flash = FlashStorage::new(); init_ota_flash(&mut flash); - let mock_region = ota_region(&mut flash, &mut binary); + let mock_region = ota_region(&mut flash, PARTITION_RAW); let mut sut = Ota::new(mock_region, 4).unwrap(); assert_eq!( @@ -682,12 +669,10 @@ mod tests { #[test] fn test_multi_updates_skip_parts() { - let mut binary = PARTITION_RAW; - let mut flash = FlashStorage::new(); init_ota_flash(&mut flash); - let mock_region = ota_region(&mut flash, &mut binary); + let mock_region = ota_region(&mut flash, PARTITION_RAW); let mut sut = Ota::new(mock_region, 16).unwrap(); assert_eq!( @@ -733,11 +718,10 @@ mod tests { #[test] fn test_read_erased_slot() { - let mut binary = PARTITION_RAW; let mut flash = FlashStorage::new(); init_ota_flash(&mut flash); - let mut region = ota_region(&mut flash, &mut binary); + let mut region = ota_region(&mut flash, PARTITION_RAW); let entry = OtaSelectEntry::read(&mut region, SLOT0_DATA_OFFSET).unwrap(); assert_eq!(entry.ota_seq, UNINITIALIZED_SEQUENCE); assert_eq!(entry.seq_label, [0xff; 20]); @@ -747,12 +731,11 @@ mod tests { #[test] fn test_read_valid_slot() { - let mut binary = PARTITION_RAW; let mut flash = FlashStorage::new(); init_ota_flash(&mut flash); flash.write(0x0000, SLOT_COUNT_1_VALID).unwrap(); - let mut region = ota_region(&mut flash, &mut binary); + let mut region = ota_region(&mut flash, PARTITION_RAW); let entry = OtaSelectEntry::read(&mut region, SLOT0_DATA_OFFSET).unwrap(); assert_eq!(entry.ota_seq, 1); assert_eq!(entry.ota_state, OtaImageState::Valid); @@ -760,7 +743,6 @@ mod tests { #[test] fn test_read_rejects_bad_crc() { - let mut binary = PARTITION_RAW; let mut flash = FlashStorage::new(); init_ota_flash(&mut flash); @@ -769,7 +751,7 @@ mod tests { slot[31] ^= 0xff; flash.write(0x0000, &slot).unwrap(); - let mut region = ota_region(&mut flash, &mut binary); + let mut region = ota_region(&mut flash, PARTITION_RAW); assert!(matches!( OtaSelectEntry::read(&mut region, SLOT0_DATA_OFFSET), Err(crate::partitions::Error::Invalid) @@ -778,7 +760,6 @@ mod tests { #[test] fn test_read_rejects_unknown_ota_state() { - let mut binary = PARTITION_RAW; let mut flash = FlashStorage::new(); init_ota_flash(&mut flash); @@ -790,7 +771,7 @@ mod tests { slot[28..32].copy_from_slice(&crc.to_le_bytes()); flash.write(0x0000, &slot).unwrap(); - let mut region = ota_region(&mut flash, &mut binary); + let mut region = ota_region(&mut flash, PARTITION_RAW); assert!(matches!( OtaSelectEntry::read(&mut region, SLOT0_DATA_OFFSET), Err(crate::partitions::Error::Invalid) @@ -799,7 +780,6 @@ mod tests { #[test] fn test_read_rejects_erased_seq_with_non_erased_state() { - let mut binary = PARTITION_RAW; let mut flash = FlashStorage::new(); init_ota_flash(&mut flash); @@ -807,7 +787,7 @@ mod tests { slot[24..28].copy_from_slice(&(OtaImageState::Valid as u32).to_le_bytes()); flash.write(0x0000, &slot).unwrap(); - let mut region = ota_region(&mut flash, &mut binary); + let mut region = ota_region(&mut flash, PARTITION_RAW); assert!(matches!( OtaSelectEntry::read(&mut region, SLOT0_DATA_OFFSET), Err(crate::partitions::Error::Invalid) @@ -816,7 +796,6 @@ mod tests { #[test] fn test_one_corrupt_slot_fails_current_app_partition() { - let mut binary = PARTITION_RAW; let mut flash = FlashStorage::new(); init_ota_flash(&mut flash); @@ -826,7 +805,7 @@ mod tests { flash.write(0x0000, &corrupt).unwrap(); flash.write(0x1000, SLOT_COUNT_2_NEW).unwrap(); - let region = ota_region(&mut flash, &mut binary); + let region = ota_region(&mut flash, PARTITION_RAW); let mut sut = Ota::new(region, 2).unwrap(); assert_eq!( sut.current_app_partition(), @@ -836,7 +815,6 @@ mod tests { #[test] fn test_reset_to_factory_after_corrupt_ota_data() { - let mut binary = PARTITION_RAW; let mut flash = FlashStorage::new(); init_ota_flash(&mut flash); @@ -846,7 +824,7 @@ mod tests { flash.write(0x0000, &corrupt).unwrap(); flash.write(0x1000, SLOT_COUNT_2_NEW).unwrap(); - let region = ota_region(&mut flash, &mut binary); + let region = ota_region(&mut flash, PARTITION_RAW); let mut sut = Ota::new(region, 2).unwrap(); assert_eq!( sut.current_app_partition(), diff --git a/esp-bootloader-esp-idf/src/partitions.rs b/esp-bootloader-esp-idf/src/partitions.rs index ded37121e3c..77fa78057b5 100644 --- a/esp-bootloader-esp-idf/src/partitions.rs +++ b/esp-bootloader-esp-idf/src/partitions.rs @@ -25,13 +25,13 @@ pub use crate::flash::FlashStorage; /// Represents a single partition entry. #[derive(Clone, Copy)] -pub struct PartitionEntry<'a> { - pub(crate) binary: &'a [u8; RAW_ENTRY_LEN], +pub struct PartitionEntry { + pub(crate) binary: [u8; RAW_ENTRY_LEN], } -impl<'a> PartitionEntry<'a> { - fn new(binary: &'a [u8; RAW_ENTRY_LEN]) -> Self { - Self { binary } +impl PartitionEntry { + fn new(binary: &[u8; RAW_ENTRY_LEN]) -> Self { + Self { binary: *binary } } /// The magic value of the entry. @@ -65,12 +65,12 @@ impl<'a> PartitionEntry<'a> { } /// The label of the partition. - pub fn label(&self) -> &'a [u8] { + pub fn label(&self) -> &[u8] { &self.binary[12..][..16] } /// The label of the partition as `&str`. - pub fn label_as_str(&self) -> &'a str { + pub fn label_as_str(&self) -> &str { let array = self.label(); let len = array .iter() @@ -138,12 +138,19 @@ impl<'a> PartitionEntry<'a> { /// Provides a "view" into the partition allowing to read/write the /// partition contents using the given [`FlashStorage`]. - pub fn as_flash_region<'d>(self, flash: &'a mut FlashStorage<'d>) -> FlashRegion<'a, 'd> { - FlashRegion { raw: self, flash } + pub fn as_flash_region<'a, 'd>(self, flash: &'a mut FlashStorage<'d>) -> FlashRegion<'a, 'd> { + FlashRegion { + offset: self.offset(), + len: self.len(), + partition_type: self.partition_type(), + read_only: self.is_read_only(), + encrypted: self.is_effectively_encrypted(), + flash, + } } } -impl core::fmt::Debug for PartitionEntry<'_> { +impl core::fmt::Debug for PartitionEntry { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { f.debug_struct("PartitionEntry") .field("magic", &self.magic()) @@ -160,7 +167,7 @@ impl core::fmt::Debug for PartitionEntry<'_> { } #[cfg(feature = "defmt")] -impl defmt::Format for PartitionEntry<'_> { +impl defmt::Format for PartitionEntry { fn format(&self, fmt: defmt::Formatter) { defmt::write!( fmt, @@ -250,26 +257,17 @@ impl<'a> PartitionTable<'a> { #[cfg(feature = "validation")] { - let (hash, index) = { - let mut i = 0; - loop { - if let Ok(entry) = raw_table.get_partition(i) { - if entry.magic() == MD5_MAGIC { - break (&entry.binary[16..][..16], i); - } - - i += 1; - if i >= raw_table.entries { - return Err(Error::Invalid); - } - } - } - }; + let index = raw_table + .binary + .iter() + .position(|entry| u16::from_le_bytes([entry[0], entry[1]]) == MD5_MAGIC) + .ok_or(Error::Invalid)?; + let hash = &raw_table.binary[index][16..][..16]; let mut hasher = crate::crypto::Md5::new(); - for i in 0..index { - hasher.update(&raw_table.binary[i]); + for entry in &raw_table.binary[..index] { + hasher.update(entry); } let calculated_hash = hasher.finalize(); @@ -314,7 +312,7 @@ impl<'a> PartitionTable<'a> { } /// Get a partition entry. - pub fn get_partition(&self, index: usize) -> Result, Error> { + pub fn get_partition(&self, index: usize) -> Result { if index >= self.entries { return Err(Error::OutOfBounds); } @@ -322,7 +320,7 @@ impl<'a> PartitionTable<'a> { } /// Get the first partition matching the given partition type. - pub fn find_partition(&self, pt: PartitionType) -> Result>, Error> { + pub fn find_partition(&self, pt: PartitionType) -> Result, Error> { for i in 0..self.entries { let entry = self.get_partition(i)?; if entry.partition_type() == pt { @@ -333,19 +331,19 @@ impl<'a> PartitionTable<'a> { } /// Returns an iterator over the partitions. - pub fn iter(&self) -> impl Iterator> { + pub fn iter(&self) -> impl Iterator { (0..self.entries).filter_map(|i| self.get_partition(i).ok()) } #[cfg(feature = "std")] /// Get the currently booted partition. - pub fn booted_partition(&self) -> Result>, Error> { + pub fn booted_partition(&self) -> Result, Error> { Err(Error::Invalid) } #[cfg(not(feature = "std"))] /// Get the currently booted partition. - pub fn booted_partition(&self) -> Result>, Error> { + pub fn booted_partition(&self) -> Result, Error> { // Read entry 0 from MMU to know which partition is mapped // // See @@ -614,18 +612,24 @@ fn read_partition_table_impl<'a, F: FlashAccess>( #[derive(Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct FlashRegion<'a, 'd> { - pub(crate) raw: PartitionEntry<'a>, + pub(crate) offset: u32, + pub(crate) len: u32, + pub(crate) partition_type: PartitionType, + pub(crate) read_only: bool, + /// Whether the partition is effectively encrypted (see + /// `PartitionEntry::is_effectively_encrypted`). + pub(crate) encrypted: bool, pub(crate) flash: &'a mut FlashStorage<'d>, } impl<'a, 'd> FlashRegion<'a, 'd> { /// Returns the size of the partition in bytes. pub fn partition_size(&self) -> usize { - self.raw.len() as _ + self.len as _ } fn range(&self) -> core::ops::Range { - self.raw.offset()..self.raw.offset() + self.raw.len() + self.offset..self.offset + self.len } fn in_range(&self, start: u32, len: usize) -> bool { @@ -634,13 +638,13 @@ impl<'a, 'd> FlashRegion<'a, 'd> { /// Read bytes from the partition. pub fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Error> { - let address = offset + self.raw.offset(); + let address = offset + self.offset; if !self.in_range(address, bytes.len()) { return Err(Error::OutOfBounds); } - if self.raw.is_effectively_encrypted() { + if self.encrypted { self.flash.flash_read_encrypted(address, bytes) } else { self.flash.flash_read(address, bytes) @@ -649,9 +653,9 @@ impl<'a, 'd> FlashRegion<'a, 'd> { /// Write bytes to the partition. pub fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Error> { - let address = offset + self.raw.offset(); + let address = offset + self.offset; - if self.raw.is_read_only() { + if self.read_only { return Err(Error::WriteProtected); } @@ -659,7 +663,7 @@ impl<'a, 'd> FlashRegion<'a, 'd> { return Err(Error::OutOfBounds); } - if self.raw.is_effectively_encrypted() { + if self.encrypted { self.flash.flash_write_encrypted(address, bytes) } else { self.flash.flash_write(address, bytes) @@ -675,10 +679,10 @@ impl<'a, 'd> FlashRegion<'a, 'd> { /// /// Addresses are relative to the partition start. pub fn erase(&mut self, from: u32, to: u32) -> Result<(), Error> { - let address_from = from + self.raw.offset(); - let address_to = to + self.raw.offset(); + let address_from = from + self.offset; + let address_to = to + self.offset; - if self.raw.is_read_only() { + if self.read_only { return Err(Error::WriteProtected); } @@ -741,7 +745,7 @@ mod embedded_storage_traits { /// Returns [`Error::NotSupported`] if this partition is treated as encrypted (e.g. app /// partitions when flash encryption is enabled). pub fn as_nor_flash<'r>(&'r mut self) -> Result, Error> { - if self.raw.is_effectively_encrypted() { + if self.encrypted { return Err(Error::NotSupported); } @@ -756,7 +760,7 @@ mod embedded_storage_traits { pub fn as_nor_flash_encrypted<'r>( &'r mut self, ) -> Result, Error> { - if !self.raw.is_effectively_encrypted() { + if !self.encrypted { return Err(Error::NotSupported); } @@ -805,7 +809,7 @@ mod embedded_storage_traits { const READ_SIZE: usize = NOR_READ_SIZE; fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> { - let address = offset + self.region.raw.offset(); + let address = offset + self.region.offset; if !self.region.in_range(address, bytes.len()) { return Err(Error::OutOfBounds); @@ -828,9 +832,9 @@ mod embedded_storage_traits { } fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> { - let address = offset + self.region.raw.offset(); + let address = offset + self.region.offset; - if self.region.raw.is_read_only() { + if self.region.read_only { return Err(Error::WriteProtected); } @@ -852,7 +856,7 @@ mod embedded_storage_traits { const READ_SIZE: usize = NOR_READ_SIZE; fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> { - let address = offset + self.region.raw.offset(); + let address = offset + self.region.offset; if !self.region.in_range(address, bytes.len()) { return Err(Error::OutOfBounds); @@ -875,9 +879,9 @@ mod embedded_storage_traits { } fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> { - let address = offset + self.region.raw.offset(); + let address = offset + self.region.offset; - if self.region.raw.is_read_only() { + if self.region.read_only { return Err(Error::WriteProtected); } @@ -1088,7 +1092,7 @@ mod storage_tests { .unwrap() .unwrap(); let mut nvs_partition = nvs.as_flash_region(&mut storage); - assert_eq!(nvs_partition.raw.offset(), 36864); + assert_eq!(nvs_partition.offset, 36864); assert_eq!(nvs_partition.capacity(), 24576); @@ -1114,7 +1118,7 @@ mod storage_tests { .unwrap() .unwrap(); let mut nvs_partition = nvs.as_flash_region(&mut storage); - assert_eq!(nvs_partition.raw.offset(), 36864); + assert_eq!(nvs_partition.offset, 36864); assert_eq!(nvs_partition.capacity(), 24576);