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    set_flash_size = (
482        flash_size_bytes(args.flash_size)
483        if args.flash_size not in ["detect", "keep"]
484        else None
485    )
486    if esp.secure_download_mode:
487        flash_end = set_flash_size
488    else:  # Check against real flash chip size if not in SDM
489        flash_end_str = detect_flash_size(esp)
490        flash_end = flash_size_bytes(flash_end_str)
491        if set_flash_size and set_flash_size > flash_end:
492            print(
493                f"WARNING: Set --flash_size {args.flash_size} "
494                f"is larger than the available flash size of {flash_end_str}."
495            )
496
497    # Verify file sizes fit in the set --flash_size, or real flash size if smaller
498    flash_end = min(set_flash_size, flash_end) if set_flash_size else flash_end
499    if flash_end is not None:
500        for address, argfile in args.addr_filename:
501            argfile.seek(0, os.SEEK_END)
502            if address + argfile.tell() > flash_end:
503                raise FatalError(
504                    f"File {argfile.name} (length {argfile.tell()}) at offset "
505                    f"{address} will not fit in {flash_end} bytes of flash. "
506                    "Change the --flash_size argument, or flashing address."
507                )
508            argfile.seek(0)
509
510    if args.erase_all:
511        erase_flash(esp, args)
512    else:
513        for address, argfile in args.addr_filename:
514            argfile.seek(0, os.SEEK_END)
515            write_end = address + argfile.tell()
516            argfile.seek(0)
517            bytes_over = address % esp.FLASH_SECTOR_SIZE
518            if bytes_over != 0:
519                print(
520                    "WARNING: Flash address {:#010x} is not aligned "
521                    "to a {:#x} byte flash sector. "
522                    "{:#x} bytes before this address will be erased.".format(
523                        address, esp.FLASH_SECTOR_SIZE, bytes_over
524                    )
525                )
526            # Print the address range of to-be-erased flash memory region
527            print(
528                "Flash will be erased from {:#010x} to {:#010x}...".format(
529                    address - bytes_over,
530                    div_roundup(write_end, esp.FLASH_SECTOR_SIZE)
531                    * esp.FLASH_SECTOR_SIZE
532                    - 1,
533                )
534            )
535
536    """ Create a list describing all the files we have to flash.
537    Each entry holds an "encrypt" flag marking whether the file needs encryption or not.
538    This list needs to be sorted.
539
540    First, append to each entry of our addr_filename list the flag args.encrypt
541    E.g., if addr_filename is [(0x1000, "partition.bin"), (0x8000, "bootloader")],
542    all_files will be [
543        (0x1000, "partition.bin", args.encrypt),
544        (0x8000, "bootloader", args.encrypt)
545        ],
546    where, of course, args.encrypt is either True or False
547    """
548    all_files = [
549        (offs, filename, args.encrypt) for (offs, filename) in args.addr_filename
550    ]
551
552    """
553    Now do the same with encrypt_files list, if defined.
554    In this case, the flag is True
555    """
556    if args.encrypt_files is not None:
557        encrypted_files_flag = [
558            (offs, filename, True) for (offs, filename) in args.encrypt_files
559        ]
560
561        # Concatenate both lists and sort them.
562        # As both list are already sorted, we could simply do a merge instead,
563        # but for the sake of simplicity and because the lists are very small,
564        # let's use sorted.
565        all_files = sorted(all_files + encrypted_files_flag, key=lambda x: x[0])
566
567    for address, argfile, encrypted in all_files:
568        compress = args.compress
569
570        # Check whether we can compress the current file before flashing
571        if compress and encrypted:
572            print("\nWARNING: - compress and encrypt options are mutually exclusive ")
573            print("Will flash %s uncompressed" % argfile.name)
574            compress = False
575
576        image = argfile.read()
577
578        if len(image) == 0:
579            print("WARNING: File %s is empty" % argfile.name)
580            continue
581
582        image = pad_to(image, esp.FLASH_ENCRYPTED_WRITE_ALIGN if encrypted else 4)
583
584        if args.no_stub:
585            print("Erasing flash...")
586
587            # It is not possible to write to not aligned addresses without stub,
588            # so there are added 0xFF (erase) bytes at the beginning of the image
589            # to align it.
590            bytes_over = address % esp.FLASH_SECTOR_SIZE
591            address -= bytes_over
592            image = b"\xFF" * bytes_over + image
593
594        if not esp.secure_download_mode and not esp.get_secure_boot_enabled():
595            image = _update_image_flash_params(esp, address, args, image)
596        else:
597            print(
598                "WARNING: Security features enabled, so not changing any flash settings."
599            )
600        calcmd5 = hashlib.md5(image).hexdigest()
601        uncsize = len(image)
602        if compress:
603            uncimage = image
604            image = zlib.compress(uncimage, 9)
605        original_image = image  # Save the whole image in case retry is needed
606        # Try again if reconnect was successful
607        for attempt in range(1, esp.WRITE_FLASH_ATTEMPTS + 1):
608            try:
609                if compress:
610                    # Decompress the compressed binary a block at a time,
611                    # to dynamically calculate the timeout based on the real write size
612                    decompress = zlib.decompressobj()
613                    blocks = esp.flash_defl_begin(uncsize, len(image), address)
614                else:
615                    blocks = esp.flash_begin(
616                        uncsize, address, begin_rom_encrypted=encrypted
617                    )
618                argfile.seek(0)  # in case we need it again
619                seq = 0
620                bytes_sent = 0  # bytes sent on wire
621                bytes_written = 0  # bytes written to flash
622                t = time.time()
623
624                timeout = DEFAULT_TIMEOUT
625
626                while len(image) > 0:
627                    print_overwrite(
628                        "Writing at 0x%08x... (%d %%)"
629                        % (address + bytes_written, 100 * (seq + 1) // blocks)
630                    )
631                    sys.stdout.flush()
632                    block = image[0 : esp.FLASH_WRITE_SIZE]
633                    if compress:
634                        # feeding each compressed block into the decompressor lets us
635                        # see block-by-block how much will be written
636                        block_uncompressed = len(decompress.decompress(block))
637                        bytes_written += block_uncompressed
638                        block_timeout = max(
639                            DEFAULT_TIMEOUT,
640                            timeout_per_mb(
641                                ERASE_WRITE_TIMEOUT_PER_MB, block_uncompressed
642                            ),
643                        )
644                        if not esp.IS_STUB:
645                            timeout = block_timeout  # ROM code writes block to flash before ACKing
646                        esp.flash_defl_block(block, seq, timeout=timeout)
647                        if esp.IS_STUB:
648                            # Stub ACKs when block is received,
649                            # then writes to flash while receiving the block after it
650                            timeout = block_timeout
651                    else:
652                        # Pad the last block
653                        block = block + b"\xff" * (esp.FLASH_WRITE_SIZE - len(block))
654                        if encrypted:
655                            esp.flash_encrypt_block(block, seq)
656                        else:
657                            esp.flash_block(block, seq)
658                        bytes_written += len(block)
659                    bytes_sent += len(block)
660                    image = image[esp.FLASH_WRITE_SIZE :]
661                    seq += 1
662                break
663            except SerialException:
664                if attempt == esp.WRITE_FLASH_ATTEMPTS or encrypted:
665                    # Already retried once or encrypted mode is disabled because of security reasons
666                    raise
667                print("\nLost connection, retrying...")
668                esp._port.close()
669                print("Waiting for the chip to reconnect", end="")
670                for _ in range(DEFAULT_CONNECT_ATTEMPTS):
671                    try:
672                        time.sleep(1)
673                        esp._port.open()
674                        print()  # Print new line which was suppressed by print(".")
675                        esp.connect()
676                        if esp.IS_STUB:
677                            # Hack to bypass the stub overwrite check
678                            esp.IS_STUB = False
679                            # Reflash stub because chip was reset
680                            esp = esp.run_stub()
681                        image = original_image
682                        break
683                    except SerialException:
684                        print(".", end="")
685                        sys.stdout.flush()
686                else:
687                    raise  # Reconnect limit reached
688
689        if esp.IS_STUB:
690            # Stub only writes each block to flash after 'ack'ing the receive,
691            # so do a final dummy operation which will not be 'ack'ed
692            # until the last block has actually been written out to flash
693            esp.read_reg(ESPLoader.CHIP_DETECT_MAGIC_REG_ADDR, timeout=timeout)
694
695        t = time.time() - t
696        speed_msg = ""
697        if compress:
698            if t > 0.0:
699                speed_msg = " (effective %.1f kbit/s)" % (uncsize / t * 8 / 1000)
700            print_overwrite(
701                "Wrote %d bytes (%d compressed) at 0x%08x in %.1f seconds%s..."
702                % (uncsize, bytes_sent, address, t, speed_msg),
703                last_line=True,
704            )
705        else:
706            if t > 0.0:
707                speed_msg = " (%.1f kbit/s)" % (bytes_written / t * 8 / 1000)
708            print_overwrite(
709                "Wrote %d bytes at 0x%08x in %.1f seconds%s..."
710                % (bytes_written, address, t, speed_msg),
711                last_line=True,
712            )
713
714        if not encrypted and not esp.secure_download_mode:
715            try:
716                res = esp.flash_md5sum(address, uncsize)
717                if res != calcmd5:
718                    print("File  md5: %s" % calcmd5)
719                    print("Flash md5: %s" % res)
720                    print(
721                        "MD5 of 0xFF is %s"
722                        % (hashlib.md5(b"\xff" * uncsize).hexdigest())
723                    )
724                    raise FatalError("MD5 of file does not match data in flash!")
725                else:
726                    print("Hash of data verified.")
727            except NotImplementedInROMError:
728                pass
729
730    print("\nLeaving...")
731
732    if esp.IS_STUB:
733        # skip sending flash_finish to ROM loader here,
734        # as it causes the loader to exit and run user code
735        esp.flash_begin(0, 0)
736
737        # Get the "encrypted" flag for the last file flashed
738        # Note: all_files list contains triplets like:
739        # (address: Integer, filename: String, encrypted: Boolean)
740        last_file_encrypted = all_files[-1][2]
741
742        # Check whether the last file flashed was compressed or not
743        if args.compress and not last_file_encrypted:
744            esp.flash_defl_finish(False)
745        else:
746            esp.flash_finish(False)
747
748    if args.verify:
749        print("Verifying just-written flash...")
750        print(
751            "(This option is deprecated, "
752            "flash contents are now always read back after flashing.)"
753        )
754        # If some encrypted files have been flashed,
755        # print a warning saying that we won't check them
756        if args.encrypt or args.encrypt_files is not None:
757            print("WARNING: - cannot verify encrypted files, they will be ignored")
758        # Call verify_flash function only if there is at least
759        # one non-encrypted file flashed
760        if not args.encrypt:
761            verify_flash(esp, args)
762
763
764def image_info(args):
765    def v2():
766        def get_key_from_value(dict, val):
767            """Get key from value in dictionary"""
768            for key, value in dict.items():
769                if value == val:
770                    return key
771            return None
772
773        print()
774        title = "{} image header".format(args.chip.upper())
775        print(title)
776        print("=" * len(title))
777        print("Image version: {}".format(image.version))
778        print(
779            "Entry point: {:#8x}".format(image.entrypoint)
780            if image.entrypoint != 0
781            else "Entry point not set"
782        )
783
784        print("Segments: {}".format(len(image.segments)))
785
786        # Flash size
787        flash_s_bits = image.flash_size_freq & 0xF0  # high four bits
788        flash_s = get_key_from_value(image.ROM_LOADER.FLASH_SIZES, flash_s_bits)
789        print(
790            "Flash size: {}".format(flash_s)
791            if flash_s is not None
792            else "WARNING: Invalid flash size ({:#02x})".format(flash_s_bits)
793        )
794
795        # Flash frequency
796        flash_fr_bits = image.flash_size_freq & 0x0F  # low four bits
797        flash_fr = get_key_from_value(image.ROM_LOADER.FLASH_FREQUENCY, flash_fr_bits)
798        print(
799            "Flash freq: {}".format(flash_fr)
800            if flash_fr is not None
801            else "WARNING: Invalid flash frequency ({:#02x})".format(flash_fr_bits)
802        )
803
804        # Flash mode
805        flash_mode = get_key_from_value(FLASH_MODES, image.flash_mode)
806        print(
807            "Flash mode: {}".format(flash_mode.upper())
808            if flash_mode is not None
809            else "WARNING: Invalid flash mode ({})".format(image.flash_mode)
810        )
811
812        # Extended header (ESP32 and later only)
813        if args.chip != "esp8266":
814            print()
815            title = "{} extended image header".format(args.chip.upper())
816            print(title)
817            print("=" * len(title))
818            print(
819                f"WP pin: {image.wp_pin:#02x}",
820                *["(disabled)"] if image.wp_pin == image.WP_PIN_DISABLED else [],
821            )
822            print(
823                "Flash pins drive settings: "
824                "clk_drv: {:#02x}, q_drv: {:#02x}, d_drv: {:#02x}, "
825                "cs0_drv: {:#02x}, hd_drv: {:#02x}, wp_drv: {:#02x}".format(
826                    image.clk_drv,
827                    image.q_drv,
828                    image.d_drv,
829                    image.cs_drv,
830                    image.hd_drv,
831                    image.wp_drv,
832                )
833            )
834            try:
835                chip = next(
836                    chip
837                    for chip in CHIP_DEFS.values()
838                    if getattr(chip, "IMAGE_CHIP_ID", None) == image.chip_id
839                )
840                print(f"Chip ID: {image.chip_id} ({chip.CHIP_NAME})")
841            except StopIteration:
842                print(f"Chip ID: {image.chip_id} (Unknown ID)")
843            print(
844                "Minimal chip revision: "
845                f"v{image.min_rev_full // 100}.{image.min_rev_full % 100}, "
846                f"(legacy min_rev = {image.min_rev})"
847            )
848            print(
849                "Maximal chip revision: "
850                f"v{image.max_rev_full // 100}.{image.max_rev_full % 100}"
851            )
852        print()
853
854        # Segments overview
855        title = "Segments information"
856        print(title)
857        print("=" * len(title))
858        headers_str = "{:>7}  {:>7}  {:>10}  {:>10}  {:10}"
859        print(
860            headers_str.format(
861                "Segment", "Length", "Load addr", "File offs", "Memory types"
862            )
863        )
864        print(
865            "{}  {}  {}  {}  {}".format("-" * 7, "-" * 7, "-" * 10, "-" * 10, "-" * 12)
866        )
867        format_str = "{:7}  {:#07x}  {:#010x}  {:#010x}  {}"
868        app_desc = None
869        bootloader_desc = None
870        for idx, seg in enumerate(image.segments):
871            segs = seg.get_memory_type(image)
872            seg_name = ", ".join(segs)
873            if "DROM" in segs:  # The DROM segment starts with the esp_app_desc_t struct
874                app_desc = seg.data[:256]
875            elif "DRAM" in segs:
876                # The DRAM segment starts with the esp_bootloader_desc_t struct
877                if len(seg.data) >= 80:
878                    bootloader_desc = seg.data[:80]
879            print(
880                format_str.format(idx, len(seg.data), seg.addr, seg.file_offs, seg_name)
881            )
882        print()
883
884        # Footer
885        title = f"{args.chip.upper()} image footer"
886        print(title)
887        print("=" * len(title))
888        calc_checksum = image.calculate_checksum()
889        print(
890            "Checksum: {:#02x} ({})".format(
891                image.checksum,
892                (
893                    "valid"
894                    if image.checksum == calc_checksum
895                    else "invalid - calculated {:02x}".format(calc_checksum)
896                ),
897            )
898        )
899        try:
900            digest_msg = "Not appended"
901            if image.append_digest:
902                is_valid = image.stored_digest == image.calc_digest
903                digest_msg = "{} ({})".format(
904                    hexify(image.calc_digest, uppercase=False),
905                    "valid" if is_valid else "invalid",
906                )
907                print("Validation hash: {}".format(digest_msg))
908        except AttributeError:
909            pass  # ESP8266 image has no append_digest field
910
911        if app_desc:
912            APP_DESC_STRUCT_FMT = "<II" + "8s" + "32s32s16s16s32s32s" + "80s"
913            (
914                magic_word,
915                secure_version,
916                reserv1,
917                version,
918                project_name,
919                time,
920                date,
921                idf_ver,
922                app_elf_sha256,
923                reserv2,
924            ) = struct.unpack(APP_DESC_STRUCT_FMT, app_desc)
925
926            if magic_word == 0xABCD5432:
927                print()
928                title = "Application information"
929                print(title)
930                print("=" * len(title))
931                print(f'Project name: {project_name.decode("utf-8")}')
932                print(f'App version: {version.decode("utf-8")}')
933                print(f'Compile time: {date.decode("utf-8")} {time.decode("utf-8")}')
934                print(f"ELF file SHA256: {hexify(app_elf_sha256, uppercase=False)}")
935                print(f'ESP-IDF: {idf_ver.decode("utf-8")}')
936                print(f"Secure version: {secure_version}")
937
938        elif bootloader_desc:
939            BOOTLOADER_DESC_STRUCT_FMT = "<B" + "3s" + "I32s24s" + "16s"
940            (
941                magic_byte,
942                reserved,
943                version,
944                idf_ver,
945                date_time,
946                reserved2,
947            ) = struct.unpack(BOOTLOADER_DESC_STRUCT_FMT, bootloader_desc)
948
949            if magic_byte == 80:
950                print()
951                title = "Bootloader information"
952                print(title)
953                print("=" * len(title))
954                print(f"Bootloader version: {version}")
955                print(f'ESP-IDF: {idf_ver.decode("utf-8")}')
956                print(f'Compile time: {date_time.decode("utf-8")}')
957
958    print(f"File size: {get_file_size(args.filename)} (bytes)")
959    with open(args.filename, "rb") as f:
960        # magic number
961        try:
962            common_header = f.read(8)
963            magic = common_header[0]
964        except IndexError:
965            raise FatalError("File is empty")
966        if magic not in [
967            ESPLoader.ESP_IMAGE_MAGIC,
968            ESP8266V2FirmwareImage.IMAGE_V2_MAGIC,
969        ]:
970            raise FatalError(
971                f"This is not a valid image (invalid magic number: {magic:#x})"
972            )
973
974        if args.chip == "auto":
975            try:
976                extended_header = f.read(16)
977
978                # append_digest, either 0 or 1
979                if extended_header[-1] not in [0, 1]:
980                    raise FatalError("Append digest field not 0 or 1")
981
982                chip_id = int.from_bytes(extended_header[4:5], "little")
983                for rom in [n for n in ROM_LIST if n.CHIP_NAME != "ESP8266"]:
984                    if chip_id == rom.IMAGE_CHIP_ID:
985                        args.chip = rom.CHIP_NAME
986                        break
987                else:
988                    raise FatalError(f"Unknown image chip ID ({chip_id})")
989            except FatalError:
990                args.chip = "esp8266"
991
992            print(f"Detected image type: {args.chip.upper()}")
993
994    image = LoadFirmwareImage(args.chip, args.filename)
995
996    if args.version == "2":
997        v2()
998        return
999
1000    print("Image version: {}".format(image.version))
1001    print(
1002        "Entry point: {:8x}".format(image.entrypoint)
1003        if image.entrypoint != 0
1004        else "Entry point not set"
1005    )
1006    print("{} segments".format(len(image.segments)))
1007    print()
1008    idx = 0
1009    for seg in image.segments:
1010        idx += 1
1011        segs = seg.get_memory_type(image)
1012        seg_name = ",".join(segs)
1013        print("Segment {}: {} [{}]".format(idx, seg, seg_name))
1014    calc_checksum = image.calculate_checksum()
1015    print(
1016        "Checksum: {:02x} ({})".format(
1017            image.checksum,
1018            (
1019                "valid"
1020                if image.checksum == calc_checksum
1021                else "invalid - calculated {:02x}".format(calc_checksum)
1022            ),
1023        )
1024    )
1025    try:
1026        digest_msg = "Not appended"
1027        if image.append_digest:
1028            is_valid = image.stored_digest == image.calc_digest
1029            digest_msg = "{} ({})".format(
1030                hexify(image.calc_digest, uppercase=False),
1031                "valid" if is_valid else "invalid",
1032            )
1033            print("Validation Hash: {}".format(digest_msg))
1034    except AttributeError:
1035        pass  # ESP8266 image has no append_digest field
1036
1037
1038def make_image(args):
1039    print("Creating {} image...".format(args.chip))
1040    image = ESP8266ROMFirmwareImage()
1041    if len(args.segfile) == 0:
1042        raise FatalError("No segments specified")
1043    if len(args.segfile) != len(args.segaddr):
1044        raise FatalError(
1045            "Number of specified files does not match number of specified addresses"
1046        )
1047    for seg, addr in zip(args.segfile, args.segaddr):
1048        with open(seg, "rb") as f:
1049            data = f.read()
1050            image.segments.append(ImageSegment(addr, data))
1051    image.entrypoint = args.entrypoint
1052    image.save(args.output)
1053    print("Successfully created {} image.".format(args.chip))
1054
1055
1056def elf2image(args):
1057    e = ELFFile(args.input)
1058    if args.chip == "auto":  # Default to ESP8266 for backwards compatibility
1059        args.chip = "esp8266"
1060
1061    print("Creating {} image...".format(args.chip))
1062
1063    if args.chip != "esp8266":
1064        image = CHIP_DEFS[args.chip].BOOTLOADER_IMAGE()
1065        if args.chip == "esp32" and args.secure_pad:
1066            image.secure_pad = "1"
1067        if args.secure_pad_v2:
1068            image.secure_pad = "2"
1069        image.min_rev = args.min_rev
1070        image.min_rev_full = args.min_rev_full
1071        image.max_rev_full = args.max_rev_full
1072        image.ram_only_header = args.ram_only_header
1073        if image.ram_only_header:
1074            image.append_digest = False
1075        else:
1076            image.append_digest = args.append_digest
1077    elif args.version == "1":  # ESP8266
1078        image = ESP8266ROMFirmwareImage()
1079    elif args.version == "2":
1080        image = ESP8266V2FirmwareImage()
1081    else:
1082        image = ESP8266V3FirmwareImage()
1083    image.entrypoint = e.entrypoint
1084    image.flash_mode = FLASH_MODES[args.flash_mode]
1085
1086    if args.flash_mmu_page_size:
1087        image.set_mmu_page_size(flash_size_bytes(args.flash_mmu_page_size))
1088
1089    # ELFSection is a subclass of ImageSegment, so can use interchangeably
1090    image.segments = e.segments if args.use_segments else e.sections
1091    if args.pad_to_size:
1092        image.pad_to_size = flash_size_bytes(args.pad_to_size)
1093    image.flash_size_freq = image.ROM_LOADER.parse_flash_size_arg(args.flash_size)
1094    image.flash_size_freq += image.ROM_LOADER.parse_flash_freq_arg(args.flash_freq)
1095
1096    if args.elf_sha256_offset:
1097        image.elf_sha256 = e.sha256()
1098        image.elf_sha256_offset = args.elf_sha256_offset
1099
1100    if args.ram_only_header:
1101        print(
1102            "Image has only RAM segments visible. "
1103            "ROM segments are hidden and SHA256 digest is not appended."
1104        )
1105        image.sort_segments()
1106
1107    before = len(image.segments)
1108    image.merge_adjacent_segments()
1109    if len(image.segments) != before:
1110        delta = before - len(image.segments)
1111        print("Merged %d ELF section%s" % (delta, "s" if delta > 1 else ""))
1112
1113    image.verify()
1114
1115    if args.output is None:
1116        args.output = image.default_output_name(args.input)
1117    image.save(args.output)
1118
1119    print("Successfully created {} image.".format(args.chip))
1120
1121
1122def read_mac(esp, args):
1123    def print_mac(label, mac):
1124        print("%s: %s" % (label, ":".join(map(lambda x: "%02x" % x, mac))))
1125
1126    eui64 = esp.read_mac("EUI64")
1127    if eui64:
1128        print_mac("MAC", eui64)
1129        print_mac("BASE MAC", esp.read_mac("BASE_MAC"))
1130        print_mac("MAC_EXT", esp.read_mac("MAC_EXT"))
1131    else:
1132        print_mac("MAC", esp.read_mac("BASE_MAC"))
1133
1134
1135def chip_id(esp, args):
1136    try:
1137        chipid = esp.chip_id()
1138        print("Chip ID: 0x%08x" % chipid)
1139    except NotSupportedError:
1140        print("Warning: %s has no Chip ID. Reading MAC instead." % esp.CHIP_NAME)
1141        read_mac(esp, args)
1142
1143
1144def erase_flash(esp, args):
1145    if not args.force and esp.CHIP_NAME != "ESP8266" and not esp.secure_download_mode:
1146        if esp.get_flash_encryption_enabled() or esp.get_secure_boot_enabled():
1147            raise FatalError(
1148                "Active security features detected, "
1149                "erasing flash is disabled as a safety measure. "
1150                "Use --force to override, "
1151                "please use with caution, otherwise it may brick your device!"
1152            )
1153    print("Erasing flash (this may take a while)...")
1154    t = time.time()
1155    esp.erase_flash()
1156    print("Chip erase completed successfully in %.1fs" % (time.time() - t))
1157
1158
1159def erase_region(esp, args):
1160    if not args.force and esp.CHIP_NAME != "ESP8266" and not esp.secure_download_mode:
1161        if esp.get_flash_encryption_enabled() or esp.get_secure_boot_enabled():
1162            raise FatalError(
1163                "Active security features detected, "
1164                "erasing flash is disabled as a safety measure. "
1165                "Use --force to override, "
1166                "please use with caution, otherwise it may brick your device!"
1167            )
1168    print("Erasing region (may be slow depending on size)...")
1169    t = time.time()
1170    esp.erase_region(args.address, args.size)
1171    print("Erase completed successfully in %.1f seconds." % (time.time() - t))
1172
1173
1174def run(esp, args):
1175    esp.run()
1176
1177
1178def detect_flash_id(esp):
1179    flash_id = esp.flash_id()
1180    print("Manufacturer: %02x" % (flash_id & 0xFF))
1181    flid_lowbyte = (flash_id >> 16) & 0xFF
1182    print("Device: %02x%02x" % ((flash_id >> 8) & 0xFF, flid_lowbyte))
1183    print(
1184        "Detected flash size: %s" % (DETECTED_FLASH_SIZES.get(flid_lowbyte, "Unknown"))
1185    )
1186
1187
1188def flash_id(esp, args):
1189    detect_flash_id(esp)
1190    flash_type = esp.flash_type()
1191    flash_type_dict = {0: "quad (4 data lines)", 1: "octal (8 data lines)"}
1192    flash_type_str = flash_type_dict.get(flash_type)
1193    if flash_type_str:
1194        print(f"Flash type set in eFuse: {flash_type_str}")
1195    esp.get_flash_voltage()
1196
1197
1198def read_flash_sfdp(esp, args):
1199    detect_flash_id(esp)
1200
1201    sfdp = esp.read_spiflash_sfdp(args.addr, args.bytes * 8)
1202    print(f"SFDP[{args.addr}..{args.addr+args.bytes-1}]: ", end="")
1203    for i in range(args.bytes):
1204        print(f"{sfdp&0xff:02X} ", end="")
1205        sfdp = sfdp >> 8
1206    print()
1207
1208
1209def read_flash(esp, args):
1210    if args.no_progress:
1211        flash_progress = None
1212    else:
1213
1214        def flash_progress(progress, length):
1215            msg = "%d (%d %%)" % (progress, progress * 100.0 / length)
1216            padding = "\b" * len(msg)
1217            if progress == length:
1218                padding = "\n"
1219            sys.stdout.write(msg + padding)
1220            sys.stdout.flush()
1221
1222    t = time.time()
1223    data = esp.read_flash(args.address, args.size, flash_progress)
1224    t = time.time() - t
1225    speed_msg = " ({:.1f} kbit/s)".format(len(data) / t * 8 / 1000) if t > 0.0 else ""
1226    print_overwrite(
1227        "Read {:d} bytes at {:#010x} in {:.1f} seconds{}...".format(
1228            len(data), args.address, t, speed_msg
1229        ),
1230        last_line=True,
1231    )
1232    with open(args.filename, "wb") as f:
1233        f.write(data)
1234
1235
1236def verify_flash(esp, args):
1237    differences = False
1238
1239    for address, argfile in args.addr_filename:
1240        image = pad_to(argfile.read(), 4)
1241        argfile.seek(0)  # rewind in case we need it again
1242
1243        image = _update_image_flash_params(esp, address, args, image)
1244
1245        image_size = len(image)
1246        print(
1247            "Verifying 0x%x (%d) bytes @ 0x%08x in flash against %s..."
1248            % (image_size, image_size, address, argfile.name)
1249        )
1250        # Try digest first, only read if there are differences.
1251        digest = esp.flash_md5sum(address, image_size)
1252        expected_digest = hashlib.md5(image).hexdigest()
1253        if digest == expected_digest:
1254            print("-- verify OK (digest matched)")
1255            continue
1256        else:
1257            differences = True
1258            if getattr(args, "diff", "no") != "yes":
1259                print("-- verify FAILED (digest mismatch)")
1260                continue
1261
1262        flash = esp.read_flash(address, image_size)
1263        assert flash != image
1264        diff = [i for i in range(image_size) if flash[i] != image[i]]
1265        print(
1266            "-- verify FAILED: %d differences, first @ 0x%08x"
1267            % (len(diff), address + diff[0])
1268        )
1269        for d in diff:
1270            flash_byte = flash[d]
1271            image_byte = image[d]
1272            print("   %08x %02x %02x" % (address + d, flash_byte, image_byte))
1273    if differences:
1274        raise FatalError("Verify failed.")
1275
1276
1277def read_flash_status(esp, args):
1278    print("Status value: 0x%04x" % esp.read_status(args.bytes))
1279
1280
1281def write_flash_status(esp, args):
1282    fmt = "0x%%0%dx" % (args.bytes * 2)
1283    args.value = args.value & ((1 << (args.bytes * 8)) - 1)
1284    print(("Initial flash status: " + fmt) % esp.read_status(args.bytes))
1285    print(("Setting flash status: " + fmt) % args.value)
1286    esp.write_status(args.value, args.bytes, args.non_volatile)
1287    print(("After flash status:   " + fmt) % esp.read_status(args.bytes))
1288
1289
1290# The following mapping was taken from the ROM code
1291# This mapping is same across all targets in the ROM
1292SECURITY_INFO_FLAG_MAP = {
1293    "SECURE_BOOT_EN": (1 << 0),
1294    "SECURE_BOOT_AGGRESSIVE_REVOKE": (1 << 1),
1295    "SECURE_DOWNLOAD_ENABLE": (1 << 2),
1296    "SECURE_BOOT_KEY_REVOKE0": (1 << 3),
1297    "SECURE_BOOT_KEY_REVOKE1": (1 << 4),
1298    "SECURE_BOOT_KEY_REVOKE2": (1 << 5),
1299    "SOFT_DIS_JTAG": (1 << 6),
1300    "HARD_DIS_JTAG": (1 << 7),
1301    "DIS_USB": (1 << 8),
1302    "DIS_DOWNLOAD_DCACHE": (1 << 9),
1303    "DIS_DOWNLOAD_ICACHE": (1 << 10),
1304}
1305
1306
1307# Get the status of respective security flag
1308def get_security_flag_status(flag_name, flags_value):
1309    try:
1310        return (flags_value & SECURITY_INFO_FLAG_MAP[flag_name]) != 0
1311    except KeyError:
1312        raise ValueError(f"Invalid flag name: {flag_name}")
1313
1314
1315def get_security_info(esp, args):
1316    si = esp.get_security_info()
1317    print()
1318    title = "Security Information:"
1319    print(title)
1320    print("=" * len(title))
1321    print("Flags: {:#010x} ({})".format(si["flags"], bin(si["flags"])))
1322    if esp.KEY_PURPOSES:
1323        print(f"Key Purposes: {si['key_purposes']}")
1324        desc = "\n  ".join(
1325            [
1326                f"BLOCK_KEY{key_num} - {esp.KEY_PURPOSES.get(purpose, 'UNKNOWN')}"
1327                for key_num, purpose in enumerate(si["key_purposes"])
1328                if key_num <= esp.EFUSE_MAX_KEY
1329            ]
1330        )
1331        print(f"  {desc}")
1332    if si["chip_id"] is not None and si["api_version"] is not None:
1333        print("Chip ID: {}".format(si["chip_id"]))
1334        print("API Version: {}".format(si["api_version"]))
1335
1336    flags = si["flags"]
1337
1338    if get_security_flag_status("SECURE_BOOT_EN", flags):
1339        print("Secure Boot: Enabled")
1340        if get_security_flag_status("SECURE_BOOT_AGGRESSIVE_REVOKE", flags):
1341            print("Secure Boot Aggressive key revocation: Enabled")
1342
1343        revoked_keys = []
1344        for i, key in enumerate(
1345            [
1346                "SECURE_BOOT_KEY_REVOKE0",
1347                "SECURE_BOOT_KEY_REVOKE1",
1348                "SECURE_BOOT_KEY_REVOKE2",
1349            ]
1350        ):
1351            if get_security_flag_status(key, flags):
1352                revoked_keys.append(i)
1353
1354        if len(revoked_keys) > 0:
1355            print("Secure Boot Key Revocation Status:\n")
1356            for i in revoked_keys:
1357                print(f"\tSecure Boot Key{i} is Revoked\n")
1358
1359    else:
1360        print("Secure Boot: Disabled")
1361
1362    flash_crypt_cnt = bin(si["flash_crypt_cnt"])
1363    if (flash_crypt_cnt.count("1") % 2) != 0:
1364        print("Flash Encryption: Enabled")
1365    else:
1366        print("Flash Encryption: Disabled")
1367
1368    CRYPT_CNT_STRING = "SPI Boot Crypt Count (SPI_BOOT_CRYPT_CNT)"
1369    if esp.CHIP_NAME == "esp32":
1370        CRYPT_CNT_STRING = "Flash Crypt Count (FLASH_CRYPT_CNT)"
1371
1372    print(f"{CRYPT_CNT_STRING}: {si['flash_crypt_cnt']:#x}")
1373
1374    if get_security_flag_status("DIS_DOWNLOAD_DCACHE", flags):
1375        print("Dcache in UART download mode: Disabled")
1376
1377    if get_security_flag_status("DIS_DOWNLOAD_ICACHE", flags):
1378        print("Icache in UART download mode: Disabled")
1379
1380    hard_dis_jtag = get_security_flag_status("HARD_DIS_JTAG", flags)
1381    soft_dis_jtag = get_security_flag_status("SOFT_DIS_JTAG", flags)
1382    if hard_dis_jtag:
1383        print("JTAG: Permanently Disabled")
1384    elif soft_dis_jtag:
1385        print("JTAG: Software Access Disabled")
1386    if get_security_flag_status("DIS_USB", flags):
1387        print("USB Access: Disabled")
1388
1389
1390def merge_bin(args):
1391    try:
1392        chip_class = CHIP_DEFS[args.chip]
1393    except KeyError:
1394        msg = (
1395            "Please specify the chip argument"
1396            if args.chip == "auto"
1397            else f"Invalid chip choice: '{args.chip}'"
1398        )
1399        msg = f"{msg} (choose from {', '.join(CHIP_LIST)})"
1400        raise FatalError(msg)
1401
1402    # sort the files by offset.
1403    # The AddrFilenamePairAction has already checked for overlap
1404    input_files = sorted(args.addr_filename, key=lambda x: x[0])
1405    if not input_files:
1406        raise FatalError("No input files specified")
1407    first_addr = input_files[0][0]
1408    if first_addr < args.target_offset:
1409        raise FatalError(
1410            f"Output file target offset is {args.target_offset:#x}. "
1411            f"Input file offset {first_addr:#x} is before this."
1412        )
1413
1414    if args.format == "uf2":
1415        with UF2Writer(
1416            chip_class.UF2_FAMILY_ID,
1417            args.output,
1418            args.chunk_size,
1419            md5_enabled=not args.md5_disable,
1420        ) as writer:
1421            for addr, argfile in input_files:
1422                print(f"Adding {argfile.name} at {addr:#x}")
1423                image = argfile.read()
1424                image = _update_image_flash_params(chip_class, addr, args, image)
1425                writer.add_file(addr, image)
1426        print(
1427            f"Wrote {os.path.getsize(args.output):#x} bytes to file {args.output}, "
1428            f"ready to be flashed with any ESP USB Bridge"
1429        )
1430
1431    elif args.format == "raw":
1432        with open(args.output, "wb") as of:
1433
1434            def pad_to(flash_offs):
1435                # account for output file offset if there is any
1436                of.write(b"\xff" * (flash_offs - args.target_offset - of.tell()))
1437
1438            for addr, argfile in input_files:
1439                pad_to(addr)
1440                image = argfile.read()
1441                image = _update_image_flash_params(chip_class, addr, args, image)
1442                of.write(image)
1443            if args.fill_flash_size:
1444                pad_to(flash_size_bytes(args.fill_flash_size))
1445            print(
1446                f"Wrote {of.tell():#x} bytes to file {args.output}, "
1447                f"ready to flash to offset {args.target_offset:#x}"
1448            )
1449    elif args.format == "hex":
1450        out = IntelHex()
1451        if len(input_files) == 1:
1452            print(
1453                "WARNING: Only one input file specified, output may include "
1454                "additional padding if input file was previously merged. "
1455                "Please refer to the documentation for more information: "
1456                "https://docs.espressif.com/projects/esptool/en/latest/esptool/basic-commands.html#hex-output-format"  # noqa E501
1457            )
1458        for addr, argfile in input_files:
1459            ihex = IntelHex()
1460            image = argfile.read()
1461            image = _update_image_flash_params(chip_class, addr, args, image)
1462            ihex.frombytes(image, addr)
1463            out.merge(ihex)
1464        out.write_hex_file(args.output)
1465        print(
1466            f"Wrote {os.path.getsize(args.output):#x} bytes to file {args.output}, "
1467            f"ready to flash to offset {args.target_offset:#x}"
1468        )
1469
1470
1471def version(args):
1472    from . import __version__
1473
1474    print(__version__)
1475