mirror of
https://github.com/espressif/esptool.git
synced 2025-10-22 16:37:36 +08:00
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.
This commit is contained in:

committed by
Radim Karniš

parent
9cb4e7de81
commit
0b3460fa12
@@ -150,21 +150,16 @@ def detect_chip(
|
|||||||
continue
|
continue
|
||||||
if chip_id == cls.IMAGE_CHIP_ID:
|
if chip_id == cls.IMAGE_CHIP_ID:
|
||||||
inst = cls(detect_port._port, baud, trace_enabled=trace_enabled)
|
inst = cls(detect_port._port, baud, trace_enabled=trace_enabled)
|
||||||
try:
|
si = inst.get_security_info()
|
||||||
inst.read_reg(
|
inst.secure_download_mode = si["parsed_flags"]["SECURE_DOWNLOAD_ENABLE"]
|
||||||
ESPLoader.CHIP_DETECT_MAGIC_REG_ADDR
|
|
||||||
) # Dummy read to check Secure Download mode
|
|
||||||
except UnsupportedCommandError:
|
|
||||||
inst.secure_download_mode = True
|
|
||||||
inst = check_if_stub(inst)
|
inst = check_if_stub(inst)
|
||||||
inst._post_connect()
|
inst._post_connect()
|
||||||
break
|
break
|
||||||
else:
|
else:
|
||||||
err_msg = f"Unexpected chip ID value {chip_id}."
|
err_msg = f"Unexpected chip ID value {chip_id}."
|
||||||
except (UnsupportedCommandError, struct.error, FatalError):
|
except (UnsupportedCommandError, FatalError):
|
||||||
# UnsupportedCommandError: ESP8266/ESP32 ROM
|
# UnsupportedCommandError: ESP8266/ESP32 ROM
|
||||||
# struct.error: ESP32-S2
|
# FatalError: ESP8266/ESP32 STUB or ESP32-S2
|
||||||
# FatalError: ESP8266/ESP32 STUB
|
|
||||||
try:
|
try:
|
||||||
chip_magic_value = detect_port.read_reg(
|
chip_magic_value = detect_port.read_reg(
|
||||||
ESPLoader.CHIP_DETECT_MAGIC_REG_ADDR
|
ESPLoader.CHIP_DETECT_MAGIC_REG_ADDR
|
||||||
@@ -1495,30 +1490,9 @@ def get_security_info(esp: ESPLoader) -> None:
|
|||||||
Args:
|
Args:
|
||||||
esp: Initiated esp object connected to a real device.
|
esp: Initiated esp object connected to a real device.
|
||||||
"""
|
"""
|
||||||
# The following mapping was taken from the ROM code
|
|
||||||
# This mapping is same across all targets in the ROM
|
|
||||||
SECURITY_INFO_FLAG_MAP = {
|
|
||||||
"SECURE_BOOT_EN": (1 << 0),
|
|
||||||
"SECURE_BOOT_AGGRESSIVE_REVOKE": (1 << 1),
|
|
||||||
"SECURE_DOWNLOAD_ENABLE": (1 << 2),
|
|
||||||
"SECURE_BOOT_KEY_REVOKE0": (1 << 3),
|
|
||||||
"SECURE_BOOT_KEY_REVOKE1": (1 << 4),
|
|
||||||
"SECURE_BOOT_KEY_REVOKE2": (1 << 5),
|
|
||||||
"SOFT_DIS_JTAG": (1 << 6),
|
|
||||||
"HARD_DIS_JTAG": (1 << 7),
|
|
||||||
"DIS_USB": (1 << 8),
|
|
||||||
"DIS_DOWNLOAD_DCACHE": (1 << 9),
|
|
||||||
"DIS_DOWNLOAD_ICACHE": (1 << 10),
|
|
||||||
}
|
|
||||||
|
|
||||||
# Get the status of respective security flag
|
|
||||||
def get_security_flag_status(flag_name, flags_value):
|
|
||||||
try:
|
|
||||||
return (flags_value & SECURITY_INFO_FLAG_MAP[flag_name]) != 0
|
|
||||||
except KeyError:
|
|
||||||
raise ValueError(f"Invalid flag name: {flag_name}")
|
|
||||||
|
|
||||||
si = esp.get_security_info()
|
si = esp.get_security_info()
|
||||||
|
parsed_flags = si["parsed_flags"]
|
||||||
|
|
||||||
title = "Security Information:"
|
title = "Security Information:"
|
||||||
log.print(title)
|
log.print(title)
|
||||||
log.print("=" * len(title))
|
log.print("=" * len(title))
|
||||||
@@ -1537,11 +1511,9 @@ def get_security_info(esp: ESPLoader) -> None:
|
|||||||
log.print("Chip ID: {}".format(si["chip_id"]))
|
log.print("Chip ID: {}".format(si["chip_id"]))
|
||||||
log.print("API Version: {}".format(si["api_version"]))
|
log.print("API Version: {}".format(si["api_version"]))
|
||||||
|
|
||||||
flags = si["flags"]
|
if parsed_flags["SECURE_BOOT_EN"]:
|
||||||
|
|
||||||
if get_security_flag_status("SECURE_BOOT_EN", flags):
|
|
||||||
log.print("Secure Boot: Enabled")
|
log.print("Secure Boot: Enabled")
|
||||||
if get_security_flag_status("SECURE_BOOT_AGGRESSIVE_REVOKE", flags):
|
if parsed_flags["SECURE_BOOT_AGGRESSIVE_REVOKE"]:
|
||||||
log.print("Secure Boot Aggressive key revocation: Enabled")
|
log.print("Secure Boot Aggressive key revocation: Enabled")
|
||||||
|
|
||||||
revoked_keys = []
|
revoked_keys = []
|
||||||
@@ -1552,7 +1524,7 @@ def get_security_info(esp: ESPLoader) -> None:
|
|||||||
"SECURE_BOOT_KEY_REVOKE2",
|
"SECURE_BOOT_KEY_REVOKE2",
|
||||||
]
|
]
|
||||||
):
|
):
|
||||||
if get_security_flag_status(key, flags):
|
if parsed_flags[key]:
|
||||||
revoked_keys.append(i)
|
revoked_keys.append(i)
|
||||||
|
|
||||||
if len(revoked_keys) > 0:
|
if len(revoked_keys) > 0:
|
||||||
@@ -1575,19 +1547,19 @@ def get_security_info(esp: ESPLoader) -> None:
|
|||||||
|
|
||||||
log.print(f"{CRYPT_CNT_STRING}: {si['flash_crypt_cnt']:#x}")
|
log.print(f"{CRYPT_CNT_STRING}: {si['flash_crypt_cnt']:#x}")
|
||||||
|
|
||||||
if get_security_flag_status("DIS_DOWNLOAD_DCACHE", flags):
|
if parsed_flags["DIS_DOWNLOAD_DCACHE"]:
|
||||||
log.print("Dcache in UART download mode: Disabled")
|
log.print("Dcache in UART download mode: Disabled")
|
||||||
|
|
||||||
if get_security_flag_status("DIS_DOWNLOAD_ICACHE", flags):
|
if parsed_flags["DIS_DOWNLOAD_ICACHE"]:
|
||||||
log.print("Icache in UART download mode: Disabled")
|
log.print("Icache in UART download mode: Disabled")
|
||||||
|
|
||||||
hard_dis_jtag = get_security_flag_status("HARD_DIS_JTAG", flags)
|
hard_dis_jtag = parsed_flags["HARD_DIS_JTAG"]
|
||||||
soft_dis_jtag = get_security_flag_status("SOFT_DIS_JTAG", flags)
|
soft_dis_jtag = parsed_flags["SOFT_DIS_JTAG"]
|
||||||
if hard_dis_jtag:
|
if hard_dis_jtag:
|
||||||
log.print("JTAG: Permanently Disabled")
|
log.print("JTAG: Permanently Disabled")
|
||||||
elif soft_dis_jtag:
|
elif soft_dis_jtag:
|
||||||
log.print("JTAG: Software Access Disabled")
|
log.print("JTAG: Software Access Disabled")
|
||||||
if get_security_flag_status("DIS_USB", flags):
|
if parsed_flags["DIS_USB"]:
|
||||||
log.print("USB Access: Disabled")
|
log.print("USB Access: Disabled")
|
||||||
|
|
||||||
|
|
||||||
|
@@ -292,7 +292,7 @@ class ESPLoader(object):
|
|||||||
IROM_MAP_END = 0x40300000
|
IROM_MAP_END = 0x40300000
|
||||||
|
|
||||||
# The number of bytes in the UART response that signify command status
|
# The number of bytes in the UART response that signify command status
|
||||||
STATUS_BYTES_LENGTH = 2
|
STATUS_BYTES_LENGTH = 4
|
||||||
|
|
||||||
# Bootloader flashing offset
|
# Bootloader flashing offset
|
||||||
BOOTLOADER_FLASH_OFFSET = 0x0
|
BOOTLOADER_FLASH_OFFSET = 0x0
|
||||||
@@ -345,9 +345,9 @@ class ESPLoader(object):
|
|||||||
# Device-and-runtime-specific cache
|
# Device-and-runtime-specific cache
|
||||||
self.cache = {
|
self.cache = {
|
||||||
"flash_id": None,
|
"flash_id": None,
|
||||||
"chip_id": None,
|
|
||||||
"uart_no": None,
|
"uart_no": None,
|
||||||
"usb_pid": None,
|
"usb_pid": None,
|
||||||
|
"security_info": None,
|
||||||
}
|
}
|
||||||
|
|
||||||
if isinstance(port, str):
|
if isinstance(port, str):
|
||||||
@@ -773,17 +773,26 @@ class ESPLoader(object):
|
|||||||
if not detecting:
|
if not detecting:
|
||||||
from .targets import ROM_LIST
|
from .targets import ROM_LIST
|
||||||
|
|
||||||
# Perform a dummy read_reg to check if the chip is in secure download mode
|
|
||||||
try:
|
|
||||||
chip_magic_value = self.read_reg(ESPLoader.CHIP_DETECT_MAGIC_REG_ADDR)
|
|
||||||
except UnsupportedCommandError:
|
|
||||||
self.secure_download_mode = True
|
|
||||||
|
|
||||||
# Check if chip supports reading chip ID from the get-security-info command
|
# Check if chip supports reading chip ID from the get-security-info command
|
||||||
try:
|
try:
|
||||||
|
# get_chip_id() raises FatalError if the chip does not have a chip ID
|
||||||
|
# (ESP32-S2)
|
||||||
chip_id = self.get_chip_id()
|
chip_id = self.get_chip_id()
|
||||||
except (UnsupportedCommandError, struct.error, FatalError):
|
si = self.get_security_info()
|
||||||
|
self.secure_download_mode = si["parsed_flags"]["SECURE_DOWNLOAD_ENABLE"]
|
||||||
|
except (UnsupportedCommandError, FatalError):
|
||||||
chip_id = None
|
chip_id = None
|
||||||
|
# Try to read the chip magic value to verify the chip type
|
||||||
|
# (ESP8266, ESP32, ESP32-S2)
|
||||||
|
try:
|
||||||
|
chip_magic_value = self.read_reg(
|
||||||
|
ESPLoader.CHIP_DETECT_MAGIC_REG_ADDR
|
||||||
|
)
|
||||||
|
except UnsupportedCommandError:
|
||||||
|
# If the chip does not support reading the chip magic value,
|
||||||
|
# it is ESP32-S2 in SDM
|
||||||
|
chip_magic_value = None
|
||||||
|
self.secure_download_mode = True
|
||||||
|
|
||||||
detected = None
|
detected = None
|
||||||
chip_arg_wrong = False
|
chip_arg_wrong = False
|
||||||
@@ -807,18 +816,14 @@ class ESPLoader(object):
|
|||||||
if cls.USES_MAGIC_VALUE and chip_magic_value == cls.MAGIC_VALUE:
|
if cls.USES_MAGIC_VALUE and chip_magic_value == cls.MAGIC_VALUE:
|
||||||
detected = cls
|
detected = cls
|
||||||
break
|
break
|
||||||
# If we can't read chip ID and the chip is in SDM (ESP32 or ESP32-S2),
|
# If we can't read chip ID and the chip is in SDM, it is ESP32-S2
|
||||||
# we can't verify
|
elif (
|
||||||
elif not chip_id and self.secure_download_mode:
|
not chip_id
|
||||||
if self.CHIP_NAME not in ["ESP32", "ESP32-S2"]:
|
and self.secure_download_mode
|
||||||
chip_arg_wrong = True
|
and self.CHIP_NAME != "ESP32-S2"
|
||||||
detected = "ESP32 or ESP32-S2"
|
):
|
||||||
else:
|
chip_arg_wrong = True
|
||||||
log.note(
|
detected = "ESP32-S2"
|
||||||
f"Can't verify this chip is {self.CHIP_NAME} "
|
|
||||||
"because of active Secure Download Mode. "
|
|
||||||
"Please check it manually."
|
|
||||||
)
|
|
||||||
|
|
||||||
if chip_arg_wrong:
|
if chip_arg_wrong:
|
||||||
if warnings and detected is None:
|
if warnings and detected is None:
|
||||||
@@ -1051,30 +1056,76 @@ class ESPLoader(object):
|
|||||||
"""Read flash type bit field from eFuse. Returns 0, 1, None (not present)"""
|
"""Read flash type bit field from eFuse. Returns 0, 1, None (not present)"""
|
||||||
return None # not implemented for all chip targets
|
return None # not implemented for all chip targets
|
||||||
|
|
||||||
def get_security_info(self):
|
def get_security_info(self, cache=True):
|
||||||
|
"""
|
||||||
|
Get security information from the ESP device including flags,
|
||||||
|
flash encryption count,
|
||||||
|
key purposes, chip ID, and API version.
|
||||||
|
|
||||||
|
The security info command response format according to the ESP32-S3
|
||||||
|
documentation:
|
||||||
|
- 32 bits flags
|
||||||
|
- 1 byte flash_crypt_cnt
|
||||||
|
- 7x1 byte key_purposes
|
||||||
|
- 32-bit word chip_id (ESP32-S3 and later)
|
||||||
|
- 32-bit word eco_version/api_version (ESP32-S3 and later)
|
||||||
|
|
||||||
|
Returns a dictionary with parsed security information and individual
|
||||||
|
flag status.
|
||||||
|
"""
|
||||||
|
if cache and self.cache["security_info"] is not None:
|
||||||
|
return self.cache["security_info"]
|
||||||
|
|
||||||
|
# The following mapping was taken from the ROM code
|
||||||
|
# This mapping is same across all targets in the ROM
|
||||||
|
SECURITY_INFO_FLAG_MAP = {
|
||||||
|
"SECURE_BOOT_EN": (1 << 0),
|
||||||
|
"SECURE_BOOT_AGGRESSIVE_REVOKE": (1 << 1),
|
||||||
|
"SECURE_DOWNLOAD_ENABLE": (1 << 2),
|
||||||
|
"SECURE_BOOT_KEY_REVOKE0": (1 << 3),
|
||||||
|
"SECURE_BOOT_KEY_REVOKE1": (1 << 4),
|
||||||
|
"SECURE_BOOT_KEY_REVOKE2": (1 << 5),
|
||||||
|
"SOFT_DIS_JTAG": (1 << 6),
|
||||||
|
"HARD_DIS_JTAG": (1 << 7),
|
||||||
|
"DIS_USB": (1 << 8),
|
||||||
|
"DIS_DOWNLOAD_DCACHE": (1 << 9),
|
||||||
|
"DIS_DOWNLOAD_ICACHE": (1 << 10),
|
||||||
|
}
|
||||||
|
|
||||||
|
def parse_security_flags(flags_value):
|
||||||
|
"""Parse security flags into individual boolean values"""
|
||||||
|
parsed_flags = {}
|
||||||
|
for flag_name, flag_mask in SECURITY_INFO_FLAG_MAP.items():
|
||||||
|
parsed_flags[flag_name] = (flags_value & flag_mask) != 0
|
||||||
|
return parsed_flags
|
||||||
|
|
||||||
res = self.check_command(
|
res = self.check_command(
|
||||||
"get security info", self.ESP_CMDS["GET_SECURITY_INFO"], b""
|
"get security info", self.ESP_CMDS["GET_SECURITY_INFO"], b""
|
||||||
)
|
)
|
||||||
esp32s2 = True if len(res) == 12 else False
|
esp32s2 = True if len(res) == 12 else False
|
||||||
res = struct.unpack("<IBBBBBBBB" if esp32s2 else "<IBBBBBBBBII", res)
|
res = struct.unpack("<IBBBBBBBB" if esp32s2 else "<IBBBBBBBBII", res)
|
||||||
return {
|
|
||||||
|
security_info = {
|
||||||
"flags": res[0],
|
"flags": res[0],
|
||||||
"flash_crypt_cnt": res[1],
|
"flash_crypt_cnt": res[1],
|
||||||
"key_purposes": res[2:9],
|
"key_purposes": res[2:9],
|
||||||
"chip_id": None if esp32s2 else res[9],
|
"chip_id": None if esp32s2 else res[9],
|
||||||
"api_version": None if esp32s2 else res[10],
|
"api_version": None if esp32s2 else res[10],
|
||||||
|
"parsed_flags": parse_security_flags(res[0]),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.cache["security_info"] = security_info
|
||||||
|
return security_info
|
||||||
|
|
||||||
def get_chip_id(self):
|
def get_chip_id(self):
|
||||||
if self.cache["chip_id"] is None:
|
chip_id = self.get_security_info()["chip_id"]
|
||||||
res = self.check_command(
|
if chip_id is None:
|
||||||
"get security info", self.ESP_CMDS["GET_SECURITY_INFO"], b""
|
raise FatalError(
|
||||||
|
"Security info command does not contain chip ID. "
|
||||||
|
"This is expected for ESP32-S2 which doesn't support chip ID "
|
||||||
|
"in security info."
|
||||||
)
|
)
|
||||||
res = struct.unpack(
|
return chip_id
|
||||||
"<IBBBBBBBBI", res[:16]
|
|
||||||
) # 4b flags, 1b flash_crypt_cnt, 7*1b key_purposes, 4b chip_id
|
|
||||||
self.cache["chip_id"] = res[9] # 2/4 status bytes invariant
|
|
||||||
return self.cache["chip_id"]
|
|
||||||
|
|
||||||
def get_uart_no(self):
|
def get_uart_no(self):
|
||||||
"""
|
"""
|
||||||
|
@@ -29,6 +29,8 @@ class ESP8266ROM(ESPLoader):
|
|||||||
|
|
||||||
UART_CLKDIV_REG = 0x60000014
|
UART_CLKDIV_REG = 0x60000014
|
||||||
|
|
||||||
|
STATUS_BYTES_LENGTH = 2
|
||||||
|
|
||||||
XTAL_CLK_DIVIDER = 2
|
XTAL_CLK_DIVIDER = 2
|
||||||
|
|
||||||
FLASH_SIZES = {
|
FLASH_SIZES = {
|
||||||
|
Reference in New Issue
Block a user