@@ -292,7 +292,7 @@ class ESPLoader(object):
292
292
IROM_MAP_END = 0x40300000
293
293
294
294
# The number of bytes in the UART response that signify command status
295
- STATUS_BYTES_LENGTH = 2
295
+ STATUS_BYTES_LENGTH = 4
296
296
297
297
# Bootloader flashing offset
298
298
BOOTLOADER_FLASH_OFFSET = 0x0
@@ -345,9 +345,9 @@ def __init__(self, port=DEFAULT_PORT, baud=ESP_ROM_BAUD, trace_enabled=False):
345
345
# Device-and-runtime-specific cache
346
346
self .cache = {
347
347
"flash_id" : None ,
348
- "chip_id" : None ,
349
348
"uart_no" : None ,
350
349
"usb_pid" : None ,
350
+ "security_info" : None ,
351
351
}
352
352
353
353
if isinstance (port , str ):
@@ -773,17 +773,26 @@ def connect(
773
773
if not detecting :
774
774
from .targets import ROM_LIST
775
775
776
- # Perform a dummy read_reg to check if the chip is in secure download mode
777
- try :
778
- chip_magic_value = self .read_reg (ESPLoader .CHIP_DETECT_MAGIC_REG_ADDR )
779
- except UnsupportedCommandError :
780
- self .secure_download_mode = True
781
-
782
776
# Check if chip supports reading chip ID from the get-security-info command
783
777
try :
778
+ # get_chip_id() raises FatalError if the chip does not have a chip ID
779
+ # (ESP32-S2)
784
780
chip_id = self .get_chip_id ()
785
- except (UnsupportedCommandError , struct .error , FatalError ):
781
+ si = self .get_security_info ()
782
+ self .secure_download_mode = si ["parsed_flags" ]["SECURE_DOWNLOAD_ENABLE" ]
783
+ except (UnsupportedCommandError , FatalError ):
786
784
chip_id = None
785
+ # Try to read the chip magic value to verify the chip type
786
+ # (ESP8266, ESP32, ESP32-S2)
787
+ try :
788
+ chip_magic_value = self .read_reg (
789
+ ESPLoader .CHIP_DETECT_MAGIC_REG_ADDR
790
+ )
791
+ except UnsupportedCommandError :
792
+ # If the chip does not support reading the chip magic value,
793
+ # it is ESP32-S2 in SDM
794
+ chip_magic_value = None
795
+ self .secure_download_mode = True
787
796
788
797
detected = None
789
798
chip_arg_wrong = False
@@ -807,18 +816,14 @@ def connect(
807
816
if cls .USES_MAGIC_VALUE and chip_magic_value == cls .MAGIC_VALUE :
808
817
detected = cls
809
818
break
810
- # If we can't read chip ID and the chip is in SDM (ESP32 or ESP32-S2),
811
- # we can't verify
812
- elif not chip_id and self .secure_download_mode :
813
- if self .CHIP_NAME not in ["ESP32" , "ESP32-S2" ]:
814
- chip_arg_wrong = True
815
- detected = "ESP32 or ESP32-S2"
816
- else :
817
- log .note (
818
- f"Can't verify this chip is { self .CHIP_NAME } "
819
- "because of active Secure Download Mode. "
820
- "Please check it manually."
821
- )
819
+ # If we can't read chip ID and the chip is in SDM, it is ESP32-S2
820
+ elif (
821
+ not chip_id
822
+ and self .secure_download_mode
823
+ and self .CHIP_NAME != "ESP32-S2"
824
+ ):
825
+ chip_arg_wrong = True
826
+ detected = "ESP32-S2"
822
827
823
828
if chip_arg_wrong :
824
829
if warnings and detected is None :
@@ -1051,30 +1056,76 @@ def flash_type(self):
1051
1056
"""Read flash type bit field from eFuse. Returns 0, 1, None (not present)"""
1052
1057
return None # not implemented for all chip targets
1053
1058
1054
- def get_security_info (self ):
1059
+ def get_security_info (self , cache = True ):
1060
+ """
1061
+ Get security information from the ESP device including flags,
1062
+ flash encryption count,
1063
+ key purposes, chip ID, and API version.
1064
+
1065
+ The security info command response format according to the ESP32-S3
1066
+ documentation:
1067
+ - 32 bits flags
1068
+ - 1 byte flash_crypt_cnt
1069
+ - 7x1 byte key_purposes
1070
+ - 32-bit word chip_id (ESP32-S3 and later)
1071
+ - 32-bit word eco_version/api_version (ESP32-S3 and later)
1072
+
1073
+ Returns a dictionary with parsed security information and individual
1074
+ flag status.
1075
+ """
1076
+ if cache and self .cache ["security_info" ] is not None :
1077
+ return self .cache ["security_info" ]
1078
+
1079
+ # The following mapping was taken from the ROM code
1080
+ # This mapping is same across all targets in the ROM
1081
+ SECURITY_INFO_FLAG_MAP = {
1082
+ "SECURE_BOOT_EN" : (1 << 0 ),
1083
+ "SECURE_BOOT_AGGRESSIVE_REVOKE" : (1 << 1 ),
1084
+ "SECURE_DOWNLOAD_ENABLE" : (1 << 2 ),
1085
+ "SECURE_BOOT_KEY_REVOKE0" : (1 << 3 ),
1086
+ "SECURE_BOOT_KEY_REVOKE1" : (1 << 4 ),
1087
+ "SECURE_BOOT_KEY_REVOKE2" : (1 << 5 ),
1088
+ "SOFT_DIS_JTAG" : (1 << 6 ),
1089
+ "HARD_DIS_JTAG" : (1 << 7 ),
1090
+ "DIS_USB" : (1 << 8 ),
1091
+ "DIS_DOWNLOAD_DCACHE" : (1 << 9 ),
1092
+ "DIS_DOWNLOAD_ICACHE" : (1 << 10 ),
1093
+ }
1094
+
1095
+ def parse_security_flags (flags_value ):
1096
+ """Parse security flags into individual boolean values"""
1097
+ parsed_flags = {}
1098
+ for flag_name , flag_mask in SECURITY_INFO_FLAG_MAP .items ():
1099
+ parsed_flags [flag_name ] = (flags_value & flag_mask ) != 0
1100
+ return parsed_flags
1101
+
1055
1102
res = self .check_command (
1056
1103
"get security info" , self .ESP_CMDS ["GET_SECURITY_INFO" ], b""
1057
1104
)
1058
1105
esp32s2 = True if len (res ) == 12 else False
1059
1106
res = struct .unpack ("<IBBBBBBBB" if esp32s2 else "<IBBBBBBBBII" , res )
1060
- return {
1107
+
1108
+ security_info = {
1061
1109
"flags" : res [0 ],
1062
1110
"flash_crypt_cnt" : res [1 ],
1063
1111
"key_purposes" : res [2 :9 ],
1064
1112
"chip_id" : None if esp32s2 else res [9 ],
1065
1113
"api_version" : None if esp32s2 else res [10 ],
1114
+ "parsed_flags" : parse_security_flags (res [0 ]),
1066
1115
}
1067
1116
1117
+ self .cache ["security_info" ] = security_info
1118
+ return security_info
1119
+
1068
1120
def get_chip_id (self ):
1069
- if self .cache ["chip_id" ] is None :
1070
- res = self .check_command (
1071
- "get security info" , self .ESP_CMDS ["GET_SECURITY_INFO" ], b""
1121
+ chip_id = self .get_security_info ()["chip_id" ]
1122
+ if chip_id is None :
1123
+ raise FatalError (
1124
+ "Security info command does not contain chip ID. "
1125
+ "This is expected for ESP32-S2 which doesn't support chip ID "
1126
+ "in security info."
1072
1127
)
1073
- res = struct .unpack (
1074
- "<IBBBBBBBBI" , res [:16 ]
1075
- ) # 4b flags, 1b flash_crypt_cnt, 7*1b key_purposes, 4b chip_id
1076
- self .cache ["chip_id" ] = res [9 ] # 2/4 status bytes invariant
1077
- return self .cache ["chip_id" ]
1128
+ return chip_id
1078
1129
1079
1130
def get_uart_no (self ):
1080
1131
"""
0 commit comments