feat(espefuse): Replace execute-scripts with public API

BREAKING CHANGE:
- execute-scripts command is removed
This commit is contained in:
Peter Dragun
2025-06-06 14:54:06 +02:00
parent d7da0f8a45
commit ff72b26bf3
18 changed files with 208 additions and 453 deletions

View File

@@ -1,119 +0,0 @@
.. _execute-scripts-cmd:
Execute Scripts
===============
The ``espefuse.py execute_scripts`` command executes scripts to burn at one time.
Positional arguments:
- ``scripts`` - it is special format of python scripts (receives list of files, like script1.py script2.py etc.).
Optional arguments:
- ``--index`` - integer index. It allows to retrieve unique data per chip from configfiles and then burn them (ex. CUSTOM_MAC, UNIQUE_ID).
- ``--configfiles`` - List of configfiles with data (receives list of configfiles, like configfile1.py configfile2.py etc.).
.. code-block:: none
> espefuse.py execute_scripts efuse_script1.py efuse_script2.py ...
This command allows burning all needed eFuses at one time based on your own python script and control issues during the burn process if so it will abort the burn process. This command has a few arguments:
* ``scripts`` is a list of scripts. The special format of python scripts can be executed inside ``espefuse.py``.
* ``--index`` integer index (it means the number of chip in the batch in the range 1 - the max number of chips in the batch). It allows to retrieve unique data per chip from configfiles and then burn them (ex. CUSTOM_MAC, UNIQUE_ID).
* ``--configfiles`` List of configfiles with data.
Below you can see some examples of the script. This script file is run from ``espefuse.py`` as ``exec(open(file.name).read())`` it means that some functions and imported libs are available for using like ``os``. Please use only provided functions.
If you want to use other libs in the script you can add them manually.
Inside this script, you can call all commands which are available in CLI, see ``espefuse.py --help``. To run a eFuse command you need to call ``espefuse(esp, efuses, args, 'burn_efuse DISABLE_DL_DECRYPT 1')``. This command will not burn eFuses immediately, the burn occurs at the end of all scripts.
If necessary, you can call ``efuses.burn_all()`` which prompts ``Type 'BURN' (all capitals) to continue.``. To skip this check and go without confirmation just add the ``--do-not-confirm`` flag to the ``execute_scripts`` command.
This command supports nesting. This means that one script can be called from another script (see the test case ``test_execute_scripts_nesting`` in ``esptool/test/test_espefuse.py``).
.. code-block:: none
> espefuse.py execute_scripts efuse_script1.py --do-not-confirm
Additionally, you can implement some checks based on the value of eFuses. To get value of an eFuse use ``efuses['FLASH_CRYPT_CNT'].get()``. Some eFuses have a dictionary to convert from a value to a human-readable as it looks in the table is printed by the ``summary`` command.
See how it is done (for ESP32) for ``CODING_SCHEME`` when ``get_meaning()`` is called:
* 0: ``NONE (BLK1-3 len=256 bits)``
* 1: ``3/4 (BLK1-3 len=192 bits)``
* 2: ``REPEAT (BLK1-3 len=128 bits) not supported``
* 3: ``NONE (BLK1-3 len=256 bits)``
.. code:: python
print("connected chip: %s, coding scheme %s" % (esp.get_chip_description(), efuses["CODING_SCHEME"].get_meaning()))
if os.path.exists("flash_encryption_key.bin"):
espefuse(esp, efuses, args, "burn_key flash_encryption flash_encryption_key.bin")
else:
raise esptool.FatalError("The 'flash_encryption_key.bin' file is missing in the project directory")
espefuse(esp, efuses, args, 'burn_efuse FLASH_CRYPT_CNT 0x7')
current_flash_crypt_cnt = efuses['FLASH_CRYPT_CNT'].get()
if current_flash_crypt_cnt in [0, 3]:
espefuse(esp, efuses, args, 'burn_efuse FLASH_CRYPT_CNT')
espefuse(esp, efuses, args, 'burn_efuse DISABLE_DL_ENCRYPT 1')
espefuse(esp, efuses, args, 'burn_efuse DISABLE_DL_DECRYPT 1')
espefuse(esp, efuses, args, 'burn_efuse DISABLE_DL_CACHE 1')
espefuse(esp, efuses, args, 'burn_efuse JTAG_DISABLE 1')
...
After ``efuses.burn_all()``, all needed eFuses will be burnt to chip in order ``BLK_MAX`` to ``BLK_0``. This order prevents cases when protection is set before the value goes to a block. Please note this while developing your scripts.
Upon completion, the new eFuses will be read back, and will be done some checks of written eFuses by ``espefuse.py``. In production, you might need to check that all written eFuses are set properly, see the example below.
The script `execute_efuse_script.py <https://github.com/espressif/esptool/blob/master/test/efuse_scripts/esp32xx/execute_efuse_script.py>`__ burns some eFuses and checks them after reading back. To check read and write protection, ``is_readable()`` and ``is_writeable()`` are called.
Burn Unique Data Per Chip
^^^^^^^^^^^^^^^^^^^^^^^^^
In case you are running the ``execute_scripts`` command from your production script, you may need to pass ``index`` to get the unique data for each chip from the ``configfiles`` (* .txt, * .json, etc.). The espefuse command will be like this, where ``{index}`` means the number of chip in the batch, you increment it by your own script in the range 1 - the max number of chips in the batch:
.. code-block:: none
espefuse.py execute_scripts efuse_script2.py --do-not-confirm --index {index} --configfiles mac_addresses.json unique_id.json
The example of a script to burn custom_mac address and unique_id getting them from configfiles.
.. code:: python
# efuse_script2.py
mac_addresses = json.load(args.configfiles[0])
unique_id = json.load(args.configfiles[1])
mac_val = mac_addresses[str(args.index)]
cmd = 'burn_custom_mac {}'.format(mac_val)
print(cmd)
espefuse(esp, efuses, args, cmd)
unique_id_val = unique_id[str(args.index)]
cmd = 'burn_efuse UNIQUE_ID {}'.format(unique_id_val)
print(cmd)
espefuse(esp, efuses, args, cmd)
The example of a script to burn custom_mac address that generated right in the script.
.. code:: python
# efuse_script2.py
step = 4
base_mac = '0xAABBCCDD0000'
mac = ''
for index in range(100):
mac = "{:012X}".format(int(base_mac, 16) + (args.index - 1) * step)
mac = ':'.join(mac[k] + mac [k + 1] for k in range(0, len(mac), 2))
break
cmd = 'burn_custom_mac mac'
print(cmd)
espefuse(esp, efuses, args, cmd)

