Skip to content

Commit c51e30a

Browse files
authored
Only limit App partitions to 16MB, allow others to be bigger. (#35)
* Only limit App partitions to 16MB, allow others to be bigger. The bootloader will crash when starting from an app partition which exceeds 16MB, but larger sizes are fine for data partitions. Fixes #34 * Additional tests. Test for error on app partition over 16MB. Test for valid data partition over 16MB. Test for error on multiple otadata partitions. Test for error on otadata partition with invalid size. * Refactor, check otadata partitions the same way as others are checked. * Formatting fixes. Should keep rustfmt happy.
1 parent 6d2d0c3 commit c51e30a

6 files changed

+89
-18
lines changed

src/lib.rs

+21-18
Original file line numberDiff line numberDiff line change
@@ -283,7 +283,7 @@ impl PartitionTable {
283283
pub fn validate(&self) -> Result<(), Error> {
284284
use self::partition::{APP_PARTITION_ALIGNMENT, DATA_PARTITION_ALIGNMENT};
285285

286-
const MAX_PART_SIZE: u32 = 0x100_0000; // 16MB
286+
const MAX_APP_PART_SIZE: u32 = 0x100_0000; // 16MB
287287
const OTADATA_SIZE: u32 = 0x2000; // 8kB
288288

289289
// There must be at least one partition with type 'app'
@@ -302,6 +302,17 @@ impl PartitionTable {
302302
return Err(Error::MultipleFactoryPartitions);
303303
}
304304

305+
// There can be at most one partition of type 'data' and of subtype 'otadata'
306+
if self
307+
.partitions
308+
.iter()
309+
.filter(|p| p.ty() == Type::Data && p.subtype() == SubType::Data(DataType::Ota))
310+
.count()
311+
> 1
312+
{
313+
return Err(Error::MultipleOtadataPartitions);
314+
}
315+
305316
for partition in &self.partitions {
306317
// Partitions of type 'app' have to be placed at offsets aligned to 0x10000
307318
// (64k)
@@ -315,11 +326,18 @@ impl PartitionTable {
315326
return Err(Error::UnalignedPartition);
316327
}
317328

318-
// Partitions cannot exceed 16MB; see:
329+
// App partitions cannot exceed 16MB; see:
319330
// https://github.com/espressif/esp-idf/blob/c212305/components/bootloader_support/src/esp_image_format.c#L158-L161
320-
if partition.size() > MAX_PART_SIZE {
331+
if partition.ty() == Type::App && partition.size() > MAX_APP_PART_SIZE {
321332
return Err(Error::PartitionTooLarge(partition.name()));
322333
}
334+
335+
if partition.ty() == Type::Data
336+
&& partition.subtype() == SubType::Data(DataType::Ota)
337+
&& partition.size() != OTADATA_SIZE
338+
{
339+
return Err(Error::InvalidOtadataPartitionSize);
340+
}
323341
}
324342

325343
for partition_a in &self.partitions {
@@ -344,21 +362,6 @@ impl PartitionTable {
344362
}
345363
}
346364

347-
// Check that otadata should be unique
348-
let ota_duplicates = self
349-
.partitions
350-
.iter()
351-
.filter(|p| p.ty() == Type::Data && p.subtype() == SubType::Data(DataType::Ota))
352-
.collect::<Vec<_>>();
353-
354-
if ota_duplicates.len() > 1 {
355-
return Err(Error::MultipleOtadataPartitions);
356-
}
357-
358-
if ota_duplicates.len() == 1 && ota_duplicates[0].size() != OTADATA_SIZE {
359-
return Err(Error::InvalidOtadataPartitionSize);
360-
}
361-
362365
Ok(())
363366
}
364367
}

tests/data/err_factory_too_large.csv

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# ESP-IDF Partition Table
2+
# Name, Type, SubType, Offset, Size, Flags
3+
factory, app, factory, 0x10000, 0x1000001,

tests/data/err_multiple_otadata.csv

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# ESP-IDF Partition Table
2+
# Name, Type, SubType, Offset, Size, Flags
3+
factory, app, factory, 0x10000, 0x100000,
4+
otadata1, data, ota, , 0x2000,
5+
otadata2, data, ota, , 0x2000,
+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
# ESP-IDF Partition Table
2+
# Name, Type, SubType, Offset, Size, Flags
3+
factory, app, factory, 0x10000, 0x100000,
4+
otadata1, data, ota, , 0x3000,

tests/data/large_data_partition.csv

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
# ESP-IDF Partition Table
2+
# Name, Type, SubType, Offset, Size, Flags
3+
nvs, data, nvs, 0x9000, 0x6000,
4+
phy_init, data, phy, 0xf000, 0x1000,
5+
factory, app, factory, 0x10000, 1M,
6+
storage, data, fat, 0x110000,29M,

tests/test_partition_table.rs

+50
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,20 @@ fn test_circuitpython_partition_tables() {
109109
}
110110
}
111111

112+
#[test]
113+
fn test_large_data_partition() {
114+
let csv = fs::read_to_string("tests/data/large_data_partition.csv").unwrap();
115+
let table = PartitionTable::try_from(csv).unwrap();
116+
let partitions = table.partitions();
117+
118+
assert_eq!(partitions.len(), 4);
119+
assert_eq!(partitions[0].name(), "nvs");
120+
assert_eq!(partitions[1].name(), "phy_init");
121+
assert_eq!(partitions[2].name(), "factory");
122+
assert_eq!(partitions[3].name(), "storage");
123+
assert_eq!(partitions[3].size(), 29 * 1024 * 1024);
124+
}
125+
112126
#[test]
113127
fn test_error_when_no_app_partition() -> Result<(), String> {
114128
let csv = fs::read_to_string("tests/data/err_no_app_partition.csv").unwrap();
@@ -133,6 +147,42 @@ fn test_error_when_multiple_factory_partitions() -> Result<(), String> {
133147
}
134148
}
135149

150+
#[test]
151+
fn test_error_factory_partition_too_large() -> Result<(), String> {
152+
let csv = fs::read_to_string("tests/data/err_factory_too_large.csv").unwrap();
153+
154+
match PartitionTable::try_from_str(csv) {
155+
Err(Error::PartitionTooLarge(name)) if name == "factory" => Ok(()),
156+
result => Err(format!(
157+
"expected `Err(PartitionTooLarge(\"factory\"))`, found `{result:?}`"
158+
)),
159+
}
160+
}
161+
162+
#[test]
163+
fn test_error_when_multiple_otadata_partitions() -> Result<(), String> {
164+
let csv = fs::read_to_string("tests/data/err_multiple_otadata.csv").unwrap();
165+
166+
match PartitionTable::try_from_str(csv) {
167+
Err(Error::MultipleOtadataPartitions) => Ok(()),
168+
result => Err(format!(
169+
"expected `Err(Error::MultipleOtadataPartitions)`, found `{result:?}`"
170+
)),
171+
}
172+
}
173+
174+
#[test]
175+
fn test_error_when_otadata_size_invalid() -> Result<(), String> {
176+
let csv = fs::read_to_string("tests/data/err_otadata_invalid_size.csv").unwrap();
177+
178+
match PartitionTable::try_from_str(csv) {
179+
Err(Error::InvalidOtadataPartitionSize) => Ok(()),
180+
result => Err(format!(
181+
"expected `Err(Error::InvalidOtadataPartitionSize)`, found `{result:?}`"
182+
)),
183+
}
184+
}
185+
136186
#[test]
137187
fn test_error_when_unaligned_app_partition() -> Result<(), String> {
138188
let csv = fs::read_to_string("tests/data/err_unaligned_app_partition.csv").unwrap();

0 commit comments

Comments
 (0)