Skip to content

Commit 7a4bce8

Browse files
committed
Correct the handling or partition table flags, simplification and cleanup
1 parent eeb0e8f commit 7a4bce8

File tree

1 file changed

+87
-51
lines changed

1 file changed

+87
-51
lines changed

espflash/src/partition_table.rs

Lines changed: 87 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ use std::{
88
use md5::{Context, Digest};
99
use regex::Regex;
1010
use serde::{Deserialize, Deserializer, Serialize};
11+
use strum::IntoEnumIterator;
12+
use strum_macros::EnumIter;
1113

1214
use crate::error::{
1315
CSVError, DuplicatePartitionsError, InvalidSubTypeError, NoAppError,
@@ -16,10 +18,11 @@ use crate::error::{
1618

1719
const MAX_PARTITION_LENGTH: usize = 0xC00;
1820
const PARTITION_TABLE_SIZE: usize = 0x1000;
21+
const PARTITION_SIZE: usize = 32;
22+
const PARTITION_ALIGNMENT: u32 = 0x10000;
1923

2024
#[derive(Copy, Clone, Debug, Deserialize, Serialize, PartialEq)]
2125
#[repr(u8)]
22-
#[allow(dead_code)]
2326
#[serde(rename_all = "lowercase")]
2427
pub enum Type {
2528
App = 0x00,
@@ -29,21 +32,14 @@ pub enum Type {
2932
impl Type {
3033
pub fn subtype_hint(&self) -> String {
3134
match self {
32-
Type::App => "'factory', 'ota_0' through 'ota_15' and 'test'".into(),
35+
Type::App => "'factory', 'ota_0' through 'ota_15', and 'test'".into(),
3336
Type::Data => {
34-
use DataType::*;
35-
let types = [
36-
Ota, Phy, Nvs, CoreDump, NvsKeys, EFuse, EspHttpd, Fat, Spiffs,
37-
];
38-
39-
let mut out = format!("'{}'", serde_plain::to_string(&types[0]).unwrap());
40-
for ty in &types[1..types.len() - 2] {
41-
let ser = serde_plain::to_string(&ty).unwrap();
42-
write!(&mut out, ", '{}'", ser).unwrap();
43-
}
37+
let types = DataType::iter()
38+
.map(|dt| format!("'{}'", serde_plain::to_string(&dt).unwrap()))
39+
.collect::<Vec<_>>();
4440

45-
let ser = serde_plain::to_string(&types[types.len() - 1]).unwrap();
46-
write!(&mut out, " and '{}'", ser).unwrap();
41+
let mut out = types[0..types.len() - 2].join(", ");
42+
write!(&mut out, ", and {}", types[types.len() - 1]).unwrap();
4743

4844
out
4945
}
@@ -52,16 +48,15 @@ impl Type {
5248
}
5349

5450
impl Display for Type {
55-
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
51+
fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
5652
write!(f, "{}", serde_plain::to_string(self).unwrap())
5753
}
5854
}
5955

6056
#[derive(Copy, Clone, Debug, Deserialize, Serialize, PartialEq)]
6157
#[repr(u8)]
62-
#[allow(dead_code)]
63-
#[serde(rename_all = "lowercase")]
6458
pub enum AppType {
59+
#[serde(rename = "factory")]
6560
Factory = 0x00,
6661
#[serde(rename = "ota_0")]
6762
Ota0 = 0x10,
@@ -95,12 +90,12 @@ pub enum AppType {
9590
Ota14 = 0x1e,
9691
#[serde(rename = "ota_15")]
9792
Ota15 = 0x1f,
93+
#[serde(rename = "test")]
9894
Test = 0x20,
9995
}
10096

101-
#[derive(Copy, Clone, Debug, Deserialize, Serialize, PartialEq)]
97+
#[derive(Copy, Clone, Debug, Deserialize, EnumIter, Serialize, PartialEq)]
10298
#[repr(u8)]
103-
#[allow(dead_code)]
10499
#[serde(rename_all = "lowercase")]
105100
pub enum DataType {
106101
Ota = 0x00,
@@ -122,20 +117,20 @@ impl DataType {
122117
}
123118

124119
#[derive(Debug, Deserialize, PartialEq, Copy, Clone)]
125-
#[allow(dead_code)]
126120
#[serde(untagged)]
127121
pub enum SubType {
128122
App(AppType),
129123
Data(DataType),
130124
}
131125

132126
impl Display for SubType {
133-
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
127+
fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
134128
let ser = match self {
135129
SubType::App(sub) => serde_plain::to_string(sub),
136130
SubType::Data(sub) => serde_plain::to_string(sub),
137131
}
138132
.unwrap();
133+
139134
write!(f, "{}", ser)
140135
}
141136
}
@@ -156,6 +151,18 @@ impl SubType {
156151
}
157152
}
158153

154+
#[derive(Copy, Clone, Debug, Deserialize, Serialize, PartialEq)]
155+
#[serde(rename_all = "lowercase")]
156+
pub enum Flags {
157+
Encrypted = 0x1,
158+
}
159+
160+
impl Flags {
161+
pub fn as_u32(&self) -> u32 {
162+
*self as u32
163+
}
164+
}
165+
159166
#[derive(Debug)]
160167
pub struct PartitionTable {
161168
partitions: Vec<Partition>,
@@ -202,7 +209,10 @@ impl PartitionTable {
202209
/// Attempt to parse a partition table from the given string. For more
203210
/// information on the partition table CSV format see:
204211
/// https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-guides/partition-tables.html
205-
pub fn try_from_str<S: Into<String>>(data: S) -> Result<Self, PartitionTableError> {
212+
pub fn try_from_str<S>(data: S) -> Result<Self, PartitionTableError>
213+
where
214+
S: Into<String>,
215+
{
206216
let data = data.into();
207217
let mut reader = csv::ReaderBuilder::new()
208218
.comment(Some(b'#'))
@@ -213,32 +223,39 @@ impl PartitionTable {
213223
// Default offset is 0x8000 in esp-idf, partition table size is 0x1000
214224
let mut offset = 0x9000;
215225
let mut partitions = Vec::with_capacity(data.lines().count());
226+
216227
for record in reader.records() {
217228
let record = record.map_err(|e| CSVError::new(e, data.clone()))?;
218229
let position = record.position();
230+
219231
let mut partition: DeserializedPartition = record
220232
.deserialize(None)
221233
.map_err(|e| CSVError::new(e, data.clone()))?;
222-
223234
partition.fixup_offset(&mut offset);
224235

225236
let mut partition = Partition::from(partition);
226237
partition.line = position.map(|pos| pos.line() as usize);
238+
227239
partitions.push(partition);
228240
}
229241

230242
let table = Self { partitions };
231243
table.validate(&data)?;
244+
232245
Ok(table)
233246
}
234247

235248
pub fn to_bytes(&self) -> Vec<u8> {
236249
let mut result = Vec::with_capacity(PARTITION_TABLE_SIZE);
237250
self.save(&mut result).unwrap();
251+
238252
result
239253
}
240254

241-
pub fn save<W: Write>(&self, writer: &mut W) -> std::io::Result<()> {
255+
pub fn save<W>(&self, writer: &mut W) -> std::io::Result<()>
256+
where
257+
W: Write,
258+
{
242259
let mut hasher = HashWriter::new(writer);
243260
for partition in &self.partitions {
244261
partition.save(&mut hasher)?;
@@ -286,7 +303,7 @@ impl PartitionTable {
286303
.into());
287304
}
288305

289-
if partition.ty == Type::App && partition.offset.rem(0x10000) != 0 {
306+
if partition.ty == Type::App && partition.offset.rem(PARTITION_ALIGNMENT) != 0 {
290307
return Err(UnalignedPartitionError::new(source, *line).into());
291308
}
292309
}
@@ -330,8 +347,6 @@ impl PartitionTable {
330347
}
331348
}
332349

333-
const PARTITION_SIZE: usize = 32;
334-
335350
#[derive(Debug, Deserialize)]
336351
pub struct DeserializedPartition {
337352
#[serde(deserialize_with = "deserialize_partition_name")]
@@ -342,13 +357,13 @@ pub struct DeserializedPartition {
342357
offset: Option<u32>,
343358
#[serde(deserialize_with = "deserialize_partition_size")]
344359
size: u32,
345-
flags: Option<u32>,
360+
flags: Option<Flags>,
346361
}
347362

348363
impl DeserializedPartition {
349364
fn align(offset: u32, ty: Type) -> u32 {
350365
let pad = match ty {
351-
Type::App => 0x10000,
366+
Type::App => PARTITION_ALIGNMENT,
352367
Type::Data => 4,
353368
};
354369

@@ -368,28 +383,14 @@ impl DeserializedPartition {
368383
}
369384
}
370385

371-
impl From<DeserializedPartition> for Partition {
372-
fn from(part: DeserializedPartition) -> Self {
373-
Partition {
374-
name: part.name,
375-
ty: part.ty,
376-
sub_type: part.sub_type,
377-
offset: part.offset.unwrap(),
378-
size: part.size,
379-
flags: part.flags,
380-
line: None,
381-
}
382-
}
383-
}
384-
385386
#[derive(Debug)]
386387
pub struct Partition {
387388
name: String,
388389
ty: Type,
389390
sub_type: SubType,
390391
offset: u32,
391392
size: u32,
392-
flags: Option<u32>,
393+
flags: Option<Flags>,
393394
line: Option<usize>,
394395
}
395396

@@ -399,7 +400,7 @@ impl Partition {
399400
sub_type: SubType,
400401
offset: u32,
401402
size: u32,
402-
flags: Option<u32>,
403+
flags: Option<Flags>,
403404
) -> Self {
404405
Partition {
405406
name,
@@ -415,7 +416,10 @@ impl Partition {
415416
}
416417
}
417418

418-
pub fn save<W: Write>(&self, writer: &mut W) -> std::io::Result<()> {
419+
pub fn save<W>(&self, writer: &mut W) -> std::io::Result<()>
420+
where
421+
W: Write,
422+
{
419423
writer.write_all(&[0xAA, 0x50])?;
420424
writer.write_all(&[self.ty as u8, self.sub_type.as_u8()])?;
421425
writer.write_all(&self.offset.to_le_bytes())?;
@@ -428,7 +432,7 @@ impl Partition {
428432
writer.write_all(&name_bytes)?;
429433

430434
let flags = match &self.flags {
431-
Some(f) => f.to_le_bytes(),
435+
Some(f) => f.as_u32().to_le_bytes(),
432436
None => 0u32.to_le_bytes(),
433437
};
434438
writer.write_all(&flags)?;
@@ -440,11 +444,29 @@ impl Partition {
440444
self.offset
441445
}
442446

447+
pub fn flags(&self) -> Option<Flags> {
448+
self.flags
449+
}
450+
443451
fn overlaps(&self, other: &Partition) -> bool {
444452
max(self.offset, other.offset) < min(self.offset + self.size, other.offset + other.size)
445453
}
446454
}
447455

456+
impl From<DeserializedPartition> for Partition {
457+
fn from(p: DeserializedPartition) -> Self {
458+
Partition {
459+
name: p.name,
460+
ty: p.ty,
461+
sub_type: p.sub_type,
462+
offset: p.offset.unwrap(),
463+
size: p.size,
464+
flags: p.flags,
465+
line: None,
466+
}
467+
}
468+
}
469+
448470
fn deserialize_partition_name<'de, D>(deserializer: D) -> Result<String, D::Error>
449471
where
450472
D: Deserializer<'de>,
@@ -511,16 +533,20 @@ where
511533
D: Deserializer<'de>,
512534
{
513535
use serde::de::Error;
536+
514537
let deserialized = deserialize_partition_offset_or_size(deserializer)?;
515538
deserialized.ok_or_else(|| Error::custom("invalid partition size/offset format"))
516539
}
517540

518-
struct HashWriter<W: Write> {
541+
struct HashWriter<W> {
519542
inner: W,
520543
hasher: Context,
521544
}
522545

523-
impl<W: Write> Write for HashWriter<W> {
546+
impl<W> Write for HashWriter<W>
547+
where
548+
W: Write,
549+
{
524550
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
525551
self.hasher.write_all(buf)?;
526552
self.inner.write(buf)
@@ -531,7 +557,10 @@ impl<W: Write> Write for HashWriter<W> {
531557
}
532558
}
533559

534-
impl<W: Write> HashWriter<W> {
560+
impl<W> HashWriter<W>
561+
where
562+
W: Write,
563+
{
535564
pub fn new(inner: W) -> Self {
536565
HashWriter {
537566
inner,
@@ -553,7 +582,7 @@ mod tests {
553582
# Name, Type, SubType, Offset, Size, Flags
554583
nvs, data, nvs, 0x9000, 0x6000,
555584
phy_init, data, phy, 0xf000, 0x1000,
556-
factory, app, factory, 0x10000, 1M,
585+
factory, app, factory, 0x10000, 1M, encrypted
557586
";
558587

559588
const PTABLE_1: &str = "
@@ -644,6 +673,12 @@ phy_init, data, phy, 0xf000, 0x1000,
644673
let pt0 = PartitionTable::try_from_str(PTABLE_0);
645674
assert!(pt0.is_ok());
646675

676+
let pt0 = pt0.unwrap();
677+
let nvs = pt0.find("nvs").unwrap();
678+
let fac = pt0.find("factory").unwrap();
679+
assert_eq!(nvs.flags(), None);
680+
assert_eq!(fac.flags(), Some(Flags::Encrypted));
681+
647682
let pt1 = PartitionTable::try_from_str(PTABLE_1);
648683
assert!(pt1.is_ok());
649684

@@ -652,6 +687,7 @@ phy_init, data, phy, 0xf000, 0x1000,
652687

653688
PartitionTable::try_from_str(PTABLE_NO_FACTORY)
654689
.expect("Failed to parse partition table without factory partition");
690+
655691
PartitionTable::try_from_str(PTABLE_NO_APP)
656692
.expect_err("Failed to reject partition table without factory or ota partition");
657693
}

0 commit comments

Comments
 (0)