feat(core): 更新py-esptool (version: 4.8.1)

This commit is contained in:
王立帮
2025-04-07 23:51:29 +08:00
parent 6b4ca0a883
commit 937ecf44f4
120 changed files with 1871 additions and 8899 deletions

View File

@@ -2,7 +2,7 @@
# Espressif Systems (Shanghai) CO LTD, other contributors as noted.
#
# SPDX-License-Identifier: GPL-2.0-or-later
# PYTHON_ARGCOMPLETE_OK
__all__ = [
"chip_id",
"detect_chip",
@@ -28,7 +28,7 @@ __all__ = [
"write_mem",
]
__version__ = "4.7.0"
__version__ = "4.8.1"
import argparse
import inspect
@@ -38,8 +38,8 @@ import sys
import time
import traceback
from bin_image import intel_hex_to_bin
from cmds import (
from esptool.bin_image import intel_hex_to_bin
from esptool.cmds import (
DETECTED_FLASH_SIZES,
chip_id,
detect_chip,
@@ -49,6 +49,7 @@ from cmds import (
erase_flash,
erase_region,
flash_id,
read_flash_sfdp,
get_security_info,
image_info,
load_ram,
@@ -65,15 +66,22 @@ from cmds import (
write_flash_status,
write_mem,
)
from config import load_config_file
from loader import DEFAULT_CONNECT_ATTEMPTS, ESPLoader, list_ports
from targets import CHIP_DEFS, CHIP_LIST, ESP32ROM
from util import (
from esptool.config import load_config_file
from esptool.loader import (
DEFAULT_CONNECT_ATTEMPTS,
DEFAULT_OPEN_PORT_ATTEMPTS,
StubFlasher,
ESPLoader,
list_ports,
)
from esptool.targets import CHIP_DEFS, CHIP_LIST, ESP32ROM
from esptool.util import (
FatalError,
NotImplementedInROMError,
flash_size_bytes,
strip_chip_name,
)
from itertools import chain, cycle, repeat
import serial
@@ -123,6 +131,14 @@ def main(argv=None, esp=None):
default=os.environ.get("ESPTOOL_BAUD", ESPLoader.ESP_ROM_BAUD),
)
parser.add_argument(
"--port-filter",
action="append",
help="Serial port device filter, can be vid=NUMBER, pid=NUMBER, name=SUBSTRING",
type=str,
default=[],
)
parser.add_argument(
"--before",
help="What to do before connecting to the chip",
@@ -145,6 +161,15 @@ def main(argv=None, esp=None):
action="store_true",
)
# --stub-version can be set with --no-stub so the tests wouldn't fail if this option is implied globally
parser.add_argument(
"--stub-version",
default=os.environ.get("ESPTOOL_STUB_VERSION", StubFlasher.STUB_SUBDIRS[0]),
choices=StubFlasher.STUB_SUBDIRS,
# not a public option and is not subject to the semantic versioning policy
help=argparse.SUPPRESS,
)
parser.add_argument(
"--trace",
"-t",
@@ -217,7 +242,12 @@ def main(argv=None, esp=None):
default="0xFFFFFFFF",
)
def add_spi_flash_subparsers(parent, allow_keep, auto_detect):
def add_spi_flash_subparsers(
parent: argparse.ArgumentParser,
allow_keep: bool,
auto_detect: bool,
size_only: bool = False,
):
"""Add common parser arguments for SPI flash properties"""
extra_keep_args = ["keep"] if allow_keep else []
@@ -234,33 +264,35 @@ def main(argv=None, esp=None):
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"),
)
if not size_only:
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",
@@ -468,7 +500,7 @@ def main(argv=None, esp=None):
parser_elf2image.add_argument(
"--use_segments",
help="If set, ELF segments will be used instead of ELF sections "
"to genereate the image.",
"to generate the image.",
action="store_true",
)
parser_elf2image.add_argument(
@@ -489,7 +521,7 @@ def main(argv=None, esp=None):
"quantity. This will make the other segments invisible to the ROM "
"loader. Use this argument with care because the ROM loader will load "
"only the RAM segments although the other segments being present in "
"the output.",
"the output. Implies --dont-append-digest",
action="store_true",
default=None,
)
@@ -540,7 +572,9 @@ def main(argv=None, esp=None):
parser_read_flash = subparsers.add_parser(
"read_flash", help="Read SPI flash content"
)
add_spi_connection_arg(parser_read_flash)
add_spi_flash_subparsers(
parser_read_flash, allow_keep=True, auto_detect=True, size_only=True
)
parser_read_flash.add_argument("address", help="Start address", type=arg_auto_int)
parser_read_flash.add_argument(
"size",
@@ -594,6 +628,14 @@ def main(argv=None, esp=None):
type=arg_auto_size,
)
parser_read_flash_sfdp = subparsers.add_parser(
"read_flash_sfdp",
help="Read SPI flash SFDP (Serial Flash Discoverable Parameters)",
)
add_spi_flash_subparsers(parser_read_flash_sfdp, allow_keep=True, auto_detect=True)
parser_read_flash_sfdp.add_argument("addr", type=arg_auto_int)
parser_read_flash_sfdp.add_argument("bytes", type=int)
parser_merge_bin = subparsers.add_parser(
"merge_bin",
help="Merge multiple raw binary files into a single file for later flashing",
@@ -664,12 +706,40 @@ def main(argv=None, esp=None):
for operation in subparsers.choices.keys():
assert operation in globals(), "%s should be a module function" % operation
# Enable argcomplete only on Unix-like systems
if sys.platform != "win32":
try:
import argcomplete
argcomplete.autocomplete(parser)
except ImportError:
pass
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)
StubFlasher.set_preferred_stub_subdir(args.stub_version)
# Parse filter arguments into separate lists
args.filterVids = []
args.filterPids = []
args.filterNames = []
for f in args.port_filter:
kvp = f.split("=")
if len(kvp) != 2:
raise FatalError("Option --port-filter argument must consist of key=value")
if kvp[0] == "vid":
args.filterVids.append(arg_auto_int(kvp[1]))
elif kvp[0] == "pid":
args.filterPids.append(arg_auto_int(kvp[1]))
elif kvp[0] == "name":
args.filterNames.append(kvp[1])
else:
raise FatalError("Option --port-filter argument key not recognized")
# operation function can take 1 arg (args), 2 args (esp, arg)
# or be a member function of the ESPLoader class.
@@ -705,10 +775,31 @@ def main(argv=None, esp=None):
initial_baud = args.baud
if args.port is None:
ser_list = get_port_list()
ser_list = get_port_list(args.filterVids, args.filterPids, args.filterNames)
print("Found %d serial ports" % len(ser_list))
else:
ser_list = [args.port]
open_port_attempts = os.environ.get(
"ESPTOOL_OPEN_PORT_ATTEMPTS", DEFAULT_OPEN_PORT_ATTEMPTS
)
try:
open_port_attempts = int(open_port_attempts)
except ValueError:
raise SystemExit("Invalid value for ESPTOOL_OPEN_PORT_ATTEMPTS")
if open_port_attempts != 1:
if args.port is None or args.chip == "auto":
print(
"WARNING: The ESPTOOL_OPEN_PORT_ATTEMPTS (open_port_attempts) option can only be used with --port and --chip arguments."
)
else:
esp = esp or connect_loop(
args.port,
initial_baud,
args.chip,
open_port_attempts,
args.trace,
args.before,
)
esp = esp or get_default_connected_device(
ser_list,
port=args.port,
@@ -770,6 +861,13 @@ def main(argv=None, esp=None):
"Keeping initial baud rate %d" % initial_baud
)
def _define_spi_conn(spi_connection):
"""Prepare SPI configuration string and value for flash_spi_attach()"""
clk, q, d, hd, cs = spi_connection
spi_config_txt = f"CLK:{clk}, Q:{q}, D:{d}, HD:{hd}, CS:{cs}"
value = (hd << 24) | (cs << 18) | (d << 12) | (q << 6) | clk
return spi_config_txt, value
# Override the common SPI flash parameter stuff if configured to do so
if hasattr(args, "spi_connection") and args.spi_connection is not None:
spi_config = args.spi_connection
@@ -781,15 +879,26 @@ def main(argv=None, esp=None):
esp.check_spi_connection(args.spi_connection)
# Encode the pin numbers as a 32-bit integer with packed 6-bit values,
# the same way the ESP ROM takes them
clk, q, d, hd, cs = args.spi_connection
spi_config = f"CLK:{clk}, Q:{q}, D:{d}, HD:{hd}, CS:{cs}"
value = (hd << 24) | (cs << 18) | (d << 12) | (q << 6) | clk
spi_config, value = _define_spi_conn(args.spi_connection)
print(f"Configuring SPI flash mode ({spi_config})...")
esp.flash_spi_attach(value)
elif args.no_stub:
print("Enabling default SPI flash mode...")
# ROM loader doesn't enable flash unless we explicitly do it
esp.flash_spi_attach(0)
if esp.CHIP_NAME != "ESP32" or esp.secure_download_mode:
print("Enabling default SPI flash mode...")
# ROM loader doesn't enable flash unless we explicitly do it
esp.flash_spi_attach(0)
else:
# ROM doesn't attach in-package flash chips
spi_chip_pads = esp.get_chip_spi_pads()
spi_config_txt, value = _define_spi_conn(spi_chip_pads)
if spi_chip_pads != (0, 0, 0, 0, 0):
print(
"Attaching flash from eFuses' SPI pads configuration"
f"({spi_config_txt})..."
)
else:
print("Enabling default SPI flash mode...")
esp.flash_spi_attach(value)
# XMC chip startup sequence
XMC_VENDOR_ID = 0x20
@@ -879,6 +988,10 @@ def main(argv=None, esp=None):
flash_size = detect_flash_size(esp, args)
elif args.flash_size == "keep":
flash_size = detect_flash_size(esp, args=None)
if not esp.IS_STUB:
print(
"WARNING: In case of failure, please set a specific --flash_size."
)
else:
flash_size = args.flash_size
@@ -968,14 +1081,29 @@ def arg_auto_chunk_size(string: str) -> int:
return num
def get_port_list():
def get_port_list(vids=[], pids=[], names=[]):
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())
ports = []
for port in list_ports.comports():
if sys.platform == "darwin" and port.device.endswith(
("Bluetooth-Incoming-Port", "wlan-debug")
):
continue
if vids and (port.vid is None or port.vid not in vids):
continue
if pids and (port.pid is None or port.pid not in pids):
continue
if names and (
port.name is None or all(name not in port.name for name in names)
):
continue
ports.append(port.device)
return sorted(ports)
def expand_file_arguments(argv):
@@ -1001,6 +1129,53 @@ def expand_file_arguments(argv):
return argv
def connect_loop(
port: str,
initial_baud: int,
chip: str,
max_retries: int,
trace: bool = False,
before: str = "default_reset",
):
chip_class = CHIP_DEFS[chip]
esp = None
print(f"Serial port {port}")
first = True
ten_cycle = cycle(chain(repeat(False, 9), (True,)))
retry_loop = chain(
repeat(False, max_retries - 1), (True,) if max_retries else cycle((False,))
)
for last, every_tenth in zip(retry_loop, ten_cycle):
try:
esp = chip_class(port, initial_baud, trace)
if not first:
# break the retrying line
print("")
esp.connect(before)
return esp
except (
FatalError,
serial.serialutil.SerialException,
IOError,
OSError,
) as err:
if esp and esp._port:
esp._port.close()
esp = None
if first:
print(err)
print("Retrying failed connection", end="", flush=True)
first = False
if last:
raise err
if every_tenth:
# print a dot every second
print(".", end="", flush=True)
time.sleep(0.1)
def get_default_connected_device(
serial_list,
port,

View File

@@ -11,16 +11,19 @@ import os
import re
import struct
import tempfile
from typing import BinaryIO, Optional
from typing import IO, Optional
from intelhex import IntelHex
from intelhex import HexRecordError, IntelHex
from loader import ESPLoader
from targets import (
from .loader import ESPLoader
from .targets import (
ESP32C2ROM,
ESP32C3ROM,
ESP32C5ROM,
ESP32C5BETA3ROM,
ESP32C6BETAROM,
ESP32C6ROM,
ESP32C61ROM,
ESP32H2BETA1ROM,
ESP32H2BETA2ROM,
ESP32H2ROM,
@@ -31,7 +34,7 @@ from targets import (
ESP32S3ROM,
ESP8266ROM,
)
from util import FatalError, byte, pad_to
from .util import FatalError, byte, pad_to
def align_file_position(f, size):
@@ -40,20 +43,24 @@ def align_file_position(f, size):
f.seek(align, 1)
def intel_hex_to_bin(file: BinaryIO, start_addr: Optional[int] = None) -> BinaryIO:
def intel_hex_to_bin(file: IO[bytes], start_addr: Optional[int] = None) -> IO[bytes]:
"""Convert IntelHex file to temp binary file with padding from start_addr
If hex file was detected return temp bin file object; input file otherwise"""
INTEL_HEX_MAGIC = b":"
magic = file.read(1)
file.seek(0)
if magic == INTEL_HEX_MAGIC:
ih = IntelHex()
ih.loadhex(file.name)
file.close()
bin = tempfile.NamedTemporaryFile(suffix=".bin", delete=False)
ih.tobinfile(bin, start=start_addr)
return bin
else:
try:
if magic == INTEL_HEX_MAGIC:
ih = IntelHex()
ih.loadhex(file.name)
file.close()
bin = tempfile.NamedTemporaryFile(suffix=".bin", delete=False)
ih.tobinfile(bin, start=start_addr)
return bin
else:
return file
except (HexRecordError, UnicodeDecodeError):
# file started with HEX magic but the rest was not according to the standard
return file
@@ -82,6 +89,9 @@ def LoadFirmwareImage(chip, image_file):
"esp32h2beta2": ESP32H2BETA2FirmwareImage,
"esp32c2": ESP32C2FirmwareImage,
"esp32c6": ESP32C6FirmwareImage,
"esp32c61": ESP32C61FirmwareImage,
"esp32c5": ESP32C5FirmwareImage,
"esp32c5beta3": ESP32C5BETA3FirmwareImage,
"esp32h2": ESP32H2FirmwareImage,
"esp32p4": ESP32P4FirmwareImage,
}[chip](f)
@@ -105,10 +115,11 @@ 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):
def __init__(self, addr, data, file_offs=None, flags=0):
self.addr = addr
self.data = data
self.file_offs = file_offs
self.flags = flags
self.include_in_checksum = True
if self.addr != 0:
self.pad_to_alignment(
@@ -157,8 +168,8 @@ 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)
def __init__(self, name, addr, data, flags):
super(ELFSection, self).__init__(addr, data, flags=flags)
self.name = name.decode("utf-8")
def __repr__(self):
@@ -168,6 +179,9 @@ class ELFSection(ImageSegment):
class BaseFirmwareImage(object):
SEG_HEADER_LEN = 8
SHA256_DIGEST_LEN = 32
ELF_FLAG_WRITE = 0x1
ELF_FLAG_READ = 0x2
ELF_FLAG_EXEC = 0x4
""" Base class with common firmware image functions """
@@ -342,6 +356,11 @@ class BaseFirmwareImage(object):
irom_segment = self.get_irom_segment()
return [s for s in self.segments if s != irom_segment]
def sort_segments(self):
if not self.segments:
return # nothing to sort
self.segments = sorted(self.segments, key=lambda s: s.addr)
def merge_adjacent_segments(self):
if not self.segments:
return # nothing to merge
@@ -358,6 +377,8 @@ class BaseFirmwareImage(object):
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),
next_elem.flags & self.ELF_FLAG_EXEC
== elem.flags & self.ELF_FLAG_EXEC,
)
):
# Merge any segment that ends where the next one starts,
@@ -613,6 +634,7 @@ class ESP32FirmwareImage(BaseFirmwareImage):
self.ram_only_header = ram_only_header
self.append_digest = append_digest
self.data_length = None
if load_file is not None:
start = load_file.tell()
@@ -631,6 +653,7 @@ class ESP32FirmwareImage(BaseFirmwareImage):
calc_digest = hashlib.sha256()
calc_digest.update(load_file.read(end - start))
self.calc_digest = calc_digest.digest() # TODO: decide what to do here?
self.data_length = end - start
self.verify()
@@ -683,7 +706,10 @@ class ESP32FirmwareImage(BaseFirmwareImage):
# So bootdesc will be at the very top of the binary at 0x20 offset
# (in the first segment).
for segment in ram_segments:
if segment.name == ".dram0.bootdesc":
if (
isinstance(segment, ELFSection)
and segment.name == ".dram0.bootdesc"
):
ram_segments.remove(segment)
ram_segments.insert(0, segment)
break
@@ -736,15 +762,25 @@ class ESP32FirmwareImage(BaseFirmwareImage):
flash_segments.reverse()
for segment in flash_segments:
pad_len = get_alignment_data_needed(segment)
while pad_len > 0:
pad_segment = ImageSegment(0, b"\x00" * pad_len, f.tell())
self.save_segment(f, pad_segment)
total_segments += 1
pad_len = get_alignment_data_needed(segment)
# write the flash segment
assert (
f.tell() + 8
) % self.IROM_ALIGN == segment.addr % self.IROM_ALIGN
# Some chips have a non-zero load offset (eg. 0x1000)
# therefore we shift the ROM segments "-load_offset"
# so it will be aligned properly after it is flashed
align_min = (
self.ROM_LOADER.BOOTLOADER_FLASH_OFFSET - self.SEG_HEADER_LEN
)
if pad_len < align_min:
# in case pad_len does not fit minimum alignment,
# pad it to next aligned boundary
pad_len += self.IROM_ALIGN
pad_len -= self.ROM_LOADER.BOOTLOADER_FLASH_OFFSET
pad_segment = ImageSegment(0, b"\x00" * pad_len, f.tell())
self.save_segment(f, pad_segment)
total_segments += 1
# check the alignment
assert (f.tell() + 8 + self.ROM_LOADER.BOOTLOADER_FLASH_OFFSET) % (
self.IROM_ALIGN
) == segment.addr % self.IROM_ALIGN
# save the flash segment but not saving its checksum neither
# saving the number of flash segments, since ROM bootloader
# should "not see" them
@@ -952,7 +988,7 @@ class ESP8266V3FirmwareImage(ESP32FirmwareImage):
while len(flash_segments) > 0:
segment = flash_segments[0]
# remove 8 bytes empty data for insert segment header
if segment.name == ".flash.rodata":
if isinstance(segment, ELFSection) and segment.name == ".flash.rodata":
segment.data = segment.data[8:]
# write the flash segment
checksum = self.save_segment(f, segment, checksum)
@@ -1114,6 +1150,33 @@ class ESP32C6FirmwareImage(ESP32FirmwareImage):
ESP32C6ROM.BOOTLOADER_IMAGE = ESP32C6FirmwareImage
class ESP32C61FirmwareImage(ESP32C6FirmwareImage):
"""ESP32C61 Firmware Image almost exactly the same as ESP32C6FirmwareImage"""
ROM_LOADER = ESP32C61ROM
ESP32C61ROM.BOOTLOADER_IMAGE = ESP32C61FirmwareImage
class ESP32C5FirmwareImage(ESP32C6FirmwareImage):
"""ESP32C5 Firmware Image almost exactly the same as ESP32C6FirmwareImage"""
ROM_LOADER = ESP32C5ROM
ESP32C5ROM.BOOTLOADER_IMAGE = ESP32C5FirmwareImage
class ESP32C5BETA3FirmwareImage(ESP32C6FirmwareImage):
"""ESP32C5BETA3 Firmware Image almost exactly the same as ESP32C6FirmwareImage"""
ROM_LOADER = ESP32C5BETA3ROM
ESP32C5BETA3ROM.BOOTLOADER_IMAGE = ESP32C5BETA3FirmwareImage
class ESP32P4FirmwareImage(ESP32FirmwareImage):
"""ESP32P4 Firmware Image almost exactly the same as ESP32FirmwareImage"""
@@ -1222,16 +1285,16 @@ class ELFFile(object):
name_offs, sec_type, _flags, lma, sec_offs, size = struct.unpack_from(
"<LLLLLL", section_header[offs:]
)
return (name_offs, sec_type, lma, size, sec_offs)
return (name_offs, sec_type, lma, size, sec_offs, _flags)
all_sections = [read_section_header(offs) for offs in section_header_offsets]
prog_sections = [s for s in all_sections if s[1] in ELFFile.PROG_SEC_TYPES]
nobits_secitons = [s for s in all_sections if s[1] == ELFFile.SEC_TYPE_NOBITS]
# search for the string table section
if not (shstrndx * self.LEN_SEC_HEADER) in section_header_offsets:
if (shstrndx * self.LEN_SEC_HEADER) not in section_header_offsets:
raise FatalError("ELF file has no STRTAB section at shstrndx %d" % shstrndx)
_, sec_type, _, sec_size, sec_offs = read_section_header(
_, sec_type, _, sec_size, sec_offs, _ = read_section_header(
shstrndx * self.LEN_SEC_HEADER
)
if sec_type != ELFFile.SEC_TYPE_STRTAB:
@@ -1253,14 +1316,14 @@ class ELFFile(object):
return f.read(size)
prog_sections = [
ELFSection(lookup_string(n_offs), lma, read_data(offs, size))
for (n_offs, _type, lma, size, offs) in prog_sections
ELFSection(lookup_string(n_offs), lma, read_data(offs, size), flags=_flags)
for (n_offs, _type, lma, size, offs, _flags) in prog_sections
if lma != 0 and size > 0
]
self.sections = prog_sections
self.nobits_sections = [
ELFSection(lookup_string(n_offs), lma, b"")
for (n_offs, _type, lma, size, offs) in nobits_secitons
ELFSection(lookup_string(n_offs), lma, b"", flags=_flags)
for (n_offs, _type, lma, size, offs, _flags) in nobits_secitons
if lma != 0 and size > 0
]
@@ -1293,7 +1356,7 @@ class ELFFile(object):
_flags,
_align,
) = struct.unpack_from("<LLLLLLLL", segment_header[offs:])
return (seg_type, lma, size, seg_offs)
return (seg_type, lma, size, seg_offs, _flags)
all_segments = [read_segment_header(offs) for offs in segment_header_offsets]
prog_segments = [s for s in all_segments if s[0] == ELFFile.SEG_TYPE_LOAD]
@@ -1303,8 +1366,8 @@ class ELFFile(object):
return f.read(size)
prog_segments = [
ELFSection(b"PHDR", lma, read_data(offs, size))
for (_type, lma, size, offs) in prog_segments
ELFSection(b"PHDR", lma, read_data(offs, size), flags=_flags)
for (_type, lma, size, offs, _flags) in prog_segments
if lma != 0 and size > 0
]
self.segments = prog_segments

