amebazii/types/
flash.rs

1//    Section 5.2 - Layout
2//    ┌──────────────────┐
3//    │ Partition Table  │ 0x00000020 - 0x00001000
4//    ├──────────────────┤
5//    │ System Data      │ 0x00001000 - 0x00002000
6//    ├──────────────────┤
7//    │ Calibration Data │ 0x00002000 - 0x00003000
8//    ├──────────────────┤
9//    │ Reserved         │ 0x00003000 - 0x00004000
10//    ├──────────────────┤
11//    │                  │
12//    │ Boot Image       │ 0x00004000 - 0x0000C000
13//    │                  │
14//    ├──────────────────┤
15//    │                  │
16//    │                  │
17//    │ Firmware 1       │ 0x0000C000
18//    │                  │
19//    │                  │
20//    ├──────────────────┤
21//    │                  │
22//    │                  │
23//    │ Firmware 2       │
24//    │                  │
25//    │                  │
26//    ├──────────────────┤
27//    │                  │
28//    │ User Data        │
29//    │                  │
30//    └──────────────────┘
31
32use std::{collections::HashMap, io};
33
34use crate::{
35    error::Error,
36    read_padding,
37    types::{image::EncryptedOr, PartTab},
38    util::write_fill,
39    write_aligned, write_padding,
40};
41
42use super::{
43    enums::PartitionType,
44    from_stream,
45    image::{
46        boot,
47        ota::{self},
48        pt::{self, Record},
49        RawImage,
50    },
51    sysctrl::SystemData,
52    FromStream, ToStream,
53};
54
55/// Represents different types of partitions in a flash image.
56#[derive(Debug)]
57pub enum Partition {
58    PartitionTable(pt::PartitionTableImage),
59    Bootloader(boot::BootImage),
60    Calibration,
61    Fw1(ota::OTAImage),
62    Fw2(ota::OTAImage),
63    Reserved,
64    Var(RawImage),
65    System(SystemData),
66    User(RawImage),
67    Mp(RawImage),
68}
69
70impl Partition {
71    /// Reads the raw image data from the reader based on the provided record size.
72    ///
73    /// # Parameters:
74    /// - `reader`: The input stream to read from.
75    /// - `record_size`: The size of the record to read.
76    ///
77    /// # Returns:
78    /// - A `RawImage` containing the raw data read from the stream.
79    fn read_raw_image<R>(reader: &mut R, record_size: u32) -> Result<RawImage, Error>
80    where
81        R: io::Read + io::Seek,
82    {
83        let mut buffer = Vec::with_capacity(record_size as usize);
84        reader.read_to_end(&mut buffer)?;
85        Ok(buffer)
86    }
87
88    /// Creates a `Partition` from a `Record` and a reader stream.
89    ///
90    /// This function reads the appropriate partition data based on the partition type from the
91    /// provided record and reader. The partition type is matched and the corresponding partition
92    /// image is created from the reader.
93    ///
94    /// # Parameters:
95    /// - `record`: The partition record containing metadata (e.g., partition type and length).
96    /// - `reader`: The input stream to read the partition data from.
97    ///
98    /// # Returns:
99    /// - A `Partition` variant matching the partition type from the `record`.
100    pub fn from_record<R>(record: &Record, reader: &mut R) -> Result<Self, Error>
101    where
102        R: io::Read + io::Seek,
103    {
104        match &record.part_type {
105            PartitionType::PartTab => Ok(Partition::PartitionTable(from_stream(reader)?)),
106            PartitionType::Boot => Ok(Partition::Bootloader(from_stream(reader)?)),
107            PartitionType::Fw1 => Ok(Partition::Fw1(from_stream(reader)?)),
108            PartitionType::Fw2 => Ok(Partition::Fw2(from_stream(reader)?)),
109            PartitionType::Cal => Ok(Partition::Calibration),
110            PartitionType::Sys => Ok(Partition::System(from_stream(reader)?)),
111            PartitionType::User => Ok(Partition::User(Self::read_raw_image(
112                reader,
113                record.length,
114            )?)),
115            PartitionType::Var => Ok(Partition::Var(Self::read_raw_image(reader, record.length)?)),
116            PartitionType::MP => Ok(Partition::Mp(Self::read_raw_image(reader, record.length)?)),
117            PartitionType::Rdp => Ok(Partition::Reserved),
118            PartitionType::Unknown => Err(Error::InvalidEnumValue(format!(
119                "Invalid partition type: {:?}",
120                record.part_type
121            ))),
122        }
123    }
124}
125
126impl ToStream for Partition {
127    fn write_to<W>(&self, writer: &mut W) -> Result<(), Error>
128    where
129        W: io::Write + io::Seek,
130    {
131        match self {
132            Partition::PartitionTable(pt) => pt.write_to(writer)?,
133            Partition::Bootloader(bt) => bt.write_to(writer)?,
134            Partition::Calibration => (),
135            Partition::Fw1(fw1) => fw1.write_to(writer)?,
136            Partition::Fw2(fw2) => fw2.write_to(writer)?,
137            Partition::Reserved => (),
138            Partition::Var(var) => writer.write_all(var)?,
139            Partition::System(sys) => sys.write_to(writer)?,
140            Partition::User(user) => writer.write_all(user)?,
141            Partition::Mp(mp) => writer.write_all(mp)?,
142        }
143
144        Ok(())
145    }
146}
147
148/// Represents a flash image, including calibration data and partitions.
149pub struct Flash {
150    /// A 16-byte calibration pattern used for calibration data.
151    calibration_pattern: [u8; 16],
152
153    /// A `HashMap` storing partitions indexed by their `PartitionType`.
154    partitions: HashMap<PartitionType, Partition>,
155}
156
157impl Default for Flash {
158    fn default() -> Self {
159        Self {
160            calibration_pattern: [0; 16],
161            partitions: HashMap::new(),
162        }
163    }
164}
165
166impl Flash {
167    /// Returns a reference to the calibration pattern.
168    ///
169    /// This function is used to retrieve the 16-byte calibration pattern for the flash image.
170    ///
171    /// # Returns:
172    /// - A reference to the 16-byte array holding the calibration pattern.
173    pub fn get_calibration_pattern(&self) -> &[u8; 16] {
174        &self.calibration_pattern
175    }
176
177    /// Returns a mutable reference to the calibration pattern.
178    ///
179    /// This function allows modification of the calibration pattern for the flash image.
180    ///
181    /// # Returns:
182    /// - A mutable reference to the 16-byte array holding the calibration pattern.
183    pub fn get_calibration_pattern_mut(&mut self) -> &mut [u8; 16] {
184        &mut self.calibration_pattern
185    }
186
187    /// Retrieves a partition by its type.
188    ///
189    /// # Parameters:
190    /// - `part_type`: The partition type to search for in the flash.
191    ///
192    /// # Returns:
193    /// - `Some(&Partition)` if the partition exists, otherwise `None`.
194    pub fn get_partition(&self, part_type: PartitionType) -> Option<&Partition> {
195        self.partitions.get(&part_type)
196    }
197
198    /// Checks whether a partition of the specified type exists.
199    ///
200    /// # Parameters:
201    /// - `part_type`: The partition type to check.
202    ///
203    /// # Returns:
204    /// - `true` if the partition exists, otherwise `false`.
205    pub fn has_partition(&self, part_type: PartitionType) -> bool {
206        self.partitions.contains_key(&part_type)
207    }
208
209    /// Sets the partition for the specified type.
210    ///
211    /// # Parameters:
212    /// - `part_type`: The type of partition to set.
213    /// - `partition`: The partition data to store.
214    pub fn set_partition(&mut self, part_type: PartitionType, partition: Partition) {
215        self.partitions.insert(part_type, partition);
216    }
217
218    /// Sets the system partition with the provided system data.
219    ///
220    /// # Arguments
221    ///
222    /// * `system_data` - The data to be written to the system partition, represented as `SystemData`.
223    pub fn set_system_partition(&mut self, system_data: SystemData) {
224        self.set_partition(PartitionType::Sys, Partition::System(system_data));
225    }
226
227    /// Sets the boot partition with the given boot image.
228    ///
229    /// # Arguments
230    ///
231    /// * `boot_image` - A `BootImage` object containing the bootloader image data.
232    pub fn set_boot_partition(&mut self, boot_image: boot::BootImage) {
233        self.set_partition(PartitionType::Boot, Partition::Bootloader(boot_image));
234    }
235
236    /// Sets the first firmware partition with the provided firmware image.
237    ///
238    /// This method configures the first firmware partition (`Fw1`) by passing an `OTAImage` object
239    /// representing the firmware image to the internal `set_partition` method.
240    ///
241    /// # Arguments
242    ///
243    /// * `fw1_image` - An `OTAImage` object containing the first firmware image to be stored.
244    pub fn set_fw1(&mut self, fw1_image: ota::OTAImage) {
245        self.set_partition(PartitionType::Fw1, Partition::Fw1(fw1_image));
246    }
247
248    /// Sets the second firmware partition with the given firmware image.
249    ///
250    /// This method configures the second firmware partition (`Fw2`) by passing an `OTAImage` object
251    /// representing the firmware image. It ensures that the firmware data is correctly placed in the
252    /// `Fw2` partition.
253    ///
254    /// # Arguments
255    ///
256    /// * `fw2_image` - An `OTAImage` object containing the second firmware image to be stored.
257    pub fn set_fw2(&mut self, fw2_image: ota::OTAImage) {
258        self.set_partition(PartitionType::Fw2, Partition::Fw2(fw2_image));
259    }
260
261    /// Sets the partition table with the provided partition table image.
262    ///
263    /// # Arguments
264    ///
265    /// * `pt_image` - A `PartitionTableImage` object representing the partition table to be stored.
266    pub fn set_partition_table(&mut self, pt_image: pt::PartitionTableImage) {
267        self.set_partition(PartitionType::PartTab, Partition::PartitionTable(pt_image));
268    }
269}
270
271impl FromStream for Flash {
272    /// Reads the flash image from the provided reader.
273    ///
274    /// This function reads the entire flash image, including the calibration pattern, partitions,
275    /// and partition records. It populates the `Flash` struct with the data read from the stream.
276    ///
277    /// # Parameters:
278    /// - `reader`: The input stream from which the flash image is read.
279    ///
280    /// # Returns:
281    /// - `Ok(())` if the flash image was successfully read and parsed.
282    /// - `Err(Error)` if there was an issue reading the flash image.
283    fn read_from<R>(&mut self, reader: &mut R) -> Result<(), Error>
284    where
285        R: io::Read + io::Seek,
286    {
287        reader.read_exact(&mut self.calibration_pattern)?;
288        read_padding!(reader, 16);
289
290        let pt_image: pt::PartitionTableImage = from_stream(reader)?;
291        if let EncryptedOr::Plain(pt) = &pt_image.pt {
292            for record in pt.get_records() {
293                reader.seek(io::SeekFrom::Start(record.start_addr as u64))?;
294                self.set_partition(record.part_type, Partition::from_record(record, reader)?);
295            }
296        }
297        self.set_partition(PartitionType::PartTab, Partition::PartitionTable(pt_image));
298        Ok(())
299    }
300}
301
302impl ToStream for Flash {
303    /// Writes the flash image to the provided writer.
304    ///
305    /// This function writes the entire flash image, including the calibration pattern, partitions,
306    /// and partition records. It populates the `Flash` struct with the data read from the stream.
307    ///
308    /// # Parameters:
309    /// - `writer`: The output stream to which the flash image is written.
310    ///
311    /// # Returns:
312    /// - `Ok(())` if the flash image was successfully written.
313    /// - `Err(Error)` if there was an issue writing the flash image.
314    fn write_to<W>(&self, writer: &mut W) -> Result<(), Error>
315    where
316        W: io::Write + io::Seek,
317    {
318        writer.write_all(&self.calibration_pattern)?;
319        write_padding!(writer, 16);
320
321        let pt_image = self.partitions.get(&PartitionType::PartTab);
322        if pt_image.is_none() {
323            return Err(Error::InvalidState("Partition table not found".to_string()));
324        }
325
326        let pt_image = pt_image.unwrap();
327        pt_image.write_to(writer)?;
328        write_aligned!(writer, 0x1000);
329
330        // system partition is mandatory
331        let system = self.partitions.get(&PartitionType::Sys);
332        if system.is_none() {
333            return Err(Error::InvalidState(
334                "System partition not found".to_string(),
335            ));
336        }
337        system.unwrap().write_to(writer)?;
338        // we don't have to align here, because the system partition already fills up
339        // the space
340
341        // calibration data: reserved
342        // reserved (backup sector for write operations)
343        write_padding!(writer, 0x2000);
344
345        // even though the next sections are mandatory, we use the records within the
346        // partition table to populate the flash image
347        if let Partition::PartitionTable(pt_image) = pt_image {
348            let pt = &pt_image.pt;
349            if pt.is_encrypted() {
350                return Err(Error::NotImplemented(
351                    "Encrypted partition table is not supported".to_string(),
352                ));
353            }
354
355            // the order must be preserved (BUT it is not checked here)
356            let pt: &PartTab = pt.as_ref();
357            self.write_partition(writer, pt, PartitionType::Boot)?;
358            self.write_partition(writer, pt, PartitionType::Fw1)?;
359            self.write_partition(writer, pt, PartitionType::Fw2)?;
360            self.write_partition(writer, pt, PartitionType::User)?;
361        }
362        Ok(())
363    }
364}
365
366impl Flash {
367    /// Fills the stream with padding up to the specified offset.
368    ///
369    /// # Arguments:
370    /// - `writer`: A mutable reference to a writer that implements the `std::io::Write` and
371    ///   `std::io::Seek` traits.
372    /// - `offset`: The offset to fill up to.
373    ///
374    /// # Returns:
375    /// - `Ok(())` if the write operation is successful.
376    /// - `Err(Error)` if there is an error during the write operation.
377    fn fill_to_offset<W>(&self, writer: &mut W, offset: u64) -> Result<(), Error>
378    where
379        W: io::Write + io::Seek,
380    {
381        let pos = writer.stream_position()?;
382        if pos > offset {
383            return Err(Error::InvalidState(format!(
384                "Cannot fill to offset {}, current position is {}",
385                offset, pos
386            )));
387        }
388
389        write_padding!(writer, offset - pos);
390        Ok(())
391    }
392
393    /// Writes a partition to the stream (if present).
394    ///
395    /// # Arguments:
396    /// - `writer`: A mutable reference to a writer that implements the `std::io::Write` and
397    ///   `std::io::Seek` traits.
398    /// - `pt`: A reference to the partition table.
399    /// - `part_type`: The type of the partition to write.
400    ///
401    /// # Returns:
402    /// - `Ok(())` if the write operation is successful.
403    /// - `Err(Error)` if there is an error during the write operation.
404    fn write_partition<W>(
405        &self,
406        writer: &mut W,
407        pt: &PartTab,
408        part_type: PartitionType,
409    ) -> Result<(), Error>
410    where
411        W: io::Write + io::Seek,
412    {
413        if let Some(record) = pt.get_record(part_type) {
414            self.fill_to_offset(writer, record.start_addr as u64)?;
415            if let Some(partition) = self.partitions.get(&record.part_type) {
416                partition.write_to(writer)?;
417            } else {
418                return Err(Error::InvalidState(format!(
419                    "Partition with type '{:?}' not found (is mandatory)",
420                    record.part_type
421                )));
422            }
423        }
424        Ok(())
425    }
426}