diff --git a/tools/esptool_py/CHANGELOG.md b/tools/esptool_py/CHANGELOG.md index f3d16e0b4c..e422e66508 100644 --- a/tools/esptool_py/CHANGELOG.md +++ b/tools/esptool_py/CHANGELOG.md @@ -1,255 +1,492 @@ + + + + +# CHANGELOG + +> All notable changes to this project are documented in this file. +> This list is not exhaustive - only important changes, fixes, and new features in the code are reflected here. + +
+ + Static Badge + + + Static Badge + + + Static Badge + +
+
+ +## v5.0.0 (2025-07-02) + +### 🚨 Breaking changes + +- - The .py suffix is deprecated for the following scripts: + - esptool + - espefuse + - espsecure + - esp_rfc2217_server *(Peter Dragun - 635cde1)* +- - execute-scripts command is removed *(Peter Dragun - ff72b26)* + +### ✨ New Features + +- **espefuse**: Use the esptool logger, more concise messages *(Radim Karniš - 983338f)* +- **espefuse**: Replace execute-scripts with public API *(Peter Dragun - ff72b26)* +- **espefuse**: Add public API for espefuse *(Peter Dragun - d7da0f8)* +- **espefuse**: Rename all commands to use dashes and add tests for deprecated commands *(Peter Dragun - ade3088)* +- **espefuse**: Add support for chaining commands with click parser *(Peter Dragun - 0a2ea69)* +- **espefuse**: Refactor CLI and use click for parsing arguments *(Peter Dragun - aa80001)* +- **espefuse**: Adds efuse calculation fields for ESP32-C5 *(Konstantin Kondrashov - 9104038)* +- **espefuse**: Adds 3-bit field for wafer major version in ESP32-P4 *(Konstantin Kondrashov - c102510)* +- **verbosity**: Allow setting silent or verbose output levels *(Radim Karniš - 90e3770)* +- **efuse**: Adds efuses for ESP32-C61 ECO3 *(Konstantin Kondrashov - 6146410)* +- **espefuse**: Support efuse for ESP32-C5 ECO2 (v1.0) *(Konstantin Kondrashov - 3726726)* +- **espsecure**: Use esptool logger, unify output format of messages *(Radim Karniš - 905249c)* +- **stub_flasher**: Support for >16MB flash on P4 and >16MB encrypted writes on S3 *(Radim Karniš - 4e6803e)* +- **espsecure**: Drop ecdsa module, use cryptography instead *(Radim Karniš - e132f6f)* +- **espsecure**: Unify all commands and options to use dash instead of underscore *(Peter Dragun - 36325fd)* +- **espsecure**: Use rich click for CLI parsing *(Peter Dragun - 9c7ddc1)* +- **targets**: Update chip features lists with more info *(Radim Karniš - 3c776aa)* +- **logging**: Add collapsible output stages and ASCII progress bars *(Radim Karniš - f3cf107)* +- **trace**: Update --trace with more info and more readable formatting *(Radim Karniš - 0beee77)* +- **cli**: Commands and options use dashes instead of underscores for uniformity *(Peter Dragun - 3cecd6d)* +- **cmds**: Expand input of all functions to file paths, bytes, or file-like objects *(Radim Karniš - 46a9e31)* +- **cmds**: Allow all functions to both return bytes and write to files *(Radim Karniš - 03b84a1)* +- **cmds**: Polish the public API, unify arg names, pack some args *(Radim Karniš - 37a13a9)* +- **cmds**: Encapsulate logic for running the stub flasher in run_stub *(Radim Karniš - 063d9d5)* +- **cli**: Add click-based CLI interface *(Peter Dragun - d40fefa)* +- **cmds**: Allow commands to output bytes, as well as write to a file *(Radim Karniš - 0153b79)* +- **cmds**: Rework the public API to work as a Python module *(Radim Karniš - ba36933)* +- **flash_attach**: Encapsulate logic for flash attaching and configuration *(Radim Karniš - 6e959ef)* +- **esp32h4**: update the ESP32H4StubLoader *(Chen Jichang - f7c78f8)* +- **espefuse**: Updates esp32h4 efuse table and fixes tests *(Konstantin Kondrashov - 3da8c57)* +- **esp32h4**: add ESP32H4 esptool support *(Chen Jichang - bcf5c6e)* +- **esp32h21**: Add Microsoft UF2 family ID *(Radim Karniš - cb0d334)* +- **errors**: Print errors to STDERR, catch KeyboardInterrupt *(Radim Karniš - 0864e17)* +- **write_flash**: Remove the superfluous --verify option *(Radim Karniš - dbf3d1c)* +- **logger**: Add a custom logger, allow output redirection *(Radim Karniš - 1ce02db)* +- **image_info**: Deprecate the --version 1 output format *(Radim Karniš - 3f625c3)* +- Remove .py suffix from scripts *(Peter Dragun - 635cde1)* +- detect flash size of Adesto flash chips *(Jaroslav Burian - 0b56f85)* +- Add support for k, M suffix for flash size *(Peter Dragun - 6f0d779)* +- Rename reset modes to use dash instead of underscore *(Peter Dragun - 851919f)* + +### 🐛 Bug Fixes + +- **logger**: Turn on smart features in more cases *(Jason2866 - 5d5eafb)* +- **elf2image**: Multiple fixes from 3rd party frameworks *(Sylvio Alves - cbd4e9b)* +- **stub_flasher**: Fix USB-Serial/JTAG mode on C5 ECO2 and C61 ECO3 *(Radim Karniš - 1decf86)* +- **write_flash**: Detect more cases of unresponsive flash, fix failing flash_size check *(Radim Karniš - e6bfc3b)* +- **stub_flasher**: Fix ESP32-C5 ECO2 flashing *(Radim Karniš - 3a4c15c)* +- **espefuse**: Fix output messages for set_flash_voltage *(Peter Dragun - daaedf8)* +- **espefuse**: JTAG_SEL_ENABLE has GPIO34 strapping pin for ESP32P4 *(Jan Beran - 78535e4)* +- **esp32c5**: fix bootloader address *(Jaroslav Burian - ec12073)* +- **autodetection**: Remove the Unsupported detection protocol stage *(Radim Karniš - 05553a4)* +- **logging**: Unify output messages, notes, and warning formatting *(Radim Karniš - 07879eb)* +- **elf2image**: fix elf2image for ram app when sha256 offset not specified *(Jaroslav Burian - 6f8ff39)* +- **esp32h4**: fix h4 chip feature *(Chen Jichang - 955943a)* +- **image_info**: Sanitize app and bootloader info of null bytes *(Radim Karniš - 8016455)* +- **lint**: Correct type annotations issues reported by mypy *(Radim Karniš - 0bca550)* +- **esptool**: Fix efuse base address for esp32h21 *(Konstantin Kondrashov - c3d28ee)* +- **elf2image**: support --flash-mmu-page-config for all chips *(Jaroslav Burian - 8be617c)* +- **elf2image**: Try to correct MMU page size if not specified *(Jaroslav Burian - f4fabc5)* +- **elf2image**: Print correct MMU page size in error message *(Jaroslav Burian - 9da4948)* +- **logging**: Avoid crashes when flushing if sys.stdout is not available *(Radim Karniš - 5176b67)* +- enable auto-detection of ESP32-S2 in secure download mode *(Jaroslav Burian - c2f5d21)* +- enable ESP32-P4 ECO5 chip detection *(Jaroslav Burian - 0b3460f)* +- Do not use padding for merged IntelHex files *(Peter Dragun - 08c170b)* +- lock upper version of click to <8.2.0 *(Peter Dragun - 5241cba)* +- Add timeout to read_flash to avoid infinite loops *(Peter Dragun - f26a7bb)* +- Close the data file after reading the data *(Stevan Stevic - 807d02b)* + +### 📖 Documentation + +- **elf2image**: Link an article with Simple Boot explanation *(Radim Karniš - 202dfad)* +- **logger**: Fix custom logger example code *(Radim Karniš - 26e86e9)* +- **logger**: Fix custom logger example code *(Radim Karniš - eaaa6b3)* +- Clarify versions in documentation *(Peter Dragun - 4586e4b)* +- Remove .py suffix from tool names *(Peter Dragun - e9f03ae)* +- Remove espefuse and espsecure migration guide for esp8266 *(Peter Dragun - b6e08a3)* +- Update migration guide for espefuse with click parser *(Peter Dragun - faf3e22)* +- Add missing esp32-p4 target to supported targets *(Peter Dragun - 8b5a5d9)* +- fix targets dropdown in production *(Peter Dragun - 2643101)* +- Update autocomplete docs for click-based CLI *(Peter Dragun - 89cfa52)* +- fix minor issues and improve vague statements *(Peter Dragun - 6d04155)* + +### 🔧 Code Refactoring + +- **cli_mode**: Improve CLI mode workflow code *(Radim Karniš - 0671d35)* +- **stub_class**: Make into a mixin to avoid code repetition *(Radim Karniš - 83613c8)* + +### 🗑️ Removals + +- **make_image**: Remove the make_image command in favor of other workflows *(Radim Karniš - 955a7c8)* +- **beta_targets**: Removed support for beta chip targets *(Radim Karniš - 8f1c206)* +- Deprecate Python versions 3.7, 3.8 and 3.9 *(Peter Dragun - 19f1bee)* + + +## v4.9.0 (2025-06-19) + +### ✨ New Features + +- **espefuse**: Add eFuses for ESP32-C61 ECO3 *(Radim Karniš - 98688ab)* +- **espefuse**: Support efuse for ESP32-C5 ECO2 (v1.0) *(Konstantin Kondrashov - ce16054)* +- **stub_flasher**: Support for >16MB flash on P4 and >16MB encrypted writes on S3 *(Radim Karniš - 0110514)* +- **espefuse**: Updates esp32h4 efuse table and fixes tests *(Konstantin Kondrashov - 777c505)* +- **esp32h4**: add ESP32H4 esptool support *(Chen Jichang - edb99bd)* +- **esp32h21**: Add Microsoft UF2 family ID *(Radim Karniš - 74d27ae)* +- **watchdog_reset**: Add a new watchdog_reset option working even in USB modes *(Radim Karniš - d37c38a)* +- **espsecure**: Improves an error message for encrypt_flash_data and decrypt_flash_data *(Konstantin Kondrashov - ef407ed)* +- **espefuse**: Clean up efuse code for ESP32H2 *(Konstantin Kondrashov - 4e922fe)* +- **espefuse**: Support different efuse table versions for ESP32H2 *(Konstantin Kondrashov - d51ecbe)* +- **espefuse**: Adds efuses for esp32h2 eco5 *(Konstantin Kondrashov - 9b74df6)* +- **esp32h21**: add ESP32H21 esptool support *(gaoxu - 92ceff2)* +- **esp32-p4**: add support for flasher stub in USB OTG mode *(Peter Dragun - 804f2db)* +- **esp32-c5**: Add ECO1 magic number *(Radim Karniš - 6cc002c)* +- **esp_rfc2217**: Improved the logger message format *(Jakub Kocka - 39a12a4)* +- **espefuse**: Adds 3 bit for PSRAM_CAP efuse field *(Konstantin Kondrashov - ab2e0bf)* +- **espefuse**: Adds API for getting block and wafer versions *(Konstantin Kondrashov - 111c6c0)* +- **espefuse**: Adds ADC calibration data for ESP32-C61 *(Konstantin Kondrashov - 36d9735)* +- **espefuse**: Adds ADC calibration data for ESP32-C5 *(Konstantin Kondrashov - a903812)* +- **espefuse**: Adds ADC calibration data for ESP32-P4 *(Konstantin Kondrashov - 215e4b8)* +- **erase_region**: Enable erasing in ROM bootloader and SDM *(Radim Karniš - e0deeac)* +- **hard_reset**: Support custom hard reset sequence configuration *(Radim Karniš - 1b15738)* +- print usb mode when output chip info *(Jan Beran - 749d1ad)* +- Add new app description segments *(Jaroslav Burian - b23e60f)* +- add filtering based on serial number *(Jaroslav Burian - 88319db)* +- Add support for Python 3.13 *(Radim Karniš - 6abd05d)* + +### 🐛 Bug Fixes + +- **stub_flasher**: Fix USB-Serial/JTAG mode on C5 ECO2 and C61 ECO3 *(Radim Karniš - 4382f14)* +- **write_flash**: Detect more cases of unresponsive flash, fix failing flash_size check *(Radim Karniš - f83d598)* +- **stub_flasher**: Fix ESP32-C5 ECO2 flashing *(Radim Karniš - bb237bc)* +- **espefuse**: Fix output messages for set_flash_voltage *(Peter Dragun - 759bcc6)* +- **espefuse**: JTAG_SEL_ENABLE has GPIO34 strapping pin for ESP32P4 *(Jan Beran - f6d1833)* +- **esp32c5**: fix bootloader address *(Jaroslav Burian - 83e0973)* +- **elf2image**: fix elf2image for ram app when sha256 offset not specified *(Radim Karniš - 9fd7b7a)* +- **esp32h4**: Correct ESP32-H4 chip features *(Radim Karniš - 5520963)* +- **esp32h21**: Fix eFuse base address *(Radim Karniš - dc05792)* +- **elf2image**: support --flash-mmu-page-config for all chips *(Jaroslav Burian - 54fdc75)* +- **elf2image**: Try to correct MMU page size if not specified *(Jaroslav Burian - d9afa9c)* +- **elf2image**: Print correct MMU page size in error message *(Jaroslav Burian - 447de60)* +- **test**: Expect the correct module name for Python's 3.14 argparse *(Karolina Surma - 98001b7)* +- **write_flash**: Skip flash_size checks if we can't read flash size *(Radim Karniš - 12095b2)* +- **save_segment**: Adds segment len check the same as bootloader does *(Konstantin Kondrashov - a6bceb7)* +- **chip_type_verification**: Enable in SDM, do not rely on magic numbers *(Radim Karniš - 598e07b)* +- **esp32-c6**: Disable RTC WDT reset to prevent port disappearing *(Radim Karniš - d47004e)* +- **esp_rfc2217**: Fixed keyboard interrupt on Windows and added info for command *(Jakub Kocka - 5569aa5)* +- **detect_chip**: Select correct loader before further operations to avoid silent failures *(Jan Beran - 8897ff8)* +- **usb_resets**: Fix resetting in USB-OTG and USB-Serial/JTAG modes *(Radim Karniš - 8298cdc)* +- Do not use padding for merged IntelHex files *(Peter Dragun - 739669f)* +- close port when connect fails *(Jaroslav Burian - d99c972)* +- Hide missing app info based on IDF version *(Jaroslav Burian - d2bca1e)* +- add delay after WDT reset for better stability *(Peter Dragun - 188c162)* +- Not reading app description for some SoCs *(Jaroslav Burian - 3555fe1)* +- Fix missing newline in output *(Jan Beran - 26b676b)* + +### 📖 Documentation + +- **esptool**: Fix reset sequences in documentation *(Jan Beran - 92160eb)* +- **flash_modes**: Correct QIO GPIO pins for all chips *(Radim Karniš - 23f11f0)* +- **espefuse**: Fixed JTAG strapping pin for ESP32-S3 in the help and documentation *(Roland Dobai - de1d1ce)* +- **scripting**: Add example of usage as a Python module *(Radim Karniš - d54e59f)* +- **esp8266**: change boot select pin to IO0 *(ChromaLock - c06ce1e)* +- **read_flash_sfdp**: Fix command formatting *(Radim Karniš - ec309bb)* +- **spi_connection**: Explain which flash chips are supported *(Radim Karniš - 6d37e30)* +- fix targets dropdown in production *(Peter Dragun - 9201ccd)* +- Point directly to the datasheet for given target *(Jan Beran - a32988e)* +- Add ESP32-C5 and ESP32-C61 docs *(Radim Karniš - f52c723)* + +--- + ## v4.8.1 (2024-09-25) -### Bug Fixes - -- **esp32c2**: Add esp32c2 eco4 rom magic value -- **packaging**: Correctly exclude the unwanted sub/modules - -## v4.8.0 (2024-09-18) - -### New Features - -- **espefuse**: Supports wafer efuse versions for esp32c61 -- **esptool**: add new command SFDP read -- **esptool**: Add option to retry connection in a loop -- **efuse**: Updates efuse table for esp32c5 -- **efuse**: Updates efuse table for esp32p4 -- **esp32c61**: Added stub flasher support -- **cli**: add autocompletions -- **esptool**: allow picking UART by VID/PID/Name -- **esp32c5**: Add USB-serial/JTAG stub support -- **esp32c5**: Add UART stub support -- **esptool**: Print key_purpose name for get_security_info cmd -- **espefuse**: Adds support extend efuse table by user CSV file -- **espefuse**: Adds efuse dump formats: separated(default) and united(new) -- **espefuse**: Adds incompatible eFuse settings check for S3 -- **reset**: Apply reconnections to the whole reset sequence, not line transitions -- **reset**: Automatically reconnect if port disconnects during reset -- **esp32-p4**: Add ECO1 magic number -- **espsecure**: Add support for secure boot v2 using ECDSA-P384 signatures -- **write_flash**: retry flashing if chip disconnects -- **espefuse**: Allow filtering efuses based on command line arguments -- **esploader**: Enable context manager for esp instances -- **espefuse**: Added check for correctness of written data -- **espefuse**: Improves help for burn_efuse cmd -- **esp32s3**: clear boot control register on hard reset -- **esp32-p4**: add spi-connection restriction to ROM class -- add UF2 IDs for ESP32-C5 and ESP32-C61 -- **espefuse**: Updates efuses for C5 and C61 -- **esp32c61**: add c61 basic flash support (no_stub) -- **esp32c5**: skipped the stub check for esp32c5 mp -- **esp32c5**: base support of esp32c5 mp (no stub) -- Added warning when secure boot enabled -- **cmds/write_flash**: Recalculated SHA digest for image binary -- print flash voltage in flash_id command -- **esptool**: Adds wafer and pkg versions -- **espefuse**: Update adc_info commands for all chips -- **espefuse**: Adds new efuses for esp32p4 -- **espefuse**: Allow the espefuse.py to work when coding scheme == 3 -- **err_defs**: Add ROM bootloader flash error definitions -- Use ruff instead of flake8 and black both in pre-commit and CI -- **esp32p4**: Enable USB-serial/JTAG in flasher stub -- **espefuse**: Postpone some efuses to burn them at the very end -- add advisory port locking -- **espefuse**: check_error --recover chip even if there are num_errors -- **espefuse**: Adds new efuses for esp32c6 and esp32h2 -- **esp32c5**: add target esp32c5 beta3 - -### Bug Fixes - -- **esptool**: Fix esp32c61 flash frequency config -- **esptool**: Fix incorrect chip version for esp32c5 -- **write_flash**: Verify if files will fit against the real flash size when possible -- **remote_ports**: Disable reset sequence when a socket is used -- **bitstring**: Restricted bitstring dependency to fix 32-bit compatibility -- **esp32_d0wdr2_v3**: Print correct chip name -- pass error message to exception in OTG mode -- **bin_image**: add check for ELF file segment when saving RAM segments -- **docs**: Add a note about entering manual bootloader mode -- **esp32c5**: Fix MAC reading for esptool -- Erase non-aligned bytes with --no-stub -- **esp32-c5**: Use a longer reset delay with usb-serial/jtag to stabilize boot-up -- **espefuse**: Use stub class if stub flasher is running -- Do not append SHA256 when `--ram-only-header` -- **elf2image**: add ELF flags to merge condition -- ram_only_header: pad flash segment to next boundary -- sort segments if ram_only_header is used -- **espefuse**: Fix efuse base addr for esp32c5 MP -- fix type annotation to comply with mypy -- **espefuse**: Fix burn_key for ECDSA_KEY, it can read pem file -- **secure_download_mode**: Disable secure boot detection and print more info -- **esptool**: clear boot control register on ESP32-S3 -- **intelhex**: catch unicode decode errors when converting hex to binary -- ROM doesn't attach in-package flash chips -- close file gracefully in espsecure -- Fixed glitches on RTS line when no_reset option on Windows -- **merge_bin**: treat files starting with colon as raw files -- Index image segments from 0 instead of 1 -- **read_flash**: add flash size arg to enable reading past 2MB without stub -- **read_flash**: flush transmit buffer less often to inrease throughput -- **esptool**: Proper alignment for SoCs with offset load -- ignore resetting on unsupported ports -- **esptool**: Remove the shebang from uf2_writer.py - -### Code Refactoring - -- Migrated esp_rfc2217_server into standalone subpackage -- **test/esptool**: Updated tests according to SHA recomputation for binary -- **style**: Comply with black>=24.0.0 +### ✨ New Features + +- **espefuse**: Supports wafer efuse versions for esp32c61 *(Konstantin Kondrashov - 0472846)* +- **esptool**: add new command SFDP read *(Xiao Xufeng - 92143ed)* +- **esptool**: Add option to retry connection in a loop *(Alfonso Acosta - 04045d6)* +- **efuse**: Updates efuse table for esp32c5 *(Konstantin Kondrashov - b3022fa)* +- **efuse**: Updates efuse table for esp32p4 *(Konstantin Kondrashov - 669a69f)* +- **esp32c61**: Added stub flasher support *(Jakub Kocka - e8b3911)* +- **cli**: add autocompletions *(Dmitriy Astapov - 7cc35e4)* +- **esptool**: allow picking UART by VID/PID/Name *(Richard Allen - 5dd3dcc)* +- **esp32c5**: Add USB-serial/JTAG stub support *(Jaroslav Burian - e170bcc)* +- **esp32c5**: Add UART stub support *(Konstantin Kondrashov - b199534)* +- **esptool**: Print key_purpose name for get_security_info cmd *(Konstantin Kondrashov - ccd8c72)* +- **espefuse**: Adds support extend efuse table by user CSV file *(Konstantin Kondrashov - 6bb2b92)* +- **espefuse**: Adds efuse dump formats: separated(default) and united(new) *(Konstantin Kondrashov - fc2856a)* +- **espefuse**: Adds incompatible eFuse settings check for S3 *(Konstantin Kondrashov - c244843)* +- **reset**: Apply reconnections to the whole reset sequence, not line transitions *(Radim Karniš - d49837e)* +- **reset**: Automatically reconnect if port disconnects during reset *(Andrew Leech - 9dc5dfb)* +- **esp32-p4**: Add ECO1 magic number *(Radim Karniš - d4d2153)* +- **espsecure**: Add support for secure boot v2 using ECDSA-P384 signatures *(harshal.patil - f014cad)* +- **write_flash**: retry flashing if chip disconnects *(Peter Dragun - a15089a)* +- **espefuse**: Allow filtering efuses based on command line arguments *(Jan Beran - bb52d36)* +- **esploader**: Enable context manager for esp instances *(Radim Karniš - d4c8cb3)* +- **espefuse**: Added check for correctness of written data *(Konstantin Kondrashov - d2bfaad)* +- **espefuse**: Improves help for burn_efuse cmd *(Konstantin Kondrashov - ef8ee8a)* +- **esp32s3**: clear boot control register on hard reset *(Peter Dragun - 1c355f9)* +- **esp32-p4**: add spi-connection restriction to ROM class *(Peter Dragun - dad0edc)* +- **espefuse**: Updates efuses for C5 and C61 *(Konstantin Kondrashov - e34df69)* +- **esp32c61**: add c61 basic flash support (no_stub) *(wanlei - ef4d8a7)* +- **esp32c5**: skipped the stub check for esp32c5 mp *(laokaiyao - a773e6b)* +- **esp32c5**: base support of esp32c5 mp (no stub) *(laokaiyao - e414cef)* +- **cmds/write_flash**: Recalculated SHA digest for image binary *(Jakub Kocka - 3b0939c)* +- **esptool**: Adds wafer and pkg versions *(Konstantin Kondrashov - 6c5cfd6)* +- **espefuse**: Update adc_info commands for all chips *(Konstantin Kondrashov - 31eb15b)* +- **espefuse**: Adds new efuses for esp32p4 *(Konstantin Kondrashov - 31477fb)* +- **espefuse**: Allow the espefuse.py to work when coding scheme == 3 *(Francisco Blas (klondike) Izquierdo Riera - 1e79f25)* +- **err_defs**: Add ROM bootloader flash error definitions *(radim.karnis - 2d8a3ad)* +- **esp32p4**: Enable USB-serial/JTAG in flasher stub *(Peter Dragun - 96a5c21)* +- **espefuse**: Postpone some efuses to burn them at the very end *(KonstantinKondrashov - bdeec68)* +- **espefuse**: check_error --recover chip even if there are num_errors *(KonstantinKondrashov - f72b5ad)* +- **espefuse**: Adds new efuses for esp32c6 and esp32h2 *(KonstantinKondrashov - 16e4fae)* +- **esp32c5**: add target esp32c5 beta3 *(laokaiyao - d9a6660)* +- add UF2 IDs for ESP32-C5 and ESP32-C61 *(Peter Dragun - cf6d94e)* +- Added warning when secure boot enabled *(Jakub Kocka - 8d26375)* +- print flash voltage in flash_id command *(Peter Dragun - 6393d6b)* +- Use ruff instead of flake8 and black both in pre-commit and CI *(Jan Beran - 1d5fcb3)* +- add advisory port locking *(Peter Dragun - 8ad6d57)* + +### 🐛 Bug Fixes + +- **esp32c2**: Add esp32c2 eco4 rom magic value *(Jiang Guang Ming - 3434433)* +- **packaging**: Correctly exclude the unwanted sub/modules *(Karolina Surma - 908d0b5)* +- **esptool**: Fix esp32c61 flash frequency config *(C.S.M - 6edafea)* +- **esptool**: Fix incorrect chip version for esp32c5 *(Konstantin Kondrashov - 138660b)* +- **write_flash**: Verify if files will fit against the real flash size when possible *(Radim Karniš - 1693449)* +- **remote_ports**: Disable reset sequence when a socket is used *(Radim Karniš - 28556fb)* +- **bitstring**: Restricted bitstring dependency to fix 32-bit compatibility *(Jakub Kocka - 4f7e223)* +- **esp32_d0wdr2_v3**: Print correct chip name *(Radim Karniš - dfd61e2)* +- **bin_image**: add check for ELF file segment when saving RAM segments *(Peter Dragun - 6e8632d)* +- **docs**: Add a note about entering manual bootloader mode *(Roland Dobai - 4d0c7d9)* +- **esp32c5**: Fix MAC reading for esptool *(Konstantin Kondrashov - 2b0ec7a)* +- **esp32-c5**: Use a longer reset delay with usb-serial/jtag to stabilize boot-up *(C.S.M - 1059ec7)* +- **espefuse**: Use stub class if stub flasher is running *(Radim Karniš - 67d66a0)* +- **elf2image**: add ELF flags to merge condition *(Marek Matej - e87cc3e)* +- **espefuse**: Fix efuse base addr for esp32c5 MP *(Konstantin Kondrashov - 248dc9a)* +- **espefuse**: Fix burn_key for ECDSA_KEY, it can read pem file *(Konstantin Kondrashov - 450db24)* +- **secure_download_mode**: Disable secure boot detection and print more info *(Radim Karniš - 1dc3c8b)* +- **esptool**: clear boot control register on ESP32-S3 *(Peter Dragun - 0215786)* +- **intelhex**: catch unicode decode errors when converting hex to binary *(Peter Dragun - a2bdaa2)* +- **merge_bin**: treat files starting with colon as raw files *(Peter Dragun - 2c0a5da)* +- **read_flash**: add flash size arg to enable reading past 2MB without stub *(Peter Dragun - f1eb65f)* +- **read_flash**: flush transmit buffer less often to inrease throughput *(Peter Dragun - 8ce5ed3)* +- **esptool**: Proper alignment for SoCs with offset load *(Marek Matej - 17866a5)* +- **esptool**: Remove the shebang from uf2_writer.py *(Karolina Surma - 45fbcdd)* +- pass error message to exception in OTG mode *(Peter Dragun - c266fdd)* +- Erase non-aligned bytes with --no-stub *(Jaroslav Burian - c984aa9)* +- Do not append SHA256 when `--ram-only-header` *(Tiago Medicci Serrano - 5d9d5be)* +- ram_only_header: pad flash segment to next boundary *(Sylvio Alves - 4394a65)* +- sort segments if ram_only_header is used *(Sylvio Alves - 4c5874a)* +- fix type annotation to comply with mypy *(Peter Dragun - 55b338a)* +- ROM doesn't attach in-package flash chips *(Jakub Kocka - bc9f2a6)* +- close file gracefully in espsecure *(gnought - 2381711)* +- Fixed glitches on RTS line when no_reset option on Windows *(Jakub Kocka - 956557b)* +- Index image segments from 0 instead of 1 *(Jan Beran - b5939da)* +- ignore resetting on unsupported ports *(Peter Dragun - e948993)* + +### 📖 Documentation + +- **troubleshooting**: Add info about debugging in USB-Serial/JTAG and USB-OTG modes *(Radim Karniš - 3a74f62)* +- **troubleshooting**: Mention needed permissions to the serial port on Linux *(Radim Karniš - 8e39ef6)* +- **troubleshooting**: Mention the ESP Hardware Design Guidelines docs *(Radim Karniš - 74ce286)* +- **flashing**: Fixed a typo in /docs/en/esptool/flashing-firmware.rst *(Green - 9f46568)* +- **sphinx-lint**: Add previous commit to .git-blame-ignore-revs *(Jan Beran - c750549)* +- **sphinx-lint**: Fix issues reported by sphinx-lint before adding it to pre-commit *(Jan Beran - 6282f98)* +- **sphinx-lint**: Add sphinx-lint to pre-commit, GH and GL pipelines *(Jan Beran - 1de1a26)* +- **esptool**: Reflect change from flake8 and black to ruff *(Jan Beran - 9f1bde4)* +- add note about Intel Hex merging limitations *(Peter Dragun - d83dd3b)* +- Updated documentation to reflect changes of SHA256 digest recomputation *(Jakub Kocka - 6c28df3)* +- add esp32p4 target to docs *(Peter Dragun - 4a6ad55)* +- Correct bootloader offsets *(radim.karnis - 79978c0)* +- Add instructions on how to update *(radim.karnis - d448851)* + +### 🔧 Code Refactoring + +- **test/esptool**: Updated tests according to SHA recomputation for binary *(Jakub Kocka - 598b703)* +- **style**: Comply with black>=24.0.0 *(radim.karnis - 5ad3c48)* +- Migrated esp_rfc2217_server into standalone subpackage *(Jakub Kocka - 9b24215)* + +--- ## v4.7.0 (2023-12-13) -### New Features - -- **test_esptool**: Added test for embedded and detected flash size match -- **spi_connection**: Support --spi-connection on all chips -- **espefuse**: Support XTS_AES_256_KEY key_purpose for ESP32P4 -- **xip_psram**: support xip psram feature on esp32p4 -- add support for intel hex format -- **esp32p4**: Stub flasher support -- **elf2image**: add ram-only-header argument -- **rfc2217_server**: Add hard reset sequence -- **espefuse**: Adds efuse ADC calibration data for ESP32H2 -- **espefuse**: Update the way to complete the operation -- add support for get_security_info on esp32c3 ECO7 -- **loader**: Added hints for some serial port issues when rising port error -- Add support for Python 3.12 -- **esp32c3**: Support ECO6 and ECO7 magic numbers -- **merge_bin**: add support for uf2 format -- **esp32-s3**: Support >16MB quad flash chips -- **efuse**: Update key purpose table and tests -- **efuse**: ESP32P4 adds ecdsa_key support -- **espefuse**: Add support for esp32p4 chip -- **esptool**: added target to esp32p4 -- **espsecure**: Allow prompting for HSM PIN in read_hsm_config -- **esptool**: Add new packages for ESP32C3 and flash efuses -- **esptool**: Add tests for get_chip_features -- **esptool**: Add PICO package for ESP32S3 and flash/psram efuses -- **get_security_info**: Improved the output format and added more details - -### Bug Fixes - -- **esp32c2**: Added get_flash_cap and get_flash_vendor -- **testloadram**: Windows assertion error -- fixed exit() to be used from right module -- **esp32c2**: Recommend using higher baud rate if connection fails -- **test_esptool**: Fixed connection issue on Windows -- **esptool**: Rephrase the --ram-only-header command message -- **load_ram**: check for overlaps in bss section -- **tests/intelhex**: make sure file is closed on Windows -- **spi_connection**: Unattach previously attached SPI flash -- **espefuse**: Fix ECDSA_FORCE_USE_HARDWARE_K for ECDSA key (esp32h2) -- **loader**: Could not open serial port message adjusted -- **flasher_stub**: fix usb-serial-jtag enabled non-related intr source -- **bin_image**: Check only ELF sections when searching for .flash.appdesc -- **danger-github**: Fir Danger GitHub token permission -- Fix redirection of STDOUT -- **autodetection**: Remove the ESP32-S2 ROM class from get_security_info autodetection -- assert in esp32 exclusive workaround -- **elf2image**: fix text/rodata mapping overlap issue on uni-idrom bus chips -- **dangerGH**: Update token permissions - allow Danger to add comments to PR -- **expand file args**: Correctly print the expanded command -- **esp32-c2**: Enable flashing in secure download mode - -### Code Refactoring - -- **stub_flasher**: Cleanup, make adding new targets easier +### ✨ New Features + +- **test_esptool**: Added test for embedded and detected flash size match *(Jakub Kocka - c0ea74a)* +- **spi_connection**: Support --spi-connection on all chips *(radim.karnis - 1a38293)* +- **espefuse**: Support XTS_AES_256_KEY key_purpose for ESP32P4 *(KonstantinKondrashov - a91eee1)* +- **xip_psram**: support xip psram feature on esp32p4 *(Armando - 1b350ce)* +- **esp32p4**: Stub flasher support *(radim.karnis - d266645)* +- **elf2image**: add ram-only-header argument *(Almir Okato - da28460)* +- **rfc2217_server**: Add hard reset sequence *(20162026 - d66de5c)* +- **espefuse**: Adds efuse ADC calibration data for ESP32H2 *(KonstantinKondrashov - 2a57d6c)* +- **espefuse**: Update the way to complete the operation *(KonstantinKondrashov - c8d688d)* +- **loader**: Added hints for some serial port issues when rising port error *(Jakub Kocka - d61da77)* +- **esp32c3**: Support ECO6 and ECO7 magic numbers *(radim.karnis - 6943c5d)* +- **merge_bin**: add support for uf2 format *(Peter Dragun - 3d899b2)* +- **esp32-s3**: Support >16MB quad flash chips *(radim.karnis - 67a91cb)* +- **efuse**: Update key purpose table and tests *(KonstantinKondrashov - cb5e850)* +- **efuse**: ESP32P4 adds ecdsa_key support *(KonstantinKondrashov - 3654267)* +- **espefuse**: Add support for esp32p4 chip *(KonstantinKondrashov - 8273916)* +- **esptool**: added target to esp32p4 *(Armando - 654e626)* +- **espsecure**: Allow prompting for HSM PIN in read_hsm_config *(Richard Retanubun - ab25fc1)* +- **esptool**: Add new packages for ESP32C3 and flash efuses *(KonstantinKondrashov - 8f37762)* +- **esptool**: Add tests for get_chip_features *(KonstantinKondrashov - d5bb1ee)* +- **esptool**: Add PICO package for ESP32S3 and flash/psram efuses *(KonstantinKondrashov - b70ead2)* +- **get_security_info**: Improved the output format and added more details *(Aditya Patwardhan - 9b95de8)* +- add support for intel hex format *(Peter Dragun - 7074bed)* +- add support for get_security_info on esp32c3 ECO7 *(Peter Dragun - 20565a0)* +- Add support for Python 3.12 *(radim.karnis - ef02d52)* + +### 🐛 Bug Fixes + +- **esp32c2**: Added get_flash_cap and get_flash_vendor *(Jakub Kocka - b8dd74d)* +- **testloadram**: Windows assertion error *(Jakub Kocka - cd51bbc)* +- **esp32c2**: Recommend using higher baud rate if connection fails *(Jakub Kocka - ef0c91f)* +- **test_esptool**: Fixed connection issue on Windows *(Jakub Kocka - 4622bb2)* +- **esptool**: Rephrase the --ram-only-header command message *(Marek Matej - da4a486)* +- **load_ram**: check for overlaps in bss section *(Peter Dragun - 3a82d7a)* +- **tests/intelhex**: make sure file is closed on Windows *(Peter Dragun - 900d385)* +- **spi_connection**: Unattach previously attached SPI flash *(radim.karnis - afaa7d2)* +- **espefuse**: Fix ECDSA_FORCE_USE_HARDWARE_K for ECDSA key (esp32h2) *(KonstantinKondrashov - f607f19)* +- **loader**: Could not open serial port message adjusted *(Jakub Kocka - 0d3a077)* +- **flasher_stub**: fix usb-serial-jtag enabled non-related intr source *(wuzhenghui - 3f2dc6f)* +- **bin_image**: Check only ELF sections when searching for .flash.appdesc *(radim.karnis - ffaf6db)* +- **danger-github**: Fir Danger GitHub token permission *(Tomas Sebestik - c0df9b7)* +- **autodetection**: Remove the ESP32-S2 ROM class from get_security_info autodetection *(radim.karnis - 3d8c304)* +- **elf2image**: fix text/rodata mapping overlap issue on uni-idrom bus chips *(wuzhenghui - c48523e)* +- **dangerGH**: Update token permissions - allow Danger to add comments to PR *(Tomas Sebestik - 6b4786a)* +- **expand file args**: Correctly print the expanded command *(radim.karnis - 2bea6f4)* +- **esp32-c2**: Enable flashing in secure download mode *(radim.karnis - e862e10)* +- fixed exit() to be used from right module *(Jakub Kocka - d1610a9)* +- Fix redirection of STDOUT *(radim.karnis - 9585c0e)* +- assert in esp32 exclusive workaround *(wuzhenghui - 5b69e07)* + +### 📖 Documentation + +- **advanced-topics**: Fixed strapping pin for Automatic Bootloader section *(Jakub Kocka - 590c2c6)* +- **serial-protocol**: add images and flowchart *(Peter Dragun - e99c114)* +- **boot_mode_selection**: Correct secondary strapping pin boot mode levels *(radim.karnis - 3b38e79)* +- **troubleshooting**: Explain issues when flashing with USB-Serial/JTAG or USB-OTG *(radim.karnis - 2a399a0)* +- **basic-commands**: added note for PowerShell users for merge_bin command *(Jakub Kocka - dc8a337)* +- Add other resources page *(radim.karnis - cc6c4ce)* + +### 🔧 Code Refactoring + +- **stub_flasher**: Cleanup, make adding new targets easier *(radim.karnis - fb7f4db)* + +--- ## v4.6.2 (2023-06-12) -### Bug Fixes - -- **CH9102F**: Suggest to install new serial drivers if writing to RAM fails -- **compressed upload**: Accept short data blocks with only Adler-32 bytes - -## v4.6.1 (2023-06-01) - -### Bug Fixes - -- **ESP32-S3**: Correct RTC WDT registers to fix resets during flashing - -## v4.6 (2023-05-29) - -### New Features - -- **esptool**: add option to dump whole flash based on detected size - -### Bug Fixes +### 🐛 Bug Fixes -- inconsistent usage of dirs separator -- USB-JTAG-Serial PID detection error -- Set flash parameters even with --flash_size keep -- **ESP32-C6**: Fix get_pkg_version and get_{major,minor}_chip_version +- **CH9102F**: Suggest to install new serial drivers if writing to RAM fails *(radim.karnis - f4b5914)* +- **compressed upload**: Accept short data blocks with only Adler-32 bytes *(radim.karnis - d984647)* -## v4.5.1 (2023-02-28) - -### Bug Fixes - -- **ESP32-S3**: Temporarily disable increasing CPU freq -- Unknown chip (ID or magic number) error -- **ESP32-S3**: Lower CPU freq to improve flasher stub stability -- **rfc2217_server**: Use new reset sequences - -## v4.5 (2023-02-10) - -### New Features +### 📖 Documentation -- **stub**: Add ESP32-S3 octal flash support -- **esp32h2**: Enable USB-JTAG/Serial mode in the stub flasher -- **bootloader reset**: Allow custom reset strategy setting with a config file -- Allow configuration with a config file -- **bootloader reset**: Tighter transitions on Unix systems -- **ci**: Publish development releases with custom pipeline -- **esp32c6 stub**: Increase CPU frequency and write/read speeds over USB-JTAG/Serial -- **esp32c6 stub**: Enable USB-JTAG/Serial -- **flash_id**: Print the flash type if available for the chip +- **boot-log**: fix list formatting *(Peter Dragun - b137d3d)* +- add c2, c6 and h2 as build targets *(Peter Dragun - 590fb55)* +- add explanation for flash_id example to avoid confusion *(Peter Dragun - fbe8066)* -### Bug Fixes +--- -- **cmds**: Make clear that flash type is from eFuse and not detection -- **load config file**: Sort unknown config options -- **esp32c6**: Workaround for bad MSPI frequency in HS mode -- **flasher_stub**: Correct boundaries for SPIWrite4B and SPIRead4B -- **secure download mode**: Reconnect if ROM refuses to respond -- **secure download mode**: Fix SDM detection on S2/S3 -- **ci**: Merge two "ci" directories and build_tools into one -- **ci**: The development release job should not run by default -- **setup**: Use latest reedsolo package which can be installed with Python3.10 and Cython -- **write_flash**: Fix `--erase-all` option -- **espefuse**: Close serial port even when espefuse fails -- **espefuse**: Fix compatibility with Bitstring>=4 - -### Code Refactoring - -- Comply with black 23.1 style -- Optimize unnecessary chip interrogations -- **connection attempt**: Decouple reset sequence settings - -## v4.4 (2022-11-21) +## v4.6.1 (2023-06-01) -### New Features +### ✨ New Features -- **flasher_stub**: Increase CPU frequency and write/read speeds over native USB (USB-OTG) -- **flasher_stub**: Increase CPU frequency and write/read speeds over USB-JTAG/Serial -- Readable error message for serial-related issues -- Detect Guru Meditation errors +- **esptool**: add option to dump whole flash based on detected size *(Peter Dragun - 049baaa)* -### Bug Fixes +### 🐛 Bug Fixes -- Add workaround for breaking changes of bitstring==4 -- close unused ports while get_default_connected_device +- **ESP32-S3**: Correct RTC WDT registers to fix resets during flashing *(radim.karnis - 6fd91af)* +- **ESP32-C6**: Fix get_pkg_version and get\_{major,minor}\_chip_version *(XiNGRZ - 555458c)* +- inconsistent usage of dirs separator *(Massimiliano Montagni - f558f22)* +- USB-JTAG-Serial PID detection error *(Dean Gardiner - 9a719f4)* +- Set flash parameters even with --flash_size keep *(radim.karnis - 0e9c85e)* -## v4.3 (2022-09-14) +### 📖 Documentation -### New Features +- **Boot log**: Add all esp targets to cover boot troubleshooting *(Peter Dragun - 5892496)* -- **image_info**: Print application information if possible -- Add Macronix flash memory density definitions -- **write_flash**: Prevent flashing incompatible images -- Recover from serial errors when flashing -- Add stub flasher error messages definitions -- **image_info**: Image type autodetection +--- -### Code Refactoring +## v4.5.1 (2023-02-28) -- **elf2image**: Simplify bootloader image selection +### ✨ New Features + +- **stub**: Add ESP32-S3 octal flash support *(Roland Dobai - b746aa7)* +- **esp32h2**: Enable USB-JTAG/Serial mode in the stub flasher *(radim.karnis - cc06208)* +- **bootloader reset**: Allow custom reset strategy setting with a config file *(radim.karnis - a8586d0)* +- **bootloader reset**: Tighter transitions on Unix systems *(radim.karnis - 353cefc)* +- **ci**: Publish development releases with custom pipeline *(Roland Dobai - 3a77f1f)* +- **esp32c6 stub**: Increase CPU frequency and write/read speeds over USB-JTAG/Serial *(radim.karnis - 180695e)* +- **esp32c6 stub**: Enable USB-JTAG/Serial *(radim.karnis - b04cc52)* +- **flash_id**: Print the flash type if available for the chip *(Roland Dobai - b25606b)* +- **flasher_stub**: Increase CPU frequency and write/read speeds over native USB (USB-OTG) *(radim.karnis - 52278a9)* +- **flasher_stub**: Increase CPU frequency and write/read speeds over USB-JTAG/Serial *(radim.karnis - dccf4df)* +- **image_info**: Print application information if possible *(radim.karnis - 82bfe98)* +- **write_flash**: Prevent flashing incompatible images *(radim.karnis - 395fcb0)* +- **image_info**: Image type autodetection *(radim.karnis - 791a20b)* +- Allow configuration with a config file *(radim.karnis - 3ad680a)* +- Readable error message for serial-related issues *(radim.karnis - 1082852)* +- Detect Guru Meditation errors *(radim.karnis - 6fac261)* +- Add Macronix flash memory density definitions *(radim.karnis - 3190894)* +- Recover from serial errors when flashing *(radim.karnis - 2fb8d45)* +- Add stub flasher error messages definitions *(radim.karnis - 66502d5)* + +### 🐛 Bug Fixes + +- **ESP32-S3**: Temporarily disable increasing CPU freq *(radim.karnis - 23a5095)* +- **ESP32-S3**: Lower CPU freq to improve flasher stub stability *(radim.karnis - 7b28699)* +- **rfc2217_server**: Use new reset sequences *(radim.karnis - 4f13cf6)* +- **cmds**: Make clear that flash type is from eFuse and not detection *(Roland Dobai - caeab98)* +- **load config file**: Sort unknown config options *(radim.karnis - cc80ecd)* +- **esp32c6**: Workaround for bad MSPI frequency in HS mode *(wuzhenghui - 4738ef7)* +- **flasher_stub**: Correct boundaries for SPIWrite4B and SPIRead4B *(Roland Dobai - 21e5914)* +- **secure download mode**: Reconnect if ROM refuses to respond *(radim.karnis - 869740a)* +- **secure download mode**: Fix SDM detection on S2/S3 *(radim.karnis - b67e557)* +- **ci**: Merge two "ci" directories and build_tools into one *(Roland Dobai - 2ed1fc1)* +- **ci**: The development release job should not run by default *(Roland Dobai - 3f3a2d3)* +- **setup**: Use latest reedsolo package which can be installed with Python3.10 and Cython *(Roland Dobai - 5490b0b)* +- **write_flash**: Fix `--erase-all` option *(radim.karnis - d0af65f)* +- **espefuse**: Close serial port even when espefuse fails *(radim.karnis - 26df171)* +- **espefuse**: Fix compatibility with Bitstring>=4 *(Roland Dobai - ee27a64)* +- Unknown chip (ID or magic number) error *(radim.karnis - 11e6425)* +- Add workaround for breaking changes of bitstring==4 *(Roland Dobai - 09e41df)* +- close unused ports while get_default_connected_device *(Fu Hanxi - 76f491e)* + +### 📖 Documentation + +- **tests**: Add test suite description and instructions *(radim.karnis - 943b997)* +- **serial port**: Update basic-options with more linux instructions *(Robin Gower - b37496f)* +- espsecure remote signing using a HSM broken link fix *(harshal.patil - 0095a26)* +- Update serial protocol description *(radim.karnis - f4ed949)* +- Describe --chip option, fix small typos *(radim.karnis - 2eff1be)* + +### 🔧 Code Refactoring + +- **connection attempt**: Decouple reset sequence settings *(radim.karnis - cee66a2)* +- **elf2image**: Simplify bootloader image selection *(radim.karnis - 2e95f66)* +- Comply with black 23.1 style *(radim.karnis - ea61f8f)* +- Optimize unnecessary chip interrogations *(radim.karnis - f3437a3)* + +--- + +
+ + + Commitizen Espressif plugin + +
+ Espressif Systems CO LTD. (2025) +
+
diff --git a/tools/esptool_py/CONTRIBUTING.rst b/tools/esptool_py/CONTRIBUTING.rst index de0ca91ed8..c946dc3f4e 100644 --- a/tools/esptool_py/CONTRIBUTING.rst +++ b/tools/esptool_py/CONTRIBUTING.rst @@ -1,19 +1,19 @@ Contributions Guide =================== -We welcome contributions to the ``esptool.py`` project! +We welcome contributions to the ``esptool`` project! How to Contribute ----------------- -Contributions to ``esptool.py`` - fixing bugs, adding features, adding documentation - are welcome. We accept contributions via `Github Pull Requests `_. +Contributions to ``esptool`` - fixing bugs, adding features, adding documentation - are welcome. We accept contributions via `Github Pull Requests `_. .. _development-setup: Development Setup ----------------- -Development mode allows you to run the latest development version from the `esptool.py repository on GitHub `_. +Development mode allows you to run the latest development version from the `esptool repository on GitHub `_. .. code-block:: sh @@ -21,11 +21,11 @@ Development mode allows you to run the latest development version from the `espt $ cd esptool $ pip install --user -e . -This will install ``esptool.py``’s dependencies and create some executable script wrappers in the user’s ``bin`` directory. The wrappers will run the scripts found in the git working directory directly, so any time the working directory contents change it will pick up the new versions. +This will install ``esptool``'s dependencies and create some executable script wrappers in the user's ``bin`` directory. The wrappers will run the scripts found in the git working directory directly, so any time the working directory contents change it will pick up the new versions. -It’s also possible to run the scripts directly from the working directory with this Development Mode installation. +It's also possible to run the scripts directly from the working directory with this Development Mode installation. -To also install additional tools needed for actually developing and testing ``esptool.py``, run this command to install a development copy of ``esptool.py`` *plus* packages useful for development: +To also install additional tools needed for actually developing and testing ``esptool``, run this command to install a development copy of ``esptool`` *plus* packages useful for development: :: @@ -36,7 +36,7 @@ To also install additional tools needed for actually developing and testing ``es Reporting Issues ---------------- -Please report bugs in ``esptool.py`` if you find them. However, before reporting a bug please check through the following: +Please report bugs in ``esptool`` if you find them. However, before reporting a bug please check through the following: * `Troubleshooting Guide `_ - common problems and known issues. @@ -44,6 +44,8 @@ Please report bugs in ``esptool.py`` if you find them. However, before reporting If you don’t find anything, please `open a new issue `_. +.. _feature-requests: + Sending Feature Requests ------------------------ @@ -56,7 +58,7 @@ Before Contributing Before sending us a Pull Request, please consider this list of points: -* Have you tried running ``esptool.py`` test suite locally? +* Have you tried running ``esptool`` test suite locally? * Is the code adequately commented for people to understand how it is structured? @@ -71,9 +73,9 @@ Before sending us a Pull Request, please consider this list of points: Code Style & Static Analysis ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Please follow these coding standards when writing code for ``esptool.py``: +Please follow these coding standards when writing code for ``esptool``: -Pre-commit checks +Pre-Commit Checks """"""""""""""""" `pre-commit `_ is a framework for managing pre-commit hooks. These hooks help to identify simple issues before committing code for review. @@ -90,13 +92,13 @@ On the first commit ``pre-commit`` will install the hooks, subsequent checks wil Conventional Commits """""""""""""""""""" -``esptool.py`` complies with the `Conventional Commits standard `_. Every commit message is checked with `Conventional Precommit Linter `_, ensuring it adheres to the standard. +``esptool`` complies with the `Conventional Commits standard `_. Every commit message is checked with `Conventional Precommit Linter `_, ensuring it adheres to the standard. Ruff """" -``esptool.py`` is `PEP8 `_ compliant and enforces this style guide. For compliance checking, we use `ruff `_. +``esptool`` is `PEP8 `_ compliant and enforces this style guide. For compliance checking, we use `ruff `_. ``Ruff`` also auto-format files in the same style as previously used ``black``. @@ -104,13 +106,13 @@ Ruff When you submit a Pull Request, the GitHub Actions automated build system will run automated checks using these tools. -Shinx-lint -"""""""""" +Sphinx-Lint +""""""""""" The documentation is checked for stylistic and formal issues by ``sphinx-lint``. -Codespell check +Codespell Check """"""""""""""" This repository utilizes an automatic `spell checker `_ integrated into the pre-commit process. If any spelling issues are detected, the recommended corrections will be applied automatically to the file, ready for commit. @@ -120,22 +122,22 @@ In the event of false positives, you can adjust the configuration in the `.codes Automated Integration Tests ^^^^^^^^^^^^^^^^^^^^^^^^^^^ -The test directory contains a `pytest `_ integration suite with some integration tests for ``esptool.py``, ``espefuse.py``, and ``espsecure.py``. +The test directory contains a `pytest `_ integration suite with some integration tests for ``esptool``, ``espefuse``, and ``espsecure``. -It is necessary to have ``esptool.py`` installed (see `Development Setup`_) in your environment in order to run these tests. +It is necessary to have ``esptool`` installed (see `Development Setup`_) in your environment in order to run these tests. The following tests run automatically by GitHub Actions for each Pull Request. You can run them locally to check for regressions in the respective functionality: * ``test_imagegen.py`` tests the ``elf2image`` command -* ``test_image_info.py`` tests the ``image_info`` command -* ``test_mergebin.py`` tests the ``merge_bin`` command -* ``test_modules.py`` tests the modules used by ``esptool.py`` for regressions -* ``test_espsecure.py`` tests ``espsecure.py`` functionality -* ``test_espsecure_hsm.py`` tests support of external HSM signing in ``espsecure.py``. These tests require additional prerequisites, see ``SoftHSM2 setup`` in the `tests workflow definition `_ for more information. +* ``test_image_info.py`` tests the ``image-info`` command +* ``test_mergebin.py`` tests the ``merge-bin`` command +* ``test_modules.py`` tests the modules used by ``esptool`` for regressions +* ``test_espsecure.py`` tests ``espsecure`` functionality +* ``test_espsecure_hsm.py`` tests support of external HSM signing in ``espsecure``. These tests require additional prerequisites, see ``SoftHSM2 setup`` in the `tests workflow definition `_ for more information. The following tests are not run automatically by GitHub Actions, because they need real connected hardware. Therefore, they need to be run locally in a command line: -* ``test_esptool.py`` contains integration tests for ``esptool.py`` and needs to be run against real Espressif hardware with the following format: +* ``test_esptool.py`` contains integration tests for ``esptool`` and needs to be run against real Espressif hardware with the following format: ``pytest test_esptool.py --port --chip --baud `` @@ -143,7 +145,7 @@ The following tests are not run automatically by GitHub Actions, because they ne ``pytest test_esptool.py --port /dev/ttyUSB0 --chip esp32 --baud 230400`` - Or to run the TestFlashing suite only (using the pytest ``-k`` option to select tests based on their name) on an ESP8266 board connected to /dev/ttyUSB2, at 460800bps: + Or to run the ``TestFlashing`` suite only (using the pytest ``-k`` option to select tests based on their name) on an ESP8266 board connected to /dev/ttyUSB2, at 460800bps: ``pytest test_esptool.py --port /dev/ttyUSB2 --chip esp8266 --baud 460800 -k TestFlashing`` @@ -151,15 +153,15 @@ The following tests are not run automatically by GitHub Actions, because they ne Some tests might fail at higher baud rates on some hardware. -* ``test_esptool_sdm.py`` contains integration tests for ``esptool.py`` with chips in secure download mode. It needs to be run against real Espressif hardware (with active SDM). The command line format is the same as for ``test_esptool.py``. +* ``test_esptool_sdm.py`` contains integration tests for ``esptool`` with chips in secure download mode. It needs to be run against real Espressif hardware (with active SDM). The command line format is the same as for ``test_esptool.py``. The following tests are not run automatically by GitHub Actions, but can be run locally in a command line: -* ``test_espefuse.py`` tests ``espefuse.py`` functionality. To run it: +* ``test_espefuse.py`` tests ``espefuse`` functionality. To run it: ``pytest test_espefuse.py --chip `` - These test use the ``--virt`` virtual mode of ``espefuse.py`` to safely test the functionality without a connection to a chip and without the possibility of affecting the actual eFuses in a real hardware. + These test use the ``--virt`` virtual mode of ``espefuse`` to safely test the functionality without a connection to a chip and without the possibility of affecting the actual eFuses in a real hardware. .. warning:: @@ -171,9 +173,7 @@ The whole test suite (without the tests needing an actual hardware or installati Pull Request Process -------------------- -.. note:: - - If you are developing the stub flasher and plan to send a pull request, please use the latest toolchains available. +If you would like to contribute to the flasher stub, please see the `Flasher stub repository `_. After you open the Pull Request, there will probably be some discussion in the comments field of the request itself. diff --git a/tools/esptool_py/MANIFEST.in b/tools/esptool_py/MANIFEST.in index f135efaa6f..38de7b16ca 100644 --- a/tools/esptool_py/MANIFEST.in +++ b/tools/esptool_py/MANIFEST.in @@ -3,7 +3,7 @@ include LICENSE include esptool/targets/stub_flasher/1/* include esptool/targets/stub_flasher/2/* include espefuse/efuse_defs/*.yaml -# sdist includes test/test*.py by default, but esptool.py tests +# sdist includes test/test*.py by default, but esptool tests # are so far only intended to run from the git repo itself prune test prune .github diff --git a/tools/esptool_py/README.md b/tools/esptool_py/README.md index afb83bc198..40815f468e 100644 --- a/tools/esptool_py/README.md +++ b/tools/esptool_py/README.md @@ -1,20 +1,20 @@ -# esptool.py +# esptool -A Python-based, open-source, platform-independent utility to communicate with the ROM bootloader in Espressif chips. +A Python-based, open-source, platform-independent serial utility for flashing, provisioning, and interacting with Espressif SoCs. [![Test esptool](https://github.com/espressif/esptool/actions/workflows/test_esptool.yml/badge.svg?branch=master)](https://github.com/espressif/esptool/actions/workflows/test_esptool.yml) [![Build esptool](https://github.com/espressif/esptool/actions/workflows/build_esptool.yml/badge.svg?branch=master)](https://github.com/espressif/esptool/actions/workflows/build_esptool.yml) ## Documentation -Visit the [documentation](https://docs.espressif.com/projects/esptool/) or run `esptool.py -h`. +Visit the [documentation](https://docs.espressif.com/projects/esptool/) or run `esptool -h`. ## Contribute -If you're interested in contributing to esptool.py, please check the [contributions guide](https://docs.espressif.com/projects/esptool/en/latest/contributing.html). +If you're interested in contributing to esptool, please check the [contributions guide](https://docs.espressif.com/projects/esptool/en/latest/contributing.html). ## About -esptool.py was initially created by Fredrik Ahlberg (@[themadinventor](https://github.com/themadinventor/)), and later maintained by Angus Gratton (@[projectgus](https://github.com/projectgus/)). It is now supported by Espressif Systems. It has also received improvements from many members of the community. +esptool was initially created by Fredrik Ahlberg (@[themadinventor](https://github.com/themadinventor/)), and later maintained by Angus Gratton (@[projectgus](https://github.com/projectgus/)). It is now supported by Espressif Systems. It has also received improvements from many members of the community. ## License diff --git a/tools/esptool_py/ci/download_flasher_stubs.py b/tools/esptool_py/ci/download_flasher_stubs.py index 8f34c41296..935e8f2a12 100755 --- a/tools/esptool_py/ci/download_flasher_stubs.py +++ b/tools/esptool_py/ci/download_flasher_stubs.py @@ -1,6 +1,6 @@ #!/usr/bin/env python # -# SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD +# SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD # SPDX-License-Identifier: GPL-2.0-or-later import glob @@ -12,42 +12,43 @@ "STUB_SET_VERSION": "1", "DOWNLOAD_URL": "https://github.com/espressif/esptool-legacy-flasher-stub/releases/download", "TAG_URL": "https://github.com/espressif/esptool-legacy-flasher-stub/releases/tag", - "VERSION": "v1.3.0", + "VERSION": "v1.6.0", "FILE_LIST": ( "esp32", "esp32c2", "esp32c3", "esp32c5", - "esp32c5beta3", "esp32c6", "esp32c61", - "esp32c6beta", "esp32h2", - "esp32h2beta1", - "esp32h2beta2", "esp32p4", "esp32s2", "esp32s3", - "esp32s3beta2", "esp8266", ), - "LICENSE": "released as Free Software under GNU General Public License Version 2 or later", + "LICENSE": "released as Free Software under GNU General Public License " + "Version 2 or later", }, { "STUB_SET_VERSION": "2", - "DOWNLOAD_URL": "https://github.com/esp-rs/esp-flasher-stub/releases/download", - "TAG_URL": "https://github.com/esp-rs/esp-flasher-stub/releases/tag", - "VERSION": "v0.3.0", + "DOWNLOAD_URL": "https://github.com/espressif/esp-flasher-stub/releases/download", + "TAG_URL": "https://github.com/espressif/esp-flasher-stub/releases/tag", + "VERSION": "v0.1.0", "FILE_LIST": ( "esp32", "esp32c2", "esp32c3", + "esp32c5", "esp32c6", + "esp32c61", "esp32h2", + "esp32p4", "esp32s2", "esp32s3", + "esp8266", ), - "LICENSE": "dual licensed under the Apache License Version 2.0 or the MIT license", + "LICENSE": "dual licensed under the Apache License Version 2.0 or the MIT " + "license", }, ) @@ -56,16 +57,17 @@ README_TEMPLATE = """# Licensing The binaries in JSON format distributed in this directory are {LICENSE}. They were released at {URL} from where the sources can be obtained. -""" +""" # noqa: E501 def main(): for stub_set in STUBS: dest_sub_dir = os.path.join(DESTINATION_DIR, stub_set["STUB_SET_VERSION"]) - """ The directory is cleaned up so we would detect if a stub was just committed into the repository but the - name was not added into the FILE_LIST of STUBS. This would be an unwanted state because the checker would not - detect any changes in that stub.""" + """ The directory is cleaned up so we would detect if a stub was just committed + into the repository but the name was not added into the FILE_LIST of STUBS. + This would be an unwanted state because the checker would not detect any + changes in that stub.""" for old_file in glob.glob(os.path.join(dest_sub_dir, "*.json")): print(f"Removing old file {old_file}") os.remove(old_file) diff --git a/tools/esptool_py/docs/_static/esptool_versions.js b/tools/esptool_py/docs/_static/esptool_versions.js index 409ecf2e9b..0cc53dd148 100644 --- a/tools/esptool_py/docs/_static/esptool_versions.js +++ b/tools/esptool_py/docs/_static/esptool_versions.js @@ -1,8 +1,11 @@ var DOCUMENTATION_VERSIONS = { - DEFAULTS: { has_targets: false, + DEFAULTS: { has_targets: true, supported_targets: [ "esp32" ] }, - VERSIONS: [ ], + VERSIONS: [ + { name: "latest", old: false, pre_release: false, supported_targets: [ "esp8266", "esp32", "esp32s2", "esp32s3", "esp32c3", "esp32c2", "esp32c6", "esp32p4", "esp32h2", "esp32c5", "esp32c61" ] }, + { name: "release-v4", old: false, pre_release: false, supported_targets: [ "esp8266", "esp32", "esp32s2", "esp32s3", "esp32c3", "esp32c2", "esp32c6", "esp32p4", "esp32h2", "esp32c5", "esp32c61" ] }, + ], IDF_TARGETS: [ { text: "ESP8266", value: "esp8266" }, { text: "ESP32", value: "esp32" }, @@ -13,5 +16,7 @@ var DOCUMENTATION_VERSIONS = { { text: "ESP32-C6", value: "esp32c6" }, { text: "ESP32-H2", value: "esp32h2" }, { text: "ESP32-P4", value: "esp32p4" }, + { text: "ESP32-C5", value: "esp32c5" }, + { text: "ESP32-C61", value: "esp32c61" }, ] }; diff --git a/tools/esptool_py/docs/conf_common.py b/tools/esptool_py/docs/conf_common.py index 03e69fc861..4edae78bc4 100644 --- a/tools/esptool_py/docs/conf_common.py +++ b/tools/esptool_py/docs/conf_common.py @@ -11,6 +11,8 @@ "esp32c6", "esp32h2", "esp32p4", + "esp32c5", + "esp32c61", ] # link roles config @@ -23,7 +25,14 @@ html_static_path = ["../_static"] # Conditional content -extensions += ["esp_docs.esp_extensions.dummy_build_system"] +extensions += [ + "esp_docs.esp_extensions.dummy_build_system", + "sphinx.ext.autodoc", + "sphinx.ext.napoleon", + "sphinx_tabs.tabs", +] + +sphinx_tabs_disable_tab_closing = True ESP8266_DOCS = [] @@ -42,9 +51,15 @@ "esp32c6": ESP32_DOCS, "esp32h2": ESP32_DOCS, "esp32p4": ESP32_DOCS, + "esp32c5": ESP32_DOCS, + "esp32c61": ESP32_DOCS, } # Extra options required by sphinx_idf_theme project_slug = "esptool" versions_url = "./_static/esptool_versions.js" + + +def conf_setup(app, config): + config.html_baseurl = f"https://docs.espressif.com/projects/esptool/{config.language}/stable/{config.idf_target}/" diff --git a/tools/esptool_py/docs/en/advanced-topics/boot-mode-selection.rst b/tools/esptool_py/docs/en/advanced-topics/boot-mode-selection.rst index dfa4a04e37..ee1eb54326 100644 --- a/tools/esptool_py/docs/en/advanced-topics/boot-mode-selection.rst +++ b/tools/esptool_py/docs/en/advanced-topics/boot-mode-selection.rst @@ -1,8 +1,8 @@ -{IDF_TARGET_STRAP_BOOT_GPIO:default="GPIO9", esp32="GPIO0", esp32s2="GPIO0", esp32s3="GPIO0", esp32p4="GPIO35"} +{IDF_TARGET_STRAP_BOOT_GPIO:default="GPIO9", esp8266="GPIO0", esp32="GPIO0", esp32s2="GPIO0", esp32s3="GPIO0", esp32p4="GPIO35", esp32c5="GPIO28"} -{IDF_TARGET_STRAP_BOOT_2_GPIO:default="GPIO8", esp32="GPIO2", esp32s2="GPIO46", esp32s3="GPIO46", esp32p4="GPIO36"} +{IDF_TARGET_STRAP_BOOT_2_GPIO:default="GPIO8", esp32="GPIO2", esp32s2="GPIO46", esp32s3="GPIO46", esp32p4="GPIO36", esp32c5="GPIO27"} -{IDF_TARGET_BOOTLOADER_OFFSET:default="0", esp32="1000", esp32s2="1000", esp32p4="2000"} +{IDF_TARGET_BOOTLOADER_OFFSET:default="0x0", esp32="0x1000", esp32s2="0x1000", esp32p4="0x2000", esp32c5="0x2000"} .. _boot-mode: @@ -53,9 +53,9 @@ This guide explains how to select the boot mode correctly and describes the boot The {IDF_TARGET_NAME} has a 45k ohm internal pull-up/pull-down resistor at {IDF_TARGET_STRAP_BOOT_GPIO} (and other pins). If you want to connect a switch button to enter the boot mode, this has to be a strong pull-down. For example a 10k resistor to GND. - Information about {IDF_TARGET_NAME} strapping pins can also be found in the `{IDF_TARGET_NAME} Datasheet `__, section "Strapping Pins". + Information about {IDF_TARGET_NAME} strapping pins can also be found in the `{IDF_TARGET_NAME} Datasheet <{IDF_TARGET_DATASHEET_EN_URL}>`__, section "Strapping Pins". - On many development boards with built-in USB/Serial, ``esptool.py`` can automatically reset the board into bootloader mode. For other configurations or custom hardware, you will need to check the orientation of some "strapping pins" to get the correct boot mode: + On many development boards with built-in USB/Serial, ``esptool`` can automatically reset the board into bootloader mode. For other configurations or custom hardware, you will need to check the orientation of some "strapping pins" to get the correct boot mode: Select Bootloader Mode ---------------------- @@ -87,7 +87,7 @@ This guide explains how to select the boot mode correctly and describes the boot {IDF_TARGET_STRAP_BOOT_2_GPIO} must also be either left unconnected/floating, or driven Low, in order to enter the serial bootloader. - .. only:: esp32c3 or esp32c2 or esp32h2 or esp32c6 or esp32p4 + .. only:: esp32c3 or esp32c2 or esp32h2 or esp32c6 or esp32p4 or esp32c5 or esp32c61 {IDF_TARGET_STRAP_BOOT_2_GPIO} must also be driven High, in order to enter the serial bootloader reliably. The strapping combination of {IDF_TARGET_STRAP_BOOT_2_GPIO} = 0 and {IDF_TARGET_STRAP_BOOT_GPIO} = 0 is invalid and will trigger unexpected behavior. @@ -99,7 +99,7 @@ This guide explains how to select the boot mode correctly and describes the boot .. only:: not esp32 - As well as the above mentioned pins, other ones influence the serial bootloader, please consult the `{IDF_TARGET_NAME} Datasheet `__, section "Strapping Pins". + As well as the above mentioned pins, other ones influence the serial bootloader, please consult the `{IDF_TARGET_NAME} Datasheet <{IDF_TARGET_DATASHEET_EN_URL}>`__, section "Strapping Pins". .. only:: esp32 @@ -113,23 +113,23 @@ This guide explains how to select the boot mode correctly and describes the boot | 15 (MTDO) | If driven Low, silences boot messages printed by the ROM bootloader. Has an internal pull-up, so unconnected = High = normal output. | +-------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ - For more information, consult the `{IDF_TARGET_NAME} Datasheet `__, section "Strapping Pins". + For more information, consult the `{IDF_TARGET_NAME} Datasheet <{IDF_TARGET_DATASHEET_EN_URL}>`__, section "Strapping Pins". .. _automatic-bootloader: Automatic Bootloader -------------------- -``esptool.py`` resets {IDF_TARGET_NAME} automatically by asserting ``DTR`` and ``RTS`` control lines of the USB to serial converter chip, i.e., FTDI, CP210x, or CH340x. The ``DTR`` and ``RTS`` control lines are in turn connected to ``{IDF_TARGET_STRAP_BOOT_GPIO}`` and ``EN`` (``CHIP_PU``) pins of {IDF_TARGET_NAME}, thus changes in the voltage levels of ``DTR`` and ``RTS`` will boot the {IDF_TARGET_NAME} into Firmware Download mode. +``esptool`` resets {IDF_TARGET_NAME} automatically by asserting ``DTR`` and ``RTS`` control lines of the USB to serial converter chip, i.e., FTDI, CP210x, or CH340x. The ``DTR`` and ``RTS`` control lines are in turn connected to ``{IDF_TARGET_STRAP_BOOT_GPIO}`` and ``EN`` (``CHIP_PU``) pins of {IDF_TARGET_NAME}, thus changes in the voltage levels of ``DTR`` and ``RTS`` will boot the {IDF_TARGET_NAME} into Firmware Download mode. .. note:: - When developing ``esptool.py``, keep in mind ``DTR`` and ``RTS`` are active low signals, i.e., ``True`` = pin @ 0V, ``False`` = pin @ VCC. + When developing ``esptool``, keep in mind ``DTR`` and ``RTS`` are active low signals, i.e., ``True`` = pin @ 0V, ``False`` = pin @ VCC. As an example of auto-reset curcuitry implementation, check the `schematic `_ of the ESP32 DevKitC development board: - The **Micro USB 5V & USB-UART** section shows the ``DTR`` and ``RTS`` control lines of the USB to serial converter chip connected to ``{IDF_TARGET_STRAP_BOOT_GPIO}`` and ``EN`` pins of the ESP module. -- Some OS and/or drivers may activate ``RTS`` and or ``DTR`` automatically when opening the serial port (true only for some serial terminal programs, not ``esptool.py``), pulling them low together and holding the ESP in reset. If ``RTS`` is wired directly to ``EN`` then RTS/CTS "hardware flow control" needs to be disabled in the serial program to avoid this. +- Some OS and/or drivers may activate ``RTS`` and or ``DTR`` automatically when opening the serial port (true only for some serial terminal programs, not ``esptool``), pulling them low together and holding the ESP in reset. If ``RTS`` is wired directly to ``EN`` then RTS/CTS "hardware flow control" needs to be disabled in the serial program to avoid this. An additional circuitry is implemented in order to avoid this problem - if both ``RTS`` and ``DTR`` are asserted together, this doesn't reset the chip. The schematic shows this specific circuit with two transistors and its truth table. - If this circuitry is implemented (all Espressif boards have it), adding a capacitor between the ``EN`` pin and ``GND`` (in the 1uF-10uF range) is necessary for the reset circuitry to work reliably. This is shown in the **ESP32 Module** section of the schematic. - The **Switch Button** section shows buttons needed for :ref:`manually switching to bootloader `. @@ -150,7 +150,7 @@ In Linux serial ports by default will assert RTS when nothing is attached to the (Some third party {IDF_TARGET_NAME} development boards use an automatic reset circuit for ``EN`` & ``{IDF_TARGET_STRAP_BOOT_GPIO}`` pins, but don't add a capacitor on the ``EN`` pin. This results in unreliable automatic reset, especially on Windows. Adding a 1uF (or higher) value capacitor between ``EN`` pin and ``GND`` may make automatic reset more reliable.) -In general, you should have no problems with the official Espressif development boards. However, ``esptool.py`` is not able to reset your hardware automatically in the following cases: +In general, you should have no problems with the official Espressif development boards. However, ``esptool`` is not able to reset your hardware automatically in the following cases: - Your hardware does not have the ``DTR`` and ``RTS`` lines connected to ``{IDF_TARGET_STRAP_BOOT_GPIO}`` and ``EN`` (``CHIP_PU``) - The ``DTR`` and ``RTS`` lines are configured differently @@ -224,7 +224,7 @@ Depending on the kind of hardware you have, it may also be possible to manually **chksum:** - If value of “chksum” == value of “csum”, it means flash has been read correctly during booting. + If value of "chksum" == value of "csum", it means flash has been read correctly during booting. The rest of boot messages are used internally by Espressif. @@ -290,15 +290,23 @@ Depending on the kind of hardware you have, it may also be possible to manually Early Flash Read Error """""""""""""""""""""" - :: + .. only:: esp8266 + + :: + + flash read err, 0 + + .. only:: not esp8266 + + :: - flash read err, {IDF_TARGET_BOOTLOADER_OFFSET} + Invalid header - This fatal error indicates that the bootloader tried to read the software bootloader header at address 0x{IDF_TARGET_BOOTLOADER_OFFSET} but failed to read valid data. Possible reasons for this include: + This fatal error indicates that the bootloader tried to read the software bootloader header at address {IDF_TARGET_BOOTLOADER_OFFSET} but failed to read valid data. Possible reasons for this include: .. list:: - - There isn't actually a bootloader at offset 0x{IDF_TARGET_BOOTLOADER_OFFSET} (maybe the bootloader was flashed to the wrong offset by mistake, or the flash has been erased and no bootloader has been flashed yet.) + - There isn't actually a bootloader at offset {IDF_TARGET_BOOTLOADER_OFFSET} (maybe the bootloader was flashed to the wrong offset by mistake, or the flash has been erased and no bootloader has been flashed yet.) - Physical problem with the connection to the flash chip, or flash chip power. - Flash encryption is enabled but the bootloader is plaintext. Alternatively, flash encryption is disabled but the bootloader is encrypted ciphertext. @@ -326,7 +334,7 @@ Depending on the kind of hardware you have, it may also be possible to manually mode:DIO, clock div:1 - This is normal boot output based on a combination of eFuse values and information read from the bootloader header at flash offset 0x{IDF_TARGET_BOOTLOADER_OFFSET}: + This is normal boot output based on a combination of eFuse values and information read from the bootloader header at flash offset {IDF_TARGET_BOOTLOADER_OFFSET}: .. list:: @@ -339,10 +347,10 @@ Depending on the kind of hardware you have, it may also be possible to manually - ``SPIWP:0xNN`` indicates a custom ``WP`` pin value, which is stored in the bootloader header. This pin value is only used if SPI flash pins have been remapped via eFuse (as shown in the ``configsip`` value). All custom pin values but WP are encoded in the configsip byte loaded from eFuse, and WP is supplied in the bootloader header. :esp32: - ``clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00`` Custom GPIO drive strength values for SPI flash pins. These are read from the bootloader header in flash. Not currently supported. - - ``mode: AAA, clock div: N``. SPI flash access mode. Read from the bootloader header, correspond to the ``--flash_mode`` and ``--flash_freq`` arguments supplied to ``esptool.py write_flash`` or ``esptool.py elf2image``. + - ``mode: AAA, clock div: N``. SPI flash access mode. Read from the bootloader header, correspond to the ``--flash-mode`` and ``--flash-freq`` arguments supplied to ``esptool write-flash`` or ``esptool elf2image``. - ``mode`` can be DIO, DOUT, QIO, or QOUT. *QIO and QOUT are not supported here*, to boot in a Quad I/O mode the ROM bootloader should load the software bootloader in a Dual I/O mode and then the ESP-IDF software bootloader enables Quad I/O based on the detected flash chip mode. - - ``clock div: N`` is the SPI flash clock frequency divider. This is an integer clock divider value from an 80MHz APB clock, based on the supplied ``--flash_freq`` argument (ie 80MHz=1, 40MHz=2, etc). - The ROM bootloader actually loads the software bootloader at a lower frequency than the flash_freq value. The initial APB clock frequency is equal to the crystal frequency, so with a 40MHz crystal the SPI clock used to load the software bootloader will be half the configured value (40MHz/2=20MHz). + - ``clock div: N`` is the SPI flash clock frequency divider. This is an integer clock divider value from an 80MHz APB clock, based on the supplied ``--flash-freq`` argument (ie 80MHz=1, 40MHz=2, etc). + The ROM bootloader actually loads the software bootloader at a lower frequency than the ``--flash-freq`` value. The initial APB clock frequency is equal to the crystal frequency, so with a 40MHz crystal the SPI clock used to load the software bootloader will be half the configured value (40MHz/2=20MHz). When the software bootloader starts it sets the APB clock to 80MHz causing the SPI clock frequency to match the value set when flashing. Software Bootloader Load Segments @@ -358,7 +366,7 @@ Depending on the kind of hardware you have, it may also be possible to manually These entries are printed as the ROM bootloader loads each segment in the software bootloader image. The load address and length of each segment is printed. - You can compare these values to the software bootloader image by running ``esptool.py --chip {IDF_TARGET_PATH_NAME} image_info /path/to/bootloader.bin`` to dump image info including a summary of each segment. Corresponding details will also be found in the bootloader ELF file headers. + You can compare these values to the software bootloader image by running ``esptool --chip {IDF_TARGET_PATH_NAME} image-info /path/to/bootloader.bin`` to dump image info including a summary of each segment. Corresponding details will also be found in the bootloader ELF file headers. If there is a problem with the SPI flash chip addressing mode, the values printed by the bootloader here may be corrupted. diff --git a/tools/esptool_py/docs/en/advanced-topics/diag/firmware_image_ext_header_format.diag b/tools/esptool_py/docs/en/advanced-topics/diag/firmware_image_ext_header_format.diag new file mode 100644 index 0000000000..1c3d8c95b4 --- /dev/null +++ b/tools/esptool_py/docs/en/advanced-topics/diag/firmware_image_ext_header_format.diag @@ -0,0 +1,15 @@ +packetdiag command_packet_format{ + colwidth = 16 + node_width = 50 + node_height = 50 + default_fontsize = 16 + + 0: "WP"; + 1-3: "Drive settings"; + 4-5: "Chip ID"; + 6: "Rev." [color = grey]; + 7-8: "Min rev."; + 9-10: "Max rev."; + 11-14: "Reserved" [color = grey]; + 15: "Hash"; +} diff --git a/tools/esptool_py/docs/en/advanced-topics/diag/firmware_image_format.diag b/tools/esptool_py/docs/en/advanced-topics/diag/firmware_image_format.diag new file mode 100644 index 0000000000..7566c37e5f --- /dev/null +++ b/tools/esptool_py/docs/en/advanced-topics/diag/firmware_image_format.diag @@ -0,0 +1,12 @@ +packetdiag command_packet_format{ + colwidth = 16 + node_width = 50 + node_height = 50 + default_fontsize = 16 + + 0-7: "Image header"; + 8-23: "Image extended header"; + 24-31: "Segmets" [color = lightgrey]; + 32-47: "..." [color = lightgrey]; + 48-63: "Footer"; +} diff --git a/tools/esptool_py/docs/en/advanced-topics/diag/firmware_image_format_esp8266.diag b/tools/esptool_py/docs/en/advanced-topics/diag/firmware_image_format_esp8266.diag new file mode 100644 index 0000000000..8b94afbd0c --- /dev/null +++ b/tools/esptool_py/docs/en/advanced-topics/diag/firmware_image_format_esp8266.diag @@ -0,0 +1,11 @@ +packetdiag command_packet_format{ + colwidth = 16 + node_width = 50 + node_height = 50 + default_fontsize = 16 + + 0-7: "Image header"; + 8-23: "Segmets" [color = lightgrey]; + 24-31: "..." [color = lightgrey]; + 32-47: "Footer"; +} diff --git a/tools/esptool_py/docs/en/advanced-topics/diag/firmware_image_header_format.diag b/tools/esptool_py/docs/en/advanced-topics/diag/firmware_image_header_format.diag new file mode 100644 index 0000000000..bc230811f5 --- /dev/null +++ b/tools/esptool_py/docs/en/advanced-topics/diag/firmware_image_header_format.diag @@ -0,0 +1,12 @@ +packetdiag command_packet_format{ + colwidth = 8 + node_width = 100 + node_height = 50 + default_fontsize = 16 + + 0: "0xE9"; + 1: "Number of\nsegments"; + 2: "Flash Mode"; + 3: "Flash\nsize/freq"; + 4-7: "Entry point"; +} diff --git a/tools/esptool_py/docs/en/advanced-topics/firmware-image-format.rst b/tools/esptool_py/docs/en/advanced-topics/firmware-image-format.rst index 10d81bb576..66f6fe1d58 100644 --- a/tools/esptool_py/docs/en/advanced-topics/firmware-image-format.rst +++ b/tools/esptool_py/docs/en/advanced-topics/firmware-image-format.rst @@ -6,7 +6,7 @@ {IDF_TARGET_FLASH_FREQ_2:default="20", esp32c2="15", esp32h2="12"} -{IDF_TARGET_BOOTLOADER_OFFSET:default="0x0", esp32="0x1000", esp32s2="0x1000", esp32p4="0x2000"} +{IDF_TARGET_BOOTLOADER_OFFSET:default="0x0", esp32="0x1000", esp32s2="0x1000", esp32p4="0x2000", esp32c5="0x2000"} .. _image-format: @@ -14,19 +14,31 @@ Firmware Image Format ===================== -This is technical documentation for the firmware image format used by the ROM bootloader. These are the images created by ``esptool.py elf2image``. +This is technical documentation for the firmware image format used by the ROM bootloader. These are the images created by ``esptool elf2image``. .. only:: esp8266 + .. packetdiag:: diag/firmware_image_format_esp8266.diag + :caption: Firmware image format + :align: center + The firmware file consists of a header, a variable number of data segments and a footer. Multi-byte fields are little-endian. .. only:: not esp8266 + .. packetdiag:: diag/firmware_image_format.diag + :caption: Firmware image format + :align: center + The firmware file consists of a header, an extended header, a variable number of data segments and a footer. Multi-byte fields are little-endian. File Header ----------- +.. packetdiag:: diag/firmware_image_header_format.diag + :caption: Firmware image header + :align: center + The image header is 8 bytes long: .. only:: esp8266 @@ -91,7 +103,25 @@ The image header is 8 bytes long: Flash frequency with value ``0`` can mean either 80MHz or 40MHz based on MSPI clock source mode. -.. only:: not (esp8266 or esp32c6 or esp32s3 or esp32s2 or esp32p4) +.. only:: esp32c5 or esp32c61 + + +--------+------------------------------------------------------------------------------------------------+ + | Byte | Description | + +========+================================================================================================+ + | 0 | Magic number (always ``0xE9``) | + +--------+------------------------------------------------------------------------------------------------+ + | 1 | Number of segments | + +--------+------------------------------------------------------------------------------------------------+ + | 2 | SPI Flash Mode (``0`` = QIO, ``1`` = QOUT, ``2`` = DIO, ``3`` = DOUT) | + +--------+------------------------------------------------------------------------------------------------+ + | 3 | High four bits - Flash size (``0`` = 1MB, ``1`` = 2MB, ``2`` = 4MB, ``3`` = 8MB, ``4`` = 16MB) | + | | | + | | Low four bits - Flash frequency (``0xf`` = {IDF_TARGET_FLASH_FREQ_F}MHz, ``0`` = {IDF_TARGET_FLASH_FREQ_0}MHz, ``2`` = {IDF_TARGET_FLASH_FREQ_2}MHz) | + +--------+------------------------------------------------------------------------------------------------+ + | 4-7 | Entry point address | + +--------+------------------------------------------------------------------------------------------------+ + +.. only:: not (esp8266 or esp32c6 or esp32s3 or esp32s2 or esp32p4 or esp32c5 or esp32c61) +--------+------------------------------------------------------------------------------------------------+ | Byte | Description | @@ -110,7 +140,10 @@ The image header is 8 bytes long: +--------+------------------------------------------------------------------------------------------------+ -``esptool.py`` overrides the 2nd and 3rd (counted from 0) bytes according to the SPI flash info provided through the command line option. These bytes are only overridden if this is a bootloader image (an image written to a correct bootloader offset of {IDF_TARGET_BOOTLOADER_OFFSET}), in this case, the appended SHA256 digest is also updated to reflect the header changes. Generating images without SHA256 digest can be achieved by running ``esptool.py elf2image`` with the ``--dont-append-digest`` argument. +``esptool`` overrides the 2nd and 3rd (counted from 0) bytes according to the SPI flash info provided through the command line options (see :ref:`flash-modes`). +These bytes are only overridden if this is a bootloader image (an image written to a correct bootloader offset of {IDF_TARGET_BOOTLOADER_OFFSET}). +In this case, the appended SHA256 digest, which is a cryptographic hash used to verify the integrity of the image, is also updated to reflect the header changes. +Generating images without SHA256 digest can be achieved by running ``esptool elf2image`` with the ``--dont-append-digest`` argument. .. only:: esp8266 @@ -121,12 +154,14 @@ The image header is 8 bytes long: Extended File Header -------------------- - The 16-byte long extended header comes right after the image header, individual segments come right after it: + .. packetdiag:: diag/firmware_image_ext_header_format.diag + :caption: Extended File Header + :align: center +--------+---------------------------------------------------------------------------------------------------------+ | Byte | Description | +========+=========================================================================================================+ - | 0 | WP pin when SPI pins set via efuse (read by ROM bootloader) | + | 0 | WP pin when SPI pins set via eFuse (read by ROM bootloader) | +--------+---------------------------------------------------------------------------------------------------------+ | 1-3 | Drive settings for the SPI flash pins (read by ROM bootloader) | +--------+---------------------------------------------------------------------------------------------------------+ @@ -171,4 +206,4 @@ The file is padded with zeros until its size is one byte less than a multiple of Analyzing a Binary Image ------------------------ -To analyze a binary image and get a complete summary of its headers and segments, use the :ref:`image_info ` command with the ``--version 2`` option. +To analyze a binary image and get a complete summary of its headers and segments, use the :ref:`image-info ` command. diff --git a/tools/esptool_py/docs/en/advanced-topics/serial-protocol.rst b/tools/esptool_py/docs/en/advanced-topics/serial-protocol.rst index 48c85eec93..17b0152278 100644 --- a/tools/esptool_py/docs/en/advanced-topics/serial-protocol.rst +++ b/tools/esptool_py/docs/en/advanced-topics/serial-protocol.rst @@ -1,5 +1,7 @@ {IDF_TARGET_SECURITY_INFO:default="32 bits ``flags``, 1 byte ``flash_crypt_cnt``, 7x1 byte ``key_purposes``, 32-bit word ``chip_id``, 32-bit word ``eco_version``", esp32s2="32 bits ``flags``, 1 byte ``flash_crypt_cnt``, 7x1 byte ``key_purposes`` "} +.. _serial-protocol: + Serial Protocol =============== @@ -7,11 +9,7 @@ This is technical documentation for the serial protocol used by the UART bootloa The UART bootloader runs on chip reset if certain strapping pins are set. See :ref:`entering-the-bootloader` for details of this process. -.. only:: not esp8266 - - The {IDF_TARGET_NAME} ROM loader serial protocol is similar to ESP8266, although {IDF_TARGET_NAME} adds some additional commands and some slightly different behaviour. - -By default, esptool uploads a stub "software loader" to the IRAM of the chip. The stub loader then replaces the ROM loader for all future interactions. This standardizes much of the behaviour. Pass ``--no-stub`` to esptool in order to disable the stub loader. See :ref:`stub` for more information. +By default, esptool uploads a stub "software loader" to the IRAM of the chip. The stub loader then replaces the ROM loader for all future interactions. This standardizes much of the behavior. Pass ``--no-stub`` to esptool in order to disable the stub loader. See :ref:`stub` for more information. .. note:: @@ -78,7 +76,7 @@ Each received command will result in a response SLIP packet sent from the ESP ch | 8..n | Data | Variable length data payload. Length indicated by "Size" field. | +--------+-------------+--------------------------------------------------------------------------------------------------------------+ -Status bytes +Status Bytes """""""""""" The final bytes of the Data payload indicate command status: @@ -153,7 +151,7 @@ After sending a command, the host should continue to read response packets until Commands ^^^^^^^^ -Supported by stub loader and ROM loader +Supported by Stub Loader and ROM Loader """"""""""""""""""""""""""""""""""""""" .. only:: esp8266 @@ -259,10 +257,10 @@ Supported by stub loader and ROM loader | ``0x14`` | GET_SECURITY_INFO | Read chip security info | | {IDF_TARGET_SECURITY_INFO} | +------------+----------------------+----------------------------------------------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------+ -Supported by stub loader only +Supported by Stub Loader Only """"""""""""""""""""""""""""" -ROM loaders will not recognise these commands. +ROM loaders will not recognize these commands. +------------+-------------------+-----------------------------------+-------------------------------------------------------------------------------------------------------------------------+----------+ | Byte | Name | Description | Input | Output | @@ -349,7 +347,7 @@ All three of these sequences follow a similar pattern: * An _END command (FLASH_END, etc) is sent to exit the bootloader and optionally reset the chip (or jump to an address in RAM, in the case of MEM_END). Not necessary to send after flashing if you wish to continue sending other or different commands. It's not necessary to send flash erase commands before sending commands to write to flash, etc. The ROM loaders erase the to-be-written region in response to the FLASH_BEGIN command. -The stub loader does just-in-time erasing as it writes data, to maximise overall flashing performance (each block of data is read into RAM via serial while the previous block is simultaneously being written to flash, and 4KB and 64KB erases are done as needed before writing to flash). +The stub loader does just-in-time erasing as it writes data, to maximize overall flashing performance (each block of data is read into RAM via serial while the previous block is simultaneously being written to flash, and 4KB and 64KB erases are done as needed before writing to flash). The block size chosen should be small enough to fit into RAM of the device. Esptool uses 16KB which gives good performance when used with the stub loader. @@ -382,7 +380,7 @@ The SPI_FLASH_MD5 command passes the start address in flash and the size of data SPI Configuration Commands ^^^^^^^^^^^^^^^^^^^^^^^^^^ -SPI Attach command +SPI Attach Command """""""""""""""""" The SPI _ATTACH command enables the SPI flash interface. It takes a 32-bit data payload which is used to determine which SPI peripheral and pins should be used to connect to SPI flash. @@ -413,7 +411,7 @@ The SPI _ATTACH command enables the SPI flash interface. It takes a 32-bit data | (other values) | Pin numbers as 6-bit values, packed into a 30-bit value. Order (from MSB): HD pin, Q pin, D pin, CS pin, CLK pin. | +------------------+----------------------------------------------------------------------------------------------------------------------------------+ - The "Default SPI flash interface" uses pins configured via the ``SPI_PAD_CONFIG_xxx`` efuses (if unset, these efuses are all zero and the default SPI flash pins given in the datasheet are used.) + The "Default SPI flash interface" uses pins configured via the ``SPI_PAD_CONFIG_xxx`` eFuses (if unset, these eFuses are all zero and the default SPI flash pins given in the datasheet are used.) When writing the values of each pin as 6-bit numbers packed into the data word, each 6-bit value uses the following representation: @@ -421,7 +419,7 @@ The SPI _ATTACH command enables the SPI flash interface. It takes a 32-bit data * Pin numbers 0 through 30 are represented as themselves. * Pin numbers 32 & 33 are represented as values 30 & 31. - * It is not possible to represent pins 30 & 31 or pins higher than 33. This is the same 6-bit representation used by the ``SPI_PAD_CONFIG_xxx`` efuses. + * It is not possible to represent pins 30 & 31 or pins higher than 33. This is the same 6-bit representation used by the ``SPI_PAD_CONFIG_xxx`` eFuses. On {IDF_TARGET_NAME} ROM loader only, there is an additional 4 bytes in the data payload of this command. These bytes should all be set to zero. @@ -436,7 +434,7 @@ The SPI_SET_PARAMS command sets some parameters of the attached SPI flash chip ( All the values which are passed except total size are hardcoded, and most are not used when writing to flash. See `flash_set_parameters function `__ in esptool for the values which it sends. -32-bit Read/Write +32-Bit Read/Write ^^^^^^^^^^^^^^^^^ The 32-bit read/write commands (READ_REG, WRITE_REG) allow word-oriented reading and writing of memory and register data. @@ -470,11 +468,11 @@ Here is a sample extract, showing a READ_REG command and response: :: - TRACE +0.000 command op=0x0a data len=4 wait_response=1 timeout=3.000 data=1400f43f - TRACE +0.000 Write 14 bytes: c0000a0400000000001400f43fc0 - TRACE +0.005 Read 1 bytes: c0 - TRACE +0.000 Read 11 bytes: 010a0200620100000000c0 - TRACE +0.000 Received full packet: 010a0200620100000000 + TRACE +0.000 --- Cmd READ_REG (0x0a) | data_len 4 | wait_response 1 | timeout 3.000 | data 00100040 --- + TRACE +0.000 Write 14 bytes: c0000a04000000000000100040c0 + TRACE +0.046 Read 1 bytes: c0 + TRACE +0.000 Read 11 bytes: 010a0200090000000000c0 + TRACE +0.000 Received full packet: 010a0200090000000000 The +X.XXX value is the time delta (in seconds) since the last trace line. @@ -487,18 +485,18 @@ Here is a second example showing part of the initial synchronization sequence (l :: - TRACE +0.000 Write 46 bytes: - c000082400000000 0007071220555555 | ...$........ UUU - 5555555555555555 5555555555555555 | UUUUUUUUUUUUUUUU - 5555555555555555 5555555555c0 | UUUUUUUUUUUUU. - TRACE +0.011 Read 1 bytes: c0 - TRACE +0.000 Read 63 bytes: - 0108040007122055 00000000c0c00108 | ...... U........ - 0400071220550000 0000c0c001080400 | .... U.......... - 0712205500000000 c0c0010804000712 | .. U............ - 205500000000c0c0 01080400071220 | U............ - TRACE +0.000 Received full packet: 010804000712205500000000 - TRACE +0.000 Received full packet: 010804000712205500000000 + TRACE +0.000 Write 46 bytes: + c000082400000000 0007071220555555 | ...$........ UUU + 5555555555555555 5555555555555555 | UUUUUUUUUUUUUUUU + 5555555555555555 5555555555c0 | UUUUUUUUUUUUU. + TRACE +0.012 Read 1 bytes: c0 + TRACE +0.000 Read 63 bytes: + 0108040007071220 00000000c0c00108 | ....... ........ + 0400070712200000 0000c0c001080400 | ..... .......... + 0707122000000000 c0c0010804000707 | ... ............ + 122000000000c0c0 01080400070712 | . ............. + TRACE +0.000 Received full packet: 010804000707122000000000 + TRACE +0.000 Received full packet: 010804000707122000000000 .. important:: diff --git a/tools/esptool_py/docs/en/advanced-topics/spi-flash-modes.rst b/tools/esptool_py/docs/en/advanced-topics/spi-flash-modes.rst index 22b54766de..e796b0fa3d 100644 --- a/tools/esptool_py/docs/en/advanced-topics/spi-flash-modes.rst +++ b/tools/esptool_py/docs/en/advanced-topics/spi-flash-modes.rst @@ -3,7 +3,7 @@ SPI Flash Modes =============== -The ESP chips support four different SPI flash access modes: DIO, DOUT, QIO & QOUT. These can be set via the ``--flash_mode`` option of ``esptool.py write_flash``. +The ESP chips support four different SPI flash access modes: DIO, DOUT, QIO & QOUT. These can be set via the ``--flash-mode`` option of ``esptool write-flash``. These options control how many I/O pins are used for communication with the attached SPI flash chip, and which SPI commands are used. @@ -26,7 +26,7 @@ In order of performance: | ``dout`` | Dual Output | 2 pins used for data. | Approx 50% slower than ``qio``. | +------------+---------------+----------------------------------+-----------------------------------+ -In general, choose the fastest option for flash_mode that works with your device. Not all devices support all modes. See FAQ below for details. +In general, choose the fastest option for ``--flash-mode`` that works with your device. Not all devices support all modes. See FAQ below for details. Mode Descriptions ----------------- @@ -74,6 +74,20 @@ In ``qout`` mode, the host uses the "Quad Output Fast Read" (6BH) command to rea In ``qio`` mode, the host uses the "Quad I/O Fast Read" (EBH) command to read data. This command is the same as "Dual I/O Fast Read", only both address & data are transferred on 4 pins instead of 2 with 4 bits per clock cycle. This makes both the address & data transfer exactly twice as fast as "Dual I/O Fast Read". +.. only:: esp32s3 + + Octal SPI + ^^^^^^^^^ + + Some ESP chips additionally support Octal SPI mode. This mode uses 8 pins for communication with the SPI flash chip, and allows for even faster data transfers than Quad SPI. This mode added four additional pins (SPIIO4~7) compared to Quad SPI for data transfers. + + The 1st and 2nd bootloaders don't support ``opi`` mode. Because of that esptool doesn't use ``opi`` and ``dout`` is used instead. The bootloader retrieves the information from eFuse and effectively replaces the mode. + + .. note:: + + Use the ``esptool flash-id`` command to check if your ESP is using Quad or Octal SPI mode. It prints information based on the eFuse settings. + + Frequently Asked Questions -------------------------- @@ -83,7 +97,7 @@ Why don't qio & qout modes work with my Espressif chip/module? It is usually one of the following reasons: * The WP and HOLD pins of the SPI flash chip are not wired to the correct GPIOs of the Espressif chip. These pins must be connected correctly for quad modes to work, and not all boards/modules connect them at all. -* The SPI flash chip does not support quad modes. Look up the flash chip datasheet to see which modes it supports. You can identify the flash chip visually, or by using the :ref:`esptool.py flash_id ` command. +* The SPI flash chip does not support quad modes. Look up the flash chip datasheet to see which modes it supports. You can identify the flash chip visually, or by using the :ref:`esptool flash-id ` command. * Quad mode is not enabled correctly for this chip model. SPI flash is not a standard, so every manufacturer implements their chip differently. Most flash chips require certain commands to be sent in order to enable Quad SPI modes, and these commands vary. For Espressif chips, this often means that the chip first boots in a Dual SPI mode and then software detects the chip type and tries to enable Quad SPI mode. If the particular chip model is not supported by the software then it won't be able to enter quad mode. @@ -105,7 +119,7 @@ How is flash mode communicated to the Espressif chip? ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ The bootloader .bin file, flashed to the SPI flash, contains a header which has flash speed, flash mode, and some other metadata. The initial host mode is determined by ROM code when it reads this header after reset. -Passing the ``--flash_mode`` argument to esptool will update this header when the file is being written to flash. +Passing the ``--flash-mode`` argument to esptool will update this header when the file is being written to flash. This only determines the mode which is used for the initial boot from reset. Software may then configure the flash mode differently as part of the boot process. diff --git a/tools/esptool_py/docs/en/conf.py b/tools/esptool_py/docs/en/conf.py index 0362dfc3e0..39d4385a67 100644 --- a/tools/esptool_py/docs/en/conf.py +++ b/tools/esptool_py/docs/en/conf.py @@ -19,10 +19,11 @@ from conf_common import * # noqa: F403,F401 # General information about the project. -project = "esptool.py" +project = "esptool" copyright = "2016 - {}, Espressif Systems (Shanghai) Co., Ltd".format( datetime.datetime.now().year ) +autodoc_typehints_format = "short" # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. diff --git a/tools/esptool_py/docs/en/espefuse/adc-info-cmd.rst b/tools/esptool_py/docs/en/espefuse/adc-info-cmd.rst index e7c11f22ce..a87461f34a 100644 --- a/tools/esptool_py/docs/en/espefuse/adc-info-cmd.rst +++ b/tools/esptool_py/docs/en/espefuse/adc-info-cmd.rst @@ -3,24 +3,24 @@ Adc Info ======== -The ``espefuse.py adc_info`` command displays information about ADC calibration data stored in eFuse. +The ``espefuse adc-info`` command displays information about ADC calibration data stored in eFuse. .. only:: esp32 .. code-block:: none - > espefuse.py adc_info + > espefuse adc-info - === Run "adc_info" command === + === Run "adc-info" command === ADC VRef calibration: 1121mV .. only:: esp32c3 or esp32s2 or esp32s3 .. code-block:: none - > espefuse.py adc_info + > espefuse adc-info - === Run "adc_info" command === + === Run "adc-info" command === Temperature Sensor Calibration = -2.1C ADC1 readings stored in efuse BLOCK2: @@ -47,9 +47,9 @@ The ``espefuse.py adc_info`` command displays information about ADC calibration .. code-block:: none - > espefuse.py adc_info + > espefuse adc-info - === Run "adc_info" command === + === Run "adc-info" command === RF_REF_I_BIAS_CONFIG: 0 LDO_VOL_BIAS_CONFIG_LOW: 0 LDO_VOL_BIAS_CONFIG_HIGH: 0 diff --git a/tools/esptool_py/docs/en/espefuse/burn-bit-cmd.rst b/tools/esptool_py/docs/en/espefuse/burn-bit-cmd.rst index 11b180bf41..5b2d5716ca 100644 --- a/tools/esptool_py/docs/en/espefuse/burn-bit-cmd.rst +++ b/tools/esptool_py/docs/en/espefuse/burn-bit-cmd.rst @@ -3,12 +3,12 @@ Burn Bit ======== -The ``espefuse.py burn_bit`` command burns bits in efuse blocks by bit number. This is useful when the fields are not represented in the eFuse table. +The ``espefuse burn-bit`` command burns bits in eFuse blocks by bit number. This is useful when the fields are not represented in the eFuse table. Positional arguments: -- ``block`` - Efuse block. -- ``bit number`` - Bit number in the efuse block [0..BLK_LEN-1] (list of numbers, like 10 15 18 17 5 etc.). +- ``block`` - eFuse block. +- ``bit number`` - Bit number in the eFuse block [0..BLK_LEN-1] (list of numbers, like 10 15 18 17 5 etc.). Optional arguments: @@ -21,9 +21,9 @@ Burning bits to BLOCK2: .. code-block:: none - > espefuse.py burn_bit BLOCK2 15 16 17 18 19 20 + > espefuse burn-bit BLOCK2 15 16 17 18 19 20 - === Run "burn_bit" command === + === Run "burn-bit" command === bit_number: [255]........................................................[0] BLOCK2 : 0x00000000000000000000000000000000000000000000000000000000001f8000 BLOCK2 (secure_boot_v1 s) [2 ] regs_to_write: 001f8000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 @@ -39,22 +39,22 @@ Burning bits to BLOCK2: Reading updated efuses... Successful -Burning In Multiple Blocks +Burning in Multiple Blocks ^^^^^^^^^^^^^^^^^^^^^^^^^^ .. code-block:: none - > espefuse.py --virt burn_bit BLOCK2 15 16 17 18 19 20 \ - burn_bit BLOCK3 15 16 17 18 19 20 + > espefuse --virt burn-bit BLOCK2 15 16 17 18 19 20 \ + burn-bit BLOCK3 15 16 17 18 19 20 - === Run "burn_bit" command === + === Run "burn-bit" command === bit_number: [255]........................................................[0] BLOCK2 : 0x00000000000000000000000000000000000000000000000000000000001f8000 BLOCK2 (secure_boot_v1 s) [2 ] regs_to_write: 001f8000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 Batch mode is enabled, the burn will be done at the end of the command. - === Run "burn_bit" command === + === Run "burn-bit" command === bit_number: [255]........................................................[0] BLOCK3 : 0x00000000000000000000000000000000000000000000000000000000001f8000 BLOCK3 ( ) [3 ] regs_to_write: 001f8000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 diff --git a/tools/esptool_py/docs/en/espefuse/burn-block-data-cmd.rst b/tools/esptool_py/docs/en/espefuse/burn-block-data-cmd.rst index 0b09519600..2c24bbf668 100644 --- a/tools/esptool_py/docs/en/espefuse/burn-block-data-cmd.rst +++ b/tools/esptool_py/docs/en/espefuse/burn-block-data-cmd.rst @@ -3,14 +3,14 @@ Burn Block Data =============== -The ``espefuse.py burn_block_data`` command allows writing arbitrary data (non-key data) from a file into an eFuse block, for software use. +The ``espefuse burn-block-data`` command allows writing arbitrary data (non-key data) from a file into an eFuse block, for software use. -This command is available in ``espefuse.py`` v2.6 and newer. +This command is available in ``espefuse`` v2.6 and newer. Positional arguments: * ``Name of key block`` -* ``Datafile``. File containing data to burn into the efuse block. The file size can be smaller than the eFuse block size. +* ``Datafile``. File containing data to burn into the eFuse block. The file size can be smaller than the eFuse block size. It can be list of blocks and datafiles (like BLOCK1 datafile1.bin BLOCK2 datafile2.bin etc.). @@ -23,9 +23,9 @@ Optional arguments: .. code-block:: none - > espefuse.py -p PORT burn_block_data --offset 6 BLOCK3 device_id.bin + > espefuse -p PORT burn-block-data --offset 6 BLOCK3 device_id.bin - === Run "burn_block_data" command === + === Run "burn-block-data" command === [03] BLOCK3 size=32 bytes, offset=06 - > [00 00 00 00 00 00 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f 00 00 00 00 00 00 00 00 00 00]. Check all blocks for burn... @@ -46,14 +46,14 @@ Peculiarities .. code-block:: none - > espefuse.py dump + > espefuse dump ... BLOCK3 ( ) [3 ] read_regs: 00000000 01000000 05040302 09080706 0d0c0b0a 00000f0e 00000000 00000000 - > espefuse.py summary + > espefuse summary .... BLOCK3 (BLOCK3): Variable Block 3 = 00 00 00 00 00 00 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f 00 00 00 00 00 00 00 00 00 00 R/W 2. Part of the eFuse block can be written at a time. The ``--offset`` argument allows writing to a byte offset inside the eFuse block itself. -3. This command is not suitable for writing key data which will be used by flash encryption or secure boot hardware, use ``burn_key`` for this. +3. This command is not suitable for writing key data which will be used by flash encryption or secure boot hardware, use ``burn-key`` for this. diff --git a/tools/esptool_py/docs/en/espefuse/burn-custom-mac-cmd.rst b/tools/esptool_py/docs/en/espefuse/burn-custom-mac-cmd.rst index fe1ea178a0..f6e262226f 100644 --- a/tools/esptool_py/docs/en/espefuse/burn-custom-mac-cmd.rst +++ b/tools/esptool_py/docs/en/espefuse/burn-custom-mac-cmd.rst @@ -3,7 +3,7 @@ Burn Custom Mac =============== -The ``espefuse.py burn_custom_mac`` command burns a 48-bit Custom MAC Address. +The ``espefuse burn-custom-mac`` command burns a 48-bit Custom MAC Address. Positional arguments: @@ -25,9 +25,9 @@ If ``CUSTOM_MAC`` is placed in an eFuse block with a coding scheme and already h .. code-block:: none - > espefuse.py burn_custom_mac 48:63:92:15:72:16 + > espefuse burn-custom-mac 48:63:92:15:72:16 - === Run "burn_custom_mac" command === + === Run "burn-custom-mac" command === - 'MAC_VERSION' (Version of the MAC field) 0x00 -> 0x1 - 'CUSTOM_MAC' (Custom MAC) 0x000000000000 -> 0x167215926348 - 'CUSTOM_MAC_CRC' (CRC of custom MAC) 0x00 -> 0x75 @@ -44,7 +44,7 @@ If ``CUSTOM_MAC`` is placed in an eFuse block with a coding scheme and already h Custom MAC Address version 1: 48:63:92:15:72:16 (CRC 0x75 OK) Successful - > espefuse.py summary + > espefuse summary ... MAC_VERSION (BLOCK3): Version of the MAC field = Custom MAC in BLOCK3 R/W (0x01) CUSTOM_MAC (BLOCK3): Custom MAC @@ -63,9 +63,9 @@ If ``CUSTOM_MAC`` is placed in an eFuse block with a coding scheme and already h .. code-block:: none - > espefuse.py burn_custom_mac 48:63:92:15:72:16 + > espefuse burn-custom-mac 48:63:92:15:72:16 - === Run "burn_custom_mac" command === + === Run "burn-custom-mac" command === - 'CUSTOM_MAC' (Custom MAC addr) 0x000000000000 -> 0x167215926348 Check all blocks for burn... @@ -85,7 +85,7 @@ If ``CUSTOM_MAC`` is placed in an eFuse block with a coding scheme and already h Custom MAC Address: 48:63:92:15:72:16 (OK) Successful - > espefuse.py summary + > espefuse summary ... CUSTOM_MAC_USED (BLOCK0) Enable CUSTOM_MAC programming = True R/W (0b1) CUSTOM_MAC (BLOCK1) Custom MAC addr @@ -97,9 +97,9 @@ If ``CUSTOM_MAC`` is placed in an eFuse block with a coding scheme and already h .. code-block:: none - > espefuse.py burn_custom_mac 48:63:92:15:72:16 + > espefuse burn-custom-mac 48:63:92:15:72:16 - === Run "burn_custom_mac" command === + === Run "burn-custom-mac" command === - 'CUSTOM_MAC' (Custom MAC Address) 0x000000000000 -> 0x167215926348 Check all blocks for burn... @@ -114,7 +114,7 @@ If ``CUSTOM_MAC`` is placed in an eFuse block with a coding scheme and already h Custom MAC Address: 48:63:92:15:72:16 (OK) Successful - > espefuse.py summary + > espefuse summary ... CUSTOM_MAC (BLOCK3) Custom MAC Address = 48:63:92:15:72:16 (OK) R/W diff --git a/tools/esptool_py/docs/en/espefuse/burn-efuse-cmd.rst b/tools/esptool_py/docs/en/espefuse/burn-efuse-cmd.rst index 4d6446d9c7..bdf59f9cc7 100644 --- a/tools/esptool_py/docs/en/espefuse/burn-efuse-cmd.rst +++ b/tools/esptool_py/docs/en/espefuse/burn-efuse-cmd.rst @@ -1,9 +1,9 @@ .. _burn-efuse-cmd: -Burn Efuse +Burn eFuse ========== -The ``espefuse.py burn_efuse`` command burns eFuses. The arguments to ``burn_efuse`` are eFuse names (as shown in summary output) and new values. +The ``espefuse burn-efuse`` command burns eFuses. The arguments to ``burn-efuse`` are eFuse names (as shown in summary output) and new values. Positional arguments: @@ -16,15 +16,15 @@ Optional arguments: It can be list of eFuse names and values (like EFUSE_NAME1 1 EFUSE_NAME2 7 EFUSE_NAME3 10 etc.). -New values can be a numeric value in decimal or hex (with "0x" prefix). eFuse bits can only be burned from 0 to 1, attempting to set any back to 0 will have no effect. Most eFuses have a limited bit width (many are only 1-bit flags). Longer eFuses (MAC addresses, keys) can be set with this command, but it's better to use a specific command (``burn_custom_mac``, ``burn_key``) for a specific field. +New values can be a numeric value in decimal or hex (with "0x" prefix). eFuse bits can only be burned from 0 to 1, attempting to set any back to 0 will have no effect. Most eFuses have a limited bit width (many are only 1-bit flags). Longer eFuses (MAC addresses, keys) can be set with this command, but it's better to use a specific command (``burn-custom-mac``, ``burn-key``) for a specific field. This command supports simultaneous burning of multiple eFuses, it doesn't matter if they are from different eFuse blocks or not. The format is the same as for burning just one eFuse, just list the eFuse name and value pairs, see the example below. .. code-block:: none - > espefuse.py --port /dev/ttyUSB0 burn_efuse DIS_USB_JTAG 1 VDD_SPI_AS_GPIO 1 + > espefuse --port /dev/ttyUSB0 burn-efuse DIS_USB_JTAG 1 VDD_SPI_AS_GPIO 1 - === Run "burn_efuse" command === + === Run "burn-efuse" command === The efuses to burn: from BLOCK0 - DIS_USB_JTAG @@ -42,7 +42,7 @@ This command supports simultaneous burning of multiple eFuses, it doesn't matter This is an irreversible operation! Type 'BURN' (all capitals) to continue. -By default, ``espefuse.py`` will ask you to type ``BURN`` before it permanently sets eFuses. The ``--do-not-confirm`` option allows you to bypass this. +By default, ``espefuse`` will ask you to type ``BURN`` before it permanently sets eFuses. The ``--do-not-confirm`` option allows you to bypass this. .. code-block:: none @@ -90,7 +90,7 @@ On {IDF_TARGET_NAME} chips without integrated SPI flash, these eFuses are left z On {IDF_TARGET_NAME} chips with integrated internal SPI flash, these eFuses are burned in the factory to the GPIO numbers where the flash is connected. These values override the defaults on boot. -In order to change the SPI flash pin configuration, these eFuses can be burned to the GPIO numbers where the flash is connected. If at least one of these eFuses is burned, all of of them must be set to the correct values. +In order to change the SPI flash pin configuration, these eFuses can be burned to the GPIO numbers where the flash is connected. If at least one of these eFuses is burned, all of them must be set to the correct values. If these eFuses are burned, GPIO1 (U0TXD pin) is no longer consulted to set the boot mode from SPI to HSPI flash on reset. @@ -102,4 +102,4 @@ For example: SPI_PAD_CONFIG_CS0 Override SD_CMD pad (GPIO11/SPICS0) = 32 R/W (0x1e) -If using the ``burn_efuse`` command to configure these pins, always specify the actual GPIO number you wish to set. +If using the ``burn-efuse`` command to configure these pins, always specify the actual GPIO number you wish to set. diff --git a/tools/esptool_py/docs/en/espefuse/burn-key-cmd.rst b/tools/esptool_py/docs/en/espefuse/burn-key-cmd.rst index c6a7714c26..722831808b 100644 --- a/tools/esptool_py/docs/en/espefuse/burn-key-cmd.rst +++ b/tools/esptool_py/docs/en/espefuse/burn-key-cmd.rst @@ -3,7 +3,7 @@ Burn Key ======== -The ``espefuse.py burn_key`` command burns keys to eFuse blocks: +The ``espefuse burn-key`` command burns keys to eFuse blocks: .. list:: @@ -78,18 +78,18 @@ Optional arguments: .. only:: esp32h2 - {IDF_TARGET_NAME} has the ECDSA accelerator for signature purposes and supports private keys based on the NIST192p or NIST256p curve. These two commands below can be used to generate such keys (``PEM`` file). The ``burn_key`` command with the ``ECDSA_KEY`` purpose takes the ``PEM`` file and writes the private key into a eFuse block. The key is written to the block in reverse byte order. + {IDF_TARGET_NAME} has the ECDSA accelerator for signature purposes and supports private keys based on the NIST192p or NIST256p curve. These two commands below can be used to generate such keys (``PEM`` file). The ``burn-key`` command with the ``ECDSA_KEY`` purpose takes the ``PEM`` file and writes the private key into a eFuse block. The key is written to the block in reverse byte order. For NIST192p, the private key is 192 bits long, so 8 padding bytes ("0x00") are added. .. code-block:: none - > espsecure.py generate_signing_key -v 2 -s ecdsa192 ecdsa192.pem + > espsecure generate_signing_key -v 2 -s ecdsa192 ecdsa192.pem ECDSA NIST192p private key in PEM format written to ecdsa192.pem .. code-block:: none - > espsecure.py generate_signing_key -v 2 -s ecdsa256 ecdsa256.pem + > espsecure generate_signing_key -v 2 -s ecdsa256 ecdsa256.pem ECDSA NIST256p private key in PEM format written to ecdsa256.pem .. only:: esp32c2 @@ -146,7 +146,7 @@ By default, when an encryption key block is burned it is also read and write pro .. code-block:: none - espefuse.py burn_key secure_boot_v1 secure_boot_key_v1.bin + espefuse burn-key secure_boot_v1 secure_boot_key_v1.bin .. only:: esp32 @@ -155,7 +155,7 @@ By default, when an encryption key block is burned it is also read and write pro Force Writing a Key ^^^^^^^^^^^^^^^^^^^ -Normally, a key will only be burned if the efuse block has not been previously written to. The ``--force-write-always`` option can be used to ignore this and try to burn the key anyhow. +Normally, a key will only be burned if the eFuse block has not been previously written to. The ``--force-write-always`` option can be used to ignore this and try to burn the key anyhow. Note that this option is still limited by the eFuse hardware - hardware does not allow any eFuse bits to be cleared 1->0, and can not write anything to write protected eFuse blocks. @@ -166,9 +166,9 @@ Usage .. code-block:: none - > espefuse.py burn_key flash_encryption 256bit_fe_key.bin + > espefuse burn-key flash_encryption 256bit_fe_key.bin - === Run "burn_key" command === + === Run "burn-key" command === Sensitive data will be hidden (see --show-sensitive-info) Burn keys to blocks: - BLOCK1 -> [?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ??] @@ -194,7 +194,7 @@ Usage .. code-block:: none - > espefuse.py summary + > espefuse summary ... BLOCK1 (BLOCK1): Flash encryption key = ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? -/- @@ -209,9 +209,9 @@ Usage .. code-block:: none - > espefuse.py burn_key flash_encryption 256bit_fe_key.bin --no-protect-key + > espefuse burn-key flash_encryption 256bit_fe_key.bin --no-protect-key - === Run "burn_key" command === + === Run "burn-key" command === Sensitive data will be hidden (see --show-sensitive-info) Burn keys to blocks: - BLOCK1 -> [?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ??] @@ -234,7 +234,7 @@ Usage .. code-block:: none - > espefuse.py summary + > espefuse summary ... BLOCK1 (BLOCK1): Flash encryption key = 1f 1e 1d 1c 1b 1a 19 18 17 16 15 14 13 12 11 10 0f 0e 0d 0c 0b 0a 09 08 07 06 05 04 03 02 01 00 R/W @@ -256,9 +256,9 @@ Usage .. code-block:: none - > espefuse.py burn_key BLOCK_KEY0 ~/esp/tests/efuse/512bits_0.bin XTS_AES_256_KEY --no-read-protect + > espefuse burn-key BLOCK_KEY0 ~/esp/tests/efuse/512bits_0.bin XTS_AES_256_KEY --no-read-protect - === Run "burn_key" command === + === Run "burn-key" command === Sensitive data will be hidden (see --show-sensitive-info) Burn keys to blocks: - BLOCK_KEY0 -> [?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ??] @@ -290,7 +290,7 @@ Usage Reading updated efuses... Successful - > espefuse.py summary + > espefuse summary ... KEY_PURPOSE_0 (BLOCK0) KEY0 purpose = XTS_AES_256_KEY_1 R/- (0x2) KEY_PURPOSE_1 (BLOCK0) KEY1 purpose = XTS_AES_256_KEY_2 R/- (0x3) @@ -308,11 +308,11 @@ Usage .. code-block:: none - > espefuse.py -c esp32c2 \ - burn_key_digest secure_images/ecdsa256_secure_boot_signing_key_v2.pem \ - burn_key BLOCK_KEY0 images/efuse/128bit_key.bin XTS_AES_128_KEY_DERIVED_FROM_128_EFUSE_BITS + > espefuse -c esp32c2 \ + burn-key-digest secure_images/ecdsa256_secure_boot_signing_key_v2.pem \ + burn-key BLOCK_KEY0 images/efuse/128bit_key.bin XTS_AES_128_KEY_DERIVED_FROM_128_EFUSE_BITS - === Run "burn_key_digest" command === + === Run "burn-key-digest" command === Sensitive data will be hidden (see --show-sensitive-info) Burn keys to blocks: - BLOCK_KEY0_HI_128 -> [?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ??] @@ -321,7 +321,7 @@ Usage Batch mode is enabled, the burn will be done at the end of the command. - === Run "burn_key" command === + === Run "burn-key" command === Sensitive data will be hidden (see --show-sensitive-info) Burn keys to blocks: - BLOCK_KEY0_LOW_128 -> [?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ??] diff --git a/tools/esptool_py/docs/en/espefuse/burn-key-digest-cmd.rst b/tools/esptool_py/docs/en/espefuse/burn-key-digest-cmd.rst index 4402a8c92c..51d88c57bc 100644 --- a/tools/esptool_py/docs/en/espefuse/burn-key-digest-cmd.rst +++ b/tools/esptool_py/docs/en/espefuse/burn-key-digest-cmd.rst @@ -1,9 +1,9 @@ .. _burn-key-digest-cmd: -Burn key Digest +Burn Key Digest =============== -The ``espefuse.py burn_key_digest`` command parses a RSA public key and burns the digest to eFuse block for use with `Secure Boot V2 `_. +The ``espefuse burn-key-digest`` command parses a RSA public key and burns the digest to eFuse block for use with `Secure Boot V2 `_. Positional arguments: @@ -44,9 +44,9 @@ Usage .. code-block:: none - > espefuse.py burn_key_digest secure_boot_key_v2_0.pem + > espefuse burn-key-digest secure_boot_key_v2_0.pem - === Run "burn_key_digest" command === + === Run "burn-key-digest" command === Sensitive data will be hidden (see --show-sensitive-info) - BLOCK2 -> [?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ??] Disabling write to efuse BLOCK2... @@ -64,7 +64,7 @@ Usage Reading updated efuses... Successful - > espefuse.py summary + > espefuse summary ... BLOCK2 (BLOCK2): Secure boot key = a2 cd 39 85 df 00 d7 95 07 0f f6 7c 8b ab e1 7d 39 11 95 c4 5b 37 6e 7b f0 ec 04 5e 36 30 02 5d R/- @@ -75,9 +75,9 @@ Usage .. code-block:: none - > espefuse.py burn_key_digest secure_boot_v2_ecdsa192.pem + > espefuse burn-key-digest secure_boot_v2_ecdsa192.pem - === Run "burn_key_digest" command === + === Run "burn-key-digest" command === Sensitive data will be hidden (see --show-sensitive-info) Burn keys to blocks: - BLOCK_KEY0_HI_128 -> [?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ??] @@ -96,7 +96,7 @@ Usage Reading updated efuses... Successful - > espefuse.py summary + > espefuse summary ... XTS_KEY_LENGTH_256 (BLOCK0) Flash encryption key length = 128 bits key R/W (0b0) ... @@ -114,12 +114,12 @@ Usage .. code-block:: none - > espefuse.py burn_key_digest \ + > espefuse burn-key-digest \ BLOCK_KEY0 ~/esp/tests/efuse/secure_boot_key_v2_0.pem SECURE_BOOT_DIGEST0 \ BLOCK_KEY1 ~/esp/tests/efuse/secure_boot_key_v2_1.pem SECURE_BOOT_DIGEST1 \ BLOCK_KEY2 ~/esp/tests/efuse/secure_boot_key_v2_2.pem SECURE_BOOT_DIGEST2 - === Run "burn_key_digest" command === + === Run "burn-key-digest" command === Sensitive data will be hidden (see --show-sensitive-info) Burn keys to blocks: - BLOCK_KEY0 -> [?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ??] @@ -154,7 +154,7 @@ Usage Reading updated efuses... Successful - > espefuse.py summary + > espefuse summary KEY_PURPOSE_0 (BLOCK0) KEY0 purpose = SECURE_BOOT_DIGEST0 R/- (0x9) KEY_PURPOSE_1 (BLOCK0) KEY1 purpose = SECURE_BOOT_DIGEST1 R/- (0xa) diff --git a/tools/esptool_py/docs/en/espefuse/check-error-cmd.rst b/tools/esptool_py/docs/en/espefuse/check-error-cmd.rst index 0723adf2c2..e80dff1dd3 100644 --- a/tools/esptool_py/docs/en/espefuse/check-error-cmd.rst +++ b/tools/esptool_py/docs/en/espefuse/check-error-cmd.rst @@ -3,7 +3,7 @@ Check Error =========== -The ``espefuse.py check_error`` command checks eFuse errors. It triggers several reads to force the eFuse controller to reload eFuses and update status registers. This command can be run after burn operations to make sure that there is not errors. +The ``espefuse check-error`` command checks eFuse errors. It triggers several reads to force the eFuse controller to reload eFuses and update status registers. This command can be run after burn operations to make sure that there is not errors. Optional argument: @@ -13,7 +13,7 @@ The summary log below shows an error in BLOCK0. .. code-block:: none - > espefuse.py summary + > espefuse summary ... WDT_DELAY_SEL (BLOCK0)[FAIL:1] Selects RTC WDT timeout threshold at startup = False R/W (0b0) ... @@ -31,7 +31,7 @@ Checks the status registers of eFuse blocks and throws an error if there is an e .. code-block:: none - > espefuse.py check_error + > espefuse check-error Error(s) in BLOCK1 [ERRORS:0 FAIL:1] Error(s) in BLOCK2 [ERRORS:1 FAIL:1] @@ -41,7 +41,7 @@ Checks the status registers of eFuse blocks and throws an error if there is an e EFUSE_RD_RS_ERR0_REG 0x00008990 EFUSE_RD_RS_ERR1_REG 0x00000000 - === Run "check_error" command === + === Run "check-error" command === Error(s) in BLOCK1 [ERRORS:0 FAIL:1] Error(s) in BLOCK2 [ERRORS:1 FAIL:1] Error(s) in BLOCK3 [ERRORS:1 FAIL:1] @@ -56,7 +56,7 @@ Repairs encoding errors in eFuse blocks, if possible. .. code-block:: none - > espefuse.py check_error --recovery + > espefuse check-error --recovery Error(s) in BLOCK1 [ERRORS:0 FAIL:1] Error(s) in BLOCK2 [ERRORS:1 FAIL:1] @@ -66,7 +66,7 @@ Repairs encoding errors in eFuse blocks, if possible. EFUSE_RD_RS_ERR0_REG 0x00008990 EFUSE_RD_RS_ERR1_REG 0x00000000 - === Run "check_error" command === + === Run "check-error" command === Error(s) in BLOCK1 [ERRORS:0 FAIL:1] Error(s) in BLOCK2 [ERRORS:1 FAIL:1] Error(s) in BLOCK3 [ERRORS:1 FAIL:1] diff --git a/tools/esptool_py/docs/en/espefuse/dump-cmd.rst b/tools/esptool_py/docs/en/espefuse/dump-cmd.rst index 8f841ccb8e..919a2ababc 100644 --- a/tools/esptool_py/docs/en/espefuse/dump-cmd.rst +++ b/tools/esptool_py/docs/en/espefuse/dump-cmd.rst @@ -3,7 +3,7 @@ Dump ==== -The ``espefuse.py dump`` command allows: +The ``espefuse dump`` command allows: - display raw values of eFuse registers, grouped by block. Output corresponds directly to eFuse register values in the `register space `__. - save dump into files. @@ -13,13 +13,13 @@ Optional arguments: - ``--format`` - Selects the dump format: - ``default`` - Usual console eFuse dump; - ``joint`` - All eFuse blocks are stored in one file; - - ``split`` - Each eFuse block is placed in its own file. The tool will create multiple files based on the given the ``--file_name`` argument. Example: "--file_name /path/blk.bin", blk0.bin, blk1.bin ... blkN.bin. Use the ``burn_block_data`` cmd to write it back to another chip. -- ``--file_name`` - The path to the file in which to save the dump, if not specified, output to the console. + - ``split`` - Each eFuse block is placed in its own file. The tool will create multiple files based on the given the ``--file-name`` argument. Example: "--file-name /path/blk.bin", blk0.bin, blk1.bin ... blkN.bin. Use the ``burn-block-data`` cmd to write it back to another chip. +- ``--file-name`` - The path to the file in which to save the dump, if not specified, output to the console. -Raw Values Of Efuse Registers +Raw Values of eFuse Registers ----------------------------- -The number of blocks depends on the chips and can vary from 4 to 11. A block can have different names, which can be used with ``burn_key`` or ``burn_block_data``. +The number of blocks depends on the chips and can vary from 4 to 11. A block can have different names, which can be used with ``burn-key`` or ``burn-block-data``. The order of registers in the dump: @@ -32,10 +32,9 @@ The order of registers in the dump: .. code-block:: none - > espefuse.py dump + > espefuse dump - Detecting chip type... Unsupported detection protocol, switching and trying again... - Connecting.... + Connecting......... Detecting chip type... ESP32 BLOCK0 ( ) [0 ] read_regs: 00000000 7e5a6e58 00e294b9 0000a200 00000333 00100000 00000004 BLOCK1 (flash_encryption) [1 ] read_regs: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 @@ -48,7 +47,7 @@ The order of registers in the dump: .. code-block:: none - > espefuse.py dump + > espefuse dump Connecting......... Detecting chip type... ESP32-C2 @@ -64,7 +63,7 @@ The order of registers in the dump: .. code-block:: none - > espefuse.py dump + > espefuse dump Connecting.... Detecting chip type... ESP32-C3 @@ -86,14 +85,14 @@ The order of registers in the dump: In the last lines, which are separated from the main dump, you can see the encoding scheme status for each block. If there are all zeros, then there are no coding scheme errors. -Save Dump To Files +Save Dump to Files ------------------ -This command saves dump for each block into a separate file. You need to provide the common path name ``/chip1/blk.bin``, it will create files in the given directory (the directory must exist): /chip1/blk0.bin, /chip1/blk1.bin - /chip1/blkN.bin. Use ``burn_block_data`` command to write them back to another chip. Note that some blocks may be read-protected, in which case the data in the block will be zero. +This command saves dump for each block into a separate file. You need to provide the common path name ``/chip1/blk.bin``, it will create files in the given directory (the directory must exist): /chip1/blk0.bin, /chip1/blk1.bin - /chip1/blkN.bin. Use ``burn-block-data`` command to write them back to another chip. Note that some blocks may be read-protected, in which case the data in the block will be zero. .. code-block:: none - > espefuse.py dump --format split --file_name backup/chip1/blk.bin + > espefuse dump --format split --file-name backup/chip1/blk.bin === Run "dump" command === backup/chip1/blk0.bin @@ -112,7 +111,7 @@ These dump files can be written to another chip: .. code-block:: none - > espefuse.py burn_block_data BLOCK0 backup/chip1/blk0.bin \ + > espefuse burn-block-data BLOCK0 backup/chip1/blk0.bin \ BLOCK1 backup/chip1/blk1.bin \ BLOCK2 backup/chip1/blk2.bin @@ -120,7 +119,7 @@ To save all eFuse blocks in one file, use the following command: .. code-block:: none - > espefuse.py dump --format joint --file_name backup/chip1/efuses.bin + > espefuse dump --format joint --file-name backup/chip1/efuses.bin === Run "dump" command === backup/chip1/efuses.bin diff --git a/tools/esptool_py/docs/en/espefuse/get-custom-mac-cmd.rst b/tools/esptool_py/docs/en/espefuse/get-custom-mac-cmd.rst index 3077e991e7..ece81b594a 100644 --- a/tools/esptool_py/docs/en/espefuse/get-custom-mac-cmd.rst +++ b/tools/esptool_py/docs/en/espefuse/get-custom-mac-cmd.rst @@ -3,7 +3,7 @@ Get Custom Mac ============== -The ``espefuse.py burn_custom_mac`` command prints the Custom MAC Address (``CUSTOM_MAC``). +The ``espefuse get-custom-mac`` command prints the Custom MAC Address (``CUSTOM_MAC``). The chips also have a factory MAC address (eFuse name ``MAC``), which is written at the factory. It can not be changed with this tool. @@ -11,9 +11,9 @@ The chips also have a factory MAC address (eFuse name ``MAC``), which is written .. code-block:: none - > espefuse.py get_custom_mac + > espefuse get-custom-mac - === Run "get_custom_mac" command === + === Run "get-custom-mac" command === Custom MAC Address version 1: 48:63:92:15:72:16 (CRC 0x75 OK) If the custom MAC address is not burned, then you will see the message "Custom MAC Address is not set in the device". And in the summary, those eFuses associated with custom MAC addresses will not show up. @@ -22,9 +22,9 @@ The chips also have a factory MAC address (eFuse name ``MAC``), which is written .. code-block:: none - > espefuse.py get_custom_mac + > espefuse get-custom-mac - === Run "get_custom_mac" command === + === Run "get-custom-mac" command === Custom MAC Address: 48:63:92:15:72:16 (OK) If the custom MAC address is not burned, then you will see the message "Custom MAC Address: 00:00:00:00:00:00 (OK)". diff --git a/tools/esptool_py/docs/en/espefuse/inc/summary_ESP32-C2.rst b/tools/esptool_py/docs/en/espefuse/inc/summary_ESP32-C2.rst index f42d80d43a..efdca4e909 100644 --- a/tools/esptool_py/docs/en/espefuse/inc/summary_ESP32-C2.rst +++ b/tools/esptool_py/docs/en/espefuse/inc/summary_ESP32-C2.rst @@ -1,6 +1,6 @@ .. code-block:: none - > espefuse.py -p PORT summary + > espefuse -p PORT summary Connecting................... Detecting chip type... ESP32-C2 diff --git a/tools/esptool_py/docs/en/espefuse/inc/summary_ESP32-C3.rst b/tools/esptool_py/docs/en/espefuse/inc/summary_ESP32-C3.rst index c20b5c58df..ba3cc874c1 100644 --- a/tools/esptool_py/docs/en/espefuse/inc/summary_ESP32-C3.rst +++ b/tools/esptool_py/docs/en/espefuse/inc/summary_ESP32-C3.rst @@ -1,6 +1,6 @@ .. code-block:: none - > espefuse.py -p PORT summary + > espefuse -p PORT summary Connecting.... Detecting chip type... ESP32-C3 diff --git a/tools/esptool_py/docs/en/espefuse/inc/summary_ESP32-C5.rst b/tools/esptool_py/docs/en/espefuse/inc/summary_ESP32-C5.rst new file mode 100644 index 0000000000..314a061e8d --- /dev/null +++ b/tools/esptool_py/docs/en/espefuse/inc/summary_ESP32-C5.rst @@ -0,0 +1,198 @@ +.. code-block:: none + + > espefuse -p PORT summary + + Connecting.... + Detecting chip type... ESP32-C5 + + === Run "summary" command === + EFUSE_NAME (Block) Description = [Meaningful Value] [Readable/Writeable] (Hex Value) + ---------------------------------------------------------------------------------------- + Config fuses: + WR_DIS (BLOCK0) Disable programming of individual eFuses = 4608 R/W (0x00001200) + RD_DIS (BLOCK0) Disable reading from BlOCK4-10 = 0 R/W (0b0000000) + DIS_ICACHE (BLOCK0) Represents whether icache is disabled or enabled.\ = False R/W (0b0) + \ 1: disabled\\ 0: enabled\\ + DIS_TWAI (BLOCK0) Represents whether TWAI function is disabled or en = False R/W (0b0) + abled.\\ 1: disabled\\ 0: enabled\\ + KM_DISABLE_DEPLOY_MODE (BLOCK0) Represents whether the deploy mode of key manager = 0 R/W (0x0) + is disable or not. \\ 1: disabled \\ 0: enabled.\\ + KM_RND_SWITCH_CYCLE (BLOCK0) Set the bits to control key manager random number = 0 R/W (0b00) + switch cycle. 0: control by register. 1: 8 km clk + cycles. 2: 16 km cycles. 3: 32 km cycles + KM_DEPLOY_ONLY_ONCE (BLOCK0) Set each bit to control whether corresponding key = 0 R/W (0x0) + can only be deployed once. 1 is true; 0 is false. + bit 0: ecsda; bit 1: xts; bit2: hmac; bit3: ds + DIS_DIRECT_BOOT (BLOCK0) Represents whether direct boot mode is disabled or = False R/W (0b0) + enabled.\\ 1: disabled\\ 0: enabled\\ + UART_PRINT_CONTROL (BLOCK0) Set the default UARTboot message output mode = Enable R/W (0b00) + HYS_EN_PAD (BLOCK0) Represents whether the hysteresis function of corr = False R/W (0b0) + esponding PAD is enabled.\\ 1: enabled\\ 0:disable + d\\ + HUK_GEN_STATE (BLOCK0) Set the bits to control validation of HUK generate = 0 R/W (0b000000000) + mode.\\ Odd of 1 is invalid.\\ Even of 1 is valid + .\\ + XTAL_48M_SEL (BLOCK0) Represents whether XTAL frequency is 48MHz or not. = 1 R/W (0b001) + If not; 40MHz XTAL will be used. If this field co + ntains Odd number bit 1: Enable 48MHz XTAL\ Even n + umber bit 1: Enable 40MHz XTAL + XTAL_48M_SEL_MODE (BLOCK0) Specify the XTAL frequency selection is decided by = True R/W (0b1) + eFuse or strapping-PAD-state. 1: eFuse\\ 0: strap + ping-PAD-state + ECC_FORCE_CONST_TIME (BLOCK0) Represents whether to force ecc to use const-time = False R/W (0b0) + calculation mode. \\ 1: Enable. \\ 0: Disable + PSRAM_CAP (BLOCK1) Psram capacity = 0 R/W (0b000) + PSRAM_VENDOR (BLOCK1) Psram vendor = 0 R/W (0b00) + TEMP (BLOCK1) Temp (die embedded inside) = 0 R/W (0b00) + TRIM_N_BIAS (BLOCK1) PADC CAL N bias = 0 R/W (0b00000) + TRIM_P_BIAS (BLOCK1) PADC CAL P bias = 0 R/W (0b00000) + BLOCK_USR_DATA (BLOCK3) User data + = 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 R/W + BLOCK_SYS_DATA2 (BLOCK10) System data part 2 (reserved) + = 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 R/W + + Flash fuses: + FLASH_TPUW (BLOCK0) Represents the flash waiting time after power-up; = 0 R/W (0x0) + in unit of ms. When the value less than 15; the wa + iting time is the programmed value. Otherwise; the + waiting time is 2 times the programmed value + FORCE_SEND_RESUME (BLOCK0) Represents whether ROM code is forced to send a re = False R/W (0b0) + sume command during SPI boot.\\ 1: forced\\ 0:not + forced\\ + FLASH_CAP (BLOCK1) Flash capacity = 0 R/W (0b000) + FLASH_VENDOR (BLOCK1) Flash vendor = 0 R/W (0b000) + + Identity fuses: + WAFER_VERSION_MINOR (BLOCK1) Minor chip version = 0 R/W (0x0) + WAFER_VERSION_MAJOR (BLOCK1) Minor chip version = 0 R/W (0b00) + DISABLE_WAFER_VERSION_MAJOR (BLOCK1) Disables check of wafer version major = False R/W (0b0) + DISABLE_BLK_VERSION_MAJOR (BLOCK1) Disables check of blk version major = False R/W (0b0) + BLK_VERSION_MINOR (BLOCK1) BLK_VERSION_MINOR of BLOCK2 = 0 R/W (0b000) + BLK_VERSION_MAJOR (BLOCK1) BLK_VERSION_MAJOR of BLOCK2 = 0 R/W (0b00) + PKG_VERSION (BLOCK1) Package version = 0 R/W (0b000) + PA_TRIM_VERSION (BLOCK1) PADC CAL PA trim version = 0 R/W (0b000) + OPTIONAL_UNIQUE_ID (BLOCK2) Optional unique 128-bit ID + = 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 R/W + + Jtag fuses: + JTAG_SEL_ENABLE (BLOCK0) Represents whether the selection between usb_to_jt = False R/W (0b0) + ag and pad_to_jtag through strapping gpio15 when b + oth EFUSE_DIS_PAD_JTAG and EFUSE_DIS_USB_JTAG are + equal to 0 is enabled or disabled.\\ 1: enabled\\ + 0: disabled\\ + SOFT_DIS_JTAG (BLOCK0) Represents whether JTAG is disabled in soft way.\\ = 0 R/W (0b000) + Odd number: disabled\\ Even number: enabled\\ + DIS_PAD_JTAG (BLOCK0) Represents whether JTAG is disabled in the hard wa = False R/W (0b0) + y(permanently).\\ 1: disabled\\ 0: enabled\\ + + Mac fuses: + MAC (BLOCK1) MAC address + = 60:55:f9:f9:54:1c (OK) R/W + MAC_EXT (BLOCK1) Represents the extended bits of MAC address = ff:fe (OK) R/W + CUSTOM_MAC (BLOCK3) Custom MAC + = 00:00:00:00:00:00 (OK) R/W + MAC_EUI64 (BLOCK1) calc MAC_EUI64 = MAC[0]:MAC[1]:MAC[2]:MAC_EXT[0]:M + = 60:55:f9:ff:fe:f9:54:1c (OK) R/W + AC_EXT[1]:MAC[3]:MAC[4]:MAC[5] + + Security fuses: + DIS_FORCE_DOWNLOAD (BLOCK0) Represents whether the function that forces chip i = False R/W (0b0) + nto download mode is disabled or enabled.\\ 1: dis + abled\\ 0: enabled\\ + SPI_DOWNLOAD_MSPI_DIS (BLOCK0) Represents whether SPI0 controller during boot_mod = False R/W (0b0) + e_download is disabled or enabled.\\ 1: disabled\\ + 0: enabled\\ + DIS_DOWNLOAD_MANUAL_ENCRYPT (BLOCK0) Represents whether flash encrypt function is disab = False R/W (0b0) + led or enabled(except in SPI boot mode).\\ 1: disa + bled\\ 0: enabled\\ + FORCE_USE_KEY_MANAGER_KEY (BLOCK0) Set each bit to control whether corresponding key = 0 R/W (0x0) + must come from key manager. 1 is true; 0 is false. + bit 0: ecsda; bit 1: xts; bit2: hmac; bit3: ds + FORCE_DISABLE_SW_INIT_KEY (BLOCK0) Set this bit to disable software written init key; = False R/W (0b0) + and force use efuse_init_key + SPI_BOOT_CRYPT_CNT (BLOCK0) Enables flash encryption when 1 or 3 bits are set = Disable R/W (0b000) + and disables otherwise + SECURE_BOOT_KEY_REVOKE0 (BLOCK0) Revoke 1st secure boot key = False R/W (0b0) + SECURE_BOOT_KEY_REVOKE1 (BLOCK0) Revoke 2nd secure boot key = False R/W (0b0) + SECURE_BOOT_KEY_REVOKE2 (BLOCK0) Revoke 3rd secure boot key = False R/W (0b0) + KEY_PURPOSE_0 (BLOCK0) Represents the purpose of Key0 = USER R/W (0x0) + KEY_PURPOSE_1 (BLOCK0) Represents the purpose of Key1 = USER R/- (0x0) + KEY_PURPOSE_2 (BLOCK0) Represents the purpose of Key2 = USER R/W (0x0) + KEY_PURPOSE_3 (BLOCK0) Represents the purpose of Key3 = USER R/W (0x0) + KEY_PURPOSE_4 (BLOCK0) Represents the purpose of Key4 = USER R/- (0x0) + KEY_PURPOSE_5 (BLOCK0) Represents the purpose of Key5 = USER R/W (0x0) + SEC_DPA_LEVEL (BLOCK0) Represents the spa secure level by configuring the = 0 R/W (0b00) + clock random divide mode + SECURE_BOOT_EN (BLOCK0) Represents whether secure boot is enabled or disab = False R/W (0b0) + led.\\ 1: enabled\\ 0: disabled\\ + SECURE_BOOT_AGGRESSIVE_REVOKE (BLOCK0) Represents whether revoking aggressive secure boot = False R/W (0b0) + is enabled or disabled.\\ 1: enabled.\\ 0: disabl + ed\\ + KM_XTS_KEY_LENGTH_256 (BLOCK0) Set this bitto configure flash encryption use xts- = False R/W (0b0) + 128 key. else use xts-256 key + DIS_DOWNLOAD_MODE (BLOCK0) Represents whether Download mode is disabled or en = False R/W (0b0) + abled.\\ 1: disabled\\ 0: enabled\\ + LOCK_KM_KEY (BLOCK0) Represetns whether to lock the efuse xts key.\\ 1. = False R/W (0b0) + Lock\\ 0: Unlock\\ + ENABLE_SECURITY_DOWNLOAD (BLOCK0) Represents whether security download is enabled or = False R/W (0b0) + disabled.\\ 1: enabled\\ 0: disabled\\ + SECURE_VERSION (BLOCK0) Represents the version used by ESP-IDF anti-rollba = 0 R/W (0x0000) + ck feature + SECURE_BOOT_DISABLE_FAST_WAKE (BLOCK0) Represents whether FAST VERIFY ON WAKE is disabled = False R/W (0b0) + or enabled when Secure Boot is enabled.\\ 1: disa + bled\\ 0: enabled\\ + XTS_DPA_PSEUDO_LEVEL (BLOCK0) Represents the pseudo round level of xts-aes anti- = 0 R/W (0b00) + dpa attack.\\ 3: High.\\ 2: Moderate 1. Low\\ 0: D + isabled\\ + XTS_DPA_CLK_ENABLE (BLOCK0) Represents whether xts-aes anti-dpa attack clock i = False R/W (0b0) + s enabled.\\ 1. Enable.\\ 0: Disable.\\ + ECDSA_DISABLE_P192 (BLOCK0) Represents whether to disable P192 curve in ECDSA. = False R/W (0b0) + \\ 1: Disabled.\\ 0: Not disable + BLOCK_KEY0 (BLOCK4) + Purpose: USER + Key0 or user data + = 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 R/W + BLOCK_KEY1 (BLOCK5) + Purpose: USER + Key1 or user data + = 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 R/W + BLOCK_KEY2 (BLOCK6) + Purpose: USER + Key2 or user data + = 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 R/W + BLOCK_KEY3 (BLOCK7) + Purpose: USER + Key3 or user data + = 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 R/W + BLOCK_KEY4 (BLOCK8) + Purpose: USER + Key4 or user data + = 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 R/W + BLOCK_KEY5 (BLOCK9) + Purpose: USER + Key5 or user data + = 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 R/W + + Usb fuses: + DIS_USB_JTAG (BLOCK0) Represents whether the function of usb switch to j = False R/W (0b0) + tag is disabled or enabled.\\ 1: disabled\\ 0: ena + bled\\ + USB_EXCHG_PINS (BLOCK0) Represents whether the D+ and D- pins is exchanged = False R/W (0b0) + .\\ 1: exchanged\\ 0: not exchanged\\ + DIS_USB_SERIAL_JTAG_ROM_PRINT (BLOCK0) Represents whether print from USB-Serial-JTAG is d = False R/W (0b0) + isabled or enabled.\\ 1: disabled\\ 0: enabled\\ + DIS_USB_SERIAL_JTAG_DOWNLOAD_MODE (BLOCK0) Represents whether the USB-Serial-JTAG download fu = False R/W (0b0) + nction is disabled or enabled.\\ 1: Disable\\ 0: E + nable\\ + + Vdd fuses: + VDD_SPI_AS_GPIO (BLOCK0) Represents whether vdd spi pin is functioned as gp = False R/W (0b0) + io.\\ 1: functioned\\ 0: not functioned\\ + + Wdt fuses: + WDT_DELAY_SEL (BLOCK0) Represents the threshold level of the RTC watchdog = 0 R/W (0b00) + STG0 timeout.\\ 0: Original threshold configurati + on value of STG0 *2 \\1: Original threshold config + uration value of STG0 *4 \\2: Original threshold c + onfiguration value of STG0 *8 \\3: Original thresh + old configuration value of STG0 *16 \\ diff --git a/tools/esptool_py/docs/en/espefuse/inc/summary_ESP32-C6.rst b/tools/esptool_py/docs/en/espefuse/inc/summary_ESP32-C6.rst index fdfc78bcec..134f2d41a7 100644 --- a/tools/esptool_py/docs/en/espefuse/inc/summary_ESP32-C6.rst +++ b/tools/esptool_py/docs/en/espefuse/inc/summary_ESP32-C6.rst @@ -1,6 +1,6 @@ .. code-block:: none - > espefuse.py -p PORT summary + > espefuse -p PORT summary Connecting.... Detecting chip type... ESP32-C6 diff --git a/tools/esptool_py/docs/en/espefuse/inc/summary_ESP32-C61.rst b/tools/esptool_py/docs/en/espefuse/inc/summary_ESP32-C61.rst new file mode 100644 index 0000000000..faa6e06de7 --- /dev/null +++ b/tools/esptool_py/docs/en/espefuse/inc/summary_ESP32-C61.rst @@ -0,0 +1,161 @@ +.. code-block:: none + + > espefuse -p PORT summary + + Connecting.... + Detecting chip type... ESP32-C61 + + === Run "summary" command === + EFUSE_NAME (Block) Description = [Meaningful Value] [Readable/Writeable] (Hex Value) + ---------------------------------------------------------------------------------------- + Config fuses: + WR_DIS (BLOCK0) Disable programming of individual eFuses = 0 R/W (0x00000000) + RD_DIS (BLOCK0) Disable reading from BlOCK4-10 = 0 R/W (0b0000000) + DIS_ICACHE (BLOCK0) Represents whether icache is disabled or enabled.\ = False R/W (0b0) + \ 1: disabled\\ 0: enabled\\ + DIS_DIRECT_BOOT (BLOCK0) Represents whether direct boot mode is disabled or = False R/W (0b0) + enabled.\\ 1. Disable\\ 0: Enable\\ + UART_PRINT_CONTROL (BLOCK0) Represents the types of UART printing = 0 R/W (0b00) + HYS_EN_PAD (BLOCK0) Represents whether the hysteresis function of corr = False R/W (0b0) + esponding PAD is enabled.\\ 1: enabled\\ 0:disable + d\\ + DIS_WIFI6 (BLOCK0) Represents whether the WiFi 6 feature is enable or = False R/W (0b0) + disable.\\ 1: WiFi 6 is disable\\ 0: WiFi 6 is en + abled.\\ + ECC_FORCE_CONST_TIME (BLOCK0) Represents whether to force ecc to use const-time = False R/W (0b0) + calculation mode. \\ 1: Enable. \\ 0: Disable + PSRAM_CAP (BLOCK1) PSRAM capacity = 1 R/W (0b001) + PSRAM_VENDOR (BLOCK1) PSRAM vendor = 1 R/W (0b01) + TEMP (BLOCK1) Temperature = 1 R/W (0b01) + BLOCK_USR_DATA (BLOCK3) User data + = 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 R/W + BLOCK_SYS_DATA2 (BLOCK10) System data part 2 (reserved) + = 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 R/W + + Flash fuses: + FLASH_TPUW (BLOCK0) Represents the flash waiting time after power-up; = 0 R/W (0x0) + in unit of ms. When the value less than 15; the wa + iting time is programmed value. Otherwise; the wai + ting time is 2 times the programmed value + FORCE_SEND_RESUME (BLOCK0) Represents whether ROM code is forced to send a re = False R/W (0b0) + sume command during SPI boot + FLASH_CAP (BLOCK1) Flash capacity = 0 R/W (0b000) + FLASH_VENDOR (BLOCK1) Flash vendor = 0 R/W (0b000) + + Identity fuses: + WAFER_VERSION_MINOR (BLOCK1) Minor chip version = 1 R/W (0x1) + WAFER_VERSION_MAJOR (BLOCK1) Major chip version = 0 R/W (0b00) + DISABLE_WAFER_VERSION_MAJOR (BLOCK1) Disables check of wafer version major = False R/W (0b0) + DISABLE_BLK_VERSION_MAJOR (BLOCK1) Disables check of blk version major = False R/W (0b0) + BLK_VERSION_MINOR (BLOCK1) BLK_VERSION_MINOR of BLOCK2 = 0 R/W (0b000) + BLK_VERSION_MAJOR (BLOCK1) BLK_VERSION_MAJOR of BLOCK2 = 0 R/W (0b00) + PKG_VERSION (BLOCK1) Package version = 0 R/W (0b000) + OPTIONAL_UNIQUE_ID (BLOCK2) Optional unique 128-bit ID + = 75 7f 2d 6e 1c 1c 60 c6 6a 63 e6 d0 d8 8a 5b 14 R/W + + Jtag fuses: + JTAG_SEL_ENABLE (BLOCK0) Represents whether the selection between usb_to_jt = False R/W (0b0) + ag and pad_to_jtag through strapping gpio15 when b + oth EFUSE_DIS_PAD_JTAG and EFUSE_DIS_USB_JTAG are + equal to 0 is enabled or disabled.\\ 1: enabled\\ + 0: disabled\\ + DIS_PAD_JTAG (BLOCK0) Represents whether JTAG is disabled in the hard wa = False R/W (0b0) + y(permanently).\\ 1: disabled\\ 0: enabled\\ + + Mac fuses: + MAC (BLOCK1) MAC address + = 60:55:f9:fb:17:58 (OK) R/W + CUSTOM_MAC (BLOCK3) Custom MAC + = 00:00:00:00:00:00 (OK) R/W + + Security fuses: + DIS_FORCE_DOWNLOAD (BLOCK0) Represents whether the function that forces chip i = False R/W (0b0) + nto download mode is disabled or enabled.\\ 1: dis + abled\\ 0: enabled\\ + SPI_DOWNLOAD_MSPI_DIS (BLOCK0) Represents whether SPI0 controller during boot_mod = False R/W (0b0) + e_download is disabled or enabled.\\ 1: disabled\\ + 0: enabled\\ + DIS_DOWNLOAD_MANUAL_ENCRYPT (BLOCK0) Represents whether flash encrypt function is disab = False R/W (0b0) + led or enabled(except in SPI boot mode).\\ 1: disa + bled\\ 0: enabled\\ + SPI_BOOT_CRYPT_CNT (BLOCK0) Enables flash encryption when 1 or 3 bits are set = Disable R/W (0b000) + and disables otherwise + SECURE_BOOT_KEY_REVOKE0 (BLOCK0) Revoke 1st secure boot key = False R/W (0b0) + SECURE_BOOT_KEY_REVOKE1 (BLOCK0) Revoke 2nd secure boot key = False R/W (0b0) + SECURE_BOOT_KEY_REVOKE2 (BLOCK0) Revoke 3rd secure boot key = False R/W (0b0) + KEY_PURPOSE_0 (BLOCK0) Represents the purpose of Key0 = USER R/W (0x0) + KEY_PURPOSE_1 (BLOCK0) Represents the purpose of Key1 = USER R/W (0x0) + KEY_PURPOSE_2 (BLOCK0) Represents the purpose of Key2 = USER R/W (0x0) + KEY_PURPOSE_3 (BLOCK0) Represents the purpose of Key3 = USER R/W (0x0) + KEY_PURPOSE_4 (BLOCK0) Represents the purpose of Key4 = USER R/W (0x0) + KEY_PURPOSE_5 (BLOCK0) Represents the purpose of Key5 = USER R/W (0x0) + SEC_DPA_LEVEL (BLOCK0) Represents the spa secure level by configuring the = 0 R/W (0b00) + clock random divide mode + SECURE_BOOT_EN (BLOCK0) Represents whether secure boot is enabled or disab = False R/W (0b0) + led.\\ 1: enabled\\ 0: disabled\\ + SECURE_BOOT_AGGRESSIVE_REVOKE (BLOCK0) Represents whether revoking aggressive secure boot = False R/W (0b0) + is enabled or disabled.\\ 1: enabled.\\ 0: disabl + ed\\ + DIS_DOWNLOAD_MODE (BLOCK0) Represents whether Download mode is disable or ena = False R/W (0b0) + ble.\\ 1. Disable\\ 0: Enable\\ + ENABLE_SECURITY_DOWNLOAD (BLOCK0) Represents whether security download is enabled or = False R/W (0b0) + disabled.\\ 1: Enable\\ 0: Disable\\ + SECURE_VERSION (BLOCK0) Represents the version used by ESP-IDF anti-rollba = 0 R/W (0x0000) + ck feature + SECURE_BOOT_DISABLE_FAST_WAKE (BLOCK0) Represents whether FAST_VERIFY_ON_WAKE is disable = False R/W (0b0) + or enable when Secure Boot is enable + XTS_DPA_CLK_ENABLE (BLOCK0) Represents whether anti-dpa attack clock function = False R/W (0b0) + is enabled.\\ 1. Enable\\ 0: Disable\\ + XTS_DPA_PSEUDO_LEVEL (BLOCK0) Represents the anti-dpa attack pseudo function lev = 0 R/W (0b00) + el.\\ 3:High\\ 2: Moderate\\ 1: Low\\ 0: Decided b + y register configuration\\ + ECDSA_DISABLE_P192 (BLOCK0) Represents whether to disable P192 curve in ECDSA. = False R/W (0b0) + \\ 1: Disabled.\\ 0: Not disable + BLOCK_KEY0 (BLOCK4) + Purpose: USER + Key0 or user data + = 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 R/W + BLOCK_KEY1 (BLOCK5) + Purpose: USER + Key1 or user data + = 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 R/W + BLOCK_KEY2 (BLOCK6) + Purpose: USER + Key2 or user data + = 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 R/W + BLOCK_KEY3 (BLOCK7) + Purpose: USER + Key3 or user data + = 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 R/W + BLOCK_KEY4 (BLOCK8) + Purpose: USER + Key4 or user data + = 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 R/W + BLOCK_KEY5 (BLOCK9) + Purpose: USER + Key5 or user data + = 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 R/W + + Usb fuses: + DIS_USB_JTAG (BLOCK0) Represents whether the function of usb switch to j = False R/W (0b0) + tag is disabled or enabled.\\ 1: disabled\\ 0: ena + bled\\ + USB_EXCHG_PINS (BLOCK0) Represents whether the D+ and D- pins is exchanged = False R/W (0b0) + .\\ 1: exchanged\\ 0: not exchanged\\ + DIS_USB_SERIAL_JTAG_ROM_PRINT (BLOCK0) Represents whether print from USB-Serial-JTAG is d = False R/W (0b0) + isabled or enabled.\\ 1. Disable\\ 0: Enable\\ + DIS_USB_SERIAL_JTAG_DOWNLOAD_MODE (BLOCK0) Represents whether the USB-Serial-JTAG download fu = False R/W (0b0) + nction is disabled or enabled.\\ 1: Disable\\ 0: E + nable\\ + + Vdd fuses: + VDD_SPI_AS_GPIO (BLOCK0) Represents whether vdd spi pin is functioned as gp = False R/W (0b0) + io.\\ 1: functioned\\ 0: not functioned\\ + + Wdt fuses: + WDT_DELAY_SEL (BLOCK0) Represents the threshold level of the RTC watchdog = 0 R/W (0b00) + STG0 timeout.\\ 0: Original threshold configurati + on value of STG0 *2 \\1: Original threshold config + uration value of STG0 *4 \\2: Original threshold c + onfiguration value of STG0 *8 \\3: Original thresh + old configuration value of STG0 *16 \\ diff --git a/tools/esptool_py/docs/en/espefuse/inc/summary_ESP32-H2.rst b/tools/esptool_py/docs/en/espefuse/inc/summary_ESP32-H2.rst index 53c48e830e..74a033bd35 100644 --- a/tools/esptool_py/docs/en/espefuse/inc/summary_ESP32-H2.rst +++ b/tools/esptool_py/docs/en/espefuse/inc/summary_ESP32-H2.rst @@ -1,6 +1,6 @@ .. code-block:: none - > espefuse.py -p PORT summary + > espefuse -p PORT summary Connecting.... Detecting chip type... ESP32-H2 diff --git a/tools/esptool_py/docs/en/espefuse/inc/summary_ESP32-P4.rst b/tools/esptool_py/docs/en/espefuse/inc/summary_ESP32-P4.rst index e03d5f06ac..aef4bdebad 100644 --- a/tools/esptool_py/docs/en/espefuse/inc/summary_ESP32-P4.rst +++ b/tools/esptool_py/docs/en/espefuse/inc/summary_ESP32-P4.rst @@ -1,6 +1,6 @@ .. code-block:: none - > espefuse.py -p PORT summary + > espefuse -p PORT summary Connecting.... Detecting chip type... ESP32-P4 @@ -64,7 +64,7 @@ Jtag fuses: JTAG_SEL_ENABLE (BLOCK0) Represents whether the selection between usb_to_jt = False R/W (0b0) - ag and pad_to_jtag through strapping gpio15 when b + ag and pad_to_jtag through strapping gpio34 when b oth EFUSE_DIS_PAD_JTAG and EFUSE_DIS_USB_JTAG are equal to 0 is enabled or disabled. 1: enabled. 0: disabled diff --git a/tools/esptool_py/docs/en/espefuse/inc/summary_ESP32-S2.rst b/tools/esptool_py/docs/en/espefuse/inc/summary_ESP32-S2.rst index 8589c36207..c1d918f392 100644 --- a/tools/esptool_py/docs/en/espefuse/inc/summary_ESP32-S2.rst +++ b/tools/esptool_py/docs/en/espefuse/inc/summary_ESP32-S2.rst @@ -1,6 +1,6 @@ .. code-block:: none - > espefuse.py -p PORT summary + > espefuse -p PORT summary Connecting.... Detecting chip type... ESP32-S2 diff --git a/tools/esptool_py/docs/en/espefuse/inc/summary_ESP32-S3.rst b/tools/esptool_py/docs/en/espefuse/inc/summary_ESP32-S3.rst index dbd4b05859..9b32662a4b 100644 --- a/tools/esptool_py/docs/en/espefuse/inc/summary_ESP32-S3.rst +++ b/tools/esptool_py/docs/en/espefuse/inc/summary_ESP32-S3.rst @@ -1,6 +1,6 @@ .. code-block:: none - > espefuse.py -p PORT summary + > espefuse -p PORT summary Connecting.... Detecting chip type... ESP32-S3 @@ -57,7 +57,7 @@ DIS_PAD_JTAG (BLOCK0) Set this bit to disable JTAG in the hard way. JTAG = False R/W (0b0) is disabled permanently STRAP_JTAG_SEL (BLOCK0) Set this bit to enable selection between usb_to_jt = False R/W (0b0) - ag and pad_to_jtag through strapping gpio10 when b + ag and pad_to_jtag through strapping gpio3 when b oth reg_dis_usb_jtag and reg_dis_pad_jtag are equa l to 0 diff --git a/tools/esptool_py/docs/en/espefuse/inc/summary_ESP32.rst b/tools/esptool_py/docs/en/espefuse/inc/summary_ESP32.rst index ff4d018f07..bc323998e4 100644 --- a/tools/esptool_py/docs/en/espefuse/inc/summary_ESP32.rst +++ b/tools/esptool_py/docs/en/espefuse/inc/summary_ESP32.rst @@ -1,6 +1,6 @@ .. code-block:: none - > espefuse.py -p PORT summary + > espefuse -p PORT summary Connecting........__ Detecting chip type... ESP32 diff --git a/tools/esptool_py/docs/en/espefuse/index.rst b/tools/esptool_py/docs/en/espefuse/index.rst index 26e19cb496..3fefe9a15b 100644 --- a/tools/esptool_py/docs/en/espefuse/index.rst +++ b/tools/esptool_py/docs/en/espefuse/index.rst @@ -1,19 +1,19 @@ .. _espefuse: -espefuse.py -=========== +espefuse +======== -``espefuse.py`` is a tool for communicating with Espressif chips for the purpose of reading/writing ("burning") the one-time-programmable eFuses. Burning occurs only in one direction from 0 to 1 (never cleared 1->0). +``espefuse`` is a tool for communicating with Espressif chips for the purpose of reading/writing ("burning") the one-time-programmable eFuses. Burning occurs only in one direction from 0 to 1 (never cleared 1->0). .. warning:: Because eFuse is one-time-programmable, it is possible to permanently damage or "brick" your {IDF_TARGET_NAME} using this tool. Use it with great care. -For more details about Espressif chips eFuse features, see the `Technical Reference Manual `__. +For more details about Espressif chips eFuse features, see the `{IDF_TARGET_NAME} Technical Reference Manual <{IDF_TARGET_TRM_EN_URL}>`__. -``espefuse.py`` is installed alongside ``esptool.py``, so if ``esptool.py`` (v2.0 or newer) is available on the PATH then ``espefuse.py`` should be as well. +``espefuse`` is installed alongside ``esptool``, so if ``esptool`` (v2.0 or newer) is available on the PATH then ``espefuse`` should be as well. -Initial State of Efuses +Initial State of eFuses ----------------------- On relatively new chip, most eFuses are unburned (value 0). Some eFuses are already burned at the factory stage: @@ -31,34 +31,33 @@ Supported Commands dump summary - burn_efuse - burn_block_data - burn_bit - read_protect_efuse and write_protect_efuse - burn_key - burn_key_digest - burn_custom_mac - get_custom_mac - adc_info - set_flash_voltage - execute_scripts - check_error + burn-efuse + burn-block-data + burn-bit + read-protect-efuse and write-protect-efuse + burn-key + burn-key-digest + burn-custom-mac + get-custom-mac + adc-info + set-flash-voltage + check-error Optional General Arguments Of Commands -------------------------------------- -- ``-h``, ``--help`` - Show help message and exit. Use ``-h`` to see a summary of all available commands and command line options. To see all options for a particular chip and command, add ``-c {IDF_TARGET_NAME}`` and ``-h`` to the command name, i.e. ``espefuse.py -c {IDF_TARGET_NAME} burn_key -h``. +- ``-h``, ``--help`` - Show help message and exit. Use ``-h`` to see a summary of all available commands and command line options. To see all options for a particular chip and command, add ``-c {IDF_TARGET_NAME}`` and ``-h`` to the command name, i.e. ``espefuse -c {IDF_TARGET_NAME} burn-key -h``. - ``--chip``, ``-c`` - Target chip type. If this argument is omitted, the tool automatically detects the chip type when connected. But if the command has a help option, the chip is not connected, and the default chip is ``esp32``, please specify the specific type of chip to get the correct help. Example of usage: ``-c esp32``, ``-c esp32c3``, ``-c esp32s2`` and others. - ``--baud``, ``-b`` - Serial port baud rate, the same as for esptool. -- ``--port``, ``-p`` - Serial port device, ``-p /dev/ttyUSB0`` (Linux and macOS) or ``-p COM1`` (Windows). -- ``--before`` - What to do before connecting to the chip: ``default_reset``, ``no_reset``, ``esp32r1``, ``no_reset_no_sync``. +- ``--port``, ``-p`` - Serial port device, for example: ``-p /dev/ttyUSB0`` (Linux and macOS) or ``-p COM1`` (Windows). +- ``--before`` - What to do before connecting to the chip: ``default-reset``, ``no-reset``, ``esp32r1``, ``no-reset-no-sync``. - ``--debug``, ``-d`` - Show debugging information. - ``--virt`` - For host tests. The tool will work in the virtual mode (without connecting to a chip). - ``--path-efuse-file`` - For host tests. Use it together with ``--virt`` option. The tool will work in the virtual mode (without connecting to a chip) and save eFuse memory to a given file. If the file does not exists the tool creates it. To reset written eFuses just delete the file. Usage: ``--path-efuse-file efuse_memory.bin``. - ``--do-not-confirm`` - Do not pause for confirmation before permanently writing eFuses. Use with caution. If this option is not used, a manual confirmation step is required, you need to enter the word ``BURN`` to continue burning. - ``--extend-efuse-table`` - CSV file from `ESP-IDF `_ (esp_efuse_custom_table.csv). -Virtual mode +Virtual Mode ^^^^^^^^^^^^ This mode is enabled with the ``--virt`` flag (need to specify chip with ``--chip``). This helps to test commands without physical access to the chip. Burned data is not saved between commands. Using ``--path-efuse-file``, you can save the written data to a file. Delete the file to clear eFuses. @@ -94,7 +93,7 @@ This tool automatically adds encoding data to the burning data if it requires. E All coding schemes (except ``None``) require additional encoding data to be provided at write time. Due to the encoding data, such blocks cannot be overwritten again without breaking the block's coding scheme. Use the :ref:`perform-multiple-operations` feature or list multiple eFuses/keys. -Burning Efuse +Burning eFuse ------------- Burning occurs in order from BLOCK(max) to BLOCK0. This prevents read/write protection from being set before the data is set. After burning, the tool reads the written data back and compares the original data, and additionally checks the status of the coding scheme, if there are any errors, it re-burns the data again to correct it. @@ -106,22 +105,22 @@ Perform Multiple Operations In A Single Espefuse Run Some eFuse blocks have an encoding scheme (Reed-Solomon or 3/4) that requires encoded data, making these blocks only writable once. If you need to write multiple keys/eFuses to one block using different commands, you can use this feature - multiple commands. This feature burns given data once at the end of all commands. All commands supported by version v3.2 or later are supported to be chained together. -The example below shows how to use the two commands ``burn_key_digest`` and ``burn_key`` to write the Secure Boot key and Flash Encryption key into one BLOCK3 for the ``ESP32-C2`` chip. Using these commands individually will result in only one key being written correctly. +The example below shows how to use the two commands ``burn-key-digest`` and ``burn-key`` to write the Secure Boot key and Flash Encryption key into one BLOCK3 for the ``ESP32-C2`` chip. Using these commands individually will result in only one key being written correctly. .. code-block:: none - > espefuse.py -c esp32c2 \ - burn_key_digest secure_images/ecdsa256_secure_boot_signing_key_v2.pem \ - burn_key BLOCK_KEY0 images/efuse/128bit_key.bin XTS_AES_128_KEY_DERIVED_FROM_128_EFUSE_BITS + > espefuse -c esp32c2 \ + burn-key-digest secure_images/ecdsa256_secure_boot_signing_key_v2.pem \ + burn-key BLOCK_KEY0 images/efuse/128bit_key.bin XTS_AES_128_KEY_DERIVED_FROM_128_EFUSE_BITS -Extend Efuse Table +Extend eFuse Table ------------------ This tool supports the use of `CSV files `_ from the `ESP-IDF `_ (e.g., ``esp_efuse_custom_table.csv``) to add custom eFuse fields. You can use this argument with any supported commands to access these custom eFuses. .. code-block:: none - > espefuse.py -c esp32 --extend-efuse-table path/esp_efuse_custom_table.csv summary + > espefuse -c esp32 --extend-efuse-table path/esp_efuse_custom_table.csv summary Below is an example of an ``esp_efuse_custom_table.csv`` file. This example demonstrates how to define single eFuse fields, ``structured eFuse fields`` and ``non-sequential bit fields``: @@ -160,16 +159,27 @@ When you include this CSV file, the tool will generate a new section in the summ You can reference these fields using the names and aliases provided in the CSV file. For non-sequential bits, the names are modified slightly with the addition of _0 and _1 postfixes for every sub-field, to ensure safer handling. -For the current example, you can reference the custom fields with the following names: MODULE_VERSION, DEVICE_ROLE, SETTING_1, SETTING_2, ID_NUM_0, ID_NUM_1, ID_NUM_2, CUSTOM_SECURE_VERSION, ID_NUMK_0, ID_NUMK_1, MY_DATA, MY_DATA_FIELD1; and alises: SETTING_1_ALT_NAME, MY_ID_NUM_0, MY_ID_NUM_1, MY_ID_NUM_2, MY_ID_NUMK_0, MY_ID_NUMK_1. +For the current example, you can reference the custom fields with the following names: MODULE_VERSION, DEVICE_ROLE, SETTING_1, SETTING_2, ID_NUM_0, ID_NUM_1, ID_NUM_2, CUSTOM_SECURE_VERSION, ID_NUMK_0, ID_NUMK_1, MY_DATA, MY_DATA_FIELD1; and aliases: SETTING_1_ALT_NAME, MY_ID_NUM_0, MY_ID_NUM_1, MY_ID_NUM_2, MY_ID_NUMK_0, MY_ID_NUMK_1. For convenience, the espefuse summary command includes the used bit range of the field in a comment, such as ``(150-157)`` len = 8 bits. For more details on the structure and usage of the CSV file, refer to the `eFuse Manager `_ chapter in the ESP-IDF documentation. +Scripting +--------- + +Espefuse can be used as a Python library. See :ref:`espefuse Scripting ` for more details. + +.. toctree:: + :maxdepth: 1 + :hidden: + + scripting + Recommendations --------------- -1. The `Technical Reference Manual `__ has a recommendation for reducing the number of burn operations as much as possible. The tool supports several ways to do this: +1. The `{IDF_TARGET_NAME} Technical Reference Manual <{IDF_TARGET_TRM_EN_URL}>`__ has a recommendation for reducing the number of burn operations as much as possible. The tool supports several ways to do this: - Combine multiple commands into one with this :ref:`perform-multiple-operations` feature. - Most commands support getting a list of arguments (eFuse names, keys). diff --git a/tools/esptool_py/docs/en/espefuse/read-write-protections-cmd.rst b/tools/esptool_py/docs/en/espefuse/read-write-protections-cmd.rst index e1611e538b..fbfbf6b640 100644 --- a/tools/esptool_py/docs/en/espefuse/read-write-protections-cmd.rst +++ b/tools/esptool_py/docs/en/espefuse/read-write-protections-cmd.rst @@ -5,8 +5,8 @@ Read Write Protection There are two commands (to get the correct list of eFuse fields that can be protected, specify the chip with ``--chip``): -- ``espefuse.py read_protect_efuse``. It sets read protection for given eFuse names. -- ``espefuse.py write_protect_efuse``. It sets write protection for given eFuse names. +- ``espefuse read-protect-efuse``. It sets read protection for given eFuse names. +- ``espefuse write-protect-efuse``. It sets write protection for given eFuse names. Positional arguments: @@ -40,9 +40,9 @@ Usage .. code-block:: none - > espefuse.py read_protect_efuse BLOCK2 BLOCK3 MAC_VERSION + > espefuse read-protect-efuse BLOCK2 BLOCK3 MAC_VERSION - === Run "read_protect_efuse" command === + === Run "read-protect-efuse" command === If Secure Boot V2 is used, BLOCK2 must be readable, please stop this operation! Permanently read-disabling efuse BLOCK2 Permanently read-disabling efuses MAC_VERSION, BLOCK3 @@ -62,9 +62,9 @@ Usage .. code-block:: none - > espefuse.py write_protect_efuse WR_DIS FLASH_CRYPT_CNT + > espefuse write-protect-efuse WR_DIS FLASH_CRYPT_CNT - === Run "write_protect_efuse" command === + === Run "write-protect-efuse" command === Permanently write-disabling efuse WR_DIS Permanently write-disabling efuses FLASH_CRYPT_CNT, UART_DOWNLOAD_DIS diff --git a/tools/esptool_py/docs/en/espefuse/scripting.rst b/tools/esptool_py/docs/en/espefuse/scripting.rst new file mode 100644 index 0000000000..261ebf8061 --- /dev/null +++ b/tools/esptool_py/docs/en/espefuse/scripting.rst @@ -0,0 +1,231 @@ +.. _espefuse-scripting: + +Embedding into Custom Scripts +============================= + +Similar to :ref:`esptool `, ``espefuse`` can be easily integrated into Python applications or called from other Python scripts. + +For details on redirecting the output, see :ref:`esptool logging section `. + +Using Espefuse as a Python Module +--------------------------------- + +The espefuse module provides a comprehensive Python API for interacting with ESP32 chips programmatically. By leveraging the API, developers can automate tasks such as reading and writing eFuse values, managing secure boot, and more. + +The API also provides the benefit of being able to chain commands with ``esptool`` commands and create a custom script. With this approach, you can e.g. flash firmware and set eFuse values in one go. + +Using the Command-Line Interface +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The most straightforward and basic integration option is to pass arguments to ``espefuse.main()``. This workaround allows you to pass exactly the same arguments as you would on the CLI: + +.. code-block:: python + + import espefuse + + command = ["--port", "/dev/ttyACM0", "summary"] + print("Using command ", " ".join(command)) + espefuse.main(command) + + +Public API Reference +^^^^^^^^^^^^^^^^^^^^ + +Basic Workflow: + +1. **Detect and Connect**: Connect to the chip and load the available eFuse commands for the given chip. +2. **Execute Commands**: Execute the commands you need, e.g. read the current eFuse values. +3. **Reset and Cleanup**: Reset the chip if needed. Context manager will take care of closing the port. + +This example demonstrates a basic workflow using the espefuse API to read the current eFuse values: + +.. code-block:: python + + from espefuse import init_commands + + PORT = "/dev/ttyACM0" + + # Autodetect and connect to the chip and load the eFuse commands for the given chip + with init_commands(port=PORT) as espefuse: + espefuse.summary() # Print the current eFuse values + + # Get the value of single eFuse + custom_mac = espefuse.efuses["CUSTOM_MAC"].get() + print(f"CUSTOM_MAC: {custom_mac}") + +.. note:: + + It is also possible to operate in virtual mode, which allows to read and write eFuse values without connecting to the chip for testing purposes. + For more information, refer to :func:`init_commands ` docstring or take a look at tests in :file:`test/test_espefuse.py`. + +------------ + +This API can be also used to chain commands with esptool commands. + +.. code-block:: python + + from espefuse import init_commands + from esptool import attach_flash, flash_id, reset_chip + + PORT = "/dev/ttyACM0" + + with init_commands(port=PORT) as espefuse: + espefuse.summary() # Get the current eFuse values + # Esptool commands + attach_flash(espefuse.esp) # Attach the flash memory chip, required for flash operations + flash_id(espefuse.esp) # Get the flash information + reset_chip(espefuse.esp, "hard-reset") # Reset the chip + +------------ + +If you would like to have a better control over the ESP object from esptool, you can first get the ESP object from esptool as described in :ref:`esptool ` and then pass it to the espefuse API. + +.. code-block:: python + + from espefuse import init_commands + from esptool import detect_chip, run_stub, attach_flash, flash_id, reset_chip + + PORT = "/dev/ttyACM0" + + # Get the ESP object from esptool + with detect_chip(PORT) as esp: + # Prepare the ESP object; run stub and attach flash + esp = run_stub(esp) + attach_flash(esp) + + # Pass the ESP object to the espefuse API + with init_commands(esp=esp) as espefuse: + espefuse.summary() # Get the current eFuse values + # External ESP object was passed, so port won't be closed here + + # Here you can continue with esptool commands if needed + flash_id(esp) # Get the flash information + + reset_chip(esp, "hard-reset") # Reset the chip + +------------ + +Batch Mode +^^^^^^^^^^ + +For burning eFuses, it is possible to use batch mode. This allows to queue multiple eFuse operations and execute them all at once. +Please note that nesting batch mode is also supported. + +Batch mode is enabled by passing the ``batch_mode=True`` argument to the function :func:`init_commands `. +Or can be enabled later by calling the :func:`use_batch_mode ` method. + +The :func:`burn_all ` method will execute all queued eFuse operations and decrement the batch mode counter. + +Here is an example of how to use a batch mode on ESP32: + +.. code-block:: python + + from espefuse import init_commands + + PORT = "/dev/ttyACM0" + + # Connect to chip and enable batch mode + with init_commands(port=PORT, batch_mode=True) as espefuse: + # Queue multiple eFuse operations + with open("flash_encryption_key.bin", "rb") as f: + espefuse.burn_key(["flash_encryption"], [f], no_protect_key=True) + espefuse.burn_efuse({"FLASH_CRYPT_CNT": 0x7}) + espefuse.burn_efuse({"DISABLE_DL_ENCRYPT": 1}) + espefuse.burn_efuse({"JTAG_DISABLE": 1}) + + # Execute all queued eFuse operations + espefuse.burn_all() + + # Check that all eFuses are set properly + espefuse.summary() + + # Checks written eFuses + if espefuse.efuses["FLASH_CRYPT_CNT"].get() != 0x7: + raise esptool.FatalError("FLASH_CRYPT_CNT was not set") + if espefuse.efuses["DISABLE_DL_ENCRYPT"].get() != 1: + raise esptool.FatalError("DISABLE_DL_ENCRYPT was not set") + if espefuse.efuses["JTAG_DISABLE"].is_readable() or espefuse.efuses["JTAG_DISABLE"].is_writeable(): + raise esptool.FatalError("JTAG_DISABLE should be read and write protected") + +.. note:: + + Please note that provided example is written for ESP32. For other chips, the names of eFuses might be different and signature of the :func:`burn_key ` function might also be different. + +After ``espefuse.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 checks will be performed on the written eFuses by ``espefuse``. In production, you might need to check that all written eFuses are set properly. +In the example above, we check that ``FLASH_CRYPT_CNT`` and ``DISABLE_DL_ENCRYPT`` are set properly. Also, we check that ``JTAG_DISABLE`` is read and write protected. + +------------ + +**The following section provides a detailed reference for the public API functions.** + +Init Commands +^^^^^^^^^^^^^ + +.. autofunction:: espefuse.init_commands + +.. autofunction:: espefuse.get_esp + +------------ + +Batch Mode Helpers +^^^^^^^^^^^^^^^^^^ + +.. autofunction:: espefuse.BaseCommands.use_batch_mode + +.. autofunction:: espefuse.BaseCommands.burn_all + +------------ + +Common Read Commands +^^^^^^^^^^^^^^^^^^^^ + +.. autofunction:: espefuse.BaseCommands.summary + +.. autofunction:: espefuse.BaseCommands.dump + +.. autofunction:: espefuse.BaseCommands.get_custom_mac + +.. autofunction:: espefuse.BaseCommands.adc_info + +.. autofunction:: espefuse.BaseCommands.check_error + +------------ + +Common Write Commands +^^^^^^^^^^^^^^^^^^^^^ + +.. autofunction:: espefuse.BaseCommands.burn_efuse + +.. autofunction:: espefuse.BaseCommands.read_protect_efuse + +.. autofunction:: espefuse.BaseCommands.write_protect_efuse + +.. autofunction:: espefuse.BaseCommands.burn_block_data + +.. autofunction:: espefuse.BaseCommands.burn_bit + +.. autofunction:: espefuse.BaseCommands.burn_custom_mac + +.. autofunction:: espefuse.BaseCommands.set_flash_voltage + +------------ + +Chip-Specific Commands +^^^^^^^^^^^^^^^^^^^^^^ + +.. autofunction:: espefuse.efuse.{IDF_TARGET_PATH_NAME}.commands.burn_key + +.. autofunction:: espefuse.efuse.{IDF_TARGET_PATH_NAME}.commands.burn_key_digest + + +eFuse Operations +^^^^^^^^^^^^^^^^ + +.. autofunction:: espefuse.efuse.base_fields.EfuseFieldBase.get + +.. autofunction:: espefuse.efuse.base_fields.EfuseFieldBase.is_readable + +.. autofunction:: espefuse.efuse.base_fields.EfuseFieldBase.is_writeable + +.. autofunction:: espefuse.efuse.base_fields.EfuseFieldBase.get_meaning diff --git a/tools/esptool_py/docs/en/espefuse/set-flash-voltage-cmd.rst b/tools/esptool_py/docs/en/espefuse/set-flash-voltage-cmd.rst index 6340858cae..f3202da4f2 100644 --- a/tools/esptool_py/docs/en/espefuse/set-flash-voltage-cmd.rst +++ b/tools/esptool_py/docs/en/espefuse/set-flash-voltage-cmd.rst @@ -9,7 +9,7 @@ Set Flash Voltage {IDF_TARGET_VDD_REG:default="VDD_SPI_XPD",esp32="XPD_SDIO_REG"} {IDF_TARGET_VDD_GPIO:default="GPIO45",esp32="GPIO12"} -The ``espefuse.py set_flash_voltage`` command permanently sets the internal flash voltage regulator to either 1.8V, 3.3V or OFF. This means a GPIO can be high or low at reset without changing the flash voltage. +The ``espefuse set-flash-voltage`` command permanently sets the internal flash voltage regulator to either 1.8V, 3.3V or OFF. This means a GPIO can be high or low at reset without changing the flash voltage. Positional arguments: @@ -19,14 +19,14 @@ Positional arguments: .. note:: - This command is not supported. The tool prints the error ``set_flash_voltage not supported!``. + This command is not supported. The tool prints the error ``set-flash-voltage not supported!``. Setting Flash Voltage ({IDF_TARGET_VDD_SPI}) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -After reset, the default {IDF_TARGET_NAME} behaviour is to enable and configure the flash voltage regulator ({IDF_TARGET_VDD_SPI}) based on the level of the MTDI pin ({IDF_TARGET_VDD_GPIO}). +After reset, the default {IDF_TARGET_NAME} behavior is to enable and configure the flash voltage regulator ({IDF_TARGET_VDD_SPI}) based on the level of the MTDI pin ({IDF_TARGET_VDD_GPIO}). -The default behaviour on reset is: +The default behavior on reset is: +--------------------+--------------------+ | MTDI | Internal Regulator | @@ -44,28 +44,28 @@ The default behaviour on reset is: Consult {IDF_TARGET_NAME} Technical Reference Manual for details. -A combination of 3 efuses (``{IDF_TARGET_VDD_FORCE}``, ``{IDF_TARGET_VDD_REG}``, ``{IDF_TARGET_VDD_TIEH}``) can be burned in order to override this behaviour and disable {IDF_TARGET_VDD_SPI} regulator, or set it to a fixed voltage. These efuses can be burned with individual ``burn_efuse`` commands, but the ``set_flash_voltage`` command makes it easier: +A combination of 3 eFuses (``{IDF_TARGET_VDD_FORCE}``, ``{IDF_TARGET_VDD_REG}``, ``{IDF_TARGET_VDD_TIEH}``) can be burned in order to override this behavior and disable {IDF_TARGET_VDD_SPI} regulator, or set it to a fixed voltage. These eFuses can be burned with individual ``burn-efuse`` commands, but the ``set-flash-voltage`` command makes it easier: Disable {IDF_TARGET_VDD_SPI} Regulator ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ .. code-block:: none - espefuse.py set_flash_voltage OFF + espefuse set-flash-voltage OFF Once set: * {IDF_TARGET_VDD_SPI} regulator always disabled. * MTDI pin ({IDF_TARGET_VDD_GPIO}) is ignored. * Flash must be powered externally and voltage supplied to {IDF_TARGET_VDD_SPI} pin of {IDF_TARGET_NAME}. -* Efuse ``{IDF_TARGET_VDD_FORCE}`` is burned. +* eFuse ``{IDF_TARGET_VDD_FORCE}`` is burned. Fixed 1.8V {IDF_TARGET_VDD_SPI} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ .. code-block:: none - espefuse.py set_flash_voltage 1.8V + espefuse set-flash-voltage 1.8V Once set: @@ -79,7 +79,7 @@ Fixed 3.3V {IDF_TARGET_VDD_SPI} .. code-block:: none - espefuse.py set_flash_voltage 3.3V + espefuse set-flash-voltage 3.3V Once set: @@ -91,19 +91,19 @@ Once set: Subsequent Changes ^^^^^^^^^^^^^^^^^^ -Once an efuse is burned it cannot be un-burned. However, changes can be made by burning additional efuses: +Once an eFuse is burned it cannot be un-burned. However, changes can be made by burning additional eFuses: -* ``set_flash_voltage OFF`` can be changed to ``1.8V`` or ``3.3V`` -* ``set_flash_voltage 1.8V`` can be changed to ``3.3V`` +* ``set-flash-voltage OFF`` can be changed to ``1.8V`` or ``3.3V`` +* ``set-flash-voltage 1.8V`` can be changed to ``3.3V`` .. only:: esp32s2 or esp32s3 .. code-block:: none - > espefuse.py set_flash_voltage 1.8V + > espefuse set-flash-voltage 1.8V - === Run "set_flash_voltage" command === + === Run "set-flash-voltage" command === Set internal flash voltage regulator (VDD_SPI) to 1.8V. VDD_SPI setting complete. @@ -122,9 +122,9 @@ Once an efuse is burned it cannot be un-burned. However, changes can be made by .. code-block:: none - > espefuse.py set_flash_voltage 3.3V + > espefuse set-flash-voltage 3.3V - === Run "set_flash_voltage" command === + === Run "set-flash-voltage" command === Enable internal flash voltage regulator (VDD_SPI) to 3.3V. VDD_SPI setting complete. @@ -143,9 +143,9 @@ Once an efuse is burned it cannot be un-burned. However, changes can be made by .. code-block:: none - > espefuse.py set_flash_voltage OFF + > espefuse set-flash-voltage OFF - === Run "set_flash_voltage" command === + === Run "set-flash-voltage" command === Disable internal flash voltage regulator (VDD_SPI). SPI flash will VDD_SPI setting complete. diff --git a/tools/esptool_py/docs/en/espefuse/summary-cmd.rst b/tools/esptool_py/docs/en/espefuse/summary-cmd.rst index 195083ebdd..f92c00e93d 100644 --- a/tools/esptool_py/docs/en/espefuse/summary-cmd.rst +++ b/tools/esptool_py/docs/en/espefuse/summary-cmd.rst @@ -3,7 +3,7 @@ Summary ======= -The ``espefuse.py summary`` command reads the eFuses from the chip and outputs them in text or json format. It is also possible to save it to a file. The command also supports eFuse filtering by name. +The ``espefuse summary`` command reads the eFuses from the chip and outputs them in text or json format. It is also possible to save it to a file. The command also supports eFuse filtering by name. Optional arguments: @@ -11,7 +11,7 @@ Optional arguments: - ``summary`` - text format (default option). - ``json`` - json format. Usage ``--format json``. - ``value_only`` - only the value of the eFuse specified as an argument will be displayed. For more information, refer to the :ref:`Filtering eFuses ` section. -- ``--file`` - File to save the efuse summary. Usage ``--file efuses.json``. +- ``--file`` - File to save the eFuse summary. Usage ``--file efuses.json``. - List of eFuses to filter. For more information, refer to the :ref:`Filtering eFuses ` section. Text Format Summary @@ -56,7 +56,7 @@ The json representation of eFuses for the ESP32 chip is shown below. .. code-block:: none - > espefuse.py summary --format json + > espefuse summary --format json { "ABS_DONE_0": { @@ -107,10 +107,8 @@ Save Json Format Summary To File .. code-block:: none - > espefuse.py summary --format json --file efuses.json + > espefuse summary --format json --file efuses.json - Connecting.......... - Detecting chip type... Unsupported detection protocol, switching and trying again... Connecting.... Detecting chip type... ESP32 @@ -122,11 +120,11 @@ Save Json Format Summary To File Filtering Efuses and Displaying Only the Value ---------------------------------------------- -The ``espefuse.py summary`` command supports filtering eFuses by name. The eFuses to filter needs to be specified as positional arguments. If no eFuses are specified, complete summary will be displayed. Example: +The ``espefuse summary`` command supports filtering eFuses by name. The eFuses to filter needs to be specified as positional arguments. If no eFuses are specified, complete summary will be displayed. Example: .. code-block:: none - > espefuse.py summary ABS_DONE_0 BLOCK1 + > espefuse summary ABS_DONE_0 BLOCK1 === Run "summary" command === EFUSE_NAME (Block) Description = [Meaningful Value] [Readable/Writeable] (Hex Value) @@ -140,7 +138,7 @@ If ``--format value_only`` is specified, only the value of the eFuse specified a .. code-block:: none - > espefuse.py summary --format value_only MAC + > espefuse summary --format value_only MAC === Run "summary" command === 00:00:00:00:00:00 (CRC 0x00 OK) diff --git a/tools/esptool_py/docs/en/espsecure/index.rst b/tools/esptool_py/docs/en/espsecure/index.rst index c03b3478e1..6bd2a1604b 100644 --- a/tools/esptool_py/docs/en/espsecure/index.rst +++ b/tools/esptool_py/docs/en/espsecure/index.rst @@ -1,9 +1,9 @@ .. _espsecure: -espsecure.py -============ +espsecure +========= -``espsecure.py`` is a tool for manipulating data that relates to the secure boot and flash encryption features of ESP32 and later Espressif chips. +``espsecure`` is a tool for manipulating data that relates to the secure boot and flash encryption features of ESP32 and later Espressif chips. For more details, see the ESP-IDF documentation which explains this tool and how to use it to enable the relevant features: @@ -12,22 +12,22 @@ For more details, see the ESP-IDF documentation which explains this tool and how .. _hsm_signing: -Remote Signing using an external HSM +Remote Signing Using an External HSM ------------------------------------ An external Hardware Security Module (HSM) can be used for remote signing of images in secure boot v2 scheme. -You must install ``esptool.py`` package with the ``hsm`` extra using the command ``pip install 'esptool[hsm]'`` to use this feature. ``esp_hsm_sign`` provides a PKCS #11 interface to communicate with the external HSM and is integrated in ``espsecure.py``. +You must install ``esptool`` package with the ``hsm`` extra using the command ``pip install 'esptool[hsm]'`` to use this feature. ``esp_hsm_sign`` provides a PKCS #11 interface to communicate with the external HSM and is integrated in ``espsecure``. The following command should be used to get an image signed using an external HSM. :: - python espsecure.py sign_data --version 2 --hsm --hsm-config --output + espsecure sign-data --version 2 --hsm --hsm-config --output The above command first extracts the public key from the HSM, generates a signature for an image using the HSM, and then creates a signature block and appends it to the image to generate a signed image. If the public key is not stored in the external HSM, you can specify the ``--pub-key`` argument to supply the public key. :: - python espsecure.py sign_data --version 2 --hsm --hsm-config --pub-key --output + espsecure sign-data --version 2 --hsm --hsm-config --pub-key --output .. note:: In case you are using ESP-IDF, then an unsigned application can be generated by disabling ``CONFIG_SECURE_BOOT_BUILD_SIGNED_BINARIES`` configuration option in the project settings. @@ -37,14 +37,14 @@ Verifying the Signed Image Once the signed image is generated, we can verify it using the following command: :: - python espsecure.py verify_signature --version 2 --hsm --hsm-config + espsecure verify-signature --version 2 --hsm --hsm-config If the public key is not stored in the external HSM, you can specify the ``--keyfile`` argument to supply the public key. :: - python espsecure.py verify_signature --version 2 --keyfile + espsecure verify-signature --version 2 --keyfile -HSM config file +HSM Config File ~~~~~~~~~~~~~~~ An HSM config file is required with the fields (``pkcs11_lib``, ``credentials``, ``slot``, ``label``, ``label_pubkey``) diff --git a/tools/esptool_py/docs/en/esptool/advanced-commands.rst b/tools/esptool_py/docs/en/esptool/advanced-commands.rst index 905739ba27..5a035a4091 100644 --- a/tools/esptool_py/docs/en/esptool/advanced-commands.rst +++ b/tools/esptool_py/docs/en/esptool/advanced-commands.rst @@ -1,90 +1,91 @@ -{IDF_TARGET_BOOTLOADER_OFFSET:default="0x0", esp32="0x1000", esp32s2="0x1000", esp32p4="0x2000"} +{IDF_TARGET_BOOTLOADER_OFFSET:default="0x0", esp32="0x1000", esp32s2="0x1000", esp32p4="0x2000", esp32c5="0x2000"} .. _advanced-commands: Advanced Commands ================= -The ``write_flash``, ``read_flash``, ``erase_flash``, ``erase_region``, ``read_mac``, ``flash_id``, ``elf2image``, ``image_info`` and ``merge_bin`` commands are all documented in the :ref:`commands` section. +The ``write-flash``, ``read-flash``, ``erase-flash``, ``erase-region``, ``read-mac``, ``flash-id``, ``elf2image``, ``image-info`` and ``merge-bin`` commands are all documented in the :ref:`commands` section. The following less common commands are for more advanced users. .. _verify-flash: -Verify Flash Data: verify_flash -------------------------------- +Verify Flash Data: ``verify-flash`` +----------------------------------- -The ``verify_flash`` command allows you to verify that data in flash matches a local file. +The ``verify-flash`` command allows you to verify that data in flash matches a local file. -The ``write_flash`` command always verifies the MD5 hash of data which is written to flash, so additional verification is not usually needed. However, if you wish to perform a byte-by-byte verification of the flash contents (and optionally print the differences to the console) then you can do so with this command: +The ``write-flash`` command always verifies the MD5 hash of data which is written to flash, so additional verification is not usually needed. However, if you wish to perform a byte-by-byte verification of the flash contents (and optionally print the differences to the console) then you can do so with this command: :: - esptool.py verify_flash --diff yes 0x40000 my_app.elf-0x40000.bin + esptool verify-flash --diff 0x40000 my_app.elf-0x40000.bin -The ``--diff yes`` option specifies that if the files are different, the details should be printed to the console. +The ``--diff`` option specifies that if the files are different, the details should be printed to the console. .. note:: .. list:: - * If verifying a default boot image (offset {IDF_TARGET_BOOTLOADER_OFFSET} for {IDF_TARGET_NAME}) then any ``--flash_mode``, ``--flash_size`` and ``--flash_freq`` arguments which were passed to `write_flash` must also be passed to ``verify_flash``. Otherwise, ``verify_flash`` will detect mismatches in the header of the image file. - * Another way to compare flash contents is to use the ``read_flash`` command, and then use binary diffing tools on the host. + * If verifying a default boot image (offset {IDF_TARGET_BOOTLOADER_OFFSET} for {IDF_TARGET_NAME}) then any ``--flash-mode``, ``--flash-size`` and ``--flash-freq`` arguments which were passed to `write-flash` must also be passed to ``verify-flash``. Otherwise, ``verify-flash`` will detect mismatches in the header of the image file. + * Another way to compare flash contents is to use the ``read-flash`` command, and then use binary diffing tools on the host. .. _dump-mem: -Dump a Memory Region to File: dump_mem --------------------------------------- +Dump a Memory Region to File: ``dump-mem`` +------------------------------------------ -The ``dump_mem`` command will dump a region from the chip's memory space to a file. For example, to dump the ROM (64 kB) from an ESP8266: +The ``dump-mem`` command will dump a region from the chip's memory space to a file. For example, to dump the ROM (64 kB) from an ESP8266: :: - esptool.py dump_mem 0x40000000 65536 iram0.bin + esptool dump-mem 0x40000000 64k iram0.bin + .. _load-ram: -Load a Binary to RAM: load_ram ------------------------------- +Load a Binary to RAM: ``load-ram`` +---------------------------------- -The ``load_ram`` command allows the loading of an executable binary image (created with the ``elf2image`` or ``make_image`` commands) directly into RAM, and then immediately executes the program contained within it. Command also supports ``.hex`` file created by ``merge_bin`` command from supported ``.bin`` files. +The ``load-ram`` command allows the loading of an executable binary image (created with the ``elf2image`` or ``make-image`` commands) directly into RAM, and then immediately executes the program contained within it. Command also supports ``.hex`` file created by ``merge-bin`` command from supported ``.bin`` files. :: - esptool.py --no-stub load_ram ./test/images/helloworld-esp8266.bin + esptool --no-stub load-ram ./test/images/helloworld-esp8266.bin .. note:: - * The binary image must only contain IRAM- and DRAM-resident segments. Any SPI flash mapped segments will not load correctly and the image will probably crash. The ``image_info`` command can be used to check the binary image contents. - * Because the software loader is resident in IRAM and DRAM, this limits the region where a new program may be loaded. An error will be printed if the new program overlaps with the software loader in RAM. Older esptool versions may hang. Pass ``esptool.py --no-stub`` to avoid this problem. + * The binary image must only contain IRAM- and DRAM-resident segments. Any SPI flash mapped segments will not load correctly and the image will probably crash. The ``image-info`` command can be used to check the binary image contents. + * Because the software loader is resident in IRAM and DRAM, this limits the region where a new program may be loaded. An error will be printed if the new program overlaps with the software loader in RAM. Older esptool versions may hang. Pass ``esptool --no-stub`` to avoid this problem. * Due to a limitation in the ROM loader, when using ``--no-stub`` any very early serial output from a program may be lost if the program resets or reconfigures the UART. To avoid this problem, a program can be compiled with ``ets_delay_us(1)`` as the very first statement after the entry point. .. _read-mem-write-mem: -Read or Write RAM: read_mem / write_mem ---------------------------------------- +Read or Write RAM: ``read-mem`` & ``write-mem`` +----------------------------------------------- -The ``read_mem`` & ``write_mem`` commands allow reading and writing single words (4 bytes) of RAM. This can be used to "peek" and "poke" at registers. +The ``read-mem`` & ``write-mem`` commands allow reading and writing single words (4 bytes) of RAM. This can be used to "peek" and "poke" at registers. :: - esptool.py write_mem 0x400C0000 0xabad1dea + esptool write-mem 0x400C0000 0xabad1dea :: - esptool.py read_mem 0x400C0000 + esptool read-mem 0x400C0000 .. _read-flash-status: -Read Flash Chip Registers: read_flash_status --------------------------------------------- +Read Flash Chip Registers: ``read-flash-status`` +------------------------------------------------ This command is intended for use when debugging hardware flash chip-related problems. It allows sending a ``RDSR``, ``RDSR2`` and/or ``RDSR3`` commands to the flash chip to read the status register contents. This can be used to check write protection status, for example: :: - esptool.py read_flash_status --bytes 2 + esptool read-flash-status --bytes 2 The ``--bytes`` argument determines how many status register bytes are read. @@ -98,16 +99,16 @@ The ``--bytes`` argument determines how many status register bytes are read. .. _write-flash-status: -Write Flash Chip Registers: write_flash_status ------------------------------------------------ +Write Flash Chip Registers: ``write-flash-status`` +-------------------------------------------------- This command is intended for use when debugging hardware flash chip-related problems. It allows sending ``WRSR``, ``WRSR2`` and/or ``WRSR3`` commands to the flash chip to write the status register contents. This can be used to clear write protection bits, for example: :: - esptool.py write_flash_status --bytes 2 --non-volatile 0 + esptool write-flash-status --bytes 2 --non-volatile 0 -The ``--bytes`` option is similar to the corresponding option for ``read_flash_status`` and causes a mix of ``WRSR`` (01h), ``WRSR2`` (31h), and ``WRSR3`` (11h) commands to be sent to the chip. If ``--bytes 2`` is used then ``WRSR`` is sent first with a 16-bit argument and then with an 8-bit argument, as different flash chips use this command differently. +The ``--bytes`` option is similar to the corresponding option for ``read-flash-status`` and causes a mix of ``WRSR`` (01h), ``WRSR2`` (31h), and ``WRSR3`` (11h) commands to be sent to the chip. If ``--bytes 2`` is used then ``WRSR`` is sent first with a 16-bit argument and then with an 8-bit argument, as different flash chips use this command differently. Otherwise, each command is accompanied by 8-bits of the new status register value. A second option ``--non-volatile`` can be used in order to send a ``WREN`` (06h) command before writing the status. This may allow non-volatile status register bits to be set or cleared. If the ``--non-volatile`` option is not supplied, a ``WEVSR`` (50h) command is sent instead of ``WREN``. @@ -122,51 +123,47 @@ A second option ``--non-volatile`` can be used in order to send a ``WREN`` (06h) .. _read-flash-sfdp: -Read Serial Flash Discoverable Parameters (SFDP) ------------------------------------------------- +Read Serial Flash Discoverable Parameters (SFDP): ``read-flash-sfdp`` +--------------------------------------------------------------------- The Serial Flash Discoverable Parameters (SFDP) store essential vendor-specific configuration data of the flash memory chip. These parameters help identify and interact with different flash devices. Usage: :: - esptool.py read_flash_sfdp 16 4 - -This will read 4 bytes from SFDP address 16. -.. only:: esp8266 + esptool read-flash-sfdp 16 4 - .. _chip-id: +This will read 4 bytes from SFDP address 16. - Read the Chip ID: chip_id - ------------------------- +.. only:: not esp8266 and not esp32 - The ``chip_id`` command allows you to read a 4 byte ID which forms part of the MAC address. It is usually better to use ``read_mac`` to identify a chip. + Read Security Info: ``get_security_info`` + ------------------------------------------ - On {IDF_TARGET_NAME}, output is the same as the ``system_get_chip_id()`` SDK function. The chip ID is four bytes long, the lower three bytes are the final bytes of the MAC address. The upper byte is zero. + The ``get_security_info`` command allows you to read security-related information (secure boot, secure download, etc.) about the Espressif devices. :: - esptool.py chip_id + esptool get_security_info - .. _make-image: - Assemble a Firmware Image: make_image - ------------------------------------- +.. only:: esp8266 - ``make_image`` allows you to manually assemble a firmware image from binary segments (such as those extracted from objcopy). For example: + .. _chip-id: - :: + Read the Chip ID: ``chip-id`` + ----------------------------- - esptool.py --chip esp8266 make_image -f app.text.bin -a 0x40100000 -f app.data.bin -a 0x3ffe8000 -f app.rodata.bin -a 0x3ffe8c00 app.flash.bin + The ``chip-id`` command allows you to read a 4 byte ID which forms part of the MAC address. It is usually better to use ``read-mac`` to identify a chip. - This command does not require a serial connection. + On {IDF_TARGET_NAME}, output is the same as the ``system_get_chip_id()`` SDK function. The chip ID is four bytes long, the lower three bytes are the final bytes of the MAC address. The upper byte is zero. - .. note:: + :: - In general, it is better to create an ELF image (including any binary data as part of the ELF, by using objcopy or other tools) and then use ``elf2image`` to generate the ``.bin`` file. + esptool chip-id .. _run: - Boot Application Code: run - -------------------------- + Boot Application Code: ``run`` + ------------------------------ The ``run`` command immediately exits the bootloader and attempts to boot the normal application code. diff --git a/tools/esptool_py/docs/en/esptool/advanced-options.rst b/tools/esptool_py/docs/en/esptool/advanced-options.rst index db47b8aa04..c0d1a81fc0 100644 --- a/tools/esptool_py/docs/en/esptool/advanced-options.rst +++ b/tools/esptool_py/docs/en/esptool/advanced-options.rst @@ -3,38 +3,39 @@ Advanced Options ================ -The following advanced configuration options can be used for all esptool commands (they are placed before the command name on the command line). +The following advanced global configuration options can be used for all esptool commands. They are placed before the command name on the command line. For example, the option ``--before no-reset`` has to be placed before ``flash-id``. The command should look like this: ``esptool --before no-reset flash-id``. -For basic/fundamental configuration options, see the :ref:`options` page. +For basic/fundamental global configuration options, see the :ref:`options` page. Reset Modes ----------- By default, esptool tries to hard reset the chip into bootloader mode before it starts and hard resets the chip to run the normal program once it is complete. The ``--before`` and ``--after`` options allow this behavior to be changed: -Reset Before Operation -^^^^^^^^^^^^^^^^^^^^^^ +Reset Before Operation: ``--before`` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ The ``--before`` argument allows you to specify whether the chip needs resetting into bootloader mode before esptool talks to it. .. list:: - * ``--before default_reset`` is the default, which uses DTR & RTS serial control lines (see :ref:`entering-the-bootloader`) to try to reset the chip into bootloader mode. - * ``--before no_reset`` will skip DTR/RTS control signal assignments and just start sending a serial synchronisation command to the chip. This is useful if your chip doesn't have DTR/RTS, or for some serial interfaces (like Arduino board onboard serial) which behave differently when DTR/RTS are toggled. - * ``--before no_reset_no_sync`` will skip DTR/RTS control signal assignments and skip also the serial synchronization command. This is useful if your chip is already running the :ref:`stub bootloader ` and you want to avoid resetting the chip and uploading the stub again. - :esp32c3 or esp32s3 or esp32c6 or esp32h2 or esp32p4: * ``--before usb_reset`` will use custom reset sequence for USB-JTAG-Serial (used for example for ESP chips connected through the USB-JTAG-Serial peripheral). Usually, this option doesn't have to be used directly. Esptool should be able to detect connection through USB-JTAG-Serial. + * ``--before default-reset`` is the default, which uses DTR & RTS serial control lines (see :ref:`entering-the-bootloader`) to try to reset the chip into bootloader mode. + * ``--before no-reset`` will skip DTR/RTS control signal assignments and just start sending a serial synchronisation command to the chip. This is useful if your chip doesn't have DTR/RTS, or for some serial interfaces (like Arduino board onboard serial) which behave differently when DTR/RTS are toggled. + * ``--before no-reset-no-sync`` will skip DTR/RTS control signal assignments and skip also the serial synchronization command. This is useful if your chip is already running the :ref:`stub bootloader ` and you want to avoid resetting the chip and uploading the stub again. + :esp32c3 or esp32s3 or esp32c6 or esp32h2 or esp32p4 or esp32c5 or esp32c61: * ``--before usb-reset`` will use custom reset sequence for USB-JTAG-Serial (used for example for ESP chips connected through the USB-JTAG-Serial peripheral). Usually, this option doesn't have to be used directly. Esptool should be able to detect connection through USB-JTAG-Serial. -Reset After Operation -^^^^^^^^^^^^^^^^^^^^^ +Reset After Operation: ``--after`` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ The ``--after`` argument allows you to specify whether the chip should be reset after the esptool operation completes: .. list:: - * ``--after hard_reset`` is the default. The DTR serial control line is used to reset the chip into a normal boot sequence. - :esp8266:* ``--after soft_reset`` This runs the user firmware, but any subsequent reset will return to the serial bootloader. This was the reset behaviour in esptool v1.x. - * ``--after no_reset`` leaves the chip in the serial bootloader, no reset is performed. - * ``--after no_reset_stub`` leaves the chip in the stub bootloader, no reset is performed. + * ``--after hard-reset`` is the default. The RTS serial control line is used to reset the chip into a normal boot sequence. + :esp8266: * ``--after soft-reset`` runs the user firmware, but any subsequent reset will return to the serial bootloader. This was the reset behaviour in esptool v1.x. + * ``--after no-reset`` leaves the chip in the serial bootloader, no reset is performed. + * ``--after no-reset-stub`` leaves the chip in the stub bootloader, no reset is performed. + :not esp8266 and not esp32 and not esp32h2 and not esp32c6: * ``--after watchdog-reset`` hard-resets the chip by triggering an internal watchdog reset. This is useful when the RTS control line is not available, especially in the USB-OTG and USB-Serial/JTAG modes. Use this if a chip is getting stuck in download mode when using the default reset method in USB-Serial/JTAG mode. Using this may cause the port to re-enumerate on Linux (e.g. ``/dev/ttyACM0`` -> ``/dev/ttyACM1``). Connect Loop @@ -59,8 +60,8 @@ There are 3 possible values for this option: .. _disable_stub: -Disabling the Stub Loader -------------------------- +Disabling the Stub Loader: ``--no-stub`` +---------------------------------------- The ``--no-stub`` option disables uploading of a software "stub loader" that manages flash operations, and only talks directly to the loader in ROM. @@ -68,19 +69,23 @@ Passing ``--no-stub`` will disable certain options, as not all options are imple .. only:: not esp8266 - Overriding SPI Flash Connections - -------------------------------- + Overriding SPI Flash Connections: ``--spi-connection`` + ------------------------------------------------------ - The optional ``--spi-connection`` argument overrides the SPI flash connection configuration on ESP32. This means that the SPI flash can be connected to other pins, or esptool can be used to communicate with a different SPI flash chip to the default. + The optional ``--spi-connection`` argument overrides the SPI flash connection configuration on {IDF_TARGET_NAME}. This means that the SPI flash can be connected to other pins, or esptool can be used to communicate with a different SPI flash chip to the default. - Supply the ``--spi-connection`` argument after the ``esptool.py`` command, ie ``esptool.py flash_id --spi-connection HSPI``. + Supply the ``--spi-connection`` argument after the ``esptool`` command, ie ``esptool flash-id --spi-connection HSPI``. + + .. note:: + + Only NOR flash chips that are capable of at least Dual I/O (DIO) mode for SPI communication are supported. SPI NAND flash chips, as well as other types of memory devices that do not meet this requirement, are not supported. Default Behavior ^^^^^^^^^^^^^^^^ - If the ``--spi-connection`` argument is not provided, the SPI flash is configured to use :ref:`pin numbers set in efuse `. These are the same SPI flash pins that are used during a normal boot. + If the ``--spi-connection`` argument is not provided, the SPI flash is configured to use :ref:`pin numbers set in eFuse `. These are the same SPI flash pins that are used during a normal boot. - The only exception to this is if the ``--no-stub`` option is also provided. In this case, efuse values are ignored and ``--spi-connection`` will default to ``--spi-connection SPI`` unless set to a different value. + The only exception to this is if the ``--no-stub`` option is also provided. In this case, eFuse values are ignored and ``--spi-connection`` will default to ``--spi-connection SPI`` unless set to a different value. .. only:: esp32 @@ -95,7 +100,7 @@ Passing ``--no-stub`` will disable certain options, as not all options are imple * HD = GPIO 9 * CS = GPIO 11 - During normal booting, this configuration is selected if all SPI pin efuses are unset and GPIO1 (U0TXD) is not pulled low (default). + During normal booting, this configuration is selected if all SPI pin eFuses are unset and GPIO1 (U0TXD) is not pulled low (default). This is the normal pin configuration for ESP32 chips that do not contain embedded flash. @@ -110,16 +115,16 @@ Passing ``--no-stub`` will disable certain options, as not all options are imple * HD = GPIO 4 * CS = GPIO 15 - During normal booting, this configuration is selected if all SPI pin efuses are unset and GPIO1 (U0TXD) is pulled low on reset. + During normal booting, this configuration is selected if all SPI pin eFuses are unset and GPIO1 (U0TXD) is pulled low on reset. Custom SPI Pin Configuration ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - ``--spi-connection ,,,,`` allows a custom list of pins to be configured for the SPI flash connection. This can be used to emulate the flash configuration equivalent to a particular set of SPI pin efuses being burned. The values supplied are GPIO numbers. + ``--spi-connection ,,,,`` allows a custom list of pins to be configured for the SPI flash connection. This can be used to emulate the flash configuration equivalent to a particular set of SPI pin eFuses being burned. The values supplied are GPIO numbers. .. only:: esp32 - For example, ``--spi-connection 6,17,8,11,16`` sets an identical configuration to the factory efuse configuration for ESP32s with embedded flash. + For example, ``--spi-connection 6,17,8,11,16`` sets an identical configuration to the factory eFuse configuration for ESP32s with embedded flash. When setting a custom pin configuration, the SPI peripheral (not HSPI) will be used unless the ``CLK`` pin value is set to 14 (HSPI CLK), in which case the HSPI peripheral will be used. @@ -133,17 +138,17 @@ Specifying Arguments via File Anywhere on the esptool command line, you can specify a file name as ``@filename.txt`` to read one or more arguments from text file ``filename.txt``. Arguments can be separated by newlines or spaces, quotes can be used to enclose arguments that span multiple words. Arguments read from the text file are expanded exactly as if they had appeared in that order on the esptool command line. -An example of this is available in the :ref:`merge_bin ` command description. +An example of this is available in the :ref:`merge-bin ` command description. -.. note:: PowerShell users +.. note:: - Because of `splatting `__ in PowerShell (method of passing a collection of parameter values to a command as a unit) there is a need to add quotes around @filename.txt ("@filename.txt") to be correctly resolved. + PowerShell users need to add quotes around @filename.txt ("@filename.txt") for it to be correctly resolved. This is because of `splatting `__, a method of passing a collection of parameter values to a command as a unit. -Filtering serial ports ----------------------- +Filtering Serial Ports: ``--port-filter`` +----------------------------------------- .. _filtering_serial_ports: -``--port-filter =`` allows limiting ports that will be tried. This can be useful when esptool is run on a system +``--port-filter =`` allows limiting ports that will be considered during chip autodetection. This can be useful when esptool is run on a system with many serial ports. There are a few different types that can be combined. A port must match all specified FilterTypes, and must match at least one FilterValue for each specified FilterType to be considered. Example filter configurations: @@ -154,5 +159,33 @@ at least one FilterValue for each specified FilterType to be considered. Example * ``--port-filter vid=0x303A --port-filter pid=0x0002`` matches Espressif ESP32-S2 in USB-OTG mode by VID and PID. * ``--port-filter vid=0x303A --port-filter pid=0x1001`` matches Espressif USB-Serial/JTAG unit used by multiple chips by VID and PID. * ``--port-filter name=ttyUSB`` matches ports where the port name contains the specified text. + * ``--port-filter serial=7c98d1065267ee11bcc4c8ab93cd958c`` matches ports where the serial number contains the specified text. See also the `Espressif USB customer-allocated PID repository `_ + +Output Verbosity +---------------- + +Output verbosity can be controlled using the ``--verbose`` and ``--silent`` flags. + +Verbose output: ``--verbose``, ``-v`` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +.. _verbose: + +The ``--verbose``, ``-v`` flag can be used to show all output without any overwriting or collapsing stages into a single line: + +.. code-block:: bash + + esptool --verbose flash-id + +See :ref:`the trace option ` if you want to dump all serial interactions to the standard output for debugging purposes. + +Silent output: ``--silent``, ``-s`` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +.. _silent: + +The ``--silent``, ``-s`` flag can be used to limit the output to errors only: + +.. code-block:: bash + + esptool -s write-flash 0x0 image.bin diff --git a/tools/esptool_py/docs/en/esptool/basic-commands.rst b/tools/esptool_py/docs/en/esptool/basic-commands.rst index 8e16df7b42..b13c718249 100644 --- a/tools/esptool_py/docs/en/esptool/basic-commands.rst +++ b/tools/esptool_py/docs/en/esptool/basic-commands.rst @@ -3,20 +3,22 @@ Basic Commands ============== -Write Binary Data to Flash: write_flash ----------------------------------------- +.. _write-flash: -Binary data can be written to the ESP's flash chip via the serial ``write_flash`` command: +Write Binary Data to Flash: ``write-flash`` +------------------------------------------- + +Binary data can be written to the ESP's flash chip via the serial ``write-flash`` command: :: - esptool.py --port COM4 write_flash 0x1000 my_app-0x01000.bin + esptool --port COM4 write-flash 0x1000 my_app-0x01000.bin Multiple flash addresses and file names can be given on the same command line: :: - esptool.py --port COM4 write_flash 0x00000 my_app.elf-0x00000.bin 0x40000 my_app.elf-0x40000.bin + esptool --port COM4 write-flash 0x00000 my_app.elf-0x00000.bin 0x40000 my_app.elf-0x40000.bin The ``--chip`` argument is optional when writing to flash, esptool will detect the type of chip when it connects to the serial port. @@ -24,16 +26,16 @@ The ``--port`` argument is documented under :ref:`serial-port`. .. only:: esp8266 - The next arguments to ``write_flash`` are one or more pairs of offset (address) and file name. When generating ESP8266 "version 1" images, the file names created by ``elf2image`` include the flash offsets as part of the file name. + The next arguments to ``write-flash`` are one or more pairs of offset (address) and file name. When generating ESP8266 "version 1" images, the file names created by ``elf2image`` include the flash offsets as part of the file name. For other types of images, consult your SDK documentation to determine the files to flash at which offsets. .. only:: not esp8266 - The next arguments to ``write_flash`` are one or more pairs of offset (address) and file name. Consult your SDK documentation to determine the files to flash at which offsets. + The next arguments to ``write-flash`` are one or more pairs of offset (address) and file name. Consult your SDK documentation to determine the files to flash at which offsets. -Numeric values passed to write_flash (and other commands) can be specified either in hex (ie 0x1000), or in decimal (ie 4096). +Numeric values passed to write-flash (and other commands) can be specified either in hex (ie 0x1000), or in decimal (ie 4096). -See the :ref:`troubleshooting` section if the ``write_flash`` command is failing, or the flashed module fails to boot. +See the :ref:`troubleshooting` section if the ``write-flash`` command is failing, or the flashed module fails to boot. Setting Flash Mode and Size ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -42,7 +44,7 @@ You may also need to specify arguments for :ref:`flash mode and flash size `_ is enabled and Encrypted Download being disabled (efuse bit ``EFUSE_DISABLE_DL_ENCRYPT`` is set). + Overwriting the encrypted firmware (bootloader, application, etc.) without the ``--encrypt`` option is disabled, if `Flash Encryption `_ is enabled and Encrypted Download being disabled (eFuse bit ``EFUSE_DISABLE_DL_ENCRYPT`` is set). .. only:: not esp32 Overwriting the encrypted firmware (bootloader, application, etc.) without the ``--encrypt`` option is disabled, if: * `Flash Encryption `_ and Secure Download Mode are enabled or - * `Flash Encryption `_ is enabled but Encrypted Download is disabled (efuse bit ``EFUSE_DIS_DOWNLOAD_MANUAL_ENCRYPT`` is set). + * `Flash Encryption `_ is enabled but Encrypted Download is disabled (eFuse bit ``EFUSE_DIS_DOWNLOAD_MANUAL_ENCRYPT`` is set). This is a safety measure to prevent accidentally overwriting the encrypted firmware with a plaintext binary, which **can ultimately lead to bricking the device**. @@ -91,53 +93,55 @@ Use the ``-e/--erase-all`` option to erase all flash sectors (not just the write Flashing an Incompatible Image ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - ``esptool.py`` checks every binary before flashing. If a valid firmware image is detected, the ``Chip ID`` and ``Minimum chip revision`` fields in its :ref:`header ` are compared against the actually connected chip. + ``esptool`` checks every binary before flashing. If a valid firmware image is detected, the ``Chip ID`` and ``Minimum chip revision`` fields in its :ref:`header ` are compared against the actually connected chip. If the image turns out to be incompatible with the chip in use or requires a newer chip revision, flashing is stopped. This behavior can be overridden with the ``--force`` option. -Read Flash Contents: read_flash --------------------------------- +Read Flash Contents: ``read-flash`` +----------------------------------- -The read_flash command allows reading back the contents of flash. The arguments to the command are an address, a size, and a filename to dump the output to. For example, to read a full 2MB of attached flash: +The read-flash command allows reading back the contents of flash. The arguments to the command are an address, a size, and a file path to output to. For example, to read a full 2MB of attached flash: :: - esptool.py -p PORT -b 460800 read_flash 0 0x200000 flash_contents.bin + esptool -p PORT -b 460800 read-flash 0 0x200000 flash_contents.bin + +Size can be specified in bytes, or with suffixes like ``k`` and ``M``. So ``0x200000`` in example can be replaced with ``2M``. It is also possible to autodetect flash size by using ``ALL`` as size. The above example with autodetection would look like this: :: - esptool.py -p PORT -b 460800 read_flash 0 ALL flash_contents.bin + esptool -p PORT -b 460800 read-flash 0 ALL flash_contents.bin .. note:: - When using the ``read_flash`` command in combination with the ``--no-stub`` argument, it may be necessary to also set the ``--flash_size`` argument to ensure proper reading of the flash contents by the ROM. + When using the ``read-flash`` command in combination with the ``--no-stub`` argument, it may be necessary to also set the ``--flash-size`` argument to ensure proper reading of the flash contents by the ROM. .. note:: - If ``write_flash`` updated the boot image's :ref:`flash mode and flash size ` during flashing then these bytes may be different when read back. + If ``write-flash`` updated the boot image's :ref:`flash mode and flash size ` during flashing then these bytes may be different when read back. -.. _erase_flash: +.. _erase-flash: -Erase Flash: erase_flash & erase_region ---------------------------------------- +Erase Flash: ``erase-flash`` & ``erase-region`` +----------------------------------------------- To erase the entire flash chip (all data replaced with 0xFF bytes): :: - esptool.py erase_flash + esptool erase-flash -To erase a region of the flash, starting at address 0x20000 with length 0x4000 bytes (16KB): +To erase a region of the flash, starting at address 0x20000 with length 16 kB (0x4000 bytes): :: - esptool.py erase_region 0x20000 0x4000 + esptool erase-region 0x20000 16k The address and length must both be multiples of the SPI flash erase sector size. This is 0x1000 (4096) bytes for supported flash chips. @@ -152,21 +156,21 @@ The address and length must both be multiples of the SPI flash erase sector size This behavior can be overridden with the ``--force`` option. **Use this only at your own risk and only if you know what you are doing!** -Read Built-in MAC Address: read_mac ------------------------------------- +Read Built-in MAC Address: ``read-mac`` +--------------------------------------- :: - esptool.py read_mac + esptool read-mac .. _read-spi-flash-id: -Read SPI Flash ID: flash_id ---------------------------- +Read SPI Flash ID: ``flash-id`` +------------------------------- :: - esptool.py flash_id + esptool flash-id Example output: @@ -180,21 +184,21 @@ Refer to `flashrom source code `__ arguments ``--flash_freq`` and ``--flash_mode``, which can be used to set the default values in the image header. This is important when generating any image which will be booted directly by the chip. -These values can also be overwritten via the ``write_flash`` command, see the `write_flash command <#write-binary-data-to-flash-write-flash>`__ for details. Overwriting these values via the ``write_flash`` command will produce an image with a recalculated SHA256 digest, otherwise, the image SHA256 digest would be invalidated by rewriting the image header. There is an option to skip appending a SHA256 digest after the image with ``--dont-append-digest`` argument of the ``elf2image`` command. +``elf2image`` also accepts the `Flash Modes <#flash-modes>`__ arguments ``--flash-freq`` and ``--flash-mode``, which can be used to set the default values in the image header. This is important when generating any image which will be booted directly by the chip. +These values can also be overwritten via the ``write-flash`` command, see the `write-flash command <#write-binary-data-to-flash-write-flash>`__ for details. Overwriting these values via the ``write-flash`` command will produce an image with a recalculated SHA256 digest, otherwise, the image SHA256 digest would be invalidated by rewriting the image header. There is an option to skip appending a SHA256 digest after the image with ``--dont-append-digest`` argument of the ``elf2image`` command. -By default, ``elf2image`` uses the sections in the ELF file to generate each segment in the binary executable. To use segments (PHDRs) instead, pass the ``--use_segments`` option. +By default, ``elf2image`` uses the sections in the ELF file to generate each segment in the binary executable. To use segments (PHDRs) instead, pass the ``--use-segments`` option. .. only:: esp8266 @@ -204,61 +208,60 @@ By default, ``elf2image`` uses the sections in the ELF file to generate each seg :: - esptool.py --chip {IDF_TARGET_NAME} elf2image --version=2 -o my_app-ota.bin my_app.elf + esptool --chip {IDF_TARGET_NAME} elf2image --version=2 -o my_app-ota.bin my_app.elf .. only:: not esp8266 - For {IDF_TARGET_NAME}, elf2image produces a single output binary "image file". By default this has the same name as the .elf file, with a .bin extension. For example: + For {IDF_TARGET_NAME}, elf2image produces a single output binary "image file". By default, this has the same name as the .elf file, with a .bin extension. For example: :: - esptool.py --chip {IDF_TARGET_NAME} elf2image my_esp_app.elf + esptool --chip {IDF_TARGET_NAME} elf2image my_esp_app.elf In the above example, the output image file would be called ``my_esp_app.bin``. The ``--ram-only-header`` configuration is mainly applicable for use within the Espressif's SIMPLE_BOOT option from 3rd party OSes such as ZephyrOS and NuttX OS. + For a detailed explanation of Simple Boot and how it works, see `Simple Boot explained `_. This option makes only the RAM segments visible to the ROM bootloader placing them at the beginning of the file and altering the segment count from the image header with the quantity of these segments, and also writing only their checksum. This segment placement may result in a more fragmented binary because of flash alignment constraints. It is strongly recommended to use this configuration with care, because the image built must then handle the basic hardware initialization and the flash mapping for code execution after ROM bootloader boot it. .. _image-info: -Output .bin Image Details: image_info -------------------------------------- +Output .bin Image Details: ``image-info`` +----------------------------------------- -The ``image_info`` command outputs some information (load addresses, sizes, etc) about a ``.bin`` file created by ``elf2image``. Command also supports ``.hex`` file created by ``merge_bin`` command from supported ``.bin`` files. - -To view more information about the image, such as set flash size, frequency and mode, or extended header information, use the ``--version 2`` option. This extended output will become the default in a future major release. +The ``image-info`` command outputs some information (load addresses, segment sizes, set flash size, frequency, and mode, extended header information, etc) about a ``.bin`` file created by ``elf2image``. Command also supports ``.hex`` file created by ``merge-bin`` command from supported ``.bin`` files. This information corresponds to the headers described in :ref:`image-format`. :: - esptool.py image_info --version 2 my_esp_app.bin + esptool image-info my_esp_app.bin .. only:: not esp8266 - If the given binary file is an application and a valid `ESP-IDF application header `__ is detected in the image, specific fields describing the application are also displayed. - - If the given binary file is a bootloader and a valid `ESP-IDF bootloader header `__ is detected in the image, specific fields describing the bootloader are also displayed. + If the given binary file is an application with a valid `ESP-IDF application header `__ + or a bootloader with a valid `ESP-IDF bootloader header `__ + detected in the image, specific fields describing the application or bootloader are also displayed. .. _merge-bin: -Merge Binaries for Flashing: merge_bin --------------------------------------- -The ``merge_bin`` command will merge multiple binary files (of any kind) into a single file that can be flashed to a device later. Any gaps between the input files are padded based on the selected output format. +Merge Binaries for Flashing: ``merge-bin`` +------------------------------------------ +The ``merge-bin`` command will merge multiple binary files (of any kind) into a single file that can be flashed to a device later. Any gaps between the input files are padded based on the selected output format. For example: :: - esptool.py --chip {IDF_TARGET_NAME} merge_bin -o merged-flash.bin --flash_mode dio --flash_size 4MB 0x1000 bootloader.bin 0x8000 partition-table.bin 0x10000 app.bin + esptool --chip {IDF_TARGET_NAME} merge-bin -o merged-flash.bin --flash-mode dio --flash-size 4MB 0x1000 bootloader.bin 0x8000 partition-table.bin 0x10000 app.bin -Will create a file ``merged-flash.bin`` with the contents of the other 3 files. This file can be later written to flash with ``esptool.py write_flash 0x0 merged-flash.bin``. +Will create a file ``merged-flash.bin`` with the contents of the other 3 files. This file can be later written to flash with ``esptool write-flash 0x0 merged-flash.bin``. **Common options:** -* The ``merge_bin`` command supports the same ``--flash_mode``, ``--flash_size`` and ``--flash_freq`` options as the ``write_flash`` command to override the bootloader flash header (see above for details). +* The ``merge-bin`` command supports the same ``--flash-mode``, ``--flash-size`` and ``--flash-freq`` options as the ``write-flash`` command to override the bootloader flash header (see above for details). These options are applied to the output file contents in the same way as when writing to flash. Make sure to pass the ``--chip`` parameter if using these options, as the supported values and the bootloader offset both depend on the chip. * The ``--format`` option will change the format of the output file. For more information about formats see formats description below. * The input files can be in either ``bin`` or ``hex`` format and they will be automatically converted to type selected by ``--format`` argument. @@ -267,7 +270,7 @@ Will create a file ``merged-flash.bin`` with the contents of the other 3 files. .. code:: sh cd build # The build directory of an ESP-IDF project - esptool.py --chip {IDF_TARGET_NAME} merge_bin -o merged-flash.bin @flash_args + esptool --chip {IDF_TARGET_NAME} merge-bin -o merged-flash.bin @flash_args HEX Output Format @@ -281,9 +284,16 @@ Intel Hex format offers distinct advantages when compared to the binary format, * **Size**: Data is carefully allocated to specific memory addresses eliminating the need for unnecessary padding. Binary images often lack detailed addressing information, leading to the inclusion of data for all memory locations from the file's initial address to its end. * **Validity Checks**: Each line in an Intel Hex file has a checksum to help find errors and make sure data stays unchanged. +When using a merged Intel Hex file with the ``write-flash`` or ``image-info`` commands, the file is automatically split into temporary raw binary files at the gaps between input files. +This splitting process allows each section to be analyzed independently, producing output similar to running ``image-info`` on the original files before merging (with the only difference being the splitting based on gaps). + +In contrast, analyzing a merged raw binary file only processes the header of the first file, providing less detailed information. + +The splitting behavior of Intel Hex files offers an additional advantage during flashing: since no padding is used between sections, flash sectors between input files remain unerased. This can significantly improve flashing speed compared to using a merged raw binary file. + .. code:: sh - esptool.py --chip {IDF_TARGET_NAME} merge_bin --format hex -o merged-flash.hex --flash_mode dio --flash_size 4MB 0x1000 bootloader.bin 0x8000 partition-table.bin 0x10000 app.bin + esptool --chip {IDF_TARGET_NAME} merge-bin --format hex -o merged-flash.hex --flash-mode dio --flash-size 4MB 0x1000 bootloader.bin 0x8000 partition-table.bin 0x10000 app.bin .. note:: @@ -303,7 +313,7 @@ The output of the command will be in ``raw`` format and gaps between individual **RAW options:** -* The ``--fill-flash-size SIZE`` option will pad the merged binary with `0xFF` bytes to the full flash specified size, for example ``--fill-flash-size 4MB`` will create a 4MB binary file. +* The ``--pad-to-size SIZE`` option will pad the merged binary with `0xFF` bytes to the full flash specified size, for example ``--pad-to-size 4MB`` will create a 4MB binary file. * The ``--target-offset 0xNNN`` option will create a merged binary that should be flashed at the specified offset, instead of at offset 0x0. @@ -322,7 +332,7 @@ Gaps between the files will be filled with `0x00` bytes. .. code:: sh - esptool.py --chip {IDF_TARGET_NAME} merge_bin --format uf2 -o merged-flash.uf2 --flash_mode dio --flash_size 4MB 0x1000 bootloader.bin 0x8000 partition-table.bin 0x10000 app.bin + esptool --chip {IDF_TARGET_NAME} merge-bin --format uf2 -o merged-flash.uf2 --flash-mode dio --flash-size 4MB 0x1000 bootloader.bin 0x8000 partition-table.bin 0x10000 app.bin Advanced Commands @@ -340,5 +350,4 @@ The following commands are less commonly used, or only of interest to advanced u * :ref:`write-flash-status` * :ref:`read-flash-sfdp` :esp8266: * :ref:`chip-id` - :esp8266: * :ref:`make-image` :esp8266: * :ref:`run` diff --git a/tools/esptool_py/docs/en/esptool/basic-options.rst b/tools/esptool_py/docs/en/esptool/basic-options.rst index 5d9bb4c468..d954e3c00b 100644 --- a/tools/esptool_py/docs/en/esptool/basic-options.rst +++ b/tools/esptool_py/docs/en/esptool/basic-options.rst @@ -5,24 +5,27 @@ Basic Options These are the basic/fundamental esptool options needed to define the communication with an ESP target. For advanced configuration options, see the :ref:`advanced-options` page. +Esptool has global and command-specific options. Global options have to be specified after ``esptool``. They are used to configure the serial port, baud rate, and chip type. +Command-specific options are specified after the command and are used to configure the command itself. For more information about commands and their options, see :ref:`commands` or see help in the command line. + .. _chip-type: -Chip Type ---------- +Chip Type: ``--chip``, ``-c`` +----------------------------- -* The target chip type can be selected using the ``--chip``/ ``-c`` option, e.g. ``esptool.py --chip {IDF_TARGET_PATH_NAME} ``. +* The target chip type can be selected using the ``--chip``/ ``-c`` option, e.g. ``esptool --chip {IDF_TARGET_PATH_NAME} ``. * A default chip type can be specified by setting the ``ESPTOOL_CHIP`` environment variable. -* If no ``-c`` option or ``ESPTOOL_CHIP`` value is specified, ``esptool.py`` automatically detects the chip type when connecting. -* Binary image generation commands, such as :ref:`elf2image ` or :ref:`merge_bin `, require the chip type to be specified. +* If no ``-c`` option or ``ESPTOOL_CHIP`` value is specified, ``esptool`` automatically detects the chip type when connecting. +* Binary image generation commands, such as :ref:`elf2image ` or :ref:`merge-bin `, require the chip type to be specified. .. _serial-port: -Serial Port ------------ +Serial Port: ``--port``, ``-p`` +------------------------------- * The serial port is selected using the ``-p`` option, like ``-p /dev/ttyUSB0`` (Linux and macOS) or ``-p COM1`` (Windows). * A default serial port can be specified by setting the ``ESPTOOL_PORT`` environment variable. -* If no ``-p`` option or ``ESPTOOL_PORT`` value is specified, ``esptool.py`` will enumerate all connected serial ports and try each one until it finds an Espressif device connected. +* If no ``-p`` option or ``ESPTOOL_PORT`` value is specified, ``esptool`` will enumerate all connected serial ports and try each one until it finds an Espressif device connected. .. note:: @@ -38,10 +41,10 @@ On most Linux distributions, the solution is to add the user to the ``dialout`` You can call ``su - $USER`` to enable read and write permissions for the serial port without having to log out and back in again. Check your Linux distribution's documentation for more information. -Baud Rate ---------- +Baud Rate: ``--baud``, ``-b`` +----------------------------- -The default esptool baud rate is 115200bps. Different rates may be set using ``-b 921600`` (or another baud rate of your choice). A default baud rate can also be specified using the ``ESPTOOL_BAUD`` environment variable. This can speed up ``write_flash`` and ``read_flash`` operations. +The default esptool baud rate is 115200bps. Different rates may be set using ``-b 921600`` (or another baud rate of your choice). A default baud rate can also be specified using the ``ESPTOOL_BAUD`` environment variable. This can speed up ``write-flash`` and ``read-flash`` operations. The baud rate is limited to 115200 when esptool establishes the initial connection, higher speeds are only used for data transfers. diff --git a/tools/esptool_py/docs/en/esptool/configuration-file.rst b/tools/esptool_py/docs/en/esptool/configuration-file.rst index 1583f86d17..8d8ccb2c9f 100644 --- a/tools/esptool_py/docs/en/esptool/configuration-file.rst +++ b/tools/esptool_py/docs/en/esptool/configuration-file.rst @@ -3,33 +3,33 @@ Configuration File ================== -``esptool.py`` relies on serial communication when connecting to, reading from, or writing to an ESP chip. -To ensure this two-way serial connection works properly, ``esptool.py`` is tuned with several pre-defined +``esptool`` relies on serial communication when connecting to, reading from, or writing to an ESP chip. +To ensure this two-way serial connection works properly, ``esptool`` is tuned with several pre-defined variables describing the timings and other nuances when sending or receiving data. These variables have been finely tuned to work in absolute majority of environments. However, it is impossible to cover all of the existing combinations of hardware, OS, and drivers. Sometimes little tweaking is necessary to cover even the most extreme edge cases. These options can be specified in a configuration file. This makes it easy to run -``esptool.py`` with custom settings, and also allows for specification of options +``esptool`` with custom settings, and also allows for specification of options that are otherwise not available to a user without having to tamper with the source code. File Location ------------- The default name for a configuration file is ``esptool.cfg``. First, the same -directory ``esptool.py`` is being run in is inspected. +directory ``esptool`` is being run in is inspected. If a configuration file is not found here, the current user's OS configuration directory is inspected next: - Linux: ``/home//.config/esptool/`` - - MacOS ``/Users//.config/esptool/`` + - macOS ``/Users//.config/esptool/`` - Windows: ``c:\Users\\AppData\Local\esptool\`` If a configuration file is still not found, the last inspected location is the home directory: - Linux: ``/home//`` - - MacOS ``/Users//`` + - macOS ``/Users//`` - Windows: ``c:\Users\\`` On Windows, the home directory can be set with the ``HOME`` or ``USERPROFILE`` environment variables. @@ -39,8 +39,8 @@ A different location for the configuration file can be specified with the ``ESPT environment variable, e.g. ``ESPTOOL_CFGFILE = ~/custom_config.cfg``. This overrides the search priorities described above. -``esptool.py`` will read settings from other usual configuration files if no other -configuration file is used. It will automatically read from ``setup.cfg`` or +``esptool`` will read settings from other usual configuration files if no other +configuration file is used. It will automatically read from ``setup.cfg`` or ``tox.ini`` if they exist. As a result, the order of priority of inspected configuration files is: @@ -53,7 +53,7 @@ As a result, the order of priority of inspected configuration files is: Syntax ------ -An ``esptool.py`` configuration file is in .ini file format: it must be +An ``esptool`` configuration file is in .ini file format: it must be introduced by an ``[esptool]`` header to be recognized as valid. This section then contains ``name = value`` entries. Lines beginning with ``#`` or ``;`` are ignored as comments. @@ -72,8 +72,8 @@ Sample configuration file: connect_attempts = 7 write_block_attempts = 2 reset_delay = 0.75 - # Overriding the default reset sequence to work in an abnormal environment - custom_reset_sequence = D0|R1|W0.1|D1|R0|W0.5|D0 + # Overriding the default reset sequence to work in an abnormal environment (prolonged delay): + custom_reset_sequence = D0|R1|W1.3|D1|R0|W0.5|D0 Options ------- @@ -87,7 +87,7 @@ Complete list of configurable options: +------------------------------+-----------------------------------------------------------+----------+ | chip_erase_timeout | Timeout for a full chip erase | 120 s | +------------------------------+-----------------------------------------------------------+----------+ -| max_timeout | The longest any command can run | 240 s | +| max_timeout | The longest any operation can run (e.g. writing a block) | 240 s | +------------------------------+-----------------------------------------------------------+----------+ | sync_timeout | Timeout for syncing with the bootloader | 0.1 s | +------------------------------+-----------------------------------------------------------+----------+ @@ -97,7 +97,7 @@ Complete list of configurable options: +------------------------------+-----------------------------------------------------------+----------+ | erase_write_timeout_per_mb | Timeout (per megabyte) for erasing and writing data | 40 s | +------------------------------+-----------------------------------------------------------+----------+ -| mem_end_rom_timeout | Short timeout for ESP_MEM_END | 0.2 s | +| mem_end_rom_timeout | Short timeout for MEM_END | 0.2 s | +------------------------------+-----------------------------------------------------------+----------+ | serial_write_timeout | Timeout for serial port write | 10 s | +------------------------------+-----------------------------------------------------------+----------+ @@ -111,14 +111,26 @@ Complete list of configurable options: +------------------------------+-----------------------------------------------------------+----------+ | custom_reset_sequence | Custom reset sequence for resetting into the bootloader | | +------------------------------+-----------------------------------------------------------+----------+ +| custom_hard_reset_sequence | Custom reset sequence for hard resetting the chip | | ++------------------------------+-----------------------------------------------------------+----------+ + + +.. note:: + + ``connect_attempts`` is the number of attempts to connect to the chip after the port is detected. This is useful when the chip does not enter bootloader mode immediately. For example, when :ref:`automatic bootloader mode ` does not work and :ref:`manual bootloader mode ` has to be used. + + On the other hand, ``open_port_attempts`` is the number of attempts to look for a port to open. When starting the command, the port does not have to be available. This can be useful when the chip is in deep sleep and is connected using USB-Serial/JTAG. In such cases, the port can disappear. -Custom Reset Sequence ---------------------- +Custom Reset Sequences +---------------------- The ``custom_reset_sequence`` configuration option allows you to define a reset sequence which will get used when an :ref:`automatic reset into the serial bootloader ` is performed. -The sequence is defined with a string in the following format: +The ``custom_hard_reset_sequence`` option allows you to define a reset sequence which will get +used when a hard reset (a reset out of the bootloader) is performed. + +A sequence is defined with a string in the following format: - Consists of individual commands divided by ``|`` (e.g. ``R0|D1|W0.5``). - Commands (e.g. ``R0``) are defined by a code (``R``) and an argument (``0``). @@ -137,14 +149,34 @@ The sequence is defined with a string in the following format: +------+-----------------------------------------------------------+-----------------+ -For example: ``D0|R1|W0.1|D1|R0|W0.5|D0`` represents the following classic reset sequence: +For example: ``D0|R1|W0.1|D1|R0|W0.05|D0`` represents the following classic reset sequence: .. code-block:: python - _setDTR(False) # IO0=HIGH + _setDTR(False) # BOOT=HIGH _setRTS(True) # EN=LOW, chip in reset time.sleep(0.1) - _setDTR(True) # IO0=LOW + _setDTR(True) # BOOT=LOW _setRTS(False) # EN=HIGH, chip out of reset time.sleep(0.05) - _setDTR(False) # IO0=HIGH, done + _setDTR(False) # BOOT=HIGH, done + +The sequence can be visualized as follows: + +.. figure:: diag/reset_sequence.svg + :align: center + :alt: Signal representation of sequence + + Signal representation of the reset sequence + +.. note:: + + Please note that this sequence is representation of signals on Espressif devkit and may differ on other boards. + +Similarly, ``R1|W0.1|R0`` represents the classic hard reset sequence: + +.. code-block:: python + + _setRTS(True) # EN=LOW, chip in reset + time.sleep(0.1) + _setRTS(False) # EN=HIGH, chip out of reset diff --git a/tools/esptool_py/docs/en/esptool/diag/reset_sequence.svg b/tools/esptool_py/docs/en/esptool/diag/reset_sequence.svg new file mode 100644 index 0000000000..32eca6bc3d --- /dev/null +++ b/tools/esptool_py/docs/en/esptool/diag/reset_sequence.svg @@ -0,0 +1,1380 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + DTR + + + + + + + + + + + + + + + + + + + + + RTS + + + + + + + + + + + + + + + + + + + + + EN + + + + + + + + + + + + + + + + + + + + + BOOT + + + + + + + + + + + + + + + + + + + + + + + + 0.1s + + + + + 0.05s + + + + + + + + + + + + + + + diff --git a/tools/esptool_py/docs/en/esptool/entering-bootloader.rst b/tools/esptool_py/docs/en/esptool/entering-bootloader.rst index 4e5a95837a..204e07ffa3 100644 --- a/tools/esptool_py/docs/en/esptool/entering-bootloader.rst +++ b/tools/esptool_py/docs/en/esptool/entering-bootloader.rst @@ -3,7 +3,7 @@ Entering the Bootloader ======================= -Espressif chips have to be reset in a certain way in order to launch the serial bootloader, only then can ``esptool.py`` communicate with the ESP chip. +Espressif chips have to be reset in a certain way in order to launch the serial bootloader, only then can ``esptool`` communicate with the ESP chip. On some development boards (including NodeMCU, WeMOS, HUZZAH Feather, Core Board, ESP32-WROVER-KIT), esptool can :ref:`automatically trigger a reset into the serial bootloader ` - in which case you don't need to read this section. diff --git a/tools/esptool_py/docs/en/esptool/flash-modes.rst b/tools/esptool_py/docs/en/esptool/flash-modes.rst index b9d18686d0..211349bb4f 100644 --- a/tools/esptool_py/docs/en/esptool/flash-modes.rst +++ b/tools/esptool_py/docs/en/esptool/flash-modes.rst @@ -1,10 +1,10 @@ -{IDF_TARGET_BOOTLOADER_OFFSET:default="0x0", esp32="0x1000", esp32s2="0x1000", esp32p4="0x2000"} +{IDF_TARGET_BOOTLOADER_OFFSET:default="0x0", esp32="0x1000", esp32s2="0x1000", esp32p4="0x2000", esp32c5="0x2000"} {IDF_TARGET_FLASH_FREQ_F:default="80", esp32c2="60", esp32h2="48"} {IDF_TARGET_FLASH_FREQ_0:default="40", esp32c2="30", esp32h2="24"} -{IDF_TARGET_FLASH_FREQ:default="``40m``, ``26m``, ``20m``, ``80m``", esp32c2="``30m``, ``20m``, ``15m``, ``60m``", esp32h2="``24m``, ``16m``, ``12m``, ``48m``", esp32c6="``40m``, ``20m``, ``80m``"} +{IDF_TARGET_FLASH_FREQ:default="``40m``, ``26m``, ``20m``, ``80m``", esp32c2="``30m``, ``20m``, ``15m``, ``60m``", esp32h2="``24m``, ``16m``, ``12m``, ``48m``", esp32c6="``40m``, ``20m``, ``80m``, esp32c5="``40m``, ``20m``, ``80m``, esp32c61="``40m``, ``20m``, ``80m``"} .. _flash-modes: @@ -12,43 +12,43 @@ Flash Modes =========== -``write_flash`` and some other commands accept command line arguments to set bootloader flash mode, flash size and flash clock frequency. The chip needs correct mode, frequency and size settings in order to run correctly - although there is some flexibility. +``write-flash`` and some other commands accept command line arguments to set bootloader flash mode, flash size and flash clock frequency. The chip needs correct mode, frequency and size settings in order to run correctly - although there is some flexibility. A header at the beginning of a bootable image contains these values. -To override these values, the options ``--flash_mode``, ``--flash_size`` and/or ``--flash_freq`` must appear after ``write_flash`` on the command line, for example: +To override these values, the options ``--flash-mode``, ``--flash-size`` and/or ``--flash-freq`` must appear after ``write-flash`` on the command line, for example: :: - esptool.py --port /dev/ttyUSB1 write_flash --flash_mode dio --flash_size 4MB 0x0 bootloader.bin + esptool --port /dev/ttyUSB1 write-flash --flash-mode dio --flash-size 4MB 0x0 bootloader.bin These options are only consulted when flashing a bootable image to an {IDF_TARGET_NAME} at offset {IDF_TARGET_BOOTLOADER_OFFSET}. These are addresses used by the ROM bootloader to load from flash. When flashing at all other offsets, these arguments are not used. -Flash Mode (--flash_mode, -fm) -------------------------------- +Flash Mode: ``--flash-mode``, ``-fm`` +------------------------------------- These set Quad Flash I/O or Dual Flash I/O modes. Valid values are ``keep``, ``qio``, ``qout``, ``dio``, ``dout``. The default is ``keep``, which keeps whatever value is already in the image file. This parameter can also be specified using the environment variable ``ESPTOOL_FM``. .. only:: esp8266 - Most boards use ``qio`` mode. Some ESP8266 modules, including the ESP-12E modules on some (not all) NodeMCU boards, are dual I/O and the firmware will only boot when flashed with ``--flash_mode dio``. + Most boards use ``qio`` mode. Some ESP8266 modules, including the ESP-12E modules on some (not all) NodeMCU boards, are dual I/O and the firmware will only boot when flashed with ``--flash-mode dio``. .. only:: not esp8266 Most {IDF_TARGET_NAME} modules use ``qio``, but are also dual I/O. -In ``qio`` mode, two additional GPIOs (9 and 10) are used for SPI flash communications. If flash mode is set to ``dio`` then these pins are available for other purposes. +In ``qio`` mode, two additional GPIOs are used for SPI flash communications. If flash mode is set to ``dio`` then these pins are available for other purposes. Search for ``SPIWP`` and ``SPIHD`` pins in the `{IDF_TARGET_NAME} Technical Reference Manual <{IDF_TARGET_TRM_EN_URL}>`__ to learn more. For a full explanation of these modes, see the :ref:`SPI Flash Modes page `. -Flash Frequency (--flash_freq, -ff) ------------------------------------- +Flash Frequency: ``--flash-freq``, ``-ff`` +------------------------------------------ Clock frequency for SPI flash interactions. Valid values are ``keep``, {IDF_TARGET_FLASH_FREQ} (MHz). The default is ``keep``, which keeps whatever value is already in the image file. This parameter can also be specified using the environment variable ``ESPTOOL_FF``. The flash chip connected to most chips works with {IDF_TARGET_FLASH_FREQ_0}MHz clock speeds, but you can try lower values if the device won't boot. The highest {IDF_TARGET_FLASH_FREQ_F}MHz flash clock speed will give the best performance, but may cause crashing if the flash or board design is not capable of this speed. -Flash Size (--flash_size, -fs) -------------------------------- +Flash Size: ``--flash-size``, ``-fs`` +------------------------------------- Size of the SPI flash, given in megabytes. @@ -56,7 +56,7 @@ Size of the SPI flash, given in megabytes. Valid values are: ``keep``, ``detect``, ``256KB``, ``512KB``, ``1MB``, ``2MB``, ``4MB``, ``2MB-c1``, ``4MB-c1``, ``8MB``, ``16MB`` -.. only:: esp32 or esp32c3 or esp32c6 or esp32c2 or esp32h2 +.. only:: esp32 or esp32c3 or esp32c6 or esp32c2 or esp32h2 or esp32c5 or esp32c61 Valid values are: ``keep``, ``detect``, ``1MB``, ``2MB``, ``4MB``, ``8MB``, ``16MB`` @@ -72,14 +72,14 @@ Size of the SPI flash, given in megabytes. For ESP8266, some :ref:`additional sizes & layouts for OTA "firmware slots" are available `. -The default ``--flash_size`` parameter is ``keep``. This means that if no ``--flash_size`` argument is passed when flashing a bootloader, the value in the bootloader .bin file header is kept instead of detecting the actual flash size and updating the header. +The default ``--flash-size`` parameter is ``keep``. This means that if no ``--flash-size`` argument is passed when flashing a bootloader, the value in the bootloader .bin file header is kept instead of detecting the actual flash size and updating the header. -To enable automatic flash size detection based on SPI flash ID, add the argument ``esptool.py [...] write_flash [...] -fs detect``. If detection fails, a warning is printed and a default value of of ``4MB`` (4 megabytes) is used. +To enable automatic flash size detection based on SPI flash ID, add the argument ``esptool [...] write-flash [...] -fs detect``. If detection fails, a warning is printed and a default value of of ``4MB`` (4 megabytes) is used. -If flash size is not successfully detected, you can find the flash size by using the ``flash_id`` command and then looking up the ID from the output (see :ref:`Read SPI flash id `). +If flash size is not successfully detected, you can find the flash size by using the ``flash-id`` command and then looking up the ID from the output (see :ref:`Read SPI flash id `). Alternatively, read off the silkscreen labelling of the flash chip and search for its datasheet. -The default ``flash_size`` parameter can also be overridden using the environment variable ``ESPTOOL_FS``. +The default ``--flash-size`` parameter can also be overridden using the environment variable ``ESPTOOL_FS``. .. only:: esp8266 @@ -92,7 +92,7 @@ The default ``flash_size`` parameter can also be overridden using the environmen If using OTA, some additional sizes & layouts for OTA "firmware slots" are available. If not using OTA updates then you can ignore these extra sizes: +-------------------+-----------------------+-----------------+-----------------+ - | flash_size arg | Number of OTA slots | OTA Slot Size | Non-OTA Space | + | flash size arg | Number of OTA slots | OTA Slot Size | Non-OTA Space | +===================+=======================+=================+=================+ | 256KB | 1 (no OTA) | 256KB | N/A | +-------------------+-----------------------+-----------------+-----------------+ @@ -113,7 +113,7 @@ The default ``flash_size`` parameter can also be overridden using the environmen | 16MB [^] | 2 | 1024KB | 14336KB | +-------------------+-----------------------+-----------------+-----------------+ - - [^] Support for 8MB & 16MB flash size is not present in all ESP8266 SDKs. If your SDK doesn't support these flash sizes, use ``--flash_size 4MB``. + - [^] Support for 8MB & 16MB flash size is not present in all ESP8266 SDKs. If your SDK doesn't support these flash sizes, use ``--flash-size 4MB``. .. only:: not esp8266 diff --git a/tools/esptool_py/docs/en/esptool/flasher-stub.rst b/tools/esptool_py/docs/en/esptool/flasher-stub.rst index c2f179be7c..ec27959d01 100644 --- a/tools/esptool_py/docs/en/esptool/flasher-stub.rst +++ b/tools/esptool_py/docs/en/esptool/flasher-stub.rst @@ -3,22 +3,22 @@ Flasher Stub ============ -``esptool.py`` is a serial flasher utility. It communicates with the ROM bootloader in `Espressif SoCs `_ in order to load user applications or read chip data via serial port. +``esptool`` is a serial flasher utility. It communicates with the ROM bootloader in `Espressif SoCs `_ in order to load user applications or read chip data via serial port. The ROM bootloader is burned into the ESP chip during manufacturing and cannot be updated. A new version is issued only when a new chip revision is released. -``esptool.py`` works around the limitations imposed by a fixed ROM bootloader by implementing a flasher stub (also known as "stub loader" or just "stub"). It is a small application used as a temporary substitute or extension for the ROM. +``esptool`` works around the limitations imposed by a fixed ROM bootloader by implementing a flasher stub (also known as "stub loader" or just "stub"). It is a small application used as a temporary substitute or extension for the ROM. -When ``esptool.py`` connects to a chip, it first uploads the flasher stub, which basically replaces the original bootloader. All following operations are then handled by the stub. +When ``esptool`` connects to a chip, it first uploads the flasher stub, which basically replaces the original bootloader. All following operations are then handled by the stub. Benefits -------- The flasher stub behaves the same as the original bootloader, but uses more heavily optimized UART routines. -The main benefit is improved performance of flashing and some other operations (like reading flash). Additionally, there are a few commands which are only available when using the stub loader (such as :ref:`erase_flash and erase_region `). It also allows to work around any bugs in ROM bootloaders. +The main benefit is improved performance of flashing and some other operations (like reading flash). Additionally, it also allows to work around any bugs in ROM bootloaders. Disabling the Stub Loader ------------------------- -There might be cases where it is necessary to disable the stub loader (e.g. debugging). To do that, run ``esptool.py`` with the ``--no-stub`` argument. All operations will then be handled by the original ROM bootloader. See the related :ref:`advanced options page `. +There might be cases where it is necessary to disable the stub loader (e.g. debugging). To do that, run ``esptool`` with the ``--no-stub`` argument. All operations will then be handled by the original ROM bootloader. See the related :ref:`advanced options page `. diff --git a/tools/esptool_py/docs/en/esptool/flashing-firmware.rst b/tools/esptool_py/docs/en/esptool/flashing-firmware.rst index 78a6c575d0..c6917b0da3 100644 --- a/tools/esptool_py/docs/en/esptool/flashing-firmware.rst +++ b/tools/esptool_py/docs/en/esptool/flashing-firmware.rst @@ -1,4 +1,4 @@ -{IDF_TARGET_BOOTLOADER_OFFSET:default="0x0", esp32="0x1000", esp32s2="0x1000", esp32p4="0x2000"} +{IDF_TARGET_BOOTLOADER_OFFSET:default="0x0", esp32="0x1000", esp32s2="0x1000", esp32p4="0x2000", esp32c5="0x2000"} .. _flashing: @@ -46,9 +46,14 @@ ESP-IDF ESP-IDF outputs the full esptool command used for flashing after the build is finished, for example:: - Project build complete. To flash, run this command: - python esptool.py -p (PORT) -b 460800 --before default_reset --after hard_reset --chip {IDF_TARGET_PATH_NAME} write_flash --flash_mode dio --flash_size detect --flash_freq 40m {IDF_TARGET_BOOTLOADER_OFFSET} build/bootloader/bootloader.bin 0x8000 build/partition_table/partition-table.bin 0x10000 build/hello_world.bin - or run 'idf.py -p (PORT) flash' + Project build complete. To flash, run: + idf.py flash + or + idf.py -p PORT flash + or + python -m esptool --chip {IDF_TARGET_PATH_NAME} -b 460800 --before default-reset --after hard-reset write-flash --flash-mode dio --flash-size 2MB --flash-freq 40m {IDF_TARGET_BOOTLOADER_OFFSET} build/bootloader/bootloader.bin 0x8000 build/partition_table/partition-table.bin 0x10000 build/hello_world.bin + or from the "esp-idf/examples/get-started/hello_world/build" directory + python -m esptool --chip {IDF_TARGET_PATH_NAME} -b 460800 --before default-reset --after hard-reset write-flash "@flash_args" Arduino ^^^^^^^ @@ -62,46 +67,48 @@ To do a verbose upload and see the exact esptool invocation, run ``pio run -v -t :: - “.../.platformio/penv/bin/python2.7” “.../.platformio/packages/tool-esptoolpy/esptool.py” --chip {IDF_TARGET_PATH_NAME} --port “/dev/cu.usbserial001” --baud 921600 --before default_reset --after hard_reset write_flash -z --flash_mode dio --flash_freq 40m --flash_size detect {IDF_TARGET_BOOTLOADER_OFFSET} .../.platformio/packages/framework-arduinoespressif32/tools/sdk/bin/bootloader_dio_40m.bin 0x8000 .../project_folder/.pio/build/esp32doit-devkit-v1/partitions.bin 0xe000 .../.platformio/packages/framework-arduinoespressif32/tools/partitions/boot_app0.bin 0x10000 .pio/build/esp32doit-devkit-v1/firmware.bin + ".../.platformio/penv/bin/python" ".../.platformio/packages/tool-esptoolpy/esptool.py" --chip {IDF_TARGET_PATH_NAME} --port "/dev/cu.usbserial001" --baud 921600 --before default-reset --after hard-reset write-flash -z --flash-mode dio --flash-freq 40m --flash-size detect {IDF_TARGET_BOOTLOADER_OFFSET} .../.platformio/packages/framework-arduinoespressif32/tools/sdk/bin/bootloader_dio_40m.bin 0x8000 .../project_folder/.pio/build/esp32doit-devkit-v1/partitions.bin 0xe000 .../.platformio/packages/framework-arduinoespressif32/tools/partitions/boot_app0.bin 0x10000 .pio/build/esp32doit-devkit-v1/firmware.bin Flashing -------- -If you split the output, you’ll find the ``write_flash`` command with a list of paths to binary files and their respective flashing offsets. If necessary, change the paths to the actual file locations. +If you split the output, you'll find the ``write-flash`` command with a list of paths to binary files and their respective flashing offsets. If necessary, change the paths to the actual file locations. Change ``PORT`` to the name of :ref:`actually used serial port ` and run the command. A successful flash looks like this:: - $ python esptool.py -p /dev/tty.usbserial-0001 -b 460800 --before default_reset --after hard_reset --chip {IDF_TARGET_PATH_NAME} write_flash --flash_mode dio --flash_size detect --flash_freq 40m {IDF_TARGET_BOOTLOADER_OFFSET} build/bootloader/bootloader.bin 0x8000 build/partition_table/partition-table.bin 0x10000 build/hello_world.bin - esptool.py v3.2-dev - Serial port /dev/tty.usbserial-0001 + $ python -m esptool -p /dev/tty.usbserial-0001 -b 460800 --before default-reset --after hard-reset --chip {IDF_TARGET_PATH_NAME} write-flash --flash-mode dio --flash-size detect --flash-freq 40m {IDF_TARGET_BOOTLOADER_OFFSET} build/bootloader/bootloader.bin 0x8000 build/partition_table/partition-table.bin 0x10000 build/hello_world.bin + esptool v5.0 + Serial port /dev/tty.usbserial-0001: Connecting......... - Chip is ESP32-D0WD (revision 1) - Features: WiFi, BT, Dual Core, 240MHz, VRef calibration in efuse, Coding Scheme None - Crystal is 40MHz - MAC: de:ad:be:ef:1d:ea - Uploading stub... - Running stub... - Stub running... - Changing baud rate to 460800 + Connected to ESP32 on /dev/tty.usbserial-0001: + Chip type: ESP32-D0WD (revision 1) + Features: WiFi, BT, Dual Core, 240MHz, Vref calibration in eFuse, Coding Scheme None + Crystal frequency: 40MHz + MAC: de:ad:be:ef:1d:ea + + Uploading stub flasher... + Running stub flasher... + Stub flasher running. + Changing baud rate to 460800... Changed. Configuring flash size... - Auto-detected Flash size: 16MB + Auto-detected flash size: 4MB Flash will be erased from 0x00001000 to 0x00007fff... Flash will be erased from 0x00008000 to 0x00008fff... Flash will be erased from 0x00010000 to 0x00039fff... - Flash params set to 0x0240 + Flash parameters set to 0x0240. + SHA digest in image updated. Compressed 25536 bytes to 15935... - Wrote 25536 bytes (15935 compressed) at 0x00001000 in 0.7 seconds (effective 275.5 kbit/s)... + Wrote 25536 bytes (15935 compressed) at 0x00001000 in 0.7 seconds (effective 275.5 kbit/s). Hash of data verified. Compressed 3072 bytes to 103... - Wrote 3072 bytes (103 compressed) at 0x00008000 in 0.1 seconds (effective 334.1 kbit/s)... + Wrote 3072 bytes (103 compressed) at 0x00008000 in 0.1 seconds (effective 334.1 kbit/s). Hash of data verified. Compressed 169232 bytes to 89490... - Wrote 169232 bytes (89490 compressed) at 0x00010000 in 2.6 seconds (effective 513.0 kbit/s)... + Wrote 169232 bytes (89490 compressed) at 0x00010000 in 2.6 seconds (effective 513.0 kbit/s). Hash of data verified. - Leaving... Hard resetting via RTS pin... It is now possible to unplug the flashed device and repeat the process by connecting another one and running the command again. diff --git a/tools/esptool_py/docs/en/esptool/index.rst b/tools/esptool_py/docs/en/esptool/index.rst index 495896fb99..b5beb29e2e 100644 --- a/tools/esptool_py/docs/en/esptool/index.rst +++ b/tools/esptool_py/docs/en/esptool/index.rst @@ -1,11 +1,11 @@ .. _esptool: -esptool.py +esptool ========== -Use ``esptool.py -h`` to see a summary of all available commands and command line options. +Use ``esptool -h`` to see a summary of all available commands and command line options. -To see all options for a particular command, append ``-h`` to the command name. ie ``esptool.py write_flash -h``. +To see all options for a particular command, append ``-h`` to the command name. ie ``esptool write-flash -h``. .. toctree:: :maxdepth: 1 @@ -14,13 +14,12 @@ To see all options for a particular command, append ``-h`` to the command name. Basic Commands Advanced Options Advanced Commands - Flasher Stub - Flash Modes Entering the Bootloader Serial Connection - Configuration File - Remote Serial Ports Flashing Firmware + Flasher Stub + Flash Modes + Configuration File Scripting .. only:: not esp8266 @@ -29,3 +28,4 @@ To see all options for a particular command, append ``-h`` to the command name. * :ref:`espefuse` * :ref:`espsecure` + * :ref:`esp_rfc2217_server ` diff --git a/tools/esptool_py/docs/en/esptool/scripting.rst b/tools/esptool_py/docs/en/esptool/scripting.rst index e4e96608d2..c2a0ee05d4 100644 --- a/tools/esptool_py/docs/en/esptool/scripting.rst +++ b/tools/esptool_py/docs/en/esptool/scripting.rst @@ -3,12 +3,355 @@ Embedding into Custom Scripts ============================= -``esptool.py``, ``espefuse.py``, and ``espsecure.py`` can easily be integrated into Python applications or called from other Python scripts. +``esptool`` can be easily integrated into Python applications or called from other Python scripts. -While it currently does have a poor Python API, something which `#208 `_ will address, it allows for passing CLI arguments to ``esptool.main()``. This workaround makes integration very straightforward as you can pass exactly the same arguments as you would on the CLI: +Using Esptool as a Python Module +-------------------------------- + +The esptool module provides a comprehensive Python API for interacting with ESP chips programmatically. By leveraging the API, developers can automate tasks such as flashing firmware, reading device information, managing flash memory, or preparing and analyzing binary images. The API supports both high-level abstractions and low-level control. + +Using the Command-Line Interface +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The most straightforward and basic integration option is to pass arguments to ``esptool.main()``. This workaround allows you to pass exactly the same arguments as you would on the CLI: .. code-block:: python - command = ['--baud', '460800', 'read_flash', '0', '0x200000', 'flash_contents.bin'] - print('Using command %s' % ' '.join(command)) + import esptool + + command = ['--baud', '460800', 'read-flash', '0', '0x200000', 'flash_contents.bin'] + print("Using command ", " ".join(command)) esptool.main(command) + +Public API Reference +^^^^^^^^^^^^^^^^^^^^ + +For more control and custom integration, esptool exposes a public API - a set of high-level functions that encapsulate common operations and simplify the interaction with the ESP chip. These functions are designed to be user-friendly and provide an intuitive way to work with the chip. The public API is the recommended way to interact with the chip programmatically. + +Basic Workflow: + +1. **Detect and Connect**: Use ``detect_chip()`` to automatically identify the connected ESP chip and establish a connection, or manually create and instantiate a specific ``ESPLoader`` object (e.g. ``ESP32ROM``) and establish a connection in two steps. +2. **Run Stub Flasher (Optional)**: Upload and execute the :ref:`stub flasher ` which provides enhanced functionality and speed. +3. **Perform Operations**: Utilize the chip object's methods or public API command functions to interact with the device. +4. **Reset and Cleanup**: Ensure proper reset and resource cleanup using context managers. + +------------ + +This example demonstrates writing two binary files using high-level commands: + +.. code-block:: python + + from esptool.cmds import detect_chip, attach_flash, reset_chip, run_stub, write_flash + + PORT = "/dev/ttyACM0" + BOOTLOADER = "bootloader.bin" + FIRMWARE = "firmware.bin" + + with detect_chip(PORT) as esp: + esp = run_stub(esp) # Skip this line to avoid running the stub flasher + attach_flash(esp) # Attach the flash memory chip, required for flash operations + with open(BOOTLOADER, "rb") as bl_file, open(FIRMWARE, "rb") as fw_file: + write_flash(esp, [(0, bl_file), (0x1000, fw_file)]) # Write the binary files + reset_chip(esp, "hard-reset") # Reset the chip + +- The ``esp`` object has to be replaced with the stub flasher object returned by ``run_stub(esp)`` when the stub flasher is activated. This step can be skipped if the stub flasher is not needed. +- Running ``attach_flash(esp)`` is required for any flash-memory-related operations to work. +- Using the ``esp`` object in a context manager ensures the port gets closed properly after the block is executed. + +------------ + +The following example demonstrates running a series of flash memory operations in one go: + +.. code-block:: python + + from esptool.cmds import ( + erase_flash, + attach_flash, + flash_id, + read_flash, + reset_chip, + run_stub, + verify_flash, + write_flash, + ) + from esptool.targets import ESP32ROM # Import the target class, e.g. ESP8266ROM, ESP32S3ROM, etc. + + PORT = "/dev/ttyACM0" + BOOTLOADER = "bootloader.bin" + FIRMWARE = "firmware.bin" + + with ESP32ROM(PORT) as esp: + esp.connect() # Connect to the ESP chip, needed when ESP32ROM is instantiated directly + esp = run_stub(esp) # Run the stub loader (optional) + attach_flash(esp) # Attach the flash memory chip, required for flash operations + flash_id(esp) # Print information about the flash chip + erase_flash(esp) # Erase the flash memory first + with open(BOOTLOADER, "rb") as bl_file, open(FIRMWARE, "rb") as fw_file: + write_flash(esp, [(0, bl_file), (0x1000, fw_file)]) # Write the binary files + verify_flash(esp, [(0, bl_file), (0x1000, fw_file)]) # Verify the written data + read_flash(esp, 0x0, 0x2400, "output.bin") # Read the flash memory into a file + reset_chip(esp, "hard-reset") # Reset the chip + +- This example doesn't use ``detect_chip()``, but instantiates a ``ESP32ROM`` class directly. This is useful when you know the target chip in advance. In this scenario ``esp.connect()`` is required to establish a connection with the device. +- Multiple operations can be chained together in a single context manager block. + +------------ + +The Public API implements a custom ``ImageSource`` input type, which expands to ``str | bytes | IO[bytes]`` - a path to the firmware image file, an opened file-like object, or the image data as bytes. + +As output, the API returns a ``bytes`` object representing the binary image or writes the image to a file if the ``output`` parameter is provided. + +The following example converts an ELF file to a flashable binary, prints the image information, and flashes the image. The example demonstrates three different ways to achieve the same result, showcasing the flexibility of the API: + +.. code-block:: python + + ELF = "firmware.elf" + + # var 1 - Loading ELF from a file, not writing binary to a file + bin_file = elf2image(ELF, "esp32c3") + image_info(bin_file) + with detect_chip(PORT) as esp: + attach_flash(esp) + write_flash(esp, [(0, bin_file)]) + + # var 2 - Loading ELF from an opened file object, not writing binary to a file + with open(ELF, "rb") as elf_file, detect_chip(PORT) as esp: + bin_file = elf2image(elf_file, "esp32c3") + image_info(bin_file) + attach_flash(esp) + write_flash(esp, [(0, bin_file)]) + + # var 3 - Loading ELF from a file, writing binary to a file + elf2image(ELF, "esp32c3", "image.bin") + image_info("image.bin") + with detect_chip(PORT) as esp: + attach_flash(esp) + write_flash(esp, [(0, "image.bin")]) + + +------------ + +**The following section provides a detailed reference for the public API functions.** + +Chip Control Operations +""""""""""""""""""""""" + +.. autofunction:: esptool.cmds.detect_chip + +.. autofunction:: esptool.cmds.run_stub + +.. autofunction:: esptool.cmds.load_ram + +.. autofunction:: esptool.cmds.run + +.. autofunction:: esptool.cmds.reset_chip + +------------ + +Chip Information Operations +""""""""""""""""""""""""""" + +.. autofunction:: esptool.cmds.chip_id + +.. autofunction:: esptool.cmds.get_security_info + +.. autofunction:: esptool.cmds.read_mac + +------------ + +Flash Memory Manipulation Operations +"""""""""""""""""""""""""""""""""""" + +.. autofunction:: esptool.cmds.attach_flash + +.. autofunction:: esptool.cmds.flash_id + +.. autofunction:: esptool.cmds.read_flash + +.. autofunction:: esptool.cmds.write_flash + +.. autofunction:: esptool.cmds.erase_flash + +.. autofunction:: esptool.cmds.erase_region + +.. autofunction:: esptool.cmds.verify_flash + +.. autofunction:: esptool.cmds.read_flash_status + +.. autofunction:: esptool.cmds.write_flash_status + +.. autofunction:: esptool.cmds.read_flash_sfdp + +------------ + +Memory Operations +""""""""""""""""" + +.. autofunction:: esptool.cmds.read_mem + +.. autofunction:: esptool.cmds.write_mem + +.. autofunction:: esptool.cmds.dump_mem + +------------ + +Binary Image Manipulation Operations +"""""""""""""""""""""""""""""""""""" + +The following commands can run without the need for a connected chip: + +.. autofunction:: esptool.cmds.elf2image + +.. autofunction:: esptool.cmds.merge_bin + +.. autofunction:: esptool.cmds.image_info + +------------ + +Utility Functions +""""""""""""""""" + +.. autofunction:: esptool.cmds.version + +------------ + +For more information, refer to the command implementations in `esptool/cmds.py `_. + + +Low-Level API Reference +^^^^^^^^^^^^^^^^^^^^^^^ + +.. warning:: + + The low-level API provides more control but requires a deeper understanding of the ESP chip, the esptool internals, and the :ref:`serial protocol `. It is recommended to use the public API functions for most use cases. + + Also, the low-level internals are not a part of the public API, so they may change in between releases. + + Please submit a :ref:`feature request ` if you are missing something from the officially supported API. + +For granular control and more configuration freedom, you can directly access the low-level methods and attributes of the ``ESPLoader`` object and create your own routines. The following is an example of a custom routine to flash the {IDF_TARGET_NAME}: + +.. note:: + + This example code is a very basic implementation of ``esptool -p /dev/ttyACM0 write-flash 0x10000 firmware.bin`` + +.. code-block:: python + + from esptool.cmds import detect_chip + + # The port of the connected ESP + PORT = "/dev/ttyACM0" + # The binary file + BIN_FILE = "./firmware.bin" + # Flash offset to flash the binary to + FLASH_ADDRESS = 0x10000 + + def progress_callback(percent): + print(f"Wrote: {int(percent)}%") + + with detect_chip(PORT) as esp: + description = esp.get_chip_description() + features = esp.get_chip_features() + print(f"Detected ESP on port {PORT}: {description}") + print("Features:", ", ".join(features)) + + esp = esp.run_stub() + with open(BIN_FILE, 'rb') as binary: + # Load the binary + binary_data = binary.read() + total_size = len(binary_data) + print(f"Binary size: {total_size} bytes") + + # Write binary blocks + esp.flash_begin(total_size, FLASH_ADDRESS) + for i in range(0, total_size, esp.FLASH_WRITE_SIZE): + block = binary_data[i:i + esp.FLASH_WRITE_SIZE] + # Pad the last block + block = block + bytes([0xFF]) * (esp.FLASH_WRITE_SIZE - len(block)) + esp.flash_block(block, i + FLASH_ADDRESS) + progress_callback(i / total_size * 100) + esp.flash_finish() + + # Reset the chip out of bootloader mode + esp.hard_reset() + +------------ + +For more information, refer to the methods of the ``ESPLoader`` class in `esptool/loader.py `_. + +.. _logging: + +Redirecting Output with a Custom Logger +--------------------------------------- + +Esptool allows redirecting output by implementing a custom logger class. This can be useful when integrating esptool with graphical user interfaces or other systems where the default console output is not appropriate. Below is an example demonstrating how to create and use a custom logger: + +.. code-block:: python + + from esptool.logger import log, TemplateLogger + import sys + + class CustomLogger(TemplateLogger): + log_to_file = True + log_file = "esptool.log" + + def print(self, message="", *args, **kwargs): + # Print to console + print(f"[CustomLogger]: {message}", *args, **kwargs) + # Optionally log to a file + if self.log_to_file: + with open(self.log_file, "a") as log: + log.write(f"{message}\n") + + def note(self, message): + self.print(f"NOTE: {message}") + + def warning(self, message): + self.print(f"WARNING: {message}") + + def error(self, message): + self.print(message, file=sys.stderr) + + def stage(self, finish=False): + # Collapsible stages not needed in this example + pass + + def progress_bar( + self, + cur_iter, + total_iters, + prefix = "", + suffix = "", + bar_length: int = 30, + ): + # Progress bars replaced with simple percentage output in this example + percent = f"{100 * (cur_iter / float(total_iters)):.1f}" + self.print(f"Finished: {percent}%") + + def set_verbosity(self, verbosity): + # Set verbosity level not needed in this example + pass + + # Replace the default logger with the custom logger + log.set_logger(CustomLogger()) + + # From now on, all esptool output will be redirected through the custom logger + # Your code here ... + +In this example, the ``CustomLogger`` class provides additional functionality such as logging messages to a file, which the original ``EsptoolLogger`` (imported from ``esptool.logger`` as an initiated object ``log``) doesn't. The ``EsptoolLogger.set_logger()`` method is used to replace the default logger with the custom logger. + +To ensure compatibility with esptool, the custom logger should re-implement (or inherit) the following methods from the original ``EsptoolLogger`` class (see the reference implementation `here `__), this is enforced by the ``TemplateLogger`` abstract class: + +- ``print``: Handles plain message logging. +- ``note``: Logs informational messages. +- ``warning``: Logs warning messages. +- ``error``: Logs error messages. +- ``stage``: Starts or ends a collapsible output stage. +- ``progress_bar``: Displays a progress bar. +- ``set_verbosity``: Sets the verbosity level for logging. + +.. autoclass:: esptool.logger.EsptoolLogger + :members: print, note, warning, error, stage, progress_bar, set_verbosity + :member-order: bysource + +These methods are essential for maintaining proper integration and behavior with esptool. Additionally, all output printing should be made using ``log.print()`` (or the respective method, such as ``log.note()`` or ``log.warning()``) instead of the standard ``print()`` function to ensure the output is routed through the custom logger. This ensures consistency and allows the custom logger to handle all output appropriately. You can further customize this logger to fit your application's needs, such as integrating with GUI components or advanced logging frameworks. diff --git a/tools/esptool_py/docs/en/index.rst b/tools/esptool_py/docs/en/index.rst index 201b71bf14..1a74e68b17 100644 --- a/tools/esptool_py/docs/en/index.rst +++ b/tools/esptool_py/docs/en/index.rst @@ -1,24 +1,34 @@ -Esptool.py Documentation -======================== +Esptool Documentation +===================== -This is the documentation for ``esptool.py`` - a Python-based, open source, platform independent utility to communicate with the ROM bootloader in `Espressif SoCs `_. +.. important:: -``esptool.py``, ``espefuse.py`` and ``espsecure.py`` are a complete toolset for working with Espressif chips. They can do a number of things, for example: + This document describes how to use ``esptool`` with the {IDF_TARGET_NAME} SoC. To switch to a different SoC target, choose target from the dropdown in the upper left corner. + + Please note that this documentation is for the version of ``esptool`` v5. You can find your version in the command output or by running ``esptool version``. + For the version of ``esptool`` v4 please refer to the `v4 documentation `_ or pick from the dropdown in the upper left corner. + +This is the documentation for ``esptool`` - a Python-based, open-source, platform-independent utility for flashing, provisioning, and interacting with Espressif SoCs. Esptool communicates with the ROM bootloader (or the flasher stub) in `Espressif SoCs `_. + +The flasher stub is a small program included with esptool that replaces the original ROM bootloader in the chip to fix some of its limitations and bugs. See :ref:`stub` for more details. + +``esptool``, ``espefuse`` and ``espsecure`` are a complete toolset for working with Espressif chips. They can do a number of things, for example: * Read, write, erase, and verify binary data stored in flash. * Read chip features and other related data such as MAC address or flash chip ID. -* Read and write the one-time-programmable efuses. +* Read and write the one-time-programmable eFuses. * Prepare binary executable images ready for flashing. * Analyze, assemble, and merge binary images. -This document describes using ``esptool.py`` with the {IDF_TARGET_NAME} SoC. To switch to a different SoC target, choose target from the dropdown in the upper left. +``esptool`` can be used both as a command-line tool and as a Python library. The command-line is the most common way to use the tool, and is the primary focus of this documentation. To use it as a library, see the :ref:`scripting ` section. + Quick Start ----------- Getting started is easy: -1) Install ``esptool.py``: +1) Install ``esptool``: :: @@ -29,14 +39,32 @@ Getting started is easy: 2) Connect an Espressif chip to your computer. -3) Run ``esptool.py`` commands. For example, to read information about your chip's SPI flash, run: +.. note:: + + Please note that serial communication has to work and chip has to be in the :ref:`download mode `. + This is usually done :ref:`automatically ` or can be done :ref:`manually `. Esptool cannot function until this is resolved. For more information, see :ref:`troubleshooting`. + +3) Run ``esptool`` commands. For example, to read information about your chip's SPI flash, run: :: - $ esptool.py -p PORT flash_id + $ esptool -p PORT flash-id Replace ``PORT`` with the name of used serial port. If connection fails, see :ref:`troubleshooting`. +After successfully executing the command, esptool will hard reset the chip, causing it to run the user code. This behavior can be adjusted, see :ref:`advanced-options`. + +Alternatives +------------ + +``esptool`` is not the only tool for working with Espressif chips. Some notable options include: + +- `esptool.js `__ is a JavaScript port of esptool that can be used in a web browser or in a Node.js environment. +- `espflash `__ is a Rust port of esptool. It relies on the support in `esp-hal `__, which may delay support for new chips. +- `OpenOCD `__ is a general-purpose tool for debugging and flashing chips. + +Among these, esptool is the most feature-rich, and support for the newest chips and features usually appears here first. + More Information ---------------- @@ -47,9 +75,11 @@ More Information Esptool :not esp8266:Espefuse :not esp8266:Espsecure + Remote Serial Ports Advanced Topics Troubleshooting Contribute Versions + Migration Guide Resources About diff --git a/tools/esptool_py/docs/en/installation.rst b/tools/esptool_py/docs/en/installation.rst index 9e201d9d37..40a1b1bb26 100644 --- a/tools/esptool_py/docs/en/installation.rst +++ b/tools/esptool_py/docs/en/installation.rst @@ -9,8 +9,8 @@ How to Install Global Installation ^^^^^^^^^^^^^^^^^^^ -You will need `Python 3.7 or newer `_ installed on your system to use the latest version of ``esptool.py``. -If your use case requires Python 2.7, 3.4, 3.5, or 3.6, please use ``esptool.py`` v3.3.* instead. +You will need `Python 3.10 or newer `_ installed on your system to use the latest version of ``esptool``. +If your use case requires Python 3.7, 3.8, or 3.9, please use ``esptool`` v4.x. For Python 2.7, 3.4, 3.5, or 3.6, please use ``esptool`` v3.3.* instead. The latest stable esptool release can be installed from `PyPI `_ via pip: @@ -18,11 +18,11 @@ The latest stable esptool release can be installed from `PyPI `_ for information about how to access pip. +With some Python installations this may not work and you'll receive an error, try ``python -m pip install esptool`` or ``pip3 install esptool``, or consult your `Python installation manual `_ for information about how to access pip. `Setuptools `_ is also a requirement which is not available on all systems by default. You can install it by a package manager of your operating system, or by ``pip install setuptools``. -After installing, you will have ``esptool.py`` installed into the default Python executables directory and you should be able to run it with the command ``esptool.py`` or ``python -m esptool``. Please note that probably only ``python -m esptool`` will work for Pythons installed from Windows Store. +After installing, you will have ``esptool`` installed into the default Python executables directory and you should be able to run it with the command ``esptool`` or ``python -m esptool``. Please note that probably only ``python -m esptool`` will work for Pythons installed from Windows Store. .. note:: @@ -31,7 +31,7 @@ After installing, you will have ``esptool.py`` installed into the default Python Virtual Environment Installation ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -To ensure that ``esptool.py`` is used in isolation, and any changes made during its usage won't affect other Python environments or SDK installations, it is advised to install it in a virtual environment and use it directly if possible (more information in the :ref:`flashing` article). +To ensure that ``esptool`` is used in isolation, and any changes made during its usage won't affect other Python environments or SDK installations, it is advised to install it in a virtual environment and use it directly if possible (more information in the :ref:`flashing` article). Creating a virtual environment (venv) is a good practice. This is particularly helpful for users who may be concerned about interfering with existing installations (e.g. in an environment of a development-setup framework). Here's a quick guide: @@ -39,70 +39,103 @@ Creating a virtual environment (venv) is a good practice. This is particularly h - Activate the virtual environment: - On Windows: ``esptoolenv\Scripts\activate`` - - On Linux or MacOS: ``source esptoolenv/bin/activate`` + - On Linux or macOS: ``source esptoolenv/bin/activate`` -- Install the latest ``esptool.py`` version within the active virtual environment: ``pip install esptool`` -- You can now use it within this virtual environment without affecting your system-wide installations: ``esptool.py `` -- When you're done using ``esptool.py``, deactivate the virtual environment: ``deactivate``. The environment can be reused by activating it again. +- Install the latest ``esptool`` version within the active virtual environment: ``pip install esptool`` +- You can now use it within this virtual environment without affecting your system-wide installations: ``esptool `` +- When you're done using ``esptool``, deactivate the virtual environment: ``deactivate``. The environment can be reused by activating it again. - If you no longer need the virtual environment, you can remove it by deleting the ``esptoolenv`` directory. +Binary Releases +^^^^^^^^^^^^^^^^ + +If you do not want to install Python and all the dependencies, you can use the pre-built binaries from the `GitHub Releases `_. + +Please note that the binaries might have some limitations: + +- The binaries might rely on some system libraries that are not available on all systems. +- The binaries are available only for selected operating systems - currently macOS (x86_64, arm64), Linux (x86_64, armv7,aarch64) and Windows (x86_64). +- The binaries might get reported as malware by your antivirus software. +- The application is larger in size compared to the Python package installation, as it includes all dependencies. +- The application has slower startup time compared to running the Python script directly. +- The application cannot be imported as a Python module in other Python applications. + +.. note:: + + For Linux, the binaries are built using Ubuntu 22.04 as the base image. That means any version older than Ubuntu 22.04 (or any other distribution that uses ``glibc<2.35``) might not work. + For using on Ubuntu 20.04, please use the Python package installation or ``v4.*`` release. + How to Update ------------- Standalone ^^^^^^^^^^ -If you are using ``esptool.py`` as a standalone tool (as a global installation or in a virtual environment), updating to the latest version released on the `PyPI `_ index is simple: +If you are using ``esptool`` as a standalone tool (as a global installation or in a virtual environment), updating to the latest version released on the `PyPI `_ index is simple: :: $ pip install --upgrade esptool -As a Component -^^^^^^^^^^^^^^ +As a Part of SDK/Framework +^^^^^^^^^^^^^^^^^^^^^^^^^^ -If ``esptool.py`` is installed as a component of a development framework (e.g. `ESP-IDF `_, `Arduino `_, or `PlatformIO `_), it is advised to follow the update guide of used framework for instructions and not to update the tool directly. +If ``esptool`` is installed as a part of a development SDK/framework (e.g. `ESP-IDF `_, `Arduino `_, or `PlatformIO `_), it is advised to follow the update guide of used framework for instructions and not to update the tool directly. -If updating directly is unavoidable, make sure you update to a compatible version by staying on the same MAJOR version number (explained in the :ref:`versions` article). For instance, if your currently installed ``esptool.py`` is ``v3.3.1``, only update to ``v3.*.*``. You risk introducing incompatible changes by updating to ``v4.*.*`` or higher. +If updating directly is unavoidable, make sure you update to a compatible version by staying on the same MAJOR version number (explained in the :ref:`versions` article). For instance, if your currently installed ``esptool`` is ``v3.3.1``, only update to ``v3.*.*``. You risk introducing incompatible changes by updating to ``v4.*.*`` or higher. :: - $ pip install esptool==3.3.2 + $ pip install "esptool<4" -Shell completions +.. _shell-completion: + +Shell Completions ----------------- To activate autocompletion, you can manually add commands provided below to your shell's config file or run them in your current terminal session for one-time activation. You will likely have to restart or re-login for the autocompletion to start working. -bash: -:: +.. tabs:: - eval "$(register-python-argcomplete esptool.py espsecure.py espefuse.py)" + .. group-tab:: Bash -zsh: + .. code-block:: bash -To activate completions in zsh, first make sure `compinit` is marked for -autoload and run autoload: + eval "$(_ESPTOOL_PY_COMPLETE=bash_source esptool)" + eval "$(_ESPSECURE_PY_COMPLETE=bash_source espsecure)" + eval "$(_ESPEFUSE_PY_COMPLETE=bash_source espefuse)" -.. code-block:: bash - autoload -U compinit - compinit + .. group-tab:: Zsh -Afterwards you can enable completions for esptool.py, espsecure.py and espefuse.py: + To activate completions in zsh, first make sure `compinit` is marked for + autoload and run autoload: -:: + .. code-block:: bash - eval "$(register-python-argcomplete esptool.py espsecure.py espefuse.py)" + autoload -U compinit + compinit -fish: + Afterwards you can enable completions for esptool, espsecure and espefuse: -Not required to be in the config file, only run once -:: + .. code-block:: bash + + eval "$(_ESPTOOL_PY_COMPLETE=zsh_source esptool)" + eval "$(_ESPSECURE_PY_COMPLETE=zsh_source espsecure)" + eval "$(_ESPEFUSE_PY_COMPLETE=zsh_source espefuse)" + + + .. group-tab:: Fish + + .. code-block:: bash + + _ESPTOOL_PY_COMPLETE=fish_source esptool | source + _ESPSECURE_PY_COMPLETE=fish_source espsecure | source + _ESPEFUSE_PY_COMPLETE=fish_source espefuse | source + - register-python-argcomplete --shell fish esptool.py espsecure.py espefuse.py >~/.config/fish/completions/esptool.py.fish Other shells nor OS Windows are not supported. diff --git a/tools/esptool_py/docs/en/migration-guide.rst b/tools/esptool_py/docs/en/migration-guide.rst new file mode 100644 index 0000000000..bbf4ec82dd --- /dev/null +++ b/tools/esptool_py/docs/en/migration-guide.rst @@ -0,0 +1,416 @@ +.. _migration: + +``v5`` Migration Guide +====================== + +This document describes the breaking changes made to esptool.py, espsecure.py and espefuse.py in the major release ``v5``. It provides guidance on adapting existing workflows and scripts to ensure compatibility when updating from ``v4.*``. + + +Command-Line Tool Invocation Changes +************************************ + +The preferred way to invoke esptool command-line tools has changed. Instead of running the scripts with `.py` suffix, you should now use the console scripts without the `.py` suffix. + +**Affected Tools:** + +- ``esptool.py`` → ``esptool`` +- ``espefuse.py`` → ``espefuse`` +- ``espsecure.py`` → ``espsecure`` +- ``esp_rfc2217_server.py`` → ``esp_rfc2217_server`` + +**Migration Steps:** + +1. Update your command-line invocations to use the new names without `.py`: + + **Before:** + + .. code-block:: bash + + esptool.py chip_id + espefuse.py summary + espsecure.py sign_data --keyfile key.pem data.bin + + **After:** + + .. code-block:: bash + + esptool chip_id + espefuse summary + espsecure sign-data --keyfile key.pem data.bin + +2. Update scripts to use the new command names. + +.. note:: + + Scripts with ``.py`` suffix are still available for backward compatibility, but they will produce deprecation warning and will be removed in the next major release. + + +esptool ``v5`` Migration Guide +****************************** + +``image-info`` Output Format Change +################################### + +The output format of the :ref:`image-info ` command has been **updated in v5**. The original format (``--version 1``) is **deprecated** and replaced by the updated format (``--version 2``). The ``--version`` argument has been **removed entirely**, and the new format is now the default and only option. + +**Changes in the New Format:** + +- Improved readability and structure. +- Additional metadata fields for better debugging and analysis. +- Consistent formatting for all ESP chip variants. + +**Migration Steps:** + +1. Update any scripts or tools that parse the ``image-info`` output to use the new format. +2. Remove any ``--version`` arguments from ``image-info`` commands. + +Output Logging +############## + +The esptool ``v5`` release introduces a centralized logging mechanism to improve output management and allow redirection. + +**Key Changes:** + +- All esptool output is now routed through an ``EsptoolLogger`` class. +- The output can include ANSI color codes for better readability. +- Custom loggers can be implemented to redirect output to files or other destinations. + +**Migration Steps:** + +1. If your scripts rely on direct ``print()`` statements, update them to use the centralized logger for consistent output. Calls to the logger should be made using ``log.print()`` (or the respective method, such as ``log.note()``, ``log.warning()``, or ``log.error()``). +2. Refer to the provided documentation to implement custom loggers as needed. +3. Update GUIs or tools to leverage the progress tracking API for better user feedback during lengthy operations. + +See the :ref:`logging ` section for more details on available logger methods and custom logger implementation. + +``write-flash`` ``--verify`` Argument +##################################### + +The ``--verify`` option for the :ref:`write-flash ` command has been **deprecated in v5**. Flash verification is performed automatically after every successful write operation when technically feasible. + +**Behavior:** + +- Verification occurs by default after flashing completes. +- No action is needed to enable verification - it is mandatory when possible. +- Verification is **skipped** if Secure Download Mode (SDM) is active or during encrypted writes (using ``--encrypt``). + +**Migration Steps:** + +1. Remove all ``--verify`` arguments from existing ``write-flash`` commands. +2. Update scripts/CI pipelines to remove ``--verify`` flags. + +Error Output Handling +##################### + +In ``v5``, error handling and output behavior have been improved to provide better user experience and script compatibility. + +**Key Changes:** + +- All error messages, including fatal errors, are now printed to **STDERR** instead of STDOUT. +- User keyboard interrupts (e.g., Ctrl+C) are caught and raise an exit code of 2 to indicate an operation interruption. +- Error messages are displayed in **red text** for better visibility. +- This change ensures that errors are not lost when STDOUT is filtered or redirected. + +**Migration Steps:** + +1. Update scripts that rely on parsing STDOUT for error messages to check STDERR instead. +2. Ensure scripts handle non-zero exit codes correctly in the case of operations interrupted by the user. + +Beta Target Support Removal +########################### + +Support for the following beta targets has been **removed in v5**: + +- ``ESP32-C5(beta3)`` +- ``ESP32-C6(beta)`` +- ``ESP32-H2(beta1)`` +- ``ESP32-H2(beta2)`` +- ``ESP32-S3(beta2)`` + +**Migration Steps:** + +1. Update any scripts or workflows not to target these beta chips. +2. Remove any references to these beta targets from CI/CD pipelines or build scripts. + +Use esptool ``v4`` for legacy workflows targeting these beta chips. + +``verify-flash`` ``--diff`` Argument +#################################### + +The format of the ``--diff`` option of the :ref:`verify-flash ` command has **changed in v5**. Previously, ``--diff=yes/no`` had to be specified to enable or disable the diff output. In the new version, the ``--diff`` option is a simple boolean switch without the need of a ``yes`` or ``no`` value. + +**Migration Steps:** + +1. Rewrite the ``--diff=yes`` argument to a simple ``--diff`` in any existing ``verify-flash`` commands in scripts/CI pipelines. Delete ``--diff=no`` completely if detailed diff output is not required. + +Using esptool as a Python Module +################################ + +All command functions (e.g., ``verify-flash``, ``write-flash``) have been refactored to remove their dependency on the ``args`` object from the argparse module. Instead, all arguments are now passed explicitly as individual parameters. This change, combined with enhancements to the public API, provides a cleaner, more modular interface for programmatic use of esptool in custom scripts and applications (see :ref:`scripting `). + +**Key Changes:** + +- Refactored Function Signatures: Previously, command functions relied on an ``args`` object (e.g., ``args.addr_filename``, ``args.diff``). Now, they take individual parameters with explicit types and default values, improving clarity and enabling a robust API. +- Public API Expansion: The public API (exposed via ``esptool.cmds``) has been formalized with high-level functions like ``detect_chip()``, ``attach_flash()``, ``write-flash()``, and ``reset_chip()``, designed for ease of use in Python scripts. + +**Migration Steps:** + +1. Update Function Calls: If you are calling esptool functions programmatically, replace ``args`` object usage with individual parameter passing. Refer to the function signatures in ``esptool.cmds`` for the new parameter names, types, and defaults. +2. Leverage the Public API: Use the new high-level functions in ``esptool.cmds`` for common operations like chip detection, flash attachment, flashing, resetting, or image generation. +3. Test your updated scripts to ensure compatibility with the new API. + +For detailed examples and API reference, see the :ref:`scripting ` section. + + +Flash Operations from Non-flash Related Commands +################################################ + +When esptool is used as a CLI tool, the following commands no longer automatically attach the flash by default, since flash access is not required for their core functionality: + +- ``load-ram`` +- ``read-mem`` +- ``write-mem`` +- ``dump-mem`` +- ``chip-id`` +- ``read-mac`` + +The ``--spi-connection`` CLI argument has been **removed** from non-flash related commands in v5. This argument had no effect on the command execution. Affected commands: + +- ``elf2image`` +- ``merge-bin`` + +**Migration Steps:** + +1. Update any scripts that attempt to attach flash from non-flash related commands. +2. If you need to attach flash for above mentioned commands, use the ``attach_flash`` function from the public API instead. For more details see :ref:`scripting `. +3. Remove the ``--spi-connection`` argument from ``elf2image`` and ``merge-bin`` commands. + + +Shell Completion +################ + +The esptool ``v5`` has switched to using `Click `_ for command line argument parsing, which changes how shell completion works. + +**Migration Steps:** + +1. Remove the old shell completion code from your scripts and shell configuration files like ``.bashrc``, ``.zshrc``, ``.config/fish/config.fish``, etc. +2. Follow the new shell completion setup instructions in the :ref:`shell-completion` section of the :ref:`installation ` guide. + +``merge-bin`` ``--fill-flash-size`` Argument +############################################ + +The ``--fill-flash-size`` option of the :ref:`merge-bin ` command has been renamed to ``--pad-to-size``. This change provides a more intuitive and descriptive name for the argument and is consistent with the naming scheme in other esptool image manipulation commands. + +**Migration Steps:** + +1. Rename the ``--fill-flash-size`` to ``--pad-to-size`` in any existing ``merge-bin`` commands in scripts/CI pipelines. + +``write-flash`` ``--ignore-flash-encryption-efuse-setting`` Argument +#################################################################### + +The ``--ignore-flash-encryption-efuse-setting`` option of the :ref:`write-flash ` command has been renamed to ``--ignore-flash-enc-efuse``. This change shortens the argument name to improve readability and consistency with other esptool options. + +**Migration Steps:** + +1. Rename the ``--ignore-flash-encryption-efuse-setting`` to ``--ignore-flash-enc-efuse`` in any existing ``write-flash`` commands in scripts/CI pipelines. + +``make_image`` Command Removal +############################## + +The ``make_image`` command for the ESP8266 has been **removed in v5**. This command has been deprecated in favor of using **objcopy** (or other tools) to generate ELF images and then using ``elf2image`` to create the final ``.bin`` file. + +**Migration Steps:** + +1. Replace any ``make_image`` workflows with the recommended way of assembling firmware images using **objcopy** and ``elf2image``. + +Using Binary from GitHub Releases on Linux +########################################## + +The ``esptool`` binary from GitHub Releases on Linux is now using Ubuntu 22.04 as the base image. That means the image is using ``glibc`` 2.35, which is not fully compatible with the ``glibc`` 2.28 from Ubuntu 20.04 (the base image for ``v4.*``). + +**Migration Steps:** + +1. Update your operating system to a newer version which bundles ``glibc`` 2.35 or later + +Command and Option Renaming +########################### + +All the commands and options have been renamed to use ``-`` instead of ``_`` as a separator (e.g., ``write_flash`` -> ``write-flash``). + +Old command and option names are **deprecated**, meaning they will work for now with a warning, but will be removed in the next major release. + +This change affects most of the commands and the following options: ``--flash_size``, ``--flash_mode``, ``--flash_freq``, ``--use_segments``. + +**Migration Steps:** + +1. Replace all underscores in command and option names with ``-`` in your scripts and CI pipelines. + +Log Format Changes +################## + +A significant amount of changes have been made to the log styling and formatting in ``v5``. Some of the messages, warnings, and errors are now formatted differently or reworded to provide more context and improve readability. Exhaustive list of changed messages won't be provided. + +**Migration Steps:** + +1. Make sure to adjust any of your scripts, asserts, CI workflows, or others to accommodate the new/changed format of messages. If you are parsing the log output (not recommended), consider importing esptool as a module and using the public API (see :ref:`here `) to get the information you need. + + +Reset Mode Renaming +################### + +Choices for the ``--before`` and ``--after`` options have been renamed to use ``-`` instead of ``_`` as a separator (e.g., ``default_reset`` -> ``default-reset``). + + +**Migration Steps:** + +1. Replace all underscores in the ``--before`` and ``--after`` options with ``-`` in your scripts. + +.. only:: not esp8266 + + espsecure ``v5`` Migration Guide + ******************************** + + Command and Option Renaming + ########################### + + All the commands and options have been renamed to use ``-`` instead of ``_`` as a separator (e.g., ``sign_data`` -> ``sign-data``). + + Old command and option names are **deprecated**, meaning they will work for now with a warning, but will be removed in the next major release. + + This change affects most of the commands and the following options: ``--aes_xts``, ``--flash_crypt_conf``, ``--append_signatures``. + + **Migration Steps:** + + 1. Replace all underscores in command and option names with ``-`` in your scripts and CI pipelines. + + Public API Changes + ################## + + The public API of ``espsecure`` has been updated to provide a more consistent and user-friendly interface for programmatic use in custom scripts and applications. + + **Key Changes:** + + - All functions now accept individual parameters instead of relying on the ``args`` object from the argparse module. Affected functions are: + - ``digest_secure_bootloader`` + - ``generate_signing_key`` + - ``digest_secure_bootloader`` + - ``generate_signing_key`` + - ``sign_data`` including ``sign_secure_boot_v1`` and ``sign_secure_boot_v2`` + - ``verify_signature`` including ``verify_signature_v1`` and ``verify_signature_v2`` + - ``extract_public_key`` + - ``signature_info_v2`` + - ``digest_sbv2_public_key`` and ``digest_rsa_public_key`` + - ``digest_private_key`` + - ``generate_flash_encryption_key`` + - ``decrypt_flash_data`` + - ``encrypt_flash_data`` + - The ``main`` function parameter ``custom_commandline`` has been renamed to ``argv`` to unify the naming convention with esptool. + + **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, ...)``. + 2. Replace the ``custom_commandline`` parameter with ``argv`` in the ``main`` function call. + + espefuse ``v5`` Migration Guide + ******************************* + + Reset Mode Renaming + ################### + + Choices for the ``--before`` option have been renamed to use ``-`` instead of ``_`` as a separator (e.g., ``default_reset`` -> ``default-reset``). + + **Migration Steps:** + + 1. Replace all underscores in the ``--before`` option with ``-`` in your scripts. + + Command and Option Renaming + ########################### + + All the commands and options have been renamed to use ``-`` instead of ``_`` as a separator (e.g., ``burn_custom_mac`` -> ``burn-custom-mac``). + + From options only ``--file_name`` has been renamed to ``--file-name``. + + Old command and option names are **deprecated**, meaning they will work for now with a warning, but will be removed in the next major release. + + **Migration Steps:** + + 1. Replace all underscores in the command names with ``-`` in your scripts. + + + ``--port`` Option is Required + ############################# + + The ``--port`` option is now required for all commands (except when using ``--virt``). Previously it was optional and defaulted to ``/dev/ttyUSB0``. + + **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`` as a Python module (see :ref:`here `). + + **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 --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`. diff --git a/tools/esptool_py/docs/en/remote-serial-ports.rst b/tools/esptool_py/docs/en/remote-serial-ports.rst new file mode 100644 index 0000000000..d450f2481e --- /dev/null +++ b/tools/esptool_py/docs/en/remote-serial-ports.rst @@ -0,0 +1,66 @@ +Remote Serial Ports +=================== + +If you would like to connect to an Espressif SoC that is not directly connected to your system, you can use a remote serial port. This is useful when the chip is on a different machine, or for example when using WSL on Windows. + +It is possible to connect to any networked remote serial port that supports `RFC2217 `__ (Telnet) protocol. To do this, specify the serial port to esptool as ``rfc2217://:``. For example, to read information about your chip's SPI flash, run: + +:: + + esptool --port rfc2217://192.168.1.77:4000 flash-id + +Custom baud rates and DTR/RTS automatic resetting are supported over the RFC2217 protocol, the same as for a local serial port. + +.. _rfc2217_server: + +Pyserial Example Servers +------------------------ + +PySerial (which is a dependency of esptool) includes two RFC2217 example programs - `a single-port example `__ and a `multi-port example `__. +These example servers can run on any OS that supports pyserial, and are the simplest way to connect to an Espressif SoC over the network. + +There is an issue with `automatic resetting due to network latency `__. In order to work around this issue, a modified version of the single-port server example called ``esp_rfc2217_server`` is provided with esptool. + +On server: + +:: + + esp_rfc2217_server -p 4000 /dev/ttyUSB1 + +On client: + +:: + + esptool --port rfc2217://ADDRESS_OF_SERVER:4000?ign_set_control flash-id + + +Raw Sockets +----------- + +For servers or hardware network/serial adapters which don't support the full RFC2217, it is also possible to specify ``--port socket://:`` syntax for a simple "raw TCP socket" protocol. + +These raw sockets don't support setting the baud rate or automatic resetting into the bootloader. If using this mode, don't pass the ``--baud`` option to esptool. You need to set the baud rate manually on the server, and manually reset the chip into the bootloader mode (or use some other signalling/control method to tell the server to do so). + +Here's a very basic example using the common Linux/macOS command line "netcat" and "stty" commands: + +On server: + +:: + + stty -F /dev/ttyUSB1 230400 # set baud rate + nc -p 4000 -lk < /dev/ttyUSB1 > /dev/ttyUSB1 + +On client: + +:: + + esptool -p socket://localhost:4000 flash-id + +.. note:: + + Using RFC2217 is strongly recommended where possible. + +More Details +------------ + +All of the remote serial port support comes via pyserial. Read more `here `__. (Please keep in mind that the link points to documentation for the most recent pyserial version. You may have an older version.) diff --git a/tools/esptool_py/docs/en/resources.rst b/tools/esptool_py/docs/en/resources.rst index 13add7041e..ac6f325cbf 100644 --- a/tools/esptool_py/docs/en/resources.rst +++ b/tools/esptool_py/docs/en/resources.rst @@ -13,7 +13,7 @@ Useful Links * Several `books `_ have been written about the ESP8266 or ESP32 series of SoCs and they are listed on `Espressif `__ web site. -* If you're interested in contributing to esptool.py, please check the :ref:`contribute` page. +* If you're interested in contributing to esptool, please check the :ref:`contribute` page. * For additional {IDF_TARGET_NAME} product related information, please refer to the `documentation `_ section of `Espressif `__ web site. @@ -23,9 +23,9 @@ Webinars and Trainings Mastering the Basics of Espressif Chips: An In-Depth Look at Chip Flashing ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -The content of this webinar is designed for developers, engineers and hobbyists interested in getting a better understanding of how to use esptool.py or other tools for the development with the ESP8266 or ESP32 series of SoCs. +The content of this webinar is designed for developers, engineers and hobbyists interested in getting a better understanding of how to use esptool or other tools for the development with the ESP8266 or ESP32 series of SoCs. -It offers an in-depth look at the inner mechanisms of esptool.py, including the :ref:`boot-mode` process. +It offers an in-depth look at the inner mechanisms of esptool, including the :ref:`boot-mode` process. .. image:: https://img.youtube.com/vi/zh-Y_s4X6zs/maxresdefault.jpg :alt: Mastering the Basics of Espressif Chips: An In-Depth Look at Chip Flashing @@ -33,7 +33,7 @@ It offers an in-depth look at the inner mechanisms of esptool.py, including the DevCon22: esptool.py: Espressif's Swiss Army Knife ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -This talk aims to show how simple, yet powerful, esptool.py is, and how to use it to tame your ESP. +This talk aims to show how simple, yet powerful, esptool is, and how to use it to tame your ESP. .. image:: https://img.youtube.com/vi/GjWGKzu3XTk/maxresdefault.jpg :alt: DevCon22: esptool.py: Espressif's Swiss Army Knife diff --git a/tools/esptool_py/docs/en/troubleshooting.rst b/tools/esptool_py/docs/en/troubleshooting.rst index 5f6fbead0f..19276d9d2d 100644 --- a/tools/esptool_py/docs/en/troubleshooting.rst +++ b/tools/esptool_py/docs/en/troubleshooting.rst @@ -1,4 +1,4 @@ -{IDF_TARGET_BOOTLOADER_OFFSET:default="0x0", esp32="0x1000", esp32s2="0x1000", esp32p4="0x2000"} +{IDF_TARGET_BOOTLOADER_OFFSET:default="0x0", esp32="0x1000", esp32s2="0x1000", esp32p4="0x2000", esp32c5="0x2000"} .. _troubleshooting: @@ -31,12 +31,12 @@ Power stability problems may also cause this (see `Insufficient Power`_.) Writing to Flash Succeeds but Program Doesn't Run ------------------------------------------------- -If esptool can flash your module with ``write_flash`` but your program doesn't run, check the following: +If esptool can flash your module with ``write-flash`` but your program doesn't run, check the following: Wrong Flash Mode ^^^^^^^^^^^^^^^^ -Some devices only support the ``dio`` flash mode. Writing to flash with ``qio`` mode will succeed but the chip can't read the flash back to run - so nothing happens on boot. Try passing the ``-fm dio`` option to ``write_flash``. +Some devices only support the ``dio`` flash mode. Writing to flash with ``qio`` mode will succeed but the chip can't read the flash back to run - so nothing happens on boot. Try passing the ``-fm dio`` option to ``write-flash``. See the :ref:`spi-flash-modes` page for a full description of the flash modes and how to determine which ones are supported on your device. @@ -113,7 +113,7 @@ Early Stage Crash If the application accidentally reconfigures the USB peripheral pins or disables the USB peripheral, the device disappears from the system. You can also encounter unstable flashing or errors like ``OSError: [Errno 71] Protocol error``. - If that happens, try to :ref:`manually enter the download mode ` and then use the :ref:`erase_flash ` command to wipe the flash memory. Then, make sure to fix the issue in the application before flashing again. + If that happens, try to :ref:`manually enter the download mode ` and then use the :ref:`erase-flash ` command to wipe the flash memory. Then, make sure to fix the issue in the application before flashing again. On boards with two USB ports (usually marked as USB and UART), you can use the USB port for flashing while listening on the UART port for debugging purposes. This setup is useful for retrieving core dumps or the reset reason in the event of a crash. To implement this, connect the UART port to another instance of any of the `serial terminal programs`_, while repeating the failing action over the USB port. You'll be able to monitor the crash log without interference from the USB port used for communication or it disappearing due to a firmware crash. If your devkit doesn't have a dedicated USB port connected to an on-board USB-to-UART bridge, you can use a separate adapter to connect to the UART pins on the board. @@ -131,14 +131,14 @@ For exact serial port configuration values, see :ref:`serial-port-settings`. Tracing Esptool Interactions ---------------------------- -Running ``esptool.py --trace`` will dump all serial interactions to the standard output (this is *a lot* of output). This can be helpful when debugging issues with the serial connection, or when providing information for bug reports. +Running ``esptool --trace`` will dump all serial interactions to the standard output (this is *a lot* of output). This can be helpful when debugging issues with the serial connection, or when providing information for bug reports. See :ref:`the related Advanced Topics page ` for more information. Configuration File ------------------ -Although ``esptool.py`` has been tuned to work in the widest possible range of environments, an incompatible combination of hardware, OS, and drivers might cause it to fail. If you suspect this is the case, a custom configuration of internal variables might be necessary. +Although ``esptool`` has been tuned to work in the widest possible range of environments, an incompatible combination of hardware, OS, and drivers might cause it to fail. If you suspect this is the case, a custom configuration of internal variables might be necessary. These variables and options can be specified in a configuration file. See :ref:`the related Configuration File page ` for more information. @@ -190,16 +190,16 @@ Other things to try: .. list:: - * Try to sync and communicate at a much lower baud rate, e.g. ``esptool.py --baud 9600 ...``. - * Try `tracing the interactions `_ running ``esptool.py --trace ...`` and see if anything is received back at all. - * Try skipping chip autodetection by specifying the chip type, run ``esptool.py --chip {IDF_TARGET_NAME} ...``. + * Try to sync and communicate at a much lower baud rate, e.g. ``esptool --baud 9600 ...``. + * Try `tracing the interactions `_ running ``esptool --trace ...`` and see if anything is received back at all. + * Try skipping chip autodetection by specifying the chip type, run ``esptool --chip {IDF_TARGET_NAME} ...``. If none of the above mentioned fixes help and your problem persists, please `open a new issue `_. A serial exception error occurred ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -``esptool.py`` uses the `pySerial `_ Python module for accessing the serial port. +``esptool`` uses the `pySerial `_ Python module for accessing the serial port. If pySerial cannot operate normally, it raises an error and terminates. An example of a pySerial error: @@ -208,7 +208,7 @@ An example of a pySerial error: A serial exception error occurred: read failed: [Errno 6] Device not configured -Errors originating from pySerial are, therefore, not a problem with ``esptool.py``, but are usually caused by a problem with hardware or drivers. +Errors originating from pySerial are, therefore, not a problem with ``esptool``, but are usually caused by a problem with hardware or drivers. Some of the most common pySerial error causes are: diff --git a/tools/esptool_py/docs/en/versions.rst b/tools/esptool_py/docs/en/versions.rst index 648701e066..a0c385fde8 100644 --- a/tools/esptool_py/docs/en/versions.rst +++ b/tools/esptool_py/docs/en/versions.rst @@ -3,7 +3,7 @@ Versions ======== -Starting from ``v4.0.0``, ``esptool.py`` adopts the `semantic versioning specification `_, following the ``MAJOR.MINOR.PATCH`` version number. +Starting from ``v4.0.0``, ``esptool`` adopts the `semantic versioning specification `_, following the ``MAJOR.MINOR.PATCH`` version number. Major release ``v4`` is under active development, receiving new features and bugfixes, while ``v3`` only keeps receiving important bugfixes. @@ -11,38 +11,38 @@ There are no support periods defined and bugfixes are not planned, therefore it .. note:: - The following information is directed mainly towards package maintainers. Regular users should always use the most recent version of ``esptool.py`` to benefit from the latest features and bugfixes. + The following information is directed mainly towards package maintainers. Regular users should always use the most recent version of ``esptool`` to benefit from the latest features and bugfixes. -Use the latest esptool (recommended) +Use the Latest Esptool (Recommended) ------------------------------------ -If your use case doesn't impose any constraints on ``esptool.py``, the latest release should be always used. +If your use case doesn't impose any constraints on ``esptool``, the latest release should be always used. To see the latest available version and its release notes, visit the `release page on GitHub `_. To get the latest possible version, simply define your dependency as ``esptool`` (without any release operator and a version identifier). -Use the latest bugfix release of a minor esptool release +Use the Latest Bugfix Release of a Minor Esptool Release -------------------------------------------------------- -Some use cases might require a specific ``esptool.py`` version without getting new features, but with automatic bugfixes. +Some use cases might require a specific ``esptool`` version without getting new features, but with automatic bugfixes. This can be achieved by defining your dependency as ``esptool~=4.0.1`` (explicitly stating the ``MAJOR``, ``MINOR``, and ``PATCH`` numbers). -This notation selects the latest version of ``esptool.py``, greater than or equal to ``v4.0.1``, but still in the ``v4.0.*`` version (this compatible release clause is approximately equivalent to the pair of comparison clauses ``>= 4.0.1``, ``== 4.0.*``). +This notation selects the latest version of ``esptool``, greater than or equal to ``v4.0.1``, but still in the ``v4.0.*`` version (this compatible release clause is approximately equivalent to the pair of comparison clauses ``>= 4.0.1``, ``== 4.0.*``). So, for example, ``v4.1.0`` won't be downloaded. More information about compatible release clauses `can be found here `_. -Use the latest esptool without any future breaking change +Use the Latest Esptool Without Any Future Breaking Change --------------------------------------------------------- If you also want to get new features (instead of just bugfixes), define your version requirement as ``esptool~=4.0`` (explicitly stating only the ``MAJOR`` and ``MINOR`` numbers). This way the latest minor versions (``>= 4.0``, ``== 4.*``) are automatically installed. -Backward-compatibility is still ensured, because ``esptool.py`` respects the semantic versioning specification (which states that breaking changes should occur only in ``MAJOR`` versions). +Backward-compatibility is still ensured, because ``esptool`` respects the semantic versioning specification (which states that breaking changes should occur only in ``MAJOR`` versions). -Use the previous major esptool release (only if you cannot upgrade) +Use the Previous Major Esptool Release (Only if You Cannot Upgrade) ------------------------------------------------------------------- -If your use case is not compatible with the latest ``MAJOR`` release of ``esptool.py``, a previous compatible version has to be specified. +If your use case is not compatible with the latest ``MAJOR`` release of ``esptool``, a previous compatible version has to be specified. This can be achieved by defining your dependency as ``esptool~=3.0`` (explicitly stating your desired ``MAJOR`` number and at least also the ``MINOR`` number, ``PATCH`` can also be specified). -Use a specific esptool release +Use a Specific Esptool Release ------------------------------ If a very specific release is required, define your dependency as ``esptool==4.1.2``. This specific version will be used and no new features or bugfixes will be automatically installed. diff --git a/tools/esptool_py/docs/requirements.txt b/tools/esptool_py/docs/requirements.txt deleted file mode 100644 index 948dda348e..0000000000 --- a/tools/esptool_py/docs/requirements.txt +++ /dev/null @@ -1 +0,0 @@ -esp-docs~=1.5 diff --git a/tools/esptool_py/esp_rfc2217_server.py b/tools/esptool_py/esp_rfc2217_server.py old mode 100755 new mode 100644 index 4d97247592..b03a684951 --- a/tools/esptool_py/esp_rfc2217_server.py +++ b/tools/esptool_py/esp_rfc2217_server.py @@ -26,7 +26,7 @@ # Esptool can connect to the ESP device through that server as it is # demonstrated in the following example: # -# esptool.py --port rfc2217://localhost:4000?ign_set_control flash_id +# esptool --port rfc2217://localhost:4000?ign_set_control flash-id # import contextlib diff --git a/tools/esptool_py/esp_rfc2217_server/__init__.py b/tools/esptool_py/esp_rfc2217_server/__init__.py index 4c489ec284..c5bc3a697f 100644 --- a/tools/esptool_py/esp_rfc2217_server/__init__.py +++ b/tools/esptool_py/esp_rfc2217_server/__init__.py @@ -18,9 +18,11 @@ import serial from esp_rfc2217_server.redirector import Redirector +from esptool.util import check_deprecated_py_suffix def main(): + check_deprecated_py_suffix("esp_rfc2217_server") import argparse parser = argparse.ArgumentParser( @@ -64,8 +66,7 @@ def main(): level = (logging.WARNING, logging.INFO, logging.DEBUG, logging.NOTSET)[ args.verbosity ] - logging.basicConfig(level=logging.INFO) - # logging.getLogger('root').setLevel(logging.INFO) + logging.basicConfig(format="%(levelname)s: %(message)s", level=logging.INFO) logging.getLogger("rfc2217").setLevel(level) # connect to serial port @@ -75,26 +76,45 @@ def main(): ser.dtr = False ser.rts = False - logging.info(" RFC 2217 TCP/IP to Serial redirector - type Ctrl-C / BREAK to quit") + logging.info("RFC 2217 TCP/IP to Serial redirector - type Ctrl-C / BREAK to quit") try: ser.open() except serial.SerialException as e: - logging.error(" Could not open serial port {}: {}".format(ser.name, e)) + logging.error(f"Could not open serial port {ser.name}: {e}") sys.exit(1) - logging.info(" Serving serial port: {}".format(ser.name)) + logging.info(f"Serving serial port: {ser.name}") settings = ser.get_settings() srv = socket.socket(socket.AF_INET, socket.SOCK_STREAM) srv.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) srv.bind(("", args.localport)) srv.listen(1) - logging.info(" TCP/IP port: {}".format(args.localport)) + logging.info(f"TCP/IP port: {args.localport}") + + host_ip = socket.gethostbyname(socket.gethostname()) + wait_msg = ( + "Waiting for connection ... use the 'rfc2217://" + f"{host_ip}:{args.localport}?ign_set_control' as a PORT" + ) + logging.info(wait_msg) + while True: + srv.settimeout(5) + client_socket = None + try: + while client_socket is None: + try: + client_socket, addr = srv.accept() + except TimeoutError: + print(".", end="", flush=True) + except KeyboardInterrupt: + print("") # resetting inline print + logging.info("Exited with keyboard interrupt") + break try: - client_socket, addr = srv.accept() - logging.info("Connected by {}:{}".format(addr[0], addr[1])) + logging.info(f"Connected by {addr[0]}:{addr[1]}") client_socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1) ser.rts = True ser.dtr = True @@ -112,7 +132,7 @@ def main(): # capable client) ser.apply_settings(settings) except KeyboardInterrupt: - sys.stdout.write("\n") + print(flush=True) break except socket.error as msg: logging.error(str(msg)) diff --git a/tools/esptool_py/esp_rfc2217_server/esp_port_manager.py b/tools/esptool_py/esp_rfc2217_server/esp_port_manager.py index 29f1046384..b5614af6f4 100644 --- a/tools/esptool_py/esp_rfc2217_server/esp_port_manager.py +++ b/tools/esptool_py/esp_rfc2217_server/esp_port_manager.py @@ -74,11 +74,15 @@ def _hard_reset_thread(self): """ if self.logger: self.logger.info("Activating hard reset in thread") - HardReset(self.serial)() + cfg_custom_hard_reset_sequence = cfg.get("custom_hard_reset_sequence") + if cfg_custom_hard_reset_sequence is not None: + CustomReset(self.serial, cfg_custom_hard_reset_sequence)() + else: + HardReset(self.serial)() def _reset_thread(self): """ - The reset logic is used from esptool.py because the RTS and DTR signals + The reset logic is used from esptool because the RTS and DTR signals cannot be retransmitted through RFC 2217 with proper timing. """ if self.logger: diff --git a/tools/esptool_py/espefuse/__init__.py b/tools/esptool_py/espefuse/__init__.py index 518e96566e..31e8998a2f 100755 --- a/tools/esptool_py/espefuse/__init__.py +++ b/tools/esptool_py/espefuse/__init__.py @@ -1,365 +1,231 @@ -# SPDX-FileCopyrightText: 2016-2022 Espressif Systems (Shanghai) CO LTD +# SPDX-FileCopyrightText: 2016-2025 Espressif Systems (Shanghai) CO LTD # # SPDX-License-Identifier: GPL-2.0-or-later -# PYTHON_ARGCOMPLETE_OK -import argparse -import os import sys -from collections import namedtuple -from io import StringIO -import espefuse.efuse.esp32 as esp32_efuse -import espefuse.efuse.esp32c2 as esp32c2_efuse -import espefuse.efuse.esp32c3 as esp32c3_efuse -import espefuse.efuse.esp32c5 as esp32c5_efuse -import espefuse.efuse.esp32c5beta3 as esp32c5beta3_efuse -import espefuse.efuse.esp32c6 as esp32c6_efuse -import espefuse.efuse.esp32c61 as esp32c61_efuse -import espefuse.efuse.esp32h2 as esp32h2_efuse -import espefuse.efuse.esp32h2beta1 as esp32h2beta1_efuse -import espefuse.efuse.esp32p4 as esp32p4_efuse -import espefuse.efuse.esp32s2 as esp32s2_efuse -import espefuse.efuse.esp32s3 as esp32s3_efuse -import espefuse.efuse.esp32s3beta2 as esp32s3beta2_efuse +import rich_click as click import esptool - -DefChip = namedtuple("DefChip", ["chip_name", "efuse_lib", "chip_class"]) - -SUPPORTED_BURN_COMMANDS = [ - "read_protect_efuse", - "write_protect_efuse", - "burn_efuse", - "burn_block_data", - "burn_bit", - "burn_key", - "burn_key_digest", - "burn_custom_mac", - "set_flash_voltage", - "execute_scripts", +from esptool.cli_util import ChipType, ResetModeType +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, + SUPPORTED_BURN_COMMANDS, + SUPPORTED_READ_COMMANDS, + SUPPORTED_CHIPS, +) +from esptool.util import check_deprecated_py_suffix + +__all__ = [ + "get_esp", + "init_commands", + "BaseCommands", + "SUPPORTED_COMMANDS", + "SUPPORTED_CHIPS", + "SUPPORTED_BURN_COMMANDS", + "SUPPORTED_READ_COMMANDS", ] -SUPPORTED_READ_COMMANDS = [ - "summary", - "dump", - "get_custom_mac", - "adc_info", - "check_error", -] -SUPPORTED_COMMANDS = SUPPORTED_READ_COMMANDS + SUPPORTED_BURN_COMMANDS - -SUPPORTED_CHIPS = { - "esp32": DefChip("ESP32", esp32_efuse, esptool.targets.ESP32ROM), - "esp32c2": DefChip("ESP32-C2", esp32c2_efuse, esptool.targets.ESP32C2ROM), - "esp32c3": DefChip("ESP32-C3", esp32c3_efuse, esptool.targets.ESP32C3ROM), - "esp32c6": DefChip("ESP32-C6", esp32c6_efuse, esptool.targets.ESP32C6ROM), - "esp32c61": DefChip("ESP32-C61", esp32c61_efuse, esptool.targets.ESP32C61ROM), - "esp32c5": DefChip("ESP32-C5", esp32c5_efuse, esptool.targets.ESP32C5ROM), - "esp32c5beta3": DefChip( - "ESP32-C5(beta3)", esp32c5beta3_efuse, esptool.targets.ESP32C5BETA3ROM - ), - "esp32h2": DefChip("ESP32-H2", esp32h2_efuse, esptool.targets.ESP32H2ROM), - "esp32p4": DefChip("ESP32-P4", esp32p4_efuse, esptool.targets.ESP32P4ROM), - "esp32h2beta1": DefChip( - "ESP32-H2(beta1)", esp32h2beta1_efuse, esptool.targets.ESP32H2BETA1ROM +@click.group( + cls=Group, + chain=True, # allow using multiple commands in a single run + no_args_is_help=True, + context_settings=dict(help_option_names=["-h", "--help"], max_content_width=120), + help=f"espefuse v{esptool.__version__} - " + "Utility for eFuse configuration in Espressif SoCs.", +) +@click.option( + "--chip", + "-c", + type=ChipType(choices=["auto"] + list(SUPPORTED_CHIPS.keys())), + default="auto", + envvar="ESPTOOL_CHIP", + help="Target chip type.", +) +@click.option( + "--baud", + "-b", + type=int, + default=esptool.ESPLoader.ESP_ROM_BAUD, + envvar="ESPTOOL_BAUD", + help="Serial port baud rate used when flashing/reading.", +) +@click.option( + "--port", + "-p", + envvar="ESPTOOL_PORT", + type=click.Path(), + help="Serial port device.", +) +@click.option( + "--before", + type=ResetModeType( + choices=["default-reset", "usb-reset", "no-reset", "no-reset-no-sync"] ), - "esp32s2": DefChip("ESP32-S2", esp32s2_efuse, esptool.targets.ESP32S2ROM), - "esp32s3": DefChip("ESP32-S3", esp32s3_efuse, esptool.targets.ESP32S3ROM), - "esp32s3beta2": DefChip( - "ESP32-S3(beta2)", esp32s3beta2_efuse, esptool.targets.ESP32S3BETA2ROM - ), -} - - -def get_esp( - port, + default="default-reset", + help="Which reset to perform before connecting to the chip.", +) +@click.option( + "--debug", "-d", is_flag=True, help="Show debugging information (loglevel=DEBUG)." +) +@click.option( + "--virt", + is_flag=True, + help="For host tests, work in virtual mode (no chip connection).", +) +@click.option( + "--path-efuse-file", + type=click.Path(), + help="For host tests, save eFuse memory to file.", +) +@click.option( + "--do-not-confirm", + is_flag=True, + help="Do not pause for confirmation before permanently writing eFuses. " + "Use with caution!", +) +@click.option( + "--postpone", + is_flag=True, + help="Postpone burning some eFuses from BLOCK0 at the end.", +) +@click.option( + "--extend-efuse-table", + type=click.File("r"), + help="CSV file from ESP-IDF (esp_efuse_custom_table.csv).", +) +@click.pass_context +def cli( + ctx, + chip, baud, - connect_mode, - chip="auto", - skip_connect=False, - virt=False, - debug=False, - virt_efuse_file=None, -): - if chip not in ["auto"] + list(SUPPORTED_CHIPS.keys()): - raise esptool.FatalError("get_esp: Unsupported chip (%s)" % chip) - if virt: - efuse = SUPPORTED_CHIPS.get(chip, SUPPORTED_CHIPS["esp32"]).efuse_lib - esp = efuse.EmulateEfuseController(virt_efuse_file, debug) - else: - if chip == "auto" and not skip_connect: - esp = esptool.cmds.detect_chip(port, baud, connect_mode) - else: - esp = SUPPORTED_CHIPS.get(chip, SUPPORTED_CHIPS["esp32"]).chip_class( - port if not skip_connect else StringIO(), baud - ) - if not skip_connect: - esp.connect(connect_mode) - if esp.sync_stub_detected: - esp = esp.STUB_CLASS(esp) - return esp - - -def get_efuses( - esp, - skip_connect=False, - debug_mode=False, - do_not_confirm=False, - extend_efuse_table=None, + port, + before, + debug, + virt, + path_efuse_file, + do_not_confirm, + postpone, + extend_efuse_table, ): - for name in SUPPORTED_CHIPS: - if SUPPORTED_CHIPS[name].chip_name == esp.CHIP_NAME: - efuse = SUPPORTED_CHIPS[name].efuse_lib - return ( - efuse.EspEfuses( - esp, skip_connect, debug_mode, do_not_confirm, extend_efuse_table - ), - efuse.operations, - ) - else: - raise esptool.FatalError("get_efuses: Unsupported chip (%s)" % esp.CHIP_NAME) - - -def split_on_groups(all_args): - """ - This function splits the all_args list into groups, - where each item is a cmd with all its args. - - Example: - all_args: - ['burn_key_digest', 'secure_images/ecdsa256_secure_boot_signing_key_v2.pem', - 'burn_key', 'BLOCK_KEY0', 'images/efuse/128bit_key', - 'XTS_AES_128_KEY_DERIVED_FROM_128_EFUSE_BITS'] - - used_cmds: ['burn_key_digest', 'burn_key'] - groups: - [['burn_key_digest', 'secure_images/ecdsa256_secure_boot_signing_key_v2.pem'], - ['burn_key', 'BLOCK_KEY0', 'images/efuse/128bit_key', - 'XTS_AES_128_KEY_DERIVED_FROM_128_EFUSE_BITS']] - """ - - groups = [] - cmd = [] - used_cmds = [] - for item in all_args: - if item in SUPPORTED_COMMANDS: - used_cmds.append(item) - if cmd != []: - groups.append(cmd) - cmd = [] - cmd.append(item) - if cmd: - groups.append(cmd) - return groups, used_cmds - - -def main(custom_commandline=None, esp=None): - """ - Main function for espefuse - - custom_commandline - Optional override for default arguments parsing - (that uses sys.argv), can be a list of custom arguments as strings. - Arguments and their values need to be added as individual items to the list - e.g. "--port /dev/ttyUSB1" thus becomes ['--port', '/dev/ttyUSB1']. - - esp - Optional override of the connected device previously - returned by esptool.get_default_connected_device() - """ + log.print(f"espefuse v{esptool.__version__}") + ctx.ensure_object(dict) + 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", []) - init_parser = argparse.ArgumentParser( - description="espefuse.py v%s - [ESP32xx] efuse get/set tool" - % esptool.__version__, - prog="espefuse", - add_help=False, - ) - - init_parser.add_argument( - "--chip", - "-c", - help="Target chip type", - choices=["auto"] + list(SUPPORTED_CHIPS.keys()), - default=os.environ.get("ESPTOOL_CHIP", "auto"), - ) - - init_parser.add_argument( - "--baud", - "-b", - help="Serial port baud rate used when flashing/reading", - type=esptool.arg_auto_int, - default=os.environ.get("ESPTOOL_BAUD", esptool.loader.ESPLoader.ESP_ROM_BAUD), - ) + if any(cmd.replace("_", "-") in DEPRECATED_COMMANDS for cmd in used_cmds): + return # do not connect to ESP if any command is deprecated - init_parser.add_argument( - "--port", - "-p", - help="Serial port device", - default=os.environ.get("ESPTOOL_PORT", esptool.loader.ESPLoader.DEFAULT_PORT), - ) - - init_parser.add_argument( - "--before", - help="What to do before connecting to the chip", - choices=["default_reset", "usb_reset", "no_reset", "no_reset_no_sync"], - default="default_reset", - ) + if not port and not external_esp and not is_help and not virt: + raise click.BadOptionUsage( + "--port", "Missing required argument. Please specify the --port option." + ) - init_parser.add_argument( - "--debug", - "-d", - help="Show debugging information (loglevel=DEBUG)", - action="store_true", - ) - init_parser.add_argument( - "--virt", - help="For host tests, the tool will work in the virtual mode " - "(without connecting to a chip).", - action="store_true", - ) - init_parser.add_argument( - "--path-efuse-file", - help="For host tests, saves efuse memory to file.", - type=str, - default=None, - ) - init_parser.add_argument( - "--do-not-confirm", - help="Do not pause for confirmation before permanently writing efuses. " - "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", - ) - init_parser.add_argument( - "--extend-efuse-table", - help="CSV file from ESP-IDF (esp_efuse_custom_table.csv)", - type=argparse.FileType("r"), - default=None, - ) - - common_args, remaining_args = init_parser.parse_known_args(custom_commandline) - debug_mode = common_args.debug - just_print_help = [ - True for arg in remaining_args if arg in ["--help", "-h"] - ] or remaining_args == [] - - print("espefuse.py v{}".format(esptool.__version__)) - - if not external_esp: + if not esp: try: esp = get_esp( - common_args.port, - common_args.baud, - common_args.before, - common_args.chip, - just_print_help, - common_args.virt, - common_args.debug, - common_args.path_efuse_file, + port, baud, before, chip, is_help, virt, debug, path_efuse_file ) except esptool.FatalError as e: raise esptool.FatalError( - f"{e}\nPlease make sure that you have specified " - "the right port with the --port argument" + f"{e}\nPlease make sure you specified the right port with --port." ) - # TODO: Require the --port argument in the next major release, ESPTOOL-490 - - efuses, efuse_operations = get_efuses( - esp, - just_print_help, - debug_mode, - common_args.do_not_confirm, - common_args.extend_efuse_table, - ) - - parser = argparse.ArgumentParser(parents=[init_parser]) - subparsers = parser.add_subparsers( - dest="operation", help="Run espefuse.py {command} -h for additional help" - ) - - efuse_operations.add_commands(subparsers, efuses) - # Enable argcomplete only on Unix-like systems - if sys.platform != "win32": - try: - import argcomplete + def close_port(): + if not external_esp and not virt and esp._port: + esp._port.close() - argcomplete.autocomplete(parser) - except ImportError: - pass + ctx.call_on_close(close_port) - grouped_remaining_args, used_cmds = split_on_groups(remaining_args) - if len(grouped_remaining_args) == 0: - parser.print_help() - parser.exit(1) - there_are_multiple_burn_commands_in_args = ( - sum(cmd in SUPPORTED_BURN_COMMANDS for cmd in used_cmds) > 1 + # handle chip auto + if chip == "auto": + if ctx.obj.get("is_help", False): + log.note( + "Chip not specified, showing commands for ESP32 by default. " + "Specify the --chip option to get chip-specific help." + ) + chip = esp.CHIP_NAME.lower() + + commands = init_commands( + esp=esp, + skip_connect=is_help, + debug=debug, + do_not_confirm=do_not_confirm, + extend_efuse_table=extend_efuse_table, ) - if there_are_multiple_burn_commands_in_args: - efuses.batch_mode_cnt += 1 + commands.efuses.postpone = postpone + commands.add_cli_commands(cli) - 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) - if args.operation is None: - parser.print_help() - parser.exit(1) - assert ( - len(unused_args) == 0 - ), 'Not all commands were recognized "{}"'.format(unused_args) - - operation_func = vars(efuse_operations)[args.operation] - # each 'operation' is a module-level function of the same name - print('\n=== Run "{}" command ==='.format(args.operation)) + multiple_burn_commands = ( + sum(cmd.replace("_", "-") in SUPPORTED_BURN_COMMANDS for cmd in used_cmds) > 1 + ) + if multiple_burn_commands: + commands.use_batch_mode() + + # Add the objects to the context + ctx.obj["debug"] = debug + ctx.obj["commands"] = commands + ctx.obj["efuses"] = commands.efuses + ctx.obj["do_not_confirm"] = do_not_confirm + + @cli.result_callback() + def process_result(result, *args, **kwargs): + if multiple_burn_commands: + if not commands.burn_all(check_batch_mode=True): + raise esptool.FatalError("BURN was not done.") + log.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) - if hasattr(args, "show_sensitive_info"): - if args.show_sensitive_info or args.debug: - args.show_sensitive_info = True - else: - print("Sensitive data will be hidden (see --show-sensitive-info)") - operation_func(esp, efuses, args) +def main(argv: list[str] | None = None, esp: esptool.ESPLoader | None = None): + """ + Main function for espefuse - if there_are_multiple_burn_commands_in_args: - efuses.batch_mode_cnt -= 1 - if not efuses.burn_all(check_batch_mode=True): - raise esptool.FatalError("BURN was not done") - print("Successful") + argv - Optional override for default arguments parsing + (that uses sys.argv), can be a list of custom arguments as strings. + Arguments and their values need to be added as individual items to the list + e.g. "--port /dev/ttyUSB1" thus becomes ['--port', '/dev/ttyUSB1']. - if ( - sum(cmd in SUPPORTED_BURN_COMMANDS for cmd in used_cmds) > 0 - and sum(cmd in SUPPORTED_READ_COMMANDS for cmd in used_cmds) > 0 - ): - # [burn_cmd1] [burn_cmd2] [read_cmd1] [burn_cmd3] [read_cmd2] - print("\n=== Run read commands after burn commands ===") - for rem_args in grouped_remaining_args: - args, unused_args = parser.parse_known_args( - rem_args, namespace=common_args - ) - current_cmd = args.operation - if current_cmd in SUPPORTED_READ_COMMANDS: - print(f"\n=== Run {args.operation} command ===") - operation_func = vars(efuse_operations)[current_cmd] - operation_func(esp, efuses, args) - finally: - if not external_esp and not common_args.virt and esp._port: - esp._port.close() + esp - Optional override of the connected device previously + returned by esptool.get_default_connected_device() + """ + args = esptool.expand_file_arguments(argv or sys.argv[1:]) + cli(args=args, esp=esp) def _main(): + check_deprecated_py_suffix(__name__) try: main() except esptool.FatalError as e: - print("\nA fatal error occurred: %s" % e) + log.error(f"\nA fatal error occurred: {e}") + sys.exit(2) + except KeyboardInterrupt: + log.error("KeyboardInterrupt: Run cancelled by user.") sys.exit(2) diff --git a/tools/esptool_py/espefuse/cli_util.py b/tools/esptool_py/espefuse/cli_util.py new file mode 100644 index 0000000000..e2ee6d97f0 --- /dev/null +++ b/tools/esptool_py/espefuse/cli_util.py @@ -0,0 +1,246 @@ +# SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD +# +# SPDX-License-Identifier: GPL-2.0-or-later + +from typing import Any + +import rich_click as click +from click.parser import OptionParser, ParsingState, _unpack_args +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, + SUPPORTED_COMMANDS, +) + + +click.rich_click.USE_CLICK_SHORT_HELP = True +click.rich_click.COMMAND_GROUPS = { + "*": [ + { + "name": "Burn commands", + "commands": SUPPORTED_BURN_COMMANDS, + }, + { + "name": "Read commands", + "commands": SUPPORTED_READ_COMMANDS, + }, + ] +} + + +class ChainParser(OptionParser): + """ + This is a modified version of the OptionParser class from click.parser. + It allows for the processing of arguments and options in interspersed order + together with chaining commands. + """ + + def _process_args_for_options(self, state: ParsingState) -> None: + while state.rargs: + arg = state.rargs.pop(0) + arglen = len(arg) + # Double dashes always handled explicitly regardless of what + # prefixes are valid. + if arg == "--": + return + # if the argument is a command, stop parsing options + elif arg.replace("_", "-") in SUPPORTED_COMMANDS: + state.largs.append(arg) + return + elif arg[:1] in self._opt_prefixes and arglen > 1: + self._process_opts(arg, state) + elif self.allow_interspersed_args: + state.largs.append(arg) + else: + state.rargs.insert(0, arg) + return + + def _process_args_for_args(self, state: ParsingState) -> None: + pargs, args = _unpack_args( + state.largs + state.rargs, [x.nargs for x in self._args] + ) + + # This check is required because of the way we modify nargs in ChainingCommand + if len(pargs) > 0: + for idx, arg in enumerate(self._args): + arg.process(pargs[idx], state) + + state.largs = args + state.rargs = [] + + +class EfuseContext(click.RichContext): + @property + def show_sensitive_info(self) -> bool: + self.ensure_object(dict) + value: bool = self.obj.get("show_sensitive_info", False) + if not value: + log.print("Sensitive data will be hidden (see --show-sensitive-info)") + return value + + +class ChainingCommand(click.RichCommand, click.Command): + context_class = EfuseContext + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + + def _is_option(self, arg: str) -> bool: + return arg.startswith("--") or arg.startswith("-") + + def invoke(self, ctx: click.Context) -> Any: + log.print(f'\n=== Run "{self.name}" command ===') + return super().invoke(ctx) + + def parse_args(self, ctx: click.Context, args: list[str]): + # This is a hack to set nargs of the last argument to the number of arguments + # that will be processed separately + param_changed = None + for idx, arg in enumerate(args): + # command found in args or option found after argument + if arg.replace("_", "-") in SUPPORTED_COMMANDS or ( + self._is_option(arg) and idx > 0 + ): + arguments_count = sum( + isinstance(param, click.Argument) for param in self.params + ) + for param in self.params: + if param.nargs != -1: + continue + # set nargs of parameter to actual count of arguments and deduct + # arguments_count as each argument will be processed separately, + # we only care about the last one with nargs=-1 + # at the end we add 1 to account for the processedargument itself + # e.g. if we have burn-bit BLOCK2 1 2 3, we want to set nargs to 3, + # so we need to account for BLOCK2 being processed separately + param.nargs = args.index(arg) - arguments_count + 1 + param_changed = param + if ( + param.nargs == 0 + and param.required + and not ctx.resilient_parsing + ): + raise click.UsageError( + f"Command `{self.name}` requires the `{param.name}` " + "argument." + ) + break + break + ret = super().parse_args(ctx, args) + # restore nargs of the last argument to -1, in case it is going to be used again + if param_changed is not None: + param.nargs = -1 + return ret + + def make_parser(self, ctx: click.Context) -> OptionParser: + """Creates the underlying option parser for this command.""" + parser = ChainParser(ctx) + parser.allow_interspersed_args = True + for param in self.get_params(ctx): + param.add_to_parser(parser, ctx) + return parser + + +class Group(EsptoolGroup): + DEPRECATED_OPTIONS = { + "--file_name": "--file-name", + } + + command_class = ChainingCommand + context_class = EfuseContext + + @staticmethod + def _split_to_groups(args: list[str]) -> tuple[list[list[str]], list[str]]: + """ + This function splits the args list into groups, + where each item is a cmd with all its args. + + Example: + all_args: + ['burn-key-digest', 'secure_images/ecdsa256_secure_boot_signing_key_v2.pem', + 'burn-key', 'BLOCK_KEY0', 'images/efuse/128bit_key', + 'XTS_AES_128_KEY_DERIVED_FROM_128_EFUSE_BITS'] + + used_cmds: ['burn-key-digest', 'burn-key'] + groups: + [['burn-key-digest', 'secure_images/ecdsa256_secure_boot_signing_key_v2.pem'], + ['burn-key', 'BLOCK_KEY0', 'images/efuse/128bit_key', + 'XTS_AES_128_KEY_DERIVED_FROM_128_EFUSE_BITS']] + """ + groups: list[list[str]] = [] + args_group: list[str] = [] + used_cmds: list[str] = [] + for arg in args: + if arg.replace("_", "-") in SUPPORTED_COMMANDS: + groups.append(args_group) + used_cmds.append(arg) + args_group = [arg] + else: + args_group.append(arg) + groups.append(args_group) + return groups, used_cmds + + @staticmethod + def repeat_read_commands( + used_cmds: list[str], groups: list[list[str]] + ) -> list[list[str]]: + if ( + sum(cmd in SUPPORTED_BURN_COMMANDS for cmd in used_cmds) > 0 + and sum(cmd in SUPPORTED_READ_COMMANDS for cmd in used_cmds) > 0 + ): + # append all read commands at the end of group + read_commands = [] + for group in groups: + if group[0] in SUPPORTED_READ_COMMANDS: + read_commands.append(group) + groups.extend(read_commands) + return groups + + def parse_args(self, ctx: click.Context, args: list[str]): + ctx.ensure_object(dict) + ctx.obj["is_help"] = any(help_arg in args for help_arg in ctx.help_option_names) + idx = ( + args.index("--chip") + if "--chip" in args + else (args.index("-c") if "-c" in args else -1) + ) + ctx.obj["chip"] = args[idx + 1] if idx != -1 and idx + 1 < len(args) else "auto" + # override the default behavior of EsptoolGroup, because we don't need + # support for parameters with nargs=-1 + args = self._replace_deprecated_args(args) + cmd_groups, used_cmds = self._split_to_groups(args) + + # Add commands for shell completion + if ctx.resilient_parsing: + commands = init_commands(port=None, chip=ctx.obj["chip"], skip_connect=True) + commands.add_cli_commands(self) + elif len(used_cmds) == 0: + self.get_help(ctx) + ctx.exit() + + cmd_groups = self.repeat_read_commands(used_cmds, cmd_groups) + args = [arg for group in cmd_groups for arg in group] + + ctx.obj["used_cmds"] = used_cmds + ctx.obj["args"] = args + return super(click.RichGroup, self).parse_args(ctx, args) + + 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 (set(self.list_commands(ctx)) - set(DEPRECATED_COMMANDS)): + chip = ctx.obj["chip"] + if chip == "auto": + log.note( + "Chip not specified, showing commands for ESP32 by default. " + "Specify the --chip option to get chip-specific help." + ) + chip = "esp32" + commands = init_commands(port=None, chip=chip, skip_connect=True) + commands.add_cli_commands(self) + return super().get_help(ctx) # type: ignore diff --git a/tools/esptool_py/espefuse/efuse/base_fields.py b/tools/esptool_py/espefuse/efuse/base_fields.py index 654d0fd668..ab39b0a757 100644 --- a/tools/esptool_py/espefuse/efuse/base_fields.py +++ b/tools/esptool_py/espefuse/efuse/base_fields.py @@ -4,15 +4,17 @@ # # SPDX-License-Identifier: GPL-2.0-or-later +from abc import abstractmethod import binascii import sys -from bitstring import BitArray, BitStream, CreationError +from bitstring import BitArray, BitStream, Bits, CreationError +from espefuse.efuse.mem_definition_base import EfuseBlocksBase, EfuseRegistersBase import esptool +from esptool.logger import log from . import util -from typing import List class CheckArgValue(object): @@ -26,7 +28,7 @@ def check_arg_value(efuse, new_value): new_value = 1 if new_value is None else int(new_value, 0) if new_value != 1: raise esptool.FatalError( - "New value is not accepted for efuse '{}' " + "New value is not accepted for eFuse '{}' " "(will always burn 0->1), given value={}".format( efuse.name, new_value ) @@ -46,7 +48,7 @@ def check_arg_value(efuse, new_value): else: if new_value is None: raise esptool.FatalError( - "New value required for efuse '{}' (given None)".format( + "New value required for eFuse '{}' (given None)".format( efuse.name ) ) @@ -59,20 +61,20 @@ def check_arg_value(efuse, new_value): elif efuse.efuse_type.startswith("bytes"): if new_value is None: raise esptool.FatalError( - "New value required for efuse '{}' (given None)".format( + "New value required for eFuse '{}' (given None)".format( efuse.name ) ) if len(new_value) * 8 != efuse.bitarray.len: raise esptool.FatalError( - "The length of efuse '{}' ({} bits) " + "The length of eFuse '{}' ({} bits) " "(given len of the new value= {} bits)".format( efuse.name, efuse.bitarray.len, len(new_value) * 8 ) ) else: raise esptool.FatalError( - "The '{}' type for the '{}' efuse is not supported yet.".format( + "The '{}' type for the '{}' eFuse is not supported yet.".format( efuse.efuse_type, efuse.name ) ) @@ -85,6 +87,10 @@ def check_arg_value(efuse, new_value): class EfuseProtectBase(object): # This class is used by EfuseBlockBase and EfuseFieldBase + read_disable_bit: int | list[int] | None + write_disable_bit: int | list[int] | None + parent: "EspEfusesBase" + name: str def get_read_disable_mask(self, blk_part=None): """Returns mask of read protection bits @@ -108,35 +114,48 @@ def get_count_read_disable_bits(self): # On the C2 chip, BLOCK_KEY0 has two read protection bits [0, 1]. return bin(self.get_read_disable_mask()).count("1") - def is_readable(self, blk_part=None): - """Return true if the efuse is readable by software""" + def is_readable(self, blk_part: int | None = None) -> bool: + """Check if the eFuse is readable by software + + Args: + blk_part: The part of the block to check. + If None, check all parts. + + Returns: + bool: True if the eFuse is readable by software + """ num_bit = self.read_disable_bit if num_bit is None: return True # read cannot be disabled - return (self.parent["RD_DIS"].get() & self.get_read_disable_mask(blk_part)) == 0 + return (self.parent["RD_DIS"].get() & self.get_read_disable_mask(blk_part)) == 0 # type: ignore def disable_read(self): num_bit = self.read_disable_bit if num_bit is None: - raise esptool.FatalError("This efuse cannot be read-disabled") + raise esptool.FatalError("This eFuse cannot be read-disabled") if not self.parent["RD_DIS"].is_writeable(): raise esptool.FatalError( - "This efuse cannot be read-disabled due the to RD_DIS field is " + "This eFuse cannot be read-disabled due to the RD_DIS field being " "already write-disabled" ) self.parent["RD_DIS"].save(self.get_read_disable_mask()) - def is_writeable(self): + def is_writeable(self) -> bool: + """Check if the eFuse is writeable by software + + Returns: + bool: True if the eFuse is writeable by software + """ num_bit = self.write_disable_bit if num_bit is None: return True # write cannot be disabled - return (self.parent["WR_DIS"].get() & (1 << num_bit)) == 0 + return (self.parent["WR_DIS"].get() & (1 << num_bit)) == 0 # type: ignore def disable_write(self): num_bit = self.write_disable_bit if not self.parent["WR_DIS"].is_writeable(): raise esptool.FatalError( - "This efuse cannot be write-disabled due to the WR_DIS field is " + "This eFuse cannot be write-disabled due to the WR_DIS field being " "already write-disabled" ) self.parent["WR_DIS"].save(1 << num_bit) @@ -144,7 +163,7 @@ def disable_write(self): def check_wr_rd_protect(self): if not self.is_readable(): error_msg = "\t{} is read-protected.".format(self.name) - "The written value can not be read, the efuse/block looks as all 0.\n" + "The written value can not be read, the eFuse/block looks as all 0.\n" error_msg += "\tBurn in this case may damage an already written value." self.parent.print_error_msg(error_msg) if not self.is_writeable(): @@ -155,26 +174,26 @@ def check_wr_rd_protect(self): class EfuseBlockBase(EfuseProtectBase): - def __init__(self, parent, param, skip_read=False): - self.parent = parent - self.name = param.name - self.alias = param.alias - self.id = param.id - self.rd_addr = param.rd_addr - self.wr_addr = param.wr_addr - self.write_disable_bit = param.write_disable_bit - self.read_disable_bit = param.read_disable_bit - self.len = param.len - self.key_purpose_name = param.key_purpose - bit_block_len = self.get_block_len() * 8 - self.bitarray = BitStream(bit_block_len) + def __init__(self, parent: "EspEfusesBase", param, skip_read: bool = False) -> None: + self.parent: EspEfusesBase = parent + self.name: str = param.name + self.alias: list[str] = param.alias + self.id: int = param.id + self.rd_addr: int = param.rd_addr + self.wr_addr: int = param.wr_addr + self.write_disable_bit: int | None = param.write_disable_bit + self.read_disable_bit: int | None = param.read_disable_bit + self.len: int = param.len + self.key_purpose_name: str | None = param.key_purpose + bit_block_len: int = self.get_block_len() * 8 + self.bitarray: BitStream = BitStream(bit_block_len) self.bitarray.set(0) - self.wr_bitarray = BitStream(bit_block_len) + self.wr_bitarray: BitStream = BitStream(bit_block_len) self.wr_bitarray.set(0) - self.fail = False - self.num_errors = 0 + self.fail: bool = False + self.num_errors: int = 0 if self.id == 0: - self.err_bitarray = BitStream(bit_block_len) + self.err_bitarray: BitStream | None = BitStream(bit_block_len) self.err_bitarray.set(0) else: self.err_bitarray = None @@ -182,6 +201,10 @@ def __init__(self, parent, param, skip_read=False): if not skip_read: self.read() + @abstractmethod + def apply_coding_scheme(self): + pass + def get_block_len(self): coding_scheme = self.get_coding_scheme() if coding_scheme == self.parent.REGS.CODING_SCHEME_NONE: @@ -207,10 +230,10 @@ def get_raw(self, from_read=True): else: return self.wr_bitarray.bytes - def get(self, from_read=True): - self.get_bitstring(from_read=from_read) + def get(self, from_read: bool = True) -> BitStream: + return self.get_bitstring(from_read=from_read) - def get_bitstring(self, from_read=True): + def get_bitstring(self, from_read: bool = True) -> BitStream: if from_read: return self.bitarray else: @@ -240,7 +263,7 @@ def read(self, print_info=True): def print_block(self, bit_string, comment, debug=False): if self.parent.debug or debug: bit_string.pos = 0 - print( + log.print( "%-15s (%-16s) [%-2d] %s:" % (self.name, " ".join(self.alias)[:16], self.id, comment), " ".join( @@ -258,7 +281,7 @@ def check_wr_data(self): if wr_data.all(False): # nothing to burn if self.parent.debug: - print("[{:02}] {:20} nothing to burn".format(self.id, self.name)) + log.print("[{:02}] {:20} nothing to burn".format(self.id, self.name)) return False if len(wr_data.bytes) != len(self.bitarray.bytes): raise esptool.FatalError( @@ -268,7 +291,7 @@ def check_wr_data(self): self.check_wr_rd_protect() if self.get_bitstring().all(False): - print( + log.print( "[{:02}] {:20} is empty, will burn the new value".format( self.id, self.name ) @@ -276,18 +299,18 @@ def check_wr_data(self): else: # the written block in chip is not empty if self.get_bitstring() == wr_data: - print( + log.print( "[{:02}] {:20} is already written the same value, " "continue with EMPTY_BLOCK".format(self.id, self.name) ) wr_data.set(0) else: - print("[{:02}] {:20} is not empty".format(self.id, self.name)) - print("\t(written ):", self.get_bitstring()) - print("\t(to write):", wr_data) + log.print("[{:02}] {:20} is not empty".format(self.id, self.name)) + log.print("\t(written ):", self.get_bitstring()) + log.print("\t(to write):", wr_data) mask = self.get_bitstring() & wr_data if mask == wr_data: - print( + log.print( "\tAll wr_data bits are set in the written block, " "continue with EMPTY_BLOCK." ) @@ -295,49 +318,47 @@ def check_wr_data(self): else: coding_scheme = self.get_coding_scheme() if coding_scheme == self.parent.REGS.CODING_SCHEME_NONE: - print("\t(coding scheme = NONE)") + log.print("\t(coding scheme = NONE)") elif coding_scheme == self.parent.REGS.CODING_SCHEME_RS: - print("\t(coding scheme = RS)") + log.print("\t(coding scheme = RS)") error_msg = ( - "\tBurn into %s is forbidden " - "(RS coding scheme does not allow this)." % (self.name) + f"\tBurn into {self.name} is forbidden " + "(RS coding scheme does not allow this)." ) self.parent.print_error_msg(error_msg) elif coding_scheme == self.parent.REGS.CODING_SCHEME_34: - print("\t(coding scheme = 3/4)") + log.print("\t(coding scheme = 3/4)") data_can_not_be_burn = False for i in range(0, self.get_bitstring().len, 6 * 8): rd_chunk = self.get_bitstring()[i : i + 6 * 8 :] wr_chunk = wr_data[i : i + 6 * 8 :] if rd_chunk.any(True): if wr_chunk.any(True): - print( - "\twritten chunk [%d] and wr_chunk " - "are not empty. " % (i // (6 * 8)), + log.print( + f"\twritten chunk [{i // (6 * 8)}] and wr_chunk" + " are not empty. ", end="", ) if rd_chunk == wr_chunk: - print( + log.print( "wr_chunk == rd_chunk. " "Continue with empty chunk." ) wr_data[i : i + 6 * 8 :].set(0) else: - print("wr_chunk != rd_chunk. Can not burn.") - print("\twritten ", rd_chunk) - print("\tto write", wr_chunk) + log.print("wr_chunk != rd_chunk. Can not burn.") + log.print("\twritten ", rd_chunk) + log.print("\tto write", wr_chunk) data_can_not_be_burn = True if data_can_not_be_burn: error_msg = ( - "\tBurn into %s is forbidden " - "(3/4 coding scheme does not allow this)." % (self.name) + f"\tBurn into {self.name} is forbidden " + "(3/4 coding scheme does not allow this)." ) self.parent.print_error_msg(error_msg) else: raise esptool.FatalError( - "The coding scheme ({}) is not supported".format( - coding_scheme - ) + f"The coding scheme ({coding_scheme}) is not supported." ) def save(self, new_data): @@ -351,16 +372,14 @@ def save(self, new_data): # *[x] - means a byte. data = BitStream(bytes=new_data[::-1], length=len(new_data) * 8) if self.parent.debug: - print( - "\twritten : {} ->\n\tto write: {}".format(self.get_bitstring(), data) - ) + log.print(f"\twritten : {self.get_bitstring()} ->\n\tto write: {data}") self.wr_bitarray.overwrite(self.wr_bitarray | data, pos=0) def burn_words(self, words): for burns in range(3): self.parent.efuse_controller_setup() if self.parent.debug: - print("Write data to BLOCK%d" % (self.id)) + log.print(f"Write data to BLOCK{self.id}") write_reg_addr = self.wr_addr for word in words: # for ep32s2: using EFUSE_PGM_DATA[0..7]_REG for writing data @@ -371,7 +390,7 @@ def burn_words(self, words): # each block has the special regs EFUSE_BLK[0..3]_WDATA[0..7]_REG # for writing data if self.parent.debug: - print("Addr 0x%08x, data=0x%08x" % (write_reg_addr, word)) + log.print(f"Addr {write_reg_addr:10x}, data={word:10x}") self.parent.write_reg(write_reg_addr, word) write_reg_addr += 4 @@ -380,10 +399,9 @@ def burn_words(self, words): self.parent.efuse_read() self.parent.get_coding_scheme_warnings(silent=True) if self.fail or self.num_errors: - print( - "Error in BLOCK%d, re-burn it again (#%d), to fix it. " - "fail_bit=%d, num_errors=%d" - % (self.id, burns, self.fail, self.num_errors) + log.print( + f"Error in BLOCK{self.id}, re-burn it again (#{burns}) to fix." + f" fail_bit={self.fail}, num_errors={self.num_errors}" ) break if not self.fail and self.num_errors == 0: @@ -391,13 +409,15 @@ def burn_words(self, words): if self.wr_bitarray & self.bitarray != self.wr_bitarray: # if the required bits are not set then we need to re-burn it again. if burns < 2: - print( - f"\nRepeat burning BLOCK{self.id} (#{burns + 2}) because not all bits were set" + log.print( + f"\nRepeat burning BLOCK{self.id} (#{burns + 2}) " + "because not all bits were set" ) continue else: - print( - f"\nAfter {burns + 1} attempts, the required data was not set to BLOCK{self.id}" + log.print( + f"\nAfter {burns + 1} attempts, the required data was not " + f"set to BLOCK{self.id}" ) break @@ -412,29 +432,28 @@ def burn(self): self.burn_words(words) self.read() if not self.is_readable(): - print( - "{} ({}) is read-protected. " - "Read back the burn value is not possible.".format( - self.name, self.alias - ) + log.print( + f"{self.name} ({self.alias}) is read-protected. " + "Read back the burn value is not possible." ) if self.bitarray.all(False): - print("Read all '0'") + log.print("Read all '0'") else: # Should never happen raise esptool.FatalError( - "The {} is read-protected but not all '0' ({})".format( - self.name, self.bitarray.hex - ) + f"The {self.name} is read-protected but not all '0' " + f"({self.bitarray.hex})" ) else: if self.wr_bitarray == self.bitarray: - print("BURN BLOCK%-2d - OK (write block == read block)" % self.id) + log.print(f"BURN BLOCK{self.id:<2d} - OK (write block == read block)") elif ( self.wr_bitarray & self.bitarray == self.wr_bitarray and self.bitarray & before_burn_bitarray == before_burn_bitarray ): - print("BURN BLOCK%-2d - OK (all write block bits are set)" % self.id) + log.print( + f"BURN BLOCK{self.id:<2d} - OK (all write block bits are set)" + ) else: # Happens only when an efuse is written and read-protected # in one command @@ -444,7 +463,7 @@ def burn(self): # raise error only for other blocks if self.id != 0: raise esptool.FatalError( - "Burn {} ({}) was not successful".format(self.name, self.alias) + f"Burn {self.name} ({self.alias}) was not successful." ) self.wr_bitarray.set(0) @@ -454,17 +473,36 @@ class EspEfusesBase(object): Wrapper object to manage the efuse fields in a connected ESP bootloader """ - _esp = None - blocks: List[EfuseBlockBase] = [] - efuses: List = [] + _esp: esptool.ESPLoader + blocks: list[EfuseBlockBase] = [] + efuses: list = [] coding_scheme = None force_write_always = None - batch_mode_cnt = 0 - postpone = False + batch_mode_cnt: int = 0 + postpone: bool = False + BURN_BLOCK_DATA_NAMES: list[str] = [] + REGS: type[EfuseRegistersBase] + Blocks: EfuseBlocksBase + + def __init__( + self, + esp: esptool.ESPLoader, + skip_connect: bool = False, + debug: bool = False, + do_not_confirm: bool = False, + extend_efuse_table: None = None, + ) -> None: + self._esp = esp + self.debug = debug + self.do_not_confirm = do_not_confirm def __iter__(self): return self.efuses.__iter__() + @abstractmethod + def __getitem__(self, efuse_name): + pass + def get_crystal_freq(self): return self._esp.get_crystal_freq() @@ -484,12 +522,24 @@ def update_reg(self, addr, mask, new_val): def efuse_controller_setup(self): pass + @abstractmethod + def write_efuses(self, block): + pass + + @abstractmethod + def efuse_read(self): + pass + + @abstractmethod + def read_coding_scheme(self): + pass + def reconnect_chip(self, esp): - print("Re-connecting...") + log.print("Re-connecting...") baudrate = esp._port.baudrate port = esp._port.port esp._port.close() - return esptool.cmds.detect_chip(port, baudrate) + return esptool.detect_chip(port, baudrate) def get_index_block_by_name(self, name): for block in self.blocks: @@ -545,8 +595,8 @@ def 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) + log.print("These BLOCK0 eFuses will be burned later at the very end:") + log.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() @@ -554,20 +604,20 @@ def get_raw_value_from_write(self, efuse_name): 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.") + log.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): + def burn_all(self, check_batch_mode: bool = False) -> bool: if check_batch_mode: if self.batch_mode_cnt != 0: - print( + log.print( "\nBatch mode is enabled, " "the burn will be done at the end of the command." ) return False - print("\nCheck all blocks for burn...") - print("idx, BLOCK_NAME, Conclusion") + log.print("\nCheck all blocks for burn...") + log.print("idx, BLOCK_NAME, Conclusion") have_wr_data_for_burn = False for block in self.blocks: block.check_wr_data() @@ -576,7 +626,7 @@ def burn_all(self, check_batch_mode=False): ): have_wr_data_for_burn = True if not have_wr_data_for_burn: - print("Nothing to burn, see messages above.") + log.print("Nothing to burn, see messages above.") return True EspEfusesBase.confirm("", self.do_not_confirm) @@ -588,16 +638,16 @@ def burn_block(block, postponed_efuses): 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'") + log.print("The postponed eFuses were not burned due to an error.") + log.print("\t1. Try to fix a coding error by this cmd:") + log.print("\t 'espefuse 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}'") + log.print("\t2. Then run the cmd to burn all postponed eFuses:") + log.print(f"\t 'espefuse burn-efuse {command_string}'") raise esptool.FatalError("Error(s) were detected in eFuses") @@ -615,7 +665,7 @@ def burn_block(block, postponed_efuses): self.recover_postponed_efuses_from_block0_to_burn(postponed_efuses) burn_block(block, postponed_efuses) - print("Reading updated efuses...") + log.print("Reading updated eFuses...") self.read_coding_scheme() self.read_blocks() self.update_efuses() @@ -623,17 +673,17 @@ def burn_block(block, postponed_efuses): @staticmethod def confirm(action, do_not_confirm): - print( + log.print( "%s%s\nThis is an irreversible operation!" % (action, "" if action.endswith("\n") else ". ") ) if not do_not_confirm: - print("Type 'BURN' (all capitals) to continue.") - # required for Pythons which disable line buffering, ie mingw in mintty - sys.stdout.flush() + log.print("Type 'BURN' (all capitals) to continue.", flush=True) + # Flush required for Pythons which disable line buffering, + # ie mingw in mintty yes = input() if yes != "BURN": - print("Aborting.") + log.print("Aborting.") sys.exit(0) def print_error_msg(self, error_msg): @@ -641,7 +691,7 @@ def print_error_msg(self, error_msg): if not self.force_write_always: error_msg += "(use '--force-write-always' option to ignore it)" if self.force_write_always: - print(error_msg, "Skipped because '--force-write-always' option.") + log.print(error_msg, "Skipped because '--force-write-always' option.") else: raise esptool.FatalError(error_msg) @@ -650,9 +700,54 @@ def get_block_errors(self, block_num): return self.blocks[block_num].num_errors, self.blocks[block_num].fail def is_efuses_incompatible_for_burn(self): - # Overwrite this function for a specific target if you want to check if a certain eFuse(s) can be burned. + # Overwrite this function for a specific target if you want to check if a + # certain eFuse(s) can be burned. return False + def get_major_chip_version(self): + try: + return self["WAFER_VERSION_MAJOR"].get() + except KeyError: + return 0 + + def get_minor_chip_version(self): + try: + return self["WAFER_VERSION_MINOR"].get() + except KeyError: + return 0 + + def get_chip_version(self): + return self.get_major_chip_version() * 100 + self.get_minor_chip_version() + + def get_major_block_version(self): + try: + return self["BLK_VERSION_MAJOR"].get() + except KeyError: + return 0 + + def get_minor_block_version(self): + try: + return self["BLK_VERSION_MINOR"].get() + except KeyError: + return 0 + + def get_block_version(self): + return self.get_major_block_version() * 100 + self.get_minor_block_version() + + def get_pkg_version(self): + try: + return self["PKG_VERSION"].get() + except KeyError: + return 0 + + @abstractmethod + def summary(self): + pass + + @abstractmethod + def get_coding_scheme_warnings(self, silent: bool = False): + pass + class EfuseFieldBase(EfuseProtectBase): def __init__(self, parent, param): @@ -665,7 +760,7 @@ def __init__(self, parent, param): self.read_disable_bit = param.read_disable_bit self.name = param.name self.efuse_class = param.class_type - self.efuse_type = param.type + self.efuse_type: str = param.type self.description = param.description self.dict_value = param.dictionary self.bit_len = param.bit_len @@ -718,10 +813,9 @@ def convert_to_bitstring(self, new_value): try: return BitArray(self.efuse_type + "={}".format(new_value)) except CreationError as err: - print( - "New value '{}' is not suitable for {} ({})".format( - new_value, self.name, self.efuse_type - ) + log.print( + f"New value '{new_value}' is not suitable for " + f"{self.name} ({self.efuse_type})" ) raise esptool.FatalError(err) @@ -733,23 +827,22 @@ def check_new_value(self, bitarray_new_value): if bitarray_new_value.len != bitarray_old_value.len: raise esptool.FatalError( - "For {} efuse, the length of the new value is wrong, " - "expected {} bits, was {} bits.".format( - self.name, bitarray_old_value.len, bitarray_new_value.len - ) + f"For {self.name} eFuse, the length of the new value is wrong, " + f"expected {bitarray_old_value.len} bits, " + f"was {bitarray_new_value.len} bits." ) 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) + error_msg = f"\tThe same value for {self.name} " + error_msg += "is already burned. Do not change the eFuse." + log.print(error_msg) bitarray_new_value.set(0) elif bitarray_new_value == self.get_bitstring(from_read=False): error_msg = "\tThe same value for {} ".format(self.name) error_msg += "is already prepared for the burn operation." - print(error_msg) + log.print(error_msg) bitarray_new_value.set(0) else: if self.name not in ["WR_DIS", "RD_DIS"]: @@ -798,49 +891,72 @@ def update(self, bit_array_block): self.fail = self.parent.blocks[self.block].fail self.num_errors = self.parent.blocks[self.block].num_errors - def get_raw(self, from_read=True): - """Return the raw (unformatted) numeric value of the efuse bits + def get_raw(self, from_read: bool = True) -> int | bytearray: + """Return the raw (unformatted) numeric value of the eFuse bits Returns a simple integer or (for some subclasses) a bitstring. type: int or bool -> int type: bytes -> bytearray + + Args: + from_read: If True, read the eFuse value from the device. + If False, use the cached value. + + Returns: + int | bytearray: The raw value of the eFuse """ - return self.get_bitstring(from_read).read(self.efuse_type) + return self.get_bitstring(from_read).read(self.efuse_type) # type: ignore + + def get(self, from_read: bool = True) -> str | int | bytearray: + """Get a formatted version of the eFuse value, suitable for display - def get(self, from_read=True): - """Get a formatted version of the efuse value, suitable for display type: int or bool -> int type: bytes -> string "01 02 03 04 05 06 07 08 ... ". Byte order [0] ... [N]. dump regs: 0x04030201 0x08070605 ... + + Args: + from_read: If True, read the eFuse value from the device. + If False, use the cached value. + + Returns: + str | int | bytearray: The formatted version of the eFuse value """ if self.efuse_type.startswith("bytes"): - return util.hexify(self.get_bitstring(from_read).bytes[::-1], " ") + return util.hexify(self.get_bitstring(from_read).bytes[::-1], " ") # type: ignore else: return self.get_raw(from_read) - def get_meaning(self, from_read=True): - """Get the meaning of efuse from dict if possible, suitable for display""" + def get_meaning(self, from_read: bool = True) -> str | int | bytearray: + """Get the meaning of eFuse from dict if possible, suitable for display + + Args: + from_read: If True, read the eFuse value from the device. + If False, use the cached value. + + Returns: + str | int | bytearray: The meaning of the eFuse + """ if self.dict_value: try: - return self.dict_value[self.get_raw(from_read)] + return self.dict_value[self.get_raw(from_read)] # type: ignore except KeyError: pass return self.get(from_read) - def get_bitstring(self, from_read=True): + def get_bitstring(self, from_read: bool = True) -> BitStream | Bits: if from_read: self.bitarray.pos = 0 return self.bitarray else: field_len = self.bitarray.len - block = self.parent.blocks[self.block] + block: EfuseBlockBase = self.parent.blocks[self.block] block.wr_bitarray.pos = block.wr_bitarray.length - ( self.word * 32 + self.pos + field_len ) return block.wr_bitarray.read(self.bitarray.len) def burn(self, new_value): - # Burn a efuse. Added for compatibility reason. + """Burn a eFuse. Added for compatibility reason.""" self.save(new_value) self.parent.burn_all() @@ -860,7 +976,7 @@ def get_info(self): return output def reset(self): - # resets a efuse that is prepared for burning + """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() diff --git a/tools/esptool_py/espefuse/efuse/base_operations.py b/tools/esptool_py/espefuse/efuse/base_operations.py index 60f18dc5e5..c732756ba9 100644 --- a/tools/esptool_py/espefuse/efuse/base_operations.py +++ b/tools/esptool_py/espefuse/efuse/base_operations.py @@ -1,804 +1,1181 @@ # This file includes the common operations with eFuses for chips # -# SPDX-FileCopyrightText: 2020-2022 Espressif Systems (Shanghai) CO LTD +# SPDX-FileCopyrightText: 2020-2025 Espressif Systems (Shanghai) CO LTD # # SPDX-License-Identifier: GPL-2.0-or-later -import argparse +from abc import abstractmethod +import io import os import json import sys +from typing import Any, BinaryIO, Callable, TextIO + +import rich_click as click from bitstring import BitStream import esptool +from esptool.logger import log from . import base_fields from . import util +from .emulate_efuse_controller_base import EmulateEfuseControllerBase -def add_common_commands(subparsers, efuses): - class ActionEfuseValuePair(argparse.Action): - def __init__(self, option_strings, dest, nargs=None, **kwargs): - self._nargs = nargs - self._choices = kwargs.get("efuse_choices") - self.efuses = kwargs.get("efuses") - del kwargs["efuse_choices"] - del kwargs["efuses"] - super(ActionEfuseValuePair, self).__init__( - option_strings, dest, nargs=nargs, **kwargs - ) +class EfuseValuePairArg(click.Argument): + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) - def __call__(self, parser, namespace, values, option_string=None): - def check_efuse_name(efuse_name, efuse_list): - if efuse_name not in self._choices: - raise esptool.FatalError( - "Invalid the efuse name '{}'. " - "Available the efuse names: {}".format( - efuse_name, self._choices - ) - ) + def make_metavar(self) -> str: + return f"[{super().make_metavar()}] ..." - efuse_value_pairs = {} - if len(values) > 1: - if len(values) % 2: - raise esptool.FatalError( - "The list does not have a valid pair (name value) {}".format( - values - ) - ) - for i in range(0, len(values), 2): - efuse_name, new_value = values[i : i + 2 :] - check_efuse_name(efuse_name, self._choices) - check_arg = base_fields.CheckArgValue(self.efuses, efuse_name) - efuse_value_pairs[efuse_name] = check_arg(new_value) - else: - # For the case of compatibility, when only the efuse_name is given - # Fields with 'bitcount' and 'bool' types can be without new_value arg - efuse_name = values[0] - check_efuse_name(efuse_name, self._choices) + def type_cast_value(self, ctx: click.Context, value: list[str]): + return self.type.convert(value, None, ctx) + + +class EfuseValuePairType(click.ParamType): + name = "efuse-value-pair" + + def __init__(self, efuse_choices, efuses): + self.efuse_choices = efuse_choices + self.efuses = efuses + + def convert(self, value: str, param: click.Parameter | None, ctx: click.Context): + def check_efuse_name(efuse_name: str): + if efuse_name not in self.efuse_choices: + raise click.BadParameter( + f"Invalid eFuse name '{efuse_name}'. " + f"Available eFuse names: {self.efuse_choices}" + ) + return efuse_name + + # Handle single value case (eFuse name only) + efuse_value_pairs = {} + if len(value) > 1: + if len(value) % 2: + raise click.BadParameter( + f"The list does not have a valid pair (name value) {value}" + ) + for i in range(0, len(value), 2): + efuse_name: str = value[i] + new_value: str = value[i + 1] + efuse_name = check_efuse_name(efuse_name) check_arg = base_fields.CheckArgValue(self.efuses, efuse_name) - efuse_value_pairs[efuse_name] = check_arg(None) - setattr(namespace, self.dest, efuse_value_pairs) - - burn = subparsers.add_parser( - "burn_efuse", help="Burn the efuse with the specified name" - ) - burn.add_argument( - "name_value_pairs", - help="Name of efuse field and new value pairs to burn. EFUSE_NAME: " - "[{}].".format(", ".join([e.name for e in efuses.efuses])), - action=ActionEfuseValuePair, - nargs="+", - metavar="[EFUSE_NAME VALUE]", - efuse_choices=[e.name for e in efuses.efuses] - + [name for e in efuses.efuses for name in e.alt_names if name != ""], - efuses=efuses, - ) - burn.add_argument( - "--force", - help="Suppress an error to burn eFuses", - action="store_true", - ) - - read_protect_efuse = subparsers.add_parser( - "read_protect_efuse", - help="Disable readback for the efuse with the specified name", - ) - read_protect_efuse.add_argument( - "efuse_name", - help="Name of efuse register to burn", - nargs="+", - choices=[e.name for e in efuses.efuses if e.read_disable_bit is not None] - + [ - name - for e in efuses.efuses - if e.read_disable_bit is not None - for name in e.alt_names - if name != "" - ], - ) - - write_protect_efuse = subparsers.add_parser( - "write_protect_efuse", - help="Disable writing to the efuse with the specified name", - ) - write_protect_efuse.add_argument( - "efuse_name", - help="Name of efuse register to burn", - nargs="+", - choices=[e.name for e in efuses.efuses if e.write_disable_bit is not None] - + [ - name - for e in efuses.efuses - if e.write_disable_bit is not None - for name in e.alt_names - if name != "" - ], - ) - - burn_block_data = subparsers.add_parser( - "burn_block_data", - help="Burn non-key data to EFUSE blocks. " - "(Don't use this command to burn key data for Flash Encryption or " - "ESP32 Secure Boot V1, as the byte order of keys is swapped (use burn_key)).", - ) - add_force_write_always(burn_block_data) - burn_block_data.add_argument( - "--offset", "-o", help="Byte offset in the efuse block", type=int, default=0 - ) - burn_block_data.add_argument( - "block", - help="Efuse block to burn.", - action="append", - choices=efuses.BURN_BLOCK_DATA_NAMES, - ) - burn_block_data.add_argument( - "datafile", - help="File containing data to burn into the efuse block", - action="append", - type=argparse.FileType("rb"), - ) - for _ in range(0, len(efuses.BURN_BLOCK_DATA_NAMES)): - burn_block_data.add_argument( - "block", - help="Efuse block to burn.", - metavar="BLOCK", - nargs="?", - action="append", - choices=efuses.BURN_BLOCK_DATA_NAMES, - ) - burn_block_data.add_argument( - "datafile", - nargs="?", - help="File containing data to burn into the efuse block", - metavar="DATAFILE", - action="append", - type=argparse.FileType("rb"), - ) + efuse_value_pairs[efuse_name] = check_arg(new_value) + + else: + # For the case of compatibility, when only the efuse_name is given + # Fields with 'bitcount' and 'bool' types can be without new_value arg + efuse_name = value[0] + check_efuse_name(efuse_name) + check_arg = base_fields.CheckArgValue(self.efuses, efuse_name) + efuse_value_pairs[efuse_name] = check_arg(None) + + return efuse_value_pairs + + +class CustomMACType(click.ParamType): + name = "custom_mac" + + def convert(self, value: str, param: click.Parameter | None, ctx: click.Context): + return base_fields.CheckArgValue(ctx.obj["efuses"], "CUSTOM_MAC")(value) + + +class TupleParameter(click.Argument): + def __init__(self, *args, **kwargs): + self.max_arity = kwargs.pop("max_arity", None) + super().__init__(*args, **kwargs) + + def make_metavar(self) -> str: + if self.nargs == 1: + return super().make_metavar() # type: ignore + if self.max_arity is None: + return f"[{super().make_metavar()}] ..." + return f"[{super().make_metavar()}] ... (max {self.max_arity} groups)" + + def type_cast_value(self, ctx: click.Context, value: list[str]) -> tuple[Any, ...]: + # This is by default eating all options, so we need to check for help option + if any(v in ctx.help_option_names for v in value): + # show help + click.echo(ctx.get_help()) + ctx.exit() + + # Check if we have more values than allowed by max_arity + if self.max_arity is not None and len(value) > self.max_arity * self.type.arity: + raise click.BadParameter( + f"Expected at most {self.max_arity} groups ({self.type.arity} values " + f"each), got {len(value)} (values: {value})" + ) + + # check that the number of values is a multiple of self.type.arity + if len(value) % self.type.arity != 0: + raise click.BadParameter( + f"Expected multiple of {self.type.arity} values, got {len(value)} " + f"(values: {value})" + ) - set_bit_cmd = subparsers.add_parser("burn_bit", help="Burn bit in the efuse block.") - add_force_write_always(set_bit_cmd) - set_bit_cmd.add_argument( - "block", help="Efuse block to burn.", choices=efuses.BURN_BLOCK_DATA_NAMES - ) - set_bit_cmd.add_argument( - "bit_number", - help="Bit number in the efuse block [0..BLK_LEN-1]", - nargs="+", - type=int, - ) - - subparsers.add_parser( - "adc_info", - help="Display information about ADC calibration data stored in efuse.", - ) - - dump_cmd = subparsers.add_parser("dump", help="Dump raw hex values of all eFuses") - dump_cmd.add_argument( - "--format", - help="Select the dump format: " - "default - usual console eFuse dump; " - "joint - all eFuse blocks are stored in one file; " - "split - each eFuse block is placed into its own file. The tool will create multiple files based on " - "the given --file_name (/path/blk.bin): blk0.bin, blk1.bin ... blkN.bin. Use the burn_block_data cmd " - "to write it back to another chip.", - choices=["default", "split", "joint"], - default="default", - ) - dump_cmd.add_argument( - "--file_name", - help="The path to the file in which to save the dump, if not specified, output to the console.", - default=sys.stdout, - ) - - summary_cmd = subparsers.add_parser( - "summary", help="Print human-readable summary of efuse values" - ) - summary_cmd.add_argument( - "--format", - help="Select the summary format", - choices=["summary", "json", "value_only"], - default="summary", - ) - summary_cmd.add_argument( - "--file", - help="File to save the efuse summary", - type=argparse.FileType("w"), - default=sys.stdout, - ) - summary_cmd.add_argument( - "efuses_to_show", - help="The efuses to show. If not provided, all efuses will be shown.", - nargs="*", - ) - - execute_scripts = subparsers.add_parser( - "execute_scripts", help="Executes scripts to burn at one time." - ) - execute_scripts.add_argument( - "scripts", - help="The special format of python scripts.", - nargs="+", - type=argparse.FileType("r"), - ) - execute_scripts.add_argument( - "--index", - help="integer index. " - "It allows to retrieve unique data per chip from configfiles " - "and then burn them (ex. CUSTOM_MAC, UNIQUE_ID).", - type=int, - ) - execute_scripts.add_argument( - "--configfiles", - help="List of configfiles with data", - nargs="?", - action="append", - type=argparse.FileType("r"), - ) - - check_error_cmd = subparsers.add_parser("check_error", help="Checks eFuse errors") - check_error_cmd.add_argument( - "--recovery", - help="Recovery of BLOCKs after encoding errors", - action="store_true", - ) - - -def add_force_write_always(p): - p.add_argument( + # split value into groups of self.type.arity and call convert() for each group + groups = [ + value[i : i + self.type.arity] + for i in range(0, len(value), self.type.arity) + ] + return tuple(self.type.convert(group, None, ctx) for group in groups) + + +class NonCompositeTuple(click.Tuple): + is_composite = False # Hack to work around click's default nargs=1 + + def __init__(self, types): + super().__init__(types) + + +def add_force_write_always(function: Callable): + def callback(ctx: click.Context, param: click.Parameter, value: str): + ctx.ensure_object(dict) + if ctx.obj.get("commands", None) is not None: + ctx.obj["commands"].efuses.force_write_always = value + + return click.option( "--force-write-always", - help="Write the efuse even if it looks like it's already been written, " + help="Write the eFuse even if it looks like it's already been written, " "or is write protected. Note that this option can't disable write protection, " "or clear any bit which has already been set.", - action="store_true", - ) + is_flag=True, + callback=callback, + )(function) -def add_show_sensitive_info_option(p): - p.add_argument( +def add_show_sensitive_info_option(function: Callable): + def callback(ctx: click.Context, param: click.Parameter, value: bool): + if value or ctx.obj.get("debug") or ctx.obj.get("show_sensitive_info", False): + value = True + ctx.obj["show_sensitive_info"] = value + return value + + return click.option( "--show-sensitive-info", help="Show data to be burned (may expose sensitive data). " "Enabled if --debug is used.", - action="store_true", - default=False, - ) - - -def summary(esp, efuses, args): - """Print a human-readable or json summary of efuse contents""" - ROW_FORMAT = "%-50s %-50s%s = %s %s %s" - human_output = args.format in ["summary", "value_only"] - value_only = args.format == "value_only" - if value_only and len(args.efuses_to_show) != 1: - raise esptool.FatalError( - "The 'value_only' format can be used exactly for one efuse." - ) - do_filtering = bool(args.efuses_to_show) - json_efuse = {} - summary_efuse = [] - if args.file != sys.stdout: - print("Saving efuse values to " + args.file.name) - if human_output and not value_only: - summary_efuse.append( - ROW_FORMAT.replace("-50", "-12") - % ( - "EFUSE_NAME (Block)", - "Description", - "", - "[Meaningful Value]", - "[Readable/Writeable]", - "(Hex Value)", - ) - ) - summary_efuse.append("-" * 88) - for category in sorted(set(e.category for e in efuses), key=lambda c: c.title()): - if human_output and not value_only: - summary_efuse.append(f"{category.title()} fuses:") - for e in (e for e in efuses if e.category == category): - if e.efuse_type.startswith("bytes"): - raw = "" - else: - raw = "({})".format(e.get_bitstring()) - (readable, writeable) = (e.is_readable(), e.is_writeable()) - if readable and writeable: - perms = "R/W" - elif readable: - perms = "R/-" - elif writeable: - perms = "-/W" - else: - perms = "-/-" - base_value = e.get_meaning() - value = str(base_value) - if not readable: - count_read_disable_bits = e.get_count_read_disable_bits() - if count_read_disable_bits == 2: - # On the C2 chip, BLOCK_KEY0 has two read protection bits [0, 1] - # related to the lower and higher part of the block. - v = [value[: (len(value) // 2)], value[(len(value) // 2) :]] - for i in range(count_read_disable_bits): - if not e.is_readable(blk_part=i): - v[i] = v[i].replace("0", "?") - value = "".join(v) - else: - value = value.replace("0", "?") - if ( - human_output - and (not do_filtering or e.name in args.efuses_to_show) - and not value_only - ): - summary_efuse.append( - ROW_FORMAT - % ( - e.get_info(), - e.description[:50], - "\n " if len(value) > 20 else "", - value, - perms, - raw, - ) - ) - desc_len = len(e.description[50:]) - if desc_len: - desc_len += 50 - for i in range(50, desc_len, 50): - summary_efuse.append( - f"{'':<50} {e.description[i : (50 + i)]:<50}" - ) - elif human_output and value_only and e.name in args.efuses_to_show: - summary_efuse.append(f"{value}") - elif args.format == "json" and ( - not do_filtering or e.name in args.efuses_to_show - ): - json_efuse[e.name] = { - "name": e.name, - "value": base_value if readable else value, - "readable": readable, - "writeable": writeable, - "description": e.description, - "category": e.category, - "block": e.block, - "word": e.word, - "pos": e.pos, - "efuse_type": e.efuse_type, - "bit_len": e.bit_len, - } - if human_output and not value_only: - # Remove empty category if efuses are filtered and there are none to show - if do_filtering and summary_efuse[-1] == f"{category.title()} fuses:": - summary_efuse.pop() - else: - summary_efuse.append("") - if human_output and not value_only: - summary_efuse.append(efuses.summary()) - warnings = efuses.get_coding_scheme_warnings() - if warnings: - summary_efuse.append( - "WARNING: Coding scheme has encoding bit error warnings" + is_flag=True, + callback=callback, + expose_value=True, # ensure that callback is called even if option is not used + )(function) + + +def protect_options(function: Callable): + function = click.option( + "--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.", + is_flag=True, + )(function) + function = click.option( + "--no-read-protect", + help="Disable read-protecting of the key. The key remains readable software.", + is_flag=True, + )(function) + return function + + +class BaseCommands: + CHIP_NAME = "auto" + efuse_lib: type[base_fields.EspEfusesBase] | None = None + efuses: base_fields.EspEfusesBase + esp: esptool.ESPLoader | EmulateEfuseControllerBase + external_esp: bool = False + + def get_efuses( + self, + skip_connect=False, + debug_mode=False, + do_not_confirm=False, + extend_efuse_table=None, + ): + if self.esp is None: + raise esptool.FatalError("get_efuses: esp is not set") + if self.CHIP_NAME == self.esp.CHIP_NAME: + if self.efuse_lib is None: + raise esptool.FatalError("get_efuses: efuse_lib is not set") + self.efuses = self.efuse_lib( + self.esp, skip_connect, debug_mode, do_not_confirm, extend_efuse_table ) - if human_output: - for line in summary_efuse: - print(line, file=args.file) - if args.file != sys.stdout: - args.file.close() - print("Done") - elif args.format == "json": - json.dump(json_efuse, args.file, sort_keys=True, indent=4) - print("") - - -def dump(esp, efuses, args): - """Dump raw efuse data registers""" - dump_file = args.file_name - to_console = args.file_name == sys.stdout - - def output_block_to_file(block, f, to_console): - block_dump = BitStream(block.get_bitstring()) - block_dump.byteswap() - if to_console: - f.write(block_dump.hex + "\n") else: - block_dump.tofile(f) + raise esptool.FatalError( + "get_efuses: Mismatch chip name " + f"({self.CHIP_NAME} != {self.esp.CHIP_NAME})" + ) - if args.format == "default": - if to_console: - # for "espefuse.py dump" cmd - for block in efuses.blocks: - block.print_block(block.get_bitstring(), "dump", debug=True) - return - else: - # for back compatibility to support "espefuse.py dump --file_name dump.bin" - args.format = "split" + def __enter__(self): + return self - if args.format == "split": - # each efuse block is placed into its own file - for block in efuses.blocks: - if not to_console: - file_dump_name = args.file_name - fname, fextension = os.path.splitext(file_dump_name) - file_dump_name = f"{fname}{block.id}{fextension}" - print(f"Dump efuse block{block.id} -> {file_dump_name}") - dump_file = open(file_dump_name, "wb") - output_block_to_file(block, dump_file, to_console) - if not to_console: - dump_file.close() - elif args.format == "joint": - # all efuse blocks are stored in one file - if not to_console: - print(f"Dump efuse blocks -> {args.file_name}") - dump_file = open(args.file_name, "wb") - for block in efuses.blocks: - output_block_to_file(block, dump_file, to_console) - if not to_console: - dump_file.close() - - -def burn_efuse(esp, efuses, args): - def print_attention(blocked_efuses_after_burn): - if len(blocked_efuses_after_burn): - print( - " ATTENTION! This BLOCK uses NOT the NONE coding scheme " - "and after 'BURN', these efuses can not be burned in the feature:" - ) - for i in range(0, len(blocked_efuses_after_burn), 5): - print( - " ", - "".join("{}".format(blocked_efuses_after_burn[i : i + 5 :])), - ) + def __exit__(self, exc_type, exc_value, traceback): + if ( + self.esp is not None + and not self.external_esp + and isinstance(self.esp, esptool.ESPLoader) + ): + self.esp._port.close() - efuse_name_list = [name for name in args.name_value_pairs.keys()] - burn_efuses_list = [efuses[name] for name in efuse_name_list] - old_value_list = [efuses[name].get_raw() for name in efuse_name_list] - new_value_list = [value for value in args.name_value_pairs.values()] - util.check_duplicate_name_in_list(efuse_name_list) - - attention = "" - print("The efuses to burn:") - for block in efuses.blocks: - burn_list_a_block = [e for e in burn_efuses_list if e.block == block.id] - if len(burn_list_a_block): - print(" from BLOCK%d" % (block.id)) - for field in burn_list_a_block: - print(" - %s" % (field.name)) - if ( - efuses.blocks[field.block].get_coding_scheme() - != efuses.REGS.CODING_SCHEME_NONE - ): - using_the_same_block_names = [ - e.name for e in efuses if e.block == field.block - ] - wr_names = [e.name for e in burn_list_a_block] - blocked_efuses_after_burn = [ - name - for name in using_the_same_block_names - if name not in wr_names - ] - attention = " (see 'ATTENTION!' above)" - if attention: - print_attention(blocked_efuses_after_burn) - - print("\nBurning efuses{}:".format(attention)) - for efuse, new_value in zip(burn_efuses_list, new_value_list): - print( - "\n - '{}' ({}) {} -> {}".format( - efuse.name, - efuse.description, - efuse.get_bitstring(), - efuse.convert_to_bitstring(new_value), + ################################# CLI definitions ################################# + + def add_cli_commands(self, cli: click.Group): + """Add the CLI commands to the given click group""" + + if self.efuses is None: + # This should never happen, but just in case someone calls it from API + raise esptool.FatalError( + "To initialize the CLI commands, you need to call get_efuses() first." ) + + efuses: list[base_fields.EfuseFieldBase] = getattr(self.efuses, "efuses", []) + + @cli.command( + "burn-efuse", + help="Burn the eFuse with the specified name.\n\n" + f"Allowed options for EFUSE_NAME: [{', '.join([e.name for e in efuses])}].", + ) + @click.argument( + "name_value_pairs", + cls=EfuseValuePairArg, + metavar=" ", + required=True, + nargs=-1, + type=EfuseValuePairType( + [e.name for e in efuses] + + [name for e in efuses for name in e.alt_names if name != ""], + self.efuses, + ), ) - efuse.save(new_value) - - print() - if "ENABLE_SECURITY_DOWNLOAD" in efuse_name_list: - print( - "ENABLE_SECURITY_DOWNLOAD -> 1: eFuses will not be read back " - "for confirmation because this mode disables " - "any SRAM and register operations." + @click.option( + "--force", is_flag=True, help="Suppress errors when burning eFuses." ) - print(" espefuse will not work.") - print(" esptool can read/write only flash.") + @click.pass_context + def burn_efuse_cli(ctx, name_value_pairs, force): + self.burn_efuse(name_value_pairs, force) - if "DIS_DOWNLOAD_MODE" in efuse_name_list: - print( - "DIS_DOWNLOAD_MODE -> 1: eFuses will not be read back for " - "confirmation because this mode disables any communication with the chip." + @cli.command( + "read-protect-efuse", + help="Disable readback for the selected eFuse with the specified name.", + short_help="Disable readback for the eFuse.", ) - print( - " espefuse/esptool will not work because " - "they will not be able to connect to the chip." + @click.argument("efuse_name", nargs=-1, required=True) + @click.pass_context + def read_protect_efuse_cli(ctx, efuse_name): + self.read_protect_efuse(efuse_name) + + @cli.command( + "write-protect-efuse", + help="Disable writing to the eFuse with the specified name.", + short_help="Disable writing to the eFuse.", ) + @click.argument("efuse_name", nargs=-1, required=True) + def write_protect_efuse_cli(efuse_name): + """Disable writing to the eFuse with the specified name.""" + self.write_protect_efuse(efuse_name) - if ( - esp.CHIP_NAME == "ESP32" - and esp.get_chip_revision() >= 300 - and "UART_DOWNLOAD_DIS" in efuse_name_list - ): - print( - "UART_DOWNLOAD_DIS -> 1: eFuses will be read for confirmation, " - "but after that connection to the chip will become impossible." + @cli.command( + "burn-block-data", + help="Burn non-key data to EFUSE blocks. " + "(Don't use this command to burn key data for Flash Encryption or ESP32 " + "Secure Boot V1, as the byte order of keys is swapped (use burn-key)).\n\n" + "Allowed options for BLOCK: " + f"[{', '.join(self.efuses.BURN_BLOCK_DATA_NAMES)}].", ) - print(" espefuse/esptool will not work.") + @click.argument( + "block_datafile", + cls=TupleParameter, + metavar=" ", + required=True, + nargs=-1, + max_arity=len(self.efuses.BURN_BLOCK_DATA_NAMES), + type=NonCompositeTuple( + [ + click.Choice(self.efuses.BURN_BLOCK_DATA_NAMES), + click.File("rb"), + ] + ), + ) + @click.option( + "--offset", + "-o", + type=int, + default=0, + help="Byte offset in the eFuse block.", + ) + @add_force_write_always + def burn_block_data_cli(block_datafile, offset, **kwargs): + block, datafile = zip(*block_datafile) + self.burn_block_data(block, datafile, offset) - if efuses.is_efuses_incompatible_for_burn(): - if args.force: - print("Ignore incompatible eFuse settings.") - else: + @cli.command("burn-bit") + @click.argument("block", required=True) + @click.argument( + "bit_number", + nargs=-1, + type=int, + required=True, + ) + @add_force_write_always + def burn_bit_cli(block, bit_number, **kwargs): + """Burn bit in the eFuse block.""" + self.burn_bit(block, bit_number) + + @cli.command("dump") + @click.option( + "--format", + type=click.Choice(["default", "split", "joint"]), + default="default", + help="Select the dump format: default - usual console eFuse dump; " + "joint - all eFuse blocks are stored in one file; " + "split - each eFuse block is placed into its own file.", + ) + @click.option( + "--file-name", + type=click.Path(dir_okay=False, writable=True), + default=None, + help="The path to the file in which to save the dump, if not specified, " + "output to the console.", + ) + def dump_cli(format, file_name): + """Dump raw hex values of all eFuses.""" + self.dump(format, file_name) + + @cli.command("summary") + @click.argument("efuses_to_show", nargs=-1, required=False) + @click.option( + "--format", + type=click.Choice(["summary", "json", "value_only"]), + default="summary", + help="Select the summary format.", + ) + @click.option( + "--file", + type=click.File("w"), + default=sys.stdout, + help="File to save the eFuse summary to.", + ) + def summary_cli(format, file, efuses_to_show=[]): + """Print human-readable summary of eFuse values.""" + self.summary(efuses_to_show, format, file) + + @cli.command("check-error") + @click.option( + "--recovery", is_flag=True, help="Recovery of BLOCKs after encoding errors." + ) + @click.pass_context + def check_error_cli(ctx, recovery): + """Checks eFuse errors.""" + self.check_error(recovery, ctx.obj["do_not_confirm"]) + + @cli.command( + "adc-info", + short_help="Display information about ADC calibration data " + "stored in eFuse.", + help="Display information about ADC calibration data stored in eFuse.", + ) + def adc_info_cli(): + self.adc_info() + + @cli.command( + "burn-custom-mac", + short_help="Burn a 48-bit Custom MAC address.", + help="Burn a 48-bit Custom MAC address to EFUSE," + f"BLOCK{self.efuses['CUSTOM_MAC'].block}. " + "Mac address should be given in hexadecimal format with bytes separated " + "by colons (e.g. AA:CD:EF:01:02:03).", + ) + @click.argument( + "mac", + type=CustomMACType(), + ) + @add_force_write_always + def burn_custom_mac_cli(mac, **kwargs): + self.burn_custom_mac(mac) + + @cli.command("get-custom-mac") + def get_custom_mac_cli(): + """Get the 48-bit Custom MAC Address.""" + self.get_custom_mac() + + ################################## Helper methods ################################## + + def use_batch_mode(self): + """Enable batch mode for eFuse operations. + + This method increments the batch mode counter, allowing multiple eFuse + operations to be queued before burning. All queued operations will be + executed together when :func:`burn_all` is called. + + This method can be called multiple times to nest batch operations. + Each call should be paired with a corresponding call to :func:`burn_all`. + """ + self.efuses.batch_mode_cnt += 1 + + def burn_all(self, check_batch_mode=False): + """Execute all queued eFuse operations and decrement batch mode counter. + + This method decrements the batch mode counter, then burns all eFuses + that have been queued during batch mode. When the counter reaches zero, + batch mode is fully exited. + + This method should be called after :func:`use_batch_mode` to execute + the queued operations. + + Args: + check_batch_mode: If True, only execute the burn operation if this is the + final burn_all call (i.e., when batch_mode_cnt reaches zero in nested + operations). + + Returns: + bool: True if the burn operation was successful. + """ + self.efuses.batch_mode_cnt -= 1 + return self.efuses.burn_all(check_batch_mode) + + def _key_block_is_unused( + self, + block: base_fields.EfuseBlockBase, + key_purpose_block: base_fields.EfuseBlockBase, + ) -> bool: + """Helper method to check if a key block is available for use""" + if not block.is_readable() or not block.is_writeable(): + return False + + if key_purpose_block.get() != "USER" or not key_purpose_block.is_writeable(): + return False + + if not block.get_bitstring().all(False): + return False + + return True + + def _get_next_key_block( + self, current_key_block: base_fields.EfuseBlockBase, block_name_list: list[str] + ) -> base_fields.EfuseBlockBase | None: + """Helper method to get the next available key block""" + key_blocks = [b for b in self.efuses.blocks if b.key_purpose_name] + start = key_blocks.index(current_key_block) + + # Sort key blocks so that we pick the next free block + # (and loop around if necessary) + key_blocks = key_blocks[start:] + key_blocks[0:start] + + # Exclude any other blocks that will be be burned + key_blocks = [b for b in key_blocks if b.name not in block_name_list] + + for block in key_blocks: + key_purpose_block = self.efuses[block.key_purpose_name] + if self._key_block_is_unused(block, key_purpose_block): + return block + + return None + + def _split_512_bit_key( + self, + block_names: list[str], + datafiles: list[BinaryIO], + keypurposes: list[str], + ) -> tuple[list[str], list[BinaryIO], list[str]]: + """Helper method to split 512-bit key into two 256-bit keys""" + keypurpose_list = list(keypurposes) + datafile_list = list(datafiles) + block_name_list = list(block_names) + + i = keypurpose_list.index("XTS_AES_256_KEY") + block_name = block_name_list[i] + + block_num = self.efuses.get_index_block_by_name(block_name) + block = self.efuses.blocks[block_num] + + data = datafile_list[i].read() + if len(data) != 64: raise esptool.FatalError( - "Incompatible eFuse settings detected, abort. (use --force flag to skip it)." + "Incorrect key file size %d, XTS_AES_256_KEY should be 64 bytes" + % len(data) ) - if not efuses.burn_all(check_batch_mode=True): - return + key_block_2 = self._get_next_key_block(block, block_name_list) + if not key_block_2: + raise esptool.FatalError("XTS_AES_256_KEY requires two free keyblocks") - print("Checking efuses...") - raise_error = False - for efuse, old_value, new_value in zip( - burn_efuses_list, old_value_list, new_value_list - ): - if not efuse.is_readable(): - print( - "Efuse %s is read-protected. Read back the burn value is not possible." - % efuse.name - ) - else: - new_value = efuse.convert_to_bitstring(new_value) - burned_value = efuse.get_bitstring() - if burned_value != new_value: - print( - burned_value, - "->", - new_value, - "Efuse %s failed to burn. Protected?" % efuse.name, - ) - raise_error = True - if raise_error: - raise esptool.FatalError("The burn was not successful.") - else: - print("Successful") + keypurpose_list.append("XTS_AES_256_KEY_1") + datafile_list.append(io.BytesIO(data[:32])) + block_name_list.append(block_name) + keypurpose_list.append("XTS_AES_256_KEY_2") + datafile_list.append(io.BytesIO(data[32:])) + block_name_list.append(key_block_2.name) -def read_protect_efuse(esp, efuses, args): - util.check_duplicate_name_in_list(args.efuse_name) + keypurpose_list.pop(i) + datafile_list.pop(i) + block_name_list.pop(i) - for efuse_name in args.efuse_name: - efuse = efuses[efuse_name] - if not efuse.is_readable(): - print("Efuse %s is already read protected" % efuse.name) - else: - if esp.CHIP_NAME == "ESP32": + return block_name_list, datafile_list, keypurpose_list + + def _convert_mac_to_bytes(self, mac: str | bytes) -> bytes: + if isinstance(mac, str): + return base_fields.CheckArgValue(self.efuses, "CUSTOM_MAC")(mac) # type: ignore + return mac + + ##################################### Commands #################################### + + def summary( + self, + efuses_to_show: list[str] = [], + format: str = "summary", + file: TextIO = sys.stdout, + ): + """ + Print a human-readable or json summary of eFuse contents. + + Args: + efuses_to_show: List of eFuse names to show. + format: Format to use for the summary. + file: File to write the summary to. + """ + ROW_FORMAT = "%-50s %-50s%s = %s %s %s" + human_output = format in ["summary", "value_only"] + value_only = format == "value_only" + if value_only and len(efuses_to_show) != 1: + raise esptool.FatalError( + "The 'value_only' format can be used exactly for one eFuse." + ) + do_filtering = bool(efuses_to_show) + json_efuse = {} + summary_efuse = [] + if file != sys.stdout: + log.print("Saving eFuse values to " + file.name) + if human_output and not value_only: + summary_efuse.append( + ROW_FORMAT.replace("-50", "-12") + % ( + "EFUSE_NAME (Block)", + "Description", + "", + "[Meaningful Value]", + "[Readable/Writeable]", + "(Hex Value)", + ) + ) + summary_efuse.append("-" * 88) + for category in sorted( + set(e.category for e in self.efuses), key=lambda c: c.title() + ): + if human_output and not value_only: + summary_efuse.append(f"{category.title()} fuses:") + for e in (e for e in self.efuses if e.category == category): + if e.efuse_type.startswith("bytes"): + raw = "" + else: + raw = "({})".format(e.get_bitstring()) + (readable, writeable) = (e.is_readable(), e.is_writeable()) + if readable and writeable: + perms = "R/W" + elif readable: + perms = "R/-" + elif writeable: + perms = "-/W" + else: + perms = "-/-" + base_value = e.get_meaning() + value = str(base_value) + if not readable: + count_read_disable_bits = e.get_count_read_disable_bits() + if count_read_disable_bits == 2: + # On the C2 chip, BLOCK_KEY0 has two read protection bits [0, 1] + # related to the lower and higher part of the block. + v = [value[: (len(value) // 2)], value[(len(value) // 2) :]] + for i in range(count_read_disable_bits): + if not e.is_readable(blk_part=i): + v[i] = v[i].replace("0", "?") + value = "".join(v) + else: + value = value.replace("0", "?") if ( - efuse_name == "BLOCK2" - and not efuses["ABS_DONE_0"].get() - and esp.get_chip_revision() >= 300 + human_output + and (not do_filtering or e.name in efuses_to_show) + and not value_only ): - if efuses["ABS_DONE_1"].get(): - raise esptool.FatalError( - "Secure Boot V2 is on (ABS_DONE_1 = True), " - "BLOCK2 must be readable, stop this operation!" - ) - else: - print( - "If Secure Boot V2 is used, BLOCK2 must be readable, " - "please stop this operation!" + summary_efuse.append( + ROW_FORMAT + % ( + e.get_info(), + e.description[:50], + "\n " if len(value) > 20 else "", + value, + perms, + raw, ) - elif esp.CHIP_NAME == "ESP32-C2": - error = ( - not efuses["XTS_KEY_LENGTH_256"].get() - and efuse_name == "BLOCK_KEY0" + ) + desc_len = len(e.description[50:]) + if desc_len: + desc_len += 50 + for i in range(50, desc_len, 50): + summary_efuse.append( + f"{'':<50} {e.description[i : (50 + i)]:<50}" + ) + elif human_output and value_only and e.name in efuses_to_show: + summary_efuse.append(f"{value}") + elif format == "json" and ( + not do_filtering or e.name in efuses_to_show + ): + json_efuse[e.name] = { + "name": e.name, + "value": base_value if readable else value, + "readable": readable, + "writeable": writeable, + "description": e.description, + "category": e.category, + "block": e.block, + "word": e.word, + "pos": e.pos, + "efuse_type": e.efuse_type, + "bit_len": e.bit_len, + } + if human_output and not value_only: + # Remove empty category when filtered efuses have none to show + if do_filtering and summary_efuse[-1] == f"{category.title()} fuses:": + summary_efuse.pop() + else: + summary_efuse.append("") + if human_output and not value_only: + summary_efuse.append(self.efuses.summary()) + warnings = self.efuses.get_coding_scheme_warnings() + if warnings: + summary_efuse.append( + "WARNING: Coding scheme has encoding bit error warnings" ) - error |= efuses["SECURE_BOOT_EN"].get() and efuse_name in [ - "BLOCK_KEY0", - "BLOCK_KEY0_HI_128", - ] - if error: - raise esptool.FatalError( - "%s must be readable, stop this operation!" % efuse_name + if human_output: + for line in summary_efuse: + log.print(line, file=file) + if file != sys.stdout: + file.close() + log.print("Done") + elif format == "json": + json.dump(json_efuse, file, sort_keys=True, indent=4) + log.print("") + + def dump(self, format: str = "default", file_name: str | None = None): + """ + Dump raw eFuse data registers. + + Args: + format: Format to use for the dump. Available options are: + - "default": Print the dump to the console. + - "split": Dump each eFuse block to a separate file. + - "joint": Dump all eFuse blocks to a single file. + file_name: File to write the dump to. If not provided, the dump will + be printed to the console. + """ + to_console = file_name is None + dump_file: BinaryIO | TextIO = sys.stdout + + def output_block_to_file( + block: base_fields.EfuseBlockBase, f: BinaryIO | TextIO, to_console: bool + ): + block_dump = BitStream(block.get_bitstring()) + block_dump.byteswap() + if to_console: + f.write(block_dump.hex + "\n") + else: + block_dump.tofile(f) # type: ignore + + if format == "default": + if to_console: + # for "espefuse dump" cmd + for block in self.efuses.blocks: + block.print_block(block.get_bitstring(), "dump", debug=True) + return + else: + # for back compatibility to support + # "espefuse dump --file_name dump.bin" + format = "split" + + if format == "split": + # each eFuse block is placed into its own file + for block in self.efuses.blocks: + if not to_console: + fname, fextension = os.path.splitext(file_name) # type: ignore + file_dump_name = f"{fname}{block.id}{fextension}" + log.print(f"Dump eFuse block{block.id} -> {file_dump_name}") + dump_file = open(file_dump_name, "wb") + output_block_to_file(block, dump_file, to_console) + if not to_console: + dump_file.close() + elif format == "joint": + # all eFuse blocks are stored in one file + if not to_console: + log.print(f"Dump eFuse blocks -> {file_name}") + dump_file = open(file_name, "wb") # type: ignore + for block in self.efuses.blocks: + output_block_to_file(block, dump_file, to_console) + if not to_console: + dump_file.close() + + def burn_efuse(self, name_value_pairs: dict[str, str], force: bool = False): + """ + Burn eFuses. + + Args: + name_value_pairs: Dictionary of eFuse names and values to burn. + force: If True, the burn will be performed even if the eFuse settings + are incompatible. + """ + + def print_attention(blocked_efuses_after_burn: list[str]): + if len(blocked_efuses_after_burn): + log.print( + " ATTENTION! This BLOCK uses NOT the NONE coding scheme " + "and after 'BURN', these efuses can not be burned in the feature:" + ) + for i in range(0, len(blocked_efuses_after_burn), 5): + log.print( + " ", + "".join("{}".format(blocked_efuses_after_burn[i : i + 5 :])), ) + + efuse_name_list = [name for name in name_value_pairs.keys()] + burn_efuses_list = [self.efuses[name] for name in efuse_name_list] + old_value_list = [self.efuses[name].get_raw() for name in efuse_name_list] + new_value_list = [value for value in name_value_pairs.values()] + util.check_duplicate_name_in_list(efuse_name_list) + + attention = "" + log.print("The eFuses to burn:") + for block in self.efuses.blocks: + burn_list_a_block = [e for e in burn_efuses_list if e.block == block.id] + if len(burn_list_a_block): + log.print(" from BLOCK%d" % (block.id)) + for field in burn_list_a_block: + log.print(" - %s" % (field.name)) + if ( + self.efuses.blocks[field.block].get_coding_scheme() + != self.efuses.REGS.CODING_SCHEME_NONE + ): + using_the_same_block_names = [ + e.name for e in self.efuses if e.block == field.block + ] + wr_names = [e.name for e in burn_list_a_block] + blocked_efuses_after_burn = [ + name + for name in using_the_same_block_names + if name not in wr_names + ] + attention = " (see 'ATTENTION!' above)" + if attention: + print_attention(blocked_efuses_after_burn) + + log.print(f"\nBurning eFuses{attention}:") + for efuse, new_value in zip(burn_efuses_list, new_value_list): + log.print( + f" - '{efuse.name}' ({efuse.description}) " + f"{efuse.get_bitstring()} -> {efuse.convert_to_bitstring(new_value)}" + ) + efuse.save(new_value) + + log.print() + if "ENABLE_SECURITY_DOWNLOAD" in efuse_name_list: + log.print( + "ENABLE_SECURITY_DOWNLOAD -> 1: eFuses will not be read back " + "for confirmation because this mode disables " + "any SRAM and register operations." + ) + log.print(" espefuse will not work.") + log.print( + " esptool can read/write only flash." + ) + + if "DIS_DOWNLOAD_MODE" in efuse_name_list: + log.print( + "DIS_DOWNLOAD_MODE -> 1: eFuses will not be read back for confirmation " + "because this mode disables any communication with the chip." + ) + log.print( + " espefuse/esptool will not work because " + "they will not be able to connect to the chip." + ) + + if ( + self.esp.CHIP_NAME == "ESP32" + and self.esp.get_chip_revision() >= 300 + and "UART_DOWNLOAD_DIS" in efuse_name_list + ): + log.print( + "UART_DOWNLOAD_DIS -> 1: eFuses will be read for confirmation, " + "but after that connection to the chip will become impossible." + ) + log.print(" espefuse/esptool will not work.") + + if self.efuses.is_efuses_incompatible_for_burn(): + if force: + log.print("Ignore incompatible eFuse settings.") + else: + raise esptool.FatalError( + "Incompatible eFuse settings detected, abort. " + "(use --force flag to skip it)." + ) + + if not self.efuses.burn_all(check_batch_mode=True): + return + + log.print("Checking eFuses...") + raise_error = False + for efuse, old_value, new_value in zip( + burn_efuses_list, old_value_list, new_value_list + ): + if not efuse.is_readable(): + log.print( + f"Efuse {efuse.name} is read-protected. " + "Read back the burn value is not possible." + ) + else: + new_value = efuse.convert_to_bitstring(new_value) + burned_value = efuse.get_bitstring() + if burned_value != new_value: + log.print( + burned_value, + "->", + new_value, + f"Efuse {efuse.name} failed to burn. Protected?", + ) + raise_error = True + if raise_error: + raise esptool.FatalError("The burn was not successful.") + else: + log.print("Successful.") + + def read_protect_efuse(self, efuse_names: list[str]): + """ + Disable readback for the eFuse with the specified name. + + Args: + efuse_names: List of eFuse names to read-protect. + """ + util.check_duplicate_name_in_list(efuse_names) + + for efuse_name in efuse_names: + efuse = self.efuses[efuse_name] + if not efuse.is_readable(): + log.print(f"Efuse {efuse.name} is already read protected") else: - for block in efuses.Blocks.BLOCKS: - block = efuses.Blocks.get(block) - if block.name == efuse_name and block.key_purpose is not None: - if not efuses[block.key_purpose].need_rd_protect( - efuses[block.key_purpose].get() - ): + if self.esp.CHIP_NAME == "ESP32": + if ( + efuse_name == "BLOCK2" + and not self.efuses["ABS_DONE_0"].get() + and self.esp.get_chip_revision() >= 300 + ): + if self.efuses["ABS_DONE_1"].get(): raise esptool.FatalError( - "%s must be readable, stop this operation!" % efuse_name + "Secure Boot V2 is on (ABS_DONE_1 = True), " + "BLOCK2 must be readable, stop this operation!" ) - break - # make full list of which efuses will be disabled - # (ie share a read disable bit) - all_disabling = [ - e for e in efuses if e.read_disable_bit == efuse.read_disable_bit - ] - names = ", ".join(e.name for e in all_disabling) - print( - "Permanently read-disabling efuse%s %s" - % ("s" if len(all_disabling) > 1 else "", names) + else: + log.print( + "If Secure Boot V2 is used, BLOCK2 must be readable, " + "please stop this operation!" + ) + elif self.esp.CHIP_NAME == "ESP32-C2": + error = ( + not self.efuses["XTS_KEY_LENGTH_256"].get() + and efuse_name == "BLOCK_KEY0" + ) + error |= self.efuses["SECURE_BOOT_EN"].get() and efuse_name in [ + "BLOCK_KEY0", + "BLOCK_KEY0_HI_128", + ] + if error: + raise esptool.FatalError( + "%s must be readable, stop this operation!" % efuse_name + ) + else: + for block in self.efuses.Blocks.BLOCKS: + block = self.efuses.Blocks.get(block) + if block.name == efuse_name and block.key_purpose is not None: + if not self.efuses[block.key_purpose].need_rd_protect( + self.efuses[block.key_purpose].get() + ): + raise esptool.FatalError( + "%s must be readable, stop this operation!" + % efuse_name + ) + break + # make full list of which efuses will be disabled + # (ie share a read disable bit) + all_disabling = [ + e + for e in self.efuses + if e.read_disable_bit == efuse.read_disable_bit + ] + names = ", ".join(e.name for e in all_disabling) + log.print( + "Permanently read-disabling eFuse%s %s" + % ("s" if len(all_disabling) > 1 else "", names) + ) + efuse.disable_read() + + if not self.efuses.burn_all(check_batch_mode=True): + return + + log.print("Checking eFuses...") + raise_error = False + for efuse_name in efuse_names: + efuse = self.efuses[efuse_name] + if efuse.is_readable(): + log.print(f"Efuse {efuse.name} is not read-protected.") + raise_error = True + if raise_error: + raise esptool.FatalError("The burn was not successful.") + else: + log.print("Successful.") + + def write_protect_efuse(self, efuse_names: list[str]): + """ + Disable writing to the eFuse with the specified name. + + Args: + efuse_names: List of eFuse names to write-protect. + """ + util.check_duplicate_name_in_list(efuse_names) + for efuse_name in efuse_names: + efuse = self.efuses[efuse_name] + if not efuse.is_writeable(): + log.print(f"Efuse {efuse.name} is already write protected.") + else: + # make full list of which efuses will be disabled + # (ie share a write disable bit) + all_disabling = [ + e + for e in self.efuses + if e.write_disable_bit == efuse.write_disable_bit + ] + names = ", ".join(e.name for e in all_disabling) + log.print( + "Permanently write-disabling eFuse%s %s" + % ("s" if len(all_disabling) > 1 else "", names) + ) + efuse.disable_write() + + if not self.efuses.burn_all(check_batch_mode=True): + return + + log.print("Checking eFuses...") + raise_error = False + for efuse_name in efuse_names: + efuse = self.efuses[efuse_name] + if efuse.is_writeable(): + log.print(f"Efuse {efuse.name} is not write-protected.") + raise_error = True + if raise_error: + raise esptool.FatalError("The burn was not successful.") + else: + log.print("Successful.") + + def burn_block_data( + self, + block_names: list[str], + datafiles: list[BinaryIO], + offset: int = 0, + ) -> None: + """ + Burn non-key data to EFUSE blocks. + + Don't use this command to burn key data for Flash Encryption or ESP32 + Secure Boot V1, as the byte order of keys is swapped (use burn-key). + + Args: + block_names: List of eFuse block names to burn data to. + datafiles: List of files to read data from. + offset: Byte offset in the eFuse block to start writing data at. + """ + block_name_list = block_names[ + 0 : len([name for name in block_names if name is not None]) : + ] + datafile_list = datafiles[ + 0 : len([name for name in datafiles if name is not None]) : + ] + + util.check_duplicate_name_in_list(block_name_list) + if offset and len(block_name_list) > 1: + raise esptool.FatalError( + "The 'offset' option is not applicable when a few blocks are passed. " + "With 'offset', should only one block be used." ) - efuse.disable_read() - - if not efuses.burn_all(check_batch_mode=True): - return - - print("Checking efuses...") - raise_error = False - for efuse_name in args.efuse_name: - efuse = efuses[efuse_name] - if efuse.is_readable(): - print("Efuse %s is not read-protected." % efuse.name) - raise_error = True - if raise_error: - raise esptool.FatalError("The burn was not successful.") - else: - print("Successful") - - -def write_protect_efuse(esp, efuses, args): - util.check_duplicate_name_in_list(args.efuse_name) - for efuse_name in args.efuse_name: - efuse = efuses[efuse_name] - if not efuse.is_writeable(): - print("Efuse %s is already write protected" % efuse.name) else: - # make full list of which efuses will be disabled - # (ie share a write disable bit) - all_disabling = [ - e for e in efuses if e.write_disable_bit == efuse.write_disable_bit - ] - names = ", ".join(e.name for e in all_disabling) - print( - "Permanently write-disabling efuse%s %s" - % ("s" if len(all_disabling) > 1 else "", names) + if offset: + num_block = self.efuses.get_index_block_by_name(block_name_list[0]) + block: base_fields.EfuseBlockBase = self.efuses.blocks[num_block] + num_bytes = block.get_block_len() + if offset >= num_bytes: + raise esptool.FatalError( + f"Invalid offset: the block{block.id} only holds " + f"{num_bytes} bytes." + ) + if len(block_name_list) != len(datafile_list): + raise esptool.FatalError( + f"The number of block_name ({len(block_name_list)}) and " + f"datafile ({len(datafile_list)}) should be the same." ) - efuse.disable_write() - - if not efuses.burn_all(check_batch_mode=True): - return - - print("Checking efuses...") - raise_error = False - for efuse_name in args.efuse_name: - efuse = efuses[efuse_name] - if efuse.is_writeable(): - print("Efuse %s is not write-protected." % efuse.name) - raise_error = True - if raise_error: - raise esptool.FatalError("The burn was not successful.") - else: - print("Successful") - - -def burn_block_data(esp, efuses, args): - block_name_list = args.block[ - 0 : len([name for name in args.block if name is not None]) : - ] - datafile_list = args.datafile[ - 0 : len([name for name in args.datafile if name is not None]) : - ] - efuses.force_write_always = args.force_write_always - - util.check_duplicate_name_in_list(block_name_list) - if args.offset and len(block_name_list) > 1: - raise esptool.FatalError( - "The 'offset' option is not applicable when a few blocks are passed. " - "With 'offset', should only one block be used." - ) - else: - offset = args.offset - if offset: - num_block = efuses.get_index_block_by_name(block_name_list[0]) - block = efuses.blocks[num_block] + + for block_name, datafile in zip(block_name_list, datafile_list): + num_block = self.efuses.get_index_block_by_name(block_name) + block = self.efuses.blocks[num_block] + data = datafile.read() num_bytes = block.get_block_len() - if offset >= num_bytes: + if offset != 0: + data = (b"\x00" * offset) + data + data = data + (b"\x00" * (num_bytes - len(data))) + if len(data) != num_bytes: raise esptool.FatalError( - "Invalid offset: the block%d only holds %d bytes." - % (block.id, num_bytes) + f"Data does not fit: the block{block.id} size is " + f"{num_bytes} bytes, data file is {len(data)} bytes, " + f"offset {offset}." ) - if len(block_name_list) != len(datafile_list): - raise esptool.FatalError( - "The number of block_name (%d) and datafile (%d) should be the same." - % (len(block_name_list), len(datafile_list)) - ) + log.print( + "[{:02}] {:20} size={:02} bytes, offset={:02} - > [{}].".format( + block.id, block.name, len(data), offset, util.hexify(data, " ") + ) + ) + block.save(data) + + if not self.efuses.burn_all(check_batch_mode=True): + return + log.print("Successful.") - for block_name, datafile in zip(block_name_list, datafile_list): - num_block = efuses.get_index_block_by_name(block_name) - block = efuses.blocks[num_block] - data = datafile.read() - num_bytes = block.get_block_len() - if offset != 0: - data = (b"\x00" * offset) + data - data = data + (b"\x00" * (num_bytes - len(data))) - if len(data) != num_bytes: + def burn_bit(self, block: str, bit_number: list[int]): + """ + Burn a single bit to the eFuse with the specified name. + + Args: + block: Name of the eFuse block to burn the bit to. + bit_number: List of bit numbers to burn. + """ + num_block = self.efuses.get_index_block_by_name(block) + block_obj: base_fields.EfuseBlockBase = self.efuses.blocks[num_block] + data_block = BitStream(block_obj.get_block_len() * 8) + data_block.set(0) + try: + data_block.set(True, bit_number) + except IndexError: raise esptool.FatalError( - "Data does not fit: the block%d size is %d bytes, " - "data file is %d bytes, offset %d" - % (block.id, num_bytes, len(data), offset) - ) - print( - "[{:02}] {:20} size={:02} bytes, offset={:02} - > [{}].".format( - block.id, block.name, len(data), offset, util.hexify(data, " ") + f"{block} has bit_number in [0..{data_block.len - 1}]" ) + data_block.reverse() + log.print( + "bit_number: " + "[%-03d]........................................................[0]" + % (data_block.len - 1) ) - block.save(data) - - if not efuses.burn_all(check_batch_mode=True): - return - print("Successful") - - -def burn_bit(esp, efuses, args): - efuses.force_write_always = args.force_write_always - num_block = efuses.get_index_block_by_name(args.block) - block = efuses.blocks[num_block] - data_block = BitStream(block.get_block_len() * 8) - data_block.set(0) - try: - data_block.set(True, args.bit_number) - except IndexError: - raise esptool.FatalError( - "%s has bit_number in [0..%d]" % (args.block, data_block.len - 1) + log.print("BLOCK%-2d :" % block_obj.id, data_block) + block_obj.print_block(data_block, "regs_to_write", debug=True) + block_obj.save(data_block.bytes[::-1]) + + if not self.efuses.burn_all(check_batch_mode=True): + return + log.print("Successful.") + + def get_error_summary(self): + self.efuses.get_coding_scheme_warnings() + error_in_blocks = any( + blk.fail or blk.num_errors != 0 for blk in self.efuses.blocks ) - data_block.reverse() - print( - "bit_number: " - "[%-03d]........................................................[0]" - % (data_block.len - 1) - ) - print("BLOCK%-2d :" % block.id, data_block) - block.print_block(data_block, "regs_to_write", debug=True) - block.save(data_block.bytes[::-1]) - - if not efuses.burn_all(check_batch_mode=True): - return - print("Successful") - - -def get_error_summary(efuses): - efuses.get_coding_scheme_warnings() - error_in_blocks = any(blk.fail or blk.num_errors != 0 for blk in efuses.blocks) - if not error_in_blocks: - return False - writable = True - for blk in efuses.blocks: - if blk.fail or blk.num_errors: - if blk.id == 0: - for field in efuses: - if field.block == blk.id and (field.fail or field.num_errors): - wr = "writable" if field.is_writeable() else "not writable" - writable &= wr == "writable" - name = field.name - val = field.get() - print(f"BLOCK{field.block:<2}: {name:<40} = {val:<8} ({wr})") - else: - wr = "writable" if blk.is_writeable() else "not writable" - writable &= wr == "writable" - name = f"{blk.name} [ERRORS:{blk.num_errors} FAIL:{int(blk.fail)}]" - val = str(blk.get_bitstring()) - print(f"BLOCK{blk.id:<2}: {name:<40} = {val:<8} ({wr})") - if not writable and error_in_blocks: - print("Not all errors can be fixed because some fields are write-protected!") - return True - - -def check_error(esp, efuses, args): - error_in_blocks = get_error_summary(efuses) - if args.recovery and error_in_blocks: - confirmed = False - for block in reversed(efuses.blocks): - if block.fail or block.num_errors > 0: - if not block.get_bitstring().all(False): - block.save(block.get_bitstring().bytes[::-1]) - if not confirmed: - confirmed = True - efuses.confirm( - "Recovery of block coding errors", args.do_not_confirm - ) - block.burn() - if confirmed: - efuses.update_efuses() - error_in_blocks = get_error_summary(efuses) - if error_in_blocks: - raise esptool.FatalError("Error(s) were detected in eFuses") - print("No errors detected") + if not error_in_blocks: + return False + writable = True + for blk in self.efuses.blocks: + if blk.fail or blk.num_errors: + if blk.id == 0: + for field in self.efuses: + if field.block == blk.id and (field.fail or field.num_errors): + wr = "writable" if field.is_writeable() else "not writable" + writable &= wr == "writable" + name = field.name + val = field.get() + log.print( + f"BLOCK{field.block:<2}: {name:<40} = {val:<8} ({wr})" + ) + else: + wr = "writable" if blk.is_writeable() else "not writable" + writable &= wr == "writable" + name = f"{blk.name} [ERRORS:{blk.num_errors} FAIL:{int(blk.fail)}]" + val = str(blk.get_bitstring()) + log.print(f"BLOCK{blk.id:<2}: {name:<40} = {val:<8} ({wr})") + if not writable and error_in_blocks: + log.print( + "Not all errors can be fixed because some fields are write-protected!" + ) + return True + + def check_error(self, recovery: bool = False, do_not_confirm: bool = False): + """ + Check for errors in the eFuse blocks. + + Args: + recovery: Recovery of BLOCKs after encoding errors. + do_not_confirm: If True, the confirmation will be skipped. + """ + error_in_blocks = self.get_error_summary() + if recovery and error_in_blocks: + confirmed = False + for block in reversed(self.efuses.blocks): + if block.fail or block.num_errors > 0: + if not block.get_bitstring().all(False): + block.save(block.get_bitstring().bytes[::-1]) + if not confirmed: + confirmed = True + self.efuses.confirm( + "Recovery of block coding errors", do_not_confirm + ) + block.burn() + if confirmed: + self.efuses.update_efuses() + error_in_blocks = self.get_error_summary() + if error_in_blocks: + raise esptool.FatalError("Error(s) were detected in eFuses.") + log.print("No errors detected.") + + def burn_custom_mac(self, mac: str | bytes): + """ + Burn a 48-bit Custom MAC Address. + + Args: + mac (str | bytes): Custom MAC Address to burn. e.g. "aa:cd:ef:11:22:33" or + b'\xaa\xcd\xef\x11\x22\x33' + """ + mac = self._convert_mac_to_bytes(mac) + + self.efuses["CUSTOM_MAC"].save(mac) + if not self.efuses.burn_all(check_batch_mode=True): + return + self.get_custom_mac() + log.print("Successful.") + + def get_custom_mac(self): + """Get the Custom MAC Address.""" + log.print(f"Custom MAC Address: {self.efuses['CUSTOM_MAC'].get()}") + + def set_flash_voltage(self, voltage: str): + """ + Set the Flash Voltage. Available only for selected chips. + + Args: + voltage: Voltage to set. Available options are: "1.8V", "3.3V", "OFF" + """ + raise esptool.FatalError("set_flash_voltage is not supported for this chip") + + def adc_info(self): + """Display information about ADC calibration data stored in eFuse.""" + raise NotImplementedError("adc-info is not implemented for this chip") + + @abstractmethod + def burn_key(self, *args, **kwargs): + """Burn a key to the eFuse. Exact implementation is chip-specific.""" + pass + + @abstractmethod + def burn_key_digest(self, *args, **kwargs): + """Burn a key digest to the eFuse. Exact implementation is chip-specific.""" + pass diff --git a/tools/esptool_py/espefuse/efuse/csv_table_parser.py b/tools/esptool_py/espefuse/efuse/csv_table_parser.py index 4bebbb02ab..4fd4cba090 100644 --- a/tools/esptool_py/espefuse/efuse/csv_table_parser.py +++ b/tools/esptool_py/espefuse/efuse/csv_table_parser.py @@ -8,6 +8,8 @@ import re import sys +from esptool.logger import log + class CSVFuseTable(list): @classmethod @@ -94,7 +96,7 @@ def verify_duplicate_name(self): field_name = p.field_name + p.group if field_name != "" and len(duplicates.intersection([field_name])) != 0: fl_error = True - print( + log.print( f"Field at {p.field_name}, {p.efuse_block}, " f"{p.bit_start}, {p.bit_count} have duplicate field_name" ) @@ -137,7 +139,9 @@ def check(p, n): def print_error(p, n, state): raise InputError( - f"Field at {p.field_name}, {p.efuse_block}, {p.bit_start}, {p.bit_count} {state} {n.field_name}, {n.efuse_block}, {n.bit_start}, {n.bit_count}" + f"Field at {p.field_name}, {p.efuse_block}, {p.bit_start}, " + f"{p.bit_count} {state} {n.field_name}, {n.efuse_block}, " + f"{n.bit_start}, {n.bit_count}" ) for p in self: @@ -238,7 +242,8 @@ def verify(self, type_table): if self.bit_start + self.bit_count > max_bits: raise ValidationError( self, - f"The field is outside the boundaries(max_bits = {max_bits}) of the {self.efuse_block} block", + f"The field is outside the boundaries (max_bits = {max_bits}) " + f"of the {self.efuse_block} block", ) def get_bit_count(self, check_define=True): diff --git a/tools/esptool_py/espefuse/efuse/emulate_efuse_controller_base.py b/tools/esptool_py/espefuse/efuse/emulate_efuse_controller_base.py index 232bfaad83..4fd6fee633 100644 --- a/tools/esptool_py/espefuse/efuse/emulate_efuse_controller_base.py +++ b/tools/esptool_py/espefuse/efuse/emulate_efuse_controller_base.py @@ -7,6 +7,7 @@ import re from bitstring import BitStream +from esptool.logger import log class EmulateEfuseControllerBase(object): @@ -96,7 +97,7 @@ def copy_blocks_wr_regs_to_rd_regs(self, updated_block=None): continue data = self.read_block(blk.id, wr_regs=True) if self.debug: - print(blk.name, data.hex) + log.print(blk.name, data.hex) plain_data = self.handle_coding_scheme(blk, data) plain_data = self.check_wr_protection_area(blk.id, plain_data) self.update_block(blk, plain_data) diff --git a/tools/esptool_py/espefuse/efuse/esp32/__init__.py b/tools/esptool_py/espefuse/efuse/esp32/__init__.py index a3b55a8023..641fadaea5 100644 --- a/tools/esptool_py/espefuse/efuse/esp32/__init__.py +++ b/tools/esptool_py/espefuse/efuse/esp32/__init__.py @@ -1,3 +1,5 @@ from . import operations from .emulate_efuse_controller import EmulateEfuseController from .fields import EspEfuses + +commands = operations.ESP32Commands diff --git a/tools/esptool_py/espefuse/efuse/esp32/emulate_efuse_controller.py b/tools/esptool_py/espefuse/efuse/esp32/emulate_efuse_controller.py index 03011fa59f..4f53f10c1f 100644 --- a/tools/esptool_py/espefuse/efuse/esp32/emulate_efuse_controller.py +++ b/tools/esptool_py/espefuse/efuse/esp32/emulate_efuse_controller.py @@ -8,6 +8,7 @@ from .mem_definition import EfuseDefineBlocks, EfuseDefineFields, EfuseDefineRegisters from ..emulate_efuse_controller_base import EmulateEfuseControllerBase, FatalError +from esptool.logger import log class EmulateEfuseController(EmulateEfuseControllerBase): @@ -49,7 +50,7 @@ def wait_idle(): if self.read_reg(self.REGS.EFUSE_REG_CMD) == 0: return raise FatalError( - "Timed out waiting for Efuse controller command to complete" + "Timed out waiting for eFuse controller command to complete" ) self.write_reg(self.REGS.EFUSE_REG_CMD, self.REGS.EFUSE_CMD_WRITE) @@ -89,7 +90,7 @@ def write_raw_coding_scheme(self, value): raise FatalError( "Error during a burning process to set the new coding scheme" ) - print("Set coding scheme = %d" % self.read_raw_coding_scheme()) + log.print(f"Set coding scheme = {self.read_raw_coding_scheme()}") def get_bitlen_of_block(self, blk, wr=False): if blk.id == 0: @@ -104,9 +105,7 @@ def get_bitlen_of_block(self, blk, wr=False): else: return 32 * blk.len * 3 // 4 else: - raise FatalError( - "The {} coding scheme is not supported".format(coding_scheme) - ) + raise FatalError(f"The {coding_scheme} coding scheme is not supported") def handle_coding_scheme(self, blk, data): # it verifies the coding scheme part of data and returns just data @@ -125,7 +124,7 @@ def handle_coding_scheme(self, blk, data): xor_res ^= byte_data mul_res += (i + 1) * bin(byte_data).count("1") if xor_res != chunk_data[6] or mul_res != chunk_data[7]: - print( + log.print( "xor_res ", xor_res, chunk_data[6], diff --git a/tools/esptool_py/espefuse/efuse/esp32/fields.py b/tools/esptool_py/espefuse/efuse/esp32/fields.py index c1a625bcb2..666076b495 100644 --- a/tools/esptool_py/espefuse/efuse/esp32/fields.py +++ b/tools/esptool_py/espefuse/efuse/esp32/fields.py @@ -9,6 +9,7 @@ import time import esptool +from esptool import log from .mem_definition import EfuseDefineBlocks, EfuseDefineFields, EfuseDefineRegisters from .. import base_fields @@ -65,9 +66,6 @@ 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, @@ -76,14 +74,12 @@ def __init__( do_not_confirm=False, extend_efuse_table=None, ): + super().__init__(esp, skip_connect, debug, do_not_confirm, extend_efuse_table) 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": raise esptool.FatalError( "Expected the 'esp' param for ESP32 chip but got for '%s'." @@ -174,8 +170,8 @@ def read_coding_scheme(self): self.coding_scheme = coding_scheme def print_status_regs(self): - print("") - print( + log.print("") + log.print( "{:27} 0x{:08x}".format( "EFUSE_REG_DEC_STATUS", self.read_reg(self.REGS.EFUSE_REG_DEC_STATUS) ) @@ -212,7 +208,7 @@ def wait_efuse_idle(self): if self.read_reg(self.REGS.EFUSE_REG_CMD) == 0: return raise esptool.FatalError( - "Timed out waiting for Efuse controller command to complete" + "Timed out waiting for eFuse controller command to complete" ) def efuse_read(self): @@ -234,9 +230,9 @@ def get_coding_scheme_warnings(self, silent=False): block.num_errors = 0 block.fail = err != 0 if not silent and block.fail: - print( - "Error(s) in BLOCK%d [ERRORS:%d FAIL:%d]" - % (block.id, block.num_errors, block.fail) + log.print( + f"Error(s) in BLOCK{block.id} " + f"[ERRORS:{block.num_errors} FAIL:{block.fail}]" ) if (self.debug or err) and not silent: self.print_status_regs() @@ -273,7 +269,7 @@ class EfuseMacField(EfuseField): (if MAC_VERSION == 1 then the CUSTOM_MAC is used) """ - def check_format(self, new_value_str): + def check_format(self, new_value_str: str | None): if new_value_str is None: raise esptool.FatalError( "Required MAC Address in AA:CD:EF:01:02:03 format!" @@ -337,10 +333,8 @@ def get(self, from_read=True): def save(self, new_value): def print_field(e, new_value): - print( - " - '{}' ({}) {} -> {}".format( - e.name, e.description, e.get_bitstring(), new_value - ) + log.print( + f" - '{e.name}' ({e.description}) {e.get_bitstring()} -> {new_value}" ) if self.name == "CUSTOM_MAC": @@ -357,9 +351,7 @@ def print_field(e, new_value): if mac_version.get() != 1: if not self.parent.force_write_always: raise esptool.FatalError( - "MAC_VERSION = {}, should be 0 or 1.".format( - mac_version.get() - ) + f"MAC_VERSION = {mac_version.get()}, should be 0 or 1." ) bitarray_mac = self.convert_to_bitstring(new_value) @@ -407,7 +399,7 @@ def get(self, from_read=True): return (hi_bits << 3) + lo_bits def save(self, new_value): - raise esptool.FatalError("Burning %s is not supported" % self.name) + raise esptool.FatalError(f"Burning {self.name} is not supported.") class EfuseSpiPinField(EfuseField): @@ -429,8 +421,7 @@ def check_format(self, new_value_str): ) elif new_value_int > 33: raise esptool.FatalError( - "IO pin %d cannot be set for SPI flash. 0-29, 32 & 33 only." - % new_value_int + f"IO pin {new_value_int} cannot be set for SPI flash. 0-29, 32 & 33 only." ) elif new_value_int in [32, 33]: return str(new_value_int - 2) diff --git a/tools/esptool_py/espefuse/efuse/esp32/operations.py b/tools/esptool_py/espefuse/efuse/esp32/operations.py index e0f419a1c1..8138377cd0 100644 --- a/tools/esptool_py/espefuse/efuse/esp32/operations.py +++ b/tools/esptool_py/espefuse/efuse/esp32/operations.py @@ -1,365 +1,327 @@ # This file includes the operations with eFuses for ESP32 chip # -# SPDX-FileCopyrightText: 2020-2022 Espressif Systems (Shanghai) CO LTD +# SPDX-FileCopyrightText: 2020-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 +from typing import BinaryIO +import rich_click as click import espsecure import esptool +from esptool.logger import log -from . import fields +from .mem_definition import EfuseDefineBlocks from .. import util +from .fields import EspEfuses from ..base_operations import ( - add_common_commands, + BaseCommands, + NonCompositeTuple, + TupleParameter, add_force_write_always, add_show_sensitive_info_option, - burn_bit, - burn_block_data, - burn_efuse, - check_error, - dump, - read_protect_efuse, - summary, - write_protect_efuse, ) -def add_commands(subparsers, efuses): - add_common_commands(subparsers, efuses) - p = subparsers.add_parser( - "burn_key", - help="Burn a 256-bit key to EFUSE: %s" % ", ".join(efuses.BLOCKS_FOR_KEYS), - ) - p.add_argument( - "--no-protect-key", - help="Disable default read- and write-protecting of the key. " - "If this option is not set, once the key is flashed " - "it cannot be read back or changed.", - action="store_true", - ) - add_force_write_always(p) - add_show_sensitive_info_option(p) - p.add_argument( - "block", - help='Key block to burn. "flash_encryption" (block1), ' - '"secure_boot_v1" (block2), "secure_boot_v2" (block2)', - action="append", - choices=efuses.BLOCKS_FOR_KEYS, - ) - p.add_argument( - "keyfile", - help="File containing 256 bits of binary key data", - action="append", - type=argparse.FileType("rb"), - ) - for _ in efuses.BLOCKS_FOR_KEYS: - p.add_argument( - "block", - help='Key block to burn. "flash_encryption" (block1), ' - '"secure_boot_v1" (block2), "secure_boot_v2" (block2)', - metavar="BLOCK", - nargs="?", - action="append", - choices=efuses.BLOCKS_FOR_KEYS, +class ESP32Commands(BaseCommands): + CHIP_NAME = "ESP32" + efuse_lib = EspEfuses + + ################################### CLI definitions ################################### + + def add_cli_commands(self, cli: click.Group): + super().add_cli_commands(cli) + blocks_for_keys = EfuseDefineBlocks().get_blocks_for_keys() + + @cli.command( + "burn-key", + help="Burn a 256-bit key to EFUSE. Arguments are pairs of block name and " + "key file, containing 256 bits of binary key data.\n\n" + f"Block is one of: [{', '.join(blocks_for_keys)}]", + ) + @click.argument( + "block_keyfile", + metavar=" ", + cls=TupleParameter, + required=True, + nargs=-1, + max_arity=len(blocks_for_keys), + type=NonCompositeTuple([click.Choice(blocks_for_keys), click.File("rb")]), + ) + @click.option( + "--no-protect-key", + is_flag=True, + help="Disable the default read- and write-protection of the key. " + "If this option is not set, once the key is flashed " + "it cannot be read back or changed.", + ) + @add_force_write_always + @add_show_sensitive_info_option + @click.pass_context + def burn_key_cli( + ctx, block_keyfile, no_protect_key, show_sensitive_info, **kwargs + ): + block, keyfile = zip(*block_keyfile) + show_sensitive_info = ctx.show_sensitive_info + self.burn_key(block, keyfile, no_protect_key, show_sensitive_info) + + @cli.command( + "burn-key-digest", + short_help="Parse a RSA public key and burn the digest.", + help="Parse a RSA public key and burn the digest to eFuse for use with Secure Boot V2.", + ) + @click.argument("keyfile", type=click.File("rb")) + @click.option( + "--no-protect-key", + is_flag=True, + help="Disable the default write-protection of the key digest. " + "If this option is not set, once the key is flashed it cannot be changed.", ) - p.add_argument( - "keyfile", - help="File containing 256 bits of binary key data", - metavar="KEYFILE", - nargs="?", - action="append", - type=argparse.FileType("rb"), + @add_force_write_always + @add_show_sensitive_info_option + @click.pass_context + def burn_key_digest_cli( + ctx, keyfile, no_protect_key, show_sensitive_info, **kwargs + ): + kwargs["show_sensitive_info"] = ctx.show_sensitive_info + self.burn_key_digest(keyfile, no_protect_key, show_sensitive_info) + + @cli.command( + "set-flash-voltage", + short_help="Permanently set the internal flash voltage regulator.", ) + @click.argument("voltage", type=click.Choice(["1.8V", "3.3V", "OFF"])) + def set_flash_voltage_cli(voltage): + """Permanently set the internal flash voltage regulator to either 1.8V, 3.3V or OFF. + This means GPIO12 can be high or low at reset without changing the flash voltage.""" + self.set_flash_voltage(voltage) + + ###################################### Commands ###################################### + + def get_custom_mac(self): + version = self.efuses["MAC_VERSION"].get() + if version > 0: + log.print( + f"Custom MAC Address version {version}: {self.efuses['CUSTOM_MAC'].get()}" + ) + else: + log.print("Custom MAC Address is not set in the device.") + + def set_flash_voltage(self, voltage: str): + sdio_force = self.efuses["XPD_SDIO_FORCE"] + sdio_tieh = self.efuses["XPD_SDIO_TIEH"] + sdio_reg = self.efuses["XPD_SDIO_REG"] - burn_key_digest = subparsers.add_parser( - "burn_key_digest", - help="Parse a RSA public key and burn the digest " - "to eFuse for use with Secure Boot V2", - ) - burn_key_digest.add_argument( - "keyfile", help="Key file to digest (PEM format)", type=argparse.FileType("rb") - ) - burn_key_digest.add_argument( - "--no-protect-key", - help="Disable default write-protecting of the key digest. " - "If this option is not set, once the key is flashed it cannot be changed.", - action="store_true", - ) - add_force_write_always(burn_key_digest) - add_show_sensitive_info_option(burn_key_digest) - - 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 GPIO12 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): - # Writing to BLK3: - # - MAC_VERSION = 1 - # - CUSTOM_MAC = AA:CD:EF:01:02:03 - # - CUSTOM_MAC_CRC = crc8(CUSTOM_MAC) - 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): - version = efuses["MAC_VERSION"].get() - if version > 0: - print( - "Custom MAC Address version {}: {}".format( - version, efuses["CUSTOM_MAC"].get() + # check efuses aren't burned in a way which makes this impossible + if voltage == "OFF" and sdio_reg.get() != 0: + raise esptool.FatalError( + "Can't set flash regulator to OFF as XPD_SDIO_REG eFuse is already burned." ) - ) - else: - print("Custom MAC Address is not set in the device.") + if voltage == "1.8V" and sdio_tieh.get() != 0: + raise esptool.FatalError( + "Can't set regulator to 1.8V is XPD_SDIO_TIEH eFuse is already burned." + ) -def set_flash_voltage(esp, efuses, args): - sdio_force = efuses["XPD_SDIO_FORCE"] - sdio_tieh = efuses["XPD_SDIO_TIEH"] - sdio_reg = efuses["XPD_SDIO_REG"] + if voltage == "OFF": + log.print( + "Disable internal flash voltage regulator (VDD_SDIO). " + "SPI flash will need to be powered from an external source.\n" + "The following eFuse is burned: XPD_SDIO_FORCE.\n" + "It is possible to later re-enable the internal regulator" + f"{'to 3.3V' if sdio_tieh.get() != 0 else 'to 1.8V or 3.3V'}" + "by burning an additional eFuse." + ) + elif voltage == "1.8V": + log.print( + "Set internal flash voltage regulator (VDD_SDIO) to 1.8V.\n" + "The following eFuses are burned: XPD_SDIO_FORCE, XPD_SDIO_REG.\n" + "It is possible to later increase the voltage to 3.3V (permanently) " + "by burning additional eFuse XPD_SDIO_TIEH." + ) + elif voltage == "3.3V": + log.print( + "Enable internal flash voltage regulator (VDD_SDIO) to 3.3V.\n" + "The following eFuses are burned: XPD_SDIO_FORCE, XPD_SDIO_REG, XPD_SDIO_TIEH." + ) - # check efuses aren't burned in a way which makes this impossible - if args.voltage == "OFF" and sdio_reg.get() != 0: - raise esptool.FatalError( - "Can't set flash regulator to OFF as XPD_SDIO_REG efuse is already burned" - ) + sdio_force.save(1) # Disable GPIO12 + if voltage != "OFF": + sdio_reg.save(1) # Enable internal regulator + if voltage == "3.3V": + sdio_tieh.save(1) + log.print("VDD_SDIO setting complete.") + if not self.efuses.burn_all(check_batch_mode=True): + return + log.print("Successful.") + + def adc_info(self): + adc_vref = self.efuses["ADC_VREF"] + blk3_reserve = self.efuses["BLK3_PART_RESERVE"] + + vref_raw = adc_vref.get_raw() + if vref_raw == 0: + log.print("ADC VRef calibration: None (1100mV nominal)") + else: + log.print(f"ADC VRef calibration: {adc_vref.get()}mV") + + if blk3_reserve.get(): + log.print("ADC readings stored in eFuse BLOCK3:") + log.print( + f" ADC1 Low reading (150mV): {self.efuses['ADC1_TP_LOW'].get()}" + ) + log.print( + f" ADC1 High reading (850mV): {self.efuses['ADC1_TP_HIGH'].get()}" + ) + log.print( + f" ADC2 Low reading (150mV): {self.efuses['ADC2_TP_LOW'].get()}" + ) + log.print( + f" ADC2 High reading (850mV): {self.efuses['ADC2_TP_HIGH'].get()}" + ) - if args.voltage == "1.8V" and sdio_tieh.get() != 0: - raise esptool.FatalError( - "Can't set regulator to 1.8V is XPD_SDIO_TIEH efuse is already burned" - ) + def burn_key( + self, + block: list[str], + keyfile: list[BinaryIO], + no_protect_key: bool = False, + show_sensitive_info: bool = False, + ): + """Burn a 256-bit key to EFUSE. Arguments are pairs of block name and + key file, containing 256 bits of binary key data. + + Args: + block: List of eFuse block names to burn keys to. + keyfile: List of open files to read key data from. + no_protect_key: If True, the write protection will NOT be enabled. + show_sensitive_info: If True, the sensitive information will be shown. + """ + datafile_list = keyfile[ + 0 : len([keyfile for keyfile in keyfile if keyfile is not None]) : + ] + block_name_list = block[ + 0 : len([block for block in block if block is not None]) : + ] + + util.check_duplicate_name_in_list(block_name_list) + if len(block_name_list) != len(datafile_list): + raise esptool.FatalError( + f"The number of blocks ({len(block_name_list)}) " + f"and datafile ({len(datafile_list)}) should be the same." + ) - if args.voltage == "OFF": - msg = "Disable internal flash voltage regulator (VDD_SDIO). " - "SPI flash will need to be powered from an external source.\n" - "The following efuse is burned: XPD_SDIO_FORCE.\n" - "It is possible to later re-enable the internal regulator (%s) " % ( - "to 3.3V" if sdio_tieh.get() != 0 else "to 1.8V or 3.3V" - ) - "by burning an additional efuse" - elif args.voltage == "1.8V": - msg = "Set internal flash voltage regulator (VDD_SDIO) to 1.8V.\n" - "The following efuses are burned: XPD_SDIO_FORCE, XPD_SDIO_REG.\n" - "It is possible to later increase the voltage to 3.3V (permanently) " - "by burning additional efuse XPD_SDIO_TIEH" - elif args.voltage == "3.3V": - msg = "Enable internal flash voltage regulator (VDD_SDIO) to 3.3V.\n" - "The following efuses are burned: XPD_SDIO_FORCE, XPD_SDIO_REG, XPD_SDIO_TIEH." - print(msg) - sdio_force.save(1) # Disable GPIO12 - if args.voltage != "OFF": - sdio_reg.save(1) # Enable internal regulator - if args.voltage == "3.3V": - sdio_tieh.save(1) - print("VDD_SDIO setting complete.") - if not efuses.burn_all(check_batch_mode=True): - return - print("Successful") - - -def adc_info(esp, efuses, args): - adc_vref = efuses["ADC_VREF"] - blk3_reserve = efuses["BLK3_PART_RESERVE"] - - vref_raw = adc_vref.get_raw() - if vref_raw == 0: - print("ADC VRef calibration: None (1100mV nominal)") - else: - print("ADC VRef calibration: %dmV" % adc_vref.get()) - - if blk3_reserve.get(): - print("ADC readings stored in efuse BLOCK3:") - print(" ADC1 Low reading (150mV): %d" % efuses["ADC1_TP_LOW"].get()) - print(" ADC1 High reading (850mV): %d" % efuses["ADC1_TP_HIGH"].get()) - print(" ADC2 Low reading (150mV): %d" % efuses["ADC2_TP_LOW"].get()) - print(" ADC2 High reading (850mV): %d" % efuses["ADC2_TP_HIGH"].get()) - - -def burn_key(esp, efuses, args): - datafile_list = args.keyfile[ - 0 : len([keyfile for keyfile in args.keyfile if keyfile is not None]) : - ] - block_name_list = args.block[ - 0 : len([block for block in args.block if block is not None]) : - ] - efuses.force_write_always = args.force_write_always - no_protect_key = args.no_protect_key - - util.check_duplicate_name_in_list(block_name_list) - if len(block_name_list) != len(datafile_list): - raise esptool.FatalError( - "The number of blocks (%d) and datafile (%d) should be the same." - % (len(block_name_list), len(datafile_list)) - ) + log.print("Burn keys to blocks:") + for block_name, datafile in zip(block_name_list, datafile_list): + efuse = None + for blk in self.efuses.blocks: + if block_name == blk.name or block_name in blk.alias: + efuse = self.efuses[blk.name] + if efuse is None: + raise esptool.FatalError(f"Unknown block name - {block_name}.") + num_bytes = efuse.bit_len // 8 + data = datafile.read() + datafile.close() + revers_msg = None + if block_name in ("flash_encryption", "secure_boot_v1"): + revers_msg = "\tReversing the byte order..." + data = data[::-1] + log.print(f" - {efuse.name}", end=" ") + log.print( + "-> [{}]".format( + util.hexify(data, " ") + if show_sensitive_info + else " ".join(["??"] * len(data)) + ) + ) + if revers_msg: + log.print(revers_msg) + if len(data) != num_bytes: + raise esptool.FatalError( + f"Incorrect key file size {len(data)}. Key file must be {num_bytes}" + f" bytes ({num_bytes * 8} bits) of raw binary key data." + ) - print("Burn keys to blocks:") - for block_name, datafile in zip(block_name_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 - data = datafile.read() - revers_msg = None - if block_name in ("flash_encryption", "secure_boot_v1"): - revers_msg = "\tReversing the byte order" - data = data[::-1] - print(" - %s" % (efuse.name), end=" ") - print( - "-> [{}]".format( - util.hexify(data, " ") - if args.show_sensitive_info - else " ".join(["??"] * len(data)) + efuse.save(data) + + if block_name in ("flash_encryption", "secure_boot_v1"): + if not no_protect_key: + log.print("\tDisabling read to key block...") + efuse.disable_read() + + if not no_protect_key: + log.print("\tDisabling write to key block...") + efuse.disable_write() + log.print("") + + if no_protect_key: + log.print("Key is left unprotected as per --no-protect-key argument.") + + msg = "Burn keys in eFuse blocks.\n" + if no_protect_key: + msg += ( + "The key block will be left readable and writeable " + "(due to --no-protect-key)." ) - ) - if revers_msg: - print(revers_msg) - if len(data) != num_bytes: + else: + msg += ( + "The key block will be read and write protected " + "(no further changes or readback)." + ) + log.print(msg, "\n") + if not self.efuses.burn_all(check_batch_mode=True): + return + log.print("Successful.") + + def burn_key_digest( + self, + keyfile: BinaryIO, + no_protect_key: bool = False, + show_sensitive_info: bool = False, + ): + """Parse a RSA public key and burn the digest to eFuse for use with Secure Boot V2. + + Args: + keyfile: Open file to read key data from. + no_protect_key: If True, the write protection will NOT be enabled. + show_sensitive_info: If True, the sensitive information will be shown. + """ + if self.efuses.coding_scheme == self.efuses.REGS.CODING_SCHEME_34: 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) + "burn-key-digest only works with 'None' coding scheme" ) - efuse.save(data) + chip_revision = self.esp.get_chip_revision() + if chip_revision < 300: + raise esptool.FatalError( + "Incorrect chip revision for Secure boot v2. " + "Detected: v%d.%d. Expected: >= v3.0" + % (chip_revision / 100, chip_revision % 100) + ) - if block_name in ("flash_encryption", "secure_boot_v1"): - if not no_protect_key: - print("\tDisabling read to key block") - efuse.disable_read() + digest = espsecure._digest_sbv2_public_key(keyfile) + efuse = self.efuses["BLOCK2"] + num_bytes = efuse.bit_len // 8 + if len(digest) != num_bytes: + raise esptool.FatalError( + f"Incorrect digest size {len(digest)}. " + f"Digest must be {num_bytes} bytes " + f"({num_bytes * 8} bits) of raw binary key data." + ) + log.print(f" - {efuse.name}", end=" ") + log.print( + "-> [{}]".format( + util.hexify(digest, " ") + if show_sensitive_info + else " ".join(["??"] * len(digest)) + ) + ) + efuse.save(digest) if not no_protect_key: - print("\tDisabling write to key block") + log.print(f"Disabling write to eFuse {efuse.name}...") efuse.disable_write() - print("") - if args.no_protect_key: - print("Key is left unprotected as per --no-protect-key argument.") - - msg = "Burn keys in efuse blocks.\n" - if no_protect_key: - msg += ( - "The key block will left readable and writeable (due to --no-protect-key)" - ) - else: - msg += "The key block will be read and write protected " - "(no further changes or readback)" - print(msg, "\n") - if not efuses.burn_all(check_batch_mode=True): - return - print("Successful") - - -def burn_key_digest(esp, efuses, args): - if efuses.coding_scheme == efuses.REGS.CODING_SCHEME_34: - raise esptool.FatalError("burn_key_digest only works with 'None' coding scheme") - - chip_revision = esp.get_chip_revision() - if chip_revision < 300: - raise esptool.FatalError( - "Incorrect chip revision for Secure boot v2. " - "Detected: v%d.%d. Expected: >= v3.0" - % (chip_revision / 100, chip_revision % 100) - ) - - digest = espsecure._digest_sbv2_public_key(args.keyfile) - efuse = efuses["BLOCK2"] - num_bytes = efuse.bit_len // 8 - 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) - ) - print(" - %s" % (efuse.name), end=" ") - print( - "-> [{}]".format( - util.hexify(digest, " ") - if args.show_sensitive_info - else " ".join(["??"] * len(digest)) - ) - ) - - efuse.save(digest) - if not args.no_protect_key: - print("Disabling write to efuse %s..." % (efuse.name)) - efuse.disable_write() - - if not efuses.burn_all(check_batch_mode=True): - return - print("Successful") - - -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") + if not self.efuses.burn_all(check_batch_mode=True): + return + log.print("Successful.") diff --git a/tools/esptool_py/espefuse/efuse/esp32c2/__init__.py b/tools/esptool_py/espefuse/efuse/esp32c2/__init__.py index a3b55a8023..167574c94b 100644 --- a/tools/esptool_py/espefuse/efuse/esp32c2/__init__.py +++ b/tools/esptool_py/espefuse/efuse/esp32c2/__init__.py @@ -1,3 +1,5 @@ from . import operations from .emulate_efuse_controller import EmulateEfuseController from .fields import EspEfuses + +commands = operations.ESP32C2Commands diff --git a/tools/esptool_py/espefuse/efuse/esp32c2/fields.py b/tools/esptool_py/espefuse/efuse/esp32c2/fields.py index a9fe33cce4..a2923b7db5 100644 --- a/tools/esptool_py/espefuse/efuse/esp32c2/fields.py +++ b/tools/esptool_py/espefuse/efuse/esp32c2/fields.py @@ -12,6 +12,7 @@ from bitstring import BitArray import esptool +from esptool.logger import log import reedsolo @@ -55,9 +56,6 @@ 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, @@ -66,14 +64,12 @@ def __init__( do_not_confirm=False, extend_efuse_table=None, ): + super().__init__(esp, skip_connect, debug, do_not_confirm, extend_efuse_table) 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-C2": raise esptool.FatalError( "Expected the 'esp' param for ESP32-C2 chip but got for '%s'." @@ -102,7 +98,7 @@ def __init__( for efuse in self.Fields.BLOCK2_CALIBRATION_EFUSES ] else: - if self["BLK_VERSION_MINOR"].get() == 1: + if self.get_block_version() >= 1: self.efuses += [ EfuseField.convert(self, efuse) for efuse in self.Fields.BLOCK2_CALIBRATION_EFUSES @@ -133,9 +129,9 @@ def read_coding_scheme(self): self.coding_scheme = self.REGS.CODING_SCHEME_RS def print_status_regs(self): - print("") + log.print("") self.blocks[0].print_block(self.blocks[0].err_bitarray, "err__regs", debug=True) - print( + log.print( "{:27} 0x{:08x}".format( "EFUSE_RD_RS_ERR_REG", self.read_reg(self.REGS.EFUSE_RD_RS_ERR_REG) ) @@ -168,7 +164,7 @@ def wait_efuse_idle(self): # For PGM_CMD it is not necessary. return raise esptool.FatalError( - "Timed out waiting for Efuse controller command to complete" + "Timed out waiting for eFuse controller command to complete" ) def efuse_program(self, block): @@ -196,30 +192,30 @@ def efuse_read(self): try: self._esp = self.reconnect_chip(self._esp) except esptool.FatalError: - print("Can not re-connect to the chip") + log.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( + log.print( "This is the correct behavior as we are actually burning " - "DIS_DOWNLOAD_MODE which disables the connection to the chip" + "DIS_DOWNLOAD_MODE which disables the connection to the chip." ) - print("DIS_DOWNLOAD_MODE is enabled") - print("Successful") + log.print("DIS_DOWNLOAD_MODE is enabled.") + log.print("Successful.") sys.exit(0) # finish without errors raise - print("Established a connection with the chip") + log.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") + log.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" + log.print( + "espefuse can not continue to work in Secure download mode." ) - print("ENABLE_SECURITY_DOWNLOAD is enabled") - print("Successful") + log.print("ENABLE_SECURITY_DOWNLOAD is enabled.") + log.print("Successful.") sys.exit(0) # finish without errors raise @@ -279,9 +275,9 @@ def get_coding_scheme_warnings(self, silent=False): 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) + log.print( + f"Error(s) in BLOCK{block.id} " + f"[ERRORS:{block.num_errors} FAIL:{block.fail}]" ) if (self.debug or ret_fail) and not silent: self.print_status_regs() @@ -346,7 +342,7 @@ def check_format(self, new_value_str): 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) + output = f"Block{self.block} has ERRORS:{errs} FAIL:{fail}" else: output = "OK" return "(" + output + ")" @@ -356,14 +352,12 @@ def get(self, from_read=True): mac = self.get_raw(from_read)[::-1] else: mac = self.get_raw(from_read) - return "%s %s" % (util.hexify(mac, ":"), self.check()) + return f"{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 - ) + log.print( + f" - '{e.name}' ({e.description}) {e.get_bitstring()} -> {new_value}" ) if self.name == "CUSTOM_MAC": @@ -371,7 +365,7 @@ def print_field(e, new_value): print_field(self, bitarray_mac) super(EfuseMacField, self).save(new_value) else: - raise esptool.FatalError("Writing Factory MAC address is not supported") + raise esptool.FatalError("Writing Factory MAC address is not supported.") class EfuseKeyPurposeField(EfuseField): diff --git a/tools/esptool_py/espefuse/efuse/esp32c2/mem_definition.py b/tools/esptool_py/espefuse/efuse/esp32c2/mem_definition.py index 47cd18c047..05b3135cda 100644 --- a/tools/esptool_py/espefuse/efuse/esp32c2/mem_definition.py +++ b/tools/esptool_py/espefuse/efuse/esp32c2/mem_definition.py @@ -97,10 +97,7 @@ 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 = [] dir_name = os.path.dirname(os.path.abspath(__file__)) diff --git a/tools/esptool_py/espefuse/efuse/esp32c2/operations.py b/tools/esptool_py/espefuse/efuse/esp32c2/operations.py index d23f6a721b..4e51f84b8d 100644 --- a/tools/esptool_py/espefuse/efuse/esp32c2/operations.py +++ b/tools/esptool_py/espefuse/efuse/esp32c2/operations.py @@ -1,351 +1,292 @@ # This file includes the operations with eFuses for ESP32-C2 chip # -# SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD +# SPDX-FileCopyrightText: 2021-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 +from io import IOBase +from typing import BinaryIO +import rich_click as click import espsecure - import esptool +from esptool.logger import log from . import fields +from .mem_definition import EfuseDefineBlocks from .. import util from ..base_operations import ( - add_common_commands, + BaseCommands, + NonCompositeTuple, + TupleParameter, add_force_write_always, add_show_sensitive_info_option, - burn_bit, - burn_block_data, - burn_efuse, - check_error, - dump, - read_protect_efuse, - summary, - write_protect_efuse, + protect_options, ) -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.", - action="store_true", - ) +class ESP32C2Commands(BaseCommands): + CHIP_NAME = "ESP32-C2" + efuse_lib = fields.EspEfuses + + ################################### CLI definitions ################################### + def add_cli_commands(self, cli: click.Group): + super().add_cli_commands(cli) + blocks_for_keys = EfuseDefineBlocks().get_blocks_for_keys() -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) - add_show_sensitive_info_option(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 128/256 bits of binary key data", - 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 range(1): - burn_key.add_argument( - "block", - help="Key block to burn", - nargs="?", - action="append", - metavar="BLOCK", - choices=efuses.BLOCKS_FOR_KEYS, + @cli.command( + "burn-key", + help="Burn the key block with the specified name. Arguments are groups of block name, " + "key file (containing 128/256 bits of binary key data) and key purpose.\n\n" + f"Block is one of: [{', '.join(blocks_for_keys)}]\n\n" + f"Key purpose is one of: [{', '.join(fields.EfuseKeyPurposeField.KEY_PURPOSES_NAME)}]", ) - burn_key.add_argument( - "keyfile", - help="File containing 128/256 bits of binary key data", - nargs="?", - action="append", - metavar="KEYFILE", - type=argparse.FileType("rb"), + @click.argument( + "block_keyfile_keypurpose", + metavar=" ", + cls=TupleParameter, + nargs=-1, + # we need to add +1 here as this chip has only one key block. SB and FE + # keys will share this key block if both of them have to be written. + max_arity=len(blocks_for_keys) + 1, + type=NonCompositeTuple( + [ + click.Choice(blocks_for_keys), + click.File("rb"), + click.Choice(fields.EfuseKeyPurposeField.KEY_PURPOSES_NAME), + ] + ), ) - burn_key.add_argument( - "keypurpose", - help="Purpose to set.", - nargs="?", - action="append", - metavar="KEYPURPOSE", - choices=fields.EfuseKeyPurposeField.KEY_PURPOSES_NAME, + @protect_options + @add_force_write_always + @add_show_sensitive_info_option + @click.pass_context + def burn_key_cli(ctx, **kwargs): + """Burn the key block with the specified name""" + block, keyfile, keypurpose = zip(*kwargs.pop("block_keyfile_keypurpose")) + kwargs.pop("force_write_always") + kwargs["show_sensitive_info"] = ctx.show_sensitive_info + self.burn_key(block, keyfile, keypurpose, **kwargs) + + @cli.command( + "burn-key-digest", + short_help="Parse an ECDSA public key and burn the digest.", + help="Parse an ECDSA public key and burn the digest to higher 128-bits of BLOCK_KEY0. " + "KEYFILE is in PEM format.", ) - - burn_key_digest = subparsers.add_parser( - "burn_key_digest", - help="Parse an ECDSA public key and burn the digest " - "to higher 128-bits of BLOCK_KEY0", - ) - protect_options(burn_key_digest) - add_force_write_always(burn_key_digest) - add_show_sensitive_info_option(burn_key_digest) - burn_key_digest.add_argument( - "keyfile", help="Key file to digest (PEM format)", type=argparse.FileType("rb") - ) - - 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 BLOCK1." - ) - 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) - efuses["CUSTOM_MAC_USED"].save(1) - 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): - print("") - # fmt: off - if efuses["BLK_VERSION_MINOR"].get() == 1: - print("Temperature Sensor Calibration = {}C".format(efuses["TEMP_CALIB"].get())) - print("ADC OCode = ", efuses["OCODE"].get()) - print("ADC1:") - print("INIT_CODE_ATTEN0 = ", efuses["ADC1_INIT_CODE_ATTEN0"].get()) - print("INIT_CODE_ATTEN3 = ", efuses["ADC1_INIT_CODE_ATTEN3"].get()) - print("CAL_VOL_ATTEN0 = ", efuses["ADC1_CAL_VOL_ATTEN0"].get()) - print("CAL_VOL_ATTEN3 = ", efuses["ADC1_CAL_VOL_ATTEN3"].get()) - else: - print("BLK_VERSION_MINOR = {}".format(efuses["BLK_VERSION_MINOR"].get_meaning())) - # 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(keypurpose_list) - if len(block_name_list) != len(datafile_list) or len(block_name_list) != len( - keypurpose_list + @click.argument("keyfile", type=click.File("rb")) + @protect_options + @add_force_write_always + @add_show_sensitive_info_option + @click.pass_context + def burn_key_digest_cli(ctx, **kwargs): + kwargs.pop("force_write_always") + kwargs["show_sensitive_info"] = ctx.show_sensitive_info + self.burn_key_digest(**kwargs) + + ###################################### Commands ###################################### + + def burn_custom_mac(self, mac: str | bytes): + mac = self._convert_mac_to_bytes(mac) + self.efuses["CUSTOM_MAC"].save(mac) + self.efuses["CUSTOM_MAC_USED"].save(1) + if not self.efuses.burn_all(check_batch_mode=True): + return + self.get_custom_mac() + log.print("Successful.") + + def adc_info(self): + log.print("Block version:", self.efuses.get_block_version()) + if self.efuses.get_block_version() >= 1: + # fmt: off + log.print(f"Temperature Sensor Calibration = {self.efuses['TEMP_CALIB'].get()}C") + log.print("ADC OCode = ", self.efuses["OCODE"].get()) + log.print("ADC1:") + log.print("INIT_CODE_ATTEN0 = ", self.efuses["ADC1_INIT_CODE_ATTEN0"].get()) + log.print("INIT_CODE_ATTEN3 = ", self.efuses["ADC1_INIT_CODE_ATTEN3"].get()) + log.print("CAL_VOL_ATTEN0 = ", self.efuses["ADC1_CAL_VOL_ATTEN0"].get()) + log.print("CAL_VOL_ATTEN3 = ", self.efuses["ADC1_CAL_VOL_ATTEN3"].get()) + # fmt: on + + def burn_key( + self, + blocks: list[str], + keyfiles: list[BinaryIO], + keypurposes: list[str], + no_write_protect: bool = False, + no_read_protect: bool = False, + show_sensitive_info: bool = False, + digest: list[bytes] | None = None, ): - 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)) - ) - - assert 1 <= len(block_name_list) <= 2, "Unexpected case" - - if len(block_name_list) == 2: - incompatible = True if "XTS_AES_128_KEY" in keypurpose_list else False - permitted_purposes = [ - "XTS_AES_128_KEY_DERIVED_FROM_128_EFUSE_BITS", - "SECURE_BOOT_DIGEST", + """Burn the key block with the specified name. Arguments are groups of block name, + key file (containing 128/256 bits of binary key data) and key purpose. + + Args: + blocks: List of eFuse block names to burn keys to. + keyfiles: List of open files to read key data from. + keypurposes: List of key purposes to burn. + no_write_protect: If True, the write protection will NOT be enabled. + no_read_protect: If True, the read protection will NOT be enabled. + show_sensitive_info: If True, the sensitive information will be shown. + digest: List of digests to burn. + """ + datafile_list: list[BinaryIO] | list[bytes] + if digest is None: + datafile_list = keyfiles[ + 0 : len([keyfile for keyfile in keyfiles if keyfile is not None]) : + ] + else: + datafile_list = digest[ + 0 : len([name for name in digest if name is not None]) : + ] + block_name_list = blocks[ + 0 : len([name for name in blocks if name is not None]) : ] - incompatible |= ( - keypurpose_list[0] in permitted_purposes - and keypurpose_list[1] not in permitted_purposes - ) - if incompatible: + keypurpose_list = keypurposes[ + 0 : len([name for name in keypurposes if name is not None]) : + ] + + util.check_duplicate_name_in_list(keypurpose_list) + if len(block_name_list) != len(datafile_list) or len(block_name_list) != len( + keypurpose_list + ): raise esptool.FatalError( - "These keypurposes are incompatible %s" % (keypurpose_list) + "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 datafile, keypurpose in zip(datafile_list, keypurpose_list): - data = datafile if isinstance(datafile, bytes) else datafile.read() - - if keypurpose == "XTS_AES_128_KEY_DERIVED_FROM_128_EFUSE_BITS": - efuse = efuses["BLOCK_KEY0_LOW_128"] - elif keypurpose == "SECURE_BOOT_DIGEST": - efuse = efuses["BLOCK_KEY0_HI_128"] - if len(data) == 32: - print( - "\tProgramming only left-most 128-bits from SHA256 hash of " - "public key to highest 128-bits of BLOCK KEY0" - ) - data = data[:16] - elif len(data) != efuse.bit_len // 8: + assert 1 <= len(block_name_list) <= 2, "Unexpected case" + + if len(block_name_list) == 2: + incompatible = True if "XTS_AES_128_KEY" in keypurpose_list else False + permitted_purposes = [ + "XTS_AES_128_KEY_DERIVED_FROM_128_EFUSE_BITS", + "SECURE_BOOT_DIGEST", + ] + incompatible |= ( + keypurpose_list[0] in permitted_purposes + and keypurpose_list[1] not in permitted_purposes + ) + if incompatible: raise esptool.FatalError( - "Wrong length of this file for SECURE_BOOT_DIGEST. " - "Got %d (expected %d or %d)" % (len(data), 32, efuse.bit_len // 8) + f"These keypurposes are incompatible {list(keypurpose_list)}" ) - assert len(data) == 16, "Only 16 bytes expected" - else: - efuse = efuses["BLOCK_KEY0"] - - num_bytes = efuse.bit_len // 8 - print(" - %s" % (efuse.name), end=" ") - revers_msg = None - if keypurpose.startswith("XTS_AES_"): - revers_msg = "\tReversing byte order for AES-XTS hardware peripheral" - data = data[::-1] - print( - "-> [{}]".format( - util.hexify(data, " ") - if args.show_sensitive_info - else " ".join(["??"] * len(data)) + log.print("Burn keys to blocks:") + for datafile, keypurpose in zip(datafile_list, keypurpose_list): + if isinstance(datafile, IOBase): + data = datafile.read() + datafile.close() + else: + data = datafile # type: ignore # this is safe but mypy still complains + + if keypurpose == "XTS_AES_128_KEY_DERIVED_FROM_128_EFUSE_BITS": + efuse = self.efuses["BLOCK_KEY0_LOW_128"] + elif keypurpose == "SECURE_BOOT_DIGEST": + efuse = self.efuses["BLOCK_KEY0_HI_128"] + if len(data) == 32: + log.print( + "\tProgramming only left-most 128-bits from SHA256 hash of " + "public key to highest 128-bits of BLOCK KEY0" + ) + data = data[:16] + elif len(data) != efuse.bit_len // 8: + raise esptool.FatalError( + "Wrong length of this file for SECURE_BOOT_DIGEST. " + f"Got {len(data)} (expected 32 or {efuse.bit_len // 8})" + ) + assert len(data) == 16, "Only 16 bytes expected" + else: + efuse = self.efuses["BLOCK_KEY0"] + + num_bytes = efuse.bit_len // 8 + + log.print(f" - {efuse.name}", end=" ") + revers_msg = None + if keypurpose.startswith("XTS_AES_"): + revers_msg = "\tReversing byte order for AES-XTS hardware peripheral..." + data = data[::-1] + log.print( + "-> [{}]".format( + util.hexify(data, " ") + if show_sensitive_info + else " ".join(["??"] * len(data)) + ) ) - ) - if revers_msg: - print(revers_msg) - if len(data) != num_bytes: + if revers_msg: + log.print(revers_msg) + if len(data) != num_bytes: + raise esptool.FatalError( + f"Incorrect key file size {len(data)}. Key file must be {num_bytes}" + f" bytes ({num_bytes * 8} bits) of raw binary key data." + ) + if keypurpose.startswith("XTS_AES_"): + read_protect = not no_read_protect + else: + read_protect = False + write_protect = not no_write_protect + + # using eFuse instead of a block gives the advantage + # of checking it as the whole field. + efuse.save(data) + + if keypurpose == "XTS_AES_128_KEY": + if self.efuses["XTS_KEY_LENGTH_256"].get(): + log.print("\t'XTS_KEY_LENGTH_256' is already '1'") + else: + log.print("\tXTS_KEY_LENGTH_256 -> 1") + self.efuses["XTS_KEY_LENGTH_256"].save(1) + + if read_protect: + log.print("\tDisabling read to key block...") + efuse.disable_read() + + if write_protect: + log.print("\tDisabling write to key block...") + efuse.disable_write() + log.print("") + + if not write_protect: + log.print("Keys will remain writeable (due to --no-write-protect).") + if no_read_protect: + log.print("Keys will remain readable (due to --no-read-protect).") + + if not self.efuses.burn_all(check_batch_mode=True): + return + log.print("Successful.") + + def burn_key_digest( + self, + keyfile: BinaryIO, + no_write_protect: bool = False, + no_read_protect: bool = False, + show_sensitive_info: bool = False, + ): + """Parse a RSA public key and burn the digest to key eFuse block. + + Args: + keyfile: Open file to read key data from. + no_write_protect: If True, the write protection will NOT be enabled. + no_read_protect: If True, the read protection will NOT be enabled. + show_sensitive_info: If True, the sensitive information will be shown. + """ + digest = espsecure._digest_sbv2_public_key(keyfile) + digest = digest[:16] + num_bytes = self.efuses["BLOCK_KEY0_HI_128"].bit_len // 8 + if len(digest) != 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) + "Incorrect digest size %d. " + "Digest must be %d bytes (%d bits) of raw binary key data." + % (len(digest), num_bytes, num_bytes * 8) ) - - if keypurpose.startswith("XTS_AES_"): - 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) - - if keypurpose == "XTS_AES_128_KEY": - if efuses["XTS_KEY_LENGTH_256"].get(): - print("\t'XTS_KEY_LENGTH_256' is already '1'") - else: - print("\tXTS_KEY_LENGTH_256 -> 1") - efuses["XTS_KEY_LENGTH_256"].save(1) - - 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): - datafile = args.keyfile - args.keypurpose = ["SECURE_BOOT_DIGEST"] - args.block = ["BLOCK_KEY0"] - digest = espsecure._digest_sbv2_public_key(datafile) - digest = digest[:16] - num_bytes = efuses["BLOCK_KEY0_HI_128"].bit_len // 8 - 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) + self.burn_key( + ["BLOCK_KEY0"], + [keyfile], + ["SECURE_BOOT_DIGEST"], + no_write_protect, + no_read_protect, + show_sensitive_info, + digest=[digest], ) - burn_key(esp, efuses, args, digest=[digest]) - - -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") diff --git a/tools/esptool_py/espefuse/efuse/esp32c3/__init__.py b/tools/esptool_py/espefuse/efuse/esp32c3/__init__.py index a3b55a8023..6c708d0c92 100644 --- a/tools/esptool_py/espefuse/efuse/esp32c3/__init__.py +++ b/tools/esptool_py/espefuse/efuse/esp32c3/__init__.py @@ -1,3 +1,5 @@ from . import operations from .emulate_efuse_controller import EmulateEfuseController from .fields import EspEfuses + +commands = operations.ESP32C3Commands diff --git a/tools/esptool_py/espefuse/efuse/esp32c3/fields.py b/tools/esptool_py/espefuse/efuse/esp32c3/fields.py index f367540a80..36a4a8df7e 100644 --- a/tools/esptool_py/espefuse/efuse/esp32c3/fields.py +++ b/tools/esptool_py/espefuse/efuse/esp32c3/fields.py @@ -12,6 +12,7 @@ from bitstring import BitArray import esptool +from esptool.logger import log import reedsolo @@ -55,9 +56,6 @@ 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, @@ -66,14 +64,12 @@ def __init__( do_not_confirm=False, extend_efuse_table=None, ): + super().__init__(esp, skip_connect, debug, do_not_confirm, extend_efuse_table) 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-C3": raise esptool.FatalError( "Expected the 'esp' param for ESP32-C3 chip but got for '%s'." @@ -102,7 +98,7 @@ def __init__( for efuse in self.Fields.BLOCK2_CALIBRATION_EFUSES ] else: - if self["BLK_VERSION_MAJOR"].get() == 1: + if self.get_block_version() >= 100: self.efuses += [ EfuseField.convert(self, efuse) for efuse in self.Fields.BLOCK2_CALIBRATION_EFUSES @@ -136,14 +132,14 @@ def read_coding_scheme(self): self.coding_scheme = self.REGS.CODING_SCHEME_RS def print_status_regs(self): - print("") + log.print("") self.blocks[0].print_block(self.blocks[0].err_bitarray, "err__regs", debug=True) - print( + log.print( "{:27} 0x{:08x}".format( "EFUSE_RD_RS_ERR0_REG", self.read_reg(self.REGS.EFUSE_RD_RS_ERR0_REG) ) ) - print( + log.print( "{:27} 0x{:08x}".format( "EFUSE_RD_RS_ERR1_REG", self.read_reg(self.REGS.EFUSE_RD_RS_ERR1_REG) ) @@ -176,7 +172,7 @@ def wait_efuse_idle(self): # For PGM_CMD it is not necessary. return raise esptool.FatalError( - "Timed out waiting for Efuse controller command to complete" + "Timed out waiting for eFuse controller command to complete." ) def efuse_program(self, block): @@ -204,30 +200,30 @@ def efuse_read(self): try: self._esp = self.reconnect_chip(self._esp) except esptool.FatalError: - print("Can not re-connect to the chip") + log.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( + log.print( "This is the correct behavior as we are actually burning " - "DIS_DOWNLOAD_MODE which disables the connection to the chip" + "DIS_DOWNLOAD_MODE which disables the connection to the chip." ) - print("DIS_DOWNLOAD_MODE is enabled") - print("Successful") + log.print("DIS_DOWNLOAD_MODE is enabled.") + log.print("Successful.") sys.exit(0) # finish without errors raise - print("Established a connection with the chip") + log.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") + log.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" + log.print( + "espefuse can not continue to work in Secure download mode." ) - print("ENABLE_SECURITY_DOWNLOAD is enabled") - print("Successful") + log.print("ENABLE_SECURITY_DOWNLOAD is enabled.") + log.print("Successful.") sys.exit(0) # finish without errors raise @@ -237,7 +233,7 @@ def set_efuse_timing(self): apb_freq = self.get_crystal_freq() if apb_freq != 40: raise esptool.FatalError( - "The eFuse supports only xtal=40M (xtal was %d)" % apb_freq + f"The eFuse supports only xtal=40M (xtal was {apb_freq})." ) self.update_reg(self.REGS.EFUSE_DAC_CONF_REG, self.REGS.EFUSE_DAC_NUM_M, 0xFF) @@ -262,7 +258,7 @@ def get_coding_scheme_warnings(self, silent=False): ] block.err_bitarray.pos = 0 for word in reversed(words): - block.err_bitarray.overwrite(BitArray("uint:32=%d" % word)) + block.err_bitarray.overwrite(BitArray(f"uint:32={word}")) block.num_errors = block.err_bitarray.count(True) block.fail = block.num_errors != 0 else: @@ -282,9 +278,9 @@ def get_coding_scheme_warnings(self, silent=False): 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) + log.print( + f"Error(s) in BLOCK{block.id} " + f"[ERRORS:{block.num_errors} FAIL:{block.fail}]." ) if (self.debug or ret_fail) and not silent: self.print_status_regs() @@ -316,7 +312,7 @@ def get(self, from_read=True): return (hi_bits << 3) + lo_bits def save(self, new_value): - raise esptool.FatalError("Burning %s is not supported" % self.name) + raise esptool.FatalError(f"Burning {self.name} is not supported.") class EfuseTempSensor(EfuseField): @@ -362,7 +358,7 @@ def check_format(self, new_value_str): 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) + output = f"Block{self.block} has ERRORS:{errs} FAIL:{fail}." else: output = "OK" return "(" + output + ")" @@ -376,10 +372,8 @@ def get(self, from_read=True): def save(self, new_value): def print_field(e, new_value): - print( - " - '{}' ({}) {} -> {}".format( - e.name, e.description, e.get_bitstring(), new_value - ) + log.print( + f" - '{e.name}' ({e.description}) {e.get_bitstring()} -> {new_value}" ) if self.name == "CUSTOM_MAC": @@ -420,9 +414,9 @@ def check_format(self, new_value_str): 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) + raise esptool.FatalError(f"'{raw_val}' can not be set (value out of range)") else: - raise esptool.FatalError("'%s' unknown name" % raw_val) + raise esptool.FatalError(f"'{raw_val}' unknown name") return raw_val def need_reverse(self, new_key_purpose): diff --git a/tools/esptool_py/espefuse/efuse/esp32c3/mem_definition.py b/tools/esptool_py/espefuse/efuse/esp32c3/mem_definition.py index bd13bd7738..105f1ed906 100644 --- a/tools/esptool_py/espefuse/efuse/esp32c3/mem_definition.py +++ b/tools/esptool_py/espefuse/efuse/esp32c3/mem_definition.py @@ -130,12 +130,8 @@ 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_MAJOR is 1, these efuse fields are in BLOCK2 self.BLOCK2_CALIBRATION_EFUSES = [] - self.CALC = [] dir_name = os.path.dirname(os.path.abspath(__file__)) diff --git a/tools/esptool_py/espefuse/efuse/esp32c3/operations.py b/tools/esptool_py/espefuse/efuse/esp32c3/operations.py index ff63109570..57baae243d 100644 --- a/tools/esptool_py/espefuse/efuse/esp32c3/operations.py +++ b/tools/esptool_py/espefuse/efuse/esp32c3/operations.py @@ -1,404 +1,310 @@ # This file includes the operations with eFuses for ESP32-C3 chip # -# SPDX-FileCopyrightText: 2020-2022 Espressif Systems (Shanghai) CO LTD +# SPDX-FileCopyrightText: 2020-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 +from io import IOBase +from typing import BinaryIO +import click import espsecure import esptool +from esptool.logger import log from . import fields from .. import util +from .mem_definition import EfuseDefineBlocks from ..base_operations import ( - add_common_commands, + BaseCommands, + NonCompositeTuple, + TupleParameter, add_force_write_always, add_show_sensitive_info_option, - burn_bit, - burn_block_data, - burn_efuse, - check_error, - dump, - read_protect_efuse, - summary, - write_protect_efuse, + protect_options, ) -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", - ) +class ESP32C3Commands(BaseCommands): + CHIP_NAME = "ESP32-C3" + efuse_lib = fields.EspEfuses + ################################### CLI definitions ################################### -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) - add_show_sensitive_info_option(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", - 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", - 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, - ) + def add_cli_commands(self, cli: click.Group): + super().add_cli_commands(cli) + blocks_for_keys = EfuseDefineBlocks().get_blocks_for_keys() - 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) - add_show_sensitive_info_option(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, + @cli.command( + "burn-key", + short_help="Burn the key block with the specified name.", + help="Burn the key block with the specified name. Arguments are groups of block name, " + "key file (containing 128/256 bits of binary key data) and key purpose.\n\n" + f"Block is one of: [{', '.join(blocks_for_keys)}]\n\n" + f"Key purpose is one of: [{', '.join(fields.EfuseKeyPurposeField.KEY_PURPOSES_NAME)}]", ) - burn_key_digest.add_argument( - "keyfile", - help="Key file to digest (PEM format)", - nargs="?", - action="append", - metavar="KEYFILE", - type=argparse.FileType("rb"), + @click.argument( + "block_keyfile_keypurpose", + metavar=" ", + cls=TupleParameter, + required=True, + nargs=-1, + max_arity=len(blocks_for_keys), + type=NonCompositeTuple( + [ + click.Choice(blocks_for_keys), + click.File("rb"), + click.Choice(fields.EfuseKeyPurposeField.KEY_PURPOSES_NAME), + ] + ), ) - burn_key_digest.add_argument( - "keypurpose", - help="Purpose to set.", - nargs="?", - action="append", - metavar="KEYPURPOSE", - choices=fields.EfuseKeyPurposeField.DIGEST_KEY_PURPOSES, + @protect_options + @add_force_write_always + @add_show_sensitive_info_option + @click.pass_context + def burn_key_cli(ctx, **kwargs): + block, keyfile, keypurpose = zip(*kwargs.pop("block_keyfile_keypurpose")) + kwargs.pop("force_write_always") + kwargs["show_sensitive_info"] = ctx.show_sensitive_info + self.burn_key(block, keyfile, keypurpose, **kwargs) + + @cli.command( + "burn-key-digest", + short_help="Parse a RSA public key and burn the digest.", + help="Parse a RSA public key and burn the digest to key eFuse block.\n\n" + f"Block is one of: [{', '.join(blocks_for_keys)}]\n\n" + f"Key purpose is one of: [{', '.join(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): - print("") - # fmt: off - if efuses["BLK_VERSION_MAJOR"].get() == 1: - print("Temperature Sensor Calibration = {}C".format(efuses["TEMP_CALIB"].get())) - print("ADC OCode = ", efuses["OCODE"].get()) - print("ADC1:") - print("INIT_CODE_ATTEN0 = ", efuses["ADC1_INIT_CODE_ATTEN0"].get()) - print("INIT_CODE_ATTEN1 = ", efuses["ADC1_INIT_CODE_ATTEN1"].get()) - print("INIT_CODE_ATTEN2 = ", efuses["ADC1_INIT_CODE_ATTEN2"].get()) - print("INIT_CODE_ATTEN3 = ", efuses["ADC1_INIT_CODE_ATTEN3"].get()) - print("CAL_VOL_ATTEN0 = ", efuses["ADC1_CAL_VOL_ATTEN0"].get()) - print("CAL_VOL_ATTEN1 = ", efuses["ADC1_CAL_VOL_ATTEN1"].get()) - print("CAL_VOL_ATTEN2 = ", efuses["ADC1_CAL_VOL_ATTEN2"].get()) - print("CAL_VOL_ATTEN3 = ", efuses["ADC1_CAL_VOL_ATTEN3"].get()) - else: - print("BLK_VERSION_MAJOR = {}".format(efuses["BLK_VERSION_MAJOR"].get_meaning())) - # 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)) + @click.argument( + "block_keyfile_keypurpose", + metavar=" ", + cls=TupleParameter, + required=True, + nargs=-1, + max_arity=len(blocks_for_keys), + type=NonCompositeTuple( + [ + click.Choice(blocks_for_keys), + click.File("rb"), + click.Choice(fields.EfuseKeyPurposeField.DIGEST_KEY_PURPOSES), + ] + ), ) - - print("Burn keys to blocks:") - for block_name, datafile, keypurpose in zip( - block_name_list, datafile_list, keypurpose_list + @protect_options + @add_force_write_always + @add_show_sensitive_info_option + @click.pass_context + def burn_key_digest_cli(ctx, **kwargs): + block, keyfile, keypurpose = zip(*kwargs.pop("block_keyfile_keypurpose")) + kwargs.pop("force_write_always") + kwargs["show_sensitive_info"] = ctx.show_sensitive_info + self.burn_key_digest(block, keyfile, keypurpose, **kwargs) + + ###################################### Commands ###################################### + + def adc_info(self): + log.print("Block version:", self.efuses.get_block_version()) + if self.efuses.get_block_version() >= 100: + # fmt: off + log.print(f"Temperature Sensor Calibration = {self.efuses['TEMP_CALIB'].get()}C") + log.print("ADC OCode = ", self.efuses["OCODE"].get()) + log.print("ADC1:") + log.print("INIT_CODE_ATTEN0 = ", self.efuses["ADC1_INIT_CODE_ATTEN0"].get()) + log.print("INIT_CODE_ATTEN1 = ", self.efuses["ADC1_INIT_CODE_ATTEN1"].get()) + log.print("INIT_CODE_ATTEN2 = ", self.efuses["ADC1_INIT_CODE_ATTEN2"].get()) + log.print("INIT_CODE_ATTEN3 = ", self.efuses["ADC1_INIT_CODE_ATTEN3"].get()) + log.print("CAL_VOL_ATTEN0 = ", self.efuses["ADC1_CAL_VOL_ATTEN0"].get()) + log.print("CAL_VOL_ATTEN1 = ", self.efuses["ADC1_CAL_VOL_ATTEN1"].get()) + log.print("CAL_VOL_ATTEN2 = ", self.efuses["ADC1_CAL_VOL_ATTEN2"].get()) + log.print("CAL_VOL_ATTEN3 = ", self.efuses["ADC1_CAL_VOL_ATTEN3"].get()) + # fmt: on + + def burn_key( + self, + blocks: list[str], + keyfiles: list[BinaryIO], + keypurposes: list[str], + no_write_protect: bool = False, + no_read_protect: bool = False, + show_sensitive_info: bool = False, + digest: list[bytes] | None = None, ): - 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] - + """Burn the key block with the specified name. Arguments are groups of block name, + key file (containing 128/256 bits of binary key data) and key purpose. + + Args: + blocks: List of eFuse block names to burn keys to. + keyfiles: List of open files to read key data from. + keypurposes: List of key purposes to burn. + no_write_protect: If True, the write protection will NOT be enabled. + no_read_protect: If True, the read protection will NOT be enabled. + show_sensitive_info: If True, the sensitive information will be shown. + digest: List of digests to burn. + """ + datafile_list: list[BinaryIO] | list[bytes] if digest is None: - data = datafile.read() + datafile_list = keyfiles[ + 0 : len([name for name in keyfiles if name is not None]) : + ] else: - data = datafile + datafile_list = digest[ + 0 : len([name for name in digest if name is not None]) : + ] + block_name_list = blocks[ + 0 : len([name for name in blocks if name is not None]) : + ] + keypurpose_list = keypurposes[ + 0 : len([name for name in keypurposes if name is not None]) : + ] - print(" - %s" % (efuse.name), end=" ") - revers_msg = None - if efuses[block.key_purpose_name].need_reverse(keypurpose): - revers_msg = "\tReversing byte order for AES-XTS hardware peripheral" - data = data[::-1] - print( - "-> [{}]".format( - util.hexify(data, " ") - if args.show_sensitive_info - else " ".join(["??"] * len(data)) - ) - ) - if revers_msg: - print(revers_msg) - if len(data) != num_bytes: + 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( - "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) + f"The number of blocks ({len(block_name_list)}), datafile ({len(datafile_list)}) " + f"and keypurpose ({len(keypurpose_list)}) should be the same." ) - 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) + log.print("Burn keys to blocks:") + for block_name, datafile, keypurpose in zip( + block_name_list, datafile_list, keypurpose_list + ): + efuse = None + for blk in self.efuses.blocks: + if block_name == blk.name or block_name in blk.alias: + efuse = self.efuses[blk.name] + if efuse is None: + raise esptool.FatalError(f"Unknown block name - {block_name}.") + num_bytes = efuse.bit_len // 8 + + block_num = self.efuses.get_index_block_by_name(block_name) + block = self.efuses.blocks[block_num] + + if isinstance(datafile, IOBase): + data = datafile.read() + datafile.close() + else: + data = datafile # type: ignore # this is safe but mypy still complains + + log.print(f" - {efuse.name}", end=" ") + revers_msg = None + if self.efuses[block.key_purpose_name].need_reverse(keypurpose): + revers_msg = "\tReversing byte order for AES-XTS hardware peripheral..." + data = data[::-1] + log.print( + "-> [{}]".format( + util.hexify(data, " ") + if show_sensitive_info + else " ".join(["??"] * len(data)) + ) + ) + if revers_msg: + log.print(revers_msg) + if len(data) != num_bytes: + raise esptool.FatalError( + f"Incorrect key file size {len(data)}. Key file must be {num_bytes} " + f"bytes ({num_bytes * 8} bits) of raw binary key 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, + if self.efuses[block.key_purpose_name].need_rd_protect(keypurpose): + read_protect = False if no_read_protect else True + else: + read_protect = False + write_protect = not 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 self.efuses[block.key_purpose_name].get() != keypurpose: + if self.efuses[block.key_purpose_name].is_writeable(): + log.print( + f"\t'{block.key_purpose_name}': " + f"'{self.efuses[block.key_purpose_name].get()}' -> '{keypurpose}'." + ) + self.efuses[block.key_purpose_name].save(keypurpose) + disable_wr_protect_key_purpose = True + else: + raise esptool.FatalError( + f"It is not possible to change '{block.key_purpose_name}' " + f"to '{keypurpose}' because write protection bit is set." ) - ) - efuses[block.key_purpose_name].save(keypurpose) - disable_wr_protect_key_purpose = True else: + log.print(f"\t'{block.key_purpose_name}' is already '{keypurpose}'.") + if self.efuses[block.key_purpose_name].is_writeable(): + disable_wr_protect_key_purpose = True + + if disable_wr_protect_key_purpose: + log.print(f"\tDisabling write to '{block.key_purpose_name}'...") + self.efuses[block.key_purpose_name].disable_write() + + if read_protect: + log.print("\tDisabling read to key block...") + efuse.disable_read() + + if write_protect: + log.print("\tDisabling write to key block...") + efuse.disable_write() + log.print("") + + if not write_protect: + log.print("Keys will remain writeable (due to --no-write-protect).") + if no_read_protect: + log.print("Keys will remain readable (due to --no-read-protect).") + + if not self.efuses.burn_all(check_batch_mode=True): + return + log.print("Successful.") + + def burn_key_digest( + self, + blocks: list[str], + keyfiles: list[BinaryIO], + keypurposes: list[str], + no_write_protect: bool = False, + no_read_protect: bool = False, + show_sensitive_info: bool = False, + ): + """Parse a RSA public key and burn the digest to key eFuse block. + + Args: + blocks: List of eFuse block names to burn keys to. + keyfiles: List of open files to read key data from. + keypurposes: List of key purposes to burn. + no_write_protect: If True, the write protection will NOT be enabled. + no_read_protect: If True, the read protection will NOT be enabled. + show_sensitive_info: If True, the sensitive information will be shown. + """ + digest_list = [] + datafile_list = keyfiles[ + 0 : len([name for name in keyfiles if name is not None]) : + ] + block_list = blocks[0 : len([block for block in blocks if block is not None]) :] + + for block_name, datafile in zip(block_list, datafile_list): + efuse = None + for block in self.efuses.blocks: + if block_name == block.name or block_name in block.alias: + efuse = self.efuses[block.name] + if efuse is None: + raise esptool.FatalError(f"Unknown block name - {block_name}.") + num_bytes = efuse.bit_len // 8 + digest = espsecure._digest_sbv2_public_key(datafile) + if len(digest) != num_bytes: raise esptool.FatalError( - "It is not possible to change '%s' to '%s' " - "because write protection bit is set." - % (block.key_purpose_name, keypurpose) + "Incorrect digest size %d. Digest must be %d bytes (%d bits) " + "of raw binary key data." % (len(digest), num_bytes, num_bytes * 8) ) - 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") + digest_list.append(digest) + + self.burn_key( + block_list, + datafile_list, + keypurposes, + no_write_protect, + no_read_protect, + show_sensitive_info, + digest=digest_list, + ) diff --git a/tools/esptool_py/espefuse/efuse/esp32c5/__init__.py b/tools/esptool_py/espefuse/efuse/esp32c5/__init__.py index a3b55a8023..94da102b64 100644 --- a/tools/esptool_py/espefuse/efuse/esp32c5/__init__.py +++ b/tools/esptool_py/espefuse/efuse/esp32c5/__init__.py @@ -1,3 +1,5 @@ from . import operations from .emulate_efuse_controller import EmulateEfuseController from .fields import EspEfuses + +commands = operations.ESP32C5Commands diff --git a/tools/esptool_py/espefuse/efuse/esp32c5/fields.py b/tools/esptool_py/espefuse/efuse/esp32c5/fields.py index b61ba6077b..63992c56c3 100644 --- a/tools/esptool_py/espefuse/efuse/esp32c5/fields.py +++ b/tools/esptool_py/espefuse/efuse/esp32c5/fields.py @@ -12,6 +12,7 @@ from bitstring import BitArray import esptool +from esptool.logger import log import reedsolo @@ -55,9 +56,6 @@ 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, @@ -66,14 +64,12 @@ def __init__( do_not_confirm=False, extend_efuse_table=None, ): + super().__init__(esp, skip_connect, debug, do_not_confirm, extend_efuse_table) 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-C5": raise esptool.FatalError( "Expected the 'esp' param for ESP32-C5 chip but got for '%s'." @@ -102,11 +98,11 @@ def __init__( 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 - # ] + if self.get_block_version() >= 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 ] @@ -136,14 +132,14 @@ def read_coding_scheme(self): self.coding_scheme = self.REGS.CODING_SCHEME_RS def print_status_regs(self): - print("") + log.print("") self.blocks[0].print_block(self.blocks[0].err_bitarray, "err__regs", debug=True) - print( + log.print( "{:27} 0x{:08x}".format( "EFUSE_RD_RS_ERR0_REG", self.read_reg(self.REGS.EFUSE_RD_RS_ERR0_REG) ) ) - print( + log.print( "{:27} 0x{:08x}".format( "EFUSE_RD_RS_ERR1_REG", self.read_reg(self.REGS.EFUSE_RD_RS_ERR1_REG) ) @@ -176,7 +172,7 @@ def wait_efuse_idle(self): # For PGM_CMD it is not necessary. return raise esptool.FatalError( - "Timed out waiting for Efuse controller command to complete" + "Timed out waiting for eFuse controller command to complete" ) def efuse_program(self, block): @@ -204,30 +200,30 @@ def efuse_read(self): try: self._esp = self.reconnect_chip(self._esp) except esptool.FatalError: - print("Can not re-connect to the chip") + log.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( + log.print( "This is the correct behavior as we are actually burning " - "DIS_DOWNLOAD_MODE which disables the connection to the chip" + "DIS_DOWNLOAD_MODE which disables the connection to the chip." ) - print("DIS_DOWNLOAD_MODE is enabled") - print("Successful") + log.print("DIS_DOWNLOAD_MODE is enabled.") + log.print("Successful.") sys.exit(0) # finish without errors raise - print("Established a connection with the chip") + log.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") + log.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" + log.print( + "espefuse can not continue to work in Secure download mode." ) - print("ENABLE_SECURITY_DOWNLOAD is enabled") - print("Successful") + log.print("ENABLE_SECURITY_DOWNLOAD is enabled.") + log.print("Successful.") sys.exit(0) # finish without errors raise @@ -280,7 +276,7 @@ def get_coding_scheme_warnings(self, silent=False): 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( + log.print( "Error(s) in BLOCK%d [ERRORS:%d FAIL:%d]" % (block.id, block.num_errors, block.fail) ) @@ -301,20 +297,55 @@ def convert(parent, efuse): "keypurpose": EfuseKeyPurposeField, "t_sensor": EfuseTempSensor, "adc_tp": EfuseAdcPointCalibration, - "wafer": EfuseWafer, + "recovery_bootloader": EfuseBtldrRecoveryField, + "bootloader_anti_rollback": EfuseBtldrAntiRollbackField, }.get(efuse.class_type, EfuseField)(parent, efuse) -class EfuseWafer(EfuseField): +class EfuseBtldrRecoveryField(EfuseField): def get(self, from_read=True): - hi_bits = self.parent["WAFER_VERSION_MINOR_HI"].get(from_read) - assert self.parent["WAFER_VERSION_MINOR_HI"].bit_len == 1 - lo_bits = self.parent["WAFER_VERSION_MINOR_LO"].get(from_read) - assert self.parent["WAFER_VERSION_MINOR_LO"].bit_len == 3 + hi_bits = self.parent["RECOVERY_BOOTLOADER_FLASH_SECTOR_HI"].get(from_read) + assert self.parent["RECOVERY_BOOTLOADER_FLASH_SECTOR_HI"].bit_len == 3 + lo_bits = self.parent["RECOVERY_BOOTLOADER_FLASH_SECTOR_LO"].get(from_read) + assert self.parent["RECOVERY_BOOTLOADER_FLASH_SECTOR_LO"].bit_len == 9 + return (hi_bits << 9) + lo_bits + + def save(self, new_value): + efuse = self.parent["RECOVERY_BOOTLOADER_FLASH_SECTOR_HI"] + efuse.save((new_value >> 9) & 3) + log.print( + f"\t - '{efuse.name}' {efuse.get_bitstring()} -> {efuse.get_bitstring(from_read=False)}" + ) + efuse = self.parent["RECOVERY_BOOTLOADER_FLASH_SECTOR_LO"] + efuse.save(new_value & 0x1FF) + log.print( + f"\t - '{efuse.name}' {efuse.get_bitstring()} -> {efuse.get_bitstring(from_read=False)}" + ) + + +class EfuseBtldrAntiRollbackField(EfuseField): + def get(self, from_read=True): + hi_bits = self.parent["BOOTLOADER_ANTI_ROLLBACK_SECURE_VERSION_HI"].get( + from_read + ) + assert self.parent["BOOTLOADER_ANTI_ROLLBACK_SECURE_VERSION_HI"].bit_len == 1 + lo_bits = self.parent["BOOTLOADER_ANTI_ROLLBACK_SECURE_VERSION_LO"].get( + from_read + ) + assert self.parent["BOOTLOADER_ANTI_ROLLBACK_SECURE_VERSION_LO"].bit_len == 3 return (hi_bits << 3) + lo_bits def save(self, new_value): - raise esptool.FatalError("Burning %s is not supported" % self.name) + efuse = self.parent["BOOTLOADER_ANTI_ROLLBACK_SECURE_VERSION_HI"] + efuse.save((new_value >> 3) & 1) + log.print( + f"\t - '{efuse.name}' {efuse.get_bitstring()} -> {efuse.get_bitstring(from_read=False)}" + ) + efuse = self.parent["BOOTLOADER_ANTI_ROLLBACK_SECURE_VERSION_LO"] + efuse.save(new_value & 0x7) + log.print( + f"\t - '{efuse.name}' {efuse.get_bitstring()} -> {efuse.get_bitstring(from_read=False)}" + ) class EfuseTempSensor(EfuseField): @@ -364,7 +395,7 @@ def check_format(self, new_value_str): 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) + output = f"Block{self.block} has ERRORS:{errs} FAIL:{fail}." else: output = "OK" return "(" + output + ")" @@ -385,10 +416,8 @@ def get(self, from_read=True): def save(self, new_value): def print_field(e, new_value): - print( - " - '{}' ({}) {} -> {}".format( - e.name, e.description, e.get_bitstring(), new_value - ) + log.print( + f" - '{e.name}' ({e.description}) {e.get_bitstring()} -> {new_value}" ) if self.name == "CUSTOM_MAC": @@ -398,15 +427,19 @@ def print_field(e, 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") + raise esptool.FatalError(f"Burning {self.name} is not supported.") # fmt: off class EfuseKeyPurposeField(EfuseField): 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 + ("ECDSA_KEY", 1, None, "Reverse", "need_rd_protect"), # ECDSA key P256 + ("ECDSA_KEY_P256", 1, None, "Reverse", "need_rd_protect"), # ECDSA key P256 ("RESERVED", 1, None, None, "no_need_rd_protect"), # Reserved + ("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_256_KEY", -1, "VIRTUAL", None, "no_need_rd_protect"), # Virtual purpose splits to XTS_AES_256_KEY_1 and XTS_AES_256_KEY_2 ("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) @@ -415,6 +448,14 @@ class EfuseKeyPurposeField(EfuseField): ("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 + ("XTS_AES_256_PSRAM_KEY_1", 13, None, "Reverse", "need_rd_protect"), # XTS_AES_256_PSRAM_KEY_1 (PSRAM encryption) + ("XTS_AES_256_PSRAM_KEY_2", 14, None, "Reverse", "need_rd_protect"), # XTS_AES_256_PSRAM_KEY_1 (PSRAM encryption) + # ("XTS_AES_256_PSRAM_KEY", -2, "VIRTUAL", None, "no_need_rd_protect"), # Virtual purpose splits to XTS_AES_256_PSRAM_KEY_1 and XTS_AES_256_PSRAM_KEY_1 + ("XTS_AES_128_PSRAM_KEY", 15, None, "Reverse", "need_rd_protect"), # XTS_AES_128_PSRAM_KEY (PSRAM encryption) + ("ECDSA_KEY_P192", 16, None, "Reverse", "need_rd_protect"), # ECDSA key P192 + ("ECDSA_KEY_P384_L", 17, None, "Reverse", "need_rd_protect"), # ECDSA key P384 low + ("ECDSA_KEY_P384_H", 18, None, "Reverse", "need_rd_protect"), # ECDSA key P384 high ] # fmt: on KEY_PURPOSES_NAME = [name[0] for name in KEY_PURPOSES] @@ -430,9 +471,9 @@ def check_format(self, new_value_str): 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) + raise esptool.FatalError(f"'{raw_val}' can not be set (value out of range).") else: - raise esptool.FatalError("'%s' unknown name" % raw_val) + raise esptool.FatalError(f"'{raw_val}' unknown name.") return raw_val def need_reverse(self, new_key_purpose): diff --git a/tools/esptool_py/espefuse/efuse/esp32c5/mem_definition.py b/tools/esptool_py/espefuse/efuse/esp32c5/mem_definition.py index 0520328baf..74643c2535 100644 --- a/tools/esptool_py/espefuse/efuse/esp32c5/mem_definition.py +++ b/tools/esptool_py/espefuse/efuse/esp32c5/mem_definition.py @@ -25,20 +25,20 @@ class EfuseDefineRegisters(EfuseRegistersBase): 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 + 0x1D0 - EFUSE_CMD_REG = DR_REG_EFUSE_BASE + 0x1D4 - EFUSE_RD_RS_ERR0_REG = DR_REG_EFUSE_BASE + 0x1C0 - EFUSE_RD_RS_ERR1_REG = DR_REG_EFUSE_BASE + 0x1C4 + 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 + 0x1E8 - EFUSE_RD_TIM_CONF_REG = DR_REG_EFUSE_BASE + 0x1EC - EFUSE_WR_TIM_CONF1_REG = DR_REG_EFUSE_BASE + 0x1F0 - EFUSE_WR_TIM_CONF2_REG = DR_REG_EFUSE_BASE + 0x1F4 - EFUSE_DATE_REG = DR_REG_EFUSE_BASE + 0x1FC + 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 @@ -114,12 +114,8 @@ 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__)) @@ -162,6 +158,26 @@ def __init__(self, extend_efuse_table) -> None: 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) + f = Field() + f.name = "RECOVERY_BOOTLOADER_FLASH_SECTOR" + f.block = 0 + f.bit_len = 12 + f.type = f"uint:{f.bit_len}" + f.category = "config" + f.class_type = "recovery_bootloader" + f.description = "calc recovery_bootloader = recovery_bootloader_hi << 9 + recovery_bootloader_lo" + self.CALC.append(f) + + f = Field() + f.name = "BOOTLOADER_ANTI_ROLLBACK_SECURE_VERSION" + f.block = 0 + f.bit_len = 4 + f.type = f"uint:{f.bit_len}" + f.category = "config" + f.class_type = "bootloader_anti_rollback" + f.description = "calc ANTI_ROLLBACK_SECURE_VERSION = ANTI_ROLLBACK_SECURE_VERSION_HI << 3 + ANTI_ROLLBACK_SECURE_VERSION_LO" + self.CALC.append(f) + for efuse in self.ALL_EFUSES: if efuse is not None: self.EFUSES.append(efuse) diff --git a/tools/esptool_py/espefuse/efuse/esp32c5/operations.py b/tools/esptool_py/espefuse/efuse/esp32c5/operations.py index 1fca6bcb69..83c571d121 100644 --- a/tools/esptool_py/espefuse/efuse/esp32c5/operations.py +++ b/tools/esptool_py/espefuse/efuse/esp32c5/operations.py @@ -1,395 +1,317 @@ # This file includes the operations with eFuses for ESP32-C5 chip # -# SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD +# SPDX-FileCopyrightText: 2024-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 +from io import IOBase +from typing import BinaryIO +import rich_click as click import espsecure - import esptool +from esptool.logger import log from . import fields +from .mem_definition import EfuseDefineBlocks from .. import util from ..base_operations import ( - add_common_commands, + BaseCommands, + NonCompositeTuple, + TupleParameter, add_force_write_always, add_show_sensitive_info_option, - burn_bit, - burn_block_data, - burn_efuse, - check_error, - dump, - read_protect_efuse, - summary, - write_protect_efuse, + protect_options, ) -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", - ) +class ESP32C5Commands(BaseCommands): + CHIP_NAME = "ESP32-C5" + efuse_lib = fields.EspEfuses + ################################### CLI definitions ################################### -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) - add_show_sensitive_info_option(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, - ) + def add_cli_commands(self, cli: click.Group): + super().add_cli_commands(cli) + blocks_for_keys = EfuseDefineBlocks().get_blocks_for_keys() - 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) - add_show_sensitive_info_option(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, + @cli.command( + "burn-key", + help="Burn the key block with the specified name. Arguments are groups of block name, " + "key file (containing 256 bits of binary key data) and key purpose.\n\n" + f"Block is one of: [{', '.join(blocks_for_keys)}]\n\n" + f"Key purpose is one of: [{', '.join(fields.EfuseKeyPurposeField.KEY_PURPOSES_NAME)}]", ) - burn_key_digest.add_argument( - "keyfile", - help="Key file to digest (PEM format)", - nargs="?", - action="append", - metavar="KEYFILE", - type=argparse.FileType("rb"), + @click.argument( + "block_keyfile_keypurpose", + metavar=" ", + cls=TupleParameter, + required=True, + nargs=-1, + max_arity=len(blocks_for_keys), + type=NonCompositeTuple( + [ + click.Choice(blocks_for_keys), + click.File("rb"), + click.Choice(fields.EfuseKeyPurposeField.KEY_PURPOSES_NAME), + ] + ), ) - burn_key_digest.add_argument( - "keypurpose", - help="Purpose to set.", - nargs="?", - action="append", - metavar="KEYPURPOSE", - choices=fields.EfuseKeyPurposeField.DIGEST_KEY_PURPOSES, + @protect_options + @add_force_write_always + @add_show_sensitive_info_option + @click.pass_context + def burn_key_cli(ctx, **kwargs): + kwargs.pop("force_write_always") + block, keyfile, keypurpose = zip(*kwargs.pop("block_keyfile_keypurpose")) + kwargs["show_sensitive_info"] = ctx.show_sensitive_info + self.burn_key(block, keyfile, keypurpose, **kwargs) + + @cli.command( + "burn-key-digest", + short_help="Parse a RSA public key and burn the digest.", + help="Parse a RSA public key and burn the digest to key eFuse block.\n\n" + f"Block is one of: [{', '.join(blocks_for_keys)}]\n\n" + f"Key purpose is one of: [{', '.join(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): - print("not supported yet") - - -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)) + @click.argument( + "block_keyfile_keypurpose", + metavar=" ", + cls=TupleParameter, + required=True, + nargs=-1, + max_arity=len(blocks_for_keys), + type=NonCompositeTuple( + [ + click.Choice(blocks_for_keys), + click.File("rb"), + click.Choice(fields.EfuseKeyPurposeField.DIGEST_KEY_PURPOSES), + ] + ), ) - - print("Burn keys to blocks:") - for block_name, datafile, keypurpose in zip( - block_name_list, datafile_list, keypurpose_list + @protect_options + @add_force_write_always + @add_show_sensitive_info_option + @click.pass_context + def burn_key_digest_cli(ctx, **kwargs): + kwargs.pop("force_write_always") + block, keyfile, keypurpose = zip(*kwargs.pop("block_keyfile_keypurpose")) + kwargs["show_sensitive_info"] = ctx.show_sensitive_info + self.burn_key_digest(block, keyfile, keypurpose, **kwargs) + + ###################################### Commands ###################################### + + def adc_info(self): + log.print("Block version:", self.efuses.get_block_version()) + if self.efuses.get_block_version() >= 1: + for efuse in self.efuses: + if efuse.category == "calibration": + log.print(f"{efuse.name:<30} = ", self.efuses[efuse.name].get()) + + def burn_key( + self, + blocks: list[str], + keyfiles: list[BinaryIO], + keypurposes: list[str], + no_write_protect: bool = False, + no_read_protect: bool = False, + show_sensitive_info: bool = False, + digest: list[bytes] | None = None, ): - 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] - + """Burn the key block with the specified name. Arguments are groups of block name, + key file (containing 256 bits of binary key data) and key purpose. + + Args: + blocks: List of eFuse block names to burn keys to. + keyfiles: List of open files to read key data from. + keypurposes: List of key purposes to burn. + no_write_protect: If True, the write protection will NOT be enabled. + no_read_protect: If True, the read protection will NOT be enabled. + show_sensitive_info: If True, the sensitive information will be shown. + digest: List of digests to burn. + """ + datafile_list: list[BinaryIO] | list[bytes] 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() + datafile_list = keyfiles[ + 0 : len([name for name in keyfiles if name is not None]) : + ] else: - data = datafile + datafile_list = digest[ + 0 : len([name for name in digest if name is not None]) : + ] + block_name_list = blocks[ + 0 : len([name for name in blocks if name is not None]) : + ] + keypurpose_list = keypurposes[ + 0 : len([name for name in keypurposes if name is not None]) : + ] - 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( - "-> [{}]".format( - util.hexify(data, " ") - if args.show_sensitive_info - else " ".join(["??"] * len(data)) + if "XTS_AES_256_KEY" in keypurpose_list: + # XTS_AES_256_KEY is not an actual HW key purpose, needs to be split into + # XTS_AES_256_KEY_1 and XTS_AES_256_KEY_2 + block_name_list, datafile_list, keypurpose_list = self._split_512_bit_key( + block_name_list, + datafile_list, # type: ignore + keypurpose_list, ) - ) - if revers_msg: - print(revers_msg) - if len(data) != num_bytes: + + 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( - "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) + "The number of blocks (%d), datafile (%d) and keypurpose (%d) " + "should be the same." + % (len(block_name_list), len(datafile_list), len(keypurpose_list)) ) - 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 + log.print("Burn keys to blocks:") + for block_name, datafile, keypurpose in zip( + block_name_list, datafile_list, keypurpose_list + ): + efuse = None + for block in self.efuses.blocks: + if block_name == block.name or block_name in block.alias: + efuse = self.efuses[block.name] + if efuse is None: + raise esptool.FatalError(f"Unknown block name - {block_name}.") + num_bytes = efuse.bit_len // 8 + + block_num = self.efuses.get_index_block_by_name(block_name) + block = self.efuses.blocks[block_num] + + if isinstance(datafile, IOBase): + if keypurpose.startswith("ECDSA_KEY"): + sk = espsecure.load_ecdsa_signing_key(datafile) # type: ignore + data = espsecure.get_ecdsa_signing_key_raw_bytes(sk) + 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() + datafile.close() + else: + data = datafile - # using efuse instead of a block gives the advantage of checking it as the whole field. - efuse.save(data) + log.print(f" - {efuse.name}", end=" ") + revers_msg = None + if self.efuses[block.key_purpose_name].need_reverse(keypurpose): + revers_msg = ( + f"\tReversing byte order for {keypurpose} hardware peripheral..." + ) + data = data[::-1] + log.print( + "-> [{}]".format( + util.hexify(data, " ") + if show_sensitive_info + else " ".join(["??"] * len(data)) + ) + ) + if revers_msg: + log.print(revers_msg) + if len(data) != num_bytes: + raise esptool.FatalError( + f"Incorrect key file size {len(data)}. Key file must be {num_bytes} " + f"bytes ({num_bytes * 8} bits) of raw binary key 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, + if self.efuses[block.key_purpose_name].need_rd_protect(keypurpose): + read_protect = False if no_read_protect else True + else: + read_protect = False + write_protect = not 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 self.efuses[block.key_purpose_name].get() != keypurpose: + if self.efuses[block.key_purpose_name].is_writeable(): + log.print( + f"\t'{block.key_purpose_name}': " + f"'{self.efuses[block.key_purpose_name].get()}' -> '{keypurpose}'." + ) + self.efuses[block.key_purpose_name].save(keypurpose) + disable_wr_protect_key_purpose = True + else: + raise esptool.FatalError( + f"It is not possible to change '{block.key_purpose_name}' " + f"to '{keypurpose}' because write protection bit is set." ) - ) - efuses[block.key_purpose_name].save(keypurpose) - disable_wr_protect_key_purpose = True else: + log.print(f"\t'{block.key_purpose_name}' is already '{keypurpose}'.") + if self.efuses[block.key_purpose_name].is_writeable(): + disable_wr_protect_key_purpose = True + + if disable_wr_protect_key_purpose: + log.print(f"\tDisabling write to '{block.key_purpose_name}'...") + self.efuses[block.key_purpose_name].disable_write() + + if read_protect: + log.print("\tDisabling read to key block...") + efuse.disable_read() + + if write_protect: + log.print("\tDisabling write to key block...") + efuse.disable_write() + log.print("") + + if not write_protect: + log.print("Keys will remain writeable (due to --no-write-protect).") + if no_read_protect: + log.print("Keys will remain readable (due to --no-read-protect).") + + if not self.efuses.burn_all(check_batch_mode=True): + return + log.print("Successful.") + + def burn_key_digest( + self, + blocks: list[str], + keyfiles: list[BinaryIO], + keypurposes: list[str], + no_write_protect: bool = False, + no_read_protect: bool = False, + show_sensitive_info: bool = False, + ): + """Parse a RSA public key and burn the digest to key eFuse block. + + Args: + blocks: List of eFuse block names to burn keys to. + keyfiles: List of open files to read key data from. + keypurposes: List of key purposes to burn. + no_write_protect: If True, the write protection will NOT be enabled. + no_read_protect: If True, the read protection will NOT be enabled. + show_sensitive_info: If True, the sensitive information will be shown. + """ + digest_list = [] + datafile_list = keyfiles[ + 0 : len([name for name in keyfiles if name is not None]) : + ] + block_list = blocks[0 : len([block for block in blocks if block is not None]) :] + + for block_name, datafile in zip(block_list, datafile_list): + efuse = None + for blk in self.efuses.blocks: + if block_name == blk.name or block_name in blk.alias: + efuse = self.efuses[blk.name] + if efuse is None: + raise esptool.FatalError(f"Unknown block name - {block_name}.") + num_bytes = efuse.bit_len // 8 + digest = espsecure._digest_sbv2_public_key(datafile) + if len(digest) != num_bytes: raise esptool.FatalError( - "It is not possible to change '%s' to '%s' " - "because write protection bit is set." - % (block.key_purpose_name, keypurpose) + f"Incorrect digest size {len(digest)}. Digest must be {num_bytes} " + f"bytes ({num_bytes * 8} bits) of raw binary key data." ) - 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") + digest_list.append(digest) + + self.burn_key( + block_list, + datafile_list, + keypurposes, + no_write_protect, + no_read_protect, + show_sensitive_info, + digest=digest_list, + ) diff --git a/tools/esptool_py/espefuse/efuse/esp32c5beta3/operations.py b/tools/esptool_py/espefuse/efuse/esp32c5beta3/operations.py deleted file mode 100644 index fbd721bd63..0000000000 --- a/tools/esptool_py/espefuse/efuse/esp32c5beta3/operations.py +++ /dev/null @@ -1,395 +0,0 @@ -# This file includes the operations with eFuses for ESP32-C5 beta3 chip -# -# SPDX-FileCopyrightText: 2023 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, - add_show_sensitive_info_option, - 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) - add_show_sensitive_info_option(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) - add_show_sensitive_info_option(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): - print("not supported yet") - - -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( - "-> [{}]".format( - util.hexify(data, " ") - if args.show_sensitive_info - else " ".join(["??"] * len(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") diff --git a/tools/esptool_py/espefuse/efuse/esp32c6/__init__.py b/tools/esptool_py/espefuse/efuse/esp32c6/__init__.py index a3b55a8023..89782d5efc 100644 --- a/tools/esptool_py/espefuse/efuse/esp32c6/__init__.py +++ b/tools/esptool_py/espefuse/efuse/esp32c6/__init__.py @@ -1,3 +1,5 @@ from . import operations from .emulate_efuse_controller import EmulateEfuseController from .fields import EspEfuses + +commands = operations.ESP32C6Commands diff --git a/tools/esptool_py/espefuse/efuse/esp32c6/fields.py b/tools/esptool_py/espefuse/efuse/esp32c6/fields.py index 70df55cecd..47662c6fc9 100644 --- a/tools/esptool_py/espefuse/efuse/esp32c6/fields.py +++ b/tools/esptool_py/espefuse/efuse/esp32c6/fields.py @@ -8,7 +8,7 @@ import struct import sys import time - +from esptool.logger import log from bitstring import BitArray import esptool @@ -55,9 +55,6 @@ 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, @@ -66,14 +63,12 @@ def __init__( do_not_confirm=False, extend_efuse_table=None, ): + super().__init__(esp, skip_connect, debug, do_not_confirm, extend_efuse_table) 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-C6": raise esptool.FatalError( "Expected the 'esp' param for ESP32-C6 chip but got for '%s'." @@ -102,7 +97,7 @@ def __init__( for efuse in self.Fields.BLOCK2_CALIBRATION_EFUSES ] else: - if self["BLK_VERSION_MINOR"].get() == 1: + if self.get_block_version() >= 1: self.efuses += [ EfuseField.convert(self, efuse) for efuse in self.Fields.BLOCK2_CALIBRATION_EFUSES @@ -136,14 +131,14 @@ def read_coding_scheme(self): self.coding_scheme = self.REGS.CODING_SCHEME_RS def print_status_regs(self): - print("") + log.print("") self.blocks[0].print_block(self.blocks[0].err_bitarray, "err__regs", debug=True) - print( + log.print( "{:27} 0x{:08x}".format( "EFUSE_RD_RS_ERR0_REG", self.read_reg(self.REGS.EFUSE_RD_RS_ERR0_REG) ) ) - print( + log.print( "{:27} 0x{:08x}".format( "EFUSE_RD_RS_ERR1_REG", self.read_reg(self.REGS.EFUSE_RD_RS_ERR1_REG) ) @@ -176,7 +171,7 @@ def wait_efuse_idle(self): # For PGM_CMD it is not necessary. return raise esptool.FatalError( - "Timed out waiting for Efuse controller command to complete" + "Timed out waiting for eFuse controller command to complete" ) def efuse_program(self, block): @@ -204,30 +199,30 @@ def efuse_read(self): try: self._esp = self.reconnect_chip(self._esp) except esptool.FatalError: - print("Can not re-connect to the chip") + log.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( + log.print( "This is the correct behavior as we are actually burning " - "DIS_DOWNLOAD_MODE which disables the connection to the chip" + "DIS_DOWNLOAD_MODE which disables the connection to the chip." ) - print("DIS_DOWNLOAD_MODE is enabled") - print("Successful") + log.print("DIS_DOWNLOAD_MODE is enabled.") + log.print("Successful.") sys.exit(0) # finish without errors raise - print("Established a connection with the chip") + log.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") + log.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" + log.print( + "espefuse can not continue to work in Secure download mode." ) - print("ENABLE_SECURITY_DOWNLOAD is enabled") - print("Successful") + log.print("ENABLE_SECURITY_DOWNLOAD is enabled.") + log.print("Successful.") sys.exit(0) # finish without errors raise @@ -280,9 +275,9 @@ def get_coding_scheme_warnings(self, silent=False): 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) + log.print( + f"Error(s) in BLOCK{block.id} " + f"[ERRORS:{block.num_errors} FAIL:{block.fail}]." ) if (self.debug or ret_fail) and not silent: self.print_status_regs() @@ -364,7 +359,7 @@ def check_format(self, new_value_str): 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) + output = f"Block{self.block} has ERRORS:{errs} FAIL:{fail}" else: output = "OK" return "(" + output + ")" @@ -385,10 +380,8 @@ def get(self, from_read=True): def save(self, new_value): def print_field(e, new_value): - print( - " - '{}' ({}) {} -> {}".format( - e.name, e.description, e.get_bitstring(), new_value - ) + log.print( + f" - '{e.name}' ({e.description}) {e.get_bitstring()} -> {new_value}" ) if self.name == "CUSTOM_MAC": @@ -429,9 +422,9 @@ def check_format(self, new_value_str): 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) + raise esptool.FatalError(f"'{raw_val}' can not be set (value out of range).") else: - raise esptool.FatalError("'%s' unknown name" % raw_val) + raise esptool.FatalError(f"'{raw_val}' unknown name.") return raw_val def need_reverse(self, new_key_purpose): diff --git a/tools/esptool_py/espefuse/efuse/esp32c6/mem_definition.py b/tools/esptool_py/espefuse/efuse/esp32c6/mem_definition.py index 4574fdf63e..788828a366 100644 --- a/tools/esptool_py/espefuse/efuse/esp32c6/mem_definition.py +++ b/tools/esptool_py/espefuse/efuse/esp32c6/mem_definition.py @@ -114,12 +114,8 @@ 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__)) diff --git a/tools/esptool_py/espefuse/efuse/esp32c6/operations.py b/tools/esptool_py/espefuse/efuse/esp32c6/operations.py index 9307ecac3b..cf04aef71f 100644 --- a/tools/esptool_py/espefuse/efuse/esp32c6/operations.py +++ b/tools/esptool_py/espefuse/efuse/esp32c6/operations.py @@ -1,411 +1,315 @@ # This file includes the operations with eFuses for ESP32-C6 chip # -# SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD +# SPDX-FileCopyrightText: 2022-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 +from io import IOBase +from typing import BinaryIO +from esptool.logger import log +import rich_click as click import espsecure - import esptool from . import fields +from .mem_definition import EfuseDefineBlocks from .. import util from ..base_operations import ( - add_common_commands, + BaseCommands, + NonCompositeTuple, + TupleParameter, add_force_write_always, add_show_sensitive_info_option, - burn_bit, - burn_block_data, - burn_efuse, - check_error, - dump, - read_protect_efuse, - summary, - write_protect_efuse, + protect_options, ) -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", - ) +class ESP32C6Commands(BaseCommands): + CHIP_NAME = "ESP32-C6" + efuse_lib = fields.EspEfuses + ################################### CLI definitions ################################### -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) - add_show_sensitive_info_option(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", - 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", - 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, - ) + def add_cli_commands(self, cli: click.Group): + super().add_cli_commands(cli) + blocks_for_keys = EfuseDefineBlocks().get_blocks_for_keys() - 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) - add_show_sensitive_info_option(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, + @cli.command( + "burn-key", + help="Burn the key block with the specified name. Arguments are groups of block name, " + "key file (containing 256 bits of binary key data) and key purpose.\n\n" + f"Block is one of: [{', '.join(blocks_for_keys)}]\n\n" + f"Key purpose is one of: [{', '.join(fields.EfuseKeyPurposeField.KEY_PURPOSES_NAME)}]", ) - burn_key_digest.add_argument( - "keyfile", - help="Key file to digest (PEM format)", - nargs="?", - action="append", - metavar="KEYFILE", - type=argparse.FileType("rb"), + @click.argument( + "block_keyfile_keypurpose", + metavar=" ", + cls=TupleParameter, + required=True, + nargs=-1, + max_arity=len(blocks_for_keys), + type=NonCompositeTuple( + [ + click.Choice(blocks_for_keys), + click.File("rb"), + click.Choice(fields.EfuseKeyPurposeField.KEY_PURPOSES_NAME), + ] + ), ) - burn_key_digest.add_argument( - "keypurpose", - help="Purpose to set.", - nargs="?", - action="append", - metavar="KEYPURPOSE", - choices=fields.EfuseKeyPurposeField.DIGEST_KEY_PURPOSES, + @protect_options + @add_force_write_always + @add_show_sensitive_info_option + @click.pass_context + def burn_key_cli(ctx, **kwargs): + kwargs.pop("force_write_always") + block, keyfile, keypurpose = zip(*kwargs.pop("block_keyfile_keypurpose")) + kwargs["show_sensitive_info"] = ctx.show_sensitive_info + self.burn_key(block, keyfile, keypurpose, **kwargs) + + @cli.command( + "burn-key-digest", + short_help="Parse a RSA public key and burn the digest.", + help="Parse a RSA public key and burn the digest to key eFuse block.\n\n" + f"Block is one of: [{', '.join(blocks_for_keys)}]\n\n" + f"Key purpose is one of: [{', '.join(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): - print("") - # fmt: off - if efuses["BLK_VERSION_MINOR"].get() == 1: - print("Temperature Sensor Calibration = {}C".format(efuses["TEMP_CALIB"].get())) - print("ADC OCode = ", efuses["OCODE"].get()) - print("ADC1:") - print("INIT_CODE_ATTEN0 = ", efuses['ADC1_INIT_CODE_ATTEN0'].get()) - print("INIT_CODE_ATTEN1 = ", efuses['ADC1_INIT_CODE_ATTEN1'].get()) - print("INIT_CODE_ATTEN2 = ", efuses['ADC1_INIT_CODE_ATTEN2'].get()) - print("INIT_CODE_ATTEN3 = ", efuses['ADC1_INIT_CODE_ATTEN3'].get()) - print("CAL_VOL_ATTEN0 = ", efuses['ADC1_CAL_VOL_ATTEN0'].get()) - print("CAL_VOL_ATTEN1 = ", efuses['ADC1_CAL_VOL_ATTEN1'].get()) - print("CAL_VOL_ATTEN2 = ", efuses['ADC1_CAL_VOL_ATTEN2'].get()) - print("CAL_VOL_ATTEN3 = ", efuses['ADC1_CAL_VOL_ATTEN3'].get()) - print("INIT_CODE_ATTEN0_CH0 = ", efuses['ADC1_INIT_CODE_ATTEN0_CH0'].get()) - print("INIT_CODE_ATTEN0_CH1 = ", efuses['ADC1_INIT_CODE_ATTEN0_CH1'].get()) - print("INIT_CODE_ATTEN0_CH2 = ", efuses['ADC1_INIT_CODE_ATTEN0_CH2'].get()) - print("INIT_CODE_ATTEN0_CH3 = ", efuses['ADC1_INIT_CODE_ATTEN0_CH3'].get()) - print("INIT_CODE_ATTEN0_CH4 = ", efuses['ADC1_INIT_CODE_ATTEN0_CH4'].get()) - print("INIT_CODE_ATTEN0_CH5 = ", efuses['ADC1_INIT_CODE_ATTEN0_CH5'].get()) - print("INIT_CODE_ATTEN0_CH6 = ", efuses['ADC1_INIT_CODE_ATTEN0_CH6'].get()) - else: - print("BLK_VERSION_MINOR = {}".format(efuses["BLK_VERSION_MINOR"].get_meaning())) - # 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)) + @click.argument( + "block_keyfile_keypurpose", + metavar=" ", + cls=TupleParameter, + required=True, + nargs=-1, + max_arity=len(blocks_for_keys), + type=NonCompositeTuple( + [ + click.Choice(blocks_for_keys), + click.File("rb"), + click.Choice(fields.EfuseKeyPurposeField.DIGEST_KEY_PURPOSES), + ] + ), ) - - print("Burn keys to blocks:") - for block_name, datafile, keypurpose in zip( - block_name_list, datafile_list, keypurpose_list + @protect_options + @add_force_write_always + @add_show_sensitive_info_option + @click.pass_context + def burn_key_digest_cli(ctx, **kwargs): + kwargs.pop("force_write_always") + block, keyfile, keypurpose = zip(*kwargs.pop("block_keyfile_keypurpose")) + kwargs["show_sensitive_info"] = ctx.show_sensitive_info + self.burn_key_digest(block, keyfile, keypurpose, **kwargs) + + ###################################### Commands ###################################### + + def adc_info(self): + log.print("Block version:", self.efuses.get_block_version()) + if self.efuses.get_block_version() >= 1: + # fmt: off + log.print(f"Temperature Sensor Calibration = {self.efuses['TEMP_CALIB'].get()}C") + log.print("ADC OCode = ", self.efuses["OCODE"].get()) + log.print("ADC1:") + log.print("INIT_CODE_ATTEN0 = ", self.efuses['ADC1_INIT_CODE_ATTEN0'].get()) + log.print("INIT_CODE_ATTEN1 = ", self.efuses['ADC1_INIT_CODE_ATTEN1'].get()) + log.print("INIT_CODE_ATTEN2 = ", self.efuses['ADC1_INIT_CODE_ATTEN2'].get()) + log.print("INIT_CODE_ATTEN3 = ", self.efuses['ADC1_INIT_CODE_ATTEN3'].get()) + log.print("CAL_VOL_ATTEN0 = ", self.efuses['ADC1_CAL_VOL_ATTEN0'].get()) + log.print("CAL_VOL_ATTEN1 = ", self.efuses['ADC1_CAL_VOL_ATTEN1'].get()) + log.print("CAL_VOL_ATTEN2 = ", self.efuses['ADC1_CAL_VOL_ATTEN2'].get()) + log.print("CAL_VOL_ATTEN3 = ", self.efuses['ADC1_CAL_VOL_ATTEN3'].get()) + log.print("INIT_CODE_ATTEN0_CH0 = ", self.efuses['ADC1_INIT_CODE_ATTEN0_CH0'].get()) + log.print("INIT_CODE_ATTEN0_CH1 = ", self.efuses['ADC1_INIT_CODE_ATTEN0_CH1'].get()) + log.print("INIT_CODE_ATTEN0_CH2 = ", self.efuses['ADC1_INIT_CODE_ATTEN0_CH2'].get()) + log.print("INIT_CODE_ATTEN0_CH3 = ", self.efuses['ADC1_INIT_CODE_ATTEN0_CH3'].get()) + log.print("INIT_CODE_ATTEN0_CH4 = ", self.efuses['ADC1_INIT_CODE_ATTEN0_CH4'].get()) + log.print("INIT_CODE_ATTEN0_CH5 = ", self.efuses['ADC1_INIT_CODE_ATTEN0_CH5'].get()) + log.print("INIT_CODE_ATTEN0_CH6 = ", self.efuses['ADC1_INIT_CODE_ATTEN0_CH6'].get()) + # fmt: on + + def burn_key( + self, + blocks: list[str], + keyfiles: list[BinaryIO], + keypurposes: list[str], + no_write_protect: bool = False, + no_read_protect: bool = False, + show_sensitive_info: bool = False, + digest: list[bytes] | None = None, ): - 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] - + """Burn the key block with the specified name. Arguments are groups of block name, + key file (containing 256 bits of binary key data) and key purpose. + + Args: + blocks: List of eFuse block names to burn keys to. + keyfiles: List of open files to read key data from. + keypurposes: List of key purposes to burn. + no_write_protect: If True, the write protection will NOT be enabled. + no_read_protect: If True, the read protection will NOT be enabled. + show_sensitive_info: If True, the sensitive information will be shown. + digest: List of digests to burn. + """ + datafile_list: list[BinaryIO] | list[bytes] if digest is None: - data = datafile.read() + datafile_list = keyfiles[ + 0 : len([name for name in keyfiles if name is not None]) : + ] else: - data = datafile + datafile_list = digest[ + 0 : len([name for name in digest if name is not None]) : + ] + block_name_list = blocks[ + 0 : len([name for name in blocks if name is not None]) : + ] + keypurpose_list = keypurposes[ + 0 : len([name for name in keypurposes if name is not None]) : + ] - print(" - %s" % (efuse.name), end=" ") - revers_msg = None - if efuses[block.key_purpose_name].need_reverse(keypurpose): - revers_msg = "\tReversing byte order for AES-XTS hardware peripheral" - data = data[::-1] - print( - "-> [{}]".format( - util.hexify(data, " ") - if args.show_sensitive_info - else " ".join(["??"] * len(data)) - ) - ) - if revers_msg: - print(revers_msg) - if len(data) != num_bytes: + 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( - "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) + "The number of blocks (%d), datafile (%d) and keypurpose (%d) should be the same." + % (len(block_name_list), len(datafile_list), len(keypurpose_list)) ) - 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) + log.print("Burn keys to blocks:") + for block_name, datafile, keypurpose in zip( + block_name_list, datafile_list, keypurpose_list + ): + efuse = None + for block in self.efuses.blocks: + if block_name == block.name or block_name in block.alias: + efuse = self.efuses[block.name] + if efuse is None: + raise esptool.FatalError(f"Unknown block name - {block_name}.") + num_bytes = efuse.bit_len // 8 + + block_num = self.efuses.get_index_block_by_name(block_name) + block = self.efuses.blocks[block_num] + + if isinstance(datafile, IOBase): + data = datafile.read() + datafile.close() + else: + data = datafile # type: ignore # this is safe but mypy still complains + + log.print(f" - {efuse.name}", end=" ") + revers_msg = None + if self.efuses[block.key_purpose_name].need_reverse(keypurpose): + revers_msg = "\tReversing byte order for AES-XTS hardware peripheral..." + data = data[::-1] + log.print( + "-> [{}]".format( + util.hexify(data, " ") + if show_sensitive_info + else " ".join(["??"] * len(data)) + ) + ) + if revers_msg: + log.print(revers_msg) + if len(data) != num_bytes: + raise esptool.FatalError( + f"Incorrect key file size {len(data)}. Key file must be {num_bytes} " + f"bytes ({num_bytes * 8} bits) of raw binary key 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, + if self.efuses[block.key_purpose_name].need_rd_protect(keypurpose): + read_protect = False if no_read_protect else True + else: + read_protect = False + write_protect = not 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 self.efuses[block.key_purpose_name].get() != keypurpose: + if self.efuses[block.key_purpose_name].is_writeable(): + log.print( + f"\t'{block.key_purpose_name}': " + f"'{self.efuses[block.key_purpose_name].get()}' -> '{keypurpose}'." + ) + self.efuses[block.key_purpose_name].save(keypurpose) + disable_wr_protect_key_purpose = True + else: + raise esptool.FatalError( + f"It is not possible to change '{block.key_purpose_name}' " + f"to '{keypurpose}' because write protection bit is set." ) - ) - efuses[block.key_purpose_name].save(keypurpose) - disable_wr_protect_key_purpose = True else: + log.print(f"\t'{block.key_purpose_name}' is already '{keypurpose}'.") + if self.efuses[block.key_purpose_name].is_writeable(): + disable_wr_protect_key_purpose = True + + if disable_wr_protect_key_purpose: + log.print(f"\tDisabling write to '{block.key_purpose_name}'...") + self.efuses[block.key_purpose_name].disable_write() + + if read_protect: + log.print("\tDisabling read to key block...") + efuse.disable_read() + + if write_protect: + log.print("\tDisabling write to key block...") + efuse.disable_write() + log.print("") + + if not write_protect: + log.print("Keys will remain writeable (due to --no-write-protect).") + if no_read_protect: + log.print("Keys will remain readable (due to --no-read-protect).") + + if not self.efuses.burn_all(check_batch_mode=True): + return + log.print("Successful.") + + def burn_key_digest( + self, + blocks: list[str], + keyfiles: list[BinaryIO], + keypurposes: list[str], + no_write_protect: bool = False, + no_read_protect: bool = False, + show_sensitive_info: bool = False, + ): + """Parse a RSA public key and burn the digest to key eFuse block. + + Args: + blocks: List of eFuse block names to burn keys to. + keyfiles: List of open files to read key data from. + keypurposes: List of key purposes to burn. + no_write_protect: If True, the write protection will NOT be enabled. + no_read_protect: If True, the read protection will NOT be enabled. + show_sensitive_info: If True, the sensitive information will be shown. + """ + digest_list = [] + datafile_list = keyfiles[ + 0 : len([name for name in keyfiles if name is not None]) : + ] + block_list = blocks[0 : len([block for block in blocks if block is not None]) :] + + for block_name, datafile in zip(block_list, datafile_list): + efuse = None + for block in self.efuses.blocks: + if block_name == block.name or block_name in block.alias: + efuse = self.efuses[block.name] + if efuse is None: + raise esptool.FatalError(f"Unknown block name - {block_name}") + num_bytes = efuse.bit_len // 8 + digest = espsecure._digest_sbv2_public_key(datafile) + if len(digest) != num_bytes: raise esptool.FatalError( - "It is not possible to change '%s' to '%s' " - "because write protection bit is set." - % (block.key_purpose_name, keypurpose) + f"Incorrect digest size {len(digest)}. Digest must be {num_bytes} " + f"bytes ({num_bytes * 8} bits) of raw binary key data." ) - 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") + digest_list.append(digest) + + self.burn_key( + block_list, + datafile_list, + keypurposes, + no_write_protect, + no_read_protect, + show_sensitive_info, + digest=digest_list, + ) diff --git a/tools/esptool_py/espefuse/efuse/esp32c61/__init__.py b/tools/esptool_py/espefuse/efuse/esp32c61/__init__.py index a3b55a8023..568e228569 100644 --- a/tools/esptool_py/espefuse/efuse/esp32c61/__init__.py +++ b/tools/esptool_py/espefuse/efuse/esp32c61/__init__.py @@ -1,3 +1,5 @@ from . import operations from .emulate_efuse_controller import EmulateEfuseController from .fields import EspEfuses + +commands = operations.ESP32C61Commands diff --git a/tools/esptool_py/espefuse/efuse/esp32c61/fields.py b/tools/esptool_py/espefuse/efuse/esp32c61/fields.py index 5f6fe7b8cb..4c129b92b0 100644 --- a/tools/esptool_py/espefuse/efuse/esp32c61/fields.py +++ b/tools/esptool_py/espefuse/efuse/esp32c61/fields.py @@ -10,6 +10,7 @@ import time from bitstring import BitArray +from esptool.logger import log import esptool @@ -55,9 +56,6 @@ 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, @@ -66,14 +64,12 @@ def __init__( do_not_confirm=False, extend_efuse_table=None, ): + super().__init__(esp, skip_connect, debug, do_not_confirm, extend_efuse_table) 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-C61": raise esptool.FatalError( "Expected the 'esp' param for ESP32-C61 chip but got for '%s'." @@ -102,11 +98,11 @@ def __init__( 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 - # ] + if self.get_block_version() >= 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 ] @@ -136,14 +132,14 @@ def read_coding_scheme(self): self.coding_scheme = self.REGS.CODING_SCHEME_RS def print_status_regs(self): - print("") + log.print("") self.blocks[0].print_block(self.blocks[0].err_bitarray, "err__regs", debug=True) - print( + log.print( "{:27} 0x{:08x}".format( "EFUSE_RD_RS_ERR0_REG", self.read_reg(self.REGS.EFUSE_RD_RS_ERR0_REG) ) ) - print( + log.print( "{:27} 0x{:08x}".format( "EFUSE_RD_RS_ERR1_REG", self.read_reg(self.REGS.EFUSE_RD_RS_ERR1_REG) ) @@ -176,7 +172,7 @@ def wait_efuse_idle(self): # For PGM_CMD it is not necessary. return raise esptool.FatalError( - "Timed out waiting for Efuse controller command to complete" + "Timed out waiting for eFuse controller command to complete" ) def efuse_program(self, block): @@ -204,30 +200,30 @@ def efuse_read(self): try: self._esp = self.reconnect_chip(self._esp) except esptool.FatalError: - print("Can not re-connect to the chip") + log.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( + log.print( "This is the correct behavior as we are actually burning " - "DIS_DOWNLOAD_MODE which disables the connection to the chip" + "DIS_DOWNLOAD_MODE which disables the connection to the chip." ) - print("DIS_DOWNLOAD_MODE is enabled") - print("Successful") + log.print("DIS_DOWNLOAD_MODE is enabled.") + log.print("Successful.") sys.exit(0) # finish without errors raise - print("Established a connection with the chip") + log.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") + log.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" + log.print( + "espefuse can not continue to work in Secure download mode." ) - print("ENABLE_SECURITY_DOWNLOAD is enabled") - print("Successful") + log.print("ENABLE_SECURITY_DOWNLOAD is enabled.") + log.print("Successful.") sys.exit(0) # finish without errors raise @@ -237,7 +233,7 @@ def set_efuse_timing(self): apb_freq = self.get_crystal_freq() if apb_freq != 40: raise esptool.FatalError( - "The eFuse supports only xtal=40M (xtal was %d)" % apb_freq + f"The eFuse supports only xtal=40M (xtal was {apb_freq})." ) self.update_reg(self.REGS.EFUSE_DAC_CONF_REG, self.REGS.EFUSE_DAC_NUM_M, 0xFF) @@ -264,7 +260,7 @@ def get_coding_scheme_warnings(self, silent=False): ] block.err_bitarray.pos = 0 for word in reversed(words): - block.err_bitarray.overwrite(BitArray("uint:32=%d" % word)) + block.err_bitarray.overwrite(BitArray(f"uint:32={word}")) block.num_errors = block.err_bitarray.count(True) block.fail = block.num_errors != 0 else: @@ -280,9 +276,9 @@ def get_coding_scheme_warnings(self, silent=False): 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) + log.print( + f"Error(s) in BLOCK{block.id} " + f"[ERRORS:{block.num_errors} FAIL:{block.fail}]." ) if (self.debug or ret_fail) and not silent: self.print_status_regs() @@ -385,10 +381,8 @@ def get(self, from_read=True): def save(self, new_value): def print_field(e, new_value): - print( - " - '{}' ({}) {} -> {}".format( - e.name, e.description, e.get_bitstring(), new_value - ) + log.print( + f" - '{e.name}' ({e.description}) {e.get_bitstring()} -> {new_value}" ) if self.name == "CUSTOM_MAC": @@ -432,9 +426,9 @@ def check_format(self, new_value_str): 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) + raise esptool.FatalError(f"'{raw_val}' can not be set (value out of range)") else: - raise esptool.FatalError("'%s' unknown name" % raw_val) + raise esptool.FatalError(f"'{raw_val}' unknown name") return raw_val def need_reverse(self, new_key_purpose): diff --git a/tools/esptool_py/espefuse/efuse/esp32c61/mem_definition.py b/tools/esptool_py/espefuse/efuse/esp32c61/mem_definition.py index 2f8f818a53..39050f66d7 100644 --- a/tools/esptool_py/espefuse/efuse/esp32c61/mem_definition.py +++ b/tools/esptool_py/espefuse/efuse/esp32c61/mem_definition.py @@ -5,7 +5,6 @@ # SPDX-License-Identifier: GPL-2.0-or-later import os -from typing import List import yaml @@ -114,13 +113,9 @@ 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: List = [] + self.CALC: list = [] dir_name = os.path.dirname(os.path.abspath(__file__)) dir_name, file_name = os.path.split(dir_name) diff --git a/tools/esptool_py/espefuse/efuse/esp32c61/operations.py b/tools/esptool_py/espefuse/efuse/esp32c61/operations.py index 2306565554..066b9626ab 100644 --- a/tools/esptool_py/espefuse/efuse/esp32c61/operations.py +++ b/tools/esptool_py/espefuse/efuse/esp32c61/operations.py @@ -1,462 +1,316 @@ # This file includes the operations with eFuses for ESP32-C61 chip # -# SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD +# SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD # # SPDX-License-Identifier: GPL-2.0-or-later -import argparse import io -import os # noqa: F401. It is used in IDF scripts -import traceback +from typing import BinaryIO +from esptool.logger import log +import rich_click as click import espsecure - import esptool from . import fields +from .mem_definition import EfuseDefineBlocks from .. import util from ..base_operations import ( - add_common_commands, + BaseCommands, + NonCompositeTuple, + TupleParameter, add_force_write_always, add_show_sensitive_info_option, - burn_bit, - burn_block_data, - burn_efuse, - check_error, - dump, - read_protect_efuse, - summary, - write_protect_efuse, + protect_options, ) -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", - ) +class ESP32C61Commands(BaseCommands): + CHIP_NAME = "ESP32-C61" + efuse_lib = fields.EspEfuses + ################################### CLI definitions ################################### -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) - add_show_sensitive_info_option(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, - ) + def add_cli_commands(self, cli: click.Group): + super().add_cli_commands(cli) + blocks_for_keys = EfuseDefineBlocks().get_blocks_for_keys() - 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) - add_show_sensitive_info_option(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, + @cli.command( + "burn-key", + help="Burn the key block with the specified name. Arguments are groups of block name, " + "key file (containing 256 bits of binary key data) and key purpose.\n\n" + f"Block is one of: [{', '.join(blocks_for_keys)}]\n\n" + f"Key purpose is one of: [{', '.join(fields.EfuseKeyPurposeField.KEY_PURPOSES_NAME)}]", ) - burn_key_digest.add_argument( - "keyfile", - help="Key file to digest (PEM format)", - nargs="?", - action="append", - metavar="KEYFILE", - type=argparse.FileType("rb"), + @click.argument( + "block_keyfile_keypurpose", + metavar=" ", + cls=TupleParameter, + required=True, + nargs=-1, + max_arity=len(blocks_for_keys), + type=NonCompositeTuple( + [ + click.Choice(blocks_for_keys), + click.File("rb"), + click.Choice(fields.EfuseKeyPurposeField.KEY_PURPOSES_NAME), + ] + ), ) - burn_key_digest.add_argument( - "keypurpose", - help="Purpose to set.", - nargs="?", - action="append", - metavar="KEYPURPOSE", - choices=fields.EfuseKeyPurposeField.DIGEST_KEY_PURPOSES, + @protect_options + @add_force_write_always + @add_show_sensitive_info_option + @click.pass_context + def burn_key_cli(ctx, **kwargs): + kwargs.pop("force_write_always") + block, keyfile, keypurpose = zip(*kwargs.pop("block_keyfile_keypurpose")) + kwargs["show_sensitive_info"] = ctx.show_sensitive_info + self.burn_key(block, keyfile, keypurpose, **kwargs) + + @cli.command( + "burn-key-digest", + short_help="Parse a RSA public key and burn the digest.", + help="Parse a RSA public key and burn the digest to key eFuse block.\n\n" + f"Block is one of: [{', '.join(blocks_for_keys)}]\n\n" + f"Key purpose is one of: [{', '.join(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): - print("not supported yet") - - -def key_block_is_unused(block, key_purpose_block): - if not block.is_readable() or not block.is_writeable(): - return False - - if key_purpose_block.get() != "USER" or not key_purpose_block.is_writeable(): - return False - - if not block.get_bitstring().all(False): - return False - - return True - - -def get_next_key_block(efuses, current_key_block, block_name_list): - key_blocks = [b for b in efuses.blocks if b.key_purpose_name] - start = key_blocks.index(current_key_block) - - # Sort key blocks so that we pick the next free block (and loop around if necessary) - key_blocks = key_blocks[start:] + key_blocks[0:start] - - # Exclude any other blocks that will be be burned - key_blocks = [b for b in key_blocks if b.name not in block_name_list] - - for block in key_blocks: - key_purpose_block = efuses[block.key_purpose_name] - if key_block_is_unused(block, key_purpose_block): - return block - - return None - - -def split_512_bit_key(efuses, block_name_list, datafile_list, keypurpose_list): - i = keypurpose_list.index("XTS_AES_256_KEY") - block_name = block_name_list[i] - - block_num = efuses.get_index_block_by_name(block_name) - block = efuses.blocks[block_num] - - data = datafile_list[i].read() - if len(data) != 64: - raise esptool.FatalError( - "Incorrect key file size %d, XTS_AES_256_KEY should be 64 bytes" % len(data) + @click.argument( + "block_keyfile_keypurpose", + metavar=" ", + cls=TupleParameter, + required=True, + nargs=-1, + max_arity=len(blocks_for_keys), + type=NonCompositeTuple( + [ + click.Choice(blocks_for_keys), + click.File("rb"), + click.Choice(fields.EfuseKeyPurposeField.DIGEST_KEY_PURPOSES), + ] + ), ) - - key_block_2 = get_next_key_block(efuses, block, block_name_list) - if not key_block_2: - raise esptool.FatalError("XTS_AES_256_KEY requires two free keyblocks") - - keypurpose_list.append("XTS_AES_256_KEY_1") - datafile_list.append(io.BytesIO(data[:32])) - block_name_list.append(block_name) - - keypurpose_list.append("XTS_AES_256_KEY_2") - datafile_list.append(io.BytesIO(data[32:])) - block_name_list.append(key_block_2.name) - - keypurpose_list.pop(i) - datafile_list.pop(i) - block_name_list.pop(i) - - -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]) : - ] - - if "XTS_AES_256_KEY" in keypurpose_list: - # XTS_AES_256_KEY is not an actual HW key purpose, needs to be split into - # XTS_AES_256_KEY_1 and XTS_AES_256_KEY_2 - split_512_bit_key(efuses, block_name_list, datafile_list, keypurpose_list) - - 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 + @protect_options + @add_force_write_always + @add_show_sensitive_info_option + @click.pass_context + def burn_key_digest_cli(ctx, **kwargs): + kwargs.pop("force_write_always") + block, keyfile, keypurpose = zip(*kwargs.pop("block_keyfile_keypurpose")) + kwargs["show_sensitive_info"] = ctx.show_sensitive_info + self.burn_key_digest(block, keyfile, keypurpose, **kwargs) + + ###################################### Commands ###################################### + + def adc_info(self): + log.print("Block version:", self.efuses.get_block_version()) + if self.efuses.get_block_version() >= 1: + for efuse in self.efuses: + if efuse.category == "calibration": + log.print(f"{efuse.name:<30} = ", self.efuses[efuse.name].get()) + + def burn_key( + self, + blocks: list[str], + keyfiles: list[BinaryIO], + keypurposes: list[str], + no_write_protect: bool = False, + no_read_protect: bool = False, + show_sensitive_info: bool = False, + digest: list[bytes] | None = None, ): - 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] - + """Burn the key block with the specified name. Arguments are groups of block name, + key file (containing 256 bits of binary key data) and key purpose. + + Args: + blocks: List of eFuse block names to burn keys to. + keyfiles: List of open files to read key data from. + keypurposes: List of key purposes to burn. + no_write_protect: If True, the write protection will NOT be enabled. + no_read_protect: If True, the read protection will NOT be enabled. + show_sensitive_info: If True, the sensitive information will be shown. + digest: List of digests to burn. + """ + datafile_list: list[BinaryIO] | list[bytes] 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() + datafile_list = keyfiles[ + 0 : len([name for name in keyfiles if name is not None]) : + ] else: - data = datafile + datafile_list = digest[ + 0 : len([name for name in digest if name is not None]) : + ] + block_name_list = blocks[ + 0 : len([name for name in blocks if name is not None]) : + ] + keypurpose_list = keypurposes[ + 0 : len([name for name in keypurposes if name is not None]) : + ] - 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( - "-> [{}]".format( - util.hexify(data, " ") - if args.show_sensitive_info - else " ".join(["??"] * len(data)) + if "XTS_AES_256_KEY" in keypurpose_list: + # XTS_AES_256_KEY is not an actual HW key purpose, needs to be split into + # XTS_AES_256_KEY_1 and XTS_AES_256_KEY_2 + block_name_list, datafile_list, keypurpose_list = self._split_512_bit_key( + block_name_list, + datafile_list, # type: ignore + keypurpose_list, ) - ) - if revers_msg: - print(revers_msg) - if len(data) != num_bytes: + + 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( - "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) + "The number of blocks (%d), datafile (%d) and keypurpose (%d) should be the same." + % (len(block_name_list), len(datafile_list), len(keypurpose_list)) ) - 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 + log.print("Burn keys to blocks:") + for block_name, datafile, keypurpose in zip( + block_name_list, datafile_list, keypurpose_list + ): + efuse = None + for block in self.efuses.blocks: + if block_name == block.name or block_name in block.alias: + efuse = self.efuses[block.name] + if efuse is None: + raise esptool.FatalError(f"Unknown block name - {block_name}.") + num_bytes = efuse.bit_len // 8 + + block_num = self.efuses.get_index_block_by_name(block_name) + block = self.efuses.blocks[block_num] + + if isinstance(datafile, io.IOBase): + if keypurpose == "ECDSA_KEY": + sk = espsecure.load_ecdsa_signing_key(datafile) # type: ignore + data = espsecure.get_ecdsa_signing_key_raw_bytes(sk) + 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() + datafile.close() + else: + data = datafile - # using efuse instead of a block gives the advantage of checking it as the whole field. - efuse.save(data) + log.print(f" - {efuse.name}", end=" ") + revers_msg = None + if self.efuses[block.key_purpose_name].need_reverse(keypurpose): + revers_msg = ( + f"\tReversing byte order for {keypurpose} hardware peripheral..." + ) + data = data[::-1] + log.print( + "-> [{}]".format( + util.hexify(data, " ") + if show_sensitive_info + else " ".join(["??"] * len(data)) + ) + ) + if revers_msg: + log.print(revers_msg) + if len(data) != num_bytes: + raise esptool.FatalError( + f"Incorrect key file size {len(data)}. Key file must be {num_bytes} " + f"bytes ({num_bytes * 8} bits) of raw binary key 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, + if self.efuses[block.key_purpose_name].need_rd_protect(keypurpose): + read_protect = False if no_read_protect else True + else: + read_protect = False + write_protect = not 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 self.efuses[block.key_purpose_name].get() != keypurpose: + if self.efuses[block.key_purpose_name].is_writeable(): + log.print( + f"\t'{block.key_purpose_name}': " + f"'{self.efuses[block.key_purpose_name].get()}' -> '{keypurpose}'." + ) + self.efuses[block.key_purpose_name].save(keypurpose) + disable_wr_protect_key_purpose = True + else: + raise esptool.FatalError( + f"It is not possible to change '{block.key_purpose_name}' " + f"to '{keypurpose}' because write protection bit is set." ) - ) - efuses[block.key_purpose_name].save(keypurpose) - disable_wr_protect_key_purpose = True else: + log.print(f"\t'{block.key_purpose_name}' is already '{keypurpose}'.") + if self.efuses[block.key_purpose_name].is_writeable(): + disable_wr_protect_key_purpose = True + + if disable_wr_protect_key_purpose: + log.print(f"\tDisabling write to '{block.key_purpose_name}'...") + self.efuses[block.key_purpose_name].disable_write() + + if read_protect: + log.print("\tDisabling read to key block...") + efuse.disable_read() + + if write_protect: + log.print("\tDisabling write to key block...") + efuse.disable_write() + log.print("") + + if not write_protect: + log.print("Keys will remain writeable (due to --no-write-protect).") + if no_read_protect: + log.print("Keys will remain readable (due to --no-read-protect).") + + if not self.efuses.burn_all(check_batch_mode=True): + return + log.print("Successful.") + + def burn_key_digest( + self, + blocks: list[str], + keyfiles: list[BinaryIO], + keypurposes: list[str], + no_write_protect: bool = False, + no_read_protect: bool = False, + show_sensitive_info: bool = False, + ): + """Parse a RSA public key and burn the digest to key eFuse block. + + Args: + blocks: List of eFuse block names to burn keys to. + keyfiles: List of open files to read key data from. + keypurposes: List of key purposes to burn. + no_write_protect: If True, the write protection will NOT be enabled. + no_read_protect: If True, the read protection will NOT be enabled. + show_sensitive_info: If True, the sensitive information will be shown. + """ + digest_list = [] + datafile_list = keyfiles[ + 0 : len([name for name in keyfiles if name is not None]) : + ] + block_list = blocks[0 : len([block for block in blocks if block is not None]) :] + + for block_name, datafile in zip(block_list, datafile_list): + efuse = None + for block in self.efuses.blocks: + if block_name == block.name or block_name in block.alias: + efuse = self.efuses[block.name] + if efuse is None: + raise esptool.FatalError(f"Unknown block name - {block_name}.") + num_bytes = efuse.bit_len // 8 + digest = espsecure._digest_sbv2_public_key(datafile) + if len(digest) != num_bytes: raise esptool.FatalError( - "It is not possible to change '%s' to '%s' " - "because write protection bit is set." - % (block.key_purpose_name, keypurpose) + "Incorrect digest size %d. Digest must be %d bytes (%d bits) " + "of raw binary key data." % (len(digest), num_bytes, num_bytes * 8) ) - 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") + digest_list.append(digest) + + self.burn_key( + block_list, + datafile_list, + keypurposes, + no_write_protect, + no_read_protect, + show_sensitive_info, + digest=digest_list, + ) diff --git a/tools/esptool_py/espefuse/efuse/esp32h2/__init__.py b/tools/esptool_py/espefuse/efuse/esp32h2/__init__.py index a3b55a8023..635662d9e3 100644 --- a/tools/esptool_py/espefuse/efuse/esp32h2/__init__.py +++ b/tools/esptool_py/espefuse/efuse/esp32h2/__init__.py @@ -1,3 +1,5 @@ from . import operations from .emulate_efuse_controller import EmulateEfuseController from .fields import EspEfuses + +commands = operations.ESP32H2Commands diff --git a/tools/esptool_py/espefuse/efuse/esp32h2/fields.py b/tools/esptool_py/espefuse/efuse/esp32h2/fields.py index 91ef6c15ca..f06f41f3cc 100644 --- a/tools/esptool_py/espefuse/efuse/esp32h2/fields.py +++ b/tools/esptool_py/espefuse/efuse/esp32h2/fields.py @@ -10,6 +10,7 @@ import time from bitstring import BitArray +from esptool.logger import log import esptool @@ -55,9 +56,6 @@ 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, @@ -66,14 +64,12 @@ def __init__( do_not_confirm=False, extend_efuse_table=None, ): + super().__init__(esp, skip_connect, debug, do_not_confirm, extend_efuse_table) 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-H2": raise esptool.FatalError( "Expected the 'esp' param for ESP32-H2 chip but got for '%s'." @@ -102,7 +98,7 @@ def __init__( for efuse in self.Fields.BLOCK2_CALIBRATION_EFUSES ] else: - if self["BLK_VERSION_MINOR"].get() == 2: + if self.get_block_version() >= 2: self.efuses += [ EfuseField.convert(self, efuse) for efuse in self.Fields.BLOCK2_CALIBRATION_EFUSES @@ -111,6 +107,10 @@ def __init__( EfuseField.convert(self, efuse) for efuse in self.Fields.CALC ] + if self.get_chip_version() <= 101: + rev = EfuseDefineFields(None, revision="esp32h2_v0.0_v1.1") + self.efuses += [EfuseField.convert(self, efuse) for efuse in rev.EFUSES] + def __getitem__(self, efuse_name): """Return the efuse field with the given name""" for e in self.efuses: @@ -136,14 +136,14 @@ def read_coding_scheme(self): self.coding_scheme = self.REGS.CODING_SCHEME_RS def print_status_regs(self): - print("") + log.print("") self.blocks[0].print_block(self.blocks[0].err_bitarray, "err__regs", debug=True) - print( + log.print( "{:27} 0x{:08x}".format( "EFUSE_RD_RS_ERR0_REG", self.read_reg(self.REGS.EFUSE_RD_RS_ERR0_REG) ) ) - print( + log.print( "{:27} 0x{:08x}".format( "EFUSE_RD_RS_ERR1_REG", self.read_reg(self.REGS.EFUSE_RD_RS_ERR1_REG) ) @@ -176,7 +176,7 @@ def wait_efuse_idle(self): # For PGM_CMD it is not necessary. return raise esptool.FatalError( - "Timed out waiting for Efuse controller command to complete" + "Timed out waiting for eFuse controller command to complete" ) def efuse_program(self, block): @@ -204,30 +204,30 @@ def efuse_read(self): try: self._esp = self.reconnect_chip(self._esp) except esptool.FatalError: - print("Can not re-connect to the chip") + log.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( + log.print( "This is the correct behavior as we are actually burning " - "DIS_DOWNLOAD_MODE which disables the connection to the chip" + "DIS_DOWNLOAD_MODE which disables the connection to the chip." ) - print("DIS_DOWNLOAD_MODE is enabled") - print("Successful") + log.print("DIS_DOWNLOAD_MODE is enabled.") + log.print("Successful.") sys.exit(0) # finish without errors raise - print("Established a connection with the chip") + log.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") + log.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" + log.print( + "espefuse can not continue to work in Secure download mode." ) - print("ENABLE_SECURITY_DOWNLOAD is enabled") - print("Successful") + log.print("ENABLE_SECURITY_DOWNLOAD is enabled.") + log.print("Successful.") sys.exit(0) # finish without errors raise @@ -281,9 +281,9 @@ def get_coding_scheme_warnings(self, silent=False): 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) + log.print( + f"Error(s) in BLOCK{block.id} " + f"[ERRORS:{block.num_errors} FAIL:{block.fail}]." ) if (self.debug or ret_fail) and not silent: self.print_status_regs() @@ -302,22 +302,9 @@ def convert(parent, efuse): "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): - hi_bits = self.parent["WAFER_VERSION_MINOR_HI"].get(from_read) - assert self.parent["WAFER_VERSION_MINOR_HI"].bit_len == 1 - lo_bits = self.parent["WAFER_VERSION_MINOR_LO"].get(from_read) - assert self.parent["WAFER_VERSION_MINOR_LO"].bit_len == 3 - return (hi_bits << 3) + lo_bits - - 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) @@ -386,10 +373,8 @@ def get(self, from_read=True): def save(self, new_value): def print_field(e, new_value): - print( - " - '{}' ({}) {} -> {}".format( - e.name, e.description, e.get_bitstring(), new_value - ) + log.print( + f" - '{e.name}' ({e.description}) {e.get_bitstring()} -> {new_value}" ) if self.name == "CUSTOM_MAC": diff --git a/tools/esptool_py/espefuse/efuse/esp32h2/mem_definition.py b/tools/esptool_py/espefuse/efuse/esp32h2/mem_definition.py index 87663e95f7..4de0e1aeb6 100644 --- a/tools/esptool_py/espefuse/efuse/esp32h2/mem_definition.py +++ b/tools/esptool_py/espefuse/efuse/esp32h2/mem_definition.py @@ -111,19 +111,17 @@ def get_burn_block_data_names(self): class EfuseDefineFields(EfuseFieldsBase): - def __init__(self, extend_efuse_table) -> None: + def __init__(self, extend_efuse_table, revision=None) -> None: # List of efuse fields from TRM the chapter eFuse Controller. self.EFUSES = [] - self.KEYBLOCKS = [] - - # if BLK_VERSION_MINOR is 2, 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) + if revision is not None: + file_name = revision file_name = file_name + ".yaml" dir_name, _ = os.path.split(dir_name) efuse_file = os.path.join(dir_name, "efuse_defs", file_name) diff --git a/tools/esptool_py/espefuse/efuse/esp32h2/operations.py b/tools/esptool_py/espefuse/efuse/esp32h2/operations.py index 2aac7448b7..719e6559f7 100644 --- a/tools/esptool_py/espefuse/efuse/esp32h2/operations.py +++ b/tools/esptool_py/espefuse/efuse/esp32h2/operations.py @@ -1,424 +1,334 @@ # This file includes the operations with eFuses for ESP32-H2 chip # -# SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD +# SPDX-FileCopyrightText: 2022-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 +from io import IOBase +from typing import BinaryIO +from esptool.logger import log +import rich_click as click import espsecure - import esptool from . import fields +from .mem_definition import EfuseDefineBlocks from .. import util from ..base_operations import ( - add_common_commands, + BaseCommands, + NonCompositeTuple, + TupleParameter, add_force_write_always, add_show_sensitive_info_option, - burn_bit, - burn_block_data, - burn_efuse, - check_error, - dump, - read_protect_efuse, - summary, - write_protect_efuse, + protect_options, ) -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", - ) +class ESP32H2Commands(BaseCommands): + CHIP_NAME = "ESP32-H2" + efuse_lib = fields.EspEfuses + ################################### CLI definitions ################################### -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) - add_show_sensitive_info_option(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, - ) + def add_cli_commands(self, cli: click.Group): + super().add_cli_commands(cli) + blocks_for_keys = EfuseDefineBlocks().get_blocks_for_keys() - 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) - add_show_sensitive_info_option(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, + @cli.command( + "burn-key", + help="Burn the key block with the specified name. Arguments are groups of block name, " + "key file (containing 256 bits of binary key data) and key purpose.\n\n" + f"Block is one of: [{', '.join(blocks_for_keys)}]\n\n" + f"Key purpose is one of: [{', '.join(fields.EfuseKeyPurposeField.KEY_PURPOSES_NAME)}]", ) - burn_key_digest.add_argument( - "keyfile", - help="Key file to digest (PEM format)", - nargs="?", - action="append", - metavar="KEYFILE", - type=argparse.FileType("rb"), + @click.argument( + "block_keyfile_keypurpose", + metavar=" ", + cls=TupleParameter, + required=True, + nargs=-1, + max_arity=len(blocks_for_keys), + type=NonCompositeTuple( + [ + click.Choice(blocks_for_keys), + click.File("rb"), + click.Choice(fields.EfuseKeyPurposeField.KEY_PURPOSES_NAME), + ] + ), ) - burn_key_digest.add_argument( - "keypurpose", - help="Purpose to set.", - nargs="?", - action="append", - metavar="KEYPURPOSE", - choices=fields.EfuseKeyPurposeField.DIGEST_KEY_PURPOSES, + @protect_options + @add_force_write_always + @add_show_sensitive_info_option + @click.pass_context + def burn_key_cli(ctx, **kwargs): + kwargs.pop("force_write_always") + block, keyfile, keypurpose = zip(*kwargs.pop("block_keyfile_keypurpose")) + kwargs["show_sensitive_info"] = ctx.show_sensitive_info + self.burn_key(block, keyfile, keypurpose, **kwargs) + + @cli.command( + "burn-key-digest", + short_help="Parse a RSA public key and burn the digest.", + help="Parse a RSA public key and burn the digest to key eFuse block.\n\n" + f"Block is one of: [{', '.join(blocks_for_keys)}]\n\n" + f"Key purpose is one of: [{', '.join(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): - print("") - # fmt: off - if efuses["BLK_VERSION_MINOR"].get() == 2: - print("Temperature Sensor Calibration = {}C".format(efuses["TEMP_CALIB"].get())) - print("") - print("ADC1:") - print("AVE_INITCODE_ATTEN0 = ", efuses["ADC1_AVE_INITCODE_ATTEN0"].get()) - print("AVE_INITCODE_ATTEN1 = ", efuses["ADC1_AVE_INITCODE_ATTEN1"].get()) - print("AVE_INITCODE_ATTEN2 = ", efuses["ADC1_AVE_INITCODE_ATTEN2"].get()) - print("AVE_INITCODE_ATTEN3 = ", efuses["ADC1_AVE_INITCODE_ATTEN3"].get()) - print("HI_DOUT_ATTEN0 = ", efuses["ADC1_HI_DOUT_ATTEN0"].get()) - print("HI_DOUT_ATTEN1 = ", efuses["ADC1_HI_DOUT_ATTEN1"].get()) - print("HI_DOUT_ATTEN2 = ", efuses["ADC1_HI_DOUT_ATTEN2"].get()) - print("HI_DOUT_ATTEN3 = ", efuses["ADC1_HI_DOUT_ATTEN3"].get()) - print("CH0_ATTEN0_INITCODE_DIFF = ", efuses["ADC1_CH0_ATTEN0_INITCODE_DIFF"].get()) - print("CH1_ATTEN0_INITCODE_DIFF = ", efuses["ADC1_CH1_ATTEN0_INITCODE_DIFF"].get()) - print("CH2_ATTEN0_INITCODE_DIFF = ", efuses["ADC1_CH2_ATTEN0_INITCODE_DIFF"].get()) - print("CH3_ATTEN0_INITCODE_DIFF = ", efuses["ADC1_CH3_ATTEN0_INITCODE_DIFF"].get()) - print("CH4_ATTEN0_INITCODE_DIFF = ", efuses["ADC1_CH4_ATTEN0_INITCODE_DIFF"].get()) - else: - print("BLK_VERSION_MINOR = {}".format(efuses["BLK_VERSION_MINOR"].get())) - # 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)) + @click.argument( + "block_keyfile_keypurpose", + metavar=" ", + cls=TupleParameter, + required=True, + nargs=-1, + max_arity=len(blocks_for_keys), + type=NonCompositeTuple( + [ + click.Choice(blocks_for_keys), + click.File("rb"), + click.Choice(fields.EfuseKeyPurposeField.DIGEST_KEY_PURPOSES), + ] + ), ) - - print("Burn keys to blocks:") - for block_name, datafile, keypurpose in zip( - block_name_list, datafile_list, keypurpose_list + @protect_options + @add_force_write_always + @add_show_sensitive_info_option + @click.pass_context + def burn_key_digest_cli(ctx, **kwargs): + kwargs.pop("force_write_always") + block, keyfile, keypurpose = zip(*kwargs.pop("block_keyfile_keypurpose")) + kwargs["show_sensitive_info"] = ctx.show_sensitive_info + self.burn_key_digest(block, keyfile, keypurpose, **kwargs) + + ###################################### Commands ###################################### + + def adc_info(self): + log.print("Block version:", self.efuses.get_block_version()) + if self.efuses.get_block_version() >= 2: + # fmt: off + log.print(f"Temperature Sensor Calibration = {self.efuses['TEMP_CALIB'].get()}C") + log.print("") + log.print("ADC1:") + log.print("AVE_INITCODE_ATTEN0 = ", self.efuses["ADC1_AVE_INITCODE_ATTEN0"].get()) + log.print("AVE_INITCODE_ATTEN1 = ", self.efuses["ADC1_AVE_INITCODE_ATTEN1"].get()) + log.print("AVE_INITCODE_ATTEN2 = ", self.efuses["ADC1_AVE_INITCODE_ATTEN2"].get()) + log.print("AVE_INITCODE_ATTEN3 = ", self.efuses["ADC1_AVE_INITCODE_ATTEN3"].get()) + log.print("HI_DOUT_ATTEN0 = ", self.efuses["ADC1_HI_DOUT_ATTEN0"].get()) + log.print("HI_DOUT_ATTEN1 = ", self.efuses["ADC1_HI_DOUT_ATTEN1"].get()) + log.print("HI_DOUT_ATTEN2 = ", self.efuses["ADC1_HI_DOUT_ATTEN2"].get()) + log.print("HI_DOUT_ATTEN3 = ", self.efuses["ADC1_HI_DOUT_ATTEN3"].get()) + log.print("CH0_ATTEN0_INITCODE_DIFF = ", self.efuses["ADC1_CH0_ATTEN0_INITCODE_DIFF"].get()) + log.print("CH1_ATTEN0_INITCODE_DIFF = ", self.efuses["ADC1_CH1_ATTEN0_INITCODE_DIFF"].get()) + log.print("CH2_ATTEN0_INITCODE_DIFF = ", self.efuses["ADC1_CH2_ATTEN0_INITCODE_DIFF"].get()) + log.print("CH3_ATTEN0_INITCODE_DIFF = ", self.efuses["ADC1_CH3_ATTEN0_INITCODE_DIFF"].get()) + log.print("CH4_ATTEN0_INITCODE_DIFF = ", self.efuses["ADC1_CH4_ATTEN0_INITCODE_DIFF"].get()) + # fmt: on + + def burn_key( + self, + blocks: list[str], + keyfiles: list[BinaryIO], + keypurposes: list[str], + no_write_protect: bool = False, + no_read_protect: bool = False, + show_sensitive_info: bool = False, + digest: list[bytes] | None = None, ): - 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] - + """Burn the key block with the specified name. Arguments are groups of block name, + key file (containing 256 bits of binary key data) and key purpose. + + Args: + blocks: List of eFuse block names to burn keys to. + keyfiles: List of open files to read key data from. + keypurposes: List of key purposes to burn. + no_write_protect: If True, the write protection will NOT be enabled. + no_read_protect: If True, the read protection will NOT be enabled. + show_sensitive_info: If True, the sensitive information will be shown. + digest: List of digests to burn. + """ + datafile_list: list[BinaryIO] | list[bytes] 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, add 8 bytes of padding - data = b"\x00" * 8 + data - else: - data = datafile.read() + datafile_list = keyfiles[ + 0 : len([name for name in keyfiles if name is not None]) : + ] else: - data = datafile + datafile_list = digest[ + 0 : len([name for name in digest if name is not None]) : + ] + block_name_list = blocks[ + 0 : len([name for name in blocks if name is not None]) : + ] + keypurpose_list = keypurposes[ + 0 : len([name for name in keypurposes if name is not None]) : + ] - 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( - "-> [{}]".format( - util.hexify(data, " ") - if args.show_sensitive_info - else " ".join(["??"] * len(data)) - ) - ) - if revers_msg: - print(revers_msg) - if len(data) != num_bytes: + 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( - "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) + f"The number of blocks ({len(block_name_list)}), " + f"datafile ({len(datafile_list)}) and keypurpose ({len(keypurpose_list)}) " + "should be the same." ) - 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) + log.print("Burn keys to blocks:") + for block_name, datafile, keypurpose in zip( + block_name_list, datafile_list, keypurpose_list + ): + efuse = None + for block in self.efuses.blocks: + if block_name == block.name or block_name in block.alias: + efuse = self.efuses[block.name] + if efuse is None: + raise esptool.FatalError(f"Unknown block name - {block_name}.") + num_bytes = efuse.bit_len // 8 + + block_num = self.efuses.get_index_block_by_name(block_name) + block = self.efuses.blocks[block_num] + + if isinstance(datafile, IOBase): + if keypurpose == "ECDSA_KEY": + sk = espsecure.load_ecdsa_signing_key(datafile) # type: ignore + data = espsecure.get_ecdsa_signing_key_raw_bytes(sk) + if len(data) == 24: + # the private key is 24 bytes long for NIST192p, add 8 bytes of padding + data = b"\x00" * 8 + data + else: + data = datafile.read() + datafile.close() + else: + data = datafile - 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, - ) + log.print(f" - {efuse.name}", end=" ") + revers_msg = None + if self.efuses[block.key_purpose_name].need_reverse(keypurpose): + revers_msg = ( + f"\tReversing byte order for {keypurpose} hardware peripheral..." ) - efuses[block.key_purpose_name].save(keypurpose) - disable_wr_protect_key_purpose = True - else: + data = data[::-1] + log.print( + "-> [{}]".format( + util.hexify(data, " ") + if show_sensitive_info + else " ".join(["??"] * len(data)) + ) + ) + if revers_msg: + log.print(revers_msg) + if len(data) != num_bytes: raise esptool.FatalError( - "It is not possible to change '%s' to '%s' " - "because write protection bit is set." - % (block.key_purpose_name, keypurpose) + "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) ) - 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 keypurpose == "ECDSA_KEY": - if efuses["ECDSA_FORCE_USE_HARDWARE_K"].get() == 0: - # For ECDSA key purpose block permanently enable - # the hardware TRNG supplied k mode (most secure mode) - print("\tECDSA_FORCE_USE_HARDWARE_K: 0 -> 1") - efuses["ECDSA_FORCE_USE_HARDWARE_K"].save(1) + if self.efuses[block.key_purpose_name].need_rd_protect(keypurpose): + read_protect = False if no_read_protect else True else: - print("\tECDSA_FORCE_USE_HARDWARE_K is already '1'") - - 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") + read_protect = False + write_protect = not 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 self.efuses[block.key_purpose_name].get() != keypurpose: + if self.efuses[block.key_purpose_name].is_writeable(): + log.print( + f"\t'{block.key_purpose_name}': " + f"'{self.efuses[block.key_purpose_name].get()}' -> '{keypurpose}'." + ) + self.efuses[block.key_purpose_name].save(keypurpose) + disable_wr_protect_key_purpose = True + else: + raise esptool.FatalError( + f"It is not possible to change '{block.key_purpose_name}' " + f"to '{keypurpose}' because write protection bit is set." + ) + else: + log.print(f"\t'{block.key_purpose_name}' is already '{keypurpose}'.") + if self.efuses[block.key_purpose_name].is_writeable(): + disable_wr_protect_key_purpose = True + + # >= ESP32-H2 ECO5 revision (v1.2) does not have ECDSA_FORCE_USE_HARDWARE_K + if self.efuses.get_chip_version() <= 101: + if keypurpose == "ECDSA_KEY": + if self.efuses["ECDSA_FORCE_USE_HARDWARE_K"].get() == 0: + # For ECDSA key purpose block permanently enable + # the hardware TRNG supplied k mode (most secure mode) + log.print("\tECDSA_FORCE_USE_HARDWARE_K: 0 -> 1") + self.efuses["ECDSA_FORCE_USE_HARDWARE_K"].save(1) + else: + log.print("\tECDSA_FORCE_USE_HARDWARE_K is already '1'") + + if disable_wr_protect_key_purpose: + log.print(f"\tDisabling write to '{block.key_purpose_name}'...") + self.efuses[block.key_purpose_name].disable_write() + + if read_protect: + log.print("\tDisabling read to key block...") + efuse.disable_read() + + if write_protect: + log.print("\tDisabling write to key block...") + efuse.disable_write() + log.print("") + + if not write_protect: + log.print("Keys will remain writeable (due to --no-write-protect).") + if no_read_protect: + log.print("Keys will remain readable (due to --no-read-protect).") + + if not self.efuses.burn_all(check_batch_mode=True): + return + log.print("Successful.") + + def burn_key_digest( + self, + blocks: list[str], + keyfiles: list[BinaryIO], + keypurposes: list[str], + no_write_protect: bool = False, + no_read_protect: bool = False, + show_sensitive_info: bool = False, + ): + """Parse a RSA public key and burn the digest to key eFuse block. + + Args: + blocks: List of eFuse block names to burn keys to. + keyfiles: List of open files to read key data from. + keypurposes: List of key purposes to burn. + no_write_protect: If True, the write protection will NOT be enabled. + no_read_protect: If True, the read protection will NOT be enabled. + show_sensitive_info: If True, the sensitive information will be shown. + """ + digest_list = [] + datafile_list = keyfiles[ + 0 : len([name for name in keyfiles if name is not None]) : + ] + block_list = blocks[0 : len([block for block in blocks if block is not None]) :] + + for block_name, datafile in zip(block_list, datafile_list): + efuse = None + for block in self.efuses.blocks: + if block_name == block.name or block_name in block.alias: + efuse = self.efuses[block.name] + if efuse is None: + raise esptool.FatalError(f"Unknown block name - {block_name}.") + num_bytes = efuse.bit_len // 8 + digest = espsecure._digest_sbv2_public_key(datafile) + if len(digest) != num_bytes: + raise esptool.FatalError( + f"Incorrect digest size {len(digest)}. Digest must be {num_bytes} " + f"bytes ({num_bytes * 8} bits) of raw binary key data." + ) + digest_list.append(digest) + + self.burn_key( + block_list, + datafile_list, + keypurposes, + no_write_protect, + no_read_protect, + show_sensitive_info, + digest=digest_list, + ) diff --git a/tools/esptool_py/espefuse/efuse/esp32c5beta3/__init__.py b/tools/esptool_py/espefuse/efuse/esp32h21/__init__.py similarity index 74% rename from tools/esptool_py/espefuse/efuse/esp32c5beta3/__init__.py rename to tools/esptool_py/espefuse/efuse/esp32h21/__init__.py index a3b55a8023..240b05442d 100644 --- a/tools/esptool_py/espefuse/efuse/esp32c5beta3/__init__.py +++ b/tools/esptool_py/espefuse/efuse/esp32h21/__init__.py @@ -1,3 +1,5 @@ from . import operations from .emulate_efuse_controller import EmulateEfuseController from .fields import EspEfuses + +commands = operations.ESP32H21Commands diff --git a/tools/esptool_py/espefuse/efuse/esp32c5beta3/emulate_efuse_controller.py b/tools/esptool_py/espefuse/efuse/esp32h21/emulate_efuse_controller.py similarity index 93% rename from tools/esptool_py/espefuse/efuse/esp32c5beta3/emulate_efuse_controller.py rename to tools/esptool_py/espefuse/efuse/esp32h21/emulate_efuse_controller.py index 3fdab6f816..6dff54ca43 100644 --- a/tools/esptool_py/espefuse/efuse/esp32c5beta3/emulate_efuse_controller.py +++ b/tools/esptool_py/espefuse/efuse/esp32h21/emulate_efuse_controller.py @@ -1,6 +1,6 @@ -# This file describes eFuses controller for ESP32-C5 beta3 chip +# This file describes eFuses controller for ESP32-H21 chip # -# SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD +# SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD # # SPDX-License-Identifier: GPL-2.0-or-later @@ -13,7 +13,7 @@ class EmulateEfuseController(EmulateEfuseControllerBase): """The class for virtual efuse operation. Using for HOST_TEST.""" - CHIP_NAME = "ESP32-C5(beta3)" + CHIP_NAME = "ESP32-H21" mem = None debug = False @@ -33,7 +33,7 @@ def get_minor_chip_version(self): return 0 def get_crystal_freq(self): - return 40 # MHz (common for all chips) + return 32 # MHz def get_security_info(self): return { diff --git a/tools/esptool_py/espefuse/efuse/esp32h2beta1/fields.py b/tools/esptool_py/espefuse/efuse/esp32h21/fields.py similarity index 85% rename from tools/esptool_py/espefuse/efuse/esp32h2beta1/fields.py rename to tools/esptool_py/espefuse/efuse/esp32h21/fields.py index 90fb72bb58..bfb1e1e7ad 100644 --- a/tools/esptool_py/espefuse/efuse/esp32h2beta1/fields.py +++ b/tools/esptool_py/espefuse/efuse/esp32h21/fields.py @@ -1,6 +1,6 @@ -# This file describes eFuses for ESP32-H2 chip +# This file describes eFuses for ESP32-H21 chip # -# SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD +# SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD # # SPDX-License-Identifier: GPL-2.0-or-later @@ -10,6 +10,7 @@ import time from bitstring import BitArray +from esptool.logger import log import esptool @@ -55,9 +56,6 @@ 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, @@ -66,17 +64,15 @@ def __init__( do_not_confirm=False, extend_efuse_table=None, ): + super().__init__(esp, skip_connect, debug, do_not_confirm, extend_efuse_table) 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-H2(beta1)": + if esp.CHIP_NAME != "ESP32-H21": raise esptool.FatalError( - "Expected the 'esp' param for ESP32-H2(beta1) chip but got for '%s'." + "Expected the 'esp' param for ESP32-H21 chip but got for '%s'." % (esp.CHIP_NAME) ) if not skip_connect: @@ -102,7 +98,7 @@ def __init__( for efuse in self.Fields.BLOCK2_CALIBRATION_EFUSES ] else: - if self["BLK_VERSION_MINOR"].get() == 2: + if self.get_block_version() >= 2: self.efuses += [ EfuseField.convert(self, efuse) for efuse in self.Fields.BLOCK2_CALIBRATION_EFUSES @@ -136,14 +132,14 @@ def read_coding_scheme(self): self.coding_scheme = self.REGS.CODING_SCHEME_RS def print_status_regs(self): - print("") + log.print("") self.blocks[0].print_block(self.blocks[0].err_bitarray, "err__regs", debug=True) - print( + log.print( "{:27} 0x{:08x}".format( "EFUSE_RD_RS_ERR0_REG", self.read_reg(self.REGS.EFUSE_RD_RS_ERR0_REG) ) ) - print( + log.print( "{:27} 0x{:08x}".format( "EFUSE_RD_RS_ERR1_REG", self.read_reg(self.REGS.EFUSE_RD_RS_ERR1_REG) ) @@ -176,7 +172,7 @@ def wait_efuse_idle(self): # For PGM_CMD it is not necessary. return raise esptool.FatalError( - "Timed out waiting for Efuse controller command to complete" + "Timed out waiting for eFuse controller command to complete" ) def efuse_program(self, block): @@ -204,30 +200,30 @@ def efuse_read(self): try: self._esp = self.reconnect_chip(self._esp) except esptool.FatalError: - print("Can not re-connect to the chip") + log.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( + log.print( "This is the correct behavior as we are actually burning " - "DIS_DOWNLOAD_MODE which disables the connection to the chip" + "DIS_DOWNLOAD_MODE which disables the connection to the chip." ) - print("DIS_DOWNLOAD_MODE is enabled") - print("Successful") + log.print("DIS_DOWNLOAD_MODE is enabled.") + log.print("Successful.") sys.exit(0) # finish without errors raise - print("Established a connection with the chip") + log.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") + log.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" + log.print( + "espefuse can not continue to work in Secure download mode." ) - print("ENABLE_SECURITY_DOWNLOAD is enabled") - print("Successful") + log.print("ENABLE_SECURITY_DOWNLOAD is enabled.") + log.print("Successful.") sys.exit(0) # finish without errors raise @@ -235,21 +231,12 @@ def set_efuse_timing(self): """Set timing registers for burning efuses""" # Configure clock apb_freq = self.get_crystal_freq() + # Based on `CONFIG_SOC_XTAL_SUPPORT_32M=y` for this target from ESP-IDF configuration if apb_freq != 32: raise esptool.FatalError( "The eFuse supports only xtal=32M (xtal was %d)" % apb_freq ) - - self.update_reg(self.REGS.EFUSE_DAC_CONF_REG, self.REGS.EFUSE_DAC_NUM_M, 0xFF) - self.update_reg( - self.REGS.EFUSE_DAC_CONF_REG, self.REGS.EFUSE_DAC_CLK_DIV_M, 0x28 - ) - self.update_reg( - self.REGS.EFUSE_WR_TIM_CONF1_REG, self.REGS.EFUSE_PWR_ON_NUM_M, 0x3000 - ) - self.update_reg( - self.REGS.EFUSE_WR_TIM_CONF2_REG, self.REGS.EFUSE_PWR_OFF_NUM_M, 0x190 - ) + # TODO: [ESP32H21] IDF-11506 def get_coding_scheme_warnings(self, silent=False): """Check if the coding scheme has detected any errors.""" @@ -280,9 +267,9 @@ def get_coding_scheme_warnings(self, silent=False): 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) + log.print( + f"Error(s) in BLOCK{block.id} " + f"[ERRORS:{block.num_errors} FAIL:{block.fail}]." ) if (self.debug or ret_fail) and not silent: self.print_status_regs() @@ -307,11 +294,8 @@ def convert(parent, efuse): class EfuseWafer(EfuseField): def get(self, from_read=True): - hi_bits = self.parent["WAFER_VERSION_MINOR_HI"].get(from_read) - assert self.parent["WAFER_VERSION_MINOR_HI"].bit_len == 1 - lo_bits = self.parent["WAFER_VERSION_MINOR_LO"].get(from_read) - assert self.parent["WAFER_VERSION_MINOR_LO"].bit_len == 3 - return (hi_bits << 3) + lo_bits + # TODO: [ESP32H21] IDF-11506 + return 0 def save(self, new_value): raise esptool.FatalError("Burning %s is not supported" % self.name) @@ -344,7 +328,7 @@ def check_format(self, new_value_str): f"MAC Address needs to be a {num_bytes}-byte hexadecimal format " "separated by colons (:)!" ) - hexad = new_value_str.replace(":", "").split(" ", 1)[0] + hexad = new_value_str.replace(":", "") hexad = hexad.split(" ", 1)[0] if self.is_field_calculated() else hexad if len(hexad) != num_bytes * 2: raise esptool.FatalError( @@ -385,10 +369,8 @@ def get(self, from_read=True): def save(self, new_value): def print_field(e, new_value): - print( - " - '{}' ({}) {} -> {}".format( - e.name, e.description, e.get_bitstring(), new_value - ) + log.print( + f" - '{e.name}' ({e.description}) {e.get_bitstring()} -> {new_value}" ) if self.name == "CUSTOM_MAC": @@ -399,14 +381,14 @@ def print_field(e, new_value): # 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") - raise esptool.FatalError("Writing Factory MAC address is not supported") # fmt: off class EfuseKeyPurposeField(EfuseField): KEY_PURPOSES = [ ("USER", 0, None, None, "no_need_rd_protect"), # User purposes (software-only use) - ("RESERVED", 1, None, None, "no_need_rd_protect"), # Reserved + ("ECDSA_KEY", 1, None, "Reverse", "need_rd_protect"), # ECDSA key + ("RESERVED", 2, None, None, "no_need_rd_protect"), # Reserved ("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) @@ -417,7 +399,6 @@ class EfuseKeyPurposeField(EfuseField): ("SECURE_BOOT_DIGEST2", 11, "DIGEST", None, "no_need_rd_protect"), # SECURE_BOOT_DIGEST2 (Secure Boot key digest) ] # 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"] @@ -459,7 +440,4 @@ def get_name(self, raw_val): def save(self, new_value): raw_val = int(self.check_format(str(new_value))) - str_new_value = self.get_name(raw_val) - if self.name == "KEY_PURPOSE_5" and str_new_value.startswith("XTS_AES"): - raise esptool.FatalError(f"{self.name} can not have {str_new_value} key due to a hardware bug (please see TRM for more details)") return super(EfuseKeyPurposeField, self).save(raw_val) diff --git a/tools/esptool_py/espefuse/efuse/esp32c5beta3/mem_definition.py b/tools/esptool_py/espefuse/efuse/esp32h21/mem_definition.py similarity index 96% rename from tools/esptool_py/espefuse/efuse/esp32c5beta3/mem_definition.py rename to tools/esptool_py/espefuse/efuse/esp32h21/mem_definition.py index 67ffff60ed..92865e0489 100644 --- a/tools/esptool_py/espefuse/efuse/esp32c5beta3/mem_definition.py +++ b/tools/esptool_py/espefuse/efuse/esp32h21/mem_definition.py @@ -1,6 +1,6 @@ -# This file describes eFuses fields and registers for ESP32-C5 beta3 chip +# This file describes eFuses fields and registers for ESP32-H21 chip # -# SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD +# SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD # # SPDX-License-Identifier: GPL-2.0-or-later @@ -20,7 +20,7 @@ class EfuseDefineRegisters(EfuseRegistersBase): EFUSE_MEM_SIZE = 0x01FC + 4 # EFUSE registers & command/conf values - DR_REG_EFUSE_BASE = 0x600B0800 + DR_REG_EFUSE_BASE = 0x600B4000 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 @@ -114,12 +114,8 @@ 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__)) diff --git a/tools/esptool_py/espefuse/efuse/esp32h21/operations.py b/tools/esptool_py/espefuse/efuse/esp32h21/operations.py new file mode 100644 index 0000000000..89de347953 --- /dev/null +++ b/tools/esptool_py/espefuse/efuse/esp32h21/operations.py @@ -0,0 +1,313 @@ +# This file includes the operations with eFuses for ESP32-H21 chip +# +# SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD +# +# SPDX-License-Identifier: GPL-2.0-or-later + +from io import IOBase +from typing import BinaryIO +from esptool.logger import log +import rich_click as click + +import espsecure +import esptool + +from . import fields +from .mem_definition import EfuseDefineBlocks +from .. import util +from ..base_operations import ( + BaseCommands, + NonCompositeTuple, + TupleParameter, + add_force_write_always, + add_show_sensitive_info_option, + protect_options, +) + + +class ESP32H21Commands(BaseCommands): + CHIP_NAME = "ESP32-H21" + efuse_lib = fields.EspEfuses + + ################################### CLI definitions ################################### + + def add_cli_commands(self, cli: click.Group): + super().add_cli_commands(cli) + blocks_for_keys = EfuseDefineBlocks().get_blocks_for_keys() + + @cli.command( + "burn-key", + help="Burn the key block with the specified name. Arguments are groups of block name, " + "key file (containing 256 bits of binary key data) and key purpose.\n\n" + f"Block is one of: [{', '.join(blocks_for_keys)}]\n\n" + f"Key purpose is one of: [{', '.join(fields.EfuseKeyPurposeField.KEY_PURPOSES_NAME)}]", + ) + @click.argument( + "block_keyfile_keypurpose", + metavar=" ", + cls=TupleParameter, + required=True, + nargs=-1, + max_arity=len(blocks_for_keys), + type=NonCompositeTuple( + [ + click.Choice(blocks_for_keys), + click.File("rb"), + click.Choice(fields.EfuseKeyPurposeField.KEY_PURPOSES_NAME), + ] + ), + ) + @protect_options + @add_force_write_always + @add_show_sensitive_info_option + @click.pass_context + def burn_key_cli(ctx, **kwargs): + kwargs.pop("force_write_always") + block, keyfile, keypurpose = zip(*kwargs.pop("block_keyfile_keypurpose")) + kwargs["show_sensitive_info"] = ctx.show_sensitive_info + self.burn_key(block, keyfile, keypurpose, **kwargs) + + @cli.command( + "burn-key-digest", + short_help="Parse a RSA public key and burn the digest.", + help="Parse a RSA public key and burn the digest to key eFuse block.\n\n" + f"Block is one of: [{', '.join(blocks_for_keys)}]\n\n" + f"Key purpose is one of: [{', '.join(fields.EfuseKeyPurposeField.DIGEST_KEY_PURPOSES)}]", + ) + @click.argument( + "block_keyfile_keypurpose", + metavar=" ", + cls=TupleParameter, + required=True, + nargs=-1, + max_arity=len(blocks_for_keys), + type=NonCompositeTuple( + [ + click.Choice(blocks_for_keys), + click.File("rb"), + click.Choice(fields.EfuseKeyPurposeField.DIGEST_KEY_PURPOSES), + ] + ), + ) + @protect_options + @add_force_write_always + @add_show_sensitive_info_option + @click.pass_context + def burn_key_digest_cli(ctx, **kwargs): + kwargs.pop("force_write_always") + block, keyfile, keypurpose = zip(*kwargs.pop("block_keyfile_keypurpose")) + kwargs["show_sensitive_info"] = ctx.show_sensitive_info + self.burn_key_digest(block, keyfile, keypurpose, **kwargs) + + ###################################### Commands ###################################### + + def adc_info(self): + # TODO: [ESP32H21] IDF-11506 + log.print("Not supported yet.") + + def burn_key( + self, + blocks: list[str], + keyfiles: list[BinaryIO], + keypurposes: list[str], + no_write_protect: bool = False, + no_read_protect: bool = False, + show_sensitive_info: bool = False, + digest: list[bytes] | None = None, + ): + """Burn the key block with the specified name. Arguments are groups of block name, + key file (containing 256 bits of binary key data) and key purpose. + + Args: + blocks: List of eFuse block names to burn keys to. + keyfiles: List of open files to read key data from. + keypurposes: List of key purposes to burn. + no_write_protect: If True, the write protection will NOT be enabled. + no_read_protect: If True, the read protection will NOT be enabled. + show_sensitive_info: If True, the sensitive information will be shown. + digest: List of digests to burn. + """ + datafile_list: list[BinaryIO] | list[bytes] + if digest is None: + datafile_list = keyfiles[ + 0 : len([name for name in keyfiles if name is not None]) : + ] + else: + datafile_list = digest[ + 0 : len([name for name in digest if name is not None]) : + ] + block_name_list = blocks[ + 0 : len([name for name in blocks if name is not None]) : + ] + keypurpose_list = keypurposes[ + 0 : len([name for name in keypurposes 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( + f"The number of blocks ({len(block_name_list)}), datafile ({len(datafile_list)}) " + f"and keypurpose ({len(keypurpose_list)}) should be the same." + ) + + log.print("Burn keys to blocks:") + for block_name, datafile, keypurpose in zip( + block_name_list, datafile_list, keypurpose_list + ): + efuse = None + for block in self.efuses.blocks: + if block_name == block.name or block_name in block.alias: + efuse = self.efuses[block.name] + if efuse is None: + raise esptool.FatalError(f"Unknown block name - {block_name}.") + num_bytes = efuse.bit_len // 8 + + block_num = self.efuses.get_index_block_by_name(block_name) + block = self.efuses.blocks[block_num] + + if isinstance(datafile, IOBase): + if keypurpose == "ECDSA_KEY": + sk = espsecure.load_ecdsa_signing_key(datafile) # type: ignore + data = espsecure.get_ecdsa_signing_key_raw_bytes(sk) + if len(data) == 24: + # the private key is 24 bytes long for NIST192p, add 8 bytes of padding + data = b"\x00" * 8 + data + else: + data = datafile.read() + datafile.close() + else: + data = datafile + + log.print(f" - {efuse.name}", end=" ") + revers_msg = None + if self.efuses[block.key_purpose_name].need_reverse(keypurpose): + revers_msg = ( + f"\tReversing byte order for {keypurpose} hardware peripheral..." + ) + data = data[::-1] + log.print( + "-> [{}]".format( + util.hexify(data, " ") + if show_sensitive_info + else " ".join(["??"] * len(data)) + ) + ) + if revers_msg: + log.print(revers_msg) + if len(data) != num_bytes: + raise esptool.FatalError( + f"Incorrect key file size {len(data)}. Key file must be {num_bytes}" + f" bytes ({num_bytes * 8} bits) of raw binary key data." + ) + + if self.efuses[block.key_purpose_name].need_rd_protect(keypurpose): + read_protect = False if no_read_protect else True + else: + read_protect = False + write_protect = not 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 self.efuses[block.key_purpose_name].get() != keypurpose: + if self.efuses[block.key_purpose_name].is_writeable(): + log.print( + f"\t'{block.key_purpose_name}': " + f"'{self.efuses[block.key_purpose_name].get()}' -> '{keypurpose}'." + ) + self.efuses[block.key_purpose_name].save(keypurpose) + disable_wr_protect_key_purpose = True + else: + raise esptool.FatalError( + f"It is not possible to change '{block.key_purpose_name}' " + f"to '{keypurpose}' because write protection bit is set." + ) + else: + log.print(f"\t'{block.key_purpose_name}' is already '{keypurpose}'.") + if self.efuses[block.key_purpose_name].is_writeable(): + disable_wr_protect_key_purpose = True + + if keypurpose == "ECDSA_KEY": + if self.efuses["ECDSA_FORCE_USE_HARDWARE_K"].get() == 0: + # For ECDSA key purpose block permanently enable + # the hardware TRNG supplied k mode (most secure mode) + log.print("\tECDSA_FORCE_USE_HARDWARE_K: 0 -> 1") + self.efuses["ECDSA_FORCE_USE_HARDWARE_K"].save(1) + else: + log.print("\tECDSA_FORCE_USE_HARDWARE_K is already '1'") + + if disable_wr_protect_key_purpose: + log.print(f"\tDisabling write to '{block.key_purpose_name}'...") + self.efuses[block.key_purpose_name].disable_write() + + if read_protect: + log.print("\tDisabling read to key block...") + efuse.disable_read() + + if write_protect: + log.print("\tDisabling write to key block...") + efuse.disable_write() + log.print("") + + if not write_protect: + log.print("Keys will remain writeable (due to --no-write-protect).") + if no_read_protect: + log.print("Keys will remain readable (due to --no-read-protect).") + + if not self.efuses.burn_all(check_batch_mode=True): + return + log.print("Successful.") + + def burn_key_digest( + self, + blocks: list[str], + keyfiles: list[BinaryIO], + keypurposes: list[str], + no_write_protect: bool = False, + no_read_protect: bool = False, + show_sensitive_info: bool = False, + ): + """Parse a RSA public key and burn the digest to key eFuse block. + + Args: + blocks: List of eFuse block names to burn keys to. + keyfiles: List of open files to read key data from. + keypurposes: List of key purposes to burn. + no_write_protect: If True, the write protection will NOT be enabled. + no_read_protect: If True, the read protection will NOT be enabled. + show_sensitive_info: If True, the sensitive information will be shown. + """ + digest_list = [] + datafile_list = keyfiles[ + 0 : len([name for name in keyfiles if name is not None]) : + ] + block_list = blocks[0 : len([block for block in blocks if block is not None]) :] + + for block_name, datafile in zip(block_list, datafile_list): + efuse = None + for block in self.efuses.blocks: + if block_name == block.name or block_name in block.alias: + efuse = self.efuses[block.name] + if efuse is None: + raise esptool.FatalError(f"Unknown block name - {block_name}.") + num_bytes = efuse.bit_len // 8 + digest = espsecure._digest_sbv2_public_key(datafile) + if len(digest) != num_bytes: + raise esptool.FatalError( + f"Incorrect digest size {len(digest)}. Digest must be {num_bytes} " + f"bytes ({num_bytes * 8} bits) of raw binary key data." + ) + digest_list.append(digest) + + self.burn_key( + block_list, + datafile_list, + keypurposes, + no_write_protect, + no_read_protect, + show_sensitive_info, + digest=digest_list, + ) diff --git a/tools/esptool_py/espefuse/efuse/esp32h2beta1/mem_definition.py b/tools/esptool_py/espefuse/efuse/esp32h2beta1/mem_definition.py deleted file mode 100644 index 73bfb370ed..0000000000 --- a/tools/esptool_py/espefuse/efuse/esp32h2beta1/mem_definition.py +++ /dev/null @@ -1,156 +0,0 @@ -# This file describes eFuses fields and registers for ESP32-H2 chip -# -# SPDX-FileCopyrightText: 2021-2022 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 -from typing import List - - -class EfuseDefineRegisters(EfuseRegistersBase): - EFUSE_MEM_SIZE = 0x01FC + 4 - - # EFUSE registers & command/conf values - DR_REG_EFUSE_BASE = 0x6001A000 - 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 + 0x1D0 - EFUSE_CMD_REG = DR_REG_EFUSE_BASE + 0x1D4 - EFUSE_RD_RS_ERR0_REG = DR_REG_EFUSE_BASE + 0x1C0 - EFUSE_RD_RS_ERR1_REG = DR_REG_EFUSE_BASE + 0x1C4 - 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 + 0x1E8 - EFUSE_RD_TIM_CONF_REG = DR_REG_EFUSE_BASE + 0x1EC - EFUSE_WR_TIM_CONF1_REG = DR_REG_EFUSE_BASE + 0x1F0 - EFUSE_WR_TIM_CONF2_REG = DR_REG_EFUSE_BASE + 0x1F4 - EFUSE_DATE_REG = DR_REG_EFUSE_BASE + 0x1FC - 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_MAJOR is 1, these efuse fields are in BLOCK2 - self.BLOCK2_CALIBRATION_EFUSES = [] - - self.CALC: List = [] - - 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) - efuse_file = efuse_file.replace("esp32h2beta1", "esp32h2") - 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 - - for efuse in self.ALL_EFUSES: - if efuse is not None: - self.EFUSES.append(efuse) - - self.ALL_EFUSES = [] diff --git a/tools/esptool_py/espefuse/efuse/esp32h2beta1/operations.py b/tools/esptool_py/espefuse/efuse/esp32h2beta1/operations.py deleted file mode 100644 index f884b8aa55..0000000000 --- a/tools/esptool_py/espefuse/efuse/esp32h2beta1/operations.py +++ /dev/null @@ -1,407 +0,0 @@ -# This file includes the operations with eFuses for ESP32-H2 chip -# -# SPDX-FileCopyrightText: 2021-2022 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, - add_show_sensitive_info_option, - 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) - add_show_sensitive_info_option(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", - 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", - 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) - add_show_sensitive_info_option(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): - print("") - # fmt: off - if efuses["BLK_VERSION_MINOR"].get() == 2: - print("Temperature Sensor Calibration = {}C".format(efuses["TEMP_CALIB"].get())) - print("") - print("ADC1:") - print("AVE_INITCODE_ATTEN0 = ", efuses["ADC1_AVE_INITCODE_ATTEN0"].get()) - print("AVE_INITCODE_ATTEN1 = ", efuses["ADC1_AVE_INITCODE_ATTEN1"].get()) - print("AVE_INITCODE_ATTEN2 = ", efuses["ADC1_AVE_INITCODE_ATTEN2"].get()) - print("AVE_INITCODE_ATTEN3 = ", efuses["ADC1_AVE_INITCODE_ATTEN3"].get()) - print("HI_DOUT_ATTEN0 = ", efuses["ADC1_HI_DOUT_ATTEN0"].get()) - print("HI_DOUT_ATTEN1 = ", efuses["ADC1_HI_DOUT_ATTEN1"].get()) - print("HI_DOUT_ATTEN2 = ", efuses["ADC1_HI_DOUT_ATTEN2"].get()) - print("HI_DOUT_ATTEN3 = ", efuses["ADC1_HI_DOUT_ATTEN3"].get()) - print("CH0_ATTEN0_INITCODE_DIFF = ", efuses["ADC1_CH0_ATTEN0_INITCODE_DIFF"].get()) - print("CH1_ATTEN0_INITCODE_DIFF = ", efuses["ADC1_CH1_ATTEN0_INITCODE_DIFF"].get()) - print("CH2_ATTEN0_INITCODE_DIFF = ", efuses["ADC1_CH2_ATTEN0_INITCODE_DIFF"].get()) - print("CH3_ATTEN0_INITCODE_DIFF = ", efuses["ADC1_CH3_ATTEN0_INITCODE_DIFF"].get()) - print("CH4_ATTEN0_INITCODE_DIFF = ", efuses["ADC1_CH4_ATTEN0_INITCODE_DIFF"].get()) - else: - print("BLK_VERSION_MINOR = {}".format(efuses["BLK_VERSION_MINOR"].get())) - # 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: - 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 = "\tReversing byte order for AES-XTS hardware peripheral" - data = data[::-1] - print( - "-> [{}]".format( - util.hexify(data, " ") - if args.show_sensitive_info - else " ".join(["??"] * len(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") diff --git a/tools/esptool_py/espefuse/efuse/esp32h2beta1/__init__.py b/tools/esptool_py/espefuse/efuse/esp32h4/__init__.py similarity index 74% rename from tools/esptool_py/espefuse/efuse/esp32h2beta1/__init__.py rename to tools/esptool_py/espefuse/efuse/esp32h4/__init__.py index a3b55a8023..2318517102 100644 --- a/tools/esptool_py/espefuse/efuse/esp32h2beta1/__init__.py +++ b/tools/esptool_py/espefuse/efuse/esp32h4/__init__.py @@ -1,3 +1,5 @@ from . import operations from .emulate_efuse_controller import EmulateEfuseController from .fields import EspEfuses + +commands = operations.ESP32H4Commands diff --git a/tools/esptool_py/espefuse/efuse/esp32h2beta1/emulate_efuse_controller.py b/tools/esptool_py/espefuse/efuse/esp32h4/emulate_efuse_controller.py similarity index 93% rename from tools/esptool_py/espefuse/efuse/esp32h2beta1/emulate_efuse_controller.py rename to tools/esptool_py/espefuse/efuse/esp32h4/emulate_efuse_controller.py index 1c336cbfea..aa468477ad 100644 --- a/tools/esptool_py/espefuse/efuse/esp32h2beta1/emulate_efuse_controller.py +++ b/tools/esptool_py/espefuse/efuse/esp32h4/emulate_efuse_controller.py @@ -1,6 +1,6 @@ -# This file describes eFuses controller for ESP32-H2 chip +# This file describes eFuses controller for ESP32-H4 chip # -# SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD +# SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD # # SPDX-License-Identifier: GPL-2.0-or-later @@ -13,7 +13,7 @@ class EmulateEfuseController(EmulateEfuseControllerBase): """The class for virtual efuse operation. Using for HOST_TEST.""" - CHIP_NAME = "ESP32-H2(beta1)" + CHIP_NAME = "ESP32-H4" mem = None debug = False @@ -33,7 +33,7 @@ def get_minor_chip_version(self): return 0 def get_crystal_freq(self): - return 32 # MHz (common for all chips) + return 32 # MHz def get_security_info(self): return { diff --git a/tools/esptool_py/espefuse/efuse/esp32c5beta3/fields.py b/tools/esptool_py/espefuse/efuse/esp32h4/fields.py similarity index 84% rename from tools/esptool_py/espefuse/efuse/esp32c5beta3/fields.py rename to tools/esptool_py/espefuse/efuse/esp32h4/fields.py index 351d73667a..498acbbac6 100644 --- a/tools/esptool_py/espefuse/efuse/esp32c5beta3/fields.py +++ b/tools/esptool_py/espefuse/efuse/esp32h4/fields.py @@ -1,15 +1,15 @@ -# This file describes eFuses for ESP32-C5 beta3 chip +# This file describes eFuses for ESP32-H4 chip # -# SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD +# SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD # # SPDX-License-Identifier: GPL-2.0-or-later import binascii import struct -import sys import time from bitstring import BitArray +from esptool.logger import log import esptool @@ -55,9 +55,6 @@ 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, @@ -66,17 +63,15 @@ def __init__( do_not_confirm=False, extend_efuse_table=None, ): + super().__init__(esp, skip_connect, debug, do_not_confirm, extend_efuse_table) 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-C5(beta3)": + if esp.CHIP_NAME != "ESP32-H4": raise esptool.FatalError( - "Expected the 'esp' param for ESP32-C5(beta3) chip but got for '%s'." + "Expected the 'esp' param for ESP32-H4 chip but got for '%s'." % (esp.CHIP_NAME) ) if not skip_connect: @@ -102,11 +97,11 @@ def __init__( 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 - # ] + if False: # 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 ] @@ -136,14 +131,14 @@ def read_coding_scheme(self): self.coding_scheme = self.REGS.CODING_SCHEME_RS def print_status_regs(self): - print("") + log.print("") self.blocks[0].print_block(self.blocks[0].err_bitarray, "err__regs", debug=True) - print( + log.print( "{:27} 0x{:08x}".format( "EFUSE_RD_RS_ERR0_REG", self.read_reg(self.REGS.EFUSE_RD_RS_ERR0_REG) ) ) - print( + log.print( "{:27} 0x{:08x}".format( "EFUSE_RD_RS_ERR1_REG", self.read_reg(self.REGS.EFUSE_RD_RS_ERR1_REG) ) @@ -176,7 +171,7 @@ def wait_efuse_idle(self): # For PGM_CMD it is not necessary. return raise esptool.FatalError( - "Timed out waiting for Efuse controller command to complete" + "Timed out waiting for eFuse controller command to complete" ) def efuse_program(self, block): @@ -204,52 +199,43 @@ def efuse_read(self): try: self._esp = self.reconnect_chip(self._esp) except esptool.FatalError: - print("Can not re-connect to the chip") + log.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( + log.print( "This is the correct behavior as we are actually burning " - "DIS_DOWNLOAD_MODE which disables the connection to the chip" + "DIS_DOWNLOAD_MODE which disables the connection to the chip." ) - print("DIS_DOWNLOAD_MODE is enabled") - print("Successful") - sys.exit(0) # finish without errors + log.print("DIS_DOWNLOAD_MODE is enabled.") + log.print("Successful.") + exit(0) # finish without errors raise - print("Established a connection with the chip") + log.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") + log.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" + log.print( + "espefuse can not continue to work in Secure download mode." ) - print("ENABLE_SECURITY_DOWNLOAD is enabled") - print("Successful") - sys.exit(0) # finish without errors + log.print("ENABLE_SECURITY_DOWNLOAD is enabled.") + log.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 not in [40, 48]: + if apb_freq != 32: raise esptool.FatalError( - "The eFuse supports only xtal=40M and 48M (xtal was %d)" % apb_freq + "The eFuse supports only xtal=32M (xtal was %d)" % apb_freq ) - self.update_reg(self.REGS.EFUSE_DAC_CONF_REG, self.REGS.EFUSE_DAC_NUM_M, 0xFF) - self.update_reg( - self.REGS.EFUSE_DAC_CONF_REG, self.REGS.EFUSE_DAC_CLK_DIV_M, 0x28 - ) - self.update_reg( - self.REGS.EFUSE_WR_TIM_CONF1_REG, self.REGS.EFUSE_PWR_ON_NUM_M, 0x3000 - ) - self.update_reg( - self.REGS.EFUSE_WR_TIM_CONF2_REG, self.REGS.EFUSE_PWR_OFF_NUM_M, 0x190 - ) + # TODO: [ESP32H4] IDF-12268 def get_coding_scheme_warnings(self, silent=False): """Check if the coding scheme has detected any errors.""" @@ -280,9 +266,9 @@ def get_coding_scheme_warnings(self, silent=False): 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) + log.print( + f"Error(s) in BLOCK{block.id} " + f"[ERRORS:{block.num_errors} FAIL:{block.fail}]." ) if (self.debug or ret_fail) and not silent: self.print_status_regs() @@ -307,11 +293,8 @@ def convert(parent, efuse): class EfuseWafer(EfuseField): def get(self, from_read=True): - hi_bits = self.parent["WAFER_VERSION_MINOR_HI"].get(from_read) - assert self.parent["WAFER_VERSION_MINOR_HI"].bit_len == 1 - lo_bits = self.parent["WAFER_VERSION_MINOR_LO"].get(from_read) - assert self.parent["WAFER_VERSION_MINOR_LO"].bit_len == 3 - return (hi_bits << 3) + lo_bits + # TODO: [ESP32H4] IDF-12268 + return 0 def save(self, new_value): raise esptool.FatalError("Burning %s is not supported" % self.name) @@ -385,10 +368,8 @@ def get(self, from_read=True): def save(self, new_value): def print_field(e, new_value): - print( - " - '{}' ({}) {} -> {}".format( - e.name, e.description, e.get_bitstring(), new_value - ) + log.print( + f" - '{e.name}' ({e.description}) {e.get_bitstring()} -> {new_value}" ) if self.name == "CUSTOM_MAC": @@ -403,10 +384,12 @@ def print_field(e, new_value): # 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 - ("RESERVED", 1, None, None, "no_need_rd_protect"), # Reserved + ("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) @@ -415,8 +398,10 @@ class EfuseKeyPurposeField(EfuseField): ("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"] @@ -458,7 +443,4 @@ def get_name(self, raw_val): def save(self, new_value): raw_val = int(self.check_format(str(new_value))) - str_new_value = self.get_name(raw_val) - if self.name == "KEY_PURPOSE_5" and str_new_value.startswith("XTS_AES"): - raise esptool.FatalError(f"{self.name} can not have {str_new_value} key due to a hardware bug (please see TRM for more details)") return super(EfuseKeyPurposeField, self).save(raw_val) diff --git a/tools/esptool_py/espefuse/efuse/esp32s3beta2/mem_definition.py b/tools/esptool_py/espefuse/efuse/esp32h4/mem_definition.py similarity index 83% rename from tools/esptool_py/espefuse/efuse/esp32s3beta2/mem_definition.py rename to tools/esptool_py/espefuse/efuse/esp32h4/mem_definition.py index 0fb1ec8bad..cd87580938 100644 --- a/tools/esptool_py/espefuse/efuse/esp32s3beta2/mem_definition.py +++ b/tools/esptool_py/espefuse/efuse/esp32h4/mem_definition.py @@ -1,6 +1,6 @@ -# This file describes eFuses fields and registers for ESP32-S3(beta2) chip +# This file describes eFuses fields and registers for ESP32-H4 chip # -# SPDX-FileCopyrightText: 2020-2022 Espressif Systems (Shanghai) CO LTD +# SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD # # SPDX-License-Identifier: GPL-2.0-or-later @@ -17,29 +17,28 @@ class EfuseDefineRegisters(EfuseRegistersBase): - EFUSE_ADDR_MASK = 0x00000FFF EFUSE_MEM_SIZE = 0x01FC + 4 # EFUSE registers & command/conf values - DR_REG_EFUSE_BASE = 0x6001A000 + 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 + 0x1D0 - EFUSE_CMD_REG = DR_REG_EFUSE_BASE + 0x1D4 - EFUSE_RD_RS_ERR0_REG = DR_REG_EFUSE_BASE + 0x1C0 - EFUSE_RD_RS_ERR1_REG = DR_REG_EFUSE_BASE + 0x1C4 + 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 + 0x1E8 - EFUSE_RD_TIM_CONF_REG = DR_REG_EFUSE_BASE + 0x1EC + 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 + 0x1FC + EFUSE_DATE_REG = DR_REG_EFUSE_BASE + 0x198 EFUSE_WRITE_OP_CODE = 0x5A5A EFUSE_READ_OP_CODE = 0x5AA5 EFUSE_PGM_CMD_MASK = 0x3 @@ -84,7 +83,7 @@ class EfuseDefineBlocks(EfuseBlocksBase): # List of efuse blocks # fmt: off BLOCKS = [ - # Name, Alias, Index, Read address, Write address, Write protect bit, Read protect bit, Len, key_purpose + # 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), @@ -118,7 +117,7 @@ def __init__(self, extend_efuse_table) -> None: self.KEYBLOCKS = [] - # if BLK_VERSION_MAJOR is 1, these efuse fields are in BLOCK2 + # if BLK_VERSION_MINOR is 1, these efuse fields are in BLOCK2 self.BLOCK2_CALIBRATION_EFUSES = [] self.CALC = [] @@ -128,7 +127,6 @@ def __init__(self, extend_efuse_table) -> None: file_name = file_name + ".yaml" dir_name, _ = os.path.split(dir_name) efuse_file = os.path.join(dir_name, "efuse_defs", file_name) - efuse_file = efuse_file.replace("esp32s3beta2", "esp32s3") with open(f"{efuse_file}", "r") as r_file: e_desc = yaml.safe_load(r_file) super().__init__(e_desc, extend_efuse_table) @@ -155,13 +153,13 @@ def __init__(self, extend_efuse_table) -> None: self.ALL_EFUSES[i] = None f = Field() - f.name = "WAFER_VERSION_MINOR" - f.block = 0 - f.bit_len = 4 - f.type = f"uint:{f.bit_len}" - f.category = "identity" - f.class_type = "wafer" - f.description = "calc WAFER VERSION MINOR = WAFER_VERSION_MINOR_HI << 3 + WAFER_VERSION_MINOR_LO (read only)" + 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: diff --git a/tools/esptool_py/espefuse/efuse/esp32h4/operations.py b/tools/esptool_py/espefuse/efuse/esp32h4/operations.py new file mode 100644 index 0000000000..b7d3e7dfcc --- /dev/null +++ b/tools/esptool_py/espefuse/efuse/esp32h4/operations.py @@ -0,0 +1,305 @@ +# 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 + +from io import IOBase +from typing import BinaryIO +from esptool.logger import log +import rich_click as click + +import espsecure +import esptool + +from . import fields +from .mem_definition import EfuseDefineBlocks +from .. import util +from ..base_operations import ( + BaseCommands, + NonCompositeTuple, + TupleParameter, + add_force_write_always, + add_show_sensitive_info_option, + protect_options, +) + + +class ESP32H4Commands(BaseCommands): + CHIP_NAME = "ESP32-H4" + efuse_lib = fields.EspEfuses + + ################################### CLI definitions ################################### + + def add_cli_commands(self, cli: click.Group): + super().add_cli_commands(cli) + blocks_for_keys = EfuseDefineBlocks().get_blocks_for_keys() + + @cli.command( + "burn-key", + help="Burn the key block with the specified name. Arguments are groups of block name, " + "key file (containing 256 bits of binary key data) and key purpose.\n\n" + f"Block is one of: [{', '.join(blocks_for_keys)}]\n\n" + f"Key purpose is one of: [{', '.join(fields.EfuseKeyPurposeField.KEY_PURPOSES_NAME)}]", + ) + @click.argument( + "block_keyfile_keypurpose", + metavar=" ", + cls=TupleParameter, + required=True, + nargs=-1, + max_arity=len(blocks_for_keys), + type=NonCompositeTuple( + [ + click.Choice(blocks_for_keys), + click.File("rb"), + click.Choice(fields.EfuseKeyPurposeField.KEY_PURPOSES_NAME), + ] + ), + ) + @protect_options + @add_force_write_always + @add_show_sensitive_info_option + @click.pass_context + def burn_key_cli(ctx, **kwargs): + """Burn the key block with the specified name""" + kwargs.pop("force_write_always") + block, keyfile, keypurpose = zip(*kwargs.pop("block_keyfile_keypurpose")) + kwargs["show_sensitive_info"] = ctx.show_sensitive_info + self.burn_key(block, keyfile, keypurpose, **kwargs) + + @cli.command( + "burn-key-digest", + short_help="Parse a RSA public key and burn the digest.", + help="Parse a RSA public key and burn the digest to key eFuse block.\n\n" + f"Block is one of: [{', '.join(blocks_for_keys)}]\n\n" + f"Key purpose is one of: [{', '.join(fields.EfuseKeyPurposeField.DIGEST_KEY_PURPOSES)}]", + ) + @click.argument( + "block_keyfile_keypurpose", + metavar=" ", + cls=TupleParameter, + required=True, + nargs=-1, + max_arity=len(blocks_for_keys), + type=NonCompositeTuple( + [ + click.Choice(blocks_for_keys), + click.File("rb"), + click.Choice(fields.EfuseKeyPurposeField.DIGEST_KEY_PURPOSES), + ] + ), + ) + @protect_options + @add_force_write_always + @add_show_sensitive_info_option + @click.pass_context + def burn_key_digest_cli(ctx, **kwargs): + kwargs.pop("force_write_always") + block, keyfile, keypurpose = zip(*kwargs.pop("block_keyfile_keypurpose")) + kwargs["show_sensitive_info"] = ctx.show_sensitive_info + self.burn_key_digest(block, keyfile, keypurpose, **kwargs) + + ###################################### Commands ###################################### + + def adc_info(self): + # TODO: [ESP32H4] Add ADC info support + log.print("Not supported yet.") + + def burn_key( + self, + blocks: list[str], + keyfiles: list[BinaryIO], + keypurposes: list[str], + no_write_protect: bool = False, + no_read_protect: bool = False, + show_sensitive_info: bool = False, + digest: list[bytes] | None = None, + ): + """Burn the key block with the specified name. Arguments are groups of block name, + key file (containing 256 bits of binary key data) and key purpose. + + Args: + blocks: List of eFuse block names to burn keys to. + keyfiles: List of open files to read key data from. + keypurposes: List of key purposes to burn. + no_write_protect: If True, the write protection will NOT be enabled. + no_read_protect: If True, the read protection will NOT be enabled. + show_sensitive_info: If True, the sensitive information will be shown. + digest: List of digests to burn. + """ + datafile_list: list[BinaryIO] | list[bytes] + if digest is None: + datafile_list = keyfiles[ + 0 : len([name for name in keyfiles if name is not None]) : + ] + else: + datafile_list = digest[ + 0 : len([name for name in digest if name is not None]) : + ] + block_name_list = blocks[ + 0 : len([name for name in blocks if name is not None]) : + ] + keypurpose_list = keypurposes[ + 0 : len([name for name in keypurposes 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)) + ) + + log.print("Burn keys to blocks:") + for block_name, datafile, keypurpose in zip( + block_name_list, datafile_list, keypurpose_list + ): + efuse = None + for block in self.efuses.blocks: + if block_name == block.name or block_name in block.alias: + efuse = self.efuses[block.name] + if efuse is None: + raise esptool.FatalError(f"Unknown block name - {block_name}.") + num_bytes = efuse.bit_len // 8 + + block_num = self.efuses.get_index_block_by_name(block_name) + block = self.efuses.blocks[block_num] + + if isinstance(datafile, IOBase): + if keypurpose == "ECDSA_KEY": + sk = espsecure._load_ecdsa_signing_key(datafile) # type: ignore + data = espsecure.get_ecdsa_signing_key_raw_bytes(sk) + 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() + datafile.close() + else: + data = datafile + + log.print(f" - {efuse.name}", end=" ") + revers_msg = None + if self.efuses[block.key_purpose_name].need_reverse(keypurpose): + revers_msg = ( + f"\tReversing byte order for {keypurpose} hardware peripheral..." + ) + data = data[::-1] + log.print( + "-> [{}]".format( + util.hexify(data, " ") + if show_sensitive_info + else " ".join(["??"] * len(data)) + ) + ) + if revers_msg: + log.print(revers_msg) + if len(data) != num_bytes: + raise esptool.FatalError( + f"Incorrect key file size {len(data)}. Key file must be {num_bytes}" + f" bytes ({num_bytes * 8} bits) of raw binary key data." + ) + + if self.efuses[block.key_purpose_name].need_rd_protect(keypurpose): + read_protect = False if no_read_protect else True + else: + read_protect = False + write_protect = not 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 self.efuses[block.key_purpose_name].get() != keypurpose: + if self.efuses[block.key_purpose_name].is_writeable(): + log.print( + f"\t'{block.key_purpose_name}': " + f"'{self.efuses[block.key_purpose_name].get()}' -> '{keypurpose}'." + ) + self.efuses[block.key_purpose_name].save(keypurpose) + disable_wr_protect_key_purpose = True + else: + raise esptool.FatalError( + f"It is not possible to change '{block.key_purpose_name}' " + f"to '{keypurpose}' because write protection bit is set." + ) + else: + log.print(f"\t'{block.key_purpose_name}' is already '{keypurpose}'.") + if self.efuses[block.key_purpose_name].is_writeable(): + disable_wr_protect_key_purpose = True + + if disable_wr_protect_key_purpose: + log.print(f"\tDisabling write to '{block.key_purpose_name}'...") + self.efuses[block.key_purpose_name].disable_write() + + if read_protect: + log.print("\tDisabling read to key block...") + efuse.disable_read() + + if write_protect: + log.print("\tDisabling write to key block...") + efuse.disable_write() + log.print("") + + if not write_protect: + log.print("Keys will remain writeable (due to --no-write-protect).") + if no_read_protect: + log.print("Keys will remain readable (due to --no-read-protect).") + + if not self.efuses.burn_all(check_batch_mode=True): + return + log.print("Successful.") + + def burn_key_digest( + self, + blocks: list[str], + keyfiles: list[BinaryIO], + keypurposes: list[str], + no_write_protect: bool = False, + no_read_protect: bool = False, + show_sensitive_info: bool = False, + ): + """Parse a RSA public key and burn the digest to key eFuse block. + + Args: + blocks: List of eFuse block names to burn keys to. + keyfiles: List of open files to read key data from. + keypurposes: List of key purposes to burn. + no_write_protect: If True, the write protection will NOT be enabled. + no_read_protect: If True, the read protection will NOT be enabled. + show_sensitive_info: If True, the sensitive information will be shown. + """ + digest_list = [] + datafile_list = keyfiles[ + 0 : len([name for name in keyfiles if name is not None]) : + ] + block_list = blocks[0 : len([block for block in blocks if block is not None]) :] + + for block_name, datafile in zip(block_list, datafile_list): + efuse = None + for block in self.efuses.blocks: + if block_name == block.name or block_name in block.alias: + efuse = self.efuses[block.name] + if efuse is None: + raise esptool.FatalError(f"Unknown block name - {block_name}.") + num_bytes = efuse.bit_len // 8 + digest = espsecure._digest_sbv2_public_key(datafile) + if len(digest) != num_bytes: + raise esptool.FatalError( + f"Incorrect digest size {len(digest)}. Digest must be {num_bytes} " + f"bytes ({num_bytes * 8} bits) of raw binary key data." + ) + digest_list.append(digest) + + self.burn_key( + block_list, + datafile_list, + keypurposes, + no_write_protect, + no_read_protect, + show_sensitive_info, + digest=digest_list, + ) diff --git a/tools/esptool_py/espefuse/efuse/esp32p4/__init__.py b/tools/esptool_py/espefuse/efuse/esp32p4/__init__.py index a3b55a8023..10143c20ed 100644 --- a/tools/esptool_py/espefuse/efuse/esp32p4/__init__.py +++ b/tools/esptool_py/espefuse/efuse/esp32p4/__init__.py @@ -1,3 +1,5 @@ from . import operations from .emulate_efuse_controller import EmulateEfuseController from .fields import EspEfuses + +commands = operations.ESP32P4Commands diff --git a/tools/esptool_py/espefuse/efuse/esp32p4/fields.py b/tools/esptool_py/espefuse/efuse/esp32p4/fields.py index e28d788dc8..1bbea20185 100644 --- a/tools/esptool_py/espefuse/efuse/esp32p4/fields.py +++ b/tools/esptool_py/espefuse/efuse/esp32p4/fields.py @@ -10,6 +10,7 @@ import time from bitstring import BitArray +from esptool.logger import log import esptool @@ -55,9 +56,6 @@ 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, @@ -66,14 +64,12 @@ def __init__( do_not_confirm=False, extend_efuse_table=None, ): + super().__init__(esp, skip_connect, debug, do_not_confirm, extend_efuse_table) 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-P4": raise esptool.FatalError( "Expected the 'esp' param for ESP32-P4 chip but got for '%s'." @@ -102,12 +98,11 @@ def __init__( for efuse in self.Fields.BLOCK2_CALIBRATION_EFUSES ] else: - # TODO add processing of self.Fields.BLOCK2_CALIBRATION_EFUSES - # if self["BLK_VERSION_MINOR"].get() == 1: - # self.efuses += [ - # EfuseField.convert(self, efuse) - # for efuse in self.Fields.BLOCK2_CALIBRATION_EFUSES - # ] + if self.get_block_version() >= 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 ] @@ -137,14 +132,14 @@ def read_coding_scheme(self): self.coding_scheme = self.REGS.CODING_SCHEME_RS def print_status_regs(self): - print("") + log.print("") self.blocks[0].print_block(self.blocks[0].err_bitarray, "err__regs", debug=True) - print( + log.print( "{:27} 0x{:08x}".format( "EFUSE_RD_RS_ERR0_REG", self.read_reg(self.REGS.EFUSE_RD_RS_ERR0_REG) ) ) - print( + log.print( "{:27} 0x{:08x}".format( "EFUSE_RD_RS_ERR1_REG", self.read_reg(self.REGS.EFUSE_RD_RS_ERR1_REG) ) @@ -177,7 +172,7 @@ def wait_efuse_idle(self): # For PGM_CMD it is not necessary. return raise esptool.FatalError( - "Timed out waiting for Efuse controller command to complete" + "Timed out waiting for eFuse controller command to complete" ) def efuse_program(self, block): @@ -205,30 +200,30 @@ def efuse_read(self): try: self._esp = self.reconnect_chip(self._esp) except esptool.FatalError: - print("Can not re-connect to the chip") + log.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( + log.print( "This is the correct behavior as we are actually burning " - "DIS_DOWNLOAD_MODE which disables the connection to the chip" + "DIS_DOWNLOAD_MODE which disables the connection to the chip." ) - print("DIS_DOWNLOAD_MODE is enabled") - print("Successful") + log.print("DIS_DOWNLOAD_MODE is enabled.") + log.print("Successful.") sys.exit(0) # finish without errors raise - print("Established a connection with the chip") + log.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") + log.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" + log.print( + "espefuse can not continue to work in Secure download mode." ) - print("ENABLE_SECURITY_DOWNLOAD is enabled") - print("Successful") + log.print("ENABLE_SECURITY_DOWNLOAD is enabled.") + log.print("Successful.") sys.exit(0) # finish without errors raise @@ -271,9 +266,9 @@ def get_coding_scheme_warnings(self, silent=False): 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) + log.print( + f"Error(s) in BLOCK{block.id} " + f"[ERRORS:{block.num_errors} FAIL:{block.fail}]." ) if (self.debug or ret_fail) and not silent: self.print_status_regs() @@ -292,9 +287,22 @@ def convert(parent, efuse): "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): + hi_bits = self.parent["WAFER_VERSION_MAJOR_HI"].get(from_read) + assert self.parent["WAFER_VERSION_MAJOR_HI"].bit_len == 1 + lo_bits = self.parent["WAFER_VERSION_MAJOR_LO"].get(from_read) + assert self.parent["WAFER_VERSION_MAJOR_LO"].bit_len == 2 + return (hi_bits << 2) + lo_bits + + def save(self, new_value): + raise esptool.FatalError(f"Burning {self.name} is not supported") + + class EfuseTempSensor(EfuseField): def get(self, from_read=True): value = self.get_bitstring(from_read) @@ -342,7 +350,7 @@ def check_format(self, new_value_str): 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) + output = f"Block{self.block} has ERRORS:{errs} FAIL:{fail}." else: output = "OK" return "(" + output + ")" @@ -363,10 +371,8 @@ def get(self, from_read=True): def save(self, new_value): def print_field(e, new_value): - print( - " - '{}' ({}) {} -> {}".format( - e.name, e.description, e.get_bitstring(), new_value - ) + log.print( + f" - '{e.name}' ({e.description}) {e.get_bitstring()} -> {new_value}" ) if self.name == "CUSTOM_MAC": @@ -376,7 +382,7 @@ def print_field(e, 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") + raise esptool.FatalError(f"Burning {self.name} is not supported.") # fmt: off @@ -411,9 +417,9 @@ def check_format(self, new_value_str): 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) + raise esptool.FatalError(f"'{raw_val}' can not be set (value out of range).") else: - raise esptool.FatalError("'%s' unknown name" % raw_val) + raise esptool.FatalError(f"'{raw_val}' unknown name.") return raw_val def need_reverse(self, new_key_purpose): diff --git a/tools/esptool_py/espefuse/efuse/esp32p4/mem_definition.py b/tools/esptool_py/espefuse/efuse/esp32p4/mem_definition.py index c1427e516a..ebd0c9187b 100644 --- a/tools/esptool_py/espefuse/efuse/esp32p4/mem_definition.py +++ b/tools/esptool_py/espefuse/efuse/esp32p4/mem_definition.py @@ -12,8 +12,8 @@ EfuseBlocksBase, EfuseFieldsBase, EfuseRegistersBase, + Field, ) -from typing import List class EfuseDefineRegisters(EfuseRegistersBase): @@ -114,13 +114,9 @@ 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: List = [] + self.CALC: list = [] dir_name = os.path.dirname(os.path.abspath(__file__)) dir_name, file_name = os.path.split(dir_name) @@ -152,6 +148,16 @@ def __init__(self, extend_efuse_table) -> None: self.BLOCK2_CALIBRATION_EFUSES.append(efuse) self.ALL_EFUSES[i] = None + f = Field() + f.name = "WAFER_VERSION_MAJOR" + f.block = 0 + f.bit_len = 3 + f.type = f"uint:{f.bit_len}" + f.category = "identity" + f.class_type = "wafer" + f.description = "calc WAFER VERSION MAJOR from (WAFER_VERSION_MAJOR_HI << 2) + WAFER_VERSION_MAJOR_LO (read only)" + self.CALC.append(f) + for efuse in self.ALL_EFUSES: if efuse is not None: self.EFUSES.append(efuse) diff --git a/tools/esptool_py/espefuse/efuse/esp32p4/operations.py b/tools/esptool_py/espefuse/efuse/esp32p4/operations.py index b24a735121..e659f3291a 100644 --- a/tools/esptool_py/espefuse/efuse/esp32p4/operations.py +++ b/tools/esptool_py/espefuse/efuse/esp32p4/operations.py @@ -1,458 +1,318 @@ # This file includes the operations with eFuses for ESP32-P4 chip # -# SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD +# SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD # # SPDX-License-Identifier: GPL-2.0-or-later -import argparse import io -import os # noqa: F401. It is used in IDF scripts -import traceback +from typing import BinaryIO +from esptool.logger import log +import rich_click as click import espsecure - import esptool from . import fields +from .mem_definition import EfuseDefineBlocks from .. import util from ..base_operations import ( - add_common_commands, + BaseCommands, + NonCompositeTuple, + TupleParameter, add_force_write_always, add_show_sensitive_info_option, - burn_bit, - burn_block_data, - burn_efuse, - check_error, - dump, - read_protect_efuse, - summary, - write_protect_efuse, + protect_options, ) -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", - ) +class ESP32P4Commands(BaseCommands): + CHIP_NAME = "ESP32-P4" + efuse_lib = fields.EspEfuses + ################################### CLI definitions ################################### -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) - add_show_sensitive_info_option(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, - ) + def add_cli_commands(self, cli: click.Group): + super().add_cli_commands(cli) + blocks_for_keys = EfuseDefineBlocks().get_blocks_for_keys() - 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) - add_show_sensitive_info_option(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, + @cli.command( + "burn-key", + short_help="Burn a key to a key eFuse block.", + help="Burn a key to a key eFuse block. Arguments are groups of block name, " + "key file (containing 256 bits of binary key data) and key purpose.\n\n" + f"Block is one of: [{', '.join(blocks_for_keys)}]\n\n" + f"Key purpose is one of: [{', '.join(fields.EfuseKeyPurposeField.KEY_PURPOSES_NAME)}]", ) - burn_key_digest.add_argument( - "keyfile", - help="Key file to digest (PEM format)", - nargs="?", - action="append", - metavar="KEYFILE", - type=argparse.FileType("rb"), + @click.argument( + "block_keyfile_keypurpose", + metavar=" ", + cls=TupleParameter, + required=True, + nargs=-1, + max_arity=len(blocks_for_keys), + type=NonCompositeTuple( + [ + click.Choice(blocks_for_keys), + click.File("rb"), + click.Choice(fields.EfuseKeyPurposeField.KEY_PURPOSES_NAME), + ] + ), ) - burn_key_digest.add_argument( - "keypurpose", - help="Purpose to set.", - nargs="?", - action="append", - metavar="KEYPURPOSE", - choices=fields.EfuseKeyPurposeField.DIGEST_KEY_PURPOSES, + @protect_options + @add_force_write_always + @add_show_sensitive_info_option + @click.pass_context + def burn_key_cli(ctx, **kwargs): + kwargs.pop("force_write_always") + block, keyfile, keypurpose = zip(*kwargs.pop("block_keyfile_keypurpose")) + kwargs["show_sensitive_info"] = ctx.show_sensitive_info + self.burn_key(block, keyfile, keypurpose, **kwargs) + + @cli.command( + "burn-key-digest", + short_help="Burn a digest to a key eFuse block.", + help="Burn a digest to a key eFuse block. Arguments are groups of block name, " + "key file (containing 256 bits of binary key data) and key purpose.\n\n" + f"Block is one of: [{', '.join(blocks_for_keys)}]\n\n" + f"Key purpose is one of: [{', '.join(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): - print("Not supported yet") - - -def get_custom_mac(esp, efuses, args): - print("Not supported yet") - - -def set_flash_voltage(esp, efuses, args): - raise esptool.FatalError("set_flash_voltage is not supported!") - - -def adc_info(esp, efuses, args): - print("not supported yet") - - -def key_block_is_unused(block, key_purpose_block): - if not block.is_readable() or not block.is_writeable(): - return False - - if key_purpose_block.get() != "USER" or not key_purpose_block.is_writeable(): - return False - - if not block.get_bitstring().all(False): - return False - - return True - - -def get_next_key_block(efuses, current_key_block, block_name_list): - key_blocks = [b for b in efuses.blocks if b.key_purpose_name] - start = key_blocks.index(current_key_block) - - # Sort key blocks so that we pick the next free block (and loop around if necessary) - key_blocks = key_blocks[start:] + key_blocks[0:start] - - # Exclude any other blocks that will be be burned - key_blocks = [b for b in key_blocks if b.name not in block_name_list] - - for block in key_blocks: - key_purpose_block = efuses[block.key_purpose_name] - if key_block_is_unused(block, key_purpose_block): - return block - - return None - - -def split_512_bit_key(efuses, block_name_list, datafile_list, keypurpose_list): - i = keypurpose_list.index("XTS_AES_256_KEY") - block_name = block_name_list[i] - - block_num = efuses.get_index_block_by_name(block_name) - block = efuses.blocks[block_num] - - data = datafile_list[i].read() - if len(data) != 64: - raise esptool.FatalError( - "Incorrect key file size %d, XTS_AES_256_KEY should be 64 bytes" % len(data) + @click.argument( + "block_keyfile_keypurpose", + metavar=" ", + cls=TupleParameter, + required=True, + nargs=-1, + max_arity=len(blocks_for_keys), + type=NonCompositeTuple( + [ + click.Choice(blocks_for_keys), + click.File("rb"), + click.Choice(fields.EfuseKeyPurposeField.DIGEST_KEY_PURPOSES), + ] + ), ) - - key_block_2 = get_next_key_block(efuses, block, block_name_list) - if not key_block_2: - raise esptool.FatalError("XTS_AES_256_KEY requires two free keyblocks") - - keypurpose_list.append("XTS_AES_256_KEY_1") - datafile_list.append(io.BytesIO(data[:32])) - block_name_list.append(block_name) - - keypurpose_list.append("XTS_AES_256_KEY_2") - datafile_list.append(io.BytesIO(data[32:])) - block_name_list.append(key_block_2.name) - - keypurpose_list.pop(i) - datafile_list.pop(i) - block_name_list.pop(i) - - -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]) : - ] - - if "XTS_AES_256_KEY" in keypurpose_list: - # XTS_AES_256_KEY is not an actual HW key purpose, needs to be split into - # XTS_AES_256_KEY_1 and XTS_AES_256_KEY_2 - split_512_bit_key(efuses, block_name_list, datafile_list, keypurpose_list) - - 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 + @protect_options + @add_force_write_always + @add_show_sensitive_info_option + @click.pass_context + def burn_key_digest_cli(ctx, **kwargs): + kwargs.pop("force_write_always") + block, keyfile, keypurpose = zip(*kwargs.pop("block_keyfile_keypurpose")) + kwargs["show_sensitive_info"] = ctx.show_sensitive_info + self.burn_key_digest(block, keyfile, keypurpose, **kwargs) + + ###################################### Commands ###################################### + + def adc_info(self): + log.print("Block version:", self.efuses.get_block_version()) + if self.efuses.get_block_version() >= 1: + for efuse in self.efuses: + if efuse.category == "calibration": + log.print(f"{efuse.name:<30} = ", self.efuses[efuse.name].get()) + + def burn_key( + self, + blocks: list[str], + keyfiles: list[BinaryIO], + keypurposes: list[str], + no_write_protect: bool = False, + no_read_protect: bool = False, + show_sensitive_info: bool = False, + digest: list[bytes] | None = None, ): - 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] - + """Burn the key block with the specified name. Arguments are groups of block name, + key file (containing 256 bits of binary key data) and key purpose. + + Args: + blocks: List of eFuse block names to burn keys to. + keyfiles: List of open files to read key data from. + keypurposes: List of key purposes to burn. + no_write_protect: If True, the write protection will NOT be enabled. + no_read_protect: If True, the read protection will NOT be enabled. + show_sensitive_info: If True, the sensitive information will be shown. + digest: List of digests to burn. + """ + datafile_list: list[BinaryIO] | list[bytes] 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, add 8 bytes of padding - data = b"\x00" * 8 + data - else: - data = datafile.read() + datafile_list = keyfiles[ + 0 : len([name for name in keyfiles if name is not None]) : + ] else: - data = datafile + datafile_list = digest[ + 0 : len([name for name in digest if name is not None]) : + ] + block_name_list = blocks[ + 0 : len([name for name in blocks if name is not None]) : + ] + keypurpose_list = keypurposes[ + 0 : len([name for name in keypurposes if name is not None]) : + ] - 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( - "-> [{}]".format( - util.hexify(data, " ") - if args.show_sensitive_info - else " ".join(["??"] * len(data)) + if "XTS_AES_256_KEY" in keypurpose_list: + # XTS_AES_256_KEY is not an actual HW key purpose, needs to be split into + # XTS_AES_256_KEY_1 and XTS_AES_256_KEY_2 + block_name_list, datafile_list, keypurpose_list = self._split_512_bit_key( + block_name_list, + datafile_list, # type: ignore + keypurpose_list, ) - ) - if revers_msg: - print(revers_msg) - if len(data) != num_bytes: + + 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( - "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) + "The number of blocks (%d), datafile (%d) and keypurpose (%d) should be the same." + % (len(block_name_list), len(datafile_list), len(keypurpose_list)) ) - 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 + log.print("Burn keys to blocks:") + for block_name, datafile, keypurpose in zip( + block_name_list, datafile_list, keypurpose_list + ): + efuse = None + for block in self.efuses.blocks: + if block_name == block.name or block_name in block.alias: + efuse = self.efuses[block.name] + if efuse is None: + raise esptool.FatalError(f"Unknown block name - {block_name}.") + num_bytes = efuse.bit_len // 8 + + block_num = self.efuses.get_index_block_by_name(block_name) + block = self.efuses.blocks[block_num] + + if isinstance(datafile, io.IOBase): + if keypurpose == "ECDSA_KEY": + sk = espsecure.load_ecdsa_signing_key(datafile) # type: ignore + data = espsecure.get_ecdsa_signing_key_raw_bytes(sk) + if len(data) == 24: + # the private key is 24 bytes long for NIST192p, add 8 bytes of padding + data = b"\x00" * 8 + data + else: + data = datafile.read() + datafile.close() + else: + data = datafile - # using efuse instead of a block gives the advantage of checking it as the whole field. - efuse.save(data) + log.print(f" - {efuse.name}", end=" ") + revers_msg = None + if self.efuses[block.key_purpose_name].need_reverse(keypurpose): + revers_msg = ( + f"\tReversing byte order for {keypurpose} hardware peripheral..." + ) + data = data[::-1] + log.print( + "-> [{}]".format( + util.hexify(data, " ") + if show_sensitive_info + else " ".join(["??"] * len(data)) + ) + ) + if revers_msg: + log.print(revers_msg) + if len(data) != num_bytes: + raise esptool.FatalError( + f"Incorrect key file size {len(data)}. Key file must be {num_bytes} " + f"bytes ({num_bytes * 8} bits) of raw binary key 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, + if self.efuses[block.key_purpose_name].need_rd_protect(keypurpose): + read_protect = False if no_read_protect else True + else: + read_protect = False + write_protect = not 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 self.efuses[block.key_purpose_name].get() != keypurpose: + if self.efuses[block.key_purpose_name].is_writeable(): + log.print( + f"\t'{block.key_purpose_name}': " + f"'{self.efuses[block.key_purpose_name].get()}' -> '{keypurpose}'." + ) + self.efuses[block.key_purpose_name].save(keypurpose) + disable_wr_protect_key_purpose = True + else: + raise esptool.FatalError( + f"It is not possible to change '{block.key_purpose_name}' " + f"to '{keypurpose}' because write protection bit is set." ) - ) - efuses[block.key_purpose_name].save(keypurpose) - disable_wr_protect_key_purpose = True else: + log.print(f"\t'{block.key_purpose_name}' is already '{keypurpose}'.") + if self.efuses[block.key_purpose_name].is_writeable(): + disable_wr_protect_key_purpose = True + + if disable_wr_protect_key_purpose: + log.print(f"\tDisabling write to '{block.key_purpose_name}'...") + self.efuses[block.key_purpose_name].disable_write() + + if read_protect: + log.print("\tDisabling read to key block...") + efuse.disable_read() + + if write_protect: + log.print("\tDisabling write to key block...") + efuse.disable_write() + log.print("") + + if not write_protect: + log.print("Keys will remain writeable (due to --no-write-protect).") + if no_read_protect: + log.print("Keys will remain readable (due to --no-read-protect).") + + if not self.efuses.burn_all(check_batch_mode=True): + return + log.print("Successful.") + + def burn_key_digest( + self, + blocks: list[str], + keyfiles: list[BinaryIO], + keypurposes: list[str], + no_write_protect: bool = False, + no_read_protect: bool = False, + show_sensitive_info: bool = False, + ): + """Parse a RSA public key and burn the digest to key eFuse block. + + Args: + blocks: List of eFuse block names to burn keys to. + keyfiles: List of open files to read key data from. + keypurposes: List of key purposes to burn. + no_write_protect: If True, the write protection will NOT be enabled. + no_read_protect: If True, the read protection will NOT be enabled. + show_sensitive_info: If True, the sensitive information will be shown. + """ + digest_list = [] + datafile_list = keyfiles[ + 0 : len([name for name in keyfiles if name is not None]) : + ] + block_list = blocks[0 : len([block for block in blocks if block is not None]) :] + + for block_name, datafile in zip(block_list, datafile_list): + efuse = None + for block in self.efuses.blocks: + if block_name == block.name or block_name in block.alias: + efuse = self.efuses[block.name] + if efuse is None: + raise esptool.FatalError(f"Unknown block name - {block_name}.") + num_bytes = efuse.bit_len // 8 + digest = espsecure._digest_sbv2_public_key(datafile) + if len(digest) != num_bytes: raise esptool.FatalError( - "It is not possible to change '%s' to '%s' " - "because write protection bit is set." - % (block.key_purpose_name, keypurpose) + f"Incorrect digest size {len(digest)}. Digest must be {num_bytes} " + f"bytes ({num_bytes * 8} bits) of raw binary key data." ) - 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") + digest_list.append(digest) + + self.burn_key( + block_list, + datafile_list, + keypurposes, + no_write_protect, + no_read_protect, + show_sensitive_info, + digest=digest_list, + ) diff --git a/tools/esptool_py/espefuse/efuse/esp32s2/__init__.py b/tools/esptool_py/espefuse/efuse/esp32s2/__init__.py index a3b55a8023..208e35edfd 100644 --- a/tools/esptool_py/espefuse/efuse/esp32s2/__init__.py +++ b/tools/esptool_py/espefuse/efuse/esp32s2/__init__.py @@ -1,3 +1,5 @@ from . import operations from .emulate_efuse_controller import EmulateEfuseController from .fields import EspEfuses + +commands = operations.ESP32S2Commands diff --git a/tools/esptool_py/espefuse/efuse/esp32s2/fields.py b/tools/esptool_py/espefuse/efuse/esp32s2/fields.py index 1339fdb143..d773e76d7a 100644 --- a/tools/esptool_py/espefuse/efuse/esp32s2/fields.py +++ b/tools/esptool_py/espefuse/efuse/esp32s2/fields.py @@ -10,6 +10,7 @@ import time from bitstring import BitArray +from esptool.logger import log import esptool @@ -55,9 +56,6 @@ 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, @@ -66,14 +64,12 @@ def __init__( do_not_confirm=False, extend_efuse_table=None, ): + super().__init__(esp, skip_connect, debug, do_not_confirm, extend_efuse_table) 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-S2": raise esptool.FatalError( "Expected the 'esp' param for ESP32-S2 chip but got for '%s'." @@ -102,7 +98,7 @@ def __init__( for efuse in self.Fields.BLOCK2_CALIBRATION_EFUSES ] else: - if self["BLK_VERSION_MINOR"].get() == 1: + if self.get_block_version() >= 1: self.efuses += [ EfuseField.convert(self, efuse) for efuse in self.Fields.BLOCK2_CALIBRATION_EFUSES @@ -136,14 +132,14 @@ def read_coding_scheme(self): self.coding_scheme = self.REGS.CODING_SCHEME_RS def print_status_regs(self): - print("") + log.print("") self.blocks[0].print_block(self.blocks[0].err_bitarray, "err__regs", debug=True) - print( + log.print( "{:27} 0x{:08x}".format( "EFUSE_RD_RS_ERR0_REG", self.read_reg(self.REGS.EFUSE_RD_RS_ERR0_REG) ) ) - print( + log.print( "{:27} 0x{:08x}".format( "EFUSE_RD_RS_ERR1_REG", self.read_reg(self.REGS.EFUSE_RD_RS_ERR1_REG) ) @@ -176,7 +172,7 @@ def wait_efuse_idle(self): # For PGM_CMD it is not necessary. return raise esptool.FatalError( - "Timed out waiting for Efuse controller command to complete" + "Timed out waiting for eFuse controller command to complete" ) def efuse_program(self, block): @@ -204,30 +200,30 @@ def efuse_read(self): try: self._esp = self.reconnect_chip(self._esp) except esptool.FatalError: - print("Can not re-connect to the chip") + log.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( + log.print( "This is the correct behavior as we are actually burning " - "DIS_DOWNLOAD_MODE which disables the connection to the chip" + "DIS_DOWNLOAD_MODE which disables the connection to the chip." ) - print("DIS_DOWNLOAD_MODE is enabled") - print("Successful") + log.print("DIS_DOWNLOAD_MODE is enabled.") + log.print("Successful.") sys.exit(0) # finish without errors raise - print("Established a connection with the chip") + log.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") + log.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" + log.print( + "espefuse can not continue to work in Secure download mode." ) - print("ENABLE_SECURITY_DOWNLOAD is enabled") - print("Successful") + log.print("ENABLE_SECURITY_DOWNLOAD is enabled.") + log.print("Successful.") sys.exit(0) # finish without errors raise @@ -319,9 +315,9 @@ def get_coding_scheme_warnings(self, silent=False): 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) + log.print( + f"Error(s) in BLOCK{block.id} " + f"[ERRORS:{block.num_errors} FAIL:{block.fail}]." ) if (self.debug or ret_fail) and not silent: self.print_status_regs() @@ -410,7 +406,7 @@ def check_format(self, new_value_str): 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) + output = f"Block{self.block} has ERRORS:{errs} FAIL:{fail}." else: output = "OK" return "(" + output + ")" @@ -424,10 +420,8 @@ def get(self, from_read=True): def save(self, new_value): def print_field(e, new_value): - print( - " - '{}' ({}) {} -> {}".format( - e.name, e.description, e.get_bitstring(), new_value - ) + log.print( + f" - '{e.name}' ({e.description}) {e.get_bitstring()} -> {new_value}" ) if self.name == "CUSTOM_MAC": diff --git a/tools/esptool_py/espefuse/efuse/esp32s2/mem_definition.py b/tools/esptool_py/espefuse/efuse/esp32s2/mem_definition.py index 4799751db9..a5888ba5d5 100644 --- a/tools/esptool_py/espefuse/efuse/esp32s2/mem_definition.py +++ b/tools/esptool_py/espefuse/efuse/esp32s2/mem_definition.py @@ -153,12 +153,8 @@ 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__)) diff --git a/tools/esptool_py/espefuse/efuse/esp32s2/operations.py b/tools/esptool_py/espefuse/efuse/esp32s2/operations.py index 3f015e2216..a841b6fda4 100644 --- a/tools/esptool_py/espefuse/efuse/esp32s2/operations.py +++ b/tools/esptool_py/espefuse/efuse/esp32s2/operations.py @@ -1,521 +1,384 @@ # This file includes the operations with eFuses for ESP32S2 chip # -# SPDX-FileCopyrightText: 2020-2022 Espressif Systems (Shanghai) CO LTD +# SPDX-FileCopyrightText: 2020-2025 Espressif Systems (Shanghai) CO LTD # # SPDX-License-Identifier: GPL-2.0-or-later -import argparse import io -import os # noqa: F401. It is used in IDF scripts -import traceback +from typing import BinaryIO +from esptool.logger import log -import espsecure +import rich_click as click +import espsecure import esptool from . import fields +from .mem_definition import EfuseDefineBlocks from .. import util from ..base_operations import ( - add_common_commands, + BaseCommands, + NonCompositeTuple, + TupleParameter, add_force_write_always, add_show_sensitive_info_option, - burn_bit, - burn_block_data, - burn_efuse, - check_error, - dump, - read_protect_efuse, - summary, - write_protect_efuse, + protect_options, ) -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) - add_show_sensitive_info_option(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", - 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", - 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, - ) +class ESP32S2Commands(BaseCommands): + CHIP_NAME = "ESP32-S2" + efuse_lib = fields.EspEfuses - 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) - add_show_sensitive_info_option(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, - ) + ################################### CLI definitions ################################### - 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): - sdio_force = efuses["VDD_SPI_FORCE"] - sdio_tieh = efuses["VDD_SPI_TIEH"] - sdio_reg = efuses["VDD_SPI_XPD"] - - # check efuses aren't burned in a way which makes this impossible - if args.voltage == "OFF" and sdio_reg.get() != 0: - raise esptool.FatalError( - "Can't set flash regulator to OFF as VDD_SPI_XPD efuse is already burned" - ) + def add_cli_commands(self, cli: click.Group): + super().add_cli_commands(cli) + blocks_for_keys = EfuseDefineBlocks().get_blocks_for_keys() - if args.voltage == "1.8V" and sdio_tieh.get() != 0: - raise esptool.FatalError( - "Can't set regulator to 1.8V is VDD_SPI_TIEH efuse is already burned" + @cli.command( + "burn-key", + help="Burn the key block with the specified name. Arguments are groups of block name, " + "key file (containing 256 bits of binary key data) and key purpose.\n\n" + f"Block is one of: [{', '.join(blocks_for_keys)}]\n\n" + f"Key purpose is one of: [{', '.join(fields.EfuseKeyPurposeField.KEY_PURPOSES_NAME)}]", ) - - if args.voltage == "OFF": - msg = "Disable internal flash voltage regulator (VDD_SPI). SPI flash will " - "need to be powered from an external source.\n" - "The following efuse is burned: VDD_SPI_FORCE.\n" - "It is possible to later re-enable the internal regulator (%s) " % ( - "to 3.3V" if sdio_tieh.get() != 0 else "to 1.8V or 3.3V" + @click.argument( + "block_keyfile_keypurpose", + metavar=" ", + cls=TupleParameter, + required=True, + nargs=-1, + max_arity=len(blocks_for_keys), + type=NonCompositeTuple( + [ + click.Choice(blocks_for_keys), + click.File("rb"), + click.Choice(fields.EfuseKeyPurposeField.KEY_PURPOSES_NAME), + ] + ), ) - "by burning an additional efuse" - elif args.voltage == "1.8V": - msg = "Set internal flash voltage regulator (VDD_SPI) to 1.8V.\n" - "The following efuses are burned: VDD_SPI_FORCE, VDD_SPI_XPD.\n" - "It is possible to later increase the voltage to 3.3V (permanently) " - "by burning additional efuse VDD_SPI_TIEH" - elif args.voltage == "3.3V": - msg = "Enable internal flash voltage regulator (VDD_SPI) to 3.3V.\n" - "The following efuses are burned: VDD_SPI_FORCE, VDD_SPI_XPD, VDD_SPI_TIEH." - print(msg) - - sdio_force.save(1) # Disable GPIO45 - if args.voltage != "OFF": - sdio_reg.save(1) # Enable internal regulator - if args.voltage == "3.3V": - sdio_tieh.save(1) - print("VDD_SPI setting complete.") - - if not efuses.burn_all(check_batch_mode=True): - return - print("Successful") - - -def adc_info(esp, efuses, args): - print("") - # fmt: off - if efuses["BLK_VERSION_MINOR"].get() == 1: - print("Temperature Sensor Calibration = {}C".format(efuses["TEMP_CALIB"].get())) - print("TADC_CALIB = {}C".format(efuses["ADC_CALIB"].get())) - print("RTCCALIB_V1IDX_A10H = ", efuses["RTCCALIB_V1IDX_A10H"].get()) - print("RTCCALIB_V1IDX_A11H = ", efuses["RTCCALIB_V1IDX_A11H"].get()) - print("RTCCALIB_V1IDX_A12H = ", efuses["RTCCALIB_V1IDX_A12H"].get()) - print("RTCCALIB_V1IDX_A13H = ", efuses["RTCCALIB_V1IDX_A13H"].get()) - print("RTCCALIB_V1IDX_A20H = ", efuses["RTCCALIB_V1IDX_A20H"].get()) - print("RTCCALIB_V1IDX_A21H = ", efuses["RTCCALIB_V1IDX_A21H"].get()) - print("RTCCALIB_V1IDX_A22H = ", efuses["RTCCALIB_V1IDX_A22H"].get()) - print("RTCCALIB_V1IDX_A23H = ", efuses["RTCCALIB_V1IDX_A23H"].get()) - print("RTCCALIB_V1IDX_A10L = ", efuses["RTCCALIB_V1IDX_A10L"].get()) - print("RTCCALIB_V1IDX_A11L = ", efuses["RTCCALIB_V1IDX_A11L"].get()) - print("RTCCALIB_V1IDX_A12L = ", efuses["RTCCALIB_V1IDX_A12L"].get()) - print("RTCCALIB_V1IDX_A13L = ", efuses["RTCCALIB_V1IDX_A13L"].get()) - print("RTCCALIB_V1IDX_A20L = ", efuses["RTCCALIB_V1IDX_A20L"].get()) - print("RTCCALIB_V1IDX_A21L = ", efuses["RTCCALIB_V1IDX_A21L"].get()) - print("RTCCALIB_V1IDX_A22L = ", efuses["RTCCALIB_V1IDX_A22L"].get()) - print("RTCCALIB_V1IDX_A23L = ", efuses["RTCCALIB_V1IDX_A23L"].get()) - else: - print("BLK_VERSION_MINOR = ", efuses["BLK_VERSION_MINOR"].get_meaning()) - # fmt: on - - -def key_block_is_unused(block, key_purpose_block): - if not block.is_readable() or not block.is_writeable(): - return False - - if key_purpose_block.get() != "USER" or not key_purpose_block.is_writeable(): - return False - - if not block.get_bitstring().all(False): - return False - - return True - - -def get_next_key_block(efuses, current_key_block, block_name_list): - key_blocks = [b for b in efuses.blocks if b.key_purpose_name] - start = key_blocks.index(current_key_block) - - # Sort key blocks so that we pick the next free block (and loop around if necessary) - key_blocks = key_blocks[start:] + key_blocks[0:start] - - # Exclude any other blocks that will be be burned - key_blocks = [b for b in key_blocks if b.name not in block_name_list] - - for block in key_blocks: - key_purpose_block = efuses[block.key_purpose_name] - if key_block_is_unused(block, key_purpose_block): - return block - - return None - - -def split_512_bit_key(efuses, block_name_list, datafile_list, keypurpose_list): - i = keypurpose_list.index("XTS_AES_256_KEY") - block_name = block_name_list[i] - - block_num = efuses.get_index_block_by_name(block_name) - block = efuses.blocks[block_num] - - data = datafile_list[i].read() - if len(data) != 64: - raise esptool.FatalError( - "Incorrect key file size %d, XTS_AES_256_KEY should be 64 bytes" % len(data) + @protect_options + @add_force_write_always + @add_show_sensitive_info_option + @click.pass_context + def burn_key_cli(ctx, **kwargs): + kwargs.pop("force_write_always") + block, keyfile, keypurpose = zip(*kwargs.pop("block_keyfile_keypurpose")) + kwargs["show_sensitive_info"] = ctx.show_sensitive_info + self.burn_key(block, keyfile, keypurpose, **kwargs) + + @cli.command( + "burn-key-digest", + help="Burn the key block with the specified name. Arguments are groups of block name, " + "key file (containing 256 bits of binary key data) and key purpose.\n\n" + f"Block is one of: [{', '.join(blocks_for_keys)}]\n\n" + f"Key purpose is one of: [{', '.join(fields.EfuseKeyPurposeField.KEY_PURPOSES_NAME)}]", ) + @click.argument( + "block_keyfile_keypurpose", + metavar=" ", + cls=TupleParameter, + required=True, + nargs=-1, + max_arity=len(blocks_for_keys), + type=NonCompositeTuple( + [ + click.Choice(blocks_for_keys), + click.File("rb"), + click.Choice(fields.EfuseKeyPurposeField.DIGEST_KEY_PURPOSES), + ] + ), + ) + @protect_options + @add_force_write_always + @add_show_sensitive_info_option + @click.pass_context + def burn_key_digest_cli(ctx, **kwargs): + kwargs.pop("force_write_always") + block, keyfile, keypurpose = zip(*kwargs.pop("block_keyfile_keypurpose")) + kwargs["show_sensitive_info"] = ctx.show_sensitive_info + self.burn_key_digest(block, keyfile, keypurpose, **kwargs) + + @cli.command( + "set-flash-voltage", + short_help="Permanently set the internal flash voltage regulator.", + ) + @click.argument("voltage", type=click.Choice(["1.8V", "3.3V", "OFF"])) + def set_flash_voltage_cli(voltage): + """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.""" + self.set_flash_voltage(voltage) - key_block_2 = get_next_key_block(efuses, block, block_name_list) - if not key_block_2: - raise esptool.FatalError("XTS_AES_256_KEY requires two free keyblocks") - - keypurpose_list.append("XTS_AES_256_KEY_1") - datafile_list.append(io.BytesIO(data[:32])) - block_name_list.append(block_name) + ###################################### Commands ###################################### - keypurpose_list.append("XTS_AES_256_KEY_2") - datafile_list.append(io.BytesIO(data[32:])) - block_name_list.append(key_block_2.name) + def set_flash_voltage(self, voltage: str): + sdio_force = self.efuses["VDD_SPI_FORCE"] + sdio_tieh = self.efuses["VDD_SPI_TIEH"] + sdio_reg = self.efuses["VDD_SPI_XPD"] - keypurpose_list.pop(i) - datafile_list.pop(i) - block_name_list.pop(i) + # check efuses aren't burned in a way which makes this impossible + if voltage == "OFF" and sdio_reg.get() != 0: + raise esptool.FatalError( + "Can't set flash regulator to OFF as VDD_SPI_XPD eFuse is already burned" + ) + if voltage == "1.8V" and sdio_tieh.get() != 0: + raise esptool.FatalError( + "Can't set regulator to 1.8V is VDD_SPI_TIEH eFuse is already burned" + ) -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]) : - ] - - if "XTS_AES_256_KEY" in keypurpose_list: - # XTS_AES_256_KEY is not an actual HW key purpose, needs to be split into - # XTS_AES_256_KEY_1 and XTS_AES_256_KEY_2 - split_512_bit_key(efuses, block_name_list, datafile_list, keypurpose_list) - - 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)) - ) + if voltage == "OFF": + log.print( + "Disable internal flash voltage regulator (VDD_SPI). " + "SPI flash will need to be powered from an external source.\n" + "The following eFuse is burned: VDD_SPI_FORCE.\n" + "It is possible to later re-enable the internal regulator" + f"{'to 3.3V' if sdio_tieh.get() != 0 else 'to 1.8V or 3.3V'}" + "by burning an additional eFuse" + ) + elif voltage == "1.8V": + log.print( + "Set internal flash voltage regulator (VDD_SPI) to 1.8V.\n" + "The following eFuses are burned: VDD_SPI_FORCE, VDD_SPI_XPD.\n" + "It is possible to later increase the voltage to 3.3V (permanently) " + "by burning additional eFuse VDD_SPI_TIEH" + ) + elif voltage == "3.3V": + log.print( + "Enable internal flash voltage regulator (VDD_SPI) to 3.3V.\n" + "The following eFuses are burned: VDD_SPI_FORCE, VDD_SPI_XPD, VDD_SPI_TIEH." + ) - print("Burn keys to blocks:") - for block_name, datafile, keypurpose in zip( - block_name_list, datafile_list, keypurpose_list + sdio_force.save(1) # Disable GPIO45 + if voltage != "OFF": + sdio_reg.save(1) # Enable internal regulator + if voltage == "3.3V": + sdio_tieh.save(1) + log.print("VDD_SPI setting complete.") + + if not self.efuses.burn_all(check_batch_mode=True): + return + log.print("Successful.") + + def adc_info(self): + log.print("Block version:", self.efuses.get_block_version()) + if self.efuses.get_block_version() >= 1: + # fmt: off + log.print(f"Temperature Sensor Calibration = {self.efuses['TEMP_CALIB'].get()}C") + log.print(f"TADC_CALIB = {self.efuses['ADC_CALIB'].get()}C") + log.print("RTCCALIB_V1IDX_A10H = ", self.efuses["RTCCALIB_V1IDX_A10H"].get()) + log.print("RTCCALIB_V1IDX_A11H = ", self.efuses["RTCCALIB_V1IDX_A11H"].get()) + log.print("RTCCALIB_V1IDX_A12H = ", self.efuses["RTCCALIB_V1IDX_A12H"].get()) + log.print("RTCCALIB_V1IDX_A13H = ", self.efuses["RTCCALIB_V1IDX_A13H"].get()) + log.print("RTCCALIB_V1IDX_A20H = ", self.efuses["RTCCALIB_V1IDX_A20H"].get()) + log.print("RTCCALIB_V1IDX_A21H = ", self.efuses["RTCCALIB_V1IDX_A21H"].get()) + log.print("RTCCALIB_V1IDX_A22H = ", self.efuses["RTCCALIB_V1IDX_A22H"].get()) + log.print("RTCCALIB_V1IDX_A23H = ", self.efuses["RTCCALIB_V1IDX_A23H"].get()) + log.print("RTCCALIB_V1IDX_A10L = ", self.efuses["RTCCALIB_V1IDX_A10L"].get()) + log.print("RTCCALIB_V1IDX_A11L = ", self.efuses["RTCCALIB_V1IDX_A11L"].get()) + log.print("RTCCALIB_V1IDX_A12L = ", self.efuses["RTCCALIB_V1IDX_A12L"].get()) + log.print("RTCCALIB_V1IDX_A13L = ", self.efuses["RTCCALIB_V1IDX_A13L"].get()) + log.print("RTCCALIB_V1IDX_A20L = ", self.efuses["RTCCALIB_V1IDX_A20L"].get()) + log.print("RTCCALIB_V1IDX_A21L = ", self.efuses["RTCCALIB_V1IDX_A21L"].get()) + log.print("RTCCALIB_V1IDX_A22L = ", self.efuses["RTCCALIB_V1IDX_A22L"].get()) + log.print("RTCCALIB_V1IDX_A23L = ", self.efuses["RTCCALIB_V1IDX_A23L"].get()) + # fmt: on + + def burn_key( + self, + blocks: list[str], + keyfiles: list[BinaryIO], + keypurposes: list[str], + no_write_protect: bool = False, + no_read_protect: bool = False, + show_sensitive_info: bool = False, + digest: list[bytes] | None = None, ): - 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] - + """Burn the key block with the specified name. Arguments are groups of block name, + key file (containing 256 bits of binary key data) and key purpose. + + Args: + blocks: List of eFuse block names to burn keys to. + keyfiles: List of open files to read key data from. + keypurposes: List of key purposes to burn. + no_write_protect: If True, the write protection will NOT be enabled. + no_read_protect: If True, the read protection will NOT be enabled. + show_sensitive_info: If True, the sensitive information will be shown. + digest: List of digests to burn. + """ + datafile_list: list[BinaryIO] | list[bytes] if digest is None: - data = datafile.read() + datafile_list = keyfiles[ + 0 : len([name for name in keyfiles if name is not None]) : + ] else: - data = datafile - - print(" - %s" % (efuse.name), end=" ") - revers_msg = None - if efuses[block.key_purpose_name].need_reverse(keypurpose): - revers_msg = "\tReversing byte order for AES-XTS hardware peripheral" - data = data[::-1] - print( - "-> [{}]".format( - util.hexify(data, " ") - if args.show_sensitive_info - else " ".join(["??"] * len(data)) + datafile_list = digest[ + 0 : len([name for name in digest if name is not None]) : + ] + block_name_list = blocks[ + 0 : len([name for name in blocks if name is not None]) : + ] + keypurpose_list = keypurposes[ + 0 : len([name for name in keypurposes if name is not None]) : + ] + + if "XTS_AES_256_KEY" in keypurpose_list: + # XTS_AES_256_KEY is not an actual HW key purpose, needs to be split into + # XTS_AES_256_KEY_1 and XTS_AES_256_KEY_2 + block_name_list, datafile_list, keypurpose_list = self._split_512_bit_key( + block_name_list, + datafile_list, # type: ignore + keypurpose_list, ) - ) - if revers_msg: - print(revers_msg) - if len(data) != num_bytes: + + 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( - "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) + "The number of blocks (%d), datafile (%d) and keypurpose (%d) should be the same." + % (len(block_name_list), len(datafile_list), len(keypurpose_list)) ) - 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, - ) + log.print("Burn keys to blocks:") + for block_name, datafile, keypurpose in zip( + block_name_list, datafile_list, keypurpose_list + ): + efuse = None + for block in self.efuses.blocks: + if block_name == block.name or block_name in block.alias: + efuse = self.efuses[block.name] + if efuse is None: + raise esptool.FatalError(f"Unknown block name - {block_name}.") + num_bytes = efuse.bit_len // 8 + + block_num = self.efuses.get_index_block_by_name(block_name) + block = self.efuses.blocks[block_num] + + if isinstance(datafile, io.IOBase): + data = datafile.read() + datafile.close() + else: + data = datafile # type: ignore # this is safe but mypy still complains + + log.print(f" - {efuse.name}", end=" ") + revers_msg = None + if self.efuses[block.key_purpose_name].need_reverse(keypurpose): + revers_msg = "\tReversing byte order for AES-XTS hardware peripheral..." + data = data[::-1] + log.print( + "-> [{}]".format( + util.hexify(data, " ") + if show_sensitive_info + else " ".join(["??"] * len(data)) ) - efuses[block.key_purpose_name].save(keypurpose) - disable_wr_protect_key_purpose = True + ) + if revers_msg: + log.print(revers_msg) + if len(data) != num_bytes: + raise esptool.FatalError( + f"Incorrect key file size {len(data)}. Key file must be {num_bytes} " + f"bytes ({num_bytes * 8} bits) of raw binary key data." + ) + + if self.efuses[block.key_purpose_name].need_rd_protect(keypurpose): + read_protect = False if no_read_protect else True + else: + read_protect = False + write_protect = not 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 self.efuses[block.key_purpose_name].get() != keypurpose: + if self.efuses[block.key_purpose_name].is_writeable(): + log.print( + f"\t'{block.key_purpose_name}': " + f"'{self.efuses[block.key_purpose_name].get()}' -> '{keypurpose}'." + ) + self.efuses[block.key_purpose_name].save(keypurpose) + disable_wr_protect_key_purpose = True + else: + raise esptool.FatalError( + f"It is not possible to change '{block.key_purpose_name}' " + f"to '{keypurpose}' because write protection bit is set." + ) else: + log.print(f"\t'{block.key_purpose_name}' is already '{keypurpose}'.") + if self.efuses[block.key_purpose_name].is_writeable(): + disable_wr_protect_key_purpose = True + + if disable_wr_protect_key_purpose: + log.print(f"\tDisabling write to '{block.key_purpose_name}'...") + self.efuses[block.key_purpose_name].disable_write() + + if read_protect: + log.print("\tDisabling read to key block...") + efuse.disable_read() + + if write_protect: + log.print("\tDisabling write to key block...") + efuse.disable_write() + log.print("") + + if not write_protect: + log.print("Keys will remain writeable (due to --no-write-protect).") + if no_read_protect: + log.print("Keys will remain readable (due to --no-read-protect).") + + if not self.efuses.burn_all(check_batch_mode=True): + return + log.print("Successful.") + + def burn_key_digest( + self, + blocks: list[str], + keyfiles: list[BinaryIO], + keypurposes: list[str], + no_write_protect: bool = False, + no_read_protect: bool = False, + show_sensitive_info: bool = False, + ): + """Parse a RSA public key and burn the digest to key eFuse block. + + Args: + blocks: List of eFuse block names to burn keys to. + keyfiles: List of open files to read key data from. + keypurposes: List of key purposes to burn. + no_write_protect: If True, the write protection will NOT be enabled. + no_read_protect: If True, the read protection will NOT be enabled. + show_sensitive_info: If True, the sensitive information will be shown. + """ + digest_list = [] + datafile_list = keyfiles[ + 0 : len([name for name in keyfiles if name is not None]) : + ] + block_list = blocks[0 : len([block for block in blocks if block is not None]) :] + + for block_name, datafile in zip(block_list, datafile_list): + efuse = None + for block in self.efuses.blocks: + if block_name == block.name or block_name in block.alias: + efuse = self.efuses[block.name] + if efuse is None: + raise esptool.FatalError(f"Unknown block name - {block_name}.") + num_bytes = efuse.bit_len // 8 + digest = espsecure._digest_sbv2_public_key(datafile) + if len(digest) != num_bytes: raise esptool.FatalError( - "It is not possible to change '%s' to '%s' because " - "write protection bit is set." - % (block.key_purpose_name, keypurpose) + f"Incorrect digest size {len(digest)}. Digest must be {num_bytes} " + f"bytes ({num_bytes * 8} bits) of raw binary key data." ) - 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") + digest_list.append(digest) + + self.burn_key( + block_list, + datafile_list, + keypurposes, + no_write_protect, + no_read_protect, + show_sensitive_info, + digest=digest_list, + ) diff --git a/tools/esptool_py/espefuse/efuse/esp32s3/__init__.py b/tools/esptool_py/espefuse/efuse/esp32s3/__init__.py index a3b55a8023..3f3d435552 100644 --- a/tools/esptool_py/espefuse/efuse/esp32s3/__init__.py +++ b/tools/esptool_py/espefuse/efuse/esp32s3/__init__.py @@ -1,3 +1,5 @@ from . import operations from .emulate_efuse_controller import EmulateEfuseController from .fields import EspEfuses + +commands = operations.ESP32S3Commands diff --git a/tools/esptool_py/espefuse/efuse/esp32s3/fields.py b/tools/esptool_py/espefuse/efuse/esp32s3/fields.py index 9bb4eeaf30..07c4f19899 100644 --- a/tools/esptool_py/espefuse/efuse/esp32s3/fields.py +++ b/tools/esptool_py/espefuse/efuse/esp32s3/fields.py @@ -10,6 +10,7 @@ import time from bitstring import BitArray +from esptool.logger import log import esptool @@ -55,9 +56,6 @@ 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, @@ -66,14 +64,12 @@ def __init__( do_not_confirm=False, extend_efuse_table=None, ): + super().__init__(esp, skip_connect, debug, do_not_confirm, extend_efuse_table) 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-S3": raise esptool.FatalError( "Expected the 'esp' param for ESP32-S3 chip but got for '%s'." @@ -102,7 +98,7 @@ def __init__( for efuse in self.Fields.BLOCK2_CALIBRATION_EFUSES ] else: - if self["BLK_VERSION_MAJOR"].get() == 1: + if self.get_block_version() >= 100: self.efuses += [ EfuseField.convert(self, efuse) for efuse in self.Fields.BLOCK2_CALIBRATION_EFUSES @@ -136,14 +132,14 @@ def read_coding_scheme(self): self.coding_scheme = self.REGS.CODING_SCHEME_RS def print_status_regs(self): - print("") + log.print("") self.blocks[0].print_block(self.blocks[0].err_bitarray, "err__regs", debug=True) - print( + log.print( "{:27} 0x{:08x}".format( "EFUSE_RD_RS_ERR0_REG", self.read_reg(self.REGS.EFUSE_RD_RS_ERR0_REG) ) ) - print( + log.print( "{:27} 0x{:08x}".format( "EFUSE_RD_RS_ERR1_REG", self.read_reg(self.REGS.EFUSE_RD_RS_ERR1_REG) ) @@ -176,7 +172,7 @@ def wait_efuse_idle(self): # For PGM_CMD it is not necessary. return raise esptool.FatalError( - "Timed out waiting for Efuse controller command to complete" + "Timed out waiting for eFuse controller command to complete" ) def efuse_program(self, block): @@ -204,30 +200,30 @@ def efuse_read(self): try: self._esp = self.reconnect_chip(self._esp) except esptool.FatalError: - print("Can not re-connect to the chip") + log.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( + log.print( "This is the correct behavior as we are actually burning " - "DIS_DOWNLOAD_MODE which disables the connection to the chip" + "DIS_DOWNLOAD_MODE which disables the connection to the chip." ) - print("DIS_DOWNLOAD_MODE is enabled") - print("Successful") + log.print("DIS_DOWNLOAD_MODE is enabled.") + log.print("Successful.") sys.exit(0) # finish without errors raise - print("Established a connection with the chip") + log.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") + log.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" + log.print( + "espefuse can not continue to work in Secure download mode." ) - print("ENABLE_SECURITY_DOWNLOAD is enabled") - print("Successful") + log.print("ENABLE_SECURITY_DOWNLOAD is enabled.") + log.print("Successful.") sys.exit(0) # finish without errors raise @@ -280,9 +276,9 @@ def get_coding_scheme_warnings(self, silent=False): 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) + log.print( + f"Error(s) in BLOCK{block.id} " + f"[ERRORS:{block.num_errors} FAIL:{block.fail}]." ) if (self.debug or ret_fail) and not silent: self.print_status_regs() @@ -319,8 +315,9 @@ def is_efuses_incompatible_for_burn(self): and self["DIS_USB_SERIAL_JTAG"].get(from_read=False) ) ): - print( - "DIS_USB_JTAG and DIS_USB_SERIAL_JTAG cannot be set together due to a bug in the ROM bootloader!" + log.print( + "DIS_USB_JTAG and DIS_USB_SERIAL_JTAG cannot be set together " + "due to a bug in the ROM bootloader!" ) return True return False @@ -335,9 +332,22 @@ def convert(parent, efuse): "t_sensor": EfuseTempSensor, "adc_tp": EfuseAdcPointCalibration, "wafer": EfuseWafer, + "psram_cap": EfusePsramCap, }.get(efuse.class_type, EfuseField)(parent, efuse) +class EfusePsramCap(EfuseField): + def get(self, from_read=True): + hi_bits = self.parent["PSRAM_CAP_3"].get(from_read) + assert self.parent["PSRAM_CAP_3"].bit_len == 1 + lo_bits = self.parent["PSRAM_CAP"].get(from_read) + assert self.parent["PSRAM_CAP"].bit_len == 2 + return (hi_bits << 2) + lo_bits + + def save(self, new_value): + raise esptool.FatalError("Burning %s is not supported" % self.name) + + class EfuseWafer(EfuseField): def get(self, from_read=True): hi_bits = self.parent["WAFER_VERSION_MINOR_HI"].get(from_read) @@ -407,10 +417,8 @@ def get(self, from_read=True): def save(self, new_value): def print_field(e, new_value): - print( - " - '{}' ({}) {} -> {}".format( - e.name, e.description, e.get_bitstring(), new_value - ) + log.print( + f" - '{e.name}' ({e.description}) {e.get_bitstring()} -> {new_value}" ) if self.name == "CUSTOM_MAC": @@ -420,7 +428,7 @@ def print_field(e, new_value): else: # Writing the BLOCK1 (MAC_SPI_8M_0) default MAC is not sensible, # as it's written in the factory. - raise esptool.FatalError("Writing Factory MAC address is not supported") + raise esptool.FatalError("Writing Factory MAC address is not supported.") # fmt: off @@ -455,9 +463,9 @@ def check_format(self, new_value_str): 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) + raise esptool.FatalError(f"'{raw_val}' can not be set (value out of range).") else: - raise esptool.FatalError("'%s' unknown name" % raw_val) + raise esptool.FatalError(f"'{raw_val}' unknown name.") return raw_val def need_reverse(self, new_key_purpose): diff --git a/tools/esptool_py/espefuse/efuse/esp32s3/mem_definition.py b/tools/esptool_py/espefuse/efuse/esp32s3/mem_definition.py index 11aecfa651..361cbb9719 100644 --- a/tools/esptool_py/espefuse/efuse/esp32s3/mem_definition.py +++ b/tools/esptool_py/espefuse/efuse/esp32s3/mem_definition.py @@ -115,12 +115,8 @@ 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_MAJOR is 1, these efuse fields are in BLOCK2 self.BLOCK2_CALIBRATION_EFUSES = [] - self.CALC = [] dir_name = os.path.dirname(os.path.abspath(__file__)) @@ -163,6 +159,16 @@ def __init__(self, extend_efuse_table) -> None: f.description = "calc WAFER VERSION MINOR = WAFER_VERSION_MINOR_HI << 3 + WAFER_VERSION_MINOR_LO (read only)" self.CALC.append(f) + f = Field() + f.name = "PSRAM_CAPACITY" + f.block = 0 + f.bit_len = 3 + f.type = f"uint:{f.bit_len}" + f.category = "identity" + f.class_type = "psram_cap" + f.description = "calc as = PSRAM_CAP_3 << 2 + PSRAM_CAP (read only)" + self.CALC.append(f) + for efuse in self.ALL_EFUSES: if efuse is not None: self.EFUSES.append(efuse) diff --git a/tools/esptool_py/espefuse/efuse/esp32s3/operations.py b/tools/esptool_py/espefuse/efuse/esp32s3/operations.py index 912ae3f0c0..021e4d8734 100644 --- a/tools/esptool_py/espefuse/efuse/esp32s3/operations.py +++ b/tools/esptool_py/espefuse/efuse/esp32s3/operations.py @@ -1,521 +1,388 @@ # This file includes the operations with eFuses for ESP32-S3 chip # -# SPDX-FileCopyrightText: 2020-2022 Espressif Systems (Shanghai) CO LTD +# SPDX-FileCopyrightText: 2020-2025 Espressif Systems (Shanghai) CO LTD # # SPDX-License-Identifier: GPL-2.0-or-later -import argparse import io -import os # noqa: F401. It is used in IDF scripts -import traceback +from typing import BinaryIO -import espsecure +from esptool.logger import log +import rich_click as click +import espsecure import esptool from . import fields +from .mem_definition import EfuseDefineBlocks from .. import util from ..base_operations import ( - add_common_commands, + BaseCommands, + TupleParameter, + NonCompositeTuple, add_force_write_always, add_show_sensitive_info_option, - burn_bit, - burn_block_data, - burn_efuse, - check_error, - dump, - read_protect_efuse, - summary, - write_protect_efuse, + protect_options, ) -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) - add_show_sensitive_info_option(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", - 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", - 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, - ) +class ESP32S3Commands(BaseCommands): + CHIP_NAME = "ESP32-S3" + efuse_lib = fields.EspEfuses - 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) - add_show_sensitive_info_option(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, - ) + ################################### CLI definitions ################################### - 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): - sdio_force = efuses["VDD_SPI_FORCE"] - sdio_tieh = efuses["VDD_SPI_TIEH"] - sdio_reg = efuses["VDD_SPI_XPD"] - - # check efuses aren't burned in a way which makes this impossible - if args.voltage == "OFF" and sdio_reg.get() != 0: - raise esptool.FatalError( - "Can't set flash regulator to OFF as VDD_SPI_XPD efuse is already burned" - ) + def add_cli_commands(self, cli: click.Group): + super().add_cli_commands(cli) + blocks_for_keys = EfuseDefineBlocks().get_blocks_for_keys() - if args.voltage == "1.8V" and sdio_tieh.get() != 0: - raise esptool.FatalError( - "Can't set regulator to 1.8V is VDD_SPI_TIEH efuse is already burned" + @cli.command( + "burn-key", + help="Burn the key block with the specified name. Arguments are groups of block name, " + "key file (containing 256 bits of binary key data) and key purpose.\n\n" + f"Block is one of: [{', '.join(blocks_for_keys)}]\n\n" + f"Key purpose is one of: [{', '.join(fields.EfuseKeyPurposeField.KEY_PURPOSES_NAME)}]", ) - - if args.voltage == "OFF": - msg = "Disable internal flash voltage regulator (VDD_SPI). " - "SPI flash will need to be powered from an external source.\n" - "The following efuse is burned: VDD_SPI_FORCE.\n" - "It is possible to later re-enable the internal regulator (%s) " % ( - "to 3.3V" if sdio_tieh.get() != 0 else "to 1.8V or 3.3V" + @click.argument( + "block_keyfile_keypurpose", + metavar=" ", + cls=TupleParameter, + required=True, + nargs=-1, + max_arity=len(blocks_for_keys), + type=NonCompositeTuple( + [ + click.Choice(blocks_for_keys), + click.File("rb"), + click.Choice(fields.EfuseKeyPurposeField.KEY_PURPOSES_NAME), + ] + ), ) - "by burning an additional efuse" - elif args.voltage == "1.8V": - msg = "Set internal flash voltage regulator (VDD_SPI) to 1.8V.\n" - "The following efuses are burned: VDD_SPI_FORCE, VDD_SPI_XPD.\n" - "It is possible to later increase the voltage to 3.3V (permanently) " - "by burning additional efuse VDD_SPI_TIEH" - elif args.voltage == "3.3V": - msg = "Enable internal flash voltage regulator (VDD_SPI) to 3.3V.\n" - "The following efuses are burned: VDD_SPI_FORCE, VDD_SPI_XPD, VDD_SPI_TIEH." - print(msg) - - sdio_force.save(1) # Disable GPIO45 - if args.voltage != "OFF": - sdio_reg.save(1) # Enable internal regulator - if args.voltage == "3.3V": - sdio_tieh.save(1) - print("VDD_SPI setting complete.") - if not efuses.burn_all(check_batch_mode=True): - return - print("Successful") - - -def adc_info(esp, efuses, args): - print("") - # fmt: off - if efuses["BLK_VERSION_MAJOR"].get() == 1: - print("Temperature Sensor Calibration = {}C".format(efuses["TEMP_CALIB"].get())) - print("ADC OCode = ", efuses["OCODE"].get()) - print("ADC1:") - print("INIT_CODE_ATTEN0 = ", efuses["ADC1_INIT_CODE_ATTEN0"].get()) - print("INIT_CODE_ATTEN1 = ", efuses["ADC1_INIT_CODE_ATTEN1"].get()) - print("INIT_CODE_ATTEN2 = ", efuses["ADC1_INIT_CODE_ATTEN2"].get()) - print("INIT_CODE_ATTEN3 = ", efuses["ADC1_INIT_CODE_ATTEN3"].get()) - print("CAL_VOL_ATTEN0 = ", efuses["ADC1_CAL_VOL_ATTEN0"].get()) - print("CAL_VOL_ATTEN1 = ", efuses["ADC1_CAL_VOL_ATTEN1"].get()) - print("CAL_VOL_ATTEN2 = ", efuses["ADC1_CAL_VOL_ATTEN2"].get()) - print("CAL_VOL_ATTEN3 = ", efuses["ADC1_CAL_VOL_ATTEN3"].get()) - print("ADC2:") - print("INIT_CODE_ATTEN0 = ", efuses["ADC2_INIT_CODE_ATTEN0"].get()) - print("INIT_CODE_ATTEN1 = ", efuses["ADC2_INIT_CODE_ATTEN1"].get()) - print("INIT_CODE_ATTEN2 = ", efuses["ADC2_INIT_CODE_ATTEN2"].get()) - print("INIT_CODE_ATTEN3 = ", efuses["ADC2_INIT_CODE_ATTEN3"].get()) - print("CAL_VOL_ATTEN0 = ", efuses["ADC2_CAL_VOL_ATTEN0"].get()) - print("CAL_VOL_ATTEN1 = ", efuses["ADC2_CAL_VOL_ATTEN1"].get()) - print("CAL_VOL_ATTEN2 = ", efuses["ADC2_CAL_VOL_ATTEN2"].get()) - else: - print("BLK_VERSION_MAJOR = ", efuses["BLK_VERSION_MAJOR"].get_meaning()) - # fmt: on - - -def key_block_is_unused(block, key_purpose_block): - if not block.is_readable() or not block.is_writeable(): - return False - - if key_purpose_block.get() != "USER" or not key_purpose_block.is_writeable(): - return False - - if not block.get_bitstring().all(False): - return False - - return True - - -def get_next_key_block(efuses, current_key_block, block_name_list): - key_blocks = [b for b in efuses.blocks if b.key_purpose_name] - start = key_blocks.index(current_key_block) - - # Sort key blocks so that we pick the next free block (and loop around if necessary) - key_blocks = key_blocks[start:] + key_blocks[0:start] - - # Exclude any other blocks that will be be burned - key_blocks = [b for b in key_blocks if b.name not in block_name_list] - - for block in key_blocks: - key_purpose_block = efuses[block.key_purpose_name] - if key_block_is_unused(block, key_purpose_block): - return block - - return None - - -def split_512_bit_key(efuses, block_name_list, datafile_list, keypurpose_list): - i = keypurpose_list.index("XTS_AES_256_KEY") - block_name = block_name_list[i] - - block_num = efuses.get_index_block_by_name(block_name) - block = efuses.blocks[block_num] - - data = datafile_list[i].read() - if len(data) != 64: - raise esptool.FatalError( - "Incorrect key file size %d, XTS_AES_256_KEY should be 64 bytes" % len(data) + @protect_options + @add_force_write_always + @add_show_sensitive_info_option + @click.pass_context + def burn_key_cli(ctx, **kwargs): + """Burn the key block with the specified name""" + kwargs.pop("force_write_always") + block, keyfile, keypurpose = zip(*kwargs.pop("block_keyfile_keypurpose")) + kwargs["show_sensitive_info"] = ctx.show_sensitive_info + self.burn_key(block, keyfile, keypurpose, **kwargs) + + @cli.command( + "burn-key-digest", + help="Burn the key block with the specified name. Arguments are groups of block name, " + "key file (containing 256 bits of binary key data) and key purpose.\n\n" + f"Block is one of: [{', '.join(blocks_for_keys)}]\n\n" + f"Key purpose is one of: [{', '.join(fields.EfuseKeyPurposeField.DIGEST_KEY_PURPOSES)}]", ) + @click.argument( + "block_keyfile_keypurpose", + metavar=" ", + cls=TupleParameter, + required=True, + nargs=-1, + max_arity=len(blocks_for_keys), + type=NonCompositeTuple( + [ + click.Choice(blocks_for_keys), + click.File("rb"), + click.Choice(fields.EfuseKeyPurposeField.DIGEST_KEY_PURPOSES), + ] + ), + ) + @protect_options + @add_force_write_always + @add_show_sensitive_info_option + @click.pass_context + def burn_key_digest_cli(ctx, **kwargs): + """Parse a RSA public key and burn the digest to key eFuse block""" + kwargs.pop("force_write_always") + block, keyfile, keypurpose = zip(*kwargs.pop("block_keyfile_keypurpose")) + kwargs["show_sensitive_info"] = ctx.show_sensitive_info + self.burn_key_digest(block, keyfile, keypurpose, **kwargs) + + @cli.command( + "set-flash-voltage", + short_help="Permanently set the internal flash voltage regulator.", + ) + @click.argument("voltage", type=click.Choice(["1.8V", "3.3V", "OFF"])) + def set_flash_voltage_cli(voltage): + """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.""" + self.set_flash_voltage(voltage) - key_block_2 = get_next_key_block(efuses, block, block_name_list) - if not key_block_2: - raise esptool.FatalError("XTS_AES_256_KEY requires two free keyblocks") - - keypurpose_list.append("XTS_AES_256_KEY_1") - datafile_list.append(io.BytesIO(data[:32])) - block_name_list.append(block_name) + ###################################### Commands ###################################### - keypurpose_list.append("XTS_AES_256_KEY_2") - datafile_list.append(io.BytesIO(data[32:])) - block_name_list.append(key_block_2.name) + def set_flash_voltage(self, voltage: str): + sdio_force = self.efuses["VDD_SPI_FORCE"] + sdio_tieh = self.efuses["VDD_SPI_TIEH"] + sdio_reg = self.efuses["VDD_SPI_XPD"] - keypurpose_list.pop(i) - datafile_list.pop(i) - block_name_list.pop(i) + # check efuses aren't burned in a way which makes this impossible + if voltage == "OFF" and sdio_reg.get() != 0: + raise esptool.FatalError( + "Can't set flash regulator to OFF as VDD_SPI_XPD eFuse is already burned" + ) + if voltage == "1.8V" and sdio_tieh.get() != 0: + raise esptool.FatalError( + "Can't set regulator to 1.8V is VDD_SPI_TIEH eFuse is already burned" + ) -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]) : - ] - - if "XTS_AES_256_KEY" in keypurpose_list: - # XTS_AES_256_KEY is not an actual HW key purpose, needs to be split into - # XTS_AES_256_KEY_1 and XTS_AES_256_KEY_2 - split_512_bit_key(efuses, block_name_list, datafile_list, keypurpose_list) - - 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)) - ) + if voltage == "OFF": + log.print( + "Disable internal flash voltage regulator (VDD_SPI). " + "SPI flash will need to be powered from an external source.\n" + "The following eFuse is burned: VDD_SPI_FORCE.\n" + "It is possible to later re-enable the internal regulator" + f"{'to 3.3V' if sdio_tieh.get() != 0 else 'to 1.8V or 3.3V'}" + "by burning an additional eFuse" + ) + elif voltage == "1.8V": + log.print( + "Set internal flash voltage regulator (VDD_SPI) to 1.8V.\n" + "The following eFuses are burned: VDD_SPI_FORCE, VDD_SPI_XPD.\n" + "It is possible to later increase the voltage to 3.3V (permanently) " + "by burning additional eFuse VDD_SPI_TIEH" + ) + elif voltage == "3.3V": + log.print( + "Enable internal flash voltage regulator (VDD_SPI) to 3.3V.\n" + "The following eFuses are burned: VDD_SPI_FORCE, VDD_SPI_XPD, VDD_SPI_TIEH." + ) - print("Burn keys to blocks:") - for block_name, datafile, keypurpose in zip( - block_name_list, datafile_list, keypurpose_list + sdio_force.save(1) # Disable GPIO45 + if voltage != "OFF": + sdio_reg.save(1) # Enable internal regulator + if voltage == "3.3V": + sdio_tieh.save(1) + log.print("VDD_SPI setting complete.") + + if not self.efuses.burn_all(check_batch_mode=True): + return + log.print("Successful.") + + def adc_info(self): + log.print("") + log.print("Block version:", self.efuses.get_block_version()) + if self.efuses.get_block_version() >= 100: + # fmt: off + log.print(f"Temperature Sensor Calibration = {self.efuses['TEMP_CALIB'].get()}C") + log.print("ADC OCode = ", self.efuses["OCODE"].get()) + log.print("ADC1:") + log.print("INIT_CODE_ATTEN0 = ", self.efuses["ADC1_INIT_CODE_ATTEN0"].get()) + log.print("INIT_CODE_ATTEN1 = ", self.efuses["ADC1_INIT_CODE_ATTEN1"].get()) + log.print("INIT_CODE_ATTEN2 = ", self.efuses["ADC1_INIT_CODE_ATTEN2"].get()) + log.print("INIT_CODE_ATTEN3 = ", self.efuses["ADC1_INIT_CODE_ATTEN3"].get()) + log.print("CAL_VOL_ATTEN0 = ", self.efuses["ADC1_CAL_VOL_ATTEN0"].get()) + log.print("CAL_VOL_ATTEN1 = ", self.efuses["ADC1_CAL_VOL_ATTEN1"].get()) + log.print("CAL_VOL_ATTEN2 = ", self.efuses["ADC1_CAL_VOL_ATTEN2"].get()) + log.print("CAL_VOL_ATTEN3 = ", self.efuses["ADC1_CAL_VOL_ATTEN3"].get()) + log.print("ADC2:") + log.print("INIT_CODE_ATTEN0 = ", self.efuses["ADC2_INIT_CODE_ATTEN0"].get()) + log.print("INIT_CODE_ATTEN1 = ", self.efuses["ADC2_INIT_CODE_ATTEN1"].get()) + log.print("INIT_CODE_ATTEN2 = ", self.efuses["ADC2_INIT_CODE_ATTEN2"].get()) + log.print("INIT_CODE_ATTEN3 = ", self.efuses["ADC2_INIT_CODE_ATTEN3"].get()) + log.print("CAL_VOL_ATTEN0 = ", self.efuses["ADC2_CAL_VOL_ATTEN0"].get()) + log.print("CAL_VOL_ATTEN1 = ", self.efuses["ADC2_CAL_VOL_ATTEN1"].get()) + log.print("CAL_VOL_ATTEN2 = ", self.efuses["ADC2_CAL_VOL_ATTEN2"].get()) + # fmt: on + + def burn_key( + self, + blocks: list[str], + keyfiles: list[BinaryIO], + keypurposes: list[str], + no_write_protect: bool = False, + no_read_protect: bool = False, + show_sensitive_info: bool = False, + digest: list[bytes] | None = None, ): - 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] - + """Burn the key block with the specified name. Arguments are groups of block name, + key file (containing 256 bits of binary key data) and key purpose. + + Args: + blocks: List of eFuse block names to burn keys to. + keyfiles: List of open files to read key data from. + keypurposes: List of key purposes to burn. + no_write_protect: If True, the write protection will NOT be enabled. + no_read_protect: If True, the read protection will NOT be enabled. + show_sensitive_info: If True, the sensitive information will be shown. + digest: List of digests to burn. + """ + datafile_list: list[BinaryIO] | list[bytes] if digest is None: - data = datafile.read() + datafile_list = keyfiles[ + 0 : len([name for name in keyfiles if name is not None]) : + ] else: - data = datafile - - print(" - %s" % (efuse.name), end=" ") - revers_msg = None - if efuses[block.key_purpose_name].need_reverse(keypurpose): - revers_msg = "\tReversing byte order for AES-XTS hardware peripheral" - data = data[::-1] - print( - "-> [{}]".format( - util.hexify(data, " ") - if args.show_sensitive_info - else " ".join(["??"] * len(data)) + datafile_list = digest[ + 0 : len([name for name in digest if name is not None]) : + ] + block_name_list = blocks[ + 0 : len([name for name in blocks if name is not None]) : + ] + keypurpose_list = keypurposes[ + 0 : len([name for name in keypurposes if name is not None]) : + ] + + if "XTS_AES_256_KEY" in keypurpose_list: + # XTS_AES_256_KEY is not an actual HW key purpose, needs to be split into + # XTS_AES_256_KEY_1 and XTS_AES_256_KEY_2 + block_name_list, datafile_list, keypurpose_list = self._split_512_bit_key( + block_name_list, + datafile_list, # type: ignore + keypurpose_list, ) - ) - if revers_msg: - print(revers_msg) - if len(data) != num_bytes: + + 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( - "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) + "The number of blocks (%d), datafile (%d) and keypurpose (%d) should be the same." + % (len(block_name_list), len(datafile_list), len(keypurpose_list)) ) - 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, - ) + log.print("Burn keys to blocks:") + for block_name, datafile, keypurpose in zip( + block_name_list, datafile_list, keypurpose_list + ): + efuse = None + for blk in self.efuses.blocks: + if block_name == blk.name or block_name in blk.alias: + efuse = self.efuses[blk.name] + if efuse is None: + raise esptool.FatalError(f"Unknown block name - {block_name}.") + num_bytes = efuse.bit_len // 8 + + block_num = self.efuses.get_index_block_by_name(block_name) + block = self.efuses.blocks[block_num] + + if isinstance(datafile, io.IOBase): + data = datafile.read() + datafile.close() + else: + data = datafile # type: ignore # this is safe but mypy still complains + + log.print(f" - {efuse.name}", end=" ") + revers_msg = None + if self.efuses[block.key_purpose_name].need_reverse(keypurpose): + revers_msg = "\tReversing byte order for AES-XTS hardware peripheral..." + data = data[::-1] + log.print( + "-> [{}]".format( + util.hexify(data, " ") + if show_sensitive_info + else " ".join(["??"] * len(data)) ) - efuses[block.key_purpose_name].save(keypurpose) - disable_wr_protect_key_purpose = True + ) + if revers_msg: + log.print(revers_msg) + if len(data) != num_bytes: + raise esptool.FatalError( + f"Incorrect key file size {len(data)}. Key file must be {num_bytes} " + f"bytes ({num_bytes * 8} bits) of raw binary key data." + ) + + if self.efuses[block.key_purpose_name].need_rd_protect(keypurpose): + read_protect = False if no_read_protect else True + else: + read_protect = False + write_protect = not 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 self.efuses[block.key_purpose_name].get() != keypurpose: + if self.efuses[block.key_purpose_name].is_writeable(): + log.print( + f"\t'{block.key_purpose_name}': " + f"'{self.efuses[block.key_purpose_name].get()}' -> '{keypurpose}'." + ) + self.efuses[block.key_purpose_name].save(keypurpose) + disable_wr_protect_key_purpose = True + else: + raise esptool.FatalError( + f"It is not possible to change '{block.key_purpose_name}' " + f"to '{keypurpose}' because write protection bit is set." + ) else: + log.print(f"\t'{block.key_purpose_name}' is already '{keypurpose}'.") + if self.efuses[block.key_purpose_name].is_writeable(): + disable_wr_protect_key_purpose = True + + if disable_wr_protect_key_purpose: + log.print(f"\tDisabling write to '{block.key_purpose_name}'...") + self.efuses[block.key_purpose_name].disable_write() + + if read_protect: + log.print("\tDisabling read to key block...") + efuse.disable_read() + + if write_protect: + log.print("\tDisabling write to key block...") + efuse.disable_write() + log.print("") + + if not write_protect: + log.print("Keys will remain writeable (due to --no-write-protect).") + if no_read_protect: + log.print("Keys will remain readable (due to --no-read-protect).") + + if not self.efuses.burn_all(check_batch_mode=True): + return + log.print("Successful.") + + def burn_key_digest( + self, + blocks: list[str], + keyfiles: list[BinaryIO], + keypurposes: list[str], + no_write_protect: bool = False, + no_read_protect: bool = False, + show_sensitive_info: bool = False, + ): + """Parse a RSA public key and burn the digest to key eFuse block. + + Args: + blocks: List of eFuse block names to burn keys to. + keyfiles: List of open files to read key data from. + keypurposes: List of key purposes to burn. + no_write_protect: If True, the write protection will NOT be enabled. + no_read_protect: If True, the read protection will NOT be enabled. + show_sensitive_info: If True, the sensitive information will be shown. + """ + digest_list = [] + datafile_list = keyfiles[ + 0 : len([name for name in keyfiles if name is not None]) : + ] + block_list = blocks[0 : len([block for block in blocks if block is not None]) :] + + for block_name, datafile in zip(block_list, datafile_list): + efuse = None + for blk in self.efuses.blocks: + if block_name == blk.name or block_name in blk.alias: + efuse = self.efuses[blk.name] + if efuse is None: + raise esptool.FatalError(f"Unknown block name - {block_name}.") + num_bytes = efuse.bit_len // 8 + digest = espsecure._digest_sbv2_public_key(datafile) + if len(digest) != num_bytes: raise esptool.FatalError( - "It is not possible to change '%s' to '%s' because " - "write protection bit is set." - % (block.key_purpose_name, keypurpose) + "Incorrect digest size %d. Digest must be %d bytes (%d bits) " + "of raw binary key data." % (len(digest), num_bytes, num_bytes * 8) ) - 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") + digest_list.append(digest) + + self.burn_key( + block_list, + datafile_list, + keypurposes, + no_write_protect, + no_read_protect, + show_sensitive_info, + digest=digest_list, + ) diff --git a/tools/esptool_py/espefuse/efuse/esp32s3beta2/__init__.py b/tools/esptool_py/espefuse/efuse/esp32s3beta2/__init__.py deleted file mode 100644 index a3b55a8023..0000000000 --- a/tools/esptool_py/espefuse/efuse/esp32s3beta2/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -from . import operations -from .emulate_efuse_controller import EmulateEfuseController -from .fields import EspEfuses diff --git a/tools/esptool_py/espefuse/efuse/esp32s3beta2/emulate_efuse_controller.py b/tools/esptool_py/espefuse/efuse/esp32s3beta2/emulate_efuse_controller.py deleted file mode 100644 index 949fc562b9..0000000000 --- a/tools/esptool_py/espefuse/efuse/esp32s3beta2/emulate_efuse_controller.py +++ /dev/null @@ -1,92 +0,0 @@ -# This file describes eFuses controller for ESP32-S3(beta2) chip -# -# SPDX-FileCopyrightText: 2020-2022 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-S3(beta2)" - 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 2 - - def get_crystal_freq(self): - return 40 # MHz (common for all chips) - - 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 diff --git a/tools/esptool_py/espefuse/efuse/esp32s3beta2/fields.py b/tools/esptool_py/espefuse/efuse/esp32s3beta2/fields.py deleted file mode 100644 index 28e03e9cfe..0000000000 --- a/tools/esptool_py/espefuse/efuse/esp32s3beta2/fields.py +++ /dev/null @@ -1,489 +0,0 @@ -# This file describes eFuses for ESP32-S3 chip -# -# SPDX-FileCopyrightText: 2020-2022 Espressif Systems (Shanghai) CO LTD -# -# SPDX-License-Identifier: GPL-2.0-or-later - -import binascii -import struct -import sys -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-S3(beta2)": - raise esptool.FatalError( - "Expected the 'esp' param for ESP32-S3(beta2) 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_MAJOR"].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") - sys.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") - sys.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 != 40: - raise esptool.FatalError( - "The eFuse supports only xtal=40M (xtal was %d)" % apb_freq - ) - - self.update_reg(self.REGS.EFUSE_DAC_CONF_REG, self.REGS.EFUSE_DAC_NUM_M, 0xFF) - self.update_reg( - self.REGS.EFUSE_DAC_CONF_REG, self.REGS.EFUSE_DAC_CLK_DIV_M, 0x28 - ) - self.update_reg( - self.REGS.EFUSE_WR_TIM_CONF1_REG, self.REGS.EFUSE_PWR_ON_NUM_M, 0x3000 - ) - self.update_reg( - self.REGS.EFUSE_WR_TIM_CONF2_REG, self.REGS.EFUSE_PWR_OFF_NUM_M, 0x190 - ) - - 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): - if self["VDD_SPI_FORCE"].get() == 0: - output = "Flash voltage (VDD_SPI) determined by GPIO45 on reset " - output += "(GPIO45=High: VDD_SPI pin is powered from internal 1.8V LDO\n" - output += "GPIO45=Low or NC: VDD_SPI pin is powered directly from " - output += "VDD3P3_RTC_IO via resistor Rspi. " - output += "Typically this voltage is 3.3 V)." - elif self["VDD_SPI_XPD"].get() == 0: - output = "Flash voltage (VDD_SPI) internal regulator disabled by efuse." - elif self["VDD_SPI_TIEH"].get() == 0: - output = "Flash voltage (VDD_SPI) set to 1.8V by efuse." - else: - output = "Flash voltage (VDD_SPI) set to 3.3V by efuse." - return output - - def is_efuses_incompatible_for_burn(self): - # getting chip version: self._esp.get_chip_revision() - if ( - ( - self["DIS_USB_JTAG"].get(from_read=True) - and self["DIS_USB_SERIAL_JTAG"].get(from_read=False) - ) - or ( - self["DIS_USB_JTAG"].get(from_read=False) - and self["DIS_USB_SERIAL_JTAG"].get(from_read=True) - ) - or ( - self["DIS_USB_JTAG"].get(from_read=False) - and self["DIS_USB_SERIAL_JTAG"].get(from_read=False) - ) - ): - print( - "DIS_USB_JTAG and DIS_USB_SERIAL_JTAG cannot be set together due to a bug in the ROM bootloader" - ) - return True - return False - - -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): - hi_bits = self.parent["WAFER_VERSION_MINOR_HI"].get(from_read) - assert self.parent["WAFER_VERSION_MINOR_HI"].bit_len == 1 - lo_bits = self.parent["WAFER_VERSION_MINOR_LO"].get(from_read) - assert self.parent["WAFER_VERSION_MINOR_LO"].bit_len == 3 - return (hi_bits << 3) + lo_bits - - 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!" - ) - if new_value_str.count(":") != 5: - raise esptool.FatalError( - "MAC Address needs to be a 6-byte hexadecimal format " - "separated by colons (:)!" - ) - hexad = new_value_str.replace(":", "") - if len(hexad) != 12: - raise esptool.FatalError( - "MAC Address needs to be a 6-byte hexadecimal number " - "(12 hexadecimal characters)!" - ) - # order of bytearray = b'\xaa\xcd\xef\x01\x02\x03', - bindata = binascii.unhexlify(hexad) - # 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] - 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("Writing Factory MAC address is not supported") - - -# fmt: off -class EfuseKeyPurposeField(EfuseField): - KEY_PURPOSES = [ - ("USER", 0, None, None, "no_need_rd_protect"), # User purposes (software-only use) - ("RESERVED", 1, None, None, "no_need_rd_protect"), # Reserved - ("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) - ("XTS_AES_256_KEY", -1, "VIRTUAL", None, "no_need_rd_protect"), # Virtual purpose splits to XTS_AES_256_KEY_1 and XTS_AES_256_KEY_2 - ] -# 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))) - str_new_value = self.get_name(raw_val) - if self.name == "KEY_PURPOSE_5" and str_new_value.startswith("XTS_AES"): - raise esptool.FatalError(f"{self.name} can not have {str_new_value} key due to a hardware bug (please see TRM for more details)") - return super(EfuseKeyPurposeField, self).save(raw_val) diff --git a/tools/esptool_py/espefuse/efuse/esp32s3beta2/operations.py b/tools/esptool_py/espefuse/efuse/esp32s3beta2/operations.py deleted file mode 100644 index 229a929023..0000000000 --- a/tools/esptool_py/espefuse/efuse/esp32s3beta2/operations.py +++ /dev/null @@ -1,521 +0,0 @@ -# This file includes the operations with eFuses for ESP32-S3(beta2) chip -# -# SPDX-FileCopyrightText: 2020-2022 Espressif Systems (Shanghai) CO LTD -# -# SPDX-License-Identifier: GPL-2.0-or-later - -import argparse -import io -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, - add_show_sensitive_info_option, - 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) - add_show_sensitive_info_option(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", - 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", - 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) - add_show_sensitive_info_option(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): - sdio_force = efuses["VDD_SPI_FORCE"] - sdio_tieh = efuses["VDD_SPI_TIEH"] - sdio_reg = efuses["VDD_SPI_XPD"] - - # check efuses aren't burned in a way which makes this impossible - if args.voltage == "OFF" and sdio_reg.get() != 0: - raise esptool.FatalError( - "Can't set flash regulator to OFF as VDD_SPI_XPD efuse is already burned" - ) - - if args.voltage == "1.8V" and sdio_tieh.get() != 0: - raise esptool.FatalError( - "Can't set regulator to 1.8V is VDD_SPI_TIEH efuse is already burned" - ) - - if args.voltage == "OFF": - msg = "Disable internal flash voltage regulator (VDD_SPI). " - "SPI flash will need to be powered from an external source.\n" - "The following efuse is burned: VDD_SPI_FORCE.\n" - "It is possible to later re-enable the internal regulator (%s) " % ( - "to 3.3V" if sdio_tieh.get() != 0 else "to 1.8V or 3.3V" - ) - "by burning an additional efuse" - elif args.voltage == "1.8V": - msg = "Set internal flash voltage regulator (VDD_SPI) to 1.8V.\n" - "The following efuses are burned: VDD_SPI_FORCE, VDD_SPI_XPD.\n" - "It is possible to later increase the voltage to 3.3V (permanently) " - "by burning additional efuse VDD_SPI_TIEH" - elif args.voltage == "3.3V": - msg = "Enable internal flash voltage regulator (VDD_SPI) to 3.3V.\n" - "The following efuses are burned: VDD_SPI_FORCE, VDD_SPI_XPD, VDD_SPI_TIEH." - print(msg) - - sdio_force.save(1) # Disable GPIO45 - if args.voltage != "OFF": - sdio_reg.save(1) # Enable internal regulator - if args.voltage == "3.3V": - sdio_tieh.save(1) - print("VDD_SPI setting complete.") - if not efuses.burn_all(check_batch_mode=True): - return - print("Successful") - - -def adc_info(esp, efuses, args): - print("") - # fmt: off - if efuses["BLK_VERSION_MAJOR"].get() == 1: - print("Temperature Sensor Calibration = {}C".format(efuses["TEMP_CALIB"].get())) - print("ADC OCode = ", efuses["OCODE"].get()) - print("ADC1:") - print("INIT_CODE_ATTEN0 = ", efuses["ADC1_INIT_CODE_ATTEN0"].get()) - print("INIT_CODE_ATTEN1 = ", efuses["ADC1_INIT_CODE_ATTEN1"].get()) - print("INIT_CODE_ATTEN2 = ", efuses["ADC1_INIT_CODE_ATTEN2"].get()) - print("INIT_CODE_ATTEN3 = ", efuses["ADC1_INIT_CODE_ATTEN3"].get()) - print("CAL_VOL_ATTEN0 = ", efuses["ADC1_CAL_VOL_ATTEN0"].get()) - print("CAL_VOL_ATTEN1 = ", efuses["ADC1_CAL_VOL_ATTEN1"].get()) - print("CAL_VOL_ATTEN2 = ", efuses["ADC1_CAL_VOL_ATTEN2"].get()) - print("CAL_VOL_ATTEN3 = ", efuses["ADC1_CAL_VOL_ATTEN3"].get()) - print("ADC2:") - print("INIT_CODE_ATTEN0 = ", efuses["ADC2_INIT_CODE_ATTEN0"].get()) - print("INIT_CODE_ATTEN1 = ", efuses["ADC2_INIT_CODE_ATTEN1"].get()) - print("INIT_CODE_ATTEN2 = ", efuses["ADC2_INIT_CODE_ATTEN2"].get()) - print("INIT_CODE_ATTEN3 = ", efuses["ADC2_INIT_CODE_ATTEN3"].get()) - print("CAL_VOL_ATTEN0 = ", efuses["ADC2_CAL_VOL_ATTEN0"].get()) - print("CAL_VOL_ATTEN1 = ", efuses["ADC2_CAL_VOL_ATTEN1"].get()) - print("CAL_VOL_ATTEN2 = ", efuses["ADC2_CAL_VOL_ATTEN2"].get()) - else: - print("BLK_VERSION_MAJOR = ", efuses["BLK_VERSION_MAJOR"].get_meaning()) - # fmt: on - - -def key_block_is_unused(block, key_purpose_block): - if not block.is_readable() or not block.is_writeable(): - return False - - if key_purpose_block.get() != "USER" or not key_purpose_block.is_writeable(): - return False - - if not block.get_bitstring().all(False): - return False - - return True - - -def get_next_key_block(efuses, current_key_block, block_name_list): - key_blocks = [b for b in efuses.blocks if b.key_purpose_name] - start = key_blocks.index(current_key_block) - - # Sort key blocks so that we pick the next free block (and loop around if necessary) - key_blocks = key_blocks[start:] + key_blocks[0:start] - - # Exclude any other blocks that will be be burned - key_blocks = [b for b in key_blocks if b.name not in block_name_list] - - for block in key_blocks: - key_purpose_block = efuses[block.key_purpose_name] - if key_block_is_unused(block, key_purpose_block): - return block - - return None - - -def split_512_bit_key(efuses, block_name_list, datafile_list, keypurpose_list): - i = keypurpose_list.index("XTS_AES_256_KEY") - block_name = block_name_list[i] - - block_num = efuses.get_index_block_by_name(block_name) - block = efuses.blocks[block_num] - - data = datafile_list[i].read() - if len(data) != 64: - raise esptool.FatalError( - "Incorrect key file size %d, XTS_AES_256_KEY should be 64 bytes" % len(data) - ) - - key_block_2 = get_next_key_block(efuses, block, block_name_list) - if not key_block_2: - raise esptool.FatalError("XTS_AES_256_KEY requires two free keyblocks") - - keypurpose_list.append("XTS_AES_256_KEY_1") - datafile_list.append(io.BytesIO(data[:32])) - block_name_list.append(block_name) - - keypurpose_list.append("XTS_AES_256_KEY_2") - datafile_list.append(io.BytesIO(data[32:])) - block_name_list.append(key_block_2.name) - - keypurpose_list.pop(i) - datafile_list.pop(i) - block_name_list.pop(i) - - -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]) : - ] - - if "XTS_AES_256_KEY" in keypurpose_list: - # XTS_AES_256_KEY is not an actual HW key purpose, needs to be split into - # XTS_AES_256_KEY_1 and XTS_AES_256_KEY_2 - split_512_bit_key(efuses, block_name_list, datafile_list, keypurpose_list) - - 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: - 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 = "\tReversing byte order for AES-XTS hardware peripheral" - data = data[::-1] - print( - "-> [{}]".format( - util.hexify(data, " ") - if args.show_sensitive_info - else " ".join(["??"] * len(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") diff --git a/tools/esptool_py/espefuse/efuse/mem_definition_base.py b/tools/esptool_py/espefuse/efuse/mem_definition_base.py index f6e2bdc0b2..07f58df2fe 100644 --- a/tools/esptool_py/espefuse/efuse/mem_definition_base.py +++ b/tools/esptool_py/espefuse/efuse/mem_definition_base.py @@ -6,7 +6,7 @@ from collections import Counter, namedtuple import esptool -from typing import Optional, List +from esptool.logger import log from .csv_table_parser import CSVFuseTable @@ -23,7 +23,7 @@ class EfuseRegistersBase(object): class EfuseBlocksBase(object): - BLOCKS: Optional[List] = None + BLOCKS: list = [] NamedtupleBlock = namedtuple( "NamedtupleBlock", "name alias id rd_addr wr_addr write_disable_bit " @@ -53,10 +53,10 @@ class Field: word = None pos = None bit_len = 0 - alt_names: List[str] = [] + alt_names: list[str] = [] type = "" write_disable_bit = None - read_disable_bit = None + read_disable_bit: list[int] | None = None category = "config" class_type = "" description = "" @@ -65,7 +65,7 @@ class Field: class EfuseFieldsBase(object): def __init__(self, e_desc, extend_efuse_table_file) -> None: - self.ALL_EFUSES: List = [] + self.ALL_EFUSES: list = [] def set_category_and_class_type(efuse, name): def includes(name, names): @@ -140,7 +140,20 @@ def includes(name, names): if name == "OPTIONAL_UNIQUE_ID": efuse.class_type = "keyblock" - elif includes(name, ["ADC", "LDO", "DBIAS", "_HVT", "CALIB", "OCODE"]): + elif includes( + name, + [ + "ADC", + "LDO", + "DBIAS", + "_HVT", + "CALIB", + "OCODE", + "TEMPERATURE", + "LSLP", + "DSLP", + ], + ): efuse.category = "calibration" if name == "ADC_VREF": efuse.class_type = "vref" @@ -186,7 +199,7 @@ def check_name_duplicates(self): name_counts = Counter(names) duplicates = {name for name, count in name_counts.items() if count > 1} if duplicates: - print("Names that are not unique: " + ", ".join(duplicates)) + log.print("Names that are not unique: " + ", ".join(duplicates)) raise esptool.FatalError("Duplicate names found in eFuses") def extend_efuses(self, extend_efuse_table_file): diff --git a/tools/esptool_py/espefuse/efuse/util.py b/tools/esptool_py/espefuse/efuse/util.py index 004e03b295..cb9547022a 100644 --- a/tools/esptool_py/espefuse/efuse/util.py +++ b/tools/esptool_py/espefuse/efuse/util.py @@ -20,9 +20,7 @@ def popcnt(b): def check_duplicate_name_in_list(name_list): duples_name = [name for i, name in enumerate(name_list) if name in name_list[:i]] if duples_name != []: - raise esptool.FatalError( - "Found repeated {} in the name list".format(duples_name) - ) + raise esptool.FatalError(f"Found repeated {duples_name} in the name list.") class SdkConfig(object): diff --git a/tools/esptool_py/espefuse/efuse_defs/esp32c5.yaml b/tools/esptool_py/espefuse/efuse_defs/esp32c5.yaml index 31af46a56d..2200b94b8e 100644 --- a/tools/esptool_py/espefuse/efuse_defs/esp32c5.yaml +++ b/tools/esptool_py/espefuse/efuse_defs/esp32c5.yaml @@ -1,104 +1,123 @@ -VER_NO: b09fa417de505238a601eddce188b696 +VER_NO: 31c7fe3f5f4e0a55b178a57126c0aca7 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_DIS0_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]'} - RESERVE_0_39 : {show: n, blk : 0, word: 1, pos : 7, len : 1, start : 39, type : bool, wr_dis: null, rd_dis: null, alt : '', dict : '', desc: Reserved; it was created by set_missed_fields_in_regs func, rloc: 'EFUSE_RD_REPEAT_DATA0_REG[7]', bloc: 'B4[7]'} - DIS_ICACHE : {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 icache is disabled or enabled.\\ 1: disabled\\ 0: enabled\\', rloc: 'EFUSE_RD_REPEAT_DATA0_REG[8]', bloc: 'B5[0]'} - DIS_USB_JTAG : {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 of usb switch to jtag is disabled or enabled.\\ 1: disabled\\ 0: enabled\\', rloc: 'EFUSE_RD_REPEAT_DATA0_REG[9]', bloc: 'B5[1]'} - RESERVE_0_42 : {show: n, blk : 0, word: 1, pos: 10, len : 1, start : 42, type : bool, wr_dis: null, rd_dis: null, alt : '', dict : '', desc: Reserved; it was created by set_missed_fields_in_regs func, rloc: 'EFUSE_RD_REPEAT_DATA0_REG[10]', bloc: 'B5[2]'} - DIS_USB_SERIAL_JTAG : {show: n, blk : 0, word: 1, pos: 11, len : 1, start : 43, 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[11]', bloc: 'B5[3]'} - DIS_FORCE_DOWNLOAD : {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 function that forces chip into download mode is disabled or enabled.\\ 1: disabled\\ 0: enabled\\', rloc: 'EFUSE_RD_REPEAT_DATA0_REG[12]', bloc: 'B5[4]'} - SPI_DOWNLOAD_MSPI_DIS : {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 SPI0 controller during boot_mode_download is disabled or enabled.\\ 1: disabled\\ 0: enabled\\', rloc: 'EFUSE_RD_REPEAT_DATA0_REG[13]', bloc: 'B5[5]'} - DIS_TWAI : {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 TWAI function is disabled or enabled.\\ 1: disabled\\ 0: enabled\\', rloc: 'EFUSE_RD_REPEAT_DATA0_REG[14]', bloc: 'B5[6]'} - JTAG_SEL_ENABLE : {show: y, blk : 0, word: 1, pos: 15, len : 1, start : 47, 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[15]', bloc: 'B5[7]'} - SOFT_DIS_JTAG : {show: y, blk : 0, word: 1, pos: 16, len : 3, start : 48, type : 'uint:3', wr_dis : 31, rd_dis: null, alt : '', dict : '', desc: 'Represents whether JTAG is disabled in soft way.\\ Odd number: disabled\\ Even number: enabled\\', rloc: 'EFUSE_RD_REPEAT_DATA0_REG[18:16]', bloc: 'B6[2:0]'} - DIS_PAD_JTAG : {show: y, blk : 0, word: 1, pos: 19, len : 1, start : 51, 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[19]', bloc: 'B6[3]'} - DIS_DOWNLOAD_MANUAL_ENCRYPT : {show: y, blk : 0, word: 1, pos: 20, len : 1, start : 52, 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[20]', bloc: 'B6[4]'} - USB_DREFH : {show: n, blk : 0, word: 1, pos: 21, len : 2, start : 53, type : 'uint:2', wr_dis : 30, rd_dis: null, alt : '', dict : '', desc: Represents the single-end input threshold vrefh; 1.76 V to 2 V with step of 80 mV, rloc: 'EFUSE_RD_REPEAT_DATA0_REG[22:21]', bloc: 'B6[6:5]'} - USB_DREFL : {show: n, blk : 0, word: 1, pos: 23, len : 2, start : 55, type : 'uint:2', wr_dis : 30, rd_dis: null, alt : '', dict : '', desc: Represents the single-end input threshold vrefl; 1.76 V to 2 V with step of 80 mV, rloc: 'EFUSE_RD_REPEAT_DATA0_REG[24:23]', bloc: 'B6[7],B7[0]'} - USB_EXCHG_PINS : {show: y, blk : 0, word: 1, pos: 25, len : 1, start : 57, type : bool, wr_dis : 30, rd_dis: null, alt : '', dict : '', desc: 'Represents whether the D+ and D- pins is exchanged.\\ 1: exchanged\\ 0: not exchanged\\', rloc: 'EFUSE_RD_REPEAT_DATA0_REG[25]', bloc: 'B7[1]'} - VDD_SPI_AS_GPIO : {show: y, blk : 0, word: 1, pos: 26, len : 1, start : 58, type : bool, wr_dis : 30, rd_dis: null, alt : '', dict : '', desc: 'Represents whether vdd spi pin is functioned as gpio.\\ 1: functioned\\ 0: not functioned\\', rloc: 'EFUSE_RD_REPEAT_DATA0_REG[26]', bloc: 'B7[2]'} - RESERVE_0_59 : {show: n, blk : 0, word: 1, pos: 27, len : 5, start : 59, type : 'uint:5', wr_dis: null, rd_dis: null, alt : '', dict : '', desc: Reserved; it was created by set_missed_fields_in_regs func, rloc: 'EFUSE_RD_REPEAT_DATA0_REG[31:27]', bloc: 'B7[7:3]'} - KM_DISABLE_DEPLOY_MODE : {show: y, blk : 0, word: 2, pos : 0, len : 4, start : 64, type : 'uint:4', wr_dis : 1, rd_dis: null, alt : '', dict : '', desc: 'Represents whether the deploy mode of key manager is disable or not. \\ 1: disabled \\ 0: enabled.\\', rloc: 'EFUSE_RD_REPEAT_DATA1_REG[3:0]', bloc: 'B8[3:0]'} - KM_RND_SWITCH_CYCLE : {show: y, blk : 0, word: 2, pos : 4, len : 2, start : 68, type : 'uint:2', wr_dis : 1, rd_dis: null, alt : '', dict : '', desc: 'Set the bits to control key manager random number switch cycle. 0: control by register. 1: 8 km clk cycles. 2: 16 km cycles. 3: 32 km cycles', rloc: 'EFUSE_RD_REPEAT_DATA1_REG[5:4]', bloc: 'B8[5:4]'} - KM_DEPLOY_ONLY_ONCE : {show: y, blk : 0, word: 2, pos : 6, len : 4, start : 70, type : 'uint:4', wr_dis : 1, rd_dis: null, alt : '', dict : '', desc: 'Set each bit to control whether corresponding key can only be deployed once. 1 is true; 0 is false. bit 0: ecsda; bit 1: xts; bit2: hmac; bit3: ds', rloc: 'EFUSE_RD_REPEAT_DATA1_REG[9:6]', bloc: 'B8[7:6],B9[1:0]'} - FORCE_USE_KEY_MANAGER_KEY : {show: y, blk : 0, word: 2, pos: 10, len : 4, start : 74, type : 'uint:4', wr_dis : 1, rd_dis: null, alt : '', dict : '', desc: 'Set each bit to control whether corresponding key must come from key manager. 1 is true; 0 is false. bit 0: ecsda; bit 1: xts; bit2: hmac; bit3: ds', rloc: 'EFUSE_RD_REPEAT_DATA1_REG[13:10]', bloc: 'B9[5:2]'} - FORCE_DISABLE_SW_INIT_KEY : {show: y, blk : 0, word: 2, pos: 14, len : 1, start : 78, type : bool, wr_dis : 1, rd_dis: null, alt : '', dict : '', desc: Set this bit to disable software written init key; and force use efuse_init_key, rloc: 'EFUSE_RD_REPEAT_DATA1_REG[14]', bloc: 'B9[6]'} - RESERVE_0_79 : {show: n, blk : 0, word: 2, pos: 15, len : 1, start : 79, type : bool, wr_dis: null, rd_dis: null, alt : '', dict : '', desc: Reserved; it was created by set_missed_fields_in_regs func, rloc: 'EFUSE_RD_REPEAT_DATA1_REG[15]', bloc: 'B9[7]'} - WDT_DELAY_SEL : {show: y, blk : 0, word: 2, pos: 16, len : 2, start : 80, type : 'uint:2', wr_dis : 3, rd_dis: null, alt : '', dict : '', desc: 'Represents the threshold level of the RTC watchdog STG0 timeout.\\ 0: Original threshold configuration value of STG0 *2 \\1: Original threshold configuration value of STG0 *4 \\2: Original threshold configuration value of STG0 *8 \\3: Original threshold configuration value of STG0 *16 \\', rloc: 'EFUSE_RD_REPEAT_DATA1_REG[17:16]', bloc: 'B10[1:0]'} - SPI_BOOT_CRYPT_CNT : {show: y, blk : 0, word: 2, pos: 18, len : 3, start : 82, 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_DATA1_REG[20:18]', bloc: 'B10[4:2]'} - SECURE_BOOT_KEY_REVOKE0 : {show: y, blk : 0, word: 2, pos: 21, len : 1, start : 85, type : bool, wr_dis : 5, rd_dis: null, alt : '', dict : '', desc: Revoke 1st secure boot key, rloc: 'EFUSE_RD_REPEAT_DATA1_REG[21]', bloc: 'B10[5]'} - SECURE_BOOT_KEY_REVOKE1 : {show: y, blk : 0, word: 2, pos: 22, len : 1, start : 86, type : bool, wr_dis : 6, rd_dis: null, alt : '', dict : '', desc: Revoke 2nd secure boot key, rloc: 'EFUSE_RD_REPEAT_DATA1_REG[22]', bloc: 'B10[6]'} - SECURE_BOOT_KEY_REVOKE2 : {show: y, blk : 0, word: 2, pos: 23, len : 1, start : 87, type : bool, wr_dis : 7, rd_dis: null, alt : '', dict : '', desc: Revoke 3rd secure boot key, rloc: 'EFUSE_RD_REPEAT_DATA1_REG[23]', bloc: 'B10[7]'} - KEY_PURPOSE_0 : {show: y, blk : 0, word: 2, pos: 24, len : 4, start : 88, type : 'uint:4', wr_dis : 8, rd_dis: null, alt : KEY0_PURPOSE, dict : '', desc: Represents the purpose of Key0, rloc: 'EFUSE_RD_REPEAT_DATA1_REG[27:24]', bloc: 'B11[3:0]'} - KEY_PURPOSE_1 : {show: y, blk : 0, word: 2, pos: 28, len : 4, start : 92, type : 'uint:4', wr_dis : 9, rd_dis: null, alt : KEY1_PURPOSE, dict : '', desc: Represents the purpose of Key1, rloc: 'EFUSE_RD_REPEAT_DATA1_REG[31:28]', bloc: 'B11[7:4]'} - KEY_PURPOSE_2 : {show: y, blk : 0, word: 3, pos : 0, len : 4, start : 96, type : 'uint:4', wr_dis : 10, rd_dis: null, alt : KEY2_PURPOSE, dict : '', desc: Represents the purpose of Key2, rloc: 'EFUSE_RD_REPEAT_DATA2_REG[3:0]', bloc: 'B12[3:0]'} - KEY_PURPOSE_3 : {show: y, blk : 0, word: 3, pos : 4, len : 4, start: 100, type : 'uint:4', wr_dis : 11, rd_dis: null, alt : KEY3_PURPOSE, dict : '', desc: Represents the purpose of Key3, rloc: 'EFUSE_RD_REPEAT_DATA2_REG[7:4]', bloc: 'B12[7:4]'} - KEY_PURPOSE_4 : {show: y, blk : 0, word: 3, pos : 8, len : 4, start: 104, type : 'uint:4', wr_dis : 12, rd_dis: null, alt : KEY4_PURPOSE, dict : '', desc: Represents the purpose of Key4, rloc: 'EFUSE_RD_REPEAT_DATA2_REG[11:8]', bloc: 'B13[3:0]'} - KEY_PURPOSE_5 : {show: y, blk : 0, word: 3, pos: 12, len : 4, start: 108, type : 'uint:4', wr_dis : 13, rd_dis: null, alt : KEY5_PURPOSE, dict : '', desc: Represents the purpose of Key5, rloc: 'EFUSE_RD_REPEAT_DATA2_REG[15:12]', bloc: 'B13[7:4]'} - SEC_DPA_LEVEL : {show: y, blk : 0, word: 3, pos: 16, len : 2, start: 112, type : 'uint:2', wr_dis : 14, rd_dis: null, alt : '', dict : '', desc: Represents the spa secure level by configuring the clock random divide mode, rloc: 'EFUSE_RD_REPEAT_DATA2_REG[17:16]', bloc: 'B14[1:0]'} - RESERVE_0_114 : {show: n, blk : 0, word: 3, pos: 18, len : 2, start: 114, type : 'uint:2', wr_dis: null, rd_dis: null, alt : '', dict : '', desc: Reserved; it was created by set_missed_fields_in_regs func, rloc: 'EFUSE_RD_REPEAT_DATA2_REG[19:18]', bloc: 'B14[3:2]'} - SECURE_BOOT_EN : {show: y, blk : 0, word: 3, pos: 20, len : 1, start: 116, 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[20]', bloc: 'B14[4]'} - SECURE_BOOT_AGGRESSIVE_REVOKE : {show: y, blk : 0, word: 3, pos: 21, len : 1, start: 117, 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[21]', bloc: 'B14[5]'} - RESERVE_0_118 : {show: n, blk : 0, word: 3, pos: 22, len : 5, start: 118, type : 'uint:5', wr_dis: null, rd_dis: null, alt : '', dict : '', desc: Reserved; it was created by set_missed_fields_in_regs func, rloc: 'EFUSE_RD_REPEAT_DATA2_REG[26:22]', bloc: 'B14[7:6],B15[2:0]'} - KM_XTS_KEY_LENGTH_256 : {show: y, blk : 0, word: 3, pos: 27, len : 1, start: 123, type : bool, wr_dis : 1, rd_dis: null, alt : '', dict : '', desc: Set this bitto configure flash encryption use xts-128 key. else use xts-256 key, rloc: 'EFUSE_RD_REPEAT_DATA2_REG[27]', bloc: 'B15[3]'} - FLASH_TPUW : {show: y, blk : 0, word: 3, pos: 28, len : 4, start: 124, type : 'uint:4', 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[31:28]', bloc: 'B15[7:4]'} - DIS_DOWNLOAD_MODE : {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 Download mode is disabled or enabled.\\ 1: disabled\\ 0: enabled\\', rloc: 'EFUSE_RD_REPEAT_DATA3_REG[0]', bloc: 'B16[0]'} - DIS_DIRECT_BOOT : {show: y, blk : 0, word: 4, pos : 1, len : 1, start: 129, 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[1]', bloc: 'B16[1]'} - DIS_USB_SERIAL_JTAG_ROM_PRINT : {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 print from USB-Serial-JTAG is disabled or enabled.\\ 1: disabled\\ 0: enabled\\', rloc: 'EFUSE_RD_REPEAT_DATA3_REG[2]', bloc: 'B16[2]'} - LOCK_KM_KEY : {show: y, blk : 0, word: 4, pos : 3, len : 1, start: 131, type : bool, wr_dis : 1, rd_dis: null, alt : '', dict : '', desc: 'Represetns whether to lock the efuse xts key.\\ 1. Lock\\ 0: Unlock\\', rloc: 'EFUSE_RD_REPEAT_DATA3_REG[3]', bloc: 'B16[3]'} - DIS_USB_SERIAL_JTAG_DOWNLOAD_MODE: {show: y, blk : 0, word: 4, pos : 4, len : 1, start: 132, type : bool, wr_dis : 18, rd_dis: null, alt : '', dict : '', desc: 'Represents whether the USB-Serial-JTAG download function is disabled or enabled.\\ 1: Disable\\ 0: Enable\\', rloc: 'EFUSE_RD_REPEAT_DATA3_REG[4]', bloc: 'B16[4]'} - ENABLE_SECURITY_DOWNLOAD : {show: y, blk : 0, word: 4, pos : 5, len : 1, start: 133, 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[5]', bloc: 'B16[5]'} - UART_PRINT_CONTROL : {show: y, blk : 0, word: 4, pos : 6, len : 2, start: 134, 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[7:6]', bloc: 'B16[7:6]'} - FORCE_SEND_RESUME : {show: y, blk : 0, word: 4, pos : 8, len : 1, start: 136, 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[8]', bloc: 'B17[0]'} - SECURE_VERSION : {show: y, blk : 0, word: 4, pos : 9, len : 16, start: 137, 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[24:9]', bloc: 'B17[7:1],B18,B19[0]'} - SECURE_BOOT_DISABLE_FAST_WAKE : {show: y, blk : 0, word: 4, pos: 25, len : 1, start: 153, type : bool, wr_dis : 18, rd_dis: null, alt : '', dict : '', desc: 'Represents whether FAST VERIFY ON WAKE is disabled or enabled when Secure Boot is enabled.\\ 1: disabled\\ 0: enabled\\', rloc: 'EFUSE_RD_REPEAT_DATA3_REG[25]', bloc: 'B19[1]'} - HYS_EN_PAD : {show: y, blk : 0, word: 4, pos: 26, len : 1, start: 154, type : bool, wr_dis : 2, rd_dis: null, alt : '', dict : '', desc: 'Represents whether the hysteresis function of corresponding PAD is enabled.\\ 1: enabled\\ 0:disabled\\', rloc: 'EFUSE_RD_REPEAT_DATA3_REG[26]', bloc: 'B19[2]'} - XTS_DPA_PSEUDO_LEVEL : {show: y, blk : 0, word: 4, pos: 27, len : 2, start: 155, type : 'uint:2', wr_dis : 14, rd_dis: null, alt : '', dict : '', desc: 'Represents the pseudo round level of xts-aes anti-dpa attack.\\ 3: High.\\ 2: Moderate 1. Low\\ 0: Disabled\\', rloc: 'EFUSE_RD_REPEAT_DATA3_REG[28:27]', bloc: 'B19[4:3]'} - XTS_DPA_CLK_ENABLE : {show: y, blk : 0, word: 4, pos: 29, len : 1, start: 157, type : bool, wr_dis : 14, rd_dis: null, alt : '', dict : '', desc: 'Represents whether xts-aes anti-dpa attack clock is enabled.\\ 1. Enable.\\ 0: Disable.\\', rloc: 'EFUSE_RD_REPEAT_DATA3_REG[29]', bloc: 'B19[5]'} - RESERVE_0_158 : {show: n, blk : 0, word: 4, pos: 30, len : 2, start: 158, type : 'uint:2', wr_dis: null, rd_dis: null, alt : '', dict : '', desc: Reserved; it was created by set_missed_fields_in_regs func, rloc: 'EFUSE_RD_REPEAT_DATA3_REG[31:30]', bloc: 'B19[7:6]'} - HUK_GEN_STATE : {show: y, blk : 0, word: 5, pos : 0, len : 9, start: 160, type : 'uint:9', wr_dis : 19, rd_dis: null, alt : '', dict : '', desc: Set the bits to control validation of HUK generate mode.\\ Odd of 1 is invalid.\\ Even of 1 is valid.\\, rloc: 'EFUSE_RD_REPEAT_DATA4_REG[8:0]', bloc: 'B20,B21[0]'} - XTAL_48M_SEL : {show: y, blk : 0, word: 5, pos : 9, len : 3, start: 169, type : 'uint:3', wr_dis : 17, rd_dis: null, alt : '', dict : '', desc: 'Represents whether XTAL frequency is 48MHz or not. If not; 40MHz XTAL will be used. If this field contains Odd number bit 1: Enable 48MHz XTAL\ Even number bit 1: Enable 40MHz XTAL', rloc: 'EFUSE_RD_REPEAT_DATA4_REG[11:9]', bloc: 'B21[3:1]'} - XTAL_48M_SEL_MODE : {show: y, blk : 0, word: 5, pos: 12, len : 1, start: 172, type : bool, wr_dis : 17, rd_dis: null, alt : '', dict : '', desc: 'Specify the XTAL frequency selection is decided by eFuse or strapping-PAD-state. 1: eFuse\\ 0: strapping-PAD-state', rloc: 'EFUSE_RD_REPEAT_DATA4_REG[12]', bloc: 'B21[4]'} - ECDSA_DISABLE_P192 : {show: y, blk : 0, word: 5, pos: 13, len : 1, start: 173, type : bool, wr_dis : 14, rd_dis: null, alt : '', dict : '', desc: 'Represents whether to disable P192 curve in ECDSA.\\ 1: Disabled.\\ 0: Not disable', rloc: 'EFUSE_RD_REPEAT_DATA4_REG[13]', bloc: 'B21[5]'} - ECC_FORCE_CONST_TIME : {show: y, blk : 0, word: 5, pos: 14, len : 1, start: 174, type : bool, wr_dis : 14, rd_dis: null, alt : '', dict : '', desc: 'Represents whether to force ecc to use const-time calculation mode. \\ 1: Enable. \\ 0: Disable', rloc: 'EFUSE_RD_REPEAT_DATA4_REG[14]', bloc: 'B21[6]'} - RESERVE_0_175 : {show: n, blk : 0, word: 5, pos: 15, len : 17, start: 175, type : 'uint:17', wr_dis: null, rd_dis: null, alt : '', dict : '', desc: Reserved; it was created by set_missed_fields_in_regs func, rloc: 'EFUSE_RD_REPEAT_DATA4_REG[31:15]', bloc: 'B21[7],B22,B23'} - 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_SYS0_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: Represents the extended bits of MAC address, rloc: 'EFUSE_RD_MAC_SYS1_REG[31:16]', bloc: 'B6,B7'} - WAFER_VERSION_MINOR : {show: y, blk : 1, word: 2, pos : 0, len : 4, start : 64, type : 'uint:4', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: Minor chip version, rloc: 'EFUSE_RD_MAC_SYS2_REG[3:0]', bloc: 'B8[3:0]'} - WAFER_VERSION_MAJOR : {show: y, blk : 1, word: 2, pos : 4, len : 2, start : 68, type : 'uint:2', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: Minor chip version, rloc: 'EFUSE_RD_MAC_SYS2_REG[5:4]', bloc: 'B8[5:4]'} - DISABLE_WAFER_VERSION_MAJOR : {show: y, blk : 1, word: 2, pos : 6, len : 1, start : 70, type : bool, wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: Disables check of wafer version major, rloc: 'EFUSE_RD_MAC_SYS2_REG[6]', bloc: 'B8[6]'} - DISABLE_BLK_VERSION_MAJOR : {show: y, blk : 1, word: 2, pos : 7, len : 1, start : 71, type : bool, wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: Disables check of blk version major, rloc: 'EFUSE_RD_MAC_SYS2_REG[7]', bloc: 'B8[7]'} - BLK_VERSION_MINOR : {show: y, blk : 1, word: 2, pos : 8, len : 3, start : 72, type : 'uint:3', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: BLK_VERSION_MINOR of BLOCK2, rloc: 'EFUSE_RD_MAC_SYS2_REG[10:8]', bloc: 'B9[2:0]'} - BLK_VERSION_MAJOR : {show: y, blk : 1, word: 2, pos: 11, len : 2, start : 75, type : 'uint:2', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: BLK_VERSION_MAJOR of BLOCK2, rloc: 'EFUSE_RD_MAC_SYS2_REG[12:11]', bloc: 'B9[4:3]'} - FLASH_CAP : {show: y, blk : 1, word: 2, pos: 13, len : 3, start : 77, type : 'uint:3', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: Flash capacity, rloc: 'EFUSE_RD_MAC_SYS2_REG[15:13]', bloc: 'B9[7:5]'} - FLASH_VENDOR : {show: y, blk : 1, word: 2, pos: 16, len : 3, start : 80, type : 'uint:3', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: Flash vendor, rloc: 'EFUSE_RD_MAC_SYS2_REG[18:16]', bloc: 'B10[2:0]'} - PSRAM_CAP : {show: y, blk : 1, word: 2, pos: 19, len : 3, start : 83, type : 'uint:3', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: Psram capacity, rloc: 'EFUSE_RD_MAC_SYS2_REG[21:19]', bloc: 'B10[5:3]'} - PSRAM_VENDOR : {show: y, blk : 1, word: 2, pos: 22, len : 2, start : 86, type : 'uint:2', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: Psram vendor, rloc: 'EFUSE_RD_MAC_SYS2_REG[23:22]', bloc: 'B10[7:6]'} - TEMP : {show: y, blk : 1, word: 2, pos: 24, len : 2, start : 88, type : 'uint:2', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: Temp (die embedded inside), rloc: 'EFUSE_RD_MAC_SYS2_REG[25:24]', bloc: 'B11[1:0]'} - PKG_VERSION : {show: y, blk : 1, word: 2, pos: 26, len : 3, start : 90, type : 'uint:3', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: Package version, rloc: 'EFUSE_RD_MAC_SYS2_REG[28:26]', bloc: 'B11[4:2]'} - PA_TRIM_VERSION : {show: y, blk : 1, word: 2, pos: 29, len : 3, start : 93, type : 'uint:3', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: PADC CAL PA trim version, rloc: 'EFUSE_RD_MAC_SYS2_REG[31:29]', bloc: 'B11[7:5]'} - TRIM_N_BIAS : {show: y, blk : 1, word: 3, pos : 0, len : 5, start : 96, type : 'uint:5', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: PADC CAL N bias, rloc: 'EFUSE_RD_MAC_SYS3_REG[4:0]', bloc: 'B12[4:0]'} - TRIM_P_BIAS : {show: y, blk : 1, word: 3, pos : 5, len : 5, start: 101, type : 'uint:5', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: PADC CAL P bias, rloc: 'EFUSE_RD_MAC_SYS3_REG[9:5]', bloc: 'B12[7:5],B13[1:0]'} - RESERVED_1_106 : {show: n, blk : 1, word: 3, pos: 10, len : 8, start: 106, type : 'uint:8', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: reserved, rloc: 'EFUSE_RD_MAC_SYS3_REG[17:10]', bloc: 'B13[7:2],B14[1:0]'} - SYS_DATA_PART0_0 : {show: n, blk : 1, word: 3, pos: 18, len : 14, start: 114, type : 'uint:14', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: Represents the first 14-bit of zeroth part of system data, rloc: 'EFUSE_RD_MAC_SYS3_REG[31:18]', bloc: 'B14[7:2],B15'} - SYS_DATA_PART0_1 : {show: n, blk : 1, word: 4, pos : 0, len : 32, start: 128, type : 'uint:32', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: Represents the first 14-bit of zeroth part of system data, rloc: EFUSE_RD_MAC_SYS4_REG, bloc: 'B16,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: Represents the second 32-bit of zeroth part of system data, rloc: EFUSE_RD_MAC_SYS5_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'} - RESERVED_2_128 : {show: n, blk : 2, word: 4, pos : 0, len : 9, start: 128, type : 'uint:9', wr_dis : 21, rd_dis: null, alt : '', dict : '', desc: reserved, 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]'} - RESERVED_2_145 : {show: n, blk : 2, word: 4, pos: 17, len : 15, start: 145, type : 'uint:15', wr_dis : 21, rd_dis: null, alt : '', dict : '', desc: reserved, rloc: 'EFUSE_RD_SYS_PART1_DATA4_REG[31:17]', bloc: 'B18[7:1],B19'} - SYS_DATA_PART1_5 : {show: n, blk : 2, word: 5, pos : 0, len : 32, start: 160, type : 'uint:32', wr_dis : 21, rd_dis: null, alt : '', dict : '', desc: Represents the zeroth 32-bit of first part of system data, rloc: EFUSE_RD_SYS_PART1_DATA5_REG, bloc: 'B20,B21,B22,B23'} - SYS_DATA_PART1_6 : {show: n, blk : 2, word: 6, pos : 0, len : 32, start: 192, type : 'uint:32', wr_dis : 21, rd_dis: null, alt : '', dict : '', desc: Represents the zeroth 32-bit of first part of system data, rloc: EFUSE_RD_SYS_PART1_DATA6_REG, bloc: 'B24,B25,B26,B27'} - SYS_DATA_PART1_7 : {show: n, blk : 2, word: 7, pos : 0, len : 32, start: 224, type : 'uint:32', wr_dis : 21, rd_dis: null, alt : '', dict : '', desc: Represents the zeroth 32-bit of first part of system data, rloc: EFUSE_RD_SYS_PART1_DATA7_REG, bloc: 'B28,B29,B30,B31'} - 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'} + 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]'} + BOOTLOADER_ANTI_ROLLBACK_SECURE_VERSION_HI: {show: y, blk : 0, word: 1, pos : 7, len : 1, start : 39, type : bool, wr_dis: null, rd_dis: null, alt : '', dict : '', desc: Represents the anti-rollback secure version of the 2nd stage bootloader used by the ROM bootloader (the high part of the field), rloc: 'EFUSE_RD_REPEAT_DATA0_REG[7]', bloc: 'B4[7]'} + DIS_ICACHE : {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 cache is disabled. 1: Disabled 0: Enabled', rloc: 'EFUSE_RD_REPEAT_DATA0_REG[8]', bloc: 'B5[0]'} + DIS_USB_JTAG : {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 USB-to-JTAG function in USB Serial/JTAG is disabled. Note that \hyperref[fielddesc:EFUSEDISUSBJTAG]{EFUSE\_DIS\_USB\_JTAG} is available only when \hyperref[fielddesc:EFUSEDISUSBSERIALJTAG]{EFUSE\_DIS\_USB\_SERIAL\_JTAG} is configured to 0. For more information; please refer to Chapter \ref{mod:bootctrl} \textit{\nameref{mod:bootctrl}}.1: Disabled0: Enabled', rloc: 'EFUSE_RD_REPEAT_DATA0_REG[9]', bloc: 'B5[1]'} + BOOTLOADER_ANTI_ROLLBACK_EN : {show: y, blk : 0, word: 1, pos: 10, len : 1, start : 42, type : bool, wr_dis: null, rd_dis: null, alt : '', dict : '', desc: 'Represents whether the ani-rollback check for the 2nd stage bootloader is enabled.1: Enabled0: Disabled', rloc: 'EFUSE_RD_REPEAT_DATA0_REG[10]', bloc: 'B5[2]'} + DIS_USB_SERIAL_JTAG : {show: n, blk : 0, word: 1, pos: 11, len : 1, start : 43, type : bool, wr_dis : 2, rd_dis: null, alt : '', dict : '', desc: 'Represents whether USB Serial/JTAG is disabled.1: Disabled0: Enabled', rloc: 'EFUSE_RD_REPEAT_DATA0_REG[11]', bloc: 'B5[3]'} + DIS_FORCE_DOWNLOAD : {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 function that forces chip into Download mode is disabled. 1: Disabled0: Enabled', rloc: 'EFUSE_RD_REPEAT_DATA0_REG[12]', bloc: 'B5[4]'} + SPI_DOWNLOAD_MSPI_DIS : {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 SPI0 controller during boot\_mode\_download is disabled.0: Enabled1: Disabled', rloc: 'EFUSE_RD_REPEAT_DATA0_REG[13]', bloc: 'B5[5]'} + DIS_TWAI : {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 TWAI$^\xAE$ function is disabled.1: Disabled0: Enabled", rloc: 'EFUSE_RD_REPEAT_DATA0_REG[14]', bloc: 'B5[6]'} + JTAG_SEL_ENABLE : {show: y, blk : 0, word: 1, pos: 15, len : 1, start : 47, type : bool, wr_dis : 2, rd_dis: null, alt : '', dict : '', desc: 'Represents whether the selection of a JTAG signal source through the strapping pin value is enabled when all of \hyperref[fielddesc:EFUSEDISPADJTAG]{EFUSE\_DIS\_PAD\_JTAG}; \hyperref[fielddesc:EFUSEDISUSBJTAG]{EFUSE\_DIS\_USB\_JTAG} and \hyperref[fielddesc:EFUSEDISUSBSERIALJTAG]{EFUSE\_DIS\_USB\_SERIAL\_JTAG} are configured to 0. For more information; please refer to Chapter \ref{mod:bootctrl} \textit{\nameref{mod:bootctrl}}.1: Enabled0: Disabled', rloc: 'EFUSE_RD_REPEAT_DATA0_REG[15]', bloc: 'B5[7]'} + SOFT_DIS_JTAG : {show: y, blk : 0, word: 1, pos: 16, len : 3, start : 48, type : 'uint:3', wr_dis : 31, rd_dis: null, alt : '', dict : '', desc: 'Represents whether PAD JTAG is disabled in the soft way. It can be restarted via HMAC. Odd count of bits with a value of 1: DisabledEven count of bits with a value of 1: Enabled', rloc: 'EFUSE_RD_REPEAT_DATA0_REG[18:16]', bloc: 'B6[2:0]'} + DIS_PAD_JTAG : {show: y, blk : 0, word: 1, pos: 19, len : 1, start : 51, type : bool, wr_dis : 2, rd_dis: null, alt : '', dict : '', desc: 'Represents whether PAD JTAG is disabled in the hard way (permanently).1: Disabled0: Enabled', rloc: 'EFUSE_RD_REPEAT_DATA0_REG[19]', bloc: 'B6[3]'} + DIS_DOWNLOAD_MANUAL_ENCRYPT : {show: y, blk : 0, word: 1, pos: 20, len : 1, start : 52, type : bool, wr_dis : 2, rd_dis: null, alt : '', dict : '', desc: 'Represents whether flash encryption is disabled (except in SPI boot mode).1: Disabled0: Enabled', rloc: 'EFUSE_RD_REPEAT_DATA0_REG[20]', bloc: 'B6[4]'} + USB_DREFH : {show: n, blk : 0, word: 1, pos: 21, len : 2, start : 53, type : 'uint:2', wr_dis : 30, rd_dis: null, alt : '', dict : '', desc: Represents the single-end input threshold vrefh; 1.76 V to 2 V with step of 80 mV, rloc: 'EFUSE_RD_REPEAT_DATA0_REG[22:21]', bloc: 'B6[6:5]'} + USB_DREFL : {show: n, blk : 0, word: 1, pos: 23, len : 2, start : 55, type : 'uint:2', wr_dis : 30, rd_dis: null, alt : '', dict : '', desc: Represents the single-end input threshold vrefl; 1.76 V to 2 V with step of 80 mV, rloc: 'EFUSE_RD_REPEAT_DATA0_REG[24:23]', bloc: 'B6[7],B7[0]'} + USB_EXCHG_PINS : {show: y, blk : 0, word: 1, pos: 25, len : 1, start : 57, type : bool, wr_dis : 30, rd_dis: null, alt : '', dict : '', desc: 'Represents whether the D+ and D- pins is exchanged.1: Exchanged0: Not exchanged', rloc: 'EFUSE_RD_REPEAT_DATA0_REG[25]', bloc: 'B7[1]'} + VDD_SPI_AS_GPIO : {show: y, blk : 0, word: 1, pos: 26, len : 1, start : 58, type : bool, wr_dis : 30, rd_dis: null, alt : '', dict : '', desc: 'Represents whether VDD SPI pin is functioned as GPIO.1: Functioned0: Not functioned', rloc: 'EFUSE_RD_REPEAT_DATA0_REG[26]', bloc: 'B7[2]'} + WDT_DELAY_SEL : {show: y, blk : 0, word: 1, pos: 27, len : 2, start : 59, type : 'uint:2', wr_dis : 3, rd_dis: null, alt : '', dict : '', desc: "Represents RTC watchdog timeout threshold.0: The originally configured STG0 threshold \xD7 21: The originally configured STG0 threshold \xD7 42: The originally configured STG0 threshold \xD7 83: The originally configured STG0 threshold \xD7 16", rloc: 'EFUSE_RD_REPEAT_DATA0_REG[28:27]', bloc: 'B7[4:3]'} + BOOTLOADER_ANTI_ROLLBACK_SECURE_VERSION_LO: {show: y, blk : 0, word: 1, pos: 29, len : 3, start : 61, type : 'uint:3', wr_dis: null, rd_dis: null, alt : '', dict : '', desc: Represents the anti-rollback secure version of the 2nd stage bootloader used by the ROM bootloader (the low part of the field), rloc: 'EFUSE_RD_REPEAT_DATA0_REG[31:29]', bloc: 'B7[7:5]'} + KM_DISABLE_DEPLOY_MODE : {show: y, blk : 0, word: 2, pos : 0, len : 4, start : 64, type : 'uint:4', wr_dis : 1, rd_dis: null, alt : '', dict : '', desc: 'Represents whether the new key deployment of key manager is disabled. Bit0: Represents whether the new ECDSA key deployment is disabled0: Enabled1: DisabledBit1: Represents whether the new XTS-AES (flash and PSRAM) key deployment is disabled0: Enabled1: DisabledBit2: Represents whether the new HMAC key deployment is disabled0: Enabled1: DisabledBit3: Represents whether the new DS key deployment is disabled0: Enabled1: Disabled', rloc: 'EFUSE_RD_REPEAT_DATA1_REG[3:0]', bloc: 'B8[3:0]'} + KM_RND_SWITCH_CYCLE : {show: y, blk : 0, word: 2, pos : 4, len : 2, start : 68, type : 'uint:2', wr_dis : 1, rd_dis: null, alt : '', dict : '', desc: 'Represents the cycle at which the Key Manager switches random numbers.0: Controlled by the \hyperref[fielddesc:KEYMNGRNDSWITCHCYCLE]{KEYMNG\_RND\_SWITCH\_CYCLE} register. For more information; please refer to Chapter \ref{mod:keymng} \textit{\nameref{mod:keymng}}1: 8 Key Manager clock cycles2: 16 Key Manager clock cycles3: 32 Key Manager clock cycles', rloc: 'EFUSE_RD_REPEAT_DATA1_REG[5:4]', bloc: 'B8[5:4]'} + KM_DEPLOY_ONLY_ONCE : {show: y, blk : 0, word: 2, pos : 6, len : 4, start : 70, type : 'uint:4', wr_dis : 1, rd_dis: null, alt : '', dict : '', desc: 'Represents whether the corresponding key can be deployed only once.Bit0: Represents whether the ECDSA key can be deployed only once0: The key can be deployed multiple times1: The key can be deployed only onceBit1: Represents whether the XTS-AES (flash and PSRAM) key can be deployed only once0: The key can be deployed multiple times1: The key can be deployed only onceBit2: Represents whether the HMAC key can be deployed only once0: The key can be deployed multiple times1: The key can be deployed only onceBit3: Represents whether the DS key can be deployed only once0: The key can be deployed multiple times1: The key can be deployed only once', rloc: 'EFUSE_RD_REPEAT_DATA1_REG[9:6]', bloc: 'B8[7:6],B9[1:0]'} + FORCE_USE_KEY_MANAGER_KEY : {show: y, blk : 0, word: 2, pos: 10, len : 4, start : 74, type : 'uint:4', wr_dis : 1, rd_dis: null, alt : '', dict : '', desc: 'Represents whether the corresponding key must come from Key Manager. Bit0: Represents whether the ECDSA key must come from Key Manager.0: The key does not need to come from Key Manager1: The key must come from Key ManagerBit1: Represents whether the XTS-AES (flash and PSRAM) key must come from Key Manager.0: The key does not need to come from Key Manager1: The key must come from Key ManagerBit2: Represents whether the HMAC key must come from Key Manager.0: The key does not need to come from Key Manager1: The key must come from Key ManagerBit3: Represents whether the DS key must come from Key Manager.0: The key does not need to come from Key Manager1: The key must come from Key Manager', rloc: 'EFUSE_RD_REPEAT_DATA1_REG[13:10]', bloc: 'B9[5:2]'} + FORCE_DISABLE_SW_INIT_KEY : {show: y, blk : 0, word: 2, pos: 14, len : 1, start : 78, type : bool, wr_dis : 1, rd_dis: null, alt : '', dict : '', desc: 'Represents whether to disable the use of the initialization key written by software and instead force use efuse\_init\_key.0: Enable1: Disable', rloc: 'EFUSE_RD_REPEAT_DATA1_REG[14]', bloc: 'B9[6]'} + BOOTLOADER_ANTI_ROLLBACK_UPDATE_IN_ROM : {show: y, blk : 0, word: 2, pos: 15, len : 1, start : 79, type : bool, wr_dis: null, rd_dis: null, alt : '', dict : '', desc: 'Represents whether the ani-rollback SECURE_VERSION will be updated from the ROM bootloader.1: Enable0: Disable', rloc: 'EFUSE_RD_REPEAT_DATA1_REG[15]', bloc: 'B9[7]'} + SPI_BOOT_CRYPT_CNT : {show: y, blk : 0, word: 2, pos: 16, len : 3, start : 80, 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_DATA1_REG[18:16]', bloc: 'B10[2:0]'} + SECURE_BOOT_KEY_REVOKE0 : {show: y, blk : 0, word: 2, pos: 19, len : 1, start : 83, type : bool, wr_dis : 5, rd_dis: null, alt : '', dict : '', desc: Revoke 1st secure boot key, rloc: 'EFUSE_RD_REPEAT_DATA1_REG[19]', bloc: 'B10[3]'} + SECURE_BOOT_KEY_REVOKE1 : {show: y, blk : 0, word: 2, pos: 20, len : 1, start : 84, type : bool, wr_dis : 6, rd_dis: null, alt : '', dict : '', desc: Revoke 2nd secure boot key, rloc: 'EFUSE_RD_REPEAT_DATA1_REG[20]', bloc: 'B10[4]'} + SECURE_BOOT_KEY_REVOKE2 : {show: y, blk : 0, word: 2, pos: 21, len : 1, start : 85, type : bool, wr_dis : 7, rd_dis: null, alt : '', dict : '', desc: Revoke 3rd secure boot key, rloc: 'EFUSE_RD_REPEAT_DATA1_REG[21]', bloc: 'B10[5]'} + KEY_PURPOSE_0 : {show: y, blk : 0, word: 2, pos: 22, len : 5, start : 86, type : 'uint:5', wr_dis : 8, rd_dis: null, alt : KEY0_PURPOSE, dict : '', desc: 'Represents the purpose of Key0. See Table \ref{tab:efuse-key-purpose}', rloc: 'EFUSE_RD_REPEAT_DATA1_REG[26:22]', bloc: 'B10[7:6],B11[2:0]'} + KEY_PURPOSE_1 : {show: y, blk : 0, word: 2, pos: 27, len : 5, start : 91, type : 'uint:5', wr_dis : 9, rd_dis: null, alt : KEY1_PURPOSE, dict : '', desc: 'Represents the purpose of Key1. See Table \ref{tab:efuse-key-purpose}', rloc: 'EFUSE_RD_REPEAT_DATA1_REG[31:27]', bloc: 'B11[7:3]'} + KEY_PURPOSE_2 : {show: y, blk : 0, word: 3, pos : 0, len : 5, start : 96, type : 'uint:5', wr_dis : 10, rd_dis: null, alt : KEY2_PURPOSE, dict : '', desc: 'Represents the purpose of Key2. See Table \ref{tab:efuse-key-purpose}', rloc: 'EFUSE_RD_REPEAT_DATA2_REG[4:0]', bloc: 'B12[4:0]'} + KEY_PURPOSE_3 : {show: y, blk : 0, word: 3, pos : 5, len : 5, start: 101, type : 'uint:5', wr_dis : 11, rd_dis: null, alt : KEY3_PURPOSE, dict : '', desc: 'Represents the purpose of Key3. See Table \ref{tab:efuse-key-purpose}', rloc: 'EFUSE_RD_REPEAT_DATA2_REG[9:5]', bloc: 'B12[7:5],B13[1:0]'} + KEY_PURPOSE_4 : {show: y, blk : 0, word: 3, pos: 10, len : 5, start: 106, type : 'uint:5', wr_dis : 12, rd_dis: null, alt : KEY4_PURPOSE, dict : '', desc: 'Represents the purpose of Key4. See Table \ref{tab:efuse-key-purpose}', rloc: 'EFUSE_RD_REPEAT_DATA2_REG[14:10]', bloc: 'B13[6:2]'} + KEY_PURPOSE_5 : {show: y, blk : 0, word: 3, pos: 15, len : 5, start: 111, type : 'uint:5', wr_dis : 13, rd_dis: null, alt : KEY5_PURPOSE, dict : '', desc: 'Represents the purpose of Key5. See Table \ref{tab:efuse-key-purpose}', rloc: 'EFUSE_RD_REPEAT_DATA2_REG[19:15]', bloc: 'B13[7],B14[3:0]'} + SEC_DPA_LEVEL : {show: y, blk : 0, word: 3, pos: 20, len : 2, start: 116, type : 'uint:2', wr_dis : 14, rd_dis: null, alt : '', dict : '', desc: 'Represents the security level of anti-DPA attack. The level is adjusted by configuring the clock random frequency division mode.0: Security level is SEC\_DPA\_OFF1: Security level is SEC\_DPA\_LOW2: Security level is SEC\_DPA\_MIDDLE3: Security level is SEC\_DPA\_HIGHFor more information; please refer to Chapter \ref{mod:sysreg} \textit{\nameref{mod:sysreg}} > Section \ref{sec:sysreg-anti-dpa-attack-security-control} \textit{\nameref{sec:sysreg-anti-dpa-attack-security-control}}.', rloc: 'EFUSE_RD_REPEAT_DATA2_REG[21:20]', bloc: 'B14[5:4]'} + RECOVERY_BOOTLOADER_FLASH_SECTOR_HI : {show: y, blk : 0, word: 3, pos: 22, len : 3, start: 118, type : 'uint:3', wr_dis: null, rd_dis: null, alt : '', dict : '', desc: Represents the starting flash sector (flash sector size is 0x1000) of the recovery bootloader used by the ROM bootloader If the primary bootloader fails. 0 and 0xFFF - this feature is disabled. (The high part of the field), rloc: 'EFUSE_RD_REPEAT_DATA2_REG[24:22]', bloc: 'B14[7:6],B15[0]'} + SECURE_BOOT_EN : {show: y, blk : 0, word: 3, pos: 25, len : 1, start: 121, type : bool, wr_dis : 15, rd_dis: null, alt : '', dict : '', desc: 'Represents whether Secure Boot is enabled.1: Enabled0: Disabled', rloc: 'EFUSE_RD_REPEAT_DATA2_REG[25]', bloc: 'B15[1]'} + SECURE_BOOT_AGGRESSIVE_REVOKE : {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 aggressive revocation of Secure Boot is enabled.1: Enabled0: Disabled', rloc: 'EFUSE_RD_REPEAT_DATA2_REG[26]', bloc: 'B15[2]'} + KM_XTS_KEY_LENGTH_256 : {show: y, blk : 0, word: 3, pos: 27, len : 1, start: 123, type : bool, wr_dis : 1, rd_dis: null, alt : '', dict : '', desc: 'Represents which key flash encryption uses.0: XTS-AES-256 key1: XTS-AES-128 key', rloc: 'EFUSE_RD_REPEAT_DATA2_REG[27]', bloc: 'B15[3]'} + FLASH_TPUW : {show: y, blk : 0, word: 3, pos: 28, len : 4, start: 124, type : 'uint:4', wr_dis : 18, rd_dis: null, alt : '', dict : '', desc: 'Represents the flash waiting time after power-up. Measurement unit: ms. When the value is less than 15; the waiting time is the programmed value. Otherwise; the waiting time is a fixed value; i.e. 30 ms', rloc: 'EFUSE_RD_REPEAT_DATA2_REG[31:28]', bloc: 'B15[7:4]'} + DIS_DOWNLOAD_MODE : {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 Download mode is disable or enable. 1. Disable 0: Enable', rloc: 'EFUSE_RD_REPEAT_DATA3_REG[0]', bloc: 'B16[0]'} + DIS_DIRECT_BOOT : {show: y, blk : 0, word: 4, pos : 1, len : 1, start: 129, type : bool, wr_dis : 18, rd_dis: null, alt : '', dict : '', desc: 'Represents whether direct boot mode is disabled or enabled. 1. Disable 0: Enable', rloc: 'EFUSE_RD_REPEAT_DATA3_REG[1]', bloc: 'B16[1]'} + DIS_USB_SERIAL_JTAG_ROM_PRINT : {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 print from USB-Serial-JTAG is disabled or enabled. 1. Disable 0: Enable', rloc: 'EFUSE_RD_REPEAT_DATA3_REG[2]', bloc: 'B16[2]'} + LOCK_KM_KEY : {show: y, blk : 0, word: 4, pos : 3, len : 1, start: 131, type : bool, wr_dis : 1, rd_dis: null, alt : '', dict : '', desc: 'Represents whether the keys in the Key Manager are locked after deployment.0: Not locked1: Locked', rloc: 'EFUSE_RD_REPEAT_DATA3_REG[3]', bloc: 'B16[3]'} + DIS_USB_SERIAL_JTAG_DOWNLOAD_MODE : {show: y, blk : 0, word: 4, pos : 4, len : 1, start: 132, type : bool, wr_dis : 18, rd_dis: null, alt : '', dict : '', desc: 'Represents whether the USB-Serial-JTAG download function is disabled or enabled. 1: Disable 0: Enable', rloc: 'EFUSE_RD_REPEAT_DATA3_REG[4]', bloc: 'B16[4]'} + ENABLE_SECURITY_DOWNLOAD : {show: y, blk : 0, word: 4, pos : 5, len : 1, start: 133, type : bool, wr_dis : 18, rd_dis: null, alt : '', dict : '', desc: 'Represents whether security download is enabled. Only downloading into flash is supported. Reading/writing RAM or registers is not supported (i.e. stub download is not supported).1: Enabled0: Disabled', rloc: 'EFUSE_RD_REPEAT_DATA3_REG[5]', bloc: 'B16[5]'} + UART_PRINT_CONTROL : {show: y, blk : 0, word: 4, pos : 6, len : 2, start: 134, 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[7:6]', bloc: 'B16[7:6]'} + FORCE_SEND_RESUME : {show: y, blk : 0, word: 4, pos : 8, len : 1, start: 136, 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[8]', bloc: 'B17[0]'} + SECURE_VERSION : {show: y, blk : 0, word: 4, pos : 9, len : 9, start: 137, type : 'uint:9', wr_dis : 18, rd_dis: null, alt : '', dict : '', desc: Represents the app secure version used by ESP-IDF anti-rollback feature, rloc: 'EFUSE_RD_REPEAT_DATA3_REG[17:9]', bloc: 'B17[7:1],B18[1:0]'} + RESERVE_0_146 : {show: n, blk : 0, word: 4, pos: 18, len : 7, start: 146, type : 'uint:7', wr_dis: null, rd_dis: null, alt : '', dict : '', desc: Reserved; it was created by set_missed_fields_in_regs func, rloc: 'EFUSE_RD_REPEAT_DATA3_REG[24:18]', bloc: 'B18[7:2],B19[0]'} + SECURE_BOOT_DISABLE_FAST_WAKE : {show: y, blk : 0, word: 4, pos: 25, len : 1, start: 153, type : bool, wr_dis : 18, rd_dis: null, alt : '', dict : '', desc: 'Represents whether FAST VERIFY ON WAKE is disabled when Secure Boot is enabled.1: Disabled0: Enabled', rloc: 'EFUSE_RD_REPEAT_DATA3_REG[25]', bloc: 'B19[1]'} + HYS_EN_PAD : {show: y, blk : 0, word: 4, pos: 26, len : 1, start: 154, type : bool, wr_dis : 2, rd_dis: null, alt : '', dict : '', desc: "Represents whether the hysteresis function of PAD0 \u2013 PAD27 is enabled.1: Enabled0: Disabled", rloc: 'EFUSE_RD_REPEAT_DATA3_REG[26]', bloc: 'B19[2]'} + XTS_DPA_PSEUDO_LEVEL : {show: y, blk : 0, word: 4, pos: 27, len : 2, start: 155, type : 'uint:2', wr_dis : 14, rd_dis: null, alt : '', dict : '', desc: 'Represents the pseudo round level of XTS-AES anti-DPA attack.0: Disabled1: Low2: Moderate3: High', rloc: 'EFUSE_RD_REPEAT_DATA3_REG[28:27]', bloc: 'B19[4:3]'} + XTS_DPA_CLK_ENABLE : {show: y, blk : 0, word: 4, pos: 29, len : 1, start: 157, type : bool, wr_dis : 14, rd_dis: null, alt : '', dict : '', desc: 'Represents whether XTS-AES anti-DPA attack clock is enabled.0: Disable1: Enabled', rloc: 'EFUSE_RD_REPEAT_DATA3_REG[29]', bloc: 'B19[5]'} + RESERVE_0_158 : {show: n, blk : 0, word: 4, pos: 30, len : 1, start: 158, type : bool, wr_dis: null, rd_dis: null, alt : '', dict : '', desc: Reserved; it was created by set_missed_fields_in_regs func, rloc: 'EFUSE_RD_REPEAT_DATA3_REG[30]', bloc: 'B19[6]'} + SECURE_BOOT_SHA384_EN : {show: y, blk : 0, word: 4, pos: 31, len : 1, start: 159, type : bool, wr_dis : 14, rd_dis: null, alt : '', dict : '', desc: Represents if the chip supports Secure Boot using SHA-384, rloc: 'EFUSE_RD_REPEAT_DATA3_REG[31]', bloc: 'B19[7]'} + HUK_GEN_STATE : {show: y, blk : 0, word: 5, pos : 0, len : 9, start: 160, type : 'uint:9', wr_dis : 19, rd_dis: null, alt : '', dict : '', desc: 'Represents whether the HUK generate mode is valid.Odd count of bits with a value of 1: InvalidEven count of bits with a value of 1: Valid', rloc: 'EFUSE_RD_REPEAT_DATA4_REG[8:0]', bloc: 'B20,B21[0]'} + XTAL_48M_SEL : {show: y, blk : 0, word: 5, pos : 9, len : 3, start: 169, type : 'uint:3', wr_dis : 17, rd_dis: null, alt : '', dict : '', desc: 'Represents whether XTAL frequency is 48MHz or not. If not; 40MHz XTAL will be used. If this field contains Odd number bit 1: Enable 48MHz XTAL\ Even number bit 1: Enable 40MHz XTAL', rloc: 'EFUSE_RD_REPEAT_DATA4_REG[11:9]', bloc: 'B21[3:1]'} + XTAL_48M_SEL_MODE : {show: y, blk : 0, word: 5, pos: 12, len : 1, start: 172, type : bool, wr_dis : 17, rd_dis: null, alt : '', dict : '', desc: 'Represents what determines the XTAL frequency in \textbf{Joint Download Boot} mode. For more information; please refer to Chapter \ref{mod:bootctrl} \textit{\nameref{mod:bootctrl}}.0: Strapping PAD state1: \hyperref[fielddesc:EFUSEXTAL48MSEL]{EFUSE\_XTAL\_48M\_SEL} in eFuse', rloc: 'EFUSE_RD_REPEAT_DATA4_REG[12]', bloc: 'B21[4]'} + ECC_FORCE_CONST_TIME : {show: y, blk : 0, word: 5, pos: 13, len : 1, start: 173, type : bool, wr_dis : 14, rd_dis: null, alt : '', dict : '', desc: 'Represents whether to force ECC to use constant-time mode for point multiplication calculation. 0: Not force1: Force', rloc: 'EFUSE_RD_REPEAT_DATA4_REG[13]', bloc: 'B21[5]'} + RECOVERY_BOOTLOADER_FLASH_SECTOR_LO : {show: y, blk : 0, word: 5, pos: 14, len : 9, start: 174, type : 'uint:9', wr_dis: null, rd_dis: null, alt : '', dict : '', desc: Represents the starting flash sector (flash sector size is 0x1000) of the recovery bootloader used by the ROM bootloader If the primary bootloader fails. 0 and 0xFFF - this feature is disabled. (The low part of the field), rloc: 'EFUSE_RD_REPEAT_DATA4_REG[22:14]', bloc: 'B21[7:6],B22[6:0]'} + RESERVE_0_183 : {show: n, blk : 0, word: 5, pos: 23, len : 9, start: 183, type : 'uint:9', wr_dis: null, rd_dis: null, alt : '', dict : '', desc: Reserved; it was created by set_missed_fields_in_regs func, rloc: 'EFUSE_RD_REPEAT_DATA4_REG[31:23]', bloc: 'B22[7],B23'} + 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_SYS0_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: Represents the extended bits of MAC address, rloc: 'EFUSE_RD_MAC_SYS1_REG[31:16]', bloc: 'B6,B7'} + WAFER_VERSION_MINOR : {show: y, blk : 1, word: 2, pos : 0, len : 4, start : 64, type : 'uint:4', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: Minor chip version, rloc: 'EFUSE_RD_MAC_SYS2_REG[3:0]', bloc: 'B8[3:0]'} + WAFER_VERSION_MAJOR : {show: y, blk : 1, word: 2, pos : 4, len : 2, start : 68, type : 'uint:2', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: Minor chip version, rloc: 'EFUSE_RD_MAC_SYS2_REG[5:4]', bloc: 'B8[5:4]'} + DISABLE_WAFER_VERSION_MAJOR : {show: y, blk : 1, word: 2, pos : 6, len : 1, start : 70, type : bool, wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: Disables check of wafer version major, rloc: 'EFUSE_RD_MAC_SYS2_REG[6]', bloc: 'B8[6]'} + DISABLE_BLK_VERSION_MAJOR : {show: y, blk : 1, word: 2, pos : 7, len : 1, start : 71, type : bool, wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: Disables check of blk version major, rloc: 'EFUSE_RD_MAC_SYS2_REG[7]', bloc: 'B8[7]'} + BLK_VERSION_MINOR : {show: y, blk : 1, word: 2, pos : 8, len : 3, start : 72, type : 'uint:3', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: BLK_VERSION_MINOR of BLOCK2, rloc: 'EFUSE_RD_MAC_SYS2_REG[10:8]', bloc: 'B9[2:0]'} + BLK_VERSION_MAJOR : {show: y, blk : 1, word: 2, pos: 11, len : 2, start : 75, type : 'uint:2', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: BLK_VERSION_MAJOR of BLOCK2, rloc: 'EFUSE_RD_MAC_SYS2_REG[12:11]', bloc: 'B9[4:3]'} + FLASH_CAP : {show: y, blk : 1, word: 2, pos: 13, len : 3, start : 77, type : 'uint:3', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: Flash capacity, rloc: 'EFUSE_RD_MAC_SYS2_REG[15:13]', bloc: 'B9[7:5]'} + FLASH_VENDOR : {show: y, blk : 1, word: 2, pos: 16, len : 3, start : 80, type : 'uint:3', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: Flash vendor, rloc: 'EFUSE_RD_MAC_SYS2_REG[18:16]', bloc: 'B10[2:0]'} + PSRAM_CAP : {show: y, blk : 1, word: 2, pos: 19, len : 3, start : 83, type : 'uint:3', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: Psram capacity, rloc: 'EFUSE_RD_MAC_SYS2_REG[21:19]', bloc: 'B10[5:3]'} + PSRAM_VENDOR : {show: y, blk : 1, word: 2, pos: 22, len : 2, start : 86, type : 'uint:2', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: Psram vendor, rloc: 'EFUSE_RD_MAC_SYS2_REG[23:22]', bloc: 'B10[7:6]'} + TEMP : {show: y, blk : 1, word: 2, pos: 24, len : 2, start : 88, type : 'uint:2', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: Temp (die embedded inside), rloc: 'EFUSE_RD_MAC_SYS2_REG[25:24]', bloc: 'B11[1:0]'} + PKG_VERSION : {show: y, blk : 1, word: 2, pos: 26, len : 3, start : 90, type : 'uint:3', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: Package version, rloc: 'EFUSE_RD_MAC_SYS2_REG[28:26]', bloc: 'B11[4:2]'} + PA_TRIM_VERSION : {show: y, blk : 1, word: 2, pos: 29, len : 3, start : 93, type : 'uint:3', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: PADC CAL PA trim version, rloc: 'EFUSE_RD_MAC_SYS2_REG[31:29]', bloc: 'B11[7:5]'} + TRIM_N_BIAS : {show: y, blk : 1, word: 3, pos : 0, len : 5, start : 96, type : 'uint:5', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: PADC CAL N bias, rloc: 'EFUSE_RD_MAC_SYS3_REG[4:0]', bloc: 'B12[4:0]'} + TRIM_P_BIAS : {show: y, blk : 1, word: 3, pos : 5, len : 5, start: 101, type : 'uint:5', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: PADC CAL P bias, rloc: 'EFUSE_RD_MAC_SYS3_REG[9:5]', bloc: 'B12[7:5],B13[1:0]'} + ACTIVE_HP_DBIAS : {show: y, blk : 1, word: 3, pos: 10, len : 4, start: 106, type : 'uint:4', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: Active HP DBIAS of fixed voltage, rloc: 'EFUSE_RD_MAC_SYS3_REG[13:10]', bloc: 'B13[5:2]'} + ACTIVE_LP_DBIAS : {show: y, blk : 1, word: 3, pos: 14, len : 4, start: 110, type : 'uint:4', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: Active LP DBIAS of fixed voltage, rloc: 'EFUSE_RD_MAC_SYS3_REG[17:14]', bloc: 'B13[7:6],B14[1:0]'} + LSLP_HP_DBG : {show: y, blk : 1, word: 3, pos: 18, len : 2, start: 114, type : 'uint:2', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: LSLP HP DBG of fixed voltage, rloc: 'EFUSE_RD_MAC_SYS3_REG[19:18]', bloc: 'B14[3:2]'} + LSLP_HP_DBIAS : {show: y, blk : 1, word: 3, pos: 20, len : 4, start: 116, type : 'uint:4', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: LSLP HP DBIAS of fixed voltage, rloc: 'EFUSE_RD_MAC_SYS3_REG[23:20]', bloc: 'B14[7:4]'} + DSLP_LP_DBG : {show: y, blk : 1, word: 3, pos: 24, len : 4, start: 120, type : 'uint:4', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: DSLP LP DBG of fixed voltage, rloc: 'EFUSE_RD_MAC_SYS3_REG[27:24]', bloc: 'B15[3:0]'} + DSLP_LP_DBIAS : {show: y, blk : 1, word: 3, pos: 28, len : 5, start: 124, type : 'uint:5', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: DSLP LP DBIAS of fixed voltage, rloc: 'EFUSE_RD_MAC_SYS3_REG[31:28]', bloc: 'B15[7:4],B16[0]'} + LP_HP_DBIAS_VOL_GAP : {show: y, blk : 1, word: 4, pos : 1, len : 5, start: 129, type : 'uint:5', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: DBIAS gap between LP and HP, rloc: 'EFUSE_RD_MAC_SYS4_REG[5:1]', bloc: 'B16[5:1]'} + REF_CURR_CODE : {show: y, blk : 1, word: 4, pos : 6, len : 4, start: 134, type : 'uint:4', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: REF PADC Calibration Curr, rloc: 'EFUSE_RD_MAC_SYS4_REG[9:6]', bloc: 'B16[7:6],B17[1:0]'} + RES_TUNE_CODE : {show: y, blk : 1, word: 4, pos: 10, len : 5, start: 138, type : 'uint:5', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: RES PADC Calibration Tune, rloc: 'EFUSE_RD_MAC_SYS4_REG[14:10]', bloc: 'B17[6:2]'} + RESERVED_1_143 : {show: n, blk : 1, word: 4, pos: 15, len : 17, start: 143, type : 'uint:17', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: reserved, rloc: 'EFUSE_RD_MAC_SYS4_REG[31:15]', bloc: 'B17[7],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: Represents the third 32-bit of zeroth part of system data, rloc: EFUSE_RD_MAC_SYS5_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'} + TEMPERATURE_SENSOR : {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_AVE_INITCODE_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: Average initcode of ADC1 atten0, rloc: 'EFUSE_RD_SYS_PART1_DATA4_REG[26:17]', bloc: 'B18[7:1],B19[2:0]'} + ADC1_AVE_INITCODE_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: Average initcode of ADC1 atten0, rloc: 'EFUSE_RD_SYS_PART1_DATA4_REG[31:27]', bloc: 'B19[7:3],B20[4:0]'} + ADC1_AVE_INITCODE_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: Average initcode of ADC1 atten0, rloc: 'EFUSE_RD_SYS_PART1_DATA5_REG[14:5]', bloc: 'B20[7:5],B21[6:0]'} + ADC1_AVE_INITCODE_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: Average initcode of ADC1 atten0, rloc: 'EFUSE_RD_SYS_PART1_DATA5_REG[24:15]', bloc: 'B21[7],B22,B23[0]'} + ADC1_HI_DOUT_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: HI DOUT of ADC1 atten0, rloc: 'EFUSE_RD_SYS_PART1_DATA5_REG[31:25]', bloc: 'B23[7:1],B24[2:0]'} + ADC1_HI_DOUT_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: HI DOUT of ADC1 atten1, rloc: 'EFUSE_RD_SYS_PART1_DATA6_REG[12:3]', bloc: 'B24[7:3],B25[4:0]'} + ADC1_HI_DOUT_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: HI DOUT of ADC1 atten2, rloc: 'EFUSE_RD_SYS_PART1_DATA6_REG[22:13]', bloc: 'B25[7:5],B26[6:0]'} + ADC1_HI_DOUT_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: HI DOUT of ADC1 atten3, rloc: 'EFUSE_RD_SYS_PART1_DATA6_REG[31:23]', bloc: 'B26[7],B27,B28[0]'} + ADC1_CH0_ATTEN0_INITCODE_DIFF : {show: y, blk : 2, word: 7, pos : 1, len : 4, start: 225, type : 'uint:4', wr_dis : 21, rd_dis: null, alt : '', dict : '', desc: Gap between ADC1 CH0 and average initcode, rloc: 'EFUSE_RD_SYS_PART1_DATA7_REG[4:1]', bloc: 'B28[4:1]'} + ADC1_CH1_ATTEN0_INITCODE_DIFF : {show: y, blk : 2, word: 7, pos : 5, len : 4, start: 229, type : 'uint:4', wr_dis : 21, rd_dis: null, alt : '', dict : '', desc: Gap between ADC1 CH1 and average initcode, rloc: 'EFUSE_RD_SYS_PART1_DATA7_REG[8:5]', bloc: 'B28[7:5],B29[0]'} + ADC1_CH2_ATTEN0_INITCODE_DIFF : {show: y, blk : 2, word: 7, pos : 9, len : 4, start: 233, type : 'uint:4', wr_dis : 21, rd_dis: null, alt : '', dict : '', desc: Gap between ADC1 CH2 and average initcode, rloc: 'EFUSE_RD_SYS_PART1_DATA7_REG[12:9]', bloc: 'B29[4:1]'} + ADC1_CH3_ATTEN0_INITCODE_DIFF : {show: y, blk : 2, word: 7, pos: 13, len : 4, start: 237, type : 'uint:4', wr_dis : 21, rd_dis: null, alt : '', dict : '', desc: Gap between ADC1 CH3 and average initcode, rloc: 'EFUSE_RD_SYS_PART1_DATA7_REG[16:13]', bloc: 'B29[7:5],B30[0]'} + ADC1_CH4_ATTEN0_INITCODE_DIFF : {show: y, blk : 2, word: 7, pos: 17, len : 4, start: 241, type : 'uint:4', wr_dis : 21, rd_dis: null, alt : '', dict : '', desc: Gap between ADC1 CH4 and average initcode, rloc: 'EFUSE_RD_SYS_PART1_DATA7_REG[20:17]', bloc: 'B30[4:1]'} + ADC1_CH5_ATTEN0_INITCODE_DIFF : {show: y, blk : 2, word: 7, pos: 21, len : 4, start: 245, type : 'uint:4', wr_dis : 21, rd_dis: null, alt : '', dict : '', desc: Gap between ADC1 CH5 and average initcode, rloc: 'EFUSE_RD_SYS_PART1_DATA7_REG[24:21]', bloc: 'B30[7:5],B31[0]'} + RESERVED_2_249 : {show: n, blk : 2, word: 7, pos: 25, len : 7, start: 249, type : 'uint:7', wr_dis : 21, rd_dis: null, alt : '', dict : '', desc: reserved, rloc: 'EFUSE_RD_SYS_PART1_DATA7_REG[31:25]', bloc: 'B31[7:1]'} + 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'} diff --git a/tools/esptool_py/espefuse/efuse_defs/esp32c61.yaml b/tools/esptool_py/espefuse/efuse_defs/esp32c61.yaml index 3cbd2660e3..4ad5d951a1 100644 --- a/tools/esptool_py/espefuse/efuse_defs/esp32c61.yaml +++ b/tools/esptool_py/espefuse/efuse_defs/esp32c61.yaml @@ -1,84 +1,105 @@ -VER_NO: e564f8042b56a475a7714bb28ecdadfa +VER_NO: d435ade68d90ef96b0522478b2d8ba75 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_DIS0_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_ICACHE : {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 icache is disabled or enabled.\\ 1: disabled\\ 0: enabled\\', rloc: 'EFUSE_RD_REPEAT_DATA0_REG[7]', bloc: 'B4[7]'} - DIS_USB_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 the function of usb switch to jtag is disabled or enabled.\\ 1: disabled\\ 0: enabled\\', rloc: 'EFUSE_RD_REPEAT_DATA0_REG[8]', bloc: 'B5[0]'} - DIS_USB_SERIAL_JTAG : {show: n, blk : 0, word: 1, pos : 9, len : 1, start : 41, 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[9]', bloc: 'B5[1]'} - DIS_FORCE_DOWNLOAD : {show: y, blk : 0, word: 1, pos: 10, len : 1, start : 42, 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[10]', bloc: 'B5[2]'} - SPI_DOWNLOAD_MSPI_DIS : {show: y, blk : 0, word: 1, pos: 11, len : 1, start : 43, type : bool, wr_dis : 2, 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[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]'} - USB_DREFH : {show: n, blk : 0, word: 1, pos: 15, len : 2, start : 47, type : 'uint:2', wr_dis : 30, rd_dis: null, alt : '', dict : '', desc: Represents the single-end input threshold vrefh; 1.76 V to 2 V with step of 80 mV, rloc: 'EFUSE_RD_REPEAT_DATA0_REG[16:15]', bloc: 'B5[7],B6[0]'} - USB_DREFL : {show: n, blk : 0, word: 1, pos: 17, len : 2, start : 49, type : 'uint:2', wr_dis : 30, rd_dis: null, alt : '', dict : '', desc: Represents the single-end input threshold vrefl; 1.76 V to 2 V with step of 80 mV, rloc: 'EFUSE_RD_REPEAT_DATA0_REG[18:17]', bloc: 'B6[2:1]'} - USB_EXCHG_PINS : {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 the D+ and D- pins is exchanged.\\ 1: exchanged\\ 0: not exchanged\\', rloc: 'EFUSE_RD_REPEAT_DATA0_REG[19]', bloc: 'B6[3]'} - VDD_SPI_AS_GPIO : {show: y, blk : 0, word: 1, pos: 20, len : 1, start : 52, type : bool, wr_dis : 30, rd_dis: null, alt : '', dict : '', desc: 'Represents whether vdd spi pin is functioned as gpio.\\ 1: functioned\\ 0: not functioned\\', rloc: 'EFUSE_RD_REPEAT_DATA0_REG[20]', bloc: 'B6[4]'} - WDT_DELAY_SEL : {show: y, blk : 0, word: 1, pos: 21, len : 2, start : 53, type : 'uint:2', wr_dis : 3, rd_dis: null, alt : '', dict : '', desc: 'Represents the threshold level of the RTC watchdog STG0 timeout.\\ 0: Original threshold configuration value of STG0 *2 \\1: Original threshold configuration value of STG0 *4 \\2: Original threshold configuration value of STG0 *8 \\3: Original threshold configuration value of STG0 *16 \\', rloc: 'EFUSE_RD_REPEAT_DATA0_REG[22:21]', bloc: 'B6[6:5]'} - 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[26]', bloc: 'B7[2]'} - 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[27]', bloc: 'B7[3]'} - 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[28]', bloc: 'B7[4]'} - RESERVE_0_61 : {show: n, blk : 0, word: 1, pos: 29, len : 3, start : 61, type : 'uint:3', wr_dis: null, rd_dis: null, alt : '', dict : '', desc: Reserved; it was created by set_missed_fields_in_regs func, rloc: 'EFUSE_RD_REPEAT_DATA0_REG[31:29]', bloc: 'B7[7:5]'} - KEY_PURPOSE_0 : {show: y, blk : 0, word: 2, pos : 0, len : 4, start : 64, type : 'uint:4', wr_dis : 8, rd_dis: null, alt : KEY0_PURPOSE, dict : '', desc: Represents the purpose of Key0, rloc: 'EFUSE_RD_REPEAT_DATA1_REG[3:0]', bloc: 'B8[3:0]'} - KEY_PURPOSE_1 : {show: y, blk : 0, word: 2, pos : 4, len : 4, start : 68, type : 'uint:4', wr_dis : 9, rd_dis: null, alt : KEY1_PURPOSE, dict : '', desc: Represents the purpose of Key1, rloc: 'EFUSE_RD_REPEAT_DATA1_REG[7:4]', bloc: 'B8[7:4]'} - KEY_PURPOSE_2 : {show: y, blk : 0, word: 2, pos : 8, len : 4, start : 72, type : 'uint:4', wr_dis : 10, rd_dis: null, alt : KEY2_PURPOSE, dict : '', desc: Represents the purpose of Key2, rloc: 'EFUSE_RD_REPEAT_DATA1_REG[11:8]', bloc: 'B9[3:0]'} - KEY_PURPOSE_3 : {show: y, blk : 0, word: 2, pos: 12, len : 4, start : 76, type : 'uint:4', wr_dis : 11, rd_dis: null, alt : KEY3_PURPOSE, dict : '', desc: Represents the purpose of Key3, rloc: 'EFUSE_RD_REPEAT_DATA1_REG[15:12]', bloc: 'B9[7:4]'} - KEY_PURPOSE_4 : {show: y, blk : 0, word: 2, pos: 16, len : 4, start : 80, type : 'uint:4', wr_dis : 12, rd_dis: null, alt : KEY4_PURPOSE, dict : '', desc: Represents the purpose of Key4, rloc: 'EFUSE_RD_REPEAT_DATA1_REG[19:16]', bloc: 'B10[3:0]'} - KEY_PURPOSE_5 : {show: y, blk : 0, word: 2, pos: 20, len : 4, start : 84, type : 'uint:4', wr_dis : 13, rd_dis: null, alt : KEY5_PURPOSE, dict : '', desc: Represents the purpose of Key5, rloc: 'EFUSE_RD_REPEAT_DATA1_REG[23:20]', bloc: 'B10[7:4]'} - SEC_DPA_LEVEL : {show: y, blk : 0, word: 2, pos: 24, len : 2, start : 88, type : 'uint:2', wr_dis : 14, rd_dis: null, alt : '', dict : '', desc: Represents the spa secure level by configuring the clock random divide mode, rloc: 'EFUSE_RD_REPEAT_DATA1_REG[25:24]', bloc: 'B11[1:0]'} - SECURE_BOOT_EN : {show: y, blk : 0, word: 2, pos: 26, len : 1, start : 90, 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_DATA1_REG[26]', bloc: 'B11[2]'} - SECURE_BOOT_AGGRESSIVE_REVOKE : {show: y, blk : 0, word: 2, pos: 27, len : 1, start : 91, 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_DATA1_REG[27]', bloc: 'B11[3]'} - FLASH_TPUW : {show: y, blk : 0, word: 2, pos: 28, len : 4, start : 92, type : 'uint:4', 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 programmed value. Otherwise; the waiting time is 2 times the programmed value, rloc: 'EFUSE_RD_REPEAT_DATA1_REG[31:28]', bloc: 'B11[7:4]'} - DIS_DOWNLOAD_MODE : {show: y, blk : 0, word: 3, pos : 0, len : 1, start : 96, type : bool, wr_dis : 18, rd_dis: null, alt : '', dict : '', desc: 'Represents whether Download mode is disable or enable.\\ 1. Disable\\ 0: Enable\\', rloc: 'EFUSE_RD_REPEAT_DATA2_REG[0]', bloc: 'B12[0]'} - DIS_DIRECT_BOOT : {show: y, blk : 0, word: 3, pos : 1, len : 1, start : 97, type : bool, wr_dis : 18, rd_dis: null, alt : '', dict : '', desc: 'Represents whether direct boot mode is disabled or enabled.\\ 1. Disable\\ 0: Enable\\', rloc: 'EFUSE_RD_REPEAT_DATA2_REG[1]', bloc: 'B12[1]'} - DIS_USB_SERIAL_JTAG_ROM_PRINT : {show: y, blk : 0, word: 3, pos : 2, len : 1, start : 98, type : bool, wr_dis : 18, rd_dis: null, alt : '', dict : '', desc: 'Represents whether print from USB-Serial-JTAG is disabled or enabled.\\ 1. Disable\\ 0: Enable\\', rloc: 'EFUSE_RD_REPEAT_DATA2_REG[2]', bloc: 'B12[2]'} - DIS_USB_SERIAL_JTAG_DOWNLOAD_MODE: {show: y, blk : 0, word: 3, pos : 3, len : 1, start : 99, type : bool, wr_dis : 18, rd_dis: null, alt : '', dict : '', desc: 'Represents whether the USB-Serial-JTAG download function is disabled or enabled.\\ 1: Disable\\ 0: Enable\\', rloc: 'EFUSE_RD_REPEAT_DATA2_REG[3]', bloc: 'B12[3]'} - ENABLE_SECURITY_DOWNLOAD : {show: y, blk : 0, word: 3, pos : 4, len : 1, start: 100, type : bool, wr_dis : 18, rd_dis: null, alt : '', dict : '', desc: 'Represents whether security download is enabled or disabled.\\ 1: Enable\\ 0: Disable\\', rloc: 'EFUSE_RD_REPEAT_DATA2_REG[4]', bloc: 'B12[4]'} - UART_PRINT_CONTROL : {show: y, blk : 0, word: 3, pos : 5, len : 2, start: 101, type : 'uint:2', wr_dis : 18, rd_dis: null, alt : '', dict : '', desc: Represents the types of UART printing, rloc: 'EFUSE_RD_REPEAT_DATA2_REG[6:5]', bloc: 'B12[6:5]'} - FORCE_SEND_RESUME : {show: y, blk : 0, word: 3, pos : 7, len : 1, start: 103, 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, rloc: 'EFUSE_RD_REPEAT_DATA2_REG[7]', bloc: 'B12[7]'} - SECURE_VERSION : {show: y, blk : 0, word: 3, pos : 8, len : 16, start: 104, 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_DATA2_REG[23:8]', bloc: 'B13,B14'} - SECURE_BOOT_DISABLE_FAST_WAKE : {show: y, blk : 0, word: 3, pos: 24, len : 1, start: 120, type : bool, wr_dis : 18, rd_dis: null, alt : '', dict : '', desc: Represents whether FAST_VERIFY_ON_WAKE is disable or enable when Secure Boot is enable, rloc: 'EFUSE_RD_REPEAT_DATA2_REG[24]', bloc: 'B15[0]'} - HYS_EN_PAD : {show: y, blk : 0, word: 3, pos: 25, len : 1, start: 121, type : bool, wr_dis : 2, rd_dis: null, alt : '', dict : '', desc: 'Represents whether the hysteresis function of corresponding PAD is enabled.\\ 1: enabled\\ 0:disabled\\', rloc: 'EFUSE_RD_REPEAT_DATA2_REG[25]', bloc: 'B15[1]'} - XTS_DPA_CLK_ENABLE : {show: y, blk : 0, word: 3, pos: 26, len : 1, start: 122, type : bool, wr_dis : 14, rd_dis: null, alt : '', dict : '', desc: 'Represents whether anti-dpa attack clock function is enabled.\\ 1. Enable\\ 0: Disable\\', rloc: 'EFUSE_RD_REPEAT_DATA2_REG[26]', bloc: 'B15[2]'} - XTS_DPA_PSEUDO_LEVEL : {show: y, blk : 0, word: 3, pos: 27, len : 2, start: 123, type : 'uint:2', wr_dis : 14, rd_dis: null, alt : '', dict : '', desc: 'Represents the anti-dpa attack pseudo function level.\\ 3:High\\ 2: Moderate\\ 1: Low\\ 0: Decided by register configuration\\', rloc: 'EFUSE_RD_REPEAT_DATA2_REG[28:27]', bloc: 'B15[4:3]'} - DIS_WIFI6 : {show: y, blk : 0, word: 3, pos: 29, len : 1, start: 125, type : bool, wr_dis : 2, rd_dis: null, alt : '', dict : '', desc: 'Represents whether the WiFi 6 feature is enable or disable.\\ 1: WiFi 6 is disable\\ 0: WiFi 6 is enabled.\\', rloc: 'EFUSE_RD_REPEAT_DATA2_REG[29]', bloc: 'B15[5]'} - ECDSA_DISABLE_P192 : {show: y, blk : 0, word: 3, pos: 30, len : 1, start: 126, type : bool, wr_dis : 14, rd_dis: null, alt : '', dict : '', desc: 'Represents whether to disable P192 curve in ECDSA.\\ 1: Disabled.\\ 0: Not disable', rloc: 'EFUSE_RD_REPEAT_DATA2_REG[30]', bloc: 'B15[6]'} - ECC_FORCE_CONST_TIME : {show: y, blk : 0, word: 3, pos: 31, len : 1, start: 127, type : bool, wr_dis : 14, rd_dis: null, alt : '', dict : '', desc: 'Represents whether to force ecc to use const-time calculation mode. \\ 1: Enable. \\ 0: Disable', rloc: 'EFUSE_RD_REPEAT_DATA2_REG[31]', bloc: 'B15[7]'} - REPEAT_DATA3 : {show: n, blk : 0, word: 4, pos : 0, len : 32, start: 128, type : 'uint:32', wr_dis: null, rd_dis: null, alt : '', dict : '', desc: Reserved, rloc: EFUSE_RD_REPEAT_DATA3_REG, bloc: 'B16,B17,B18,B19'} - REPEAT_DATA4 : {show: n, blk : 0, word: 5, pos : 0, len : 32, start: 160, type : 'uint:32', wr_dis: null, rd_dis: null, alt : '', dict : '', desc: Reserved, rloc: EFUSE_RD_REPEAT_DATA4_REG, bloc: 'B20,B21,B22,B23'} - 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_SYS0_REG, bloc: 'B0,B1,B2,B3,B4,B5'} - C61_NO_EXTENTION : {show: n, blk : 1, word: 1, pos: 16, len : 16, start : 48, type : 'uint:16', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: Reserved, rloc: 'EFUSE_RD_MAC_SYS1_REG[31:16]', bloc: 'B6,B7'} - WAFER_VERSION_MINOR : {show: y, blk : 1, word: 2, pos : 0, len : 4, start : 64, type : 'uint:4', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: Minor chip version, rloc: 'EFUSE_RD_MAC_SYS2_REG[3:0]', bloc: 'B8[3:0]'} - WAFER_VERSION_MAJOR : {show: y, blk : 1, word: 2, pos : 4, len : 2, start : 68, type : 'uint:2', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: Major chip version, rloc: 'EFUSE_RD_MAC_SYS2_REG[5:4]', bloc: 'B8[5:4]'} - DISABLE_WAFER_VERSION_MAJOR : {show: y, blk : 1, word: 2, pos : 6, len : 1, start : 70, type : bool, wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: Disables check of wafer version major, rloc: 'EFUSE_RD_MAC_SYS2_REG[6]', bloc: 'B8[6]'} - DISABLE_BLK_VERSION_MAJOR : {show: y, blk : 1, word: 2, pos : 7, len : 1, start : 71, type : bool, wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: Disables check of blk version major, rloc: 'EFUSE_RD_MAC_SYS2_REG[7]', bloc: 'B8[7]'} - BLK_VERSION_MINOR : {show: y, blk : 1, word: 2, pos : 8, len : 3, start : 72, type : 'uint:3', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: BLK_VERSION_MINOR of BLOCK2, rloc: 'EFUSE_RD_MAC_SYS2_REG[10:8]', bloc: 'B9[2:0]'} - BLK_VERSION_MAJOR : {show: y, blk : 1, word: 2, pos: 11, len : 2, start : 75, type : 'uint:2', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: BLK_VERSION_MAJOR of BLOCK2, rloc: 'EFUSE_RD_MAC_SYS2_REG[12:11]', bloc: 'B9[4:3]'} - FLASH_CAP : {show: y, blk : 1, word: 2, pos: 13, len : 3, start : 77, type : 'uint:3', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: Flash capacity, rloc: 'EFUSE_RD_MAC_SYS2_REG[15:13]', bloc: 'B9[7:5]'} - FLASH_VENDOR : {show: y, blk : 1, word: 2, pos: 16, len : 3, start : 80, type : 'uint:3', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: Flash vendor, rloc: 'EFUSE_RD_MAC_SYS2_REG[18:16]', bloc: 'B10[2:0]'} - PSRAM_CAP : {show: y, blk : 1, word: 2, pos: 19, len : 3, start : 83, type : 'uint:3', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: PSRAM capacity, rloc: 'EFUSE_RD_MAC_SYS2_REG[21:19]', bloc: 'B10[5:3]'} - PSRAM_VENDOR : {show: y, blk : 1, word: 2, pos: 22, len : 2, start : 86, type : 'uint:2', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: PSRAM vendor, rloc: 'EFUSE_RD_MAC_SYS2_REG[23:22]', bloc: 'B10[7:6]'} - TEMP : {show: y, blk : 1, word: 2, pos: 24, len : 2, start : 88, type : 'uint:2', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: Temperature, rloc: 'EFUSE_RD_MAC_SYS2_REG[25:24]', bloc: 'B11[1:0]'} - PKG_VERSION : {show: y, blk : 1, word: 2, pos: 26, len : 3, start : 90, type : 'uint:3', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: Package version, rloc: 'EFUSE_RD_MAC_SYS2_REG[28:26]', bloc: 'B11[4:2]'} - RESERVED_1_93 : {show: n, blk : 1, word: 2, pos: 29, len : 3, start : 93, type : 'uint:3', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: reserved, rloc: 'EFUSE_RD_MAC_SYS2_REG[31:29]', bloc: 'B11[7:5]'} - MAC_RESERVED_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: Reserved, rloc: 'EFUSE_RD_MAC_SYS3_REG[17:0]', bloc: 'B12,B13,B14[1:0]'} - SYS_DATA_PART0_0 : {show: n, blk : 1, word: 3, pos: 18, len : 14, start: 114, type : 'uint:14', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: Represents the first 14-bit of zeroth part of system data, rloc: 'EFUSE_RD_MAC_SYS3_REG[31:18]', bloc: 'B14[7:2],B15'} - SYS_DATA_PART0_1 : {show: n, blk : 1, word: 4, pos : 0, len : 32, start: 128, type : 'uint:32', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: Represents the first 14-bit of zeroth part of system data, rloc: EFUSE_RD_MAC_SYS4_REG, bloc: 'B16,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: Represents the second 32-bit of zeroth part of system data, rloc: EFUSE_RD_MAC_SYS5_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'} - SYS_DATA_PART1_4 : {show: n, blk : 2, word: 4, pos : 0, len : 32, start: 128, type : 'uint:32', wr_dis : 21, rd_dis: null, alt : '', dict : '', desc: Represents the zeroth 32-bit of first part of system data, rloc: EFUSE_RD_SYS_PART1_DATA4_REG, bloc: 'B16,B17,B18,B19'} - SYS_DATA_PART1_5 : {show: n, blk : 2, word: 5, pos : 0, len : 32, start: 160, type : 'uint:32', wr_dis : 21, rd_dis: null, alt : '', dict : '', desc: Represents the zeroth 32-bit of first part of system data, rloc: EFUSE_RD_SYS_PART1_DATA5_REG, bloc: 'B20,B21,B22,B23'} - SYS_DATA_PART1_6 : {show: n, blk : 2, word: 6, pos : 0, len : 32, start: 192, type : 'uint:32', wr_dis : 21, rd_dis: null, alt : '', dict : '', desc: Represents the zeroth 32-bit of first part of system data, rloc: EFUSE_RD_SYS_PART1_DATA6_REG, bloc: 'B24,B25,B26,B27'} - SYS_DATA_PART1_7 : {show: n, blk : 2, word: 7, pos : 0, len : 32, start: 224, type : 'uint:32', wr_dis : 21, rd_dis: null, alt : '', dict : '', desc: Represents the zeroth 32-bit of first part of system data, rloc: EFUSE_RD_SYS_PART1_DATA7_REG, bloc: 'B28,B29,B30,B31'} - 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'} + 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_DIS0_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_ICACHE : {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 cache is disabled. 1: Disabled 0: Enabled.', rloc: 'EFUSE_RD_REPEAT_DATA0_REG[7]', bloc: 'B4[7]'} + DIS_USB_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 the function of usb switch to jtag is disabled or enabled. 1: disabled 0: enabled', rloc: 'EFUSE_RD_REPEAT_DATA0_REG[8]', bloc: 'B5[0]'} + DIS_USB_SERIAL_JTAG : {show: n, blk : 0, word: 1, pos : 9, len : 1, start : 41, 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[9]', bloc: 'B5[1]'} + DIS_FORCE_DOWNLOAD : {show: y, blk : 0, word: 1, pos: 10, len : 1, start : 42, 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[10]', bloc: 'B5[2]'} + SPI_DOWNLOAD_MSPI_DIS : {show: y, blk : 0, word: 1, pos: 11, len : 1, start : 43, type : bool, wr_dis : 2, 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[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]'} + USB_DREFH : {show: n, blk : 0, word: 1, pos: 15, len : 2, start : 47, type : 'uint:2', wr_dis : 30, rd_dis: null, alt : '', dict : '', desc: Represents the single-end input threshold vrefh of USB_SERIAL_JTAG PHY; 1.76 V to 2 V with step of 80 mV, rloc: 'EFUSE_RD_REPEAT_DATA0_REG[16:15]', bloc: 'B5[7],B6[0]'} + USB_DREFL : {show: n, blk : 0, word: 1, pos: 17, len : 2, start : 49, type : 'uint:2', wr_dis : 30, rd_dis: null, alt : '', dict : '', desc: Represents the single-end input threshold vrefl of USB_SERIAL_JTAG PHY; 1.76 V to 2 V with step of 80 mV, rloc: 'EFUSE_RD_REPEAT_DATA0_REG[18:17]', bloc: 'B6[2:1]'} + USB_EXCHG_PINS : {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 the D+ and D- pins of USB_SERIAL_JTAG PHY is exchanged. 1: exchanged 0: not exchanged', rloc: 'EFUSE_RD_REPEAT_DATA0_REG[19]', bloc: 'B6[3]'} + VDD_SPI_AS_GPIO : {show: y, blk : 0, word: 1, pos: 20, len : 1, start : 52, type : bool, wr_dis : 30, rd_dis: null, alt : '', dict : '', desc: 'Represents whether vdd spi pin is functioned as gpio. 1: functioned 0: not functioned', rloc: 'EFUSE_RD_REPEAT_DATA0_REG[20]', bloc: 'B6[4]'} + WDT_DELAY_SEL : {show: y, blk : 0, word: 1, pos: 21, len : 2, start : 53, type : 'uint:2', wr_dis : 3, rd_dis: null, alt : '', dict : '', desc: lp wdt timeout threshold at startup = initial timeout value * (2 ^ (EFUSE_WDT_DELAY_SEL + 1)), rloc: 'EFUSE_RD_REPEAT_DATA0_REG[22:21]', bloc: 'B6[6:5]'} + 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[26]', bloc: 'B7[2]'} + 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[27]', bloc: 'B7[3]'} + 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[28]', bloc: 'B7[4]'} + RESERVE_0_61 : {show: n, blk : 0, word: 1, pos: 29, len : 3, start : 61, type : 'uint:3', wr_dis: null, rd_dis: null, alt : '', dict : '', desc: Reserved; it was created by set_missed_fields_in_regs func, rloc: 'EFUSE_RD_REPEAT_DATA0_REG[31:29]', bloc: 'B7[7:5]'} + KEY_PURPOSE_0 : {show: y, blk : 0, word: 2, pos : 0, len : 4, start : 64, type : 'uint:4', wr_dis : 8, rd_dis: null, alt : KEY0_PURPOSE, dict : '', desc: Represents the purpose of Key0, rloc: 'EFUSE_RD_REPEAT_DATA1_REG[3:0]', bloc: 'B8[3:0]'} + KEY_PURPOSE_1 : {show: y, blk : 0, word: 2, pos : 4, len : 4, start : 68, type : 'uint:4', wr_dis : 9, rd_dis: null, alt : KEY1_PURPOSE, dict : '', desc: Represents the purpose of Key1, rloc: 'EFUSE_RD_REPEAT_DATA1_REG[7:4]', bloc: 'B8[7:4]'} + KEY_PURPOSE_2 : {show: y, blk : 0, word: 2, pos : 8, len : 4, start : 72, type : 'uint:4', wr_dis : 10, rd_dis: null, alt : KEY2_PURPOSE, dict : '', desc: Represents the purpose of Key2, rloc: 'EFUSE_RD_REPEAT_DATA1_REG[11:8]', bloc: 'B9[3:0]'} + KEY_PURPOSE_3 : {show: y, blk : 0, word: 2, pos: 12, len : 4, start : 76, type : 'uint:4', wr_dis : 11, rd_dis: null, alt : KEY3_PURPOSE, dict : '', desc: Represents the purpose of Key3, rloc: 'EFUSE_RD_REPEAT_DATA1_REG[15:12]', bloc: 'B9[7:4]'} + KEY_PURPOSE_4 : {show: y, blk : 0, word: 2, pos: 16, len : 4, start : 80, type : 'uint:4', wr_dis : 12, rd_dis: null, alt : KEY4_PURPOSE, dict : '', desc: Represents the purpose of Key4, rloc: 'EFUSE_RD_REPEAT_DATA1_REG[19:16]', bloc: 'B10[3:0]'} + KEY_PURPOSE_5 : {show: y, blk : 0, word: 2, pos: 20, len : 4, start : 84, type : 'uint:4', wr_dis : 13, rd_dis: null, alt : KEY5_PURPOSE, dict : '', desc: Represents the purpose of Key5, rloc: 'EFUSE_RD_REPEAT_DATA1_REG[23:20]', bloc: 'B10[7:4]'} + SEC_DPA_LEVEL : {show: y, blk : 0, word: 2, pos: 24, len : 2, start : 88, type : 'uint:2', wr_dis : 14, rd_dis: null, alt : '', dict : '', desc: Represents the spa secure level by configuring the clock random divide mode, rloc: 'EFUSE_RD_REPEAT_DATA1_REG[25:24]', bloc: 'B11[1:0]'} + SECURE_BOOT_EN : {show: y, blk : 0, word: 2, pos: 26, len : 1, start : 90, type : bool, wr_dis : 15, rd_dis: null, alt : '', dict : '', desc: 'Represents whether secure boot is enabled or disabled. 1. Enable 0: Disable', rloc: 'EFUSE_RD_REPEAT_DATA1_REG[26]', bloc: 'B11[2]'} + SECURE_BOOT_AGGRESSIVE_REVOKE : {show: y, blk : 0, word: 2, pos: 27, len : 1, start : 91, type : bool, wr_dis : 16, rd_dis: null, alt : '', dict : '', desc: 'Represents whether revoking aggressive secure boot is enabled or disabled. 1. Enable 0: Disable', rloc: 'EFUSE_RD_REPEAT_DATA1_REG[27]', bloc: 'B11[3]'} + FLASH_TPUW : {show: y, blk : 0, word: 2, pos: 28, len : 4, start : 92, type : 'uint:4', 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 programmed value. Otherwise; the waiting time is 2 times the programmed value, rloc: 'EFUSE_RD_REPEAT_DATA1_REG[31:28]', bloc: 'B11[7:4]'} + DIS_DOWNLOAD_MODE : {show: y, blk : 0, word: 3, pos : 0, len : 1, start : 96, type : bool, wr_dis : 18, rd_dis: null, alt : '', dict : '', desc: 'Represents whether Download mode is disable or enable. 1. Disable 0: Enable', rloc: 'EFUSE_RD_REPEAT_DATA2_REG[0]', bloc: 'B12[0]'} + DIS_DIRECT_BOOT : {show: y, blk : 0, word: 3, pos : 1, len : 1, start : 97, type : bool, wr_dis : 18, rd_dis: null, alt : '', dict : '', desc: 'Represents whether direct boot mode is disabled or enabled. 1. Disable 0: Enable', rloc: 'EFUSE_RD_REPEAT_DATA2_REG[1]', bloc: 'B12[1]'} + DIS_USB_SERIAL_JTAG_ROM_PRINT : {show: y, blk : 0, word: 3, pos : 2, len : 1, start : 98, type : bool, wr_dis : 18, rd_dis: null, alt : '', dict : '', desc: 'Represents whether print from USB-Serial-JTAG is disabled or enabled. 1. Disable 0: Enable', rloc: 'EFUSE_RD_REPEAT_DATA2_REG[2]', bloc: 'B12[2]'} + DIS_USB_SERIAL_JTAG_DOWNLOAD_MODE : {show: y, blk : 0, word: 3, pos : 3, len : 1, start : 99, type : bool, wr_dis : 18, rd_dis: null, alt : '', dict : '', desc: 'Represents whether the USB-Serial-JTAG download function is disabled or enabled. 1: Disable 0: Enable', rloc: 'EFUSE_RD_REPEAT_DATA2_REG[3]', bloc: 'B12[3]'} + ENABLE_SECURITY_DOWNLOAD : {show: y, blk : 0, word: 3, pos : 4, len : 1, start: 100, type : bool, wr_dis : 18, rd_dis: null, alt : '', dict : '', desc: 'Represents whether security download is enabled or disabled. 1: Enable 0: Disable', rloc: 'EFUSE_RD_REPEAT_DATA2_REG[4]', bloc: 'B12[4]'} + UART_PRINT_CONTROL : {show: y, blk : 0, word: 3, pos : 5, len : 2, start: 101, type : 'uint:2', wr_dis : 18, rd_dis: null, alt : '', dict : '', desc: Represents the types of UART printing, rloc: 'EFUSE_RD_REPEAT_DATA2_REG[6:5]', bloc: 'B12[6:5]'} + FORCE_SEND_RESUME : {show: y, blk : 0, word: 3, pos : 7, len : 1, start: 103, 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, rloc: 'EFUSE_RD_REPEAT_DATA2_REG[7]', bloc: 'B12[7]'} + SECURE_VERSION : {show: y, blk : 0, word: 3, pos : 8, len : 16, start: 104, 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_DATA2_REG[23:8]', bloc: 'B13,B14'} + SECURE_BOOT_DISABLE_FAST_WAKE : {show: y, blk : 0, word: 3, pos: 24, len : 1, start: 120, type : bool, wr_dis : 18, rd_dis: null, alt : '', dict : '', desc: Represents whether FAST_VERIFY_ON_WAKE is disable or enable when Secure Boot is enable, rloc: 'EFUSE_RD_REPEAT_DATA2_REG[24]', bloc: 'B15[0]'} + HYS_EN_PAD : {show: y, blk : 0, word: 3, pos: 25, len : 1, start: 121, type : bool, wr_dis : 2, rd_dis: null, alt : '', dict : '', desc: Set bits to enable hysteresis function of PAD0~27, rloc: 'EFUSE_RD_REPEAT_DATA2_REG[25]', bloc: 'B15[1]'} + XTS_DPA_CLK_ENABLE : {show: y, blk : 0, word: 3, pos: 26, len : 1, start: 122, type : bool, wr_dis : 14, rd_dis: null, alt : '', dict : '', desc: 'Represents whether xts-aes anti-dpa attack clock is enabled. 1. Enable. 0: Disable.', rloc: 'EFUSE_RD_REPEAT_DATA2_REG[26]', bloc: 'B15[2]'} + XTS_DPA_PSEUDO_LEVEL : {show: y, blk : 0, word: 3, pos: 27, len : 2, start: 123, type : 'uint:2', wr_dis : 14, rd_dis: null, alt : '', dict : '', desc: 'Represents the pseudo round level of xts-aes anti-dpa attack. 3: High. 2: Moderate 1. Low 0: Disabled', rloc: 'EFUSE_RD_REPEAT_DATA2_REG[28:27]', bloc: 'B15[4:3]'} + DIS_WIFI6 : {show: y, blk : 0, word: 3, pos: 29, len : 1, start: 125, type : bool, wr_dis : 2, rd_dis: null, alt : '', dict : '', desc: 'Represents whether the WIFI6 feature is enable or disabled. 1: WIFI6 is disable; 0: WIFI6 is enabled', rloc: 'EFUSE_RD_REPEAT_DATA2_REG[29]', bloc: 'B15[5]'} + ECDSA_DISABLE_P192 : {show: y, blk : 0, word: 3, pos: 30, len : 1, start: 126, type : bool, wr_dis : 14, rd_dis: null, alt : '', dict : '', desc: 'Represents whether to disable P192 curve in ECDSA. 1: Disabled. 0: Not disabled', rloc: 'EFUSE_RD_REPEAT_DATA2_REG[30]', bloc: 'B15[6]'} + ECC_FORCE_CONST_TIME : {show: y, blk : 0, word: 3, pos: 31, len : 1, start: 127, type : bool, wr_dis : 14, rd_dis: null, alt : '', dict : '', desc: 'Represents whether to force ecc to use const-time calculation mode. 1: Enable. 0: Disable', rloc: 'EFUSE_RD_REPEAT_DATA2_REG[31]', bloc: 'B15[7]'} + BOOTLOADER_ANTI_ROLLBACK_SECURE_VERSION: {show: y, blk : 0, word: 4, pos : 0, len : 4, start: 128, type : 'uint:4', wr_dis: null, rd_dis: null, alt : '', dict : '', desc: Represents the anti-rollback secure version of the 2nd stage bootloader used by the ROM bootloader, rloc: 'EFUSE_RD_REPEAT_DATA3_REG[3:0]', bloc: 'B16[3:0]'} + BOOTLOADER_ANTI_ROLLBACK_EN : {show: y, blk : 0, word: 4, pos : 4, len : 1, start: 132, type : bool, wr_dis: null, rd_dis: null, alt : '', dict : '', desc: 'Represents whether the ani-rollback check for the 2nd stage bootloader is enabled.1: Enabled0: Disabled', rloc: 'EFUSE_RD_REPEAT_DATA3_REG[4]', bloc: 'B16[4]'} + BOOTLOADER_ANTI_ROLLBACK_UPDATE_IN_ROM : {show: y, blk : 0, word: 4, pos : 5, len : 1, start: 133, type : bool, wr_dis: null, rd_dis: null, alt : '', dict : '', desc: 'Represents whether the ani-rollback SECURE_VERSION will be updated from the ROM bootloader.1: Enable0: Disable', rloc: 'EFUSE_RD_REPEAT_DATA3_REG[5]', bloc: 'B16[5]'} + RECOVERY_BOOTLOADER_FLASH_SECTOR : {show: y, blk : 0, word: 4, pos : 6, len : 12, start: 134, type : 'uint:12', wr_dis: null, rd_dis: null, alt : '', dict : '', desc: Represents the starting flash sector (flash sector size is 0x1000) of the recovery bootloader used by the ROM bootloader If the primary bootloader fails. 0 and 0xFFF - this feature is disabled, rloc: 'EFUSE_RD_REPEAT_DATA3_REG[17:6]', bloc: 'B16[7:6],B17,B18[1:0]'} + RESERVE_0_146 : {show: n, blk : 0, word: 4, pos: 18, len : 14, start: 146, type : 'uint:14', wr_dis: null, rd_dis: null, alt : '', dict : '', desc: Reserved; it was created by set_missed_fields_in_regs func, rloc: 'EFUSE_RD_REPEAT_DATA3_REG[31:18]', bloc: 'B18[7:2],B19'} + REPEAT_DATA4 : {show: y, blk : 0, word: 5, pos : 0, len : 24, start: 160, type : 'uint:24', wr_dis: null, rd_dis: null, alt : '', dict : '', desc: Reserved, rloc: 'EFUSE_RD_REPEAT_DATA4_REG[23:0]', bloc: 'B20,B21,B22'} + RESERVE_0_184 : {show: n, blk : 0, word: 5, pos: 24, len : 8, start: 184, type : 'uint:8', wr_dis: null, rd_dis: null, alt : '', dict : '', desc: Reserved; it was created by set_missed_fields_in_regs func, rloc: 'EFUSE_RD_REPEAT_DATA4_REG[31:24]', bloc: B23} + 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_SYS0_REG, bloc: 'B0,B1,B2,B3,B4,B5'} + RESERVE_1_48 : {show: n, blk : 1, word: 1, pos: 16, len : 16, start : 48, type : 'uint:16', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: Reserved; it was created by set_missed_fields_in_regs func, rloc: 'EFUSE_RD_MAC_SYS1_REG[31:16]', bloc: 'B6,B7'} + WAFER_VERSION_MINOR : {show: y, blk : 1, word: 2, pos : 0, len : 4, start : 64, type : 'uint:4', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: Minor chip version, rloc: 'EFUSE_RD_MAC_SYS2_REG[3:0]', bloc: 'B8[3:0]'} + WAFER_VERSION_MAJOR : {show: y, blk : 1, word: 2, pos : 4, len : 2, start : 68, type : 'uint:2', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: Major chip version, rloc: 'EFUSE_RD_MAC_SYS2_REG[5:4]', bloc: 'B8[5:4]'} + DISABLE_WAFER_VERSION_MAJOR : {show: y, blk : 1, word: 2, pos : 6, len : 1, start : 70, type : bool, wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: Disables check of wafer version major, rloc: 'EFUSE_RD_MAC_SYS2_REG[6]', bloc: 'B8[6]'} + DISABLE_BLK_VERSION_MAJOR : {show: y, blk : 1, word: 2, pos : 7, len : 1, start : 71, type : bool, wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: Disables check of blk version major, rloc: 'EFUSE_RD_MAC_SYS2_REG[7]', bloc: 'B8[7]'} + BLK_VERSION_MINOR : {show: y, blk : 1, word: 2, pos : 8, len : 3, start : 72, type : 'uint:3', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: BLK_VERSION_MINOR of BLOCK2, rloc: 'EFUSE_RD_MAC_SYS2_REG[10:8]', bloc: 'B9[2:0]'} + BLK_VERSION_MAJOR : {show: y, blk : 1, word: 2, pos: 11, len : 2, start : 75, type : 'uint:2', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: BLK_VERSION_MAJOR of BLOCK2, rloc: 'EFUSE_RD_MAC_SYS2_REG[12:11]', bloc: 'B9[4:3]'} + FLASH_CAP : {show: y, blk : 1, word: 2, pos: 13, len : 3, start : 77, type : 'uint:3', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: Flash capacity, rloc: 'EFUSE_RD_MAC_SYS2_REG[15:13]', bloc: 'B9[7:5]'} + FLASH_VENDOR : {show: y, blk : 1, word: 2, pos: 16, len : 3, start : 80, type : 'uint:3', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: Flash vendor, rloc: 'EFUSE_RD_MAC_SYS2_REG[18:16]', bloc: 'B10[2:0]'} + PSRAM_CAP : {show: y, blk : 1, word: 2, pos: 19, len : 3, start : 83, type : 'uint:3', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: PSRAM capacity, rloc: 'EFUSE_RD_MAC_SYS2_REG[21:19]', bloc: 'B10[5:3]'} + PSRAM_VENDOR : {show: y, blk : 1, word: 2, pos: 22, len : 2, start : 86, type : 'uint:2', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: PSRAM vendor, rloc: 'EFUSE_RD_MAC_SYS2_REG[23:22]', bloc: 'B10[7:6]'} + TEMP : {show: y, blk : 1, word: 2, pos: 24, len : 2, start : 88, type : 'uint:2', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: Temperature, rloc: 'EFUSE_RD_MAC_SYS2_REG[25:24]', bloc: 'B11[1:0]'} + PKG_VERSION : {show: y, blk : 1, word: 2, pos: 26, len : 3, start : 90, type : 'uint:3', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: Package version, rloc: 'EFUSE_RD_MAC_SYS2_REG[28:26]', bloc: 'B11[4:2]'} + ACTIVE_HP_DBIAS : {show: y, blk : 1, word: 2, pos: 29, len : 4, start : 93, type : 'uint:4', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: Active HP DBIAS of fixed voltage, rloc: 'EFUSE_RD_MAC_SYS2_REG[31:29]', bloc: 'B11[7:5],B12[0]'} + ACTIVE_LP_DBIAS : {show: y, blk : 1, word: 3, pos : 1, len : 4, start : 97, type : 'uint:4', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: Active LP DBIAS of fixed voltage, rloc: 'EFUSE_RD_MAC_SYS3_REG[4:1]', bloc: 'B12[4:1]'} + LSLP_HP_DBG : {show: y, blk : 1, word: 3, pos : 5, len : 2, start: 101, type : 'uint:2', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: LSLP HP DBG of fixed voltage, rloc: 'EFUSE_RD_MAC_SYS3_REG[6:5]', bloc: 'B12[6:5]'} + LSLP_HP_DBIAS : {show: y, blk : 1, word: 3, pos : 7, len : 4, start: 103, type : 'uint:4', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: LSLP HP DBIAS of fixed voltage, rloc: 'EFUSE_RD_MAC_SYS3_REG[10:7]', bloc: 'B12[7],B13[2:0]'} + DSLP_LP_DBG : {show: y, blk : 1, word: 3, pos: 11, len : 4, start: 107, type : 'uint:4', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: DSLP LP DBG of fixed voltage, rloc: 'EFUSE_RD_MAC_SYS3_REG[14:11]', bloc: 'B13[6:3]'} + DSLP_LP_DBIAS : {show: y, blk : 1, word: 3, pos: 15, len : 5, start: 111, type : 'uint:5', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: DSLP LP DBIAS of fixed voltage, rloc: 'EFUSE_RD_MAC_SYS3_REG[19:15]', bloc: 'B13[7],B14[3:0]'} + LP_HP_DBIAS_VOL_GAP : {show: y, blk : 1, word: 3, pos: 20, len : 5, start: 116, type : 'uint:5', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: DBIAS gap between LP and HP, rloc: 'EFUSE_RD_MAC_SYS3_REG[24:20]', bloc: 'B14[7:4],B15[0]'} + RESERVED_1_121 : {show: n, blk : 1, word: 3, pos: 25, len : 7, start: 121, type : 'uint:7', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: reserved, rloc: 'EFUSE_RD_MAC_SYS3_REG[31:25]', bloc: 'B15[7:1]'} + SYS_DATA_PART0_1 : {show: n, blk : 1, word: 4, pos : 0, len : 32, start: 128, type : 'uint:32', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: Represents the first 14-bit of zeroth part of system data, rloc: EFUSE_RD_MAC_SYS4_REG, bloc: 'B16,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: Represents the second 32-bit of zeroth part of system data, rloc: EFUSE_RD_MAC_SYS5_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'} + TEMPERATURE_SENSOR : {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 calibration, rloc: 'EFUSE_RD_SYS_PART1_DATA4_REG[16:9]', bloc: 'B17[7:1],B18[0]'} + ADC1_AVE_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: Average initcode of ADC1 atten0, rloc: 'EFUSE_RD_SYS_PART1_DATA4_REG[26:17]', bloc: 'B18[7:1],B19[2:0]'} + ADC1_AVE_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: Average initcode of ADC1 atten1, rloc: 'EFUSE_RD_SYS_PART1_DATA4_REG[31:27]', bloc: 'B19[7:3],B20[4:0]'} + ADC1_AVE_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: Average initcode of ADC1 atten2, rloc: 'EFUSE_RD_SYS_PART1_DATA5_REG[14:5]', bloc: 'B20[7:5],B21[6:0]'} + ADC1_AVE_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: Average initcode of ADC1 atten3, rloc: 'EFUSE_RD_SYS_PART1_DATA5_REG[24:15]', bloc: 'B21[7],B22,B23[0]'} + ADC1_HI_DOUT_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: HI_DOUT of ADC1 atten0, rloc: 'EFUSE_RD_SYS_PART1_DATA5_REG[31:25]', bloc: 'B23[7:1],B24[2:0]'} + ADC1_HI_DOUT_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: HI_DOUT of ADC1 atten1, rloc: 'EFUSE_RD_SYS_PART1_DATA6_REG[12:3]', bloc: 'B24[7:3],B25[4:0]'} + ADC1_HI_DOUT_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: HI_DOUT of ADC1 atten2, rloc: 'EFUSE_RD_SYS_PART1_DATA6_REG[22:13]', bloc: 'B25[7:5],B26[6:0]'} + ADC1_HI_DOUT_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: HI_DOUT of ADC1 atten3, rloc: 'EFUSE_RD_SYS_PART1_DATA6_REG[31:23]', bloc: 'B26[7],B27,B28[0]'} + ADC1_CH0_ATTEN0_INITCODE_DIFF : {show: y, blk : 2, word: 7, pos : 1, len : 4, start: 225, type : 'uint:4', wr_dis : 21, rd_dis: null, alt : '', dict : '', desc: Gap between ADC1 CH0 and average initcode, rloc: 'EFUSE_RD_SYS_PART1_DATA7_REG[4:1]', bloc: 'B28[4:1]'} + ADC1_CH1_ATTEN0_INITCODE_DIFF : {show: y, blk : 2, word: 7, pos : 5, len : 4, start: 229, type : 'uint:4', wr_dis : 21, rd_dis: null, alt : '', dict : '', desc: Gap between ADC1 CH1 and average initcode, rloc: 'EFUSE_RD_SYS_PART1_DATA7_REG[8:5]', bloc: 'B28[7:5],B29[0]'} + ADC1_CH2_ATTEN0_INITCODE_DIFF : {show: y, blk : 2, word: 7, pos : 9, len : 4, start: 233, type : 'uint:4', wr_dis : 21, rd_dis: null, alt : '', dict : '', desc: Gap between ADC1 CH2 and average initcode, rloc: 'EFUSE_RD_SYS_PART1_DATA7_REG[12:9]', bloc: 'B29[4:1]'} + ADC1_CH3_ATTEN0_INITCODE_DIFF : {show: y, blk : 2, word: 7, pos: 13, len : 4, start: 237, type : 'uint:4', wr_dis : 21, rd_dis: null, alt : '', dict : '', desc: Gap between ADC1 CH3 and average initcode, rloc: 'EFUSE_RD_SYS_PART1_DATA7_REG[16:13]', bloc: 'B29[7:5],B30[0]'} + RESERVED_2_241 : {show: n, blk : 2, word: 7, pos: 17, len : 15, start: 241, type : 'uint:15', wr_dis : 21, rd_dis: null, alt : '', dict : '', desc: reserved, rloc: 'EFUSE_RD_SYS_PART1_DATA7_REG[31:17]', bloc: 'B30[7:1],B31'} + 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'} diff --git a/tools/esptool_py/espefuse/efuse_defs/esp32h2.yaml b/tools/esptool_py/espefuse/efuse_defs/esp32h2.yaml index 7dddf5108e..ecd3df3e9d 100644 --- a/tools/esptool_py/espefuse/efuse_defs/esp32h2.yaml +++ b/tools/esptool_py/espefuse/efuse_defs/esp32h2.yaml @@ -1,4 +1,4 @@ -VER_NO: ef562916e77cf77203c1a4c0cff35ac5 +VER_NO: 44563d2af4ebdba4db6c0a34a50c94f9 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]'} @@ -18,9 +18,9 @@ EFUSES: USB_DREFL : {show: n, blk : 0, word: 1, pos: 23, len : 2, start : 55, type : 'uint:2', wr_dis : 30, rd_dis: null, alt : '', dict : '', desc: Represents the single-end input threshold vrefl; 1.76 V to 2 V with step of 80 mV, rloc: 'EFUSE_RD_REPEAT_DATA0_REG[24:23]', bloc: 'B6[7],B7[0]'} USB_EXCHG_PINS : {show: y, blk : 0, word: 1, pos: 25, len : 1, start : 57, type : bool, wr_dis : 30, rd_dis: null, alt : '', dict : '', desc: 'Represents whether the D+ and D- pins is exchanged. 1: exchanged. 0: not exchanged', rloc: 'EFUSE_RD_REPEAT_DATA0_REG[25]', bloc: 'B7[1]'} VDD_SPI_AS_GPIO : {show: y, blk : 0, word: 1, pos: 26, len : 1, start : 58, type : bool, wr_dis : 30, rd_dis: null, alt : '', dict : '', desc: 'Represents whether vdd spi pin is functioned as gpio. 1: functioned. 0: not functioned', rloc: 'EFUSE_RD_REPEAT_DATA0_REG[26]', bloc: 'B7[2]'} - RPT4_RESERVED0_2 : {show: n, blk : 0, word: 1, pos: 27, len : 2, start : 59, type : 'uint:2', wr_dis: null, rd_dis: null, alt : '', dict : '', desc: Reserved, rloc: 'EFUSE_RD_REPEAT_DATA0_REG[28:27]', bloc: 'B7[4:3]'} - RPT4_RESERVED0_1 : {show: n, blk : 0, word: 1, pos: 29, len : 1, start : 61, type : bool, wr_dis: null, rd_dis: null, alt : '', dict : '', desc: Reserved, rloc: 'EFUSE_RD_REPEAT_DATA0_REG[29]', bloc: 'B7[5]'} - RPT4_RESERVED0_0 : {show: n, blk : 0, word: 1, pos: 30, len : 2, start : 62, type : 'uint:2', wr_dis: null, rd_dis: null, alt : '', dict : '', desc: Reserved, rloc: 'EFUSE_RD_REPEAT_DATA0_REG[31:30]', bloc: 'B7[7:6]'} + ECDSA_CURVE_MODE : {show: y, blk : 0, word: 1, pos: 27, len : 2, start : 59, type : 'uint:2', wr_dis : 17, rd_dis: null, alt : '', dict : '', desc: 'Configures the curve of ECDSA calculation: 0: only enable P256. 1: only enable P192. 2: both enable P256 and P192. 3: only enable P256', rloc: 'EFUSE_RD_REPEAT_DATA0_REG[28:27]', bloc: 'B7[4:3]'} + ECC_FORCE_CONST_TIME : {show: y, blk : 0, word: 1, pos: 29, len : 1, start : 61, type : bool, wr_dis : 17, rd_dis: null, alt : '', dict : '', desc: Set this bit to permanently turn on ECC const-time mode, rloc: 'EFUSE_RD_REPEAT_DATA0_REG[29]', bloc: 'B7[5]'} + XTS_DPA_PSEUDO_LEVEL : {show: y, blk : 0, word: 1, pos: 30, len : 2, start : 62, type : 'uint:2', wr_dis : 14, rd_dis: null, alt : '', dict : '', desc: 'Set this bit to control the xts pseudo-round anti-dpa attack function: 0: controlled by register. 1-3: the higher the value is; the more pseudo-rounds are inserted to the xts-aes calculation', rloc: 'EFUSE_RD_REPEAT_DATA0_REG[31:30]', bloc: 'B7[7:6]'} RPT4_RESERVED1_1 : {show: n, blk : 0, word: 2, pos : 0, len : 16, start : 64, type : 'uint:16', wr_dis: null, rd_dis: null, alt : '', dict : '', desc: Reserved, rloc: 'EFUSE_RD_REPEAT_DATA1_REG[15:0]', bloc: 'B8,B9'} WDT_DELAY_SEL : {show: y, blk : 0, word: 2, pos: 16, len : 2, start : 80, type : 'uint:2', wr_dis : 3, rd_dis: null, alt : '', dict : '', desc: 'Represents whether RTC watchdog timeout threshold is selected at startup. 1: selected. 0: not selected', rloc: 'EFUSE_RD_REPEAT_DATA1_REG[17:16]', bloc: 'B10[1:0]'} SPI_BOOT_CRYPT_CNT : {show: y, blk : 0, word: 2, pos: 18, len : 3, start : 82, 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_DATA1_REG[20:18]', bloc: 'B10[4:2]'} @@ -34,11 +34,12 @@ EFUSES: KEY_PURPOSE_4 : {show: y, blk : 0, word: 3, pos : 8, len : 4, start: 104, type : 'uint:4', wr_dis : 12, rd_dis: null, alt : KEY4_PURPOSE, dict : '', desc: Represents the purpose of Key4, rloc: 'EFUSE_RD_REPEAT_DATA2_REG[11:8]', bloc: 'B13[3:0]'} KEY_PURPOSE_5 : {show: y, blk : 0, word: 3, pos: 12, len : 4, start: 108, type : 'uint:4', wr_dis : 13, rd_dis: null, alt : KEY5_PURPOSE, dict : '', desc: Represents the purpose of Key5, rloc: 'EFUSE_RD_REPEAT_DATA2_REG[15:12]', bloc: 'B13[7:4]'} SEC_DPA_LEVEL : {show: y, blk : 0, word: 3, pos: 16, len : 2, start: 112, type : 'uint:2', wr_dis : 14, rd_dis: null, alt : '', dict : '', desc: Represents the spa secure level by configuring the clock random divide mode, rloc: 'EFUSE_RD_REPEAT_DATA2_REG[17:16]', bloc: 'B14[1:0]'} - ECDSA_FORCE_USE_HARDWARE_K : {show: y, blk : 0, word: 3, pos: 18, len : 1, start: 114, type : bool, wr_dis : 17, rd_dis: null, alt : '', dict : '', desc: 'Represents whether hardware random number k is forced used in ESDCA. 1: force used. 0: not force used', rloc: 'EFUSE_RD_REPEAT_DATA2_REG[18]', bloc: 'B14[2]'} + RESERVE_0_114 : {show: n, blk : 0, word: 3, pos: 18, len : 1, start: 114, type : bool, wr_dis : 17, rd_dis: null, alt : '', dict : '', desc: Reserved, rloc: 'EFUSE_RD_REPEAT_DATA2_REG[18]', bloc: 'B14[2]'} CRYPT_DPA_ENABLE : {show: y, blk : 0, word: 3, pos: 19, len : 1, start: 115, type : bool, wr_dis : 14, rd_dis: null, alt : '', dict : '', desc: 'Represents whether anti-dpa attack is enabled. 1:enabled. 0: disabled', rloc: 'EFUSE_RD_REPEAT_DATA2_REG[19]', bloc: 'B14[3]'} SECURE_BOOT_EN : {show: y, blk : 0, word: 3, pos: 20, len : 1, start: 116, 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[20]', bloc: 'B14[4]'} SECURE_BOOT_AGGRESSIVE_REVOKE : {show: y, blk : 0, word: 3, pos: 21, len : 1, start: 117, 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[21]', bloc: 'B14[5]'} - RPT4_RESERVED2_0 : {show: n, blk : 0, word: 3, pos: 22, len : 6, start: 118, type : 'uint:6', wr_dis: null, rd_dis: null, alt : '', dict : '', desc: Reserved, rloc: 'EFUSE_RD_REPEAT_DATA2_REG[27:22]', bloc: 'B14[7:6],B15[3:0]'} + POWERGLITCH_EN1 : {show: y, blk : 0, word: 3, pos: 22, len : 5, start: 118, type : 'uint:5', wr_dis : 2, rd_dis: null, alt : '', dict : '', desc: Set these bits to enable power glitch function when chip power on, rloc: 'EFUSE_RD_REPEAT_DATA2_REG[26:22]', bloc: 'B14[7:6],B15[2:0]'} + RESERVED_0_123 : {show: n, blk : 0, word: 3, pos: 27, len : 1, start: 123, type : bool, wr_dis: null, rd_dis: null, alt : '', dict : '', desc: reserved, rloc: 'EFUSE_RD_REPEAT_DATA2_REG[27]', bloc: 'B15[3]'} FLASH_TPUW : {show: y, blk : 0, word: 3, pos: 28, len : 4, start: 124, type : 'uint:4', 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[31:28]', bloc: 'B15[7:4]'} DIS_DOWNLOAD_MODE : {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 Download mode is disabled or enabled. 1: disabled. 0: enabled', rloc: 'EFUSE_RD_REPEAT_DATA3_REG[0]', bloc: 'B16[0]'} DIS_DIRECT_BOOT : {show: y, blk : 0, word: 4, pos : 1, len : 1, start: 129, 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[1]', bloc: 'B16[1]'} diff --git a/tools/esptool_py/espefuse/efuse_defs/esp32c5beta3.yaml b/tools/esptool_py/espefuse/efuse_defs/esp32h21.yaml similarity index 55% rename from tools/esptool_py/espefuse/efuse_defs/esp32c5beta3.yaml rename to tools/esptool_py/espefuse/efuse_defs/esp32h21.yaml index 31af46a56d..878b44651e 100644 --- a/tools/esptool_py/espefuse/efuse_defs/esp32c5beta3.yaml +++ b/tools/esptool_py/espefuse/efuse_defs/esp32h21.yaml @@ -1,31 +1,28 @@ -VER_NO: b09fa417de505238a601eddce188b696 +VER_NO: ef562916e77cf77203c1a4c0cff35ac5 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_DIS0_REG, bloc: 'B0,B1,B2,B3'} + 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]'} - RESERVE_0_39 : {show: n, blk : 0, word: 1, pos : 7, len : 1, start : 39, type : bool, wr_dis: null, rd_dis: null, alt : '', dict : '', desc: Reserved; it was created by set_missed_fields_in_regs func, rloc: 'EFUSE_RD_REPEAT_DATA0_REG[7]', bloc: 'B4[7]'} - DIS_ICACHE : {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 icache is disabled or enabled.\\ 1: disabled\\ 0: enabled\\', rloc: 'EFUSE_RD_REPEAT_DATA0_REG[8]', bloc: 'B5[0]'} - DIS_USB_JTAG : {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 of usb switch to jtag is disabled or enabled.\\ 1: disabled\\ 0: enabled\\', rloc: 'EFUSE_RD_REPEAT_DATA0_REG[9]', bloc: 'B5[1]'} - RESERVE_0_42 : {show: n, blk : 0, word: 1, pos: 10, len : 1, start : 42, type : bool, wr_dis: null, rd_dis: null, alt : '', dict : '', desc: Reserved; it was created by set_missed_fields_in_regs func, rloc: 'EFUSE_RD_REPEAT_DATA0_REG[10]', bloc: 'B5[2]'} - DIS_USB_SERIAL_JTAG : {show: n, blk : 0, word: 1, pos: 11, len : 1, start : 43, 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[11]', bloc: 'B5[3]'} - DIS_FORCE_DOWNLOAD : {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 function that forces chip into download mode is disabled or enabled.\\ 1: disabled\\ 0: enabled\\', rloc: 'EFUSE_RD_REPEAT_DATA0_REG[12]', bloc: 'B5[4]'} - SPI_DOWNLOAD_MSPI_DIS : {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 SPI0 controller during boot_mode_download is disabled or enabled.\\ 1: disabled\\ 0: enabled\\', rloc: 'EFUSE_RD_REPEAT_DATA0_REG[13]', bloc: 'B5[5]'} - DIS_TWAI : {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 TWAI function is disabled or enabled.\\ 1: disabled\\ 0: enabled\\', rloc: 'EFUSE_RD_REPEAT_DATA0_REG[14]', bloc: 'B5[6]'} - JTAG_SEL_ENABLE : {show: y, blk : 0, word: 1, pos: 15, len : 1, start : 47, 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[15]', bloc: 'B5[7]'} - SOFT_DIS_JTAG : {show: y, blk : 0, word: 1, pos: 16, len : 3, start : 48, type : 'uint:3', wr_dis : 31, rd_dis: null, alt : '', dict : '', desc: 'Represents whether JTAG is disabled in soft way.\\ Odd number: disabled\\ Even number: enabled\\', rloc: 'EFUSE_RD_REPEAT_DATA0_REG[18:16]', bloc: 'B6[2:0]'} - DIS_PAD_JTAG : {show: y, blk : 0, word: 1, pos: 19, len : 1, start : 51, 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[19]', bloc: 'B6[3]'} - DIS_DOWNLOAD_MANUAL_ENCRYPT : {show: y, blk : 0, word: 1, pos: 20, len : 1, start : 52, 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[20]', bloc: 'B6[4]'} + RPT4_RESERVED0_4 : {show: n, blk : 0, word: 1, pos : 7, len : 1, start : 39, type : bool, wr_dis: null, rd_dis: null, alt : '', dict : '', desc: Reserved, rloc: 'EFUSE_RD_REPEAT_DATA0_REG[7]', bloc: 'B4[7]'} + DIS_ICACHE : {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 icache is disabled or enabled. 1: disabled. 0: enabled', rloc: 'EFUSE_RD_REPEAT_DATA0_REG[8]', bloc: 'B5[0]'} + DIS_USB_JTAG : {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 of usb switch to jtag is disabled or enabled. 1: disabled. 0: enabled', rloc: 'EFUSE_RD_REPEAT_DATA0_REG[9]', bloc: 'B5[1]'} + POWERGLITCH_EN : {show: y, blk : 0, word: 1, pos: 10, len : 1, start : 42, type : bool, wr_dis : 2, rd_dis: null, alt : '', dict : '', desc: 'Represents whether power glitch function is enabled. 1: enabled. 0: disabled', rloc: 'EFUSE_RD_REPEAT_DATA0_REG[10]', bloc: 'B5[2]'} + DIS_USB_SERIAL_JTAG : {show: n, blk : 0, word: 1, pos: 11, len : 1, start : 43, 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[11]', bloc: 'B5[3]'} + DIS_FORCE_DOWNLOAD : {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 function that forces chip into download mode is disabled or enabled. 1: disabled. 0: enabled', rloc: 'EFUSE_RD_REPEAT_DATA0_REG[12]', bloc: 'B5[4]'} + SPI_DOWNLOAD_MSPI_DIS : {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 SPI0 controller during boot_mode_download is disabled or enabled. 1: disabled. 0: enabled', rloc: 'EFUSE_RD_REPEAT_DATA0_REG[13]', bloc: 'B5[5]'} + DIS_TWAI : {show: y, blk : 0, word: 1, pos: 14, len : 1, start : 46, 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[14]', bloc: 'B5[6]'} + JTAG_SEL_ENABLE : {show: y, blk : 0, word: 1, pos: 15, len : 1, start : 47, type : bool, wr_dis : 2, rd_dis: null, alt : '', dict : '', desc: Set this bit to enable selection between usb_to_jtag and pad_to_jtag through strapping gpio25 when both EFUSE_DIS_PAD_JTAG and EFUSE_DIS_USB_JTAG are equal to 0, rloc: 'EFUSE_RD_REPEAT_DATA0_REG[15]', bloc: 'B5[7]'} + SOFT_DIS_JTAG : {show: y, blk : 0, word: 1, pos: 16, len : 3, start : 48, type : 'uint:3', wr_dis : 31, rd_dis: null, alt : '', dict : '', desc: 'Represents whether JTAG is disabled in soft way. Odd number: disabled. Even number: enabled', rloc: 'EFUSE_RD_REPEAT_DATA0_REG[18:16]', bloc: 'B6[2:0]'} + DIS_PAD_JTAG : {show: y, blk : 0, word: 1, pos: 19, len : 1, start : 51, 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[19]', bloc: 'B6[3]'} + DIS_DOWNLOAD_MANUAL_ENCRYPT : {show: y, blk : 0, word: 1, pos: 20, len : 1, start : 52, 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[20]', bloc: 'B6[4]'} USB_DREFH : {show: n, blk : 0, word: 1, pos: 21, len : 2, start : 53, type : 'uint:2', wr_dis : 30, rd_dis: null, alt : '', dict : '', desc: Represents the single-end input threshold vrefh; 1.76 V to 2 V with step of 80 mV, rloc: 'EFUSE_RD_REPEAT_DATA0_REG[22:21]', bloc: 'B6[6:5]'} USB_DREFL : {show: n, blk : 0, word: 1, pos: 23, len : 2, start : 55, type : 'uint:2', wr_dis : 30, rd_dis: null, alt : '', dict : '', desc: Represents the single-end input threshold vrefl; 1.76 V to 2 V with step of 80 mV, rloc: 'EFUSE_RD_REPEAT_DATA0_REG[24:23]', bloc: 'B6[7],B7[0]'} - USB_EXCHG_PINS : {show: y, blk : 0, word: 1, pos: 25, len : 1, start : 57, type : bool, wr_dis : 30, rd_dis: null, alt : '', dict : '', desc: 'Represents whether the D+ and D- pins is exchanged.\\ 1: exchanged\\ 0: not exchanged\\', rloc: 'EFUSE_RD_REPEAT_DATA0_REG[25]', bloc: 'B7[1]'} - VDD_SPI_AS_GPIO : {show: y, blk : 0, word: 1, pos: 26, len : 1, start : 58, type : bool, wr_dis : 30, rd_dis: null, alt : '', dict : '', desc: 'Represents whether vdd spi pin is functioned as gpio.\\ 1: functioned\\ 0: not functioned\\', rloc: 'EFUSE_RD_REPEAT_DATA0_REG[26]', bloc: 'B7[2]'} - RESERVE_0_59 : {show: n, blk : 0, word: 1, pos: 27, len : 5, start : 59, type : 'uint:5', wr_dis: null, rd_dis: null, alt : '', dict : '', desc: Reserved; it was created by set_missed_fields_in_regs func, rloc: 'EFUSE_RD_REPEAT_DATA0_REG[31:27]', bloc: 'B7[7:3]'} - KM_DISABLE_DEPLOY_MODE : {show: y, blk : 0, word: 2, pos : 0, len : 4, start : 64, type : 'uint:4', wr_dis : 1, rd_dis: null, alt : '', dict : '', desc: 'Represents whether the deploy mode of key manager is disable or not. \\ 1: disabled \\ 0: enabled.\\', rloc: 'EFUSE_RD_REPEAT_DATA1_REG[3:0]', bloc: 'B8[3:0]'} - KM_RND_SWITCH_CYCLE : {show: y, blk : 0, word: 2, pos : 4, len : 2, start : 68, type : 'uint:2', wr_dis : 1, rd_dis: null, alt : '', dict : '', desc: 'Set the bits to control key manager random number switch cycle. 0: control by register. 1: 8 km clk cycles. 2: 16 km cycles. 3: 32 km cycles', rloc: 'EFUSE_RD_REPEAT_DATA1_REG[5:4]', bloc: 'B8[5:4]'} - KM_DEPLOY_ONLY_ONCE : {show: y, blk : 0, word: 2, pos : 6, len : 4, start : 70, type : 'uint:4', wr_dis : 1, rd_dis: null, alt : '', dict : '', desc: 'Set each bit to control whether corresponding key can only be deployed once. 1 is true; 0 is false. bit 0: ecsda; bit 1: xts; bit2: hmac; bit3: ds', rloc: 'EFUSE_RD_REPEAT_DATA1_REG[9:6]', bloc: 'B8[7:6],B9[1:0]'} - FORCE_USE_KEY_MANAGER_KEY : {show: y, blk : 0, word: 2, pos: 10, len : 4, start : 74, type : 'uint:4', wr_dis : 1, rd_dis: null, alt : '', dict : '', desc: 'Set each bit to control whether corresponding key must come from key manager. 1 is true; 0 is false. bit 0: ecsda; bit 1: xts; bit2: hmac; bit3: ds', rloc: 'EFUSE_RD_REPEAT_DATA1_REG[13:10]', bloc: 'B9[5:2]'} - FORCE_DISABLE_SW_INIT_KEY : {show: y, blk : 0, word: 2, pos: 14, len : 1, start : 78, type : bool, wr_dis : 1, rd_dis: null, alt : '', dict : '', desc: Set this bit to disable software written init key; and force use efuse_init_key, rloc: 'EFUSE_RD_REPEAT_DATA1_REG[14]', bloc: 'B9[6]'} - RESERVE_0_79 : {show: n, blk : 0, word: 2, pos: 15, len : 1, start : 79, type : bool, wr_dis: null, rd_dis: null, alt : '', dict : '', desc: Reserved; it was created by set_missed_fields_in_regs func, rloc: 'EFUSE_RD_REPEAT_DATA1_REG[15]', bloc: 'B9[7]'} - WDT_DELAY_SEL : {show: y, blk : 0, word: 2, pos: 16, len : 2, start : 80, type : 'uint:2', wr_dis : 3, rd_dis: null, alt : '', dict : '', desc: 'Represents the threshold level of the RTC watchdog STG0 timeout.\\ 0: Original threshold configuration value of STG0 *2 \\1: Original threshold configuration value of STG0 *4 \\2: Original threshold configuration value of STG0 *8 \\3: Original threshold configuration value of STG0 *16 \\', rloc: 'EFUSE_RD_REPEAT_DATA1_REG[17:16]', bloc: 'B10[1:0]'} + USB_EXCHG_PINS : {show: y, blk : 0, word: 1, pos: 25, len : 1, start : 57, type : bool, wr_dis : 30, rd_dis: null, alt : '', dict : '', desc: 'Represents whether the D+ and D- pins is exchanged. 1: exchanged. 0: not exchanged', rloc: 'EFUSE_RD_REPEAT_DATA0_REG[25]', bloc: 'B7[1]'} + VDD_SPI_AS_GPIO : {show: y, blk : 0, word: 1, pos: 26, len : 1, start : 58, type : bool, wr_dis : 30, rd_dis: null, alt : '', dict : '', desc: 'Represents whether vdd spi pin is functioned as gpio. 1: functioned. 0: not functioned', rloc: 'EFUSE_RD_REPEAT_DATA0_REG[26]', bloc: 'B7[2]'} + RPT4_RESERVED0_2 : {show: y, blk : 0, word: 1, pos: 27, len : 2, start : 59, type : 'uint:2', wr_dis: null, rd_dis: null, alt : '', dict : '', desc: Reserved, rloc: 'EFUSE_RD_REPEAT_DATA0_REG[28:27]', bloc: 'B7[4:3]'} + RPT4_RESERVED0_1 : {show: y, blk : 0, word: 1, pos: 29, len : 1, start : 61, type : bool, wr_dis: null, rd_dis: null, alt : '', dict : '', desc: Reserved, rloc: 'EFUSE_RD_REPEAT_DATA0_REG[29]', bloc: 'B7[5]'} + RPT4_RESERVED0_0 : {show: n, blk : 0, word: 1, pos: 30, len : 2, start : 62, type : 'uint:2', wr_dis: null, rd_dis: null, alt : '', dict : '', desc: Reserved, rloc: 'EFUSE_RD_REPEAT_DATA0_REG[31:30]', bloc: 'B7[7:6]'} + RPT4_RESERVED1_1 : {show: n, blk : 0, word: 2, pos : 0, len : 16, start : 64, type : 'uint:16', wr_dis: null, rd_dis: null, alt : '', dict : '', desc: Reserved, rloc: 'EFUSE_RD_REPEAT_DATA1_REG[15:0]', bloc: 'B8,B9'} + WDT_DELAY_SEL : {show: y, blk : 0, word: 2, pos: 16, len : 2, start : 80, type : 'uint:2', wr_dis : 3, rd_dis: null, alt : '', dict : '', desc: 'Represents whether RTC watchdog timeout threshold is selected at startup. 1: selected. 0: not selected', rloc: 'EFUSE_RD_REPEAT_DATA1_REG[17:16]', bloc: 'B10[1:0]'} SPI_BOOT_CRYPT_CNT : {show: y, blk : 0, word: 2, pos: 18, len : 3, start : 82, 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_DATA1_REG[20:18]', bloc: 'B10[4:2]'} SECURE_BOOT_KEY_REVOKE0 : {show: y, blk : 0, word: 2, pos: 21, len : 1, start : 85, type : bool, wr_dis : 5, rd_dis: null, alt : '', dict : '', desc: Revoke 1st secure boot key, rloc: 'EFUSE_RD_REPEAT_DATA1_REG[21]', bloc: 'B10[5]'} SECURE_BOOT_KEY_REVOKE1 : {show: y, blk : 0, word: 2, pos: 22, len : 1, start : 86, type : bool, wr_dis : 6, rd_dis: null, alt : '', dict : '', desc: Revoke 2nd secure boot key, rloc: 'EFUSE_RD_REPEAT_DATA1_REG[22]', bloc: 'B10[6]'} @@ -37,60 +34,65 @@ EFUSES: KEY_PURPOSE_4 : {show: y, blk : 0, word: 3, pos : 8, len : 4, start: 104, type : 'uint:4', wr_dis : 12, rd_dis: null, alt : KEY4_PURPOSE, dict : '', desc: Represents the purpose of Key4, rloc: 'EFUSE_RD_REPEAT_DATA2_REG[11:8]', bloc: 'B13[3:0]'} KEY_PURPOSE_5 : {show: y, blk : 0, word: 3, pos: 12, len : 4, start: 108, type : 'uint:4', wr_dis : 13, rd_dis: null, alt : KEY5_PURPOSE, dict : '', desc: Represents the purpose of Key5, rloc: 'EFUSE_RD_REPEAT_DATA2_REG[15:12]', bloc: 'B13[7:4]'} SEC_DPA_LEVEL : {show: y, blk : 0, word: 3, pos: 16, len : 2, start: 112, type : 'uint:2', wr_dis : 14, rd_dis: null, alt : '', dict : '', desc: Represents the spa secure level by configuring the clock random divide mode, rloc: 'EFUSE_RD_REPEAT_DATA2_REG[17:16]', bloc: 'B14[1:0]'} - RESERVE_0_114 : {show: n, blk : 0, word: 3, pos: 18, len : 2, start: 114, type : 'uint:2', wr_dis: null, rd_dis: null, alt : '', dict : '', desc: Reserved; it was created by set_missed_fields_in_regs func, rloc: 'EFUSE_RD_REPEAT_DATA2_REG[19:18]', bloc: 'B14[3:2]'} - SECURE_BOOT_EN : {show: y, blk : 0, word: 3, pos: 20, len : 1, start: 116, 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[20]', bloc: 'B14[4]'} - SECURE_BOOT_AGGRESSIVE_REVOKE : {show: y, blk : 0, word: 3, pos: 21, len : 1, start: 117, 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[21]', bloc: 'B14[5]'} - RESERVE_0_118 : {show: n, blk : 0, word: 3, pos: 22, len : 5, start: 118, type : 'uint:5', wr_dis: null, rd_dis: null, alt : '', dict : '', desc: Reserved; it was created by set_missed_fields_in_regs func, rloc: 'EFUSE_RD_REPEAT_DATA2_REG[26:22]', bloc: 'B14[7:6],B15[2:0]'} - KM_XTS_KEY_LENGTH_256 : {show: y, blk : 0, word: 3, pos: 27, len : 1, start: 123, type : bool, wr_dis : 1, rd_dis: null, alt : '', dict : '', desc: Set this bitto configure flash encryption use xts-128 key. else use xts-256 key, rloc: 'EFUSE_RD_REPEAT_DATA2_REG[27]', bloc: 'B15[3]'} + ECDSA_FORCE_USE_HARDWARE_K : {show: y, blk : 0, word: 3, pos: 18, len : 1, start: 114, type : bool, wr_dis : 17, rd_dis: null, alt : '', dict : '', desc: 'Represents whether hardware random number k is forced used in ESDCA. 1: force used. 0: not force used', rloc: 'EFUSE_RD_REPEAT_DATA2_REG[18]', bloc: 'B14[2]'} + CRYPT_DPA_ENABLE : {show: y, blk : 0, word: 3, pos: 19, len : 1, start: 115, type : bool, wr_dis : 14, rd_dis: null, alt : '', dict : '', desc: 'Represents whether anti-dpa attack is enabled. 1:enabled. 0: disabled', rloc: 'EFUSE_RD_REPEAT_DATA2_REG[19]', bloc: 'B14[3]'} + SECURE_BOOT_EN : {show: y, blk : 0, word: 3, pos: 20, len : 1, start: 116, 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[20]', bloc: 'B14[4]'} + SECURE_BOOT_AGGRESSIVE_REVOKE : {show: y, blk : 0, word: 3, pos: 21, len : 1, start: 117, 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[21]', bloc: 'B14[5]'} + RPT4_RESERVED2_0 : {show: n, blk : 0, word: 3, pos: 22, len : 6, start: 118, type : 'uint:6', wr_dis: null, rd_dis: null, alt : '', dict : '', desc: Reserved, rloc: 'EFUSE_RD_REPEAT_DATA2_REG[27:22]', bloc: 'B14[7:6],B15[3:0]'} FLASH_TPUW : {show: y, blk : 0, word: 3, pos: 28, len : 4, start: 124, type : 'uint:4', 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[31:28]', bloc: 'B15[7:4]'} - DIS_DOWNLOAD_MODE : {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 Download mode is disabled or enabled.\\ 1: disabled\\ 0: enabled\\', rloc: 'EFUSE_RD_REPEAT_DATA3_REG[0]', bloc: 'B16[0]'} - DIS_DIRECT_BOOT : {show: y, blk : 0, word: 4, pos : 1, len : 1, start: 129, 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[1]', bloc: 'B16[1]'} - DIS_USB_SERIAL_JTAG_ROM_PRINT : {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 print from USB-Serial-JTAG is disabled or enabled.\\ 1: disabled\\ 0: enabled\\', rloc: 'EFUSE_RD_REPEAT_DATA3_REG[2]', bloc: 'B16[2]'} - LOCK_KM_KEY : {show: y, blk : 0, word: 4, pos : 3, len : 1, start: 131, type : bool, wr_dis : 1, rd_dis: null, alt : '', dict : '', desc: 'Represetns whether to lock the efuse xts key.\\ 1. Lock\\ 0: Unlock\\', rloc: 'EFUSE_RD_REPEAT_DATA3_REG[3]', bloc: 'B16[3]'} - DIS_USB_SERIAL_JTAG_DOWNLOAD_MODE: {show: y, blk : 0, word: 4, pos : 4, len : 1, start: 132, type : bool, wr_dis : 18, rd_dis: null, alt : '', dict : '', desc: 'Represents whether the USB-Serial-JTAG download function is disabled or enabled.\\ 1: Disable\\ 0: Enable\\', rloc: 'EFUSE_RD_REPEAT_DATA3_REG[4]', bloc: 'B16[4]'} - ENABLE_SECURITY_DOWNLOAD : {show: y, blk : 0, word: 4, pos : 5, len : 1, start: 133, 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[5]', bloc: 'B16[5]'} + DIS_DOWNLOAD_MODE : {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 Download mode is disabled or enabled. 1: disabled. 0: enabled', rloc: 'EFUSE_RD_REPEAT_DATA3_REG[0]', bloc: 'B16[0]'} + DIS_DIRECT_BOOT : {show: y, blk : 0, word: 4, pos : 1, len : 1, start: 129, 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[1]', bloc: 'B16[1]'} + DIS_USB_SERIAL_JTAG_ROM_PRINT : {show: y, blk : 0, word: 4, pos : 2, len : 1, start: 130, type : bool, wr_dis : 18, rd_dis: null, alt : DIS_USB_PRINT, dict : '', desc: Set this bit to disable USB-Serial-JTAG print during rom boot, rloc: 'EFUSE_RD_REPEAT_DATA3_REG[2]', bloc: 'B16[2]'} + RPT4_RESERVED3_5 : {show: n, blk : 0, word: 4, pos : 3, len : 1, start: 131, type : bool, wr_dis: null, rd_dis: null, alt : '', dict : '', desc: Reserved, rloc: 'EFUSE_RD_REPEAT_DATA3_REG[3]', bloc: 'B16[3]'} + DIS_USB_SERIAL_JTAG_DOWNLOAD_MODE: {show: y, blk : 0, word: 4, pos : 4, len : 1, start: 132, 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[4]', bloc: 'B16[4]'} + ENABLE_SECURITY_DOWNLOAD : {show: y, blk : 0, word: 4, pos : 5, len : 1, start: 133, 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[5]', bloc: 'B16[5]'} UART_PRINT_CONTROL : {show: y, blk : 0, word: 4, pos : 6, len : 2, start: 134, 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[7:6]', bloc: 'B16[7:6]'} - FORCE_SEND_RESUME : {show: y, blk : 0, word: 4, pos : 8, len : 1, start: 136, 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[8]', bloc: 'B17[0]'} + FORCE_SEND_RESUME : {show: y, blk : 0, word: 4, pos : 8, len : 1, start: 136, 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[8]', bloc: 'B17[0]'} SECURE_VERSION : {show: y, blk : 0, word: 4, pos : 9, len : 16, start: 137, 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[24:9]', bloc: 'B17[7:1],B18,B19[0]'} - SECURE_BOOT_DISABLE_FAST_WAKE : {show: y, blk : 0, word: 4, pos: 25, len : 1, start: 153, type : bool, wr_dis : 18, rd_dis: null, alt : '', dict : '', desc: 'Represents whether FAST VERIFY ON WAKE is disabled or enabled when Secure Boot is enabled.\\ 1: disabled\\ 0: enabled\\', rloc: 'EFUSE_RD_REPEAT_DATA3_REG[25]', bloc: 'B19[1]'} - HYS_EN_PAD : {show: y, blk : 0, word: 4, pos: 26, len : 1, start: 154, type : bool, wr_dis : 2, rd_dis: null, alt : '', dict : '', desc: 'Represents whether the hysteresis function of corresponding PAD is enabled.\\ 1: enabled\\ 0:disabled\\', rloc: 'EFUSE_RD_REPEAT_DATA3_REG[26]', bloc: 'B19[2]'} - XTS_DPA_PSEUDO_LEVEL : {show: y, blk : 0, word: 4, pos: 27, len : 2, start: 155, type : 'uint:2', wr_dis : 14, rd_dis: null, alt : '', dict : '', desc: 'Represents the pseudo round level of xts-aes anti-dpa attack.\\ 3: High.\\ 2: Moderate 1. Low\\ 0: Disabled\\', rloc: 'EFUSE_RD_REPEAT_DATA3_REG[28:27]', bloc: 'B19[4:3]'} - XTS_DPA_CLK_ENABLE : {show: y, blk : 0, word: 4, pos: 29, len : 1, start: 157, type : bool, wr_dis : 14, rd_dis: null, alt : '', dict : '', desc: 'Represents whether xts-aes anti-dpa attack clock is enabled.\\ 1. Enable.\\ 0: Disable.\\', rloc: 'EFUSE_RD_REPEAT_DATA3_REG[29]', bloc: 'B19[5]'} - RESERVE_0_158 : {show: n, blk : 0, word: 4, pos: 30, len : 2, start: 158, type : 'uint:2', wr_dis: null, rd_dis: null, alt : '', dict : '', desc: Reserved; it was created by set_missed_fields_in_regs func, rloc: 'EFUSE_RD_REPEAT_DATA3_REG[31:30]', bloc: 'B19[7:6]'} - HUK_GEN_STATE : {show: y, blk : 0, word: 5, pos : 0, len : 9, start: 160, type : 'uint:9', wr_dis : 19, rd_dis: null, alt : '', dict : '', desc: Set the bits to control validation of HUK generate mode.\\ Odd of 1 is invalid.\\ Even of 1 is valid.\\, rloc: 'EFUSE_RD_REPEAT_DATA4_REG[8:0]', bloc: 'B20,B21[0]'} - XTAL_48M_SEL : {show: y, blk : 0, word: 5, pos : 9, len : 3, start: 169, type : 'uint:3', wr_dis : 17, rd_dis: null, alt : '', dict : '', desc: 'Represents whether XTAL frequency is 48MHz or not. If not; 40MHz XTAL will be used. If this field contains Odd number bit 1: Enable 48MHz XTAL\ Even number bit 1: Enable 40MHz XTAL', rloc: 'EFUSE_RD_REPEAT_DATA4_REG[11:9]', bloc: 'B21[3:1]'} - XTAL_48M_SEL_MODE : {show: y, blk : 0, word: 5, pos: 12, len : 1, start: 172, type : bool, wr_dis : 17, rd_dis: null, alt : '', dict : '', desc: 'Specify the XTAL frequency selection is decided by eFuse or strapping-PAD-state. 1: eFuse\\ 0: strapping-PAD-state', rloc: 'EFUSE_RD_REPEAT_DATA4_REG[12]', bloc: 'B21[4]'} - ECDSA_DISABLE_P192 : {show: y, blk : 0, word: 5, pos: 13, len : 1, start: 173, type : bool, wr_dis : 14, rd_dis: null, alt : '', dict : '', desc: 'Represents whether to disable P192 curve in ECDSA.\\ 1: Disabled.\\ 0: Not disable', rloc: 'EFUSE_RD_REPEAT_DATA4_REG[13]', bloc: 'B21[5]'} - ECC_FORCE_CONST_TIME : {show: y, blk : 0, word: 5, pos: 14, len : 1, start: 174, type : bool, wr_dis : 14, rd_dis: null, alt : '', dict : '', desc: 'Represents whether to force ecc to use const-time calculation mode. \\ 1: Enable. \\ 0: Disable', rloc: 'EFUSE_RD_REPEAT_DATA4_REG[14]', bloc: 'B21[6]'} - RESERVE_0_175 : {show: n, blk : 0, word: 5, pos: 15, len : 17, start: 175, type : 'uint:17', wr_dis: null, rd_dis: null, alt : '', dict : '', desc: Reserved; it was created by set_missed_fields_in_regs func, rloc: 'EFUSE_RD_REPEAT_DATA4_REG[31:15]', bloc: 'B21[7],B22,B23'} - 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_SYS0_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: Represents the extended bits of MAC address, rloc: 'EFUSE_RD_MAC_SYS1_REG[31:16]', bloc: 'B6,B7'} - WAFER_VERSION_MINOR : {show: y, blk : 1, word: 2, pos : 0, len : 4, start : 64, type : 'uint:4', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: Minor chip version, rloc: 'EFUSE_RD_MAC_SYS2_REG[3:0]', bloc: 'B8[3:0]'} - WAFER_VERSION_MAJOR : {show: y, blk : 1, word: 2, pos : 4, len : 2, start : 68, type : 'uint:2', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: Minor chip version, rloc: 'EFUSE_RD_MAC_SYS2_REG[5:4]', bloc: 'B8[5:4]'} - DISABLE_WAFER_VERSION_MAJOR : {show: y, blk : 1, word: 2, pos : 6, len : 1, start : 70, type : bool, wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: Disables check of wafer version major, rloc: 'EFUSE_RD_MAC_SYS2_REG[6]', bloc: 'B8[6]'} - DISABLE_BLK_VERSION_MAJOR : {show: y, blk : 1, word: 2, pos : 7, len : 1, start : 71, type : bool, wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: Disables check of blk version major, rloc: 'EFUSE_RD_MAC_SYS2_REG[7]', bloc: 'B8[7]'} - BLK_VERSION_MINOR : {show: y, blk : 1, word: 2, pos : 8, len : 3, start : 72, type : 'uint:3', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: BLK_VERSION_MINOR of BLOCK2, rloc: 'EFUSE_RD_MAC_SYS2_REG[10:8]', bloc: 'B9[2:0]'} - BLK_VERSION_MAJOR : {show: y, blk : 1, word: 2, pos: 11, len : 2, start : 75, type : 'uint:2', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: BLK_VERSION_MAJOR of BLOCK2, rloc: 'EFUSE_RD_MAC_SYS2_REG[12:11]', bloc: 'B9[4:3]'} - FLASH_CAP : {show: y, blk : 1, word: 2, pos: 13, len : 3, start : 77, type : 'uint:3', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: Flash capacity, rloc: 'EFUSE_RD_MAC_SYS2_REG[15:13]', bloc: 'B9[7:5]'} - FLASH_VENDOR : {show: y, blk : 1, word: 2, pos: 16, len : 3, start : 80, type : 'uint:3', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: Flash vendor, rloc: 'EFUSE_RD_MAC_SYS2_REG[18:16]', bloc: 'B10[2:0]'} - PSRAM_CAP : {show: y, blk : 1, word: 2, pos: 19, len : 3, start : 83, type : 'uint:3', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: Psram capacity, rloc: 'EFUSE_RD_MAC_SYS2_REG[21:19]', bloc: 'B10[5:3]'} - PSRAM_VENDOR : {show: y, blk : 1, word: 2, pos: 22, len : 2, start : 86, type : 'uint:2', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: Psram vendor, rloc: 'EFUSE_RD_MAC_SYS2_REG[23:22]', bloc: 'B10[7:6]'} - TEMP : {show: y, blk : 1, word: 2, pos: 24, len : 2, start : 88, type : 'uint:2', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: Temp (die embedded inside), rloc: 'EFUSE_RD_MAC_SYS2_REG[25:24]', bloc: 'B11[1:0]'} - PKG_VERSION : {show: y, blk : 1, word: 2, pos: 26, len : 3, start : 90, type : 'uint:3', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: Package version, rloc: 'EFUSE_RD_MAC_SYS2_REG[28:26]', bloc: 'B11[4:2]'} - PA_TRIM_VERSION : {show: y, blk : 1, word: 2, pos: 29, len : 3, start : 93, type : 'uint:3', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: PADC CAL PA trim version, rloc: 'EFUSE_RD_MAC_SYS2_REG[31:29]', bloc: 'B11[7:5]'} - TRIM_N_BIAS : {show: y, blk : 1, word: 3, pos : 0, len : 5, start : 96, type : 'uint:5', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: PADC CAL N bias, rloc: 'EFUSE_RD_MAC_SYS3_REG[4:0]', bloc: 'B12[4:0]'} - TRIM_P_BIAS : {show: y, blk : 1, word: 3, pos : 5, len : 5, start: 101, type : 'uint:5', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: PADC CAL P bias, rloc: 'EFUSE_RD_MAC_SYS3_REG[9:5]', bloc: 'B12[7:5],B13[1:0]'} - RESERVED_1_106 : {show: n, blk : 1, word: 3, pos: 10, len : 8, start: 106, type : 'uint:8', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: reserved, rloc: 'EFUSE_RD_MAC_SYS3_REG[17:10]', bloc: 'B13[7:2],B14[1:0]'} - SYS_DATA_PART0_0 : {show: n, blk : 1, word: 3, pos: 18, len : 14, start: 114, type : 'uint:14', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: Represents the first 14-bit of zeroth part of system data, rloc: 'EFUSE_RD_MAC_SYS3_REG[31:18]', bloc: 'B14[7:2],B15'} - SYS_DATA_PART0_1 : {show: n, blk : 1, word: 4, pos : 0, len : 32, start: 128, type : 'uint:32', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: Represents the first 14-bit of zeroth part of system data, rloc: EFUSE_RD_MAC_SYS4_REG, bloc: 'B16,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: Represents the second 32-bit of zeroth part of system data, rloc: EFUSE_RD_MAC_SYS5_REG, bloc: 'B20,B21,B22,B23'} + SECURE_BOOT_DISABLE_FAST_WAKE : {show: y, blk : 0, word: 4, pos: 25, len : 1, start: 153, type : bool, wr_dis : 18, rd_dis: null, alt : '', dict : '', desc: 'Represents whether FAST VERIFY ON WAKE is disabled or enabled when Secure Boot is enabled. 1: disabled. 0: enabled', rloc: 'EFUSE_RD_REPEAT_DATA3_REG[25]', bloc: 'B19[1]'} + HYS_EN_PAD0 : {show: y, blk : 0, word: 4, pos: 26, len : 6, start: 154, type : 'uint:6', wr_dis : 19, rd_dis: null, alt : '', dict : '', desc: Set bits to enable hysteresis function of PAD0~5, rloc: 'EFUSE_RD_REPEAT_DATA3_REG[31:26]', bloc: 'B19[7:2]'} + HYS_EN_PAD1 : {show: y, blk : 0, word: 5, pos : 0, len : 22, start: 160, type : 'uint:22', wr_dis : 19, rd_dis: null, alt : '', dict : '', desc: Set bits to enable hysteresis function of PAD6~27, rloc: 'EFUSE_RD_REPEAT_DATA4_REG[21:0]', bloc: 'B20,B21,B22[5:0]'} + RPT4_RESERVED4_1 : {show: n, blk : 0, word: 5, pos: 22, len : 2, start: 182, type : 'uint:2', wr_dis: null, rd_dis: null, alt : '', dict : '', desc: Reserved, rloc: 'EFUSE_RD_REPEAT_DATA4_REG[23:22]', bloc: 'B22[7:6]'} + RPT4_RESERVED4_0 : {show: n, blk : 0, word: 5, pos: 24, len : 8, start: 184, type : 'uint:8', wr_dis: null, rd_dis: null, alt : '', dict : '', desc: Reserved, rloc: 'EFUSE_RD_REPEAT_DATA4_REG[31:24]', bloc: B23} + 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_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_SYS_1_REG[31:16]', bloc: 'B6,B7'} + RXIQ_VERSION : {show: y, blk : 1, word: 2, pos : 0, len : 3, start : 64, type : 'uint:3', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: Stores RF Calibration data. RXIQ version, rloc: 'EFUSE_RD_MAC_SYS_2_REG[2:0]', bloc: 'B8[2:0]'} + RXIQ_0 : {show: y, blk : 1, word: 2, pos : 3, len : 7, start : 67, type : 'uint:7', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: Stores RF Calibration data. RXIQ data 0, rloc: 'EFUSE_RD_MAC_SYS_2_REG[9:3]', bloc: 'B8[7:3],B9[1:0]'} + RXIQ_1 : {show: y, blk : 1, word: 2, pos: 10, len : 7, start : 74, type : 'uint:7', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: Stores RF Calibration data. RXIQ data 1, rloc: 'EFUSE_RD_MAC_SYS_2_REG[16:10]', bloc: 'B9[7:2],B10[0]'} + ACTIVE_HP_DBIAS : {show: y, blk : 1, word: 2, pos: 17, len : 5, start : 81, type : 'uint:5', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: Stores the PMU active hp dbias, rloc: 'EFUSE_RD_MAC_SYS_2_REG[21:17]', bloc: 'B10[5:1]'} + ACTIVE_LP_DBIAS : {show: y, blk : 1, word: 2, pos: 22, len : 5, start : 86, type : 'uint:5', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: Stores the PMU active lp dbias, rloc: 'EFUSE_RD_MAC_SYS_2_REG[26:22]', bloc: 'B10[7:6],B11[2:0]'} + DSLP_DBIAS : {show: y, blk : 1, word: 2, pos: 27, len : 4, start : 91, type : 'uint:4', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: Stores the PMU sleep dbias, rloc: 'EFUSE_RD_MAC_SYS_2_REG[30:27]', bloc: 'B11[6:3]'} + DBIAS_VOL_GAP : {show: y, blk : 1, word: 2, pos: 31, len : 5, start : 95, type : 'uint:5', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: Stores the low 1 bit of dbias_vol_gap, rloc: 'EFUSE_RD_MAC_SYS_2_REG[31]', bloc: 'B11[7],B12[3:0]'} + MAC_RESERVED_2 : {show: n, blk : 1, word: 3, pos : 4, len : 14, start: 100, type : 'uint:14', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: Reserved, rloc: 'EFUSE_RD_MAC_SYS_3_REG[17:4]', bloc: 'B12[7:4],B13,B14[1:0]'} + WAFER_VERSION_MINOR : {show: y, blk : 1, word: 3, pos: 18, len : 3, start: 114, type : 'uint:3', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: Stores the wafer version minor, rloc: 'EFUSE_RD_MAC_SYS_3_REG[20:18]', bloc: 'B14[4:2]'} + WAFER_VERSION_MAJOR : {show: y, blk : 1, word: 3, pos: 21, len : 2, start: 117, type : 'uint:2', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: Stores the wafer version major, rloc: 'EFUSE_RD_MAC_SYS_3_REG[22:21]', bloc: 'B14[6:5]'} + DISABLE_WAFER_VERSION_MAJOR : {show: y, blk : 1, word: 3, pos: 23, len : 1, start: 119, type : bool, wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: Disables check of wafer version major, rloc: 'EFUSE_RD_MAC_SYS_3_REG[23]', bloc: 'B14[7]'} + FLASH_CAP : {show: y, blk : 1, word: 3, pos: 24, len : 3, start: 120, type : 'uint:3', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: Stores the flash cap, rloc: 'EFUSE_RD_MAC_SYS_3_REG[26:24]', bloc: 'B15[2:0]'} + FLASH_TEMP : {show: y, blk : 1, word: 3, pos: 27, len : 2, start: 123, type : 'uint:2', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: Stores the flash temp, rloc: 'EFUSE_RD_MAC_SYS_3_REG[28:27]', bloc: 'B15[4:3]'} + FLASH_VENDOR : {show: y, blk : 1, word: 3, pos: 29, len : 3, start: 125, type : 'uint:3', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: Stores the flash vendor, rloc: 'EFUSE_RD_MAC_SYS_3_REG[31:29]', bloc: 'B15[7:5]'} + PKG_VERSION : {show: y, blk : 1, word: 4, pos : 0, len : 3, start: 128, type : 'uint:3', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: Package version, rloc: 'EFUSE_RD_MAC_SYS_4_REG[2:0]', bloc: 'B16[2:0]'} + RESERVED_1_131 : {show: n, blk : 1, word: 4, pos : 3, len : 29, start: 131, type : 'uint:29', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: reserved, rloc: 'EFUSE_RD_MAC_SYS_4_REG[31:3]', bloc: 'B16[7:3],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_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'} - RESERVED_2_128 : {show: n, blk : 2, word: 4, pos : 0, len : 9, start: 128, type : 'uint:9', wr_dis : 21, rd_dis: null, alt : '', dict : '', desc: reserved, 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]'} - RESERVED_2_145 : {show: n, blk : 2, word: 4, pos: 17, len : 15, start: 145, type : 'uint:15', wr_dis : 21, rd_dis: null, alt : '', dict : '', desc: reserved, rloc: 'EFUSE_RD_SYS_PART1_DATA4_REG[31:17]', bloc: 'B18[7:1],B19'} - SYS_DATA_PART1_5 : {show: n, blk : 2, word: 5, pos : 0, len : 32, start: 160, type : 'uint:32', wr_dis : 21, rd_dis: null, alt : '', dict : '', desc: Represents the zeroth 32-bit of first part of system data, rloc: EFUSE_RD_SYS_PART1_DATA5_REG, bloc: 'B20,B21,B22,B23'} - SYS_DATA_PART1_6 : {show: n, blk : 2, word: 6, pos : 0, len : 32, start: 192, type : 'uint:32', wr_dis : 21, rd_dis: null, alt : '', dict : '', desc: Represents the zeroth 32-bit of first part of system data, rloc: EFUSE_RD_SYS_PART1_DATA6_REG, bloc: 'B24,B25,B26,B27'} - SYS_DATA_PART1_7 : {show: n, blk : 2, word: 7, pos : 0, len : 32, start: 224, type : 'uint:32', wr_dis : 21, rd_dis: null, alt : '', dict : '', desc: Represents the zeroth 32-bit of first part of system data, rloc: EFUSE_RD_SYS_PART1_DATA7_REG, bloc: 'B28,B29,B30,B31'} + RESERVED_2_128 : {show: n, blk : 2, word: 4, pos : 0, len : 2, start: 128, type : 'uint:2', wr_dis : 21, rd_dis: null, alt : '', dict : '', desc: reserved, rloc: 'EFUSE_RD_SYS_PART1_DATA4_REG[1:0]', bloc: 'B16[1:0]'} + BLK_VERSION_MINOR : {show: y, blk : 2, word: 4, pos : 2, len : 3, start: 130, type : 'uint:3', wr_dis : 21, rd_dis: null, alt : '', dict : '', desc: 'BLK_VERSION_MINOR of BLOCK2. 1: RF Calibration data in BLOCK1', rloc: 'EFUSE_RD_SYS_PART1_DATA4_REG[4:2]', bloc: 'B16[4:2]'} + BLK_VERSION_MAJOR : {show: y, blk : 2, word: 4, pos : 5, len : 2, start: 133, type : 'uint:2', wr_dis : 21, rd_dis: null, alt : '', dict : '', desc: BLK_VERSION_MAJOR of BLOCK2, rloc: 'EFUSE_RD_SYS_PART1_DATA4_REG[6:5]', bloc: 'B16[6:5]'} + DISABLE_BLK_VERSION_MAJOR : {show: y, blk : 2, word: 4, pos : 7, len : 1, start: 135, type : bool, wr_dis : 21, rd_dis: null, alt : '', dict : '', desc: Disables check of blk version major, rloc: 'EFUSE_RD_SYS_PART1_DATA4_REG[7]', bloc: 'B16[7]'} + TEMP_CALIB : {show: y, blk : 2, word: 4, pos : 8, len : 9, start: 136, type : 'uint:9', wr_dis : 21, rd_dis: null, alt : '', dict : '', desc: Temperature calibration data, rloc: 'EFUSE_RD_SYS_PART1_DATA4_REG[16:8]', bloc: 'B17,B18[0]'} + ADC1_AVE_INITCODE_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 calibration data, rloc: 'EFUSE_RD_SYS_PART1_DATA4_REG[26:17]', bloc: 'B18[7:1],B19[2:0]'} + ADC1_AVE_INITCODE_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 calibration data, rloc: 'EFUSE_RD_SYS_PART1_DATA4_REG[31:27]', bloc: 'B19[7:3],B20[4:0]'} + ADC1_AVE_INITCODE_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 calibration data, rloc: 'EFUSE_RD_SYS_PART1_DATA5_REG[14:5]', bloc: 'B20[7:5],B21[6:0]'} + ADC1_AVE_INITCODE_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 calibration data, rloc: 'EFUSE_RD_SYS_PART1_DATA5_REG[24:15]', bloc: 'B21[7],B22,B23[0]'} + ADC1_HI_DOUT_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 data, rloc: 'EFUSE_RD_SYS_PART1_DATA5_REG[31:25]', bloc: 'B23[7:1],B24[2:0]'} + ADC1_HI_DOUT_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 data, rloc: 'EFUSE_RD_SYS_PART1_DATA6_REG[12:3]', bloc: 'B24[7:3],B25[4:0]'} + ADC1_HI_DOUT_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 data, rloc: 'EFUSE_RD_SYS_PART1_DATA6_REG[22:13]', bloc: 'B25[7:5],B26[6:0]'} + ADC1_HI_DOUT_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 data, rloc: 'EFUSE_RD_SYS_PART1_DATA6_REG[31:23]', bloc: 'B26[7],B27,B28[0]'} + ADC1_CH0_ATTEN0_INITCODE_DIFF : {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 calibration data, rloc: 'EFUSE_RD_SYS_PART1_DATA7_REG[4:1]', bloc: 'B28[4:1]'} + ADC1_CH1_ATTEN0_INITCODE_DIFF : {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 calibration data, rloc: 'EFUSE_RD_SYS_PART1_DATA7_REG[8:5]', bloc: 'B28[7:5],B29[0]'} + ADC1_CH2_ATTEN0_INITCODE_DIFF : {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 calibration data, rloc: 'EFUSE_RD_SYS_PART1_DATA7_REG[12:9]', bloc: 'B29[4:1]'} + ADC1_CH3_ATTEN0_INITCODE_DIFF : {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 calibration data, rloc: 'EFUSE_RD_SYS_PART1_DATA7_REG[16:13]', bloc: 'B29[7:5],B30[0]'} + ADC1_CH4_ATTEN0_INITCODE_DIFF : {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 calibration data, rloc: 'EFUSE_RD_SYS_PART1_DATA7_REG[20:17]', bloc: 'B30[4:1]'} + RESERVED_2_245 : {show: n, blk : 2, word: 7, pos: 21, len : 11, start: 245, type : 'uint:11', wr_dis : 21, rd_dis: null, alt : '', dict : '', desc: reserved, rloc: 'EFUSE_RD_SYS_PART1_DATA7_REG[31:21]', bloc: 'B30[7:5],B31'} 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'} diff --git a/tools/esptool_py/espefuse/efuse_defs/esp32h2_v0.0_v1.1.yaml b/tools/esptool_py/espefuse/efuse_defs/esp32h2_v0.0_v1.1.yaml new file mode 100644 index 0000000000..228df47fb5 --- /dev/null +++ b/tools/esptool_py/espefuse/efuse_defs/esp32h2_v0.0_v1.1.yaml @@ -0,0 +1,3 @@ +VER_NO: 44563d2af4ebdba4db6c0a34a50c94f9 +EFUSES: + ECDSA_FORCE_USE_HARDWARE_K : {show: y, blk : 0, word: 3, pos: 18, len : 1, start: 114, type : bool, wr_dis : 17, rd_dis: null, alt : '', dict : '', desc: 'Represents whether hardware random number k is forced used in ESDCA. 1: force used. 0: not force used', rloc: 'EFUSE_RD_REPEAT_DATA2_REG[18]', bloc: 'B14[2]'} diff --git a/tools/esptool_py/espefuse/efuse_defs/esp32h4.yaml b/tools/esptool_py/espefuse/efuse_defs/esp32h4.yaml new file mode 100644 index 0000000000..ed81481292 --- /dev/null +++ b/tools/esptool_py/espefuse/efuse_defs/esp32h4.yaml @@ -0,0 +1,92 @@ +VER_NO: 7bc342bad0952907e1db21112d258c6b +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_DIS0_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]'} + RESERVE_0_40 : {show: n, blk : 0, word: 1, pos : 8, len : 1, start : 40, type : bool, wr_dis: null, rd_dis: null, alt : '', dict : '', desc: Reserved; it was created by set_missed_fields_in_regs func, 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 : 2, 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 : '', 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]'} + RESERVE_0_47 : {show: n, blk : 0, word: 1, pos: 15, len : 3, start : 47, type : 'uint:3', wr_dis: null, rd_dis: null, alt : '', dict : '', desc: Reserved; it was created by set_missed_fields_in_regs func, rloc: 'EFUSE_RD_REPEAT_DATA0_REG[17:15]', bloc: 'B5[7],B6[1:0]'} + PVT_GLITCH_EN : {show: y, blk : 0, word: 1, pos: 18, len : 1, start : 50, type : bool, wr_dis: null, rd_dis: null, alt : '', dict : '', desc: 'Represents whether to enable PVT power glitch monitor function.1:Enable. 0:Disable', rloc: 'EFUSE_RD_REPEAT_DATA0_REG[18]', bloc: 'B6[2]'} + RESERVE_0_51 : {show: n, blk : 0, word: 1, pos: 19, len : 1, start : 51, type : bool, wr_dis: null, rd_dis: null, alt : '', dict : '', desc: Reserved; it was created by set_missed_fields_in_regs func, 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: null, 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: 'Represents whether the CPU-Core1 is disabled. 1: Disabled. 0: Not disable', 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[26]', bloc: 'B7[2]'} + 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[27]', bloc: 'B7[3]'} + 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[28]', bloc: 'B7[4]'} + RESERVE_0_61 : {show: n, blk : 0, word: 1, pos: 29, len : 3, start : 61, type : 'uint:3', wr_dis: null, rd_dis: null, alt : '', dict : '', desc: Reserved; it was created by set_missed_fields_in_regs func, rloc: 'EFUSE_RD_REPEAT_DATA0_REG[31:29]', bloc: 'B7[7:5]'} + 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 : '', 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 : 14, rd_dis: null, alt : '', dict : '', desc: 'Represents the pseudo round level of xts-aes anti-dpa attack. 3: High. 2: Moderate 1. Low 0: Disabled', 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 : 14, rd_dis: null, alt : '', dict : '', desc: 'Represents whether xts-aes anti-dpa attack clock is enabled. 1. Enable. 0: Disable.', 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 : 14, rd_dis: null, alt : '', dict : '', desc: 'Represents whether to force ecc to use const-time calculation mode. 1: Enable. 0: Disable', 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: null, rd_dis: null, alt : '', dict : '', desc: Represents 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 : 1, rd_dis: null, alt : '', dict : '', desc: 'Represents whether the new key deployment of key manager is disabled. Bit0: Represents whether the new ECDSA key deployment is disabled0: Enabled1: DisabledBit1: Represents whether the new XTS-AES (flash and PSRAM) key deployment is disabled0: Enabled1: DisabledBit2: Represents whether the new HMAC key deployment is disabled0: Enabled1: DisabledBit3: Represents whether the new DS key deployment is disabled0: Enabled1: 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 : 1, rd_dis: null, alt : '', dict : '', desc: 'Represents the cycle at which the Key Manager switches random numbers.0: Controlled by the \hyperref[fielddesc:KEYMNGRNDSWITCHCYCLE]{KEYMNG\_RND\_SWITCH\_CYCLE} register. For more information; please refer to Chapter \ref{mod:keymng} \textit{\nameref{mod:keymng}}1: 8 Key Manager clock cycles2: 16 Key Manager clock cycles3: 32 Key Manager clock cycles', 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 : 1, rd_dis: null, alt : '', dict : '', desc: 'Represents whether the corresponding key can be deployed only once.Bit0: Represents whether the ECDSA key can be deployed only once0: The key can be deployed multiple times1: The key can be deployed only onceBit1: Represents whether the XTS-AES (flash and PSRAM) key can be deployed only once0: The key can be deployed multiple times1: The key can be deployed only onceBit2: Represents whether the HMAC key can be deployed only once0: The key can be deployed multiple times1: The key can be deployed only onceBit3: Represents whether the DS key can be deployed only once0: The key can be deployed multiple times1: The 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 : 1, rd_dis: null, alt : '', dict : '', desc: 'Represents whether the corresponding key must come from Key Manager. Bit0: Represents whether the ECDSA key must come from Key Manager.0: The key does not need to come from Key Manager1: The key must come from Key ManagerBit1: Represents whether the XTS-AES (flash and PSRAM) key must come from Key Manager.0: The key does not need to come from Key Manager1: The key must come from Key ManagerBit2: Represents whether the HMAC key must come from Key Manager.0: The key does not need to come from Key Manager1: The key must come from Key ManagerBit3: Represents whether the DS key must come from Key Manager.0: The key does not need to come from Key Manager1: The 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 : 1, rd_dis: null, alt : '', dict : '', desc: 'Represents whether to disable the use of the initialization key written by software and instead force use efuse\_init\_key.0: Enable1: Disable', 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 : 1, rd_dis: null, alt : '', dict : '', desc: 'Represents which key flash encryption uses.0: XTS-AES-256 key1: XTS-AES-128 key', 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 : 1, rd_dis: null, alt : '', dict : '', desc: 'Represents whether the keys in the Key Manager are locked after deployment.0: Not locked1: Locked', 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]'} + RESERVE_0_126 : {show: n, blk : 0, word: 3, pos: 30, len : 1, start: 126, type : bool, wr_dis: null, rd_dis: null, alt : '', dict : '', desc: Reserved; it was created by set_missed_fields_in_regs func, rloc: 'EFUSE_RD_REPEAT_DATA2_REG[30]', bloc: 'B15[6]'} + 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 : '', 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: Disable 0: Enable', 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 : '', desc: 'Represents the type of UART printing. 00: force enable printing 01: enable printing when GPIO8 is reset at low level 10: enable printing when GPIO8 is reset at high level 11: force disable printing', 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,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: 'Represents whether the HUK generate mode is valid.Odd count of bits with a value of 1: InvalidEven count of bits with a value of 1: Valid', 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: null, rd_dis: null, alt : '', dict : '', desc: 'Represents whether to select efuse control flash ldo default voltage. 1 : efuse 0 : strapping', rloc: 'EFUSE_RD_REPEAT_DATA3_REG[28]', bloc: 'B19[4]'} + RESERVE_0_157 : {show: n, blk : 0, word: 4, pos: 29, len : 3, start: 157, type : 'uint:3', wr_dis: null, rd_dis: null, alt : '', dict : '', desc: Reserved; it was created by set_missed_fields_in_regs func, rloc: 'EFUSE_RD_REPEAT_DATA3_REG[31:29]', bloc: 'B19[7:5]'} + RESERVE_0_160 : {show: n, blk : 0, word: 5, pos : 0, len : 8, start: 160, type : 'uint:8', wr_dis: null, rd_dis: null, alt : '', dict : '', desc: Reserved; it was created by set_missed_fields_in_regs func, rloc: 'EFUSE_RD_REPEAT_DATA4_REG[7:0]', bloc: B20} + USB_EXCHG_PINS : {show: y, blk : 0, word: 5, pos : 8, len : 1, start: 168, type : bool, wr_dis : 30, rd_dis: null, alt : '', dict : '', desc: 'Represents whether the D+ and D- pins of USB_SERIAL_JTAG PHY is exchanged. 1: exchanged 0: not exchanged', rloc: 'EFUSE_RD_REPEAT_DATA4_REG[8]', bloc: '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: 'Represents whether the D+ and D- pins of USB_OTG_FS PHY is exchanged. 1: exchanged 0: not exchanged', 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: 'Represents whether to exchange the USB_SERIAL_JTAG PHY with USB_OTG_FS PHY. 1: exchanged. 0: not exchanged', rloc: 'EFUSE_RD_REPEAT_DATA4_REG[10]', bloc: 'B21[2]'} + SOFT_DIS_JTAG : {show: y, blk : 0, word: 5, pos: 11, len : 3, start: 171, type : 'uint:3', wr_dis : 31, rd_dis: null, alt : '', dict : '', desc: 'Represents whether JTAG is disabled in soft way. Odd number: disabled Even number: enabled', rloc: 'EFUSE_RD_REPEAT_DATA4_REG[13:11]', bloc: 'B21[5:3]'} + IO_LDO_ADJUST : {show: y, blk : 0, word: 5, pos: 14, len : 8, start: 174, type : 'uint:8', wr_dis: null, rd_dis: null, alt : '', dict : '', desc: Represents configuration of IO LDO mode and voltage., rloc: 'EFUSE_RD_REPEAT_DATA4_REG[21:14]', bloc: 'B21[7:6],B22[5:0]'} + IO_LDO_1P8 : {show: y, blk : 0, word: 5, pos: 22, len : 1, start: 182, type : bool, wr_dis: null, rd_dis: null, alt : '', dict : '', desc: 'Represents select IO LDO voltage to 1.8V or 3.3V. 1: 1.8V 0: 3.3V', rloc: 'EFUSE_RD_REPEAT_DATA4_REG[22]', bloc: 'B22[6]'} + DCDC_CCM_EN : {show: y, blk : 0, word: 5, pos: 23, len : 1, start: 183, type : bool, wr_dis: null, rd_dis: null, alt : '', dict : '', desc: Represents whether change DCDC to CCM mode, rloc: 'EFUSE_RD_REPEAT_DATA4_REG[23]', bloc: 'B22[7]'} + RESERVE_0_184 : {show: n, blk : 0, word: 5, pos: 24, len : 8, start: 184, type : 'uint:8', wr_dis: null, rd_dis: null, alt : '', dict : '', desc: Reserved; it was created by set_missed_fields_in_regs func, rloc: 'EFUSE_RD_REPEAT_DATA4_REG[31:24]', bloc: B23} + 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_SYS0_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: Represents the extended bits of MAC address, rloc: 'EFUSE_RD_MAC_SYS1_REG[31:16]', bloc: 'B6,B7'} + PVT_LIMIT : {show: y, blk : 1, word: 2, pos : 0, len : 16, start : 64, type : 'uint:16', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: Power glitch monitor threthold, rloc: 'EFUSE_RD_MAC_SYS2_REG[15:0]', bloc: 'B8,B9'} + PVT_CELL_SELECT : {show: y, blk : 1, word: 2, pos: 16, len : 7, start : 80, type : 'uint:7', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: Power glitch monitor PVT cell select, rloc: 'EFUSE_RD_MAC_SYS2_REG[22:16]', bloc: 'B10[6:0]'} + PVT_PUMP_LIMIT : {show: y, blk : 1, word: 2, pos: 23, len : 8, start : 87, type : 'uint:8', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: Use to configure voltage monitor limit for charge pump, rloc: 'EFUSE_RD_MAC_SYS2_REG[30:23]', bloc: 'B10[7],B11[6:0]'} + RESERVE_1_95 : {show: n, blk : 1, word: 2, pos: 31, len : 1, start : 95, type : bool, wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: Reserved; it was created by set_missed_fields_in_regs func, rloc: 'EFUSE_RD_MAC_SYS2_REG[31]', bloc: 'B11[7]'} + PUMP_DRV : {show: y, blk : 1, word: 3, pos : 0, len : 4, start : 96, type : 'uint:4', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: Use to configure charge pump voltage gain, rloc: 'EFUSE_RD_MAC_SYS3_REG[3:0]', bloc: 'B12[3:0]'} + WDT_DELAY_SEL : {show: y, blk : 1, word: 3, pos : 4, len : 2, start: 100, type : 'uint:2', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: 'Represents the threshold level of the RTC watchdog STG0 timeout. 0: Original threshold configuration value of STG0 *2 1: Original threshold configuration value of STG0 *4 2: Original threshold configuration value of STG0 *8 3: Original threshold configuration value of STG0 *16 ', rloc: 'EFUSE_RD_MAC_SYS3_REG[5:4]', bloc: 'B12[5:4]'} + HYS_EN_PAD : {show: y, blk : 1, word: 3, pos : 6, len : 1, start: 102, type : bool, wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: 'Represents whether the hysteresis function of corresponding PAD is enabled. 1: enabled 0:disabled', rloc: 'EFUSE_RD_MAC_SYS3_REG[6]', bloc: 'B12[6]'} + PVT_GLITCH_CHARGE_RESET : {show: y, blk : 1, word: 3, pos : 7, len : 1, start: 103, type : bool, wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: 'Represents whether to trigger reset or charge pump when PVT power glitch happened.1:Trigger charge pump. 0:Trigger reset', rloc: 'EFUSE_RD_MAC_SYS3_REG[7]', bloc: 'B12[7]'} + RESERVE_1_104 : {show: n, blk : 1, word: 3, pos : 8, len : 1, start: 104, type : bool, wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: Reserved; it was created by set_missed_fields_in_regs func, rloc: 'EFUSE_RD_MAC_SYS3_REG[8]', bloc: 'B13[0]'} + VDD_SPI_LDO_ADJUST : {show: y, blk : 1, word: 3, pos : 9, len : 8, start: 105, type : 'uint:8', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: Represents configuration of FLASH LDO mode and voltage., rloc: 'EFUSE_RD_MAC_SYS3_REG[16:9]', bloc: 'B13[7:1],B14[0]'} + FLASH_LDO_POWER_SEL : {show: y, blk : 1, word: 3, pos: 17, len : 1, start: 113, type : bool, wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: 'Represents which flash ldo be select: 1: FLASH LDO 1P2 0 : FLASH LDO 1P8', rloc: 'EFUSE_RD_MAC_SYS3_REG[17]', bloc: 'B14[1]'} + SYS_DATA_PART0_0 : {show: n, blk : 1, word: 3, pos: 18, len : 14, start: 114, type : 'uint:14', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: Represents the first 14-bit of zeroth part of system data, rloc: 'EFUSE_RD_MAC_SYS3_REG[31:18]', bloc: 'B14[7:2],B15'} + SYS_DATA_PART0_1 : {show: n, blk : 1, word: 4, pos : 0, len : 32, start: 128, type : 'uint:32', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: Represents the first 14-bit of zeroth part of system data, rloc: EFUSE_RD_MAC_SYS4_REG, bloc: 'B16,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: Represents the second 32-bit of zeroth part of system data, rloc: EFUSE_RD_MAC_SYS5_REG, bloc: 'B20,B21,B22,B23'} + SYS_DATA_PART1_0 : {show: n, blk : 2, word: 0, pos : 0, len: 256, start : 0, type: 'bytes:32', wr_dis : 21, rd_dis: null, alt : '', dict : '', desc: Represents the zeroth 32-bit of first part of system data, rloc: EFUSE_RD_SYS_PART1_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_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'} diff --git a/tools/esptool_py/espefuse/efuse_defs/esp32p4.yaml b/tools/esptool_py/espefuse/efuse_defs/esp32p4.yaml index e4ffea60d7..d002552a4f 100644 --- a/tools/esptool_py/espefuse/efuse_defs/esp32p4.yaml +++ b/tools/esptool_py/espefuse/efuse_defs/esp32p4.yaml @@ -1,4 +1,4 @@ -VER_NO: d4a48929387e281bd05db8cfb3a85f60 +VER_NO: f7765f0ac3faf4b54f8c1f064307522c 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]'} @@ -10,7 +10,7 @@ EFUSES: DIS_FORCE_DOWNLOAD : {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 function that forces chip into download mode is disabled or enabled. 1: disabled. 0: enabled', rloc: 'EFUSE_RD_REPEAT_DATA0_REG[12]', bloc: 'B5[4]'} SPI_DOWNLOAD_MSPI_DIS : {show: y, blk : 0, word: 1, pos: 13, len : 1, start : 45, type : bool, wr_dis : 2, rd_dis: null, alt : '', dict : '', desc: Set this bit to disable accessing MSPI flash/MSPI ram by SYS AXI matrix during boot_mode_download, rloc: 'EFUSE_RD_REPEAT_DATA0_REG[13]', bloc: 'B5[5]'} DIS_TWAI : {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 TWAI function is disabled or enabled. 1: disabled. 0: enabled', rloc: 'EFUSE_RD_REPEAT_DATA0_REG[14]', bloc: 'B5[6]'} - JTAG_SEL_ENABLE : {show: y, blk : 0, word: 1, pos: 15, len : 1, start : 47, 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[15]', bloc: 'B5[7]'} + JTAG_SEL_ENABLE : {show: y, blk : 0, word: 1, pos: 15, len : 1, start : 47, 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 gpio34 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[15]', bloc: 'B5[7]'} SOFT_DIS_JTAG : {show: y, blk : 0, word: 1, pos: 16, len : 3, start : 48, type : 'uint:3', wr_dis : 31, rd_dis: null, alt : '', dict : '', desc: 'Represents whether JTAG is disabled in soft way. Odd number: disabled. Even number: enabled', rloc: 'EFUSE_RD_REPEAT_DATA0_REG[18:16]', bloc: 'B6[2:0]'} DIS_PAD_JTAG : {show: y, blk : 0, word: 1, pos: 19, len : 1, start : 51, 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[19]', bloc: 'B6[3]'} DIS_DOWNLOAD_MANUAL_ENCRYPT : {show: y, blk : 0, word: 1, pos: 20, len : 1, start : 52, 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[20]', bloc: 'B6[4]'} @@ -74,7 +74,7 @@ EFUSES: 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_SYS_0_REG, bloc: 'B0,B1,B2,B3,B4,B5'} RESERVED_1_16 : {show: n, blk : 1, word: 1, pos: 16, len : 16, start : 48, type : 'uint:16', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: Stores the extended bits of MAC address, rloc: 'EFUSE_RD_MAC_SYS_1_REG[31:16]', bloc: 'B6,B7'} WAFER_VERSION_MINOR : {show: y, blk : 1, word: 2, pos : 0, len : 4, start : 64, type : 'uint:4', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: Minor chip version, rloc: 'EFUSE_RD_MAC_SYS_2_REG[3:0]', bloc: 'B8[3:0]'} - WAFER_VERSION_MAJOR : {show: y, blk : 1, word: 2, pos : 4, len : 2, start : 68, type : 'uint:2', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: Major chip version, rloc: 'EFUSE_RD_MAC_SYS_2_REG[5:4]', bloc: 'B8[5:4]'} + WAFER_VERSION_MAJOR_LO : {show: y, blk : 1, word: 2, pos : 4, len : 2, start : 68, type : 'uint:2', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: Major chip version (lower 2 bits), rloc: 'EFUSE_RD_MAC_SYS_2_REG[5:4]', bloc: 'B8[5:4]'} DISABLE_WAFER_VERSION_MAJOR : {show: y, blk : 1, word: 2, pos : 6, len : 1, start : 70, type : bool, wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: Disables check of wafer version major, rloc: 'EFUSE_RD_MAC_SYS_2_REG[6]', bloc: 'B8[6]'} DISABLE_BLK_VERSION_MAJOR : {show: y, blk : 1, word: 2, pos : 7, len : 1, start : 71, type : bool, wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: Disables check of blk version major, rloc: 'EFUSE_RD_MAC_SYS_2_REG[7]', bloc: 'B8[7]'} BLK_VERSION_MINOR : {show: y, blk : 1, word: 2, pos : 8, len : 3, start : 72, type : 'uint:3', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: BLK_VERSION_MINOR of BLOCK2, rloc: 'EFUSE_RD_MAC_SYS_2_REG[10:8]', bloc: 'B9[2:0]'} @@ -83,16 +83,39 @@ EFUSES: TEMP : {show: y, blk : 1, word: 2, pos: 16, len : 2, start : 80, type : 'uint:2', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: Operating temperature of the ESP chip, rloc: 'EFUSE_RD_MAC_SYS_2_REG[17:16]', bloc: 'B10[1:0]'} PSRAM_VENDOR : {show: y, blk : 1, word: 2, pos: 18, len : 2, start : 82, type : 'uint:2', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: PSRAM vendor, rloc: 'EFUSE_RD_MAC_SYS_2_REG[19:18]', bloc: 'B10[3:2]'} PKG_VERSION : {show: y, blk : 1, word: 2, pos: 20, len : 3, start : 84, type : 'uint:3', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: Package version, rloc: 'EFUSE_RD_MAC_SYS_2_REG[22:20]', bloc: 'B10[6:4]'} - RESERVED_1_87 : {show: n, blk : 1, word: 2, pos: 23, len : 9, start : 87, type : 'uint:9', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: reserved, rloc: 'EFUSE_RD_MAC_SYS_2_REG[31:23]', bloc: 'B10[7],B11'} - MAC_RESERVED_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: Reserved, rloc: 'EFUSE_RD_MAC_SYS_3_REG[17:0]', bloc: 'B12,B13,B14[1:0]'} - SYS_DATA_PART0_0 : {show: n, blk : 1, word: 3, pos: 18, len : 14, start: 114, type : 'uint:14', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: Stores the first 14 bits of the zeroth part of system data, rloc: 'EFUSE_RD_MAC_SYS_3_REG[31:18]', bloc: 'B14[7:2],B15'} - SYS_DATA_PART0_1 : {show: n, blk : 1, word: 4, pos : 0, len : 32, start: 128, type : 'uint:32', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: Stores the first 32 bits of the zeroth part of system data, rloc: EFUSE_RD_MAC_SYS_4_REG, bloc: 'B16,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_SYS_5_REG, bloc: 'B20,B21,B22,B23'} + WAFER_VERSION_MAJOR_HI : {show: y, blk : 1, word: 2, pos: 23, len : 1, start : 87, type : bool, wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: Major chip version (MSB), rloc: 'EFUSE_RD_MAC_SYS_2_REG[23]', bloc: 'B10[7]'} + LDO_VO1_DREF : {show: y, blk : 1, word: 2, pos: 24, len : 4, start : 88, type : 'uint:4', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: Output VO1 parameter, rloc: 'EFUSE_RD_MAC_SYS_2_REG[27:24]', bloc: 'B11[3:0]'} + LDO_VO2_DREF : {show: y, blk : 1, word: 2, pos: 28, len : 4, start : 92, type : 'uint:4', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: Output VO2 parameter, rloc: 'EFUSE_RD_MAC_SYS_2_REG[31:28]', bloc: 'B11[7:4]'} + LDO_VO1_MUL : {show: y, blk : 1, word: 3, pos : 0, len : 3, start : 96, type : 'uint:3', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: Output VO1 parameter, rloc: 'EFUSE_RD_MAC_SYS_3_REG[2:0]', bloc: 'B12[2:0]'} + LDO_VO2_MUL : {show: y, blk : 1, word: 3, pos : 3, len : 3, start : 99, type : 'uint:3', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: Output VO2 parameter, rloc: 'EFUSE_RD_MAC_SYS_3_REG[5:3]', bloc: 'B12[5:3]'} + LDO_VO3_K : {show: y, blk : 1, word: 3, pos : 6, len : 8, start: 102, type : 'uint:8', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: Output VO3 calibration parameter, rloc: 'EFUSE_RD_MAC_SYS_3_REG[13:6]', bloc: 'B12[7:6],B13[5:0]'} + LDO_VO3_VOS : {show: y, blk : 1, word: 3, pos: 14, len : 6, start: 110, type : 'uint:6', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: Output VO3 calibration parameter, rloc: 'EFUSE_RD_MAC_SYS_3_REG[19:14]', bloc: 'B13[7:6],B14[3:0]'} + LDO_VO3_C : {show: y, blk : 1, word: 3, pos: 20, len : 6, start: 116, type : 'uint:6', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: Output VO3 calibration parameter, rloc: 'EFUSE_RD_MAC_SYS_3_REG[25:20]', bloc: 'B14[7:4],B15[1:0]'} + LDO_VO4_K : {show: y, blk : 1, word: 3, pos: 26, len : 8, start: 122, type : 'uint:8', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: Output VO4 calibration parameter, rloc: 'EFUSE_RD_MAC_SYS_3_REG[31:26]', bloc: 'B15[7:2],B16[1:0]'} + LDO_VO4_VOS : {show: y, blk : 1, word: 4, pos : 2, len : 6, start: 130, type : 'uint:6', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: Output VO4 calibration parameter, rloc: 'EFUSE_RD_MAC_SYS_4_REG[7:2]', bloc: 'B16[7:2]'} + LDO_VO4_C : {show: y, blk : 1, word: 4, pos : 8, len : 6, start: 136, type : 'uint:6', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: Output VO4 calibration parameter, rloc: 'EFUSE_RD_MAC_SYS_4_REG[13:8]', bloc: 'B17[5:0]'} + RESERVED_1_142 : {show: n, blk : 1, word: 4, pos: 14, len : 2, start: 142, type : 'uint:2', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: reserved, rloc: 'EFUSE_RD_MAC_SYS_4_REG[15:14]', bloc: 'B17[7:6]'} + ACTIVE_HP_DBIAS : {show: y, blk : 1, word: 4, pos: 16, len : 4, start: 144, type : 'uint:4', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: Active HP DBIAS of fixed voltage, rloc: 'EFUSE_RD_MAC_SYS_4_REG[19:16]', bloc: 'B18[3:0]'} + ACTIVE_LP_DBIAS : {show: y, blk : 1, word: 4, pos: 20, len : 4, start: 148, type : 'uint:4', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: Active LP DBIAS of fixed voltage, rloc: 'EFUSE_RD_MAC_SYS_4_REG[23:20]', bloc: 'B18[7:4]'} + LSLP_HP_DBIAS : {show: y, blk : 1, word: 4, pos: 24, len : 4, start: 152, type : 'uint:4', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: LSLP HP DBIAS of fixed voltage, rloc: 'EFUSE_RD_MAC_SYS_4_REG[27:24]', bloc: 'B19[3:0]'} + DSLP_DBG : {show: y, blk : 1, word: 4, pos: 28, len : 4, start: 156, type : 'uint:4', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: DSLP BDG of fixed voltage, rloc: 'EFUSE_RD_MAC_SYS_4_REG[31:28]', bloc: 'B19[7:4]'} + DSLP_LP_DBIAS : {show: y, blk : 1, word: 5, pos : 0, len : 5, start: 160, type : 'uint:5', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: DSLP LP DBIAS of fixed voltage, rloc: 'EFUSE_RD_MAC_SYS_5_REG[4:0]', bloc: 'B20[4:0]'} + LP_DCDC_DBIAS_VOL_GAP : {show: y, blk : 1, word: 5, pos : 5, len : 5, start: 165, type : 'uint:5', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: DBIAS gap between LP and DCDC, rloc: 'EFUSE_RD_MAC_SYS_5_REG[9:5]', bloc: 'B20[7:5],B21[1:0]'} + RESERVED_1_170 : {show: n, blk : 1, word: 5, pos: 10, len : 22, start: 170, type : 'uint:22', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: reserved, rloc: 'EFUSE_RD_MAC_SYS_5_REG[31:10]', bloc: 'B21[7:2],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'} - SYS_DATA_PART1_4 : {show: n, blk : 2, word: 4, pos : 0, len : 32, start: 128, type : 'uint:32', wr_dis : 21, rd_dis: null, alt : '', dict : '', desc: Stores the fourth 32 bits of the first part of system data, rloc: EFUSE_RD_SYS_PART1_DATA4_REG, bloc: 'B16,B17,B18,B19'} - SYS_DATA_PART1_5 : {show: n, blk : 2, word: 5, pos : 0, len : 32, start: 160, type : 'uint:32', wr_dis : 21, rd_dis: null, alt : '', dict : '', desc: Stores the fifth 32 bits of the first part of system data, rloc: EFUSE_RD_SYS_PART1_DATA5_REG, bloc: 'B20,B21,B22,B23'} - SYS_DATA_PART1_6 : {show: n, blk : 2, word: 6, pos : 0, len : 32, start: 192, type : 'uint:32', wr_dis : 21, rd_dis: null, alt : '', dict : '', desc: Stores the sixth 32 bits of the first part of system data, rloc: EFUSE_RD_SYS_PART1_DATA6_REG, bloc: 'B24,B25,B26,B27'} - SYS_DATA_PART1_7 : {show: n, blk : 2, word: 7, pos : 0, len : 32, start: 224, type : 'uint:32', wr_dis : 21, rd_dis: null, alt : '', dict : '', desc: Stores the seventh 32 bits of the first part of system data, rloc: EFUSE_RD_SYS_PART1_DATA7_REG, bloc: 'B28,B29,B30,B31'} + ADC1_AVE_INITCODE_ATTEN0 : {show: y, blk : 2, word: 4, pos : 0, len : 10, start: 128, type : 'uint:10', wr_dis : 21, rd_dis: null, alt : '', dict : '', desc: Average initcode of ADC1 atten0, rloc: 'EFUSE_RD_SYS_PART1_DATA4_REG[9:0]', bloc: 'B16,B17[1:0]'} + ADC1_AVE_INITCODE_ATTEN1 : {show: y, blk : 2, word: 4, pos: 10, len : 10, start: 138, type : 'uint:10', wr_dis : 21, rd_dis: null, alt : '', dict : '', desc: Average initcode of ADC1 atten1, rloc: 'EFUSE_RD_SYS_PART1_DATA4_REG[19:10]', bloc: 'B17[7:2],B18[3:0]'} + ADC1_AVE_INITCODE_ATTEN2 : {show: y, blk : 2, word: 4, pos: 20, len : 10, start: 148, type : 'uint:10', wr_dis : 21, rd_dis: null, alt : '', dict : '', desc: Average initcode of ADC1 atten2, rloc: 'EFUSE_RD_SYS_PART1_DATA4_REG[29:20]', bloc: 'B18[7:4],B19[5:0]'} + ADC1_AVE_INITCODE_ATTEN3 : {show: y, blk : 2, word: 4, pos: 30, len : 10, start: 158, type : 'uint:10', wr_dis : 21, rd_dis: null, alt : '', dict : '', desc: Average initcode of ADC1 atten3, rloc: 'EFUSE_RD_SYS_PART1_DATA4_REG[31:30]', bloc: 'B19[7:6],B20'} + ADC2_AVE_INITCODE_ATTEN0 : {show: y, blk : 2, word: 5, pos : 8, len : 10, start: 168, type : 'uint:10', wr_dis : 21, rd_dis: null, alt : '', dict : '', desc: Average initcode of ADC2 atten0, rloc: 'EFUSE_RD_SYS_PART1_DATA5_REG[17:8]', bloc: 'B21,B22[1:0]'} + ADC2_AVE_INITCODE_ATTEN1 : {show: y, blk : 2, word: 5, pos: 18, len : 10, start: 178, type : 'uint:10', wr_dis : 21, rd_dis: null, alt : '', dict : '', desc: Average initcode of ADC2 atten1, rloc: 'EFUSE_RD_SYS_PART1_DATA5_REG[27:18]', bloc: 'B22[7:2],B23[3:0]'} + ADC2_AVE_INITCODE_ATTEN2 : {show: y, blk : 2, word: 5, pos: 28, len : 10, start: 188, type : 'uint:10', wr_dis : 21, rd_dis: null, alt : '', dict : '', desc: Average initcode of ADC2 atten2, rloc: 'EFUSE_RD_SYS_PART1_DATA5_REG[31:28]', bloc: 'B23[7:4],B24[5:0]'} + ADC2_AVE_INITCODE_ATTEN3 : {show: y, blk : 2, word: 6, pos : 6, len : 10, start: 198, type : 'uint:10', wr_dis : 21, rd_dis: null, alt : '', dict : '', desc: Average initcode of ADC2 atten3, rloc: 'EFUSE_RD_SYS_PART1_DATA6_REG[15:6]', bloc: 'B24[7:6],B25'} + ADC1_HI_DOUT_ATTEN0 : {show: y, blk : 2, word: 6, pos: 16, len : 10, start: 208, type : 'uint:10', wr_dis : 21, rd_dis: null, alt : '', dict : '', desc: HI_DOUT of ADC1 atten0, rloc: 'EFUSE_RD_SYS_PART1_DATA6_REG[25:16]', bloc: 'B26,B27[1:0]'} + ADC1_HI_DOUT_ATTEN1 : {show: y, blk : 2, word: 6, pos: 26, len : 10, start: 218, type : 'uint:10', wr_dis : 21, rd_dis: null, alt : '', dict : '', desc: HI_DOUT of ADC1 atten1, rloc: 'EFUSE_RD_SYS_PART1_DATA6_REG[31:26]', bloc: 'B27[7:2],B28[3:0]'} + ADC1_HI_DOUT_ATTEN2 : {show: y, blk : 2, word: 7, pos : 4, len : 10, start: 228, type : 'uint:10', wr_dis : 21, rd_dis: null, alt : '', dict : '', desc: HI_DOUT of ADC1 atten2, rloc: 'EFUSE_RD_SYS_PART1_DATA7_REG[13:4]', bloc: 'B28[7:4],B29[5:0]'} + ADC1_HI_DOUT_ATTEN3 : {show: y, blk : 2, word: 7, pos: 14, len : 10, start: 238, type : 'uint:10', wr_dis : 21, rd_dis: null, alt : '', dict : '', desc: HI_DOUT of ADC1 atten3, rloc: 'EFUSE_RD_SYS_PART1_DATA7_REG[23:14]', bloc: 'B29[7:6],B30'} + RESERVED_2_248 : {show: n, blk : 2, word: 7, pos: 24, len : 8, start: 248, type : 'uint:8', wr_dis : 21, rd_dis: null, alt : '', dict : '', desc: reserved, rloc: 'EFUSE_RD_SYS_PART1_DATA7_REG[31:24]', bloc: B31} 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'} @@ -103,4 +126,27 @@ EFUSES: 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'} + ADC2_HI_DOUT_ATTEN0 : {show: y, blk: 10, word: 0, pos : 0, len : 10, start : 0, type : 'uint:10', wr_dis : 29, rd_dis : 6, alt : '', dict : '', desc: HI_DOUT of ADC2 atten0, rloc: 'EFUSE_RD_SYS_PART2_DATA0_REG[9:0]', bloc: 'B0,B1[1:0]'} + ADC2_HI_DOUT_ATTEN1 : {show: y, blk: 10, word: 0, pos: 10, len : 10, start : 10, type : 'uint:10', wr_dis : 29, rd_dis : 6, alt : '', dict : '', desc: HI_DOUT of ADC2 atten1, rloc: 'EFUSE_RD_SYS_PART2_DATA0_REG[19:10]', bloc: 'B1[7:2],B2[3:0]'} + ADC2_HI_DOUT_ATTEN2 : {show: y, blk: 10, word: 0, pos: 20, len : 10, start : 20, type : 'uint:10', wr_dis : 29, rd_dis : 6, alt : '', dict : '', desc: HI_DOUT of ADC2 atten2, rloc: 'EFUSE_RD_SYS_PART2_DATA0_REG[29:20]', bloc: 'B2[7:4],B3[5:0]'} + ADC2_HI_DOUT_ATTEN3 : {show: y, blk: 10, word: 0, pos: 30, len : 10, start : 30, type : 'uint:10', wr_dis : 29, rd_dis : 6, alt : '', dict : '', desc: HI_DOUT of ADC2 atten3, rloc: 'EFUSE_RD_SYS_PART2_DATA0_REG[31:30]', bloc: 'B3[7:6],B4'} + ADC1_CH0_ATTEN0_INITCODE_DIFF : {show: y, blk: 10, word: 1, pos : 8, len : 4, start : 40, type : 'uint:4', wr_dis : 29, rd_dis : 6, alt : '', dict : '', desc: Gap between ADC1_ch0 and average initcode, rloc: 'EFUSE_RD_SYS_PART2_DATA1_REG[11:8]', bloc: 'B5[3:0]'} + ADC1_CH1_ATTEN0_INITCODE_DIFF : {show: y, blk: 10, word: 1, pos: 12, len : 4, start : 44, type : 'uint:4', wr_dis : 29, rd_dis : 6, alt : '', dict : '', desc: Gap between ADC1_ch1 and average initcode, rloc: 'EFUSE_RD_SYS_PART2_DATA1_REG[15:12]', bloc: 'B5[7:4]'} + ADC1_CH2_ATTEN0_INITCODE_DIFF : {show: y, blk: 10, word: 1, pos: 16, len : 4, start : 48, type : 'uint:4', wr_dis : 29, rd_dis : 6, alt : '', dict : '', desc: Gap between ADC1_ch2 and average initcode, rloc: 'EFUSE_RD_SYS_PART2_DATA1_REG[19:16]', bloc: 'B6[3:0]'} + ADC1_CH3_ATTEN0_INITCODE_DIFF : {show: y, blk: 10, word: 1, pos: 20, len : 4, start : 52, type : 'uint:4', wr_dis : 29, rd_dis : 6, alt : '', dict : '', desc: Gap between ADC1_ch3 and average initcode, rloc: 'EFUSE_RD_SYS_PART2_DATA1_REG[23:20]', bloc: 'B6[7:4]'} + ADC1_CH4_ATTEN0_INITCODE_DIFF : {show: y, blk: 10, word: 1, pos: 24, len : 4, start : 56, type : 'uint:4', wr_dis : 29, rd_dis : 6, alt : '', dict : '', desc: Gap between ADC1_ch4 and average initcode, rloc: 'EFUSE_RD_SYS_PART2_DATA1_REG[27:24]', bloc: 'B7[3:0]'} + ADC1_CH5_ATTEN0_INITCODE_DIFF : {show: y, blk: 10, word: 1, pos: 28, len : 4, start : 60, type : 'uint:4', wr_dis : 29, rd_dis : 6, alt : '', dict : '', desc: Gap between ADC1_ch5 and average initcode, rloc: 'EFUSE_RD_SYS_PART2_DATA1_REG[31:28]', bloc: 'B7[7:4]'} + ADC1_CH6_ATTEN0_INITCODE_DIFF : {show: y, blk: 10, word: 2, pos : 0, len : 4, start : 64, type : 'uint:4', wr_dis : 29, rd_dis : 6, alt : '', dict : '', desc: Gap between ADC1_ch6 and average initcode, rloc: 'EFUSE_RD_SYS_PART2_DATA2_REG[3:0]', bloc: 'B8[3:0]'} + ADC1_CH7_ATTEN0_INITCODE_DIFF : {show: y, blk: 10, word: 2, pos : 4, len : 4, start : 68, type : 'uint:4', wr_dis : 29, rd_dis : 6, alt : '', dict : '', desc: Gap between ADC1_ch7 and average initcode, rloc: 'EFUSE_RD_SYS_PART2_DATA2_REG[7:4]', bloc: 'B8[7:4]'} + ADC2_CH0_ATTEN0_INITCODE_DIFF : {show: y, blk: 10, word: 2, pos : 8, len : 4, start : 72, type : 'uint:4', wr_dis : 29, rd_dis : 6, alt : '', dict : '', desc: Gap between ADC2_ch0 and average initcode, rloc: 'EFUSE_RD_SYS_PART2_DATA2_REG[11:8]', bloc: 'B9[3:0]'} + ADC2_CH1_ATTEN0_INITCODE_DIFF : {show: y, blk: 10, word: 2, pos: 12, len : 4, start : 76, type : 'uint:4', wr_dis : 29, rd_dis : 6, alt : '', dict : '', desc: Gap between ADC2_ch1 and average initcode, rloc: 'EFUSE_RD_SYS_PART2_DATA2_REG[15:12]', bloc: 'B9[7:4]'} + ADC2_CH2_ATTEN0_INITCODE_DIFF : {show: y, blk: 10, word: 2, pos: 16, len : 4, start : 80, type : 'uint:4', wr_dis : 29, rd_dis : 6, alt : '', dict : '', desc: Gap between ADC2_ch2 and average initcode, rloc: 'EFUSE_RD_SYS_PART2_DATA2_REG[19:16]', bloc: 'B10[3:0]'} + ADC2_CH3_ATTEN0_INITCODE_DIFF : {show: y, blk: 10, word: 2, pos: 20, len : 4, start : 84, type : 'uint:4', wr_dis : 29, rd_dis : 6, alt : '', dict : '', desc: Gap between ADC2_ch3 and average initcode, rloc: 'EFUSE_RD_SYS_PART2_DATA2_REG[23:20]', bloc: 'B10[7:4]'} + ADC2_CH4_ATTEN0_INITCODE_DIFF : {show: y, blk: 10, word: 2, pos: 24, len : 4, start : 88, type : 'uint:4', wr_dis : 29, rd_dis : 6, alt : '', dict : '', desc: Gap between ADC2_ch4 and average initcode, rloc: 'EFUSE_RD_SYS_PART2_DATA2_REG[27:24]', bloc: 'B11[3:0]'} + ADC2_CH5_ATTEN0_INITCODE_DIFF : {show: y, blk: 10, word: 2, pos: 28, len : 4, start : 92, type : 'uint:4', wr_dis : 29, rd_dis : 6, alt : '', dict : '', desc: Gap between ADC2_ch5 and average initcode, rloc: 'EFUSE_RD_SYS_PART2_DATA2_REG[31:28]', bloc: 'B11[7:4]'} + TEMPERATURE_SENSOR : {show: y, blk: 10, word: 3, pos : 0, len : 9, start : 96, type : 'uint:9', wr_dis : 29, rd_dis : 6, alt : '', dict : '', desc: Temperature calibration data, rloc: 'EFUSE_RD_SYS_PART2_DATA3_REG[8:0]', bloc: 'B12,B13[0]'} + RESERVED_10_105 : {show: n, blk: 10, word: 3, pos : 9, len : 23, start: 105, type : 'uint:23', wr_dis : 29, rd_dis : 6, alt : '', dict : '', desc: reserved, rloc: 'EFUSE_RD_SYS_PART2_DATA3_REG[31:9]', bloc: 'B13[7:1],B14,B15'} + SYS_DATA_PART2_4 : {show: n, blk: 10, word: 4, pos : 0, len : 32, start: 128, type : 'uint:32', wr_dis : 29, rd_dis : 6, alt : '', dict : '', desc: Stores the $nth 32 bits of the 2nd part of system data, rloc: EFUSE_RD_SYS_PART2_DATA4_REG, bloc: 'B16,B17,B18,B19'} + SYS_DATA_PART2_5 : {show: n, blk: 10, word: 5, pos : 0, len : 32, start: 160, type : 'uint:32', wr_dis : 29, rd_dis : 6, alt : '', dict : '', desc: Stores the $nth 32 bits of the 2nd part of system data, rloc: EFUSE_RD_SYS_PART2_DATA5_REG, bloc: 'B20,B21,B22,B23'} + SYS_DATA_PART2_6 : {show: n, blk: 10, word: 6, pos : 0, len : 32, start: 192, type : 'uint:32', wr_dis : 29, rd_dis : 6, alt : '', dict : '', desc: Stores the $nth 32 bits of the 2nd part of system data, rloc: EFUSE_RD_SYS_PART2_DATA6_REG, bloc: 'B24,B25,B26,B27'} + SYS_DATA_PART2_7 : {show: n, blk: 10, word: 7, pos : 0, len : 32, start: 224, type : 'uint:32', wr_dis : 29, rd_dis : 6, alt : '', dict : '', desc: Stores the $nth 32 bits of the 2nd part of system data, rloc: EFUSE_RD_SYS_PART2_DATA7_REG, bloc: 'B28,B29,B30,B31'} diff --git a/tools/esptool_py/espefuse/efuse_defs/esp32s3.yaml b/tools/esptool_py/espefuse/efuse_defs/esp32s3.yaml index 4d4c69511d..dcafa868f4 100644 --- a/tools/esptool_py/espefuse/efuse_defs/esp32s3.yaml +++ b/tools/esptool_py/espefuse/efuse_defs/esp32s3.yaml @@ -1,4 +1,4 @@ -VER_NO: f75f74727101326a187188a23f4a6c70 +VER_NO: 7127dd097e72bb90d0b790d460993126 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]'} @@ -47,7 +47,7 @@ EFUSES: SECURE_BOOT_AGGRESSIVE_REVOKE : {show: y, blk : 0, word: 3, pos: 21, len : 1, start: 117, type : bool, wr_dis : 16, rd_dis: null, alt : '', dict : '', desc: Set this bit to enable revoking aggressive secure boot, rloc: 'EFUSE_RD_REPEAT_DATA2_REG[21]', bloc: 'B14[5]'} DIS_USB_JTAG : {show: y, blk : 0, word: 3, pos: 22, len : 1, start: 118, type : bool, wr_dis : 2, rd_dis: null, alt : '', dict : '', desc: Set this bit to disable function of usb switch to jtag in module of usb device, rloc: 'EFUSE_RD_REPEAT_DATA2_REG[22]', bloc: 'B14[6]'} DIS_USB_SERIAL_JTAG : {show: y, blk : 0, word: 3, pos: 23, len : 1, start: 119, type : bool, wr_dis : 2, rd_dis: null, alt : DIS_USB_DEVICE, dict : '', desc: Set this bit to disable usb device, rloc: 'EFUSE_RD_REPEAT_DATA2_REG[23]', bloc: 'B14[7]'} - STRAP_JTAG_SEL : {show: y, blk : 0, word: 3, pos: 24, len : 1, start: 120, type : bool, wr_dis : 2, rd_dis: null, alt : '', dict : '', desc: Set this bit to enable selection between usb_to_jtag and pad_to_jtag through strapping gpio10 when both reg_dis_usb_jtag and reg_dis_pad_jtag are equal to 0, rloc: 'EFUSE_RD_REPEAT_DATA2_REG[24]', bloc: 'B15[0]'} + STRAP_JTAG_SEL : {show: y, blk : 0, word: 3, pos: 24, len : 1, start: 120, type : bool, wr_dis : 2, rd_dis: null, alt : '', dict : '', desc: Set this bit to enable selection between usb_to_jtag and pad_to_jtag through strapping gpio3 when both reg_dis_usb_jtag and reg_dis_pad_jtag are equal to 0, rloc: 'EFUSE_RD_REPEAT_DATA2_REG[24]', bloc: 'B15[0]'} USB_PHY_SEL : {show: y, blk : 0, word: 3, pos: 25, len : 1, start: 121, type : bool, wr_dis : 2, rd_dis: null, alt : '', dict: '{0: "internal PHY is assigned to USB Device while external PHY is assigned to USB OTG", 1: "internal PHY is assigned to USB OTG while external PHY is assigned to USB Device"}', desc: This bit is used to switch internal PHY and external PHY for USB OTG and USB Device, rloc: 'EFUSE_RD_REPEAT_DATA2_REG[25]', bloc: 'B15[1]'} POWER_GLITCH_DSENSE : {show: n, blk : 0, word: 3, pos: 26, len : 2, start: 122, type : 'uint:2', wr_dis : 17, rd_dis: null, alt : '', dict : '', desc: Sample delay configuration of power glitch, rloc: 'EFUSE_RD_REPEAT_DATA2_REG[27:26]', bloc: 'B15[3:2]'} FLASH_TPUW : {show: y, blk : 0, word: 3, pos: 28, len : 4, start: 124, type : 'uint:4', wr_dis : 18, rd_dis: null, alt : '', dict : '', desc: Configures flash waiting time after power-up; in unit of ms. If the value is less than 15; the waiting time is the configurable value. Otherwise; the waiting time is twice the configurable value, rloc: 'EFUSE_RD_REPEAT_DATA2_REG[31:28]', bloc: 'B15[7:4]'} @@ -87,7 +87,7 @@ EFUSES: FLASH_CAP : {show: y, blk : 1, word: 3, pos: 27, len : 3, start: 123, type : 'uint:3', wr_dis : 20, rd_dis: null, alt : '', dict: '{0: "None", 1: "8M", 2: "4M"}', desc: Flash capacity, rloc: 'EFUSE_RD_MAC_SPI_SYS_3_REG[29:27]', bloc: 'B15[5:3]'} FLASH_TEMP : {show: y, blk : 1, word: 3, pos: 30, len : 2, start: 126, type : 'uint:2', wr_dis : 20, rd_dis: null, alt : '', dict: '{0: "None", 1: "105C", 2: "85C"}', desc: Flash temperature, rloc: 'EFUSE_RD_MAC_SPI_SYS_3_REG[31:30]', bloc: 'B15[7:6]'} FLASH_VENDOR : {show: y, blk : 1, word: 4, pos : 0, len : 3, start: 128, type : 'uint:3', wr_dis : 20, rd_dis: null, alt : '', dict: '{0: "None", 1: "XMC", 2: "GD", 3: "FM", 4: "TT", 5: "BY"}', desc: Flash vendor, rloc: 'EFUSE_RD_MAC_SPI_SYS_4_REG[2:0]', bloc: 'B16[2:0]'} - PSRAM_CAP : {show: y, blk : 1, word: 4, pos : 3, len : 2, start: 131, type : 'uint:2', wr_dis : 20, rd_dis: null, alt : '', dict: '{0: "None", 1: "8M", 2: "2M"}', desc: PSRAM capacity, rloc: 'EFUSE_RD_MAC_SPI_SYS_4_REG[4:3]', bloc: 'B16[4:3]'} + PSRAM_CAP : {show: y, blk : 1, word: 4, pos : 3, len : 2, start: 131, type : 'uint:2', wr_dis : 20, rd_dis: null, alt : '', dict: '{0: "None", 1: "8M", 2: "2M", 3: "16M", 4: "4M"}', desc: PSRAM capacity, rloc: 'EFUSE_RD_MAC_SPI_SYS_4_REG[4:3]', bloc: 'B16[4:3]'} PSRAM_TEMP : {show: y, blk : 1, word: 4, pos : 5, len : 2, start: 133, type : 'uint:2', wr_dis : 20, rd_dis: null, alt : '', dict: '{0: "None", 1: "105C", 2: "85C"}', desc: PSRAM temperature, rloc: 'EFUSE_RD_MAC_SPI_SYS_4_REG[6:5]', bloc: 'B16[6:5]'} PSRAM_VENDOR : {show: y, blk : 1, word: 4, pos : 7, len : 2, start: 135, type : 'uint:2', wr_dis : 20, rd_dis: null, alt : '', dict: '{0: "None", 1: "AP_3v3", 2: "AP_1v8"}', desc: PSRAM vendor, rloc: 'EFUSE_RD_MAC_SPI_SYS_4_REG[8:7]', bloc: 'B16[7],B17[0]'} RESERVED_1_137 : {show: n, blk : 1, word: 4, pos : 9, len : 4, start: 137, type : 'uint:4', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: reserved, rloc: 'EFUSE_RD_MAC_SPI_SYS_4_REG[12:9]', bloc: 'B17[4:1]'} @@ -96,7 +96,9 @@ EFUSES: V_RTC_DBIAS20 : {show: y, blk : 1, word: 4, pos: 27, len : 8, start: 155, type : 'uint:8', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: BLOCK1 voltage of rtc dbias20, rloc: 'EFUSE_RD_MAC_SPI_SYS_4_REG[31:27]', bloc: 'B19[7:3],B20[2:0]'} V_DIG_DBIAS20 : {show: y, blk : 1, word: 5, pos : 3, len : 8, start: 163, type : 'uint:8', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: BLOCK1 voltage of digital dbias20, rloc: 'EFUSE_RD_MAC_SPI_SYS_5_REG[10:3]', bloc: 'B20[7:3],B21[2:0]'} DIG_DBIAS_HVT : {show: y, blk : 1, word: 5, pos: 11, len : 5, start: 171, type : 'uint:5', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: BLOCK1 digital dbias when hvt, rloc: 'EFUSE_RD_MAC_SPI_SYS_5_REG[15:11]', bloc: 'B21[7:3]'} - RESERVED_1_176 : {show: n, blk : 1, word: 5, pos: 16, len : 7, start: 176, type : 'uint:7', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: reserved, rloc: 'EFUSE_RD_MAC_SPI_SYS_5_REG[22:16]', bloc: 'B22[6:0]'} + RESERVED_1_176 : {show: n, blk : 1, word: 5, pos: 16, len : 3, start: 176, type : 'uint:3', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: reserved, rloc: 'EFUSE_RD_MAC_SPI_SYS_5_REG[18:16]', bloc: 'B22[2:0]'} + PSRAM_CAP_3 : {show: y, blk : 1, word: 5, pos: 19, len : 1, start: 179, type : bool, wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: PSRAM capacity bit 3, rloc: 'EFUSE_RD_MAC_SPI_SYS_5_REG[19]', bloc: 'B22[3]'} + RESERVED_1_180 : {show: n, blk : 1, word: 5, pos: 20, len : 3, start: 180, type : 'uint:3', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: reserved, rloc: 'EFUSE_RD_MAC_SPI_SYS_5_REG[22:20]', bloc: 'B22[6:4]'} WAFER_VERSION_MINOR_HI : {show: y, blk : 1, word: 5, pos: 23, len : 1, start: 183, type : bool, wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: WAFER_VERSION_MINOR most significant bit, rloc: 'EFUSE_RD_MAC_SPI_SYS_5_REG[23]', bloc: 'B22[7]'} WAFER_VERSION_MAJOR : {show: y, blk : 1, word: 5, pos: 24, len : 2, start: 184, type : 'uint:2', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: WAFER_VERSION_MAJOR, rloc: 'EFUSE_RD_MAC_SPI_SYS_5_REG[25:24]', bloc: 'B23[1:0]'} ADC2_CAL_VOL_ATTEN3 : {show: y, blk : 1, word: 5, pos: 26, len : 6, start: 186, type : 'uint:6', wr_dis : 20, rd_dis: null, alt : '', dict : '', desc: ADC2 calibration voltage at atten3, rloc: 'EFUSE_RD_MAC_SPI_SYS_5_REG[31:26]', bloc: 'B23[7:2]'} diff --git a/tools/esptool_py/espefuse/efuse_interface.py b/tools/esptool_py/espefuse/efuse_interface.py new file mode 100644 index 0000000000..b496796e11 --- /dev/null +++ b/tools/esptool_py/espefuse/efuse_interface.py @@ -0,0 +1,197 @@ +# SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD +# +# SPDX-License-Identifier: GPL-2.0-or-later + +from dataclasses import dataclass +from io import StringIO +from typing import Any + +from espefuse.efuse.base_operations import BaseCommands +from espefuse.efuse.emulate_efuse_controller_base import EmulateEfuseControllerBase +import esptool +from esptool.util import strip_chip_name + +import espefuse.efuse.esp32 as esp32_efuse +import espefuse.efuse.esp32c2 as esp32c2_efuse +import espefuse.efuse.esp32c3 as esp32c3_efuse +import espefuse.efuse.esp32c5 as esp32c5_efuse +import espefuse.efuse.esp32c6 as esp32c6_efuse +import espefuse.efuse.esp32c61 as esp32c61_efuse +import espefuse.efuse.esp32h2 as esp32h2_efuse +import espefuse.efuse.esp32h21 as esp32h21_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 + + +@dataclass +class DefChip: + efuse_lib: Any + chip_class: type[esptool.ESPLoader] + + +SUPPORTED_BURN_COMMANDS = [ + "read-protect-efuse", + "write-protect-efuse", + "burn-efuse", + "burn-block-data", + "burn-bit", + "burn-key", + "burn-key-digest", + "burn-custom-mac", + "set-flash-voltage", +] + +SUPPORTED_READ_COMMANDS = [ + "summary", + "dump", + "get-custom-mac", + "adc-info", + "check-error", +] + +DEPRECATED_COMMANDS = ["execute-scripts"] + +SUPPORTED_COMMANDS = ( + SUPPORTED_READ_COMMANDS + SUPPORTED_BURN_COMMANDS + DEPRECATED_COMMANDS +) + +SUPPORTED_CHIPS = { + "esp32": DefChip(esp32_efuse, esptool.targets.ESP32ROM), + "esp32c2": DefChip(esp32c2_efuse, esptool.targets.ESP32C2ROM), + "esp32c3": DefChip(esp32c3_efuse, esptool.targets.ESP32C3ROM), + "esp32c6": DefChip(esp32c6_efuse, esptool.targets.ESP32C6ROM), + "esp32c61": DefChip(esp32c61_efuse, esptool.targets.ESP32C61ROM), + "esp32c5": DefChip(esp32c5_efuse, esptool.targets.ESP32C5ROM), + "esp32h2": DefChip(esp32h2_efuse, esptool.targets.ESP32H2ROM), + "esp32h21": DefChip(esp32h21_efuse, esptool.targets.ESP32H21ROM), + "esp32h4": DefChip(esp32h4_efuse, esptool.targets.ESP32H4ROM), + "esp32p4": DefChip(esp32p4_efuse, esptool.targets.ESP32P4ROM), + "esp32s2": DefChip(esp32s2_efuse, esptool.targets.ESP32S2ROM), + "esp32s3": DefChip(esp32s3_efuse, esptool.targets.ESP32S3ROM), +} + + +def _get_command_class(chip_name: str) -> BaseCommands: + return SUPPORTED_CHIPS[chip_name].efuse_lib.commands() # type: ignore + + +def init_commands( + port: str | None = None, + baud: int = 115200, + before: str = "default-reset", + chip: str = "auto", + esp: esptool.ESPLoader | EmulateEfuseControllerBase | None = None, + **kwargs: Any, +) -> BaseCommands: + """Get the ESP eFuse commands class for the given chip + This function will establish a connection to the chip and + return the ESP eFuse commands class with initialized chip + and eFuse values. + + Either esp or port should be provided. If both are provided, esp will be used. + If neither is provided, the function will create a mock ESPLoader object for tests. + + Args: + port: The port to connect to the chip + baud: The baud rate to connect to the chip + before: The reset mode to use before connecting to the chip + chip: The chip to use. + esp: Optional ESPLoader object to use. If provided, the port, baud, before, and + chip arguments will be ignored. If provided, user has to take care of + closing the port. + + Keyword Args: + skip_connect (bool): Whether to skip connecting to the chip. Default is False. + virt (bool): Whether to use virtual mode. Default is False. + debug (bool): Whether to enable debug mode. Default is False. + virt_efuse_file (str): The file to save the eFuse values to. Default is None. + do_not_confirm (bool): Whether to skip confirmation before burning eFuse. + Default is False. + extend_efuse_table (str): The file to extend the eFuse table from. + Default is None. + batch_mode (bool): Whether to enable batch mode. Default is False. + + Returns: + The ESP eFuse commands class + """ + skip_connect = kwargs.get("skip_connect", False) + virt = kwargs.get("virt", False) + debug = kwargs.get("debug", False) + virt_efuse_file = kwargs.get("virt_efuse_file", None) + do_not_confirm = kwargs.get("do_not_confirm", False) + extend_efuse_table = kwargs.get("extend_efuse_table", None) + external_esp = esp is not None + batch_mode = kwargs.get("batch_mode", False) + + if esp is None: + esp = get_esp( + port, baud, before, chip, skip_connect, virt, debug, virt_efuse_file + ) + + commands = _get_command_class(strip_chip_name(esp.CHIP_NAME)) + commands.esp = esp + commands.external_esp = external_esp + commands.get_efuses( + skip_connect=skip_connect, + debug_mode=debug, + do_not_confirm=do_not_confirm, + extend_efuse_table=extend_efuse_table, + ) + if batch_mode: + commands.use_batch_mode() + return commands + + +def get_esp( + port: str | None = None, + baud: int = 115200, + before: str = "default-reset", + chip: str = "auto", + skip_connect: bool = False, + virt: bool = False, + debug: bool = False, + virt_efuse_file: str | None = None, +) -> esptool.ESPLoader | EmulateEfuseControllerBase: + """Get the ESPLoader object for the given chip. + Uses :func:`esptool.cmds.detect_chip` function. + + Args: + port: The port to connect to the chip + baud: The baud rate to connect to the chip + before: The reset mode to use before connecting to the chip + Supported values are: "default-reset", "usb-reset", "no-reset", + "no-reset-no-sync" + chip: The chip to use + skip_connect: Whether to skip connecting to the chip + virt: Whether to use virtual mode + debug: Whether to enable debug mode + virt_efuse_file: The file to save the eFuse values to + + Returns: + The ESPLoader object or EmulateEfuseController object + """ + if chip not in ["auto"] + list(SUPPORTED_CHIPS.keys()): + raise esptool.FatalError(f"get_esp: Unsupported chip ({chip})") + + if virt: + efuse = SUPPORTED_CHIPS.get(chip, SUPPORTED_CHIPS["esp32"]).efuse_lib + return efuse.EmulateEfuseController(virt_efuse_file, debug) # type: ignore + + if chip == "auto" and not skip_connect: + if port is None: + raise esptool.FatalError( + "get_esp: Port is required when chip is 'auto' to detect the chip" + ) + return esptool.detect_chip(port, baud, before) + + esp = SUPPORTED_CHIPS.get(chip, SUPPORTED_CHIPS["esp32"]).chip_class( + port if not skip_connect else StringIO(), # type: ignore + baud, + ) + if not skip_connect: + esp.connect(before) + if esp.sync_stub_detected: + esp = esp.STUB_CLASS(esp) # type: ignore + return esp diff --git a/tools/esptool_py/espsecure/__init__.py b/tools/esptool_py/espsecure/__init__.py index 81688c104d..2906124b01 100755 --- a/tools/esptool_py/espsecure/__init__.py +++ b/tools/esptool_py/espsecure/__init__.py @@ -1,8 +1,9 @@ -# SPDX-FileCopyrightText: 2016-2023 Espressif Systems (Shanghai) CO LTD +# SPDX-FileCopyrightText: 2016-2025 Espressif Systems (Shanghai) CO LTD # # SPDX-License-Identifier: GPL-2.0-or-later -# PYTHON_ARGCOMPLETE_OK -import argparse +import configparser +from dataclasses import dataclass +import rich_click as click import hashlib import operator import os @@ -10,8 +11,7 @@ import sys import tempfile import zlib -from collections import namedtuple -from io import IOBase +from typing import IO from cryptography import exceptions from cryptography.hazmat.backends import default_backend @@ -21,9 +21,10 @@ from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes from cryptography.utils import int_to_bytes -import ecdsa +from esptool.logger import log import esptool +from esptool.util import check_deprecated_py_suffix SIG_BLOCK_MAGIC = 0xE7 @@ -46,26 +47,28 @@ ) -def get_chunks(source, chunk_len): +def get_chunks(source: bytes, chunk_len: int): """Returns an iterator over 'chunk_len' chunks of 'source'""" return (source[i : i + chunk_len] for i in range(0, len(source), chunk_len)) -def endian_swap_words(source): +def endian_swap_words(source: bytes) -> bytes: """Endian-swap each word in 'source' bitstring""" assert len(source) % 4 == 0 words = "I" * (len(source) // 4) return struct.pack("<" + words, *struct.unpack(">" + words, source)) -def swap_word_order(source): +def swap_word_order(source: bytes) -> bytes: """Swap the order of the words in 'source' bitstring""" assert len(source) % 4 == 0 words = "I" * (len(source) // 4) return struct.pack(words, *reversed(struct.unpack(words, source))) -def _load_hardware_key(keyfile): +def _load_hardware_key( + keyfile: IO, is_flash_encryption_key: bool, aes_xts: bool = False +): """Load a 128/256/512-bit key, similar to stored in efuse, from a file 128-bit keys will be extended to 256-bit using the SHA256 of the key @@ -75,41 +78,56 @@ def _load_hardware_key(keyfile): key = keyfile.read() if len(key) not in [16, 24, 32, 64]: raise esptool.FatalError( - "Key file contains wrong length (%d bytes), 16, 24, 32 or 64 expected." - % len(key) + f"Key file contains wrong length ({len(key)} bytes), " + "16, 24, 32 or 64 expected." ) + if is_flash_encryption_key: + if aes_xts: + if len(key) not in [16, 32, 64]: + raise esptool.FatalError( + "AES_XTS supports only 128, 256, and 512-bit keys. " + f"Provided key is {len(key) * 8} bits." + ) + else: + if len(key) not in [24, 32]: + raise esptool.FatalError( + "ESP32 supports only 192 and 256-bit keys. Provided key is " + f"{len(key) * 8} bits. Use --aes_xts for other chips." + ) if len(key) == 16: key = _sha256_digest(key) - print("Using 128-bit key (extended)") + log.print("Using 128-bit key (extended).") elif len(key) == 24: key = key + key[8:16] assert len(key) == 32 - print("Using 192-bit key (extended)") + log.print("Using 192-bit key (extended).") elif len(key) == 32: - print("Using 256-bit key") + log.print("Using 256-bit key.") else: - print("Using 512-bit key") + log.print("Using 512-bit key.") return key -def digest_secure_bootloader(args): +def digest_secure_bootloader( + keyfile: IO, output: str | None, iv_file: IO | None, image: IO +): """Calculate the digest of a bootloader image, in the same way the hardware secure boot engine would do so. Can be used with a pre-loaded key to update a secure bootloader.""" - _check_output_is_not_input(args.keyfile, args.output) - _check_output_is_not_input(args.image, args.output) - _check_output_is_not_input(args.iv, args.output) - if args.iv is not None: - print("WARNING: --iv argument is for TESTING PURPOSES ONLY") - iv = args.iv.read(128) + _check_output_is_not_input(keyfile, output) + _check_output_is_not_input(image, output) + _check_output_is_not_input(iv_file, output) + if iv_file is not None: + log.warning("--iv argument is for TESTING PURPOSES ONLY") + iv = iv_file.read(128) else: iv = os.urandom(128) - plaintext_image = args.image.read() - args.image.seek(0) + plaintext_image = image.read() + image.seek(0) # secure boot engine reads in 128 byte blocks (ie SHA512 block # size), but also doesn't look for any appended SHA-256 digest - fw_image = esptool.bin_image.ESP32FirmwareImage(args.image) + fw_image = esptool.bin_image.ESP32FirmwareImage(image) if fw_image.append_digest: if len(plaintext_image) % 128 <= 32: # ROM bootloader will read to the end of the 128 byte block, but not @@ -120,7 +138,7 @@ def digest_secure_bootloader(args): # if image isn't 128 byte multiple then pad with 0xFF (ie unwritten flash) # as this is what the secure boot engine will see if len(plaintext_image) % 128 != 0: - plaintext_image += b"\xFF" * (128 - (len(plaintext_image) % 128)) + plaintext_image += b"\xff" * (128 - (len(plaintext_image) % 128)) plaintext = iv + plaintext_image @@ -129,7 +147,7 @@ def digest_secure_bootloader(args): # produce the digest. Each block in/out of ECB is reordered # (due to hardware quirks not for security.) - key = _load_hardware_key(args.keyfile) + key = _load_hardware_key(keyfile, False) backend = default_backend() cipher = Cipher(algorithms.AES(key), modes.ECB(), backend=backend) encryptor = cipher.encryptor() @@ -145,38 +163,49 @@ def digest_secure_bootloader(args): # Python hashlib can build each SHA block internally digest.update(block[::-1]) - if args.output is None: - args.output = os.path.splitext(args.image.name)[0] + "-digest-0x0000.bin" - with open(args.output, "wb") as f: + if output is None: + output = os.path.splitext(image.name)[0] + "-digest-0x0000.bin" + with open(output, "wb") as f: f.write(iv) - digest = digest.digest() - for word in get_chunks(digest, 4): + for word in get_chunks(digest.digest(), 4): f.write(word[::-1]) # swap word order in the result - f.write(b"\xFF" * (0x1000 - f.tell())) # pad to 0x1000 + f.write(b"\xff" * (0x1000 - f.tell())) # pad to 0x1000 f.write(plaintext_image) - print("digest+image written to %s" % args.output) + log.print(f'Digest + image written to "{output}"') + + +def _generate_ecdsa_signing_key(curve_id: ec.EllipticCurve, keyfile: str): + if curve_id not in [ec.SECP192R1, ec.SECP256R1, ec.SECP384R1]: + raise ValueError( + f"Unsupported curve: {curve_id}, " + "only NIST192p, NIST256p, NIST384p are supported." + ) + private_key = ec.generate_private_key(curve_id()) + pem = private_key.private_bytes( + encoding=serialization.Encoding.PEM, + format=serialization.PrivateFormat.TraditionalOpenSSL, + encryption_algorithm=serialization.NoEncryption(), + ) -def _generate_ecdsa_signing_key(curve_id, keyfile): - sk = ecdsa.SigningKey.generate(curve=curve_id) with open(keyfile, "wb") as f: - f.write(sk.to_pem()) + f.write(pem) -def generate_signing_key(args): - if os.path.exists(args.keyfile): - raise esptool.FatalError("ERROR: Key file %s already exists" % args.keyfile) - if args.version == "1": - if hasattr(args, "scheme"): - if args.scheme != "ecdsa256" and args.scheme is not None: - raise esptool.FatalError("ERROR: V1 only supports ECDSA256") +def generate_signing_key(version: int, scheme: str | None, keyfile: str): + if os.path.exists(keyfile): + raise esptool.FatalError(f"ERROR: Key file {keyfile} already exists.") + if version == "1": + if scheme is not None: + if scheme != "ecdsa256" and scheme is not None: + raise esptool.FatalError("ERROR: V1 only supports ECDSA256.") """ Generate an ECDSA signing key for signing secure boot images (post-bootloader) """ - _generate_ecdsa_signing_key(ecdsa.NIST256p, args.keyfile) - print("ECDSA NIST256p private key in PEM format written to %s" % args.keyfile) - elif args.version == "2": - if args.scheme == "rsa3072" or args.scheme is None: + _generate_ecdsa_signing_key(ec.SECP256R1, keyfile) + log.print(f'ECDSA NIST256p private key in PEM format written to "{keyfile}".') + elif version == "2": + if scheme == "rsa3072" or scheme is None: """Generate a RSA 3072 signing key for signing secure boot images""" private_key = rsa.generate_private_key( public_exponent=65537, key_size=3072, backend=default_backend() @@ -185,68 +214,80 @@ def generate_signing_key(args): format=serialization.PrivateFormat.TraditionalOpenSSL, encryption_algorithm=serialization.NoEncryption(), ) - with open(args.keyfile, "wb") as f: + with open(keyfile, "wb") as f: f.write(private_key) - print(f"RSA 3072 private key in PEM format written to {args.keyfile}") - elif args.scheme == "ecdsa192": + log.print(f'RSA 3072 private key in PEM format written to "{keyfile}".') + elif scheme == "ecdsa192": """Generate a ECDSA 192 signing key for signing secure boot images""" - _generate_ecdsa_signing_key(ecdsa.NIST192p, args.keyfile) - print(f"ECDSA NIST192p private key in PEM format written to {args.keyfile}") - elif args.scheme == "ecdsa256": + _generate_ecdsa_signing_key(ec.SECP192R1, keyfile) + log.print( + f'ECDSA NIST192p private key in PEM format written to "{keyfile}".' + ) + elif scheme == "ecdsa256": """Generate a ECDSA 256 signing key for signing secure boot images""" - _generate_ecdsa_signing_key(ecdsa.NIST256p, args.keyfile) - print(f"ECDSA NIST256p private key in PEM format written to {args.keyfile}") - elif args.scheme == "ecdsa384": + _generate_ecdsa_signing_key(ec.SECP256R1, keyfile) + log.print( + f'ECDSA NIST256p private key in PEM format written to "{keyfile}".' + ) + elif scheme == "ecdsa384": """Generate a ECDSA 384 signing key for signing secure boot images""" - _generate_ecdsa_signing_key(ecdsa.NIST384p, args.keyfile) - print(f"ECDSA NIST384p private key in PEM format written to {args.keyfile}") + _generate_ecdsa_signing_key(ec.SECP384R1, keyfile) + log.print( + f'ECDSA NIST384p private key in PEM format written to "{keyfile}".' + ) else: - raise esptool.FatalError("ERROR: Unsupported signing scheme {args.scheme}") + raise esptool.FatalError(f"ERROR: Unsupported signing scheme {scheme}.") -def load_ecdsa_signing_key(keyfile): +def load_ecdsa_signing_key(keyfile: IO) -> ec.EllipticCurvePrivateKey: """Load ECDSA signing key""" try: - sk = ecdsa.SigningKey.from_pem(keyfile.read()) + sk = serialization.load_pem_private_key( + keyfile.read(), password=None, backend=default_backend() + ) except ValueError: raise esptool.FatalError( "Incorrect ECDSA private key specified. " "Please check algorithm and/or format." ) - if sk.curve not in [ecdsa.NIST192p, ecdsa.NIST256p]: - raise esptool.FatalError("Supports NIST192p and NIST256p keys only") + if not isinstance(sk.curve, (ec.SECP192R1, ec.SECP256R1)): + raise esptool.FatalError("Supports NIST192p and NIST256p keys only.") return sk -def _load_ecdsa_signing_key(keyfile): +def _load_ecdsa_signing_key(keyfile: IO) -> ec.EllipticCurvePrivateKey: """Load ECDSA signing key for Secure Boot V1 only""" sk = load_ecdsa_signing_key(keyfile) - if sk.curve != ecdsa.NIST256p: + if not isinstance(sk.curve, ec.SECP256R1): raise esptool.FatalError( "Signing key uses incorrect curve. ESP32 Secure Boot only supports " - "NIST256p (openssl calls this curve 'prime256v1')" + "NIST256p (openssl calls this curve 'prime256v1')." ) return sk -def _load_ecdsa_verifying_key(keyfile): +def _load_ecdsa_verifying_key(keyfile: IO) -> ec.EllipticCurvePublicKey: """Load ECDSA verifying key for Secure Boot V1 only""" try: - vk = ecdsa.VerifyingKey.from_pem(keyfile.read()) + vk = serialization.load_pem_public_key( + keyfile.read(), backend=default_backend() + ) except ValueError: raise esptool.FatalError( "Incorrect ECDSA public key specified. " "Please check algorithm and/or format." ) - if vk.curve != ecdsa.NIST256p: + if not isinstance(vk.curve, ec.SECP256R1): raise esptool.FatalError( "Signing key uses incorrect curve. ESP32 Secure Boot only supports " - "NIST256p (openssl calls this curve 'prime256v1')" + "NIST256p (openssl calls this curve 'prime256v1')." ) return vk -def _load_sbv2_signing_key(keydata): +def _load_sbv2_signing_key( + keydata: bytes, +) -> rsa.RSAPrivateKey | ec.EllipticCurvePrivateKey: """ Load Secure Boot V2 signing key @@ -258,22 +299,23 @@ def _load_sbv2_signing_key(keydata): if isinstance(sk, rsa.RSAPrivateKey): if sk.key_size != 3072: raise esptool.FatalError( - "Key file has length %d bits. Secure boot v2 only supports RSA-3072." - % sk.key_size + f"Key file has length {sk.key_size} bits. Secure boot v2 only " + "supports RSA-3072." ) return sk if isinstance(sk, ec.EllipticCurvePrivateKey): if not isinstance(sk.curve, (ec.SECP192R1, ec.SECP256R1, ec.SECP384R1)): raise esptool.FatalError( "Key file uses incorrect curve. Secure Boot V2 + ECDSA only supports " - "NIST192p, NIST256p, NIST384p (aka prime192v1 / secp192r1, prime256v1 / secp256r1, secp384r1)" + "NIST192p, NIST256p, NIST384p (aka prime192v1 / secp192r1, " + "prime256v1 / secp256r1, secp384r1)." ) return sk - raise esptool.FatalError("Unsupported signing key for Secure Boot V2") + raise esptool.FatalError("Unsupported signing key for Secure Boot V2.") -def _load_sbv2_pub_key(keydata): +def _load_sbv2_pub_key(keydata: bytes) -> rsa.RSAPublicKey | ec.EllipticCurvePublicKey: """ Load Secure Boot V2 public key, can be rsa.RSAPublicKey or ec.EllipticCurvePublicKey """ @@ -281,22 +323,23 @@ def _load_sbv2_pub_key(keydata): if isinstance(vk, rsa.RSAPublicKey): if vk.key_size != 3072: raise esptool.FatalError( - "Key file has length %d bits. Secure boot v2 only supports RSA-3072." - % vk.key_size + f"Key file has length {vk.key_size} bits. Secure boot v2 only " + "supports RSA-3072." ) return vk if isinstance(vk, ec.EllipticCurvePublicKey): if not isinstance(vk.curve, (ec.SECP192R1, ec.SECP256R1, ec.SECP384R1)): raise esptool.FatalError( "Key file uses incorrect curve. Secure Boot V2 + ECDSA only supports " - "NIST192p, NIST256p, NIST384p (aka prime192v1 / secp192r1, prime256v1 / secp256r1, secp384r1)" + "NIST192p, NIST256p, NIST384p (aka prime192v1 / secp192r1, " + "prime256v1 / secp256r1, secp384r1)." ) return vk - raise esptool.FatalError("Unsupported public key for Secure Boot V2") + raise esptool.FatalError("Unsupported public key for Secure Boot V2.") -def _get_sbv2_pub_key(keyfile): +def _get_sbv2_pub_key(keyfile: IO) -> rsa.RSAPublicKey | ec.EllipticCurvePublicKey: key_data = keyfile.read() if ( b"-BEGIN RSA PRIVATE KEY" in key_data @@ -309,15 +352,23 @@ def _get_sbv2_pub_key(keyfile): else: raise esptool.FatalError( "Verification key does not appear to be an RSA Private or " - "Public key in PEM format. Unsupported" + "Public key in PEM format. Unsupported." ) return vk -def _get_sbv2_rsa_primitives(public_key): - primitives = namedtuple("primitives", ["n", "e", "m", "rinv"]) +@dataclass +class Primitives: + n: int + e: int + m: int + rinv: int + + +def _get_sbv2_rsa_primitives(public_key: rsa.RSAPublicKey) -> Primitives: + primitives = Primitives(0, 0, 0, 0) numbers = public_key.public_numbers() - primitives.n = numbers.n # + primitives.n = numbers.n primitives.e = numbers.e # two public key components # Note: this cheats and calls a private 'rsa' method to get the modular @@ -329,76 +380,127 @@ def _get_sbv2_rsa_primitives(public_key): return primitives -def _microecc_format(a, b, curve_len): +def _microecc_format(a: int, b: int, curve_len: int) -> bytes: """ Given two numbers (curve coordinates or (r,s) signature), write them out as a little-endian byte sequence suitable for micro-ecc "native little endian" mode """ byte_len = int(curve_len / 8) - ab = int_to_bytes(a, byte_len)[::-1] + int_to_bytes(b, byte_len)[::-1] + ab: bytes = int_to_bytes(a, byte_len)[::-1] + int_to_bytes(b, byte_len)[::-1] assert len(ab) in [48, 64, 96] return ab -def sign_data(args): - if args.keyfile: - _check_output_is_not_input(args.keyfile, args.output) - _check_output_is_not_input(args.datafile, args.output) - if args.version == "1": - return sign_secure_boot_v1(args) - elif args.version == "2": - return sign_secure_boot_v2(args) +def sign_data( + version: int, + keyfile: list[IO], + output: str | None, + append_signatures: bool, + hsm: bool, + hsm_config: IO | None, + pub_key: list[IO], + signature: list[IO], + datafile: IO, +): + if keyfile: + for file in keyfile: + _check_output_is_not_input(file, output) + _check_output_is_not_input(datafile, output) + if version == "1": + return sign_secure_boot_v1(keyfile, output, hsm, pub_key, signature, datafile) + elif version == "2": + return sign_secure_boot_v2( + keyfile, + output, + append_signatures, + hsm, + hsm_config, + pub_key, + signature, + datafile, + ) -def sign_secure_boot_v1(args): +def sign_secure_boot_v1( + keyfile: list[IO], + output: str | None, + hsm: bool, + pub_key: list[IO], + signatures: list[IO], + datafile: IO, +): """ Sign a data file with a ECDSA private key, append binary signature to file contents """ - binary_content = args.datafile.read() + binary_content = datafile.read() - if args.hsm: + if hsm: raise esptool.FatalError( "Secure Boot V1 does not support signing using an " "external Hardware Security Module (HSM)" ) - if args.signature: - print("Pre-calculated signatures found") - if len(args.pub_key) > 1: - raise esptool.FatalError("Secure Boot V1 only supports one signing key") - signature = args.signature[0].read() + if signatures: + log.print("Pre-calculated signatures found...") + if len(pub_key) > 1: + raise esptool.FatalError("Secure Boot V1 only supports one signing key.") + raw_signature = signatures[0].read() + # Signature needs to be DER-encoded for verification + r = int.from_bytes(raw_signature[:32], "big") + s = int.from_bytes(raw_signature[32:], "big") + signature = utils.encode_dss_signature(r, s) # get verifying/public key - vk = _load_ecdsa_verifying_key(args.pub_key[0]) + vk = _load_ecdsa_verifying_key(pub_key[0]) else: - if len(args.keyfile) > 1: - raise esptool.FatalError("Secure Boot V1 only supports one signing key") - sk = _load_ecdsa_signing_key(args.keyfile[0]) + if len(keyfile) > 1: + raise esptool.FatalError("Secure Boot V1 only supports one signing key.") + sk = _load_ecdsa_signing_key(keyfile[0]) - # calculate signature of binary data - signature = sk.sign_deterministic(binary_content, hashlib.sha256) + # calculate signature of binary data, returns DER-encoded signature + signature = sk.sign( + binary_content, ec.ECDSA(hashes.SHA256(), deterministic_signing=True) + ) # get verifying/public key - vk = sk.get_verifying_key() + vk = sk.public_key() # back-verify signature - vk.verify(signature, binary_content, hashlib.sha256) # throws exception on failure - if args.output is None or os.path.abspath(args.output) == os.path.abspath( - args.datafile.name + vk.verify( + signature, binary_content, ec.ECDSA(hashes.SHA256()) + ) # throws exception on failure + + # Secure boot signature block stores raw signature bytes, create raw signature + r, s = utils.decode_dss_signature(signature) + r_bytes = r.to_bytes(32, byteorder="big") + s_bytes = s.to_bytes(32, byteorder="big") + signature = r_bytes + s_bytes + + if output is None or os.path.abspath(output) == os.path.abspath( + datafile.name ): # append signature to input file - args.datafile.close() - outfile = open(args.datafile.name, "ab") + datafile.close() + outfile = open(datafile.name, "ab") else: # write file & signature to new file - outfile = open(args.output, "wb") + outfile = open(output, "wb") outfile.write(binary_content) outfile.write( struct.pack("I", 0) ) # Version indicator, allow for different curves/formats later outfile.write(signature) outfile.close() - print("Signed %d bytes of data from %s" % (len(binary_content), args.datafile.name)) - - -def sign_secure_boot_v2(args): + log.print(f'Signed {len(binary_content)} bytes of data from "{datafile.name}".') + + +def sign_secure_boot_v2( + keyfile: list[IO], + output: str | None, + append_signatures: bool, + hsm: bool, + hsm_config: IO | None, + pub_key: list[IO], + signature: list[IO], + datafile: IO, +): """ Sign a firmware app image with an RSA private key using RSA-PSS, or ECDSA private key using P192 or P256 or P384. @@ -406,15 +508,12 @@ def sign_secure_boot_v2(args): Write output file with a Secure Boot V2 header appended. """ SIG_BLOCK_MAX_COUNT = 3 - contents = args.datafile.read() + contents = datafile.read() sig_block_num = 0 signature_sector = b"" - signature = args.signature - pub_key = args.pub_key - if len(contents) % SECTOR_SIZE != 0: - if args.signature: + if signature: raise esptool.FatalError( "Secure Boot V2 requires the signature block to start " "from a 4KB aligned sector " @@ -422,13 +521,13 @@ def sign_secure_boot_v2(args): ) else: pad_by = SECTOR_SIZE - (len(contents) % SECTOR_SIZE) - print( + log.print( f"Padding data contents by {pad_by} bytes " - "so signature sector aligns at sector boundary" + "so signature sector aligns at sector boundary." ) contents += b"\xff" * pad_by - elif args.append_signatures: + elif append_signatures: while sig_block_num < SIG_BLOCK_MAX_COUNT: sig_block = validate_signature_block(contents, sig_block_num) if sig_block is None: @@ -439,62 +538,62 @@ def sign_secure_boot_v2(args): sig_block_num += 1 if len(signature_sector) % SIG_BLOCK_SIZE != 0: - raise esptool.FatalError("Incorrect signature sector size") + raise esptool.FatalError("Incorrect signature sector size.") if sig_block_num == 0: - print( + log.print( "No valid signature blocks found. " "Discarding --append-signature and proceeding to sign the image afresh." ) else: - print( + log.print( f"{sig_block_num} valid signature block(s) already present " "in the signature sector." ) if sig_block_num == SIG_BLOCK_MAX_COUNT: raise esptool.FatalError( - f"Upto {SIG_BLOCK_MAX_COUNT} signature blocks are supported. " - "(For ESP32-ECO3 only 1 signature block is supported)" + f"Up to {SIG_BLOCK_MAX_COUNT} signature blocks are supported " + "(For ESP32-ECO3 only 1 signature block is supported)." ) # Signature stripped off the content # (the legitimate blocks are included in signature_sector) contents = contents[: len(contents) - SECTOR_SIZE] - if args.hsm: - if args.hsm_config is None: + if hsm: + if hsm_config is None: raise esptool.FatalError( "Config file is required to generate signature using an external HSM." ) - import espsecure.esp_hsm_sign as hsm + import espsecure.esp_hsm_sign as hsm_sign try: - config = hsm.read_hsm_config(args.hsm_config) + config = hsm_sign.read_hsm_config(hsm_config) except Exception as e: - raise esptool.FatalError(f"Incorrect HSM config file format ({e})") - if pub_key is None: + raise esptool.FatalError(f"Incorrect HSM config file format ({e}).") + if len(pub_key) == 0: pub_key = extract_pubkey_from_hsm(config) signature = generate_signature_using_hsm(config, contents) if signature: - print("Pre-calculated signatures found") + log.print("Pre-calculated signatures found...") key_count = len(pub_key) if len(signature) != key_count: raise esptool.FatalError( f"Number of public keys ({key_count}) not equal to " - f"the number of signatures {len(signature)}." + f"the number of signatures ({len(signature)})." ) else: - key_count = len(args.keyfile) + key_count = len(keyfile) empty_signature_blocks = SIG_BLOCK_MAX_COUNT - sig_block_num if key_count > empty_signature_blocks: raise esptool.FatalError( - f"Number of keys({key_count}) more than the empty signature blocks." - f"({empty_signature_blocks})" + f"Number of keys ({key_count}) more than the empty signature blocks " + f"({empty_signature_blocks})." ) - print(f"{key_count} signing key(s) found.") + log.print(f"{key_count} signing key(s) found.") # Generate signature block using pre-calculated signatures if signature: @@ -503,12 +602,10 @@ def sign_secure_boot_v2(args): ) # Generate signature block by signing using private keys else: - signature_block = generate_signature_block_using_private_key( - args.keyfile, contents - ) + signature_block = generate_signature_block_using_private_key(keyfile, contents) if signature_block is None or len(signature_block) == 0: - raise esptool.FatalError("Signature Block generation failed") + raise esptool.FatalError("Signature Block generation failed.") signature_sector += signature_block @@ -517,7 +614,7 @@ def sign_secure_boot_v2(args): and len(signature_sector) > SIG_BLOCK_SIZE * 3 and len(signature_sector) % SIG_BLOCK_SIZE != 0 ): - raise esptool.FatalError("Incorrect signature sector generation") + raise esptool.FatalError("Incorrect signature sector generation.") total_sig_blocks = len(signature_sector) // SIG_BLOCK_SIZE @@ -526,21 +623,23 @@ def sign_secure_boot_v2(args): b"\xff" * (SECTOR_SIZE - len(signature_sector)) ) if len(signature_sector) != SECTOR_SIZE: - raise esptool.FatalError("Incorrect signature sector size") + raise esptool.FatalError("Incorrect signature sector size.") # Write to output file, or append to existing file - if args.output is None: - args.datafile.close() - args.output = args.datafile.name - with open(args.output, "wb") as f: + if output is None: + datafile.close() + output = datafile.name + with open(output, "wb") as f: f.write(contents + signature_sector) - print( - f"Signed {len(contents)} bytes of data from {args.datafile.name}. " + log.print( + f'Signed {len(contents)} bytes of data from "{datafile.name}". ' f"Signature sector now has {total_sig_blocks} signature blocks." ) -def generate_signature_using_hsm(config, contents): +def generate_signature_using_hsm( + config: configparser.SectionProxy, contents: bytes +) -> list[IO]: import espsecure.esp_hsm_sign as hsm session = hsm.establish_session(config) @@ -556,13 +655,13 @@ def generate_signature_using_hsm(config, contents): def generate_signature_block_using_pre_calculated_signature( - signature, pub_key, contents -): + signature: list[IO], pub_key: list[IO], contents: bytes +) -> bytes: signature_blocks = b"" for sig, pk in zip(signature, pub_key): try: public_key = _get_sbv2_pub_key(pk) - signature = sig.read() + sig_bytes = sig.read() if isinstance(public_key, rsa.RSAPublicKey): # Calculate digest of data file digest = _sha256_digest(contents) @@ -570,14 +669,14 @@ def generate_signature_block_using_pre_calculated_signature( rsa_primitives = _get_sbv2_rsa_primitives(public_key) # Verify the signature public_key.verify( - signature, + sig_bytes, digest, padding.PSS(mgf=padding.MGF1(hashes.SHA256()), salt_length=32), utils.Prehashed(hashes.SHA256()), ) signature_block = generate_rsa_signature_block( - digest, rsa_primitives, signature + digest, rsa_primitives, sig_bytes ) else: # ECDSA signature @@ -602,11 +701,11 @@ def generate_signature_block_using_pre_calculated_signature( # Verify the signature public_key.verify( - signature, digest, ec.ECDSA(utils.Prehashed(hash_type)) + sig_bytes, digest, ec.ECDSA(utils.Prehashed(hash_type)) ) pubkey_point = _microecc_format(numbers.x, numbers.y, curve_len) - r, s = utils.decode_dss_signature(signature) + r, s = utils.decode_dss_signature(sig_bytes) signature_rs = _microecc_format(r, s, curve_len) signature_block = generate_ecdsa_signature_block( digest, curve_id, pubkey_point, signature_rs @@ -615,19 +714,21 @@ def generate_signature_block_using_pre_calculated_signature( raise esptool.FatalError( "Signature verification failed: Invalid Signature\n" "The pre-calculated signature has not been signed " - "using the given public key" + "using the given public key." ) signature_block += struct.pack(" bytes: signature_blocks = b"" for keyfile in keyfiles: private_key = _load_sbv2_signing_key(keyfile.read()) @@ -683,13 +784,15 @@ def generate_signature_block_using_private_key(keyfiles, contents): signature_block += b"\x00" * 16 # padding if len(signature_block) != SIG_BLOCK_SIZE: - raise esptool.FatalError("Incorrect signature block size") + raise esptool.FatalError("Incorrect signature block size.") signature_blocks += signature_block return signature_blocks -def generate_rsa_signature_block(digest, rsa_primitives, signature): +def generate_rsa_signature_block( + digest: bytes, rsa_primitives: Primitives, signature: bytes +) -> bytes: """ Encode in rsa signature block format @@ -712,7 +815,9 @@ def generate_rsa_signature_block(digest, rsa_primitives, signature): return signature_block -def generate_ecdsa_signature_block(digest, curve_id, pubkey_point, signature_rs): +def generate_ecdsa_signature_block( + digest: bytes, curve_id: int, pubkey_point: bytes, signature_rs: bytes +) -> bytes: """ Encode in rsa signature block format @@ -750,54 +855,68 @@ def generate_ecdsa_signature_block(digest, curve_id, pubkey_point, signature_rs) return signature_block -def verify_signature(args): - if args.version == "1": - return verify_signature_v1(args) - elif args.version == "2": - return verify_signature_v2(args) +def verify_signature( + version: int, + hsm: bool, + hsm_config: IO | None, + keyfile: IO, + datafile: IO, +): + if version == "1": + return verify_signature_v1(keyfile, datafile) + elif version == "2": + return verify_signature_v2(hsm, hsm_config, keyfile, datafile) -def verify_signature_v1(args): +def verify_signature_v1(keyfile: IO, datafile: IO): """Verify a previously signed binary image, using the ECDSA public key""" - key_data = args.keyfile.read() + key_data = keyfile.read() if b"-BEGIN EC PRIVATE KEY" in key_data: - sk = ecdsa.SigningKey.from_pem(key_data) - vk = sk.get_verifying_key() + sk = serialization.load_pem_private_key( + key_data, password=None, backend=default_backend() + ) + vk = sk.public_key() elif b"-BEGIN PUBLIC KEY" in key_data: - vk = ecdsa.VerifyingKey.from_pem(key_data) - elif len(key_data) == 64: - vk = ecdsa.VerifyingKey.from_string(key_data, curve=ecdsa.NIST256p) + vk = serialization.load_pem_public_key(key_data, backend=default_backend()) + elif len(key_data) == 64: # Raw public key bytes + x = int.from_bytes(key_data[:32], byteorder="big") # x coordinates + y = int.from_bytes(key_data[32:], byteorder="big") # y coordinates + numbers = ec.EllipticCurvePublicNumbers(x, y, ec.SECP256R1()) + vk = numbers.public_key(backend=default_backend()) else: raise esptool.FatalError( "Verification key does not appear to be an EC key in PEM format " - "or binary EC public key data. Unsupported" + "or binary EC public key data. Unsupported." ) - if vk.curve != ecdsa.NIST256p: + if not isinstance(vk.curve, ec.SECP256R1): raise esptool.FatalError( "Public key uses incorrect curve. ESP32 Secure Boot only supports " - "NIST256p (openssl calls this curve 'prime256v1" + "NIST256p (openssl calls this curve 'prime256v1')." ) - binary_content = args.datafile.read() + binary_content = datafile.read() data = binary_content[0:-68] sig_version, signature = struct.unpack("I64s", binary_content[-68:]) if sig_version != 0: raise esptool.FatalError( - "Signature block has version %d. This version of espsecure " - "only supports version 0." % sig_version + f"Signature block has version {sig_version}. This version of espsecure " + "only supports version 0." ) - print("Verifying %d bytes of data" % len(data)) + log.print(f"Verifying {len(data)} bytes of data...") try: - if vk.verify(signature, data, hashlib.sha256): - print("Signature is valid") - else: - raise esptool.FatalError("Signature is not valid") - except ecdsa.keys.BadSignatureError: - raise esptool.FatalError("Signature is not valid") + # Convert raw signature to DER format + r = int.from_bytes(signature[:32], byteorder="big") + s = int.from_bytes(signature[32:], byteorder="big") + der_signature = utils.encode_dss_signature(r, s) + vk.verify(der_signature, data, ec.ECDSA(hashes.SHA256())) + log.print("Signature is valid.") + except exceptions.InvalidSignature: + raise esptool.FatalError("Signature is not valid.") -def validate_signature_block(image_content, sig_blk_num): + +def validate_signature_block(image_content: bytes, sig_blk_num: int) -> bytes | None: offset = -SECTOR_SIZE + sig_blk_num * SIG_BLOCK_SIZE sig_blk = image_content[offset : offset + SIG_BLOCK_SIZE] assert len(sig_blk) == SIG_BLOCK_SIZE @@ -818,25 +937,24 @@ def validate_signature_block(image_content, sig_blk_num): if is_invalid_block or blk_crc != calc_crc & 0xFFFFFFFF: # Signature block invalid return None key_type = "RSA" if version == SIG_BLOCK_VERSION_RSA else "ECDSA" - print(f"Signature block {sig_blk_num} is valid ({key_type}).") + log.print(f"Signature block {sig_blk_num} is valid ({key_type}).") return sig_blk -def verify_signature_v2(args): +def verify_signature_v2(hsm: bool, hsm_config: IO | None, keyfile: IO, datafile: IO): """Verify a previously signed binary image, using the RSA or ECDSA public key""" - keyfile = args.keyfile - if args.hsm: - if args.hsm_config is None: + if hsm: + if hsm_config is None: raise esptool.FatalError( "Config file is required to extract public key from an external HSM." ) - import espsecure.esp_hsm_sign as hsm + import espsecure.esp_hsm_sign as hsm_sign try: - config = hsm.read_hsm_config(args.hsm_config) + config = hsm_sign.read_hsm_config(hsm_config) except Exception as e: - raise esptool.FatalError(f"Incorrect HSM config file format ({e})") + raise esptool.FatalError(f"Incorrect HSM config file format ({e}).") # get public key from HSM keyfile = extract_pubkey_from_hsm(config)[0] @@ -847,7 +965,7 @@ def verify_signature_v2(args): elif isinstance(vk, ec.EllipticCurvePublicKey): SIG_BLOCK_MAX_COUNT = 1 - image_content = args.datafile.read() + image_content = datafile.read() if len(image_content) < SECTOR_SIZE or len(image_content) % SECTOR_SIZE != 0: raise esptool.FatalError( "Invalid datafile. Data size should be non-zero & a multiple of 4096." @@ -858,7 +976,7 @@ def verify_signature_v2(args): for sig_blk_num in range(SIG_BLOCK_MAX_COUNT): sig_blk = validate_signature_block(image_content, sig_blk_num) if sig_blk is None: - print(f"Signature block {sig_blk_num} invalid. Skipping.") + log.print(f"Signature block {sig_blk_num} invalid. Skipping.") continue _, version, ecdsa_sha_version = struct.unpack(" list[IO]: import espsecure.esp_hsm_sign as hsm session = hsm.establish_session(config) @@ -994,14 +1108,14 @@ def _sha384_digest(contents): return digest.digest() -def signature_info_v2(args): +def signature_info_v2(datafile: IO): """ Validates the signature block and prints the RSA/ECDSA public key digest for valid blocks """ SIG_BLOCK_MAX_COUNT = 3 - image_content = args.datafile.read() + image_content = datafile.read() if len(image_content) < SECTOR_SIZE or len(image_content) % SECTOR_SIZE != 0: raise esptool.FatalError( "Invalid datafile. Data size should be non-zero & a multiple of 4096." @@ -1010,9 +1124,9 @@ def signature_info_v2(args): for sig_blk_num in range(SIG_BLOCK_MAX_COUNT): sig_blk = validate_signature_block(image_content, sig_blk_num) if sig_blk is None: - print( - "Signature block %d absent/invalid. Skipping checking next blocks." - % sig_blk_num + log.print( + f"Signature block {sig_blk_num} absent/invalid. Skipping checking " + "next blocks..." ) return @@ -1027,31 +1141,32 @@ def signature_info_v2(args): if sig_data[2] != digest: raise esptool.FatalError( - "Digest in signature block %d doesn't match the image digest." - % (sig_blk_num) + f"Digest in signature block {sig_blk_num} doesn't match " + "the image digest." ) offset = -SECTOR_SIZE + sig_blk_num * SIG_BLOCK_SIZE - sig_blk = image_content[offset : offset + SIG_BLOCK_SIZE] + sig_block = image_content[offset : offset + SIG_BLOCK_SIZE] if sig_data[1] == SIG_BLOCK_VERSION_RSA: - key_digest = _sha256_digest(sig_blk[36:812]) + key_digest = _sha256_digest(sig_block[36:812]) elif sig_data[1] == SIG_BLOCK_VERSION_ECDSA: if ecdsa_sha_version == ECDSA_SHA_384: - key_digest = _sha256_digest(sig_blk[52:149]) + key_digest = _sha256_digest(sig_block[52:149]) else: - key_digest = _sha256_digest(sig_blk[36:101]) + key_digest = _sha256_digest(sig_block[36:101]) else: raise esptool.FatalError( - "Unsupported scheme in signature block %d" % (sig_blk_num) + f"Unsupported scheme in signature block {sig_blk_num}." ) - print( - "Public key digest for block %d: %s" - % (sig_blk_num, " ".join("{:02x}".format(c) for c in bytearray(key_digest))) + log.print( + f"Public key digest for block {sig_blk_num}: ".join( + f"{c:02x}" for c in bytearray(key_digest) + ) ) -def _digest_sbv2_public_key(keyfile): +def _digest_sbv2_public_key(keyfile: IO) -> bytes: public_key = _get_sbv2_pub_key(keyfile) if isinstance(public_key, rsa.RSAPublicKey): @@ -1100,39 +1215,32 @@ def _digest_sbv2_public_key(keyfile): return hashlib.sha256(binary_format).digest() -def digest_sbv2_public_key(args): - _check_output_is_not_input(args.keyfile, args.output) - public_key_digest = _digest_sbv2_public_key(args.keyfile) - with open(args.output, "wb") as f: - print( - "Writing the public key digest of %s to %s." - % (args.keyfile.name, args.output) - ) +def digest_sbv2_public_key(keyfile: IO, output: str): + _check_output_is_not_input(keyfile, output) + public_key_digest = _digest_sbv2_public_key(keyfile) + with open(output, "wb") as f: + log.print(f'Writing the public key digest of "{keyfile.name}" to "{output}".') f.write(public_key_digest) -def digest_rsa_public_key(args): - # Kept for compatibility purpose - digest_sbv2_public_key(args) +def get_ecdsa_signing_key_raw_bytes(sk): + return sk.private_numbers().private_value.to_bytes( + length=(sk.key_size + 7) // 8, byteorder="big" + ) -def digest_private_key(args): - _check_output_is_not_input(args.keyfile, args.digest_file) - sk = _load_ecdsa_signing_key(args.keyfile) - repr(sk.to_string()) - digest = hashlib.sha256() - digest.update(sk.to_string()) - result = digest.digest() - if args.keylen == 192: +def digest_private_key(keyfile: IO, keylen: int, digest_file: IO): + _check_output_is_not_input(keyfile, digest_file) + sk = _load_ecdsa_signing_key(keyfile) + private_bytes = get_ecdsa_signing_key_raw_bytes(sk) + result = hashlib.sha256(private_bytes).digest() + if keylen == 192: result = result[0:24] - args.digest_file.write(result) - print( - "SHA-256 digest of private key %s%s written to %s" - % ( - args.keyfile.name, - "" if args.keylen == 256 else " (truncated to 192 bits)", - args.digest_file.name, - ) + digest_file.write(result) + len_msg = "" if keylen == 256 else " (truncated to 192 bits)" + log.print( + f'SHA-256 digest of private key "{keyfile.name}"{len_msg} ' + f'written to "{digest_file.name}".' ) @@ -1162,23 +1270,7 @@ def digest_private_key(args): # fmt: on -def _flash_encryption_tweak_range(flash_crypt_config=0xF): - """Return a list of the bit indexes that the "key tweak" applies to, - as determined by the FLASH_CRYPT_CONFIG 4 bit efuse value. - """ - tweak_range = [] - if (flash_crypt_config & 1) != 0: - tweak_range += range(67) - if (flash_crypt_config & 2) != 0: - tweak_range += range(67, 132) - if (flash_crypt_config & 4) != 0: - tweak_range += range(132, 195) - if (flash_crypt_config & 8) != 0: - tweak_range += range(195, 256) - return tweak_range - - -def _flash_encryption_tweak_range_bits(flash_crypt_config=0xF): +def _flash_encryption_tweak_range_bits(flash_crypt_config: int = 0xF) -> int: """Return bits (in reverse order) that the "key tweak" applies to, as determined by the FLASH_CRYPT_CONFIG 4 bit efuse value. """ @@ -1210,7 +1302,7 @@ def _flash_encryption_tweak_range_bits(flash_crypt_config=0xF): mul2_mask = 0x000000000000007FE00000000000000FF000000000000007E00000000000000F -def _flash_encryption_tweak_key(key, offset, tweak_range): +def _flash_encryption_tweak_key(key: int, offset: int, tweak_range: int) -> bytes: """Apply XOR "tweak" values to the key, derived from flash offset 'offset'. This matches the ESP32 hardware flash encryption. @@ -1225,23 +1317,35 @@ def _flash_encryption_tweak_key(key, offset, tweak_range): return int.to_bytes(key, length=32, byteorder="big", signed=False) -def generate_flash_encryption_key(args): - print("Writing %d random bits to key file %s" % (args.keylen, args.key_file.name)) - args.key_file.write(os.urandom(args.keylen // 8)) - - def _flash_encryption_operation_esp32( - output_file, input_file, flash_address, keyfile, flash_crypt_conf, do_decrypt + output_file: IO, + input_file: IO, + flash_address: int, + keyfile: IO, + flash_crypt_conf: int, + do_decrypt: bool, ): - key = _load_hardware_key(keyfile) + """ + Perform flash encryption or decryption operation for ESP32. + + This function handles the encryption or decryption of flash data for ESP32 chip. + It reads data from the input file, processes it in 16-byte blocks, and writes the + processed data to the output file. The function ensures that the key length is + either 192 or 256 bits, as required by the ESP32 chip. It also checks that the flash + address is a multiple of 16. + + Note: This function is specific to the ESP32 chip. For other chips, use the + --aes_xts flag to call the correct function. + """ + key = _load_hardware_key(keyfile, True, aes_xts=False) if flash_address % 16 != 0: raise esptool.FatalError( - "Starting flash address 0x%x must be a multiple of 16" % flash_address + f"Starting flash address {flash_address:#x} must be a multiple of 16." ) if flash_crypt_conf == 0: - print("WARNING: Setting FLASH_CRYPT_CONF to zero is not recommended") + log.warning("Setting FLASH_CRYPT_CONF to zero is not recommended.") tweak_range = _flash_encryption_tweak_range_bits(flash_crypt_conf) key = int.from_bytes(key, byteorder="big", signed=False) @@ -1256,12 +1360,12 @@ def _flash_encryption_operation_esp32( break elif len(block) < 16: if do_decrypt: - raise esptool.FatalError("Data length is not a multiple of 16 bytes") + raise esptool.FatalError("Data length is not a multiple of 16 bytes.") pad = 16 - len(block) block = block + os.urandom(pad) - print( - "Note: Padding with %d bytes of random data " - "(encrypted data must be multiple of 16 bytes long)" % pad + log.print( + f"Note: Padding with {pad} bytes of random data " + "(encrypted data must be multiple of 16 bytes long)." ) if block_offs % 32 == 0 or cipher is None: @@ -1308,7 +1412,7 @@ def _flash_encryption_operation_esp32( def _flash_encryption_operation_aes_xts( - output_file, input_file, flash_address, keyfile, do_decrypt + output_file: IO, input_file: IO, flash_address: int, keyfile: IO, do_decrypt: bool ): """ Apply the AES-XTS algorithm with the hardware addressing scheme used by Espressif @@ -1322,21 +1426,21 @@ def _flash_encryption_operation_aes_xts( """ backend = default_backend() - key = _load_hardware_key(keyfile) + key = _load_hardware_key(keyfile, True, aes_xts=True) indata = input_file.read() if flash_address % 16 != 0: raise esptool.FatalError( - "Starting flash address 0x%x must be a multiple of 16" % flash_address + f"Starting flash address {flash_address:#x} must be a multiple of 16." ) if len(indata) % 16 != 0: raise esptool.FatalError( - "Input data length (%d) must be a multiple of 16" % len(indata) + f"Input data length ({len(indata)}) must be a multiple of 16." ) if len(indata) == 0: - raise esptool.FatalError("Input data must be longer than 0") + raise esptool.FatalError("Input data must be longer than 0.") # left pad for a 1024-bit aligned address pad_left = flash_address % 0x80 @@ -1350,24 +1454,22 @@ def _flash_encryption_operation_aes_xts( inblocks = _split_blocks(indata, 0x80) # split into 1024 bit blocks - output = [] + output_list = [] for inblock in inblocks: # for each block tweak = struct.pack(" 0: - if not self.file_obj: - self.file_obj = open(self.path, "wb") - self.file_obj.write(payload) +@cli.command("digest-private-key") +@click.option( + "--keyfile", + "-k", + type=click.File("rb"), + required=True, + help="Private key file (PEM format) to generate a digest from.", +) +@click.option( + "--keylen", + "-l", + type=int, + default=256, + help="Length of private key digest file to generate (in bits). 3/4 Coding Scheme " + "requires 192 bit key.", +) +@click.argument("digest-file", type=click.File("wb", lazy=True)) +def digest_private_key_cli(keyfile, keylen, digest_file): + """Generate a SHA-256 digest of the private signing key.""" + digest_private_key(keyfile, keylen, digest_file) + + +@cli.command("generate-flash-encryption-key") +@click.option( + "--keylen", + "-l", + type=int, + default=256, + help="Length of private key digest file to generate (in bits). 3/4 Coding Scheme " + "requires 192 bit key.", +) +@click.argument("key-file", type=click.File("wb", lazy=True)) +def generate_flash_encryption_key(keylen: int, key_file: IO): + """Generate a development-use flash encryption key with random data.""" + log.print(f'Writing {keylen} random bits to key file "{key_file.name}".') + key_file.write(os.urandom(keylen // 8)) + + +@cli.command("decrypt-flash-data") +@click.option( + "--keyfile", + "-k", + type=click.File("rb"), + required=True, + help="File with flash encryption key.", +) +@click.option( + "--output", + "-o", + type=click.File("wb", lazy=True), + required=True, + help="Output file for plaintext data.", +) +@click.option( + "--address", + "-a", + type=esptool.cli_util.AnyIntType(), + required=True, + help="Address offset in flash that file was read from.", +) +@click.option( + "--flash-crypt-conf", + type=esptool.cli_util.AnyIntType(), + default=0xF, + help="Override FLASH_CRYPT_CONF eFuse value (default is 0xF) (applicable only for " + "ESP32).", +) +@click.option( + "--aes-xts", + "-x", + is_flag=True, + help="Decrypt data using AES-XTS (not applicable for ESP32).", +) +@click.argument("encrypted-file", type=click.File("rb")) +def decrypt_flash_data_cli( + keyfile, output, address, flash_crypt_conf, aes_xts, encrypted_file +): + """Decrypt some data read from encrypted flash (using known key).""" + decrypt_flash_data( + keyfile, output, address, flash_crypt_conf, aes_xts, encrypted_file + ) - def close(self): - if self.file_obj: - self.file_obj.close() - self.file_obj = None - @property - def name(self): - return self.path +@cli.command("encrypt-flash-data") +@click.option( + "--keyfile", + "-k", + type=click.File("rb"), + required=True, + help="File with flash encryption key.", +) +@click.option( + "--output", + "-o", + type=click.File("wb", lazy=True), + required=True, + help="Output file for encrypted data.", +) +@click.option( + "--address", + "-a", + type=esptool.cli_util.AnyIntType(), + help="Address offset in flash where file will be flashed.", + required=True, +) +@click.option( + "--flash-crypt-conf", + type=esptool.cli_util.AnyIntType(), + default=0xF, + help="Override FLASH_CRYPT_CONF eFuse value (default is 0xF) (applicable only for " + "ESP32).", +) +@click.option( + "--aes-xts", + "-x", + is_flag=True, + help="Encrypt data using AES-XTS (not applicable for ESP32).", +) +@click.argument("plaintext-file", type=click.File("rb")) +def encrypt_flash_data_cli( + keyfile, output, address, flash_crypt_conf, aes_xts, plaintext_file +): + """Encrypt some data suitable for encrypted flash (using known key).""" + encrypt_flash_data( + keyfile, output, address, flash_crypt_conf, aes_xts, plaintext_file + ) -def main(custom_commandline=None): +def main(argv: list[str] | None = None): """ Main function for espsecure - custom_commandline - Optional override for default arguments parsing + argv - Optional override for default arguments parsing (that uses sys.argv), can be a list of custom arguments as strings. Arguments and their values need to be added as individual items to the list e.g. "--port /dev/ttyUSB1" thus becomes ['--port', '/dev/ttyUSB1']. """ - parser = argparse.ArgumentParser( - description="espsecure.py v%s - ESP32 Secure Boot & Flash Encryption tool" - % esptool.__version__, - prog="espsecure", - ) - - subparsers = parser.add_subparsers( - dest="operation", help="Run espsecure.py {command} -h for additional help" - ) - - p = subparsers.add_parser( - "digest_secure_bootloader", - help="Take a bootloader binary image and a secure boot key, " - "and output a combined digest+binary suitable for flashing along " - "with the precalculated secure boot key.", - ) - p.add_argument( - "--keyfile", - "-k", - help="256 bit key for secure boot digest.", - type=argparse.FileType("rb"), - required=True, - ) - p.add_argument("--output", "-o", help="Output file for signed digest image.") - p.add_argument( - "--iv", - help="128 byte IV file. Supply a file for testing purposes only, " - "if not supplied an IV will be randomly generated.", - type=argparse.FileType("rb"), - ) - p.add_argument( - "image", - help="Bootloader image file to calculate digest from", - type=argparse.FileType("rb"), - ) - - p = subparsers.add_parser( - "generate_signing_key", - help="Generate a private key for signing secure boot images " - "as per the secure boot version. " - "Key file is generated in PEM format, " - "Secure Boot V1 - ECDSA NIST256p private key. " - "Secure Boot V2 - RSA 3072, ECDSA NIST384p, ECDSA NIST256p, ECDSA NIST192p private key.", - ) - p.add_argument( - "--version", - "-v", - help="Version of the secure boot signing scheme to use.", - choices=["1", "2"], - default="1", - ) - p.add_argument( - "--scheme", - "-s", - help="Scheme of secure boot signing.", - choices=["rsa3072", "ecdsa192", "ecdsa256", "ecdsa384"], - required=False, - ) - p.add_argument( - "keyfile", help="Filename for private key file (embedded public key)" - ) - - p = subparsers.add_parser( - "sign_data", - help="Sign a data file for use with secure boot. " - "Signing algorithm is deterministic ECDSA w/ SHA-512 (V1) " - "or either RSA-PSS or ECDSA w/ SHA-256 or ECDSA w/ SHA-384 (V2).", - ) - p.add_argument( - "--version", - "-v", - help="Version of the secure boot signing scheme to use.", - choices=["1", "2"], - required=True, - ) - p.add_argument( - "--keyfile", - "-k", - help="Private key file for signing. Key is in PEM format.", - type=argparse.FileType("rb"), - nargs="+", - ) - p.add_argument( - "--append_signatures", - "-a", - help="Append signature block(s) to already signed image. " - "Not valid for ESP32 and ESP32-C2.", - action="store_true", - ) - p.add_argument( - "--hsm", - help="Use an external Hardware Security Module " - "to generate signature using PKCS#11 interface.", - action="store_true", - ) - p.add_argument( - "--hsm-config", - help="Config file for the external Hardware Security Module " - "to be used to generate signature.", - default=None, - ) - p.add_argument( - "--pub-key", - help="Public key files corresponding to the private key used to generate " - "the pre-calculated signatures. Keys should be in PEM format.", - type=argparse.FileType("rb"), - nargs="+", - ) - p.add_argument( - "--signature", - help="Pre-calculated signatures. " - "Signatures generated using external private keys e.g. keys stored in HSM.", - type=argparse.FileType("rb"), - nargs="+", - default=None, - ) - p.add_argument( - "--output", - "-o", - help="Output file for signed digest image. Default is to sign the input file.", - ) - p.add_argument( - "datafile", - help="File to sign. For version 1, this can be any file. " - "For version 2, this must be a valid app image.", - type=argparse.FileType("rb"), - ) - - p = subparsers.add_parser( - "verify_signature", - help='Verify a data file previously signed by "sign_data", ' - "using the public key.", - ) - p.add_argument( - "--version", - "-v", - help="Version of the secure boot scheme to use.", - choices=["1", "2"], - required=True, - ) - p.add_argument( - "--hsm", - help="Use an external Hardware Security Module " - "to verify signature using PKCS#11 interface.", - action="store_true", - ) - p.add_argument( - "--hsm-config", - help="Config file for the external Hardware Security Module " - "to be used to verify signature.", - default=None, - ) - p.add_argument( - "--keyfile", - "-k", - help="Public key file for verification. " - "Can be private or public key in PEM format.", - type=argparse.FileType("rb"), - ) - p.add_argument( - "datafile", - help="Signed data file to verify signature.", - type=argparse.FileType("rb"), - ) - - p = subparsers.add_parser( - "extract_public_key", - help="Extract the public verification key for signatures, " - "save it as a raw binary file.", - ) - p.add_argument( - "--version", - "-v", - help="Version of the secure boot signing scheme to use.", - choices=["1", "2"], - default="1", - ) - p.add_argument( - "--keyfile", - "-k", - help="Private key file (PEM format) to extract the " - "public verification key from.", - type=argparse.FileType("rb"), - required=True, - ) - p.add_argument( - "public_keyfile", help="File to save new public key into", type=OutFileType() - ) - - # Kept for compatibility purpose. We can deprecate this in a future release - p = subparsers.add_parser( - "digest_rsa_public_key", - help="Generate an SHA-256 digest of the RSA public key. " - "This digest is burned into the eFuse and asserts the legitimacy " - "of the public key for Secure boot v2.", - ) - p.add_argument( - "--keyfile", - "-k", - help="Public key file for verification. " - "Can be private or public key in PEM format.", - type=argparse.FileType("rb"), - required=True, - ) - p.add_argument("--output", "-o", help="Output file for the digest.", required=True) - - p = subparsers.add_parser( - "digest_sbv2_public_key", - help="Generate an SHA-256 digest of the public key. " - "This digest is burned into the eFuse and asserts the legitimacy " - "of the public key for Secure boot v2.", - ) - p.add_argument( - "--keyfile", - "-k", - help="Public key file for verification. " - "Can be private or public key in PEM format.", - type=argparse.FileType("rb"), - required=True, - ) - p.add_argument("--output", "-o", help="Output file for the digest.", required=True) - - p = subparsers.add_parser( - "signature_info_v2", - help="Reads the signature block and provides the signature block information.", - ) - p.add_argument( - "datafile", - help="Secure boot v2 signed data file.", - type=argparse.FileType("rb"), - ) - - p = subparsers.add_parser( - "digest_private_key", - help="Generate an SHA-256 digest of the private signing key. " - "This can be used as a reproducible secure bootloader (only secure boot v1) " - "or flash encryption key.", - ) - p.add_argument( - "--keyfile", - "-k", - help="Private key file (PEM format) to generate a digest from.", - type=argparse.FileType("rb"), - required=True, - ) - p.add_argument( - "--keylen", - "-l", - help="Length of private key digest file to generate (in bits). " - "3/4 Coding Scheme requires 192 bit key.", - choices=[192, 256], - default=256, - type=int, - ) - p.add_argument( - "digest_file", help="File to write 32 byte digest into", type=OutFileType() - ) - - p = subparsers.add_parser( - "generate_flash_encryption_key", - help="Generate a development-use flash encryption key with random data.", - ) - p.add_argument( - "--keylen", - "-l", - help="Length of private key digest file to generate (in bits). " - "3/4 Coding Scheme requires 192 bit key.", - choices=[128, 192, 256, 512], - default=256, - type=int, - ) - p.add_argument( - "key_file", - help="File to write 16, 24, 32 or 64 byte key into", - type=OutFileType(), - ) - - p = subparsers.add_parser( - "decrypt_flash_data", - help="Decrypt some data read from encrypted flash (using known key)", - ) - p.add_argument( - "encrypted_file", - help="File with encrypted flash contents", - type=argparse.FileType("rb"), - ) - p.add_argument( - "--aes_xts", - "-x", - help="Decrypt data using AES-XTS as used on " - "ESP32-S2, ESP32-C2, ESP32-C3, ESP32-C6, ESP32-C5, ESP32-C61 and ESP32-P4", - action="store_true", - ) - p.add_argument( - "--keyfile", - "-k", - help="File with flash encryption key", - type=argparse.FileType("rb"), - required=True, - ) - p.add_argument( - "--output", - "-o", - help="Output file for plaintext data.", - type=OutFileType(), - required=True, - ) - p.add_argument( - "--address", - "-a", - help="Address offset in flash that file was read from.", - required=True, - type=esptool.arg_auto_int, - ) - p.add_argument( - "--flash_crypt_conf", - help="Override FLASH_CRYPT_CONF efuse value (default is 0XF).", - required=False, - default=0xF, - type=esptool.arg_auto_int, - ) - - p = subparsers.add_parser( - "encrypt_flash_data", - help="Encrypt some data suitable for encrypted flash (using known key)", - ) - p.add_argument( - "--aes_xts", - "-x", - help="Encrypt data using AES-XTS as used on " - "ESP32-S2, ESP32-C2, ESP32-C3, ESP32-C6, ESP32-C5, ESP32-C61 and ESP32-P4", - action="store_true", - ) - p.add_argument( - "--keyfile", - "-k", - help="File with flash encryption key", - type=argparse.FileType("rb"), - required=True, - ) - p.add_argument( - "--output", - "-o", - help="Output file for encrypted data.", - type=OutFileType(), - required=True, - ) - p.add_argument( - "--address", - "-a", - help="Address offset in flash where file will be flashed.", - required=True, - type=esptool.arg_auto_int, - ) - p.add_argument( - "--flash_crypt_conf", - help="Override FLASH_CRYPT_CONF efuse value (default is 0XF).", - required=False, - default=0xF, - type=esptool.arg_auto_int, - ) - p.add_argument( - "plaintext_file", - help="File with plaintext content for encrypting", - type=argparse.FileType("rb"), - ) - - # Enable argcomplete only on Unix-like systems - if sys.platform != "win32": - try: - import argcomplete - - argcomplete.autocomplete(parser) - except ImportError: - pass - - args = parser.parse_args(custom_commandline) - print("espsecure.py v%s" % esptool.__version__) - if args.operation is None: - parser.print_help() - parser.exit(1) - - try: - # each 'operation' is a module-level function of the same name - operation_func = globals()[args.operation] - operation_func(args) - finally: - for arg_name in vars(args): - obj = getattr(args, arg_name) - if isinstance(obj, (OutFileType, IOBase)): - obj.close() - elif isinstance(obj, list): - for f in [o for o in obj if isinstance(o, IOBase)]: - f.close() + cli(args=argv) def _main(): + check_deprecated_py_suffix(__name__) try: main() except esptool.FatalError as e: - print("\nA fatal error occurred: %s" % e) + log.error(f"\nA fatal error occurred: {e}") sys.exit(2) except ValueError as e: try: if [arg for arg in e.args if "Could not deserialize key data." in arg]: - print( + log.error( "Note: This error originates from the cryptography module. " "It is likely not a problem with espsecure, " "please make sure you are using a compatible OpenSSL backend." ) finally: raise + except KeyboardInterrupt: + log.error("KeyboardInterrupt: Run cancelled by user.") + sys.exit(2) if __name__ == "__main__": diff --git a/tools/esptool_py/espsecure/esp_hsm_sign/__init__.py b/tools/esptool_py/espsecure/esp_hsm_sign/__init__.py index d255116adf..c2e300d306 100644 --- a/tools/esptool_py/espsecure/esp_hsm_sign/__init__.py +++ b/tools/esptool_py/espsecure/esp_hsm_sign/__init__.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD +# SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD # # SPDX-License-Identifier: GPL-2.0-or-later @@ -6,7 +6,9 @@ import configparser import os import sys +from esptool.logger import log from getpass import getpass +from typing import IO try: import pkcs11 @@ -20,13 +22,12 @@ import cryptography.hazmat.primitives.asymmetric.ec as EC import cryptography.hazmat.primitives.asymmetric.rsa as RSA +import cryptography.hazmat.primitives.asymmetric.utils as utils -import ecdsa - -def read_hsm_config(configfile): +def read_hsm_config(configfile: IO) -> configparser.SectionProxy: config = configparser.ConfigParser() - config.read(configfile) + config.read_file(configfile) section = "hsm_config" if not config.has_section(section): @@ -46,13 +47,13 @@ def read_hsm_config(configfile): return config[section] -def establish_session(config): - print("Trying to establish a session with the HSM.") +def establish_session(config: configparser.SectionProxy) -> pkcs11.Session: + log.print("Trying to establish a session with the HSM...") try: if os.path.exists(config["pkcs11_lib"]): lib = pkcs11.lib(config["pkcs11_lib"]) else: - print(f'LIB file does not exist at {config["pkcs11_lib"]}') + log.error(f'LIB file does not exist at "{config["pkcs11_lib"]}".') sys.exit(1) for slot in lib.get_slots(token_present=True): if slot.slot_id == int(config["slot"]): @@ -60,36 +61,40 @@ def establish_session(config): token = slot.get_token() session = token.open(rw=True, user_pin=config["credentials"]) - print(f'Session creation successful with HSM slot {int(config["slot"])}.') + log.print(f"Session creation successful with HSM slot {int(config['slot'])}.") return session except pkcs11.exceptions.PKCS11Error as e: handle_exceptions(e) - print("Session establishment failed") + log.error("Session establishment failed.") sys.exit(1) -def get_privkey_info(session, config): +def get_privkey_info( + session: pkcs11.Session, config: configparser.SectionProxy +) -> pkcs11.Key: try: private_key = session.get_key( object_class=pkcs11.constants.ObjectClass.PRIVATE_KEY, label=config["label"] ) - print(f'Got private key metadata with label {config["label"]}.') + log.print(f"Got private key metadata with label {config['label']}.") return private_key except pkcs11.exceptions.PKCS11Error as e: handle_exceptions(e) - print("Failed to get the private key") + log.error("Failed to get the private key.") sys.exit(1) -def get_pubkey(session, config): - print("Trying to extract public key from the HSM.") +def get_pubkey( + session: pkcs11.Session, config: configparser.SectionProxy +) -> EC.EllipticCurvePublicKey | RSA.RSAPublicKey: + log.print("Trying to extract public key from the HSM...") try: if "label_pubkey" in config: public_key_label = config["label_pubkey"] else: - print( + log.print( "Config option 'label_pubkey' not found, " "using config option 'label' for public key." ) @@ -107,56 +112,64 @@ def get_pubkey(session, config): public_key = RSA.RSAPublicNumbers(e, n).public_key() elif public_key.key_type == pkcs11.mechanisms.KeyType.EC: - ecpoints, _ = ecdsa.der.remove_octet_string( - public_key[pkcs11.Attribute.EC_POINT] - ) + # EC_POINT is encoded as an octet string + # First byte is "0x04" indicating uncompressed point format + # followed by length bytes + ec_point_der = public_key[pkcs11.Attribute.EC_POINT] + if ec_point_der[0] != 0x04: # octet string tag + raise ValueError( + "Invalid EC_POINT encoding. " + f"Wanted type 'octetstring' (0x04), got {ec_point_der[0]:#02x}." + ) + length = ec_point_der[1] + ecpoints = ec_point_der[2 : 2 + length] public_key = EC.EllipticCurvePublicKey.from_encoded_point( EC.SECP256R1(), ecpoints ) else: - print("Incorrect public key algorithm") + log.error("Incorrect public key algorithm.") sys.exit(1) - print(f"Got public key with label {public_key_label}.") + log.print(f"Got public key with label {public_key_label}.") return public_key except pkcs11.exceptions.PKCS11Error as e: handle_exceptions(e) - print("Failed to extract the public key") + log.error("Failed to extract the public key.") sys.exit(1) -def sign_payload(private_key, payload): +def sign_payload(private_key: pkcs11.Key, payload: bytes) -> bytes: try: - print("Signing payload using the HSM.") + log.print("Signing payload using the HSM...") key_type = private_key.key_type mechanism, mechanism_params = get_mechanism(key_type) - signature = private_key.sign( + signature: bytes = private_key.sign( data=payload, mechanism=mechanism, mechanism_param=mechanism_params ) if len(signature) != 0: - print("Signature generation successful.") + log.print("Signature generation successful.") if key_type == pkcs11.mechanisms.KeyType.EC: r = int(binascii.hexlify(signature[:32]), 16) s = int(binascii.hexlify(signature[32:]), 16) - # der encoding in case of ecdsa signatures - signature = ecdsa.der.encode_sequence( - ecdsa.der.encode_integer(r), ecdsa.der.encode_integer(s) - ) + # ECDSA signature is encoded as a DER sequence + signature = utils.encode_dss_signature(r, s) return signature except pkcs11.exceptions.PKCS11Error as e: handle_exceptions(e, mechanism) - print("Payload Signing Failed") + log.error("Payload signing failed.") sys.exit(1) -def get_mechanism(key_type): +def get_mechanism( + key_type: pkcs11.mechanisms.KeyType, +) -> tuple[pkcs11.mechanisms.Mechanism, tuple | None]: if key_type == pkcs11.mechanisms.KeyType.RSA: return pkcs11.mechanisms.Mechanism.SHA256_RSA_PKCS_PSS, ( pkcs11.mechanisms.Mechanism.SHA256, @@ -166,15 +179,15 @@ def get_mechanism(key_type): elif key_type == pkcs11.mechanisms.KeyType.EC: return pkcs11.mechanisms.Mechanism.ECDSA_SHA256, None else: - print("Invalid signing key mechanism") + log.error("Invalid signing key mechanism.") sys.exit(1) -def close_connection(session): +def close_connection(session: pkcs11.Session): try: session.close() - print("Connection closed successfully") + log.print("Connection closed successfully.") except pkcs11.exceptions.PKCS11Error as e: handle_exceptions(e) - print("Failed to close the HSM session") + log.error("Failed to close the HSM session.") sys.exit(1) diff --git a/tools/esptool_py/espsecure/esp_hsm_sign/exceptions.py b/tools/esptool_py/espsecure/esp_hsm_sign/exceptions.py index 47ba8e3b03..07a6414a57 100644 --- a/tools/esptool_py/espsecure/esp_hsm_sign/exceptions.py +++ b/tools/esptool_py/espsecure/esp_hsm_sign/exceptions.py @@ -1,7 +1,9 @@ -# SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD +# SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD # # SPDX-License-Identifier: GPL-2.0-or-later +from esptool.logger import log + from pkcs11.exceptions import ( AlreadyInitialized, AnotherUserAlreadyLoggedIn, @@ -20,31 +22,33 @@ def handle_exceptions(e, info=""): exception_type = e.__class__ if exception_type == MechanismInvalid: - print("The External HSM does not support the given mechanism", info) + log.error(f"The External HSM does not support the given mechanism: {info}") elif exception_type == FunctionFailed: - print( - "Please ensure proper configuration, privileges and environment variables" + log.error( + "Please ensure proper configuration, privileges and environment variables." ) elif exception_type == AlreadyInitialized: - print("pkcs11 is already initialized with another library") + log.error("pkcs11 is already initialized with another library.") elif exception_type == AnotherUserAlreadyLoggedIn: - print("Another User has been already logged in") + log.error("Another User has been already logged in.") elif exception_type == ArgumentsBad: - print("Please check the arguments supplied to the function") + log.error("Please check the arguments supplied to the function.") elif exception_type == DomainParamsInvalid: - print("Invalid or unsupported domain parameters were supplied to the function") + log.error( + "Invalid or unsupported domain parameters were supplied to the function." + ) elif exception_type == DeviceRemoved: - print( + log.error( "The token has been removed from its slot during " - "the execution of the function" + "the execution of the function." ) elif exception_type == NoSuchToken: - print("No such token found") + log.error("No such token found.") elif exception_type == NoSuchKey: - print("No such key found") + log.error("No such key found.") elif exception_type == OperationNotInitialized: - print("Operation not Initialized") + log.error("Operation not initialized.") elif exception_type == SessionClosed: - print("Session already closed") + log.error("Session already closed.") else: - print(e.__class__, info) + log.error(f"{e.__class__}: {info}") diff --git a/tools/esptool_py/esptool/__init__.py b/tools/esptool_py/esptool/__init__.py index 7f919bc3f4..1060dae31c 100644 --- a/tools/esptool_py/esptool/__init__.py +++ b/tools/esptool_py/esptool/__init__.py @@ -1,8 +1,8 @@ -# 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 -# PYTHON_ARGCOMPLETE_OK + __all__ = [ "chip_id", "detect_chip", @@ -11,16 +11,19 @@ "erase_flash", "erase_region", "flash_id", + "attach_flash", "get_security_info", "image_info", "load_ram", - "make_image", "merge_bin", "read_flash", "read_flash_status", + "read_flash_sfdp", "read_mac", "read_mem", + "reset_chip", "run", + "run_stub", "verify_flash", "version", "write_flash", @@ -28,19 +31,17 @@ "write_mem", ] -__version__ = "4.8.1" +__version__ = "5.0.0" -import argparse -import inspect import os import shlex import sys import time import traceback +import rich_click as click +import typing as t -from esptool.bin_image import intel_hex_to_bin from esptool.cmds import ( - DETECTED_FLASH_SIZES, chip_id, detect_chip, detect_flash_size, @@ -48,18 +49,20 @@ elf2image, erase_flash, erase_region, + attach_flash, flash_id, read_flash_sfdp, get_security_info, image_info, load_ram, - make_image, merge_bin, read_flash, read_flash_status, read_mac, read_mem, + reset_chip, run, + run_stub, verify_flash, version, write_flash, @@ -74,1019 +77,976 @@ ESPLoader, list_ports, ) +from esptool.logger import log from esptool.targets import CHIP_DEFS, CHIP_LIST, ESP32ROM from esptool.util import ( FatalError, NotImplementedInROMError, + check_deprecated_py_suffix, flash_size_bytes, - strip_chip_name, ) from itertools import chain, cycle, repeat import serial +from esptool.cli_util import ( + AutoSizeType, + Group, + AddrFilenameArg, + AutoChunkSizeType, + ChipType, + AnyIntType, + OptionEatAll, + MutuallyExclusiveOption, + ResetModeType, + SpiConnectionType, + AutoHex2BinType, + AddrFilenamePairType, + parse_port_filters, + parse_size_arg, +) -def main(argv=None, esp=None): - """ - Main function for esptool - - argv - Optional override for default arguments parsing (that uses sys.argv), - can be a list of custom arguments as strings. Arguments and their values - need to be added as individual items to the list - e.g. "-b 115200" thus becomes ['-b', '115200']. - - esp - Optional override of the connected device previously - returned by get_default_connected_device() - """ - - external_esp = esp is not None - - parser = argparse.ArgumentParser( - description="esptool.py v%s - Espressif chips ROM Bootloader Utility" - % __version__, - prog="esptool", - ) - - parser.add_argument( - "--chip", - "-c", - help="Target chip type", - type=strip_chip_name, - choices=["auto"] + CHIP_LIST, - default=os.environ.get("ESPTOOL_CHIP", "auto"), - ) - - parser.add_argument( - "--port", - "-p", - help="Serial port device", - default=os.environ.get("ESPTOOL_PORT", None), - ) - - parser.add_argument( - "--baud", - "-b", - help="Serial port baud rate used when flashing/reading", - type=arg_auto_int, - default=os.environ.get("ESPTOOL_BAUD", ESPLoader.ESP_ROM_BAUD), - ) - - parser.add_argument( - "--port-filter", - action="append", - help="Serial port device filter, can be vid=NUMBER, pid=NUMBER, name=SUBSTRING", - type=str, - default=[], - ) - - parser.add_argument( - "--before", - help="What to do before connecting to the chip", - choices=["default_reset", "usb_reset", "no_reset", "no_reset_no_sync"], - default=os.environ.get("ESPTOOL_BEFORE", "default_reset"), - ) - - parser.add_argument( - "--after", - "-a", - help="What to do after esptool.py is finished", - choices=["hard_reset", "soft_reset", "no_reset", "no_reset_stub"], - default=os.environ.get("ESPTOOL_AFTER", "hard_reset"), - ) - - parser.add_argument( - "--no-stub", - help="Disable launching the flasher stub, only talk to ROM bootloader. " - "Some features will not be available.", - action="store_true", - ) - - # --stub-version can be set with --no-stub so the tests wouldn't fail if this option is implied globally - parser.add_argument( - "--stub-version", - default=os.environ.get("ESPTOOL_STUB_VERSION", StubFlasher.STUB_SUBDIRS[0]), - choices=StubFlasher.STUB_SUBDIRS, - # not a public option and is not subject to the semantic versioning policy - help=argparse.SUPPRESS, - ) - - parser.add_argument( - "--trace", - "-t", - help="Enable trace-level output of esptool.py interactions.", - action="store_true", - ) - - parser.add_argument( - "--override-vddsdio", - help="Override ESP32 VDDSDIO internal voltage regulator (use with care)", - choices=ESP32ROM.OVERRIDE_VDDSDIO_CHOICES, - nargs="?", - ) - - parser.add_argument( - "--connect-attempts", - help=( - "Number of attempts to connect, negative or 0 for infinite. " - "Default: %d." % DEFAULT_CONNECT_ATTEMPTS - ), - type=int, - default=os.environ.get("ESPTOOL_CONNECT_ATTEMPTS", DEFAULT_CONNECT_ATTEMPTS), - ) +# Show arguments in the help output, this was default in argparse +click.rich_click.SHOW_ARGUMENTS = True +# Force alignment of commands table with groups +click.rich_click.STYLE_COMMANDS_TABLE_COLUMN_WIDTH_RATIO = (1, 3) +# Option group definitions, used for grouping options in the help output +# Similar to 'add_argument_group' from argparse +click.rich_click.OPTION_GROUPS = { + "* merge-bin": [ + { + "name": "UF2 options", + "options": [ + "--chunk-size", + "--md5-disable", + ], + }, + { + "name": "RAW options", + "options": [ + "--target-offset", + "--pad-to-size", + ], + }, + ], + "*": [ + { + "name": "Flash options", + "options": [ + "--flash-freq", + "--flash-mode", + "--flash-size", + "--spi-connection", + ], + } + ], +} +click.rich_click.COMMAND_GROUPS = { + "*": [ + { + "name": "Basic commands", + "commands": [ + "write-flash", + "read-flash", + "erase-flash", + "erase-region", + "read-mac", + "flash-id", + "elf2image", + "image-info", + "merge-bin", + "version", + ], + }, + { + "name": "Advanced commands", + "commands": [ + "verify-flash", + "load-ram", + "dump-mem", + "read-mem", + "write-mem", + "read-flash-status", + "write-flash-status", + "read-flash-sfdp", + "get-security-info", + "chip-id", + "run", + ], + }, + ], +} - subparsers = parser.add_subparsers( - dest="operation", help="Run esptool.py {command} -h for additional help" - ) +################################### REUSABLE OPTIONS ################################### - def add_spi_connection_arg(parent): - parent.add_argument( - "--spi-connection", - "-sc", - help="Override default SPI Flash connection. " - "Value can be SPI, HSPI or a comma-separated list of 5 I/O numbers " - "to use for SPI flash (CLK,Q,D,HD,CS). Not supported with ESP8266.", - action=SpiConnectionAction, - ) - parser_load_ram = subparsers.add_parser( - "load_ram", help="Download an image to RAM and execute" - ) - parser_load_ram.add_argument( - "filename", help="Firmware image", action=AutoHex2BinAction - ) +def add_spi_connection_arg(function): + function = click.option( + "--spi-connection", + "-sc", + help="Override default SPI flash memory connection. " + "Value can be SPI, HSPI or a comma-separated list of 5 I/O numbers " + "to use for SPI flash (CLK,Q,D,HD,CS). Not supported with ESP8266.", + type=SpiConnectionType(), + )(function) + return function - parser_dump_mem = subparsers.add_parser( - "dump_mem", help="Dump arbitrary memory to disk" - ) - parser_dump_mem.add_argument("address", help="Base address", type=arg_auto_int) - parser_dump_mem.add_argument( - "size", help="Size of region to dump", type=arg_auto_int - ) - parser_dump_mem.add_argument("filename", help="Name of binary dump") - parser_read_mem = subparsers.add_parser( - "read_mem", help="Read arbitrary memory location" - ) - parser_read_mem.add_argument("address", help="Address to read", type=arg_auto_int) +def add_spi_flash_options( + allow_keep: bool = False, auto_detect: bool = False, size_only: bool = False +) -> t.Callable: + """Add common parser arguments for SPI flash properties""" - parser_write_mem = subparsers.add_parser( - "write_mem", help="Read-modify-write to arbitrary memory location" - ) - parser_write_mem.add_argument("address", help="Address to write", type=arg_auto_int) - parser_write_mem.add_argument("value", help="Value", type=arg_auto_int) - parser_write_mem.add_argument( - "mask", - help="Mask of bits to write", - type=arg_auto_int, - nargs="?", - default="0xFFFFFFFF", - ) + extra_keep_args = ["keep"] if allow_keep else [] - def add_spi_flash_subparsers( - parent: argparse.ArgumentParser, - allow_keep: bool, - auto_detect: bool, - size_only: bool = False, - ): - """Add common parser arguments for SPI flash properties""" - extra_keep_args = ["keep"] if allow_keep else [] - - if auto_detect and allow_keep: - extra_fs_message = ", detect, or keep" - flash_sizes = ["detect", "keep"] - elif auto_detect: - extra_fs_message = ", or detect" - flash_sizes = ["detect"] - elif allow_keep: - extra_fs_message = ", or keep" - flash_sizes = ["keep"] - else: - extra_fs_message = "" - flash_sizes = [] + flash_sizes = [] + if auto_detect: + flash_sizes.append("detect") + if allow_keep: + flash_sizes.append("keep") + def wrapper(function): if not size_only: - parent.add_argument( - "--flash_freq", + function = click.option( + "--flash-freq", "-ff", - help="SPI Flash frequency", - choices=extra_keep_args - + [ - "80m", - "60m", - "48m", - "40m", - "30m", - "26m", - "24m", - "20m", - "16m", - "15m", - "12m", - ], + help="SPI flash memory frequency.", + type=click.Choice( + extra_keep_args + + [ + "80m", + "60m", + "48m", + "40m", + "30m", + "26m", + "24m", + "20m", + "16m", + "15m", + "12m", + ] + ), default=os.environ.get("ESPTOOL_FF", "keep" if allow_keep else None), - ) - parent.add_argument( - "--flash_mode", + )(function) + function = click.option( + "--flash-mode", "-fm", - help="SPI Flash mode", - choices=extra_keep_args + ["qio", "qout", "dio", "dout"], + help="SPI flash memory mode.", + type=click.Choice(extra_keep_args + ["qio", "qout", "dio", "dout"]), default=os.environ.get("ESPTOOL_FM", "keep" if allow_keep else "qio"), - ) + )(function) - parent.add_argument( - "--flash_size", + function = click.option( + "--flash-size", "-fs", - help="SPI Flash size in MegaBytes " - "(1MB, 2MB, 4MB, 8MB, 16MB, 32MB, 64MB, 128MB) " - "plus ESP8266-only (256KB, 512KB, 2MB-c1, 4MB-c1)" + extra_fs_message, - choices=flash_sizes - + [ - "256KB", - "512KB", - "1MB", - "2MB", - "2MB-c1", - "4MB", - "4MB-c1", - "8MB", - "16MB", - "32MB", - "64MB", - "128MB", - ], + help="SPI flash memory size. " + "ESP8266-only sizes: 256KB, 512KB, 2MB-c1, 4MB-c1.", + type=click.Choice( + flash_sizes + + [ + "256KB", + "512KB", + "1MB", + "2MB", + "2MB-c1", + "4MB", + "4MB-c1", + "8MB", + "16MB", + "32MB", + "64MB", + "128MB", + ] + ), default=os.environ.get("ESPTOOL_FS", "keep" if allow_keep else "1MB"), - ) - add_spi_connection_arg(parent) + )(function) + return function - parser_write_flash = subparsers.add_parser( - "write_flash", help="Write a binary blob to flash" - ) + return wrapper - parser_write_flash.add_argument( - "addr_filename", - metavar="
", - help="Address followed by binary filename, separated by space", - action=AddrFilenamePairAction, - ) - parser_write_flash.add_argument( - "--erase-all", - "-e", - help="Erase all regions of flash (not just write areas) before programming", - action="store_true", - ) - add_spi_flash_subparsers(parser_write_flash, allow_keep=True, auto_detect=True) - parser_write_flash.add_argument( - "--no-progress", "-p", help="Suppress progress output", action="store_true" - ) - parser_write_flash.add_argument( - "--verify", - help="Verify just-written data on flash " - "(mostly superfluous, data is read back during flashing)", - action="store_true", - ) - parser_write_flash.add_argument( - "--encrypt", - help="Apply flash encryption when writing data " - "(required correct efuse settings)", - action="store_true", - ) - # In order to not break backward compatibility, - # our list of encrypted files to flash is a new parameter - parser_write_flash.add_argument( - "--encrypt-files", - metavar="
", - help="Files to be encrypted on the flash. " - "Address followed by binary filename, separated by space.", - action=AddrFilenamePairAction, - ) - parser_write_flash.add_argument( - "--ignore-flash-encryption-efuse-setting", - help="Ignore flash encryption efuse settings ", - action="store_true", - ) - parser_write_flash.add_argument( - "--force", - help="Force write, skip security and compatibility checks. Use with caution!", - action="store_true", - ) - - compress_args = parser_write_flash.add_mutually_exclusive_group(required=False) - compress_args.add_argument( - "--compress", - "-z", - help="Compress data in transfer (default unless --no-stub is specified)", - action="store_true", - default=None, - ) - compress_args.add_argument( - "--no-compress", - "-u", - help="Disable data compression during transfer " - "(default if --no-stub is specified)", - action="store_true", - ) - - subparsers.add_parser("run", help="Run application code in flash") - - parser_image_info = subparsers.add_parser( - "image_info", help="Dump headers from a binary file (bootloader or application)" - ) - parser_image_info.add_argument( - "filename", help="Image file to parse", action=AutoHex2BinAction - ) - parser_image_info.add_argument( - "--version", - "-v", - help="Output format version (1 - legacy, 2 - extended)", - choices=["1", "2"], - default="1", - ) - - parser_make_image = subparsers.add_parser( - "make_image", help="Create an application image from binary files" - ) - parser_make_image.add_argument("output", help="Output image file") - parser_make_image.add_argument( - "--segfile", "-f", action="append", help="Segment input file" - ) - parser_make_image.add_argument( - "--segaddr", - "-a", - action="append", - help="Segment base address", - type=arg_auto_int, - ) - parser_make_image.add_argument( - "--entrypoint", - "-e", - help="Address of entry point", - type=arg_auto_int, - default=0, - ) - - parser_elf2image = subparsers.add_parser( - "elf2image", help="Create an application image from ELF file" - ) - parser_elf2image.add_argument("input", help="Input ELF file") - parser_elf2image.add_argument( - "--output", - "-o", - help="Output filename prefix (for version 1 image), " - "or filename (for version 2 single image)", - type=str, - ) - parser_elf2image.add_argument( - "--version", - "-e", - help="Output image version", - choices=["1", "2", "3"], - default="1", - ) - parser_elf2image.add_argument( - # it kept for compatibility - # Minimum chip revision (deprecated, consider using --min-rev-full) - "--min-rev", - "-r", - help=argparse.SUPPRESS, - type=int, - choices=range(256), - metavar="{0, ... 255}", - default=0, - ) - parser_elf2image.add_argument( - "--min-rev-full", - help="Minimal chip revision (in format: major * 100 + minor)", - type=int, - choices=range(65536), - metavar="{0, ... 65535}", - default=0, - ) - parser_elf2image.add_argument( - "--max-rev-full", - help="Maximal chip revision (in format: major * 100 + minor)", - type=int, - choices=range(65536), - metavar="{0, ... 65535}", - default=65535, - ) - parser_elf2image.add_argument( - "--secure-pad", - action="store_true", - help="Pad image so once signed it will end on a 64KB boundary. " - "For Secure Boot v1 images only.", - ) - parser_elf2image.add_argument( - "--secure-pad-v2", - action="store_true", - help="Pad image to 64KB, so once signed its signature sector will" - "start at the next 64K block. For Secure Boot v2 images only.", - ) - parser_elf2image.add_argument( - "--elf-sha256-offset", - help="If set, insert SHA256 hash (32 bytes) of the input ELF file " - "at specified offset in the binary.", - type=arg_auto_int, - default=None, - ) - parser_elf2image.add_argument( - "--dont-append-digest", - dest="append_digest", - help="Don't append a SHA256 digest of the entire image after the checksum. " - "This argument is not supported and ignored for ESP8266.", - action="store_false", - default=True, - ) - parser_elf2image.add_argument( - "--use_segments", - help="If set, ELF segments will be used instead of ELF sections " - "to generate the image.", - action="store_true", - ) - parser_elf2image.add_argument( - "--flash-mmu-page-size", - help="Change flash MMU page size.", - choices=["64KB", "32KB", "16KB", "8KB"], - ) - parser_elf2image.add_argument( - "--pad-to-size", - help="The block size with which the final binary image after padding " - "must be aligned to. Value 0xFF is used for padding, similar to erase_flash", - default=None, - ) - parser_elf2image.add_argument( - "--ram-only-header", - help="Order segments of the output so IRAM and DRAM are placed at the " - "beginning and force the main header segment number to RAM segments " - "quantity. This will make the other segments invisible to the ROM " - "loader. Use this argument with care because the ROM loader will load " - "only the RAM segments although the other segments being present in " - "the output. Implies --dont-append-digest", - action="store_true", - default=None, - ) +def check_flash_size(esp: ESPLoader, address: int, size: int) -> None: + # Check if we are writing/erasing/reading past 16MB boundary + if ( + not (esp.IS_STUB and esp.CHIP_NAME in ["ESP32-S3", "ESP32-P4"]) + and address + size > 0x1000000 + ): + raise FatalError( + f"Can't access flash regions larger than 16MB " + f"(set size {size:#x} from address {address:#010x} goes past 16MB " + f"by {address + size - 0x1000000:#x} bytes)." + ) + # Check if we are writing/reading past detected flash size + if not esp.secure_download_mode: + detected_size_str = detect_flash_size(esp) + if not detected_size_str: + return + detected_size = flash_size_bytes(detected_size_str) + if address + size > detected_size: + raise FatalError( + f"Can't access flash regions larger than detected flash size " + f"(set size {size:#x} from address {address:#010x} goes past " + f"{detected_size_str} by {address + size - detected_size:#x} bytes)." + ) - add_spi_flash_subparsers(parser_elf2image, allow_keep=False, auto_detect=False) - subparsers.add_parser("read_mac", help="Read MAC address from OTP ROM") +############################### GLOBAL OPTIONS AND MAIN ############################### - subparsers.add_parser("chip_id", help="Read Chip ID from OTP ROM") - parser_flash_id = subparsers.add_parser( - "flash_id", help="Read SPI flash manufacturer and device ID" - ) - add_spi_connection_arg(parser_flash_id) +@click.group( + cls=Group, + no_args_is_help=True, + context_settings=dict(help_option_names=["-h", "--help"], max_content_width=120), + help=f"esptool v{__version__} - serial utility for flashing, provisioning, " + "and interacting with Espressif SoCs.", +) +@click.option( + "--chip", + "-c", + type=ChipType(["auto"] + CHIP_LIST), + default=os.environ.get("ESPTOOL_CHIP", "auto"), + help="Target chip type.", +) +@click.option( + "--port", + "-p", + type=click.Path(), + default=os.environ.get("ESPTOOL_PORT", None), + help="Serial port device.", +) +@click.option( + "--baud", + "-b", + type=AnyIntType(), + default=os.environ.get("ESPTOOL_BAUD", ESPLoader.ESP_ROM_BAUD), + help="Serial port baud rate used when flashing/reading.", +) +@click.option( + "--port-filter", + multiple=True, + type=str, + cls=OptionEatAll, + help="Serial port device filter, can be vid=NUMBER, pid=NUMBER, name=SUBSTRING, " + "serial=SUBSTRING.", +) +@click.option( + "--before", + type=ResetModeType(["default-reset", "usb-reset", "no-reset", "no-reset-no-sync"]), + default=os.environ.get("ESPTOOL_BEFORE", "default-reset"), + help="Which reset to perform before connecting to the chip.", +) +@click.option( + "--after", + "-a", + type=ResetModeType( + ["hard-reset", "soft-reset", "no-reset", "no-reset-stub", "watchdog-reset"] + ), + default=os.environ.get("ESPTOOL_AFTER", "hard-reset"), + help="Which reset to perform after operation is finished.", +) +@click.option( + "--no-stub", + is_flag=True, + help="Disable launching the flasher stub, only talk to ROM bootloader. " + "Some features will not be available.", +) +# --stub-version can be set with --no-stub so the tests wouldn't fail if this option +# is implied globally +@click.option( + "--stub-version", + default=os.environ.get("ESPTOOL_STUB_VERSION", "1"), + type=click.Choice(["1", "2"]), + # not a public option and is not subject to the semantic versioning policy + hidden=True, +) +@click.option( + "--trace", + "-t", + is_flag=True, + help="Enable trace-level output of esptool interactions.", +) +@click.option( + "--verbose", + "-v", + is_flag=True, + help="Print all output, disable collapsing output stages.", +) +@click.option( + "--silent", + "-s", + is_flag=True, + help="Silence all output except for errors.", +) +@click.option( + "--override-vddsdio", + type=click.Choice(ESP32ROM.OVERRIDE_VDDSDIO_CHOICES), + help="Override ESP32 VDDSDIO internal voltage regulator (use with care).", +) +@click.option( + "--connect-attempts", + type=int, + default=os.environ.get("ESPTOOL_CONNECT_ATTEMPTS", DEFAULT_CONNECT_ATTEMPTS), + help=f"Number of attempts to connect, negative or 0 for infinite. " + f"Default: {DEFAULT_CONNECT_ATTEMPTS}.", +) +@click.pass_context +def cli( + ctx, + **kwargs, +): + ctx.ensure_object(dict) + ctx.obj.update(kwargs) + if ctx.obj["verbose"] and ctx.obj["silent"]: + raise FatalError( + "Cannot use both --verbose and --silent options at the same time." + ) + if ctx.obj["trace"] and ctx.obj["silent"]: + raise FatalError( + "Cannot use both --trace and --silent options at the same time." + ) + if ctx.obj["verbose"]: + log.set_verbosity("verbose") + elif ctx.obj["silent"]: + log.set_verbosity("silent") + ctx.obj["invoked_subcommand"] = ctx.invoked_subcommand + ctx.obj["esp"] = getattr(ctx, "esp", None) + log.print(f"esptool v{__version__}") + load_config_file(verbose=True) - parser_read_status = subparsers.add_parser( - "read_flash_status", help="Read SPI flash status register" - ) - add_spi_connection_arg(parser_read_status) - parser_read_status.add_argument( - "--bytes", - help="Number of bytes to read (1-3)", - type=int, - choices=[1, 2, 3], - default=2, - ) +def prepare_esp_object(ctx): + """Prepare ESP object for operation""" + StubFlasher.set_stub_subdir(ctx.obj["stub_version"]) + # Commands that require an ESP object (flash read/write, etc.) + # 1) Get the ESP object + ####################### - parser_write_status = subparsers.add_parser( - "write_flash_status", help="Write SPI flash status register" - ) + # Disable output stage collapsing, colors, and overwriting in trace mode + if ctx.obj["trace"]: + log._smart_features = False - add_spi_connection_arg(parser_write_status) - parser_write_status.add_argument( - "--non-volatile", - help="Write non-volatile bits (use with caution)", - action="store_true", - ) - parser_write_status.add_argument( - "--bytes", - help="Number of status bytes to write (1-3)", - type=int, - choices=[1, 2, 3], - default=2, - ) - parser_write_status.add_argument("value", help="New value", type=arg_auto_int) + log.stage() - parser_read_flash = subparsers.add_parser( - "read_flash", help="Read SPI flash content" - ) - add_spi_flash_subparsers( - parser_read_flash, allow_keep=True, auto_detect=True, size_only=True - ) - parser_read_flash.add_argument("address", help="Start address", type=arg_auto_int) - parser_read_flash.add_argument( - "size", - help="Size of region to dump. Use `ALL` to read to the end of flash.", - type=arg_auto_size, - ) - parser_read_flash.add_argument("filename", help="Name of binary dump") - parser_read_flash.add_argument( - "--no-progress", "-p", help="Suppress progress output", action="store_true" - ) + if ctx.obj["before"] != "no-reset-no-sync": + initial_baud = min( + ESPLoader.ESP_ROM_BAUD, ctx.obj["baud"] + ) # don't sync faster than the default baud rate + else: + initial_baud = ctx.obj["baud"] - parser_verify_flash = subparsers.add_parser( - "verify_flash", help="Verify a binary blob against flash" - ) - parser_verify_flash.add_argument( - "addr_filename", - help="Address and binary file to verify there, separated by space", - action=AddrFilenamePairAction, + if ctx.obj["port"] is None: + filters = parse_port_filters(ctx.obj["port_filter"]) + ser_list = get_port_list(*filters) + log.print(f"Found {len(ser_list)} serial ports...") + else: + ser_list = [ctx.obj["port"]] + open_port_attempts = os.environ.get( + "ESPTOOL_OPEN_PORT_ATTEMPTS", DEFAULT_OPEN_PORT_ATTEMPTS ) - parser_verify_flash.add_argument( - "--diff", "-d", help="Show differences", choices=["no", "yes"], default="no" + try: + open_port_attempts = int(open_port_attempts) + except ValueError: + raise SystemExit("Invalid value for ESPTOOL_OPEN_PORT_ATTEMPTS.") + + esp = ctx.obj.get("esp", None) + ctx.obj["external_esp"] = esp is not None + if open_port_attempts != 1: + if ctx.obj["port"] is None or ctx.obj["chip"] == "auto": + log.warning( + "The ESPTOOL_OPEN_PORT_ATTEMPTS (open_port_attempts) option " + "can only be used with --port and --chip arguments." + ) + else: + esp = esp or connect_loop( + ctx.obj["port"], + initial_baud, + ctx.obj["chip"], + open_port_attempts, + ctx.obj["trace"], + ctx.obj["before"], + ) + esp = esp or get_default_connected_device( + ser_list, + port=ctx.obj["port"], + connect_attempts=ctx.obj["connect_attempts"], + initial_baud=initial_baud, + chip=ctx.obj["chip"], + trace=ctx.obj["trace"], + before=ctx.obj["before"], ) - add_spi_flash_subparsers(parser_verify_flash, allow_keep=True, auto_detect=True) - parser_erase_flash = subparsers.add_parser( - "erase_flash", help="Perform Chip Erase on SPI flash" - ) - parser_erase_flash.add_argument( - "--force", - help="Erase flash even if security features are enabled. Use with caution!", - action="store_true", - ) - add_spi_connection_arg(parser_erase_flash) + if esp is None: + raise FatalError( + "Could not connect to an Espressif device " + f"on any of the {len(ser_list)} available serial ports." + ) - parser_erase_region = subparsers.add_parser( - "erase_region", help="Erase a region of the flash" - ) - parser_erase_region.add_argument( - "--force", - help="Erase region even if security features are enabled. Use with caution!", - action="store_true", - ) - add_spi_connection_arg(parser_erase_region) - parser_erase_region.add_argument( - "address", help="Start address (must be multiple of 4096)", type=arg_auto_int - ) - parser_erase_region.add_argument( - "size", - help="Size of region to erase (must be multiple of 4096). " - "Use `ALL` to erase to the end of flash.", - type=arg_auto_size, - ) + log.stage(finish=True) + log.print(f"Connected to {esp.CHIP_NAME} on {esp._port.port}:") - parser_read_flash_sfdp = subparsers.add_parser( - "read_flash_sfdp", - help="Read SPI flash SFDP (Serial Flash Discoverable Parameters)", - ) - add_spi_flash_subparsers(parser_read_flash_sfdp, allow_keep=True, auto_detect=True) - parser_read_flash_sfdp.add_argument("addr", type=arg_auto_int) - parser_read_flash_sfdp.add_argument("bytes", type=int) + # 2) Print the chip info + ######################## - parser_merge_bin = subparsers.add_parser( - "merge_bin", - help="Merge multiple raw binary files into a single file for later flashing", - ) + if esp.secure_download_mode: + log.print(f"{'Chip type:':<20}{esp.CHIP_NAME} in Secure Download Mode") + else: + log.print(f"{'Chip type:':<20}{esp.get_chip_description()}") + log.print(f"{'Features:':<20}{', '.join(esp.get_chip_features())}") + log.print(f"{'Crystal frequency:':<20}{esp.get_crystal_freq()}MHz") + usb_mode = esp.get_usb_mode() + if usb_mode is not None: + log.print(f"{'USB mode:':<20}{usb_mode}") + read_mac(esp) + log.print() + + # 3) Perform sanity checks + ########################## + + if esp.secure_download_mode and ctx.obj["invoked_subcommand"] not in ( + "get-security-info", + "write-flash", + "erase-region", + ): + raise FatalError( + f"The '{ctx.obj['invoked_subcommand']}' command is not available " + "in Secure Download Mode." + ) - parser_merge_bin.add_argument( - "--output", "-o", help="Output filename", type=str, required=True - ) - parser_merge_bin.add_argument( - "--format", - "-f", - help="Format of the output file", - choices=["raw", "uf2", "hex"], - default="raw", - ) - uf2_group = parser_merge_bin.add_argument_group("UF2 format") - uf2_group.add_argument( - "--chunk-size", - help="Specify the used data part of the 512 byte UF2 block. " - "A common value is 256. By default the largest possible value will be used.", - default=None, - type=arg_auto_chunk_size, - ) - uf2_group.add_argument( - "--md5-disable", - help="Disable MD5 checksum in UF2 output", - action="store_true", - ) - add_spi_flash_subparsers(parser_merge_bin, allow_keep=True, auto_detect=False) - - raw_group = parser_merge_bin.add_argument_group("RAW format") - raw_group.add_argument( - "--target-offset", - "-t", - help="Target offset where the output file will be flashed", - type=arg_auto_int, - default=0, - ) - raw_group.add_argument( - "--fill-flash-size", - help="If set, the final binary file will be padded with FF " - "bytes up to this flash size.", - choices=[ - "256KB", - "512KB", - "1MB", - "2MB", - "4MB", - "8MB", - "16MB", - "32MB", - "64MB", - "128MB", - ], - ) - parser_merge_bin.add_argument( - "addr_filename", - metavar="
", - help="Address followed by binary filename, separated by space", - action=AddrFilenamePairAction, - ) + # 4) Upload the stub flasher + ############################ - subparsers.add_parser("get_security_info", help="Get some security-related data") + if not ctx.obj["no_stub"]: + esp = run_stub(esp) - subparsers.add_parser("version", help="Print esptool version") + # 5) Configure the baud rate and voltage regulator + ################################################## - # internal sanity check - every operation matches a module function of the same name - for operation in subparsers.choices.keys(): - assert operation in globals(), "%s should be a module function" % operation + if ctx.obj["override_vddsdio"]: + esp.override_vddsdio(ctx.obj["override_vddsdio"]) - # Enable argcomplete only on Unix-like systems - if sys.platform != "win32": + if ctx.obj["baud"] > initial_baud: try: - import argcomplete + esp.change_baud(ctx.obj["baud"]) + except NotImplementedInROMError: + log.warning( + f"ROM doesn't support changing baud rate. " + f"Keeping initial baud rate {initial_baud}." + ) - argcomplete.autocomplete(parser) - except ImportError: - pass + # 6) Prepare to run the operation + ################################# + # Running operation is done inside each command function, as they have different + # arguments and behaviour + # Prepare object for operation (commands) + ctx.obj["esp"] = esp + log.print() + + # 7) Attach the onboard/external flash chip and perform command + ############################################################### + # This will follow in command-specific functions or argument processing decorators + # After the command is done (either successfully or with an error), the following + # teardown function will be called + + @ctx.call_on_close + def teardown(): + """Common teardown for all commands with chip - reset chip and close port""" + # 8) Close all open files + ######################### + for f in getattr(ctx, "_open_files", []): + f.close() + + # 9) Reset the chip + ################### + log.print() + # Handle post-operation behaviour (reset or other) + if ctx.obj["invoked_subcommand"] == "load-ram": + # the ESP is now running the loaded image, so let it run + log.print("Exiting immediately.") + else: + reset_chip(esp, ctx.obj["after"]) - argv = expand_file_arguments(argv or sys.argv[1:]) + # 10) Finish and close the port + ############################## - args = parser.parse_args(argv) - print("esptool.py v%s" % __version__) - load_config_file(verbose=True) + if not ctx.obj["external_esp"]: + esp._port.close() - StubFlasher.set_preferred_stub_subdir(args.stub_version) - - # Parse filter arguments into separate lists - args.filterVids = [] - args.filterPids = [] - args.filterNames = [] - for f in args.port_filter: - kvp = f.split("=") - if len(kvp) != 2: - raise FatalError("Option --port-filter argument must consist of key=value") - if kvp[0] == "vid": - args.filterVids.append(arg_auto_int(kvp[1])) - elif kvp[0] == "pid": - args.filterPids.append(arg_auto_int(kvp[1])) - elif kvp[0] == "name": - args.filterNames.append(kvp[1]) - else: - raise FatalError("Option --port-filter argument key not recognized") - # operation function can take 1 arg (args), 2 args (esp, arg) - # or be a member function of the ESPLoader class. +###################################### COMMANDS ####################################### - if args.operation is None: - parser.print_help() - sys.exit(1) +@cli.command("load-ram") +@click.argument("filename", type=AutoHex2BinType()) +@click.pass_context +def load_ram_cli(ctx, filename: list[tuple[int | None, t.IO[bytes]]]): + """Download an image to RAM and execute.""" + if len(filename) > 1: + raise FatalError( + "Merged binary image detected. " + "Only one file can be specified for the load-ram command." + ) + prepare_esp_object(ctx) + load_ram(ctx.obj["esp"], filename[0][1].name) + + +@cli.command("dump-mem") +@click.argument("address", type=AnyIntType()) +@click.argument("size", type=AutoSizeType(allow_all=False)) +@click.argument("output", type=click.Path()) +@click.pass_context +def dump_mem_cli(ctx, address, size, output): + """Dump arbitrary memory to a file.""" + prepare_esp_object(ctx) + dump_mem(ctx.obj["esp"], address, size, output) + + +@cli.command("read-mem") +@click.argument("address", type=AnyIntType()) +@click.pass_context +def read_mem_cli(ctx, address): + """Read arbitrary memory location.""" + prepare_esp_object(ctx) + read_mem(ctx.obj["esp"], address) + + +@cli.command("write-mem") +@click.argument("address", type=AnyIntType()) +@click.argument("value", type=AnyIntType()) +@click.argument("mask", type=AnyIntType(), default=0xFFFFFFFF) +@click.pass_context +def write_mem_cli(ctx, address, value, mask): + """Modify or write to arbitrary memory location.""" + prepare_esp_object(ctx) + write_mem(ctx.obj["esp"], address, value, mask) + + +@cli.command(name="write-flash") +@click.argument("addr-filename", nargs=-1, required=True, cls=AddrFilenameArg) +@click.option( + "--erase-all", + "-e", + is_flag=True, + help="Erase all regions of flash (not just write areas) before programming.", +) +@click.option("--no-progress", "-p", is_flag=True, help="Suppress progress output.") +@click.option( + "--encrypt", + is_flag=True, + help="Apply flash encryption when writing data (required correct eFuse settings).", +) +@click.option( + "--encrypt-files", + type=AddrFilenamePairType(), + cls=OptionEatAll, + help="Files to be encrypted during flashing. The address is followed by binary " + "filename, separated by space.", +) +@click.option( + "--ignore-flash-enc-efuse", + is_flag=True, + help="Ignore flash encryption eFuse settings.", +) +@click.option( + "--force", + is_flag=True, + help="Force write, skip security and compatibility checks. Use with caution!", +) +@click.option( + "--compress", + "-z", + is_flag=True, + help="Compress data during transfer (default unless --no-stub is specified).", + exclusive_with=["no-compress"], + cls=MutuallyExclusiveOption, +) +@click.option( + "--no-compress", + "-u", + is_flag=True, + default=False, + help="Disable data compression during transfer (default if --no-stub is specified)", + exclusive_with=["compress"], + cls=MutuallyExclusiveOption, +) +@add_spi_flash_options(allow_keep=True, auto_detect=True) +@add_spi_connection_arg +@click.pass_context +def write_flash_cli(ctx, addr_filename, **kwargs): + """Write a binary blob to flash. The address is followed by binary filename, + separated by space.""" # Forbid the usage of both --encrypt, which means encrypt all the given files, # and --encrypt-files, which represents the list of files to encrypt. # The reason is that allowing both at the same time increases the chances of # having contradictory lists (e.g. one file not available in one of list). - if ( - args.operation == "write_flash" - and args.encrypt - and args.encrypt_files is not None - ): + if kwargs["encrypt"] and kwargs["encrypt_files"] is not None: raise FatalError( "Options --encrypt and --encrypt-files " "must not be specified at the same time." ) + prepare_esp_object(ctx) + attach_flash(ctx.obj["esp"], kwargs.pop("spi_connection", None)) + write_flash(ctx.obj["esp"], addr_filename, **kwargs) + + +@cli.command("run") +@click.pass_context +def run_cli(ctx): + """Run application code loaded in flash.""" + prepare_esp_object(ctx) + attach_flash(ctx.obj["esp"]) + run(ctx.obj["esp"]) + + +@cli.command("image-info") +@click.argument("filename", type=AutoHex2BinType()) +@click.pass_context +def image_info_cli(ctx, filename: list[tuple[int | None, t.IO[bytes]]]): + """Print information about a firmware image (bootloader or application).""" + chip = None if ctx.obj["chip"] == "auto" else ctx.obj["chip"] + if len(filename) == 1: + image_info(filename[0][1].name, chip=chip) + else: + image_info(filename, chip=chip) # type: ignore - operation_func = globals()[args.operation] - operation_args = inspect.getfullargspec(operation_func).args - - if ( - operation_args[0] == "esp" - ): # operation function takes an ESPLoader connection object - if args.before != "no_reset_no_sync": - initial_baud = min( - ESPLoader.ESP_ROM_BAUD, args.baud - ) # don't sync faster than the default baud rate - else: - initial_baud = args.baud - if args.port is None: - ser_list = get_port_list(args.filterVids, args.filterPids, args.filterNames) - print("Found %d serial ports" % len(ser_list)) - else: - ser_list = [args.port] - open_port_attempts = os.environ.get( - "ESPTOOL_OPEN_PORT_ATTEMPTS", DEFAULT_OPEN_PORT_ATTEMPTS +@cli.command("elf2image") +@click.argument("filename", type=click.Path(exists=True)) +@click.option( + "--output", + "-o", + type=str, + help="Output filename or filename prefix (for ESP8266 v1 image).", +) +@click.option( + "--version", + "-e", + type=click.Choice(["1", "2", "3"]), + default="1", + help="Output image version.", +) +@click.option( + # Kept for compatibility + # Minimum chip revision (deprecated, consider using --min-rev-full) + "--min-rev", + "-r", + type=click.IntRange(0, 256), + default=0, + hidden=True, +) +@click.option( + "--min-rev-full", + type=click.IntRange(0, 65536), + default=0, + help="Minimal chip revision (in format: major * 100 + minor).", +) +@click.option( + "--max-rev-full", + type=click.IntRange(0, 65536), + default=65535, + help="Maximal chip revision (in format: major * 100 + minor).", +) +@click.option( + "--secure-pad", + is_flag=True, + help="Pad image so once signed it will end on a 64KB boundary. For Secure Boot " + "v1 images only.", +) +@click.option( + "--secure-pad-v2", + is_flag=True, + help="Pad image to 64KB, so once signed its signature sector will start at the " + "next 64K block. For Secure Boot v2 images only.", +) +@click.option( + "--elf-sha256-offset", + type=AnyIntType(), + default=None, + help="If set, insert SHA256 hash (32 bytes) of the input ELF file at specified " + "offset in the binary.", +) +@click.option( + "--dont-append-digest", + is_flag=True, + default=False, + help="Don't append a SHA256 digest of the entire image after the checksum. " + "This argument is not supported and ignored for ESP8266.", +) +@click.option( + "--use-segments", + is_flag=True, + help="If set, ELF segments will be used instead of ELF sections to generate the " + "image.", +) +@click.option( + "--flash-mmu-page-size", + type=click.Choice(["64KB", "32KB", "16KB", "8KB"]), + help="Change flash MMU page size.", +) +@click.option( + "--pad-to-size", + type=int, + default=None, + help="The block size to pad the final binary image to. " + "Value 0xFF is used for padding.", +) +@click.option( + "--ram-only-header", + is_flag=True, + help="Order segments so IRAM and DRAM are placed at the beginning " + "and force the main header segment number to RAM segments quantity. This will " + "make the other segments invisible to the ROM loader. Use with " + "care, the ROM loader will only load the RAM segments although the other " + "segments being present in the output. Implies --dont-append-digest.", +) +@add_spi_flash_options(allow_keep=False, auto_detect=False) +@click.pass_context +def elf2image_cli(ctx, filename, **kwargs): + """Create an application image from ELF file""" + if ctx.obj["chip"] == "auto": + raise FatalError( + f"Specify the --chip argument (choose from {', '.join(CHIP_LIST)})." ) - try: - open_port_attempts = int(open_port_attempts) - except ValueError: - raise SystemExit("Invalid value for ESPTOOL_OPEN_PORT_ATTEMPTS") - if open_port_attempts != 1: - if args.port is None or args.chip == "auto": - print( - "WARNING: The ESPTOOL_OPEN_PORT_ATTEMPTS (open_port_attempts) option can only be used with --port and --chip arguments." - ) - else: - esp = esp or connect_loop( - args.port, - initial_baud, - args.chip, - open_port_attempts, - args.trace, - args.before, - ) - esp = esp or get_default_connected_device( - ser_list, - port=args.port, - connect_attempts=args.connect_attempts, - initial_baud=initial_baud, - chip=args.chip, - trace=args.trace, - before=args.before, + append_digest = not kwargs.pop("dont_append_digest", False) + output = kwargs.pop("output", None) + output = "auto" if output is None else output + elf2image(filename, ctx.obj["chip"], output, append_digest=append_digest, **kwargs) + + +@cli.command("read-mac") +@click.pass_context +def read_mac_cli(ctx): + """Print the device MAC address.""" + prepare_esp_object(ctx) + read_mac(ctx.obj["esp"]) + + +@cli.command("chip-id") +@click.pass_context +def chip_id_cli(ctx): + """Print the device chip ID.""" + prepare_esp_object(ctx) + chip_id(ctx.obj["esp"]) + + +@cli.command("flash-id") +@add_spi_connection_arg +@click.pass_context +def flash_id_cli(ctx, **kwargs): + """Print the SPI flash memory manufacturer and device ID.""" + prepare_esp_object(ctx) + attach_flash(ctx.obj["esp"], kwargs.pop("spi_connection", None)) + flash_id(ctx.obj["esp"]) + + +@cli.command("read-flash-status") +@click.option( + "--bytes", + type=click.Choice(["1", "2", "3"]), + default="2", + help="Number of status bytes to read (1-3).", +) +@add_spi_connection_arg +@click.pass_context +def read_flash_status_cli(ctx, bytes, **kwargs): + """Read SPI flash memory status register.""" + prepare_esp_object(ctx) + attach_flash(ctx.obj["esp"], kwargs.pop("spi_connection", None)) + read_flash_status(ctx.obj["esp"], int(bytes)) + + +@cli.command("write-flash-status") +@click.option( + "--non-volatile", + is_flag=True, + help="Write non-volatile bits (use with caution).", +) +@click.option( + "--bytes", + type=click.Choice(["1", "2", "3"]), + default="2", + help="Number of status bytes to write (1-3).", +) +@click.argument("value", type=AnyIntType()) +@add_spi_connection_arg +@click.pass_context +def write_flash_status_cli(ctx, value, bytes, **kwargs): + """Write SPI flash memory status register.""" + prepare_esp_object(ctx) + attach_flash(ctx.obj["esp"], kwargs.pop("spi_connection", None)) + write_flash_status(ctx.obj["esp"], value, int(bytes), **kwargs) + + +@cli.command("read-flash") +@click.argument("address", type=AnyIntType()) +@click.argument("size", type=AutoSizeType()) +@click.argument("output", type=click.Path()) +@click.option("--no-progress", "-p", is_flag=True, help="Suppress progress output.") +@add_spi_flash_options(allow_keep=True, auto_detect=True, size_only=True) +@add_spi_connection_arg +@click.pass_context +def read_flash_cli(ctx, address, size, output, **kwargs): + """Read SPI flash memory content.""" + prepare_esp_object(ctx) + attach_flash(ctx.obj["esp"], kwargs.pop("spi_connection", None)) + size = parse_size_arg(ctx.obj["esp"], size) + check_flash_size(ctx.obj["esp"], address, size) + read_flash(ctx.obj["esp"], address, size, output, **kwargs) + + +@cli.command("verify-flash") +@click.argument("addr-filename", nargs=-1, required=True, cls=AddrFilenameArg) +@click.option("--diff", "-d", is_flag=True, help="Show differences.") +@add_spi_flash_options(allow_keep=True, auto_detect=True) +@add_spi_connection_arg +@click.pass_context +def verify_flash_cli(ctx, addr_filename, diff, **kwargs): + """Verify a binary blob against the flash memory content.""" + prepare_esp_object(ctx) + attach_flash(ctx.obj["esp"], kwargs.pop("spi_connection", None)) + verify_flash(ctx.obj["esp"], addr_filename, diff=diff, **kwargs) + + +@cli.command("erase-flash") +@click.option( + "--force", + is_flag=True, + help="Erase flash even if security features are enabled. Use with caution!", +) +@add_spi_connection_arg +@click.pass_context +def erase_flash_cli(ctx, force, **kwargs): + """Erase the SPI flash memory.""" + prepare_esp_object(ctx) + attach_flash(ctx.obj["esp"], kwargs.pop("spi_connection", None)) + erase_flash(ctx.obj["esp"], force) + + +@cli.command("erase-region") +@click.option( + "--force", + is_flag=True, + help="Erase region even if security features are enabled. Use with caution!", +) +@click.argument("address", type=AnyIntType()) +@click.argument("size", type=AutoSizeType()) +@click.option( + "--force", + is_flag=True, + help="Erase region even if security features are enabled. Use with caution!", +) +@add_spi_connection_arg +@click.pass_context +def erase_region_cli(ctx, address, size, force, **kwargs): + """Erase a region of the SPI flash memory.""" + prepare_esp_object(ctx) + attach_flash(ctx.obj["esp"], kwargs.pop("spi_connection", None)) + size = parse_size_arg(ctx.obj["esp"], size) + check_flash_size(ctx.obj["esp"], address, size) + erase_region(ctx.obj["esp"], address, size, force) + + +@cli.command("read-flash-sfdp") +@click.argument("address", type=AnyIntType()) +@click.argument("bytes", type=AnyIntType()) +@add_spi_flash_options(allow_keep=True, auto_detect=True) +@add_spi_connection_arg +@click.pass_context +def read_flash_sfdp_cli(ctx, address, bytes, **kwargs): + """Read SPI flash SFDP (Serial Flash Discoverable Parameters).""" + prepare_esp_object(ctx) + attach_flash(ctx.obj["esp"], kwargs.pop("spi_connection", None)) + read_flash_sfdp(ctx.obj["esp"], address, bytes) + + +@cli.command("merge-bin") +@click.argument("addr-filename", nargs=-1, required=True, cls=AddrFilenameArg) +@click.option("--output", "-o", type=str, required=True, help="Output filename.") +@click.option( + "--format", + "-f", + type=click.Choice(["raw", "uf2", "hex"]), + default="raw", + help="Format of the output file.", +) +@click.option( # UF2 only + "--chunk-size", + type=AutoChunkSizeType(), + help="Specify the used data part of the 512 byte UF2 block. A common value is 256. " + "By default the largest possible value will be used.", +) +@click.option( # UF2 only + "--md5-disable", + is_flag=True, + help="Disable MD5 checksum in UF2 output.", +) +@click.option( # RAW only + "--target-offset", + "-t", + type=AnyIntType(), + default=0, + help="Target offset where the output file will be flashed.", +) +@click.option( # RAW only + "--pad-to-size", + type=click.Choice( + ["256KB", "512KB", "1MB", "2MB", "4MB", "8MB", "16MB", "32MB", "64MB", "128MB"] + ), + help="If set, the final binary file will be padded with 0xFF bytes up to this flash" + " size.", +) +@add_spi_flash_options(allow_keep=True, auto_detect=False) +@click.pass_context +def merge_bin_cli(ctx, addr_filename, **kwargs): + """Merge multiple raw binary files into a single flashable file.""" + if ctx.obj["chip"] == "auto": + raise FatalError( + f"Specify the --chip argument (choose from {', '.join(CHIP_LIST)})." ) + merge_bin(addr_filename, chip=ctx.obj["chip"], **kwargs) - if esp is None: - raise FatalError( - "Could not connect to an Espressif device " - "on any of the %d available serial ports." % len(ser_list) - ) - - if esp.secure_download_mode: - print("Chip is %s in Secure Download Mode" % esp.CHIP_NAME) - else: - print("Chip is %s" % (esp.get_chip_description())) - print("Features: %s" % ", ".join(esp.get_chip_features())) - print("Crystal is %dMHz" % esp.get_crystal_freq()) - read_mac(esp, args) - - if not args.no_stub: - if esp.secure_download_mode: - print( - "WARNING: Stub loader is not supported in Secure Download Mode, " - "setting --no-stub" - ) - args.no_stub = True - elif not esp.IS_STUB and esp.stub_is_disabled: - print( - "WARNING: Stub loader has been disabled for compatibility, " - "setting --no-stub" - ) - args.no_stub = True - else: - try: - esp = esp.run_stub() - except Exception: - # The CH9102 bridge (PID: 0x55D4) can have issues on MacOS - if sys.platform == "darwin" and esp._get_pid() == 0x55D4: - print( - "\nNote: If issues persist, " - "try installing the WCH USB-to-Serial MacOS driver." - ) - raise - - if args.override_vddsdio: - esp.override_vddsdio(args.override_vddsdio) - - if args.baud > initial_baud: - try: - esp.change_baud(args.baud) - except NotImplementedInROMError: - print( - "WARNING: ROM doesn't support changing baud rate. " - "Keeping initial baud rate %d" % initial_baud - ) - - def _define_spi_conn(spi_connection): - """Prepare SPI configuration string and value for flash_spi_attach()""" - clk, q, d, hd, cs = spi_connection - spi_config_txt = f"CLK:{clk}, Q:{q}, D:{d}, HD:{hd}, CS:{cs}" - value = (hd << 24) | (cs << 18) | (d << 12) | (q << 6) | clk - return spi_config_txt, value - - # Override the common SPI flash parameter stuff if configured to do so - if hasattr(args, "spi_connection") and args.spi_connection is not None: - spi_config = args.spi_connection - if args.spi_connection == "SPI": - value = 0 - elif args.spi_connection == "HSPI": - value = 1 - else: - esp.check_spi_connection(args.spi_connection) - # Encode the pin numbers as a 32-bit integer with packed 6-bit values, - # the same way the ESP ROM takes them - spi_config, value = _define_spi_conn(args.spi_connection) - print(f"Configuring SPI flash mode ({spi_config})...") - esp.flash_spi_attach(value) - elif args.no_stub: - if esp.CHIP_NAME != "ESP32" or esp.secure_download_mode: - print("Enabling default SPI flash mode...") - # ROM loader doesn't enable flash unless we explicitly do it - esp.flash_spi_attach(0) - else: - # ROM doesn't attach in-package flash chips - spi_chip_pads = esp.get_chip_spi_pads() - spi_config_txt, value = _define_spi_conn(spi_chip_pads) - if spi_chip_pads != (0, 0, 0, 0, 0): - print( - "Attaching flash from eFuses' SPI pads configuration" - f"({spi_config_txt})..." - ) - else: - print("Enabling default SPI flash mode...") - esp.flash_spi_attach(value) - - # XMC chip startup sequence - XMC_VENDOR_ID = 0x20 - - def is_xmc_chip_strict(): - id = esp.flash_id() - rdid = ((id & 0xFF) << 16) | ((id >> 16) & 0xFF) | (id & 0xFF00) - - vendor_id = (rdid >> 16) & 0xFF - mfid = (rdid >> 8) & 0xFF - cpid = rdid & 0xFF - - if vendor_id != XMC_VENDOR_ID: - return False - - matched = False - if mfid == 0x40: - if cpid >= 0x13 and cpid <= 0x20: - matched = True - elif mfid == 0x41: - if cpid >= 0x17 and cpid <= 0x20: - matched = True - elif mfid == 0x50: - if cpid >= 0x15 and cpid <= 0x16: - matched = True - return matched - - def flash_xmc_startup(): - # If the RDID value is a valid XMC one, may skip the flow - fast_check = True - if fast_check and is_xmc_chip_strict(): - return # Successful XMC flash chip boot-up detected by RDID, skipping. - - sfdp_mfid_addr = 0x10 - mf_id = esp.read_spiflash_sfdp(sfdp_mfid_addr, 8) - if mf_id != XMC_VENDOR_ID: # Non-XMC chip detected by SFDP Read, skipping. - return - - print( - "WARNING: XMC flash chip boot-up failure detected! " - "Running XMC25QHxxC startup flow" - ) - esp.run_spiflash_command(0xB9) # Enter DPD - esp.run_spiflash_command(0x79) # Enter UDPD - esp.run_spiflash_command(0xFF) # Exit UDPD - time.sleep(0.002) # Delay tXUDPD - esp.run_spiflash_command(0xAB) # Release Power-Down - time.sleep(0.00002) - # Check for success - if not is_xmc_chip_strict(): - print("WARNING: XMC flash boot-up fix failed.") - print("XMC flash chip boot-up fix successful!") - - # Check flash chip connection - if not esp.secure_download_mode: - try: - flash_id = esp.flash_id() - if flash_id in (0xFFFFFF, 0x000000): - print( - "WARNING: Failed to communicate with the flash chip, " - "read/write operations will fail. " - "Try checking the chip connections or removing " - "any other hardware connected to IOs." - ) - if ( - hasattr(args, "spi_connection") - and args.spi_connection is not None - ): - print( - "Some GPIO pins might be used by other peripherals, " - "try using another --spi-connection combination." - ) - - except FatalError as e: - raise FatalError(f"Unable to verify flash chip connection ({e}).") - - # Check if XMC SPI flash chip booted-up successfully, fix if not - if not esp.secure_download_mode: - try: - flash_xmc_startup() - except FatalError as e: - esp.trace(f"Unable to perform XMC flash chip startup sequence ({e}).") - - if hasattr(args, "flash_size"): - print("Configuring flash size...") - if args.flash_size == "detect": - flash_size = detect_flash_size(esp, args) - elif args.flash_size == "keep": - flash_size = detect_flash_size(esp, args=None) - if not esp.IS_STUB: - print( - "WARNING: In case of failure, please set a specific --flash_size." - ) - else: - flash_size = args.flash_size - - if flash_size is not None: # Secure download mode - esp.flash_set_parameters(flash_size_bytes(flash_size)) - # Check if stub supports chosen flash size - if ( - esp.IS_STUB - and esp.CHIP_NAME != "ESP32-S3" - and flash_size_bytes(flash_size) > 16 * 1024 * 1024 - ): - print( - "WARNING: Flasher stub doesn't fully support flash size larger " - "than 16MB, in case of failure use --no-stub." - ) - - if getattr(args, "size", "") == "all": - if esp.secure_download_mode: - raise FatalError( - "Detecting flash size is not supported in secure download mode. " - "Set an exact size value." - ) - # detect flash size - flash_id = esp.flash_id() - size_id = flash_id >> 16 - size_str = DETECTED_FLASH_SIZES.get(size_id) - if size_str is None: - raise FatalError( - "Detecting flash size failed. Set an exact size value." - ) - print(f"Detected flash size: {size_str}") - args.size = flash_size_bytes(size_str) - - if esp.IS_STUB and hasattr(args, "address") and hasattr(args, "size"): - if esp.CHIP_NAME != "ESP32-S3" and args.address + args.size > 0x1000000: - print( - "WARNING: Flasher stub doesn't fully support flash size larger " - "than 16MB, in case of failure use --no-stub." - ) - try: - operation_func(esp, args) - finally: - try: # Clean up AddrFilenamePairAction files - for address, argfile in args.addr_filename: - argfile.close() - except AttributeError: - pass - - # Handle post-operation behaviour (reset or other) - if operation_func == load_ram: - # the ESP is now running the loaded image, so let it run - print("Exiting immediately.") - elif args.after == "hard_reset": - esp.hard_reset() - elif args.after == "soft_reset": - print("Soft resetting...") - # flash_finish will trigger a soft reset - esp.soft_reset(False) - elif args.after == "no_reset_stub": - print("Staying in flasher stub.") - else: # args.after == 'no_reset' - print("Staying in bootloader.") - if esp.IS_STUB: - esp.soft_reset(True) # exit stub back to ROM loader - - if not external_esp: - esp._port.close() - - else: - operation_func(args) +@cli.command("get-security-info") +@click.pass_context +def get_security_info_cli(ctx): + """Print security information report.""" + prepare_esp_object(ctx) + get_security_info(ctx.obj["esp"]) -def arg_auto_int(x): - return int(x, 0) +@cli.command("version") +def version_cli(): + """Print esptool version.""" + version() -def arg_auto_size(x): - x = x.lower() - return x if x == "all" else arg_auto_int(x) +def main(argv: list[str] | None = None, esp: ESPLoader | None = None): + """ + Main function for esptool + argv - Optional override for default arguments parsing (that uses sys.argv), + can be a list of custom arguments as strings. Arguments and their values + need to be added as individual items to the list + e.g. "-b 115200" thus becomes ['-b', '115200']. -def arg_auto_chunk_size(string: str) -> int: - num = int(string, 0) - if num & 3 != 0: - raise argparse.ArgumentTypeError("Chunk size should be a 4-byte aligned number") - return num + esp - Optional override of the connected device previously + returned by get_default_connected_device() + """ + args = expand_file_arguments(argv or sys.argv[1:]) + cli(args=args, esp=esp) -def get_port_list(vids=[], pids=[], names=[]): +def get_port_list( + vids: list[str] = [], + pids: list[str] = [], + names: list[str] = [], + serials: list[str] = [], +) -> list[str]: if list_ports is None: raise FatalError( "Listing all serial ports is currently not available. " - "Please try to specify the port when running esptool.py or update " - "the pyserial package to the latest version" + "Please try to specify the port when running esptool or update " + "the pyserial package to the latest version." ) ports = [] for port in list_ports.comports(): @@ -1102,11 +1062,16 @@ def get_port_list(vids=[], pids=[], names=[]): port.name is None or all(name not in port.name for name in names) ): continue + if serials and ( + port.serial_number is None + or all(serial not in port.serial_number for serial in serials) + ): + continue ports.append(port.device) return sorted(ports) -def expand_file_arguments(argv): +def expand_file_arguments(argv: list[str]) -> list[str]: """ Any argument starting with "@" gets replaced with all values read from a text file. Text file arguments can be split by newline or by space. @@ -1124,7 +1089,7 @@ def expand_file_arguments(argv): else: new_args.append(arg) if expanded: - print(f"esptool.py {' '.join(new_args)}") + log.print(f"esptool {' '.join(new_args)}") return new_args return argv @@ -1135,11 +1100,11 @@ def connect_loop( chip: str, max_retries: int, trace: bool = False, - before: str = "default_reset", + before: str = "default-reset", ): chip_class = CHIP_DEFS[chip] esp = None - print(f"Serial port {port}") + log.print(f"Serial port {port}:") first = True ten_cycle = cycle(chain(repeat(False, 9), (True,))) @@ -1152,7 +1117,7 @@ def connect_loop( esp = chip_class(port, initial_baud, trace) if not first: # break the retrying line - print("") + log.print("") esp.connect(before) return esp except ( @@ -1165,29 +1130,29 @@ def connect_loop( esp._port.close() esp = None if first: - print(err) - print("Retrying failed connection", end="", flush=True) + log.print(err) + log.print("Retrying failed connection", end="", flush=True) first = False if last: raise err if every_tenth: # print a dot every second - print(".", end="", flush=True) + log.print(".", end="", flush=True) time.sleep(0.1) def get_default_connected_device( - serial_list, - port, - connect_attempts, - initial_baud, - chip="auto", - trace=False, - before="default_reset", + serial_list: list[str], + port: str, + connect_attempts: int, + initial_baud: int, + chip: str = "auto", + trace: bool = False, + before: str = "default-reset", ): _esp = None for each_port in reversed(serial_list): - print("Serial port %s" % each_port) + log.print(f"Serial port {each_port}:") try: if chip == "auto": _esp = detect_chip( @@ -1201,135 +1166,38 @@ def get_default_connected_device( except (FatalError, OSError) as err: if port is not None: raise - print("%s failed to connect: %s" % (each_port, err)) + log.error(f"{each_port} failed to connect: {err}") if _esp and _esp._port: _esp._port.close() _esp = None return _esp -class SpiConnectionAction(argparse.Action): - """ - Custom action to parse 'spi connection' override. - Values are SPI, HSPI, or a sequence of 5 pin numbers separated by commas. - """ - - def __call__(self, parser, namespace, value, option_string=None): - if value.upper() in ["SPI", "HSPI"]: - values = value.upper() - elif "," in value: - values = value.split(",") - if len(values) != 5: - raise argparse.ArgumentError( - self, - f"{value} is not a valid list of comma-separate pin numbers. " - "Must be 5 numbers - CLK,Q,D,HD,CS.", - ) - try: - values = tuple(int(v, 0) for v in values) - except ValueError: - raise argparse.ArgumentError( - self, - f"{values} is not a valid argument. " - "All pins must be numeric values", - ) - else: - raise argparse.ArgumentError( - self, - f"{value} is not a valid spi-connection value. " - "Values are SPI, HSPI, or a sequence of 5 pin numbers - CLK,Q,D,HD,CS.", - ) - setattr(namespace, self.dest, values) - - -class AutoHex2BinAction(argparse.Action): - """Custom parser class for auto conversion of input files from hex to bin""" - - def __call__(self, parser, namespace, value, option_string=None): - try: - with open(value, "rb") as f: - # if hex file was detected replace hex file with converted temp bin - # otherwise keep the original file - value = intel_hex_to_bin(f).name - except IOError as e: - raise argparse.ArgumentError(self, e) - setattr(namespace, self.dest, value) - - -class AddrFilenamePairAction(argparse.Action): - """Custom parser class for the address/filename pairs passed as arguments""" - - def __init__(self, option_strings, dest, nargs="+", **kwargs): - super(AddrFilenamePairAction, self).__init__( - option_strings, dest, nargs, **kwargs - ) - - def __call__(self, parser, namespace, values, option_string=None): - # validate pair arguments - pairs = [] - for i in range(0, len(values), 2): - try: - address = int(values[i], 0) - except ValueError: - raise argparse.ArgumentError( - self, 'Address "%s" must be a number' % values[i] - ) - try: - argfile = open(values[i + 1], "rb") - except IOError as e: - raise argparse.ArgumentError(self, e) - except IndexError: - raise argparse.ArgumentError( - self, - "Must be pairs of an address " - "and the binary filename to write there", - ) - # check for intel hex files and convert them to bin - argfile = intel_hex_to_bin(argfile, address) - pairs.append((address, argfile)) - - # Sort the addresses and check for overlapping - end = 0 - for address, argfile in sorted(pairs, key=lambda x: x[0]): - argfile.seek(0, 2) # seek to end - size = argfile.tell() - argfile.seek(0) - sector_start = address & ~(ESPLoader.FLASH_SECTOR_SIZE - 1) - sector_end = ( - (address + size + ESPLoader.FLASH_SECTOR_SIZE - 1) - & ~(ESPLoader.FLASH_SECTOR_SIZE - 1) - ) - 1 - if sector_start < end: - message = "Detected overlap at address: 0x%x for file: %s" % ( - address, - argfile.name, - ) - raise argparse.ArgumentError(self, message) - end = sector_end - setattr(namespace, self.dest, pairs) - - def _main(): + check_deprecated_py_suffix(__name__) try: main() except FatalError as e: - print(f"\nA fatal error occurred: {e}") + log.error(f"\nA fatal error occurred: {e}") sys.exit(2) except serial.serialutil.SerialException as e: - print(f"\nA serial exception error occurred: {e}") - print( + log.error(f"\nA serial exception error occurred: {e}") + log.error( "Note: This error originates from pySerial. " "It is likely not a problem with esptool, " "but with the hardware connection or drivers." ) - print( + log.error( "For troubleshooting steps visit: " "https://docs.espressif.com/projects/esptool/en/latest/troubleshooting.html" ) sys.exit(1) except StopIteration: - print(traceback.format_exc()) - print("A fatal error occurred: The chip stopped responding.") + log.error(traceback.format_exc()) + log.error("A fatal error occurred: The chip stopped responding.") + sys.exit(2) + except KeyboardInterrupt: + log.error("KeyboardInterrupt: Run cancelled by user.") sys.exit(2) diff --git a/tools/esptool_py/esptool/bin_image.py b/tools/esptool_py/esptool/bin_image.py index 7ac9ed3c9c..8e51d8b9cf 100644 --- a/tools/esptool_py/esptool/bin_image.py +++ b/tools/esptool_py/esptool/bin_image.py @@ -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 @@ -11,30 +11,28 @@ import re import struct import tempfile -from typing import IO, Optional +from typing import IO from intelhex import HexRecordError, IntelHex from .loader import ESPLoader +from .logger import log from .targets import ( ESP32C2ROM, ESP32C3ROM, ESP32C5ROM, - ESP32C5BETA3ROM, - ESP32C6BETAROM, ESP32C6ROM, ESP32C61ROM, - ESP32H2BETA1ROM, - ESP32H2BETA2ROM, ESP32H2ROM, + ESP32H21ROM, + ESP32H4ROM, ESP32P4ROM, ESP32ROM, ESP32S2ROM, - ESP32S3BETA2ROM, ESP32S3ROM, ESP8266ROM, ) -from .util import FatalError, byte, pad_to +from .util import FatalError, byte, ImageSource, get_bytes, pad_to def align_file_position(f, size): @@ -43,9 +41,46 @@ def align_file_position(f, size): f.seek(align, 1) -def intel_hex_to_bin(file: IO[bytes], start_addr: Optional[int] = None) -> IO[bytes]: - """Convert IntelHex file to temp binary file with padding from start_addr - If hex file was detected return temp bin file object; input file otherwise""" +def _find_subsequences(addresses: list[int]) -> list[tuple[int, int]]: + """Find continuous subsequences in a list of addresses""" + if not addresses: + return [] + + sorted_seq = sorted(addresses) + + subsequences = [] + start = sorted_seq[0] + + for prev, num in zip(sorted_seq, sorted_seq[1:]): + if num != prev + 1: + # Found a gap, save the current subsequence + subsequences.append((start, prev)) + start = num + + # Add the last subsequence + subsequences.append((start, sorted_seq[-1])) + + return subsequences + + +def _split_intel_hex_file(ih: IntelHex) -> list[tuple[int, IO[bytes]]]: + """Split an IntelHex file into multiple temporary binary files based on the gaps + in the addresses""" + subsequences = _find_subsequences(ih.addresses()) + bins: list[tuple[int, IO[bytes]]] = [] + for start, end in subsequences: + bin = tempfile.NamedTemporaryFile(suffix=".bin", delete=False) + ih.tobinfile(bin, start=start, end=end) + bin.seek(0) # make sure the file is at the beginning + bins.append((start, bin)) + return bins + + +def intel_hex_to_bin( + file: IO[bytes], start_addr: int | None = None +) -> list[tuple[int | None, IO[bytes]]]: + """Convert IntelHex file to list of temp binary files + If not hex file return input file otherwise""" INTEL_HEX_MAGIC = b":" magic = file.read(1) file.seek(0) @@ -54,61 +89,51 @@ def intel_hex_to_bin(file: IO[bytes], start_addr: Optional[int] = None) -> IO[by ih = IntelHex() ih.loadhex(file.name) file.close() - bin = tempfile.NamedTemporaryFile(suffix=".bin", delete=False) - ih.tobinfile(bin, start=start_addr) - return bin + return _split_intel_hex_file(ih) # type: ignore else: - return file + return [(start_addr, file)] except (HexRecordError, UnicodeDecodeError): # file started with HEX magic but the rest was not according to the standard - return file + return [(start_addr, file)] -def LoadFirmwareImage(chip, image_file): +def LoadFirmwareImage(chip: str, image_data: ImageSource): """ Load a firmware image. Can be for any supported SoC. ESP8266 images will be examined to determine if they are original ROM firmware images (ESP8266ROMFirmwareImage) or "v2" OTA bootloader images. - Returns a BaseFirmwareImage subclass, either ESP8266ROMFirmwareImage (v1) - or ESP8266V2FirmwareImage (v2). + Returns a BaseFirmwareImage subclass. """ - - def select_image_class(f, chip): - chip = re.sub(r"[-()]", "", chip.lower()) - if chip != "esp8266": - return { - "esp32": ESP32FirmwareImage, - "esp32s2": ESP32S2FirmwareImage, - "esp32s3beta2": ESP32S3BETA2FirmwareImage, - "esp32s3": ESP32S3FirmwareImage, - "esp32c3": ESP32C3FirmwareImage, - "esp32c6beta": ESP32C6BETAFirmwareImage, - "esp32h2beta1": ESP32H2BETA1FirmwareImage, - "esp32h2beta2": ESP32H2BETA2FirmwareImage, - "esp32c2": ESP32C2FirmwareImage, - "esp32c6": ESP32C6FirmwareImage, - "esp32c61": ESP32C61FirmwareImage, - "esp32c5": ESP32C5FirmwareImage, - "esp32c5beta3": ESP32C5BETA3FirmwareImage, - "esp32h2": ESP32H2FirmwareImage, - "esp32p4": ESP32P4FirmwareImage, - }[chip](f) - else: # Otherwise, ESP8266 so look at magic to determine the image type - magic = ord(f.read(1)) - f.seek(0) - if magic == ESPLoader.ESP_IMAGE_MAGIC: - return ESP8266ROMFirmwareImage(f) - elif magic == ESP8266V2FirmwareImage.IMAGE_V2_MAGIC: - return ESP8266V2FirmwareImage(f) - else: - raise FatalError("Invalid image magic number: %d" % magic) - - if isinstance(image_file, str): - with open(image_file, "rb") as f: - return select_image_class(f, chip) - return select_image_class(image_file, chip) + data, _ = get_bytes(image_data) + f = io.BytesIO(data) + chip = re.sub(r"[-()]", "", chip.lower()) + if chip == "esp8266": + # Look at the magic number to determine the ESP8266 image type + magic = ord(f.read(1)) + f.seek(0) + if magic == ESPLoader.ESP_IMAGE_MAGIC: + return ESP8266ROMFirmwareImage(f) + elif magic == ESP8266V2FirmwareImage.IMAGE_V2_MAGIC: + return ESP8266V2FirmwareImage(f) + else: + raise FatalError(f"Invalid image magic number: {magic}") + else: + return { + "esp32": ESP32FirmwareImage, + "esp32s2": ESP32S2FirmwareImage, + "esp32s3": ESP32S3FirmwareImage, + "esp32c3": ESP32C3FirmwareImage, + "esp32c2": ESP32C2FirmwareImage, + "esp32c6": ESP32C6FirmwareImage, + "esp32c61": ESP32C61FirmwareImage, + "esp32c5": ESP32C5FirmwareImage, + "esp32h2": ESP32H2FirmwareImage, + "esp32h21": ESP32H21FirmwareImage, + "esp32p4": ESP32P4FirmwareImage, + "esp32h4": ESP32H4FirmwareImage, + }[chip](f) class ImageSegment(object): @@ -164,14 +189,22 @@ def get_memory_type(self, image): def pad_to_alignment(self, alignment): self.data = pad_to(self.data, alignment, b"\x00") - def addr_align(self, alignment): + def end_addr_if_aligned(self, alignment): + """ + Return the segment end address as it would be if + aligned as requested by the argument. + """ end_addr = self.addr + len(self.data) addr_mod = end_addr % alignment if addr_mod != 0: end_addr += alignment - addr_mod return end_addr - def pad_to_addr(self, addr): + def pad_until_addr(self, addr): + """ + Pad the segment with `0x00` starting with segment address + until the address given by the argument. + """ pad = addr - (self.addr + len(self.data)) if pad > 0: self.data += b"\x00" * pad @@ -192,9 +225,8 @@ def __repr__(self): class BaseFirmwareImage(object): SEG_HEADER_LEN = 8 SHA256_DIGEST_LEN = 32 - ELF_FLAG_WRITE = 0x1 - ELF_FLAG_READ = 0x2 - ELF_FLAG_EXEC = 0x4 + IROM_ALIGN = 0 + MMU_PAGE_SIZE_CONF: tuple[int, ...] = () """ Base class with common firmware image functions """ @@ -243,7 +275,7 @@ def load_segment(self, f, is_irom_segment=False): def warn_if_unusual_segment(self, offset, size, is_irom_segment): if not is_irom_segment: if offset > 0x40200000 or offset < 0x3FFE0000 or size > 65536: - print("WARNING: Suspicious segment 0x%x, length %d" % (offset, size)) + log.warning(f"Suspicious segment {offset:#x}, length {size}") def maybe_patch_segment_data(self, f, segment_data): """ @@ -287,13 +319,26 @@ def maybe_patch_segment_data(self, f, segment_data): ) return segment_data - def save_segment(self, f, segment, checksum=None): + def save_segment(self, f, segment, checksum=None, segment_name=None): """ Save the next segment to the image file, return next checksum value if provided """ segment_data = self.maybe_patch_segment_data(f, segment.data) - f.write(struct.pack("= SIXTEEN_MB: + raise FatalError( + f"Invalid {segment_name} segment length {segment_len:#x}. " + "The 16 MB limit has been exceeded." + ) + f.write(struct.pack(" tuple[bytes | None, bytes] | None: + irom_data: bytes | None = None + other_data: bytes | None = None + + # Handle IROM data irom_segment = self.get_irom_segment() if irom_segment is not None: - with open( - "%s0x%05x.bin" - % (basename, irom_segment.addr - ESP8266ROM.IROM_MAP_START), - "wb", - ) as f: - f.write(irom_segment.data) - - # everything but IROM goes at 0x00000 in an image file - normal_segments = self.get_non_irom_segments() - with open("%s0x00000.bin" % basename, "wb") as f: + irom_data = irom_segment.data + + # Handle other segments (everything but IROM) + with io.BytesIO() as f: # Use BytesIO to write to memory + normal_segments = self.get_non_irom_segments() self.write_common_header(f, normal_segments) checksum = ESPLoader.ESP_CHECKSUM_MAGIC for segment in normal_segments: checksum = self.save_segment(f, segment, checksum) self.append_checksum(f, checksum) + other_data = f.getvalue() # Get the bytes from BytesIO + + if filename is not None: + # Write IROM data to a file + if irom_data is not None: + offset = irom_segment.addr - ESP8266ROM.IROM_MAP_START + with open(f"{filename}{offset:#07x}.bin", "wb") as f: + f.write(irom_data) + # Write other data to a file + if other_data is not None: + with open(f"{filename}{0:#07x}.bin", "wb") as f: + f.write(other_data) + return None + else: + return (irom_data, other_data) + ESP8266ROM.BOOTLOADER_IMAGE = ESP8266ROMFirmwareImage @@ -518,9 +588,8 @@ def __init__(self, load_file=None): if segments != self.IMAGE_V2_SEGMENT: # segment count is not really segment count here, # but we expect to see '4' - print( - 'Warning: V2 header has unexpected "segment" count %d (usually 4)' - % segments + log.warning( + f'V2 header has unexpected "segment" count {segments} (usually 4)' ) # irom segment comes before the second header @@ -540,22 +609,22 @@ def __init__(self, load_file=None): segments = self.load_common_header(load_file, ESPLoader.ESP_IMAGE_MAGIC) if first_flash_mode != self.flash_mode: - print( - "WARNING: Flash mode value in first header (0x%02x) disagrees " - "with second (0x%02x). Using second value." - % (first_flash_mode, self.flash_mode) + log.warning( + f"Flash mode value in first header ({first_flash_mode:#04x}) " + f"disagrees with second ({self.flash_mode:#04x}). " + "Using second value." ) if first_flash_size_freq != self.flash_size_freq: - print( - "WARNING: Flash size/freq value in first header (0x%02x) disagrees " - "with second (0x%02x). Using second value." - % (first_flash_size_freq, self.flash_size_freq) + log.warning( + "Flash size/freq value in first header " + f"({first_flash_size_freq:#04x}) disagrees with second " + f"({self.flash_size_freq:#04x}). Using second value." ) if first_entrypoint != self.entrypoint: - print( - "WARNING: Entrypoint address in first header (0x%08x) disagrees " - "with second header (0x%08x). Using second value." - % (first_entrypoint, self.entrypoint) + log.warning( + f"Entrypoint address in first header ({first_entrypoint:#010x}) " + f"disagrees with second header ({self.entrypoint:#010x}). " + "Using second value." ) # load all the usual segments @@ -577,8 +646,8 @@ def default_output_name(self, input_file): irom_offs & ~(ESPLoader.FLASH_SECTOR_SIZE - 1), ) - def save(self, filename): - with open(filename, "wb") as f: + def save(self, filename: str | None) -> bytes | None: + with io.BytesIO() as f: # Write to memory first # Save first header for irom0 segment f.write( struct.pack( @@ -608,13 +677,20 @@ def save(self, filename): checksum = self.save_segment(f, segment, checksum) self.append_checksum(f, checksum) - # calculate a crc32 of entire file and append - # (algorithm used by recent 8266 SDK bootloaders) - with open(filename, "rb") as f: + # Calculate CRC32 of the entire file and append + f.seek(0) # Move to the start of the BytesIO buffer crc = esp8266_crc32(f.read()) - with open(filename, "ab") as f: f.write(struct.pack(b" bytes | None: total_segments = 0 with io.BytesIO() as f: # write file to memory first self.write_common_header(f, self.segments) @@ -755,10 +831,10 @@ def save(self, filename): for segment in flash_segments[1:]: if segment.addr // self.IROM_ALIGN == last_addr // self.IROM_ALIGN: raise FatalError( - "Segment loaded at 0x%08x lands in same 64KB flash mapping " - "as segment loaded at 0x%08x. Can't generate binary. " + f"Segment loaded at {segment.addr:#010x} lands in same " + f"{self.IROM_ALIGN // 1024} KB flash mapping as segment " + f"loaded at {last_addr:#010x}. Can't generate binary. " "Suggest changing linker script or ELF to merge sections." - % (segment.addr, last_addr) ) last_addr = segment.addr @@ -787,7 +863,7 @@ def get_alignment_data_needed(segment): # and checksum (ROM bootloader will only care for RAM segments and its # correct checksums) for segment in ram_segments: - checksum = self.save_segment(f, segment, checksum) + checksum = self.save_segment(f, segment, checksum, segment.name) total_segments += 1 self.append_checksum(f, checksum) @@ -805,7 +881,7 @@ def get_alignment_data_needed(segment): pad_len -= self.ROM_LOADER.BOOTLOADER_FLASH_OFFSET pad_segment = ImageSegment(0, b"\x00" * pad_len, f.tell()) - self.save_segment(f, pad_segment) + self.save_segment(f, pad_segment, None, segment.name) total_segments += 1 # check the alignment assert (f.tell() + 8 + self.ROM_LOADER.BOOTLOADER_FLASH_OFFSET) % ( @@ -817,7 +893,7 @@ def get_alignment_data_needed(segment): self.save_flash_segment(f, segment) total_segments += 1 else: # not self.ram_only_header - # try to fit each flash segment on a 64kB aligned boundary + # try to fit each flash segment on a MMU page size aligned boundary # by padding with parts of the non-flash segments... while len(flash_segments) > 0: segment = flash_segments[0] @@ -829,7 +905,9 @@ def get_alignment_data_needed(segment): ram_segments.pop(0) else: pad_segment = ImageSegment(0, b"\x00" * pad_len, f.tell()) - checksum = self.save_segment(f, pad_segment, checksum) + checksum = self.save_segment( + f, pad_segment, checksum, segment.name + ) total_segments += 1 else: # write the flash segment @@ -842,12 +920,12 @@ def get_alignment_data_needed(segment): # flash segments all written, so write any remaining RAM segments for segment in ram_segments: - checksum = self.save_segment(f, segment, checksum) + checksum = self.save_segment(f, segment, checksum, segment.name) total_segments += 1 if self.secure_pad: - # pad the image so that after signing it will end on a a 64KB boundary. - # This ensures all mapped flash content will be verified. + # pad the image so that after signing it will end on a a MMU page size + # boundary. This ensures all mapped flash content will be verified. if not self.append_digest: raise FatalError( "secure_pad only applies if a SHA-256 digest " @@ -865,7 +943,7 @@ def get_alignment_data_needed(segment): elif self.secure_pad == "2": # Secure Boot V2 # after checksum: SHA-256 digest + # signature sector, - # but we place signature sector after the 64KB boundary + # but we place signature sector after the MMU page size boundary space_after_checksum = 32 pad_len = ( self.IROM_ALIGN - align_past - checksum_space - space_after_checksum @@ -906,8 +984,14 @@ def get_alignment_data_needed(segment): pad_by = self.pad_to_size - (image_length % self.pad_to_size) f.write(b"\xff" * pad_by) - with open(filename, "wb") as real_file: - real_file.write(f.getvalue()) + if filename is not None: + # Write the content to a real file + with open(filename, "wb") as real_file: + real_file.write(f.getvalue()) + return None + else: + # Return the bytes if no filename is provided + return f.getvalue() def load_extended_header(self, load_file): def split_byte(n): @@ -926,12 +1010,10 @@ def split_byte(n): self.chip_id = fields[4] if self.chip_id != self.ROM_LOADER.IMAGE_CHIP_ID: - print( - ( - "Unexpected chip id in image. Expected %d but value was %d. " - "Is this image for a different chip model?" - ) - % (self.ROM_LOADER.IMAGE_CHIP_ID, self.chip_id) + log.warning( + f"Unexpected chip ID in image. Expected {self.ROM_LOADER.IMAGE_CHIP_ID}" + f" but value was {self.chip_id}. Is this image for a different " + "chip model?" ) self.min_rev = fields[5] @@ -978,7 +1060,7 @@ class ESP8266V3FirmwareImage(ESP32FirmwareImage): def is_flash_addr(self, addr): return addr > ESP8266ROM.IROM_MAP_START - def save(self, filename): + def save(self, filename: str | None) -> bytes | None: total_segments = 0 with io.BytesIO() as f: # write file to memory first self.write_common_header(f, self.segments) @@ -1006,14 +1088,14 @@ def save(self, filename): for segment in flash_segments[1:]: if segment.addr // self.IROM_ALIGN == last_addr // self.IROM_ALIGN: raise FatalError( - "Segment loaded at 0x%08x lands in same 64KB flash mapping " - "as segment loaded at 0x%08x. Can't generate binary. " + f"Segment loaded at {segment.addr:#010x} lands in same " + f"{self.IROM_ALIGN // 1024} KB flash mapping as segment " + f"loaded at {last_addr:#010x}. Can't generate binary. " "Suggest changing linker script or ELF to merge sections." - % (segment.addr, last_addr) ) last_addr = segment.addr - # try to fit each flash segment on a 64kB aligned boundary + # try to fit each flash segment on a MMU page size aligned boundary # by padding with parts of the non-flash segments... while len(flash_segments) > 0: segment = flash_segments[0] @@ -1046,8 +1128,14 @@ def save(self, filename): digest.update(f.read(image_length)) f.write(digest.digest()) - with open(filename, "wb") as real_file: - real_file.write(f.getvalue()) + if filename is not None: + # Write the content to a real file + with open(filename, "wb") as real_file: + real_file.write(f.getvalue()) + return None + else: + # Return the bytes if no filename is provided + return f.getvalue() def load_extended_header(self, load_file): def split_byte(n): @@ -1074,9 +1162,9 @@ def split_byte(n): # remaining fields in the middle should all be zero if any(f for f in fields[4:15] if f != 0): - print( - "Warning: some reserved header fields have non-zero values. " - "This image may be from a newer esptool.py?" + log.warning( + "Some reserved header fields have non-zero values. " + "This image may be from a newer esptool?" ) @@ -1092,15 +1180,6 @@ class ESP32S2FirmwareImage(ESP32FirmwareImage): ESP32S2ROM.BOOTLOADER_IMAGE = ESP32S2FirmwareImage -class ESP32S3BETA2FirmwareImage(ESP32FirmwareImage): - """ESP32S3 Firmware Image almost exactly the same as ESP32FirmwareImage""" - - ROM_LOADER = ESP32S3BETA2ROM - - -ESP32S3BETA2ROM.BOOTLOADER_IMAGE = ESP32S3BETA2FirmwareImage - - class ESP32S3FirmwareImage(ESP32FirmwareImage): """ESP32S3 Firmware Image almost exactly the same as ESP32FirmwareImage""" @@ -1119,45 +1198,11 @@ class ESP32C3FirmwareImage(ESP32FirmwareImage): ESP32C3ROM.BOOTLOADER_IMAGE = ESP32C3FirmwareImage -class ESP32C6BETAFirmwareImage(ESP32FirmwareImage): - """ESP32C6 Firmware Image almost exactly the same as ESP32FirmwareImage""" - - ROM_LOADER = ESP32C6BETAROM - - -ESP32C6BETAROM.BOOTLOADER_IMAGE = ESP32C6BETAFirmwareImage - - -class ESP32H2BETA1FirmwareImage(ESP32FirmwareImage): - """ESP32H2 Firmware Image almost exactly the same as ESP32FirmwareImage""" - - ROM_LOADER = ESP32H2BETA1ROM - - -ESP32H2BETA1ROM.BOOTLOADER_IMAGE = ESP32H2BETA1FirmwareImage - - -class ESP32H2BETA2FirmwareImage(ESP32FirmwareImage): - """ESP32H2 Firmware Image almost exactly the same as ESP32FirmwareImage""" - - ROM_LOADER = ESP32H2BETA2ROM - - -ESP32H2BETA2ROM.BOOTLOADER_IMAGE = ESP32H2BETA2FirmwareImage - - class ESP32C2FirmwareImage(ESP32FirmwareImage): """ESP32C2 Firmware Image almost exactly the same as ESP32FirmwareImage""" ROM_LOADER = ESP32C2ROM - - def set_mmu_page_size(self, size): - if size not in [16384, 32768, 65536]: - raise FatalError( - "{} bytes is not a valid ESP32-C2 page size, " - "select from 64KB, 32KB, 16KB.".format(size) - ) - self.IROM_ALIGN = size + MMU_PAGE_SIZE_CONF = (16384, 32768, 65536) ESP32C2ROM.BOOTLOADER_IMAGE = ESP32C2FirmwareImage @@ -1167,14 +1212,7 @@ class ESP32C6FirmwareImage(ESP32FirmwareImage): """ESP32C6 Firmware Image almost exactly the same as ESP32FirmwareImage""" ROM_LOADER = ESP32C6ROM - - def set_mmu_page_size(self, size): - if size not in [8192, 16384, 32768, 65536]: - raise FatalError( - "{} bytes is not a valid ESP32-C6 page size, " - "select from 64KB, 32KB, 16KB, 8KB.".format(size) - ) - self.IROM_ALIGN = size + MMU_PAGE_SIZE_CONF = (8192, 16384, 32768, 65536) ESP32C6ROM.BOOTLOADER_IMAGE = ESP32C6FirmwareImage @@ -1189,8 +1227,8 @@ class ESP32C61FirmwareImage(ESP32C6FirmwareImage): ESP32C61ROM.BOOTLOADER_IMAGE = ESP32C61FirmwareImage -class ESP32C5FirmwareImage(ESP32C6FirmwareImage): - """ESP32C5 Firmware Image almost exactly the same as ESP32C6FirmwareImage""" +class ESP32C5FirmwareImage(ESP32FirmwareImage): + """ESP32C5 Firmware Image almost exactly the same as ESP32FirmwareImage""" ROM_LOADER = ESP32C5ROM @@ -1198,13 +1236,21 @@ class ESP32C5FirmwareImage(ESP32C6FirmwareImage): ESP32C5ROM.BOOTLOADER_IMAGE = ESP32C5FirmwareImage -class ESP32C5BETA3FirmwareImage(ESP32C6FirmwareImage): - """ESP32C5BETA3 Firmware Image almost exactly the same as ESP32C6FirmwareImage""" +class ESP32H4FirmwareImage(ESP32FirmwareImage): + """ESP32H4 Firmware Image almost exactly the same as ESP32FirmwareImage""" + + ROM_LOADER = ESP32H4ROM - ROM_LOADER = ESP32C5BETA3ROM + def set_mmu_page_size(self, size): + if size not in [8192, 16384, 32768, 65536]: + raise FatalError( + "{} bytes is not a valid ESP32-H4 page size, " + "select from 64KB, 32KB, 16KB, 8KB.".format(size) + ) + self.IROM_ALIGN = size -ESP32C5BETA3ROM.BOOTLOADER_IMAGE = ESP32C5BETA3FirmwareImage +ESP32H4ROM.BOOTLOADER_IMAGE = ESP32H4FirmwareImage class ESP32P4FirmwareImage(ESP32FirmwareImage): @@ -1225,6 +1271,15 @@ class ESP32H2FirmwareImage(ESP32C6FirmwareImage): ESP32H2ROM.BOOTLOADER_IMAGE = ESP32H2FirmwareImage +class ESP32H21FirmwareImage(ESP32C6FirmwareImage): + """ESP32H21 Firmware Image almost exactly the same as ESP32FirmwareImage""" + + ROM_LOADER = ESP32H21ROM + + +ESP32H21ROM.BOOTLOADER_IMAGE = ESP32H21FirmwareImage + + class ELFFile(object): SEC_TYPE_PROGBITS = 0x01 SEC_TYPE_STRTAB = 0x03 @@ -1239,11 +1294,10 @@ class ELFFile(object): SEG_TYPE_LOAD = 0x01 LEN_SEG_HEADER = 0x20 - def __init__(self, name): - # Load sections from the ELF file - self.name = name - with open(self.name, "rb") as f: - self._read_elf_file(f) + def __init__(self, data): + self.data, self.name = get_bytes(data) + f = io.BytesIO(self.data) + self._read_elf_file(f) def get_section(self, section_name): for s in self.sections: @@ -1254,6 +1308,7 @@ def get_section(self, section_name): def _read_elf_file(self, f): # read the ELF file header LEN_FILE_HEADER = 0x34 + source = "Image" if self.name is None else f"'{self.name}'" try: ( ident, @@ -1271,25 +1326,23 @@ def _read_elf_file(self, f): shnum, shstrndx, ) = struct.unpack("<16sHHLLLLLHHHHHH", f.read(LEN_FILE_HEADER)) - except struct.error as e: - raise FatalError( - "Failed to read a valid ELF header from %s: %s" % (self.name, e) - ) + except struct.error as e: + raise FatalError(f"{source} does not have a valid ELF header: {e}") if byte(ident, 0) != 0x7F or ident[1:4] != b"ELF": - raise FatalError("%s has invalid ELF magic header" % self.name) + raise FatalError(f"{source} has invalid ELF magic header") if machine not in [0x5E, 0xF3]: raise FatalError( - "%s does not appear to be an Xtensa or an RISCV ELF file. " - "e_machine=%04x" % (self.name, machine) + f"{source} does not appear to be an Xtensa or an RISCV ELF image. " + f"(e_machine = {machine:#06x})" ) if shentsize != self.LEN_SEC_HEADER: raise FatalError( - "%s has unexpected section header entry size 0x%x (not 0x%x)" - % (self.name, shentsize, self.LEN_SEC_HEADER) + f"{source} has unexpected section header entry size {shentsize:#x} " + f"(not {self.LEN_SEC_HEADER:#x})" ) if shnum == 0: - raise FatalError("%s has 0 section headers" % (self.name)) + raise FatalError(f"{source} has 0 section headers") self._read_sections(f, shoff, shnum, shstrndx) self._read_segments(f, _phoff, _phnum, shstrndx) @@ -1299,13 +1352,13 @@ def _read_sections(self, f, section_header_offs, section_header_count, shstrndx) section_header = f.read(len_bytes) if len(section_header) == 0: raise FatalError( - "No section header found at offset %04x in ELF file." - % section_header_offs + f"No section header found at offset {section_header_offs:#06x} " + "in ELF image." ) if len(section_header) != (len_bytes): raise FatalError( - "Only read 0x%x bytes from section header (expected 0x%x.) " - "Truncated ELF file?" % (len(section_header), len_bytes) + f"Only read {len(section_header):#x} bytes from section header " + f"(expected {len_bytes:#x}). Truncated ELF image?" ) # walk through the section header and extract all sections @@ -1319,8 +1372,8 @@ def read_section_header(offs): lma, sec_offs, size, - link, - info, + _, + _, align, ) = struct.unpack_from(" 0 ] self.segments = prog_segments @@ -1419,6 +1470,6 @@ def read_data(offs, size): def sha256(self): # return SHA256 hash of the input ELF file sha256 = hashlib.sha256() - with open(self.name, "rb") as f: - sha256.update(f.read()) + f = io.BytesIO(self.data) + sha256.update(f.read()) return sha256.digest() diff --git a/tools/esptool_py/esptool/cli_util.py b/tools/esptool_py/esptool/cli_util.py new file mode 100644 index 0000000000..647c011d18 --- /dev/null +++ b/tools/esptool_py/esptool/cli_util.py @@ -0,0 +1,428 @@ +# SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD +# +# SPDX-License-Identifier: GPL-2.0-or-later + + +import rich_click as click + +from esptool.bin_image import ESPLoader, intel_hex_to_bin +from esptool.cmds import detect_flash_size +from esptool.util import FatalError, flash_size_bytes, strip_chip_name +from esptool.logger import log +from typing import IO, Any + +################################ Custom types ################################# + + +class ChipType(click.Choice): + """Custom type to accept chip names in any case and with or without hyphen""" + + def convert( + self, value: str, param: click.Parameter | None, ctx: click.Context + ) -> Any: + value = strip_chip_name(value) + return super().convert(value, param, ctx) + + +class ResetModeType(click.Choice): + """Custom type to accept reset mode names with underscores as separators + for compatibility with v4""" + + def convert(self, value: str, param: click.Parameter, ctx: click.Context) -> Any: + if "_" in value: + new_value = value.replace("_", "-") + if new_value not in self.choices: + raise click.BadParameter(f"{value} is not a valid reset mode.") + log.warning( + f"Deprecated: Choice '{value}' for option '--{param.name}' is " + f"deprecated. Use '{new_value}' instead." + ) + return new_value + return super().convert(value, param, ctx) + + +class AnyIntType(click.ParamType): + """Custom type to parse any integer value - decimal, hex, octal, or binary""" + + name = "integer" + + def convert( + self, value: str, param: click.Parameter | None, ctx: click.Context + ) -> int: + if isinstance(value, int): # default value is already an int + return value + try: + return arg_auto_int(value) + except ValueError: + raise click.BadParameter(f"{value!r} is not a valid integer.") + + +class AutoSizeType(AnyIntType): + """Similar to AnyIntType but allows 'k', 'M' suffixes for kilo(1024), Mega(1024^2) + and 'all' as a value to e.g. read whole flash""" + + def __init__(self, allow_all: bool = True): + self.allow_all = allow_all + super().__init__() + + def convert( + self, value: str, param: click.Parameter | None, ctx: click.Context + ) -> Any: + if self.allow_all and value.lower() == "all": + return value + # Handle suffixes like 'k', 'M' for kilo, mega + if value[-1] in ("k", "M"): + try: + num = arg_auto_int(value[:-1]) + except ValueError: + raise click.BadParameter(f"{value!r} is not a valid integer") + if value[-1] == "k": + num *= 1024 + elif value[-1] == "M": + num *= 1024 * 1024 + return num + return super().convert(value, param, ctx) + + +class AutoChunkSizeType(AnyIntType): + """Custom type for chunk size that must be 4-byte aligned""" + + name = "integer" + + def convert( + self, value: str, param: click.Parameter | None, ctx: click.Context + ) -> int: + num = super().convert(value, param, ctx) + if num & 3 != 0: + raise click.BadParameter("Chunk size should be a 4-byte aligned number.") + return num + + +class SpiConnectionType(click.ParamType): + """ + Custom type to parse 'spi connection' override. + Values are SPI, HSPI, or a sequence of 5 pin numbers separated by commas. + """ + + name = "spi-connection" + + def convert( + self, value: str, param: click.Parameter | None, ctx: click.Context + ) -> str | tuple[int, int, int, int, int]: + if value.upper() in ["SPI", "HSPI"]: + return value.upper() + elif "," in value: + values = value.split(",") + if len(values) != 5: + raise click.BadParameter( + f"{value} is not a valid list of comma-separated pin numbers. " + "Must be 5 numbers - CLK,Q,D,HD,CS.", + ) + try: + return tuple(arg_auto_int(v) for v in values) # type: ignore + except ValueError: + raise click.BadParameter( + f"{values} is not a valid argument. " + "All pins must be numeric values.", + ) + else: + raise click.BadParameter( + f"{value} is not a valid spi-connection value. " + "Values are SPI, HSPI, or a sequence of 5 pin numbers - CLK,Q,D,HD,CS.", + ) + + +class AutoHex2BinType(click.Path): + """Custom type for auto conversion of input files from hex to bin""" + + def __init__(self, exists=True): + super().__init__(exists=exists) + + def convert( + self, value: str, param: click.Parameter | None, ctx: click.Context + ) -> list[tuple[int | None, IO[bytes]]]: + try: + with open(value, "rb") as f: + # if hex file was detected replace hex file with converted temp bin + # otherwise keep the original file + return intel_hex_to_bin(f) + except IOError as e: + raise click.BadParameter(str(e)) + + +class AddrFilenamePairType(click.Path): + """Custom type for the address/filename pairs passed as arguments""" + + name = "addr-filename-pair" + + def get_metavar(self, param): + return "
" + + def convert( + self, + value: list[str], + param: click.Parameter | None, + ctx: click.Context, + ): + if len(value) % 2 != 0: + raise click.BadParameter( + "Must be pairs of an address and the binary filename to write there.", + ) + if len(value) == 0: + return value + + pairs: list[tuple[int, IO[bytes]]] = [] + for i in range(0, len(value), 2): + try: + address = arg_auto_int(value[i]) + except ValueError: + raise click.BadParameter(f'Address "{value[i]}" must be a number.') + try: + # Store file handle in context for later cleanup + if not hasattr(ctx, "_open_files"): + ctx._open_files = [] + argfile_f = open(value[i + 1], "rb") + ctx._open_files.append(argfile_f) + except IOError as e: + raise click.BadParameter(str(e)) + # check for intel hex files and convert them to bin + argfile_list = intel_hex_to_bin(argfile_f, address) + pairs.extend(argfile_list) # type: ignore + + # Sort the addresses and check for overlapping + end = 0 + for address, argfile in sorted(pairs, key=lambda x: x[0]): + argfile.seek(0, 2) # seek to end + size = argfile.tell() + argfile.seek(0) + sector_start = address & ~(ESPLoader.FLASH_SECTOR_SIZE - 1) + sector_end = ( + (address + size + ESPLoader.FLASH_SECTOR_SIZE - 1) + & ~(ESPLoader.FLASH_SECTOR_SIZE - 1) + ) - 1 + if sector_start < end: + raise click.BadParameter( + f"Detected overlap at address: " + f"{address:#x} for file: {argfile.name}.", + ) + end = sector_end + return pairs + + +########################### Custom option/argument ############################ + + +class Group(click.RichGroup): + DEPRECATED_OPTIONS = { + "--flash_size": "--flash-size", + "--flash_freq": "--flash-freq", + "--flash_mode": "--flash-mode", + "--use_segments": "--use-segments", + "--ignore_flash_encryption_efuse_setting": "--ignore-flash-enc-efuse", + "--fill-flash-size": "--pad-to-size", + } + + def __call__(self, esp: ESPLoader | None = None, *args, **kwargs): + self._esp = esp # store the external esp object in the group + return super().__call__(*args, **kwargs) + + def _replace_deprecated_args(self, args: list[str]) -> list[str]: + new_args = [] + for arg in args: + if arg in self.DEPRECATED_OPTIONS.keys(): + # Replace underscores with hyphens in option names + new_name = self.DEPRECATED_OPTIONS[arg] + if new_name != arg: + log.warning( + f"Deprecated: Option '{arg}' is deprecated. " + f"Use '{new_name}' instead." + ) + arg = new_name + new_args.append(arg) + return new_args + + def parse_args(self, ctx: click.Context, args: list[str]): + """Set a flag if --help is used to skip the main""" + ctx.esp = self._esp + ctx._commands_list = self.list_commands(ctx) # used for EatAllOptions + args = self._replace_deprecated_args(args) + return super().parse_args(ctx, args) + + def get_command(self, ctx: click.Context, cmd_name: str) -> click.Command | None: + """Allow dash and underscore for commands for compatibility with v4""" + rv = click.Group.get_command(self, ctx, cmd_name) + if rv is not None: + return rv + for cmd in self.list_commands(ctx): + cmd_alias = cmd.replace("-", "_") + if cmd_alias == cmd_name: + log.warning( + f"Deprecated: Command '{cmd_name}' is deprecated. " + f"Use '{cmd}' instead." + ) + return click.Group.get_command(self, ctx, cmd) + return None + + def resolve_command( + self, ctx: click.Context, args: list[str] + ) -> tuple[str | None, click.Command | None, list[str]]: + # always return the full command name + _, cmd, args = super().resolve_command(ctx, args) + if cmd is None: + return None, None, args + return cmd.name, cmd, args + + +class AddrFilenameArg(click.Argument): + """Parse arguments as list instead of each value individually""" + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.type = AddrFilenamePairType() + + def type_cast_value(self, ctx: click.Context, value: list[str]): + return self.type.convert(value, None, ctx) + + +class OptionEatAll(click.Option): + """Grab all arguments up to the next option/command. + Imitates argparse nargs='*' for options.""" + + def __init__(self, *args, **kwargs): + super(OptionEatAll, self).__init__(*args, **kwargs) + self._previous_parser_process = None + self._eat_all_parser = None + # Set the metavar dynamically based on the type's metavar + if self.type and hasattr(self.type, "name"): + self.metavar = f"[{self.type.get_metavar(None) or self.type.name.upper()}]" + + def add_to_parser(self, parser, ctx): + def parser_process(value, state): + # Method to hook into the parser.process + done = False + value = [value] + # Grab everything up to the next option/command + while state.rargs and not done: + for prefix in self._eat_all_parser.prefixes: + if state.rargs[0].startswith(prefix): + done = True + break + if state.rargs[0] in self._commands_list: + done = True + if not done: + value.append(state.rargs.pop(0)) + + # Call the original parser process method on the rest of the arguments + self._previous_parser_process(value, state) + + retval = super(OptionEatAll, self).add_to_parser(parser, ctx) + for name in self.opts: + # Get the parser for the current option + current_parser = parser._long_opt.get(name) or parser._short_opt.get(name) + if current_parser: + # Replace the parser.process with our hook + self._eat_all_parser = current_parser + self._previous_parser_process = current_parser.process + current_parser.process = parser_process + # Avoid reading commands as arguments if this class was used before cmd + self._commands_list = getattr(ctx, "_commands_list", []) + break + return retval + + +class MutuallyExclusiveOption(click.Option): + """Custom option class to enforce mutually exclusive options in click. + Similar to argparse function `add_mutually_exclusive_group`. + + This class ensures that certain options cannot be used together by raising + a UsageError if mutually exclusive options are provided. + + For example, `--compress` and `--no-compress` are mutually exclusive options. + """ + + def __init__(self, *args, **kwargs): + self.mutually_exclusive = set(kwargs.pop("exclusive_with", [])) + if self.mutually_exclusive: + ex_str = ", ".join( + [self._to_option_name(opt) for opt in self.mutually_exclusive] + ) + kwargs["help"] = ( + f"{kwargs.get('help', '')} NOTE: This argument is mutually exclusive " + f"with arguments: {ex_str}." + ) + super(MutuallyExclusiveOption, self).__init__(*args, **kwargs) + + def _to_option_name(self, name: str) -> str: + """Convert dictionary entry for option ('my_name') to click option name + ('--my-name'). Add '--' prefix and replace '_' with '-'. This is assuming + options don't use '_'.""" + return f"--{name.replace('_', '-')}" + + def handle_parse_result(self, ctx, opts, args): + if self.mutually_exclusive.intersection(opts) and self.name in opts: + options = ", ".join( + [self._to_option_name(opt) for opt in self.mutually_exclusive] + ) + raise click.UsageError( + f"Illegal usage: {self._to_option_name(self.name)} is mutually " + f"exclusive with arguments: {options}." + ) + return super(MutuallyExclusiveOption, self).handle_parse_result(ctx, opts, args) + + +############################## Helper functions ############################### + + +def arg_auto_int(x: str) -> int: + """Parse an integer value in any base""" + return int(x, 0) + + +def parse_port_filters( + value: list[str], +) -> tuple[list[int], list[int], list[str], list[str]]: + """Parse port filter arguments into separate lists for each filter type""" + filterVids = [] + filterPids = [] + filterNames = [] + filterSerials = [] + for f in value: + kvp = f.split("=") + if len(kvp) != 2: + FatalError("Option --port-filter argument must consist of key=value.") + if kvp[0] == "vid": + filterVids.append(arg_auto_int(kvp[1])) + elif kvp[0] == "pid": + filterPids.append(arg_auto_int(kvp[1])) + elif kvp[0] == "name": + filterNames.append(kvp[1]) + elif kvp[0] == "serial": + filterSerials.append(kvp[1]) + else: + raise FatalError("Option --port-filter argument key not recognized.") + return filterVids, filterPids, filterNames, filterSerials + + +def parse_size_arg(esp: ESPLoader, size: int | str) -> int: + """Parse the flash size argument and return the size in bytes""" + if isinstance(size, int): + if not esp.secure_download_mode: + detected_size = flash_size_bytes(detect_flash_size(esp)) + if detected_size and size > detected_size: + raise FatalError( + f"Specified size {size:#x} is greater than detected flash size " + f"{detected_size:#x}.", + ) + return size + if size.lower() != "all": + raise FatalError(f"Invalid size value: {size}. Use an integer or 'all'.") + if esp.secure_download_mode: + raise FatalError( + "Detecting flash size is not supported in secure download mode. " + "Set an exact size value.", + ) + size_str = detect_flash_size(esp) + if size_str is None: + raise FatalError("Detecting flash size failed. Set an exact size value.") + log.print(f"Detected flash size: {size_str}") + return flash_size_bytes(size_str) # type: ignore # size_str is not None diff --git a/tools/esptool_py/esptool/cmds.py b/tools/esptool_py/esptool/cmds.py index 4948c9890f..213dee84a3 100644 --- a/tools/esptool_py/esptool/cmds.py +++ b/tools/esptool_py/esptool/cmds.py @@ -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 @@ -14,8 +14,9 @@ from intelhex import IntelHex from serial import SerialException +from typing import cast -from .bin_image import ELFFile, ImageSegment, LoadFirmwareImage +from .bin_image import ELFFile, LoadFirmwareImage from .bin_image import ( ESP8266ROMFirmwareImage, ESP8266V2FirmwareImage, @@ -28,6 +29,8 @@ ESPLoader, timeout_per_mb, ) +from .logger import log + from .targets import CHIP_DEFS, CHIP_LIST, ROM_LIST from .uf2_writer import UF2Writer from .util import ( @@ -39,12 +42,28 @@ from .util import ( div_roundup, flash_size_bytes, - get_file_size, hexify, + ImageSource, + get_bytes, + get_key_from_value, pad_to, - print_overwrite, + sanitize_string, ) + +# Vendors with different detection logic +ADESTO_VENDOR_ID = 0x1F +XMC_VENDOR_ID = 0x20 + +DETECTED_FLASH_SIZES_ADESTO = { + 0x04: "512KB", + 0x05: "1MB", + 0x06: "2MB", + 0x07: "4MB", + 0x08: "8MB", + 0x09: "16MB", +} + DETECTED_FLASH_SIZES = { 0x12: "256KB", 0x13: "512KB", @@ -71,195 +90,328 @@ 0x3A: "64MB", } -FLASH_MODES = {"qio": 0, "qout": 1, "dio": 2, "dout": 3} +FLASH_MODES = { + "qio": 0, + "qout": 1, + "dio": 2, + "dout": 3, +} def detect_chip( - port=ESPLoader.DEFAULT_PORT, - baud=ESPLoader.ESP_ROM_BAUD, - connect_mode="default_reset", - trace_enabled=False, - connect_attempts=DEFAULT_CONNECT_ATTEMPTS, -): - """Use serial access to detect the chip type. - - First, get_security_info command is sent to detect the ID of the chip - (supported only by ESP32-C3 and later, works even in the Secure Download Mode). - If this fails, we reconnect and fall-back to reading the magic number. - It's mapped at a specific ROM address and has a different value on each chip model. - This way we use one memory read and compare it to the magic number for each chip. - - This routine automatically performs ESPLoader.connect() (passing - connect_mode parameter) as part of querying the chip. + port: str = ESPLoader.DEFAULT_PORT, + baud: int = ESPLoader.ESP_ROM_BAUD, + connect_mode: str = "default-reset", + trace_enabled: bool = False, + connect_attempts: int = DEFAULT_CONNECT_ATTEMPTS, +) -> ESPLoader: + """ + Detect the type of ESP device connected via serial, + connect to it, and return an active ESPLoader object. + + Args: + port: The serial port to use for communication. + baud: The baud rate for serial communication. + connect_mode: The chip reset method to perform when connecting to the ESP device + (``"default-reset"``, ``"usb-reset"``, + ``"no-reset"``, ``"no-reset-no-sync"``) + trace_enabled: Enables or disables tracing for debugging purposes. + connect_attempts: Number of connection attempts before failing. + + Returns: + An initialized instance of the detected chip class ready for use. """ inst = None detect_port = ESPLoader(port, baud, trace_enabled=trace_enabled) if detect_port.serial_port.startswith("rfc2217:"): detect_port.USES_RFC2217 = True detect_port.connect(connect_mode, connect_attempts, detecting=True) + + def check_if_stub(instance: ESPLoader) -> ESPLoader: + log.print(f" {instance.CHIP_NAME}") + if detect_port.sync_stub_detected and instance.STUB_CLASS is not None: + instance = instance.STUB_CLASS(instance) + instance.sync_stub_detected = True + return instance + + """ + First, get-security-info command is sent to detect the ID of the chip + (supported only by ESP32-C3 and later, works even in the Secure Download Mode). + If this fails, we reconnect and fall-back to reading the magic number. + It's mapped at a specific ROM address and has a different value on each chip model. + This way we use one memory read and compare it to the magic number for each chip. + """ try: - print("Detecting chip type...", end="") + log.print("Detecting chip type...", end="", flush=True) chip_id = detect_port.get_chip_id() - for cls in [ - n for n in ROM_LIST if n.CHIP_NAME not in ("ESP8266", "ESP32", "ESP32-S2") - ]: - # cmd not supported on ESP8266 and ESP32 + ESP32-S2 doesn't return chip_id + for cls in ROM_LIST: + # cmd not supported on ESP8266 and ESP32 + ESP32-S2 doesn't return chip-id + if cls.USES_MAGIC_VALUE: + continue if chip_id == cls.IMAGE_CHIP_ID: inst = cls(detect_port._port, baud, trace_enabled=trace_enabled) - try: - inst.read_reg( - ESPLoader.CHIP_DETECT_MAGIC_REG_ADDR - ) # Dummy read to check Secure Download mode - except UnsupportedCommandError: - inst.secure_download_mode = True + si = inst.get_security_info() + inst.secure_download_mode = si["parsed_flags"]["SECURE_DOWNLOAD_ENABLE"] + inst = check_if_stub(inst) inst._post_connect() break else: err_msg = f"Unexpected chip ID value {chip_id}." - except (UnsupportedCommandError, struct.error, FatalError) as e: + except (UnsupportedCommandError, FatalError): # UnsupportedCommandError: ESP8266/ESP32 ROM - # struct.error: ESP32-S2 - # FatalError: ESP8266/ESP32 STUB - print(" Unsupported detection protocol, switching and trying again...") + # FatalError: ESP8266/ESP32 STUB or ESP32-S2 try: - # ESP32/ESP8266 are reset after an unsupported command, need to reconnect - # (not needed on ESP32-S2) - if not isinstance(e, struct.error): - detect_port.connect( - connect_mode, connect_attempts, detecting=True, warnings=False - ) - print("Detecting chip type...", end="") - sys.stdout.flush() chip_magic_value = detect_port.read_reg( ESPLoader.CHIP_DETECT_MAGIC_REG_ADDR ) - - for cls in ROM_LIST: - if chip_magic_value in cls.CHIP_DETECT_MAGIC_VALUE: - inst = cls(detect_port._port, baud, trace_enabled=trace_enabled) - inst._post_connect() - inst.check_chip_id() - break - else: - err_msg = f"Unexpected chip magic value {chip_magic_value:#010x}." except UnsupportedCommandError: - raise FatalError( - "Unsupported Command Error received. " - "Probably this means Secure Download Mode is enabled, " - "autodetection will not work. Need to manually specify the chip." + # Only ESP32-S2 does not support chip id detection + # and supports secure download mode + inst = CHIP_DEFS["esp32s2"]( + detect_port._port, baud, trace_enabled=trace_enabled ) - finally: - if inst is not None: - print(" %s" % inst.CHIP_NAME, end="") - if detect_port.sync_stub_detected: - inst = inst.STUB_CLASS(inst) - inst.sync_stub_detected = True - print("") # end line + si = inst.get_security_info() + inst.secure_download_mode = si["parsed_flags"]["SECURE_DOWNLOAD_ENABLE"] + inst = check_if_stub(inst) + inst._post_connect() return inst + except FatalError: + log.print(" Autodetection failed, trying again...") + detect_port.connect( + connect_mode, connect_attempts, detecting=True, warnings=False + ) + log.print("Detecting chip type...", end="", flush=True) + chip_magic_value = detect_port.read_reg( + ESPLoader.CHIP_DETECT_MAGIC_REG_ADDR + ) + + for cls in ROM_LIST: + if not cls.USES_MAGIC_VALUE: + continue + if chip_magic_value == cls.MAGIC_VALUE: + inst = cls(detect_port._port, baud, trace_enabled=trace_enabled) + inst = check_if_stub(inst) + inst._post_connect() + break + else: + err_msg = f"Unexpected chip magic value {chip_magic_value:#010x}." + + if inst is not None: + return inst + raise FatalError( f"{err_msg} Failed to autodetect chip type." "\nProbably it is unsupported by this version of esptool." ) -# "Operation" commands, executable at command line. One function each -# -# Each function takes either two args (, ) or a single -# argument. +# Commands that require an ESP object +##################################### -def load_ram(esp, args): - image = LoadFirmwareImage(esp.CHIP_NAME, args.filename) +def load_ram(esp: ESPLoader, input: ImageSource) -> None: + """ + Load a firmware image into RAM and execute it on the ESP device. - print("RAM boot...") - for seg in image.segments: + Args: + esp: Initiated esp object connected to a real device. + input: Path to the firmware image file, opened file-like object, + or the image data as bytes. + """ + data, source = get_bytes(input) + image = LoadFirmwareImage(esp.CHIP_NAME, data) + + log.stage() + source = "image" if source is None else f"'{source}'" + log.print(f"Loading {source} to RAM...") + for i, seg in enumerate(image.segments, start=1): size = len(seg.data) - print("Downloading %d bytes at %08x..." % (size, seg.addr), end=" ") - sys.stdout.flush() + log.progress_bar( + cur_iter=i, + total_iters=len(image.segments), + prefix=f"Downloading {size} bytes at {seg.addr:#010x} ", + suffix="...", + ) + esp.mem_begin( size, div_roundup(size, esp.ESP_RAM_BLOCK), esp.ESP_RAM_BLOCK, seg.addr ) - seq = 0 while len(seg.data) > 0: esp.mem_block(seg.data[0 : esp.ESP_RAM_BLOCK], seq) seg.data = seg.data[esp.ESP_RAM_BLOCK :] seq += 1 - print("done!") - - print("All segments done, executing at %08x" % image.entrypoint) + log.stage(finish=True) + log.print( + f"Loaded {len(image.segments)} segments from {source} to RAM, " + f"executing at {image.entrypoint:#010x}." + ) esp.mem_finish(image.entrypoint) -def read_mem(esp, args): - print("0x%08x = 0x%08x" % (args.address, esp.read_reg(args.address))) +def read_mem(esp: ESPLoader, address: int) -> None: + """ + Read and display a 32-bit value from a memory address on the ESP device. + Args: + esp: Initiated esp object connected to a real device. + address: Memory address to read from (32-bit aligned). + """ + log.print(f"{address:#010x} = {esp.read_reg(address):#010x}") -def write_mem(esp, args): - esp.write_reg(args.address, args.value, args.mask, 0) - print("Wrote %08x, mask %08x to %08x" % (args.value, args.mask, args.address)) +def write_mem(esp: ESPLoader, address: int, value: int, mask: int = 0xFFFFFFFF) -> None: + """ + Write a 32-bit value to a memory address on the ESP device with optional bitmask. -def dump_mem(esp, args): - with open(args.filename, "wb") as f: - for i in range(args.size // 4): - d = esp.read_reg(args.address + (i * 4)) - f.write(struct.pack(b"> 16 - flash_size = DETECTED_FLASH_SIZES.get(size_id) - if args is not None and args.flash_size == "detect": - if flash_size is None: - flash_size = "4MB" - print( - "WARNING: Could not auto-detect Flash size " - f"(FlashID={flash_id:#x}, SizeID={size_id:#x}), defaulting to 4MB" +def dump_mem( + esp: ESPLoader, address: int, size: int, output: str | None = None +) -> bytes | None: + """ + Dump a block of memory from the ESP device. + + Args: + esp: Initiated esp object connected to a real device. + address: Starting memory address to dump from. + size: Number of bytes to dump. + output: Path to output file for binary data. If None, returns the data. + + Returns: + Memory dump as bytes if output is None; + otherwise, returns None after writing to file. + """ + data = io.BytesIO() # Use BytesIO to store the memory dump + log.stage() + log.print( + f"Dumping {size} bytes from {address:#010x}" + + (f" to file '{output}'..." if output else "...") + ) + t = time.time() + # Read the memory in 4-byte chunks. + for i in range(size // 4): + cur_addr = address + (i * 4) + d = esp.read_reg(cur_addr) + data.write(struct.pack(" 0.0 else "" + dest_msg = f" to '{output}'" if output else "" + log.stage(finish=True) + log.print( + f"Dumped {data.tell()} bytes from {address:#010x} in {t:.1f} seconds" + f"{speed_msg}{dest_msg}." + ) + if output: + with open(output, "wb") as f: + f.write(data.getvalue()) + return None + else: + return data.getvalue() + + +def _get_flash_info(esp: ESPLoader, cache: bool = True) -> tuple[int, int, str | None]: + """ + Get the flash memory chip information including vendor ID, device ID, and + flash size. + + Args: + esp: Initiated esp object connected to a real device. + cache: Whether to use cached flash ID (default: True). + + Returns: + Tuple containing (vendor_id, device_id, flash_size) + """ + flash_id = esp.flash_id(cache=cache) + vendor_id = flash_id & 0xFF + # Swap the bytes of the device ID by taking the high byte first, then the low byte + device_id = ((flash_id >> 16) & 0xFF) | ((flash_id >> 8) & 0xFF) << 8 + + if vendor_id == ADESTO_VENDOR_ID: + # Lower 5 bits of second byte of flash_id is size_id + size_id = (flash_id >> 8) & 0x1F + flash_size = DETECTED_FLASH_SIZES_ADESTO.get(size_id) + else: + size_id = flash_id >> 16 + flash_size = DETECTED_FLASH_SIZES.get(size_id) + + return vendor_id, device_id, flash_size + + +def detect_flash_size(esp: ESPLoader) -> str | None: + """ + Detect the flash size of the connected ESP device. + + Args: + esp: Initiated esp object connected to a real device. + + Returns: + Detected flash size in bytes, or None if unrecognized. + """ + if esp.secure_download_mode: + raise FatalError( + "Detecting flash size is not supported in secure download mode. " + "Need to manually specify flash size." + ) + _, _, flash_size = _get_flash_info(esp) return flash_size -def _update_image_flash_params(esp, address, args, image): +def _update_image_flash_params(esp, address, flash_freq, flash_mode, flash_size, image): """ - Modify the flash mode & size bytes if this looks like an executable bootloader image + Update the flash mode, size, and freq parameters in a bootloader image, + if applicable. + + Args: + esp (ESPLoader): ESPLoader object that provides device-specific attributes + (e.g., BOOTLOADER_FLASH_OFFSET, ESP_IMAGE_MAGIC, CHIP_NAME) and methods for + image verification and parameter parsing. + address (int): The flash memory address where the image is to be written. + flash_freq (str, optional): Flash frequency setting + (``"keep"`` to retain current). + flash_mode (str, optional): Flash mode setting + (``"keep"`` to retain current). + flash_size (str, optional): Flash size setting + (``"keep"`` to retain current). + image (bytes): The image data that may contain an executable bootloader image. + + Returns: + bytes: The modified image data with updated flash parameters + (and recalculated SHA256 digest, if applicable), + or the original image if no modifications were performed. """ if len(image) < 8: return image # not long enough to be a bootloader image - - # unpack the (potential) image header - magic, _, flash_mode, flash_size_freq = struct.unpack("BBBB", image[:4]) if address != esp.BOOTLOADER_FLASH_OFFSET: return image # not flashing bootloader offset, so don't modify this - - if (args.flash_mode, args.flash_freq, args.flash_size) == ("keep",) * 3: + if (flash_mode, flash_freq, flash_size) == ("keep",) * 3: return image # all settings are 'keep', not modifying anything + # unpack the (potential) image header + magic, _, img_flash_mode, img_flash_size_freq = struct.unpack("BBBB", image[:4]) + # easy check if this is an image: does it start with a magic byte? if magic != esp.ESP_IMAGE_MAGIC: - print( - "Warning: Image file at 0x%x doesn't look like an image file, " - "so not changing any flash settings." % address + log.warning( + f"Image file at {address:#x} doesn't look like an image file, " + "so not changing any flash settings." ) return image @@ -270,31 +422,33 @@ def _update_image_flash_params(esp, address, args, image): test_image = esp.BOOTLOADER_IMAGE(io.BytesIO(image)) test_image.verify() except Exception: - print( - "Warning: Image file at 0x%x is not a valid %s image, " - "so not changing any flash settings." % (address, esp.CHIP_NAME) + log.warning( + f"Image file at {address:#x} is not a valid {esp.CHIP_NAME} image," + " so not changing any flash settings." ) return image # After the 8-byte header comes the extended header for chips others than ESP8266. - # The 15th byte of the extended header indicates if the image is protected by - # a SHA256 checksum. In that case we recalculate the SHA digest after modifying the header. - sha_appended = args.chip != "esp8266" and image[8 + 15] == 1 + # The 15th byte of the extended header indicates if the image is protected by SHA256 + # checksum. In that case we recalculate the SHA digest after modifying the header. + sha_appended = esp.CHIP_NAME != "esp8266" and image[8 + 15] == 1 - if args.flash_mode != "keep": - flash_mode = FLASH_MODES[args.flash_mode] + if flash_mode != "keep": + img_flash_mode = FLASH_MODES[flash_mode] - flash_freq = flash_size_freq & 0x0F - if args.flash_freq != "keep": - flash_freq = esp.parse_flash_freq_arg(args.flash_freq) + img_flash_freq = img_flash_size_freq & 0x0F + if flash_freq != "keep": + img_flash_freq = esp.parse_flash_freq_arg(flash_freq) - flash_size = flash_size_freq & 0xF0 - if args.flash_size != "keep": - flash_size = esp.parse_flash_size_arg(args.flash_size) + img_flash_size = img_flash_size_freq & 0xF0 + if flash_size != "keep": + img_flash_size = esp.parse_flash_size_arg(flash_size) - flash_params = struct.pack(b"BB", flash_mode, flash_size + flash_freq) + flash_params = struct.pack(b"BB", img_flash_mode, img_flash_size + img_flash_freq) if flash_params != image[2:4]: - print("Flash params set to 0x%04x" % struct.unpack(">H", flash_params)) + log.print( + f"Flash parameters set to {struct.unpack('>H', flash_params)[0]:#06x}." + ) image = image[0:2] + flash_params + image[4:] # recalculate the SHA digest if it was appended @@ -317,17 +471,17 @@ def _update_image_flash_params(esp, address, args, image): ) ) - # get the SHA digest newly stored in the image and compare it to the calculated one + # get SHA digest newly stored in the image and compare it to the calculated one image_stored_sha = image[ image_object.data_length : image_object.data_length + image_object.SHA256_DIGEST_LEN ] if hexify(sha_digest_calculated) == hexify(image_stored_sha): - print("SHA digest in image updated") + log.print("SHA digest in image updated.") else: - print( - "WARNING: SHA recalculation for binary failed!\n" + log.warning( + "SHA recalculation for binary failed!\n" f"\tExpected calculated SHA: {hexify(sha_digest_calculated)}\n" f"\tSHA stored in binary: {hexify(image_stored_sha)}" ) @@ -335,36 +489,85 @@ def _update_image_flash_params(esp, address, args, image): return image -def write_flash(esp, args): - # set args.compress based on default behaviour: - # -> if either --compress or --no-compress is set, honour that - # -> otherwise, set --compress unless --no-stub is set - if args.compress is None and not args.no_compress: - args.compress = not args.no_stub - - if not args.force and esp.CHIP_NAME != "ESP8266" and not esp.secure_download_mode: +def write_flash( + esp: ESPLoader, + addr_data: list[tuple[int, ImageSource]], + flash_freq: str = "keep", + flash_mode: str = "keep", + flash_size: str = "keep", + **kwargs, +) -> None: + """ + Write firmware or data to the SPI flash memory of an ESP device. + + Args: + esp: Initiated esp object connected to a real device. + addr_data: List of (address, data) tuples specifying where + to write each file or data in flash memory. The data can be + a file path (str), bytes, or a file-like object. + flash_freq: Flash frequency to set in the bootloader image header + (``"keep"`` to retain current). + flash_mode: Flash mode to set in the bootloader image header + (``"keep"`` to retain current). + flash_size: Flash size to set in the bootloader image header + (``"keep"`` to retain current). + + Keyword Args: + erase_all (bool): Erase the entire flash before writing. + encrypt (bool): Encrypt all files during flashing. + encrypt_files (list[tuple[int, ImageSource]] | None): List of + (address, data) tuples for files to encrypt individually. + compress (bool): Compress data before flashing. + no_compress (bool): Don't compress data before flashing. + force (bool): Ignore safety checks (e.g., overwriting bootloader, flash size). + ignore_flash_enc_efuse (bool): Ignore flash encryption eFuse settings. + no_progress (bool): Disable progress updates. + """ + # Normalize addr_data to use bytes + norm_addr_data = [(addr, get_bytes(data)) for addr, data in addr_data] + + # Set default values of optional arguments + erase_all: bool = kwargs.get("erase_all", False) + encrypt: bool = kwargs.get("encrypt", False) + encrypt_files: list[tuple[int, ImageSource]] | None = kwargs.get( + "encrypt_files", None + ) + compress: bool = kwargs.get("compress", False) + no_compress: bool = kwargs.get("no_compress", False) + force: bool = kwargs.get("force", False) + ignore_flash_enc_efuse: bool = kwargs.get("ignore_flash_enc_efuse", False) + no_progress: bool = kwargs.get("no_progress", False) + + # set compress based on default behaviour: + # -> if either "compress" or "no_compress" is set, honour that + # -> otherwise, set "compress" unless the stub flasher is disabled + if not compress and not no_compress: + compress = esp.IS_STUB + + if not force and esp.CHIP_NAME != "ESP8266" and not esp.secure_download_mode: # Check if secure boot is active if esp.get_secure_boot_enabled(): - for address, _ in args.addr_filename: + for address, _ in norm_addr_data: if address < 0x8000: raise FatalError( "Secure Boot detected, writing to flash regions < 0x8000 " "is disabled to protect the bootloader. " - "Use --force to override, " + "Use the force argument to override, " "please use with caution, otherwise it may brick your device!" ) # Check if chip_id and min_rev in image are valid for the target in use - for _, argfile in args.addr_filename: + for _, (data, name) in norm_addr_data: try: - image = LoadFirmwareImage(esp.CHIP_NAME, argfile) + image = LoadFirmwareImage(esp.CHIP_NAME, data) except (FatalError, struct.error, RuntimeError): continue - finally: - argfile.seek(0) # LoadFirmwareImage changes the file handle position if image.chip_id != esp.IMAGE_CHIP_ID: + msg = ( + "Input does not contain" if name is None else f"'{name}' is not an" + ) raise FatalError( - f"{argfile.name} is not an {esp.CHIP_NAME} image. " - "Use --force to flash anyway." + f"{msg} an {esp.CHIP_NAME} image. " + "Use the force argument to flash anyway." ) # this logic below decides which min_rev to use, min_rev or min/max_rev_full @@ -382,7 +585,7 @@ def write_flash(esp, args): if use_rev_full_fields: rev = esp.get_chip_revision() if rev < image.min_rev_full or rev > image.max_rev_full: - error_str = f"{argfile.name} requires chip revision in range " + error_str = f"'{name}' requires chip revision in range " error_str += ( f"[v{image.min_rev_full // 100}.{image.min_rev_full % 100} - " ) @@ -393,7 +596,9 @@ def write_flash(esp, args): f"v{image.max_rev_full // 100}.{image.max_rev_full % 100}] " ) error_str += f"(this chip is revision v{rev // 100}.{rev % 100})" - raise FatalError(f"{error_str}. Use --force to flash anyway.") + raise FatalError( + f"{error_str}. Use the force argument to flash anyway." + ) else: # In IDF, image.min_rev is set based on Kconfig option. # For C3 chip, image.min_rev is the Minor revision @@ -404,14 +609,14 @@ def write_flash(esp, args): rev = esp.get_major_chip_version() if rev < image.min_rev: raise FatalError( - f"{argfile.name} requires chip revision " + f"'{name}' requires chip revision " f"{image.min_rev} or higher (this chip is revision {rev}). " - "Use --force to flash anyway." + "Use the force argument to flash anyway." ) # In case we have encrypted files to write, # we first do few sanity checks before actual flash - if args.encrypt or args.encrypt_files is not None: + if encrypt or encrypt_files is not None: do_write = True if not esp.secure_download_mode: @@ -426,33 +631,40 @@ def write_flash(esp, args): crypt_cfg_efuse = esp.get_flash_crypt_config() if crypt_cfg_efuse is not None and crypt_cfg_efuse != 0xF: - print("Unexpected FLASH_CRYPT_CONFIG value: 0x%x" % (crypt_cfg_efuse)) + log.print(f"Unexpected FLASH_CRYPT_CONFIG value: {crypt_cfg_efuse:#x}") do_write = False enc_key_valid = esp.is_flash_encryption_key_valid() if not enc_key_valid: - print("Flash encryption key is not programmed") + log.print("Flash encryption key is not programmed.") do_write = False # Determine which files list contain the ones to encrypt - files_to_encrypt = args.addr_filename if args.encrypt else args.encrypt_files + files_to_encrypt = ( + norm_addr_data + if encrypt is not None + else [(addr, get_bytes(data)) for addr, data in encrypt_files] + ) - for address, argfile in files_to_encrypt: - if address % esp.FLASH_ENCRYPTED_WRITE_ALIGN: - print( - "File %s address 0x%x is not %d byte aligned, can't flash encrypted" - % (argfile.name, address, esp.FLASH_ENCRYPTED_WRITE_ALIGN) - ) - do_write = False + if files_to_encrypt is not None: + for address, (data, name) in files_to_encrypt: + if address % esp.FLASH_ENCRYPTED_WRITE_ALIGN: + source = "Input image" if name is None else f"'{name}'" + log.warning( + f"{source} (address {address:#x}) is not " + f"{esp.FLASH_ENCRYPTED_WRITE_ALIGN} byte aligned, " + "can't flash encrypted." + ) + do_write = False - if not do_write and not args.ignore_flash_encryption_efuse_setting: + if not do_write and not ignore_flash_enc_efuse: raise FatalError( "Can't perform encrypted flash write, " - "consult Flash Encryption documentation for more information" + "consult Flash Encryption documentation for more information." ) else: - if not args.force and esp.CHIP_NAME != "ESP8266": + if not force and esp.CHIP_NAME != "ESP8266": # ESP32 does not support `get_security_info()` and `secure_download_mode` if ( esp.CHIP_NAME != "ESP32" @@ -463,7 +675,7 @@ def write_flash(esp, args): "WARNING: Detected flash encryption and " "secure download mode enabled.\n" "Flashing plaintext binary may brick your device! " - "Use --force to override the warning." + "Use the force argument to override the warning." ) if ( @@ -475,56 +687,53 @@ def write_flash(esp, args): "WARNING: Detected flash encryption enabled and " "download manual encrypt disabled.\n" "Flashing plaintext binary may brick your device! " - "Use --force to override the warning." + "Use the force argument to override the warning." ) + flash_size = _set_flash_parameters(esp, flash_size) # Set flash size parameters + set_flash_size = ( - flash_size_bytes(args.flash_size) - if args.flash_size not in ["detect", "keep"] - else None + flash_size_bytes(flash_size) if flash_size not in ["detect", "keep"] else None ) if esp.secure_download_mode: flash_end = set_flash_size else: # Check against real flash chip size if not in SDM flash_end_str = detect_flash_size(esp) flash_end = flash_size_bytes(flash_end_str) - if set_flash_size and set_flash_size > flash_end: - print( - f"WARNING: Set --flash_size {args.flash_size} " + if set_flash_size and flash_end and set_flash_size > flash_end: + log.warning( + f"Set flash_size {flash_size} " f"is larger than the available flash size of {flash_end_str}." ) - # Verify file sizes fit in the set --flash_size, or real flash size if smaller - flash_end = min(set_flash_size, flash_end) if set_flash_size else flash_end + # Verify file sizes fit in the set flash_size, or real flash size if smaller + flash_end = ( + min(set_flash_size, flash_end) if set_flash_size and flash_end else flash_end + ) if flash_end is not None: - for address, argfile in args.addr_filename: - argfile.seek(0, os.SEEK_END) - if address + argfile.tell() > flash_end: + for address, (data, name) in norm_addr_data: + if address + len(data) > flash_end: + source = "Input image" if name is None else f"File '{name}'" raise FatalError( - f"File {argfile.name} (length {argfile.tell()}) at offset " - f"{address} will not fit in {flash_end} bytes of flash. " - "Change the --flash_size argument, or flashing address." + f"{source} (length {len(data)}) at offset " + f"{address:#010x} will not fit in {flash_end} bytes of flash. " + "Change the flash_size argument or flashing address." ) - argfile.seek(0) - if args.erase_all: - erase_flash(esp, args) + if erase_all: + erase_flash(esp) else: - for address, argfile in args.addr_filename: - argfile.seek(0, os.SEEK_END) - write_end = address + argfile.tell() - argfile.seek(0) + for address, (data, _) in norm_addr_data: + write_end = address + len(data) bytes_over = address % esp.FLASH_SECTOR_SIZE if bytes_over != 0: - print( - "WARNING: Flash address {:#010x} is not aligned " - "to a {:#x} byte flash sector. " - "{:#x} bytes before this address will be erased.".format( - address, esp.FLASH_SECTOR_SIZE, bytes_over - ) + log.note( + f"Flash address {address:#010x} is not aligned " + f"to a {esp.FLASH_SECTOR_SIZE:#x} byte flash sector. " + f"{bytes_over:#x} bytes before this address will be erased." ) # Print the address range of to-be-erased flash memory region - print( + log.print( "Flash will be erased from {:#010x} to {:#010x}...".format( address - bytes_over, div_roundup(write_end, esp.FLASH_SECTOR_SIZE) @@ -533,29 +742,28 @@ def write_flash(esp, args): ) ) - """ Create a list describing all the files we have to flash. + """ + Create a list describing all the files we have to flash. Each entry holds an "encrypt" flag marking whether the file needs encryption or not. This list needs to be sorted. - First, append to each entry of our addr_filename list the flag args.encrypt - E.g., if addr_filename is [(0x1000, "partition.bin"), (0x8000, "bootloader")], + First, append to each entry of our addr_data list the flag "encrypt" + E.g., if addr_data is [(0x1000, "partition.bin"), (0x8000, "bootloader")], all_files will be [ - (0x1000, "partition.bin", args.encrypt), - (0x8000, "bootloader", args.encrypt) + (0x1000, data, "partition.bin", encrypt), + (0x8000, data, "bootloader", encrypt) ], - where, of course, args.encrypt is either True or False + where, of course, encrypt is either True or False """ - all_files = [ - (offs, filename, args.encrypt) for (offs, filename) in args.addr_filename - ] + all_files = [(addr, data, name, encrypt) for (addr, (data, name)) in norm_addr_data] """ Now do the same with encrypt_files list, if defined. In this case, the flag is True """ - if args.encrypt_files is not None: + if encrypt_files is not None: encrypted_files_flag = [ - (offs, filename, True) for (offs, filename) in args.encrypt_files + (addr, *get_bytes(data), True) for (addr, data) in encrypt_files ] # Concatenate both lists and sort them. @@ -564,71 +772,80 @@ def write_flash(esp, args): # let's use sorted. all_files = sorted(all_files + encrypted_files_flag, key=lambda x: x[0]) - for address, argfile, encrypted in all_files: - compress = args.compress + for address, data, name, encrypted in all_files: + compress = compress # Check whether we can compress the current file before flashing if compress and encrypted: - print("\nWARNING: - compress and encrypt options are mutually exclusive ") - print("Will flash %s uncompressed" % argfile.name) + source = "input bytes" if name is None else f"'{name}'" + log.print("\n") + log.warning("Compress and encrypt options are mutually exclusive.") + log.print(f"Will flash {source} uncompressed.") compress = False - image = argfile.read() + image = data if len(image) == 0: - print("WARNING: File %s is empty" % argfile.name) + log.warning( + "Input bytes are empty." if name is None else f"'{name}' is empty." + ) continue image = pad_to(image, esp.FLASH_ENCRYPTED_WRITE_ALIGN if encrypted else 4) - if args.no_stub: - print("Erasing flash...") + if not esp.IS_STUB: + log.print("Erasing flash...") # It is not possible to write to not aligned addresses without stub, # so there are added 0xFF (erase) bytes at the beginning of the image # to align it. bytes_over = address % esp.FLASH_SECTOR_SIZE address -= bytes_over - image = b"\xFF" * bytes_over + image + image = b"\xff" * bytes_over + image if not esp.secure_download_mode and not esp.get_secure_boot_enabled(): - image = _update_image_flash_params(esp, address, args, image) + image = _update_image_flash_params( + esp, address, flash_freq, flash_mode, flash_size, image + ) else: - print( - "WARNING: Security features enabled, so not changing any flash settings." + log.warning( + "Security features enabled, so not changing any flash settings." ) calcmd5 = hashlib.md5(image).hexdigest() uncsize = len(image) if compress: uncimage = image image = zlib.compress(uncimage, 9) + compsize = len(image) original_image = image # Save the whole image in case retry is needed # Try again if reconnect was successful + log.stage() for attempt in range(1, esp.WRITE_FLASH_ATTEMPTS + 1): try: if compress: # Decompress the compressed binary a block at a time, # to dynamically calculate the timeout based on the real write size decompress = zlib.decompressobj() - blocks = esp.flash_defl_begin(uncsize, len(image), address) + esp.flash_defl_begin(uncsize, compsize, address) else: - blocks = esp.flash_begin( - uncsize, address, begin_rom_encrypted=encrypted - ) - argfile.seek(0) # in case we need it again + esp.flash_begin(uncsize, address, begin_rom_encrypted=encrypted) seq = 0 bytes_sent = 0 # bytes sent on wire bytes_written = 0 # bytes written to flash t = time.time() timeout = DEFAULT_TIMEOUT - - while len(image) > 0: - print_overwrite( - "Writing at 0x%08x... (%d %%)" - % (address + bytes_written, 100 * (seq + 1) // blocks) - ) - sys.stdout.flush() + image_size = compsize if compress else uncsize + while len(image) >= 0: + if not no_progress: + log.progress_bar( + cur_iter=image_size - len(image), + total_iters=image_size, + prefix=f"Writing at {address + bytes_written:#010x} ", + suffix=f" {bytes_sent}/{image_size} bytes...", + ) + if len(image) == 0: # All data sent, print 100% progress and end + break block = image[0 : esp.FLASH_WRITE_SIZE] if compress: # feeding each compressed block into the decompressor lets us @@ -642,7 +859,8 @@ def write_flash(esp, args): ), ) if not esp.IS_STUB: - timeout = block_timeout # ROM code writes block to flash before ACKing + # ROM code writes block to flash before ACKing + timeout = block_timeout esp.flash_defl_block(block, seq, timeout=timeout) if esp.IS_STUB: # Stub ACKs when block is received, @@ -662,16 +880,17 @@ def write_flash(esp, args): break except SerialException: if attempt == esp.WRITE_FLASH_ATTEMPTS or encrypted: - # Already retried once or encrypted mode is disabled because of security reasons + # Already retried once or encrypted mode is disabled because of + # security reasons raise - print("\nLost connection, retrying...") + log.print("\nLost connection, retrying...") esp._port.close() - print("Waiting for the chip to reconnect", end="") + log.print("Waiting for the chip to reconnect", end="") for _ in range(DEFAULT_CONNECT_ATTEMPTS): try: time.sleep(1) esp._port.open() - print() # Print new line which was suppressed by print(".") + log.print() # Print new line which was suppressed by print(".") esp.connect() if esp.IS_STUB: # Hack to bypass the stub overwrite check @@ -681,8 +900,7 @@ def write_flash(esp, args): image = original_image break except SerialException: - print(".", end="") - sys.stdout.flush() + log.print(".", end="", flush=True) else: raise # Reconnect limit reached @@ -694,40 +912,41 @@ def write_flash(esp, args): t = time.time() - t speed_msg = "" + log.stage(finish=True) if compress: if t > 0.0: - speed_msg = " (effective %.1f kbit/s)" % (uncsize / t * 8 / 1000) - print_overwrite( - "Wrote %d bytes (%d compressed) at 0x%08x in %.1f seconds%s..." - % (uncsize, bytes_sent, address, t, speed_msg), - last_line=True, + speed_msg = f" ({uncsize / t * 8 / 1000:.1f} kbit/s)" + log.print( + f"Wrote {uncsize} bytes ({bytes_sent} compressed) " + f"at {address:#010x} in {t:.1f} seconds{speed_msg}." ) else: if t > 0.0: speed_msg = " (%.1f kbit/s)" % (bytes_written / t * 8 / 1000) - print_overwrite( - "Wrote %d bytes at 0x%08x in %.1f seconds%s..." - % (bytes_written, address, t, speed_msg), - last_line=True, + log.print( + f"Wrote {bytes_written} bytes at {address:#010x} in {t:.1f} " + f"seconds{speed_msg}." ) if not encrypted and not esp.secure_download_mode: try: res = esp.flash_md5sum(address, uncsize) if res != calcmd5: - print("File md5: %s" % calcmd5) - print("Flash md5: %s" % res) - print( - "MD5 of 0xFF is %s" - % (hashlib.md5(b"\xff" * uncsize).hexdigest()) - ) + log.print(f"Input MD5: {calcmd5}") + log.print(f"Flash MD5: {res}") + if res == hashlib.md5(b"\xff" * uncsize).hexdigest(): + raise FatalError( + "Write failed, the written flash region is empty." + ) raise FatalError("MD5 of file does not match data in flash!") else: - print("Hash of data verified.") + log.print("Hash of data verified.") except NotImplementedInROMError: pass - - print("\nLeaving...") + else: + log.print( + "Cannot verify written data if encrypted or in secure download mode." + ) if esp.IS_STUB: # skip sending flash_finish to ROM loader here, @@ -735,592 +954,556 @@ def write_flash(esp, args): esp.flash_begin(0, 0) # Get the "encrypted" flag for the last file flashed - # Note: all_files list contains triplets like: - # (address: Integer, filename: String, encrypted: Boolean) - last_file_encrypted = all_files[-1][2] + # Note: all_files list contains quadruplets like: + # (address: int, filename: str | None, data: bytes, encrypted: bool) + last_file_encrypted = all_files[-1][3] # Check whether the last file flashed was compressed or not - if args.compress and not last_file_encrypted: + if compress and not last_file_encrypted: esp.flash_defl_finish(False) else: esp.flash_finish(False) - if args.verify: - print("Verifying just-written flash...") - print( - "(This option is deprecated, " - "flash contents are now always read back after flashing.)" - ) - # If some encrypted files have been flashed, - # print a warning saying that we won't check them - if args.encrypt or args.encrypt_files is not None: - print("WARNING: - cannot verify encrypted files, they will be ignored") - # Call verify_flash function only if there is at least - # one non-encrypted file flashed - if not args.encrypt: - verify_flash(esp, args) - - -def image_info(args): - def v2(): - def get_key_from_value(dict, val): - """Get key from value in dictionary""" - for key, value in dict.items(): - if value == val: - return key - return None - - print() - title = "{} image header".format(args.chip.upper()) - print(title) - print("=" * len(title)) - print("Image version: {}".format(image.version)) - print( - "Entry point: {:#8x}".format(image.entrypoint) - if image.entrypoint != 0 - else "Entry point not set" - ) - - print("Segments: {}".format(len(image.segments))) - - # Flash size - flash_s_bits = image.flash_size_freq & 0xF0 # high four bits - flash_s = get_key_from_value(image.ROM_LOADER.FLASH_SIZES, flash_s_bits) - print( - "Flash size: {}".format(flash_s) - if flash_s is not None - else "WARNING: Invalid flash size ({:#02x})".format(flash_s_bits) - ) - - # Flash frequency - flash_fr_bits = image.flash_size_freq & 0x0F # low four bits - flash_fr = get_key_from_value(image.ROM_LOADER.FLASH_FREQUENCY, flash_fr_bits) - print( - "Flash freq: {}".format(flash_fr) - if flash_fr is not None - else "WARNING: Invalid flash frequency ({:#02x})".format(flash_fr_bits) - ) - - # Flash mode - flash_mode = get_key_from_value(FLASH_MODES, image.flash_mode) - print( - "Flash mode: {}".format(flash_mode.upper()) - if flash_mode is not None - else "WARNING: Invalid flash mode ({})".format(image.flash_mode) - ) - - # Extended header (ESP32 and later only) - if args.chip != "esp8266": - print() - title = "{} extended image header".format(args.chip.upper()) - print(title) - print("=" * len(title)) - print( - f"WP pin: {image.wp_pin:#02x}", - *["(disabled)"] if image.wp_pin == image.WP_PIN_DISABLED else [], - ) - print( - "Flash pins drive settings: " - "clk_drv: {:#02x}, q_drv: {:#02x}, d_drv: {:#02x}, " - "cs0_drv: {:#02x}, hd_drv: {:#02x}, wp_drv: {:#02x}".format( - image.clk_drv, - image.q_drv, - image.d_drv, - image.cs_drv, - image.hd_drv, - image.wp_drv, - ) - ) - try: - chip = next( - chip - for chip in CHIP_DEFS.values() - if getattr(chip, "IMAGE_CHIP_ID", None) == image.chip_id - ) - print(f"Chip ID: {image.chip_id} ({chip.CHIP_NAME})") - except StopIteration: - print(f"Chip ID: {image.chip_id} (Unknown ID)") - print( - "Minimal chip revision: " - f"v{image.min_rev_full // 100}.{image.min_rev_full % 100}, " - f"(legacy min_rev = {image.min_rev})" - ) - print( - "Maximal chip revision: " - f"v{image.max_rev_full // 100}.{image.max_rev_full % 100}" - ) - print() - - # Segments overview - title = "Segments information" - print(title) - print("=" * len(title)) - headers_str = "{:>7} {:>7} {:>10} {:>10} {:10}" - print( - headers_str.format( - "Segment", "Length", "Load addr", "File offs", "Memory types" - ) - ) - print( - "{} {} {} {} {}".format("-" * 7, "-" * 7, "-" * 10, "-" * 10, "-" * 12) - ) - format_str = "{:7} {:#07x} {:#010x} {:#010x} {}" - app_desc = None - bootloader_desc = None - for idx, seg in enumerate(image.segments): - segs = seg.get_memory_type(image) - seg_name = ", ".join(segs) - if "DROM" in segs: # The DROM segment starts with the esp_app_desc_t struct - app_desc = seg.data[:256] - elif "DRAM" in segs: - # The DRAM segment starts with the esp_bootloader_desc_t struct - if len(seg.data) >= 80: - bootloader_desc = seg.data[:80] - print( - format_str.format(idx, len(seg.data), seg.addr, seg.file_offs, seg_name) - ) - print() - - # Footer - title = f"{args.chip.upper()} image footer" - print(title) - print("=" * len(title)) - calc_checksum = image.calculate_checksum() - print( - "Checksum: {:#02x} ({})".format( - image.checksum, - ( - "valid" - if image.checksum == calc_checksum - else "invalid - calculated {:02x}".format(calc_checksum) - ), - ) - ) - try: - digest_msg = "Not appended" - if image.append_digest: - is_valid = image.stored_digest == image.calc_digest - digest_msg = "{} ({})".format( - hexify(image.calc_digest, uppercase=False), - "valid" if is_valid else "invalid", - ) - print("Validation hash: {}".format(digest_msg)) - except AttributeError: - pass # ESP8266 image has no append_digest field - - if app_desc: - APP_DESC_STRUCT_FMT = " None: + """ + Read and display the MAC address of the ESP device. - # append_digest, either 0 or 1 - if extended_header[-1] not in [0, 1]: - raise FatalError("Append digest field not 0 or 1") + Args: + esp: Initiated esp object connected to a real device. + """ - chip_id = int.from_bytes(extended_header[4:5], "little") - for rom in [n for n in ROM_LIST if n.CHIP_NAME != "ESP8266"]: - if chip_id == rom.IMAGE_CHIP_ID: - args.chip = rom.CHIP_NAME - break - else: - raise FatalError(f"Unknown image chip ID ({chip_id})") - except FatalError: - args.chip = "esp8266" + def print_mac(label, mac): + log.print(f"{label + ':':<20}{':'.join(f'{x:02x}' for x in mac)}") - print(f"Detected image type: {args.chip.upper()}") + eui64 = esp.read_mac("EUI64") + if eui64: + print_mac("MAC", eui64) + print_mac("BASE MAC", esp.read_mac("BASE_MAC")) + print_mac("MAC_EXT", esp.read_mac("MAC_EXT")) + else: + print_mac("MAC", esp.read_mac("BASE_MAC")) - image = LoadFirmwareImage(args.chip, args.filename) - if args.version == "2": - v2() - return +def chip_id(esp: ESPLoader) -> None: + """ + Read and display the Chip ID of the ESP device if available, + otherwise fall back to displaying the MAC address. - print("Image version: {}".format(image.version)) - print( - "Entry point: {:8x}".format(image.entrypoint) - if image.entrypoint != 0 - else "Entry point not set" - ) - print("{} segments".format(len(image.segments))) - print() - idx = 0 - for seg in image.segments: - idx += 1 - segs = seg.get_memory_type(image) - seg_name = ",".join(segs) - print("Segment {}: {} [{}]".format(idx, seg, seg_name)) - calc_checksum = image.calculate_checksum() - print( - "Checksum: {:02x} ({})".format( - image.checksum, - ( - "valid" - if image.checksum == calc_checksum - else "invalid - calculated {:02x}".format(calc_checksum) - ), - ) - ) + Args: + esp: Initiated esp object connected to a real device. + """ try: - digest_msg = "Not appended" - if image.append_digest: - is_valid = image.stored_digest == image.calc_digest - digest_msg = "{} ({})".format( - hexify(image.calc_digest, uppercase=False), - "valid" if is_valid else "invalid", - ) - print("Validation Hash: {}".format(digest_msg)) - except AttributeError: - pass # ESP8266 image has no append_digest field - - -def make_image(args): - print("Creating {} image...".format(args.chip)) - image = ESP8266ROMFirmwareImage() - if len(args.segfile) == 0: - raise FatalError("No segments specified") - if len(args.segfile) != len(args.segaddr): - raise FatalError( - "Number of specified files does not match number of specified addresses" - ) - for seg, addr in zip(args.segfile, args.segaddr): - with open(seg, "rb") as f: - data = f.read() - image.segments.append(ImageSegment(addr, data)) - image.entrypoint = args.entrypoint - image.save(args.output) - print("Successfully created {} image.".format(args.chip)) - + chipid = esp.chip_id() + log.print(f"Chip ID: {chipid:#010x}") + except NotSupportedError: + log.warning(f"{esp.CHIP_NAME} has no chip ID. Reading MAC address instead.") + read_mac(esp) -def elf2image(args): - e = ELFFile(args.input) - if args.chip == "auto": # Default to ESP8266 for backwards compatibility - args.chip = "esp8266" - print("Creating {} image...".format(args.chip)) +def attach_flash( + esp: ESPLoader, + spi_connection: (tuple[int, int, int, int, int] | str) | None = None, +) -> None: + """ + Configure and attach a SPI flash memory chip to the ESP device, + verify the connection. + All following flash operations will be performed on the attached flash chip. + + Args: + esp: Initiated esp object connected to a real device. + spi_connection: Custom SPI connection configuration. + This can either be a tuple containing five pin numbers + ``(CLK, Q, D, HD, CS)`` for manual configuration + or a string (``"SPI"`` or ``"HSPI"``) representing a pre-defined config. + If not provided, the default flash connection is used. + """ - if args.chip != "esp8266": - image = CHIP_DEFS[args.chip].BOOTLOADER_IMAGE() - if args.chip == "esp32" and args.secure_pad: - image.secure_pad = "1" - if args.secure_pad_v2: - image.secure_pad = "2" - image.min_rev = args.min_rev - image.min_rev_full = args.min_rev_full - image.max_rev_full = args.max_rev_full - image.ram_only_header = args.ram_only_header - if image.ram_only_header: - image.append_digest = False + def _define_spi_conn(spi_connection): + """Prepare SPI configuration string and value for flash_spi_attach()""" + clk, q, d, hd, cs = spi_connection + spi_config_txt = f"CLK:{clk}, Q:{q}, D:{d}, HD:{hd}, CS:{cs}" + value = (hd << 24) | (cs << 18) | (d << 12) | (q << 6) | clk + return spi_config_txt, value + + # Override the common SPI flash parameter stuff if configured to do so + if spi_connection is not None: + spi_config = spi_connection + if spi_connection == "SPI": + value = 0 + elif spi_connection == "HSPI": + value = 1 else: - image.append_digest = args.append_digest - elif args.version == "1": # ESP8266 - image = ESP8266ROMFirmwareImage() - elif args.version == "2": - image = ESP8266V2FirmwareImage() - else: - image = ESP8266V3FirmwareImage() - image.entrypoint = e.entrypoint - image.flash_mode = FLASH_MODES[args.flash_mode] - - if args.flash_mmu_page_size: - image.set_mmu_page_size(flash_size_bytes(args.flash_mmu_page_size)) - - # ELFSection is a subclass of ImageSegment, so can use interchangeably - image.segments = e.segments if args.use_segments else e.sections - if args.pad_to_size: - image.pad_to_size = flash_size_bytes(args.pad_to_size) - image.flash_size_freq = image.ROM_LOADER.parse_flash_size_arg(args.flash_size) - image.flash_size_freq += image.ROM_LOADER.parse_flash_freq_arg(args.flash_freq) - - if args.elf_sha256_offset: - image.elf_sha256 = e.sha256() - image.elf_sha256_offset = args.elf_sha256_offset - - if args.ram_only_header: - print( - "Image has only RAM segments visible. " - "ROM segments are hidden and SHA256 digest is not appended." + esp.check_spi_connection(spi_connection) + # Encode the pin numbers as a 32-bit integer with packed 6-bit values, + # the same way the ESP ROM takes them + spi_config, value = _define_spi_conn(spi_connection) + log.print(f"Configuring SPI flash mode ({spi_config})...") + esp.flash_spi_attach(value) + elif not esp.IS_STUB: + if esp.CHIP_NAME != "ESP32" or esp.secure_download_mode: + log.print("Enabling default SPI flash mode...") + # ROM loader doesn't enable flash unless we explicitly do it + esp.flash_spi_attach(0) + else: + # ROM doesn't attach in-package flash chips + spi_chip_pads = esp.get_chip_spi_pads() + spi_config_txt, value = _define_spi_conn(spi_chip_pads) + if spi_chip_pads != (0, 0, 0, 0, 0): + log.print( + "Attaching flash from eFuses' SPI pads configuration " + f"({spi_config_txt})..." + ) + else: + log.print("Enabling default SPI flash mode...") + esp.flash_spi_attach(value) + + def is_xmc_chip_strict(): + # Read ID without cache, because it should be different after the XMC startup + vendor_id, device_id, _ = _get_flash_info(esp, False) + if vendor_id != XMC_VENDOR_ID: + return False + + mfid = (device_id >> 8) & 0xFF + cpid = device_id & 0xFF + + matched = False + if mfid == 0x40: + if cpid >= 0x13 and cpid <= 0x20: + matched = True + elif mfid == 0x41: + if cpid >= 0x17 and cpid <= 0x20: + matched = True + elif mfid == 0x50: + if cpid >= 0x15 and cpid <= 0x16: + matched = True + return matched + + def flash_xmc_startup(): + # If the RDID value is a valid XMC one, may skip the flow + fast_check = True + if fast_check and is_xmc_chip_strict(): + return # Successful XMC flash chip boot-up detected by RDID, skipping. + + sfdp_mfid_addr = 0x10 + mf_id = esp.read_spiflash_sfdp(sfdp_mfid_addr, 8) + if mf_id != XMC_VENDOR_ID: # Non-XMC chip detected by SFDP Read, skipping. + return + + log.warning( + "XMC flash chip boot-up failure detected! Running XMC25QHxxC startup flow." ) - image.sort_segments() - - before = len(image.segments) - image.merge_adjacent_segments() - if len(image.segments) != before: - delta = before - len(image.segments) - print("Merged %d ELF section%s" % (delta, "s" if delta > 1 else "")) + esp.run_spiflash_command(0xB9) # Enter DPD + esp.run_spiflash_command(0x79) # Enter UDPD + esp.run_spiflash_command(0xFF) # Exit UDPD + time.sleep(0.002) # Delay tXUDPD + esp.run_spiflash_command(0xAB) # Release Power-Down + time.sleep(0.00002) + # Check for success + if not is_xmc_chip_strict(): + log.warning("XMC flash boot-up fix failed.") + log.print("XMC flash chip boot-up fix successful!") + + # Check if XMC SPI flash chip booted-up successfully, fix if not + if not esp.secure_download_mode: + try: + flash_xmc_startup() + except FatalError as e: + esp.trace(f"Unable to perform XMC flash chip startup sequence ({e}).") - image.verify() + # Check flash chip connection + if not esp.secure_download_mode: + try: + flash_id = esp.flash_id() + if flash_id in (0xFFFFFF, 0x000000, 0xFFFF3F): + log.warning( + "Failed to communicate with the flash chip, " + "read/write operations will fail. " + "Try checking the chip connections or removing " + "any other hardware connected to IOs." + ) + if spi_connection is not None: + log.note( + "Some GPIO pins might be used by other peripherals, try using " + "another combination of pins for SPI flash connection." + ) - if args.output is None: - args.output = image.default_output_name(args.input) - image.save(args.output) + except FatalError as e: + raise FatalError(f"Unable to verify flash chip connection ({e}).") - print("Successfully created {} image.".format(args.chip)) +def _set_flash_parameters(esp, flash_size="keep"): + """ + Configure the ESP device's flash memory parameters based on the selected flash size. + + Must be called after attach_flash() and before any flash read/write operations. + It supports three modes of operation based on the flash_size argument: + - "detect": Automatically detects the flash size + (with a fallback to 4MB if detection fails) + - "keep": Leaves the flash parameters unchanged in the image header, + but configures the SPI flash chip with its detected size (if possible) + - Explicit size (e.g., "4MB", "8MB", etc.): Directly uses the specified flash size + + Args: + esp (ESPLoader): Initiated esp object connected to a real device. + flash_size (str, optional): The flash size setting to use. Can be "detect", + "keep", or an explicit flash size value + (default: "keep"). + + Returns: + str | Any: Returns "keep" if flash_size was "keep", or the flash size value + used for configuration. In "detect" mode, this is the auto-detected + flash size (or "4MB" as a fallback). + """ -def read_mac(esp, args): - def print_mac(label, mac): - print("%s: %s" % (label, ":".join(map(lambda x: "%02x" % x, mac)))) + log.print("Configuring flash size...") + keep = flash_size == "keep" - eui64 = esp.read_mac("EUI64") - if eui64: - print_mac("MAC", eui64) - print_mac("BASE MAC", esp.read_mac("BASE_MAC")) - print_mac("MAC_EXT", esp.read_mac("MAC_EXT")) - else: - print_mac("MAC", esp.read_mac("BASE_MAC")) + # Determine flash size + if flash_size == "detect": + flash_size = detect_flash_size(esp) + if flash_size is None: + log.warning("Could not auto-detect flash size, defaulting to 4MB.") + flash_size = "4MB" + else: + log.print(f"Auto-detected flash size: {flash_size}") + elif flash_size == "keep": + # Set flash size will not change in image header, + # but the flash chip should be configured with the real size if possible + flash_size = None if esp.secure_download_mode else detect_flash_size(esp) + if not esp.IS_STUB: + log.note("In case of failure, please set a specific flash size.") + + # Set flash parameters + if flash_size is not None: # Not "keep" in secure download mode + esp.flash_set_parameters(flash_size_bytes(flash_size)) + # Check if stub/ROM supports chosen flash size + if ( + not (esp.IS_STUB and esp.CHIP_NAME in ["ESP32-S3", "ESP32-P4"]) + and flash_size_bytes(flash_size) > 16 * 1024 * 1024 + ): + log.note( + "Flash sizes larger than than 16MB are not fully supported. " + "Change the flash size argument in case of a failure." + ) + return "keep" if keep else flash_size -def chip_id(esp, args): - try: - chipid = esp.chip_id() - print("Chip ID: 0x%08x" % chipid) - except NotSupportedError: - print("Warning: %s has no Chip ID. Reading MAC instead." % esp.CHIP_NAME) - read_mac(esp, args) +def erase_flash(esp: ESPLoader, force: bool = False) -> None: + """ + Erase the SPI flash memory of the ESP device. -def erase_flash(esp, args): - if not args.force and esp.CHIP_NAME != "ESP8266" and not esp.secure_download_mode: + Args: + esp: Initiated esp object connected to a real device. + force: Bypass the security checks for flash encryption and secure boot. + """ + if not force and esp.CHIP_NAME != "ESP8266" and not esp.secure_download_mode: if esp.get_flash_encryption_enabled() or esp.get_secure_boot_enabled(): raise FatalError( "Active security features detected, " "erasing flash is disabled as a safety measure. " - "Use --force to override, " + "Use the force argument to override, " "please use with caution, otherwise it may brick your device!" ) - print("Erasing flash (this may take a while)...") + log.stage() + log.print("Erasing flash memory (this may take a while)...") + if esp.CHIP_NAME != "ESP8266" and not esp.IS_STUB: + log.note( + "You can use the erase-region command in ROM bootloader " + "mode to erase a specific region." + ) t = time.time() esp.erase_flash() - print("Chip erase completed successfully in %.1fs" % (time.time() - t)) + log.stage(finish=True) + log.print(f"Flash memory erased successfully in {time.time() - t:.1f} seconds.") + +def erase_region(esp: ESPLoader, address: int, size: int, force: bool = False) -> None: + """ + Erase a specific region of the SPI flash memory of the ESP device. -def erase_region(esp, args): - if not args.force and esp.CHIP_NAME != "ESP8266" and not esp.secure_download_mode: + Args: + esp: Initiated esp object connected to a real device. + address: The starting address from which to begin erasing. + size: The total number of bytes to erase. + force: Bypass the security checks for flash encryption and secure boot. + """ + if address % ESPLoader.FLASH_SECTOR_SIZE != 0: + raise FatalError( + f"Offset to erase from must be a multiple of {ESPLoader.FLASH_SECTOR_SIZE}." + ) + if size % ESPLoader.FLASH_SECTOR_SIZE != 0: + raise FatalError( + "Size of data to erase must be a multiple of " + f"{ESPLoader.FLASH_SECTOR_SIZE}." + ) + if not force and esp.CHIP_NAME != "ESP8266" and not esp.secure_download_mode: if esp.get_flash_encryption_enabled() or esp.get_secure_boot_enabled(): raise FatalError( "Active security features detected, " "erasing flash is disabled as a safety measure. " - "Use --force to override, " + "Use the force argument to override, " "please use with caution, otherwise it may brick your device!" ) - print("Erasing region (may be slow depending on size)...") + log.stage() + log.print( + "Erasing flash memory region (this may take a while depending on size)..." + ) t = time.time() - esp.erase_region(args.address, args.size) - print("Erase completed successfully in %.1f seconds." % (time.time() - t)) + if esp.CHIP_NAME != "ESP8266" and not esp.IS_STUB: + # flash_begin triggers a flash erase, enabling erasing in ROM and SDM + esp.flash_begin(size, address, logging=False) + else: + esp.erase_region(address, size) + log.stage(finish=True) + log.print( + f"Flash memory region erased successfully in {time.time() - t:.1f} seconds." + ) + +def run(esp: ESPLoader) -> None: + """ + Execute the firmware loaded on the ESP device. -def run(esp, args): + Args: + esp: Initiated esp object connected to a real device. + """ esp.run() -def detect_flash_id(esp): - flash_id = esp.flash_id() - print("Manufacturer: %02x" % (flash_id & 0xFF)) - flid_lowbyte = (flash_id >> 16) & 0xFF - print("Device: %02x%02x" % ((flash_id >> 8) & 0xFF, flid_lowbyte)) - print( - "Detected flash size: %s" % (DETECTED_FLASH_SIZES.get(flid_lowbyte, "Unknown")) - ) +def print_flash_id(esp: ESPLoader) -> None: + """ + Read and display the SPI flash memory chip ID and related information. + + Args: + esp: Initiated esp object connected to a real device. + """ + manufacturer_id, device_id, flash_size = _get_flash_info(esp) + log.print(f"Manufacturer: {manufacturer_id:02x}") + log.print(f"Device: {device_id:04x}") + log.print(f"Detected flash size: {flash_size or 'Unknown'}") -def flash_id(esp, args): - detect_flash_id(esp) +def flash_id(esp: ESPLoader) -> None: + """ + Read and display the SPI flash memory chip identification and configuration details, + such as the manufacturer ID, device ID, detected flash size, type, and voltage. + + Args: + esp: Initiated esp object connected to a real device. + """ + title = "Flash Memory Information:" + log.print(title) + log.print("=" * len(title)) + print_flash_id(esp) flash_type = esp.flash_type() flash_type_dict = {0: "quad (4 data lines)", 1: "octal (8 data lines)"} flash_type_str = flash_type_dict.get(flash_type) if flash_type_str: - print(f"Flash type set in eFuse: {flash_type_str}") - esp.get_flash_voltage() + log.print(f"Flash type set in eFuse: {flash_type_str}") + try: + esp.get_flash_voltage() + except NotSupportedError: + pass # Ignore if not supported -def read_flash_sfdp(esp, args): - detect_flash_id(esp) +def read_flash_sfdp(esp: ESPLoader, address: int, bytes: int = 1) -> None: + """ + Read and display the Serial Flash Discoverable Parameters (SFDP) + from the flash memory. - sfdp = esp.read_spiflash_sfdp(args.addr, args.bytes * 8) - print(f"SFDP[{args.addr}..{args.addr+args.bytes-1}]: ", end="") - for i in range(args.bytes): - print(f"{sfdp&0xff:02X} ", end="") + Args: + esp: Initiated esp object connected to a real device. + address: Starting address in the SFDP region to read from. + bytes: Number of bytes to read (1-4). + """ + if not (1 <= bytes <= 4): + raise FatalError("Invalid number of bytes to read from SFDP (1-4).") + print_flash_id(esp) + sfdp = esp.read_spiflash_sfdp(address, bytes * 8) + log.print(f"Flash memory SFDP[{address}..{address + bytes - 1}]: ", end="") + for _ in range(bytes): + log.print(f"{sfdp & 0xFF:#04x} ", end="") sfdp = sfdp >> 8 - print() + log.print() -def read_flash(esp, args): - if args.no_progress: +def read_flash( + esp: ESPLoader, + address: int, + size: int, + output: str | None = None, + flash_size: str = "keep", + no_progress: bool = False, +) -> bytes | None: + """ + Read a specified region of SPI flash memory of an ESP device + and optionally save it to a file. + + Args: + esp: Initiated esp object connected to a real device. + address: The starting address in flash memory to read from. + size: The number of bytes to read. + output: The name of the file to save the read data. + If None, the function returns the data. + flash_size: Flash size setting, needs to be set only when + the stub flasher is disabled. + Options: ``"detect"``: auto-detect flash size with fallback to 4MB, + ``"keep"``: auto-detect but skip setting parameters in SDM, + Explicit size: use the specified flash size. + no_progress: Disable printing progress. + + Returns: + The read flash data as bytes if output is None; otherwise, + returns None after writing to file. + """ + _set_flash_parameters(esp, flash_size) + if no_progress: flash_progress = None else: - def flash_progress(progress, length): - msg = "%d (%d %%)" % (progress, progress * 100.0 / length) - padding = "\b" * len(msg) - if progress == length: - padding = "\n" - sys.stdout.write(msg + padding) - sys.stdout.flush() + def flash_progress(progress, length, offset): + log.progress_bar( + cur_iter=progress, + total_iters=length, + prefix=f"Reading from {offset + progress:#010x} ", + suffix=f" {progress}/{length} bytes...", + ) + log.stage() t = time.time() - data = esp.read_flash(args.address, args.size, flash_progress) + data = esp.read_flash(address, size, flash_progress) t = time.time() - t speed_msg = " ({:.1f} kbit/s)".format(len(data) / t * 8 / 1000) if t > 0.0 else "" - print_overwrite( - "Read {:d} bytes at {:#010x} in {:.1f} seconds{}...".format( - len(data), args.address, t, speed_msg - ), - last_line=True, + dest_msg = f" to '{output}'" if output else "" + log.stage(finish=True) + log.print( + f"Read {len(data)} bytes from {address:#010x} in {t:.1f} seconds" + f"{speed_msg}{dest_msg}." ) - with open(args.filename, "wb") as f: - f.write(data) + if output: + with open(output, "wb") as f: + f.write(data) + return None + else: + return data -def verify_flash(esp, args): - differences = False +def verify_flash( + esp: ESPLoader, + addr_data: list[tuple[int, ImageSource]], + flash_freq: str = "keep", + flash_mode: str = "keep", + flash_size: str = "keep", + diff: bool = False, +) -> None: + """ + Verify the contents of the SPI flash memory against the provided binary files + or byte data. + + Args: + esp: Initiated esp object connected to a real device. + addr_data: List of (address, data) tuples specifying what + parts of flash memory to verify. The data can be + a file path (str), bytes, or a file-like object. + flash_freq: Flash frequency setting (``"keep"`` to retain current). + flash_mode: Flash mode setting (``"keep"`` to retain current). + flash_size: Flash size setting (``"keep"`` to retain current). + diff: If True, perform a byte-by-byte comparison on failure. + """ + flash_size = _set_flash_parameters(esp, flash_size) # Set flash size parameters + mismatch = False - for address, argfile in args.addr_filename: - image = pad_to(argfile.read(), 4) - argfile.seek(0) # rewind in case we need it again + for address, data in addr_data: + data, source = get_bytes(data) + image = pad_to(data, 4) - image = _update_image_flash_params(esp, address, args, image) + image = _update_image_flash_params( + esp, address, flash_freq, flash_mode, flash_size, image + ) image_size = len(image) - print( - "Verifying 0x%x (%d) bytes @ 0x%08x in flash against %s..." - % (image_size, image_size, address, argfile.name) + source = "input bytes" if source is None else f"'{source}'" + log.print( + f"Verifying {image_size:#x} ({image_size}) bytes " + f"at {address:#010x} in flash against {source}..." ) # Try digest first, only read if there are differences. digest = esp.flash_md5sum(address, image_size) expected_digest = hashlib.md5(image).hexdigest() if digest == expected_digest: - print("-- verify OK (digest matched)") + log.print("Verification successful (digest matched).") continue else: - differences = True - if getattr(args, "diff", "no") != "yes": - print("-- verify FAILED (digest mismatch)") + mismatch = True + if not diff: + log.print("Verification failed (digest mismatch).") continue flash = esp.read_flash(address, image_size) assert flash != image - diff = [i for i in range(image_size) if flash[i] != image[i]] - print( - "-- verify FAILED: %d differences, first @ 0x%08x" - % (len(diff), address + diff[0]) + differences = [i for i in range(image_size) if flash[i] != image[i]] + log.print( + f"Verification failed: {len(differences)} differences, " + f"first at {address + differences[0]:#010x}:" ) - for d in diff: + for d in differences: flash_byte = flash[d] image_byte = image[d] - print(" %08x %02x %02x" % (address + d, flash_byte, image_byte)) - if differences: - raise FatalError("Verify failed.") - - -def read_flash_status(esp, args): - print("Status value: 0x%04x" % esp.read_status(args.bytes)) - - -def write_flash_status(esp, args): - fmt = "0x%%0%dx" % (args.bytes * 2) - args.value = args.value & ((1 << (args.bytes * 8)) - 1) - print(("Initial flash status: " + fmt) % esp.read_status(args.bytes)) - print(("Setting flash status: " + fmt) % args.value) - esp.write_status(args.value, args.bytes, args.non_volatile) - print(("After flash status: " + fmt) % esp.read_status(args.bytes)) - - -# The following mapping was taken from the ROM code -# This mapping is same across all targets in the ROM -SECURITY_INFO_FLAG_MAP = { - "SECURE_BOOT_EN": (1 << 0), - "SECURE_BOOT_AGGRESSIVE_REVOKE": (1 << 1), - "SECURE_DOWNLOAD_ENABLE": (1 << 2), - "SECURE_BOOT_KEY_REVOKE0": (1 << 3), - "SECURE_BOOT_KEY_REVOKE1": (1 << 4), - "SECURE_BOOT_KEY_REVOKE2": (1 << 5), - "SOFT_DIS_JTAG": (1 << 6), - "HARD_DIS_JTAG": (1 << 7), - "DIS_USB": (1 << 8), - "DIS_DOWNLOAD_DCACHE": (1 << 9), - "DIS_DOWNLOAD_ICACHE": (1 << 10), -} + log.print(f" {address + d:#010x} {flash_byte:02x} {image_byte:02x}") + if mismatch: + raise FatalError("Verification failed.") -# Get the status of respective security flag -def get_security_flag_status(flag_name, flags_value): - try: - return (flags_value & SECURITY_INFO_FLAG_MAP[flag_name]) != 0 - except KeyError: - raise ValueError(f"Invalid flag name: {flag_name}") +def read_flash_status(esp: ESPLoader, bytes: int = 2) -> None: + """ + Read and print the status register value of the SPI flash memory. + + Args: + esp: Initiated esp object connected to a real device. + bytes: Number of bytes to read. + """ + log.print(f"Flash memory status: {esp.read_status(bytes):#06x}") -def get_security_info(esp, args): +def write_flash_status( + esp: ESPLoader, value: int, bytes: int = 2, non_volatile: bool = False +) -> None: + """ + Write a new value to the SPI flash memory status register and verify the update. + + Args: + esp: Initiated esp object connected to a real device. + value: The new status register value to write. + bytes: Number of bytes to write. + non_volatile: If True, allows non-volatile status register bits + to be written. + """ + fmt = f"0x%0{bytes * 2}x" + value = value & ((1 << (bytes * 8)) - 1) + log.print(f"Initial flash memory status: {fmt % esp.read_status(bytes)}") + log.print(f"Setting flash memory status: {fmt % value}") + esp.write_status(value, bytes, non_volatile) + log.print(f"After flash memory status: {fmt % esp.read_status(bytes)}") + + +def get_security_info(esp: ESPLoader) -> None: + """ + Read and display security-related information about the ESP device. + + Args: + esp: Initiated esp object connected to a real device. + """ si = esp.get_security_info() - print() + parsed_flags = si["parsed_flags"] + title = "Security Information:" - print(title) - print("=" * len(title)) - print("Flags: {:#010x} ({})".format(si["flags"], bin(si["flags"]))) + log.print(title) + log.print("=" * len(title)) + log.print("Flags: {:#010x} ({})".format(si["flags"], bin(si["flags"]))) if esp.KEY_PURPOSES: - print(f"Key Purposes: {si['key_purposes']}") + log.print(f"Key Purposes: {si['key_purposes']}") desc = "\n ".join( [ f"BLOCK_KEY{key_num} - {esp.KEY_PURPOSES.get(purpose, 'UNKNOWN')}" @@ -1328,17 +1511,15 @@ def get_security_info(esp, args): if key_num <= esp.EFUSE_MAX_KEY ] ) - print(f" {desc}") + log.print(f" {desc}") if si["chip_id"] is not None and si["api_version"] is not None: - print("Chip ID: {}".format(si["chip_id"])) - print("API Version: {}".format(si["api_version"])) + log.print("Chip ID: {}".format(si["chip_id"])) + log.print("API Version: {}".format(si["api_version"])) - flags = si["flags"] - - if get_security_flag_status("SECURE_BOOT_EN", flags): - print("Secure Boot: Enabled") - if get_security_flag_status("SECURE_BOOT_AGGRESSIVE_REVOKE", flags): - print("Secure Boot Aggressive key revocation: Enabled") + if parsed_flags["SECURE_BOOT_EN"]: + log.print("Secure Boot: Enabled") + if parsed_flags["SECURE_BOOT_AGGRESSIVE_REVOKE"]: + log.print("Secure Boot Aggressive key revocation: Enabled") revoked_keys = [] for i, key in enumerate( @@ -1348,127 +1529,817 @@ def get_security_info(esp, args): "SECURE_BOOT_KEY_REVOKE2", ] ): - if get_security_flag_status(key, flags): + if parsed_flags[key]: revoked_keys.append(i) if len(revoked_keys) > 0: - print("Secure Boot Key Revocation Status:\n") + log.print("Secure Boot Key Revocation Status:\n") for i in revoked_keys: - print(f"\tSecure Boot Key{i} is Revoked\n") + log.print(f"\tSecure Boot Key{i} is Revoked\n") else: - print("Secure Boot: Disabled") + log.print("Secure Boot: Disabled") flash_crypt_cnt = bin(si["flash_crypt_cnt"]) if (flash_crypt_cnt.count("1") % 2) != 0: - print("Flash Encryption: Enabled") + log.print("Flash Encryption: Enabled") else: - print("Flash Encryption: Disabled") + log.print("Flash Encryption: Disabled") CRYPT_CNT_STRING = "SPI Boot Crypt Count (SPI_BOOT_CRYPT_CNT)" if esp.CHIP_NAME == "esp32": CRYPT_CNT_STRING = "Flash Crypt Count (FLASH_CRYPT_CNT)" - print(f"{CRYPT_CNT_STRING}: {si['flash_crypt_cnt']:#x}") + log.print(f"{CRYPT_CNT_STRING}: {si['flash_crypt_cnt']:#x}") - if get_security_flag_status("DIS_DOWNLOAD_DCACHE", flags): - print("Dcache in UART download mode: Disabled") + if parsed_flags["DIS_DOWNLOAD_DCACHE"]: + log.print("Dcache in UART download mode: Disabled") - if get_security_flag_status("DIS_DOWNLOAD_ICACHE", flags): - print("Icache in UART download mode: Disabled") + if parsed_flags["DIS_DOWNLOAD_ICACHE"]: + log.print("Icache in UART download mode: Disabled") - hard_dis_jtag = get_security_flag_status("HARD_DIS_JTAG", flags) - soft_dis_jtag = get_security_flag_status("SOFT_DIS_JTAG", flags) + hard_dis_jtag = parsed_flags["HARD_DIS_JTAG"] + soft_dis_jtag = parsed_flags["SOFT_DIS_JTAG"] if hard_dis_jtag: - print("JTAG: Permanently Disabled") + log.print("JTAG: Permanently Disabled") elif soft_dis_jtag: - print("JTAG: Software Access Disabled") - if get_security_flag_status("DIS_USB", flags): - print("USB Access: Disabled") + log.print("JTAG: Software Access Disabled") + if parsed_flags["DIS_USB"]: + log.print("USB Access: Disabled") + + +def reset_chip(esp: ESPLoader, reset_mode: str = "hard-reset") -> None: + """ + Reset the ESP device. + + Args: + esp: Initiated esp object connected to a real device. + reset_mode: Reset mode to use ( + ``"hard-reset"``: perform a hard reset using the RTS control line, + ``"soft-reset"``: perform a soft reset, + ``"no-reset"``: stay in bootloader, + ``"no-reset-stub"``: stay in flasher stub, + ``"watchdog-reset"``: perform a hard reset utilizing a software watchdog. + ) + + """ + if reset_mode == "hard-reset": + esp.hard_reset() + elif reset_mode == "soft-reset": + log.print("Soft resetting...") + # flash_finish will trigger a soft reset + esp.soft_reset(False) + elif reset_mode == "no-reset-stub": + log.print("Staying in flasher stub.") + elif reset_mode == "watchdog-reset": + if esp.secure_download_mode: + log.warning( + "Watchdog hard reset is not supported in Secure Download Mode, " + "attempting classic hard reset instead." + ) + esp.hard_reset() + else: + esp.watchdog_reset() + elif reset_mode == "no-reset": + log.print("Staying in bootloader.") + if esp.IS_STUB: + esp.soft_reset(True) # Exit the stub flasher back to ROM loader + else: + raise FatalError(f"Invalid reset mode: {reset_mode}") + + +def run_stub(esp: ESPLoader) -> ESPLoader: + """ + Load and execute the stub loader on the ESP device. If stub loading + is not supported or is explicitly disabled, warnings are logged. + + Args: + esp: Initiated esp object connected to a real device. + + Returns: + The esp instance, either as a stub child class in a state + where the stub has been executed, or in its original state + if the stub loader is disabled or unsupported. + """ + if esp.secure_download_mode: + log.warning( + "Stub flasher is not supported in Secure Download Mode, " + "it has been disabled. Set --no-stub to suppress this warning." + ) + elif not esp.IS_STUB and esp.stub_is_disabled: + log.warning( + "Stub flasher has been disabled for compatibility, " + "set --no-stub to suppress this warning." + ) + elif esp.CHIP_NAME in [ + "ESP32-H21", + "ESP32-H4", + ]: # TODO: [ESP32H21] IDF-11509 [ESP32H4] IDF-12271 + log.warning( + f"Stub flasher is not yet supported on {esp.CHIP_NAME}, " + "it has been disabled. Set --no-stub to suppress this warning." + ) + else: + try: + return esp.run_stub() + except Exception: + # The CH9102 bridge (PID: 0x55D4) can have issues on MacOS + if sys.platform == "darwin" and esp._get_pid() == 0x55D4: + log.print() + log.note( + "If issues persist, " + "try installing the WCH USB-to-Serial MacOS driver." + ) + raise + return esp + + +# Commands that don't require an ESP object (image manipulation, etc.) +###################################################################### + + +def _parse_app_info(app_info_segment): + """ + Check if correct magic word is present in the app_info and parse the app_info struct + """ + app_info = app_info_segment[:256] + # More info about the app_info struct can be found at: + # https://docs.espressif.com/projects/esp-idf/en/stable/esp32/api-reference/system/app_image_format.html#application-description + APP_DESC_STRUCT_FMT = " None: + """ + Display detailed information about an ESP firmware image. + + Args: + input: Path to the firmware image file, opened file-like object, + or the image data as bytes. If a list of tuples is provided, + each tuple contains an offset and an image data as bytes. Used for + merged binary images. + chip: Target ESP device type (e.g., ``"esp32"``). If None, the chip + type will be automatically detected from the image header. + """ + if isinstance(input, list): + log.print("Merged binary image detected. Processing each file individually.") + for i, file in enumerate(input): + data, _ = get_bytes(file[1]) + + offset_str = hex(file[0]) if file[0] is not None else "unknown" + line = ( + f"Processing file {i + 1}/{len(input)}, " + f"offset: {offset_str}, size: {len(data)} bytes" + ) + log.print() + log.print("=" * len(line)) + log.print(line) + log.print("=" * len(line)) + + try: + detected_chip = _parse_image_info_header(data, chip) + except Exception as e: + log.error(f"Error processing file {i + 1}/{len(input)}: {e}") + log.error("Probably not a valid firmware image (e.g. partition table).") + continue + + if ( + i == 0 and chip is None + ): # We don't need to print the image type for each file + log.print(f"Detected image type: {detected_chip.upper()}") + chip = detected_chip + _print_image_info(detected_chip, data) + + else: + data, _ = get_bytes(input) + detected_chip = _parse_image_info_header(data, chip) + + log.print(f"Image size: {len(data)} bytes") + if chip is None: + log.print(f"Detected image type: {detected_chip.upper()}") + + _print_image_info(detected_chip, data) + +def _parse_image_info_header(data: bytes, chip: str | None = None) -> str: + """Parse the image info header and return the chip type.""" + stream = io.BytesIO(data) + common_header = stream.read(8) + if chip is None: + extended_header = stream.read(16) + stream.seek(0) -def merge_bin(args): + # Check magic number try: - chip_class = CHIP_DEFS[args.chip] + magic = common_header[0] + except IndexError: + raise FatalError("Image is empty.") + if magic not in [ + ESPLoader.ESP_IMAGE_MAGIC, + ESP8266V2FirmwareImage.IMAGE_V2_MAGIC, + ]: + raise FatalError( + f"This is not a valid image (invalid magic number: {magic:#x})." + ) + + if chip is None: + try: + # append_digest, either 0 or 1 + if extended_header[-1] not in [0, 1]: + raise FatalError("Append digest field not 0 or 1.") + + chip_id = int.from_bytes(extended_header[4:5], "little") + for rom in ROM_LIST: + if chip_id == rom.IMAGE_CHIP_ID: + chip = rom.CHIP_NAME + break + else: + raise FatalError(f"Unknown image chip ID ({chip_id}).") + except FatalError: + chip = "esp8266" + + return chip + + +def _print_image_info(chip: str, data: bytes) -> None: + image = LoadFirmwareImage(chip, data) + + log.print() + title = f"{chip.upper()} Image Header" + log.print(title) + log.print("=" * len(title)) + log.print(f"Image version: {image.version}") + log.print( + f"Entry point: {image.entrypoint:#8x}" + if image.entrypoint != 0 + else "Entry point not set" + ) + + log.print(f"Segments: {len(image.segments)}") + + # Flash size + flash_s_bits = image.flash_size_freq & 0xF0 # high four bits + flash_s = get_key_from_value(image.ROM_LOADER.FLASH_SIZES, flash_s_bits) + if flash_s is not None: + log.print(f"Flash size: {flash_s}") + else: + log.warning(f"Invalid flash size ({flash_s_bits:#02x})") + + # Flash frequency + flash_fr_bits = image.flash_size_freq & 0x0F # low four bits + flash_fr = get_key_from_value(image.ROM_LOADER.FLASH_FREQUENCY, flash_fr_bits) + if flash_fr is not None: + log.print(f"Flash freq: {flash_fr}") + else: + log.warning(f"Invalid flash frequency ({flash_fr_bits:#02x})") + + # Flash mode + flash_mode = get_key_from_value(FLASH_MODES, image.flash_mode) + if flash_mode is not None: + log.print(f"Flash mode: {flash_mode.upper()}") + else: + log.warning(f"Invalid flash mode ({image.flash_mode})") + + # Extended header (ESP32 and later only) + if chip != "esp8266": + log.print() + title = f"{chip.upper()} Extended Image Header" + log.print(title) + log.print("=" * len(title)) + log.print( + f"WP pin: {image.wp_pin:#02x}", + *["(disabled)"] if image.wp_pin == image.WP_PIN_DISABLED else [], + ) + log.print( + "Flash pins drive settings: " + "clk_drv: {:#02x}, q_drv: {:#02x}, d_drv: {:#02x}, " + "cs0_drv: {:#02x}, hd_drv: {:#02x}, wp_drv: {:#02x}".format( + image.clk_drv, + image.q_drv, + image.d_drv, + image.cs_drv, + image.hd_drv, + image.wp_drv, + ) + ) + try: + chip_class = next( + chip + for chip in CHIP_DEFS.values() + if getattr(chip, "IMAGE_CHIP_ID", None) == image.chip_id + ) + log.print(f"Chip ID: {image.chip_id} ({chip_class.CHIP_NAME})") + except StopIteration: + log.print(f"Chip ID: {image.chip_id} (Unknown ID)") + log.print( + "Minimal chip revision: " + f"v{image.min_rev_full // 100}.{image.min_rev_full % 100}, " + f"(legacy min_rev = {image.min_rev})" + ) + log.print( + "Maximal chip revision: " + f"v{image.max_rev_full // 100}.{image.max_rev_full % 100}" + ) + log.print() + + # Segments overview + title = "Segments Information" + log.print(title) + log.print("=" * len(title)) + headers_str = "{:>7} {:>7} {:>10} {:>10} {:10}" + log.print( + headers_str.format( + "Segment", "Length", "Load addr", "File offs", "Memory types" + ) + ) + log.print(f"{'-' * 7} {'-' * 7} {'-' * 10} {'-' * 10} {'-' * 12}") + format_str = "{:7} {:#07x} {:#010x} {:#010x} {}" + app_desc_seg = None + bootloader_desc_seg = None + for idx, seg in enumerate(image.segments): + segs = seg.get_memory_type(image) + seg_name = ", ".join(segs) + # The DROM segment starts with the esp_app_desc_t struct + if "DROM" in segs and app_desc_seg is None: + app_desc_seg = seg.data + elif "DRAM" in segs: + # The DRAM segment starts with the esp_bootloader_desc_t struct + if len(seg.data) >= 80: + bootloader_desc_seg = seg.data + log.print( + format_str.format(idx, len(seg.data), seg.addr, seg.file_offs, seg_name) + ) + log.print() + + # Footer + title = f"{chip.upper()} Image Footer" + log.print(title) + log.print("=" * len(title)) + calc_checksum = image.calculate_checksum() + log.print( + "Checksum: {:#04x} ({})".format( + image.checksum, + ( + "valid" + if image.checksum == calc_checksum + else f"invalid - calculated {calc_checksum:#04x}" + ), + ) + ) + try: + digest_msg = "Not appended" + if image.append_digest: + is_valid = image.stored_digest == image.calc_digest + digest_msg = "{} ({})".format( + hexify(image.calc_digest, uppercase=False), + "valid" if is_valid else "invalid", + ) + log.print(f"Validation hash: {digest_msg}") + except AttributeError: + pass # ESP8266 image has no append_digest field + + if app_desc_seg: + app_desc = _parse_app_info(app_desc_seg) + if app_desc: + log.print() + title = "Application Information" + log.print(title) + log.print("=" * len(title)) + log.print(f"Project name: {app_desc['project_name']}") + log.print(f"App version: {app_desc['version']}") + log.print(f"Compile time: {app_desc['date']} {app_desc['time']}") + log.print(f"ELF file SHA256: {app_desc['app_elf_sha256']}") + log.print(f"ESP-IDF: {app_desc['idf_ver']}") + log.print( + f"Minimal eFuse block revision: {app_desc['min_efuse_blk_rev_full']}" + ) + log.print( + f"Maximal eFuse block revision: {app_desc['max_efuse_blk_rev_full']}" + ) + if app_desc["mmu_page_size"]: + log.print(f"MMU page size: {app_desc['mmu_page_size']}") + log.print(f"Secure version: {app_desc['secure_version']}") + + elif bootloader_desc_seg: + bootloader_desc = _parse_bootloader_info(bootloader_desc_seg) + if bootloader_desc: + log.print() + title = "Bootloader Information" + log.print(title) + log.print("=" * len(title)) + log.print(f"Bootloader version: {bootloader_desc['version']}") + log.print(f"ESP-IDF: {bootloader_desc['idf_ver']}") + log.print(f"Compile time: {bootloader_desc['date_time']}") + + +def merge_bin( + addr_data: list[tuple[int, ImageSource]], + chip: str, + output: str | None = None, + flash_freq: str = "keep", + flash_mode: str = "keep", + flash_size: str = "keep", + format: str = "raw", + **kwargs, +) -> bytes | None: + """ + Merge multiple binary files into a single output file for flashing to an ESP device. + + Take multiple binary files along with their flash addresses and merge them + into a unified binary in either raw, UF2, or Intel HEX format. + Also apply necessary flash parameters and ensure correct alignment for flashing. + + Args: + addr_data: List of (address, data) tuples specifying where + to write each file or data in flash memory. The data can be + a file path (str), bytes, or a file-like object. + chip: Target ESP device type (e.g., ``"esp32"``). + output: Path to the output file where the merged binary will be written. + If None, the merged binary will be returned as bytes. + flash_freq: Flash frequency to set in the image header + (``"keep"`` to retain current). + flash_mode: Flash mode to set in the image header + (``"keep"`` to retain current). + flash_size: Flash size to set in the image header + (``"keep"`` to retain current). + format: Output format (``"raw"``, ``"uf2"``, or ``"hex"``). + + Keyword Args: + target_offset (int): Starting offset for the merged output. + pad_to_size (str | None): If specified, pad the output to a specific flash size. + chunk_size (int | None): Chunk size for UF2 format. + md5_disable (bool): If True, disable MD5 checks in UF2 format. + + Returns: + The merged binary data as bytes if output is None; otherwise, + returns None after writing to file. + """ + + # Set default values of optional arguments + target_offset: int = kwargs.get("target_offset", 0) + pad_to_size: str | None = kwargs.get("pad_to_size", None) + chunk_size: int | None = kwargs.get("chunk_size", None) + md5_disable: bool = kwargs.get("md5_disable", False) + + if format not in ["raw", "uf2", "hex"]: + raise FatalError( + f"Invalid format: '{format}', choose from 'raw', 'uf2', 'hex'." + ) + + if output is None and format in ["uf2", "hex"]: + raise FatalError(f"Output file must be specified with {format.upper()} format.") + + try: + chip_class = CHIP_DEFS[chip] except KeyError: - msg = ( - "Please specify the chip argument" - if args.chip == "auto" - else f"Invalid chip choice: '{args.chip}'" + raise FatalError( + f"Invalid chip choice: '{chip}' (choose from {', '.join(CHIP_LIST)})." ) - msg = f"{msg} (choose from {', '.join(CHIP_LIST)})" - raise FatalError(msg) # sort the files by offset. # The AddrFilenamePairAction has already checked for overlap - input_files = sorted(args.addr_filename, key=lambda x: x[0]) - if not input_files: - raise FatalError("No input files specified") - first_addr = input_files[0][0] - if first_addr < args.target_offset: + addr_data = sorted(addr_data, key=lambda x: x[0]) + if not addr_data: + raise FatalError("No input data.") + first_addr = addr_data[0][0] + if first_addr < target_offset: raise FatalError( - f"Output file target offset is {args.target_offset:#x}. " - f"Input file offset {first_addr:#x} is before this." + f"Output data target offset is {target_offset:#x}. " + f"Input data offset {first_addr:#x} is before this." ) - if args.format == "uf2": + if output is not None and format == "uf2": with UF2Writer( chip_class.UF2_FAMILY_ID, - args.output, - args.chunk_size, - md5_enabled=not args.md5_disable, + output, + chunk_size, + md5_enabled=not md5_disable, ) as writer: - for addr, argfile in input_files: - print(f"Adding {argfile.name} at {addr:#x}") - image = argfile.read() - image = _update_image_flash_params(chip_class, addr, args, image) + for addr, data in addr_data: + image, source = get_bytes(data) + source = "bytes" if source is None else f"'{source}'" + log.print(f"Adding {source} at {addr:#x}...") + image = _update_image_flash_params( + chip_class, addr, flash_freq, flash_mode, flash_size, image + ) writer.add_file(addr, image) - print( - f"Wrote {os.path.getsize(args.output):#x} bytes to file {args.output}, " - f"ready to be flashed with any ESP USB Bridge" + log.print( + f"Wrote {os.path.getsize(output):#x} bytes to file '{output}', " + f"ready to be flashed with any ESP USB Bridge." ) - elif args.format == "raw": - with open(args.output, "wb") as of: + elif format == "raw": + of = io.BytesIO() if output is None else open(output, "wb") + try: def pad_to(flash_offs): # account for output file offset if there is any - of.write(b"\xff" * (flash_offs - args.target_offset - of.tell())) + of.write(b"\xff" * (flash_offs - target_offset - of.tell())) - for addr, argfile in input_files: + for addr, data in addr_data: pad_to(addr) - image = argfile.read() - image = _update_image_flash_params(chip_class, addr, args, image) + image, _ = get_bytes(data) + image = _update_image_flash_params( + chip_class, addr, flash_freq, flash_mode, flash_size, image + ) of.write(image) - if args.fill_flash_size: - pad_to(flash_size_bytes(args.fill_flash_size)) - print( - f"Wrote {of.tell():#x} bytes to file {args.output}, " - f"ready to flash to offset {args.target_offset:#x}" + if pad_to_size: + pad_to(flash_size_bytes(pad_to_size)) + size = of.tell() + finally: + if output is not None: + of.close() + + if output is None and isinstance(of, io.BytesIO): + log.print( + f"Merged {size:#x} bytes, ready to flash to offset {target_offset:#x}." + ) + return of.getvalue() + else: + log.print( + f"Wrote {size:#x} bytes to file '{output}', " + f"ready to flash to offset {target_offset:#x}." ) - elif args.format == "hex": + return None + + elif output is not None and format == "hex": out = IntelHex() - if len(input_files) == 1: - print( - "WARNING: Only one input file specified, output may include " + if len(addr_data) == 1: + log.warning( + "Only one input file specified, output may include " "additional padding if input file was previously merged. " "Please refer to the documentation for more information: " "https://docs.espressif.com/projects/esptool/en/latest/esptool/basic-commands.html#hex-output-format" # noqa E501 ) - for addr, argfile in input_files: + for addr, data in addr_data: ihex = IntelHex() - image = argfile.read() - image = _update_image_flash_params(chip_class, addr, args, image) + image, _ = get_bytes(data) + image = _update_image_flash_params( + chip_class, addr, flash_freq, flash_mode, flash_size, image + ) ihex.frombytes(image, addr) out.merge(ihex) - out.write_hex_file(args.output) - print( - f"Wrote {os.path.getsize(args.output):#x} bytes to file {args.output}, " - f"ready to flash to offset {args.target_offset:#x}" + out.write_hex_file(output) + log.print( + f"Wrote {os.path.getsize(output):#x} bytes to file '{output}', " + f"ready to flash to offset {target_offset:#x}." ) + return None + + +def elf2image( + input: ImageSource, + chip: str, + output: str | None = None, + flash_freq: str | None = None, + flash_mode: str = "qio", + flash_size: str = "1MB", + **kwargs, +) -> bytes | tuple[bytes | None, bytes] | None: + """ + Convert ELF data into a firmware image suitable for flashing onto an ESP device. + + Args: + input: Path to the ELF file to convert, opened file-like object, + or the ELF data as bytes. + chip: Target ESP device type. + output: Path to save the generated firmware image. If "auto", a default + pre-defined path is used. If None, the image is not written to a file, + but returned as bytes. + flash_freq: Flash frequency to set in the image header. + flash_mode: Flash mode to set in the image header. + flash_size: Flash size to set in the image header. + + Keyword Args: + version (int): ESP8266-only, firmware image version. + min_rev (int): Minimum chip revision required in legacy format. + min_rev_full (int): Minimum chip revision required in extended format. + max_rev_full (int): Maximum chip revision allowed in extended format. + secure_pad (bool): ESP32-only, enable secure padding. + secure_pad_v2 (bool): Enable version 2 secure padding. + elf_sha256_offset (int): Offset for storing the ELF file's SHA-256 hash. + append_digest (bool): Whether to append a digest to the firmware image. + use_segments (bool): Use ELF segments instead of sections. + flash_mmu_page_size (str): MMU page size for flash mapping. + pad_to_size (str): Pad the final image to a specific flash size. + ram_only_header (bool): Include only RAM segments and no SHA-256 hash. + + Returns: + The firmware image as bytes if output is None; otherwise, + returns None after writing to file. + When ESP8266 V1 image is generated, returns a tuple of bytes + of IROM data and the rest if output is None; otherwise, + returns None after writing to two files. + """ + # Set default values of optional arguments + version: int = kwargs.get("version", 1) + min_rev: int = kwargs.get("min_rev", 0) + min_rev_full: int = kwargs.get("min_rev_full", 0) + max_rev_full: int = kwargs.get("max_rev_full", 65535) + secure_pad: bool = kwargs.get("secure_pad", False) + secure_pad_v2: bool = kwargs.get("secure_pad_v2", False) + elf_sha256_offset: int | None = kwargs.get("elf_sha256_offset", None) + append_digest: bool = kwargs.get("append_digest", True) + use_segments: bool = kwargs.get("use_segments", False) + flash_mmu_page_size: str | None = kwargs.get("flash_mmu_page_size", None) + pad_to_size: str | None = kwargs.get("pad_to_size", None) + ram_only_header: bool = kwargs.get("ram_only_header", False) + + if chip not in CHIP_LIST: + raise FatalError( + f"Invalid chip choice: '{chip}' (choose from {', '.join(CHIP_LIST)})." + ) -def version(args): + data, source = get_bytes(input) + e = ELFFile(data) + log.print(f"Creating {chip.upper()} image...") + if chip != "esp8266": + bootloader_image = CHIP_DEFS[chip].BOOTLOADER_IMAGE + if bootloader_image is None: + raise FatalError(f"Missing bootloader image definition for {chip}.") + else: + image = bootloader_image() + if chip == "esp32" and secure_pad: + image.secure_pad = "1" + if secure_pad_v2: + image.secure_pad = "2" + image.min_rev = min_rev + image.min_rev_full = min_rev_full + image.max_rev_full = max_rev_full + image.ram_only_header = ram_only_header + if image.ram_only_header: + image.append_digest = False + else: + image.append_digest = append_digest + elif version == "1": # ESP8266 + image = ESP8266ROMFirmwareImage() + elif version == "2": + image = ESP8266V2FirmwareImage() + else: + image = ESP8266V3FirmwareImage() + image.entrypoint = e.entrypoint + image.flash_mode = FLASH_MODES[flash_mode] + + if flash_mmu_page_size: + image.set_mmu_page_size(flash_size_bytes(flash_mmu_page_size)) + else: + appdesc_seg = None + for seg in e.sections: + if ".flash.appdesc" in seg.name: + appdesc_seg = seg + break + # If ELF file contains an app description segment, which is in the flash memory + # (RAM build has it too, but does not have MMU page size), + # and chip has configurable MMU page size. + if ( + appdesc_seg + and image.is_flash_addr(appdesc_seg.addr) + and image.MMU_PAGE_SIZE_CONF + ): + app_desc = _parse_app_info(appdesc_seg.data) + if app_desc: + # MMU page size is set in app description segment since ESP-IDF v5.4 + if app_desc["mmu_page_size"]: + image.set_mmu_page_size(flash_size_bytes(app_desc["mmu_page_size"])) + # Try to set the correct MMU page size based on the app description + # starting address which, without image + extended header (24 bytes) + # and segment header (8 bytes), should be aligned to MMU page size. + else: + for mmu_page_size in reversed(image.MMU_PAGE_SIZE_CONF): + if (appdesc_seg.addr - 24 - 8) % mmu_page_size == 0: + image.set_mmu_page_size(mmu_page_size) + log.print( + "MMU page size not specified, set to " + f"{image.IROM_ALIGN // 1024} KB" + ) + break + else: + log.warning( + "App description segment is not aligned to MMU page size, " + "probably linker script issue or wrong MMU page size. " + "Try to set MMU page size parameter manually." + ) + + # ELFSection is a subclass of ImageSegment, so can use interchangeably + image.segments = e.segments if use_segments else e.sections + if pad_to_size: + image.pad_to_size = flash_size_bytes(pad_to_size) + image.flash_size_freq = image.ROM_LOADER.parse_flash_size_arg(flash_size) + image.flash_size_freq += image.ROM_LOADER.parse_flash_freq_arg(flash_freq) + + if elf_sha256_offset: + image.elf_sha256 = e.sha256() + image.elf_sha256_offset = elf_sha256_offset + else: + # If ELF file contains an app_desc section and it is in flash, + # put the SHA256 digest at correct offset. + # If it is flash build, it should always be 0xB0. + appdesc_segs = [seg for seg in image.segments if ".flash.appdesc" in seg.name] + if appdesc_segs and image.is_flash_addr(appdesc_segs[0].addr): + image.elf_sha256 = e.sha256() + image.elf_sha256_offset = 0xB0 + + if ram_only_header: + log.print( + "Image has only RAM segments visible. " + "ROM segments are hidden and SHA256 digest is not appended." + ) + image.sort_segments() + + before = len(image.segments) + image.merge_adjacent_segments() + if len(image.segments) != before: + delta = before - len(image.segments) + log.print(f"Merged {delta} ELF section{'s' if delta > 1 else ''}.") + + image.verify() + log.print(f"Successfully created {chip.upper()} image.") + + if output == "auto": + source = f"{chip}_image" if source is None else source + output = image.default_output_name(source) + return cast(bytes | tuple[bytes | None, bytes] | None, image.save(output)) + + +def version() -> None: + """ + Print the current esptool version. + """ from . import __version__ - print(__version__) + log.print(__version__) diff --git a/tools/esptool_py/esptool/config.py b/tools/esptool_py/esptool/config.py index ebbe3b8eed..ebb59389aa 100644 --- a/tools/esptool_py/esptool/config.py +++ b/tools/esptool_py/esptool/config.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2014-2023 Espressif Systems (Shanghai) CO LTD, +# SPDX-FileCopyrightText: 2014-2025 Espressif Systems (Shanghai) CO LTD, # other contributors as noted. # # SPDX-License-Identifier: GPL-2.0-or-later @@ -6,6 +6,8 @@ import configparser import os +from .logger import log + CONFIG_OPTIONS = [ "timeout", "chip_erase_timeout", @@ -21,6 +23,7 @@ "reset_delay", "open_port_attempts", "custom_reset_sequence", + "custom_hard_reset_sequence", ] @@ -39,7 +42,7 @@ def _validate_config_file(file_path, verbose=False): no_of_unknown_opts = len(unknown_opts) if no_of_unknown_opts > 0: suffix = "s" if no_of_unknown_opts > 1 else "" - print( + log.note( "Ignoring unknown config file option{}: {}".format( suffix, ", ".join(unknown_opts) ) @@ -47,7 +50,7 @@ def _validate_config_file(file_path, verbose=False): return True except (UnicodeDecodeError, configparser.Error) as e: if verbose: - print(f"Ignoring invalid config file {file_path}: {e}") + log.note(f"Ignoring invalid config file {file_path}: {e}") return False @@ -86,7 +89,7 @@ def load_config_file(verbose=False): cfg.read(cfg_file_path) if verbose: msg = " (set with ESPTOOL_CFGFILE)" if set_with_env_var else "" - print( + log.print( f"Loaded custom configuration from " f"{os.path.abspath(cfg_file_path)}{msg}" ) diff --git a/tools/esptool_py/esptool/loader.py b/tools/esptool_py/esptool/loader.py index ca3ab9329d..adf59cdff2 100644 --- a/tools/esptool_py/esptool/loader.py +++ b/tools/esptool_py/esptool/loader.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2014-2023 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 @@ -13,10 +13,9 @@ import struct import sys import time -from typing import Optional - from .config import load_config_file +from .logger import log from .reset import ( ClassicReset, CustomReset, @@ -25,15 +24,27 @@ USBJTAGSerialReset, UnixTightReset, ) -from .util import FatalError, NotImplementedInROMError, UnsupportedCommandError -from .util import byte, hexify, mask_to_shift, pad_to, strip_chip_name +from .util import ( + FatalError, + NotImplementedInROMError, + NotSupportedError, + UnsupportedCommandError, +) +from .util import ( + byte, + get_key_from_value, + hexify, + mask_to_shift, + pad_to, + strip_chip_name, +) try: import serial except ImportError: - print( - "Pyserial is not installed for %s. " - "Check the README for installation instructions." % (sys.executable) + log.error( + f"PySerial is not installed for {sys.executable}. " + "Check the documentation for installation instructions." ) raise @@ -42,7 +53,7 @@ try: if "serialization" in serial.__doc__ and "deserialization" in serial.__doc__: raise ImportError( - "esptool.py depends on pyserial, but there is a conflict with a currently " + "esptool depends on pySerial, but there is a conflict with a currently " "installed package named 'serial'.\n" "You may work around this by 'pip uninstall serial; pip install pyserial' " "but this may break other installed Python software " @@ -53,20 +64,20 @@ " for discussion of the underlying issue(s)." ) except TypeError: - pass # __doc__ returns None for pyserial + pass # __doc__ returns None for pySerial try: import serial.tools.list_ports as list_ports except ImportError: - print( - "The installed version (%s) of pyserial appears to be too old for esptool.py " - "(Python interpreter %s). Check the README for installation instructions." - % (serial.VERSION, sys.executable) + log.error( + f"The installed version ({serial.VERSION}) of pySerial appears to be too old " + f"for esptool (Python interpreter {sys.executable}). " + "Check the documentation for installation instructions." ) raise except Exception: if sys.platform == "darwin": - # swallow the exception, this is a known issue in pyserial+macOS Big Sur preview + # swallow the exception, this is a known issue in pySerial+macOS Big Sur preview # ref https://github.com/espressif/esptool/issues/540 list_ports = None else: @@ -90,7 +101,7 @@ ERASE_REGION_TIMEOUT_PER_MB = cfg.getfloat("erase_region_timeout_per_mb", 30) # Timeout (per megabyte) for erasing and writing data ERASE_WRITE_TIMEOUT_PER_MB = cfg.getfloat("erase_write_timeout_per_mb", 40) -# Short timeout for ESP_MEM_END, as it may never respond +# Short timeout for MEM_END, as it may never respond MEM_END_ROM_TIMEOUT = cfg.getfloat("mem_end_rom_timeout", 0.2) # Timeout for serial port write DEFAULT_SERIAL_WRITE_TIMEOUT = cfg.getfloat("serial_write_timeout", 10) @@ -143,17 +154,10 @@ def stub_and_esp32_function_only(func): ) -def esp32s3_or_newer_function_only(func): - """Attribute for a function only supported by ESP32S3 and later chips ROM""" - return check_supported_function( - func, lambda o: o.CHIP_NAME not in ["ESP8266", "ESP32", "ESP32-S2"] - ) - - class StubFlasher: STUB_DIR = os.path.join(os.path.dirname(__file__), "targets", "stub_flasher") # directories will be searched in the order of STUB_SUBDIRS - STUB_SUBDIRS = ["1", "2"] + STUB_SUBDIRS = ["1"] def __init__(self, chip_name): with open(self.get_json_path(chip_name)) as json_file: @@ -178,19 +182,20 @@ def get_json_path(self, chip_name): json_path = os.path.join(self.STUB_DIR, subdir, f"{chip_name}.json") if os.path.exists(json_path): if i: - print( - f"Warning: Stub version {self.STUB_SUBDIRS[0]} doesn't exist, using {subdir} instead" + log.warning( + f"Stub version {self.STUB_SUBDIRS[0]} doesn't exist, " + f"using {subdir} instead" ) return json_path else: - raise FileNotFoundError(f"Stub flasher JSON file for {chip_name} not found") + raise FileNotFoundError( + f"Stub flasher JSON file for {chip_name} not found." + ) @classmethod - def set_preferred_stub_subdir(cls, subdir): - if subdir in cls.STUB_SUBDIRS: - cls.STUB_SUBDIRS.remove(subdir) - cls.STUB_SUBDIRS.insert(0, subdir) + def set_stub_subdir(cls, subdir): + cls.STUB_SUBDIRS = [subdir] class ESPLoader(object): @@ -206,45 +211,50 @@ class ESPLoader(object): CHIP_NAME = "Espressif device" IS_STUB = False - STUB_CLASS: Optional[object] = None - BOOTLOADER_IMAGE: Optional[object] = None + STUB_CLASS: type["ESPLoader"] | None = None + BOOTLOADER_IMAGE: type | None = None + IMAGE_CHIP_ID: int | None = None + + # Chip uses magic number for chip type autodetection + USES_MAGIC_VALUE = True + MAGIC_VALUE: int | None = None + + UF2_FAMILY_ID: int = 0x0 DEFAULT_PORT = "/dev/ttyUSB0" USES_RFC2217 = False - # Commands supported by ESP8266 ROM bootloader - ESP_FLASH_BEGIN = 0x02 - ESP_FLASH_DATA = 0x03 - ESP_FLASH_END = 0x04 - ESP_MEM_BEGIN = 0x05 - ESP_MEM_END = 0x06 - ESP_MEM_DATA = 0x07 - ESP_SYNC = 0x08 - ESP_WRITE_REG = 0x09 - ESP_READ_REG = 0x0A - - # Some commands supported by ESP32 and later chips ROM bootloader (or -8266 w/ stub) - ESP_SPI_SET_PARAMS = 0x0B - ESP_SPI_ATTACH = 0x0D - ESP_READ_FLASH_SLOW = 0x0E # ROM only, much slower than the stub flash read - ESP_CHANGE_BAUDRATE = 0x0F - ESP_FLASH_DEFL_BEGIN = 0x10 - ESP_FLASH_DEFL_DATA = 0x11 - ESP_FLASH_DEFL_END = 0x12 - ESP_SPI_FLASH_MD5 = 0x13 - - # Commands supported by ESP32-S2 and later chips ROM bootloader only - ESP_GET_SECURITY_INFO = 0x14 - - # Some commands supported by stub only - ESP_ERASE_FLASH = 0xD0 - ESP_ERASE_REGION = 0xD1 - ESP_READ_FLASH = 0xD2 - ESP_RUN_USER_CODE = 0xD3 - - # Flash encryption encrypted data command - ESP_FLASH_ENCRYPT_DATA = 0xD4 + ESP_CMDS = { + # Commands supported by ESP8266 ROM bootloader + "FLASH_BEGIN": 0x02, + "FLASH_DATA": 0x03, + "FLASH_END": 0x04, + "MEM_BEGIN": 0x05, + "MEM_END": 0x06, + "MEM_DATA": 0x07, + "SYNC": 0x08, + "WRITE_REG": 0x09, + "READ_REG": 0x0A, + # Commands supported by ESP32 and later chips ROM bootloader (or -8266 w/ stub) + "SPI_SET_PARAMS": 0x0B, + "SPI_ATTACH": 0x0D, + "READ_FLASH_SLOW": 0x0E, # ROM only, much slower than the stub flash read + "CHANGE_BAUDRATE": 0x0F, + "FLASH_DEFL_BEGIN": 0x10, + "FLASH_DEFL_DATA": 0x11, + "FLASH_DEFL_END": 0x12, + "SPI_FLASH_MD5": 0x13, + # Commands supported by ESP32-S2 and later chips ROM bootloader only + "GET_SECURITY_INFO": 0x14, + # Some commands supported by stub only + "ERASE_FLASH": 0xD0, + "ERASE_REGION": 0xD1, + "READ_FLASH": 0xD2, + "RUN_USER_CODE": 0xD3, + # Flash encryption encrypted data command + "FLASH_ENCRYPT_DATA": 0xD4, + } # Response code(s) sent by ROM ROM_INVALID_RECV_MSG = 0x05 # response if an invalid message is received @@ -268,7 +278,8 @@ class ESPLoader(object): UART_DATE_REG_ADDR = 0x60000078 - # Whether the SPI peripheral sends from MSB of 32-bit register, or the MSB of valid LSB bits. + # Whether the SPI peripheral sends from MSB of 32-bit register, or the MSB of valid + # LSB bits. SPI_ADDR_REG_MSB = True # This ROM address has a different value on each chip model @@ -280,16 +291,13 @@ class ESPLoader(object): IROM_MAP_START = 0x40200000 IROM_MAP_END = 0x40300000 - # The number of bytes in the UART response that signify command status - STATUS_BYTES_LENGTH = 2 - # Bootloader flashing offset BOOTLOADER_FLASH_OFFSET = 0x0 # ROM supports an encrypted flashing mode SUPPORTS_ENCRYPTED_FLASH = False - # Response to ESP_SYNC might indicate that flasher stub is running + # Response to SYNC might indicate that flasher stub is running # instead of the ROM bootloader sync_stub_detected = False @@ -297,11 +305,22 @@ class ESPLoader(object): USB_JTAG_SERIAL_PID = 0x1001 # Chip IDs that are no longer supported by esptool - UNSUPPORTED_CHIPS = {6: "ESP32-S3(beta 3)"} + UNSUPPORTED_CHIPS = { + 4: "ESP32-S3(beta2)", + 6: "ESP32-S3(beta3)", + 7: "ESP32-C6(beta)", + 10: "ESP32-H2(beta1)", + 14: "ESP32-H2(beta2)", + 17: "ESP32-C5(beta3)", + } # Number of attempts to write flash data WRITE_FLASH_ATTEMPTS = 2 + FLASH_ENCRYPTED_WRITE_ALIGN = 16 + KEY_PURPOSES: dict[int, str] = {} + EFUSE_MAX_KEY = 5 + def __init__(self, port=DEFAULT_PORT, baud=ESP_ROM_BAUD, trace_enabled=False): """Base constructor for ESPLoader bootloader interaction @@ -323,9 +342,9 @@ def __init__(self, port=DEFAULT_PORT, baud=ESP_ROM_BAUD, trace_enabled=False): # Device-and-runtime-specific cache self.cache = { "flash_id": None, - "chip_id": None, "uart_no": None, "usb_pid": None, + "security_info": None, } if isinstance(port, str): @@ -403,8 +422,7 @@ def _set_port_baudrate(self, baud): self._port.baudrate = baud except IOError: raise FatalError( - "Failed to set baud rate %d. The driver may not support this rate." - % baud + f"Failed to set baud rate {baud}. The driver may not support this rate." ) def read(self): @@ -418,10 +436,10 @@ def write(self, packet): + (packet.replace(b"\xdb", b"\xdb\xdd").replace(b"\xc0", b"\xdb\xdc")) + b"\xc0" ) - self.trace("Write %d bytes: %s", len(buf), HexFormatter(buf)) + self.trace(f"{f'Write {len(buf)} bytes:':<21} {HexFormatter(buf)}") self._port.write(buf) - def trace(self, message, *format_args): + def trace(self, message, newline=False): if self._trace_enabled: now = time.time() try: @@ -429,8 +447,8 @@ def trace(self, message, *format_args): except AttributeError: delta = 0.0 self._last_trace = now - prefix = "TRACE +%.3f " % delta - print(prefix + (message % format_args)) + prefix = f" TRACE +{delta:.3f} " + log.print("\n" if newline else "", f"{prefix} {message}") @staticmethod def checksum(data, state=ESP_CHECKSUM_MAGIC): @@ -457,13 +475,10 @@ def command( try: if op is not None: self.trace( - "command op=0x%02x data len=%s wait_response=%d " - "timeout=%.3f data=%s", - op, - len(data), - 1 if wait_response else 0, - timeout, - HexFormatter(data), + f"--- Cmd {get_key_from_value(self.ESP_CMDS, op)} ({op:#04x}) | " + f"data_len {len(data)} | wait_response {1 if wait_response else 0}" + f" | timeout {timeout:.3f} | data {HexFormatter(data)} ---", + newline=True, ) pkt = struct.pack(b" self.STATUS_BYTES_LENGTH: - return data[: -self.STATUS_BYTES_LENGTH] + # Execute the command and get the result + val, data = self.command(op, data, chk, timeout=timeout) + + # Check if we have enough data, + # including the expected response data and status bytes + if len(data) < resp_data_len + STATUS_BYTES_LENGTH: + # If we don't have enough data, check the first 2 bytes as status + status_bytes = data[0:2] + # Only care if the first byte is non-zero. + # If it is, the second byte is a reason. + if status_bytes[0] != 0: + raise FatalError.WithResult(f"Failed to {op_description}", status_bytes) + else: + raise FatalError( + f"Failed to {op_description}. " + f"Only got {len(data)} byte status response." + ) + # The status bytes are positioned after the expected response data + # (first two bytes after resp_data_len are the status bytes) + status_bytes = data[resp_data_len : resp_data_len + STATUS_BYTES_LENGTH] + # Only care if the first byte is non-zero. + # If it is, the second byte is a reason. + if status_bytes[0] != 0: + raise FatalError.WithResult(f"Failed to {op_description}", status_bytes) + + if resp_data_len > 0: + return data[:resp_data_len] else: - # otherwise, just return the 'val' field which comes from the reply header - # (this is used by read_reg) return val def flush_input(self): @@ -537,7 +571,9 @@ def flush_input(self): def sync(self): val, _ = self.command( - self.ESP_SYNC, b"\x07\x07\x12\x20" + 32 * b"\x55", timeout=SYNC_TIMEOUT + self.ESP_CMDS["SYNC"], + b"\x07\x07\x12\x20" + 32 * b"\x55", + timeout=SYNC_TIMEOUT, ) # ROM bootloaders send some non-zero "val" response. The flasher stub sends 0. @@ -554,7 +590,7 @@ def _get_pid(self): return self.cache["usb_pid"] if list_ports is None: - print( + log.print( "\nListing all serial ports is currently not available. " "Can't get device PID." ) @@ -563,7 +599,7 @@ def _get_pid(self): # Pyserial only identifies regular ports, URL handlers are not supported if not active_port.lower().startswith(("com", "/dev/")): - print( + log.print( "\nDevice PID identification is only supported on " "COM and /dev/ serial ports." ) @@ -582,12 +618,12 @@ def _get_pid(self): if p.device in active_ports: self.cache["usb_pid"] = p.pid return p.pid - print( + log.print( f"\nFailed to get PID of a device on {active_port}, " "using standard reset sequence." ) - def _connect_attempt(self, reset_strategy, mode="default_reset"): + def _connect_attempt(self, reset_strategy, mode="default-reset"): """A single connection attempt""" last_error = None boot_log_detected = False @@ -595,10 +631,10 @@ def _connect_attempt(self, reset_strategy, mode="default_reset"): # If we're doing no_sync, we're likely communicating as a pass through # with an intermediate device to the ESP32 - if mode == "no_reset_no_sync": + if mode == "no-reset-no-sync": return last_error - if mode != "no_reset": + if mode != "no-reset": if not self.USES_RFC2217: # Might block on rfc2217 ports # Empty serial buffer to isolate boot log self._port.reset_input_buffer() @@ -623,8 +659,7 @@ def _connect_attempt(self, reset_strategy, mode="default_reset"): self.sync() return None except FatalError as e: - print(".", end="") - sys.stdout.flush() + log.print(".", end="", flush=True) time.sleep(0.05) last_error = e @@ -677,7 +712,7 @@ def _construct_reset_strategy_sequence(self, mode): delay = extra_delay = 7 # USB-JTAG/Serial mode - if mode == "usb_reset" or self._get_pid() == self.USB_JTAG_SERIAL_PID: + if mode == "usb-reset" or self._get_pid() == self.USB_JTAG_SERIAL_PID: return (USBJTAGSerialReset(self._port),) # USB-to-Serial bridge @@ -696,29 +731,28 @@ def _construct_reset_strategy_sequence(self, mode): def connect( self, - mode="default_reset", + mode="default-reset", attempts=DEFAULT_CONNECT_ATTEMPTS, detecting=False, warnings=True, ): """Try connecting repeatedly until successful, or giving up""" - if warnings and mode in ["no_reset", "no_reset_no_sync"]: - print( - 'WARNING: Pre-connection option "{}" was selected.'.format(mode), + if warnings and mode in ["no-reset", "no-reset-no-sync"]: + log.note( + f'Pre-connection option "{mode}" was selected. ' "Connection may fail if the chip is not in bootloader " - "or flasher stub mode.", + "or flasher stub mode." ) if self._port.name.startswith("socket:"): - mode = "no_reset" # not possible to toggle DTR/RTS over a TCP socket - print( - "Note: It's not possible to reset the chip over a TCP socket. " + mode = "no-reset" # not possible to toggle DTR/RTS over a TCP socket + log.note( + "It's not possible to reset the chip over a TCP socket. " "Automatic resetting to bootloader has been disabled, " "reset the chip manually." ) - print("Connecting...", end="") - sys.stdout.flush() + log.print("Connecting...", end="", flush=True) last_error = None reset_sequence = self._construct_reset_strategy_sequence(mode) @@ -731,17 +765,17 @@ def connect( if last_error is None: break finally: - print("") # end 'Connecting...' line + log.print("") # end 'Connecting...' line if last_error is not None: additional_msg = "" if self.CHIP_NAME == "ESP32-C2" and self._port.baudrate < 115200: additional_msg = ( - "\nNote: Please set a higher baud rate (--baud)" + "\nNote: Please set a higher baud rate" " if ESP32-C2 doesn't connect" " (at least 115200 Bd is recommended)." ) - + self._port.close() raise FatalError( "Failed to connect to {}: {}" f"{additional_msg}" @@ -752,41 +786,80 @@ def connect( ) if not detecting: - try: - from .targets import ROM_LIST - - # check the date code registers match what we expect to see - chip_magic_value = self.read_reg(ESPLoader.CHIP_DETECT_MAGIC_REG_ADDR) - if chip_magic_value not in self.CHIP_DETECT_MAGIC_VALUE: - actually = None - for cls in ROM_LIST: - if chip_magic_value in cls.CHIP_DETECT_MAGIC_VALUE: - actually = cls - break - if warnings and actually is None: - print( - "WARNING: This chip doesn't appear to be a %s " - "(chip magic value 0x%08x). " - "Probably it is unsupported by this version of esptool." - % (self.CHIP_NAME, chip_magic_value) - ) - else: - raise FatalError( - "This chip is %s not %s. Wrong --chip argument?" - % (actually.CHIP_NAME, self.CHIP_NAME) - ) - except UnsupportedCommandError: - self.secure_download_mode = True + from .targets import ROM_LIST + # Check if chip supports reading chip ID from the get-security-info command try: - self.check_chip_id() - except UnsupportedCommandError: - # Fix for ROM not responding in SDM, reconnect and try again - if self.secure_download_mode: - self._connect_attempt(mode, reset_sequence[0]) - self.check_chip_id() + # get_chip_id() raises FatalError if the chip does not have a chip ID + # (ESP32-S2) + chip_id = self.get_chip_id() + si = self.get_security_info() + self.secure_download_mode = si["parsed_flags"]["SECURE_DOWNLOAD_ENABLE"] + except (UnsupportedCommandError, FatalError): + chip_id = None + # Try to read the chip magic value to verify the chip type + # (ESP8266, ESP32, ESP32-S2) + try: + chip_magic_value = self.read_reg( + ESPLoader.CHIP_DETECT_MAGIC_REG_ADDR + ) + except UnsupportedCommandError: + # If the chip does not support reading the chip magic value, + # it is ESP32-S2 in SDM + chip_magic_value = None + self.secure_download_mode = True + + detected = None + chip_arg_wrong = False + + # If we can read chip ID (ESP32-S3 and later), verify the ID + if chip_id and (self.USES_MAGIC_VALUE or chip_id != self.IMAGE_CHIP_ID): + chip_arg_wrong = True + for cls in ROM_LIST: + if not cls.USES_MAGIC_VALUE and chip_id == cls.IMAGE_CHIP_ID: + detected = cls + break + # If we can't read chip ID (ESP8266, ESP32, ESP32-S2), + # try to verify the chip by magic value + elif ( + not chip_id + and not self.secure_download_mode + and (not self.USES_MAGIC_VALUE or chip_magic_value != self.MAGIC_VALUE) + ): + chip_arg_wrong = True + for cls in ROM_LIST: + if cls.USES_MAGIC_VALUE and chip_magic_value == cls.MAGIC_VALUE: + detected = cls + break + # If we can't read chip ID and the chip is in SDM, it is ESP32-S2 + elif ( + not chip_id + and self.secure_download_mode + and self.CHIP_NAME != "ESP32-S2" + ): + chip_arg_wrong = True + detected = "ESP32-S2" + + if chip_arg_wrong: + if warnings and detected is None: + specifier = ( + f"(read chip ID {chip_id})" + if chip_id + else f"(read chip magic value {chip_magic_value:#08x})" + ) + log.warning( + f"This chip doesn't appear to be an {self.CHIP_NAME} " + f"{specifier}. Probably it is unsupported by this version " + "of esptool. Will attempt to continue anyway." + ) else: - raise + chip_type = ( + detected if isinstance(detected, str) else detected.CHIP_NAME + ) + raise FatalError( + f"This chip is {chip_type}, not {self.CHIP_NAME}. " + "Wrong chip argument?" + ) self._post_connect() def _post_connect(self): @@ -798,17 +871,10 @@ def _post_connect(self): def read_reg(self, addr, timeout=DEFAULT_TIMEOUT): """Read memory address in target""" - # we don't call check_command here because read_reg() function is called - # when detecting chip type, and the way we check for success - # (STATUS_BYTES_LENGTH) is different for different chip types (!) - val, data = self.command( - self.ESP_READ_REG, struct.pack(" stub_start: raise FatalError( - "Software loader is resident at 0x%08x-0x%08x. " - "Can't load binary at overlapping address range 0x%08x-0x%08x. " - "Either change binary loading address, or use the --no-stub " - "option to disable the software loader." - % (stub_start, stub_end, load_start, load_end) + "Stub flasher is resident at " + f"{stub_start:#010x}-{stub_end:#010x}. " + "Can't load binary at overlapping address range " + f"{load_start:#010x}-{load_end:#010x}. Either change binary " + "loading address, or disable the stub flasher." ) return self.check_command( "enter RAM download mode", - self.ESP_MEM_BEGIN, + self.ESP_CMDS["MEM_BEGIN"], struct.pack(" "ESPLoader": + log.stage() if stub is None: stub = StubFlasher(self.CHIP_NAME) if self.sync_stub_detected: - print("Stub is already running. No upload is necessary.") - return self.STUB_CLASS(self) + log.stage(finish=True) + log.print("Stub flasher is already running. No upload is necessary.") + return self.STUB_CLASS(self) if self.STUB_CLASS is not None else self # Upload - print("Uploading stub...") + log.print("Uploading stub flasher...") for field in [stub.text, stub.data]: if field is not None: offs = stub.text_start if field == stub.text else stub.data_start @@ -1066,21 +1271,22 @@ def run_stub(self, stub=None): from_offs = seq * self.ESP_RAM_BLOCK to_offs = from_offs + self.ESP_RAM_BLOCK self.mem_block(field[from_offs:to_offs], seq) - print("Running stub...") + log.print("Running stub flasher...") self.mem_finish(stub.entry) try: p = self.read() except StopIteration: raise FatalError( - "Failed to start stub. There was no response." + "Failed to start stub flasher. There was no response." "\nTry increasing timeouts, for more information see: " "https://docs.espressif.com/projects/esptool/en/latest/esptool/configuration-file.html" # noqa E501 ) if p != b"OHAI": - raise FatalError(f"Failed to start stub. Unexpected response: {p}") - print("Stub running...") - return self.STUB_CLASS(self) + raise FatalError(f"Failed to start stub flasher. Unexpected response: {p}") + log.stage(finish=True) + log.print("Stub flasher running.") + return self.STUB_CLASS(self) if self.STUB_CLASS is not None else self @stub_and_esp32_function_only def flash_defl_begin(self, size, compsize, offset): @@ -1105,7 +1311,7 @@ def flash_defl_begin(self, size, compsize, offset): timeout = timeout_per_mb( ERASE_REGION_TIMEOUT_PER_MB, write_size ) # ROM performs the erase up front - print("Compressed %d bytes to %d..." % (size, compsize)) + log.print(f"Compressed {size} bytes to {compsize}...") params = struct.pack( " bytes: raise NotImplementedInROMError(self, self.read_flash_slow) - def read_flash(self, offset, length, progress_fn=None): + def read_flash(self, offset, length, progress_fn=None) -> bytes: if not self.IS_STUB: return self.read_flash_slow(offset, length, progress_fn) # ROM-only routine # issue a standard bootloader command to trigger the read self.check_command( "read flash", - self.ESP_READ_FLASH, + self.ESP_CMDS["READ_FLASH"], struct.pack(" length: - raise FatalError("Read more than expected") + raise FatalError("Read more than expected.") digest_frame = self.read() if len(digest_frame) != 16: - raise FatalError("Expected digest, got: %s" % hexify(digest_frame)) + raise FatalError(f"Expected digest, got: {hexify(digest_frame)}") expected_digest = hexify(digest_frame).upper() digest = hashlib.md5(data).hexdigest().upper() if digest != expected_digest: raise FatalError( - "Digest mismatch: expected %s, got %s" % (expected_digest, digest) + f"Digest mismatch: expected {expected_digest}, got {digest}" ) return data @@ -1256,15 +1463,15 @@ def flash_spi_attach(self, hspi_arg): ESP8266 ROM does this when you send flash_begin, ESP32 ROM has it as a SPI command. """ - # last 3 bytes in ESP_SPI_ATTACH argument are reserved values + # last 3 bytes in SPI_ATTACH argument are reserved values arg = struct.pack(" 64: raise FatalError( - "Writing more than 64 bytes of data with one SPI " - "command is unsupported" + "Writing more than 64 bytes of data with one SPI command is unsupported" ) data_bits = len(data) * 8 @@ -1518,16 +1724,20 @@ def get_crystal_freq(self): else: norm_xtal = 26 if abs(norm_xtal - est_xtal) > 1: - print( - "WARNING: Detected crystal freq %.2fMHz is quite different to " - "normalized freq %dMHz. Unsupported crystal in use?" - % (est_xtal, norm_xtal) + log.warning( + f"Detected crystal freq {est_xtal:.2f} MHz is quite different to " + f"normalized freq {norm_xtal} MHz. Unsupported crystal in use?" ) + return norm_xtal - def hard_reset(self): - print("Hard resetting via RTS pin...") - HardReset(self._port)() + def hard_reset(self, uses_usb=False): + log.print("Hard resetting via RTS pin...") + cfg_custom_hard_reset_sequence = cfg.get("custom_hard_reset_sequence") + if cfg_custom_hard_reset_sequence is not None: + CustomReset(self._port, cfg_custom_hard_reset_sequence)() + else: + HardReset(self._port, uses_usb)() def soft_reset(self, stay_in_bootloader): if not self.IS_STUB: @@ -1550,24 +1760,33 @@ def soft_reset(self, stay_in_bootloader): else: # running user code from stub loader requires some hacks # in the stub loader - self.command(self.ESP_RUN_USER_CODE, wait_response=False) + self.command(self.ESP_CMDS["RUN_USER_CODE"], wait_response=False) - def check_chip_id(self): - try: - chip_id = self.get_chip_id() - if chip_id != self.IMAGE_CHIP_ID: - print( - "WARNING: Chip ID {} ({}) doesn't match expected Chip ID {}. " - "esptool may not work correctly.".format( - chip_id, - self.UNSUPPORTED_CHIPS.get(chip_id, "Unknown"), - self.IMAGE_CHIP_ID, - ) - ) - # Try to flash anyways by disabling stub - self.stub_is_disabled = True - except NotImplementedInROMError: - pass + def watchdog_reset(self): + log.note( + f"Watchdog hard reset is not supported on {self.CHIP_NAME}, " + "attempting classic hard reset instead." + ) + self.hard_reset() + + +class StubMixin: + """ + Mixin class bundling the stub loader-specific properties. + Not intended for direct instantiation. + A child class (e.g. ESP32StubLoader) uses multiple inheritance + to combine this mixin class (StubMixin) with a parent class (e.g. ESP32ROM). + """ + + FLASH_WRITE_SIZE = 0x4000 # Default value, can be overridden + 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 def slip_reader(port, trace_function): @@ -1604,7 +1823,7 @@ def detect_panic_handler(input): if i is not None ] cause = f" {cause[0]}" if len(cause) else "" - msg = f"Guru Meditation Error detected{cause}" + msg = f"Guru Meditation Error detected{cause}." raise FatalError(msg) partial_packet = None @@ -1621,28 +1840,29 @@ def detect_panic_handler(input): else "No serial data received." ) else: # fail during packet transfer - msg = "Packet content transfer stopped (received {} bytes)".format( - len(partial_packet) - ) + msg = "Packet content transfer stopped " + f"(received {len(partial_packet)} bytes)." trace_function(msg) raise FatalError(msg) - trace_function("Read %d bytes: %s", len(read_bytes), HexFormatter(read_bytes)) + trace_function( + f"{f'Read {len(read_bytes)} bytes:':<21} {HexFormatter(read_bytes)}" + ) for b in read_bytes: b = bytes([b]) if partial_packet is None: # waiting for packet header if b == b"\xc0": partial_packet = b"" else: - trace_function("Read invalid data: %s", HexFormatter(read_bytes)) + trace_function(f"Read invalid data: {HexFormatter(read_bytes)}") remaining_data = port.read(port.inWaiting()) trace_function( - "Remaining data in serial buffer: %s", - HexFormatter(remaining_data), + "Remaining data in serial buffer: " + f"{HexFormatter(remaining_data)}", ) detect_panic_handler(read_bytes + remaining_data) raise FatalError( - "Invalid head of packet (0x%s): " - "Possible serial noise or corruption." % hexify(b) + f"Invalid head of packet (0x{hexify(b)}): " + "Possible serial noise or corruption." ) elif in_escape: # part-way through escape sequence in_escape = False @@ -1651,18 +1871,18 @@ def detect_panic_handler(input): elif b == b"\xdd": partial_packet += b"\xdb" else: - trace_function("Read invalid data: %s", HexFormatter(read_bytes)) + trace_function(f"Read invalid data: {HexFormatter(read_bytes)}") remaining_data = port.read(port.inWaiting()) trace_function( - "Remaining data in serial buffer: %s", - HexFormatter(remaining_data), + "Remaining data in serial buffer: " + f"{HexFormatter(remaining_data)}" ) detect_panic_handler(read_bytes + remaining_data) - raise FatalError("Invalid SLIP escape (0xdb, 0x%s)" % (hexify(b))) + raise FatalError(f"Invalid SLIP escape (0xdb, 0x{hexify(b)}).") elif b == b"\xdb": # start of escape sequence in_escape = True elif b == b"\xc0": # end of packet - trace_function("Received full packet: %s", HexFormatter(partial_packet)) + trace_function(f"Received full packet: {HexFormatter(partial_packet)}") yield partial_packet partial_packet = None successful_slip = True diff --git a/tools/esptool_py/esptool/logger.py b/tools/esptool_py/esptool/logger.py new file mode 100644 index 0000000000..b4ea21545e --- /dev/null +++ b/tools/esptool_py/esptool/logger.py @@ -0,0 +1,273 @@ +# SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD +# +# SPDX-License-Identifier: GPL-2.0-or-later + +from abc import ABC, abstractmethod +import sys +import os + + +class TemplateLogger(ABC): + @abstractmethod + def print(self, *args, **kwargs): + """ + Log a plain message. + """ + pass + + @abstractmethod + def note(self, message: str): + """ + Log a Note: message. + """ + pass + + @abstractmethod + def warning(self, message: str): + """ + Log a Warning: message. + """ + pass + + @abstractmethod + def error(self, message: str): + """ + Log an error message. + """ + pass + + @abstractmethod + def stage(self, finish: bool = False): + """ + Start or finish a new collapsible stage. + """ + pass + + @abstractmethod + def progress_bar( + self, + cur_iter: int, + total_iters: int, + prefix: str = "", + suffix: str = "", + bar_length: int = 30, + ): + """ + Print a progress bar. + """ + pass + + @abstractmethod + def set_verbosity(self, verbosity: str): + """ + Set the verbosity level. + """ + pass + + +class EsptoolLogger(TemplateLogger): + ansi_red: str = "" + ansi_yellow: str = "" + ansi_blue: str = "" + ansi_normal: str = "" + ansi_clear: str = "" + ansi_line_up: str = "" + ansi_line_clear: str = "" + + _stage_active: bool = False + _newline_count: int = 0 + _kept_lines: list[str] = [] + + _smart_features: bool = False + _verbosity: str | None = None + _print_anyway: bool = False + + def __new__(cls): + """ + Singleton to ensure only one instance of the logger exists. + """ + if not hasattr(cls, "instance"): + cls.instance = super(EsptoolLogger, cls).__new__(cls) + cls.instance.set_verbosity("auto") + return cls.instance + + @classmethod + def _del(cls) -> None: + if hasattr(cls, "instance"): + del cls.instance + + @classmethod + def _set_smart_features(cls, override: bool | None = None): + # Check for smart terminal and color support + if override is not None: + cls.instance._smart_features = override + else: + is_tty = hasattr(sys.stdout, "isatty") and sys.stdout.isatty() + term_supports_color = os.getenv("TERM", "").lower() in ( + "xterm", + "xterm-256color", + "screen", + "screen-256color", + "linux", + "vt100", + ) + no_color = os.getenv("NO_COLOR", "").strip().lower() in ("1", "true", "yes") + + # Determine if colors should be enabled + cls.instance._smart_features = ( + is_tty or term_supports_color and not no_color + ) + # Handle Windows specifically + if sys.platform == "win32" and cls.instance._smart_features: + try: + from colorama import init + + init() # Enable ANSI support on Windows + except ImportError: + cls.instance._smart_features = False + + if cls.instance._smart_features: + cls.instance.ansi_red = "\033[1;31m" + cls.instance.ansi_yellow = "\033[0;33m" + cls.instance.ansi_blue = "\033[1;36m" + cls.instance.ansi_normal = "\033[0m" + cls.instance.ansi_clear = "\033[K" + cls.instance.ansi_line_up = "\033[1A" + cls.instance.ansi_line_clear = "\x1b[2K" + else: + cls.instance.ansi_red = "" + cls.instance.ansi_yellow = "" + cls.instance.ansi_blue = "" + cls.instance.ansi_normal = "" + cls.instance.ansi_clear = "" + cls.instance.ansi_line_up = "" + cls.instance.ansi_line_clear = "" + + def print(self, *args, **kwargs): + """ + Log a plain message. Count newlines if in a collapsing stage. + """ + if self._verbosity == "silent" and not self._print_anyway: + return + if self._stage_active: + # Count the number of newlines in the message + message = "".join(map(str, args)) + self._newline_count += message.count("\n") + if kwargs.get("end", "\n") == "\n": + self._newline_count += 1 + print(*args, **kwargs) + self._print_anyway = False + + def note(self, message: str): + """ + Log a Note: message in blue and white. + """ + formatted_message = f"{self.ansi_blue}Note:{self.ansi_normal} {message}" + if self._stage_active: + self._kept_lines.append(formatted_message) + self.print(formatted_message) + + def warning(self, message: str): + """ + Log a Warning: message in yellow and white. + """ + formatted_message = f"{self.ansi_yellow}Warning:{self.ansi_normal} {message}" + if self._stage_active: + self._kept_lines.append(formatted_message) + self.print(formatted_message) + + def error(self, message: str): + """ + Log an error message in red to stderr. + """ + formatted_message = f"{self.ansi_red}{message}{self.ansi_normal}" + self._print_anyway = True + self.print(formatted_message, file=sys.stderr) + + def stage(self, finish: bool = False): + """ + Start or finish a collapsible stage. + Any log messages printed between the start and finish will be deleted + when the stage is successfully finished. + Warnings and notes will be saved and printed at the end of the stage. + If terminal doesn't support ANSI escape codes, no collapsing happens. + """ + if finish: + if not self._stage_active: + return + # Deactivate stage to stop collecting input + self._stage_active = False + + if self._smart_features: + # Delete printed lines + self.print( + f"{self.ansi_line_up}{self.ansi_line_clear}" + * (self._newline_count), + end="", + flush=True, + ) + # Print saved warnings and notes + for line in self._kept_lines: + self.print(line) + + # Clean the buffers for next stage + self._kept_lines.clear() + self._newline_count = 0 + else: + self._stage_active = True + + def progress_bar( + self, + cur_iter: int, + total_iters: int, + prefix: str = "", + suffix: str = "", + bar_length: int = 30, + ): + """ + Call in a loop to print a progress bar overwriting itself in place. + If terminal doesn't support ANSI escape codes, no overwriting happens. + """ + filled = int(bar_length * cur_iter // total_iters) + if filled == bar_length: + bar = "=" * bar_length + elif filled == 0: + bar = " " * bar_length + else: + bar = f"{'=' * (filled - 1)}>{' ' * (bar_length - filled)}" + + percent = f"{100 * (cur_iter / float(total_iters)):.1f}" + self.print( + f"\r{self.ansi_clear}{prefix}[{bar}] {percent:>5}%{suffix} ", + end="\n" if not self._smart_features or cur_iter == total_iters else "", + flush=True, + ) + + def set_logger(self, new_logger): + self.__class__ = new_logger.__class__ + + def set_verbosity(self, verbosity: str): + """ + Set the verbosity level to one of the following: + - "auto": Enable smart terminal features and colors if supported by the terminal + - "verbose": Enable verbose output (no collapsing output) + - "silent": Disable all output except errors + - "compact": Enable smart terminal features and colors even if not supported + """ + if verbosity == self._verbosity: + return + + self._verbosity = verbosity + if verbosity == "auto": + self._set_smart_features() + elif verbosity == "verbose": + self._set_smart_features(override=False) + elif verbosity == "silent": + pass + elif verbosity == "compact": + self._set_smart_features(override=True) + else: + raise ValueError(f"Invalid verbosity level: {verbosity}") + + +log = EsptoolLogger() diff --git a/tools/esptool_py/esptool/reset.py b/tools/esptool_py/esptool/reset.py index ef91e4bdc1..2a782e7b71 100644 --- a/tools/esptool_py/esptool/reset.py +++ b/tools/esptool_py/esptool/reset.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2014-2023 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 @@ -8,6 +8,7 @@ import struct import time +from .logger import log from .util import FatalError, PrintOnce # Used for resetting into bootloader on Unix-like systems @@ -27,7 +28,7 @@ class ResetStrategy(object): - print_once = PrintOnce() + print_once = PrintOnce(log.warning) def __init__(self, port, reset_delay=DEFAULT_RESET_DELAY): self.port = port @@ -50,10 +51,10 @@ def __call__(self): # ENOTTY for TIOCMSET; EINVAL for TIOCMGET if e.errno in [errno.ENOTTY, errno.EINVAL]: self.print_once( - "WARNING: Chip was NOT reset. Setting RTS/DTR lines is not " - f"supported for port '{self.port.name}'. Set --before and --after " - "arguments to 'no_reset' and switch to bootloader manually to " - "avoid this warning." + "Chip was NOT reset. Setting RTS/DTR lines is not " + f"supported for port '{self.port.name}'. Set --before and " + "--after arguments to 'no-reset' and switch to bootloader " + "manually to avoid this warning." ) break elif not retry: @@ -205,5 +206,5 @@ def _parse_string_to_seq(self, seq_str): cmds = seq_str.split("|") fn_calls_list = [self.format_dict[cmd[0]].format(cmd[1:]) for cmd in cmds] except Exception as e: - raise FatalError(f'Invalid "custom_reset_sequence" option format: {e}') + raise FatalError(f"Invalid custom reset sequence option format: {e}") return "\n".join(fn_calls_list) diff --git a/tools/esptool_py/esptool/targets/__init__.py b/tools/esptool_py/esptool/targets/__init__.py index 30c08605d7..ea5c8d7aef 100644 --- a/tools/esptool_py/esptool/targets/__init__.py +++ b/tools/esptool_py/esptool/targets/__init__.py @@ -2,17 +2,14 @@ from .esp32c2 import ESP32C2ROM from .esp32c3 import ESP32C3ROM from .esp32c5 import ESP32C5ROM -from .esp32c5beta3 import ESP32C5BETA3ROM from .esp32c6 import ESP32C6ROM from .esp32c61 import ESP32C61ROM -from .esp32c6beta import ESP32C6BETAROM from .esp32h2 import ESP32H2ROM -from .esp32h2beta1 import ESP32H2BETA1ROM -from .esp32h2beta2 import ESP32H2BETA2ROM +from .esp32h21 import ESP32H21ROM +from .esp32h4 import ESP32H4ROM from .esp32p4 import ESP32P4ROM from .esp32s2 import ESP32S2ROM from .esp32s3 import ESP32S3ROM -from .esp32s3beta2 import ESP32S3BETA2ROM from .esp8266 import ESP8266ROM @@ -20,19 +17,16 @@ "esp8266": ESP8266ROM, "esp32": ESP32ROM, "esp32s2": ESP32S2ROM, - "esp32s3beta2": ESP32S3BETA2ROM, "esp32s3": ESP32S3ROM, "esp32c3": ESP32C3ROM, - "esp32c6beta": ESP32C6BETAROM, - "esp32h2beta1": ESP32H2BETA1ROM, - "esp32h2beta2": ESP32H2BETA2ROM, "esp32c2": ESP32C2ROM, "esp32c6": ESP32C6ROM, "esp32c61": ESP32C61ROM, "esp32c5": ESP32C5ROM, - "esp32c5beta3": ESP32C5BETA3ROM, "esp32h2": ESP32H2ROM, + "esp32h21": ESP32H21ROM, "esp32p4": ESP32P4ROM, + "esp32h4": ESP32H4ROM, } CHIP_LIST = list(CHIP_DEFS.keys()) diff --git a/tools/esptool_py/esptool/targets/esp32.py b/tools/esptool_py/esptool/targets/esp32.py index aef531a0d6..6ab879a69b 100644 --- a/tools/esptool_py/esptool/targets/esp32.py +++ b/tools/esptool_py/esptool/targets/esp32.py @@ -1,14 +1,14 @@ -# 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 import struct import time -from typing import Dict, Optional -from ..loader import ESPLoader -from ..util import FatalError, NotSupportedError +from ..loader import ESPLoader, StubMixin +from ..logger import log +from ..util import FatalError class ESP32ROM(ESPLoader): @@ -16,9 +16,8 @@ class ESP32ROM(ESPLoader): CHIP_NAME = "ESP32" IMAGE_CHIP_ID = 0 - IS_STUB = False - CHIP_DETECT_MAGIC_VALUE = [0x00F01D83] + MAGIC_VALUE = 0x00F01D83 IROM_MAP_START = 0x400D0000 IROM_MAP_END = 0x40400000 @@ -26,9 +25,6 @@ class ESP32ROM(ESPLoader): DROM_MAP_START = 0x3F400000 DROM_MAP_END = 0x3F800000 - # ESP32 uses a 4 byte status reply - STATUS_BYTES_LENGTH = 4 - SPI_REG_BASE = 0x3FF42000 SPI_USR_OFFS = 0x1C SPI_USR1_OFFS = 0x20 @@ -125,8 +121,6 @@ class ESP32ROM(ESPLoader): UF2_FAMILY_ID = 0x1C5F21B0 - KEY_PURPOSES: Dict[int, str] = {} - """ Try to read the BLOCK1 (encryption key) and check if it is valid """ def is_flash_encryption_key_valid(self): @@ -201,9 +195,6 @@ def get_pkg_version(self): pkg_version += ((word3 >> 2) & 0x1) << 3 return pkg_version - def get_chip_revision(self): - return self.get_major_chip_version() * 100 + self.get_minor_chip_version() - def get_minor_chip_version(self): return (self.read_efuse(5) >> 24) & 0x3 @@ -237,12 +228,12 @@ def get_chip_description(self): 5: "ESP32-PICO-V3" if rev3 else "ESP32-PICO-D4", 6: "ESP32-PICO-V3-02", 7: "ESP32-D0WDR2-V3", - }.get(pkg_version, "unknown ESP32") + }.get(pkg_version, "Unknown ESP32") return f"{chip_name} (revision v{major_rev}.{minor_rev})" def get_chip_features(self): - features = ["WiFi"] + features = ["Wi-Fi"] word3 = self.read_efuse(3) # names of variables in this section are lowercase @@ -255,9 +246,9 @@ def get_chip_features(self): chip_ver_dis_app_cpu = word3 & (1 << 0) if chip_ver_dis_app_cpu: - features += ["Single Core"] + features += ["Single Core + LP Core"] else: - features += ["Dual Core"] + features += ["Dual Core + LP Core"] chip_cpu_freq_rated = word3 & (1 << 13) if chip_cpu_freq_rated: @@ -277,7 +268,7 @@ def get_chip_features(self): word4 = self.read_efuse(4) adc_vref = (word4 >> 8) & 0x1F if adc_vref: - features += ["VRef calibration in efuse"] + features += ["Vref calibration in eFuse"] blk3_part_res = word3 >> 14 & 0x1 if blk3_part_res: @@ -315,9 +306,6 @@ def read_efuse(self, n): """Read the nth word of the ESP3x EFUSE region.""" return self.read_reg(self.EFUSE_RD_REG_BASE + (4 * n)) - def chip_id(self): - raise NotSupportedError(self, "Function chip_id") - def read_mac(self, mac_type="BASE_MAC"): """Read MAC from EFUSE region""" if mac_type != "BASE_MAC": @@ -330,7 +318,7 @@ def read_mac(self, mac_type="BASE_MAC"): def get_erase_size(self, offset, size): return size - def _get_efuse_flash_voltage(self) -> Optional[str]: + def _get_efuse_flash_voltage(self) -> str | None: efuse = self.read_reg(self.EFUSE_VDD_SPI_REG) # check efuse setting if efuse & (self.VDD_SPI_FORCE | self.VDD_SPI_XPD | self.VDD_SPI_TIEH): @@ -341,7 +329,7 @@ def _get_efuse_flash_voltage(self) -> Optional[str]: return "OFF" return None - def _get_rtc_cntl_flash_voltage(self) -> Optional[str]: + def _get_rtc_cntl_flash_voltage(self) -> str | None: reg = self.read_reg(self.RTC_CNTL_SDIO_CONF_REG) # check if override is set in RTC_CNTL_SDIO_CONF_REG if reg & self.RTC_CNTL_SDIO_FORCE: @@ -365,16 +353,18 @@ def get_flash_voltage(self): strap_reg &= self.GPIO_STRAP_VDDSPI_MASK voltage = "1.8V" if strap_reg else "3.3V" source = "a strapping pin" - print(f"Flash voltage set by {source} to {voltage}") + log.print(f"Flash voltage set by {source}: {voltage}") def override_vddsdio(self, new_voltage): new_voltage = new_voltage.upper() if new_voltage not in self.OVERRIDE_VDDSDIO_CHOICES: raise FatalError( - f"The only accepted VDDSDIO overrides are {', '.join(self.OVERRIDE_VDDSDIO_CHOICES)}" + "The only accepted VDDSDIO overrides are , ".join( + self.OVERRIDE_VDDSDIO_CHOICES + ) ) # RTC_CNTL_SDIO_TIEH is not used here, setting TIEH=1 would set 3.3V output, - # not safe for esptool.py to do + # not safe for esptool to do reg_val = self.RTC_CNTL_SDIO_FORCE # override efuse setting reg_val |= self.RTC_CNTL_SDIO_PD_EN @@ -387,7 +377,7 @@ def override_vddsdio(self, new_voltage): | self.RTC_CNTL_DREFL_SDIO_M ) # boost voltage self.write_reg(self.RTC_CNTL_SDIO_CONF_REG, reg_val) - print("VDDSDIO regulator set to %s" % new_voltage) + log.print(f"VDDSDIO regulator set to {new_voltage}.") def read_flash_slow(self, offset, length, progress_fn): BLOCK_LEN = 64 # ROM read limit per command (this limit is why it's so slow) @@ -398,24 +388,23 @@ def read_flash_slow(self, offset, length, progress_fn): try: r = self.check_command( "read flash block", - self.ESP_READ_FLASH_SLOW, + self.ESP_CMDS["READ_FLASH_SLOW"], struct.pack(" 33000000 else 26000000 false_rom_baud = int(baud * rom_calculated_freq // valid_freq) - print(f"Changing baud rate to {baud}") - self.command(self.ESP_CHANGE_BAUDRATE, struct.pack("> 16) & 0xF @@ -113,11 +119,12 @@ def change_baud(self, baud): # a 26 MHz XTAL. false_rom_baud = baud * 40 // 26 - print(f"Changing baud rate to {baud}") + log.print(f"Changing baud rate to {baud}...") self.command( - self.ESP_CHANGE_BAUDRATE, struct.pack("> self.PCR_SYSCLK_XTAL_FREQ_S def hard_reset(self): - print("Hard resetting via RTS pin...") - HardReset(self._port, self.uses_usb_jtag_serial())() + ESPLoader.hard_reset(self, self.uses_usb_jtag_serial()) def change_baud(self, baud): if not self.IS_STUB: crystal_freq_rom_expect = self.get_crystal_freq_rom_expect() crystal_freq_detect = self.get_crystal_freq() - print( - f"ROM expects crystal freq: {crystal_freq_rom_expect} MHz, detected {crystal_freq_detect} MHz" + log.print( + f"ROM expects crystal freq: {crystal_freq_rom_expect} MHz, " + f"detected {crystal_freq_detect} MHz." ) baud_rate = baud # If detect the XTAL is 48MHz, but the ROM code expects it to be 40MHz @@ -149,42 +157,59 @@ def change_baud(self, baud): ESPLoader.change_baud(self, baud_rate) return - print(f"Changing baud rate to {baud_rate}") - self.command(self.ESP_CHANGE_BAUDRATE, struct.pack(" self.EFUSE_MAX_KEY: + raise FatalError( + f"Valid key block numbers must be in range 0-{self.EFUSE_MAX_KEY}" + ) + + 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(self.EFUSE_MAX_KEY + 1) + ] + + 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, 29))): raise FatalError("SPI Pin numbers must be in the range 0-28.") 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, " + log.warning( + "GPIO pins 13 and 14 are used by USB-Serial/JTAG, " "consider using other pins for SPI flash connection." ) + def watchdog_reset(self): + # Watchdog reset disabled in parent (ESP32-C6) ROM, re-enable it + ESP32C3ROM.watchdog_reset(self) -class ESP32C5StubLoader(ESP32C5ROM): - """Access class for ESP32C5 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 +class ESP32C5StubLoader(StubMixin, ESP32C5ROM): + """Stub loader for ESP32-C5, runs on top of ROM.""" - 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 + pass ESP32C5ROM.STUB_CLASS = ESP32C5StubLoader diff --git a/tools/esptool_py/esptool/targets/esp32c5beta3.py b/tools/esptool_py/esptool/targets/esp32c5beta3.py deleted file mode 100644 index 66b09ce66b..0000000000 --- a/tools/esptool_py/esptool/targets/esp32c5beta3.py +++ /dev/null @@ -1,129 +0,0 @@ -# SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD -# -# SPDX-License-Identifier: GPL-2.0-or-later - -import struct -import time -from typing import Dict - -from .esp32c6 import ESP32C6ROM -from ..loader import ESPLoader - - -class ESP32C5BETA3ROM(ESP32C6ROM): - CHIP_NAME = "ESP32-C5(beta3)" - IMAGE_CHIP_ID = 17 - - IROM_MAP_START = 0x41000000 - IROM_MAP_END = 0x41800000 - DROM_MAP_START = 0x41000000 - DROM_MAP_END = 0x41800000 - - # Magic value for ESP32C5(beta3) - CHIP_DETECT_MAGIC_VALUE = [0xE10D8082] - - FLASH_FREQUENCY = { - "80m": 0xF, - "40m": 0x0, - "20m": 0x2, - } - - MEMORY_MAP = [ - [0x00000000, 0x00010000, "PADDING"], - [0x41800000, 0x42000000, "DROM"], - [0x40800000, 0x40880000, "DRAM"], - [0x40800000, 0x40880000, "BYTE_ACCESSIBLE"], - [0x4004A000, 0x40050000, "DROM_MASK"], - [0x40000000, 0x4004A000, "IROM_MASK"], - [0x41000000, 0x41800000, "IROM"], - [0x40800000, 0x40880000, "IRAM"], - [0x50000000, 0x50004000, "RTC_IRAM"], - [0x50000000, 0x50004000, "RTC_DRAM"], - [0x600FE000, 0x60100000, "MEM_INTERNAL2"], - ] - - EFUSE_MAX_KEY = 5 - KEY_PURPOSES: Dict[int, str] = { - 0: "USER/EMPTY", - 1: "ECDSA_KEY", - 2: "XTS_AES_256_KEY_1", - 3: "XTS_AES_256_KEY_2", - 4: "XTS_AES_128_KEY", - 5: "HMAC_DOWN_ALL", - 6: "HMAC_DOWN_JTAG", - 7: "HMAC_DOWN_DIGITAL_SIGNATURE", - 8: "HMAC_UP", - 9: "SECURE_BOOT_DIGEST0", - 10: "SECURE_BOOT_DIGEST1", - 11: "SECURE_BOOT_DIGEST2", - 12: "KM_INIT_KEY", - } - - def get_pkg_version(self): - num_word = 2 - return (self.read_reg(self.EFUSE_BLOCK1_ADDR + (4 * num_word)) >> 26) & 0x07 - - def get_minor_chip_version(self): - num_word = 2 - return (self.read_reg(self.EFUSE_BLOCK1_ADDR + (4 * num_word)) >> 0) & 0x0F - - def get_major_chip_version(self): - num_word = 2 - return (self.read_reg(self.EFUSE_BLOCK1_ADDR + (4 * num_word)) >> 4) & 0x03 - - def get_chip_description(self): - chip_name = { - 0: "ESP32-C5 beta3 (QFN40)", - }.get(self.get_pkg_version(), "unknown ESP32-C5 beta3") - 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_crystal_freq(self): - # The crystal detection algorithm of ESP32/ESP8266 - # works for ESP32-C5 beta3 as well. - return ESPLoader.get_crystal_freq(self) - - def change_baud(self, baud): - rom_with_48M_XTAL = not self.IS_STUB and self.get_crystal_freq() == 48 - if rom_with_48M_XTAL: - # The code is copied over from ESPLoader.change_baud(). - # Probably this is just a temporary solution until the next chip revision. - - # The ROM code thinks it uses a 40 MHz XTAL. Recompute the baud rate - # in order to trick the ROM code to set the correct baud rate for - # a 48 MHz XTAL. - false_rom_baud = baud * 40 // 48 - - print(f"Changing baud rate to {baud}") - self.command( - self.ESP_CHANGE_BAUDRATE, struct.pack(" self.EFUSE_MAX_KEY: raise FatalError( @@ -188,29 +186,21 @@ def check_spi_connection(self, spi_connection): if not set(spi_connection).issubset(set(range(0, 31))): raise FatalError("SPI Pin numbers must be in the range 0-30.") if any([v for v in spi_connection if v in [12, 13]]): - print( - "WARNING: GPIO pins 12 and 13 are used by USB-Serial/JTAG, " + log.warning( + "GPIO pins 12 and 13 are used by USB-Serial/JTAG, " "consider using other pins for SPI flash connection." ) + def watchdog_reset(self): + # Bug in the USB-Serial/JTAG controller can cause the port to disappear + # if watchdog reset happens, disable it on ESP32-C6 + ESPLoader.watchdog_reset(self) -class ESP32C6StubLoader(ESP32C6ROM): - """Access class for ESP32C6 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 +class ESP32C6StubLoader(StubMixin, ESP32C6ROM): + """Stub loader for ESP32-C6, runs on top of ROM.""" - 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 + pass ESP32C6ROM.STUB_CLASS = ESP32C6StubLoader diff --git a/tools/esptool_py/esptool/targets/esp32c61.py b/tools/esptool_py/esptool/targets/esp32c61.py index 2132bda3fd..18b7356af9 100644 --- a/tools/esptool_py/esptool/targets/esp32c61.py +++ b/tools/esptool_py/esptool/targets/esp32c61.py @@ -1,20 +1,19 @@ -# SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD +# SPDX-FileCopyrightText: 2024-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 typing import Dict +from .esp32c3 import ESP32C3ROM from .esp32c6 import ESP32C6ROM +from ..loader import StubMixin class ESP32C61ROM(ESP32C6ROM): CHIP_NAME = "ESP32-C61" IMAGE_CHIP_ID = 20 - # Magic value for ESP32C61 - CHIP_DETECT_MAGIC_VALUE = [0x33F0206F, 0x2421606F] - UART_DATE_REG_ADDR = 0x60000000 + 0x7C EFUSE_BASE = 0x600B4800 @@ -53,12 +52,12 @@ class ESP32C61ROM(ESP32C6ROM): MEMORY_MAP = [ [0x00000000, 0x00010000, "PADDING"], - [0x41800000, 0x42000000, "DROM"], + [0x42000000, 0x44000000, "DROM"], [0x40800000, 0x40860000, "DRAM"], [0x40800000, 0x40860000, "BYTE_ACCESSIBLE"], [0x4004AC00, 0x40050000, "DROM_MASK"], [0x40000000, 0x4004AC00, "IROM_MASK"], - [0x41000000, 0x41800000, "IROM"], + [0x42000000, 0x44000000, "IROM"], [0x40800000, 0x40860000, "IRAM"], [0x50000000, 0x50004000, "RTC_IRAM"], [0x50000000, 0x50004000, "RTC_DRAM"], @@ -67,8 +66,7 @@ class ESP32C61ROM(ESP32C6ROM): UF2_FAMILY_ID = 0x77D850C4 - EFUSE_MAX_KEY = 5 - KEY_PURPOSES: Dict[int, str] = { + KEY_PURPOSES: dict[int, str] = { 0: "USER/EMPTY", 1: "ECDSA_KEY", 2: "XTS_AES_256_KEY_1", @@ -102,13 +100,13 @@ def get_major_chip_version(self): def get_chip_description(self): chip_name = { 0: "ESP32-C61", - }.get(self.get_pkg_version(), "unknown ESP32-C61") + }.get(self.get_pkg_version(), "Unknown ESP32-C61") 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"] + return ["Wi-Fi 6", "BT 5 (LE)", "Single Core", "160MHz"] def read_mac(self, mac_type="BASE_MAC"): """Read MAC from EFUSE region""" @@ -121,24 +119,15 @@ def read_mac(self, mac_type="BASE_MAC"): } return macs.get(mac_type, None) + def watchdog_reset(self): + # Watchdog reset disabled in parent (ESP32-C6) ROM, re-enable it + ESP32C3ROM.watchdog_reset(self) -class ESP32C61StubLoader(ESP32C61ROM): - """Access class for ESP32C61 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 +class ESP32C61StubLoader(StubMixin, ESP32C61ROM): + """Stub loader for ESP32-C61, runs on top of ROM.""" - 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 + pass ESP32C61ROM.STUB_CLASS = ESP32C61StubLoader diff --git a/tools/esptool_py/esptool/targets/esp32c6beta.py b/tools/esptool_py/esptool/targets/esp32c6beta.py deleted file mode 100644 index b6e100bb42..0000000000 --- a/tools/esptool_py/esptool/targets/esp32c6beta.py +++ /dev/null @@ -1,27 +0,0 @@ -# SPDX-FileCopyrightText: 2014-2022 Fredrik Ahlberg, Angus Gratton, -# Espressif Systems (Shanghai) CO LTD, other contributors as noted. -# -# SPDX-License-Identifier: GPL-2.0-or-later - -from .esp32c3 import ESP32C3ROM - - -class ESP32C6BETAROM(ESP32C3ROM): - CHIP_NAME = "ESP32-C6(beta)" - IMAGE_CHIP_ID = 7 - - CHIP_DETECT_MAGIC_VALUE = [0x0DA1806F] - - UART_DATE_REG_ADDR = 0x00000500 - - def get_chip_description(self): - chip_name = { - 0: "ESP32-C6 (QFN40)", - 1: "ESP32-C6FH4 (QFN32)", - }.get(self.get_pkg_version(), "unknown ESP32-C6") - 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 _post_connect(self): - pass diff --git a/tools/esptool_py/esptool/targets/esp32h2.py b/tools/esptool_py/esptool/targets/esp32h2.py index da7d34a589..a47583383d 100644 --- a/tools/esptool_py/esptool/targets/esp32h2.py +++ b/tools/esptool_py/esptool/targets/esp32h2.py @@ -1,11 +1,11 @@ -# SPDX-FileCopyrightText: 2022 Fredrik Ahlberg, Angus Gratton, +# SPDX-FileCopyrightText: 2024-2025 Fredrik Ahlberg, Angus Gratton, # Espressif Systems (Shanghai) CO LTD, other contributors as noted. # # SPDX-License-Identifier: GPL-2.0-or-later -from typing import Dict - from .esp32c6 import ESP32C6ROM +from ..loader import ESPLoader, StubMixin +from ..logger import log from ..util import FatalError @@ -13,11 +13,9 @@ class ESP32H2ROM(ESP32C6ROM): CHIP_NAME = "ESP32-H2" IMAGE_CHIP_ID = 16 - # Magic value for ESP32H2 - CHIP_DETECT_MAGIC_VALUE = [0xD7B73E80] - DR_REG_LP_WDT_BASE = 0x600B1C00 RTC_CNTL_WDTCONFIG0_REG = DR_REG_LP_WDT_BASE + 0x0 # LP_WDT_RWDT_CONFIG0_REG + RTC_CNTL_WDTCONFIG1_REG = DR_REG_LP_WDT_BASE + 0x0004 # LP_WDT_RWDT_CONFIG1_REG RTC_CNTL_WDTWPROTECT_REG = DR_REG_LP_WDT_BASE + 0x001C # LP_WDT_RWDT_WPROTECT_REG RTC_CNTL_SWD_CONF_REG = DR_REG_LP_WDT_BASE + 0x0020 # LP_WDT_SWD_CONFIG_REG @@ -25,6 +23,9 @@ class ESP32H2ROM(ESP32C6ROM): RTC_CNTL_SWD_WPROTECT_REG = DR_REG_LP_WDT_BASE + 0x0024 # LP_WDT_SWD_WPROTECT_REG RTC_CNTL_SWD_WKEY = 0x50D83AA1 # LP_WDT_SWD_WKEY, same as WDT key in this case + UARTDEV_BUF_NO = 0x4084FEFC # 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 + FLASH_FREQUENCY = { "48m": 0xF, "24m": 0x0, @@ -34,8 +35,7 @@ class ESP32H2ROM(ESP32C6ROM): UF2_FAMILY_ID = 0x332726F6 - EFUSE_MAX_KEY = 5 - KEY_PURPOSES: Dict[int, str] = { + KEY_PURPOSES: dict[int, str] = { 0: "USER/EMPTY", 1: "ECDSA_KEY", 2: "XTS_AES_256_KEY_1", @@ -65,45 +65,36 @@ def get_major_chip_version(self): def get_chip_description(self): chip_name = { 0: "ESP32-H2", - }.get(self.get_pkg_version(), "unknown ESP32-H2") + }.get(self.get_pkg_version(), "Unknown ESP32-H2") 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 ["BLE", "IEEE802.15.4"] + return ["BT 5 (LE)", "IEEE802.15.4", "Single Core", "96MHz"] def get_crystal_freq(self): # ESP32H2 XTAL is fixed to 32MHz return 32 + # Watchdog reset is not supported on ESP32-H2 + def watchdog_reset(self): + ESPLoader.watchdog_reset(self) + def check_spi_connection(self, spi_connection): if not set(spi_connection).issubset(set(range(0, 28))): raise FatalError("SPI Pin numbers must be in the range 0-27.") if any([v for v in spi_connection if v in [26, 27]]): - print( - "WARNING: GPIO pins 26 and 27 are used by USB-Serial/JTAG, " + log.warning( + "GPIO pins 26 and 27 are used by USB-Serial/JTAG, " "consider using other pins for SPI flash connection." ) -class ESP32H2StubLoader(ESP32H2ROM): - """Access class for ESP32H2 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 +class ESP32H2StubLoader(StubMixin, ESP32H2ROM): + """Stub loader for ESP32-H2, runs on top of ROM.""" - 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 + pass ESP32H2ROM.STUB_CLASS = ESP32H2StubLoader diff --git a/tools/esptool_py/esptool/targets/esp32h21.py b/tools/esptool_py/esptool/targets/esp32h21.py new file mode 100644 index 0000000000..ad96920535 --- /dev/null +++ b/tools/esptool_py/esptool/targets/esp32h21.py @@ -0,0 +1,96 @@ +# SPDX-FileCopyrightText: 2024-2025 Fredrik Ahlberg, Angus Gratton, +# Espressif Systems (Shanghai) CO LTD, other contributors as noted. +# +# SPDX-License-Identifier: GPL-2.0-or-later + + +from .esp32h2 import ESP32H2ROM +from ..loader import StubMixin +from ..logger import log +from ..util import FatalError + + +class ESP32H21ROM(ESP32H2ROM): + CHIP_NAME = "ESP32-H21" + IMAGE_CHIP_ID = 25 + + UF2_FAMILY_ID = 0xB6DD00AF + + DR_REG_LP_WDT_BASE = 0x600B1C00 + RTC_CNTL_WDTCONFIG0_REG = DR_REG_LP_WDT_BASE + 0x0 # LP_WDT_RWDT_CONFIG0_REG + RTC_CNTL_WDTWPROTECT_REG = DR_REG_LP_WDT_BASE + 0x001C # LP_WDT_RWDT_WPROTECT_REG + + RTC_CNTL_SWD_CONF_REG = DR_REG_LP_WDT_BASE + 0x0020 # LP_WDT_SWD_CONFIG_REG + RTC_CNTL_SWD_AUTO_FEED_EN = 1 << 18 + RTC_CNTL_SWD_WPROTECT_REG = DR_REG_LP_WDT_BASE + 0x0024 # LP_WDT_SWD_WPROTECT_REG + RTC_CNTL_SWD_WKEY = 0x50D83AA1 # LP_WDT_SWD_WKEY, same as WDT key in this case + + EFUSE_BASE = 0x600B4000 + 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 = 24 + EFUSE_PURPOSE_KEY1_REG = EFUSE_BASE + 0x34 + EFUSE_PURPOSE_KEY1_SHIFT = 28 + EFUSE_PURPOSE_KEY2_REG = EFUSE_BASE + 0x38 + EFUSE_PURPOSE_KEY2_SHIFT = 0 + EFUSE_PURPOSE_KEY3_REG = EFUSE_BASE + 0x38 + EFUSE_PURPOSE_KEY3_SHIFT = 4 + EFUSE_PURPOSE_KEY4_REG = EFUSE_BASE + 0x38 + EFUSE_PURPOSE_KEY4_SHIFT = 8 + EFUSE_PURPOSE_KEY5_REG = EFUSE_BASE + 0x38 + EFUSE_PURPOSE_KEY5_SHIFT = 12 + + EFUSE_DIS_DOWNLOAD_MANUAL_ENCRYPT_REG = EFUSE_RD_REG_BASE + EFUSE_DIS_DOWNLOAD_MANUAL_ENCRYPT = 1 << 20 + + EFUSE_SPI_BOOT_CRYPT_CNT_REG = EFUSE_BASE + 0x034 + EFUSE_SPI_BOOT_CRYPT_CNT_MASK = 0x7 << 18 + + EFUSE_SECURE_BOOT_EN_REG = EFUSE_BASE + 0x038 + EFUSE_SECURE_BOOT_EN_MASK = 1 << 20 + + 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-H21", + }.get(self.get_pkg_version(), "Unknown ESP32-H21") + 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 ["BT 5 (LE)", "IEEE802.15.4", "Single Core", "96MHz"] + + def get_crystal_freq(self): + # ESP32H21 XTAL is fixed to 32MHz + return 32 + + def check_spi_connection(self, spi_connection): + if not set(spi_connection).issubset(set(range(0, 28))): + raise FatalError("SPI Pin numbers must be in the range 0-27.") + if any([v for v in spi_connection if v in [26, 27]]): + log.warning( + "GPIO pins 26 and 27 are used by USB-Serial/JTAG, " + "consider using other pins for SPI flash connection." + ) + + +class ESP32H21StubLoader(StubMixin, ESP32H21ROM): + """Stub loader for ESP32-H21, runs on top of ROM.""" + + pass + + +ESP32H21ROM.STUB_CLASS = ESP32H21StubLoader diff --git a/tools/esptool_py/esptool/targets/esp32h2beta1.py b/tools/esptool_py/esptool/targets/esp32h2beta1.py deleted file mode 100644 index 999a16e08d..0000000000 --- a/tools/esptool_py/esptool/targets/esp32h2beta1.py +++ /dev/null @@ -1,186 +0,0 @@ -# SPDX-FileCopyrightText: 2014-2022 Fredrik Ahlberg, Angus Gratton, -# Espressif Systems (Shanghai) CO LTD, other contributors as noted. -# -# SPDX-License-Identifier: GPL-2.0-or-later - -import struct -from typing import Dict - -from .esp32c3 import ESP32C3ROM -from ..util import FatalError, NotImplementedInROMError - -from typing import List - - -class ESP32H2BETA1ROM(ESP32C3ROM): - CHIP_NAME = "ESP32-H2(beta1)" - IMAGE_CHIP_ID = 10 - - IROM_MAP_START = 0x42000000 - IROM_MAP_END = 0x42800000 - DROM_MAP_START = 0x3C000000 - DROM_MAP_END = 0x3C800000 - - SPI_REG_BASE = 0x60002000 - 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 - - BOOTLOADER_FLASH_OFFSET = 0x0 - - CHIP_DETECT_MAGIC_VALUE = [0xCA26CC22] - - UART_DATE_REG_ADDR = 0x60000000 + 0x7C - - EFUSE_BASE = 0x6001A000 - 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 = 24 - EFUSE_PURPOSE_KEY1_REG = EFUSE_BASE + 0x34 - EFUSE_PURPOSE_KEY1_SHIFT = 28 - EFUSE_PURPOSE_KEY2_REG = EFUSE_BASE + 0x38 - EFUSE_PURPOSE_KEY2_SHIFT = 0 - EFUSE_PURPOSE_KEY3_REG = EFUSE_BASE + 0x38 - EFUSE_PURPOSE_KEY3_SHIFT = 4 - EFUSE_PURPOSE_KEY4_REG = EFUSE_BASE + 0x38 - EFUSE_PURPOSE_KEY4_SHIFT = 8 - EFUSE_PURPOSE_KEY5_REG = EFUSE_BASE + 0x38 - EFUSE_PURPOSE_KEY5_SHIFT = 12 - - EFUSE_DIS_DOWNLOAD_MANUAL_ENCRYPT_REG = EFUSE_RD_REG_BASE - EFUSE_DIS_DOWNLOAD_MANUAL_ENCRYPT = 1 << 20 - - EFUSE_SPI_BOOT_CRYPT_CNT_REG = EFUSE_BASE + 0x034 - EFUSE_SPI_BOOT_CRYPT_CNT_MASK = 0x7 << 18 - - EFUSE_SECURE_BOOT_EN_REG = EFUSE_BASE + 0x038 - EFUSE_SECURE_BOOT_EN_MASK = 1 << 20 - - PURPOSE_VAL_XTS_AES128_KEY = 4 - - SUPPORTS_ENCRYPTED_FLASH = True - - FLASH_ENCRYPTED_WRITE_ALIGN = 16 - - MEMORY_MAP: List = [] - - FLASH_FREQUENCY = { - "48m": 0xF, - "24m": 0x0, - "16m": 0x1, - "12m": 0x2, - } - - EFUSE_MAX_KEY = 5 - KEY_PURPOSES: Dict[int, str] = { - 0: "USER/EMPTY", - 1: "ECDSA_KEY", - 2: "RESERVED", - 4: "XTS_AES_128_KEY", - 5: "HMAC_DOWN_ALL", - 6: "HMAC_DOWN_JTAG", - 7: "HMAC_DOWN_DIGITAL_SIGNATURE", - 8: "HMAC_UP", - 9: "SECURE_BOOT_DIGEST0", - 10: "SECURE_BOOT_DIGEST1", - 11: "SECURE_BOOT_DIGEST2", - } - - def get_pkg_version(self): - num_word = 4 - return (self.read_reg(self.EFUSE_BLOCK1_ADDR + (4 * num_word)) >> 0) & 0x07 - - def get_minor_chip_version(self): - num_word = 3 - return (self.read_reg(self.EFUSE_BLOCK1_ADDR + (4 * num_word)) >> 18) & 0x07 - - def get_major_chip_version(self): - num_word = 3 - return (self.read_reg(self.EFUSE_BLOCK1_ADDR + (4 * num_word)) >> 21) & 0x03 - - def get_chip_description(self): - chip_name = { - 0: "ESP32-H2", - }.get(self.get_pkg_version(), "unknown ESP32-H2") - 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 ["BLE", "IEEE802.15.4"] - - def get_crystal_freq(self): - return 32 - - def override_vddsdio(self, new_voltage): - raise NotImplementedInROMError( - "VDD_SDIO overrides are not supported for ESP32-H2" - ) - - def read_mac(self, mac_type="BASE_MAC"): - """Read MAC from EFUSE region""" - if mac_type != "BASE_MAC": - return None - mac0 = self.read_reg(self.MAC_EFUSE_REG) - mac1 = self.read_reg(self.MAC_EFUSE_REG + 4) # only bottom 16 bits are MAC - bitstring = struct.pack(">II", mac1, mac0)[2:] - return tuple(bitstring) - - def get_flash_crypt_config(self): - return None # doesn't exist on ESP32-H2 - - def get_key_block_purpose(self, key_block): - if key_block < 0 or key_block > self.EFUSE_MAX_KEY: - raise FatalError( - f"Valid key block numbers must be in range 0-{self.EFUSE_MAX_KEY}" - ) - - 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) & 0xF - - 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(self.EFUSE_MAX_KEY + 1) - ] - - return any(p == self.PURPOSE_VAL_XTS_AES128_KEY for p in purposes) - - def _post_connect(self): - pass - - -class ESP32H2BETA1StubLoader(ESP32H2BETA1ROM): - """Access class for ESP32H2BETA1 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 - - -ESP32H2BETA1ROM.STUB_CLASS = ESP32H2BETA1StubLoader diff --git a/tools/esptool_py/esptool/targets/esp32h2beta2.py b/tools/esptool_py/esptool/targets/esp32h2beta2.py deleted file mode 100644 index 6fa8f587c7..0000000000 --- a/tools/esptool_py/esptool/targets/esp32h2beta2.py +++ /dev/null @@ -1,43 +0,0 @@ -# SPDX-FileCopyrightText: 2014-2022 Fredrik Ahlberg, Angus Gratton, -# Espressif Systems (Shanghai) CO LTD, other contributors as noted. -# -# SPDX-License-Identifier: GPL-2.0-or-later - -from .esp32h2beta1 import ESP32H2BETA1ROM - - -class ESP32H2BETA2ROM(ESP32H2BETA1ROM): - CHIP_NAME = "ESP32-H2(beta2)" - IMAGE_CHIP_ID = 14 - - CHIP_DETECT_MAGIC_VALUE = [0x6881B06F] - - def get_chip_description(self): - chip_name = { - 1: "ESP32-H2(beta2)", - }.get(self.get_pkg_version(), "unknown ESP32-H2") - major_rev = self.get_major_chip_version() - minor_rev = self.get_minor_chip_version() - return f"{chip_name} (revision v{major_rev}.{minor_rev})" - - -class ESP32H2BETA2StubLoader(ESP32H2BETA2ROM): - """Access class for ESP32H2BETA2 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 - - -ESP32H2BETA2ROM.STUB_CLASS = ESP32H2BETA2StubLoader diff --git a/tools/esptool_py/esptool/targets/esp32h4.py b/tools/esptool_py/esptool/targets/esp32h4.py new file mode 100644 index 0000000000..15edc4c3b1 --- /dev/null +++ b/tools/esptool_py/esptool/targets/esp32h4.py @@ -0,0 +1,201 @@ +# 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, StubMixin +from ..logger import log +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 ["BT 5 (LE)", "IEEE802.15.4", "Dual Core", "96MHz"] + + 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]]): + log.warning( + "GPIO pins 13 and 14 are used by USB-Serial/JTAG, " + "consider using other pins for SPI flash connection." + ) + + +class ESP32H4StubLoader(StubMixin, ESP32H4ROM): + """Stub loader for ESP32-H4, runs on top of ROM.""" + + pass + + +ESP32H4ROM.STUB_CLASS = ESP32H4StubLoader diff --git a/tools/esptool_py/esptool/targets/esp32p4.py b/tools/esptool_py/esptool/targets/esp32p4.py index 2b0a06498b..fbd839fa14 100644 --- a/tools/esptool_py/esptool/targets/esp32p4.py +++ b/tools/esptool_py/esptool/targets/esp32p4.py @@ -1,14 +1,15 @@ -# SPDX-FileCopyrightText: 2023 Fredrik Ahlberg, Angus Gratton, +# SPDX-FileCopyrightText: 2024-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 typing import Dict +from time import sleep from .esp32 import ESP32ROM -from ..loader import ESPLoader -from ..util import FatalError, NotImplementedInROMError +from ..loader import ESPLoader, StubMixin +from ..logger import log +from ..util import FatalError, NotSupportedError class ESP32P4ROM(ESP32ROM): @@ -22,8 +23,6 @@ class ESP32P4ROM(ESP32ROM): BOOTLOADER_FLASH_OFFSET = 0x2000 # First 2 sectors are reserved for FE purposes - CHIP_DETECT_MAGIC_VALUE = [0x0, 0x0ADDBAD0] - UART_DATE_REG_ADDR = 0x500CA000 + 0x8C EFUSE_BASE = 0x5012D000 @@ -40,6 +39,8 @@ class ESP32P4ROM(ESP32ROM): SPI_ADDR_REG_MSB = False + USES_MAGIC_VALUE = False + EFUSE_RD_REG_BASE = EFUSE_BASE + 0x030 # BLOCK0 read base address EFUSE_PURPOSE_KEY0_REG = EFUSE_BASE + 0x34 @@ -68,10 +69,21 @@ class ESP32P4ROM(ESP32ROM): PURPOSE_VAL_XTS_AES256_KEY_2 = 3 PURPOSE_VAL_XTS_AES128_KEY = 4 + USB_RAM_BLOCK = 0x800 # Max block size USB-OTG is used + + GPIO_STRAP_REG = 0x500E0038 + GPIO_STRAP_SPI_BOOT_MASK = 0x8 # Not download mode + RTC_CNTL_OPTION1_REG = 0x50110008 + RTC_CNTL_FORCE_DOWNLOAD_BOOT_MASK = 0x4 # Is download mode forced over USB? + SUPPORTS_ENCRYPTED_FLASH = True FLASH_ENCRYPTED_WRITE_ALIGN = 16 + UARTDEV_BUF_NO = 0x4FF3FEC8 # Variable in ROM .bss which indicates the port in use + UARTDEV_BUF_NO_USB_OTG = 5 # The above var when USB-OTG is used + UARTDEV_BUF_NO_USB_JTAG_SERIAL = 6 # The above var when USB-JTAG/Serial is used + MEMORY_MAP = [ [0x00000000, 0x00010000, "PADDING"], [0x40000000, 0x4C000000, "DROM"], @@ -88,8 +100,7 @@ class ESP32P4ROM(ESP32ROM): UF2_FAMILY_ID = 0x3D308E94 - EFUSE_MAX_KEY = 5 - KEY_PURPOSES: Dict[int, str] = { + KEY_PURPOSES: dict[int, str] = { 0: "USER/EMPTY", 1: "ECDSA_KEY", 2: "XTS_AES_256_KEY_1", @@ -105,6 +116,17 @@ class ESP32P4ROM(ESP32ROM): 12: "KM_INIT_KEY", } + DR_REG_LP_WDT_BASE = 0x50116000 + RTC_CNTL_WDTCONFIG0_REG = DR_REG_LP_WDT_BASE + 0x0 # LP_WDT_CONFIG0_REG + RTC_CNTL_WDTCONFIG1_REG = DR_REG_LP_WDT_BASE + 0x0004 # LP_WDT_CONFIG1_REG + RTC_CNTL_WDTWPROTECT_REG = DR_REG_LP_WDT_BASE + 0x0018 # LP_WDT_WPROTECT_REG + RTC_CNTL_WDT_WKEY = 0x50D83AA1 + + RTC_CNTL_SWD_CONF_REG = DR_REG_LP_WDT_BASE + 0x001C # RTC_WDT_SWD_CONFIG_REG + RTC_CNTL_SWD_AUTO_FEED_EN = 1 << 18 + RTC_CNTL_SWD_WPROTECT_REG = DR_REG_LP_WDT_BASE + 0x0020 # RTC_WDT_SWD_WPROTECT_REG + RTC_CNTL_SWD_WKEY = 0x50D83AA1 # RTC_WDT_SWD_WKEY, same as WDT key in this case + def get_pkg_version(self): num_word = 2 return (self.read_reg(self.EFUSE_BLOCK1_ADDR + (4 * num_word)) >> 20) & 0x07 @@ -115,30 +137,29 @@ def get_minor_chip_version(self): def get_major_chip_version(self): num_word = 2 - return (self.read_reg(self.EFUSE_BLOCK1_ADDR + (4 * num_word)) >> 4) & 0x03 + word = self.read_reg(self.EFUSE_BLOCK1_ADDR + (4 * num_word)) + return (((word >> 23) & 1) << 2) | ((word >> 4) & 0x03) def get_chip_description(self): chip_name = { 0: "ESP32-P4", - }.get(self.get_pkg_version(), "unknown ESP32-P4") + }.get(self.get_pkg_version(), "Unknown ESP32-P4") 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 ["High-Performance MCU"] + return ["Dual Core + LP Core", "400MHz"] def get_crystal_freq(self): # ESP32P4 XTAL is fixed to 40MHz return 40 def get_flash_voltage(self): - pass # not supported on ESP32-P4 + raise NotSupportedError(self, "Reading flash voltage") def override_vddsdio(self, new_voltage): - raise NotImplementedInROMError( - "VDD_SDIO overrides are not supported for ESP32-P4" - ) + raise NotSupportedError(self, "Overriding VDDSDIO") def read_mac(self, mac_type="BASE_MAC"): """Read MAC from EFUSE region""" @@ -191,38 +212,79 @@ def change_baud(self, baud): ESPLoader.change_baud(self, baud) def _post_connect(self): - pass - # TODO: Disable watchdogs when USB modes are supported in the stub - # if not self.sync_stub_detected: # Don't run if stub is reused - # self.disable_watchdogs() + if self.uses_usb_otg(): + self.ESP_RAM_BLOCK = self.USB_RAM_BLOCK + if not self.sync_stub_detected: # Don't run if stub is reused + self.disable_watchdogs() + + def uses_usb_otg(self): + """ + Check the UARTDEV_BUF_NO register to see if USB-OTG console is being used + """ + if self.secure_download_mode: + return False # can't detect native USB in secure download mode + return self.get_uart_no() == self.UARTDEV_BUF_NO_USB_OTG + + def uses_usb_jtag_serial(self): + """ + Check the UARTDEV_BUF_NO register to see if USB-JTAG/Serial is being used + """ + if self.secure_download_mode: + return False # can't detect USB-JTAG/Serial in secure download mode + return self.get_uart_no() == self.UARTDEV_BUF_NO_USB_JTAG_SERIAL + + def disable_watchdogs(self): + # When USB-JTAG/Serial is used, the RTC WDT and SWD watchdog are not reset + # and can then reset the board during flashing. Disable them. + if self.uses_usb_jtag_serial(): + # Disable RTC WDT + self.write_reg(self.RTC_CNTL_WDTWPROTECT_REG, self.RTC_CNTL_SWD_WKEY) + self.write_reg(self.RTC_CNTL_WDTCONFIG0_REG, 0) + self.write_reg(self.RTC_CNTL_WDTWPROTECT_REG, 0) + + # Automatically feed SWD + self.write_reg(self.RTC_CNTL_SWD_WPROTECT_REG, self.RTC_CNTL_SWD_WKEY) + self.write_reg( + self.RTC_CNTL_SWD_CONF_REG, + self.read_reg(self.RTC_CNTL_SWD_CONF_REG) + | self.RTC_CNTL_SWD_AUTO_FEED_EN, + ) + self.write_reg(self.RTC_CNTL_SWD_WPROTECT_REG, 0) def check_spi_connection(self, spi_connection): if not set(spi_connection).issubset(set(range(0, 55))): raise FatalError("SPI Pin numbers must be in the range 0-54.") if any([v for v in spi_connection if v in [24, 25]]): - print( - "WARNING: GPIO pins 24 and 25 are used by USB-Serial/JTAG, " + log.warning( + "GPIO pins 24 and 25 are used by USB-Serial/JTAG, " "consider using other pins for SPI flash connection." ) + def watchdog_reset(self): + log.print("Hard resetting with a watchdog...") + self.write_reg(self.RTC_CNTL_WDTWPROTECT_REG, self.RTC_CNTL_WDT_WKEY) # unlock + self.write_reg(self.RTC_CNTL_WDTCONFIG1_REG, 2000) # set WDT timeout + self.write_reg( + self.RTC_CNTL_WDTCONFIG0_REG, (1 << 31) | (5 << 28) | (1 << 8) | 2 + ) # enable WDT + self.write_reg(self.RTC_CNTL_WDTWPROTECT_REG, 0) # lock + sleep(0.5) # wait for reset to take effect -class ESP32P4StubLoader(ESP32P4ROM): - """Access class for ESP32P4 stub loader, runs on top of ROM. + def hard_reset(self): + if self.uses_usb_otg(): + self.watchdog_reset() + else: + ESPLoader.hard_reset(self) - (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 +class ESP32P4StubLoader(StubMixin, ESP32P4ROM): + """Stub loader for ESP32-P4, runs on top of ROM.""" 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 + super().__init__(rom_loader) # Initialize the mixin + if rom_loader.uses_usb_otg(): + self.ESP_RAM_BLOCK = self.USB_RAM_BLOCK + self.FLASH_WRITE_SIZE = self.USB_RAM_BLOCK ESP32P4ROM.STUB_CLASS = ESP32P4StubLoader diff --git a/tools/esptool_py/esptool/targets/esp32s2.py b/tools/esptool_py/esptool/targets/esp32s2.py index e16f532f09..dbc07921de 100644 --- a/tools/esptool_py/esptool/targets/esp32s2.py +++ b/tools/esptool_py/esptool/targets/esp32s2.py @@ -1,16 +1,15 @@ -# SPDX-FileCopyrightText: 2014-2023 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 -import os import struct -from typing import Dict +from time import sleep from .esp32 import ESP32ROM -from ..loader import ESPLoader -from ..reset import HardReset -from ..util import FatalError, NotImplementedInROMError +from ..loader import ESPLoader, StubMixin +from ..logger import log +from ..util import FatalError, NotSupportedError class ESP32S2ROM(ESP32ROM): @@ -22,7 +21,7 @@ class ESP32S2ROM(ESP32ROM): DROM_MAP_START = 0x3F000000 DROM_MAP_END = 0x3F3F0000 - CHIP_DETECT_MAGIC_VALUE = [0x000007C6] + MAGIC_VALUE = 0x000007C6 SPI_REG_BASE = 0x3F402000 SPI_USR_OFFS = 0x18 @@ -83,11 +82,17 @@ class ESP32S2ROM(ESP32ROM): USB_RAM_BLOCK = 0x800 # Max block size USB-OTG is used GPIO_STRAP_REG = 0x3F404038 - GPIO_STRAP_SPI_BOOT_MASK = 0x8 # Not download mode + GPIO_STRAP_SPI_BOOT_MASK = 1 << 3 # Not download mode GPIO_STRAP_VDDSPI_MASK = 1 << 4 RTC_CNTL_OPTION1_REG = 0x3F408128 RTC_CNTL_FORCE_DOWNLOAD_BOOT_MASK = 0x1 # Is download mode forced over USB? + RTCCNTL_BASE_REG = 0x3F408000 + RTC_CNTL_WDTCONFIG0_REG = RTCCNTL_BASE_REG + 0x0094 + RTC_CNTL_WDTCONFIG1_REG = RTCCNTL_BASE_REG + 0x0098 + RTC_CNTL_WDTWPROTECT_REG = RTCCNTL_BASE_REG + 0x00AC + RTC_CNTL_WDT_WKEY = 0x50D83AA1 + MEMORY_MAP = [ [0x00000000, 0x00010000, "PADDING"], [0x3F000000, 0x3FF80000, "DROM"], @@ -110,8 +115,7 @@ class ESP32S2ROM(ESP32ROM): UF2_FAMILY_ID = 0xBFDD4EEE - EFUSE_MAX_KEY = 5 - KEY_PURPOSES: Dict[int, str] = { + KEY_PURPOSES: dict[int, str] = { 0: "USER/EMPTY", 1: "RESERVED", 2: "XTS_AES_256_KEY_1", @@ -169,17 +173,14 @@ def get_chip_description(self): 100: "ESP32-S2R2", }.get( self.get_flash_cap() + self.get_psram_cap() * 100, - "unknown ESP32-S2", + "Unknown ESP32-S2", ) 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): - features = ["WiFi"] - - if self.secure_download_mode: - features += ["Secure Download Mode Enabled"] + features = ["Wi-Fi", "Single Core", "240MHz"] flash_version = { 0: "No Embedded Flash", @@ -197,9 +198,9 @@ def get_chip_features(self): block2_version = { 0: "No calibration in BLK2 of efuse", - 1: "ADC and temperature sensor calibration in BLK2 of efuse V1", - 2: "ADC and temperature sensor calibration in BLK2 of efuse V2", - }.get(self.get_block2_version(), "Unknown Calibration in BLK2") + 1: "ADC and temperature sensor calibration in BLK2 of eFuse V1", + 2: "ADC and temperature sensor calibration in BLK2 of eFuse V2", + }.get(self.get_block2_version(), "Unknown calibration in BLK2") features += [block2_version] return features @@ -212,9 +213,7 @@ def _get_rtc_cntl_flash_voltage(self): return None # not supported on ESP32-S2 def override_vddsdio(self, new_voltage): - raise NotImplementedInROMError( - "VDD_SDIO overrides are not supported for ESP32-S2" - ) + raise NotSupportedError(self, "Overriding VDDSDIO") def read_mac(self, mac_type="BASE_MAC"): """Read MAC from EFUSE region""" @@ -283,35 +282,30 @@ def _post_connect(self): if self.uses_usb_otg(): self.ESP_RAM_BLOCK = self.USB_RAM_BLOCK - def _check_if_can_reset(self): - """ - Check the strapping register to see if we can reset out of download mode. - """ - if os.getenv("ESPTOOL_TESTING") is not None: - print("ESPTOOL_TESTING is set, ignoring strapping mode check") - # Esptool tests over USB-OTG run with GPIO0 strapped low, - # don't complain in this case. - return - strap_reg = self.read_reg(self.GPIO_STRAP_REG) - force_dl_reg = self.read_reg(self.RTC_CNTL_OPTION1_REG) - if ( - strap_reg & self.GPIO_STRAP_SPI_BOOT_MASK == 0 - and force_dl_reg & self.RTC_CNTL_FORCE_DOWNLOAD_BOOT_MASK == 0 - ): - raise SystemExit( - f"Error: {self.get_chip_description()} chip was placed into download " - "mode using GPIO0.\nesptool.py can not exit the download mode over " - "USB. To run the app, reset the chip manually.\n" - "To suppress this note, set --after option to 'no_reset'." - ) + def watchdog_reset(self): + log.print("Hard resetting with a watchdog...") + self.write_reg(self.RTC_CNTL_WDTWPROTECT_REG, self.RTC_CNTL_WDT_WKEY) # unlock + self.write_reg(self.RTC_CNTL_WDTCONFIG1_REG, 2000) # set WDT timeout + self.write_reg( + self.RTC_CNTL_WDTCONFIG0_REG, (1 << 31) | (5 << 28) | (1 << 8) | 2 + ) # enable WDT + self.write_reg(self.RTC_CNTL_WDTWPROTECT_REG, 0) # lock + sleep(0.5) # wait for reset to take effect def hard_reset(self): uses_usb_otg = self.uses_usb_otg() if uses_usb_otg: - self._check_if_can_reset() - - print("Hard resetting via RTS pin...") - HardReset(self._port, uses_usb_otg)() + # Check the strapping register to see if we can perform a watchdog reset + strap_reg = self.read_reg(self.GPIO_STRAP_REG) + force_dl_reg = self.read_reg(self.RTC_CNTL_OPTION1_REG) + if ( + strap_reg & self.GPIO_STRAP_SPI_BOOT_MASK == 0 # GPIO0 low + and force_dl_reg & self.RTC_CNTL_FORCE_DOWNLOAD_BOOT_MASK == 0 + ): + self.watchdog_reset() + return + + ESPLoader.hard_reset(self, uses_usb_otg) def change_baud(self, baud): ESPLoader.change_baud(self, baud) @@ -320,30 +314,17 @@ def check_spi_connection(self, spi_connection): if not set(spi_connection).issubset(set(range(0, 22)) | set(range(26, 47))): raise FatalError("SPI Pin numbers must be in the range 0-21, or 26-46.") if any([v for v in spi_connection if v in [19, 20]]): - print( - "WARNING: GPIO pins 19 and 20 are used by USB-OTG, " + log.warning( + "GPIO pins 19 and 20 are used by USB-OTG, " "consider using other pins for SPI flash connection." ) -class ESP32S2StubLoader(ESP32S2ROM): - """Access class for ESP32-S2 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 +class ESP32S2StubLoader(StubMixin, ESP32S2ROM): + """Stub loader for ESP32-S2, runs on top of ROM.""" 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 - + super().__init__(rom_loader) # Initialize the mixin if rom_loader.uses_usb_otg(): self.ESP_RAM_BLOCK = self.USB_RAM_BLOCK self.FLASH_WRITE_SIZE = self.USB_RAM_BLOCK diff --git a/tools/esptool_py/esptool/targets/esp32s3.py b/tools/esptool_py/esptool/targets/esp32s3.py index fb0f23a2f3..a8d6ea2394 100644 --- a/tools/esptool_py/esptool/targets/esp32s3.py +++ b/tools/esptool_py/esptool/targets/esp32s3.py @@ -1,16 +1,15 @@ -# SPDX-FileCopyrightText: 2014-2023 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 -import os import struct -from typing import Dict +from time import sleep from .esp32 import ESP32ROM -from ..loader import ESPLoader -from ..reset import HardReset -from ..util import FatalError, NotImplementedInROMError +from ..loader import ESPLoader, StubMixin +from ..logger import log +from ..util import FatalError, NotSupportedError class ESP32S3ROM(ESP32ROM): @@ -18,8 +17,6 @@ class ESP32S3ROM(ESP32ROM): IMAGE_CHIP_ID = 9 - CHIP_DETECT_MAGIC_VALUE = [0x9] - IROM_MAP_START = 0x42000000 IROM_MAP_END = 0x44000000 DROM_MAP_START = 0x3C000000 @@ -37,6 +34,8 @@ class ESP32S3ROM(ESP32ROM): SPI_ADDR_REG_MSB = False + USES_MAGIC_VALUE = False + BOOTLOADER_FLASH_OFFSET = 0x0 SUPPORTS_ENCRYPTED_FLASH = True @@ -91,13 +90,14 @@ class ESP32S3ROM(ESP32ROM): RTC_CNTL_SWD_WKEY = 0x8F1D312A RTC_CNTL_WDTCONFIG0_REG = RTCCNTL_BASE_REG + 0x0098 + RTC_CNTL_WDTCONFIG1_REG = RTCCNTL_BASE_REG + 0x009C RTC_CNTL_WDTWPROTECT_REG = RTCCNTL_BASE_REG + 0x00B0 RTC_CNTL_WDT_WKEY = 0x50D83AA1 USB_RAM_BLOCK = 0x800 # Max block size USB-OTG is used GPIO_STRAP_REG = 0x60004038 - GPIO_STRAP_SPI_BOOT_MASK = 0x8 # Not download mode + GPIO_STRAP_SPI_BOOT_MASK = 1 << 3 # Not download mode GPIO_STRAP_VDDSPI_MASK = 1 << 4 RTC_CNTL_OPTION1_REG = 0x6000812C RTC_CNTL_FORCE_DOWNLOAD_BOOT_MASK = 0x1 # Is download mode forced over USB? @@ -126,8 +126,7 @@ class ESP32S3ROM(ESP32ROM): UF2_FAMILY_ID = 0xC47E5767 - EFUSE_MAX_KEY = 5 - KEY_PURPOSES: Dict[int, str] = { + KEY_PURPOSES: dict[int, str] = { 0: "USER/EMPTY", 1: "RESERVED", 2: "XTS_AES_256_KEY_1", @@ -195,7 +194,7 @@ def get_chip_description(self): chip_name = { 0: "ESP32-S3 (QFN56)", 1: "ESP32-S3-PICO-1 (LGA56)", - }.get(pkg_version, "unknown ESP32-S3") + }.get(pkg_version, "Unknown ESP32-S3") return f"{chip_name} (revision v{major_rev}.{minor_rev})" @@ -210,7 +209,12 @@ def get_flash_vendor(self): def get_psram_cap(self): num_word = 4 - return (self.read_reg(self.EFUSE_BLOCK1_ADDR + (4 * num_word)) >> 3) & 0x03 + psram_cap = (self.read_reg(self.EFUSE_BLOCK1_ADDR + (4 * num_word)) >> 3) & 0x03 + num_word = 5 + psram_cap_hi_bit = ( + self.read_reg(self.EFUSE_BLOCK1_ADDR + (4 * num_word)) >> 19 + ) & 0x01 + return (psram_cap_hi_bit << 2) | psram_cap def get_psram_vendor(self): num_word = 4 @@ -218,7 +222,7 @@ def get_psram_vendor(self): return {1: "AP_3v3", 2: "AP_1v8"}.get(vendor_id, "") def get_chip_features(self): - features = ["WiFi", "BLE"] + features = ["Wi-Fi", "BT 5 (LE)", "Dual Core + LP Core", "240MHz"] flash = { 0: None, @@ -232,6 +236,8 @@ def get_chip_features(self): 0: None, 1: "Embedded PSRAM 8MB", 2: "Embedded PSRAM 2MB", + 3: "Embedded PSRAM 16MB", + 4: "Embedded PSRAM 4MB", }.get(self.get_psram_cap(), "Unknown Embedded PSRAM") if psram is not None: features += [psram + f" ({self.get_psram_vendor()})"] @@ -284,9 +290,7 @@ def _get_rtc_cntl_flash_voltage(self): return None # not supported on ESP32-S3 def override_vddsdio(self, new_voltage): - raise NotImplementedInROMError( - "VDD_SDIO overrides are not supported for ESP32-S3" - ) + raise NotSupportedError(self, "Overriding VDDSDIO") def read_mac(self, mac_type="BASE_MAC"): """Read MAC from EFUSE region""" @@ -345,45 +349,41 @@ def _post_connect(self): if not self.sync_stub_detected: # Don't run if stub is reused self.disable_watchdogs() - def _check_if_can_reset(self): - """ - Check the strapping register to see if we can reset out of download mode. - """ - if os.getenv("ESPTOOL_TESTING") is not None: - print("ESPTOOL_TESTING is set, ignoring strapping mode check") - # Esptool tests over USB-OTG run with GPIO0 strapped low, - # don't complain in this case. - return - strap_reg = self.read_reg(self.GPIO_STRAP_REG) - force_dl_reg = self.read_reg(self.RTC_CNTL_OPTION1_REG) - if ( - strap_reg & self.GPIO_STRAP_SPI_BOOT_MASK == 0 - and force_dl_reg & self.RTC_CNTL_FORCE_DOWNLOAD_BOOT_MASK == 0 - ): - raise SystemExit( - f"Error: {self.get_chip_description()} chip was placed into download " - "mode using GPIO0.\nesptool.py can not exit the download mode over " - "USB. To run the app, reset the chip manually.\n" - "To suppress this note, set --after option to 'no_reset'." - ) + def watchdog_reset(self): + log.print("Hard resetting with a watchdog...") + self.write_reg(self.RTC_CNTL_WDTWPROTECT_REG, self.RTC_CNTL_WDT_WKEY) # unlock + self.write_reg(self.RTC_CNTL_WDTCONFIG1_REG, 2000) # set WDT timeout + self.write_reg( + self.RTC_CNTL_WDTCONFIG0_REG, (1 << 31) | (5 << 28) | (1 << 8) | 2 + ) # enable WDT + self.write_reg(self.RTC_CNTL_WDTWPROTECT_REG, 0) # lock + sleep(0.5) # wait for reset to take effect def hard_reset(self): - uses_usb_otg = self.uses_usb_otg() - if uses_usb_otg: - self._check_if_can_reset() - try: - # Clear force download boot mode to avoid the chip being stuck in download mode after reset - # workaround for issue: https://github.com/espressif/arduino-esp32/issues/6762 + # Clear force download boot mode to avoid chip being stuck in download mode + # after reset. Workaround for issue: + # https://github.com/espressif/arduino-esp32/issues/6762 self.write_reg( self.RTC_CNTL_OPTION1_REG, 0, self.RTC_CNTL_FORCE_DOWNLOAD_BOOT_MASK ) except Exception: - # Skip if response was not valid and proceed to reset; e.g. when monitoring while resetting + # Skip invalid response and continue reset (can happen when monitoring + # during reset) pass - - print("Hard resetting via RTS pin...") - HardReset(self._port, uses_usb_otg)() + uses_usb_otg = self.uses_usb_otg() + if uses_usb_otg: + # Check the strapping register to see if we can perform a watchdog reset + strap_reg = self.read_reg(self.GPIO_STRAP_REG) + force_dl_reg = self.read_reg(self.RTC_CNTL_OPTION1_REG) + if ( + strap_reg & self.GPIO_STRAP_SPI_BOOT_MASK == 0 # GPIO0 low + and force_dl_reg & self.RTC_CNTL_FORCE_DOWNLOAD_BOOT_MASK == 0 + ): + self.watchdog_reset() + return + + ESPLoader.hard_reset(self, uses_usb_otg) def change_baud(self, baud): ESPLoader.change_baud(self, baud) @@ -394,30 +394,17 @@ def check_spi_connection(self, spi_connection): if spi_connection[3] > 46: # hd_gpio_num must be <= SPI_GPIO_NUM_LIMIT (46) raise FatalError("SPI HD Pin number must be <= 46.") if any([v for v in spi_connection if v in [19, 20]]): - print( - "WARNING: GPIO pins 19 and 20 are used by USB-Serial/JTAG and USB-OTG, " + log.warning( + "GPIO pins 19 and 20 are used by USB-Serial/JTAG and USB-OTG, " "consider using other pins for SPI flash connection." ) -class ESP32S3StubLoader(ESP32S3ROM): - """Access class for ESP32S3 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 +class ESP32S3StubLoader(StubMixin, ESP32S3ROM): + """Stub loader for ESP32-S3, runs on top of ROM.""" 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 - + super().__init__(rom_loader) # Initialize the mixin if rom_loader.uses_usb_otg(): self.ESP_RAM_BLOCK = self.USB_RAM_BLOCK self.FLASH_WRITE_SIZE = self.USB_RAM_BLOCK diff --git a/tools/esptool_py/esptool/targets/esp32s3beta2.py b/tools/esptool_py/esptool/targets/esp32s3beta2.py deleted file mode 100644 index f91bb3cb24..0000000000 --- a/tools/esptool_py/esptool/targets/esp32s3beta2.py +++ /dev/null @@ -1,37 +0,0 @@ -# SPDX-FileCopyrightText: 2014-2022 Fredrik Ahlberg, Angus Gratton, -# Espressif Systems (Shanghai) CO LTD, other contributors as noted. -# -# SPDX-License-Identifier: GPL-2.0-or-later - -from .esp32s3 import ESP32S3ROM - - -class ESP32S3BETA2ROM(ESP32S3ROM): - CHIP_NAME = "ESP32-S3(beta2)" - IMAGE_CHIP_ID = 4 - - CHIP_DETECT_MAGIC_VALUE = [0xEB004136] - - EFUSE_BASE = 0x6001A000 # BLOCK0 read base address - - -class ESP32S3BETA2StubLoader(ESP32S3BETA2ROM): - """Access class for ESP32S3 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 - - -ESP32S3BETA2ROM.STUB_CLASS = ESP32S3BETA2StubLoader diff --git a/tools/esptool_py/esptool/targets/esp8266.py b/tools/esptool_py/esptool/targets/esp8266.py index 9f8d7c17d5..31d278b266 100644 --- a/tools/esptool_py/esptool/targets/esp8266.py +++ b/tools/esptool_py/esptool/targets/esp8266.py @@ -1,19 +1,18 @@ -# 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 -from ..loader import ESPLoader -from ..util import FatalError, NotSupportedError +from ..loader import ESPLoader, StubMixin +from ..util import FatalError class ESP8266ROM(ESPLoader): """Access class for ESP8266 ROM bootloader""" CHIP_NAME = "ESP8266" - IS_STUB = False - CHIP_DETECT_MAGIC_VALUE = [0xFFF0C101] + MAGIC_VALUE = 0xFFF0C101 # OTP ROM addresses ESP_OTP_MAC0 = 0x3FF00050 @@ -107,7 +106,7 @@ def get_chip_description(self): return "ESP8266EX" def get_chip_features(self): - features = ["WiFi"] + features = ["Wi-Fi", "160MHz"] if "ESP8285" in self.get_chip_description(): features += ["Embedded Flash"] return features @@ -169,31 +168,9 @@ def get_erase_size(self, offset, size): else: return (num_sectors - head_sectors) * sector_size - def get_flash_voltage(self): - pass # not supported on ESP8266 - def override_vddsdio(self, new_voltage): - raise NotSupportedError(self, "Overriding VDDSDIO") - - def check_spi_connection(self, spi_connection): - raise NotSupportedError(self, "Setting --spi-connection") - - def get_secure_boot_enabled(self): - return False # ESP8266 doesn't have security features - - -class ESP8266StubLoader(ESP8266ROM): - """Access class for ESP8266 stub loader, runs on top of ROM.""" - - FLASH_WRITE_SIZE = 0x4000 # matches MAX_WRITE_BLOCK in stub_loader.c - 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 +class ESP8266StubLoader(StubMixin, ESP8266ROM): + """Stub loader for ESP8266, runs on top of ROM.""" def get_erase_size(self, offset, size): return size # stub doesn't have same size bug as ROM loader diff --git a/tools/esptool_py/esptool/targets/stub_flasher/1/README.md b/tools/esptool_py/esptool/targets/stub_flasher/1/README.md index 44e3ca3157..8284ab5177 100644 --- a/tools/esptool_py/esptool/targets/stub_flasher/1/README.md +++ b/tools/esptool_py/esptool/targets/stub_flasher/1/README.md @@ -1,3 +1,3 @@ # Licensing -The binaries in JSON format distributed in this directory are released as Free Software under GNU General Public License Version 2 or later. They were released at https://github.com/espressif/esptool-legacy-flasher-stub/releases/tag/v1.3.0 from where the sources can be obtained. +The binaries in JSON format distributed in this directory are released as Free Software under GNU General Public License Version 2 or later. They were released at https://github.com/espressif/esptool-legacy-flasher-stub/releases/tag/v1.6.0 from where the sources can be obtained. diff --git a/tools/esptool_py/esptool/targets/stub_flasher/1/esp32.json b/tools/esptool_py/esptool/targets/stub_flasher/1/esp32.json index 56221e30bd..f1e1d8d8c3 100644 --- a/tools/esptool_py/esptool/targets/stub_flasher/1/esp32.json +++ b/tools/esptool_py/esptool/targets/stub_flasher/1/esp32.json @@ -1,8 +1,8 @@ { - "entry": 1074521580, - "text": "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", + "entry": 1074521568, + "text": "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", "text_start": 1074520064, - "data": "DMD8P+znC0B/6AtAZ+0LQAbpC0Cf6AtABukLQGXpC0CC6gtA9OoLQJ3qC0CV5wtAGuoLQHTqC0CI6QtAGOsLQLDpC0AY6wtAbegLQMroC0AG6QtAZekLQIXoC0DI6wtAKe0LQLjmC0BL7QtAuOYLQLjmC0C45gtAuOYLQLjmC0C45gtAuOYLQLjmC0Bv6wtAuOYLQEnsC0Ap7QtA", + "data": "DMD8P93nC0Bz6AtAK+0LQPXoC0CW6AtA9egLQE7pC0BT6gtAyeoLQG7qC0CJ5wtA/+kLQEjqC0Bs6QtA6uoLQJTpC0Dq6gtAaugLQLvoC0D16AtATukLQHzoC0Cc6wtA7+wLQKrmC0AP7QtAquYLQKrmC0Cq5gtAquYLQKrmC0Cq5gtAquYLQKrmC0BL6wtAquYLQB7sC0Dv7AtA", "data_start": 1073605544, "bss_start": 1073528832 } \ No newline at end of file diff --git a/tools/esptool_py/esptool/targets/stub_flasher/1/esp32c2.json b/tools/esptool_py/esptool/targets/stub_flasher/1/esp32c2.json index f10ec7b48e..6a7ba77d33 100644 --- a/tools/esptool_py/esptool/targets/stub_flasher/1/esp32c2.json +++ b/tools/esptool_py/esptool/targets/stub_flasher/1/esp32c2.json @@ -1,8 +1,8 @@ { - "entry": 1077413304, - "text": "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", + "entry": 1077413160, + "text": "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", "text_start": 1077411840, - "data": "DEDKP+AIOEAsCThAhAk4QFIKOEC+CjhAbAo4QKgHOEAOCjhATgo4QJgJOEBYBzhAzAk4QFgHOEC6CDhA/gg4QCwJOECECThAzAg4QBIIOEBCCDhAyAg4QBYNOEAsCThA1gs4QMoMOECkBjhA9Aw4QKQGOECkBjhApAY4QKQGOECkBjhApAY4QKQGOECkBjhAcgs4QKQGOEDyCzhAygw4QA==", + "data": "DEDKPygIOECACDhAEAk4QPIJOEBeCjhADAo4QBAHOECuCThA7gk4QCwJOEDABjhAYAk4QMAGOEACCDhARgg4QIAIOEAQCThAFAg4QD4HOEByBzhAEAg4QLQMOECACDhAeAs4QGgMOEAMBjhAkgw4QAwGOEAMBjhADAY4QAwGOEAMBjhADAY4QAwGOEAMBjhAFAs4QAwGOECUCzhAaAw4QA==", "data_start": 1070295976, "bss_start": 1070219264 } \ No newline at end of file diff --git a/tools/esptool_py/esptool/targets/stub_flasher/1/esp32c3.json b/tools/esptool_py/esptool/targets/stub_flasher/1/esp32c3.json index 788ae646df..5c1aeebf57 100644 --- a/tools/esptool_py/esptool/targets/stub_flasher/1/esp32c3.json +++ b/tools/esptool_py/esptool/targets/stub_flasher/1/esp32c3.json @@ -1,8 +1,8 @@ { - "entry": 1077413584, - "text": "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", + "entry": 1077413408, + "text": "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", "text_start": 1077411840, - "data": "GEDIP8AKOEAQCzhAaAs4QDYMOECiDDhAUAw4QHIJOEDyCzhAMgw4QHwLOEAiCThAsAs4QCIJOECaCjhA4Ao4QBALOEBoCzhArAo4QNYJOEAgCjhAqAo4QPoOOEAQCzhAug04QLIOOEBiCDhA2g44QGIIOEBiCDhAYgg4QGIIOEBiCDhAYgg4QGIIOEBiCDhAVg04QGIIOEDYDThAsg44QA==", + "data": "GEDIP+gJOEBECjhA1Ao4QLYLOEAiDDhA0As4QLwIOEByCzhAsgs4QPAKOEBsCDhAJAs4QGwIOEDCCThACAo4QEQKOEDUCjhA1Ak4QOQIOEAwCThA0Ak4QHYOOEBECjhAPA04QC4OOECsBzhAVg44QKwHOECsBzhArAc4QKwHOECsBzhArAc4QKwHOECsBzhA2Aw4QKwHOEBaDThALg44QA==", "data_start": 1070164916, "bss_start": 1070088192 } \ No newline at end of file diff --git a/tools/esptool_py/esptool/targets/stub_flasher/1/esp32c5.json b/tools/esptool_py/esptool/targets/stub_flasher/1/esp32c5.json index 871a95d5e1..b9e8069097 100644 --- a/tools/esptool_py/esptool/targets/stub_flasher/1/esp32c5.json +++ b/tools/esptool_py/esptool/targets/stub_flasher/1/esp32c5.json @@ -1,8 +1,8 @@ { - "entry": 1082132164, - "text": "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", + "entry": 1082131984, + "text": "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", "text_start": 1082130432, - "data": "FACEQG4KgEC+CoBAFguAQOQLgEBQDIBA/guAQDoJgECgC4BA4AuAQCoLgEDqCIBAXguAQOoIgEBICoBAjgqAQL4KgEAWC4BAWgqAQJ4JgEDOCYBAVgqAQKgOgEC+CoBAaA2AQGAOgEAqCIBAiA6AQCoIgEAqCIBAKgiAQCoIgEAqCIBAKgiAQCoIgEAqCIBABA2AQCoIgECGDYBAYA6AQA==", + "data": "FACEQJAJgEDsCYBAfAqAQF4LgEDKC4BAeAuAQH4IgEAaC4BAWguAQJgKgEAuCIBAzAqAQC4IgEBqCYBAsAmAQOwJgEB8CoBAfAmAQKYIgEDaCIBAeAmAQB4OgEDsCYBA5AyAQNYNgEBuB4BA/g2AQG4HgEBuB4BAbgeAQG4HgEBuB4BAbgeAQG4HgEBuB4BAgAyAQG4HgEACDYBA1g2AQA==", "data_start": 1082469296, "bss_start": 1082392576 } \ No newline at end of file diff --git a/tools/esptool_py/esptool/targets/stub_flasher/1/esp32c5beta3.json b/tools/esptool_py/esptool/targets/stub_flasher/1/esp32c5beta3.json deleted file mode 100644 index c41e549ca9..0000000000 --- a/tools/esptool_py/esptool/targets/stub_flasher/1/esp32c5beta3.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "entry": 1082131910, - "text": "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", - "text_start": 1082130432, - "data": "DACEQO4IgEA6CYBAkgmAQGAKgEDMCoBAegqAQLYHgEAcCoBAXAqAQKYJgEBmB4BA2gmAQGYHgEDICIBADAmAQDoJgECSCYBA2giAQCAIgEBQCIBA1giAQCQNgEA6CYBA5AuAQNgMgECyBoBAAg2AQLIGgECyBoBAsgaAQLIGgECyBoBAsgaAQLIGgECyBoBAgAuAQLIGgEAADIBA2AyAQA==", - "data_start": 1082469288, - "bss_start": 1082392576 -} \ No newline at end of file diff --git a/tools/esptool_py/esptool/targets/stub_flasher/1/esp32c6.json b/tools/esptool_py/esptool/targets/stub_flasher/1/esp32c6.json index b903b35215..ff82d3e5ab 100644 --- a/tools/esptool_py/esptool/targets/stub_flasher/1/esp32c6.json +++ b/tools/esptool_py/esptool/targets/stub_flasher/1/esp32c6.json @@ -1,8 +1,8 @@ { - "entry": 1082132164, - "text": "QREixCbCBsa39wBgEUc3BIRA2Mu39ABgEwQEANxAkYuR57JAIkSSREEBgoCIQBxAE3X1D4KX3bcBEbcHAGBOxoOphwBKyDcJhEAmylLEBs4izLcEAGB9WhMJCQDATBN09A8N4PJAYkQjqDQBQknSRLJJIkoFYYKAiECDJwkAE3X1D4KXfRTjGUT/yb8TBwAMlEGqh2MY5QCFR4XGI6AFAHlVgoAFR2OH5gAJRmONxgB9VYKAQgUTB7ANQYVjlecCiUecwfW3kwbADWMW1QCYwRMFAAyCgJMG0A19VWOV1wCYwRMFsA2CgLc1hUBBEZOFhboGxmE/Y0UFBrc3hUCThweyA6cHCAPWRwgTdfUPkwYWAMIGwYIjktcIMpcjAKcAA9dHCJFnk4cHBGMe9wI3t4RAEwcHsqFnupcDpgcIt/aEQLc3hUCThweyk4YGtmMf5gAjpscII6DXCCOSBwghoPlX4wb1/LJAQQGCgCOm1wgjoOcI3bc3NwBgfEudi/X/NycAYHxLnYv1/4KAQREGxt03tzcAYCOmBwI3BwAImMOYQ33/yFeyQBNF9f8FiUEBgoBBEQbG2T993TcHAEC3NwBgmMM3NwBgHEP9/7JAQQGCgEERIsQ3hIRAkwdEAUrAA6kHAQbGJsJjCgkERTc5xb1HEwREAYFEY9YnAQREvYiTtBQAfTeFPxxENwaAABOXxwCZ4DcGAAG39v8AdY+3NgBg2MKQwphCff9BR5HgBUczCelAupcjKCQBHMSyQCJEkkQCSUEBgoABEQbOIswlNzcEzj9sABMFRP+XAID/54Cg8qqHBUWV57JHk/cHID7GiTc3NwBgHEe3BkAAEwVE/9WPHMeyRZcAgP/ngCDwMzWgAPJAYkQFYYKAQRG3h4RABsaTh0cBBUcjgOcAE9fFAJjHBWd9F8zDyMf5jTqVqpWxgYzLI6oHAEE3GcETBVAMskBBAYKAAREizDeEhECTB0QBJsrER07GBs5KyKqJEwREAWPzlQCuhKnAAylEACaZE1nJABxIY1XwABxEY175ArU9fd1IQCaGzoWXAID/54Ag4xN19Q8BxZMHQAxcyFxAppdcwFxEhY9cxPJAYkTSREJJskkFYYKAaTVtv0ERBsaXAID/54BA1gNFhQGyQHUVEzUVAEEBgoBBEQbGxTcNxbcHhECThwcA1EOZzjdnCWATBwcRHEM3Bv3/fRbxjzcGAwDxjtWPHMOyQEEBgoBBEQbGbTcRwQ1FskBBARcDgP9nAIPMQREGxibCIsSqhJcAgP/ngODJWTcNyTcHhECTBgcAg9eGABMEBwCFB8IHwYMjlPYAkwYADGOG1AATB+ADY3X3AG03IxQEALJAIkSSREEBgoBBEQbGEwcADGMa5QATBbANRTcTBcANskBBAVm/EwewDeMb5f5xNxMF0A31t0ERIsQmwgbGKoSzBLUAYxeUALJAIkSSREEBgoADRQQABQRNP+23NXEmy07H/XKFaf10Is1KyVLFVsMGz5OEhPoWkZOHCQemlxgIs4TnACqJJoUuhJcAgP/ngIAsk4cJBxgIBWq6l7OKR0Ex5AVnfXWTBYX6kwcHBxMFhfkUCKqXM4XXAJMHBweul7OF1wAqxpcAgP/ngEApMkXBRZU3AUWFYhaR+kBqRNpESkm6SSpKmkoNYYKAooljc4oAhWlOhtaFSoWXAID/54DAxRN19Q8B7U6G1oUmhZcAgP/ngIAkTpkzBDRBUbcTBTAGVb8TBQAMSb0xcf1yBWdO11LVVtNezwbfIt0m20rZWtFizWbLaslux/13FpETBwcHPpccCLqXPsYjqgf4qokuirKKtov1M5MHAAIZwbcHAgA+hZcAgP/ngCAdhWdj5VcTBWR9eRMJifqTBwQHypcYCDOJ5wBKhZcAgP/ngKAbfXsTDDv5kwyL+RMHBAeTBwQHFAhil+aXgUQzDNcAs4zXAFJNY3xNCWPxpANBqJk/ooUIAY01uTcihgwBSoWXAID/54CAF6KZopRj9UQDs4ekQWPxdwMzBJpAY/OKAFaEIoYMAU6FlwCA/+eAALUTdfUPVd0CzAFEeV2NTaMJAQBihZcAgP/ngECkffkDRTEB5oWFNGNPBQDj4o3+hWeThwcHopcYCLqX2pcjiqf4BQTxt+MVpf2RR+MF9PYFZ311kwcHB5MFhfoTBYX5FAiqlzOF1wCTBwcHrpezhdcAKsaXAID/54CgDXE9MkXBRWUzUT3BMbcHAgAZ4ZMHAAI+hZcAgP/ngKAKhWIWkfpQalTaVEpZulkqWppaClv6S2pM2kxKTbpNKWGCgLdXQUkZcZOH94QBRYbeotym2srYztbS1NbS2tDezuLM5srqyO7GPs6XAID/54CAnaE5DcE3ZwlgEwcHERxDtwaEQCOi9gC3Bv3//Rb1j8Fm1Y8cwxU5Bc23JwtgN0fYUJOGh8ETBxeqmMIThgfAIyAGACOgBgCThgfCmMKTh8fBmEM3BgQAUY+YwyOgBgC3B4RANzeFQJOHBwATBwe7IaAjoAcAkQfj7ef+RTuRRWgIdTllM7e3hECThweyIWc+lyMg9wi3B4BANwmEQJOHhw4jIPkAtzmFQEU+EwkJAJOJCbJjBQUQtwcBYEVHI6DnDIVFRUWXAID/54AA9rcFgEABRpOFBQBFRZcAgP/ngAD3t/cAYBFHmMs3BQIAlwCA/+eAQPa3FwlgiF+BRbeEhEBxiWEVEzUVAJcAgP/ngACewWf9FxMHABCFZkFmtwUAAQFFk4REAbcKhEANapcAgP/ngACUE4tKASaag6fJCPXfg6vJCIVHI6YJCCMC8QKDxxsACUcjE+ECowLxAgLUTUdjgecIUUdjj+cGKUdjn+cAg8c7AAPHKwCiB9mPEUdjlucAg6eLAJxDPtRFMaFFSBB1NoPHOwADxysAogfZjxFnQQdjdPcEEwWwDRk+EwXADQE+EwXgDik2jTlBt7cFgEABRpOFhQMVRZcAgP/ngADoNwcAYFxHEwUAApPnFxBcxzG3yUcjE/ECTbcDxxsA0UZj5+YChUZj5uYAAUwTBPAPhah5FxN39w/JRuPo5v63NoVACgeThka7NpcYQwKHkwYHA5P29g8RRuNp1vwTB/cCE3f3D41GY+vmCLc2hUAKB5OGBsA2lxhDAocTB0ACY5jnEALUHUQBRaU0AUVVPPE26TahRUgQfRTRPHX0AUwBRBN19A9xPBN1/A9ZPH024x4E6oPHGwBJR2No9zAJR+N29+r1F5P39w89R+Ng9+o3N4VAigcTBwfBupecQ4KHBUSd63AQgUUBRZfwf//ngABxHeHRRWgQnTwBRDGoBUSB75fwf//ngAB2MzSgACmgIUdjhecABUQBTGG3A6yLAAOkywCzZ4wA0gf19+/wv4V98cFsIpz9HH19MwWMQFXcs3eVAZXjwWwzBYxAY+aMAv18MwWMQFXQMYGX8H//54CAclX5ZpT1tzGBl/B//+eAgHFV8WqU0bdBgZfwf//ngMBwUfkzBJRBwbchR+OJ5/ABTBMEAAwxt0FHzb9BRwVE45zn9oOlywADpYsA5TKxv0FHBUTjkuf2A6cLAZFnY+rnHoOlSwEDpYsA7/D/gDW/QUcFROOS5/SDpwsBEWdjavccA6fLAIOlSwEDpYsAM4TnAu/wb/4jrAQAIySKsDG3A8cEAGMDBxQDp4sAwRcTBAAMYxP3AMBIAUeTBvAOY0b3AoPHWwADx0sAAUyiB9mPA8drAEIHXY+Dx3sA4gfZj+OB9uYTBBAMqb0zhusAA0aGAQUHsY7ht4PHBAD9x9xEY50HFMBII4AEAH21YUdjlucCg6fLAQOniwGDpksBA6YLAYOlywADpYsAl/B//+eAQGEqjDM0oAAptQFMBUQRtRFHBUTjmufmt5cAYLRfZXd9FwVm+Y7RjgOliwC037RXgUX5jtGOtNf0X/mO0Y703/RTdY9Rj/jTl/B//+eAIGQpvRP39wDjFQfqk9xHABOEiwABTH1d43Sc20hEl/B//+eAIEgYRFRAEED5jmMHpwEcQhNH9/99j9mOFMIFDEEE2b8RR6W1QUcFROOX596Dp4sAA6dLASMo+QAjJukAdbuDJckAwReR5YnPAUwTBGAMibsDJwkBY2b3BhP3NwDjGQfiAygJAQFGAUczBehAs4blAGNp9wDjBAbSIyipACMm2QAxuzOG6wAQThEHkMIFRum/IUcFROOR59gDJAkBGcATBIAMIygJACMmCQAzNIAApbMBTBMEIAztsQFMEwSADM2xAUwTBJAM6bkTByANY4PnDBMHQA3jm+e4A8Q7AIPHKwAiBF2Ml/B//+eAQEcDrMQAQRRjc4QBIozjCQy2wEBilDGAnEhjVfAAnERjW/QK7/Cvy3XdyEBihpOFiwGX8H//54BAQwHFkwdADNzI3EDil9zA3ESzh4dB3MSX8H//54AgQiW2CWUTBQVxA6zLAAOkiwCX8H//54CgMrcHAGDYS7cGAAHBFpNXRwESB3WPvYvZj7OHhwMBRbPVhwKX8H//54DAMxMFgD6X8H//54BAL+m8g6ZLAQOmCwGDpcsAA6WLAO/w7/vRtIPFOwCDxysAE4WLAaIF3Y3BFe/wj9V1tO/w78Q9vwPEOwCDxysAE4yLASIEXYzcREEUzeORR4VLY/+HCJMHkAzcyEG0A6cNACLQBUizh+xAPtaDJ4qwY3P0AA1IQsY6xO/wb8AiRzJIN4WEQOKFfBCThkoBEBATBcUCl/B//+eAIDE3t4RAkwhHAYJXA6eIsIOlDQAdjB2PPpyyVyOk6LCqi76VI6C9AJOHSgGdjQHFoWdjl/UAWoXv8C/LI6BtAQnE3ESZw+NPcPdj3wsAkwdwDL23hUu3PYVAt4yEQJONDbuTjEwB6b/jnQuc3ETjigeckweADKm3g6eLAOOTB5zv8C/TCWUTBQVxl/B//+eAoBzv8K/Ol/B//+eA4CBVsgOkywDjDwSY7/Cv0BMFgD6X8H//54BAGu/wT8wClFGy7/DPy/ZQZlTWVEZZtlkmWpZaBlv2S2ZM1kxGTbZNCWGCgAAA", + "entry": 1082131984, + "text": "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", "text_start": 1082130432, - "data": "FACEQHIKgEDCCoBAGguAQOgLgEBUDIBAAgyAQD4JgECkC4BA5AuAQC4LgEDuCIBAYguAQO4IgEBMCoBAkgqAQMIKgEAaC4BAXgqAQKIJgEDSCYBAWgqAQKwOgEDCCoBAbA2AQGQOgEAuCIBAjA6AQC4IgEAuCIBALgiAQC4IgEAuCIBALgiAQC4IgEAuCIBACA2AQC4IgECKDYBAZA6AQA==", + "data": "FACEQJIJgEDuCYBAfgqAQGALgEDMC4BAeguAQIAIgEAcC4BAXAuAQJoKgEAwCIBAzgqAQDAIgEBsCYBAsgmAQO4JgEB+CoBAfgmAQKgIgEDcCIBAegmAQCAOgEDuCYBA5gyAQNgNgEBwB4BAAA6AQHAHgEBwB4BAcAeAQHAHgEBwB4BAcAeAQHAHgEBwB4BAggyAQHAHgEAEDYBA2A2AQA==", "data_start": 1082469296, "bss_start": 1082392576 } \ No newline at end of file diff --git a/tools/esptool_py/esptool/targets/stub_flasher/1/esp32c61.json b/tools/esptool_py/esptool/targets/stub_flasher/1/esp32c61.json index 2a95414c8d..354ce70118 100644 --- a/tools/esptool_py/esptool/targets/stub_flasher/1/esp32c61.json +++ b/tools/esptool_py/esptool/targets/stub_flasher/1/esp32c61.json @@ -1,8 +1,8 @@ { - "entry": 1082132164, - "text": "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", + "entry": 1082131984, + "text": "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", "text_start": 1082130432, - "data": "FECAQHQKgEDECoBAHAuAQOoLgEBWDIBABAyAQEAJgECmC4BA5guAQDALgEDwCIBAZAuAQPAIgEBOCoBAlAqAQMQKgEAcC4BAYAqAQKQJgEDUCYBAXAqAQK4OgEDECoBAbg2AQGYOgEAwCIBAjg6AQDAIgEAwCIBAMAiAQDAIgEAwCIBAMAiAQDAIgEAwCIBACg2AQDAIgECMDYBAZg6AQA==", + "data": "FECAQJYJgEDyCYBAggqAQGQLgEDQC4BAfguAQIQIgEAgC4BAYAuAQJ4KgEA0CIBA0gqAQDQIgEBwCYBAtgmAQPIJgECCCoBAggmAQKwIgEDgCIBAfgmAQCQOgEDyCYBA6gyAQNwNgEB0B4BABA6AQHQHgEB0B4BAdAeAQHQHgEB0B4BAdAeAQHQHgEB0B4BAhgyAQHQHgEAIDYBA3A2AQA==", "data_start": 1082223536, "bss_start": 1082146816 } \ No newline at end of file diff --git a/tools/esptool_py/esptool/targets/stub_flasher/1/esp32c6beta.json b/tools/esptool_py/esptool/targets/stub_flasher/1/esp32c6beta.json deleted file mode 100644 index 7fd5c0ec6c..0000000000 --- a/tools/esptool_py/esptool/targets/stub_flasher/1/esp32c6beta.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "entry": 1077413318, - "text": "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", - "text_start": 1077411840, - "data": "DEDIP/gIOEBECThAnAk4QGoKOEDWCjhAhAo4QMAHOEAmCjhAZgo4QLAJOEBwBzhA5Ak4QHAHOEDSCDhAFgk4QEQJOECcCThA5Ag4QCoIOEBaCDhA4Ag4QC4NOEBECThA7gs4QOIMOEC8BjhADA04QLwGOEC8BjhAvAY4QLwGOEC8BjhAvAY4QLwGOEC8BjhAigs4QLwGOEAKDDhA4gw4QA==", - "data_start": 1070164904, - "bss_start": 1070088192 -} \ No newline at end of file diff --git a/tools/esptool_py/esptool/targets/stub_flasher/1/esp32h2.json b/tools/esptool_py/esptool/targets/stub_flasher/1/esp32h2.json index 24964cde64..66c4998911 100644 --- a/tools/esptool_py/esptool/targets/stub_flasher/1/esp32h2.json +++ b/tools/esptool_py/esptool/targets/stub_flasher/1/esp32h2.json @@ -1,8 +1,8 @@ { - "entry": 1082132164, - "text": "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", + "entry": 1082131984, + "text": "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", "text_start": 1082130432, - "data": "FACDQHIKgEDCCoBAGguAQOgLgEBUDIBAAgyAQD4JgECkC4BA5AuAQC4LgEDuCIBAYguAQO4IgEBMCoBAkgqAQMIKgEAaC4BAXgqAQKIJgEDSCYBAWgqAQKwOgEDCCoBAbA2AQGQOgEAuCIBAjA6AQC4IgEAuCIBALgiAQC4IgEAuCIBALgiAQC4IgEAuCIBACA2AQC4IgECKDYBAZA6AQA==", + "data": "FACDQJIJgEDuCYBAfgqAQGALgEDMC4BAeguAQIAIgEAcC4BAXAuAQJoKgEAwCIBAzgqAQDAIgEBsCYBAsgmAQO4JgEB+CoBAfgmAQKgIgEDcCIBAegmAQCAOgEDuCYBA5gyAQNgNgEBwB4BAAA6AQHAHgEBwB4BAcAeAQHAHgEBwB4BAcAeAQHAHgEBwB4BAggyAQHAHgEAEDYBA2A2AQA==", "data_start": 1082403760, "bss_start": 1082327040 } \ No newline at end of file diff --git a/tools/esptool_py/esptool/targets/stub_flasher/1/esp32h2beta1.json b/tools/esptool_py/esptool/targets/stub_flasher/1/esp32h2beta1.json deleted file mode 100644 index 03e110c5c2..0000000000 --- a/tools/esptool_py/esptool/targets/stub_flasher/1/esp32h2beta1.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "entry": 1077413318, - "text": "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", - "text_start": 1077411840, - "data": "DEDIP/gIOEBECThAnAk4QGoKOEDWCjhAhAo4QMAHOEAmCjhAZgo4QLAJOEBwBzhA5Ak4QHAHOEDSCDhAFgk4QEQJOECcCThA5Ag4QCoIOEBaCDhA4Ag4QC4NOEBECThA7gs4QOIMOEC8BjhADA04QLwGOEC8BjhAvAY4QLwGOEC8BjhAvAY4QLwGOEC8BjhAigs4QLwGOEAKDDhA4gw4QA==", - "data_start": 1070164904, - "bss_start": 1070088192 -} \ No newline at end of file diff --git a/tools/esptool_py/esptool/targets/stub_flasher/1/esp32h2beta2.json b/tools/esptool_py/esptool/targets/stub_flasher/1/esp32h2beta2.json deleted file mode 100644 index ce3afe7ac0..0000000000 --- a/tools/esptool_py/esptool/targets/stub_flasher/1/esp32h2beta2.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "entry": 1077413318, - "text": "ARG3BwBgTsaDqYcASsg3Scg/JspSxAbOIsy3BABgfVoTCQkAwEwTdPQ/DeDyQGJEI6g0AUJJ0kSySSJKBWGCgIhAgycJABN19Q+Cl30U4xlE/8m/EwcADJRBqodjGOUAhUeFxiOgBQB5VYKABUdjh+YACUZjjcYAfVWCgEIFEwewDUGFY5XnAolHnMH1t5MGwA1jFtUAmMETBQAMgoCTBtANfVVjldcAmMETBbANgoC3dck/QRGThQW6BsZhP2NFBQa3d8k/k4eHsQOnBwgD1kcIE3X1D5MGFgDCBsGCI5LXCDKXIwCnAAPXRwiRZ5OHBwRjHvcCN/fIPxMHh7GhZ7qXA6YHCLc2yT+3d8k/k4eHsZOGhrVjH+YAI6bHCCOg1wgjkgcIIaD5V+MG9fyyQEEBgoAjptcII6DnCN23NycAYHxLnYv1/zc3AGB8S52L9f+CgEERBsbdN7cnAGAjpgcCNwcACJjDmEN9/8hXskATRfX/BYlBAYKAQREGxtk/fd03BwBAtycAYJjDNycAYBxD/f+yQEEBgoBBESLEN8TIP5MHxABKwAOpBwEGxibCYwoJBEU3OcW9RxMExACBRGPWJwEERL2Ik7QUAH03hT8cRDcGgAATl8cAmeA3BgABt/b/AHWPtyYAYNjCkMKYQn3/QUeR4AVHMwnpQLqXIygkARzEskAiRJJEAklBAYKAQREGxhMHAAxjEOUCEwWwDZcAyP/ngIDjEwXADbJAQQEXA8j/ZwCD4hMHsA3jGOX+lwDI/+eAgOETBdANxbdBESLEJsIGxiqEswS1AGMXlACyQCJEkkRBAYKAA0UEAAUERTfttxMFAAwXA8j/ZwAD3jVxJstOx/1yhWn9dCLNSslSxVbDBs+ThIT6FpGThwkHppcYCLOE5wAqiSaFLoSXAMj/54BgWpOHCQcYCAVqupezikdBMeQFZ311kwWF+pMHBwcTBYX5FAiqlzOF1wCTBwcHrpezhdcAKsaXAMj/54AgVzJFwUWhPwFFhWIWkfpAakTaREpJukkqSppKDWGCgKKJY3OKAIVpTobWhUqFlwDI/+eA4OITdfUPAe1OhtaFJoWXAMj/54BgUk6ZMwQ0QVG3EwUwBlW/MXH9ck7XUtVW017PBt8i3SbbStla0WLNZstqyW7HqokWkRMFAAIuirKKtosCypcAyP/ngCBNhWdj4FcThWR9dBMEhPqThwQHopcYCDOE5wAihZcAyP/ngKBLfXsTDDv5kwyL+ROHBAeThwQHFAhil+aXAUkzDNcAs4zXAFJNY3xNCWNxqQNBqFU1poUIAaU9cT0mhgwBIoWXAMj/54CAR6aZJpljdUkDswepQWPxdwOzBCpBY/OaANaEJoYMAU6FlwDI/+eAQNQTdfUPVd0CzIFEeV2NTaMJAQBihZcAyP/ngMDDffkDRTEB5oUFMWNPBQDj4p3+hWeThwcHppcYCLqX2pcjiqf4hQTxt+MVpf2RR+OF9PYFZ311kwcHB5MFhfoTBYX5FAiqlzOF1wCTBwcHrpezhdcAKsaXAMj/54CgPe0zMkXBRX07zTMTBQAClwDI/+eAQDuFYhaR+lBqVNpUSlm6WSpamloKW/pLakzaTEpNuk0pYYKAAREGziLMnTk3BM4/bAATBQT/lwDI/+eAwMqqhwVFleeyR5P3ByA+xkE5NycAYBxHtwZAABMFBP/VjxzHskWXAMj/54BAyDM1oADyQGJEBWGCgEERt8fIPwbGk4fHAAVHI4DnABPXxQCYxwVnfRfMw8jH+Y06laqVsYGMyyOqBwBBNxnBEwVQDLJAQQGCgAERIsw3xMg/kwfEACbKxEdOxgbOSsiqiRMExABj85UAroSpwAMpRAAmmRNZyQAcSGNV8AAcRGNe+QLpNn3dSEAmhs6FlwDI/+eAQLsTdfUPAcWTB0AMXMhcQKaXXMBcRIWPXMTyQGJE0kRCSbJJBWGCgOE+bb+3V0FJGXGTh/eEAUU+zobeotym2srYztbS1NbS2tDezuLM5srqyO7GlwDI/+eAIK23R8g/N3fJP5OHBwATB4e6Y+XnFK0xkUVoCD05jTG398g/k4eHsSFnPpcjIPcItwU4QLcHOECThwcLAUaThQUAN0nIPxVFIyD5AJcAyP/ngEAgNwcAYFxHEwUAArd5yT+T5xcQXMeXAMj/54AAH5cAyP/ngAAwt0cAYJxfk4mJsRMJCQAJ5fGL4RcTtRcAgUWXAMj/54AgsMFnt8TIP/0XEwcAEIVmQWa3BQABAUWThMQAt0rIPw1qlwDI/+eA4KoTi8oAJpqDp8kI9d+Dq8kIhUcjpgkIIwLxAoPHGwAJRyMT4QKjAvECAtRNR2OL5wZRR2OJ5wYpR2Of5wCDxzsAA8crAKIH2Y8RR2OW5wCDp4sAnEM+1KU2oUVIEDU+g8c7AAPHKwCiB9mPEWdBB2N+9wITBbANlwDI/+eAwJITBcANlwDI/+eAAJITBeAOlwDI/+eAQJElNr23I6AHAJEHRb3JRyMT8QJ9twPHGwDRRmPn5gKFRmPm5gABTBME8A+dqHkXE3f3D8lG4+jm/rd2yT8KB5OGxro2lxhDAoeTBgcDk/b2DxFG42nW/BMH9wITd/cPjUZj7uYIt3bJPwoHk4aGvzaXGEMChxMHQAJjmucQAtQdRAFFlwDI/+eAoIgBRSU8aTxhPKFFSBB9FK00ffABTAFEE3X0DwU0E3X8Dyk8tTzjEQTsg8cbAElHY2X3MAlH43n36vUXk/f3Dz1H42P36jd3yT+KBxMHh8C6l5xDgocFRJ3rcBCBRQFFlwDI/+eAQIgd4dFFaBAxNAFEMagFRIHvlwDI/+eAQI0zNKAAKaAhR2OF5wAFRAFMYbcDrIsAA6TLALNnjADSB/X3sTFl9cFsIpz9HH19MwWMQF3cs3eVAZXjwWwzBYxAY+aMAv18MwWMQF3QMYGXAMj/54DgiV35ZpT1tzGBlwDI/+eA4Ihd8WqU0bdBgZcAyP/ngCCIWfkzBJRBwbchR+OK5/ABTBMEAAw5t0FHzb9BRwVE453n9oOlywADpYsAcTK5v0FHBUTjk+f2A6cLAZFnY+jnHoOlSwEDpYsACTGBt0FHBUTjlOf0g6cLARFnY2n3HAOnywCDpUsBA6WLADOE5wLxPiOsBAAjJIqwCb8DxwQAYwMHFAOniwDBFxMEAAxjE/cAwEgBR5MG8A5jRvcCg8dbAAPHSwABTKIH2Y8Dx2sAQgddj4PHewDiB9mP44T25hMEEAyFtTOG6wADRoYBBQexjuG3g8cEAP3H3ERjnQcUwEgjgAQAVb1hR2OW5wKDp8sBA6eLAYOmSwEDpgsBg6XLAAOliwCX8Mf/54DgeCqMMzSgAAG9AUwFRCm1EUcFROOd5+a3lwBgtEtld30XBWb5jtGOA6WLALTL9EOBRfmO0Y70w/RL+Y7RjvTLtEN1j1GPuMOX8Mf/54DAdgW1E/f3AOMXB+qT3EcAE4SLAAFMfV3jd5zbSESX8Mf/54AAYBhEVEAQQPmOYwenARxCE0f3/32P2Y4UwgUMQQTZvxFHtbVBRwVE45rn3oOniwADp0sBIyT5ACMi6QDJs4MlSQDBF5Hlic8BTBMEYAyhuwMniQBjZvcGE/c3AOMbB+IDKIkAAUYBRzMF6ECzhuUAY2n3AOMHBtIjJKkAIyLZAA2zM4brABBOEQeQwgVG6b8hRwVE45Tn2AMkiQAZwBMEgAwjJAkAIyIJADM0gAC9swFMEwQgDMW5AUwTBIAM5bEBTBMEkAzFsRMHIA1jg+cMEwdADeOR57oDxDsAg8crACIEXYyX8Mf/54DgXgOsxABBFGNzhAEijOMPDLbAQGKUMYCcSGNV8ACcRGNa9Arv8A/gdd3IQGKGk4WLAZfwx//ngOBaAcWTB0AM3MjcQOKX3MDcRLOHh0HcxJfwx//ngMBZFb4JZRMFBXEDrMsAA6SLAJfwx//ngIBKtwcAYNhLtwYAAcEWk1dHARIHdY+9i9mPs4eHAwFFs9WHApfwx//ngGBLEwWAPpfwx//ngCBH3bSDpksBA6YLAYOlywADpYsA7/AP9sG8g8U7AIPHKwAThYsBogXdjcEVgTptvO/wb9mBtwPEOwCDxysAE4yLASIEXYzcREEUxeORR4VLY/6HCJMHkAzcyHm0A6cNACLQBUizh+xAPtaDJ4qwY3P0AA1IQsY6xO/w79QiRzJIN8XIP+KFfBCThsoAEBATBUUCl/DH/+eA4Eg398g/kwjHAIJXA6eIsIOlDQAdjB2PPpyyVyOk6LCqi76VI6C9AJOHygCdjQHFoWdjlvUAWoV1MCOgbQEJxNxEmcPjQHD5Y98LAJMHcAyFv4VLt33JP7fMyD+TjY26k4zMAOm/45ULntxE44IHnpMHgAyxt4OniwDjmwecAUWX8Mf/54BAOAllEwUFcZfwx//ngGA0l/DH/+eAYDhNugOkywDjBgSaAUWX8Mf/54CgNRMFgD6X8Mf/54DgMQKUQbr2UGZU1lRGWbZZJlqWWgZb9ktmTNZMRk22TQlhgoA=", - "text_start": 1077411840, - "data": "DEDIP/gIOEBECThAnAk4QGoKOEDWCjhAhAo4QMAHOEAmCjhAZgo4QLAJOEBwBzhA5Ak4QHAHOEDSCDhAFgk4QEQJOECcCThA5Ag4QCoIOEBaCDhA4Ag4QC4NOEBECThA7gs4QOIMOEC8BjhADA04QLwGOEC8BjhAvAY4QLwGOEC8BjhAvAY4QLwGOEC8BjhAigs4QLwGOEAKDDhA4gw4QA==", - "data_start": 1070164904, - "bss_start": 1070088192 -} \ No newline at end of file diff --git a/tools/esptool_py/esptool/targets/stub_flasher/1/esp32p4.json b/tools/esptool_py/esptool/targets/stub_flasher/1/esp32p4.json index 6f37e91b27..8b61274af5 100644 --- a/tools/esptool_py/esptool/targets/stub_flasher/1/esp32p4.json +++ b/tools/esptool_py/esptool/targets/stub_flasher/1/esp32p4.json @@ -1,8 +1,8 @@ { - "entry": 1341195918, - "text": "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", + "entry": 1341196642, + "text": "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", "text_start": 1341194240, - "data": "EAD1TwYK8U9WCvFPrgrxT4QL8U/wC/FPngvxT9QI8U9AC/FPgAvxT8IK8U+ECPFP9grxT4QI8U/gCfFPJgrxT1YK8U+uCvFP8gnxTzgJ8U9oCfFP7gnxT0AO8U9WCvFPCA3xTwAO8U/EB/FPJA7xT8QH8U/EB/FPxAfxT8QH8U/EB/FPxAfxT8QH8U/EB/FPpAzxT8QH8U8mDfFPAA7xTw==", - "data_start": 1341533100, + "data": "YAD1T3gO8U/GDvFPZA/xT0oQ8U+kEPFPXBDxT8oM8U/+D/FPRhDxT4IP8U96DPFPqg/xT3oM8U9UDvFPkg7xT8YO8U9kD/FPZg7xT/QM8U8oDfFPYg7xT3YU8U/GDvFPGBLxTzYU8U8eC/FPWhTxTx4L8U8eC/FPHgvxTx4L8U8eC/FPHgvxTx4L8U8eC/FPthHxTx4L8U9SE/FPNhTxTw==", + "data_start": 1341533180, "bss_start": 1341456384 } \ No newline at end of file diff --git a/tools/esptool_py/esptool/targets/stub_flasher/1/esp32s2.json b/tools/esptool_py/esptool/targets/stub_flasher/1/esp32s2.json index 68de613890..f1e5014787 100644 --- a/tools/esptool_py/esptool/targets/stub_flasher/1/esp32s2.json +++ b/tools/esptool_py/esptool/targets/stub_flasher/1/esp32s2.json @@ -1,8 +1,8 @@ { - "entry": 1073907716, - "text": "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", + "entry": 1073907752, + "text": "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", "text_start": 1073905664, - "data": "WAD9P0uLAkDdiwJA8pACQGaMAkD+iwJAZowCQMWMAkDejQJAUY4CQPmNAkDVigJAd40CQNCNAkDojAJAdI4CQBCNAkB0jgJAy4sCQCqMAkBmjAJAxYwCQOOLAkAXiwJAN48CQKqQAkDqiQJA0ZACQOqJAkDqiQJA6okCQOqJAkDqiQJA6okCQOqJAkDqiQJA1I4CQOqJAkDJjwJAqpACQA==", + "data": "WAD9P2aLAkD7iwJA0pACQH2MAkAejAJAfYwCQNaMAkDbjQJAUY4CQPaNAkDxigJAh40CQNCNAkD0jAJAco4CQByNAkByjgJA8osCQEOMAkB9jAJA1owCQASMAkAyiwJALI8CQI6QAkAGigJAsZACQAaKAkAGigJABooCQAaKAkAGigJABooCQAaKAkAGigJA0o4CQAaKAkC8jwJAjpACQA==", "data_start": 1073622012, "bss_start": 1073545216 } \ No newline at end of file diff --git a/tools/esptool_py/esptool/targets/stub_flasher/1/esp32s3.json b/tools/esptool_py/esptool/targets/stub_flasher/1/esp32s3.json index 484a8832fd..26d80f6c41 100644 --- a/tools/esptool_py/esptool/targets/stub_flasher/1/esp32s3.json +++ b/tools/esptool_py/esptool/targets/stub_flasher/1/esp32s3.json @@ -1,8 +1,8 @@ { - "entry": 1077381760, - "text": "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", + "entry": 1077381852, + "text": "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", "text_start": 1077379072, - "data": "XADKP16ON0AzjzdAR5Q3QL2PN0BTjzdAvY83QB2QN0A6kTdArJE3QFWRN0DpjTdA0JA3QCyRN0BAkDdA0JE3QGiQN0DQkTdAIY83QH6PN0C9jzdAHZA3QDmPN0AqjjdAkJI3QA2UN0AAjTdALZQ3QACNN0AAjTdAAI03QACNN0AAjTdAAI03QACNN0AAjTdAKpI3QACNN0AlkzdADZQ3QAQInwAAAAAAAAAYAQQIBQAAAAAAAAAIAQQIBgAAAAAAAAAAAQQIIQAAAAAAIAAAEQQI3AAAAAAAIAAAEQQIDAAAAAAAIAAAAQQIEgAAAAAAIAAAESAoDAAQAQAA", + "data": "XADKP6qON0B/jzdAo5U3QA2QN0CijzdADZA3QGaQN0BnkTdA4JE3QISRN0A5jjdAF5E3QFyRN0CEkDdAAZI3QKyQN0ABkjdAdo83QM+PN0ANkDdAZpA3QIiPN0B6jjdAvJI3QGeVN0BSjTdAh5U3QFKNN0BSjTdAUo03QFKNN0BSjTdAUo03QFKNN0BSjTdAX5I3QFKNN0CUlDdAZ5U3QAQInwAAAAAAAAAYAQQIBQAAAAAAAAAIAQQIBgAAAAAAAAAAAQQIIQAAAAAAIAAAEQQI3AAAAAAAIAAAEQQIDAAAAAAAIAAAAQQIEgAAAAAAIAAAESAoDAAQAQAA", "data_start": 1070279676, "bss_start": 1070202880 } \ No newline at end of file diff --git a/tools/esptool_py/esptool/targets/stub_flasher/1/esp32s3beta2.json b/tools/esptool_py/esptool/targets/stub_flasher/1/esp32s3beta2.json deleted file mode 100644 index da770f7b97..0000000000 --- a/tools/esptool_py/esptool/targets/stub_flasher/1/esp32s3beta2.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "entry": 1077380596, - "text": "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", - "text_start": 1077379072, - "data": "DADKPxeIN0CriDdAw403QDeJN0DLiDdAN4k3QJaJN0C2ijdAKIs3QNGKN0ChhzdASIo3QKiKN0C5iTdATIs3QOGJN0BMizdAmYg3QPiIN0A3iTdAlok3QLGIN0DjhzdABIw3QIWNN0DAhjdAp403QMCGN0DAhjdAwIY3QMCGN0DAhjdAwIY3QMCGN0DAhjdAqYs3QMCGN0CZjDdAhY03QA==", - "data_start": 1070279592, - "bss_start": 1070202880 -} \ No newline at end of file diff --git a/tools/esptool_py/esptool/targets/stub_flasher/2/LICENSE-APACHE b/tools/esptool_py/esptool/targets/stub_flasher/2/LICENSE-APACHE index f8e5e5ea03..d11f8e7ae8 100644 --- a/tools/esptool_py/esptool/targets/stub_flasher/2/LICENSE-APACHE +++ b/tools/esptool_py/esptool/targets/stub_flasher/2/LICENSE-APACHE @@ -1,201 +1,201 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - -TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - -1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - -2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - -3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - -4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - -5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - -6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - -7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - -8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - -9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - -END OF TERMS AND CONDITIONS - -APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - -Copyright [yyyy] [name of copyright owner] - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. \ No newline at end of file + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2025 Espressif Systems (Shanghai) CO LTD + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/tools/esptool_py/esptool/targets/stub_flasher/2/LICENSE-MIT b/tools/esptool_py/esptool/targets/stub_flasher/2/LICENSE-MIT index 3e8a085340..73068b768c 100644 --- a/tools/esptool_py/esptool/targets/stub_flasher/2/LICENSE-MIT +++ b/tools/esptool_py/esptool/targets/stub_flasher/2/LICENSE-MIT @@ -1,25 +1,21 @@ -Copyright 2022 esp-rs +MIT License -Permission is hereby granted, free of charge, to any -person obtaining a copy of this software and associated -documentation files (the "Software"), to deal in the -Software without restriction, including without -limitation the rights to use, copy, modify, merge, -publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software -is furnished to do so, subject to the following -conditions: +Copyright (c) 2025 Espressif Systems (Shanghai) CO LTD -The above copyright notice and this permission notice -shall be included in all copies or substantial portions -of the Software. +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF -ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED -TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A -PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT -SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR -IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -DEALINGS IN THE SOFTWARE. +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/tools/esptool_py/esptool/targets/stub_flasher/2/README.md b/tools/esptool_py/esptool/targets/stub_flasher/2/README.md index eae371e43f..76742396d1 100644 --- a/tools/esptool_py/esptool/targets/stub_flasher/2/README.md +++ b/tools/esptool_py/esptool/targets/stub_flasher/2/README.md @@ -1,3 +1,3 @@ # Licensing -The binaries in JSON format distributed in this directory are dual licensed under the Apache License Version 2.0 or the MIT license. They were released at https://github.com/esp-rs/esp-flasher-stub/releases/tag/v0.3.0 from where the sources can be obtained. +The binaries in JSON format distributed in this directory are dual licensed under the Apache License Version 2.0 or the MIT license. They were released at https://github.com/espressif/esp-flasher-stub/releases/tag/v0.1.0 from where the sources can be obtained. diff --git a/tools/esptool_py/esptool/targets/stub_flasher/2/esp32.json b/tools/esptool_py/esptool/targets/stub_flasher/2/esp32.json index 8e6bd05380..efb590a102 100644 --- a/tools/esptool_py/esptool/targets/stub_flasher/2/esp32.json +++ b/tools/esptool_py/esptool/targets/stub_flasher/2/esp32.json @@ -1 +1,5 @@ -{"entry":1074274996,"text":"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GTAMEzIhGjAEEzIhGzAQEzIhHDAREzIhHTAgEzIhHjAhEzIhHzAiEzIhIDAjEzIhITDq8zIhIjDr8zIhIzDs8zIhJDDo8zIhJTDp8wMBJhMBJyMBKDMBKUMBKlMBK2MBLHMBLYMBLpMBL6MBMLMBMcMBMtMBM+MBNPMBNSIhBDIhBUIhBlIhB2IhCHIhCYIhCpIhC6IhDLIhDcIhDtIhD+IhEPIhEYAAAPBBABABIBLR/wJhAwDRSQDmAwJhAQDoAwJhEwDuAwJhFACxAwJhAADBSQDRAwJhAoXe/wF5/gDmExAgAGIhEyZGCBBxIFUoAEYEAAF0/gDmExAgAGKgARBxIJWd/4Xt/wIhAQDmEwIhAACxEwIhAhIhAxAgAAAwAPBBABABIBLR/wJhAwDRSQDmAwJhAQDoAwJhEwDuAwJhFADAAwJhAADBSQDXAwJhAgXX/2IhExBxIJUhAAXo/wIhAQDmEwIhAACxEwIhAhIhAxAgAAAyAPBBAAAQASAS0f8CYQMA0UkAwgMCYQEAsgMCYQAAwUkA0gMCYQJF0v8BSv4A5hMQIABioAIQcSDVkv/F4v8CIQEAwhMCIQAAshMCIQISIQMQIAAQMgDwQQAQASAS0f8CYQMA0UkAwwMCYQEAswMCYQAAwUkA0wMCYQIFzf8BM/4A5hMQIABioAMQcSCVjf+F3f8CIQEAwxMCIQAAsxMCIQISIQMQIAAQMwDwQQAQASAS0f8CYQMA0UkAxAMCYQEAtAMCYQAAwUkA1AMCYQLFx/8BIf4A5hMQIABioAQQcSDVwv9F2P8CIQEAxBMCIQAAtBMCIQISIQMQIAAQNADwQQAQASAS0f8CYQMA0UkAxQMCYQEAtQMCYQAAwUkA1QMCYQKFwv8BDf4A5hMQIABioAUQcSCVvv8F0/8CIQEAxRMCIQAAtRMCIQISIQMQIAAQNQDwQQAQASAS0f8CYQMA0UkAxgMCYQEAtgMCYQAAwUkA1gMCYQJFvf8B+f0A5hMQIABioAYQcSBVuv/Fzf8CIQEAxhMCIQAAthMCIQISIQMQIAAQNgDwQQAQASAS0f8CYQMA0UkAxwMCYQEAtwMCYQAAwUkA1wMCYQIFuP8B5f0A5hMQIABioAcQcSAVtv+FyP8CIQEAxxMCIQAAtxMCIQISIQMQIAAQNwDwQQA2QQCBv/2tAr0D4AgAHfAANkEA8EEAAADkJwhA5CcIQOQnCEDkJwhA5CcIQOQnCEDkJwhA5CcIQOQnCEDkJwhAAAAAAAAAAADkJwhA5CcIQOQnCEDkJwhA5CcIQOQnCEDkJwhA5CcIQOQnCEDkJwhA5CcIQOQnCEDkJwhA5CcIQOQnCEDkJwhA5CcIQOQnCEDkJwhA5CcIQOQnCEDkJwhA5CcIQOQnCEDkJwhA5CcIQOQnCEDkJwhA5CcIQOQnCEDkJwhA5CcIQOQnCEDkJwhA5CcIQOQnCEDkJwhA5CcIQOQnCEDkJwhA5CcIQOQnCEDkJwhA5CcIQOQnCEDkJwhA5CcIQOQnCEDkJwhA5CcIQOQnCEDkJwhA5CcIQOQnCEDkJwhA5CcIQOQnCEA=","text_start":1074266112,"data":"5hMIQO0UCEBGFQhA5hMIQKsVCEDtFAhAGhYIQFEWCEChFghAABcIQKkeCEAMFwhAqR4IQD4XCEDmEwhA7RQIQEYVCEDoFwhAlRwIQN4UCEAWGQhAaRkIQKIVCEDtFAhATisIQA4pCEAGLAhABiwIQAYsCEA5KwhABiwIQAYsCEA/KwhARSsIQEsrCEDA29zb3QA6ADsAPAA9AD4APwBAAEEAAAAAAAAA/zcGAAAAOAAAiMAoAAAAUwAAAYQAAAAAAEAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAwAAAAEAAAABAAAAAAAAAAMAAAAAAAAAAQAAAAEAAAACAAAAAgAAAAIAAAADAAAAAwAAAAAAAAAAAAAAAAAAAAMAAAAAAAAAAwAAAAAAAAAAAAAAAAABAAIAAwAEAAUABgAHAAgACQBFAEUADAANAA4ADwAQABEAEgATABQAFQAWABcAGAAZABoAGwAcAB0AHgAfACAAIQAiACMAJAAlACYAJwAoACkAKgArACwALQAuAC8AMAAxADIAMwA0ADUANgA3ADgAOQA6ADsAPAA9AD4APwBAAEEAQgBDAEQAAAAAAQAA","data_start":1073414144} \ No newline at end of file +{ + "entry": 1074520064, + "text": "NmEADAgQoSCJARARIOUAAKgBjEoQESBlAQAd8DZhAH0BKQeoByUCAD3wHfA2YQB9ASkHqAflAQA98B3wNmEAfQEpBz3wHfAANmEAfQEpBz3wHfAANmEAfQEpBz3wHfAA", + "text_start": 1074520064 +} \ No newline at end of file diff --git a/tools/esptool_py/esptool/targets/stub_flasher/2/esp32c2.json b/tools/esptool_py/esptool/targets/stub_flasher/2/esp32c2.json index 72c2ac6cca..cd15fc27c3 100644 --- a/tools/esptool_py/esptool/targets/stub_flasher/2/esp32c2.json +++ b/tools/esptool_py/esptool/targets/stub_flasher/2/esp32c2.json @@ -1 +1,5 @@ -{"entry":1077411840,"text":"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E7wEBBOEBPyTBQAEs4qVQJO8FABRwQoFNpUDKoUMBUWBRSKGlwAAAOeAgKIqhK6JBUWBRVaGlwAAAOeA4KETBgAEY+PEACKFY+PEAM6FE3r6ABOE/P+zeaQAbYwFRYFFJoaXAAAA54CgnrMGgEEz9qYA7Y4SCigIKprShMhAjEDYRJxEVY3RjUGPs+c3AZzE2MSMwMjAsagFRYFFJoaXAAAA54DgmqqJLooFRYFFIoaXAAAA54DAmSqLLoQFRYFFVoaXAAAA54AgmRMGAARj48QAooVj48QAWoUzBoBBs3ZGATN2NgH9HDP0vACz+awAMksTRfT/k8X5/5PG9v8TRvb/M3nJADN93QCz/b0As/urAFJGtyYMYI21OUWiRWNttRgTlSUAtwXLP5OFhQoulQhBwUUSRmNxtRgSBSwILpVMQQhBAUkBS7P7xQCCRTN8tQA3tXwHkwwVUzcFyz8TDQUIkw0ABDNlLAGz5WsBTY1jCQUQYxsJAjMFYEEzdasAMwWVA22BapUDRQUAEwUFAmMJDAKzBYBBs3W8ALOFlQPtgeqVg8QFAA2oMwUgQTN1qQAzBZUDbYFqlQNFBQDjGwz8swVwQbP1uwCzhZUD7YHqlYPFBQCThAUCs2V8AZnhkwQFBCaFl/D//+eAYBGTFQUBwYGThWX9kcXCRZcAAADngOALBUWBRSaGlwAAAOeAIISqia6KE4YE/AVFgUWXAAAA54DggiqKLoQzhp1ABUWBRZcAAADngCCCY+O0AaKFY+O0AVKFE7YEBDMGwECzdlYBM3Y2ARO3FAB9F/mNeY0TRfX/k8X1/xNG9v+Txvb/s/vbADN8zAAze7sAM3mpAN21CAKDIMUEIEVkQQMpBQSDKcUDAyqFA4MqRQMDKwUDgyvFAgMshQKDLEUCAy0FAoMtxQFxYYKAAABCBUGBNyY4QBMGBmsKBTKVHEE3JThAEwXlo2OEpwAuhYKHAACqhQlFFwMAAGcAI8CqhQ1FFwMAAGcAY7+qhRFFFwMAAGcAo76qhRVFFwMAAGcA472qhRlFFwMAAGcAI72qhR1FFwMAAGcAY7yqhSFFFwMAAGcAo7uqhSVFFwMAAGcA47qqhSlFFwMAAGcAI7qqhS1FFwMAAGcAY7mqhTFFFwMAAGcAo7iqhTVFFwMAAGcA47eqhTlFFwMAAGcAI7eqhT1FFwMAAGcAY7YAAD4aOEA+GjhAPho4QD4aOEA+GjhAPho4QD4aOEA+GjhAPho4QD4aOEA+GjhAPho4QD4aOEA+GjhAPho4QD4aOEA+GjhAPho4QD4aOEA+GjhAPho4QD4aOEA+GjhAPho4QD4aOEA+GjhAPho4QD4aOEA+GjhAPho4QD4aOEA+GjhAPho4QD4aOEA+GjhAPho4QD4aOEA+GjhAPho4QD4aOEA+GjhAPho4QA==","text_start":1077411840,"data":"0As4QKIMOEDeDDhA0As4QIoOOECiDDhATA04QLQNOEDoDjhAFA84QOIUOEAyDThA4hQ4QOgNOEDQCzhAogw4QN4MOEBeDzhAgA04QI4MOED4EThAZBA4QCgNOECiDDhAAAEcAh0OGAMeFhQPGREECB8bDRcVExAHGgwSBgsFCgnA29zb3QAAAAEAAAACAAAAAwAAAAQAAAAFAAAABgAAAAcAAAAIAAAACQAAAAoAAAALAAAADAAAAA0AAAAOAAAADwAAAD4aOEA+GjhAAAAAAD4aOEA+GjhAPho4QAAAAAA+GjhAPho4QD4aOEAAAAAAPho4QAQAAAAEAAAABAAAAAQAAAAMAAAABAAAAAwAAAAEAAAABAAAAAwAAABAAAAAgAAAAAAIAAAAAAAQQAAAAAAgAAAEAAAABAAAACAAAAACAAAACAAAAAgAAAAIAAAACAAAABAAAAAIAAAAEAAAAAgAAAAIAAAAEAAAAL////9///////f//////++//////9////v////7////3/////3///8=","data_start":1070268448} \ No newline at end of file +{ + "entry": 1077411840, + "text": "ARFoAAbOAsY5IDJFEcEVIPJABWGCgAERBs4izAAQIyak/gMlxP49KAEA8kBiRAVhgoABEQbOIswAECMmpP4DJcT+JSgBAPJAYkQFYYKAAREGziLMABAjJqT+AQDyQGJEBWGCgAERBs4izAAQIyak/gEA8kBiRAVhgoABEQbOIswAECMmpP4BAPJAYkQFYYKA", + "text_start": 1077411840 +} \ No newline at end of file diff --git a/tools/esptool_py/esptool/targets/stub_flasher/2/esp32c3.json b/tools/esptool_py/esptool/targets/stub_flasher/2/esp32c3.json index 97538d65a1..cd15fc27c3 100644 --- a/tools/esptool_py/esptool/targets/stub_flasher/2/esp32c3.json +++ b/tools/esptool_py/esptool/targets/stub_flasher/2/esp32c3.json @@ -1 +1,5 @@ -{"entry":1077411840,"text":"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YwsFGGObDQIzBXBBM/WrADMFZQNtgTKVA0UFABMFBQJjCQkCswUgQbN1uQCzhWUD7YGylYPEBQANqDMFsEEz9a0AMwVlA22BMpUDRQUA4xsJ/LMFoEGzdb0As4VlA+2BspWDxQUAk4QFArNlqQGZ4ZMEBQQTlSQAVY0IQRO8BAQThAT8kwUABLOKlUCTvBQAUcEKBTaVAypFEQVFgUUihpcAAADngICiKoSuiQVFgUVWhpcAAADngOChEwYABGPjxAAihWPjxADOhRN6+gAThPz/s3mkAG2MBUWBRSaGlwAAAOeAoJ6zBoBBM/amAO2OEgooCCqa0oTIQIxA2EScRFWN0Y1Bj7PnNwGcxNjEjMDIwLGoBUWBRSaGlwAAAOeA4JqqiS6KBUWBRSKGlwAAAOeAwJkqiy6EBUWBRVaGlwAAAOeAIJkTBgAEY+PEAKKFY+PEAFqFMwaAQbN2RgEzdjYB/Rwz9LwAs/msADJLE0X0/5PF+f+Txvb/E0b2/zN5yQAzfd0As/29ALP7qwBSRrcmDGCNtTlFokVjbbUYE5UlALcFyT+ThYUKLpUIQcFFEkZjcbUYEgUsCC6VTEEIQQFJAUuz+8UAgkUzfLUAN7V8B5MMFVM3Bck/Ew0FCJMNAAQzZSwBs+VrAU2NYwkFEGMbCQIzBWBBM3WrADMFlQNtgWqVA0UFABMFBQJjCQwCswWAQbN1vACzhZUD7YHqlYPEBQANqDMFIEEzdakAMwWVA22BapUDRQUA4xsM/LMFcEGz9bsAs4WVA+2B6pWDxQUAk4QFArNlfAGZ4ZMEBQQmhZfw///ngKDokxUFAcGBk4Ul/JHFwkWXAAAA54DgCwVFgUUmhpcAAADngCCEqomuihOGBPwFRYFFlwAAAOeA4IIqii6EM4adQAVFgUWXAAAA54AggmPjtAGihWPjtAFShRO2BAQzBsBAs3ZWATN2NgETtxQAfRf5jXmNE0X1/5PF9f8TRvb/k8b2/7P72wAzfMwAM3u7ADN5qQDdtQgCgyDFBCBFZEEDKQUEgynFAwMqhQODKkUDAysFA4MrxQIDLIUCgyxFAgMtBQKDLcUBcWGCgAAAQgVBgTc2OEATBgarCgUylRxBNyU4QBMFBddjhKcALoWChwAAqoUJRRcDAABnACPAqoUNRRcDAABnAGO/qoURRRcDAABnAKO+qoUVRRcDAABnAOO9qoUZRRcDAABnACO9qoUdRRcDAABnAGO8qoUhRRcDAABnAKO7qoUlRRcDAABnAOO6qoUpRRcDAABnACO6qoUtRRcDAABnAGO5qoUxRRcDAABnAKO4qoU1RRcDAABnAOO3qoU5RRcDAABnACO3qoU9RRcDAABnAGO2AABwHThAcB04QHAdOEBwHThAcB04QHAdOEBwHThAcB04QHAdOEBwHThAcB04QHAdOEBwHThAcB04QHAdOEBwHThAcB04QHAdOEBwHThAcB04QHAdOEBwHThAcB04QHAdOEBwHThAcB04QHAdOEBwHThAcB04QHAdOEBwHThAcB04QHAdOEBwHThAcB04QHAdOEBwHThAcB04QHAdOEBwHThAcB04QHAdOEBwHThAcB04QHAdOEBwHThAcB04QHAdOEBwHThAcB04QHAdOEBwHThAcB04QHAdOEBwHThAcB04QHAdOEBwHThAcB04QHAdOEBwHThAcB04QA==","text_start":1077411840,"data":"4Aw4QLYNOEDyDThA4Aw4QJwPOEC2DThAXg44QMYOOED6DzhAJhA4QJwKOEBEDjhAnAo4QPoOOEDgDDhAtg04QPINOEBwEDhAkg44QKINOEAKEzhAdhE4QDoOOEC2DThAAAEcAh0OGAMeFhQPGREECB8bDRcVExAHGgwSBgsFCgnA29zb3QAAAAEAAAACAAAAAwAAAAQAAAAFAAAABgAAAAcAAAAIAAAACQAAAAoAAAALAAAADAAAAA0AAAAOAAAADwAAAHAdOEBwHThAAAAAAHAdOEBwHThAcB04QAAAAABwHThAcB04QHAdOEAAAAAAcB04QA==","data_start":1070137376} \ No newline at end of file +{ + "entry": 1077411840, + "text": "ARFoAAbOAsY5IDJFEcEVIPJABWGCgAERBs4izAAQIyak/gMlxP49KAEA8kBiRAVhgoABEQbOIswAECMmpP4DJcT+JSgBAPJAYkQFYYKAAREGziLMABAjJqT+AQDyQGJEBWGCgAERBs4izAAQIyak/gEA8kBiRAVhgoABEQbOIswAECMmpP4BAPJAYkQFYYKA", + "text_start": 1077411840 +} \ No newline at end of file diff --git a/tools/esptool_py/esptool/targets/stub_flasher/2/esp32c5.json b/tools/esptool_py/esptool/targets/stub_flasher/2/esp32c5.json new file mode 100644 index 0000000000..5033ef2cf0 --- /dev/null +++ b/tools/esptool_py/esptool/targets/stub_flasher/2/esp32c5.json @@ -0,0 +1,5 @@ +{ + "entry": 1082130432, + "text": "ARFoAAbOAsY5IDJFEcEVIPJABWGCgAERBs4izAAQIyak/gMlxP49KAEA8kBiRAVhgoABEQbOIswAECMmpP4DJcT+JSgBAPJAYkQFYYKAAREGziLMABAjJqT+AQDyQGJEBWGCgAERBs4izAAQIyak/gEA8kBiRAVhgoABEQbOIswAECMmpP4BAPJAYkQFYYKA", + "text_start": 1082130432 +} \ No newline at end of file diff --git a/tools/esptool_py/esptool/targets/stub_flasher/2/esp32c6.json b/tools/esptool_py/esptool/targets/stub_flasher/2/esp32c6.json index 25c7710657..5033ef2cf0 100644 --- a/tools/esptool_py/esptool/targets/stub_flasher/2/esp32c6.json +++ b/tools/esptool_py/esptool/targets/stub_flasher/2/esp32c6.json @@ -1 +1,5 @@ -{"entry":1082130432,"text":"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lwAAAOeAYJIihZcAAADngACUhUUihZcAAADngACUhUUihZcAAADngACUIoWBRZcAAADngECQIoWBRZcAAADngECQSkVFjSrJABECywLNAtG3BABAJs8ihZcAAADngOCShUUihZcAAADngKCNhUUihZcAAADngKChhUUihZcAAADngKCNClUzdSUBRY2q1CrRAtkIGYVFlwAAAOeAgI0IGZcAAADngKCNCBmXAAAA54DAjQgZgUWXAAAA54CAiAgZhUWXAAAA54CAnAgZhUWXAAAA54CAiALTIBEC1QLXSBGBRZcAAADngACLGlUzdXUB3t4q00gRlwAAAOeAgIFIEZcAAADngKCBIoWXAAAA54BAiSKFlwAAAOeAYIkihZcAAADngICJKlWDRcEKM3W1ATNlhQGq1irVLtdBRSrbSBmpRZfw///ngOBISBmtRZfw///ngCBIWlW3RY///RVtjbcFEABNjSrbSBn1RZfw///ngEBGAt0oGZcAAADngKCEKBmXAAAA54CghCgZlwAAAOeAoIQoGZcAAADngKCEKBmXAAAA54CghCgZlwAAAOeAoIQoGZcAAADngKCEKBmXAAAA54CghCgZlwAAAOeAoIRKXFpVqs5qVarMKkUq0DpFKtJaRSrYakUq1npFKtQaVSreAsVkAQLHAskoAYVFl/D//+eAQHAoAZfw///ngOBxKkUzdWUBKsUoAYVFl/D//+eAIHIoAYFFl/D//+eA4HIoAZfw///ngMBtKAGX8P//54DgbYVFJoWX8P//54CgboVFJoWX8P//54CgbiaFl/D//+eAwG4mhYFFl/D//+eAwG4mhYFFl/D//+eAwG5KRTN1NQEqyQQRAssCzQLPAtEmhZfw///ngEBvJoWBRZfw///ngABqhUUmhZfw///ngAB+hUUmhZfw///ngABqClVKxDN9JQFq0QLTJBEC1QLXSBGBRZfw///ngOBrGlUzdXUBKtNIEZfw///ngIBiSBGX8P//54CgYiaFl/D//+eAQGomhZfw///ngGBqJoWX8P//54CAaipVM3W1AYNFwQo3BgAIUY2qyirVLtcTBQAgKtkIGalFl/D//+eAoCkIGbFFl/D//+eA4CgIGbVFl/D//+eAIChKVbcFceD9FW2NtwWAEk2NKtkIGflFl/D//+eAQCYIGf1Fl/D//+eAgCUC20gZ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nMTYxIzAyMCxqAVFgUUmhpcAAADngGCNqokuigVFgUUihpcAAADngECMKosuhAVFgUVWhpcAAADngKCLEwYABGPjxACihWPjxABahTMGkEGzdkYBM3Y2Af0dM/S9ALP5rQBySxNF9P+Txfn/k8b2/xNG9v+z+8sAM3nZADN9vQAzfKwAEla5vUlF4kVjYLUaE5UlALdFgECThcUOLpUIQcFF0kZjdLUYEgUsEC6VDEVQQQhBAUkz+9UAskWze7YAwkUzfLUAN7V8B5MMFVM3RYBAEw1FDJMNAAQzZWwBs+UrAU2NYwkFEGMbCwIzBSBBM3WpADMFlQNtgWqVA0UFABMFBQJjCQwCswWAQbN1vACzhZUD7YHqlYPEBQANqDMFYEEzdasAMwWVA22BapUDRQUA4xsM/LMFcEGz9bsAs4WVA+2B6pWDxQUAk4QFArNlfAGZ4ZMEBQQmhZfg///ngGC3kxUFAcGBk4U1+5HFglWXAAAA54DgCwVFgUUmhpfw///ngIB2qomuihOGBPwFRYFFl/D//+eAQHUqii6EM4adQAVFgUWX8P//54CAdGPjtAGihWPjtAFShRO2BAQzBsBAs3ZWATN2NgETtxQAfRf5jXmNE0X1/5PF9f8TRvb/k8b2/7P72wAzfMwAM3m5ADN7qwDdtQgCgyDFBSBNZEkDKQUFgynFBAMqhQSDKkUEAysFBIMrxQMDLIUDgyxFAwMtBQODLcUCNWGCgAAAQgVBgTdGgEATBgbvCgUylRxBNzWAQBMFxQ1jhKcALoWChwAAqoUJRRcDAABnAGO/qoUNRRcDAABnAKO+qoURRRcDAABnAOO9qoUVRRcDAABnACO9qoUZRRcDAABnAGO8qoUdRRcDAABnAKO7qoUhRRcDAABnAOO6qoUlRRcDAABnACO6qoUpRRcDAABnAGO5qoUtRRcDAABnAKO4qoUxRRcDAABnAOO3qoU1RRcDAABnACO3qoU5RRcDAABnAGO2qoU9RRcDAABnAKO1qoVBRRcDAABnAOO0qoVFRRcDAABnACO0qoVJRRcDAABnAGOzqoVNRRcDAABnAKOyAADcMIBA3DCAQNwwgEDcMIBA3DCAQNwwgEDcMIBA3DCAQNwwgEDcMIBA3DCAQNwwgEDcMIBA3DCAQNwwgEDcMIBA3DCAQNwwgEDcMIBA3DCAQNwwgEDcMIBA3DCAQNwwgEDcMIBA3DCAQNwwgEDcMIBA3DCAQNwwgEDcMIBA3DCAQNwwgEDcMIBA3DCAQNwwgEDcMIBA3DCAQNwwgEDcMIBA3DCAQNwwgEDcMIBA3DCAQNwwgEDcMIBA3DCAQNwwgEDcMIBA3DCAQNwwgEDcMIBA3DCAQNwwgEDcMIBA3DCAQNwwgEDcMIBA3DCAQNwwgEDcMIBA3DCAQNwwgEDcMIBA3DCAQNwwgEDcMIBA3DCAQNwwgEDcMIBA3DCAQNwwgEDcMIBA3DCAQNwwgEDcMIBA3DCAQA==","text_start":1082130432,"data":"4AuAQLYMgEDyDIBA4AuAQJwOgEC2DIBAXg2AQMYNgED6DoBAJg+AQPYUgEBEDYBA9hSAQPoNgEDgC4BAtgyAQPIMgEBwD4BAkg2AQKIMgEAKEoBAdhCAQDoNgEC2DIBA3////9/////f////f////+/////f////3//////+//8gAAAAIAAAACAAAACAAAAAEAAAACAAAAAgAAAAAAEAAAABHAIdDhgDHhYUDxkRBAgfGw0XFRMQBxoMEgYLBQoJwNvc290AAAABAAAAAgAAAAAAAAAAAAAAAwAAAAQAAAAAAAAAAAAAAAUAAAAGAAAABwAAAAgAAAAJAAAACgAAAAsAAAAMAAAADQAAAA4AAAAPAAAAAQAAAAIAAAAFAAAABgAAAAkAAAAKAAAACwAAAAwAAAANAAAADgAAAA8AAAAQAAAAEQAAABIAAAATAAAA3DCAQNwwgEAAAAAA3DCAQNwwgEDcMIBAAAAAANwwgEDcMIBA3DCAQAAAAADcMIBA","data_start":1082146852} \ No newline at end of file +{ + "entry": 1082130432, + "text": "ARFoAAbOAsY5IDJFEcEVIPJABWGCgAERBs4izAAQIyak/gMlxP49KAEA8kBiRAVhgoABEQbOIswAECMmpP4DJcT+JSgBAPJAYkQFYYKAAREGziLMABAjJqT+AQDyQGJEBWGCgAERBs4izAAQIyak/gEA8kBiRAVhgoABEQbOIswAECMmpP4BAPJAYkQFYYKA", + "text_start": 1082130432 +} \ No newline at end of file diff --git a/tools/esptool_py/esptool/targets/stub_flasher/2/esp32c61.json b/tools/esptool_py/esptool/targets/stub_flasher/2/esp32c61.json new file mode 100644 index 0000000000..5033ef2cf0 --- /dev/null +++ b/tools/esptool_py/esptool/targets/stub_flasher/2/esp32c61.json @@ -0,0 +1,5 @@ +{ + "entry": 1082130432, + "text": "ARFoAAbOAsY5IDJFEcEVIPJABWGCgAERBs4izAAQIyak/gMlxP49KAEA8kBiRAVhgoABEQbOIswAECMmpP4DJcT+JSgBAPJAYkQFYYKAAREGziLMABAjJqT+AQDyQGJEBWGCgAERBs4izAAQIyak/gEA8kBiRAVhgoABEQbOIswAECMmpP4BAPJAYkQFYYKA", + "text_start": 1082130432 +} \ No newline at end of file diff --git a/tools/esptool_py/esptool/targets/stub_flasher/2/esp32h2.json b/tools/esptool_py/esptool/targets/stub_flasher/2/esp32h2.json index 310e523e2b..5033ef2cf0 100644 --- a/tools/esptool_py/esptool/targets/stub_flasher/2/esp32h2.json +++ b/tools/esptool_py/esptool/targets/stub_flasher/2/esp32h2.json @@ -1 +1,5 @@ -{"entry":1082130432,"text":"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","text_start":1082130432,"data":"vAuAQJIMgEDODIBAvAuAQHgOgECSDIBAOg2AQKINgEDWDoBAAg+AQNIUgEAgDYBA0hSAQNYNgEC8C4BAkgyAQM4MgEBMD4BAbg2AQH4MgEDmEYBAUhCAQBYNgECSDIBAAAIAAAACAAAAAgAAAAgAAAABAAAAAgAAAAIAAAAQAAD//f////3////9////9/////7////9/////f///+///wABHAIdDhgDHhYUDxkRBAgfGw0XFRMQBxoMEgYLBQoJwNvc290AAAABAAAAAgAAAAAAAAAAAAAAAwAAAAQAAAAAAAAAAAAAAAUAAAAGAAAABwAAAAgAAAAJAAAACgAAAAsAAAAMAAAADQAAAA4AAAAPAAAAAQAAAAIAAAAFAAAABgAAAAkAAAAKAAAACwAAAAwAAAANAAAADgAAAA8AAAAQAAAAEQAAABIAAAATAAAAKBuAQCgbgEAAAAAAKBuAQCgbgEAoG4BAAAAAACgbgEAoG4BAKBuAQAAAAAAoG4BA","data_start":1082141160} \ No newline at end of file +{ + "entry": 1082130432, + "text": "ARFoAAbOAsY5IDJFEcEVIPJABWGCgAERBs4izAAQIyak/gMlxP49KAEA8kBiRAVhgoABEQbOIswAECMmpP4DJcT+JSgBAPJAYkQFYYKAAREGziLMABAjJqT+AQDyQGJEBWGCgAERBs4izAAQIyak/gEA8kBiRAVhgoABEQbOIswAECMmpP4BAPJAYkQFYYKA", + "text_start": 1082130432 +} \ No newline at end of file diff --git a/tools/esptool_py/esptool/targets/stub_flasher/2/esp32p4.json b/tools/esptool_py/esptool/targets/stub_flasher/2/esp32p4.json new file mode 100644 index 0000000000..f8c7868ebe --- /dev/null +++ b/tools/esptool_py/esptool/targets/stub_flasher/2/esp32p4.json @@ -0,0 +1,5 @@ +{ + "entry": 1341194240, + "text": "ARFoAAbOAsY5IDJFEcEVIPJABWGCgAERBs4izAAQIyak/gMlxP49KAEA8kBiRAVhgoABEQbOIswAECMmpP4DJcT+JSgBAPJAYkQFYYKAAREGziLMABAjJqT+AQDyQGJEBWGCgAERBs4izAAQIyak/gEA8kBiRAVhgoABEQbOIswAECMmpP4BAPJAYkQFYYKA", + "text_start": 1341194240 +} \ No newline at end of file diff --git a/tools/esptool_py/esptool/targets/stub_flasher/2/esp32s2.json b/tools/esptool_py/esptool/targets/stub_flasher/2/esp32s2.json index 23ffd983c7..aef0fd5005 100644 --- a/tools/esptool_py/esptool/targets/stub_flasher/2/esp32s2.json +++ b/tools/esptool_py/esptool/targets/stub_flasher/2/esp32s2.json @@ -1 +1,5 @@ -{"entry":1073913140,"text":"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5hMQIABioAUQcSCV0P8F3P8CIQEAxRMCIQAAtRMCIQISIQMQIAAQNQDwQQAQASAS0f8CYQMA0UkAxgMCYQEAtgMCYQAAwUkA1gMCYQJFz/8BR/4A5hMQIABioAYQcSBVzP/F1v8CIQEAxhMCIQAAthMCIQISIQMQIAAQNgDwQQAQASAS0f8CYQMA0UkAxwMCYQEAtwMCYQAAwUkA1wMCYQIFyv8BM/4A5hMQIABioAcQcSAVyP+F0f8CIQEAxxMCIQAAtxMCIQISIQMQIAAQNwDwQQA2QQCBEP6tAr0D4AgAHfAANkEA8EEAAAAEowJABKMCQASjAkAEowJABKMCQASjAkAEowJABKMCQASjAkAEowJABKMCQASjAkAEowJABKMCQASjAkAEowJABKMCQASjAkAEowJABKMCQASjAkAEowJABKMCQASjAkAEowJABKMCQASjAkAEowJABKMCQASjAkAEowJABKMCQASjAkAEowJABKMCQASjAkAEowJABKMCQASjAkAEowJABKMCQAAAAAAAAAAAAAAAAAAAAAAEowJABKMCQASjAkAEowJABKMCQASjAkAEowJABKMCQASjAkAEowJABKMCQASjAkAEowJABKMCQASjAkAEowJABKMCQASjAkAEowJABKMCQASjAkAEowJABKMCQASjAkAEowJABKMCQASjAkAEowJABKMCQAAAAAAEowJABKMCQASjAkAEowJABKMCQASjAkAEowJABKMCQASjAkAAAAAABKMCQASjAkAEowJABKMCQASjAkAAAAAABKMCQASjAkAEowJABKMCQA==","text_start":1073905664,"data":"a5ACQG+RAkAbkgJAa5ACQGuSAkBvkQJA2pICQBCTAkBgkwJAvpMCQMCaAkDtkwJAwJoCQByUAkBrkAJAb5ECQBuSAkDGlAJA0pUCQF2RAkAClgJAWJYCQFmYAkBvkQJAvKYCQCKkAkADpwJAA6cCQAOnAkCnpgJAA6cCQAOnAkCtpgJAs6YCQLmmAkDA29zb3QA+AD8AQABBAEIAQwBEAEUARwBIAEkAAAAAAP83BgAAADgAAIjAKAAAAFMAAAGEAAAAAABAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAMAAAABAAAAAQAAAAAAAAADAAAAAAAAAAEAAAABAAAAAgAAAAIAAAACAAAAAwAAAAMAAAAAAAAAAAAAAAAAAAADAAAAAAAAAAMAAAAAAAAAAAAAAAAAAQACAAMABAAFAAYABwAIAAkACgALAAwADQAOAA8AEAARABIAEwAUABUAFgAXABgAGQAaABsAHAAdAB4AHwAgACEAIgAjACQAJQAmACcAKABfAF8AXwBfAC0ALgAvADAAMQAyADMANAA1ADYANwA4ADkAOgA7ADwAPQA+AD8AQABBAEIAQwBEAEUARgBHAEgASQBfAEsATABNAE4ATwBQAFEAUgBTAF8AVQBWAFcAWABZAF8AWwBcAF0AXgAAAA==","data_start":1073464320} \ No newline at end of file +{ + "entry": 1073905664, + "text": "NmEADAgQoSCJARARIOUAAKgBjEoQESBlAQAd8DZhAH0BKQeoByUCAD3wHfA2YQB9ASkHqAflAQA98B3wNmEAfQEpBz3wHfAANmEAfQEpBz3wHfAANmEAfQEpBz3wHfAA", + "text_start": 1073905664 +} \ No newline at end of file diff --git a/tools/esptool_py/esptool/targets/stub_flasher/2/esp32s3.json b/tools/esptool_py/esptool/targets/stub_flasher/2/esp32s3.json index 8be985b1b7..ea3f955ecc 100644 --- a/tools/esptool_py/esptool/targets/stub_flasher/2/esp32s3.json +++ b/tools/esptool_py/esptool/targets/stub_flasher/2/esp32s3.json @@ -1 +1,5 @@ -{"entry":1077391268,"text":"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HTAgEzIhHjAhEzIhHzAiEzIhIDAjEzIhJDDo8zIhJTDp8wMBJhMBJyMBKDMBKUMBKlMBK2MBLHMBLYMBLpMBL6MBMLMBMcMBMtMBM+MBNPMBNSIhBDIhBUIhBlIhB2IhCHIhCYIhCpIhC6IhDLIhDcIhDtIhD+IhEPIhEYAAAPBBAD3wEAEgEtH/AmEDANFJAOYDAmEBAOgDAmETAO4DAmEUALEDAmEAAMFJANEDAmECxeD/Adj+AOYTECAAYiETJkYIEHEgVSgARgQAAdP+AOYTECAAYqABEHEgVbX/he7/AiEBAOYTAiEAALETAiECEiEDECAAADAA8EEAEAEgEtH/AmEDANFJAOYDAmEBAOgDAmETAO4DAmEUAMADAmEAAMFJANcDAmECRdn/YiETEHEglSEABen/AiEBAOYTAiEAALETAiECEiEDECAAADIA8EEAABABIBLR/wJhAwDRSQDCAwJhAQCyAwJhAADBSQDSAwJhAoXU/wGp/gDmExAgAGKgAhBxIJWq/8Xj/wIhAQDCEwIhAACyEwIhAhIhAxAgABAyAPBBABABIBLR/wJhAwDRSQDDAwJhAQCzAwJhAADBSQDTAwJhAkXP/wGS/gDmExAgAGKgAxBxIFWl/4Xe/wIhAQDDEwIhAACzEwIhAhIhAxAgABAzAPBBABABIBLR/wJhAwDRSQDEAwJhAQC0AwJhAADBSQDUAwJhAgXK/wGA/gDmExAgAGKgBBBxINW//0XZ/wIhAQDEEwIhAAC0EwIhAhIhAxAgABA0APBBABABIBLR/wJhAwDRSQDFAwJhAQC1AwJhAADBSQDVAwJhAsXE/wFs/gDmExAgAGKgBRBxIJW7/wXU/wIhAQDFEwIhAAC1EwIhAhIhAxAgABA1APBBABABIBLR/wJhAwDRSQDGAwJhAQC2AwJhAADBSQDWAwJhAoW//wFY/gDmExAgAGKgBhBxIFW3/8XO/wIhAQDGEwIhAAC2EwIhAhIhAxAgABA2APBBABABIBLR/wJhAwDRSQDHAwJhAQC3AwJhAADBSQDXAwJhAkW6/wFE/gDmExAgAGKgBxBxIBWz/4XJ/wIhAQDHEwIhAAC3EwIhAhIhAxAgABA3APBBADZBAIEQ/q0CvQPgCAAd8AA2QQDwQQAAAISsN0CErDdAhKw3QISsN0CErDdAhKw3QISsN0CErDdAhKw3QISsN0CErDdAhKw3QISsN0CErDdAhKw3QISsN0CErDdAhKw3QISsN0CErDdAhKw3QISsN0CErDdAAAAAAISsN0CErDdAhKw3QISsN0CErDdAhKw3QISsN0CErDdAhKw3QAAAAAAAAAAAhKw3QISsN0CErDdAhKw3QISsN0CErDdAhKw3QISsN0CErDdAhKw3QISsN0AAAAAAhKw3QISsN0CErDdAhKw3QISsN0CErDdAhKw3QISsN0CErDdAhKw3QISsN0CErDdAhKw3QISsN0CErDdAhKw3QISsN0CErDdAhKw3QISsN0CErDdAhKw3QISsN0CErDdAhKw3QISsN0CErDdAhKw3QISsN0CErDdAhKw3QAAAAACErDdAhKw3QISsN0CErDdAhKw3QISsN0CErDdAhKw3QISsN0CErDdAhKw3QISsN0CErDdAhKw3QISsN0CErDdAhKw3QISsN0CErDdAhKw3QA==","text_start":1077379072,"data":"lZQ3QJmVN0BFljdAlZQ3QJWWN0CZlTdABJc3QDqXN0CKlzdA6Jc3QOmeN0AXmDdA6Z43QEaYN0CVlDdAmZU3QEWWN0DvmDdA+5k3QIeVN0ArmjdAgZo3QIGcN0CZlTdATK43QN+tN0BTrjdAU643QFOuN0A3rjdAU643QFOuN0A9rjdAQ643QEmuN0DA29zb3QAAAAAAAAD/NwYAAAA4AACIwCgAAABTAAABhAAAAAAAQAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAADAAAAAQAAAAEAAAAAAAAAAwAAAAAAAAABAAAAAQAAAAIAAAACAAAAAgAAAAMAAAADAAAAAAAAAAAAAAAAAAAAAwAAAAAAAAADAAAAAAAAAAAAAAAAAAEAAgADAAQABQAGAAcACAAJAAoACwAMAA0ADgAPABAAEQASABMAFAAVABYAYwAYABkAGgAbABwAHQAeAB8AIABjAGMAIwAkACUAJgAnACgAKQAqACsALAAtAGMALwAwADEAMgAzADQANQA2ADcAOAA5ADoAOwA8AD0APgA/AEAAQQBCAEMARABFAEYARwBIAEkASgBLAEwATQBjAE8AUABRAFIAUwBUAFUAVgBXAFgAWQBaAFsAXABdAF4AXwBgAGEAYgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAA==","data_start":1070137376} \ No newline at end of file +{ + "entry": 1077379072, + "text": "NmEADAgQoSCJARARIOUAAKgBjEoQESBlAQAd8DZhAH0BKQeoByUCAD3wHfA2YQB9ASkHqAflAQA98B3wNmEAfQEpBz3wHfAANmEAfQEpBz3wHfAANmEAfQEpBz3wHfAA", + "text_start": 1077379072 +} \ No newline at end of file diff --git a/tools/esptool_py/esptool/targets/stub_flasher/2/esp8266.json b/tools/esptool_py/esptool/targets/stub_flasher/2/esp8266.json new file mode 100644 index 0000000000..209c43c1ee --- /dev/null +++ b/tools/esptool_py/esptool/targets/stub_flasher/2/esp8266.json @@ -0,0 +1,5 @@ +{ + "entry": 1074843652, + "text": "qBAAQAH//0YAAAAAEsHgIqAAImEALQECYQcFAQAoAYwShQIACHESwSAN8AASweAJcflh/QEpDygPRQQAPfAdDwhx+GESwSAN8AAAABLB4Alx+WH9ASkPKA+FAwA98B0PCHH4YRLBIA3wAAAAEsHg+XH9ASkPPfAdD/hxEsEgDfASweD5cf0BKQ898B0P+HESwSAN8BLB4Plx/QEpDz3wHQ/4cRLBIA3w", + "text_start": 1074843648 +} \ No newline at end of file diff --git a/tools/esptool_py/esptool/targets/stub_flasher/stub_flasher_32.json b/tools/esptool_py/esptool/targets/stub_flasher/stub_flasher_32.json deleted file mode 100644 index 56221e30bd..0000000000 --- a/tools/esptool_py/esptool/targets/stub_flasher/stub_flasher_32.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "entry": 1074521580, - "text": "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", - "text_start": 1074520064, - "data": "DMD8P+znC0B/6AtAZ+0LQAbpC0Cf6AtABukLQGXpC0CC6gtA9OoLQJ3qC0CV5wtAGuoLQHTqC0CI6QtAGOsLQLDpC0AY6wtAbegLQMroC0AG6QtAZekLQIXoC0DI6wtAKe0LQLjmC0BL7QtAuOYLQLjmC0C45gtAuOYLQLjmC0C45gtAuOYLQLjmC0Bv6wtAuOYLQEnsC0Ap7QtA", - "data_start": 1073605544, - "bss_start": 1073528832 -} \ No newline at end of file diff --git a/tools/esptool_py/esptool/targets/stub_flasher/stub_flasher_32c2.json b/tools/esptool_py/esptool/targets/stub_flasher/stub_flasher_32c2.json deleted file mode 100644 index f10ec7b48e..0000000000 --- a/tools/esptool_py/esptool/targets/stub_flasher/stub_flasher_32c2.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "entry": 1077413304, - "text": "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", - "text_start": 1077411840, - "data": "DEDKP+AIOEAsCThAhAk4QFIKOEC+CjhAbAo4QKgHOEAOCjhATgo4QJgJOEBYBzhAzAk4QFgHOEC6CDhA/gg4QCwJOECECThAzAg4QBIIOEBCCDhAyAg4QBYNOEAsCThA1gs4QMoMOECkBjhA9Aw4QKQGOECkBjhApAY4QKQGOECkBjhApAY4QKQGOECkBjhAcgs4QKQGOEDyCzhAygw4QA==", - "data_start": 1070295976, - "bss_start": 1070219264 -} \ No newline at end of file diff --git a/tools/esptool_py/esptool/targets/stub_flasher/stub_flasher_32c3.json b/tools/esptool_py/esptool/targets/stub_flasher/stub_flasher_32c3.json deleted file mode 100644 index 788ae646df..0000000000 --- a/tools/esptool_py/esptool/targets/stub_flasher/stub_flasher_32c3.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "entry": 1077413584, - "text": "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", - "text_start": 1077411840, - "data": "GEDIP8AKOEAQCzhAaAs4QDYMOECiDDhAUAw4QHIJOEDyCzhAMgw4QHwLOEAiCThAsAs4QCIJOECaCjhA4Ao4QBALOEBoCzhArAo4QNYJOEAgCjhAqAo4QPoOOEAQCzhAug04QLIOOEBiCDhA2g44QGIIOEBiCDhAYgg4QGIIOEBiCDhAYgg4QGIIOEBiCDhAVg04QGIIOEDYDThAsg44QA==", - "data_start": 1070164916, - "bss_start": 1070088192 -} \ No newline at end of file diff --git a/tools/esptool_py/esptool/targets/stub_flasher/stub_flasher_32c5beta3.json b/tools/esptool_py/esptool/targets/stub_flasher/stub_flasher_32c5beta3.json deleted file mode 100644 index 5266fcdc3c..0000000000 --- a/tools/esptool_py/esptool/targets/stub_flasher/stub_flasher_32c5beta3.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "entry": 1082132164, - "text": "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", - "text_start": 1082130432, - "data": "FACEQGwKgEC8CoBAFAuAQOILgEBODIBA/AuAQDgJgECeC4BA3guAQCgLgEDoCIBAXAuAQOgIgEBGCoBAjAqAQLwKgEAUC4BAWAqAQJwJgEDMCYBAVAqAQKYOgEC8CoBAZg2AQF4OgEAoCIBAhg6AQCgIgEAoCIBAKAiAQCgIgEAoCIBAKAiAQCgIgEAoCIBAAg2AQCgIgECEDYBAXg6AQA==", - "data_start": 1082469296, - "bss_start": 1082392576 -} \ No newline at end of file diff --git a/tools/esptool_py/esptool/targets/stub_flasher/stub_flasher_32c6.json b/tools/esptool_py/esptool/targets/stub_flasher/stub_flasher_32c6.json deleted file mode 100644 index b903b35215..0000000000 --- a/tools/esptool_py/esptool/targets/stub_flasher/stub_flasher_32c6.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "entry": 1082132164, - "text": "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", - "text_start": 1082130432, - "data": "FACEQHIKgEDCCoBAGguAQOgLgEBUDIBAAgyAQD4JgECkC4BA5AuAQC4LgEDuCIBAYguAQO4IgEBMCoBAkgqAQMIKgEAaC4BAXgqAQKIJgEDSCYBAWgqAQKwOgEDCCoBAbA2AQGQOgEAuCIBAjA6AQC4IgEAuCIBALgiAQC4IgEAuCIBALgiAQC4IgEAuCIBACA2AQC4IgECKDYBAZA6AQA==", - "data_start": 1082469296, - "bss_start": 1082392576 -} \ No newline at end of file diff --git a/tools/esptool_py/esptool/targets/stub_flasher/stub_flasher_32c6beta.json b/tools/esptool_py/esptool/targets/stub_flasher/stub_flasher_32c6beta.json deleted file mode 100644 index 7fd5c0ec6c..0000000000 --- a/tools/esptool_py/esptool/targets/stub_flasher/stub_flasher_32c6beta.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "entry": 1077413318, - "text": "ARG3BwBgTsaDqYcASsg3Scg/JspSxAbOIsy3BABgfVoTCQkAwEwTdPQ/DeDyQGJEI6g0AUJJ0kSySSJKBWGCgIhAgycJABN19Q+Cl30U4xlE/8m/EwcADJRBqodjGOUAhUeFxiOgBQB5VYKABUdjh+YACUZjjcYAfVWCgEIFEwewDUGFY5XnAolHnMH1t5MGwA1jFtUAmMETBQAMgoCTBtANfVVjldcAmMETBbANgoC3dck/QRGThQW6BsZhP2NFBQa3d8k/k4eHsQOnBwgD1kcIE3X1D5MGFgDCBsGCI5LXCDKXIwCnAAPXRwiRZ5OHBwRjHvcCN/fIPxMHh7GhZ7qXA6YHCLc2yT+3d8k/k4eHsZOGhrVjH+YAI6bHCCOg1wgjkgcIIaD5V+MG9fyyQEEBgoAjptcII6DnCN23NycAYHxLnYv1/zc3AGB8S52L9f+CgEERBsbdN7cnAGAjpgcCNwcACJjDmEN9/8hXskATRfX/BYlBAYKAQREGxtk/fd03BwBAtycAYJjDNycAYBxD/f+yQEEBgoBBESLEN8TIP5MHxABKwAOpBwEGxibCYwoJBEU3OcW9RxMExACBRGPWJwEERL2Ik7QUAH03hT8cRDcGgAATl8cAmeA3BgABt/b/AHWPtyYAYNjCkMKYQn3/QUeR4AVHMwnpQLqXIygkARzEskAiRJJEAklBAYKAQREGxhMHAAxjEOUCEwWwDZcAyP/ngMDjEwXADbJAQQEXA8j/ZwDD4hMHsA3jGOX+lwDI/+eAwOETBdANxbdBESLEJsIGxiqEswS1AGMXlACyQCJEkkRBAYKAA0UEAAUERTfttxMFAAwXA8j/ZwBD3jVxJstOx/1yhWn9dCLNSslSxVbDBs+ThIT6FpGThwkHppcYCLOE5wAqiSaFLoSXAMj/54AgNpOHCQcYCAVqupezikdBMeQFZ311kwWF+pMHBwcTBYX5FAiqlzOF1wCTBwcHrpezhdcAKsaXAMj/54DgMjJFwUWhPwFFhWIWkfpAakTaREpJukkqSppKDWGCgKKJY3OKAIVpTobWhUqFlwDI/+eA4OATdfUPAe1OhtaFJoWXAMj/54AgLk6ZMwQ0QVG3EwUwBlW/MXH9ck7XUtVW017PBt8i3SbbStla0WLNZstqyW7HqokWkRMFAAIuirKKtosCypcAyP/ngOAohWdj4FcThWR9dBMEhPqThwQHopcYCDOE5wAihZcAyP/ngGAnfXsTDDv5kwyL+ROHBAeThwQHFAhil+aXAUkzDNcAs4zXAFJNY3xNCWNxqQNBqFU1poUIAaU9cT0mhgwBIoWXAMj/54BAI6aZJpljdUkDswepQWPxdwOzBCpBY/OaANaEJoYMAU6FlwDI/+eAQNITdfUPVd0CzIFEeV2NTaMJAQBihZcAyP/ngADEffkDRTEB5oUFMWNPBQDj4p3+hWeThwcHppcYCLqX2pcjiqf4hQTxt+MVpf2RR+OF9PYFZ311kwcHB5MFhfoTBYX5FAiqlzOF1wCTBwcHrpezhdcAKsaXAMj/54BgGe0zMkXBRX07zTMTBQAClwDI/+eAABeFYhaR+lBqVNpUSlm6WSpamloKW/pLakzaTEpNuk0pYYKAAREGziLMnTk3BM4/bAATBUT/lwDI/+eAQMiqhwVFleeyR5P3ByA+xkE5NycAYBxHtwZAABMFRP/VjxzHskWXAMj/54DAxTM1oADyQGJEBWGCgEERt8fIPwbGk4fHAAVHI4DnABPXxQCYxwVnfRfMw8jH+Y06laqVsYGMyyOqBwBBNxnBEwVQDLJAQQGCgAERIsw3xMg/kwfEACbKxEdOxgbOSsiqiRMExABj85UAroSpwAMpRAAmmRNZyQAcSGNV8AAcRGNe+QLpNn3dSEAmhs6FlwDI/+eAQLkTdfUPAcWTB0AMXMhcQKaXXMBcRIWPXMTyQGJE0kRCSbJJBWGCgOE+bb+3V0FJGXGTh/eEAUU+zobeotym2srYztbS1NbS2tDezuLM5srqyO7GlwDI/+eAoKy3R8g/N3fJP5OHBwATB4e6Y+XnFK0xkUVoCD05jTG398g/k4eHsSFnPpcjIPcItwU4QLcHOECThwcLAUaThQUAN0nIPxVFIyD5AJcAyP/ngAD8NwcAYFxHEwUAArd5yT+T5xcQXMeXAMj/54DA+pcAyP/ngEALt0cAYJxfk4mJsRMJCQAJ5fGL4RcTtRcAgUWXAMj/54CgrcFnt8TIP/0XEwcAEIVmQWa3BQABAUWThMQAt0rIPw1qlwDI/+eAIKgTi8oAJpqDp8kI9d+Dq8kIhUcjpgkIIwLxAoPHGwAJRyMT4QKjAvECAtRNR2OL5wZRR2OJ5wYpR2Of5wCDxzsAA8crAKIH2Y8RR2OW5wCDp4sAnEM+1KU2oUVIEDU+g8c7AAPHKwCiB9mPEWdBB2N+9wITBbANlwDI/+eAAJMTBcANlwDI/+eAQJITBeAOlwDI/+eAgJElNr23I6AHAJEHRb3JRyMT8QJ9twPHGwDRRmPn5gKFRmPm5gABTBME8A+dqHkXE3f3D8lG4+jm/rd2yT8KB5OGxro2lxhDAoeTBgcDk/b2DxFG42nW/BMH9wITd/cPjUZj7uYIt3bJPwoHk4aGvzaXGEMChxMHQAJjmucQAtQdRAFFlwDI/+eA4IgBRSU8aTxhPKFFSBB9FK00ffABTAFEE3X0DwU0E3X8Dyk8tTzjEQTsg8cbAElHY2X3MAlH43n36vUXk/f3Dz1H42P36jd3yT+KBxMHh8C6l5xDgocFRJ3rcBCBRQFFl7DM/+eA4JMd4dFFaBAxNAFEMagFRIHvlwDI/+eAAI0zNKAAKaAhR2OF5wAFRAFMYbcDrIsAA6TLALNnjADSB/X3sTFl9cFsIpz9HH19MwWMQF3cs3eVAZXjwWwzBYxAY+aMAv18MwWMQF3QMYGXAMj/54AgiF35ZpT1tzGBlwDI/+eAIIdd8WqU0bdBgZcAyP/ngOCFWfkzBJRBwbchR+OK5/ABTBMEAAw5t0FHzb9BRwVE453n9oOlywADpYsAcTK5v0FHBUTjk+f2A6cLAZFnY+jnHoOlSwEDpYsACTGBt0FHBUTjlOf0g6cLARFnY2n3HAOnywCDpUsBA6WLADOE5wLxPiOsBAAjJIqwCb8DxwQAYwMHFAOniwDBFxMEAAxjE/cAwEgBR5MG8A5jRvcCg8dbAAPHSwABTKIH2Y8Dx2sAQgddj4PHewDiB9mP44T25hMEEAyFtTOG6wADRoYBBQexjuG3g8cEAP3H3ERjnQcUwEgjgAQAVb1hR2OW5wKDp8sBA6eLAYOmSwEDpgsBg6XLAAOliwCX8Mf/54AgdiqMMzSgAAG9AUwFRCm1EUcFROOd5+a3lwBgtF9ld30XBWb5jtGOA6WLALTftFeBRfmO0Y601/Rf+Y7RjvTf9FN1j1GP+NOX8Mf/54BAdAW1E/f3AOMXB+qT3EcAE4SLAAFMfV3jd5zbSESX8Mf/54BAYBhEVEAQQPmOYwenARxCE0f3/32P2Y4UwgUMQQTZvxFHtbVBRwVE45rn3oOniwADp0sBIyT5ACMi6QDJs4MlSQDBF5Hlic8BTBMEYAyhuwMniQBjZvcGE/c3AOMbB+IDKIkAAUYBRzMF6ECzhuUAY2n3AOMHBtIjJKkAIyLZAA2zM4brABBOEQeQwgVG6b8hRwVE45Tn2AMkiQAZwBMEgAwjJAkAIyIJADM0gAC9swFMEwQgDMW5AUwTBIAM5bEBTBMEkAzFsRMHIA1jg+cMEwdADeOR57oDxDsAg8crACIEXYyX8Mf/54CgXgOsxABBFGNzhAEijOMPDLbAQGKUMYCcSGNV8ACcRGNa9Arv8A/gdd3IQGKGk4WLAZfwx//ngKBaAcWTB0AM3MjcQOKX3MDcRLOHh0HcxJfwx//ngIBZFb4JZRMFBXEDrMsAA6SLAJfwx//ngMBKtwcAYNhLtwYAAcEWk1dHARIHdY+9i9mPs4eHAwFFs9WHApfwx//ngKBLEwWAPpfwx//ngGBH3bSDpksBA6YLAYOlywADpYsA7/AP9sG8g8U7AIPHKwAThYsBogXdjcEVgTptvO/wb9mBtwPEOwCDxysAE4yLASIEXYzcREEUxeORR4VLY/6HCJMHkAzcyHm0A6cNACLQBUizh+xAPtaDJ4qwY3P0AA1IQsY6xO/w79QiRzJIN8XIP+KFfBCThsoAEBATBUUCl/DH/+eAoEg398g/kwjHAIJXA6eIsIOlDQAdjB2PPpyyVyOk6LCqi76VI6C9AJOHygCdjQHFoWdjlvUAWoV1MCOgbQEJxNxEmcPjQHD5Y98LAJMHcAyFv4VLt33JP7fMyD+TjY26k4zMAOm/45ULntxE44IHnpMHgAyxt4OniwDjmwecAUWX8Mf/54CAOAllEwUFcZfwx//ngKA0l/DH/+eAIDhNugOkywDjBgSaAUWX8Mf/54DgNRMFgD6X8Mf/54AgMgKUQbr2UGZU1lRGWbZZJlqWWgZb9ktmTNZMRk22TQlhgoA=", - "text_start": 1077411840, - "data": "DEDIP/gIOEBECThAnAk4QGoKOEDWCjhAhAo4QMAHOEAmCjhAZgo4QLAJOEBwBzhA5Ak4QHAHOEDSCDhAFgk4QEQJOECcCThA5Ag4QCoIOEBaCDhA4Ag4QC4NOEBECThA7gs4QOIMOEC8BjhADA04QLwGOEC8BjhAvAY4QLwGOEC8BjhAvAY4QLwGOEC8BjhAigs4QLwGOEAKDDhA4gw4QA==", - "data_start": 1070164904, - "bss_start": 1070088192 -} \ No newline at end of file diff --git a/tools/esptool_py/esptool/targets/stub_flasher/stub_flasher_32h2.json b/tools/esptool_py/esptool/targets/stub_flasher/stub_flasher_32h2.json deleted file mode 100644 index 24964cde64..0000000000 --- a/tools/esptool_py/esptool/targets/stub_flasher/stub_flasher_32h2.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "entry": 1082132164, - "text": "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", - "text_start": 1082130432, - "data": "FACDQHIKgEDCCoBAGguAQOgLgEBUDIBAAgyAQD4JgECkC4BA5AuAQC4LgEDuCIBAYguAQO4IgEBMCoBAkgqAQMIKgEAaC4BAXgqAQKIJgEDSCYBAWgqAQKwOgEDCCoBAbA2AQGQOgEAuCIBAjA6AQC4IgEAuCIBALgiAQC4IgEAuCIBALgiAQC4IgEAuCIBACA2AQC4IgECKDYBAZA6AQA==", - "data_start": 1082403760, - "bss_start": 1082327040 -} \ No newline at end of file diff --git a/tools/esptool_py/esptool/targets/stub_flasher/stub_flasher_32h2beta1.json b/tools/esptool_py/esptool/targets/stub_flasher/stub_flasher_32h2beta1.json deleted file mode 100644 index 03e110c5c2..0000000000 --- a/tools/esptool_py/esptool/targets/stub_flasher/stub_flasher_32h2beta1.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "entry": 1077413318, - "text": "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", - "text_start": 1077411840, - "data": "DEDIP/gIOEBECThAnAk4QGoKOEDWCjhAhAo4QMAHOEAmCjhAZgo4QLAJOEBwBzhA5Ak4QHAHOEDSCDhAFgk4QEQJOECcCThA5Ag4QCoIOEBaCDhA4Ag4QC4NOEBECThA7gs4QOIMOEC8BjhADA04QLwGOEC8BjhAvAY4QLwGOEC8BjhAvAY4QLwGOEC8BjhAigs4QLwGOEAKDDhA4gw4QA==", - "data_start": 1070164904, - "bss_start": 1070088192 -} \ No newline at end of file diff --git a/tools/esptool_py/esptool/targets/stub_flasher/stub_flasher_32h2beta2.json b/tools/esptool_py/esptool/targets/stub_flasher/stub_flasher_32h2beta2.json deleted file mode 100644 index ce3afe7ac0..0000000000 --- a/tools/esptool_py/esptool/targets/stub_flasher/stub_flasher_32h2beta2.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "entry": 1077413318, - "text": "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", - "text_start": 1077411840, - "data": "DEDIP/gIOEBECThAnAk4QGoKOEDWCjhAhAo4QMAHOEAmCjhAZgo4QLAJOEBwBzhA5Ak4QHAHOEDSCDhAFgk4QEQJOECcCThA5Ag4QCoIOEBaCDhA4Ag4QC4NOEBECThA7gs4QOIMOEC8BjhADA04QLwGOEC8BjhAvAY4QLwGOEC8BjhAvAY4QLwGOEC8BjhAigs4QLwGOEAKDDhA4gw4QA==", - "data_start": 1070164904, - "bss_start": 1070088192 -} \ No newline at end of file diff --git a/tools/esptool_py/esptool/targets/stub_flasher/stub_flasher_32p4.json b/tools/esptool_py/esptool/targets/stub_flasher/stub_flasher_32p4.json deleted file mode 100644 index 6f37e91b27..0000000000 --- a/tools/esptool_py/esptool/targets/stub_flasher/stub_flasher_32p4.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "entry": 1341195918, - "text": "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", - "text_start": 1341194240, - "data": "EAD1TwYK8U9WCvFPrgrxT4QL8U/wC/FPngvxT9QI8U9AC/FPgAvxT8IK8U+ECPFP9grxT4QI8U/gCfFPJgrxT1YK8U+uCvFP8gnxTzgJ8U9oCfFP7gnxT0AO8U9WCvFPCA3xTwAO8U/EB/FPJA7xT8QH8U/EB/FPxAfxT8QH8U/EB/FPxAfxT8QH8U/EB/FPpAzxT8QH8U8mDfFPAA7xTw==", - "data_start": 1341533100, - "bss_start": 1341456384 -} \ No newline at end of file diff --git a/tools/esptool_py/esptool/targets/stub_flasher/stub_flasher_32s2.json b/tools/esptool_py/esptool/targets/stub_flasher/stub_flasher_32s2.json deleted file mode 100644 index 68de613890..0000000000 --- a/tools/esptool_py/esptool/targets/stub_flasher/stub_flasher_32s2.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "entry": 1073907716, - "text": "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", - "text_start": 1073905664, - "data": "WAD9P0uLAkDdiwJA8pACQGaMAkD+iwJAZowCQMWMAkDejQJAUY4CQPmNAkDVigJAd40CQNCNAkDojAJAdI4CQBCNAkB0jgJAy4sCQCqMAkBmjAJAxYwCQOOLAkAXiwJAN48CQKqQAkDqiQJA0ZACQOqJAkDqiQJA6okCQOqJAkDqiQJA6okCQOqJAkDqiQJA1I4CQOqJAkDJjwJAqpACQA==", - "data_start": 1073622012, - "bss_start": 1073545216 -} \ No newline at end of file diff --git a/tools/esptool_py/esptool/targets/stub_flasher/stub_flasher_32s3.json b/tools/esptool_py/esptool/targets/stub_flasher/stub_flasher_32s3.json deleted file mode 100644 index 484a8832fd..0000000000 --- a/tools/esptool_py/esptool/targets/stub_flasher/stub_flasher_32s3.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "entry": 1077381760, - "text": "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", - "text_start": 1077379072, - "data": "XADKP16ON0AzjzdAR5Q3QL2PN0BTjzdAvY83QB2QN0A6kTdArJE3QFWRN0DpjTdA0JA3QCyRN0BAkDdA0JE3QGiQN0DQkTdAIY83QH6PN0C9jzdAHZA3QDmPN0AqjjdAkJI3QA2UN0AAjTdALZQ3QACNN0AAjTdAAI03QACNN0AAjTdAAI03QACNN0AAjTdAKpI3QACNN0AlkzdADZQ3QAQInwAAAAAAAAAYAQQIBQAAAAAAAAAIAQQIBgAAAAAAAAAAAQQIIQAAAAAAIAAAEQQI3AAAAAAAIAAAEQQIDAAAAAAAIAAAAQQIEgAAAAAAIAAAESAoDAAQAQAA", - "data_start": 1070279676, - "bss_start": 1070202880 -} \ No newline at end of file diff --git a/tools/esptool_py/esptool/targets/stub_flasher/stub_flasher_32s3beta2.json b/tools/esptool_py/esptool/targets/stub_flasher/stub_flasher_32s3beta2.json deleted file mode 100644 index da770f7b97..0000000000 --- a/tools/esptool_py/esptool/targets/stub_flasher/stub_flasher_32s3beta2.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "entry": 1077380596, - "text": "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", - "text_start": 1077379072, - "data": "DADKPxeIN0CriDdAw403QDeJN0DLiDdAN4k3QJaJN0C2ijdAKIs3QNGKN0ChhzdASIo3QKiKN0C5iTdATIs3QOGJN0BMizdAmYg3QPiIN0A3iTdAlok3QLGIN0DjhzdABIw3QIWNN0DAhjdAp403QMCGN0DAhjdAwIY3QMCGN0DAhjdAwIY3QMCGN0DAhjdAqYs3QMCGN0CZjDdAhY03QA==", - "data_start": 1070279592, - "bss_start": 1070202880 -} \ No newline at end of file diff --git a/tools/esptool_py/esptool/targets/stub_flasher/stub_flasher_8266.json b/tools/esptool_py/esptool/targets/stub_flasher/stub_flasher_8266.json deleted file mode 100644 index f68ffef926..0000000000 --- a/tools/esptool_py/esptool/targets/stub_flasher/stub_flasher_8266.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "entry": 1074843652, - "text": "qBAAQAH//0ZzAAAAkIH/PwgB/z+AgAAAhIAAAEBAAABIQf8/lIH/PzH5/xLB8CAgdAJhA4XwATKv/pZyA1H0/0H2/zH0/yAgdDA1gEpVwCAAaANCFQBAMPQbQ0BA9MAgAEJVADo2wCAAIkMAIhUAMev/ICD0N5I/Ieb/Meb/Qen/OjLAIABoA1Hm/yeWEoYAAAAAAMAgACkEwCAAWQNGAgDAIABZBMAgACkDMdv/OiIMA8AgADJSAAgxEsEQDfAAoA0AAJiB/z8Agf4/T0hBSais/z+krP8/KNAQQFzqEEAMAABg//8AAAAQAAAAAAEAAAAAAYyAAAAQQAAAAAD//wBAAAAAgf4/BIH+PxAnAAAUAABg//8PAKis/z8Igf4/uKz/PwCAAAA4KQAAkI//PwiD/z8Qg/8/rKz/P5yv/z8wnf8/iK//P5gbAAAACAAAYAkAAFAOAABQEgAAPCkAALCs/z+0rP8/1Kr/PzspAADwgf8/DK//P5Cu/z+ACwAAEK7/P5Ct/z8BAAAAAAAAALAVAADx/wAAmKz/P7wPAECIDwBAqA8AQFg/AEBERgBALEwAQHhIAEAASgBAtEkAQMwuAEDYOQBASN8AQJDhAEBMJgBAhEkAQCG9/5KhEJARwCJhIyKgAAJhQ8JhQtJhQeJhQPJhPwHp/8AAACGz/zG0/wwEBgEAAEkCSyI3MvjFtgEioIwMQyohBakBxbUBIX3/wXv/Maz/KizAIADJAiGp/wwEOQIxqf8MUgHZ/8AAADGn/yKhAcAgAEgDICQgwCAAKQMioCAB0//AAAAB0v/AAAAB0v/AAABxnv9Rn/9Bn/8xn/9ioQAMAgHN/8AAACGd/zFj/yojwCAAOAIWc//AIADYAgwDwCAAOQIMEiJBhCINAQwkIkGFQlFDMmEiJpIJHDM3EiCGCAAAACINAzINAoAiETAiIGZCESgtwCAAKAIiYSIGAQAcIiJRQ8WpASKghAyDGiJFnAEiDQMyDQKAIhEwMiAhgP83shMioMAFlwEioO6FlgEFpwFG3P8AACINAQy0R5ICBpkAJzRDZmICxssA9nIgZjIChnEA9kIIZiICxlYARsoAZkICBocAZlICxqsAhsYAJoJ59oIChqsADJRHkgKGjwBmkgIGowAGwAAcJEeSAkZ8ACc0Jwz0R5IChj4AJzQLDNRHkgKGgwDGtwAAZrICRksAHBRHkgJGWABGswBCoNFHEmgnNBEcNEeSAkY4AEKg0EcST8asAABCoNJHkgKGLwAyoNM3kgJGnAVGpwAsQgwOJ5MCBnEFRisAIqAAhYkBIqAARYkBxZkBhZkBIqCEMqAIGiILzMWLAVbc/QwOzQ5GmwAAzBOGZgVGlQAmgwLGkwAGZwUBaf/AAAD6zJwixo8AAAAgLEEBZv/AAABWEiPy3/DwLMDML4ZwBQAgMPRWE/7hLP+GAwAgIPUBXv/AAABW0iDg/8DwLMD3PuqGAwAgLEEBV//AAABWUh/y3/DwLMBWr/5GYQUmg4DGAQAAAGazAkbd/wwOwqDAhngAAABmswJGSwUGcgAAwqABJrMCBnAAIi0EMRj/4qAAwqDCJ7MCxm4AOF0oLYV3AUZDBQDCoAEmswKGZgAyLQQhD//ioADCoMI3sgJGZQAoPQwcIOOCOF0oLcV0ATH4/gwESWMy0yvpIyDEgwZaAAAh9P4MDkICAMKgxueUAsZYAMhSKC0yw/AwIsBCoMAgxJMizRhNAmKg78YBAFIEABtEUGYwIFTANyXxMg0FUg0EIg0GgDMRACIRUEMgQDIgIg0HDA6AIgEwIiAgJsAyoMEgw5OGQwAAACHa/gwOMgIAwqDG55MCxj4AODLCoMjnEwIGPADiQgDIUgY6AByCDA4MHCcTAgY3AAYQBWZDAoYWBUYwADAgNAwOwqDA5xIChjAAMPRBi+3NAnzzxgwAKD4yYTEBAv/AAABILigeYi4AICQQMiExJgQOwCAAUiYAQEMwUEQQQCIgwCAAKQYbzOLOEPc8yMaB/2ZDAkaA/wai/2azAgYABcYWAAAAYcH+DA5IBgwVMsPwLQ5AJYMwXoNQIhDCoMbnkktxuv7tAogHwqDJNzg+MFAUwqDAos0YjNUGDABaKigCS1UpBEtEDBJQmMA3Ne0WYtpJBpkHxmf/ZoMChuwEDBwMDsYBAAAA4qAAwqD/wCB0BWAB4CB0xV8BRXABVkzAIg0BDPM3EjEnMxVmQgIGtgRmYgLGugQmMgLG+f4GGQAAHCM3kgIGsAQyoNI3EkUcEzcSAkbz/sYYACGV/ug90i0CAcD+wAAAIZP+wCAAOAIhkv4gIxDgIoLQPSAFjAE9Ai0MAbn+wAAAIqPoAbb+wAAAxuP+WF1ITTg9Ii0CxWsBBuD+ADINAyINAoAzESAzIDLD8CLNGEVKAcbZ/gAiDQMyDQKAIhEwIiAxZ/4iwvAiYSkoMwwUIMSDwMB0jExSISn2VQvSzRjSYSQMH8Z3BAAioMkpU8bK/iFx/nGQ/rIiAGEs/oKgAyInApIhKYJhJ7DGwCc5BAwaomEnsmE2BTkBsiE2cWf+UiEkYiEpcEvAykRqVQuEUmElgmErhwQCxk4Ed7sCRk0EkUj+PFOo6VIpEGIpFShpomEoUmEmYmEqyHniKRT4+SezAsbuAzFV/jAioCgCoAIAMTz+DA4MEumT6YMp0ymj4mEm/Q7iYSjNDoYGAHIhJwwTcGEEfMRgQ5NtBDliXQtyISSG4AMAAIIhJJIhJSEs/pe42DIIABt4OYKGBgCiIScMIzBqEHzFDBRgRYNtBDliXQuG1ANyISRSISUhIf5Xt9tSBwD4glmSgC8RHPNaIkJhMVJhNLJhNhvXRXgBDBNCITFSITSyITZWEgEioCAgVRBWhQDwIDQiwvggNYPw9EGL/wwSYSf+AB9AAFKhVzYPAA9AQPCRDAbwYoMwZiCcJgwfhgAA0iEkIQb+LEM5Yl0LhpwAXQu2PCAGDwByISd8w3BhBAwSYCODbQIMMwYWAAAAXQvSISRGAAD9BoIhJYe92RvdCy0iAgAAHEAAIqGLzCDuILY85G0PcfH94CAkKbcgIUEpx+DjQcLM/VYiIMAgJCc8KEYRAJIhJ3zDkGEEDBJgI4NtAgxTIeX9OWJ9DQaVAwAAAF0L0iEkRgAA/QaiISWnvdEb3QstIgIAABxAACKhi8wg7iDAICQnPOHAICQAAkDg4JEir/ggzBDyoAAWnAaGDAAAAHIhJ3zDcGEEDBJgI4NtAgxjBuf/0iEkXQuCISWHveAb3QstIgIAABxAACKhIO4gi8y2jOQhxf3CzPj6MiHc/Soj4kIA4OhBhgwAAACSIScME5BhBHzEYDSDbQMMc8bU/9IhJF0LoiElIbj9p73dQc/9Mg0A+iJKIjJCABvdG//2TwKG3P8hsP189iLSKfISHCISHSBmMGBg9GefBwYeANIhJF0LLHMGQAC2jCFGDwAAciEnfMNwYQQMEmAjg20CPDMGu/8AAF0L0iEkRgAA/QaCISWHvdkb3QstIgIAABxAACKhi8wg7iC2jORtD+CQdJJhKODoQcLM+P0GRgIAPEOG0wLSISRdCyFj/Se176IhKAtvokUAG1UWhgdWrPiGHAAMk8bKAl0L0iEkRgAA/QYhWf0ntepGBgByISd8w3BhBAwSYCODbQIsY8aY/9IhJLBbIIIhJYe935FO/dBowFApwGeyAiBiIGe/AW0PTQbQPSBQJSBSYTRiYTWyYTYBs/3AAABiITVSITSyITZq3WpVYG/AVmb5Rs8C/QYmMgjGBAAA0iEkXQsMoyFn/TlifQ1GFgMAAAwPJhICRiAAIqEgImcRLAQhev1CZxIyoAVSYTRiYTVyYTOyYTYBnf3AAAByITOyITZiITVSITQ9ByKgkEKgCEJDWAsiGzNWUv8ioHAMkzJH6AsiG3dWUv8clHKhWJFN/Qx4RgIAAHoimiKCQgAtAxsyR5PxIWL9MWL9DIQGAQBCQgAbIjeS90ZgASFf/foiIgIAJzwdRg8AAACiISd8w6BhBAwSYCODbQIMswZT/9IhJF0LIVT9+iJiISVnvdsb3Qs9MgMAABxAADOhMO4gMgIAi8w3POEhTP1BTP36IjICAAwSABNAACKhQE+gCyLgIhAwzMAAA0Dg4JFIBDEl/SokMD+gImMRG//2PwKG3v8hP/1CoSAMA1JhNLJhNgFf/cAAAH0NDA9SITSyITZGFQAAAIIhJ3zDgGEEDBJgI4NtAgzjBrMCciEkXQuSISWXt+AbdwsnIgIAABxAACKhIO4gi8y2POQhK/1BCv36IiICAOAwJCpEISj9wsz9KiQyQgDg40Eb/yED/TIiEzc/0xwzMmIT3QdtDwYcAUwEDAMiwURSYTRiYTWyYTZyYTMBO/3AAAByITOB9fwioWCAh4JBFv0qKPoiMqAAIsIYgmEyATL9wAAAgiEyIRH9QqSAKij6IgwDIsIYASz9wAAAqM+CITLwKqAiIhGK/6JhLSJhLk0PUiE0YiE1ciEzsiE2BgQAACIPWBv/ECKgMiIRGzMyYhEyIS5AL8A3MuYMAikRKQGtAgwT4EMRksFESvmYD0pBKinwIhEbMykUmqpms+Ux3vw6IowS9iorIc78QqbQQEeCgshYKogioLwqJIJhLAwJfPNCYTkiYTDGQwAAXQvSISRGAAD9BiwzxpgAAKIhLIIKAIJhNxaIDhAooHgCG/f5Av0IDALwIhEiYThCIThwIAQiYS8L/0AiIHBxQVZf/gynhzc7cHgRkHcgAHcRcHAxQiEwcmEvDBpxrvwAGEAAqqEqhHCIkPD6EXKj/4YCAABCIS+qIkJYAPqIJ7fyBiAAciE5IICUioeioLBBofyqiECIkHKYDMxnMlgMfQMyw/4gKUGhm/zypLDGCgAggASAh8BCITl894CHMIqE8IiAoIiQcpgMzHcyWAwwcyAyw/6CITcLiIJhN0IhNwy4ICFBh5TIICAEIHfAfPoiITlwejB6ciKksCp3IYb8IHeQklcMQiEsG5kbREJhLHIhLpcXAsa9/4IhLSYoAsaYAEaBAAzix7ICxi8AkiEl0CnApiICBiUAIZv84DCUQXX8KiNAIpAiEgwAMhEwIDGW8gAwKTEWEgUnPAJGIwAGEgAADKPHs0KRkPx8+AADQOBgkWBgBCAoMCommiJAIpAikgwbc9ZCBitjPQdnvN0GBgCiISd8w6BhBAwSYCODbQIcA8Z1/tIhJF0LYiElZ73gIg0AGz0AHEAAIqEg7iCLzAzi3QPHMgJG2/+GBwAiDQGLPAATQAAyoSINACvdABxAACKhICMgIO4gwswQIW784DCUYUj8KiNgIpAyEgwAMxEwIDGWogAwOTEgIIRGCQAAAIFl/AykfPcbNAAEQOBAkUBABCAnMCokiiJgIpAikgxNA5Yi/gADQODgkTDMwCJhKAzzJyMVITP8ciEo+jIhV/wb/yojckIABjQAAIIhKGa4Gtx/HAmSYSgGAQDSISRdCxwTISj8fPY5YgZB/jFM/CojIsLwIgIAImEmJzwdBg4AoiEnfMOgYQQMEmAjg20CHCPGNf4AANIhJF0LYiElZ73eG90LLSICAHIhJgAcQAAioYvMIO4gdzzhgiEmMTn8kiEoDBYAGEAAZqGaMwtmMsPw4CYQYgMAAAhA4OCRKmYhMvyAzMAqLwwDZrkMMQX8+kMxLvw6NDIDAE0GUmE0YmE1smE2AUH8wAAAYiE1UiE0av+yITaGAAAADA9x+vtCJxFiJxJqZGe/AoZ5//eWB4YCANIhJF0LHFNGyf8A8Rr8IRv8PQ9SYTRiYTWyYTZyYTMBLfzAAAByITMhBPwyJxFCJxI6PwEo/MAAALIhNmIhNVIhNDHj+yjDCyIpw/Hh+3jP1me4hj4BYiElDOLQNsCmQw9Br/tQNMCmIwJGTQDGMQIAx7ICRi4ApiMCBiUAQdX74CCUQCKQIhK8ADIRMCAxlgIBMCkxFkIFJzwChiQAxhIAAAAMo8ezRHz4kqSwAANA4GCRYGAEICgwKiaaIkAikCKSDBtz1oIGK2M9B2e83YYGAHIhJ3zDcGEEDBJgI4NtAhxzxtT9AADSISRdC4IhJYe93iINABs9ABxAACKhIO4gi8wM4t0DxzICxtv/BggAAAAiDQGLPAATQAAyoSINACvdABxAACKhICMgIO4gwswQQaj74CCUQCKQIhK8ACIRIPAxlo8AICkx8PCExggADKN892KksBsjAANA4DCRMDAE8Pcw+vNq/0D/kPKfDD0Cli/+AAJA4OCRIMzAIqD/96ICxkAAhgIAAByDBtMA0iEkXQshYvsnte/yRQBtDxtVRusADOLHMhkyDQEiDQCAMxEgIyAAHEAAIqEg7iAr3cLMEDGD++AglKoiMCKQIhIMACIRIDAxICkx1hMCDKQbJAAEQOBAkUBABDA5MDo0QXj7ijNAM5AykwxNApbz/f0DAAJA4OCRIMzAd4N8YqAOxzYaQg0BIg0AgEQRICQgABxAACKhIO4g0s0CwswQQWn74CCUqiJAIpBCEgwARBFAIDFASTHWEgIMphtGAAZA4GCRYGAEICkwKiZhXvuKImAikCKSDG0ElvL9MkUAAARA4OCRQMzAdwIIG1X9AkYCAAAAIkUBK1UGc//wYIRm9gKGswAirv8qZiF6++BmEWoiKAIiYSYhePtyISZqYvgGFpcFdzwdBg4AAACCISd8w4BhBAwSYCODbQIckwZb/dIhJF0LkiEll73gG90LLSICAKIhJgAcQAAioYvMIO4gpzzhYiEmDBIAFkAAIqELIuAiEGDMwAAGQODgkSr/DOLHsgJGMAByISXQJ8CmIgKGJQBBLPvgIJRAIpAi0g8iEgwAMhEwIDGW8gAwKTEWMgUnPAJGJACGEgAADKPHs0SRT/t8+AADQOBgkWBgBCAoMCommiJAIpAikgwbc9aCBitjPQdnvN2GBgCCISd8w4BhBAwSYCODbQIco8Yr/QAA0iEkXQuSISWXvd4iDQAbPQAcQAAioSDuIIvMDOLdA8cyAkbb/wYIAAAAIg0BizwAE0AAMqEiDQAr3QAcQAAioSAjICDuIMLMEGH/+uAglGAikCLSDzISDAAzETAgMZaCADA5MSAghMYIAIEk+wykfPcbNAAEQOBAkUBABCAnMCokiiJgIpAikgxNA5Yi/gADQODgkTDMwDEa++AiESozOAMyYSYxGPuiISYqIygCImEoFgoGpzweRg4AciEnfMNwYQQMEmAjg20CHLPG9/wAAADSISRdC4IhJYe93RvdCy0iAgCSISYAHEAAIqGLzCDuIJc84aIhJgwSABpAACKhYiEoCyLgIhAqZgAKQODgkaDMwGJhKHHi+oIhKHB1wJIhKzHf+oAnwJAiEDoicmEqPQUntQE9AkGW+vozbQ83tG0GEgAhwPosUzliBm4APFMhvfp9DTliDCZGbABdC9IhJEYAAP0GIYv6J7XhoiEqYiEociErYCrAMcn6cCIQKiMiAgAbqiJFAKJhKhtVC29WH/0GDAAAMgIAYsb9MkUAMgIBMkUBMgICOyIyRQI7VfY24xYGATICADJFAGYmBSICASJFAWpV/QaioLB8+YKksHKhAAa9/iGc+iiyB+IChpb8wCAkJzwgRg8AgiEnfMOAYQQMEmAjg20CLAMGrPwAAF0L0iEkRgAA/QaSISWXvdkb3QstIgIAABxAACKhi8wg7iDAICQnPOHAICQAAkDg4JF8giDMEH0NRgEAAAt3wsz4oiEkd7oC9ozxIbD6MbD6TQxSYTRyYTOyYTZFlAALIrIhNnIhM1IhNCDuEAwPFkwGhgwAAACCISd8w4BhBAwSYCODbQIskwYPAHIhJF0LkiEll7fgG3cLJyICAAAcQAAioSDuIIvMtozk4DB0wsz44OhBhgoAoiEnfMOgYQQMEmAjg20CLKMhX/o5YoYPAAAAciEkXQtiISVnt9kyBwAbd0FZ+hv/KKSAIhEwIiAppPZPB8bd/3IhJF0LIVL6LCM5YgwGhgEAciEkXQt89iYWFEsmzGJGAwALd8LM+IIhJHe4AvaM8YFI+iF4+jF4+sl4TQxSYTRiYTVyYTOCYTKyYTbFhQCCITKSISiiISYLIpnokiEq4OIQomgQciEzoiEkUiE0siE2YiE1+fjiaBSSaBWg18CwxcD9BpZWDjFl+vjYLQwFfgDw4PRNAvDw9X0MDHhiITWyITZGJQAAAJICAKICAurpkgIB6pma7vr+4gIDmpqa/5qe4gIEmv+anuICBZr/mp7iAgaa/5qe4gIHmv+a7ur/iyI6kkc5wEAjQbAisLCQYEYCAAAyAgAbIjru6v8qOb0CRzPvMUf6LQ5CYTFiYTVyYTOCYTKyYTZFdQAxQfrtAi0PxXQAQiExciEzsiE2QHfAgiEyQTr6YiE1/QKMhy0LsDjAxub/AAAA/xEhAfrq7+nS/QbcVvii8O7AfO/g94NGAgAAAAAMDN0M8q/9MS36UiEpKCNiISTQIsDQVcDaZtEJ+ikjOA1xCPpSYSnKU1kNcDXADAIMFfAlg2JhJCAgdFaCAELTgEAlgxaSAMH++S0MBSkAyQ2CISmcKJHl+Sg5FrIA8C8x8CLA1iIAxoP7MqDHId/5li8BjB9GS/oh3PkyIgPME4ZI+jKgyDlShkb6KC2MEsZE+iHo+QEU+sAAAAEW+sAAAEZA+sg9zByGPvoio+gBDvrAAADADADGOvriYSIMfEaN+gEO+sAAAAwcDAMGCAAAyC34PfAsICAgtMwSxpT6Rif7Mi0DIi0CxTIAMqAADBwgw4PGIvt4fWhtWF1ITTg9KC0MDAH0+cAAAO0CDBLgwpOGHvsAAAHu+cAAAAwMBhj7ACHC+UhdOC1JAiHA+TkCBvr/Qb75DAI4BMKgyDDCgykEQbr5PQwMHCkEMMKDBgz7xzICxvT9xvv9AiFDkqEQwiFC0iFB4iFA8iE/mhEN8AAACAAAYBwAAGAAAABgEAAAYCH8/xLB8OkBwCAA6AIJMckh2REh+P/AIADIAsDAdJzs0Zb5RgQAAAAx9P/AIAAoAzgNICB0wAMAC8xmDOqG9P8h7/8IMcAgAOkCyCHYEegBEsEQDfAAAAD4AgBgEAIAYAACAGAAAAAIIfz/wCAAOAIwMCRWQ/8h+f9B+v/AIAA5AjH3/8AgAEkDwCAASANWdP/AIAAoAgwTICAEMCIwDfAAAIAAAAAAQP///wAEAgBgEsHwySHBbPkJMShM2REWgghF+v8WIggoTAzzDA0nowwoLDAiEAwTINOD0NB0EBEgRfj/FmL/Id7/Me7/wCAAOQLAIAAyIgBWY/8x1//AIAAoAyAgJFZC/ygsMeX/QEIRIWH50DKDIeT/ICQQQeT/wCAAKQQhz//AIAA5AsAgADgCVnP/DBIcA9Ajk90CKEzQIsApTCgs2tLZLAgxyCHYERLBEA3wAAAATEoAQBLB4MlhwUH5+TH4POlBCXHZUe0C97MB/QMWHwTYHNrf0NxBBgEAAACF8v8oTKYSBCgsJ63yRe3/FpL/KBxNDz0OAe7/wAAAICB0jDIioMQpXCgcSDz6IvBEwCkcSTwIcchh2FHoQfgxEsEgDfAAAAD/DwAAUSb5EsHwCTEMFEJFADBMQUklQfr/ORUpNTAwtEoiKiMgLEEpRQwCImUFAVf5wAAACDEyoMUgI5MSwRAN8AAAADA7AEASwfAJMTKgwDeSESKg2wH7/8AAACKg3EYEAAAAADKg2zeSCAH2/8AAACKg3QH0/8AAAAgxEsEQDfAAAAASwfDJIdkRCTHNAjrSRgIAACIMAMLMAcX6/9ec8wIhA8IhAtgREsEQDfAAAFgQAABwEAAAGJgAQBxLAEA0mABAAJkAQJH7/xLB4Mlh6UH5MQlx2VGQEcDtAiLREM0DAfX/wAAA8fb4hgoA3QzHvwHdD00NPQEtDgHw/8AAACAgdPxCTQ09ASLREAHs/8AAANDugNDMwFYc/SHl/zLREBAigAHn/8AAACHh/xwDGiIF9f8tDAYBAAAAIqBjkd3/mhEIcchh2FHoQfgxEsEgDfAAEsHwIqDACTEBuv/AAAAIMRLBEA3wAAAAbBAAAGgQAAB0EAAAeBAAAHwQAACAEAAAkBAAAJgPAECMOwBAEsHgkfz/+TH9AiHG/8lh2VEJcelBkBHAGiI5AjHy/ywCGjNJA0Hw/9LREBpEwqAAUmQAwm0aAfD/wAAAYer/Ibz4GmZoBmeyAsZJAC0NAbb/wAAAIbP/MeX/KkEaM0kDRj4AAABhr/8x3/8aZmgGGjPoA8AmwOeyAiDiIGHd/z0BGmZZBk0O8C8gAaj/wAAAMdj/ICB0GjNYA4yyDARCbRbtBMYSAAAAAEHR/+r/GkRZBAXx/z0OLQGF4/9F8P9NDj0B0C0gAZr/wAAAYcn/6swaZlgGIZP/GiIoAie8vDHC/1AswBozOAM3sgJG3f9G6v9CoABCTWwhuf8QIoABv//AAABWAv9huf8iDWwQZoA4BkUHAPfiEfZODkGx/xpE6jQiQwAb7sbx/zKv/jeSwSZOKSF7/9A9IBAigAF+/8AAAAXo/yF2/xwDGiJF2v9F5/8sAgGm+MAAAIYFAGFx/1ItGhpmaAZntchXPAIG2f/G7/8AkaD/mhEIcchh2FHoQfgxEsEgDfBdAkKgwCgDR5UOzDIMEoYGAAwCKQN84g3wJhIFJiIRxgsAQqDbLQVHlSkMIikDBggAIqDcJ5UIDBIpAy0EDfAAQqDdfPJHlQsMEikDIqDbDfAAfPIN8AAAtiMwbQJQ9kBA80BHtSlQRMAAFEAAM6EMAjc2BDBmwBsi8CIRMDFBC0RWxP43NgEbIg3wAIyTDfA3NgwMEg3wAAAAAABESVYwDAIN8LYjKFDyQEDzQEe1F1BEwAAUQAAzoTcyAjAiwDAxQULE/1YE/zcyAjAiwA3wzFMAAABESVYwDAIN8AAAAAAUQObECSAzgQAioQ3wAAAAMqEMAg3wAA==", - "text_start": 1074843648, - "data": "CIH+PwUFBAACAwcAAwMLANTXEEAL2BBAOdgQQNbYEECF5xBAOtkQQJDZEEDc2RBAhecQQKLaEEAf2xBA4NsQQIXnEECF5xBAeNwQQIXnEEBV3xBAHOAQQFfgEECF5xBAhecQQPPgEECF5xBA2+EQQIHiEEDA4xBAf+QQQFDlEECF5xBAhecQQIXnEECF5xBAfuYQQIXnEEB05xBAsN0QQKnYEEDC5RBAydoQQBvaEECF5xBACOcQQE/nEECF5xBAhecQQIXnEECF5xBAhecQQIXnEECF5xBAhecQQELaEEB/2hBA2uUQQAEAAAACAAAAAwAAAAQAAAAFAAAABwAAAAkAAAANAAAAEQAAABkAAAAhAAAAMQAAAEEAAABhAAAAgQAAAMEAAAABAQAAgQEAAAECAAABAwAAAQQAAAEGAAABCAAAAQwAAAEQAAABGAAAASAAAAEwAAABQAAAAWAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAABAAAAAgAAAAIAAAADAAAAAwAAAAQAAAAEAAAABQAAAAUAAAAGAAAABgAAAAcAAAAHAAAACAAAAAgAAAAJAAAACQAAAAoAAAAKAAAACwAAAAsAAAAMAAAADAAAAA0AAAANAAAAAAAAAAAAAAADAAAABAAAAAUAAAAGAAAABwAAAAgAAAAJAAAACgAAAAsAAAANAAAADwAAABEAAAATAAAAFwAAABsAAAAfAAAAIwAAACsAAAAzAAAAOwAAAEMAAABTAAAAYwAAAHMAAACDAAAAowAAAMMAAADjAAAAAgEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAQAAAAEAAAABAAAAAgAAAAIAAAACAAAAAgAAAAMAAAADAAAAAwAAAAMAAAAEAAAABAAAAAQAAAAEAAAABQAAAAUAAAAFAAAABQAAAAAAAAAAAAAAAAAAABAREgAIBwkGCgULBAwDDQIOAQ8AAQEAAAEAAAAEAAAA", - "data_start": 1073720488, - "bss_start": 1073643776 -} \ No newline at end of file diff --git a/tools/esptool_py/esptool/uf2_writer.py b/tools/esptool_py/esptool/uf2_writer.py index 81772d4249..e972fa2231 100644 --- a/tools/esptool_py/esptool/uf2_writer.py +++ b/tools/esptool_py/esptool/uf2_writer.py @@ -1,11 +1,9 @@ -# SPDX-FileCopyrightText: 2020-2023 Espressif Systems (Shanghai) CO LTD +# SPDX-FileCopyrightText: 2020-2025 Espressif Systems (Shanghai) CO LTD # SPDX-License-Identifier: GPL-2.0-or-later # Code was originally licensed under Apache 2.0 before the release of ESP-IDF v5.2 import hashlib -import os import struct -from typing import List from esptool.util import div_roundup @@ -25,8 +23,8 @@ class UF2Writer(object): def __init__( self, chip_id: int, - output_file: os.PathLike, - chunk_size: int, + output_file: str, + chunk_size: int | None, md5_enabled: bool = True, ) -> None: if not md5_enabled: @@ -44,7 +42,7 @@ def __init__( def __enter__(self) -> "UF2Writer": return self - def __exit__(self, exc_type: str, exc_val: int, exc_tb: List) -> None: + def __exit__(self, exc_type: str, exc_val: int, exc_tb: list) -> None: if self.f: self.f.close() diff --git a/tools/esptool_py/esptool/util.py b/tools/esptool_py/esptool/util.py index 1527618854..73e448c82a 100644 --- a/tools/esptool_py/esptool/util.py +++ b/tools/esptool_py/esptool/util.py @@ -1,12 +1,16 @@ -# 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 - +from __future__ import annotations import os import re import struct -import sys + +from typing import IO, TypeAlias + +# Define a custom type for the input +ImageSource: TypeAlias = str | bytes | IO[bytes] def byte(bitstr, index): @@ -31,7 +35,7 @@ def div_roundup(a, b): def flash_size_bytes(size): - """Given a flash size of the type passed in args.flash_size + """Given a flash size of the type passed in size (ie 512KB or 1MB) then return the size in bytes. """ if size is None: @@ -41,7 +45,7 @@ def flash_size_bytes(size): elif "KB" in size: return int(size[: size.index("KB")]) * 1024 else: - raise FatalError("Unknown size %s" % size) + raise FatalError(f"Unknown size {size}") def hexify(s, uppercase=True): @@ -49,7 +53,7 @@ def hexify(s, uppercase=True): return "".join(format_str % c for c in s) -def pad_to(data, alignment, pad_character=b"\xFF"): +def pad_to(data, alignment, pad_character=b"\xff"): """Pad to the next alignment boundary""" pad_mod = len(data) % alignment if pad_mod != 0: @@ -57,23 +61,6 @@ def pad_to(data, alignment, pad_character=b"\xFF"): return data -def print_overwrite(message, last_line=False): - """Print a message, overwriting the currently printed line. - - If last_line is False, don't append a newline at the end - (expecting another subsequent call will overwrite this one.) - - After a sequence of calls with last_line=False, call once with last_line=True. - - If output is not a TTY (for example redirected a pipe), - no overwriting happens and this function is the same as print(). - """ - if hasattr(sys.stdout, "isatty") and sys.stdout.isatty(): - print("\r%s" % message, end="\n" if last_line else "") - else: - print(message) - - def expand_chip_name(chip_name): """Change chip name to official form, e.g. `esp32s3beta2` -> `ESP32-S3(beta2)`""" # Put "-" after "esp32" @@ -99,17 +86,73 @@ def get_file_size(path_to_file): return file_size +def sanitize_string(byte_string): + return byte_string.decode("utf-8").replace("\0", "") + + +def get_bytes(input: ImageSource) -> tuple[bytes, str | None]: + """ + Normalize the input (file path, bytes, or an opened file-like object) into bytes + and provide a name of the source. + + Args: + input: The input file path, bytes, or an opened file-like object. + + Returns: + A tuple containing the normalized bytes and the source of the input. + """ + if isinstance(input, str): + with open(input, "rb") as f: + data = f.read() + source = input + elif isinstance(input, bytes): + data = input + source = None + elif hasattr(input, "read") and hasattr(input, "write") and hasattr(input, "close"): + pos = input.tell() + data = input.read() + input.seek(pos) # Reset the file pointer + source = input.name + else: + raise FatalError(f"Invalid input type {type(input)}") + return data, source + + +def get_key_from_value(dict, val): + """ + Get key from value in dictionary, assumes unique values in dictionary + """ + for key, value in dict.items(): + if value == val: + return key + return None + + +def check_deprecated_py_suffix(module_name: str) -> None: + """Check if called with deprecated .py suffix""" + import sys + from esptool import log + + script_name = sys.argv[0] if sys.argv else "" + if script_name.endswith(module_name + ".py"): + log.warning( + f"DEPRECATED: '{module_name}.py' is deprecated. Please use '{module_name}' " + "instead. The '.py' suffix will be removed in a future major release." + ) + + class PrintOnce: """ Class for printing messages just once. Can be useful when running in a loop """ - def __init__(self) -> None: + def __init__(self, print_callback) -> None: self.already_printed = False + self.print_callback = print_callback def __call__(self, text) -> None: if not self.already_printed: - print(text) + self.print_callback(text) self.already_printed = True diff --git a/tools/esptool_py/pyproject.toml b/tools/esptool_py/pyproject.toml index 96eb71a059..2079bb95a0 100644 --- a/tools/esptool_py/pyproject.toml +++ b/tools/esptool_py/pyproject.toml @@ -11,7 +11,7 @@ ] readme = {file = "README.md", content-type = "text/markdown"} license = {text = "GPLv2+"} - description = "A serial utility to communicate & flash code to Espressif chips." + description = "A serial utility for flashing, provisioning, and interacting with Espressif SoCs." classifiers = [ "Development Status :: 5 - Production/Stable", "Intended Audience :: Developers", @@ -22,25 +22,23 @@ "Topic :: Software Development :: Embedded Systems", "Environment :: Console", "License :: OSI Approved :: GNU General Public License v2 or later (GPLv2+)", - "Programming Language :: Python :: 3.7", - "Programming Language :: Python :: 3.8", - "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: 3.13", ] - requires-python = ">=3.7" + requires-python = ">=3.10" dynamic = ["version", "scripts"] dependencies = [ "bitstring>=3.1.6,!=4.2.0", - "cryptography>=2.1.4", - "ecdsa>=0.16.0", + "cryptography>=43.0.0", "pyserial>=3.3", "reedsolo>=1.5.3,<1.8", "PyYAML>=5.1", "intelhex", - 'argcomplete>=3; sys_platform != "win32"', + "rich_click", + "click<8.2.0", ] [project.urls] @@ -58,9 +56,13 @@ "pytest", "pytest-rerunfailures", "requests", - "commitizen", + "czespressif", ] hsm = ["python-pkcs11"] + docs = [ + "esp-docs~=1.10", + "sphinx-tabs", + ] [tool.setuptools] include-package-data = true @@ -75,7 +77,8 @@ version = {attr = "esptool.__init__.__version__"} [tool.commitizen] - version = "4.8.1" + name = "czespressif" + version = "5.0.0" update_changelog_on_bump = true tag_format = "v$version" changelog_start_rev = "v4.2.1" @@ -85,19 +88,7 @@ version_files = [ "esptool/__init__.py:__version__" ] - change_type_order = [ - "BREAKING CHANGE", - "New Features", - "Bug Fixes", - "Code Refactoring", - "Performance Improvements" - ] -[tool.commitizen.change_type_map] - feat = "New Features" - fix = "Bug Fixes" - refactor = "Code Refactoring" - perf = "Performance Improvements" [tool.codespell] skip = '*.bin,*test/images/efuse/*,*docs/en/espefuse/inc/*' @@ -108,7 +99,7 @@ disallow_incomplete_defs = false # Disallows defining functions with incomplete type annotations disallow_untyped_defs = false # Disallows defining functions without type annotations or with incomplete type annotations ignore_missing_imports = true # Suppress error messages about imports that cannot be resolved - python_version = "3.7" # Specifies the Python version used to parse and check the target program + python_version = "3.10" # Specifies the Python version used to parse and check the target program warn_no_return = true # Shows errors for missing return statements on some execution paths warn_return_any = true # Shows a warning when returning a value with type Any from a function declared with a non- Any return type @@ -122,18 +113,14 @@ ] line-length = 88 - - select = ['E', 'F', 'W'] - ignore = ["E203"] - - target-version = "py37" + target-version = "py310" [tool.ruff.lint] # Enable Pyflakes (`F`) and a subset of the pycodestyle (`E`) codes by default. # Unlike Flake8, Ruff doesn't enable pycodestyle warnings (`W`) or # McCabe complexity (`C901`) by default. - select = ["E4", "E7", "E9", "F"] - ignore = [] + select = ['E', 'F', 'W'] + ignore = ["E203"] # Allow fix for all enabled rules (when `--fix`) is provided. fixable = ["ALL"] diff --git a/tools/esptool_py/setup.py b/tools/esptool_py/setup.py index 0ba8099218..6dfef4f9dd 100644 --- a/tools/esptool_py/setup.py +++ b/tools/esptool_py/setup.py @@ -2,12 +2,25 @@ from setuptools import setup if os.name != "nt": + # For backward compatibility with py suffix scripts = ["esptool.py", "espefuse.py", "espsecure.py", "esp_rfc2217_server.py"] - entry_points = {} + entry_points = { + "console_scripts": [ + "esptool=esptool.__init__:_main", + "espsecure=espsecure.__init__:_main", + "espefuse=espefuse.__init__:_main", + "esp_rfc2217_server=esp_rfc2217_server.__init__:main", + ], + } else: scripts = [] entry_points = { "console_scripts": [ + "esptool=esptool.__init__:_main", + "espsecure=espsecure.__init__:_main", + "espefuse=espefuse.__init__:_main", + "esp_rfc2217_server=esp_rfc2217_server.__init__:main", + # For backward compatibility with py suffix "esptool.py=esptool.__init__:_main", "espsecure.py=espsecure.__init__:_main", "espefuse.py=espefuse.__init__:_main", diff --git a/tools/esptool_py/test/README.md b/tools/esptool_py/test/README.md index aa1431880a..5aeb8e2701 100644 --- a/tools/esptool_py/test/README.md +++ b/tools/esptool_py/test/README.md @@ -1,3 +1,3 @@ -# esptool.py test suite +# esptool test suite -See the [Automated Integration Tests section in `esptool.py` documentation](https://docs.espressif.com/projects/esptool/en/latest/esp32/contributing.html#automated-integration-tests) to learn about the test suite and how to run it. +See the [Automated Integration Tests section in `esptool` documentation](https://docs.espressif.com/projects/esptool/en/latest/esp32/contributing.html#automated-integration-tests) to learn about the test suite and how to run it. diff --git a/tools/esptool_py/test/conftest.py b/tools/esptool_py/test/conftest.py index b87fe56248..9e64d199c8 100644 --- a/tools/esptool_py/test/conftest.py +++ b/tools/esptool_py/test/conftest.py @@ -1,4 +1,5 @@ import pytest +import os def pytest_addoption(parser): @@ -59,3 +60,11 @@ def need_to_install_package_err(): "Instructions: https://docs.espressif.com/projects/esptool/en/latest/" "contributing.html#development-setup" ) + + +@pytest.fixture(scope="session", autouse=True) +def set_terminal_properties(): + """Make sure terminal width is set to 120 columns and color is disabled for + consistent test output.""" + os.environ["COLUMNS"] = "120" + os.environ["NO_COLOR"] = "1" diff --git a/tools/esptool_py/test/efuse_scripts/efuse_burn1.py b/tools/esptool_py/test/efuse_scripts/efuse_burn1.py deleted file mode 100644 index ea337ed8bc..0000000000 --- a/tools/esptool_py/test/efuse_scripts/efuse_burn1.py +++ /dev/null @@ -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" diff --git a/tools/esptool_py/test/efuse_scripts/efuse_burn2.py b/tools/esptool_py/test/efuse_scripts/efuse_burn2.py deleted file mode 100644 index 559f2cdbea..0000000000 --- a/tools/esptool_py/test/efuse_scripts/efuse_burn2.py +++ /dev/null @@ -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" diff --git a/tools/esptool_py/test/efuse_scripts/esp32/config1.json b/tools/esptool_py/test/efuse_scripts/esp32/config1.json deleted file mode 100644 index 45f59311c7..0000000000 --- a/tools/esptool_py/test/efuse_scripts/esp32/config1.json +++ /dev/null @@ -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" - ] -} \ No newline at end of file diff --git a/tools/esptool_py/test/efuse_scripts/esp32/config2.json b/tools/esptool_py/test/efuse_scripts/esp32/config2.json deleted file mode 100644 index d509222f35..0000000000 --- a/tools/esptool_py/test/efuse_scripts/esp32/config2.json +++ /dev/null @@ -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}" - ] -} \ No newline at end of file diff --git a/tools/esptool_py/test/efuse_scripts/esp32/execute_efuse_script.py b/tools/esptool_py/test/efuse_scripts/esp32/execute_efuse_script.py deleted file mode 100644 index becedd026f..0000000000 --- a/tools/esptool_py/test/efuse_scripts/esp32/execute_efuse_script.py +++ /dev/null @@ -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") diff --git a/tools/esptool_py/test/efuse_scripts/esp32/execute_efuse_script2.py b/tools/esptool_py/test/efuse_scripts/esp32/execute_efuse_script2.py deleted file mode 100644 index e906f61d14..0000000000 --- a/tools/esptool_py/test/efuse_scripts/esp32/execute_efuse_script2.py +++ /dev/null @@ -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") diff --git a/tools/esptool_py/test/efuse_scripts/esp32xx/config1.json b/tools/esptool_py/test/efuse_scripts/esp32xx/config1.json deleted file mode 100644 index b2f1201733..0000000000 --- a/tools/esptool_py/test/efuse_scripts/esp32xx/config1.json +++ /dev/null @@ -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" - ] -} \ No newline at end of file diff --git a/tools/esptool_py/test/efuse_scripts/esp32xx/config2.json b/tools/esptool_py/test/efuse_scripts/esp32xx/config2.json deleted file mode 100644 index d4d5c02346..0000000000 --- a/tools/esptool_py/test/efuse_scripts/esp32xx/config2.json +++ /dev/null @@ -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}" - ] -} \ No newline at end of file diff --git a/tools/esptool_py/test/efuse_scripts/esp32xx/execute_efuse_script.py b/tools/esptool_py/test/efuse_scripts/esp32xx/execute_efuse_script.py deleted file mode 100644 index 9ccb0e408c..0000000000 --- a/tools/esptool_py/test/efuse_scripts/esp32xx/execute_efuse_script.py +++ /dev/null @@ -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") diff --git a/tools/esptool_py/test/efuse_scripts/esp32xx/execute_efuse_script2.py b/tools/esptool_py/test/efuse_scripts/esp32xx/execute_efuse_script2.py deleted file mode 100644 index af01379eb5..0000000000 --- a/tools/esptool_py/test/efuse_scripts/esp32xx/execute_efuse_script2.py +++ /dev/null @@ -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") diff --git a/tools/esptool_py/test/elf2image/esp32-app-cust-ver-info.elf b/tools/esptool_py/test/elf2image/esp32-app-cust-ver-info.elf deleted file mode 100755 index 389dbbe998..0000000000 Binary files a/tools/esptool_py/test/elf2image/esp32-app-cust-ver-info.elf and /dev/null differ diff --git a/tools/esptool_py/test/elf2image/esp32c6-appdesc.elf b/tools/esptool_py/test/elf2image/esp32c6-appdesc.elf new file mode 100755 index 0000000000..40fb1ae98e Binary files /dev/null and b/tools/esptool_py/test/elf2image/esp32c6-appdesc.elf differ diff --git a/tools/esptool_py/test/elf2image/esp32c6-appdesc/Makefile b/tools/esptool_py/test/elf2image/esp32c6-appdesc/Makefile new file mode 100644 index 0000000000..18615d55d8 --- /dev/null +++ b/tools/esptool_py/test/elf2image/esp32c6-appdesc/Makefile @@ -0,0 +1,18 @@ +CC = riscv32-esp-elf-gcc +CFLAGS = -Os -ffreestanding -nostdlib +LDFLAGS = -T esp32c6-appdesc.ld + +TARGET = esp32c6-appdesc.elf +SRC = main.c +OBJ = main.o + +all: $(TARGET) + +$(TARGET): $(OBJ) + $(CC) $(CFLAGS) $^ -o $@ $(LDFLAGS) + +%.o: %.c + $(CC) $(CFLAGS) -c $< -o $@ + +clean: + rm -f $(OBJ) $(TARGET) \ No newline at end of file diff --git a/tools/esptool_py/test/elf2image/esp32c6-appdesc/esp32c6-appdesc.ld b/tools/esptool_py/test/elf2image/esp32c6-appdesc/esp32c6-appdesc.ld new file mode 100644 index 0000000000..0dfaafa16f --- /dev/null +++ b/tools/esptool_py/test/elf2image/esp32c6-appdesc/esp32c6-appdesc.ld @@ -0,0 +1,16 @@ +MEMORY +{ + /** + * 0x42000000 is start of flash + 0x20 for image + extended header and segment header + * 0x14c is length of esp_app_desc_t structure + */ + drom_seg (R) : org = 0x42000020, len = 0x14c +} + +SECTIONS +{ + .flash.appdesc : + { + KEEP(*(.flash.appdesc)) + } > drom_seg +} \ No newline at end of file diff --git a/tools/esptool_py/test/elf2image/esp32c6-appdesc/main.c b/tools/esptool_py/test/elf2image/esp32c6-appdesc/main.c new file mode 100644 index 0000000000..59c2638cfa --- /dev/null +++ b/tools/esptool_py/test/elf2image/esp32c6-appdesc/main.c @@ -0,0 +1,45 @@ +#include + +// This is the structure of the application description section in the binary image (taken from ESP-IDF). +typedef struct { + uint32_t magic_word; + uint32_t secure_version; + uint32_t reserv1[2]; + char version[32]; + char project_name[32]; + char time[16]; + char date[16]; + char idf_ver[32]; + uint8_t app_elf_sha256[32]; + uint16_t min_efuse_blk_rev_full; + uint16_t max_efuse_blk_rev_full; + uint8_t mmu_page_size; + uint8_t reserv3[3]; + uint32_t reserv2[18]; +} esp_app_desc_t; + +__attribute__((section(".flash.appdesc"))) +esp_app_desc_t my_app_desc = { + .magic_word = 0xABCD5432, + .secure_version = 0xffffffff, + .reserv1 = {0xffffffff, 0xffffffff}, + .version = "_______________________________", + .project_name = "-------------------------------", + .time = "xxxxxxxxxxxxxxx", + .date = "yyyyyyyyyyyyyyy", + .idf_ver = "zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz", + .app_elf_sha256 = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }, + .min_efuse_blk_rev_full = 0xffff, + .max_efuse_blk_rev_full = 0xffff, + .mmu_page_size = 0, + .reserv3 = {0xff, 0xff, 0xff}, + .reserv2 = { + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff + }, +}; \ No newline at end of file diff --git a/tools/esptool_py/test/elf2image/esp8266-nonossdkv20-at-v2.elf-0x00000.bin b/tools/esptool_py/test/elf2image/esp8266-nonossdkv20-at-v2.elf-0x00000.bin new file mode 100644 index 0000000000..ef49875b40 Binary files /dev/null and b/tools/esptool_py/test/elf2image/esp8266-nonossdkv20-at-v2.elf-0x00000.bin differ diff --git a/tools/esptool_py/test/elf2image/esp8266-nonossdkv20-at-v2.elf-0x01010.bin b/tools/esptool_py/test/elf2image/esp8266-nonossdkv20-at-v2.elf-0x01010.bin new file mode 100644 index 0000000000..7af86bad8e Binary files /dev/null and b/tools/esptool_py/test/elf2image/esp8266-nonossdkv20-at-v2.elf-0x01010.bin differ diff --git a/tools/esptool_py/test/images/efuse/256bit_3 b/tools/esptool_py/test/images/efuse/256bit_3 new file mode 100644 index 0000000000..a44d599808 --- /dev/null +++ b/tools/esptool_py/test/images/efuse/256bit_3 @@ -0,0 +1 @@ +w \ No newline at end of file diff --git a/tools/esptool_py/test/images/ram_helloworld/source/Makefile b/tools/esptool_py/test/images/ram_helloworld/source/Makefile index 0132b9ebee..1378440f2c 100644 --- a/tools/esptool_py/test/images/ram_helloworld/source/Makefile +++ b/tools/esptool_py/test/images/ram_helloworld/source/Makefile @@ -21,7 +21,6 @@ BUILD_DIR = build APP_ELF_8266 = $(BUILD_DIR)/$(APP)-esp8266.elf APP_ELF_32 = $(BUILD_DIR)/$(APP)-esp32.elf APP_ELF_32S2 = $(BUILD_DIR)/$(APP)-esp32s2.elf -APP_ELF_32S3_BETA_2 = $(BUILD_DIR)/$(APP)-esp32s3beta2.elf APP_ELF_32S3 = $(BUILD_DIR)/$(APP)-esp32s3.elf APP_ELF_32C3 = $(BUILD_DIR)/$(APP)-esp32c3.elf APP_ELF_32C2 = $(BUILD_DIR)/$(APP)-esp32c2.elf @@ -61,10 +60,6 @@ $(APP_ELF_32S2): $(SRCS) $(BUILD_DIR) ld/app_32s2.ld @echo " CC(32S2) $^ -> $@" $(Q) $(CROSS_32S2)gcc $(CFLAGS) -DESP32S2=1 -Tapp_32s2.ld -Wl,-Map=$(@:.elf=.map) -o $@ $(filter %.c, $^) $(LDLIBS) -$(APP_ELF_32S3_BETA_2): $(SRCS) $(BUILD_DIR) ld/app_32s3_beta_2.ld - @echo " CC(32S3) $^ -> $@" - $(Q) $(CROSS_32S3)gcc $(CFLAGS) -DESP32S3=1 -Tapp_32s3.ld -Wl,-Map=$(@:.elf=.map) -o $@ $(filter %.c, $^) $(LDLIBS) - $(APP_ELF_32S3): $(SRCS) $(BUILD_DIR) ld/app_32s3.ld @echo " CC(32S3) $^ -> $@" $(Q) $(CROSS_32S3)gcc $(CFLAGS) -DESP32S3=1 -Tapp_32s3.ld -Wl,-Map=$(@:.elf=.map) -o $@ $(filter %.c, $^) $(LDLIBS) diff --git a/tools/esptool_py/test/secure_images/bootloader_signed_v2.bin b/tools/esptool_py/test/secure_images/bootloader_signed_v2_rsa.bin similarity index 100% rename from tools/esptool_py/test/secure_images/bootloader_signed_v2.bin rename to tools/esptool_py/test/secure_images/bootloader_signed_v2_rsa.bin diff --git a/tools/esptool_py/test/secure_images/ecdsa_secure_boot_signing_key.pem b/tools/esptool_py/test/secure_images/ecdsa256_secure_boot_signing_key.pem similarity index 100% rename from tools/esptool_py/test/secure_images/ecdsa_secure_boot_signing_key.pem rename to tools/esptool_py/test/secure_images/ecdsa256_secure_boot_signing_key.pem diff --git a/tools/esptool_py/test/secure_images/ecdsa_secure_boot_signing_key2.pem b/tools/esptool_py/test/secure_images/ecdsa256_secure_boot_signing_key2.pem similarity index 100% rename from tools/esptool_py/test/secure_images/ecdsa_secure_boot_signing_key2.pem rename to tools/esptool_py/test/secure_images/ecdsa256_secure_boot_signing_key2.pem diff --git a/tools/esptool_py/test/secure_images/ecdsa_secure_boot_signing_key_pkcs8.pem b/tools/esptool_py/test/secure_images/ecdsa256_secure_boot_signing_key_pkcs8.pem similarity index 100% rename from tools/esptool_py/test/secure_images/ecdsa_secure_boot_signing_key_pkcs8.pem rename to tools/esptool_py/test/secure_images/ecdsa256_secure_boot_signing_key_pkcs8.pem diff --git a/tools/esptool_py/test/secure_images/ecdsa_secure_boot_signing_pubkey.pem b/tools/esptool_py/test/secure_images/ecdsa256_secure_boot_signing_pubkey.pem similarity index 100% rename from tools/esptool_py/test/secure_images/ecdsa_secure_boot_signing_pubkey.pem rename to tools/esptool_py/test/secure_images/ecdsa256_secure_boot_signing_pubkey.pem diff --git a/tools/esptool_py/test/secure_images/ecdsa_secure_boot_signing_pubkey2.pem b/tools/esptool_py/test/secure_images/ecdsa256_secure_boot_signing_pubkey2.pem similarity index 100% rename from tools/esptool_py/test/secure_images/ecdsa_secure_boot_signing_pubkey2.pem rename to tools/esptool_py/test/secure_images/ecdsa256_secure_boot_signing_pubkey2.pem diff --git a/tools/esptool_py/test/secure_images/ecdsa256_secure_boot_signing_pubkey_raw.bin b/tools/esptool_py/test/secure_images/ecdsa256_secure_boot_signing_pubkey_raw.bin new file mode 100644 index 0000000000..f390d03356 --- /dev/null +++ b/tools/esptool_py/test/secure_images/ecdsa256_secure_boot_signing_pubkey_raw.bin @@ -0,0 +1 @@ +4?>sE!bZyP4]ۧ7pmV8M' \ No newline at end of file diff --git a/tools/esptool_py/test/test_espefuse.py b/tools/esptool_py/test/test_espefuse.py index 2c7b626c4c..5b5178b4e1 100755 --- a/tools/esptool_py/test/test_espefuse.py +++ b/tools/esptool_py/test/test_espefuse.py @@ -1,8 +1,8 @@ -# HOST_TEST for espefuse.py using the pytest framework +# HOST_TEST for espefuse using the pytest framework # -# Supports esp32, esp32s2, esp32s3beta2, esp32s3, -# esp32c3, esp32h2beta1, esp32c2, esp32c6, esp32p4, -# esp32c61, esp32c5, esp32c5beta3. +# Supports esp32, esp32s2, esp32s3, esp32c3, +# esp32c2, esp32c6, esp32p4, esp32c61, +# esp32c5, # # How to use: # @@ -17,7 +17,7 @@ # - `pytest test_espefuse.py \ # --chip esp32 --port /dev/ttyUSB0 --reset-port /dev/ttyUSB1` # -# where - --port - a port for espefuse.py operation +# where - --port - a port for espefuse operation # - --reset-port - a port to clear efuses (connect RTS or DTR ->- J14 pin 39) # # Note: For FPGA with ESP32 image, you need to set an env variable ESPTOOL_ENV_FPGA to 1 @@ -39,7 +39,6 @@ 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 @@ -60,8 +59,13 @@ if arg_chip not in SUPPORTED_CHIPS: pytest.exit(f"{arg_chip} is not a supported target, choose from {SUPPORTED_CHIPS}") -print(f"\nHost tests of espefuse.py for {arg_chip}:") -print("Running espefuse.py tests...") +print(f"\nHost tests of espefuse for {arg_chip}:") +print("Running espefuse 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 @@ -76,8 +80,7 @@ def setup_method(self): self._set_target_wafer_version() else: self.base_cmd = ( - f"{sys.executable} -m espefuse --chip {arg_chip} " - f"--port {arg_port} -d" + f"{sys.executable} -m espefuse --chip {arg_chip} --port {arg_port} -d" ) self.reset_efuses() @@ -98,30 +101,25 @@ def reset_efuses(self): reset_port.rts = False def get_esptool(self): - if reset_port is not None: - import esptool - - esp = esptool.cmds.detect_chip(port=arg_port) - del esptool - else: - import espefuse + import espefuse - efuse = espefuse.SUPPORTED_CHIPS[arg_chip].efuse_lib - esp = efuse.EmulateEfuseController(self.efuse_file.name) - del espefuse - del efuse + esp = espefuse.get_esp( + port=arg_port, + virt=reset_port is None, + virt_efuse_file=self.efuse_file.name, + ) return esp def _set_34_coding_scheme(self): - self.espefuse_py("burn_efuse CODING_SCHEME 1") + self.espefuse_py("burn-efuse CODING_SCHEME 1") def _set_none_recovery_coding_scheme(self): - self.espefuse_py("burn_efuse CODING_SCHEME 3") + self.espefuse_py("burn-efuse CODING_SCHEME 3") def _set_target_wafer_version(self): # ESP32 has to be ECO3 (v3.0) for tests if arg_chip == "esp32": - self.espefuse_py("burn_efuse CHIP_VER_REV1 1 CHIP_VER_REV2 1") + self.espefuse_py("burn-efuse CHIP_VER_REV1 1 CHIP_VER_REV2 1") def check_data_block_in_log( self, log, file_path, repeat=1, reverse_order=False, offset=0 @@ -141,21 +139,38 @@ def espefuse_py(self, cmd, do_not_confirm=True, check_msg=None, ret_code=0): full_cmd = " ".join( [self.base_cmd, "--do-not-confirm" if do_not_confirm else "", cmd] ) + print("Running command: ", full_cmd) output = self._run_command(full_cmd, check_msg, ret_code) self._run_command( - " ".join([self.base_cmd, "check_error"]), "No errors detected", 0 + " ".join([self.base_cmd, "check-error"]), "No errors detected", 0 ) print(output) return output def _run_command(self, cmd, check_msg, ret_code): try: + env = os.environ.copy() + # Comprehensive color disabling and terminal size control + env.update( + # NO_COLOR and COLUMNS should be already set from conftest.py; + # We set them here for completeness and to avoid any mismatch. + # The rest is required only for espefuse tests in some edge cases + # (e.g. GH actions, etc.) + { + "NO_COLOR": "1", + "COLUMNS": "120", # Set terminal width for help output + "LINES": "24", # Some terminal may not apply COLUMNS without LINES + "TERM": "dumb", # Force terminal to dumb mode + } + ) p = subprocess.Popen( cmd.split(), shell=False, stdin=subprocess.PIPE, stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, universal_newlines=True, + env=env, ) output, _ = p.communicate() returncode = p.returncode @@ -173,27 +188,34 @@ def _run_command(self, cmd, check_msg, ret_code): class TestReadCommands(EfuseTestCase): def test_help(self): - self.espefuse_not_virt_py("--help", check_msg="usage: __main__.py [-h]") + self.espefuse_not_virt_py( + "--help", + check_msg=f"Usage: {ESPEFUSE_MODNAME} [OPTIONS] " + "COMMAND1 [ARGS]... [COMMAND2 [ARGS]...]...", + ) self.espefuse_not_virt_py(f"--chip {arg_chip} --help") - def test_help2(self): - self.espefuse_not_virt_py("", check_msg="usage: __main__.py [-h]", ret_code=1) - def test_dump(self): self.espefuse_py("dump -h") self.espefuse_py("dump") def test_dump_format_joint(self): tmp_file = tempfile.NamedTemporaryFile(delete=False) - self.espefuse_py(f"dump --format joint --file_name {tmp_file.name}") + self.espefuse_py(f"dump --format joint --file-name {tmp_file.name}") + + def test_dump_format_joint_stdout(self): + self.espefuse_py("dump --format joint") def test_dump_split_default(self): tmp_file = tempfile.NamedTemporaryFile(delete=False) - self.espefuse_py(f"dump --file_name {tmp_file.name}") + self.espefuse_py(f"dump --file-name {tmp_file.name}") + + def test_dump_format_split_stdout(self): + self.espefuse_py("dump --format split") def test_dump_split(self): tmp_file = tempfile.NamedTemporaryFile(delete=False) - self.espefuse_py(f"dump --format split --file_name {tmp_file.name}") + self.espefuse_py(f"dump --format split --file-name {tmp_file.name}") def test_summary(self): self.espefuse_py("summary -h") @@ -207,47 +229,44 @@ def test_summary_filter(self): self.espefuse_py("summary --format value_only MAC") self.espefuse_py( "summary --format value_only MAC WR_DIS", - check_msg="The 'value_only' format can be used exactly for one efuse.", + check_msg="The 'value_only' format can be used exactly for one eFuse.", ret_code=2, ) - @pytest.mark.skipif( - arg_chip == "esp32p4", reason="No Custom MAC Address defined yet" - ) def test_get_custom_mac(self): - self.espefuse_py("get_custom_mac -h") + self.espefuse_py("get-custom-mac -h") if arg_chip == "esp32": right_msg = "Custom MAC Address is not set in the device." else: right_msg = "Custom MAC Address: 00:00:00:00:00:00 (OK)" - self.espefuse_py("get_custom_mac", check_msg=right_msg) + self.espefuse_py("get-custom-mac", check_msg=right_msg) def test_adc_info(self): - self.espefuse_py("adc_info -h") - self.espefuse_py("adc_info") + self.espefuse_py("adc-info -h") + self.espefuse_py("adc-info") def test_adc_info_2(self): if arg_chip == "esp32": - self.espefuse_py("burn_efuse BLK3_PART_RESERVE 1") - elif arg_chip in ["esp32c3", "esp32s3", "esp32s3beta2"]: - self.espefuse_py("burn_efuse BLK_VERSION_MAJOR 1") + self.espefuse_py("burn-efuse BLK3_PART_RESERVE 1") + elif arg_chip in ["esp32c3", "esp32s3"]: + self.espefuse_py("burn-efuse BLK_VERSION_MAJOR 1") elif arg_chip in ["esp32c2", "esp32s2", "esp32c6"]: - self.espefuse_py("burn_efuse BLK_VERSION_MINOR 1") - elif arg_chip in ["esp32h2", "esp32h2beta1"]: - self.espefuse_py("burn_efuse BLK_VERSION_MINOR 2") - self.espefuse_py("adc_info") + self.espefuse_py("burn-efuse BLK_VERSION_MINOR 1") + elif arg_chip in ["esp32h2", "esp32p4"]: + self.espefuse_py("burn-efuse BLK_VERSION_MINOR 2") + self.espefuse_py("adc-info") def test_check_error(self): - self.espefuse_py("check_error -h") - self.espefuse_py("check_error") - self.espefuse_py("check_error --recovery") + self.espefuse_py("check-error -h") + self.espefuse_py("check-error") + self.espefuse_py("check-error --recovery") class TestReadProtectionCommands(EfuseTestCase): def test_read_protect_efuse(self): - self.espefuse_py("read_protect_efuse -h") + self.espefuse_py("read-protect-efuse -h") if arg_chip == "esp32": - cmd = "read_protect_efuse \ + cmd = "read-protect-efuse \ CODING_SCHEME \ MAC_VERSION \ BLOCK1 \ @@ -255,12 +274,12 @@ def test_read_protect_efuse(self): BLOCK3" count_protects = 5 elif arg_chip == "esp32c2": - cmd = "read_protect_efuse \ + cmd = "read-protect-efuse \ BLOCK_KEY0_LOW_128" count_protects = 1 else: self.espefuse_py( - "burn_efuse \ + "burn-efuse \ KEY_PURPOSE_0 HMAC_UP \ KEY_PURPOSE_1 XTS_AES_128_KEY \ KEY_PURPOSE_2 XTS_AES_128_KEY \ @@ -268,7 +287,7 @@ def test_read_protect_efuse(self): KEY_PURPOSE_4 HMAC_DOWN_JTAG \ KEY_PURPOSE_5 HMAC_DOWN_DIGITAL_SIGNATURE" ) - cmd = "read_protect_efuse \ + cmd = "read-protect-efuse \ BLOCK_KEY0 \ BLOCK_KEY1 \ BLOCK_KEY2 \ @@ -280,8 +299,11 @@ def test_read_protect_efuse(self): output = self.espefuse_py(cmd) assert count_protects == output.count("is already read protected") + @pytest.mark.skipif( + arg_chip == "esp32p4", reason="BLOCK_SYS_DATA2 is used by ADC calib" + ) def test_read_protect_efuse2(self): - self.espefuse_py("write_protect_efuse RD_DIS") + self.espefuse_py("write-protect-efuse RD_DIS") if arg_chip == "esp32": efuse_name = "CODING_SCHEME" elif arg_chip == "esp32c2": @@ -289,18 +311,18 @@ def test_read_protect_efuse2(self): else: efuse_name = "BLOCK_SYS_DATA2" self.espefuse_py( - f"read_protect_efuse {efuse_name}", - check_msg="A fatal error occurred: This efuse cannot be read-disabled " - "due the to RD_DIS field is already write-disabled", + f"read-protect-efuse {efuse_name}", + check_msg="A fatal error occurred: This eFuse cannot be read-disabled " + "due to the RD_DIS field being already write-disabled", ret_code=2, ) @pytest.mark.skipif(arg_chip != "esp32", reason="when the purpose of BLOCK2 is set") def test_read_protect_efuse3(self): - self.espefuse_py("burn_efuse ABS_DONE_1 1") - self.espefuse_py(f"burn_key BLOCK2 {IMAGES_DIR}/256bit") + self.espefuse_py("burn-efuse ABS_DONE_1 1") + self.espefuse_py(f"burn-key BLOCK2 {IMAGES_DIR}/256bit") self.espefuse_py( - "read_protect_efuse BLOCK2", + "read-protect-efuse BLOCK2", check_msg="Secure Boot V2 is on (ABS_DONE_1 = True), " "BLOCK2 must be readable, stop this operation!", ret_code=2, @@ -308,15 +330,15 @@ def test_read_protect_efuse3(self): def test_read_protect_efuse4(self): if arg_chip == "esp32": - self.espefuse_py(f"burn_key BLOCK2 {IMAGES_DIR}/256bit") + self.espefuse_py(f"burn-key BLOCK2 {IMAGES_DIR}/256bit") msg = "must be readable, please stop this operation!" - self.espefuse_py("read_protect_efuse BLOCK2", check_msg=msg) + self.espefuse_py("read-protect-efuse BLOCK2", check_msg=msg) elif arg_chip == "esp32c2": self.espefuse_py( - f"burn_key BLOCK_KEY0 {IMAGES_DIR}/128bit_key SECURE_BOOT_DIGEST" + f"burn-key BLOCK_KEY0 {IMAGES_DIR}/128bit_key SECURE_BOOT_DIGEST" ) self.espefuse_py( - "read_protect_efuse BLOCK_KEY0", + "read-protect-efuse BLOCK_KEY0", check_msg="A fatal error occurred: " "BLOCK_KEY0 must be readable, stop this operation!", ret_code=2, @@ -324,11 +346,11 @@ def test_read_protect_efuse4(self): else: key1_purpose = ( "USER" - if arg_chip in ["esp32p4", "esp32c61", "esp32c5", "esp32c5beta3"] + if arg_chip in ["esp32p4", "esp32c61", "esp32c5", "esp32h4"] else "RESERVED" ) self.espefuse_py( - f"burn_key BLOCK_KEY0 {IMAGES_DIR}/256bit USER \ + f"burn-key BLOCK_KEY0 {IMAGES_DIR}/256bit USER \ BLOCK_KEY1 {IMAGES_DIR}/256bit {key1_purpose} \ BLOCK_KEY2 {IMAGES_DIR}/256bit SECURE_BOOT_DIGEST0 \ BLOCK_KEY3 {IMAGES_DIR}/256bit SECURE_BOOT_DIGEST1 \ @@ -336,45 +358,45 @@ def test_read_protect_efuse4(self): BLOCK_KEY5 {IMAGES_DIR}/256bit HMAC_UP" ) self.espefuse_py( - "read_protect_efuse BLOCK_KEY0", + "read-protect-efuse BLOCK_KEY0", check_msg="A fatal error occurred: " "BLOCK_KEY0 must be readable, stop this operation!", ret_code=2, ) self.espefuse_py( - "read_protect_efuse BLOCK_KEY1", + "read-protect-efuse BLOCK_KEY1", check_msg="A fatal error occurred: " "BLOCK_KEY1 must be readable, stop this operation!", ret_code=2, ) self.espefuse_py( - "read_protect_efuse BLOCK_KEY2", + "read-protect-efuse BLOCK_KEY2", check_msg="A fatal error occurred: " "BLOCK_KEY2 must be readable, stop this operation!", ret_code=2, ) self.espefuse_py( - "read_protect_efuse BLOCK_KEY3", + "read-protect-efuse BLOCK_KEY3", check_msg="A fatal error occurred: " "BLOCK_KEY3 must be readable, stop this operation!", ret_code=2, ) self.espefuse_py( - "read_protect_efuse BLOCK_KEY4", + "read-protect-efuse BLOCK_KEY4", check_msg="A fatal error occurred: " "BLOCK_KEY4 must be readable, stop this operation!", ret_code=2, ) - self.espefuse_py("read_protect_efuse BLOCK_KEY5") + self.espefuse_py("read-protect-efuse BLOCK_KEY5") @pytest.mark.skipif( arg_chip != "esp32", - reason="system parameters efuse read-protection is supported only by esp32, " + reason="system parameters eFuse read-protection is supported only by esp32, " "other chips protect whole blocks", ) def test_burn_and_read_protect_efuse(self): self.espefuse_py( - "burn_efuse FLASH_CRYPT_CONFIG 15 RD_DIS 8", + "burn-efuse FLASH_CRYPT_CONFIG 15 RD_DIS 8", check_msg="Efuse FLASH_CRYPT_CONFIG is read-protected. " "Read back the burn value is not possible.", ) @@ -382,7 +404,7 @@ def test_burn_and_read_protect_efuse(self): class TestWriteProtectionCommands(EfuseTestCase): def test_write_protect_efuse(self): - self.espefuse_py("write_protect_efuse -h") + self.espefuse_py("write-protect-efuse -h") if arg_chip == "esp32": efuse_lists = """WR_DIS RD_DIS CODING_SCHEME XPD_SDIO_FORCE XPD_SDIO_REG XPD_SDIO_TIEH SPI_PAD_CONFIG_CLK @@ -398,7 +420,7 @@ def test_write_protect_efuse(self): SPI_BOOT_CRYPT_CNT""" efuse_lists2 = "RD_DIS KEY_PURPOSE_0 KEY_PURPOSE_2" else: - efuse_lists = """RD_DIS DIS_ICACHE DIS_FORCE_DOWNLOAD + efuse_lists = """RD_DIS DIS_FORCE_DOWNLOAD DIS_DOWNLOAD_MANUAL_ENCRYPT USB_EXCHG_PINS WDT_DELAY_SEL SPI_BOOT_CRYPT_CNT SECURE_BOOT_KEY_REVOKE0 SECURE_BOOT_KEY_REVOKE1 @@ -412,38 +434,37 @@ def test_write_protect_efuse(self): BLOCK_KEY2 BLOCK_KEY3 BLOCK_KEY4 BLOCK_KEY5""" if arg_chip not in [ "esp32h2", - "esp32h2beta1", "esp32c6", "esp32c61", "esp32c5", - "esp32c5beta3", + "esp32h21", + "esp32h4", ]: efuse_lists += """ DIS_DOWNLOAD_ICACHE SPI_PAD_CONFIG_CLK SPI_PAD_CONFIG_Q SPI_PAD_CONFIG_D SPI_PAD_CONFIG_CS SPI_PAD_CONFIG_HD SPI_PAD_CONFIG_WP SPI_PAD_CONFIG_DQS SPI_PAD_CONFIG_D4 SPI_PAD_CONFIG_D5 SPI_PAD_CONFIG_D6 SPI_PAD_CONFIG_D7""" - efuse_lists2 = "RD_DIS DIS_ICACHE" - self.espefuse_py(f"write_protect_efuse {efuse_lists}") - output = self.espefuse_py(f"write_protect_efuse {efuse_lists2}") - assert output.count("is already write protected") == 2 + efuse_lists2 = "RD_DIS" + self.espefuse_py(f"write-protect-efuse {efuse_lists}") + output = self.espefuse_py(f"write-protect-efuse {efuse_lists2}") + assert output.count("is already write protected") >= 1 def test_write_protect_efuse2(self): if arg_chip == "esp32": - self.espefuse_py("write_protect_efuse WR_DIS") + self.espefuse_py("write-protect-efuse WR_DIS") self.espefuse_py( - "write_protect_efuse CODING_SCHEME", - check_msg="A fatal error occurred: This efuse cannot be write-disabled " - "due to the WR_DIS field is already write-disabled", + "write-protect-efuse CODING_SCHEME", + check_msg="A fatal error occurred: This eFuse cannot be write-disabled " + "due to the WR_DIS field being already write-disabled", ret_code=2, ) -@pytest.mark.skipif(arg_chip == "esp32p4", reason="No Custom MAC Address defined yet") class TestBurnCustomMacCommands(EfuseTestCase): def test_burn_custom_mac(self): - self.espefuse_py("burn_custom_mac -h") - cmd = "burn_custom_mac AA:CD:EF:11:22:33" + self.espefuse_py("burn-custom-mac -h") + cmd = "burn-custom-mac AA:CD:EF:11:22:33" mac = "aa:cd:ef:11:22:33" if arg_chip == "esp32": self.espefuse_py( @@ -454,7 +475,7 @@ def test_burn_custom_mac(self): def test_burn_custom_mac2(self): self.espefuse_py( - "burn_custom_mac AA:CD:EF:11:22:33:44", + "burn-custom-mac AA:CD:EF:11:22:33:44", check_msg="A fatal error occurred: MAC Address needs to be a 6-byte " "hexadecimal format separated by colons (:)!", ret_code=2, @@ -462,7 +483,7 @@ def test_burn_custom_mac2(self): def test_burn_custom_mac3(self): self.espefuse_py( - "burn_custom_mac AB:CD:EF:11:22:33", + "burn-custom-mac AB:CD:EF:11:22:33", check_msg="A fatal error occurred: Custom MAC must be a unicast MAC!", ret_code=2, ) @@ -472,18 +493,18 @@ def test_burn_custom_mac3(self): ) def test_burn_custom_mac_with_34_coding_scheme(self): self._set_34_coding_scheme() - self.espefuse_py("burn_custom_mac -h") + self.espefuse_py("burn-custom-mac -h") self.espefuse_py( - "burn_custom_mac AA:CD:EF:01:02:03", + "burn-custom-mac AA:CD:EF:01:02:03", check_msg="Custom MAC Address version 1: aa:cd:ef:01:02:03 (CRC 0x56 OK)", ) self.espefuse_py( - "get_custom_mac", + "get-custom-mac", check_msg="Custom MAC Address version 1: aa:cd:ef:01:02:03 (CRC 0x56 OK)", ) self.espefuse_py( - "burn_custom_mac FE:22:33:44:55:66", + "burn-custom-mac FE:22:33:44:55:66", check_msg="New value contains some bits that cannot be cleared " "(value will be 0x675745ffeffe)", ret_code=2, @@ -501,65 +522,65 @@ def test_burn_custom_mac_with_34_coding_scheme(self): ) class TestSetFlashVoltageCommands(EfuseTestCase): def test_set_flash_voltage_1_8v(self): - self.espefuse_py("set_flash_voltage -h") + self.espefuse_py("set-flash-voltage -h") vdd = "VDD_SDIO" if arg_chip == "esp32" else "VDD_SPI" self.espefuse_py( - "set_flash_voltage 1.8V", + "set-flash-voltage 1.8V", check_msg=f"Set internal flash voltage regulator ({vdd}) to 1.8V.", ) if arg_chip == "esp32": error_msg = "A fatal error occurred: " - "Can't set flash regulator to OFF as XPD_SDIO_REG efuse is already burned" + "Can't set flash regulator to OFF as XPD_SDIO_REG eFuse is already burned" else: error_msg = "A fatal error occurred: " - "Can't set flash regulator to OFF as VDD_SPI_XPD efuse is already burned" + "Can't set flash regulator to OFF as VDD_SPI_XPD eFuse is already burned" self.espefuse_py( - "set_flash_voltage 3.3V", + "set-flash-voltage 3.3V", check_msg=f"Enable internal flash voltage regulator ({vdd}) to 3.3V.", ) - self.espefuse_py("set_flash_voltage OFF", check_msg=error_msg, ret_code=2) + self.espefuse_py("set-flash-voltage OFF", check_msg=error_msg, ret_code=2) def test_set_flash_voltage_3_3v(self): vdd = "VDD_SDIO" if arg_chip == "esp32" else "VDD_SPI" self.espefuse_py( - "set_flash_voltage 3.3V", + "set-flash-voltage 3.3V", check_msg=f"Enable internal flash voltage regulator ({vdd}) to 3.3V.", ) if arg_chip == "esp32": error_msg = "A fatal error occurred: " - "Can't set regulator to 1.8V is XPD_SDIO_TIEH efuse is already burned" + "Can't set regulator to 1.8V is XPD_SDIO_TIEH eFuse is already burned" else: error_msg = "A fatal error occurred: " - "Can't set regulator to 1.8V is VDD_SPI_TIEH efuse is already burned" - self.espefuse_py("set_flash_voltage 1.8V", check_msg=error_msg, ret_code=2) + "Can't set regulator to 1.8V is VDD_SPI_TIEH eFuse is already burned" + self.espefuse_py("set-flash-voltage 1.8V", check_msg=error_msg, ret_code=2) if arg_chip == "esp32": error_msg = "A fatal error occurred: " - "Can't set flash regulator to OFF as XPD_SDIO_REG efuse is already burned" + "Can't set flash regulator to OFF as XPD_SDIO_REG eFuse is already burned" else: error_msg = "A fatal error occurred: " - "Can't set flash regulator to OFF as VDD_SPI_XPD efuse is already burned" - self.espefuse_py("set_flash_voltage OFF", check_msg=error_msg, ret_code=2) + "Can't set flash regulator to OFF as VDD_SPI_XPD eFuse is already burned" + self.espefuse_py("set-flash-voltage OFF", check_msg=error_msg, ret_code=2) def test_set_flash_voltage_off(self): vdd = "VDD_SDIO" if arg_chip == "esp32" else "VDD_SPI" self.espefuse_py( - "set_flash_voltage OFF", + "set-flash-voltage OFF", check_msg=f"Disable internal flash voltage regulator ({vdd})", ) self.espefuse_py( - "set_flash_voltage 3.3V", + "set-flash-voltage 3.3V", check_msg=f"Enable internal flash voltage regulator ({vdd}) to 3.3V.", ) def test_set_flash_voltage_off2(self): vdd = "VDD_SDIO" if arg_chip == "esp32" else "VDD_SPI" self.espefuse_py( - "set_flash_voltage OFF", + "set-flash-voltage OFF", check_msg=f"Disable internal flash voltage regulator ({vdd})", ) self.espefuse_py( - "set_flash_voltage 1.8V", + "set-flash-voltage 1.8V", check_msg=f"Set internal flash voltage regulator ({vdd}) to 1.8V.", ) @@ -567,54 +588,54 @@ def test_set_flash_voltage_off2(self): @pytest.mark.skipif(arg_chip != "esp32c3", reason="Not necessary for all chips") class TestValueArgForBurnEfuseCommands(EfuseTestCase): def test_efuse_is_bool_given_none(self): - self.espefuse_py("burn_efuse SECURE_BOOT_KEY_REVOKE0") + self.espefuse_py("burn-efuse SECURE_BOOT_KEY_REVOKE0") def test_efuse_is_bool_given_0(self): self.espefuse_py( - "burn_efuse SECURE_BOOT_KEY_REVOKE0 0", + "burn-efuse SECURE_BOOT_KEY_REVOKE0 0", check_msg="A fatal error occurred: " - "New value is not accepted for efuse 'SECURE_BOOT_KEY_REVOKE0' " + "New value is not accepted for eFuse 'SECURE_BOOT_KEY_REVOKE0' " "(will always burn 0->1), given value=0", ret_code=2, ) def test_efuse_is_bool_given_2(self): self.espefuse_py( - "burn_efuse SECURE_BOOT_KEY_REVOKE0 2", + "burn-efuse SECURE_BOOT_KEY_REVOKE0 2", check_msg="A fatal error occurred: " - "New value is not accepted for efuse 'SECURE_BOOT_KEY_REVOKE0' " + "New value is not accepted for eFuse 'SECURE_BOOT_KEY_REVOKE0' " "(will always burn 0->1), given value=2", ret_code=2, ) def test_efuse_is_bytes_ok(self): self.espefuse_py( - "burn_efuse OPTIONAL_UNIQUE_ID 0x12345678123456781234567812345678" + "burn-efuse OPTIONAL_UNIQUE_ID 0x12345678123456781234567812345678" ) def test_efuse_is_bytes_given_short_val(self): self.espefuse_py( - "burn_efuse OPTIONAL_UNIQUE_ID 0x1234567812345678", + "burn-efuse OPTIONAL_UNIQUE_ID 0x1234567812345678", check_msg="A fatal error occurred: " - "The length of efuse 'OPTIONAL_UNIQUE_ID' (128 bits) " + "The length of eFuse 'OPTIONAL_UNIQUE_ID' (128 bits) " "(given len of the new value= 64 bits)", ret_code=2, ) def test_efuse_is_bytes_given_none(self): self.espefuse_py( - "burn_efuse OPTIONAL_UNIQUE_ID", + "burn-efuse OPTIONAL_UNIQUE_ID", check_msg="A fatal error occurred: " - "New value required for efuse 'OPTIONAL_UNIQUE_ID' (given None)", + "New value required for eFuse 'OPTIONAL_UNIQUE_ID' (given None)", ret_code=2, ) def test_efuse_is_int_ok(self): - self.espefuse_py("burn_efuse SPI_PAD_CONFIG_D 7") + self.espefuse_py("burn-efuse SPI_PAD_CONFIG_D 7") def test_efuse_is_int_given_out_of_range_val(self): self.espefuse_py( - "burn_efuse SPI_PAD_CONFIG_D 200", + "burn-efuse SPI_PAD_CONFIG_D 200", check_msg="A fatal error occurred: " "200 is too large an unsigned integer for a bitstring " "of length 6. The allowed range is [0, 63].", @@ -623,15 +644,15 @@ def test_efuse_is_int_given_out_of_range_val(self): def test_efuse_is_int_given_none(self): self.espefuse_py( - "burn_efuse SPI_PAD_CONFIG_D", + "burn-efuse SPI_PAD_CONFIG_D", check_msg="A fatal error occurred: " - "New value required for efuse 'SPI_PAD_CONFIG_D' (given None)", + "New value required for eFuse 'SPI_PAD_CONFIG_D' (given None)", ret_code=2, ) def test_efuse_is_int_given_0(self): self.espefuse_py( - "burn_efuse SPI_PAD_CONFIG_D 0", + "burn-efuse SPI_PAD_CONFIG_D 0", check_msg="A fatal error occurred: " "New value should not be 0 for 'SPI_PAD_CONFIG_D' " "(given value= 0)", @@ -640,7 +661,7 @@ def test_efuse_is_int_given_0(self): def test_efuse_is_bitcount_given_out_of_range_val(self): self.espefuse_py( - "burn_efuse SPI_BOOT_CRYPT_CNT 9", + "burn-efuse SPI_BOOT_CRYPT_CNT 9", check_msg="A fatal error occurred: " "9 is too large an unsigned integer for a bitstring " "of length 3. The allowed range is [0, 7].", @@ -648,11 +669,11 @@ def test_efuse_is_bitcount_given_out_of_range_val(self): ) def test_efuse_is_bitcount_given_increase_over_max(self): - self.espefuse_py("burn_efuse SPI_BOOT_CRYPT_CNT") - self.espefuse_py("burn_efuse SPI_BOOT_CRYPT_CNT") - self.espefuse_py("burn_efuse SPI_BOOT_CRYPT_CNT") + self.espefuse_py("burn-efuse SPI_BOOT_CRYPT_CNT") + self.espefuse_py("burn-efuse SPI_BOOT_CRYPT_CNT") + self.espefuse_py("burn-efuse SPI_BOOT_CRYPT_CNT") self.espefuse_py( - "burn_efuse SPI_BOOT_CRYPT_CNT", + "burn-efuse SPI_BOOT_CRYPT_CNT", check_msg="A fatal error occurred: " "15 is too large an unsigned integer for a bitstring " "of length 3. The allowed range is [0, 7].", @@ -667,51 +688,48 @@ class TestBurnEfuseCommands(EfuseTestCase): ) def test_set_spi_flash_pin_efuses(self): self.espefuse_py( - "burn_efuse SPI_PAD_CONFIG_HD 30", + "burn-efuse SPI_PAD_CONFIG_HD 30", check_msg="A fatal error occurred: " "IO pins 30 & 31 cannot be set for SPI flash. 0-29, 32 & 33 only.", ret_code=2, ) self.espefuse_py( - "burn_efuse SPI_PAD_CONFIG_Q 0x23", + "burn-efuse SPI_PAD_CONFIG_Q 0x23", check_msg="A fatal error occurred: " "IO pin 35 cannot be set for SPI flash. 0-29, 32 & 33 only.", ret_code=2, ) - output = self.espefuse_py("burn_efuse SPI_PAD_CONFIG_CS0 33") + output = self.espefuse_py("burn-efuse SPI_PAD_CONFIG_CS0 33") assert "(Override SD_CMD pad (GPIO11/SPICS0)) 0b00000 -> 0b11111" in output assert "BURN BLOCK0 - OK (all write block bits are set)" in output - @pytest.mark.skipif( - arg_chip == "esp32p4", reason="No Custom MAC Address defined yet" - ) def test_burn_mac_custom_efuse(self): crc_msg = "(OK)" - self.espefuse_py("burn_efuse -h") + self.espefuse_py("burn-efuse -h") if arg_chip == "esp32": self.espefuse_py( - "burn_efuse MAC AA:CD:EF:01:02:03", + "burn-efuse MAC AA:CD:EF:01:02:03", check_msg="Writing Factory MAC address is not supported", ret_code=2, ) - self.espefuse_py("burn_efuse MAC_VERSION 1") + self.espefuse_py("burn-efuse MAC_VERSION 1") crc_msg = "(CRC 0x56 OK)" if arg_chip == "esp32c2": - self.espefuse_py("burn_efuse CUSTOM_MAC_USED 1") - self.espefuse_py("burn_efuse -h") + self.espefuse_py("burn-efuse CUSTOM_MAC_USED 1") + self.espefuse_py("burn-efuse -h") self.espefuse_py( - "burn_efuse CUSTOM_MAC AB:CD:EF:01:02:03", + "burn-efuse CUSTOM_MAC AB:CD:EF:01:02:03", check_msg="A fatal error occurred: Custom MAC must be a unicast MAC!", ret_code=2, ) - self.espefuse_py("burn_efuse CUSTOM_MAC AA:CD:EF:01:02:03") - self.espefuse_py("get_custom_mac", check_msg=f"aa:cd:ef:01:02:03 {crc_msg}") + self.espefuse_py("burn-efuse CUSTOM_MAC AA:CD:EF:01:02:03") + self.espefuse_py("get-custom-mac", check_msg=f"aa:cd:ef:01:02:03 {crc_msg}") def test_burn_efuse(self): - self.espefuse_py("burn_efuse -h") + self.espefuse_py("burn-efuse -h") if arg_chip == "esp32": self.espefuse_py( - "burn_efuse \ + "burn-efuse \ CHIP_VER_REV2 1 \ DISABLE_DL_ENCRYPT 1 \ CONSOLE_DEBUG_DISABLE 1" @@ -720,7 +738,7 @@ def test_burn_efuse(self): blk2 = "BLOCK2" elif arg_chip == "esp32c2": self.espefuse_py( - "burn_efuse \ + "burn-efuse \ XTS_KEY_LENGTH_256 1 \ UART_PRINT_CONTROL 1 \ FORCE_SEND_RESUME 1" @@ -729,25 +747,25 @@ def test_burn_efuse(self): blk2 = None else: self.espefuse_py( - "burn_efuse \ + "burn-efuse \ SECURE_BOOT_EN 1 \ UART_PRINT_CONTROL 1" ) - if arg_chip not in ["esp32c5", "esp32c5beta3", "esp32c61"]: + if arg_chip not in ["esp32h21", "esp32h4"]: # chips having the OPTIONAL_UNIQUE_ID field self.espefuse_py( - "burn_efuse \ + "burn-efuse \ OPTIONAL_UNIQUE_ID 0x2328ad5ac9145f698f843a26d6eae168", check_msg="-> 0x2328ad5ac9145f698f843a26d6eae168", ) - output = self.espefuse_py("summary -d") + output = self.espefuse_py("-d summary") assert ( "read_regs: d6eae168 8f843a26 c9145f69 2328ad5a " "00000000 00000000 00000000 00000000" ) in output assert "= 68 e1 ea d6 26 3a 84 8f 69 5f 14 c9 5a ad 28 23 R/W" in output self.espefuse_py( - "burn_bit BLOCK_SYS_DATA 1", + "burn-bit BLOCK_SYS_DATA 1", check_msg="Burn into BLOCK_SYS_DATA is forbidden " "(RS coding scheme does not allow this).", ret_code=2, @@ -755,14 +773,14 @@ def test_burn_efuse(self): blk1 = "BLOCK_KEY1" blk2 = "BLOCK_KEY2" output = self.espefuse_py( - f"burn_efuse {blk1}" + f"burn-efuse {blk1}" + " 0x00010203040506070809111111111111111111111111111111110000112233FF" ) assert ( "-> 0x00010203040506070809111111111111111111111111111111110000112233ff" in output ) - output = self.espefuse_py("summary -d") + output = self.espefuse_py("-d summary") assert ( "read_regs: 112233ff 11110000 11111111 11111111 " "11111111 08091111 04050607 00010203" @@ -774,14 +792,14 @@ def test_burn_efuse(self): if blk2 is not None: output = self.espefuse_py( - f"burn_efuse {blk2}" + f"burn-efuse {blk2}" + " 00010203040506070809111111111111111111111111111111110000112233FF" ) assert ( "-> 0xff33221100001111111111111111111111111111111109080706050403020100" in output ) - output = self.espefuse_py("summary -d") + output = self.espefuse_py("-d summary") assert ( "read_regs: 03020100 07060504 11110908 11111111 " "11111111 11111111 00001111 ff332211" @@ -796,10 +814,10 @@ def test_burn_efuse(self): ) def test_burn_efuse_with_34_coding_scheme(self): self._set_34_coding_scheme() - self.espefuse_py("burn_efuse BLK3_PART_RESERVE 1") - self.espefuse_py("burn_efuse ADC1_TP_LOW 50") + self.espefuse_py("burn-efuse BLK3_PART_RESERVE 1") + self.espefuse_py("burn-efuse ADC1_TP_LOW 50") self.espefuse_py( - "burn_efuse ADC1_TP_HIGH 55", + "burn-efuse ADC1_TP_HIGH 55", check_msg="Burn into BLOCK3 is forbidden " "(3/4 coding scheme does not allow this)", ret_code=2, @@ -810,9 +828,9 @@ def test_burn_efuse_with_34_coding_scheme(self): ) def test_burn_efuse_with_34_coding_scheme2(self): self._set_34_coding_scheme() - self.espefuse_py("burn_efuse BLK3_PART_RESERVE 1") + self.espefuse_py("burn-efuse BLK3_PART_RESERVE 1") self.espefuse_py( - "burn_efuse \ + "burn-efuse \ ADC1_TP_LOW 50 \ ADC1_TP_HIGH 55 \ ADC2_TP_LOW 40 \ @@ -821,72 +839,72 @@ def test_burn_efuse_with_34_coding_scheme2(self): @pytest.mark.skipif( arg_chip != "esp32s3", - reason="Currently S3 only has this efuse incompatibility check", + reason="Currently S3 only has this eFuse incompatibility check", ) def test_burn_efuse_incompatibility_check(self): self.espefuse_py( - "burn_efuse DIS_USB_JTAG 1 DIS_USB_SERIAL_JTAG 1", + "burn-efuse DIS_USB_JTAG 1 DIS_USB_SERIAL_JTAG 1", check_msg="Incompatible eFuse settings detected, abort", ret_code=2, ) - self.espefuse_py("burn_efuse DIS_USB_JTAG 1") + self.espefuse_py("burn-efuse DIS_USB_JTAG 1") self.espefuse_py( - "burn_efuse DIS_USB_SERIAL_JTAG 1", + "burn-efuse DIS_USB_SERIAL_JTAG 1", check_msg="Incompatible eFuse settings detected, abort", ret_code=2, ) - self.espefuse_py("burn_efuse DIS_USB_SERIAL_JTAG 1 --force") + self.espefuse_py("burn-efuse DIS_USB_SERIAL_JTAG 1 --force") class TestBurnKeyCommands(EfuseTestCase): @pytest.mark.skipif(arg_chip != "esp32", reason="ESP32-only") def test_burn_key_3_key_blocks(self): - self.espefuse_py("burn_key -h") + self.espefuse_py("burn-key -h") self.espefuse_py( - f"burn_key BLOCK1 {IMAGES_DIR}/192bit", + f"burn-key BLOCK1 {IMAGES_DIR}/192bit", check_msg="A fatal error occurred: Incorrect key file size 24. " "Key file must be 32 bytes (256 bits) of raw binary key data.", ret_code=2, ) self.espefuse_py( - f"burn_key \ + f"burn-key \ BLOCK1 {IMAGES_DIR}/256bit \ BLOCK2 {IMAGES_DIR}/256bit_1 \ BLOCK3 {IMAGES_DIR}/256bit_2 --no-protect-key" ) - output = self.espefuse_py("summary -d") + output = self.espefuse_py("-d summary") self.check_data_block_in_log(output, f"{IMAGES_DIR}/256bit") self.check_data_block_in_log(output, f"{IMAGES_DIR}/256bit_1") self.check_data_block_in_log(output, f"{IMAGES_DIR}/256bit_2") self.espefuse_py( - f"burn_key \ + f"burn-key \ BLOCK1 {IMAGES_DIR}/256bit \ BLOCK2 {IMAGES_DIR}/256bit_1 \ BLOCK3 {IMAGES_DIR}/256bit_2" ) - output = self.espefuse_py("summary -d") + output = self.espefuse_py("-d summary") self.check_data_block_in_log(output, f"{IMAGES_DIR}/256bit") self.check_data_block_in_log(output, f"{IMAGES_DIR}/256bit_1") self.check_data_block_in_log(output, f"{IMAGES_DIR}/256bit_2") @pytest.mark.skipif(arg_chip != "esp32c2", reason="ESP32-C2-only") def test_burn_key_1_key_block(self): - self.espefuse_py("burn_key -h") + self.espefuse_py("burn-key -h") self.espefuse_py( - f"burn_key BLOCK_KEY0 {IMAGES_DIR}/128bit XTS_AES_128_KEY", + f"burn-key BLOCK_KEY0 {IMAGES_DIR}/128bit XTS_AES_128_KEY", check_msg="A fatal error occurred: Incorrect key file size 16. " "Key file must be 32 bytes (256 bits) of raw binary key data.", ret_code=2, ) self.espefuse_py( - f"burn_key BLOCK_KEY0 {IMAGES_DIR}/256bit XTS_AES_128_KEY --no-read-protect" + f"burn-key BLOCK_KEY0 {IMAGES_DIR}/256bit XTS_AES_128_KEY --no-read-protect" ) - output = self.espefuse_py("summary -d") + output = self.espefuse_py("-d summary") self.check_data_block_in_log(output, f"{IMAGES_DIR}/256bit", reverse_order=True) - self.espefuse_py(f"burn_key BLOCK_KEY0 {IMAGES_DIR}/256bit XTS_AES_128_KEY") - output = self.espefuse_py("summary -d") + self.espefuse_py(f"burn-key BLOCK_KEY0 {IMAGES_DIR}/256bit XTS_AES_128_KEY") + output = self.espefuse_py("-d summary") assert ( "[3 ] read_regs: 00000000 00000000 00000000 00000000 " "00000000 00000000 00000000 00000000" @@ -899,31 +917,31 @@ def test_burn_key_1_key_block(self): @pytest.mark.skipif(arg_chip != "esp32c2", reason="ESP32-C2-only") def test_burn_key_one_key_block_with_fe_and_sb_keys(self): - self.espefuse_py("burn_key -h") + self.espefuse_py("burn-key -h") self.espefuse_py( - f"burn_key BLOCK_KEY0 {IMAGES_DIR}/256bit XTS_AES_128_KEY \ + f"burn-key BLOCK_KEY0 {IMAGES_DIR}/256bit XTS_AES_128_KEY \ BLOCK_KEY0 {IMAGES_DIR}/128bit_key SECURE_BOOT_DIGEST", check_msg="A fatal error occurred: These keypurposes are incompatible " "['XTS_AES_128_KEY', 'SECURE_BOOT_DIGEST']", ret_code=2, ) self.espefuse_py( - f"burn_key BLOCK_KEY0 {IMAGES_DIR}/128bit_key " + f"burn-key BLOCK_KEY0 {IMAGES_DIR}/128bit_key " f"XTS_AES_128_KEY_DERIVED_FROM_128_EFUSE_BITS " f"BLOCK_KEY0 {IMAGES_DIR}/128bit_key SECURE_BOOT_DIGEST --no-read-protect" ) - output = self.espefuse_py("summary -d") + output = self.espefuse_py("-d summary") assert ( "[3 ] read_regs: 0c0d0e0f 08090a0b 04050607 00010203 " "03020100 07060504 0b0a0908 0f0e0d0c" ) in output self.espefuse_py( - f"burn_key BLOCK_KEY0 {IMAGES_DIR}/128bit_key " + f"burn-key BLOCK_KEY0 {IMAGES_DIR}/128bit_key " "XTS_AES_128_KEY_DERIVED_FROM_128_EFUSE_BITS " f"BLOCK_KEY0 {IMAGES_DIR}/128bit_key SECURE_BOOT_DIGEST" ) - output = self.espefuse_py("summary -d") + output = self.espefuse_py("-d summary") assert ( "[3 ] read_regs: 00000000 00000000 00000000 00000000 " "03020100 07060504 0b0a0908 0f0e0d0c" @@ -941,20 +959,17 @@ def test_burn_key_one_key_block_with_fe_and_sb_keys(self): not in [ "esp32s2", "esp32s3", - "esp32s3beta1", "esp32c3", - "esp32h2beta1", "esp32c6", "esp32h2", "esp32p4", "esp32c5", - "esp32c5beta3", "esp32c61", ], reason="Only chips with 6 keys", ) def test_burn_key_with_6_keys(self): - cmd = f"burn_key \ + cmd = f"burn-key \ BLOCK_KEY0 {IMAGES_DIR}/256bit XTS_AES_256_KEY_1 \ BLOCK_KEY1 {IMAGES_DIR}/256bit_1 XTS_AES_256_KEY_2 \ BLOCK_KEY2 {IMAGES_DIR}/256bit_2 XTS_AES_128_KEY" @@ -962,14 +977,12 @@ def test_burn_key_with_6_keys(self): "esp32c3", "esp32c6", "esp32h2", - "esp32h2beta1", "esp32c5", - "esp32c5beta3", ]: cmd = cmd.replace("XTS_AES_256_KEY_1", "XTS_AES_128_KEY") cmd = cmd.replace("XTS_AES_256_KEY_2", "XTS_AES_128_KEY") self.espefuse_py(cmd + " --no-read-protect --no-write-protect") - output = self.espefuse_py("summary -d") + output = self.espefuse_py("-d summary") self.check_data_block_in_log(output, f"{IMAGES_DIR}/256bit", reverse_order=True) self.check_data_block_in_log( output, f"{IMAGES_DIR}/256bit_1", reverse_order=True @@ -979,7 +992,7 @@ def test_burn_key_with_6_keys(self): ) self.espefuse_py(cmd) - output = self.espefuse_py("summary -d") + output = self.espefuse_py("-d summary") assert ( "[4 ] read_regs: 00000000 00000000 00000000 00000000 " "00000000 00000000 00000000 00000000" @@ -994,12 +1007,12 @@ def test_burn_key_with_6_keys(self): ) in output self.espefuse_py( - f"burn_key \ + f"burn-key \ BLOCK_KEY3 {IMAGES_DIR}/256bit SECURE_BOOT_DIGEST0 \ BLOCK_KEY4 {IMAGES_DIR}/256bit_1 SECURE_BOOT_DIGEST1 \ BLOCK_KEY5 {IMAGES_DIR}/256bit_2 SECURE_BOOT_DIGEST2" ) - output = self.espefuse_py("summary -d") + output = self.espefuse_py("-d summary") self.check_data_block_in_log(output, f"{IMAGES_DIR}/256bit") self.check_data_block_in_log(output, f"{IMAGES_DIR}/256bit_1") self.check_data_block_in_log(output, f"{IMAGES_DIR}/256bit_2") @@ -1010,29 +1023,29 @@ def test_burn_key_with_6_keys(self): def test_burn_key_with_34_coding_scheme(self): self._set_34_coding_scheme() self.espefuse_py( - f"burn_key BLOCK1 {IMAGES_DIR}/256bit", + f"burn-key BLOCK1 {IMAGES_DIR}/256bit", check_msg="A fatal error occurred: Incorrect key file size 32. " "Key file must be 24 bytes (192 bits) of raw binary key data.", ret_code=2, ) self.espefuse_py( - f"burn_key \ + f"burn-key \ BLOCK1 {IMAGES_DIR}/192bit \ BLOCK2 {IMAGES_DIR}/192bit_1 \ BLOCK3 {IMAGES_DIR}/192bit_2 --no-protect-key" ) - output = self.espefuse_py("summary -d") + output = self.espefuse_py("-d summary") self.check_data_block_in_log(output, f"{IMAGES_DIR}/192bit") self.check_data_block_in_log(output, f"{IMAGES_DIR}/192bit_1") self.check_data_block_in_log(output, f"{IMAGES_DIR}/192bit_2") self.espefuse_py( - f"burn_key \ + f"burn-key \ BLOCK1 {IMAGES_DIR}/192bit \ BLOCK2 {IMAGES_DIR}/192bit_1 \ BLOCK3 {IMAGES_DIR}/192bit_2" ) - output = self.espefuse_py("summary -d") + output = self.espefuse_py("-d summary") self.check_data_block_in_log(output, f"{IMAGES_DIR}/192bit") self.check_data_block_in_log(output, f"{IMAGES_DIR}/192bit_1") self.check_data_block_in_log(output, f"{IMAGES_DIR}/192bit_2") @@ -1043,11 +1056,11 @@ def test_burn_key_with_34_coding_scheme(self): ) def test_burn_key_512bit(self): self.espefuse_py( - f"burn_key \ + f"burn-key \ BLOCK_KEY0 {IMAGES_DIR}/256bit_1_256bit_2_combined \ XTS_AES_256_KEY --no-read-protect --no-write-protect" ) - output = self.espefuse_py("summary -d") + output = self.espefuse_py("-d summary") self.check_data_block_in_log( output, f"{IMAGES_DIR}/256bit_1", reverse_order=True ) @@ -1063,25 +1076,25 @@ def test_burn_key_512bit_non_consecutive_blocks(self): # Burn efuses separately to test different kinds # of "key used" detection criteria self.espefuse_py( - f"burn_key \ + f"burn-key \ BLOCK_KEY2 {IMAGES_DIR}/256bit XTS_AES_128_KEY" ) self.espefuse_py( - f"burn_key \ + f"burn-key \ BLOCK_KEY4 {IMAGES_DIR}/256bit SECURE_BOOT_DIGEST0" ) self.espefuse_py( - f"burn_key \ + f"burn-key \ BLOCK_KEY1 {IMAGES_DIR}/256bit_1_256bit_2_combined \ XTS_AES_256_KEY --no-read-protect --no-write-protect" ) self.espefuse_py( - f"burn_key \ + f"burn-key \ BLOCK_KEY5 {IMAGES_DIR}/256bit USER --no-read-protect --no-write-protect" ) # Second half of key should burn to first available key block (BLOCK_KEY5) - output = self.espefuse_py("summary -d") + output = self.espefuse_py("-d summary") self.check_data_block_in_log( output, f"{IMAGES_DIR}/256bit_1", reverse_order=True ) @@ -1104,7 +1117,7 @@ def test_burn_key_512bit_non_consecutive_blocks(self): ) def test_burn_key_512bit_non_consecutive_blocks_loop_around(self): self.espefuse_py( - f"burn_key \ + f"burn-key \ BLOCK_KEY2 {IMAGES_DIR}/256bit XTS_AES_128_KEY \ BLOCK_KEY3 {IMAGES_DIR}/256bit USER \ BLOCK_KEY4 {IMAGES_DIR}/256bit SECURE_BOOT_DIGEST0 \ @@ -1114,7 +1127,7 @@ def test_burn_key_512bit_non_consecutive_blocks_loop_around(self): ) # Second half of key should burn to first available key block (BLOCK_KEY0) - output = self.espefuse_py("summary -d") + output = self.espefuse_py("-d summary") self.check_data_block_in_log( output, f"{IMAGES_DIR}/256bit_1", reverse_order=True ) @@ -1132,18 +1145,18 @@ def test_burn_key_512bit_non_consecutive_blocks_loop_around(self): ) in output @pytest.mark.skipif( - arg_chip not in ["esp32h2", "esp32c5", "esp32c5beta3", "esp32c61", "esp32p4"], + arg_chip not in ["esp32h2", "esp32c5", "esp32c61", "esp32p4"], reason="These chips support ECDSA_KEY", ) def test_burn_key_ecdsa_key(self): self.espefuse_py( - f"burn_key \ + f"burn-key \ BLOCK_KEY0 {S_IMAGES_DIR}/ecdsa192_secure_boot_signing_key_v2.pem \ ECDSA_KEY \ BLOCK_KEY1 {S_IMAGES_DIR}/ecdsa256_secure_boot_signing_key_v2.pem \ ECDSA_KEY" ) - output = self.espefuse_py("summary -d") + output = self.espefuse_py("-d summary") assert 2 == output.count( "= ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? " "?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? -/-" @@ -1158,19 +1171,19 @@ def test_burn_key_ecdsa_key(self): ) in output @pytest.mark.skipif( - arg_chip not in ["esp32h2", "esp32c5", "esp32c5beta3", "esp32c61", "esp32p4"], + arg_chip not in ["esp32h2", "esp32c5", "esp32c61", "esp32p4"], reason="These chips support ECDSA_KEY", ) def test_burn_key_ecdsa_key_check_byte_order(self): self.espefuse_py( - f"burn_key \ + f"burn-key \ BLOCK_KEY0 {S_IMAGES_DIR}/ecdsa192_secure_boot_signing_key_v2.pem \ ECDSA_KEY \ BLOCK_KEY1 {S_IMAGES_DIR}/ecdsa256_secure_boot_signing_key_v2.pem \ ECDSA_KEY \ --no-read-protect" ) - output = self.espefuse_py("summary -d") + output = self.espefuse_py("-d summary") assert ( "= c8 c4 5d 62 9e 05 05 bd cb 04 a4 7c 06 f5 86 14 " "cb 23 81 23 95 b7 71 4f 00 00 00 00 00 00 00 00 R/-" @@ -1191,24 +1204,23 @@ def test_burn_key_ecdsa_key_check_byte_order(self): class TestBurnBlockDataCommands(EfuseTestCase): def test_burn_block_data_check_args(self): - self.espefuse_py("burn_block_data -h") + self.espefuse_py("burn-block-data -h") blk0 = "BLOCK0" blk1 = "BLOCK1" self.espefuse_py( - f"burn_block_data {blk0} {IMAGES_DIR}/224bit {blk1}", - check_msg="A fatal error occurred: " - "The number of block_name (2) and datafile (1) should be the same.", + f"burn-block-data {blk0} {IMAGES_DIR}/224bit {blk1}", + check_msg="Expected multiple of 2 values, got 3", ret_code=2, ) @pytest.mark.skipif(arg_chip != "esp32", reason="ESP32-only") def test_burn_block_data_with_3_key_blocks(self): self.espefuse_py( - f"burn_block_data \ + f"burn-block-data \ BLOCK0 {IMAGES_DIR}/224bit \ BLOCK3 {IMAGES_DIR}/256bit" ) - output = self.espefuse_py("summary -d") + output = self.espefuse_py("-d summary") assert ( "[3 ] read_regs: a3a2a1a0 a7a6a5a4 abaaa9a8 afaeadac " "b3b2b1b0 b7b6b5b4 bbbab9b8 bfbebdbc" @@ -1216,31 +1228,31 @@ def test_burn_block_data_with_3_key_blocks(self): self.check_data_block_in_log(output, f"{IMAGES_DIR}/256bit") self.espefuse_py( - f"burn_block_data \ + f"burn-block-data \ BLOCK2 {IMAGES_DIR}/256bit_1" ) self.check_data_block_in_log( - self.espefuse_py("summary -d"), f"{IMAGES_DIR}/256bit_1" + self.espefuse_py("-d summary"), f"{IMAGES_DIR}/256bit_1" ) self.espefuse_py( - f"burn_block_data \ + f"burn-block-data \ BLOCK1 {IMAGES_DIR}/256bit_2" ) self.check_data_block_in_log( - self.espefuse_py("summary -d"), f"{IMAGES_DIR}/256bit_2" + self.espefuse_py("-d summary"), f"{IMAGES_DIR}/256bit_2" ) @pytest.mark.skipif(arg_chip != "esp32c2", reason="ESP32-C2-only") def test_burn_block_data_with_1_key_block(self): self.espefuse_py( - f"burn_block_data \ + f"burn-block-data \ BLOCK0 {IMAGES_DIR}/64bit \ BLOCK1 {IMAGES_DIR}/96bit \ BLOCK2 {IMAGES_DIR}/256bit \ BLOCK3 {IMAGES_DIR}/256bit" ) - output = self.espefuse_py("summary -d") + output = self.espefuse_py("-d summary") assert "[0 ] read_regs: 00000001 0000000c" in output assert "[1 ] read_regs: 03020100 07060504 000a0908" in output assert ( @@ -1257,25 +1269,22 @@ def test_burn_block_data_with_1_key_block(self): not in [ "esp32s2", "esp32s3", - "esp32s3beta1", "esp32c3", - "esp32h2beta1", "esp32c6", "esp32h2", "esp32p4", "esp32c5", - "esp32c5beta3", "esp32c61", ], reason="Only chip with 6 keys", ) def test_burn_block_data_with_6_keys(self): self.espefuse_py( - f"burn_block_data \ + f"burn-block-data \ BLOCK0 {IMAGES_DIR}/192bit \ BLOCK3 {IMAGES_DIR}/256bit" ) - output = self.espefuse_py("summary -d") + output = self.espefuse_py("-d summary") assert ( "[0 ] read_regs: 00000000 07060500 00000908 00000000 13000000 00161514" in output @@ -1286,39 +1295,41 @@ def test_burn_block_data_with_6_keys(self): ) in output self.check_data_block_in_log(output, f"{IMAGES_DIR}/256bit") - self.espefuse_py( - f"burn_block_data \ - BLOCK10 {IMAGES_DIR}/256bit_1" - ) - self.check_data_block_in_log( - self.espefuse_py("summary -d"), f"{IMAGES_DIR}/256bit_1" - ) + if arg_chip != "esp32p4": + # BLOCK10 is free. In P4 it is used for ADC calib data. + self.espefuse_py( + f"burn-block-data \ + BLOCK10 {IMAGES_DIR}/256bit_3" + ) + self.check_data_block_in_log( + self.espefuse_py("-d summary"), f"{IMAGES_DIR}/256bit_3" + ) self.espefuse_py( - f"burn_block_data \ + f"burn-block-data \ BLOCK1 {IMAGES_DIR}/192bit \ BLOCK5 {IMAGES_DIR}/256bit_1 \ BLOCK6 {IMAGES_DIR}/256bit_2" ) - output = self.espefuse_py("summary -d") + output = self.espefuse_py("-d summary") assert ( "[1 ] read_regs: 00000000 07060500 00000908 00000000 13000000 00161514" in output ) self.check_data_block_in_log(output, f"{IMAGES_DIR}/256bit") - self.check_data_block_in_log(output, f"{IMAGES_DIR}/256bit_1", 2) + self.check_data_block_in_log(output, f"{IMAGES_DIR}/256bit_1") self.check_data_block_in_log(output, f"{IMAGES_DIR}/256bit_2") def test_burn_block_data_check_errors(self): self.espefuse_py( - f"burn_block_data \ + f"burn-block-data \ BLOCK2 {IMAGES_DIR}/192bit \ BLOCK2 {IMAGES_DIR}/192bit_1", check_msg="A fatal error occurred: Found repeated", ret_code=2, ) self.espefuse_py( - f"burn_block_data \ + f"burn-block-data \ BLOCK2 {IMAGES_DIR}/192bit \ BLOCK3 {IMAGES_DIR}/192bit_1 \ --offset 4", @@ -1327,12 +1338,12 @@ def test_burn_block_data_check_errors(self): ret_code=2, ) self.espefuse_py( - f"burn_block_data BLOCK0 {IMAGES_DIR}/192bit --offset 33", + f"burn-block-data BLOCK0 {IMAGES_DIR}/192bit --offset 33", check_msg="A fatal error occurred: Invalid offset: the block0 only holds", ret_code=2, ) self.espefuse_py( - f"burn_block_data BLOCK0 {IMAGES_DIR}/256bit --offset 4", + f"burn-block-data BLOCK0 {IMAGES_DIR}/256bit --offset 4", check_msg="A fatal error occurred: Data does not fit:", ret_code=2, ) @@ -1341,45 +1352,45 @@ def test_burn_block_data_check_errors(self): def test_burn_block_data_with_offset_for_3_key_blocks(self): offset = 1 self.espefuse_py( - f"burn_block_data --offset {offset} BLOCK0 {IMAGES_DIR}/192bit" + f"burn-block-data --offset {offset} BLOCK0 {IMAGES_DIR}/192bit" ) offset = 4 self.espefuse_py( - f"burn_block_data --offset {offset} BLOCK1 {IMAGES_DIR}/192bit_1" + f"burn-block-data --offset {offset} BLOCK1 {IMAGES_DIR}/192bit_1" ) self.check_data_block_in_log( - self.espefuse_py("summary -d"), f"{IMAGES_DIR}/192bit_1", offset=offset + self.espefuse_py("-d summary"), f"{IMAGES_DIR}/192bit_1", offset=offset ) offset = 6 self.espefuse_py( - f"burn_block_data --offset {offset} BLOCK2 {IMAGES_DIR}/192bit_2" + f"burn-block-data --offset {offset} BLOCK2 {IMAGES_DIR}/192bit_2" ) self.check_data_block_in_log( - self.espefuse_py("summary -d"), f"{IMAGES_DIR}/192bit_2", offset=offset + self.espefuse_py("-d summary"), f"{IMAGES_DIR}/192bit_2", offset=offset ) offset = 8 self.espefuse_py( - f"burn_block_data --offset {offset} BLOCK3 {IMAGES_DIR}/192bit_2" + f"burn-block-data --offset {offset} BLOCK3 {IMAGES_DIR}/192bit_2" ) self.check_data_block_in_log( - self.espefuse_py("summary -d"), f"{IMAGES_DIR}/192bit_2", offset=offset + self.espefuse_py("-d summary"), f"{IMAGES_DIR}/192bit_2", offset=offset ) @pytest.mark.skipif(arg_chip != "esp32c2", reason="ESP32-C2-only") def test_burn_block_data_with_offset_1_key_block(self): offset = 4 - self.espefuse_py(f"burn_block_data --offset {offset} BLOCK1 {IMAGES_DIR}/92bit") - output = self.espefuse_py("summary -d") + self.espefuse_py(f"burn-block-data --offset {offset} BLOCK1 {IMAGES_DIR}/92bit") + output = self.espefuse_py("-d summary") assert "[1 ] read_regs: 00000000 03020100 00060504" in output offset = 6 self.espefuse_py( - f"burn_block_data --offset {offset} BLOCK2 {IMAGES_DIR}/192bit_1" + f"burn-block-data --offset {offset} BLOCK2 {IMAGES_DIR}/192bit_1" ) - output = self.espefuse_py("summary -d") + output = self.espefuse_py("-d summary") assert ( "[2 ] read_regs: 00000000 00110000 05000000 09080706 " "0d0c0b0a 11100f0e 15141312 00002116" @@ -1387,10 +1398,10 @@ def test_burn_block_data_with_offset_1_key_block(self): offset = 8 self.espefuse_py( - f"burn_block_data --offset {offset} BLOCK3 {IMAGES_DIR}/192bit_2" + f"burn-block-data --offset {offset} BLOCK3 {IMAGES_DIR}/192bit_2" ) self.check_data_block_in_log( - self.espefuse_py("summary -d"), f"{IMAGES_DIR}/192bit_2", offset=offset + self.espefuse_py("-d summary"), f"{IMAGES_DIR}/192bit_2", offset=offset ) @pytest.mark.skipif( @@ -1398,14 +1409,11 @@ def test_burn_block_data_with_offset_1_key_block(self): not in [ "esp32s2", "esp32s3", - "esp32s3beta1", "esp32c3", - "esp32h2beta1", "esp32c6", "esp32h2", "esp32p4", "esp32c5", - "esp32c5beta3", "esp32c61", ], reason="Only chips with 6 keys", @@ -1413,26 +1421,26 @@ def test_burn_block_data_with_offset_1_key_block(self): def test_burn_block_data_with_offset_6_keys(self): offset = 4 self.espefuse_py( - f"burn_block_data --offset {offset} BLOCK_KEY0 {IMAGES_DIR}/192bit_1" + f"burn-block-data --offset {offset} BLOCK_KEY0 {IMAGES_DIR}/192bit_1" ) self.check_data_block_in_log( - self.espefuse_py("summary -d"), f"{IMAGES_DIR}/192bit_1", offset=offset + self.espefuse_py("-d summary"), f"{IMAGES_DIR}/192bit_1", offset=offset ) offset = 6 self.espefuse_py( - f"burn_block_data --offset {offset} BLOCK_KEY1 {IMAGES_DIR}/192bit_2" + f"burn-block-data --offset {offset} BLOCK_KEY1 {IMAGES_DIR}/192bit_2" ) self.check_data_block_in_log( - self.espefuse_py("summary -d"), f"{IMAGES_DIR}/192bit_2", offset=offset + self.espefuse_py("-d summary"), f"{IMAGES_DIR}/192bit_2", offset=offset ) offset = 8 self.espefuse_py( - f"burn_block_data --offset {offset} BLOCK_KEY2 {IMAGES_DIR}/192bit_2" + f"burn-block-data --offset {offset} BLOCK_KEY2 {IMAGES_DIR}/192bit_2" ) self.check_data_block_in_log( - self.espefuse_py("summary -d"), f"{IMAGES_DIR}/192bit_2", offset=offset + self.espefuse_py("-d summary"), f"{IMAGES_DIR}/192bit_2", offset=offset ) @pytest.mark.skipif( @@ -1441,19 +1449,19 @@ def test_burn_block_data_with_offset_6_keys(self): def test_burn_block_data_with_34_coding_scheme(self): self._set_34_coding_scheme() self.espefuse_py( - f"burn_block_data BLOCK1 {IMAGES_DIR}/256bit", + f"burn-block-data BLOCK1 {IMAGES_DIR}/256bit", check_msg="A fatal error occurred: Data does not fit: " "the block1 size is 24 bytes, data file is 32 bytes, offset 0", ret_code=2, ) self.espefuse_py( - f"burn_block_data \ + f"burn-block-data \ BLOCK1 {IMAGES_DIR}/192bit \ BLOCK2 {IMAGES_DIR}/192bit_1 \ BLOCK3 {IMAGES_DIR}/192bit_2" ) - output = self.espefuse_py("summary -d") + output = self.espefuse_py("-d summary") self.check_data_block_in_log(output, f"{IMAGES_DIR}/192bit") self.check_data_block_in_log(output, f"{IMAGES_DIR}/192bit_1") self.check_data_block_in_log(output, f"{IMAGES_DIR}/192bit_2") @@ -1466,59 +1474,59 @@ def test_burn_block_data_with_34_coding_scheme_and_offset(self): offset = 4 self.espefuse_py( - f"burn_block_data --offset {offset} BLOCK1 {IMAGES_DIR}/128bit" + f"burn-block-data --offset {offset} BLOCK1 {IMAGES_DIR}/128bit" ) self.check_data_block_in_log( - self.espefuse_py("summary -d"), f"{IMAGES_DIR}/128bit", offset=offset + self.espefuse_py("-d summary"), f"{IMAGES_DIR}/128bit", offset=offset ) offset = 6 self.espefuse_py( - f"burn_block_data --offset {offset} BLOCK2 {IMAGES_DIR}/128bit" + f"burn-block-data --offset {offset} BLOCK2 {IMAGES_DIR}/128bit" ) self.check_data_block_in_log( - self.espefuse_py("summary -d"), f"{IMAGES_DIR}/128bit", offset=offset + self.espefuse_py("-d summary"), f"{IMAGES_DIR}/128bit", offset=offset ) offset = 8 self.espefuse_py( - f"burn_block_data --offset {offset} BLOCK3 {IMAGES_DIR}/128bit" + f"burn-block-data --offset {offset} BLOCK3 {IMAGES_DIR}/128bit" ) self.check_data_block_in_log( - self.espefuse_py("summary -d"), f"{IMAGES_DIR}/128bit", offset=offset + self.espefuse_py("-d summary"), f"{IMAGES_DIR}/128bit", offset=offset ) @pytest.mark.skipif(arg_chip != "esp32", reason="ESP32-only, supports 2 key blocks") class TestBurnKeyDigestCommandsEsp32(EfuseTestCase): def test_burn_key_digest(self): - self.espefuse_py("burn_key_digest -h") + self.espefuse_py("burn-key-digest -h") esp = self.get_esptool() if esp.get_chip_revision() >= 300: self.espefuse_py( - f"burn_key_digest {S_IMAGES_DIR}/rsa_secure_boot_signing_key.pem" + f"burn-key-digest {S_IMAGES_DIR}/rsa_secure_boot_signing_key.pem" ) - output = self.espefuse_py("summary -d") + output = self.espefuse_py("-d summary") assert ( " = 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 R/-" ) in output else: self.espefuse_py( - f"burn_key_digest {S_IMAGES_DIR}/rsa_secure_boot_signing_key.pem", + f"burn-key-digest {S_IMAGES_DIR}/rsa_secure_boot_signing_key.pem", check_msg="Incorrect chip revision for Secure boot v2.", ret_code=2, ) def test_burn_key_from_digest(self): - # python espsecure.py digest_rsa_public_key + # python espsecure digest_rsa_public_key # --keyfile test/{S_IMAGES_DIR}/rsa_secure_boot_signing_key.pem # -o {S_IMAGES_DIR}/rsa_public_key_digest.bin self.espefuse_py( - f"burn_key \ + f"burn-key \ BLOCK2 {S_IMAGES_DIR}/rsa_public_key_digest.bin --no-protect-key" ) - output = self.espefuse_py("summary -d") + output = self.espefuse_py("-d summary") assert 1 == output.count( " = 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 R/W" @@ -1527,8 +1535,8 @@ def test_burn_key_from_digest(self): def test_burn_key_digest_with_34_coding_scheme(self): self._set_34_coding_scheme() self.espefuse_py( - f"burn_key_digest {S_IMAGES_DIR}/rsa_secure_boot_signing_key.pem", - check_msg="burn_key_digest only works with 'None' coding scheme", + f"burn-key-digest {S_IMAGES_DIR}/rsa_secure_boot_signing_key.pem", + check_msg="burn-key-digest only works with 'None' coding scheme", ret_code=2, ) @@ -1536,13 +1544,13 @@ def test_burn_key_digest_with_34_coding_scheme(self): @pytest.mark.skipif(arg_chip != "esp32c2", reason="ESP32-C2-only, supports 1 key block") class TestBurnKeyDigestCommandsEsp32C2(EfuseTestCase): def test_burn_key_digest1(self): - # python espsecure.py generate_signing_key --version 2 + # python espsecure generate_signing_key --version 2 # secure_images/ecdsa192_secure_boot_signing_key_v2.pem --scheme ecdsa192 - self.espefuse_py("burn_key_digest -h") + self.espefuse_py("burn-key-digest -h") self.espefuse_py( - f"burn_key_digest {S_IMAGES_DIR}/ecdsa192_secure_boot_signing_key_v2.pem" + f"burn-key-digest {S_IMAGES_DIR}/ecdsa192_secure_boot_signing_key_v2.pem" ) - output = self.espefuse_py("summary -d") + output = self.espefuse_py("-d summary") assert " = 1e 3d 15 16 96 ca 7f 22 a6 e8 8b d5 27 a0 3b 3b R/-" in output assert ( " = 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 " @@ -1550,13 +1558,13 @@ def test_burn_key_digest1(self): ) in output def test_burn_key_digest2(self): - # python espsecure.py generate_signing_key --version 2 + # python espsecure generate_signing_key --version 2 # secure_images/ecdsa256_secure_boot_signing_key_v2.pem --scheme ecdsa256 - self.espefuse_py("burn_key_digest -h") + self.espefuse_py("burn-key-digest -h") self.espefuse_py( - f"burn_key_digest {S_IMAGES_DIR}/ecdsa256_secure_boot_signing_key_v2.pem" + f"burn-key-digest {S_IMAGES_DIR}/ecdsa256_secure_boot_signing_key_v2.pem" ) - output = self.espefuse_py("summary -d") + output = self.espefuse_py("-d summary") assert " = bf 0f 6a f6 8b d3 6d 8b 53 b3 da a9 33 f6 0a 04 R/-" in output assert ( " = 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 " @@ -1564,28 +1572,28 @@ def test_burn_key_digest2(self): ) in output def test_burn_key_from_digest1(self): - # python espsecure.py digest_sbv2_public_key --keyfile + # python espsecure digest_sbv2_public_key --keyfile # secure_images/ecdsa192_secure_boot_signing_key_v2.pem # -o secure_images/ecdsa192_public_key_digest_v2.bin self.espefuse_py( - "burn_key BLOCK_KEY0 " + "burn-key BLOCK_KEY0 " f"{S_IMAGES_DIR}/ecdsa192_public_key_digest_v2.bin SECURE_BOOT_DIGEST" ) - output = self.espefuse_py("summary -d") + output = self.espefuse_py("-d summary") assert ( " = 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 " "1e 3d 15 16 96 ca 7f 22 a6 e8 8b d5 27 a0 3b 3b R/-" ) in output def test_burn_key_from_digest2(self): - # python espsecure.py digest_sbv2_public_key --keyfile + # python espsecure digest_sbv2_public_key --keyfile # secure_images/ecdsa256_secure_boot_signing_key_v2.pem # -o secure_images/ecdsa256_public_key_digest_v2.bin self.espefuse_py( - "burn_key BLOCK_KEY0 " + "burn-key BLOCK_KEY0 " f"{S_IMAGES_DIR}/ecdsa256_public_key_digest_v2.bin SECURE_BOOT_DIGEST" ) - output = self.espefuse_py("summary -d") + output = self.espefuse_py("-d summary") assert ( " = 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 " "bf 0f 6a f6 8b d3 6d 8b 53 b3 da a9 33 f6 0a 04 R/-" @@ -1597,34 +1605,30 @@ def test_burn_key_from_digest2(self): not in [ "esp32s2", "esp32s3", - "esp32s3beta1", "esp32c3", - "esp32h2beta1", "esp32c6", "esp32h2", "esp32p4", "esp32c5", - "esp32c5beta3", "esp32c61", ], reason="Supports 6 key blocks", ) class TestBurnKeyDigestCommands(EfuseTestCase): def test_burn_key_digest(self): - self.espefuse_py("burn_key_digest -h") + self.espefuse_py("burn-key-digest -h") self.espefuse_py( - f"burn_key_digest \ + f"burn-key-digest \ BLOCK_KEY0 \ {S_IMAGES_DIR}/rsa_secure_boot_signing_key.pem SECURE_BOOT_DIGEST0 \ BLOCK_KEY1 \ {S_IMAGES_DIR}/rsa_secure_boot_signing_key2.pem SECURE_BOOT_DIGEST1 \ BLOCK_KEY2 ", - check_msg="A fatal error occurred: The number of blocks (3), " - "datafile (2) and keypurpose (2) should be the same.", + check_msg="Expected multiple of 3 values, got 7", ret_code=2, ) self.espefuse_py( - f"burn_key_digest \ + f"burn-key-digest \ BLOCK_KEY0 \ {S_IMAGES_DIR}/rsa_secure_boot_signing_key.pem SECURE_BOOT_DIGEST0 \ BLOCK_KEY1 \ @@ -1632,7 +1636,7 @@ def test_burn_key_digest(self): BLOCK_KEY2 \ {S_IMAGES_DIR}/rsa_secure_boot_signing_key2.pem SECURE_BOOT_DIGEST2" ) - output = self.espefuse_py("summary -d") + output = self.espefuse_py("-d summary") assert 1 == output.count( " = 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 R/-" @@ -1643,25 +1647,25 @@ def test_burn_key_digest(self): ) def test_burn_key_from_digest(self): - # python espsecure.py digest_rsa_public_key + # python espsecure digest_rsa_public_key # --keyfile test/secure_images/rsa_secure_boot_signing_key.pem # -o secure_images/rsa_public_key_digest.bin self.espefuse_py( - f"burn_key \ + f"burn-key \ BLOCK_KEY0 {S_IMAGES_DIR}/rsa_public_key_digest.bin SECURE_BOOT_DIGEST0" ) - output = self.espefuse_py("summary -d") + output = self.espefuse_py("-d summary") assert 1 == output.count( " = 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 R/-" ) self.espefuse_py( - f"burn_key_digest \ + f"burn-key-digest \ BLOCK_KEY1 \ {S_IMAGES_DIR}/rsa_secure_boot_signing_key.pem SECURE_BOOT_DIGEST1" ) - output = self.espefuse_py("summary -d") + output = self.espefuse_py("-d summary") assert 2 == output.count( " = 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 R/-" @@ -1671,8 +1675,8 @@ def test_burn_key_from_digest(self): class TestBurnBitCommands(EfuseTestCase): @pytest.mark.skipif(arg_chip != "esp32", reason="ESP32-only") def test_burn_bit_for_chips_with_3_key_blocks(self): - self.espefuse_py("burn_bit -h") - self.espefuse_py("burn_bit BLOCK3 0 1 2 4 8 16 32 64 96 128 160 192 224 255") + self.espefuse_py("burn-bit -h") + self.espefuse_py("burn-bit BLOCK3 0 1 2 4 8 16 32 64 96 128 160 192 224 255") self.espefuse_py( "summary", check_msg="17 01 01 00 01 00 00 00 01 00 00 00 01 00 00 " @@ -1680,7 +1684,7 @@ def test_burn_bit_for_chips_with_3_key_blocks(self): ) self.espefuse_py( - "burn_bit BLOCK3 3 5 6 7 9 10 11 12 13 14 15 31 63 95 127 159 191 223 254" + "burn-bit BLOCK3 3 5 6 7 9 10 11 12 13 14 15 31 63 95 127 159 191 223 254" ) self.espefuse_py( "summary", @@ -1690,21 +1694,21 @@ def test_burn_bit_for_chips_with_3_key_blocks(self): @pytest.mark.skipif(arg_chip != "esp32c2", reason="ESP32-C2-only") def test_burn_bit_for_chips_with_1_key_block(self): - self.espefuse_py("burn_bit -h") - self.espefuse_py("burn_bit BLOCK3 0 1 2 4 8 16 32 64 96 128 160 192 224 255") + self.espefuse_py("burn-bit -h") + self.espefuse_py("burn-bit BLOCK3 0 1 2 4 8 16 32 64 96 128 160 192 224 255") self.espefuse_py( "summary", check_msg="17 01 01 00 01 00 00 00 01 00 00 00 01 00 " "00 00 01 00 00 00 01 00 00 00 01 00 00 00 01 00 00 80", ) self.espefuse_py( - "burn_bit BLOCK3 100", + "burn-bit BLOCK3 100", check_msg="Burn into BLOCK_KEY0 is forbidden " "(RS coding scheme does not allow this)", ret_code=2, ) - self.espefuse_py("burn_bit BLOCK0 0 1 2") + self.espefuse_py("burn-bit BLOCK0 0 1 2") self.espefuse_py("summary", check_msg="[0 ] read_regs: 00000007 00000000") @pytest.mark.skipif( @@ -1712,41 +1716,38 @@ def test_burn_bit_for_chips_with_1_key_block(self): not in [ "esp32s2", "esp32s3", - "esp32s3beta1", "esp32c3", - "esp32h2beta1", "esp32c6", "esp32h2", "esp32p4", "esp32c5", - "esp32c5beta3", "esp32c61", ], reason="Only chip with 6 keys", ) def test_burn_bit_for_chips_with_6_key_blocks(self): - self.espefuse_py("burn_bit -h") - self.espefuse_py("burn_bit BLOCK3 0 1 2 4 8 16 32 64 96 128 160 192 224 255") + self.espefuse_py("burn-bit -h") + self.espefuse_py("burn-bit BLOCK3 0 1 2 4 8 16 32 64 96 128 160 192 224 255") self.espefuse_py( "summary", check_msg="17 01 01 00 01 00 00 00 01 00 00 00 01 00 " "00 00 01 00 00 00 01 00 00 00 01 00 00 00 01 00 00 80", ) self.espefuse_py( - "burn_bit BLOCK3 100", + "burn-bit BLOCK3 100", check_msg="Burn into BLOCK_USR_DATA is forbidden " "(RS coding scheme does not allow this)", ret_code=2, ) - self.espefuse_py("burn_bit BLOCK0 13") + self.espefuse_py("burn-bit BLOCK0 13") self.espefuse_py( "summary", check_msg="[0 ] read_regs: 00002000 00000000 00000000 " "00000000 00000000 00000000", ) - self.espefuse_py("burn_bit BLOCK0 24") + self.espefuse_py("burn-bit BLOCK0 24") self.espefuse_py( "summary", check_msg="[0 ] read_regs: 01002000 00000000 00000000 " @@ -1758,14 +1759,14 @@ def test_burn_bit_for_chips_with_6_key_blocks(self): ) def test_burn_bit_with_34_coding_scheme(self): self._set_34_coding_scheme() - self.espefuse_py("burn_bit BLOCK3 0 1 2 4 8 16 32 64 96 128 160 191") + self.espefuse_py("burn-bit BLOCK3 0 1 2 4 8 16 32 64 96 128 160 191") self.espefuse_py( "summary", check_msg="17 01 01 00 01 00 00 00 01 00 00 00 01 00 " "00 00 01 00 00 00 01 00 00 80", ) self.espefuse_py( - "burn_bit BLOCK3 17", + "burn-bit BLOCK3 17", check_msg="Burn into BLOCK3 is forbidden " "(3/4 coding scheme does not allow this).", ret_code=2, @@ -1774,7 +1775,7 @@ def test_burn_bit_with_34_coding_scheme(self): @pytest.mark.skipif(arg_chip != "esp32", reason="ESP32-only") def test_burn_bit_with_none_recovery_coding_scheme(self): self._set_none_recovery_coding_scheme() - self.espefuse_py("burn_bit BLOCK3 0 1 2 4 8 16 32 64 96 128 160 192 224 255") + self.espefuse_py("burn-bit BLOCK3 0 1 2 4 8 16 32 64 96 128 160 192 224 255") self.espefuse_py( "summary", check_msg="17 01 01 00 01 00 00 00 01 00 00 00 01 00 00 " @@ -1789,11 +1790,11 @@ class TestByteOrderBurnKeyCommand(EfuseTestCase): def test_1_secure_boot_v1(self): if arg_chip == "esp32": self.espefuse_py( - f"burn_key \ + f"burn-key \ flash_encryption {IMAGES_DIR}/256bit \ secure_boot_v1 {IMAGES_DIR}/256bit_1 --no-protect-key" ) - output = self.espefuse_py("summary -d") + output = self.espefuse_py("-d summary") self.check_data_block_in_log( output, f"{IMAGES_DIR}/256bit", reverse_order=True ) @@ -1802,11 +1803,11 @@ def test_1_secure_boot_v1(self): ) self.espefuse_py( - f"burn_key \ + f"burn-key \ flash_encryption {IMAGES_DIR}/256bit \ secure_boot_v1 {IMAGES_DIR}/256bit_1" ) - output = self.espefuse_py("summary -d") + output = self.espefuse_py("-d summary") assert ( "[1 ] read_regs: 00000000 00000000 00000000 00000000 " "00000000 00000000 00000000 00000000" @@ -1823,11 +1824,11 @@ def test_1_secure_boot_v1(self): def test_2_secure_boot_v1(self): if arg_chip == "esp32": self.espefuse_py( - f"burn_key \ + f"burn-key \ flash_encryption {IMAGES_DIR}/256bit \ secure_boot_v2 {IMAGES_DIR}/256bit_1 --no-protect-key" ) - output = self.espefuse_py("summary -d") + output = self.espefuse_py("-d summary") self.check_data_block_in_log( output, f"{IMAGES_DIR}/256bit", reverse_order=True ) @@ -1836,11 +1837,11 @@ def test_2_secure_boot_v1(self): ) self.espefuse_py( - f"burn_key \ + f"burn-key \ flash_encryption {IMAGES_DIR}/256bit \ secure_boot_v2 {IMAGES_DIR}/256bit_1" ) - output = self.espefuse_py("summary -d") + output = self.espefuse_py("-d summary") assert ( "[1 ] read_regs: 00000000 00000000 00000000 00000000 " "00000000 00000000 00000000 00000000" @@ -1850,121 +1851,173 @@ def test_2_secure_boot_v1(self): ) -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 + + 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 != "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" + ) - @classmethod - def teardown_class(self): - # Restore the stored working directory - os.chdir(self.stored_dir) + 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() + ) - @pytest.mark.skipif( - arg_chip in ["esp32c2", "esp32p4"], - 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") + 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 ( + 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 - @pytest.mark.skipif( - arg_chip in ["esp32c2", "esp32p4"], - 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") - - 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("summary -d") - if arg_chip in ["esp32", "esp32c2"]: assert ( - "[3 ] read_regs: e00007ff 00000000 00000000 00000000 " - "00000000 00000000 00000000 00000000" - ) in output - else: + 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 ( - "[8 ] read_regs: e00007ff 00000000 00000000 00000000 " - "00000000 00000000 00000000 00000000" - ) in output + espefuse.efuses["BLOCK1"].is_readable() + and espefuse.efuses["BLOCK1"].is_writeable() + ) - 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("summary -d") - if arg_chip in ["esp32", "esp32c2"]: assert ( - "[2 ] 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 ( - "[3 ] read_regs: ffffffff 00000000 00000000 00000000 " - "00000000 00000000 00000000 00000000" - ) in output - else: + espefuse.efuses["CUSTOM_MAC"].get_meaning() + == "aa:cd:ef:11:22:33 (CRC 0x63 OK)" + ) assert ( - "[7 ] read_regs: 10000000 00000000 00000000 00000000 " - "00000000 00000000 00000000 00000000" - ) in output + 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 ( - "[8 ] read_regs: ffffffff 00000000 00000000 00000000 " - "00000000 00000000 00000000 00000000" - ) in output + 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): def test_multiple_cmds_help(self): if arg_chip == "esp32c2": command1 = ( - f"burn_key_digest {S_IMAGES_DIR}" + f"burn-key-digest {S_IMAGES_DIR}" "/ecdsa256_secure_boot_signing_key_v2.pem" ) command2 = ( - f"burn_key BLOCK_KEY0 {IMAGES_DIR}/128bit_key " + f"burn-key BLOCK_KEY0 {IMAGES_DIR}/128bit_key " "XTS_AES_128_KEY_DERIVED_FROM_128_EFUSE_BITS" ) elif arg_chip == "esp32": - command1 = f"burn_key_digest {S_IMAGES_DIR}/rsa_secure_boot_signing_key.pem" - command2 = f"burn_key flash_encryption {IMAGES_DIR}/256bit" + command1 = f"burn-key-digest {S_IMAGES_DIR}/rsa_secure_boot_signing_key.pem" + command2 = f"burn-key flash-encryption {IMAGES_DIR}/256bit" else: - command1 = f"burn_key_digest BLOCK_KEY0 \ + command1 = f"burn-key-digest BLOCK_KEY0 \ {S_IMAGES_DIR}/rsa_secure_boot_signing_key.pem SECURE_BOOT_DIGEST0" - command2 = f"burn_key BLOCK_KEY0 \ + command2 = f"burn-key BLOCK_KEY0 \ {S_IMAGES_DIR}/rsa_public_key_digest.bin SECURE_BOOT_DIGEST0" self.espefuse_py( f"-h {command1} {command2}", - check_msg="usage: __main__.py [-h]", + check_msg=f"Usage: {ESPEFUSE_MODNAME}", ) self.espefuse_py( f"{command1} -h {command2}", - check_msg="usage: __main__.py burn_key_digest [-h]", + check_msg=f"Usage: {ESPEFUSE_MODNAME} burn-key-digest [OPTIONS] " + + ( + "KEYFILE" + if arg_chip in ["esp32", "esp32c2"] + else "[ ] ..." + ), ) self.espefuse_py( f"{command1} {command2} -h", - check_msg="usage: __main__.py burn_key [-h]", + check_msg=f"Usage: {ESPEFUSE_MODNAME} burn-key [OPTIONS] " + + ( + "[ ] ..." + if arg_chip == "esp32" + else "[ ] ..." + ), ) @pytest.mark.skipif( @@ -1972,12 +2025,12 @@ def test_multiple_cmds_help(self): ) def test_1_esp32c2(self): self.espefuse_py( - f"burn_key_digest {S_IMAGES_DIR}/ecdsa256_secure_boot_signing_key_v2.pem \ - burn_key BLOCK_KEY0 {IMAGES_DIR}/128bit_key \ + f"burn-key-digest {S_IMAGES_DIR}/ecdsa256_secure_boot_signing_key_v2.pem \ + burn-key BLOCK_KEY0 {IMAGES_DIR}/128bit_key \ XTS_AES_128_KEY_DERIVED_FROM_128_EFUSE_BITS --no-read-protect \ summary" ) - output = self.espefuse_py("summary -d") + output = self.espefuse_py("-d summary") assert ( "[3 ] read_regs: 0c0d0e0f 08090a0b 04050607 00010203 " "f66a0fbf 8b6dd38b a9dab353 040af633" @@ -1990,12 +2043,12 @@ def test_1_esp32c2(self): ) def test_2_esp32c2(self): self.espefuse_py( - f"burn_key_digest {S_IMAGES_DIR}/ecdsa256_secure_boot_signing_key_v2.pem \ - burn_key BLOCK_KEY0 \ + f"burn-key-digest {S_IMAGES_DIR}/ecdsa256_secure_boot_signing_key_v2.pem \ + burn-key BLOCK_KEY0 \ {IMAGES_DIR}/128bit_key XTS_AES_128_KEY_DERIVED_FROM_128_EFUSE_BITS \ summary" ) - output = self.espefuse_py("summary -d") + output = self.espefuse_py("-d summary") assert ( "[3 ] read_regs: 00000000 00000000 00000000 00000000 " "f66a0fbf 8b6dd38b a9dab353 040af633" @@ -2007,22 +2060,44 @@ def test_burn_bit(self): if arg_chip == "esp32": self._set_34_coding_scheme() self.espefuse_py( + "burn-bit BLOCK2 0 1 2 3 \ + burn-bit BLOCK2 4 5 6 7 \ + burn-bit BLOCK2 8 9 10 11 \ + burn-bit BLOCK2 12 13 14 15 \ + summary" + ) + output = self.espefuse_py("-d summary") + assert "[2 ] read_regs: 0000ffff 00000000" in output + + @pytest.mark.skipif( + arg_chip != "esp32", reason="We don't need to test this on all chips." + ) + def test_burn_bit_deprecated_commands(self): + """Run deprecated commands and check that the new ones are used instead. + Make sure that this works with command chaining and nargs=-1.""" + if arg_chip == "esp32": + self._set_34_coding_scheme() + output = self.espefuse_py( "burn_bit BLOCK2 0 1 2 3 \ burn_bit BLOCK2 4 5 6 7 \ burn_bit BLOCK2 8 9 10 11 \ burn_bit BLOCK2 12 13 14 15 \ summary" ) - output = self.espefuse_py("summary -d") + assert ( + "Warning: Deprecated: Command 'burn_bit' is deprecated. " + "Use 'burn-bit' instead." in output + ) + output = self.espefuse_py("-d summary") assert "[2 ] read_regs: 0000ffff 00000000" in output def test_not_burn_cmds(self): self.espefuse_py( "summary \ dump \ - get_custom_mac \ - adc_info \ - check_error" + get-custom-mac \ + adc-info \ + check-error" ) @@ -2033,7 +2108,7 @@ def test_not_burn_cmds(self): class TestKeyPurposes(EfuseTestCase): def test_burn_xts_aes_key_purpose(self): self.espefuse_py( - "burn_efuse KEY_PURPOSE_5 XTS_AES_128_KEY", + "burn-efuse KEY_PURPOSE_5 XTS_AES_128_KEY", check_msg="A fatal error occurred: " "KEY_PURPOSE_5 can not have XTS_AES_128_KEY " "key due to a hardware bug (please see TRM for more details)", @@ -2045,7 +2120,7 @@ def test_burn_xts_aes_key_purpose(self): ) def test_burn_ecdsa_key_purpose(self): self.espefuse_py( - "burn_efuse KEY_PURPOSE_5 ECDSA_KEY", + "burn-efuse KEY_PURPOSE_5 ECDSA_KEY", check_msg="A fatal error occurred: " "KEY_PURPOSE_5 can not have ECDSA_KEY " "key due to a hardware bug (please see TRM for more details)", @@ -2054,7 +2129,7 @@ def test_burn_ecdsa_key_purpose(self): def test_burn_xts_aes_key(self): self.espefuse_py( - f"burn_key \ + f"burn-key \ BLOCK_KEY5 {IMAGES_DIR}/256bit XTS_AES_128_KEY", check_msg="A fatal error occurred: " "KEY_PURPOSE_5 can not have XTS_AES_128_KEY " @@ -2067,7 +2142,7 @@ def test_burn_xts_aes_key(self): ) def test_burn_ecdsa_key(self): self.espefuse_py( - f"burn_key \ + f"burn-key \ BLOCK_KEY5 {S_IMAGES_DIR}/ecdsa192_secure_boot_signing_key_v2.pem \ ECDSA_KEY", check_msg="A fatal error occurred: " @@ -2081,24 +2156,24 @@ 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" + 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 \ + 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" + 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 postponed eFuses from BLOCK0" in output assert "BURN BLOCK0 - OK" in output assert "Successful" in output @@ -2118,7 +2193,7 @@ def test_extend_efuse_table_with_csv_file(self): assert "ID_NUMK_1 (BLOCK3)" in output self.espefuse_py( - f"--extend-efuse-table {csv_file} burn_efuse \ + f"--extend-efuse-table {csv_file} burn-efuse \ MODULE_VERSION 1 \ CUSTOM_SECURE_VERSION 4 \ SETTING_1_ALT_NAME 7 \ diff --git a/tools/esptool_py/test/test_espsecure.py b/tools/esptool_py/test/test_espsecure.py index 6fc2cb3197..d9e224afc5 100755 --- a/tools/esptool_py/test/test_espsecure.py +++ b/tools/esptool_py/test/test_espsecure.py @@ -1,4 +1,4 @@ -# Tests for espsecure.py using the pytest framework +# Tests for espsecure using the pytest framework # # Assumes openssl binary is in the PATH @@ -9,7 +9,6 @@ import subprocess import sys import tempfile -from collections import namedtuple from conftest import need_to_install_package_err @@ -28,10 +27,10 @@ class EspSecureTestCase: def run_espsecure(self, args): """ - Run espsecure.py with the specified arguments + Run espsecure with the specified arguments Returns output as a string if there is any, - raises an exception if espsecure.py fails + raises an exception if espsecure fails """ cmd = [sys.executable, "-m", "espsecure"] + args.split(" ") print("\nExecuting {}...".format(" ".join(cmd))) @@ -64,21 +63,16 @@ def _open(self, image_file): class TestESP32SecureBootloader(EspSecureTestCase): def test_digest_bootloader(self): - DBArgs = namedtuple( - "digest_bootloader_args", ["keyfile", "output", "iv", "image"] - ) - try: output_file = tempfile.NamedTemporaryFile(delete=False) output_file.close() - args = DBArgs( + espsecure.digest_secure_bootloader( self._open("256bit_key.bin"), output_file.name, self._open("256bit_iv.bin"), self._open("bootloader.bin"), ) - espsecure.digest_secure_bootloader(args) with open(output_file.name, "rb") as of: with self._open("bootloader_digested.bin") as ef: @@ -87,16 +81,19 @@ def test_digest_bootloader(self): os.unlink(output_file.name) def test_digest_rsa_public_key(self): - DigestRSAArgs = namedtuple("digest_rsa_public_key_args", ["keyfile", "output"]) - try: output_file = tempfile.NamedTemporaryFile(delete=False) output_file.close() - args = DigestRSAArgs( - self._open("rsa_secure_boot_signing_key.pem"), output_file.name + out = self.run_espsecure( + "digest-rsa-public-key --keyfile " + "secure_images/rsa_secure_boot_signing_key.pem " + f"-o {output_file.name}" + ) + assert ( + "DeprecationWarning: The command 'digest-rsa-public-key' is deprecated." + in out ) - espsecure.digest_rsa_public_key(args) with open(output_file.name, "rb") as of: with self._open("rsa_public_key_digest.bin") as ef: @@ -106,44 +103,19 @@ def test_digest_rsa_public_key(self): class TestSigning(EspSecureTestCase): - VerifyArgs = namedtuple( - "verify_signature_args", ["version", "hsm", "hsm_config", "keyfile", "datafile"] - ) - - SignArgs = namedtuple( - "sign_data_args", - [ - "version", - "keyfile", - "output", - "append_signatures", - "hsm", - "hsm_config", - "pub_key", - "signature", - "datafile", - ], - ) - - ExtractKeyArgs = namedtuple( - "extract_public_key_args", ["version", "keyfile", "public_keyfile"] - ) - - GenerateKeyArgs = namedtuple("generate_key_args", ["version", "scheme", "keyfile"]) - def test_key_generation_v1(self): with tempfile.TemporaryDirectory() as keydir: # keyfile cannot exist before generation -> tempfile.NamedTemporaryFile() # cannot be used for keyfile keyfile_name = os.path.join(keydir, "key.pem") - self.run_espsecure(f"generate_signing_key --version 1 {keyfile_name}") + self.run_espsecure(f"generate-signing-key --version 1 {keyfile_name}") def test_key_generation_v2(self): with tempfile.TemporaryDirectory() as keydir: # keyfile cannot exist before generation -> tempfile.NamedTemporaryFile() # cannot be used for keyfile keyfile_name = os.path.join(keydir, "key.pem") - self.run_espsecure(f"generate_signing_key --version 2 {keyfile_name}") + self.run_espsecure(f"generate-signing-key --version 2 {keyfile_name}") def _test_sign_v1_data(self, key_name): try: @@ -152,7 +124,7 @@ def _test_sign_v1_data(self, key_name): # Note: signing bootloader is not actually needed # for ESP32, it's just a handy file to sign - args = self.SignArgs( + espsecure.sign_data( "1", [self._open(key_name)], output_file.name, @@ -163,7 +135,6 @@ def _test_sign_v1_data(self, key_name): None, self._open("bootloader.bin"), ) - espsecure.sign_data(args) with open(output_file.name, "rb") as of: with self._open("bootloader_signed.bin") as ef: @@ -173,19 +144,19 @@ def _test_sign_v1_data(self, key_name): os.unlink(output_file.name) def test_sign_v1_data(self): - self._test_sign_v1_data("ecdsa_secure_boot_signing_key.pem") + self._test_sign_v1_data("ecdsa256_secure_boot_signing_key.pem") def test_sign_v1_data_pkcs8(self): - self._test_sign_v1_data("ecdsa_secure_boot_signing_key_pkcs8.pem") + self._test_sign_v1_data("ecdsa256_secure_boot_signing_key_pkcs8.pem") def test_sign_v1_with_pre_calculated_signature(self): # Sign using pre-calculated signature + Verify - signing_pubkey = "ecdsa_secure_boot_signing_pubkey.pem" + signing_pubkey = "ecdsa256_secure_boot_signing_pubkey.pem" pre_calculated_signature = "pre_calculated_bootloader_signature.bin" try: output_file = tempfile.NamedTemporaryFile(delete=False) - args = self.SignArgs( + espsecure.sign_data( "1", None, output_file.name, @@ -196,50 +167,41 @@ def test_sign_v1_with_pre_calculated_signature(self): [self._open(pre_calculated_signature)], self._open("bootloader.bin"), ) - espsecure.sign_data(args) - args = self.VerifyArgs( + espsecure.verify_signature( "1", False, None, self._open(signing_pubkey), output_file ) - espsecure.verify_signature(args) finally: output_file.close() os.unlink(output_file.name) - def test_sign_v2_data(self): - signing_keys = [ - "rsa_secure_boot_signing_key.pem", - "ecdsa192_secure_boot_signing_key.pem", - "ecdsa_secure_boot_signing_key.pem", - "ecdsa384_secure_boot_signing_key.pem", - ] - for key in signing_keys: - try: - output_file = tempfile.NamedTemporaryFile(delete=False) - args = self.SignArgs( - "2", - [self._open(key)], - output_file.name, - False, - False, - None, - None, - None, - self._open("bootloader_unsigned_v2.bin"), - ) - espsecure.sign_data(args) + @pytest.mark.parametrize("scheme", ["rsa", "ecdsa192", "ecdsa256", "ecdsa384"]) + def test_sign_v2_data(self, scheme): + key = f"{scheme}_secure_boot_signing_key.pem" + try: + output_file = tempfile.NamedTemporaryFile(delete=False) + espsecure.sign_data( + "2", + [self._open(key)], + output_file.name, + False, + False, + None, + None, + None, + self._open("bootloader_unsigned_v2.bin"), + ) - args = self.VerifyArgs("2", False, None, self._open(key), output_file) - espsecure.verify_signature(args) - finally: - output_file.close() - os.unlink(output_file.name) + espsecure.verify_signature("2", False, None, self._open(key), output_file) + finally: + output_file.close() + os.unlink(output_file.name) def test_sign_v2_multiple_keys(self): # 3 keys + Verify with 3rd key try: output_file = tempfile.NamedTemporaryFile(delete=False) - args = self.SignArgs( + espsecure.sign_data( "2", [ self._open("rsa_secure_boot_signing_key.pem"), @@ -254,46 +216,43 @@ def test_sign_v2_multiple_keys(self): None, self._open("bootloader_unsigned_v2.bin"), ) - espsecure.sign_data(args) - args = self.VerifyArgs( + espsecure.verify_signature( "2", False, None, self._open("rsa_secure_boot_signing_key3.pem"), output_file, ) - espsecure.verify_signature(args) output_file.seek(0) - args = self.VerifyArgs( + espsecure.verify_signature( "2", False, None, self._open("rsa_secure_boot_signing_key2.pem"), output_file, ) - espsecure.verify_signature(args) output_file.seek(0) - args = self.VerifyArgs( + espsecure.verify_signature( "2", False, None, self._open("rsa_secure_boot_signing_key.pem"), output_file, ) - espsecure.verify_signature(args) finally: output_file.close() os.unlink(output_file.name) def test_sign_v2_append_signatures(self): # Append signatures + Verify with an appended key - # (bootloader_signed_v2.bin already signed with rsa_secure_boot_signing_key.pem) + # (bootloader_signed_v2_rsa.bin already signed with + # rsa_secure_boot_signing_key.pem) try: output_file = tempfile.NamedTemporaryFile(delete=False) - args = self.SignArgs( + espsecure.sign_data( "2", [ self._open("rsa_secure_boot_signing_key2.pem"), @@ -305,38 +264,34 @@ def test_sign_v2_append_signatures(self): None, None, None, - self._open("bootloader_signed_v2.bin"), + self._open("bootloader_signed_v2_rsa.bin"), ) - espsecure.sign_data(args) - args = self.VerifyArgs( + espsecure.verify_signature( "2", False, None, self._open("rsa_secure_boot_signing_key.pem"), output_file, ) - espsecure.verify_signature(args) output_file.seek(0) - args = self.VerifyArgs( + espsecure.verify_signature( "2", False, None, self._open("rsa_secure_boot_signing_key2.pem"), output_file, ) - espsecure.verify_signature(args) output_file.seek(0) - args = self.VerifyArgs( + espsecure.verify_signature( "2", False, None, self._open("rsa_secure_boot_signing_key3.pem"), output_file, ) - espsecure.verify_signature(args) finally: output_file.close() os.unlink(output_file.name) @@ -346,7 +301,7 @@ def test_sign_v2_append_signatures_multiple_steps(self): try: output_file1 = tempfile.NamedTemporaryFile(delete=False) output_file2 = tempfile.NamedTemporaryFile(delete=False) - args = self.SignArgs( + espsecure.sign_data( "2", [self._open("rsa_secure_boot_signing_key2.pem")], output_file1.name, @@ -355,11 +310,10 @@ def test_sign_v2_append_signatures_multiple_steps(self): None, None, None, - self._open("bootloader_signed_v2.bin"), + self._open("bootloader_signed_v2_rsa.bin"), ) - espsecure.sign_data(args) - args = self.SignArgs( + espsecure.sign_data( "2", [self._open("rsa_secure_boot_signing_key3.pem")], output_file2.name, @@ -370,79 +324,63 @@ def test_sign_v2_append_signatures_multiple_steps(self): None, output_file1, ) - espsecure.sign_data(args) - args = self.VerifyArgs( + espsecure.verify_signature( "2", False, None, self._open("rsa_secure_boot_signing_key.pem"), output_file2, ) - espsecure.verify_signature(args) output_file2.seek(0) - args = self.VerifyArgs( + espsecure.verify_signature( "2", False, None, self._open("rsa_secure_boot_signing_key2.pem"), output_file2, ) - espsecure.verify_signature(args) output_file2.seek(0) - args = self.VerifyArgs( + espsecure.verify_signature( "2", False, None, self._open("rsa_secure_boot_signing_key3.pem"), output_file2, ) - espsecure.verify_signature(args) finally: output_file1.close() os.unlink(output_file1.name) output_file2.close() os.unlink(output_file2.name) - def test_sign_v2_with_pre_calculated_signature(self): + @pytest.mark.parametrize("scheme", ["rsa", "ecdsa192", "ecdsa256", "ecdsa384"]) + def test_sign_v2_with_pre_calculated_signature(self, scheme): # Sign using pre-calculated signature + Verify - signing_keys = [ - "rsa_secure_boot_signing_pubkey.pem", - "ecdsa192_secure_boot_signing_pubkey.pem", - "ecdsa_secure_boot_signing_pubkey.pem", - "ecdsa384_secure_boot_signing_pubkey.pem", - ] - pre_calculated_signatures = [ - "pre_calculated_bootloader_signature_rsa.bin", - "pre_calculated_bootloader_signature_ecdsa192.bin", - "pre_calculated_bootloader_signature_ecdsa256.bin", - "pre_calculated_bootloader_signature_ecdsa384.bin", - ] - for pub_key, signature in zip(signing_keys, pre_calculated_signatures): - try: - output_file = tempfile.NamedTemporaryFile(delete=False) - args = self.SignArgs( - "2", - None, - output_file.name, - False, - False, - None, - [self._open(pub_key)], - [self._open(signature)], - self._open("bootloader_unsigned_v2.bin"), - ) - espsecure.sign_data(args) + pub_key = f"{scheme}_secure_boot_signing_pubkey.pem" + signature = f"pre_calculated_bootloader_signature_{scheme}.bin" + try: + output_file = tempfile.NamedTemporaryFile(delete=False) + espsecure.sign_data( + "2", + None, + output_file.name, + False, + False, + None, + [self._open(pub_key)], + [self._open(signature)], + self._open("bootloader_unsigned_v2.bin"), + ) - args = self.VerifyArgs( - "2", False, None, self._open(pub_key), output_file - ) - espsecure.verify_signature(args) - finally: - output_file.close() - os.unlink(output_file.name) + espsecure.verify_signature( + "2", False, None, self._open(pub_key), output_file + ) + finally: + output_file.close() + os.unlink(output_file.name) def test_sign_v2_with_multiple_pre_calculated_signatures(self): # Sign using multiple pre-calculated signatures + Verify @@ -458,7 +396,7 @@ def test_sign_v2_with_multiple_pre_calculated_signatures(self): ] try: output_file = tempfile.NamedTemporaryFile(delete=False) - args = self.SignArgs( + espsecure.sign_data( "2", None, output_file.name, @@ -469,349 +407,214 @@ def test_sign_v2_with_multiple_pre_calculated_signatures(self): [self._open(signature) for signature in pre_calculated_signatures], self._open("bootloader_unsigned_v2.bin"), ) - espsecure.sign_data(args) - args = self.VerifyArgs( + espsecure.verify_signature( "2", False, None, self._open(signing_pubkeys[0]), output_file ) - espsecure.verify_signature(args) finally: output_file.close() os.unlink(output_file.name) - def test_verify_signature_signing_key(self): - # correct key v1 - args = self.VerifyArgs( - "1", - False, - None, - self._open("ecdsa_secure_boot_signing_key.pem"), - self._open("bootloader_signed.bin"), - ) - espsecure.verify_signature(args) - - # correct key v2 - args = self.VerifyArgs( - "2", - False, - None, - self._open("rsa_secure_boot_signing_key.pem"), - self._open("bootloader_signed_v2.bin"), - ) - espsecure.verify_signature(args) - - # correct key v2 (ecdsa384) - args = self.VerifyArgs( - "2", - False, - None, - self._open("ecdsa384_secure_boot_signing_key.pem"), - self._open("bootloader_signed_v2_ecdsa384.bin"), - ) - espsecure.verify_signature(args) - - # correct key v2 (ecdsa256) - args = self.VerifyArgs( - "2", - False, - None, - self._open("ecdsa_secure_boot_signing_key.pem"), - self._open("bootloader_signed_v2_ecdsa256.bin"), - ) - espsecure.verify_signature(args) - - # correct key v2 (ecdsa192) - args = self.VerifyArgs( - "2", + @pytest.mark.parametrize( + "version, keyfile, datafile", + [ + ("1", "ecdsa256_secure_boot_signing_key.pem", "bootloader_signed.bin"), + ( + "1", + "ecdsa256_secure_boot_signing_pubkey_raw.bin", + "bootloader_signed.bin", + ), + ("2", "rsa_secure_boot_signing_key.pem", "bootloader_signed_v2_rsa.bin"), + ( + "2", + "ecdsa384_secure_boot_signing_key.pem", + "bootloader_signed_v2_ecdsa384.bin", + ), + ( + "2", + "ecdsa256_secure_boot_signing_key.pem", + "bootloader_signed_v2_ecdsa256.bin", + ), + ( + "2", + "ecdsa192_secure_boot_signing_key.pem", + "bootloader_signed_v2_ecdsa192.bin", + ), + ], + ids=[ + "v1_pem", + "v1_raw", + "v2_rsa", + "v2_ecdsa384", + "v2_ecdsa256", + "v2_ecdsa192", + ], + ) + def test_verify_signature_correct_key(self, version, keyfile, datafile): + espsecure.verify_signature( + version, False, None, - self._open("ecdsa192_secure_boot_signing_key.pem"), - self._open("bootloader_signed_v2_ecdsa192.bin"), + self._open(keyfile), + self._open(datafile), ) - espsecure.verify_signature(args) - # wrong key v1 - args = self.VerifyArgs( - "1", - False, - None, - self._open("ecdsa_secure_boot_signing_key2.pem"), - self._open("bootloader_signed.bin"), - ) + def test_verify_signature_wrong_key_v1(self): with pytest.raises(esptool.FatalError) as cm: - espsecure.verify_signature(args) + espsecure.verify_signature( + "1", + False, + None, + self._open("ecdsa256_secure_boot_signing_key2.pem"), + self._open("bootloader_signed.bin"), + ) assert "Signature is not valid" in str(cm.value) - # wrong key v2 - args = self.VerifyArgs( - "2", - False, - None, - self._open("rsa_secure_boot_signing_key2.pem"), - self._open("bootloader_signed_v2.bin"), - ) + @pytest.mark.parametrize("scheme", ["rsa", "ecdsa192", "ecdsa256", "ecdsa384"]) + def test_verify_signature_wrong_key_v2(self, scheme): with pytest.raises(esptool.FatalError) as cm: - espsecure.verify_signature(args) + espsecure.verify_signature( + "2", + False, + None, + self._open(f"{scheme}_secure_boot_signing_key2.pem"), + self._open(f"bootloader_signed_v2_{scheme}.bin"), + ) assert "Signature could not be verified with the provided key." in str(cm.value) - # right key, wrong scheme (ecdsa256, v2) - args = self.VerifyArgs( - "2", - False, - None, - self._open("ecdsa_secure_boot_signing_key.pem"), - self._open("bootloader_signed.bin"), - ) + def test_verify_signature_wrong_scheme(self): with pytest.raises(esptool.FatalError) as cm: - espsecure.verify_signature(args) + espsecure.verify_signature( + "2", + False, + None, + self._open("ecdsa256_secure_boot_signing_key.pem"), + self._open("bootloader_signed.bin"), + ) assert "Invalid datafile" in str(cm.value) - # wrong key v2 (ecdsa384) - args = self.VerifyArgs( - "2", - False, - None, - self._open("ecdsa384_secure_boot_signing_key2.pem"), - self._open("bootloader_signed_v2_ecdsa384.bin"), - ) + def test_verify_signature_multi_signed_wrong_key(self): with pytest.raises(esptool.FatalError) as cm: - espsecure.verify_signature(args) - assert "Signature could not be verified with the provided key." in str(cm.value) - - # wrong key v2 (ecdsa256) - args = self.VerifyArgs( - "2", - False, - None, - self._open("ecdsa_secure_boot_signing_key2.pem"), - self._open("bootloader_signed_v2_ecdsa256.bin"), - ) - with pytest.raises(esptool.FatalError) as cm: - espsecure.verify_signature(args) + espsecure.verify_signature( + "2", + False, + None, + self._open("rsa_secure_boot_signing_key4.pem"), + self._open("bootloader_multi_signed_v2.bin"), + ) assert "Signature could not be verified with the provided key." in str(cm.value) - # wrong key v2 (ecdsa192) - args = self.VerifyArgs( - "2", + @pytest.mark.parametrize( + "version, keyfile, datafile", + [ + ("1", "ecdsa256_secure_boot_signing_pubkey.pem", "bootloader_signed.bin"), + ("2", "rsa_secure_boot_signing_pubkey.pem", "bootloader_signed_v2_rsa.bin"), + ( + "2", + "ecdsa384_secure_boot_signing_pubkey.pem", + "bootloader_signed_v2_ecdsa384.bin", + ), + ( + "2", + "ecdsa256_secure_boot_signing_pubkey.pem", + "bootloader_signed_v2_ecdsa256.bin", + ), + ( + "2", + "ecdsa192_secure_boot_signing_pubkey.pem", + "bootloader_signed_v2_ecdsa192.bin", + ), + ], + ids=["v1", "v2_rsa", "v2_ecdsa384", "v2_ecdsa256", "v2_ecdsa192"], + ) + def test_verify_signature_correct_pubkey(self, version, keyfile, datafile): + espsecure.verify_signature( + version, False, None, - self._open("ecdsa192_secure_boot_signing_key2.pem"), - self._open("bootloader_signed_v2_ecdsa192.bin"), + self._open(keyfile), + self._open(datafile), ) - with pytest.raises(esptool.FatalError) as cm: - espsecure.verify_signature(args) - assert "Signature could not be verified with the provided key." in str(cm.value) - # multi-signed wrong key v2 - args = self.VerifyArgs( - "2", - False, - None, - self._open("rsa_secure_boot_signing_key4.pem"), - self._open("bootloader_multi_signed_v2.bin"), - ) + def test_verify_signature_wrong_pubkey_v1(self): with pytest.raises(esptool.FatalError) as cm: - espsecure.verify_signature(args) - assert "Signature could not be verified with the provided key." in str(cm.value) - - def test_verify_signature_public_key(self): - # correct key v1 - args = self.VerifyArgs( - "1", - False, - None, - self._open("ecdsa_secure_boot_signing_pubkey.pem"), - self._open("bootloader_signed.bin"), - ) - espsecure.verify_signature(args) - - # correct key v2 - args = self.VerifyArgs( - "2", - False, - None, - self._open("rsa_secure_boot_signing_pubkey.pem"), - self._open("bootloader_signed_v2.bin"), - ) - espsecure.verify_signature(args) - - # correct key v2 (ecdsa384) - args = self.VerifyArgs( - "2", - False, - None, - self._open("ecdsa384_secure_boot_signing_pubkey.pem"), - self._open("bootloader_signed_v2_ecdsa384.bin"), - ) - espsecure.verify_signature(args) - - # correct key v2 (ecdsa256) - args = self.VerifyArgs( - "2", - False, - None, - self._open("ecdsa_secure_boot_signing_pubkey.pem"), - self._open("bootloader_signed_v2_ecdsa256.bin"), - ) - espsecure.verify_signature(args) - - # correct key v2 (ecdsa192) - args = self.VerifyArgs( - "2", - False, - None, - self._open("ecdsa192_secure_boot_signing_pubkey.pem"), - self._open("bootloader_signed_v2_ecdsa192.bin"), - ) - espsecure.verify_signature(args) - - # wrong key v1 - args = self.VerifyArgs( - "1", - False, - None, - self._open("ecdsa_secure_boot_signing_pubkey2.pem"), - self._open("bootloader_signed.bin"), - ) - with pytest.raises(esptool.FatalError) as cm: - espsecure.verify_signature(args) + espsecure.verify_signature( + "1", + False, + None, + self._open("ecdsa256_secure_boot_signing_pubkey2.pem"), + self._open("bootloader_signed.bin"), + ) assert "Signature is not valid" in str(cm.value) - # wrong key v2 - args = self.VerifyArgs( - "2", - False, - None, - self._open("rsa_secure_boot_signing_pubkey2.pem"), - self._open("bootloader_signed_v2.bin"), - ) + @pytest.mark.parametrize("scheme", ["rsa", "ecdsa192", "ecdsa256", "ecdsa384"]) + def test_verify_signature_wrong_pubkey_v2(self, scheme): with pytest.raises(esptool.FatalError) as cm: - espsecure.verify_signature(args) - assert "Signature could not be verified with the provided key." in str(cm.value) - - # wrong key v2 (ecdsa384) - args = self.VerifyArgs( - "2", - False, - None, - self._open("ecdsa384_secure_boot_signing_pubkey2.pem"), - self._open("bootloader_signed_v2_ecdsa384.bin"), - ) - with pytest.raises(esptool.FatalError) as cm: - espsecure.verify_signature(args) - assert "Signature could not be verified with the provided key." in str(cm.value) - - # wrong key v2 (ecdsa256) - args = self.VerifyArgs( - "2", - False, - None, - self._open("ecdsa_secure_boot_signing_pubkey2.pem"), - self._open("bootloader_signed_v2_ecdsa256.bin"), - ) - with pytest.raises(esptool.FatalError) as cm: - espsecure.verify_signature(args) - assert "Signature could not be verified with the provided key." in str(cm.value) - - # wrong key v2 (ecdsa192) - args = self.VerifyArgs( - "2", - False, - None, - self._open("ecdsa192_secure_boot_signing_pubkey2.pem"), - self._open("bootloader_signed_v2_ecdsa192.bin"), - ) - with pytest.raises(esptool.FatalError) as cm: - espsecure.verify_signature(args) + espsecure.verify_signature( + "2", + False, + None, + self._open(f"{scheme}_secure_boot_signing_pubkey2.pem"), + self._open(f"bootloader_signed_v2_{scheme}.bin"), + ) assert "Signature could not be verified with the provided key." in str(cm.value) - # multi-signed wrong key v2 - args = self.VerifyArgs( - "2", - False, - None, - self._open("rsa_secure_boot_signing_pubkey4.pem"), - self._open("bootloader_multi_signed_v2.bin"), - ) + def test_verify_signature_multi_signed_wrong_pubkey(self): with pytest.raises(esptool.FatalError) as cm: - espsecure.verify_signature(args) + espsecure.verify_signature( + "2", + False, + None, + self._open("rsa_secure_boot_signing_pubkey4.pem"), + self._open("bootloader_multi_signed_v2.bin"), + ) assert "Signature could not be verified with the provided key." in str(cm.value) def test_extract_binary_public_key(self): - with tempfile.NamedTemporaryFile() as pub_keyfile, tempfile.NamedTemporaryFile() as pub_keyfile2: # noqa E501 - args = self.ExtractKeyArgs( - "1", self._open("ecdsa_secure_boot_signing_key.pem"), pub_keyfile + with ( + tempfile.NamedTemporaryFile() as pub_keyfile, + tempfile.NamedTemporaryFile() as pub_keyfile2, + ): + espsecure.extract_public_key( + "1", self._open("ecdsa256_secure_boot_signing_key.pem"), pub_keyfile ) - espsecure.extract_public_key(args) - args = self.ExtractKeyArgs( - "1", self._open("ecdsa_secure_boot_signing_key2.pem"), pub_keyfile2 + espsecure.extract_public_key( + "1", self._open("ecdsa256_secure_boot_signing_key2.pem"), pub_keyfile2 ) - espsecure.extract_public_key(args) pub_keyfile.seek(0) pub_keyfile2.seek(0) # use correct extracted public key to verify - args = self.VerifyArgs( + espsecure.verify_signature( "1", False, None, pub_keyfile, self._open("bootloader_signed.bin") ) - espsecure.verify_signature(args) # use wrong extracted public key to try and verify - args = self.VerifyArgs( - "1", False, None, pub_keyfile2, self._open("bootloader_signed.bin") - ) with pytest.raises(esptool.FatalError) as cm: - espsecure.verify_signature(args) + espsecure.verify_signature( + "1", False, None, pub_keyfile2, self._open("bootloader_signed.bin") + ) assert "Signature is not valid" in str(cm.value) - def test_generate_and_extract_key_v2(self): + @pytest.mark.parametrize("scheme", ["rsa3072", "ecdsa192", "ecdsa256", "ecdsa384"]) + def test_generate_and_extract_key_v2(self, scheme): with tempfile.TemporaryDirectory() as keydir: # keyfile cannot exist before generation -> tempfile.NamedTemporaryFile() # cannot be used for keyfile keyfile_name = os.path.join(keydir, "key.pem") - # We need to manually delete the keyfile as we are iterating over - # different schemes with the same keyfile so instead of using addCleanup, - # we remove it using os.remove at the end of each pass - for scheme in ["rsa3072", "ecdsa192", "ecdsa256", "ecdsa384"]: - args = self.GenerateKeyArgs("2", scheme, keyfile_name) - espsecure.generate_signing_key(args) + espsecure.generate_signing_key("2", scheme, keyfile_name) - with tempfile.NamedTemporaryFile() as pub_keyfile, open( - keyfile_name, "rb" - ) as keyfile: - args = self.ExtractKeyArgs("2", keyfile, pub_keyfile) - espsecure.extract_public_key(args) - os.remove(keyfile_name) + with ( + tempfile.NamedTemporaryFile() as pub_keyfile, + open(keyfile_name, "rb") as keyfile, + ): + espsecure.extract_public_key("2", keyfile, pub_keyfile) class TestFlashEncryption(EspSecureTestCase): - EncryptArgs = namedtuple( - "encrypt_flash_data_args", - [ - "keyfile", - "output", - "address", - "flash_crypt_conf", - "aes_xts", - "plaintext_file", - ], - ) - - DecryptArgs = namedtuple( - "decrypt_flash_data_args", - [ - "keyfile", - "output", - "address", - "flash_crypt_conf", - "aes_xts", - "encrypted_file", - ], - ) - def _test_encrypt_decrypt( self, input_plaintext, @@ -825,10 +628,9 @@ def _test_encrypt_decrypt( keyfile = self._open(key_path) ciphertext = io.BytesIO() - args = self.EncryptArgs( + espsecure.encrypt_flash_data( keyfile, ciphertext, offset, flash_crypt_conf, aes_xts, original_plaintext ) - espsecure.encrypt_flash_data(args) original_plaintext.seek(0) assert original_plaintext.read() != ciphertext.getvalue() @@ -838,10 +640,9 @@ def _test_encrypt_decrypt( ciphertext.seek(0) keyfile.seek(0) plaintext = io.BytesIO() - args = self.DecryptArgs( + espsecure.decrypt_flash_data( keyfile, plaintext, offset, flash_crypt_conf, aes_xts, ciphertext ) - espsecure.decrypt_flash_data(args) original_plaintext.seek(0) assert original_plaintext.read() == plaintext.getvalue() @@ -929,12 +730,10 @@ def test_padding(self): keyfile = self._open("256bit_key.bin") address = 0x1000 - encrypt_args_padded = self.EncryptArgs( + espsecure.encrypt_flash_data( keyfile, ciphertext_full_block, address, None, "aes_xts", plaintext_file ) - espsecure.encrypt_flash_data(encrypt_args_padded) - # Test with different number of bytes per encryption call # Final ciphertext should still be the same if padding is done correctly bytes_per_encrypt = [16, 32, 64, 128] @@ -950,7 +749,7 @@ def test_padding(self): # encrypt the whole plaintext a substring of b bytes at a time plaintext_sub = io.BytesIO(plaintext[offset : offset + b]) - encrypt_args = self.EncryptArgs( + espsecure.encrypt_flash_data( keyfile, ciphertext, address + offset, @@ -959,8 +758,6 @@ def test_padding(self): plaintext_sub, ) - espsecure.encrypt_flash_data(encrypt_args) - assert ciphertext_full_block.getvalue() == ciphertext.getvalue() @@ -970,8 +767,8 @@ def test_digest_private_key(self): outfile_name = f.name self.run_espsecure( - "digest_private_key " - "--keyfile secure_images/ecdsa_secure_boot_signing_key.pem " + "digest-private-key " + "--keyfile secure_images/ecdsa256_secure_boot_signing_key.pem " f"{outfile_name}" ) @@ -981,9 +778,9 @@ def test_digest_private_key(self): ) def test_digest_private_key_with_invalid_output(self, capsys): - fname = "secure_images/ecdsa_secure_boot_signing_key.pem" + fname = "secure_images/ecdsa256_secure_boot_signing_key.pem" with pytest.raises(subprocess.CalledProcessError): - self.run_espsecure(f"digest_private_key --keyfile {fname} {fname}") + self.run_espsecure(f"digest-private-key --keyfile {fname} {fname}") output = capsys.readouterr().out assert "should not be the same!" in output diff --git a/tools/esptool_py/test/test_espsecure_hsm.py b/tools/esptool_py/test/test_espsecure_hsm.py index 1f8c52672a..7b80e97151 100644 --- a/tools/esptool_py/test/test_espsecure_hsm.py +++ b/tools/esptool_py/test/test_espsecure_hsm.py @@ -1,4 +1,4 @@ -# Tests for espsecure.py (esp_hsm_sign.py) using the pytest framework +# Tests for espsecure (esp_hsm_sign.py) using the pytest framework # # Assumes openssl binary is in the PATH @@ -7,7 +7,6 @@ import os.path import sys import tempfile -from collections import namedtuple from conftest import need_to_install_package_err @@ -127,123 +126,121 @@ def softhsm_setup_token(self, filename, token_label): class TestSigning(EspSecureHSMTestCase): - VerifyArgs = namedtuple( - "verify_signature_args", ["version", "hsm", "hsm_config", "keyfile", "datafile"] - ) - - SignArgs = namedtuple( - "sign_data_args", - [ - "version", - "keyfile", - "output", - "append_signatures", - "hsm", - "hsm_config", - "pub_key", - "signature", - "datafile", - ], - ) - def test_sign_v2_hsm(self): # Sign using SoftHSMv2 + Verify self.softhsm_setup_token("softhsm_v2.ini", "softhsm-test-token") - with tempfile.NamedTemporaryFile() as output_file: - args = self.SignArgs( + with ( + tempfile.NamedTemporaryFile() as output_file, + open( + os.path.join(TEST_DIR, "secure_images", "softhsm_v2.ini"), "r" + ) as config_file, + ): + espsecure.sign_data( "2", None, output_file.name, False, True, - os.path.join(TEST_DIR, "secure_images", "softhsm_v2.ini"), - None, - None, + config_file, + [], + [], self._open("bootloader_unsigned_v2.bin"), ) - espsecure.sign_data(args) - - args = self.VerifyArgs( + config_file.seek(0) + espsecure.verify_signature( "2", True, - os.path.join(TEST_DIR, "secure_images", "softhsm_v2.ini"), + config_file, None, output_file, ) - espsecure.verify_signature(args) def test_sign_v2_hsm_append_signatures_multiple_steps(self): # Append signatures using HSM + Verify with an appended key self.softhsm_setup_token("softhsm_v2_1.ini", "softhsm-test-token-1") - with tempfile.NamedTemporaryFile() as output_file1: - args = self.SignArgs( + with ( + tempfile.NamedTemporaryFile() as output_file1, + open( + os.path.join(TEST_DIR, "secure_images", "softhsm_v2_1.ini"), "r" + ) as config_file1, + ): + espsecure.sign_data( "2", None, output_file1.name, True, True, - os.path.join(TEST_DIR, "secure_images", "softhsm_v2_1.ini"), - None, - None, + config_file1, + [], + [], self._open("bootloader_unsigned_v2.bin"), ) - espsecure.sign_data(args) self.softhsm_setup_token("softhsm_v2_2.ini", "softhsm-test-token-2") - with tempfile.NamedTemporaryFile() as output_file2: - args = self.SignArgs( + with ( + tempfile.NamedTemporaryFile() as output_file2, + open( + os.path.join(TEST_DIR, "secure_images", "softhsm_v2_2.ini"), "r" + ) as config_file2, + ): + espsecure.sign_data( "2", None, output_file2.name, True, True, - os.path.join(TEST_DIR, "secure_images", "softhsm_v2_2.ini"), - None, - None, + config_file2, + [], + [], self._open(output_file1.name), ) - espsecure.sign_data(args) self.softhsm_setup_token("softhsm_v2_3.ini", "softhsm-test-token-3") - with tempfile.NamedTemporaryFile() as output_file3: - args = self.SignArgs( + with ( + tempfile.NamedTemporaryFile() as output_file3, + open( + os.path.join(TEST_DIR, "secure_images", "softhsm_v2_3.ini"), + "r", + ) as config_file3, + ): + espsecure.sign_data( "2", None, output_file3.name, True, True, - os.path.join(TEST_DIR, "secure_images", "softhsm_v2_3.ini"), - None, - None, + config_file3, + [], + [], self._open(output_file2.name), ) - espsecure.sign_data(args) - args = self.VerifyArgs( + config_file1.seek(0) + config_file2.seek(0) + config_file3.seek(0) + + espsecure.verify_signature( "2", True, - os.path.join(TEST_DIR, "secure_images", "softhsm_v2_1.ini"), + config_file1, None, output_file3, ) - espsecure.verify_signature(args) output_file3.seek(0) - args = self.VerifyArgs( + espsecure.verify_signature( "2", True, - os.path.join(TEST_DIR, "secure_images", "softhsm_v2_2.ini"), + config_file2, None, output_file3, ) - espsecure.verify_signature(args) output_file3.seek(0) - args = self.VerifyArgs( + espsecure.verify_signature( "2", True, - os.path.join(TEST_DIR, "secure_images", "softhsm_v2_3.ini"), + config_file3, None, output_file3, ) - espsecure.verify_signature(args) diff --git a/tools/esptool_py/test/test_esptool.py b/tools/esptool_py/test/test_esptool.py index 9928b6f813..9ab1f70c6c 100755 --- a/tools/esptool_py/test/test_esptool.py +++ b/tools/esptool_py/test/test_esptool.py @@ -1,4 +1,4 @@ -# Unit tests (really integration tests) for esptool.py using the pytest framework +# Unit tests (really integration tests) for esptool using the pytest framework # Uses a device connected to the serial port. # # RUNNING THIS WILL MESS UP THE DEVICE'S SPI FLASH CONTENTS @@ -8,10 +8,17 @@ # Run with a physical connection to a chip: # - `pytest test_esptool.py --chip esp32 --port /dev/ttyUSB0 --baud 115200` # -# where - --port - a serial port for esptool.py operation +# where - --port - a serial port for esptool operation # - --chip - ESP chip name # - --baud - baud rate # - --with-trace - trace all interactions (True or False) +# +# To run the tests in USB-OTG mode, ground the boot mode straping pin +# and set ESPTOOL_TEST_USB_OTG environment variable to any value. +# +# To run the tests in USB-Serial/JTAG mode, set both --port and --preload-port +# options. The --preload-port needs to be connected to a USB-to-UART bridge, +# while --port needs to be connected to the USB-Serial/JTAG peripheral. import os import os.path @@ -21,11 +28,11 @@ import subprocess import sys import tempfile -import time +from io import StringIO from socket import AF_INET, SOCK_STREAM, socket from time import sleep -from typing import List from unittest.mock import MagicMock +from unittest.mock import patch # Link command line options --port, --chip, --baud, --with-trace, and --preload-port from conftest import ( @@ -43,6 +50,21 @@ try: import esptool import espefuse + from esptool.cmds import ( + detect_chip, + erase_flash, + attach_flash, + flash_id, + image_info, + merge_bin, + read_flash_sfdp, + read_flash, + read_mac, + reset_chip, + verify_flash, + version, + write_flash, + ) except ImportError: need_to_install_package_err() @@ -51,10 +73,7 @@ TEST_DIR = os.path.abspath(os.path.dirname(__file__)) -# esptool.py skips strapping mode check in USB-CDC case if this is set -os.environ["ESPTOOL_TESTING"] = "1" - -print("Running esptool.py tests...") +print("Running esptool tests...") class ESPRFC2217Server(object): @@ -64,7 +83,7 @@ def __init__(self, rfc2217_port=None): self.port = rfc2217_port or self.get_free_port() self.cmd = [ sys.executable, - os.path.join(TEST_DIR, "..", "esp_rfc2217_server.py"), + os.path.join(TEST_DIR, "..", "esp_rfc2217_server"), "-p", str(self.port), arg_port, @@ -145,8 +164,8 @@ def run_esptool(self, args, baud=None, chip=None, port=None, preload=True): This is needed in USB-JTAG/Serial mode to disable the RTC watchdog, which causes the port to periodically disappear. - Returns output from esptool.py as a string if there is any. - Raises an exception if esptool.py fails. + Returns output from esptool as a string if there is any. + Raises an exception if esptool fails. """ def run_esptool_process(cmd): @@ -176,8 +195,13 @@ def run_esptool_process(cmd): base_cmd += ["--port", port or arg_port] if baud or arg_baud is not None: base_cmd += ["--baud", str(baud or arg_baud)] - usb_jtag_serial_reset = ["--before", "usb_reset"] if arg_preload_port else [] - full_cmd = base_cmd + usb_jtag_serial_reset + args.split(" ") + usb_jtag_serial_reset = ["--before", "usb-reset"] if arg_preload_port else [] + usb_otg_dont_reset = ( + ["--after", "no-reset-stub"] if "ESPTOOL_TEST_USB_OTG" in os.environ else [] + ) + full_cmd = ( + base_cmd + usb_jtag_serial_reset + usb_otg_dont_reset + args.split(" ") + ) # Preload a dummy binary to disable the RTC watchdog, needed in USB-JTAG/Serial if ( @@ -198,26 +222,30 @@ def run_esptool_process(cmd): base_cmd[port_index] = arg_preload_port # Set the port to the preload one preload_cmd = base_cmd + [ "--no-stub", - "load_ram", + "load-ram", f"{TEST_DIR}/images/ram_helloworld/helloworld-{arg_chip}.bin", ] print("\nPreloading dummy binary to disable RTC watchdog...") run_esptool_process(preload_cmd) print("Dummy binary preloaded successfully.") - time.sleep(0.3) # Wait for the app to run and port to appear + sleep(0.3) # Wait for the app to run and port to appear # Run the command print(f'\nRunning the "{args}" command...') output = run_esptool_process(full_cmd) print(output) # for more complete stdout logs on failure + + if "ESPTOOL_TEST_USB_OTG" in os.environ: + sleep(0.5) # Wait for the port to enumerate between tests + return output def run_esptool_error(self, args, baud=None, chip=None): """ - Run esptool.py similar to run_esptool, but expect an error. + Run esptool similar to run_esptool, but expect an error. Verifies the error is an expected error not an unhandled exception, - and returns the output from esptool.py as a string. + and returns the output from esptool as a string. """ with pytest.raises(subprocess.CalledProcessError) as fail: self.run_esptool(args, baud, chip) @@ -243,7 +271,7 @@ def readback(self, offset, length, spi_connection=None): dump_file = tempfile.NamedTemporaryFile(delete=False) # a file we can read into try: cmd = ( - f"--before default_reset read_flash {offset} {length} {dump_file.name}" + f"--before default-reset read-flash {offset} {length} {dump_file.name}" ) if spi_connection: cmd += f" --spi-connection {spi_connection}" @@ -251,9 +279,10 @@ def readback(self, offset, length, spi_connection=None): with open(dump_file.name, "rb") as f: rb = f.read() - assert length == len( - rb - ), f"read_flash length {length} offset {offset:#x} yielded {len(rb)} bytes!" + rb_len = len(rb) + assert length == rb_len, ( + f"read-flash length {length} offset {offset:#x} yielded {rb_len} bytes!" + ) return rb finally: dump_file.close() @@ -261,9 +290,9 @@ def readback(self, offset, length, spi_connection=None): def diff(self, readback, compare_to): for rb_b, ct_b, offs in zip(readback, compare_to, range(len(readback))): - assert ( - rb_b == ct_b - ), f"First difference at offset {offs:#x} Expected {ct_b} got {rb_b}" + assert rb_b == ct_b, ( + f"First difference at offset {offs:#x} Expected {ct_b} got {rb_b}" + ) def verify_readback( self, offset, length, compare_to, is_bootloader=False, spi_connection=None @@ -284,6 +313,20 @@ def verify_readback( ct = ct[8:] self.diff(rb, ct) + def verify_output(self, expected_out: list[bytes]): + """Verify that at least one element of expected_out is in serial output""" + # Setting rtscts to true enables hardware flow control. + # This removes unwanted RTS logic level changes for some machines + # (and, therefore, chip resets) + # when the port is opened by the following function. + # As a result, if an app loaded to RAM, it has a chance to run and send + # "Hello world" data without unwanted chip reset. + with serial.serial_for_url(arg_port, arg_baud, rtscts=True) as p: + p.timeout = 5 + output = p.read(100) + print(f"Output: {output}") + assert any(item in output for item in expected_out) + @pytest.mark.skipif(arg_chip != "esp32", reason="ESP32 only") class TestFlashEncryption(EsptoolTestCase): @@ -291,8 +334,8 @@ def valid_key_present(self): try: esp = esptool.ESP32ROM(arg_port) esp.connect() - efuses, _ = espefuse.get_efuses(esp=esp) - blk1_rd_en = efuses["BLOCK1"].is_readable() + efuse_cls = espefuse.init_commands(esp=esp) + blk1_rd_en = efuse_cls.efuses["BLOCK1"].is_readable() return not blk1_rd_en finally: esp._port.close() @@ -305,12 +348,12 @@ def test_blank_efuse_encrypt_write_abort(self): pytest.skip("Valid encryption key already programmed, aborting the test") self.run_esptool( - "write_flash 0x1000 images/bootloader_esp32.bin " + "write-flash 0x1000 images/bootloader_esp32.bin " "0x8000 images/partitions_singleapp.bin " "0x10000 images/ram_helloworld/helloworld-esp32.bin" ) output = self.run_esptool_error( - "write_flash --encrypt 0x10000 images/ram_helloworld/helloworld-esp32.bin" + "write-flash --encrypt 0x10000 images/ram_helloworld/helloworld-esp32.bin" ) assert "Flash encryption key is not programmed".lower() in output.lower() @@ -326,13 +369,13 @@ def test_blank_efuse_encrypt_write_continue1(self): pytest.skip("Valid encryption key already programmed, aborting the test") self.run_esptool( - "write_flash --encrypt --ignore-flash-encryption-efuse-setting " + "write-flash --encrypt --ignore-flash-enc-efuse " "0x10000 images/ram_helloworld/helloworld-esp32.bin" ) - self.run_esptool("read_flash 0x10000 192 images/read_encrypted_flash.bin") + self.run_esptool("read-flash 0x10000 192 images/read_encrypted_flash.bin") self.run_espsecure( - "encrypt_flash_data --address 0x10000 --keyfile images/aes_key.bin " - "--flash_crypt_conf 0 --output images/local_enc.bin " + "encrypt-flash-data --address 0x10000 --keyfile images/aes_key.bin " + "--flash-crypt-conf 0 --output images/local_enc.bin " "images/ram_helloworld/helloworld-esp32.bin" ) @@ -344,9 +387,9 @@ def test_blank_efuse_encrypt_write_continue1(self): read_file2 = file2.read() for rf1, rf2, i in zip(read_file1, read_file2, range(len(read_file2))): - assert ( - rf1 == rf2 - ), f"Encrypted write failed: file mismatch at byte position {i}" + assert rf1 == rf2, ( + f"Encrypted write failed: file mismatch at byte position {i}" + ) print("Encrypted write success") finally: @@ -365,13 +408,13 @@ def test_blank_efuse_encrypt_write_continue2(self): pytest.skip("Valid encryption key already programmed, aborting the test") self.run_esptool( - "write_flash --encrypt --ignore-flash-encryption-efuse-setting " + "write-flash --encrypt --ignore-flash-enc-efuse " "0x10000 images/ram_helloworld/helloworld-esp32_edit.bin" ) - self.run_esptool("read_flash 0x10000 192 images/read_encrypted_flash.bin") + self.run_esptool("read-flash 0x10000 192 images/read_encrypted_flash.bin") self.run_espsecure( - "encrypt_flash_data --address 0x10000 --keyfile images/aes_key.bin " - "--flash_crypt_conf 0 --output images/local_enc.bin " + "encrypt-flash-data --address 0x10000 --keyfile images/aes_key.bin " + "--flash-crypt-conf 0 --output images/local_enc.bin " "images/ram_helloworld/helloworld-esp32.bin" ) @@ -393,26 +436,45 @@ def test_blank_efuse_encrypt_write_continue2(self): class TestFlashing(EsptoolTestCase): @pytest.mark.quick_test def test_short_flash(self): - self.run_esptool("write_flash 0x0 images/one_kb.bin") + self.run_esptool("write-flash 0x0 images/one_kb.bin") + self.verify_readback(0, 1024, "images/one_kb.bin") + + @pytest.mark.skipif(arg_chip != "esp32", reason="Don't need to test multiple times") + def test_short_flash_deprecated(self): + out = self.run_esptool( + "--before default_reset write_flash 0x0 images/one_kb.bin --flash_size keep" + ) + assert ( + "Deprecated: Choice 'default_reset' for option '--before' is deprecated. " + "Use 'default-reset' instead." in out + ) + assert ( + "Deprecated: Option '--flash_size' is deprecated. " + "Use '--flash-size' instead." in out + ) + assert ( + "Deprecated: Command 'write_flash' is deprecated. " + "Use 'write-flash' instead." in out + ) self.verify_readback(0, 1024, "images/one_kb.bin") @pytest.mark.quick_test def test_highspeed_flash(self): - self.run_esptool("write_flash 0x0 images/fifty_kb.bin", baud=921600) + self.run_esptool("write-flash 0x0 images/fifty_kb.bin", baud=921600) self.verify_readback(0, 50 * 1024, "images/fifty_kb.bin") def test_adjacent_flash(self): - self.run_esptool("write_flash 0x0 images/sector.bin 0x1000 images/fifty_kb.bin") + self.run_esptool("write-flash 0x0 images/sector.bin 0x1000 images/fifty_kb.bin") self.verify_readback(0, 4096, "images/sector.bin") self.verify_readback(4096, 50 * 1024, "images/fifty_kb.bin") def test_short_flash_hex(self): fd, f = tempfile.mkstemp(suffix=".hex") try: - self.run_esptool(f"merge_bin --format hex 0x0 images/one_kb.bin -o {f}") + self.run_esptool(f"merge-bin --format hex 0x0 images/one_kb.bin -o {f}") # make sure file is closed before running next command (mainly for Windows) os.close(fd) - self.run_esptool(f"write_flash 0x0 {f}") + self.run_esptool(f"write-flash 0x0 {f}") self.verify_readback(0, 1024, "images/one_kb.bin") finally: os.unlink(f) @@ -421,14 +483,14 @@ def test_adjacent_flash_hex(self): fd1, f1 = tempfile.mkstemp(suffix=".hex") fd2, f2 = tempfile.mkstemp(suffix=".hex") try: - self.run_esptool(f"merge_bin --format hex 0x0 images/sector.bin -o {f1}") + self.run_esptool(f"merge-bin --format hex 0x0 images/sector.bin -o {f1}") # make sure file is closed before running next command (mainly for Windows) os.close(fd1) self.run_esptool( - f"merge_bin --format hex 0x1000 images/fifty_kb.bin -o {f2}" + f"merge-bin --format hex 0x1000 images/fifty_kb.bin -o {f2}" ) os.close(fd2) - self.run_esptool(f"write_flash 0x0 {f1} 0x1000 {f2}") + self.run_esptool(f"write-flash 0x0 {f1} 0x1000 {f2}") self.verify_readback(0, 4096, "images/sector.bin") self.verify_readback(4096, 50 * 1024, "images/fifty_kb.bin") finally: @@ -439,20 +501,20 @@ def test_adjacent_flash_mixed(self): fd, f = tempfile.mkstemp(suffix=".hex") try: self.run_esptool( - f"merge_bin --format hex 0x1000 images/fifty_kb.bin -o {f}" + f"merge-bin --format hex 0x1000 images/fifty_kb.bin -o {f}" ) # make sure file is closed before running next command (mainly for Windows) os.close(fd) - self.run_esptool(f"write_flash 0x0 images/sector.bin 0x1000 {f}") + self.run_esptool(f"write-flash 0x0 images/sector.bin 0x1000 {f}") self.verify_readback(0, 4096, "images/sector.bin") self.verify_readback(4096, 50 * 1024, "images/fifty_kb.bin") finally: os.unlink(f) def test_adjacent_independent_flash(self): - self.run_esptool("write_flash 0x0 images/sector.bin") + self.run_esptool("write-flash 0x0 images/sector.bin") self.verify_readback(0, 4096, "images/sector.bin") - self.run_esptool("write_flash 0x1000 images/fifty_kb.bin") + self.run_esptool("write-flash 0x1000 images/fifty_kb.bin") self.verify_readback(4096, 50 * 1024, "images/fifty_kb.bin") # writing flash the second time shouldn't have corrupted the first time self.verify_readback(0, 4096, "images/sector.bin") @@ -464,11 +526,11 @@ def test_last_bytes_of_32M_flash(self): flash_size = 32 * 1024 * 1024 image_size = 1024 offset = flash_size - image_size - self.run_esptool("write_flash {} images/one_kb.bin".format(hex(offset))) - # Some of the functions cannot handle 32-bit addresses - i.e. addresses accessing - # the higher 16MB will manipulate with the lower 16MB flash area. + self.run_esptool("write-flash {} images/one_kb.bin".format(hex(offset))) + # Some of the functions cannot handle 32-bit addresses - i.e. addresses + # accessing the higher 16MB will manipulate with the lower 16MB flash area. offset2 = offset & 0xFFFFFF - self.run_esptool("write_flash {} images/one_kb_all_ef.bin".format(hex(offset2))) + self.run_esptool("write-flash {} images/one_kb_all_ef.bin".format(hex(offset2))) self.verify_readback(offset, image_size, "images/one_kb.bin") @pytest.mark.skipif( @@ -476,17 +538,17 @@ def test_last_bytes_of_32M_flash(self): ) def test_write_larger_area_to_32M_flash(self): offset = 18 * 1024 * 1024 - self.run_esptool("write_flash {} images/one_mb.bin".format(hex(offset))) - # Some of the functions cannot handle 32-bit addresses - i.e. addresses accessing - # the higher 16MB will manipulate with the lower 16MB flash area. + self.run_esptool("write-flash {} images/one_mb.bin".format(hex(offset))) + # Some of the functions cannot handle 32-bit addresses - i.e. addresses + # accessing the higher 16MB will manipulate with the lower 16MB flash area. offset2 = offset & 0xFFFFFF - self.run_esptool("write_flash {} images/one_kb_all_ef.bin".format(hex(offset2))) + self.run_esptool("write-flash {} images/one_kb_all_ef.bin".format(hex(offset2))) self.verify_readback(offset, 1 * 1024 * 1024, "images/one_mb.bin") def test_correct_offset(self): """Verify writing at an offset actually writes to that offset.""" - self.run_esptool("write_flash 0x2000 images/sector.bin") - time.sleep(0.1) + self.run_esptool("write-flash 0x2000 images/sector.bin") + sleep(0.1) three_sectors = self.readback(0, 0x3000) last_sector = three_sectors[0x2000:] with open("images/sector.bin", "rb") as f: @@ -496,7 +558,7 @@ def test_correct_offset(self): @pytest.mark.quick_test def test_no_compression_flash(self): self.run_esptool( - "write_flash -u 0x0 images/sector.bin 0x1000 images/fifty_kb.bin" + "write-flash -u 0x0 images/sector.bin 0x1000 images/fifty_kb.bin" ) self.verify_readback(0, 4096, "images/sector.bin") self.verify_readback(4096, 50 * 1024, "images/fifty_kb.bin") @@ -505,7 +567,7 @@ def test_no_compression_flash(self): @pytest.mark.skipif(arg_chip == "esp8266", reason="Added in ESP32") def test_compressed_nostub_flash(self): self.run_esptool( - "--no-stub write_flash -z 0x0 images/sector.bin 0x1000 images/fifty_kb.bin" + "--no-stub write-flash -z 0x0 images/sector.bin 0x1000 images/fifty_kb.bin" ) self.verify_readback(0, 4096, "images/sector.bin") self.verify_readback(4096, 50 * 1024, "images/fifty_kb.bin") @@ -518,13 +580,13 @@ def _test_partition_table_then_bootloader(self, args): self.verify_readback(0x4000, 96, "images/partitions_singleapp.bin") def test_partition_table_then_bootloader(self): - self._test_partition_table_then_bootloader("write_flash --force") + self._test_partition_table_then_bootloader("write-flash --force") def test_partition_table_then_bootloader_no_compression(self): - self._test_partition_table_then_bootloader("write_flash --force -u") + self._test_partition_table_then_bootloader("write-flash --force -u") def test_partition_table_then_bootloader_nostub(self): - self._test_partition_table_then_bootloader("--no-stub write_flash --force") + self._test_partition_table_then_bootloader("--no-stub write-flash --force") # note: there is no "partition table then bootloader" test that # uses --no-stub and -z, as the ESP32 ROM over-erases and can't @@ -532,16 +594,16 @@ def test_partition_table_then_bootloader_nostub(self): # test_compressed_nostub_flash() instead. def test_length_not_aligned_4bytes(self): - self.run_esptool("write_flash 0x0 images/not_4_byte_aligned.bin") + self.run_esptool("write-flash 0x0 images/not_4_byte_aligned.bin") def test_length_not_aligned_4bytes_no_compression(self): - self.run_esptool("write_flash -u 0x0 images/not_4_byte_aligned.bin") + self.run_esptool("write-flash -u 0x0 images/not_4_byte_aligned.bin") @pytest.mark.quick_test @pytest.mark.host_test def test_write_overlap(self): output = self.run_esptool_error( - "write_flash 0x0 images/bootloader_esp32.bin 0x1000 images/one_kb.bin" + "write-flash 0x0 images/bootloader_esp32.bin 0x1000 images/one_kb.bin" ) assert "Detected overlap at address: 0x1000 " in output @@ -549,7 +611,7 @@ def test_write_overlap(self): @pytest.mark.host_test def test_repeated_address(self): output = self.run_esptool_error( - "write_flash 0x0 images/one_kb.bin 0x0 images/one_kb.bin" + "write-flash 0x0 images/one_kb.bin 0x0 images/one_kb.bin" ) assert "Detected overlap at address: 0x0 " in output @@ -559,13 +621,13 @@ def test_write_sector_overlap(self): # These two 1KB files don't overlap, # but they do both touch sector at 0x1000 so should fail output = self.run_esptool_error( - "write_flash 0xd00 images/one_kb.bin 0x1d00 images/one_kb.bin" + "write-flash 0xd00 images/one_kb.bin 0x1d00 images/one_kb.bin" ) assert "Detected overlap at address: 0x1d00" in output def test_write_no_overlap(self): output = self.run_esptool( - "write_flash 0x0 images/one_kb.bin 0x2000 images/one_kb.bin" + "write-flash 0x0 images/one_kb.bin 0x2000 images/one_kb.bin" ) assert "Detected overlap at address" not in output @@ -575,7 +637,7 @@ def test_compressible_file(self): file_size = 1024 * 1024 input_file.write(b"\x00" * file_size) input_file.close() - self.run_esptool(f"write_flash 0x10000 {input_file.name}") + self.run_esptool(f"write-flash 0x10000 {input_file.name}") finally: os.unlink(input_file.name) @@ -589,7 +651,7 @@ def test_compressible_non_trivial_file(self): struct.pack("B", random.randrange(0, 1 << 8)) * same_bytes ) input_file.close() - self.run_esptool(f"write_flash 0x10000 {input_file.name}") + self.run_esptool(f"write-flash 0x10000 {input_file.name}") finally: os.unlink(input_file.name) @@ -597,23 +659,23 @@ def test_compressible_non_trivial_file(self): def test_zero_length(self): # Zero length files are skipped with a warning output = self.run_esptool( - "write_flash 0x10000 images/one_kb.bin 0x11000 images/zerolength.bin" + "write-flash 0x10000 images/one_kb.bin 0x11000 images/zerolength.bin" ) self.verify_readback(0x10000, 1024, "images/one_kb.bin") - assert "zerolength.bin is empty" in output + assert "'images/zerolength.bin' is empty" in output @pytest.mark.quick_test def test_single_byte(self): - self.run_esptool("write_flash 0x0 images/onebyte.bin") + self.run_esptool("write-flash 0x0 images/onebyte.bin") self.verify_readback(0x0, 1, "images/onebyte.bin") def test_erase_range_messages(self): output = self.run_esptool( - "write_flash 0x1000 images/sector.bin 0x0FC00 images/one_kb.bin" + "write-flash 0x1000 images/sector.bin 0x0FC00 images/one_kb.bin" ) assert "Flash will be erased from 0x00001000 to 0x00001fff..." in output assert ( - "WARNING: Flash address 0x0000fc00 is not aligned to a 0x1000 " + "Flash address 0x0000fc00 is not aligned to a 0x1000 " "byte flash sector. 0xc00 bytes before this address will be erased." in output ) @@ -627,12 +689,12 @@ def test_erase_range_messages(self): ) def test_write_image_for_another_target(self): output = self.run_esptool_error( - "write_flash 0x0 images/esp32s3_header.bin 0x1000 images/one_kb.bin" + "write-flash 0x0 images/esp32s3_header.bin 0x1000 images/one_kb.bin" ) - assert "Unexpected chip id in image." in output + assert "Unexpected chip ID in image." in output assert "value was 9. Is this image for a different chip model?" in output - assert "images/esp32s3_header.bin is not an " in output - assert "image. Use --force to flash anyway." in output + assert "'images/esp32s3_header.bin' is not an " in output + assert "image. Use the force argument to flash anyway." in output @pytest.mark.skipif( arg_chip == "esp8266", reason="chip_id field exist in ESP32 and later images" @@ -642,11 +704,11 @@ def test_write_image_for_another_target(self): ) def test_write_image_for_another_revision(self): output = self.run_esptool_error( - "write_flash 0x0 images/one_kb.bin 0x1000 images/esp32s3_header.bin" + "write-flash 0x0 images/one_kb.bin 0x1000 images/esp32s3_header.bin" ) - assert "images/esp32s3_header.bin requires chip revision 10" in output + assert "'images/esp32s3_header.bin' requires chip revision 10" in output assert "or higher (this chip is revision" in output - assert "Use --force to flash anyway." in output + assert "Use the force argument to flash anyway." in output @pytest.mark.skipif( arg_chip != "esp32c3", reason="This check happens only on a valid image" @@ -654,28 +716,29 @@ def test_write_image_for_another_revision(self): def test_flash_with_min_max_rev(self): """Use min/max_rev_full field to specify chip revision""" output = self.run_esptool_error( - "write_flash 0x0 images/one_kb.bin 0x1000 images/esp32c3_header_min_rev.bin" + "write-flash 0x0 images/one_kb.bin 0x1000 images/esp32c3_header_min_rev.bin" ) assert ( - "images/esp32c3_header_min_rev.bin " + "'images/esp32c3_header_min_rev.bin' " "requires chip revision in range [v2.55 - max rev not set]" in output ) - assert "Use --force to flash anyway." in output + assert "Use the force argument to flash anyway." in output @pytest.mark.quick_test def test_erase_before_write(self): - output = self.run_esptool("write_flash --erase-all 0x0 images/one_kb.bin") - assert "Chip erase completed successfully" in output + output = self.run_esptool("write-flash --erase-all 0x0 images/one_kb.bin") + assert "Flash memory erased successfully" in output assert "Hash of data verified" in output @pytest.mark.quick_test def test_flash_not_aligned_nostub(self): - output = self.run_esptool("--no-stub write_flash 0x1 images/one_kb.bin") + output = self.run_esptool("--no-stub write-flash 0x1 images/one_kb.bin") assert ( - "WARNING: Flash address 0x00000001 is not aligned to a 0x1000 byte flash sector. 0x1 bytes before this address will be erased." - in output + "Flash address 0x00000001 is not aligned to a 0x1000 byte flash sector. " + "0x1 bytes before this address will be erased." in output ) - assert "Hard resetting via RTS pin..." in output + if "ESPTOOL_TEST_USB_OTG" not in os.environ: + assert "Hard resetting" in output @pytest.mark.skipif(arg_preload_port is False, reason="USB-JTAG/Serial only") @pytest.mark.skipif(arg_chip != "esp32c3", reason="ESP32-C3 only") @@ -690,13 +753,13 @@ def test_flash_overclocked(self): SYSTEM_SOC_CLK_MAX = 1 output = self.run_esptool( - "--after no_reset_stub write_flash 0x0 images/one_mb.bin", preload=False + "--after no-reset-stub write-flash 0x0 images/one_mb.bin", preload=False ) faster = re.search(r"(\d+(\.\d+)?)\s+seconds", output) assert faster, "Duration summary not found in the output" with esptool.cmds.detect_chip( - port=arg_port, connect_mode="no_reset" + port=arg_port, connect_mode="no-reset" ) as reg_mod: reg_mod.write_reg( SYSTEM_SYSCLK_CONF_REG, @@ -711,13 +774,13 @@ def test_flash_overclocked(self): ) output = self.run_esptool( - "--before no_reset write_flash 0x0 images/one_mb.bin", preload=False + "--before no-reset write-flash 0x0 images/one_mb.bin", preload=False ) slower = re.search(r"(\d+(\.\d+)?)\s+seconds", output) assert slower, "Duration summary not found in the output" - assert ( - float(slower.group(1)) - float(faster.group(1)) > 1 - ), "Overclocking failed" + assert float(slower.group(1)) - float(faster.group(1)) > 1, ( + "Overclocking failed" + ) @pytest.mark.skipif(arg_preload_port is False, reason="USB-JTAG/Serial only") @pytest.mark.skipif(arg_chip != "esp32c3", reason="ESP32-C3 only") @@ -745,14 +808,14 @@ def test_flash_watchdogs(self): reg_mod.run_stub() output = self.run_esptool( - "--before no_reset --after no_reset_stub flash_id", preload=False + "--before no-reset --after no-reset-stub flash-id", preload=False ) - assert "Stub is already running. No upload is necessary." in output + assert "Stub flasher is already running. No upload is necessary." in output - time.sleep(10) # Wait if RTC WDT triggers + sleep(10) # Wait if RTC WDT triggers with esptool.cmds.detect_chip( - port=arg_port, connect_mode="no_reset" + port=arg_port, connect_mode="no-reset" ) as reg_mod: output = reg_mod.read_reg(reg_mod.RTC_CNTL_WDTCONFIG0_REG) assert output == 0, "RTC WDT is not disabled" @@ -764,11 +827,11 @@ def test_flash_watchdogs(self): @pytest.mark.skipif( arg_chip in ["esp8266", "esp32"], - reason="get_security_info command is supported on ESP32S2 and later", + reason="get-security-info command is supported on ESP32S2 and later", ) class TestSecurityInfo(EsptoolTestCase): def test_show_security_info(self): - res = self.run_esptool("get_security_info") + res = self.run_esptool("get-security-info") assert "Flags" in res assert "Crypt Count" in res if arg_chip != "esp32c2": @@ -788,38 +851,45 @@ def test_show_security_info(self): class TestFlashSizes(EsptoolTestCase): def test_high_offset(self): - self.run_esptool("write_flash -fs 4MB 0x300000 images/one_kb.bin") + self.run_esptool("write-flash -fs 4MB 0x300000 images/one_kb.bin") self.verify_readback(0x300000, 1024, "images/one_kb.bin") def test_high_offset_no_compression(self): - self.run_esptool("write_flash -u -fs 4MB 0x300000 images/one_kb.bin") + self.run_esptool("write-flash -u -fs 4MB 0x300000 images/one_kb.bin") self.verify_readback(0x300000, 1024, "images/one_kb.bin") def test_large_image(self): - self.run_esptool("write_flash -fs 4MB 0x280000 images/one_mb.bin") + self.run_esptool("write-flash -fs 4MB 0x280000 images/one_mb.bin") self.verify_readback(0x280000, 0x100000, "images/one_mb.bin") def test_large_no_compression(self): - self.run_esptool("write_flash -u -fs 4MB 0x280000 images/one_mb.bin") + self.run_esptool("write-flash -u -fs 4MB 0x280000 images/one_mb.bin") self.verify_readback(0x280000, 0x100000, "images/one_mb.bin") @pytest.mark.quick_test @pytest.mark.host_test def test_invalid_size_arg(self): - self.run_esptool_error("write_flash -fs 10MB 0x6000 images/one_kb.bin") + self.run_esptool_error("write-flash -fs 10MB 0x6000 images/one_kb.bin") def test_write_past_end_fails(self): output = self.run_esptool_error( - "write_flash -fs 1MB 0x280000 images/one_kb.bin" + "write-flash -fs 1MB 0x280000 images/one_kb.bin" ) - assert "File images/one_kb.bin" in output + assert "File 'images/one_kb.bin'" in output assert "will not fit" in output + @pytest.mark.skipif(arg_chip != "esp32", reason="Don't need to test multiple times") + def test_read_past_end_fails(self): + output = self.run_esptool_error( + "read-flash 0xffffff 1 out.bin" + ) # 0xffffff is well past the end of the flash in most cases (16MB) + assert "Can't access flash regions larger than detected flash size" in output + def test_write_no_compression_past_end_fails(self): output = self.run_esptool_error( - "write_flash -u -fs 1MB 0x280000 images/one_kb.bin" + "write-flash -u -fs 1MB 0x280000 images/one_kb.bin" ) - assert "File images/one_kb.bin" in output + assert "File 'images/one_kb.bin'" in output assert "will not fit" in output @pytest.mark.skipif( @@ -836,7 +906,7 @@ def test_flash_size_keep(self): with open(image, "rb") as f: f.seek(0, 2) image_len = f.tell() - self.run_esptool(f"write_flash -fs keep {offset} {image}") + self.run_esptool(f"write-flash -fs keep {offset} {image}") # header should be the same as in the .bin file self.verify_readback(offset, image_len, image) @@ -846,18 +916,19 @@ def test_flash_size_keep(self): def test_read_nostub_high_offset(self): offset = 0x300000 length = 1024 - self.run_esptool(f"write_flash -fs detect {offset} images/one_kb.bin") + self.run_esptool(f"write-flash -fs detect {offset} images/one_kb.bin") dump_file = tempfile.NamedTemporaryFile(delete=False) # readback with no-stub and flash-size set try: self.run_esptool( - f"--no-stub read_flash -fs detect {offset} 1024 {dump_file.name}" + f"--no-stub read-flash -fs detect {offset} 1k {dump_file.name}" ) with open(dump_file.name, "rb") as f: rb = f.read() - assert length == len( - rb - ), f"read_flash length {length} offset {offset:#x} yielded {len(rb)} bytes!" + rb_len = len(rb) + assert length == rb_len, ( + f"read-flash length {length} offset {offset:#x} yielded {rb_len} bytes!" + ) finally: dump_file.close() os.unlink(dump_file.name) @@ -871,7 +942,7 @@ class TestFlashDetection(EsptoolTestCase): @pytest.mark.quick_test def test_flash_id(self): """Test manufacturer and device response of flash detection.""" - res = self.run_esptool("flash_id") + res = self.run_esptool("flash-id") assert "Manufacturer:" in res assert "Device:" in res @@ -882,7 +953,7 @@ def test_flash_id_expand_args(self): """ try: arg_file = tempfile.NamedTemporaryFile(delete=False) - arg_file.write(b"flash_id\n") + arg_file.write(b"flash-id\n") arg_file.close() res = self.run_esptool(f"@{arg_file.name}") assert "Manufacturer:" in res @@ -893,17 +964,25 @@ def test_flash_id_expand_args(self): @pytest.mark.quick_test def test_flash_id_trace(self): """Test trace functionality on flash detection, running without stub""" - res = self.run_esptool("--trace flash_id") + res = self.run_esptool("--trace flash-id") # read register command - assert re.search(r"TRACE \+\d.\d{3} command op=0x0a .*", res) is not None + assert ( + re.search(r"TRACE \+\d.\d{3} --- Cmd READ_REG \(0x0a\) .*", res) + is not None + ) # write register command - assert re.search(r"TRACE \+\d.\d{3} command op=0x09 .*", res) is not None - assert re.search(r"TRACE \+\d.\d{3} Read \d* bytes: .*", res) is not None - assert re.search(r"TRACE \+\d.\d{3} Write \d* bytes: .*", res) is not None - assert re.search(r"TRACE \+\d.\d{3} Received full packet: .*", res) is not None + assert ( + re.search(r"TRACE \+\d.\d{3} --- Cmd WRITE_REG \(0x09\) .*", res) + is not None + ) + assert re.search(r"TRACE \+\d.\d{3} Read \d* bytes:", res) is not None + assert re.search(r"TRACE \+\d.\d{3} Write \d+ bytes:", res) is not None + assert ( + re.search(r"TRACE \+\d.\d{3} Received full packet: .*", res) is not None + ) # flasher stub handshake assert ( - re.search(r"TRACE \+\d.\d{3} Received full packet: 4f484149", res) + re.search(r"TRACE \+\d.\d{3} Received full packet: 4f484149", res) is not None ) assert "Manufacturer:" in res @@ -920,7 +999,7 @@ def test_flash_size(self): # but it does not have the same amount of efuse blocks # the methods are overwritten # in case anything changes this test will fail to remind us - res = self.run_esptool("flash_id") + res = self.run_esptool("flash-id") lines = res.splitlines() for line in lines: assert "embedded flash" not in line.lower() @@ -928,10 +1007,10 @@ def test_flash_size(self): @pytest.mark.quick_test def test_flash_sfdp(self): """Test manufacturer and device response of flash detection.""" - res = self.run_esptool("read_flash_sfdp 0 4") - assert "SFDP[0..3]: 53 46 44 50" in res - res = self.run_esptool("read_flash_sfdp 1 3") - assert "SFDP[1..3]: 46 44 50 " in res + res = self.run_esptool("read-flash-sfdp 0 4") + assert "SFDP[0..3]: 0x53 0x46 0x44 0x50" in res + res = self.run_esptool("read-flash-sfdp 1 3") + assert "SFDP[1..3]: 0x46 0x44 0x50" in res @pytest.mark.skipif( @@ -942,9 +1021,9 @@ class TestExternalFlash(EsptoolTestCase): def test_short_flash_to_external_stub(self): # First flash internal flash, then external - self.run_esptool("write_flash 0x0 images/one_kb.bin") + self.run_esptool("write-flash 0x0 images/one_kb.bin") self.run_esptool( - f"write_flash --spi-connection {self.conn} 0x0 images/sector.bin" + f"write-flash --spi-connection {self.conn} 0x0 images/sector.bin" ) self.verify_readback(0, 1024, "images/one_kb.bin") @@ -952,18 +1031,18 @@ def test_short_flash_to_external_stub(self): # First flash external flash, then internal self.run_esptool( - f"write_flash --spi-connection {self.conn} 0x0 images/one_kb.bin" + f"write-flash --spi-connection {self.conn} 0x0 images/one_kb.bin" ) - self.run_esptool("write_flash 0x0 images/sector.bin") + self.run_esptool("write-flash 0x0 images/sector.bin") self.verify_readback(0, 1024, "images/sector.bin") self.verify_readback(0, 1024, "images/one_kb.bin", spi_connection=self.conn) def test_short_flash_to_external_ROM(self): # First flash internal flash, then external - self.run_esptool("--no-stub write_flash 0x0 images/one_kb.bin") + self.run_esptool("--no-stub write-flash 0x0 images/one_kb.bin") self.run_esptool( - f"--no-stub write_flash --spi-connection {self.conn} 0x0 images/sector.bin" + f"--no-stub write-flash --spi-connection {self.conn} 0x0 images/sector.bin" ) self.verify_readback(0, 1024, "images/one_kb.bin") @@ -971,113 +1050,122 @@ def test_short_flash_to_external_ROM(self): # First flash external flash, then internal self.run_esptool( - f"--no-stub write_flash --spi-connection {self.conn} 0x0 images/one_kb.bin" + f"--no-stub write-flash --spi-connection {self.conn} 0x0 images/one_kb.bin" ) - self.run_esptool("--no-stub write_flash 0x0 images/sector.bin") + self.run_esptool("--no-stub write-flash 0x0 images/sector.bin") self.verify_readback(0, 1024, "images/sector.bin") self.verify_readback(0, 1024, "images/one_kb.bin", spi_connection=self.conn) +@pytest.mark.skipif( + "ESPTOOL_TEST_USB_OTG" in os.environ, + reason="USB-OTG tests require --after no-reset for stability.", +) class TestStubReuse(EsptoolTestCase): def test_stub_reuse_with_synchronization(self): """Keep the flasher stub running and reuse it the next time.""" res = self.run_esptool( - "--after no_reset_stub flash_id" + "--after no-reset-stub flash-id" ) # flasher stub keeps running after this assert "Manufacturer:" in res res = self.run_esptool( - "--before no_reset flash_id", + "--before no-reset flash-id", preload=False, ) # do sync before (without reset it talks to the flasher stub) assert "Manufacturer:" in res - @pytest.mark.skipif(arg_chip != "esp8266", reason="ESP8266 only") def test_stub_reuse_without_synchronization(self): """ Keep the flasher stub running and reuse it the next time without synchronization. - - Synchronization is necessary for chips where the ROM bootloader has different - status length in comparison to the flasher stub. - Therefore, this is ESP8266 only test. """ - res = self.run_esptool("--after no_reset_stub flash_id") + res = self.run_esptool("--after no-reset-stub flash-id") assert "Manufacturer:" in res - res = self.run_esptool("--before no_reset_no_sync flash_id") + res = self.run_esptool("--before no-reset-no-sync flash-id", preload=False) assert "Manufacturer:" in res class TestErase(EsptoolTestCase): @pytest.mark.quick_test def test_chip_erase(self): - self.run_esptool("write_flash 0x10000 images/one_kb.bin") + self.run_esptool("write-flash 0x10000 images/one_kb.bin") self.verify_readback(0x10000, 0x400, "images/one_kb.bin") - self.run_esptool("erase_flash") + self.run_esptool("erase-flash") empty = self.readback(0x10000, 0x400) - assert empty == b"\xFF" * 0x400 + assert empty == b"\xff" * 0x400 def test_region_erase(self): - self.run_esptool("write_flash 0x10000 images/one_kb.bin") - self.run_esptool("write_flash 0x11000 images/sector.bin") + self.run_esptool("write-flash 0x10000 images/one_kb.bin") + self.run_esptool("write-flash 0x11000 images/sector.bin") self.verify_readback(0x10000, 0x400, "images/one_kb.bin") self.verify_readback(0x11000, 0x1000, "images/sector.bin") # erase only the flash sector containing one_kb.bin - self.run_esptool("erase_region 0x10000 0x1000") + self.run_esptool("erase-region 0x10000 0x1000") self.verify_readback(0x11000, 0x1000, "images/sector.bin") empty = self.readback(0x10000, 0x1000) - assert empty == b"\xFF" * 0x1000 + assert empty == b"\xff" * 0x1000 def test_region_erase_all(self): - res = self.run_esptool("erase_region 0x0 ALL") + res = self.run_esptool("erase-region 0x0 ALL") assert re.search(r"Detected flash size: \d+[KM]B", res) is not None def test_large_region_erase(self): # verifies that erasing a large region doesn't time out - self.run_esptool("erase_region 0x0 0x100000") + self.run_esptool("erase-region 0x0 0x100000") + + @pytest.mark.skipif(arg_chip == "esp8266", reason="Not supported on ESP8266") + def test_region_erase_no_stub(self): + self.run_esptool("write-flash 0x10000 images/one_kb.bin") + self.run_esptool("write-flash 0x11000 images/sector.bin") + self.verify_readback(0x10000, 0x400, "images/one_kb.bin") + self.verify_readback(0x11000, 0x1000, "images/sector.bin") + # erase only the flash sector containing one_kb.bin + self.run_esptool("--no-stub erase-region 0x10000 0x1000") + self.verify_readback(0x11000, 0x1000, "images/sector.bin") + empty = self.readback(0x10000, 0x1000) + assert empty == b"\xff" * 0x1000 class TestSectorBoundaries(EsptoolTestCase): def test_end_sector(self): - self.run_esptool("write_flash 0x10000 images/sector.bin") - self.run_esptool("write_flash 0x0FC00 images/one_kb.bin") + self.run_esptool("write-flash 0x10000 images/sector.bin") + self.run_esptool("write-flash 0x0FC00 images/one_kb.bin") self.verify_readback(0x0FC00, 0x400, "images/one_kb.bin") self.verify_readback(0x10000, 0x1000, "images/sector.bin") def test_end_sector_uncompressed(self): - self.run_esptool("write_flash -u 0x10000 images/sector.bin") - self.run_esptool("write_flash -u 0x0FC00 images/one_kb.bin") + self.run_esptool("write-flash -u 0x10000 images/sector.bin") + self.run_esptool("write-flash -u 0x0FC00 images/one_kb.bin") self.verify_readback(0x0FC00, 0x400, "images/one_kb.bin") self.verify_readback(0x10000, 0x1000, "images/sector.bin") def test_overlap(self): - self.run_esptool("write_flash 0x20800 images/sector.bin") + self.run_esptool("write-flash 0x20800 images/sector.bin") self.verify_readback(0x20800, 0x1000, "images/sector.bin") class TestVerifyCommand(EsptoolTestCase): @pytest.mark.quick_test def test_verify_success(self): - self.run_esptool("write_flash 0x5000 images/one_kb.bin") - self.run_esptool("verify_flash 0x5000 images/one_kb.bin") + self.run_esptool("write-flash 0x5000 images/one_kb.bin") + self.run_esptool("verify-flash 0x5000 images/one_kb.bin") def test_verify_failure(self): - self.run_esptool("write_flash 0x6000 images/sector.bin") - output = self.run_esptool_error( - "verify_flash --diff=yes 0x6000 images/one_kb.bin" - ) - assert "verify FAILED" in output - assert "first @ 0x00006000" in output + self.run_esptool("write-flash 0x6000 images/sector.bin") + output = self.run_esptool_error("verify-flash --diff 0x6000 images/one_kb.bin") + assert "Verification failed:" in output + assert "first at 0x00006000:" in output def test_verify_unaligned_length(self): - self.run_esptool("write_flash 0x0 images/not_4_byte_aligned.bin") - self.run_esptool("verify_flash 0x0 images/not_4_byte_aligned.bin") + self.run_esptool("write-flash 0x0 images/not_4_byte_aligned.bin") + self.run_esptool("verify-flash 0x0 images/not_4_byte_aligned.bin") class TestReadIdentityValues(EsptoolTestCase): @pytest.mark.quick_test def test_read_mac(self): - output = self.run_esptool("read_mac") + output = self.run_esptool("read-mac") mac = re.search(r"[0-9a-f:]{17}", output) assert mac is not None mac = mac.group(0) @@ -1086,7 +1174,7 @@ def test_read_mac(self): @pytest.mark.skipif(arg_chip != "esp8266", reason="ESP8266 only") def test_read_chip_id(self): - output = self.run_esptool("chip_id") + output = self.run_esptool("chip-id") idstr = re.search("Chip ID: 0x([0-9a-f]+)", output) assert idstr is not None idstr = idstr.group(1) @@ -1097,23 +1185,24 @@ def test_read_chip_id(self): class TestMemoryOperations(EsptoolTestCase): @pytest.mark.quick_test def test_memory_dump(self): - output = self.run_esptool("dump_mem 0x50000000 128 memout.bin") - assert "Read 128 bytes" in output + output = self.run_esptool("dump-mem 0x50000000 128 memout.bin") + assert "Dumped 128 bytes from 0x50000000" in output + assert "to 'memout.bin'" in output os.remove("memout.bin") def test_memory_write(self): - output = self.run_esptool("write_mem 0x400C0000 0xabad1dea 0x0000ffff") - assert "Wrote abad1dea" in output - assert "mask 0000ffff" in output - assert "to 400c0000" in output + output = self.run_esptool("write-mem 0x400C0000 0xabad1dea 0x0000ffff") + assert "Wrote 0xabad1dea" in output + assert "mask 0x0000ffff" in output + assert "to 0x400c0000" in output def test_memory_read(self): - output = self.run_esptool("read_mem 0x400C0000") + output = self.run_esptool("read-mem 0x400C0000") assert "0x400c0000 =" in output class TestKeepImageSettings(EsptoolTestCase): - """Tests for the -fm keep, -ff keep options for write_flash""" + """Tests for the -fm keep, -ff keep options for write-flash""" @classmethod def setup_class(self): @@ -1129,17 +1218,17 @@ def setup_class(self): ) def test_keep_does_not_change_settings(self): # defaults should all be keep - self.run_esptool(f"write_flash -fs keep {self.flash_offset:#x} {self.BL_IMAGE}") + self.run_esptool(f"write-flash -fs keep {self.flash_offset:#x} {self.BL_IMAGE}") self.verify_readback(self.flash_offset, 8, self.BL_IMAGE, False) # can also explicitly set all options self.run_esptool( - f"write_flash -fm keep -ff keep -fs keep " + f"write-flash -fm keep -ff keep -fs keep " f"{self.flash_offset:#x} {self.BL_IMAGE}" ) self.verify_readback(self.flash_offset, 8, self.BL_IMAGE, False) - # verify_flash should also use 'keep' + # verify-flash should also use 'keep' self.run_esptool( - f"verify_flash -fs keep {self.flash_offset:#x} {self.BL_IMAGE}" + f"verify-flash -fs keep {self.flash_offset:#x} {self.BL_IMAGE}" ) @pytest.mark.skipif( @@ -1149,7 +1238,7 @@ def test_keep_does_not_change_settings(self): @pytest.mark.quick_test def test_detect_size_changes_size(self): self.run_esptool( - f"write_flash -fs detect {self.flash_offset:#x} {self.BL_IMAGE}" + f"write-flash -fs detect {self.flash_offset:#x} {self.BL_IMAGE}" ) readback = self.readback(self.flash_offset, 8) assert self.header[:3] == readback[:3] # first 3 bytes unchanged @@ -1162,7 +1251,7 @@ def test_detect_size_changes_size(self): ) def test_explicit_set_size_freq_mode(self): self.run_esptool( - f"write_flash -fs 2MB -fm dout -ff 80m " + f"write-flash -fs 2MB -fm dout -ff 80m " f"{self.flash_offset:#x} {self.BL_IMAGE}" ) @@ -1177,12 +1266,12 @@ def test_explicit_set_size_freq_mode(self): assert self.header[3] != readback[3] # size/freq values have changed assert self.header[4:] == readback[4:] # entrypoint address hasn't changed - # verify_flash should pass if we match params, fail otherwise + # verify-flash should pass if we match params, fail otherwise self.run_esptool( - f"verify_flash -fs 2MB -fm dout -ff 80m " + f"verify-flash -fs 2MB -fm dout -ff 80m " f"{self.flash_offset:#x} {self.BL_IMAGE}" ) - self.run_esptool_error(f"verify_flash {self.flash_offset:#x} {self.BL_IMAGE}") + self.run_esptool_error(f"verify-flash {self.flash_offset:#x} {self.BL_IMAGE}") @pytest.mark.skipif( @@ -1193,7 +1282,7 @@ class TestLoadRAM(EsptoolTestCase): # flashing an application not supporting USB-CDC will make # /dev/ttyACM0 disappear and USB-CDC tests will not work anymore - def verify_output(self, expected_out: List[bytes]): + def verify_output(self, expected_out: list[bytes]): """Verify that at least one element of expected_out is in serial output""" # Setting rtscts to true enables hardware flow control. # This removes unwanted RTS logic level changes for some machines @@ -1209,18 +1298,18 @@ def verify_output(self, expected_out: List[bytes]): @pytest.mark.quick_test def test_load_ram(self): - """Verify load_ram command + """Verify load-ram command The "hello world" binary programs for each chip print "Hello world!\n" to the serial port. """ - self.run_esptool(f"load_ram images/ram_helloworld/helloworld-{arg_chip}.bin") + self.run_esptool(f"load-ram images/ram_helloworld/helloworld-{arg_chip}.bin") self.verify_output( [b"Hello world!", b'\xce?\x13\x05\x04\xd0\x97A\x11"\xc4\x06\xc67\x04'] ) def test_load_ram_hex(self): - """Verify load_ram command with hex file as input + """Verify load-ram command with hex file as input The "hello world" binary programs for each chip print "Hello world!\n" to the serial port. @@ -1228,12 +1317,12 @@ def test_load_ram_hex(self): fd, f = tempfile.mkstemp(suffix=".hex") try: self.run_esptool( - f"merge_bin --format hex -o {f} 0x0 " + f"merge-bin --format hex -o {f} 0x0 " f"images/ram_helloworld/helloworld-{arg_chip}.bin" ) # make sure file is closed before running next command (mainly for Windows) os.close(fd) - self.run_esptool(f"load_ram {f}") + self.run_esptool(f"load-ram {f}") self.verify_output( [b"Hello world!", b'\xce?\x13\x05\x04\xd0\x97A\x11"\xc4\x06\xc67\x04'] ) @@ -1255,11 +1344,11 @@ def test_deep_sleep_flash(self): # not even necessary to wake successfully from sleep, # going into deep sleep is enough # (so GPIO16, etc, config is not important for this test) - self.run_esptool("write_flash 0x0 images/esp8266_deepsleep.bin", baud=230400) + self.run_esptool("write-flash 0x0 images/esp8266_deepsleep.bin", baud=230400) - time.sleep(0.25) # give ESP8266 time to enter deep sleep + sleep(0.25) # give ESP8266 time to enter deep sleep - self.run_esptool("write_flash 0x0 images/fifty_kb.bin", baud=230400) + self.run_esptool("write-flash 0x0 images/fifty_kb.bin", baud=230400) self.verify_readback(0, 50 * 1024, "images/fifty_kb.bin") @@ -1274,13 +1363,13 @@ def test_flash_header_rewrite(self): bl_image = f"images/bootloader_{arg_chip}.bin" output = self.run_esptool( - f"write_flash -fm dout -ff 20m {bl_offset:#x} {bl_image}" + f"write-flash -fm dout -ff 20m {bl_offset:#x} {bl_image}" ) if arg_chip in ["esp8266", "esp32"]: - # ESP8266 doesn't support this; The test image for ESP32 just doesn't have it. - assert "Flash params set to" in output + # ESP8266 lacks this feature; ESP32 test image doesn't include it + assert "Flash parameters set to" in output else: - assert "Flash params set to" in output + assert "Flash parameters set to" in output # Since SHA recalculation is supported for changed bootloader header assert "SHA digest in image updated" in output @@ -1290,7 +1379,7 @@ def test_flash_header_no_magic_no_rewrite(self): bl_offset = esptool.CHIP_DEFS[arg_chip].BOOTLOADER_FLASH_OFFSET for image in ["images/one_kb.bin", "images/one_kb_all_ef.bin"]: output = self.run_esptool( - f"write_flash -fm dout -ff 20m {bl_offset:#x} {image}" + f"write-flash -fm dout -ff 20m {bl_offset:#x} {image}" ) "not changing any flash settings" in output self.verify_readback(bl_offset, 1024, image) @@ -1299,17 +1388,108 @@ def test_flash_header_no_magic_no_rewrite(self): class TestAutoDetect(EsptoolTestCase): def _check_output(self, output): expected_chip_name = esptool.util.expand_chip_name(arg_chip) - if arg_chip not in ["esp8266", "esp32", "esp32s2"]: - assert "Unsupported detection protocol" not in output assert f"Detecting chip type... {expected_chip_name}" in output - assert f"Chip is {expected_chip_name}" in output + assert f"{'Chip type:':<20}{expected_chip_name}" in output @pytest.mark.quick_test def test_auto_detect(self): - output = self.run_esptool("chip_id", chip="auto") + output = self.run_esptool("chip-id", chip="auto") self._check_output(output) +class TestChipDetectionValidation(EsptoolTestCase): + """Test the chip detection validation logic in ESPLoader.connect() method. + + This tests the section that validates if the connected chip matches the + specified chip argument, covering scenarios with: + - Chips that use chip ID detection (ESP32-S3 and later) + - Chips that use magic value detection (ESP8266, ESP32, ESP32-S2) + - ESP32-S2 in Secure Download Mode (SDM) + - Correct chip argument vs wrong chip argument detection + """ + + def _find_different_chip(self, chip_name, detection_method): + """Find a different chip from the specified chip name + based on the detection method, except ESP32-S2. + + Args: + chip_name: The name of the chip to find a different chip for. + detection_method: The detection method to use. + + Returns: + The name of the different chip. + """ + for chip in esptool.CHIP_DEFS: + if chip != chip_name and chip != "esp32s2": + if ( + detection_method == "chip_id" + and not esptool.CHIP_DEFS[chip].USES_MAGIC_VALUE + ): + if ( + esptool.CHIP_DEFS[chip].IMAGE_CHIP_ID + != esptool.CHIP_DEFS[chip_name].IMAGE_CHIP_ID + ): + return chip + elif ( + detection_method == "magic_value" + and esptool.CHIP_DEFS[chip].USES_MAGIC_VALUE + ): + if ( + esptool.CHIP_DEFS[chip].MAGIC_VALUE + != esptool.CHIP_DEFS[chip_name].MAGIC_VALUE + ): + return chip + + @pytest.mark.quick_test + def test_chips_with_chip_id_detection(self): + # First verify the correct chip works + output = self.run_esptool(f"--chip {arg_chip} flash-id") + assert "Stub flasher running." in output + + # Find a different chip with different chip ID to test mismatch detection + different_chip_id = self._find_different_chip(arg_chip, "chip_id") + error_output = self.run_esptool_error(f"--chip {different_chip_id} flash-id") + assert ( + f"This chip is {arg_chip.upper()}, not {different_chip_id.upper()}." + in error_output + or "Wrong chip argument?" in error_output + ) + + # Find a different chip with different magic value to test mismatch detection + different_chip_magic = self._find_different_chip(arg_chip, "magic_value") + error_output = self.run_esptool_error(f"--chip {different_chip_magic} flash-id") + assert ( + f"This chip is {arg_chip.upper()}, not {different_chip_magic.upper()}." + in error_output + or "Wrong chip argument?" in error_output + ) + + if arg_chip != "esp32s2": + # ESP32-S2 is special case that has security info, but not chip ID + error_output = self.run_esptool_error("--chip esp32s2 flash-id") + assert ( + f"This chip is {arg_chip.upper()}, not ESP32-S2." in error_output + or "Wrong chip argument?" in error_output + ) + + +class TestUSBMode(EsptoolTestCase): + @pytest.mark.quick_test + def test_usb_mode(self): + output = self.run_esptool("chip-id") + expected_usb_mode = ( + "USB-OTG" + if os.environ.get("ESPTOOL_TEST_USB_OTG") == "1" + else "USB-Serial/JTAG" + if arg_preload_port + else None + ) + + if expected_usb_mode: + assert "USB mode: " in output + assert expected_usb_mode in output + + @pytest.mark.flaky(reruns=5) @pytest.mark.skipif(arg_preload_port is not False, reason="USB-to-UART bridge only") @pytest.mark.skipif(os.name == "nt", reason="Linux/MacOS only") @@ -1317,7 +1497,7 @@ class TestVirtualPort(TestAutoDetect): def test_auto_detect_virtual_port(self): with ESPRFC2217Server() as server: output = self.run_esptool( - "chip_id", + "chip-id", chip="auto", port=f"rfc2217://localhost:{str(server.port)}?ign_set_control", ) @@ -1327,7 +1507,7 @@ def test_highspeed_flash_virtual_port(self): with ESPRFC2217Server() as server: rfc2217_port = f"rfc2217://localhost:{str(server.port)}?ign_set_control" self.run_esptool( - "write_flash 0x0 images/fifty_kb.bin", + "write-flash 0x0 images/fifty_kb.bin", baud=921600, port=rfc2217_port, ) @@ -1344,7 +1524,7 @@ def pty_port(self): @pytest.mark.host_test def test_pty_port(self, pty_port): - cmd = [sys.executable, "-m", "esptool", "--port", pty_port, "chip_id"] + cmd = [sys.executable, "-m", "esptool", "--port", pty_port, "chip-id"] output = subprocess.run( cmd, cwd=TEST_DIR, @@ -1355,7 +1535,7 @@ def test_pty_port(self, pty_port): assert output.returncode != 0 output = output.stdout.decode("utf-8") print(output) # for logging - assert "WARNING: Chip was NOT reset." in output + assert "Chip was NOT reset." in output @pytest.mark.quick_test @@ -1422,13 +1602,13 @@ def test_read_write_memory_stub(self): ) def test_read_write_flash_status(self): """Read flash status and write back the same status""" - res = self.run_esptool("read_flash_status") - match = re.search(r"Status value: (0x[\d|a-f]*)", res) + res = self.run_esptool("read-flash-status") + match = re.search(r"Flash memory status: (0x[\d|a-f]*)", res) assert match is not None - res = self.run_esptool(f"write_flash_status {match.group(1)}") - assert f"Initial flash status: {match.group(1)}" in res - assert f"Setting flash status: {match.group(1)}" in res - assert f"After flash status: {match.group(1)}" in res + res = self.run_esptool(f"write-flash-status {match.group(1)}") + assert f"Initial flash memory status: {match.group(1)}" in res + assert f"Setting flash memory status: {match.group(1)}" in res + assert f"After flash memory status: {match.group(1)}" in res def test_read_chip_description(self): try: @@ -1459,36 +1639,24 @@ def test_read_get_chip_features(self): @pytest.mark.skipif( - arg_chip != "esp8266", reason="Make image option is supported only on ESP8266" + "ESPTOOL_TEST_USB_OTG" in os.environ or arg_preload_port is not False, + reason="Boot mode strapping pin pulled constantly low, " + "can't reset out of bootloader", ) -class TestMakeImage(EsptoolTestCase): - def verify_image(self, offset, length, image, compare_to): - with open(image, "rb") as f: - f.seek(offset) - rb = f.read(length) - with open(compare_to, "rb") as f: - ct = f.read() - if len(rb) != len(ct): - print( - f"WARNING: Expected length {len(ct)} doesn't match comparison {len(rb)}" - ) - print(f"Readback {len(rb)} bytes") - self.diff(rb, ct) - - def test_make_image(self): - output = self.run_esptool( - "make_image test" - " -a 0x0 -f images/sector.bin -a 0x1000 -f images/fifty_kb.bin" - ) - try: - assert "Successfully created esp8266 image." in output - assert os.path.exists("test0x00000.bin") - self.verify_image(16, 4096, "test0x00000.bin", "images/sector.bin") - self.verify_image( - 4096 + 24, 50 * 1024, "test0x00000.bin", "images/fifty_kb.bin" +class TestReset(EsptoolTestCase): + def test_watchdog_reset(self): + # Erase the bootloader to get "invalid header" output + test watchdog reset + res = self.run_esptool("--after watchdog-reset erase-region 0x0 0x4000") + if arg_chip in ["esp8266", "esp32", "esp32h2", "esp32c6"]: + assert "Watchdog hard reset is not supported" in res + assert "Hard resetting via RTS pin..." in res + else: + assert "Hard resetting with a watchdog..." in res + # If there is no output, the chip did not reset + # Mangled bytes are for C2 26 MHz when the baudrate doesn't match + self.verify_output( + [b"invalid header", b"\x02b\xe2n\x9e\xe0p\x12n\x9c\x0cn"] ) - finally: - os.remove("test0x00000.bin") @pytest.mark.skipif(arg_chip != "esp32", reason="Don't need to test multiple times") @@ -1514,10 +1682,7 @@ def __exit__(self, exc_type, exc_value, exc_tb): assert not os.path.exists(self.file_path) dummy_config = ( - "[esptool]\n" - "connect_attempts = 5\n" - "reset_delay = 1\n" - "serial_write_timeout = 12" + "[esptool]\nconnect_attempts = 5\nreset_delay = 1\nserial_write_timeout = 12" ) @pytest.mark.host_test @@ -1537,7 +1702,7 @@ def test_load_config_file(self): assert f"Loaded custom configuration from {config_file_path}" not in output # Correct header, but options are unparsable - faulty_config = "[esptool]\n" "connect_attempts = 5\n" "connect_attempts = 9\n" + faulty_config = "[esptool]\nconnect_attempts = 5\nconnect_attempts = 9\n" with self.ConfigFile(config_file_path, faulty_config): output = self.run_esptool("version") assert f"Ignoring invalid config file {config_file_path}" in output @@ -1547,9 +1712,7 @@ def test_load_config_file(self): ) # Correct header, unknown option (or a typo) - faulty_config = ( - "[esptool]\n" "connect_attempts = 9\n" "timoout = 2\n" "bits = 2" - ) + faulty_config = "[esptool]\nconnect_attempts = 9\ntimoout = 2\nbits = 2" with self.ConfigFile(config_file_path, faulty_config): output = self.run_esptool("version") assert "Ignoring unknown config file options: bits, timoout" in output @@ -1583,7 +1746,7 @@ def test_load_config_file_with_env_var(self): def test_custom_reset_sequence(self): # This reset sequence will fail to reset the chip to bootloader, - # the flash_id operation should therefore fail. + # the flash-id operation should therefore fail. # Also tests the number of connection attempts. reset_seq_config = ( "[esptool]\n" @@ -1592,7 +1755,7 @@ def test_custom_reset_sequence(self): ) config_file_path = os.path.join(os.getcwd(), "esptool.cfg") with self.ConfigFile(config_file_path, reset_seq_config): - output = self.run_esptool_error("flash_id") + output = self.run_esptool_error("flash-id") assert f"Loaded custom configuration from {config_file_path}" in output assert "A fatal error occurred: Failed to connect to" in output # Connection attempts are represented with dots, @@ -1600,11 +1763,11 @@ def test_custom_reset_sequence(self): assert "Connecting............." not in output # Test invalid custom_reset_sequence format is not accepted - invalid_reset_seq_config = "[esptool]\n" "custom_reset_sequence = F0|R1|C0|A5\n" + invalid_reset_seq_config = "[esptool]\ncustom_reset_sequence = F0|R1|C0|A5\n" with self.ConfigFile(config_file_path, invalid_reset_seq_config): - output = self.run_esptool_error("flash_id") + output = self.run_esptool_error("flash-id") assert f"Loaded custom configuration from {config_file_path}" in output - assert 'Invalid "custom_reset_sequence" option format:' in output + assert "Invalid custom reset sequence option format:" in output def test_open_port_attempts(self): # Test that the open_port_attempts option is loaded correctly @@ -1613,12 +1776,140 @@ def test_open_port_attempts(self): "[esptool]\n" f"open_port_attempts = {connect_attempts}\n" "connect_attempts = 1\n" - "custom_reset_sequence = D0\n" # Invalid reset sequence to make sure connection fails - ) + "custom_reset_sequence = D0\n" + ) # Invalid reset sequence to make sure connection fails config_file_path = os.path.join(os.getcwd(), "esptool.cfg") with self.ConfigFile(config_file_path, config): - output = self.run_esptool_error("flash_id") + output = self.run_esptool_error("flash-id") assert f"Loaded custom configuration from {config_file_path}" in output assert "Retrying failed connection" in output for _ in range(connect_attempts): assert "Connecting........" in output + + +@pytest.mark.skipif( + "ESPTOOL_TEST_USB_OTG" in os.environ, + reason="May cause port disappearing in USB-OTG mode on CI.", +) +class TestESPObjectOperations(EsptoolTestCase): + def capture_stdout(test_function): + """Decorator to capture stdout.""" + + def wrapper(*args, **kwargs): + with patch("sys.stdout", new=StringIO()) as fake_out: + test_function(*args, **kwargs, fake_out=fake_out) + + return wrapper + + @pytest.mark.skipif( + arg_chip in ["esp32s2", "esp32p4"], + reason="ESP32-S2 attaches the flash even without explicit attach", + ) + @pytest.mark.quick_test + @capture_stdout + def test_attach_flash(self, fake_out): + with esptool.CHIP_DEFS[arg_chip](port=arg_port) as esp: + # Try communicating with the flash chip without attaching it + esp.connect() + flash_id(esp) + read_flash_sfdp(esp, 0, 4) + output_pre = fake_out.getvalue() + # Clear the output buffer + fake_out.truncate(0) + fake_out.seek(0) + # Attach the flash chip and try again + attach_flash(esp) + flash_id(esp) + read_flash_sfdp(esp, 0, 4) + reset_chip(esp, "hard-reset") + output_post = fake_out.getvalue() + assert "Detected flash size: Unknown" in output_pre + assert "Device: ffff" in output_pre or "Device: 0000" in output_pre + assert ( + "SFDP[0..3]: 0xff 0xff 0xff 0xff" in output_pre + or "SFDP[0..3]: 0x00 0x00 0x00 0x00" in output_pre + ) + assert "Detected flash size: Unknown" not in output_post + assert "Device: ffff" not in output_post + assert "SFDP[0..3]: 0x53 0x46 0x44 0x50" in output_post + + @pytest.mark.quick_test + @capture_stdout + def test_stub_run(self, fake_out): + with esptool.CHIP_DEFS[arg_chip](port=arg_port) as esp: + esp.connect() + esp = esp.run_stub() + read_mac(esp) + reset_chip(esp, "hard-reset") + output = fake_out.getvalue() + assert "Stub flasher running" in output + assert "MAC:" in output + + @capture_stdout + def test_flash_operations(self, fake_out): + with detect_chip(port=arg_port) as esp: # Test with chip autodetection + esp = esp.run_stub() + try: + attach_flash(esp) + with open("images/one_kb.bin", "rb") as input: + write_flash(esp, [(0x0, input), (0x2000, input)]) + read_flash(esp, 0x0, 0x2400, "output.bin") + verify_flash(esp, [(0x0, input), (0x2000, input)]) + with open("output.bin", "rb") as output: + verify_flash(esp, [(0x0, output)]) + erase_flash(esp) + read_flash(esp, 0x0, 0x10, "output.bin") + finally: + reset_chip(esp, "hard-reset") + os.remove("output.bin") + output = fake_out.getvalue() + assert "Stub flasher running" in output + assert "Hash of data verified" in output + assert "Read 9216 bytes" in output + assert "Verification successful (digest matched)" in output + assert "Flash memory erased successfully" in output + assert "Hard resetting" in output + + @pytest.mark.quick_test + @pytest.mark.host_test + @capture_stdout + def test_non_esp_operations(self, fake_out): + image_info("images/bootloader_esp32.bin") + with open("images/one_kb.bin", "rb") as input: + try: + merge_bin([(0x0, input), (0x2000, input)], arg_chip, "output.bin") + finally: + os.remove("output.bin") + version() + output = fake_out.getvalue() + assert "Detected image type: ESP32" in output + assert "Checksum: 0x83 (valid)" in output + assert "Wrote 0x2400 bytes to file 'output.bin'" in output + assert esptool.__version__ in output + + +@pytest.mark.host_test +class TestOldScripts: + def test_esptool_py(self): + output = subprocess.check_output(["esptool.py", "-h"]) + decoded = output.decode("utf-8") + assert "esptool.py" in decoded + assert "DEPRECATED" in decoded + + def test_espefuse_py(self): + output = subprocess.check_output(["espefuse.py", "-h"]) + decoded = output.decode("utf-8") + assert "espefuse.py" in decoded + assert "DEPRECATED" in decoded + + def test_espsecure_py(self): + output = subprocess.check_output(["espsecure.py", "-h"]) + decoded = output.decode("utf-8") + assert "espsecure.py" in decoded + assert "DEPRECATED" in decoded + + def test_esp_rfc2217_server_py(self): + output = subprocess.check_output(["esp_rfc2217_server.py", "-h"]) + decoded = output.decode("utf-8") + assert "esp_rfc2217_server.py" in decoded + assert "DEPRECATED" in decoded diff --git a/tools/esptool_py/test/test_esptool_sdm.py b/tools/esptool_py/test/test_esptool_sdm.py index eacf107d59..7e1442501d 100644 --- a/tools/esptool_py/test/test_esptool_sdm.py +++ b/tools/esptool_py/test/test_esptool_sdm.py @@ -1,4 +1,4 @@ -# Unit tests (really integration tests) for esptool.py using the pytest framework +# Unit tests (really integration tests) for esptool using the pytest framework # Uses a device in the Secure Download Mode connected to the serial port. # # RUNNING THIS WILL MESS UP THE DEVICE'S SPI FLASH CONTENTS @@ -8,7 +8,7 @@ # Run with a physical connection to a chip: # - `pytest test_esptool_sdm.py --chip esp32 --port /dev/ttyUSB0 --baud 115200` # -# where - --port - a serial port for esptool.py operation +# where - --port - a serial port for esptool operation # - --chip - ESP chip name # - --baud - baud rate # - --with-trace - trace all interactions (True or False) @@ -22,48 +22,49 @@ class TestSecureDownloadMode(EsptoolTestCase): expected_chip_name = esptool.util.expand_chip_name(arg_chip) + @pytest.mark.skipif( + arg_chip in ("esp8266", "esp32"), + reason="No get-security-info on ESP8266 and ESP32", + ) def test_auto_detect(self): - output = self.run_esptool_error("flash_id", chip="auto") + output = self.run_esptool("get-security-info", chip="auto") - if arg_chip in ["esp32", "esp32s2"]: # no autodetection with get_security_info + if arg_chip == "esp32s2": # no autodetection from security info, only magic no. assert "Secure Download Mode is enabled" in output - assert "Unsupported detection protocol" in output + assert "autodetection will not work" in output else: - assert "Unsupported detection protocol" not in output assert f"Detecting chip type... {self.expected_chip_name}" in output - assert "Stub loader is not supported in Secure Download Mode" in output assert ( - f"Chip is {self.expected_chip_name} in Secure Download Mode" in output + f"{'Chip type:':<20}{self.expected_chip_name} " + "in Secure Download Mode" in output ) # Commands not supported in SDM def test_sdm_incompatible_commands(self): - output = self.run_esptool_error("flash_id") # flash_id - assert "This command (0xa) is not supported in Secure Download Mode" in output + output = self.run_esptool_error("flash-id") # flash-id + assert "The 'flash-id' command is not available" in output - output = self.run_esptool_error("read_flash 0 10 out.bin") # read_flash - assert "This command (0xe) is not supported in Secure Download Mode" in output - - output = self.run_esptool_error("erase_flash") # erase_flash - assert ( - f"{self.expected_chip_name} ROM does not support function erase_flash" - in output - ) + output = self.run_esptool_error("read-flash 0 10 out.bin") # read-flash + assert "The 'read-flash' command is not available" in output # Commands supported in SDM def test_sdm_compatible_commands(self): - output = self.run_esptool("write_flash 0x0 images/one_kb.bin") # write_flash + output = self.run_esptool("write-flash 0x0 images/one_kb.bin") # write-flash assert "Security features enabled, so not changing any flash settings" in output assert "Wrote 1024 bytes" in output assert "Hash of data verified." not in output # Verification not supported output = self.run_esptool_error( - "write_flash --flash_size detect 0x0 images/one_kb.bin" + "write-flash --flash-size detect 0x0 images/one_kb.bin" ) assert ( "Detecting flash size is not supported in secure download mode." in output ) - if arg_chip != "esp32": # esp32 does not support get_security_info - output = self.run_esptool("get_security_info") # get_security_info + output = self.run_esptool("erase-region 0 4096") # erase-region + assert "Stub flasher is not supported in Secure Download Mode" in output + assert "Flash memory region erased successfully" in output + + if arg_chip != "esp32": # esp32 does not support get-security-info + output = self.run_esptool("get-security-info") assert "Security Information:" in output diff --git a/tools/esptool_py/test/test_image_info.py b/tools/esptool_py/test/test_image_info.py index f84c624737..c83ce4367f 100755 --- a/tools/esptool_py/test/test_image_info.py +++ b/tools/esptool_py/test/test_image_info.py @@ -25,8 +25,8 @@ def read_image(filename): @pytest.mark.host_test class TestImageInfo: - def run_image_info(self, chip, file, version=None): - """Runs image_info on a binary file. + def run_image_info(self, chip, file): + """Runs image-info on a binary file. Returns the command output. Filenames are relative to the 'test/images' directory. """ @@ -37,49 +37,27 @@ def run_image_info(self, chip, file, version=None): "esptool", "--chip", chip, - "image_info", + "image-info", ] - if version is not None: - cmd += ["--version", str(version)] # if path was passed use the whole path # if file does not exists try to use file from IMAGES_DIR directory cmd += [file] if os.path.isfile(file) else ["".join([IMAGES_DIR, os.sep, file])] print("\nExecuting {}".format(" ".join(cmd))) try: - output = subprocess.check_output(cmd) + output = subprocess.check_output(cmd, stderr=subprocess.STDOUT) output = output.decode("utf-8") print(output) # for more complete stdout logs on failure - assert ( - "warning" not in output.lower() - ), "image_info should not output warnings" + assert "warning" not in output.lower(), ( + "image-info should not output warnings" + ) return output except subprocess.CalledProcessError as e: print(e.output) raise - def test_v1_esp32(self): - out = self.run_image_info("esp32", "bootloader_esp32.bin") - assert "Entry point: 4009816c" in out, "Wrong entry point" - assert "Checksum: 83 (valid)" in out, "Invalid checksum" - assert "4 segments" in out, "Wrong number of segments" - assert ( - "Segment 3: len 0x01068 load 0x40078000 file_offs 0x00000b64 [CACHE_APP]" - in out - ), "Wrong segment info" - - def test_v1_esp8266(self): - out = self.run_image_info("esp8266", ESP8266_BIN) - assert "Image version: 1" in out, "Wrong image version" - assert "Entry point: 40101844" in out, "Wrong entry point" - assert "Checksum: 6b (valid)" in out, "Invalid checksum" - assert "1 segments" in out, "Wrong number of segments" - assert ( - "Segment 1: len 0x00014 load 0x40100000 file_offs 0x00000008 [IRAM]" in out - ), "Wrong segment info" - - def test_v2_esp32c3(self): - out = self.run_image_info("esp32c3", "bootloader_esp32c3.bin", "2") + def test_esp32c3(self): + out = self.run_image_info("esp32c3", "bootloader_esp32c3.bin") # Header assert "Entry point: 0x403c0000" in out, "Wrong entry point" @@ -100,9 +78,9 @@ def test_v2_esp32c3(self): assert "Maximal chip revision: v0.0" in out, "Wrong min revision" # Segments - assert ( - "1 0x01864 0x3fcd6114 0x00000034 DRAM, BYTE_ACCESSIBLE" in out - ), "Wrong segment info" + assert "1 0x01864 0x3fcd6114 0x00000034 DRAM, BYTE_ACCESSIBLE" in out, ( + "Wrong segment info" + ) # Footer assert "Checksum: 0x77 (valid)" in out, "Invalid checksum" @@ -117,8 +95,8 @@ def test_v2_esp32c3(self): if ex_hdr[15] == 1: # Hash appended assert "Validation hash: 4faeab1bd3fd" in out, "Invalid hash" - def test_v2_esp8266(self): - out = self.run_image_info("esp8266", ESP8266_BIN, "2") + def test_esp8266(self): + out = self.run_image_info("esp8266", ESP8266_BIN) assert "Image version: 1" in out, "Wrong image version" assert "Entry point: 0x40101844" in out, "Wrong entry point" assert "Flash size: 512KB" in out, "Wrong flash size" @@ -129,46 +107,41 @@ def test_v2_esp8266(self): assert "0 0x00014 0x40100000 0x00000008 IRAM" in out, "Wrong segment info" def test_image_type_detection(self): - # ESP8266, version 1 and 2 - out = self.run_image_info("auto", ESP8266_BIN, "1") - assert "Detected image type: ESP8266" in out - assert "Segment 1: len 0x00014" in out - out = self.run_image_info("auto", ESP8266_BIN, "2") + # ESP8266 + out = self.run_image_info("auto", ESP8266_BIN) assert "Detected image type: ESP8266" in out assert "Flash freq: 40m" in out - out = self.run_image_info("auto", "esp8266_deepsleep.bin", "2") + out = self.run_image_info("auto", "esp8266_deepsleep.bin") assert "Detected image type: ESP8266" in out # ESP32, with and without detection - out = self.run_image_info("auto", "bootloader_esp32.bin", "2") + out = self.run_image_info("auto", "bootloader_esp32.bin") assert "Detected image type: ESP32" in out - out = self.run_image_info( - "auto", "ram_helloworld/helloworld-esp32_edit.bin", "2" - ) + out = self.run_image_info("auto", "ram_helloworld/helloworld-esp32_edit.bin") assert "Detected image type: ESP32" in out - out = self.run_image_info("esp32", "bootloader_esp32.bin", "2") + out = self.run_image_info("esp32", "bootloader_esp32.bin") assert "Detected image type: ESP32" not in out # ESP32-C3 - out = self.run_image_info("auto", "bootloader_esp32c3.bin", "2") + out = self.run_image_info("auto", "bootloader_esp32c3.bin") assert "Detected image type: ESP32-C3" in out # ESP32-S3 - out = self.run_image_info("auto", "esp32s3_header.bin", "2") + out = self.run_image_info("auto", "esp32s3_header.bin") assert "Detected image type: ESP32-S3" in out def test_invalid_image_type_detection(self, capsys): with pytest.raises(subprocess.CalledProcessError): # Invalid image - self.run_image_info("auto", "one_kb.bin", "2") + self.run_image_info("auto", "one_kb.bin") assert ( "This is not a valid image (invalid magic number: 0xed)" in capsys.readouterr().out ) def test_application_info(self): - out = self.run_image_info("auto", "esp_idf_blink_esp32s2.bin", "2") - assert "Application information" in out + out = self.run_image_info("auto", "esp_idf_blink_esp32s2.bin") + assert "Application Information" in out assert "Project name: blink" in out assert "App version: qa-test-v5.0-20220830-4-g4532e6" in out assert "Secure version: 0" in out @@ -178,25 +151,25 @@ def test_application_info(self): assert "cd0dab311febb0a3ea79eaa223ac2b0" in out assert "ESP-IDF: v5.0-beta1-427-g4532e6e0b2-dirt" in out # No application info in image - out = self.run_image_info("auto", "bootloader_esp32.bin", "2") - assert "Application information" not in out - out = self.run_image_info("auto", ESP8266_BIN, "2") - assert "Application information" not in out + out = self.run_image_info("auto", "bootloader_esp32.bin") + assert "Application Information" not in out + out = self.run_image_info("auto", ESP8266_BIN) + assert "Application Information" not in out def test_bootloader_info(self): # This bootloader binary is built from "hello_world" project # with default settings, IDF version is v5.2. - out = self.run_image_info("esp32", "bootloader_esp32_v5_2.bin", "2") - assert "File size: 26768 (bytes)" in out - assert "Bootloader information" in out + out = self.run_image_info("esp32", "bootloader_esp32_v5_2.bin") + assert "Image size: 26768 bytes" in out + assert "Bootloader Information" in out assert "Bootloader version: 1" in out assert "ESP-IDF: v5.2-dev-254-g1950b15" in out assert "Compile time: Apr 25 2023 00:13:32" in out def test_intel_hex(self): - # This bootloader binary is built from "hello_world" project - # with default settings, IDF version is v5.2. - # File is converted to Intel Hex using merge_bin + # Convert and merge two files to Intel Hex using merge-bin + # Run image-info on the resulting Intel Hex file + # Verify that image info is shown for both files def convert_bin2hex(file): subprocess.check_output( @@ -205,12 +178,14 @@ def convert_bin2hex(file): "-m", "esptool", "--chip", - "esp32", - "merge_bin", + "esp32c3", + "merge-bin", "--format", "hex", "0x0", - "".join([IMAGES_DIR, os.sep, "bootloader_esp32_v5_2.bin"]), + os.path.join(IMAGES_DIR, "bootloader_esp32c3.bin"), + "0x8000", + os.path.join(IMAGES_DIR, "esp32c3_header_min_rev.bin"), "-o", file, ] @@ -219,12 +194,13 @@ def convert_bin2hex(file): fd, file = tempfile.mkstemp(suffix=".hex") try: convert_bin2hex(file) - out = self.run_image_info("esp32", file, "2") - assert "File size: 26768 (bytes)" in out - assert "Bootloader information" in out - assert "Bootloader version: 1" in out - assert "ESP-IDF: v5.2-dev-254-g1950b15" in out - assert "Compile time: Apr 25 2023 00:13:32" in out + out = self.run_image_info("esp32c3", file) + assert ( + "Merged binary image detected. Processing each file individually." + in out + ) + assert "Processing file 1/2, offset: 0x0, size: 17744 bytes" in out + assert "Processing file 2/2, offset: 0x8000, size: 48 bytes" in out finally: try: # make sure that file was closed before removing it diff --git a/tools/esptool_py/test/test_imagegen.py b/tools/esptool_py/test/test_imagegen.py index 5dfbce29c7..ea17e18d8e 100755 --- a/tools/esptool_py/test/test_imagegen.py +++ b/tools/esptool_py/test/test_imagegen.py @@ -5,6 +5,7 @@ import struct import subprocess import sys +import math from conftest import need_to_install_package_err @@ -77,9 +78,9 @@ def assertImageDoesNotContainSection(self, image, elf, section_name): print( f"comparing seg {seg.addr:#x} sec {sh_addr:#x} len {len(data):#x}" ) - assert ( - seg.addr != sh_addr - ), f"{section_name} should not be in the binary image" + assert seg.addr != sh_addr, ( + f"{section_name} should not be in the binary image" + ) def assertImageContainsSection(self, image, elf, section_name): """ @@ -102,9 +103,9 @@ def assertImageContainsSection(self, image, elf, section_name): ) if seg.addr == sh_addr: overlap_len = min(len(seg.data), len(data)) - assert ( - data[:overlap_len] == seg.data[:overlap_len] - ), f"ELF '{section_name}' section has mis-matching bin image data" + assert data[:overlap_len] == seg.data[:overlap_len], ( + f"ELF '{section_name}' section has mis-matching bin image data" + ) sh_addr += overlap_len data = data[overlap_len:] @@ -116,10 +117,10 @@ def assertImageContainsSection(self, image, elf, section_name): def assertImageInfo(self, binpath, chip="esp8266", assert_sha=False): """ - Run esptool.py image_info on a binary file, + Run esptool image-info on a binary file, assert no red flags about contents. """ - cmd = [sys.executable, "-m", "esptool", "--chip", chip, "image_info", binpath] + cmd = [sys.executable, "-m", "esptool", "--chip", chip, "image-info", binpath] try: output = subprocess.check_output(cmd) output = output.decode("utf-8") @@ -127,18 +128,20 @@ def assertImageInfo(self, binpath, chip="esp8266", assert_sha=False): except subprocess.CalledProcessError as e: print(e.output) raise - assert re.search( - r"Checksum: [a-fA-F0-9]{2} \(valid\)", output - ), "Checksum calculation should be valid" + assert re.search(r"Checksum: 0x[a-fA-F0-9]{2} \(valid\)", output), ( + "Checksum calculation should be valid" + ) if assert_sha: - assert re.search( - r"Validation Hash: [a-fA-F0-9]{64} \(valid\)", output - ), "SHA256 should be valid" - assert ( - "warning" not in output.lower() - ), "Should be no warnings in image_info output" - - def run_elf2image(self, chip, elf_path, version=None, extra_args=[]): + assert re.search(r"Validation hash: [a-fA-F0-9]{64} \(valid\)", output), ( + "SHA256 should be valid" + ) + assert "warning" not in output.lower(), ( + "Should be no warnings in image-info output" + ) + + def run_elf2image( + self, chip, elf_path, version=None, extra_args=[], allow_warnings=False + ): """Run elf2image on elf_path""" cmd = [sys.executable, "-m", "esptool", "--chip", chip, "elf2image"] if version is not None: @@ -146,12 +149,13 @@ def run_elf2image(self, chip, elf_path, version=None, extra_args=[]): cmd += [elf_path] + extra_args print("\nExecuting {}".format(" ".join(cmd))) try: - output = subprocess.check_output(cmd) + output = subprocess.check_output(cmd, stderr=subprocess.STDOUT) output = output.decode("utf-8") print(output) - assert ( - "warning" not in output.lower() - ), "elf2image should not output warnings" + if not allow_warnings: + assert "warning" not in output.lower(), ( + "elf2image should not output warnings" + ) except subprocess.CalledProcessError as e: print(e.output) raise @@ -169,17 +173,17 @@ def setup_class(self): @classmethod def teardown_class(self): - super(TestESP8266V1Image, self).teardown_class() try_delete(self.BIN_LOAD) try_delete(self.BIN_IROM) + super(TestESP8266V1Image, self).teardown_class() def test_irom_bin(self): with open(self.ELF, "rb") as f: e = ELFFile(f) irom_section = e.get_section_by_name(".irom0.text") - assert ( - irom_section.header.sh_size == os.stat(self.BIN_IROM).st_size - ), "IROM raw binary file should be same length as .irom0.text section" + assert irom_section.header.sh_size == os.stat(self.BIN_IROM).st_size, ( + "IROM raw binary file should be same length as .irom0.text section" + ) def test_loaded_sections(self): image = esptool.bin_image.LoadFirmwareImage("esp8266", self.BIN_LOAD) @@ -326,11 +330,11 @@ def test_use_segments(self): # this ELF will produce 8 segments in the bin image = self._test_elf2image(ELF, BIN) # Adjacent sections are now merged, len(image.segments) should - # equal 5 (instead of 8). - assert len(image.segments) == 5 + # equal 4 (instead of 8). + assert len(image.segments) == 4 - # --use_segments uses ELF segments(phdrs), produces just 2 segments in the bin - image = self._test_elf2image(ELF, BIN, ["--use_segments"]) + # --use-segments uses ELF segments(phdrs), produces just 2 segments in the bin + image = self._test_elf2image(ELF, BIN, ["--use-segments"]) assert len(image.segments) == 2 def test_ram_only_header(self): @@ -350,7 +354,7 @@ def test_2mb(self): "esp8266", ELF, version=2, - extra_args=["--flash_size", "2MB", "--flash_mode", "dio"], + extra_args=["--flash-size", "2MB", "--flash-mode", "dio"], ) with open(BIN, "rb") as f: header = f.read(4) @@ -371,9 +375,9 @@ def test_16mb(self): "esp32", ELF, extra_args=[ - "--flash_size", + "--flash-size", "16MB", - "--flash_mode", + "--flash-mode", "dio", "--min-rev", "1", @@ -390,15 +394,16 @@ def test_16mb(self): class TestELFSHA256(BaseTestCase): - ELF = "esp32-app-cust-ver-info.elf" + ELF = "esp32c6-appdesc.elf" SHA_OFFS = 0xB0 # absolute offset of the SHA in the .bin file - BIN = "esp32-app-cust-ver-info.bin" + BIN = "esp32c6-appdesc.bin" """ - esp32-app-cust-ver-info.elf was built with the following application version info: + esp32c6-appdesc.elf was built with the following application version info: - const __attribute__((section(".rodata_desc"))) esp_app_desc_t esp_app_desc = { - .magic_word = 0xffffffff, + __attribute__((section(".flash.appdesc"))) + esp_app_desc_t my_app_desc = { + .magic_word = 0xABCD5432, .secure_version = 0xffffffff, .reserv1 = {0xffffffff, 0xffffffff}, .version = "_______________________________", @@ -406,21 +411,28 @@ class TestELFSHA256(BaseTestCase): .time = "xxxxxxxxxxxxxxx", .date = "yyyyyyyyyyyyyyy", .idf_ver = "zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz", - .app_elf_sha256 = - {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - .reserv2 = {0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, - 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, - 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, - 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff}, + .app_elf_sha256 = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }, + .min_efuse_blk_rev_full = 0xffff, + .max_efuse_blk_rev_full = 0xffff, + .mmu_page_size = 0, + .reserv3 = {0xff, 0xff, 0xff}, + .reserv2 = { + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff + }, }; This leaves zeroes only for the fields of SHA-256 and the test will fail if the placement of zeroes are tested at the wrong place. - 00000000: e907 0020 780f 0840 ee00 0000 0000 0000 ... x..@........ - 00000010: 0000 0000 0000 0001 2000 403f 605a 0000 ........ .@?`Z.. - 00000020: ffff ffff ffff ffff ffff ffff ffff ffff ................ + 00000000: e901 0000 2000 0042 ee00 0000 0d00 0000 .... ..B........ + 00000010: 00ff ff00 0000 0001 2000 0042 0001 0000 ........ ..B.... + 00000020: 3254 cdab ffff ffff ffff ffff ffff ffff 2T.............. 00000030: 5f5f 5f5f 5f5f 5f5f 5f5f 5f5f 5f5f 5f5f ________________ 00000040: 5f5f 5f5f 5f5f 5f5f 5f5f 5f5f 5f5f 5f00 _______________. 00000050: 2d2d 2d2d 2d2d 2d2d 2d2d 2d2d 2d2d 2d2d ---------------- @@ -431,37 +443,46 @@ class TestELFSHA256(BaseTestCase): 000000a0: 7a7a 7a7a 7a7a 7a7a 7a7a 7a7a 7a7a 7a00 zzzzzzzzzzzzzzz. 000000b0: 0000 0000 0000 0000 0000 0000 0000 0000 ................ SHA-256 here 000000c0: 0000 0000 0000 0000 0000 0000 0000 0000 ................ - 000000d0: ffff ffff ffff ffff ffff ffff ffff ffff ................ + 000000d0: ffff ffff 00ff ffff ffff ffff ffff ffff ................ 000000e0: ffff ffff ffff ffff ffff ffff ffff ffff ................ 000000f0: ffff ffff ffff ffff ffff ffff ffff ffff ................ 00000100: ffff ffff ffff ffff ffff ffff ffff ffff ................ 00000110: ffff ffff ffff ffff ffff ffff ffff ffff ................ - 00000120: 6370 755f 7374 6172 7400 0000 1b5b 303b cpu_start....[0; """ - def test_binary_patched(self): + def verify_sha256(self, elf_path, bin_path): + image = esptool.bin_image.LoadFirmwareImage("esp32c6", bin_path) + rodata_segment = image.segments[0] + bin_sha256 = rodata_segment.data[ + self.SHA_OFFS - 0x20 : self.SHA_OFFS - 0x20 + 32 + ] # subtract 0x20 byte header here + + with open(elf_path, "rb") as f: + elf_computed_sha256 = hashlib.sha256(f.read()).digest() + + with open(bin_path, "rb") as f: + f.seek(self.SHA_OFFS) + bin_sha256_raw = f.read(len(elf_computed_sha256)) + + assert elf_computed_sha256 == bin_sha256 + assert elf_computed_sha256 == bin_sha256_raw + + def test_binary_patched_parameter(self): try: self.run_elf2image( - "esp32", + "esp32c6", self.ELF, extra_args=["--elf-sha256-offset", f"{self.SHA_OFFS:#x}"], ) - image = esptool.bin_image.LoadFirmwareImage("esp32", self.BIN) - rodata_segment = image.segments[0] - bin_sha256 = rodata_segment.data[ - self.SHA_OFFS - 0x20 : self.SHA_OFFS - 0x20 + 32 - ] # subtract 0x20 byte header here - - with open(self.ELF, "rb") as f: - elf_computed_sha256 = hashlib.sha256(f.read()).digest() - - with open(self.BIN, "rb") as f: - f.seek(self.SHA_OFFS) - bin_sha256_raw = f.read(len(elf_computed_sha256)) + self.verify_sha256(self.ELF, self.BIN) + finally: + try_delete(self.BIN) - assert elf_computed_sha256 == bin_sha256 - assert elf_computed_sha256 == bin_sha256_raw + def test_binary_patched(self): + try: + self.run_elf2image("esp32c6", self.ELF) + self.verify_sha256(self.ELF, self.BIN) finally: try_delete(self.BIN) @@ -516,3 +537,99 @@ def test_hash_append(self): assert bin_without_hash[self.HASH_APPEND_OFFSET] == 0 assert bytes(expected_bin_without_hash) == bin_without_hash + + +class TestMMUPageSize(BaseTestCase): + def test_appdesc_aligned(self, capsys): + ELF = "esp32c6-appdesc.elf" + BIN = "esp32c6-appdesc.bin" + try: + self.run_elf2image("esp32c6", ELF) + output = capsys.readouterr().out + print(output) + assert "MMU page size not specified, set to 64 KB" in output + finally: + try_delete(BIN) + + @staticmethod + def _modify_section_address(elf_path, section_name, address_offset): + with open(elf_path, "rb+") as f: + elf = ELFFile(f) + section = elf.get_section_by_name(section_name) + + if not section: + raise ValueError(f"Section {section_name} not found") + + # This finds the index of the specified section in the ELF file, + # compute the section header’s position in the file (using section index, + # the section header table’s offset and section header entry size) and then + # modify the section’s address in memory by the specified offset. + index = elf.get_section_index(section_name) + sh_entry_offset = elf.header["e_shoff"] + index * elf.header["e_shentsize"] + section.header.sh_addr += address_offset + + # Write modified header to file + f.seek(sh_entry_offset) + f.write(elf.structs.Elf_Shdr.build(section.header)) + + def test_appdesc_not_aligned(self, capsys): + ELF = "esp32c6-appdesc.elf" + BIN = "esp32c6-appdesc.bin" + ADDRESS_OFFSET = 4 # 4 bytes is minimum allowed + + self._modify_section_address(ELF, ".flash.appdesc", ADDRESS_OFFSET) + try: + self.run_elf2image("esp32c6", ELF, allow_warnings=True) + output = capsys.readouterr().out + print(output) + assert ( + "App description segment is not aligned to MMU page size, probably " + "linker script issue or wrong MMU page size. " + "Try to set MMU page size parameter manually." in output + ) + finally: + # Restore original address to be able to run other tests + self._modify_section_address(ELF, ".flash.appdesc", -ADDRESS_OFFSET) + try_delete(BIN) + + @staticmethod + def _change_appdesc_mmu_page_size(elf_path, mmu_page_size): + """ + Change the MMU page size in the appdesc section of the ELF file. + The following values can be chosen: 0 (empty), 8192, 16384, 32768, 65536. + The numbers are not valid for all chips, so refer to the documentation + of the chip being used. + """ + with open(elf_path, "rb+") as f: + elf = ELFFile(f) + section = elf.get_section_by_name(".flash.appdesc") + + if not section: + raise ValueError("Section .flash.appdesc not found") + + # The mmu_page_size is a power of 2, so we convert it to the corresponding + # value for the appdesc section. It can also be empty (0). + if mmu_page_size == 0: + mmu_page_size_appdesc = 0 + else: + mmu_page_size_appdesc = int(math.log2(mmu_page_size)) + + # Go to the mmu_page_size field in the appdesc section (at offset 0xB4) and + # modify it + f.seek(section.header.sh_offset + 0xB4) + f.write(mmu_page_size_appdesc.to_bytes(4, byteorder="little")) + + def test_appdesc_data(self, capsys): + ELF = "esp32c6-appdesc.elf" + BIN = "esp32c6-appdesc.bin" + MMU_PAGE_SIZE = 65536 + + self._change_appdesc_mmu_page_size(ELF, MMU_PAGE_SIZE) + try: + self.run_elf2image("esp32c6", ELF) + output = capsys.readouterr().out + assert "MMU page size" not in output + print(output) + finally: + self._change_appdesc_mmu_page_size(ELF, 0) + try_delete(BIN) diff --git a/tools/esptool_py/test/test_logger.py b/tools/esptool_py/test/test_logger.py new file mode 100644 index 0000000000..aebe3c7bf1 --- /dev/null +++ b/tools/esptool_py/test/test_logger.py @@ -0,0 +1,154 @@ +import pytest +from io import StringIO +from unittest.mock import patch +from esptool import __version__ +from esptool.logger import EsptoolLogger, log, TemplateLogger +from esptool.cmds import version + + +# Custom logger that implements all methods +class CustomLogger(TemplateLogger): + def print(self, *args, **kwargs): + print("Custom logger:", *args, **kwargs) + + def note(self, message: str): + """ + Logs a Note: message. + """ + pass + + def warning(self, message: str): + """ + Logs a Warning: message. + """ + pass + + def error(self, message: str): + """ + Logs an error message. + """ + pass + + def stage(self, finish=False): + pass + + def progress_bar( + self, + cur_iter: int, + total_iters: int, + prefix: str = "", + suffix: str = "", + bar_length: int = 30, + ): + pass + + def set_verbosity(self, verbosity: str): + pass + + +# Custom logger that doesn't implement all methods +class CustomLoggerIncomplete: + def print(self, *args, **kwargs): + pass + + +@pytest.mark.host_test +class TestLogger: + @pytest.fixture + def logger(self): + log = EsptoolLogger() + log._set_smart_features(True) + return log + + def test_singleton(self, logger): + logger2 = EsptoolLogger() + assert logger is logger2 + assert logger is log + + def test_print(self, logger): + with patch("sys.stdout", new=StringIO()) as fake_out: + logger.print("With newline") + logger.print("Without newline", end="") + assert fake_out.getvalue() == "With newline\nWithout newline" + + def test_note_message(self, logger): + with patch("sys.stdout", new=StringIO()) as fake_out: + logger.note("This is a note") + assert ( + fake_out.getvalue() + == f"{logger.ansi_blue}Note:{logger.ansi_normal} This is a note\n" + ) + + def test_warning_message(self, logger): + with patch("sys.stdout", new=StringIO()) as fake_out: + logger.warning("This is a warning") + assert ( + fake_out.getvalue() + == f"{logger.ansi_yellow}Warning:{logger.ansi_normal} " + "This is a warning\n" + ) + + def test_error_message(self, logger): + with patch("sys.stderr", new=StringIO()) as fake_out: + logger.error("This is an error") + assert ( + fake_out.getvalue() + == f"{logger.ansi_red}This is an error{logger.ansi_normal}\n" + ) + + def test_stage(self, logger): + with patch("sys.stdout", new=StringIO()) as fake_out: + logger.stage() + assert logger._stage_active + logger.print("Line1") + logger.print("Line2") + logger.stage(finish=True) + assert not logger._stage_active + logger.print("Line3") + + output = fake_out.getvalue() + assert f"{logger.ansi_line_up}{logger.ansi_line_clear}" * 2 in output + assert "Line1\nLine2\n" in output + assert "Line1\nLine2\nLine3\n" not in output + + def test_progress_bar(self, logger): + with patch("sys.stdout", new=StringIO()) as fake_out: + logger.progress_bar( + cur_iter=2, + total_iters=4, + prefix="Progress: ", + suffix=" (2/4)", + bar_length=10, + ) + logger.progress_bar( + cur_iter=4, + total_iters=4, + prefix="Progress: ", + suffix=" (4/4)", + bar_length=10, + ) + output = fake_out.getvalue() + assert "Progress: [====> ] 50.0% (2/4)" in output + assert "Progress: [==========] 100.0% (4/4) \n" in output + + def test_set_incomplete_logger(self, logger): + with pytest.raises( + TypeError, + match="'CustomLoggerIncomplete' object layout differs from 'EsptoolLogger'", + ): + logger.set_logger(CustomLoggerIncomplete()) + + def test_set_logger(self, logger): + # Original logger + with patch("sys.stdout", new=StringIO()) as fake_out: + version() # This will log.print the estool version + output = fake_out.getvalue() + assert output == f"{__version__}\n" + + # Replace logger with custom one + with patch("sys.stdout", new=StringIO()) as fake_out: + logger.set_logger(CustomLogger()) + assert isinstance(logger, CustomLogger) + version() # This will use print from CustomLogger + output = fake_out.getvalue() + assert output == f"Custom logger: {__version__}\n" diff --git a/tools/esptool_py/test/test_merge_bin.py b/tools/esptool_py/test/test_merge_bin.py index 75df9bc8b6..d61b902a71 100755 --- a/tools/esptool_py/test/test_merge_bin.py +++ b/tools/esptool_py/test/test_merge_bin.py @@ -32,7 +32,7 @@ def read_image(filename): @pytest.mark.host_test class TestMergeBin: def run_merge_bin(self, chip, offsets_names, options=[], allow_warnings=False): - """Run merge_bin on a list of (offset, filename) tuples + """Run merge-bin on a list of (offset, filename) tuples with output to a named temporary file. Filenames are relative to the 'test/images' directory. @@ -49,7 +49,7 @@ def run_merge_bin(self, chip, offsets_names, options=[], allow_warnings=False): "esptool", "--chip", chip, - "merge_bin", + "merge-bin", "-o", output_file.name, ] + options @@ -63,9 +63,9 @@ def run_merge_bin(self, chip, offsets_names, options=[], allow_warnings=False): output = output.decode("utf-8") print(output) if not allow_warnings: - assert ( - "warning" not in output.lower() - ), "merge_bin should not output warnings" + assert "warning" not in output.lower(), ( + "merge-bin should not output warnings" + ) with open(output_file.name, "rb") as f: return f.read() @@ -78,7 +78,7 @@ def run_merge_bin(self, chip, offsets_names, options=[], allow_warnings=False): def assertAllFF(self, some_bytes): # this may need some improving as the failed assert messages may be # very long and/or useless! - assert b"\xFF" * len(some_bytes) == some_bytes + assert b"\xff" * len(some_bytes) == some_bytes def test_simple_merge(self): merged = self.run_merge_bin( @@ -127,7 +127,7 @@ def test_update_bootloader_params(self): (0x1000, "bootloader_esp32.bin"), (0x10000, "ram_helloworld/helloworld-esp32.bin"), ], - ["--flash_size", "2MB", "--flash_mode", "dout"], + ["--flash-size", "2MB", "--flash-mode", "dout"], ) self.assertAllFF(merged[:0x1000]) @@ -169,9 +169,9 @@ def test_target_offset(self): assert helloworld == merged[0xF000 : 0xF000 + len(helloworld)] self.assertAllFF(merged[0x1000 + len(bootloader) : 0xF000]) - def test_fill_flash_size(self): + def test_pad_to_size(self): merged = self.run_merge_bin( - "esp32c3", [(0x0, "bootloader_esp32c3.bin")], ["--fill-flash-size", "4MB"] + "esp32c3", [(0x0, "bootloader_esp32c3.bin")], ["--pad-to-size", "4MB"] ) bootloader = read_image("bootloader_esp32c3.bin") @@ -179,14 +179,14 @@ def test_fill_flash_size(self): assert bootloader == merged[: len(bootloader)] self.assertAllFF(merged[len(bootloader) :]) - def test_fill_flash_size_w_target_offset(self): + def test_pad_to_size_w_target_offset(self): merged = self.run_merge_bin( "esp32", [ (0x1000, "bootloader_esp32.bin"), (0x10000, "ram_helloworld/helloworld-esp32.bin"), ], - ["--target-offset", "0x1000", "--fill-flash-size", "2MB"], + ["--target-offset", "0x1000", "--pad-to-size", "2MB"], ) # full length is without target-offset arg @@ -215,7 +215,7 @@ def test_merge_mixed(self): merged = self.run_merge_bin( "esp32", [(0x1000, f.name), (0x10000, "ram_helloworld/helloworld-esp32.bin")], - ["--target-offset", "0x1000", "--fill-flash-size", "2MB"], + ["--target-offset", "0x1000", "--pad-to-size", "2MB"], ) finally: os.unlink(f.name) @@ -262,7 +262,7 @@ def test_merge_bin2hex(self): ) source = read_image("bootloader_esp32.bin") # verify that padding was done correctly - assert b"\xFF" * 0x1000 == merged_bin[:0x1000] + assert b"\xff" * 0x1000 == merged_bin[:0x1000] # verify the file itself assert source == merged_bin[0x1000:] @@ -356,7 +356,7 @@ def generate_uf2( "esptool", "--chip", chip_id, - "merge_bin", + "merge-bin", "--format", "uf2", "-o", @@ -372,11 +372,13 @@ def generate_uf2( output = subprocess.check_output(com_args + file_args, stderr=subprocess.STDOUT) output = output.decode("utf-8") print(output) - assert "warning" not in output.lower(), "merge_bin should not output warnings" + assert "warning" not in output.lower(), "merge-bin should not output warnings" - exp_list = [f"Adding {f} at {hex(addr)}" for addr, f in iter_addr_offset_tuples] + exp_list = [ + f"Adding '{f}' at {hex(addr)}" for addr, f in iter_addr_offset_tuples + ] exp_list += [ - f"bytes to file {of_name}, ready to be flashed with any ESP USB Bridge" + f"bytes to file '{of_name}', ready to be flashed with any ESP USB Bridge" ] for e in exp_list: assert e in output diff --git a/tools/esptool_py/test/test_modules.py b/tools/esptool_py/test/test_modules.py index c0bf8de851..547298efea 100755 --- a/tools/esptool_py/test/test_modules.py +++ b/tools/esptool_py/test/test_modules.py @@ -1,5 +1,5 @@ # Tests for regressions in python modules -# used by esptool.py, espefuse.py, and espsecure.py +# used by esptool, espefuse, and espsecure import pytest diff --git a/tools/esptool_py/flasher_stub/Makefile b/tools/flasher_stub/Makefile similarity index 83% rename from tools/esptool_py/flasher_stub/Makefile rename to tools/flasher_stub/Makefile index 1d78f4d0cc..588d976266 100644 --- a/tools/esptool_py/flasher_stub/Makefile +++ b/tools/flasher_stub/Makefile @@ -50,27 +50,29 @@ ifneq ("$(V)","1") Q = @ endif -STUB = stub_flasher +STUB = esp SRCS = stub_flasher.c slip.c stub_commands.c stub_write_flash.c stub_io.c SRCS_8266 = miniz.c BUILD_DIR = build -ESPTOOL_STUBS_DIR = ../esptool/targets/stub_flasher - -STUB_ELF_8266 = $(BUILD_DIR)/$(STUB)_8266.elf -STUB_ELF_32 = $(BUILD_DIR)/$(STUB)_32.elf -STUB_ELF_32S2 = $(BUILD_DIR)/$(STUB)_32s2.elf -STUB_ELF_32S3_BETA_2 = $(BUILD_DIR)/$(STUB)_32s3beta2.elf -STUB_ELF_32S3 = $(BUILD_DIR)/$(STUB)_32s3.elf -STUB_ELF_32C3 = $(BUILD_DIR)/$(STUB)_32c3.elf -STUB_ELF_32C6BETA = $(BUILD_DIR)/$(STUB)_32c6beta.elf -STUB_ELF_32H2_BETA_1 = $(BUILD_DIR)/$(STUB)_32h2beta1.elf -STUB_ELF_32H2_BETA_2 = $(BUILD_DIR)/$(STUB)_32h2beta2.elf -STUB_ELF_32C2 = $(BUILD_DIR)/$(STUB)_32c2.elf -STUB_ELF_32C6 = $(BUILD_DIR)/$(STUB)_32c6.elf -STUB_ELF_32C5_BETA_3 = $(BUILD_DIR)/$(STUB)_32c5beta3.elf -STUB_ELF_32H2 = $(BUILD_DIR)/$(STUB)_32h2.elf -STUB_ELF_32P4 = $(BUILD_DIR)/$(STUB)_32p4.elf +ESPTOOL_STUBS_DIR = .. + +STUB_ELF_8266 = $(BUILD_DIR)/$(STUB)8266.elf +STUB_ELF_32 = $(BUILD_DIR)/$(STUB)32.elf +STUB_ELF_32S2 = $(BUILD_DIR)/$(STUB)32s2.elf +STUB_ELF_32S3_BETA_2 = $(BUILD_DIR)/$(STUB)32s3beta2.elf +STUB_ELF_32S3 = $(BUILD_DIR)/$(STUB)32s3.elf +STUB_ELF_32C3 = $(BUILD_DIR)/$(STUB)32c3.elf +STUB_ELF_32C6BETA = $(BUILD_DIR)/$(STUB)32c6beta.elf +STUB_ELF_32H2_BETA_1 = $(BUILD_DIR)/$(STUB)32h2beta1.elf +STUB_ELF_32H2_BETA_2 = $(BUILD_DIR)/$(STUB)32h2beta2.elf +STUB_ELF_32C2 = $(BUILD_DIR)/$(STUB)32c2.elf +STUB_ELF_32C6 = $(BUILD_DIR)/$(STUB)32c6.elf +STUB_ELF_32C61 = $(BUILD_DIR)/$(STUB)32c61.elf +STUB_ELF_32C5 = $(BUILD_DIR)/$(STUB)32c5.elf +STUB_ELF_32C5_BETA_3 = $(BUILD_DIR)/$(STUB)32c5beta3.elf +STUB_ELF_32H2 = $(BUILD_DIR)/$(STUB)32h2.elf +STUB_ELF_32P4 = $(BUILD_DIR)/$(STUB)32p4.elf STUBS_ELF = ifneq ($(WITHOUT_ESP8266),1) @@ -94,6 +96,8 @@ STUBS_ELF += \ $(STUB_ELF_32H2_BETA_2) \ $(STUB_ELF_32C2) \ $(STUB_ELF_32C6) \ + $(STUB_ELF_32C61) \ + $(STUB_ELF_32C5) \ $(STUB_ELF_32C5_BETA_3) \ $(STUB_ELF_32H2) \ $(STUB_ELF_32P4) @@ -171,6 +175,14 @@ $(STUB_ELF_32C6): $(SRCS) $(BUILD_DIR) ld/stub_32c6.ld @echo " CC(32C6) $^ -> $@" $(Q) $(CROSS_ESPRISCV32)gcc $(CFLAGS_ESPRISCV32) -DESP32C6=1 -Tstub_32c6.ld -Wl,-Map=$(@:.elf=.map) -o $@ $(filter %.c, $^) $(LDLIBS) +$(STUB_ELF_32C61): $(SRCS) $(BUILD_DIR) ld/stub_32c61.ld + @echo " CC(32C61) $^ -> $@" + $(Q) $(CROSS_ESPRISCV32)gcc $(CFLAGS_ESPRISCV32) -DESP32C61=1 -Tstub_32c61.ld -Wl,-Map=$(@:.elf=.map) -o $@ $(filter %.c, $^) $(LDLIBS) + +$(STUB_ELF_32C5): $(SRCS) $(BUILD_DIR) ld/stub_32c5.ld + @echo " CC(32C5) $^ -> $@" + $(Q) $(CROSS_ESPRISCV32)gcc $(CFLAGS_ESPRISCV32) -DESP32C5=1 -Tstub_32c5.ld -Wl,-Map=$(@:.elf=.map) -o $@ $(filter %.c, $^) $(LDLIBS) + $(STUB_ELF_32C5_BETA_3): $(SRCS) $(BUILD_DIR) ld/stub_32c5_beta_3.ld @echo " CC(32C5BETA3) $^ -> $@" $(Q) $(CROSS_ESPRISCV32)gcc $(CFLAGS_ESPRISCV32) -DESP32C5BETA3=1 -Tstub_32c5_beta_3.ld -Wl,-Map=$(@:.elf=.map) -o $@ $(filter %.c, $^) $(LDLIBS) diff --git a/tools/esptool_py/flasher_stub/README.md b/tools/flasher_stub/README.md similarity index 100% rename from tools/esptool_py/flasher_stub/README.md rename to tools/flasher_stub/README.md diff --git a/tools/esptool_py/flasher_stub/compare_stubs.py b/tools/flasher_stub/compare_stubs.py similarity index 100% rename from tools/esptool_py/flasher_stub/compare_stubs.py rename to tools/flasher_stub/compare_stubs.py diff --git a/tools/esptool_py/flasher_stub/esptool_test_stub.py b/tools/flasher_stub/esptool_test_stub.py similarity index 100% rename from tools/esptool_py/flasher_stub/esptool_test_stub.py rename to tools/flasher_stub/esptool_test_stub.py diff --git a/tools/esptool_py/flasher_stub/include/miniz.h b/tools/flasher_stub/include/miniz.h similarity index 100% rename from tools/esptool_py/flasher_stub/include/miniz.h rename to tools/flasher_stub/include/miniz.h diff --git a/tools/esptool_py/flasher_stub/include/rom_functions.h b/tools/flasher_stub/include/rom_functions.h similarity index 90% rename from tools/esptool_py/flasher_stub/include/rom_functions.h rename to tools/flasher_stub/include/rom_functions.h index 5c7d5a8d8c..928eebc43a 100644 --- a/tools/esptool_py/flasher_stub/include/rom_functions.h +++ b/tools/flasher_stub/include/rom_functions.h @@ -18,11 +18,11 @@ int uart_rx_one_char(uint8_t *ch); uint8_t uart_rx_one_char_block(); int uart_tx_one_char(char ch); -#if ESP32C6 || ESP32H2 || ESP32C5BETA3 || ESP32P4 +#if ESP32C61 || ESP32C6 || ESP32H2 || ESP32C5 || ESP32C5BETA3 || ESP32P4 /* uart_tx_one_char doesn't send data to USB device serial, needs to be replaced */ int uart_tx_one_char2(char ch); #define uart_tx_one_char(ch) uart_tx_one_char2(ch) -#endif // ESP32C6 || ESP32H2 || ESP32C5BETA3 || ESP32P4 +#endif // ESP32C61 ||ESP32C6 || ESP32H2 || ESP32C5 || ESP32C5BETA3 || ESP32P4 void uart_div_modify(uint32_t uart_no, uint32_t baud_div); @@ -181,7 +181,7 @@ typedef struct { UartDevice * GetUartDevice(); #endif // WITH_USB_JTAG_SERIAL || WITH_USB_OTG -#if defined(ESP32S3) +#if defined(ESP32S3) || defined(ESP32P4) #define BIT(nr) (1UL << (nr)) #define ESP_ROM_OPIFLASH_SEL_CS0 (BIT(0)) @@ -348,9 +348,35 @@ void esp_rom_opiflash_exec_cmd(int spi_num, SpiFlashRdMode mode, uint32_t cs_mask, bool is_write_erase_operation); +#if ESP32P4 +extern uint32_t _rom_eco_version; // rom constant to define ECO version +void esp_rom_opiflash_exec_cmd_eco1(int spi_num, SpiFlashRdMode mode, + uint32_t cmd, int cmd_bit_len, + uint32_t addr, int addr_bit_len, + int dummy_bits, + uint8_t* mosi_data, int mosi_bit_len, + uint8_t* miso_data, int miso_bit_len, + uint32_t cs_mask, + bool is_write_erase_operation); + +void esp_rom_opiflash_exec_cmd_eco2(int spi_num, SpiFlashRdMode mode, + uint32_t cmd, int cmd_bit_len, + uint32_t addr, int addr_bit_len, + int dummy_bits, + uint8_t* mosi_data, int mosi_bit_len, + uint8_t* miso_data, int miso_bit_len, + uint32_t cs_mask, + bool is_write_erase_operation); +#endif // ESP32P4 + esp_rom_spiflash_result_t esp_rom_opiflash_wait_idle(); esp_rom_spiflash_result_t esp_rom_opiflash_wren(); esp_rom_spiflash_result_t esp_rom_opiflash_erase_sector(uint32_t sector_num); esp_rom_spiflash_result_t esp_rom_opiflash_erase_block_64k(uint32_t block_num); SpiFlashOpResult SPI_write_enable(esp_rom_spiflash_chip_t *spi); -#endif // ESP32S3 + +void esp_rom_spiflash_write_encrypted_enable(); +void esp_rom_spiflash_write_encrypted_disable(); +SpiFlashOpResult esp_rom_spiflash_unlock(); +SpiFlashOpResult esp_rom_spiflash_wait_idle(void); +#endif // ESP32S3 || ESP32P4 diff --git a/tools/esptool_py/flasher_stub/include/slip.h b/tools/flasher_stub/include/slip.h similarity index 100% rename from tools/esptool_py/flasher_stub/include/slip.h rename to tools/flasher_stub/include/slip.h diff --git a/tools/esptool_py/flasher_stub/include/soc_support.h b/tools/flasher_stub/include/soc_support.h similarity index 81% rename from tools/esptool_py/flasher_stub/include/soc_support.h rename to tools/flasher_stub/include/soc_support.h index eed774bdc2..df28ec1534 100644 --- a/tools/esptool_py/flasher_stub/include/soc_support.h +++ b/tools/flasher_stub/include/soc_support.h @@ -24,6 +24,18 @@ #define REG_SET_MASK(reg, mask) WRITE_REG((reg), (READ_REG(reg)|(mask))) #define REG_CLR_MASK(reg, mask) WRITE_REG((reg), (READ_REG(reg)&(~(mask)))) #define REG_SET_FIELD(_r, _f, _v) (WRITE_REG((_r),((READ_REG(_r) & ~((_f) << (_f##_S)))|(((_v) & (_f))<<(_f##_S))))) +#define REG_GET_FIELD(_r, _f) ((READ_REG(_r) >> (_f##_S)) & (_f##_V)) +#define REG_SET_BIT(_r, _b) \ + do \ + { \ + *(volatile uint32_t *)(_r) = (*(volatile uint32_t *)(_r)) | (_b); \ + } while (0) + +#define REG_CLR_BIT(_r, _b) \ + do \ + { \ + *(volatile uint32_t *)(_r) = (*(volatile uint32_t *)(_r)) & (~(_b)); \ + } while (0) #define ESP32_OR_LATER !(ESP8266) #define ESP32S2_OR_LATER !(ESP8266 || ESP32) @@ -46,9 +58,14 @@ #define WITH_USB_OTG 1 #endif // ESP32S3 -#ifdef ESP32C5BETA3 +#ifdef ESP32C5 #define WITH_USB_JTAG_SERIAL 1 #define IS_RISCV 1 +#endif // ESP32C5 + +#ifdef ESP32C5BETA3 +#define WITH_USB_JTAG_SERIAL 0 +#define IS_RISCV 1 #endif // ESP32C5BETA3 #ifdef ESP32C6 @@ -56,6 +73,11 @@ #define IS_RISCV 1 #endif // ESP32C6 +#ifdef ESP32C61 +#define WITH_USB_JTAG_SERIAL 1 +#define IS_RISCV 1 +#endif // ESP32C61 + #ifdef ESP32H2 #define WITH_USB_JTAG_SERIAL 1 #define IS_RISCV 1 @@ -63,8 +85,7 @@ #ifdef ESP32P4 #define WITH_USB_JTAG_SERIAL 1 -// TODO: Add support for USB OTG when MP is available -// #define WITH_USB_OTG 1 +#define WITH_USB_OTG 1 #define IS_RISCV 1 #endif // ESP32P4 @@ -115,6 +136,7 @@ #define USB_DEVICE_BASE_REG 0x60038000 #define SYSTEM_BASE_REG 0x600C0000 #define DR_REG_IO_MUX_BASE 0x60009000 +#define DR_REG_AES_XTS_BASE 0x600CC000 #endif #ifdef ESP32S3BETA2 @@ -173,7 +195,7 @@ #define DR_REG_IO_MUX_BASE 0x60009000 #endif -#if ESP32C6 || ESP32C5BETA3 +#if ESP32C61 || ESP32C6 || ESP32C5 || ESP32C5BETA3 #define UART_BASE_REG 0x60000000 /* UART0 */ #define SPI_BASE_REG 0x60003000 /* SPI peripheral 1, used for SPI flash */ #define SPI0_BASE_REG 0x60002000 /* SPI peripheral 0, inner state machine */ @@ -199,10 +221,13 @@ #define UART_BASE_REG 0x500CA000 /* UART0 */ #define SPI_BASE_REG 0x5008D000 /* SPI peripheral 1, used for SPI flash */ #define SPI0_BASE_REG 0x5008C000 /* SPI peripheral 0, inner state machine */ +#define USB_BASE_REG 0x50000000 #define GPIO_BASE_REG 0x500E0000 #define USB_DEVICE_BASE_REG 0x500D2000 +#define DR_REG_LP_SYS_BASE 0x50110000 #define DR_REG_LP_WDT_BASE 0x50116000 #define DR_REG_IO_MUX_BASE 0x500E1000 +#define HP_SYS_USBOTG20_CTRL_REG 0x500E515C #endif /********************************************************** @@ -226,7 +251,7 @@ #define UART_INT_CLR(X) (UART_BASE_REG + 0x10) #define UART_STATUS(X) (UART_BASE_REG + 0x1C) -#if ESP32S2_OR_LATER && !ESP32C6 && !ESP32H2 +#if ESP32S2_OR_LATER && !ESP32C61 && !ESP32C6 && !ESP32H2 #define UART_RXFIFO_CNT_M 0x3FF #else #define UART_RXFIFO_CNT_M 0xFF @@ -333,14 +358,34 @@ #define ETS_USB_INUM 17 /* arbitrary level 1 level interrupt */ #endif // ESP32S3 -#if ESP32C6 || ESP32C5BETA3 +#if ESP32C6 #define UART_USB_JTAG_SERIAL 3 #define DR_REG_INTERRUPT_MATRIX_BASE 0x60010000 #define INTERRUPT_CORE0_USB_INTR_MAP_REG (DR_REG_INTERRUPT_MATRIX_BASE + 0xC0) /* USB-JTAG-Serial, INTMTX_CORE0_USB_INTR_MAP_REG */ #define ETS_USB_INUM 17 /* arbitrary level 1 level interrupt */ -#endif // ESP32C6 || ESP32C5BETA3 +#endif // ESP32C6 + +#if ESP32C61 +#define UART_USB_JTAG_SERIAL 4 + +#define DR_REG_INTERRUPT_MATRIX_BASE 0x60010000 +#define INTERRUPT_CORE0_USB_INTR_MAP_REG (DR_REG_INTERRUPT_MATRIX_BASE + 0xB0) /* USB-JTAG-Serial, INTMTX_CORE0_USB_INTR_MAP_REG */ + +#define CLIC_EXT_INTR_NUM_OFFSET 16 /* For CLIC first 16 interrupts are reserved as internal */ +#define ETS_USB_INUM 17 /* arbitrary level 1 level interrupt */ +#endif // ESP32C61 + +#if ESP32C5 +#define UART_USB_JTAG_SERIAL 3 + +#define DR_REG_INTERRUPT_MATRIX_BASE 0x60010000 +#define INTERRUPT_CORE0_USB_INTR_MAP_REG (DR_REG_INTERRUPT_MATRIX_BASE + 0xD8) /* USB-JTAG-Serial, INTERRUPT_CORE0_USB_SERIAL_JTAG_INTR_MAP_REG */ + +#define CLIC_EXT_INTR_NUM_OFFSET 16 /* For CLIC first 16 interrupts are reserved as internal */ +#define ETS_USB_INUM 17 /* arbitrary level 1 level interrupt */ +#endif // ESP32C5 #ifdef ESP32H2 #define UART_USB_JTAG_SERIAL 3 @@ -357,6 +402,7 @@ #define DR_REG_INTERRUPT_MATRIX_BASE 0x500D6000 #define INTERRUPT_CORE0_USB_INTR_MAP_REG (DR_REG_INTERRUPT_MATRIX_BASE + 0x58) /* USB-JTAG-Serial, CORE0_USB_DEVICE_INT_MAP_REG */ +#define INTERRUPT_CORE0_USB_OTG_INT_MAP_REG (DR_REG_INTERRUPT_MATRIX_BASE + 0x174) /* DWC-OTG, CORE0_USB_OTG_INT_MAP_REG */ #define CLIC_EXT_INTR_NUM_OFFSET 16 /* For CLIC first 16 interrupts are reserved as internal */ #define ETS_USB_INUM 17 /* arbitrary level 1 level interrupt */ @@ -403,8 +449,9 @@ #define RTC_CNTL_SWD_AUTO_FEED_EN (1 << 31) #endif -#if ESP32C6 || ESP32C5BETA3 || ESP32P4 -#define RTC_CNTL_WDTCONFIG0_REG (DR_REG_LP_WDT_BASE + 0x0) // LP_WDT_RWDT_CONFIG0_REG +#if ESP32C61 || ESP32C6 || ESP32C5 || ESP32C5BETA3 || ESP32P4 +#define RTC_CNTL_WDTCONFIG0_REG (DR_REG_LP_WDT_BASE + 0x0) // LP_WDT_RWDT_CONFIG0_REG +#define RTC_CNTL_OPTION1_REG (DR_REG_LP_SYS_BASE + 0x08) // LP_SYSTEM_REG_SYS_CTRL_REG #define RTC_CNTL_WDTWPROTECT_REG (DR_REG_LP_WDT_BASE + 0x0018) // LP_WDT_RWDT_WPROTECT_REG #define RTC_CNTL_SWD_CONF_REG (DR_REG_LP_WDT_BASE + 0x001C) // LP_WDT_SWD_CONFIG_REG #define RTC_CNTL_SWD_WPROTECT_REG (DR_REG_LP_WDT_BASE + 0x0020) // LP_WDT_SWD_WPROTECT_REG @@ -422,7 +469,11 @@ #endif #define RTC_CNTL_WDT_WKEY 0x50D83AA1 -#define RTC_CNTL_FORCE_DOWNLOAD_BOOT (1 << 0) +#ifdef ESP32P4 + #define RTC_CNTL_FORCE_DOWNLOAD_BOOT (1 << 2) +#else + #define RTC_CNTL_FORCE_DOWNLOAD_BOOT (1 << 0) +#endif // ESP32P4 /********************************************************** * SYSTEM registers @@ -456,6 +507,14 @@ #define SYSTEM_SOC_CLK_MAX 1 #endif // ESP32S2 +#ifdef ESP32C5 +#define PCR_SYSCLK_CONF_REG (DR_REG_PCR_BASE + 0x10c) +#define PCR_SOC_CLK_SEL_M ((PCR_SOC_CLK_SEL_V)<<(PCR_SOC_CLK_SEL_S)) +#define PCR_SOC_CLK_SEL_V 0x3 +#define PCR_SOC_CLK_SEL_S 16 +#define PCR_SOC_CLK_MAX 3 // CPU_CLK frequency is 240 MHz (source is PLL_F240_CLK) +#endif // ESP32C5 + #ifdef ESP32C5BETA3 #define PCR_SYSCLK_CONF_REG (DR_REG_PCR_BASE + 0x10c) #define PCR_SOC_CLK_SEL_M ((PCR_SOC_CLK_SEL_V)<<(PCR_SOC_CLK_SEL_S)) @@ -472,6 +531,14 @@ #define PCR_SOC_CLK_MAX 1 // CPU_CLK frequency is 160 MHz (source is PLL_CLK) #endif // ESP32C6 +#ifdef ESP32C61 +#define PCR_SYSCLK_CONF_REG (DR_REG_PCR_BASE + 0xe8) +#define PCR_SOC_CLK_SEL_M ((PCR_SOC_CLK_SEL_V)<<(PCR_SOC_CLK_SEL_S)) +#define PCR_SOC_CLK_SEL_V 0x3 +#define PCR_SOC_CLK_SEL_S 16 +#define PCR_SOC_CLK_MAX 1 // CPU_CLK frequency is 160 MHz (source is PLL_CLK) +#endif // ESP32C61 + #ifdef ESP32H2 #define PCR_SYSCLK_CONF_REG (DR_REG_PCR_BASE + 0x10c) #define PCR_SOC_CLK_SEL_M ((PCR_SOC_CLK_SEL_V)<<(PCR_SOC_CLK_SEL_S)) @@ -501,9 +568,9 @@ #define ROM_SPIFLASH_LEGACY 0x3ffae270 #endif // ESP32 || ESP32S2 || ESP32S3 || ESP32S3BETA2 -#if ESP32C3 || ESP32C6BETA || ESP32C2 || ESP32C6 || ESP32C5BETA3 +#if ESP32C3 || ESP32C6BETA || ESP32C2 || ESP32C6 || ESP32C61 || ESP32C5BETA3 #define ROM_SPIFLASH_LEGACY 0x3fcdfff4 -#endif // ESP32C3 || ESP32C6BETA || ESP32C2 || ESP32C6 +#endif // ESP32C3 || ESP32C6BETA || ESP32C2 || ESP32C6 || ESP32C61 || ESP32C5BETA3 #if ESP32H2BETA1 || ESP32H2BETA2 #define ROM_SPIFLASH_LEGACY 0x3fcdfff0 @@ -513,6 +580,10 @@ #define ROM_SPIFLASH_LEGACY 0x4084fff0 #endif // ESP32H2 +#if ESP32C5 +#define ROM_SPIFLASH_LEGACY 0x4085fff0 +#endif // ESP32C5 + #if ESP32P4 #define ROM_SPIFLASH_LEGACY 0x4ff3ffec #endif // ESP32P4 @@ -567,13 +638,13 @@ #define FUNC_GPIO 1 #endif // ESP32C2 -#if ESP32C6 || ESP32C6BETA || ESP32C5BETA3 +#if ESP32C61 || ESP32C6 || ESP32C6BETA || ESP32C5 || ESP32C5BETA3 #define PERIPHS_IO_MUX_SPICLK_U (DR_REG_IO_MUX_BASE + 0x78) #define PERIPHS_IO_MUX_SPIQ_U (DR_REG_IO_MUX_BASE + 0x68) #define PERIPHS_IO_MUX_SPID_U (DR_REG_IO_MUX_BASE + 0x7c) #define PERIPHS_IO_MUX_SPICS0_U (DR_REG_IO_MUX_BASE + 0x64) #define FUNC_GPIO 1 -#endif // ESP32C6 || ESP32C6BETA +#endif // ESP32C61 || ESP32C6 || ESP32C6BETA || ESP32C5 || ESP32C5BETA3 #if ESP32H2 || ESP32H2BETA1 || ESP32H2BETA2 #define PERIPHS_IO_MUX_SPICLK_U (DR_REG_IO_MUX_BASE + 0x50) @@ -590,3 +661,31 @@ #define PERIPHS_IO_MUX_SPICS0_U (DR_REG_IO_MUX_BASE + 0x78) #define FUNC_GPIO 1 #endif // ESP32P4 + +/********************************************************** + * AES-XTS peripheral + */ + +#define MAX_ENCRYPT_BLOCK 64 + +#if ESP32S3 +#define AES_XTS_PLAIN_BASE (DR_REG_AES_XTS_BASE + 0x00) +#define AES_XTS_SIZE_REG (DR_REG_AES_XTS_BASE + 0x40) +#define AES_XTS_DESTINATION_REG (DR_REG_AES_XTS_BASE + 0x44) +#define AES_XTS_PHYSICAL_ADDR_REG (DR_REG_AES_XTS_BASE + 0x48) +#define AES_XTS_TRIGGER_REG (DR_REG_AES_XTS_BASE + 0x4C) +#define AES_XTS_RELEASE_REG (DR_REG_AES_XTS_BASE + 0x50) +#define AES_XTS_DESTROY_REG (DR_REG_AES_XTS_BASE + 0x54) +#define AES_XTS_STATE_REG (DR_REG_AES_XTS_BASE + 0x58) +#endif // ESP32S3 + +#if ESP32P4 +#define AES_XTS_PLAIN_BASE (SPI0_BASE_REG + 0x300) +#define AES_XTS_SIZE_REG (SPI0_BASE_REG + 0x340) +#define AES_XTS_DESTINATION_REG (SPI0_BASE_REG + 0x344) +#define AES_XTS_PHYSICAL_ADDR_REG (SPI0_BASE_REG + 0x348) +#define AES_XTS_TRIGGER_REG (SPI0_BASE_REG + 0x34C) +#define AES_XTS_RELEASE_REG (SPI0_BASE_REG + 0x350) +#define AES_XTS_DESTROY_REG (SPI0_BASE_REG + 0x354) +#define AES_XTS_STATE_REG (SPI0_BASE_REG + 0x358) +#endif // ESP32P4 diff --git a/tools/esptool_py/flasher_stub/include/stub_commands.h b/tools/flasher_stub/include/stub_commands.h similarity index 100% rename from tools/esptool_py/flasher_stub/include/stub_commands.h rename to tools/flasher_stub/include/stub_commands.h diff --git a/tools/esptool_py/flasher_stub/include/stub_flasher.h b/tools/flasher_stub/include/stub_flasher.h similarity index 96% rename from tools/esptool_py/flasher_stub/include/stub_flasher.h rename to tools/flasher_stub/include/stub_flasher.h index 3cf1f63105..5f880192eb 100644 --- a/tools/esptool_py/flasher_stub/include/stub_flasher.h +++ b/tools/flasher_stub/include/stub_flasher.h @@ -20,8 +20,8 @@ #define FLASH_STATUS_MASK 0xFFFF #define SECTORS_PER_BLOCK (FLASH_BLOCK_SIZE / FLASH_SECTOR_SIZE) -/* 32-bit addressing is supported only by ESP32S3 */ -#if defined(ESP32S3) && !defined(ESP32S3BETA2) +/* 32-bit addressing is supported only by ESP32S3 and ESP32P4 */ +#if (ESP32S3 && !ESP32S3BETA2) || ESP32P4 #define FLASH_MAX_SIZE 64*1024*1024 extern bool large_flash_mode; #else diff --git a/tools/esptool_py/flasher_stub/include/stub_io.h b/tools/flasher_stub/include/stub_io.h similarity index 100% rename from tools/esptool_py/flasher_stub/include/stub_io.h rename to tools/flasher_stub/include/stub_io.h diff --git a/tools/esptool_py/flasher_stub/include/stub_write_flash.h b/tools/flasher_stub/include/stub_write_flash.h similarity index 94% rename from tools/esptool_py/flasher_stub/include/stub_write_flash.h rename to tools/flasher_stub/include/stub_write_flash.h index 9e1a7c25ca..5ed98e8a04 100644 --- a/tools/esptool_py/flasher_stub/include/stub_write_flash.h +++ b/tools/flasher_stub/include/stub_write_flash.h @@ -26,5 +26,9 @@ void handle_flash_encrypt_data(void *data_buf, uint32_t length); void handle_flash_deflated_data(void *data_buf, uint32_t length); +#if ESP32P4 +void spi_write_enable(void); +#endif + /* same command used for deflated or non-deflated mode */ esp_command_error handle_flash_end(void); diff --git a/tools/esptool_py/flasher_stub/ld/rom_32.ld b/tools/flasher_stub/ld/rom_32.ld similarity index 100% rename from tools/esptool_py/flasher_stub/ld/rom_32.ld rename to tools/flasher_stub/ld/rom_32.ld diff --git a/tools/esptool_py/flasher_stub/ld/rom_32c2.ld b/tools/flasher_stub/ld/rom_32c2.ld similarity index 100% rename from tools/esptool_py/flasher_stub/ld/rom_32c2.ld rename to tools/flasher_stub/ld/rom_32c2.ld diff --git a/tools/esptool_py/flasher_stub/ld/rom_32c3.ld b/tools/flasher_stub/ld/rom_32c3.ld similarity index 100% rename from tools/esptool_py/flasher_stub/ld/rom_32c3.ld rename to tools/flasher_stub/ld/rom_32c3.ld diff --git a/tools/flasher_stub/ld/rom_32c5.ld b/tools/flasher_stub/ld/rom_32c5.ld new file mode 100755 index 0000000000..3ef16e1fe3 --- /dev/null +++ b/tools/flasher_stub/ld/rom_32c5.ld @@ -0,0 +1,483 @@ +/* ROM function interface esp32c5.rom.ld for esp32c5 + * + * + * Generated from ./target/esp32c5/interface-esp32c5.yml md5sum f5c146321f24f88ad1f27234da5aed11 + * + * Compatible with ROM where ECO version equal or greater to 0. + * + * THIS FILE WAS AUTOMATICALLY GENERATED. DO NOT EDIT. + */ + +/*************************************** + Group common + ***************************************/ + +PROVIDE ( SPIWrite = esp_rom_spiflash_write); +PROVIDE ( SPI_read_status_high = esp_rom_spiflash_read_statushigh); +PROVIDE ( SPI_write_status = esp_rom_spiflash_write_status); +PROVIDE ( SPIRead = esp_rom_spiflash_read); +PROVIDE ( SPIParamCfg = esp_rom_spiflash_config_param); +PROVIDE ( SPIEraseChip = esp_rom_spiflash_erase_chip); +PROVIDE ( SPIEraseSector = esp_rom_spiflash_erase_sector); +PROVIDE ( SPIEraseBlock = esp_rom_spiflash_erase_block); +PROVIDE ( SPI_Write_Encrypt_Enable = esp_rom_spiflash_write_encrypted_enable); +PROVIDE ( SPI_Write_Encrypt_Disable = esp_rom_spiflash_write_encrypted_disable); +PROVIDE ( SPI_Encrypt_Write = esp_rom_spiflash_write_encrypted); + +/*************************************** + Group common + ***************************************/ + +/* Functions */ +rtc_get_reset_reason = 0x40000018; +rtc_get_wakeup_cause = 0x4000001c; +pmu_enable_unhold_pads = 0x40000020; +ets_printf = 0x40000024; +ets_install_putc1 = 0x40000028; +ets_install_putc2 = 0x4000002c; +ets_install_uart_printf = 0x40000030; +ets_install_usb_printf = 0x40000034; +ets_get_printf_channel = 0x40000038; +ets_delay_us = 0x4000003c; +ets_get_cpu_frequency = 0x40000040; +ets_update_cpu_frequency = 0x40000044; +ets_install_lock = 0x40000048; +UartRxString = 0x4000004c; +UartGetCmdLn = 0x40000050; +uart_tx_one_char = 0x40000054; +uart_tx_one_char2 = 0x40000058; +uart_tx_one_char3 = 0x4000005c; +uart_rx_one_char = 0x40000060; +uart_rx_one_char_block = 0x40000064; +uart_rx_intr_handler = 0x40000068; +uart_rx_readbuff = 0x4000006c; +uartAttach = 0x40000070; +uart_tx_flush = 0x40000074; +uart_tx_wait_idle = 0x40000078; +uart_div_modify = 0x4000007c; +ets_write_char_uart = 0x40000080; +uart_tx_switch = 0x40000084; +uart_buff_switch = 0x40000088; +roundup2 = 0x4000008c; +multofup = 0x40000090; +software_reset = 0x40000094; +software_reset_cpu = 0x40000098; +ets_clk_assist_debug_clock_enable = 0x4000009c; +clear_super_wdt_reset_flag = 0x400000a0; +disable_default_watchdog = 0x400000a4; +esp_rom_set_rtc_wake_addr = 0x400000a8; +esp_rom_get_rtc_wake_addr = 0x400000ac; +send_packet = 0x400000b0; +recv_packet = 0x400000b4; +GetUartDevice = 0x400000b8; +UartDwnLdProc = 0x400000bc; +GetSecurityInfoProc = 0x400000c0; +Uart_Init = 0x400000c4; +ets_set_user_start = 0x400000c8; +/* Data (.data, .bss, .rodata) */ +ets_rom_layout_p = 0x4004fffc; +ets_ops_table_ptr = 0x4085fff8; +g_saved_pc = 0x4085fffc; + + +/*************************************** + Group miniz + ***************************************/ + +/* Functions */ +mz_adler32 = 0x400000cc; +mz_free = 0x400000d0; +tdefl_compress = 0x400000d4; +tdefl_compress_buffer = 0x400000d8; +tdefl_compress_mem_to_heap = 0x400000dc; +tdefl_compress_mem_to_mem = 0x400000e0; +tdefl_compress_mem_to_output = 0x400000e4; +tdefl_get_adler32 = 0x400000e8; +tdefl_get_prev_return_status = 0x400000ec; +tdefl_init = 0x400000f0; +tdefl_write_image_to_png_file_in_memory = 0x400000f4; +tdefl_write_image_to_png_file_in_memory_ex = 0x400000f8; +tinfl_decompress = 0x400000fc; +tinfl_decompress_mem_to_callback = 0x40000100; +tinfl_decompress_mem_to_heap = 0x40000104; +tinfl_decompress_mem_to_mem = 0x40000108; + + +/*************************************** + Group tjpgd + ***************************************/ + +/* Functions */ +jd_prepare = 0x4000010c; +jd_decomp = 0x40000110; + + +/*************************************** + Group spi_extmem_common + ***************************************/ + +/* Functions */ +esp_rom_spi_cmd_config = 0x40000114; +esp_rom_spi_cmd_start = 0x40000118; +esp_rom_spi_set_op_mode = 0x4000011c; + + +/*************************************** + Group spiflash_legacy + ***************************************/ + +/* Functions */ +esp_rom_spiflash_wait_idle = 0x40000120; +esp_rom_spiflash_write_encrypted = 0x40000124; +esp_rom_spiflash_write_encrypted_dest = 0x40000128; +esp_rom_spiflash_write_encrypted_enable = 0x4000012c; +esp_rom_spiflash_write_encrypted_disable = 0x40000130; +esp_rom_spiflash_erase_chip = 0x40000134; +_esp_rom_spiflash_erase_sector = 0x40000138; +_esp_rom_spiflash_erase_block = 0x4000013c; +_esp_rom_spiflash_write = 0x40000140; +_esp_rom_spiflash_read = 0x40000144; +_esp_rom_spiflash_unlock = 0x40000148; +_SPIEraseArea = 0x4000014c; +_SPI_write_enable = 0x40000150; +esp_rom_spiflash_erase_sector = 0x40000154; +esp_rom_spiflash_erase_block = 0x40000158; +esp_rom_spiflash_write = 0x4000015c; +esp_rom_spiflash_read = 0x40000160; +esp_rom_spiflash_unlock = 0x40000164; +SPIEraseArea = 0x40000168; +SPI_write_enable = 0x4000016c; +esp_rom_spiflash_config_param = 0x40000170; +esp_rom_spiflash_read_user_cmd = 0x40000174; +esp_rom_spiflash_select_qio_pins = 0x40000178; +esp_rom_spi_flash_auto_sus_res = 0x4000017c; +esp_rom_spi_flash_send_resume = 0x40000180; +esp_rom_spi_flash_update_id = 0x40000184; +esp_rom_spiflash_config_clk = 0x40000188; +esp_rom_spiflash_config_readmode = 0x4000018c; +esp_rom_spiflash_read_status = 0x40000190; +esp_rom_spiflash_read_statushigh = 0x40000194; +esp_rom_spiflash_write_status = 0x40000198; +esp_rom_spiflash_write_disable = 0x4000019c; +spi_cache_mode_switch = 0x400001a0; +spi_common_set_dummy_output = 0x400001a4; +spi_common_set_flash_cs_timing = 0x400001a8; +esp_rom_spi_set_address_bit_len = 0x400001ac; +SPILock = 0x400001b0; +SPIMasterReadModeCnfig = 0x400001b4; +SPI_Common_Command = 0x400001b8; +SPI_WakeUp = 0x400001bc; +SPI_block_erase = 0x400001c0; +SPI_chip_erase = 0x400001c4; +SPI_init = 0x400001c8; +SPI_page_program = 0x400001cc; +SPI_read_data = 0x400001d0; +SPI_sector_erase = 0x400001d4; +SelectSpiFunction = 0x400001d8; +SetSpiDrvs = 0x400001dc; +Wait_SPI_Idle = 0x400001e0; +spi_dummy_len_fix = 0x400001e4; +Disable_QMode = 0x400001e8; +Enable_QMode = 0x400001ec; +spi_flash_attach = 0x400001f0; +spi_flash_get_chip_size = 0x400001f4; +spi_flash_guard_set = 0x400001f8; +spi_flash_guard_get = 0x400001fc; +spi_flash_read_encrypted = 0x40000200; +/* Data (.data, .bss, .rodata) */ +rom_spiflash_legacy_funcs = 0x4085fff0; +rom_spiflash_legacy_data = 0x4085ffec; +g_flash_guard_ops = 0x4085fff4; + + +/*************************************** + Group hal_wdt + ***************************************/ + +/* Functions */ +wdt_hal_init = 0x400003a4; +wdt_hal_deinit = 0x400003a8; +wdt_hal_config_stage = 0x400003ac; +wdt_hal_write_protect_disable = 0x400003b0; +wdt_hal_write_protect_enable = 0x400003b4; +wdt_hal_enable = 0x400003b8; +wdt_hal_disable = 0x400003bc; +wdt_hal_handle_intr = 0x400003c0; +wdt_hal_feed = 0x400003c4; +wdt_hal_set_flashboot_en = 0x400003c8; +wdt_hal_is_enabled = 0x400003cc; + + +/*************************************** + Group hal_systimer + ***************************************/ + +/* Functions */ +systimer_hal_init = 0x400003d0; +systimer_hal_deinit = 0x400003d4; +systimer_hal_set_tick_rate_ops = 0x400003d8; +systimer_hal_get_counter_value = 0x400003dc; +systimer_hal_get_time = 0x400003e0; +systimer_hal_set_alarm_target = 0x400003e4; +systimer_hal_set_alarm_period = 0x400003e8; +systimer_hal_get_alarm_value = 0x400003ec; +systimer_hal_enable_alarm_int = 0x400003f0; +systimer_hal_on_apb_freq_update = 0x400003f4; +systimer_hal_counter_value_advance = 0x400003f8; +systimer_hal_enable_counter = 0x400003fc; +systimer_hal_select_alarm_mode = 0x40000400; +systimer_hal_connect_alarm_counter = 0x40000404; +systimer_hal_counter_can_stall_by_cpu = 0x40000408; + + +/*************************************** + Group cache + ***************************************/ + +/* Functions */ +Cache_Get_Line_Size = 0x40000638; +Cache_Get_Mode = 0x4000063c; +Cache_Address_Through_Cache = 0x40000640; +ROM_Boot_Cache_Init = 0x40000644; +Cache_Sync_Items = 0x40000648; +Cache_Op_Addr = 0x4000064c; +Cache_Invalidate_Addr = 0x40000650; +Cache_Clean_Addr = 0x40000654; +Cache_WriteBack_Addr = 0x40000658; +Cache_WriteBack_Invalidate_Addr = 0x4000065c; +Cache_Invalidate_All = 0x40000660; +Cache_Clean_All = 0x40000664; +Cache_WriteBack_All = 0x40000668; +Cache_WriteBack_Invalidate_All = 0x4000066c; +Cache_Mask_All = 0x40000670; +Cache_UnMask_Dram0 = 0x40000674; +Cache_Suspend_Autoload = 0x40000678; +Cache_Resume_Autoload = 0x4000067c; +Cache_Start_Preload = 0x40000680; +Cache_Preload_Done = 0x40000684; +Cache_End_Preload = 0x40000688; +Cache_Config_Autoload = 0x4000068c; +Cache_Enable_Autoload = 0x40000690; +Cache_Disable_Autoload = 0x40000694; +Cache_Enable_PreLock = 0x40000698; +Cache_Disable_PreLock = 0x4000069c; +Cache_Lock_Items = 0x400006a0; +Cache_Lock_Addr = 0x400006a4; +Cache_Unlock_Addr = 0x400006a8; +Cache_Disable_Cache = 0x400006ac; +Cache_Enable_Cache = 0x400006b0; +Cache_Suspend_Cache = 0x400006b4; +Cache_Resume_Cache = 0x400006b8; +Cache_Freeze_Enable = 0x400006bc; +Cache_Freeze_Disable = 0x400006c0; +Cache_Set_IDROM_MMU_Size = 0x400006c4; +Cache_Get_IROM_MMU_End = 0x400006c8; +Cache_Get_DROM_MMU_End = 0x400006cc; +Cache_MMU_Init = 0x400006d0; +Cache_MSPI_MMU_Set = 0x400006d4; +Cache_MSPI_MMU_Set_Secure = 0x400006d8; +Cache_Count_Flash_Pages = 0x400006dc; +Cache_Travel_Tag_Memory = 0x400006e0; +Cache_Get_Virtual_Addr = 0x400006e4; +flash2spiram_instruction_offset = 0x400006e8; +flash2spiram_rodata_offset = 0x400006ec; +flash_instr_rodata_start_page = 0x400006f0; +flash_instr_rodata_end_page = 0x400006f4; +Cache_Set_IDROM_MMU_Info = 0x400006f8; +Cache_Flash_To_SPIRAM_Copy = 0x400006fc; +/* Data (.data, .bss, .rodata) */ +rom_cache_op_cb = 0x4085ffcc; +rom_cache_internal_table_ptr = 0x4085ffc8; + + +/*************************************** + Group clock + ***************************************/ + +/* Functions */ +ets_clk_get_xtal_freq = 0x40000700; +ets_clk_get_cpu_freq = 0x40000704; + + +/*************************************** + Group gpio + ***************************************/ + +/* Functions */ +gpio_set_output_level = 0x40000708; +gpio_get_input_level = 0x4000070c; +gpio_matrix_in = 0x40000710; +gpio_matrix_out = 0x40000714; +gpio_bypass_matrix_in = 0x40000718; +gpio_output_disable = 0x4000071c; +gpio_output_enable = 0x40000720; +gpio_pad_input_disable = 0x40000724; +gpio_pad_input_enable = 0x40000728; +gpio_pad_pulldown = 0x4000072c; +gpio_pad_pullup = 0x40000730; +gpio_pad_select_gpio = 0x40000734; +gpio_pad_set_drv = 0x40000738; +gpio_pad_unhold = 0x4000073c; +gpio_pad_hold = 0x40000740; + + +/*************************************** + Group interrupts + ***************************************/ + +/* Functions */ +esprv_intc_int_set_priority = 0x40000744; +esprv_intc_int_set_threshold = 0x40000748; +esprv_intc_int_enable = 0x4000074c; +esprv_intc_int_disable = 0x40000750; +esprv_intc_int_set_type = 0x40000754; +PROVIDE( intr_handler_set = 0x40000758 ); +intr_matrix_set = 0x4000075c; +ets_intr_register_ctx = 0x40000760; +ets_intr_lock = 0x40000764; +ets_intr_unlock = 0x40000768; +ets_isr_attach = 0x4000076c; +ets_isr_mask = 0x40000770; +ets_isr_unmask = 0x40000774; + + +/*************************************** + Group crc + ***************************************/ + +/* Functions */ +crc32_le = 0x40000778; +crc16_le = 0x4000077c; +crc8_le = 0x40000780; +crc32_be = 0x40000784; +crc16_be = 0x40000788; +crc8_be = 0x4000078c; +esp_crc8 = 0x40000790; +/* Data (.data, .bss, .rodata) */ +crc32_le_table_ptr = 0x4004fff8; +crc16_le_table_ptr = 0x4004fff4; +crc8_le_table_ptr = 0x4004fff0; +crc32_be_table_ptr = 0x4004ffec; +crc16_be_table_ptr = 0x4004ffe8; +crc8_be_table_ptr = 0x4004ffe4; + + +/*************************************** + Group md5 + ***************************************/ + +/* Functions */ +md5_vector = 0x40000794; +MD5Init = 0x40000798; +MD5Update = 0x4000079c; +MD5Final = 0x400007a0; + + +/*************************************** + Group hwcrypto + ***************************************/ + +/* Functions */ +ets_sha_enable = 0x400007a4; +ets_sha_disable = 0x400007a8; +ets_sha_get_state = 0x400007ac; +ets_sha_init = 0x400007b0; +ets_sha_process = 0x400007b4; +ets_sha_starts = 0x400007b8; +ets_sha_update = 0x400007bc; +ets_sha_finish = 0x400007c0; +ets_sha_clone = 0x400007c4; +ets_hmac_enable = 0x400007c8; +ets_hmac_disable = 0x400007cc; +ets_hmac_calculate_message = 0x400007d0; +ets_hmac_calculate_downstream = 0x400007d4; +ets_hmac_invalidate_downstream = 0x400007d8; +ets_aes_enable = 0x400007dc; +ets_aes_disable = 0x400007e0; +ets_aes_setkey = 0x400007e4; +ets_aes_block = 0x400007e8; +ets_aes_setkey_dec = 0x400007ec; +ets_aes_setkey_enc = 0x400007f0; +ets_bigint_enable = 0x400007f4; +ets_bigint_disable = 0x400007f8; +ets_bigint_multiply = 0x400007fc; +ets_bigint_modmult = 0x40000800; +ets_bigint_modexp = 0x40000804; +ets_bigint_wait_finish = 0x40000808; +ets_bigint_getz = 0x4000080c; +ets_ds_enable = 0x40000810; +ets_ds_disable = 0x40000814; +ets_ds_start_sign = 0x40000818; +ets_ds_is_busy = 0x4000081c; +ets_ds_finish_sign = 0x40000820; +ets_ds_encrypt_params = 0x40000824; + + +/*************************************** + Group efuse + ***************************************/ + +/* Functions */ +ets_efuse_read = 0x40000828; +ets_efuse_program = 0x4000082c; +ets_efuse_clear_program_registers = 0x40000830; +ets_efuse_write_key = 0x40000834; +ets_efuse_get_read_register_address = 0x40000838; +ets_efuse_get_key_purpose = 0x4000083c; +ets_efuse_key_block_unused = 0x40000840; +ets_efuse_find_unused_key_block = 0x40000844; +ets_efuse_rs_calculate = 0x40000848; +ets_efuse_count_unused_key_blocks = 0x4000084c; +ets_efuse_secure_boot_enabled = 0x40000850; +ets_efuse_secure_boot_aggressive_revoke_enabled = 0x40000854; +ets_efuse_cache_encryption_enabled = 0x40000858; +ets_efuse_download_modes_disabled = 0x4000085c; +ets_efuse_find_purpose = 0x40000860; +ets_efuse_force_send_resume = 0x40000864; +ets_efuse_get_flash_delay_us = 0x40000868; +ets_efuse_get_uart_print_control = 0x4000086c; +ets_efuse_direct_boot_mode_disabled = 0x40000870; +ets_efuse_security_download_modes_enabled = 0x40000874; +ets_efuse_jtag_disabled = 0x40000878; +ets_efuse_usb_print_is_disabled = 0x4000087c; +ets_efuse_usb_download_mode_disabled = 0x40000880; +ets_efuse_usb_device_disabled = 0x40000884; +ets_efuse_secure_boot_fast_wake_enabled = 0x40000888; +ets_jtag_enable_temporarily = 0x4000088c; + + +/*************************************** + Group key_mgr + ***************************************/ + +/* Functions */ +esp_rom_check_recover_key = 0x40000890; +esp_rom_km_huk_conf = 0x40000894; +esp_rom_km_huk_risk = 0x40000898; + + +/*************************************** + Group secureboot + ***************************************/ + +/* Functions */ +ets_emsa_pss_verify = 0x4000089c; +ets_rsa_pss_verify = 0x400008a0; +ets_ecdsa_verify = 0x400008a4; +ets_secure_boot_verify_bootloader_with_keys = 0x400008a8; +ets_secure_boot_verify_signature = 0x400008ac; +ets_secure_boot_read_key_digests = 0x400008b0; +ets_mgf1_sha256 = 0x400008b4; +ets_secure_boot_revoke_public_key_digest = 0x400008b8; + + +/*************************************** + Group usb_device_uart + ***************************************/ + +/* Functions */ +usb_serial_device_rx_one_char = 0x40000ab8; +usb_serial_device_rx_one_char_block = 0x40000abc; +usb_serial_device_tx_flush = 0x40000ac0; +usb_serial_device_tx_one_char = 0x40000ac4; + diff --git a/tools/esptool_py/flasher_stub/ld/rom_32c5_beta_3.ld b/tools/flasher_stub/ld/rom_32c5_beta_3.ld similarity index 100% rename from tools/esptool_py/flasher_stub/ld/rom_32c5_beta_3.ld rename to tools/flasher_stub/ld/rom_32c5_beta_3.ld diff --git a/tools/esptool_py/flasher_stub/ld/rom_32c6.ld b/tools/flasher_stub/ld/rom_32c6.ld similarity index 100% rename from tools/esptool_py/flasher_stub/ld/rom_32c6.ld rename to tools/flasher_stub/ld/rom_32c6.ld diff --git a/tools/flasher_stub/ld/rom_32c61.ld b/tools/flasher_stub/ld/rom_32c61.ld new file mode 100644 index 0000000000..ac4419af7d --- /dev/null +++ b/tools/flasher_stub/ld/rom_32c61.ld @@ -0,0 +1,406 @@ +/* ROM function interface esp32c61.rom.ld for esp32c61 + * + * + * Generated from ./target/esp32c6lite/interface-esp32c6lite.yml md5sum 27eb0efac0883ee622c22767242c9457 + * + * Compatible with ROM where ECO version equal or greater to 0. + * + * THIS FILE WAS AUTOMATICALLY GENERATED. DO NOT EDIT. + */ + +/*************************************** + Group common + ***************************************/ + +PROVIDE ( SPIWrite = esp_rom_spiflash_write); +PROVIDE ( SPI_read_status_high = esp_rom_spiflash_read_statushigh); +PROVIDE ( SPI_write_status = esp_rom_spiflash_write_status); +PROVIDE ( SPIRead = esp_rom_spiflash_read); +PROVIDE ( SPIParamCfg = esp_rom_spiflash_config_param); +PROVIDE ( SPIEraseChip = esp_rom_spiflash_erase_chip); +PROVIDE ( SPIEraseSector = esp_rom_spiflash_erase_sector); +PROVIDE ( SPIEraseBlock = esp_rom_spiflash_erase_block); +PROVIDE ( SPI_Write_Encrypt_Enable = esp_rom_spiflash_write_encrypted_enable); +PROVIDE ( SPI_Write_Encrypt_Disable = esp_rom_spiflash_write_encrypted_disable); +PROVIDE ( SPI_Encrypt_Write = esp_rom_spiflash_write_encrypted); + +/*************************************** + Group common + ***************************************/ + +/* Functions */ +rtc_get_reset_reason = 0x40000018; +rtc_get_wakeup_cause = 0x4000001c; +pmu_enable_unhold_pads = 0x40000020; +ets_printf = 0x40000024; +ets_install_putc1 = 0x40000028; +ets_install_putc2 = 0x4000002c; +ets_install_uart_printf = 0x40000030; +ets_install_usb_printf = 0x40000034; +ets_get_printf_channel = 0x40000038; +ets_delay_us = 0x4000003c; +ets_get_cpu_frequency = 0x40000040; +ets_update_cpu_frequency = 0x40000044; +ets_install_lock = 0x40000048; +UartRxString = 0x4000004c; +UartGetCmdLn = 0x40000050; +uart_tx_one_char = 0x40000054; +uart_tx_one_char2 = 0x40000058; +uart_tx_one_char3 = 0x4000005c; +uart_rx_one_char = 0x40000060; +uart_rx_one_char_block = 0x40000064; +uart_rx_intr_handler = 0x40000068; +uart_rx_readbuff = 0x4000006c; +uartAttach = 0x40000070; +uart_tx_flush = 0x40000074; +uart_tx_wait_idle = 0x40000078; +uart_div_modify = 0x4000007c; +ets_write_char_uart = 0x40000080; +uart_tx_switch = 0x40000084; +uart_buff_switch = 0x40000088; +roundup2 = 0x4000008c; +multofup = 0x40000090; +software_reset = 0x40000094; +software_reset_cpu = 0x40000098; +ets_clk_assist_debug_clock_enable = 0x4000009c; +clear_super_wdt_reset_flag = 0x400000a0; +disable_default_watchdog = 0x400000a4; +esp_rom_set_rtc_wake_addr = 0x400000a8; +esp_rom_get_rtc_wake_addr = 0x400000ac; +send_packet = 0x400000b0; +recv_packet = 0x400000b4; +GetUartDevice = 0x400000b8; +UartDwnLdProc = 0x400000bc; +GetSecurityInfoProc = 0x400000c0; +Uart_Init = 0x400000c4; +ets_set_user_start = 0x400000c8; +/* Data (.data, .bss, .rodata) */ +ets_rom_layout_p = 0x4003fffc; +ets_ops_table_ptr = 0x4084fff8; +g_saved_pc = 0x4084fffc; + + +/*************************************** + Group miniz + ***************************************/ + +/* Functions */ +mz_adler32 = 0x400000cc; +mz_free = 0x400000d0; +tdefl_compress = 0x400000d4; +tdefl_compress_buffer = 0x400000d8; +tdefl_compress_mem_to_heap = 0x400000dc; +tdefl_compress_mem_to_mem = 0x400000e0; +tdefl_compress_mem_to_output = 0x400000e4; +tdefl_get_adler32 = 0x400000e8; +tdefl_get_prev_return_status = 0x400000ec; +tdefl_init = 0x400000f0; +tdefl_write_image_to_png_file_in_memory = 0x400000f4; +tdefl_write_image_to_png_file_in_memory_ex = 0x400000f8; +tinfl_decompress = 0x400000fc; +tinfl_decompress_mem_to_callback = 0x40000100; +tinfl_decompress_mem_to_heap = 0x40000104; +tinfl_decompress_mem_to_mem = 0x40000108; + + +/*************************************** + Group tjpgd + ***************************************/ + +/* Functions */ +jd_prepare = 0x4000010c; +jd_decomp = 0x40000110; + + +/*************************************** + Group spi_extmem_common + ***************************************/ + +/* Functions */ +esp_rom_spi_cmd_config = 0x40000114; +esp_rom_spi_cmd_start = 0x40000118; +esp_rom_spi_set_op_mode = 0x4000011c; + + +/*************************************** + Group spiflash_legacy + ***************************************/ + +/* Functions */ +esp_rom_spiflash_wait_idle = 0x40000120; +esp_rom_spiflash_write_encrypted = 0x40000124; +esp_rom_spiflash_write_encrypted_dest = 0x40000128; +esp_rom_spiflash_write_encrypted_enable = 0x4000012c; +esp_rom_spiflash_write_encrypted_disable = 0x40000130; +esp_rom_spiflash_erase_chip = 0x40000134; +_esp_rom_spiflash_erase_sector = 0x40000138; +_esp_rom_spiflash_erase_block = 0x4000013c; +_esp_rom_spiflash_write = 0x40000140; +_esp_rom_spiflash_read = 0x40000144; +_esp_rom_spiflash_unlock = 0x40000148; +_SPIEraseArea = 0x4000014c; +_SPI_write_enable = 0x40000150; +esp_rom_spiflash_erase_sector = 0x40000154; +esp_rom_spiflash_erase_block = 0x40000158; +esp_rom_spiflash_write = 0x4000015c; +esp_rom_spiflash_read = 0x40000160; +esp_rom_spiflash_unlock = 0x40000164; +SPIEraseArea = 0x40000168; +SPI_write_enable = 0x4000016c; +esp_rom_spiflash_config_param = 0x40000170; +esp_rom_spiflash_read_user_cmd = 0x40000174; +esp_rom_spiflash_select_qio_pins = 0x40000178; +esp_rom_spi_flash_auto_sus_res = 0x4000017c; +esp_rom_spi_flash_send_resume = 0x40000180; +esp_rom_spi_flash_update_id = 0x40000184; +esp_rom_spiflash_config_clk = 0x40000188; +esp_rom_spiflash_config_readmode = 0x4000018c; +esp_rom_spiflash_read_status = 0x40000190; +esp_rom_spiflash_read_statushigh = 0x40000194; +esp_rom_spiflash_write_status = 0x40000198; +esp_rom_spiflash_write_disable = 0x4000019c; +spi_cache_mode_switch = 0x400001a0; +spi_common_set_dummy_output = 0x400001a4; +spi_common_set_flash_cs_timing = 0x400001a8; +esp_rom_spi_set_address_bit_len = 0x400001ac; +SPILock = 0x400001b0; +SPIMasterReadModeCnfig = 0x400001b4; +SPI_Common_Command = 0x400001b8; +SPI_WakeUp = 0x400001bc; +SPI_block_erase = 0x400001c0; +SPI_chip_erase = 0x400001c4; +SPI_init = 0x400001c8; +SPI_page_program = 0x400001cc; +SPI_read_data = 0x400001d0; +SPI_sector_erase = 0x400001d4; +SelectSpiFunction = 0x400001d8; +SetSpiDrvs = 0x400001dc; +Wait_SPI_Idle = 0x400001e0; +spi_dummy_len_fix = 0x400001e4; +Disable_QMode = 0x400001e8; +Enable_QMode = 0x400001ec; +spi_flash_attach = 0x400001f0; +spi_flash_get_chip_size = 0x400001f4; +spi_flash_guard_set = 0x400001f8; +spi_flash_guard_get = 0x400001fc; +spi_flash_read_encrypted = 0x40000200; +/* Data (.data, .bss, .rodata) */ +rom_spiflash_legacy_funcs = 0x4084fff0; +rom_spiflash_legacy_data = 0x4084ffec; +g_flash_guard_ops = 0x4084fff4; + + +/*************************************** + Group cache + ***************************************/ + +/* Functions */ +Cache_Get_Line_Size = 0x40000614; +Cache_Get_Mode = 0x40000618; +Cache_Address_Through_Cache = 0x4000061c; +ROM_Boot_Cache_Init = 0x40000620; +MMU_Set_Page_Mode = 0x40000624; +MMU_Get_Page_Mode = 0x40000628; +Cache_Sync_Items = 0x4000062c; +Cache_Op_Addr = 0x40000630; +Cache_Invalidate_Addr = 0x40000634; +Cache_Clean_Addr = 0x40000638; +Cache_WriteBack_Addr = 0x4000063c; +Cache_WriteBack_Invalidate_Addr = 0x40000640; +Cache_Invalidate_All = 0x40000644; +Cache_Clean_All = 0x40000648; +Cache_WriteBack_All = 0x4000064c; +Cache_WriteBack_Invalidate_All = 0x40000650; +Cache_Mask_All = 0x40000654; +Cache_UnMask_Dram0 = 0x40000658; +Cache_Suspend_Autoload = 0x4000065c; +Cache_Resume_Autoload = 0x40000660; +Cache_Start_Preload = 0x40000664; +Cache_Preload_Done = 0x40000668; +Cache_End_Preload = 0x4000066c; +Cache_Config_Autoload = 0x40000670; +Cache_Enable_Autoload = 0x40000674; +Cache_Disable_Autoload = 0x40000678; +Cache_Enable_PreLock = 0x4000067c; +Cache_Disable_PreLock = 0x40000680; +Cache_Lock_Items = 0x40000684; +Cache_Lock_Addr = 0x40000688; +Cache_Unlock_Addr = 0x4000068c; +Cache_Disable_Cache = 0x40000690; +Cache_Enable_Cache = 0x40000694; +Cache_Suspend_Cache = 0x40000698; +Cache_Resume_Cache = 0x4000069c; +Cache_Freeze_Enable = 0x400006a0; +Cache_Freeze_Disable = 0x400006a4; +Cache_Set_IDROM_MMU_Size = 0x400006a8; +Cache_Get_IROM_MMU_End = 0x400006ac; +Cache_Get_DROM_MMU_End = 0x400006b0; +Cache_MMU_Init = 0x400006b4; +Cache_MSPI_MMU_Set = 0x400006b8; +Cache_MSPI_MMU_Set_Secure = 0x400006bc; +Cache_Count_Flash_Pages = 0x400006c0; +Cache_Travel_Tag_Memory = 0x400006c4; +Cache_Get_Virtual_Addr = 0x400006c8; +flash2spiram_instruction_offset = 0x400006cc; +flash2spiram_rodata_offset = 0x400006d0; +flash_instr_rodata_start_page = 0x400006d4; +flash_instr_rodata_end_page = 0x400006d8; +Cache_Set_IDROM_MMU_Info = 0x400006dc; +Cache_Flash_To_SPIRAM_Copy = 0x400006e0; +/* Data (.data, .bss, .rodata) */ +rom_cache_op_cb = 0x4084ffcc; +rom_cache_internal_table_ptr = 0x4084ffc8; + + +/*************************************** + Group clock + ***************************************/ + +/* Functions */ +ets_clk_get_xtal_freq = 0x400006e4; +ets_clk_get_cpu_freq = 0x400006e8; + + +/*************************************** + Group gpio + ***************************************/ + +/* Functions */ +gpio_set_output_level = 0x400006ec; +gpio_get_input_level = 0x400006f0; +gpio_matrix_in = 0x400006f4; +gpio_matrix_out = 0x400006f8; +gpio_bypass_matrix_in = 0x400006fc; +gpio_output_disable = 0x40000700; +gpio_output_enable = 0x40000704; +gpio_pad_input_disable = 0x40000708; +gpio_pad_input_enable = 0x4000070c; +gpio_pad_pulldown = 0x40000710; +gpio_pad_pullup = 0x40000714; +gpio_pad_select_gpio = 0x40000718; +gpio_pad_set_drv = 0x4000071c; +gpio_pad_unhold = 0x40000720; +gpio_pad_hold = 0x40000724; + + +/*************************************** + Group interrupts + ***************************************/ + +/* Functions */ +esprv_intc_int_set_priority = 0x40000728; +esprv_intc_int_set_threshold = 0x4000072c; +esprv_intc_int_enable = 0x40000730; +esprv_intc_int_disable = 0x40000734; +esprv_intc_int_set_type = 0x40000738; +PROVIDE( intr_handler_set = 0x4000073c ); +intr_matrix_set = 0x40000740; +ets_intr_register_ctx = 0x40000744; +ets_intr_lock = 0x40000748; +ets_intr_unlock = 0x4000074c; +ets_isr_attach = 0x40000750; +ets_isr_mask = 0x40000754; +ets_isr_unmask = 0x40000758; + + +/*************************************** + Group crc + ***************************************/ + +/* Functions */ +crc32_le = 0x4000075c; +crc16_le = 0x40000760; +crc8_le = 0x40000764; +crc32_be = 0x40000768; +crc16_be = 0x4000076c; +crc8_be = 0x40000770; +esp_crc8 = 0x40000774; +/* Data (.data, .bss, .rodata) */ +crc32_le_table_ptr = 0x4003fff8; +crc16_le_table_ptr = 0x4003fff4; +crc8_le_table_ptr = 0x4003fff0; +crc32_be_table_ptr = 0x4003ffec; +crc16_be_table_ptr = 0x4003ffe8; +crc8_be_table_ptr = 0x4003ffe4; + + +/*************************************** + Group md5 + ***************************************/ + +/* Functions */ +md5_vector = 0x40000778; +MD5Init = 0x4000077c; +MD5Update = 0x40000780; +MD5Final = 0x40000784; + + +/*************************************** + Group hwcrypto + ***************************************/ + +/* Functions */ +ets_sha_enable = 0x40000788; +ets_sha_disable = 0x4000078c; +ets_sha_get_state = 0x40000790; +ets_sha_init = 0x40000794; +ets_sha_process = 0x40000798; +ets_sha_starts = 0x4000079c; +ets_sha_update = 0x400007a0; +ets_sha_finish = 0x400007a4; +ets_sha_clone = 0x400007a8; + + +/*************************************** + Group efuse + ***************************************/ + +/* Functions */ +ets_efuse_read = 0x400007ac; +ets_efuse_program = 0x400007b0; +ets_efuse_clear_program_registers = 0x400007b4; +ets_efuse_write_key = 0x400007b8; +ets_efuse_get_read_register_address = 0x400007bc; +ets_efuse_get_key_purpose = 0x400007c0; +ets_efuse_key_block_unused = 0x400007c4; +ets_efuse_find_unused_key_block = 0x400007c8; +ets_efuse_rs_calculate = 0x400007cc; +ets_efuse_count_unused_key_blocks = 0x400007d0; +ets_efuse_secure_boot_enabled = 0x400007d4; +ets_efuse_secure_boot_aggressive_revoke_enabled = 0x400007d8; +ets_efuse_cache_encryption_enabled = 0x400007dc; +ets_efuse_download_modes_disabled = 0x400007e0; +ets_efuse_find_purpose = 0x400007e4; +ets_efuse_force_send_resume = 0x400007e8; +ets_efuse_get_flash_delay_us = 0x400007ec; +ets_efuse_get_uart_print_control = 0x400007f0; +ets_efuse_direct_boot_mode_disabled = 0x400007f4; +ets_efuse_security_download_modes_enabled = 0x400007f8; +ets_efuse_jtag_disabled = 0x400007fc; +ets_efuse_usb_print_is_disabled = 0x40000800; +ets_efuse_usb_download_mode_disabled = 0x40000804; +ets_efuse_usb_device_disabled = 0x40000808; +ets_efuse_secure_boot_fast_wake_enabled = 0x4000080c; + + +/*************************************** + Group secureboot + ***************************************/ + +/* Functions */ +ets_ecdsa_verify = 0x40000810; +ets_secure_boot_verify_bootloader_with_keys = 0x40000814; +ets_secure_boot_verify_signature = 0x40000818; +ets_secure_boot_read_key_digests = 0x4000081c; +ets_secure_boot_revoke_public_key_digest = 0x40000820; + + +/*************************************** + Group usb_device_uart + ***************************************/ + +/* Functions */ +usb_serial_device_rx_one_char = 0x40000a20; +usb_serial_device_rx_one_char_block = 0x40000a24; +usb_serial_device_tx_flush = 0x40000a28; +usb_serial_device_tx_one_char = 0x40000a2c; diff --git a/tools/esptool_py/flasher_stub/ld/rom_32c6_beta.ld b/tools/flasher_stub/ld/rom_32c6_beta.ld similarity index 100% rename from tools/esptool_py/flasher_stub/ld/rom_32c6_beta.ld rename to tools/flasher_stub/ld/rom_32c6_beta.ld diff --git a/tools/esptool_py/flasher_stub/ld/rom_32h2.ld b/tools/flasher_stub/ld/rom_32h2.ld similarity index 100% rename from tools/esptool_py/flasher_stub/ld/rom_32h2.ld rename to tools/flasher_stub/ld/rom_32h2.ld diff --git a/tools/esptool_py/flasher_stub/ld/rom_32h2_beta_1.ld b/tools/flasher_stub/ld/rom_32h2_beta_1.ld similarity index 100% rename from tools/esptool_py/flasher_stub/ld/rom_32h2_beta_1.ld rename to tools/flasher_stub/ld/rom_32h2_beta_1.ld diff --git a/tools/esptool_py/flasher_stub/ld/rom_32h2_beta_2.ld b/tools/flasher_stub/ld/rom_32h2_beta_2.ld similarity index 100% rename from tools/esptool_py/flasher_stub/ld/rom_32h2_beta_2.ld rename to tools/flasher_stub/ld/rom_32h2_beta_2.ld diff --git a/tools/esptool_py/flasher_stub/ld/rom_32p4.ld b/tools/flasher_stub/ld/rom_32p4.ld similarity index 98% rename from tools/esptool_py/flasher_stub/ld/rom_32p4.ld rename to tools/flasher_stub/ld/rom_32p4.ld index 51ac8cded6..89bd00f3be 100644 --- a/tools/esptool_py/flasher_stub/ld/rom_32p4.ld +++ b/tools/flasher_stub/ld/rom_32p4.ld @@ -77,6 +77,9 @@ ets_set_user_start = 0x4fc000c4; ets_rom_layout_p = 0x4fc1fffc; ets_ops_table_ptr = 0x4ff3fff4; g_saved_pc = 0x4ff3fff8; +memcpy = 0x4fc0026c; +esp_rom_opiflash_exec_cmd_eco1 = 0x4fc0fe76; +esp_rom_opiflash_exec_cmd_eco2 = 0x4fc10320; /*************************************** @@ -617,3 +620,12 @@ usb_dev_deinit = 0x4fc00a44; usb_dw_ctrl_deinit = 0x4fc00a48; /* Data (.data, .bss, .rodata) */ s_usb_osglue = 0x4ff3ffc8; + + +/*************************************** + ROM version variables for esp32p4 + These addresses should be compatible with any ROM version for this chip. + ***************************************/ + +_rom_chip_id = 0x4fc00010; +_rom_eco_version = 0x4fc00014; diff --git a/tools/esptool_py/flasher_stub/ld/rom_32s2.ld b/tools/flasher_stub/ld/rom_32s2.ld similarity index 100% rename from tools/esptool_py/flasher_stub/ld/rom_32s2.ld rename to tools/flasher_stub/ld/rom_32s2.ld diff --git a/tools/esptool_py/flasher_stub/ld/rom_32s3.ld b/tools/flasher_stub/ld/rom_32s3.ld similarity index 99% rename from tools/esptool_py/flasher_stub/ld/rom_32s3.ld rename to tools/flasher_stub/ld/rom_32s3.ld index 721824c1d0..17d4127b08 100644 --- a/tools/esptool_py/flasher_stub/ld/rom_32s3.ld +++ b/tools/flasher_stub/ld/rom_32s3.ld @@ -529,6 +529,8 @@ ets_isr_unmask = 0x40001b90; ***************************************/ /* Functions */ + +memcpy = 0x400011f4; xthal_bcopy = 0x40001b9c; xthal_memcpy = 0x40001ba8; xthal_get_ccompare = 0x40001bb4; diff --git a/tools/esptool_py/flasher_stub/ld/rom_32s3_beta_2.ld b/tools/flasher_stub/ld/rom_32s3_beta_2.ld similarity index 100% rename from tools/esptool_py/flasher_stub/ld/rom_32s3_beta_2.ld rename to tools/flasher_stub/ld/rom_32s3_beta_2.ld diff --git a/tools/esptool_py/flasher_stub/ld/rom_8266.ld b/tools/flasher_stub/ld/rom_8266.ld similarity index 100% rename from tools/esptool_py/flasher_stub/ld/rom_8266.ld rename to tools/flasher_stub/ld/rom_8266.ld diff --git a/tools/esptool_py/flasher_stub/ld/stub_32.ld b/tools/flasher_stub/ld/stub_32.ld similarity index 100% rename from tools/esptool_py/flasher_stub/ld/stub_32.ld rename to tools/flasher_stub/ld/stub_32.ld diff --git a/tools/esptool_py/flasher_stub/ld/stub_32c2.ld b/tools/flasher_stub/ld/stub_32c2.ld similarity index 100% rename from tools/esptool_py/flasher_stub/ld/stub_32c2.ld rename to tools/flasher_stub/ld/stub_32c2.ld diff --git a/tools/esptool_py/flasher_stub/ld/stub_32c3.ld b/tools/flasher_stub/ld/stub_32c3.ld similarity index 100% rename from tools/esptool_py/flasher_stub/ld/stub_32c3.ld rename to tools/flasher_stub/ld/stub_32c3.ld diff --git a/tools/flasher_stub/ld/stub_32c5.ld b/tools/flasher_stub/ld/stub_32c5.ld new file mode 100644 index 0000000000..0ddc7cf961 --- /dev/null +++ b/tools/flasher_stub/ld/stub_32c5.ld @@ -0,0 +1,26 @@ +MEMORY { + iram : org = 0x40800000, len = 0x4000 + dram : org = 0x40840000, len = 0x18000 +} + +ENTRY(stub_main) + +SECTIONS { + .text : ALIGN(4) { + *(.literal) + *(.text .text.*) + } > iram + + .bss : ALIGN(4) { + _bss_start = ABSOLUTE(.); + *(.bss) + _bss_end = ABSOLUTE(.); + } > dram + + .data : ALIGN(4) { + *(.data) + *(.rodata .rodata.*) + } > dram +} + +INCLUDE "rom_32c5.ld" diff --git a/tools/esptool_py/flasher_stub/ld/stub_32c5_beta_3.ld b/tools/flasher_stub/ld/stub_32c5_beta_3.ld similarity index 100% rename from tools/esptool_py/flasher_stub/ld/stub_32c5_beta_3.ld rename to tools/flasher_stub/ld/stub_32c5_beta_3.ld diff --git a/tools/esptool_py/flasher_stub/ld/stub_32c6.ld b/tools/flasher_stub/ld/stub_32c6.ld similarity index 100% rename from tools/esptool_py/flasher_stub/ld/stub_32c6.ld rename to tools/flasher_stub/ld/stub_32c6.ld diff --git a/tools/flasher_stub/ld/stub_32c61.ld b/tools/flasher_stub/ld/stub_32c61.ld new file mode 100644 index 0000000000..8cda581f34 --- /dev/null +++ b/tools/flasher_stub/ld/stub_32c61.ld @@ -0,0 +1,26 @@ +MEMORY { + iram : org = 0x40800000, len = 0x4000 + dram : org = 0x40804000, len = 0x18000 +} + +ENTRY(stub_main) + +SECTIONS { + .text : ALIGN(4) { + *(.literal) + *(.text .text.*) + } > iram + + .bss : ALIGN(4) { + _bss_start = ABSOLUTE(.); + *(.bss) + _bss_end = ABSOLUTE(.); + } > dram + + .data : ALIGN(4) { + *(.data) + *(.rodata .rodata.*) + } > dram +} + +INCLUDE "rom_32c61.ld" diff --git a/tools/esptool_py/flasher_stub/ld/stub_32c6_beta.ld b/tools/flasher_stub/ld/stub_32c6_beta.ld similarity index 100% rename from tools/esptool_py/flasher_stub/ld/stub_32c6_beta.ld rename to tools/flasher_stub/ld/stub_32c6_beta.ld diff --git a/tools/esptool_py/flasher_stub/ld/stub_32h2.ld b/tools/flasher_stub/ld/stub_32h2.ld similarity index 100% rename from tools/esptool_py/flasher_stub/ld/stub_32h2.ld rename to tools/flasher_stub/ld/stub_32h2.ld diff --git a/tools/esptool_py/flasher_stub/ld/stub_32h2_beta_1.ld b/tools/flasher_stub/ld/stub_32h2_beta_1.ld similarity index 100% rename from tools/esptool_py/flasher_stub/ld/stub_32h2_beta_1.ld rename to tools/flasher_stub/ld/stub_32h2_beta_1.ld diff --git a/tools/esptool_py/flasher_stub/ld/stub_32h2_beta_2.ld b/tools/flasher_stub/ld/stub_32h2_beta_2.ld similarity index 100% rename from tools/esptool_py/flasher_stub/ld/stub_32h2_beta_2.ld rename to tools/flasher_stub/ld/stub_32h2_beta_2.ld diff --git a/tools/esptool_py/flasher_stub/ld/stub_32p4.ld b/tools/flasher_stub/ld/stub_32p4.ld similarity index 100% rename from tools/esptool_py/flasher_stub/ld/stub_32p4.ld rename to tools/flasher_stub/ld/stub_32p4.ld diff --git a/tools/esptool_py/flasher_stub/ld/stub_32s2.ld b/tools/flasher_stub/ld/stub_32s2.ld similarity index 100% rename from tools/esptool_py/flasher_stub/ld/stub_32s2.ld rename to tools/flasher_stub/ld/stub_32s2.ld diff --git a/tools/esptool_py/flasher_stub/ld/stub_32s3.ld b/tools/flasher_stub/ld/stub_32s3.ld similarity index 100% rename from tools/esptool_py/flasher_stub/ld/stub_32s3.ld rename to tools/flasher_stub/ld/stub_32s3.ld diff --git a/tools/esptool_py/flasher_stub/ld/stub_32s3_beta_2.ld b/tools/flasher_stub/ld/stub_32s3_beta_2.ld similarity index 100% rename from tools/esptool_py/flasher_stub/ld/stub_32s3_beta_2.ld rename to tools/flasher_stub/ld/stub_32s3_beta_2.ld diff --git a/tools/esptool_py/flasher_stub/ld/stub_8266.ld b/tools/flasher_stub/ld/stub_8266.ld similarity index 100% rename from tools/esptool_py/flasher_stub/ld/stub_8266.ld rename to tools/flasher_stub/ld/stub_8266.ld diff --git a/tools/esptool_py/flasher_stub/miniz.c b/tools/flasher_stub/miniz.c similarity index 100% rename from tools/esptool_py/flasher_stub/miniz.c rename to tools/flasher_stub/miniz.c diff --git a/tools/esptool_py/flasher_stub/run_tests_with_stub.sh b/tools/flasher_stub/run_tests_with_stub.sh similarity index 100% rename from tools/esptool_py/flasher_stub/run_tests_with_stub.sh rename to tools/flasher_stub/run_tests_with_stub.sh diff --git a/tools/esptool_py/flasher_stub/slip.c b/tools/flasher_stub/slip.c similarity index 100% rename from tools/esptool_py/flasher_stub/slip.c rename to tools/flasher_stub/slip.c diff --git a/tools/esptool_py/flasher_stub/stub_commands.c b/tools/flasher_stub/stub_commands.c similarity index 84% rename from tools/esptool_py/flasher_stub/stub_commands.c rename to tools/flasher_stub/stub_commands.c index f2b658245e..de71639b94 100644 --- a/tools/esptool_py/flasher_stub/stub_commands.c +++ b/tools/flasher_stub/stub_commands.c @@ -6,18 +6,19 @@ #include #include "stub_commands.h" +#include "stub_write_flash.h" #include "stub_flasher.h" #include "rom_functions.h" #include "slip.h" #include "soc_support.h" #include "stub_io.h" -#if defined(ESP32S3) && !defined(ESP32S3BETA2) +#if (ESP32S3 && !ESP32S3BETA2) || ESP32P4 static esp_rom_spiflash_result_t SPIRead4B(int spi_num, uint32_t flash_addr, uint8_t* buf, int len) { uint8_t cmd_len = 8; - esp_rom_opiflash_wait_idle(); + esp_rom_spiflash_wait_idle(); while (len > 0) { int rd_length; if (len > 16 ) { //16 = read_sub_len @@ -54,6 +55,21 @@ int handle_flash_erase(uint32_t addr, uint32_t len) { } else { if (SPIEraseSector(addr / FLASH_SECTOR_SIZE) != 0) return 0x35; } + #elif ESP32P4 + if (large_flash_mode) { + spi_write_enable(); + esp_rom_spiflash_wait_idle(); + esp_rom_opiflash_exec_cmd(1, SPI_FLASH_SLOWRD_MODE, + CMD_SECTOR_ERASE_4B, 8, + addr, 32, + 0, + NULL, 0, + NULL, 0, + 1, + true); + } else { + if (SPIEraseSector(addr / FLASH_SECTOR_SIZE) != 0) return 0x35; + } #else if (SPIEraseSector(addr / FLASH_SECTOR_SIZE) != 0) return 0x35; #endif // ESP32S3 @@ -68,6 +84,21 @@ int handle_flash_erase(uint32_t addr, uint32_t len) { } else { if (SPIEraseBlock(addr / FLASH_BLOCK_SIZE) != 0) return 0x36; } + #elif ESP32P4 + if (large_flash_mode) { + spi_write_enable(); + esp_rom_spiflash_wait_idle(); + esp_rom_opiflash_exec_cmd(1, SPI_FLASH_SLOWRD_MODE, + CMD_LARGE_BLOCK_ERASE_4B, 8, + addr, 32, + 0, + NULL, 0, + NULL, 0, + 1, + true); + } else { + if (SPIEraseBlock(addr / FLASH_BLOCK_SIZE) != 0) return 0x36; + } #else if (SPIEraseBlock(addr / FLASH_BLOCK_SIZE) != 0) return 0x36; #endif // ESP32S3 @@ -82,6 +113,21 @@ int handle_flash_erase(uint32_t addr, uint32_t len) { } else { if (SPIEraseSector(addr / FLASH_SECTOR_SIZE) != 0) return 0x37; } + #elif ESP32P4 + if (large_flash_mode) { + spi_write_enable(); + esp_rom_spiflash_wait_idle(); + esp_rom_opiflash_exec_cmd(1, SPI_FLASH_SLOWRD_MODE, + CMD_SECTOR_ERASE_4B, 8, + addr, 32, + 0, + NULL, 0, + NULL, 0, + 1, + true); + } else { + if (SPIEraseSector(addr / FLASH_SECTOR_SIZE) != 0) return 0x35; + } #else if (SPIEraseSector(addr / FLASH_SECTOR_SIZE) != 0) return 0x37; #endif // ESP32S3 @@ -111,7 +157,7 @@ void handle_flash_read(uint32_t addr, uint32_t len, uint32_t block_size, while (num_sent < len && num_sent - num_acked < max_in_flight) { uint32_t n = len - num_sent; if (n > block_size) n = block_size; - #if defined(ESP32S3) && !defined(ESP32S3BETA2) + #if (ESP32S3 && !ESP32S3BETA2) || ESP32P4 if (large_flash_mode) { res = SPIRead4B(1, addr, buf, n); } else { @@ -151,7 +197,7 @@ int handle_flash_get_md5sum(uint32_t addr, uint32_t len) { if (n > FLASH_SECTOR_SIZE) { n = FLASH_SECTOR_SIZE; } - #if defined(ESP32S3) && !defined(ESP32S3BETA2) + #if (ESP32S3 && !ESP32S3BETA2) || ESP32P4 if (large_flash_mode) { res = SPIRead4B(1, addr, buf, n); } else { diff --git a/tools/esptool_py/flasher_stub/stub_flasher.c b/tools/flasher_stub/stub_flasher.c similarity index 93% rename from tools/esptool_py/flasher_stub/stub_flasher.c rename to tools/flasher_stub/stub_flasher.c index 445e425676..97434850ff 100644 --- a/tools/esptool_py/flasher_stub/stub_flasher.c +++ b/tools/flasher_stub/stub_flasher.c @@ -48,6 +48,39 @@ static uint8_t calculate_checksum(uint8_t *buf, int length) return res; } +#if ESP32P4 +void esp_rom_opiflash_exec_cmd(int spi_num, SpiFlashRdMode mode, + uint32_t cmd, int cmd_bit_len, + uint32_t addr, int addr_bit_len, + int dummy_bits, + uint8_t* mosi_data, int mosi_bit_len, + uint8_t* miso_data, int miso_bit_len, + uint32_t cs_mask, + bool is_write_erase_operation) +{ + + if (_rom_eco_version == 2) { + esp_rom_opiflash_exec_cmd_eco2(spi_num, mode, + cmd, cmd_bit_len, + addr, addr_bit_len, + dummy_bits, + mosi_data, mosi_bit_len, + miso_data, miso_bit_len, + cs_mask, + is_write_erase_operation); + } else { + esp_rom_opiflash_exec_cmd_eco1(spi_num, mode, + cmd, cmd_bit_len, + addr, addr_bit_len, + dummy_bits, + mosi_data, mosi_bit_len, + miso_data, miso_bit_len, + cs_mask, + is_write_erase_operation); + } +} +#endif // ESP32P4 + #if USE_MAX_CPU_FREQ static bool can_use_max_cpu_freq() { @@ -63,7 +96,7 @@ static bool can_use_max_cpu_freq() #endif } -#if ESP32C6 || ESP32H2 || ESP32C5BETA3 +#if ESP32C61 || ESP32C6 || ESP32H2 || ESP32C5 || ESP32C5BETA3 static uint32_t pcr_sysclk_conf_reg = 0; #else static uint32_t cpu_per_conf_reg = 0; @@ -75,7 +108,7 @@ static void set_max_cpu_freq() if (can_use_max_cpu_freq()) { /* Set CPU frequency to max. This also increases SPI speed. */ - #if ESP32C6 || ESP32H2 || ESP32C5BETA3 + #if ESP32C61 || ESP32C6 || ESP32H2 || ESP32C5 || ESP32C5BETA3 pcr_sysclk_conf_reg = READ_REG(PCR_SYSCLK_CONF_REG); WRITE_REG(PCR_SYSCLK_CONF_REG, (pcr_sysclk_conf_reg & ~PCR_SOC_CLK_SEL_M) | (PCR_SOC_CLK_MAX << PCR_SOC_CLK_SEL_S)); #else @@ -92,7 +125,7 @@ static void reset_cpu_freq() { /* Restore saved sysclk_conf and cpu_per_conf registers. Use only if set_max_cpu_freq() has been called. */ - #if ESP32C6 || ESP32H2 || ESP32C5BETA3 + #if ESP32C61 || ESP32C6 || ESP32H2 || ESP32C5 || ESP32C5BETA3 if (can_use_max_cpu_freq() && pcr_sysclk_conf_reg != 0) { WRITE_REG(PCR_SYSCLK_CONF_REG, (READ_REG(PCR_SYSCLK_CONF_REG) & ~PCR_SOC_CLK_SEL_M) | (pcr_sysclk_conf_reg & PCR_SOC_CLK_SEL_M)); @@ -123,7 +156,7 @@ static void disable_watchdogs() } #endif // WITH_USB_JTAG_SERIAL -#if ESP32S3 && !ESP32S3BETA2 +#if (ESP32S3 && !ESP32S3BETA2) || ESP32P4 bool large_flash_mode = false; bool flash_larger_than_16mb() @@ -141,7 +174,7 @@ bool flash_larger_than_16mb() uint8_t flid_lowbyte = (flash_id >> 16) & 0xFF; return ((flid_lowbyte >= 0x19 && flid_lowbyte < 0x30) || (flid_lowbyte >= 0x39)); // See DETECTED_FLASH_SIZES in esptool } -#endif // ESP32S3 +#endif // (ESP32S3 && !ESP32S3BETA2) || ESP32P4 static void stub_handle_rx_byte(char byte) { @@ -523,6 +556,8 @@ void stub_main() esp_rom_opiflash_legacy_driver_init(&flash_driver); esp_rom_opiflash_wait_idle(); } + #elif ESP32P4 + large_flash_mode = flash_larger_than_16mb(); #endif //ESP32S3 && !ESP32S3BETA2 SPIParamCfg(0, FLASH_MAX_SIZE, FLASH_BLOCK_SIZE, FLASH_SECTOR_SIZE, FLASH_PAGE_SIZE, FLASH_STATUS_MASK); diff --git a/tools/esptool_py/flasher_stub/stub_io.c b/tools/flasher_stub/stub_io.c similarity index 96% rename from tools/esptool_py/flasher_stub/stub_io.c rename to tools/flasher_stub/stub_io.c index f86f67e7ac..c0e559b1fe 100644 --- a/tools/esptool_py/flasher_stub/stub_io.c +++ b/tools/flasher_stub/stub_io.c @@ -65,7 +65,7 @@ static void stub_configure_rx_uart(void) #if WITH_USB_JTAG_SERIAL if (stub_uses_usb_jtag_serial()) { #if IS_RISCV - #if ESP32P4 + #if ESP32P4 || ESP32C5 || ESP32C61 WRITE_REG(INTERRUPT_CORE0_USB_INTR_MAP_REG, ETS_USB_INUM + CLIC_EXT_INTR_NUM_OFFSET); #else WRITE_REG(INTERRUPT_CORE0_USB_INTR_MAP_REG, ETS_USB_INUM); // Route USB interrupt to CPU @@ -155,6 +155,11 @@ static void stub_configure_rx_usb(void) intr_matrix_set(0, ETS_USB_INTR_SOURCE, ETS_USB_INUM); #elif ESP32S3 WRITE_REG(INTERRUPT_CORE0_USB_INTR_MAP_REG, ETS_USB_INUM); + #elif ESP32P4 + // Additional setting to solve missing DCONN event on ESP32P4 (IDF-9953). + REG_SET_MASK(HP_SYS_USBOTG20_CTRL_REG, 1 << 21); /* set HP_SYS_OTG_SUSPENDM */ + WRITE_REG(INTERRUPT_CORE0_USB_OTG_INT_MAP_REG, ETS_USB_INUM + CLIC_EXT_INTR_NUM_OFFSET); + esprv_intc_int_set_priority(ETS_USB_INUM, 1); #endif ets_isr_attach(ETS_USB_INUM, usb_dw_isr_handler_wrapper, NULL); ets_isr_unmask(1 << ETS_USB_INUM); diff --git a/tools/esptool_py/flasher_stub/stub_write_flash.c b/tools/flasher_stub/stub_write_flash.c similarity index 71% rename from tools/esptool_py/flasher_stub/stub_write_flash.c rename to tools/flasher_stub/stub_write_flash.c index e925d9f74f..55900f6eb9 100644 --- a/tools/esptool_py/flasher_stub/stub_write_flash.c +++ b/tools/flasher_stub/stub_write_flash.c @@ -86,7 +86,7 @@ static bool spiflash_is_ready(void) return (status_value & STATUS_WIP_BIT) == 0; } -static void spi_write_enable(void) +void spi_write_enable(void) { while(!spiflash_is_ready()) { } @@ -136,12 +136,12 @@ SpiFlashOpResult SPIUnlock(void) } #endif // ESP32_OR_LATER -#if defined(ESP32S3) && !defined(ESP32S3BETA2) +#if (ESP32S3 && !ESP32S3BETA2) || ESP32P4 static esp_rom_spiflash_result_t page_program_internal(int spi_num, uint32_t spi_addr, uint8_t* addr_source, uint32_t byte_length) { uint32_t temp_addr; int32_t temp_bl; - esp_rom_opiflash_wait_idle(); + esp_rom_spiflash_wait_idle(); temp_addr = spi_addr; temp_bl = byte_length; uint32_t temp_len = 0; @@ -151,7 +151,7 @@ static esp_rom_spiflash_result_t page_program_internal(int spi_num, uint32_t spi int dummy = 0; while (temp_bl > 0 ) { - esp_rom_opiflash_wren(); + spi_write_enable(); temp_len = (temp_bl >= 32) ? 32 : temp_bl; //32 = write_sub_len esp_rom_opiflash_exec_cmd(spi_num, SPI_FLASH_FASTRD_MODE, cmd, cmd_len, @@ -161,23 +161,21 @@ static esp_rom_spiflash_result_t page_program_internal(int spi_num, uint32_t spi NULL, 0, ESP_ROM_OPIFLASH_SEL_CS0, true); - esp_rom_opiflash_wait_idle(); + esp_rom_spiflash_wait_idle(); addr_source += temp_len; temp_addr += temp_len; temp_bl -= temp_len; } return ESP_ROM_SPIFLASH_RESULT_OK; } -#endif // ESP32S3 -#if defined(ESP32S3) && !defined(ESP32S3BETA2) static esp_rom_spiflash_result_t SPIWrite4B(int spi_num, uint32_t target, uint8_t *src_addr, int32_t len) { uint32_t page_size = 256; uint32_t pgm_len, pgm_num; uint8_t i; - esp_rom_opiflash_wait_idle(); + esp_rom_spiflash_wait_idle(); pgm_len = page_size - (target % page_size); if (len < pgm_len) { page_program_internal(spi_num, target, src_addr, len); @@ -192,10 +190,112 @@ static esp_rom_spiflash_result_t SPIWrite4B(int spi_num, uint32_t target, uint8_ //remain parts to program page_program_internal(spi_num, target + pgm_len, (src_addr + pgm_len), len - pgm_len); } - esp_rom_opiflash_wait_idle(); + esp_rom_spiflash_wait_idle(); return ESP_ROM_SPIFLASH_RESULT_OK; } -#endif // defined(ESP32S3) && !defined(ESP32S3BETA2) + +SpiFlashOpResult SPI_Encrypt_Write_4B(uint32_t flash_addr, const void* data, uint32_t len) { + const uint8_t *data_bytes = (const uint8_t *)data; + SpiFlashOpResult result = SPI_FLASH_RESULT_OK; + + if (flash_addr % 16 != 0 || len % 16 != 0) + { + // Minimum 128-bit size & alignment + return SPI_FLASH_RESULT_ERR; + } + + esp_rom_spiflash_write_encrypted_enable(); + + WRITE_REG(AES_XTS_DESTINATION_REG, 0); + + result = esp_rom_spiflash_unlock(); + if (result != SPI_FLASH_RESULT_OK) + { + goto done_encrypt_write_4B; + } + + while (len > 0) + { + int next_block; + int timeout = 0; + const int TIMEOUT_LIMIT = 100000; + int page_size = 256; + + // Write the largest block possible + if (flash_addr % 64 == 0 && len >= 64 && (page_size - flash_addr % page_size >= 64)) { + next_block = 64; + } + else if (flash_addr % 32 == 0 && len >= 32 && (page_size - flash_addr % page_size >= 32)) { + next_block = 32; + } + else { + next_block = 16; + } + + WRITE_REG(AES_XTS_SIZE_REG, next_block >> 5); // 0, 1, 2 + + uint32_t plaintext_offs = (flash_addr % MAX_ENCRYPT_BLOCK); + memcpy((void *)(AES_XTS_PLAIN_BASE + plaintext_offs), data_bytes, next_block); + + WRITE_REG(AES_XTS_PHYSICAL_ADDR_REG, flash_addr); + + // Perform the encryption + WRITE_REG(AES_XTS_TRIGGER_REG, 1); + + // Prepare the flash chip (same time as AES operation, for performance) + spi_write_enable(); + + // Wait for the encryption to finish + while (READ_REG(AES_XTS_STATE_REG) != 0x2 && timeout < TIMEOUT_LIMIT) { + timeout++; + } + if (timeout == TIMEOUT_LIMIT) { + result = SPI_FLASH_RESULT_TIMEOUT; + goto done_encrypt_write_4B; + } + + esp_rom_spiflash_wait_idle(); + + // Make the result visible to the SPI controller + WRITE_REG(AES_XTS_RELEASE_REG, 1); + + while (READ_REG(AES_XTS_STATE_REG) != 0x3 && timeout < TIMEOUT_LIMIT) { + timeout++; + } + if (timeout == TIMEOUT_LIMIT) { + result = SPI_FLASH_RESULT_TIMEOUT; + goto done_encrypt_write_4B; + } + + result = esp_rom_spiflash_wait_idle(); + if (result != SPI_FLASH_RESULT_OK) { + goto done_encrypt_write_4B; + } + + // Write the encrypted page to flash + esp_rom_opiflash_exec_cmd(1, SPI_FLASH_FASTRD_MODE, + CMD_PROGRAM_PAGE_4B, 8, + flash_addr, 32, // address is set in AES_XTS_PHYSICAL_ADDR_REG, only the length is actually needed + 0, + NULL, (8 * next_block - 1), // data is exposed by AES-XTS, just set the length + NULL, 0, + ESP_ROM_OPIFLASH_SEL_CS0, + true); + + WRITE_REG(AES_XTS_DESTROY_REG, 1); + + len -= next_block; + data_bytes += next_block; + flash_addr += next_block; + } + + result = esp_rom_spiflash_wait_idle(); + +done_encrypt_write_4B: + esp_rom_spiflash_write_encrypted_disable(); + return result; +} +#endif // (ESP32S3 && !ESP32S3BETA2) || ESP32P4 esp_command_error handle_flash_begin(uint32_t total_size, uint32_t offset) { fs.in_flash_mode = true; @@ -302,6 +402,60 @@ static void start_next_erase(void) WRITE_REG(SPI_CMD_REG, command); while(READ_REG(SPI_CMD_REG) != 0) { } } + #elif ESP32P4 + if (large_flash_mode) { + esp_rom_spiflash_wait_idle(); + spi_write_enable(); + if (block_erase) { + if (fs.next_erase_sector * FLASH_SECTOR_SIZE < (1 << 24)) { + esp_rom_opiflash_exec_cmd(1, SPI_FLASH_SLOWRD_MODE, + CMD_LARGE_BLOCK_ERASE, 8, + fs.next_erase_sector * FLASH_SECTOR_SIZE, 24, + 0, + NULL, 0, + NULL, 0, + 1, + true); + } else { + esp_rom_opiflash_exec_cmd(1, SPI_FLASH_SLOWRD_MODE, + CMD_LARGE_BLOCK_ERASE_4B, 8, + fs.next_erase_sector * FLASH_SECTOR_SIZE, 32, + 0, + NULL, 0, + NULL, 0, + 1, + true); + } + } + else { + if (fs.next_erase_sector * FLASH_SECTOR_SIZE < (1 << 24)) { + esp_rom_opiflash_exec_cmd(1, SPI_FLASH_SLOWRD_MODE, + CMD_SECTOR_ERASE, 8, + fs.next_erase_sector * FLASH_SECTOR_SIZE, 24, + 0, + NULL, 0, + NULL, 0, + 1, + true); + } else { + esp_rom_opiflash_exec_cmd(1, SPI_FLASH_SLOWRD_MODE, + CMD_SECTOR_ERASE_4B, 8, + fs.next_erase_sector * FLASH_SECTOR_SIZE, 32, + 0, + NULL, 0, + NULL, 0, + 1, + true); + } + } + esp_rom_spiflash_wait_idle(); + } else { + uint32_t addr = fs.next_erase_sector * FLASH_SECTOR_SIZE; + uint32_t command = block_erase ? SPI_FLASH_BE : SPI_FLASH_SE; /* block erase, 64KB : sector erase, 4KB */ + WRITE_REG(SPI_ADDR_REG, addr & 0xffffff); + WRITE_REG(SPI_CMD_REG, command); + while(READ_REG(SPI_CMD_REG) != 0) { } + } #else uint32_t addr = fs.next_erase_sector * FLASH_SECTOR_SIZE; uint32_t command = block_erase ? SPI_FLASH_BE : SPI_FLASH_SE; /* block erase, 64KB : sector erase, 4KB */ @@ -345,7 +499,7 @@ void handle_flash_data(void *data_buf, uint32_t length) { {} /* do the actual write */ - #if defined(ESP32S3) && !defined(ESP32S3BETA2) + #if (ESP32S3 && !ESP32S3BETA2) || ESP32P4 if (large_flash_mode){ res = SPIWrite4B(1, fs.next_write, data_buf, length); } else { @@ -397,6 +551,12 @@ void handle_flash_encrypt_data(void *data_buf, uint32_t length) { /* do the actual write */ #if ESP32 res = esp_rom_spiflash_write_encrypted(fs.next_write, data_buf, length); +#elif (ESP32S3 && !ESP32S3BETA2) || ESP32P4 + if (large_flash_mode){ + res = SPI_Encrypt_Write_4B(fs.next_write, data_buf, length); + } else { + res = SPI_Encrypt_Write(fs.next_write, data_buf, length); + } #else res = SPI_Encrypt_Write(fs.next_write, data_buf, length); #endif diff --git a/tools/esptool_py/flasher_stub/wrap_stub.py b/tools/flasher_stub/wrap_stub.py similarity index 100% rename from tools/esptool_py/flasher_stub/wrap_stub.py rename to tools/flasher_stub/wrap_stub.py diff --git a/zephyr/esp32c6/CMakeLists.txt b/zephyr/esp32c6/CMakeLists.txt index ebc0d12de8..29f7331a2f 100644 --- a/zephyr/esp32c6/CMakeLists.txt +++ b/zephyr/esp32c6/CMakeLists.txt @@ -127,7 +127,7 @@ if(CONFIG_SOC_SERIES_ESP32C6) -T${CMAKE_CURRENT_SOURCE_DIR}/../../components/esp_rom/${CONFIG_SOC_SERIES}/ld/${CONFIG_SOC_SERIES}.rom.version.ld -T${CMAKE_CURRENT_SOURCE_DIR}/../../components/esp_rom/${CONFIG_SOC_SERIES}/ld/${CONFIG_SOC_SERIES}.rom.wdt.ld -T${CMAKE_CURRENT_SOURCE_DIR}/../../components/soc/${CONFIG_SOC_SERIES}/ld/${CONFIG_SOC_SERIES}.peripherals.ld - -T${CMAKE_CURRENT_SOURCE_DIR}/../../tools/esptool_py/flasher_stub/ld/rom_32c6.ld + -T${CMAKE_CURRENT_SOURCE_DIR}/../../tools/flasher_stub/ld/rom_32c6.ld ) zephyr_compile_definitions(ESP_PLATFORM)