View File

@@ -10,31 +10,33 @@ import struct
import sys
import time
import zlib
import itertools
from intelhex import IntelHex
from serial import SerialException
from bin_image import ELFFile, ImageSegment, LoadFirmwareImage
from bin_image import (
from .bin_image import ELFFile, ImageSegment, LoadFirmwareImage
from .bin_image import (
ESP8266ROMFirmwareImage,
ESP8266V2FirmwareImage,
ESP8266V3FirmwareImage,
)
from loader import (
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 uf2_writer import UF2Writer
from util import (
from .targets import CHIP_DEFS, CHIP_LIST, ROM_LIST
from .uf2_writer import UF2Writer
from .util import (
FatalError,
NotImplementedInROMError,
NotSupportedError,
UnsupportedCommandError,
)
from util import (
from .util import (
div_roundup,
flash_size_bytes,
get_file_size,
@@ -115,7 +117,7 @@ def detect_chip(
else:
err_msg = f"Unexpected chip ID value {chip_id}."
except (UnsupportedCommandError, struct.error, FatalError) as e:
# UnsupportedCommmanddError: ESP8266/ESP32 ROM
# UnsupportedCommandError: ESP8266/ESP32 ROM
# struct.error: ESP32-S2
# FatalError: ESP8266/ESP32 STUB
print(" Unsupported detection protocol, switching and trying again...")
@@ -229,7 +231,7 @@ def detect_flash_size(esp, args=None):
if flash_size is None:
flash_size = "4MB"
print(
"Warning: Could not auto-detect Flash size "
"WARNING: Could not auto-detect Flash size "
f"(FlashID={flash_id:#x}, SizeID={size_id:#x}), defaulting to 4MB"
)
else:
@@ -276,49 +278,60 @@ def _update_image_flash_params(esp, address, args, 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
)
)
# a SHA256 checksum. In that case we recalculate the SHA digest after modifying the header.
sha_appended = args.chip != "esp8266" and image[8 + 15] == 1
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_mode = FLASH_MODES[args.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_freq = esp.parse_flash_freq_arg(args.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_size = esp.parse_flash_size_arg(args.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:]
# recalculate the SHA digest if it was appended
if sha_appended:
# Since the changes are only made for images located in the bootloader offset,
# we can assume that the image is always a bootloader image.
# For merged binaries, we check the bootloader SHA when parameters are changed.
image_object = esp.BOOTLOADER_IMAGE(io.BytesIO(image))
# get the image header, extended header (if present) and data
image_data_before_sha = image[: image_object.data_length]
# get the image data after the SHA digest (primary for merged binaries)
image_data_after_sha = image[
(image_object.data_length + image_object.SHA256_DIGEST_LEN) :
]
sha_digest_calculated = hashlib.sha256(image_data_before_sha).digest()
image = bytes(
itertools.chain(
image_data_before_sha, sha_digest_calculated, image_data_after_sha
)
)
# get the SHA digest newly stored in the image and compare it to the calculated one
image_stored_sha = image[
image_object.data_length : image_object.data_length
+ image_object.SHA256_DIGEST_LEN
]
if hexify(sha_digest_calculated) == hexify(image_stored_sha):
print("SHA digest in image updated")
else:
print(
"WARNING: SHA recalculation for binary failed!\n"
f"\tExpected calculated SHA: {hexify(sha_digest_calculated)}\n"
f"\tSHA stored in binary: {hexify(image_stored_sha)}"
)
return image
@@ -465,19 +478,32 @@ def write_flash(esp, args):
"Use --force to override the warning."
)
# verify file sizes fit in flash
flash_end = flash_size_bytes(
detect_flash_size(esp) if args.flash_size == "keep" else args.flash_size
set_flash_size = (
flash_size_bytes(args.flash_size)
if args.flash_size not in ["detect", "keep"]
else None
)
if flash_end is not None: # Not in secure download mode
if esp.secure_download_mode:
flash_end = set_flash_size
else: # Check against real flash chip size if not in SDM
flash_end_str = detect_flash_size(esp)
flash_end = flash_size_bytes(flash_end_str)
if set_flash_size and set_flash_size > flash_end:
print(
f"WARNING: Set --flash_size {args.flash_size} "
f"is larger than the available flash size of {flash_end_str}."
)
# Verify file sizes fit in the set --flash_size, or real flash size if smaller
flash_end = min(set_flash_size, flash_end) if set_flash_size else flash_end
if flash_end is not None:
for address, argfile in args.addr_filename:
argfile.seek(0, os.SEEK_END)
if address + argfile.tell() > flash_end:
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)
f"File {argfile.name} (length {argfile.tell()}) at offset "
f"{address} will not fit in {flash_end} bytes of flash. "
"Change the --flash_size argument, or flashing address."
)
argfile.seek(0)
@@ -547,70 +573,118 @@ def write_flash(esp, args):
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
)
image = argfile.read()
if len(image) == 0:
print("WARNING: File %s is empty" % argfile.name)
continue
image = _update_image_flash_params(esp, address, args, image)
image = pad_to(image, esp.FLASH_ENCRYPTED_WRITE_ALIGN if encrypted else 4)
if args.no_stub:
print("Erasing flash...")
# It is not possible to write to not aligned addresses without stub,
# so there are added 0xFF (erase) bytes at the beginning of the image
# to align it.
bytes_over = address % esp.FLASH_SECTOR_SIZE
address -= bytes_over
image = b"\xFF" * bytes_over + image
if not esp.secure_download_mode and not esp.get_secure_boot_enabled():
image = _update_image_flash_params(esp, address, args, image)
else:
print(
"WARNING: Security features enabled, so not changing any flash settings."
)
calcmd5 = hashlib.md5(image).hexdigest()
uncsize = len(image)
if compress:
uncimage = image
image = zlib.compress(uncimage, 9)
# 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)
original_image = image # Save the whole image in case retry is needed
# Try again if reconnect was successful
for attempt in range(1, esp.WRITE_FLASH_ATTEMPTS + 1):
try:
if compress:
# Decompress the compressed binary a block at a time,
# to dynamically calculate the timeout based on the real write size
decompress = zlib.decompressobj()
blocks = esp.flash_defl_begin(uncsize, len(image), address)
else:
esp.flash_block(block, seq)
bytes_written += len(block)
bytes_sent += len(block)
image = image[esp.FLASH_WRITE_SIZE :]
seq += 1
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
break
except SerialException:
if attempt == esp.WRITE_FLASH_ATTEMPTS or encrypted:
# Already retried once or encrypted mode is disabled because of security reasons
raise
print("\nLost connection, retrying...")
esp._port.close()
print("Waiting for the chip to reconnect", end="")
for _ in range(DEFAULT_CONNECT_ATTEMPTS):
try:
time.sleep(1)
esp._port.open()
print() # Print new line which was suppressed by print(".")
esp.connect()
if esp.IS_STUB:
# Hack to bypass the stub overwrite check
esp.IS_STUB = False
# Reflash stub because chip was reset
esp = esp.run_stub()
image = original_image
break
except SerialException:
print(".", end="")
sys.stdout.flush()
else:
raise # Reconnect limit reached
if esp.IS_STUB:
# Stub only writes each block to flash after 'ack'ing the receive,
@@ -645,7 +719,7 @@ def write_flash(esp, args):
print("Flash md5: %s" % res)
print(
"MD5 of 0xFF is %s"
% (hashlib.md5(b"\xFF" * uncsize).hexdigest())
% (hashlib.md5(b"\xff" * uncsize).hexdigest())
)
raise FatalError("MD5 of file does not match data in flash!")
else:
@@ -793,7 +867,7 @@ def image_info(args):
format_str = "{:7} {:#07x} {:#010x} {:#010x} {}"
app_desc = None
bootloader_desc = None
for idx, seg in enumerate(image.segments, start=1):
for idx, seg in enumerate(image.segments):
segs = seg.get_memory_type(image)
seg_name = ", ".join(segs)
if "DROM" in segs: # The DROM segment starts with the esp_app_desc_t struct
@@ -815,9 +889,11 @@ def image_info(args):
print(
"Checksum: {:#02x} ({})".format(
image.checksum,
"valid"
if image.checksum == calc_checksum
else "invalid - calculated {:02x}".format(calc_checksum),
(
"valid"
if image.checksum == calc_checksum
else "invalid - calculated {:02x}".format(calc_checksum)
),
)
)
try:
@@ -892,8 +968,7 @@ def image_info(args):
ESP8266V2FirmwareImage.IMAGE_V2_MAGIC,
]:
raise FatalError(
"This is not a valid image "
"(invalid magic number: {:#x})".format(magic)
f"This is not a valid image (invalid magic number: {magic:#x})"
)
if args.chip == "auto":
@@ -940,9 +1015,11 @@ def image_info(args):
print(
"Checksum: {:02x} ({})".format(
image.checksum,
"valid"
if image.checksum == calc_checksum
else "invalid - calculated {:02x}".format(calc_checksum),
(
"valid"
if image.checksum == calc_checksum
else "invalid - calculated {:02x}".format(calc_checksum)
),
)
)
try:
@@ -982,8 +1059,6 @@ def elf2image(args):
args.chip = "esp8266"
print("Creating {} image...".format(args.chip))
if args.ram_only_header:
print("ROM segments hidden - only RAM segments are visible to the ROM loader!")
if args.chip != "esp8266":
image = CHIP_DEFS[args.chip].BOOTLOADER_IMAGE()
@@ -995,7 +1070,10 @@ def elf2image(args):
image.min_rev_full = args.min_rev_full
image.max_rev_full = args.max_rev_full
image.ram_only_header = args.ram_only_header
image.append_digest = args.append_digest
if image.ram_only_header:
image.append_digest = False
else:
image.append_digest = args.append_digest
elif args.version == "1": # ESP8266
image = ESP8266ROMFirmwareImage()
elif args.version == "2":
@@ -1019,6 +1097,13 @@ def elf2image(args):
image.elf_sha256 = e.sha256()
image.elf_sha256_offset = args.elf_sha256_offset
if args.ram_only_header:
print(
"Image has only RAM segments visible. "
"ROM segments are hidden and SHA256 digest is not appended."
)
image.sort_segments()
before = len(image.segments)
image.merge_adjacent_segments()
if len(image.segments) != before:
@@ -1090,7 +1175,7 @@ def run(esp, args):
esp.run()
def flash_id(esp, args):
def detect_flash_id(esp):
flash_id = esp.flash_id()
print("Manufacturer: %02x" % (flash_id & 0xFF))
flid_lowbyte = (flash_id >> 16) & 0xFF
@@ -1098,11 +1183,27 @@ def flash_id(esp, args):
print(
"Detected flash size: %s" % (DETECTED_FLASH_SIZES.get(flid_lowbyte, "Unknown"))
)
def flash_id(esp, args):
detect_flash_id(esp)
flash_type = esp.flash_type()
flash_type_dict = {0: "quad (4 data lines)", 1: "octal (8 data lines)"}
flash_type_str = flash_type_dict.get(flash_type)
if flash_type_str:
print(f"Flash type set in eFuse: {flash_type_str}")
esp.get_flash_voltage()
def read_flash_sfdp(esp, args):
detect_flash_id(esp)
sfdp = esp.read_spiflash_sfdp(args.addr, args.bytes * 8)
print(f"SFDP[{args.addr}..{args.addr+args.bytes-1}]: ", end="")
for i in range(args.bytes):
print(f"{sfdp&0xff:02X} ", end="")
sfdp = sfdp >> 8
print()
def read_flash(esp, args):
@@ -1218,7 +1319,16 @@ def get_security_info(esp, args):
print(title)
print("=" * len(title))
print("Flags: {:#010x} ({})".format(si["flags"], bin(si["flags"])))
print("Key Purposes: {}".format(si["key_purposes"]))
if esp.KEY_PURPOSES:
print(f"Key Purposes: {si['key_purposes']}")
desc = "\n ".join(
[
f"BLOCK_KEY{key_num} - {esp.KEY_PURPOSES.get(purpose, 'UNKNOWN')}"
for key_num, purpose in enumerate(si["key_purposes"])
if key_num <= esp.EFUSE_MAX_KEY
]
)
print(f" {desc}")
if si["chip_id"] is not None and si["api_version"] is not None:
print("Chip ID: {}".format(si["chip_id"]))
print("API Version: {}".format(si["api_version"]))
@@ -1270,7 +1380,7 @@ def get_security_info(esp, args):
hard_dis_jtag = get_security_flag_status("HARD_DIS_JTAG", flags)
soft_dis_jtag = get_security_flag_status("SOFT_DIS_JTAG", flags)
if hard_dis_jtag:
print("JTAG: Permenantly Disabled")
print("JTAG: Permanently Disabled")
elif soft_dis_jtag:
print("JTAG: Software Access Disabled")
if get_security_flag_status("DIS_USB", flags):
@@ -1323,7 +1433,7 @@ def merge_bin(args):
def pad_to(flash_offs):
# account for output file offset if there is any
of.write(b"\xFF" * (flash_offs - args.target_offset - of.tell()))
of.write(b"\xff" * (flash_offs - args.target_offset - of.tell()))
for addr, argfile in input_files:
pad_to(addr)
@@ -1338,6 +1448,13 @@ def merge_bin(args):
)
elif args.format == "hex":
out = IntelHex()
if len(input_files) == 1:
print(
"WARNING: Only one input file specified, output may include "
"additional padding if input file was previously merged. "
"Please refer to the documentation for more information: "
"https://docs.espressif.com/projects/esptool/en/latest/esptool/basic-commands.html#hex-output-format" # noqa E501
)
for addr, argfile in input_files:
ihex = IntelHex()
image = argfile.read()

View File

@@ -19,6 +19,7 @@ CONFIG_OPTIONS = [
"connect_attempts",
"write_block_attempts",
"reset_delay",
"open_port_attempts",
"custom_reset_sequence",
]

File diff suppressed because it is too large Load Diff

View File

@@ -1,39 +0,0 @@
# Copyright (c) 2016-2018, Alexander Belchenko
# All rights reserved.
#
# Redistribution and use in source and binary forms,
# with or without modification, are permitted provided
# that the following conditions are met:
#
# * Redistributions of source code must retain
# the above copyright notice, this list of conditions
# and the following disclaimer.
# * Redistributions in binary form must reproduce
# the above copyright notice, this list of conditions
# and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
# * Neither the name of the author nor the names
# of its contributors may be used to endorse
# or promote products derived from this software
# without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
# BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
# AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
# IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
# OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
# OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
# EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
if __name__ == '__main__':
print("Welcome to IntelHex Python library.")
print()
print("The intelhex package has some executable points:")
print(" python -m intelhex.test -- easy way to run unit tests.")
print(" python -m intelhex.bench -- run benchmarks.")

View File

@@ -1,3 +0,0 @@
# IntelHex library version information
version_info = (2, 3, 0)
version_str = '.'.join([str(i) for i in version_info])

View File

@@ -1,360 +0,0 @@
#!/usr/bin/python
# (c) Alexander Belchenko, 2007, 2009
# [2013/08] NOTE: This file is keeping for historical reasons.
# It may or may not work actually with current version of intelhex,
# and most likely it requires some fixes here and there.
"""Benchmarking.
Run each test 3 times and get median value.
Using 10K array as base test time.
Each other test compared with base with next formula::
Tc * Nb
q = ---------
Tb * Nc
Here:
* Tc - execution time of current test
* Tb - execution time of base
* Nb - array size of base (10K)
* Nc - array size of current test
If resulting value is ``q <= 1.0`` it's the best possible result,
i.e. time increase proportionally to array size.
"""
import gc
import sys
import time
import intelhex
from intelhex.compat import StringIO, range_g
def median(values):
"""Return median value for the list of values.
@param values: list of values for processing.
@return: median value.
"""
values.sort()
n = int(len(values) / 2)
return values[n]
def run_test(func, fobj):
"""Run func with argument fobj and measure execution time.
@param func: function for test
@param fobj: data for test
@return: execution time
"""
gc.disable()
try:
begin = time.time()
func(fobj)
end = time.time()
finally:
gc.enable()
return end - begin
def run_readtest_N_times(func, hexstr, n):
"""Run each test N times.
@param func: function for test
@param hexstr: string with content of hex file to read
@param n: times to repeat.
@return: (median time, times list)
"""
assert n > 0
times = []
for i in range_g(n):
sio = StringIO(hexstr)
times.append(run_test(func, sio))
sio.close()
t = median(times)
return t, times
def run_writetest_N_times(func, n):
"""Run each test N times.
@param func: function for test
@param n: times to repeat.
@return: (median time, times list)
"""
assert n > 0
times = []
for i in range_g(n):
sio = StringIO()
times.append(run_test(func, sio))
sio.close()
t = median(times)
return t, times
def time_coef(tc, nc, tb, nb):
"""Return time coefficient relative to base numbers.
@param tc: current test time
@param nc: current test data size
@param tb: base test time
@param nb: base test data size
@return: time coef.
"""
tc = float(tc)
nc = float(nc)
tb = float(tb)
nb = float(nb)
q = (tc * nb) / (tb * nc)
return q
def get_test_data(n1, offset, n2):
"""Create test data on given pattern.
@param n1: size of first part of array at base address 0.
@param offset: offset for second part of array.
@param n2: size of second part of array at given offset.
@return: (overall size, hex file, IntelHex object)
"""
# make IntelHex object
ih = intelhex.IntelHex()
addr = 0
for i in range_g(n1):
ih[addr] = addr % 256
addr += 1
addr += offset
for i in range_g(n2):
ih[addr] = addr % 256
addr += 1
# make hex file
sio = StringIO()
ih.write_hex_file(sio)
hexstr = sio.getvalue()
sio.close()
#
return n1+n2, hexstr, ih
def get_base_50K():
return get_test_data(50000, 0, 0)
def get_250K():
return get_test_data(250000, 0, 0)
def get_100K_100K():
return get_test_data(100000, 1000000, 100000)
def get_0_100K():
return get_test_data(0, 1000000, 100000)
def get_1M():
return get_test_data(1000000, 0, 0)
class Measure(object):
"""Measure execution time helper."""
data_set = [
# (data name, getter)
('base 50K', get_base_50K), # first should be base numbers
('250K', get_250K),
('1M', get_1M),
('100K+100K', get_100K_100K),
('0+100K', get_0_100K),
]
def __init__(self, n=3, read=True, write=True):
self.n = n
self.read = read
self.write = write
self.results = []
def measure_one(self, data):
"""Do measuring of read and write operations.
@param data: 3-tuple from get_test_data
@return: (time readhex, time writehex)
"""
_unused, hexstr, ih = data
tread, twrite = 0.0, 0.0
if self.read:
tread = run_readtest_N_times(intelhex.IntelHex, hexstr, self.n)[0]
if self.write:
twrite = run_writetest_N_times(ih.write_hex_file, self.n)[0]
return tread, twrite
def measure_all(self):
for name, getter in self.data_set:
data = getter()
times = self.measure_one(data)
self.results.append((name, times, data[0]))
def print_report(self, to_file=None):
if to_file is None:
to_file = sys.stdout
base_title, base_times, base_n = self.results[0]
base_read, base_write = base_times
read_report = ['%-10s\t%7.3f' % (base_title, base_read)]
write_report = ['%-10s\t%7.3f' % (base_title, base_write)]
for item in self.results[1:]:
cur_title, cur_times, cur_n = item
cur_read, cur_write = cur_times
if self.read:
qread = time_coef(cur_read, cur_n,
base_read, base_n)
read_report.append('%-10s\t%7.3f\t%7.3f' % (cur_title,
cur_read,
qread))
if self.write:
qwrite = time_coef(cur_write, cur_n,
base_write, base_n)
write_report.append('%-10s\t%7.3f\t%7.3f' % (cur_title,
cur_write,
qwrite))
if self.read:
to_file.write('Read operation:\n')
to_file.write('\n'.join(read_report))
to_file.write('\n\n')
if self.write:
to_file.write('Write operation:\n')
to_file.write('\n'.join(write_report))
to_file.write('\n\n')
HELP = """\
Usage: python _bench.py [OPTIONS]
Options:
-h this help
-n N repeat tests N times
-r run only tests for read operation
-w run only tests for write operation
If option -r or -w is not specified then all tests will be run.
"""
def main(argv=None):
"""Main function to run benchmarks.
@param argv: command-line arguments.
@return: exit code (0 is OK).
"""
import getopt
# default values
test_read = None
test_write = None
n = 3 # number of repeat
if argv is None:
argv = sys.argv[1:]
try:
opts, args = getopt.getopt(argv, 'hn:rw', [])
for o,a in opts:
if o == '-h':
print(HELP)
return 0
elif o == '-n':
n = int(a)
elif o == '-r':
test_read = True
elif o == '-w':
test_write = True
if args:
raise getopt.GetoptError('Arguments are not used.')
except getopt.GetoptError:
msg = sys.exc_info()[1] # current exception
txt = str(msg)
print(txt)
return 1
if (test_read, test_write) == (None, None):
test_read = test_write = True
m = Measure(n, test_read, test_write)
m.measure_all()
m.print_report()
return 0
if __name__ == '__main__':
sys.exit(main(sys.argv[1:]))
"""
Some Results
************
21/04/2007 revno.40
Python 2.5 @ Windows XP, Intel Celeron M CPU 430 @ 1.73GHz
Read operation:
base 10K 0.031
100K 0.360 1.161
1M 3.500 1.129
100K+100K 0.719 1.160
0+100K 0.360 1.161
Write operation:
base 10K 0.031
100K 0.297 0.958
1M 2.953 0.953
100K+100K 1.328 2.142
0+100K 0.312 1.006
21/04/2007 revno.46
Python 2.5 @ Windows XP, Intel Celeron M CPU 430 @ 1.73GHz
Read operation:
base 10K 0.016
100K 0.203 1.269
1M 2.000 1.250
100K+100K 0.422 1.319
0+100K 0.203 1.269
Write operation:
base 10K 0.031
100K 0.297 0.958
1M 2.969 0.958
100K+100K 1.328 2.142
0+100K 0.312 1.006
22/04/2007 revno.48
Python 2.5 @ Windows XP, Intel Celeron M CPU 430 @ 1.73GHz
Read operation:
base 10K 0.016
100K 0.187 1.169
1M 1.891 1.182
100K+100K 0.406 1.269
0+100K 0.188 1.175
Write operation:
base 10K 0.031
100K 0.296 0.955
1M 2.969 0.958
100K+100K 1.328 2.142
0+100K 0.312 1.006
19/08/2008 revno.72
Python 2.5.2 @ Windows XP, Intel Celeron M CPU 430 @ 1.73GHz
Read operation:
base 10K 0.016
100K 0.171 1.069
1M 1.734 1.084
100K+100K 0.375 1.172
0+100K 0.172 1.075
Write operation:
base 10K 0.016
100K 0.156 0.975
1M 1.532 0.957
100K+100K 0.344 1.075
0+100K 0.156 0.975
"""

View File

@@ -1,160 +0,0 @@
# Copyright (c) 2011, Bernhard Leiner
# Copyright (c) 2013-2018 Alexander Belchenko
# All rights reserved.
#
# Redistribution and use in source and binary forms,
# with or without modification, are permitted provided
# that the following conditions are met:
#
# * Redistributions of source code must retain
# the above copyright notice, this list of conditions
# and the following disclaimer.
# * Redistributions in binary form must reproduce
# the above copyright notice, this list of conditions
# and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
# * Neither the name of the author nor the names
# of its contributors may be used to endorse
# or promote products derived from this software
# without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
# BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
# AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
# IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
# OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
# OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
# EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
'''Compatibility functions for python 2 and 3.
@author Bernhard Leiner (bleiner AT gmail com)
@author Alexander Belchenko (alexander belchenko AT gmail com)
'''
__docformat__ = "javadoc"
import sys, array
if sys.version_info[0] >= 3:
# Python 3
Python = 3
def asbytes(s):
if isinstance(s, bytes):
return s
return s.encode('latin1')
def asstr(s):
if isinstance(s, str):
return s
return s.decode('latin1')
# for python >= 3.2 use 'tobytes', otherwise 'tostring'
array_tobytes = array.array.tobytes if sys.version_info[1] >= 2 else array.array.tostring
IntTypes = (int,)
StrType = str
UnicodeType = str
range_g = range # range generator
def range_l(*args): # range list
return list(range(*args))
def dict_keys(dikt): # dict keys list
return list(dikt.keys())
def dict_keys_g(dikt): # dict keys generator
return dikt.keys()
def dict_items_g(dikt): # dict items generator
return dikt.items()
from io import StringIO, BytesIO
def get_binary_stdout():
return sys.stdout.buffer
def get_binary_stdin():
return sys.stdin.buffer
else:
# Python 2
Python = 2
asbytes = str
asstr = str
array_tobytes = array.array.tostring
IntTypes = (int, long)
StrType = basestring
UnicodeType = unicode
#range_g = xrange # range generator
def range_g(*args):
# we want to use xrange here but on python 2 it does not work with long ints
try:
return xrange(*args)
except OverflowError:
start = 0
stop = 0
step = 1
n = len(args)
if n == 1:
stop = args[0]
elif n == 2:
start, stop = args
elif n == 3:
start, stop, step = args
else:
raise TypeError('wrong number of arguments in range_g call!')
if step == 0:
raise ValueError('step cannot be zero')
if step > 0:
def up(start, stop, step):
while start < stop:
yield start
start += step
return up(start, stop, step)
else:
def down(start, stop, step):
while start > stop:
yield start
start += step
return down(start, stop, step)
range_l = range # range list
def dict_keys(dikt): # dict keys list
return dikt.keys()
def dict_keys_g(dikt): # dict keys generator
return dikt.keys()
def dict_items_g(dikt): # dict items generator
return dikt.items()
from cStringIO import StringIO
BytesIO = StringIO
import os
def _force_stream_binary(stream):
"""Force binary mode for stream on Windows."""
if os.name == 'nt':
f_fileno = getattr(stream, 'fileno', None)
if f_fileno:
fileno = f_fileno()
if fileno >= 0:
import msvcrt
msvcrt.setmode(fileno, os.O_BINARY)
return stream
def get_binary_stdout():
return _force_stream_binary(sys.stdout)
def get_binary_stdin():
return _force_stream_binary(sys.stdin)

View File

@@ -1,64 +0,0 @@
# Recursive version sys.getsizeof(). Extendable with custom handlers.
# Code from http://code.activestate.com/recipes/577504/
# Created by Raymond Hettinger on Fri, 17 Dec 2010 (MIT)
import sys
from itertools import chain
from collections import deque
try:
from reprlib import repr
except ImportError:
pass
def total_size(o, handlers={}, verbose=False):
""" Returns the approximate memory footprint an object and all of its contents.
Automatically finds the contents of the following builtin containers and
their subclasses: tuple, list, deque, dict, set and frozenset.
To search other containers, add handlers to iterate over their contents:
handlers = {SomeContainerClass: iter,
OtherContainerClass: OtherContainerClass.get_elements}
"""
dict_handler = lambda d: chain.from_iterable(d.items())
all_handlers = {tuple: iter,
list: iter,
deque: iter,
dict: dict_handler,
set: iter,
frozenset: iter,
}
all_handlers.update(handlers) # user handlers take precedence
seen = set() # track which object id's have already been seen
default_size = sys.getsizeof(0) # estimate sizeof object without __sizeof__
def sizeof(o):
if id(o) in seen: # do not double count the same object
return 0
seen.add(id(o))
s = sys.getsizeof(o, default_size)
if verbose:
print(s, type(o), repr(o))#, file=stderr)
for typ, handler in all_handlers.items():
if isinstance(o, typ):
s += sum(map(sizeof, handler(o)))
break
return s
return sizeof(o)
##### Example call #####
if __name__ == '__main__':
#d = dict(a=1, b=2, c=3, d=[4,5,6,7], e='a string of chars')
print("dict 3 elements")
d = {0:0xFF, 1:0xEE, 2:0xCC}
print(total_size(d, verbose=True))
#print("array 3 elements")
#import array
#print(total_size(array.array('B', b'\x01\x02\x03')))

File diff suppressed because it is too large Load Diff

View File

@@ -13,9 +13,11 @@ import string
import struct
import sys
import time
from typing import Optional
from config import load_config_file
from reset import (
from .config import load_config_file
from .reset import (
ClassicReset,
CustomReset,
DEFAULT_RESET_DELAY,
@@ -23,8 +25,8 @@ from reset import (
USBJTAGSerialReset,
UnixTightReset,
)
from util import FatalError, NotImplementedInROMError, UnsupportedCommandError
from util import byte, hexify, mask_to_shift, pad_to, strip_chip_name
from .util import FatalError, NotImplementedInROMError, UnsupportedCommandError
from .util import byte, hexify, mask_to_shift, pad_to, strip_chip_name
try:
import serial
@@ -59,7 +61,7 @@ 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)
% (serial.VERSION, sys.executable)
)
raise
except Exception:
@@ -96,14 +98,8 @@ DEFAULT_SERIAL_WRITE_TIMEOUT = cfg.getfloat("serial_write_timeout", 10)
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 os.path.join(STUBS_DIR, f"stub_flasher_{chip_name}.json")
# Number of times to try opening the serial port
DEFAULT_OPEN_PORT_ATTEMPTS = cfg.getint("open_port_attempts", 1)
def timeout_per_mb(seconds_per_mb, size_bytes):
@@ -155,8 +151,12 @@ def esp32s3_or_newer_function_only(func):
class StubFlasher:
def __init__(self, json_path):
with open(json_path) as json_file:
STUB_DIR = os.path.join(os.path.dirname(__file__), "targets", "stub_flasher")
# directories will be searched in the order of STUB_SUBDIRS
STUB_SUBDIRS = ["1", "2"]
def __init__(self, chip_name):
with open(self.get_json_path(chip_name)) as json_file:
stub = json.load(json_file)
self.text = base64.b64decode(stub["text"])
@@ -172,6 +172,26 @@ class StubFlasher:
self.bss_start = stub.get("bss_start")
def get_json_path(self, chip_name):
chip_name = strip_chip_name(chip_name)
for i, subdir in enumerate(self.STUB_SUBDIRS):
json_path = os.path.join(self.STUB_DIR, subdir, f"{chip_name}.json")
if os.path.exists(json_path):
if i:
print(
f"Warning: Stub version {self.STUB_SUBDIRS[0]} doesn't exist, using {subdir} instead"
)
return json_path
else:
raise FileNotFoundError(f"Stub flasher JSON file for {chip_name} not found")
@classmethod
def set_preferred_stub_subdir(cls, subdir):
if subdir in cls.STUB_SUBDIRS:
cls.STUB_SUBDIRS.remove(subdir)
cls.STUB_SUBDIRS.insert(0, subdir)
class ESPLoader(object):
"""Base class providing access to ESP ROM & software stub bootloaders.
@@ -179,12 +199,15 @@ class ESPLoader(object):
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.
appropriate subclass instance. You can also use a context manager as
"with detect_chip() as esp:" to ensure the serial port is closed when done.
"""
CHIP_NAME = "Espressif device"
IS_STUB = False
STUB_CLASS: Optional[object] = None
BOOTLOADER_IMAGE: Optional[object] = None
DEFAULT_PORT = "/dev/ttyUSB0"
@@ -201,7 +224,7 @@ class ESPLoader(object):
ESP_WRITE_REG = 0x09
ESP_READ_REG = 0x0A
# Some comands supported by ESP32 and later chips ROM bootloader (or -8266 w/ stub)
# Some commands supported by ESP32 and later chips ROM bootloader (or -8266 w/ stub)
ESP_SPI_SET_PARAMS = 0x0B
ESP_SPI_ATTACH = 0x0D
ESP_READ_FLASH_SLOW = 0x0E # ROM only, much slower than the stub flash read
@@ -245,6 +268,9 @@ class ESPLoader(object):
UART_DATE_REG_ADDR = 0x60000078
# Whether the SPI peripheral sends from MSB of 32-bit register, or the MSB of valid LSB bits.
SPI_ADDR_REG_MSB = True
# This ROM address has a different value on each chip model
CHIP_DETECT_MAGIC_REG_ADDR = 0x40001000
@@ -273,11 +299,15 @@ class ESPLoader(object):
# Chip IDs that are no longer supported by esptool
UNSUPPORTED_CHIPS = {6: "ESP32-S3(beta 3)"}
# Number of attempts to write flash data
WRITE_FLASH_ATTEMPTS = 2
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().
ROM class directly, or use cmds.detect_chip(). You can use the with
statement to ensure the serial port is closed when done.
This base class has all of the instance methods for bootloader
functionality supported across various chips & stub
@@ -300,7 +330,17 @@ class ESPLoader(object):
if isinstance(port, str):
try:
self._port = serial.serial_for_url(port)
self._port = serial.serial_for_url(
port, exclusive=True, do_not_open=True
)
if sys.platform == "win32":
# When opening a port on Windows,
# the RTS/DTR (active low) lines
# need to be set to False (pulled high)
# to avoid unwanted chip reset
self._port.rts = False
self._port.dtr = False
self._port.open()
except serial.serialutil.SerialException as e:
port_issues = [
[ # does not exist error
@@ -316,10 +356,7 @@ class ESPLoader(object):
port_issues.append(
[ # permission denied error
re.compile(r"Permission denied", re.IGNORECASE),
(
"Try to add user into dialout group: "
"sudo usermod -a -G dialout $USER"
),
("Try to add user into dialout or uucp group."),
],
)
@@ -351,6 +388,12 @@ class ESPLoader(object):
# need to set the property back to None or it will continue to fail
self._port.write_timeout = None
def __enter__(self):
return self
def __exit__(self, exc_type, exc_value, traceback):
self._port.close()
@property
def serial_port(self):
return self._port.port
@@ -499,7 +542,7 @@ class ESPLoader(object):
# 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.
# reset properly and esptool is talking to the flasher stub.
self.sync_stub_detected = val == 0
for _ in range(7):
@@ -665,6 +708,15 @@ class ESPLoader(object):
"Connection may fail if the chip is not in bootloader "
"or flasher stub mode.",
)
if self._port.name.startswith("socket:"):
mode = "no_reset" # not possible to toggle DTR/RTS over a TCP socket
print(
"Note: It's not possible to reset the chip over a TCP socket. "
"Automatic resetting to bootloader has been disabled, "
"reset the chip manually."
)
print("Connecting...", end="")
sys.stdout.flush()
last_error = None
@@ -701,7 +753,7 @@ class ESPLoader(object):
if not detecting:
try:
from targets import ROM_LIST
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)
@@ -788,11 +840,14 @@ class ESPLoader(object):
"""Start downloading an application image to RAM"""
# check we're not going to overwrite a running stub with this data
if self.IS_STUB:
stub = StubFlasher(get_stub_json_path(self.CHIP_NAME))
stub = StubFlasher(self.CHIP_NAME)
load_start = offset
load_end = offset + size
for stub_start, stub_end in [
(stub.bss_start, stub.data_start + len(stub.data)), # DRAM = bss+data
(
stub.bss_start or stub.data_start,
stub.data_start + len(stub.data),
), # DRAM = bss+data
(stub.text_start, stub.text_start + len(stub.text)), # IRAM
]:
if load_start < stub_end and load_end > stub_start:
@@ -993,7 +1048,7 @@ class ESPLoader(object):
def run_stub(self, stub=None):
if stub is None:
stub = StubFlasher(get_stub_json_path(self.CHIP_NAME))
stub = StubFlasher(self.CHIP_NAME)
if self.sync_stub_detected:
print("Stub is already running. No upload is necessary.")
@@ -1349,7 +1404,9 @@ class ESPLoader(object):
self.write_reg(
SPI_USR2_REG, (7 << SPI_USR2_COMMAND_LEN_SHIFT) | spiflash_command
)
if addr and addr_len > 0:
if addr_len > 0:
if self.SPI_ADDR_REG_MSB:
addr = addr << (32 - addr_len)
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
@@ -1454,7 +1511,12 @@ class ESPLoader(object):
# 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 est_xtal > 45:
norm_xtal = 48
elif est_xtal > 33:
norm_xtal = 40
else:
norm_xtal = 26
if abs(norm_xtal - est_xtal) > 1:
print(
"WARNING: Detected crystal freq %.2fMHz is quite different to "
@@ -1636,12 +1698,14 @@ class HexFormatter(object):
while len(s) > 0:
line = s[:16]
ascii_line = "".join(
c
if (
c == " "
or (c in string.printable and c not in string.whitespace)
(
c
if (
c == " "
or (c in string.printable and c not in string.whitespace)
)
else "."
)
else "."
for c in line.decode("ascii", "replace")
)
s = s[16:]

View File

@@ -3,11 +3,12 @@
#
# SPDX-License-Identifier: GPL-2.0-or-later
import errno
import os
import struct
import time
from util import FatalError
from .util import FatalError, PrintOnce
# Used for resetting into bootloader on Unix-like systems
if os.name != "nt":
@@ -26,11 +27,41 @@ DEFAULT_RESET_DELAY = 0.05 # default time to wait before releasing boot pin aft
class ResetStrategy(object):
print_once = PrintOnce()
def __init__(self, port, reset_delay=DEFAULT_RESET_DELAY):
self.port = port
self.reset_delay = reset_delay
def __call__():
def __call__(self):
"""
On targets with USB modes, the reset process can cause the port to
disconnect / reconnect during reset.
This will retry reconnections on ports that
drop out during the reset sequence.
"""
for retry in reversed(range(3)):
try:
if not self.port.isOpen():
self.port.open()
self.reset()
break
except OSError as e:
# ENOTTY for TIOCMSET; EINVAL for TIOCMGET
if e.errno in [errno.ENOTTY, errno.EINVAL]:
self.print_once(
"WARNING: Chip was NOT reset. Setting RTS/DTR lines is not "
f"supported for port '{self.port.name}'. Set --before and --after "
"arguments to 'no_reset' and switch to bootloader manually to "
"avoid this warning."
)
break
elif not retry:
raise
self.port.close()
time.sleep(0.5)
def reset(self):
pass
def _setDTR(self, state):
@@ -63,7 +94,7 @@ class ClassicReset(ResetStrategy):
Classic reset sequence, sets DTR and RTS lines sequentially.
"""
def __call__(self):
def reset(self):
self._setDTR(False) # IO0=HIGH
self._setRTS(True) # EN=LOW, chip in reset
time.sleep(0.1)
@@ -79,7 +110,7 @@ class UnixTightReset(ResetStrategy):
which allows setting DTR and RTS lines at the same time.
"""
def __call__(self):
def reset(self):
self._setDTRandRTS(False, False)
self._setDTRandRTS(True, True)
self._setDTRandRTS(False, True) # IO0=HIGH & EN=LOW, chip in reset
@@ -96,7 +127,7 @@ class USBJTAGSerialReset(ResetStrategy):
is connecting via its USB-JTAG-Serial peripheral.
"""
def __call__(self):
def reset(self):
self._setRTS(False)
self._setDTR(False) # Idle
time.sleep(0.1)
@@ -117,13 +148,13 @@ class HardReset(ResetStrategy):
Can be used to reset out of the bootloader or to restart a running app.
"""
def __init__(self, port, uses_usb_otg=False):
def __init__(self, port, uses_usb=False):
super().__init__(port)
self.uses_usb_otg = uses_usb_otg
self.uses_usb = uses_usb
def __call__(self):
def reset(self):
self._setRTS(True) # EN->LOW
if self.uses_usb_otg:
if self.uses_usb:
# Give the chip some time to come out of reset,
# to be able to handle further DTR/RTS transitions
time.sleep(0.2)
@@ -162,7 +193,7 @@ class CustomReset(ResetStrategy):
"U": "self._setDTRandRTS({})",
}
def __call__(self):
def reset(self):
exec(self.constructed_strategy)
def __init__(self, port, seq_str):

View File

@@ -1,7 +1,10 @@
from .esp32 import ESP32ROM
from .esp32c2 import ESP32C2ROM
from .esp32c3 import ESP32C3ROM
from .esp32c5 import ESP32C5ROM
from .esp32c5beta3 import ESP32C5BETA3ROM
from .esp32c6 import ESP32C6ROM
from .esp32c61 import ESP32C61ROM
from .esp32c6beta import ESP32C6BETAROM
from .esp32h2 import ESP32H2ROM
from .esp32h2beta1 import ESP32H2BETA1ROM
@@ -25,6 +28,9 @@ CHIP_DEFS = {
"esp32h2beta2": ESP32H2BETA2ROM,
"esp32c2": ESP32C2ROM,
"esp32c6": ESP32C6ROM,
"esp32c61": ESP32C61ROM,
"esp32c5": ESP32C5ROM,
"esp32c5beta3": ESP32C5BETA3ROM,
"esp32h2": ESP32H2ROM,
"esp32p4": ESP32P4ROM,
}

View File

@@ -5,9 +5,10 @@
import struct
import time
from typing import Dict, Optional
from loader import ESPLoader
from util import FatalError, NotSupportedError
from ..loader import ESPLoader
from ..util import FatalError, NotSupportedError
class ESP32ROM(ESPLoader):
@@ -36,6 +37,9 @@ class ESP32ROM(ESPLoader):
SPI_MISO_DLEN_OFFS = 0x2C
EFUSE_RD_REG_BASE = 0x3FF5A000
EFUSE_BLK0_RDATA3_REG_OFFS = EFUSE_RD_REG_BASE + 0x00C
EFUSE_BLK0_RDATA5_REG_OFFS = EFUSE_RD_REG_BASE + 0x014
EFUSE_DIS_DOWNLOAD_MANUAL_ENCRYPT_REG = EFUSE_RD_REG_BASE + 0x18
EFUSE_DIS_DOWNLOAD_MANUAL_ENCRYPT = 1 << 7 # EFUSE_RD_DISABLE_DL_ENCRYPT
@@ -46,6 +50,11 @@ class ESP32ROM(ESPLoader):
EFUSE_RD_ABS_DONE_0_MASK = 1 << 4
EFUSE_RD_ABS_DONE_1_MASK = 1 << 5
EFUSE_VDD_SPI_REG = EFUSE_RD_REG_BASE + 0x10
VDD_SPI_XPD = 1 << 14 # XPD_SDIO_REG
VDD_SPI_TIEH = 1 << 15 # XPD_SDIO_TIEH
VDD_SPI_FORCE = 1 << 16 # XPD_SDIO_FORCE
DR_REG_SYSCON_BASE = 0x3FF66000
APB_CTL_DATE_ADDR = DR_REG_SYSCON_BASE + 0x7C
APB_CTL_DATE_V = 0x1
@@ -61,6 +70,17 @@ class ESP32ROM(ESPLoader):
TIMERS_RTC_CALI_VALUE = 0x01FFFFFF
TIMERS_RTC_CALI_VALUE_S = 7
GPIO_STRAP_REG = 0x3FF44038
GPIO_STRAP_VDDSPI_MASK = 1 << 5 # GPIO_STRAP_VDDSDIO
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_FORCE = 1 << 22
RTC_CNTL_SDIO_PD_EN = 1 << 21
FLASH_SIZES = {
"1MB": 0x00,
"2MB": 0x10,
@@ -105,6 +125,8 @@ class ESP32ROM(ESPLoader):
UF2_FAMILY_ID = 0x1C5F21B0
KEY_PURPOSES: Dict[int, str] = {}
""" Try to read the BLOCK1 (encryption key) and check if it is valid """
def is_flash_encryption_key_valid(self):
@@ -121,7 +143,7 @@ class ESP32ROM(ESPLoader):
# 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
# non-zero key. We break out on first occurrence of non-zero value
key_word = [0] * 7
for i in range(len(key_word)):
key_word[i] = self.read_efuse(14 + i)
@@ -205,11 +227,11 @@ class ESP32ROM(ESPLoader):
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
sc = self.read_efuse(3) & (1 << 0) # single core, 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",
0: "ESP32-S0WDQ6" if sc else "ESP32-D0WDQ6-V3" if rev3 else "ESP32-D0WDQ6",
1: "ESP32-S0WD" if sc else "ESP32-D0WD-V3" if rev3 else "ESP32-D0WD",
2: "ESP32-D2WD",
4: "ESP32-U4WDH",
5: "ESP32-PICO-V3" if rev3 else "ESP32-PICO-D4",
@@ -217,10 +239,6 @@ class ESP32ROM(ESPLoader):
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):
@@ -269,13 +287,30 @@ class ESP32ROM(ESPLoader):
coding_scheme = word6 & 0x3
features += [
"Coding Scheme %s"
% {0: "None", 1: "3/4", 2: "Repeat (UNSUPPORTED)", 3: "Invalid"}[
coding_scheme
]
% {
0: "None",
1: "3/4",
2: "Repeat (UNSUPPORTED)",
3: "None (may contain encoding data)",
}[coding_scheme]
]
return features
def get_chip_spi_pads(self):
"""Read chip spi pad config
return: clk, q, d, hd, cd
"""
efuse_blk0_rdata5 = self.read_reg(self.EFUSE_BLK0_RDATA5_REG_OFFS)
spi_pad_clk = efuse_blk0_rdata5 & 0x1F
spi_pad_q = (efuse_blk0_rdata5 >> 5) & 0x1F
spi_pad_d = (efuse_blk0_rdata5 >> 10) & 0x1F
spi_pad_cs = (efuse_blk0_rdata5 >> 15) & 0x1F
efuse_blk0_rdata3_reg = self.read_reg(self.EFUSE_BLK0_RDATA3_REG_OFFS)
spi_pad_hd = (efuse_blk0_rdata3_reg >> 4) & 0x1F
return spi_pad_clk, spi_pad_q, spi_pad_d, spi_pad_hd, spi_pad_cs
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))
@@ -295,32 +330,63 @@ class ESP32ROM(ESPLoader):
def get_erase_size(self, offset, size):
return size
def _get_efuse_flash_voltage(self) -> Optional[str]:
efuse = self.read_reg(self.EFUSE_VDD_SPI_REG)
# check efuse setting
if efuse & (self.VDD_SPI_FORCE | self.VDD_SPI_XPD | self.VDD_SPI_TIEH):
return "3.3V"
elif efuse & (self.VDD_SPI_FORCE | self.VDD_SPI_XPD):
return "1.8V"
elif efuse & self.VDD_SPI_FORCE:
return "OFF"
return None
def _get_rtc_cntl_flash_voltage(self) -> Optional[str]:
reg = self.read_reg(self.RTC_CNTL_SDIO_CONF_REG)
# check if override is set in RTC_CNTL_SDIO_CONF_REG
if reg & self.RTC_CNTL_SDIO_FORCE:
if reg & self.RTC_CNTL_DREFH_SDIO_M:
return "1.9V"
elif reg & self.RTC_CNTL_XPD_SDIO_REG:
return "1.8V"
else:
return "OFF"
return None
def get_flash_voltage(self):
"""Get flash voltage setting and print it to the console."""
voltage = self._get_rtc_cntl_flash_voltage()
source = "RTC_CNTL"
if not voltage:
voltage = self._get_efuse_flash_voltage()
source = "eFuse"
if not voltage:
strap_reg = self.read_reg(self.GPIO_STRAP_REG)
strap_reg &= self.GPIO_STRAP_VDDSPI_MASK
voltage = "1.8V" if strap_reg else "3.3V"
source = "a strapping pin"
print(f"Flash voltage set by {source} to {voltage}")
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'"
f"The only accepted VDDSDIO overrides are {', '.join(self.OVERRIDE_VDDSDIO_CHOICES)}"
)
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,
# RTC_CNTL_SDIO_TIEH is 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
reg_val = self.RTC_CNTL_SDIO_FORCE # override efuse setting
reg_val |= self.RTC_CNTL_SDIO_PD_EN
if new_voltage != "OFF":
reg_val |= RTC_CNTL_XPD_SDIO_REG # enable internal LDO
reg_val |= self.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
self.RTC_CNTL_DREFH_SDIO_M
| self.RTC_CNTL_DREFM_SDIO_M
| self.RTC_CNTL_DREFL_SDIO_M
) # boost voltage
self.write_reg(RTC_CNTL_SDIO_CONF_REG, reg_val)
self.write_reg(self.RTC_CNTL_SDIO_CONF_REG, reg_val)
print("VDDSDIO regulator set to %s" % new_voltage)
def read_flash_slow(self, offset, length, progress_fn):
@@ -329,11 +395,17 @@ class ESP32ROM(ESPLoader):
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("<II", offset + len(data), block_len),
)
try:
r = self.check_command(
"read flash block",
self.ESP_READ_FLASH_SLOW,
struct.pack("<II", offset + len(data), block_len),
)
except FatalError:
print(
"Hint: Consider specifying flash size using '--flash_size' argument"
)
raise
if len(r) < block_len:
raise FatalError(
"Expected %d byte block, got %d bytes. Serial errors?"

View File

@@ -5,10 +5,11 @@
import struct
import time
from typing import Dict
from .esp32c3 import ESP32C3ROM
from loader import ESPLoader
from util import FatalError
from ..loader import ESPLoader
from ..util import FatalError
class ESP32C2ROM(ESP32C3ROM):
@@ -20,8 +21,8 @@ class ESP32C2ROM(ESP32C3ROM):
DROM_MAP_START = 0x3C000000
DROM_MAP_END = 0x3C400000
# Magic value for ESP32C2 ECO0 and ECO1 respectively
CHIP_DETECT_MAGIC_VALUE = [0x6F51306F, 0x7C41A06F]
# Magic value for ESP32C2 ECO0 , ECO1 and ECO4 respectively
CHIP_DETECT_MAGIC_VALUE = [0x6F51306F, 0x7C41A06F, 0x0C21E06F]
EFUSE_BASE = 0x60008800
EFUSE_BLOCK2_ADDR = EFUSE_BASE + 0x040
@@ -64,6 +65,8 @@ class ESP32C2ROM(ESP32C3ROM):
UF2_FAMILY_ID = 0x2B88D29C
KEY_PURPOSES: Dict[int, str] = {}
def get_pkg_version(self):
num_word = 1
return (self.read_reg(self.EFUSE_BLOCK2_ADDR + (4 * num_word)) >> 22) & 0x07
@@ -146,7 +149,7 @@ class ESP32C2ROM(ESP32C3ROM):
# When chip has not generated AES/encryption key in BLOCK3,
# 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
# non-zero key. We break out on first occurrence of non-zero value
key_word = [0] * 7 if key_len_256 else [0] * 3
for i in range(len(key_word)):
key_word[i] = self.read_reg(self.EFUSE_BLOCK_KEY0_REG + i * 4)

View File

@@ -4,10 +4,11 @@
# SPDX-License-Identifier: GPL-2.0-or-later
import struct
from typing import Dict
from .esp32 import ESP32ROM
from loader import ESPLoader
from util import FatalError, NotImplementedInROMError
from ..loader import ESPLoader
from ..util import FatalError, NotImplementedInROMError
class ESP32C3ROM(ESP32ROM):
@@ -27,6 +28,8 @@ class ESP32C3ROM(ESP32ROM):
SPI_MISO_DLEN_OFFS = 0x28
SPI_W0_OFFS = 0x58
SPI_ADDR_REG_MSB = False
BOOTLOADER_FLASH_OFFSET = 0x0
# Magic values for ESP32-C3 eco 1+2, eco 3, eco 6, and eco 7 respectively
@@ -99,6 +102,20 @@ class ESP32C3ROM(ESP32ROM):
UF2_FAMILY_ID = 0xD42BA06C
EFUSE_MAX_KEY = 5
KEY_PURPOSES: Dict[int, str] = {
0: "USER/EMPTY",
1: "RESERVED",
4: "XTS_AES_128_KEY",
5: "HMAC_DOWN_ALL",
6: "HMAC_DOWN_JTAG",
7: "HMAC_DOWN_DIGITAL_SIGNATURE",
8: "HMAC_UP",
9: "SECURE_BOOT_DIGEST0",
10: "SECURE_BOOT_DIGEST1",
11: "SECURE_BOOT_DIGEST2",
}
def get_pkg_version(self):
num_word = 3
return (self.read_reg(self.EFUSE_BLOCK1_ADDR + (4 * num_word)) >> 21) & 0x07
@@ -152,6 +169,9 @@ class ESP32C3ROM(ESP32ROM):
# ESP32C3 XTAL is fixed to 40MHz
return 40
def get_flash_voltage(self):
pass # not supported on ESP32-C3
def override_vddsdio(self, new_voltage):
raise NotImplementedInROMError(
"VDD_SDIO overrides are not supported for ESP32-C3"
@@ -176,8 +196,10 @@ class ESP32C3ROM(ESP32ROM):
)
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")
if key_block < 0 or key_block > self.EFUSE_MAX_KEY:
raise FatalError(
f"Valid key block numbers must be in range 0-{self.EFUSE_MAX_KEY}"
)
reg, shift = [
(self.EFUSE_PURPOSE_KEY0_REG, self.EFUSE_PURPOSE_KEY0_SHIFT),
@@ -191,7 +213,9 @@ class ESP32C3ROM(ESP32ROM):
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)]
purposes = [
self.get_key_block_purpose(b) for b in range(self.EFUSE_MAX_KEY + 1)
]
return any(p == self.PURPOSE_VAL_XTS_AES128_KEY for p in purposes)

