55//! device.
66
77use std:: {
8+ collections:: HashMap ,
9+ fmt,
810 io:: { BufWriter , Read , Write } ,
911 iter:: zip,
1012 thread:: sleep,
@@ -13,6 +15,7 @@ use std::{
1315
1416use log:: { debug, info} ;
1517use regex:: Regex ;
18+ use serde:: { Deserialize , Serialize } ;
1619use serialport:: { SerialPort , UsbPortInfo } ;
1720use slip_codec:: SlipDecoder ;
1821
@@ -52,6 +55,176 @@ pub type Port = serialport::TTYPort;
5255/// Alias for the serial COMPort.
5356pub type Port = serialport:: COMPort ;
5457
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+
55228/// An established connection with a target device.
56229#[ derive( Debug ) ]
57230pub struct Connection {
@@ -552,24 +725,54 @@ impl Connection {
552725 self . before_operation
553726 }
554727
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+
555747 /// Detects which chip is connected to this connection.
748+ #[ cfg( feature = "serialport" ) ]
556749 pub fn detect_chip (
557750 & mut self ,
558751 use_stub : bool ,
559752 ) -> 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+ }
573776 }
574777}
575778
0 commit comments