1pub const PARTITION_TABLE_MAX_LEN: usize = 0xC00;
12
13const PARTITION_TABLE_OFFSET: u32 =
14 esp_config::esp_config_int!(u32, "ESP_BOOTLOADER_ESP_IDF_CONFIG_PARTITION_TABLE_OFFSET");
15
16const RAW_ENTRY_LEN: usize = 32;
17const ENTRY_MAGIC: u16 = 0x50aa;
18#[cfg(feature = "validation")]
19const MD5_MAGIC: u16 = 0xebeb;
20
21const OTA_SUBTYPE_OFFSET: u8 = 0x10;
22
23use crate::flash::FlashAccess;
24pub use crate::flash::FlashStorage;
25
26#[derive(Clone, Copy)]
28pub struct PartitionEntry {
29 pub(crate) binary: [u8; RAW_ENTRY_LEN],
30}
31
32impl PartitionEntry {
33 fn new(binary: &[u8; RAW_ENTRY_LEN]) -> Self {
34 Self { binary: *binary }
35 }
36
37 pub fn magic(&self) -> u16 {
39 u16::from_le_bytes(unwrap!(self.binary[..2].try_into()))
40 }
41
42 pub fn raw_type(&self) -> u8 {
44 self.binary[2]
45 }
46
47 pub fn raw_subtype(&self) -> u8 {
49 self.binary[3]
50 }
51
52 pub fn offset(&self) -> u32 {
54 u32::from_le_bytes(unwrap!(self.binary[4..][..4].try_into()))
55 }
56
57 pub fn len(&self) -> u32 {
59 u32::from_le_bytes(unwrap!(self.binary[8..][..4].try_into()))
60 }
61
62 pub fn is_empty(&self) -> bool {
64 self.len() == 0
65 }
66
67 pub fn label(&self) -> &[u8] {
69 &self.binary[12..][..16]
70 }
71
72 pub fn label_as_str(&self) -> &str {
74 let array = self.label();
75 let len = array
76 .iter()
77 .position(|b| *b == 0 || *b == 0xff)
78 .unwrap_or(array.len());
79 unsafe {
80 core::str::from_utf8_unchecked(core::slice::from_raw_parts(array.as_ptr().cast(), len))
81 }
82 }
83
84 pub fn flags(&self) -> u32 {
87 u32::from_le_bytes(unwrap!(self.binary[28..][..4].try_into()))
88 }
89
90 pub fn is_read_only(&self) -> bool {
92 self.flags() & 0b01 != 0
93 }
94
95 pub fn is_encrypted(&self) -> bool {
101 self.flags() & 0b10 != 0
102 }
103
104 pub(crate) fn is_effectively_encrypted(&self) -> bool {
108 #[cfg(feature = "std")]
109 let enabled = false;
110
111 #[cfg(not(feature = "std"))]
112 let enabled = esp_storage::flash_encryption();
113
114 enabled
115 && (self.is_encrypted()
116 || matches!(self.partition_type(), PartitionType::App(_))
117 || matches!(self.partition_type(), PartitionType::PartitionTable(_))
118 || matches!(
119 self.partition_type(),
120 PartitionType::Data(DataPartitionSubType::NvsKeys)
121 )
122 || matches!(
123 self.partition_type(),
124 PartitionType::Data(DataPartitionSubType::Ota)
125 ))
126 }
127
128 pub fn partition_type(&self) -> PartitionType {
130 match self.raw_type() {
131 0 => PartitionType::App(unwrap!(self.raw_subtype().try_into())),
132 1 => PartitionType::Data(unwrap!(self.raw_subtype().try_into())),
133 2 => PartitionType::Bootloader(unwrap!(self.raw_subtype().try_into())),
134 3 => PartitionType::PartitionTable(unwrap!(self.raw_subtype().try_into())),
135 _ => unreachable!(),
136 }
137 }
138
139 pub fn as_flash_region<'a, 'd>(self, flash: &'a mut FlashStorage<'d>) -> FlashRegion<'a, 'd> {
142 FlashRegion {
143 offset: self.offset(),
144 len: self.len(),
145 partition_type: self.partition_type(),
146 read_only: self.is_read_only(),
147 encrypted: self.is_effectively_encrypted(),
148 flash,
149 }
150 }
151
152 pub fn sha256(&self, flash: &mut FlashStorage<'_>) -> Result<[u8; 32], Error> {
162 if self.is_empty() {
163 return Err(Error::InvalidArgument);
164 }
165
166 let address = self.offset();
167 let encrypted = self.is_effectively_encrypted();
168 let mut size = self.len();
169
170 if matches!(
171 self.partition_type(),
172 PartitionType::App(_) | PartitionType::Bootloader(_)
173 ) {
174 let data = get_image_metadata(flash, address, size, encrypted)?;
175 if data.hash_appended {
176 let calc = sha256_flash_contents(
177 flash,
178 address,
179 data.image_len - PARTITION_HASH_LEN as u32,
180 encrypted,
181 )?;
182 if calc != data.image_digest {
183 return Err(Error::InvalidImage);
184 }
185 return Ok(data.image_digest);
186 }
187 size = data.image_len;
188 }
189
190 sha256_flash_contents(flash, address, size, encrypted)
191 }
192}
193
194impl core::fmt::Debug for PartitionEntry {
195 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
196 f.debug_struct("PartitionEntry")
197 .field("magic", &self.magic())
198 .field("raw_type", &self.raw_type())
199 .field("raw_subtype", &self.raw_subtype())
200 .field("offset", &self.offset())
201 .field("len", &self.len())
202 .field("label", &self.label_as_str())
203 .field("flags", &self.flags())
204 .field("is_read_only", &self.is_read_only())
205 .field("is_encrypted", &self.is_encrypted())
206 .finish()
207 }
208}
209
210#[cfg(feature = "defmt")]
211impl defmt::Format for PartitionEntry {
212 fn format(&self, fmt: defmt::Formatter) {
213 defmt::write!(
214 fmt,
215 "PartitionEntry (\
216 magic = {}, \
217 raw_type = {}, \
218 raw_subtype = {}, \
219 offset = {}, \
220 len = {}, \
221 label = {}, \
222 flags = {}, \
223 is_read_only = {}, \
224 is_encrypted = {}\
225 )",
226 self.magic(),
227 self.raw_type(),
228 self.raw_subtype(),
229 self.offset(),
230 self.len(),
231 self.label_as_str(),
232 self.flags(),
233 self.is_read_only(),
234 self.is_encrypted()
235 )
236 }
237}
238
239#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, strum::Display)]
241#[cfg_attr(feature = "defmt", derive(defmt::Format))]
242#[non_exhaustive]
243pub enum Error {
244 Invalid,
246 OutOfBounds,
248 StorageError,
250 WriteProtected,
252 InvalidPartition {
254 expected_size: usize,
255 expected_type: PartitionType,
256 },
257 InvalidState,
259 InvalidArgument,
261 NotSupported,
264 InvalidImage,
266}
267
268impl core::error::Error for Error {}
269
270#[derive(Debug)]
272#[cfg_attr(feature = "defmt", derive(defmt::Format))]
273pub struct PartitionTable<'a> {
274 binary: &'a [[u8; RAW_ENTRY_LEN]],
275 entries: usize,
276}
277
278impl<'a> PartitionTable<'a> {
279 fn new(binary: &'a [u8]) -> Result<Self, Error> {
280 if binary.len() > PARTITION_TABLE_MAX_LEN {
281 return Err(Error::Invalid);
282 }
283
284 let (binary, rem) = binary.as_chunks::<RAW_ENTRY_LEN>();
285 if !rem.is_empty() {
286 return Err(Error::Invalid);
287 }
288
289 if binary.is_empty() {
290 return Ok(Self {
291 binary: &[],
292 entries: 0,
293 });
294 }
295
296 let mut raw_table = Self {
297 binary,
298 entries: binary.len(),
299 };
300
301 #[cfg(feature = "validation")]
302 {
303 let index = raw_table
304 .binary
305 .iter()
306 .position(|entry| u16::from_le_bytes([entry[0], entry[1]]) == MD5_MAGIC)
307 .ok_or(Error::Invalid)?;
308 let hash = &raw_table.binary[index][16..][..16];
309
310 let mut hasher = crate::crypto::Md5::new();
311
312 for entry in &raw_table.binary[..index] {
313 hasher.update(entry);
314 }
315 let calculated_hash = hasher.finalize();
316
317 if calculated_hash != hash {
318 return Err(Error::Invalid);
319 }
320 }
321
322 let entries = {
323 let mut i = 0;
324 loop {
325 if let Ok(entry) = raw_table.get_partition(i) {
326 if entry.magic() != ENTRY_MAGIC {
327 break;
328 }
329
330 i += 1;
331
332 if i == raw_table.entries {
333 break;
334 }
335 } else {
336 return Err(Error::Invalid);
337 }
338 }
339 i
340 };
341
342 raw_table.entries = entries;
343
344 Ok(raw_table)
345 }
346
347 pub fn len(&self) -> usize {
349 self.entries
350 }
351
352 pub fn is_empty(&self) -> bool {
354 self.entries == 0
355 }
356
357 pub fn get_partition(&self, index: usize) -> Result<PartitionEntry, Error> {
359 if index >= self.entries {
360 return Err(Error::OutOfBounds);
361 }
362 Ok(PartitionEntry::new(&self.binary[index]))
363 }
364
365 pub fn find_partition(&self, pt: PartitionType) -> Result<Option<PartitionEntry>, Error> {
367 for i in 0..self.entries {
368 let entry = self.get_partition(i)?;
369 if entry.partition_type() == pt {
370 return Ok(Some(entry));
371 }
372 }
373 Ok(None)
374 }
375
376 pub fn iter(&self) -> impl Iterator<Item = PartitionEntry> {
378 (0..self.entries).filter_map(|i| self.get_partition(i).ok())
379 }
380
381 #[cfg(feature = "std")]
382 pub fn booted_partition(&self) -> Result<Option<PartitionEntry>, Error> {
384 Err(Error::Invalid)
385 }
386
387 #[cfg(not(feature = "std"))]
388 pub fn booted_partition(&self) -> Result<Option<PartitionEntry>, Error> {
390 cfg_select! {
394 feature = "esp32" => {
395 let paddr = unsafe { ((0x3FF10000 as *const u32).read_volatile() & 0xff) << 16 };
396 }
397 feature = "esp32s2" => {
398 let paddr = unsafe {
399 (((0x61801000 + 128 * 4) as *const u32).read_volatile() & 0xff) << 16
400 };
401 }
402 feature = "esp32s3" => {
403 let paddr = unsafe { ((0x600C5000 as *const u32).read_volatile() & 0xff) << 16 };
405 }
406 any(feature = "esp32c2", feature = "esp32c3") => {
407 let paddr = unsafe { ((0x600c5000 as *const u32).read_volatile() & 0xff) << 16 };
408 }
409 feature = "esp32p4" => {
410 let paddr = unsafe {
413 ((0x5008C000 + 0x380) as *mut u32).write_volatile(0); (((0x5008C000 + 0x37c) as *const u32).read_volatile() & 0xff) << 16 };
416 }
417 feature = "esp32s31" => {
418 let paddr = unsafe {
421 ((0x20500000 + 0x380) as *mut u32).write_volatile(0); (((0x20500000 + 0x37c) as *const u32).read_volatile() & 0x7ff) << 16 };
424 }
425 any(
426 feature = "esp32c5",
427 feature = "esp32c6",
428 feature = "esp32c61",
429 feature = "esp32h2"
430 ) => {
431 let paddr = unsafe {
432 ((0x60002000 + 0x380) as *mut u32).write_volatile(0);
433 (((0x60002000 + 0x37c) as *const u32).read_volatile() & 0xff) << 16
434 };
435 }
436 _ => {}
437 }
438
439 for id in 0..self.len() {
440 let entry = self.get_partition(id)?;
441 if entry.offset() == paddr {
442 return Ok(Some(entry));
443 }
444 }
445
446 Ok(None)
447 }
448}
449
450#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)]
452#[cfg_attr(feature = "defmt", derive(defmt::Format))]
453pub enum PartitionType {
454 App(AppPartitionSubType),
456 Data(DataPartitionSubType),
458 Bootloader(BootloaderPartitionSubType),
460 PartitionTable(PartitionTablePartitionSubType),
462}
463
464#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)]
466#[cfg_attr(feature = "defmt", derive(defmt::Format))]
467#[repr(u8)]
468pub enum RawPartitionType {
469 App = 0,
471 Data,
473 Bootloader,
475 PartitionTable,
477}
478
479#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, strum::FromRepr)]
481#[cfg_attr(feature = "defmt", derive(defmt::Format))]
482#[repr(u8)]
483pub enum AppPartitionSubType {
484 Factory = 0,
486 Ota0 = OTA_SUBTYPE_OFFSET,
488 Ota1,
490 Ota2,
492 Ota3,
494 Ota4,
496 Ota5,
498 Ota6,
500 Ota7,
502 Ota8,
504 Ota9,
506 Ota10,
508 Ota11,
510 Ota12,
512 Ota13,
514 Ota14,
516 Ota15,
518 Test,
520}
521
522impl AppPartitionSubType {
523 pub(crate) fn ota_app_number(&self) -> u8 {
524 *self as u8 - OTA_SUBTYPE_OFFSET
525 }
526
527 pub(crate) fn from_ota_app_number(number: u8) -> Result<Self, Error> {
528 if number > 16 {
529 return Err(Error::InvalidArgument);
530 }
531 Self::try_from(number + OTA_SUBTYPE_OFFSET)
532 }
533}
534
535impl TryFrom<u8> for AppPartitionSubType {
536 type Error = Error;
537
538 fn try_from(value: u8) -> Result<Self, Self::Error> {
539 AppPartitionSubType::from_repr(value).ok_or(Error::Invalid)
540 }
541}
542
543#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, strum::FromRepr)]
545#[cfg_attr(feature = "defmt", derive(defmt::Format))]
546#[repr(u8)]
547pub enum DataPartitionSubType {
548 Ota = 0,
552 Phy,
555 Nvs,
557 Coredump,
559 NvsKeys,
561 EfuseEm,
563 Undefined,
566 Fat = 0x81,
568 Spiffs = 0x82,
570 LittleFs = 0x83,
572}
573
574impl TryFrom<u8> for DataPartitionSubType {
575 type Error = Error;
576
577 fn try_from(value: u8) -> Result<Self, Self::Error> {
578 DataPartitionSubType::from_repr(value).ok_or(Error::Invalid)
579 }
580}
581
582#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, strum::FromRepr)]
584#[cfg_attr(feature = "defmt", derive(defmt::Format))]
585#[repr(u8)]
586pub enum BootloaderPartitionSubType {
587 Primary = 0,
589 Ota = 1,
592}
593
594impl TryFrom<u8> for BootloaderPartitionSubType {
595 type Error = Error;
596
597 fn try_from(value: u8) -> Result<Self, Self::Error> {
598 BootloaderPartitionSubType::from_repr(value).ok_or(Error::Invalid)
599 }
600}
601
602#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, strum::FromRepr)]
604#[cfg_attr(feature = "defmt", derive(defmt::Format))]
605#[repr(u8)]
606pub enum PartitionTablePartitionSubType {
607 Primary = 0,
609 Ota = 1,
612}
613
614impl TryFrom<u8> for PartitionTablePartitionSubType {
615 type Error = Error;
616
617 fn try_from(value: u8) -> Result<Self, Self::Error> {
618 PartitionTablePartitionSubType::from_repr(value).ok_or(Error::Invalid)
619 }
620}
621
622pub fn read_partition_table<'a, 'd>(
626 flash: &mut FlashStorage<'d>,
627 storage: &'a mut [u8],
628) -> Result<PartitionTable<'a>, Error> {
629 read_partition_table_impl(flash, storage)
630}
631
632fn read_partition_table_impl<'a, F: FlashAccess>(
633 flash: &mut F,
634 storage: &'a mut [u8],
635) -> Result<PartitionTable<'a>, Error> {
636 #[cfg(feature = "std")]
637 let enabled = false;
638
639 #[cfg(not(feature = "std"))]
640 let enabled = esp_storage::flash_encryption();
641
642 if enabled {
643 flash.flash_read_encrypted(PARTITION_TABLE_OFFSET, storage)?;
644 } else {
645 flash.flash_read(PARTITION_TABLE_OFFSET, storage)?;
646 }
647
648 PartitionTable::new(storage)
649}
650
651const PARTITION_HASH_LEN: usize = 32;
652const IMAGE_HEADER_MAGIC: u8 = 0xE9;
653const IMAGE_HEADER_LEN: u32 = 24;
654const IMAGE_MAX_SEGMENTS: u8 = 16;
655const IMAGE_MAX_FLASH_ADDR_SIZE: u32 = 16 * 1024 * 1024;
656
657struct ImageMetadata {
659 image_len: u32,
660 image_digest: [u8; PARTITION_HASH_LEN],
661 hash_appended: bool,
662}
663
664fn get_image_metadata<F: FlashAccess>(
671 flash: &mut F,
672 address: u32,
673 part_size: u32,
674 encrypted: bool,
675) -> Result<ImageMetadata, Error> {
676 if part_size == 0 || part_size > IMAGE_MAX_FLASH_ADDR_SIZE {
677 return Err(Error::InvalidArgument);
678 }
679
680 let mut hdr = [0u8; IMAGE_HEADER_LEN as usize];
682 flash_read(flash, address, &mut hdr, encrypted)?;
683 if hdr[0] != IMAGE_HEADER_MAGIC || hdr[1] > IMAGE_MAX_SEGMENTS {
685 return Err(Error::InvalidImage);
686 }
687
688 let mut image_len = IMAGE_HEADER_LEN;
689
690 for _ in 0..hdr[1] {
692 let mut seg = [0u8; 8];
693 flash_read(flash, address + image_len, &mut seg, encrypted)?;
694 let data_len = u32::from_le_bytes(unwrap!(seg[4..8].try_into()));
696 if data_len % 4 != 0 || data_len >= IMAGE_MAX_FLASH_ADDR_SIZE {
697 return Err(Error::InvalidImage);
698 }
699 image_len = image_len
700 .checked_add(8 + data_len)
701 .ok_or(Error::InvalidImage)?;
702 }
703
704 image_len = (image_len + 1 + 15) & !15;
707
708 let hash_appended = hdr[23] != 0;
710 let mut image_digest = [0u8; PARTITION_HASH_LEN];
711 if hash_appended {
712 flash_read(flash, address + image_len, &mut image_digest, encrypted)?;
713 image_len += PARTITION_HASH_LEN as u32;
714 }
715
716 if image_len > part_size {
717 return Err(Error::InvalidImage);
718 }
719
720 Ok(ImageMetadata {
721 image_len,
722 image_digest,
723 hash_appended,
724 })
725}
726
727fn flash_read<F: FlashAccess>(
728 flash: &mut F,
729 address: u32,
730 bytes: &mut [u8],
731 encrypted: bool,
732) -> Result<(), Error> {
733 if encrypted {
734 flash.flash_read_encrypted(address, bytes)
735 } else {
736 flash.flash_read(address, bytes)
737 }
738}
739
740fn sha256_flash_contents<F: FlashAccess>(
745 flash: &mut F,
746 mut flash_offset: u32,
747 mut len: u32,
748 encrypted: bool,
749) -> Result<[u8; PARTITION_HASH_LEN], Error> {
750 use sha2::{Digest, Sha256};
751
752 let mut hasher = Sha256::new();
753 let mut chunk = [0u8; 4096];
754
755 while len > 0 {
756 let n = len.min(chunk.len() as u32) as usize;
757 flash_read(flash, flash_offset, &mut chunk[..n], encrypted)?;
758 hasher.update(&chunk[..n]);
759 flash_offset += n as u32;
760 len -= n as u32;
761 }
762
763 Ok(hasher.finalize().into())
764}
765
766#[derive(Debug)]
771#[cfg_attr(feature = "defmt", derive(defmt::Format))]
772pub struct FlashRegion<'a, 'd> {
773 pub(crate) offset: u32,
774 pub(crate) len: u32,
775 pub(crate) partition_type: PartitionType,
776 pub(crate) read_only: bool,
777 pub(crate) encrypted: bool,
780 pub(crate) flash: &'a mut FlashStorage<'d>,
781}
782
783impl<'a, 'd> FlashRegion<'a, 'd> {
784 pub fn partition_size(&self) -> usize {
786 self.len as _
787 }
788
789 fn range(&self) -> core::ops::Range<u32> {
790 self.offset..self.offset + self.len
791 }
792
793 fn in_range(&self, start: u32, len: usize) -> bool {
794 self.range().contains(&start) && (start + len as u32 <= self.range().end)
795 }
796
797 pub fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Error> {
799 let address = offset + self.offset;
800
801 if !self.in_range(address, bytes.len()) {
802 return Err(Error::OutOfBounds);
803 }
804
805 if self.encrypted {
806 self.flash.flash_read_encrypted(address, bytes)
807 } else {
808 self.flash.flash_read(address, bytes)
809 }
810 }
811
812 pub fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Error> {
814 let address = offset + self.offset;
815
816 if self.read_only {
817 return Err(Error::WriteProtected);
818 }
819
820 if !self.in_range(address, bytes.len()) {
821 return Err(Error::OutOfBounds);
822 }
823
824 if self.encrypted {
825 self.flash.flash_write_encrypted(address, bytes)
826 } else {
827 self.flash.flash_write(address, bytes)
828 }
829 }
830
831 pub fn capacity(&self) -> usize {
833 self.partition_size()
834 }
835
836 pub fn erase(&mut self, from: u32, to: u32) -> Result<(), Error> {
840 let address_from = from + self.offset;
841 let address_to = to + self.offset;
842
843 if self.read_only {
844 return Err(Error::WriteProtected);
845 }
846
847 if from > to {
848 return Err(Error::OutOfBounds);
849 }
850
851 if !self.in_range(address_from, (address_to - address_from) as usize) {
852 return Err(Error::OutOfBounds);
853 }
854
855 self.flash.flash_erase(address_from, address_to)
856 }
857}
858
859#[cfg(feature = "embedded-storage")]
860pub struct NorFlashRegion<'r, 'a, 'd> {
862 region: &'r mut FlashRegion<'a, 'd>,
863}
864
865#[cfg(feature = "embedded-storage")]
866pub struct EncryptedNorFlashRegion<'r, 'a, 'd> {
871 region: &'r mut FlashRegion<'a, 'd>,
872}
873
874#[cfg(feature = "embedded-storage")]
875mod embedded_storage_traits {
876 use ::embedded_storage::{
877 ReadStorage,
878 Region,
879 Storage,
880 nor_flash::{
881 ErrorType,
882 MultiwriteNorFlash,
883 NorFlash,
884 NorFlashError,
885 NorFlashErrorKind,
886 ReadNorFlash,
887 },
888 };
889
890 use super::*;
891
892 const NOR_READ_SIZE: usize = <FlashStorage<'static> as FlashAccess>::READ_SIZE;
893 const NOR_WRITE_SIZE: usize = <FlashStorage<'static> as FlashAccess>::WRITE_SIZE;
894 const NOR_ERASE_SIZE: usize = <FlashStorage<'static> as FlashAccess>::ERASE_SIZE;
895 const ENCRYPTED_WRITE_SIZE: usize =
896 <FlashStorage<'static> as FlashAccess>::SECTOR_SIZE as usize;
897
898 impl<'a, 'd> FlashRegion<'a, 'd> {
899 pub fn as_nor_flash<'r>(&'r mut self) -> Result<NorFlashRegion<'r, 'a, 'd>, Error> {
906 if self.encrypted {
907 return Err(Error::NotSupported);
908 }
909
910 Ok(NorFlashRegion { region: self })
911 }
912
913 pub fn as_nor_flash_encrypted<'r>(
919 &'r mut self,
920 ) -> Result<EncryptedNorFlashRegion<'r, 'a, 'd>, Error> {
921 if !self.encrypted {
922 return Err(Error::NotSupported);
923 }
924
925 Ok(EncryptedNorFlashRegion { region: self })
926 }
927 }
928
929 impl Region for FlashRegion<'_, '_> {
930 fn contains(&self, address: u32) -> bool {
931 self.range().contains(&address)
932 }
933 }
934
935 impl ReadStorage for FlashRegion<'_, '_> {
936 type Error = Error;
937
938 fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> {
939 FlashRegion::read(self, offset, bytes)
940 }
941
942 fn capacity(&self) -> usize {
943 FlashRegion::capacity(self)
944 }
945 }
946
947 impl Storage for FlashRegion<'_, '_> {
948 fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> {
949 FlashRegion::write(self, offset, bytes)
950 }
951 }
952
953 impl NorFlashError for Error {
954 fn kind(&self) -> NorFlashErrorKind {
955 match self {
956 Error::OutOfBounds => NorFlashErrorKind::OutOfBounds,
957 _ => NorFlashErrorKind::Other,
958 }
959 }
960 }
961
962 impl ErrorType for NorFlashRegion<'_, '_, '_> {
963 type Error = Error;
964 }
965
966 impl ReadNorFlash for NorFlashRegion<'_, '_, '_> {
967 const READ_SIZE: usize = NOR_READ_SIZE;
968
969 fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> {
970 let address = offset + self.region.offset;
971
972 if !self.region.in_range(address, bytes.len()) {
973 return Err(Error::OutOfBounds);
974 }
975
976 self.region.flash.flash_read_nor(address, bytes)
977 }
978
979 fn capacity(&self) -> usize {
980 self.region.capacity()
981 }
982 }
983
984 impl NorFlash for NorFlashRegion<'_, '_, '_> {
985 const WRITE_SIZE: usize = NOR_WRITE_SIZE;
986 const ERASE_SIZE: usize = NOR_ERASE_SIZE;
987
988 fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> {
989 self.region.erase(from, to)
990 }
991
992 fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> {
993 let address = offset + self.region.offset;
994
995 if self.region.read_only {
996 return Err(Error::WriteProtected);
997 }
998
999 if !self.region.in_range(address, bytes.len()) {
1000 return Err(Error::OutOfBounds);
1001 }
1002
1003 self.region.flash.flash_write_nor(address, bytes)
1004 }
1005 }
1006
1007 impl MultiwriteNorFlash for NorFlashRegion<'_, '_, '_> {}
1008
1009 impl ErrorType for EncryptedNorFlashRegion<'_, '_, '_> {
1010 type Error = Error;
1011 }
1012
1013 impl ReadNorFlash for EncryptedNorFlashRegion<'_, '_, '_> {
1014 const READ_SIZE: usize = NOR_READ_SIZE;
1015
1016 fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> {
1017 let address = offset + self.region.offset;
1018
1019 if !self.region.in_range(address, bytes.len()) {
1020 return Err(Error::OutOfBounds);
1021 }
1022
1023 self.region.flash.flash_read_encrypted(address, bytes)
1024 }
1025
1026 fn capacity(&self) -> usize {
1027 self.region.capacity()
1028 }
1029 }
1030
1031 impl NorFlash for EncryptedNorFlashRegion<'_, '_, '_> {
1032 const WRITE_SIZE: usize = ENCRYPTED_WRITE_SIZE;
1033 const ERASE_SIZE: usize = NOR_ERASE_SIZE;
1034
1035 fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> {
1036 self.region.erase(from, to)
1037 }
1038
1039 fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> {
1040 let address = offset + self.region.offset;
1041
1042 if self.region.read_only {
1043 return Err(Error::WriteProtected);
1044 }
1045
1046 if !self.region.in_range(address, bytes.len()) {
1047 return Err(Error::OutOfBounds);
1048 }
1049
1050 self.region.flash.flash_write_encrypted(address, bytes)
1051 }
1052 }
1053}
1054
1055#[cfg(test)]
1056mod tests {
1057 use super::*;
1058
1059 static SIMPLE: &[u8] = include_bytes!("../testdata/single_factory_no_ota.bin");
1060 static OTA: &[u8] = include_bytes!("../testdata/factory_app_two_ota.bin");
1061
1062 #[test]
1063 fn read_simple() {
1064 let pt = PartitionTable::new(SIMPLE).unwrap();
1065
1066 assert_eq!(3, pt.len());
1067
1068 assert_eq!(1, pt.get_partition(0).unwrap().raw_type());
1069 assert_eq!(1, pt.get_partition(1).unwrap().raw_type());
1070 assert_eq!(0, pt.get_partition(2).unwrap().raw_type());
1071
1072 assert_eq!(2, pt.get_partition(0).unwrap().raw_subtype());
1073 assert_eq!(1, pt.get_partition(1).unwrap().raw_subtype());
1074 assert_eq!(0, pt.get_partition(2).unwrap().raw_subtype());
1075
1076 assert_eq!(
1077 PartitionType::Data(DataPartitionSubType::Nvs),
1078 pt.get_partition(0).unwrap().partition_type()
1079 );
1080 assert_eq!(
1081 PartitionType::Data(DataPartitionSubType::Phy),
1082 pt.get_partition(1).unwrap().partition_type()
1083 );
1084 assert_eq!(
1085 PartitionType::App(AppPartitionSubType::Factory),
1086 pt.get_partition(2).unwrap().partition_type()
1087 );
1088
1089 assert_eq!(0x9000, pt.get_partition(0).unwrap().offset());
1090 assert_eq!(0xf000, pt.get_partition(1).unwrap().offset());
1091 assert_eq!(0x10000, pt.get_partition(2).unwrap().offset());
1092
1093 assert_eq!(0x6000, pt.get_partition(0).unwrap().len());
1094 assert_eq!(0x1000, pt.get_partition(1).unwrap().len());
1095 assert_eq!(0x100000, pt.get_partition(2).unwrap().len());
1096
1097 assert_eq!("nvs", pt.get_partition(0).unwrap().label_as_str());
1098 assert_eq!("phy_init", pt.get_partition(1).unwrap().label_as_str());
1099 assert_eq!("factory", pt.get_partition(2).unwrap().label_as_str());
1100
1101 assert_eq!(false, pt.get_partition(0).unwrap().is_read_only());
1102 assert_eq!(false, pt.get_partition(1).unwrap().is_read_only());
1103 assert_eq!(false, pt.get_partition(2).unwrap().is_read_only());
1104
1105 assert_eq!(false, pt.get_partition(0).unwrap().is_encrypted());
1106 assert_eq!(false, pt.get_partition(1).unwrap().is_encrypted());
1107 assert_eq!(false, pt.get_partition(2).unwrap().is_encrypted());
1108 }
1109
1110 #[test]
1111 fn read_ota() {
1112 let pt = PartitionTable::new(OTA).unwrap();
1113
1114 assert_eq!(6, pt.len());
1115
1116 assert_eq!(1, pt.get_partition(0).unwrap().raw_type());
1117 assert_eq!(1, pt.get_partition(1).unwrap().raw_type());
1118 assert_eq!(1, pt.get_partition(2).unwrap().raw_type());
1119 assert_eq!(0, pt.get_partition(3).unwrap().raw_type());
1120 assert_eq!(0, pt.get_partition(4).unwrap().raw_type());
1121 assert_eq!(0, pt.get_partition(5).unwrap().raw_type());
1122
1123 assert_eq!(2, pt.get_partition(0).unwrap().raw_subtype());
1124 assert_eq!(0, pt.get_partition(1).unwrap().raw_subtype());
1125 assert_eq!(1, pt.get_partition(2).unwrap().raw_subtype());
1126 assert_eq!(0, pt.get_partition(3).unwrap().raw_subtype());
1127 assert_eq!(0x10, pt.get_partition(4).unwrap().raw_subtype());
1128 assert_eq!(0x11, pt.get_partition(5).unwrap().raw_subtype());
1129
1130 assert_eq!(
1131 PartitionType::Data(DataPartitionSubType::Nvs),
1132 pt.get_partition(0).unwrap().partition_type()
1133 );
1134 assert_eq!(
1135 PartitionType::Data(DataPartitionSubType::Ota),
1136 pt.get_partition(1).unwrap().partition_type()
1137 );
1138 assert_eq!(
1139 PartitionType::Data(DataPartitionSubType::Phy),
1140 pt.get_partition(2).unwrap().partition_type()
1141 );
1142 assert_eq!(
1143 PartitionType::App(AppPartitionSubType::Factory),
1144 pt.get_partition(3).unwrap().partition_type()
1145 );
1146 assert_eq!(
1147 PartitionType::App(AppPartitionSubType::Ota0),
1148 pt.get_partition(4).unwrap().partition_type()
1149 );
1150 assert_eq!(
1151 PartitionType::App(AppPartitionSubType::Ota1),
1152 pt.get_partition(5).unwrap().partition_type()
1153 );
1154
1155 assert_eq!(0x9000, pt.get_partition(0).unwrap().offset());
1156 assert_eq!(0xd000, pt.get_partition(1).unwrap().offset());
1157 assert_eq!(0xf000, pt.get_partition(2).unwrap().offset());
1158 assert_eq!(0x10000, pt.get_partition(3).unwrap().offset());
1159 assert_eq!(0x110000, pt.get_partition(4).unwrap().offset());
1160 assert_eq!(0x210000, pt.get_partition(5).unwrap().offset());
1161
1162 assert_eq!(0x4000, pt.get_partition(0).unwrap().len());
1163 assert_eq!(0x2000, pt.get_partition(1).unwrap().len());
1164 assert_eq!(0x1000, pt.get_partition(2).unwrap().len());
1165 assert_eq!(0x100000, pt.get_partition(3).unwrap().len());
1166 assert_eq!(0x100000, pt.get_partition(4).unwrap().len());
1167 assert_eq!(0x100000, pt.get_partition(5).unwrap().len());
1168
1169 assert_eq!("nvs", pt.get_partition(0).unwrap().label_as_str());
1170 assert_eq!("otadata", pt.get_partition(1).unwrap().label_as_str());
1171 assert_eq!("phy_init", pt.get_partition(2).unwrap().label_as_str());
1172 assert_eq!("factory", pt.get_partition(3).unwrap().label_as_str());
1173 assert_eq!("ota_0", pt.get_partition(4).unwrap().label_as_str());
1174 assert_eq!("ota_1", pt.get_partition(5).unwrap().label_as_str());
1175
1176 assert_eq!(false, pt.get_partition(0).unwrap().is_read_only());
1177 assert_eq!(false, pt.get_partition(1).unwrap().is_read_only());
1178 assert_eq!(false, pt.get_partition(2).unwrap().is_read_only());
1179 assert_eq!(false, pt.get_partition(3).unwrap().is_read_only());
1180 assert_eq!(false, pt.get_partition(4).unwrap().is_read_only());
1181 assert_eq!(false, pt.get_partition(5).unwrap().is_read_only());
1182
1183 assert_eq!(false, pt.get_partition(0).unwrap().is_encrypted());
1184 assert_eq!(false, pt.get_partition(1).unwrap().is_encrypted());
1185 assert_eq!(false, pt.get_partition(2).unwrap().is_encrypted());
1186 assert_eq!(false, pt.get_partition(3).unwrap().is_encrypted());
1187 assert_eq!(false, pt.get_partition(4).unwrap().is_encrypted());
1188 assert_eq!(false, pt.get_partition(5).unwrap().is_encrypted());
1189 }
1190
1191 #[test]
1192 fn empty_byte_array() {
1193 let pt = PartitionTable::new(&[]).unwrap();
1194
1195 assert_eq!(0, pt.len());
1196 assert!(matches!(pt.get_partition(0), Err(Error::OutOfBounds)));
1197 }
1198
1199 #[test]
1200 fn validation_fails_wo_hash() {
1201 assert!(matches!(
1202 PartitionTable::new(&SIMPLE[..RAW_ENTRY_LEN * 3]),
1203 Err(Error::Invalid)
1204 ));
1205 }
1206
1207 #[test]
1208 fn validation_fails_wo_hash_max_entries() {
1209 let mut data = [0u8; PARTITION_TABLE_MAX_LEN];
1210 for i in 0..96 {
1211 data[(i * RAW_ENTRY_LEN)..][..RAW_ENTRY_LEN].copy_from_slice(&SIMPLE[..32]);
1212 }
1213
1214 assert!(matches!(PartitionTable::new(&data), Err(Error::Invalid)));
1215 }
1216
1217 #[test]
1218 fn validation_succeeds_with_enough_entries() {
1219 assert_eq!(
1220 3,
1221 PartitionTable::new(&SIMPLE[..RAW_ENTRY_LEN * 4])
1222 .unwrap()
1223 .len()
1224 );
1225 }
1226}
1227
1228#[cfg(test)]
1229mod storage_tests {
1230 use super::*;
1231
1232 fn test_flash() -> FlashStorage<'static> {
1233 let mut flash = FlashStorage::new();
1234 let mut data = [23u8; 0x10000];
1235 data[PARTITION_TABLE_OFFSET as usize..][..PARTITION_TABLE_MAX_LEN]
1236 .copy_from_slice(include_bytes!("../testdata/single_factory_no_ota.bin"));
1237 flash.write(0, &data).unwrap();
1238 flash
1239 }
1240
1241 #[test]
1242 fn can_read_write_all_of_nvs() {
1243 let mut storage = test_flash();
1244
1245 let mut buffer = [0u8; PARTITION_TABLE_MAX_LEN];
1246 let pt = read_partition_table(&mut storage, &mut buffer).unwrap();
1247
1248 let nvs = pt
1249 .find_partition(PartitionType::Data(DataPartitionSubType::Nvs))
1250 .unwrap()
1251 .unwrap();
1252 let mut nvs_partition = nvs.as_flash_region(&mut storage);
1253 assert_eq!(nvs_partition.offset, 36864);
1254
1255 assert_eq!(nvs_partition.capacity(), 24576);
1256
1257 let mut buffer = [0u8; 24576];
1258 nvs_partition.read(0, &mut buffer).unwrap();
1259 assert!(buffer.iter().all(|v| *v == 23));
1260 buffer.fill(42);
1261 nvs_partition.write(0, &buffer).unwrap();
1262 let mut buffer = [0u8; 24576];
1263 nvs_partition.read(0, &mut buffer).unwrap();
1264 assert!(buffer.iter().all(|v| *v == 42));
1265 }
1266
1267 #[test]
1268 fn cannot_read_write_more_than_partition_size() {
1269 let mut storage = test_flash();
1270
1271 let mut buffer = [0u8; PARTITION_TABLE_MAX_LEN];
1272 let pt = read_partition_table(&mut storage, &mut buffer).unwrap();
1273
1274 let nvs = pt
1275 .find_partition(PartitionType::Data(DataPartitionSubType::Nvs))
1276 .unwrap()
1277 .unwrap();
1278 let mut nvs_partition = nvs.as_flash_region(&mut storage);
1279 assert_eq!(nvs_partition.offset, 36864);
1280
1281 assert_eq!(nvs_partition.capacity(), 24576);
1282
1283 let mut buffer = [0u8; 24577];
1284 assert!(nvs_partition.read(0, &mut buffer) == Err(Error::OutOfBounds));
1285 }
1286
1287 #[test]
1288 fn can_erase_up_to_partition_end() {
1289 let mut storage = test_flash();
1290
1291 let mut buffer = [0u8; PARTITION_TABLE_MAX_LEN];
1292 let pt = read_partition_table(&mut storage, &mut buffer).unwrap();
1293
1294 let nvs = pt
1295 .find_partition(PartitionType::Data(DataPartitionSubType::Nvs))
1296 .unwrap()
1297 .unwrap();
1298 let mut nvs_partition = nvs.as_flash_region(&mut storage);
1299
1300 let capacity = nvs_partition.capacity() as u32;
1301 assert_eq!(capacity, 24576);
1302
1303 nvs_partition.write(0, &[42u8; 24576]).unwrap();
1304
1305 nvs_partition.erase(capacity - 4096, capacity).unwrap();
1306 let mut buffer = [0u8; 4096];
1307 nvs_partition.read(capacity - 4096, &mut buffer).unwrap();
1308 assert!(buffer.iter().all(|v| *v == 0xff));
1309
1310 nvs_partition.erase(0, capacity).unwrap();
1311 let mut buffer = [0u8; 24576];
1312 nvs_partition.read(0, &mut buffer).unwrap();
1313 assert!(buffer.iter().all(|v| *v == 0xff));
1314 }
1315
1316 #[test]
1317 fn cannot_erase_out_of_bounds() {
1318 let mut storage = test_flash();
1319
1320 let mut buffer = [0u8; PARTITION_TABLE_MAX_LEN];
1321 let pt = read_partition_table(&mut storage, &mut buffer).unwrap();
1322
1323 let nvs = pt
1324 .find_partition(PartitionType::Data(DataPartitionSubType::Nvs))
1325 .unwrap()
1326 .unwrap();
1327 let mut nvs_partition = nvs.as_flash_region(&mut storage);
1328
1329 let capacity = nvs_partition.capacity() as u32;
1330
1331 assert!(nvs_partition.erase(0, capacity + 4096) == Err(Error::OutOfBounds));
1332 assert!(nvs_partition.erase(capacity, capacity + 4096) == Err(Error::OutOfBounds));
1333 assert!(nvs_partition.erase(4096, 0) == Err(Error::OutOfBounds));
1334 }
1335}
1336
1337#[cfg(test)]
1338mod sha256_tests {
1339 use super::*;
1340
1341 const NVS_DIGEST: [u8; 32] = [
1343 0xb0, 0x5b, 0x4f, 0x2c, 0xc2, 0xa7, 0x54, 0x25, 0x54, 0xfa, 0x32, 0x8b, 0xd0, 0x5d, 0x86,
1344 0x7f, 0x0c, 0x1d, 0xae, 0xed, 0x46, 0x48, 0x8e, 0x31, 0xb0, 0x0c, 0xb0, 0xaa, 0xe5, 0xb5,
1345 0x49, 0x81,
1346 ];
1347
1348 const IMAGE_DIGEST_WITH_HASH_FLAG: [u8; 32] = [
1350 0xb2, 0xb7, 0x64, 0x4a, 0x57, 0x62, 0x46, 0x05, 0xf7, 0xe4, 0xb1, 0xc3, 0xbf, 0x96, 0x5a,
1351 0x20, 0x87, 0x37, 0x3d, 0x7a, 0xc6, 0x2d, 0xf8, 0x6a, 0xcf, 0x2b, 0x1a, 0xcf, 0xe4, 0x8e,
1352 0xe8, 0xa0,
1353 ];
1354
1355 const IMAGE_DIGEST_WITHOUT_HASH_FLAG: [u8; 32] = [
1357 0x02, 0x50, 0xbb, 0x56, 0xe1, 0x91, 0xf6, 0x6d, 0xde, 0xf1, 0x5e, 0x2d, 0x7c, 0xb4, 0x48,
1358 0x23, 0x75, 0x36, 0x52, 0x54, 0x7f, 0xc3, 0xd9, 0xd5, 0x83, 0xaa, 0xca, 0x2e, 0xec, 0xfe,
1359 0x99, 0x00,
1360 ];
1361
1362 fn test_flash() -> FlashStorage<'static> {
1363 let mut flash = FlashStorage::new();
1364 let mut data = [0xffu8; 0x10000];
1365 data[PARTITION_TABLE_OFFSET as usize..][..PARTITION_TABLE_MAX_LEN]
1366 .copy_from_slice(include_bytes!("../testdata/single_factory_no_ota.bin"));
1367 flash.write(0, &data).unwrap();
1368 flash
1369 }
1370
1371 fn write_minimal_app_image(
1373 flash: &mut FlashStorage<'static>,
1374 offset: u32,
1375 hash_appended: bool,
1376 ) {
1377 let mut image = [0u8; 64];
1378 image[0] = IMAGE_HEADER_MAGIC;
1379 image[23] = u8::from(hash_appended);
1380 if hash_appended {
1382 image[32..64].copy_from_slice(&IMAGE_DIGEST_WITH_HASH_FLAG);
1383 flash.write(offset, &image).unwrap();
1384 } else {
1385 flash.write(offset, &image[..32]).unwrap();
1386 }
1387 }
1388
1389 #[test]
1390 fn sha256_of_data_partition_matches_known_digest() {
1391 let mut flash = test_flash();
1392
1393 let mut buffer = [0u8; PARTITION_TABLE_MAX_LEN];
1394 let pt = read_partition_table(&mut flash, &mut buffer).unwrap();
1395 let nvs = pt
1396 .find_partition(PartitionType::Data(DataPartitionSubType::Nvs))
1397 .unwrap()
1398 .unwrap();
1399
1400 nvs.as_flash_region(&mut flash)
1401 .write(0, &[0xa5u8; 0x6000])
1402 .unwrap();
1403
1404 assert_eq!(nvs.sha256(&mut flash).unwrap(), NVS_DIGEST);
1405 }
1406
1407 #[test]
1408 fn sha256_of_app_with_appended_hash_returns_validation_digest() {
1409 let mut flash = test_flash();
1410
1411 let mut buffer = [0u8; PARTITION_TABLE_MAX_LEN];
1412 let pt = read_partition_table(&mut flash, &mut buffer).unwrap();
1413 let factory = pt
1414 .find_partition(PartitionType::App(AppPartitionSubType::Factory))
1415 .unwrap()
1416 .unwrap();
1417
1418 write_minimal_app_image(&mut flash, factory.offset(), true);
1419
1420 assert_eq!(
1421 factory.sha256(&mut flash).unwrap(),
1422 IMAGE_DIGEST_WITH_HASH_FLAG
1423 );
1424 }
1425
1426 #[test]
1427 fn sha256_of_app_without_appended_hash_hashes_image() {
1428 let mut flash = test_flash();
1429
1430 let mut buffer = [0u8; PARTITION_TABLE_MAX_LEN];
1431 let pt = read_partition_table(&mut flash, &mut buffer).unwrap();
1432 let factory = pt
1433 .find_partition(PartitionType::App(AppPartitionSubType::Factory))
1434 .unwrap()
1435 .unwrap();
1436
1437 write_minimal_app_image(&mut flash, factory.offset(), false);
1438
1439 assert_eq!(
1440 factory.sha256(&mut flash).unwrap(),
1441 IMAGE_DIGEST_WITHOUT_HASH_FLAG
1442 );
1443 }
1444
1445 #[test]
1446 fn sha256_rejects_corrupt_appended_hash() {
1447 let mut flash = test_flash();
1448
1449 let mut buffer = [0u8; PARTITION_TABLE_MAX_LEN];
1450 let pt = read_partition_table(&mut flash, &mut buffer).unwrap();
1451 let factory = pt
1452 .find_partition(PartitionType::App(AppPartitionSubType::Factory))
1453 .unwrap()
1454 .unwrap();
1455
1456 write_minimal_app_image(&mut flash, factory.offset(), true);
1457
1458 flash.write(factory.offset() + 32, &[0u8; 32]).unwrap();
1460
1461 assert_eq!(factory.sha256(&mut flash), Err(Error::InvalidImage));
1462 }
1463}
1464
1465#[cfg(all(test, feature = "embedded-storage"))]
1466mod nor_flash_tests {
1467 use embedded_storage::nor_flash::{MultiwriteNorFlash, NorFlash, ReadNorFlash};
1468
1469 use super::*;
1470
1471 fn test_flash() -> FlashStorage<'static> {
1472 let mut flash = FlashStorage::new();
1473 let mut data = [23u8; 0x10000];
1474 data[PARTITION_TABLE_OFFSET as usize..][..PARTITION_TABLE_MAX_LEN]
1475 .copy_from_slice(include_bytes!("../testdata/single_factory_no_ota.bin"));
1476 flash.write(0, &data).unwrap();
1477 flash
1478 }
1479
1480 #[test]
1481 fn plain_nor_flash_implements_multi_write() {
1482 fn assert_multi_write<N: MultiwriteNorFlash>() {}
1483 assert_multi_write::<NorFlashRegion<'static, 'static, 'static>>();
1484 }
1485
1486 #[test]
1487 fn as_nor_flash_succeeds_on_plain_partition() {
1488 let mut storage = test_flash();
1489
1490 let mut buffer = [0u8; PARTITION_TABLE_MAX_LEN];
1491 let pt = read_partition_table(&mut storage, &mut buffer).unwrap();
1492
1493 let nvs = pt
1494 .find_partition(PartitionType::Data(DataPartitionSubType::Nvs))
1495 .unwrap()
1496 .unwrap();
1497 let mut nvs_partition = nvs.as_flash_region(&mut storage);
1498
1499 assert!(nvs_partition.as_nor_flash().is_ok());
1500 assert!(matches!(
1501 nvs_partition.as_nor_flash_encrypted(),
1502 Err(Error::NotSupported)
1503 ));
1504 }
1505
1506 #[test]
1507 fn nor_flash_write_sizes() {
1508 assert_eq!(
1509 <NorFlashRegion<'static, 'static, 'static> as NorFlash>::WRITE_SIZE,
1510 <FlashStorage<'static> as FlashAccess>::WRITE_SIZE
1511 );
1512 assert_eq!(
1513 <EncryptedNorFlashRegion<'static, 'static, 'static> as NorFlash>::WRITE_SIZE,
1514 <FlashStorage<'static> as FlashAccess>::SECTOR_SIZE as usize
1515 );
1516 assert_eq!(
1517 <NorFlashRegion<'static, 'static, 'static> as ReadNorFlash>::READ_SIZE,
1518 <FlashStorage<'static> as FlashAccess>::READ_SIZE
1519 );
1520 assert_eq!(
1521 <EncryptedNorFlashRegion<'static, 'static, 'static> as ReadNorFlash>::READ_SIZE,
1522 <FlashStorage<'static> as FlashAccess>::READ_SIZE
1523 );
1524 }
1525
1526 #[test]
1527 fn nor_flash_erase_bounds() {
1528 let mut storage = test_flash();
1529
1530 let mut buffer = [0u8; PARTITION_TABLE_MAX_LEN];
1531 let pt = read_partition_table(&mut storage, &mut buffer).unwrap();
1532
1533 let nvs = pt
1534 .find_partition(PartitionType::Data(DataPartitionSubType::Nvs))
1535 .unwrap()
1536 .unwrap();
1537 let mut nvs_partition = nvs.as_flash_region(&mut storage);
1538 let capacity = nvs_partition.capacity() as u32;
1539 let mut nor_flash = nvs_partition.as_nor_flash().unwrap();
1540
1541 nor_flash.erase(0, capacity).unwrap();
1542 let mut buffer = [0u8; 4096];
1543 nor_flash.read(capacity - 4096, &mut buffer).unwrap();
1544 assert!(buffer.iter().all(|v| *v == 0xff));
1545
1546 assert!(nor_flash.erase(0, capacity + 4096) == Err(Error::OutOfBounds));
1547 assert!(nor_flash.erase(4096, 0) == Err(Error::OutOfBounds));
1548 }
1549}