esp_partition_table/
entry.rs

1use crate::{utils, PartitionError, PartitionType};
2
3#[cfg(feature = "heapless")]
4use heapless::String;
5
6/// Data buffer for partition entry
7pub type PartitionBuffer = [u8; PartitionEntry::SIZE];
8
9/// ESP Partition info
10///
11/// Binary representation:
12///
13/// Off | Len | Desc
14/// --- | --- | ----
15///   0 |   2 | Magic
16///   2 |   1 | Type
17///   3 |   1 | SubType
18///   4 |   4 | Offset
19///   8 |   4 | Size
20///  12 |  16 | Name
21///  28 |   4 | Flags
22///
23#[derive(Clone, Debug, Default, PartialEq, Eq)]
24pub struct PartitionEntry {
25    /// Partition type and subtype
26    pub type_: PartitionType,
27
28    /// Partition offset
29    pub offset: u32,
30
31    /// Partition size
32    pub size: usize,
33
34    /// Partition name
35    #[cfg(feature = "heapless")]
36    pub name: String<{ Self::MAX_NAME_LEN }>,
37
38    #[cfg(not(feature = "heapless"))]
39    name: [u8; Self::MAX_NAME_LEN],
40
41    /// Partition encrypted flag
42    pub encrypted: bool,
43}
44
45impl PartitionEntry {
46    /// Magic bytes at beginning binary partition representation
47    pub const MAGIC: [u8; 2] = [0xAA, 0x50];
48
49    /// The size of binary represented partition data
50    pub const SIZE: usize = 32;
51
52    /// Max partition name length
53    pub const MAX_NAME_LEN: usize = 16;
54
55    /// Create partition info
56    pub fn new(
57        type_: impl Into<PartitionType>,
58        offset: u32,
59        size: usize,
60        name: impl AsRef<str>,
61        encrypted: bool,
62    ) -> Result<Self, PartitionError> {
63        let name = name.as_ref();
64
65        #[cfg(feature = "heapless")]
66        let name = name.try_into().map_err(|_| PartitionError::InvalidString)?;
67
68        #[cfg(not(feature = "heapless"))]
69        let name = {
70            let mut name_data = [0u8; Self::MAX_NAME_LEN];
71            utils::name_into(&mut name_data, name)?;
72            name_data
73        };
74
75        Ok(Self {
76            type_: type_.into(),
77            offset,
78            size,
79            name,
80            encrypted,
81        })
82    }
83
84    /// Set partition offset with alignment check
85    pub fn set_offset(&mut self, offset: u32) -> Result<(), PartitionError> {
86        self.type_.check_offset(offset)?;
87        self.offset = offset;
88        Ok(())
89    }
90
91    /// Get partition name
92    pub fn name(&self) -> &str {
93        #[cfg(not(feature = "heapless"))]
94        {
95            let name = utils::name_trim(&self.name);
96            // utf8 data already validated
97            unsafe { core::str::from_utf8_unchecked(name) }
98        }
99
100        #[cfg(feature = "heapless")]
101        {
102            &self.name
103        }
104    }
105
106    /// Set partition name
107    pub fn set_name(&mut self, name: impl AsRef<str>) -> Result<(), PartitionError> {
108        let name = name.as_ref();
109
110        #[cfg(feature = "heapless")]
111        {
112            self.name = name.try_into().map_err(|_| PartitionError::InvalidString)?;
113            Ok(())
114        }
115
116        #[cfg(not(feature = "heapless"))]
117        {
118            utils::name_into(&mut self.name, name)
119        }
120    }
121
122    /// Convert partition data from binary representation
123    pub fn from_bytes(data: &PartitionBuffer) -> Result<Self, PartitionError> {
124        let (magic, data) = data
125            .split_first_chunk()
126            .ok_or(PartitionError::NotEnoughData)?;
127        if magic != &Self::MAGIC {
128            return Err(PartitionError::InvalidMagic);
129        }
130
131        let (type_data, data) = data
132            .split_first_chunk()
133            .ok_or(PartitionError::NotEnoughData)?;
134        let type_ = type_data.try_into()?;
135
136        let (offset_data, data) = data
137            .split_first_chunk()
138            .ok_or(PartitionError::NotEnoughData)?;
139        let offset = u32::from_le_bytes(*offset_data);
140
141        let (size_data, data) = data
142            .split_first_chunk()
143            .ok_or(PartitionError::NotEnoughData)?;
144        let size = u32::from_le_bytes(*size_data) as usize;
145
146        let (name_data, data) = data
147            .split_first_chunk()
148            .ok_or(PartitionError::NotEnoughData)?;
149        let _name_str = utils::name_from(name_data)?;
150
151        #[cfg(feature = "heapless")]
152        let name = _name_str
153            .try_into()
154            .map_err(|_| PartitionError::TooManyData)?;
155
156        #[cfg(not(feature = "heapless"))]
157        let name = *name_data;
158
159        let (flags_data, _) = data
160            .split_first_chunk()
161            .ok_or(PartitionError::NotEnoughData)?;
162        let flags = u32::from_le_bytes(*flags_data);
163
164        let encrypted = flags & 0x01 != 0;
165
166        Ok(Self {
167            type_,
168            offset,
169            size,
170            name,
171            encrypted,
172        })
173    }
174
175    /// Convert partition data to binary representation
176    pub fn to_bytes(&self, data: &mut PartitionBuffer) -> Result<(), PartitionError> {
177        self.type_.check_offset(self.offset)?;
178
179        let (magic_data, data) = data
180            .split_first_chunk_mut()
181            .ok_or(PartitionError::NotEnoughData)?;
182        *magic_data = Self::MAGIC;
183
184        let (type_data, data) = data
185            .split_first_chunk_mut()
186            .ok_or(PartitionError::NotEnoughData)?;
187        self.type_.to_bytes(type_data)?;
188
189        let (offset_data, data) = data
190            .split_first_chunk_mut()
191            .ok_or(PartitionError::NotEnoughData)?;
192        *offset_data = self.offset.to_le_bytes();
193
194        let (size_data, data) = data
195            .split_first_chunk_mut()
196            .ok_or(PartitionError::NotEnoughData)?;
197        *size_data = (self.size as u32).to_le_bytes();
198
199        let (name_data, data) = data
200            .split_first_chunk_mut()
201            .ok_or(PartitionError::NotEnoughData)?;
202
203        #[cfg(feature = "heapless")]
204        utils::name_into(name_data, self.name.as_str())?;
205
206        #[cfg(not(feature = "heapless"))]
207        {
208            *name_data = self.name;
209        }
210
211        let (flags_data, _) = data
212            .split_first_chunk_mut()
213            .ok_or(PartitionError::NotEnoughData)?;
214        *flags_data = (self.encrypted as u32).to_le_bytes();
215
216        Ok(())
217    }
218}
219
220impl AsRef<PartitionEntry> for PartitionEntry {
221    fn as_ref(&self) -> &Self {
222        self
223    }
224}
225
226impl TryFrom<&[u8]> for PartitionEntry {
227    type Error = PartitionError;
228
229    fn try_from(data: &[u8]) -> Result<Self, Self::Error> {
230        <&PartitionBuffer>::try_from(data)
231            .map_err(|_| PartitionError::NotEnoughData)
232            .and_then(Self::try_from)
233    }
234}
235
236impl TryFrom<&PartitionBuffer> for PartitionEntry {
237    type Error = PartitionError;
238
239    fn try_from(data: &PartitionBuffer) -> Result<Self, Self::Error> {
240        Self::from_bytes(data)
241    }
242}
243
244/// MD5 checksum data
245pub type Md5Data = [u8; 16];
246
247/// ESP Partition MD5
248///
249/// Binary representation:
250///
251/// Off | Len | Desc
252/// --- | --- | ----
253///   0 |   2 | Magic
254///   2 |  14 | Reserved
255///  16 |  16 | MD5 data
256///
257#[derive(Clone, Debug, Default, PartialEq, Eq)]
258pub struct PartitionMd5 {
259    /// MD5 checksum data
260    pub data: Md5Data,
261}
262
263impl From<PartitionMd5> for Md5Data {
264    fn from(md5: PartitionMd5) -> Self {
265        md5.data
266    }
267}
268
269impl From<Md5Data> for PartitionMd5 {
270    fn from(data: Md5Data) -> Self {
271        Self { data }
272    }
273}
274
275#[cfg(feature = "md5")]
276impl From<md5::Digest> for PartitionMd5 {
277    fn from(digest: md5::Digest) -> Self {
278        Self {
279            data: digest.into(),
280        }
281    }
282}
283
284impl PartitionMd5 {
285    /// Magic bytes
286    pub const MAGIC: [u8; 2] = [0xeb, 0xeb];
287
288    /// The size of reserved space between magic bytes and MD5 data
289    pub const RESERVED_SIZE: usize = 14;
290
291    /// The content of reserved space between magic bytes and MD5 data
292    pub const RESERVED_DATA: u8 = 0xff;
293
294    /// Convert md5 data from binary representation
295    pub fn from_bytes(data: &PartitionBuffer) -> Result<Self, PartitionError> {
296        let (magic, data) = data
297            .split_first_chunk()
298            .ok_or(PartitionError::NotEnoughData)?;
299        if magic != &Self::MAGIC {
300            return Err(PartitionError::InvalidMagic);
301        }
302
303        let (reserved_data, data) = data
304            .split_first_chunk::<{ Self::RESERVED_SIZE }>()
305            .ok_or(PartitionError::NotEnoughData)?;
306        for reserved in reserved_data {
307            if *reserved != Self::RESERVED_DATA {
308                return Err(PartitionError::InvalidMagic);
309            }
310        }
311
312        let (md5_data, _) = data
313            .split_first_chunk()
314            .ok_or(PartitionError::NotEnoughData)?;
315
316        Ok(Self { data: *md5_data })
317    }
318
319    /// Convert md5 data to binary representation
320    pub fn to_bytes(&self, data: &mut PartitionBuffer) -> Result<(), PartitionError> {
321        let (magic_data, data) = data
322            .split_first_chunk_mut()
323            .ok_or(PartitionError::NotEnoughData)?;
324        *magic_data = Self::MAGIC;
325
326        let (reserved_data, data) = data
327            .split_first_chunk_mut::<{ Self::RESERVED_SIZE }>()
328            .ok_or(PartitionError::NotEnoughData)?;
329        reserved_data.fill(Self::RESERVED_DATA);
330
331        let (md5_data, _) = data
332            .split_first_chunk_mut()
333            .ok_or(PartitionError::NotEnoughData)?;
334        *md5_data = self.data;
335
336        Ok(())
337    }
338}
339
340impl TryFrom<&[u8]> for PartitionMd5 {
341    type Error = PartitionError;
342
343    fn try_from(data: &[u8]) -> Result<Self, Self::Error> {
344        <&PartitionBuffer>::try_from(data)
345            .map_err(|_| PartitionError::NotEnoughData)
346            .and_then(Self::try_from)
347    }
348}
349
350impl TryFrom<&PartitionBuffer> for PartitionMd5 {
351    type Error = PartitionError;
352
353    fn try_from(data: &PartitionBuffer) -> Result<Self, Self::Error> {
354        Self::from_bytes(data)
355    }
356}