View File

@@ -0,0 +1,190 @@
# SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
#
# SPDX-License-Identifier: GPL-2.0-or-later
import struct
import time
from typing import Dict
from .esp32c6 import ESP32C6ROM
from ..loader import ESPLoader
from ..reset import HardReset
from ..util import FatalError
class ESP32C5ROM(ESP32C6ROM):
CHIP_NAME = "ESP32-C5"
IMAGE_CHIP_ID = 23
EFUSE_BASE = 0x600B4800
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
IROM_MAP_START = 0x42000000
IROM_MAP_END = 0x42800000
DROM_MAP_START = 0x42800000
DROM_MAP_END = 0x43000000
PCR_SYSCLK_CONF_REG = 0x60096110
PCR_SYSCLK_XTAL_FREQ_V = 0x7F << 24
PCR_SYSCLK_XTAL_FREQ_S = 24
UARTDEV_BUF_NO = 0x4085F51C # Variable in ROM .bss which indicates the port in use
# Magic value for ESP32C5
CHIP_DETECT_MAGIC_VALUE = [0x1101406F]
FLASH_FREQUENCY = {
"80m": 0xF,
"40m": 0x0,
"20m": 0x2,
}
MEMORY_MAP = [
[0x00000000, 0x00010000, "PADDING"],
[0x42800000, 0x43000000, "DROM"],
[0x40800000, 0x40860000, "DRAM"],
[0x40800000, 0x40860000, "BYTE_ACCESSIBLE"],
[0x4003A000, 0x40040000, "DROM_MASK"],
[0x40000000, 0x4003A000, "IROM_MASK"],
[0x42000000, 0x42800000, "IROM"],
[0x40800000, 0x40860000, "IRAM"],
[0x50000000, 0x50004000, "RTC_IRAM"],
[0x50000000, 0x50004000, "RTC_DRAM"],
[0x600FE000, 0x60100000, "MEM_INTERNAL2"],
]
UF2_FAMILY_ID = 0xF71C0343
EFUSE_MAX_KEY = 5
KEY_PURPOSES: Dict[int, str] = {
0: "USER/EMPTY",
1: "ECDSA_KEY",
2: "XTS_AES_256_KEY_1",
3: "XTS_AES_256_KEY_2",
4: "XTS_AES_128_KEY",
5: "HMAC_DOWN_ALL",
6: "HMAC_DOWN_JTAG",
7: "HMAC_DOWN_DIGITAL_SIGNATURE",
8: "HMAC_UP",
9: "SECURE_BOOT_DIGEST0",
10: "SECURE_BOOT_DIGEST1",
11: "SECURE_BOOT_DIGEST2",
12: "KM_INIT_KEY",
}
def get_pkg_version(self):
num_word = 2
return (self.read_reg(self.EFUSE_BLOCK1_ADDR + (4 * num_word)) >> 26) & 0x07
def get_minor_chip_version(self):
num_word = 2
return (self.read_reg(self.EFUSE_BLOCK1_ADDR + (4 * num_word)) >> 0) & 0x0F
def get_major_chip_version(self):
num_word = 2
return (self.read_reg(self.EFUSE_BLOCK1_ADDR + (4 * num_word)) >> 4) & 0x03
def get_chip_description(self):
chip_name = {
0: "ESP32-C5",
}.get(self.get_pkg_version(), "unknown ESP32-C5")
major_rev = self.get_major_chip_version()
minor_rev = self.get_minor_chip_version()
return f"{chip_name} (revision v{major_rev}.{minor_rev})"
def get_crystal_freq(self):
# The crystal detection algorithm of ESP32/ESP8266
# works for ESP32-C5 as well.
return ESPLoader.get_crystal_freq(self)
def get_crystal_freq_rom_expect(self):
return (
self.read_reg(self.PCR_SYSCLK_CONF_REG) & self.PCR_SYSCLK_XTAL_FREQ_V
) >> self.PCR_SYSCLK_XTAL_FREQ_S
def hard_reset(self):
print("Hard resetting via RTS pin...")
HardReset(self._port, self.uses_usb_jtag_serial())()
def change_baud(self, baud):
if not self.IS_STUB:
crystal_freq_rom_expect = self.get_crystal_freq_rom_expect()
crystal_freq_detect = self.get_crystal_freq()
print(
f"ROM expects crystal freq: {crystal_freq_rom_expect} MHz, detected {crystal_freq_detect} MHz"
)
baud_rate = baud
# If detect the XTAL is 48MHz, but the ROM code expects it to be 40MHz
if crystal_freq_detect == 48 and crystal_freq_rom_expect == 40:
baud_rate = baud * 40 // 48
# If detect the XTAL is 40MHz, but the ROM code expects it to be 48MHz
elif crystal_freq_detect == 40 and crystal_freq_rom_expect == 48:
baud_rate = baud * 48 // 40
else:
ESPLoader.change_baud(self, baud_rate)
return
print(f"Changing baud rate to {baud_rate}")
self.command(self.ESP_CHANGE_BAUDRATE, struct.pack("<II", baud_rate, 0))
print("Changed.")
self._set_port_baudrate(baud)
time.sleep(0.05) # get rid of garbage sent during baud rate change
self.flush_input()
else:
ESPLoader.change_baud(self, baud)
def check_spi_connection(self, spi_connection):
if not set(spi_connection).issubset(set(range(0, 29))):
raise FatalError("SPI Pin numbers must be in the range 0-28.")
if any([v for v in spi_connection if v in [13, 14]]):
print(
"WARNING: GPIO pins 13 and 14 are used by USB-Serial/JTAG, "
"consider using other pins for SPI flash connection."
)
class ESP32C5StubLoader(ESP32C5ROM):
"""Access class for ESP32C5 stub loader, runs on top of ROM.
(Basically the same as ESP32StubLoader, but different base class.
Can possibly be made into a mixin.)
"""
FLASH_WRITE_SIZE = 0x4000 # matches MAX_WRITE_BLOCK in stub_loader.c
STATUS_BYTES_LENGTH = 2 # same as ESP8266, different to ESP32 ROM
IS_STUB = True
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
ESP32C5ROM.STUB_CLASS = ESP32C5StubLoader

