mirror of
https://github.com/espressif/esptool.git
synced 2025-10-19 11:43:48 +08:00
feat(esp32h4): add ESP32H4 esptool support
This commit is contained in:
@@ -119,6 +119,7 @@ host_tests_espefuse:
|
||||
- esp32s3
|
||||
- esp32s3beta2
|
||||
- esp32h21
|
||||
- esp32h4
|
||||
script:
|
||||
- coverage run --parallel-mode -m pytest ${CI_PROJECT_DIR}/test/test_espefuse.py --chip ${TARGET}
|
||||
# some .coverage files in sub-directories are not collected on some runners, move them first
|
||||
|
@@ -19,6 +19,7 @@ import espefuse.efuse.esp32c61 as esp32c61_efuse
|
||||
import espefuse.efuse.esp32h2 as esp32h2_efuse
|
||||
import espefuse.efuse.esp32h21 as esp32h21_efuse
|
||||
import espefuse.efuse.esp32h2beta1 as esp32h2beta1_efuse
|
||||
import espefuse.efuse.esp32h4 as esp32h4_efuse
|
||||
import espefuse.efuse.esp32p4 as esp32p4_efuse
|
||||
import espefuse.efuse.esp32s2 as esp32s2_efuse
|
||||
import espefuse.efuse.esp32s3 as esp32s3_efuse
|
||||
@@ -63,6 +64,7 @@ SUPPORTED_CHIPS = {
|
||||
),
|
||||
"esp32h2": DefChip("ESP32-H2", esp32h2_efuse, esptool.targets.ESP32H2ROM),
|
||||
"esp32h21": DefChip("ESP32-H21", esp32h21_efuse, esptool.targets.ESP32H21ROM),
|
||||
"esp32h4": DefChip("ESP32-H4", esp32h4_efuse, esptool.targets.ESP32H4ROM),
|
||||
"esp32p4": DefChip("ESP32-P4", esp32p4_efuse, esptool.targets.ESP32P4ROM),
|
||||
"esp32h2beta1": DefChip(
|
||||
"ESP32-H2(beta1)", esp32h2beta1_efuse, esptool.targets.ESP32H2BETA1ROM
|
||||
|
3
espefuse/efuse/esp32h4/__init__.py
Normal file
3
espefuse/efuse/esp32h4/__init__.py
Normal file
@@ -0,0 +1,3 @@
|
||||
from . import operations
|
||||
from .emulate_efuse_controller import EmulateEfuseController
|
||||
from .fields import EspEfuses
|
92
espefuse/efuse/esp32h4/emulate_efuse_controller.py
Normal file
92
espefuse/efuse/esp32h4/emulate_efuse_controller.py
Normal file
@@ -0,0 +1,92 @@
|
||||
# This file describes eFuses controller for ESP32-H4 chip
|
||||
#
|
||||
# SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
import reedsolo
|
||||
|
||||
from .mem_definition import EfuseDefineBlocks, EfuseDefineFields, EfuseDefineRegisters
|
||||
from ..emulate_efuse_controller_base import EmulateEfuseControllerBase, FatalError
|
||||
|
||||
|
||||
class EmulateEfuseController(EmulateEfuseControllerBase):
|
||||
"""The class for virtual efuse operation. Using for HOST_TEST."""
|
||||
|
||||
CHIP_NAME = "ESP32-H4"
|
||||
mem = None
|
||||
debug = False
|
||||
|
||||
def __init__(self, efuse_file=None, debug=False):
|
||||
self.Blocks = EfuseDefineBlocks
|
||||
self.Fields = EfuseDefineFields(None)
|
||||
self.REGS = EfuseDefineRegisters
|
||||
super(EmulateEfuseController, self).__init__(efuse_file, debug)
|
||||
self.write_reg(self.REGS.EFUSE_CMD_REG, 0)
|
||||
|
||||
""" esptool method start >>"""
|
||||
|
||||
def get_major_chip_version(self):
|
||||
return 0
|
||||
|
||||
def get_minor_chip_version(self):
|
||||
return 0
|
||||
|
||||
def get_crystal_freq(self):
|
||||
return 32 # MHz
|
||||
|
||||
def get_security_info(self):
|
||||
return {
|
||||
"flags": 0,
|
||||
"flash_crypt_cnt": 0,
|
||||
"key_purposes": 0,
|
||||
"chip_id": 0,
|
||||
"api_version": 0,
|
||||
}
|
||||
|
||||
""" << esptool method end """
|
||||
|
||||
def handle_writing_event(self, addr, value):
|
||||
if addr == self.REGS.EFUSE_CMD_REG:
|
||||
if value & self.REGS.EFUSE_PGM_CMD:
|
||||
self.copy_blocks_wr_regs_to_rd_regs(updated_block=(value >> 2) & 0xF)
|
||||
self.clean_blocks_wr_regs()
|
||||
self.check_rd_protection_area()
|
||||
self.write_reg(addr, 0)
|
||||
self.write_reg(self.REGS.EFUSE_CMD_REG, 0)
|
||||
elif value == self.REGS.EFUSE_READ_CMD:
|
||||
self.write_reg(addr, 0)
|
||||
self.write_reg(self.REGS.EFUSE_CMD_REG, 0)
|
||||
self.save_to_file()
|
||||
|
||||
def get_bitlen_of_block(self, blk, wr=False):
|
||||
if blk.id == 0:
|
||||
if wr:
|
||||
return 32 * 8
|
||||
else:
|
||||
return 32 * blk.len
|
||||
else:
|
||||
if wr:
|
||||
rs_coding = 32 * 3
|
||||
return 32 * 8 + rs_coding
|
||||
else:
|
||||
return 32 * blk.len
|
||||
|
||||
def handle_coding_scheme(self, blk, data):
|
||||
if blk.id != 0:
|
||||
# CODING_SCHEME RS applied only for all blocks except BLK0.
|
||||
coded_bytes = 12
|
||||
data.pos = coded_bytes * 8
|
||||
plain_data = data.readlist("32*uint:8")[::-1]
|
||||
# takes 32 bytes
|
||||
# apply RS encoding
|
||||
rs = reedsolo.RSCodec(coded_bytes)
|
||||
# 32 byte of data + 12 bytes RS
|
||||
calc_encoded_data = list(rs.encode([x for x in plain_data]))
|
||||
data.pos = 0
|
||||
if calc_encoded_data != data.readlist("44*uint:8")[::-1]:
|
||||
raise FatalError("Error in coding scheme data")
|
||||
data = data[coded_bytes * 8 :]
|
||||
if blk.len < 8:
|
||||
data = data[(8 - blk.len) * 32 :]
|
||||
return data
|
452
espefuse/efuse/esp32h4/fields.py
Normal file
452
espefuse/efuse/esp32h4/fields.py
Normal file
@@ -0,0 +1,452 @@
|
||||
# This file describes eFuses for ESP32-H4 chip
|
||||
#
|
||||
# SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
import binascii
|
||||
import struct
|
||||
import time
|
||||
|
||||
from bitstring import BitArray
|
||||
|
||||
import esptool
|
||||
|
||||
import reedsolo
|
||||
|
||||
from .mem_definition import EfuseDefineBlocks, EfuseDefineFields, EfuseDefineRegisters
|
||||
from .. import base_fields
|
||||
from .. import util
|
||||
|
||||
|
||||
class EfuseBlock(base_fields.EfuseBlockBase):
|
||||
def len_of_burn_unit(self):
|
||||
# The writing register window is 8 registers for any blocks.
|
||||
# len in bytes
|
||||
return 8 * 4
|
||||
|
||||
def __init__(self, parent, param, skip_read=False):
|
||||
parent.read_coding_scheme()
|
||||
super(EfuseBlock, self).__init__(parent, param, skip_read=skip_read)
|
||||
|
||||
def apply_coding_scheme(self):
|
||||
data = self.get_raw(from_read=False)[::-1]
|
||||
if len(data) < self.len_of_burn_unit():
|
||||
add_empty_bytes = self.len_of_burn_unit() - len(data)
|
||||
data = data + (b"\x00" * add_empty_bytes)
|
||||
if self.get_coding_scheme() == self.parent.REGS.CODING_SCHEME_RS:
|
||||
# takes 32 bytes
|
||||
# apply RS encoding
|
||||
rs = reedsolo.RSCodec(12)
|
||||
# 32 byte of data + 12 bytes RS
|
||||
encoded_data = rs.encode([x for x in data])
|
||||
words = struct.unpack("<" + "I" * 11, encoded_data)
|
||||
# returns 11 words (8 words of data + 3 words of RS coding)
|
||||
else:
|
||||
# takes 32 bytes
|
||||
words = struct.unpack("<" + ("I" * (len(data) // 4)), data)
|
||||
# returns 8 words
|
||||
return words
|
||||
|
||||
|
||||
class EspEfuses(base_fields.EspEfusesBase):
|
||||
"""
|
||||
Wrapper object to manage the efuse fields in a connected ESP bootloader
|
||||
"""
|
||||
|
||||
debug = False
|
||||
do_not_confirm = False
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
esp,
|
||||
skip_connect=False,
|
||||
debug=False,
|
||||
do_not_confirm=False,
|
||||
extend_efuse_table=None,
|
||||
):
|
||||
self.Blocks = EfuseDefineBlocks()
|
||||
self.Fields = EfuseDefineFields(extend_efuse_table)
|
||||
self.REGS = EfuseDefineRegisters
|
||||
self.BURN_BLOCK_DATA_NAMES = self.Blocks.get_burn_block_data_names()
|
||||
self.BLOCKS_FOR_KEYS = self.Blocks.get_blocks_for_keys()
|
||||
self._esp = esp
|
||||
self.debug = debug
|
||||
self.do_not_confirm = do_not_confirm
|
||||
if esp.CHIP_NAME != "ESP32-H4":
|
||||
raise esptool.FatalError(
|
||||
"Expected the 'esp' param for ESP32-H4 chip but got for '%s'."
|
||||
% (esp.CHIP_NAME)
|
||||
)
|
||||
if not skip_connect:
|
||||
flags = self._esp.get_security_info()["flags"]
|
||||
GET_SECURITY_INFO_FLAG_SECURE_DOWNLOAD_ENABLE = 1 << 2
|
||||
if flags & GET_SECURITY_INFO_FLAG_SECURE_DOWNLOAD_ENABLE:
|
||||
raise esptool.FatalError(
|
||||
"Secure Download Mode is enabled. The tool can not read eFuses."
|
||||
)
|
||||
self.blocks = [
|
||||
EfuseBlock(self, self.Blocks.get(block), skip_read=skip_connect)
|
||||
for block in self.Blocks.BLOCKS
|
||||
]
|
||||
if not skip_connect:
|
||||
self.get_coding_scheme_warnings()
|
||||
self.efuses = [EfuseField.convert(self, efuse) for efuse in self.Fields.EFUSES]
|
||||
self.efuses += [
|
||||
EfuseField.convert(self, efuse) for efuse in self.Fields.KEYBLOCKS
|
||||
]
|
||||
if skip_connect:
|
||||
self.efuses += [
|
||||
EfuseField.convert(self, efuse)
|
||||
for efuse in self.Fields.BLOCK2_CALIBRATION_EFUSES
|
||||
]
|
||||
else:
|
||||
if self["BLK_VERSION_MINOR"].get() == 1:
|
||||
self.efuses += [
|
||||
EfuseField.convert(self, efuse)
|
||||
for efuse in self.Fields.BLOCK2_CALIBRATION_EFUSES
|
||||
]
|
||||
self.efuses += [
|
||||
EfuseField.convert(self, efuse) for efuse in self.Fields.CALC
|
||||
]
|
||||
|
||||
def __getitem__(self, efuse_name):
|
||||
"""Return the efuse field with the given name"""
|
||||
for e in self.efuses:
|
||||
if efuse_name == e.name or any(x == efuse_name for x in e.alt_names):
|
||||
return e
|
||||
new_fields = False
|
||||
for efuse in self.Fields.BLOCK2_CALIBRATION_EFUSES:
|
||||
if efuse.name == efuse_name or any(
|
||||
x == efuse_name for x in efuse.alt_names
|
||||
):
|
||||
self.efuses += [
|
||||
EfuseField.convert(self, efuse)
|
||||
for efuse in self.Fields.BLOCK2_CALIBRATION_EFUSES
|
||||
]
|
||||
new_fields = True
|
||||
if new_fields:
|
||||
for e in self.efuses:
|
||||
if efuse_name == e.name or any(x == efuse_name for x in e.alt_names):
|
||||
return e
|
||||
raise KeyError
|
||||
|
||||
def read_coding_scheme(self):
|
||||
self.coding_scheme = self.REGS.CODING_SCHEME_RS
|
||||
|
||||
def print_status_regs(self):
|
||||
print("")
|
||||
self.blocks[0].print_block(self.blocks[0].err_bitarray, "err__regs", debug=True)
|
||||
print(
|
||||
"{:27} 0x{:08x}".format(
|
||||
"EFUSE_RD_RS_ERR0_REG", self.read_reg(self.REGS.EFUSE_RD_RS_ERR0_REG)
|
||||
)
|
||||
)
|
||||
print(
|
||||
"{:27} 0x{:08x}".format(
|
||||
"EFUSE_RD_RS_ERR1_REG", self.read_reg(self.REGS.EFUSE_RD_RS_ERR1_REG)
|
||||
)
|
||||
)
|
||||
|
||||
def efuse_controller_setup(self):
|
||||
self.set_efuse_timing()
|
||||
self.clear_pgm_registers()
|
||||
self.wait_efuse_idle()
|
||||
|
||||
def write_efuses(self, block):
|
||||
self.efuse_program(block)
|
||||
return self.get_coding_scheme_warnings(silent=True)
|
||||
|
||||
def clear_pgm_registers(self):
|
||||
self.wait_efuse_idle()
|
||||
for r in range(
|
||||
self.REGS.EFUSE_PGM_DATA0_REG, self.REGS.EFUSE_PGM_DATA0_REG + 32, 4
|
||||
):
|
||||
self.write_reg(r, 0)
|
||||
|
||||
def wait_efuse_idle(self):
|
||||
deadline = time.time() + self.REGS.EFUSE_BURN_TIMEOUT
|
||||
while time.time() < deadline:
|
||||
cmds = self.REGS.EFUSE_PGM_CMD | self.REGS.EFUSE_READ_CMD
|
||||
if self.read_reg(self.REGS.EFUSE_CMD_REG) & cmds == 0:
|
||||
if self.read_reg(self.REGS.EFUSE_CMD_REG) & cmds == 0:
|
||||
# Due to a hardware error, we have to read READ_CMD again
|
||||
# to make sure the efuse clock is normal.
|
||||
# For PGM_CMD it is not necessary.
|
||||
return
|
||||
raise esptool.FatalError(
|
||||
"Timed out waiting for Efuse controller command to complete"
|
||||
)
|
||||
|
||||
def efuse_program(self, block):
|
||||
self.wait_efuse_idle()
|
||||
self.write_reg(self.REGS.EFUSE_CONF_REG, self.REGS.EFUSE_WRITE_OP_CODE)
|
||||
self.write_reg(self.REGS.EFUSE_CMD_REG, self.REGS.EFUSE_PGM_CMD | (block << 2))
|
||||
self.wait_efuse_idle()
|
||||
self.clear_pgm_registers()
|
||||
self.efuse_read()
|
||||
|
||||
def efuse_read(self):
|
||||
self.wait_efuse_idle()
|
||||
self.write_reg(self.REGS.EFUSE_CONF_REG, self.REGS.EFUSE_READ_OP_CODE)
|
||||
# need to add a delay after triggering EFUSE_READ_CMD, as ROM loader checks some
|
||||
# efuse registers after each command is completed
|
||||
# if ENABLE_SECURITY_DOWNLOAD or DIS_DOWNLOAD_MODE is enabled by the current cmd, then we need to try to reconnect to the chip.
|
||||
try:
|
||||
self.write_reg(
|
||||
self.REGS.EFUSE_CMD_REG, self.REGS.EFUSE_READ_CMD, delay_after_us=1000
|
||||
)
|
||||
self.wait_efuse_idle()
|
||||
except esptool.FatalError:
|
||||
secure_download_mode_before = self._esp.secure_download_mode
|
||||
|
||||
try:
|
||||
self._esp = self.reconnect_chip(self._esp)
|
||||
except esptool.FatalError:
|
||||
print("Can not re-connect to the chip")
|
||||
if not self["DIS_DOWNLOAD_MODE"].get() and self[
|
||||
"DIS_DOWNLOAD_MODE"
|
||||
].get(from_read=False):
|
||||
print(
|
||||
"This is the correct behavior as we are actually burning "
|
||||
"DIS_DOWNLOAD_MODE which disables the connection to the chip"
|
||||
)
|
||||
print("DIS_DOWNLOAD_MODE is enabled")
|
||||
print("Successful")
|
||||
exit(0) # finish without errors
|
||||
raise
|
||||
|
||||
print("Established a connection with the chip")
|
||||
if self._esp.secure_download_mode and not secure_download_mode_before:
|
||||
print("Secure download mode is enabled")
|
||||
if not self["ENABLE_SECURITY_DOWNLOAD"].get() and self[
|
||||
"ENABLE_SECURITY_DOWNLOAD"
|
||||
].get(from_read=False):
|
||||
print(
|
||||
"espefuse tool can not continue to work in Secure download mode"
|
||||
)
|
||||
print("ENABLE_SECURITY_DOWNLOAD is enabled")
|
||||
print("Successful")
|
||||
exit(0) # finish without errors
|
||||
raise
|
||||
|
||||
def set_efuse_timing(self):
|
||||
"""Set timing registers for burning efuses"""
|
||||
# Configure clock
|
||||
apb_freq = self.get_crystal_freq()
|
||||
if apb_freq != 32:
|
||||
raise esptool.FatalError(
|
||||
"The eFuse supports only xtal=32M (xtal was %d)" % apb_freq
|
||||
)
|
||||
|
||||
# TODO: [ESP32H4] IDF-12268
|
||||
|
||||
def get_coding_scheme_warnings(self, silent=False):
|
||||
"""Check if the coding scheme has detected any errors."""
|
||||
old_addr_reg = 0
|
||||
reg_value = 0
|
||||
ret_fail = False
|
||||
for block in self.blocks:
|
||||
if block.id == 0:
|
||||
words = [
|
||||
self.read_reg(self.REGS.EFUSE_RD_REPEAT_ERR0_REG + offs * 4)
|
||||
for offs in range(5)
|
||||
]
|
||||
block.err_bitarray.pos = 0
|
||||
for word in reversed(words):
|
||||
block.err_bitarray.overwrite(BitArray("uint:32=%d" % word))
|
||||
block.num_errors = block.err_bitarray.count(True)
|
||||
block.fail = block.num_errors != 0
|
||||
else:
|
||||
addr_reg, err_num_mask, err_num_offs, fail_bit = self.REGS.BLOCK_ERRORS[
|
||||
block.id
|
||||
]
|
||||
if err_num_mask is None or err_num_offs is None or fail_bit is None:
|
||||
continue
|
||||
if addr_reg != old_addr_reg:
|
||||
old_addr_reg = addr_reg
|
||||
reg_value = self.read_reg(addr_reg)
|
||||
block.fail = reg_value & (1 << fail_bit) != 0
|
||||
block.num_errors = (reg_value >> err_num_offs) & err_num_mask
|
||||
ret_fail |= block.fail
|
||||
if not silent and (block.fail or block.num_errors):
|
||||
print(
|
||||
"Error(s) in BLOCK%d [ERRORS:%d FAIL:%d]"
|
||||
% (block.id, block.num_errors, block.fail)
|
||||
)
|
||||
if (self.debug or ret_fail) and not silent:
|
||||
self.print_status_regs()
|
||||
return ret_fail
|
||||
|
||||
def summary(self):
|
||||
# TODO add support set_flash_voltage - "Flash voltage (VDD_SPI)"
|
||||
return ""
|
||||
|
||||
|
||||
class EfuseField(base_fields.EfuseFieldBase):
|
||||
@staticmethod
|
||||
def convert(parent, efuse):
|
||||
return {
|
||||
"mac": EfuseMacField,
|
||||
"keypurpose": EfuseKeyPurposeField,
|
||||
"t_sensor": EfuseTempSensor,
|
||||
"adc_tp": EfuseAdcPointCalibration,
|
||||
"wafer": EfuseWafer,
|
||||
}.get(efuse.class_type, EfuseField)(parent, efuse)
|
||||
|
||||
|
||||
class EfuseWafer(EfuseField):
|
||||
def get(self, from_read=True):
|
||||
# TODO: [ESP32H4] IDF-12268
|
||||
return 0
|
||||
|
||||
def save(self, new_value):
|
||||
raise esptool.FatalError("Burning %s is not supported" % self.name)
|
||||
|
||||
|
||||
class EfuseTempSensor(EfuseField):
|
||||
def get(self, from_read=True):
|
||||
value = self.get_bitstring(from_read)
|
||||
sig = -1 if value[0] else 1
|
||||
return sig * value[1:].uint * 0.1
|
||||
|
||||
|
||||
class EfuseAdcPointCalibration(EfuseField):
|
||||
def get(self, from_read=True):
|
||||
STEP_SIZE = 4
|
||||
value = self.get_bitstring(from_read)
|
||||
sig = -1 if value[0] else 1
|
||||
return sig * value[1:].uint * STEP_SIZE
|
||||
|
||||
|
||||
class EfuseMacField(EfuseField):
|
||||
def check_format(self, new_value_str):
|
||||
if new_value_str is None:
|
||||
raise esptool.FatalError(
|
||||
"Required MAC Address in AA:CD:EF:01:02:03 format!"
|
||||
)
|
||||
num_bytes = 8 if self.name == "MAC_EUI64" else 6
|
||||
if new_value_str.count(":") != num_bytes - 1:
|
||||
raise esptool.FatalError(
|
||||
f"MAC Address needs to be a {num_bytes}-byte hexadecimal format "
|
||||
"separated by colons (:)!"
|
||||
)
|
||||
hexad = new_value_str.replace(":", "").split(" ", 1)[0]
|
||||
hexad = hexad.split(" ", 1)[0] if self.is_field_calculated() else hexad
|
||||
if len(hexad) != num_bytes * 2:
|
||||
raise esptool.FatalError(
|
||||
f"MAC Address needs to be a {num_bytes}-byte hexadecimal number "
|
||||
f"({num_bytes * 2} hexadecimal characters)!"
|
||||
)
|
||||
# order of bytearray = b'\xaa\xcd\xef\x01\x02\x03',
|
||||
bindata = binascii.unhexlify(hexad)
|
||||
|
||||
if not self.is_field_calculated():
|
||||
# unicast address check according to
|
||||
# https://tools.ietf.org/html/rfc7042#section-2.1
|
||||
if esptool.util.byte(bindata, 0) & 0x01:
|
||||
raise esptool.FatalError("Custom MAC must be a unicast MAC!")
|
||||
return bindata
|
||||
|
||||
def check(self):
|
||||
errs, fail = self.parent.get_block_errors(self.block)
|
||||
if errs != 0 or fail:
|
||||
output = "Block%d has ERRORS:%d FAIL:%d" % (self.block, errs, fail)
|
||||
else:
|
||||
output = "OK"
|
||||
return "(" + output + ")"
|
||||
|
||||
def get(self, from_read=True):
|
||||
if self.name == "CUSTOM_MAC":
|
||||
mac = self.get_raw(from_read)[::-1]
|
||||
elif self.name == "MAC":
|
||||
mac = self.get_raw(from_read)
|
||||
elif self.name == "MAC_EUI64":
|
||||
mac = self.parent["MAC"].get_bitstring(from_read).copy()
|
||||
mac_ext = self.parent["MAC_EXT"].get_bitstring(from_read)
|
||||
mac.insert(mac_ext, 24)
|
||||
mac = mac.bytes
|
||||
else:
|
||||
mac = self.get_raw(from_read)
|
||||
return "%s %s" % (util.hexify(mac, ":"), self.check())
|
||||
|
||||
def save(self, new_value):
|
||||
def print_field(e, new_value):
|
||||
print(
|
||||
" - '{}' ({}) {} -> {}".format(
|
||||
e.name, e.description, e.get_bitstring(), new_value
|
||||
)
|
||||
)
|
||||
|
||||
if self.name == "CUSTOM_MAC":
|
||||
bitarray_mac = self.convert_to_bitstring(new_value)
|
||||
print_field(self, bitarray_mac)
|
||||
super(EfuseMacField, self).save(new_value)
|
||||
else:
|
||||
# Writing the BLOCK1 (MAC_SPI_8M_0) default MAC is not possible,
|
||||
# as it's written in the factory.
|
||||
raise esptool.FatalError(f"Burning {self.name} is not supported")
|
||||
|
||||
|
||||
# fmt: off
|
||||
class EfuseKeyPurposeField(EfuseField):
|
||||
# TODO: [ESP32H4] IDF-12268 need check
|
||||
KEY_PURPOSES = [
|
||||
("USER", 0, None, None, "no_need_rd_protect"), # User purposes (software-only use)
|
||||
("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)
|
||||
("XTS_AES_128_KEY", 4, None, "Reverse", "need_rd_protect"), # XTS_AES_128_KEY (flash/PSRAM encryption)
|
||||
("HMAC_DOWN_ALL", 5, None, None, "need_rd_protect"), # HMAC Downstream mode
|
||||
("HMAC_DOWN_JTAG", 6, None, None, "need_rd_protect"), # JTAG soft enable key (uses HMAC Downstream mode)
|
||||
("HMAC_DOWN_DIGITAL_SIGNATURE", 7, None, None, "need_rd_protect"), # Digital Signature peripheral key (uses HMAC Downstream mode)
|
||||
("HMAC_UP", 8, None, None, "need_rd_protect"), # HMAC Upstream mode
|
||||
("SECURE_BOOT_DIGEST0", 9, "DIGEST", None, "no_need_rd_protect"), # SECURE_BOOT_DIGEST0 (Secure Boot key digest)
|
||||
("SECURE_BOOT_DIGEST1", 10, "DIGEST", None, "no_need_rd_protect"), # SECURE_BOOT_DIGEST1 (Secure Boot key digest)
|
||||
("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
|
||||
]
|
||||
# fmt: on
|
||||
|
||||
KEY_PURPOSES_NAME = [name[0] for name in KEY_PURPOSES]
|
||||
DIGEST_KEY_PURPOSES = [name[0] for name in KEY_PURPOSES if name[2] == "DIGEST"]
|
||||
|
||||
def check_format(self, new_value_str):
|
||||
# str convert to int: "XTS_AES_128_KEY" - > str(4)
|
||||
# if int: 4 -> str(4)
|
||||
raw_val = new_value_str
|
||||
for purpose_name in self.KEY_PURPOSES:
|
||||
if purpose_name[0] == new_value_str:
|
||||
raw_val = str(purpose_name[1])
|
||||
break
|
||||
if raw_val.isdigit():
|
||||
if int(raw_val) not in [p[1] for p in self.KEY_PURPOSES if p[1] > 0]:
|
||||
raise esptool.FatalError("'%s' can not be set (value out of range)" % raw_val)
|
||||
else:
|
||||
raise esptool.FatalError("'%s' unknown name" % raw_val)
|
||||
return raw_val
|
||||
|
||||
def need_reverse(self, new_key_purpose):
|
||||
for key in self.KEY_PURPOSES:
|
||||
if key[0] == new_key_purpose:
|
||||
return key[3] == "Reverse"
|
||||
|
||||
def need_rd_protect(self, new_key_purpose):
|
||||
for key in self.KEY_PURPOSES:
|
||||
if key[0] == new_key_purpose:
|
||||
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]
|
||||
return "FORBIDDEN_STATE"
|
||||
|
||||
def get_name(self, raw_val):
|
||||
for key in self.KEY_PURPOSES:
|
||||
if key[1] == raw_val:
|
||||
return key[0]
|
||||
|
||||
def save(self, new_value):
|
||||
raw_val = int(self.check_format(str(new_value)))
|
||||
return super(EfuseKeyPurposeField, self).save(raw_val)
|
169
espefuse/efuse/esp32h4/mem_definition.py
Normal file
169
espefuse/efuse/esp32h4/mem_definition.py
Normal file
@@ -0,0 +1,169 @@
|
||||
# This file describes eFuses fields and registers for ESP32-H4 chip
|
||||
#
|
||||
# SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
import os
|
||||
|
||||
import yaml
|
||||
|
||||
from ..mem_definition_base import (
|
||||
EfuseBlocksBase,
|
||||
EfuseFieldsBase,
|
||||
EfuseRegistersBase,
|
||||
Field,
|
||||
)
|
||||
|
||||
|
||||
class EfuseDefineRegisters(EfuseRegistersBase):
|
||||
EFUSE_MEM_SIZE = 0x01FC + 4
|
||||
|
||||
# EFUSE registers & command/conf values
|
||||
DR_REG_EFUSE_BASE = 0x600B1800
|
||||
EFUSE_PGM_DATA0_REG = DR_REG_EFUSE_BASE
|
||||
EFUSE_CHECK_VALUE0_REG = DR_REG_EFUSE_BASE + 0x020
|
||||
EFUSE_CLK_REG = DR_REG_EFUSE_BASE + 0x1C8
|
||||
EFUSE_CONF_REG = DR_REG_EFUSE_BASE + 0x1CC
|
||||
EFUSE_STATUS_REG = DR_REG_EFUSE_BASE + 0x1D4
|
||||
EFUSE_CMD_REG = DR_REG_EFUSE_BASE + 0x1D8
|
||||
EFUSE_RD_RS_ERR0_REG = DR_REG_EFUSE_BASE + 0x190
|
||||
EFUSE_RD_RS_ERR1_REG = DR_REG_EFUSE_BASE + 0x194
|
||||
EFUSE_RD_REPEAT_ERR0_REG = DR_REG_EFUSE_BASE + 0x17C
|
||||
EFUSE_RD_REPEAT_ERR1_REG = DR_REG_EFUSE_BASE + 0x180
|
||||
EFUSE_RD_REPEAT_ERR2_REG = DR_REG_EFUSE_BASE + 0x184
|
||||
EFUSE_RD_REPEAT_ERR3_REG = DR_REG_EFUSE_BASE + 0x188
|
||||
EFUSE_RD_REPEAT_ERR4_REG = DR_REG_EFUSE_BASE + 0x18C
|
||||
EFUSE_DAC_CONF_REG = DR_REG_EFUSE_BASE + 0x1EC
|
||||
EFUSE_RD_TIM_CONF_REG = DR_REG_EFUSE_BASE + 0x1F0
|
||||
EFUSE_WR_TIM_CONF1_REG = DR_REG_EFUSE_BASE + 0x1F4
|
||||
EFUSE_WR_TIM_CONF2_REG = DR_REG_EFUSE_BASE + 0x1F8
|
||||
EFUSE_DATE_REG = DR_REG_EFUSE_BASE + 0x198
|
||||
EFUSE_WRITE_OP_CODE = 0x5A5A
|
||||
EFUSE_READ_OP_CODE = 0x5AA5
|
||||
EFUSE_PGM_CMD_MASK = 0x3
|
||||
EFUSE_PGM_CMD = 0x2
|
||||
EFUSE_READ_CMD = 0x1
|
||||
|
||||
BLOCK_ERRORS = [
|
||||
# error_reg, err_num_mask, err_num_offs, fail_bit
|
||||
(EFUSE_RD_REPEAT_ERR0_REG, None, None, None), # BLOCK0
|
||||
(EFUSE_RD_RS_ERR0_REG, 0x7, 0, 3), # MAC_SPI_8M_0
|
||||
(EFUSE_RD_RS_ERR0_REG, 0x7, 4, 7), # BLOCK_SYS_DATA
|
||||
(EFUSE_RD_RS_ERR0_REG, 0x7, 8, 11), # BLOCK_USR_DATA
|
||||
(EFUSE_RD_RS_ERR0_REG, 0x7, 12, 15), # BLOCK_KEY0
|
||||
(EFUSE_RD_RS_ERR0_REG, 0x7, 16, 19), # BLOCK_KEY1
|
||||
(EFUSE_RD_RS_ERR0_REG, 0x7, 20, 23), # BLOCK_KEY2
|
||||
(EFUSE_RD_RS_ERR0_REG, 0x7, 24, 27), # BLOCK_KEY3
|
||||
(EFUSE_RD_RS_ERR0_REG, 0x7, 28, 31), # BLOCK_KEY4
|
||||
(EFUSE_RD_RS_ERR1_REG, 0x7, 0, 3), # BLOCK_KEY5
|
||||
(EFUSE_RD_RS_ERR1_REG, 0x7, 4, 7), # BLOCK_SYS_DATA2
|
||||
]
|
||||
|
||||
# EFUSE_WR_TIM_CONF2_REG
|
||||
EFUSE_PWR_OFF_NUM_S = 0
|
||||
EFUSE_PWR_OFF_NUM_M = 0xFFFF << EFUSE_PWR_OFF_NUM_S
|
||||
|
||||
# EFUSE_WR_TIM_CONF1_REG
|
||||
EFUSE_PWR_ON_NUM_S = 8
|
||||
EFUSE_PWR_ON_NUM_M = 0x0000FFFF << EFUSE_PWR_ON_NUM_S
|
||||
|
||||
# EFUSE_DAC_CONF_REG
|
||||
EFUSE_DAC_CLK_DIV_S = 0
|
||||
EFUSE_DAC_CLK_DIV_M = 0xFF << EFUSE_DAC_CLK_DIV_S
|
||||
|
||||
# EFUSE_DAC_CONF_REG
|
||||
EFUSE_DAC_NUM_S = 9
|
||||
EFUSE_DAC_NUM_M = 0xFF << EFUSE_DAC_NUM_S
|
||||
|
||||
|
||||
class EfuseDefineBlocks(EfuseBlocksBase):
|
||||
__base_rd_regs = EfuseDefineRegisters.DR_REG_EFUSE_BASE
|
||||
__base_wr_regs = EfuseDefineRegisters.EFUSE_PGM_DATA0_REG
|
||||
# List of efuse blocks
|
||||
# fmt: off
|
||||
BLOCKS = [
|
||||
# Name, Alias, Index, Read address, Write address, Write protect bit, Read protect bit, Len, key_purpose
|
||||
("BLOCK0", [], 0, __base_rd_regs + 0x02C, __base_wr_regs, None, None, 6, None),
|
||||
("MAC_SPI_8M_0", ["BLOCK1"], 1, __base_rd_regs + 0x044, __base_wr_regs, 20, None, 6, None),
|
||||
("BLOCK_SYS_DATA", ["BLOCK2"], 2, __base_rd_regs + 0x05C, __base_wr_regs, 21, None, 8, None),
|
||||
("BLOCK_USR_DATA", ["BLOCK3"], 3, __base_rd_regs + 0x07C, __base_wr_regs, 22, None, 8, None),
|
||||
("BLOCK_KEY0", ["BLOCK4"], 4, __base_rd_regs + 0x09C, __base_wr_regs, 23, 0, 8, "KEY_PURPOSE_0"),
|
||||
("BLOCK_KEY1", ["BLOCK5"], 5, __base_rd_regs + 0x0BC, __base_wr_regs, 24, 1, 8, "KEY_PURPOSE_1"),
|
||||
("BLOCK_KEY2", ["BLOCK6"], 6, __base_rd_regs + 0x0DC, __base_wr_regs, 25, 2, 8, "KEY_PURPOSE_2"),
|
||||
("BLOCK_KEY3", ["BLOCK7"], 7, __base_rd_regs + 0x0FC, __base_wr_regs, 26, 3, 8, "KEY_PURPOSE_3"),
|
||||
("BLOCK_KEY4", ["BLOCK8"], 8, __base_rd_regs + 0x11C, __base_wr_regs, 27, 4, 8, "KEY_PURPOSE_4"),
|
||||
("BLOCK_KEY5", ["BLOCK9"], 9, __base_rd_regs + 0x13C, __base_wr_regs, 28, 5, 8, "KEY_PURPOSE_5"),
|
||||
("BLOCK_SYS_DATA2", ["BLOCK10"], 10, __base_rd_regs + 0x15C, __base_wr_regs, 29, 6, 8, None),
|
||||
]
|
||||
# fmt: on
|
||||
|
||||
def get_burn_block_data_names(self):
|
||||
list_of_names = []
|
||||
for block in self.BLOCKS:
|
||||
blk = self.get(block)
|
||||
if blk.name:
|
||||
list_of_names.append(blk.name)
|
||||
if blk.alias:
|
||||
for alias in blk.alias:
|
||||
list_of_names.append(alias)
|
||||
return list_of_names
|
||||
|
||||
|
||||
class EfuseDefineFields(EfuseFieldsBase):
|
||||
def __init__(self, extend_efuse_table) -> None:
|
||||
# List of efuse fields from TRM the chapter eFuse Controller.
|
||||
self.EFUSES = []
|
||||
|
||||
self.KEYBLOCKS = []
|
||||
|
||||
# if BLK_VERSION_MINOR is 1, these efuse fields are in BLOCK2
|
||||
self.BLOCK2_CALIBRATION_EFUSES = []
|
||||
|
||||
self.CALC = []
|
||||
|
||||
dir_name = os.path.dirname(os.path.abspath(__file__))
|
||||
dir_name, file_name = os.path.split(dir_name)
|
||||
file_name = file_name + ".yaml"
|
||||
dir_name, _ = os.path.split(dir_name)
|
||||
efuse_file = os.path.join(dir_name, "efuse_defs", file_name)
|
||||
with open(f"{efuse_file}", "r") as r_file:
|
||||
e_desc = yaml.safe_load(r_file)
|
||||
super().__init__(e_desc, extend_efuse_table)
|
||||
|
||||
for i, efuse in enumerate(self.ALL_EFUSES):
|
||||
if efuse.name in [
|
||||
"BLOCK_USR_DATA",
|
||||
"BLOCK_KEY0",
|
||||
"BLOCK_KEY1",
|
||||
"BLOCK_KEY2",
|
||||
"BLOCK_KEY3",
|
||||
"BLOCK_KEY4",
|
||||
"BLOCK_KEY5",
|
||||
"BLOCK_SYS_DATA2",
|
||||
]:
|
||||
if efuse.name == "BLOCK_USR_DATA":
|
||||
efuse.bit_len = 256
|
||||
efuse.type = "bytes:32"
|
||||
self.KEYBLOCKS.append(efuse)
|
||||
self.ALL_EFUSES[i] = None
|
||||
|
||||
elif efuse.category == "calibration":
|
||||
self.BLOCK2_CALIBRATION_EFUSES.append(efuse)
|
||||
self.ALL_EFUSES[i] = None
|
||||
|
||||
f = Field()
|
||||
f.name = "MAC_EUI64"
|
||||
f.block = 1
|
||||
f.bit_len = 64
|
||||
f.type = f"bytes:{f.bit_len // 8}"
|
||||
f.category = "MAC"
|
||||
f.class_type = "mac"
|
||||
f.description = "calc MAC_EUI64 = MAC[0]:MAC[1]:MAC[2]:MAC_EXT[0]:MAC_EXT[1]:MAC[3]:MAC[4]:MAC[5]"
|
||||
self.CALC.append(f)
|
||||
|
||||
for efuse in self.ALL_EFUSES:
|
||||
if efuse is not None:
|
||||
self.EFUSES.append(efuse)
|
||||
|
||||
self.ALL_EFUSES = []
|
389
espefuse/efuse/esp32h4/operations.py
Normal file
389
espefuse/efuse/esp32h4/operations.py
Normal file
@@ -0,0 +1,389 @@
|
||||
# This file includes the operations with eFuses for ESP32-H4 chip
|
||||
#
|
||||
# SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
import argparse
|
||||
import os # noqa: F401. It is used in IDF scripts
|
||||
import traceback
|
||||
|
||||
import espsecure
|
||||
|
||||
import esptool
|
||||
|
||||
from . import fields
|
||||
from .. import util
|
||||
from ..base_operations import (
|
||||
add_common_commands,
|
||||
add_force_write_always,
|
||||
burn_bit,
|
||||
burn_block_data,
|
||||
burn_efuse,
|
||||
check_error,
|
||||
dump,
|
||||
read_protect_efuse,
|
||||
summary,
|
||||
write_protect_efuse,
|
||||
)
|
||||
|
||||
|
||||
def protect_options(p):
|
||||
p.add_argument(
|
||||
"--no-write-protect",
|
||||
help="Disable write-protecting of the key. The key remains writable. "
|
||||
"(The keys use the RS coding scheme that does not support "
|
||||
"post-write data changes. Forced write can damage RS encoding bits.) "
|
||||
"The write-protecting of keypurposes does not depend on the option, "
|
||||
"it will be set anyway.",
|
||||
action="store_true",
|
||||
)
|
||||
p.add_argument(
|
||||
"--no-read-protect",
|
||||
help="Disable read-protecting of the key. The key remains readable software."
|
||||
"The key with keypurpose[USER, RESERVED and *_DIGEST] "
|
||||
"will remain readable anyway. For the rest keypurposes the read-protection "
|
||||
"will be defined the option (Read-protect by default).",
|
||||
action="store_true",
|
||||
)
|
||||
|
||||
|
||||
def add_commands(subparsers, efuses):
|
||||
add_common_commands(subparsers, efuses)
|
||||
burn_key = subparsers.add_parser(
|
||||
"burn_key", help="Burn the key block with the specified name"
|
||||
)
|
||||
protect_options(burn_key)
|
||||
add_force_write_always(burn_key)
|
||||
burn_key.add_argument(
|
||||
"block",
|
||||
help="Key block to burn",
|
||||
action="append",
|
||||
choices=efuses.BLOCKS_FOR_KEYS,
|
||||
)
|
||||
burn_key.add_argument(
|
||||
"keyfile",
|
||||
help="File containing 256 bits of binary key data. For the ECDSA_KEY purpose use PEM file.",
|
||||
action="append",
|
||||
type=argparse.FileType("rb"),
|
||||
)
|
||||
burn_key.add_argument(
|
||||
"keypurpose",
|
||||
help="Purpose to set.",
|
||||
action="append",
|
||||
choices=fields.EfuseKeyPurposeField.KEY_PURPOSES_NAME,
|
||||
)
|
||||
for _ in efuses.BLOCKS_FOR_KEYS:
|
||||
burn_key.add_argument(
|
||||
"block",
|
||||
help="Key block to burn",
|
||||
nargs="?",
|
||||
action="append",
|
||||
metavar="BLOCK",
|
||||
choices=efuses.BLOCKS_FOR_KEYS,
|
||||
)
|
||||
burn_key.add_argument(
|
||||
"keyfile",
|
||||
help="File containing 256 bits of binary key data. For the ECDSA_KEY purpose use PEM file.",
|
||||
nargs="?",
|
||||
action="append",
|
||||
metavar="KEYFILE",
|
||||
type=argparse.FileType("rb"),
|
||||
)
|
||||
burn_key.add_argument(
|
||||
"keypurpose",
|
||||
help="Purpose to set.",
|
||||
nargs="?",
|
||||
action="append",
|
||||
metavar="KEYPURPOSE",
|
||||
choices=fields.EfuseKeyPurposeField.KEY_PURPOSES_NAME,
|
||||
)
|
||||
|
||||
burn_key_digest = subparsers.add_parser(
|
||||
"burn_key_digest",
|
||||
help="Parse a RSA public key and burn the digest to key efuse block",
|
||||
)
|
||||
protect_options(burn_key_digest)
|
||||
add_force_write_always(burn_key_digest)
|
||||
burn_key_digest.add_argument(
|
||||
"block",
|
||||
help="Key block to burn",
|
||||
action="append",
|
||||
choices=efuses.BLOCKS_FOR_KEYS,
|
||||
)
|
||||
burn_key_digest.add_argument(
|
||||
"keyfile",
|
||||
help="Key file to digest (PEM format)",
|
||||
action="append",
|
||||
type=argparse.FileType("rb"),
|
||||
)
|
||||
burn_key_digest.add_argument(
|
||||
"keypurpose",
|
||||
help="Purpose to set.",
|
||||
action="append",
|
||||
choices=fields.EfuseKeyPurposeField.DIGEST_KEY_PURPOSES,
|
||||
)
|
||||
for _ in efuses.BLOCKS_FOR_KEYS:
|
||||
burn_key_digest.add_argument(
|
||||
"block",
|
||||
help="Key block to burn",
|
||||
nargs="?",
|
||||
action="append",
|
||||
metavar="BLOCK",
|
||||
choices=efuses.BLOCKS_FOR_KEYS,
|
||||
)
|
||||
burn_key_digest.add_argument(
|
||||
"keyfile",
|
||||
help="Key file to digest (PEM format)",
|
||||
nargs="?",
|
||||
action="append",
|
||||
metavar="KEYFILE",
|
||||
type=argparse.FileType("rb"),
|
||||
)
|
||||
burn_key_digest.add_argument(
|
||||
"keypurpose",
|
||||
help="Purpose to set.",
|
||||
nargs="?",
|
||||
action="append",
|
||||
metavar="KEYPURPOSE",
|
||||
choices=fields.EfuseKeyPurposeField.DIGEST_KEY_PURPOSES,
|
||||
)
|
||||
|
||||
p = subparsers.add_parser(
|
||||
"set_flash_voltage",
|
||||
help="Permanently set the internal flash voltage regulator "
|
||||
"to either 1.8V, 3.3V or OFF. "
|
||||
"This means GPIO45 can be high or low at reset without "
|
||||
"changing the flash voltage.",
|
||||
)
|
||||
p.add_argument("voltage", help="Voltage selection", choices=["1.8V", "3.3V", "OFF"])
|
||||
|
||||
p = subparsers.add_parser(
|
||||
"burn_custom_mac", help="Burn a 48-bit Custom MAC Address to EFUSE BLOCK3."
|
||||
)
|
||||
p.add_argument(
|
||||
"mac",
|
||||
help="Custom MAC Address to burn given in hexadecimal format with bytes "
|
||||
"separated by colons (e.g. AA:CD:EF:01:02:03).",
|
||||
type=fields.base_fields.CheckArgValue(efuses, "CUSTOM_MAC"),
|
||||
)
|
||||
add_force_write_always(p)
|
||||
|
||||
p = subparsers.add_parser("get_custom_mac", help="Prints the Custom MAC Address.")
|
||||
|
||||
|
||||
def burn_custom_mac(esp, efuses, args):
|
||||
efuses["CUSTOM_MAC"].save(args.mac)
|
||||
if not efuses.burn_all(check_batch_mode=True):
|
||||
return
|
||||
get_custom_mac(esp, efuses, args)
|
||||
print("Successful")
|
||||
|
||||
|
||||
def get_custom_mac(esp, efuses, args):
|
||||
print("Custom MAC Address: {}".format(efuses["CUSTOM_MAC"].get()))
|
||||
|
||||
|
||||
def set_flash_voltage(esp, efuses, args):
|
||||
raise esptool.FatalError("set_flash_voltage is not supported!")
|
||||
|
||||
|
||||
def adc_info(esp, efuses, args):
|
||||
# fmt: off
|
||||
# TODO: [ESP32H4] IDF-12268
|
||||
print("Not supported yet")
|
||||
# fmt: on
|
||||
|
||||
|
||||
def burn_key(esp, efuses, args, digest=None):
|
||||
if digest is None:
|
||||
datafile_list = args.keyfile[
|
||||
0 : len([name for name in args.keyfile if name is not None]) :
|
||||
]
|
||||
else:
|
||||
datafile_list = digest[0 : len([name for name in digest if name is not None]) :]
|
||||
efuses.force_write_always = args.force_write_always
|
||||
block_name_list = args.block[
|
||||
0 : len([name for name in args.block if name is not None]) :
|
||||
]
|
||||
keypurpose_list = args.keypurpose[
|
||||
0 : len([name for name in args.keypurpose if name is not None]) :
|
||||
]
|
||||
|
||||
util.check_duplicate_name_in_list(block_name_list)
|
||||
if len(block_name_list) != len(datafile_list) or len(block_name_list) != len(
|
||||
keypurpose_list
|
||||
):
|
||||
raise esptool.FatalError(
|
||||
"The number of blocks (%d), datafile (%d) and keypurpose (%d) "
|
||||
"should be the same."
|
||||
% (len(block_name_list), len(datafile_list), len(keypurpose_list))
|
||||
)
|
||||
|
||||
print("Burn keys to blocks:")
|
||||
for block_name, datafile, keypurpose in zip(
|
||||
block_name_list, datafile_list, keypurpose_list
|
||||
):
|
||||
efuse = None
|
||||
for block in efuses.blocks:
|
||||
if block_name == block.name or block_name in block.alias:
|
||||
efuse = efuses[block.name]
|
||||
if efuse is None:
|
||||
raise esptool.FatalError("Unknown block name - %s" % (block_name))
|
||||
num_bytes = efuse.bit_len // 8
|
||||
|
||||
block_num = efuses.get_index_block_by_name(block_name)
|
||||
block = efuses.blocks[block_num]
|
||||
|
||||
if digest is None:
|
||||
if keypurpose == "ECDSA_KEY":
|
||||
sk = espsecure._load_ecdsa_signing_key(datafile)
|
||||
data = sk.to_string()
|
||||
if len(data) == 24:
|
||||
# the private key is 24 bytes long for NIST192p, and 8 bytes of padding
|
||||
data = b"\x00" * 8 + data
|
||||
else:
|
||||
data = datafile.read()
|
||||
else:
|
||||
data = datafile
|
||||
|
||||
print(" - %s" % (efuse.name), end=" ")
|
||||
revers_msg = None
|
||||
if efuses[block.key_purpose_name].need_reverse(keypurpose):
|
||||
revers_msg = f"\tReversing byte order for {keypurpose} hardware peripheral"
|
||||
data = data[::-1]
|
||||
print("-> [%s]" % (util.hexify(data, " ")))
|
||||
if revers_msg:
|
||||
print(revers_msg)
|
||||
if len(data) != num_bytes:
|
||||
raise esptool.FatalError(
|
||||
"Incorrect key file size %d. Key file must be %d bytes (%d bits) "
|
||||
"of raw binary key data." % (len(data), num_bytes, num_bytes * 8)
|
||||
)
|
||||
|
||||
if efuses[block.key_purpose_name].need_rd_protect(keypurpose):
|
||||
read_protect = False if args.no_read_protect else True
|
||||
else:
|
||||
read_protect = False
|
||||
write_protect = not args.no_write_protect
|
||||
|
||||
# using efuse instead of a block gives the advantage of checking it as the whole field.
|
||||
efuse.save(data)
|
||||
|
||||
disable_wr_protect_key_purpose = False
|
||||
if efuses[block.key_purpose_name].get() != keypurpose:
|
||||
if efuses[block.key_purpose_name].is_writeable():
|
||||
print(
|
||||
"\t'%s': '%s' -> '%s'."
|
||||
% (
|
||||
block.key_purpose_name,
|
||||
efuses[block.key_purpose_name].get(),
|
||||
keypurpose,
|
||||
)
|
||||
)
|
||||
efuses[block.key_purpose_name].save(keypurpose)
|
||||
disable_wr_protect_key_purpose = True
|
||||
else:
|
||||
raise esptool.FatalError(
|
||||
"It is not possible to change '%s' to '%s' "
|
||||
"because write protection bit is set."
|
||||
% (block.key_purpose_name, keypurpose)
|
||||
)
|
||||
else:
|
||||
print("\t'%s' is already '%s'." % (block.key_purpose_name, keypurpose))
|
||||
if efuses[block.key_purpose_name].is_writeable():
|
||||
disable_wr_protect_key_purpose = True
|
||||
|
||||
if disable_wr_protect_key_purpose:
|
||||
print("\tDisabling write to '%s'." % block.key_purpose_name)
|
||||
efuses[block.key_purpose_name].disable_write()
|
||||
|
||||
if read_protect:
|
||||
print("\tDisabling read to key block")
|
||||
efuse.disable_read()
|
||||
|
||||
if write_protect:
|
||||
print("\tDisabling write to key block")
|
||||
efuse.disable_write()
|
||||
print("")
|
||||
|
||||
if not write_protect:
|
||||
print("Keys will remain writeable (due to --no-write-protect)")
|
||||
if args.no_read_protect:
|
||||
print("Keys will remain readable (due to --no-read-protect)")
|
||||
|
||||
if not efuses.burn_all(check_batch_mode=True):
|
||||
return
|
||||
print("Successful")
|
||||
|
||||
|
||||
def burn_key_digest(esp, efuses, args):
|
||||
digest_list = []
|
||||
datafile_list = args.keyfile[
|
||||
0 : len([name for name in args.keyfile if name is not None]) :
|
||||
]
|
||||
block_list = args.block[
|
||||
0 : len([block for block in args.block if block is not None]) :
|
||||
]
|
||||
for block_name, datafile in zip(block_list, datafile_list):
|
||||
efuse = None
|
||||
for block in efuses.blocks:
|
||||
if block_name == block.name or block_name in block.alias:
|
||||
efuse = efuses[block.name]
|
||||
if efuse is None:
|
||||
raise esptool.FatalError("Unknown block name - %s" % (block_name))
|
||||
num_bytes = efuse.bit_len // 8
|
||||
digest = espsecure._digest_sbv2_public_key(datafile)
|
||||
if len(digest) != num_bytes:
|
||||
raise esptool.FatalError(
|
||||
"Incorrect digest size %d. Digest must be %d bytes (%d bits) "
|
||||
"of raw binary key data." % (len(digest), num_bytes, num_bytes * 8)
|
||||
)
|
||||
digest_list.append(digest)
|
||||
burn_key(esp, efuses, args, digest=digest_list)
|
||||
|
||||
|
||||
def espefuse(esp, efuses, args, command):
|
||||
parser = argparse.ArgumentParser()
|
||||
subparsers = parser.add_subparsers(dest="operation")
|
||||
add_commands(subparsers, efuses)
|
||||
try:
|
||||
cmd_line_args = parser.parse_args(command.split())
|
||||
except SystemExit:
|
||||
traceback.print_stack()
|
||||
raise esptool.FatalError('"{}" - incorrect command'.format(command))
|
||||
if cmd_line_args.operation == "execute_scripts":
|
||||
configfiles = cmd_line_args.configfiles
|
||||
index = cmd_line_args.index
|
||||
# copy arguments from args to cmd_line_args
|
||||
vars(cmd_line_args).update(vars(args))
|
||||
if cmd_line_args.operation == "execute_scripts":
|
||||
cmd_line_args.configfiles = configfiles
|
||||
cmd_line_args.index = index
|
||||
if cmd_line_args.operation is None:
|
||||
parser.print_help()
|
||||
parser.exit(1)
|
||||
operation_func = globals()[cmd_line_args.operation]
|
||||
# each 'operation' is a module-level function of the same name
|
||||
operation_func(esp, efuses, cmd_line_args)
|
||||
|
||||
|
||||
def execute_scripts(esp, efuses, args):
|
||||
efuses.batch_mode_cnt += 1
|
||||
del args.operation
|
||||
scripts = args.scripts
|
||||
del args.scripts
|
||||
|
||||
for file in scripts:
|
||||
with open(file.name, "r") as file:
|
||||
exec(compile(file.read(), file.name, "exec"))
|
||||
|
||||
if args.debug:
|
||||
for block in efuses.blocks:
|
||||
data = block.get_bitstring(from_read=False)
|
||||
block.print_block(data, "regs_for_burn", args.debug)
|
||||
|
||||
efuses.batch_mode_cnt -= 1
|
||||
if not efuses.burn_all(check_batch_mode=True):
|
||||
return
|
||||
print("Successful")
|
105
espefuse/efuse_defs/esp32h4.yaml
Normal file
105
espefuse/efuse_defs/esp32h4.yaml
Normal file
@@ -0,0 +1,105 @@
|
||||
VER_NO: 709e8ea096e8a03a10006d40d5451a49
|
||||
EFUSES:
|
||||
WR_DIS : {show: y, blk : 0, word: 0, pos : 0, len : 32, start : 0, type : 'uint:32', wr_dis: null, rd_dis: null, alt : '', dict : '', desc: Disable programming of individual eFuses, rloc: EFUSE_RD_WR_DIS_REG, bloc: 'B0,B1,B2,B3'}
|
||||
RD_DIS : {show: y, blk : 0, word: 1, pos : 0, len : 7, start : 32, type : 'uint:7', wr_dis : 0, rd_dis: null, alt : '', dict : '', desc: Disable reading from BlOCK4-10, rloc: 'EFUSE_RD_REPEAT_DATA0_REG[6:0]', bloc: 'B4[6:0]'}
|
||||
DIS_USB_JTAG : {show: y, blk : 0, word: 1, pos : 7, len : 1, start : 39, type : bool, wr_dis : 2, rd_dis: null, alt : '', dict : '', desc: 'Represents whether the function of usb switch to jtag is disabled or enabled. 1: disabled. 0: enabled', rloc: 'EFUSE_RD_REPEAT_DATA0_REG[7]', bloc: 'B4[7]'}
|
||||
DIS_USB_SERIAL_JTAG : {show: y, blk : 0, word: 1, pos: 8, len : 1, start : 40, type : bool, wr_dis : 2, rd_dis: null, alt : '', dict : '', desc: 'Represents whether USB-Serial-JTAG is disabled or enabled. 1: disabled. 0: enabled', rloc: 'EFUSE_RD_REPEAT_DATA0_REG[8]', bloc: 'B5[0]'}
|
||||
DIS_FORCE_DOWNLOAD : {show: y, blk : 0, word: 1, pos: 9, len : 1, start : 41, type : bool, wr_dis : 2, rd_dis: null, alt : '', dict : '', desc: 'Represents whether the function that forces chip into download mode is disabled or enabled. 1: disabled. 0: enabled', rloc: 'EFUSE_RD_REPEAT_DATA0_REG[9]', bloc: 'B5[1]'}
|
||||
SPI_DOWNLOAD_MSPI_DIS : {show: y, blk : 0, word: 1, pos: 10, len : 1, start : 42, type : bool, wr_dis : 17, rd_dis: null, alt : '', dict : '', desc: 'Represents whether SPI0 controller during boot_mode_download is disabled or enabled. 1: disabled. 0: enabled', rloc: 'EFUSE_RD_REPEAT_DATA0_REG[10]', bloc: 'B5[2]'}
|
||||
DIS_TWAI : {show: y, blk : 0, word: 1, pos: 11, len : 1, start : 43, type : bool, wr_dis : 2, rd_dis: null, alt : DIS_CAN, dict : '', desc: 'Represents whether TWAI function is disabled or enabled. 1: disabled. 0: enabled', rloc: 'EFUSE_RD_REPEAT_DATA0_REG[11]', bloc: 'B5[3]'}
|
||||
JTAG_SEL_ENABLE : {show: y, blk : 0, word: 1, pos: 12, len : 1, start : 44, type : bool, wr_dis : 2, rd_dis: null, alt : '', dict : '', desc: 'Represents whether the selection between usb_to_jtag and pad_to_jtag through strapping gpio15 when both EFUSE_DIS_PAD_JTAG and EFUSE_DIS_USB_JTAG are equal to 0 is enabled or disabled. 1: enabled. 0: disabled', rloc: 'EFUSE_RD_REPEAT_DATA0_REG[12]', bloc: 'B5[4]'}
|
||||
DIS_PAD_JTAG : {show: y, blk : 0, word: 1, pos: 13, len : 1, start : 45, type : bool, wr_dis : 2, rd_dis: null, alt : '', dict : '', desc: 'Represents whether JTAG is disabled in the hard way(permanently). 1: disabled. 0: enabled', rloc: 'EFUSE_RD_REPEAT_DATA0_REG[13]', bloc: 'B5[5]'}
|
||||
DIS_DOWNLOAD_MANUAL_ENCRYPT : {show: y, blk : 0, word: 1, pos: 14, len : 1, start : 46, type : bool, wr_dis : 2, rd_dis: null, alt : '', dict : '', desc: 'Represents whether flash encrypt function is disabled or enabled(except in SPI boot mode). 1: disabled. 0: enabled', rloc: 'EFUSE_RD_REPEAT_DATA0_REG[14]', bloc: 'B5[6]'}
|
||||
RESERVED_RPT0_15_16 : {show: n, blk : 0, word: 1, pos: 15, len : 2, start : 47, type : 'uint:2', wr_dis : 30, rd_dis: null, alt : '', dict : '', desc: Reserved, rloc: 'EFUSE_RD_REPEAT_DATA0_REG[16:15]', bloc: 'B5[7],B6[0]'}
|
||||
RESERVED_RPT0_17 : {show: n, blk : 0, word: 1, pos: 17, len : 1, start : 49, type : bool, wr_dis : 30, rd_dis: null, alt : '', dict : '', desc: Reserved, rloc: 'EFUSE_RD_REPEAT_DATA0_REG[17]', bloc: 'B6[1]'}
|
||||
PVT_GLITCH_EN : {show: y, blk : 0, word: 1, pos: 18, len : 1, start : 50, type : bool, wr_dis : 30, rd_dis: null, alt : '', dict : '', desc: Represents whether to enable PVT power glitch monitor function, rloc: 'EFUSE_RD_REPEAT_DATA0_REG[18]', bloc: 'B6[2]'}
|
||||
PVT_GLITCH_CHARGE_RESET : {show: y, blk : 0, word: 1, pos: 19, len : 1, start : 51, type : bool, wr_dis : 30, rd_dis: null, alt : '', dict : '', desc: Represents whether to trigger reset or charge pump when PVT power glitch happened, rloc: 'EFUSE_RD_REPEAT_DATA0_REG[19]', bloc: 'B6[3]'}
|
||||
PVT_GLITCH_MODE : {show: y, blk : 0, word: 1, pos: 20, len : 2, start : 52, type : 'uint:2', wr_dis : 3, rd_dis: null, alt : '', dict : '', desc: Use to configure glitch mode, rloc: 'EFUSE_RD_REPEAT_DATA0_REG[21:20]', bloc: 'B6[5:4]'}
|
||||
DIS_CORE1 : {show: y, blk : 0, word: 1, pos: 22, len : 1, start: 54, type : bool, wr_dis: null, rd_dis: null, alt : '', dict : '', desc: Disable core1, rloc: 'EFUSE_RD_REPEAT_DATA0_REG[22]', bloc: 'B6[6]'}
|
||||
SPI_BOOT_CRYPT_CNT : {show: y, blk : 0, word: 1, pos: 23, len : 3, start : 55, type : 'uint:3', wr_dis : 4, rd_dis: null, alt : '', dict: '{0: "Disable", 1: "Enable", 3: "Disable", 7: "Enable"}', desc: Enables flash encryption when 1 or 3 bits are set and disables otherwise, rloc: 'EFUSE_RD_REPEAT_DATA0_REG[25:23]', bloc: 'B6[7],B7[1:0]'}
|
||||
SECURE_BOOT_KEY_REVOKE0 : {show: y, blk : 0, word: 1, pos: 26, len : 1, start : 58, type : bool, wr_dis : 5, rd_dis: null, alt : '', dict : '', desc: Revoke 1st secure boot key, rloc: 'EFUSE_RD_REPEAT_DATA0_REG[25]', bloc: 'B7[1]'}
|
||||
SECURE_BOOT_KEY_REVOKE1 : {show: y, blk : 0, word: 1, pos: 27, len : 1, start : 59, type : bool, wr_dis : 6, rd_dis: null, alt : '', dict : '', desc: Revoke 2nd secure boot key, rloc: 'EFUSE_RD_REPEAT_DATA0_REG[26]', bloc: 'B7[2]'}
|
||||
SECURE_BOOT_KEY_REVOKE2 : {show: y, blk : 0, word: 1, pos: 28, len : 1, start : 60, type : bool, wr_dis : 7, rd_dis: null, alt : '', dict : '', desc: Revoke 3rd secure boot key, rloc: 'EFUSE_RD_REPEAT_DATA0_REG[27]', bloc: 'B7[3]'}
|
||||
KEY_PURPOSE_0 : {show: y, blk : 0, word: 2, pos: 0, len : 5, start : 64, type : 'uint:5', wr_dis : 8, rd_dis: null, alt : KEY0_PURPOSE, dict : '', desc: Represents the purpose of Key0, rloc: 'EFUSE_RD_REPEAT_DATA1_REG[4:0]', bloc: 'B8[4:0]'}
|
||||
KEY_PURPOSE_1 : {show: y, blk : 0, word: 2, pos: 5, len : 5, start : 69, type : 'uint:5', wr_dis : 9, rd_dis: null, alt : KEY1_PURPOSE, dict : '', desc: Represents the purpose of Key1, rloc: 'EFUSE_RD_REPEAT_DATA1_REG[9:5]', bloc: 'B8[7:5],B9[1:0]'}
|
||||
KEY_PURPOSE_2 : {show: y, blk : 0, word: 2, pos: 10, len : 5, start : 74, type : 'uint:5', wr_dis : 10, rd_dis: null, alt : KEY2_PURPOSE, dict : '', desc: Represents the purpose of Key2, rloc: 'EFUSE_RD_REPEAT_DATA1_REG[14:10]', bloc: 'B9[6:2]'}
|
||||
KEY_PURPOSE_3 : {show: y, blk : 0, word: 2, pos: 15, len : 5, start: 79, type : 'uint:5', wr_dis : 11, rd_dis: null, alt : KEY3_PURPOSE, dict : '', desc: Represents the purpose of Key3, rloc: 'EFUSE_RD_REPEAT_DATA1_REG[19:15]', bloc: 'B9[7],B10[3:0]'}
|
||||
KEY_PURPOSE_4 : {show: y, blk : 0, word: 2, pos: 20, len : 5, start: 84, type : 'uint:5', wr_dis : 12, rd_dis: null, alt : KEY4_PURPOSE, dict : '', desc: Represents the purpose of Key4, rloc: 'EFUSE_RD_REPEAT_DATA1_REG[24:20]', bloc: 'B10[7:4],B11[0]'}
|
||||
KEY_PURPOSE_5 : {show: y, blk : 0, word: 2, pos: 25, len : 5, start: 89, type : 'uint:5', wr_dis : 13, rd_dis: null, alt : KEY5_PURPOSE, dict : '', desc: Represents the purpose of Key5, rloc: 'EFUSE_RD_REPEAT_DATA1_REG[29:25]', bloc: 'B11[5:1]'}
|
||||
SEC_DPA_LEVEL : {show: y, blk : 0, word: 2, pos: 30, len : 2, start: 94, type : 'uint:2', wr_dis : 14, rd_dis: null, alt : DPA_SEC_LEVEL, dict : '', desc: Represents the spa secure level by configuring the clock random divide mode, rloc: 'EFUSE_RD_REPEAT_DATA1_REG[31:30]', bloc: 'B11[7:6]'}
|
||||
XTS_DPA_PSEUDO_LEVEL : {show: y, blk : 0, word: 3, pos: 0, len : 2, start: 96, type : 'uint:2', wr_dis: null, rd_dis: null, alt : '', dict : '', desc: Reserved, rloc: 'EFUSE_RD_REPEAT_DATA2_REG[1:0]', bloc: 'B12[1:0]'}
|
||||
XTS_DPA_CLK_ENABLE : {show: y, blk : 0, word: 3, pos: 2, len : 1, start: 98, type : bool, wr_dis: null, rd_dis: null, alt : '', dict : '', desc: Reserved, rloc: 'EFUSE_RD_REPEAT_DATA2_REG[2]', bloc: 'B12[2]'}
|
||||
ECC_FORCE_CONST_TIME : {show: y, blk : 0, word: 3, pos: 3, len : 1, start: 99, type : bool, wr_dis: 19, rd_dis: null, alt : '', dict : '', desc: 'ecc force const time', rloc: 'EFUSE_RD_REPEAT_DATA2_REG[3]', bloc: 'B12[3]'}
|
||||
ECDSA_P384_ENABLE : {show: y, blk : 0, word: 3, pos: 4, len : 1, start: 100, type : bool, wr_dis: 19, rd_dis: null, alt : '', dict : '', desc: 'if the chip supports ECDSA P384', rloc: 'EFUSE_RD_REPEAT_DATA2_REG[4]', bloc: 'B12[4]'}
|
||||
SECURE_BOOT_EN : {show: y, blk : 0, word: 3, pos: 5, len : 1, start: 101, type : bool, wr_dis : 15, rd_dis: null, alt : '', dict : '', desc: 'Represents whether secure boot is enabled or disabled. 1: enabled. 0: disabled', rloc: 'EFUSE_RD_REPEAT_DATA2_REG[5]', bloc: 'B12[5]'}
|
||||
SECURE_BOOT_AGGRESSIVE_REVOKE : {show: y, blk : 0, word: 3, pos: 6, len : 1, start: 102, type : bool, wr_dis : 16, rd_dis: null, alt : '', dict : '', desc: 'Represents whether revoking aggressive secure boot is enabled or disabled. 1: enabled. 0: disabled', rloc: 'EFUSE_RD_REPEAT_DATA2_REG[6]', bloc: 'B12[6]'}
|
||||
KM_DISABLE_DEPLOY_MODE : {show: y, blk : 0, word: 3, pos: 7, len : 5, start: 103, type : 'uint:5', wr_dis : 16, rd_dis: null, alt : '', dict : '', desc: Represents whether the new key deployment of key manager is disabled, rloc: 'EFUSE_RD_REPEAT_DATA2_REG[11:7]', bloc: 'B12[7],B13[3:0]'}
|
||||
KM_RND_SWITCH_CYCLE : {show: y, blk : 0, word: 3, pos: 12, len : 2, start: 108, type : 'uint:2', wr_dis : 16, rd_dis: null, alt : '', dict : '', desc: Represents the cycle at which the Key Manager switches random numbers, rloc: 'EFUSE_RD_REPEAT_DATA2_REG[13:12]', bloc: 'B13[5:4]'}
|
||||
KM_DEPLOY_ONLY_ONCE : {show: y, blk : 0, word: 3, pos: 14, len : 5, start: 110, type : 'uint:5', wr_dis : 16, rd_dis: null, alt : '', dict : '', desc: Represents whether the corresponding key can be deployed only once, rloc: 'EFUSE_RD_REPEAT_DATA2_REG[18:14]', bloc: 'B13[7:6],B14[2:0]'}
|
||||
FORCE_USE_KEY_MANAGER_KEY : {show: y, blk : 0, word: 3, pos: 19, len : 5, start: 115, type : 'uint:5', wr_dis : 16, rd_dis: null, alt : '', dict : '', desc: Represents whether the corresponding key must come from Key Manager, rloc: 'EFUSE_RD_REPEAT_DATA2_REG[23:19]', bloc: 'B14[7:3]'}
|
||||
FORCE_DISABLE_SW_INIT_KEY : {show: y, blk : 0, word: 3, pos: 24, len : 1, start: 120, type : bool, wr_dis : 16, rd_dis: null, alt : '', dict : '', desc: Represents whether to disable the use of the initialization key written by softw, rloc: 'EFUSE_RD_REPEAT_DATA2_REG[24]', bloc: 'B15[0]'}
|
||||
KM_XTS_KEY_LENGTH_256 : {show: y, blk : 0, word: 3, pos: 25, len : 1, start: 121, type : bool, wr_dis : 16, rd_dis: null, alt : '', dict : '', desc: Represents which key flash encryption uses, rloc: 'EFUSE_RD_REPEAT_DATA2_REG[25]', bloc: 'B15[1]'}
|
||||
LOCK_KM_KEY : {show: y, blk : 0, word: 3, pos: 26, len : 1, start: 122, type : bool, wr_dis : 16, rd_dis: null, alt : '', dict : '', desc: Represents whether the keys in the Key Manager are locked after deployment, rloc: 'EFUSE_RD_REPEAT_DATA2_REG[26]', bloc: 'B15[2]'}
|
||||
FLASH_TPUW : {show: y, blk : 0, word: 3, pos: 27, len : 3, start: 123, type : 'uint:3', wr_dis : 18, rd_dis: null, alt : '', dict : '', desc: Represents the flash waiting time after power-up; in unit of ms. When the value less than 15; the waiting time is the programmed value. Otherwise; the waiting time is 2 times the programmed value, rloc: 'EFUSE_RD_REPEAT_DATA2_REG[29:27]', bloc: 'B15[5:3]'}
|
||||
DIS_DOWNLOAD_MODE : {show: y, blk : 0, word: 3, pos: 31, len : 1, start: 127, type : bool, wr_dis : 18, rd_dis: null, alt : '', dict : '', desc: 'Represents whether Download mode is disabled or enabled. 1: disabled. 0: enabled', rloc: 'EFUSE_RD_REPEAT_DATA2_REG[31]', bloc: 'B15[7]'}
|
||||
DIS_DIRECT_BOOT : {show: y, blk : 0, word: 4, pos: 0, len : 1, start: 128, type : bool, wr_dis : 18, rd_dis: null, alt : '', dict : '', desc: 'Represents whether direct boot mode is disabled or enabled. 1: disabled. 0: enabled', rloc: 'EFUSE_RD_REPEAT_DATA3_REG[0]', bloc: 'B16[0]'}
|
||||
DIS_USB_SERIAL_JTAG_ROM_PRINT : {show: y, blk : 0, word: 4, pos: 1, len : 1, start: 129, type : bool, wr_dis : 18, rd_dis: null, alt : DIS_USB_PRINT, dict : '', desc: 'Represents whether print from USB-Serial-JTAG is disabled or enabled. 1: disabled. 0: enabled', rloc: 'EFUSE_RD_REPEAT_DATA3_REG[1]', bloc: 'B16[1]'}
|
||||
DIS_USB_SERIAL_JTAG_DOWNLOAD_MODE: {show: y, blk : 0, word: 4, pos: 2, len : 1, start: 130, type : bool, wr_dis : 18, rd_dis: null, alt : '', dict : '', desc: 'Represents whether the USB-Serial-JTAG download function is disabled or enabled. 1: disabled. 0: enabled', rloc: 'EFUSE_RD_REPEAT_DATA3_REG[2]', bloc: 'B16[2]'}
|
||||
ENABLE_SECURITY_DOWNLOAD : {show: y, blk : 0, word: 4, pos: 3, len : 1, start: 131, type : bool, wr_dis : 18, rd_dis: null, alt : '', dict : '', desc: 'Represents whether security download is enabled or disabled. 1: enabled. 0: disabled', rloc: 'EFUSE_RD_REPEAT_DATA3_REG[3]', bloc: 'B16[3]'}
|
||||
UART_PRINT_CONTROL : {show: y, blk : 0, word: 4, pos: 4, len : 2, start: 132, type : 'uint:2', wr_dis : 18, rd_dis: null, alt : '', dict: '{0: "Enable", 1: "Enable when GPIO8 is low at reset", 2: "Enable when GPIO8 is high at reset", 3: "Disable"}', desc: Set the default UARTboot message output mode, rloc: 'EFUSE_RD_REPEAT_DATA3_REG[5:4]', bloc: 'B16[5:4]'}
|
||||
FORCE_SEND_RESUME : {show: y, blk : 0, word: 4, pos: 6, len : 1, start: 134, type : bool, wr_dis : 18, rd_dis: null, alt : '', dict : '', desc: 'Represents whether ROM code is forced to send a resume command during SPI boot. 1: forced. 0:not forced', rloc: 'EFUSE_RD_REPEAT_DATA3_REG[6]', bloc: 'B16[6]'}
|
||||
SECURE_VERSION : {show: y, blk : 0, word: 4, pos: 7, len : 16, start: 135, type : 'uint:16', wr_dis : 18, rd_dis: null, alt : '', dict : '', desc: Represents the version used by ESP-IDF anti-rollback feature, rloc: 'EFUSE_RD_REPEAT_DATA3_REG[22:7]', bloc: 'B16[7],B17[7:0],B18[6:0]'}
|
||||
HUK_GEN_STATE : {show: y, blk : 0, word: 4, pos: 23, len : 5, start: 151, type : 'uint:5', wr_dis : 19, rd_dis: null, alt : '', dict : '', desc: huk gen state, rloc: 'EFUSE_RD_REPEAT_DATA3_REG[27:23]', bloc: 'B18[7],B19[3:0]'}
|
||||
FLASH_LDO_EFUSE_SEL : {show: y, blk : 0, word: 4, pos: 28, len : 1, start: 156, type : bool, wr_dis : 18, rd_dis: null, alt : '', dict : '', desc: 'Represents whether the HUK generate mode is valid', rloc: 'EFUSE_RD_REPEAT_DATA3_REG[28]', bloc: 'B19[4]'}
|
||||
USB_DREFH : {show: y, blk : 0, word: 5, pos: 0, len : 2, start: 160, type : 'uint:2', wr_dis: null, rd_dis: null, alt : '', dict : '', desc: USB device DREFH, rloc: 'EFUSE_RD_REPEAT_DATA4_REG[1:0]', bloc: 'B20[1:0]'}
|
||||
USB_DREFL : {show: y, blk : 0, word: 5, pos: 2, len : 2, start: 162, type : 'uint:2', wr_dis: null, rd_dis: null, alt : '', dict : '', desc: USB device DREFL, rloc: 'EFUSE_RD_REPEAT_DATA4_REG[3:2]]', bloc: 'B20[3:2]'}
|
||||
USB_EXCHG_PINS : {show: y, blk : 0, word: 5, pos: 4, len : 1, start: 164, type : bool, wr_dis: null, rd_dis: null, alt : '', dict : '', desc: Represents whether the D+ and D- pins of USB_SERIAL_JTAG PHY is exchanged, rloc: 'EFUSE_RD_REPEAT_DATA4_REG[4]]', bloc: 'B20[4]'}
|
||||
USB_OTG_FS_DREFH : {show: y, blk : 0, word: 5, pos: 5, len : 2, start: 165, type : 'uint:2', wr_dis: null, rd_dis: null, alt : '', dict : '', desc: USB OTG FS DREFH, rloc: 'EFUSE_RD_REPEAT_DATA4_REG[6:5]', bloc: 'B20[6:5]'}
|
||||
USB_OTG_FS_DREFL : {show: y, blk : 0, word: 5, pos: 7, len : 2, start: 167, type : 'uint:2', wr_dis: null, rd_dis: null, alt : '', dict : '', desc: USB OTG FS DREFL, rloc: 'EFUSE_RD_REPEAT_DATA4_REG[8:7]', bloc: 'B20[7],B21[0]'}
|
||||
USB_OTG_FS_EXCHG_PINS : {show: y, blk : 0, word: 5, pos: 9, len : 1, start: 169, type : bool, wr_dis: null, rd_dis: null, alt : '', dict : '', desc: USB OTG FS EXCHG PINS, rloc: 'EFUSE_RD_REPEAT_DATA4_REG[9]', bloc: 'B21[1]'}
|
||||
USB_PHY_SEL : {show: y, blk : 0, word: 5, pos: 10, len : 1, start: 170, type : bool, wr_dis: null, rd_dis: null, alt : '', dict : '', desc: USB PHY SEL, rloc: 'EFUSE_RD_REPEAT_DATA4_REG[10]', bloc: 'B21[2]'}
|
||||
MAC : {show: y, blk : 1, word: 0, pos : 0, len : 48, start : 0, type : 'bytes:6', wr_dis : 20, rd_dis: null, alt : MAC_FACTORY, dict : '', desc: MAC address, rloc: EFUSE_RD_MAC_SPI_SYS_0_REG, bloc: 'B0,B1,B2,B3,B4,B5'}
|
||||
MAC_EXT : {show: y, blk : 1, word: 1, pos: 16, len : 16, start : 48, type : 'bytes:2', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: Stores the extended bits of MAC address, rloc: 'EFUSE_RD_MAC_SPI_SYS_1_REG[31:16]', bloc: 'B6,B7'}
|
||||
MAC_SPI_RESERVED : {show: n, blk : 1, word: 2, pos : 0, len : 14, start : 64, type : 'uint:14', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: Reserved, rloc: 'EFUSE_RD_MAC_SPI_SYS_2_REG[13:0]', bloc: 'B8,B9[5:0]'}
|
||||
SPI_PAD_CONF_1 : {show: n, blk : 1, word: 2, pos: 14, len : 18, start : 78, type : 'uint:18', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: Stores the first part of SPI_PAD_CONF, rloc: 'EFUSE_RD_MAC_SPI_SYS_2_REG[31:14]', bloc: 'B9[7:6],B10,B11'}
|
||||
SPI_PAD_CONF_2 : {show: n, blk : 1, word: 3, pos : 0, len : 18, start : 96, type : 'uint:18', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: Stores the second part of SPI_PAD_CONF, rloc: 'EFUSE_RD_MAC_SPI_SYS_3_REG[17:0]', bloc: 'B12,B13,B14[1:0]'}
|
||||
WAFER_VERSION_MINOR : {show: y, blk : 1, word: 3, pos: 18, len : 4, start: 114, type : 'uint:4', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: '', rloc: 'EFUSE_RD_MAC_SPI_SYS_3_REG[21:18]', bloc: 'B14[5:2]'}
|
||||
WAFER_VERSION_MAJOR : {show: y, blk : 1, word: 3, pos: 22, len : 2, start: 118, type : 'uint:2', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: '', rloc: 'EFUSE_RD_MAC_SPI_SYS_3_REG[23:22]', bloc: 'B14[7:6]'}
|
||||
PKG_VERSION : {show: y, blk : 1, word: 3, pos: 24, len : 3, start: 120, type : 'uint:3', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: Package version, rloc: 'EFUSE_RD_MAC_SPI_SYS_3_REG[26:24]', bloc: 'B15[2:0]'}
|
||||
BLK_VERSION_MINOR : {show: y, blk : 1, word: 3, pos: 27, len : 3, start: 123, type : 'uint:3', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: BLK_VERSION_MINOR of BLOCK2, rloc: 'EFUSE_RD_MAC_SPI_SYS_3_REG[29:27]', bloc: 'B15[5:3]'}
|
||||
BLK_VERSION_MAJOR : {show: y, blk : 1, word: 3, pos: 30, len : 2, start: 126, type : 'uint:2', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: BLK_VERSION_MAJOR of BLOCK2, rloc: 'EFUSE_RD_MAC_SPI_SYS_3_REG[31:30]', bloc: 'B15[7:6]'}
|
||||
FLASH_CAP : {show: y, blk : 1, word: 4, pos : 0, len : 3, start: 128, type : 'uint:3', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: '', rloc: 'EFUSE_RD_MAC_SPI_SYS_4_REG[2:0]', bloc: 'B16[2:0]'}
|
||||
FLASH_TEMP : {show: y, blk : 1, word: 4, pos : 3, len : 2, start: 131, type : 'uint:2', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: '', rloc: 'EFUSE_RD_MAC_SPI_SYS_4_REG[4:3]', bloc: 'B16[4:3]'}
|
||||
FLASH_VENDOR : {show: y, blk : 1, word: 4, pos : 5, len : 3, start: 133, type : 'uint:3', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: '', rloc: 'EFUSE_RD_MAC_SPI_SYS_4_REG[7:5]', bloc: 'B16[7:5]'}
|
||||
RESERVED_1_136 : {show: n, blk : 1, word: 4, pos : 8, len : 24, start: 136, type : 'uint:24', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: reserved, rloc: 'EFUSE_RD_MAC_SPI_SYS_4_REG[31:8]', bloc: 'B17,B18,B19'}
|
||||
SYS_DATA_PART0_2 : {show: n, blk : 1, word: 5, pos : 0, len : 32, start: 160, type : 'uint:32', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: Stores the second 32 bits of the zeroth part of system data, rloc: EFUSE_RD_MAC_SPI_SYS_5_REG, bloc: 'B20,B21,B22,B23'}
|
||||
OPTIONAL_UNIQUE_ID : {show: y, blk : 2, word: 0, pos : 0, len: 128, start : 0, type: 'bytes:16', wr_dis : 21, rd_dis: null, alt : '', dict : '', desc: Optional unique 128-bit ID, rloc: EFUSE_RD_SYS_PART1_DATA0_REG, bloc: 'B0,B1,B2,B3,B4,B5,B6,B7,B8,B9,B10,B11,B12,B13,B14,B15'}
|
||||
TEMP_CALIB : {show: y, blk : 2, word: 4, pos : 0, len : 9, start: 128, type : 'uint:9', wr_dis : 21, rd_dis: null, alt : '', dict : '', desc: Temperature calibration data, rloc: 'EFUSE_RD_SYS_PART1_DATA4_REG[8:0]', bloc: 'B16,B17[0]'}
|
||||
OCODE : {show: y, blk : 2, word: 4, pos : 9, len : 8, start: 137, type : 'uint:8', wr_dis : 21, rd_dis: null, alt : '', dict : '', desc: ADC OCode, rloc: 'EFUSE_RD_SYS_PART1_DATA4_REG[16:9]', bloc: 'B17[7:1],B18[0]'}
|
||||
ADC1_INIT_CODE_ATTEN0 : {show: y, blk : 2, word: 4, pos: 17, len : 10, start: 145, type : 'uint:10', wr_dis : 21, rd_dis: null, alt : '', dict : '', desc: ADC1 init code at atten0, rloc: 'EFUSE_RD_SYS_PART1_DATA4_REG[26:17]', bloc: 'B18[7:1],B19[2:0]'}
|
||||
ADC1_INIT_CODE_ATTEN1 : {show: y, blk : 2, word: 4, pos: 27, len : 10, start: 155, type : 'uint:10', wr_dis : 21, rd_dis: null, alt : '', dict : '', desc: ADC1 init code at atten1, rloc: 'EFUSE_RD_SYS_PART1_DATA4_REG[31:27]', bloc: 'B19[7:3],B20[4:0]'}
|
||||
ADC1_INIT_CODE_ATTEN2 : {show: y, blk : 2, word: 5, pos : 5, len : 10, start: 165, type : 'uint:10', wr_dis : 21, rd_dis: null, alt : '', dict : '', desc: ADC1 init code at atten2, rloc: 'EFUSE_RD_SYS_PART1_DATA5_REG[14:5]', bloc: 'B20[7:5],B21[6:0]'}
|
||||
ADC1_INIT_CODE_ATTEN3 : {show: y, blk : 2, word: 5, pos: 15, len : 10, start: 175, type : 'uint:10', wr_dis : 21, rd_dis: null, alt : '', dict : '', desc: ADC1 init code at atten3, rloc: 'EFUSE_RD_SYS_PART1_DATA5_REG[24:15]', bloc: 'B21[7],B22,B23[0]'}
|
||||
ADC1_CAL_VOL_ATTEN0 : {show: y, blk : 2, word: 5, pos: 25, len : 10, start: 185, type : 'uint:10', wr_dis : 21, rd_dis: null, alt : '', dict : '', desc: ADC1 calibration voltage at atten0, rloc: 'EFUSE_RD_SYS_PART1_DATA5_REG[31:25]', bloc: 'B23[7:1],B24[2:0]'}
|
||||
ADC1_CAL_VOL_ATTEN1 : {show: y, blk : 2, word: 6, pos : 3, len : 10, start: 195, type : 'uint:10', wr_dis : 21, rd_dis: null, alt : '', dict : '', desc: ADC1 calibration voltage at atten1, rloc: 'EFUSE_RD_SYS_PART1_DATA6_REG[12:3]', bloc: 'B24[7:3],B25[4:0]'}
|
||||
ADC1_CAL_VOL_ATTEN2 : {show: y, blk : 2, word: 6, pos: 13, len : 10, start: 205, type : 'uint:10', wr_dis : 21, rd_dis: null, alt : '', dict : '', desc: ADC1 calibration voltage at atten2, rloc: 'EFUSE_RD_SYS_PART1_DATA6_REG[22:13]', bloc: 'B25[7:5],B26[6:0]'}
|
||||
ADC1_CAL_VOL_ATTEN3 : {show: y, blk : 2, word: 6, pos: 23, len : 10, start: 215, type : 'uint:10', wr_dis : 21, rd_dis: null, alt : '', dict : '', desc: ADC1 calibration voltage at atten3, rloc: 'EFUSE_RD_SYS_PART1_DATA6_REG[31:23]', bloc: 'B26[7],B27,B28[0]'}
|
||||
ADC1_INIT_CODE_ATTEN0_CH0 : {show: y, blk : 2, word: 7, pos : 1, len : 4, start: 225, type : 'uint:4', wr_dis : 21, rd_dis: null, alt : '', dict : '', desc: ADC1 init code at atten0 ch0, rloc: 'EFUSE_RD_SYS_PART1_DATA7_REG[4:1]', bloc: 'B28[4:1]'}
|
||||
ADC1_INIT_CODE_ATTEN0_CH1 : {show: y, blk : 2, word: 7, pos : 5, len : 4, start: 229, type : 'uint:4', wr_dis : 21, rd_dis: null, alt : '', dict : '', desc: ADC1 init code at atten0 ch1, rloc: 'EFUSE_RD_SYS_PART1_DATA7_REG[8:5]', bloc: 'B28[7:5],B29[0]'}
|
||||
ADC1_INIT_CODE_ATTEN0_CH2 : {show: y, blk : 2, word: 7, pos : 9, len : 4, start: 233, type : 'uint:4', wr_dis : 21, rd_dis: null, alt : '', dict : '', desc: ADC1 init code at atten0 ch2, rloc: 'EFUSE_RD_SYS_PART1_DATA7_REG[12:9]', bloc: 'B29[4:1]'}
|
||||
ADC1_INIT_CODE_ATTEN0_CH3 : {show: y, blk : 2, word: 7, pos: 13, len : 4, start: 237, type : 'uint:4', wr_dis : 21, rd_dis: null, alt : '', dict : '', desc: ADC1 init code at atten0 ch3, rloc: 'EFUSE_RD_SYS_PART1_DATA7_REG[16:13]', bloc: 'B29[7:5],B30[0]'}
|
||||
ADC1_INIT_CODE_ATTEN0_CH4 : {show: y, blk : 2, word: 7, pos: 17, len : 4, start: 241, type : 'uint:4', wr_dis : 21, rd_dis: null, alt : '', dict : '', desc: ADC1 init code at atten0 ch4, rloc: 'EFUSE_RD_SYS_PART1_DATA7_REG[20:17]', bloc: 'B30[4:1]'}
|
||||
ADC1_INIT_CODE_ATTEN0_CH5 : {show: y, blk : 2, word: 7, pos: 21, len : 4, start: 245, type : 'uint:4', wr_dis : 21, rd_dis: null, alt : '', dict : '', desc: ADC1 init code at atten0 ch5, rloc: 'EFUSE_RD_SYS_PART1_DATA7_REG[24:21]', bloc: 'B30[7:5],B31[0]'}
|
||||
ADC1_INIT_CODE_ATTEN0_CH6 : {show: y, blk : 2, word: 7, pos: 25, len : 4, start: 249, type : 'uint:4', wr_dis : 21, rd_dis: null, alt : '', dict : '', desc: ADC1 init code at atten0 ch6, rloc: 'EFUSE_RD_SYS_PART1_DATA7_REG[28:25]', bloc: 'B31[4:1]'}
|
||||
RESERVED_2_253 : {show: n, blk : 2, word: 7, pos: 29, len : 3, start: 253, type : 'uint:3', wr_dis : 21, rd_dis: null, alt : '', dict : '', desc: reserved, rloc: 'EFUSE_RD_SYS_PART1_DATA7_REG[31:29]', bloc: 'B31[7:5]'}
|
||||
BLOCK_USR_DATA : {show: y, blk : 3, word: 0, pos : 0, len: 192, start : 0, type: 'bytes:24', wr_dis : 22, rd_dis: null, alt : USER_DATA, dict : '', desc: User data, rloc: EFUSE_RD_USR_DATA0_REG, bloc: 'B0,B1,B2,B3,B4,B5,B6,B7,B8,B9,B10,B11,B12,B13,B14,B15,B16,B17,B18,B19,B20,B21,B22,B23'}
|
||||
RESERVED_3_192 : {show: n, blk : 3, word: 6, pos : 0, len : 8, start: 192, type : 'uint:8', wr_dis : 22, rd_dis: null, alt : '', dict : '', desc: reserved, rloc: 'EFUSE_RD_USR_DATA6_REG[7:0]', bloc: B24}
|
||||
CUSTOM_MAC : {show: y, blk : 3, word: 6, pos : 8, len : 48, start: 200, type : 'bytes:6', wr_dis : 22, rd_dis: null, alt: MAC_CUSTOM USER_DATA_MAC_CUSTOM, dict : '', desc: Custom MAC, rloc: 'EFUSE_RD_USR_DATA6_REG[31:8]', bloc: 'B25,B26,B27,B28,B29,B30'}
|
||||
RESERVED_3_248 : {show: n, blk : 3, word: 7, pos: 24, len : 8, start: 248, type : 'uint:8', wr_dis : 22, rd_dis: null, alt : '', dict : '', desc: reserved, rloc: 'EFUSE_RD_USR_DATA7_REG[31:24]', bloc: B31}
|
||||
BLOCK_KEY0 : {show: y, blk : 4, word: 0, pos : 0, len: 256, start : 0, type: 'bytes:32', wr_dis : 23, rd_dis : 0, alt : KEY0, dict : '', desc: Key0 or user data, rloc: EFUSE_RD_KEY0_DATA0_REG, bloc: 'B0,B1,B2,B3,B4,B5,B6,B7,B8,B9,B10,B11,B12,B13,B14,B15,B16,B17,B18,B19,B20,B21,B22,B23,B24,B25,B26,B27,B28,B29,B30,B31'}
|
||||
BLOCK_KEY1 : {show: y, blk : 5, word: 0, pos : 0, len: 256, start : 0, type: 'bytes:32', wr_dis : 24, rd_dis : 1, alt : KEY1, dict : '', desc: Key1 or user data, rloc: EFUSE_RD_KEY1_DATA0_REG, bloc: 'B0,B1,B2,B3,B4,B5,B6,B7,B8,B9,B10,B11,B12,B13,B14,B15,B16,B17,B18,B19,B20,B21,B22,B23,B24,B25,B26,B27,B28,B29,B30,B31'}
|
||||
BLOCK_KEY2 : {show: y, blk : 6, word: 0, pos : 0, len: 256, start : 0, type: 'bytes:32', wr_dis : 25, rd_dis : 2, alt : KEY2, dict : '', desc: Key2 or user data, rloc: EFUSE_RD_KEY2_DATA0_REG, bloc: 'B0,B1,B2,B3,B4,B5,B6,B7,B8,B9,B10,B11,B12,B13,B14,B15,B16,B17,B18,B19,B20,B21,B22,B23,B24,B25,B26,B27,B28,B29,B30,B31'}
|
||||
BLOCK_KEY3 : {show: y, blk : 7, word: 0, pos : 0, len: 256, start : 0, type: 'bytes:32', wr_dis : 26, rd_dis : 3, alt : KEY3, dict : '', desc: Key3 or user data, rloc: EFUSE_RD_KEY3_DATA0_REG, bloc: 'B0,B1,B2,B3,B4,B5,B6,B7,B8,B9,B10,B11,B12,B13,B14,B15,B16,B17,B18,B19,B20,B21,B22,B23,B24,B25,B26,B27,B28,B29,B30,B31'}
|
||||
BLOCK_KEY4 : {show: y, blk : 8, word: 0, pos : 0, len: 256, start : 0, type: 'bytes:32', wr_dis : 27, rd_dis : 4, alt : KEY4, dict : '', desc: Key4 or user data, rloc: EFUSE_RD_KEY4_DATA0_REG, bloc: 'B0,B1,B2,B3,B4,B5,B6,B7,B8,B9,B10,B11,B12,B13,B14,B15,B16,B17,B18,B19,B20,B21,B22,B23,B24,B25,B26,B27,B28,B29,B30,B31'}
|
||||
BLOCK_KEY5 : {show: y, blk : 9, word: 0, pos : 0, len: 256, start : 0, type: 'bytes:32', wr_dis : 28, rd_dis : 5, alt : KEY5, dict : '', desc: Key5 or user data, rloc: EFUSE_RD_KEY5_DATA0_REG, bloc: 'B0,B1,B2,B3,B4,B5,B6,B7,B8,B9,B10,B11,B12,B13,B14,B15,B16,B17,B18,B19,B20,B21,B22,B23,B24,B25,B26,B27,B28,B29,B30,B31'}
|
||||
BLOCK_SYS_DATA2 : {show: y, blk: 10, word: 0, pos : 0, len: 256, start : 0, type: 'bytes:32', wr_dis : 29, rd_dis : 6, alt : SYS_DATA_PART2, dict : '', desc: System data part 2 (reserved), rloc: EFUSE_RD_SYS_PART2_DATA0_REG, bloc: 'B0,B1,B2,B3,B4,B5,B6,B7,B8,B9,B10,B11,B12,B13,B14,B15,B16,B17,B18,B19,B20,B21,B22,B23,B24,B25,B26,B27,B28,B29,B30,B31'}
|
@@ -1,4 +1,4 @@
|
||||
# SPDX-FileCopyrightText: 2014-2022 Fredrik Ahlberg, Angus Gratton,
|
||||
# SPDX-FileCopyrightText: 2014-2025 Fredrik Ahlberg, Angus Gratton,
|
||||
# Espressif Systems (Shanghai) CO LTD, other contributors as noted.
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
@@ -852,7 +852,10 @@ def main(argv=None, esp=None):
|
||||
"setting --no-stub"
|
||||
)
|
||||
args.no_stub = True
|
||||
elif esp.CHIP_NAME == "ESP32-H21": # TODO: [ESP32H21] IDF-11509
|
||||
elif esp.CHIP_NAME in [
|
||||
"ESP32-H21",
|
||||
"ESP32-H4",
|
||||
]: # TODO: [ESP32H21] IDF-11509 [ESP32H4] IDF-12271
|
||||
print(
|
||||
f"WARNING: Stub loader is not yet supported on {esp.CHIP_NAME}, "
|
||||
"setting --no-stub"
|
||||
|
@@ -28,6 +28,7 @@ from .targets import (
|
||||
ESP32H2BETA2ROM,
|
||||
ESP32H2ROM,
|
||||
ESP32H21ROM,
|
||||
ESP32H4ROM,
|
||||
ESP32P4ROM,
|
||||
ESP32ROM,
|
||||
ESP32S2ROM,
|
||||
@@ -96,6 +97,7 @@ def LoadFirmwareImage(chip, image_file):
|
||||
"esp32h2": ESP32H2FirmwareImage,
|
||||
"esp32h21": ESP32H21FirmwareImage,
|
||||
"esp32p4": ESP32P4FirmwareImage,
|
||||
"esp32h4": ESP32H4FirmwareImage,
|
||||
}[chip](f)
|
||||
else: # Otherwise, ESP8266 so look at magic to determine the image type
|
||||
magic = ord(f.read(1))
|
||||
@@ -1180,6 +1182,13 @@ class ESP32C5BETA3FirmwareImage(ESP32C6FirmwareImage):
|
||||
|
||||
|
||||
ESP32C5BETA3ROM.BOOTLOADER_IMAGE = ESP32C5BETA3FirmwareImage
|
||||
class ESP32H4FirmwareImage(ESP32FirmwareImage):
|
||||
"""ESP32H4 Firmware Image almost exactly the same as ESP32FirmwareImage"""
|
||||
|
||||
ROM_LOADER = ESP32H4ROM
|
||||
|
||||
|
||||
ESP32H4ROM.BOOTLOADER_IMAGE = ESP32H4FirmwareImage
|
||||
|
||||
|
||||
class ESP32P4FirmwareImage(ESP32FirmwareImage):
|
||||
|
@@ -10,6 +10,7 @@ from .esp32h2 import ESP32H2ROM
|
||||
from .esp32h21 import ESP32H21ROM
|
||||
from .esp32h2beta1 import ESP32H2BETA1ROM
|
||||
from .esp32h2beta2 import ESP32H2BETA2ROM
|
||||
from .esp32h4 import ESP32H4ROM
|
||||
from .esp32p4 import ESP32P4ROM
|
||||
from .esp32s2 import ESP32S2ROM
|
||||
from .esp32s3 import ESP32S3ROM
|
||||
@@ -35,6 +36,7 @@ CHIP_DEFS = {
|
||||
"esp32h2": ESP32H2ROM,
|
||||
"esp32h21": ESP32H21ROM,
|
||||
"esp32p4": ESP32P4ROM,
|
||||
"esp32h4": ESP32H4ROM,
|
||||
}
|
||||
|
||||
CHIP_LIST = list(CHIP_DEFS.keys())
|
||||
|
213
esptool/targets/esp32h4.py
Normal file
213
esptool/targets/esp32h4.py
Normal file
@@ -0,0 +1,213 @@
|
||||
# SPDX-FileCopyrightText: 2025 Fredrik Ahlberg, Angus Gratton,
|
||||
# Espressif Systems (Shanghai) CO LTD, other contributors as noted.
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
import struct
|
||||
|
||||
from .esp32c3 import ESP32C3ROM
|
||||
from ..loader import ESPLoader
|
||||
from ..util import FatalError
|
||||
|
||||
|
||||
class ESP32H4ROM(ESP32C3ROM):
|
||||
CHIP_NAME = "ESP32-H4"
|
||||
IMAGE_CHIP_ID = 28
|
||||
|
||||
IROM_MAP_START = 0x42000000
|
||||
IROM_MAP_END = 0x42800000
|
||||
DROM_MAP_START = 0x42800000
|
||||
DROM_MAP_END = 0x43000000
|
||||
|
||||
BOOTLOADER_FLASH_OFFSET = 0x2000
|
||||
|
||||
SPI_REG_BASE = 0x60099000
|
||||
SPI_USR_OFFS = 0x18
|
||||
SPI_USR1_OFFS = 0x1C
|
||||
SPI_USR2_OFFS = 0x20
|
||||
SPI_MOSI_DLEN_OFFS = 0x24
|
||||
SPI_MISO_DLEN_OFFS = 0x28
|
||||
SPI_W0_OFFS = 0x58
|
||||
|
||||
UART_DATE_REG_ADDR = 0x60012000 + 0x7C
|
||||
|
||||
EFUSE_BASE = 0x600B1800
|
||||
EFUSE_BLOCK1_ADDR = EFUSE_BASE + 0x044
|
||||
MAC_EFUSE_REG = EFUSE_BASE + 0x044
|
||||
|
||||
EFUSE_RD_REG_BASE = EFUSE_BASE + 0x030 # BLOCK0 read base address
|
||||
|
||||
EFUSE_PURPOSE_KEY0_REG = EFUSE_BASE + 0x34
|
||||
EFUSE_PURPOSE_KEY0_SHIFT = 0
|
||||
EFUSE_PURPOSE_KEY1_REG = EFUSE_BASE + 0x34
|
||||
EFUSE_PURPOSE_KEY1_SHIFT = 5
|
||||
EFUSE_PURPOSE_KEY2_REG = EFUSE_BASE + 0x34
|
||||
EFUSE_PURPOSE_KEY2_SHIFT = 10
|
||||
EFUSE_PURPOSE_KEY3_REG = EFUSE_BASE + 0x34
|
||||
EFUSE_PURPOSE_KEY3_SHIFT = 15
|
||||
EFUSE_PURPOSE_KEY4_REG = EFUSE_BASE + 0x34
|
||||
EFUSE_PURPOSE_KEY4_SHIFT = 20
|
||||
EFUSE_PURPOSE_KEY5_REG = EFUSE_BASE + 0x34
|
||||
EFUSE_PURPOSE_KEY5_SHIFT = 25
|
||||
|
||||
EFUSE_DIS_DOWNLOAD_MANUAL_ENCRYPT_REG = EFUSE_RD_REG_BASE
|
||||
EFUSE_DIS_DOWNLOAD_MANUAL_ENCRYPT = 1 << 14
|
||||
|
||||
EFUSE_SPI_BOOT_CRYPT_CNT_REG = EFUSE_BASE + 0x030
|
||||
EFUSE_SPI_BOOT_CRYPT_CNT_MASK = 0x7 << 23
|
||||
|
||||
EFUSE_SECURE_BOOT_EN_REG = EFUSE_BASE + 0x038
|
||||
EFUSE_SECURE_BOOT_EN_MASK = 1 << 5
|
||||
|
||||
EFUSE_FORCE_USE_KM_KEY_REG = EFUSE_BASE + 0x038
|
||||
EFUSE_FORCE_USE_KM_KEY_MASK = 0xF << 19
|
||||
|
||||
PURPOSE_VAL_XTS_AES128_KEY = 4
|
||||
|
||||
SUPPORTS_ENCRYPTED_FLASH = True
|
||||
|
||||
FLASH_ENCRYPTED_WRITE_ALIGN = 16
|
||||
|
||||
UARTDEV_BUF_NO = 0x4087F580 # Variable in ROM .bss which indicates the port in use
|
||||
UARTDEV_BUF_NO_USB_JTAG_SERIAL = 3 # The above var when USB-JTAG/Serial is used
|
||||
|
||||
DR_REG_LP_WDT_BASE = 0x600B5400
|
||||
RTC_CNTL_WDTCONFIG0_REG = DR_REG_LP_WDT_BASE + 0x0 # LP_WDT_RWDT_CONFIG0_REG
|
||||
RTC_CNTL_WDTWPROTECT_REG = DR_REG_LP_WDT_BASE + 0x0018 # LP_WDT_RWDT_WPROTECT_REG
|
||||
|
||||
RTC_CNTL_SWD_CONF_REG = DR_REG_LP_WDT_BASE + 0x001C # LP_WDT_SWD_CONFIG_REG
|
||||
RTC_CNTL_SWD_AUTO_FEED_EN = 1 << 18
|
||||
RTC_CNTL_SWD_WPROTECT_REG = DR_REG_LP_WDT_BASE + 0x0020 # LP_WDT_SWD_WPROTECT_REG
|
||||
RTC_CNTL_SWD_WKEY = 0x50D83AA1 # LP_WDT_SWD_WKEY, same as WDT key in this case
|
||||
|
||||
PCR_SYSCLK_CONF_REG = 0x60096110
|
||||
PCR_SYSCLK_XTAL_FREQ_V = 0x7F << 24
|
||||
PCR_SYSCLK_XTAL_FREQ_S = 24
|
||||
|
||||
FLASH_FREQUENCY = {
|
||||
"48m": 0x0,
|
||||
"24m": 0x0,
|
||||
"16m": 0x1,
|
||||
"12m": 0x2,
|
||||
}
|
||||
|
||||
MEMORY_MAP = [
|
||||
[0x00000000, 0x00010000, "PADDING"],
|
||||
[0x42800000, 0x43000000, "DROM"],
|
||||
[0x40800000, 0x40880000, "DRAM"],
|
||||
[0x40800000, 0x40880000, "BYTE_ACCESSIBLE"],
|
||||
[0x4004AC00, 0x40050000, "DROM_MASK"],
|
||||
[0x40000000, 0x4004AC00, "IROM_MASK"],
|
||||
[0x42000000, 0x42800000, "IROM"],
|
||||
[0x40800000, 0x40880000, "IRAM"],
|
||||
[0x50000000, 0x50004000, "RTC_IRAM"],
|
||||
[0x50000000, 0x50004000, "RTC_DRAM"],
|
||||
[0x600FE000, 0x60100000, "MEM_INTERNAL2"],
|
||||
]
|
||||
|
||||
UF2_FAMILY_ID = 0x9E0BAA8A
|
||||
|
||||
# not alloc yet, return 0
|
||||
def get_pkg_version(self):
|
||||
return 0
|
||||
|
||||
def get_minor_chip_version(self):
|
||||
return 0
|
||||
|
||||
def get_major_chip_version(self):
|
||||
return 0
|
||||
|
||||
def get_chip_description(self):
|
||||
chip_name = {
|
||||
0: "ESP32-H4 (QFN40)",
|
||||
}.get(self.get_pkg_version(), "unknown ESP32-H4")
|
||||
major_rev = self.get_major_chip_version()
|
||||
minor_rev = self.get_minor_chip_version()
|
||||
return f"{chip_name} (revision v{major_rev}.{minor_rev})"
|
||||
|
||||
def get_chip_features(self):
|
||||
return ["WiFi 6", "BT 5", "IEEE802.15.4"]
|
||||
|
||||
def get_crystal_freq(self):
|
||||
# ESP32H4 XTAL is fixed to 32MHz
|
||||
return 32
|
||||
|
||||
def change_baud(self, baud):
|
||||
ESPLoader.change_baud(self, baud)
|
||||
|
||||
def read_mac(self, mac_type="BASE_MAC"):
|
||||
"""Read MAC from EFUSE region"""
|
||||
mac0 = self.read_reg(self.MAC_EFUSE_REG)
|
||||
mac1 = self.read_reg(self.MAC_EFUSE_REG + 4) # only bottom 16 bits are MAC
|
||||
base_mac = struct.pack(">II", mac1, mac0)[2:]
|
||||
ext_mac = struct.pack(">H", (mac1 >> 16) & 0xFFFF)
|
||||
eui64 = base_mac[0:3] + ext_mac + base_mac[3:6]
|
||||
# BASE MAC: 60:55:f9:f7:2c:a2
|
||||
# EUI64 MAC: 60:55:f9:ff:fe:f7:2c:a2
|
||||
# EXT_MAC: ff:fe
|
||||
macs = {
|
||||
"BASE_MAC": tuple(base_mac),
|
||||
"EUI64": tuple(eui64),
|
||||
"MAC_EXT": tuple(ext_mac),
|
||||
}
|
||||
return macs.get(mac_type, None)
|
||||
|
||||
def get_flash_crypt_config(self):
|
||||
return None # doesn't exist on ESP32-H4
|
||||
|
||||
def get_secure_boot_enabled(self):
|
||||
return (
|
||||
self.read_reg(self.EFUSE_SECURE_BOOT_EN_REG)
|
||||
& self.EFUSE_SECURE_BOOT_EN_MASK
|
||||
)
|
||||
|
||||
def get_key_block_purpose(self, key_block):
|
||||
if key_block < 0 or key_block > 5:
|
||||
raise FatalError("Valid key block numbers must be in range 0-5")
|
||||
|
||||
reg, shift = [
|
||||
(self.EFUSE_PURPOSE_KEY0_REG, self.EFUSE_PURPOSE_KEY0_SHIFT),
|
||||
(self.EFUSE_PURPOSE_KEY1_REG, self.EFUSE_PURPOSE_KEY1_SHIFT),
|
||||
(self.EFUSE_PURPOSE_KEY2_REG, self.EFUSE_PURPOSE_KEY2_SHIFT),
|
||||
(self.EFUSE_PURPOSE_KEY3_REG, self.EFUSE_PURPOSE_KEY3_SHIFT),
|
||||
(self.EFUSE_PURPOSE_KEY4_REG, self.EFUSE_PURPOSE_KEY4_SHIFT),
|
||||
(self.EFUSE_PURPOSE_KEY5_REG, self.EFUSE_PURPOSE_KEY5_SHIFT),
|
||||
][key_block]
|
||||
return (self.read_reg(reg) >> shift) & 0x1F
|
||||
|
||||
def is_flash_encryption_key_valid(self):
|
||||
# Need to see an AES-128 key
|
||||
purposes = [self.get_key_block_purpose(b) for b in range(6)]
|
||||
|
||||
return any(p == self.PURPOSE_VAL_XTS_AES128_KEY for p in purposes)
|
||||
|
||||
def check_spi_connection(self, spi_connection):
|
||||
if not set(spi_connection).issubset(set(range(0, 40))):
|
||||
raise FatalError("SPI Pin numbers must be in the range 0-39.")
|
||||
if any([v for v in spi_connection if v in [13, 14]]):
|
||||
print(
|
||||
"WARNING: GPIO pins 13 and 14 are used by USB-Serial/JTAG, "
|
||||
"consider using other pins for SPI flash connection."
|
||||
)
|
||||
|
||||
|
||||
class ESP32H4StubLoader(ESP32H4ROM):
|
||||
"""Access class for ESP32H4 stub loader, runs on top of ROM.
|
||||
|
||||
(Basically the same as ESP32StubLoader, but different base class.
|
||||
Can possibly be made into a mixin.)
|
||||
"""
|
||||
|
||||
FLASH_WRITE_SIZE = 0x4000 # matches MAX_WRITE_BLOCK in stub_loader.c
|
||||
STATUS_BYTES_LENGTH = 2 # same as ESP8266, different to ESP32 ROM
|
||||
IS_STUB = True
|
||||
|
||||
def __init__(self, rom_loader):
|
||||
self.secure_download_mode = rom_loader.secure_download_mode
|
||||
self._port = rom_loader._port
|
||||
self._trace_enabled = rom_loader._trace_enabled
|
||||
self.cache = rom_loader.cache
|
||||
self.flush_input() # resets _slip_reader
|
||||
|
||||
|
||||
ESP32H4ROM.STUB_CLASS = ESP32H4StubLoader
|
@@ -252,7 +252,8 @@ class TestReadCommands(EfuseTestCase):
|
||||
|
||||
|
||||
# TODO: [ESP32H21] IDF-11506
|
||||
@pytest.mark.skipif(arg_chip == "esp32h21", reason="Not supported yet")
|
||||
# TODO: [ESP32H4] IDF-12268
|
||||
@pytest.mark.skipif(arg_chip in ["esp32h21", "esp32h4"], reason="Not supported yet")
|
||||
class TestReadProtectionCommands(EfuseTestCase):
|
||||
def test_read_protect_efuse(self):
|
||||
self.espefuse_py("read_protect_efuse -h")
|
||||
@@ -394,7 +395,8 @@ class TestReadProtectionCommands(EfuseTestCase):
|
||||
|
||||
|
||||
# TODO: [ESP32H21] IDF-11506
|
||||
@pytest.mark.skipif(arg_chip == "esp32h21", reason="Not supported yet")
|
||||
# TODO: [ESP32H4] IDF-12268
|
||||
@pytest.mark.skipif(arg_chip in ["esp32h21", "esp32h4"], reason="Not supported yet")
|
||||
class TestWriteProtectionCommands(EfuseTestCase):
|
||||
def test_write_protect_efuse(self):
|
||||
self.espefuse_py("write_protect_efuse -h")
|
||||
@@ -455,6 +457,9 @@ class TestWriteProtectionCommands(EfuseTestCase):
|
||||
|
||||
|
||||
@pytest.mark.skipif(arg_chip == "esp32p4", reason="No Custom MAC Address defined yet")
|
||||
@pytest.mark.skipif(
|
||||
arg_chip == "esp32h4", reason="Not supported yet"
|
||||
) # TODO: [ESP32H4] IDF-12268
|
||||
class TestBurnCustomMacCommands(EfuseTestCase):
|
||||
def test_burn_custom_mac(self):
|
||||
self.espefuse_py("burn_custom_mac -h")
|
||||
@@ -675,6 +680,9 @@ class TestValueArgForBurnEfuseCommands(EfuseTestCase):
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.skipif(
|
||||
arg_chip == "esp32h4", reason="Not supported yet"
|
||||
) # TODO: [ESP32H4] IDF-12268
|
||||
class TestBurnEfuseCommands(EfuseTestCase):
|
||||
@pytest.mark.skipif(
|
||||
arg_chip != "esp32",
|
||||
@@ -723,11 +731,9 @@ class TestBurnEfuseCommands(EfuseTestCase):
|
||||
self.espefuse_py("get_custom_mac", check_msg=f"aa:cd:ef:01:02:03 {crc_msg}")
|
||||
|
||||
# TODO: [ESP32H21] IDF-11506
|
||||
# TODO: [ESP32H4] IDF-12268
|
||||
@pytest.mark.skipif(
|
||||
arg_chip
|
||||
in [
|
||||
"esp32h21",
|
||||
],
|
||||
arg_chip in ["esp32h21", "esp32h4"],
|
||||
reason="No such eFuses, will be defined later",
|
||||
)
|
||||
def test_burn_efuse(self):
|
||||
@@ -1876,7 +1882,8 @@ class TestByteOrderBurnKeyCommand(EfuseTestCase):
|
||||
|
||||
|
||||
# TODO: [ESP32H21] IDF-11506
|
||||
@pytest.mark.skipif(arg_chip == "esp32h21", reason="Not supported yet")
|
||||
# TODO: [ESP32H4] IDF-12268
|
||||
@pytest.mark.skipif(arg_chip in ["esp32h21", "esp32h4"], reason="Not supported yet")
|
||||
class TestExecuteScriptsCommands(EfuseTestCase):
|
||||
@classmethod
|
||||
def setup_class(self):
|
||||
@@ -1959,6 +1966,9 @@ class TestExecuteScriptsCommands(EfuseTestCase):
|
||||
) in output
|
||||
|
||||
|
||||
@pytest.mark.skipif(
|
||||
arg_chip == "esp32h4", reason="Not supported yet"
|
||||
) # TODO: [ESP32H4] IDF-12268
|
||||
class TestMultipleCommands(EfuseTestCase):
|
||||
def test_multiple_cmds_help(self):
|
||||
if arg_chip == "esp32c2":
|
||||
@@ -2104,6 +2114,9 @@ class TestKeyPurposes(EfuseTestCase):
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.skipif(
|
||||
arg_chip == "esp32h4", reason="Not supported yet"
|
||||
) # TODO: [ESP32H4] IDF-12268
|
||||
class TestPostponedEfuses(EfuseTestCase):
|
||||
def test_postpone_efuses(self):
|
||||
if arg_chip == "esp32":
|
||||
@@ -2130,6 +2143,9 @@ class TestPostponedEfuses(EfuseTestCase):
|
||||
assert "Successful" in output
|
||||
|
||||
|
||||
@pytest.mark.skipif(
|
||||
arg_chip == "esp32h4", reason="Not supported yet"
|
||||
) # TODO: [ESP32H4] IDF-12268
|
||||
class TestCSVEfuseTable(EfuseTestCase):
|
||||
def test_extend_efuse_table_with_csv_file(self):
|
||||
csv_file = f"{IMAGES_DIR}/esp_efuse_custom_table.csv"
|
||||
|
Reference in New Issue
Block a user