mirror of
https://github.com/espressif/esptool.git
synced 2025-10-16 23:06:31 +08:00
feat(espefuse): Postpone some efuses to burn them at the very end
This commit is contained in:

committed by
Radim Karniš

parent
d4488513e9
commit
bdeec68cb6
@@ -214,6 +214,12 @@ def main(custom_commandline=None, esp=None):
|
||||
"Use with caution.",
|
||||
action="store_true",
|
||||
)
|
||||
init_parser.add_argument(
|
||||
"--postpone",
|
||||
help="Postpone burning some efuses from BLOCK0 at the end, "
|
||||
"(efuses which disable access to blocks or chip).",
|
||||
action="store_true",
|
||||
)
|
||||
|
||||
common_args, remaining_args = init_parser.parse_known_args(custom_commandline)
|
||||
debug_mode = common_args.debug or ("dump" in remaining_args)
|
||||
@@ -263,6 +269,8 @@ def main(custom_commandline=None, esp=None):
|
||||
if there_are_multiple_burn_commands_in_args:
|
||||
efuses.batch_mode_cnt += 1
|
||||
|
||||
efuses.postpone = common_args.postpone
|
||||
|
||||
try:
|
||||
for rem_args in grouped_remaining_args:
|
||||
args, unused_args = parser.parse_known_args(rem_args, namespace=common_args)
|
||||
@@ -289,6 +297,7 @@ def main(custom_commandline=None, esp=None):
|
||||
efuses.batch_mode_cnt -= 1
|
||||
if not efuses.burn_all(check_batch_mode=True):
|
||||
raise esptool.FatalError("BURN was not done")
|
||||
print("Successful")
|
||||
finally:
|
||||
if not external_esp and not common_args.virt and esp._port:
|
||||
esp._port.close()
|
||||
|
@@ -445,6 +445,7 @@ class EspEfusesBase(object):
|
||||
coding_scheme = None
|
||||
force_write_always = None
|
||||
batch_mode_cnt = 0
|
||||
postpone = False
|
||||
|
||||
def __iter__(self):
|
||||
return self.efuses.__iter__()
|
||||
@@ -489,6 +490,59 @@ class EspEfusesBase(object):
|
||||
for efuse in self.efuses:
|
||||
efuse.update(self.blocks[efuse.block].bitarray)
|
||||
|
||||
def postpone_efuses_from_block0_to_burn(self, block):
|
||||
postpone_efuses = {}
|
||||
|
||||
if block.id != 0:
|
||||
return postpone_efuses
|
||||
|
||||
# We need to check this list of efuses. If we are going to burn an efuse
|
||||
# from this list, then we need to split the burn operation into two
|
||||
# steps. The first step involves burning efuses not in this list. In
|
||||
# case of an error during this step, we can recover by burning the
|
||||
# efuses from this list at the very end. This approach provides the
|
||||
# ability to recover efuses if an error occurs during the initial burn
|
||||
# operation.
|
||||
|
||||
# List the efuses here that must be burned at the very end, such as read
|
||||
# and write protection fields, as well as efuses that disable
|
||||
# communication with the espefuse tool.
|
||||
efuses_list = ["WR_DIS", "RD_DIS"]
|
||||
if self._esp.CHIP_NAME == "ESP32":
|
||||
# Efuses below disables communication with the espefuse tool.
|
||||
efuses_list.append("UART_DOWNLOAD_DIS")
|
||||
# other efuses that are better to burn at the very end.
|
||||
efuses_list.append("ABS_DONE_1")
|
||||
efuses_list.append("FLASH_CRYPT_CNT")
|
||||
else:
|
||||
# Efuses below disables communication with the espefuse tool.
|
||||
efuses_list.append("ENABLE_SECURITY_DOWNLOAD")
|
||||
efuses_list.append("DIS_DOWNLOAD_MODE")
|
||||
# other efuses that are better to burn at the very end.
|
||||
efuses_list.append("SPI_BOOT_CRYPT_CNT")
|
||||
efuses_list.append("SECURE_BOOT_EN")
|
||||
|
||||
def get_raw_value_from_write(self, efuse_name):
|
||||
return self[efuse_name].get_bitstring(from_read=False)
|
||||
|
||||
for efuse_name in efuses_list:
|
||||
postpone_efuses[efuse_name] = get_raw_value_from_write(self, efuse_name)
|
||||
|
||||
if any(value != 0 for value in postpone_efuses.values()):
|
||||
if self.debug:
|
||||
print("These BLOCK0 efuses will be burned later at the very end:")
|
||||
print(postpone_efuses)
|
||||
# exclude these efuses from the first burn (postpone them till the end).
|
||||
for key_name in postpone_efuses.keys():
|
||||
self[key_name].reset()
|
||||
return postpone_efuses
|
||||
|
||||
def recover_postponed_efuses_from_block0_to_burn(self, postpone_efuses):
|
||||
if any(value != 0 for value in postpone_efuses.values()):
|
||||
print("Burn postponed efuses from BLOCK0.")
|
||||
for key_name in postpone_efuses.keys():
|
||||
self[key_name].save(postpone_efuses[key_name])
|
||||
|
||||
def burn_all(self, check_batch_mode=False):
|
||||
if check_batch_mode:
|
||||
if self.batch_mode_cnt != 0:
|
||||
@@ -508,18 +562,44 @@ class EspEfusesBase(object):
|
||||
have_wr_data_for_burn = True
|
||||
if not have_wr_data_for_burn:
|
||||
print("Nothing to burn, see messages above.")
|
||||
return
|
||||
return True
|
||||
EspEfusesBase.confirm("", self.do_not_confirm)
|
||||
|
||||
# Burn from BLKn -> BLK0. Because BLK0 can set rd or/and wr protection bits.
|
||||
for block in reversed(self.blocks):
|
||||
def burn_block(block, postponed_efuses):
|
||||
old_fail = block.fail
|
||||
old_num_errors = block.num_errors
|
||||
block.burn()
|
||||
if (block.fail and old_fail != block.fail) or (
|
||||
block.num_errors and block.num_errors > old_num_errors
|
||||
):
|
||||
if postponed_efuses:
|
||||
print("The postponed efuses were not burned due to an error.")
|
||||
print("\t1. Try to fix a coding error by this cmd:")
|
||||
print("\t 'espefuse.py check_error --recovery'")
|
||||
command_string = " ".join(
|
||||
f"{key} {value}"
|
||||
for key, value in postponed_efuses.items()
|
||||
if value.any(True)
|
||||
)
|
||||
print("\t2. Then run the cmd to burn all postponed efuses:")
|
||||
print(f"\t 'espefuse.py burn_efuse {command_string}'")
|
||||
|
||||
raise esptool.FatalError("Error(s) were detected in eFuses")
|
||||
|
||||
# Burn from BLKn -> BLK0. Because BLK0 can set rd or/and wr protection bits.
|
||||
for block in reversed(self.blocks):
|
||||
postponed_efuses = (
|
||||
self.postpone_efuses_from_block0_to_burn(block)
|
||||
if self.postpone
|
||||
else None
|
||||
)
|
||||
|
||||
burn_block(block, postponed_efuses)
|
||||
|
||||
if postponed_efuses:
|
||||
self.recover_postponed_efuses_from_block0_to_burn(postponed_efuses)
|
||||
burn_block(block, postponed_efuses)
|
||||
|
||||
print("Reading updated efuses...")
|
||||
self.read_coding_scheme()
|
||||
self.read_blocks()
|
||||
@@ -628,6 +708,10 @@ class EfuseFieldBase(EfuseProtectBase):
|
||||
|
||||
def check_new_value(self, bitarray_new_value):
|
||||
bitarray_old_value = self.get_bitstring() | self.get_bitstring(from_read=False)
|
||||
|
||||
if not bitarray_new_value.any(True) and not bitarray_old_value.any(True):
|
||||
return
|
||||
|
||||
if bitarray_new_value.len != bitarray_old_value.len:
|
||||
raise esptool.FatalError(
|
||||
"For {} efuse, the length of the new value is wrong, "
|
||||
@@ -635,7 +719,10 @@ class EfuseFieldBase(EfuseProtectBase):
|
||||
self.name, bitarray_old_value.len, bitarray_new_value.len
|
||||
)
|
||||
)
|
||||
if bitarray_new_value == bitarray_old_value:
|
||||
if (
|
||||
bitarray_new_value == bitarray_old_value
|
||||
or bitarray_new_value & self.get_bitstring() == bitarray_new_value
|
||||
):
|
||||
error_msg = "\tThe same value for {} ".format(self.name)
|
||||
error_msg += "is already burned. Do not change the efuse."
|
||||
print(error_msg)
|
||||
@@ -752,3 +839,14 @@ class EfuseFieldBase(EfuseProtectBase):
|
||||
if name is not None:
|
||||
output += f"\n Purpose: {self.parent[name].get()}\n "
|
||||
return output
|
||||
|
||||
def reset(self):
|
||||
# resets a efuse that is prepared for burning
|
||||
bitarray_field = self.convert_to_bitstring(0)
|
||||
block = self.parent.blocks[self.block]
|
||||
wr_bitarray_temp = block.wr_bitarray.copy()
|
||||
position = wr_bitarray_temp.length - (
|
||||
self.word * 32 + self.pos + bitarray_field.len
|
||||
)
|
||||
wr_bitarray_temp.overwrite(bitarray_field, pos=position)
|
||||
block.wr_bitarray = wr_bitarray_temp
|
||||
|
@@ -1977,3 +1977,29 @@ class TestKeyPurposes(EfuseTestCase):
|
||||
"key due to a hardware bug (please see TRM for more details)",
|
||||
ret_code=2,
|
||||
)
|
||||
|
||||
|
||||
class TestPostponedEfuses(EfuseTestCase):
|
||||
def test_postpone_efuses(self):
|
||||
if arg_chip == "esp32":
|
||||
cmd = f"--postpone \
|
||||
burn_efuse UART_DOWNLOAD_DIS 1 \
|
||||
burn_key BLOCK1 {IMAGES_DIR}/256bit \
|
||||
burn_efuse ABS_DONE_1 1 FLASH_CRYPT_CNT 1"
|
||||
num = 1
|
||||
else:
|
||||
sb_digest_name = (
|
||||
"SECURE_BOOT_DIGEST" if arg_chip == "esp32c2" else "SECURE_BOOT_DIGEST0"
|
||||
)
|
||||
cmd = f"--postpone \
|
||||
burn_efuse ENABLE_SECURITY_DOWNLOAD 1 DIS_DOWNLOAD_MODE 1 \
|
||||
SECURE_VERSION 1 \
|
||||
burn_key BLOCK_KEY0 {IMAGES_DIR}/256bit {sb_digest_name} \
|
||||
burn_efuse SPI_BOOT_CRYPT_CNT 1 SECURE_BOOT_EN 1"
|
||||
num = 3 if arg_chip == "esp32c2" else 4
|
||||
output = self.espefuse_py(cmd)
|
||||
assert f"BURN BLOCK{num} - OK" in output
|
||||
assert "BURN BLOCK0 - OK" in output
|
||||
assert "Burn postponed efuses from BLOCK0" in output
|
||||
assert "BURN BLOCK0 - OK" in output
|
||||
assert "Successful" in output
|
||||
|
Reference in New Issue
Block a user