Skip to content

Commit 6ef50ca

Browse files
authored
Merge pull request #106 from bugadani/partitions
Support multiple storage partitions and empty partition offsets
2 parents 90884cb + 12277c4 commit 6ef50ca

File tree

1 file changed

+156
-13
lines changed

1 file changed

+156
-13
lines changed

espflash/src/partition_table.rs

Lines changed: 156 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,12 @@ pub enum DataType {
115115
Spiffs = 0x82,
116116
}
117117

118+
impl DataType {
119+
fn is_multiple_allowed(self) -> bool {
120+
matches!(self, Self::Fat | Self::Spiffs)
121+
}
122+
}
123+
118124
#[derive(Debug, Deserialize, PartialEq, Copy, Clone)]
119125
#[allow(dead_code)]
120126
#[serde(untagged)]
@@ -141,6 +147,13 @@ impl SubType {
141147
SubType::Data(ty) => *ty as u8,
142148
}
143149
}
150+
151+
fn is_multiple_allowed(self) -> bool {
152+
match self {
153+
SubType::App(_) => false,
154+
SubType::Data(ty) => ty.is_multiple_allowed(),
155+
}
156+
}
144157
}
145158

146159
#[derive(Debug)]
@@ -197,13 +210,19 @@ impl PartitionTable {
197210
.trim(csv::Trim::All)
198211
.from_reader(data.trim().as_bytes());
199212

213+
// Default offset is 0x8000 in esp-idf, partition table size is 0x1000
214+
let mut offset = 0x9000;
200215
let mut partitions = Vec::with_capacity(data.lines().count());
201216
for record in reader.records() {
202217
let record = record.map_err(|e| CSVError::new(e, data.clone()))?;
203218
let position = record.position();
204-
let mut partition: Partition = record
219+
let mut partition: DeserializedPartition = record
205220
.deserialize(None)
206221
.map_err(|e| CSVError::new(e, data.clone()))?;
222+
223+
partition.fixup_offset(&mut offset);
224+
225+
let mut partition = Partition::from(partition);
207226
partition.line = position.map(|pos| pos.line() as usize);
208227
partitions.push(partition);
209228
}
@@ -286,7 +305,9 @@ impl PartitionTable {
286305
.into());
287306
}
288307

