Skip to content

Commit 0b3460f

Browse files
Dzarda7radimkarnis
authored andcommitted
fix: enable ESP32-P4 ECO5 chip detection
Register was read in order to detect SDM, this cannot be done with new ESP32-P4 ECO5, because it has different address space and crashes when trying to read current magic address. This also improves detection for ESP32-S2 as is is only chip that has SDM support and is detected by reading magic address.
1 parent 9cb4e7d commit 0b3460f

File tree

3 files changed

+98
-73
lines changed

3 files changed

+98
-73
lines changed

esptool/cmds.py

Lines changed: 14 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -150,21 +150,16 @@ def check_if_stub(instance: ESPLoader) -> ESPLoader:
150150
continue
151151
if chip_id == cls.IMAGE_CHIP_ID:
152152
inst = cls(detect_port._port, baud, trace_enabled=trace_enabled)
153-
try:
154-
inst.read_reg(
155-
ESPLoader.CHIP_DETECT_MAGIC_REG_ADDR
156-
) # Dummy read to check Secure Download mode
157-
except UnsupportedCommandError:
158-
inst.secure_download_mode = True
153+
si = inst.get_security_info()
154+
inst.secure_download_mode = si["parsed_flags"]["SECURE_DOWNLOAD_ENABLE"]
159155
inst = check_if_stub(inst)
160156
inst._post_connect()
161157
break
162158
else:
163159
err_msg = f"Unexpected chip ID value {chip_id}."
164-
except (UnsupportedCommandError, struct.error, FatalError):
160+
except (UnsupportedCommandError, FatalError):
165161
# UnsupportedCommandError: ESP8266/ESP32 ROM
166-
# struct.error: ESP32-S2
167-
# FatalError: ESP8266/ESP32 STUB
162+
# FatalError: ESP8266/ESP32 STUB or ESP32-S2
168163
try:
169164
chip_magic_value = detect_port.read_reg(
170165
ESPLoader.CHIP_DETECT_MAGIC_REG_ADDR
@@ -1495,30 +1490,9 @@ def get_security_info(esp: ESPLoader) -> None:
14951490
Args:
14961491
esp: Initiated esp object connected to a real device.
14971492
"""
1498-
# The following mapping was taken from the ROM code
1499-
# This mapping is same across all targets in the ROM
1500-
SECURITY_INFO_FLAG_MAP = {
1501-
"SECURE_BOOT_EN": (1 << 0),
1502-
"SECURE_BOOT_AGGRESSIVE_REVOKE": (1 << 1),
1503-
"SECURE_DOWNLOAD_ENABLE": (1 << 2),
1504-
"SECURE_BOOT_KEY_REVOKE0": (1 << 3),
1505-
"SECURE_BOOT_KEY_REVOKE1": (1 << 4),
1506-
"SECURE_BOOT_KEY_REVOKE2": (1 << 5),
1507-
"SOFT_DIS_JTAG": (1 << 6),
1508-
"HARD_DIS_JTAG": (1 << 7),
1509-
"DIS_USB": (1 << 8),
1510-
"DIS_DOWNLOAD_DCACHE": (1 << 9),
1511-
"DIS_DOWNLOAD_ICACHE": (1 << 10),
1512-
}
1513-
1514-
# Get the status of respective security flag
1515-
def get_security_flag_status(flag_name, flags_value):
1516-
try:
1517-
return (flags_value & SECURITY_INFO_FLAG_MAP[flag_name]) != 0
1518-
except KeyError:
1519-
raise ValueError(f"Invalid flag name: {flag_name}")
1520-
15211493
si = esp.get_security_info()
1494+
parsed_flags = si["parsed_flags"]
1495+
15221496
title = "Security Information:"
15231497
log.print(title)
15241498
log.print("=" * len(title))
@@ -1537,11 +1511,9 @@ def get_security_flag_status(flag_name, flags_value):
15371511
log.print("Chip ID: {}".format(si["chip_id"]))
15381512
log.print("API Version: {}".format(si["api_version"]))
15391513

1540-
flags = si["flags"]
1541-
1542-
if get_security_flag_status("SECURE_BOOT_EN", flags):
1514+
if parsed_flags["SECURE_BOOT_EN"]:
15431515
log.print("Secure Boot: Enabled")
1544-
if get_security_flag_status("SECURE_BOOT_AGGRESSIVE_REVOKE", flags):
1516+
if parsed_flags["SECURE_BOOT_AGGRESSIVE_REVOKE"]:
15451517
log.print("Secure Boot Aggressive key revocation: Enabled")
15461518

15471519
revoked_keys = []
@@ -1552,7 +1524,7 @@ def get_security_flag_status(flag_name, flags_value):
15521524
"SECURE_BOOT_KEY_REVOKE2",
15531525
]
15541526
):
1555-
if get_security_flag_status(key, flags):
1527+
if parsed_flags[key]:
15561528
revoked_keys.append(i)
15571529

15581530
if len(revoked_keys) > 0:
@@ -1575,19 +1547,19 @@ def get_security_flag_status(flag_name, flags_value):
15751547

15761548
log.print(f"{CRYPT_CNT_STRING}: {si['flash_crypt_cnt']:#x}")
15771549

1578-
if get_security_flag_status("DIS_DOWNLOAD_DCACHE", flags):
1550+
if parsed_flags["DIS_DOWNLOAD_DCACHE"]:
15791551
log.print("Dcache in UART download mode: Disabled")
15801552

1581-
if get_security_flag_status("DIS_DOWNLOAD_ICACHE", flags):
1553+
if parsed_flags["DIS_DOWNLOAD_ICACHE"]:
15821554
log.print("Icache in UART download mode: Disabled")
15831555

1584-
hard_dis_jtag = get_security_flag_status("HARD_DIS_JTAG", flags)
1585-
soft_dis_jtag = get_security_flag_status("SOFT_DIS_JTAG", flags)
1556+
hard_dis_jtag = parsed_flags["HARD_DIS_JTAG"]
1557+
soft_dis_jtag = parsed_flags["SOFT_DIS_JTAG"]
15861558
if hard_dis_jtag:
15871559
log.print("JTAG: Permanently Disabled")
15881560
elif soft_dis_jtag:
15891561
log.print("JTAG: Software Access Disabled")
1590-
if get_security_flag_status("DIS_USB", flags):
1562+
if parsed_flags["DIS_USB"]:
15911563
log.print("USB Access: Disabled")
15921564

15931565

esptool/loader.py

Lines changed: 82 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -292,7 +292,7 @@ class ESPLoader(object):
292292
IROM_MAP_END = 0x40300000
293293

294294
# The number of bytes in the UART response that signify command status
295-
STATUS_BYTES_LENGTH = 2
295+
STATUS_BYTES_LENGTH = 4
296296

297297
# Bootloader flashing offset
298298
BOOTLOADER_FLASH_OFFSET = 0x0
@@ -345,9 +345,9 @@ def __init__(self, port=DEFAULT_PORT, baud=ESP_ROM_BAUD, trace_enabled=False):
345345
# Device-and-runtime-specific cache
346346
self.cache = {
347347
"flash_id": None,
348-
"chip_id": None,
349348
"uart_no": None,
350349
"usb_pid": None,
350+
"security_info": None,
351351
}
352352

353353
if isinstance(port, str):
@@ -773,17 +773,26 @@ def connect(
773773
if not detecting:
774774
from .targets import ROM_LIST
775775

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-
782776
# Check if chip supports reading chip ID from the get-security-info command
783777
try:
778+
# get_chip_id() raises FatalError if the chip does not have a chip ID
779+
# (ESP32-S2)
784780
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):
786784
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
787796

788797
detected = None
789798
chip_arg_wrong = False
@@ -807,18 +816,14 @@ def connect(
807816
if cls.USES_MAGIC_VALUE and chip_magic_value == cls.MAGIC_VALUE:
808817
detected = cls
809818
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"
822827

823828
if chip_arg_wrong:
824829
if warnings and detected is None:
@@ -1051,30 +1056,76 @@ def flash_type(self):
10511056
"""Read flash type bit field from eFuse. Returns 0, 1, None (not present)"""
10521057
return None # not implemented for all chip targets
10531058

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+
10551102
res = self.check_command(
10561103
"get security info", self.ESP_CMDS["GET_SECURITY_INFO"], b""
10571104
)
10581105
esp32s2 = True if len(res) == 12 else False
10591106
res = struct.unpack("<IBBBBBBBB" if esp32s2 else "<IBBBBBBBBII", res)
1060-
return {
1107+
1108+
security_info = {
10611109
"flags": res[0],
10621110
"flash_crypt_cnt": res[1],
10631111
"key_purposes": res[2:9],
10641112
"chip_id": None if esp32s2 else res[9],
10651113
"api_version": None if esp32s2 else res[10],
1114+
"parsed_flags": parse_security_flags(res[0]),
10661115
}
10671116

1117+
self.cache["security_info"] = security_info
1118+
return security_info
1119+
10681120
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."
10721127
)
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
10781129

10791130
def get_uart_no(self):
10801131
"""

esptool/targets/esp8266.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ class ESP8266ROM(ESPLoader):
2929

3030
UART_CLKDIV_REG = 0x60000014
3131

32+
STATUS_BYTES_LENGTH = 2
33+
3234
XTAL_CLK_DIVIDER = 2
3335

3436
FLASH_SIZES = {

0 commit comments

Comments
 (0)