mirror of
https://github.com/espressif/esptool.git
synced 2025-10-14 02:43:11 +08:00
fix(espefuse): Fix ECDSA key purposes for ESP32-P4
This commit is contained in:

committed by
Radim Karniš

parent
1f9c5e92ee
commit
ae23ab254d
@@ -27,7 +27,7 @@ class EmulateEfuseController(EmulateEfuseControllerBase):
|
||||
""" esptool method start >>"""
|
||||
|
||||
def get_major_chip_version(self):
|
||||
return 0
|
||||
return 3
|
||||
|
||||
def get_minor_chip_version(self):
|
||||
return 0
|
||||
|
@@ -290,9 +290,50 @@ class EfuseField(base_fields.EfuseFieldBase):
|
||||
"t_sensor": EfuseTempSensor,
|
||||
"adc_tp": EfuseAdcPointCalibration,
|
||||
"wafer": EfuseWafer,
|
||||
"recovery_bootloader": EfuseBtldrRecoveryField,
|
||||
}.get(efuse.class_type, EfuseField)(parent, efuse)
|
||||
|
||||
|
||||
class EfuseBtldrRecoveryField(EfuseField):
|
||||
"""
|
||||
Handles composite recovery bootloader flash sector fields for ESP32-P4 ECO5 (v3.0).
|
||||
Combines/splits the following eFuse fields:
|
||||
- RECOVERY_BOOTLOADER_FLASH_SECTOR_0_1 (bits 1:0, uint:2)
|
||||
- RECOVERY_BOOTLOADER_FLASH_SECTOR_2_2 (bit 2, bool)
|
||||
- RECOVERY_BOOTLOADER_FLASH_SECTOR_3_6 (bits 6:3, uint:4)
|
||||
- RECOVERY_BOOTLOADER_FLASH_SECTOR_7_7 (bit 7, bool)
|
||||
- RECOVERY_BOOTLOADER_FLASH_SECTOR_8_10 (bits 10:8, uint:3)
|
||||
- RECOVERY_BOOTLOADER_FLASH_SECTOR_11_11(bit 11, bool)
|
||||
"""
|
||||
|
||||
FIELD_ORDER = [
|
||||
("RECOVERY_BOOTLOADER_FLASH_SECTOR_0_1", 0, 2),
|
||||
("RECOVERY_BOOTLOADER_FLASH_SECTOR_2_2", 2, 1),
|
||||
("RECOVERY_BOOTLOADER_FLASH_SECTOR_3_6", 3, 4),
|
||||
("RECOVERY_BOOTLOADER_FLASH_SECTOR_7_7", 7, 1),
|
||||
("RECOVERY_BOOTLOADER_FLASH_SECTOR_8_10", 8, 3),
|
||||
("RECOVERY_BOOTLOADER_FLASH_SECTOR_11_11", 11, 1),
|
||||
]
|
||||
|
||||
def get(self, from_read=True):
|
||||
value = 0
|
||||
for field_name, bit_offset, bit_len in self.FIELD_ORDER:
|
||||
field = self.parent[field_name]
|
||||
field_val = field.get(from_read)
|
||||
assert field.bit_len == bit_len
|
||||
value |= (field_val & ((1 << bit_len) - 1)) << bit_offset
|
||||
return value
|
||||
|
||||
def save(self, new_value):
|
||||
for field_name, bit_offset, bit_len in self.FIELD_ORDER:
|
||||
field = self.parent[field_name]
|
||||
field_val = (new_value >> bit_offset) & ((1 << bit_len) - 1)
|
||||
field.save(field_val)
|
||||
log.print(
|
||||
f"\t - '{field.name}' {field.get_bitstring()} -> {field.get_bitstring(from_read=False)}"
|
||||
)
|
||||
|
||||
|
||||
class EfuseWafer(EfuseField):
|
||||
def get(self, from_read=True):
|
||||
hi_bits = self.parent["WAFER_VERSION_MAJOR_HI"].get(from_read)
|
||||
@@ -389,10 +430,11 @@ class EfuseMacField(EfuseField):
|
||||
|
||||
# fmt: off
|
||||
class EfuseKeyPurposeField(EfuseField):
|
||||
key_purpose_len = 4 # bits for key purpose
|
||||
key_purpose_len = 5 # bits for key purpose
|
||||
KeyPurposeType = tuple[str, int, str | None, str | None, str]
|
||||
KEY_PURPOSES: list[KeyPurposeType] = [
|
||||
("USER", 0, None, None, "no_need_rd_protect"), # User purposes (software-only use)
|
||||
("ECDSA_KEY_P256", 1, None, "Reverse", "need_rd_protect"), # ECDSA key P256
|
||||
("ECDSA_KEY", 1, None, "Reverse", "need_rd_protect"), # ECDSA key
|
||||
("XTS_AES_256_KEY_1", 2, None, "Reverse", "need_rd_protect"), # XTS_AES_256_KEY_1 (flash/PSRAM encryption)
|
||||
("XTS_AES_256_KEY_2", 3, None, "Reverse", "need_rd_protect"), # XTS_AES_256_KEY_2 (flash/PSRAM encryption)
|
||||
@@ -406,6 +448,10 @@ class EfuseKeyPurposeField(EfuseField):
|
||||
("SECURE_BOOT_DIGEST2", 11, "DIGEST", None, "no_need_rd_protect"), # SECURE_BOOT_DIGEST2 (Secure Boot key digest)
|
||||
("KM_INIT_KEY", 12, None, None, "need_rd_protect"), # init key that is used for the generation of AES/ECDSA key
|
||||
("XTS_AES_256_KEY", -1, "VIRTUAL", None, "no_need_rd_protect"), # Virtual purpose splits to XTS_AES_256_KEY_1 and XTS_AES_256_KEY_2
|
||||
("ECDSA_KEY_P192", 16, None, "Reverse", "need_rd_protect"), # ECDSA key P192
|
||||
("ECDSA_KEY_P384_L", 17, None, "Reverse", "need_rd_protect"), # ECDSA key P384 low
|
||||
("ECDSA_KEY_P384_H", 18, None, "Reverse", "need_rd_protect"), # ECDSA key P384 high
|
||||
("ECDSA_KEY_P384", -3, "VIRTUAL", None, "need_rd_protect"), # Virtual purpose splits to ECDSA_KEY_P384_L and ECDSA_KEY_P384_H
|
||||
]
|
||||
CUSTOM_KEY_PURPOSES: list[KeyPurposeType] = []
|
||||
for id in range(0, 1 << key_purpose_len):
|
||||
@@ -445,9 +491,25 @@ class EfuseKeyPurposeField(EfuseField):
|
||||
return key[4] == "need_rd_protect"
|
||||
|
||||
def get(self, from_read=True):
|
||||
for p in self.KEY_PURPOSES:
|
||||
if p[1] == self.get_raw(from_read):
|
||||
return p[0]
|
||||
# Handle special case for KEY_PURPOSE_<digit>_H fields (e.g., KEY_PURPOSE_0_H ... KEY_PURPOSE_9_H)
|
||||
if self.name.startswith("KEY_PURPOSE_") and self.name.endswith("_H"):
|
||||
return self.get_raw(from_read)
|
||||
else:
|
||||
if any(
|
||||
efuse is not None
|
||||
and getattr(efuse, "name", None) == "KEY_PURPOSE_0_H"
|
||||
for efuse in self.parent
|
||||
): # check if the hi bit field for KEY_PURPOSE_.. exists
|
||||
hi_bits = self.parent[f"{self.name}_H"].get_raw(from_read)
|
||||
assert self.parent[f"{self.name}_H"].bit_len == 1
|
||||
lo_bits = self.parent[f"{self.name}"].get_raw(from_read)
|
||||
assert self.parent[f"{self.name}"].bit_len == 4
|
||||
raw_val = (hi_bits << 4) + lo_bits
|
||||
else:
|
||||
raw_val = self.get_raw(from_read)
|
||||
for p in self.KEY_PURPOSES:
|
||||
if p[1] == raw_val:
|
||||
return p[0]
|
||||
return "FORBIDDEN_STATE"
|
||||
|
||||
def get_name(self, raw_val):
|
||||
@@ -457,4 +519,31 @@ class EfuseKeyPurposeField(EfuseField):
|
||||
|
||||
def save(self, new_value):
|
||||
raw_val = int(self.check_format(str(new_value)))
|
||||
return super().save(raw_val)
|
||||
# Check if _H field exists (5-bit key purpose split into lo/hi)
|
||||
if (any(
|
||||
efuse is not None
|
||||
and getattr(efuse, "name", None) == "KEY_PURPOSE_0_H"
|
||||
for efuse in self.parent
|
||||
)
|
||||
and self.name.startswith("KEY_PURPOSE_")
|
||||
and not self.name.endswith("_H")
|
||||
):
|
||||
FIELD_ORDER = [
|
||||
(self.name, 0), # lo bits (bits 0-3)
|
||||
(f"{self.name}_H", 4), # hi bit (bit 4)
|
||||
]
|
||||
for field_name, bit_offset in FIELD_ORDER:
|
||||
field = self.parent[field_name]
|
||||
field_val = (raw_val >> bit_offset) & ((1 << field.bit_len) - 1)
|
||||
print(field_val, field_name)
|
||||
if field_val != 0:
|
||||
if field_name.endswith("_H"):
|
||||
field.save(field_val)
|
||||
else:
|
||||
super().save(field_val)
|
||||
log.print(
|
||||
f"\t - '{field.name}' {field.get_bitstring()} -> {field.get_bitstring(from_read=False)}"
|
||||
)
|
||||
else:
|
||||
# Single field, just save as usual
|
||||
super().save(raw_val)
|
||||
|
@@ -160,6 +160,21 @@ class EfuseDefineFields(EfuseFieldsBase):
|
||||
f.description = "calc WAFER VERSION MAJOR from (WAFER_VERSION_MAJOR_HI << 2) + WAFER_VERSION_MAJOR_LO (read only)"
|
||||
self.CALC.append(f)
|
||||
|
||||
if any(
|
||||
efuse is not None
|
||||
and getattr(efuse, "name", None) == "RECOVERY_BOOTLOADER_FLASH_SECTOR_0_1"
|
||||
for efuse in self.ALL_EFUSES
|
||||
):
|
||||
f = Field()
|
||||
f.name = "RECOVERY_BOOTLOADER_FLASH_SECTOR"
|
||||
f.block = 0
|
||||
f.bit_len = 12
|
||||
f.type = f"uint:{f.bit_len}"
|
||||
f.category = "config"
|
||||
f.class_type = "recovery_bootloader"
|
||||
f.description = "recovery_bootloader = RECOVERY_BOOTLOADER_FLASH_SECTOR_0_1 + 2_2 + 3_6 + 7_7 + 8_10 + 11_11"
|
||||
self.CALC.append(f)
|
||||
|
||||
for efuse in self.ALL_EFUSES:
|
||||
if efuse is not None:
|
||||
self.EFUSES.append(efuse)
|
||||
|
@@ -145,9 +145,12 @@ class EfuseTestCase:
|
||||
self.espefuse_py("burn-efuse CODING_SCHEME 3")
|
||||
|
||||
def _set_target_wafer_version(self):
|
||||
# ESP32 has to be ECO3 (v3.0) for tests
|
||||
if arg_chip == "esp32":
|
||||
# ESP32 has to be ECO3 (v3.0) for tests
|
||||
self.espefuse_py("burn-efuse CHIP_VER_REV1 1 CHIP_VER_REV2 1")
|
||||
if arg_chip == "esp32p4":
|
||||
# ESP32P4 has to be ECO5 (v3.0) for tests
|
||||
self.espefuse_py("burn-efuse WAFER_VERSION_MAJOR_LO 3")
|
||||
|
||||
def check_data_block_in_log(
|
||||
self, log, file_path, repeat=1, reverse_order=False, offset=0
|
||||
@@ -280,7 +283,7 @@ class TestReadCommands(EfuseTestCase):
|
||||
self.espefuse_py("burn-efuse BLK_VERSION_MAJOR 1")
|
||||
elif arg_chip in ["esp32c2", "esp32s2", "esp32c6"]:
|
||||
self.espefuse_py("burn-efuse BLK_VERSION_MINOR 1")
|
||||
elif arg_chip in ["esp32h2", "esp32p4"]:
|
||||
elif arg_chip in ["esp32h2"]:
|
||||
self.espefuse_py("burn-efuse BLK_VERSION_MINOR 2")
|
||||
self.espefuse_py("adc-info")
|
||||
|
||||
@@ -1388,15 +1391,10 @@ class TestBurnBlockDataCommands(EfuseTestCase):
|
||||
|
||||
self.espefuse_py(
|
||||
f"burn-block-data \
|
||||
BLOCK1 {IMAGES_DIR}/192bit \
|
||||
BLOCK5 {IMAGES_DIR}/256bit_1 \
|
||||
BLOCK6 {IMAGES_DIR}/256bit_2"
|
||||
)
|
||||
output = self.espefuse_py("-d summary")
|
||||
assert (
|
||||
"[1 ] read_regs: 00000000 07060500 00000908 00000000 13000000 00161514"
|
||||
in output
|
||||
)
|
||||
self.check_data_block_in_log(output, f"{IMAGES_DIR}/256bit")
|
||||
self.check_data_block_in_log(output, f"{IMAGES_DIR}/256bit_1")
|
||||
self.check_data_block_in_log(output, f"{IMAGES_DIR}/256bit_2")
|
||||
|
Reference in New Issue
Block a user