View File

@@ -0,0 +1,129 @@
# SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
#
# SPDX-License-Identifier: GPL-2.0-or-later
import struct
import time
from typing import Dict
from .esp32c6 import ESP32C6ROM
from ..loader import ESPLoader
class ESP32C5BETA3ROM(ESP32C6ROM):
CHIP_NAME = "ESP32-C5(beta3)"
IMAGE_CHIP_ID = 17
IROM_MAP_START = 0x41000000
IROM_MAP_END = 0x41800000
DROM_MAP_START = 0x41000000
DROM_MAP_END = 0x41800000
# Magic value for ESP32C5(beta3)
CHIP_DETECT_MAGIC_VALUE = [0xE10D8082]
FLASH_FREQUENCY = {
"80m": 0xF,
"40m": 0x0,
"20m": 0x2,
}
MEMORY_MAP = [
[0x00000000, 0x00010000, "PADDING"],
[0x41800000, 0x42000000, "DROM"],
[0x40800000, 0x40880000, "DRAM"],
[0x40800000, 0x40880000, "BYTE_ACCESSIBLE"],
[0x4004A000, 0x40050000, "DROM_MASK"],
[0x40000000, 0x4004A000, "IROM_MASK"],
[0x41000000, 0x41800000, "IROM"],
[0x40800000, 0x40880000, "IRAM"],
[0x50000000, 0x50004000, "RTC_IRAM"],
[0x50000000, 0x50004000, "RTC_DRAM"],
[0x600FE000, 0x60100000, "MEM_INTERNAL2"],
]
EFUSE_MAX_KEY = 5
KEY_PURPOSES: Dict[int, str] = {
0: "USER/EMPTY",
1: "ECDSA_KEY",
2: "XTS_AES_256_KEY_1",
3: "XTS_AES_256_KEY_2",
4: "XTS_AES_128_KEY",
5: "HMAC_DOWN_ALL",
6: "HMAC_DOWN_JTAG",
7: "HMAC_DOWN_DIGITAL_SIGNATURE",
8: "HMAC_UP",
9: "SECURE_BOOT_DIGEST0",
10: "SECURE_BOOT_DIGEST1",
11: "SECURE_BOOT_DIGEST2",
12: "KM_INIT_KEY",
}
def get_pkg_version(self):
num_word = 2
return (self.read_reg(self.EFUSE_BLOCK1_ADDR + (4 * num_word)) >> 26) & 0x07
def get_minor_chip_version(self):
num_word = 2
return (self.read_reg(self.EFUSE_BLOCK1_ADDR + (4 * num_word)) >> 0) & 0x0F
def get_major_chip_version(self):
num_word = 2
return (self.read_reg(self.EFUSE_BLOCK1_ADDR + (4 * num_word)) >> 4) & 0x03
def get_chip_description(self):
chip_name = {
0: "ESP32-C5 beta3 (QFN40)",
}.get(self.get_pkg_version(), "unknown ESP32-C5 beta3")
major_rev = self.get_major_chip_version()
minor_rev = self.get_minor_chip_version()
return f"{chip_name} (revision v{major_rev}.{minor_rev})"
def get_crystal_freq(self):
# The crystal detection algorithm of ESP32/ESP8266
# works for ESP32-C5 beta3 as well.
return ESPLoader.get_crystal_freq(self)
def change_baud(self, baud):
rom_with_48M_XTAL = not self.IS_STUB and self.get_crystal_freq() == 48
if rom_with_48M_XTAL:
# The code is copied over from ESPLoader.change_baud().
# Probably this is just a temporary solution until the next chip revision.
# The ROM code thinks it uses a 40 MHz XTAL. Recompute the baud rate
# in order to trick the ROM code to set the correct baud rate for
# a 48 MHz XTAL.
false_rom_baud = baud * 40 // 48
print(f"Changing baud rate to {baud}")
self.command(
self.ESP_CHANGE_BAUDRATE, struct.pack("<II", false_rom_baud, 0)
)
print("Changed.")
self._set_port_baudrate(baud)
time.sleep(0.05) # get rid of garbage sent during baud rate change
self.flush_input()
else:
ESPLoader.change_baud(self, baud)
class ESP32C5BETA3StubLoader(ESP32C5BETA3ROM):
"""Access class for ESP32C5BETA3 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
ESP32C5BETA3ROM.STUB_CLASS = ESP32C5BETA3StubLoader

View File

@@ -6,7 +6,7 @@
import struct
from .esp32c3 import ESP32C3ROM
from util import FatalError, NotImplementedInROMError
from ..util import FatalError, NotImplementedInROMError
class ESP32C6ROM(ESP32C3ROM):
@@ -161,8 +161,10 @@ class ESP32C6ROM(ESP32C3ROM):
)
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")
if key_block < 0 or key_block > self.EFUSE_MAX_KEY:
raise FatalError(
f"Valid key block numbers must be in range 0-{self.EFUSE_MAX_KEY}"
)
reg, shift = [
(self.EFUSE_PURPOSE_KEY0_REG, self.EFUSE_PURPOSE_KEY0_SHIFT),
@@ -176,7 +178,9 @@ class ESP32C6ROM(ESP32C3ROM):
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)]
purposes = [
self.get_key_block_purpose(b) for b in range(self.EFUSE_MAX_KEY + 1)
]
return any(p == self.PURPOSE_VAL_XTS_AES128_KEY for p in purposes)

View File

@@ -0,0 +1,144 @@
# SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
#
# SPDX-License-Identifier: GPL-2.0-or-later
import struct
from typing import Dict
from .esp32c6 import ESP32C6ROM
class ESP32C61ROM(ESP32C6ROM):
CHIP_NAME = "ESP32-C61"
IMAGE_CHIP_ID = 20
# Magic value for ESP32C61
CHIP_DETECT_MAGIC_VALUE = [0x33F0206F, 0x2421606F]
UART_DATE_REG_ADDR = 0x60000000 + 0x7C
EFUSE_BASE = 0x600B4800
EFUSE_BLOCK1_ADDR = EFUSE_BASE + 0x044
MAC_EFUSE_REG = EFUSE_BASE + 0x044
EFUSE_RD_REG_BASE = EFUSE_BASE + 0x030 # BLOCK0 read base address
EFUSE_PURPOSE_KEY0_REG = EFUSE_BASE + 0x34
EFUSE_PURPOSE_KEY0_SHIFT = 0
EFUSE_PURPOSE_KEY1_REG = EFUSE_BASE + 0x34
EFUSE_PURPOSE_KEY1_SHIFT = 4
EFUSE_PURPOSE_KEY2_REG = EFUSE_BASE + 0x34
EFUSE_PURPOSE_KEY2_SHIFT = 8
EFUSE_PURPOSE_KEY3_REG = EFUSE_BASE + 0x34
EFUSE_PURPOSE_KEY3_SHIFT = 12
EFUSE_PURPOSE_KEY4_REG = EFUSE_BASE + 0x34
EFUSE_PURPOSE_KEY4_SHIFT = 16
EFUSE_PURPOSE_KEY5_REG = EFUSE_BASE + 0x34
EFUSE_PURPOSE_KEY5_SHIFT = 20
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 + 0x030
EFUSE_SPI_BOOT_CRYPT_CNT_MASK = 0x7 << 23
EFUSE_SECURE_BOOT_EN_REG = EFUSE_BASE + 0x034
EFUSE_SECURE_BOOT_EN_MASK = 1 << 26
FLASH_FREQUENCY = {
"80m": 0xF,
"40m": 0x0,
"20m": 0x2,
}
MEMORY_MAP = [
[0x00000000, 0x00010000, "PADDING"],
[0x41800000, 0x42000000, "DROM"],
[0x40800000, 0x40860000, "DRAM"],
[0x40800000, 0x40860000, "BYTE_ACCESSIBLE"],
[0x4004AC00, 0x40050000, "DROM_MASK"],
[0x40000000, 0x4004AC00, "IROM_MASK"],
[0x41000000, 0x41800000, "IROM"],
[0x40800000, 0x40860000, "IRAM"],
[0x50000000, 0x50004000, "RTC_IRAM"],
[0x50000000, 0x50004000, "RTC_DRAM"],
[0x600FE000, 0x60100000, "MEM_INTERNAL2"],
]
UF2_FAMILY_ID = 0x77D850C4
EFUSE_MAX_KEY = 5
KEY_PURPOSES: Dict[int, str] = {
0: "USER/EMPTY",
1: "ECDSA_KEY",
2: "XTS_AES_256_KEY_1",
3: "XTS_AES_256_KEY_2",
4: "XTS_AES_128_KEY",
5: "HMAC_DOWN_ALL",
6: "HMAC_DOWN_JTAG",
7: "HMAC_DOWN_DIGITAL_SIGNATURE",
8: "HMAC_UP",
9: "SECURE_BOOT_DIGEST0",
10: "SECURE_BOOT_DIGEST1",
11: "SECURE_BOOT_DIGEST2",
12: "KM_INIT_KEY",
13: "XTS_AES_256_KEY_1_PSRAM",
14: "XTS_AES_256_KEY_2_PSRAM",
15: "XTS_AES_128_KEY_PSRAM",
}
def get_pkg_version(self):
num_word = 2
return (self.read_reg(self.EFUSE_BLOCK1_ADDR + (4 * num_word)) >> 26) & 0x07
def get_minor_chip_version(self):
num_word = 2
return (self.read_reg(self.EFUSE_BLOCK1_ADDR + (4 * num_word)) >> 0) & 0x0F
def get_major_chip_version(self):
num_word = 2
return (self.read_reg(self.EFUSE_BLOCK1_ADDR + (4 * num_word)) >> 4) & 0x03
def get_chip_description(self):
chip_name = {
0: "ESP32-C61",
}.get(self.get_pkg_version(), "unknown ESP32-C61")
major_rev = self.get_major_chip_version()
minor_rev = self.get_minor_chip_version()
return f"{chip_name} (revision v{major_rev}.{minor_rev})"
def get_chip_features(self):
return ["WiFi 6", "BT 5"]
def read_mac(self, mac_type="BASE_MAC"):
"""Read MAC from EFUSE region"""
mac0 = self.read_reg(self.MAC_EFUSE_REG)
mac1 = self.read_reg(self.MAC_EFUSE_REG + 4) # only bottom 16 bits are MAC
base_mac = struct.pack(">II", mac1, mac0)[2:]
# BASE MAC: 60:55:f9:f7:2c:a2
macs = {
"BASE_MAC": tuple(base_mac),
}
return macs.get(mac_type, None)
class ESP32C61StubLoader(ESP32C61ROM):
"""Access class for ESP32C61 stub loader, runs on top of ROM.
(Basically the same as ESP32StubLoader, but different base class.
Can possibly be made into a mixin.)
"""
FLASH_WRITE_SIZE = 0x4000 # matches MAX_WRITE_BLOCK in stub_loader.c
STATUS_BYTES_LENGTH = 2 # same as ESP8266, different to ESP32 ROM
IS_STUB = True
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
ESP32C61ROM.STUB_CLASS = ESP32C61StubLoader

View File

@@ -3,8 +3,10 @@
#
# SPDX-License-Identifier: GPL-2.0-or-later
from typing import Dict
from .esp32c6 import ESP32C6ROM
from util import FatalError
from ..util import FatalError
class ESP32H2ROM(ESP32C6ROM):
@@ -32,6 +34,22 @@ class ESP32H2ROM(ESP32C6ROM):
UF2_FAMILY_ID = 0x332726F6
EFUSE_MAX_KEY = 5
KEY_PURPOSES: Dict[int, str] = {
0: "USER/EMPTY",
1: "ECDSA_KEY",
2: "XTS_AES_256_KEY_1",
3: "XTS_AES_256_KEY_2",
4: "XTS_AES_128_KEY",
5: "HMAC_DOWN_ALL",
6: "HMAC_DOWN_JTAG",
7: "HMAC_DOWN_DIGITAL_SIGNATURE",
8: "HMAC_UP",
9: "SECURE_BOOT_DIGEST0",
10: "SECURE_BOOT_DIGEST1",
11: "SECURE_BOOT_DIGEST2",
}
def get_pkg_version(self):
num_word = 4
return (self.read_reg(self.EFUSE_BLOCK1_ADDR + (4 * num_word)) >> 0) & 0x07

View File

@@ -4,9 +4,12 @@
# SPDX-License-Identifier: GPL-2.0-or-later
import struct
from typing import Dict
from .esp32c3 import ESP32C3ROM
from util import FatalError, NotImplementedInROMError
from ..util import FatalError, NotImplementedInROMError
from typing import List
class ESP32H2BETA1ROM(ESP32C3ROM):
@@ -66,7 +69,7 @@ class ESP32H2BETA1ROM(ESP32C3ROM):
FLASH_ENCRYPTED_WRITE_ALIGN = 16
MEMORY_MAP = []
MEMORY_MAP: List = []
FLASH_FREQUENCY = {
"48m": 0xF,
@@ -75,6 +78,21 @@ class ESP32H2BETA1ROM(ESP32C3ROM):
"12m": 0x2,
}
EFUSE_MAX_KEY = 5
KEY_PURPOSES: Dict[int, str] = {
0: "USER/EMPTY",
1: "ECDSA_KEY",
2: "RESERVED",
4: "XTS_AES_128_KEY",
5: "HMAC_DOWN_ALL",
6: "HMAC_DOWN_JTAG",
7: "HMAC_DOWN_DIGITAL_SIGNATURE",
8: "HMAC_UP",
9: "SECURE_BOOT_DIGEST0",
10: "SECURE_BOOT_DIGEST1",
11: "SECURE_BOOT_DIGEST2",
}
def get_pkg_version(self):
num_word = 4
return (self.read_reg(self.EFUSE_BLOCK1_ADDR + (4 * num_word)) >> 0) & 0x07
@@ -119,8 +137,10 @@ class ESP32H2BETA1ROM(ESP32C3ROM):
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")
if key_block < 0 or key_block > self.EFUSE_MAX_KEY:
raise FatalError(
f"Valid key block numbers must be in range 0-{self.EFUSE_MAX_KEY}"
)
reg, shift = [
(self.EFUSE_PURPOSE_KEY0_REG, self.EFUSE_PURPOSE_KEY0_SHIFT),
@@ -134,7 +154,9 @@ class ESP32H2BETA1ROM(ESP32C3ROM):
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)]
purposes = [
self.get_key_block_purpose(b) for b in range(self.EFUSE_MAX_KEY + 1)
]
return any(p == self.PURPOSE_VAL_XTS_AES128_KEY for p in purposes)

View File

@@ -4,10 +4,11 @@
# SPDX-License-Identifier: GPL-2.0-or-later
import struct
from typing import Dict
from .esp32 import ESP32ROM
from loader import ESPLoader
from util import FatalError, NotImplementedInROMError
from ..loader import ESPLoader
from ..util import FatalError, NotImplementedInROMError
class ESP32P4ROM(ESP32ROM):
@@ -21,7 +22,7 @@ class ESP32P4ROM(ESP32ROM):
BOOTLOADER_FLASH_OFFSET = 0x2000 # First 2 sectors are reserved for FE purposes
CHIP_DETECT_MAGIC_VALUE = [0x0]
CHIP_DETECT_MAGIC_VALUE = [0x0, 0x0ADDBAD0]
UART_DATE_REG_ADDR = 0x500CA000 + 0x8C
@@ -37,6 +38,8 @@ class ESP32P4ROM(ESP32ROM):
SPI_MISO_DLEN_OFFS = 0x28
SPI_W0_OFFS = 0x58
SPI_ADDR_REG_MSB = False
EFUSE_RD_REG_BASE = EFUSE_BASE + 0x030 # BLOCK0 read base address
EFUSE_PURPOSE_KEY0_REG = EFUSE_BASE + 0x34
@@ -85,17 +88,34 @@ class ESP32P4ROM(ESP32ROM):
UF2_FAMILY_ID = 0x3D308E94
EFUSE_MAX_KEY = 5
KEY_PURPOSES: Dict[int, str] = {
0: "USER/EMPTY",
1: "ECDSA_KEY",
2: "XTS_AES_256_KEY_1",
3: "XTS_AES_256_KEY_2",
4: "XTS_AES_128_KEY",
5: "HMAC_DOWN_ALL",
6: "HMAC_DOWN_JTAG",
7: "HMAC_DOWN_DIGITAL_SIGNATURE",
8: "HMAC_UP",
9: "SECURE_BOOT_DIGEST0",
10: "SECURE_BOOT_DIGEST1",
11: "SECURE_BOOT_DIGEST2",
12: "KM_INIT_KEY",
}
def get_pkg_version(self):
# ESP32P4 TODO
return 0
num_word = 2
return (self.read_reg(self.EFUSE_BLOCK1_ADDR + (4 * num_word)) >> 20) & 0x07
def get_minor_chip_version(self):
# ESP32P4 TODO
return 0
num_word = 2
return (self.read_reg(self.EFUSE_BLOCK1_ADDR + (4 * num_word)) >> 0) & 0x0F
def get_major_chip_version(self):
# ESP32P4 TODO
return 0
num_word = 2
return (self.read_reg(self.EFUSE_BLOCK1_ADDR + (4 * num_word)) >> 4) & 0x03
def get_chip_description(self):
chip_name = {
@@ -112,6 +132,9 @@ class ESP32P4ROM(ESP32ROM):
# ESP32P4 XTAL is fixed to 40MHz
return 40
def get_flash_voltage(self):
pass # not supported on ESP32-P4
def override_vddsdio(self, new_voltage):
raise NotImplementedInROMError(
"VDD_SDIO overrides are not supported for ESP32-P4"
@@ -136,8 +159,10 @@ class ESP32P4ROM(ESP32ROM):
)
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")
if key_block < 0 or key_block > self.EFUSE_MAX_KEY:
raise FatalError(
f"Valid key block numbers must be in range 0-{self.EFUSE_MAX_KEY}"
)
reg, shift = [
(self.EFUSE_PURPOSE_KEY0_REG, self.EFUSE_PURPOSE_KEY0_SHIFT),
@@ -151,7 +176,9 @@ class ESP32P4ROM(ESP32ROM):
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)]
purposes = [
self.get_key_block_purpose(b) for b in range(self.EFUSE_MAX_KEY + 1)
]
if any(p == self.PURPOSE_VAL_XTS_AES128_KEY for p in purposes):
return True
@@ -170,7 +197,13 @@ class ESP32P4ROM(ESP32ROM):
# self.disable_watchdogs()
def check_spi_connection(self, spi_connection):
pass # TODO: Define GPIOs for --spi-connection
if not set(spi_connection).issubset(set(range(0, 55))):
raise FatalError("SPI Pin numbers must be in the range 0-54.")
if any([v for v in spi_connection if v in [24, 25]]):
print(
"WARNING: GPIO pins 24 and 25 are used by USB-Serial/JTAG, "
"consider using other pins for SPI flash connection."
)
class ESP32P4StubLoader(ESP32P4ROM):

View File

@@ -5,11 +5,12 @@
import os
import struct
from typing import Dict
from .esp32 import ESP32ROM
from loader import ESPLoader
from reset import HardReset
from util import FatalError, NotImplementedInROMError
from ..loader import ESPLoader
from ..reset import HardReset
from ..util import FatalError, NotImplementedInROMError
class ESP32S2ROM(ESP32ROM):
@@ -31,6 +32,8 @@ class ESP32S2ROM(ESP32ROM):
SPI_MISO_DLEN_OFFS = 0x28
SPI_W0_OFFS = 0x58
SPI_ADDR_REG_MSB = False
MAC_EFUSE_REG = 0x3F41A044 # ESP32-S2 has special block for MAC efuses
UART_CLKDIV_REG = 0x3F400014
@@ -81,6 +84,7 @@ class ESP32S2ROM(ESP32ROM):
GPIO_STRAP_REG = 0x3F404038
GPIO_STRAP_SPI_BOOT_MASK = 0x8 # Not download mode
GPIO_STRAP_VDDSPI_MASK = 1 << 4
RTC_CNTL_OPTION1_REG = 0x3F408128
RTC_CNTL_FORCE_DOWNLOAD_BOOT_MASK = 0x1 # Is download mode forced over USB?
@@ -99,8 +103,29 @@ class ESP32S2ROM(ESP32ROM):
[0x50000000, 0x50002000, "RTC_DATA"],
]
EFUSE_VDD_SPI_REG = EFUSE_BASE + 0x34
VDD_SPI_XPD = 1 << 4
VDD_SPI_TIEH = 1 << 5
VDD_SPI_FORCE = 1 << 6
UF2_FAMILY_ID = 0xBFDD4EEE
EFUSE_MAX_KEY = 5
KEY_PURPOSES: Dict[int, str] = {
0: "USER/EMPTY",
1: "RESERVED",
2: "XTS_AES_256_KEY_1",
3: "XTS_AES_256_KEY_2",
4: "XTS_AES_128_KEY",
5: "HMAC_DOWN_ALL",
6: "HMAC_DOWN_JTAG",
7: "HMAC_DOWN_DIGITAL_SIGNATURE",
8: "HMAC_UP",
9: "SECURE_BOOT_DIGEST0",
10: "SECURE_BOOT_DIGEST1",
11: "SECURE_BOOT_DIGEST2",
}
def get_pkg_version(self):
num_word = 4
return (self.read_reg(self.EFUSE_BLOCK1_ADDR + (4 * num_word)) >> 0) & 0x0F
@@ -183,6 +208,9 @@ class ESP32S2ROM(ESP32ROM):
# ESP32-S2 XTAL is fixed to 40MHz
return 40
def _get_rtc_cntl_flash_voltage(self):
return None # not supported on ESP32-S2
def override_vddsdio(self, new_voltage):
raise NotImplementedInROMError(
"VDD_SDIO overrides are not supported for ESP32-S2"
@@ -215,8 +243,10 @@ class ESP32S2ROM(ESP32ROM):
)
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")
if key_block < 0 or key_block > self.EFUSE_MAX_KEY:
raise FatalError(
f"Valid key block numbers must be in range 0-{self.EFUSE_MAX_KEY}"
)
reg, shift = [
(self.EFUSE_PURPOSE_KEY0_REG, self.EFUSE_PURPOSE_KEY0_SHIFT),
@@ -230,7 +260,9 @@ class ESP32S2ROM(ESP32ROM):
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)]
purposes = [
self.get_key_block_purpose(b) for b in range(self.EFUSE_MAX_KEY + 1)
]
if any(p == self.PURPOSE_VAL_XTS_AES128_KEY for p in purposes):
return True
@@ -266,15 +298,12 @@ class ESP32S2ROM(ESP32ROM):
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(
f"Error: {self.get_chip_description()} chip was placed into download "
"mode using GPIO0.\nesptool.py can not exit the download mode over "
"USB. To run the app, reset the chip manually.\n"
"To suppress this note, set --after option to 'no_reset'."
)
raise SystemExit(1)
def hard_reset(self):
uses_usb_otg = self.uses_usb_otg()

View File

@@ -5,11 +5,12 @@
import os
import struct
from typing import Dict
from .esp32 import ESP32ROM
from loader import ESPLoader
from reset import HardReset
from util import FatalError, NotImplementedInROMError
from ..loader import ESPLoader
from ..reset import HardReset
from ..util import FatalError, NotImplementedInROMError
class ESP32S3ROM(ESP32ROM):
@@ -34,6 +35,8 @@ class ESP32S3ROM(ESP32ROM):
SPI_MISO_DLEN_OFFS = 0x28
SPI_W0_OFFS = 0x58
SPI_ADDR_REG_MSB = False
BOOTLOADER_FLASH_OFFSET = 0x0
SUPPORTS_ENCRYPTED_FLASH = True
@@ -95,6 +98,7 @@ class ESP32S3ROM(ESP32ROM):
GPIO_STRAP_REG = 0x60004038
GPIO_STRAP_SPI_BOOT_MASK = 0x8 # Not download mode
GPIO_STRAP_VDDSPI_MASK = 1 << 4
RTC_CNTL_OPTION1_REG = 0x6000812C
RTC_CNTL_FORCE_DOWNLOAD_BOOT_MASK = 0x1 # Is download mode forced over USB?
@@ -115,8 +119,29 @@ class ESP32S3ROM(ESP32ROM):
[0x50000000, 0x50002000, "RTC_DATA"],
]
EFUSE_VDD_SPI_REG = EFUSE_BASE + 0x34
VDD_SPI_XPD = 1 << 4
VDD_SPI_TIEH = 1 << 5
VDD_SPI_FORCE = 1 << 6
UF2_FAMILY_ID = 0xC47E5767
EFUSE_MAX_KEY = 5
KEY_PURPOSES: Dict[int, str] = {
0: "USER/EMPTY",
1: "RESERVED",
2: "XTS_AES_256_KEY_1",
3: "XTS_AES_256_KEY_2",
4: "XTS_AES_128_KEY",
5: "HMAC_DOWN_ALL",
6: "HMAC_DOWN_JTAG",
7: "HMAC_DOWN_DIGITAL_SIGNATURE",
8: "HMAC_UP",
9: "SECURE_BOOT_DIGEST0",
10: "SECURE_BOOT_DIGEST1",
11: "SECURE_BOOT_DIGEST2",
}
def get_pkg_version(self):
num_word = 3
return (self.read_reg(self.EFUSE_BLOCK1_ADDR + (4 * num_word)) >> 21) & 0x07
@@ -221,8 +246,10 @@ class ESP32S3ROM(ESP32ROM):
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")
if key_block < 0 or key_block > self.EFUSE_MAX_KEY:
raise FatalError(
f"Valid key block numbers must be in range 0-{self.EFUSE_MAX_KEY}"
)
reg, shift = [
(self.EFUSE_PURPOSE_KEY0_REG, self.EFUSE_PURPOSE_KEY0_SHIFT),
@@ -236,7 +263,9 @@ class ESP32S3ROM(ESP32ROM):
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)]
purposes = [
self.get_key_block_purpose(b) for b in range(self.EFUSE_MAX_KEY + 1)
]
if any(p == self.PURPOSE_VAL_XTS_AES128_KEY for p in purposes):
return True
@@ -251,6 +280,9 @@ class ESP32S3ROM(ESP32ROM):
& self.EFUSE_SECURE_BOOT_EN_MASK
)
def _get_rtc_cntl_flash_voltage(self):
return None # not supported on ESP32-S3
def override_vddsdio(self, new_voltage):
raise NotImplementedInROMError(
"VDD_SDIO overrides are not supported for ESP32-S3"
@@ -328,21 +360,28 @@ class ESP32S3ROM(ESP32ROM):
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(
f"Error: {self.get_chip_description()} chip was placed into download "
"mode using GPIO0.\nesptool.py can not exit the download mode over "
"USB. To run the app, reset the chip manually.\n"
"To suppress this note, set --after option to 'no_reset'."
)
raise SystemExit(1)
def hard_reset(self):
uses_usb_otg = self.uses_usb_otg()
if uses_usb_otg:
self._check_if_can_reset()
try:
# Clear force download boot mode to avoid the chip being stuck in download mode after reset
# workaround for issue: https://github.com/espressif/arduino-esp32/issues/6762
self.write_reg(
self.RTC_CNTL_OPTION1_REG, 0, self.RTC_CNTL_FORCE_DOWNLOAD_BOOT_MASK
)
except Exception:
# Skip if response was not valid and proceed to reset; e.g. when monitoring while resetting
pass
print("Hard resetting via RTS pin...")
HardReset(self._port, uses_usb_otg)()

View File

@@ -3,8 +3,8 @@
#
# SPDX-License-Identifier: GPL-2.0-or-later
from loader import ESPLoader
from util import FatalError, NotSupportedError
from ..loader import ESPLoader
from ..util import FatalError, NotSupportedError
class ESP8266ROM(ESPLoader):
@@ -169,12 +169,18 @@ class ESP8266ROM(ESPLoader):
else:
return (num_sectors - head_sectors) * sector_size
def get_flash_voltage(self):
pass # not supported on ESP8266
def override_vddsdio(self, new_voltage):
raise NotSupportedError(self, "Overriding VDDSDIO")
def check_spi_connection(self, spi_connection):
raise NotSupportedError(self, "Setting --spi-connection")
def get_secure_boot_enabled(self):
return False # ESP8266 doesn't have security features
class ESP8266StubLoader(ESP8266ROM):
"""Access class for ESP8266 stub loader, runs on top of ROM."""

View File

@@ -0,0 +1,3 @@
# Licensing
The binaries in JSON format distributed in this directory are released as Free Software under GNU General Public License Version 2 or later. They were released at https://github.com/espressif/esptool-legacy-flasher-stub/releases/tag/v1.3.0 from where the sources can be obtained.

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,8 @@
{
"entry": 1082131910,
"text": "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",
"text_start": 1082130432,
"data": "DACEQO4IgEA6CYBAkgmAQGAKgEDMCoBAegqAQLYHgEAcCoBAXAqAQKYJgEBmB4BA2gmAQGYHgEDICIBADAmAQDoJgECSCYBA2giAQCAIgEBQCIBA1giAQCQNgEA6CYBA5AuAQNgMgECyBoBAAg2AQLIGgECyBoBAsgaAQLIGgECyBoBAsgaAQLIGgECyBoBAgAuAQLIGgEAADIBA2AyAQA==",
"data_start": 1082469288,
"bss_start": 1082392576
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,8 @@
{
"entry": 1341195918,
"text": "QREixCbCBsa3Jw1QEUc3BPVP2Mu3JA1QEwQEANxAkYuR57JAIkSSREEBgoCIQBxAE3X1D4KX3bcBEbenDFBOxoOphwBKyDcJ9U8mylLEBs4izLekDFB9WhMJCQDATBN09D8N4PJAYkQjqDQBQknSRLJJIkoFYYKAiECDJwkAE3X1D4KXfRTjGUT/yb8TBwAMlEGqh2MY5QCFR4XGI6AFAHlVgoAFR2OH5gAJRmONxgB9VYKAQgUTB7ANQYVjlecCiUecwfW3kwbADWMW1QCYwRMFAAyCgJMG0A19VWOV1wCYwRMFsA2CgLc19k9BEZOFRboGxmE/Y0UFBrc39k+Th8exA6cHCAPWRwgTdfUPkwYWAMIGwYIjktcIMpcjAKcAA9dHCJFnk4cHBGMe9wI3t/VPEwfHsaFnupcDpgcIt/b1T7c39k+Th8exk4bGtWMf5gAjpscII6DXCCOSBwghoPlX4wb1/LJAQQGCgCOm1wgjoOcI3bc31whQfEudi/X/N8cIUHxLnYv1/4KAQREGxt03t9cIUCOmBwI3BwAImMOYQ33/yFeyQBNF9f8FiUEBgoBBEQbG2T993TcHAEC31whQmMM31whQHEP9/7JAQQGCgEERIsQ3hPVPkwcEAUrAA6kHAQbGJsJjCgkERTc5xb1HEwQEAYFEY9YnAQREvYiTtBQAfTeFPxxENwaAABOXxwCZ4DcGAAG39v8AdY+31ghQ2MKQwphCff9BR5HgBUczCelAupcjKCQBHMSyQCJEkkQCSUEBgoABEQbOIswlNzcE9E9sABMFxP6XAM//54Ag86qHBUWV57JHk/cHID7GiTc31whQHEe3BkAAEwXE/tWPHMeyRZcAz//ngKDwMzWgAPJAYkQFYYKAQRG3h/VPBsaThwcBBUcjgOcAE9fFAJjHBWd9F8zDyMf5jTqVqpWxgYzLI6oHAEE3GcETBVAMskBBAYKAAREizDeE9U+TBwQBJsrER07GBs5KyKqJEwQEAWPzlQCuhKnAAylEACaZE1nJABxIY1XwABxEY175ArU9fd1IQCaGzoWXAM//54Cg4xN19Q8BxZMHQAxcyFxAppdcwFxEhY9cxPJAYkTSREJJskkFYYKAaTVtv0ERBsaXAM//54BA1gNFhQGyQGkVEzUVAEEBgoBBEQbGxTcRwRlFskBBARcDz/9nAOPPQREGxibCIsSqhJcAz//ngADNdT8NyTcH9U+TBgcAg9dGABMEBwCFB8IHwYMjkvYAkwYADGOG1AATB+ADY3X3AG03IxIEALJAIkSSREEBgoBBEQbGEwcADGMa5QATBbANRTcTBcANskBBAVm/EwewDeMb5f5xNxMF0A31t0ERIsQmwgbGKoSzBLUAYxeUALJAIkSSREEBgoADRQQABQRNP+23NXEmy07H/XKFaf10Is1KyVLFVsMGz5OEhPoWkZOHCQemlxgIs4TnACqJJoUuhJcAz//ngOAZk4cJBxgIBWq6l7OKR0Ex5AVnfXWTBYX6kwcHBxMFhfkUCKqXM4XXAJMHBweul7OF1wAqxpcAz//ngKAWMkXBRZU3AUWFYhaR+kBqRNpESkm6SSpKmkoNYYKAooljc4oAhWlOhtaFSoWXAM//54CgyRN19Q8B7U6G1oUmhZcAz//ngOARTpkzBDRBUbcTBTAGVb8TBQAMSb0xcf1yBWdO11LVVtNezwbfIt0m20rZWtFizWbLaslux/13FpETBwcHPpccCLqXPsYjqgf4qokuirKKtosNNZMHAAIZwbcHAgA+hZcAz//ngIAKhWdj5VcTBWR9eRMJifqTBwQHypcYCDOJ5wBKhZcAz//ngAAJfXsTDDv5kwyL+RMHBAeTBwQHFAhil+aXgUQzDNcAs4zXAFJNY3xNCWPxpANBqJk/ooUIAY01uTcihgwBSoWXAM//54DgBKKZopRj9UQDs4ekQWPxdwMzBJpAY/OKAFaEIoYMAU6FlwDP/+eA4LgTdfUPVd0CzAFEeV2NTaMJAQBihZcAz//ngKCnffkDRTEB5oVZPGNPBQDj4o3+hWeThwcHopcYCLqX2pcjiqf4BQTxt+MVpf2RR+MF9PYFZ311kwcHB5MFhfoTBYX5FAiqlzOF1wCTBwcHrpezhdcAKsaXAM//54AA+3E9MkXBRWUzUT3dObcHAgAZ4ZMHAAI+hZcAz//ngAD4hWIWkfpQalTaVEpZulkqWppaClv6S2pM2kxKTbpNKWGCgLdXQUkZcZOH94QBRYbeotym2srYztbS1NbS2tDezuLM5srqyO7GPs6XAM//54DgoHkxBcU3R9hQt2cRUBMHF6qYzyOgBwAjrAcAmNPYT7cGBABVj9jPI6AHArcH9U83N/ZPk4cHABMHx7ohoCOgBwCRB+Pt5/7VM5FFaAjFOfE7t7f1T5OHx7EhZz6XIyD3CLcH8U83CfVPk4eHDiMg+QC3OfZPKTmTicmxEwkJAGMFBRC3Zw1QEwcQArjPhUVFRZcAz//ngKDmtwXxTwFGk4UFAEVFlwDP/+eAoOe3Jw1QEUeYyzcFAgCXAM//54Dg5rcHDlCIX4FFt4T1T3GJYRUTNRUAlwDP/+eAYKXBZ/0XEwcAEIVmQWa3BQABAUWThAQBtwr1Tw1qlwDP/+eAIJsTiwoBJpqDp8kI9d+Dq8kIhUcjpgkIIwLxAoPHGwAJRyMT4QKjAvECAtRNR2OB5whRR2OP5wYpR2Of5wCDxzsAA8crAKIH2Y8RR2OW5wCDp4sAnEM+1NE5oUVIEMU2g8c7AAPHKwCiB9mPEWdBB2N09wQTBbANqTYTBcANkTYTBeAOPT5dMUG3twXxTwFGk4WFAxVFlwDP/+eAoNg3pwxQXEcTBQACk+cXEFzHMbfJRyMT8QJNtwPHGwDRRmPn5gKFRmPm5gABTBME8A+FqHkXE3f3D8lG4+jm/rc29k8KB5OGBrs2lxhDAoeTBgcDk/b2DxFG42nW/BMH9wITd/cPjUZj6+YItzb2TwoHk4bGvzaXGEMChxMHQAJjl+cQAtQdRAFFcTwBReU0ATH9PqFFSBB9FCE2dfQBTAFEE3X0D8E8E3X8D+k0zTbjHgTqg8cbAElHY2v3MAlH43b36vUXk/f3Dz1H42D36jc39k+KBxMHx8C6l5xDgocFRJ3rcBCBRQFFl/DO/+eAoHcd4dFFaBBtNAFEMagFRIHvl/DO/+eAIH0zNKAAKaAhR2OF5wAFRAFMYbcDrIsAA6TLALNnjADSB/X30TBl9cFsIpz9HH19MwWMQF3cs3eVAZXjwWwzBYxAY+aMAv18MwWMQF3QMYGX8M7/54DAeV35ZpT1tzGBl/DO/+eAwHhd8WqU0bdBgZfwzv/ngAB4WfkzBJRBwbchR+OK5/ABTBMEAAw5t0FHzb9BRwVE453n9oOlywADpYsAOTy5v0FHBUTjk+f2A6cLAZFnY+7nHoOlSwEDpYsA7/C/hz2/QUcFROOT5/SDpwsBEWdjbvccA6fLAIOlSwEDpYsAM4TnAu/wP4UjrAQAIySKsDm3A8cEAGMHBxQDp4sAwRcTBAAMYxP3AMBIAUeTBvAOY0b3AoPHWwADx0sAAUyiB9mPA8drAEIHXY+Dx3sA4gfZj+OC9uYTBBAMsb0zhusAA0aGAQUHsY7ht4PHBAD9y9xEY5EHFsBII4AEAEW9YUdjlucCg6fLAQOniwGDpksBA6YLAYOlywADpYsAl/DO/+eAgGgqjDM0oAAxtQFMBUQZtRFHBUTjm+fmtxcOUPRfZXd9FwVm+Y7RjgOliwCThQcI9N+UQfmO0Y6UwZOFRwiUQfmO0Y6UwbRfgUV1j1GPuN+X8M7/54AgaxG9E/f3AOMRB+qT3EcAE4SLAAFMfV3jcZzbSESX8M7/54AgThhEVEAQQPmOYwenARxCE0f3/32P2Y4UwgUMQQTZvxFHhbVBRwVE45Tn3oOniwADp0sBIyb5ACMk6QBdu4MliQDBF5Hlic8BTBMEYAyxswMnyQBjZvcGE/c3AOMVB+IDKMkAAUYBRzMF6ECzhuUAY2n3AOMBBtIjJqkAIyTZABm7M4brABBOEQeQwgVG6b8hRwVE457n1gMkyQAZwBMEgAwjJgkAIyQJADM0gACNswFMEwQgDNWxAUwTBIAM8bkBTBMEkAzRuRMHIA1jg+cMEwdADeOY57gDxDsAg8crACIEXYyX8M7/54AATgOsxABBFGNzhAEijOMGDLbAQGKUMYCcSGNV8ACcRGNb9Arv8O/Rdd3IQGKGk4WLAZfwzv/ngABKAcWTB0AM3MjcQOKX3MDcRLOHh0HcxJfwzv/ngOBIDbYJZRMFBXEDrMsAA6SLAJfwzv/ngKA4t6cMUNhLtwYAAcEWk1dHARIHdY+9i9mPs4eHAwFFs9WHApfwzv/ngAA6EwWAPpfwzv/ngEA10byDpksBA6YLAYOlywADpYsA7/DP/n28g8U7AIPHKwAThYsBogXdjcEV7/DP21207/Avyz2/A8Q7AIPHKwATjIsBIgRdjNxEQRTN45FHhUtj/4cIkweQDNzIrbwDpw0AItAFSLOH7EA+1oMnirBjc/QADUhCxjrE7/CvxiJHMkg3hfVP4oV8EJOGCgEQEBMFhQKX8M7/54BgNze39U+TCAcBglcDp4iwg6UNAB2MHY8+nLJXI6TosKqLvpUjoL0Ak4cKAZ2NAcWhZ2OX9QBahe/wb9EjoG0BCcTcRJnD409w92PfCwCTB3AMvbeFS7c99k+3jPVPk43NupOMDAHpv+OaC5zcROOHB5yTB4AMqbeDp4sA45AHnO/wD9YJZRMFBXGX8M7/54CgIpfwzv/ngKAnTbIDpMsA4w4EmO/wz9MTBYA+l/DO/+eAgCAClFmy9lBmVNZURlm2WSZalloGW/ZLZkzWTEZNtk0JYYKAAAA=",
"text_start": 1341194240,
"data": "EAD1TwYK8U9WCvFPrgrxT4QL8U/wC/FPngvxT9QI8U9AC/FPgAvxT8IK8U+ECPFP9grxT4QI8U/gCfFPJgrxT1YK8U+uCvFP8gnxTzgJ8U9oCfFP7gnxT0AO8U9WCvFPCA3xTwAO8U/EB/FPJA7xT8QH8U/EB/FPxAfxT8QH8U/EB/FPxAfxT8QH8U/EB/FPpAzxT8QH8U8mDfFPAA7xTw==",
"data_start": 1341533100,
"bss_start": 1341456384
}

View File

@@ -0,0 +1,201 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View File

@@ -0,0 +1,25 @@
Copyright 2022 esp-rs
Permission is hereby granted, free of charge, to any
person obtaining a copy of this software and associated
documentation files (the "Software"), to deal in the
Software without restriction, including without
limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software
is furnished to do so, subject to the following
conditions:
The above copyright notice and this permission notice
shall be included in all copies or substantial portions
of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.

View File

@@ -0,0 +1,3 @@
# Licensing
The binaries in JSON format distributed in this directory are dual licensed under the Apache License Version 2.0 or the MIT license. They were released at https://github.com/esp-rs/esp-flasher-stub/releases/tag/v0.3.0 from where the sources can be obtained.

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -1,8 +0,0 @@
{
"entry": 1341195718,
"text": "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",
"text_start": 1341194240,
"data": "DAD1T+4I8U86CfFPkgnxT2gK8U/UCvFPggrxT7YH8U8kCvFPZArxT6YJ8U9mB/FP2gnxT2YH8U/ICPFPDAnxTzoJ8U+SCfFP2gjxTyAI8U9QCPFP1gjxTywN8U86CfFP7AvxT+AM8U+yBvFPCg3xT7IG8U+yBvFPsgbxT7IG8U+yBvFPsgbxT7IG8U+yBvFPiAvxT7IG8U8IDPFP4AzxTw==",
"data_start": 1341533096,
"bss_start": 1341456384
}

View File

@@ -1,5 +1,3 @@
#!/usr/bin/env python
#
# SPDX-FileCopyrightText: 2020-2023 Espressif Systems (Shanghai) CO LTD
# SPDX-License-Identifier: GPL-2.0-or-later
# Code was originally licensed under Apache 2.0 before the release of ESP-IDF v5.2
@@ -9,7 +7,7 @@ import os
import struct
from typing import List
from util import div_roundup
from esptool.util import div_roundup
class UF2Writer(object):

View File

@@ -99,6 +99,20 @@ def get_file_size(path_to_file):
return file_size
class PrintOnce:
"""
Class for printing messages just once. Can be useful when running in a loop
"""
def __init__(self) -> None:
self.already_printed = False
def __call__(self, text) -> None:
if not self.already_printed:
print(text)
self.already_printed = True
class FatalError(RuntimeError):
"""
Wrapper class for runtime errors that aren't caused by internal bugs, but by
@@ -128,6 +142,12 @@ class FatalError(RuntimeError):
0x109: "CRC or checksum was invalid",
0x10A: "Version was invalid",
0x10B: "MAC address was invalid",
0x6001: "Flash operation failed",
0x6002: "Flash operation timed out",
0x6003: "Flash not initialised properly",
0x6004: "Operation not supported by the host SPI bus",
0x6005: "Operation not supported by the flash chip",
0x6006: "Can't write, protection enabled",
# Flasher stub error codes
0xC000: "Bad data length",
0xC100: "Bad data checksum",