diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 0000000..96bff3b --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,63 @@ +name: Build Release + +on: + release: + types: [published] + +jobs: + build: + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + target: + - "ttgo-lora32-v21" + - "heltec-lora32-v2" + - "heltec_wifi_lora_32_V3" + - "ESP32_DIY_LoRa" + - "ESP32_DIY_1W_LoRa" + - "ttgo-t-beam-v1_2" + - "ttgo-t-beam-v1" + - "ttgo-t-beam-v1_SX1268" + - "ttgo-t-beam-v1_2_SX1262" + # - "heltec_wireless_stick_lite" # NOT FULLY TESTED + steps: + - uses: actions/checkout@v3 + + - uses: actions/setup-python@v4 + with: + python-version: "3.9" + + - name: Install PlatformIO Core + run: pip install --upgrade platformio + + - name: Build target + run: pio run -e ${{ matrix.target }} + + - name: Build FS + run: pio run --target buildfs -e ${{ matrix.target }} + + - name: Move Files + run: | + mkdir -p installer/firmware + cp .pio/build/${{ matrix.target }}/firmware.bin installer/firmware/ + cp .pio/build/${{ matrix.target }}/bootloader.bin installer/firmware/ + cp .pio/build/${{ matrix.target }}/partitions.bin installer/firmware/ + cp .pio/build/${{ matrix.target }}/spiffs.bin installer/firmware/ + cp ~/.platformio/packages/framework-arduinoespressif32/tools/partitions/boot_app0.bin installer/firmware/ + + - name: Install Zip + run: sudo apt-get install zip + + - name: Archive Files + run: zip -r installer.zip installer/ + + - name: Upload Release Asset + uses: actions/upload-release-asset@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + upload_url: ${{ github.event.release.upload_url }} + asset_path: ./installer.zip + asset_name: ${{ matrix.target }}.zip + asset_content_type: application/zip diff --git a/.gitignore b/.gitignore index 4de822f..3d683e2 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,7 @@ .vscode/launch.json .vscode/ipch .DS_Store +/data_embed/*.gz +installer/firmware +installer/*.bin +**/__pycache__/ \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..f179091 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,4 @@ +{ + "editor.tabSize": 4, + "editor.formatOnSave": true +} diff --git a/installer/bin/esptool/esptool.py b/installer/bin/esptool/esptool.py new file mode 100644 index 0000000..60e5bd3 --- /dev/null +++ b/installer/bin/esptool/esptool.py @@ -0,0 +1,34 @@ +#!/usr/bin/env python +# +# 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 + +# This executable script is a thin wrapper around the main functionality +# in the esptool Python package + +# When updating this script, please also update espefuse.py and espsecure.py + +import contextlib +import os +import sys + +if os.name != "nt": + # Linux/macOS: remove current script directory to avoid importing this file + # as a module; we want to import the installed esptool module instead + with contextlib.suppress(ValueError): + if sys.path[0].endswith("/bin"): + sys.path.pop(0) + sys.path.remove(os.path.dirname(sys.executable)) + + # Linux/macOS: delete imported module entry to force Python to load + # the module from scratch; this enables importing esptool module in + # other Python scripts + with contextlib.suppress(KeyError): + del sys.modules["esptool"] + +import esptool + +if __name__ == "__main__": + esptool._main() diff --git a/installer/bin/esptool/esptool/__init__.py b/installer/bin/esptool/esptool/__init__.py new file mode 100644 index 0000000..24272f2 --- /dev/null +++ b/installer/bin/esptool/esptool/__init__.py @@ -0,0 +1,1055 @@ +# 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 + +__all__ = [ + "chip_id", + "detect_chip", + "dump_mem", + "elf2image", + "erase_flash", + "erase_region", + "flash_id", + "get_security_info", + "image_info", + "load_ram", + "make_image", + "merge_bin", + "read_flash", + "read_flash_status", + "read_mac", + "read_mem", + "run", + "verify_flash", + "version", + "write_flash", + "write_flash_status", + "write_mem", +] + +__version__ = "4.5.1" + +import argparse +import inspect +import os +import shlex +import sys +import time +import traceback + +from esptool.cmds import ( + chip_id, + detect_chip, + detect_flash_size, + dump_mem, + elf2image, + erase_flash, + erase_region, + flash_id, + get_security_info, + image_info, + load_ram, + make_image, + merge_bin, + read_flash, + read_flash_status, + read_mac, + read_mem, + run, + verify_flash, + version, + write_flash, + write_flash_status, + write_mem, +) +from esptool.config import load_config_file +from esptool.loader import DEFAULT_CONNECT_ATTEMPTS, ESPLoader, list_ports +from esptool.targets import CHIP_DEFS, CHIP_LIST, ESP32ROM +from esptool.util import ( + FatalError, + NotImplementedInROMError, + flash_size_bytes, + strip_chip_name, +) + +import serial + + +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( + "--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", + ) + + 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), + ) + + subparsers = parser.add_subparsers( + dest="operation", help="Run esptool.py {command} -h for additional help" + ) + + def add_spi_connection_arg(parent): + parent.add_argument( + "--spi-connection", + "-sc", + help="ESP32-only argument. 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).", + 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") + + 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) + + 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", + ) + + def add_spi_flash_subparsers(parent, allow_keep, auto_detect): + """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 = [] + + parent.add_argument( + "--flash_freq", + "-ff", + help="SPI Flash frequency", + choices=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", + "-fm", + help="SPI Flash mode", + choices=extra_keep_args + ["qio", "qout", "dio", "dout"], + default=os.environ.get("ESPTOOL_FM", "keep" if allow_keep else "qio"), + ) + parent.add_argument( + "--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", + ], + default=os.environ.get("ESPTOOL_FS", "keep" if allow_keep else "1MB"), + ) + add_spi_connection_arg(parent) + + parser_write_flash = subparsers.add_parser( + "write_flash", help="Write a binary blob to flash" + ) + + 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 an application image" + ) + parser_image_info.add_argument("filename", help="Image file to parse") + 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 genereate 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, + ) + + add_spi_flash_subparsers(parser_elf2image, allow_keep=False, auto_detect=False) + + subparsers.add_parser("read_mac", help="Read MAC address from OTP ROM") + + 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) + + 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, + ) + + parser_write_status = subparsers.add_parser( + "write_flash_status", help="Write SPI flash status register" + ) + + 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) + + parser_read_flash = subparsers.add_parser( + "read_flash", help="Read SPI flash content" + ) + add_spi_connection_arg(parser_read_flash) + 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", type=arg_auto_int + ) + 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" + ) + + 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, + ) + parser_verify_flash.add_argument( + "--diff", "-d", help="Show differences", choices=["no", "yes"], default="no" + ) + 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) + + 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)", + type=arg_auto_int, + ) + + parser_merge_bin = subparsers.add_parser( + "merge_bin", + help="Merge multiple raw binary files into a single file for later flashing", + ) + + 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", default="raw" + ) # for future expansion + add_spi_flash_subparsers(parser_merge_bin, allow_keep=True, auto_detect=False) + + parser_merge_bin.add_argument( + "--target-offset", + "-t", + help="Target offset where the output file will be flashed", + type=arg_auto_int, + default=0, + ) + parser_merge_bin.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, + ) + + subparsers.add_parser("get_security_info", help="Get some security-related data") + + subparsers.add_parser("version", help="Print esptool version") + + # 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 + + argv = expand_file_arguments(argv or sys.argv[1:]) + + args = parser.parse_args(argv) + print("esptool.py v%s" % __version__) + load_config_file(verbose=True) + + # operation function can take 1 arg (args), 2 args (esp, arg) + # or be a member function of the ESPLoader class. + + if args.operation is None: + parser.print_help() + sys.exit(1) + + # 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 + ): + raise FatalError( + "Options --encrypt and --encrypt-files " + "must not be specified at the same time." + ) + + 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() + print("Found %d serial ports" % len(ser_list)) + else: + ser_list = [args.port] + 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, + ) + + 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: + esp = esp.run_stub() + + 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 + ) + + # override common SPI flash parameter stuff if configured to do so + if hasattr(args, "spi_connection") and args.spi_connection is not None: + if esp.CHIP_NAME != "ESP32": + raise FatalError( + "Chip %s does not support --spi-connection option." % esp.CHIP_NAME + ) + print("Configuring SPI flash mode...") + esp.flash_spi_attach(args.spi_connection) + elif args.no_stub: + print("Enabling default SPI flash mode...") + # ROM loader doesn't enable flash unless we explicitly do it + esp.flash_spi_attach(0) + + # 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." + ) + 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...") + detect_flash_size(esp, args) + if args.flash_size != "keep": # TODO: should set this even with 'keep' + esp.flash_set_parameters(flash_size_bytes(args.flash_size)) + # Check if stub supports chosen flash size + if esp.IS_STUB and args.flash_size in ("32MB", "64MB", "128MB"): + print( + "WARNING: Flasher stub doesn't fully support flash size larger " + "than 16MB, in case of failure use --no-stub." + ) + + if esp.IS_STUB and hasattr(args, "address") and hasattr(args, "size"): + if 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) + + +def arg_auto_int(x): + return int(x, 0) + + +def get_port_list(): + 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" + ) + return sorted(ports.device for ports in list_ports.comports()) + + +def expand_file_arguments(argv): + """ + 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. + Values are added "as-is", as if they were specified in this order + on the command line. + """ + new_args = [] + expanded = False + for arg in argv: + if arg.startswith("@"): + expanded = True + with open(arg[1:], "r") as f: + for line in f.readlines(): + new_args += shlex.split(line) + else: + new_args.append(arg) + if expanded: + print("esptool %s" % (" ".join(new_args[1:]))) + return new_args + return argv + + +def get_default_connected_device( + serial_list, + port, + connect_attempts, + initial_baud, + chip="auto", + trace=False, + before="default_reset", +): + _esp = None + for each_port in reversed(serial_list): + print("Serial port %s" % each_port) + try: + if chip == "auto": + _esp = detect_chip( + each_port, initial_baud, before, trace, connect_attempts + ) + else: + chip_class = CHIP_DEFS[chip] + _esp = chip_class(each_port, initial_baud, trace) + _esp.connect(before, connect_attempts) + break + except (FatalError, OSError) as err: + if port is not None: + raise + print("%s failed to connect: %s" % (each_port, 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() == "SPI": + value = 0 + elif value.upper() == "HSPI": + value = 1 + elif "," in value: + values = value.split(",") + if len(values) != 5: + raise argparse.ArgumentError( + self, + "%s is not a valid list of comma-separate pin numbers. " + "Must be 5 numbers - CLK,Q,D,HD,CS." % value, + ) + try: + values = tuple(int(v, 0) for v in values) + except ValueError: + raise argparse.ArgumentError( + self, + "%s is not a valid argument. All pins must be numeric values" + % values, + ) + if any([v for v in values if v > 33 or v < 0]): + raise argparse.ArgumentError( + self, "Pin numbers must be in the range 0-33." + ) + # encode the pin numbers as a 32-bit integer with packed 6-bit values, + # the same way ESP32 ROM takes them + # TODO: make this less ESP32 ROM specific somehow... + clk, q, d, hd, cs = values + value = (hd << 24) | (cs << 18) | (d << 12) | (q << 6) | clk + else: + raise argparse.ArgumentError( + self, + "%s is not a valid spi-connection value. " + "Values are SPI, HSPI, or a sequence of 5 pin numbers CLK,Q,D,HD,CS)." + % value, + ) + 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", + ) + 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(): + try: + main() + except FatalError as e: + print(f"\nA fatal error occurred: {e}") + sys.exit(2) + except serial.serialutil.SerialException as e: + print(f"\nA serial exception error occurred: {e}") + print( + "Note: This error originates from pySerial. " + "It is likely not a problem with esptool, " + "but with the hardware connection or drivers." + ) + print( + "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.") + sys.exit(2) + + +if __name__ == "__main__": + _main() diff --git a/installer/bin/esptool/esptool/__main__.py b/installer/bin/esptool/esptool/__main__.py new file mode 100644 index 0000000..11e3bce --- /dev/null +++ b/installer/bin/esptool/esptool/__main__.py @@ -0,0 +1,9 @@ +# 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 esptool + +if __name__ == "__main__": + esptool._main() diff --git a/installer/bin/esptool/esptool/bin_image.py b/installer/bin/esptool/esptool/bin_image.py new file mode 100644 index 0000000..b001a1e --- /dev/null +++ b/installer/bin/esptool/esptool/bin_image.py @@ -0,0 +1,1239 @@ +# 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 binascii +import copy +import hashlib +import io +import os +import re +import struct + +from .loader import ESPLoader +from .targets import ( + ESP32C2ROM, + ESP32C3ROM, + ESP32C6BETAROM, + ESP32C6ROM, + ESP32H2BETA1ROM, + ESP32H2BETA2ROM, + ESP32H2ROM, + ESP32ROM, + ESP32S2ROM, + ESP32S3BETA2ROM, + ESP32S3ROM, + ESP8266ROM, +) +from .util import FatalError, byte, pad_to + + +def align_file_position(f, size): + """Align the position in the file to the next block of specified size""" + align = (size - 1) - (f.tell() % size) + f.seek(align, 1) + + +def LoadFirmwareImage(chip, image_file): + """ + 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). + """ + + 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, + "esp32h2": ESP32H2FirmwareImage, + }[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) + + +class ImageSegment(object): + """Wrapper class for a segment in an ESP image + (very similar to a section in an ELFImage also)""" + + def __init__(self, addr, data, file_offs=None): + self.addr = addr + self.data = data + self.file_offs = file_offs + self.include_in_checksum = True + if self.addr != 0: + self.pad_to_alignment( + 4 + ) # pad all "real" ImageSegments 4 byte aligned length + + def copy_with_new_addr(self, new_addr): + """Return a new ImageSegment with same data, but mapped at + a new address.""" + return ImageSegment(new_addr, self.data, 0) + + def split_image(self, split_len): + """Return a new ImageSegment which splits "split_len" bytes + from the beginning of the data. Remaining bytes are kept in + this segment object (and the start address is adjusted to match.)""" + result = copy.copy(self) + result.data = self.data[:split_len] + self.data = self.data[split_len:] + self.addr += split_len + self.file_offs = None + result.file_offs = None + return result + + def __repr__(self): + r = "len 0x%05x load 0x%08x" % (len(self.data), self.addr) + if self.file_offs is not None: + r += " file_offs 0x%08x" % (self.file_offs) + return r + + def get_memory_type(self, image): + """ + Return a list describing the memory type(s) that is covered by this + segment's start address. + """ + return [ + map_range[2] + for map_range in image.ROM_LOADER.MEMORY_MAP + if map_range[0] <= self.addr < map_range[1] + ] + + def pad_to_alignment(self, alignment): + self.data = pad_to(self.data, alignment, b"\x00") + + +class ELFSection(ImageSegment): + """Wrapper class for a section in an ELF image, has a section + name as well as the common properties of an ImageSegment.""" + + def __init__(self, name, addr, data): + super(ELFSection, self).__init__(addr, data) + self.name = name.decode("utf-8") + + def __repr__(self): + return "%s %s" % (self.name, super(ELFSection, self).__repr__()) + + +class BaseFirmwareImage(object): + SEG_HEADER_LEN = 8 + SHA256_DIGEST_LEN = 32 + + """ Base class with common firmware image functions """ + + def __init__(self): + self.segments = [] + self.entrypoint = 0 + self.elf_sha256 = None + self.elf_sha256_offset = 0 + self.pad_to_size = 0 + + def load_common_header(self, load_file, expected_magic): + ( + magic, + segments, + self.flash_mode, + self.flash_size_freq, + self.entrypoint, + ) = struct.unpack(" 16: + raise FatalError( + "Invalid segment count %d (max 16). " + "Usually this indicates a linker script problem." % len(self.segments) + ) + + def load_segment(self, f, is_irom_segment=False): + """Load the next segment from the image file""" + file_offs = f.tell() + (offset, size) = struct.unpack(" 0x40200000 or offset < 0x3FFE0000 or size > 65536: + print("WARNING: Suspicious segment 0x%x, length %d" % (offset, size)) + + def maybe_patch_segment_data(self, f, segment_data): + """ + If SHA256 digest of the ELF file needs to be inserted into this segment, do so. + Returns segment data. + """ + segment_len = len(segment_data) + file_pos = f.tell() # file_pos is position in the .bin file + if ( + self.elf_sha256_offset >= file_pos + and self.elf_sha256_offset < file_pos + segment_len + ): + # SHA256 digest needs to be patched into this binary segment, + # calculate offset of the digest inside the binary segment. + patch_offset = self.elf_sha256_offset - file_pos + # Sanity checks + if ( + patch_offset < self.SEG_HEADER_LEN + or patch_offset + self.SHA256_DIGEST_LEN > segment_len + ): + raise FatalError( + "Cannot place SHA256 digest on segment boundary" + "(elf_sha256_offset=%d, file_pos=%d, segment_size=%d)" + % (self.elf_sha256_offset, file_pos, segment_len) + ) + # offset relative to the data part + patch_offset -= self.SEG_HEADER_LEN + if ( + segment_data[patch_offset : patch_offset + self.SHA256_DIGEST_LEN] + != b"\x00" * self.SHA256_DIGEST_LEN + ): + raise FatalError( + "Contents of segment at SHA256 digest offset 0x%x are not all zero." + " Refusing to overwrite." % self.elf_sha256_offset + ) + assert len(self.elf_sha256) == self.SHA256_DIGEST_LEN + segment_data = ( + segment_data[0:patch_offset] + + self.elf_sha256 + + segment_data[patch_offset + self.SHA256_DIGEST_LEN :] + ) + return segment_data + + def save_segment(self, f, segment, checksum=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(" 0: + if len(irom_segments) != 1: + raise FatalError( + "Found %d segments that could be irom0. Bad ELF file?" + % len(irom_segments) + ) + return irom_segments[0] + return None + + def get_non_irom_segments(self): + irom_segment = self.get_irom_segment() + return [s for s in self.segments if s != irom_segment] + + def merge_adjacent_segments(self): + if not self.segments: + return # nothing to merge + + segments = [] + # The easiest way to merge the sections is the browse them backward. + for i in range(len(self.segments) - 1, 0, -1): + # elem is the previous section, the one `next_elem` may need to be + # merged in + elem = self.segments[i - 1] + next_elem = self.segments[i] + if all( + ( + elem.get_memory_type(self) == next_elem.get_memory_type(self), + elem.include_in_checksum == next_elem.include_in_checksum, + next_elem.addr == elem.addr + len(elem.data), + ) + ): + # Merge any segment that ends where the next one starts, + # without spanning memory types + # + # (don't 'pad' any gaps here as they may be excluded from the image + # due to 'noinit' or other reasons.) + elem.data += next_elem.data + else: + # The section next_elem cannot be merged into the previous one, + # which means it needs to be part of the final segments. + # As we are browsing the list backward, the elements need to be + # inserted at the beginning of the final list. + segments.insert(0, next_elem) + + # The first segment will always be here as it cannot be merged into any + # "previous" section. + segments.insert(0, self.segments[0]) + + # note: we could sort segments here as well, but the ordering of segments is + # sometimes important for other reasons (like embedded ELF SHA-256), + # so we assume that the linker script will have produced any adjacent sections + # in linear order in the ELF, anyhow. + self.segments = segments + + def set_mmu_page_size(self, size): + """ + If supported, this should be overridden by the chip-specific class. + Gets called in elf2image. + """ + print( + "WARNING: Changing MMU page size is not supported on {}! " + "Defaulting to 64KB.".format(self.ROM_LOADER.CHIP_NAME) + ) + + +class ESP8266ROMFirmwareImage(BaseFirmwareImage): + """'Version 1' firmware image, segments loaded directly by the ROM bootloader.""" + + ROM_LOADER = ESP8266ROM + + def __init__(self, load_file=None): + super(ESP8266ROMFirmwareImage, self).__init__() + self.flash_mode = 0 + self.flash_size_freq = 0 + self.version = 1 + + if load_file is not None: + segments = self.load_common_header(load_file, ESPLoader.ESP_IMAGE_MAGIC) + + for _ in range(segments): + self.load_segment(load_file) + self.checksum = self.read_checksum(load_file) + + self.verify() + + def default_output_name(self, input_file): + """Derive a default output name from the ELF name.""" + return input_file + "-" + + def save(self, basename): + """Save a set of V1 images for flashing. Parameter is a base filename.""" + # IROM data goes in its own plain binary file + 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: + 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) + + +ESP8266ROM.BOOTLOADER_IMAGE = ESP8266ROMFirmwareImage + + +class ESP8266V2FirmwareImage(BaseFirmwareImage): + """'Version 2' firmware image, segments loaded by software bootloader stub + (ie Espressif bootloader or rboot) + """ + + ROM_LOADER = ESP8266ROM + # First byte of the "v2" application image + IMAGE_V2_MAGIC = 0xEA + + # First 'segment' value in a "v2" application image, + # appears to be a constant version value? + IMAGE_V2_SEGMENT = 4 + + def __init__(self, load_file=None): + super(ESP8266V2FirmwareImage, self).__init__() + self.version = 2 + if load_file is not None: + segments = self.load_common_header(load_file, self.IMAGE_V2_MAGIC) + 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 + ) + + # irom segment comes before the second header + # + # the file is saved in the image with a zero load address + # in the header, so we need to calculate a load address + irom_segment = self.load_segment(load_file, True) + # for actual mapped addr, add ESP8266ROM.IROM_MAP_START + flashing_addr + 8 + irom_segment.addr = 0 + irom_segment.include_in_checksum = False + + first_flash_mode = self.flash_mode + first_flash_size_freq = self.flash_size_freq + first_entrypoint = self.entrypoint + # load the second header + + 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) + ) + 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) + ) + 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) + ) + + # load all the usual segments + for _ in range(segments): + self.load_segment(load_file) + self.checksum = self.read_checksum(load_file) + + self.verify() + + def default_output_name(self, input_file): + """Derive a default output name from the ELF name.""" + irom_segment = self.get_irom_segment() + if irom_segment is not None: + irom_offs = irom_segment.addr - ESP8266ROM.IROM_MAP_START + else: + irom_offs = 0 + return "%s-0x%05x.bin" % ( + os.path.splitext(input_file)[0], + irom_offs & ~(ESPLoader.FLASH_SECTOR_SIZE - 1), + ) + + def save(self, filename): + with open(filename, "wb") as f: + # Save first header for irom0 segment + f.write( + struct.pack( + b" 0: + last_addr = flash_segments[0].addr + 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. " + "Suggest changing linker script or ELF to merge sections." + % (segment.addr, last_addr) + ) + last_addr = segment.addr + + def get_alignment_data_needed(segment): + # Actual alignment (in data bytes) required for a segment header: + # positioned so that after we write the next 8 byte header, + # file_offs % IROM_ALIGN == segment.addr % IROM_ALIGN + # + # (this is because the segment's vaddr may not be IROM_ALIGNed, + # more likely is aligned IROM_ALIGN+0x18 + # to account for the binary file header + align_past = (segment.addr % self.IROM_ALIGN) - self.SEG_HEADER_LEN + pad_len = (self.IROM_ALIGN - (f.tell() % self.IROM_ALIGN)) + align_past + if pad_len == 0 or pad_len == self.IROM_ALIGN: + return 0 # already aligned + + # subtract SEG_HEADER_LEN a second time, + # as the padding block has a header as well + pad_len -= self.SEG_HEADER_LEN + if pad_len < 0: + pad_len += self.IROM_ALIGN + return pad_len + + # try to fit each flash segment on a 64kB aligned boundary + # by padding with parts of the non-flash segments... + while len(flash_segments) > 0: + segment = flash_segments[0] + pad_len = get_alignment_data_needed(segment) + if pad_len > 0: # need to pad + if len(ram_segments) > 0 and pad_len > self.SEG_HEADER_LEN: + pad_segment = ram_segments[0].split_image(pad_len) + if len(ram_segments[0].data) == 0: + ram_segments.pop(0) + else: + pad_segment = ImageSegment(0, b"\x00" * pad_len, f.tell()) + checksum = self.save_segment(f, pad_segment, checksum) + total_segments += 1 + else: + # write the flash segment + assert ( + f.tell() + 8 + ) % self.IROM_ALIGN == segment.addr % self.IROM_ALIGN + checksum = self.save_flash_segment(f, segment, checksum) + flash_segments.pop(0) + total_segments += 1 + + # flash segments all written, so write any remaining RAM segments + for segment in ram_segments: + checksum = self.save_segment(f, segment, checksum) + 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. + if not self.append_digest: + raise FatalError( + "secure_pad only applies if a SHA-256 digest " + "is also appended to the image" + ) + align_past = (f.tell() + self.SEG_HEADER_LEN) % self.IROM_ALIGN + # 16 byte aligned checksum + # (force the alignment to simplify calculations) + checksum_space = 16 + if self.secure_pad == "1": + # after checksum: SHA-256 digest + + # (to be added by signing process) version, + # signature + 12 trailing bytes due to alignment + space_after_checksum = 32 + 4 + 64 + 12 + elif self.secure_pad == "2": # Secure Boot V2 + # after checksum: SHA-256 digest + + # signature sector, + # but we place signature sector after the 64KB boundary + space_after_checksum = 32 + pad_len = ( + self.IROM_ALIGN - align_past - checksum_space - space_after_checksum + ) % self.IROM_ALIGN + pad_segment = ImageSegment(0, b"\x00" * pad_len, f.tell()) + + checksum = self.save_segment(f, pad_segment, checksum) + total_segments += 1 + + # done writing segments + self.append_checksum(f, checksum) + image_length = f.tell() + + if self.secure_pad: + assert ((image_length + space_after_checksum) % self.IROM_ALIGN) == 0 + + # kinda hacky: go back to the initial header and write the new segment count + # that includes padding segments. This header is not checksummed + f.seek(1) + f.write(bytes([total_segments])) + + if self.append_digest: + # calculate the SHA256 of the whole file and append it + f.seek(0) + digest = hashlib.sha256() + digest.update(f.read(image_length)) + f.write(digest.digest()) + + if self.pad_to_size: + image_length = f.tell() + if image_length % self.pad_to_size != 0: + 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()) + + def save_flash_segment(self, f, segment, checksum=None): + """ + Save the next segment to the image file, return next checksum value if provided + """ + segment_end_pos = f.tell() + len(segment.data) + self.SEG_HEADER_LEN + segment_len_remainder = segment_end_pos % self.IROM_ALIGN + if segment_len_remainder < 0x24: + # Work around a bug in ESP-IDF 2nd stage bootloader, that it didn't map the + # last MMU page, if an IROM/DROM segment was < 0x24 bytes + # over the page boundary. + segment.data += b"\x00" * (0x24 - segment_len_remainder) + return self.save_segment(f, segment, checksum) + + def load_extended_header(self, load_file): + def split_byte(n): + return (n & 0x0F, (n >> 4) & 0x0F) + + fields = list( + struct.unpack(self.EXTENDED_HEADER_STRUCT_FMT, load_file.read(16)) + ) + + self.wp_pin = fields[0] + + # SPI pin drive stengths are two per byte + self.clk_drv, self.q_drv = split_byte(fields[1]) + self.d_drv, self.cs_drv = split_byte(fields[2]) + self.hd_drv, self.wp_drv = split_byte(fields[3]) + + 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) + ) + + self.min_rev = fields[5] + self.min_rev_full = fields[6] + self.max_rev_full = fields[7] + + # reserved fields in the middle should all be zero + if any(f for f in fields[8:-1] if f != 0): + print( + "Warning: some reserved header fields have non-zero values. " + "This image may be from a newer esptool.py?" + ) + + append_digest = fields[-1] # last byte is append_digest + if append_digest in [0, 1]: + self.append_digest = append_digest == 1 + else: + raise RuntimeError( + "Invalid value for append_digest field (0x%02x). Should be 0 or 1.", + append_digest, + ) + + def save_extended_header(self, save_file): + def join_byte(ln, hn): + return (ln & 0x0F) + ((hn & 0x0F) << 4) + + append_digest = 1 if self.append_digest else 0 + + fields = [ + self.wp_pin, + join_byte(self.clk_drv, self.q_drv), + join_byte(self.d_drv, self.cs_drv), + join_byte(self.hd_drv, self.wp_drv), + self.ROM_LOADER.IMAGE_CHIP_ID, + self.min_rev, + self.min_rev_full, + self.max_rev_full, + ] + fields += [0] * 4 # padding + fields += [append_digest] + + packed = struct.pack(self.EXTENDED_HEADER_STRUCT_FMT, *fields) + save_file.write(packed) + + +class ESP8266V3FirmwareImage(ESP32FirmwareImage): + """ESP8266 V3 firmware image is very similar to ESP32 image""" + + EXTENDED_HEADER_STRUCT_FMT = "B" * 16 + + def is_flash_addr(self, addr): + return addr > ESP8266ROM.IROM_MAP_START + + def save(self, filename): + total_segments = 0 + with io.BytesIO() as f: # write file to memory first + self.write_common_header(f, self.segments) + + checksum = ESPLoader.ESP_CHECKSUM_MAGIC + + # split segments into flash-mapped vs ram-loaded, + # and take copies so we can mutate them + flash_segments = [ + copy.deepcopy(s) + for s in sorted(self.segments, key=lambda s: s.addr) + if self.is_flash_addr(s.addr) and len(s.data) + ] + ram_segments = [ + copy.deepcopy(s) + for s in sorted(self.segments, key=lambda s: s.addr) + if not self.is_flash_addr(s.addr) and len(s.data) + ] + + # check for multiple ELF sections that are mapped in the same + # flash mapping region. This is usually a sign of a broken linker script, + # but if you have a legitimate use case then let us know + if len(flash_segments) > 0: + last_addr = flash_segments[0].addr + 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. " + "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 + # by padding with parts of the non-flash segments... + while len(flash_segments) > 0: + segment = flash_segments[0] + # remove 8 bytes empty data for insert segment header + if segment.name == ".flash.rodata": + segment.data = segment.data[8:] + # write the flash segment + checksum = self.save_segment(f, segment, checksum) + flash_segments.pop(0) + total_segments += 1 + + # flash segments all written, so write any remaining RAM segments + for segment in ram_segments: + checksum = self.save_segment(f, segment, checksum) + total_segments += 1 + + # done writing segments + self.append_checksum(f, checksum) + image_length = f.tell() + + # kinda hacky: go back to the initial header and write the new segment count + # that includes padding segments. This header is not checksummed + f.seek(1) + f.write(bytes([total_segments])) + + if self.append_digest: + # calculate the SHA256 of the whole file and append it + f.seek(0) + digest = hashlib.sha256() + digest.update(f.read(image_length)) + f.write(digest.digest()) + + with open(filename, "wb") as real_file: + real_file.write(f.getvalue()) + + def load_extended_header(self, load_file): + def split_byte(n): + return (n & 0x0F, (n >> 4) & 0x0F) + + fields = list( + struct.unpack(self.EXTENDED_HEADER_STRUCT_FMT, load_file.read(16)) + ) + + self.wp_pin = fields[0] + + # SPI pin drive stengths are two per byte + self.clk_drv, self.q_drv = split_byte(fields[1]) + self.d_drv, self.cs_drv = split_byte(fields[2]) + self.hd_drv, self.wp_drv = split_byte(fields[3]) + + if fields[15] in [0, 1]: + self.append_digest = fields[15] == 1 + else: + raise RuntimeError( + "Invalid value for append_digest field (0x%02x). Should be 0 or 1.", + fields[15], + ) + + # 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?" + ) + + +ESP32ROM.BOOTLOADER_IMAGE = ESP32FirmwareImage + + +class ESP32S2FirmwareImage(ESP32FirmwareImage): + """ESP32S2 Firmware Image almost exactly the same as ESP32FirmwareImage""" + + ROM_LOADER = ESP32S2ROM + + +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""" + + ROM_LOADER = ESP32S3ROM + + +ESP32S3ROM.BOOTLOADER_IMAGE = ESP32S3FirmwareImage + + +class ESP32C3FirmwareImage(ESP32FirmwareImage): + """ESP32C3 Firmware Image almost exactly the same as ESP32FirmwareImage""" + + ROM_LOADER = ESP32C3ROM + + +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 + + +ESP32C2ROM.BOOTLOADER_IMAGE = ESP32C2FirmwareImage + + +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 + + +ESP32C6ROM.BOOTLOADER_IMAGE = ESP32C6FirmwareImage + + +class ESP32H2FirmwareImage(ESP32C6FirmwareImage): + """ESP32H2 Firmware Image almost exactly the same as ESP32FirmwareImage""" + + ROM_LOADER = ESP32H2ROM + + +ESP32H2ROM.BOOTLOADER_IMAGE = ESP32H2FirmwareImage + + +class ELFFile(object): + SEC_TYPE_PROGBITS = 0x01 + SEC_TYPE_STRTAB = 0x03 + SEC_TYPE_INITARRAY = 0x0E + SEC_TYPE_FINIARRAY = 0x0F + + PROG_SEC_TYPES = (SEC_TYPE_PROGBITS, SEC_TYPE_INITARRAY, SEC_TYPE_FINIARRAY) + + LEN_SEC_HEADER = 0x28 + + 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 get_section(self, section_name): + for s in self.sections: + if s.name == section_name: + return s + raise ValueError("No section %s in ELF file" % section_name) + + def _read_elf_file(self, f): + # read the ELF file header + LEN_FILE_HEADER = 0x34 + try: + ( + ident, + _type, + machine, + _version, + self.entrypoint, + _phoff, + shoff, + _flags, + _ehsize, + _phentsize, + _phnum, + shentsize, + 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) + ) + + if byte(ident, 0) != 0x7F or ident[1:4] != b"ELF": + raise FatalError("%s has invalid ELF magic header" % self.name) + 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) + ) + 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) + ) + if shnum == 0: + raise FatalError("%s has 0 section headers" % (self.name)) + self._read_sections(f, shoff, shnum, shstrndx) + self._read_segments(f, _phoff, _phnum, shstrndx) + + def _read_sections(self, f, section_header_offs, section_header_count, shstrndx): + f.seek(section_header_offs) + len_bytes = section_header_count * self.LEN_SEC_HEADER + 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 + ) + 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) + ) + + # walk through the section header and extract all sections + section_header_offsets = range(0, len(section_header), self.LEN_SEC_HEADER) + + def read_section_header(offs): + name_offs, sec_type, _flags, lma, sec_offs, size = struct.unpack_from( + " 0 + ] + self.sections = prog_sections + + def _read_segments(self, f, segment_header_offs, segment_header_count, shstrndx): + f.seek(segment_header_offs) + len_bytes = segment_header_count * self.LEN_SEG_HEADER + segment_header = f.read(len_bytes) + if len(segment_header) == 0: + raise FatalError( + "No segment header found at offset %04x in ELF file." + % segment_header_offs + ) + if len(segment_header) != (len_bytes): + raise FatalError( + "Only read 0x%x bytes from segment header (expected 0x%x.) " + "Truncated ELF file?" % (len(segment_header), len_bytes) + ) + + # walk through the segment header and extract all segments + segment_header_offsets = range(0, len(segment_header), self.LEN_SEG_HEADER) + + def read_segment_header(offs): + ( + seg_type, + seg_offs, + _vaddr, + lma, + size, + _memsize, + _flags, + _align, + ) = struct.unpack_from(" 0 + ] + self.segments = prog_segments + + 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()) + return sha256.digest() diff --git a/installer/bin/esptool/esptool/cmds.py b/installer/bin/esptool/esptool/cmds.py new file mode 100644 index 0000000..a299058 --- /dev/null +++ b/installer/bin/esptool/esptool/cmds.py @@ -0,0 +1,1198 @@ +# 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 hashlib +import io +import os +import struct +import sys +import time +import zlib + +from .bin_image import ELFFile, ImageSegment, LoadFirmwareImage +from .bin_image import ( + ESP8266ROMFirmwareImage, + ESP8266V2FirmwareImage, + ESP8266V3FirmwareImage, +) +from .loader import ( + DEFAULT_CONNECT_ATTEMPTS, + DEFAULT_TIMEOUT, + ERASE_WRITE_TIMEOUT_PER_MB, + ESPLoader, + timeout_per_mb, +) +from .targets import CHIP_DEFS, CHIP_LIST, ROM_LIST +from .util import ( + FatalError, + NotImplementedInROMError, + NotSupportedError, + UnsupportedCommandError, +) +from .util import ( + div_roundup, + flash_size_bytes, + hexify, + pad_to, + print_overwrite, +) + +DETECTED_FLASH_SIZES = { + 0x12: "256KB", + 0x13: "512KB", + 0x14: "1MB", + 0x15: "2MB", + 0x16: "4MB", + 0x17: "8MB", + 0x18: "16MB", + 0x19: "32MB", + 0x1A: "64MB", + 0x1B: "128MB", + 0x1C: "256MB", + 0x20: "64MB", + 0x21: "128MB", + 0x22: "256MB", + 0x32: "256KB", + 0x33: "512KB", + 0x34: "1MB", + 0x35: "2MB", + 0x36: "4MB", + 0x37: "8MB", + 0x38: "16MB", + 0x39: "32MB", + 0x3A: "64MB", +} + +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. + """ + 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) + try: + print("Detecting chip type...", end="") + chip_id = detect_port.get_chip_id() + for cls in [ + n for n in ROM_LIST if n.CHIP_NAME not in ("ESP8266", "ESP32", "ESP32S2") + ]: + # cmd not supported on ESP8266 and ESP32 + ESP32-S2 doesn't return chip_id + 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 + inst._post_connect() + break + else: + err_msg = f"Unexpected chip ID value {chip_id}." + except (UnsupportedCommandError, struct.error, FatalError) as e: + # UnsupportedCommmanddError: ESP8266/ESP32 ROM + # struct.error: ESP32-S2 + # FatalError: ESP8266/ESP32 STUB + print(" Unsupported detection protocol, switching and trying again...") + 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." + ) + 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 + 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. + + +def load_ram(esp, args): + image = LoadFirmwareImage(esp.CHIP_NAME, args.filename) + + print("RAM boot...") + for seg in image.segments: + size = len(seg.data) + print("Downloading %d bytes at %08x..." % (size, seg.addr), end=" ") + sys.stdout.flush() + 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) + esp.mem_finish(image.entrypoint) + + +def read_mem(esp, args): + print("0x%08x = 0x%08x" % (args.address, esp.read_reg(args.address))) + + +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 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 + args.flash_size = DETECTED_FLASH_SIZES.get(size_id) + if args.flash_size is None: + print( + "Warning: Could not auto-detect Flash size (FlashID=0x%x, SizeID=0x%x)," + " defaulting to 4MB" % (flash_id, size_id) + ) + args.flash_size = "4MB" + else: + print("Auto-detected Flash size:", args.flash_size) + + +def _update_image_flash_params(esp, address, args, image): + """ + Modify the flash mode & size bytes if this looks like an executable bootloader image + """ + 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: + return image # all settings are 'keep', not modifying anything + + # 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 + ) + return image + + # make sure this really is an image, and not just data that + # starts with esp.ESP_IMAGE_MAGIC (mostly a problem for encrypted + # images that happen to start with a magic byte + try: + 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) + ) + 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 should not modify the header because + # the checksum check would fail. + sha_implies_keep = args.chip != "esp8266" and image[8 + 15] == 1 + + def print_keep_warning(arg_to_keep, arg_used): + print( + "Warning: Image file at {addr} is protected with a hash checksum, " + "so not changing the flash {arg} setting. " + "Use the --flash_{arg}=keep option instead of --flash_{arg}={arg_orig} " + "in order to remove this warning, or use the --dont-append-digest option " + "for the elf2image command in order to generate an image file " + "without a hash checksum".format( + addr=hex(address), arg=arg_to_keep, arg_orig=arg_used + ) + ) + + if args.flash_mode != "keep": + new_flash_mode = FLASH_MODES[args.flash_mode] + if flash_mode != new_flash_mode and sha_implies_keep: + print_keep_warning("mode", args.flash_mode) + else: + flash_mode = new_flash_mode + + flash_freq = flash_size_freq & 0x0F + if args.flash_freq != "keep": + new_flash_freq = esp.parse_flash_freq_arg(args.flash_freq) + if flash_freq != new_flash_freq and sha_implies_keep: + print_keep_warning("frequency", args.flash_freq) + else: + flash_freq = new_flash_freq + + flash_size = flash_size_freq & 0xF0 + if args.flash_size != "keep": + new_flash_size = esp.parse_flash_size_arg(args.flash_size) + if flash_size != new_flash_size and sha_implies_keep: + print_keep_warning("size", args.flash_size) + else: + flash_size = new_flash_size + + flash_params = struct.pack(b"BB", flash_mode, flash_size + flash_freq) + if flash_params != image[2:4]: + print("Flash params set to 0x%04x" % struct.unpack(">H", flash_params)) + image = image[0:2] + flash_params + image[4:] + 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: + # Check if secure boot is active + if esp.get_secure_boot_enabled(): + for address, _ in args.addr_filename: + if address < 0x8000: + raise FatalError( + "Secure Boot detected, writing to flash regions < 0x8000 " + "is disabled to protect the bootloader. " + "Use --force 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: + try: + image = LoadFirmwareImage(esp.CHIP_NAME, argfile) + except (FatalError, struct.error, RuntimeError): + continue + finally: + argfile.seek(0) # LoadFirmwareImage changes the file handle position + if image.chip_id != esp.IMAGE_CHIP_ID: + raise FatalError( + f"{argfile.name} is not an {esp.CHIP_NAME} image. " + "Use --force to flash anyway." + ) + + # this logic below decides which min_rev to use, min_rev or min/max_rev_full + if image.max_rev_full == 0: # image does not have max/min_rev_full fields + use_rev_full_fields = False + elif image.max_rev_full == 65535: # image has default value of max_rev_full + use_rev_full_fields = True + if ( + image.min_rev_full == 0 and image.min_rev != 0 + ): # min_rev_full is not set, min_rev is used + use_rev_full_fields = False + else: # max_rev_full set to a version + use_rev_full_fields = True + + 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"[v{image.min_rev_full // 100}.{image.min_rev_full % 100} - " + ) + if image.max_rev_full == 65535: + error_str += "max rev not set] " + else: + error_str += ( + 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.") + else: + # In IDF, image.min_rev is set based on Kconfig option. + # For C3 chip, image.min_rev is the Minor revision + # while for the rest chips it is the Major revision. + if esp.CHIP_NAME == "ESP32-C3": + rev = esp.get_minor_chip_version() + else: + rev = esp.get_major_chip_version() + if rev < image.min_rev: + raise FatalError( + f"{argfile.name} requires chip revision " + f"{image.min_rev} or higher (this chip is revision {rev}). " + "Use --force 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: + do_write = True + + if not esp.secure_download_mode: + if esp.get_encrypted_download_disabled(): + raise FatalError( + "This chip has encrypt functionality " + "in UART download mode disabled. " + "This is the Flash Encryption configuration for Production mode " + "instead of Development mode." + ) + + 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)) + do_write = False + + enc_key_valid = esp.is_flash_encryption_key_valid() + + if not enc_key_valid: + 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 + + 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 not do_write and not args.ignore_flash_encryption_efuse_setting: + raise FatalError( + "Can't perform encrypted flash write, " + "consult Flash Encryption documentation for more information" + ) + else: + if not args.force and esp.CHIP_NAME != "ESP8266": + # ESP32 does not support `get_security_info()` and `secure_download_mode` + if ( + esp.CHIP_NAME != "ESP32" + and esp.secure_download_mode + and bin(esp.get_security_info()["flash_crypt_cnt"]).count("1") & 1 != 0 + ): + raise FatalError( + "WARNING: Detected flash encryption and " + "secure download mode enabled.\n" + "Flashing plaintext binary may brick your device! " + "Use --force to override the warning." + ) + + if ( + not esp.secure_download_mode + and esp.get_encrypted_download_disabled() + and esp.get_flash_encryption_enabled() + ): + raise FatalError( + "WARNING: Detected flash encryption enabled and " + "download manual encrypt disabled.\n" + "Flashing plaintext binary may brick your device! " + "Use --force to override the warning." + ) + + # verify file sizes fit in flash + if args.flash_size != "keep": # TODO: check this even with 'keep' + flash_end = flash_size_bytes(args.flash_size) + for address, argfile in args.addr_filename: + argfile.seek(0, os.SEEK_END) + if address + argfile.tell() > flash_end: + raise FatalError( + "File %s (length %d) at offset %d " + "will not fit in %d bytes of flash. " + "Use --flash_size argument, or change flashing address." + % (argfile.name, argfile.tell(), address, flash_end) + ) + argfile.seek(0) + + if args.erase_all: + erase_flash(esp, args) + else: + for address, argfile in args.addr_filename: + argfile.seek(0, os.SEEK_END) + write_end = address + argfile.tell() + argfile.seek(0) + 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 + ) + ) + # Print the address range of to-be-erased flash memory region + print( + "Flash will be erased from {:#010x} to {:#010x}...".format( + address - bytes_over, + div_roundup(write_end, esp.FLASH_SECTOR_SIZE) + * esp.FLASH_SECTOR_SIZE + - 1, + ) + ) + + """ 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")], + all_files will be [ + (0x1000, "partition.bin", args.encrypt), + (0x8000, "bootloader", args.encrypt) + ], + where, of course, args.encrypt is either True or False + """ + all_files = [ + (offs, filename, args.encrypt) for (offs, filename) in args.addr_filename + ] + + """ + Now do the same with encrypt_files list, if defined. + In this case, the flag is True + """ + if args.encrypt_files is not None: + encrypted_files_flag = [ + (offs, filename, True) for (offs, filename) in args.encrypt_files + ] + + # Concatenate both lists and sort them. + # As both list are already sorted, we could simply do a merge instead, + # but for the sake of simplicity and because the lists are very small, + # 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 + + # 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) + compress = False + + if args.no_stub: + print("Erasing flash...") + image = pad_to( + argfile.read(), esp.FLASH_ENCRYPTED_WRITE_ALIGN if encrypted else 4 + ) + if len(image) == 0: + print("WARNING: File %s is empty" % argfile.name) + continue + image = _update_image_flash_params(esp, address, args, image) + calcmd5 = hashlib.md5(image).hexdigest() + uncsize = len(image) + if compress: + uncimage = image + image = zlib.compress(uncimage, 9) + # 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) + else: + blocks = esp.flash_begin(uncsize, address, begin_rom_encrypted=encrypted) + argfile.seek(0) # in case we need it again + 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() + block = image[0 : esp.FLASH_WRITE_SIZE] + if compress: + # feeding each compressed block into the decompressor lets us + # see block-by-block how much will be written + block_uncompressed = len(decompress.decompress(block)) + bytes_written += block_uncompressed + block_timeout = max( + DEFAULT_TIMEOUT, + timeout_per_mb(ERASE_WRITE_TIMEOUT_PER_MB, block_uncompressed), + ) + if not esp.IS_STUB: + timeout = ( + block_timeout # ROM code writes block to flash before ACKing + ) + esp.flash_defl_block(block, seq, timeout=timeout) + if esp.IS_STUB: + # Stub ACKs when block is received, + # then writes to flash while receiving the block after it + timeout = block_timeout + else: + # Pad the last block + block = block + b"\xff" * (esp.FLASH_WRITE_SIZE - len(block)) + if encrypted: + esp.flash_encrypt_block(block, seq) + else: + esp.flash_block(block, seq) + bytes_written += len(block) + bytes_sent += len(block) + image = image[esp.FLASH_WRITE_SIZE :] + seq += 1 + + if esp.IS_STUB: + # Stub only writes each block to flash after 'ack'ing the receive, + # so do a final dummy operation which will not be 'ack'ed + # until the last block has actually been written out to flash + esp.read_reg(ESPLoader.CHIP_DETECT_MAGIC_REG_ADDR, timeout=timeout) + + t = time.time() - t + speed_msg = "" + 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, + ) + 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, + ) + + 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()) + ) + raise FatalError("MD5 of file does not match data in flash!") + else: + print("Hash of data verified.") + except NotImplementedInROMError: + pass + + print("\nLeaving...") + + if esp.IS_STUB: + # skip sending flash_finish to ROM loader here, + # as it causes the loader to exit and run user code + 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] + + # Check whether the last file flashed was compressed or not + if args.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("WP pin: {:#02x}".format(image.wp_pin)) + 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, + ) + ) + print("Chip ID: {}".format(image.chip_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 + for idx, seg in enumerate(image.segments, start=1): + 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] + 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 = " 1 else "")) + + image.verify() + + if args.output is None: + args.output = image.default_output_name(args.input) + image.save(args.output) + + print("Successfully created {} image.".format(args.chip)) + + +def read_mac(esp, args): + mac = esp.read_mac() + + def print_mac(label, mac): + print("%s: %s" % (label, ":".join(map(lambda x: "%02x" % x, mac)))) + + print_mac("MAC", mac) + + +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, args): + if not args.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, " + "please use with caution, otherwise it may brick your device!" + ) + print("Erasing flash (this may take a while)...") + t = time.time() + esp.erase_flash() + print("Chip erase completed successfully in %.1fs" % (time.time() - t)) + + +def erase_region(esp, args): + if not args.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, " + "please use with caution, otherwise it may brick your device!" + ) + print("Erasing region (may be slow depending on size)...") + t = time.time() + esp.erase_region(args.address, args.size) + print("Erase completed successfully in %.1f seconds." % (time.time() - t)) + + +def run(esp, args): + esp.run() + + +def flash_id(esp, args): + 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")) + ) + 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}") + + +def read_flash(esp, args): + if args.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() + + t = time.time() + data = esp.read_flash(args.address, args.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, + ) + with open(args.filename, "wb") as f: + f.write(data) + + +def verify_flash(esp, args): + differences = False + + for address, argfile in args.addr_filename: + image = pad_to(argfile.read(), 4) + argfile.seek(0) # rewind in case we need it again + + image = _update_image_flash_params(esp, address, args, image) + + image_size = len(image) + print( + "Verifying 0x%x (%d) bytes @ 0x%08x in flash against %s..." + % (image_size, image_size, address, argfile.name) + ) + # 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)") + continue + else: + differences = True + if getattr(args, "diff", "no") != "yes": + print("-- verify 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]) + ) + for d in diff: + 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)) + + +def get_security_info(esp, args): + si = esp.get_security_info() + # TODO: better display + print("Flags: {:#010x} ({})".format(si["flags"], bin(si["flags"]))) + print("Flash_Crypt_Cnt: {:#x}".format(si["flash_crypt_cnt"])) + print("Key_Purposes: {}".format(si["key_purposes"])) + 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"])) + + +def merge_bin(args): + try: + chip_class = CHIP_DEFS[args.chip] + except KeyError: + msg = ( + "Please specify the chip argument" + if args.chip == "auto" + else "Invalid chip choice: '{}'".format(args.chip) + ) + msg = msg + " (choose from {})".format(", ".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: + raise FatalError( + "Output file target offset is 0x%x. Input file offset 0x%x is before this." + % (args.target_offset, first_addr) + ) + + if args.format != "raw": + raise FatalError( + "This version of esptool only supports the 'raw' output format" + ) + + with open(args.output, "wb") as of: + + 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())) + + for addr, argfile in input_files: + pad_to(addr) + image = argfile.read() + image = _update_image_flash_params(chip_class, addr, args, image) + of.write(image) + if args.fill_flash_size: + pad_to(flash_size_bytes(args.fill_flash_size)) + print( + "Wrote 0x%x bytes to file %s, ready to flash to offset 0x%x" + % (of.tell(), args.output, args.target_offset) + ) + + +def version(args): + from . import __version__ + + print(__version__) diff --git a/installer/bin/esptool/esptool/config.py b/installer/bin/esptool/esptool/config.py new file mode 100644 index 0000000..5566bec --- /dev/null +++ b/installer/bin/esptool/esptool/config.py @@ -0,0 +1,92 @@ +# SPDX-FileCopyrightText: 2014-2023 Espressif Systems (Shanghai) CO LTD, +# other contributors as noted. +# +# SPDX-License-Identifier: GPL-2.0-or-later + +import configparser +import os + +CONFIG_OPTIONS = [ + "timeout", + "chip_erase_timeout", + "max_timeout", + "sync_timeout", + "md5_timeout_per_mb", + "erase_region_timeout_per_mb", + "erase_write_timeout_per_mb", + "mem_end_rom_timeout", + "serial_write_timeout", + "connect_attempts", + "write_block_attempts", + "reset_delay", + "custom_reset_sequence", +] + + +def _validate_config_file(file_path, verbose=False): + if not os.path.exists(file_path): + return False + + cfg = configparser.RawConfigParser() + try: + cfg.read(file_path, encoding="UTF-8") + # Only consider it a valid config file if it contains [esptool] section + if cfg.has_section("esptool"): + if verbose: + unknown_opts = list(set(cfg.options("esptool")) - set(CONFIG_OPTIONS)) + unknown_opts.sort() + no_of_unknown_opts = len(unknown_opts) + if no_of_unknown_opts > 0: + suffix = "s" if no_of_unknown_opts > 1 else "" + print( + "Ignoring unknown config file option{}: {}".format( + suffix, ", ".join(unknown_opts) + ) + ) + return True + except (UnicodeDecodeError, configparser.Error) as e: + if verbose: + print(f"Ignoring invalid config file {file_path}: {e}") + return False + + +def _find_config_file(dir_path, verbose=False): + for candidate in ("esptool.cfg", "setup.cfg", "tox.ini"): + cfg_path = os.path.join(dir_path, candidate) + if _validate_config_file(cfg_path, verbose): + return cfg_path + return None + + +def load_config_file(verbose=False): + set_with_env_var = False + env_var_path = os.environ.get("ESPTOOL_CFGFILE") + if env_var_path is not None and _validate_config_file(env_var_path): + cfg_file_path = env_var_path + set_with_env_var = True + else: + home_dir = os.path.expanduser("~") + os_config_dir = ( + f"{home_dir}/.config/esptool" + if os.name == "posix" + else f"{home_dir}/AppData/Local/esptool/" + ) + # Search priority: 1) current dir, 2) OS specific config dir, 3) home dir + for dir_path in (os.getcwd(), os_config_dir, home_dir): + cfg_file_path = _find_config_file(dir_path, verbose) + if cfg_file_path: + break + + cfg = configparser.ConfigParser() + cfg["esptool"] = {} # Create an empty esptool config for when no file is found + + if cfg_file_path is not None: + # If config file is found and validated, read and parse it + cfg.read(cfg_file_path) + if verbose: + msg = " (set with ESPTOOL_CFGFILE)" if set_with_env_var else "" + print( + f"Loaded custom configuration from " + f"{os.path.abspath(cfg_file_path)}{msg}" + ) + return cfg, cfg_file_path diff --git a/installer/bin/esptool/esptool/loader.py b/installer/bin/esptool/esptool/loader.py new file mode 100644 index 0000000..eb64023 --- /dev/null +++ b/installer/bin/esptool/esptool/loader.py @@ -0,0 +1,1608 @@ +# SPDX-FileCopyrightText: 2014-2023 Fredrik Ahlberg, Angus Gratton, +# Espressif Systems (Shanghai) CO LTD, other contributors as noted. +# +# SPDX-License-Identifier: GPL-2.0-or-later + +import base64 +import hashlib +import itertools +import json +import os +import re +import string +import struct +import sys +import time + +from .config import load_config_file +from .reset import ( + ClassicReset, + CustomReset, + DEFAULT_RESET_DELAY, + HardReset, + USBJTAGSerialReset, + UnixTightReset, +) +from .util import FatalError, NotImplementedInROMError, UnsupportedCommandError +from .util import byte, 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) + ) + raise + +# check 'serial' is 'pyserial' and not 'serial' +# ref. https://github.com/espressif/esptool/issues/269 +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 " + "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 " + "that depends on 'serial'.\n" + "There is no good fix for this right now, " + "apart from configuring virtualenvs. " + "See https://github.com/espressif/esptool/issues/269#issuecomment-385298196" + " for discussion of the underlying issue(s)." + ) +except TypeError: + 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." + % (sys.VERSION, sys.executable) + ) + raise +except Exception: + if sys.platform == "darwin": + # 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: + raise + + +cfg, _ = load_config_file() +cfg = cfg["esptool"] + +# Timeout for most flash operations +DEFAULT_TIMEOUT = cfg.getfloat("timeout", 3) +# Timeout for full chip erase +CHIP_ERASE_TIMEOUT = cfg.getfloat("chip_erase_timeout", 120) +# Longest any command can run +MAX_TIMEOUT = cfg.getfloat("max_timeout", CHIP_ERASE_TIMEOUT * 2) +# Timeout for syncing with bootloader +SYNC_TIMEOUT = cfg.getfloat("sync_timeout", 0.1) +# Timeout (per megabyte) for calculating md5sum +MD5_TIMEOUT_PER_MB = cfg.getfloat("md5_timeout_per_mb", 8) +# Timeout (per megabyte) for erasing a region +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 +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) +# Default number of times to try connection +DEFAULT_CONNECT_ATTEMPTS = cfg.getint("connect_attempts", 7) +# Number of times to try writing a data block +WRITE_BLOCK_ATTEMPTS = cfg.getint("write_block_attempts", 3) + +STUBS_DIR = os.path.join(os.path.dirname(__file__), "./targets/stub_flasher/") + + +def get_stub_json_path(chip_name): + chip_name = strip_chip_name(chip_name) + chip_name = chip_name.replace("esp", "") + return STUBS_DIR + "stub_flasher_" + chip_name + ".json" + + +def timeout_per_mb(seconds_per_mb, size_bytes): + """Scales timeouts which are size-specific""" + result = seconds_per_mb * (size_bytes / 1e6) + if result < DEFAULT_TIMEOUT: + return DEFAULT_TIMEOUT + return result + + +def check_supported_function(func, check_func): + """ + Decorator implementation that wraps a check around an ESPLoader + bootloader function to check if it's supported. + + This is used to capture the multidimensional differences in + functionality between the ESP8266 & ESP32 (and later chips) ROM loaders, and the + software stub that runs on these. Not possible to do this cleanly + via inheritance alone. + """ + + def inner(*args, **kwargs): + obj = args[0] + if check_func(obj): + return func(*args, **kwargs) + else: + raise NotImplementedInROMError(obj, func) + + return inner + + +def stub_function_only(func): + """Attribute for a function only supported in the software stub loader""" + return check_supported_function(func, lambda o: o.IS_STUB) + + +def stub_and_esp32_function_only(func): + """Attribute for a function only supported by stubs or ESP32 and later chips ROM""" + return check_supported_function( + func, lambda o: o.IS_STUB or o.CHIP_NAME not in ["ESP8266"] + ) + + +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: + def __init__(self, json_path): + with open(json_path) as json_file: + stub = json.load(json_file) + + self.text = base64.b64decode(stub["text"]) + self.text_start = stub["text_start"] + self.entry = stub["entry"] + + try: + self.data = base64.b64decode(stub["data"]) + self.data_start = stub["data_start"] + except KeyError: + self.data = None + self.data_start = None + + +class ESPLoader(object): + """Base class providing access to ESP ROM & software stub bootloaders. + Subclasses provide ESP8266 & ESP32 Family specific functionality. + + Don't instantiate this base class directly, either instantiate a subclass or + call cmds.detect_chip() which will interrogate the chip and return the + appropriate subclass instance. + + """ + + CHIP_NAME = "Espressif device" + IS_STUB = False + + FPGA_SLOW_BOOT = False + + 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 comands 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 + + # Response code(s) sent by ROM + ROM_INVALID_RECV_MSG = 0x05 # response if an invalid message is received + + # Maximum block sized for RAM and Flash writes, respectively. + ESP_RAM_BLOCK = 0x1800 + + FLASH_WRITE_SIZE = 0x400 + + # Default baudrate. The ROM auto-bauds, so we can use more or less whatever we want. + ESP_ROM_BAUD = 115200 + + # First byte of the application image + ESP_IMAGE_MAGIC = 0xE9 + + # Initial state for the checksum routine + ESP_CHECKSUM_MAGIC = 0xEF + + # Flash sector size, minimum unit of erase. + FLASH_SECTOR_SIZE = 0x1000 + + UART_DATE_REG_ADDR = 0x60000078 + + # This ROM address has a different value on each chip model + CHIP_DETECT_MAGIC_REG_ADDR = 0x40001000 + + UART_CLKDIV_MASK = 0xFFFFF + + # Memory addresses + 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 + # instead of the ROM bootloader + sync_stub_detected = False + + # Device PIDs + USB_JTAG_SERIAL_PID = 0x1001 + + # Chip IDs that are no longer supported by esptool + UNSUPPORTED_CHIPS = {6: "ESP32-S3(beta 3)"} + + def __init__(self, port=DEFAULT_PORT, baud=ESP_ROM_BAUD, trace_enabled=False): + """Base constructor for ESPLoader bootloader interaction + + Don't call this constructor, either instantiate a specific + ROM class directly, or use cmds.detect_chip(). + + This base class has all of the instance methods for bootloader + functionality supported across various chips & stub + loaders. Subclasses replace the functions they don't support + with ones which throw NotImplementedInROMError(). + + """ + # True if esptool detects the ROM is in Secure Download Mode + self.secure_download_mode = False + # True if esptool detects conditions which require the stub to be disabled + self.stub_is_disabled = False + + # Device-and-runtime-specific cache + self.cache = { + "flash_id": None, + "chip_id": None, + "uart_no": None, + } + + if isinstance(port, str): + try: + self._port = serial.serial_for_url(port) + except serial.serialutil.SerialException: + raise FatalError(f"Could not open {port}, the port doesn't exist") + else: + self._port = port + self._slip_reader = slip_reader(self._port, self.trace) + # setting baud rate in a separate step is a workaround for + # CH341 driver on some Linux versions (this opens at 9600 then + # sets), shouldn't matter for other platforms/drivers. See + # https://github.com/espressif/esptool/issues/44#issuecomment-107094446 + self._set_port_baudrate(baud) + self._trace_enabled = trace_enabled + # set write timeout, to prevent esptool blocked at write forever. + try: + self._port.write_timeout = DEFAULT_SERIAL_WRITE_TIMEOUT + except NotImplementedError: + # no write timeout for RFC2217 ports + # need to set the property back to None or it will continue to fail + self._port.write_timeout = None + + @property + def serial_port(self): + return self._port.port + + def _set_port_baudrate(self, baud): + try: + self._port.baudrate = baud + except IOError: + raise FatalError( + "Failed to set baud rate %d. The driver may not support this rate." + % baud + ) + + def read(self): + """Read a SLIP packet from the serial port""" + return next(self._slip_reader) + + def write(self, packet): + """Write bytes to the serial port while performing SLIP escaping""" + buf = ( + b"\xc0" + + (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._port.write(buf) + + def trace(self, message, *format_args): + if self._trace_enabled: + now = time.time() + try: + delta = now - self._last_trace + except AttributeError: + delta = 0.0 + self._last_trace = now + prefix = "TRACE +%.3f " % delta + print(prefix + (message % format_args)) + + @staticmethod + def checksum(data, state=ESP_CHECKSUM_MAGIC): + """Calculate checksum of a blob, as it is defined by the ROM""" + for b in data: + state ^= b + + return state + + def command( + self, + op=None, + data=b"", + chk=0, + wait_response=True, + timeout=DEFAULT_TIMEOUT, + ): + """Send a request and read the response""" + saved_timeout = self._port.timeout + new_timeout = min(timeout, MAX_TIMEOUT) + if new_timeout != saved_timeout: + self._port.timeout = new_timeout + + 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), + ) + pkt = struct.pack(b" self.STATUS_BYTES_LENGTH: + return data[: -self.STATUS_BYTES_LENGTH] + 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): + self._port.flushInput() + self._slip_reader = slip_reader(self._port, self.trace) + + def sync(self): + val, _ = self.command( + self.ESP_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. + # If we receive 0 then it probably indicates that the chip wasn't or couldn't be + # reseted properly and esptool is talking to the flasher stub. + self.sync_stub_detected = val == 0 + + for _ in range(7): + val, _ = self.command() + self.sync_stub_detected &= val == 0 + + def _get_pid(self): + if list_ports is None: + print( + "\nListing all serial ports is currently not available. " + "Can't get device PID." + ) + return + active_port = self._port.port + + # Pyserial only identifies regular ports, URL handlers are not supported + if not active_port.lower().startswith(("com", "/dev/")): + print( + "\nDevice PID identification is only supported on " + "COM and /dev/ serial ports." + ) + return + # Return the real path if the active port is a symlink + if active_port.startswith("/dev/") and os.path.islink(active_port): + active_port = os.path.realpath(active_port) + + # The "cu" (call-up) device has to be used for outgoing communication on MacOS + if sys.platform == "darwin" and "tty" in active_port: + active_port = [active_port, active_port.replace("tty", "cu")] + ports = list_ports.comports() + for p in ports: + if p.device in active_port: + return p.pid + print( + "\nFailed to get PID of a device on {}, " + "using standard reset sequence.".format(active_port) + ) + + def _connect_attempt(self, reset_strategy, mode="default_reset"): + """A single connection attempt""" + last_error = None + boot_log_detected = False + download_mode = False + + # 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": + return last_error + + 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() + + reset_strategy() # Reset the chip to bootloader (download mode) + + # Detect the ROM boot log and check actual boot mode (ESP32 and later only) + waiting = self._port.inWaiting() + read_bytes = self._port.read(waiting) + data = re.search( + b"boot:(0x[0-9a-fA-F]+)(.*waiting for download)?", read_bytes, re.DOTALL + ) + if data is not None: + boot_log_detected = True + boot_mode = data.group(1) + download_mode = data.group(2) is not None + + for _ in range(5): + try: + self.flush_input() + self._port.flushOutput() + self.sync() + return None + except FatalError as e: + print(".", end="") + sys.stdout.flush() + time.sleep(0.05) + last_error = e + + if boot_log_detected: + last_error = FatalError( + "Wrong boot mode detected ({})! " + "The chip needs to be in download mode.".format( + boot_mode.decode("utf-8") + ) + ) + if download_mode: + last_error = FatalError( + "Download mode successfully detected, but getting no sync reply: " + "The serial TX path seems to be down." + ) + return last_error + + def get_memory_region(self, name): + """ + Returns a tuple of (start, end) for the memory map entry with the given name, + or None if it doesn't exist + """ + try: + return [(start, end) for (start, end, n) in self.MEMORY_MAP if n == name][0] + except IndexError: + return None + + def _construct_reset_strategy_sequence(self, mode): + """ + Constructs a sequence of reset strategies based on the OS, + used ESP chip, external settings, and environment variables. + Returns a tuple of one or more reset strategies to be tried sequentially. + """ + cfg_custom_reset_sequence = cfg.get("custom_reset_sequence") + if cfg_custom_reset_sequence is not None: + return (CustomReset(self._port, cfg_custom_reset_sequence),) + + cfg_reset_delay = cfg.getfloat("reset_delay") + if cfg_reset_delay is not None: + delay = extra_delay = cfg_reset_delay + else: + delay = DEFAULT_RESET_DELAY + extra_delay = DEFAULT_RESET_DELAY + 0.5 + + # This FPGA delay is for Espressif internal use + if ( + self.FPGA_SLOW_BOOT + and os.environ.get("ESPTOOL_ENV_FPGA", "").strip() == "1" + ): + delay = extra_delay = 7 + + # USB-JTAG/Serial mode + if mode == "usb_reset" or self._get_pid() == self.USB_JTAG_SERIAL_PID: + return (USBJTAGSerialReset(self._port),) + + # USB-to-Serial bridge + if os.name != "nt" and not self._port.name.startswith("rfc2217:"): + return ( + UnixTightReset(self._port, delay), + UnixTightReset(self._port, extra_delay), + ClassicReset(self._port, delay), + ClassicReset(self._port, extra_delay), + ) + + return ( + ClassicReset(self._port, delay), + ClassicReset(self._port, extra_delay), + ) + + def connect( + self, + 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), + "Connection may fail if the chip is not in bootloader " + "or flasher stub mode.", + ) + print("Connecting...", end="") + sys.stdout.flush() + last_error = None + + reset_sequence = self._construct_reset_strategy_sequence(mode) + try: + for _, reset_strategy in zip( + range(attempts) if attempts > 0 else itertools.count(), + itertools.cycle(reset_sequence), + ): + last_error = self._connect_attempt(reset_strategy, mode) + if last_error is None: + break + finally: + print("") # end 'Connecting...' line + + if last_error is not None: + raise FatalError( + "Failed to connect to {}: {}" + "\nFor troubleshooting steps visit: " + "https://docs.espressif.com/projects/esptool/en/latest/troubleshooting.html".format( # noqa E501 + self.CHIP_NAME, last_error + ) + ) + + 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 + + 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() + else: + raise + self._post_connect() + + def _post_connect(self): + """ + Additional initialization hook, may be overridden by the chip-specific class. + Gets called after connect, and after auto-detection. + """ + pass + + 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(" 0: + # add a dummy write to a date register as an excuse to have a delay + command += struct.pack( + " 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." + % (start, end, load_start, load_end) + ) + + return self.check_command( + "enter RAM download mode", + self.ESP_MEM_BEGIN, + struct.pack(" length: + raise FatalError("Read more than expected") + + digest_frame = self.read() + if len(digest_frame) != 16: + raise FatalError("Expected digest, got: %s" % 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) + ) + return data + + def flash_spi_attach(self, hspi_arg): + """Send SPI attach command to enable the SPI flash pins + + 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 + arg = struct.pack(" 0: + self.write_reg(SPI_MOSI_DLEN_REG, mosi_bits - 1) + if miso_bits > 0: + self.write_reg(SPI_MISO_DLEN_REG, miso_bits - 1) + flags = 0 + if dummy_len > 0: + flags |= dummy_len - 1 + if addr_len > 0: + flags |= (addr_len - 1) << SPI_USR_ADDR_LEN_SHIFT + if flags: + self.write_reg(SPI_USR1_REG, flags) + + else: + + def set_data_lengths(mosi_bits, miso_bits): + SPI_DATA_LEN_REG = SPI_USR1_REG + SPI_MOSI_BITLEN_S = 17 + SPI_MISO_BITLEN_S = 8 + mosi_mask = 0 if (mosi_bits == 0) else (mosi_bits - 1) + miso_mask = 0 if (miso_bits == 0) else (miso_bits - 1) + flags = (miso_mask << SPI_MISO_BITLEN_S) | ( + mosi_mask << SPI_MOSI_BITLEN_S + ) + if dummy_len > 0: + flags |= dummy_len - 1 + if addr_len > 0: + flags |= (addr_len - 1) << SPI_USR_ADDR_LEN_SHIFT + self.write_reg(SPI_DATA_LEN_REG, flags) + + # SPI peripheral "command" bitmasks for SPI_CMD_REG + SPI_CMD_USR = 1 << 18 + + # shift values + SPI_USR2_COMMAND_LEN_SHIFT = 28 + SPI_USR_ADDR_LEN_SHIFT = 26 + + if read_bits > 32: + raise FatalError( + "Reading more than 32 bits back from a SPI flash " + "operation is unsupported" + ) + if len(data) > 64: + raise FatalError( + "Writing more than 64 bytes of data with one SPI " + "command is unsupported" + ) + + data_bits = len(data) * 8 + old_spi_usr = self.read_reg(SPI_USR_REG) + old_spi_usr2 = self.read_reg(SPI_USR2_REG) + flags = SPI_USR_COMMAND + if read_bits > 0: + flags |= SPI_USR_MISO + if data_bits > 0: + flags |= SPI_USR_MOSI + if addr_len > 0: + flags |= SPI_USR_ADDR + if dummy_len > 0: + flags |= SPI_USR_DUMMY + set_data_lengths(data_bits, read_bits) + self.write_reg(SPI_USR_REG, flags) + self.write_reg( + SPI_USR2_REG, (7 << SPI_USR2_COMMAND_LEN_SHIFT) | spiflash_command + ) + if addr and addr_len > 0: + self.write_reg(SPI_ADDR_REG, addr) + if data_bits == 0: + self.write_reg(SPI_W0_REG, 0) # clear data register before we read it + else: + data = pad_to(data, 4, b"\00") # pad to 32-bit multiple + words = struct.unpack("I" * (len(data) // 4), data) + next_reg = SPI_W0_REG + for word in words: + self.write_reg(next_reg, word) + next_reg += 4 + self.write_reg(SPI_CMD_REG, SPI_CMD_USR) + + def wait_done(): + for _ in range(10): + if (self.read_reg(SPI_CMD_REG) & SPI_CMD_USR) == 0: + return + raise FatalError("SPI command did not complete in time") + + wait_done() + + status = self.read_reg(SPI_W0_REG) + # restore some SPI controller registers + self.write_reg(SPI_USR_REG, old_spi_usr) + self.write_reg(SPI_USR2_REG, old_spi_usr2) + return status + + def read_spiflash_sfdp(self, addr, read_bits): + CMD_RDSFDP = 0x5A + return self.run_spiflash_command( + CMD_RDSFDP, read_bits=read_bits, addr=addr, addr_len=24, dummy_len=8 + ) + + def read_status(self, num_bytes=2): + """Read up to 24 bits (num_bytes) of SPI flash status register contents + via RDSR, RDSR2, RDSR3 commands + + Not all SPI flash supports all three commands. The upper 1 or 2 + bytes may be 0xFF. + """ + SPIFLASH_RDSR = 0x05 + SPIFLASH_RDSR2 = 0x35 + SPIFLASH_RDSR3 = 0x15 + + status = 0 + shift = 0 + for cmd in [SPIFLASH_RDSR, SPIFLASH_RDSR2, SPIFLASH_RDSR3][0:num_bytes]: + status += self.run_spiflash_command(cmd, read_bits=8) << shift + shift += 8 + return status + + def write_status(self, new_status, num_bytes=2, set_non_volatile=False): + """Write up to 24 bits (num_bytes) of new status register + + num_bytes can be 1, 2 or 3. + + Not all flash supports the additional commands to write the + second and third byte of the status register. When writing 2 + bytes, esptool also sends a 16-byte WRSR command (as some + flash types use this instead of WRSR2.) + + If the set_non_volatile flag is set, non-volatile bits will + be set as well as volatile ones (WREN used instead of WEVSR). + + """ + SPIFLASH_WRSR = 0x01 + SPIFLASH_WRSR2 = 0x31 + SPIFLASH_WRSR3 = 0x11 + SPIFLASH_WEVSR = 0x50 + SPIFLASH_WREN = 0x06 + SPIFLASH_WRDI = 0x04 + + enable_cmd = SPIFLASH_WREN if set_non_volatile else SPIFLASH_WEVSR + + # try using a 16-bit WRSR (not supported by all chips) + # this may be redundant, but shouldn't hurt + if num_bytes == 2: + self.run_spiflash_command(enable_cmd) + self.run_spiflash_command(SPIFLASH_WRSR, struct.pack(">= 8 + + self.run_spiflash_command(SPIFLASH_WRDI) + + def get_crystal_freq(self): + """ + Figure out the crystal frequency from the UART clock divider + + Returns a normalized value in integer MHz (only values 40 or 26 are supported) + """ + # The logic here is: + # - We know that our baud rate and the ESP UART baud rate are roughly the same, + # or we couldn't communicate + # - We can read the UART clock divider register to know how the ESP derives this + # from the APB bus frequency + # - Multiplying these two together gives us the bus frequency which is either + # the crystal frequency (ESP32) or double the crystal frequency (ESP8266). + # See the self.XTAL_CLK_DIVIDER parameter for this factor. + uart_div = self.read_reg(self.UART_CLKDIV_REG) & self.UART_CLKDIV_MASK + est_xtal = (self._port.baudrate * uart_div) / 1e6 / self.XTAL_CLK_DIVIDER + norm_xtal = 40 if est_xtal > 33 else 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) + ) + return norm_xtal + + def hard_reset(self): + print("Hard resetting via RTS pin...") + HardReset(self._port)() + + def soft_reset(self, stay_in_bootloader): + if not self.IS_STUB: + if stay_in_bootloader: + return # ROM bootloader is already in bootloader! + else: + # 'run user code' is as close to a soft reset as we can do + self.flash_begin(0, 0) + self.flash_finish(False) + else: + if stay_in_bootloader: + # soft resetting from the stub loader + # will re-load the ROM bootloader + self.flash_begin(0, 0) + self.flash_finish(True) + elif self.CHIP_NAME != "ESP8266": + raise FatalError( + "Soft resetting is currently only supported on ESP8266" + ) + else: + # running user code from stub loader requires some hacks + # in the stub loader + self.command(self.ESP_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 slip_reader(port, trace_function): + """Generator to read SLIP packets from a serial port. + Yields one full SLIP packet at a time, raises exception on timeout or invalid data. + + Designed to avoid too many calls to serial.read(1), which can bog + down on slow systems. + """ + + def detect_panic_handler(input): + """ + Checks the input bytes for panic handler messages. + Raises a FatalError if Guru Meditation or Fatal Exception is found, as both + of these are used between different ROM versions. + Tries to also parse the error cause (e.g. IllegalInstruction). + """ + + guru_meditation = ( + rb"G?uru Meditation Error: (?:Core \d panic'ed \(([a-zA-Z ]*)\))?" + ) + fatal_exception = rb"F?atal exception \(\d+\): (?:([a-zA-Z ]*)?.*epc)?" + + # Search either for Guru Meditation or Fatal Exception + data = re.search( + rb"".join([rb"(?:", guru_meditation, rb"|", fatal_exception, rb")"]), + input, + re.DOTALL, + ) + if data is not None: + cause = [ + "({})".format(i.decode("utf-8")) + for i in [data.group(1), data.group(2)] + if i is not None + ] + cause = f" {cause[0]}" if len(cause) else "" + msg = f"Guru Meditation Error detected{cause}" + raise FatalError(msg) + + partial_packet = None + in_escape = False + successful_slip = False + while True: + waiting = port.inWaiting() + read_bytes = port.read(1 if waiting == 0 else waiting) + if read_bytes == b"": + if partial_packet is None: # fail due to no data + msg = ( + "Serial data stream stopped: Possible serial noise or corruption." + if successful_slip + else "No serial data received." + ) + else: # fail during packet transfer + msg = "Packet content transfer stopped (received {} bytes)".format( + len(partial_packet) + ) + trace_function(msg) + raise FatalError(msg) + trace_function("Read %d bytes: %s", len(read_bytes), 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)) + remaining_data = port.read(port.inWaiting()) + trace_function( + "Remaining data in serial buffer: %s", + 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) + ) + elif in_escape: # part-way through escape sequence + in_escape = False + if b == b"\xdc": + partial_packet += b"\xc0" + elif b == b"\xdd": + partial_packet += b"\xdb" + else: + trace_function("Read invalid data: %s", HexFormatter(read_bytes)) + remaining_data = port.read(port.inWaiting()) + trace_function( + "Remaining data in serial buffer: %s", + HexFormatter(remaining_data), + ) + detect_panic_handler(read_bytes + remaining_data) + raise FatalError("Invalid SLIP escape (0xdb, 0x%s)" % (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)) + yield partial_packet + partial_packet = None + successful_slip = True + else: # normal byte in packet + partial_packet += b + + +class HexFormatter(object): + """ + Wrapper class which takes binary data in its constructor + and returns a hex string as it's __str__ method. + + This is intended for "lazy formatting" of trace() output + in hex format. Avoids overhead (significant on slow computers) + of generating long hex strings even if tracing is disabled. + + Note that this doesn't save any overhead if passed as an + argument to "%", only when passed to trace() + + If auto_split is set (default), any long line (> 16 bytes) will be + printed as separately indented lines, with ASCII decoding at the end + of each line. + """ + + def __init__(self, binary_string, auto_split=True): + self._s = binary_string + self._auto_split = auto_split + + def __str__(self): + if self._auto_split and len(self._s) > 16: + result = "" + s = self._s + while len(s) > 0: + line = s[:16] + ascii_line = "".join( + c + if ( + c == " " + or (c in string.printable and c not in string.whitespace) + ) + else "." + for c in line.decode("ascii", "replace") + ) + s = s[16:] + result += "\n %-16s %-16s | %s" % ( + hexify(line[:8], False), + hexify(line[8:], False), + ascii_line, + ) + return result + else: + return hexify(self._s, False) diff --git a/installer/bin/esptool/esptool/reset.py b/installer/bin/esptool/esptool/reset.py new file mode 100644 index 0000000..39b9a15 --- /dev/null +++ b/installer/bin/esptool/esptool/reset.py @@ -0,0 +1,178 @@ +# SPDX-FileCopyrightText: 2014-2023 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 +import time + +from .util import FatalError + +# Used for resetting into bootloader on Unix-like systems +if os.name != "nt": + import fcntl + import termios + + # Constants used for terminal status lines reading/setting. + # Taken from pySerial's backend for IO: + # https://github.com/pyserial/pyserial/blob/master/serial/serialposix.py + TIOCMSET = getattr(termios, "TIOCMSET", 0x5418) + TIOCMGET = getattr(termios, "TIOCMGET", 0x5415) + TIOCM_DTR = getattr(termios, "TIOCM_DTR", 0x002) + TIOCM_RTS = getattr(termios, "TIOCM_RTS", 0x004) + +DEFAULT_RESET_DELAY = 0.05 # default time to wait before releasing boot pin after reset + + +class ResetStrategy(object): + def __init__(self, port, reset_delay=DEFAULT_RESET_DELAY): + self.port = port + self.reset_delay = reset_delay + + def __call__(): + pass + + def _setDTR(self, state): + self.port.setDTR(state) + + def _setRTS(self, state): + self.port.setRTS(state) + # Work-around for adapters on Windows using the usbser.sys driver: + # generate a dummy change to DTR so that the set-control-line-state + # request is sent with the updated RTS state and the same DTR state + self.port.setDTR(self.port.dtr) + + def _setDTRandRTS(self, dtr=False, rts=False): + status = struct.unpack( + "I", fcntl.ioctl(self.port.fileno(), TIOCMGET, struct.pack("I", 0)) + )[0] + if dtr: + status |= TIOCM_DTR + else: + status &= ~TIOCM_DTR + if rts: + status |= TIOCM_RTS + else: + status &= ~TIOCM_RTS + fcntl.ioctl(self.port.fileno(), TIOCMSET, struct.pack("I", status)) + + +class ClassicReset(ResetStrategy): + """ + Classic reset sequence, sets DTR and RTS lines sequentially. + """ + + def __call__(self): + self._setDTR(False) # IO0=HIGH + self._setRTS(True) # EN=LOW, chip in reset + time.sleep(0.1) + self._setDTR(True) # IO0=LOW + self._setRTS(False) # EN=HIGH, chip out of reset + time.sleep(self.reset_delay) + self._setDTR(False) # IO0=HIGH, done + + +class UnixTightReset(ResetStrategy): + """ + UNIX-only reset sequence with custom implementation, + which allows setting DTR and RTS lines at the same time. + """ + + def __call__(self): + self._setDTRandRTS(False, False) + self._setDTRandRTS(True, True) + self._setDTRandRTS(False, True) # IO0=HIGH & EN=LOW, chip in reset + time.sleep(0.1) + self._setDTRandRTS(True, False) # IO0=LOW & EN=HIGH, chip out of reset + time.sleep(self.reset_delay) + self._setDTRandRTS(False, False) # IO0=HIGH, done + self._setDTR(False) # Needed in some environments to ensure IO0=HIGH + + +class USBJTAGSerialReset(ResetStrategy): + """ + Custom reset sequence, which is required when the device + is connecting via its USB-JTAG-Serial peripheral. + """ + + def __call__(self): + self._setRTS(False) + self._setDTR(False) # Idle + time.sleep(0.1) + self._setDTR(True) # Set IO0 + self._setRTS(False) + time.sleep(0.1) + self._setRTS(True) # Reset. Calls inverted to go through (1,1) instead of (0,0) + self._setDTR(False) + self._setRTS(True) # RTS set as Windows only propagates DTR on RTS setting + time.sleep(0.1) + self._setDTR(False) + self._setRTS(False) # Chip out of reset + + +class HardReset(ResetStrategy): + """ + Reset sequence for hard resetting the chip. + Can be used to reset out of the bootloader or to restart a running app. + """ + + def __init__(self, port, uses_usb_otg=False): + super().__init__(port) + self.uses_usb_otg = uses_usb_otg + + def __call__(self): + self._setRTS(True) # EN->LOW + if self.uses_usb_otg: + # Give the chip some time to come out of reset, + # to be able to handle further DTR/RTS transitions + time.sleep(0.2) + self._setRTS(False) + time.sleep(0.2) + else: + time.sleep(0.1) + self._setRTS(False) + + +class CustomReset(ResetStrategy): + """ + Custom reset strategy defined with a string. + + CustomReset object is created as "rst = CustomReset(port, seq_str)" + and can be later executed simply with "rst()" + + The seq_str input string consists of individual commands divided by "|". + Commands (e.g. R0) are defined by a code (R) and an argument (0). + + The commands are: + D: setDTR - 1=True / 0=False + R: setRTS - 1=True / 0=False + U: setDTRandRTS (Unix-only) - 0,0 / 0,1 / 1,0 / or 1,1 + W: Wait (time delay) - positive float number + + e.g. + "D0|R1|W0.1|D1|R0|W0.05|D0" represents the ClassicReset strategy + "U1,1|U0,1|W0.1|U1,0|W0.05|U0,0" represents the UnixTightReset strategy + """ + + format_dict = { + "D": "self.port.setDTR({})", + "R": "self.port.setRTS({})", + "W": "time.sleep({})", + "U": "self._setDTRandRTS({})", + } + + def __call__(self): + exec(self.constructed_strategy) + + def __init__(self, port, seq_str): + super().__init__(port) + self.constructed_strategy = self._parse_string_to_seq(seq_str) + + def _parse_string_to_seq(self, seq_str): + try: + 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}') + return "\n".join(fn_calls_list) diff --git a/installer/bin/esptool/esptool/targets/__init__.py b/installer/bin/esptool/esptool/targets/__init__.py new file mode 100644 index 0000000..9d76ca5 --- /dev/null +++ b/installer/bin/esptool/esptool/targets/__init__.py @@ -0,0 +1,31 @@ +from .esp32 import ESP32ROM +from .esp32c2 import ESP32C2ROM +from .esp32c3 import ESP32C3ROM +from .esp32c6 import ESP32C6ROM +from .esp32c6beta import ESP32C6BETAROM +from .esp32h2 import ESP32H2ROM +from .esp32h2beta1 import ESP32H2BETA1ROM +from .esp32h2beta2 import ESP32H2BETA2ROM +from .esp32s2 import ESP32S2ROM +from .esp32s3 import ESP32S3ROM +from .esp32s3beta2 import ESP32S3BETA2ROM +from .esp8266 import ESP8266ROM + + +CHIP_DEFS = { + "esp8266": ESP8266ROM, + "esp32": ESP32ROM, + "esp32s2": ESP32S2ROM, + "esp32s3beta2": ESP32S3BETA2ROM, + "esp32s3": ESP32S3ROM, + "esp32c3": ESP32C3ROM, + "esp32c6beta": ESP32C6BETAROM, + "esp32h2beta1": ESP32H2BETA1ROM, + "esp32h2beta2": ESP32H2BETA2ROM, + "esp32c2": ESP32C2ROM, + "esp32c6": ESP32C6ROM, + "esp32h2": ESP32H2ROM, +} + +CHIP_LIST = list(CHIP_DEFS.keys()) +ROM_LIST = list(CHIP_DEFS.values()) diff --git a/installer/bin/esptool/esptool/targets/esp32.py b/installer/bin/esptool/esptool/targets/esp32.py new file mode 100644 index 0000000..b805ed4 --- /dev/null +++ b/installer/bin/esptool/esptool/targets/esp32.py @@ -0,0 +1,393 @@ +# 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 +import time + +from ..loader import ESPLoader +from ..util import FatalError, NotSupportedError + + +class ESP32ROM(ESPLoader): + """Access class for ESP32 ROM bootloader""" + + CHIP_NAME = "ESP32" + IMAGE_CHIP_ID = 0 + IS_STUB = False + + FPGA_SLOW_BOOT = True + + CHIP_DETECT_MAGIC_VALUE = [0x00F01D83] + + IROM_MAP_START = 0x400D0000 + IROM_MAP_END = 0x40400000 + + 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 + SPI_USR2_OFFS = 0x24 + SPI_MOSI_DLEN_OFFS = 0x28 + SPI_MISO_DLEN_OFFS = 0x2C + EFUSE_RD_REG_BASE = 0x3FF5A000 + + EFUSE_DIS_DOWNLOAD_MANUAL_ENCRYPT_REG = EFUSE_RD_REG_BASE + 0x18 + EFUSE_DIS_DOWNLOAD_MANUAL_ENCRYPT = 1 << 7 # EFUSE_RD_DISABLE_DL_ENCRYPT + + EFUSE_SPI_BOOT_CRYPT_CNT_REG = EFUSE_RD_REG_BASE # EFUSE_BLK0_WDATA0_REG + EFUSE_SPI_BOOT_CRYPT_CNT_MASK = 0x7F << 20 # EFUSE_FLASH_CRYPT_CNT + + EFUSE_RD_ABS_DONE_REG = EFUSE_RD_REG_BASE + 0x018 + EFUSE_RD_ABS_DONE_0_MASK = 1 << 4 + EFUSE_RD_ABS_DONE_1_MASK = 1 << 5 + + DR_REG_SYSCON_BASE = 0x3FF66000 + APB_CTL_DATE_ADDR = DR_REG_SYSCON_BASE + 0x7C + APB_CTL_DATE_V = 0x1 + APB_CTL_DATE_S = 31 + + SPI_W0_OFFS = 0x80 + + UART_CLKDIV_REG = 0x3FF40014 + + XTAL_CLK_DIVIDER = 1 + + RTCCALICFG1 = 0x3FF5F06C + TIMERS_RTC_CALI_VALUE = 0x01FFFFFF + TIMERS_RTC_CALI_VALUE_S = 7 + + FLASH_SIZES = { + "1MB": 0x00, + "2MB": 0x10, + "4MB": 0x20, + "8MB": 0x30, + "16MB": 0x40, + "32MB": 0x50, + "64MB": 0x60, + "128MB": 0x70, + } + + FLASH_FREQUENCY = { + "80m": 0xF, + "40m": 0x0, + "26m": 0x1, + "20m": 0x2, + } + + BOOTLOADER_FLASH_OFFSET = 0x1000 + + OVERRIDE_VDDSDIO_CHOICES = ["1.8V", "1.9V", "OFF"] + + MEMORY_MAP = [ + [0x00000000, 0x00010000, "PADDING"], + [0x3F400000, 0x3F800000, "DROM"], + [0x3F800000, 0x3FC00000, "EXTRAM_DATA"], + [0x3FF80000, 0x3FF82000, "RTC_DRAM"], + [0x3FF90000, 0x40000000, "BYTE_ACCESSIBLE"], + [0x3FFAE000, 0x40000000, "DRAM"], + [0x3FFE0000, 0x3FFFFFFC, "DIRAM_DRAM"], + [0x40000000, 0x40070000, "IROM"], + [0x40070000, 0x40078000, "CACHE_PRO"], + [0x40078000, 0x40080000, "CACHE_APP"], + [0x40080000, 0x400A0000, "IRAM"], + [0x400A0000, 0x400BFFFC, "DIRAM_IRAM"], + [0x400C0000, 0x400C2000, "RTC_IRAM"], + [0x400D0000, 0x40400000, "IROM"], + [0x50000000, 0x50002000, "RTC_DATA"], + ] + + FLASH_ENCRYPTED_WRITE_ALIGN = 32 + + """ Try to read the BLOCK1 (encryption key) and check if it is valid """ + + def is_flash_encryption_key_valid(self): + """Bit 0 of efuse_rd_disable[3:0] is mapped to BLOCK1 + this bit is at position 16 in EFUSE_BLK0_RDATA0_REG""" + word0 = self.read_efuse(0) + rd_disable = (word0 >> 16) & 0x1 + + # reading of BLOCK1 is NOT ALLOWED so we assume valid key is programmed + if rd_disable: + return True + else: + # reading of BLOCK1 is ALLOWED so we will read and verify for non-zero. + # When ESP32 has not generated AES/encryption key in BLOCK1, + # the contents will be readable and 0. + # If the flash encryption is enabled it is expected to have a valid + # non-zero key. We break out on first occurance of non-zero value + key_word = [0] * 7 + for i in range(len(key_word)): + key_word[i] = self.read_efuse(14 + i) + # key is non-zero so break & return + if key_word[i] != 0: + return True + return False + + def get_flash_crypt_config(self): + """For flash encryption related commands we need to make sure + user has programmed all the relevant efuse correctly so before + writing encrypted write_flash_encrypt esptool will verify the values + of flash_crypt_config to be non zero if they are not read + protected. If the values are zero a warning will be printed + + bit 3 in efuse_rd_disable[3:0] is mapped to flash_crypt_config + this bit is at position 19 in EFUSE_BLK0_RDATA0_REG""" + word0 = self.read_efuse(0) + rd_disable = (word0 >> 19) & 0x1 + + if rd_disable == 0: + """we can read the flash_crypt_config efuse value + so go & read it (EFUSE_BLK0_RDATA5_REG[31:28])""" + word5 = self.read_efuse(5) + word5 = (word5 >> 28) & 0xF + return word5 + else: + # if read of the efuse is disabled we assume it is set correctly + return 0xF + + def get_encrypted_download_disabled(self): + return ( + self.read_reg(self.EFUSE_DIS_DOWNLOAD_MANUAL_ENCRYPT_REG) + & self.EFUSE_DIS_DOWNLOAD_MANUAL_ENCRYPT + ) + + def get_flash_encryption_enabled(self): + flash_crypt_cnt = ( + self.read_reg(self.EFUSE_SPI_BOOT_CRYPT_CNT_REG) + & self.EFUSE_SPI_BOOT_CRYPT_CNT_MASK + ) + # Flash encryption enabled when odd number of bits are set + return bin(flash_crypt_cnt).count("1") & 1 != 0 + + def get_secure_boot_enabled(self): + efuses = self.read_reg(self.EFUSE_RD_ABS_DONE_REG) + rev = self.get_chip_revision() + return efuses & self.EFUSE_RD_ABS_DONE_0_MASK or ( + rev >= 300 and efuses & self.EFUSE_RD_ABS_DONE_1_MASK + ) + + def get_pkg_version(self): + word3 = self.read_efuse(3) + pkg_version = (word3 >> 9) & 0x07 + 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 + + def get_major_chip_version(self): + rev_bit0 = (self.read_efuse(3) >> 15) & 0x1 + rev_bit1 = (self.read_efuse(5) >> 20) & 0x1 + apb_ctl_date = self.read_reg(self.APB_CTL_DATE_ADDR) + rev_bit2 = (apb_ctl_date >> self.APB_CTL_DATE_S) & self.APB_CTL_DATE_V + combine_value = (rev_bit2 << 2) | (rev_bit1 << 1) | rev_bit0 + + revision = { + 0: 0, + 1: 1, + 3: 2, + 7: 3, + }.get(combine_value, 0) + return revision + + def get_chip_description(self): + pkg_version = self.get_pkg_version() + major_rev = self.get_major_chip_version() + minor_rev = self.get_minor_chip_version() + rev3 = major_rev == 3 + single_core = self.read_efuse(3) & (1 << 0) # CHIP_VER DIS_APP_CPU + + chip_name = { + 0: "ESP32-S0WDQ6" if single_core else "ESP32-D0WDQ6", + 1: "ESP32-S0WD" if single_core else "ESP32-D0WD", + 2: "ESP32-D2WD", + 4: "ESP32-U4WDH", + 5: "ESP32-PICO-V3" if rev3 else "ESP32-PICO-D4", + 6: "ESP32-PICO-V3-02", + 7: "ESP32-D0WDR2-V3", + }.get(pkg_version, "unknown ESP32") + + # ESP32-D0WD-V3, ESP32-D0WDQ6-V3 + if chip_name.startswith("ESP32-D0WD") and rev3: + chip_name += "-V3" + + return f"{chip_name} (revision v{major_rev}.{minor_rev})" + + def get_chip_features(self): + features = ["WiFi"] + word3 = self.read_efuse(3) + + # names of variables in this section are lowercase + # versions of EFUSE names as documented in TRM and + # ESP-IDF efuse_reg.h + + chip_ver_dis_bt = word3 & (1 << 1) + if chip_ver_dis_bt == 0: + features += ["BT"] + + chip_ver_dis_app_cpu = word3 & (1 << 0) + if chip_ver_dis_app_cpu: + features += ["Single Core"] + else: + features += ["Dual Core"] + + chip_cpu_freq_rated = word3 & (1 << 13) + if chip_cpu_freq_rated: + chip_cpu_freq_low = word3 & (1 << 12) + if chip_cpu_freq_low: + features += ["160MHz"] + else: + features += ["240MHz"] + + pkg_version = self.get_pkg_version() + if pkg_version in [2, 4, 5, 6]: + features += ["Embedded Flash"] + + if pkg_version == 6: + features += ["Embedded PSRAM"] + + word4 = self.read_efuse(4) + adc_vref = (word4 >> 8) & 0x1F + if adc_vref: + features += ["VRef calibration in efuse"] + + blk3_part_res = word3 >> 14 & 0x1 + if blk3_part_res: + features += ["BLK3 partially reserved"] + + word6 = self.read_efuse(6) + coding_scheme = word6 & 0x3 + features += [ + "Coding Scheme %s" + % {0: "None", 1: "3/4", 2: "Repeat (UNSUPPORTED)", 3: "Invalid"}[ + coding_scheme + ] + ] + + return features + + 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, "chip_id") + + def read_mac(self): + """Read MAC from EFUSE region""" + words = [self.read_efuse(2), self.read_efuse(1)] + bitstring = struct.pack(">II", *words) + bitstring = bitstring[2:8] # trim the 2 byte CRC + return tuple(bitstring) + + def get_erase_size(self, offset, size): + return size + + def override_vddsdio(self, new_voltage): + new_voltage = new_voltage.upper() + if new_voltage not in self.OVERRIDE_VDDSDIO_CHOICES: + raise FatalError( + "The only accepted VDDSDIO overrides are '1.8V', '1.9V' and 'OFF'" + ) + RTC_CNTL_SDIO_CONF_REG = 0x3FF48074 + RTC_CNTL_XPD_SDIO_REG = 1 << 31 + RTC_CNTL_DREFH_SDIO_M = 3 << 29 + RTC_CNTL_DREFM_SDIO_M = 3 << 27 + RTC_CNTL_DREFL_SDIO_M = 3 << 25 + # RTC_CNTL_SDIO_TIEH = (1 << 23) + # not used here, setting TIEH=1 would set 3.3V output, + # not safe for esptool.py to do + RTC_CNTL_SDIO_FORCE = 1 << 22 + RTC_CNTL_SDIO_PD_EN = 1 << 21 + + reg_val = RTC_CNTL_SDIO_FORCE # override efuse setting + reg_val |= RTC_CNTL_SDIO_PD_EN + if new_voltage != "OFF": + reg_val |= RTC_CNTL_XPD_SDIO_REG # enable internal LDO + if new_voltage == "1.9V": + reg_val |= ( + RTC_CNTL_DREFH_SDIO_M | RTC_CNTL_DREFM_SDIO_M | RTC_CNTL_DREFL_SDIO_M + ) # boost voltage + self.write_reg(RTC_CNTL_SDIO_CONF_REG, reg_val) + print("VDDSDIO regulator set to %s" % 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) + + data = b"" + while len(data) < length: + block_len = min(BLOCK_LEN, length - len(data)) + r = self.check_command( + "read flash block", + self.ESP_READ_FLASH_SLOW, + struct.pack("> self.TIMERS_RTC_CALI_VALUE_S + ) & self.TIMERS_RTC_CALI_VALUE + clk_8M_freq = self.read_efuse(4) & (0xFF) # EFUSE_RD_CK8M_FREQ + rom_calculated_freq = cali_val * 15625 * clk_8M_freq / 40 + return rom_calculated_freq + + def change_baud(self, baud): + # It's a workaround to avoid esp32 CK_8M frequency drift. + rom_calculated_freq = self.get_rom_cal_crystal_freq() + valid_freq = 40000000 if rom_calculated_freq > 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("> 22) & 0x07 + + def get_chip_description(self): + chip_name = { + 0: "ESP32-C2", + 1: "ESP32-C2", + }.get(self.get_pkg_version(), "unknown ESP32-C2") + 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_minor_chip_version(self): + num_word = 1 + return (self.read_reg(self.EFUSE_BLOCK2_ADDR + (4 * num_word)) >> 16) & 0xF + + def get_major_chip_version(self): + num_word = 1 + return (self.read_reg(self.EFUSE_BLOCK2_ADDR + (4 * num_word)) >> 20) & 0x3 + + def get_crystal_freq(self): + # The crystal detection algorithm of ESP32/ESP8266 works for ESP32-C2 as well. + return ESPLoader.get_crystal_freq(self) + + def change_baud(self, baud): + rom_with_26M_XTAL = not self.IS_STUB and self.get_crystal_freq() == 26 + if rom_with_26M_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 26 MHz XTAL. + false_rom_baud = baud * 40 // 26 + + print(f"Changing baud rate to {baud}") + self.command( + self.ESP_CHANGE_BAUDRATE, struct.pack("> 21) & 0x07 + + def get_minor_chip_version(self): + hi_num_word = 5 + hi = (self.read_reg(self.EFUSE_BLOCK1_ADDR + (4 * hi_num_word)) >> 23) & 0x01 + low_num_word = 3 + low = (self.read_reg(self.EFUSE_BLOCK1_ADDR + (4 * low_num_word)) >> 18) & 0x07 + return (hi << 3) + low + + def get_major_chip_version(self): + num_word = 5 + return (self.read_reg(self.EFUSE_BLOCK1_ADDR + (4 * num_word)) >> 24) & 0x03 + + def get_chip_description(self): + chip_name = { + 0: "ESP32-C3", + }.get(self.get_pkg_version(), "unknown ESP32-C3") + 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", "BLE"] + + def get_crystal_freq(self): + # ESP32C3 XTAL is fixed to 40MHz + return 40 + + def override_vddsdio(self, new_voltage): + raise NotImplementedInROMError( + "VDD_SDIO overrides are not supported for ESP32-C3" + ) + + def read_mac(self): + 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-C3 + + 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) & 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(6)] + + return any(p == self.PURPOSE_VAL_XTS_AES128_KEY for p in purposes) + + def change_baud(self, baud): + ESPLoader.change_baud(self, baud) + + 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_rtc_watchdog(self): + # When USB-JTAG/Serial is used, the RTC watchdog is not reset + # and can then reset the board during flashing. Disable it. + if self.uses_usb_jtag_serial(): + self.write_reg(self.RTC_CNTL_WDTWPROTECT_REG, self.RTC_CNTL_WDT_WKEY) + self.write_reg(self.RTC_CNTL_WDTCONFIG0_REG, 0) + self.write_reg(self.RTC_CNTL_WDTWPROTECT_REG, 0) + + def _post_connect(self): + if not self.sync_stub_detected: # Don't run if stub is reused + self.disable_rtc_watchdog() + + +class ESP32C3StubLoader(ESP32C3ROM): + """Access class for ESP32C3 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 + + +ESP32C3ROM.STUB_CLASS = ESP32C3StubLoader diff --git a/installer/bin/esptool/esptool/targets/esp32c6.py b/installer/bin/esptool/esptool/targets/esp32c6.py new file mode 100644 index 0000000..0c4575e --- /dev/null +++ b/installer/bin/esptool/esptool/targets/esp32c6.py @@ -0,0 +1,189 @@ +# SPDX-FileCopyrightText: 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 .esp32c3 import ESP32C3ROM +from ..util import FatalError, NotImplementedInROMError + + +class ESP32C6ROM(ESP32C3ROM): + CHIP_NAME = "ESP32-C6" + IMAGE_CHIP_ID = 13 + + FPGA_SLOW_BOOT = False + + IROM_MAP_START = 0x42000000 + IROM_MAP_END = 0x42800000 + DROM_MAP_START = 0x42800000 + DROM_MAP_END = 0x43000000 + + BOOTLOADER_FLASH_OFFSET = 0x0 + + # Magic value for ESP32C6 + CHIP_DETECT_MAGIC_VALUE = [0x2CE0806F] + + SPI_REG_BASE = 0x60003000 + 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 = 0x60000000 + 0x7C + + EFUSE_BASE = 0x600B0800 + 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 + + 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 = 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 + 0x0018 # LP_WDT_RWDT_WPROTECT_REG + + FLASH_FREQUENCY = { + "80m": 0x0, # workaround for wrong mspi HS div value in ROM + "40m": 0x0, + "20m": 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"], + ] + + def get_pkg_version(self): + num_word = 3 + return (self.read_reg(self.EFUSE_BLOCK1_ADDR + (4 * num_word)) >> 21) & 0x07 + + def get_minor_chip_version(self): + hi_num_word = 5 + hi = (self.read_reg(self.EFUSE_BLOCK1_ADDR + (4 * hi_num_word)) >> 23) & 0x01 + low_num_word = 3 + low = (self.read_reg(self.EFUSE_BLOCK1_ADDR + (4 * low_num_word)) >> 18) & 0x07 + return (hi << 3) + low + + def get_major_chip_version(self): + num_word = 5 + return (self.read_reg(self.EFUSE_BLOCK1_ADDR + (4 * num_word)) >> 24) & 0x03 + + def get_chip_description(self): + chip_name = { + 0: "ESP32-C6", + }.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 get_chip_features(self): + return ["WiFi 6", "BT 5", "IEEE802.15.4"] + + def get_crystal_freq(self): + # ESP32C6 XTAL is fixed to 40MHz + return 40 + + def override_vddsdio(self, new_voltage): + raise NotImplementedInROMError( + "VDD_SDIO overrides are not supported for ESP32-C6" + ) + + def read_mac(self): + 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-C6 + + 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) & 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(6)] + + return any(p == self.PURPOSE_VAL_XTS_AES128_KEY for p in purposes) + + +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 + + 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 + + +ESP32C6ROM.STUB_CLASS = ESP32C6StubLoader diff --git a/installer/bin/esptool/esptool/targets/esp32c6beta.py b/installer/bin/esptool/esptool/targets/esp32c6beta.py new file mode 100644 index 0000000..4c4b6be --- /dev/null +++ b/installer/bin/esptool/esptool/targets/esp32c6beta.py @@ -0,0 +1,26 @@ +# 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", + }.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/installer/bin/esptool/esptool/targets/esp32h2.py b/installer/bin/esptool/esptool/targets/esp32h2.py new file mode 100644 index 0000000..a37617e --- /dev/null +++ b/installer/bin/esptool/esptool/targets/esp32h2.py @@ -0,0 +1,65 @@ +# SPDX-FileCopyrightText: 2022 Fredrik Ahlberg, Angus Gratton, +# Espressif Systems (Shanghai) CO LTD, other contributors as noted. +# +# SPDX-License-Identifier: GPL-2.0-or-later + +from .esp32c6 import ESP32C6ROM + + +class ESP32H2ROM(ESP32C6ROM): + CHIP_NAME = "ESP32-H2" + IMAGE_CHIP_ID = 16 + + # Magic value for ESP32H2 + CHIP_DETECT_MAGIC_VALUE = [0xD7B73E80] + + FLASH_FREQUENCY = { + "48m": 0xF, + "24m": 0x0, + "16m": 0x1, + "12m": 0x2, + } + + def get_pkg_version(self): + num_word = 3 + block1_addr = self.EFUSE_BASE + 0x044 + word3 = self.read_reg(block1_addr + (4 * num_word)) + pkg_version = (word3 >> 21) & 0x0F + return pkg_version + + 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"] + + def get_crystal_freq(self): + # ESP32H2 XTAL is fixed to 32MHz + return 32 + + +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 + + 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 + + +ESP32H2ROM.STUB_CLASS = ESP32H2StubLoader diff --git a/installer/bin/esptool/esptool/targets/esp32h2beta1.py b/installer/bin/esptool/esptool/targets/esp32h2beta1.py new file mode 100644 index 0000000..66b6df9 --- /dev/null +++ b/installer/bin/esptool/esptool/targets/esp32h2beta1.py @@ -0,0 +1,164 @@ +# 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 .esp32c3 import ESP32C3ROM +from ..util import FatalError, NotImplementedInROMError + + +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 = [] + + FLASH_FREQUENCY = { + "48m": 0xF, + "24m": 0x0, + "16m": 0x1, + "12m": 0x2, + } + + def get_pkg_version(self): + num_word = 3 + return (self.read_reg(self.EFUSE_BLOCK1_ADDR + (4 * num_word)) >> 21) & 0x0F + + def get_minor_chip_version(self): + hi_num_word = 5 + hi = (self.read_reg(self.EFUSE_BLOCK1_ADDR + (4 * hi_num_word)) >> 23) & 0x01 + low_num_word = 3 + low = (self.read_reg(self.EFUSE_BLOCK1_ADDR + (4 * low_num_word)) >> 18) & 0x07 + return (hi << 3) + low + + def get_major_chip_version(self): + num_word = 5 + return (self.read_reg(self.EFUSE_BLOCK1_ADDR + (4 * num_word)) >> 24) & 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): + 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 > 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) & 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(6)] + + 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/installer/bin/esptool/esptool/targets/esp32h2beta2.py b/installer/bin/esptool/esptool/targets/esp32h2beta2.py new file mode 100644 index 0000000..6fa8f58 --- /dev/null +++ b/installer/bin/esptool/esptool/targets/esp32h2beta2.py @@ -0,0 +1,43 @@ +# 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/installer/bin/esptool/esptool/targets/esp32s2.py b/installer/bin/esptool/esptool/targets/esp32s2.py new file mode 100644 index 0000000..bffd553 --- /dev/null +++ b/installer/bin/esptool/esptool/targets/esp32s2.py @@ -0,0 +1,305 @@ +# SPDX-FileCopyrightText: 2014-2023 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 .esp32 import ESP32ROM +from ..loader import ESPLoader +from ..reset import HardReset +from ..util import FatalError, NotImplementedInROMError + + +class ESP32S2ROM(ESP32ROM): + CHIP_NAME = "ESP32-S2" + IMAGE_CHIP_ID = 2 + + FPGA_SLOW_BOOT = False + + IROM_MAP_START = 0x40080000 + IROM_MAP_END = 0x40B80000 + DROM_MAP_START = 0x3F000000 + DROM_MAP_END = 0x3F3F0000 + + CHIP_DETECT_MAGIC_VALUE = [0x000007C6] + + SPI_REG_BASE = 0x3F402000 + 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 + + MAC_EFUSE_REG = 0x3F41A044 # ESP32-S2 has special block for MAC efuses + + UART_CLKDIV_REG = 0x3F400014 + + SUPPORTS_ENCRYPTED_FLASH = True + + FLASH_ENCRYPTED_WRITE_ALIGN = 16 + + # todo: use espefuse APIs to get this info + EFUSE_BASE = 0x3F41A000 + EFUSE_RD_REG_BASE = EFUSE_BASE + 0x030 # BLOCK0 read base address + EFUSE_BLOCK1_ADDR = EFUSE_BASE + 0x044 + EFUSE_BLOCK2_ADDR = EFUSE_BASE + 0x05C + + 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 << 19 + + 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 + + EFUSE_RD_REPEAT_DATA3_REG = EFUSE_BASE + 0x3C + EFUSE_RD_REPEAT_DATA3_REG_FLASH_TYPE_MASK = 1 << 9 + + PURPOSE_VAL_XTS_AES256_KEY_1 = 2 + PURPOSE_VAL_XTS_AES256_KEY_2 = 3 + PURPOSE_VAL_XTS_AES128_KEY = 4 + + UARTDEV_BUF_NO = 0x3FFFFD14 # Variable in ROM .bss which indicates the port in use + UARTDEV_BUF_NO_USB_OTG = 2 # Value of the above indicating that USB-OTG is in use + + USB_RAM_BLOCK = 0x800 # Max block size USB-OTG is used + + GPIO_STRAP_REG = 0x3F404038 + GPIO_STRAP_SPI_BOOT_MASK = 0x8 # Not download mode + RTC_CNTL_OPTION1_REG = 0x3F408128 + RTC_CNTL_FORCE_DOWNLOAD_BOOT_MASK = 0x1 # Is download mode forced over USB? + + MEMORY_MAP = [ + [0x00000000, 0x00010000, "PADDING"], + [0x3F000000, 0x3FF80000, "DROM"], + [0x3F500000, 0x3FF80000, "EXTRAM_DATA"], + [0x3FF9E000, 0x3FFA0000, "RTC_DRAM"], + [0x3FF9E000, 0x40000000, "BYTE_ACCESSIBLE"], + [0x3FF9E000, 0x40072000, "MEM_INTERNAL"], + [0x3FFB0000, 0x40000000, "DRAM"], + [0x40000000, 0x4001A100, "IROM_MASK"], + [0x40020000, 0x40070000, "IRAM"], + [0x40070000, 0x40072000, "RTC_IRAM"], + [0x40080000, 0x40800000, "IROM"], + [0x50000000, 0x50002000, "RTC_DATA"], + ] + + def get_pkg_version(self): + num_word = 4 + return (self.read_reg(self.EFUSE_BLOCK1_ADDR + (4 * num_word)) >> 0) & 0x0F + + def get_minor_chip_version(self): + hi_num_word = 3 + hi = (self.read_reg(self.EFUSE_BLOCK1_ADDR + (4 * hi_num_word)) >> 20) & 0x01 + low_num_word = 4 + low = (self.read_reg(self.EFUSE_BLOCK1_ADDR + (4 * low_num_word)) >> 4) & 0x07 + return (hi << 3) + low + + def get_major_chip_version(self): + num_word = 3 + return (self.read_reg(self.EFUSE_BLOCK1_ADDR + (4 * num_word)) >> 18) & 0x03 + + def get_flash_version(self): + num_word = 3 + return (self.read_reg(self.EFUSE_BLOCK1_ADDR + (4 * num_word)) >> 21) & 0x0F + + def get_psram_version(self): + num_word = 3 + return (self.read_reg(self.EFUSE_BLOCK1_ADDR + (4 * num_word)) >> 28) & 0x0F + + def get_block2_version(self): + # BLK_VERSION_MINOR + num_word = 4 + return (self.read_reg(self.EFUSE_BLOCK2_ADDR + (4 * num_word)) >> 4) & 0x07 + + def get_chip_description(self): + chip_name = { + 0: "ESP32-S2", + 1: "ESP32-S2FH2", + 2: "ESP32-S2FH4", + 102: "ESP32-S2FNR2", + 100: "ESP32-S2R2", + }.get( + self.get_flash_version() + self.get_psram_version() * 100, + "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"] + + flash_version = { + 0: "No Embedded Flash", + 1: "Embedded Flash 2MB", + 2: "Embedded Flash 4MB", + }.get(self.get_flash_version(), "Unknown Embedded Flash") + features += [flash_version] + + psram_version = { + 0: "No Embedded PSRAM", + 1: "Embedded PSRAM 2MB", + 2: "Embedded PSRAM 4MB", + }.get(self.get_psram_version(), "Unknown Embedded PSRAM") + features += [psram_version] + + 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") + features += [block2_version] + + return features + + def get_crystal_freq(self): + # ESP32-S2 XTAL is fixed to 40MHz + return 40 + + def override_vddsdio(self, new_voltage): + raise NotImplementedInROMError( + "VDD_SDIO overrides are not supported for ESP32-S2" + ) + + def read_mac(self): + 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 flash_type(self): + return ( + 1 + if self.read_reg(self.EFUSE_RD_REPEAT_DATA3_REG) + & self.EFUSE_RD_REPEAT_DATA3_REG_FLASH_TYPE_MASK + else 0 + ) + + def get_flash_crypt_config(self): + return None # doesn't exist on ESP32-S2 + + 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) & 0xF + + def is_flash_encryption_key_valid(self): + # Need to see either an AES-128 key or two AES-256 keys + purposes = [self.get_key_block_purpose(b) for b in range(6)] + + if any(p == self.PURPOSE_VAL_XTS_AES128_KEY for p in purposes): + return True + + return any(p == self.PURPOSE_VAL_XTS_AES256_KEY_1 for p in purposes) and any( + p == self.PURPOSE_VAL_XTS_AES256_KEY_2 for p in purposes + ) + + 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 _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 + ): + print( + "WARNING: {} chip was placed into download mode using GPIO0.\n" + "esptool.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'.".format( + self.get_chip_description() + ) + ) + raise SystemExit(1) + + 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)() + + def change_baud(self, baud): + ESPLoader.change_baud(self, baud) + + +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 + + 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 + + if rom_loader.uses_usb_otg(): + self.ESP_RAM_BLOCK = self.USB_RAM_BLOCK + self.FLASH_WRITE_SIZE = self.USB_RAM_BLOCK + + +ESP32S2ROM.STUB_CLASS = ESP32S2StubLoader diff --git a/installer/bin/esptool/esptool/targets/esp32s3.py b/installer/bin/esptool/esptool/targets/esp32s3.py new file mode 100644 index 0000000..660537b --- /dev/null +++ b/installer/bin/esptool/esptool/targets/esp32s3.py @@ -0,0 +1,315 @@ +# SPDX-FileCopyrightText: 2014-2023 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 .esp32 import ESP32ROM +from ..loader import ESPLoader +from ..reset import HardReset +from ..util import FatalError, NotImplementedInROMError + + +class ESP32S3ROM(ESP32ROM): + CHIP_NAME = "ESP32-S3" + + IMAGE_CHIP_ID = 9 + + CHIP_DETECT_MAGIC_VALUE = [0x9] + + FPGA_SLOW_BOOT = False + + IROM_MAP_START = 0x42000000 + IROM_MAP_END = 0x44000000 + DROM_MAP_START = 0x3C000000 + DROM_MAP_END = 0x3E000000 + + UART_DATE_REG_ADDR = 0x60000080 + + 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 + + SUPPORTS_ENCRYPTED_FLASH = True + + FLASH_ENCRYPTED_WRITE_ALIGN = 16 + + # todo: use espefuse APIs to get this info + EFUSE_BASE = 0x60007000 # BLOCK0 read base address + EFUSE_BLOCK1_ADDR = EFUSE_BASE + 0x44 + EFUSE_BLOCK2_ADDR = EFUSE_BASE + 0x5C + 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 + + EFUSE_RD_REPEAT_DATA3_REG = EFUSE_BASE + 0x3C + EFUSE_RD_REPEAT_DATA3_REG_FLASH_TYPE_MASK = 1 << 9 + + PURPOSE_VAL_XTS_AES256_KEY_1 = 2 + PURPOSE_VAL_XTS_AES256_KEY_2 = 3 + PURPOSE_VAL_XTS_AES128_KEY = 4 + + UARTDEV_BUF_NO = 0x3FCEF14C # Variable in ROM .bss which indicates the port in use + UARTDEV_BUF_NO_USB_OTG = 3 # The above var when USB-OTG is used + UARTDEV_BUF_NO_USB_JTAG_SERIAL = 4 # The above var when USB-JTAG/Serial is used + + RTC_CNTL_WDT_WKEY = 0x50D83AA1 + RTCCNTL_BASE_REG = 0x60008000 + RTC_CNTL_WDTCONFIG0_REG = RTCCNTL_BASE_REG + 0x0090 + RTC_CNTL_WDTWPROTECT_REG = RTCCNTL_BASE_REG + 0x00B0 + + USB_RAM_BLOCK = 0x800 # Max block size USB-OTG is used + + GPIO_STRAP_REG = 0x60004038 + GPIO_STRAP_SPI_BOOT_MASK = 0x8 # Not download mode + RTC_CNTL_OPTION1_REG = 0x6000812C + RTC_CNTL_FORCE_DOWNLOAD_BOOT_MASK = 0x1 # Is download mode forced over USB? + + UART_CLKDIV_REG = 0x60000014 + + MEMORY_MAP = [ + [0x00000000, 0x00010000, "PADDING"], + [0x3C000000, 0x3D000000, "DROM"], + [0x3D000000, 0x3E000000, "EXTRAM_DATA"], + [0x600FE000, 0x60100000, "RTC_DRAM"], + [0x3FC88000, 0x3FD00000, "BYTE_ACCESSIBLE"], + [0x3FC88000, 0x403E2000, "MEM_INTERNAL"], + [0x3FC88000, 0x3FD00000, "DRAM"], + [0x40000000, 0x4001A100, "IROM_MASK"], + [0x40370000, 0x403E0000, "IRAM"], + [0x600FE000, 0x60100000, "RTC_IRAM"], + [0x42000000, 0x42800000, "IROM"], + [0x50000000, 0x50002000, "RTC_DATA"], + ] + + def get_pkg_version(self): + num_word = 3 + return (self.read_reg(self.EFUSE_BLOCK1_ADDR + (4 * num_word)) >> 21) & 0x07 + + def is_eco0(self, minor_raw): + # Workaround: The major version field was allocated to other purposes + # when block version is v1.1. + # Luckily only chip v0.0 have this kind of block version and efuse usage. + return ( + (minor_raw & 0x7) == 0 + and self.get_blk_version_major() == 1 + and self.get_blk_version_minor() == 1 + ) + + def get_minor_chip_version(self): + minor_raw = self.get_raw_minor_chip_version() + if self.is_eco0(minor_raw): + return 0 + return minor_raw + + def get_raw_minor_chip_version(self): + hi_num_word = 5 + hi = (self.read_reg(self.EFUSE_BLOCK1_ADDR + (4 * hi_num_word)) >> 23) & 0x01 + low_num_word = 3 + low = (self.read_reg(self.EFUSE_BLOCK1_ADDR + (4 * low_num_word)) >> 18) & 0x07 + return (hi << 3) + low + + def get_blk_version_major(self): + num_word = 4 + return (self.read_reg(self.EFUSE_BLOCK2_ADDR + (4 * num_word)) >> 0) & 0x03 + + def get_blk_version_minor(self): + num_word = 3 + return (self.read_reg(self.EFUSE_BLOCK1_ADDR + (4 * num_word)) >> 24) & 0x07 + + def get_major_chip_version(self): + minor_raw = self.get_raw_minor_chip_version() + if self.is_eco0(minor_raw): + return 0 + return self.get_raw_major_chip_version() + + def get_raw_major_chip_version(self): + num_word = 5 + return (self.read_reg(self.EFUSE_BLOCK1_ADDR + (4 * num_word)) >> 24) & 0x03 + + def get_chip_description(self): + major_rev = self.get_major_chip_version() + minor_rev = self.get_minor_chip_version() + return f"{self.CHIP_NAME} (revision v{major_rev}.{minor_rev})" + + def get_chip_features(self): + return ["WiFi", "BLE"] + + def get_crystal_freq(self): + # ESP32S3 XTAL is fixed to 40MHz + return 40 + + def get_flash_crypt_config(self): + return None # doesn't exist on ESP32-S3 + + 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) & 0xF + + def is_flash_encryption_key_valid(self): + # Need to see either an AES-128 key or two AES-256 keys + purposes = [self.get_key_block_purpose(b) for b in range(6)] + + if any(p == self.PURPOSE_VAL_XTS_AES128_KEY for p in purposes): + return True + + return any(p == self.PURPOSE_VAL_XTS_AES256_KEY_1 for p in purposes) and any( + p == self.PURPOSE_VAL_XTS_AES256_KEY_2 for p in purposes + ) + + def get_secure_boot_enabled(self): + return ( + self.read_reg(self.EFUSE_SECURE_BOOT_EN_REG) + & self.EFUSE_SECURE_BOOT_EN_MASK + ) + + def override_vddsdio(self, new_voltage): + raise NotImplementedInROMError( + "VDD_SDIO overrides are not supported for ESP32-S3" + ) + + def read_mac(self): + 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 flash_type(self): + return ( + 1 + if self.read_reg(self.EFUSE_RD_REPEAT_DATA3_REG) + & self.EFUSE_RD_REPEAT_DATA3_REG_FLASH_TYPE_MASK + else 0 + ) + + 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_rtc_watchdog(self): + # When USB-JTAG/Serial is used, the RTC watchdog is not reset + # and can then reset the board during flashing. Disable it. + if self.uses_usb_jtag_serial(): + self.write_reg(self.RTC_CNTL_WDTWPROTECT_REG, self.RTC_CNTL_WDT_WKEY) + self.write_reg(self.RTC_CNTL_WDTCONFIG0_REG, 0) + self.write_reg(self.RTC_CNTL_WDTWPROTECT_REG, 0) + + def _post_connect(self): + 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_rtc_watchdog() + + 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 + ): + print( + "WARNING: {} chip was placed into download mode using GPIO0.\n" + "esptool.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'.".format( + self.get_chip_description() + ) + ) + raise SystemExit(1) + + 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)() + + def change_baud(self, baud): + ESPLoader.change_baud(self, baud) + + +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 + + 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 + + if rom_loader.uses_usb_otg(): + self.ESP_RAM_BLOCK = self.USB_RAM_BLOCK + self.FLASH_WRITE_SIZE = self.USB_RAM_BLOCK + + +ESP32S3ROM.STUB_CLASS = ESP32S3StubLoader diff --git a/installer/bin/esptool/esptool/targets/esp32s3beta2.py b/installer/bin/esptool/esptool/targets/esp32s3beta2.py new file mode 100644 index 0000000..b7958bd --- /dev/null +++ b/installer/bin/esptool/esptool/targets/esp32s3beta2.py @@ -0,0 +1,42 @@ +# 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 + + def get_chip_description(self): + major_rev = self.get_major_chip_version() + minor_rev = self.get_minor_chip_version() + return f"{self.CHIP_NAME} (revision v{major_rev}.{minor_rev})" + + +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/installer/bin/esptool/esptool/targets/esp8266.py b/installer/bin/esptool/esptool/targets/esp8266.py new file mode 100644 index 0000000..f996663 --- /dev/null +++ b/installer/bin/esptool/esptool/targets/esp8266.py @@ -0,0 +1,191 @@ +# 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 ..loader import ESPLoader +from ..util import FatalError, NotImplementedInROMError + + +class ESP8266ROM(ESPLoader): + """Access class for ESP8266 ROM bootloader""" + + CHIP_NAME = "ESP8266" + IS_STUB = False + + CHIP_DETECT_MAGIC_VALUE = [0xFFF0C101] + + # OTP ROM addresses + ESP_OTP_MAC0 = 0x3FF00050 + ESP_OTP_MAC1 = 0x3FF00054 + ESP_OTP_MAC3 = 0x3FF0005C + + SPI_REG_BASE = 0x60000200 + SPI_USR_OFFS = 0x1C + SPI_USR1_OFFS = 0x20 + SPI_USR2_OFFS = 0x24 + SPI_MOSI_DLEN_OFFS = None + SPI_MISO_DLEN_OFFS = None + SPI_W0_OFFS = 0x40 + + UART_CLKDIV_REG = 0x60000014 + + XTAL_CLK_DIVIDER = 2 + + FLASH_SIZES = { + "512KB": 0x00, + "256KB": 0x10, + "1MB": 0x20, + "2MB": 0x30, + "4MB": 0x40, + "2MB-c1": 0x50, + "4MB-c1": 0x60, + "8MB": 0x80, + "16MB": 0x90, + } + + FLASH_FREQUENCY = { + "80m": 0xF, + "40m": 0x0, + "26m": 0x1, + "20m": 0x2, + } + + BOOTLOADER_FLASH_OFFSET = 0 + + MEMORY_MAP = [ + [0x3FF00000, 0x3FF00010, "DPORT"], + [0x3FFE8000, 0x40000000, "DRAM"], + [0x40100000, 0x40108000, "IRAM"], + [0x40201010, 0x402E1010, "IROM"], + ] + + def get_efuses(self): + # Return the 128 bits of ESP8266 efuse as a single Python integer + result = self.read_reg(0x3FF0005C) << 96 + result |= self.read_reg(0x3FF00058) << 64 + result |= self.read_reg(0x3FF00054) << 32 + result |= self.read_reg(0x3FF00050) + return result + + def _get_flash_size(self, efuses): + # rX_Y = EFUSE_DATA_OUTX[Y] + r0_4 = (efuses & (1 << 4)) != 0 + r3_25 = (efuses & (1 << 121)) != 0 + r3_26 = (efuses & (1 << 122)) != 0 + r3_27 = (efuses & (1 << 123)) != 0 + + if r0_4 and not r3_25: + if not r3_27 and not r3_26: + return 1 + elif not r3_27 and r3_26: + return 2 + if not r0_4 and r3_25: + if not r3_27 and not r3_26: + return 2 + elif not r3_27 and r3_26: + return 4 + return -1 + + def get_chip_description(self): + efuses = self.get_efuses() + is_8285 = ( + efuses & ((1 << 4) | 1 << 80) + ) != 0 # One or the other efuse bit is set for ESP8285 + if is_8285: + flash_size = self._get_flash_size(efuses) + max_temp = ( + efuses & (1 << 5) + ) != 0 # This efuse bit identifies the max flash temperature + chip_name = { + 1: "ESP8285H08" if max_temp else "ESP8285N08", + 2: "ESP8285H16" if max_temp else "ESP8285N16", + }.get(flash_size, "ESP8285") + return chip_name + return "ESP8266EX" + + def get_chip_features(self): + features = ["WiFi"] + if "ESP8285" in self.get_chip_description(): + features += ["Embedded Flash"] + return features + + def flash_spi_attach(self, hspi_arg): + if self.IS_STUB: + super(ESP8266ROM, self).flash_spi_attach(hspi_arg) + else: + # ESP8266 ROM has no flash_spi_attach command in serial protocol, + # but flash_begin will do it + self.flash_begin(0, 0) + + def flash_set_parameters(self, size): + # not implemented in ROM, but OK to silently skip for ROM + if self.IS_STUB: + super(ESP8266ROM, self).flash_set_parameters(size) + + def chip_id(self): + """ + Read Chip ID from efuse - the equivalent of the SDK system_get_chip_id() func + """ + id0 = self.read_reg(self.ESP_OTP_MAC0) + id1 = self.read_reg(self.ESP_OTP_MAC1) + return (id0 >> 24) | ((id1 & 0xFFFFFF) << 8) + + def read_mac(self): + """Read MAC from OTP ROM""" + mac0 = self.read_reg(self.ESP_OTP_MAC0) + mac1 = self.read_reg(self.ESP_OTP_MAC1) + mac3 = self.read_reg(self.ESP_OTP_MAC3) + if mac3 != 0: + oui = ((mac3 >> 16) & 0xFF, (mac3 >> 8) & 0xFF, mac3 & 0xFF) + elif ((mac1 >> 16) & 0xFF) == 0: + oui = (0x18, 0xFE, 0x34) + elif ((mac1 >> 16) & 0xFF) == 1: + oui = (0xAC, 0xD0, 0x74) + else: + raise FatalError("Unknown OUI") + return oui + ((mac1 >> 8) & 0xFF, mac1 & 0xFF, (mac0 >> 24) & 0xFF) + + def get_erase_size(self, offset, size): + """Calculate an erase size given a specific size in bytes. + + Provides a workaround for the bootloader erase bug.""" + + sectors_per_block = 16 + sector_size = self.FLASH_SECTOR_SIZE + num_sectors = (size + sector_size - 1) // sector_size + start_sector = offset // sector_size + + head_sectors = sectors_per_block - (start_sector % sectors_per_block) + if num_sectors < head_sectors: + head_sectors = num_sectors + + if num_sectors < 2 * head_sectors: + return (num_sectors + 1) // 2 * sector_size + else: + return (num_sectors - head_sectors) * sector_size + + def override_vddsdio(self, new_voltage): + raise NotImplementedInROMError( + "Overriding VDDSDIO setting only applies to ESP32" + ) + + +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 + + def get_erase_size(self, offset, size): + return size # stub doesn't have same size bug as ROM loader + + +ESP8266ROM.STUB_CLASS = ESP8266StubLoader diff --git a/installer/bin/esptool/esptool/targets/stub_flasher/stub_flasher_32.json b/installer/bin/esptool/esptool/targets/stub_flasher/stub_flasher_32.json new file mode 100644 index 0000000..3e87d81 --- /dev/null +++ b/installer/bin/esptool/esptool/targets/stub_flasher/stub_flasher_32.json @@ -0,0 +1,7 @@ +{ + "entry": 1074521560, + "text": "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", + "text_start": 1074520064, + "data": "GOv8P9jnC0Bx6AtA8+wLQO3oC0CP6AtA7egLQEnpC0AG6gtAeOoLQCHqC0CB5wtAo+kLQPjpC0Bn6QtAmuoLQI7pC0Ca6gtAXegLQLPoC0Dt6AtASekLQHfoC0BM6wtAs+wLQKXmC0DX7AtApeYLQKXmC0Cl5gtApeYLQKXmC0Cl5gtApeYLQKXmC0Dz6gtApeYLQM3rC0Cz7AtA", + "data_start": 1073605544 +} \ No newline at end of file diff --git a/installer/bin/esptool/esptool/targets/stub_flasher/stub_flasher_32c2.json b/installer/bin/esptool/esptool/targets/stub_flasher/stub_flasher_32c2.json new file mode 100644 index 0000000..07887d4 --- /dev/null +++ b/installer/bin/esptool/esptool/targets/stub_flasher/stub_flasher_32c2.json @@ -0,0 +1,7 @@ +{ + "entry": 1077413304, + "text": "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", + "text_start": 1077411840, + "data": "GGvKP+AIOEAsCThAhAk4QCgKOECUCjhAQgo4QKgHOEDkCThAJAo4QJgJOEBYBzhAzAk4QFgHOEC6CDhA/gg4QCwJOECECThAzAg4QBIIOEBCCDhAyAg4QOYMOEAsCThArAs4QJoMOECkBjhAxAw4QKQGOECkBjhApAY4QKQGOECkBjhApAY4QKQGOECkBjhASAs4QKQGOEDICzhAmgw4QA==", + "data_start": 1070295976 +} \ No newline at end of file diff --git a/installer/bin/esptool/esptool/targets/stub_flasher/stub_flasher_32c3.json b/installer/bin/esptool/esptool/targets/stub_flasher/stub_flasher_32c3.json new file mode 100644 index 0000000..8426a63 --- /dev/null +++ b/installer/bin/esptool/esptool/targets/stub_flasher/stub_flasher_32c3.json @@ -0,0 +1,7 @@ +{ + "entry": 1077413532, + "text": "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", + "text_start": 1077411840, + "data": "IGvIP1YKOECmCjhA/go4QKILOEAODDhAvAs4QCIJOEBeCzhAngs4QBILOEDSCDhARgs4QNIIOEAwCjhAdgo4QKYKOED+CjhAQgo4QIYJOEC2CThAPgo4QGAOOECmCjhAJg04QBgOOEASCDhAQA44QBIIOEASCDhAEgg4QBIIOEASCDhAEgg4QBIIOEASCDhAwgw4QBIIOEBEDThAGA44QA==", + "data_start": 1070164912 +} \ No newline at end of file diff --git a/installer/bin/esptool/esptool/targets/stub_flasher/stub_flasher_32c6.json b/installer/bin/esptool/esptool/targets/stub_flasher/stub_flasher_32c6.json new file mode 100644 index 0000000..2bf3acc --- /dev/null +++ b/installer/bin/esptool/esptool/targets/stub_flasher/stub_flasher_32c6.json @@ -0,0 +1,7 @@ +{ + "entry": 1082132112, + "text": "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", + "text_start": 1082130432, + "data": "HCuEQCoKgEB6CoBA0gqAQHYLgEDiC4BAkAuAQPYIgEAyC4BAcguAQOYKgECmCIBAGguAQKYIgEAECoBASgqAQHoKgEDSCoBAFgqAQFoJgECKCYBAEgqAQDQOgEB6CoBA+gyAQOwNgEDmB4BAFA6AQOYHgEDmB4BA5geAQOYHgEDmB4BA5geAQOYHgEDmB4BAlgyAQOYHgEAYDYBA7A2AQA==", + "data_start": 1082469292 +} \ No newline at end of file diff --git a/installer/bin/esptool/esptool/targets/stub_flasher/stub_flasher_32c6beta.json b/installer/bin/esptool/esptool/targets/stub_flasher/stub_flasher_32c6beta.json new file mode 100644 index 0000000..4709f9d --- /dev/null +++ b/installer/bin/esptool/esptool/targets/stub_flasher/stub_flasher_32c6beta.json @@ -0,0 +1,7 @@ +{ + "entry": 1077413318, + "text": "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", + "text_start": 1077411840, + "data": "GGvIP/gIOEBECThAnAk4QEAKOECsCjhAWgo4QMAHOED8CThAPAo4QLAJOEBwBzhA5Ak4QHAHOEDSCDhAFgk4QEQJOECcCThA5Ag4QCoIOEBaCDhA4Ag4QP4MOEBECThAxAs4QLIMOEC8BjhA3Aw4QLwGOEC8BjhAvAY4QLwGOEC8BjhAvAY4QLwGOEC8BjhAYAs4QLwGOEDgCzhAsgw4QA==", + "data_start": 1070164904 +} \ No newline at end of file diff --git a/installer/bin/esptool/esptool/targets/stub_flasher/stub_flasher_32h2.json b/installer/bin/esptool/esptool/targets/stub_flasher/stub_flasher_32h2.json new file mode 100644 index 0000000..f003a55 --- /dev/null +++ b/installer/bin/esptool/esptool/targets/stub_flasher/stub_flasher_32h2.json @@ -0,0 +1,7 @@ +{ + "entry": 1082132112, + "text": "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", + "text_start": 1082130432, + "data": "HCuDQCoKgEB6CoBA0gqAQHYLgEDiC4BAkAuAQPYIgEAyC4BAcguAQOYKgECmCIBAGguAQKYIgEAECoBASgqAQHoKgEDSCoBAFgqAQFoJgECKCYBAEgqAQDQOgEB6CoBA+gyAQOwNgEDmB4BAFA6AQOYHgEDmB4BA5geAQOYHgEDmB4BA5geAQOYHgEDmB4BAlgyAQOYHgEAYDYBA7A2AQA==", + "data_start": 1082403756 +} \ No newline at end of file diff --git a/installer/bin/esptool/esptool/targets/stub_flasher/stub_flasher_32h2beta1.json b/installer/bin/esptool/esptool/targets/stub_flasher/stub_flasher_32h2beta1.json new file mode 100644 index 0000000..37d29e8 --- /dev/null +++ b/installer/bin/esptool/esptool/targets/stub_flasher/stub_flasher_32h2beta1.json @@ -0,0 +1,7 @@ +{ + "entry": 1077413318, + "text": "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", + "text_start": 1077411840, + "data": "GGvIP/gIOEBECThAnAk4QEAKOECsCjhAWgo4QMAHOED8CThAPAo4QLAJOEBwBzhA5Ak4QHAHOEDSCDhAFgk4QEQJOECcCThA5Ag4QCoIOEBaCDhA4Ag4QP4MOEBECThAxAs4QLIMOEC8BjhA3Aw4QLwGOEC8BjhAvAY4QLwGOEC8BjhAvAY4QLwGOEC8BjhAYAs4QLwGOEDgCzhAsgw4QA==", + "data_start": 1070164904 +} \ No newline at end of file diff --git a/installer/bin/esptool/esptool/targets/stub_flasher/stub_flasher_32h2beta2.json b/installer/bin/esptool/esptool/targets/stub_flasher/stub_flasher_32h2beta2.json new file mode 100644 index 0000000..f7cc1ce --- /dev/null +++ b/installer/bin/esptool/esptool/targets/stub_flasher/stub_flasher_32h2beta2.json @@ -0,0 +1,7 @@ +{ + "entry": 1077413318, + "text": "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", + "text_start": 1077411840, + "data": "GGvIP/gIOEBECThAnAk4QEAKOECsCjhAWgo4QMAHOED8CThAPAo4QLAJOEBwBzhA5Ak4QHAHOEDSCDhAFgk4QEQJOECcCThA5Ag4QCoIOEBaCDhA4Ag4QP4MOEBECThAxAs4QLIMOEC8BjhA3Aw4QLwGOEC8BjhAvAY4QLwGOEC8BjhAvAY4QLwGOEC8BjhAYAs4QLwGOEDgCzhAsgw4QA==", + "data_start": 1070164904 +} \ No newline at end of file diff --git a/installer/bin/esptool/esptool/targets/stub_flasher/stub_flasher_32s2.json b/installer/bin/esptool/esptool/targets/stub_flasher/stub_flasher_32s2.json new file mode 100644 index 0000000..de8cdc2 --- /dev/null +++ b/installer/bin/esptool/esptool/targets/stub_flasher/stub_flasher_32s2.json @@ -0,0 +1,7 @@ +{ + "entry": 1073907696, + "text": "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", + "text_start": 1073905664, + "data": "ZCv9PzaLAkDBiwJAhpACQEqMAkDjiwJASowCQKmMAkByjQJA5Y0CQI2NAkDAigJAC40CQGSNAkDMjAJACI4CQPaMAkAIjgJAr4sCQA6MAkBKjAJAqYwCQMeLAkACiwJAx44CQD2QAkDYiQJAZZACQNiJAkDYiQJA2IkCQNiJAkDYiQJA2IkCQNiJAkDYiQJAZI4CQNiJAkBZjwJAPZACQA==", + "data_start": 1073622012 +} \ No newline at end of file diff --git a/installer/bin/esptool/esptool/targets/stub_flasher/stub_flasher_32s3.json b/installer/bin/esptool/esptool/targets/stub_flasher/stub_flasher_32s3.json new file mode 100644 index 0000000..1a73fca --- /dev/null +++ b/installer/bin/esptool/esptool/targets/stub_flasher/stub_flasher_32s3.json @@ -0,0 +1,7 @@ +{ + "entry": 1077381684, + "text": "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", + "text_start": 1077379072, + "data": "ZCvKP5aNN0B7jjdAPJM3QAaPN0CbjjdABo83QGWPN0AykDdApZA3QE2QN0AhjTdAyI83QCSQN0CIjzdAx5A3QLKPN0DHkDdAaY43QMaON0AGjzdAZY83QIGON0BijTdAiJE3QAKTN0A+jDdAIpM3QD6MN0A+jDdAPow3QD6MN0A+jDdAPow3QD6MN0A+jDdAIpE3QD6MN0AdkjdAApM3QAQInwAAAAAAAAAYAQQIBQAAAAAAAAAIAQQIBgAAAAAAAAAAAQQIIQAAAAAAIAAAEQQI3AAAAAAAIAAAEQQIDAAAAAAAIAAAAQQIEgAAAAAAIAAAESAoDAAQAQAA", + "data_start": 1070279668 +} \ No newline at end of file diff --git a/installer/bin/esptool/esptool/targets/stub_flasher/stub_flasher_32s3beta2.json b/installer/bin/esptool/esptool/targets/stub_flasher/stub_flasher_32s3beta2.json new file mode 100644 index 0000000..8621a93 --- /dev/null +++ b/installer/bin/esptool/esptool/targets/stub_flasher/stub_flasher_32s3beta2.json @@ -0,0 +1,7 @@ +{ + "entry": 1077381176, + "text": "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", + "text_start": 1077379072, + "data": "ZCvKP4KLN0APjDdA15A3QJqMN0AvjDdAmow3QPmMN0DGjTdAOY43QOGNN0ANizdAXI03QLiNN0AcjTdAXI43QEaNN0BcjjdA/Ys3QFqMN0CajDdA+Yw3QBWMN0BOizdAHI83QJuQN0AsijdAvZA3QCyKN0AsijdALIo3QCyKN0AsijdALIo3QCyKN0AsijdAto43QCyKN0CyjzdAm5A3QA==", + "data_start": 1070279668 +} \ No newline at end of file diff --git a/installer/bin/esptool/esptool/targets/stub_flasher/stub_flasher_8266.json b/installer/bin/esptool/esptool/targets/stub_flasher/stub_flasher_8266.json new file mode 100644 index 0000000..8d42b24 --- /dev/null +++ b/installer/bin/esptool/esptool/targets/stub_flasher/stub_flasher_8266.json @@ -0,0 +1,7 @@ +{ + "entry": 1074843652, + "text": "qBAAQAH//0Z0AAAAkIH/PwgB/z+AgAAAhIAAAEBAAABIQf8/lIH/PzH5/xLB8CAgdAJhA4XvATKv/pZyA1H0/0H2/zH0/yAgdDA1gEpVwCAAaANCFQBAMPQbQ0BA9MAgAEJVADo2wCAAIkMAIhUAMev/ICD0N5I/Ieb/Meb/Qen/OjLAIABoA1Hm/yeWEoYAAAAAAMAgACkEwCAAWQNGAgDAIABZBMAgACkDMdv/OiIMA8AgADJSAAgxEsEQDfAAoA0AAJiB/z8Agf4/T0hBSais/z+krP8/KNAQQEzqEEAMAABg//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/P5iq/z+8DwBAiA8AQKgPAEBYPwBAREYAQCxMAEB4SABAAEoAQLRJAEDMLgBA2DkAQEjfAECQ4QBATCYAQIRJAEAhvP+SoRCQEcAiYSMioAACYUPCYULSYUHiYUDyYT8B6f/AAAAhsv8xs/8MBAYBAABJAksiNzL4hbUBIqCMDEMqIcWnAYW0ASF8/8F6/zGr/yoswCAAyQIhqP8MBDkCMaj/DFIB2f/AAAAxpv8ioQHAIABIAyAkIMAgACkDIqAgAdP/wAAAAdL/wAAAAdL/wAAAcZ3/UZ7/QZ7/MZ7/YqEADAIBzf/AAAAhnP8xYv8qI8AgADgCFnP/wCAA2AIMA8AgADkCDBIiQYQiDQEMJCJBhUJRQzJhIiaSCRwzNxIghggAAAAiDQMyDQKAIhEwIiBmQhEoLcAgACgCImEiBgEAHCIiUUOFqAEioIQMgxoiBZsBIg0DMg0CgCIRMDIgIX//N7ITIqDAxZUBIqDuRZUBxaUBRtz/AAAiDQEMtEeSAgaZACc0Q2ZiAsbLAPZyIGYyAoZxAPZCCGYiAsZWAEbKAGZCAgaHAGZSAsarAIbGACaCefaCAoarAAyUR5ICho8AZpICBqMABsAAHCRHkgJGfAAnNCcM9EeSAoY+ACc0CwzUR5IChoMAxrcAAGayAkZLABwUR5ICRlgARrMAQqDRRxJoJzQRHDRHkgJGOABCoNBHEk/GrAAAQqDSR5IChi8AMqDTN5ICRpcFRqcALEIMDieTAgZqBUYrACKgAEWIASKgAAWIAYWYAUWYASKghDKgCBoiC8yFigFW3P0MDs0ORpsAAMwThl8FRpUAJoMCxpMABmAFAWn/wAAA+sycIsaPAAAAICxBAWb/wAAAVhIj8t/w8CzAzC+GaQUAIDD0VhP+4Sv/hgMAICD1AV7/wAAAVtIg4P/A8CzA9z7qhgMAICxBAVf/wAAAVlIf8t/w8CzAVq/+RloFJoOAxgEAAABmswJG3f8MDsKgwIZ4AAAAZrMCRkQFBnIAAMKgASazAgZwACItBDEX/+KgAMKgwiezAsZuADhdKC1FdgFGPAUAwqABJrMChmYAMi0EIQ7/4qAAwqDCN7ICRmUAKD0MHCDjgjhdKC2FcwEx9/4MBEljMtMr6SMgxIMGWgAAIfP+DA5CAgDCoMbnlALGWADIUigtMsPwMCLAQqDAIMSTIs0YTQJioO/GAQBSBAAbRFBmMCBUwDcl8TINBVINBCINBoAzEQAiEVBDIEAyICINBwwOgCIBMCIgICbAMqDBIMOThkMAAAAh2f4MDjICAMKgxueTAsY+ADgywqDI5xMCBjwA4kIAyFIGOgAcggwODBwnEwIGNwAGCQVmQwKGDwVGMAAwIDQMDsKgwOcSAoYwADD0QYvtzQJ888YMACg+MmExAQL/wAAASC4oHmIuACAkEDIhMSYEDsAgAFImAEBDMFBEEEAiIMAgACkGG8zizhD3PMjGgf9mQwJGgP8Gov9mswIG+QTGFgAAAGHA/gwOSAYMFTLD8C0OQCWDMF6DUCIQwqDG55JLcbn+7QKIB8KgyTc4PjBQFMKgwKLNGIzVBgwAWiooAktVKQRLRAwSUJjANzXtFmLaSQaZB8Zn/2aDAoblBAwcDA7GAQAAAOKgAMKg/8AgdMVeAeAgdIVeAQVvAVZMwCINAQzzNxIxJzMVZkICxq4EZmIChrMEJjICxvn+BhkAABwjN5ICxqgEMqDSNxJFHBM3EgJG8/5GGQAhlP7oPdItAgHA/sAAACGS/sAgADgCIZH+ICMQ4CKC0D0gxYoBPQItDAG5/sAAACKj6AG2/sAAAMbj/lhdSE04PSItAoVqAQbg/gAyDQMiDQKAMxEgMyAyw/AizRgFSQHG2f4AAABSzRhSYSQiDQMyDQKAIhEwIiAiwvAiYSoMH4Z0BCF3/nGW/rIiAGEy/oKgAyInApIhKoJhJ7DGwCc5BAwaomEnsmE2hTkBsiE2cW3+UiEkYiEqcEvAykRqVQuEUmElgmEshwQCxk0Ed7sCRkwEmO2iLRBSLRUobZJhKKJhJlJhKTxTyH3iLRT4/SezAkbuAzFc/jAioCgCoAIAMUL+DA4MEumT6YMp0ymj4mEm/Q7iYSjNDkYGAHIhJwwTcGEEfMRgQ5NtBDliXQtyISQG4AMAgiEkkiElITP+l7jZMggAG3g5goYGAKIhJwwjMGoQfMUMFGBFg20EOWJdC0bUA3IhJFIhJSEo/le321IHAPiCWZKALxEc81oiQmExUmE0smE2G9cFeQEME0IhMVIhNLIhNlYSASKgICBVEFaFAPAgNCLC+CA1g/D0QYv/DBJhLv4AH0AAUqFXNg8AD0BA8JEMBvBigzBmIJxGDB8GAQAAANIhJCEM/ixDOWJdCwabAF0Ltjwehg4AciEnfMNwYQQMEmAjg20CDDOGFQBdC9IhJEYAAP0GgiElh73bG90LLSICAAAcQAAioYvMIO4gtjzkbQ9x+P3gICQptyAhQSnH4ONBwsz9VuIfwCAkJzwoRhEAkiEnfMOQYQQMEmAjg20CDFMh7P05Yn0NxpQDAAAAXQvSISRGAAD9BqIhJae90RvdCy0iAgAAHEAAIqGLzCDuIMAgJCc84cAgJAACQODgkSKv+CDMEPKgABacBoYMAAAAciEnfMNwYQQMEmAjg20CDGMG5//SISRdC4IhJYe94BvdCy0iAgAAHEAAIqEg7iCLzLaM5CHM/cLM+PoyIeP9KiPiQgDg6EGGDAAAAJIhJwwTkGEEfMRgNINtAwxzxtT/0iEkXQuiISUhv/2nvd1B1v0yDQD6IkoiMkIAG90b//ZPAobc/yHt/Xz28hIcIhIdIGYwYGD0Z58Hxh0A0iEkXQssc8Y/ALaMIAYPAHIhJ3zDcGEEDBJgI4NtAjwzBrz/AABdC9IhJEYAAP0GgiElh73ZG90LLSICAAAcQAAioYvMIO4gtozkbQ/gkHSSYSjg6EHCzPj9BkYCADxDhtQC0iEkXQsha/0nte+iISgLb6JFABtVFoYHVrz4hhwADJPGywJdC9IhJEYAAP0GIWH9J7XqhgYAciEnfMNwYQQMEmAjg20CLGPGmf8AANIhJF0LgiElh73ekVb90GjAUCnAZ7IBbQJnvwFtD00G0D0gUCUgUmE0YmE1smE2Abz9wAAAYiE1UiE0siE2at1qVWBvwFZm+UbQAv0GJjIIxgQAANIhJF0LDKMhb/05Yn0NBhcDAAAMDyYSAkYgACKhICJnESwEIYL9QmcSMqAFUmE0YmE1cmEzsmE2Aab9wAAAciEzsiE2YiE1UiE0PQcioJBCoAhCQ1gLIhszVlL/IqBwDJMyR+gLIht3VlL/HJRyoViRVf0MeEYCAAB6IpoigkIALQMbMkeT8SFq/TFq/QyEBgEAQkIAGyI3kvdGYQEhZ/36IiICACc8HUYPAAAAoiEnfMOgYQQMEmAjg20CDLMGVP/SISRdCyFc/foiYiElZ73bG90LPTIDAAAcQAAzoTDuIDICAIvMNzzhIVT9QVT9+iIyAgAMEgATQAAioUBPoAsi4CIQMMzAAANA4OCRSAQxLf0qJDA/oCJjERv/9j8Cht7/IUf9QqEgDANSYTSyYTYBaP3AAAB9DQwPUiE0siE2RhUAAACCISd8w4BhBAwSYCODbQIM4wa0AnIhJF0LkiEll7fgG3cLJyICAAAcQAAioSDuIIvMtjzkITP9QRL9+iIiAgDgMCQqRCEw/cLM/SokMkIA4ONBG/8hC/0yIhM3P9McMzJiE90HbQ8GHQEATAQyoAAiwURSYTRiYTWyYTZyYTMBQ/3AAAByITOB/fwioWCAh4JBHv0qKPoiDAMiwhiCYTIBO/3AAACCITIhGf1CpIAqKPoiDAMiwhgBNf3AAACoz4IhMvAqoCIiEYr/omEtImEuTQ9SITRiITVyITOyITbGAwAiD1gb/xAioDIiERszMmIRMiEuQC/ANzLmDAIpESkBrQIME+BDEZLBREr5mA9KQSop8CIRGzMpFJqqZrPlMeb8OiKMEvYqKyHW/EKm0EBHgoLIWCqIIqC8KiSCYSsMCXzzQmE5ImEwxkMAAF0L0iEkRgAA/QYsM8aZAACiISuCCgCCYTcWiA4QKKB4Ahv3+QL9CAwC8CIRImE4QiE4cCAEImEvC/9AIiBwcUFWX/4Mp4c3O3B4EZB3IAB3EXBwMUIhMHJhLwwacbb8ABhAAKqhKoRwiJDw+hFyo/+GAgAAQiEvqiJCWAD6iCe38gYgAHIhOSCAlIqHoqCwQan8qohAiJBymAzMZzJYDH0DMsP+IClBoaP88qSwxgoAIIAEgIfAQiE5fPeAhzCKhPCIgKCIkHKYDMx3MlgMMHMgMsP+giE3C4iCYTdCITcMuCAhQYeUyCAgBCB3wHz6IiE5cHowenIipLAqdyGO/CB3kJJXDEIhKxuZG0RCYStyIS6XFwLGvf+CIS0mKALGmQBGggAM4seyAsYwAJIhJdApwKYiAoYlACGj/OAwlEF9/CojQCKQIhIMADIRMCAxlvIAMCkxFjIFJzwCRiQAhhIAAAyjx7NEkZj8fPgAA0DgYJFgYAQgKDAqJpoiQCKQIpIMG3PWggYrYz0HZ7zdhgYAoiEnfMOgYQQMEmAjg20CHAPGdv4AANIhJF0LYiElZ73eIg0AGz0AHEAAIqEg7iCLzAzi3QPHMgLG2v8GCAAiDQEyzAgAE0AAMqEiDQDSzQIAHEAAIqEgIyAg7iDCzBAhdfzgMJRhT/wqI2AikDISDAAzETAgMZaiADA5MSAghEYJAAAAgWz8DKR89xs0AARA4ECRQEAEICcwKiSKImAikCKSDE0DliL+AANA4OCRMMzAImEoDPMnIxUhOvxyISj6MiFe/Bv/KiNyQgAGNAAAgiEoZrga3H8cCZJhKAYBANIhJF0LHBMhL/x89jliBkH+MVP8KiMiwvAiAgAiYSYnPB0GDgCiISd8w6BhBAwSYCODbQIcI8Y1/gAA0iEkXQtiISVnvd4b3QstIgIAciEmABxAACKhi8wg7iB3POGCISYxQPySISgMFgAYQABmoZozC2Yyw/DgJhBiAwAACEDg4JEqZiE5/IDMwCovDANmuQwxDPz6QzE1/Do0MgMATQZSYTRiYTWyYTYBSfzAAABiITVSITRq/7IhNoYAAAAMD3EB/EInEWInEmpkZ78Chnj/95YHhgIA0iEkXQscU0bJ/wDxIfwhIvw9D1JhNGJhNbJhNnJhMwE1/MAAAHIhMyEL/DInEUInEjo/ATD8wAAAsiE2YiE1UiE0Mer7KMMLIinD8ej7eM/WN7iGPgFiISUM4tA2wKZDDkG2+1A0wKYjAkZNAMYyAseyAoYuAKYjAkYlAEHc++AglEAikCISvAAyETAgMZYSATApMRZSBSc8AsYkAAYTAAAAAAyjx7NEfPiSpLAAA0DgYJFgYAQgKDAqJpoiQCKQIpIMG3PWggYrYz0HZ7zdhgYAciEnfMNwYQQMEmAjg20CHHPG1P0AANIhJF0LgiElh73eIg0AGz0AHEAAIqEg7iCLzAzi3QPHMgKG2/8GCAAAACINAYs8ABNAADKhIg0AK90AHEAAIqEgIyAg7iDCzBBBr/vgIJRAIpAiErwAIhEg8DGWjwAgKTHw8ITGCAAMo3z3YqSwGyMAA0DgMJEwMATw9zD682r/QP+Q8p8MPQKWL/4AAkDg4JEgzMAioP/3ogLGQACGAgAAHIMG0wDSISRdCyFp+ye17/JFAG0PG1VG6wAM4scyGTINASINAIAzESAjIAAcQAAioSDuICvdwswQMYr74CCUqiIwIpAiEgwAIhEgMDEgKTHWEwIMpBskAARA4ECRQEAEMDkwOjRBf/uKM0AzkDKTDE0ClvP9/QMAAkDg4JEgzMB3g3xioA7HNhpCDQEiDQCARBEgJCAAHEAAIqEg7iDSzQLCzBBBcPvgIJSqIkAikEISDABEEUAgMUBJMdYSAgymG0YABkDgYJFgYAQgKTAqJmFl+4oiYCKQIpIMbQSW8v0yRQAABEDg4JFAzMB3AggbVf0CRgIAAAAiRQErVQZz//BghGb2AoazACKu/ypmIYH74GYRaiIoAiJhJiF/+3IhJmpi+AYWhwV3PBzGDQCCISd8w4BhBAwSYCODbQIck4Zb/QDSISRdC5IhJZe93xvdCy0iAgCiISYAHEAAIqGLzCDuIKc84WIhJgwSABZAACKhCyLgIhBgzMAABkDg4JEq/wzix7IChjAAciEl0CfApiICxiUAQTP74CCUQCKQItIPIhIMADIRMCAxlgIBMCkxFkIFJzwChiQAxhIAAAAMo8ezRJFW+3z4AANA4GCRYGAEICgwKiaaIkAikCKSDBtz1oIGK2M9B2e83YYGAIIhJ3zDgGEEDBJgI4NtAhyjxiv9AADSISRdC5IhJZe93iINABs9ABxAACKhIO4gi8wM4t0DxzICBtv/BggAAAAiDQGLPAATQAAyoSINACvdABxAACKhICMgIO4gwswQYQb74CCUYCKQItIPMhIMADMRMCAxloIAMDkxICCExggAgSv7DKR89xs0AARA4ECRQEAEICcwKiSKImAikCKSDE0DliL+AANA4OCRMMzAMSH74CIRKjM4AzJhJjEf+6IhJiojKAIiYSgWCganPB5GDgByISd8w3BhBAwSYCODbQIcs8b3/AAAANIhJF0LgiElh73dG90LLSICAJIhJgAcQAAioYvMIO4glzzhoiEmDBIAGkAAIqFiISgLIuAiECpmAApA4OCRoMzAYmEocen6giEocHXAkiEsMeb6gCfAkCIQOiJyYSk9BSe1AT0CQZ36+jNtDze0bQYSACHH+ixTOWLGbQA8UyHE+n0NOWIMJgZsAF0L0iEkRgAA/QYhkvonteGiISliIShyISxgKsAx0PpwIhAqIyICABuqIkUAomEpG1ULb1Yf/QYMAAAyAgBixv0yRQAyAgEyRQEyAgI7IjJFAjtV9jbjFgYBMgIAMkUAZiYFIgIBIkUBalX9BqKgsHz5gqSwcqEABr3+IaP6KLIH4gIGl/zAICQnPCBGDwCCISd8w4BhBAwSYCODbQIsAwas/AAAXQvSISRGAAD9BpIhJZe92RvdCy0iAgAAHEAAIqGLzCDuIMAgJCc84cAgJAACQODgkXyCIMwQfQ1GAQAAC3fCzPiiISR3ugL2jPEht/oxt/pNDFJhNHJhM7JhNgWVAAsisiE2ciEzUiE0IO4QDA8WLAaGDAAAAIIhJ3zDgGEEDBJgI4NtAiyTBg8AciEkXQuSISWXt+AbdwsnIgIAABxAACKhIO4gi8y2jOTgMHTCzPjg6EEGCgCiISd8w6BhBAwSYCODbQIsoyFm+jliRg8AciEkXQtiISVnt9syBwAbd0Fg+hv/KKSAIhEwIiAppPZPCEbe/wByISRdCyFa+iwjOWIMBoYBAHIhJF0LfPYmFhVLJsxyhgMAAAt3wsz4giEkd7gC9ozxgU/6IX/6MX/6yXhNDFJhNGJhNXJhM4JhMrJhNoWGAIIhMpIhKKIhJgsimeiSISng4hCiaBByITOiISRSITSyITZiITX5+OJoFJJoFaDXwLDFwP0GllYOMWz6+NgtDMV+APDg9E0C8PD1fQwMeGIhNbIhNkYlAAAAkgIAogIC6umSAgHqmZru+v7iAgOampr/mp7iAgSa/5qe4gIFmv+anuICBpr/mp7iAgea/5ru6v+LIjqSRznAQCNBsCKwsJBgRgIAADICABsiOu7q/yo5vQJHM+8xTvotDkJhMWJhNXJhM4JhMrJhNgV2ADFI+u0CLQ+FdQBCITFyITOyITZAd8CCITJBQfpiITX9AoyHLQuwOMDG5v8AAAD/ESEI+urv6dL9BtxW+KLw7sB87+D3g0YCAAAAAAwM3Qzyr/0xNPpSISooI2IhJNAiwNBVwNpm0RD6KSM4DXEP+lJhKspTWQ1wNcAMAgwV8CWDYmEkICB0VoIAQtOAQCWDFpIAwQX6LQzFKQDJDYIhKtHs+Yz4KD0WsgDwLzHwIsDWIgDGhPvWjwAioMcpXQY6AABWTw4oPcwSRlH6IqDIhgAAIqDJKV3GTfooLYwSBkz6Ie75ARv6wAAAAR76wAAAhkf6yD3MHMZF+iKj6AEV+sAAAMAMAAZC+gDiYSIMfEaU+gEV+sAAAAwcDAMGCAAAyC34PfAsICAgtMwSxpv6Ri77Mi0DIi0CRTMAMqAADBwgw4PGKft4fWhtWF1ITTg9KC0MDAH7+cAAAO0CDBLgwpOGJfsAAAH1+cAAAAwMBh/7ACHI+UhdOC1JAiHG+TkCBvr/QcT5DAI4BMKgyDDCgykEQcD5PQwMHCkEMMKDBhP7xzICxvP9xvr9KD0WIvLGF/oCIUOSoRDCIULSIUHiIUDyIT+aEQ3wAAAIAABgHAAAYAAAAGAQAABgIfz/EsHw6QHAIADoAgkxySHZESH4/8AgAMgCwMB0nOzRmvlGBAAAADH0/8AgACgDOA0gIHTAAwALzGYM6ob0/yHv/wgxwCAA6QLIIdgR6AESwRAN8AAAAPgCAGAQAgBgAAIAYAAAAAgh/P/AIAA4AjAwJFZD/yH5/0H6/8AgADkCMff/wCAASQPAIABIA1Z0/8AgACgCDBMgIAQwIjAN8AAAgAAAAABA////AAQCAGASwfDJIcFw+QkxKEzZERaCCEX6/xYiCChMDPMMDSejDCgsMCIQDBMg04PQ0HQQESBF+P8WYv8h3v8x7v/AIAA5AsAgADIiAFZj/zHX/8AgACgDICAkVkL/KCwx5f9AQhEhZfnQMoMh5P8gJBBB5P/AIAApBCHP/8AgADkCwCAAOAJWc/8MEhwD0COT3QIoTNAiwClMKCza0tksCDHIIdgREsEQDfAAAABMSgBAEsHgyWHBRfn5Mfg86UEJcdlR7QL3swH9AxYfBNgc2t/Q3EEGAQAAAIXy/yhMphIEKCwnrfJF7f8Wkv8oHE0PPQ4B7v/AAAAgIHSMMiKgxClcKBxIPPoi8ETAKRxJPAhxyGHYUehB+DESwSAN8AAAAP8PAABRKvkSwfAJMQwUQkUAMExBSSVB+v85FSk1MDC0SiIqIyAsQSlFDAIiZQUBXPnAAAAIMTKgxSAjkxLBEA3wAAAAMDsAQBLB8AkxMqDAN5IRIqDbAfv/wAAAIqDcRgQAAAAAMqDbN5IIAfb/wAAAIqDdAfT/wAAACDESwRAN8AAAABLB8Mkh2REJMc0COtJGAgAAIgwAwswBxfr/15zzAiEDwiEC2BESwRAN8AAAWBAAAHAQAAAYmABAHEsAQDSYAEAAmQBAkfv/EsHgyWHpQfkxCXHZUZARwO0CItEQzQMB9f/AAADx+viGCgDdDMe/Ad0PTQ09AS0OAfD/wAAAICB0/EJNDT0BItEQAez/wAAA0O6A0MzAVhz9IeX/MtEQECKAAef/wAAAIeH/HAMaIgX1/y0MBgEAAAAioGOR3f+aEQhxyGHYUehB+DESwSAN8AASwfAioMAJMQG6/8AAAAgxEsEQDfAAAABsEAAAaBAAAHQQAAB4EAAAfBAAAIAQAACQEAAAmA8AQIw7AEASweCR/P/5Mf0CIcb/yWHZUQlx6UGQEcAaIjkCMfL/LAIaM0kDQfD/0tEQGkTCoABSZADCbRoB8P/AAABh6v8hwPgaZmgGZ7ICxkkALQ0Btv/AAAAhs/8x5f8qQRozSQNGPgAAAGGv/zHf/xpmaAYaM+gDwCbA57ICIOIgYd3/PQEaZlkGTQ7wLyABqP/AAAAx2P8gIHQaM1gDjLIMBEJtFu0ExhIAAAAAQdH/6v8aRFkEBfH/PQ4tAYXj/0Xw/00OPQHQLSABmv/AAABhyf/qzBpmWAYhk/8aIigCJ7y8McL/UCzAGjM4AzeyAkbd/0bq/0KgAEJNbCG5/xAigAG//8AAAFYC/2G5/yINbBBmgDgGRQcA9+IR9k4OQbH/GkTqNCJDABvuxvH/Mq/+N5LBJk4pIXv/0D0gECKAAX7/wAAABej/IXb/HAMaIkXa/0Xn/ywCAav4wAAAhgUAYXH/Ui0aGmZoBme1yFc8AgbZ/8bv/wCRoP+aEQhxyGHYUehB+DESwSAN8F0CQqDAKANHlQ7MMgwShgYADAIpA3ziDfAmEgUmIhHGCwBCoNstBUeVKQwiKQMGCAAioNwnlQgMEikDLQQN8ABCoN188keVCwwSKQMioNsN8AB88g3wAAC2IzBtAlD2QEDzQEe1KVBEwAAUQAAzoQwCNzYEMGbAGyLwIhEwMUELRFbE/jc2ARsiDfAAjJMN8Dc2DAwSDfAAAAAAAERJVjAMAg3wtiMoUPJAQPNAR7UXUETAABRAADOhNzICMCLAMDFBQsT/VgT/NzICMCLADfDMUwAAAERJVjAMAg3wAAAAABRA5sQJIDOBACKhDfAAAAAyoQwCDfAA", + "text_start": 1074843648, + "data": "CIH+PwUFBAACAwcAAwMLALnXEEDv1xBAHdgQQLrYEEBo5xBAHtkQQHTZEEDA2RBAaOcQQILaEED/2hBAwNsQQGjnEEBo5xBAWNwQQGjnEEA33xBAAOAQQDvgEEBo5xBAaOcQQNfgEEBo5xBAv+EQQGXiEECj4xBAY+QQQDTlEEBo5xBAaOcQQGjnEEBo5xBAYuYQQGjnEEBX5xBAkN0QQI/YEECm5RBAq9oQQPzZEEBo5xBA7OYQQDHnEEBo5xBAaOcQQGjnEEBo5xBAaOcQQGjnEEBo5xBAaOcQQCLaEEBf2hBAvuUQQAEAAAACAAAAAwAAAAQAAAAFAAAABwAAAAkAAAANAAAAEQAAABkAAAAhAAAAMQAAAEEAAABhAAAAgQAAAMEAAAABAQAAgQEAAAECAAABAwAAAQQAAAEGAAABCAAAAQwAAAEQAAABGAAAASAAAAEwAAABQAAAAWAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAABAAAAAgAAAAIAAAADAAAAAwAAAAQAAAAEAAAABQAAAAUAAAAGAAAABgAAAAcAAAAHAAAACAAAAAgAAAAJAAAACQAAAAoAAAAKAAAACwAAAAsAAAAMAAAADAAAAA0AAAANAAAAAAAAAAAAAAADAAAABAAAAAUAAAAGAAAABwAAAAgAAAAJAAAACgAAAAsAAAANAAAADwAAABEAAAATAAAAFwAAABsAAAAfAAAAIwAAACsAAAAzAAAAOwAAAEMAAABTAAAAYwAAAHMAAACDAAAAowAAAMMAAADjAAAAAgEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAQAAAAEAAAABAAAAAgAAAAIAAAACAAAAAgAAAAMAAAADAAAAAwAAAAMAAAAEAAAABAAAAAQAAAAEAAAABQAAAAUAAAAFAAAABQAAAAAAAAAAAAAAAAAAABAREgAIBwkGCgULBAwDDQIOAQ8AAQEAAAEAAAAEAAAA", + "data_start": 1073720488 +} \ No newline at end of file diff --git a/installer/bin/esptool/esptool/util.py b/installer/bin/esptool/esptool/util.py new file mode 100644 index 0000000..66d9bd4 --- /dev/null +++ b/installer/bin/esptool/esptool/util.py @@ -0,0 +1,174 @@ +# 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 re +import struct +import sys + + +def byte(bitstr, index): + return bitstr[index] + + +def mask_to_shift(mask): + """Return the index of the least significant bit in the mask""" + shift = 0 + while mask & 0x1 == 0: + shift += 1 + mask >>= 1 + return shift + + +def div_roundup(a, b): + """Return a/b rounded up to nearest integer, + equivalent result to int(math.ceil(float(int(a)) / float(int(b))), only + without possible floating point accuracy errors. + """ + return (int(a) + int(b) - 1) // int(b) + + +def flash_size_bytes(size): + """Given a flash size of the type passed in args.flash_size + (ie 512KB or 1MB) then return the size in bytes. + """ + if "MB" in size: + return int(size[: size.index("MB")]) * 1024 * 1024 + elif "KB" in size: + return int(size[: size.index("KB")]) * 1024 + else: + raise FatalError("Unknown size %s" % size) + + +def hexify(s, uppercase=True): + format_str = "%02X" if uppercase else "%02x" + return "".join(format_str % c for c in s) + + +def pad_to(data, alignment, pad_character=b"\xFF"): + """Pad to the next alignment boundary""" + pad_mod = len(data) % alignment + if pad_mod != 0: + data += pad_character * (alignment - pad_mod) + 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 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" + chip_name = re.sub(r"(esp32)(?!$)", r"\1-", chip_name) + # Put "()" around "betaN" + chip_name = re.sub(r"(beta\d*)", r"(\1)", chip_name) + # Uppercase everything before "(betaN)" + chip_name = re.sub(r"^[^\(]+", lambda x: x.group(0).upper(), chip_name) + return chip_name + + +def strip_chip_name(chip_name): + """Strip chip name to normalized form, e.g. `ESP32-S3(beta2)` -> `esp32s3beta2`""" + return re.sub(r"[-()]", "", chip_name.lower()) + + +class FatalError(RuntimeError): + """ + Wrapper class for runtime errors that aren't caused by internal bugs, but by + ESP ROM responses or input content. + """ + + def __init__(self, message): + RuntimeError.__init__(self, message) + + @staticmethod + def WithResult(message, result): + """ + Return a fatal error object that appends the hex values of + 'result' and its meaning as a string formatted argument. + """ + + err_defs = { + # ROM error codes + 0x101: "Out of memory", + 0x102: "Invalid argument", + 0x103: "Invalid state", + 0x104: "Invalid size", + 0x105: "Requested resource not found", + 0x106: "Operation or feature not supported", + 0x107: "Operation timed out", + 0x108: "Received response was invalid", + 0x109: "CRC or checksum was invalid", + 0x10A: "Version was invalid", + 0x10B: "MAC address was invalid", + # Flasher stub error codes + 0xC000: "Bad data length", + 0xC100: "Bad data checksum", + 0xC200: "Bad blocksize", + 0xC300: "Invalid command", + 0xC400: "Failed SPI operation", + 0xC500: "Failed SPI unlock", + 0xC600: "Not in flash mode", + 0xC700: "Inflate error", + 0xC800: "Not enough data", + 0xC900: "Too much data", + 0xFF00: "Command not implemented", + } + + err_code = struct.unpack(">H", result[:2]) + message += " (result was {}: {})".format( + hexify(result), err_defs.get(err_code[0], "Unknown result") + ) + return FatalError(message) + + +class NotImplementedInROMError(FatalError): + """ + Wrapper class for the error thrown when a particular ESP bootloader function + is not implemented in the ROM bootloader. + """ + + def __init__(self, bootloader, func): + FatalError.__init__( + self, + "%s ROM does not support function %s." + % (bootloader.CHIP_NAME, func.__name__), + ) + + +class NotSupportedError(FatalError): + def __init__(self, esp, function_name): + FatalError.__init__( + self, + "Function %s is not supported for %s." % (function_name, esp.CHIP_NAME), + ) + + +class UnsupportedCommandError(RuntimeError): + """ + Wrapper class for when ROM loader returns an invalid command response. + + Usually this indicates the loader is running in Secure Download Mode. + """ + + def __init__(self, esp, op): + if esp.secure_download_mode: + msg = "This command (0x%x) is not supported in Secure Download Mode" % op + else: + msg = "Invalid (unsupported) command 0x%x" % op + RuntimeError.__init__(self, msg) diff --git a/installer/install_upgrade.bat b/installer/install_upgrade.bat new file mode 100644 index 0000000..bc95ac2 --- /dev/null +++ b/installer/install_upgrade.bat @@ -0,0 +1,10 @@ +@echo off +echo Your firmware will be upgraded without factory reset. +echo IF THIS IS YOUR FIRST FLASH ON THIS BOARD PLEASE USE install_with_factory_reset.bat INSTEAD! + +set /p port="Enter COM port (for example COM5): " + +python.exe bin/esptool/esptool.py --chip esp32 --port "%port%" --baud 460800 --before default_reset --after hard_reset write_flash -z --flash_mode dio --flash_freq 40m --flash_size 4MB 0x1000 firmware/bootloader.bin 0x8000 firmware/partitions.bin 0xe000 firmware/boot_app0.bin 0x10000 firmware/firmware.bin + +echo "Firmware flashed" +pause \ No newline at end of file diff --git a/installer/install_upgrade.sh b/installer/install_upgrade.sh new file mode 100644 index 0000000..81a238e --- /dev/null +++ b/installer/install_upgrade.sh @@ -0,0 +1,9 @@ +#!/bin/bash +echo "Your firmware will be upgraded without factory reset." +echo "IF THIS IS YOUR FIRST FLASH ON THIS BOARD PLEASE USE install_with_factory_reset.sh INSTEAD!" + +read -p "Enter COM port (for example /dev/ttyS5): " port + +python3 ./bin/esptool/esptool.py --chip esp32 --port "$port" --baud 460800 --before default_reset --after hard_reset write_flash -z --flash_mode dio --flash_freq 40m --flash_size 4MB 0x1000 firmware/bootloader.bin 0x8000 firmware/partitions.bin 0xe000 firmware/boot_app0.bin 0x10000 firmware/firmware.bin + +echo "Firmware flashed" \ No newline at end of file diff --git a/installer/install_with_factory_reset.bat b/installer/install_with_factory_reset.bat new file mode 100644 index 0000000..03f318c --- /dev/null +++ b/installer/install_with_factory_reset.bat @@ -0,0 +1,14 @@ +@echo off +echo Your current configuration will be LOST!!! +echo Your current configuration will be LOST!!! +echo Your current configuration will be LOST!!! +echo If you already have this board flashed with our firmware please use install_upgrade.bat instead! + +set /p port="Enter COM port (for example COM5): " + +python.exe bin/esptool/esptool.py --chip esp32 --port "%port%" --baud 460800 --before default_reset --after hard_reset write_flash -z --flash_mode dio --flash_freq 40m --flash_size 4MB 2686976 firmware/spiffs.bin + +python.exe bin/esptool/esptool.py --chip esp32 --port "%port%" --baud 460800 --before default_reset --after hard_reset write_flash -z --flash_mode dio --flash_freq 40m --flash_size 4MB 0x1000 firmware/bootloader.bin 0x8000 firmware/partitions.bin 0xe000 firmware/boot_app0.bin 0x10000 firmware/firmware.bin + +echo "Firmware flashed" +pause \ No newline at end of file diff --git a/installer/install_with_factory_reset.sh b/installer/install_with_factory_reset.sh new file mode 100644 index 0000000..e69de29