mirror of
https://github.com/espressif/esptool.git
synced 2025-10-17 16:01:18 +08:00
feat(image_info): Image type autodetection
This commit is contained in:
@@ -187,13 +187,7 @@ This information corresponds to the headers described in :ref:`image-format`.
|
||||
|
||||
::
|
||||
|
||||
esptool.py --chip {IDF_TARGET_NAME} image_info --version 2 my_esp_app.bin
|
||||
|
||||
.. only:: not esp8266
|
||||
|
||||
.. note::
|
||||
|
||||
Note that ``--chip {IDF_TARGET_NAME}`` is required when reading {IDF_TARGET_NAME} images. Otherwise the default is ``--chip esp8266`` and the image will be interpreted as an invalid ESP8266 image.
|
||||
esptool.py image_info --version 2 my_esp_app.bin
|
||||
|
||||
.. _merge-bin:
|
||||
|
||||
|
@@ -33,20 +33,6 @@ def align_file_position(f, size):
|
||||
f.seek(align, 1)
|
||||
|
||||
|
||||
class ESPBOOTLOADER(object):
|
||||
"""
|
||||
These are constants related to software ESP8266 bootloader,
|
||||
working with 'v2' image files
|
||||
"""
|
||||
|
||||
# First byte of the "v2" application image
|
||||
IMAGE_V2_MAGIC = 0xEA
|
||||
|
||||
# First 'segment' value in a "v2" application image,
|
||||
# appears to be a constant version value?
|
||||
IMAGE_V2_SEGMENT = 4
|
||||
|
||||
|
||||
def LoadFirmwareImage(chip, filename):
|
||||
"""
|
||||
Load a firmware image. Can be for any supported SoC.
|
||||
@@ -76,7 +62,7 @@ def LoadFirmwareImage(chip, filename):
|
||||
f.seek(0)
|
||||
if magic == ESPLoader.ESP_IMAGE_MAGIC:
|
||||
return ESP8266ROMFirmwareImage(f)
|
||||
elif magic == ESPBOOTLOADER.IMAGE_V2_MAGIC:
|
||||
elif magic == ESP8266V2FirmwareImage.IMAGE_V2_MAGIC:
|
||||
return ESP8266V2FirmwareImage(f)
|
||||
else:
|
||||
raise FatalError("Invalid image magic number: %d" % magic)
|
||||
@@ -415,13 +401,19 @@ class ESP8266V2FirmwareImage(BaseFirmwareImage):
|
||||
"""
|
||||
|
||||
ROM_LOADER = ESP8266ROM
|
||||
# First byte of the "v2" application image
|
||||
IMAGE_V2_MAGIC = 0xEA
|
||||
|
||||
# First 'segment' value in a "v2" application image,
|
||||
# appears to be a constant version value?
|
||||
IMAGE_V2_SEGMENT = 4
|
||||
|
||||
def __init__(self, load_file=None):
|
||||
super(ESP8266V2FirmwareImage, self).__init__()
|
||||
self.version = 2
|
||||
if load_file is not None:
|
||||
segments = self.load_common_header(load_file, ESPBOOTLOADER.IMAGE_V2_MAGIC)
|
||||
if segments != ESPBOOTLOADER.IMAGE_V2_SEGMENT:
|
||||
segments = self.load_common_header(load_file, self.IMAGE_V2_MAGIC)
|
||||
if segments != self.IMAGE_V2_SEGMENT:
|
||||
# segment count is not really segment count here,
|
||||
# but we expect to see '4'
|
||||
print(
|
||||
@@ -489,8 +481,8 @@ class ESP8266V2FirmwareImage(BaseFirmwareImage):
|
||||
f.write(
|
||||
struct.pack(
|
||||
b"<BBBBI",
|
||||
ESPBOOTLOADER.IMAGE_V2_MAGIC,
|
||||
ESPBOOTLOADER.IMAGE_V2_SEGMENT,
|
||||
self.IMAGE_V2_MAGIC,
|
||||
self.IMAGE_V2_SEGMENT,
|
||||
self.flash_mode,
|
||||
self.flash_size_freq,
|
||||
self.entrypoint,
|
||||
|
@@ -33,7 +33,7 @@ from .loader import (
|
||||
ESPLoader,
|
||||
timeout_per_mb,
|
||||
)
|
||||
from .targets import CHIP_DEFS, CHIP_LIST, ROM_LIST
|
||||
from .targets import CHIP_DEFS, CHIP_LIST, ESP8266ROM, ROM_LIST
|
||||
from .util import (
|
||||
FatalError,
|
||||
NotImplementedInROMError,
|
||||
@@ -708,9 +708,45 @@ def image_info(args):
|
||||
except AttributeError:
|
||||
pass # ESP8266 image has no append_digest field
|
||||
|
||||
if args.chip == "auto":
|
||||
print("WARNING: --chip not specified, defaulting to ESP8266.")
|
||||
args.chip = "esp8266"
|
||||
with open(args.filename, "rb") as f:
|
||||
# magic number
|
||||
try:
|
||||
common_header = f.read(8)
|
||||
magic = common_header[0]
|
||||
except IndexError:
|
||||
raise FatalError("File is empty")
|
||||
if magic not in [
|
||||
ESPLoader.ESP_IMAGE_MAGIC,
|
||||
ESP8266V2FirmwareImage.IMAGE_V2_MAGIC,
|
||||
]:
|
||||
raise FatalError(
|
||||
"This is not a valid image "
|
||||
"(invalid magic number: {:#x})".format(magic)
|
||||
)
|
||||
|
||||
if args.chip == "auto":
|
||||
try:
|
||||
extended_header = f.read(16)
|
||||
# reserved fields, should all be zero
|
||||
if int.from_bytes(extended_header[7:-1], "little") != 0:
|
||||
raise FatalError("Reserved fields not all zero")
|
||||
|
||||
# append_digest, either 0 or 1
|
||||
if extended_header[-1] not in [0, 1]:
|
||||
raise FatalError("Append digest field not 0 or 1")
|
||||
|
||||
chip_id = int.from_bytes(extended_header[4:5], "little")
|
||||
for rom in [n for n in ROM_LIST if n != ESP8266ROM]:
|
||||
if chip_id == rom.IMAGE_CHIP_ID:
|
||||
args.chip = rom.CHIP_NAME
|
||||
break
|
||||
else:
|
||||
raise FatalError(f"Unknown image chip ID ({chip_id})")
|
||||
except FatalError:
|
||||
args.chip = "esp8266"
|
||||
|
||||
print(f"Detected image type: {args.chip.upper()}")
|
||||
|
||||
image = LoadFirmwareImage(args.chip, args.filename)
|
||||
|
||||
if args.version == "2":
|
||||
|
@@ -132,6 +132,38 @@ class ImageInfoTests(unittest.TestCase):
|
||||
"Wrong segment info",
|
||||
)
|
||||
|
||||
def test_image_type_detection(self):
|
||||
# ESP8266, version 1 and 2
|
||||
out = self.run_image_info("auto", NODEMCU_FILE, "1")
|
||||
self.assertTrue("Detected image type: ESP8266" in out)
|
||||
self.assertTrue("Segment 1: len 0x05ed4" in out)
|
||||
out = self.run_image_info("auto", NODEMCU_FILE, "2")
|
||||
self.assertTrue("Detected image type: ESP8266" in out)
|
||||
self.assertTrue("Flash freq: 40m" in out)
|
||||
out = self.run_image_info("auto", "esp8266_deepsleep.bin", "2")
|
||||
self.assertTrue("Detected image type: ESP8266" in out)
|
||||
|
||||
# ESP32, with and without detection
|
||||
out = self.run_image_info("auto", "bootloader_esp32.bin", "2")
|
||||
self.assertTrue("Detected image type: ESP32" in out)
|
||||
out = self.run_image_info("auto", "helloworld-esp32_edit.bin", "2")
|
||||
self.assertTrue("Detected image type: ESP32" in out)
|
||||
out = self.run_image_info("esp32", "bootloader_esp32.bin", "2")
|
||||
self.assertFalse("Detected image type: ESP32" in out)
|
||||
|
||||
# ESP32-C3
|
||||
out = self.run_image_info("auto", "bootloader_esp32c3.bin", "2")
|
||||
self.assertTrue("Detected image type: ESP32-C3" in out)
|
||||
|
||||
# ESP32-S3
|
||||
out = self.run_image_info("auto", "bootloader_esp32s3.bin", "2")
|
||||
self.assertTrue("Detected image type: ESP32-S3" in out)
|
||||
|
||||
@unittest.expectedFailure
|
||||
def test_invalid_image_type_detection(self):
|
||||
# Invalid image
|
||||
self.run_image_info("auto", "one_kb.bin", "2")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main(buffer=True)
|
||||
|
Reference in New Issue
Block a user