@@ -8,6 +8,8 @@ use std::{
8
8
use md5:: { Context , Digest } ;
9
9
use regex:: Regex ;
10
10
use serde:: { Deserialize , Deserializer , Serialize } ;
11
+ use strum:: IntoEnumIterator ;
12
+ use strum_macros:: EnumIter ;
11
13
12
14
use crate :: error:: {
13
15
CSVError , DuplicatePartitionsError , InvalidSubTypeError , NoAppError ,
@@ -16,10 +18,11 @@ use crate::error::{
16
18
17
19
const MAX_PARTITION_LENGTH : usize = 0xC00 ;
18
20
const PARTITION_TABLE_SIZE : usize = 0x1000 ;
21
+ const PARTITION_SIZE : usize = 32 ;
22
+ const PARTITION_ALIGNMENT : u32 = 0x10000 ;
19
23
20
24
#[ derive( Copy , Clone , Debug , Deserialize , Serialize , PartialEq ) ]
21
25
#[ repr( u8 ) ]
22
- #[ allow( dead_code) ]
23
26
#[ serde( rename_all = "lowercase" ) ]
24
27
pub enum Type {
25
28
App = 0x00 ,
@@ -29,21 +32,14 @@ pub enum Type {
29
32
impl Type {
30
33
pub fn subtype_hint ( & self ) -> String {
31
34
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 ( ) ,
33
36
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 < _ > > ( ) ;
44
40
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 ( ) ;
47
43
48
44
out
49
45
}
@@ -52,16 +48,15 @@ impl Type {
52
48
}
53
49
54
50
impl Display for Type {
55
- fn fmt ( & self , f : & mut Formatter < ' _ > ) -> std:: fmt:: Result {
51
+ fn fmt ( & self , f : & mut Formatter ) -> std:: fmt:: Result {
56
52
write ! ( f, "{}" , serde_plain:: to_string( self ) . unwrap( ) )
57
53
}
58
54
}
59
55
60
56
#[ derive( Copy , Clone , Debug , Deserialize , Serialize , PartialEq ) ]
61
57
#[ repr( u8 ) ]
62
- #[ allow( dead_code) ]
63
- #[ serde( rename_all = "lowercase" ) ]
64
58
pub enum AppType {
59
+ #[ serde( rename = "factory" ) ]
65
60
Factory = 0x00 ,
66
61
#[ serde( rename = "ota_0" ) ]
67
62
Ota0 = 0x10 ,
@@ -95,12 +90,12 @@ pub enum AppType {
95
90
Ota14 = 0x1e ,
96
91
#[ serde( rename = "ota_15" ) ]
97
92
Ota15 = 0x1f ,
93
+ #[ serde( rename = "test" ) ]
98
94
Test = 0x20 ,
99
95
}
100
96
101
- #[ derive( Copy , Clone , Debug , Deserialize , Serialize , PartialEq ) ]
97
+ #[ derive( Copy , Clone , Debug , Deserialize , EnumIter , Serialize , PartialEq ) ]
102
98
#[ repr( u8 ) ]
103
- #[ allow( dead_code) ]
104
99
#[ serde( rename_all = "lowercase" ) ]
105
100
pub enum DataType {
106
101
Ota = 0x00 ,
@@ -122,20 +117,20 @@ impl DataType {
122
117
}
123
118
124
119
#[ derive( Debug , Deserialize , PartialEq , Copy , Clone ) ]
125
- #[ allow( dead_code) ]
126
120
#[ serde( untagged) ]
127
121
pub enum SubType {
128
122
App ( AppType ) ,
129
123
Data ( DataType ) ,
130
124
}
131
125
132
126
impl Display for SubType {
133
- fn fmt ( & self , f : & mut Formatter < ' _ > ) -> std:: fmt:: Result {
127
+ fn fmt ( & self , f : & mut Formatter ) -> std:: fmt:: Result {
134
128
let ser = match self {
135
129
SubType :: App ( sub) => serde_plain:: to_string ( sub) ,
136
130
SubType :: Data ( sub) => serde_plain:: to_string ( sub) ,
137
131
}
138
132
. unwrap ( ) ;
133
+
139
134
write ! ( f, "{}" , ser)
140
135
}
141
136
}
@@ -156,6 +151,18 @@ impl SubType {
156
151
}
157
152
}
158
153
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
+
159
166
#[ derive( Debug ) ]
160
167
pub struct PartitionTable {
161
168
partitions : Vec < Partition > ,
@@ -202,7 +209,10 @@ impl PartitionTable {
202
209
/// Attempt to parse a partition table from the given string. For more
203
210
/// information on the partition table CSV format see:
204
211
/// 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
+ {
206
216
let data = data. into ( ) ;
207
217
let mut reader = csv:: ReaderBuilder :: new ( )
208
218
. comment ( Some ( b'#' ) )
@@ -213,32 +223,39 @@ impl PartitionTable {
213
223
// Default offset is 0x8000 in esp-idf, partition table size is 0x1000
214
224
let mut offset = 0x9000 ;
215
225
let mut partitions = Vec :: with_capacity ( data. lines ( ) . count ( ) ) ;
226
+
216
227
for record in reader. records ( ) {
217
228
let record = record. map_err ( |e| CSVError :: new ( e, data. clone ( ) ) ) ?;
218
229
let position = record. position ( ) ;
230
+
219
231
let mut partition: DeserializedPartition = record
220
232
. deserialize ( None )
221
233
. map_err ( |e| CSVError :: new ( e, data. clone ( ) ) ) ?;
222
-
223
234
partition. fixup_offset ( & mut offset) ;
224
235
225
236
let mut partition = Partition :: from ( partition) ;
226
237
partition. line = position. map ( |pos| pos. line ( ) as usize ) ;
238
+
227
239
partitions. push ( partition) ;
228
240
}
229
241
230
242
let table = Self { partitions } ;
231
243
table. validate ( & data) ?;
244
+
232
245
Ok ( table)
233
246
}
234
247
235
248
pub fn to_bytes ( & self ) -> Vec < u8 > {
236
249
let mut result = Vec :: with_capacity ( PARTITION_TABLE_SIZE ) ;
237
250
self . save ( & mut result) . unwrap ( ) ;
251
+
238
252
result
239
253
}
240
254
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
+ {
242
259
let mut hasher = HashWriter :: new ( writer) ;
243
260
for partition in & self . partitions {
244
261
partition. save ( & mut hasher) ?;
@@ -286,7 +303,7 @@ impl PartitionTable {
286
303
. into ( ) ) ;
287
304
}
288
305
289
- if partition. ty == Type :: App && partition. offset . rem ( 0x10000 ) != 0 {
306
+ if partition. ty == Type :: App && partition. offset . rem ( PARTITION_ALIGNMENT ) != 0 {
290
307
return Err ( UnalignedPartitionError :: new ( source, * line) . into ( ) ) ;
291
308
}
292
309
}
@@ -330,8 +347,6 @@ impl PartitionTable {
330
347
}
331
348
}
332
349
333
- const PARTITION_SIZE : usize = 32 ;
334
-
335
350
#[ derive( Debug , Deserialize ) ]
336
351
pub struct DeserializedPartition {
337
352
#[ serde( deserialize_with = "deserialize_partition_name" ) ]
@@ -342,13 +357,13 @@ pub struct DeserializedPartition {
342
357
offset : Option < u32 > ,
343
358
#[ serde( deserialize_with = "deserialize_partition_size" ) ]
344
359
size : u32 ,
345
- flags : Option < u32 > ,
360
+ flags : Option < Flags > ,
346
361
}
347
362
348
363
impl DeserializedPartition {
349
364
fn align ( offset : u32 , ty : Type ) -> u32 {
350
365
let pad = match ty {
351
- Type :: App => 0x10000 ,
366
+ Type :: App => PARTITION_ALIGNMENT ,
352
367
Type :: Data => 4 ,
353
368
} ;
354
369
@@ -368,28 +383,14 @@ impl DeserializedPartition {
368
383
}
369
384
}
370
385
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
-
385
386
#[ derive( Debug ) ]
386
387
pub struct Partition {
387
388
name : String ,
388
389
ty : Type ,
389
390
sub_type : SubType ,
390
391
offset : u32 ,
391
392
size : u32 ,
392
- flags : Option < u32 > ,
393
+ flags : Option < Flags > ,
393
394
line : Option < usize > ,
394
395
}
395
396
@@ -399,7 +400,7 @@ impl Partition {
399
400
sub_type : SubType ,
400
401
offset : u32 ,
401
402
size : u32 ,
402
- flags : Option < u32 > ,
403
+ flags : Option < Flags > ,
403
404
) -> Self {
404
405
Partition {
405
406
name,
@@ -415,7 +416,10 @@ impl Partition {
415
416
}
416
417
}
417
418
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
+ {
419
423
writer. write_all ( & [ 0xAA , 0x50 ] ) ?;
420
424
writer. write_all ( & [ self . ty as u8 , self . sub_type . as_u8 ( ) ] ) ?;
421
425
writer. write_all ( & self . offset . to_le_bytes ( ) ) ?;
@@ -428,7 +432,7 @@ impl Partition {
428
432
writer. write_all ( & name_bytes) ?;
429
433
430
434
let flags = match & self . flags {
431
- Some ( f) => f. to_le_bytes ( ) ,
435
+ Some ( f) => f. as_u32 ( ) . to_le_bytes ( ) ,
432
436
None => 0u32 . to_le_bytes ( ) ,
433
437
} ;
434
438
writer. write_all ( & flags) ?;
@@ -440,11 +444,29 @@ impl Partition {
440
444
self . offset
441
445
}
442
446
447
+ pub fn flags ( & self ) -> Option < Flags > {
448
+ self . flags
449
+ }
450
+
443
451
fn overlaps ( & self , other : & Partition ) -> bool {
444
452
max ( self . offset , other. offset ) < min ( self . offset + self . size , other. offset + other. size )
445
453
}
446
454
}
447
455
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
+
448
470
fn deserialize_partition_name < ' de , D > ( deserializer : D ) -> Result < String , D :: Error >
449
471
where
450
472
D : Deserializer < ' de > ,
@@ -511,16 +533,20 @@ where
511
533
D : Deserializer < ' de > ,
512
534
{
513
535
use serde:: de:: Error ;
536
+
514
537
let deserialized = deserialize_partition_offset_or_size ( deserializer) ?;
515
538
deserialized. ok_or_else ( || Error :: custom ( "invalid partition size/offset format" ) )
516
539
}
517
540
518
- struct HashWriter < W : Write > {
541
+ struct HashWriter < W > {
519
542
inner : W ,
520
543
hasher : Context ,
521
544
}
522
545
523
- impl < W : Write > Write for HashWriter < W > {
546
+ impl < W > Write for HashWriter < W >
547
+ where
548
+ W : Write ,
549
+ {
524
550
fn write ( & mut self , buf : & [ u8 ] ) -> std:: io:: Result < usize > {
525
551
self . hasher . write_all ( buf) ?;
526
552
self . inner . write ( buf)
@@ -531,7 +557,10 @@ impl<W: Write> Write for HashWriter<W> {
531
557
}
532
558
}
533
559
534
- impl < W : Write > HashWriter < W > {
560
+ impl < W > HashWriter < W >
561
+ where
562
+ W : Write ,
563
+ {
535
564
pub fn new ( inner : W ) -> Self {
536
565
HashWriter {
537
566
inner,
@@ -553,7 +582,7 @@ mod tests {
553
582
# Name, Type, SubType, Offset, Size, Flags
554
583
nvs, data, nvs, 0x9000, 0x6000,
555
584
phy_init, data, phy, 0xf000, 0x1000,
556
- factory, app, factory, 0x10000, 1M,
585
+ factory, app, factory, 0x10000, 1M, encrypted
557
586
" ;
558
587
559
588
const PTABLE_1 : & str = "
@@ -644,6 +673,12 @@ phy_init, data, phy, 0xf000, 0x1000,
644
673
let pt0 = PartitionTable :: try_from_str ( PTABLE_0 ) ;
645
674
assert ! ( pt0. is_ok( ) ) ;
646
675
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
+
647
682
let pt1 = PartitionTable :: try_from_str ( PTABLE_1 ) ;
648
683
assert ! ( pt1. is_ok( ) ) ;
649
684
@@ -652,6 +687,7 @@ phy_init, data, phy, 0xf000, 0x1000,
652
687
653
688
PartitionTable :: try_from_str ( PTABLE_NO_FACTORY )
654
689
. expect ( "Failed to parse partition table without factory partition" ) ;
690
+
655
691
PartitionTable :: try_from_str ( PTABLE_NO_APP )
656
692
. expect_err ( "Failed to reject partition table without factory or ota partition" ) ;
657
693
}
0 commit comments