289-
if partition1.sub_type == partition2.sub_type {
308+
if partition1.sub_type == partition2.sub_type
309+
&& !SubType::is_multiple_allowed(partition1.sub_type)
310+
{
290311
return Err(DuplicatePartitionsError::new(
291312
source, *line1, *line2, "sub-type",
292313
)
@@ -310,17 +331,63 @@ impl PartitionTable {
310331
const PARTITION_SIZE: usize = 32;
311332

312333
#[derive(Debug, Deserialize)]
313-
pub struct Partition {
334+
pub struct DeserializedPartition {
314335
#[serde(deserialize_with = "deserialize_partition_name")]
315336
name: String,
316337
ty: Type,
317338
sub_type: SubType,
318-
#[serde(deserialize_with = "deserialize_partition_offset_or_size")]
339+
#[serde(deserialize_with = "deserialize_partition_offset")]
340+
offset: Option<u32>,
341+
#[serde(deserialize_with = "deserialize_partition_size")]
342+
size: u32,
343+
flags: Option<u32>,
344+
}
345+
346+
impl DeserializedPartition {
347+
fn align(offset: u32, ty: Type) -> u32 {
348+
let pad = match ty {
349+
Type::App => 0x10000,
350+
Type::Data => 4,
351+
};
352+
353+
if offset % pad != 0 {
354+
offset + pad - (offset % pad)
355+
} else {
356+
offset
357+
}
358+
}
359+
360+
fn fixup_offset(&mut self, offset: &mut u32) {
361+
if self.offset.is_none() {
362+
self.offset = Some(Self::align(*offset, self.ty));
363+
}
364+
365+
*offset = self.offset.unwrap() + self.size;
366+
}
367+
}
368+
369+
impl From<DeserializedPartition> for Partition {
370+
fn from(part: DeserializedPartition) -> Self {
371+
Partition {
372+
name: part.name,
373+
ty: part.ty,
374+
sub_type: part.sub_type,
375+
offset: part.offset.unwrap(),
376+
size: part.size,
377+
flags: part.flags,
378+
line: None,
379+
}
380+
}
381+
}
382+
383+
#[derive(Debug)]
384+
pub struct Partition {
385+
name: String,
386+
ty: Type,
387+
sub_type: SubType,
319388
offset: u32,
320-
#[serde(deserialize_with = "deserialize_partition_offset_or_size")]
321389
size: u32,
322390
flags: Option<u32>,
323-
#[serde(skip)]
324391
line: Option<usize>,
325392
}
326393

@@ -393,7 +460,7 @@ where
393460
Ok(maybe_truncated)
394461
}
395462

396-
fn deserialize_partition_offset_or_size<'de, D>(deserializer: D) -> Result<u32, D::Error>
463+
fn deserialize_partition_offset_or_size<'de, D>(deserializer: D) -> Result<Option<u32>, D::Error>
397464
where
398465
D: Deserializer<'de>,
399466
{
@@ -404,17 +471,17 @@ where
404471

405472
// NOTE: Partitions of type 'app' must be placed at offsets aligned to 0x10000
406473
// (64K).
407-
// TODO: The specification states that offsets may be left blank, however that
408-
// is not presently supported in this implementation.
409-
if buf.starts_with("0x") {
474+
if buf.trim().is_empty() {
475+
Ok(None)
476+
} else if buf.starts_with("0x") {
410477
// Hexadecimal format
411478
let src = buf.trim_start_matches("0x");
412479
let size = u32::from_str_radix(src, 16).unwrap();
413480

414-
Ok(size)
481+
Ok(Some(size))
415482
} else if let Ok(size) = buf.parse::<u32>() {
416483
// Decimal format
417-
Ok(size)
484+
Ok(Some(size))
418485
} else if let Some(captures) = re.captures(&buf) {
419486
// Size multiplier format (1k, 2M, etc.)
420487
let digits = captures.get(1).unwrap().as_str().parse::<u32>().unwrap();
@@ -424,12 +491,28 @@ where
424491
_ => unreachable!(),
425492
};
426493

427-
Ok(digits * multiplier)
494+
Ok(Some(digits * multiplier))
428495
} else {
429496
Err(Error::custom("invalid partition size/offset format"))
430497
}
431498
}
432499

500+
fn deserialize_partition_offset<'de, D>(deserializer: D) -> Result<Option<u32>, D::Error>
501+
where
502+
D: Deserializer<'de>,
503+
{
504+
deserialize_partition_offset_or_size(deserializer)
505+
}
506+
507+
fn deserialize_partition_size<'de, D>(deserializer: D) -> Result<u32, D::Error>
508+
where
509+
D: Deserializer<'de>,
510+
{
511+
use serde::de::Error;
512+
let deserialized = deserialize_partition_offset_or_size(deserializer)?;
513+
deserialized.ok_or_else(|| Error::custom("invalid partition size/offset format"))
514+
}
515+
433516
struct HashWriter<W: Write> {
434517
inner: W,
435518
hasher: Context,
@@ -480,6 +563,33 @@ phy_init, data, phy, 0xf000, 0x1000,
480563
factory, app, factory, 0x10000, 1M,
481564
ota_0, app, ota_0, 0x110000, 1M,
482565
ota_1, app, ota_1, 0x210000, 1M,
566+
";
567+
568+
const PTABLE_2: &str = "
569+
# ESP-IDF Partition Table
570+
# Name, Type, SubType, Offset, Size, Flags
571+
nvs, data, nvs, , 0x4000,
572+
phy_init, data, phy, , 0x1000,
573+
factory, app, factory, , 1M,
574+
";
575+
576+
const PTABLE_3: &str = "
577+
# ESP-IDF Partition Table
578+
# Name, Type, SubType, Offset, Size, Flags
579+
nvs, data, nvs, 0x10000, 0x4000,
580+
phy_init, data, phy, , 0x1000,
581+
factory, app, factory, , 1M,
582+
";
583+
584+
const PTABLE_SPIFFS: &str = "
585+
# ESP-IDF Partition Table
586+
# Name, Type, SubType, Offset, Size, Flags
587+
nvs, data, nvs, 0x9000, 0x4000,
588+
otadata, data, ota, 0xd000, 0x2000,
589+
phy_init, data, phy, 0xf000, 0x1000,
590+
factory, app, factory, 0x10000, 1M,
591+
a, data, spiffs, 0x110000, 1M,
592+
b, data, spiffs, 0x210000, 1M,
483593
";
484594

485595
#[test]
@@ -516,5 +626,38 @@ ota_1, app, ota_1, 0x210000, 1M,
516626

517627
let pt1 = PartitionTable::try_from_str(PTABLE_1);
518628
assert!(pt1.is_ok());
629+
630+
let pt_spiffs = PartitionTable::try_from_str(PTABLE_SPIFFS);
631+
assert!(pt_spiffs.is_ok());
632+
}
633+
634+
#[test]
635+
fn blank_offsets_are_filled_in() {
636+
let pt2 = PartitionTable::try_from_str(PTABLE_2)
637+
.expect("Failed to parse partition table with blank offsets");
638+
639+
assert_eq!(3, pt2.partitions.len());
640+
assert_eq!(0x4000, pt2.partitions[0].size);
641+
assert_eq!(0x1000, pt2.partitions[1].size);
642+
assert_eq!(0x100000, pt2.partitions[2].size);
643+
644+
assert_eq!(0x9000, pt2.partitions[0].offset);
645+
assert_eq!(0xd000, pt2.partitions[1].offset);
646+
assert_eq!(0x10000, pt2.partitions[2].offset);
647+
}
648+
649+
#[test]
650+
fn first_offsets_are_respected() {
651+
let pt3 = PartitionTable::try_from_str(PTABLE_3)
652+
.expect("Failed to parse partition table with blank offsets");
653+
654+
assert_eq!(3, pt3.partitions.len());
655+
assert_eq!(0x4000, pt3.partitions[0].size);
656+
assert_eq!(0x1000, pt3.partitions[1].size);
657+
assert_eq!(0x100000, pt3.partitions[2].size);
658+
659+
assert_eq!(0x10000, pt3.partitions[0].offset);
660+
assert_eq!(0x14000, pt3.partitions[1].offset);
661+
assert_eq!(0x20000, pt3.partitions[2].offset);
519662
}
520663
}

0 commit comments

Comments
 (0)