@@ -197,13 +197,19 @@ impl PartitionTable {
197
197
. trim ( csv:: Trim :: All )
198
198
. from_reader ( data. trim ( ) . as_bytes ( ) ) ;
199
199
200
+ // Default offset is 0x8000 in esp-idf, partition table size is 0x1000
201
+ let mut offset = 0x9000 ;
200
202
let mut partitions = Vec :: with_capacity ( data. lines ( ) . count ( ) ) ;
201
203
for record in reader. records ( ) {
202
204
let record = record. map_err ( |e| CSVError :: new ( e, data. clone ( ) ) ) ?;
203
205
let position = record. position ( ) ;
204
- let mut partition: Partition = record
206
+ let mut partition: DeserializedPartition = record
205
207
. deserialize ( None )
206
208
. map_err ( |e| CSVError :: new ( e, data. clone ( ) ) ) ?;
209
+
210
+ partition. fixup_offset ( & mut offset) ;
211
+
212
+ let mut partition = Partition :: from ( partition) ;
207
213
partition. line = position. map ( |pos| pos. line ( ) as usize ) ;
208
214
partitions. push ( partition) ;
209
215
}
@@ -310,17 +316,63 @@ impl PartitionTable {
310
316
const PARTITION_SIZE : usize = 32 ;
311
317
312
318
#[ derive( Debug , Deserialize ) ]
313
- pub struct Partition {
319
+ pub struct DeserializedPartition {
314
320
#[ serde( deserialize_with = "deserialize_partition_name" ) ]
315
321
name : String ,
316
322
ty : Type ,
317
323
sub_type : SubType ,
318
- #[ serde( deserialize_with = "deserialize_partition_offset_or_size" ) ]
324
+ #[ serde( deserialize_with = "deserialize_partition_offset" ) ]
325
+ offset : Option < u32 > ,
326
+ #[ serde( deserialize_with = "deserialize_partition_size" ) ]
327
+ size : u32 ,
328
+ flags : Option < u32 > ,
329
+ }
330
+
331
+ impl DeserializedPartition {
332
+ fn align ( offset : u32 , ty : Type ) -> u32 {
333
+ let pad = match ty {
334
+ Type :: App => 0x10000 ,
335
+ Type :: Data => 4 ,
336
+ } ;
337
+
338
+ if offset % pad != 0 {
339
+ offset + pad - ( offset % pad)
340
+ } else {
341
+ offset
342
+ }
343
+ }
344
+
345
+ fn fixup_offset ( & mut self , offset : & mut u32 ) {
346
+ if self . offset . is_none ( ) {
347
+ self . offset = Some ( Self :: align ( * offset, self . ty ) ) ;
348
+ }
349
+
350
+ * offset = self . offset . unwrap ( ) + self . size ;
351
+ }
352
+ }
353
+
354
+ impl From < DeserializedPartition > for Partition {
355
+ fn from ( part : DeserializedPartition ) -> Self {
356
+ Partition {
357
+ name : part. name ,
358
+ ty : part. ty ,
359
+ sub_type : part. sub_type ,
360
+ offset : part. offset . unwrap ( ) ,
361
+ size : part. size ,
362
+ flags : part. flags ,
363
+ line : None ,
364
+ }
365
+ }
366
+ }
367
+
368
+ #[ derive( Debug ) ]
369
+ pub struct Partition {
370
+ name : String ,
371
+ ty : Type ,
372
+ sub_type : SubType ,
319
373
offset : u32 ,
320
- #[ serde( deserialize_with = "deserialize_partition_offset_or_size" ) ]
321
374
size : u32 ,
322
375
flags : Option < u32 > ,
323
- #[ serde( skip) ]
324
376
line : Option < usize > ,
325
377
}
326
378
@@ -393,7 +445,7 @@ where
393
445
Ok ( maybe_truncated)
394
446
}
395
447
396
- fn deserialize_partition_offset_or_size < ' de , D > ( deserializer : D ) -> Result < u32 , D :: Error >
448
+ fn deserialize_partition_offset_or_size < ' de , D > ( deserializer : D ) -> Result < Option < u32 > , D :: Error >
397
449
where
398
450
D : Deserializer < ' de > ,
399
451
{
@@ -404,17 +456,17 @@ where
404
456
405
457
// NOTE: Partitions of type 'app' must be placed at offsets aligned to 0x10000
406
458
// (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" ) {
459
+ if buf . trim ( ) . is_empty ( ) {
460
+ Ok ( None )
461
+ } else if buf. starts_with ( "0x" ) {
410
462
// Hexadecimal format
411
463
let src = buf. trim_start_matches ( "0x" ) ;
412
464
let size = u32:: from_str_radix ( src, 16 ) . unwrap ( ) ;
413
465
414
- Ok ( size)
466
+ Ok ( Some ( size) )
415
467
} else if let Ok ( size) = buf. parse :: < u32 > ( ) {
416
468
// Decimal format
417
- Ok ( size)
469
+ Ok ( Some ( size) )
418
470
} else if let Some ( captures) = re. captures ( & buf) {
419
471
// Size multiplier format (1k, 2M, etc.)
420
472
let digits = captures. get ( 1 ) . unwrap ( ) . as_str ( ) . parse :: < u32 > ( ) . unwrap ( ) ;
@@ -424,12 +476,28 @@ where
424
476
_ => unreachable ! ( ) ,
425
477
} ;
426
478
427
- Ok ( digits * multiplier)
479
+ Ok ( Some ( digits * multiplier) )
428
480
} else {
429
481
Err ( Error :: custom ( "invalid partition size/offset format" ) )
430
482
}
431
483
}
432
484
485
+ fn deserialize_partition_offset < ' de , D > ( deserializer : D ) -> Result < Option < u32 > , D :: Error >
486
+ where
487
+ D : Deserializer < ' de > ,
488
+ {
489
+ deserialize_partition_offset_or_size ( deserializer)
490
+ }
491
+
492
+ fn deserialize_partition_size < ' de , D > ( deserializer : D ) -> Result < u32 , D :: Error >
493
+ where
494
+ D : Deserializer < ' de > ,
495
+ {
496
+ use serde:: de:: Error ;
497
+ let deserialized = deserialize_partition_offset_or_size ( deserializer) ?;
498
+ deserialized. ok_or_else ( || Error :: custom ( "invalid partition size/offset format" ) )
499
+ }
500
+
433
501
struct HashWriter < W : Write > {
434
502
inner : W ,
435
503
hasher : Context ,
@@ -480,6 +548,22 @@ phy_init, data, phy, 0xf000, 0x1000,
480
548
factory, app, factory, 0x10000, 1M,
481
549
ota_0, app, ota_0, 0x110000, 1M,
482
550
ota_1, app, ota_1, 0x210000, 1M,
551
+ " ;
552
+
553
+ const PTABLE_2 : & str = "
554
+ # ESP-IDF Partition Table
555
+ # Name, Type, SubType, Offset, Size, Flags
556
+ nvs, data, nvs, , 0x4000,
557
+ phy_init, data, phy, , 0x1000,
558
+ factory, app, factory, , 1M,
559
+ " ;
560
+
561
+ const PTABLE_3 : & str = "
562
+ # ESP-IDF Partition Table
563
+ # Name, Type, SubType, Offset, Size, Flags
564
+ nvs, data, nvs, 0x10000, 0x4000,
565
+ phy_init, data, phy, , 0x1000,
566
+ factory, app, factory, , 1M,
483
567
" ;
484
568
485
569
#[ test]
@@ -517,4 +601,32 @@ ota_1, app, ota_1, 0x210000, 1M,
517
601
let pt1 = PartitionTable :: try_from_str ( PTABLE_1 ) ;
518
602
assert ! ( pt1. is_ok( ) ) ;
519
603
}
604
+
605
+ #[ test]
606
+ fn blank_offsets_are_filled_in ( ) {
607
+ let pt2 = PartitionTable :: try_from_str ( PTABLE_2 ) . expect ( "Failed to parse partition table with blank offsets" ) ;
608
+
609
+ assert_eq ! ( 3 , pt2. partitions. len( ) ) ;
610
+ assert_eq ! ( 0x4000 , pt2. partitions[ 0 ] . size) ;
611
+ assert_eq ! ( 0x1000 , pt2. partitions[ 1 ] . size) ;
612
+ assert_eq ! ( 0x100000 , pt2. partitions[ 2 ] . size) ;
613
+
614
+ assert_eq ! ( 0x9000 , pt2. partitions[ 0 ] . offset) ;
615
+ assert_eq ! ( 0xd000 , pt2. partitions[ 1 ] . offset) ;
616
+ assert_eq ! ( 0x10000 , pt2. partitions[ 2 ] . offset) ;
617
+ }
618
+
619
+ #[ test]
620
+ fn first_offsets_are_respected ( ) {
621
+ let pt3 = PartitionTable :: try_from_str ( PTABLE_3 ) . expect ( "Failed to parse partition table with blank offsets" ) ;
622
+
623
+ assert_eq ! ( 3 , pt3. partitions. len( ) ) ;
624
+ assert_eq ! ( 0x4000 , pt3. partitions[ 0 ] . size) ;
625
+ assert_eq ! ( 0x1000 , pt3. partitions[ 1 ] . size) ;
626
+ assert_eq ! ( 0x100000 , pt3. partitions[ 2 ] . size) ;
627
+
628
+ assert_eq ! ( 0x10000 , pt3. partitions[ 0 ] . offset) ;
629
+ assert_eq ! ( 0x14000 , pt3. partitions[ 1 ] . offset) ;
630
+ assert_eq ! ( 0x20000 , pt3. partitions[ 2 ] . offset) ;
631
+ }
520
632
}
0 commit comments