View File

@@ -41,7 +41,6 @@ Supported Commands
get-custom-mac <get-custom-mac-cmd>
adc-info <adc-info-cmd>
set-flash-voltage <set-flash-voltage-cmd>
execute-scripts <execute-scripts-cmd>
check-error <check-error-cmd>
Optional General Arguments Of Commands

View File

@@ -225,7 +225,6 @@ Choices for the ``--before`` and ``--after`` options have been renamed to use ``
1. Replace all underscores in the ``--before`` and ``--after`` options with ``-`` in your scripts.
espsecure.py ``v5`` Migration Guide
***********************************
@@ -268,9 +267,9 @@ The public API of ``espsecure.py`` has been updated to provide a more consistent
**Migration Steps:**
1. Update function calls to pass individual parameters instead of the ``args`` object. For example:
``sign_data(args)`` -> ``sign_data(data=args.data, key=args.key, ...)``
or if you were mocking the args object, now you don't have to do that and you can pass parameters directly to the function like:
``sign_data(data=data, key=key, ...)``.
``sign_data(args)`` -> ``sign_data(data=args.data, key=args.key, ...)``
or if you were mocking the args object, now you don't have to do that and you can pass parameters directly to the function like:
``sign_data(data=data, key=key, ...)``.
2. Replace the ``custom_commandline`` parameter with ``argv`` in the ``main`` function call.
espefuse.py ``v5`` Migration Guide
@@ -307,3 +306,69 @@ The ``--port`` option is now required for all commands (except when using ``--vi
**Migration Steps:**
1. Add the ``--port`` option to all your espefuse commands.
``execute-scripts`` Command Removal
###################################
The ``execute-scripts`` command has been **removed in v5**. This command was used to execute custom eFuses scripts. It was deprecated in favor of using ``espefuse.py`` as a Python module (see :ref:`here <espefuse-scripting>`).
**Migration Steps:**
1. Refactor any workflows using the deprecated ``execute-scripts`` to use the public API.
2. Make sure to use the ``batch_mode`` argument for ``init_commands`` to avoid burning eFuses one by one.
3. Variables ``idx`` and ``configfiles`` are no longer supported. These can be replaced with simple for loops in Python.
For example, the following commands and script (using ESP32):
.. code-block:: bash
> espefuse.py --port /dev/ttyUSB0 execute_scripts efuse_script.py --do-not-confirm
.. code-block:: python
espefuse(esp, efuses, args, "burn_efuse JTAG_DISABLE 1 DISABLE_SDIO_HOST 1 CONSOLE_DEBUG_DISABLE 1")
espefuse(esp, efuses, args, "burn_key flash_encryption ../../images/efuse/256bit --no-protect-key")
espefuse(esp, efuses, args, "burn_key_digest ../../secure_images/rsa_secure_boot_signing_key.pem")
espefuse(esp, efuses, args, "burn_bit BLOCK3 64 66 69 72 78 82 83 90")
espefuse(esp, efuses, args, "burn_custom_mac AA:BB:CC:DD:EE:88")
efuses.burn_all()
espefuse(esp, efuses, args, "summary")
espefuse(esp, efuses, args, "adc_info")
espefuse(esp, efuses, args, "get_custom_mac")
if not efuses["BLOCK1"].is_readable() or not efuses["BLOCK1"].is_writeable():
raise Exception("BLOCK1 should be readable and writeable")
Can be replaced with public API:
.. code-block:: python
from espefuse import init_commands
with init_commands(port="/dev/ttyUSB0", batch_mode=True, do_not_confirm=True) as espefuse:
espefuse.burn_efuse({"JTAG_DISABLE": "1", "DISABLE_SDIO_HOST": "1", "CONSOLE_DEBUG_DISABLE": "1"})
with open("../../images/efuse/256bit", "rb") as f:
espefuse.burn_key(["flash_encryption"], [f], no_protect_key=True)
with open("../../secure_images/rsa_secure_boot_signing_key.pem", "rb") as f:
espefuse.burn_key_digest([f])
espefuse.burn_bit("BLOCK3", [64, 66, 69, 72, 78, 82, 83, 90])
espefuse.burn_custom_mac(b"\xaa\xbb\xcc\xdd\xee\x88")
espefuse.burn_all()
espefuse.summary()
espefuse.adc_info()
espefuse.get_custom_mac()
if not espefuse.efuses["BLOCK1"].is_readable() or not espefuse.efuses["BLOCK1"].is_writeable():
raise Exception("BLOCK1 should be readable and writeable")
.. note::
Please note that the ``batch_mode`` argument for ``init_commands`` is required to avoid burning eFuses one by one. This was previously
the default behavior for ``execute-scripts`` command.
For more details on the public API, see :ref:`espefuse-scripting`.

View File

@@ -13,6 +13,7 @@ from esptool.logger import log
from espefuse.cli_util import Group
from espefuse.efuse.base_operations import BaseCommands
from espefuse.efuse_interface import (
DEPRECATED_COMMANDS,
get_esp,
init_commands,
SUPPORTED_COMMANDS,
@@ -118,6 +119,10 @@ def cli(
esp = ctx.obj.get("esp", None)
external_esp = esp is not None
is_help = ctx.obj.get("is_help", False)
used_cmds = ctx.obj.get("used_cmds", [])
if any(cmd.replace("_", "-") in DEPRECATED_COMMANDS for cmd in used_cmds):
return # do not connect to ESP if any command is deprecated
if not port and not external_esp and not is_help and not virt:
raise click.BadOptionUsage(
@@ -159,7 +164,6 @@ def cli(
commands.efuses.postpone = postpone
commands.add_cli_commands(cli)
used_cmds = ctx.obj["used_cmds"]
multiple_burn_commands = (
sum(cmd.replace("_", "-") in SUPPORTED_BURN_COMMANDS for cmd in used_cmds) > 1
)
@@ -180,6 +184,20 @@ def cli(
print("Successful")
@cli.command("execute-scripts", hidden=True)
@click.argument("scripts", nargs=-1, type=click.UNPROCESSED)
@click.option("--index", type=click.UNPROCESSED)
@click.option("--configfiles", type=click.UNPROCESSED)
def execute_scripts_cli(scripts, index, configfiles):
"""REMOVED: See Migration guide in documentation for details."""
log.error(
"REMOVED: `execute_scripts` was replaced with the public API in v5. "
"Please see Migration Guide in documentation for details: "
"https://docs.espressif.com/projects/esptool/en/latest/migration-guide.html#espefuse-py-v5-migration-guide"
)
sys.exit(2)
def main(argv: list[str] | None = None, esp: esptool.ESPLoader | None = None):
"""
Main function for espefuse

View File

@@ -10,6 +10,7 @@ from esptool.cli_util import Group as EsptoolGroup
from esptool.logger import log
from espefuse.efuse_interface import (
DEPRECATED_COMMANDS,
init_commands,
SUPPORTED_BURN_COMMANDS,
SUPPORTED_READ_COMMANDS,
@@ -224,7 +225,7 @@ class Group(EsptoolGroup):
def get_help(self, ctx: click.Context) -> str:
# help was called without any commands, so we need to add the commands for the
# default chip
if not self.list_commands(ctx):
if not (set(self.list_commands(ctx)) - set(DEPRECATED_COMMANDS)):
chip = ctx.obj["chip"]
if chip == "auto":
log.note(

View File

@@ -355,25 +355,6 @@ class BaseCommands:
"""Print human-readable summary of eFuse values."""
self.summary(efuses_to_show, format, file)
@cli.command("execute-scripts")
@click.argument("scripts", nargs=-1, type=click.File("r"), required=True)
@click.option(
"--index",
type=int,
help="integer index. It allows to retrieve unique data per chip "
"from configfiles and then burn them (ex. CUSTOM_MAC, UNIQUE_ID).",
)
@click.option(
"--configfiles",
type=click.File("r"),
multiple=True,
help="List of configfiles with data",
)
@click.pass_context
def execute_scripts_cli(ctx, scripts, index, configfiles):
"""Executes scripts to burn at one time."""
self.execute_scripts(scripts, ctx.obj["debug"], configfiles, index)
@cli.command("check-error")
@click.option(
"--recovery", is_flag=True, help="Recovery of BLOCKs after encoding errors"
@@ -1151,9 +1132,6 @@ class BaseCommands:
raise esptool.FatalError("Error(s) were detected in eFuses")
print("No errors detected")
def execute_scripts(self, scripts, debug, configfiles, index):
raise NotImplementedError("execute_scripts is not implemented")
def burn_custom_mac(self, mac: str | bytes):
"""
Burn a 48-bit Custom MAC Address.

View File

@@ -41,7 +41,6 @@ SUPPORTED_BURN_COMMANDS = [
"burn-key-digest",
"burn-custom-mac",
"set-flash-voltage",
"execute-scripts",
]
SUPPORTED_READ_COMMANDS = [
@@ -52,7 +51,11 @@ SUPPORTED_READ_COMMANDS = [
"check-error",
]
SUPPORTED_COMMANDS = SUPPORTED_READ_COMMANDS + SUPPORTED_BURN_COMMANDS
DEPRECATED_COMMANDS = ["execute-scripts"]
SUPPORTED_COMMANDS = (
SUPPORTED_READ_COMMANDS + SUPPORTED_BURN_COMMANDS + DEPRECATED_COMMANDS
)
SUPPORTED_CHIPS = {
"esp32": DefChip(esp32_efuse, esptool.targets.ESP32ROM),

View File

@@ -1,18 +0,0 @@
# flake8: noqa
# SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
#
# SPDX-License-Identifier: GPL-2.0-or-later
import json
config = json.load(args.configfiles[0])
assert args.index == 10, "Index should be 10"
for cmd in config["burn_efuses1"]:
cmd = cmd.format(index=args.index)
print(cmd)
espefuse(esp, efuses, args, cmd)
assert args.index == 10, "Index should be 10"

View File

@@ -1,18 +0,0 @@
# flake8: noqa
# SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
#
# SPDX-License-Identifier: GPL-2.0-or-later
import json
config = json.load(args.configfiles[0])
assert args.index == 28, "Should be index from the first script = 28"
for cmd in config["burn_efuses2"]:
cmd = cmd.format(index=args.index)
print(cmd)
espefuse(esp, efuses, args, cmd)
assert args.index == 28, "Should be index from the first script = 28"

View File

@@ -1,6 +0,0 @@
{
"burn_efuses1": [
"burn_bit BLOCK3 0 1 2 3 4 5 6 7 8 9 {index}",
"burn_bit BLOCK3 29 30 31"
]
}

View File

@@ -1,8 +0,0 @@
{
"burn_efuses2": [
"burn_bit BLOCK3 11 12 13 14 15 16 17 18 19",
"burn_bit BLOCK3 20 21 22 23 24 25 26 27 {index}",
"execute_scripts efuse_scripts/efuse_burn1.py --index 10 --configfiles efuse_scripts/esp32/config1.json",
"burn_bit BLOCK2 {index}"
]
}

View File

@@ -1,50 +0,0 @@
# flake8: noqa
# fmt: off
espefuse(esp, efuses, args, "burn_efuse JTAG_DISABLE 1 DISABLE_SDIO_HOST 1 CONSOLE_DEBUG_DISABLE 1")
espefuse(esp, efuses, args, "burn_key flash_encryption ../../images/efuse/256bit --no-protect-key")
espefuse(esp, efuses, args, "burn_key_digest ../../secure_images/rsa_secure_boot_signing_key.pem")
espefuse(esp, efuses, args, "burn_bit BLOCK3 64 66 69 72 78 82 83 90")
espefuse(esp, efuses, args, "burn_custom_mac AA:BB:CC:DD:EE:88")
efuses.burn_all()
espefuse(esp, efuses, args, "summary")
espefuse(esp, efuses, args, "adc_info")
espefuse(esp, efuses, args, "get_custom_mac")
# Checks written eFuses
if efuses["JTAG_DISABLE"].get() != 1:
raise esptool.FatalError("JTAG_DISABLE was not set")
if efuses["DISABLE_SDIO_HOST"].get() != 1:
raise esptool.FatalError("DISABLE_SDIO_HOST was not set")
if efuses["CONSOLE_DEBUG_DISABLE"].get() != 1:
raise esptool.FatalError("CONSOLE_DEBUG_DISABLE was not set")
if efuses["BLOCK1"].get_meaning() != "bf be bd bc bb ba b9 b8 b7 b6 b5 b4 b3 b2 b1 b0 af ae ad ac ab aa a9 a8 a7 a6 a5 a4 a3 a2 a1 a0":
raise esptool.FatalError("BLOCK1 was not set correctly")
if not efuses["BLOCK1"].is_readable() or not efuses["BLOCK1"].is_writeable():
raise esptool.FatalError("BLOCK1 should be readable and writeable")
if efuses["BLOCK2"].get_meaning() != "cb 27 91 a3 71 b0 c0 32 2b f7 37 04 78 ba 09 62 22 4c ab 1c f2 28 78 79 e4 29 67 3e 7d a8 44 63":
raise esptool.FatalError("BLOCK2 was not set correctly")
if not efuses["BLOCK2"].is_readable() or efuses["BLOCK2"].is_writeable():
raise esptool.FatalError("BLOCK2 should not be readable and not writeable")
if efuses["BLOCK3"].get_meaning() != "69 aa bb cc dd ee 88 00 25 41 0c 04 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 00 00 00 00 00":
raise esptool.FatalError("BLOCK3 was not set correctly")
if efuses["CUSTOM_MAC"].get_meaning() != "aa:bb:cc:dd:ee:88 (CRC 0x69 OK)":
raise esptool.FatalError("CUSTOM_MAC was not set correctly")
espefuse(esp, efuses, args, "read_protect_efuse BLOCK1")
espefuse(esp, efuses, args, "write_protect_efuse BLOCK1")
efuses.burn_all()
if efuses["BLOCK1"].is_readable() or efuses["BLOCK1"].is_writeable():
raise esptool.FatalError("BLOCK_KEY0 should be not readable and not writeable")

View File

@@ -1,23 +0,0 @@
# flake8: noqa
# fmt: off
espefuse(esp, efuses, args, "burn_efuse JTAG_DISABLE 1 DISABLE_SDIO_HOST 1 CONSOLE_DEBUG_DISABLE 1")
if efuses["JTAG_DISABLE"].get() != 0:
raise esptool.FatalError("Burn should be at the end")
espefuse(esp, efuses, args, "burn_key flash_encryption ../../images/efuse/256bit --no-protect-key")
if efuses["BLOCK1"].get_meaning() != "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00":
raise esptool.FatalError("Burn should be at the end")
if not efuses["BLOCK1"].is_readable() or not efuses["BLOCK1"].is_writeable():
raise esptool.FatalError("Burn should be at the end")
espefuse(esp, efuses, args, "burn_key_digest ../../secure_images/rsa_secure_boot_signing_key.pem")
if efuses["BLOCK2"].get_meaning() != "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00":
raise esptool.FatalError("Burn should be at the end")
if not efuses["BLOCK2"].is_readable() or not efuses["BLOCK2"].is_writeable():
raise esptool.FatalError("Burn should be at the end")
espefuse(esp, efuses, args, "burn_bit BLOCK3 64 66 69 72 78 82 83 90")
espefuse(esp, efuses, args, "burn_custom_mac AA:BB:CC:DD:EE:88")
if efuses["BLOCK3"].get_meaning() != "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00":
raise esptool.FatalError("Burn should be at the end")

View File

@@ -1,6 +0,0 @@
{
"burn_efuses1": [
"burn_bit BLOCK_KEY4 0 1 2 3 4 5 6 7 8 9 {index}",
"burn_bit BLOCK_KEY4 29 30 31"
]
}

View File

@@ -1,8 +0,0 @@
{
"burn_efuses2": [
"burn_bit BLOCK_KEY4 11 12 13 14 15 16 17 18 19",
"burn_bit BLOCK_KEY4 20 21 22 23 24 25 26 27 {index}",
"execute_scripts efuse_scripts/efuse_burn1.py --index 10 --configfiles efuse_scripts/esp32xx/config1.json",
"burn_bit BLOCK_KEY3 {index}"
]
}

View File

@@ -1,64 +0,0 @@
# flake8: noqa
# fmt: off
espefuse(esp, efuses, args, 'burn_efuse DIS_FORCE_DOWNLOAD 1 DIS_DOWNLOAD_MODE 1')
espefuse(esp, efuses, args, 'burn_bit BLOCK_USR_DATA 64 66 69 72 78 82 83 90')
espefuse(esp, efuses, args, 'read_protect_efuse BLOCK_SYS_DATA2')
espefuse(esp, efuses, args, 'write_protect_efuse BLOCK_SYS_DATA2')
espefuse(esp, efuses, args, 'burn_block_data BLOCK_KEY5 ../../images/efuse/256bit')
espefuse(esp, efuses, args, 'burn_key BLOCK_KEY0 ../../images/efuse/256bit XTS_AES_128_KEY --no-read-protect')
espefuse(esp, efuses, args, 'burn_key_digest BLOCK_KEY1 ../../secure_images/rsa_secure_boot_signing_key.pem SECURE_BOOT_DIGEST0')
efuses.burn_all()
espefuse(esp, efuses, args, 'summary')
espefuse(esp, efuses, args, 'adc_info')
# Checks written eFuses
if efuses["DIS_FORCE_DOWNLOAD"].get() != 1:
raise esptool.FatalError("DIS_FORCE_DOWNLOAD was not set")
if efuses["DIS_DOWNLOAD_MODE"].get() != 1:
raise esptool.FatalError("DIS_DOWNLOAD_MODE was not set")
if efuses["BLOCK_USR_DATA"].get_meaning() != "00 00 00 00 00 00 00 00 25 41 0c 04 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00":
raise esptool.FatalError("BLOCK_USR_DATA was not set correctly")
if efuses["BLOCK_SYS_DATA2"].is_readable() or efuses["BLOCK_SYS_DATA2"].is_writeable():
raise esptool.FatalError("BLOCK_SYS_DATA2 should be read and write protected")
if efuses["BLOCK_KEY5"].get_meaning() != "a0 a1 a2 a3 a4 a5 a6 a7 a8 a9 aa ab ac ad ae af b0 b1 b2 b3 b4 b5 b6 b7 b8 b9 ba bb bc bd be bf":
raise esptool.FatalError("BLOCK_KEY5 was not set correctly")
if efuses["BLOCK_KEY0"].get_meaning() != "bf be bd bc bb ba b9 b8 b7 b6 b5 b4 b3 b2 b1 b0 af ae ad ac ab aa a9 a8 a7 a6 a5 a4 a3 a2 a1 a0":
raise esptool.FatalError("BLOCK_KEY0 was not set correctly")
if not efuses["BLOCK_KEY0"].is_readable() or efuses["BLOCK_KEY0"].is_writeable():
raise esptool.FatalError("BLOCK_KEY0 should be readable and not writable")
if efuses["KEY_PURPOSE_0"].get_meaning() != "XTS_AES_128_KEY":
raise esptool.FatalError("KEY_PURPOSE_0 was not set XTS_AES_128_KEY")
if efuses["KEY_PURPOSE_0"].is_writeable():
raise esptool.FatalError("KEY_PURPOSE_0 should be write-protected")
if efuses["BLOCK_KEY1"].get_meaning() != "cb 27 91 a3 71 b0 c0 32 2b f7 37 04 78 ba 09 62 22 4c ab 1c f2 28 78 79 e4 29 67 3e 7d a8 44 63":
raise esptool.FatalError("BLOCK_KEY1 was not set correctly")
if efuses["KEY_PURPOSE_1"].get_meaning() != "SECURE_BOOT_DIGEST0":
raise esptool.FatalError("KEY_PURPOSE_1 was not set SECURE_BOOT_DIGEST0")
if efuses["KEY_PURPOSE_1"].is_writeable():
raise esptool.FatalError("KEY_PURPOSE_1 should be write-protected")
if not efuses["BLOCK_KEY1"].is_readable() or efuses["BLOCK_KEY1"].is_writeable():
raise esptool.FatalError("BLOCK_KEY1 should be readable and not writable")
espefuse(esp, efuses, args, 'burn_key BLOCK_KEY0 ../../images/efuse/256bit XTS_AES_128_KEY')
efuses.burn_all()
if efuses["BLOCK_KEY0"].is_readable() or efuses["BLOCK_KEY0"].is_writeable():
raise esptool.FatalError("BLOCK_KEY0 should be not readable and not writeable")

View File

@@ -1,30 +0,0 @@
# flake8: noqa
# fmt: off
espefuse(esp, efuses, args, 'burn_efuse DIS_FORCE_DOWNLOAD 1 DIS_DOWNLOAD_MODE 1')
if efuses["DIS_FORCE_DOWNLOAD"].get() != 0:
raise esptool.FatalError("Burn should be at the end")
espefuse(esp, efuses, args, 'burn_bit BLOCK_USR_DATA 64 66 69 72 78 82 83 90')
if efuses["BLOCK_USR_DATA"].get_meaning() != "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00":
raise esptool.FatalError("Burn should be at the end")
espefuse(esp, efuses, args, 'read_protect_efuse BLOCK_SYS_DATA2')
espefuse(esp, efuses, args, 'write_protect_efuse BLOCK_SYS_DATA2')
if not efuses["BLOCK_SYS_DATA2"].is_readable() or not efuses["BLOCK_SYS_DATA2"].is_writeable():
raise esptool.FatalError("Burn should be at the end")
espefuse(esp, efuses, args, 'burn_block_data BLOCK_KEY5 ../../images/efuse/256bit')
if efuses["BLOCK_KEY5"].get_meaning() != "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00":
raise esptool.FatalError("Burn should be at the end")
espefuse(esp, efuses, args, 'burn_key BLOCK_KEY0 ../../images/efuse/256bit XTS_AES_128_KEY --no-read-protect')
if efuses["BLOCK_KEY0"].get_meaning() != "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00":
raise esptool.FatalError("Burn should be at the end")
if not efuses["BLOCK_KEY0"].is_readable() or not efuses["BLOCK_KEY0"].is_writeable():
raise esptool.FatalError("Burn should be at the end")
espefuse(esp, efuses, args, 'burn_key_digest BLOCK_KEY1 ../../secure_images/rsa_secure_boot_signing_key.pem SECURE_BOOT_DIGEST0')
if efuses["BLOCK_KEY1"].get_meaning() != "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00":
raise esptool.FatalError("Burn should be at the end")
if not efuses["BLOCK_KEY1"].is_readable() or not efuses["BLOCK_KEY1"].is_writeable():
raise esptool.FatalError("Burn should be at the end")

View File

@@ -39,7 +39,6 @@ from conftest import arg_chip, arg_port, arg_reset_port, need_to_install_package
TEST_DIR = os.path.abspath(os.path.dirname(__file__))
IMAGES_DIR = os.path.join(TEST_DIR, "images", "efuse")
S_IMAGES_DIR = os.path.join(TEST_DIR, "secure_images")
EFUSE_S_DIR = os.path.join(TEST_DIR, "efuse_scripts")
import pytest
@@ -66,6 +65,7 @@ print("Running espefuse.py tests...")
# The default value of the program name for argparse has changed in Python 3.14
# https://docs.python.org/dev/whatsnew/3.14.html#argparse
ESPEFUSE_MODNAME = "python -m espefuse"
EMPTY_BLOCK = " ".join(["00"] * 32) # "00 00 ... 00"
@pytest.mark.host_test
@@ -1851,87 +1851,128 @@ class TestByteOrderBurnKeyCommand(EfuseTestCase):
)
@pytest.mark.skip("This is not supported yet with new click parser")
class TestExecuteScriptsCommands(EfuseTestCase):
@classmethod
def setup_class(self):
# Save the current working directory to be restored later
self.stored_dir = os.getcwd()
class TestPublicAPI(EfuseTestCase):
def _init_commands(self, **kwargs):
from espefuse import init_commands
@classmethod
def teardown_class(self):
# Restore the stored working directory
os.chdir(self.stored_dir)
args = {
"do_not_confirm": True,
"chip": arg_chip,
"virt": True,
"virt_efuse_file": self.efuse_file.name,
"debug": True,
}
args.update(kwargs)
return init_commands(**args)
@pytest.mark.skipif(
arg_chip in ["esp32c2", "esp32p4", "esp32h21", "esp32h4"],
reason="These chips do not have eFuses used in this test",
)
def test_execute_scripts_with_check_that_only_one_burn(self):
self.espefuse_py("execute_scripts -h")
name = arg_chip if arg_chip in ["esp32", "esp32c2"] else "esp32xx"
os.chdir(os.path.join(TEST_DIR, "efuse_scripts", name))
self.espefuse_py("execute_scripts execute_efuse_script2.py")
@pytest.mark.skipif(arg_chip != "esp32", reason="This test is only for esp32")
def test_public_api_batch_mode(self):
with self._init_commands(batch_mode=True) as espefuse:
espefuse.burn_efuse(
{
"JTAG_DISABLE": "1",
"DISABLE_SDIO_HOST": "1",
"CONSOLE_DEBUG_DISABLE": "1",
}
)
assert espefuse.efuses["JTAG_DISABLE"].get() == 0, (
"Burn should be at the end"
)
@pytest.mark.skipif(
arg_chip in ["esp32c2", "esp32p4", "esp32h21", "esp32h4"],
reason="These chips do not have eFuses used in this test",
)
def test_execute_scripts_with_check(self):
self.espefuse_py("execute_scripts -h")
name = arg_chip if arg_chip in ["esp32", "esp32c2"] else "esp32xx"
os.chdir(os.path.join(TEST_DIR, "efuse_scripts", name))
self.espefuse_py("execute_scripts execute_efuse_script.py")
with open(IMAGES_DIR + "/256bit", "rb") as f:
espefuse.burn_key(["flash_encryption"], [f], no_protect_key=True)
assert espefuse.efuses["BLOCK1"].get_meaning() == EMPTY_BLOCK
assert (
espefuse.efuses["BLOCK1"].is_readable()
and espefuse.efuses["BLOCK1"].is_writeable()
)
def test_execute_scripts_with_index_and_config(self):
os.chdir(TEST_DIR)
if arg_chip in ["esp32", "esp32c2"]:
cmd = f"execute_scripts {EFUSE_S_DIR}/efuse_burn1.py --index 10 \
--configfiles {EFUSE_S_DIR}/esp32/config1.json"
else:
cmd = f"execute_scripts {EFUSE_S_DIR}/efuse_burn1.py --index 10 \
--configfiles {EFUSE_S_DIR}/esp32xx/config1.json"
self.espefuse_py(cmd)
output = self.espefuse_py("-d summary")
if arg_chip in ["esp32", "esp32c2"]:
with open(S_IMAGES_DIR + "/rsa_secure_boot_signing_key.pem", "rb") as f:
espefuse.burn_key_digest(f)
assert espefuse.efuses["BLOCK2"].get_meaning() == EMPTY_BLOCK
assert (
"[3 ] read_regs: e00007ff 00000000 00000000 00000000 "
"00000000 00000000 00000000 00000000"
) in output
else:
assert (
"[8 ] read_regs: e00007ff 00000000 00000000 00000000 "
"00000000 00000000 00000000 00000000"
) in output
espefuse.efuses["BLOCK2"].is_readable()
and espefuse.efuses["BLOCK2"].is_writeable()
)
espefuse.burn_bit("BLOCK3", [64, 66, 69, 72, 78, 82, 83, 90])
espefuse.burn_custom_mac("aa:cd:ef:11:22:33")
assert espefuse.efuses["BLOCK3"].get_meaning() == EMPTY_BLOCK
espefuse.burn_all()
espefuse.summary()
assert espefuse.efuses["JTAG_DISABLE"].get() == 1
assert espefuse.efuses["DISABLE_SDIO_HOST"].get() == 1
assert espefuse.efuses["CONSOLE_DEBUG_DISABLE"].get() == 1
def test_execute_scripts_nesting(self):
os.chdir(TEST_DIR)
if arg_chip in ["esp32", "esp32c2"]:
cmd = f"execute_scripts {EFUSE_S_DIR}/efuse_burn2.py --index 28 \
--configfiles {EFUSE_S_DIR}/esp32/config2.json"
else:
cmd = f"execute_scripts {EFUSE_S_DIR}/efuse_burn2.py --index 28 \
--configfiles {EFUSE_S_DIR}/esp32xx/config2.json"
self.espefuse_py(cmd)
output = self.espefuse_py("-d summary")
if arg_chip in ["esp32", "esp32c2"]:
assert (
"[2 ] read_regs: 10000000 00000000 00000000 00000000 "
"00000000 00000000 00000000 00000000"
) in output
espefuse.efuses["BLOCK1"].get_meaning()
== "bf be bd bc bb ba b9 b8 b7 b6 b5 b4 b3 b2 b1 b0 af "
"ae ad ac ab aa a9 a8 a7 a6 a5 a4 a3 a2 a1 a0"
)
assert (
"[3 ] read_regs: ffffffff 00000000 00000000 00000000 "
"00000000 00000000 00000000 00000000"
) in output
else:
espefuse.efuses["BLOCK1"].is_readable()
and espefuse.efuses["BLOCK1"].is_writeable()
)
assert (
"[7 ] read_regs: 10000000 00000000 00000000 00000000 "
"00000000 00000000 00000000 00000000"
) in output
espefuse.efuses["BLOCK2"].get_meaning()
== "cb 27 91 a3 71 b0 c0 32 2b f7 37 04 78 ba 09 62 22 "
"4c ab 1c f2 28 78 79 e4 29 67 3e 7d a8 44 63"
)
assert (
"[8 ] read_regs: ffffffff 00000000 00000000 00000000 "
"00000000 00000000 00000000 00000000"
) in output
espefuse.efuses["CUSTOM_MAC"].get_meaning()
== "aa:cd:ef:11:22:33 (CRC 0x63 OK)"
)
assert (
espefuse.efuses["BLOCK3"].get_meaning()
== "63 aa cd ef 11 22 33 00 25 41 0c 04 00 00 00 00 "
"00 00 00 00 00 00 00 01 00 00 00 00 00 00 00 00"
)
def test_public_api_nesting(self):
from espefuse import init_commands
def burn_custom_mac(esp):
with init_commands(
esp=esp, batch_mode=True, do_not_confirm=True
) as espefuse:
espefuse.burn_custom_mac(b"\xaa\xcd\xef\x11\x22\x33")
espefuse.burn_all()
with self._init_commands(batch_mode=True) as espefuse:
espefuse.burn_efuse({"WR_DIS": "2", "RD_DIS": "1"})
# Burn should be at the end; so the eFuses should be set to 0
assert espefuse.efuses["WR_DIS"].get() == 0
assert espefuse.efuses["RD_DIS"].get() == 0
burn_custom_mac(espefuse.esp)
espefuse.summary()
# Make sure that the eFuses are not set; last burn_all is not called yet
assert espefuse.efuses["WR_DIS"].get() == 0
assert espefuse.efuses["RD_DIS"].get() == 0
assert (
espefuse.efuses["CUSTOM_MAC"].get_meaning()
== "00:00:00:00:00:00 (CRC 0x00 OK)"
if arg_chip == "esp32"
else "00:00:00:00:00:00 (OK)"
)
# Last burn_all should actually set eFuses
espefuse.burn_all()
espefuse.summary()
assert espefuse.efuses["WR_DIS"].get() == 2
assert espefuse.efuses["RD_DIS"].get() == 1
assert (
espefuse.efuses["CUSTOM_MAC"].get_meaning()
== "aa:cd:ef:11:22:33 (CRC 0x63 OK)"
if arg_chip == "esp32"
else "aa:cd:ef:11:22:33 (OK)"
)
class TestMultipleCommands(EfuseTestCase):