@@ -115,6 +115,12 @@ pub enum DataType {
115
115
Spiffs = 0x82 ,
116
116
}
117
117
118
+ impl DataType {
119
+ fn is_multiple_allowed ( self ) -> bool {
120
+ matches ! ( self , Self :: Fat | Self :: Spiffs )
121
+ }
122
+ }
123
+
118
124
#[ derive( Debug , Deserialize , PartialEq , Copy , Clone ) ]
119
125
#[ allow( dead_code) ]
120
126
#[ serde( untagged) ]
@@ -141,6 +147,13 @@ impl SubType {
141
147
SubType :: Data ( ty) => * ty as u8 ,
142
148
}
143
149
}
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
+ }
144
157
}
145
158
146
159
#[ derive( Debug ) ]
@@ -197,13 +210,19 @@ impl PartitionTable {
197
210
. trim ( csv:: Trim :: All )
198
211
. from_reader ( data. trim ( ) . as_bytes ( ) ) ;
199
212
213
+ // Default offset is 0x8000 in esp-idf, partition table size is 0x1000
214
+ let mut offset = 0x9000 ;
200
215
let mut partitions = Vec :: with_capacity ( data. lines ( ) . count ( ) ) ;
201
216
for record in reader. records ( ) {
202
217
let record = record. map_err ( |e| CSVError :: new ( e, data. clone ( ) ) ) ?;
203
218
let position = record. position ( ) ;
204
- let mut partition: Partition = record
219
+ let mut partition: DeserializedPartition = record
205
220
. deserialize ( None )
206
221
. map_err ( |e| CSVError :: new ( e, data. clone ( ) ) ) ?;
222
+
223
+ partition. fixup_offset ( & mut offset) ;
224
+
225
+ let mut partition = Partition :: from ( partition) ;
207
226
partition. line = position. map ( |pos| pos. line ( ) as usize ) ;
208
227
partitions. push ( partition) ;
209
228
}
@@ -286,7 +305,9 @@ impl PartitionTable {
286
305
. into ( ) ) ;
287
306
}
288
307
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
+ {
290
311
return Err ( DuplicatePartitionsError :: new (
291
312
source, * line1, * line2, "sub-type" ,
292
313
)
@@ -310,17 +331,63 @@ impl PartitionTable {
310
331
const PARTITION_SIZE : usize = 32 ;
311
332
312
333
#[ derive( Debug , Deserialize ) ]
313
- pub struct Partition {
334
+ pub struct DeserializedPartition {
314
335
#[ serde( deserialize_with = "deserialize_partition_name" ) ]
315
336
name : String ,
316
337
ty : Type ,
317
338
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 ,
319
388
offset : u32 ,
320
- #[ serde( deserialize_with = "deserialize_partition_offset_or_size" ) ]
321
389
size : u32 ,
322
390
flags : Option < u32 > ,
323
- #[ serde( skip) ]
324
391
line : Option < usize > ,
325
392
}
326
393
@@ -393,7 +460,7 @@ where
393
460
Ok ( maybe_truncated)
394
461
}
395
462
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 >
397
464
where
398
465
D : Deserializer < ' de > ,
399
466
{
@@ -404,17 +471,17 @@ where
404
471
405
472
// NOTE: Partitions of type 'app' must be placed at offsets aligned to 0x10000
406
473
// (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" ) {
410
477
// Hexadecimal format
411
478
let src = buf. trim_start_matches ( "0x" ) ;
412
479
let size = u32:: from_str_radix ( src, 16 ) . unwrap ( ) ;
413
480
414
- Ok ( size)
481
+ Ok ( Some ( size) )
415
482
} else if let Ok ( size) = buf. parse :: < u32 > ( ) {
416
483
// Decimal format
417
- Ok ( size)
484
+ Ok ( Some ( size) )
418
485
} else if let Some ( captures) = re. captures ( & buf) {
419
486
// Size multiplier format (1k, 2M, etc.)
420
487
let digits = captures. get ( 1 ) . unwrap ( ) . as_str ( ) . parse :: < u32 > ( ) . unwrap ( ) ;
@@ -424,12 +491,28 @@ where
424
491
_ => unreachable ! ( ) ,
425
492
} ;
426
493
427
- Ok ( digits * multiplier)
494
+ Ok ( Some ( digits * multiplier) )
428
495
} else {
429
496
Err ( Error :: custom ( "invalid partition size/offset format" ) )
430
497
}
431
498
}
432
499
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
+
433
516
struct HashWriter < W : Write > {
434
517
inner : W ,
435
518
hasher : Context ,
@@ -480,6 +563,33 @@ phy_init, data, phy, 0xf000, 0x1000,
480
563
factory, app, factory, 0x10000, 1M,
481
564
ota_0, app, ota_0, 0x110000, 1M,
482
565
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,
483
593
" ;
484
594
485
595
#[ test]
@@ -516,5 +626,38 @@ ota_1, app, ota_1, 0x210000, 1M,
516
626
517
627
let pt1 = PartitionTable :: try_from_str ( PTABLE_1 ) ;
518
628
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) ;
519
662
}
520
663
}
0 commit comments