5
5
//! device.
6
6
7
7
use std:: {
8
+ collections:: HashMap ,
9
+ fmt,
8
10
io:: { BufWriter , Read , Write } ,
9
11
iter:: zip,
10
12
thread:: sleep,
@@ -13,6 +15,7 @@ use std::{
13
15
14
16
use log:: { debug, info} ;
15
17
use regex:: Regex ;
18
+ use serde:: { Deserialize , Serialize } ;
16
19
use serialport:: { SerialPort , UsbPortInfo } ;
17
20
use slip_codec:: SlipDecoder ;
18
21
@@ -52,6 +55,176 @@ pub type Port = serialport::TTYPort;
52
55
/// Alias for the serial COMPort.
53
56
pub type Port = serialport:: COMPort ;
54
57
58
+ /// Security Info Response containing chip security information
59
+ #[ derive( Debug , Clone , Copy , PartialEq , Eq , Hash , Deserialize , Serialize ) ]
60
+ pub struct SecurityInfo {
61
+ /// 32 bits flags
62
+ pub flags : u32 ,
63
+ /// 1 byte flash_crypt_cnt
64
+ pub flash_crypt_cnt : u8 ,
65
+ /// 7 bytes key purposes
66
+ pub key_purposes : [ u8 ; 7 ] ,
67
+ /// 32-bit word chip id
68
+ pub chip_id : Option < u32 > ,
69
+ /// 32-bit word eco version
70
+ pub eco_version : Option < u32 > ,
71
+ }
72
+
73
+ impl SecurityInfo {
74
+ fn security_flag_map ( ) -> HashMap < & ' static str , u32 > {
75
+ HashMap :: from ( [
76
+ ( "SECURE_BOOT_EN" , 1 << 0 ) ,
77
+ ( "SECURE_BOOT_AGGRESSIVE_REVOKE" , 1 << 1 ) ,
78
+ ( "SECURE_DOWNLOAD_ENABLE" , 1 << 2 ) ,
79
+ ( "SECURE_BOOT_KEY_REVOKE0" , 1 << 3 ) ,
80
+ ( "SECURE_BOOT_KEY_REVOKE1" , 1 << 4 ) ,
81
+ ( "SECURE_BOOT_KEY_REVOKE2" , 1 << 5 ) ,
82
+ ( "SOFT_DIS_JTAG" , 1 << 6 ) ,
83
+ ( "HARD_DIS_JTAG" , 1 << 7 ) ,
84
+ ( "DIS_USB" , 1 << 8 ) ,
85
+ ( "DIS_DOWNLOAD_DCACHE" , 1 << 9 ) ,
86
+ ( "DIS_DOWNLOAD_ICACHE" , 1 << 10 ) ,
87
+ ] )
88
+ }
89
+
90
+ fn security_flag_status ( & self , flag_name : & str ) -> bool {
91
+ if let Some ( & flag) = Self :: security_flag_map ( ) . get ( flag_name) {
92
+ ( self . flags & flag) != 0
93
+ } else {
94
+ false
95
+ }
96
+ }
97
+ }
98
+
99
+ impl TryFrom < & [ u8 ] > for SecurityInfo {
100
+ type Error = Error ;
101
+
102
+ fn try_from ( bytes : & [ u8 ] ) -> Result < Self , Self :: Error > {
103
+ let esp32s2 = bytes. len ( ) == 12 ;
104
+
105
+ if bytes. len ( ) < 12 {
106
+ return Err ( Error :: InvalidResponse ( format ! (
107
+ "expected response of at least 12 bytes, received {} bytes" ,
108
+ bytes. len( )
109
+ ) ) ) ;
110
+ }
111
+
112
+ // Parse response bytes
113
+ let flags = u32:: from_le_bytes ( bytes[ 0 ..4 ] . try_into ( ) ?) ;
114
+ let flash_crypt_cnt = bytes[ 4 ] ;
115
+ let key_purposes: [ u8 ; 7 ] = bytes[ 5 ..12 ] . try_into ( ) ?;
116
+
117
+ let ( chip_id, eco_version) = if esp32s2 {
118
+ ( None , None ) // ESP32-S2 doesn't have these values
119
+ } else {
120
+ if bytes. len ( ) < 20 {
121
+ return Err ( Error :: InvalidResponse ( format ! (
122
+ "expected response of at least 20 bytes, received {} bytes" ,
123
+ bytes. len( )
124
+ ) ) ) ;
125
+ }
126
+ let chip_id = u32:: from_le_bytes ( bytes[ 12 ..16 ] . try_into ( ) ?) ;
127
+ let eco_version = u32:: from_le_bytes ( bytes[ 16 ..20 ] . try_into ( ) ?) ;
128
+ ( Some ( chip_id) , Some ( eco_version) )
129
+ } ;
130
+
131
+ Ok ( SecurityInfo {
132
+ flags,
133
+ flash_crypt_cnt,
134
+ key_purposes,
135
+ chip_id,
136
+ eco_version,
137
+ } )
138
+ }
139
+ }
140
+
141
+ impl fmt:: Display for SecurityInfo {
142
+ fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
143
+ let key_purposes_str = self
144
+ . key_purposes
145
+ . iter ( )
146
+ . map ( |b| format ! ( "{b}" ) )
147
+ . collect :: < Vec < _ > > ( )
148
+ . join ( ", " ) ;
149
+
150
+ writeln ! ( f, "\n Security Information:" ) ?;
151
+ writeln ! ( f, "=====================" ) ?;
152
+ writeln ! ( f, "Flags: {:#010x} ({:b})" , self . flags, self . flags) ?;
153
+ writeln ! ( f, "Key Purposes: [{key_purposes_str}]" ) ?;
154
+
155
+ // Only print Chip ID if it's Some(value)
156
+ if let Some ( chip_id) = self . chip_id {
157
+ writeln ! ( f, "Chip ID: {chip_id}" ) ?;
158
+ }
159
+
160
+ // Only print API Version if it's Some(value)
161
+ if let Some ( api_version) = self . eco_version {
162
+ writeln ! ( f, "API Version: {api_version}" ) ?;
163
+ }
164
+
165
+ // Secure Boot
166
+ if self . security_flag_status ( "SECURE_BOOT_EN" ) {
167
+ writeln ! ( f, "Secure Boot: Enabled" ) ?;
168
+ if self . security_flag_status ( "SECURE_BOOT_AGGRESSIVE_REVOKE" ) {
169
+ writeln ! ( f, "Secure Boot Aggressive key revocation: Enabled" ) ?;
170
+ }
171
+
172
+ let revoked_keys: Vec < _ > = [
173
+ "SECURE_BOOT_KEY_REVOKE0" ,
174
+ "SECURE_BOOT_KEY_REVOKE1" ,
175
+ "SECURE_BOOT_KEY_REVOKE2" ,
176
+ ]
177
+ . iter ( )
178
+ . enumerate ( )
179
+ . filter ( |( _, key) | self . security_flag_status ( key) )
180
+ . map ( |( i, _) | format ! ( "Secure Boot Key{i} is Revoked" ) )
181
+ . collect ( ) ;
182
+
183
+ if !revoked_keys. is_empty ( ) {
184
+ writeln ! (
185
+ f,
186
+ "Secure Boot Key Revocation Status:\n {}" ,
187
+ revoked_keys. join( "\n " )
188
+ ) ?;
189
+ }
190
+ } else {
191
+ writeln ! ( f, "Secure Boot: Disabled" ) ?;
192
+ }
193
+
194
+ // Flash Encryption
195
+ if self . flash_crypt_cnt . count_ones ( ) % 2 != 0 {
196
+ writeln ! ( f, "Flash Encryption: Enabled" ) ?;
197
+ } else {
198
+ writeln ! ( f, "Flash Encryption: Disabled" ) ?;
199
+ }
200
+
201
+ let crypt_cnt_str = "SPI Boot Crypt Count (SPI_BOOT_CRYPT_CNT)" ;
202
+ writeln ! ( f, "{}: 0x{:x}" , crypt_cnt_str, self . flash_crypt_cnt) ?;
203
+
204
+ // Cache Disabling
205
+ if self . security_flag_status ( "DIS_DOWNLOAD_DCACHE" ) {
206
+ writeln ! ( f, "Dcache in UART download mode: Disabled" ) ?;
207
+ }
208
+ if self . security_flag_status ( "DIS_DOWNLOAD_ICACHE" ) {
209
+ writeln ! ( f, "Icache in UART download mode: Disabled" ) ?;
210
+ }
211
+
212
+ // JTAG Status
213
+ if self . security_flag_status ( "HARD_DIS_JTAG" ) {
214
+ writeln ! ( f, "JTAG: Permanently Disabled" ) ?;
215
+ } else if self . security_flag_status ( "SOFT_DIS_JTAG" ) {
216
+ writeln ! ( f, "JTAG: Software Access Disabled" ) ?;
217
+ }
218
+
219
+ // USB Access
220
+ if self . security_flag_status ( "DIS_USB" ) {
221
+ writeln ! ( f, "USB Access: Disabled" ) ?;
222
+ }
223
+
224
+ Ok ( ( ) )
225
+ }
226
+ }
227
+
55
228
/// An established connection with a target device.
56
229
#[ derive( Debug ) ]
57
230
pub struct Connection {
@@ -552,24 +725,54 @@ impl Connection {
552
725
self . before_operation
553
726
}
554
727
728
+ /// Gets security information from the chip.
729
+ #[ cfg( feature = "serialport" ) ]
730
+ pub fn security_info ( & mut self , use_stub : bool ) -> Result < SecurityInfo , crate :: error:: Error > {
731
+ self . with_timeout ( CommandType :: GetSecurityInfo . timeout ( ) , |connection| {
732
+ let response = connection. command ( Command :: GetSecurityInfo ) ?;
733
+ // Extract raw bytes and convert them into `SecurityInfo`
734
+ if let crate :: command:: CommandResponseValue :: Vector ( data) = response {
735
+ // HACK: Not quite sure why there seem to be 4 extra bytes at the end of the
736
+ // response when the stub is not being used...
737
+ let end = if use_stub { data. len ( ) } else { data. len ( ) - 4 } ;
738
+ SecurityInfo :: try_from ( & data[ ..end] )
739
+ } else {
740
+ Err ( Error :: InvalidResponse (
741
+ "response was not a vector of bytes" . into ( ) ,
742
+ ) )
743
+ }
744
+ } )
745
+ }
746
+
555
747
/// Detects which chip is connected to this connection.
748
+ #[ cfg( feature = "serialport" ) ]
556
749
pub fn detect_chip (
557
750
& mut self ,
558
751
use_stub : bool ,
559
752
) -> Result < crate :: target:: Chip , crate :: error:: Error > {
560
- // Try to read the magic value from the chip
561
- let magic = if use_stub {
562
- self . with_timeout ( CommandType :: ReadReg . timeout ( ) , |connection| {
563
- connection. command ( Command :: ReadReg {
564
- address : CHIP_DETECT_MAGIC_REG_ADDR ,
565
- } )
566
- } ) ?
567
- . try_into ( ) ?
568
- } else {
569
- self . read_reg ( CHIP_DETECT_MAGIC_REG_ADDR ) ?
570
- } ;
571
- debug ! ( "Read chip magic value: 0x{magic:08x}" ) ;
572
- Chip :: from_magic ( magic)
753
+ match self . security_info ( use_stub) {
754
+ Ok ( info) if info. chip_id . is_some ( ) => {
755
+ let chip_id = info. chip_id . unwrap ( ) as u16 ;
756
+ let chip = Chip :: try_from ( chip_id) ?;
757
+
758
+ Ok ( chip)
759
+ }
760
+ _ => {
761
+ // Fall back to reading the magic value from the chip
762
+ let magic = if use_stub {
763
+ self . with_timeout ( CommandType :: ReadReg . timeout ( ) , |connection| {
764
+ connection. command ( Command :: ReadReg {
765
+ address : CHIP_DETECT_MAGIC_REG_ADDR ,
766
+ } )
767
+ } ) ?
768
+ . try_into ( ) ?
769
+ } else {
770
+ self . read_reg ( CHIP_DETECT_MAGIC_REG_ADDR ) ?
771
+ } ;
772
+ debug ! ( "Read chip magic value: 0x{magic:08x}" ) ;
773
+ Chip :: from_magic ( magic)
774
+ }
775
+ }
573
776
}
574
777
}
575
778
0 commit comments