1# SPDX-FileCopyrightText: 2014-2022 Fredrik Ahlberg, Angus Gratton,
2# Espressif Systems (Shanghai) CO LTD, other contributors as noted.
3#
4# SPDX-License-Identifier: GPL-2.0-or-later
5
6import hashlib
7import io
8import os
9import struct
10import sys
11import time
12import zlib
13import itertools
14
15from intelhex import IntelHex
16from serial import SerialException
17
18from .bin_image import ELFFile, ImageSegment, LoadFirmwareImage
19from .bin_image import (
20    ESP8266ROMFirmwareImage,
21    ESP8266V2FirmwareImage,
22    ESP8266V3FirmwareImage,
23)
24from .loader import (
25    DEFAULT_CONNECT_ATTEMPTS,
26    DEFAULT_TIMEOUT,
27    ERASE_WRITE_TIMEOUT_PER_MB,
28    ESPLoader,
29    timeout_per_mb,
30)
31from .targets import CHIP_DEFS, CHIP_LIST, ROM_LIST
32from .uf2_writer import UF2Writer
33from .util import (
34    FatalError,
35    NotImplementedInROMError,
36    NotSupportedError,
37    UnsupportedCommandError,
38)
39from .util import (
40    div_roundup,
41    flash_size_bytes,
42    get_file_size,
43    hexify,
44    pad_to,
45    print_overwrite,
46)
47
48DETECTED_FLASH_SIZES = {
49    0x12: "256KB",
50    0x13: "512KB",
51    0x14: "1MB",
52    0x15: "2MB",
53    0x16: "4MB",
54    0x17: "8MB",
55    0x18: "16MB",
56    0x19: "32MB",
57    0x1A: "64MB",
58    0x1B: "128MB",
59    0x1C: "256MB",
60    0x20: "64MB",
61    0x21: "128MB",
62    0x22: "256MB",
63    0x32: "256KB",
64    0x33: "512KB",
65    0x34: "1MB",
66    0x35: "2MB",
67    0x36: "4MB",
68    0x37: "8MB",
69    0x38: "16MB",
70    0x39: "32MB",
71    0x3A: "64MB",
72}
73
74FLASH_MODES = {"qio": 0, "qout": 1, "dio": 2, "dout": 3}
75
76
77def detect_chip(
78    port=ESPLoader.DEFAULT_PORT,
79    baud=ESPLoader.ESP_ROM_BAUD,
80    connect_mode="default_reset",
81    trace_enabled=False,
82    connect_attempts=DEFAULT_CONNECT_ATTEMPTS,
83):
84    """Use serial access to detect the chip type.
85
86    First, get_security_info command is sent to detect the ID of the chip
87    (supported only by ESP32-C3 and later, works even in the Secure Download Mode).
88    If this fails, we reconnect and fall-back to reading the magic number.
89    It's mapped at a specific ROM address and has a different value on each chip model.
90    This way we use one memory read and compare it to the magic number for each chip.
91
92    This routine automatically performs ESPLoader.connect() (passing
93    connect_mode parameter) as part of querying the chip.
94    """
95    inst = None
96    detect_port = ESPLoader(port, baud, trace_enabled=trace_enabled)
97    if detect_port.serial_port.startswith("rfc2217:"):
98        detect_port.USES_RFC2217 = True
99    detect_port.connect(connect_mode, connect_attempts, detecting=True)
100    try:
101        print("Detecting chip type...", end="")
102        chip_id = detect_port.get_chip_id()
103        for cls in [
104            n for n in ROM_LIST if n.CHIP_NAME not in ("ESP8266", "ESP32", "ESP32-S2")
105        ]:
106            # cmd not supported on ESP8266 and ESP32 + ESP32-S2 doesn't return chip_id
107            if chip_id == cls.IMAGE_CHIP_ID:
108                inst = cls(detect_port._port, baud, trace_enabled=trace_enabled)
109                try:
110                    inst.read_reg(
111                        ESPLoader.CHIP_DETECT_MAGIC_REG_ADDR
112                    )  # Dummy read to check Secure Download mode
113                except UnsupportedCommandError:
114                    inst.secure_download_mode = True
115                inst._post_connect()
116                break
117        else:
118            err_msg = f"Unexpected chip ID value {chip_id}."
119    except (UnsupportedCommandError, struct.error, FatalError) as e:
120        # UnsupportedCommandError: ESP8266/ESP32 ROM
121        # struct.error: ESP32-S2
122        # FatalError: ESP8266/ESP32 STUB
123        print(" Unsupported detection protocol, switching and trying again...")
124        try:
125            # ESP32/ESP8266 are reset after an unsupported command, need to reconnect
126            # (not needed on ESP32-S2)
127            if not isinstance(e, struct.error):
128                detect_port.connect(
129                    connect_mode, connect_attempts, detecting=True, warnings=False
130                )
131            print("Detecting chip type...", end="")
132            sys.stdout.flush()
133            chip_magic_value = detect_port.read_reg(
134                ESPLoader.CHIP_DETECT_MAGIC_REG_ADDR
135            )
136
137            for cls in ROM_LIST:
138                if chip_magic_value in cls.CHIP_DETECT_MAGIC_VALUE:
139                    inst = cls(detect_port._port, baud, trace_enabled=trace_enabled)
140                    inst._post_connect()
141                    inst.check_chip_id()
142                    break
143            else:
144                err_msg = f"Unexpected chip magic value {chip_magic_value:#010x}."
145        except UnsupportedCommandError:
146            raise FatalError(
147                "Unsupported Command Error received. "
148                "Probably this means Secure Download Mode is enabled, "
149                "autodetection will not work. Need to manually specify the chip."
150            )
151    finally:
152        if inst is not None:
153            print(" %s" % inst.CHIP_NAME, end="")
154            if detect_port.sync_stub_detected:
155                inst = inst.STUB_CLASS(inst)
156                inst.sync_stub_detected = True
157            print("")  # end line
158            return inst
159    raise FatalError(
160        f"{err_msg} Failed to autodetect chip type."
161        "\nProbably it is unsupported by this version of esptool."
162    )
163
164
165# "Operation" commands, executable at command line. One function each
166#
167# Each function takes either two args (<ESPLoader instance>, <args>) or a single <args>
168# argument.
169
170
171def load_ram(esp, args):
172    image = LoadFirmwareImage(esp.CHIP_NAME, args.filename)
173
174    print("RAM boot...")
175    for seg in image.segments:
176        size = len(seg.data)
177        print("Downloading %d bytes at %08x..." % (size, seg.addr), end=" ")
178        sys.stdout.flush()
179        esp.mem_begin(
180            size, div_roundup(size, esp.ESP_RAM_BLOCK), esp.ESP_RAM_BLOCK, seg.addr
181        )
182
183        seq = 0
184        while len(seg.data) > 0:
185            esp.mem_block(seg.data[0 : esp.ESP_RAM_BLOCK], seq)
186            seg.data = seg.data[esp.ESP_RAM_BLOCK :]
187            seq += 1
188        print("done!")
189
190    print("All segments done, executing at %08x" % image.entrypoint)
191    esp.mem_finish(image.entrypoint)
192
193
194def read_mem(esp, args):
195    print("0x%08x = 0x%08x" % (args.address, esp.read_reg(args.address)))
196
197
198def write_mem(esp, args):
199    esp.write_reg(args.address, args.value, args.mask, 0)
200    print("Wrote %08x, mask %08x to %08x" % (args.value, args.mask, args.address))
201
202
203def dump_mem(esp, args):
204    with open(args.filename, "wb") as f:
205        for i in range(args.size // 4):
206            d = esp.read_reg(args.address + (i * 4))
207            f.write(struct.pack(b"<I", d))
208            if f.tell() % 1024 == 0:
209                print_overwrite(
210                    "%d bytes read... (%d %%)" % (f.tell(), f.tell() * 100 // args.size)
211                )
212            sys.stdout.flush()
213        print_overwrite("Read %d bytes" % f.tell(), last_line=True)
214    print("Done!")
215
216
217def detect_flash_size(esp, args=None):
218    # TODO: Remove the dependency on args in the next major release (v5.0)
219    if esp.secure_download_mode:
220        if args is not None and args.flash_size == "detect":
221            raise FatalError(
222                "Detecting flash size is not supported in secure download mode. "
223                "Need to manually specify flash size."
224            )
225        else:
226            return None
227    flash_id = esp.flash_id()
228    size_id = flash_id >> 16
229    flash_size = DETECTED_FLASH_SIZES.get(size_id)
230    if args is not None and args.flash_size == "detect":
231        if flash_size is None:
232            flash_size = "4MB"
233            print(
234                "WARNING: Could not auto-detect Flash size "
235                f"(FlashID={flash_id:#x}, SizeID={size_id:#x}), defaulting to 4MB"
236            )
237        else:
238            print("Auto-detected Flash size:", flash_size)
239        args.flash_size = flash_size
240    return flash_size
241
242
243def _update_image_flash_params(esp, address, args, image):
244    """
245    Modify the flash mode & size bytes if this looks like an executable bootloader image
246    """
247    if len(image) < 8:
248        return image  # not long enough to be a bootloader image
249
250    # unpack the (potential) image header
251    magic, _, flash_mode, flash_size_freq = struct.unpack("BBBB", image[:4])
252    if address != esp.BOOTLOADER_FLASH_OFFSET:
253        return image  # not flashing bootloader offset, so don't modify this
254
255    if (args.flash_mode, args.flash_freq, args.flash_size) == ("keep",) * 3:
256        return image  # all settings are 'keep', not modifying anything
257
258    # easy check if this is an image: does it start with a magic byte?
259    if magic != esp.ESP_IMAGE_MAGIC:
260        print(
261            "Warning: Image file at 0x%x doesn't look like an image file, "
262            "so not changing any flash settings." % address
263        )
264        return image
265
266    # make sure this really is an image, and not just data that
267    # starts with esp.ESP_IMAGE_MAGIC (mostly a problem for encrypted
268    # images that happen to start with a magic byte
269    try:
270        test_image = esp.BOOTLOADER_IMAGE(io.BytesIO(image))
271        test_image.verify()
272    except Exception:
273        print(
274            "Warning: Image file at 0x%x is not a valid %s image, "
275            "so not changing any flash settings." % (address, esp.CHIP_NAME)
276        )
277        return image
278
279    # After the 8-byte header comes the extended header for chips others than ESP8266.
280    # The 15th byte of the extended header indicates if the image is protected by
281    # a SHA256 checksum. In that case we recalculate the SHA digest after modifying the header.
282    sha_appended = args.chip != "esp8266" and image[8 + 15] == 1
283
284    if args.flash_mode != "keep":
285        flash_mode = FLASH_MODES[args.flash_mode]
286
287    flash_freq = flash_size_freq & 0x0F
288    if args.flash_freq != "keep":
289        flash_freq = esp.parse_flash_freq_arg(args.flash_freq)
290
291    flash_size = flash_size_freq & 0xF0
292    if args.flash_size != "keep":
293        flash_size = esp.parse_flash_size_arg(args.flash_size)
294
295    flash_params = struct.pack(b"BB", flash_mode, flash_size + flash_freq)
296    if flash_params != image[2:4]:
297        print("Flash params set to 0x%04x" % struct.unpack(">H", flash_params))
298        image = image[0:2] + flash_params + image[4:]
299
300    # recalculate the SHA digest if it was appended
301    if sha_appended:
302        # Since the changes are only made for images located in the bootloader offset,
303        # we can assume that the image is always a bootloader image.
304        # For merged binaries, we check the bootloader SHA when parameters are changed.
305        image_object = esp.BOOTLOADER_IMAGE(io.BytesIO(image))
306        # get the image header, extended header (if present) and data
307        image_data_before_sha = image[: image_object.data_length]
308        # get the image data after the SHA digest (primary for merged binaries)
309        image_data_after_sha = image[
310            (image_object.data_length + image_object.SHA256_DIGEST_LEN) :
311        ]
312
313        sha_digest_calculated = hashlib.sha256(image_data_before_sha).digest()
314        image = bytes(
315            itertools.chain(
316                image_data_before_sha, sha_digest_calculated, image_data_after_sha
317            )
318        )
319
320        # get the SHA digest newly stored in the image and compare it to the calculated one
321        image_stored_sha = image[
322            image_object.data_length : image_object.data_length
323            + image_object.SHA256_DIGEST_LEN
324        ]
325
326        if hexify(sha_digest_calculated) == hexify(image_stored_sha):
327            print("SHA digest in image updated")
328        else:
329            print(
330                "WARNING: SHA recalculation for binary failed!\n"
331                f"\tExpected calculated SHA: {hexify(sha_digest_calculated)}\n"
332                f"\tSHA stored in binary:    {hexify(image_stored_sha)}"
333            )
334
335    return image
336
337
338def write_flash(esp, args):
339    # set args.compress based on default behaviour:
340    # -> if either --compress or --no-compress is set, honour that
341    # -> otherwise, set --compress unless --no-stub is set
342    if args.compress is None and not args.no_compress:
343        args.compress = not args.no_stub
344
345    if not args.force and esp.CHIP_NAME != "ESP8266" and not esp.secure_download_mode:
346        # Check if secure boot is active
347        if esp.get_secure_boot_enabled():
348            for address, _ in args.addr_filename:
349                if address < 0x8000:
350                    raise FatalError(
351                        "Secure Boot detected, writing to flash regions < 0x8000 "
352                        "is disabled to protect the bootloader. "
353                        "Use --force to override, "
354                        "please use with caution, otherwise it may brick your device!"
355                    )
356        # Check if chip_id and min_rev in image are valid for the target in use
357        for _, argfile in args.addr_filename:
358            try:
359                image = LoadFirmwareImage(esp.CHIP_NAME, argfile)
360            except (FatalError, struct.error, RuntimeError):
361                continue
362            finally:
363                argfile.seek(0)  # LoadFirmwareImage changes the file handle position
364            if image.chip_id != esp.IMAGE_CHIP_ID:
365                raise FatalError(
366                    f"{argfile.name} is not an {esp.CHIP_NAME} image. "
367                    "Use --force to flash anyway."
368                )
369
370            # this logic below decides which min_rev to use, min_rev or min/max_rev_full
371            if image.max_rev_full == 0:  # image does not have max/min_rev_full fields
372                use_rev_full_fields = False
373            elif image.max_rev_full == 65535:  # image has default value of max_rev_full
374                use_rev_full_fields = True
375                if (
376                    image.min_rev_full == 0 and image.min_rev != 0
377                ):  # min_rev_full is not set, min_rev is used
378                    use_rev_full_fields = False
379            else:  # max_rev_full set to a version
380                use_rev_full_fields = True
381
382            if use_rev_full_fields:
383                rev = esp.get_chip_revision()
384                if rev < image.min_rev_full or rev > image.max_rev_full:
385                    error_str = f"{argfile.name} requires chip revision in range "
386                    error_str += (
387                        f"[v{image.min_rev_full // 100}.{image.min_rev_full % 100} - "
388                    )
389                    if image.max_rev_full == 65535:
390                        error_str += "max rev not set] "
391                    else:
392                        error_str += (
393                            f"v{image.max_rev_full // 100}.{image.max_rev_full % 100}] "
394                        )
395                    error_str += f"(this chip is revision v{rev // 100}.{rev % 100})"
396                    raise FatalError(f"{error_str}. Use --force to flash anyway.")
397            else:
398                # In IDF, image.min_rev is set based on Kconfig option.
399                # For C3 chip, image.min_rev is the Minor revision
400                # while for the rest chips it is the Major revision.
401                if esp.CHIP_NAME == "ESP32-C3":
402                    rev = esp.get_minor_chip_version()
403                else:
404                    rev = esp.get_major_chip_version()
405                if rev < image.min_rev:
406                    raise FatalError(
407                        f"{argfile.name} requires chip revision "
408                        f"{image.min_rev} or higher (this chip is revision {rev}). "
409                        "Use --force to flash anyway."
410                    )
411
412    # In case we have encrypted files to write,
413    # we first do few sanity checks before actual flash
414    if args.encrypt or args.encrypt_files is not None:
415        do_write = True
416
417        if not esp.secure_download_mode:
418            if esp.get_encrypted_download_disabled():
419                raise FatalError(
420                    "This chip has encrypt functionality "
421                    "in UART download mode disabled. "
422                    "This is the Flash Encryption configuration for Production mode "
423                    "instead of Development mode."
424                )
425
426            crypt_cfg_efuse = esp.get_flash_crypt_config()
427
428            if crypt_cfg_efuse is not None and crypt_cfg_efuse != 0xF:
429                print("Unexpected FLASH_CRYPT_CONFIG value: 0x%x" % (crypt_cfg_efuse))
430                do_write = False
431
432            enc_key_valid = esp.is_flash_encryption_key_valid()
433
434            if not enc_key_valid:
435                print("Flash encryption key is not programmed")
436                do_write = False
437
438        # Determine which files list contain the ones to encrypt
439        files_to_encrypt = args.addr_filename if args.encrypt else args.encrypt_files
440
441        for address, argfile in files_to_encrypt:
442            if address % esp.FLASH_ENCRYPTED_WRITE_ALIGN:
443                print(
444                    "File %s address 0x%x is not %d byte aligned, can't flash encrypted"
445                    % (argfile.name, address, esp.FLASH_ENCRYPTED_WRITE_ALIGN)
446                )
447                do_write = False
448
449        if not do_write and not args.ignore_flash_encryption_efuse_setting:
450            raise FatalError(
451                "Can't perform encrypted flash write, "
452                "consult Flash Encryption documentation for more information"
453            )
454    else:
455        if not args.force and esp.CHIP_NAME != "ESP8266":
456            # ESP32 does not support `get_security_info()` and `secure_download_mode`
457            if (
458                esp.CHIP_NAME != "ESP32"
459                and esp.secure_download_mode
460                and bin(esp.get_security_info()["flash_crypt_cnt"]).count("1") & 1 != 0
461            ):
462                raise FatalError(
463                    "WARNING: Detected flash encryption and "
464                    "secure download mode enabled.\n"
465                    "Flashing plaintext binary may brick your device! "
466                    "Use --force to override the warning."
467                )
468
469            if (
470                not esp.secure_download_mode
471                and esp.get_encrypted_download_disabled()
472                and esp.get_flash_encryption_enabled()
473            ):
474                raise FatalError(
475                    "WARNING: Detected flash encryption enabled and "
476                    "download manual encrypt disabled.\n"
477                    "Flashing plaintext binary may brick your device! "
478                    "Use --force to override the warning."
479                )
480
481    # verify file sizes fit in flash
482    flash_end = flash_size_bytes(
483        detect_flash_size(esp) if args.flash_size == "keep" else args.flash_size
484    )
485    if flash_end is not None:  # Not in secure download mode
486        for address, argfile in args.addr_filename:
487            argfile.seek(0, os.SEEK_END)
488            if address + argfile.tell() > flash_end:
489                raise FatalError(
490                    "File %s (length %d) at offset %d "
491                    "will not fit in %d bytes of flash. "
492                    "Use --flash_size argument, or change flashing address."
493                    % (argfile.name, argfile.tell(), address, flash_end)
494                )
495            argfile.seek(0)
496
497    if args.erase_all:
498        erase_flash(esp, args)
499    else:
500        for address, argfile in args.addr_filename:
501            argfile.seek(0, os.SEEK_END)
502            write_end = address + argfile.tell()
503            argfile.seek(0)
504            bytes_over = address % esp.FLASH_SECTOR_SIZE
505            if bytes_over != 0:
506                print(
507                    "WARNING: Flash address {:#010x} is not aligned "
508                    "to a {:#x} byte flash sector. "
509                    "{:#x} bytes before this address will be erased.".format(
510                        address, esp.FLASH_SECTOR_SIZE, bytes_over
511                    )
512                )
513            # Print the address range of to-be-erased flash memory region
514            print(
515                "Flash will be erased from {:#010x} to {:#010x}...".format(
516                    address - bytes_over,
517                    div_roundup(write_end, esp.FLASH_SECTOR_SIZE)
518                    * esp.FLASH_SECTOR_SIZE
519                    - 1,
520                )
521            )
522
523    """ Create a list describing all the files we have to flash.
524    Each entry holds an "encrypt" flag marking whether the file needs encryption or not.
525    This list needs to be sorted.
526
527    First, append to each entry of our addr_filename list the flag args.encrypt
528    E.g., if addr_filename is [(0x1000, "partition.bin"), (0x8000, "bootloader")],
529    all_files will be [
530        (0x1000, "partition.bin", args.encrypt),
531        (0x8000, "bootloader", args.encrypt)
532        ],
533    where, of course, args.encrypt is either True or False
534    """
535    all_files = [
536        (offs, filename, args.encrypt) for (offs, filename) in args.addr_filename
537    ]
538
539    """
540    Now do the same with encrypt_files list, if defined.
541    In this case, the flag is True
542    """
543    if args.encrypt_files is not None:
544        encrypted_files_flag = [
545            (offs, filename, True) for (offs, filename) in args.encrypt_files
546        ]
547
548        # Concatenate both lists and sort them.
549        # As both list are already sorted, we could simply do a merge instead,
550        # but for the sake of simplicity and because the lists are very small,
551        # let's use sorted.
552        all_files = sorted(all_files + encrypted_files_flag, key=lambda x: x[0])
553
554    for address, argfile, encrypted in all_files:
555        compress = args.compress
556
557        # Check whether we can compress the current file before flashing
558        if compress and encrypted:
559            print("\nWARNING: - compress and encrypt options are mutually exclusive ")
560            print("Will flash %s uncompressed" % argfile.name)
561            compress = False
562
563        if args.no_stub:
564            print("Erasing flash...")
565        image = pad_to(
566            argfile.read(), esp.FLASH_ENCRYPTED_WRITE_ALIGN if encrypted else 4
567        )
568        if len(image) == 0:
569            print("WARNING: File %s is empty" % argfile.name)
570            continue
571
572        if not esp.secure_download_mode and not esp.get_secure_boot_enabled():
573            image = _update_image_flash_params(esp, address, args, image)
574        else:
575            print(
576                "WARNING: Security features enabled, so not changing any flash settings."
577            )
578        calcmd5 = hashlib.md5(image).hexdigest()
579        uncsize = len(image)
580        if compress:
581            uncimage = image
582            image = zlib.compress(uncimage, 9)
583        original_image = image  # Save the whole image in case retry is needed
584        # Try again if reconnect was successful
585        for attempt in range(1, esp.WRITE_FLASH_ATTEMPTS + 1):
586            try:
587                if compress:
588                    # Decompress the compressed binary a block at a time,
589                    # to dynamically calculate the timeout based on the real write size
590                    decompress = zlib.decompressobj()
591                    blocks = esp.flash_defl_begin(uncsize, len(image), address)
592                else:
593                    blocks = esp.flash_begin(
594                        uncsize, address, begin_rom_encrypted=encrypted
595                    )
596                argfile.seek(0)  # in case we need it again
597                seq = 0
598                bytes_sent = 0  # bytes sent on wire
599                bytes_written = 0  # bytes written to flash
600                t = time.time()
601
602                timeout = DEFAULT_TIMEOUT
603
604                while len(image) > 0:
605                    print_overwrite(
606                        "Writing at 0x%08x... (%d %%)"
607                        % (address + bytes_written, 100 * (seq + 1) // blocks)
608                    )
609                    sys.stdout.flush()
610                    block = image[0 : esp.FLASH_WRITE_SIZE]
611                    if compress:
612                        # feeding each compressed block into the decompressor lets us
613                        # see block-by-block how much will be written
614                        block_uncompressed = len(decompress.decompress(block))
615                        bytes_written += block_uncompressed
616                        block_timeout = max(
617                            DEFAULT_TIMEOUT,
618                            timeout_per_mb(
619                                ERASE_WRITE_TIMEOUT_PER_MB, block_uncompressed
620                            ),
621                        )
622                        if not esp.IS_STUB:
623                            timeout = block_timeout  # ROM code writes block to flash before ACKing
624                        esp.flash_defl_block(block, seq, timeout=timeout)
625                        if esp.IS_STUB:
626                            # Stub ACKs when block is received,
627                            # then writes to flash while receiving the block after it
628                            timeout = block_timeout
629                    else:
630                        # Pad the last block
631                        block = block + b"\xff" * (esp.FLASH_WRITE_SIZE - len(block))
632                        if encrypted:
633                            esp.flash_encrypt_block(block, seq)
634                        else:
635                            esp.flash_block(block, seq)
636                        bytes_written += len(block)
637                    bytes_sent += len(block)
638                    image = image[esp.FLASH_WRITE_SIZE :]
639                    seq += 1
640                break
641            except SerialException:
642                if attempt == esp.WRITE_FLASH_ATTEMPTS or encrypted:
643                    # Already retried once or encrypted mode is disabled because of security reasons
644                    raise
645                print("\nLost connection, retrying...")
646                esp._port.close()
647                print("Waiting for the chip to reconnect", end="")
648                for _ in range(DEFAULT_CONNECT_ATTEMPTS):
649                    try:
650                        time.sleep(1)
651                        esp._port.open()
652                        print()  # Print new line which was suppressed by print(".")
653                        esp.connect()
654                        if esp.IS_STUB:
655                            # Hack to bypass the stub overwrite check
656                            esp.IS_STUB = False
657                            # Reflash stub because chip was reset
658                            esp = esp.run_stub()
659                        image = original_image
660                        break
661                    except SerialException:
662                        print(".", end="")
663                        sys.stdout.flush()
664                else:
665                    raise  # Reconnect limit reached
666
667        if esp.IS_STUB:
668            # Stub only writes each block to flash after 'ack'ing the receive,
669            # so do a final dummy operation which will not be 'ack'ed
670            # until the last block has actually been written out to flash
671            esp.read_reg(ESPLoader.CHIP_DETECT_MAGIC_REG_ADDR, timeout=timeout)
672
673        t = time.time() - t
674        speed_msg = ""
675        if compress:
676            if t > 0.0:
677                speed_msg = " (effective %.1f kbit/s)" % (uncsize / t * 8 / 1000)
678            print_overwrite(
679                "Wrote %d bytes (%d compressed) at 0x%08x in %.1f seconds%s..."
680                % (uncsize, bytes_sent, address, t, speed_msg),
681                last_line=True,
682            )
683        else:
684            if t > 0.0:
685                speed_msg = " (%.1f kbit/s)" % (bytes_written / t * 8 / 1000)
686            print_overwrite(
687                "Wrote %d bytes at 0x%08x in %.1f seconds%s..."
688                % (bytes_written, address, t, speed_msg),
689                last_line=True,
690            )
691
692        if not encrypted and not esp.secure_download_mode:
693            try:
694                res = esp.flash_md5sum(address, uncsize)
695                if res != calcmd5:
696                    print("File  md5: %s" % calcmd5)
697                    print("Flash md5: %s" % res)
698                    print(
699                        "MD5 of 0xFF is %s"
700                        % (hashlib.md5(b"\xff" * uncsize).hexdigest())
701                    )
702                    raise FatalError("MD5 of file does not match data in flash!")
703                else:
704                    print("Hash of data verified.")
705            except NotImplementedInROMError:
706                pass
707
708    print("\nLeaving...")
709
710    if esp.IS_STUB:
711        # skip sending flash_finish to ROM loader here,
712        # as it causes the loader to exit and run user code
713        esp.flash_begin(0, 0)
714
715        # Get the "encrypted" flag for the last file flashed
716        # Note: all_files list contains triplets like:
717        # (address: Integer, filename: String, encrypted: Boolean)
718        last_file_encrypted = all_files[-1][2]
719
720        # Check whether the last file flashed was compressed or not
721        if args.compress and not last_file_encrypted:
722            esp.flash_defl_finish(False)
723        else:
724            esp.flash_finish(False)
725
726    if args.verify:
727        print("Verifying just-written flash...")
728        print(
729            "(This option is deprecated, "
730            "flash contents are now always read back after flashing.)"
731        )
732        # If some encrypted files have been flashed,
733        # print a warning saying that we won't check them
734        if args.encrypt or args.encrypt_files is not None:
735            print("WARNING: - cannot verify encrypted files, they will be ignored")
736        # Call verify_flash function only if there is at least
737        # one non-encrypted file flashed
738        if not args.encrypt:
739            verify_flash(esp, args)
740
741
742def image_info(args):
743    def v2():
744        def get_key_from_value(dict, val):
745            """Get key from value in dictionary"""
746            for key, value in dict.items():
747                if value == val:
748                    return key
749            return None
750
751        print()
752        title = "{} image header".format(args.chip.upper())
753        print(title)
754        print("=" * len(title))
755        print("Image version: {}".format(image.version))
756        print(
757            "Entry point: {:#8x}".format(image.entrypoint)
758            if image.entrypoint != 0
759            else "Entry point not set"
760        )
761
762        print("Segments: {}".format(len(image.segments)))
763
764        # Flash size
765        flash_s_bits = image.flash_size_freq & 0xF0  # high four bits
766        flash_s = get_key_from_value(image.ROM_LOADER.FLASH_SIZES, flash_s_bits)
767        print(
768            "Flash size: {}".format(flash_s)
769            if flash_s is not None
770            else "WARNING: Invalid flash size ({:#02x})".format(flash_s_bits)
771        )
772
773        # Flash frequency
774        flash_fr_bits = image.flash_size_freq & 0x0F  # low four bits
775        flash_fr = get_key_from_value(image.ROM_LOADER.FLASH_FREQUENCY, flash_fr_bits)
776        print(
777            "Flash freq: {}".format(flash_fr)
778            if flash_fr is not None
779            else "WARNING: Invalid flash frequency ({:#02x})".format(flash_fr_bits)
780        )
781
782        # Flash mode
783        flash_mode = get_key_from_value(FLASH_MODES, image.flash_mode)
784        print(
785            "Flash mode: {}".format(flash_mode.upper())
786            if flash_mode is not None
787            else "WARNING: Invalid flash mode ({})".format(image.flash_mode)
788        )
789
790        # Extended header (ESP32 and later only)
791        if args.chip != "esp8266":
792            print()
793            title = "{} extended image header".format(args.chip.upper())
794            print(title)
795            print("=" * len(title))
796            print(
797                f"WP pin: {image.wp_pin:#02x}",
798                *["(disabled)"] if image.wp_pin == image.WP_PIN_DISABLED else [],
799            )
800            print(
801                "Flash pins drive settings: "
802                "clk_drv: {:#02x}, q_drv: {:#02x}, d_drv: {:#02x}, "
803                "cs0_drv: {:#02x}, hd_drv: {:#02x}, wp_drv: {:#02x}".format(
804                    image.clk_drv,
805                    image.q_drv,
806                    image.d_drv,
807                    image.cs_drv,
808                    image.hd_drv,
809                    image.wp_drv,
810                )
811            )
812            try:
813                chip = next(
814                    chip
815                    for chip in CHIP_DEFS.values()
816                    if getattr(chip, "IMAGE_CHIP_ID", None) == image.chip_id
817                )
818                print(f"Chip ID: {image.chip_id} ({chip.CHIP_NAME})")
819            except StopIteration:
820                print(f"Chip ID: {image.chip_id} (Unknown ID)")
821            print(
822                "Minimal chip revision: "
823                f"v{image.min_rev_full // 100}.{image.min_rev_full % 100}, "
824                f"(legacy min_rev = {image.min_rev})"
825            )
826            print(
827                "Maximal chip revision: "
828                f"v{image.max_rev_full // 100}.{image.max_rev_full % 100}"
829            )
830        print()
831
832        # Segments overview
833        title = "Segments information"
834        print(title)
835        print("=" * len(title))
836        headers_str = "{:>7}  {:>7}  {:>10}  {:>10}  {:10}"
837        print(
838            headers_str.format(
839                "Segment", "Length", "Load addr", "File offs", "Memory types"
840            )
841        )
842        print(
843            "{}  {}  {}  {}  {}".format("-" * 7, "-" * 7, "-" * 10, "-" * 10, "-" * 12)
844        )
845        format_str = "{:7}  {:#07x}  {:#010x}  {:#010x}  {}"
846        app_desc = None
847        bootloader_desc = None
848        for idx, seg in enumerate(image.segments):
849            segs = seg.get_memory_type(image)
850            seg_name = ", ".join(segs)
851            if "DROM" in segs:  # The DROM segment starts with the esp_app_desc_t struct
852                app_desc = seg.data[:256]
853            elif "DRAM" in segs:
854                # The DRAM segment starts with the esp_bootloader_desc_t struct
855                if len(seg.data) >= 80:
856                    bootloader_desc = seg.data[:80]
857            print(
858                format_str.format(idx, len(seg.data), seg.addr, seg.file_offs, seg_name)
859            )
860        print()
861
862        # Footer
863        title = f"{args.chip.upper()} image footer"
864        print(title)
865        print("=" * len(title))
866        calc_checksum = image.calculate_checksum()
867        print(
868            "Checksum: {:#02x} ({})".format(
869                image.checksum,
870                (
871                    "valid"
872                    if image.checksum == calc_checksum
873                    else "invalid - calculated {:02x}".format(calc_checksum)
874                ),
875            )
876        )
877        try:
878            digest_msg = "Not appended"
879            if image.append_digest:
880                is_valid = image.stored_digest == image.calc_digest
881                digest_msg = "{} ({})".format(
882                    hexify(image.calc_digest, uppercase=False),
883                    "valid" if is_valid else "invalid",
884                )
885                print("Validation hash: {}".format(digest_msg))
886        except AttributeError:
887            pass  # ESP8266 image has no append_digest field
888
889        if app_desc:
890            APP_DESC_STRUCT_FMT = "<II" + "8s" + "32s32s16s16s32s32s" + "80s"
891            (
892                magic_word,
893                secure_version,
894                reserv1,
895                version,
896                project_name,
897                time,
898                date,
899                idf_ver,
900                app_elf_sha256,
901                reserv2,
902            ) = struct.unpack(APP_DESC_STRUCT_FMT, app_desc)
903
904            if magic_word == 0xABCD5432:
905                print()
906                title = "Application information"
907                print(title)
908                print("=" * len(title))
909                print(f'Project name: {project_name.decode("utf-8")}')
910                print(f'App version: {version.decode("utf-8")}')
911                print(f'Compile time: {date.decode("utf-8")} {time.decode("utf-8")}')
912                print(f"ELF file SHA256: {hexify(app_elf_sha256, uppercase=False)}")
913                print(f'ESP-IDF: {idf_ver.decode("utf-8")}')
914                print(f"Secure version: {secure_version}")
915
916        elif bootloader_desc:
917            BOOTLOADER_DESC_STRUCT_FMT = "<B" + "3s" + "I32s24s" + "16s"
918            (
919                magic_byte,
920                reserved,
921                version,
922                idf_ver,
923                date_time,
924                reserved2,
925            ) = struct.unpack(BOOTLOADER_DESC_STRUCT_FMT, bootloader_desc)
926
927            if magic_byte == 80:
928                print()
929                title = "Bootloader information"
930                print(title)
931                print("=" * len(title))
932                print(f"Bootloader version: {version}")
933                print(f'ESP-IDF: {idf_ver.decode("utf-8")}')
934                print(f'Compile time: {date_time.decode("utf-8")}')
935
936    print(f"File size: {get_file_size(args.filename)} (bytes)")
937    with open(args.filename, "rb") as f:
938        # magic number
939        try:
940            common_header = f.read(8)
941            magic = common_header[0]
942        except IndexError:
943            raise FatalError("File is empty")
944        if magic not in [
945            ESPLoader.ESP_IMAGE_MAGIC,
946            ESP8266V2FirmwareImage.IMAGE_V2_MAGIC,
947        ]:
948            raise FatalError(
949                "This is not a valid image " "(invalid magic number: {:#x})".format(
950                    magic
951                )
952            )
953
954        if args.chip == "auto":
955            try:
956                extended_header = f.read(16)
957
958                # append_digest, either 0 or 1
959                if extended_header[-1] not in [0, 1]:
960                    raise FatalError("Append digest field not 0 or 1")
961
962                chip_id = int.from_bytes(extended_header[4:5], "little")
963                for rom in [n for n in ROM_LIST if n.CHIP_NAME != "ESP8266"]:
964                    if chip_id == rom.IMAGE_CHIP_ID:
965                        args.chip = rom.CHIP_NAME
966                        break
967                else:
968                    raise FatalError(f"Unknown image chip ID ({chip_id})")
969            except FatalError:
970                args.chip = "esp8266"
971
972            print(f"Detected image type: {args.chip.upper()}")
973
974    image = LoadFirmwareImage(args.chip, args.filename)
975
976    if args.version == "2":
977        v2()
978        return
979
980    print("Image version: {}".format(image.version))
981    print(
982        "Entry point: {:8x}".format(image.entrypoint)
983        if image.entrypoint != 0
984        else "Entry point not set"
985    )
986    print("{} segments".format(len(image.segments)))
987    print()
988    idx = 0
989    for seg in image.segments:
990        idx += 1
991        segs = seg.get_memory_type(image)
992        seg_name = ",".join(segs)
993        print("Segment {}: {} [{}]".format(idx, seg, seg_name))
994    calc_checksum = image.calculate_checksum()
995    print(
996        "Checksum: {:02x} ({})".format(
997            image.checksum,
998            (
999                "valid"
1000                if image.checksum == calc_checksum
1001                else "invalid - calculated {:02x}".format(calc_checksum)
1002            ),
1003        )
1004    )
1005    try:
1006        digest_msg = "Not appended"
1007        if image.append_digest:
1008            is_valid = image.stored_digest == image.calc_digest
1009            digest_msg = "{} ({})".format(
1010                hexify(image.calc_digest, uppercase=False),
1011                "valid" if is_valid else "invalid",
1012            )
1013            print("Validation Hash: {}".format(digest_msg))
1014    except AttributeError:
1015        pass  # ESP8266 image has no append_digest field
1016
1017
1018def make_image(args):
1019    print("Creating {} image...".format(args.chip))
1020    image = ESP8266ROMFirmwareImage()
1021    if len(args.segfile) == 0:
1022        raise FatalError("No segments specified")
1023    if len(args.segfile) != len(args.segaddr):
1024        raise FatalError(
1025            "Number of specified files does not match number of specified addresses"
1026        )
1027    for seg, addr in zip(args.segfile, args.segaddr):
1028        with open(seg, "rb") as f:
1029            data = f.read()
1030            image.segments.append(ImageSegment(addr, data))
1031    image.entrypoint = args.entrypoint
1032    image.save(args.output)
1033    print("Successfully created {} image.".format(args.chip))
1034
1035
1036def elf2image(args):
1037    e = ELFFile(args.input)
1038    if args.chip == "auto":  # Default to ESP8266 for backwards compatibility
1039        args.chip = "esp8266"
1040
1041    print("Creating {} image...".format(args.chip))
1042
1043    if args.chip != "esp8266":
1044        image = CHIP_DEFS[args.chip].BOOTLOADER_IMAGE()
1045        if args.chip == "esp32" and args.secure_pad:
1046            image.secure_pad = "1"
1047        if args.secure_pad_v2:
1048            image.secure_pad = "2"
1049        image.min_rev = args.min_rev
1050        image.min_rev_full = args.min_rev_full
1051        image.max_rev_full = args.max_rev_full
1052        image.ram_only_header = args.ram_only_header
1053        if image.ram_only_header:
1054            image.append_digest = False
1055        else:
1056            image.append_digest = args.append_digest
1057    elif args.version == "1":  # ESP8266
1058        image = ESP8266ROMFirmwareImage()
1059    elif args.version == "2":
1060        image = ESP8266V2FirmwareImage()
1061    else:
1062        image = ESP8266V3FirmwareImage()
1063    image.entrypoint = e.entrypoint
1064    image.flash_mode = FLASH_MODES[args.flash_mode]
1065
1066    if args.flash_mmu_page_size:
1067        image.set_mmu_page_size(flash_size_bytes(args.flash_mmu_page_size))
1068
1069    # ELFSection is a subclass of ImageSegment, so can use interchangeably
1070    image.segments = e.segments if args.use_segments else e.sections
1071    if args.pad_to_size:
1072        image.pad_to_size = flash_size_bytes(args.pad_to_size)
1073    image.flash_size_freq = image.ROM_LOADER.parse_flash_size_arg(args.flash_size)
1074    image.flash_size_freq += image.ROM_LOADER.parse_flash_freq_arg(args.flash_freq)
1075
1076    if args.elf_sha256_offset:
1077        image.elf_sha256 = e.sha256()
1078        image.elf_sha256_offset = args.elf_sha256_offset
1079
1080    if args.ram_only_header:
1081        print(
1082            "Image has only RAM segments visible. "
1083            "ROM segments are hidden and SHA256 digest is not appended."
1084        )
1085        image.sort_segments()
1086
1087    before = len(image.segments)
1088    image.merge_adjacent_segments()
1089    if len(image.segments) != before:
1090        delta = before - len(image.segments)
1091        print("Merged %d ELF section%s" % (delta, "s" if delta > 1 else ""))
1092
1093    image.verify()
1094
1095    if args.output is None:
1096        args.output = image.default_output_name(args.input)
1097    image.save(args.output)
1098
1099    print("Successfully created {} image.".format(args.chip))
1100
1101
1102def read_mac(esp, args):
1103    def print_mac(label, mac):
1104        print("%s: %s" % (label, ":".join(map(lambda x: "%02x" % x, mac))))
1105
1106    eui64 = esp.read_mac("EUI64")
1107    if eui64:
1108        print_mac("MAC", eui64)
1109        print_mac("BASE MAC", esp.read_mac("BASE_MAC"))
1110        print_mac("MAC_EXT", esp.read_mac("MAC_EXT"))
1111    else:
1112        print_mac("MAC", esp.read_mac("BASE_MAC"))
1113
1114
1115def chip_id(esp, args):
1116    try:
1117        chipid = esp.chip_id()
1118        print("Chip ID: 0x%08x" % chipid)
1119    except NotSupportedError:
1120        print("Warning: %s has no Chip ID. Reading MAC instead." % esp.CHIP_NAME)
1121        read_mac(esp, args)
1122
1123
1124def erase_flash(esp, args):
1125    if not args.force and esp.CHIP_NAME != "ESP8266" and not esp.secure_download_mode:
1126        if esp.get_flash_encryption_enabled() or esp.get_secure_boot_enabled():
1127            raise FatalError(
1128                "Active security features detected, "
1129                "erasing flash is disabled as a safety measure. "
1130                "Use --force to override, "
1131                "please use with caution, otherwise it may brick your device!"
1132            )
1133    print("Erasing flash (this may take a while)...")
1134    t = time.time()
1135    esp.erase_flash()
1136    print("Chip erase completed successfully in %.1fs" % (time.time() - t))
1137
1138
1139def erase_region(esp, args):
1140    if not args.force and esp.CHIP_NAME != "ESP8266" and not esp.secure_download_mode:
1141        if esp.get_flash_encryption_enabled() or esp.get_secure_boot_enabled():
1142            raise FatalError(
1143                "Active security features detected, "
1144                "erasing flash is disabled as a safety measure. "
1145                "Use --force to override, "
1146                "please use with caution, otherwise it may brick your device!"
1147            )
1148    print("Erasing region (may be slow depending on size)...")
1149    t = time.time()
1150    esp.erase_region(args.address, args.size)
1151    print("Erase completed successfully in %.1f seconds." % (time.time() - t))
1152
1153
1154def run(esp, args):
1155    esp.run()
1156
1157
1158def flash_id(esp, args):
1159    flash_id = esp.flash_id()
1160    print("Manufacturer: %02x" % (flash_id & 0xFF))
1161    flid_lowbyte = (flash_id >> 16) & 0xFF
1162    print("Device: %02x%02x" % ((flash_id >> 8) & 0xFF, flid_lowbyte))
1163    print(
1164        "Detected flash size: %s" % (DETECTED_FLASH_SIZES.get(flid_lowbyte, "Unknown"))
1165    )
1166    flash_type = esp.flash_type()
1167    flash_type_dict = {0: "quad (4 data lines)", 1: "octal (8 data lines)"}
1168    flash_type_str = flash_type_dict.get(flash_type)
1169    if flash_type_str:
1170        print(f"Flash type set in eFuse: {flash_type_str}")
1171    esp.get_flash_voltage()
1172
1173
1174def read_flash(esp, args):
1175    if args.no_progress:
1176        flash_progress = None
1177    else:
1178
1179        def flash_progress(progress, length):
1180            msg = "%d (%d %%)" % (progress, progress * 100.0 / length)
1181            padding = "\b" * len(msg)
1182            if progress == length:
1183                padding = "\n"
1184            sys.stdout.write(msg + padding)
1185            sys.stdout.flush()
1186
1187    t = time.time()
1188    data = esp.read_flash(args.address, args.size, flash_progress)
1189    t = time.time() - t
1190    speed_msg = " ({:.1f} kbit/s)".format(len(data) / t * 8 / 1000) if t > 0.0 else ""
1191    print_overwrite(
1192        "Read {:d} bytes at {:#010x} in {:.1f} seconds{}...".format(
1193            len(data), args.address, t, speed_msg
1194        ),
1195        last_line=True,
1196    )
1197    with open(args.filename, "wb") as f:
1198        f.write(data)
1199
1200
1201def verify_flash(esp, args):
1202    differences = False
1203
1204    for address, argfile in args.addr_filename:
1205        image = pad_to(argfile.read(), 4)
1206        argfile.seek(0)  # rewind in case we need it again
1207
1208        image = _update_image_flash_params(esp, address, args, image)
1209
1210        image_size = len(image)
1211        print(
1212            "Verifying 0x%x (%d) bytes @ 0x%08x in flash against %s..."
1213            % (image_size, image_size, address, argfile.name)
1214        )
1215        # Try digest first, only read if there are differences.
1216        digest = esp.flash_md5sum(address, image_size)
1217        expected_digest = hashlib.md5(image).hexdigest()
1218        if digest == expected_digest:
1219            print("-- verify OK (digest matched)")
1220            continue
1221        else:
1222            differences = True
1223            if getattr(args, "diff", "no") != "yes":
1224                print("-- verify FAILED (digest mismatch)")
1225                continue
1226
1227        flash = esp.read_flash(address, image_size)
1228        assert flash != image
1229        diff = [i for i in range(image_size) if flash[i] != image[i]]
1230        print(
1231            "-- verify FAILED: %d differences, first @ 0x%08x"
1232            % (len(diff), address + diff[0])
1233        )
1234        for d in diff:
1235            flash_byte = flash[d]
1236            image_byte = image[d]
1237            print("   %08x %02x %02x" % (address + d, flash_byte, image_byte))
1238    if differences:
1239        raise FatalError("Verify failed.")
1240
1241
1242def read_flash_status(esp, args):
1243    print("Status value: 0x%04x" % esp.read_status(args.bytes))
1244
1245
1246def write_flash_status(esp, args):
1247    fmt = "0x%%0%dx" % (args.bytes * 2)
1248    args.value = args.value & ((1 << (args.bytes * 8)) - 1)
1249    print(("Initial flash status: " + fmt) % esp.read_status(args.bytes))
1250    print(("Setting flash status: " + fmt) % args.value)
1251    esp.write_status(args.value, args.bytes, args.non_volatile)
1252    print(("After flash status:   " + fmt) % esp.read_status(args.bytes))
1253
1254
1255# The following mapping was taken from the ROM code
1256# This mapping is same across all targets in the ROM
1257SECURITY_INFO_FLAG_MAP = {
1258    "SECURE_BOOT_EN": (1 << 0),
1259    "SECURE_BOOT_AGGRESSIVE_REVOKE": (1 << 1),
1260    "SECURE_DOWNLOAD_ENABLE": (1 << 2),
1261    "SECURE_BOOT_KEY_REVOKE0": (1 << 3),
1262    "SECURE_BOOT_KEY_REVOKE1": (1 << 4),
1263    "SECURE_BOOT_KEY_REVOKE2": (1 << 5),
1264    "SOFT_DIS_JTAG": (1 << 6),
1265    "HARD_DIS_JTAG": (1 << 7),
1266    "DIS_USB": (1 << 8),
1267    "DIS_DOWNLOAD_DCACHE": (1 << 9),
1268    "DIS_DOWNLOAD_ICACHE": (1 << 10),
1269}
1270
1271
1272# Get the status of respective security flag
1273def get_security_flag_status(flag_name, flags_value):
1274    try:
1275        return (flags_value & SECURITY_INFO_FLAG_MAP[flag_name]) != 0
1276    except KeyError:
1277        raise ValueError(f"Invalid flag name: {flag_name}")
1278
1279
1280def get_security_info(esp, args):
1281    si = esp.get_security_info()
1282    print()
1283    title = "Security Information:"
1284    print(title)
1285    print("=" * len(title))
1286    print("Flags: {:#010x} ({})".format(si["flags"], bin(si["flags"])))
1287    print("Key Purposes: {}".format(si["key_purposes"]))
1288    if si["chip_id"] is not None and si["api_version"] is not None:
1289        print("Chip ID: {}".format(si["chip_id"]))
1290        print("API Version: {}".format(si["api_version"]))
1291
1292    flags = si["flags"]
1293
1294    if get_security_flag_status("SECURE_BOOT_EN", flags):
1295        print("Secure Boot: Enabled")
1296        if get_security_flag_status("SECURE_BOOT_AGGRESSIVE_REVOKE", flags):
1297            print("Secure Boot Aggressive key revocation: Enabled")
1298
1299        revoked_keys = []
1300        for i, key in enumerate(
1301            [
1302                "SECURE_BOOT_KEY_REVOKE0",
1303                "SECURE_BOOT_KEY_REVOKE1",
1304                "SECURE_BOOT_KEY_REVOKE2",
1305            ]
1306        ):
1307            if get_security_flag_status(key, flags):
1308                revoked_keys.append(i)
1309
1310        if len(revoked_keys) > 0:
1311            print("Secure Boot Key Revocation Status:\n")
1312            for i in revoked_keys:
1313                print(f"\tSecure Boot Key{i} is Revoked\n")
1314
1315    else:
1316        print("Secure Boot: Disabled")
1317
1318    flash_crypt_cnt = bin(si["flash_crypt_cnt"])
1319    if (flash_crypt_cnt.count("1") % 2) != 0:
1320        print("Flash Encryption: Enabled")
1321    else:
1322        print("Flash Encryption: Disabled")
1323
1324    CRYPT_CNT_STRING = "SPI Boot Crypt Count (SPI_BOOT_CRYPT_CNT)"
1325    if esp.CHIP_NAME == "esp32":
1326        CRYPT_CNT_STRING = "Flash Crypt Count (FLASH_CRYPT_CNT)"
1327
1328    print(f"{CRYPT_CNT_STRING}: {si['flash_crypt_cnt']:#x}")
1329
1330    if get_security_flag_status("DIS_DOWNLOAD_DCACHE", flags):
1331        print("Dcache in UART download mode: Disabled")
1332
1333    if get_security_flag_status("DIS_DOWNLOAD_ICACHE", flags):
1334        print("Icache in UART download mode: Disabled")
1335
1336    hard_dis_jtag = get_security_flag_status("HARD_DIS_JTAG", flags)
1337    soft_dis_jtag = get_security_flag_status("SOFT_DIS_JTAG", flags)
1338    if hard_dis_jtag:
1339        print("JTAG: Permanently Disabled")
1340    elif soft_dis_jtag:
1341        print("JTAG: Software Access Disabled")
1342    if get_security_flag_status("DIS_USB", flags):
1343        print("USB Access: Disabled")
1344
1345
1346def merge_bin(args):
1347    try:
1348        chip_class = CHIP_DEFS[args.chip]
1349    except KeyError:
1350        msg = (
1351            "Please specify the chip argument"
1352            if args.chip == "auto"
1353            else f"Invalid chip choice: '{args.chip}'"
1354        )
1355        msg = f"{msg} (choose from {', '.join(CHIP_LIST)})"
1356        raise FatalError(msg)
1357
1358    # sort the files by offset.
1359    # The AddrFilenamePairAction has already checked for overlap
1360    input_files = sorted(args.addr_filename, key=lambda x: x[0])
1361    if not input_files:
1362        raise FatalError("No input files specified")
1363    first_addr = input_files[0][0]
1364    if first_addr < args.target_offset:
1365        raise FatalError(
1366            f"Output file target offset is {args.target_offset:#x}. "
1367            f"Input file offset {first_addr:#x} is before this."
1368        )
1369
1370    if args.format == "uf2":
1371        with UF2Writer(
1372            chip_class.UF2_FAMILY_ID,
1373            args.output,
1374            args.chunk_size,
1375            md5_enabled=not args.md5_disable,
1376        ) as writer:
1377            for addr, argfile in input_files:
1378                print(f"Adding {argfile.name} at {addr:#x}")
1379                image = argfile.read()
1380                image = _update_image_flash_params(chip_class, addr, args, image)
1381                writer.add_file(addr, image)
1382        print(
1383            f"Wrote {os.path.getsize(args.output):#x} bytes to file {args.output}, "
1384            f"ready to be flashed with any ESP USB Bridge"
1385        )
1386
1387    elif args.format == "raw":
1388        with open(args.output, "wb") as of:
1389
1390            def pad_to(flash_offs):
1391                # account for output file offset if there is any
1392                of.write(b"\xff" * (flash_offs - args.target_offset - of.tell()))
1393
1394            for addr, argfile in input_files:
1395                pad_to(addr)
1396                image = argfile.read()
1397                image = _update_image_flash_params(chip_class, addr, args, image)
1398                of.write(image)
1399            if args.fill_flash_size:
1400                pad_to(flash_size_bytes(args.fill_flash_size))
1401            print(
1402                f"Wrote {of.tell():#x} bytes to file {args.output}, "
1403                f"ready to flash to offset {args.target_offset:#x}"
1404            )
1405    elif args.format == "hex":
1406        out = IntelHex()
1407        for addr, argfile in input_files:
1408            ihex = IntelHex()
1409            image = argfile.read()
1410            image = _update_image_flash_params(chip_class, addr, args, image)
1411            ihex.frombytes(image, addr)
1412            out.merge(ihex)
1413        out.write_hex_file(args.output)
1414        print(
1415            f"Wrote {os.path.getsize(args.output):#x} bytes to file {args.output}, "
1416            f"ready to flash to offset {args.target_offset:#x}"
1417        )
1418
1419
1420def version(args):
1421    from . import __version__
1422
1423    print(__version__)
1424