Skip to main content

memsolve/
esp.rs

1//! Support for ESP32 partition table generation.
2use esp_idf_part::{Flags, Partition, PartitionTable, SubType, Type};
3
4use crate::{
5    Memory,
6    layout::{Layout, ResolvedLayout},
7    section::{ResolvedSection, Section},
8};
9
10/// A section with ESP32 metadata.
11pub type EspSection = Section<EspMetaData>;
12/// A layout with ESP32 metadata in the sections.
13pub type EspLayout = Layout<EspMetaData>;
14/// A memory with ESP32 metadata in the sections.
15pub type EspMemory = Memory<EspMetaData>;
16
17/// ESP32 partition metadata.
18#[derive(Clone, Debug)]
19pub struct EspMetaData {
20    /// The ESP32 partition type.
21    pub partition_type: esp_idf_part::Type,
22    /// The ESP32 partition subtype.
23    pub partition_subtype: esp_idf_part::SubType,
24    /// The ESP32 partition flags.
25    pub flags: esp_idf_part::Flags,
26}
27
28impl EspMetaData {
29    /// Creates new partition metadata
30    #[must_use]
31    pub fn new(partition_type: Type, partition_subtype: SubType, flags: Flags) -> Self {
32        Self {
33            partition_type,
34            partition_subtype,
35            flags,
36        }
37    }
38
39    /// Set the flags on the metadata.
40    #[must_use]
41    pub fn set_flags(mut self, flags: Flags) -> Self {
42        self.flags = flags;
43        self
44    }
45}
46
47impl Section<()> {
48    /// Add ESP partition data to this section.
49    #[must_use]
50    pub fn add_esp_metadata(self, partition_type: Type, subtype: impl Into<SubType>) -> EspSection {
51        let metadata = EspMetaData::new(partition_type, subtype.into(), Flags::empty());
52        self.replace_metadata(metadata)
53    }
54}
55
56impl Memory<()> {
57    /// Convert the memory to use [`EspMetaData`]
58    ///
59    /// # Panics
60    ///
61    /// Panics when the layout in the memory contains sections
62    #[must_use]
63    pub fn with_esp_metadata(self) -> EspMemory {
64        Memory {
65            chip: self.chip,
66            layout: self.layout.with_esp_metadata(),
67        }
68    }
69}
70
71impl Layout<()> {
72    /// Converts an empty layout to use [`EspMetaData`]
73    ///
74    /// # Panics
75    ///
76    /// Panics when the layout contains sections
77    #[must_use]
78    pub fn with_esp_metadata(self) -> EspLayout {
79        assert!(self.sections.is_empty());
80
81        Layout::empty()
82    }
83}
84
85impl Section<EspMetaData> {
86    /// set ESP partition flags on this section.
87    #[must_use]
88    pub fn set_esp_flags(mut self, flags: Flags) -> Section<EspMetaData> {
89        self.metadata = self.metadata.set_flags(flags);
90        self
91    }
92}
93
94impl ResolvedSection<EspMetaData> {
95    /// Generate a [`esp_idf_part::Partition`] from this section.
96    #[cfg(feature = "esp")]
97    #[must_use]
98    #[allow(clippy::cast_possible_truncation)]
99    pub fn as_esp_partition(&self) -> Partition {
100        Partition::new(
101            &self.linker_name,
102            self.metadata.partition_type,
103            self.metadata.partition_subtype,
104            self.address as u32,
105            self.size as u32,
106            self.metadata.flags,
107        )
108    }
109}
110
111impl ResolvedLayout<EspMetaData> {
112    /// Generate a [`esp_idf_part::PartitionTable`] from this layout.
113    #[must_use]
114    pub fn into_esp_partition(&self) -> PartitionTable {
115        let partitions = self
116            .sections
117            .iter()
118            .map(ResolvedSection::as_esp_partition)
119            .collect();
120        PartitionTable::new(partitions)
121    }
122}
123
124#[cfg(test)]
125mod tests {
126    use esp_idf_part::{AppType, DataType};
127
128    use crate::{Memory, chip::Chip};
129
130    use super::*;
131
132    #[test]
133    fn default_layout() {
134        let esp_single_factory_app = "# ESP-IDF Partition Table\n\
135        # Name,Type,SubType,Offset,Size,Flags\n\
136        nvs,data,nvs,0x9000,0x6000,\n\
137        phy_init,data,phy,0xf000,0x1000,\n\
138        factory,app,factory,0x10000,0x100000,\n\
139        ";
140
141        let nvs = Section::new("nvs")
142            .unwrap()
143            .set_size(0x6000)
144            .set_address(0x9000)
145            .add_esp_metadata(Type::Data, DataType::Nvs);
146        let phy_init = Section::new("phy_init")
147            .unwrap()
148            .set_size(0x1000)
149            .set_address(0xf000)
150            .add_esp_metadata(Type::Data, DataType::Phy);
151        let factory = Section::new("factory")
152            .unwrap()
153            .set_size(1024 * 1024)
154            .set_address_align(64 * 1024)
155            .add_esp_metadata(Type::App, AppType::Factory);
156
157        let chip = Chip::new(512, 0, 2048 * 1048).unwrap();
158        let mut memory = Memory::new(chip);
159        memory.add_section(nvs);
160        memory.add_section(phy_init);
161        memory.add_section(factory);
162
163        let resolved = memory.resolve_layout().unwrap();
164
165        let csv = resolved.into_esp_partition().to_csv().unwrap();
166        assert_eq!(&csv, esp_single_factory_app);
167    }
168}